[Why]
Support for triggering ALLM in modern TVs is missing.

When HDMI vsif was added in 2019:
commit 3c2381b92cba ("drm/amd/display: add support for VSIP info packet")
it was improperly handeled as HDMI actually has two separate vsifs. The
implementation was based on H14b-vsif and ALLM bit was messing it up
because H14b-vsif doesn't support ALLM. It was later removed in:
commit 75f77aafe281 ("drm/amd/display: Send H14b-VSIF specified in HDMI")

ALLM is supported by hf-vsif (HDMI Forum) instead.

[How]
Add proper logic to construct either h14b-vsif or hf-vsif based on
required capabilities. Currently, only ALLM from hf-vsif is supported.

Turns out, hf-vsif is almost identical to h14b-vsif, BUT has additional
two bytes of data after OUI. First byte is static and seems like
a version supported by leftover define. Second byte consists of 3D and
ALLM bits.

Implement logic to offset 3D data if building hf-vsif.

Signed-off-by: Tomasz Pakuła <[email protected]>
---
 .../display/modules/info_packet/info_packet.c | 112 ++++++++++++------
 1 file changed, 73 insertions(+), 39 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c 
b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
index 294f56d20062..1a1ddcdb4362 100644
--- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
@@ -49,7 +49,10 @@ enum vsc_packet_revision {
 };
 
 #define HDMI_INFOFRAME_TYPE_VENDOR 0x81
-#define HF_VSIF_VERSION 1
+#define HDMI_INFOFRAME_LENGTH_MASK 0x1F
+#define HF_VSIF_VERSION  1
+#define HF_VSIF_3D_BIT   0
+#define HF_VSIF_ALLM_BIT 1
 
 // VTEM Byte Offset
 #define VTEM_PB0               0
@@ -496,9 +499,28 @@ void mod_build_vsc_infopacket(const struct dc_stream_state 
*stream,
        }
 }
 
+static bool is_hdmi_vic_mode(const struct dc_stream_state *stream)
+{
+       bool allm = stream->link->local_sink->edid_caps.allm;
+       bool stereo = stream->view_format != VIEW_3D_FORMAT_NONE;
+
+       if (stream->timing.hdmi_vic == 0)
+               return false;
+
+       if (stream->timing.h_total < 3840 ||
+           stream->timing.v_total < 2160)
+               return false;
+
+       if (stereo || allm)
+               return false;
+
+       return true;
+}
+
 /**
  *  mod_build_hf_vsif_infopacket - Prepare HDMI Vendor Specific info frame.
  *                                 Follows HDMI Spec to build up Vendor 
Specific info frame
+ *                                 Conforms to h14b-vsif or hf-vsif based on 
the capabilities
  *
  *  @stream:      contains data we may need to construct VSIF (i.e. 
timing_3d_format, etc.)
  *  @info_packet: output structure where to store VSIF
@@ -506,63 +528,75 @@ void mod_build_vsc_infopacket(const struct 
dc_stream_state *stream,
 void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
                struct dc_info_packet *info_packet)
 {
-               unsigned int length = 5;
                bool hdmi_vic_mode = false;
+               bool allm = false;
+               bool stereo = false;
                uint8_t checksum = 0;
-               uint32_t i = 0;
+               uint8_t offset = 0;
+               uint8_t i = 0;
+               uint8_t length = 5;
+               uint32_t oui = HDMI_IEEE_OUI;
                enum dc_timing_3d_format format;
 
                info_packet->valid = false;
+
                format = stream->timing.timing_3d_format;
                if (stream->view_format == VIEW_3D_FORMAT_NONE)
                        format = TIMING_3D_FORMAT_NONE;
+               stereo = format != TIMING_3D_FORMAT_NONE;
+               hdmi_vic_mode = is_hdmi_vic_mode(stream);
 
-               if (stream->timing.hdmi_vic != 0
-                               && stream->timing.h_total >= 3840
-                               && stream->timing.v_total >= 2160
-                               && format == TIMING_3D_FORMAT_NONE)
-                       hdmi_vic_mode = true;
-
-               if ((format == TIMING_3D_FORMAT_NONE) && !hdmi_vic_mode)
+               if (!stereo && !hdmi_vic_mode && !allm)
                        return;
 
-               info_packet->sb[1] = 0x03;
-               info_packet->sb[2] = 0x0C;
-               info_packet->sb[3] = 0x00;
+               if (allm)
+                       oui = HDMI_FORUM_IEEE_OUI;
 
-               if (format != TIMING_3D_FORMAT_NONE)
-                       info_packet->sb[4] = (2 << 5);
+               info_packet->sb[1] = oui & 0xff;
+               info_packet->sb[2] = (oui >> 8) & 0xff;
+               info_packet->sb[3] = (oui >> 16) & 0xff;
 
-               else if (hdmi_vic_mode)
-                       info_packet->sb[4] = (1 << 5);
-
-               switch (format) {
-               case TIMING_3D_FORMAT_HW_FRAME_PACKING:
-               case TIMING_3D_FORMAT_SW_FRAME_PACKING:
-                       info_packet->sb[5] = (0x0 << 4);
-                       break;
-
-               case TIMING_3D_FORMAT_SIDE_BY_SIDE:
-               case TIMING_3D_FORMAT_SBS_SW_PACKED:
-                       info_packet->sb[5] = (0x8 << 4);
-                       length = 6;
-                       break;
-
-               case TIMING_3D_FORMAT_TOP_AND_BOTTOM:
-               case TIMING_3D_FORMAT_TB_SW_PACKED:
-                       info_packet->sb[5] = (0x6 << 4);
-                       break;
-
-               default:
-                       break;
+               if (oui == HDMI_FORUM_IEEE_OUI) {
+                       offset = 2;
+                       length += 2;
+                       info_packet->sb[4] = HF_VSIF_VERSION;
+                       info_packet->sb[5] = stereo << HF_VSIF_3D_BIT;
+                       info_packet->sb[5] = allm << HF_VSIF_ALLM_BIT;
                }
 
-               if (hdmi_vic_mode)
+               if (stereo) {
+                       info_packet->sb[4 + offset] = (2 << 5);
+
+                       switch (format) {
+                       case TIMING_3D_FORMAT_HW_FRAME_PACKING:
+                       case TIMING_3D_FORMAT_SW_FRAME_PACKING:
+                               info_packet->sb[5 + offset] = (0x0 << 4);
+                               break;
+
+                       case TIMING_3D_FORMAT_SIDE_BY_SIDE:
+                       case TIMING_3D_FORMAT_SBS_SW_PACKED:
+                               info_packet->sb[5 + offset] = (0x8 << 4);
+                               ++length;
+                               break;
+
+                       case TIMING_3D_FORMAT_TOP_AND_BOTTOM:
+                       case TIMING_3D_FORMAT_TB_SW_PACKED:
+                               info_packet->sb[5 + offset] = (0x6 << 4);
+                               break;
+
+                       default:
+                               break;
+                       }
+
+               /* Doesn't need the offset as it can't be used with hf-vsif */
+               } else if (hdmi_vic_mode) {
+                       info_packet->sb[4] = (1 << 5);
                        info_packet->sb[5] = stream->timing.hdmi_vic;
+               }
 
                info_packet->hb0 = HDMI_INFOFRAME_TYPE_VENDOR;
                info_packet->hb1 = 0x01;
-               info_packet->hb2 = (uint8_t) (length);
+               info_packet->hb2 = length & HDMI_INFOFRAME_LENGTH_MASK;
 
                checksum += info_packet->hb0;
                checksum += info_packet->hb1;
-- 
2.52.0

Reply via email to