From: Christian König christian.koenig@amd.com
Just power down the PLL when we get a VCLK or DCLK of zero. Enabling the bypass mode early should also allow us to switch UVD clocks on the fly.
Signed-off-by: Christian König christian.koenig@amd.com --- drivers/gpu/drm/radeon/evergreen.c | 22 ++++++++++++++-------- drivers/gpu/drm/radeon/rv770.c | 20 +++++++++++++------- drivers/gpu/drm/radeon/si.c | 22 ++++++++++++++-------- 3 files changed, 41 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 124c193..facfa84 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -189,6 +189,20 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) unsigned vco_freq; int r;
+ /* bypass vclk and dclk with bclk */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1), + ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); + + /* put PLL in bypass mode */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); + + if (!vclk || !dclk) { + /* keep the Bypass mode, put PLL to sleep */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK); + return 0; + } + /* loop through vco from low to high */ for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) { unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384; @@ -236,14 +250,6 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
mdelay(1);
- /* bypass vclk and dclk with bclk */ - WREG32_P(CG_UPLL_FUNC_CNTL_2, - VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1), - ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); - - /* put PLL in bypass mode */ - WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); - r = evergreen_uvd_send_upll_ctlreq(rdev); if (r) return r; diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index 777f537..59065ba 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -100,6 +100,17 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) if (rdev->family == CHIP_RV740) return evergreen_set_uvd_clocks(rdev, vclk, dclk);
+ /* bypass vclk and dclk with bclk */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1), + ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); + + if (!vclk || !dclk) { + /* keep the Bypass mode, put PLL to sleep */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK); + return 0; + } + /* loop through vco from low to high */ vco_min = max(max(vco_min, vclk), dclk); for (vco_freq = vco_min; vco_freq <= vco_max; vco_freq += 500) { @@ -139,16 +150,11 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) } }
- /* bypass vclk and dclk with bclk */ - WREG32_P(CG_UPLL_FUNC_CNTL_2, - VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1), - ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); - /* set UPLL_FB_DIV to 0x50000 */ WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(0x50000), ~UPLL_FB_DIV_MASK);
- /* deassert UPLL_RESET */ - WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK); + /* deassert UPLL_RESET and UPLL_SLEEP */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~(UPLL_RESET_MASK | UPLL_SLEEP_MASK));
/* assert BYPASS EN and FB_DIV[0] <- ??? why? */ WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index aa2c555..b7d78f2 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -4680,6 +4680,20 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) unsigned vco_freq; int r;
+ /* bypass vclk and dclk with bclk */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1), + ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); + + /* put PLL in bypass mode */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); + + if (!vclk || !dclk) { + /* keep the Bypass mode, put PLL to sleep */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK); + return 0; + } + /* loop through vco from low to high */ for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) { unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384; @@ -4730,14 +4744,6 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
mdelay(1);
- /* bypass vclk and dclk with bclk */ - WREG32_P(CG_UPLL_FUNC_CNTL_2, - VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1), - ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); - - /* put PLL in bypass mode */ - WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); - r = si_uvd_send_upll_ctlreq(rdev); if (r) return r;
From: Christian König christian.koenig@amd.com
That not only saves some power, but also solves problems with older chips where an idle UVD block on higher clocks can cause problems.
Signed-off-by: Christian König christian.koenig@amd.com --- drivers/gpu/drm/radeon/radeon.h | 2 ++ drivers/gpu/drm/radeon/radeon_cs.c | 4 ++++ drivers/gpu/drm/radeon/radeon_uvd.c | 30 ++++++++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 18904fb..deafb54 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1143,6 +1143,7 @@ struct radeon_uvd { uint64_t gpu_addr; atomic_t handles[RADEON_MAX_UVD_HANDLES]; struct drm_file *filp[RADEON_MAX_UVD_HANDLES]; + struct delayed_work idle_work; };
int radeon_uvd_init(struct radeon_device *rdev); @@ -1157,6 +1158,7 @@ void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo); void radeon_uvd_free_handles(struct radeon_device *rdev, struct drm_file *filp); int radeon_uvd_cs_parse(struct radeon_cs_parser *parser); +void radeon_uvd_note_usage(struct radeon_device *rdev);
struct r600_audio { int channels; diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index c740707..7e265a5 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -549,6 +549,10 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) r = radeon_cs_handle_lockup(rdev, r); return r; } + + if (parser.ring == R600_RING_TYPE_UVD_INDEX) + radeon_uvd_note_usage(rdev); + r = radeon_cs_ib_chunk(rdev, &parser); if (r) { goto out; diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index 15580fb..0312a7f 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -36,6 +36,9 @@ #include "radeon.h" #include "r600d.h"
+/* 1 second timeout */ +#define UVD_IDLE_TIMEOUT_MS 1000 + /* Firmware Names */ #define FIRMWARE_RV710 "radeon/RV710_uvd.bin" #define FIRMWARE_CYPRESS "radeon/CYPRESS_uvd.bin" @@ -47,6 +50,8 @@ MODULE_FIRMWARE(FIRMWARE_CYPRESS); MODULE_FIRMWARE(FIRMWARE_SUMO); MODULE_FIRMWARE(FIRMWARE_TAHITI);
+static void radeon_uvd_idle_work_handler(struct work_struct *work); + int radeon_uvd_init(struct radeon_device *rdev) { struct platform_device *pdev; @@ -54,6 +59,8 @@ int radeon_uvd_init(struct radeon_device *rdev) const char *fw_name; int i, r;
+ INIT_DELAYED_WORK(&rdev->uvd.idle_work, radeon_uvd_idle_work_handler); + pdev = platform_device_register_simple("radeon_uvd", 0, NULL, 0); r = IS_ERR(pdev); if (r) { @@ -188,8 +195,6 @@ int radeon_uvd_resume(struct radeon_device *rdev)
radeon_bo_unreserve(rdev->uvd.vcpu_bo);
- radeon_set_uvd_clocks(rdev, 53300, 40000); - return 0; }
@@ -666,3 +671,24 @@ int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring,
return radeon_uvd_send_msg(rdev, ring, bo, fence); } + +static void radeon_uvd_idle_work_handler(struct work_struct *work) +{ + struct radeon_device *rdev = + container_of(work, struct radeon_device, uvd.idle_work.work); + + if (radeon_fence_count_emitted(rdev, R600_RING_TYPE_UVD_INDEX) == 0) + radeon_set_uvd_clocks(rdev, 0, 0); + else + schedule_delayed_work(&rdev->uvd.idle_work, + msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS)); +} + +void radeon_uvd_note_usage(struct radeon_device *rdev) +{ + bool set_clocks = !cancel_delayed_work_sync(&rdev->uvd.idle_work); + set_clocks &= schedule_delayed_work(&rdev->uvd.idle_work, + msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS)); + if (set_clocks) + radeon_set_uvd_clocks(rdev, 53300, 40000); +}
On Thu, Apr 18, 2013 at 9:25 AM, Christian König deathsimple@vodafone.de wrote:
From: Christian König christian.koenig@amd.com
Just power down the PLL when we get a VCLK or DCLK of zero. Enabling the bypass mode early should also allow us to switch UVD clocks on the fly.
Signed-off-by: Christian König christian.koenig@amd.com
Looks good. both applied.
Alex
drivers/gpu/drm/radeon/evergreen.c | 22 ++++++++++++++-------- drivers/gpu/drm/radeon/rv770.c | 20 +++++++++++++------- drivers/gpu/drm/radeon/si.c | 22 ++++++++++++++-------- 3 files changed, 41 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 124c193..facfa84 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -189,6 +189,20 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) unsigned vco_freq; int r;
/* bypass vclk and dclk with bclk */
WREG32_P(CG_UPLL_FUNC_CNTL_2,
VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1),
~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
/* put PLL in bypass mode */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK);
if (!vclk || !dclk) {
/* keep the Bypass mode, put PLL to sleep */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
return 0;
}
/* loop through vco from low to high */ for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) { unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384;
@@ -236,14 +250,6 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
mdelay(1);
/* bypass vclk and dclk with bclk */
WREG32_P(CG_UPLL_FUNC_CNTL_2,
VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1),
~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
/* put PLL in bypass mode */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK);
r = evergreen_uvd_send_upll_ctlreq(rdev); if (r) return r;
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index 777f537..59065ba 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -100,6 +100,17 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) if (rdev->family == CHIP_RV740) return evergreen_set_uvd_clocks(rdev, vclk, dclk);
/* bypass vclk and dclk with bclk */
WREG32_P(CG_UPLL_FUNC_CNTL_2,
VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1),
~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
if (!vclk || !dclk) {
/* keep the Bypass mode, put PLL to sleep */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
return 0;
}
/* loop through vco from low to high */ vco_min = max(max(vco_min, vclk), dclk); for (vco_freq = vco_min; vco_freq <= vco_max; vco_freq += 500) {
@@ -139,16 +150,11 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) } }
/* bypass vclk and dclk with bclk */
WREG32_P(CG_UPLL_FUNC_CNTL_2,
VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1),
~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
/* set UPLL_FB_DIV to 0x50000 */ WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(0x50000), ~UPLL_FB_DIV_MASK);
/* deassert UPLL_RESET */
WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);
/* deassert UPLL_RESET and UPLL_SLEEP */
WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~(UPLL_RESET_MASK | UPLL_SLEEP_MASK)); /* assert BYPASS EN and FB_DIV[0] <- ??? why? */ WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK);
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index aa2c555..b7d78f2 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -4680,6 +4680,20 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) unsigned vco_freq; int r;
/* bypass vclk and dclk with bclk */
WREG32_P(CG_UPLL_FUNC_CNTL_2,
VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1),
~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
/* put PLL in bypass mode */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK);
if (!vclk || !dclk) {
/* keep the Bypass mode, put PLL to sleep */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
return 0;
}
/* loop through vco from low to high */ for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) { unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384;
@@ -4730,14 +4744,6 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
mdelay(1);
/* bypass vclk and dclk with bclk */
WREG32_P(CG_UPLL_FUNC_CNTL_2,
VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1),
~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
/* put PLL in bypass mode */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK);
r = si_uvd_send_upll_ctlreq(rdev); if (r) return r;
-- 1.7.10.4
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
dri-devel@lists.freedesktop.org