diff options
-rw-r--r-- | drivers/gpu/drm/i915/intel_lvds.c | 299 |
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 | ||
159 | static void | ||
160 | centre_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 | |||
181 | static void | ||
182 | centre_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 | |||
202 | static 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 | |||
159 | static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, | 215 | static 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, | |||
463 | out: | 380 | out: |
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 |