[PATCH 07/11] drm/radeon: add set_uvd_clocks callback for evergreen

2013-04-08 Thread Christian König
From: Alex Deucher 

v2: remove unneeded register definitions

Signed-off-by: Alex Deucher 
Signed-off-by: Christian K?nig 
Reviewed-by: Jerome Glisse 
---
 drivers/gpu/drm/radeon/evergreen.c   |  164 ++
 drivers/gpu/drm/radeon/evergreend.h  |   27 ++
 drivers/gpu/drm/radeon/radeon_asic.c |3 +
 drivers/gpu/drm/radeon/radeon_asic.h |1 +
 4 files changed, 195 insertions(+)

diff --git a/drivers/gpu/drm/radeon/evergreen.c 
b/drivers/gpu/drm/radeon/evergreen.c
index bdd3d34..a6e7186 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -131,6 +131,170 @@ done:
return r;
 }

+static int evergreen_uvd_calc_post_div(unsigned target_freq,
+  unsigned vco_freq,
+  unsigned *div)
+{
+   /* target larger than vco frequency ? */
+   if (vco_freq < target_freq)
+   return -1; /* forget it */
+
+   /* Fclk = Fvco / PDIV */
+   *div = vco_freq / target_freq;
+
+   /* we alway need a frequency less than or equal the target */
+   if ((vco_freq / *div) > target_freq)
+   *div += 1;
+
+   /* dividers above 5 must be even */
+   if (*div > 5 && *div % 2)
+   *div += 1;
+
+   /* out of range ? */
+   if (*div >= 128)
+   return -1; /* forget it */
+
+   return vco_freq / *div;
+}
+
+static int evergreen_uvd_send_upll_ctlreq(struct radeon_device *rdev)
+{
+   unsigned i;
+
+   /* assert UPLL_CTLREQ */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK);
+
+   /* wait for CTLACK and CTLACK2 to get asserted */
+   for (i = 0; i < 100; ++i) {
+   uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK;
+   if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask)
+   break;
+   mdelay(10);
+   }
+   if (i == 100)
+   return -ETIMEDOUT;
+
+   /* deassert UPLL_CTLREQ */
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK);
+
+   return 0;
+}
+
+int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
+{
+   /* start off with something large */
+   int optimal_diff_score = 0x7FF;
+   unsigned optimal_fb_div = 0, optimal_vclk_div = 0;
+   unsigned optimal_dclk_div = 0, optimal_vco_freq = 0;
+   unsigned vco_freq;
+   int r;
+
+   /* loop through vco from low to high */
+   for (vco_freq = 125000; vco_freq <= 25; vco_freq += 100) {
+   unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 
16384;
+   int calc_clk, diff_score, diff_vclk, diff_dclk;
+   unsigned vclk_div, dclk_div;
+
+   /* fb div out of range ? */
+   if (fb_div > 0x03FF)
+   break; /* it can oly get worse */
+
+   /* calc vclk with current vco freq. */
+   calc_clk = evergreen_uvd_calc_post_div(vclk, vco_freq, 
&vclk_div);
+   if (calc_clk == -1)
+   break; /* vco is too big, it has to stop. */
+   diff_vclk = vclk - calc_clk;
+
+   /* calc dclk with current vco freq. */
+   calc_clk = evergreen_uvd_calc_post_div(dclk, vco_freq, 
&dclk_div);
+   if (calc_clk == -1)
+   break; /* vco is too big, it has to stop. */
+   diff_dclk = dclk - calc_clk;
+
+   /* determine if this vco setting is better than current optimal 
settings */
+   diff_score = abs(diff_vclk) + abs(diff_dclk);
+   if (diff_score < optimal_diff_score) {
+   optimal_fb_div = fb_div;
+   optimal_vclk_div = vclk_div;
+   optimal_dclk_div = dclk_div;
+   optimal_vco_freq = vco_freq;
+   optimal_diff_score = diff_score;
+   if (optimal_diff_score == 0)
+   break; /* it can't get better than this */
+   }
+   }
+
+   /* set VCO_MODE to 1 */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK);
+
+   /* toggle UPLL_SLEEP to 1 then back to 0 */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK);
+
+   /* deassert UPLL_RESET */
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);
+
+   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;
+
+   /* assert UPLL_RESET again */
+   WREG32_P(

[PATCH 07/11] drm/radeon: add set_uvd_clocks callback for evergreen

2013-04-08 Thread Christian König
From: Alex Deucher 

v2: remove unneeded register definitions

Signed-off-by: Alex Deucher 
Signed-off-by: Christian König 
Reviewed-by: Jerome Glisse 
---
 drivers/gpu/drm/radeon/evergreen.c   |  164 ++
 drivers/gpu/drm/radeon/evergreend.h  |   27 ++
 drivers/gpu/drm/radeon/radeon_asic.c |3 +
 drivers/gpu/drm/radeon/radeon_asic.h |1 +
 4 files changed, 195 insertions(+)

diff --git a/drivers/gpu/drm/radeon/evergreen.c 
b/drivers/gpu/drm/radeon/evergreen.c
index bdd3d34..a6e7186 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -131,6 +131,170 @@ done:
return r;
 }
 
+static int evergreen_uvd_calc_post_div(unsigned target_freq,
+  unsigned vco_freq,
+  unsigned *div)
+{
+   /* target larger than vco frequency ? */
+   if (vco_freq < target_freq)
+   return -1; /* forget it */
+
+   /* Fclk = Fvco / PDIV */
+   *div = vco_freq / target_freq;
+
+   /* we alway need a frequency less than or equal the target */
+   if ((vco_freq / *div) > target_freq)
+   *div += 1;
+
+   /* dividers above 5 must be even */
+   if (*div > 5 && *div % 2)
+   *div += 1;
+
+   /* out of range ? */
+   if (*div >= 128)
+   return -1; /* forget it */
+
+   return vco_freq / *div;
+}
+
+static int evergreen_uvd_send_upll_ctlreq(struct radeon_device *rdev)
+{
+   unsigned i;
+
+   /* assert UPLL_CTLREQ */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK);
+
+   /* wait for CTLACK and CTLACK2 to get asserted */
+   for (i = 0; i < 100; ++i) {
+   uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK;
+   if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask)
+   break;
+   mdelay(10);
+   }
+   if (i == 100)
+   return -ETIMEDOUT;
+
+   /* deassert UPLL_CTLREQ */
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK);
+
+   return 0;
+}
+
+int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
+{
+   /* start off with something large */
+   int optimal_diff_score = 0x7FF;
+   unsigned optimal_fb_div = 0, optimal_vclk_div = 0;
+   unsigned optimal_dclk_div = 0, optimal_vco_freq = 0;
+   unsigned vco_freq;
+   int r;
+
+   /* loop through vco from low to high */
+   for (vco_freq = 125000; vco_freq <= 25; vco_freq += 100) {
+   unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 
16384;
+   int calc_clk, diff_score, diff_vclk, diff_dclk;
+   unsigned vclk_div, dclk_div;
+
+   /* fb div out of range ? */
+   if (fb_div > 0x03FF)
+   break; /* it can oly get worse */
+
+   /* calc vclk with current vco freq. */
+   calc_clk = evergreen_uvd_calc_post_div(vclk, vco_freq, 
&vclk_div);
+   if (calc_clk == -1)
+   break; /* vco is too big, it has to stop. */
+   diff_vclk = vclk - calc_clk;
+
+   /* calc dclk with current vco freq. */
+   calc_clk = evergreen_uvd_calc_post_div(dclk, vco_freq, 
&dclk_div);
+   if (calc_clk == -1)
+   break; /* vco is too big, it has to stop. */
+   diff_dclk = dclk - calc_clk;
+
+   /* determine if this vco setting is better than current optimal 
settings */
+   diff_score = abs(diff_vclk) + abs(diff_dclk);
+   if (diff_score < optimal_diff_score) {
+   optimal_fb_div = fb_div;
+   optimal_vclk_div = vclk_div;
+   optimal_dclk_div = dclk_div;
+   optimal_vco_freq = vco_freq;
+   optimal_diff_score = diff_score;
+   if (optimal_diff_score == 0)
+   break; /* it can't get better than this */
+   }
+   }
+
+   /* set VCO_MODE to 1 */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK);
+
+   /* toggle UPLL_SLEEP to 1 then back to 0 */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK);
+
+   /* deassert UPLL_RESET */
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);
+
+   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;
+
+   /* assert UPLL_RESET again */
+   WREG32_P

[PATCH 07/11] drm/radeon: add set_uvd_clocks callback for evergreen

2013-04-06 Thread Christian König
From: Alex Deucher 

v2: remove unneeded register definitions

Signed-off-by: Alex Deucher 
Signed-off-by: Christian K?nig 
Reviewed-by: Jerome Glisse 
---
 drivers/gpu/drm/radeon/evergreen.c   |  164 ++
 drivers/gpu/drm/radeon/evergreend.h  |   27 ++
 drivers/gpu/drm/radeon/radeon_asic.c |3 +
 drivers/gpu/drm/radeon/radeon_asic.h |1 +
 4 files changed, 195 insertions(+)

diff --git a/drivers/gpu/drm/radeon/evergreen.c 
b/drivers/gpu/drm/radeon/evergreen.c
index bdd3d34..a6e7186 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -131,6 +131,170 @@ done:
return r;
 }

+static int evergreen_uvd_calc_post_div(unsigned target_freq,
+  unsigned vco_freq,
+  unsigned *div)
+{
+   /* target larger than vco frequency ? */
+   if (vco_freq < target_freq)
+   return -1; /* forget it */
+
+   /* Fclk = Fvco / PDIV */
+   *div = vco_freq / target_freq;
+
+   /* we alway need a frequency less than or equal the target */
+   if ((vco_freq / *div) > target_freq)
+   *div += 1;
+
+   /* dividers above 5 must be even */
+   if (*div > 5 && *div % 2)
+   *div += 1;
+
+   /* out of range ? */
+   if (*div >= 128)
+   return -1; /* forget it */
+
+   return vco_freq / *div;
+}
+
+static int evergreen_uvd_send_upll_ctlreq(struct radeon_device *rdev)
+{
+   unsigned i;
+
+   /* assert UPLL_CTLREQ */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK);
+
+   /* wait for CTLACK and CTLACK2 to get asserted */
+   for (i = 0; i < 100; ++i) {
+   uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK;
+   if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask)
+   break;
+   mdelay(10);
+   }
+   if (i == 100)
+   return -ETIMEDOUT;
+
+   /* deassert UPLL_CTLREQ */
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK);
+
+   return 0;
+}
+
+int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
+{
+   /* start off with something large */
+   int optimal_diff_score = 0x7FF;
+   unsigned optimal_fb_div = 0, optimal_vclk_div = 0;
+   unsigned optimal_dclk_div = 0, optimal_vco_freq = 0;
+   unsigned vco_freq;
+   int r;
+
+   /* loop through vco from low to high */
+   for (vco_freq = 125000; vco_freq <= 25; vco_freq += 100) {
+   unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 
16384;
+   int calc_clk, diff_score, diff_vclk, diff_dclk;
+   unsigned vclk_div, dclk_div;
+
+   /* fb div out of range ? */
+   if (fb_div > 0x03FF)
+   break; /* it can oly get worse */
+
+   /* calc vclk with current vco freq. */
+   calc_clk = evergreen_uvd_calc_post_div(vclk, vco_freq, 
&vclk_div);
+   if (calc_clk == -1)
+   break; /* vco is too big, it has to stop. */
+   diff_vclk = vclk - calc_clk;
+
+   /* calc dclk with current vco freq. */
+   calc_clk = evergreen_uvd_calc_post_div(dclk, vco_freq, 
&dclk_div);
+   if (calc_clk == -1)
+   break; /* vco is too big, it has to stop. */
+   diff_dclk = dclk - calc_clk;
+
+   /* determine if this vco setting is better than current optimal 
settings */
+   diff_score = abs(diff_vclk) + abs(diff_dclk);
+   if (diff_score < optimal_diff_score) {
+   optimal_fb_div = fb_div;
+   optimal_vclk_div = vclk_div;
+   optimal_dclk_div = dclk_div;
+   optimal_vco_freq = vco_freq;
+   optimal_diff_score = diff_score;
+   if (optimal_diff_score == 0)
+   break; /* it can't get better than this */
+   }
+   }
+
+   /* set VCO_MODE to 1 */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK);
+
+   /* toggle UPLL_SLEEP to 1 then back to 0 */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK);
+
+   /* deassert UPLL_RESET */
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);
+
+   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;
+
+   /* assert UPLL_RESET again */
+   WREG32_P(

[PATCH 07/11] drm/radeon: add set_uvd_clocks callback for evergreen

2013-04-06 Thread Christian König
From: Alex Deucher 

v2: remove unneeded register definitions

Signed-off-by: Alex Deucher 
Signed-off-by: Christian König 
Reviewed-by: Jerome Glisse 
---
 drivers/gpu/drm/radeon/evergreen.c   |  164 ++
 drivers/gpu/drm/radeon/evergreend.h  |   27 ++
 drivers/gpu/drm/radeon/radeon_asic.c |3 +
 drivers/gpu/drm/radeon/radeon_asic.h |1 +
 4 files changed, 195 insertions(+)

diff --git a/drivers/gpu/drm/radeon/evergreen.c 
b/drivers/gpu/drm/radeon/evergreen.c
index bdd3d34..a6e7186 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -131,6 +131,170 @@ done:
return r;
 }
 
+static int evergreen_uvd_calc_post_div(unsigned target_freq,
+  unsigned vco_freq,
+  unsigned *div)
+{
+   /* target larger than vco frequency ? */
+   if (vco_freq < target_freq)
+   return -1; /* forget it */
+
+   /* Fclk = Fvco / PDIV */
+   *div = vco_freq / target_freq;
+
+   /* we alway need a frequency less than or equal the target */
+   if ((vco_freq / *div) > target_freq)
+   *div += 1;
+
+   /* dividers above 5 must be even */
+   if (*div > 5 && *div % 2)
+   *div += 1;
+
+   /* out of range ? */
+   if (*div >= 128)
+   return -1; /* forget it */
+
+   return vco_freq / *div;
+}
+
+static int evergreen_uvd_send_upll_ctlreq(struct radeon_device *rdev)
+{
+   unsigned i;
+
+   /* assert UPLL_CTLREQ */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK);
+
+   /* wait for CTLACK and CTLACK2 to get asserted */
+   for (i = 0; i < 100; ++i) {
+   uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK;
+   if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask)
+   break;
+   mdelay(10);
+   }
+   if (i == 100)
+   return -ETIMEDOUT;
+
+   /* deassert UPLL_CTLREQ */
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK);
+
+   return 0;
+}
+
+int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
+{
+   /* start off with something large */
+   int optimal_diff_score = 0x7FF;
+   unsigned optimal_fb_div = 0, optimal_vclk_div = 0;
+   unsigned optimal_dclk_div = 0, optimal_vco_freq = 0;
+   unsigned vco_freq;
+   int r;
+
+   /* loop through vco from low to high */
+   for (vco_freq = 125000; vco_freq <= 25; vco_freq += 100) {
+   unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 
16384;
+   int calc_clk, diff_score, diff_vclk, diff_dclk;
+   unsigned vclk_div, dclk_div;
+
+   /* fb div out of range ? */
+   if (fb_div > 0x03FF)
+   break; /* it can oly get worse */
+
+   /* calc vclk with current vco freq. */
+   calc_clk = evergreen_uvd_calc_post_div(vclk, vco_freq, 
&vclk_div);
+   if (calc_clk == -1)
+   break; /* vco is too big, it has to stop. */
+   diff_vclk = vclk - calc_clk;
+
+   /* calc dclk with current vco freq. */
+   calc_clk = evergreen_uvd_calc_post_div(dclk, vco_freq, 
&dclk_div);
+   if (calc_clk == -1)
+   break; /* vco is too big, it has to stop. */
+   diff_dclk = dclk - calc_clk;
+
+   /* determine if this vco setting is better than current optimal 
settings */
+   diff_score = abs(diff_vclk) + abs(diff_dclk);
+   if (diff_score < optimal_diff_score) {
+   optimal_fb_div = fb_div;
+   optimal_vclk_div = vclk_div;
+   optimal_dclk_div = dclk_div;
+   optimal_vco_freq = vco_freq;
+   optimal_diff_score = diff_score;
+   if (optimal_diff_score == 0)
+   break; /* it can't get better than this */
+   }
+   }
+
+   /* set VCO_MODE to 1 */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK);
+
+   /* toggle UPLL_SLEEP to 1 then back to 0 */
+   WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK);
+
+   /* deassert UPLL_RESET */
+   WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);
+
+   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;
+
+   /* assert UPLL_RESET again */
+   WREG32_P