i915: enable AVI infoframe for intel_hdmi.c [v4]
authorDavid Härdeman <david@hardeman.nu>
Fri, 24 Sep 2010 19:44:32 +0000 (21:44 +0200)
committerChris Wilson <chris@chris-wilson.co.uk>
Fri, 22 Oct 2010 08:14:30 +0000 (09:14 +0100)
This patch enables the sending of AVI infoframes in
drivers/gpu/drm/i915/intel_hdmi.c.

My receiver currently loses sync when the HDMI output on my computer
(DG45FC motherboard) is switched from 800x600 (the BIOS resolution) to
1920x1080 as part of the boot. Fixable by switching inputs on the receiver
a couple of times.

With this patch, my receiver has not lost sync yet (> 40 tries).

Fourth version, now based on drm-intel-next from:
git://git.kernel.org/pub/scm/linux/kernel/git/ickle/drm-intel.git

Two questions still remain:

I'm assuming that the sdvo hardware also stores a header ECC byte in
the MSB of the first dword - is this correct?

Does the SDVOB and SDVOC handling in intel_hdmi_set_avi_infoframe()
look correct?

Signed-off-by: David Härdeman <david@hardeman.nu>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_sdvo.c

index c52e209..25ed911 100644 (file)
 #define   LVDS_B0B3_POWER_DOWN         (0 << 2)
 #define   LVDS_B0B3_POWER_UP           (3 << 2)
 
+/* Video Data Island Packet control */
+#define VIDEO_DIP_DATA         0x61178
+#define VIDEO_DIP_CTL          0x61170
+#define   VIDEO_DIP_ENABLE             (1 << 31)
+#define   VIDEO_DIP_PORT_B             (1 << 29)
+#define   VIDEO_DIP_PORT_C             (2 << 29)
+#define   VIDEO_DIP_ENABLE_AVI         (1 << 21)
+#define   VIDEO_DIP_ENABLE_VENDOR      (2 << 21)
+#define   VIDEO_DIP_ENABLE_SPD         (8 << 21)
+#define   VIDEO_DIP_SELECT_AVI         (0 << 19)
+#define   VIDEO_DIP_SELECT_VENDOR      (1 << 19)
+#define   VIDEO_DIP_SELECT_SPD         (3 << 19)
+#define   VIDEO_DIP_FREQ_ONCE          (0 << 16)
+#define   VIDEO_DIP_FREQ_VSYNC         (1 << 16)
+#define   VIDEO_DIP_FREQ_2VSYNC                (2 << 16)
+
 /* Panel power sequencing */
 #define PP_STATUS      0x61200
 #define   PP_ON                (1 << 31)
index 0581e5e..9af9f86 100644 (file)
@@ -178,6 +178,38 @@ struct intel_crtc {
 #define to_intel_encoder(x) container_of(x, struct intel_encoder, base)
 #define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base)
 
+#define DIP_TYPE_AVI    0x82
+#define DIP_VERSION_AVI 0x2
+#define DIP_LEN_AVI     13
+
+struct dip_infoframe {
+       uint8_t type;           /* HB0 */
+       uint8_t ver;            /* HB1 */
+       uint8_t len;            /* HB2 - body len, not including checksum */
+       uint8_t ecc;            /* Header ECC */
+       uint8_t checksum;       /* PB0 */
+       union {
+               struct {
+                       /* PB1 - Y 6:5, A 4:4, B 3:2, S 1:0 */
+                       uint8_t Y_A_B_S;
+                       /* PB2 - C 7:6, M 5:4, R 3:0 */
+                       uint8_t C_M_R;
+                       /* PB3 - ITC 7:7, EC 6:4, Q 3:2, SC 1:0 */
+                       uint8_t ITC_EC_Q_SC;
+                       /* PB4 - VIC 6:0 */
+                       uint8_t VIC;
+                       /* PB5 - PR 3:0 */
+                       uint8_t PR;
+                       /* PB6 to PB13 */
+                       uint16_t top_bar_end;
+                       uint16_t bottom_bar_start;
+                       uint16_t left_bar_end;
+                       uint16_t right_bar_start;
+               } avi;
+               uint8_t payload[27];
+       } __attribute__ ((packed)) body;
+} __attribute__((packed));
+
 static inline struct drm_crtc *
 intel_get_crtc_for_pipe(struct drm_device *dev, int pipe)
 {
@@ -200,6 +232,7 @@ extern bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus);
 
 extern void intel_crt_init(struct drm_device *dev);
 extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg);
+void intel_dip_infoframe_csum(struct dip_infoframe *avi_if);
 extern bool intel_sdvo_init(struct drm_device *dev, int output_device);
 extern void intel_dvo_init(struct drm_device *dev);
 extern void intel_tv_init(struct drm_device *dev);
index 6c3b2ec..0d0273e 100644 (file)
@@ -58,6 +58,60 @@ 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)
+{
+       uint8_t *data = (uint8_t *)avi_if;
+       uint8_t sum = 0;
+       unsigned i;
+
+       avi_if->checksum = 0;
+       avi_if->ecc = 0;
+
+       for (i = 0; i < sizeof(*avi_if); i++)
+               sum += data[i];
+
+       avi_if->checksum = 0x100 - sum;
+}
+
+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,
+       };
+       uint32_t *data = (uint32_t *)&avi_if;
+       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;
+
+       if (!intel_hdmi->has_hdmi_sink)
+               return;
+
+       /* 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;
+
+       I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
+                  VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC);
+
+       intel_dip_infoframe_csum(&avi_if);
+       for (i = 0; i < sizeof(avi_if); 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);
+}
+
 static void intel_hdmi_mode_set(struct drm_encoder *encoder,
                                struct drm_display_mode *mode,
                                struct drm_display_mode *adjusted_mode)
@@ -79,8 +133,10 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
        if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
                sdvox |= HDMI_MODE_SELECT;
 
-       if (intel_hdmi->has_audio)
+       if (intel_hdmi->has_audio) {
                sdvox |= SDVO_AUDIO_ENABLE;
+               sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC;
+       }
 
        if (intel_crtc->pipe == 1) {
                if (HAS_PCH_CPT(dev))
@@ -91,6 +147,8 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
 
        I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
        POSTING_READ(intel_hdmi->sdvox_reg);
+
+       intel_hdmi_set_avi_infoframe(encoder);
 }
 
 static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
index c245383..6739a74 100644 (file)
@@ -854,115 +854,33 @@ static void intel_sdvo_dump_hdmi_buf(struct intel_sdvo *intel_sdvo)
 }
 #endif
 
-static bool intel_sdvo_set_hdmi_buf(struct intel_sdvo *intel_sdvo,
-                                   int index,
-                                   uint8_t *data, int8_t size, uint8_t tx_rate)
-{
-    uint8_t set_buf_index[2];
-
-    set_buf_index[0] = index;
-    set_buf_index[1] = 0;
-
-    if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_INDEX,
-                             set_buf_index, 2))
-           return false;
-
-    for (; size > 0; size -= 8) {
-       if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_DATA, data, 8))
-               return false;
-
-       data += 8;
-    }
-
-    return intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_TXRATE, &tx_rate, 1);
-}
-
-static uint8_t intel_sdvo_calc_hbuf_csum(uint8_t *data, uint8_t size)
-{
-       uint8_t csum = 0;
-       int i;
-
-       for (i = 0; i < size; i++)
-               csum += data[i];
-
-       return 0x100 - csum;
-}
-
-#define DIP_TYPE_AVI   0x82
-#define DIP_VERSION_AVI        0x2
-#define DIP_LEN_AVI    13
-
-struct dip_infoframe {
-       uint8_t type;
-       uint8_t version;
-       uint8_t len;
-       uint8_t checksum;
-       union {
-               struct {
-                       /* Packet Byte #1 */
-                       uint8_t S:2;
-                       uint8_t B:2;
-                       uint8_t A:1;
-                       uint8_t Y:2;
-                       uint8_t rsvd1:1;
-                       /* Packet Byte #2 */
-                       uint8_t R:4;
-                       uint8_t M:2;
-                       uint8_t C:2;
-                       /* Packet Byte #3 */
-                       uint8_t SC:2;
-                       uint8_t Q:2;
-                       uint8_t EC:3;
-                       uint8_t ITC:1;
-                       /* Packet Byte #4 */
-                       uint8_t VIC:7;
-                       uint8_t rsvd2:1;
-                       /* Packet Byte #5 */
-                       uint8_t PR:4;
-                       uint8_t rsvd3:4;
-                       /* Packet Byte #6~13 */
-                       uint16_t top_bar_end;
-                       uint16_t bottom_bar_start;
-                       uint16_t left_bar_end;
-                       uint16_t right_bar_start;
-               } avi;
-               struct {
-                       /* Packet Byte #1 */
-                       uint8_t channel_count:3;
-                       uint8_t rsvd1:1;
-                       uint8_t coding_type:4;
-                       /* Packet Byte #2 */
-                       uint8_t sample_size:2; /* SS0, SS1 */
-                       uint8_t sample_frequency:3;
-                       uint8_t rsvd2:3;
-                       /* Packet Byte #3 */
-                       uint8_t coding_type_private:5;
-                       uint8_t rsvd3:3;
-                       /* Packet Byte #4 */
-                       uint8_t channel_allocation;
-                       /* Packet Byte #5 */
-                       uint8_t rsvd4:3;
-                       uint8_t level_shift:4;
-                       uint8_t downmix_inhibit:1;
-               } audio;
-               uint8_t payload[28];
-       } __attribute__ ((packed)) u;
-} __attribute__((packed));
-
-static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo,
-                                        struct drm_display_mode * mode)
+static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo)
 {
        struct dip_infoframe avi_if = {
                .type = DIP_TYPE_AVI,
-               .version = DIP_VERSION_AVI,
+               .ver = DIP_VERSION_AVI,
                .len = DIP_LEN_AVI,
        };
+       uint8_t tx_rate = SDVO_HBUF_TX_VSYNC;
+       uint8_t set_buf_index[2] = { 1, 0 };
+       uint64_t *data = (uint64_t *)&avi_if;
+       unsigned i;
+
+       intel_dip_infoframe_csum(&avi_if);
+
+       if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_INDEX,
+                                 set_buf_index, 2))
+               return false;
+
+       for (i = 0; i < sizeof(avi_if); i += 8) {
+               if (!intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_DATA,
+                                         data, 8))
+                       return false;
+               data++;
+       }
 
-       avi_if.checksum = intel_sdvo_calc_hbuf_csum((uint8_t *)&avi_if,
-                                                   4 + avi_if.len);
-       return intel_sdvo_set_hdmi_buf(intel_sdvo, 1, (uint8_t *)&avi_if,
-                                      4 + avi_if.len,
-                                      SDVO_HBUF_TX_VSYNC);
+       return intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_TXRATE,
+                                   &tx_rate, 1);
 }
 
 static bool intel_sdvo_set_tv_format(struct intel_sdvo *intel_sdvo)
@@ -1116,7 +1034,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
                return;
 
        if (intel_sdvo->is_hdmi &&
-           !intel_sdvo_set_avi_infoframe(intel_sdvo, mode))
+           !intel_sdvo_set_avi_infoframe(intel_sdvo))
                return;
 
        if (intel_sdvo->is_tv &&