]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
drm/i915: prepare HDMI link for Haswell
authorEugeni Dodonov <eugeni.dodonov@intel.com>
Wed, 9 May 2012 18:37:31 +0000 (15:37 -0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Sat, 19 May 2012 20:39:51 +0000 (22:39 +0200)
On Haswell, we need to properly train the DDI buffers prior to enabling
HDMI, and enable the required clocks with correct dividers for the desired
frequency.

Also, we cannot simple reuse HDMI routines from previous generations of
GPU, as most of HDMI-specific stuff is being done via the DDI port
programming instead of HDMI-specific registers.

This commit take advantage of the WR PLL clock table which is in a
separate (previous) commit to select the right divisors for each mode.

Signed-off-by: Eugeni Dodonov <eugeni.dodonov@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_hdmi.c

index d4b268e50565b7fa27fc7a2690c7f30ec733ccab..46d1e886c6923f9b9e8373dc65e6417b3c5f98a8 100644 (file)
@@ -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);
+}
index afdd74f57d911b63b6dd5fe8c0c44b83785df96e..d51f27faa202c4ab3404540c7c441396181e5b31 100644 (file)
@@ -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__ */
index a7871b18c201040cb110d19195a4f9b37269af14..03b3524a6ba2d018e90acc890782f72d7813351a 100644 (file)
@@ -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);