diff options
author | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2014-09-15 08:40:47 -0400 |
---|---|---|
committer | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2014-11-12 06:40:25 -0500 |
commit | 33f13120e59cb4eddedc09039d8602d94aa864b4 (patch) | |
tree | 08de9e1a6ee2fb9eff526e442d14ec73bf83f956 | |
parent | 31dd0f4be428c7059c697332e27417cb6b08087d (diff) |
OMAPDSS: HDMI: rewrite HDMI PLL calculation code
The code calculating HDMI PLL parameters has always been very confusing.
Now that we are implementing a common PLL library for the DSS, it's
important that the PLL code is understandable.
This patch rewrites the calculation code, and removes a few hacks that
were used there.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
-rw-r--r-- | drivers/video/fbdev/omap2/dss/hdmi.h | 10 | ||||
-rw-r--r-- | drivers/video/fbdev/omap2/dss/hdmi4.c | 9 | ||||
-rw-r--r-- | drivers/video/fbdev/omap2/dss/hdmi5.c | 9 | ||||
-rw-r--r-- | drivers/video/fbdev/omap2/dss/hdmi_phy.c | 31 | ||||
-rw-r--r-- | drivers/video/fbdev/omap2/dss/hdmi_pll.c | 95 |
5 files changed, 71 insertions, 83 deletions
diff --git a/drivers/video/fbdev/omap2/dss/hdmi.h b/drivers/video/fbdev/omap2/dss/hdmi.h index 4bbc9d206f4a..4b9bf0804a48 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi.h +++ b/drivers/video/fbdev/omap2/dss/hdmi.h | |||
@@ -191,7 +191,9 @@ struct hdmi_pll_info { | |||
191 | u32 regmf; | 191 | u32 regmf; |
192 | u16 regm2; | 192 | u16 regm2; |
193 | u16 regsd; | 193 | u16 regsd; |
194 | u16 dcofreq; | 194 | |
195 | unsigned long clkdco; | ||
196 | unsigned long clkout; | ||
195 | }; | 197 | }; |
196 | 198 | ||
197 | struct hdmi_audio_format { | 199 | struct hdmi_audio_format { |
@@ -313,11 +315,13 @@ int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp); | |||
313 | int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp); | 315 | int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp); |
314 | void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp); | 316 | void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp); |
315 | void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s); | 317 | void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s); |
316 | void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy); | 318 | void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, |
319 | unsigned long target_tmds); | ||
317 | int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll); | 320 | int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll); |
318 | 321 | ||
319 | /* HDMI PHY funcs */ | 322 | /* HDMI PHY funcs */ |
320 | int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg); | 323 | int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk, |
324 | unsigned long lfbitclk); | ||
321 | void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s); | 325 | void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s); |
322 | int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy); | 326 | int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy); |
323 | int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes); | 327 | int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes); |
diff --git a/drivers/video/fbdev/omap2/dss/hdmi4.c b/drivers/video/fbdev/omap2/dss/hdmi4.c index 9a8713ca090c..1f2fbccaff1f 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi4.c +++ b/drivers/video/fbdev/omap2/dss/hdmi4.c | |||
@@ -180,7 +180,6 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) | |||
180 | int r; | 180 | int r; |
181 | struct omap_video_timings *p; | 181 | struct omap_video_timings *p; |
182 | struct omap_overlay_manager *mgr = hdmi.output.manager; | 182 | struct omap_overlay_manager *mgr = hdmi.output.manager; |
183 | unsigned long phy; | ||
184 | struct hdmi_wp_data *wp = &hdmi.wp; | 183 | struct hdmi_wp_data *wp = &hdmi.wp; |
185 | 184 | ||
186 | r = hdmi_power_on_core(dssdev); | 185 | r = hdmi_power_on_core(dssdev); |
@@ -195,10 +194,7 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) | |||
195 | 194 | ||
196 | DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); | 195 | DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); |
197 | 196 | ||
198 | /* the functions below use kHz pixel clock. TODO: change to Hz */ | 197 | hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), p->pixelclock); |
199 | phy = p->pixelclock / 1000; | ||
200 | |||
201 | hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy); | ||
202 | 198 | ||
203 | /* config the PLL and PHY hdmi_set_pll_pwrfirst */ | 199 | /* config the PLL and PHY hdmi_set_pll_pwrfirst */ |
204 | r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp); | 200 | r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp); |
@@ -207,7 +203,8 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) | |||
207 | goto err_pll_enable; | 203 | goto err_pll_enable; |
208 | } | 204 | } |
209 | 205 | ||
210 | r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); | 206 | r = hdmi_phy_configure(&hdmi.phy, hdmi.pll.info.clkdco, |
207 | hdmi.pll.info.clkout); | ||
211 | if (r) { | 208 | if (r) { |
212 | DSSDBG("Failed to configure PHY\n"); | 209 | DSSDBG("Failed to configure PHY\n"); |
213 | goto err_phy_cfg; | 210 | goto err_phy_cfg; |
diff --git a/drivers/video/fbdev/omap2/dss/hdmi5.c b/drivers/video/fbdev/omap2/dss/hdmi5.c index c053d692ec16..e8ca9106c8af 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi5.c +++ b/drivers/video/fbdev/omap2/dss/hdmi5.c | |||
@@ -198,7 +198,6 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) | |||
198 | int r; | 198 | int r; |
199 | struct omap_video_timings *p; | 199 | struct omap_video_timings *p; |
200 | struct omap_overlay_manager *mgr = hdmi.output.manager; | 200 | struct omap_overlay_manager *mgr = hdmi.output.manager; |
201 | unsigned long phy; | ||
202 | 201 | ||
203 | r = hdmi_power_on_core(dssdev); | 202 | r = hdmi_power_on_core(dssdev); |
204 | if (r) | 203 | if (r) |
@@ -208,10 +207,7 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) | |||
208 | 207 | ||
209 | DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); | 208 | DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); |
210 | 209 | ||
211 | /* the functions below use kHz pixel clock. TODO: change to Hz */ | 210 | hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), p->pixelclock); |
212 | phy = p->pixelclock / 1000; | ||
213 | |||
214 | hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy); | ||
215 | 211 | ||
216 | /* disable and clear irqs */ | 212 | /* disable and clear irqs */ |
217 | hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); | 213 | hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); |
@@ -225,7 +221,8 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) | |||
225 | goto err_pll_enable; | 221 | goto err_pll_enable; |
226 | } | 222 | } |
227 | 223 | ||
228 | r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); | 224 | r = hdmi_phy_configure(&hdmi.phy, hdmi.pll.info.clkdco, |
225 | hdmi.pll.info.clkout); | ||
229 | if (r) { | 226 | if (r) { |
230 | DSSDBG("Failed to start PHY\n"); | 227 | DSSDBG("Failed to start PHY\n"); |
231 | goto err_phy_cfg; | 228 | goto err_phy_cfg; |
diff --git a/drivers/video/fbdev/omap2/dss/hdmi_phy.c b/drivers/video/fbdev/omap2/dss/hdmi_phy.c index e007ac892d79..bc9e07d2afbe 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_phy.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_phy.c | |||
@@ -20,9 +20,7 @@ | |||
20 | 20 | ||
21 | struct hdmi_phy_features { | 21 | struct hdmi_phy_features { |
22 | bool bist_ctrl; | 22 | bool bist_ctrl; |
23 | bool calc_freqout; | ||
24 | bool ldo_voltage; | 23 | bool ldo_voltage; |
25 | unsigned long dcofreq_min; | ||
26 | unsigned long max_phy; | 24 | unsigned long max_phy; |
27 | }; | 25 | }; |
28 | 26 | ||
@@ -132,7 +130,8 @@ static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy) | |||
132 | REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27); | 130 | REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27); |
133 | } | 131 | } |
134 | 132 | ||
135 | int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg) | 133 | int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk, |
134 | unsigned long lfbitclk) | ||
136 | { | 135 | { |
137 | u8 freqout; | 136 | u8 freqout; |
138 | 137 | ||
@@ -149,20 +148,16 @@ int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg) | |||
149 | if (phy_feat->bist_ctrl) | 148 | if (phy_feat->bist_ctrl) |
150 | REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11); | 149 | REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11); |
151 | 150 | ||
152 | if (phy_feat->calc_freqout) { | 151 | /* |
153 | /* DCOCLK/10 is pixel clock, compare pclk with DCOCLK_MIN/10 */ | 152 | * If the hfbitclk != lfbitclk, it means the lfbitclk was configured |
154 | u32 dco_min = phy_feat->dcofreq_min / 10; | 153 | * to be used for TMDS. |
155 | u32 pclk = cfg->timings.pixelclock; | 154 | */ |
156 | 155 | if (hfbitclk != lfbitclk) | |
157 | if (pclk < dco_min) | 156 | freqout = 0; |
158 | freqout = 0; | 157 | else if (hfbitclk / 10 < phy_feat->max_phy) |
159 | else if ((pclk >= dco_min) && (pclk < phy_feat->max_phy)) | ||
160 | freqout = 1; | ||
161 | else | ||
162 | freqout = 2; | ||
163 | } else { | ||
164 | freqout = 1; | 158 | freqout = 1; |
165 | } | 159 | else |
160 | freqout = 2; | ||
166 | 161 | ||
167 | /* | 162 | /* |
168 | * Write to phy address 0 to configure the clock | 163 | * Write to phy address 0 to configure the clock |
@@ -184,17 +179,13 @@ int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg) | |||
184 | 179 | ||
185 | static const struct hdmi_phy_features omap44xx_phy_feats = { | 180 | static const struct hdmi_phy_features omap44xx_phy_feats = { |
186 | .bist_ctrl = false, | 181 | .bist_ctrl = false, |
187 | .calc_freqout = false, | ||
188 | .ldo_voltage = true, | 182 | .ldo_voltage = true, |
189 | .dcofreq_min = 500000000, | ||
190 | .max_phy = 185675000, | 183 | .max_phy = 185675000, |
191 | }; | 184 | }; |
192 | 185 | ||
193 | static const struct hdmi_phy_features omap54xx_phy_feats = { | 186 | static const struct hdmi_phy_features omap54xx_phy_feats = { |
194 | .bist_ctrl = true, | 187 | .bist_ctrl = true, |
195 | .calc_freqout = true, | ||
196 | .ldo_voltage = false, | 188 | .ldo_voltage = false, |
197 | .dcofreq_min = 750000000, | ||
198 | .max_phy = 186000000, | 189 | .max_phy = 186000000, |
199 | }; | 190 | }; |
200 | 191 | ||
diff --git a/drivers/video/fbdev/omap2/dss/hdmi_pll.c b/drivers/video/fbdev/omap2/dss/hdmi_pll.c index b28d41a08a8f..f04d435c4c0f 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_pll.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_pll.c | |||
@@ -20,14 +20,9 @@ | |||
20 | #include "dss.h" | 20 | #include "dss.h" |
21 | #include "hdmi.h" | 21 | #include "hdmi.h" |
22 | 22 | ||
23 | #define HDMI_DEFAULT_REGN 16 | ||
24 | #define HDMI_DEFAULT_REGM2 1 | ||
25 | |||
26 | struct hdmi_pll_features { | 23 | struct hdmi_pll_features { |
27 | bool has_refsel; | 24 | bool has_refsel; |
28 | bool sys_reset; | 25 | bool sys_reset; |
29 | /* this is a hack, need to replace it with a better computation of M2 */ | ||
30 | bool bound_dcofreq; | ||
31 | unsigned long fint_min, fint_max; | 26 | unsigned long fint_min, fint_max; |
32 | u16 regm_max; | 27 | u16 regm_max; |
33 | unsigned long dcofreq_low_min, dcofreq_low_max; | 28 | unsigned long dcofreq_low_min, dcofreq_low_max; |
@@ -52,55 +47,61 @@ void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s) | |||
52 | DUMPPLL(PLLCTRL_CFG4); | 47 | DUMPPLL(PLLCTRL_CFG4); |
53 | } | 48 | } |
54 | 49 | ||
55 | void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy) | 50 | void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, |
51 | unsigned long target_tmds) | ||
56 | { | 52 | { |
57 | struct hdmi_pll_info *pi = &pll->info; | 53 | struct hdmi_pll_info *pi = &pll->info; |
58 | unsigned long refclk; | 54 | unsigned long fint, clkdco, clkout; |
59 | u32 mf; | 55 | unsigned long target_bitclk, target_clkdco; |
56 | unsigned long min_dco; | ||
57 | unsigned n, m, mf, m2, sd; | ||
58 | |||
59 | DSSDBG("clkin %lu, target tmds %lu\n", clkin, target_tmds); | ||
60 | |||
61 | target_bitclk = target_tmds * 10; | ||
60 | 62 | ||
61 | /* use our funky units */ | 63 | /* Fint */ |
62 | clkin /= 10000; | 64 | n = DIV_ROUND_UP(clkin, pll_feat->fint_max); |
65 | fint = clkin / n; | ||
63 | 66 | ||
64 | /* | 67 | /* adjust m2 so that the clkdco will be high enough */ |
65 | * Input clock is predivided by N + 1 | 68 | min_dco = roundup(pll_feat->dcofreq_low_min, fint); |
66 | * out put of which is reference clk | 69 | m2 = DIV_ROUND_UP(min_dco, target_bitclk); |
67 | */ | 70 | if (m2 == 0) |
71 | m2 = 1; | ||
68 | 72 | ||
69 | pi->regn = HDMI_DEFAULT_REGN; | 73 | target_clkdco = target_bitclk * m2; |
74 | m = target_clkdco / fint; | ||
70 | 75 | ||
71 | refclk = clkin / pi->regn; | 76 | clkdco = fint * m; |
72 | 77 | ||
73 | /* temorary hack to make sure DCO freq isn't calculated too low */ | 78 | /* adjust clkdco with fractional mf */ |
74 | if (pll_feat->bound_dcofreq && phy <= 65000) | 79 | if (WARN_ON(target_clkdco - clkdco > fint)) |
75 | pi->regm2 = 3; | 80 | mf = 0; |
76 | else | 81 | else |
77 | pi->regm2 = HDMI_DEFAULT_REGM2; | 82 | mf = (u32)div_u64(262144ull * (target_clkdco - clkdco), fint); |
78 | 83 | ||
79 | /* | 84 | if (mf > 0) |
80 | * multiplier is pixel_clk/ref_clk | 85 | clkdco += (u32)div_u64((u64)mf * fint, 262144); |
81 | * Multiplying by 100 to avoid fractional part removal | ||
82 | */ | ||
83 | pi->regm = phy * pi->regm2 / refclk; | ||
84 | |||
85 | /* | ||
86 | * fractional multiplier is remainder of the difference between | ||
87 | * multiplier and actual phy(required pixel clock thus should be | ||
88 | * multiplied by 2^18(262144) divided by the reference clock | ||
89 | */ | ||
90 | mf = (phy - pi->regm / pi->regm2 * refclk) * 262144; | ||
91 | pi->regmf = pi->regm2 * mf / refclk; | ||
92 | |||
93 | /* | ||
94 | * Dcofreq should be set to 1 if required pixel clock | ||
95 | * is greater than 1000MHz | ||
96 | */ | ||
97 | pi->dcofreq = phy > 1000 * 100; | ||
98 | pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10; | ||
99 | |||
100 | DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf); | ||
101 | DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); | ||
102 | } | ||
103 | 86 | ||
87 | clkout = clkdco / m2; | ||
88 | |||
89 | /* sigma-delta */ | ||
90 | sd = DIV_ROUND_UP(fint * m, 250000000); | ||
91 | |||
92 | DSSDBG("N = %u, M = %u, M.f = %u, M2 = %u, SD = %u\n", | ||
93 | n, m, mf, m2, sd); | ||
94 | DSSDBG("Fint %lu, clkdco %lu, clkout %lu\n", fint, clkdco, clkout); | ||
95 | |||
96 | pi->regn = n; | ||
97 | pi->regm = m; | ||
98 | pi->regmf = mf; | ||
99 | pi->regm2 = m2; | ||
100 | pi->regsd = sd; | ||
101 | |||
102 | pi->clkdco = clkdco; | ||
103 | pi->clkout = clkout; | ||
104 | } | ||
104 | 105 | ||
105 | static int hdmi_pll_config(struct hdmi_pll_data *pll) | 106 | static int hdmi_pll_config(struct hdmi_pll_data *pll) |
106 | { | 107 | { |
@@ -123,7 +124,7 @@ static int hdmi_pll_config(struct hdmi_pll_data *pll) | |||
123 | if (pll_feat->has_refsel) | 124 | if (pll_feat->has_refsel) |
124 | r = FLD_MOD(r, 0x3, 22, 21); /* REFSEL = SYSCLK */ | 125 | r = FLD_MOD(r, 0x3, 22, 21); /* REFSEL = SYSCLK */ |
125 | 126 | ||
126 | if (fmt->dcofreq) | 127 | if (fmt->clkdco > pll_feat->dcofreq_low_max) |
127 | r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ | 128 | r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ |
128 | else | 129 | else |
129 | r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ | 130 | r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ |
@@ -210,7 +211,6 @@ void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) | |||
210 | 211 | ||
211 | static const struct hdmi_pll_features omap44xx_pll_feats = { | 212 | static const struct hdmi_pll_features omap44xx_pll_feats = { |
212 | .sys_reset = false, | 213 | .sys_reset = false, |
213 | .bound_dcofreq = false, | ||
214 | .fint_min = 500000, | 214 | .fint_min = 500000, |
215 | .fint_max = 2500000, | 215 | .fint_max = 2500000, |
216 | .regm_max = 4095, | 216 | .regm_max = 4095, |
@@ -223,7 +223,6 @@ static const struct hdmi_pll_features omap44xx_pll_feats = { | |||
223 | static const struct hdmi_pll_features omap54xx_pll_feats = { | 223 | static const struct hdmi_pll_features omap54xx_pll_feats = { |
224 | .has_refsel = true, | 224 | .has_refsel = true, |
225 | .sys_reset = true, | 225 | .sys_reset = true, |
226 | .bound_dcofreq = true, | ||
227 | .fint_min = 620000, | 226 | .fint_min = 620000, |
228 | .fint_max = 2500000, | 227 | .fint_max = 2500000, |
229 | .regm_max = 2046, | 228 | .regm_max = 2046, |