diff options
| -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) |
