brcm2708: update linux 4.4 patches to latest version
[openwrt/staging/yousong.git] / target / linux / brcm2708 / patches-4.4 / 0522-drm-vc4-Set-up-the-AVI-and-SPD-infoframes.patch
diff --git a/target/linux/brcm2708/patches-4.4/0522-drm-vc4-Set-up-the-AVI-and-SPD-infoframes.patch b/target/linux/brcm2708/patches-4.4/0522-drm-vc4-Set-up-the-AVI-and-SPD-infoframes.patch
new file mode 100644 (file)
index 0000000..0858b97
--- /dev/null
@@ -0,0 +1,223 @@
+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)