Parse the AMD VSDB v3 from CTA extension blocks and store the result
in struct drm_amd_vsdb_info, a new field of drm_display_info. This
includes replay mode, panel type, and luminance ranges.

Signed-off-by: Chenyu Chen <[email protected]>
---
 drivers/gpu/drm/drm_edid.c  | 72 +++++++++++++++++++++++++++++++++++++
 include/drm/drm_connector.h | 38 ++++++++++++++++++++
 2 files changed, 110 insertions(+)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 26bb7710a462..76280e6e1892 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -99,6 +99,29 @@ enum drm_edid_internal_quirk {
 };
 
 #define MICROSOFT_IEEE_OUI     0xca125c
+#define AMD_IEEE_OUI        0x00001A
+
+#define AMD_VSDB_V3_PAYLOAD_MIN_LEN 15
+#define AMD_VSDB_V3_PAYLOAD_MAX_LEN 20
+
+struct amd_vsdb_v3_payload {
+       u8 oui[3];
+       u8 version;
+       u8 feature_caps;
+       u8 rsvd0[3];
+       u8 cs_eotf_support;
+       u8 lum1_max;
+       u8 lum1_min;
+       u8 lum2_max;
+       u8 lum2_min;
+       u8 rsvd1[2];
+       /*
+        * Bytes beyond AMD_VSDB_V3_PAYLOAD_MIN_LEN are optional; a
+        * monitor may provide a payload as short as 15 bytes.  Always
+        * check cea_db_payload_len() before accessing extra[].
+        */
+       u8 extra[AMD_VSDB_V3_PAYLOAD_MAX_LEN - AMD_VSDB_V3_PAYLOAD_MIN_LEN];
+} __packed;
 
 struct detailed_mode_closure {
        struct drm_connector *connector;
@@ -5205,6 +5228,13 @@ static bool cea_db_is_microsoft_vsdb(const struct cea_db 
*db)
                cea_db_payload_len(db) == 21;
 }
 
+static bool cea_db_is_amd_vsdb(const struct cea_db *db)
+{
+       return cea_db_is_vendor(db, AMD_IEEE_OUI) &&
+               cea_db_payload_len(db) >= AMD_VSDB_V3_PAYLOAD_MIN_LEN &&
+               cea_db_payload_len(db) <= AMD_VSDB_V3_PAYLOAD_MAX_LEN;
+}
+
 static bool cea_db_is_vcdb(const struct cea_db *db)
 {
        return cea_db_is_extended_tag(db, CTA_EXT_DB_VIDEO_CAP) &&
@@ -6401,6 +6431,45 @@ static void drm_parse_microsoft_vsdb(struct 
drm_connector *connector,
                    connector->base.id, connector->name, version, db[5]);
 }
 
+static void drm_parse_amd_vsdb(struct drm_connector *connector,
+                                                          const struct cea_db 
*db)
+{
+       struct drm_display_info *info = &connector->display_info;
+       const u8 *data = cea_db_data(db);
+       const struct amd_vsdb_v3_payload *p;
+
+       p = (const struct amd_vsdb_v3_payload *)data;
+
+       if (p->version != 0x03) {
+               drm_dbg_kms(connector->dev,
+                           "[CONNECTOR:%d:%s] Unsupported AMD VSDB version 
%u\n",
+                           connector->base.id, connector->name, p->version);
+               return;
+       }
+
+       info->amd_vsdb.version = p->version;
+       info->amd_vsdb.replay_mode = p->feature_caps & 0x40;
+       info->amd_vsdb.panel_type = (p->cs_eotf_support & 0xC0) >> 6;
+       info->amd_vsdb.luminance_range1.max_luminance = p->lum1_max;
+       info->amd_vsdb.luminance_range1.min_luminance = p->lum1_min;
+       info->amd_vsdb.luminance_range2.max_luminance = p->lum2_max;
+       info->amd_vsdb.luminance_range2.min_luminance = p->lum2_min;
+
+       /*
+        * The AMD VSDB v3 payload length is variable (15..20 bytes).
+        * All fields through p->rsvd1 (byte 14) are always present,
+        * but p->extra[] (bytes 15+) may not be.  Any future access to
+        * extra[] must be guarded with a runtime length check to avoid
+        * out-of-bounds reads on shorter (but spec-valid) payloads.
+        * For example:
+        *
+        *   int len = cea_db_payload_len(db);
+        *
+        *   if (len > AMD_VSDB_V3_PAYLOAD_MIN_LEN)
+        *       info->amd_vsdb.foo = p->extra[0];
+        */
+}
+
 static void drm_parse_cea_ext(struct drm_connector *connector,
                              const struct drm_edid *drm_edid)
 {
@@ -6449,6 +6518,8 @@ static void drm_parse_cea_ext(struct drm_connector 
*connector,
                        drm_parse_hdmi_forum_scds(connector, data);
                else if (cea_db_is_microsoft_vsdb(db))
                        drm_parse_microsoft_vsdb(connector, data);
+               else if (cea_db_is_amd_vsdb(db))
+                       drm_parse_amd_vsdb(connector, db);
                else if (cea_db_is_y420cmdb(db))
                        parse_cta_y420cmdb(connector, db, &y420cmdb_map);
                else if (cea_db_is_y420vdb(db))
@@ -6641,6 +6712,7 @@ static void drm_reset_display_info(struct drm_connector 
*connector)
        info->quirks = 0;
 
        info->source_physical_address = CEC_PHYS_ADDR_INVALID;
+       memset(&info->amd_vsdb, 0, sizeof(info->amd_vsdb));
 }
 
 static void update_displayid_info(struct drm_connector *connector,
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index c18be8c19de0..c398dbc68bbc 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -667,6 +667,39 @@ enum drm_bus_flags {
        DRM_BUS_FLAG_SHARP_SIGNALS = BIT(8),
 };
 
+/**
+ * struct drm_amd_vsdb_info - AMD-specific VSDB information
+ *
+ * This structure holds information parsed from the AMD Vendor-Specific Data
+ * Block (VSDB) version 3.
+ */
+struct drm_amd_vsdb_info {
+       /**
+        * @version: Version of the Vendor-Specific Data Block (VSDB)
+        */
+       u8 version;
+
+       /**
+        * @replay_mode: Panel Replay supported
+        */
+       bool replay_mode;
+
+       /**
+        * @panel_type: Panel technology type
+        */
+       u8 panel_type;
+
+       /**
+        * @luminance_range1: Luminance for max back light
+        */
+       struct drm_luminance_range_info luminance_range1;
+
+       /**
+        * @luminance_range2: Luminance for min back light
+        */
+       struct drm_luminance_range_info luminance_range2;
+};
+
 /**
  * struct drm_display_info - runtime data about the connected sink
  *
@@ -861,6 +894,11 @@ struct drm_display_info {
         * Defaults to CEC_PHYS_ADDR_INVALID (0xffff).
         */
        u16 source_physical_address;
+
+       /**
+        * @amd_vsdb: AMD-specific VSDB information.
+        */
+       struct drm_amd_vsdb_info amd_vsdb;
 };
 
 int drm_display_info_set_bus_formats(struct drm_display_info *info,
-- 
2.43.0

Reply via email to