aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_lvds.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/intel_lvds.c')
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c331
1 files changed, 120 insertions, 211 deletions
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 0eab8df5bf7e..0a2e60059fb3 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) {
@@ -200,27 +242,25 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
200 if (dev_priv->panel_fixed_mode == NULL) 242 if (dev_priv->panel_fixed_mode == NULL)
201 return true; 243 return true;
202 /* 244 /*
203 * If we have timings from the BIOS for the panel, put them in 245 * We have timings from the BIOS for the panel, put them in
204 * to the adjusted mode. The CRTC will be set up for this mode, 246 * to the adjusted mode. The CRTC will be set up for this mode,
205 * with the panel scaling set up to source from the H/VDisplay 247 * with the panel scaling set up to source from the H/VDisplay
206 * of the original mode. 248 * of the original mode.
207 */ 249 */
208 if (dev_priv->panel_fixed_mode != NULL) { 250 adjusted_mode->hdisplay = dev_priv->panel_fixed_mode->hdisplay;
209 adjusted_mode->hdisplay = dev_priv->panel_fixed_mode->hdisplay; 251 adjusted_mode->hsync_start =
210 adjusted_mode->hsync_start = 252 dev_priv->panel_fixed_mode->hsync_start;
211 dev_priv->panel_fixed_mode->hsync_start; 253 adjusted_mode->hsync_end =
212 adjusted_mode->hsync_end = 254 dev_priv->panel_fixed_mode->hsync_end;
213 dev_priv->panel_fixed_mode->hsync_end; 255 adjusted_mode->htotal = dev_priv->panel_fixed_mode->htotal;
214 adjusted_mode->htotal = dev_priv->panel_fixed_mode->htotal; 256 adjusted_mode->vdisplay = dev_priv->panel_fixed_mode->vdisplay;
215 adjusted_mode->vdisplay = dev_priv->panel_fixed_mode->vdisplay; 257 adjusted_mode->vsync_start =
216 adjusted_mode->vsync_start = 258 dev_priv->panel_fixed_mode->vsync_start;
217 dev_priv->panel_fixed_mode->vsync_start; 259 adjusted_mode->vsync_end =
218 adjusted_mode->vsync_end = 260 dev_priv->panel_fixed_mode->vsync_end;
219 dev_priv->panel_fixed_mode->vsync_end; 261 adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal;
220 adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal; 262 adjusted_mode->clock = dev_priv->panel_fixed_mode->clock;
221 adjusted_mode->clock = dev_priv->panel_fixed_mode->clock; 263 drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
222 drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
223 }
224 264
225 /* Make sure pre-965s set dither correctly */ 265 /* Make sure pre-965s set dither correctly */
226 if (!IS_I965G(dev)) { 266 if (!IS_I965G(dev)) {
@@ -230,11 +270,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
230 270
231 /* Native modes don't need fitting */ 271 /* Native modes don't need fitting */
232 if (adjusted_mode->hdisplay == mode->hdisplay && 272 if (adjusted_mode->hdisplay == mode->hdisplay &&
233 adjusted_mode->vdisplay == mode->vdisplay) { 273 adjusted_mode->vdisplay == mode->vdisplay)
234 pfit_pgm_ratios = 0;
235 border = 0;
236 goto out; 274 goto out;
237 }
238 275
239 /* full screen scale for now */ 276 /* full screen scale for now */
240 if (HAS_PCH_SPLIT(dev)) 277 if (HAS_PCH_SPLIT(dev))
@@ -242,25 +279,9 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
242 279
243 /* 965+ wants fuzzy fitting */ 280 /* 965+ wants fuzzy fitting */
244 if (IS_I965G(dev)) 281 if (IS_I965G(dev))
245 pfit_control |= (intel_crtc->pipe << PFIT_PIPE_SHIFT) | 282 pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
246 PFIT_FILTER_FUZZY; 283 PFIT_FILTER_FUZZY);
247 284
248 hsync_width = adjusted_mode->crtc_hsync_end -
249 adjusted_mode->crtc_hsync_start;
250 vsync_width = adjusted_mode->crtc_vsync_end -
251 adjusted_mode->crtc_vsync_start;
252 hblank_width = adjusted_mode->crtc_hblank_end -
253 adjusted_mode->crtc_hblank_start;
254 vblank_width = adjusted_mode->crtc_vblank_end -
255 adjusted_mode->crtc_vblank_start;
256 /*
257 * Deal with panel fitting options. Figure out how to stretch the
258 * image based on its aspect ratio & the current panel fitting mode.
259 */
260 panel_ratio = adjusted_mode->hdisplay * PANEL_RATIO_FACTOR /
261 adjusted_mode->vdisplay;
262 desired_ratio = mode->hdisplay * PANEL_RATIO_FACTOR /
263 mode->vdisplay;
264 /* 285 /*
265 * 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
266 * the screen. Should be enabled before the pipe is enabled, according 287 * the screen. Should be enabled before the pipe is enabled, according
@@ -278,170 +299,63 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
278 * For centered modes, we have to calculate border widths & 299 * For centered modes, we have to calculate border widths &
279 * heights and modify the values programmed into the CRTC. 300 * heights and modify the values programmed into the CRTC.
280 */ 301 */
281 left_border = (adjusted_mode->hdisplay - mode->hdisplay) / 2; 302 centre_horizontally(adjusted_mode, mode->hdisplay);
282 right_border = left_border; 303 centre_vertically(adjusted_mode, mode->vdisplay);
283 if (mode->hdisplay & 1) 304 border = LVDS_BORDER_ENABLE;
284 right_border++;
285 top_border = (adjusted_mode->vdisplay - mode->vdisplay) / 2;
286 bottom_border = top_border;
287 if (mode->vdisplay & 1)
288 bottom_border++;
289 /* Set active & border values */
290 adjusted_mode->crtc_hdisplay = mode->hdisplay;
291 /* Keep the boder be even */
292 if (right_border & 1)
293 right_border++;
294 /* use the border directly instead of border minuse one */
295 adjusted_mode->crtc_hblank_start = mode->hdisplay +
296 right_border;
297 /* keep the blank width constant */
298 adjusted_mode->crtc_hblank_end =
299 adjusted_mode->crtc_hblank_start + hblank_width;
300 /* get the hsync pos relative to hblank start */
301 hsync_pos = (hblank_width - hsync_width) / 2;
302 /* keep the hsync pos be even */
303 if (hsync_pos & 1)
304 hsync_pos++;
305 adjusted_mode->crtc_hsync_start =
306 adjusted_mode->crtc_hblank_start + hsync_pos;
307 /* keep the hsync width constant */
308 adjusted_mode->crtc_hsync_end =
309 adjusted_mode->crtc_hsync_start + hsync_width;
310 adjusted_mode->crtc_vdisplay = mode->vdisplay;
311 /* use the border instead of border minus one */
312 adjusted_mode->crtc_vblank_start = mode->vdisplay +
313 bottom_border;
314 /* keep the vblank width constant */
315 adjusted_mode->crtc_vblank_end =
316 adjusted_mode->crtc_vblank_start + vblank_width;
317 /* get the vsync start postion relative to vblank start */
318 vsync_pos = (vblank_width - vsync_width) / 2;
319 adjusted_mode->crtc_vsync_start =
320 adjusted_mode->crtc_vblank_start + vsync_pos;
321 /* keep the vsync width constant */
322 adjusted_mode->crtc_vsync_end =
323 adjusted_mode->crtc_vsync_start + vsync_width;
324 border = 1;
325 break; 305 break;
306
326 case DRM_MODE_SCALE_ASPECT: 307 case DRM_MODE_SCALE_ASPECT:
327 /* Scale but preserve the spect ratio */ 308 /* Scale but preserve the aspect ratio */
328 pfit_control |= PFIT_ENABLE;
329 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;
330 /* 965+ is easy, it does everything in hw */ 314 /* 965+ is easy, it does everything in hw */
331 if (panel_ratio > desired_ratio) 315 if (scaled_width > scaled_height)
332 pfit_control |= PFIT_SCALING_PILLAR; 316 pfit_control |= PFIT_SCALING_PILLAR;
333 else if (panel_ratio < desired_ratio) 317 else if (scaled_width < scaled_height)
334 pfit_control |= PFIT_SCALING_LETTER; 318 pfit_control |= PFIT_SCALING_LETTER;
335 else 319 else
336 pfit_control |= PFIT_SCALING_AUTO; 320 pfit_control |= PFIT_SCALING_AUTO;
337 } else { 321 } else {
322 u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
323 u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
338 /* 324 /*
339 * For earlier chips we have to calculate the scaling 325 * For earlier chips we have to calculate the scaling
340 * ratio by hand and program it into the 326 * ratio by hand and program it into the
341 * PFIT_PGM_RATIO register 327 * PFIT_PGM_RATIO register
342 */ 328 */
343 u32 horiz_bits, vert_bits, bits = 12; 329 if (scaled_width > scaled_height) { /* pillar */
344 horiz_ratio = mode->hdisplay * PANEL_RATIO_FACTOR/ 330 centre_horizontally(adjusted_mode, scaled_height / mode->vdisplay);
345 adjusted_mode->hdisplay; 331
346 vert_ratio = mode->vdisplay * PANEL_RATIO_FACTOR/ 332 border = LVDS_BORDER_ENABLE;
347 adjusted_mode->vdisplay; 333 if (mode->vdisplay != adjusted_mode->vdisplay) {
348 horiz_scale = adjusted_mode->hdisplay * 334 u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay);
349 PANEL_RATIO_FACTOR / mode->hdisplay; 335 pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
350 vert_scale = adjusted_mode->vdisplay * 336 bits << PFIT_VERT_SCALE_SHIFT);
351 PANEL_RATIO_FACTOR / mode->vdisplay; 337 pfit_control |= (PFIT_ENABLE |
352 338 VERT_INTERP_BILINEAR |
353 /* retain aspect ratio */ 339 HORIZ_INTERP_BILINEAR);
354 if (panel_ratio > desired_ratio) { /* Pillar */ 340 }
355 u32 scaled_width; 341 } else if (scaled_width < scaled_height) { /* letter */
356 scaled_width = mode->hdisplay * vert_scale / 342 centre_vertically(adjusted_mode, scaled_width / mode->hdisplay);
357 PANEL_RATIO_FACTOR; 343
358 horiz_ratio = vert_ratio; 344 border = LVDS_BORDER_ENABLE;
359 pfit_control |= (VERT_AUTO_SCALE | 345 if (mode->hdisplay != adjusted_mode->hdisplay) {
360 VERT_INTERP_BILINEAR | 346 u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay);
361 HORIZ_INTERP_BILINEAR); 347 pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
362 /* Pillar will have left/right borders */ 348 bits << PFIT_VERT_SCALE_SHIFT);
363 left_border = (adjusted_mode->hdisplay - 349 pfit_control |= (PFIT_ENABLE |
364 scaled_width) / 2; 350 VERT_INTERP_BILINEAR |
365 right_border = left_border; 351 HORIZ_INTERP_BILINEAR);
366 if (mode->hdisplay & 1) /* odd resolutions */ 352 }
367 right_border++; 353 } else
368 /* keep the border be even */ 354 /* Aspects match, Let hw scale both directions */
369 if (right_border & 1) 355 pfit_control |= (PFIT_ENABLE |
370 right_border++; 356 VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
371 adjusted_mode->crtc_hdisplay = scaled_width;
372 /* use border instead of border minus one */
373 adjusted_mode->crtc_hblank_start =
374 scaled_width + right_border;
375 /* keep the hblank width constant */
376 adjusted_mode->crtc_hblank_end =
377 adjusted_mode->crtc_hblank_start +
378 hblank_width;
379 /*
380 * get the hsync start pos relative to
381 * hblank start
382 */
383 hsync_pos = (hblank_width - hsync_width) / 2;
384 /* keep the hsync_pos be even */
385 if (hsync_pos & 1)
386 hsync_pos++;
387 adjusted_mode->crtc_hsync_start =
388 adjusted_mode->crtc_hblank_start +
389 hsync_pos;
390 /* keept hsync width constant */
391 adjusted_mode->crtc_hsync_end =
392 adjusted_mode->crtc_hsync_start +
393 hsync_width;
394 border = 1;
395 } else if (panel_ratio < desired_ratio) { /* letter */
396 u32 scaled_height = mode->vdisplay *
397 horiz_scale / PANEL_RATIO_FACTOR;
398 vert_ratio = horiz_ratio;
399 pfit_control |= (HORIZ_AUTO_SCALE |
400 VERT_INTERP_BILINEAR |
401 HORIZ_INTERP_BILINEAR);
402 /* Letterbox will have top/bottom border */
403 top_border = (adjusted_mode->vdisplay -
404 scaled_height) / 2;
405 bottom_border = top_border;
406 if (mode->vdisplay & 1)
407 bottom_border++;
408 adjusted_mode->crtc_vdisplay = scaled_height;
409 /* use border instead of border minus one */
410 adjusted_mode->crtc_vblank_start =
411 scaled_height + bottom_border;
412 /* keep the vblank width constant */
413 adjusted_mode->crtc_vblank_end =
414 adjusted_mode->crtc_vblank_start +
415 vblank_width;
416 /*
417 * get the vsync start pos relative to
418 * vblank start
419 */
420 vsync_pos = (vblank_width - vsync_width) / 2;
421 adjusted_mode->crtc_vsync_start =
422 adjusted_mode->crtc_vblank_start +
423 vsync_pos;
424 /* keep the vsync width constant */
425 adjusted_mode->crtc_vsync_end =
426 adjusted_mode->crtc_vsync_start +
427 vsync_width;
428 border = 1;
429 } else {
430 /* Aspects match, Let hw scale both directions */
431 pfit_control |= (VERT_AUTO_SCALE |
432 HORIZ_AUTO_SCALE |
433 VERT_INTERP_BILINEAR | 357 VERT_INTERP_BILINEAR |
434 HORIZ_INTERP_BILINEAR); 358 HORIZ_INTERP_BILINEAR);
435 }
436 horiz_bits = (1 << bits) * horiz_ratio /
437 PANEL_RATIO_FACTOR;
438 vert_bits = (1 << bits) * vert_ratio /
439 PANEL_RATIO_FACTOR;
440 pfit_pgm_ratios =
441 ((vert_bits << PFIT_VERT_SCALE_SHIFT) &
442 PFIT_VERT_SCALE_MASK) |
443 ((horiz_bits << PFIT_HORIZ_SCALE_SHIFT) &
444 PFIT_HORIZ_SCALE_MASK);
445 } 359 }
446 break; 360 break;
447 361
@@ -458,6 +372,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
458 VERT_INTERP_BILINEAR | 372 VERT_INTERP_BILINEAR |
459 HORIZ_INTERP_BILINEAR); 373 HORIZ_INTERP_BILINEAR);
460 break; 374 break;
375
461 default: 376 default:
462 break; 377 break;
463 } 378 }
@@ -465,14 +380,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
465out: 380out:
466 lvds_priv->pfit_control = pfit_control; 381 lvds_priv->pfit_control = pfit_control;
467 lvds_priv->pfit_pgm_ratios = pfit_pgm_ratios; 382 lvds_priv->pfit_pgm_ratios = pfit_pgm_ratios;
468 /* 383 dev_priv->lvds_border_bits = border;
469 * When there exists the border, it means that the LVDS_BORDR 384
470 * should be enabled.
471 */
472 if (border)
473 dev_priv->lvds_border_bits |= LVDS_BORDER_ENABLE;
474 else
475 dev_priv->lvds_border_bits &= ~(LVDS_BORDER_ENABLE);
476 /* 385 /*
477 * 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
478 * panels to reduce power consumption, and perhaps match the 387 * panels to reduce power consumption, and perhaps match the