Commit 118362a9 authored by Chenyu Chen's avatar Chenyu Chen Committed by Alex Deucher
Browse files

drm/edid: Parse AMD Vendor-Specific Data Block



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: default avatarChenyu Chen <chen-yu.chen@amd.com>
Reviewed-by: default avatarMario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 57ce498f
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -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,
+38 −0
Original line number Diff line number Diff line
@@ -693,6 +693,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
 *
@@ -883,6 +916,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,