aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915')
-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