Use the generic HDMI infoframe helpers to get rid of the duplicate implementation in the i915 driver. Signed-off-by: Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx> --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/i915/intel_drv.h | 62 +-------- drivers/gpu/drm/i915/intel_hdmi.c | 268 +++++++++++++++++++++----------------- drivers/gpu/drm/i915/intel_sdvo.c | 22 ++-- 4 files changed, 159 insertions(+), 195 deletions(-) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 94a4623..5225012 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -144,6 +144,8 @@ config DRM_I915 select INPUT if ACPI select ACPI_VIDEO if ACPI select ACPI_BUTTON if ACPI + select DRM_HDMI + select HDMI help Choose this option if you have a system that has "Intel Graphics Media Accelerator" or "HD Graphics" integrated graphics, diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 522061c..70a7440 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -276,63 +276,6 @@ struct cxsr_latency { #define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base) #define to_intel_plane(x) container_of(x, struct intel_plane, base) -#define DIP_HEADER_SIZE 5 - -#define DIP_TYPE_AVI 0x82 -#define DIP_VERSION_AVI 0x2 -#define DIP_LEN_AVI 13 -#define DIP_AVI_PR_1 0 -#define DIP_AVI_PR_2 1 - -#define DIP_TYPE_SPD 0x83 -#define DIP_VERSION_SPD 0x1 -#define DIP_LEN_SPD 25 -#define DIP_SPD_UNKNOWN 0 -#define DIP_SPD_DSTB 0x1 -#define DIP_SPD_DVDP 0x2 -#define DIP_SPD_DVHS 0x3 -#define DIP_SPD_HDDVR 0x4 -#define DIP_SPD_DVC 0x5 -#define DIP_SPD_DSC 0x6 -#define DIP_SPD_VCD 0x7 -#define DIP_SPD_GAME 0x8 -#define DIP_SPD_PC 0x9 -#define DIP_SPD_BD 0xa -#define DIP_SPD_SCD 0xb - -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 - YQ 7:6, CN 5:4, PR 3:0 */ - uint8_t YQ_CN_PR; - /* PB6 to PB13 */ - uint16_t top_bar_end; - uint16_t bottom_bar_start; - uint16_t left_bar_end; - uint16_t right_bar_start; - } __attribute__ ((packed)) avi; - struct { - uint8_t vn[8]; - uint8_t pd[16]; - uint8_t sdi; - } __attribute__ ((packed)) spd; - uint8_t payload[27]; - } __attribute__ ((packed)) body; -} __attribute__((packed)); - struct intel_hdmi { u32 sdvox_reg; int ddc_bus; @@ -340,8 +283,8 @@ struct intel_hdmi { bool has_hdmi_sink; bool has_audio; enum hdmi_force_audio force_audio; - void (*write_infoframe)(struct drm_encoder *encoder, - struct dip_infoframe *frame); + void (*write_infoframe)(struct drm_encoder *encoder, const void *frame, + size_t len); void (*set_infoframes)(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode); }; @@ -430,7 +373,6 @@ extern struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder); extern bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); -extern void intel_dip_infoframe_csum(struct dip_infoframe *avi_if); extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob); extern void intel_dvo_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 5c279b4..a5492b1 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -29,9 +29,11 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/hdmi.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> +#include <drm/drm_hdmi.h> #include "intel_drv.h" #include <drm/i915_drm.h> #include "i915_drv.h" @@ -66,102 +68,112 @@ static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector) return enc_to_intel_hdmi(&intel_attached_encoder(connector)->base); } -void intel_dip_infoframe_csum(struct dip_infoframe *frame) +static u32 g4x_infoframe_index(enum hdmi_infoframe_type type) { - uint8_t *data = (uint8_t *)frame; - uint8_t sum = 0; - unsigned i; - - frame->checksum = 0; - frame->ecc = 0; - - for (i = 0; i < frame->len + DIP_HEADER_SIZE; i++) - sum += data[i]; - - frame->checksum = 0x100 - sum; -} - -static u32 g4x_infoframe_index(struct dip_infoframe *frame) -{ - switch (frame->type) { - case DIP_TYPE_AVI: + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: return VIDEO_DIP_SELECT_AVI; - case DIP_TYPE_SPD: + case HDMI_INFOFRAME_TYPE_SPD: return VIDEO_DIP_SELECT_SPD; default: - DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; } } -static u32 g4x_infoframe_enable(struct dip_infoframe *frame) +static u32 g4x_infoframe_enable(enum hdmi_infoframe_type type) { - switch (frame->type) { - case DIP_TYPE_AVI: + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: return VIDEO_DIP_ENABLE_AVI; - case DIP_TYPE_SPD: + case HDMI_INFOFRAME_TYPE_SPD: return VIDEO_DIP_ENABLE_SPD; default: - DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; } } -static u32 hsw_infoframe_enable(struct dip_infoframe *frame) +static u32 hsw_infoframe_enable(enum hdmi_infoframe_type type) { - switch (frame->type) { - case DIP_TYPE_AVI: + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: return VIDEO_DIP_ENABLE_AVI_HSW; - case DIP_TYPE_SPD: + case HDMI_INFOFRAME_TYPE_SPD: return VIDEO_DIP_ENABLE_SPD_HSW; default: - DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; } } -static u32 hsw_infoframe_data_reg(struct dip_infoframe *frame, enum pipe pipe) +static u32 hsw_infoframe_data_reg(enum hdmi_infoframe_type type, enum pipe pipe) { - switch (frame->type) { - case DIP_TYPE_AVI: + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: return HSW_TVIDEO_DIP_AVI_DATA(pipe); - case DIP_TYPE_SPD: + case HDMI_INFOFRAME_TYPE_SPD: return HSW_TVIDEO_DIP_SPD_DATA(pipe); default: - DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; } } -static void g4x_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) +static u32 intel_dip_pack(const u8 *data, size_t size) { - uint32_t *data = (uint32_t *)frame; + u32 value = 0; + size_t i; + + for (i = size; i > 0; i--) + value = (value << 8) | data[i - 1]; + + return value; +} + +static void g4x_dip_write(struct drm_i915_private *dev_priv, + unsigned long offset, const void *dip, size_t len) +{ + u32 data; + size_t i; + + /* Write infoframe header and zero out ECC at byte 4 */ + data = intel_dip_pack(dip, 3); + I915_WRITE(offset, data); + + for (i = 3; i < len; i += 4) { + size_t rem = min_t(size_t, len - i, 4); + data = intel_dip_pack(dip + i, rem); + I915_WRITE(offset, data); + } + + /* Write every possible data byte to force correct ECC calculation. */ + for (; i < VIDEO_DIP_DATA_SIZE; i += 4) + I915_WRITE(offset, 0); +} + +static void g4x_write_infoframe(struct drm_encoder *encoder, const void *frame, + size_t len) +{ + enum hdmi_infoframe_type type = ((const u8 *)frame)[0]; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; 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"); val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ - val |= g4x_infoframe_index(frame); + val |= g4x_infoframe_index(type); - val &= ~g4x_infoframe_enable(frame); + val &= ~g4x_infoframe_enable(type); I915_WRITE(VIDEO_DIP_CTL, val); mmiowb(); - for (i = 0; i < len; i += 4) { - 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); + g4x_dip_write(dev_priv, VIDEO_DIP_DATA, frame, len); mmiowb(); - val |= g4x_infoframe_enable(frame); + val |= g4x_infoframe_enable(type); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC; @@ -169,37 +181,31 @@ static void g4x_write_infoframe(struct drm_encoder *encoder, POSTING_READ(VIDEO_DIP_CTL); } -static void ibx_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) +static void ibx_write_infoframe(struct drm_encoder *encoder, const void *frame, + size_t len) { - uint32_t *data = (uint32_t *)frame; + enum hdmi_infoframe_type type = ((const u8 *)frame)[0]; 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); + val |= g4x_infoframe_index(type); - val &= ~g4x_infoframe_enable(frame); + val &= ~g4x_infoframe_enable(type); I915_WRITE(reg, val); mmiowb(); - for (i = 0; i < len; i += 4) { - 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); + g4x_dip_write(dev_priv, TVIDEO_DIP_DATA(intel_crtc->pipe), frame, + len); mmiowb(); - val |= g4x_infoframe_enable(frame); + val |= g4x_infoframe_enable(type); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC; @@ -207,40 +213,33 @@ static void ibx_write_infoframe(struct drm_encoder *encoder, POSTING_READ(reg); } -static void cpt_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) +static void cpt_write_infoframe(struct drm_encoder *encoder, const void *frame, + size_t len) { - uint32_t *data = (uint32_t *)frame; + enum hdmi_infoframe_type type = ((const u8 *)frame)[0]; 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); + val |= g4x_infoframe_index(type); /* 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); + if (type != HDMI_INFOFRAME_TYPE_AVI) + val &= ~g4x_infoframe_enable(type); I915_WRITE(reg, val); mmiowb(); - for (i = 0; i < len; i += 4) { - 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); + g4x_dip_write(dev_priv, TVIDEO_DIP_DATA(intel_crtc->pipe), frame, len); mmiowb(); - val |= g4x_infoframe_enable(frame); + val |= g4x_infoframe_enable(type); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC; @@ -248,37 +247,31 @@ static void cpt_write_infoframe(struct drm_encoder *encoder, POSTING_READ(reg); } -static void vlv_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) +static void vlv_write_infoframe(struct drm_encoder *encoder, const void *frame, + size_t len) { - uint32_t *data = (uint32_t *)frame; + enum hdmi_infoframe_type type = ((const u8 *)frame)[0]; 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_index(type); - val &= ~g4x_infoframe_enable(frame); + val &= ~g4x_infoframe_enable(type); I915_WRITE(reg, val); mmiowb(); - for (i = 0; i < len; i += 4) { - 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); + g4x_dip_write(dev_priv, VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), frame, + len); mmiowb(); - val |= g4x_infoframe_enable(frame); + val |= g4x_infoframe_enable(type); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC; @@ -286,76 +279,105 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, POSTING_READ(reg); } -static void hsw_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) +static void hsw_write_infoframe(struct drm_encoder *encoder, const void *frame, + size_t len) { - uint32_t *data = (uint32_t *)frame; + enum hdmi_infoframe_type type = ((const u8 *)frame)[0]; 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 data_reg = hsw_infoframe_data_reg(type, intel_crtc->pipe); u32 val = I915_READ(ctl_reg); + unsigned long offset; + u32 data; + size_t i; if (data_reg == 0) return; - val &= ~hsw_infoframe_enable(frame); + val &= ~hsw_infoframe_enable(type); I915_WRITE(ctl_reg, val); mmiowb(); - for (i = 0; i < len; i += 4) { - I915_WRITE(data_reg + i, *data); - data++; + + /* Write infoframe header and zero out ECC at byte 4 */ + data = intel_dip_pack(frame, 3); + I915_WRITE(data_reg, data); + + for (i = 3, offset = 4; i < len; i += 4, offset += 4) { + size_t rem = min_t(size_t, len - i, 4); + data = intel_dip_pack(frame + i, rem); + I915_WRITE(data_reg + offset, 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); + for (; i < VIDEO_DIP_DATA_SIZE; i += 4, offset += 4) + I915_WRITE(data_reg + offset, 0); + mmiowb(); - val |= hsw_infoframe_enable(frame); + val |= hsw_infoframe_enable(type); I915_WRITE(ctl_reg, val); POSTING_READ(ctl_reg); } +/* static void intel_set_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) + const struct hdmi_infoframe *frame) { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + u8 buffer[32]; + ssize_t len; - intel_dip_infoframe_csum(frame); - intel_hdmi->write_infoframe(encoder, frame); + len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer)); + if (len < 0) + return; + + intel_hdmi->write_infoframe(encoder, buffer, len); } +*/ 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, - .ver = DIP_VERSION_AVI, - .len = DIP_LEN_AVI, - }; + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + u8 buffer[HDMI_AVI_INFOFRAME_SIZE]; + struct hdmi_avi_infoframe frame; + ssize_t len; + + len = drm_hdmi_avi_infoframe_from_display_mode(&frame, adjusted_mode); + if (len < 0) + return; if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) - avi_if.body.avi.YQ_CN_PR |= DIP_AVI_PR_2; + frame.pixel_repeat = 2; + + len = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (len < 0) + return; - intel_set_infoframe(encoder, &avi_if); + intel_hdmi->write_infoframe(encoder, buffer, len); } static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) { - struct dip_infoframe spd_if; + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + u8 buffer[HDMI_SPD_INFOFRAME_SIZE]; + struct hdmi_spd_infoframe frame; + ssize_t len; + + len = hdmi_spd_infoframe_init(&frame, "Intel", "Integrated gfx"); + if (len < 0) + return; + + frame.sdi = HDMI_SPD_SDI_PC; - 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; + len = hdmi_spd_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (len < 0) + return; - intel_set_infoframe(encoder, &spd_if); + intel_hdmi->write_infoframe(encoder, buffer, len); } static void g4x_set_infoframes(struct drm_encoder *encoder, diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 4b07401..192d791 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -29,6 +29,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/export.h> +#include <linux/hdmi.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> @@ -935,20 +936,17 @@ static bool intel_sdvo_write_infoframe(struct intel_sdvo *intel_sdvo, static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo) { - struct dip_infoframe avi_if = { - .type = DIP_TYPE_AVI, - .ver = DIP_VERSION_AVI, - .len = DIP_LEN_AVI, - }; - uint8_t sdvo_data[4 + sizeof(avi_if.body.avi)]; + uint8_t sdvo_data[HDMI_AVI_INFOFRAME_SIZE]; + struct hdmi_avi_infoframe frame; + ssize_t len; - intel_dip_infoframe_csum(&avi_if); + len = hdmi_avi_infoframe_init(&frame); + if (len < 0) + return false; - /* sdvo spec says that the ecc is handled by the hw, and it looks like - * we must not send the ecc field, either. */ - memcpy(sdvo_data, &avi_if, 3); - sdvo_data[3] = avi_if.checksum; - memcpy(&sdvo_data[4], &avi_if.body, sizeof(avi_if.body.avi)); + len = hdmi_avi_infoframe_pack(&frame, sdvo_data, sizeof(sdvo_data)); + if (len < 0) + return false; return intel_sdvo_write_infoframe(intel_sdvo, SDVO_HBUF_INDEX_AVI_IF, SDVO_HBUF_TX_VSYNC, -- 1.8.0.1 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel