diff options
Diffstat (limited to 'drivers/gpu/drm/i915')
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 100 |
1 files changed, 88 insertions, 12 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 89f7af0bdb12..8e29545273b6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c | |||
@@ -56,11 +56,13 @@ typedef struct { | |||
56 | } intel_p2_t; | 56 | } intel_p2_t; |
57 | 57 | ||
58 | #define INTEL_P2_NUM 2 | 58 | #define INTEL_P2_NUM 2 |
59 | 59 | typedef struct intel_limit intel_limit_t; | |
60 | typedef struct { | 60 | struct intel_limit { |
61 | intel_range_t dot, vco, n, m, m1, m2, p, p1; | 61 | intel_range_t dot, vco, n, m, m1, m2, p, p1; |
62 | intel_p2_t p2; | 62 | intel_p2_t p2; |
63 | } intel_limit_t; | 63 | bool (* find_pll)(const intel_limit_t *, struct drm_crtc *, |
64 | int, int, intel_clock_t *); | ||
65 | }; | ||
64 | 66 | ||
65 | #define I8XX_DOT_MIN 25000 | 67 | #define I8XX_DOT_MIN 25000 |
66 | #define I8XX_DOT_MAX 350000 | 68 | #define I8XX_DOT_MAX 350000 |
@@ -198,6 +200,12 @@ typedef struct { | |||
198 | #define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7 | 200 | #define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7 |
199 | #define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0 | 201 | #define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0 |
200 | 202 | ||
203 | static bool | ||
204 | intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, | ||
205 | int target, int refclk, intel_clock_t *best_clock); | ||
206 | static bool | ||
207 | intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, | ||
208 | int target, int refclk, intel_clock_t *best_clock); | ||
201 | 209 | ||
202 | static const intel_limit_t intel_limits[] = { | 210 | static const intel_limit_t intel_limits[] = { |
203 | { /* INTEL_LIMIT_I8XX_DVO_DAC */ | 211 | { /* INTEL_LIMIT_I8XX_DVO_DAC */ |
@@ -211,6 +219,7 @@ static const intel_limit_t intel_limits[] = { | |||
211 | .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX }, | 219 | .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX }, |
212 | .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, | 220 | .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, |
213 | .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST }, | 221 | .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST }, |
222 | .find_pll = intel_find_best_PLL, | ||
214 | }, | 223 | }, |
215 | { /* INTEL_LIMIT_I8XX_LVDS */ | 224 | { /* INTEL_LIMIT_I8XX_LVDS */ |
216 | .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, | 225 | .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, |
@@ -223,6 +232,7 @@ static const intel_limit_t intel_limits[] = { | |||
223 | .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX }, | 232 | .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX }, |
224 | .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, | 233 | .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, |
225 | .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST }, | 234 | .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST }, |
235 | .find_pll = intel_find_best_PLL, | ||
226 | }, | 236 | }, |
227 | { /* INTEL_LIMIT_I9XX_SDVO_DAC */ | 237 | { /* INTEL_LIMIT_I9XX_SDVO_DAC */ |
228 | .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, | 238 | .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, |
@@ -235,6 +245,7 @@ static const intel_limit_t intel_limits[] = { | |||
235 | .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, | 245 | .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, |
236 | .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, | 246 | .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, |
237 | .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST }, | 247 | .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST }, |
248 | .find_pll = intel_find_best_PLL, | ||
238 | }, | 249 | }, |
239 | { /* INTEL_LIMIT_I9XX_LVDS */ | 250 | { /* INTEL_LIMIT_I9XX_LVDS */ |
240 | .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, | 251 | .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, |
@@ -250,6 +261,7 @@ static const intel_limit_t intel_limits[] = { | |||
250 | */ | 261 | */ |
251 | .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, | 262 | .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, |
252 | .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST }, | 263 | .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST }, |
264 | .find_pll = intel_find_best_PLL, | ||
253 | }, | 265 | }, |
254 | /* below parameter and function is for G4X Chipset Family*/ | 266 | /* below parameter and function is for G4X Chipset Family*/ |
255 | { /* INTEL_LIMIT_G4X_SDVO */ | 267 | { /* INTEL_LIMIT_G4X_SDVO */ |
@@ -265,6 +277,7 @@ static const intel_limit_t intel_limits[] = { | |||
265 | .p2_slow = G4X_P2_SDVO_SLOW, | 277 | .p2_slow = G4X_P2_SDVO_SLOW, |
266 | .p2_fast = G4X_P2_SDVO_FAST | 278 | .p2_fast = G4X_P2_SDVO_FAST |
267 | }, | 279 | }, |
280 | .find_pll = intel_g4x_find_best_PLL, | ||
268 | }, | 281 | }, |
269 | { /* INTEL_LIMIT_G4X_HDMI_DAC */ | 282 | { /* INTEL_LIMIT_G4X_HDMI_DAC */ |
270 | .dot = { .min = G4X_DOT_HDMI_DAC_MIN, .max = G4X_DOT_HDMI_DAC_MAX }, | 283 | .dot = { .min = G4X_DOT_HDMI_DAC_MIN, .max = G4X_DOT_HDMI_DAC_MAX }, |
@@ -279,6 +292,7 @@ static const intel_limit_t intel_limits[] = { | |||
279 | .p2_slow = G4X_P2_HDMI_DAC_SLOW, | 292 | .p2_slow = G4X_P2_HDMI_DAC_SLOW, |
280 | .p2_fast = G4X_P2_HDMI_DAC_FAST | 293 | .p2_fast = G4X_P2_HDMI_DAC_FAST |
281 | }, | 294 | }, |
295 | .find_pll = intel_g4x_find_best_PLL, | ||
282 | }, | 296 | }, |
283 | { /* INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS */ | 297 | { /* INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS */ |
284 | .dot = { .min = G4X_DOT_SINGLE_CHANNEL_LVDS_MIN, | 298 | .dot = { .min = G4X_DOT_SINGLE_CHANNEL_LVDS_MIN, |
@@ -301,6 +315,7 @@ static const intel_limit_t intel_limits[] = { | |||
301 | .p2_slow = G4X_P2_SINGLE_CHANNEL_LVDS_SLOW, | 315 | .p2_slow = G4X_P2_SINGLE_CHANNEL_LVDS_SLOW, |
302 | .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST | 316 | .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST |
303 | }, | 317 | }, |
318 | .find_pll = intel_g4x_find_best_PLL, | ||
304 | }, | 319 | }, |
305 | { /* INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS */ | 320 | { /* INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS */ |
306 | .dot = { .min = G4X_DOT_DUAL_CHANNEL_LVDS_MIN, | 321 | .dot = { .min = G4X_DOT_DUAL_CHANNEL_LVDS_MIN, |
@@ -323,6 +338,7 @@ static const intel_limit_t intel_limits[] = { | |||
323 | .p2_slow = G4X_P2_DUAL_CHANNEL_LVDS_SLOW, | 338 | .p2_slow = G4X_P2_DUAL_CHANNEL_LVDS_SLOW, |
324 | .p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST | 339 | .p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST |
325 | }, | 340 | }, |
341 | .find_pll = intel_g4x_find_best_PLL, | ||
326 | }, | 342 | }, |
327 | }; | 343 | }; |
328 | 344 | ||
@@ -437,18 +453,14 @@ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock) | |||
437 | return true; | 453 | return true; |
438 | } | 454 | } |
439 | 455 | ||
440 | /** | 456 | static bool |
441 | * Returns a set of divisors for the desired target clock with the given | 457 | intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, |
442 | * refclk, or FALSE. The returned values represent the clock equation: | 458 | int target, int refclk, intel_clock_t *best_clock) |
443 | * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. | 459 | |
444 | */ | ||
445 | static bool intel_find_best_PLL(struct drm_crtc *crtc, int target, | ||
446 | int refclk, intel_clock_t *best_clock) | ||
447 | { | 460 | { |
448 | struct drm_device *dev = crtc->dev; | 461 | struct drm_device *dev = crtc->dev; |
449 | struct drm_i915_private *dev_priv = dev->dev_private; | 462 | struct drm_i915_private *dev_priv = dev->dev_private; |
450 | intel_clock_t clock; | 463 | intel_clock_t clock; |
451 | const intel_limit_t *limit = intel_limit(crtc); | ||
452 | int err = target; | 464 | int err = target; |
453 | 465 | ||
454 | if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && | 466 | if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && |
@@ -500,6 +512,63 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target, | |||
500 | return (err != target); | 512 | return (err != target); |
501 | } | 513 | } |
502 | 514 | ||
515 | static bool | ||
516 | intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, | ||
517 | int target, int refclk, intel_clock_t *best_clock) | ||
518 | { | ||
519 | struct drm_device *dev = crtc->dev; | ||
520 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
521 | intel_clock_t clock; | ||
522 | int max_n; | ||
523 | bool found; | ||
524 | /* approximately equals target * 0.00488 */ | ||
525 | int err_most = (target >> 8) + (target >> 10); | ||
526 | found = false; | ||
527 | |||
528 | if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { | ||
529 | if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == | ||
530 | LVDS_CLKB_POWER_UP) | ||
531 | clock.p2 = limit->p2.p2_fast; | ||
532 | else | ||
533 | clock.p2 = limit->p2.p2_slow; | ||
534 | } else { | ||
535 | if (target < limit->p2.dot_limit) | ||
536 | clock.p2 = limit->p2.p2_slow; | ||
537 | else | ||
538 | clock.p2 = limit->p2.p2_fast; | ||
539 | } | ||
540 | |||
541 | memset(best_clock, 0, sizeof(*best_clock)); | ||
542 | max_n = limit->n.max; | ||
543 | /* based on hardware requriment prefer smaller n to precision */ | ||
544 | for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { | ||
545 | /* based on hardware requirment prefere larger m1,m2, p1 */ | ||
546 | for (clock.m1 = limit->m1.max; | ||
547 | clock.m1 >= limit->m1.min; clock.m1--) { | ||
548 | for (clock.m2 = limit->m2.max; | ||
549 | clock.m2 >= limit->m2.min; clock.m2--) { | ||
550 | for (clock.p1 = limit->p1.max; | ||
551 | clock.p1 >= limit->p1.min; clock.p1--) { | ||
552 | int this_err; | ||
553 | |||
554 | intel_clock(refclk, &clock); | ||
555 | if (!intel_PLL_is_valid(crtc, &clock)) | ||
556 | continue; | ||
557 | this_err = abs(clock.dot - target) ; | ||
558 | if (this_err < err_most) { | ||
559 | *best_clock = clock; | ||
560 | err_most = this_err; | ||
561 | max_n = clock.n; | ||
562 | found = true; | ||
563 | } | ||
564 | } | ||
565 | } | ||
566 | } | ||
567 | } | ||
568 | |||
569 | return found; | ||
570 | } | ||
571 | |||
503 | void | 572 | void |
504 | intel_wait_for_vblank(struct drm_device *dev) | 573 | intel_wait_for_vblank(struct drm_device *dev) |
505 | { | 574 | { |
@@ -918,6 +987,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, | |||
918 | bool is_crt = false, is_lvds = false, is_tv = false; | 987 | bool is_crt = false, is_lvds = false, is_tv = false; |
919 | struct drm_mode_config *mode_config = &dev->mode_config; | 988 | struct drm_mode_config *mode_config = &dev->mode_config; |
920 | struct drm_connector *connector; | 989 | struct drm_connector *connector; |
990 | const intel_limit_t *limit; | ||
921 | int ret; | 991 | int ret; |
922 | 992 | ||
923 | drm_vblank_pre_modeset(dev, pipe); | 993 | drm_vblank_pre_modeset(dev, pipe); |
@@ -961,7 +1031,13 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, | |||
961 | refclk = 48000; | 1031 | refclk = 48000; |
962 | } | 1032 | } |
963 | 1033 | ||
964 | ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock); | 1034 | /* |
1035 | * Returns a set of divisors for the desired target clock with the given | ||
1036 | * refclk, or FALSE. The returned values represent the clock equation: | ||
1037 | * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. | ||
1038 | */ | ||
1039 | limit = intel_limit(crtc); | ||
1040 | ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock); | ||
965 | if (!ok) { | 1041 | if (!ok) { |
966 | DRM_ERROR("Couldn't find PLL settings for mode!\n"); | 1042 | DRM_ERROR("Couldn't find PLL settings for mode!\n"); |
967 | return -EINVAL; | 1043 | return -EINVAL; |