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 at 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(-)

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.9.3

Reply via email to