123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- From 76359522fa9c449fb715d1933523c153cc1871f3 Mon Sep 17 00:00:00 2001
- From: Eric Anholt <eric@anholt.net>
- Date: Thu, 29 Sep 2016 10:34:21 -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 <eric@anholt.net>
- ---
- drivers/gpu/drm/vc4/vc4_hdmi.c | 136 +++++++++++++++++++++++++++++++++++++++--
- drivers/gpu/drm/vc4/vc4_regs.h | 5 ++
- 2 files changed, 136 insertions(+), 5 deletions(-)
- --- 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(
- 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);
-
- @@ -281,6 +289,117 @@ static const struct drm_encoder_funcs vc
- .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)
- @@ -349,8 +468,9 @@ static void vc4_hdmi_encoder_mode_set(st
-
- 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]
- @@ -368,6 +488,9 @@ static void vc4_hdmi_encoder_mode_set(st
- 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. */
- @@ -386,6 +509,8 @@ static void vc4_hdmi_encoder_disable(str
- 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);
- @@ -438,9 +563,10 @@ static void vc4_hdmi_encoder_enable(stru
- 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;
- --- a/drivers/gpu/drm/vc4/vc4_regs.h
- +++ b/drivers/gpu/drm/vc4/vc4_regs.h
- @@ -443,6 +443,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)
- @@ -504,6 +506,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)
|