From 481b6af3d1f36d4a19bd36321c1e9f713db49aad Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 23 Aug 2010 17:43:35 +0100 Subject: drm/i915: Drop the msleep parameter to wait_for() Jesse's feedback from using the wait_for() macro was that the msleep argument was that it was superfluous and made the macro more difficult to use and to read. As the actually amount of time to sleep is not critical, the crucial part is to sleep and let the processor schedule something else whilst we wait for the event, replace the argument with a hardcoded value. Signed-off-by: Chris Wilson Cc: Jesse Barnes --- drivers/gpu/drm/i915/intel_lvds.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 4fbb0165b26f..fe79c5a2740c 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -114,7 +114,7 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); - if (wait_for(I915_READ(status_reg) & PP_ON, 1000, 0)) + if (wait_for(I915_READ(status_reg) & PP_ON, 1000)) DRM_ERROR("timed out waiting to enable LVDS pipe"); intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle); @@ -123,7 +123,7 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); - if (wait_for((I915_READ(status_reg) & PP_ON) == 0, 1000, 0)) + if (wait_for((I915_READ(status_reg) & PP_ON) == 0, 1000)) DRM_ERROR("timed out waiting for LVDS pipe to turn off"); I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); -- cgit From 425904dd8a86d9ca3a3be38eaaa12b4844dceed6 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 22 Aug 2010 18:21:42 +0100 Subject: drm/i915: Addin-offset is an unreliable indicator of LVDS presence (v2) My Samsung N210 has a VBT with DEVICE_TYPE_INT_LFP with a zero addin-offset. With the check in place, the panel was declared absent. v2: Only trust BIOS writers that have graduated to writing OpRegions. (We are all doomed.) Signed-off-by: Chris Wilson Cc: Zhao Yakui Cc: Adam Jackson Reviewed-by: Adam Jackson --- drivers/gpu/drm/i915/intel_lvds.c | 46 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 20 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index fe79c5a2740c..047bd9538c6a 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -777,38 +777,44 @@ static void intel_find_lvds_downclock(struct drm_device *dev, * If it is present, return 1. * If it is not present, return false. * If no child dev is parsed from VBT, it assumes that the LVDS is present. - * Note: The addin_offset should also be checked for LVDS panel. - * Only when it is non-zero, it is assumed that it is present. */ -static int lvds_is_present_in_vbt(struct drm_device *dev) +static bool lvds_is_present_in_vbt(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct child_device_config *p_child; - int i, ret; + int i; if (!dev_priv->child_dev_num) - return 1; + return true; - ret = 0; for (i = 0; i < dev_priv->child_dev_num; i++) { - p_child = dev_priv->child_dev + i; - /* - * If the device type is not LFP, continue. - * If the device type is 0x22, it is also regarded as LFP. + struct child_device_config *child = dev_priv->child_dev + i; + + /* If the device type is not LFP, continue. + * We have to check both the new identifiers as well as the + * old for compatibility with some BIOSes. */ - if (p_child->device_type != DEVICE_TYPE_INT_LFP && - p_child->device_type != DEVICE_TYPE_LFP) + if (child->device_type != DEVICE_TYPE_INT_LFP && + child->device_type != DEVICE_TYPE_LFP) continue; - /* The addin_offset should be checked. Only when it is - * non-zero, it is regarded as present. + /* However, we cannot trust the BIOS writers to populate + * the VBT correctly. Since LVDS requires additional + * information from AIM blocks, a non-zero addin offset is + * a good indicator that the LVDS is actually present. */ - if (p_child->addin_offset) { - ret = 1; - break; - } + if (child->addin_offset) + return true; + + /* But even then some BIOS writers perform some black magic + * and instantiate the device without reference to any + * additional data. Trust that if the VBT was written into + * the OpRegion then they have validated the LVDS's existence. + */ + if (dev_priv->opregion.vbt) + return true; } - return ret; + + return false; } /** -- cgit From a95735569312f2ab0c80425e2cd1e5cb0b4e1870 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 22 Aug 2010 13:18:16 +0100 Subject: drm/i915: Refactor panel backlight controls There were two instances of code to control the panel backlight and neither handled the complete set of device variations. Fixes: Bug 29716 - [GM965] Regression: Backlight resets to minimum when changing resolution https://bugs.freedesktop.org/show_bug.cgi?id=29716 And a bug on one of my PineView boxes which overflowed the backlight value. Incorporates part of a similar patch by Matthew Garrett that exposes a native Intel backlight controller. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 56 ++++----------------------------------- 1 file changed, 5 insertions(+), 51 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 047bd9538c6a..8320279fad58 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -53,43 +53,6 @@ static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder) return container_of(enc_to_intel_encoder(encoder), struct intel_lvds, base); } -/** - * Sets the backlight level. - * - * \param level backlight level, from 0 to intel_lvds_get_max_backlight(). - */ -static void intel_lvds_set_backlight(struct drm_device *dev, int level) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 blc_pwm_ctl, reg; - - if (HAS_PCH_SPLIT(dev)) - reg = BLC_PWM_CPU_CTL; - else - reg = BLC_PWM_CTL; - - blc_pwm_ctl = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK; - I915_WRITE(reg, (blc_pwm_ctl | - (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); -} - -/** - * Returns the maximum level of the backlight duty cycle field. - */ -static u32 intel_lvds_get_max_backlight(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 reg; - - if (HAS_PCH_SPLIT(dev)) - reg = BLC_PWM_PCH_CTL2; - else - reg = BLC_PWM_CTL; - - return ((I915_READ(reg) & BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; -} - /** * Sets the power state for the panel. */ @@ -117,9 +80,9 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) if (wait_for(I915_READ(status_reg) & PP_ON, 1000)) DRM_ERROR("timed out waiting to enable LVDS pipe"); - intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle); + intel_panel_set_backlight(dev, dev_priv->backlight_level); } else { - intel_lvds_set_backlight(dev, 0); + intel_panel_set_backlight(dev, 0); I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); @@ -386,16 +349,8 @@ static void intel_lvds_prepare(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 reg; - - if (HAS_PCH_SPLIT(dev)) - reg = BLC_PWM_CPU_CTL; - else - reg = BLC_PWM_CTL; - dev_priv->saveBLC_PWM_CTL = I915_READ(reg); - dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & - BACKLIGHT_DUTY_CYCLE_MASK); + dev_priv->backlight_level = intel_panel_get_backlight(dev); intel_lvds_set_power(dev, false); } @@ -405,9 +360,8 @@ static void intel_lvds_commit( struct drm_encoder *encoder) struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - if (dev_priv->backlight_duty_cycle == 0) - dev_priv->backlight_duty_cycle = - intel_lvds_get_max_backlight(dev); + if (dev_priv->backlight_level == 0) + dev_priv->backlight_level = intel_panel_get_max_backlight(dev); intel_lvds_set_power(dev, true); } -- cgit From 309b1e3ab750c0ad4d77c6a6e434402e3346baf4 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 18 May 2010 13:53:16 -0400 Subject: drm/i915: Don't disable panel for modesetting if pfit hasn't changed It seems to be possible to program a new mode without disabling the panel if the panel fitter setup doesn't change. Add support for that. Signed-off-by: Matthew Garrett Acked-by: Jesse Barnes Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 8320279fad58..ef6455104ff1 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -53,6 +53,16 @@ static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder) return container_of(enc_to_intel_encoder(encoder), struct intel_lvds, base); } +static void intel_lvds_lock_panel(struct drm_device *dev, bool lock) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (lock) + I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3); + else + I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); +} + /** * Sets the power state for the panel. */ @@ -349,10 +359,14 @@ static void intel_lvds_prepare(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); dev_priv->backlight_level = intel_panel_get_backlight(dev); - intel_lvds_set_power(dev, false); + if (intel_lvds->pfit_control == I915_READ(PFIT_CONTROL)) + intel_lvds_lock_panel(dev, false); + else + intel_lvds_set_power(dev, false); } static void intel_lvds_commit( struct drm_encoder *encoder) @@ -363,7 +377,10 @@ static void intel_lvds_commit( struct drm_encoder *encoder) if (dev_priv->backlight_level == 0) dev_priv->backlight_level = intel_panel_get_max_backlight(dev); - intel_lvds_set_power(dev, true); + if ((I915_READ(PP_CONTROL) & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS) + intel_lvds_lock_panel(dev, true); + else + intel_lvds_set_power(dev, true); } static void intel_lvds_mode_set(struct drm_encoder *encoder, -- cgit From 4ef69c7a64b78d477d1666eba258ca049e8bac91 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 9 Sep 2010 15:14:28 +0100 Subject: drm/i915: Rename intel_encoder->enc to base for consistency [Patch is slightly larger than is strictly necessary to fixup surrounding checkpatch.pl errors.] Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes --- drivers/gpu/drm/i915/intel_lvds.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index ef6455104ff1..ed1c87636814 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -50,7 +50,7 @@ struct intel_lvds { static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder) { - return container_of(enc_to_intel_encoder(encoder), struct intel_lvds, base); + return container_of(encoder, struct intel_lvds, base.base); } static void intel_lvds_lock_panel(struct drm_device *dev, bool lock) @@ -437,7 +437,7 @@ static int intel_lvds_get_modes(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); struct drm_i915_private *dev_priv = dev->dev_private; int ret = 0; @@ -839,15 +839,15 @@ void intel_lvds_init(struct drm_device *dev) } intel_encoder = &intel_lvds->base; - encoder = &intel_encoder->enc; + encoder = &intel_encoder->base; connector = &intel_connector->base; drm_connector_init(dev, &intel_connector->base, &intel_lvds_connector_funcs, DRM_MODE_CONNECTOR_LVDS); - drm_encoder_init(dev, &intel_encoder->enc, &intel_lvds_enc_funcs, + drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS); - drm_mode_connector_attach_encoder(&intel_connector->base, &intel_encoder->enc); + drm_mode_connector_attach_encoder(&intel_connector->base, &intel_encoder->base); intel_encoder->type = INTEL_OUTPUT_LVDS; intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT); -- cgit From f875c15a4fbf37534dda30771d8bde8604fbbf09 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 9 Sep 2010 15:44:14 +0100 Subject: drm/i915: Use the direct mapping of pipe->crtc Why iterate all the crtcs to find the pipe, when we already know which crtc is attached to which pipe? Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes --- drivers/gpu/drm/i915/intel_lvds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index ed1c87636814..987973f4ff7d 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -932,7 +932,7 @@ void intel_lvds_init(struct drm_device *dev) lvds = I915_READ(LVDS); pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; - crtc = intel_get_crtc_from_pipe(dev, pipe); + crtc = intel_get_crtc_for_pipe(dev, pipe); if (crtc && (lvds & LVDS_PORT_EN)) { dev_priv->panel_fixed_mode = intel_crtc_mode_get(dev, crtc); -- cgit From df0e924883d029a8651a2a0c7b8da67a07611ed2 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 9 Sep 2010 16:20:55 +0100 Subject: drm/i915: Make the connector->encoder relationship explicit Currently we have a exact mapping of a connector onto an encoder for its whole lifetime. Make this an explicit property of the structure and so simplify the code. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes --- drivers/gpu/drm/i915/intel_lvds.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 987973f4ff7d..93a711d9dcf5 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -436,14 +436,11 @@ static enum drm_connector_status intel_lvds_detect(struct drm_connector *connect static int intel_lvds_get_modes(struct drm_connector *connector) { struct drm_device *dev = connector->dev; - struct drm_encoder *encoder = intel_attached_encoder(connector); - struct intel_encoder *intel_encoder = to_intel_encoder(encoder); struct drm_i915_private *dev_priv = dev->dev_private; - int ret = 0; if (dev_priv->lvds_edid_good) { - ret = intel_ddc_get_modes(connector, intel_encoder->ddc_bus); - + struct intel_encoder *encoder = intel_attached_encoder(connector); + int ret = intel_ddc_get_modes(connector, encoder->ddc_bus); if (ret) return ret; } @@ -596,7 +593,7 @@ static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { .get_modes = intel_lvds_get_modes, .mode_valid = intel_lvds_mode_valid, - .best_encoder = intel_attached_encoder, + .best_encoder = intel_best_encoder, }; static const struct drm_connector_funcs intel_lvds_connector_funcs = { @@ -847,7 +844,7 @@ void intel_lvds_init(struct drm_device *dev) drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS); - drm_mode_connector_attach_encoder(&intel_connector->base, &intel_encoder->base); + intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_LVDS; intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT); -- cgit From 77d07fd9d73ef28689737c0952dbd5d6a5017743 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 12 Sep 2010 12:42:35 +0100 Subject: drm/i915/lvds: Remove busy wait for powering down the panel Just assume that it will turn off... Reported-by: Sitsofe Wheeler Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 93a711d9dcf5..f533169e5d8b 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -83,10 +83,7 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) if (on) { I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN); - POSTING_READ(lvds_reg); - - I915_WRITE(ctl_reg, I915_READ(ctl_reg) | - POWER_TARGET_ON); + I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); if (wait_for(I915_READ(status_reg) & PP_ON, 1000)) DRM_ERROR("timed out waiting to enable LVDS pipe"); @@ -94,11 +91,7 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) } else { intel_panel_set_backlight(dev, 0); - I915_WRITE(ctl_reg, I915_READ(ctl_reg) & - ~POWER_TARGET_ON); - if (wait_for((I915_READ(status_reg) & PP_ON) == 0, 1000)) - DRM_ERROR("timed out waiting for LVDS pipe to turn off"); - + I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); POSTING_READ(lvds_reg); } -- cgit From c9f9ccc150e119bab6a1003e7762b024623011d8 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 12 Sep 2010 13:07:25 +0100 Subject: drm/i915/lvds: Remove busy wait for powering up the panel. We just assume that it will happen in a timely manner. A variant of this patch was first written and tested by Arjan van de Van. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index f533169e5d8b..9089604011f9 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -84,17 +84,13 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) if (on) { I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN); I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); - if (wait_for(I915_READ(status_reg) & PP_ON, 1000)) - DRM_ERROR("timed out waiting to enable LVDS pipe"); - intel_panel_set_backlight(dev, dev_priv->backlight_level); } else { intel_panel_set_backlight(dev, 0); - I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); - POSTING_READ(lvds_reg); } + POSTING_READ(lvds_reg); } static void intel_lvds_dpms(struct drm_encoder *encoder, int mode) -- cgit From e9e331a8abeece1565d383510ed985945132ffe3 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 13 Sep 2010 01:16:10 +0100 Subject: drm/i915/lvds: Ensure panel is unlocked for Ironlake or the panel fitter Commit 77d07fd9d73ef28689737c0952dbd5d6a5017743 introduced a regression where by not waiting for the panel to be turned off, left the panel and PLL registers locked across the modeset. Thus the panel remaining blank. As pointed out by Daniel Vetter, when testing LVDS it helps to open the laptop and look at the actual panel you are purporting to test. A second issue with the patch was that in order to modify the panel fitter before gen5, the pipe and the panel must have be completely powered down. So we wait. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 103 +++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 28 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 9089604011f9..bfc1bb443b05 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -46,6 +46,7 @@ struct intel_lvds { int fitting_mode; u32 pfit_control; u32 pfit_pgm_ratios; + bool pfit_dirty; }; static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder) @@ -53,31 +54,20 @@ static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder) return container_of(encoder, struct intel_lvds, base.base); } -static void intel_lvds_lock_panel(struct drm_device *dev, bool lock) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - if (lock) - I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3); - else - I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); -} - /** * Sets the power state for the panel. */ -static void intel_lvds_set_power(struct drm_device *dev, bool on) +static void intel_lvds_set_power(struct intel_lvds *intel_lvds, bool on) { + struct drm_device *dev = intel_lvds->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 ctl_reg, status_reg, lvds_reg; + u32 ctl_reg, lvds_reg; if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; - status_reg = PCH_PP_STATUS; lvds_reg = PCH_LVDS; } else { ctl_reg = PP_CONTROL; - status_reg = PP_STATUS; lvds_reg = LVDS; } @@ -86,8 +76,18 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); intel_panel_set_backlight(dev, dev_priv->backlight_level); } else { + dev_priv->backlight_level = intel_panel_get_backlight(dev); + intel_panel_set_backlight(dev, 0); I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); + + if (intel_lvds->pfit_control) { + if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000)) + DRM_ERROR("timed out waiting for panel to power off\n"); + I915_WRITE(PFIT_CONTROL, 0); + intel_lvds->pfit_control = 0; + } + I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); } POSTING_READ(lvds_reg); @@ -95,12 +95,12 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) static void intel_lvds_dpms(struct drm_encoder *encoder, int mode) { - struct drm_device *dev = encoder->dev; + struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); if (mode == DRM_MODE_DPMS_ON) - intel_lvds_set_power(dev, true); + intel_lvds_set_power(intel_lvds, true); else - intel_lvds_set_power(dev, false); + intel_lvds_set_power(intel_lvds, false); /* XXX: We never power down the LVDS pairs. */ } @@ -331,8 +331,12 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, } out: - intel_lvds->pfit_control = pfit_control; - intel_lvds->pfit_pgm_ratios = pfit_pgm_ratios; + if (pfit_control != intel_lvds->pfit_control || + pfit_pgm_ratios != intel_lvds->pfit_pgm_ratios) { + intel_lvds->pfit_control = pfit_control; + intel_lvds->pfit_pgm_ratios = pfit_pgm_ratios; + intel_lvds->pfit_dirty = true; + } dev_priv->lvds_border_bits = border; /* @@ -352,24 +356,56 @@ static void intel_lvds_prepare(struct drm_encoder *encoder) dev_priv->backlight_level = intel_panel_get_backlight(dev); - if (intel_lvds->pfit_control == I915_READ(PFIT_CONTROL)) - intel_lvds_lock_panel(dev, false); - else - intel_lvds_set_power(dev, false); + /* We try to do the minimum that is necessary in order to unlock + * the registers for mode setting. + * + * On Ironlake, this is quite simple as we just set the unlock key + * and ignore all subtleties. (This may cause some issues...) + * + * Prior to Ironlake, we must disable the pipe if we want to adjust + * the panel fitter. However at all other times we can just reset + * the registers regardless. + */ + + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(PCH_PP_CONTROL, + I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); + } else if (intel_lvds->pfit_dirty) { + I915_WRITE(PP_CONTROL, + I915_READ(PP_CONTROL) & ~POWER_TARGET_ON); + I915_WRITE(LVDS, I915_READ(LVDS) & ~LVDS_PORT_EN); + } else { + I915_WRITE(PP_CONTROL, + I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); + } } -static void intel_lvds_commit( struct drm_encoder *encoder) +static void intel_lvds_commit(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); if (dev_priv->backlight_level == 0) dev_priv->backlight_level = intel_panel_get_max_backlight(dev); - if ((I915_READ(PP_CONTROL) & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS) - intel_lvds_lock_panel(dev, true); - else - intel_lvds_set_power(dev, true); + /* Undo any unlocking done in prepare to prevent accidental + * adjustment of the registers. + */ + if (HAS_PCH_SPLIT(dev)) { + u32 val = I915_READ(PCH_PP_CONTROL); + if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS) + I915_WRITE(PCH_PP_CONTROL, val & 0x3); + } else { + u32 val = I915_READ(PP_CONTROL); + if ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS) + I915_WRITE(PP_CONTROL, val & 0x3); + } + + /* Always do a full power on as we do not know what state + * we were left in. + */ + intel_lvds_set_power(intel_lvds, true); } static void intel_lvds_mode_set(struct drm_encoder *encoder, @@ -389,13 +425,20 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder, if (HAS_PCH_SPLIT(dev)) return; + if (!intel_lvds->pfit_dirty) + return; + /* * Enable automatic panel scaling so that non-native modes fill the * screen. Should be enabled before the pipe is enabled, according to * register description and PRM. */ + if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000)) + DRM_ERROR("timed out waiting for panel to power off\n"); + I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios); I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control); + intel_lvds->pfit_dirty = false; } /** @@ -824,6 +867,10 @@ void intel_lvds_init(struct drm_device *dev) return; } + if (!HAS_PCH_SPLIT(dev)) { + intel_lvds->pfit_control = I915_READ(PFIT_CONTROL); + } + intel_encoder = &intel_lvds->base; encoder = &intel_encoder->base; connector = &intel_connector->base; -- cgit From 8aadf70bd72c8f15994e68503af8f6722cd5c813 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 12 Sep 2010 16:33:47 +0100 Subject: drm/i915/lvds: Remove incorrect mode locking One doesn't need to hold the mode lock in order to duplicate a mode. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index bfc1bb443b05..a05ca3286782 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -732,7 +732,6 @@ static void intel_find_lvds_downclock(struct drm_device *dev, panel_fixed_mode = dev_priv->panel_fixed_mode; temp_downclock = panel_fixed_mode->clock; - mutex_lock(&dev->mode_config.mutex); list_for_each_entry(scan, &connector->probed_modes, head) { /* * If one mode has the same resolution with the fixed_panel @@ -758,7 +757,6 @@ static void intel_find_lvds_downclock(struct drm_device *dev, } } } - mutex_unlock(&dev->mode_config.mutex); if (temp_downclock < panel_fixed_mode->clock && i915_lvds_downclock) { /* We found the downclock for LVDS. */ @@ -929,23 +927,18 @@ void intel_lvds_init(struct drm_device *dev) dev_priv->lvds_edid_good = false; list_for_each_entry(scan, &connector->probed_modes, head) { - mutex_lock(&dev->mode_config.mutex); if (scan->type & DRM_MODE_TYPE_PREFERRED) { dev_priv->panel_fixed_mode = drm_mode_duplicate(dev, scan); - mutex_unlock(&dev->mode_config.mutex); intel_find_lvds_downclock(dev, connector); goto out; } - mutex_unlock(&dev->mode_config.mutex); } /* Failed to get EDID, what about VBT? */ if (dev_priv->lfp_lvds_vbt_mode) { - mutex_lock(&dev->mode_config.mutex); dev_priv->panel_fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); - mutex_unlock(&dev->mode_config.mutex); if (dev_priv->panel_fixed_mode) { dev_priv->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; -- cgit From 788319d48dc2b61db732b19bb9598c062c75ec37 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 12 Sep 2010 17:34:41 +0100 Subject: drm/i915/lvds: Move private data to the connector from the device. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 155 +++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 77 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index a05ca3286782..b56b59236e31 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -43,17 +43,28 @@ /* Private structure for the integrated LVDS support */ struct intel_lvds { struct intel_encoder base; + + bool edid_good; + int fitting_mode; u32 pfit_control; u32 pfit_pgm_ratios; bool pfit_dirty; + + struct drm_display_mode *fixed_mode; }; -static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder) +static struct intel_lvds *to_intel_lvds(struct drm_encoder *encoder) { return container_of(encoder, struct intel_lvds, base.base); } +static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector) +{ + return container_of(intel_attached_encoder(connector), + struct intel_lvds, base); +} + /** * Sets the power state for the panel. */ @@ -95,7 +106,7 @@ static void intel_lvds_set_power(struct intel_lvds *intel_lvds, bool on) static void intel_lvds_dpms(struct drm_encoder *encoder, int mode) { - struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); + struct intel_lvds *intel_lvds = to_intel_lvds(encoder); if (mode == DRM_MODE_DPMS_ON) intel_lvds_set_power(intel_lvds, true); @@ -108,16 +119,13 @@ static void intel_lvds_dpms(struct drm_encoder *encoder, int mode) static int intel_lvds_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode; + struct intel_lvds *intel_lvds = intel_attached_lvds(connector); + struct drm_display_mode *fixed_mode = intel_lvds->fixed_mode; - if (fixed_mode) { - if (mode->hdisplay > fixed_mode->hdisplay) - return MODE_PANEL; - if (mode->vdisplay > fixed_mode->vdisplay) - return MODE_PANEL; - } + if (mode->hdisplay > fixed_mode->hdisplay) + return MODE_PANEL; + if (mode->vdisplay > fixed_mode->vdisplay) + return MODE_PANEL; return MODE_OK; } @@ -185,7 +193,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); - struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); + struct intel_lvds *intel_lvds = to_intel_lvds(encoder); struct drm_encoder *tmp_encoder; u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; @@ -203,9 +211,6 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, return false; } } - /* If we don't have a panel mode, there is nothing we can do */ - if (dev_priv->panel_fixed_mode == NULL) - return true; /* * We have timings from the BIOS for the panel, put them in @@ -213,7 +218,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, * with the panel scaling set up to source from the H/VDisplay * of the original mode. */ - intel_fixed_panel_mode(dev_priv->panel_fixed_mode, adjusted_mode); + intel_fixed_panel_mode(intel_lvds->fixed_mode, adjusted_mode); if (HAS_PCH_SPLIT(dev)) { intel_pch_panel_fitting(dev, intel_lvds->fitting_mode, @@ -352,7 +357,7 @@ static void intel_lvds_prepare(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); + struct intel_lvds *intel_lvds = to_intel_lvds(encoder); dev_priv->backlight_level = intel_panel_get_backlight(dev); @@ -384,7 +389,7 @@ static void intel_lvds_commit(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); + struct intel_lvds *intel_lvds = to_intel_lvds(encoder); if (dev_priv->backlight_level == 0) dev_priv->backlight_level = intel_panel_get_max_backlight(dev); @@ -414,7 +419,7 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder, { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); + struct intel_lvds *intel_lvds = to_intel_lvds(encoder); /* * The LVDS pin pair will already have been turned on in the @@ -467,35 +472,23 @@ static enum drm_connector_status intel_lvds_detect(struct drm_connector *connect */ static int intel_lvds_get_modes(struct drm_connector *connector) { + struct intel_lvds *intel_lvds = intel_attached_lvds(connector); struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_display_mode *mode; - if (dev_priv->lvds_edid_good) { - struct intel_encoder *encoder = intel_attached_encoder(connector); - int ret = intel_ddc_get_modes(connector, encoder->ddc_bus); + if (intel_lvds->edid_good) { + int ret = intel_ddc_get_modes(connector, + intel_lvds->base.ddc_bus); if (ret) return ret; } - /* Didn't get an EDID, so - * Set wide sync ranges so we get all modes - * handed to valid_mode for checking - */ - connector->display_info.min_vfreq = 0; - connector->display_info.max_vfreq = 200; - connector->display_info.min_hfreq = 0; - connector->display_info.max_hfreq = 200; - - if (dev_priv->panel_fixed_mode != NULL) { - struct drm_display_mode *mode; + mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode); + if (mode == 0) + return 0; - mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); - drm_mode_probed_add(connector, mode); - - return 1; - } - - return 0; + drm_mode_probed_add(connector, mode); + return 1; } static int intel_no_modeset_on_lid_dmi_callback(const struct dmi_system_id *id) @@ -584,18 +577,17 @@ static int intel_lvds_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t value) { + struct intel_lvds *intel_lvds = intel_attached_lvds(connector); struct drm_device *dev = connector->dev; - if (property == dev->mode_config.scaling_mode_property && - connector->encoder) { - struct drm_crtc *crtc = connector->encoder->crtc; - struct drm_encoder *encoder = connector->encoder; - struct intel_lvds *intel_lvds = enc_to_intel_lvds(encoder); + if (property == dev->mode_config.scaling_mode_property) { + struct drm_crtc *crtc = intel_lvds->base.base.crtc; if (value == DRM_MODE_SCALE_NONE) { DRM_DEBUG_KMS("no scaling not supported\n"); - return 0; + return -EINVAL; } + if (intel_lvds->fitting_mode == value) { /* the LVDS scaling property is not changed */ return 0; @@ -723,15 +715,14 @@ static const struct dmi_system_id intel_no_lvds[] = { * Find the reduced downclock for LVDS in EDID. */ static void intel_find_lvds_downclock(struct drm_device *dev, - struct drm_connector *connector) + struct drm_display_mode *fixed_mode, + struct drm_connector *connector) { struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_display_mode *scan, *panel_fixed_mode; + struct drm_display_mode *scan; int temp_downclock; - panel_fixed_mode = dev_priv->panel_fixed_mode; - temp_downclock = panel_fixed_mode->clock; - + temp_downclock = fixed_mode->clock; list_for_each_entry(scan, &connector->probed_modes, head) { /* * If one mode has the same resolution with the fixed_panel @@ -740,14 +731,14 @@ static void intel_find_lvds_downclock(struct drm_device *dev, * case we can set the different FPx0/1 to dynamically select * between low and high frequency. */ - if (scan->hdisplay == panel_fixed_mode->hdisplay && - scan->hsync_start == panel_fixed_mode->hsync_start && - scan->hsync_end == panel_fixed_mode->hsync_end && - scan->htotal == panel_fixed_mode->htotal && - scan->vdisplay == panel_fixed_mode->vdisplay && - scan->vsync_start == panel_fixed_mode->vsync_start && - scan->vsync_end == panel_fixed_mode->vsync_end && - scan->vtotal == panel_fixed_mode->vtotal) { + if (scan->hdisplay == fixed_mode->hdisplay && + scan->hsync_start == fixed_mode->hsync_start && + scan->hsync_end == fixed_mode->hsync_end && + scan->htotal == fixed_mode->htotal && + scan->vdisplay == fixed_mode->vdisplay && + scan->vsync_start == fixed_mode->vsync_start && + scan->vsync_end == fixed_mode->vsync_end && + scan->vtotal == fixed_mode->vtotal) { if (scan->clock < temp_downclock) { /* * The downclock is already found. But we @@ -757,16 +748,14 @@ static void intel_find_lvds_downclock(struct drm_device *dev, } } } - if (temp_downclock < panel_fixed_mode->clock && - i915_lvds_downclock) { + if (temp_downclock < fixed_mode->clock && i915_lvds_downclock) { /* We found the downclock for LVDS. */ dev_priv->lvds_downclock_avail = 1; dev_priv->lvds_downclock = temp_downclock; DRM_DEBUG_KMS("LVDS downclock is found in EDID. " - "Normal clock %dKhz, downclock %dKhz\n", - panel_fixed_mode->clock, temp_downclock); + "Normal clock %dKhz, downclock %dKhz\n", + fixed_mode->clock, temp_downclock); } - return; } /* @@ -921,26 +910,38 @@ void intel_lvds_init(struct drm_device *dev) * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ - dev_priv->lvds_edid_good = true; - + intel_lvds->edid_good = true; if (!intel_ddc_get_modes(connector, intel_encoder->ddc_bus)) - dev_priv->lvds_edid_good = false; + intel_lvds->edid_good = false; + + if (!intel_lvds->edid_good) { + /* Didn't get an EDID, so + * Set wide sync ranges so we get all modes + * handed to valid_mode for checking + */ + connector->display_info.min_vfreq = 0; + connector->display_info.max_vfreq = 200; + connector->display_info.min_hfreq = 0; + connector->display_info.max_hfreq = 200; + } list_for_each_entry(scan, &connector->probed_modes, head) { if (scan->type & DRM_MODE_TYPE_PREFERRED) { - dev_priv->panel_fixed_mode = + intel_lvds->fixed_mode = drm_mode_duplicate(dev, scan); - intel_find_lvds_downclock(dev, connector); + intel_find_lvds_downclock(dev, + intel_lvds->fixed_mode, + connector); goto out; } } /* Failed to get EDID, what about VBT? */ if (dev_priv->lfp_lvds_vbt_mode) { - dev_priv->panel_fixed_mode = + intel_lvds->fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); - if (dev_priv->panel_fixed_mode) { - dev_priv->panel_fixed_mode->type |= + if (intel_lvds->fixed_mode) { + intel_lvds->fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; goto out; } @@ -961,16 +962,16 @@ void intel_lvds_init(struct drm_device *dev) crtc = intel_get_crtc_for_pipe(dev, pipe); if (crtc && (lvds & LVDS_PORT_EN)) { - dev_priv->panel_fixed_mode = intel_crtc_mode_get(dev, crtc); - if (dev_priv->panel_fixed_mode) { - dev_priv->panel_fixed_mode->type |= + intel_lvds->fixed_mode = intel_crtc_mode_get(dev, crtc); + if (intel_lvds->fixed_mode) { + intel_lvds->fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; goto out; } } /* If we still don't have a mode after all that, give up. */ - if (!dev_priv->panel_fixed_mode) + if (!intel_lvds->fixed_mode) goto failed; out: -- cgit From 890f3359f7b84d7015104360d647ccac5f515542 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 14 Sep 2010 16:46:59 +0100 Subject: drm/i915/i2c: Track the parent encoder rather than just the dev The SDVO proxy i2c adapter wants to be able to use information stored in the encoder, so pass that through intel_i2c rather than iterate over all known encoders every time. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index b56b59236e31..2ff4a5cb2d56 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -899,7 +899,8 @@ void intel_lvds_init(struct drm_device *dev) */ /* Set up the DDC bus. */ - intel_encoder->ddc_bus = intel_i2c_create(dev, gpio, "LVDSDDC_C"); + intel_encoder->ddc_bus = intel_i2c_create(intel_encoder, + gpio, "LVDSDDC_C"); if (!intel_encoder->ddc_bus) { dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " "failed.\n"); -- cgit From f899fc64cda8569d0529452aafc0da31c042df2e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 20 Jul 2010 15:44:45 -0700 Subject: drm/i915: use GMBUS to manage i2c links Use the GMBUS interface rather than direct bit banging to grab the EDID over DDC (and for other forms of auxiliary communication with external display controllers). The hope is that this method will be much faster and more reliable than bit banging for fetching EDIDs from buggy monitors or through switches, though we still preserve the bit banging as a fallback in case GMBUS fails. Based on an original patch by Jesse Barnes. Cc: Jesse Barnes Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 2ff4a5cb2d56..9177c17853e5 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -474,11 +474,12 @@ static int intel_lvds_get_modes(struct drm_connector *connector) { struct intel_lvds *intel_lvds = intel_attached_lvds(connector); struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_display_mode *mode; if (intel_lvds->edid_good) { int ret = intel_ddc_get_modes(connector, - intel_lvds->base.ddc_bus); + &dev_priv->gmbus[GMBUS_PORT_PANEL].adapter); if (ret) return ret; } @@ -898,21 +899,12 @@ void intel_lvds_init(struct drm_device *dev) * if closed, act like it's not there for now */ - /* Set up the DDC bus. */ - intel_encoder->ddc_bus = intel_i2c_create(intel_encoder, - gpio, "LVDSDDC_C"); - if (!intel_encoder->ddc_bus) { - dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " - "failed.\n"); - goto failed; - } - /* * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ intel_lvds->edid_good = true; - if (!intel_ddc_get_modes(connector, intel_encoder->ddc_bus)) + if (!intel_ddc_get_modes(connector, &dev_priv->gmbus[GMBUS_PORT_PANEL].adapter)) intel_lvds->edid_good = false; if (!intel_lvds->edid_good) { @@ -999,8 +991,6 @@ out: failed: DRM_DEBUG_KMS("No LVDS modes found, disabling.\n"); - if (intel_encoder->ddc_bus) - intel_i2c_destroy(intel_encoder->ddc_bus); drm_connector_cleanup(connector); drm_encoder_cleanup(encoder); kfree(intel_lvds); -- cgit From 219adae138513bae20b256f1946b9cb3b75ca05c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 16 Sep 2010 23:05:10 +0100 Subject: drm/i915: Cache LVDS EDID We assume that the panel is permenantly connected and that the EDID data is consistent from boot, so simply cache the whole EDID for the panel. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index f6a72cbb152d..5666e89288d3 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -44,7 +44,7 @@ struct intel_lvds { struct intel_encoder base; - bool edid_good; + struct edid *edid; int fitting_mode; u32 pfit_control; @@ -475,14 +475,12 @@ static int intel_lvds_get_modes(struct drm_connector *connector) { struct intel_lvds *intel_lvds = intel_attached_lvds(connector); struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_display_mode *mode; - if (intel_lvds->edid_good) { - int ret = intel_ddc_get_modes(connector, - &dev_priv->gmbus[GMBUS_PORT_PANEL].adapter); - if (ret) - return ret; + if (intel_lvds->edid) { + drm_mode_connector_update_edid_property(connector, + intel_lvds->edid); + return drm_add_edid_modes(connector, intel_lvds->edid); } mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode); @@ -906,11 +904,10 @@ void intel_lvds_init(struct drm_device *dev) * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ - intel_lvds->edid_good = true; - if (!intel_ddc_get_modes(connector, &dev_priv->gmbus[GMBUS_PORT_PANEL].adapter)) - intel_lvds->edid_good = false; + intel_lvds->edid = drm_get_edid(connector, + &dev_priv->gmbus[GMBUS_PORT_PANEL].adapter); - if (!intel_lvds->edid_good) { + if (!intel_lvds->edid) { /* Didn't get an EDID, so * Set wide sync ranges so we get all modes * handed to valid_mode for checking -- cgit From a6c45cf013a57e32ddae43dd4ac911eb4a3919fd Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 17 Sep 2010 00:32:17 +0100 Subject: drm/i915: INTEL_INFO->gen supercedes i8xx, i9xx, i965g Avoid confusion between i965g meaning broadwater and the gen4+ chipset families. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 5666e89288d3..02c5aed36c87 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -198,7 +198,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; /* Should never happen!! */ - if (!IS_I965G(dev) && intel_crtc->pipe == 0) { + if (INTEL_INFO(dev)->gen < 4 && intel_crtc->pipe == 0) { DRM_ERROR("Can't support LVDS on pipe A\n"); return false; } @@ -227,7 +227,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, } /* Make sure pre-965s set dither correctly */ - if (!IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen < 4) { if (dev_priv->panel_wants_dither || dev_priv->lvds_dither) pfit_control |= PANEL_8TO6_DITHER_ENABLE; } @@ -238,7 +238,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, goto out; /* 965+ wants fuzzy fitting */ - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | PFIT_FILTER_FUZZY); @@ -264,7 +264,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, case DRM_MODE_SCALE_ASPECT: /* Scale but preserve the aspect ratio */ - if (IS_I965G(dev)) { + if (INTEL_INFO(dev)->gen >= 4) { u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay; u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; @@ -323,7 +323,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, * Fortunately this is all done for us in hw. */ pfit_control |= PFIT_ENABLE; - if (IS_I965G(dev)) + if (INTEL_INFO(dev)->gen >= 4) pfit_control |= PFIT_SCALING_AUTO; else pfit_control |= (VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | -- cgit From 4fd21dc8ee6fde52a99042186ff94de1b5e8b43c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 21 Sep 2010 14:06:12 +0100 Subject: drm/i915/lvds: Unlock the PP register when panel-fitting As we do not wait for the panel to turn off when we need to adjust the panel-fitting registers we also need to unlock the PLLs as with the non-pfit update path. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 02c5aed36c87..2bcea8000859 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -97,6 +97,7 @@ static void intel_lvds_set_power(struct intel_lvds *intel_lvds, bool on) DRM_ERROR("timed out waiting for panel to power off\n"); I915_WRITE(PFIT_CONTROL, 0); intel_lvds->pfit_control = 0; + intel_lvds->pfit_dirty = false; } I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); @@ -377,8 +378,8 @@ static void intel_lvds_prepare(struct drm_encoder *encoder) I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); } else if (intel_lvds->pfit_dirty) { I915_WRITE(PP_CONTROL, - I915_READ(PP_CONTROL) & ~POWER_TARGET_ON); - I915_WRITE(LVDS, I915_READ(LVDS) & ~LVDS_PORT_EN); + (I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS) + & ~POWER_TARGET_ON); } else { I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); @@ -438,6 +439,9 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder, * screen. Should be enabled before the pipe is enabled, according to * register description and PRM. */ + DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n", + intel_lvds->pfit_control, + intel_lvds->pfit_pgm_ratios); if (wait_for((I915_READ(PP_STATUS) & PP_ON) == 0, 1000)) DRM_ERROR("timed out waiting for panel to power off\n"); -- cgit From 428d2e828c0a68206e5158a42451487601dc9194 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 23 Sep 2010 11:16:49 +0100 Subject: drm/i915/lvds: Probe DDC on creation Try to validate the panel's connection by writing to address 0xA0. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=18072 Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 2bcea8000859..e1f6e05169f6 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -810,6 +810,22 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev) return false; } +static bool intel_lvds_ddc_probe(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u8 buf = 0; + struct i2c_msg msgs[] = { + { + .addr = 0xA0, + .flags = 0, + .len = 1, + .buf = &buf, + }, + }; + struct i2c_adapter *i2c = &dev_priv->gmbus[GMBUS_PORT_PANEL].adapter; + return i2c_transfer(i2c, msgs, 1) == 1; +} + /** * intel_lvds_init - setup LVDS connectors on this device * @dev: drm device @@ -849,6 +865,11 @@ void intel_lvds_init(struct drm_device *dev) gpio = PCH_GPIOC; } + if (!intel_lvds_ddc_probe(dev)) { + DRM_DEBUG_KMS("LVDS did not respond to DDC probe\n"); + return; + } + intel_lvds = kzalloc(sizeof(struct intel_lvds), GFP_KERNEL); if (!intel_lvds) { return; -- cgit From d3849eded23e6c78b19acc1a3a7811a01d2f541d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 23 Sep 2010 22:12:23 +0100 Subject: drm/i915: Remove unused dev_priv->panel_wants_dither This is now private to the DVO connector, remove it from the main device private. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index e1f6e05169f6..1317731bc8ed 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -229,7 +229,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, /* Make sure pre-965s set dither correctly */ if (INTEL_INFO(dev)->gen < 4) { - if (dev_priv->panel_wants_dither || dev_priv->lvds_dither) + if (dev_priv->lvds_dither) pfit_control |= PANEL_8TO6_DITHER_ENABLE; } -- cgit From 270eea0fd71ae95654606ff7448f195fa22d12c5 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 24 Sep 2010 01:15:02 +0100 Subject: drm/i915/lvds: Use the GMBUS pin if specified in VBT Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 1317731bc8ed..95e035a6009e 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -771,7 +771,8 @@ static void intel_find_lvds_downclock(struct drm_device *dev, * If it is not present, return false. * If no child dev is parsed from VBT, it assumes that the LVDS is present. */ -static bool lvds_is_present_in_vbt(struct drm_device *dev) +static bool lvds_is_present_in_vbt(struct drm_device *dev, + u8 *i2c_pin) { struct drm_i915_private *dev_priv = dev->dev_private; int i; @@ -790,6 +791,9 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev) child->device_type != DEVICE_TYPE_LFP) continue; + if (child->i2c_pin) + *i2c_pin = child->i2c_pin; + /* However, we cannot trust the BIOS writers to populate * the VBT correctly. Since LVDS requires additional * information from AIM blocks, a non-zero addin offset is @@ -810,7 +814,7 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev) return false; } -static bool intel_lvds_ddc_probe(struct drm_device *dev) +static bool intel_lvds_ddc_probe(struct drm_device *dev, u8 pin) { struct drm_i915_private *dev_priv = dev->dev_private; u8 buf = 0; @@ -822,7 +826,7 @@ static bool intel_lvds_ddc_probe(struct drm_device *dev) .buf = &buf, }, }; - struct i2c_adapter *i2c = &dev_priv->gmbus[GMBUS_PORT_PANEL].adapter; + struct i2c_adapter *i2c = &dev_priv->gmbus[pin].adapter; return i2c_transfer(i2c, msgs, 1) == 1; } @@ -844,13 +848,15 @@ void intel_lvds_init(struct drm_device *dev) struct drm_display_mode *scan; /* *modes, *bios_mode; */ struct drm_crtc *crtc; u32 lvds; - int pipe, gpio = GPIOC; + int pipe; + u8 pin; /* Skip init on machines we know falsely report LVDS */ if (dmi_check_system(intel_no_lvds)) return; - if (!lvds_is_present_in_vbt(dev)) { + pin = GMBUS_PORT_PANEL; + if (!lvds_is_present_in_vbt(dev, &pin)) { DRM_DEBUG_KMS("LVDS is not present in VBT\n"); return; } @@ -862,10 +868,9 @@ void intel_lvds_init(struct drm_device *dev) DRM_DEBUG_KMS("disable LVDS for eDP support\n"); return; } - gpio = PCH_GPIOC; } - if (!intel_lvds_ddc_probe(dev)) { + if (!intel_lvds_ddc_probe(dev, pin)) { DRM_DEBUG_KMS("LVDS did not respond to DDC probe\n"); return; } @@ -930,7 +935,7 @@ void intel_lvds_init(struct drm_device *dev) * preferred mode is the right one. */ intel_lvds->edid = drm_get_edid(connector, - &dev_priv->gmbus[GMBUS_PORT_PANEL].adapter); + &dev_priv->gmbus[pin].adapter); if (!intel_lvds->edid) { /* Didn't get an EDID, so -- cgit From 5ceb0f9bb7bde101d8b07cb803002591dcb8c804 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 24 Sep 2010 10:24:28 +0100 Subject: drm/i915: Parse the eDP link configuration from the vBIOS First step, lets have a look at the values for troublesome panels and see if they may be used to improve our link training. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 95e035a6009e..98172bcf485f 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -864,7 +864,7 @@ void intel_lvds_init(struct drm_device *dev) if (HAS_PCH_SPLIT(dev)) { if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) return; - if (dev_priv->edp_support) { + if (dev_priv->edp.support) { DRM_DEBUG_KMS("disable LVDS for eDP support\n"); return; } -- cgit From b8232e906381dcba2bb26f0d849d4c25cc9b1368 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 28 Sep 2010 16:41:32 +0100 Subject: drm/i915: Disable LVDS i2c probing when using GPIO bit banging This check only appears to succeed when using GMBUS, so we need to skip it if we have fallen back to using GPIO bit banging. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_lvds.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu/drm/i915/intel_lvds.c') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 98172bcf485f..f1a649990ea9 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -827,6 +827,9 @@ static bool intel_lvds_ddc_probe(struct drm_device *dev, u8 pin) }, }; struct i2c_adapter *i2c = &dev_priv->gmbus[pin].adapter; + /* XXX this only appears to work when using GMBUS */ + if (intel_gmbus_is_forced_bit(i2c)) + return true; return i2c_transfer(i2c, msgs, 1) == 1; } -- cgit