aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2010-07-18 07:05:54 -0400
committerEric Anholt <eric@anholt.net>2010-08-01 22:35:17 -0400
commit49be663f9952d0fc50bb0a4a75c3fd201e40ec59 (patch)
tree368f935fe959b8eabb8480ff3660334b798af025
parent71677043350874c55f60dce06a03ab61e3af6e93 (diff)
drm/i915: Refactor panel fitting on the LVDS. (v2)
Move the common routines into separate functions to not only increase readability, but also throwaway surplus code. In doing so, we review the calculation of the aspect preserving scaling and avoid the use of fixed-point until we need to calculate the accurate scale factor. v2: Improve comments as suggested by Jesse. 1 files changed, 105 insertions(+), 194 deletions(-) Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Eric Anholt <eric@anholt.net>
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c299
1 files changed, 105 insertions, 194 deletions
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 6ef9388c54d..0a2e60059fb 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -156,31 +156,73 @@ static int intel_lvds_mode_valid(struct drm_connector *connector,
156 return MODE_OK; 156 return MODE_OK;
157} 157}
158 158
159static void
160centre_horizontally(struct drm_display_mode *mode,
161 int width)
162{
163 u32 border, sync_pos, blank_width, sync_width;
164
165 /* keep the hsync and hblank widths constant */
166 sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start;
167 blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start;
168 sync_pos = (blank_width - sync_width + 1) / 2;
169
170 border = (mode->hdisplay - width + 1) / 2;
171 border += border & 1; /* make the border even */
172
173 mode->crtc_hdisplay = width;
174 mode->crtc_hblank_start = width + border;
175 mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width;
176
177 mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos;
178 mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width;
179}
180
181static void
182centre_vertically(struct drm_display_mode *mode,
183 int height)
184{
185 u32 border, sync_pos, blank_width, sync_width;
186
187 /* keep the vsync and vblank widths constant */
188 sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start;
189 blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start;
190 sync_pos = (blank_width - sync_width + 1) / 2;
191
192 border = (mode->vdisplay - height + 1) / 2;
193
194 mode->crtc_vdisplay = height;
195 mode->crtc_vblank_start = height + border;
196 mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width;
197
198 mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos;
199 mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width;
200}
201
202static inline u32 panel_fitter_scaling(u32 source, u32 target)
203{
204 /*
205 * Floating point operation is not supported. So the FACTOR
206 * is defined, which can avoid the floating point computation
207 * when calculating the panel ratio.
208 */
209#define ACCURACY 12
210#define FACTOR (1 << ACCURACY)
211 u32 ratio = source * FACTOR / target;
212 return (FACTOR * ratio + FACTOR/2) / FACTOR;
213}
214
159static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, 215static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
160 struct drm_display_mode *mode, 216 struct drm_display_mode *mode,
161 struct drm_display_mode *adjusted_mode) 217 struct drm_display_mode *adjusted_mode)
162{ 218{
163 /*
164 * float point operation is not supported . So the PANEL_RATIO_FACTOR
165 * is defined, which can avoid the float point computation when
166 * calculating the panel ratio.
167 */
168#define PANEL_RATIO_FACTOR 8192
169 struct drm_device *dev = encoder->dev; 219 struct drm_device *dev = encoder->dev;
170 struct drm_i915_private *dev_priv = dev->dev_private; 220 struct drm_i915_private *dev_priv = dev->dev_private;
171 struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 221 struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
172 struct drm_encoder *tmp_encoder; 222 struct drm_encoder *tmp_encoder;
173 struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); 223 struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
174 struct intel_lvds_priv *lvds_priv = intel_encoder->dev_priv; 224 struct intel_lvds_priv *lvds_priv = intel_encoder->dev_priv;
175 u32 pfit_control = 0, pfit_pgm_ratios = 0; 225 u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
176 int left_border = 0, right_border = 0, top_border = 0;
177 int bottom_border = 0;
178 bool border = 0;
179 int panel_ratio, desired_ratio, vert_scale, horiz_scale;
180 int horiz_ratio, vert_ratio;
181 u32 hsync_width, vsync_width;
182 u32 hblank_width, vblank_width;
183 u32 hsync_pos, vsync_pos;
184 226
185 /* Should never happen!! */ 227 /* Should never happen!! */
186 if (!IS_I965G(dev) && intel_crtc->pipe == 0) { 228 if (!IS_I965G(dev) && intel_crtc->pipe == 0) {
@@ -228,11 +270,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
228 270
229 /* Native modes don't need fitting */ 271 /* Native modes don't need fitting */
230 if (adjusted_mode->hdisplay == mode->hdisplay && 272 if (adjusted_mode->hdisplay == mode->hdisplay &&
231 adjusted_mode->vdisplay == mode->vdisplay) { 273 adjusted_mode->vdisplay == mode->vdisplay)
232 pfit_pgm_ratios = 0;
233 border = 0;
234 goto out; 274 goto out;
235 }
236 275
237 /* full screen scale for now */ 276 /* full screen scale for now */
238 if (HAS_PCH_SPLIT(dev)) 277 if (HAS_PCH_SPLIT(dev))
@@ -240,25 +279,9 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
240 279
241 /* 965+ wants fuzzy fitting */ 280 /* 965+ wants fuzzy fitting */
242 if (IS_I965G(dev)) 281 if (IS_I965G(dev))
243 pfit_control |= (intel_crtc->pipe << PFIT_PIPE_SHIFT) | 282 pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
244 PFIT_FILTER_FUZZY; 283 PFIT_FILTER_FUZZY);
245 284
246 hsync_width = adjusted_mode->crtc_hsync_end -
247 adjusted_mode->crtc_hsync_start;
248 vsync_width = adjusted_mode->crtc_vsync_end -
249 adjusted_mode->crtc_vsync_start;
250 hblank_width = adjusted_mode->crtc_hblank_end -
251 adjusted_mode->crtc_hblank_start;
252 vblank_width = adjusted_mode->crtc_vblank_end -
253 adjusted_mode->crtc_vblank_start;
254 /*
255 * Deal with panel fitting options. Figure out how to stretch the
256 * image based on its aspect ratio & the current panel fitting mode.
257 */
258 panel_ratio = adjusted_mode->hdisplay * PANEL_RATIO_FACTOR /
259 adjusted_mode->vdisplay;
260 desired_ratio = mode->hdisplay * PANEL_RATIO_FACTOR /
261 mode->vdisplay;
262 /* 285 /*
263 * Enable automatic panel scaling for non-native modes so that they fill 286 * Enable automatic panel scaling for non-native modes so that they fill
264 * the screen. Should be enabled before the pipe is enabled, according 287 * the screen. Should be enabled before the pipe is enabled, according
@@ -276,170 +299,63 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
276 * For centered modes, we have to calculate border widths & 299 * For centered modes, we have to calculate border widths &
277 * heights and modify the values programmed into the CRTC. 300 * heights and modify the values programmed into the CRTC.
278 */ 301 */
279 left_border = (adjusted_mode->hdisplay - mode->hdisplay) / 2; 302 centre_horizontally(adjusted_mode, mode->hdisplay);
280 right_border = left_border; 303 centre_vertically(adjusted_mode, mode->vdisplay);
281 if (mode->hdisplay & 1) 304 border = LVDS_BORDER_ENABLE;
282 right_border++;
283 top_border = (adjusted_mode->vdisplay - mode->vdisplay) / 2;
284 bottom_border = top_border;
285 if (mode->vdisplay & 1)
286 bottom_border++;
287 /* Set active & border values */
288 adjusted_mode->crtc_hdisplay = mode->hdisplay;
289 /* Keep the boder be even */
290 if (right_border & 1)
291 right_border++;
292 /* use the border directly instead of border minuse one */
293 adjusted_mode->crtc_hblank_start = mode->hdisplay +
294 right_border;
295 /* keep the blank width constant */
296 adjusted_mode->crtc_hblank_end =
297 adjusted_mode->crtc_hblank_start + hblank_width;
298 /* get the hsync pos relative to hblank start */
299 hsync_pos = (hblank_width - hsync_width) / 2;
300 /* keep the hsync pos be even */
301 if (hsync_pos & 1)
302 hsync_pos++;
303 adjusted_mode->crtc_hsync_start =
304 adjusted_mode->crtc_hblank_start + hsync_pos;
305 /* keep the hsync width constant */
306 adjusted_mode->crtc_hsync_end =
307 adjusted_mode->crtc_hsync_start + hsync_width;
308 adjusted_mode->crtc_vdisplay = mode->vdisplay;
309 /* use the border instead of border minus one */
310 adjusted_mode->crtc_vblank_start = mode->vdisplay +
311 bottom_border;
312 /* keep the vblank width constant */
313 adjusted_mode->crtc_vblank_end =
314 adjusted_mode->crtc_vblank_start + vblank_width;
315 /* get the vsync start postion relative to vblank start */
316 vsync_pos = (vblank_width - vsync_width) / 2;
317 adjusted_mode->crtc_vsync_start =
318 adjusted_mode->crtc_vblank_start + vsync_pos;
319 /* keep the vsync width constant */
320 adjusted_mode->crtc_vsync_end =
321 adjusted_mode->crtc_vsync_start + vsync_width;
322 border = 1;
323 break; 305 break;
306
324 case DRM_MODE_SCALE_ASPECT: 307 case DRM_MODE_SCALE_ASPECT:
325 /* Scale but preserve the spect ratio */ 308 /* Scale but preserve the aspect ratio */
326 pfit_control |= PFIT_ENABLE;
327 if (IS_I965G(dev)) { 309 if (IS_I965G(dev)) {
310 u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
311 u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
312
313 pfit_control |= PFIT_ENABLE;
328 /* 965+ is easy, it does everything in hw */ 314 /* 965+ is easy, it does everything in hw */
329 if (panel_ratio > desired_ratio) 315 if (scaled_width > scaled_height)
330 pfit_control |= PFIT_SCALING_PILLAR; 316 pfit_control |= PFIT_SCALING_PILLAR;
331 else if (panel_ratio < desired_ratio) 317 else if (scaled_width < scaled_height)
332 pfit_control |= PFIT_SCALING_LETTER; 318 pfit_control |= PFIT_SCALING_LETTER;
333 else 319 else
334 pfit_control |= PFIT_SCALING_AUTO; 320 pfit_control |= PFIT_SCALING_AUTO;
335 } else { 321 } else {
322 u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
323 u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
336 /* 324 /*
337 * For earlier chips we have to calculate the scaling 325 * For earlier chips we have to calculate the scaling
338 * ratio by hand and program it into the 326 * ratio by hand and program it into the
339 * PFIT_PGM_RATIO register 327 * PFIT_PGM_RATIO register
340 */ 328 */
341 u32 horiz_bits, vert_bits, bits = 12; 329 if (scaled_width > scaled_height) { /* pillar */
342 horiz_ratio = mode->hdisplay * PANEL_RATIO_FACTOR/ 330 centre_horizontally(adjusted_mode, scaled_height / mode->vdisplay);
343 adjusted_mode->hdisplay; 331
344 vert_ratio = mode->vdisplay * PANEL_RATIO_FACTOR/ 332 border = LVDS_BORDER_ENABLE;
345 adjusted_mode->vdisplay; 333 if (mode->vdisplay != adjusted_mode->vdisplay) {
346 horiz_scale = adjusted_mode->hdisplay * 334 u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay);
347 PANEL_RATIO_FACTOR / mode->hdisplay; 335 pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
348 vert_scale = adjusted_mode->vdisplay * 336 bits << PFIT_VERT_SCALE_SHIFT);
349 PANEL_RATIO_FACTOR / mode->vdisplay; 337 pfit_control |= (PFIT_ENABLE |
350 338 VERT_INTERP_BILINEAR |
351 /* retain aspect ratio */ 339 HORIZ_INTERP_BILINEAR);
352 if (panel_ratio > desired_ratio) { /* Pillar */ 340 }
353 u32 scaled_width; 341 } else if (scaled_width < scaled_height) { /* letter */
354 scaled_width = mode->hdisplay * vert_scale / 342 centre_vertically(adjusted_mode, scaled_width / mode->hdisplay);
355 PANEL_RATIO_FACTOR; 343
356 horiz_ratio = vert_ratio; 344 border = LVDS_BORDER_ENABLE;
357 pfit_control |= (VERT_AUTO_SCALE | 345 if (mode->hdisplay != adjusted_mode->hdisplay) {
358 VERT_INTERP_BILINEAR | 346 u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay);
359 HORIZ_INTERP_BILINEAR); 347 pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
360 /* Pillar will have left/right borders */ 348 bits << PFIT_VERT_SCALE_SHIFT);
361 left_border = (adjusted_mode->hdisplay - 349 pfit_control |= (PFIT_ENABLE |
362 scaled_width) / 2; 350 VERT_INTERP_BILINEAR |
363 right_border = left_border; 351 HORIZ_INTERP_BILINEAR);
364 if (mode->hdisplay & 1) /* odd resolutions */ 352 }
365 right_border++; 353 } else
366 /* keep the border be even */ 354 /* Aspects match, Let hw scale both directions */
367 if (right_border & 1) 355 pfit_control |= (PFIT_ENABLE |
368 right_border++; 356 VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
369 adjusted_mode->crtc_hdisplay = scaled_width;
370 /* use border instead of border minus one */
371 adjusted_mode->crtc_hblank_start =
372 scaled_width + right_border;
373 /* keep the hblank width constant */
374 adjusted_mode->crtc_hblank_end =
375 adjusted_mode->crtc_hblank_start +
376 hblank_width;
377 /*
378 * get the hsync start pos relative to
379 * hblank start
380 */
381 hsync_pos = (hblank_width - hsync_width) / 2;
382 /* keep the hsync_pos be even */
383 if (hsync_pos & 1)
384 hsync_pos++;
385 adjusted_mode->crtc_hsync_start =
386 adjusted_mode->crtc_hblank_start +
387 hsync_pos;
388 /* keept hsync width constant */
389 adjusted_mode->crtc_hsync_end =
390 adjusted_mode->crtc_hsync_start +
391 hsync_width;
392 border = 1;
393 } else if (panel_ratio < desired_ratio) { /* letter */
394 u32 scaled_height = mode->vdisplay *
395 horiz_scale / PANEL_RATIO_FACTOR;
396 vert_ratio = horiz_ratio;
397 pfit_control |= (HORIZ_AUTO_SCALE |
398 VERT_INTERP_BILINEAR | 357 VERT_INTERP_BILINEAR |
399 HORIZ_INTERP_BILINEAR); 358 HORIZ_INTERP_BILINEAR);
400 /* Letterbox will have top/bottom border */
401 top_border = (adjusted_mode->vdisplay -
402 scaled_height) / 2;
403 bottom_border = top_border;
404 if (mode->vdisplay & 1)
405 bottom_border++;
406 adjusted_mode->crtc_vdisplay = scaled_height;
407 /* use border instead of border minus one */
408 adjusted_mode->crtc_vblank_start =
409 scaled_height + bottom_border;
410 /* keep the vblank width constant */
411 adjusted_mode->crtc_vblank_end =
412 adjusted_mode->crtc_vblank_start +
413 vblank_width;
414 /*
415 * get the vsync start pos relative to
416 * vblank start
417 */
418 vsync_pos = (vblank_width - vsync_width) / 2;
419 adjusted_mode->crtc_vsync_start =
420 adjusted_mode->crtc_vblank_start +
421 vsync_pos;
422 /* keep the vsync width constant */
423 adjusted_mode->crtc_vsync_end =
424 adjusted_mode->crtc_vsync_start +
425 vsync_width;
426 border = 1;
427 } else {
428 /* Aspects match, Let hw scale both directions */
429 pfit_control |= (VERT_AUTO_SCALE |
430 HORIZ_AUTO_SCALE |
431 VERT_INTERP_BILINEAR |
432 HORIZ_INTERP_BILINEAR);
433 }
434 horiz_bits = (1 << bits) * horiz_ratio /
435 PANEL_RATIO_FACTOR;
436 vert_bits = (1 << bits) * vert_ratio /
437 PANEL_RATIO_FACTOR;
438 pfit_pgm_ratios =
439 ((vert_bits << PFIT_VERT_SCALE_SHIFT) &
440 PFIT_VERT_SCALE_MASK) |
441 ((horiz_bits << PFIT_HORIZ_SCALE_SHIFT) &
442 PFIT_HORIZ_SCALE_MASK);
443 } 359 }
444 break; 360 break;
445 361
@@ -456,6 +372,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
456 VERT_INTERP_BILINEAR | 372 VERT_INTERP_BILINEAR |
457 HORIZ_INTERP_BILINEAR); 373 HORIZ_INTERP_BILINEAR);
458 break; 374 break;
375
459 default: 376 default:
460 break; 377 break;
461 } 378 }
@@ -463,14 +380,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
463out: 380out:
464 lvds_priv->pfit_control = pfit_control; 381 lvds_priv->pfit_control = pfit_control;
465 lvds_priv->pfit_pgm_ratios = pfit_pgm_ratios; 382 lvds_priv->pfit_pgm_ratios = pfit_pgm_ratios;
466 /* 383 dev_priv->lvds_border_bits = border;
467 * When there exists the border, it means that the LVDS_BORDR 384
468 * should be enabled.
469 */
470 if (border)
471 dev_priv->lvds_border_bits |= LVDS_BORDER_ENABLE;
472 else
473 dev_priv->lvds_border_bits &= ~(LVDS_BORDER_ENABLE);
474 /* 385 /*
475 * XXX: It would be nice to support lower refresh rates on the 386 * XXX: It would be nice to support lower refresh rates on the
476 * panels to reduce power consumption, and perhaps match the 387 * panels to reduce power consumption, and perhaps match the