[PATCH 1/2] drm/edid: read the luminance range from DisplayID 2.0

Cristian La Spina <[email protected]> Sun, 2 Aug 2026 19:06:46 +0200
Newsgroups org.freedesktop.lists.intel-gfx,org.freedesktop.lists.dri-devel,org.freedesktop.lists.intel-xe,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Panels carrying CTA HDR static metadata get their luminance range
computed into display_info.luminance_range by
drm_calculate_luminance_range(). Panels publishing the same information
through the DisplayID 2.0 Display Parameters Data Block get nothing:
drm_displayid_parse_display_params() already reads that block, but only
for the device technology.

Such panels exist. The OLED panel in the Lenovo Yoga 9 2-in-1 14IPH11
(EDO EE00QBA63.E) carries two DisplayID 2.0 extension blocks and no CTA
extension at all, and declares:

  Native Maximum Luminance (Full Coverage): 500.000 cd/m^2
  Native Minimum Luminance:                   4.000 cd/m^2

Fill in the luminance range from those fields when the EDID has not
already provided one, so drivers doing nits based backlight control have
a real range to work with instead of a hardcoded guess. CTA HDR static
metadata is parsed first and keeps precedence, so panels providing both
go on behaving exactly as before.

DisplayID 2.0 encodes luminance as IEEE 754 binary16, an encoding the
core has not needed until now; add a small decoder for it.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Cristian La Spina <[email protected]>
---
 drivers/gpu/drm/drm_edid.c  | 47 +++++++++++++++++++++++++++++++++++++
 include/drm/drm_connector.h |  3 ++-
 2 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 07970e5b5..f4845d799 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -6733,6 +6733,31 @@ static void drm_displayid_process_base_section_header(struct drm_connector *conn
 		info->non_desktop = true;
 }
 
+/*
+ * Convert an IEEE 754 binary16 value, the encoding DisplayID 2.0 uses for
+ * luminance, to nits rounded to nearest. Negative, zero, subnormal, infinite
+ * and NaN all report 0, meaning "no usable value".
+ */
+static u32 displayid_binary16_to_nits(u16 val)
+{
+	int exp = (val >> 10) & 0x1f;
+	u32 mant = (val & 0x3ff) | 0x400;
+	int shift;
+
+	if (val & 0x8000 || exp == 0 || exp == 0x1f)
+		return 0;
+
+	shift = exp - 25;
+	if (shift >= 0)
+		return shift > 15 ? 0 : mant << shift;
+
+	shift = -shift;
+	if (shift > 11)
+		return 0;
+
+	return (mant + (1 << (shift - 1))) >> shift;
+}
+
 static void
 drm_displayid_parse_display_params(struct drm_connector *connector,
 				   const struct displayid_block *block)
@@ -6740,6 +6765,7 @@ drm_displayid_parse_display_params(struct drm_connector *connector,
 	struct drm_display_info *info = &connector->display_info;
 	const struct displayid_display_params_block *params =
 		(const struct displayid_display_params_block *)block;
+	u32 max_luminance, min_luminance;
 	u8 tech;
 
 	if (block->num_bytes < sizeof(*params) - sizeof(params->base)) {
@@ -6770,6 +6796,27 @@ drm_displayid_parse_display_params(struct drm_connector *connector,
 	default:
 		break;
 	}
+
+	/*
+	 * CTA HDR static metadata is parsed before this and takes precedence,
+	 * so panels providing both keep the range they already have.
+	 */
+	if (info->luminance_range.max_luminance)
+		return;
+
+	max_luminance = displayid_binary16_to_nits(le16_to_cpu(params->max_luminance_full));
+	min_luminance = displayid_binary16_to_nits(le16_to_cpu(params->min_luminance));
+
+	if (!max_luminance || min_luminance >= max_luminance)
+		return;
+
+	info->luminance_range.max_luminance = max_luminance;
+	info->luminance_range.min_luminance = min_luminance;
+
+	drm_dbg_kms(connector->dev,
+		    "[CONNECTOR:%d:%s] DisplayID luminance range %u-%u nits\n",
+		    connector->base.id, connector->name,
+		    min_luminance, max_luminance);
 }
 
 static void update_displayid_info(struct drm_connector *connector,
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index a0cf0268d..15176ae63 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -425,7 +425,8 @@ struct drm_monitor_range_info {
  * &drm_display_info. Calculated using data in EDID
  *
  * This struct is used to store a luminance range supported by panel
- * as calculated using data from EDID's static hdr metadata.
+ * as calculated using data from EDID's static hdr metadata, or as read
+ * from the DisplayID 2.0 Display Parameters Data Block.
  *
  * @min_luminance: This is the min supported luminance value
  *
-- 
2.52.0