diff options
author | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2013-03-05 10:07:16 -0500 |
---|---|---|
committer | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2013-04-03 08:19:18 -0400 |
commit | 100c826235793345efe06b3558cc9d36166b1e26 (patch) | |
tree | 04ed26d04565ce34381a00554f0eca9bdc6c3b7a | |
parent | 36816faadff37ac7d29be20471d37a2b938ece5d (diff) |
OMAPDSS: DPI: use new clock calculation code
Use the new clock calculation code in the DPI driver.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
-rw-r--r-- | drivers/video/omap2/dss/dpi.c | 212 |
1 files changed, 172 insertions, 40 deletions
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index c8c7776c8cfa..abe1a4e23c36 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c | |||
@@ -105,31 +105,170 @@ static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel) | |||
105 | } | 105 | } |
106 | } | 106 | } |
107 | 107 | ||
108 | struct dpi_clk_calc_ctx { | ||
109 | struct platform_device *dsidev; | ||
110 | |||
111 | /* inputs */ | ||
112 | |||
113 | unsigned long pck_min, pck_max; | ||
114 | |||
115 | /* outputs */ | ||
116 | |||
117 | struct dsi_clock_info dsi_cinfo; | ||
118 | struct dss_clock_info dss_cinfo; | ||
119 | struct dispc_clock_info dispc_cinfo; | ||
120 | }; | ||
121 | |||
122 | static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck, | ||
123 | unsigned long pck, void *data) | ||
124 | { | ||
125 | struct dpi_clk_calc_ctx *ctx = data; | ||
126 | |||
127 | /* | ||
128 | * Odd dividers give us uneven duty cycle, causing problem when level | ||
129 | * shifted. So skip all odd dividers when the pixel clock is on the | ||
130 | * higher side. | ||
131 | */ | ||
132 | if (ctx->pck_min >= 1000000) { | ||
133 | if (lckd > 1 && lckd % 2 != 0) | ||
134 | return false; | ||
135 | |||
136 | if (pckd > 1 && pckd % 2 != 0) | ||
137 | return false; | ||
138 | } | ||
139 | |||
140 | ctx->dispc_cinfo.lck_div = lckd; | ||
141 | ctx->dispc_cinfo.pck_div = pckd; | ||
142 | ctx->dispc_cinfo.lck = lck; | ||
143 | ctx->dispc_cinfo.pck = pck; | ||
144 | |||
145 | return true; | ||
146 | } | ||
147 | |||
148 | |||
149 | static bool dpi_calc_hsdiv_cb(int regm_dispc, unsigned long dispc, | ||
150 | void *data) | ||
151 | { | ||
152 | struct dpi_clk_calc_ctx *ctx = data; | ||
153 | |||
154 | /* | ||
155 | * Odd dividers give us uneven duty cycle, causing problem when level | ||
156 | * shifted. So skip all odd dividers when the pixel clock is on the | ||
157 | * higher side. | ||
158 | */ | ||
159 | if (regm_dispc > 1 && regm_dispc % 2 != 0 && ctx->pck_min >= 1000000) | ||
160 | return false; | ||
161 | |||
162 | ctx->dsi_cinfo.regm_dispc = regm_dispc; | ||
163 | ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc; | ||
164 | |||
165 | return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max, | ||
166 | dpi_calc_dispc_cb, ctx); | ||
167 | } | ||
168 | |||
169 | |||
170 | static bool dpi_calc_pll_cb(int regn, int regm, unsigned long fint, | ||
171 | unsigned long pll, | ||
172 | void *data) | ||
173 | { | ||
174 | struct dpi_clk_calc_ctx *ctx = data; | ||
175 | |||
176 | ctx->dsi_cinfo.regn = regn; | ||
177 | ctx->dsi_cinfo.regm = regm; | ||
178 | ctx->dsi_cinfo.fint = fint; | ||
179 | ctx->dsi_cinfo.clkin4ddr = pll; | ||
180 | |||
181 | return dsi_hsdiv_calc(ctx->dsidev, pll, ctx->pck_min, | ||
182 | dpi_calc_hsdiv_cb, ctx); | ||
183 | } | ||
184 | |||
185 | static bool dpi_calc_dss_cb(int fckd, unsigned long fck, void *data) | ||
186 | { | ||
187 | struct dpi_clk_calc_ctx *ctx = data; | ||
188 | |||
189 | ctx->dss_cinfo.fck = fck; | ||
190 | ctx->dss_cinfo.fck_div = fckd; | ||
191 | |||
192 | return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max, | ||
193 | dpi_calc_dispc_cb, ctx); | ||
194 | } | ||
195 | |||
196 | static bool dpi_dsi_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx) | ||
197 | { | ||
198 | unsigned long clkin; | ||
199 | unsigned long pll_min, pll_max; | ||
200 | |||
201 | clkin = dsi_get_pll_clkin(dpi.dsidev); | ||
202 | |||
203 | memset(ctx, 0, sizeof(*ctx)); | ||
204 | ctx->dsidev = dpi.dsidev; | ||
205 | ctx->pck_min = pck - 1000; | ||
206 | ctx->pck_max = pck + 1000; | ||
207 | ctx->dsi_cinfo.clkin = clkin; | ||
208 | |||
209 | pll_min = 0; | ||
210 | pll_max = 0; | ||
211 | |||
212 | return dsi_pll_calc(dpi.dsidev, clkin, | ||
213 | pll_min, pll_max, | ||
214 | dpi_calc_pll_cb, ctx); | ||
215 | } | ||
216 | |||
217 | static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx) | ||
218 | { | ||
219 | int i; | ||
220 | |||
221 | /* | ||
222 | * DSS fck gives us very few possibilities, so finding a good pixel | ||
223 | * clock may not be possible. We try multiple times to find the clock, | ||
224 | * each time widening the pixel clock range we look for, up to | ||
225 | * +/- 1MHz. | ||
226 | */ | ||
227 | |||
228 | for (i = 0; i < 10; ++i) { | ||
229 | bool ok; | ||
230 | |||
231 | memset(ctx, 0, sizeof(*ctx)); | ||
232 | if (pck > 1000 * i * i * i) | ||
233 | ctx->pck_min = max(pck - 1000 * i * i * i, 0lu); | ||
234 | else | ||
235 | ctx->pck_min = 0; | ||
236 | ctx->pck_max = pck + 1000 * i * i * i; | ||
237 | |||
238 | ok = dss_div_calc(ctx->pck_min, dpi_calc_dss_cb, ctx); | ||
239 | if (ok) | ||
240 | return ok; | ||
241 | } | ||
242 | |||
243 | return false; | ||
244 | } | ||
245 | |||
246 | |||
247 | |||
108 | static int dpi_set_dsi_clk(enum omap_channel channel, | 248 | static int dpi_set_dsi_clk(enum omap_channel channel, |
109 | unsigned long pck_req, unsigned long *fck, int *lck_div, | 249 | unsigned long pck_req, unsigned long *fck, int *lck_div, |
110 | int *pck_div) | 250 | int *pck_div) |
111 | { | 251 | { |
112 | struct dsi_clock_info dsi_cinfo; | 252 | struct dpi_clk_calc_ctx ctx; |
113 | struct dispc_clock_info dispc_cinfo; | ||
114 | int r; | 253 | int r; |
254 | bool ok; | ||
115 | 255 | ||
116 | r = dsi_pll_calc_clock_div_pck(dpi.dsidev, pck_req, &dsi_cinfo, | 256 | ok = dpi_dsi_clk_calc(pck_req, &ctx); |
117 | &dispc_cinfo); | 257 | if (!ok) |
118 | if (r) | 258 | return -EINVAL; |
119 | return r; | ||
120 | 259 | ||
121 | r = dsi_pll_set_clock_div(dpi.dsidev, &dsi_cinfo); | 260 | r = dsi_pll_set_clock_div(dpi.dsidev, &ctx.dsi_cinfo); |
122 | if (r) | 261 | if (r) |
123 | return r; | 262 | return r; |
124 | 263 | ||
125 | dss_select_lcd_clk_source(channel, | 264 | dss_select_lcd_clk_source(channel, |
126 | dpi_get_alt_clk_src(channel)); | 265 | dpi_get_alt_clk_src(channel)); |
127 | 266 | ||
128 | dpi.mgr_config.clock_info = dispc_cinfo; | 267 | dpi.mgr_config.clock_info = ctx.dispc_cinfo; |
129 | 268 | ||
130 | *fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk; | 269 | *fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk; |
131 | *lck_div = dispc_cinfo.lck_div; | 270 | *lck_div = ctx.dispc_cinfo.lck_div; |
132 | *pck_div = dispc_cinfo.pck_div; | 271 | *pck_div = ctx.dispc_cinfo.pck_div; |
133 | 272 | ||
134 | return 0; | 273 | return 0; |
135 | } | 274 | } |
@@ -137,23 +276,23 @@ static int dpi_set_dsi_clk(enum omap_channel channel, | |||
137 | static int dpi_set_dispc_clk(unsigned long pck_req, unsigned long *fck, | 276 | static int dpi_set_dispc_clk(unsigned long pck_req, unsigned long *fck, |
138 | int *lck_div, int *pck_div) | 277 | int *lck_div, int *pck_div) |
139 | { | 278 | { |
140 | struct dss_clock_info dss_cinfo; | 279 | struct dpi_clk_calc_ctx ctx; |
141 | struct dispc_clock_info dispc_cinfo; | ||
142 | int r; | 280 | int r; |
281 | bool ok; | ||
143 | 282 | ||
144 | r = dss_calc_clock_div(pck_req, &dss_cinfo, &dispc_cinfo); | 283 | ok = dpi_dss_clk_calc(pck_req, &ctx); |
145 | if (r) | 284 | if (!ok) |
146 | return r; | 285 | return -EINVAL; |
147 | 286 | ||
148 | r = dss_set_clock_div(&dss_cinfo); | 287 | r = dss_set_clock_div(&ctx.dss_cinfo); |
149 | if (r) | 288 | if (r) |
150 | return r; | 289 | return r; |
151 | 290 | ||
152 | dpi.mgr_config.clock_info = dispc_cinfo; | 291 | dpi.mgr_config.clock_info = ctx.dispc_cinfo; |
153 | 292 | ||
154 | *fck = dss_cinfo.fck; | 293 | *fck = ctx.dss_cinfo.fck; |
155 | *lck_div = dispc_cinfo.lck_div; | 294 | *lck_div = ctx.dispc_cinfo.lck_div; |
156 | *pck_div = dispc_cinfo.pck_div; | 295 | *pck_div = ctx.dispc_cinfo.pck_div; |
157 | 296 | ||
158 | return 0; | 297 | return 0; |
159 | } | 298 | } |
@@ -333,12 +472,12 @@ EXPORT_SYMBOL(omapdss_dpi_set_timings); | |||
333 | int dpi_check_timings(struct omap_dss_device *dssdev, | 472 | int dpi_check_timings(struct omap_dss_device *dssdev, |
334 | struct omap_video_timings *timings) | 473 | struct omap_video_timings *timings) |
335 | { | 474 | { |
336 | int r; | ||
337 | struct omap_overlay_manager *mgr = dpi.output.manager; | 475 | struct omap_overlay_manager *mgr = dpi.output.manager; |
338 | int lck_div, pck_div; | 476 | int lck_div, pck_div; |
339 | unsigned long fck; | 477 | unsigned long fck; |
340 | unsigned long pck; | 478 | unsigned long pck; |
341 | struct dispc_clock_info dispc_cinfo; | 479 | struct dpi_clk_calc_ctx ctx; |
480 | bool ok; | ||
342 | 481 | ||
343 | if (mgr && !dispc_mgr_timings_ok(mgr->id, timings)) | 482 | if (mgr && !dispc_mgr_timings_ok(mgr->id, timings)) |
344 | return -EINVAL; | 483 | return -EINVAL; |
@@ -347,28 +486,21 @@ int dpi_check_timings(struct omap_dss_device *dssdev, | |||
347 | return -EINVAL; | 486 | return -EINVAL; |
348 | 487 | ||
349 | if (dpi.dsidev) { | 488 | if (dpi.dsidev) { |
350 | struct dsi_clock_info dsi_cinfo; | 489 | ok = dpi_dsi_clk_calc(timings->pixel_clock * 1000, &ctx); |
351 | r = dsi_pll_calc_clock_div_pck(dpi.dsidev, | 490 | if (!ok) |
352 | timings->pixel_clock * 1000, | 491 | return -EINVAL; |
353 | &dsi_cinfo, &dispc_cinfo); | ||
354 | 492 | ||
355 | if (r) | 493 | fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk; |
356 | return r; | ||
357 | |||
358 | fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk; | ||
359 | } else { | 494 | } else { |
360 | struct dss_clock_info dss_cinfo; | 495 | ok = dpi_dss_clk_calc(timings->pixel_clock * 1000, &ctx); |
361 | r = dss_calc_clock_div(timings->pixel_clock * 1000, | 496 | if (!ok) |
362 | &dss_cinfo, &dispc_cinfo); | 497 | return -EINVAL; |
363 | |||
364 | if (r) | ||
365 | return r; | ||
366 | 498 | ||
367 | fck = dss_cinfo.fck; | 499 | fck = ctx.dss_cinfo.fck; |
368 | } | 500 | } |
369 | 501 | ||
370 | lck_div = dispc_cinfo.lck_div; | 502 | lck_div = ctx.dispc_cinfo.lck_div; |
371 | pck_div = dispc_cinfo.pck_div; | 503 | pck_div = ctx.dispc_cinfo.pck_div; |
372 | 504 | ||
373 | pck = fck / lck_div / pck_div / 1000; | 505 | pck = fck / lck_div / pck_div / 1000; |
374 | 506 | ||