[PATCH v10 07/69] drm/connector: Add HDMI 2.0 scrambler infrastructure

Maxime Ripard mripard at kernel.org
Tue Aug 25 02:29:53 PDT 2026


On Fri, Aug 21, 2026 at 10:04:14PM +0300, Cristian Ciocaltea wrote:
> On 8/20/26 7:56 PM, Maxime Ripard wrote:
> > On Wed, Aug 19, 2026 at 10:33:04PM +0300, Cristian Ciocaltea wrote:
> >> On 8/19/26 1:12 PM, Maxime Ripard wrote:
> >>> On Fri, Jul 31, 2026 at 07:19:14PM +0300, Cristian Ciocaltea wrote:
> >>>> Add the connector-level infrastructure to support HDMI 2.0 scrambling:
> >>>>
> >>>> - A drm_connector_hdmi_scrambler_supported() helper to report whether
> >>>>   the source supports the scrambling capability, based on the presence
> >>>>   of the newly introduced .scrambler_{enable|disable}() callbacks in
> >>>>   drm_connector_hdmi_funcs are mandatory
> >>>> - A scrambler_needed flag to be managed by the hdmi state helpers based
> >>>>   on the negotiated TMDS character rate and the source/sink scrambling
> >>>>   capabilities
> >>>> - A scrambler_enabled flag to track whether scrambling is currently
> >>>>   active
> >>>> - A delayed work item (scdc_work) to monitor sink-side scrambling status
> >>>>   and retry the setup if the sink resets it
> >>>> - A scdc_work_initialized flag to support lazy initialization of the
> >>>>   work item on the first scrambling enable and guard the teardown paths
> >>>>
> >>>> These are intended to be used by SCDC scrambling helpers to coordinate
> >>>> scrambling setup and teardown between the source driver and the DRM
> >>>> core.
> >>>>
> >>>> Tested-by: Maud Spierings <maud_spierings at hotmail.com>
> >>>> Tested-by: Diederik de Haas <diederik at cknow-tech.com>  # NanoPC-T6 LTS, Rock 5B
> >>>> Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea at collabora.com>
> >>>> ---
> >>>>  drivers/gpu/drm/drm_connector.c | 31 ++++++++++++---
> >>>>  include/drm/drm_connector.h     | 83 +++++++++++++++++++++++++++++++++++++++++
> >>>>  2 files changed, 109 insertions(+), 5 deletions(-)
> >>>>
> >>>> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> >>>> index 4721cdeafc84..a18410faf040 100644
> >>>> --- a/drivers/gpu/drm/drm_connector.c
> >>>> +++ b/drivers/gpu/drm/drm_connector.c
> >>>> @@ -622,12 +622,29 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
> >>>>  	 * 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)
> >>>> +	if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_2_0) {
> >>>> +		if (!hdmi_funcs->scrambler_enable || !hdmi_funcs->scrambler_disable) {
> >>>> +			drm_err(dev, "Scrambler callbacks missing for HDMI 2.x\n");
> >>>> +			return -EINVAL;
> >>>> +		}
> >>>> +
> >>>>  		connector->hdmi.max_tmds_char_rate = HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ;
> >>>> -	else if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_1_3)
> >>>> -		connector->hdmi.max_tmds_char_rate = HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ;
> >>>> -	else if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_1_0)
> >>>> -		connector->hdmi.max_tmds_char_rate = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ;
> >>>> +	} else {
> >>>> +		/*
> >>>> +		 * Scrambler callbacks are only valid for connectors advertising
> >>>> +		 * HDMI 2.0 capability. drm_connector_hdmi_scrambler_supported()
> >>>> +		 * relies on their presence to report scrambling support.
> >>>> +		 */
> >>>> +		if (hdmi_funcs->scrambler_enable || hdmi_funcs->scrambler_disable) {
> >>>> +			drm_err(dev, "Scrambler callbacks unexpected for HDMI 1.x\n");
> >>>> +			return -EINVAL;
> >>>> +		}
> >>>> +
> >>>> +		if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_1_3)
> >>>> +			connector->hdmi.max_tmds_char_rate = HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ;
> >>>> +		else if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_1_0)
> >>>> +			connector->hdmi.max_tmds_char_rate = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ;
> >>>> +	}
> >>>
> >>> I'd put it into a separate test (possibly earlier). Merging both the
> >>> tmds rate default and the scrambler callbacks check makes it messier
> >>> than it would be if we had two separate tests.
> >>
> >> Ack. How about the following?
> >>
> >> 	if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_2_0)
> >> 		connector->hdmi.max_tmds_char_rate = HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ;
> >> 	else if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_1_3)
> >> 		connector->hdmi.max_tmds_char_rate = HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ;
> >> 	else if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_1_0)
> >> 		connector->hdmi.max_tmds_char_rate = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ;
> >>
> >> 	if (hdmi_funcs->supported_tmds_char_rate) {
> >> 		if (hdmi_funcs->supported_tmds_char_rate > connector->hdmi.max_tmds_char_rate) {
> >> 			drm_err(dev, "Enforced max_tmds_char_rate exceeds %llu spec limit\n",
> >> 				connector->hdmi.max_tmds_char_rate);
> >> 			return -EINVAL;
> >> 		}
> >>
> >> 		connector->hdmi.max_tmds_char_rate = hdmi_funcs->supported_tmds_char_rate;
> >> 	}
> >>
> >> 	if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_2_0) {
> >> 		if (!hdmi_funcs->scrambler_enable || !hdmi_funcs->scrambler_disable) {
> >> 			drm_err(dev, "Scrambler callbacks missing for HDMI 2.x\n");
> >> 			return -EINVAL;
> >> 		}
> >> 	} else {
> >> 		/*
> >> 		 * Scrambler callbacks are only valid for connectors advertising
> >> 		 * HDMI 2.0 capability. drm_connector_hdmi_scrambler_supported()
> >> 		 * relies on their presence to report scrambling support.
> >> 		 */
> >> 		if (hdmi_funcs->scrambler_enable || hdmi_funcs->scrambler_disable) {
> >> 			drm_err(dev, "Scrambler callbacks unexpected for HDMI 1.x\n");
> >> 			return -EINVAL;
> >> 		}
> >> 	}
> > 
> > I don't think we need the else clause at all. It's not valid, but it's
> > also not creating any issue.
> 
> As discussed a while ago, we used to have a scrambler_supported flag, inferred
> from supported_hdmi_ver, which allowed helpers to verify the capability when
> needed.  That flag has now been removed and replaced by
> drm_connector_hdmi_scrambler_supported(), which relies exclusively on the
> presence of the scrambler callbacks to report whether the capability is
> supported. 
> 
> If we don't ensure that these callbacks are *not* set for HDMI 1.x cases, one
> could set supported_hdmi_ver to HDMI_VERSION_1_4, for example, while still
> providing the scrambler_{enable,disable} funcs.  This would lead to an
> inconsistency between the maximum TMDS character rate inferred from
> supported_hdmi_ver and the capability reported by
> drm_connector_hdmi_scrambler_supported().

This is the problem then. scrambler is mandatory for HDMI2.0, and
HDMI1.4 will never reach HDMI2.0 TMDS rates.

scrambler supported is HDMI 2.0 and scrambler_enable and
scrambler_disable are set. if HDMI 1.4 is used, then the scrambler must
not be supported, ever.

> > I'd move that second check earlier together with the infoframe callbacks
> > checks and so on too.
> 
> Ack.
> 
> >>>>  	if (hdmi_funcs->supported_tmds_char_rate) {
> >>>>  		if (hdmi_funcs->supported_tmds_char_rate > connector->hdmi.max_tmds_char_rate) {
> >>>> @@ -635,6 +652,7 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
> >>>>  				connector->hdmi.max_tmds_char_rate);
> >>>>  			return -EINVAL;
> >>>>  		}
> >>>> +
> >>>>  		connector->hdmi.max_tmds_char_rate = hdmi_funcs->supported_tmds_char_rate;
> >>>>  	}
> >>
> >> [...]
> >>
> >>>> +	/**
> >>>> +	 * @scdc_work: Work item currently used to monitor sink-side scrambling
> >>>> +	 * status and retry setup if the sink resets it.
> >>>> +	 */
> >>>> +	struct delayed_work scdc_work;
> >>>> +
> >>>> +	/**
> >>>> +	 * @scdc_work_initialized: Tracks whether @scdc_work has been set up via
> >>>> +	 * INIT_DELAYED_WORK(). The work item is initialized lazily on the first
> >>>> +	 * scrambling enable, so this guards the teardown paths against touching
> >>>> +	 * an uninitialized work item.
> >>>> +	 */
> >>>> +	bool scdc_work_initialized;
> >>>> +
> >>>
> >>> Why should we track whether it's initialized or not? I'd always
> >>> initialize it, but only ever schedule something if we're using the
> >>> scrambler.
> >>
> >> Having this initialized in the connector would lead to a module dependency
> >> cycle.
> >>
> >> Currently the work function lives in drm_hdmi_helper.c, which is built into
> >> drm_display_helper module:
> >>
> >> static void drm_connector_hdmi_scdc_work(struct work_struct *work)
> >> {
> >> 	[...]
> >> 	if (READ_ONCE(connector->hdmi.scrambler_enabled) &&
> >> 	    !drm_scdc_get_scrambling_status(connector))
> >> 		drm_connector_hdmi_try_scrambling_setup(connector);
> >> 	[...]
> >> }
> >>
> >> int drm_connector_hdmi_enable_scrambling(struct drm_connector *connector,
> >> 					 const struct drm_connector_state *conn_state)
> >> {
> >>
> >> 	[...]
> >> 	if (!hdmi->scdc_work_initialized) {
> >> 		INIT_DELAYED_WORK(&hdmi->scdc_work,
> >> 				  drm_connector_hdmi_scdc_work);
> >> 		hdmi->scdc_work_initialized = true;
> >> 	}
> >> 	[...]
> >> }
> >>
> >> If we move INIT_DELAYED_WORK() into the connector (i.e. in drm.ko), the work
> >> function has to be reachable from there.  The following attempts to accomplish
> >> that would fail:
> >>
> >> - Keep the work function in drm_hdmi_helper.c and export it from
> >>   drm_display_helper.
> >>
> >> - Move the work function into drm_connector.c and export 
> >>   drm_connector_hdmi_try_scrambling_setup(), or a wrapper function, from 
> >>   drm_display_helper.
> > 
> > An alternative could be to move drm_connector_hdmi_init to
> > drm_hdmi_helper.c, no?
> 
> I haven't considered this option so far, as I believe it would also require some
> refactoring to get right - for example, moving HDMI-related initialization from
> the generic drm_connector_init_only() to drm_connector_hdmi_init(), and
> splitting drm_connector_cleanup() into a dedicated drm_connector_hdmi_cleanup()
> utility.
> 
> > But yeah, if we can't let's keep it like that
> 
> Should I proceed with this refactoring, or would it be better to postpone it
> until I send out the HDMI 2.1 patches, to avoid expanding this series even
> further?

we can postpone it if you prefer, or even to a separate series

Maxime
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 273 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20260825/85ae5114/attachment.sig>


More information about the linux-arm-kernel mailing list