diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_hdmi.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_hdmi.c | 225 |
1 files changed, 188 insertions, 37 deletions
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 926934a482ec..aa0a8e83142e 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c | |||
@@ -40,12 +40,76 @@ | |||
40 | struct intel_hdmi { | 40 | struct intel_hdmi { |
41 | struct intel_encoder base; | 41 | struct intel_encoder base; |
42 | u32 sdvox_reg; | 42 | u32 sdvox_reg; |
43 | int ddc_bus; | ||
44 | uint32_t color_range; | ||
43 | bool has_hdmi_sink; | 45 | bool has_hdmi_sink; |
46 | bool has_audio; | ||
47 | int force_audio; | ||
44 | }; | 48 | }; |
45 | 49 | ||
46 | static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) | 50 | static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) |
47 | { | 51 | { |
48 | return container_of(enc_to_intel_encoder(encoder), struct intel_hdmi, base); | 52 | return container_of(encoder, struct intel_hdmi, base.base); |
53 | } | ||
54 | |||
55 | static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector) | ||
56 | { | ||
57 | return container_of(intel_attached_encoder(connector), | ||
58 | struct intel_hdmi, base); | ||
59 | } | ||
60 | |||
61 | void intel_dip_infoframe_csum(struct dip_infoframe *avi_if) | ||
62 | { | ||
63 | uint8_t *data = (uint8_t *)avi_if; | ||
64 | uint8_t sum = 0; | ||
65 | unsigned i; | ||
66 | |||
67 | avi_if->checksum = 0; | ||
68 | avi_if->ecc = 0; | ||
69 | |||
70 | for (i = 0; i < sizeof(*avi_if); i++) | ||
71 | sum += data[i]; | ||
72 | |||
73 | avi_if->checksum = 0x100 - sum; | ||
74 | } | ||
75 | |||
76 | static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) | ||
77 | { | ||
78 | struct dip_infoframe avi_if = { | ||
79 | .type = DIP_TYPE_AVI, | ||
80 | .ver = DIP_VERSION_AVI, | ||
81 | .len = DIP_LEN_AVI, | ||
82 | }; | ||
83 | uint32_t *data = (uint32_t *)&avi_if; | ||
84 | struct drm_device *dev = encoder->dev; | ||
85 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
86 | struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); | ||
87 | u32 port; | ||
88 | unsigned i; | ||
89 | |||
90 | if (!intel_hdmi->has_hdmi_sink) | ||
91 | return; | ||
92 | |||
93 | /* XXX first guess at handling video port, is this corrent? */ | ||
94 | if (intel_hdmi->sdvox_reg == SDVOB) | ||
95 | port = VIDEO_DIP_PORT_B; | ||
96 | else if (intel_hdmi->sdvox_reg == SDVOC) | ||
97 | port = VIDEO_DIP_PORT_C; | ||
98 | else | ||
99 | return; | ||
100 | |||
101 | I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port | | ||
102 | VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC); | ||
103 | |||
104 | intel_dip_infoframe_csum(&avi_if); | ||
105 | for (i = 0; i < sizeof(avi_if); i += 4) { | ||
106 | I915_WRITE(VIDEO_DIP_DATA, *data); | ||
107 | data++; | ||
108 | } | ||
109 | |||
110 | I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port | | ||
111 | VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC | | ||
112 | VIDEO_DIP_ENABLE_AVI); | ||
49 | } | 113 | } |
50 | 114 | ||
51 | static void intel_hdmi_mode_set(struct drm_encoder *encoder, | 115 | static void intel_hdmi_mode_set(struct drm_encoder *encoder, |
@@ -60,15 +124,19 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, | |||
60 | u32 sdvox; | 124 | u32 sdvox; |
61 | 125 | ||
62 | sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE; | 126 | sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE; |
127 | sdvox |= intel_hdmi->color_range; | ||
63 | if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) | 128 | if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) |
64 | sdvox |= SDVO_VSYNC_ACTIVE_HIGH; | 129 | sdvox |= SDVO_VSYNC_ACTIVE_HIGH; |
65 | if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) | 130 | if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) |
66 | sdvox |= SDVO_HSYNC_ACTIVE_HIGH; | 131 | sdvox |= SDVO_HSYNC_ACTIVE_HIGH; |
67 | 132 | ||
68 | if (intel_hdmi->has_hdmi_sink) { | 133 | /* Required on CPT */ |
134 | if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev)) | ||
135 | sdvox |= HDMI_MODE_SELECT; | ||
136 | |||
137 | if (intel_hdmi->has_audio) { | ||
69 | sdvox |= SDVO_AUDIO_ENABLE; | 138 | sdvox |= SDVO_AUDIO_ENABLE; |
70 | if (HAS_PCH_CPT(dev)) | 139 | sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC; |
71 | sdvox |= HDMI_MODE_SELECT; | ||
72 | } | 140 | } |
73 | 141 | ||
74 | if (intel_crtc->pipe == 1) { | 142 | if (intel_crtc->pipe == 1) { |
@@ -80,6 +148,8 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, | |||
80 | 148 | ||
81 | I915_WRITE(intel_hdmi->sdvox_reg, sdvox); | 149 | I915_WRITE(intel_hdmi->sdvox_reg, sdvox); |
82 | POSTING_READ(intel_hdmi->sdvox_reg); | 150 | POSTING_READ(intel_hdmi->sdvox_reg); |
151 | |||
152 | intel_hdmi_set_avi_infoframe(encoder); | ||
83 | } | 153 | } |
84 | 154 | ||
85 | static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) | 155 | static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) |
@@ -123,7 +193,7 @@ static int intel_hdmi_mode_valid(struct drm_connector *connector, | |||
123 | if (mode->clock > 165000) | 193 | if (mode->clock > 165000) |
124 | return MODE_CLOCK_HIGH; | 194 | return MODE_CLOCK_HIGH; |
125 | if (mode->clock < 20000) | 195 | if (mode->clock < 20000) |
126 | return MODE_CLOCK_HIGH; | 196 | return MODE_CLOCK_LOW; |
127 | 197 | ||
128 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | 198 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) |
129 | return MODE_NO_DBLESCAN; | 199 | return MODE_NO_DBLESCAN; |
@@ -141,36 +211,121 @@ static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, | |||
141 | static enum drm_connector_status | 211 | static enum drm_connector_status |
142 | intel_hdmi_detect(struct drm_connector *connector, bool force) | 212 | intel_hdmi_detect(struct drm_connector *connector, bool force) |
143 | { | 213 | { |
144 | struct drm_encoder *encoder = intel_attached_encoder(connector); | 214 | struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); |
145 | struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); | 215 | struct drm_i915_private *dev_priv = connector->dev->dev_private; |
146 | struct edid *edid = NULL; | 216 | struct edid *edid; |
147 | enum drm_connector_status status = connector_status_disconnected; | 217 | enum drm_connector_status status = connector_status_disconnected; |
148 | 218 | ||
149 | intel_hdmi->has_hdmi_sink = false; | 219 | intel_hdmi->has_hdmi_sink = false; |
150 | edid = drm_get_edid(connector, intel_hdmi->base.ddc_bus); | 220 | intel_hdmi->has_audio = false; |
221 | edid = drm_get_edid(connector, | ||
222 | &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter); | ||
151 | 223 | ||
152 | if (edid) { | 224 | if (edid) { |
153 | if (edid->input & DRM_EDID_INPUT_DIGITAL) { | 225 | if (edid->input & DRM_EDID_INPUT_DIGITAL) { |
154 | status = connector_status_connected; | 226 | status = connector_status_connected; |
155 | intel_hdmi->has_hdmi_sink = drm_detect_hdmi_monitor(edid); | 227 | intel_hdmi->has_hdmi_sink = drm_detect_hdmi_monitor(edid); |
228 | intel_hdmi->has_audio = drm_detect_monitor_audio(edid); | ||
156 | } | 229 | } |
157 | connector->display_info.raw_edid = NULL; | 230 | connector->display_info.raw_edid = NULL; |
158 | kfree(edid); | 231 | kfree(edid); |
159 | } | 232 | } |
160 | 233 | ||
234 | if (status == connector_status_connected) { | ||
235 | if (intel_hdmi->force_audio) | ||
236 | intel_hdmi->has_audio = intel_hdmi->force_audio > 0; | ||
237 | } | ||
238 | |||
161 | return status; | 239 | return status; |
162 | } | 240 | } |
163 | 241 | ||
164 | static int intel_hdmi_get_modes(struct drm_connector *connector) | 242 | static int intel_hdmi_get_modes(struct drm_connector *connector) |
165 | { | 243 | { |
166 | struct drm_encoder *encoder = intel_attached_encoder(connector); | 244 | struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); |
167 | struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); | 245 | struct drm_i915_private *dev_priv = connector->dev->dev_private; |
168 | 246 | ||
169 | /* We should parse the EDID data and find out if it's an HDMI sink so | 247 | /* We should parse the EDID data and find out if it's an HDMI sink so |
170 | * we can send audio to it. | 248 | * we can send audio to it. |
171 | */ | 249 | */ |
172 | 250 | ||
173 | return intel_ddc_get_modes(connector, intel_hdmi->base.ddc_bus); | 251 | return intel_ddc_get_modes(connector, |
252 | &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter); | ||
253 | } | ||
254 | |||
255 | static bool | ||
256 | intel_hdmi_detect_audio(struct drm_connector *connector) | ||
257 | { | ||
258 | struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); | ||
259 | struct drm_i915_private *dev_priv = connector->dev->dev_private; | ||
260 | struct edid *edid; | ||
261 | bool has_audio = false; | ||
262 | |||
263 | edid = drm_get_edid(connector, | ||
264 | &dev_priv->gmbus[intel_hdmi->ddc_bus].adapter); | ||
265 | if (edid) { | ||
266 | if (edid->input & DRM_EDID_INPUT_DIGITAL) | ||
267 | has_audio = drm_detect_monitor_audio(edid); | ||
268 | |||
269 | connector->display_info.raw_edid = NULL; | ||
270 | kfree(edid); | ||
271 | } | ||
272 | |||
273 | return has_audio; | ||
274 | } | ||
275 | |||
276 | static int | ||
277 | intel_hdmi_set_property(struct drm_connector *connector, | ||
278 | struct drm_property *property, | ||
279 | uint64_t val) | ||
280 | { | ||
281 | struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); | ||
282 | struct drm_i915_private *dev_priv = connector->dev->dev_private; | ||
283 | int ret; | ||
284 | |||
285 | ret = drm_connector_property_set_value(connector, property, val); | ||
286 | if (ret) | ||
287 | return ret; | ||
288 | |||
289 | if (property == dev_priv->force_audio_property) { | ||
290 | int i = val; | ||
291 | bool has_audio; | ||
292 | |||
293 | if (i == intel_hdmi->force_audio) | ||
294 | return 0; | ||
295 | |||
296 | intel_hdmi->force_audio = i; | ||
297 | |||
298 | if (i == 0) | ||
299 | has_audio = intel_hdmi_detect_audio(connector); | ||
300 | else | ||
301 | has_audio = i > 0; | ||
302 | |||
303 | if (has_audio == intel_hdmi->has_audio) | ||
304 | return 0; | ||
305 | |||
306 | intel_hdmi->has_audio = has_audio; | ||
307 | goto done; | ||
308 | } | ||
309 | |||
310 | if (property == dev_priv->broadcast_rgb_property) { | ||
311 | if (val == !!intel_hdmi->color_range) | ||
312 | return 0; | ||
313 | |||
314 | intel_hdmi->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0; | ||
315 | goto done; | ||
316 | } | ||
317 | |||
318 | return -EINVAL; | ||
319 | |||
320 | done: | ||
321 | if (intel_hdmi->base.base.crtc) { | ||
322 | struct drm_crtc *crtc = intel_hdmi->base.base.crtc; | ||
323 | drm_crtc_helper_set_mode(crtc, &crtc->mode, | ||
324 | crtc->x, crtc->y, | ||
325 | crtc->fb); | ||
326 | } | ||
327 | |||
328 | return 0; | ||
174 | } | 329 | } |
175 | 330 | ||
176 | static void intel_hdmi_destroy(struct drm_connector *connector) | 331 | static void intel_hdmi_destroy(struct drm_connector *connector) |
@@ -192,19 +347,27 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = { | |||
192 | .dpms = drm_helper_connector_dpms, | 347 | .dpms = drm_helper_connector_dpms, |
193 | .detect = intel_hdmi_detect, | 348 | .detect = intel_hdmi_detect, |
194 | .fill_modes = drm_helper_probe_single_connector_modes, | 349 | .fill_modes = drm_helper_probe_single_connector_modes, |
350 | .set_property = intel_hdmi_set_property, | ||
195 | .destroy = intel_hdmi_destroy, | 351 | .destroy = intel_hdmi_destroy, |
196 | }; | 352 | }; |
197 | 353 | ||
198 | static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = { | 354 | static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = { |
199 | .get_modes = intel_hdmi_get_modes, | 355 | .get_modes = intel_hdmi_get_modes, |
200 | .mode_valid = intel_hdmi_mode_valid, | 356 | .mode_valid = intel_hdmi_mode_valid, |
201 | .best_encoder = intel_attached_encoder, | 357 | .best_encoder = intel_best_encoder, |
202 | }; | 358 | }; |
203 | 359 | ||
204 | static const struct drm_encoder_funcs intel_hdmi_enc_funcs = { | 360 | static const struct drm_encoder_funcs intel_hdmi_enc_funcs = { |
205 | .destroy = intel_encoder_destroy, | 361 | .destroy = intel_encoder_destroy, |
206 | }; | 362 | }; |
207 | 363 | ||
364 | static void | ||
365 | intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector) | ||
366 | { | ||
367 | intel_attach_force_audio_property(connector); | ||
368 | intel_attach_broadcast_rgb_property(connector); | ||
369 | } | ||
370 | |||
208 | void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) | 371 | void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) |
209 | { | 372 | { |
210 | struct drm_i915_private *dev_priv = dev->dev_private; | 373 | struct drm_i915_private *dev_priv = dev->dev_private; |
@@ -224,6 +387,9 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) | |||
224 | } | 387 | } |
225 | 388 | ||
226 | intel_encoder = &intel_hdmi->base; | 389 | intel_encoder = &intel_hdmi->base; |
390 | drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs, | ||
391 | DRM_MODE_ENCODER_TMDS); | ||
392 | |||
227 | connector = &intel_connector->base; | 393 | connector = &intel_connector->base; |
228 | drm_connector_init(dev, connector, &intel_hdmi_connector_funcs, | 394 | drm_connector_init(dev, connector, &intel_hdmi_connector_funcs, |
229 | DRM_MODE_CONNECTOR_HDMIA); | 395 | DRM_MODE_CONNECTOR_HDMIA); |
@@ -239,39 +405,33 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) | |||
239 | /* Set up the DDC bus. */ | 405 | /* Set up the DDC bus. */ |
240 | if (sdvox_reg == SDVOB) { | 406 | if (sdvox_reg == SDVOB) { |
241 | intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT); | 407 | intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT); |
242 | intel_encoder->ddc_bus = intel_i2c_create(dev, GPIOE, "HDMIB"); | 408 | intel_hdmi->ddc_bus = GMBUS_PORT_DPB; |
243 | dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; | 409 | dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; |
244 | } else if (sdvox_reg == SDVOC) { | 410 | } else if (sdvox_reg == SDVOC) { |
245 | intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT); | 411 | intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT); |
246 | intel_encoder->ddc_bus = intel_i2c_create(dev, GPIOD, "HDMIC"); | 412 | intel_hdmi->ddc_bus = GMBUS_PORT_DPC; |
247 | dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; | 413 | dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; |
248 | } else if (sdvox_reg == HDMIB) { | 414 | } else if (sdvox_reg == HDMIB) { |
249 | intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT); | 415 | intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT); |
250 | intel_encoder->ddc_bus = intel_i2c_create(dev, PCH_GPIOE, | 416 | intel_hdmi->ddc_bus = GMBUS_PORT_DPB; |
251 | "HDMIB"); | ||
252 | dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; | 417 | dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; |
253 | } else if (sdvox_reg == HDMIC) { | 418 | } else if (sdvox_reg == HDMIC) { |
254 | intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT); | 419 | intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT); |
255 | intel_encoder->ddc_bus = intel_i2c_create(dev, PCH_GPIOD, | 420 | intel_hdmi->ddc_bus = GMBUS_PORT_DPC; |
256 | "HDMIC"); | ||
257 | dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; | 421 | dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; |
258 | } else if (sdvox_reg == HDMID) { | 422 | } else if (sdvox_reg == HDMID) { |
259 | intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT); | 423 | intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT); |
260 | intel_encoder->ddc_bus = intel_i2c_create(dev, PCH_GPIOF, | 424 | intel_hdmi->ddc_bus = GMBUS_PORT_DPD; |
261 | "HDMID"); | ||
262 | dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; | 425 | dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; |
263 | } | 426 | } |
264 | if (!intel_encoder->ddc_bus) | ||
265 | goto err_connector; | ||
266 | 427 | ||
267 | intel_hdmi->sdvox_reg = sdvox_reg; | 428 | intel_hdmi->sdvox_reg = sdvox_reg; |
268 | 429 | ||
269 | drm_encoder_init(dev, &intel_encoder->enc, &intel_hdmi_enc_funcs, | 430 | drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); |
270 | DRM_MODE_ENCODER_TMDS); | 431 | |
271 | drm_encoder_helper_add(&intel_encoder->enc, &intel_hdmi_helper_funcs); | 432 | intel_hdmi_add_properties(intel_hdmi, connector); |
272 | 433 | ||
273 | drm_mode_connector_attach_encoder(&intel_connector->base, | 434 | intel_connector_attach_encoder(intel_connector, intel_encoder); |
274 | &intel_encoder->enc); | ||
275 | drm_sysfs_connector_add(connector); | 435 | drm_sysfs_connector_add(connector); |
276 | 436 | ||
277 | /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written | 437 | /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written |
@@ -282,13 +442,4 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) | |||
282 | u32 temp = I915_READ(PEG_BAND_GAP_DATA); | 442 | u32 temp = I915_READ(PEG_BAND_GAP_DATA); |
283 | I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); | 443 | I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); |
284 | } | 444 | } |
285 | |||
286 | return; | ||
287 | |||
288 | err_connector: | ||
289 | drm_connector_cleanup(connector); | ||
290 | kfree(intel_hdmi); | ||
291 | kfree(intel_connector); | ||
292 | |||
293 | return; | ||
294 | } | 445 | } |