|
@@ -43,102 +43,76 @@
|
|
|
/* Private structure for the integrated LVDS support */
|
|
|
struct intel_lvds {
|
|
|
struct intel_encoder base;
|
|
|
+
|
|
|
+ struct edid *edid;
|
|
|
+
|
|
|
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(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)));
|
|
|
+ return container_of(encoder, struct intel_lvds, base.base);
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * Returns the maximum level of the backlight duty cycle field.
|
|
|
- */
|
|
|
-static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
|
|
|
+static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector)
|
|
|
{
|
|
|
- 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;
|
|
|
+ return container_of(intel_attached_encoder(connector),
|
|
|
+ struct intel_lvds, base);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 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;
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
- if (wait_for(I915_READ(status_reg) & PP_ON, 1000, 0))
|
|
|
- DRM_ERROR("timed out waiting to enable LVDS pipe");
|
|
|
-
|
|
|
- intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle);
|
|
|
+ I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON);
|
|
|
+ intel_panel_set_backlight(dev, dev_priv->backlight_level);
|
|
|
} else {
|
|
|
- intel_lvds_set_backlight(dev, 0);
|
|
|
+ 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);
|
|
|
|
|
|
- I915_WRITE(ctl_reg, I915_READ(ctl_reg) &
|
|
|
- ~POWER_TARGET_ON);
|
|
|
- if (wait_for((I915_READ(status_reg) & PP_ON) == 0, 1000, 0))
|
|
|
- DRM_ERROR("timed out waiting for LVDS pipe to turn off");
|
|
|
+ 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;
|
|
|
+ intel_lvds->pfit_dirty = false;
|
|
|
+ }
|
|
|
|
|
|
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)
|
|
|
{
|
|
|
- struct drm_device *dev = encoder->dev;
|
|
|
+ struct intel_lvds *intel_lvds = 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. */
|
|
|
}
|
|
@@ -146,16 +120,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;
|
|
|
}
|
|
@@ -223,12 +194,12 @@ 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;
|
|
|
|
|
|
/* 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;
|
|
|
}
|
|
@@ -241,9 +212,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
|
|
@@ -251,7 +219,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,
|
|
@@ -260,8 +228,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
|
|
|
}
|
|
|
|
|
|
/* Make sure pre-965s set dither correctly */
|
|
|
- if (!IS_I965G(dev)) {
|
|
|
- if (dev_priv->panel_wants_dither || dev_priv->lvds_dither)
|
|
|
+ if (INTEL_INFO(dev)->gen < 4) {
|
|
|
+ if (dev_priv->lvds_dither)
|
|
|
pfit_control |= PANEL_8TO6_DITHER_ENABLE;
|
|
|
}
|
|
|
|
|
@@ -271,7 +239,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);
|
|
|
|
|
@@ -297,7 +265,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;
|
|
|
|
|
@@ -356,7 +324,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 |
|
|
@@ -369,8 +337,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;
|
|
|
|
|
|
/*
|
|
@@ -386,30 +358,60 @@ 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);
|
|
|
+ struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
|
|
|
+
|
|
|
+ dev_priv->backlight_level = intel_panel_get_backlight(dev);
|
|
|
+
|
|
|
+ /* 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.
|
|
|
+ */
|
|
|
|
|
|
- intel_lvds_set_power(dev, false);
|
|
|
+ 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) | PANEL_UNLOCK_REGS)
|
|
|
+ & ~POWER_TARGET_ON);
|
|
|
+ } 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 = to_intel_lvds(encoder);
|
|
|
|
|
|
- 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);
|
|
|
+
|
|
|
+ /* 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);
|
|
|
+ }
|
|
|
|
|
|
- intel_lvds_set_power(dev, true);
|
|
|
+ /* 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,
|
|
@@ -418,7 +420,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
|
|
@@ -429,13 +431,23 @@ 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.
|
|
|
*/
|
|
|
+ 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");
|
|
|
+
|
|
|
I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios);
|
|
|
I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control);
|
|
|
+ intel_lvds->pfit_dirty = false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -465,38 +477,22 @@ intel_lvds_detect(struct drm_connector *connector, bool force)
|
|
|
*/
|
|
|
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_encoder *encoder = intel_attached_encoder(connector);
|
|
|
- struct intel_encoder *intel_encoder = enc_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 drm_display_mode *mode;
|
|
|
|
|
|
- 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);
|
|
|
}
|
|
|
|
|
|
- /* 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, dev_priv->panel_fixed_mode);
|
|
|
- drm_mode_probed_add(connector, mode);
|
|
|
-
|
|
|
- return 1;
|
|
|
- }
|
|
|
+ mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode);
|
|
|
+ if (mode == 0)
|
|
|
+ return 0;
|
|
|
|
|
|
- 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)
|
|
@@ -587,18 +583,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;
|
|
@@ -628,7 +623,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 = {
|
|
@@ -726,16 +721,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;
|
|
|
-
|
|
|
- mutex_lock(&dev->mode_config.mutex);
|
|
|
+ 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
|
|
@@ -744,14 +737,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
|
|
@@ -761,17 +754,14 @@ 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) {
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -780,38 +770,67 @@ 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,
|
|
|
+ u8 *i2c_pin)
|
|
|
{
|
|
|
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.
|
|
|
+ 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
|
|
|
+ * 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;
|
|
|
+}
|
|
|
+
|
|
|
+static bool intel_lvds_ddc_probe(struct drm_device *dev, u8 pin)
|
|
|
+{
|
|
|
+ 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[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;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -832,13 +851,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;
|
|
|
}
|
|
@@ -846,11 +867,15 @@ 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;
|
|
|
}
|
|
|
- gpio = PCH_GPIOC;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!intel_lvds_ddc_probe(dev, pin)) {
|
|
|
+ DRM_DEBUG_KMS("LVDS did not respond to DDC probe\n");
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
intel_lvds = kzalloc(sizeof(struct intel_lvds), GFP_KERNEL);
|
|
@@ -864,16 +889,20 @@ 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->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);
|
|
|
+ intel_connector_attach_encoder(intel_connector, intel_encoder);
|
|
|
intel_encoder->type = INTEL_OUTPUT_LVDS;
|
|
|
|
|
|
intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT);
|
|
@@ -904,43 +933,41 @@ 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(dev, 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.
|
|
|
*/
|
|
|
- dev_priv->lvds_edid_good = true;
|
|
|
+ intel_lvds->edid = drm_get_edid(connector,
|
|
|
+ &dev_priv->gmbus[pin].adapter);
|
|
|
|
|
|
- if (!intel_ddc_get_modes(connector, intel_encoder->ddc_bus))
|
|
|
- dev_priv->lvds_edid_good = false;
|
|
|
+ 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
|
|
|
+ */
|
|
|
+ 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) {
|
|
|
- mutex_lock(&dev->mode_config.mutex);
|
|
|
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
|
|
|
- dev_priv->panel_fixed_mode =
|
|
|
+ intel_lvds->fixed_mode =
|
|
|
drm_mode_duplicate(dev, scan);
|
|
|
- mutex_unlock(&dev->mode_config.mutex);
|
|
|
- intel_find_lvds_downclock(dev, connector);
|
|
|
+ intel_find_lvds_downclock(dev,
|
|
|
+ intel_lvds->fixed_mode,
|
|
|
+ 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 =
|
|
|
+ intel_lvds->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 |=
|
|
|
+ if (intel_lvds->fixed_mode) {
|
|
|
+ intel_lvds->fixed_mode->type |=
|
|
|
DRM_MODE_TYPE_PREFERRED;
|
|
|
goto out;
|
|
|
}
|
|
@@ -958,19 +985,19 @@ 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);
|
|
|
- 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:
|
|
@@ -997,8 +1024,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);
|