* [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage
@ 2026-11-14 18:03 Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 01/25] video/hdmi: Copy SPD vendor/product with strtomem_pad() Cristian Ciocaltea
` (24 more replies)
0 siblings, 25 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel, stable,
Diederik de Haas, Robin Räber, Igor Paunovic
This is the remaining part of "[PATCH v11 00/74] Add HDMI 2.0 support to
DW HDMI QP TX" [1], to clean up the DRM HDMI connector infrastructure
and extend the KUnit coverage of the HDMI connector, bridge and
probe-helper code.
Patch series structure
======================
Patches 1-2 fix the SPD InfoFrame vendor/product copies by using
strtomem_pad(), so that non-NUL-terminated data is no longer read past
its end, and define the related field lengths.
Patches 3-4 drop drm_atomic_helper_connector_hdmi_force(), which has no
users left now that drivers use .force_ctx(), and make the state helper
reject connectors that lack HDMI funcs instead of dereferencing them.
Patches 5-7 convert the connector and HDMI tests to
drmm_connector_hdmi_init() and remove the transitional
drmm_connector_hdmi_ini2().
Patches 8-12 drop the redundant HDMI vendor/product, supported_formats
and max_bpc fields from the connector, use the SPD InfoFrame field
length defines, and fix a comment in drmm_connector_hdmi_init().
Patches 13-15 fix the conformity of two existing EDID test blobs and add
a 4K@60Hz EDID with a 600MHz TMDS limit.
Patches 16-22 add connector and HDMI state helper tests:
max_tmds_char_rate initialization and limits, the source-side scrambling
decision, SPD InfoFrame vendor/product generation, HDMI 2.0 scrambling
helpers and SCDC resync on hotplug.
Patches 23-24 set the supported HDMI version in the bridge tests and add
HDMI 2.0 bridge connector coverage.
Patch 25 adds .force_ctx() connector callback coverage to the probe
helper tests.
Changes since [1]
=================
- Added new patch 'drm/display: hdmi-state-helper: Reject connectors
lacking HDMI funcs'
- Split 'video/hdmi: Define SPD InfoFrame field lengths and use
strtomem_pad()' into
* Copy SPD vendor/product with strtomem_pad()
* Define SPD InfoFrame vendor/product field lengths
- Fixed comment block in drmm_connector_hdmi_init()
- Dropped the now redundant !connector->hdmi.funcs check in
'drm/connector: Drop redundant hdmi supported_formats field'
- Updated some of the existing test cases
* connector: test NULL hdmi_funcs rejection, max_tmds_char_rate
inference for all HDMI versions, HDMI 2.0 rate capping, and
scrambler callbacks being ignored below HDMI 2.0
* hdmi_state_helper: verify the scrambling decision accounts for
output format and bpc, and gets cleared when switching to a low TMDS
rate mode
* SPD InfoFrame: also check the packed buffer handed over to
.write_infoframe(), and drop the redundant atomic check that
triggered drm_WARN_ON(state->checked)
- New patch adding HDMI 2.0 scrambling helpers coverage, using a fake
SCDC sink on the DDC bus: drm_hdmi_mode_needs_scrambling(),
drm_connector_hdmi_{enable,disable}_scrambling() including error paths
and rollback, and the SCDC status monitoring work
- New patch adding SCDC resync on hotplug coverage, i.e.
drm_connector_hdmi_sync_scdc() via the hotplug and .force_ctx()
helpers
- New patch adding probe helper .force_ctx() coverage, including the
-EDEADLK backoff and retry path
- New patch adding HDMI 2.0 bridge connector coverage: scrambler
callback forwarding, missing ops rejection, and version and max TMDS
character rate propagation
KUnit runtime dependency
========================
Please note that a few unit tests are expected to fail until the
following commits from drm-misc-fixes land in drm-misc-next:
- 59d532e39ea7 ("drm/display: hdmi: Use committed state to decide on SCDC resync")
- b4146c6155b1 ("drm/display: hdmi: Reset sink scrambling on non-scrambled modesets")
[1] https://lore.kernel.org/all/20260901-dw-hdmi-qp-scramb-v11-0-bc12954a0688@collabora.com/
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
Cristian Ciocaltea (25):
video/hdmi: Copy SPD vendor/product with strtomem_pad()
video/hdmi: Define SPD InfoFrame vendor/product field lengths
drm/display: hdmi-state-helper: Drop drm_atomic_helper_connector_hdmi_force()
drm/display: hdmi-state-helper: Reject connectors lacking HDMI funcs
drm/tests: connector: Convert to drmm_connector_hdmi_init()
drm/tests: hdmi: Convert to drmm_connector_hdmi_init()
drm/connector: Remove drmm_connector_hdmi_ini2()
drm/connector: Drop redundant hdmi vendor/product fields
drm/connector: Drop redundant hdmi supported_formats field
drm/connector: Drop redundant max_bpc field
drm/connector: Use the SPD InfoFrame field length defines
drm/connector: Fix comment in drmm_connector_hdmi_init()
drm/tests: edid: Fix conformity for 1080p+4K YUV420 200MHz EDID
drm/tests: edid: Fix conformity for 4K RGB/YUV 340MHz EDID
drm/tests: edid: Add 4K@60Hz EDID with 600MHz TMDS
drm/tests: connector: Add HDMI max_tmds_char_rate init coverage
drm/tests: connector: Add HDMI source-side scrambler coverage
drm/tests: hdmi: Cover mode validation against connector max_tmds_char_rate
drm/tests: hdmi: Verify scrambler_needed computation
drm/tests: hdmi: Add SPD InfoFrame vendor/product coverage
drm/tests: hdmi: Add HDMI 2.0 scrambling helpers coverage
drm/tests: hdmi: Verify SCDC resync on hotplug
drm/tests: bridge: Set supported HDMI version
drm/tests: bridge: Add HDMI 2.0 bridge connector coverage
drm/tests: probe-helper: Add .force_ctx() callback coverage
drivers/gpu/drm/display/drm_hdmi_state_helper.c | 30 +-
drivers/gpu/drm/drm_connector.c | 123 +-
drivers/gpu/drm/tests/drm_bridge_test.c | 185 +-
drivers/gpu/drm/tests/drm_connector_test.c | 643 +++++--
drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 2015 ++++++++++++++++++--
drivers/gpu/drm/tests/drm_kunit_edid.c | 251 ++-
drivers/gpu/drm/tests/drm_kunit_edid.h | 1 +
drivers/gpu/drm/tests/drm_probe_helper_test.c | 296 +++
drivers/video/hdmi.c | 31 +-
include/drm/display/drm_hdmi_state_helper.h | 1 -
include/drm/drm_connector.h | 38 +-
include/linux/hdmi.h | 7 +-
12 files changed, 3047 insertions(+), 574 deletions(-)
---
base-commit: fc72cd52fb284f3707fa36a68ea5a22543a51d71
change-id: 20261114-drm-conn-hdmi-cleanup-d9d94c81dceb
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH 01/25] video/hdmi: Copy SPD vendor/product with strtomem_pad()
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:15 ` sashiko-bot
2026-11-14 18:03 ` [PATCH 02/25] video/hdmi: Define SPD InfoFrame vendor/product field lengths Cristian Ciocaltea
` (23 subsequent siblings)
24 siblings, 1 reply; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel, stable
The HDMI specification defines the SPD InfoFrame Vendor Name and Product
Description as fixed-size fields, padded with zeros and left without any
trailing NUL when a name spans the whole field.
Mark the fields as non-strings, so that the copies can be handed over to
strtomem_pad(), which implements precisely the required semantics. This
also bounds the reads from the source strings, whereas the open-coded
strlen() could run past the end of the buffer in the
hdmi_spd_infoframe_unpack() path, where the names come straight from the
wire and are not NUL-terminated.
Fixes: 2c676f378edb ("[media] hdmi: added unpack and logging functions for InfoFrames")
Cc: stable@vger.kernel.org # v6.1+
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/video/hdmi.c | 11 +++++------
include/linux/hdmi.h | 4 ++--
2 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 45b42f14a750..cd18121afa20 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -217,23 +217,22 @@ EXPORT_SYMBOL(hdmi_avi_infoframe_pack);
* @vendor: vendor string
* @product: product string
*
+ * Both strings are copied into the fixed-size infoframe fields,
+ * truncated if too long and padded with zeros otherwise.
+ *
* Returns 0 on success or a negative error code on failure.
*/
int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame,
const char *vendor, const char *product)
{
- size_t len;
-
memset(frame, 0, sizeof(*frame));
frame->type = HDMI_INFOFRAME_TYPE_SPD;
frame->version = 1;
frame->length = HDMI_SPD_INFOFRAME_SIZE;
- len = strlen(vendor);
- memcpy(frame->vendor, vendor, min(len, sizeof(frame->vendor)));
- len = strlen(product);
- memcpy(frame->product, product, min(len, sizeof(frame->product)));
+ strtomem_pad(frame->vendor, vendor, 0);
+ strtomem_pad(frame->product, product, 0);
return 0;
}
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index b80a5ee63bb2..08922d5330d6 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -271,8 +271,8 @@ struct hdmi_spd_infoframe {
enum hdmi_infoframe_type type;
unsigned char version;
unsigned char length;
- char vendor[8];
- char product[16];
+ char vendor[8] __nonstring;
+ char product[16] __nonstring;
enum hdmi_spd_sdi sdi;
};
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 02/25] video/hdmi: Define SPD InfoFrame vendor/product field lengths
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 01/25] video/hdmi: Copy SPD vendor/product with strtomem_pad() Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 03/25] drm/display: hdmi-state-helper: Drop drm_atomic_helper_connector_hdmi_force() Cristian Ciocaltea
` (22 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
The HDMI specification defines the SPD InfoFrame Vendor Name and
Product Description as fixed-size fields of 8 and 16 bytes
respectively.
Add defines for those lengths and use them in place of the related
magic numbers, including the payload offsets and the log format
precisions. While at it, fix the continuation line alignment in
hdmi_spd_infoframe_log().
No functional change intended.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/video/hdmi.c | 20 +++++++++++++-------
include/linux/hdmi.h | 7 +++++--
2 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index cd18121afa20..7eeadac2de69 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -304,9 +304,11 @@ ssize_t hdmi_spd_infoframe_pack_only(const struct hdmi_spd_infoframe *frame,
ptr += HDMI_INFOFRAME_HEADER_SIZE;
memcpy(ptr, frame->vendor, sizeof(frame->vendor));
- memcpy(ptr + 8, frame->product, sizeof(frame->product));
+ memcpy(ptr + HDMI_SPD_INFOFRAME_VENDOR_LEN, frame->product,
+ sizeof(frame->product));
- ptr[24] = frame->sdi;
+ ptr[HDMI_SPD_INFOFRAME_VENDOR_LEN + HDMI_SPD_INFOFRAME_PRODUCT_LEN] =
+ frame->sdi;
hdmi_infoframe_set_checksum(buffer, length);
@@ -1284,10 +1286,12 @@ static void hdmi_spd_infoframe_log(const char *level,
hdmi_infoframe_log_header(level, dev,
(const struct hdmi_any_infoframe *)frame);
- hdmi_log(" vendor: %.8s\n", frame->vendor);
- hdmi_log(" product: %.16s\n", frame->product);
+ hdmi_log(" vendor: %.*s\n", HDMI_SPD_INFOFRAME_VENDOR_LEN,
+ frame->vendor);
+ hdmi_log(" product: %.*s\n", HDMI_SPD_INFOFRAME_PRODUCT_LEN,
+ frame->product);
hdmi_log(" source device information: %s (0x%x)\n",
- hdmi_spd_sdi_get_name(frame->sdi), frame->sdi);
+ hdmi_spd_sdi_get_name(frame->sdi), frame->sdi);
}
static const char *
@@ -1642,11 +1646,13 @@ static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame,
ptr += HDMI_INFOFRAME_HEADER_SIZE;
- ret = hdmi_spd_infoframe_init(frame, ptr, ptr + 8);
+ ret = hdmi_spd_infoframe_init(frame, ptr,
+ ptr + HDMI_SPD_INFOFRAME_VENDOR_LEN);
if (ret)
return ret;
- frame->sdi = ptr[24];
+ frame->sdi = ptr[HDMI_SPD_INFOFRAME_VENDOR_LEN +
+ HDMI_SPD_INFOFRAME_PRODUCT_LEN];
return 0;
}
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index 08922d5330d6..7d96821b486d 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -267,12 +267,15 @@ enum hdmi_spd_sdi {
HDMI_SPD_SDI_PMP,
};
+#define HDMI_SPD_INFOFRAME_VENDOR_LEN 8
+#define HDMI_SPD_INFOFRAME_PRODUCT_LEN 16
+
struct hdmi_spd_infoframe {
enum hdmi_infoframe_type type;
unsigned char version;
unsigned char length;
- char vendor[8] __nonstring;
- char product[16] __nonstring;
+ char vendor[HDMI_SPD_INFOFRAME_VENDOR_LEN] __nonstring;
+ char product[HDMI_SPD_INFOFRAME_PRODUCT_LEN] __nonstring;
enum hdmi_spd_sdi sdi;
};
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 03/25] drm/display: hdmi-state-helper: Drop drm_atomic_helper_connector_hdmi_force()
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 01/25] video/hdmi: Copy SPD vendor/product with strtomem_pad() Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 02/25] video/hdmi: Define SPD InfoFrame vendor/product field lengths Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 04/25] drm/display: hdmi-state-helper: Reject connectors lacking HDMI funcs Cristian Ciocaltea
` (21 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
With all callers converted to the .force_ctx() connector helper, this
function has no users left. Remove it.
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/display/drm_hdmi_state_helper.c | 15 ---------------
include/drm/display/drm_hdmi_state_helper.h | 1 -
2 files changed, 16 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
index 66ae678fc078..c50cedc7fa65 100644
--- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c
+++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
@@ -1440,21 +1440,6 @@ int drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_hotplug);
-/**
- * drm_atomic_helper_connector_hdmi_force - HDMI Connector implementation of the force callback
- * @connector: A pointer to the HDMI connector
- *
- * This function implements the .force() callback for the HDMI connectors. It
- * can either be used directly as the callback or should be called from within
- * the .force() callback implementation to maintain the HDMI-specific
- * connector's data.
- */
-void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector)
-{
- drm_atomic_helper_connector_hdmi_update(connector, NULL, connector->status);
-}
-EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_force);
-
/**
* drm_atomic_helper_connector_hdmi_force_ctx - HDMI Connector implementation
* of the force_ctx callback
diff --git a/include/drm/display/drm_hdmi_state_helper.h b/include/drm/display/drm_hdmi_state_helper.h
index 21e5a9215319..ddbfb5b80fa8 100644
--- a/include/drm/display/drm_hdmi_state_helper.h
+++ b/include/drm/display/drm_hdmi_state_helper.h
@@ -27,7 +27,6 @@ int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *con
int drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx,
enum drm_connector_status status);
-void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector);
int drm_atomic_helper_connector_hdmi_force_ctx(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx);
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 04/25] drm/display: hdmi-state-helper: Reject connectors lacking HDMI funcs
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (2 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 03/25] drm/display: hdmi-state-helper: Drop drm_atomic_helper_connector_hdmi_force() Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 05/25] drm/tests: connector: Convert to drmm_connector_hdmi_init() Cristian Ciocaltea
` (20 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
drm_atomic_helper_connector_hdmi_check() must only be used with
connectors set up via drmm_connector_hdmi_init(), which guarantees
connector->hdmi.funcs is set.
Other connectors used to fail gracefully: with max_bpc and
supported_formats both zero, hdmi_compute_config() found no usable
format/bpc combination and returned -EINVAL before
hdmi_generate_infoframes() dereferenced hdmi.funcs.
As those fields are being replaced by drm_connector_hdmi_funcs
counterparts, hdmi_compute_config() would dereference hdmi.funcs right
away. To preserve the old behaviour, return an error early for such
connectors, and use drm_WARN_ON_ONCE() to flag the driver bug.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/display/drm_hdmi_state_helper.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
index c50cedc7fa65..f533d5a7aab9 100644
--- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c
+++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
@@ -912,6 +912,9 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
if (!new_conn_state->crtc || !new_conn_state->best_encoder)
return 0;
+ if (drm_WARN_ON_ONCE(connector->dev, !connector->hdmi.funcs))
+ return -EINVAL;
+
ret = hdmi_compute_config(connector, new_conn_state, mode);
if (ret)
return ret;
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 05/25] drm/tests: connector: Convert to drmm_connector_hdmi_init()
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (3 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 04/25] drm/display: hdmi-state-helper: Reject connectors lacking HDMI funcs Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 06/25] drm/tests: hdmi: " Cristian Ciocaltea
` (19 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
Switch tests over to the new drmm_connector_hdmi_init() signature, where
the vendor, product, supported_formats and max_bpc values previously
passed as arguments are now provided through struct
drm_connector_hdmi_funcs.
As a consequence, the tests exercising those values can no longer share
the static dummy_hdmi_funcs and need a modified copy of their own.
Since the connector retains a pointer to it, such a copy must not be
placed on the stack, which is gone by the time the DRM-managed actions
release the connector. Keep it in struct drm_connector_init_priv
instead: it embeds the DRM device and is only freed via
managed.final_kfree, i.e. once drm_dev_release() has run all DRM-managed
actions.
Note that a kunit_kzalloc() buffer would not be suitable, as KUnit
releases its resources in reverse order, hence one allocated by a test
would be freed before the DRM device allocated by the suite .init().
Since struct drm_connector_hdmi_funcs is now mandatory, as it carries
the HDMI controller capabilities, also verify that registering an HDMI
connector without it fails.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_connector_test.c | 269 +++++++++++++++--------------
1 file changed, 137 insertions(+), 132 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index 2856294bafda..354e3088cf0f 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -23,6 +23,8 @@ struct drm_connector_init_priv {
struct drm_device drm;
struct drm_connector connector;
struct i2c_adapter ddc;
+ /* Mutable copy of a static instance for test-specific overrides */
+ struct drm_connector_hdmi_funcs hdmi_funcs;
};
static int accept_infoframe_clear_infoframe(struct drm_connector *connector)
@@ -37,6 +39,11 @@ static int accept_infoframe_write_infoframe(struct drm_connector *connector,
}
static const struct drm_connector_hdmi_funcs dummy_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_1_4,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
@@ -669,14 +676,11 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test)
struct drm_connector_init_priv *priv = test->priv;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", "Product",
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
}
@@ -689,17 +693,31 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test)
struct drm_connector_init_priv *priv = test->priv;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", "Product",
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- NULL,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ NULL);
KUNIT_EXPECT_EQ(test, ret, 0);
}
+/*
+ * Test that the registration of an HDMI connector without any HDMI
+ * functions and capabilities fails.
+ */
+static void drm_test_connector_hdmi_init_null_hdmi_funcs(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ NULL,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
/*
* Test that the registration of an HDMI connector with a NULL vendor
* fails.
@@ -709,14 +727,14 @@ static void drm_test_connector_hdmi_init_null_vendor(struct kunit *test)
struct drm_connector_init_priv *priv = test->priv;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- NULL, "Product",
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.vendor = NULL;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -729,14 +747,14 @@ static void drm_test_connector_hdmi_init_null_product(struct kunit *test)
struct drm_connector_init_priv *priv = test->priv;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", NULL,
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.product = NULL;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -750,19 +768,19 @@ static void drm_test_connector_hdmi_init_product_valid(struct kunit *test)
const unsigned char expected_product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] = {
'P', 'r', 'o', 'd',
};
- const char *product_name = "Prod";
int ret;
- KUNIT_ASSERT_LT(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.product = "Prod";
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", product_name,
+ KUNIT_ASSERT_LT(test, strlen(priv->hdmi_funcs.product),
+ DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_MEMEQ(test,
priv->connector.hdmi.product,
@@ -783,19 +801,19 @@ static void drm_test_connector_hdmi_init_product_length_exact(struct kunit *test
'P', 'r', 'o', 'd', 'u', 'c', 't',
'P', 'r',
};
- const char *product_name = "ProductProductPr";
int ret;
- KUNIT_ASSERT_EQ(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.product = "ProductProductPr";
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", product_name,
+ KUNIT_ASSERT_EQ(test, strlen(priv->hdmi_funcs.product),
+ DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_MEMEQ(test,
priv->connector.hdmi.product,
@@ -810,19 +828,19 @@ static void drm_test_connector_hdmi_init_product_length_exact(struct kunit *test
static void drm_test_connector_hdmi_init_product_length_too_long(struct kunit *test)
{
struct drm_connector_init_priv *priv = test->priv;
- const char *product_name = "ProductProductProduct";
int ret;
- KUNIT_ASSERT_GT(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.product = "ProductProductProduct";
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", product_name,
+ KUNIT_ASSERT_GT(test, strlen(priv->hdmi_funcs.product),
+ DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -836,19 +854,19 @@ static void drm_test_connector_hdmi_init_vendor_valid(struct kunit *test)
const char expected_vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] = {
'V', 'e', 'n', 'd',
};
- const char *vendor_name = "Vend";
int ret;
- KUNIT_ASSERT_LT(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN);
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.vendor = "Vend";
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- vendor_name, "Product",
+ KUNIT_ASSERT_LT(test, strlen(priv->hdmi_funcs.vendor),
+ DRM_CONNECTOR_HDMI_VENDOR_LEN);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_MEMEQ(test,
priv->connector.hdmi.vendor,
@@ -868,19 +886,19 @@ static void drm_test_connector_hdmi_init_vendor_length_exact(struct kunit *test)
'V', 'e', 'n', 'd', 'o', 'r',
'V', 'e',
};
- const char *vendor_name = "VendorVe";
int ret;
- KUNIT_ASSERT_EQ(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN);
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.vendor = "VendorVe";
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- vendor_name, "Product",
+ KUNIT_ASSERT_EQ(test, strlen(priv->hdmi_funcs.vendor),
+ DRM_CONNECTOR_HDMI_VENDOR_LEN);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_MEMEQ(test,
priv->connector.hdmi.vendor,
@@ -895,19 +913,17 @@ static void drm_test_connector_hdmi_init_vendor_length_exact(struct kunit *test)
static void drm_test_connector_hdmi_init_vendor_length_too_long(struct kunit *test)
{
struct drm_connector_init_priv *priv = test->priv;
- const char *vendor_name = "VendorVendor";
int ret;
- KUNIT_ASSERT_GT(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN);
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.vendor = "VendorVendor";
+ KUNIT_ASSERT_GT(test, strlen(priv->hdmi_funcs.vendor), DRM_CONNECTOR_HDMI_VENDOR_LEN);
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- vendor_name, "Product",
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -920,14 +936,14 @@ static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test)
struct drm_connector_init_priv *priv = test->priv;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", "Product",
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.max_bpc = 9;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 9);
+ &priv->ddc);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -940,14 +956,14 @@ static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test)
struct drm_connector_init_priv *priv = test->priv;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", "Product",
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.max_bpc = 0;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 0);
+ &priv->ddc);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -965,14 +981,11 @@ static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test)
uint64_t val;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, connector,
- "Vendor", "Product",
+ ret = drmm_connector_hdmi_init(&priv->drm, connector,
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
prop = connector->max_bpc_property;
@@ -1006,14 +1019,14 @@ static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test)
uint64_t val;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, connector,
- "Vendor", "Product",
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.max_bpc = 10;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 10);
+ &priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
prop = connector->max_bpc_property;
@@ -1047,14 +1060,14 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test)
uint64_t val;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, connector,
- "Vendor", "Product",
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.max_bpc = 12;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 12);
+ &priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
prop = connector->max_bpc_property;
@@ -1083,14 +1096,14 @@ static void drm_test_connector_hdmi_init_formats_empty(struct kunit *test)
struct drm_connector_init_priv *priv = test->priv;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", "Product",
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.supported_formats = 0;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- 0,
- 8);
+ &priv->ddc);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -1103,14 +1116,14 @@ static void drm_test_connector_hdmi_init_formats_no_rgb(struct kunit *test)
struct drm_connector_init_priv *priv = test->priv;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", "Product",
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422);
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -1161,14 +1174,14 @@ static void drm_test_connector_hdmi_init_formats_yuv420_allowed(struct kunit *te
params = test->param_value;
priv->connector.ycbcr_420_allowed = params->yuv420_allowed;
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", "Product",
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.supported_formats = params->supported_formats;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
- &dummy_hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- params->supported_formats,
- 8);
+ &priv->ddc);
KUNIT_EXPECT_EQ(test, ret, params->expected_result);
}
@@ -1182,14 +1195,11 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test)
unsigned int connector_type = *(unsigned int *)test->param_value;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", "Product",
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
&dummy_hdmi_funcs,
connector_type,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
}
@@ -1217,14 +1227,11 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test)
unsigned int connector_type = *(unsigned int *)test->param_value;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector,
- "Vendor", "Product",
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
&dummy_hdmi_funcs,
connector_type,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_LT(test, ret, 0);
}
@@ -1266,6 +1273,7 @@ static struct kunit_case drmm_connector_hdmi_init_tests[] = {
KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_formats_yuv420_allowed,
drm_connector_hdmi_init_formats_yuv420_allowed_gen_params),
KUNIT_CASE(drm_test_connector_hdmi_init_null_ddc),
+ KUNIT_CASE(drm_test_connector_hdmi_init_null_hdmi_funcs),
KUNIT_CASE(drm_test_connector_hdmi_init_null_product),
KUNIT_CASE(drm_test_connector_hdmi_init_null_vendor),
KUNIT_CASE(drm_test_connector_hdmi_init_product_length_exact),
@@ -1494,14 +1502,11 @@ static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector(
struct drm_property *prop;
int ret;
- ret = drmm_connector_hdmi_ini2(&priv->drm, connector,
- "Vendor", "Product",
+ ret = drmm_connector_hdmi_init(&priv->drm, connector,
&dummy_funcs,
&dummy_hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- &priv->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ &priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
ret = drm_connector_attach_broadcast_rgb_property(connector);
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 06/25] drm/tests: hdmi: Convert to drmm_connector_hdmi_init()
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (4 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 05/25] drm/tests: connector: Convert to drmm_connector_hdmi_init() Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:14 ` sashiko-bot
2026-11-14 18:03 ` [PATCH 07/25] drm/connector: Remove drmm_connector_hdmi_ini2() Cristian Ciocaltea
` (18 subsequent siblings)
24 siblings, 1 reply; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
Switch tests over to the new drmm_connector_hdmi_init() signature, where
the vendor, product, supported_formats and max_bpc values previously
passed as arguments are now provided through struct
drm_connector_hdmi_funcs.
As a consequence, the tests exercising those values can no longer share
the static hdmi_funcs and need a modified copy of their own. Since the
connector retains a pointer to it, such a copy must not live on the
caller's stack, which is gone by the time the DRM-managed actions
release the connector.
Let __connector_hdmi_init() store it in struct
drm_atomic_helper_connector_hdmi_priv instead. This structure embeds
the DRM device and is freed through managed.final_kfree only after
drm_dev_release() has run all DRM-managed actions, so it strictly
outlives the connector.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 334 ++++++++++-----------
1 file changed, 164 insertions(+), 170 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
index 336bb644cb7c..f1bdd197429c 100644
--- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
@@ -32,6 +32,7 @@ struct drm_atomic_helper_connector_hdmi_priv {
struct drm_crtc *crtc;
struct drm_encoder encoder;
struct drm_connector connector;
+ struct drm_connector_hdmi_funcs hdmi_funcs;
const void *current_edid;
size_t current_edid_len;
@@ -124,6 +125,11 @@ static int accept_infoframe_write_infoframe(struct drm_connector *connector,
}
static const struct drm_connector_hdmi_funcs dummy_connector_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_1_4,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
@@ -143,6 +149,11 @@ reject_connector_tmds_char_rate_valid(const struct drm_connector *connector,
}
static const struct drm_connector_hdmi_funcs reject_connector_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_1_4,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
.tmds_char_rate_valid = reject_connector_tmds_char_rate_valid,
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
@@ -163,6 +174,11 @@ reject_100mhz_connector_tmds_char_rate_valid(const struct drm_connector *connect
}
static const struct drm_connector_hdmi_funcs reject_100mhz_connector_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_1_4,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
.tmds_char_rate_valid = reject_100mhz_connector_tmds_char_rate_valid,
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
@@ -240,8 +256,6 @@ static const struct drm_encoder_helper_funcs test_encoder_helper_funcs = {
static
struct drm_atomic_helper_connector_hdmi_priv *
__connector_hdmi_init(struct kunit *test,
- unsigned int formats,
- unsigned int max_bpc,
const struct drm_connector_hdmi_funcs *hdmi_funcs,
const void *edid_data, size_t edid_len)
{
@@ -282,16 +296,15 @@ __connector_hdmi_init(struct kunit *test,
enc->possible_crtcs = drm_crtc_mask(priv->crtc);
conn = &priv->connector;
- conn->ycbcr_420_allowed = !!(formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420));
+ conn->ycbcr_420_allowed = !!(hdmi_funcs->supported_formats &
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420));
- ret = drmm_connector_hdmi_ini2(drm, conn,
- "Vendor", "Product",
+ priv->hdmi_funcs = *hdmi_funcs;
+ ret = drmm_connector_hdmi_init(drm, conn,
&dummy_connector_funcs,
- hdmi_funcs,
+ &priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- NULL,
- formats,
- max_bpc);
+ NULL);
KUNIT_ASSERT_EQ(test, ret, 0);
drm_connector_helper_add(conn, &dummy_connector_helper_funcs);
@@ -307,18 +320,14 @@ __connector_hdmi_init(struct kunit *test,
return priv;
}
-#define drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, formats, max_bpc, funcs, edid) \
- __connector_hdmi_init(test, formats, max_bpc, funcs, edid, ARRAY_SIZE(edid))
+#define drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, funcs, edid) \
+ __connector_hdmi_init(test, funcs, edid, ARRAY_SIZE(edid))
static
struct drm_atomic_helper_connector_hdmi_priv *
-drm_kunit_helper_connector_hdmi_init(struct kunit *test,
- unsigned int formats,
- unsigned int max_bpc)
+drm_kunit_helper_connector_hdmi_init(struct kunit *test)
{
return drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- formats,
- max_bpc,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
}
@@ -342,9 +351,7 @@ static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
@@ -417,9 +424,7 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes
struct drm_crtc *crtc;
int ret;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
@@ -492,9 +497,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
@@ -559,9 +562,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
@@ -626,9 +627,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
@@ -695,9 +694,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
@@ -764,9 +761,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
@@ -833,9 +828,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te
struct drm_crtc *crtc;
int ret;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
@@ -892,6 +885,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te
*/
static void drm_test_check_broadcast_rgb_cea_mode_yuv420(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
enum drm_hdmi_broadcast_rgb broadcast_rgb;
struct drm_modeset_acquire_ctx ctx;
@@ -904,12 +898,11 @@ static void drm_test_check_broadcast_rgb_cea_mode_yuv420(struct kunit *test)
int ret;
broadcast_rgb = *(enum drm_hdmi_broadcast_rgb *)test->param_value;
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420),
- 8,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -993,6 +986,7 @@ KUNIT_ARRAY_PARAM(check_broadcast_rgb_cea_mode_yuv420,
*/
static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *old_conn_state;
@@ -1005,10 +999,9 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 10;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 10,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1075,6 +1068,7 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
*/
static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *old_conn_state;
@@ -1087,10 +1081,9 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 10;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 10,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1154,6 +1147,7 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
*/
static void drm_test_check_output_bpc_dvi(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -1164,12 +1158,12 @@ static void drm_test_check_output_bpc_dvi(struct kunit *test)
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 12;
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444);
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_dvi_1080p);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1222,8 +1216,6 @@ static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test)
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1267,6 +1259,7 @@ static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test)
*/
static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -1276,10 +1269,9 @@ static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test)
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 10;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 10,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1322,6 +1314,7 @@ static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test)
*/
static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -1331,10 +1324,9 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test)
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 12;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1390,9 +1382,7 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm = &priv->drm;
@@ -1447,6 +1437,7 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test)
*/
static void drm_test_check_max_tmds_rate_bpc_fallback_rgb(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -1458,10 +1449,9 @@ static void drm_test_check_max_tmds_rate_bpc_fallback_rgb(struct kunit *test)
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 12;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1521,6 +1511,7 @@ static void drm_test_check_max_tmds_rate_bpc_fallback_rgb(struct kunit *test)
*/
static void drm_test_check_max_tmds_rate_bpc_fallback_yuv420(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -1532,11 +1523,11 @@ static void drm_test_check_max_tmds_rate_bpc_fallback_yuv420(struct kunit *test)
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 12;
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1597,6 +1588,7 @@ static void drm_test_check_max_tmds_rate_bpc_fallback_yuv420(struct kunit *test)
*/
static void drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv422(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -1608,12 +1600,12 @@ static void drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv422(struct kunit
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 12;
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444);
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1678,6 +1670,7 @@ static void drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv422(struct kunit
*/
static void drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv420(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -1689,11 +1682,11 @@ static void drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv420(struct kunit
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 12;
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1749,6 +1742,7 @@ static void drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv420(struct kunit
*/
static void drm_test_check_driver_unsupported_fallback_yuv420(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -1761,10 +1755,9 @@ static void drm_test_check_driver_unsupported_fallback_yuv420(struct kunit *test
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 12;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1835,6 +1828,7 @@ static void drm_test_check_driver_unsupported_fallback_yuv420(struct kunit *test
*/
static void drm_test_check_output_bpc_format_vic_1(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -1846,12 +1840,12 @@ static void drm_test_check_output_bpc_format_vic_1(struct kunit *test)
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 12;
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444);
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1906,6 +1900,7 @@ static void drm_test_check_output_bpc_format_vic_1(struct kunit *test)
*/
static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -1917,10 +1912,9 @@ static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test)
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 12;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -1979,6 +1973,7 @@ static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test)
*/
static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -1990,12 +1985,12 @@ static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 12;
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444);
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2067,8 +2062,6 @@ static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2121,6 +2114,7 @@ static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test
*/
static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_connector_state *conn_state;
@@ -2132,12 +2126,12 @@ static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *tes
struct drm_crtc *crtc;
int ret;
+ hdmi_funcs.max_bpc = 12;
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444);
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2196,9 +2190,7 @@ static void drm_test_check_disable_connector(struct kunit *test)
struct drm_crtc *crtc;
int ret;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
drm_modeset_acquire_init(&ctx, 0);
@@ -2265,6 +2257,7 @@ struct color_format_test_param {
*/
static void drm_test_check_hdmi_color_format(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
const struct color_format_test_param *param = test->param_value;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
@@ -2275,13 +2268,13 @@ static void drm_test_check_hdmi_color_format(struct kunit *test)
struct drm_display_mode *preferred;
int ret;
+ hdmi_funcs.max_bpc = 12;
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444);
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2373,6 +2366,7 @@ KUNIT_ARRAY_PARAM_DESC(check_hdmi_color_format, hdmi_color_format_params, desc);
*/
static void drm_test_check_hdmi_color_format_420_only(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
const struct color_format_test_param *param = test->param_value;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
@@ -2382,13 +2376,13 @@ static void drm_test_check_hdmi_color_format_420_only(struct kunit *test)
struct drm_display_mode *dank;
int ret;
+ hdmi_funcs.max_bpc = 12;
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444);
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
- 12,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2516,9 +2510,7 @@ static void drm_test_check_broadcast_rgb_value(struct kunit *test)
struct drm_connector_state *conn_state;
struct drm_connector *conn;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -2538,9 +2530,7 @@ static void drm_test_check_bpc_8_value(struct kunit *test)
struct drm_connector_state *conn_state;
struct drm_connector *conn;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -2558,13 +2548,15 @@ static void drm_test_check_bpc_8_value(struct kunit *test)
*/
static void drm_test_check_bpc_10_value(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
struct drm_connector *conn;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 10);
+ hdmi_funcs.max_bpc = 10;
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &hdmi_funcs,
+ test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -2582,13 +2574,15 @@ static void drm_test_check_bpc_10_value(struct kunit *test)
*/
static void drm_test_check_bpc_12_value(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
struct drm_connector *conn;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 12);
+ hdmi_funcs.max_bpc = 12;
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &hdmi_funcs,
+ test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -2604,15 +2598,17 @@ static void drm_test_check_bpc_12_value(struct kunit *test)
*/
static void drm_test_check_format_value(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
struct drm_connector *conn;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
- 8);
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444);
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &hdmi_funcs,
+ test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -2626,15 +2622,18 @@ static void drm_test_check_format_value(struct kunit *test)
*/
static void drm_test_check_tmds_char_value(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_connector_state *conn_state;
struct drm_connector *conn;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
- 12);
+ hdmi_funcs.max_bpc = 12;
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444);
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &hdmi_funcs,
+ test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -2668,9 +2667,7 @@ static void drm_test_check_mode_valid(struct kunit *test)
struct drm_connector *conn;
struct drm_display_mode *preferred;
- priv = drm_kunit_helper_connector_hdmi_init(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8);
+ priv = drm_kunit_helper_connector_hdmi_init(test);
KUNIT_ASSERT_NOT_NULL(test, priv);
conn = &priv->connector;
@@ -2693,8 +2690,6 @@ static void drm_test_check_mode_valid_reject_rate(struct kunit *test)
struct drm_display_mode *preferred;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8,
&reject_100mhz_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2725,8 +2720,6 @@ static void drm_test_check_mode_valid_reject(struct kunit *test)
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8,
&reject_connector_hdmi_funcs,
no_edid);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2755,8 +2748,6 @@ static void drm_test_check_mode_valid_reject_max_clock(struct kunit *test)
struct drm_display_mode *preferred;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_100mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2778,15 +2769,15 @@ static void drm_test_check_mode_valid_reject_max_clock(struct kunit *test)
*/
static void drm_test_check_mode_valid_yuv420_only_max_clock(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_display_mode *dank;
struct drm_connector *conn;
+ hdmi_funcs.supported_formats = BIT(HDMI_COLORSPACE_RGB) |
+ BIT(HDMI_COLORSPACE_YUV420);
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(HDMI_COLORSPACE_RGB) |
- BIT(HDMI_COLORSPACE_YUV420),
- 8,
- &dummy_connector_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2818,8 +2809,6 @@ drm_test_check_mode_valid_reject_yuv420_only_connector(struct kunit *test)
struct drm_connector *conn;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(HDMI_COLORSPACE_RGB),
- 8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2844,8 +2833,6 @@ drm_test_check_mode_valid_accept_yuv420_also_connector_rgb(struct kunit *test)
struct drm_connector *conn;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(HDMI_COLORSPACE_RGB),
- 8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2894,8 +2881,6 @@ static void drm_test_check_infoframes(struct kunit *test)
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8,
&dummy_connector_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -2960,6 +2945,11 @@ static int reject_infoframe_write_infoframe(struct drm_connector *connector,
}
static const struct drm_connector_hdmi_funcs reject_avi_infoframe_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_1_4,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = reject_infoframe_write_infoframe,
@@ -2988,8 +2978,6 @@ static void drm_test_check_reject_avi_infoframe(struct kunit *test)
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8,
&reject_avi_infoframe_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -3050,6 +3038,11 @@ static void drm_test_check_reject_avi_infoframe(struct kunit *test)
}
static const struct drm_connector_hdmi_funcs reject_hdr_infoframe_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_1_4,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
@@ -3083,8 +3076,6 @@ static void drm_test_check_reject_hdr_infoframe_bpc_8(struct kunit *test)
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8,
&reject_hdr_infoframe_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz_hdr);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -3172,6 +3163,7 @@ static void drm_test_check_reject_hdr_infoframe_bpc_8(struct kunit *test)
*/
static void drm_test_check_reject_hdr_infoframe_bpc_10(struct kunit *test)
{
+ struct drm_connector_hdmi_funcs hdmi_funcs = reject_hdr_infoframe_hdmi_funcs;
struct drm_atomic_helper_connector_hdmi_priv *priv;
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_commit *state;
@@ -3187,10 +3179,9 @@ static void drm_test_check_reject_hdr_infoframe_bpc_10(struct kunit *test)
bool replaced;
int ret;
+ hdmi_funcs.max_bpc = 10;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 10,
- &reject_hdr_infoframe_hdmi_funcs,
+ &hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz_hdr);
KUNIT_ASSERT_NOT_NULL(test, priv);
@@ -3280,6 +3271,11 @@ static void drm_test_check_reject_hdr_infoframe_bpc_10(struct kunit *test)
}
static const struct drm_connector_hdmi_funcs reject_audio_infoframe_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_1_4,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
.avi = {
.clear_infoframe = accept_infoframe_clear_infoframe,
.write_infoframe = accept_infoframe_write_infoframe,
@@ -3314,8 +3310,6 @@ static void drm_test_check_reject_audio_infoframe(struct kunit *test)
int ret;
priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
- 8,
&reject_audio_infoframe_hdmi_funcs,
test_edid_hdmi_1080p_rgb_max_200mhz);
KUNIT_ASSERT_NOT_NULL(test, priv);
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 07/25] drm/connector: Remove drmm_connector_hdmi_ini2()
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (5 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 06/25] drm/tests: hdmi: " Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 08/25] drm/connector: Drop redundant hdmi vendor/product fields Cristian Ciocaltea
` (17 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel, Diederik de Haas,
Robin Räber, Igor Paunovic
All callers have been converted to the new drmm_connector_hdmi_init()
signature, so drop the transitional drmm_connector_hdmi_init2() helper
along with its declaration, EXPORT_SYMBOL() and kernel-doc.
Tested-by: Diederik de Haas <diederik@cknow-tech.com> # NanoPC-T6 LTS, Rock 5B
Tested-by: Robin Räber <robin.raebi@gmail.com>
Tested-by: Igor Paunovic <royalnet026@gmail.com> # RK3588 Orange Pi 5 Plus, dual HDMI
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/drm_connector.c | 102 ----------------------------------------
include/drm/drm_connector.h | 9 ----
2 files changed, 111 deletions(-)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index d66188521881..bb508ec7a8cd 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -682,108 +682,6 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
}
EXPORT_SYMBOL(drmm_connector_hdmi_init);
-/**
- * drmm_connector_hdmi_ini2 - Init a preallocated HDMI connector
- * @dev: DRM device
- * @connector: A pointer to the HDMI connector to init
- * @vendor: HDMI Controller Vendor name
- * @product: HDMI Controller Product name
- * @funcs: callbacks for this connector
- * @hdmi_funcs: HDMI-related callbacks for this connector
- * @connector_type: user visible type of the connector
- * @ddc: optional pointer to the associated ddc adapter
- * @supported_formats: Bitmask of @drm_output_color_format listing supported output formats
- * @max_bpc: Maximum bits per char the HDMI connector supports
- *
- * Initialises a preallocated HDMI connector. Connectors can be
- * subclassed as part of driver connector objects.
- *
- * Cleanup is automatically handled with a call to
- * drm_connector_cleanup() in a DRM-managed action.
- *
- * The connector structure should be allocated with drmm_kzalloc().
- *
- * The @drm_connector_funcs.destroy hook must be NULL.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drmm_connector_hdmi_ini2(struct drm_device *dev,
- struct drm_connector *connector,
- const char *vendor, const char *product,
- const struct drm_connector_funcs *funcs,
- const struct drm_connector_hdmi_funcs *hdmi_funcs,
- int connector_type,
- struct i2c_adapter *ddc,
- unsigned long supported_formats,
- unsigned int max_bpc)
-{
- int ret;
-
- if (!vendor || !product)
- return -EINVAL;
-
- if ((strlen(vendor) > DRM_CONNECTOR_HDMI_VENDOR_LEN) ||
- (strlen(product) > DRM_CONNECTOR_HDMI_PRODUCT_LEN))
- return -EINVAL;
-
- if (!(connector_type == DRM_MODE_CONNECTOR_HDMIA ||
- connector_type == DRM_MODE_CONNECTOR_HDMIB))
- return -EINVAL;
-
- if (!supported_formats || !(supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444)))
- return -EINVAL;
-
- if (connector->ycbcr_420_allowed != !!(supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)))
- return -EINVAL;
-
- if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12))
- return -EINVAL;
-
- if (!hdmi_funcs->avi.clear_infoframe ||
- !hdmi_funcs->avi.write_infoframe ||
- !hdmi_funcs->hdmi.clear_infoframe ||
- !hdmi_funcs->hdmi.write_infoframe)
- return -EINVAL;
-
- ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
- if (ret)
- return ret;
-
- connector->hdmi.supported_formats = supported_formats;
- strtomem_pad(connector->hdmi.vendor, vendor, 0);
- strtomem_pad(connector->hdmi.product, product, 0);
-
- /*
- * drm_connector_attach_max_bpc_property() requires the
- * connector to have a state.
- */
- if (connector->funcs->atomic_create_state) {
- struct drm_connector_state *state;
-
- state = connector->funcs->atomic_create_state(connector);
- if (IS_ERR(state))
- return PTR_ERR(state);
-
- connector->state = state;
- }
-
- drm_connector_attach_max_bpc_property(connector, 8, max_bpc);
- connector->max_bpc = max_bpc;
-
- if (max_bpc > 8)
- drm_connector_attach_hdr_output_metadata_property(connector);
-
- ret = drm_connector_attach_color_format_property(connector, supported_formats);
- if (ret)
- return ret;
-
- connector->hdmi.funcs = hdmi_funcs;
-
- return 0;
-}
-EXPORT_SYMBOL(drmm_connector_hdmi_ini2);
-
/**
* drm_connector_attach_edid_property - attach edid property.
* @connector: the connector
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index f656ed8324d3..241d20a587e5 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -2731,15 +2731,6 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
const struct drm_connector_hdmi_funcs *hdmi_funcs,
int connector_type,
struct i2c_adapter *ddc);
-int drmm_connector_hdmi_ini2(struct drm_device *dev,
- struct drm_connector *connector,
- const char *vendor, const char *product,
- const struct drm_connector_funcs *funcs,
- const struct drm_connector_hdmi_funcs *hdmi_funcs,
- int connector_type,
- struct i2c_adapter *ddc,
- unsigned long supported_formats,
- unsigned int max_bpc);
void drm_connector_attach_edid_property(struct drm_connector *connector);
int drm_connector_register(struct drm_connector *connector);
int drm_connector_dynamic_register(struct drm_connector *connector);
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 08/25] drm/connector: Drop redundant hdmi vendor/product fields
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (6 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 07/25] drm/connector: Remove drmm_connector_hdmi_ini2() Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 09/25] drm/connector: Drop redundant hdmi supported_formats field Cristian Ciocaltea
` (16 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel, Diederik de Haas,
Robin Räber, Igor Paunovic
Now that all users migrated to the new drmm_connector_hdmi_init()
signature, vendor and product are provided through struct
drm_connector_hdmi_funcs, a reference to which is already stored in
drm_connector_hdmi.
Drop the redundant fields from drm_connector_hdmi and point its users to
hdmi.funcs->vendor and hdmi.funcs->product instead.
The connector registration tests used to verify that both names were
stored zero-padded and without any trailing NUL, as the SPD InfoFrame
requires. That will be covered by dedicated SPD InfoFrame tests, hence
drop the KUNIT_EXPECT_MEMEQ() checks on the removed fields.
Tested-by: Diederik de Haas <diederik@cknow-tech.com> # NanoPC-T6 LTS, Rock 5B
Tested-by: Robin Räber <robin.raebi@gmail.com>
Tested-by: Igor Paunovic <royalnet026@gmail.com> # RK3588 Orange Pi 5 Plus, dual HDMI
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/display/drm_hdmi_state_helper.c | 4 +--
drivers/gpu/drm/drm_connector.c | 4 ---
drivers/gpu/drm/tests/drm_connector_test.c | 41 +++----------------------
include/drm/drm_connector.h | 14 ++-------
4 files changed, 8 insertions(+), 55 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
index f533d5a7aab9..0e282abb118d 100644
--- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c
+++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
@@ -785,8 +785,8 @@ static int hdmi_generate_spd_infoframe(const struct drm_connector *connector,
return 0;
ret = hdmi_spd_infoframe_init(frame,
- connector->hdmi.vendor,
- connector->hdmi.product);
+ connector->hdmi.funcs->vendor,
+ connector->hdmi.funcs->product);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index bb508ec7a8cd..b1b7662c575b 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -646,10 +646,6 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
connector->hdmi.max_tmds_char_rate = hdmi_funcs->supported_tmds_char_rate;
}
- /* TODO: remove after conversion to new drmm_connector_hdmi_init() */
- strtomem_pad(connector->hdmi.vendor, hdmi_funcs->vendor, 0);
- strtomem_pad(connector->hdmi.product, hdmi_funcs->product, 0);
-
/*
* drm_connector_attach_max_bpc_property() requires the
* connector to have a state.
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index 354e3088cf0f..d98c6a42ccae 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -760,14 +760,11 @@ static void drm_test_connector_hdmi_init_null_product(struct kunit *test)
/*
* Test that the registration of a connector with a valid, shorter than
- * the max length, product name succeeds, and is stored padded with 0.
+ * the max length, product name succeeds.
*/
static void drm_test_connector_hdmi_init_product_valid(struct kunit *test)
{
struct drm_connector_init_priv *priv = test->priv;
- const unsigned char expected_product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] = {
- 'P', 'r', 'o', 'd',
- };
int ret;
priv->hdmi_funcs = dummy_hdmi_funcs;
@@ -782,25 +779,15 @@ static void drm_test_connector_hdmi_init_product_valid(struct kunit *test)
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_MEMEQ(test,
- priv->connector.hdmi.product,
- expected_product,
- sizeof(priv->connector.hdmi.product));
}
/*
* Test that the registration of a connector with a valid, at max
- * length, product name succeeds, and is stored padded without any
- * trailing \0.
+ * length, product name succeeds.
*/
static void drm_test_connector_hdmi_init_product_length_exact(struct kunit *test)
{
struct drm_connector_init_priv *priv = test->priv;
- const unsigned char expected_product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] = {
- 'P', 'r', 'o', 'd', 'u', 'c', 't',
- 'P', 'r', 'o', 'd', 'u', 'c', 't',
- 'P', 'r',
- };
int ret;
priv->hdmi_funcs = dummy_hdmi_funcs;
@@ -815,10 +802,6 @@ static void drm_test_connector_hdmi_init_product_length_exact(struct kunit *test
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_MEMEQ(test,
- priv->connector.hdmi.product,
- expected_product,
- sizeof(priv->connector.hdmi.product));
}
/*
@@ -846,14 +829,11 @@ static void drm_test_connector_hdmi_init_product_length_too_long(struct kunit *t
/*
* Test that the registration of a connector with a vendor name smaller
- * than the maximum length succeeds, and is stored padded with zeros.
+ * than the maximum length succeeds.
*/
static void drm_test_connector_hdmi_init_vendor_valid(struct kunit *test)
{
struct drm_connector_init_priv *priv = test->priv;
- const char expected_vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] = {
- 'V', 'e', 'n', 'd',
- };
int ret;
priv->hdmi_funcs = dummy_hdmi_funcs;
@@ -868,24 +848,15 @@ static void drm_test_connector_hdmi_init_vendor_valid(struct kunit *test)
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_MEMEQ(test,
- priv->connector.hdmi.vendor,
- expected_vendor,
- sizeof(priv->connector.hdmi.vendor));
}
/*
* Test that the registration of a connector with a vendor name at the
- * maximum length succeeds, and is stored padded without the trailing
- * zero.
+ * maximum length succeeds.
*/
static void drm_test_connector_hdmi_init_vendor_length_exact(struct kunit *test)
{
struct drm_connector_init_priv *priv = test->priv;
- const char expected_vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] = {
- 'V', 'e', 'n', 'd', 'o', 'r',
- 'V', 'e',
- };
int ret;
priv->hdmi_funcs = dummy_hdmi_funcs;
@@ -900,10 +871,6 @@ static void drm_test_connector_hdmi_init_vendor_length_exact(struct kunit *test)
DRM_MODE_CONNECTOR_HDMIA,
&priv->ddc);
KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_MEMEQ(test,
- priv->connector.hdmi.vendor,
- expected_vendor,
- sizeof(priv->connector.hdmi.vendor));
}
/*
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 241d20a587e5..c75bf65532d4 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1485,11 +1485,13 @@ struct drm_connector_infoframe_funcs {
* and controller capabilities
*/
struct drm_connector_hdmi_funcs {
+#define DRM_CONNECTOR_HDMI_VENDOR_LEN 8
/**
* @vendor: HDMI Controller Vendor name.
*/
const char *vendor;
+#define DRM_CONNECTOR_HDMI_PRODUCT_LEN 16
/**
* @product: HDMI Controller Product name
*/
@@ -2178,18 +2180,6 @@ struct drm_connector_hdmi_audio {
* struct drm_connector_hdmi - DRM Connector HDMI-related structure
*/
struct drm_connector_hdmi {
-#define DRM_CONNECTOR_HDMI_VENDOR_LEN 8
- /**
- * @vendor: HDMI Controller Vendor Name
- */
- unsigned char vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN] __nonstring;
-
-#define DRM_CONNECTOR_HDMI_PRODUCT_LEN 16
- /**
- * @product: HDMI Controller Product Name
- */
- unsigned char product[DRM_CONNECTOR_HDMI_PRODUCT_LEN] __nonstring;
-
/**
* @supported_formats: Bitmask of @drm_output_color_format
* supported by the controller.
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 09/25] drm/connector: Drop redundant hdmi supported_formats field
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (7 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 08/25] drm/connector: Drop redundant hdmi vendor/product fields Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 10/25] drm/connector: Drop redundant max_bpc field Cristian Ciocaltea
` (15 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel, Diederik de Haas,
Robin Räber, Igor Paunovic
Now that all users migrated to the new drmm_connector_hdmi_init()
signature, supported_formats is provided through struct
drm_connector_hdmi_funcs, a reference to which is already stored in
drm_connector_hdmi.
Drop the redundant field from drm_connector_hdmi and point its users to
hdmi.funcs->supported_formats instead.
Tested-by: Diederik de Haas <diederik@cknow-tech.com> # NanoPC-T6 LTS, Rock 5B
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Tested-by: Robin Räber <robin.raebi@gmail.com>
Tested-by: Igor Paunovic <royalnet026@gmail.com> # RK3588 Orange Pi 5 Plus, dual HDMI
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/display/drm_hdmi_state_helper.c | 2 +-
drivers/gpu/drm/drm_connector.c | 3 ---
include/drm/drm_connector.h | 6 ------
3 files changed, 1 insertion(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
index 0e282abb118d..97027dd17276 100644
--- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c
+++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
@@ -427,7 +427,7 @@ sink_supports_format_bpc(const struct drm_connector *connector,
return false;
}
- if (!(connector->hdmi.supported_formats & BIT(format))) {
+ if (!(connector->hdmi.funcs->supported_formats & BIT(format))) {
drm_dbg_kms(dev, "%s format unsupported by the connector.\n",
drm_hdmi_connector_get_output_format_name(format));
return false;
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index b1b7662c575b..2fb6fc0243b5 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -617,9 +617,6 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
if (ret)
return ret;
- /* TODO: remove after conversion to new drmm_connector_hdmi_init() */
- connector->hdmi.supported_formats = hdmi_funcs->supported_formats;
-
/*
* The supported HDMI version can be used to determinate the maximum
* TMDS character rate allowed by the specification. Some controllers,
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index c75bf65532d4..312638824918 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -2180,12 +2180,6 @@ struct drm_connector_hdmi_audio {
* struct drm_connector_hdmi - DRM Connector HDMI-related structure
*/
struct drm_connector_hdmi {
- /**
- * @supported_formats: Bitmask of @drm_output_color_format
- * supported by the controller.
- */
- unsigned long supported_formats;
-
/**
* @max_tmds_char_rate: Maximum TMDS character rate, in Hz,
* supported by the controller.
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 10/25] drm/connector: Drop redundant max_bpc field
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (8 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 09/25] drm/connector: Drop redundant hdmi supported_formats field Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 11/25] drm/connector: Use the SPD InfoFrame field length defines Cristian Ciocaltea
` (14 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel, Diederik de Haas,
Robin Räber, Igor Paunovic
Now that all users migrated to the new drmm_connector_hdmi_init()
signature, max_bpc is provided through struct drm_connector_hdmi_funcs,
a reference to which is already stored in drm_connector_hdmi.
Drop the redundant connector->max_bpc field and point its users to
hdmi.funcs->max_bpc instead.
Tested-by: Diederik de Haas <diederik@cknow-tech.com> # NanoPC-T6 LTS, Rock 5B
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Tested-by: Robin Räber <robin.raebi@gmail.com>
Tested-by: Igor Paunovic <royalnet026@gmail.com> # RK3588 Orange Pi 5 Plus, dual HDMI
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/display/drm_hdmi_state_helper.c | 6 +++---
drivers/gpu/drm/drm_connector.c | 2 --
include/drm/drm_connector.h | 5 -----
3 files changed, 3 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
index 97027dd17276..a85f85b844cb 100644
--- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c
+++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
@@ -320,7 +320,7 @@
void __drm_atomic_helper_connector_hdmi_state_init(struct drm_connector_state *new_conn_state,
struct drm_connector *connector)
{
- unsigned int max_bpc = connector->max_bpc;
+ unsigned int max_bpc = connector->hdmi.funcs ? connector->hdmi.funcs->max_bpc : 0;
new_conn_state->max_bpc = max_bpc;
new_conn_state->max_requested_bpc = max_bpc;
@@ -678,7 +678,7 @@ hdmi_compute_config(const struct drm_connector *connector,
{
unsigned int max_bpc = clamp_t(unsigned int,
conn_state->max_bpc,
- 8, connector->max_bpc);
+ 8, connector->hdmi.funcs->max_bpc);
enum drm_output_color_format fmt;
int ret;
@@ -811,7 +811,7 @@ static int hdmi_generate_hdr_infoframe(const struct drm_connector *connector,
if (!connector->hdmi.funcs->hdr_drm.write_infoframe)
return 0;
- if (connector->max_bpc < 10)
+ if (connector->hdmi.funcs->max_bpc < 10)
return 0;
if (!conn_state->hdr_output_metadata)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 2fb6fc0243b5..95dd4012580c 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -658,8 +658,6 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
}
drm_connector_attach_max_bpc_property(connector, 8, hdmi_funcs->max_bpc);
- /* TODO: remove after conversion to new drmm_connector_hdmi_init() */
- connector->max_bpc = hdmi_funcs->max_bpc;
if (hdmi_funcs->max_bpc > 8)
drm_connector_attach_hdr_output_metadata_property(connector);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 312638824918..262bc4d91a3c 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -2446,11 +2446,6 @@ struct drm_connector {
*/
struct drm_property_blob *path_blob_ptr;
- /**
- * @max_bpc: Maximum bits per color channel the connector supports.
- */
- unsigned int max_bpc;
-
/**
* @max_bpc_property: Default connector property for the max bpc to be
* driven out of the connector.
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 11/25] drm/connector: Use the SPD InfoFrame field length defines
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (9 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 10/25] drm/connector: Drop redundant max_bpc field Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 12/25] drm/connector: Fix comment in drmm_connector_hdmi_init() Cristian Ciocaltea
` (13 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel, Robin Räber,
Igor Paunovic
DRM_CONNECTOR_HDMI_{VENDOR,PRODUCT}_LEN used to size the vendor and
product arrays in struct drm_connector_hdmi. Those arrays are gone and
both names are now only validated before being copied into the SPD
InfoFrame, hence the limits they have to be checked against are the ones
of the SPD InfoFrame fields themselves.
Switch the remaining users over to
HDMI_SPD_INFOFRAME_{VENDOR,PRODUCT}_LEN and drop the DRM specific
defines, so that the two cannot drift apart.
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Tested-by: Robin Räber <robin.raebi@gmail.com>
Tested-by: Igor Paunovic <royalnet026@gmail.com> # RK3588 Orange Pi 5 Plus, dual HDMI
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/drm_connector.c | 4 ++--
drivers/gpu/drm/tests/drm_connector_test.c | 13 +++++++------
include/drm/drm_connector.h | 8 ++++----
3 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 95dd4012580c..355ba53cab6b 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -579,8 +579,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
if (!hdmi_funcs->vendor || !hdmi_funcs->product)
return -EINVAL;
- if ((strlen(hdmi_funcs->vendor) > DRM_CONNECTOR_HDMI_VENDOR_LEN) ||
- (strlen(hdmi_funcs->product) > DRM_CONNECTOR_HDMI_PRODUCT_LEN))
+ if ((strlen(hdmi_funcs->vendor) > HDMI_SPD_INFOFRAME_VENDOR_LEN) ||
+ (strlen(hdmi_funcs->product) > HDMI_SPD_INFOFRAME_PRODUCT_LEN))
return -EINVAL;
if (!hdmi_funcs->supported_formats ||
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index d98c6a42ccae..591557c4a8bb 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -771,7 +771,7 @@ static void drm_test_connector_hdmi_init_product_valid(struct kunit *test)
priv->hdmi_funcs.product = "Prod";
KUNIT_ASSERT_LT(test, strlen(priv->hdmi_funcs.product),
- DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+ HDMI_SPD_INFOFRAME_PRODUCT_LEN);
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
@@ -794,7 +794,7 @@ static void drm_test_connector_hdmi_init_product_length_exact(struct kunit *test
priv->hdmi_funcs.product = "ProductProductPr";
KUNIT_ASSERT_EQ(test, strlen(priv->hdmi_funcs.product),
- DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+ HDMI_SPD_INFOFRAME_PRODUCT_LEN);
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
@@ -817,7 +817,7 @@ static void drm_test_connector_hdmi_init_product_length_too_long(struct kunit *t
priv->hdmi_funcs.product = "ProductProductProduct";
KUNIT_ASSERT_GT(test, strlen(priv->hdmi_funcs.product),
- DRM_CONNECTOR_HDMI_PRODUCT_LEN);
+ HDMI_SPD_INFOFRAME_PRODUCT_LEN);
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
@@ -840,7 +840,7 @@ static void drm_test_connector_hdmi_init_vendor_valid(struct kunit *test)
priv->hdmi_funcs.vendor = "Vend";
KUNIT_ASSERT_LT(test, strlen(priv->hdmi_funcs.vendor),
- DRM_CONNECTOR_HDMI_VENDOR_LEN);
+ HDMI_SPD_INFOFRAME_VENDOR_LEN);
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
@@ -863,7 +863,7 @@ static void drm_test_connector_hdmi_init_vendor_length_exact(struct kunit *test)
priv->hdmi_funcs.vendor = "VendorVe";
KUNIT_ASSERT_EQ(test, strlen(priv->hdmi_funcs.vendor),
- DRM_CONNECTOR_HDMI_VENDOR_LEN);
+ HDMI_SPD_INFOFRAME_VENDOR_LEN);
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
@@ -884,7 +884,8 @@ static void drm_test_connector_hdmi_init_vendor_length_too_long(struct kunit *te
priv->hdmi_funcs = dummy_hdmi_funcs;
priv->hdmi_funcs.vendor = "VendorVendor";
- KUNIT_ASSERT_GT(test, strlen(priv->hdmi_funcs.vendor), DRM_CONNECTOR_HDMI_VENDOR_LEN);
+ KUNIT_ASSERT_GT(test, strlen(priv->hdmi_funcs.vendor),
+ HDMI_SPD_INFOFRAME_VENDOR_LEN);
ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
&dummy_funcs,
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 262bc4d91a3c..2d9ae3a99329 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1485,15 +1485,15 @@ struct drm_connector_infoframe_funcs {
* and controller capabilities
*/
struct drm_connector_hdmi_funcs {
-#define DRM_CONNECTOR_HDMI_VENDOR_LEN 8
/**
- * @vendor: HDMI Controller Vendor name.
+ * @vendor: HDMI Controller Vendor name, at most
+ * HDMI_SPD_INFOFRAME_VENDOR_LEN characters long.
*/
const char *vendor;
-#define DRM_CONNECTOR_HDMI_PRODUCT_LEN 16
/**
- * @product: HDMI Controller Product name
+ * @product: HDMI Controller Product name, at most
+ * HDMI_SPD_INFOFRAME_PRODUCT_LEN characters long.
*/
const char *product;
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 12/25] drm/connector: Fix comment in drmm_connector_hdmi_init()
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (10 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 11/25] drm/connector: Use the SPD InfoFrame field length defines Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 13/25] drm/tests: edid: Fix conformity for 1080p+4K YUV420 200MHz EDID Cristian Ciocaltea
` (12 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
The comment related to the computation of maximum TMDS character rate
refers to a non-existent caps->max_tmds_char_rate, a left-over from the
early stages of development.
Update the statement to reference hdmi_funcs->supported_tmds_char_rate.
While at it, replace the incorrect "determinate" with "determine" in the
same comment block.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/drm_connector.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 355ba53cab6b..663b3072a1d0 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -618,13 +618,13 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
return ret;
/*
- * The supported HDMI version can be used to determinate the maximum
+ * The supported HDMI version can be used to determine the maximum
* TMDS character rate allowed by the specification. Some controllers,
* however, may support a lower rate than that version would imply.
*
- * A non-zero caps->max_tmds_char_rate lets drivers override this
- * default with the actual controller capability. A value of zero keeps
- * the limit inferred from supported_hdmi_ver.
+ * A non-zero hdmi_funcs->supported_tmds_char_rate lets drivers override
+ * this default with the actual controller capability. A value of zero
+ * keeps the limit inferred from supported_hdmi_ver.
*/
if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_2_0)
connector->hdmi.max_tmds_char_rate = HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ;
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 13/25] drm/tests: edid: Fix conformity for 1080p+4K YUV420 200MHz EDID
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (11 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 12/25] drm/connector: Fix comment in drmm_connector_hdmi_init() Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 14/25] drm/tests: edid: Fix conformity for 4K RGB/YUV 340MHz EDID Cristian Ciocaltea
` (11 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
The test EDID added by commit 58fe1d78605e ("drm/tests: hdmi: Provide
EDID supporting 4K@30Hz with YUV420 only") fails 'edid-decode --check'
(v1.33.0) with the following issues:
Warnings:
Block 1, CTA-861 Extension Block:
IT Video Formats are overscanned by default, but normally this should be underscanned.
YCbCr 4:2:0 Video Data Block: Some YCbCr 4:2:0 timings are invalid for HDMI 2.1
(which requires an RGB timings pixel rate >= 590 MHz).
EDID:
Base EDID: Some timings are out of range of the Monitor Ranges:
Maximum Clock: 297.000 MHz (Monitor: 170.000 MHz)
CTA-861: Multiple native progressive timings are defined.
Failures:
Block 0, Base EDID:
Vendor & Product Identification: EDID 1.3 does not support week 0xff.
Block 1, CTA-861 Extension Block:
Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: Max TMDS rate is > 0 and <= 340 or > 600.
YCbCr 4:2:0 Capability Map Data Block: Empty Capability Map.
YCbCr 4:2:0 Video Data Block: YCbCr 4:2:0-only VIC 95 is also a regular VIC.
Missing VCDB, needed for Set Selectable RGB Quantization to avoid interop issues.
EDID:
CTA-861: The maximum HDMI TMDS clock is 200000 kHz, but one or more video timings go up to 297000 kHz.
Address them while preserving the advertised maximum resolutions, BPC
and TMDS clock used by the existing KUnit tests:
- Week 0xff is not valid for EDID 1.3; set it to 0x00 so that the Model
year field becomes a regular Year of Manufacture.
- The HDMI Forum VSDB Max TMDS Character Rate must be 0 when no rate
above 340 MHz is supported. The HDMI VSDB still advertises 200 MHz as
Max TMDS Clock.
- VIC 95 (3840x2160@30Hz) was reported as YUV420-only but was listed in
both the regular Video Data Block and the YCbCr 4:2:0 Video Data
Block. Drop it from the regular VDB (so it stays advertised in the
4:2:0 VDB only) and remove the now unnecessary empty YCbCr 4:2:0
Capability Map Data Block.
- Add a Video Capability Data Block advertising selectable RGB and YCC
quantization range plus underscan for IT/CE formats; also set the 'IT
Video Formats Underscanned' bit in the CTA-861 extension header to
match.
- Bump the Display Range Limits max pixel clock from 170 MHz to 300 MHz
so it covers the 297 MHz pixel rate of VIC 95.
- Drop the 'native' flag from VIC 16 in the Video Data Block so it is no
longer flagged as a second native progressive timing alongside the
preferred DTD.
The only remaining warning is HDMI 2.1 related and can be ignored, as it
requires that any mode listed in the YCbCr 4:2:0 Video Data Block must
have a corresponding RGB pixel clock >= 590 MHz.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_kunit_edid.c | 65 +++++++++++++++++++++-------------
1 file changed, 41 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.c b/drivers/gpu/drm/tests/drm_kunit_edid.c
index c807ae0d9faa..cf28f3c0f844 100644
--- a/drivers/gpu/drm/tests/drm_kunit_edid.c
+++ b/drivers/gpu/drm/tests/drm_kunit_edid.c
@@ -766,22 +766,22 @@ EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz);
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00
- * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26
+ * 00 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26
* 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
* 45 00 c0 1c 32 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18
- * 55 18 5e 11 00 0a 20 20 20 20 20 20 00 00 00 10
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 bb
+ * 55 18 5e 1e 00 0a 20 20 20 20 20 20 00 00 00 10
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ad
*
- * 02 03 29 31 42 90 5f 6c 03 0c 00 10 00 78 28 20
- * 00 00 01 03 6d d8 5d c4 01 28 80 07 00 00 00 00
- * 00 00 e3 0f 00 00 e2 0e 5f 00 00 00 00 00 00 00
+ * 02 03 27 b1 41 10 6c 03 0c 00 10 00 78 28 20 00
+ * 00 01 03 6d d8 5d c4 01 00 80 07 00 00 00 00 00
+ * 00 e2 00 ca e2 0e 5f 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ca
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 9a
*
* ----------------
*
@@ -790,7 +790,7 @@ EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz);
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 52
- * Model year: 2025
+ * Made in: 2025
* Basic Display Parameters & Features:
* Digital display
* Maximum image size: 96 cm x 54 cm
@@ -813,21 +813,21 @@ EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz);
* Vfront 4 Vsync 5 Vback 36 Vpol P
* Display Product Name: 'Test EDID'
* Display Range Limits:
- * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 170 MHz
+ * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 300 MHz
* Dummy Descriptor:
* Extension blocks: 1
- * Checksum: 0xbb
+ * Checksum: 0xad
*
* ----------------
*
* Block 1, CTA-861 Extension Block:
* Revision: 3
+ * Underscans IT Video Formats by default
* Supports YCbCr 4:4:4
* Supports YCbCr 4:2:2
* Native detailed modes: 1
* Video Data Block:
- * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (native)
- * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
+ * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz
* Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
* Source physical address: 1.0.0.0
* DC_48bit
@@ -838,40 +838,57 @@ EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz);
* Extended HDMI video details:
* Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8:
* Version: 1
- * Maximum TMDS Character Rate: 200 MHz
* SCDC Present
* Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding
* Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding
* Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding
- * YCbCr 4:2:0 Capability Map Data Block:
- * Empty Capability Map
+ * VRRmin: 0 Hz
+ * VRRmax: 0 Hz
+ * Video Capability Data Block:
+ * YCbCr quantization: Selectable (via AVI YQ)
+ * RGB quantization: Selectable (via AVI Q)
+ * PT scan behavior: No Data
+ * IT scan behavior: Always Underscanned
+ * CE scan behavior: Always Underscanned
* YCbCr 4:2:0 Video Data Block:
* VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
- * Checksum: 0xca
+ * Checksum: 0x9a Unused space in Extension Block: 88 bytes
+ *
+ * ----------------
+ *
+ * edid-decode 1.33.0
+ *
+ * Warnings:
+ *
+ * Block 1, CTA-861 Extension Block:
+ * YCbCr 4:2:0 Video Data Block: Some YCbCr 4:2:0 timings are invalid for HDMI 2.1
+ * (which requires an RGB timings pixel rate >= 590 MHz).
+ *
+ * EDID conformity: PASS
*/
const unsigned char test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
- 0x55, 0x18, 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x55, 0x18, 0x5e, 0x1e, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbb, 0x02, 0x03, 0x29, 0x31,
- 0x42, 0x90, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x28, 0x20,
- 0x00, 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x28, 0x80, 0x07,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x00, 0x00, 0xe2, 0x0e,
- 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xad, 0x02, 0x03, 0x27, 0xb1,
+ 0x41, 0x10, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x28, 0x20, 0x00,
+ 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x00, 0x80, 0x07, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe2, 0x0e, 0x5f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xca
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x9a,
};
EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 14/25] drm/tests: edid: Fix conformity for 4K RGB/YUV 340MHz EDID
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (12 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 13/25] drm/tests: edid: Fix conformity for 1080p+4K YUV420 200MHz EDID Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 15/25] drm/tests: edid: Add 4K@60Hz EDID with 600MHz TMDS Cristian Ciocaltea
` (10 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
The test EDID added by commit 54a5f1c4d5f8 ("drm/tests: hdmi: Provide
EDID supporting 4K@30Hz with RGB/YUV") fails 'edid-decode --check'
(v1.33.0) with the following issues:
Warnings:
Block 1, CTA-861 Extension Block:
IT Video Formats are overscanned by default, but normally this should be underscanned.
YCbCr 4:2:0 Capability Map Data Block: Some YCbCr 4:2:0 timings are invalid for HDMI 2.1
(which requires an RGB timings pixel rate >= 590 MHz).
Failures:
Block 0, Base EDID:
Vendor & Product Identification: EDID 1.3 does not support week 0xff.
Detailed Timing Descriptor #1: Mismatch of image size 1600x900 mm vs display size 960x540 mm.
Block 1, CTA-861 Extension Block:
Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: Max TMDS rate is > 0 and <= 340 or > 600.
YCbCr 4:2:0 Video Data Block: This Data Block is empty.
Missing VCDB, needed for Set Selectable RGB Quantization to avoid interop issues.
EDID:
Base EDID: The DTD max image size is 1600x900mm, which is larger than the display size 960.0x540.0mm.
Address them while preserving the advertised maximum resolution, BPC and
TMDS clock used by the existing KUnit tests:
- Week 0xff is not valid for EDID 1.3; set it to 0x00 so that the Model
year field becomes a regular Year of Manufacture.
- Align the DTD image size with the Basic Display Parameters maximum
image size (96 cm x 54 cm).
- The HDMI Forum VSDB Max TMDS Character Rate must be 0 when no rate
above 340 MHz is supported. The HDMI VSDB still advertises 340 MHz as
Max TMDS Clock.
- Drop the empty YCbCr 4:2:0 Video Data Block. VIC 95 (3840x2160@30Hz)
remains advertised as YUV420-capable via the YCbCr 4:2:0 Capability
Map Data Block, which references the corresponding entry in the
regular Video Data Block.
- Add a Video Capability Data Block advertising selectable RGB and YCC
quantization range plus underscan for IT/CE formats; also set the 'IT
Video Formats Underscanned' bit in the CTA-861 extension header to
match.
The only remaining warning is HDMI 2.1 related and can be ignored, as it
requires that any mode listed in the YCbCr 4:2:0 Video Data Block must
have a corresponding RGB pixel clock >= 590 MHz.
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_kunit_edid.c | 57 ++++++++++++++++++++++------------
1 file changed, 38 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.c b/drivers/gpu/drm/tests/drm_kunit_edid.c
index cf28f3c0f844..c41ddfc0e32a 100644
--- a/drivers/gpu/drm/tests/drm_kunit_edid.c
+++ b/drivers/gpu/drm/tests/drm_kunit_edid.c
@@ -900,22 +900,22 @@ EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
* edid-decode (hex):
*
* 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00
- * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26
+ * 00 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26
* 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01
* 01 01 01 01 01 01 04 74 00 30 f2 70 5a 80 b0 58
- * 8a 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
+ * 8a 00 c0 1c 32 00 00 1e 00 00 00 fc 00 54 65 73
* 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18
* 55 18 5e 22 00 0a 20 20 20 20 20 20 00 00 00 10
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ce
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 e6
*
- * 02 03 27 31 41 5f 6c 03 0c 00 10 00 78 44 20 00
- * 00 01 03 6d d8 5d c4 01 44 80 07 00 00 00 00 00
- * 00 e3 0f 01 00 e1 0e 00 00 00 00 00 00 00 00 00
+ * 02 03 28 b1 41 5f 6c 03 0c 00 10 00 78 44 20 00
+ * 00 01 03 6d d8 5d c4 01 00 80 07 00 00 00 00 00
+ * 00 e2 00 ca e3 0f 01 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 84
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 8a
*
* ----------------
*
@@ -924,7 +924,7 @@ EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
* Vendor & Product Identification:
* Manufacturer: LNX
* Model: 52
- * Model year: 2025
+ * Made in: 2025
* Basic Display Parameters & Features:
* Digital display
* Maximum image size: 96 cm x 54 cm
@@ -942,7 +942,7 @@ EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
* DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
* Standard Timings: none
* Detailed Timing Descriptors:
- * DTD 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz (1600 mm x 900 mm)
+ * DTD 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz (960 mm x 540 mm)
* Hfront 176 Hsync 88 Hback 296 Hpol P
* Vfront 8 Vsync 10 Vback 72 Vpol P
* Display Product Name: 'Test EDID'
@@ -950,12 +950,13 @@ EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
* Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 340 MHz
* Dummy Descriptor:
* Extension blocks: 1
- * Checksum: 0xce
+ * Checksum: 0xe6
*
* ----------------
*
* Block 1, CTA-861 Extension Block:
* Revision: 3
+ * Underscans IT Video Formats by default
* Supports YCbCr 4:4:4
* Supports YCbCr 4:2:2
* Native detailed modes: 1
@@ -971,31 +972,49 @@ EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz);
* Extended HDMI video details:
* Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8:
* Version: 1
- * Maximum TMDS Character Rate: 340 MHz
* SCDC Present
* Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding
* Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding
* Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding
+ * VRRmin: 0 Hz
+ * VRRmax: 0 Hz
+ * Video Capability Data Block:
+ * YCbCr quantization: Selectable (via AVI YQ)
+ * RGB quantization: Selectable (via AVI Q)
+ * PT scan behavior: No Data
+ * IT scan behavior: Always Underscanned
+ * CE scan behavior: Always Underscanned
* YCbCr 4:2:0 Capability Map Data Block:
* VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz
- * YCbCr 4:2:0 Video Data Block:
- * Checksum: 0x84
+ * Checksum: 0x8a Unused space in Extension Block: 87 bytes
+ *
+ * ----------------
+ *
+ * edid-decode 1.33.0
+ *
+ * Warnings:
+ *
+ * Block 1, CTA-861 Extension Block:
+ * YCbCr 4:2:0 Capability Map Data Block: Some YCbCr 4:2:0 timings are invalid for HDMI 2.1
+ * (which requires an RGB timings pixel rate >= 590 MHz).
+ *
+ * EDID conformity: PASS
*/
const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x74, 0x00, 0x30, 0xf2, 0x70,
- 0x5a, 0x80, 0xb0, 0x58, 0x8a, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
+ 0x5a, 0x80, 0xb0, 0x58, 0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
0x55, 0x18, 0x5e, 0x22, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xce, 0x02, 0x03, 0x27, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe6, 0x02, 0x03, 0x28, 0xb1,
0x41, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x44, 0x20, 0x00,
- 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x44, 0x80, 0x07, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x01, 0x00, 0xe1, 0x0e, 0x00,
+ 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x00, 0x80, 0x07, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe3, 0x0f, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -1003,7 +1022,7 @@ const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x84
+ 0x00, 0x00, 0x00, 0x8a,
};
EXPORT_SYMBOL(test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz);
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 15/25] drm/tests: edid: Add 4K@60Hz EDID with 600MHz TMDS
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (13 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 14/25] drm/tests: edid: Fix conformity for 4K RGB/YUV 340MHz EDID Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 16/25] drm/tests: connector: Add HDMI max_tmds_char_rate init coverage Cristian Ciocaltea
` (9 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
Add a new test EDID advertising the following capabilities:
Max resolution: 3840x2160@60Hz with RGB, YUV444, YUV422, YUV420
Max BPC: 16
Max TMDS clock: 600 MHz
This is needed to exercise the new HDMI infrastructure that decides
whether scrambling needs to be enabled for a given display mode and
to validate that high-rate modes are properly rejected/accepted
depending on the source/sink scrambling capability.
The EDID is derived from test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz
with the following changes:
- Upgraded the first DTD from 3840x2160@30 (297 MHz) to 3840x2160@60
(594 MHz). The H/V totals (4400x2250) and porches already match the
HDMI 2.0 4K@60Hz standard timing, so only the pixel clock had to be
bumped.
- Widened Display Range Limits descriptor to cover the new timing, i.e.
24-140 kHz H, max dotclock 600 MHz.
- Updated CTA-861 Video Data Block to advertise VIC 97 (4K@60Hz) instead
of VIC 95 (4k30).
- Bumped HF-VSDB Max TMDS Character Rate to 600 MHz, keeping
SCDC_Present set so that the sink advertises scrambling support.
- Cleared HDMI VSDB Max TMDS clock byte, as required by HDMI 2.0 when
the HF-VSDB declares a Max TMDS Character Rate above 340 MHz.
- Provided a Video Capability Data Block advertising selectable RGB and
YCC quantization range plus underscan for IT/CE formats; also set the
'IT Video Formats Underscanned' bit in the CTA-861 extension header to
match.
- Aligned base block image size with the DTD (160x90 cm) and set the
Week of Manufacture to 0x00 instead of 0xff, since the Model Year
flag is not supported by EDID 1.3, so that the Model year field
becomes a regular Year of Manufacture. Both fix inherited EDID
conformity failures.
- Dropped the empty YCbCr 4:2:0 Video Data Block inherited from the
parent EDID, which is flagged as a failure by edid-decode v1.33.0
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_kunit_edid.c | 129 +++++++++++++++++++++++++++++++++
drivers/gpu/drm/tests/drm_kunit_edid.h | 1 +
2 files changed, 130 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.c b/drivers/gpu/drm/tests/drm_kunit_edid.c
index c41ddfc0e32a..a6c5e8d6077a 100644
--- a/drivers/gpu/drm/tests/drm_kunit_edid.c
+++ b/drivers/gpu/drm/tests/drm_kunit_edid.c
@@ -1026,6 +1026,135 @@ const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[] = {
};
EXPORT_SYMBOL(test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz);
+/*
+ * Max resolution: 3840x2160@60Hz with RGB, YUV444, YUV422, YUV420
+ * Max BPC: 16 for all modes
+ * Max TMDS clock: 600 MHz
+ *
+ * edid-decode (hex):
+ *
+ * 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00
+ * 00 23 01 03 80 a0 5a 78 0f ee 91 a3 54 4c 99 26
+ * 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01
+ * 01 01 01 01 01 01 08 e8 00 30 f2 70 5a 80 b0 58
+ * 8a 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73
+ * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18
+ * 55 18 8c 3c 00 0a 20 20 20 20 20 20 00 00 00 10
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 a9
+ *
+ * 02 03 28 b1 41 61 6c 03 0c 00 10 00 78 00 20 00
+ * 00 01 03 6d d8 5d c4 01 78 80 07 00 00 00 00 00
+ * 00 e2 00 ca e3 0f 01 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 54
+ *
+ * ----------------
+ *
+ * Block 0, Base EDID:
+ * EDID Structure Version & Revision: 1.3
+ * Vendor & Product Identification:
+ * Manufacturer: LNX
+ * Model: 52
+ * Made in: 2025
+ * Basic Display Parameters & Features:
+ * Digital display
+ * Maximum image size: 160 cm x 90 cm
+ * Gamma: 2.20
+ * RGB color display
+ * Default (sRGB) color space is primary color space
+ * First detailed timing is the preferred timing
+ * Supports GTF timings within operating range
+ * Color Characteristics:
+ * Red : 0.6396, 0.3300
+ * Green: 0.2998, 0.5996
+ * Blue : 0.1503, 0.0595
+ * White: 0.3125, 0.3291
+ * Established Timings I & II:
+ * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz
+ * Standard Timings: none
+ * Detailed Timing Descriptors:
+ * DTD 1: 3840x2160 60.000000 Hz 16:9 135.000 kHz 594.000000 MHz (1600 mm x 900 mm)
+ * Hfront 176 Hsync 88 Hback 296 Hpol P
+ * Vfront 8 Vsync 10 Vback 72 Vpol P
+ * Display Product Name: 'Test EDID'
+ * Display Range Limits:
+ * Monitor ranges (GTF): 24-85 Hz V, 24-140 kHz H, max dotclock 600 MHz
+ * Dummy Descriptor:
+ * Extension blocks: 1
+ * Checksum: 0xa9
+ *
+ * ----------------
+ *
+ * Block 1, CTA-861 Extension Block:
+ * Revision: 3
+ * Underscans IT Video Formats by default
+ * Supports YCbCr 4:4:4
+ * Supports YCbCr 4:2:2
+ * Native detailed modes: 1
+ * Video Data Block:
+ * VIC 97: 3840x2160 60.000000 Hz 16:9 135.000 kHz 594.000000 MHz
+ * Vendor-Specific Data Block (HDMI), OUI 00-0C-03:
+ * Source physical address: 1.0.0.0
+ * DC_48bit
+ * DC_36bit
+ * DC_30bit
+ * DC_Y444
+ * Maximum TMDS clock: 0 MHz
+ * Extended HDMI video details:
+ * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8:
+ * Version: 1
+ * Maximum TMDS Character Rate: 600 MHz
+ * SCDC Present
+ * Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding
+ * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding
+ * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding
+ * VRRmin: 0 Hz
+ * VRRmax: 0 Hz
+ * Video Capability Data Block:
+ * YCbCr quantization: Selectable (via AVI YQ)
+ * RGB quantization: Selectable (via AVI Q)
+ * PT scan behavior: No Data
+ * IT scan behavior: Always Underscanned
+ * CE scan behavior: Always Underscanned
+ * YCbCr 4:2:0 Capability Map Data Block:
+ * VIC 97: 3840x2160 60.000000 Hz 16:9 135.000 kHz 594.000000 MHz
+ * Checksum: 0x54 Unused space in Extension Block: 87 bytes
+ *
+ * ----------------
+ *
+ * edid-decode 1.33.0
+ *
+ * EDID conformity: PASS
+ */
+const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x01, 0x03, 0x80, 0xa0, 0x5a, 0x78,
+ 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0xe8, 0x00, 0x30, 0xf2, 0x70,
+ 0x5a, 0x80, 0xb0, 0x58, 0x8a, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44,
+ 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
+ 0x55, 0x18, 0x8c, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa9, 0x02, 0x03, 0x28, 0xb1,
+ 0x41, 0x61, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x00, 0x20, 0x00,
+ 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x78, 0x80, 0x07, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe3, 0x0f, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x54,
+};
+EXPORT_SYMBOL(test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
MODULE_DESCRIPTION("EDID Definitions for KUnit tests");
MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h
index 28b4df93a555..fb5b0295e35e 100644
--- a/drivers/gpu/drm/tests/drm_kunit_edid.h
+++ b/drivers/gpu/drm/tests/drm_kunit_edid.h
@@ -12,5 +12,6 @@ extern const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz[256];
extern const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[256];
extern const unsigned char test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz[256];
extern const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[256];
+extern const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz[256];
#endif // DRM_KUNIT_EDID_H_
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 16/25] drm/tests: connector: Add HDMI max_tmds_char_rate init coverage
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (14 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 15/25] drm/tests: edid: Add 4K@60Hz EDID with 600MHz TMDS Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 17/25] drm/tests: connector: Add HDMI source-side scrambler coverage Cristian Ciocaltea
` (8 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
Add KUnit tests for the new drmm_connector_hdmi_init() extension, which
infers the connector's max_tmds_char_rate from the HDMI specification
version advertised by the driver, for every known version. Also
validate a driver-capped max_tmds_char_rate against the version-inferred
limit: override below, accept at, reject above, reject when no version
is advertised. Additionally, make sure an HDMI 2.0 connector capped at
the HDMI 1.3 limit is still reported as scrambling capable.
Since HDMI_VERSION_2_0 requires scrambler callbacks to be provided, add
a dummy_hdmi_funcs_scrambler instance for tests that initialize HDMI 2.0
connectors.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_connector_test.c | 207 +++++++++++++++++++++++++++++
1 file changed, 207 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index 591557c4a8bb..698706fca5db 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -54,6 +54,34 @@ static const struct drm_connector_hdmi_funcs dummy_hdmi_funcs = {
},
};
+static int accept_scrambler_enable(struct drm_connector *connector)
+{
+ return 0;
+}
+
+static int accept_scrambler_disable(struct drm_connector *connector)
+{
+ return 0;
+}
+
+static const struct drm_connector_hdmi_funcs dummy_hdmi_funcs_scrambler = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_2_0,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
+ .scrambler_enable = accept_scrambler_enable,
+ .scrambler_disable = accept_scrambler_disable,
+ .avi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+ .hdmi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+};
+
static const struct drm_connector_funcs dummy_funcs = {
.atomic_create_state = drm_atomic_helper_connector_create_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -1229,6 +1257,178 @@ KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_type_invalid,
drm_connector_hdmi_init_type_invalid_tests,
drm_connector_hdmi_init_type_desc);
+/*
+ * Test that the registration of an HDMI connector without an explicit max TMDS
+ * character rate being provided succeeds, and the connector limit is inferred
+ * from the advertised HDMI specification version.
+ */
+struct drm_connector_hdmi_init_max_tmds_rate_inferred_case {
+ const char *desc;
+ enum hdmi_version ver;
+ unsigned long long expected;
+};
+
+static void drm_test_connector_hdmi_init_max_tmds_rate_inferred(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ const struct drm_connector_hdmi_init_max_tmds_rate_inferred_case *params =
+ test->param_value;
+ int ret;
+
+ priv->hdmi_funcs = params->ver < HDMI_VERSION_2_0 ?
+ dummy_hdmi_funcs : dummy_hdmi_funcs_scrambler;
+ priv->hdmi_funcs.supported_hdmi_ver = params->ver;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ &priv->hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, priv->connector.hdmi.max_tmds_char_rate,
+ params->expected);
+}
+
+static const struct drm_connector_hdmi_init_max_tmds_rate_inferred_case
+drm_connector_hdmi_init_max_tmds_rate_inferred_tests[] = {
+ { "unknown", HDMI_VERSION_UNKNOWN, 0 },
+ { "1.0", HDMI_VERSION_1_0, HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ },
+ { "1.1", HDMI_VERSION_1_1, HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ },
+ { "1.2", HDMI_VERSION_1_2, HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ },
+ { "1.3", HDMI_VERSION_1_3, HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ },
+ { "1.4", HDMI_VERSION_1_4, HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ },
+ { "2.0", HDMI_VERSION_2_0, HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ },
+ { "2.1", HDMI_VERSION_2_1, HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ },
+ { "2.2", HDMI_VERSION_2_2, HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ },
+};
+
+static void drm_connector_hdmi_init_max_tmds_rate_inferred_desc(
+ const struct drm_connector_hdmi_init_max_tmds_rate_inferred_case *t,
+ char *desc)
+{
+ strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_max_tmds_rate_inferred,
+ drm_connector_hdmi_init_max_tmds_rate_inferred_tests,
+ drm_connector_hdmi_init_max_tmds_rate_inferred_desc);
+
+/*
+ * Test that the registration of an HDMI connector providing a max TMDS
+ * character rate strictly below the version-inferred limit succeeds, and
+ * the connector limit is overridden.
+ */
+static void drm_test_connector_hdmi_init_max_tmds_rate_override(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.supported_hdmi_ver = HDMI_VERSION_1_4;
+ priv->hdmi_funcs.supported_tmds_char_rate = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ &priv->hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, priv->connector.hdmi.max_tmds_char_rate,
+ HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ);
+}
+
+/*
+ * Test that the registration of an HDMI 2.0 connector capping its max TMDS
+ * character rate at the HDMI 1.3 limit succeeds, and the connector limit is
+ * overridden while the scrambling capability is still reported.
+ */
+static void drm_test_connector_hdmi_init_max_tmds_rate_override_hdmi20(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ priv->hdmi_funcs = dummy_hdmi_funcs_scrambler;
+ priv->hdmi_funcs.supported_tmds_char_rate = HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ &priv->hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, priv->connector.hdmi.max_tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+ KUNIT_EXPECT_TRUE(test, drm_connector_hdmi_scrambler_supported(&priv->connector));
+}
+
+/*
+ * Test that the registration of an HDMI connector providing a max TMDS
+ * character rate equal to the version-inferred limit succeeds.
+ */
+static void drm_test_connector_hdmi_init_max_tmds_rate_at_limit(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.supported_hdmi_ver = HDMI_VERSION_1_2;
+ priv->hdmi_funcs.supported_tmds_char_rate = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ &priv->hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, priv->connector.hdmi.max_tmds_char_rate,
+ HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ);
+}
+
+/*
+ * Test that the registration of an HDMI connector providing a max TMDS
+ * character rate that exceeds the limit inferred from the advertised HDMI
+ * specification version fails.
+ */
+static void drm_test_connector_hdmi_init_max_tmds_rate_off_limit(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.supported_hdmi_ver = HDMI_VERSION_1_2;
+ priv->hdmi_funcs.supported_tmds_char_rate = HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ &priv->hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+/*
+ * Test that the registration of an HDMI connector providing a non-zero max
+ * TMDS character rate without an HDMI specification version fails, as the
+ * version-inferred limit defaults to zero and any positive override would
+ * exceed it.
+ */
+static void drm_test_connector_hdmi_init_max_tmds_rate_no_version(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ priv->hdmi_funcs = dummy_hdmi_funcs;
+ priv->hdmi_funcs.supported_hdmi_ver = HDMI_VERSION_UNKNOWN;
+ priv->hdmi_funcs.supported_tmds_char_rate = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ &priv->hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
static struct kunit_case drmm_connector_hdmi_init_tests[] = {
KUNIT_CASE(drm_test_connector_hdmi_init_valid),
KUNIT_CASE(drm_test_connector_hdmi_init_bpc_8),
@@ -1254,6 +1454,13 @@ static struct kunit_case drmm_connector_hdmi_init_tests[] = {
drm_connector_hdmi_init_type_valid_gen_params),
KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_type_invalid,
drm_connector_hdmi_init_type_invalid_gen_params),
+ KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_max_tmds_rate_inferred,
+ drm_connector_hdmi_init_max_tmds_rate_inferred_gen_params),
+ KUNIT_CASE(drm_test_connector_hdmi_init_max_tmds_rate_override),
+ KUNIT_CASE(drm_test_connector_hdmi_init_max_tmds_rate_override_hdmi20),
+ KUNIT_CASE(drm_test_connector_hdmi_init_max_tmds_rate_at_limit),
+ KUNIT_CASE(drm_test_connector_hdmi_init_max_tmds_rate_off_limit),
+ KUNIT_CASE(drm_test_connector_hdmi_init_max_tmds_rate_no_version),
{ }
};
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 17/25] drm/tests: connector: Add HDMI source-side scrambler coverage
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (15 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 16/25] drm/tests: connector: Add HDMI max_tmds_char_rate init coverage Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 18/25] drm/tests: hdmi: Cover mode validation against connector max_tmds_char_rate Cristian Ciocaltea
` (7 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
Add KUnit tests for the HDMI source-side scrambling validation done by
the new drmm_connector_hdmi_init() helper.
Connector initialization derives scrambler support from the
driver-provided supported_hdmi_ver capability. Therefore, connectors
that advertise HDMI 2.0 or later must provide both .scrambler_enable()
and .scrambler_disable() callbacks. Registration is rejected if either
callback is missing. Conversely, connectors advertising an earlier
version are never reported as scrambling capable, even when providing
those callbacks.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_connector_test.c | 125 +++++++++++++++++++++++++++++
1 file changed, 125 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index 698706fca5db..1520e4f8a9ab 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -1429,6 +1429,126 @@ static void drm_test_connector_hdmi_init_max_tmds_rate_no_version(struct kunit *
KUNIT_EXPECT_LT(test, ret, 0);
}
+/*
+ * Test that the registration of an HDMI connector advertising HDMI 2.0 support
+ * succeeds when the .scrambler_{enable,disable} callbacks are provided.
+ */
+static void drm_test_connector_hdmi_init_scrambler_valid(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ &dummy_hdmi_funcs_scrambler,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, drm_connector_hdmi_scrambler_supported(&priv->connector));
+}
+
+/*
+ * Test that the registration of an HDMI connector not advertising HDMI 2.0
+ * support succeeds, and the connector is not reported as scrambling capable.
+ */
+static void drm_test_connector_hdmi_init_scrambler_unsupported(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ &dummy_hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_FALSE(test, drm_connector_hdmi_scrambler_supported(&priv->connector));
+}
+
+/*
+ * Test that the registration of an HDMI connector not advertising HDMI 2.0
+ * support, but providing the .scrambler_{enable,disable} callbacks anyway,
+ * succeeds and the connector is still not reported as scrambling capable.
+ */
+static void drm_test_connector_hdmi_init_scrambler_cb_ignored(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ priv->hdmi_funcs = dummy_hdmi_funcs_scrambler;
+ priv->hdmi_funcs.supported_hdmi_ver = HDMI_VERSION_1_4;
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ &priv->hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_FALSE(test, drm_connector_hdmi_scrambler_supported(&priv->connector));
+}
+
+enum drm_connector_hdmi_scrambler_cb {
+ SCRAMBLER_CALLBACK_ENABLE,
+ SCRAMBLER_CALLBACK_DISABLE,
+ SCRAMBLER_CALLBACK_ALL,
+};
+
+struct drm_connector_hdmi_init_scrambler_missing_cb_case {
+ const char *desc;
+ enum drm_connector_hdmi_scrambler_cb cb;
+};
+
+/*
+ * Test that the registration of an HDMI connector advertising HDMI 2.0 support
+ * fails when any single .scrambler_* callback is missing.
+ */
+static void drm_test_connector_hdmi_init_scrambler_missing_cb(struct kunit *test)
+{
+ const struct drm_connector_hdmi_init_scrambler_missing_cb_case *params = test->param_value;
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ priv->hdmi_funcs = dummy_hdmi_funcs_scrambler;
+
+ switch (params->cb) {
+ case SCRAMBLER_CALLBACK_ENABLE:
+ priv->hdmi_funcs.scrambler_enable = NULL;
+ break;
+ case SCRAMBLER_CALLBACK_DISABLE:
+ priv->hdmi_funcs.scrambler_disable = NULL;
+ break;
+ case SCRAMBLER_CALLBACK_ALL:
+ priv->hdmi_funcs.scrambler_enable = NULL;
+ priv->hdmi_funcs.scrambler_disable = NULL;
+ break;
+ }
+
+ ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ &priv->hdmi_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+static const struct drm_connector_hdmi_init_scrambler_missing_cb_case
+drm_connector_hdmi_init_scrambler_missing_cb_tests[] = {
+ { "scrambler_enable", SCRAMBLER_CALLBACK_ENABLE },
+ { "scrambler_disable", SCRAMBLER_CALLBACK_DISABLE },
+ { "scrambler_*", SCRAMBLER_CALLBACK_ALL },
+};
+
+static void
+drm_connector_hdmi_init_scrambler_missing_cb_desc(const struct drm_connector_hdmi_init_scrambler_missing_cb_case *t,
+ char *desc)
+{
+ strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_connector_hdmi_init_scrambler_missing_cb,
+ drm_connector_hdmi_init_scrambler_missing_cb_tests,
+ drm_connector_hdmi_init_scrambler_missing_cb_desc);
+
static struct kunit_case drmm_connector_hdmi_init_tests[] = {
KUNIT_CASE(drm_test_connector_hdmi_init_valid),
KUNIT_CASE(drm_test_connector_hdmi_init_bpc_8),
@@ -1461,6 +1581,11 @@ static struct kunit_case drmm_connector_hdmi_init_tests[] = {
KUNIT_CASE(drm_test_connector_hdmi_init_max_tmds_rate_at_limit),
KUNIT_CASE(drm_test_connector_hdmi_init_max_tmds_rate_off_limit),
KUNIT_CASE(drm_test_connector_hdmi_init_max_tmds_rate_no_version),
+ KUNIT_CASE(drm_test_connector_hdmi_init_scrambler_valid),
+ KUNIT_CASE(drm_test_connector_hdmi_init_scrambler_unsupported),
+ KUNIT_CASE(drm_test_connector_hdmi_init_scrambler_cb_ignored),
+ KUNIT_CASE_PARAM(drm_test_connector_hdmi_init_scrambler_missing_cb,
+ drm_connector_hdmi_init_scrambler_missing_cb_gen_params),
{ }
};
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 18/25] drm/tests: hdmi: Cover mode validation against connector max_tmds_char_rate
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (16 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 17/25] drm/tests: connector: Add HDMI source-side scrambler coverage Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 19/25] drm/tests: hdmi: Verify scrambler_needed computation Cristian Ciocaltea
` (6 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
Test the hdmi_clock_valid() logic that rejects modes whose TMDS
character rate exceeds connector->hdmi.max_tmds_char_rate.
Exercise both an explicit limit set through drm_connector_hdmi_funcs'
supported_tmds_char_rate and the limit inferred from supported_hdmi_ver.
Also verify that a driver hook can further restrict the connector limit.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 116 +++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
index f1bdd197429c..d94ec8680a1e 100644
--- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
@@ -190,6 +190,32 @@ static const struct drm_connector_hdmi_funcs reject_100mhz_connector_hdmi_funcs
},
};
+static enum drm_mode_status
+accept_any_connector_tmds_char_rate_valid(const struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ unsigned long long tmds_rate)
+{
+ return MODE_OK;
+}
+
+static const struct drm_connector_hdmi_funcs reject_over_165mhz_connector_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_1_3,
+ .supported_tmds_char_rate = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
+ .tmds_char_rate_valid = accept_any_connector_tmds_char_rate_valid,
+ .avi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+ .hdmi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+};
+
static int dummy_connector_get_modes(struct drm_connector *connector)
{
struct drm_atomic_helper_connector_hdmi_priv *priv =
@@ -2762,6 +2788,93 @@ static void drm_test_check_mode_valid_reject_max_clock(struct kunit *test)
KUNIT_EXPECT_EQ(test, preferred->clock, 25200);
}
+/*
+ * Test that the max_tmds_char_rate inferred from supported_hdmi_ver is applied
+ * as a mode-filtering limit: with an HDMI 1.3 connector and no driver hook,
+ * drm_hdmi_connector_mode_valid() will reject modes whose TMDS character rate
+ * exceeds 340 MHz, even though the sink itself advertises a higher limit
+ * (600 MHz).
+ */
+static void
+drm_test_check_mode_valid_reject_inferred_max_tmds_char_rate(struct kunit *test)
+{
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_funcs;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_display_mode *preferred;
+
+ hdmi_funcs.supported_hdmi_ver = HDMI_VERSION_1_3;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, &hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ KUNIT_ASSERT_EQ(test, priv->connector.hdmi.max_tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+
+ preferred = find_preferred_mode(&priv->connector);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_EXPECT_EQ(test, preferred->hdisplay, 2560);
+ KUNIT_EXPECT_EQ(test, preferred->vdisplay, 1600);
+ KUNIT_EXPECT_EQ(test, preferred->clock, 268500);
+}
+
+/*
+ * Test that a connector-provided max_tmds_char_rate limit (165 MHz) takes
+ * precedence over a permissive .tmds_char_rate_valid() driver hook and
+ * drm_hdmi_connector_mode_valid() will reject modes whose computed TMDS
+ * character rate exceeds the connector limit.
+ */
+static void
+drm_test_check_mode_valid_connector_max_tmds_char_rate_precedes_driver_hook(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_display_mode *preferred;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &reject_over_165mhz_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ KUNIT_ASSERT_EQ(test, priv->connector.hdmi.max_tmds_char_rate,
+ HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ);
+
+ preferred = find_preferred_mode(&priv->connector);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_EXPECT_EQ(test, preferred->hdisplay, 2048);
+ KUNIT_EXPECT_EQ(test, preferred->vdisplay, 1152);
+ KUNIT_EXPECT_EQ(test, preferred->clock, 162000);
+}
+
+/*
+ * Test that a connector-provided max_tmds_char_rate (165 MHz) can be further
+ * limited by a more restrictive (100 MHz) .tmds_char_rate_valid() driver hook
+ * and drm_hdmi_connector_mode_valid() will reject modes whose computed TMDS
+ * character rate exceeds the hook limit.
+ */
+static void
+drm_test_check_mode_valid_driver_hook_limits_connector_max_tmds_char_rate(struct kunit *test)
+{
+ struct drm_connector_hdmi_funcs hdmi_funcs = reject_100mhz_connector_hdmi_funcs;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_display_mode *preferred;
+
+ hdmi_funcs.supported_hdmi_ver = HDMI_VERSION_1_3;
+ hdmi_funcs.supported_tmds_char_rate = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, &hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ KUNIT_ASSERT_EQ(test, priv->connector.hdmi.max_tmds_char_rate,
+ HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ);
+
+ preferred = find_preferred_mode(&priv->connector);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_EXPECT_EQ(test, preferred->hdisplay, 1440);
+ KUNIT_EXPECT_EQ(test, preferred->vdisplay, 900);
+ KUNIT_EXPECT_EQ(test, preferred->clock, 88750);
+}
+
/*
* Test that drm_hdmi_connector_mode_valid() will accept modes that require a
* 4:2:0 chroma subsampling, even if said mode would violate maximum clock
@@ -2852,6 +2965,9 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_mode_valid_tests[] = {
KUNIT_CASE(drm_test_check_mode_valid_reject),
KUNIT_CASE(drm_test_check_mode_valid_reject_rate),
KUNIT_CASE(drm_test_check_mode_valid_reject_max_clock),
+ KUNIT_CASE(drm_test_check_mode_valid_reject_inferred_max_tmds_char_rate),
+ KUNIT_CASE(drm_test_check_mode_valid_connector_max_tmds_char_rate_precedes_driver_hook),
+ KUNIT_CASE(drm_test_check_mode_valid_driver_hook_limits_connector_max_tmds_char_rate),
KUNIT_CASE(drm_test_check_mode_valid_yuv420_only_max_clock),
KUNIT_CASE(drm_test_check_mode_valid_reject_yuv420_only_connector),
KUNIT_CASE(drm_test_check_mode_valid_accept_yuv420_also_connector_rgb),
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 19/25] drm/tests: hdmi: Verify scrambler_needed computation
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (17 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 18/25] drm/tests: hdmi: Cover mode validation against connector max_tmds_char_rate Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 20/25] drm/tests: hdmi: Add SPD InfoFrame vendor/product coverage Cristian Ciocaltea
` (5 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
Scrambling is only required when the TMDS character rate exceeds 340
MHz, so the decision must be based on that rate rather than on the mode
clock. Add atomic-check tests on an HDMI 2.0 connector, for which the
core infers scrambler support and a 600 MHz TMDS limit, covering:
- 1080p@60 CEA (148.5 MHz TMDS): scrambler_needed clear
- 4K@60 RGB preferred mode (594 MHz TMDS): scrambler_needed set
- 4K@60 YUV420 (297 MHz TMDS): scrambler_needed clear
- 4K@30 RGB at 10 bpc (371.25 MHz TMDS): scrambler_needed set
Also check that scrambler_needed is cleared when switching from a
high-rate to a low-rate mode, instead of being carried over from the
previous state.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 390 +++++++++++++++++++++
1 file changed, 390 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
index d94ec8680a1e..b98060b73742 100644
--- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
@@ -216,6 +216,34 @@ static const struct drm_connector_hdmi_funcs reject_over_165mhz_connector_hdmi_f
},
};
+static int accept_scrambler_enable(struct drm_connector *connector)
+{
+ return 0;
+}
+
+static int accept_scrambler_disable(struct drm_connector *connector)
+{
+ return 0;
+}
+
+static const struct drm_connector_hdmi_funcs scrambler_connector_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_2_0,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
+ .scrambler_enable = accept_scrambler_enable,
+ .scrambler_disable = accept_scrambler_disable,
+ .avi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+ .hdmi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+};
+
static int dummy_connector_get_modes(struct drm_connector *connector)
{
struct drm_atomic_helper_connector_hdmi_priv *priv =
@@ -2263,6 +2291,363 @@ static void drm_test_check_disable_connector(struct kunit *test)
drm_modeset_acquire_fini(&ctx);
}
+/*
+ * Test that on an HDMI connector with source+sink scrambling support, driving
+ * a sub-340 MHz CEA mode, we end up with scrambler_needed cleared.
+ */
+static void drm_test_check_scrambler_needed_low_rate(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_connector_state *conn_state;
+ struct drm_display_info *info;
+ struct drm_display_mode *low_rate_mode;
+ struct drm_connector *conn;
+ struct drm_device *drm;
+ struct drm_crtc *crtc;
+ unsigned long long rate;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &scrambler_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ conn = &priv->connector;
+ info = &conn->display_info;
+ KUNIT_ASSERT_TRUE(test, drm_connector_hdmi_scrambler_supported(conn));
+ KUNIT_ASSERT_TRUE(test, info->is_hdmi);
+ KUNIT_ASSERT_TRUE(test, info->hdmi.scdc.supported);
+ KUNIT_ASSERT_TRUE(test, info->hdmi.scdc.scrambling.supported);
+
+ low_rate_mode = drm_kunit_display_mode_from_cea_vic(test, drm, 16);
+ KUNIT_ASSERT_NOT_NULL(test, low_rate_mode);
+
+ rate = drm_hdmi_compute_mode_clock(low_rate_mode, 8,
+ DRM_OUTPUT_COLOR_FORMAT_RGB444);
+ KUNIT_ASSERT_LT(test, rate, HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+retry_conn_enable:
+ ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn,
+ low_rate_mode, &ctx);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_conn_enable;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ conn_state = conn->state;
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ KUNIT_EXPECT_LE(test, conn_state->hdmi.tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+ KUNIT_EXPECT_FALSE(test, conn_state->hdmi.scrambler_needed);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+/*
+ * Test that on an HDMI connector with source+sink scrambling support, driving
+ * the 4K@60 RGB preferred mode (594 MHz TMDS, above the 340 MHz threshold),
+ * we end up with scrambler_needed set.
+ */
+static void drm_test_check_scrambler_needed_high_rate(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_connector_state *conn_state;
+ struct drm_display_info *info;
+ struct drm_display_mode *preferred;
+ struct drm_connector *conn;
+ struct drm_device *drm;
+ struct drm_crtc *crtc;
+ unsigned long long rate;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &scrambler_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ conn = &priv->connector;
+ info = &conn->display_info;
+ KUNIT_ASSERT_TRUE(test, drm_connector_hdmi_scrambler_supported(conn));
+ KUNIT_ASSERT_TRUE(test, info->is_hdmi);
+ KUNIT_ASSERT_TRUE(test, info->hdmi.scdc.supported);
+ KUNIT_ASSERT_TRUE(test, info->hdmi.scdc.scrambling.supported);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+
+ rate = drm_hdmi_compute_mode_clock(preferred, 8,
+ DRM_OUTPUT_COLOR_FORMAT_RGB444);
+ KUNIT_ASSERT_GT(test, rate, HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+retry_conn_enable:
+ ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn,
+ preferred, &ctx);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_conn_enable;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ conn_state = conn->state;
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ KUNIT_EXPECT_GT(test, conn_state->hdmi.tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+ KUNIT_EXPECT_TRUE(test, conn_state->hdmi.scrambler_needed);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+/*
+ * Test that on an HDMI connector with source+sink scrambling support, driving
+ * the 4K@60 preferred mode with a YUV420 output format (297 MHz TMDS), we end
+ * up with scrambler_needed cleared.
+ */
+static void drm_test_check_scrambler_needed_yuv420(struct kunit *test)
+{
+ struct drm_connector_hdmi_funcs hdmi_funcs = scrambler_connector_hdmi_funcs;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_connector_state *conn_state;
+ struct drm_atomic_commit *state;
+ struct drm_display_info *info;
+ struct drm_display_mode *preferred;
+ struct drm_connector *conn;
+ struct drm_device *drm;
+ struct drm_crtc *crtc;
+ int ret;
+
+ hdmi_funcs.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ conn = &priv->connector;
+ info = &conn->display_info;
+ KUNIT_ASSERT_TRUE(test, drm_connector_hdmi_scrambler_supported(conn));
+ KUNIT_ASSERT_TRUE(test, info->is_hdmi);
+ KUNIT_ASSERT_TRUE(test, info->hdmi.scdc.supported);
+ KUNIT_ASSERT_TRUE(test, info->hdmi.scdc.scrambling.supported);
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+ KUNIT_ASSERT_TRUE(test, drm_mode_is_420_also(&conn->display_info, preferred));
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+retry_conn_enable:
+ ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn,
+ preferred, &ctx);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_conn_enable;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ conn_state = conn->state;
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format,
+ DRM_OUTPUT_COLOR_FORMAT_RGB444);
+ KUNIT_EXPECT_GT(test, conn_state->hdmi.tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+ KUNIT_EXPECT_TRUE(test, conn_state->hdmi.scrambler_needed);
+
+ state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+retry_conn_state:
+ conn_state = drm_atomic_get_connector_state(state, conn);
+ if (PTR_ERR(conn_state) == -EDEADLK) {
+ drm_atomic_commit_clear(state);
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_conn_state;
+ }
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ conn_state->color_format = DRM_CONNECTOR_COLOR_FORMAT_YCBCR420;
+
+ ret = drm_atomic_commit(state);
+ if (ret == -EDEADLK) {
+ drm_atomic_commit_clear(state);
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_conn_state;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ conn_state = conn->state;
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format,
+ DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
+ KUNIT_EXPECT_LE(test, conn_state->hdmi.tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+ KUNIT_EXPECT_FALSE(test, conn_state->hdmi.scrambler_needed);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+/*
+ * Test that on an HDMI connector with source+sink scrambling support, driving
+ * a 4K@30 mode (297 MHz TMDS at 8 bpc) with 10 bpc RGB (371.25 MHz TMDS), we
+ * end up with scrambler_needed set, i.e. the decision accounts for the bpc.
+ */
+static void drm_test_check_scrambler_needed_deep_color(struct kunit *test)
+{
+ struct drm_connector_hdmi_funcs hdmi_funcs = scrambler_connector_hdmi_funcs;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_connector_state *conn_state;
+ struct drm_display_mode *mode;
+ struct drm_connector *conn;
+ struct drm_device *drm;
+ struct drm_crtc *crtc;
+ unsigned long long rate;
+ int ret;
+
+ hdmi_funcs.max_bpc = 10;
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ conn = &priv->connector;
+
+ mode = drm_kunit_display_mode_from_cea_vic(test, drm, 95);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ rate = drm_hdmi_compute_mode_clock(mode, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444);
+ KUNIT_ASSERT_LE(test, rate, HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+
+ rate = drm_hdmi_compute_mode_clock(mode, 10, DRM_OUTPUT_COLOR_FORMAT_RGB444);
+ KUNIT_ASSERT_GT(test, rate, HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+retry_conn_enable:
+ ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn,
+ mode, &ctx);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_conn_enable;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ conn_state = conn->state;
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10);
+ KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format,
+ DRM_OUTPUT_COLOR_FORMAT_RGB444);
+ KUNIT_EXPECT_GT(test, conn_state->hdmi.tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+ KUNIT_EXPECT_TRUE(test, conn_state->hdmi.scrambler_needed);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+/*
+ * Test that on an HDMI connector with source+sink scrambling support,
+ * switching from the 4K@60 RGB preferred mode (594 MHz TMDS) to a 1080p@60
+ * CEA mode (148.5 MHz TMDS), we end up with scrambler_needed cleared.
+ */
+static void drm_test_check_scrambler_needed_cleared(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_display_mode *low_rate_mode;
+ struct drm_display_mode *preferred;
+ struct drm_connector *conn;
+ struct drm_device *drm;
+ struct drm_crtc *crtc;
+ unsigned long long rate;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &scrambler_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ conn = &priv->connector;
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+
+ rate = drm_hdmi_compute_mode_clock(preferred, 8,
+ DRM_OUTPUT_COLOR_FORMAT_RGB444);
+ KUNIT_ASSERT_GT(test, rate, HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+
+ low_rate_mode = drm_kunit_display_mode_from_cea_vic(test, drm, 16);
+ KUNIT_ASSERT_NOT_NULL(test, low_rate_mode);
+
+ rate = drm_hdmi_compute_mode_clock(low_rate_mode, 8,
+ DRM_OUTPUT_COLOR_FORMAT_RGB444);
+ KUNIT_ASSERT_LE(test, rate, HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+retry_conn_enable:
+ ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn,
+ preferred, &ctx);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_conn_enable;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_GT(test, conn->state->hdmi.tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+ KUNIT_ASSERT_TRUE(test, conn->state->hdmi.scrambler_needed);
+
+retry_conn_low_rate:
+ ret = drm_kunit_helper_enable_crtc_connector(test, drm, crtc, conn,
+ low_rate_mode, &ctx);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_conn_low_rate;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_LE(test, conn->state->hdmi.tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+ KUNIT_EXPECT_FALSE(test, conn->state->hdmi.scrambler_needed);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
struct color_format_test_param {
enum drm_connector_color_format fmt;
enum drm_output_color_format expected;
@@ -2505,6 +2890,11 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_8bpc),
KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_10bpc),
KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_12bpc),
+ KUNIT_CASE(drm_test_check_scrambler_needed_low_rate),
+ KUNIT_CASE(drm_test_check_scrambler_needed_high_rate),
+ KUNIT_CASE(drm_test_check_scrambler_needed_yuv420),
+ KUNIT_CASE(drm_test_check_scrambler_needed_deep_color),
+ KUNIT_CASE(drm_test_check_scrambler_needed_cleared),
KUNIT_CASE_PARAM(drm_test_check_hdmi_color_format,
check_hdmi_color_format_gen_params),
KUNIT_CASE_PARAM(drm_test_check_hdmi_color_format_420_only,
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 20/25] drm/tests: hdmi: Add SPD InfoFrame vendor/product coverage
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (18 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 19/25] drm/tests: hdmi: Verify scrambler_needed computation Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 21/25] drm/tests: hdmi: Add HDMI 2.0 scrambling helpers coverage Cristian Ciocaltea
` (4 subsequent siblings)
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
The vendor and product strings provided through struct
drm_connector_hdmi_funcs end up in the SPD InfoFrame, whose fields are
defined by the HDMI specification as fixed-size: 8 bytes for the vendor
name and 16 bytes for the product description, padded with zeros and
left without any trailing NUL when a name spans the whole field.
Add a connector variant implementing the SPD InfoFrame hooks, along with
parametrized tests covering both the padded and the exact length cases.
Check the names both in the connector state and in the packed InfoFrame
passed to the .write_infoframe() hook, which is captured for that
purpose.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 167 ++++++++++++++++++++-
1 file changed, 166 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
index b98060b73742..294be8f4a307 100644
--- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
@@ -38,6 +38,9 @@ struct drm_atomic_helper_connector_hdmi_priv {
size_t current_edid_len;
int hdmi_update_failures;
+
+ u8 spd_infoframe[HDMI_INFOFRAME_SIZE(SPD)];
+ size_t spd_infoframe_len;
};
#define connector_to_priv(c) \
@@ -3444,6 +3447,167 @@ static void drm_test_check_infoframes(struct kunit *test)
drm_modeset_acquire_fini(&ctx);
}
+struct drm_hdmi_spd_infoframe_test {
+ const char *name;
+ const char *vendor;
+ const char *product;
+ char expected_vendor[HDMI_SPD_INFOFRAME_VENDOR_LEN];
+ char expected_product[HDMI_SPD_INFOFRAME_PRODUCT_LEN];
+};
+
+static const struct drm_hdmi_spd_infoframe_test drm_hdmi_spd_infoframe_tests[] = {
+ {
+ .name = "shorter-than-field",
+ .vendor = "Vend",
+ .product = "Prod",
+ .expected_vendor = { 'V', 'e', 'n', 'd' },
+ .expected_product = { 'P', 'r', 'o', 'd' },
+ },
+ {
+ .name = "whole-field",
+ .vendor = "VendorVe",
+ .product = "ProductProductPr",
+ .expected_vendor = {
+ 'V', 'e', 'n', 'd', 'o', 'r', 'V', 'e',
+ },
+ .expected_product = {
+ 'P', 'r', 'o', 'd', 'u', 'c', 't',
+ 'P', 'r', 'o', 'd', 'u', 'c', 't',
+ 'P', 'r',
+ },
+ },
+};
+
+static void drm_hdmi_spd_infoframe_desc(const struct drm_hdmi_spd_infoframe_test *t,
+ char *desc)
+{
+ strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_hdmi_spd_infoframe, drm_hdmi_spd_infoframe_tests,
+ drm_hdmi_spd_infoframe_desc);
+
+static int capture_spd_write_infoframe(struct drm_connector *connector,
+ const u8 *buffer, size_t len)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv =
+ connector_to_priv(connector);
+
+ if (len > sizeof(priv->spd_infoframe))
+ return -EINVAL;
+
+ memcpy(priv->spd_infoframe, buffer, len);
+ priv->spd_infoframe_len = len;
+
+ return 0;
+}
+
+/*
+ * The SPD InfoFrame is only generated for connectors implementing the
+ * related hooks, hence the SPD tests cannot rely on the plain dummy funcs.
+ */
+static const struct drm_connector_hdmi_funcs dummy_connector_hdmi_spd_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_1_4,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
+ .avi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+ .hdmi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+ .spd = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = capture_spd_write_infoframe,
+ },
+};
+
+/*
+ * Test that the vendor and product names end up in the SPD InfoFrame padded
+ * with zeros, and without any trailing NUL when spanning the whole field,
+ * both in the connector state and in the packed buffer handed over to the
+ * driver.
+ */
+static void drm_test_check_spd_infoframe(struct kunit *test)
+{
+ const struct drm_hdmi_spd_infoframe_test *param = test->param_value;
+ struct drm_connector_hdmi_funcs hdmi_funcs = dummy_connector_hdmi_spd_funcs;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_connector_state *conn_state;
+ struct drm_display_mode *preferred;
+ struct hdmi_spd_infoframe *frame;
+ struct drm_connector *conn;
+ struct drm_device *drm;
+ struct drm_crtc *crtc;
+ const u8 *payload;
+ int ret;
+
+ hdmi_funcs.vendor = param->vendor;
+ hdmi_funcs.product = param->product;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &hdmi_funcs,
+ test_edid_hdmi_1080p_rgb_max_200mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ drm = &priv->drm;
+ crtc = priv->crtc;
+ conn = &priv->connector;
+
+ preferred = find_preferred_mode(conn);
+ KUNIT_ASSERT_NOT_NULL(test, preferred);
+
+ drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+retry_conn_enable:
+ ret = drm_kunit_helper_enable_crtc_connector(test, drm,
+ crtc, conn,
+ preferred,
+ &ctx);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_conn_enable;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_ASSERT_EQ(test, priv->hdmi_update_failures, 0);
+
+ conn_state = conn->state;
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+ KUNIT_ASSERT_TRUE(test, conn_state->hdmi.infoframes.spd.set);
+
+ frame = &conn_state->hdmi.infoframes.spd.data.spd;
+
+ KUNIT_EXPECT_MEMEQ(test, frame->vendor, param->expected_vendor,
+ sizeof(frame->vendor));
+ KUNIT_EXPECT_MEMEQ(test, frame->product, param->expected_product,
+ sizeof(frame->product));
+
+ KUNIT_ASSERT_EQ(test, priv->spd_infoframe_len, HDMI_INFOFRAME_SIZE(SPD));
+ KUNIT_EXPECT_EQ(test, priv->spd_infoframe[0], HDMI_INFOFRAME_TYPE_SPD);
+
+ payload = priv->spd_infoframe + HDMI_INFOFRAME_HEADER_SIZE;
+ KUNIT_EXPECT_MEMEQ(test, payload, param->expected_vendor,
+ HDMI_SPD_INFOFRAME_VENDOR_LEN);
+ KUNIT_EXPECT_MEMEQ(test, payload + HDMI_SPD_INFOFRAME_VENDOR_LEN,
+ param->expected_product,
+ HDMI_SPD_INFOFRAME_PRODUCT_LEN);
+ KUNIT_EXPECT_EQ(test,
+ payload[HDMI_SPD_INFOFRAME_VENDOR_LEN +
+ HDMI_SPD_INFOFRAME_PRODUCT_LEN],
+ HDMI_SPD_SDI_PC);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
static int reject_infoframe_write_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
@@ -3917,9 +4081,10 @@ static void drm_test_check_reject_audio_infoframe(struct kunit *test)
drm_modeset_acquire_fini(&ctx);
}
-
static struct kunit_case drm_atomic_helper_connector_hdmi_infoframes_tests[] = {
KUNIT_CASE(drm_test_check_infoframes),
+ KUNIT_CASE_PARAM(drm_test_check_spd_infoframe,
+ drm_hdmi_spd_infoframe_gen_params),
KUNIT_CASE(drm_test_check_reject_avi_infoframe),
KUNIT_CASE(drm_test_check_reject_hdr_infoframe_bpc_8),
KUNIT_CASE(drm_test_check_reject_hdr_infoframe_bpc_10),
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 21/25] drm/tests: hdmi: Add HDMI 2.0 scrambling helpers coverage
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (19 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 20/25] drm/tests: hdmi: Add SPD InfoFrame vendor/product coverage Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:21 ` sashiko-bot
2026-11-14 18:03 ` [PATCH 22/25] drm/tests: hdmi: Verify SCDC resync on hotplug Cristian Ciocaltea
` (3 subsequent siblings)
24 siblings, 1 reply; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
In order to provide test coverage for the scrambling helpers, i.e.
drm_hdmi_mode_needs_scrambling() and
drm_connector_hdmi_{enable,disable}_scrambling(), as well as the SCDC
status monitoring work, provide a fake DDC adapter modelling the SCDC
register map of a sink, and let __connector_hdmi_init() optionally hook
it up to the connector, so that the sink side of the scrambling setup
can be verified.
Add tests to make sure that:
- drm_hdmi_mode_needs_scrambling() accounts for the mode, bpc and output
format when comparing the TMDS character rate against 340 MHz
- enabling scrambling configures both the sink and the source, and
advertises the source SCDC version, while disabling reverts that
- enabling scrambling for a mode not needing it clears any stale
scrambling setup on the sink side, without involving the source
- enabling scrambling fails without touching the sink when the source or
the sink lack support, or when there is no DDC adapter
- DDC and source-side failures are reported and rolled back
- disabling scrambling is a no-op unless enabled, and copes with
scrambler_enabled being forced by the driver
- the monitoring work restores the sink setup when lost, stops once the
sink reports scrambling as active, and never touches the DDC bus after
the connector got unregistered
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 700 ++++++++++++++++++++-
1 file changed, 697 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
index 294be8f4a307..46654d8fa45c 100644
--- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
@@ -4,6 +4,9 @@
* Kunit test for drm_hdmi_state_helper functions
*/
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_atomic_uapi.h>
@@ -19,6 +22,7 @@
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
+#include <drm/display/drm_scdc_helper.h>
#include "../drm_crtc_internal.h"
@@ -41,6 +45,16 @@ struct drm_atomic_helper_connector_hdmi_priv {
u8 spd_infoframe[HDMI_INFOFRAME_SIZE(SPD)];
size_t spd_infoframe_len;
+
+ /* Fake DDC adapter exposing an SCDC register map */
+ struct i2c_adapter ddc;
+ u8 scdc_regs[256];
+ unsigned int scdc_xfer_count;
+ int scdc_xfer_err;
+
+ unsigned int scrambler_enable_count;
+ unsigned int scrambler_disable_count;
+ int scrambler_enable_ret;
};
#define connector_to_priv(c) \
@@ -310,13 +324,87 @@ static const struct drm_encoder_helper_funcs test_encoder_helper_funcs = {
.atomic_enable = test_encoder_atomic_enable,
};
+#define TEST_SCDC_I2C_ADDR 0x54
+
+/*
+ * Minimal SCDC sink model: register reads are expected as a one byte offset
+ * write followed by a read, and register writes as a single message carrying
+ * the offset followed by the data, matching drm_scdc_{read,write}().
+ */
+static int fake_scdc_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv =
+ i2c_get_adapdata(adapter);
+ unsigned int offset;
+
+ priv->scdc_xfer_count++;
+
+ if (priv->scdc_xfer_err)
+ return priv->scdc_xfer_err;
+
+ if (msgs[0].addr != TEST_SCDC_I2C_ADDR || msgs[0].flags & I2C_M_RD ||
+ !msgs[0].len)
+ return -ENXIO;
+
+ offset = msgs[0].buf[0];
+
+ if (num == 2 && msgs[0].len == 1 &&
+ msgs[1].addr == TEST_SCDC_I2C_ADDR && msgs[1].flags & I2C_M_RD) {
+ if (offset + msgs[1].len > sizeof(priv->scdc_regs))
+ return -EINVAL;
+
+ memcpy(msgs[1].buf, &priv->scdc_regs[offset], msgs[1].len);
+ return num;
+ }
+
+ if (num == 1) {
+ if (offset + msgs[0].len - 1 > sizeof(priv->scdc_regs))
+ return -EINVAL;
+
+ memcpy(&priv->scdc_regs[offset], &msgs[0].buf[1], msgs[0].len - 1);
+ return num;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static u32 fake_scdc_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm fake_scdc_algorithm = {
+ .master_xfer = fake_scdc_xfer,
+ .functionality = fake_scdc_func,
+};
+
+static void i2c_del_adapter_wrapper(void *ptr)
+{
+ i2c_del_adapter(ptr);
+}
+
+/*
+ * The SCDC monitoring work may still be pending when a test completes, make
+ * sure it is gone before the DDC adapter gets removed.
+ */
+static void cancel_scdc_work_wrapper(void *ptr)
+{
+ struct drm_connector *connector = ptr;
+
+ if (connector->hdmi.scdc_work_initialized)
+ cancel_delayed_work_sync(&connector->hdmi.scdc_work);
+}
+
static
struct drm_atomic_helper_connector_hdmi_priv *
__connector_hdmi_init(struct kunit *test,
const struct drm_connector_hdmi_funcs *hdmi_funcs,
- const void *edid_data, size_t edid_len)
+ const void *edid_data, size_t edid_len,
+ bool with_scdc)
{
struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct i2c_adapter *ddc = NULL;
struct drm_connector *conn;
struct drm_encoder *enc;
struct drm_device *drm;
@@ -356,14 +444,37 @@ __connector_hdmi_init(struct kunit *test,
conn->ycbcr_420_allowed = !!(hdmi_funcs->supported_formats &
BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420));
+ if (with_scdc) {
+ strscpy(priv->ddc.name, "fake-scdc-ddc", sizeof(priv->ddc.name));
+ priv->ddc.owner = THIS_MODULE;
+ priv->ddc.algo = &fake_scdc_algorithm;
+ priv->ddc.dev.parent = dev;
+ i2c_set_adapdata(&priv->ddc, priv);
+
+ ret = i2c_add_adapter(&priv->ddc);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = kunit_add_action_or_reset(test, i2c_del_adapter_wrapper,
+ &priv->ddc);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ddc = &priv->ddc;
+ }
+
priv->hdmi_funcs = *hdmi_funcs;
ret = drmm_connector_hdmi_init(drm, conn,
&dummy_connector_funcs,
&priv->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- NULL);
+ ddc);
KUNIT_ASSERT_EQ(test, ret, 0);
+ if (with_scdc) {
+ ret = kunit_add_action_or_reset(test, cancel_scdc_work_wrapper,
+ conn);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ }
+
drm_connector_helper_add(conn, &dummy_connector_helper_funcs);
drm_connector_attach_encoder(conn, enc);
@@ -378,7 +489,10 @@ __connector_hdmi_init(struct kunit *test,
}
#define drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, funcs, edid) \
- __connector_hdmi_init(test, funcs, edid, ARRAY_SIZE(edid))
+ __connector_hdmi_init(test, funcs, edid, ARRAY_SIZE(edid), false)
+
+#define drm_kunit_helper_connector_hdmi_init_with_scdc(test, funcs, edid) \
+ __connector_hdmi_init(test, funcs, edid, ARRAY_SIZE(edid), true)
static
struct drm_atomic_helper_connector_hdmi_priv *
@@ -4097,13 +4211,593 @@ static struct kunit_suite drm_atomic_helper_connector_hdmi_infoframes_test_suite
.test_cases = drm_atomic_helper_connector_hdmi_infoframes_tests,
};
+struct drm_hdmi_mode_needs_scrambling_test {
+ const char *desc;
+ u8 vic;
+ unsigned int bpc;
+ enum drm_output_color_format fmt;
+ bool expected;
+};
+
+static const struct drm_hdmi_mode_needs_scrambling_test
+drm_hdmi_mode_needs_scrambling_params[] = {
+ { "1080p60 RGB 8bpc", 16, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444, false },
+ { "2160p30 RGB 8bpc", 95, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444, false },
+ { "2160p30 RGB 10bpc", 95, 10, DRM_OUTPUT_COLOR_FORMAT_RGB444, true },
+ { "2160p30 YUV422 12bpc", 95, 12, DRM_OUTPUT_COLOR_FORMAT_YCBCR422, false },
+ { "2160p60 RGB 8bpc", 97, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444, true },
+ { "2160p60 YUV420 8bpc", 97, 8, DRM_OUTPUT_COLOR_FORMAT_YCBCR420, false },
+ { "2160p60 YUV420 12bpc", 97, 12, DRM_OUTPUT_COLOR_FORMAT_YCBCR420, true },
+};
+
+static void
+drm_hdmi_mode_needs_scrambling_desc(const struct drm_hdmi_mode_needs_scrambling_test *t,
+ char *desc)
+{
+ strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_hdmi_mode_needs_scrambling, drm_hdmi_mode_needs_scrambling_params,
+ drm_hdmi_mode_needs_scrambling_desc);
+
+/*
+ * Test that drm_hdmi_mode_needs_scrambling() requires scrambling only when the
+ * TMDS character rate resulting from the mode, bpc and output format exceeds
+ * 340 MHz.
+ */
+static void drm_test_hdmi_mode_needs_scrambling(struct kunit *test)
+{
+ const struct drm_hdmi_mode_needs_scrambling_test *params = test->param_value;
+ struct drm_display_mode *mode;
+ struct drm_device *drm;
+ struct device *dev;
+
+ dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0,
+ DRIVER_MODESET);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+
+ mode = drm_kunit_display_mode_from_cea_vic(test, drm, params->vic);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ KUNIT_EXPECT_EQ(test,
+ drm_hdmi_mode_needs_scrambling(mode, params->bpc, params->fmt),
+ params->expected);
+}
+
+static int count_scrambler_enable(struct drm_connector *connector)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv =
+ connector_to_priv(connector);
+
+ priv->scrambler_enable_count++;
+
+ return priv->scrambler_enable_ret;
+}
+
+static int count_scrambler_disable(struct drm_connector *connector)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv =
+ connector_to_priv(connector);
+
+ priv->scrambler_disable_count++;
+
+ return 0;
+}
+
+static const struct drm_connector_hdmi_funcs scdc_connector_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_2_0,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
+ .scrambler_enable = count_scrambler_enable,
+ .scrambler_disable = count_scrambler_disable,
+ .avi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+ .hdmi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+};
+
+#define SCDC_TMDS_CONFIG_SCRAMBLED \
+ (SCDC_SCRAMBLING_ENABLE | SCDC_TMDS_BIT_CLOCK_RATIO_BY_40)
+
+/*
+ * drm_connector_hdmi_enable_scrambling() arms the SCDC monitoring work with
+ * a delay. Cancel it, so that the test alone controls when the work runs,
+ * whether or not it has already done so. As the fake sink never reports
+ * scrambling as active, a past run leaves the sink setup unchanged.
+ */
+static void scdc_test_stop_work(struct drm_connector *connector)
+{
+ cancel_delayed_work_sync(&connector->hdmi.scdc_work);
+}
+
+static void scdc_test_run_work(struct drm_connector *connector)
+{
+ struct work_struct *work = &connector->hdmi.scdc_work.work;
+
+ work->func(work);
+}
+
+/*
+ * Test that enabling scrambling configures both the sink, through SCDC, and
+ * the source, advertises the source SCDC version and starts monitoring the
+ * sink status, while disabling it reverts all of that.
+ */
+static void drm_test_hdmi_scrambling_enable_disable(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test,
+ &scdc_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ conn_state->hdmi.scrambler_needed = true;
+
+ priv->scdc_regs[SCDC_SINK_VERSION] = 1;
+
+ ret = drm_connector_hdmi_enable_scrambling(conn, conn_state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, priv->scdc_regs[SCDC_SOURCE_VERSION], 1);
+ KUNIT_EXPECT_EQ(test, priv->scdc_regs[SCDC_TMDS_CONFIG],
+ SCDC_TMDS_CONFIG_SCRAMBLED);
+ KUNIT_EXPECT_EQ(test, priv->scrambler_enable_count, 1);
+ KUNIT_EXPECT_EQ(test, priv->scrambler_disable_count, 0);
+ KUNIT_EXPECT_TRUE(test, conn->hdmi.scrambler_enabled);
+ /* The work may be executing already, i.e. not pending */
+ KUNIT_EXPECT_TRUE(test, work_busy(&conn->hdmi.scdc_work.work));
+
+ ret = drm_connector_hdmi_disable_scrambling(conn);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, priv->scdc_regs[SCDC_TMDS_CONFIG], 0);
+ KUNIT_EXPECT_EQ(test, priv->scrambler_enable_count, 1);
+ KUNIT_EXPECT_EQ(test, priv->scrambler_disable_count, 1);
+ KUNIT_EXPECT_FALSE(test, conn->hdmi.scrambler_enabled);
+ KUNIT_EXPECT_FALSE(test, delayed_work_pending(&conn->hdmi.scdc_work));
+}
+
+/*
+ * Test that enabling scrambling for a mode that doesn't need it leaves the
+ * source alone, but clears any stale scrambling setup on the sink side.
+ */
+static void drm_test_hdmi_scrambling_enable_not_needed(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test,
+ &scdc_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ conn_state->hdmi.scrambler_needed = false;
+
+ priv->scdc_regs[SCDC_TMDS_CONFIG] = SCDC_TMDS_CONFIG_SCRAMBLED;
+
+ ret = drm_connector_hdmi_enable_scrambling(conn, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, priv->scdc_regs[SCDC_TMDS_CONFIG], 0);
+ KUNIT_EXPECT_EQ(test, priv->scrambler_enable_count, 0);
+ KUNIT_EXPECT_FALSE(test, conn->hdmi.scrambler_enabled);
+ KUNIT_EXPECT_FALSE(test, conn->hdmi.scdc_work_initialized);
+}
+
+/*
+ * Test that enabling scrambling fails without touching the sink when the
+ * source doesn't support it.
+ */
+static void drm_test_hdmi_scrambling_enable_source_unsupported(struct kunit *test)
+{
+ struct drm_connector_hdmi_funcs hdmi_funcs = scdc_connector_hdmi_funcs;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ int ret;
+
+ hdmi_funcs.supported_hdmi_ver = HDMI_VERSION_1_4;
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test, &hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ conn_state->hdmi.scrambler_needed = true;
+
+ ret = drm_connector_hdmi_enable_scrambling(conn, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+
+ KUNIT_EXPECT_EQ(test, priv->scdc_xfer_count, 0);
+ KUNIT_EXPECT_EQ(test, priv->scrambler_enable_count, 0);
+ KUNIT_EXPECT_FALSE(test, conn->hdmi.scrambler_enabled);
+}
+
+/*
+ * Test that enabling scrambling fails without touching the sink when the
+ * sink doesn't support it.
+ */
+static void drm_test_hdmi_scrambling_enable_sink_unsupported(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test,
+ &scdc_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_FALSE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ conn_state->hdmi.scrambler_needed = true;
+
+ ret = drm_connector_hdmi_enable_scrambling(conn, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+
+ KUNIT_EXPECT_EQ(test, priv->scdc_xfer_count, 0);
+ KUNIT_EXPECT_EQ(test, priv->scrambler_enable_count, 0);
+ KUNIT_EXPECT_FALSE(test, conn->hdmi.scrambler_enabled);
+}
+
+/*
+ * Test that enabling scrambling fails on a connector without a DDC adapter,
+ * which is needed to configure the sink.
+ */
+static void drm_test_hdmi_scrambling_enable_no_ddc(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
+ &scdc_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ conn_state->hdmi.scrambler_needed = true;
+
+ ret = drm_connector_hdmi_enable_scrambling(conn, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+
+ KUNIT_EXPECT_EQ(test, priv->scrambler_enable_count, 0);
+ KUNIT_EXPECT_FALSE(test, conn->hdmi.scrambler_enabled);
+}
+
+/*
+ * Test that a failure to configure the sink through SCDC is reported and the
+ * source is left alone.
+ */
+static void drm_test_hdmi_scrambling_enable_ddc_error(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test,
+ &scdc_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ conn_state->hdmi.scrambler_needed = true;
+
+ priv->scdc_xfer_err = -EIO;
+
+ ret = drm_connector_hdmi_enable_scrambling(conn, conn_state);
+ KUNIT_EXPECT_LT(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, priv->scrambler_enable_count, 0);
+ KUNIT_EXPECT_FALSE(test, conn->hdmi.scrambler_enabled);
+ KUNIT_EXPECT_FALSE(test, delayed_work_pending(&conn->hdmi.scdc_work));
+}
+
+/*
+ * Test that a failure to enable scrambling on the source side is reported,
+ * and the sink scrambling setup and status monitoring are rolled back.
+ */
+static void drm_test_hdmi_scrambling_enable_source_error(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test,
+ &scdc_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ conn_state->hdmi.scrambler_needed = true;
+
+ priv->scrambler_enable_ret = -EIO;
+
+ ret = drm_connector_hdmi_enable_scrambling(conn, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, -EIO);
+
+ KUNIT_EXPECT_EQ(test, priv->scrambler_enable_count, 1);
+ KUNIT_EXPECT_EQ(test, priv->scdc_regs[SCDC_TMDS_CONFIG], 0);
+ KUNIT_EXPECT_FALSE(test, conn->hdmi.scrambler_enabled);
+ KUNIT_EXPECT_FALSE(test, delayed_work_pending(&conn->hdmi.scdc_work));
+}
+
+/*
+ * Test that disabling scrambling when it isn't enabled is a no-op.
+ */
+static void drm_test_hdmi_scrambling_disable_not_enabled(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector *conn;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test,
+ &scdc_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ priv->scdc_regs[SCDC_TMDS_CONFIG] = SCDC_TMDS_CONFIG_SCRAMBLED;
+
+ ret = drm_connector_hdmi_disable_scrambling(conn);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, priv->scdc_xfer_count, 0);
+ KUNIT_EXPECT_EQ(test, priv->scdc_regs[SCDC_TMDS_CONFIG],
+ SCDC_TMDS_CONFIG_SCRAMBLED);
+ KUNIT_EXPECT_EQ(test, priv->scrambler_disable_count, 0);
+}
+
+/*
+ * Test that disabling scrambling works when scrambler_enabled has been forced
+ * by the driver to get a disable at boot, i.e. without going through the
+ * enable path that sets up the status monitoring work.
+ */
+static void drm_test_hdmi_scrambling_disable_forced(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector *conn;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test,
+ &scdc_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ priv->scdc_regs[SCDC_TMDS_CONFIG] = SCDC_TMDS_CONFIG_SCRAMBLED;
+ WRITE_ONCE(conn->hdmi.scrambler_enabled, true);
+
+ ret = drm_connector_hdmi_disable_scrambling(conn);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_EQ(test, priv->scdc_regs[SCDC_TMDS_CONFIG], 0);
+ KUNIT_EXPECT_EQ(test, priv->scrambler_disable_count, 1);
+ KUNIT_EXPECT_FALSE(test, conn->hdmi.scrambler_enabled);
+ KUNIT_EXPECT_FALSE(test, conn->hdmi.scdc_work_initialized);
+}
+
+/*
+ * Test that the status monitoring work restores the sink scrambling setup
+ * when the sink reports scrambling as inactive, and keeps monitoring.
+ */
+static void drm_test_hdmi_scrambling_work_restore(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test,
+ &scdc_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ conn_state->hdmi.scrambler_needed = true;
+
+ ret = drm_connector_hdmi_enable_scrambling(conn, conn_state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ scdc_test_stop_work(conn);
+
+ /* Simulate the sink lost its SCDC setup */
+ priv->scdc_regs[SCDC_TMDS_CONFIG] = 0;
+ priv->scdc_regs[SCDC_SCRAMBLER_STATUS] = 0;
+
+ scdc_test_run_work(conn);
+
+ KUNIT_EXPECT_EQ(test, priv->scdc_regs[SCDC_TMDS_CONFIG],
+ SCDC_TMDS_CONFIG_SCRAMBLED);
+ KUNIT_EXPECT_EQ(test, priv->scrambler_enable_count, 1);
+ /* The re-armed work may be executing already, i.e. not pending */
+ KUNIT_EXPECT_TRUE(test, work_busy(&conn->hdmi.scdc_work.work));
+
+ ret = drm_connector_hdmi_disable_scrambling(conn);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/*
+ * Test that the status monitoring work leaves the sink alone and stops
+ * monitoring once the sink reports scrambling as active.
+ */
+static void drm_test_hdmi_scrambling_work_active(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ unsigned int xfer_count;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test,
+ &scdc_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ conn_state->hdmi.scrambler_needed = true;
+
+ ret = drm_connector_hdmi_enable_scrambling(conn, conn_state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ scdc_test_stop_work(conn);
+
+ priv->scdc_regs[SCDC_SCRAMBLER_STATUS] = SCDC_SCRAMBLING_STATUS;
+ xfer_count = priv->scdc_xfer_count;
+
+ scdc_test_run_work(conn);
+
+ /* Only the status register has been read */
+ KUNIT_EXPECT_EQ(test, priv->scdc_xfer_count, xfer_count + 1);
+ KUNIT_EXPECT_FALSE(test, delayed_work_pending(&conn->hdmi.scdc_work));
+
+ ret = drm_connector_hdmi_disable_scrambling(conn);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/*
+ * Test that the status monitoring work never accesses the DDC bus once the
+ * connector got unregistered, as the adapter may already be gone.
+ */
+static void drm_test_hdmi_scrambling_work_unregistered(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ unsigned int xfer_count;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test,
+ &scdc_connector_hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ conn = &priv->connector;
+ KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ conn_state->hdmi.scrambler_needed = true;
+
+ ret = drm_connector_hdmi_enable_scrambling(conn, conn_state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ scdc_test_stop_work(conn);
+
+ conn->registration_state = DRM_CONNECTOR_UNREGISTERED;
+ xfer_count = priv->scdc_xfer_count;
+
+ scdc_test_run_work(conn);
+
+ KUNIT_EXPECT_EQ(test, priv->scdc_xfer_count, xfer_count);
+ KUNIT_EXPECT_FALSE(test, delayed_work_pending(&conn->hdmi.scdc_work));
+
+ conn->registration_state = DRM_CONNECTOR_INITIALIZING;
+
+ ret = drm_connector_hdmi_disable_scrambling(conn);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+static struct kunit_case drm_atomic_helper_connector_hdmi_scrambling_tests[] = {
+ KUNIT_CASE_PARAM(drm_test_hdmi_mode_needs_scrambling,
+ drm_hdmi_mode_needs_scrambling_gen_params),
+ KUNIT_CASE(drm_test_hdmi_scrambling_enable_disable),
+ KUNIT_CASE(drm_test_hdmi_scrambling_enable_not_needed),
+ KUNIT_CASE(drm_test_hdmi_scrambling_enable_source_unsupported),
+ KUNIT_CASE(drm_test_hdmi_scrambling_enable_sink_unsupported),
+ KUNIT_CASE(drm_test_hdmi_scrambling_enable_no_ddc),
+ KUNIT_CASE(drm_test_hdmi_scrambling_enable_ddc_error),
+ KUNIT_CASE(drm_test_hdmi_scrambling_enable_source_error),
+ KUNIT_CASE(drm_test_hdmi_scrambling_disable_not_enabled),
+ KUNIT_CASE(drm_test_hdmi_scrambling_disable_forced),
+ KUNIT_CASE(drm_test_hdmi_scrambling_work_restore),
+ KUNIT_CASE(drm_test_hdmi_scrambling_work_active),
+ KUNIT_CASE(drm_test_hdmi_scrambling_work_unregistered),
+ { }
+};
+
+static struct kunit_suite drm_atomic_helper_connector_hdmi_scrambling_test_suite = {
+ .name = "drm_atomic_helper_connector_hdmi_scrambling",
+ .test_cases = drm_atomic_helper_connector_hdmi_scrambling_tests,
+};
+
kunit_test_suites(
&drm_atomic_helper_connector_hdmi_check_test_suite,
&drm_atomic_helper_connector_hdmi_reset_test_suite,
&drm_atomic_helper_connector_hdmi_mode_valid_test_suite,
&drm_atomic_helper_connector_hdmi_infoframes_test_suite,
+ &drm_atomic_helper_connector_hdmi_scrambling_test_suite,
);
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
+MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@collabora.com>");
MODULE_DESCRIPTION("Kunit test for drm_hdmi_state_helper functions");
MODULE_LICENSE("GPL");
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 22/25] drm/tests: hdmi: Verify SCDC resync on hotplug
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (20 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 21/25] drm/tests: hdmi: Add HDMI 2.0 scrambling helpers coverage Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:22 ` sashiko-bot
2026-11-14 18:03 ` [PATCH 23/25] drm/tests: bridge: Set supported HDMI version Cristian Ciocaltea
` (2 subsequent siblings)
24 siblings, 1 reply; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
When a sink gets reconnected while the display pipeline is active, its
SCDC scrambling setup is lost and must be restored through a CRTC reset.
This is handled by drm_connector_hdmi_sync_scdc(), which is invoked by
the HDMI connector hotplug and .force_ctx() helpers.
Count the encoder enables to detect CRTC resets, and add tests to make
sure that:
- the CRTC is reset only if the sink got (re)plugged, the active mode
needs scrambling and the sink reports scrambling as disabled
- drm_atomic_helper_connector_hdmi_hotplug() leaves the pipeline alone
on disconnect, while on reconnect it restores the sink setup only if
the sink lost it while being unplugged
- DDC errors are reported by drm_connector_hdmi_sync_scdc(), but not by
drm_atomic_helper_connector_hdmi_hotplug(), as .detect_ctx() may only
return a connector status or -EDEADLK
- drm_atomic_helper_connector_hdmi_force_ctx() restores the sink setup
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c | 312 +++++++++++++++++++++
1 file changed, 312 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
index 46654d8fa45c..f17ccbf52bb1 100644
--- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
@@ -42,6 +42,7 @@ struct drm_atomic_helper_connector_hdmi_priv {
size_t current_edid_len;
int hdmi_update_failures;
+ unsigned int encoder_enable_count;
u8 spd_infoframe[HDMI_INFOFRAME_SIZE(SPD)];
size_t spd_infoframe_len;
@@ -315,6 +316,8 @@ static void test_encoder_atomic_enable(struct drm_encoder *encoder,
encoder_to_priv(encoder);
int ret;
+ priv->encoder_enable_count++;
+
ret = drm_atomic_helper_connector_hdmi_update_infoframes(&priv->connector, state);
if (ret)
priv->hdmi_update_failures++;
@@ -4789,12 +4792,321 @@ static struct kunit_suite drm_atomic_helper_connector_hdmi_scrambling_test_suite
.test_cases = drm_atomic_helper_connector_hdmi_scrambling_tests,
};
+static const struct drm_edid *test_read_edid(struct drm_connector *connector)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv =
+ connector_to_priv(connector);
+
+ return drm_edid_alloc(priv->current_edid, priv->current_edid_len);
+}
+
+static const struct drm_connector_hdmi_funcs scdc_read_edid_connector_hdmi_funcs = {
+ .vendor = "Vendor",
+ .product = "Product",
+ .supported_hdmi_ver = HDMI_VERSION_2_0,
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
+ .max_bpc = 8,
+ .read_edid = test_read_edid,
+ .scrambler_enable = count_scrambler_enable,
+ .scrambler_disable = count_scrambler_disable,
+ .avi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+ .hdmi = {
+ .clear_infoframe = accept_infoframe_clear_infoframe,
+ .write_infoframe = accept_infoframe_write_infoframe,
+ },
+};
+
+/*
+ * Sets up an HDMI 2.0 connector attached to a sink supporting scrambling and
+ * drives the given CEA mode on it, leaving the locks held in @ctx.
+ */
+static struct drm_atomic_helper_connector_hdmi_priv *
+sync_scdc_test_init(struct kunit *test,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
+ u8 vic, struct drm_modeset_acquire_ctx *ctx)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_display_mode *mode;
+ int ret;
+
+ priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test, hdmi_funcs,
+ test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+
+ drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs);
+
+ mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, vic);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ drm_modeset_acquire_init(ctx, 0);
+
+retry_conn_enable:
+ ret = drm_kunit_helper_enable_crtc_connector(test, &priv->drm,
+ priv->crtc,
+ &priv->connector,
+ mode, ctx);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(ctx);
+ if (!ret)
+ goto retry_conn_enable;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ KUNIT_ASSERT_EQ(test, priv->encoder_enable_count, 1);
+
+ return priv;
+}
+
+struct drm_hdmi_sync_scdc_test {
+ const char *desc;
+ u8 vic;
+ bool plugged;
+ u8 tmds_config;
+ int xfer_err;
+ int expected_ret;
+ bool expected_reset;
+};
+
+static const struct drm_hdmi_sync_scdc_test drm_hdmi_sync_scdc_params[] = {
+ {
+ .desc = "scrambling lost",
+ .vic = 97,
+ .plugged = true,
+ .expected_reset = true,
+ },
+ {
+ .desc = "scrambling active",
+ .vic = 97,
+ .plugged = true,
+ .tmds_config = SCDC_SCRAMBLING_ENABLE,
+ },
+ {
+ .desc = "scrambling not needed",
+ .vic = 16,
+ .plugged = true,
+ },
+ {
+ .desc = "unplugged",
+ .vic = 97,
+ .plugged = false,
+ },
+ {
+ .desc = "ddc error",
+ .vic = 97,
+ .plugged = true,
+ .xfer_err = -EIO,
+ .expected_ret = -EIO,
+ },
+};
+
+static void drm_hdmi_sync_scdc_desc(const struct drm_hdmi_sync_scdc_test *t,
+ char *desc)
+{
+ strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_hdmi_sync_scdc, drm_hdmi_sync_scdc_params,
+ drm_hdmi_sync_scdc_desc);
+
+/*
+ * Test that drm_connector_hdmi_sync_scdc() resets the CRTC to restore the sink
+ * scrambling setup only when the sink got (re)plugged, the active mode needs
+ * scrambling and the sink reports it as disabled.
+ */
+static void drm_test_hdmi_sync_scdc(struct kunit *test)
+{
+ const struct drm_hdmi_sync_scdc_test *params = test->param_value;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_modeset_acquire_ctx ctx;
+ unsigned int xfer_count;
+ int ret;
+
+ priv = sync_scdc_test_init(test, &scdc_connector_hdmi_funcs,
+ params->vic, &ctx);
+
+ KUNIT_ASSERT_EQ(test, priv->connector.state->hdmi.scrambler_needed,
+ params->vic == 97);
+
+ priv->scdc_regs[SCDC_TMDS_CONFIG] = params->tmds_config;
+ priv->scdc_xfer_err = params->xfer_err;
+ xfer_count = priv->scdc_xfer_count;
+
+retry:
+ ret = drm_connector_hdmi_sync_scdc(&priv->connector, params->plugged, &ctx);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry;
+ }
+ KUNIT_EXPECT_EQ(test, ret, params->expected_ret);
+
+ KUNIT_EXPECT_EQ(test, priv->encoder_enable_count,
+ params->expected_reset ? 2 : 1);
+
+ if (!params->plugged || params->vic != 97)
+ KUNIT_EXPECT_EQ(test, priv->scdc_xfer_count, xfer_count);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+struct drm_hdmi_sync_scdc_hotplug_test {
+ const char *desc;
+ bool sink_reset;
+};
+
+static const struct drm_hdmi_sync_scdc_hotplug_test
+drm_hdmi_sync_scdc_hotplug_params[] = {
+ { .desc = "sink setup lost", .sink_reset = true, },
+ { .desc = "sink setup kept", .sink_reset = false, },
+};
+
+static void
+drm_hdmi_sync_scdc_hotplug_desc(const struct drm_hdmi_sync_scdc_hotplug_test *t,
+ char *desc)
+{
+ strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_hdmi_sync_scdc_hotplug,
+ drm_hdmi_sync_scdc_hotplug_params,
+ drm_hdmi_sync_scdc_hotplug_desc);
+
+/*
+ * Test that drm_atomic_helper_connector_hdmi_hotplug() leaves the pipeline
+ * alone on disconnect, while on reconnect it restores the sink scrambling
+ * setup only if the sink lost it while being unplugged.
+ */
+static void drm_test_hdmi_sync_scdc_hotplug(struct kunit *test)
+{
+ const struct drm_hdmi_sync_scdc_hotplug_test *params = test->param_value;
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_connector *conn;
+ int ret;
+
+ priv = sync_scdc_test_init(test, &scdc_read_edid_connector_hdmi_funcs,
+ 97, &ctx);
+ KUNIT_ASSERT_TRUE(test, priv->connector.state->hdmi.scrambler_needed);
+
+ priv->scdc_regs[SCDC_TMDS_CONFIG] = SCDC_SCRAMBLING_ENABLE;
+ conn = &priv->connector;
+
+retry_disconnect:
+ ret = drm_atomic_helper_connector_hdmi_hotplug(conn, &ctx,
+ connector_status_disconnected);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_disconnect;
+ }
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, priv->encoder_enable_count, 1);
+ KUNIT_EXPECT_FALSE(test, conn->display_info.is_hdmi);
+
+ /* Simulate the sink losing its SCDC setup while being unplugged */
+ if (params->sink_reset)
+ priv->scdc_regs[SCDC_TMDS_CONFIG] = 0;
+
+retry_connect:
+ ret = drm_atomic_helper_connector_hdmi_hotplug(conn, &ctx,
+ connector_status_connected);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry_connect;
+ }
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
+ KUNIT_EXPECT_EQ(test, priv->encoder_enable_count,
+ params->sink_reset ? 2 : 1);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+/*
+ * Test that drm_atomic_helper_connector_hdmi_hotplug() doesn't report SCDC
+ * errors, as .detect_ctx() may only return a connector status or -EDEADLK.
+ */
+static void drm_test_hdmi_sync_scdc_hotplug_ddc_error(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_modeset_acquire_ctx ctx;
+ int ret;
+
+ priv = sync_scdc_test_init(test, &scdc_read_edid_connector_hdmi_funcs,
+ 97, &ctx);
+
+ priv->scdc_xfer_err = -EIO;
+
+retry:
+ ret = drm_atomic_helper_connector_hdmi_hotplug(&priv->connector, &ctx,
+ connector_status_connected);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry;
+ }
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, priv->encoder_enable_count, 1);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+/*
+ * Test that drm_atomic_helper_connector_hdmi_force_ctx() restores the sink
+ * scrambling setup of a connector forced on.
+ */
+static void drm_test_hdmi_sync_scdc_force_ctx(struct kunit *test)
+{
+ struct drm_atomic_helper_connector_hdmi_priv *priv;
+ struct drm_modeset_acquire_ctx ctx;
+ int ret;
+
+ priv = sync_scdc_test_init(test, &scdc_read_edid_connector_hdmi_funcs,
+ 97, &ctx);
+
+ KUNIT_ASSERT_EQ(test, priv->connector.status, connector_status_connected);
+
+retry:
+ ret = drm_atomic_helper_connector_hdmi_force_ctx(&priv->connector, &ctx);
+ if (ret == -EDEADLK) {
+ ret = drm_modeset_backoff(&ctx);
+ if (!ret)
+ goto retry;
+ }
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, priv->encoder_enable_count, 2);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+}
+
+static struct kunit_case drm_atomic_helper_connector_hdmi_sync_scdc_tests[] = {
+ KUNIT_CASE_PARAM(drm_test_hdmi_sync_scdc, drm_hdmi_sync_scdc_gen_params),
+ KUNIT_CASE_PARAM(drm_test_hdmi_sync_scdc_hotplug,
+ drm_hdmi_sync_scdc_hotplug_gen_params),
+ KUNIT_CASE(drm_test_hdmi_sync_scdc_hotplug_ddc_error),
+ KUNIT_CASE(drm_test_hdmi_sync_scdc_force_ctx),
+ { }
+};
+
+static struct kunit_suite drm_atomic_helper_connector_hdmi_sync_scdc_test_suite = {
+ .name = "drm_atomic_helper_connector_hdmi_sync_scdc",
+ .test_cases = drm_atomic_helper_connector_hdmi_sync_scdc_tests,
+};
+
kunit_test_suites(
&drm_atomic_helper_connector_hdmi_check_test_suite,
&drm_atomic_helper_connector_hdmi_reset_test_suite,
&drm_atomic_helper_connector_hdmi_mode_valid_test_suite,
&drm_atomic_helper_connector_hdmi_infoframes_test_suite,
&drm_atomic_helper_connector_hdmi_scrambling_test_suite,
+ &drm_atomic_helper_connector_hdmi_sync_scdc_test_suite,
);
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 23/25] drm/tests: bridge: Set supported HDMI version
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (21 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 22/25] drm/tests: hdmi: Verify SCDC resync on hotplug Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:14 ` sashiko-bot
2026-11-14 18:03 ` [PATCH 24/25] drm/tests: bridge: Add HDMI 2.0 bridge connector coverage Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 25/25] drm/tests: probe-helper: Add .force_ctx() callback coverage Cristian Ciocaltea
24 siblings, 1 reply; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
Bridges using DRM_BRIDGE_OP_HDMI are now expected to also provide a
supported HDMI version. This is not yet enforced, but will become
mandatory once the migration to the new HDMI connector initialization
API is completed.
Advertise HDMI 1.4 as the default for the test bridges, since that is
the highest version that does not require additional initialization
data. This also avoids implying support for HDMI capabilities they do
not currently model.
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_bridge_test.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_bridge_test.c b/drivers/gpu/drm/tests/drm_bridge_test.c
index 15583ade8494..a445f71c31b8 100644
--- a/drivers/gpu/drm/tests/drm_bridge_test.c
+++ b/drivers/gpu/drm/tests/drm_bridge_test.c
@@ -497,6 +497,7 @@ drm_test_bridge_chain_init(struct kunit *test, unsigned int num_bridges,
bridge->type = DRM_MODE_CONNECTOR_HDMIA;
bridge->vendor = "LNX";
bridge->product = "KUnit";
+ bridge->supported_hdmi_ver = HDMI_VERSION_1_4;
bridge->supported_formats = (BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) |
BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
@@ -597,6 +598,7 @@ drm_test_bridge_hdmi_init(struct kunit *test, const struct drm_bridge_funcs *fun
bridge->ops |= DRM_BRIDGE_OP_HDMI;
bridge->vendor = "LNX";
bridge->product = "KUnit";
+ bridge->supported_hdmi_ver = HDMI_VERSION_1_4;
ret = drm_kunit_bridge_add(test, bridge);
if (ret)
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 24/25] drm/tests: bridge: Add HDMI 2.0 bridge connector coverage
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (22 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 23/25] drm/tests: bridge: Set supported HDMI version Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 25/25] drm/tests: probe-helper: Add .force_ctx() callback coverage Cristian Ciocaltea
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
The bridge connector derives the HDMI connector scrambling capability
and max TMDS character rate from the HDMI bridge, and forwards the
scrambler callbacks to it.
Let the HDMI bridge init helper take the supported HDMI version and max
TMDS character rate, and add tests to make sure that:
- an HDMI 2.0 bridge results in a scrambling capable connector, limited
to the HDMI 2.0 max TMDS character rate, whose scrambler callbacks are
forwarded to the bridge
- an HDMI 1.4 bridge results in a connector without scrambling support,
even when the bridge provides scrambler callbacks
- the bridge max TMDS character rate overrides the version-inferred one
- the connector creation fails for an HDMI 2.0 bridge missing any of the
scrambler callbacks
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_bridge_test.c | 185 +++++++++++++++++++++++++++++++-
1 file changed, 182 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_bridge_test.c b/drivers/gpu/drm/tests/drm_bridge_test.c
index a445f71c31b8..51212e33a1ae 100644
--- a/drivers/gpu/drm/tests/drm_bridge_test.c
+++ b/drivers/gpu/drm/tests/drm_bridge_test.c
@@ -30,6 +30,8 @@
struct drm_bridge_priv {
unsigned int enable_count;
unsigned int disable_count;
+ unsigned int scrambler_enable_count;
+ unsigned int scrambler_disable_count;
struct drm_bridge bridge;
void *data;
};
@@ -542,8 +544,10 @@ drm_test_bridge_chain_init(struct kunit *test, unsigned int num_bridges,
}
static struct drm_bridge_init_priv *
-drm_test_bridge_hdmi_init(struct kunit *test, const struct drm_bridge_funcs *funcs,
- unsigned int supported_formats, int max_bpc)
+__drm_test_bridge_hdmi_init(struct kunit *test, const struct drm_bridge_funcs *funcs,
+ unsigned int supported_formats, int max_bpc,
+ enum hdmi_version hdmi_ver,
+ unsigned long long max_tmds_char_rate)
{
struct drm_bridge_init_priv *priv;
struct drm_encoder *enc;
@@ -598,7 +602,8 @@ drm_test_bridge_hdmi_init(struct kunit *test, const struct drm_bridge_funcs *fun
bridge->ops |= DRM_BRIDGE_OP_HDMI;
bridge->vendor = "LNX";
bridge->product = "KUnit";
- bridge->supported_hdmi_ver = HDMI_VERSION_1_4;
+ bridge->supported_hdmi_ver = hdmi_ver;
+ bridge->max_tmds_char_rate = max_tmds_char_rate;
ret = drm_kunit_bridge_add(test, bridge);
if (ret)
@@ -619,6 +624,14 @@ drm_test_bridge_hdmi_init(struct kunit *test, const struct drm_bridge_funcs *fun
return priv;
}
+static struct drm_bridge_init_priv *
+drm_test_bridge_hdmi_init(struct kunit *test, const struct drm_bridge_funcs *funcs,
+ unsigned int supported_formats, int max_bpc)
+{
+ return __drm_test_bridge_hdmi_init(test, funcs, supported_formats,
+ max_bpc, HDMI_VERSION_1_4, 0);
+}
+
/*
* Test that drm_bridge_get_current_state() returns the last committed
* state for an atomic bridge.
@@ -1373,11 +1386,177 @@ static struct kunit_suite drm_bridge_bus_fmt_test_suite = {
.test_cases = drm_bridge_bus_fmt_tests,
};
+static int drm_test_bridge_hdmi_scrambler_enable(struct drm_bridge *bridge)
+{
+ bridge_to_priv(bridge)->scrambler_enable_count++;
+
+ return 0;
+}
+
+static int drm_test_bridge_hdmi_scrambler_disable(struct drm_bridge *bridge)
+{
+ bridge_to_priv(bridge)->scrambler_disable_count++;
+
+ return 0;
+}
+
+#define DRM_TEST_BRIDGE_HDMI_SCRAMBLER_FUNCS(_enable, _disable) \
+ { \
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, \
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, \
+ .atomic_create_state = drm_atomic_helper_bridge_create_state, \
+ .hdmi_write_avi_infoframe = dummy_write_infoframe, \
+ .hdmi_write_hdmi_infoframe = dummy_write_infoframe, \
+ .hdmi_clear_avi_infoframe = dummy_clear_infoframe, \
+ .hdmi_clear_hdmi_infoframe = dummy_clear_infoframe, \
+ .hdmi_scrambler_enable = (_enable), \
+ .hdmi_scrambler_disable = (_disable), \
+ }
+
+static const struct drm_bridge_funcs drm_test_bridge_hdmi_scrambler_funcs =
+ DRM_TEST_BRIDGE_HDMI_SCRAMBLER_FUNCS(drm_test_bridge_hdmi_scrambler_enable,
+ drm_test_bridge_hdmi_scrambler_disable);
+
+static const struct drm_bridge_funcs drm_test_bridge_hdmi_no_scrambler_enable_funcs =
+ DRM_TEST_BRIDGE_HDMI_SCRAMBLER_FUNCS(NULL,
+ drm_test_bridge_hdmi_scrambler_disable);
+
+static const struct drm_bridge_funcs drm_test_bridge_hdmi_no_scrambler_disable_funcs =
+ DRM_TEST_BRIDGE_HDMI_SCRAMBLER_FUNCS(drm_test_bridge_hdmi_scrambler_enable,
+ NULL);
+
+/*
+ * Test that the connector created for an HDMI 2.0 bridge reports the
+ * scrambling capability along with the HDMI 2.0 max TMDS character rate,
+ * and forwards the scrambler callbacks to the bridge.
+ */
+static void drm_test_bridge_connector_hdmi_scrambler(struct kunit *test)
+{
+ struct drm_bridge_init_priv *priv;
+ struct drm_connector *connector;
+ int ret;
+
+ priv = __drm_test_bridge_hdmi_init(test, &drm_test_bridge_hdmi_scrambler_funcs,
+ BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444), 8,
+ HDMI_VERSION_2_0, 0);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ connector = priv->connector;
+ KUNIT_EXPECT_TRUE(test, drm_connector_hdmi_scrambler_supported(connector));
+ KUNIT_EXPECT_EQ(test, connector->hdmi.max_tmds_char_rate,
+ HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ);
+
+ KUNIT_ASSERT_NOT_NULL(test, connector->hdmi.funcs->scrambler_enable);
+ ret = connector->hdmi.funcs->scrambler_enable(connector);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, priv->test_bridge->scrambler_enable_count, 1);
+ KUNIT_EXPECT_EQ(test, priv->test_bridge->scrambler_disable_count, 0);
+
+ KUNIT_ASSERT_NOT_NULL(test, connector->hdmi.funcs->scrambler_disable);
+ ret = connector->hdmi.funcs->scrambler_disable(connector);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, priv->test_bridge->scrambler_enable_count, 1);
+ KUNIT_EXPECT_EQ(test, priv->test_bridge->scrambler_disable_count, 1);
+}
+
+/*
+ * Test that the connector created for an HDMI 1.4 bridge doesn't report the
+ * scrambling capability and ignores the bridge scrambler callbacks.
+ */
+static void drm_test_bridge_connector_hdmi_no_scrambler(struct kunit *test)
+{
+ struct drm_bridge_init_priv *priv;
+ struct drm_connector *connector;
+
+ priv = __drm_test_bridge_hdmi_init(test, &drm_test_bridge_hdmi_scrambler_funcs,
+ BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444), 8,
+ HDMI_VERSION_1_4, 0);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ connector = priv->connector;
+ KUNIT_EXPECT_FALSE(test, drm_connector_hdmi_scrambler_supported(connector));
+ KUNIT_EXPECT_EQ(test, connector->hdmi.max_tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+ KUNIT_EXPECT_NULL(test, connector->hdmi.funcs->scrambler_enable);
+ KUNIT_EXPECT_NULL(test, connector->hdmi.funcs->scrambler_disable);
+}
+
+/*
+ * Test that the max TMDS character rate of an HDMI bridge overrides the
+ * one inferred from the supported HDMI version.
+ */
+static void drm_test_bridge_connector_hdmi_max_tmds_char_rate(struct kunit *test)
+{
+ struct drm_bridge_init_priv *priv;
+
+ priv = __drm_test_bridge_hdmi_init(test, &drm_test_bridge_hdmi_scrambler_funcs,
+ BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444), 8,
+ HDMI_VERSION_2_0,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ KUNIT_EXPECT_EQ(test, priv->connector->hdmi.max_tmds_char_rate,
+ HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
+}
+
+struct drm_bridge_connector_hdmi_missing_scrambler_test {
+ const char *desc;
+ const struct drm_bridge_funcs *funcs;
+};
+
+static const struct drm_bridge_connector_hdmi_missing_scrambler_test
+drm_bridge_connector_hdmi_missing_scrambler_params[] = {
+ { "hdmi_scrambler_enable", &drm_test_bridge_hdmi_no_scrambler_enable_funcs },
+ { "hdmi_scrambler_disable", &drm_test_bridge_hdmi_no_scrambler_disable_funcs },
+};
+
+static void
+drm_bridge_connector_hdmi_missing_scrambler_desc(const struct drm_bridge_connector_hdmi_missing_scrambler_test *t,
+ char *desc)
+{
+ strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_bridge_connector_hdmi_missing_scrambler,
+ drm_bridge_connector_hdmi_missing_scrambler_params,
+ drm_bridge_connector_hdmi_missing_scrambler_desc);
+
+/*
+ * Test that creating the connector for an HDMI 2.0 bridge fails when any of
+ * the bridge scrambler callbacks is missing.
+ */
+static void drm_test_bridge_connector_hdmi_missing_scrambler(struct kunit *test)
+{
+ const struct drm_bridge_connector_hdmi_missing_scrambler_test *params =
+ test->param_value;
+ struct drm_bridge_init_priv *priv;
+
+ priv = __drm_test_bridge_hdmi_init(test, params->funcs,
+ BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444), 8,
+ HDMI_VERSION_2_0, 0);
+ KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(priv), -EINVAL);
+}
+
+static struct kunit_case drm_bridge_connector_hdmi_tests[] = {
+ KUNIT_CASE(drm_test_bridge_connector_hdmi_scrambler),
+ KUNIT_CASE(drm_test_bridge_connector_hdmi_no_scrambler),
+ KUNIT_CASE(drm_test_bridge_connector_hdmi_max_tmds_char_rate),
+ KUNIT_CASE_PARAM(drm_test_bridge_connector_hdmi_missing_scrambler,
+ drm_bridge_connector_hdmi_missing_scrambler_gen_params),
+ { }
+};
+
+static struct kunit_suite drm_bridge_connector_hdmi_test_suite = {
+ .name = "drm_bridge_connector_hdmi",
+ .test_cases = drm_bridge_connector_hdmi_tests,
+};
+
kunit_test_suites(
&drm_bridge_get_current_state_test_suite,
&drm_bridge_helper_reset_crtc_test_suite,
&drm_bridge_alloc_test_suite,
&drm_bridge_bus_fmt_test_suite,
+ &drm_bridge_connector_hdmi_test_suite,
);
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH 25/25] drm/tests: probe-helper: Add .force_ctx() callback coverage
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
` (23 preceding siblings ...)
2026-11-14 18:03 ` [PATCH 24/25] drm/tests: bridge: Add HDMI 2.0 bridge connector coverage Cristian Ciocaltea
@ 2026-11-14 18:03 ` Cristian Ciocaltea
24 siblings, 0 replies; 31+ messages in thread
From: Cristian Ciocaltea @ 2026-11-14 18:03 UTC (permalink / raw)
To: Helge Deller, Martin Bugge, Thierry Reding, Hans Verkuil,
Mauro Carvalho Chehab, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, linux-fbdev, dri-devel, linux-kernel
The .force_ctx() connector helper callback takes precedence over the
.force() connector callback when probing a forced connector.
Add tests to make sure that:
- .force_ctx() is called instead of .force(), with the forced status
already set and the connection_mutex held through the acquire context
- errors other than -EDEADLK don't prevent the connector from being
probed
- .force() is still called when .force_ctx() is not provided
- -EDEADLK makes the probe back off and retry, while restoring the
connector status beforehand, so that the status change gets detected
For the latter, a genuine deadlock is needed, since
drm_modeset_backoff() expects a contended lock. Create one with an
older acquire context in a kthread, which wounds the probe context by
requesting the connection_mutex while holding a lock that the probe
context tries to take from .force_ctx().
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/tests/drm_probe_helper_test.c | 296 ++++++++++++++++++++++++++
1 file changed, 296 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c
index ebc0392585e7..5d7dc4725518 100644
--- a/drivers/gpu/drm/tests/drm_probe_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_probe_helper_test.c
@@ -3,6 +3,9 @@
* Kunit test for drm_probe_helper functions
*/
+#include <linux/completion.h>
+#include <linux/kthread.h>
+
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_device.h>
@@ -11,14 +14,24 @@
#include <drm/drm_mode.h>
#include <drm/drm_modes.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_modeset_lock.h>
#include <drm/drm_probe_helper.h>
#include <kunit/test.h>
+struct drm_probe_helper_contender;
+
struct drm_probe_helper_test_priv {
struct drm_device *drm;
struct device *dev;
struct drm_connector connector;
+
+ unsigned int force_count;
+ unsigned int force_ctx_count;
+ int force_ctx_ret;
+ bool force_ctx_locked;
+ enum drm_connector_status force_ctx_status;
+ struct drm_probe_helper_contender *contender;
};
static const struct drm_connector_helper_funcs drm_probe_helper_connector_helper_funcs = {
@@ -212,6 +225,289 @@ static struct kunit_suite drm_test_connector_helper_tv_get_modes_suite = {
kunit_test_suite(drm_test_connector_helper_tv_get_modes_suite);
+/*
+ * Runs an acquire context older than the probe one, which first takes @lock,
+ * then waits to be told to also request the connection_mutex held by the
+ * probe context. Being older, it wounds the probe context, which will get
+ * -EDEADLK as soon as it tries to take @lock.
+ */
+struct drm_probe_helper_contender {
+ struct drm_device *drm;
+ struct drm_modeset_lock lock;
+ struct completion locked;
+ struct completion go;
+ struct completion done;
+ int ret;
+};
+
+static int drm_probe_helper_contender_fn(void *data)
+{
+ struct drm_probe_helper_contender *contender = data;
+ struct drm_modeset_acquire_ctx ctx;
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ contender->ret = drm_modeset_lock(&contender->lock, &ctx);
+ complete(&contender->locked);
+
+ if (!contender->ret) {
+ if (wait_for_completion_timeout(&contender->go, 5 * HZ))
+ contender->ret =
+ drm_modeset_lock(&contender->drm->mode_config.connection_mutex,
+ &ctx);
+ else
+ contender->ret = -ETIMEDOUT;
+ }
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+
+ complete(&contender->done);
+
+ return 0;
+}
+
+static struct drm_probe_helper_test_priv *
+connector_to_probe_priv(struct drm_connector *connector)
+{
+ return container_of(connector, struct drm_probe_helper_test_priv, connector);
+}
+
+static void drm_probe_helper_test_force(struct drm_connector *connector)
+{
+ connector_to_probe_priv(connector)->force_count++;
+}
+
+static int drm_probe_helper_test_force_ctx(struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ struct drm_probe_helper_test_priv *priv = connector_to_probe_priv(connector);
+ struct drm_probe_helper_contender *contender = priv->contender;
+ int ret;
+
+ priv->force_ctx_count++;
+ priv->force_ctx_status = connector->status;
+ priv->force_ctx_locked =
+ drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex);
+
+ if (contender && priv->force_ctx_count == 1) {
+ complete(&contender->go);
+
+ ret = drm_modeset_lock(&contender->lock, ctx);
+ if (ret)
+ return ret;
+ }
+
+ return priv->force_ctx_ret;
+}
+
+static int drm_probe_helper_test_get_modes(struct drm_connector *connector)
+{
+ return 0;
+}
+
+static const struct drm_connector_helper_funcs drm_probe_helper_force_ctx_helper_funcs = {
+ .get_modes = drm_probe_helper_test_get_modes,
+ .force_ctx = drm_probe_helper_test_force_ctx,
+};
+
+static const struct drm_connector_helper_funcs drm_probe_helper_force_helper_funcs = {
+ .get_modes = drm_probe_helper_test_get_modes,
+};
+
+static const struct drm_connector_funcs drm_probe_helper_force_connector_funcs = {
+ .force = drm_probe_helper_test_force,
+ .atomic_create_state = drm_atomic_helper_connector_create_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+};
+
+static int drm_probe_helper_force_test_init(struct kunit *test)
+{
+ struct drm_probe_helper_test_priv *priv;
+ int ret;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+ test->priv = priv;
+
+ priv->dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
+
+ priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev,
+ sizeof(*priv->drm), 0,
+ DRIVER_MODESET | DRIVER_ATOMIC);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
+
+ ret = drmm_connector_init(priv->drm, &priv->connector,
+ &drm_probe_helper_force_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_connector_helper_add(&priv->connector,
+ &drm_probe_helper_force_ctx_helper_funcs);
+
+ priv->connector.force = DRM_FORCE_ON;
+
+ return 0;
+}
+
+static int drm_probe_helper_test_probe(struct drm_probe_helper_test_priv *priv)
+{
+ int count;
+
+ mutex_lock(&priv->drm->mode_config.mutex);
+
+ count = drm_helper_probe_single_connector_modes(&priv->connector, 1920, 1080);
+
+ mutex_unlock(&priv->drm->mode_config.mutex);
+
+ return count;
+}
+
+/*
+ * Test that probing a forced connector calls .force_ctx(), in favour of
+ * .force(), with the forced status already set and the connection_mutex held
+ * through the acquire context.
+ */
+static void drm_test_probe_helper_force_ctx(struct kunit *test)
+{
+ struct drm_probe_helper_test_priv *priv = test->priv;
+ int count;
+
+ count = drm_probe_helper_test_probe(priv);
+ KUNIT_EXPECT_GT(test, count, 0);
+
+ KUNIT_EXPECT_EQ(test, priv->force_ctx_count, 1);
+ KUNIT_EXPECT_EQ(test, priv->force_count, 0);
+ KUNIT_EXPECT_EQ(test, priv->force_ctx_status, connector_status_connected);
+ KUNIT_EXPECT_TRUE(test, priv->force_ctx_locked);
+ KUNIT_EXPECT_EQ(test, priv->connector.status, connector_status_connected);
+}
+
+/*
+ * Test that probing a connector forced off calls .force_ctx() with the
+ * disconnected status set.
+ */
+static void drm_test_probe_helper_force_ctx_off(struct kunit *test)
+{
+ struct drm_probe_helper_test_priv *priv = test->priv;
+ int count;
+
+ priv->connector.force = DRM_FORCE_OFF;
+
+ count = drm_probe_helper_test_probe(priv);
+ KUNIT_EXPECT_EQ(test, count, 0);
+
+ KUNIT_EXPECT_EQ(test, priv->force_ctx_count, 1);
+ KUNIT_EXPECT_EQ(test, priv->force_ctx_status, connector_status_disconnected);
+ KUNIT_EXPECT_EQ(test, priv->connector.status, connector_status_disconnected);
+}
+
+/*
+ * Test that a .force_ctx() error other than -EDEADLK doesn't prevent the
+ * forced connector from being probed.
+ */
+static void drm_test_probe_helper_force_ctx_error(struct kunit *test)
+{
+ struct drm_probe_helper_test_priv *priv = test->priv;
+ int count;
+
+ priv->force_ctx_ret = -EIO;
+
+ count = drm_probe_helper_test_probe(priv);
+ KUNIT_EXPECT_GT(test, count, 0);
+
+ KUNIT_EXPECT_EQ(test, priv->force_ctx_count, 1);
+ KUNIT_EXPECT_EQ(test, priv->connector.status, connector_status_connected);
+}
+
+/*
+ * Test that a .force_ctx() returning -EDEADLK makes the probe back off and
+ * retry, while restoring the connector status beforehand, so that the status
+ * change still gets detected.
+ */
+static void drm_test_probe_helper_force_ctx_deadlock(struct kunit *test)
+{
+ struct drm_probe_helper_test_priv *priv = test->priv;
+ struct drm_probe_helper_contender *contender;
+ struct task_struct *task;
+ int count = 0;
+ bool done;
+
+ KUNIT_ASSERT_EQ(test, priv->connector.status, connector_status_unknown);
+ KUNIT_ASSERT_FALSE(test, priv->drm->mode_config.delayed_event);
+
+ contender = kunit_kzalloc(test, sizeof(*contender), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, contender);
+
+ contender->drm = priv->drm;
+ drm_modeset_lock_init(&contender->lock);
+ init_completion(&contender->locked);
+ init_completion(&contender->go);
+ init_completion(&contender->done);
+
+ task = kthread_run(drm_probe_helper_contender_fn, contender,
+ "drm-probe-contender");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, task);
+
+ /* Make sure the contender context is the older one */
+ wait_for_completion(&contender->locked);
+ if (!contender->ret) {
+ priv->contender = contender;
+ count = drm_probe_helper_test_probe(priv);
+ }
+
+ /* The contender must be gone before releasing its resources */
+ done = wait_for_completion_timeout(&contender->done, 10 * HZ);
+ KUNIT_ASSERT_TRUE(test, done);
+ KUNIT_ASSERT_EQ(test, contender->ret, 0);
+
+ KUNIT_EXPECT_GT(test, count, 0);
+ KUNIT_EXPECT_EQ(test, priv->force_ctx_count, 2);
+ KUNIT_EXPECT_EQ(test, priv->force_ctx_status, connector_status_connected);
+ KUNIT_EXPECT_EQ(test, priv->connector.status, connector_status_connected);
+ KUNIT_EXPECT_TRUE(test, priv->drm->mode_config.delayed_event);
+}
+
+/*
+ * Test that probing a forced connector still calls .force() when no
+ * .force_ctx() is provided.
+ */
+static void drm_test_probe_helper_force(struct kunit *test)
+{
+ struct drm_probe_helper_test_priv *priv = test->priv;
+ int count;
+
+ drm_connector_helper_add(&priv->connector,
+ &drm_probe_helper_force_helper_funcs);
+
+ count = drm_probe_helper_test_probe(priv);
+ KUNIT_EXPECT_GT(test, count, 0);
+
+ KUNIT_EXPECT_EQ(test, priv->force_count, 1);
+ KUNIT_EXPECT_EQ(test, priv->force_ctx_count, 0);
+ KUNIT_EXPECT_EQ(test, priv->connector.status, connector_status_connected);
+}
+
+static struct kunit_case drm_test_probe_helper_force_tests[] = {
+ KUNIT_CASE(drm_test_probe_helper_force_ctx),
+ KUNIT_CASE(drm_test_probe_helper_force_ctx_off),
+ KUNIT_CASE(drm_test_probe_helper_force_ctx_error),
+ KUNIT_CASE(drm_test_probe_helper_force_ctx_deadlock),
+ KUNIT_CASE(drm_test_probe_helper_force),
+ { }
+};
+
+static struct kunit_suite drm_test_probe_helper_force_suite = {
+ .name = "drm_probe_helper_force",
+ .init = drm_probe_helper_force_test_init,
+ .test_cases = drm_test_probe_helper_force_tests,
+};
+
+kunit_test_suite(drm_test_probe_helper_force_suite);
+
MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
MODULE_DESCRIPTION("Kunit test for drm_probe_helper functions");
MODULE_LICENSE("GPL");
--
2.56.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH 06/25] drm/tests: hdmi: Convert to drmm_connector_hdmi_init()
2026-11-14 18:03 ` [PATCH 06/25] drm/tests: hdmi: " Cristian Ciocaltea
@ 2026-11-14 18:14 ` sashiko-bot
0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-11-14 18:14 UTC (permalink / raw)
To: Cristian Ciocaltea; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found no new issues
in this patch, but noticed 1 related pre-existing issue it does not
introduce:
- [Medium] drm/tests: ww_mutex leak in
drm_test_check_reject_audio_infoframe() [1]
--
commit d078fa85b984429f42b17f7efe1e93d8055ccfb2
Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
drm/tests: hdmi: Convert to drmm_connector_hdmi_init()
This commit converts the HDMI state helper KUnit tests to the new
drmm_connector_hdmi_init() signature and stores each connector's
drm_connector_hdmi_funcs copy in drm_atomic_helper_connector_hdmi_priv so
it outlives the connector.
> diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
> index 336bb644cb7c9..f1bdd197429c6 100644
> --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
> +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
[ ... ]
> @@ -3314,8 +3310,6 @@ static void drm_test_check_reject_audio_infoframe(struct kunit *test)
> int ret;
>
> priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test,
> - BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444),
> - 8,
> &reject_audio_infoframe_hdmi_funcs,
> test_edid_hdmi_1080p_rgb_max_200mhz);
> KUNIT_ASSERT_NOT_NULL(test, priv);
[Severity: Medium]
This problem wasn't introduced by this patch, but can a failed assertion in
drm_test_check_reject_audio_infoframe() leak the modeset ww_mutex locks in
ctx [1]?
drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c:drm_test_check_reject_audio_infoframe() {
...
drm_modeset_acquire_init(&ctx, 0);
retry_conn_enable:
ret = drm_kunit_helper_enable_crtc_connector(test, drm,
crtc, conn,
preferred,
&ctx);
...
KUNIT_ASSERT_EQ(test, ret, 0);
...
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
KUNIT_ASSERT_* returns immediately on failure, bypassing
drm_modeset_drop_locks() and drm_modeset_acquire_fini() at the end of the
function. Because ctx lives on the stack, returning with locks held leaves
dangling lock list nodes pointing to out-of-scope stack memory, triggering
lockdep warnings and potential crashes during subsequent tests or DRM
device cleanup.
[1] https://sashiko.dev/#/bug/linux-1258d4b2-95ea-43e2-a892-e2011a232ee8
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114-drm-conn-hdmi-cleanup-v1-0-20e8a33a2286@collabora.com?part=6
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 23/25] drm/tests: bridge: Set supported HDMI version
2026-11-14 18:03 ` [PATCH 23/25] drm/tests: bridge: Set supported HDMI version Cristian Ciocaltea
@ 2026-11-14 18:14 ` sashiko-bot
0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-11-14 18:14 UTC (permalink / raw)
To: Cristian Ciocaltea; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found no new issues
in this patch, but noticed 1 related pre-existing issue it does not
introduce:
- [Medium] drm/tests: bridge: memory leak of edid in
drm_test_bridge_chain_init() [1]
--
commit e45be11e80ff96e23882e09db24056eb896ea3a6
Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
drm/tests: bridge: Set supported HDMI version
This commit sets supported_hdmi_ver to HDMI_VERSION_1_4 on HDMI test
bridges in the KUnit bridge tests.
> diff --git a/drivers/gpu/drm/tests/drm_bridge_test.c b/drivers/gpu/drm/tests/drm_bridge_test.c
> index 15583ade84946..a445f71c31b84 100644
> --- a/drivers/gpu/drm/tests/drm_bridge_test.c
> +++ b/drivers/gpu/drm/tests/drm_bridge_test.c
> @@ -497,6 +497,7 @@ drm_test_bridge_chain_init(struct kunit *test, unsigned int num_bridges,
> bridge->type = DRM_MODE_CONNECTOR_HDMIA;
> bridge->vendor = "LNX";
> bridge->product = "KUnit";
> + bridge->supported_hdmi_ver = HDMI_VERSION_1_4;
> bridge->supported_formats = (BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
> BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) |
> BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
[Severity: Medium]
This problem wasn't introduced by this patch, but does
drm_test_bridge_chain_init() leak edid [1] when has_hdmi is true?
drivers/gpu/drm/tests/drm_bridge_test.c:drm_test_bridge_chain_init() {
...
scoped_guard(mutex, &drm->mode_config.mutex) {
edid = drm_edid_alloc(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz,
ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz));
if (!edid)
return ERR_PTR(-EINVAL);
drm_edid_connector_update(priv->connector, edid);
KUNIT_ASSERT_GT(test, drm_edid_connector_add_modes(priv->connector), 0);
ret = priv->connector->funcs->fill_modes(priv->connector, 4096, 4096);
}
...
}
drm_edid_connector_update() copies the EDID data into a connector property
blob without taking ownership of edid, and drm_edid_free() is never called
before returning.
[1] https://sashiko.dev/#/bug/linux-cc41077e-4212-413a-a83b-7a6487bad187
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114-drm-conn-hdmi-cleanup-v1-0-20e8a33a2286@collabora.com?part=23
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 01/25] video/hdmi: Copy SPD vendor/product with strtomem_pad()
2026-11-14 18:03 ` [PATCH 01/25] video/hdmi: Copy SPD vendor/product with strtomem_pad() Cristian Ciocaltea
@ 2026-11-14 18:15 ` sashiko-bot
0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-11-14 18:15 UTC (permalink / raw)
To: Cristian Ciocaltea; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] video/hdmi: inaccurate Fixes trailer for
hdmi_spd_infoframe_init()
--
commit 7959e6f9456b8b3e3ae9874fc5b26920be113583
Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
> video/hdmi: Copy SPD vendor/product with strtomem_pad()
>
> The HDMI specification defines the SPD InfoFrame Vendor Name and Product
> Description as fixed-size fields, padded with zeros and left without any
> trailing NUL when a name spans the whole field.
>
> Mark the fields as non-strings, so that the copies can be handed over to
> strtomem_pad(), which implements precisely the required semantics. This
> also bounds the reads from the source strings, whereas the open-coded
> strlen() could run past the end of the buffer in the
> hdmi_spd_infoframe_unpack() path, where the names come straight from the
> wire and are not NUL-terminated.
>
> Fixes: 2c676f378edb ("[media] hdmi: added unpack and logging functions for InfoFrames")
> Cc: stable@vger.kernel.org # v6.1+
> Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
[Severity: Low]
Should the Fixes: tag point to 35bed3fa9ec5 ("drivers/video: Fix
-Wstringop-truncation in hdmi.c") instead?
When 2c676f378edb ("[media] hdmi: added unpack and logging functions for
InfoFrames") added hdmi_spd_infoframe_unpack(), hdmi_spd_infoframe_init()
used strncpy() bounded by sizeof(frame->vendor) and
sizeof(frame->product). The unbounded strlen() calls replaced by this
patch were introduced by 35bed3fa9ec5 ("drivers/video: Fix
-Wstringop-truncation in hdmi.c").
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114-drm-conn-hdmi-cleanup-v1-0-20e8a33a2286@collabora.com?part=1
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 21/25] drm/tests: hdmi: Add HDMI 2.0 scrambling helpers coverage
2026-11-14 18:03 ` [PATCH 21/25] drm/tests: hdmi: Add HDMI 2.0 scrambling helpers coverage Cristian Ciocaltea
@ 2026-11-14 18:21 ` sashiko-bot
0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-11-14 18:21 UTC (permalink / raw)
To: Cristian Ciocaltea; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] drm/tests: hdmi: drm_connector_hdmi_enable_scrambling() SCDC
check fails
--
commit 352379847f7c6ceae1ef1736d628408794656e3d
Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
drm/tests: hdmi: Add HDMI 2.0 scrambling helpers coverage
This commit adds KUnit tests and a fake SCDC DDC adapter to cover the HDMI
2.0 scrambling helpers and SCDC status monitoring work.
> diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
> index 294be8f4a3076..46654d8fa45cf 100644
> --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
> +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
[ ... ]
> +/*
> + * Test that enabling scrambling for a mode that doesn't need it leaves the
> + * source alone, but clears any stale scrambling setup on the sink side.
> + */
> +static void drm_test_hdmi_scrambling_enable_not_needed(struct kunit *test)
> +{
> + struct drm_atomic_helper_connector_hdmi_priv *priv;
> + struct drm_connector_state *conn_state;
> + struct drm_connector *conn;
> + int ret;
> +
> + priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test,
> + &scdc_connector_hdmi_funcs,
> + test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + conn = &priv->connector;
> + KUNIT_ASSERT_TRUE(test, conn->display_info.hdmi.scdc.scrambling.supported);
> +
> + conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
> + KUNIT_ASSERT_NOT_NULL(test, conn_state);
> +
> + conn_state->hdmi.scrambler_needed = false;
> +
> + priv->scdc_regs[SCDC_TMDS_CONFIG] = SCDC_TMDS_CONFIG_SCRAMBLED;
> +
> + ret = drm_connector_hdmi_enable_scrambling(conn, conn_state);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> +
> + KUNIT_EXPECT_EQ(test, priv->scdc_regs[SCDC_TMDS_CONFIG], 0);
[Severity: Medium]
Does this KUNIT_EXPECT_EQ() on priv->scdc_regs[SCDC_TMDS_CONFIG] always
fail?
drivers/gpu/drm/display/drm_hdmi_helper.c:drm_connector_hdmi_enable_scrambling() {
...
if (!conn_state->hdmi.scrambler_needed)
return 0;
...
}
When conn_state->hdmi.scrambler_needed is false,
drm_connector_hdmi_enable_scrambling() returns 0 immediately without
touching SCDC, so priv->scdc_regs[SCDC_TMDS_CONFIG] stays at
SCDC_TMDS_CONFIG_SCRAMBLED and drm_test_hdmi_scrambling_enable_not_needed()
fails on every run. The commit message also claims that enabling
scrambling for a mode not needing it clears stale sink scrambling setup.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114-drm-conn-hdmi-cleanup-v1-0-20e8a33a2286@collabora.com?part=21
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH 22/25] drm/tests: hdmi: Verify SCDC resync on hotplug
2026-11-14 18:03 ` [PATCH 22/25] drm/tests: hdmi: Verify SCDC resync on hotplug Cristian Ciocaltea
@ 2026-11-14 18:22 ` sashiko-bot
0 siblings, 0 replies; 31+ messages in thread
From: sashiko-bot @ 2026-11-14 18:22 UTC (permalink / raw)
To: Cristian Ciocaltea; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] drm/tests: hdmi: missing scrambler enable in
sync_scdc_test_init()
--
commit ad6b966d9b68c01d91b4a1ff13528d634475ed1e
Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
drm/tests: hdmi: Verify SCDC resync on hotplug
This commit adds KUnit tests for SCDC scrambling resynchronization across
drm_connector_hdmi_sync_scdc(), drm_atomic_helper_connector_hdmi_hotplug(),
and drm_atomic_helper_connector_hdmi_force_ctx().
> diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
> index 46654d8fa45cf..f17ccbf52bb18 100644
> --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
> +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
[ ... ]
> @@ -315,6 +316,8 @@ static void test_encoder_atomic_enable(struct drm_encoder *encoder,
> encoder_to_priv(encoder);
> int ret;
>
> + priv->encoder_enable_count++;
> +
> ret = drm_atomic_helper_connector_hdmi_update_infoframes(&priv->connector, state);
> if (ret)
> priv->hdmi_update_failures++;
> @@ -4789,12 +4792,321 @@ static struct kunit_suite drm_atomic_helper_connector_hdmi_scrambling_test_suite
[ ... ]
> +static struct drm_atomic_helper_connector_hdmi_priv *
> +sync_scdc_test_init(struct kunit *test,
> + const struct drm_connector_hdmi_funcs *hdmi_funcs,
> + u8 vic, struct drm_modeset_acquire_ctx *ctx)
> +{
> + struct drm_atomic_helper_connector_hdmi_priv *priv;
> + struct drm_display_mode *mode;
> + int ret;
> +
> + priv = drm_kunit_helper_connector_hdmi_init_with_scdc(test, hdmi_funcs,
> + test_edid_hdmi_4k_rgb_yuv420_dc_max_600mhz);
> + KUNIT_ASSERT_NOT_NULL(test, priv);
> +
> + drm_encoder_helper_add(&priv->encoder, &test_encoder_helper_funcs);
> +
> + mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, vic);
> + KUNIT_ASSERT_NOT_NULL(test, mode);
> +
> + drm_modeset_acquire_init(ctx, 0);
> +
> +retry_conn_enable:
> + ret = drm_kunit_helper_enable_crtc_connector(test, &priv->drm,
> + priv->crtc,
> + &priv->connector,
> + mode, ctx);
> + if (ret == -EDEADLK) {
> + ret = drm_modeset_backoff(ctx);
> + if (!ret)
> + goto retry_conn_enable;
> + }
> + KUNIT_ASSERT_EQ(test, ret, 0);
> + KUNIT_ASSERT_EQ(test, priv->encoder_enable_count, 1);
> +
> + return priv;
> +}
[Severity: Medium]
Does sync_scdc_test_init() leave priv->connector.hdmi.scrambler_enabled as
false when enabling a mode that requires scrambling?
Enabling the CRTC sets priv->connector.state->hdmi.scrambler_needed during
atomic check and invokes test_encoder_atomic_enable(), which updates
infoframes but never calls drm_connector_hdmi_enable_scrambling().
drm_connector_hdmi_sync_scdc() checks scrambler_enabled before inspecting
SCDC state:
drivers/gpu/drm/display/drm_hdmi_helper.c:drm_connector_hdmi_sync_scdc() {
...
if (!drm_connector_hdmi_scrambler_supported(connector) ||
!READ_ONCE(hdmi->scrambler_enabled))
return 0;
...
}
Because scrambler_enabled remains false, drm_connector_hdmi_sync_scdc()
returns 0 immediately without reading SCDC_TMDS_CONFIG over DDC or resetting
the CRTC, causing the subtests that expect a CRTC reset or DDC error to fail
their KUnit assertions and the remaining subtests to pass without exercising
SCDC reads. Calling drm_connector_hdmi_enable_scrambling() from
test_encoder_atomic_enable() (or in sync_scdc_test_init() after enabling the
CRTC) would populate scrambler_enabled as expected.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261114-drm-conn-hdmi-cleanup-v1-0-20e8a33a2286@collabora.com?part=22
^ permalink raw reply [flat|nested] 31+ messages in thread
end of thread, other threads:[~2026-11-14 18:22 UTC | newest]
Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-14 18:03 [PATCH 00/25] drm: HDMI connector API cleanup and HDMI 2.0 test coverage Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 01/25] video/hdmi: Copy SPD vendor/product with strtomem_pad() Cristian Ciocaltea
2026-11-14 18:15 ` sashiko-bot
2026-11-14 18:03 ` [PATCH 02/25] video/hdmi: Define SPD InfoFrame vendor/product field lengths Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 03/25] drm/display: hdmi-state-helper: Drop drm_atomic_helper_connector_hdmi_force() Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 04/25] drm/display: hdmi-state-helper: Reject connectors lacking HDMI funcs Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 05/25] drm/tests: connector: Convert to drmm_connector_hdmi_init() Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 06/25] drm/tests: hdmi: " Cristian Ciocaltea
2026-11-14 18:14 ` sashiko-bot
2026-11-14 18:03 ` [PATCH 07/25] drm/connector: Remove drmm_connector_hdmi_ini2() Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 08/25] drm/connector: Drop redundant hdmi vendor/product fields Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 09/25] drm/connector: Drop redundant hdmi supported_formats field Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 10/25] drm/connector: Drop redundant max_bpc field Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 11/25] drm/connector: Use the SPD InfoFrame field length defines Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 12/25] drm/connector: Fix comment in drmm_connector_hdmi_init() Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 13/25] drm/tests: edid: Fix conformity for 1080p+4K YUV420 200MHz EDID Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 14/25] drm/tests: edid: Fix conformity for 4K RGB/YUV 340MHz EDID Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 15/25] drm/tests: edid: Add 4K@60Hz EDID with 600MHz TMDS Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 16/25] drm/tests: connector: Add HDMI max_tmds_char_rate init coverage Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 17/25] drm/tests: connector: Add HDMI source-side scrambler coverage Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 18/25] drm/tests: hdmi: Cover mode validation against connector max_tmds_char_rate Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 19/25] drm/tests: hdmi: Verify scrambler_needed computation Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 20/25] drm/tests: hdmi: Add SPD InfoFrame vendor/product coverage Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 21/25] drm/tests: hdmi: Add HDMI 2.0 scrambling helpers coverage Cristian Ciocaltea
2026-11-14 18:21 ` sashiko-bot
2026-11-14 18:03 ` [PATCH 22/25] drm/tests: hdmi: Verify SCDC resync on hotplug Cristian Ciocaltea
2026-11-14 18:22 ` sashiko-bot
2026-11-14 18:03 ` [PATCH 23/25] drm/tests: bridge: Set supported HDMI version Cristian Ciocaltea
2026-11-14 18:14 ` sashiko-bot
2026-11-14 18:03 ` [PATCH 24/25] drm/tests: bridge: Add HDMI 2.0 bridge connector coverage Cristian Ciocaltea
2026-11-14 18:03 ` [PATCH 25/25] drm/tests: probe-helper: Add .force_ctx() callback coverage Cristian Ciocaltea
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.