Merge branch 'master' of git://git.infradead.org/users/linville/wireless-next into...
[pandora-kernel.git] / drivers / gpu / drm / i915 / intel_hdmi.c
index 1ed8e69..226ba83 100644 (file)
@@ -45,6 +45,8 @@ struct intel_hdmi {
        bool has_hdmi_sink;
        bool has_audio;
        int force_audio;
+       void (*write_infoframe)(struct drm_encoder *encoder,
+                               struct dip_infoframe *frame);
 };
 
 static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
@@ -58,37 +60,70 @@ static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector)
                            struct intel_hdmi, base);
 }
 
-void intel_dip_infoframe_csum(struct dip_infoframe *avi_if)
+void intel_dip_infoframe_csum(struct dip_infoframe *frame)
 {
-       uint8_t *data = (uint8_t *)avi_if;
+       uint8_t *data = (uint8_t *)frame;
        uint8_t sum = 0;
        unsigned i;
 
-       avi_if->checksum = 0;
-       avi_if->ecc = 0;
+       frame->checksum = 0;
+       frame->ecc = 0;
 
-       for (i = 0; i < sizeof(*avi_if); i++)
+       /* Header isn't part of the checksum */
+       for (i = 5; i < frame->len; i++)
                sum += data[i];
 
-       avi_if->checksum = 0x100 - sum;
+       frame->checksum = 0x100 - sum;
 }
 
-static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+static u32 intel_infoframe_index(struct dip_infoframe *frame)
 {
-       struct dip_infoframe avi_if = {
-               .type = DIP_TYPE_AVI,
-               .ver = DIP_VERSION_AVI,
-               .len = DIP_LEN_AVI,
-       };
-       uint32_t *data = (uint32_t *)&avi_if;
+       u32 flags = 0;
+
+       switch (frame->type) {
+       case DIP_TYPE_AVI:
+               flags |= VIDEO_DIP_SELECT_AVI;
+               break;
+       case DIP_TYPE_SPD:
+               flags |= VIDEO_DIP_SELECT_SPD;
+               break;
+       default:
+               DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
+               break;
+       }
+
+       return flags;
+}
+
+static u32 intel_infoframe_flags(struct dip_infoframe *frame)
+{
+       u32 flags = 0;
+
+       switch (frame->type) {
+       case DIP_TYPE_AVI:
+               flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC;
+               break;
+       case DIP_TYPE_SPD:
+               flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_2VSYNC;
+               break;
+       default:
+               DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
+               break;
+       }
+
+       return flags;
+}
+
+static void i9xx_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;
-       unsigned i;
+       u32 port, flags, val = I915_READ(VIDEO_DIP_CTL);
+       unsigned i, len = DIP_HEADER_SIZE + frame->len;
 
-       if (!intel_hdmi->has_hdmi_sink)
-               return;
 
        /* XXX first guess at handling video port, is this corrent? */
        if (intel_hdmi->sdvox_reg == SDVOB)
@@ -98,18 +133,87 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
        else
                return;
 
-       I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
-                  VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC);
+       flags = intel_infoframe_index(frame);
+
+       val &= ~VIDEO_DIP_SELECT_MASK;
 
-       intel_dip_infoframe_csum(&avi_if);
-       for (i = 0; i < sizeof(avi_if); i += 4) {
+       I915_WRITE(VIDEO_DIP_CTL, val | port | flags);
+
+       for (i = 0; i < len; i += 4) {
                I915_WRITE(VIDEO_DIP_DATA, *data);
                data++;
        }
 
-       I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
-                  VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC |
-                  VIDEO_DIP_ENABLE_AVI);
+       flags |= intel_infoframe_flags(frame);
+
+       I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags);
+}
+
+static void ironlake_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);
+       int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+       unsigned i, len = DIP_HEADER_SIZE + frame->len;
+       u32 flags, val = I915_READ(reg);
+
+       intel_wait_for_vblank(dev, intel_crtc->pipe);
+
+       flags = intel_infoframe_index(frame);
+
+       val &= ~VIDEO_DIP_SELECT_MASK;
+
+       I915_WRITE(reg, val | flags);
+
+       for (i = 0; i < len; i += 4) {
+               I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
+               data++;
+       }
+
+       flags |= intel_infoframe_flags(frame);
+
+       I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
+}
+static void intel_set_infoframe(struct drm_encoder *encoder,
+                               struct dip_infoframe *frame)
+{
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
+       if (!intel_hdmi->has_hdmi_sink)
+               return;
+
+       intel_dip_infoframe_csum(frame);
+       intel_hdmi->write_infoframe(encoder, frame);
+}
+
+static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+{
+       struct dip_infoframe avi_if = {
+               .type = DIP_TYPE_AVI,
+               .ver = DIP_VERSION_AVI,
+               .len = DIP_LEN_AVI,
+       };
+
+       intel_set_infoframe(encoder, &avi_if);
+}
+
+static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
+{
+       struct dip_infoframe spd_if;
+
+       memset(&spd_if, 0, sizeof(spd_if));
+       spd_if.type = DIP_TYPE_SPD;
+       spd_if.ver = DIP_VERSION_SPD;
+       spd_if.len = DIP_LEN_SPD;
+       strcpy(spd_if.body.spd.vn, "Intel");
+       strcpy(spd_if.body.spd.pd, "Integrated gfx");
+       spd_if.body.spd.sdi = DIP_SPD_PC;
+
+       intel_set_infoframe(encoder, &spd_if);
 }
 
 static void intel_hdmi_mode_set(struct drm_encoder *encoder,
@@ -156,6 +260,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
        POSTING_READ(intel_hdmi->sdvox_reg);
 
        intel_hdmi_set_avi_infoframe(encoder);
+       intel_hdmi_set_spd_infoframe(encoder);
 }
 
 static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
@@ -433,6 +538,11 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
 
        intel_hdmi->sdvox_reg = sdvox_reg;
 
+       if (!HAS_PCH_SPLIT(dev))
+               intel_hdmi->write_infoframe = i9xx_write_infoframe;
+       else
+               intel_hdmi->write_infoframe = ironlake_write_infoframe;
+
        drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
 
        intel_hdmi_add_properties(intel_hdmi, connector);