diff options
author | Eric Anholt <eric@anholt.net> | 2016-09-29 18:34:43 -0400 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2016-10-06 14:58:28 -0400 |
commit | 21317b3fba5428c97779cc8a988ac603e82abd8b (patch) | |
tree | bd5112275c0eb2253ae44764406d2e67f2ea3317 | |
parent | 682e62c45406ccf81216481be59c2f7ca5a883d4 (diff) |
drm/vc4: Set up the AVI and SPD infoframes.
Fixes a purple bar on the left side of the screen with my Dell
2408WFP. It will also be required for supporting the double-clocked
video modes.
Signed-off-by: Eric Anholt <eric@anholt.net>
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_hdmi.c | 136 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_regs.h | 5 |
2 files changed, 136 insertions, 5 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index d94108ca961d..d6b54b905bee 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c | |||
@@ -62,6 +62,8 @@ struct vc4_hdmi { | |||
62 | struct vc4_hdmi_encoder { | 62 | struct vc4_hdmi_encoder { |
63 | struct vc4_encoder base; | 63 | struct vc4_encoder base; |
64 | bool hdmi_monitor; | 64 | bool hdmi_monitor; |
65 | bool limited_rgb_range; | ||
66 | bool rgb_range_selectable; | ||
65 | }; | 67 | }; |
66 | 68 | ||
67 | static inline struct vc4_hdmi_encoder * | 69 | static inline struct vc4_hdmi_encoder * |
@@ -205,6 +207,12 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) | |||
205 | return -ENODEV; | 207 | return -ENODEV; |
206 | 208 | ||
207 | vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid); | 209 | vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid); |
210 | |||
211 | if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) { | ||
212 | vc4_encoder->rgb_range_selectable = | ||
213 | drm_rgb_quant_range_selectable(edid); | ||
214 | } | ||
215 | |||
208 | drm_mode_connector_update_edid_property(connector, edid); | 216 | drm_mode_connector_update_edid_property(connector, edid); |
209 | ret = drm_add_edid_modes(connector, edid); | 217 | ret = drm_add_edid_modes(connector, edid); |
210 | 218 | ||
@@ -272,6 +280,117 @@ static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = { | |||
272 | .destroy = vc4_hdmi_encoder_destroy, | 280 | .destroy = vc4_hdmi_encoder_destroy, |
273 | }; | 281 | }; |
274 | 282 | ||
283 | static int vc4_hdmi_stop_packet(struct drm_encoder *encoder, | ||
284 | enum hdmi_infoframe_type type) | ||
285 | { | ||
286 | struct drm_device *dev = encoder->dev; | ||
287 | struct vc4_dev *vc4 = to_vc4_dev(dev); | ||
288 | u32 packet_id = type - 0x80; | ||
289 | |||
290 | HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, | ||
291 | HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id)); | ||
292 | |||
293 | return wait_for(!(HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) & | ||
294 | BIT(packet_id)), 100); | ||
295 | } | ||
296 | |||
297 | static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, | ||
298 | union hdmi_infoframe *frame) | ||
299 | { | ||
300 | struct drm_device *dev = encoder->dev; | ||
301 | struct vc4_dev *vc4 = to_vc4_dev(dev); | ||
302 | u32 packet_id = frame->any.type - 0x80; | ||
303 | u32 packet_reg = VC4_HDMI_GCP_0 + VC4_HDMI_PACKET_STRIDE * packet_id; | ||
304 | uint8_t buffer[VC4_HDMI_PACKET_STRIDE]; | ||
305 | ssize_t len, i; | ||
306 | int ret; | ||
307 | |||
308 | WARN_ONCE(!(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & | ||
309 | VC4_HDMI_RAM_PACKET_ENABLE), | ||
310 | "Packet RAM has to be on to store the packet."); | ||
311 | |||
312 | len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer)); | ||
313 | if (len < 0) | ||
314 | return; | ||
315 | |||
316 | ret = vc4_hdmi_stop_packet(encoder, frame->any.type); | ||
317 | if (ret) { | ||
318 | DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret); | ||
319 | return; | ||
320 | } | ||
321 | |||
322 | for (i = 0; i < len; i += 7) { | ||
323 | HDMI_WRITE(packet_reg, | ||
324 | buffer[i + 0] << 0 | | ||
325 | buffer[i + 1] << 8 | | ||
326 | buffer[i + 2] << 16); | ||
327 | packet_reg += 4; | ||
328 | |||
329 | HDMI_WRITE(packet_reg, | ||
330 | buffer[i + 3] << 0 | | ||
331 | buffer[i + 4] << 8 | | ||
332 | buffer[i + 5] << 16 | | ||
333 | buffer[i + 6] << 24); | ||
334 | packet_reg += 4; | ||
335 | } | ||
336 | |||
337 | HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, | ||
338 | HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) | BIT(packet_id)); | ||
339 | ret = wait_for((HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) & | ||
340 | BIT(packet_id)), 100); | ||
341 | if (ret) | ||
342 | DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret); | ||
343 | } | ||
344 | |||
345 | static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) | ||
346 | { | ||
347 | struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); | ||
348 | struct drm_crtc *crtc = encoder->crtc; | ||
349 | const struct drm_display_mode *mode = &crtc->state->adjusted_mode; | ||
350 | union hdmi_infoframe frame; | ||
351 | int ret; | ||
352 | |||
353 | ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode); | ||
354 | if (ret < 0) { | ||
355 | DRM_ERROR("couldn't fill AVI infoframe\n"); | ||
356 | return; | ||
357 | } | ||
358 | |||
359 | if (vc4_encoder->rgb_range_selectable) { | ||
360 | if (vc4_encoder->limited_rgb_range) { | ||
361 | frame.avi.quantization_range = | ||
362 | HDMI_QUANTIZATION_RANGE_LIMITED; | ||
363 | } else { | ||
364 | frame.avi.quantization_range = | ||
365 | HDMI_QUANTIZATION_RANGE_FULL; | ||
366 | } | ||
367 | } | ||
368 | |||
369 | vc4_hdmi_write_infoframe(encoder, &frame); | ||
370 | } | ||
371 | |||
372 | static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder) | ||
373 | { | ||
374 | union hdmi_infoframe frame; | ||
375 | int ret; | ||
376 | |||
377 | ret = hdmi_spd_infoframe_init(&frame.spd, "Broadcom", "Videocore"); | ||
378 | if (ret < 0) { | ||
379 | DRM_ERROR("couldn't fill SPD infoframe\n"); | ||
380 | return; | ||
381 | } | ||
382 | |||
383 | frame.spd.sdi = HDMI_SPD_SDI_PC; | ||
384 | |||
385 | vc4_hdmi_write_infoframe(encoder, &frame); | ||
386 | } | ||
387 | |||
388 | static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) | ||
389 | { | ||
390 | vc4_hdmi_set_avi_infoframe(encoder); | ||
391 | vc4_hdmi_set_spd_infoframe(encoder); | ||
392 | } | ||
393 | |||
275 | static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, | 394 | static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, |
276 | struct drm_display_mode *unadjusted_mode, | 395 | struct drm_display_mode *unadjusted_mode, |
277 | struct drm_display_mode *mode) | 396 | struct drm_display_mode *mode) |
@@ -340,8 +459,9 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, | |||
340 | 459 | ||
341 | if (vc4_encoder->hdmi_monitor && drm_match_cea_mode(mode) > 1) { | 460 | if (vc4_encoder->hdmi_monitor && drm_match_cea_mode(mode) > 1) { |
342 | /* CEA VICs other than #1 requre limited range RGB | 461 | /* CEA VICs other than #1 requre limited range RGB |
343 | * output. Apply a colorspace conversion to squash | 462 | * output unless overridden by an AVI infoframe. |
344 | * 0-255 down to 16-235. The matrix here is: | 463 | * Apply a colorspace conversion to squash 0-255 down |
464 | * to 16-235. The matrix here is: | ||
345 | * | 465 | * |
346 | * [ 0 0 0.8594 16] | 466 | * [ 0 0 0.8594 16] |
347 | * [ 0 0.8594 0 16] | 467 | * [ 0 0.8594 0 16] |
@@ -359,6 +479,9 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, | |||
359 | HD_WRITE(VC4_HD_CSC_24_23, (0x100 << 16) | 0x000); | 479 | HD_WRITE(VC4_HD_CSC_24_23, (0x100 << 16) | 0x000); |
360 | HD_WRITE(VC4_HD_CSC_32_31, (0x000 << 16) | 0x6e0); | 480 | HD_WRITE(VC4_HD_CSC_32_31, (0x000 << 16) | 0x6e0); |
361 | HD_WRITE(VC4_HD_CSC_34_33, (0x100 << 16) | 0x000); | 481 | HD_WRITE(VC4_HD_CSC_34_33, (0x100 << 16) | 0x000); |
482 | vc4_encoder->limited_rgb_range = true; | ||
483 | } else { | ||
484 | vc4_encoder->limited_rgb_range = false; | ||
362 | } | 485 | } |
363 | 486 | ||
364 | /* The RGB order applies even when CSC is disabled. */ | 487 | /* The RGB order applies even when CSC is disabled. */ |
@@ -377,6 +500,8 @@ static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder) | |||
377 | struct drm_device *dev = encoder->dev; | 500 | struct drm_device *dev = encoder->dev; |
378 | struct vc4_dev *vc4 = to_vc4_dev(dev); | 501 | struct vc4_dev *vc4 = to_vc4_dev(dev); |
379 | 502 | ||
503 | HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0); | ||
504 | |||
380 | HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); | 505 | HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); |
381 | HD_WRITE(VC4_HD_VID_CTL, | 506 | HD_WRITE(VC4_HD_VID_CTL, |
382 | HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE); | 507 | HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE); |
@@ -429,9 +554,10 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) | |||
429 | HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | | 554 | HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | |
430 | VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT); | 555 | VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT); |
431 | 556 | ||
432 | /* XXX: Set HDMI_RAM_PACKET_CONFIG (1 << 16) and set | 557 | HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, |
433 | * up the infoframe. | 558 | VC4_HDMI_RAM_PACKET_ENABLE); |
434 | */ | 559 | |
560 | vc4_hdmi_set_infoframes(encoder); | ||
435 | 561 | ||
436 | drift = HDMI_READ(VC4_HDMI_FIFO_CTL); | 562 | drift = HDMI_READ(VC4_HDMI_FIFO_CTL); |
437 | drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK; | 563 | drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK; |
diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index c5a423ead86f..0b868aafa8db 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h | |||
@@ -441,6 +441,8 @@ | |||
441 | #define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0 | 441 | #define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0 |
442 | # define VC4_HDMI_RAM_PACKET_ENABLE BIT(16) | 442 | # define VC4_HDMI_RAM_PACKET_ENABLE BIT(16) |
443 | 443 | ||
444 | #define VC4_HDMI_RAM_PACKET_STATUS 0x0a4 | ||
445 | |||
444 | #define VC4_HDMI_HORZA 0x0c4 | 446 | #define VC4_HDMI_HORZA 0x0c4 |
445 | # define VC4_HDMI_HORZA_VPOS BIT(14) | 447 | # define VC4_HDMI_HORZA_VPOS BIT(14) |
446 | # define VC4_HDMI_HORZA_HPOS BIT(13) | 448 | # define VC4_HDMI_HORZA_HPOS BIT(13) |
@@ -502,6 +504,9 @@ | |||
502 | 504 | ||
503 | #define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0 | 505 | #define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0 |
504 | 506 | ||
507 | #define VC4_HDMI_GCP_0 0x400 | ||
508 | #define VC4_HDMI_PACKET_STRIDE 0x24 | ||
509 | |||
505 | #define VC4_HD_M_CTL 0x00c | 510 | #define VC4_HD_M_CTL 0x00c |
506 | # define VC4_HD_M_REGISTER_FILE_STANDBY (3 << 6) | 511 | # define VC4_HD_M_REGISTER_FILE_STANDBY (3 << 6) |
507 | # define VC4_HD_M_RAM_STANDBY (3 << 4) | 512 | # define VC4_HD_M_RAM_STANDBY (3 << 4) |