drm/i915: simplify possible_clones computation
[deliverable/linux.git] / drivers / gpu / drm / i915 / intel_hdmi.c
index 2d7f47b56b6ae7a7b1922a328ab971fe9d63077a..593b8fe2e00ac669c5e1e0368a14f85d0e4f463d 100644 (file)
 #include "i915_drm.h"
 #include "i915_drv.h"
 
-struct intel_hdmi {
-       struct intel_encoder base;
-       u32 sdvox_reg;
-       int ddc_bus;
-       uint32_t color_range;
-       bool has_hdmi_sink;
-       bool has_audio;
-       enum hdmi_force_audio force_audio;
-       void (*write_infoframe)(struct drm_encoder *encoder,
-                               struct dip_infoframe *frame);
-};
+static void
+assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi)
+{
+       struct drm_device *dev = intel_hdmi->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t enabled_bits;
+
+       enabled_bits = IS_HASWELL(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE;
+
+       WARN(I915_READ(intel_hdmi->sdvox_reg) & enabled_bits,
+            "HDMI port enabled, expecting disabled\n");
+}
 
-static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
+struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
 {
        return container_of(encoder, struct intel_hdmi, base.base);
 }
@@ -75,121 +76,240 @@ void intel_dip_infoframe_csum(struct dip_infoframe *frame)
        frame->checksum = 0x100 - sum;
 }
 
-static u32 intel_infoframe_index(struct dip_infoframe *frame)
+static u32 g4x_infoframe_index(struct dip_infoframe *frame)
 {
-       u32 flags = 0;
-
        switch (frame->type) {
        case DIP_TYPE_AVI:
-               flags |= VIDEO_DIP_SELECT_AVI;
-               break;
+               return VIDEO_DIP_SELECT_AVI;
        case DIP_TYPE_SPD:
-               flags |= VIDEO_DIP_SELECT_SPD;
-               break;
+               return VIDEO_DIP_SELECT_SPD;
        default:
                DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
-               break;
+               return 0;
        }
-
-       return flags;
 }
 
-static u32 intel_infoframe_flags(struct dip_infoframe *frame)
+static u32 g4x_infoframe_enable(struct dip_infoframe *frame)
 {
-       u32 flags = 0;
+       switch (frame->type) {
+       case DIP_TYPE_AVI:
+               return VIDEO_DIP_ENABLE_AVI;
+       case DIP_TYPE_SPD:
+               return VIDEO_DIP_ENABLE_SPD;
+       default:
+               DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
+               return 0;
+       }
+}
 
+static u32 hsw_infoframe_enable(struct dip_infoframe *frame)
+{
        switch (frame->type) {
        case DIP_TYPE_AVI:
-               flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC;
-               break;
+               return VIDEO_DIP_ENABLE_AVI_HSW;
        case DIP_TYPE_SPD:
-               flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_VSYNC;
-               break;
+               return VIDEO_DIP_ENABLE_SPD_HSW;
        default:
                DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
-               break;
+               return 0;
        }
+}
 
-       return flags;
+static u32 hsw_infoframe_data_reg(struct dip_infoframe *frame, enum pipe pipe)
+{
+       switch (frame->type) {
+       case DIP_TYPE_AVI:
+               return HSW_TVIDEO_DIP_AVI_DATA(pipe);
+       case DIP_TYPE_SPD:
+               return HSW_TVIDEO_DIP_SPD_DATA(pipe);
+       default:
+               DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
+               return 0;
+       }
 }
 
-static void i9xx_write_infoframe(struct drm_encoder *encoder,
-                                struct dip_infoframe *frame)
+static void g4x_write_infoframe(struct drm_encoder *encoder,
+                               struct dip_infoframe *frame)
 {
        uint32_t *data = (uint32_t *)frame;
        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);
-       u32 port, flags, val = I915_READ(VIDEO_DIP_CTL);
+       u32 val = I915_READ(VIDEO_DIP_CTL);
        unsigned i, len = DIP_HEADER_SIZE + frame->len;
 
+       WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
 
-       /* XXX first guess at handling video port, is this corrent? */
-       if (intel_hdmi->sdvox_reg == SDVOB)
-               port = VIDEO_DIP_PORT_B;
-       else if (intel_hdmi->sdvox_reg == SDVOC)
-               port = VIDEO_DIP_PORT_C;
-       else
-               return;
-
-       flags = intel_infoframe_index(frame);
+       val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
+       val |= g4x_infoframe_index(frame);
 
-       val &= ~VIDEO_DIP_SELECT_MASK;
+       val &= ~g4x_infoframe_enable(frame);
 
-       I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags);
+       I915_WRITE(VIDEO_DIP_CTL, val);
 
+       mmiowb();
        for (i = 0; i < len; i += 4) {
                I915_WRITE(VIDEO_DIP_DATA, *data);
                data++;
        }
+       mmiowb();
 
-       flags |= intel_infoframe_flags(frame);
+       val |= g4x_infoframe_enable(frame);
+       val &= ~VIDEO_DIP_FREQ_MASK;
+       val |= VIDEO_DIP_FREQ_VSYNC;
 
-       I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags);
+       I915_WRITE(VIDEO_DIP_CTL, val);
+       POSTING_READ(VIDEO_DIP_CTL);
 }
 
-static void ironlake_write_infoframe(struct drm_encoder *encoder,
-                                    struct dip_infoframe *frame)
+static void ibx_write_infoframe(struct drm_encoder *encoder,
+                               struct dip_infoframe *frame)
 {
        uint32_t *data = (uint32_t *)frame;
        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_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
        int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
        unsigned i, len = DIP_HEADER_SIZE + frame->len;
-       u32 flags, val = I915_READ(reg);
+       u32 val = I915_READ(reg);
 
-       intel_wait_for_vblank(dev, intel_crtc->pipe);
+       WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
 
-       flags = intel_infoframe_index(frame);
+       val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
+       val |= g4x_infoframe_index(frame);
+
+       val &= ~g4x_infoframe_enable(frame);
+
+       I915_WRITE(reg, val);
+
+       mmiowb();
+       for (i = 0; i < len; i += 4) {
+               I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
+               data++;
+       }
+       mmiowb();
+
+       val |= g4x_infoframe_enable(frame);
+       val &= ~VIDEO_DIP_FREQ_MASK;
+       val |= VIDEO_DIP_FREQ_VSYNC;
+
+       I915_WRITE(reg, val);
+       POSTING_READ(reg);
+}
+
+static void cpt_write_infoframe(struct drm_encoder *encoder,
+                               struct dip_infoframe *frame)
+{
+       uint32_t *data = (uint32_t *)frame;
+       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);
+       int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+       unsigned i, len = DIP_HEADER_SIZE + frame->len;
+       u32 val = I915_READ(reg);
+
+       WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
 
        val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
+       val |= g4x_infoframe_index(frame);
 
-       I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
+       /* The DIP control register spec says that we need to update the AVI
+        * infoframe without clearing its enable bit */
+       if (frame->type != DIP_TYPE_AVI)
+               val &= ~g4x_infoframe_enable(frame);
 
+       I915_WRITE(reg, val);
+
+       mmiowb();
        for (i = 0; i < len; i += 4) {
                I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
                data++;
        }
+       mmiowb();
 
-       flags |= intel_infoframe_flags(frame);
+       val |= g4x_infoframe_enable(frame);
+       val &= ~VIDEO_DIP_FREQ_MASK;
+       val |= VIDEO_DIP_FREQ_VSYNC;
 
-       I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
+       I915_WRITE(reg, val);
+       POSTING_READ(reg);
 }
-static void intel_set_infoframe(struct drm_encoder *encoder,
+
+static void vlv_write_infoframe(struct drm_encoder *encoder,
+                                    struct dip_infoframe *frame)
+{
+       uint32_t *data = (uint32_t *)frame;
+       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);
+       int reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe);
+       unsigned i, len = DIP_HEADER_SIZE + frame->len;
+       u32 val = I915_READ(reg);
+
+       WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
+
+       val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
+       val |= g4x_infoframe_index(frame);
+
+       val &= ~g4x_infoframe_enable(frame);
+
+       I915_WRITE(reg, val);
+
+       mmiowb();
+       for (i = 0; i < len; i += 4) {
+               I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
+               data++;
+       }
+       mmiowb();
+
+       val |= g4x_infoframe_enable(frame);
+       val &= ~VIDEO_DIP_FREQ_MASK;
+       val |= VIDEO_DIP_FREQ_VSYNC;
+
+       I915_WRITE(reg, val);
+       POSTING_READ(reg);
+}
+
+static void hsw_write_infoframe(struct drm_encoder *encoder,
                                struct dip_infoframe *frame)
 {
-       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       uint32_t *data = (uint32_t *)frame;
+       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);
+       u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->pipe);
+       u32 data_reg = hsw_infoframe_data_reg(frame, intel_crtc->pipe);
+       unsigned int i, len = DIP_HEADER_SIZE + frame->len;
+       u32 val = I915_READ(ctl_reg);
 
-       if (!intel_hdmi->has_hdmi_sink)
+       if (data_reg == 0)
                return;
 
+       val &= ~hsw_infoframe_enable(frame);
+       I915_WRITE(ctl_reg, val);
+
+       mmiowb();
+       for (i = 0; i < len; i += 4) {
+               I915_WRITE(data_reg + i, *data);
+               data++;
+       }
+       mmiowb();
+
+       val |= hsw_infoframe_enable(frame);
+       I915_WRITE(ctl_reg, val);
+       POSTING_READ(ctl_reg);
+}
+
+static void intel_set_infoframe(struct drm_encoder *encoder,
+                               struct dip_infoframe *frame)
+{
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
        intel_dip_infoframe_csum(frame);
        intel_hdmi->write_infoframe(encoder, frame);
 }
 
-static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
+                                        struct drm_display_mode *adjusted_mode)
 {
        struct dip_infoframe avi_if = {
                .type = DIP_TYPE_AVI,
@@ -197,6 +317,9 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
                .len = DIP_LEN_AVI,
        };
 
+       if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK)
+               avi_if.body.avi.YQ_CN_PR |= DIP_AVI_PR_2;
+
        intel_set_infoframe(encoder, &avi_if);
 }
 
@@ -215,18 +338,234 @@ static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
        intel_set_infoframe(encoder, &spd_if);
 }
 
+static void g4x_set_infoframes(struct drm_encoder *encoder,
+                              struct drm_display_mode *adjusted_mode)
+{
+       struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       u32 reg = VIDEO_DIP_CTL;
+       u32 val = I915_READ(reg);
+       u32 port;
+
+       assert_hdmi_port_disabled(intel_hdmi);
+
+       /* If the registers were not initialized yet, they might be zeroes,
+        * which means we're selecting the AVI DIP and we're setting its
+        * frequency to once. This seems to really confuse the HW and make
+        * things stop working (the register spec says the AVI always needs to
+        * be sent every VSync). So here we avoid writing to the register more
+        * than we need and also explicitly select the AVI DIP and explicitly
+        * set its frequency to every VSync. Avoiding to write it twice seems to
+        * be enough to solve the problem, but being defensive shouldn't hurt us
+        * either. */
+       val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
+
+       if (!intel_hdmi->has_hdmi_sink) {
+               if (!(val & VIDEO_DIP_ENABLE))
+                       return;
+               val &= ~VIDEO_DIP_ENABLE;
+               I915_WRITE(reg, val);
+               POSTING_READ(reg);
+               return;
+       }
+
+       switch (intel_hdmi->sdvox_reg) {
+       case SDVOB:
+               port = VIDEO_DIP_PORT_B;
+               break;
+       case SDVOC:
+               port = VIDEO_DIP_PORT_C;
+               break;
+       default:
+               return;
+       }
+
+       if (port != (val & VIDEO_DIP_PORT_MASK)) {
+               if (val & VIDEO_DIP_ENABLE) {
+                       val &= ~VIDEO_DIP_ENABLE;
+                       I915_WRITE(reg, val);
+                       POSTING_READ(reg);
+               }
+               val &= ~VIDEO_DIP_PORT_MASK;
+               val |= port;
+       }
+
+       val |= VIDEO_DIP_ENABLE;
+       val &= ~VIDEO_DIP_ENABLE_VENDOR;
+
+       I915_WRITE(reg, val);
+       POSTING_READ(reg);
+
+       intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
+       intel_hdmi_set_spd_infoframe(encoder);
+}
+
+static void ibx_set_infoframes(struct drm_encoder *encoder,
+                              struct drm_display_mode *adjusted_mode)
+{
+       struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+       u32 val = I915_READ(reg);
+       u32 port;
+
+       assert_hdmi_port_disabled(intel_hdmi);
+
+       /* See the big comment in g4x_set_infoframes() */
+       val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
+
+       if (!intel_hdmi->has_hdmi_sink) {
+               if (!(val & VIDEO_DIP_ENABLE))
+                       return;
+               val &= ~VIDEO_DIP_ENABLE;
+               I915_WRITE(reg, val);
+               POSTING_READ(reg);
+               return;
+       }
+
+       switch (intel_hdmi->sdvox_reg) {
+       case HDMIB:
+               port = VIDEO_DIP_PORT_B;
+               break;
+       case HDMIC:
+               port = VIDEO_DIP_PORT_C;
+               break;
+       case HDMID:
+               port = VIDEO_DIP_PORT_D;
+               break;
+       default:
+               return;
+       }
+
+       if (port != (val & VIDEO_DIP_PORT_MASK)) {
+               if (val & VIDEO_DIP_ENABLE) {
+                       val &= ~VIDEO_DIP_ENABLE;
+                       I915_WRITE(reg, val);
+                       POSTING_READ(reg);
+               }
+               val &= ~VIDEO_DIP_PORT_MASK;
+               val |= port;
+       }
+
+       val |= VIDEO_DIP_ENABLE;
+       val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
+                VIDEO_DIP_ENABLE_GCP);
+
+       I915_WRITE(reg, val);
+       POSTING_READ(reg);
+
+       intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
+       intel_hdmi_set_spd_infoframe(encoder);
+}
+
+static void cpt_set_infoframes(struct drm_encoder *encoder,
+                              struct drm_display_mode *adjusted_mode)
+{
+       struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       u32 reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+       u32 val = I915_READ(reg);
+
+       assert_hdmi_port_disabled(intel_hdmi);
+
+       /* See the big comment in g4x_set_infoframes() */
+       val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
+
+       if (!intel_hdmi->has_hdmi_sink) {
+               if (!(val & VIDEO_DIP_ENABLE))
+                       return;
+               val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI);
+               I915_WRITE(reg, val);
+               POSTING_READ(reg);
+               return;
+       }
+
+       /* Set both together, unset both together: see the spec. */
+       val |= VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI;
+       val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
+                VIDEO_DIP_ENABLE_GCP);
+
+       I915_WRITE(reg, val);
+       POSTING_READ(reg);
+
+       intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
+       intel_hdmi_set_spd_infoframe(encoder);
+}
+
+static void vlv_set_infoframes(struct drm_encoder *encoder,
+                              struct drm_display_mode *adjusted_mode)
+{
+       struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       u32 reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe);
+       u32 val = I915_READ(reg);
+
+       assert_hdmi_port_disabled(intel_hdmi);
+
+       /* See the big comment in g4x_set_infoframes() */
+       val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
+
+       if (!intel_hdmi->has_hdmi_sink) {
+               if (!(val & VIDEO_DIP_ENABLE))
+                       return;
+               val &= ~VIDEO_DIP_ENABLE;
+               I915_WRITE(reg, val);
+               POSTING_READ(reg);
+               return;
+       }
+
+       val |= VIDEO_DIP_ENABLE;
+       val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
+                VIDEO_DIP_ENABLE_GCP);
+
+       I915_WRITE(reg, val);
+       POSTING_READ(reg);
+
+       intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
+       intel_hdmi_set_spd_infoframe(encoder);
+}
+
+static void hsw_set_infoframes(struct drm_encoder *encoder,
+                              struct drm_display_mode *adjusted_mode)
+{
+       struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       u32 reg = HSW_TVIDEO_DIP_CTL(intel_crtc->pipe);
+       u32 val = I915_READ(reg);
+
+       assert_hdmi_port_disabled(intel_hdmi);
+
+       if (!intel_hdmi->has_hdmi_sink) {
+               I915_WRITE(reg, 0);
+               POSTING_READ(reg);
+               return;
+       }
+
+       val &= ~(VIDEO_DIP_ENABLE_VSC_HSW | VIDEO_DIP_ENABLE_GCP_HSW |
+                VIDEO_DIP_ENABLE_VS_HSW | VIDEO_DIP_ENABLE_GMP_HSW);
+
+       I915_WRITE(reg, val);
+       POSTING_READ(reg);
+
+       intel_hdmi_set_avi_infoframe(encoder, adjusted_mode);
+       intel_hdmi_set_spd_infoframe(encoder);
+}
+
 static void intel_hdmi_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_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
        u32 sdvox;
 
-       sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE;
+       sdvox = SDVO_ENCODING_HDMI;
        if (!HAS_PCH_SPLIT(dev))
                sdvox |= intel_hdmi->color_range;
        if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
@@ -253,14 +592,13 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
 
        if (HAS_PCH_CPT(dev))
                sdvox |= PORT_TRANS_SEL_CPT(intel_crtc->pipe);
-       else if (intel_crtc->pipe == 1)
+       else if (intel_crtc->pipe == PIPE_B)
                sdvox |= SDVO_PIPE_B_SELECT;
 
        I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
        POSTING_READ(intel_hdmi->sdvox_reg);
 
-       intel_hdmi_set_avi_infoframe(encoder);
-       intel_hdmi_set_spd_infoframe(encoder);
+       intel_hdmi->set_infoframes(encoder, adjusted_mode);
 }
 
 static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
@@ -276,6 +614,36 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
 
        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->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;
+               }
+       }
+
        /* HW workaround, need to toggle enable bit off and on for 12bpc, but
         * we do this anyway which shows more stable in testing.
         */
@@ -317,12 +685,33 @@ static int intel_hdmi_mode_valid(struct drm_connector *connector,
 }
 
 static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
-                                 struct drm_display_mode *mode,
+                                 const struct drm_display_mode *mode,
                                  struct drm_display_mode *adjusted_mode)
 {
        return true;
 }
 
+static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi)
+{
+       struct drm_device *dev = intel_hdmi->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t bit;
+
+       switch (intel_hdmi->sdvox_reg) {
+       case SDVOB:
+               bit = HDMIB_HOTPLUG_LIVE_STATUS;
+               break;
+       case SDVOC:
+               bit = HDMIC_HOTPLUG_LIVE_STATUS;
+               break;
+       default:
+               bit = 0;
+               break;
+       }
+
+       return I915_READ(PORT_HOTPLUG_STAT) & bit;
+}
+
 static enum drm_connector_status
 intel_hdmi_detect(struct drm_connector *connector, bool force)
 {
@@ -331,10 +720,14 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
        struct edid *edid;
        enum drm_connector_status status = connector_status_disconnected;
 
+       if (IS_G4X(connector->dev) && !g4x_hdmi_connected(intel_hdmi))
+               return status;
+
        intel_hdmi->has_hdmi_sink = false;
        intel_hdmi->has_audio = false;
        edid = drm_get_edid(connector,
-                           &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
+                           intel_gmbus_get_adapter(dev_priv,
+                                                   intel_hdmi->ddc_bus));
 
        if (edid) {
                if (edid->input & DRM_EDID_INPUT_DIGITAL) {
@@ -367,7 +760,8 @@ static int intel_hdmi_get_modes(struct drm_connector *connector)
         */
 
        return intel_ddc_get_modes(connector,
-                                  &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
+                                  intel_gmbus_get_adapter(dev_priv,
+                                                          intel_hdmi->ddc_bus));
 }
 
 static bool
@@ -379,7 +773,8 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
        bool has_audio = false;
 
        edid = drm_get_edid(connector,
-                           &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
+                           intel_gmbus_get_adapter(dev_priv,
+                                                   intel_hdmi->ddc_bus));
        if (edid) {
                if (edid->input & DRM_EDID_INPUT_DIGITAL)
                        has_audio = drm_detect_monitor_audio(edid);
@@ -393,8 +788,8 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
 
 static int
 intel_hdmi_set_property(struct drm_connector *connector,
-                     struct drm_property *property,
-                     uint64_t val)
+                       struct drm_property *property,
+                       uint64_t val)
 {
        struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
        struct drm_i915_private *dev_priv = connector->dev->dev_private;
@@ -453,6 +848,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,
@@ -493,7 +896,6 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
        struct intel_encoder *intel_encoder;
        struct intel_connector *intel_connector;
        struct intel_hdmi *intel_hdmi;
-       int i;
 
        intel_hdmi = kzalloc(sizeof(struct intel_hdmi), GFP_KERNEL);
        if (!intel_hdmi)
@@ -521,41 +923,68 @@ 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);
 
+       intel_encoder->cloneable = false;
+
        /* 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_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_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_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 */
+               BUG();
        }
 
        intel_hdmi->sdvox_reg = sdvox_reg;
 
        if (!HAS_PCH_SPLIT(dev)) {
-               intel_hdmi->write_infoframe = i9xx_write_infoframe;
-               I915_WRITE(VIDEO_DIP_CTL, 0);
+               intel_hdmi->write_infoframe = g4x_write_infoframe;
+               intel_hdmi->set_infoframes = g4x_set_infoframes;
+       } else if (IS_VALLEYVIEW(dev)) {
+               intel_hdmi->write_infoframe = vlv_write_infoframe;
+               intel_hdmi->set_infoframes = vlv_set_infoframes;
+       } else if (IS_HASWELL(dev)) {
+               intel_hdmi->write_infoframe = hsw_write_infoframe;
+               intel_hdmi->set_infoframes = hsw_set_infoframes;
+       } else if (HAS_PCH_IBX(dev)) {
+               intel_hdmi->write_infoframe = ibx_write_infoframe;
+               intel_hdmi->set_infoframes = ibx_set_infoframes;
        } else {
-               intel_hdmi->write_infoframe = ironlake_write_infoframe;
-               for_each_pipe(i)
-                       I915_WRITE(TVIDEO_DIP_CTL(i), 0);
+               intel_hdmi->write_infoframe = cpt_write_infoframe;
+               intel_hdmi->set_infoframes = cpt_set_infoframes;
        }
 
-       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);
 
This page took 0.035009 seconds and 5 git commands to generate.