Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
[linux-2.6-microblaze.git] / drivers / gpu / drm / i915 / intel_hdmi.c
index 025be7d..9ba0aae 100644 (file)
@@ -150,6 +150,9 @@ static void g4x_write_infoframe(struct drm_encoder *encoder,
                I915_WRITE(VIDEO_DIP_DATA, *data);
                data++;
        }
+       /* Write every possible data byte to force correct ECC calculation. */
+       for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
+               I915_WRITE(VIDEO_DIP_DATA, 0);
        mmiowb();
 
        val |= g4x_infoframe_enable(frame);
@@ -185,6 +188,9 @@ static void ibx_write_infoframe(struct drm_encoder *encoder,
                I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
                data++;
        }
+       /* Write every possible data byte to force correct ECC calculation. */
+       for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
+               I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
        mmiowb();
 
        val |= g4x_infoframe_enable(frame);
@@ -223,6 +229,9 @@ static void cpt_write_infoframe(struct drm_encoder *encoder,
                I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
                data++;
        }
+       /* Write every possible data byte to force correct ECC calculation. */
+       for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
+               I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
        mmiowb();
 
        val |= g4x_infoframe_enable(frame);
@@ -258,6 +267,9 @@ static void vlv_write_infoframe(struct drm_encoder *encoder,
                I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
                data++;
        }
+       /* Write every possible data byte to force correct ECC calculation. */
+       for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
+               I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
        mmiowb();
 
        val |= g4x_infoframe_enable(frame);
@@ -291,6 +303,9 @@ static void hsw_write_infoframe(struct drm_encoder *encoder,
                I915_WRITE(data_reg + i, *data);
                data++;
        }
+       /* Write every possible data byte to force correct ECC calculation. */
+       for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
+               I915_WRITE(data_reg + i, 0);
        mmiowb();
 
        val |= hsw_infoframe_enable(frame);
@@ -376,6 +391,7 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
                port = VIDEO_DIP_PORT_C;
                break;
        default:
+               BUG();
                return;
        }
 
@@ -434,6 +450,7 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
                port = VIDEO_DIP_PORT_D;
                break;
        default:
+               BUG();
                return;
        }
 
@@ -600,15 +617,36 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
        intel_hdmi->set_infoframes(encoder, adjusted_mode);
 }
 
-static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
+static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
+                                   enum pipe *pipe)
 {
-       struct drm_device *dev = encoder->dev;
+       struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+       u32 tmp;
+
+       tmp = I915_READ(intel_hdmi->sdvox_reg);
+
+       if (!(tmp & SDVO_ENABLE))
+               return false;
+
+       if (HAS_PCH_CPT(dev))
+               *pipe = PORT_TO_PIPE_CPT(tmp);
+       else
+               *pipe = PORT_TO_PIPE(tmp);
+
+       return true;
+}
+
+static void intel_enable_hdmi(struct intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
        u32 temp;
        u32 enable_bits = SDVO_ENABLE;
 
-       if (intel_hdmi->has_audio || mode != DRM_MODE_DPMS_ON)
+       if (intel_hdmi->has_audio)
                enable_bits |= SDVO_AUDIO_ENABLE;
 
        temp = I915_READ(intel_hdmi->sdvox_reg);
@@ -616,31 +654,12 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
        /* HW workaround for IBX, we need to move the port to transcoder A
         * before disabling it. */
        if (HAS_PCH_IBX(dev)) {
-               struct drm_crtc *crtc = encoder->crtc;
+               struct drm_crtc *crtc = encoder->base.crtc;
                int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
 
-               if (mode != DRM_MODE_DPMS_ON) {
-                       if (temp & SDVO_PIPE_B_SELECT) {
-                               temp &= ~SDVO_PIPE_B_SELECT;
-                               I915_WRITE(intel_hdmi->sdvox_reg, temp);
-                               POSTING_READ(intel_hdmi->sdvox_reg);
-
-                               /* Again we need to write this twice. */
-                               I915_WRITE(intel_hdmi->sdvox_reg, temp);
-                               POSTING_READ(intel_hdmi->sdvox_reg);
-
-                               /* Transcoder selection bits only update
-                                * effectively on vblank. */
-                               if (crtc)
-                                       intel_wait_for_vblank(dev, pipe);
-                               else
-                                       msleep(50);
-                       }
-               } else {
-                       /* Restore the transcoder select bit. */
-                       if (pipe == PIPE_B)
-                               enable_bits |= SDVO_PIPE_B_SELECT;
-               }
+               /* Restore the transcoder select bit. */
+               if (pipe == PIPE_B)
+                       enable_bits |= SDVO_PIPE_B_SELECT;
        }
 
        /* HW workaround, need to toggle enable bit off and on for 12bpc, but
@@ -651,12 +670,64 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
                POSTING_READ(intel_hdmi->sdvox_reg);
        }
 
-       if (mode != DRM_MODE_DPMS_ON) {
-               temp &= ~enable_bits;
-       } else {
-               temp |= enable_bits;
+       temp |= enable_bits;
+
+       I915_WRITE(intel_hdmi->sdvox_reg, temp);
+       POSTING_READ(intel_hdmi->sdvox_reg);
+
+       /* HW workaround, need to write this twice for issue that may result
+        * in first write getting masked.
+        */
+       if (HAS_PCH_SPLIT(dev)) {
+               I915_WRITE(intel_hdmi->sdvox_reg, temp);
+               POSTING_READ(intel_hdmi->sdvox_reg);
+       }
+}
+
+static void intel_disable_hdmi(struct intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+       u32 temp;
+       u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE;
+
+       temp = I915_READ(intel_hdmi->sdvox_reg);
+
+       /* HW workaround for IBX, we need to move the port to transcoder A
+        * before disabling it. */
+       if (HAS_PCH_IBX(dev)) {
+               struct drm_crtc *crtc = encoder->base.crtc;
+               int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
+
+               if (temp & SDVO_PIPE_B_SELECT) {
+                       temp &= ~SDVO_PIPE_B_SELECT;
+                       I915_WRITE(intel_hdmi->sdvox_reg, temp);
+                       POSTING_READ(intel_hdmi->sdvox_reg);
+
+                       /* Again we need to write this twice. */
+                       I915_WRITE(intel_hdmi->sdvox_reg, temp);
+                       POSTING_READ(intel_hdmi->sdvox_reg);
+
+                       /* Transcoder selection bits only update
+                        * effectively on vblank. */
+                       if (crtc)
+                               intel_wait_for_vblank(dev, pipe);
+                       else
+                               msleep(50);
+               }
+       }
+
+       /* HW workaround, need to toggle enable bit off and on for 12bpc, but
+        * we do this anyway which shows more stable in testing.
+        */
+       if (HAS_PCH_SPLIT(dev)) {
+               I915_WRITE(intel_hdmi->sdvox_reg, temp & ~SDVO_ENABLE);
+               POSTING_READ(intel_hdmi->sdvox_reg);
        }
 
+       temp &= ~enable_bits;
+
        I915_WRITE(intel_hdmi->sdvox_reg, temp);
        POSTING_READ(intel_hdmi->sdvox_reg);
 
@@ -736,7 +807,6 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
                                                drm_detect_hdmi_monitor(edid);
                        intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
                }
-               connector->display_info.raw_edid = NULL;
                kfree(edid);
        }
 
@@ -777,8 +847,6 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
        if (edid) {
                if (edid->input & DRM_EDID_INPUT_DIGITAL)
                        has_audio = drm_detect_monitor_audio(edid);
-
-               connector->display_info.raw_edid = NULL;
                kfree(edid);
        }
 
@@ -832,9 +900,8 @@ intel_hdmi_set_property(struct drm_connector *connector,
 done:
        if (intel_hdmi->base.base.crtc) {
                struct drm_crtc *crtc = intel_hdmi->base.base.crtc;
-               drm_crtc_helper_set_mode(crtc, &crtc->mode,
-                                        crtc->x, crtc->y,
-                                        crtc->fb);
+               intel_set_mode(crtc, &crtc->mode,
+                              crtc->x, crtc->y, crtc->fb);
        }
 
        return 0;
@@ -848,23 +915,19 @@ static void intel_hdmi_destroy(struct drm_connector *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,
+       .disable = intel_encoder_noop,
 };
 
 static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {
-       .dpms = intel_hdmi_dpms,
        .mode_fixup = intel_hdmi_mode_fixup,
-       .prepare = intel_encoder_prepare,
        .mode_set = intel_hdmi_mode_set,
-       .commit = intel_encoder_commit,
+       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
+       .dpms = intel_connector_dpms,
        .detect = intel_hdmi_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .set_property = intel_hdmi_set_property,
@@ -888,7 +951,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
        intel_attach_broadcast_rgb_property(connector);
 }
 
-void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
+void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_connector *connector;
@@ -922,48 +985,25 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
        connector->doublescan_allowed = 0;
        intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
 
-       /* Set up the DDC bus. */
-       if (sdvox_reg == SDVOB) {
-               intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
-               intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
-               dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == SDVOC) {
-               intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
-               intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
-               dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == HDMIB) {
-               intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
-               intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
-               dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == HDMIC) {
-               intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT);
-               intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
-               dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == HDMID) {
-               intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT);
-               intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
-               dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == DDI_BUF_CTL(PORT_B)) {
-               DRM_DEBUG_DRIVER("LPT: detected output on DDI B\n");
-               intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
+       intel_encoder->cloneable = false;
+
+       intel_hdmi->ddi_port = port;
+       switch (port) {
+       case PORT_B:
                intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
-               intel_hdmi->ddi_port = PORT_B;
                dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == DDI_BUF_CTL(PORT_C)) {
-               DRM_DEBUG_DRIVER("LPT: detected output on DDI C\n");
-               intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
+               break;
+       case PORT_C:
                intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
-               intel_hdmi->ddi_port = PORT_C;
                dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
-       } else if (sdvox_reg == DDI_BUF_CTL(PORT_D)) {
-               DRM_DEBUG_DRIVER("LPT: detected output on DDI D\n");
-               intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
+               break;
+       case PORT_D:
                intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
-               intel_hdmi->ddi_port = PORT_D;
                dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
-       } else {
-               /* If we got an unknown sdvox_reg, things are pretty much broken
-                * in a way that we should let the kernel know about it */
+               break;
+       case PORT_A:
+               /* Internal port only for eDP. */
+       default:
                BUG();
        }
 
@@ -986,10 +1026,21 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
                intel_hdmi->set_infoframes = cpt_set_infoframes;
        }
 
-       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);
+       if (IS_HASWELL(dev)) {
+               intel_encoder->enable = intel_enable_ddi;
+               intel_encoder->disable = intel_disable_ddi;
+               intel_encoder->get_hw_state = intel_ddi_get_hw_state;
+               drm_encoder_helper_add(&intel_encoder->base,
+                                      &intel_hdmi_helper_funcs_hsw);
+       } else {
+               intel_encoder->enable = intel_enable_hdmi;
+               intel_encoder->disable = intel_disable_hdmi;
+               intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
+               drm_encoder_helper_add(&intel_encoder->base,
+                                      &intel_hdmi_helper_funcs);
+       }
+       intel_connector->get_hw_state = intel_connector_get_hw_state;
+
 
        intel_hdmi_add_properties(intel_hdmi, connector);