diff options
-rw-r--r-- | drivers/gpu/drm/i915/intel_ddi.c | 116 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_drv.h | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_hdmi.c | 13 |
3 files changed, 133 insertions, 1 deletions
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index d4b268e50565..46d1e886c692 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -637,3 +637,119 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { {297000, 2, 22, 20}, {298000, 2, 21, 19}, }; + +void intel_ddi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + int port = intel_hdmi->ddi_port; + int pipe = intel_crtc->pipe; + int p, n2, r2, valid=0; + u32 temp, i; + + /* On Haswell, we need to enable the clocks and prepare DDI function to + * work in HDMI mode for this pipe. + */ + DRM_DEBUG_KMS("Preparing HDMI DDI mode for Haswell on port %c, pipe %c\n", port_name(port), pipe_name(pipe)); + + for (i=0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++) { + if (crtc->mode.clock == wrpll_tmds_clock_table[i].clock) { + p = wrpll_tmds_clock_table[i].p; + n2 = wrpll_tmds_clock_table[i].n2; + r2 = wrpll_tmds_clock_table[i].r2; + + DRM_DEBUG_KMS("WR PLL clock: found settings for %dKHz refresh rate: p=%d, n2=%d, r2=%d\n", + crtc->mode.clock, + p, n2, r2); + + valid = 1; + break; + } + } + + if (!valid) { + DRM_ERROR("Unable to find WR PLL clock settings for %dKHz refresh rate\n", + crtc->mode.clock); + return; + } + + /* Enable LCPLL if disabled */ + temp = I915_READ(LCPLL_CTL); + if (temp & LCPLL_PLL_DISABLE) + I915_WRITE(LCPLL_CTL, + temp & ~LCPLL_PLL_DISABLE); + + /* Configure WR PLL 1, program the correct divider values for + * the desired frequency and wait for warmup */ + I915_WRITE(WRPLL_CTL1, + WRPLL_PLL_ENABLE | + WRPLL_PLL_SELECT_LCPLL_2700 | + WRPLL_DIVIDER_REFERENCE(r2) | + WRPLL_DIVIDER_FEEDBACK(n2) | + WRPLL_DIVIDER_POST(p)); + + udelay(20); + + /* Use WRPLL1 clock to drive the output to the port, and tell the pipe to use + * this port for connection. + */ + I915_WRITE(PORT_CLK_SEL(port), + PORT_CLK_SEL_WRPLL1); + I915_WRITE(PIPE_CLK_SEL(pipe), + PIPE_CLK_SEL_PORT(port)); + + udelay(20); + + if (intel_hdmi->has_audio) { + /* Proper support for digital audio needs a new logic and a new set + * of registers, so we leave it for future patch bombing. + */ + DRM_DEBUG_DRIVER("HDMI audio on pipe %c not yet supported on DDI\n", + pipe_name(intel_crtc->pipe)); + } + + /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in HDMI mode */ + temp = I915_READ(DDI_FUNC_CTL(pipe)); + temp &= ~PIPE_DDI_PORT_MASK; + temp &= ~PIPE_DDI_BPC_12; + temp |= PIPE_DDI_SELECT_PORT(port) | + PIPE_DDI_MODE_SELECT_HDMI | + ((intel_crtc->bpp > 24) ? + PIPE_DDI_BPC_12 : + PIPE_DDI_BPC_8) | + PIPE_DDI_FUNC_ENABLE; + + I915_WRITE(DDI_FUNC_CTL(pipe), temp); + + intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); + intel_hdmi_set_spd_infoframe(encoder); +} + +void intel_ddi_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + int port = intel_hdmi->ddi_port; + u32 temp; + + temp = I915_READ(DDI_BUF_CTL(port)); + + if (mode != DRM_MODE_DPMS_ON) { + temp &= ~DDI_BUF_CTL_ENABLE; + } else { + temp |= DDI_BUF_CTL_ENABLE; + } + + /* Enable DDI_BUF_CTL. In HDMI/DVI mode, the port width, + * and swing/emphasis values are ignored so nothing special needs + * to be done besides enabling the port. + */ + I915_WRITE(DDI_BUF_CTL(port), + temp); +} diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index afdd74f57d91..d51f27faa202 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -497,4 +497,9 @@ extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv); extern void gen6_disable_rps(struct drm_device *dev); extern void intel_init_emon(struct drm_device *dev); +extern void intel_ddi_dpms(struct drm_encoder *encoder, int mode); +extern void intel_ddi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index a7871b18c201..03b3524a6ba2 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -560,6 +560,14 @@ static void intel_hdmi_destroy(struct drm_connector *connector) kfree(connector); } +static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs_hsw = { + .dpms = intel_ddi_dpms, + .mode_fixup = intel_hdmi_mode_fixup, + .prepare = intel_encoder_prepare, + .mode_set = intel_ddi_mode_set, + .commit = intel_encoder_commit, +}; + static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { .dpms = intel_hdmi_dpms, .mode_fixup = intel_hdmi_mode_fixup, @@ -699,7 +707,10 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) I915_WRITE(TVIDEO_DIP_CTL(i), 0); } - drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); + if (IS_HASWELL(dev)) + drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs_hsw); + else + drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); intel_hdmi_add_properties(intel_hdmi, connector); |