* [PATCH 0/4] drm/edid: Parse AMD VSDB FreeSync range in common code
@ 2026-07-21 16:44 Alex Huang
2026-07-21 16:44 ` [PATCH 1/4] drm/edid: Parse AMD VSDB for FreeSync refresh range Alex Huang
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Alex Huang @ 2026-07-21 16:44 UTC (permalink / raw)
To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
harry.wentland, sunpeng.li, siqueira, alexander.deucher,
christian.koenig, superm1, alex.hung
Cc: dri-devel, amd-gfx, linux-kernel, Alex Huang
This patch series implements parsing for AMD VSDB block's FreeSync
related data directly in the EDID common parser.
Some monitor manufacturers advertise their VRR capability exclusively
using the AMD VSDB block's FreeSync data fields, many other
manufacturers (notably pre EDID 1.4 monitors and some post 1.4 monitors)
also do not include the relevant continuous frequency feature flag in
the EDID. This makes it difficult to differentiate true VRR capability
from supporting a few refresh rates between the range specified in EDID
refresh rate range field in a spec compliant manner.
Incidentally also resolves a recent regression where amdgpu failed to
detect some DP monitors as VRR capable after EDID parser was moved to
DRM common.
Alex Huang (4):
drm/edid: Parse AMD VSDB for FreeSync refresh range
drm/amd/display: Use HDMI FreeSync range from common EDID parser
drm/amd/display: Refactor FreeSync capability detection flow
drm/amd/display: Remove unused DMCU/DMUB EDID CEA parser
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 252 +++---------------
drivers/gpu/drm/amd/display/dc/Makefile | 1 -
.../gpu/drm/amd/display/dc/dc_edid_parser.c | 80 ------
.../gpu/drm/amd/display/dc/dc_edid_parser.h | 44 ---
drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c | 121 ---------
drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h | 10 -
.../gpu/drm/amd/display/dmub/inc/dmub_cmd.h | 71 -----
drivers/gpu/drm/drm_edid.c | 134 ++++++++--
include/drm/drm_connector.h | 22 +-
9 files changed, 169 insertions(+), 566 deletions(-)
delete mode 100644 drivers/gpu/drm/amd/display/dc/dc_edid_parser.c
delete mode 100644 drivers/gpu/drm/amd/display/dc/dc_edid_parser.h
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/4] drm/edid: Parse AMD VSDB for FreeSync refresh range
2026-07-21 16:44 [PATCH 0/4] drm/edid: Parse AMD VSDB FreeSync range in common code Alex Huang
@ 2026-07-21 16:44 ` Alex Huang
2026-07-21 16:54 ` sashiko-bot
2026-07-21 17:00 ` Mario Limonciello
2026-07-21 16:44 ` [PATCH 2/4] drm/amd/display: Use HDMI FreeSync range from common EDID parser Alex Huang
` (2 subsequent siblings)
3 siblings, 2 replies; 10+ messages in thread
From: Alex Huang @ 2026-07-21 16:44 UTC (permalink / raw)
To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
harry.wentland, sunpeng.li, siqueira, alexander.deucher,
christian.koenig, superm1, alex.hung
Cc: dri-devel, amd-gfx, linux-kernel, Alex Huang
Commit 118362a96286 ("drm/edid: Parse AMD Vendor-Specific Data Block")
added parsing of the AMD VSDB, but only for version 3 and only for the
replay/panel-type/luminance fields. Monitors that only advertise
FreeSync through a v1 or v2 AMD VSDB therefore get no variable refresh
rate range from the common EDID parser.
Restructure drm_parse_amd_vsdb() and implement parsing for the FreeSync
refresh range fields provided in the AMD VSDB.
Signed-off-by: Alex Huang <Alex.Huang2@amd.com>
---
drivers/gpu/drm/drm_edid.c | 134 ++++++++++++++++++++++++++++--------
include/drm/drm_connector.h | 22 +++++-
2 files changed, 126 insertions(+), 30 deletions(-)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index df3c25bac761..c9452066b9be 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -101,20 +101,58 @@ enum drm_edid_internal_quirk {
#define MICROSOFT_IEEE_OUI 0xca125c
#define AMD_IEEE_OUI 0x00001A
+#define AMD_VSDB_V1_PAYLOAD_LEN 8
+#define AMD_VSDB_V2_PAYLOAD_LEN 13
#define AMD_VSDB_V3_PAYLOAD_MIN_LEN 15
-#define AMD_VSDB_V3_PAYLOAD_MAX_LEN 20
+#define AMD_VSDB_V3_PAYLOAD_MAX_LEN 21
+
+#define AMD_VSDB_V3_MAX_VFREQ_EXT2_LEN 21
+
+struct amd_vsdb_common_payload {
+ u8 oui[3];
+ u8 version;
+} __packed;
+
+struct amd_vsdb_v1_payload {
+ u8 oui[3];
+ u8 version;
+ u8 feature_caps;
+ u8 min_vfreq;
+ u8 max_vfreq;
+ u8 freesync_vcp_code;
+} __packed;
+
+struct amd_vsdb_v2_payload {
+ u8 oui[3];
+ u8 version;
+ u8 feature_caps;
+ u8 min_vfreq;
+ u8 max_vfreq;
+ u8 freesync_vcp_code;
+ u8 cs_eotf_support;
+ u8 lum1_max;
+ u8 lum1_min;
+ u8 lum2_max;
+ u8 lum2_min;
+} __packed;
struct amd_vsdb_v3_payload {
u8 oui[3];
u8 version;
u8 feature_caps;
- u8 rsvd0[3];
+ u8 min_vfreq;
+ u8 max_vfreq;
+ u8 freesync_vcp_code;
u8 cs_eotf_support;
u8 lum1_max;
u8 lum1_min;
u8 lum2_max;
u8 lum2_min;
- u8 rsvd1[2];
+ u8 max_vfreq_ext_low;
+ /*
+ * byte 14: bits 0-1 extend MSB of max refresh rate
+ */
+ u8 max_vfreq_ext_high;
/*
* Bytes beyond AMD_VSDB_V3_PAYLOAD_MIN_LEN are optional; a
* monitor may provide a payload as short as 15 bytes. Always
@@ -5231,7 +5269,7 @@ static bool cea_db_is_microsoft_vsdb(const struct cea_db *db)
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_V1_PAYLOAD_LEN &&
cea_db_payload_len(db) <= AMD_VSDB_V3_PAYLOAD_MAX_LEN;
}
@@ -6431,43 +6469,81 @@ static void drm_parse_microsoft_vsdb(struct drm_connector *connector,
connector->base.id, connector->name, version, db[5]);
}
+static void drm_parse_amd_vsdb_v1(struct drm_display_info *info,
+ const u8 *data)
+{
+ const struct amd_vsdb_v1_payload *p =
+ (const struct amd_vsdb_v1_payload *)data;
+
+ info->amd_vsdb.freesync_supported = p->feature_caps & 0x1;
+ info->amd_vsdb.min_frame_rate = p->min_vfreq;
+ info->amd_vsdb.max_frame_rate = p->max_vfreq;
+ info->amd_vsdb.freesync_vcp_code = p->freesync_vcp_code;
+}
+
+static void drm_parse_amd_vsdb_v2(struct drm_display_info *info,
+ const u8 *data)
+{
+ const struct amd_vsdb_v2_payload *p =
+ (const struct amd_vsdb_v2_payload *)data;
+
+ 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;
+}
+
+static void drm_parse_amd_vsdb_v3(struct drm_display_info *info,
+ const u8 *data, int payload_len)
+{
+ const struct amd_vsdb_v3_payload *p =
+ (const struct amd_vsdb_v3_payload *)data;
+ u16 max_frame_rate;
+
+ info->amd_vsdb.replay_mode = p->feature_caps & 0x40;
+ info->amd_vsdb.panel_type = (p->cs_eotf_support & 0xC0) >> 6;
+
+ /* vfreq is provded in a different set of fields for v3. */
+ max_frame_rate = p->max_vfreq_ext_low |
+ (p->max_vfreq_ext_high & 0x3) << 8;
+
+ /*
+ * The AMD VSDB v3 payload length is variable (15..21 bytes).
+ * All fields through max_vfreq_ext_high (byte 14) are always
+ * present, but p->extra[] (bytes 15+) may not be, so any access
+ * to extra[] must be guarded with a runtime length check to
+ * avoid out-of-bounds reads on shorter (but spec-valid) payloads.
+ */
+ if (payload_len >= AMD_VSDB_V3_MAX_VFREQ_EXT2_LEN)
+ max_frame_rate |= (p->extra[5] & 0x3) << 10;
+
+ info->amd_vsdb.max_frame_rate = max_frame_rate;
+}
+
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;
+ const struct amd_vsdb_common_payload *common =
+ (const struct amd_vsdb_common_payload *)data;
- if (p->version != 0x03) {
+ if (common->version < 1 || common->version > 3) {
drm_dbg_kms(connector->dev,
"[CONNECTOR:%d:%s] Unsupported AMD VSDB version %u\n",
- connector->base.id, connector->name, p->version);
+ connector->base.id, connector->name, common->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;
+ info->amd_vsdb.version = common->version;
- /*
- * 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];
- */
+ drm_parse_amd_vsdb_v1(info, data);
+
+ if (common->version >= 2)
+ drm_parse_amd_vsdb_v2(info, data);
+
+ if (common->version >= 3)
+ drm_parse_amd_vsdb_v3(info, data, cea_db_payload_len(db));
}
static void drm_parse_cea_ext(struct drm_connector *connector,
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 5f5ca023cd65..0914a2bd8ef1 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -697,7 +697,7 @@ enum drm_bus_flags {
* struct drm_amd_vsdb_info - AMD-specific VSDB information
*
* This structure holds information parsed from the AMD Vendor-Specific Data
- * Block (VSDB) version 3.
+ * Block (VSDB) versions 1 to 3.
*/
struct drm_amd_vsdb_info {
/**
@@ -705,6 +705,26 @@ struct drm_amd_vsdb_info {
*/
u8 version;
+ /**
+ * @freesync_supported: FreeSync (variable refresh rate) supported by panel
+ */
+ bool freesync_supported;
+
+ /**
+ * @min_frame_rate: FreeSync minimum refresh rate in Hz
+ */
+ u16 min_frame_rate;
+
+ /**
+ * @max_frame_rate: FreeSync maximum refresh rate in Hz
+ */
+ u16 max_frame_rate;
+
+ /**
+ * @freesync_vcp_code: MCCS VCP code for FreeSync
+ */
+ u8 freesync_vcp_code;
+
/**
* @replay_mode: Panel Replay supported
*/
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/4] drm/amd/display: Use HDMI FreeSync range from common EDID parser
2026-07-21 16:44 [PATCH 0/4] drm/edid: Parse AMD VSDB FreeSync range in common code Alex Huang
2026-07-21 16:44 ` [PATCH 1/4] drm/edid: Parse AMD VSDB for FreeSync refresh range Alex Huang
@ 2026-07-21 16:44 ` Alex Huang
2026-07-21 17:03 ` sashiko-bot
2026-07-21 16:44 ` [PATCH 3/4] drm/amd/display: Clean up FreeSync capability detection Alex Huang
2026-07-21 16:44 ` [PATCH 4/4] drm/amd/display: Remove unused DMCU/DMUB EDID CEA parser Alex Huang
3 siblings, 1 reply; 10+ messages in thread
From: Alex Huang @ 2026-07-21 16:44 UTC (permalink / raw)
To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
harry.wentland, sunpeng.li, siqueira, alexander.deucher,
christian.koenig, superm1, alex.hung
Cc: dri-devel, amd-gfx, linux-kernel, Alex Huang
HDMI FreeSync on amdgpu is detected using the AMD VSDB parser in the
DMUB firmware, now that the common DRM EDID parser can do the same, use
the common parser.
Extend get_amd_vsdb() to copy the relevant flags and data now being
extracted from the AMD VSDB. Additionally, parse_hdmi_amd_vsdb() and the
parse_edid_cea() firmware helpers now have no callers; remove them.
Signed-off-by: Alex Huang <Alex.Huang2@amd.com>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 178 +-----------------
1 file changed, 8 insertions(+), 170 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 9c564cd5edee..fd9bb205c8f0 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -36,7 +36,6 @@
#include "dc/inc/hw/dmcu.h"
#include "dc/inc/hw/abm.h"
#include "dc/dc_dmub_srv.h"
-#include "dc/dc_edid_parser.h"
#include "dc/dc_stat.h"
#include "dc/dc_state.h"
#include "amdgpu_dm_trace.h"
@@ -13632,139 +13631,6 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
return ret;
}
-static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm,
- unsigned int offset,
- unsigned int total_length,
- u8 *data,
- unsigned int length,
- struct amdgpu_hdmi_vsdb_info *vsdb)
-{
- bool res;
- union dmub_rb_cmd cmd;
- struct dmub_cmd_send_edid_cea *input;
- struct dmub_cmd_edid_cea_output *output;
-
- if (length > DMUB_EDID_CEA_DATA_CHUNK_BYTES)
- return false;
-
- memset(&cmd, 0, sizeof(cmd));
-
- input = &cmd.edid_cea.data.input;
-
- cmd.edid_cea.header.type = DMUB_CMD__EDID_CEA;
- cmd.edid_cea.header.sub_type = 0;
- cmd.edid_cea.header.payload_bytes =
- sizeof(cmd.edid_cea) - sizeof(cmd.edid_cea.header);
- input->offset = offset;
- input->length = length;
- input->cea_total_length = total_length;
- memcpy(input->payload, data, length);
-
- res = dc_wake_and_execute_dmub_cmd(dm->dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY);
- if (!res) {
- drm_err(adev_to_drm(dm->adev), "EDID CEA parser failed\n");
- return false;
- }
-
- output = &cmd.edid_cea.data.output;
-
- if (output->type == DMUB_CMD__EDID_CEA_ACK) {
- if (!output->ack.success) {
- drm_err(adev_to_drm(dm->adev), "EDID CEA ack failed at offset %d\n",
- output->ack.offset);
- }
- } else if (output->type == DMUB_CMD__EDID_CEA_AMD_VSDB) {
- if (!output->amd_vsdb.vsdb_found)
- return false;
-
- vsdb->freesync_supported = output->amd_vsdb.freesync_supported;
- vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version;
- vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate;
- vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate;
- vsdb->freesync_mccs_vcp_code = output->amd_vsdb.freesync_mccs_vcp_code;
- } else {
- drm_warn(adev_to_drm(dm->adev), "Unknown EDID CEA parser results\n");
- return false;
- }
-
- return true;
-}
-
-static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- int i;
-
- /* send extension block to DMCU for parsing */
- for (i = 0; i < len; i += 8) {
- bool res;
- int offset;
-
- /* send 8 bytes a time */
- if (!dc_edid_parser_send_cea(dm->dc, i, len, &edid_ext[i], 8))
- return false;
-
- if (i+8 == len) {
- /* EDID block sent completed, expect result */
- int version, min_rate, max_rate;
-
- res = dc_edid_parser_recv_amd_vsdb(dm->dc, &version, &min_rate, &max_rate);
- if (res) {
- /* amd vsdb found */
- vsdb_info->freesync_supported = 1;
- vsdb_info->amd_vsdb_version = version;
- vsdb_info->min_refresh_rate_hz = min_rate;
- vsdb_info->max_refresh_rate_hz = max_rate;
- /* Not enabled on DMCU*/
- vsdb_info->freesync_mccs_vcp_code = 0;
- return true;
- }
- /* not amd vsdb */
- return false;
- }
-
- /* check for ack*/
- res = dc_edid_parser_recv_cea_ack(dm->dc, &offset);
- if (!res)
- return false;
- }
-
- return false;
-}
-
-static bool parse_edid_cea_dmub(struct amdgpu_display_manager *dm,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- int i;
-
- /* send extension block to DMCU for parsing */
- for (i = 0; i < len; i += 8) {
- /* send 8 bytes a time */
- if (!dm_edid_parser_send_cea(dm, i, len, &edid_ext[i], 8, vsdb_info))
- return false;
- }
-
- return vsdb_info->freesync_supported;
-}
-
-static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
- bool ret;
-
- mutex_lock(&adev->dm.dc_lock);
- if (adev->dm.dmub_srv)
- ret = parse_edid_cea_dmub(&adev->dm, edid_ext, len, vsdb_info);
- else
- ret = parse_edid_cea_dmcu(&adev->dm, edid_ext, len, vsdb_info);
- mutex_unlock(&adev->dm.dc_lock);
- return ret;
-}
-
static void parse_edid_displayid_vrr(struct drm_connector *connector,
const struct edid *edid)
{
@@ -13815,42 +13681,14 @@ static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
vsdb_info->replay_mode = connector->display_info.amd_vsdb.replay_mode;
vsdb_info->amd_vsdb_version = connector->display_info.amd_vsdb.version;
+ vsdb_info->freesync_supported = connector->display_info.amd_vsdb.freesync_supported;
+ vsdb_info->min_refresh_rate_hz = connector->display_info.amd_vsdb.min_frame_rate;
+ vsdb_info->max_refresh_rate_hz = connector->display_info.amd_vsdb.max_frame_rate;
+ vsdb_info->freesync_mccs_vcp_code = connector->display_info.amd_vsdb.freesync_vcp_code;
return connector->display_info.amd_vsdb.version != 0;
}
-static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- const struct edid *edid,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- u8 *edid_ext = NULL;
- int i;
- bool valid_vsdb_found = false;
-
- /*----- drm_find_cea_extension() -----*/
- /* No EDID or EDID extensions */
- if (edid == NULL || edid->extensions == 0)
- return -ENODEV;
-
- /* Find CEA extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (uint8_t *)edid + EDID_LENGTH * (i + 1);
- if (edid_ext[0] == CEA_EXT)
- break;
- }
-
- if (i == edid->extensions)
- return -ENODEV;
-
- /*----- cea_db_offsets() -----*/
- if (edid_ext[0] != CEA_EXT)
- return -ENODEV;
-
- valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, vsdb_info);
-
- return valid_vsdb_found ? i : -ENODEV;
-}
-
/**
* amdgpu_dm_update_freesync_caps - Update Freesync capabilities
*
@@ -13932,8 +13770,8 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
}
} else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0) {
+ i = get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
+ if (i) {
amdgpu_dm_connector->vsdb_info = vsdb_info;
sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
@@ -13953,8 +13791,8 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link);
if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0) {
+ i = get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
+ if (i) {
amdgpu_dm_connector->vsdb_info = vsdb_info;
sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/4] drm/amd/display: Clean up FreeSync capability detection
2026-07-21 16:44 [PATCH 0/4] drm/edid: Parse AMD VSDB FreeSync range in common code Alex Huang
2026-07-21 16:44 ` [PATCH 1/4] drm/edid: Parse AMD VSDB for FreeSync refresh range Alex Huang
2026-07-21 16:44 ` [PATCH 2/4] drm/amd/display: Use HDMI FreeSync range from common EDID parser Alex Huang
@ 2026-07-21 16:44 ` Alex Huang
2026-07-21 16:55 ` sashiko-bot
2026-07-21 16:44 ` [PATCH 4/4] drm/amd/display: Remove unused DMCU/DMUB EDID CEA parser Alex Huang
3 siblings, 1 reply; 10+ messages in thread
From: Alex Huang @ 2026-07-21 16:44 UTC (permalink / raw)
To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
harry.wentland, sunpeng.li, siqueira, alexander.deucher,
christian.koenig, superm1, alex.hung
Cc: dri-devel, amd-gfx, linux-kernel, Alex Huang
Many duplicate codes can be extracted to common code, fill
amdgpu_dm_connector fields outside of the DP/eDP, HDMI branches,
pull the VSDB data once using get_amd_vsdb() and then derive the refresh
rate ranges.
When a monitor advertises support for FreeSync, the refresh rate from
the AMD VSDB is used, otherwise, falls back on the monitor_range, either
EDID refresh rate range field or DisplayID VRR data.
Signed-off-by: Alex Huang <Alex.Huang2@amd.com>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 82 +++++++++----------
1 file changed, 39 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index fd9bb205c8f0..23bfcefa1dbb 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -13707,7 +13707,7 @@ static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
const struct drm_edid *drm_edid, bool do_mccs)
{
- int i = 0;
+ bool has_vsdb = 0;
struct amdgpu_dm_connector *amdgpu_dm_connector =
to_amdgpu_dm_connector(connector);
struct dm_connector_state *dm_con_state = NULL;
@@ -13751,63 +13751,59 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
connector->display_info.monitor_range.max_vfreq == 0))
parse_edid_displayid_vrr(connector, edid);
- if (edid && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
- sink->sink_signal == SIGNAL_TYPE_EDP)) {
- if (amdgpu_dm_connector->dc_link &&
- amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) {
- amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq;
- amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
+ has_vsdb = get_amd_vsdb(amdgpu_dm_connector, &vsdb_info) != 0;
+
+ if (has_vsdb) {
+ amdgpu_dm_connector->vsdb_info = vsdb_info;
+ amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version;
+
+ /* copy refresh rate info as long as VSDB advertises FreeSync */
+ if (vsdb_info.freesync_supported) {
+ amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
+ amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
+
+ connector->display_info.monitor_range.min_vfreq =
+ vsdb_info.min_refresh_rate_hz;
+ connector->display_info.monitor_range.max_vfreq =
+ vsdb_info.max_refresh_rate_hz;
}
+ } else {
+ /* fall back to the base EDID range when there is no VSDB */
+ amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq;
+ amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq;
+ }
- get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
+ if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
+ sink->sink_signal == SIGNAL_TYPE_EDP) {
+ if (amdgpu_dm_connector->dc_link &&
+ amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param &&
+ amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
+ freesync_capable = true;
if (vsdb_info.replay_mode) {
amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode;
- amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version;
amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP;
}
+ } else if (has_vsdb && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
+ sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
- } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
- i = get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
- if (i) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
- sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
-
- if (vsdb_info.freesync_supported) {
- amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
- amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
-
- connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
- connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
- }
- }
+ if (vsdb_info.freesync_supported &&
+ amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
+ freesync_capable = true;
}
if (amdgpu_dm_connector->dc_link)
as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link);
- if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) {
- i = get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
- if (i) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
- sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
+ if (has_vsdb && as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) {
+ sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
- if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) {
- amdgpu_dm_connector->pack_sdp_v1_3 = true;
- amdgpu_dm_connector->as_type = as_type;
+ if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) {
+ amdgpu_dm_connector->pack_sdp_v1_3 = true;
+ amdgpu_dm_connector->as_type = as_type;
- amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
- amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
-
- connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
- connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
- }
+ if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
+ freesync_capable = true;
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 4/4] drm/amd/display: Remove unused DMCU/DMUB EDID CEA parser
2026-07-21 16:44 [PATCH 0/4] drm/edid: Parse AMD VSDB FreeSync range in common code Alex Huang
` (2 preceding siblings ...)
2026-07-21 16:44 ` [PATCH 3/4] drm/amd/display: Clean up FreeSync capability detection Alex Huang
@ 2026-07-21 16:44 ` Alex Huang
2026-07-21 16:59 ` sashiko-bot
3 siblings, 1 reply; 10+ messages in thread
From: Alex Huang @ 2026-07-21 16:44 UTC (permalink / raw)
To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
harry.wentland, sunpeng.li, siqueira, alexander.deucher,
christian.koenig, superm1, alex.hung
Cc: dri-devel, amd-gfx, linux-kernel, Alex Huang
With HDMI FreeSync detection now sourced from the common DRM EDID
parser, the DMUB parsing for AMD VSDB is no longer used.
Remove all relevant fields and files for this flow, it is no longer
necessary to ever take firmware round-trip for this.
Signed-off-by: Alex Huang <Alex.Huang2@amd.com>
---
drivers/gpu/drm/amd/display/dc/Makefile | 1 -
.../gpu/drm/amd/display/dc/dc_edid_parser.c | 80 ------------
.../gpu/drm/amd/display/dc/dc_edid_parser.h | 44 -------
drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c | 121 ------------------
drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h | 10 --
.../gpu/drm/amd/display/dmub/inc/dmub_cmd.h | 71 ----------
6 files changed, 327 deletions(-)
delete mode 100644 drivers/gpu/drm/amd/display/dc/dc_edid_parser.c
delete mode 100644 drivers/gpu/drm/amd/display/dc/dc_edid_parser.h
diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile
index 93d02956c5eb..e88a9cea8d88 100644
--- a/drivers/gpu/drm/amd/display/dc/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/Makefile
@@ -63,7 +63,6 @@ include $(AMD_DC)
FILES =
FILES += dc_dmub_srv.o
-FILES += dc_edid_parser.o
FILES += dc_fused_io.o
FILES += dc_helper.o
FILES += core/dc.o
diff --git a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.c b/drivers/gpu/drm/amd/display/dc/dc_edid_parser.c
deleted file mode 100644
index 0db5b49e9d5e..000000000000
--- a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2021 Advanced Micro Devices, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: AMD
- *
- */
-
-#include "dce/dce_dmcu.h"
-#include "dc_edid_parser.h"
-
-bool dc_edid_parser_send_cea(struct dc *dc,
- int offset,
- int total_length,
- uint8_t *data,
- int length)
-{
- struct dmcu *dmcu = dc->res_pool->dmcu;
-
- if (dmcu &&
- dmcu->funcs->is_dmcu_initialized(dmcu) &&
- dmcu->funcs->send_edid_cea) {
- return dmcu->funcs->send_edid_cea(dmcu,
- offset,
- total_length,
- data,
- length);
- }
-
- return false;
-}
-
-bool dc_edid_parser_recv_cea_ack(struct dc *dc, int *offset)
-{
- struct dmcu *dmcu = dc->res_pool->dmcu;
-
- if (dmcu &&
- dmcu->funcs->is_dmcu_initialized(dmcu) &&
- dmcu->funcs->recv_edid_cea_ack) {
- return dmcu->funcs->recv_edid_cea_ack(dmcu, offset);
- }
-
- return false;
-}
-
-bool dc_edid_parser_recv_amd_vsdb(struct dc *dc,
- int *version,
- int *min_frame_rate,
- int *max_frame_rate)
-{
- struct dmcu *dmcu = dc->res_pool->dmcu;
-
- if (dmcu &&
- dmcu->funcs->is_dmcu_initialized(dmcu) &&
- dmcu->funcs->recv_amd_vsdb) {
- return dmcu->funcs->recv_amd_vsdb(dmcu,
- version,
- min_frame_rate,
- max_frame_rate);
- }
-
- return false;
-}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.h b/drivers/gpu/drm/amd/display/dc/dc_edid_parser.h
deleted file mode 100644
index da67ec06f0a2..000000000000
--- a/drivers/gpu/drm/amd/display/dc/dc_edid_parser.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2021 Advanced Micro Devices, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: AMD
- *
- */
-
-#ifndef _DC_EDID_PARSER_H_
-#define _DC_EDID_PARSER_H_
-
-#include "core_types.h"
-
-bool dc_edid_parser_send_cea(struct dc *dc,
- int offset,
- int total_length,
- uint8_t *data,
- int length);
-
-bool dc_edid_parser_recv_cea_ack(struct dc *dc, int *offset);
-
-bool dc_edid_parser_recv_amd_vsdb(struct dc *dc,
- int *version,
- int *min_frame_rate,
- int *max_frame_rate);
-
-#endif /* _DC_EDID_PARSER_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c
index 25ebd8a52ae4..23de1c793ec3 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c
@@ -56,9 +56,6 @@
#define MCP_BL_SET_PWM_FRAC 0x6A /* Enable or disable Fractional PWM */
#define CRC_WIN_NOTIFY 0x92
#define CRC_STOP_UPDATE 0x93
-#define MCP_SEND_EDID_CEA 0xA0
-#define EDID_CEA_CMD_ACK 1
-#define EDID_CEA_CMD_NACK 2
#define MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK 0x00000001L
// PSP FW version
@@ -812,121 +809,6 @@ static bool dcn20_unlock_phy(struct dmcu *dmcu)
return true;
}
-static bool dcn10_send_edid_cea(struct dmcu *dmcu,
- int offset,
- int total_length,
- uint8_t *data,
- int length)
-{
- struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu);
- uint32_t header, data1, data2;
-
- /* If microcontroller is not running, do nothing */
- if (dmcu->dmcu_state != DMCU_RUNNING)
- return false;
-
- if (length > 8 || length <= 0)
- return false;
-
- header = ((uint32_t)offset & 0xFFFF) << 16 | (total_length & 0xFFFF);
- data1 = (((uint32_t)data[0]) << 24) | (((uint32_t)data[1]) << 16) |
- (((uint32_t)data[2]) << 8) | ((uint32_t)data[3]);
- data2 = (((uint32_t)data[4]) << 24) | (((uint32_t)data[5]) << 16) |
- (((uint32_t)data[6]) << 8) | ((uint32_t)data[7]);
-
- /* waitDMCUReadyForCmd */
- REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000);
-
- /* setDMCUParam_Cmd */
- REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, MCP_SEND_EDID_CEA);
-
- REG_WRITE(MASTER_COMM_DATA_REG1, header);
- REG_WRITE(MASTER_COMM_DATA_REG2, data1);
- REG_WRITE(MASTER_COMM_DATA_REG3, data2);
-
- /* notifyDMCUMsg */
- REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1);
-
- /* waitDMCUReadyForCmd */
- REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000);
-
- return true;
-}
-
-static bool dcn10_get_scp_results(struct dmcu *dmcu,
- uint32_t *cmd,
- uint32_t *data1,
- uint32_t *data2,
- uint32_t *data3)
-{
- struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu);
-
- /* If microcontroller is not running, do nothing */
- if (dmcu->dmcu_state != DMCU_RUNNING)
- return false;
-
- *cmd = REG_READ(SLAVE_COMM_CMD_REG);
- *data1 = REG_READ(SLAVE_COMM_DATA_REG1);
- *data2 = REG_READ(SLAVE_COMM_DATA_REG2);
- *data3 = REG_READ(SLAVE_COMM_DATA_REG3);
-
- /* clear SCP interrupt */
- REG_UPDATE(SLAVE_COMM_CNTL_REG, SLAVE_COMM_INTERRUPT, 0);
-
- return true;
-}
-
-static bool dcn10_recv_amd_vsdb(struct dmcu *dmcu,
- int *version,
- int *min_frame_rate,
- int *max_frame_rate)
-{
- uint32_t data[4];
- int cmd, ack, len;
-
- if (!dcn10_get_scp_results(dmcu, &data[0], &data[1], &data[2], &data[3]))
- return false;
-
- cmd = data[0] & 0x3FF;
- len = (data[0] >> 10) & 0x3F;
- ack = data[1];
-
- if (cmd != MCP_SEND_EDID_CEA || ack != EDID_CEA_CMD_ACK || len != 12)
- return false;
-
- if ((data[2] & 0xFF)) {
- *version = (data[2] >> 8) & 0xFF;
- *min_frame_rate = (data[3] >> 16) & 0xFFFF;
- *max_frame_rate = data[3] & 0xFFFF;
- return true;
- }
-
- return false;
-}
-
-static bool dcn10_recv_edid_cea_ack(struct dmcu *dmcu, int *offset)
-{
- uint32_t data[4];
- int cmd, ack;
-
- if (!dcn10_get_scp_results(dmcu,
- &data[0], &data[1], &data[2], &data[3]))
- return false;
-
- cmd = data[0] & 0x3FF;
- ack = data[1];
-
- if (cmd != MCP_SEND_EDID_CEA)
- return false;
-
- if (ack == EDID_CEA_CMD_ACK)
- return true;
-
- *offset = data[2]; /* nack */
- return false;
-}
-
-
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
static void dcn10_forward_crc_window(struct dmcu *dmcu,
struct rect *rect,
@@ -1030,9 +912,6 @@ static const struct dmcu_funcs dcn10_funcs = {
.get_psr_state = dcn10_get_dmcu_psr_state,
.set_psr_wait_loop = dcn10_psr_wait_loop,
.get_psr_wait_loop = dcn10_get_psr_wait_loop,
- .send_edid_cea = dcn10_send_edid_cea,
- .recv_amd_vsdb = dcn10_recv_amd_vsdb,
- .recv_edid_cea_ack = dcn10_recv_edid_cea_ack,
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
.forward_crc_window = dcn10_forward_crc_window,
.stop_crc_win_update = dcn10_stop_crc_win_update,
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h
index de3113ecbc77..eebfd177463d 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h
@@ -74,16 +74,6 @@ struct dmcu_funcs {
bool (*is_dmcu_initialized)(struct dmcu *dmcu);
bool (*lock_phy)(struct dmcu *dmcu);
bool (*unlock_phy)(struct dmcu *dmcu);
- bool (*send_edid_cea)(struct dmcu *dmcu,
- int offset,
- int total_length,
- uint8_t *data,
- int length);
- bool (*recv_amd_vsdb)(struct dmcu *dmcu,
- int *version,
- int *min_frame_rate,
- int *max_frame_rate);
- bool (*recv_edid_cea_ack)(struct dmcu *dmcu, int *offset);
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
void (*forward_crc_window)(struct dmcu *dmcu,
struct rect *rect,
diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
index 6f6a59a23495..ffe8192d223b 100644
--- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
+++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
@@ -1891,10 +1891,6 @@ enum dmub_cmd_type {
* Command type used for interfacing with DPIA.
*/
DMUB_CMD__DPIA = 77,
- /**
- * Command type used for EDID CEA parsing
- */
- DMUB_CMD__EDID_CEA = 79,
/**
* Command type used for getting usbc cable ID
*/
@@ -6732,69 +6728,6 @@ struct dmub_rb_cmd_transmitter_set_phy_fsm {
struct dmub_rb_cmd_transmitter_set_phy_fsm_data data; /**< payload */
};
-/**
- * Maximum number of bytes a chunk sent to DMUB for parsing
- */
-#define DMUB_EDID_CEA_DATA_CHUNK_BYTES 8
-
-/**
- * Represent a chunk of CEA blocks sent to DMUB for parsing
- */
-struct dmub_cmd_send_edid_cea {
- uint16_t offset; /**< offset into the CEA block */
- uint8_t length; /**< number of bytes in payload to copy as part of CEA block */
- uint16_t cea_total_length; /**< total length of the CEA block */
- uint8_t payload[DMUB_EDID_CEA_DATA_CHUNK_BYTES]; /**< data chunk of the CEA block */
- uint8_t pad[3]; /**< padding and for future expansion */
-};
-
-/**
- * Result of VSDB parsing from CEA block
- */
-struct dmub_cmd_edid_cea_amd_vsdb {
- uint8_t vsdb_found; /**< 1 if parsing has found valid AMD VSDB */
- uint8_t freesync_supported; /**< 1 if Freesync is supported */
- uint16_t amd_vsdb_version; /**< AMD VSDB version */
- uint16_t min_frame_rate; /**< Maximum frame rate */
- uint16_t max_frame_rate; /**< Minimum frame rate */
- uint8_t freesync_mccs_vcp_code; /**< Freesync MCCS VCP code */
-};
-
-/**
- * Result of sending a CEA chunk
- */
-struct dmub_cmd_edid_cea_ack {
- uint16_t offset; /**< offset of the chunk into the CEA block */
- uint8_t success; /**< 1 if this sending of chunk succeeded */
- uint8_t pad; /**< padding and for future expansion */
-};
-
-/**
- * Specify whether the result is an ACK/NACK or the parsing has finished
- */
-enum dmub_cmd_edid_cea_reply_type {
- DMUB_CMD__EDID_CEA_AMD_VSDB = 1, /**< VSDB parsing has finished */
- DMUB_CMD__EDID_CEA_ACK = 2, /**< acknowledges the CEA sending is OK or failing */
-};
-
-/**
- * Definition of a DMUB_CMD__EDID_CEA command.
- */
-struct dmub_rb_cmd_edid_cea {
- struct dmub_cmd_header header; /**< Command header */
- union dmub_cmd_edid_cea_data {
- struct dmub_cmd_send_edid_cea input; /**< input to send CEA chunks */
- struct dmub_cmd_edid_cea_output { /**< output with results */
- uint8_t type; /**< dmub_cmd_edid_cea_reply_type */
- union {
- struct dmub_cmd_edid_cea_amd_vsdb amd_vsdb;
- struct dmub_cmd_edid_cea_ack ack;
- };
- } output; /**< output to retrieve ACK/NACK or VSDB parsing results */
- } data; /**< Command data */
-
-};
-
/**
* struct dmub_cmd_cable_id_input - Defines the input of DMUB_CMD_GET_USBC_CABLE_ID command.
*/
@@ -7588,10 +7521,6 @@ union dmub_rb_cmd {
* Definition of a DMUB_CMD__DPIA_SET_TPS_NOTIFICATION command.
*/
struct dmub_rb_cmd_set_tps_notification set_tps_notification;
- /**
- * Definition of a DMUB_CMD__EDID_CEA command.
- */
- struct dmub_rb_cmd_edid_cea edid_cea;
/**
* Definition of a DMUB_CMD_GET_USBC_CABLE_ID command.
*/
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] drm/edid: Parse AMD VSDB for FreeSync refresh range
2026-07-21 16:44 ` [PATCH 1/4] drm/edid: Parse AMD VSDB for FreeSync refresh range Alex Huang
@ 2026-07-21 16:54 ` sashiko-bot
2026-07-21 17:00 ` Mario Limonciello
1 sibling, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-07-21 16:54 UTC (permalink / raw)
To: Alex Huang; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing payload length validation for AMD VSDB versions 2 and 3 leads to out-of-bounds memory reads.
--
--- Patch [1]: [PATCH 1/4] drm/edid: Parse AMD VSDB for FreeSync refresh range ---
commit 494775781ee504bb22668b33e2cf4cf7e240bfaa
Author: Alex Huang <Alex.Huang2@amd.com>
drm/edid: Parse AMD VSDB for FreeSync refresh range
This commit restructures the AMD VSDB parsing to support FreeSync refresh
ranges. It introduces structures for v1 and v2 payloads and relaxes the
overall minimum length check to accommodate v1 blocks.
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index df3c25bac761d..c9452066b9bee 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
[ ... ]
> @@ -5231,7 +5269,7 @@ static bool cea_db_is_microsoft_vsdb(const struct cea_db *db)
> 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_V1_PAYLOAD_LEN &&
> cea_db_payload_len(db) <= AMD_VSDB_V3_PAYLOAD_MAX_LEN;
> }
[ ... ]
> @@ -6431,43 +6469,81 @@ static void drm_parse_microsoft_vsdb(struct drm_connector *connector,
[ ... ]
> static void drm_parse_amd_vsdb(struct drm_connector *connector,
> const struct cea_db *db)
> {
[ ... ]
> + drm_parse_amd_vsdb_v1(info, data);
> +
> + if (common->version >= 2)
> + drm_parse_amd_vsdb_v2(info, data);
> +
> + if (common->version >= 3)
> + drm_parse_amd_vsdb_v3(info, data, cea_db_payload_len(db));
[Severity: High]
Can this lead to an out-of-bounds read if a malformed EDID provides a short
payload but advertises a newer version?
In cea_db_is_amd_vsdb(), the minimum payload length check is relaxed to
AMD_VSDB_V1_PAYLOAD_LEN (8 bytes). However, in drm_parse_amd_vsdb(), if
a block claims to be version 3 but only provides 8 bytes of data, it will
pass the earlier check but still execute drm_parse_amd_vsdb_v2() and
drm_parse_amd_vsdb_v3() here.
Since the v2 and v3 parsing functions expect up to 13 and 15 bytes
respectively, reading these structs from an 8-byte payload will read past
the end of the allocated buffer. Should we verify that cea_db_payload_len(db)
is large enough for the specific version being parsed before calling the v2
and v3 parsing functions?
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721164430.2121326-1-Alex.Huang2@amd.com?part=1
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 3/4] drm/amd/display: Clean up FreeSync capability detection
2026-07-21 16:44 ` [PATCH 3/4] drm/amd/display: Clean up FreeSync capability detection Alex Huang
@ 2026-07-21 16:55 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-07-21 16:55 UTC (permalink / raw)
To: Alex Huang; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Missing fallback to EDID monitor range when an AMD VSDB is present but does not advertise FreeSync support.
- [Low] Using integer literal 0 to initialize a boolean variable.
--
--- Patch [3]: [PATCH 3/4] drm/amd/display: Clean up FreeSync capability detection ---
commit f8ae668c998c7d60648194bc1a8a582d9e952ad8
Author: Alex Huang <Alex.Huang2@amd.com>
drm/amd/display: Clean up FreeSync capability detection
This commit extracts duplicate FreeSync capability detection code into
common code, pulling VSDB data once and falling back to monitor ranges
when needed.
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -13707,7 +13707,7 @@ static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
> void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
> const struct drm_edid *drm_edid, bool do_mccs)
> {
> - int i = 0;
> + bool has_vsdb = 0;
[Severity: Low]
This isn't a bug, but is it better to initialize this boolean variable
with 'false' instead of '0'?
[ ... ]
> + if (has_vsdb) {
> + amdgpu_dm_connector->vsdb_info = vsdb_info;
> + amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version;
> +
> + /* copy refresh rate info as long as VSDB advertises FreeSync */
> + if (vsdb_info.freesync_supported) {
> + amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
> + amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
> +
> + connector->display_info.monitor_range.min_vfreq =
> + vsdb_info.min_refresh_rate_hz;
> + connector->display_info.monitor_range.max_vfreq =
> + vsdb_info.max_refresh_rate_hz;
> }
> + } else {
> + /* fall back to the base EDID range when there is no VSDB */
> + amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq;
> + amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq;
> + }
[Severity: High]
What happens if a monitor has an AMD VSDB (has_vsdb is true) but does not
advertise FreeSync support (vsdb_info.freesync_supported is false)?
In this scenario, it looks like the code skips assigning the VSDB refresh
rates but also skips the else block that falls back to the EDID monitor
range. Will amdgpu_dm_connector->min_vfreq and max_vfreq be left
unmodified, potentially retaining stale values from a previously connected
monitor?
[ ... ]
> + if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
> + sink->sink_signal == SIGNAL_TYPE_EDP) {
> + if (amdgpu_dm_connector->dc_link &&
> + amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param &&
> + amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
> + freesync_capable = true;
[Severity: High]
If min_vfreq and max_vfreq retain stale values, could this arithmetic check
evaluate to true and erroneously enable FreeSync?
For example, when hotplugging an eDP panel that has a VSDB for Replay mode
but no FreeSync, it seems freesync_capable might be incorrectly set to true
based on the previous display's refresh rates.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721164430.2121326-1-Alex.Huang2@amd.com?part=3
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 4/4] drm/amd/display: Remove unused DMCU/DMUB EDID CEA parser
2026-07-21 16:44 ` [PATCH 4/4] drm/amd/display: Remove unused DMCU/DMUB EDID CEA parser Alex Huang
@ 2026-07-21 16:59 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-07-21 16:59 UTC (permalink / raw)
To: Alex Huang; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Parsing AMD VSDB blocks of version 2 or 3 without verifying the payload length leads to out-of-bounds memory reads on malformed EDIDs.
--
--- Patch [4]: [PATCH 4/4] drm/amd/display: Remove unused DMCU/DMUB EDID CEA parser ---
commit f5b939679f4dd7676bcd444c9eae98b5a45015a4
Author: Alex Huang <Alex.Huang2@amd.com>
drm/amd/display: Remove unused DMCU/DMUB EDID CEA parser
With HDMI FreeSync detection now sourced from the common DRM EDID parser,
the DMUB parsing for AMD VSDB is no longer used. This commit removes all
relevant fields and files for this flow.
> With HDMI FreeSync detection now sourced from the common DRM EDID
> parser, the DMUB parsing for AMD VSDB is no longer used.
[Severity: High]
Does the common DRM EDID parser validate the payload length before parsing?
Looking at drm_parse_amd_vsdb() in drivers/gpu/drm/drm_edid.c, the version
checks lack corresponding payload length validation before calling the v2
and v3 parsing functions:
drivers/gpu/drm/drm_edid.c:drm_parse_amd_vsdb() {
if (common->version >= 2)
drm_parse_amd_vsdb_v2(info, data);
if (common->version >= 3)
drm_parse_amd_vsdb_v3(info, data, cea_db_payload_len(db));
}
If a malformed EDID presents a block with payload_len = 8 and version = 3,
drm_parse_amd_vsdb_v2() unconditionally accesses fields up to offset 12
(p->lum2_min):
drivers/gpu/drm/drm_edid.c:drm_parse_amd_vsdb_v2() {
...
info->amd_vsdb.luminance_range2.min_luminance = p->lum2_min;
}
Similarly, drm_parse_amd_vsdb_v3() unconditionally accesses fields up to
offset 14 (p->max_vfreq_ext_high):
drivers/gpu/drm/drm_edid.c:drm_parse_amd_vsdb_v3() {
...
max_frame_rate = p->max_vfreq_ext_low |
(p->max_vfreq_ext_high & 0x3) << 8;
...
}
Can this lead to out-of-bounds memory reads on malformed EDIDs? Since the
block could be as short as 8 bytes, these accesses might read past the
allocated buffer into adjacent heap memory.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721164430.2121326-1-Alex.Huang2@amd.com?part=4
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] drm/edid: Parse AMD VSDB for FreeSync refresh range
2026-07-21 16:44 ` [PATCH 1/4] drm/edid: Parse AMD VSDB for FreeSync refresh range Alex Huang
2026-07-21 16:54 ` sashiko-bot
@ 2026-07-21 17:00 ` Mario Limonciello
1 sibling, 0 replies; 10+ messages in thread
From: Mario Limonciello @ 2026-07-21 17:00 UTC (permalink / raw)
To: Alex Huang, maarten.lankhorst, mripard, tzimmermann, airlied,
simona, harry.wentland, sunpeng.li, siqueira, alexander.deucher,
christian.koenig, alex.hung
Cc: dri-devel, amd-gfx, linux-kernel
On 7/21/26 11:44, Alex Huang wrote:
> Commit 118362a96286 ("drm/edid: Parse AMD Vendor-Specific Data Block")
> added parsing of the AMD VSDB, but only for version 3 and only for the
> replay/panel-type/luminance fields. Monitors that only advertise
> FreeSync through a v1 or v2 AMD VSDB therefore get no variable refresh
> rate range from the common EDID parser.
>
> Restructure drm_parse_amd_vsdb() and implement parsing for the FreeSync
> refresh range fields provided in the AMD VSDB.
>
> Signed-off-by: Alex Huang <Alex.Huang2@amd.com>
> ---
> drivers/gpu/drm/drm_edid.c | 134 ++++++++++++++++++++++++++++--------
> include/drm/drm_connector.h | 22 +++++-
> 2 files changed, 126 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index df3c25bac761..c9452066b9be 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -101,20 +101,58 @@ enum drm_edid_internal_quirk {
> #define MICROSOFT_IEEE_OUI 0xca125c
> #define AMD_IEEE_OUI 0x00001A
>
> +#define AMD_VSDB_V1_PAYLOAD_LEN 8
> +#define AMD_VSDB_V2_PAYLOAD_LEN 13
> #define AMD_VSDB_V3_PAYLOAD_MIN_LEN 15
> -#define AMD_VSDB_V3_PAYLOAD_MAX_LEN 20
> +#define AMD_VSDB_V3_PAYLOAD_MAX_LEN 21
> +
> +#define AMD_VSDB_V3_MAX_VFREQ_EXT2_LEN 21
> +
> +struct amd_vsdb_common_payload {
> + u8 oui[3];
> + u8 version;
> +} __packed;
> +
> +struct amd_vsdb_v1_payload {
> + u8 oui[3];
> + u8 version;
Since this is just the common header would it be helpful to have here:
struct amd_vsdb_common_payload common;
> + u8 feature_caps;
> + u8 min_vfreq;
> + u8 max_vfreq;
> + u8 freesync_vcp_code;
> +} __packed;
> +
> +struct amd_vsdb_v2_payload {
> + u8 oui[3];
> + u8 version;
> + u8 feature_caps;
> + u8 min_vfreq;
> + u8 max_vfreq;
> + u8 freesync_vcp_code;
Similarly would it be helpful to have here:
struct amd_vsdb_common_payload v1;
> + u8 cs_eotf_support;
> + u8 lum1_max;
> + u8 lum1_min;
> + u8 lum2_max;
> + u8 lum2_min;
> +} __packed;
>
> struct amd_vsdb_v3_payload {
> u8 oui[3];
> u8 version;
> u8 feature_caps;
> - u8 rsvd0[3];
> + u8 min_vfreq;
> + u8 max_vfreq;
> + u8 freesync_vcp_code;
> u8 cs_eotf_support;
> u8 lum1_max;
> u8 lum1_min;
> u8 lum2_max;
> u8 lum2_min;
And then
struct amd_vsdb_common_payload v2;
> - u8 rsvd1[2];
> + u8 max_vfreq_ext_low;
> + /*
> + * byte 14: bits 0-1 extend MSB of max refresh rate
> + */
> + u8 max_vfreq_ext_high;
> /*
> * Bytes beyond AMD_VSDB_V3_PAYLOAD_MIN_LEN are optional; a
> * monitor may provide a payload as short as 15 bytes. Always
> @@ -5231,7 +5269,7 @@ static bool cea_db_is_microsoft_vsdb(const struct cea_db *db)
> 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_V1_PAYLOAD_LEN &&
> cea_db_payload_len(db) <= AMD_VSDB_V3_PAYLOAD_MAX_LEN;
> }
>
> @@ -6431,43 +6469,81 @@ static void drm_parse_microsoft_vsdb(struct drm_connector *connector,
> connector->base.id, connector->name, version, db[5]);
> }
>
> +static void drm_parse_amd_vsdb_v1(struct drm_display_info *info,
> + const u8 *data)
> +{
> + const struct amd_vsdb_v1_payload *p =
> + (const struct amd_vsdb_v1_payload *)data;
> +
> + info->amd_vsdb.freesync_supported = p->feature_caps & 0x1;
> + info->amd_vsdb.min_frame_rate = p->min_vfreq;
> + info->amd_vsdb.max_frame_rate = p->max_vfreq;
> + info->amd_vsdb.freesync_vcp_code = p->freesync_vcp_code;
> +}
> +
> +static void drm_parse_amd_vsdb_v2(struct drm_display_info *info,
> + const u8 *data)
> +{
> + const struct amd_vsdb_v2_payload *p =
> + (const struct amd_vsdb_v2_payload *)data;
> +
> + 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;
> +}
> +
> +static void drm_parse_amd_vsdb_v3(struct drm_display_info *info,
> + const u8 *data, int payload_len)
> +{
> + const struct amd_vsdb_v3_payload *p =
> + (const struct amd_vsdb_v3_payload *)data;
> + u16 max_frame_rate;
> +
> + info->amd_vsdb.replay_mode = p->feature_caps & 0x40;
> + info->amd_vsdb.panel_type = (p->cs_eotf_support & 0xC0) >> 6;
> +
> + /* vfreq is provded in a different set of fields for v3. */
> + max_frame_rate = p->max_vfreq_ext_low |
> + (p->max_vfreq_ext_high & 0x3) << 8;
> +
> + /*
> + * The AMD VSDB v3 payload length is variable (15..21 bytes).
> + * All fields through max_vfreq_ext_high (byte 14) are always
> + * present, but p->extra[] (bytes 15+) may not be, so any access
> + * to extra[] must be guarded with a runtime length check to
> + * avoid out-of-bounds reads on shorter (but spec-valid) payloads.
> + */
> + if (payload_len >= AMD_VSDB_V3_MAX_VFREQ_EXT2_LEN)
> + max_frame_rate |= (p->extra[5] & 0x3) << 10;
> +
> + info->amd_vsdb.max_frame_rate = max_frame_rate;
> +}
> +
If you follow my suggestions you might be able to do a ladder where
drm_parse_amd_vsdb_v3() calls drm_parse_amd_vsdb_v2() with those pieces
and that calls drm_parse_amd_vsdb_v1() and that calls
drm_parse_amd_vsdb_common() or similar.
> 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;
> + const struct amd_vsdb_common_payload *common =
> + (const struct amd_vsdb_common_payload *)data;
>
> - if (p->version != 0x03) {
> + if (common->version < 1 || common->version > 3) {
> drm_dbg_kms(connector->dev,
> "[CONNECTOR:%d:%s] Unsupported AMD VSDB version %u\n",
> - connector->base.id, connector->name, p->version);
> + connector->base.id, connector->name, common->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;
> + info->amd_vsdb.version = common->version;
>
> - /*
> - * 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];
> - */
> + drm_parse_amd_vsdb_v1(info, data);
> +
> + if (common->version >= 2)
> + drm_parse_amd_vsdb_v2(info, data);
> +
> + if (common->version >= 3)
> + drm_parse_amd_vsdb_v3(info, data, cea_db_payload_len(db));
> }
>
> static void drm_parse_cea_ext(struct drm_connector *connector,
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 5f5ca023cd65..0914a2bd8ef1 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -697,7 +697,7 @@ enum drm_bus_flags {
> * struct drm_amd_vsdb_info - AMD-specific VSDB information
> *
> * This structure holds information parsed from the AMD Vendor-Specific Data
> - * Block (VSDB) version 3.
> + * Block (VSDB) versions 1 to 3.
> */
> struct drm_amd_vsdb_info {
> /**
> @@ -705,6 +705,26 @@ struct drm_amd_vsdb_info {
> */
> u8 version;
>
> + /**
> + * @freesync_supported: FreeSync (variable refresh rate) supported by panel
> + */
> + bool freesync_supported;
> +
> + /**
> + * @min_frame_rate: FreeSync minimum refresh rate in Hz
> + */
> + u16 min_frame_rate;
> +
> + /**
> + * @max_frame_rate: FreeSync maximum refresh rate in Hz
> + */
> + u16 max_frame_rate;
> +
> + /**
> + * @freesync_vcp_code: MCCS VCP code for FreeSync
> + */
> + u8 freesync_vcp_code;
> +
> /**
> * @replay_mode: Panel Replay supported
> */
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/4] drm/amd/display: Use HDMI FreeSync range from common EDID parser
2026-07-21 16:44 ` [PATCH 2/4] drm/amd/display: Use HDMI FreeSync range from common EDID parser Alex Huang
@ 2026-07-21 17:03 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-07-21 17:03 UTC (permalink / raw)
To: Alex Huang; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing payload length checks in AMD VSDB version 2 and 3 parsing leads to out-of-bounds memory reads when processing malformed EDIDs.
--
--- Patch [2]: [PATCH 2/4] drm/amd/display: Use HDMI FreeSync range from common EDID parser ---
commit 1c50a992def070041d1c59487f692155608cc345
Author: Alex Huang <Alex.Huang2@amd.com>
Subject: drm/amd/display: Use HDMI FreeSync range from common EDID parser
This commit modifies the AMD GPU display driver to use the common DRM EDID
parser for detecting HDMI FreeSync capabilities, rather than relying on the
AMD VSDB parser in the DMUB firmware.
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> index 9c564cd5edeea..fd9bb205c8f08 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
[ ... ]
> @@ -13932,8 +13770,8 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
> }
>
> } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
> - i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
> - if (i >= 0) {
> + i = get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
> + if (i) {
[Severity: High]
Does relying on the common DRM EDID parser here expose us to out-of-bounds
memory reads when processing malformed EDIDs?
Looking at the common parser in drivers/gpu/drm/drm_edid.c,
drm_parse_amd_vsdb() fails to validate the payload length before delegating
to version-specific parsers:
drivers/gpu/drm/drm_edid.c:drm_parse_amd_vsdb() {
...
info->amd_vsdb.version = common->version;
drm_parse_amd_vsdb_v1(info, data);
if (common->version >= 2)
drm_parse_amd_vsdb_v2(info, data);
if (common->version >= 3)
drm_parse_amd_vsdb_v3(info, data, cea_db_payload_len(db));
}
The v2 parser unconditionally accesses struct fields up to offset 12 without
checking if the payload length is sufficient:
drivers/gpu/drm/drm_edid.c:drm_parse_amd_vsdb_v2() {
...
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;
...
}
Similarly, the v3 parser unconditionally accesses fields up to offset 14:
drivers/gpu/drm/drm_edid.c:drm_parse_amd_vsdb_v3() {
...
/* vfreq is provded in a different set of fields for v3. */
max_frame_rate = p->max_vfreq_ext_low |
(p->max_vfreq_ext_high & 0x3) << 8;
...
}
If a monitor provides an AMD VSDB block with version 2 or 3 but a payload
length smaller than required, could this lead to an out-of-bounds read of
up to 7 bytes in the slab?
> amdgpu_dm_connector->vsdb_info = vsdb_info;
> sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721164430.2121326-1-Alex.Huang2@amd.com?part=2
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-07-21 17:03 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-21 16:44 [PATCH 0/4] drm/edid: Parse AMD VSDB FreeSync range in common code Alex Huang
2026-07-21 16:44 ` [PATCH 1/4] drm/edid: Parse AMD VSDB for FreeSync refresh range Alex Huang
2026-07-21 16:54 ` sashiko-bot
2026-07-21 17:00 ` Mario Limonciello
2026-07-21 16:44 ` [PATCH 2/4] drm/amd/display: Use HDMI FreeSync range from common EDID parser Alex Huang
2026-07-21 17:03 ` sashiko-bot
2026-07-21 16:44 ` [PATCH 3/4] drm/amd/display: Clean up FreeSync capability detection Alex Huang
2026-07-21 16:55 ` sashiko-bot
2026-07-21 16:44 ` [PATCH 4/4] drm/amd/display: Remove unused DMCU/DMUB EDID CEA parser Alex Huang
2026-07-21 16:59 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.