Commit 3c17fe4b authored by David Härdeman's avatar David Härdeman Committed by Chris Wilson

i915: enable AVI infoframe for intel_hdmi.c [v4]

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: default avatarDavid Härdeman <david@hardeman.nu>
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
parent dc3f82c2
...@@ -1353,6 +1353,22 @@ ...@@ -1353,6 +1353,22 @@
#define LVDS_B0B3_POWER_DOWN (0 << 2) #define LVDS_B0B3_POWER_DOWN (0 << 2)
#define LVDS_B0B3_POWER_UP (3 << 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 */ /* Panel power sequencing */
#define PP_STATUS 0x61200 #define PP_STATUS 0x61200
#define PP_ON (1 << 31) #define PP_ON (1 << 31)
......
...@@ -178,6 +178,38 @@ struct intel_crtc { ...@@ -178,6 +178,38 @@ struct intel_crtc {
#define to_intel_encoder(x) container_of(x, struct intel_encoder, base) #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 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 * static inline struct drm_crtc *
intel_get_crtc_for_pipe(struct drm_device *dev, int pipe) 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); ...@@ -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_crt_init(struct drm_device *dev);
extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg); 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 bool intel_sdvo_init(struct drm_device *dev, int output_device);
extern void intel_dvo_init(struct drm_device *dev); extern void intel_dvo_init(struct drm_device *dev);
extern void intel_tv_init(struct drm_device *dev); extern void intel_tv_init(struct drm_device *dev);
......
...@@ -58,6 +58,60 @@ static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector) ...@@ -58,6 +58,60 @@ static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector)
struct intel_hdmi, base); 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, static void intel_hdmi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *adjusted_mode)
...@@ -79,8 +133,10 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, ...@@ -79,8 +133,10 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev)) if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
sdvox |= HDMI_MODE_SELECT; sdvox |= HDMI_MODE_SELECT;
if (intel_hdmi->has_audio) if (intel_hdmi->has_audio) {
sdvox |= SDVO_AUDIO_ENABLE; sdvox |= SDVO_AUDIO_ENABLE;
sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC;
}
if (intel_crtc->pipe == 1) { if (intel_crtc->pipe == 1) {
if (HAS_PCH_CPT(dev)) if (HAS_PCH_CPT(dev))
...@@ -91,6 +147,8 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, ...@@ -91,6 +147,8 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
I915_WRITE(intel_hdmi->sdvox_reg, sdvox); I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
POSTING_READ(intel_hdmi->sdvox_reg); POSTING_READ(intel_hdmi->sdvox_reg);
intel_hdmi_set_avi_infoframe(encoder);
} }
static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
......
...@@ -854,115 +854,33 @@ static void intel_sdvo_dump_hdmi_buf(struct intel_sdvo *intel_sdvo) ...@@ -854,115 +854,33 @@ static void intel_sdvo_dump_hdmi_buf(struct intel_sdvo *intel_sdvo)
} }
#endif #endif
static bool intel_sdvo_set_hdmi_buf(struct intel_sdvo *intel_sdvo, static bool intel_sdvo_set_avi_infoframe(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)
{ {
struct dip_infoframe avi_if = { struct dip_infoframe avi_if = {
.type = DIP_TYPE_AVI, .type = DIP_TYPE_AVI,
.version = DIP_VERSION_AVI, .ver = DIP_VERSION_AVI,
.len = DIP_LEN_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, return intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_HBUF_TXRATE,
4 + avi_if.len); &tx_rate, 1);
return intel_sdvo_set_hdmi_buf(intel_sdvo, 1, (uint8_t *)&avi_if,
4 + avi_if.len,
SDVO_HBUF_TX_VSYNC);
} }
static bool intel_sdvo_set_tv_format(struct intel_sdvo *intel_sdvo) 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, ...@@ -1116,7 +1034,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
return; return;
if (intel_sdvo->is_hdmi && if (intel_sdvo->is_hdmi &&
!intel_sdvo_set_avi_infoframe(intel_sdvo, mode)) !intel_sdvo_set_avi_infoframe(intel_sdvo))
return; return;
if (intel_sdvo->is_tv && if (intel_sdvo->is_tv &&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment