|
@@ -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,52 +133,72 @@ 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;
|
|
|
+
|
|
|
+ I915_WRITE(VIDEO_DIP_CTL, val | port | flags);
|
|
|
|
|
|
- intel_dip_infoframe_csum(&avi_if);
|
|
|
- for (i = 0; i < sizeof(avi_if); i += 4) {
|
|
|
+ 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 intel_ironlake_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
|
|
|
+static void ironlake_write_infoframe(struct drm_encoder *encoder,
|
|
|
+ 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;
|
|
|
+ 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);
|
|
|
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;
|
|
|
-
|
|
|
- if (!intel_hdmi->has_hdmi_sink)
|
|
|
- return;
|
|
|
+ unsigned i, len = DIP_HEADER_SIZE + frame->len;
|
|
|
+ u32 flags, val = I915_READ(reg);
|
|
|
|
|
|
intel_wait_for_vblank(dev, intel_crtc->pipe);
|
|
|
|
|
|
- I915_WRITE(reg, VIDEO_DIP_SELECT_AVI);
|
|
|
+ flags = intel_infoframe_index(frame);
|
|
|
|
|
|
- intel_dip_infoframe_csum(&avi_if);
|
|
|
- for (i = 0; i < sizeof(avi_if); i += 4) {
|
|
|
+ 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++;
|
|
|
}
|
|
|
|
|
|
- I915_WRITE(reg, VIDEO_DIP_ENABLE | VIDEO_DIP_SELECT_AVI |
|
|
|
- VIDEO_DIP_FREQ_VSYNC | (DIP_LEN_AVI << 8) |
|
|
|
- VIDEO_DIP_ENABLE_AVI);
|
|
|
+ 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_mode_set(struct drm_encoder *encoder,
|
|
@@ -189,10 +244,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
|
|
|
I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
|
|
|
POSTING_READ(intel_hdmi->sdvox_reg);
|
|
|
|
|
|
- if (HAS_PCH_SPLIT(dev))
|
|
|
- intel_ironlake_hdmi_set_avi_infoframe(encoder);
|
|
|
- else
|
|
|
- intel_hdmi_set_avi_infoframe(encoder);
|
|
|
+ intel_hdmi_set_avi_infoframe(encoder);
|
|
|
}
|
|
|
|
|
|
static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
|
|
@@ -470,6 +522,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);
|