aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_hdmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/intel_hdmi.c')
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c158
1 files changed, 134 insertions, 24 deletions
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 1ed8e6903915..226ba830f383 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -45,6 +45,8 @@ struct intel_hdmi {
45 bool has_hdmi_sink; 45 bool has_hdmi_sink;
46 bool has_audio; 46 bool has_audio;
47 int force_audio; 47 int force_audio;
48 void (*write_infoframe)(struct drm_encoder *encoder,
49 struct dip_infoframe *frame);
48}; 50};
49 51
50static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) 52static 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)
58 struct intel_hdmi, base); 60 struct intel_hdmi, base);
59} 61}
60 62
61void intel_dip_infoframe_csum(struct dip_infoframe *avi_if) 63void intel_dip_infoframe_csum(struct dip_infoframe *frame)
62{ 64{
63 uint8_t *data = (uint8_t *)avi_if; 65 uint8_t *data = (uint8_t *)frame;
64 uint8_t sum = 0; 66 uint8_t sum = 0;
65 unsigned i; 67 unsigned i;
66 68
67 avi_if->checksum = 0; 69 frame->checksum = 0;
68 avi_if->ecc = 0; 70 frame->ecc = 0;
69 71
70 for (i = 0; i < sizeof(*avi_if); i++) 72 /* Header isn't part of the checksum */
73 for (i = 5; i < frame->len; i++)
71 sum += data[i]; 74 sum += data[i];
72 75
73 avi_if->checksum = 0x100 - sum; 76 frame->checksum = 0x100 - sum;
74} 77}
75 78
76static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) 79static u32 intel_infoframe_index(struct dip_infoframe *frame)
77{ 80{
78 struct dip_infoframe avi_if = { 81 u32 flags = 0;
79 .type = DIP_TYPE_AVI, 82
80 .ver = DIP_VERSION_AVI, 83 switch (frame->type) {
81 .len = DIP_LEN_AVI, 84 case DIP_TYPE_AVI:
82 }; 85 flags |= VIDEO_DIP_SELECT_AVI;
83 uint32_t *data = (uint32_t *)&avi_if; 86 break;
87 case DIP_TYPE_SPD:
88 flags |= VIDEO_DIP_SELECT_SPD;
89 break;
90 default:
91 DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
92 break;
93 }
94
95 return flags;
96}
97
98static u32 intel_infoframe_flags(struct dip_infoframe *frame)
99{
100 u32 flags = 0;
101
102 switch (frame->type) {
103 case DIP_TYPE_AVI:
104 flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC;
105 break;
106 case DIP_TYPE_SPD:
107 flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_2VSYNC;
108 break;
109 default:
110 DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
111 break;
112 }
113
114 return flags;
115}
116
117static void i9xx_write_infoframe(struct drm_encoder *encoder,
118 struct dip_infoframe *frame)
119{
120 uint32_t *data = (uint32_t *)frame;
84 struct drm_device *dev = encoder->dev; 121 struct drm_device *dev = encoder->dev;
85 struct drm_i915_private *dev_priv = dev->dev_private; 122 struct drm_i915_private *dev_priv = dev->dev_private;
86 struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); 123 struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
87 u32 port; 124 u32 port, flags, val = I915_READ(VIDEO_DIP_CTL);
88 unsigned i; 125 unsigned i, len = DIP_HEADER_SIZE + frame->len;
89 126
90 if (!intel_hdmi->has_hdmi_sink)
91 return;
92 127
93 /* XXX first guess at handling video port, is this corrent? */ 128 /* XXX first guess at handling video port, is this corrent? */
94 if (intel_hdmi->sdvox_reg == SDVOB) 129 if (intel_hdmi->sdvox_reg == SDVOB)
@@ -98,18 +133,87 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
98 else 133 else
99 return; 134 return;
100 135
101 I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port | 136 flags = intel_infoframe_index(frame);
102 VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC); 137
138 val &= ~VIDEO_DIP_SELECT_MASK;
103 139
104 intel_dip_infoframe_csum(&avi_if); 140 I915_WRITE(VIDEO_DIP_CTL, val | port | flags);
105 for (i = 0; i < sizeof(avi_if); i += 4) { 141
142 for (i = 0; i < len; i += 4) {
106 I915_WRITE(VIDEO_DIP_DATA, *data); 143 I915_WRITE(VIDEO_DIP_DATA, *data);
107 data++; 144 data++;
108 } 145 }
109 146
110 I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port | 147 flags |= intel_infoframe_flags(frame);
111 VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC | 148
112 VIDEO_DIP_ENABLE_AVI); 149 I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags);
150}
151
152static void ironlake_write_infoframe(struct drm_encoder *encoder,
153 struct dip_infoframe *frame)
154{
155 uint32_t *data = (uint32_t *)frame;
156 struct drm_device *dev = encoder->dev;
157 struct drm_i915_private *dev_priv = dev->dev_private;
158 struct drm_crtc *crtc = encoder->crtc;
159 struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
160 int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
161 unsigned i, len = DIP_HEADER_SIZE + frame->len;
162 u32 flags, val = I915_READ(reg);
163
164 intel_wait_for_vblank(dev, intel_crtc->pipe);
165
166 flags = intel_infoframe_index(frame);
167
168 val &= ~VIDEO_DIP_SELECT_MASK;
169
170 I915_WRITE(reg, val | flags);
171
172 for (i = 0; i < len; i += 4) {
173 I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
174 data++;
175 }
176
177 flags |= intel_infoframe_flags(frame);
178
179 I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
180}
181static void intel_set_infoframe(struct drm_encoder *encoder,
182 struct dip_infoframe *frame)
183{
184 struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
185
186 if (!intel_hdmi->has_hdmi_sink)
187 return;
188
189 intel_dip_infoframe_csum(frame);
190 intel_hdmi->write_infoframe(encoder, frame);
191}
192
193static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
194{
195 struct dip_infoframe avi_if = {
196 .type = DIP_TYPE_AVI,
197 .ver = DIP_VERSION_AVI,
198 .len = DIP_LEN_AVI,
199 };
200
201 intel_set_infoframe(encoder, &avi_if);
202}
203
204static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
205{
206 struct dip_infoframe spd_if;
207
208 memset(&spd_if, 0, sizeof(spd_if));
209 spd_if.type = DIP_TYPE_SPD;
210 spd_if.ver = DIP_VERSION_SPD;
211 spd_if.len = DIP_LEN_SPD;
212 strcpy(spd_if.body.spd.vn, "Intel");
213 strcpy(spd_if.body.spd.pd, "Integrated gfx");
214 spd_if.body.spd.sdi = DIP_SPD_PC;
215
216 intel_set_infoframe(encoder, &spd_if);
113} 217}
114 218
115static void intel_hdmi_mode_set(struct drm_encoder *encoder, 219static void intel_hdmi_mode_set(struct drm_encoder *encoder,
@@ -156,6 +260,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
156 POSTING_READ(intel_hdmi->sdvox_reg); 260 POSTING_READ(intel_hdmi->sdvox_reg);
157 261
158 intel_hdmi_set_avi_infoframe(encoder); 262 intel_hdmi_set_avi_infoframe(encoder);
263 intel_hdmi_set_spd_infoframe(encoder);
159} 264}
160 265
161static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) 266static 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)
433 538
434 intel_hdmi->sdvox_reg = sdvox_reg; 539 intel_hdmi->sdvox_reg = sdvox_reg;
435 540
541 if (!HAS_PCH_SPLIT(dev))
542 intel_hdmi->write_infoframe = i9xx_write_infoframe;
543 else
544 intel_hdmi->write_infoframe = ironlake_write_infoframe;
545
436 drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); 546 drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
437 547
438 intel_hdmi_add_properties(intel_hdmi, connector); 548 intel_hdmi_add_properties(intel_hdmi, connector);