From 21317b3fba5428c97779cc8a988ac603e82abd8b Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 29 Sep 2016 15:34:43 -0700 Subject: [PATCH] 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 --- drivers/gpu/drm/vc4/vc4_hdmi.c | 136 +++++++++++++++++++++++++++++++-- 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 { struct vc4_hdmi_encoder { struct vc4_encoder base; bool hdmi_monitor; + bool limited_rgb_range; + bool rgb_range_selectable; }; static inline struct vc4_hdmi_encoder * @@ -205,6 +207,12 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) return -ENODEV; vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid); + + if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) { + vc4_encoder->rgb_range_selectable = + drm_rgb_quant_range_selectable(edid); + } + drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); @@ -272,6 +280,117 @@ static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = { .destroy = vc4_hdmi_encoder_destroy, }; +static int vc4_hdmi_stop_packet(struct drm_encoder *encoder, + enum hdmi_infoframe_type type) +{ + struct drm_device *dev = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + u32 packet_id = type - 0x80; + + HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, + HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id)); + + return wait_for(!(HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) & + BIT(packet_id)), 100); +} + +static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, + union hdmi_infoframe *frame) +{ + struct drm_device *dev = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + u32 packet_id = frame->any.type - 0x80; + u32 packet_reg = VC4_HDMI_GCP_0 + VC4_HDMI_PACKET_STRIDE * packet_id; + uint8_t buffer[VC4_HDMI_PACKET_STRIDE]; + ssize_t len, i; + int ret; + + WARN_ONCE(!(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & + VC4_HDMI_RAM_PACKET_ENABLE), + "Packet RAM has to be on to store the packet."); + + len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer)); + if (len < 0) + return; + + ret = vc4_hdmi_stop_packet(encoder, frame->any.type); + if (ret) { + DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret); + return; + } + + for (i = 0; i < len; i += 7) { + HDMI_WRITE(packet_reg, + buffer[i + 0] << 0 | + buffer[i + 1] << 8 | + buffer[i + 2] << 16); + packet_reg += 4; + + HDMI_WRITE(packet_reg, + buffer[i + 3] << 0 | + buffer[i + 4] << 8 | + buffer[i + 5] << 16 | + buffer[i + 6] << 24); + packet_reg += 4; + } + + HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, + HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) | BIT(packet_id)); + ret = wait_for((HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) & + BIT(packet_id)), 100); + if (ret) + DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret); +} + +static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) +{ + struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); + struct drm_crtc *crtc = encoder->crtc; + const struct drm_display_mode *mode = &crtc->state->adjusted_mode; + union hdmi_infoframe frame; + int ret; + + ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode); + if (ret < 0) { + DRM_ERROR("couldn't fill AVI infoframe\n"); + return; + } + + if (vc4_encoder->rgb_range_selectable) { + if (vc4_encoder->limited_rgb_range) { + frame.avi.quantization_range = + HDMI_QUANTIZATION_RANGE_LIMITED; + } else { + frame.avi.quantization_range = + HDMI_QUANTIZATION_RANGE_FULL; + } + } + + vc4_hdmi_write_infoframe(encoder, &frame); +} + +static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder) +{ + union hdmi_infoframe frame; + int ret; + + ret = hdmi_spd_infoframe_init(&frame.spd, "Broadcom", "Videocore"); + if (ret < 0) { + DRM_ERROR("couldn't fill SPD infoframe\n"); + return; + } + + frame.spd.sdi = HDMI_SPD_SDI_PC; + + vc4_hdmi_write_infoframe(encoder, &frame); +} + +static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) +{ + vc4_hdmi_set_avi_infoframe(encoder); + vc4_hdmi_set_spd_infoframe(encoder); +} + static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *unadjusted_mode, struct drm_display_mode *mode) @@ -340,8 +459,9 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, if (vc4_encoder->hdmi_monitor && drm_match_cea_mode(mode) > 1) { /* CEA VICs other than #1 requre limited range RGB - * output. Apply a colorspace conversion to squash - * 0-255 down to 16-235. The matrix here is: + * output unless overridden by an AVI infoframe. + * Apply a colorspace conversion to squash 0-255 down + * to 16-235. The matrix here is: * * [ 0 0 0.8594 16] * [ 0 0.8594 0 16] @@ -359,6 +479,9 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, HD_WRITE(VC4_HD_CSC_24_23, (0x100 << 16) | 0x000); HD_WRITE(VC4_HD_CSC_32_31, (0x000 << 16) | 0x6e0); HD_WRITE(VC4_HD_CSC_34_33, (0x100 << 16) | 0x000); + vc4_encoder->limited_rgb_range = true; + } else { + vc4_encoder->limited_rgb_range = false; } /* The RGB order applies even when CSC is disabled. */ @@ -377,6 +500,8 @@ static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder) struct drm_device *dev = encoder->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); + HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0); + HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); HD_WRITE(VC4_HD_VID_CTL, 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) HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT); - /* XXX: Set HDMI_RAM_PACKET_CONFIG (1 << 16) and set - * up the infoframe. - */ + HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, + VC4_HDMI_RAM_PACKET_ENABLE); + + vc4_hdmi_set_infoframes(encoder); drift = HDMI_READ(VC4_HDMI_FIFO_CTL); 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 @@ #define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0 # define VC4_HDMI_RAM_PACKET_ENABLE BIT(16) +#define VC4_HDMI_RAM_PACKET_STATUS 0x0a4 + #define VC4_HDMI_HORZA 0x0c4 # define VC4_HDMI_HORZA_VPOS BIT(14) # define VC4_HDMI_HORZA_HPOS BIT(13) @@ -502,6 +504,9 @@ #define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0 +#define VC4_HDMI_GCP_0 0x400 +#define VC4_HDMI_PACKET_STRIDE 0x24 + #define VC4_HD_M_CTL 0x00c # define VC4_HD_M_REGISTER_FILE_STANDBY (3 << 6) # define VC4_HD_M_RAM_STANDBY (3 << 4) -- 2.20.1