* [PATCH v4 1/9] drm/vc4: hdmi: Use common TMDS char rate constants
2026-11-10 0:13 [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
@ 2026-11-10 0:13 ` Cristian Ciocaltea
2026-11-10 0:13 ` [PATCH v4 2/9] drm/vc4: hdmi: Switch to drm_hdmi_mode_needs_scrambling() Cristian Ciocaltea
` (8 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Cristian Ciocaltea @ 2026-11-10 0:13 UTC (permalink / raw)
To: Maxime Ripard, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
Replace HDMI_14_MAX_TMDS_CLK defined locally with
HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ provided by linux/hdmi.h. Note this
incorrectly referenced HDMI 1.4, as the 340 MHz maximum TMDS character
rate was actually introduced in HDMI 1.3.
Similarly, use HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ instead of the 600000000
magic number.
Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index b4ad8467d49c..22533f1c6207 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -44,6 +44,7 @@
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/gpio/consumer.h>
+#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -112,8 +113,6 @@
#define HSM_MIN_CLOCK_FREQ 120000000
#define CEC_CLOCK_FREQ 40000
-#define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000)
-
static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi)
{
struct drm_display_info *display = &vc4_hdmi->connector.display_info;
@@ -136,7 +135,7 @@ static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode,
{
unsigned long long clock = drm_hdmi_compute_mode_clock(mode, bpc, fmt);
- return clock > HDMI_14_MAX_TMDS_CLK;
+ return clock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ;
}
static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
@@ -1735,7 +1734,7 @@ vc4_hdmi_connector_clock_valid(const struct drm_connector *connector,
if (clock > vc4_hdmi->variant->max_pixel_clock)
return MODE_CLOCK_HIGH;
- if (!vc4->hvs->vc5_hdmi_enable_hdmi_20 && clock > HDMI_14_MAX_TMDS_CLK)
+ if (!vc4->hvs->vc5_hdmi_enable_hdmi_20 && clock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ)
return MODE_CLOCK_HIGH;
/* 4096x2160@60 is not reliable without overclocking core */
@@ -3269,7 +3268,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
* vc4_hdmi_disable_scrambling() will thus run at boot, make
* sure it's disabled, and avoid any inconsistency.
*/
- if (variant->max_pixel_clock > HDMI_14_MAX_TMDS_CLK)
+ if (variant->max_pixel_clock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ)
vc4_hdmi->scdc_enabled = true;
ret = variant->init_resources(drm, vc4_hdmi);
@@ -3399,7 +3398,7 @@ static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = {
.encoder_type = VC4_ENCODER_TYPE_HDMI0,
.debugfs_name = "hdmi0_regs",
.card_name = "vc4-hdmi-0",
- .max_pixel_clock = 600000000,
+ .max_pixel_clock = HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ,
.registers = vc5_hdmi_hdmi0_fields,
.num_registers = ARRAY_SIZE(vc5_hdmi_hdmi0_fields),
.phy_lane_mapping = {
@@ -3428,7 +3427,7 @@ static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
.encoder_type = VC4_ENCODER_TYPE_HDMI1,
.debugfs_name = "hdmi1_regs",
.card_name = "vc4-hdmi-1",
- .max_pixel_clock = HDMI_14_MAX_TMDS_CLK,
+ .max_pixel_clock = HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ,
.registers = vc5_hdmi_hdmi1_fields,
.num_registers = ARRAY_SIZE(vc5_hdmi_hdmi1_fields),
.phy_lane_mapping = {
@@ -3457,7 +3456,7 @@ static const struct vc4_hdmi_variant bcm2712_hdmi0_variant = {
.encoder_type = VC4_ENCODER_TYPE_HDMI0,
.debugfs_name = "hdmi0_regs",
.card_name = "vc4-hdmi-0",
- .max_pixel_clock = 600000000,
+ .max_pixel_clock = HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ,
.registers = vc6_hdmi_hdmi0_fields,
.num_registers = ARRAY_SIZE(vc6_hdmi_hdmi0_fields),
.phy_lane_mapping = {
@@ -3484,7 +3483,7 @@ static const struct vc4_hdmi_variant bcm2712_hdmi1_variant = {
.encoder_type = VC4_ENCODER_TYPE_HDMI1,
.debugfs_name = "hdmi1_regs",
.card_name = "vc4-hdmi-1",
- .max_pixel_clock = 600000000,
+ .max_pixel_clock = HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ,
.registers = vc6_hdmi_hdmi1_fields,
.num_registers = ARRAY_SIZE(vc6_hdmi_hdmi1_fields),
.phy_lane_mapping = {
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread* [PATCH v4 2/9] drm/vc4: hdmi: Switch to drm_hdmi_mode_needs_scrambling()
2026-11-10 0:13 [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
2026-11-10 0:13 ` [PATCH v4 1/9] drm/vc4: hdmi: Use common TMDS char rate constants Cristian Ciocaltea
@ 2026-11-10 0:13 ` Cristian Ciocaltea
2026-11-10 11:57 ` Maíra Canal
2026-11-10 0:13 ` [PATCH v4 3/9] drm/vc4: hdmi: Switch to .force_ctx() connector helper Cristian Ciocaltea
` (7 subsequent siblings)
9 siblings, 1 reply; 15+ messages in thread
From: Cristian Ciocaltea @ 2026-11-10 0:13 UTC (permalink / raw)
To: Maxime Ripard, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
The VC4 HDMI driver has a local helper to check whether a mode requires
scrambling. It is equivalent to drm_hdmi_mode_needs_scrambling(), so
use the common helper instead.
No functional change intended.
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 22533f1c6207..d9383af60906 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -129,15 +129,6 @@ static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi)
return true;
}
-static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode,
- unsigned int bpc,
- enum drm_output_color_format fmt)
-{
- unsigned long long clock = drm_hdmi_compute_mode_clock(mode, bpc, fmt);
-
- return clock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ;
-}
-
static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
{
struct drm_debugfs_entry *entry = m->private;
@@ -312,7 +303,7 @@ static int vc4_hdmi_reset_link(struct drm_connector *connector,
return 0;
}
- scrambling_needed = vc4_hdmi_mode_needs_scrambling(&vc4_hdmi->saved_adjusted_mode,
+ scrambling_needed = drm_hdmi_mode_needs_scrambling(&vc4_hdmi->saved_adjusted_mode,
vc4_hdmi->output_bpc,
vc4_hdmi->output_format);
if (!scrambling_needed) {
@@ -442,7 +433,8 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
const struct drm_display_mode *mode;
list_for_each_entry(mode, &connector->probed_modes, head) {
- if (vc4_hdmi_mode_needs_scrambling(mode, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444)) {
+ if (drm_hdmi_mode_needs_scrambling(mode, 8,
+ DRM_OUTPUT_COLOR_FORMAT_RGB444)) {
drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz.");
drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60.");
}
@@ -819,7 +811,7 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
if (!vc4_hdmi_supports_scrambling(vc4_hdmi))
return;
- if (!vc4_hdmi_mode_needs_scrambling(mode,
+ if (!drm_hdmi_mode_needs_scrambling(mode,
vc4_hdmi->output_bpc,
vc4_hdmi->output_format))
return;
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH v4 2/9] drm/vc4: hdmi: Switch to drm_hdmi_mode_needs_scrambling()
2026-11-10 0:13 ` [PATCH v4 2/9] drm/vc4: hdmi: Switch to drm_hdmi_mode_needs_scrambling() Cristian Ciocaltea
@ 2026-11-10 11:57 ` Maíra Canal
0 siblings, 0 replies; 15+ messages in thread
From: Maíra Canal @ 2026-11-10 11:57 UTC (permalink / raw)
To: Cristian Ciocaltea, Maxime Ripard, Dave Stevenson,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
On 09/11/26 21:13, Cristian Ciocaltea wrote:
> The VC4 HDMI driver has a local helper to check whether a mode requires
> scrambling. It is equivalent to drm_hdmi_mode_needs_scrambling(), so
> use the common helper instead.
>
> No functional change intended.
>
> Acked-by: Maxime Ripard <mripard@kernel.org>
> Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Reviewed-by: Maíra Canal <mcanal@igalia.com>
Best regards,
- Maíra
> ---
> drivers/gpu/drm/vc4/vc4_hdmi.c | 16 ++++------------
> 1 file changed, 4 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> index 22533f1c6207..d9383af60906 100644
> --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> @@ -129,15 +129,6 @@ static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi)
> return true;
> }
>
> -static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode,
> - unsigned int bpc,
> - enum drm_output_color_format fmt)
> -{
> - unsigned long long clock = drm_hdmi_compute_mode_clock(mode, bpc, fmt);
> -
> - return clock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ;
> -}
> -
> static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
> {
> struct drm_debugfs_entry *entry = m->private;
> @@ -312,7 +303,7 @@ static int vc4_hdmi_reset_link(struct drm_connector *connector,
> return 0;
> }
>
> - scrambling_needed = vc4_hdmi_mode_needs_scrambling(&vc4_hdmi->saved_adjusted_mode,
> + scrambling_needed = drm_hdmi_mode_needs_scrambling(&vc4_hdmi->saved_adjusted_mode,
> vc4_hdmi->output_bpc,
> vc4_hdmi->output_format);
> if (!scrambling_needed) {
> @@ -442,7 +433,8 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
> const struct drm_display_mode *mode;
>
> list_for_each_entry(mode, &connector->probed_modes, head) {
> - if (vc4_hdmi_mode_needs_scrambling(mode, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444)) {
> + if (drm_hdmi_mode_needs_scrambling(mode, 8,
> + DRM_OUTPUT_COLOR_FORMAT_RGB444)) {
> drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz.");
> drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60.");
> }
> @@ -819,7 +811,7 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
> if (!vc4_hdmi_supports_scrambling(vc4_hdmi))
> return;
>
> - if (!vc4_hdmi_mode_needs_scrambling(mode,
> + if (!drm_hdmi_mode_needs_scrambling(mode,
> vc4_hdmi->output_bpc,
> vc4_hdmi->output_format))
> return;
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 3/9] drm/vc4: hdmi: Switch to .force_ctx() connector helper
2026-11-10 0:13 [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
2026-11-10 0:13 ` [PATCH v4 1/9] drm/vc4: hdmi: Use common TMDS char rate constants Cristian Ciocaltea
2026-11-10 0:13 ` [PATCH v4 2/9] drm/vc4: hdmi: Switch to drm_hdmi_mode_needs_scrambling() Cristian Ciocaltea
@ 2026-11-10 0:13 ` Cristian Ciocaltea
2026-11-10 0:13 ` [PATCH v4 4/9] drm/vc4: hdmi: Propagate -EDEADLK to the top level Cristian Ciocaltea
` (6 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Cristian Ciocaltea @ 2026-11-10 0:13 UTC (permalink / raw)
To: Maxime Ripard, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
Use drm_atomic_helper_connector_hdmi_force_ctx() as the .force_ctx()
implementation, so that the acquire context created by the probe helper
reaches the HDMI connector framework.
This is a prerequisite for converting the driver to the common HDMI 2.0
scrambling infrastructure, which implicitly handles SCDC state recovery
through a CRTC reset when the sink reconnects while the display pipeline
is active. Note that this does not interfere with the local SCDC
state-management implementation, since the driver's scrambler capability
has not yet been exposed through the common helpers.
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index d9383af60906..0e6696644687 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -514,7 +514,6 @@ vc4_hdmi_connector_create_state(struct drm_connector *connector)
static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
.debugfs_init = drm_hdmi_connector_debugfs_init,
- .force = drm_atomic_helper_connector_hdmi_force,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_create_state = vc4_hdmi_connector_create_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -523,6 +522,7 @@ static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
.detect_ctx = vc4_hdmi_connector_detect_ctx,
+ .force_ctx = drm_atomic_helper_connector_hdmi_force_ctx,
.get_modes = vc4_hdmi_connector_get_modes,
.atomic_check = vc4_hdmi_connector_atomic_check,
.mode_valid = drm_hdmi_connector_mode_valid,
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread* [PATCH v4 4/9] drm/vc4: hdmi: Propagate -EDEADLK to the top level
2026-11-10 0:13 [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
` (2 preceding siblings ...)
2026-11-10 0:13 ` [PATCH v4 3/9] drm/vc4: hdmi: Switch to .force_ctx() connector helper Cristian Ciocaltea
@ 2026-11-10 0:13 ` Cristian Ciocaltea
2026-11-10 12:08 ` Maíra Canal
2026-11-10 0:13 ` [PATCH v4 5/9] drm/vc4: hdmi: Convert to drmm_connector_hdmi_init() Cristian Ciocaltea
` (5 subsequent siblings)
9 siblings, 1 reply; 15+ messages in thread
From: Cristian Ciocaltea @ 2026-11-10 0:13 UTC (permalink / raw)
To: Maxime Ripard, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
drm_connector_helper_funcs.detect_ctx() is expected to propagate
-EDEADLK so that context owners, e.g. drm_helper_probe_detect_ctx(), can
perform the required backoff and retry.
vc4_hdmi_handle_hotplug() instead runs its own retry loop and calls
drm_modeset_backoff() on an acquire context it does not own. This drops
all locks in the context, including connection_mutex held by the core.
Drop the local retry logic and propagate -EDEADLK to the caller instead.
This aligns VC4 with the expected detect_ctx() semantics.
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 23 +++++++----------------
1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 0e6696644687..fc9d5fe07849 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -343,12 +343,11 @@ static int vc4_hdmi_reset_link(struct drm_connector *connector,
return drm_atomic_helper_reset_crtc(crtc, ctx);
}
-static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
- struct drm_modeset_acquire_ctx *ctx,
- enum drm_connector_status status)
+static int vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
+ struct drm_modeset_acquire_ctx *ctx,
+ enum drm_connector_status status)
{
struct drm_connector *connector = &vc4_hdmi->connector;
- int ret;
/*
* NOTE: This function should really be called with vc4_hdmi->mutex
@@ -367,17 +366,9 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
drm_atomic_helper_connector_hdmi_hotplug(connector, ctx, status);
if (status != connector_status_connected)
- return;
+ return 0;
- for (;;) {
- ret = vc4_hdmi_reset_link(connector, ctx);
- if (ret == -EDEADLK) {
- drm_modeset_backoff(ctx);
- continue;
- }
-
- break;
- }
+ return vc4_hdmi_reset_link(connector, ctx);
}
static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
@@ -415,10 +406,10 @@ static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
status = connector_status_connected;
}
- vc4_hdmi_handle_hotplug(vc4_hdmi, ctx, status);
+ ret = vc4_hdmi_handle_hotplug(vc4_hdmi, ctx, status);
pm_runtime_put(&vc4_hdmi->pdev->dev);
- return status;
+ return ret == -EDEADLK ? ret : status;
}
static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH v4 4/9] drm/vc4: hdmi: Propagate -EDEADLK to the top level
2026-11-10 0:13 ` [PATCH v4 4/9] drm/vc4: hdmi: Propagate -EDEADLK to the top level Cristian Ciocaltea
@ 2026-11-10 12:08 ` Maíra Canal
0 siblings, 0 replies; 15+ messages in thread
From: Maíra Canal @ 2026-11-10 12:08 UTC (permalink / raw)
To: Cristian Ciocaltea, Maxime Ripard, Dave Stevenson,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
On 09/11/26 21:13, Cristian Ciocaltea wrote:
> drm_connector_helper_funcs.detect_ctx() is expected to propagate
> -EDEADLK so that context owners, e.g. drm_helper_probe_detect_ctx(), can
> perform the required backoff and retry.
>
> vc4_hdmi_handle_hotplug() instead runs its own retry loop and calls
> drm_modeset_backoff() on an acquire context it does not own. This drops
> all locks in the context, including connection_mutex held by the core.
>
> Drop the local retry logic and propagate -EDEADLK to the caller instead.
> This aligns VC4 with the expected detect_ctx() semantics.
>
> Acked-by: Maxime Ripard <mripard@kernel.org>
> Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Reviewed-by: Maíra Canal <mcanal@igalia.com>
Best regards,
- Maíra
> ---
> drivers/gpu/drm/vc4/vc4_hdmi.c | 23 +++++++----------------
> 1 file changed, 7 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> index 0e6696644687..fc9d5fe07849 100644
> --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> @@ -343,12 +343,11 @@ static int vc4_hdmi_reset_link(struct drm_connector *connector,
> return drm_atomic_helper_reset_crtc(crtc, ctx);
> }
>
> -static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
> - struct drm_modeset_acquire_ctx *ctx,
> - enum drm_connector_status status)
> +static int vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
> + struct drm_modeset_acquire_ctx *ctx,
> + enum drm_connector_status status)
> {
> struct drm_connector *connector = &vc4_hdmi->connector;
> - int ret;
>
> /*
> * NOTE: This function should really be called with vc4_hdmi->mutex
> @@ -367,17 +366,9 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
> drm_atomic_helper_connector_hdmi_hotplug(connector, ctx, status);
>
> if (status != connector_status_connected)
> - return;
> + return 0;
>
> - for (;;) {
> - ret = vc4_hdmi_reset_link(connector, ctx);
> - if (ret == -EDEADLK) {
> - drm_modeset_backoff(ctx);
> - continue;
> - }
> -
> - break;
> - }
> + return vc4_hdmi_reset_link(connector, ctx);
> }
>
> static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
> @@ -415,10 +406,10 @@ static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
> status = connector_status_connected;
> }
>
> - vc4_hdmi_handle_hotplug(vc4_hdmi, ctx, status);
> + ret = vc4_hdmi_handle_hotplug(vc4_hdmi, ctx, status);
> pm_runtime_put(&vc4_hdmi->pdev->dev);
>
> - return status;
> + return ret == -EDEADLK ? ret : status;
> }
>
> static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 5/9] drm/vc4: hdmi: Convert to drmm_connector_hdmi_init()
2026-11-10 0:13 [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
` (3 preceding siblings ...)
2026-11-10 0:13 ` [PATCH v4 4/9] drm/vc4: hdmi: Propagate -EDEADLK to the top level Cristian Ciocaltea
@ 2026-11-10 0:13 ` Cristian Ciocaltea
2026-11-10 0:13 ` [PATCH v4 6/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
` (4 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Cristian Ciocaltea @ 2026-11-10 0:13 UTC (permalink / raw)
To: Maxime Ripard, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
Switch driver over to the new drmm_connector_hdmi_init() signature. The
vendor, product, supported_formats and max_bpc values previously passed
as arguments are now provided through struct drm_connector_hdmi_funcs,
together with the new supported_hdmi_ver field.
VC4 needs three distinct capability sets, based on the maximum TMDS
character rate and color depth supported by each controller variant:
RPi 0-3: 165 MHz (HDMI 1.2), 8-bit
RPi 4 (HDMI1): 340 MHz (HDMI 1.4), 12-bit
RPi 4 (HDMI0), RPi 5: 600 MHz (HDMI 2.0), 12-bit
Introduce three drm_connector_hdmi_funcs instances for these tiers and
assign them to the corresponding vc4_hdmi_variant entries. Additionally,
drop the now redundant .supports_hdr field of struct vc4_hdmi_variant.
As the callback members are identical across all three, factor the
shared initializers into a VC4_HDMI_CONNECTOR_FUNCS_COMMON macro to
avoid duplication, with each variant adding only its distinguishing
max_bpc/supported_hdmi_ver fields.
Note that HDMI 2.0 cannot be advertised at this point, since it requires
converting the driver to common scrambling infrastructure. This will be
handled separately, hence temporarily use HDMI_VERSION_UNKNOWN for the
related hdmi_funcs instance to skip generic scrambler and TMDS rate
validation, and continue to rely on current driver-local implementation.
No functional change intended.
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 93 ++++++++++++++++++++++++------------------
drivers/gpu/drm/vc4/vc4_hdmi.h | 6 +--
2 files changed, 56 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index fc9d5fe07849..c3636ed60d56 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -519,7 +519,6 @@ static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs =
.mode_valid = drm_hdmi_connector_mode_valid,
};
-static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs;
static const struct drm_connector_hdmi_audio_funcs vc4_hdmi_audio_funcs;
static int vc4_hdmi_connector_init(struct drm_device *dev,
@@ -527,22 +526,13 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
{
struct drm_connector *connector = &vc4_hdmi->connector;
struct drm_encoder *encoder = &vc4_hdmi->encoder.base;
- unsigned int max_bpc = 8;
int ret;
- if (vc4_hdmi->variant->supports_hdr)
- max_bpc = 12;
-
- ret = drmm_connector_hdmi_ini2(dev, connector,
- "Broadcom", "Videocore",
+ ret = drmm_connector_hdmi_init(dev, connector,
&vc4_hdmi_connector_funcs,
- &vc4_hdmi_hdmi_connector_funcs,
+ vc4_hdmi->variant->hdmi_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- vc4_hdmi->ddc,
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444),
- max_bpc);
+ vc4_hdmi->ddc);
if (ret)
return ret;
@@ -1729,28 +1719,51 @@ vc4_hdmi_connector_clock_valid(const struct drm_connector *connector,
return MODE_OK;
}
-static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs = {
- .tmds_char_rate_valid = vc4_hdmi_connector_clock_valid,
- .avi = {
- .clear_infoframe = vc4_hdmi_clear_avi_infoframe,
- .write_infoframe = vc4_hdmi_write_avi_infoframe,
- },
- .hdmi = {
- .clear_infoframe = vc4_hdmi_clear_hdmi_infoframe,
- .write_infoframe = vc4_hdmi_write_hdmi_infoframe,
- },
- .audio = {
- .clear_infoframe = vc4_hdmi_clear_audio_infoframe,
- .write_infoframe = vc4_hdmi_write_audio_infoframe,
- },
- .hdr_drm = {
- .clear_infoframe = vc4_hdmi_clear_hdr_drm_infoframe,
- .write_infoframe = vc4_hdmi_write_hdr_drm_infoframe,
- },
- .spd = {
- .clear_infoframe = vc4_hdmi_clear_spd_infoframe,
- .write_infoframe = vc4_hdmi_write_spd_infoframe,
- },
+#define VC4_HDMI_CONNECTOR_FUNCS_COMMON \
+ .vendor = "Broadcom", \
+ .product = "Videocore", \
+ .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | \
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | \
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444), \
+ .tmds_char_rate_valid = vc4_hdmi_connector_clock_valid, \
+ .avi = { \
+ .clear_infoframe = vc4_hdmi_clear_avi_infoframe, \
+ .write_infoframe = vc4_hdmi_write_avi_infoframe, \
+ }, \
+ .hdmi = { \
+ .clear_infoframe = vc4_hdmi_clear_hdmi_infoframe, \
+ .write_infoframe = vc4_hdmi_write_hdmi_infoframe, \
+ }, \
+ .audio = { \
+ .clear_infoframe = vc4_hdmi_clear_audio_infoframe, \
+ .write_infoframe = vc4_hdmi_write_audio_infoframe, \
+ }, \
+ .hdr_drm = { \
+ .clear_infoframe = vc4_hdmi_clear_hdr_drm_infoframe, \
+ .write_infoframe = vc4_hdmi_write_hdr_drm_infoframe, \
+ }, \
+ .spd = { \
+ .clear_infoframe = vc4_hdmi_clear_spd_infoframe, \
+ .write_infoframe = vc4_hdmi_write_spd_infoframe, \
+ }
+
+static const struct drm_connector_hdmi_funcs vc4_hdmi_connector_funcs_hdmi12 = {
+ VC4_HDMI_CONNECTOR_FUNCS_COMMON,
+ .max_bpc = 8,
+ .supported_hdmi_ver = HDMI_VERSION_1_2,
+};
+
+static const struct drm_connector_hdmi_funcs vc4_hdmi_connector_funcs_hdmi14 = {
+ VC4_HDMI_CONNECTOR_FUNCS_COMMON,
+ .max_bpc = 12,
+ .supported_hdmi_ver = HDMI_VERSION_1_4,
+};
+
+static const struct drm_connector_hdmi_funcs vc4_hdmi_connector_funcs_hdmi20 = {
+ VC4_HDMI_CONNECTOR_FUNCS_COMMON,
+ .max_bpc = 12,
+ /* TODO: set HDMI_VERSION_2_0 and convert to common scrambler infra */
+ .supported_hdmi_ver = HDMI_VERSION_UNKNOWN,
};
#define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL
@@ -3374,7 +3387,7 @@ static const struct vc4_hdmi_variant bcm2835_variant = {
.phy_rng_enable = vc4_hdmi_phy_rng_enable,
.phy_rng_disable = vc4_hdmi_phy_rng_disable,
.channel_map = vc4_hdmi_channel_map,
- .supports_hdr = false,
+ .hdmi_funcs = &vc4_hdmi_connector_funcs_hdmi12,
};
static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = {
@@ -3402,8 +3415,8 @@ static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = {
.phy_rng_enable = vc5_hdmi_phy_rng_enable,
.phy_rng_disable = vc5_hdmi_phy_rng_disable,
.channel_map = vc5_hdmi_channel_map,
- .supports_hdr = true,
.hp_detect = vc5_hdmi_hp_detect,
+ .hdmi_funcs = &vc4_hdmi_connector_funcs_hdmi20,
};
static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
@@ -3431,8 +3444,8 @@ static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
.phy_rng_enable = vc5_hdmi_phy_rng_enable,
.phy_rng_disable = vc5_hdmi_phy_rng_disable,
.channel_map = vc5_hdmi_channel_map,
- .supports_hdr = true,
.hp_detect = vc5_hdmi_hp_detect,
+ .hdmi_funcs = &vc4_hdmi_connector_funcs_hdmi14,
};
static const struct vc4_hdmi_variant bcm2712_hdmi0_variant = {
@@ -3458,8 +3471,8 @@ static const struct vc4_hdmi_variant bcm2712_hdmi0_variant = {
.phy_init = vc6_hdmi_phy_init,
.phy_disable = vc6_hdmi_phy_disable,
.channel_map = vc5_hdmi_channel_map,
- .supports_hdr = true,
.hp_detect = vc5_hdmi_hp_detect,
+ .hdmi_funcs = &vc4_hdmi_connector_funcs_hdmi20,
};
static const struct vc4_hdmi_variant bcm2712_hdmi1_variant = {
@@ -3485,8 +3498,8 @@ static const struct vc4_hdmi_variant bcm2712_hdmi1_variant = {
.phy_init = vc6_hdmi_phy_init,
.phy_disable = vc6_hdmi_phy_disable,
.channel_map = vc5_hdmi_channel_map,
- .supports_hdr = true,
.hp_detect = vc5_hdmi_hp_detect,
+ .hdmi_funcs = &vc4_hdmi_connector_funcs_hdmi20,
};
static const struct of_device_id vc4_hdmi_dt_match[] = {
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
index e4ad1889cbd4..b2879c29d02e 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
@@ -90,11 +90,11 @@ struct vc4_hdmi_variant {
/* Callback to get channel map */
u32 (*channel_map)(struct vc4_hdmi *vc4_hdmi, u32 channel_mask);
- /* Enables HDR metadata */
- bool supports_hdr;
-
/* Callback for hardware specific hotplug detect */
bool (*hp_detect)(struct vc4_hdmi *vc4_hdmi);
+
+ /* HDMI-related connector callbacks and controller capabilities */
+ const struct drm_connector_hdmi_funcs *hdmi_funcs;
};
/* HDMI audio information */
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread* [PATCH v4 6/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure
2026-11-10 0:13 [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
` (4 preceding siblings ...)
2026-11-10 0:13 ` [PATCH v4 5/9] drm/vc4: hdmi: Convert to drmm_connector_hdmi_init() Cristian Ciocaltea
@ 2026-11-10 0:13 ` Cristian Ciocaltea
2026-11-10 12:11 ` Maíra Canal
2026-11-10 0:13 ` [PATCH v4 7/9] drm/vc4: hdmi: Clamp CEC clock divider to its register field width Cristian Ciocaltea
` (3 subsequent siblings)
9 siblings, 1 reply; 15+ messages in thread
From: Cristian Ciocaltea @ 2026-11-10 0:13 UTC (permalink / raw)
To: Maxime Ripard, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
Replace driver local scrambling logic with the newly introduced common
HDMI 2.0 scrambling support:
- Advertise source-side scrambling capability by setting supported HDMI
version based on the variant's max_pixel_clock.
- Provide minimal .scrambler_{enable,disable} connector callbacks that
only toggle the VC5 HDMI_SCRAMBLER_CTL register, while delegating
sink-side SCDC programming and periodic status monitoring to
drm_connector_hdmi_{enable,disable}_scrambling().
- Drop vc4_hdmi_reset_link() and vc4_hdmi_handle_hotplug(), switching
the .detect_ctx() path to drm_atomic_helper_connector_hdmi_hotplug()
which internally calls drm_connector_hdmi_sync_scdc() to trigger a
CRTC reset on reconnection.
- Drop the local scrambling_work delayed workqueue and scdc_enabled
flag, now tracked by the common drm_connector_hdmi layer, as well as
the now unused output_{bpc,color_format} driver data members.
No functional change intended.
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 226 +++++------------------------------------
drivers/gpu/drm/vc4/vc4_hdmi.h | 21 ----
2 files changed, 25 insertions(+), 222 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index c3636ed60d56..d7ef47dc4b02 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -35,7 +35,6 @@
#include <drm/display/drm_hdmi_cec_helper.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
-#include <drm/display/drm_scdc_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
@@ -113,22 +112,6 @@
#define HSM_MIN_CLOCK_FREQ 120000000
#define CEC_CLOCK_FREQ 40000
-static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi)
-{
- struct drm_display_info *display = &vc4_hdmi->connector.display_info;
-
- lockdep_assert_held(&vc4_hdmi->mutex);
-
- if (!display->is_hdmi)
- return false;
-
- if (!display->hdmi.scdc.supported ||
- !display->hdmi.scdc.scrambling.supported)
- return false;
-
- return true;
-}
-
static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
{
struct drm_debugfs_entry *entry = m->private;
@@ -262,115 +245,6 @@ static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi)
static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {}
#endif
-static int vc4_hdmi_reset_link(struct drm_connector *connector,
- struct drm_modeset_acquire_ctx *ctx)
-{
- struct drm_device *drm;
- struct vc4_hdmi *vc4_hdmi;
- struct drm_connector_state *conn_state;
- struct drm_crtc_state *crtc_state;
- struct drm_crtc *crtc;
- bool scrambling_needed;
- u8 config;
- int ret;
-
- if (!connector)
- return 0;
-
- drm = connector->dev;
- ret = drm_modeset_lock(&drm->mode_config.connection_mutex, ctx);
- if (ret)
- return ret;
-
- conn_state = connector->state;
- crtc = conn_state->crtc;
- if (!crtc)
- return 0;
-
- ret = drm_modeset_lock(&crtc->mutex, ctx);
- if (ret)
- return ret;
-
- crtc_state = crtc->state;
- if (!crtc_state->active)
- return 0;
-
- vc4_hdmi = connector_to_vc4_hdmi(connector);
- mutex_lock(&vc4_hdmi->mutex);
-
- if (!vc4_hdmi_supports_scrambling(vc4_hdmi)) {
- mutex_unlock(&vc4_hdmi->mutex);
- return 0;
- }
-
- scrambling_needed = drm_hdmi_mode_needs_scrambling(&vc4_hdmi->saved_adjusted_mode,
- vc4_hdmi->output_bpc,
- vc4_hdmi->output_format);
- if (!scrambling_needed) {
- mutex_unlock(&vc4_hdmi->mutex);
- return 0;
- }
-
- if (conn_state->commit &&
- !try_wait_for_completion(&conn_state->commit->hw_done)) {
- mutex_unlock(&vc4_hdmi->mutex);
- return 0;
- }
-
- ret = drm_scdc_readb(connector->ddc, SCDC_TMDS_CONFIG, &config);
- if (ret < 0) {
- drm_err(drm, "Failed to read TMDS config: %d\n", ret);
- mutex_unlock(&vc4_hdmi->mutex);
- return 0;
- }
-
- if (!!(config & SCDC_SCRAMBLING_ENABLE) == scrambling_needed) {
- mutex_unlock(&vc4_hdmi->mutex);
- return 0;
- }
-
- mutex_unlock(&vc4_hdmi->mutex);
-
- /*
- * HDMI 2.0 says that one should not send scrambled data
- * prior to configuring the sink scrambling, and that
- * TMDS clock/data transmission should be suspended when
- * changing the TMDS clock rate in the sink. So let's
- * just do a full modeset here, even though some sinks
- * would be perfectly happy if were to just reconfigure
- * the SCDC settings on the fly.
- */
- return drm_atomic_helper_reset_crtc(crtc, ctx);
-}
-
-static int vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
- struct drm_modeset_acquire_ctx *ctx,
- enum drm_connector_status status)
-{
- struct drm_connector *connector = &vc4_hdmi->connector;
-
- /*
- * NOTE: This function should really be called with vc4_hdmi->mutex
- * held, but doing so results in reentrancy issues since
- * cec_s_phys_addr() might call .adap_enable, which leads to that
- * funtion being called with our mutex held.
- *
- * A similar situation occurs with vc4_hdmi_reset_link() that
- * will call into our KMS hooks if the scrambling was enabled.
- *
- * Concurrency isn't an issue at the moment since we don't share
- * any state with any of the other frameworks so we can ignore
- * the lock for now.
- */
-
- drm_atomic_helper_connector_hdmi_hotplug(connector, ctx, status);
-
- if (status != connector_status_connected)
- return 0;
-
- return vc4_hdmi_reset_link(connector, ctx);
-}
-
static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx,
bool force)
@@ -382,8 +256,8 @@ static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
/*
* NOTE: This function should really take vc4_hdmi->mutex, but
* doing so results in reentrancy issues since
- * vc4_hdmi_handle_hotplug() can call into other functions that
- * would take the mutex while it's held here.
+ * drm_atomic_helper_connector_hdmi_hotplug() can call into other
+ * functions that would take the mutex while it's held here.
*
* Concurrency isn't an issue at the moment since we don't share
* any state with any of the other frameworks so we can ignore
@@ -406,7 +280,8 @@ static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
status = connector_status_connected;
}
- ret = vc4_hdmi_handle_hotplug(vc4_hdmi, ctx, status);
+ ret = drm_atomic_helper_connector_hdmi_hotplug(connector, ctx, status);
+
pm_runtime_put(&vc4_hdmi->pdev->dev);
return ret == -EDEADLK ? ret : status;
@@ -544,6 +419,14 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs);
+ /*
+ * Since we don't know the state of the controller and its
+ * display (if any), let's assume it's always enabled.
+ * drm_connector_hdmi_disable_scrambling() will thus run at boot,
+ * make sure it's disabled, and avoid any inconsistency.
+ */
+ connector->hdmi.scrambler_enabled = drm_connector_hdmi_scrambler_supported(connector);
+
/*
* Some of the properties below require access to state, like bpc.
* Allocate some default initial connector state with our reset helper.
@@ -776,32 +659,15 @@ static int vc4_hdmi_write_spd_infoframe(struct drm_connector *connector,
buffer, len);
}
-#define SCRAMBLING_POLLING_DELAY_MS 1000
-
-static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
+static int vc4_hdmi_scrambler_enable(struct drm_connector *connector)
{
- struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
- struct drm_connector *connector = &vc4_hdmi->connector;
+ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
struct drm_device *drm = connector->dev;
- const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
unsigned long flags;
int idx;
- lockdep_assert_held(&vc4_hdmi->mutex);
-
- if (!vc4_hdmi_supports_scrambling(vc4_hdmi))
- return;
-
- if (!drm_hdmi_mode_needs_scrambling(mode,
- vc4_hdmi->output_bpc,
- vc4_hdmi->output_format))
- return;
-
if (!drm_dev_enter(drm, &idx))
- return;
-
- drm_scdc_set_high_tmds_clock_ratio(connector, true);
- drm_scdc_set_scrambling(connector, true);
+ return 0;
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) |
@@ -810,58 +676,27 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
drm_dev_exit(idx);
- vc4_hdmi->scdc_enabled = true;
-
- queue_delayed_work(system_percpu_wq, &vc4_hdmi->scrambling_work,
- msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS));
+ return 0;
}
-static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder)
+static int vc4_hdmi_scrambler_disable(struct drm_connector *connector)
{
- struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
- struct drm_connector *connector = &vc4_hdmi->connector;
+ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
struct drm_device *drm = connector->dev;
unsigned long flags;
int idx;
- lockdep_assert_held(&vc4_hdmi->mutex);
-
- if (!vc4_hdmi->scdc_enabled)
- return;
-
- vc4_hdmi->scdc_enabled = false;
-
- cancel_delayed_work_sync(&vc4_hdmi->scrambling_work);
-
if (!drm_dev_enter(drm, &idx))
- return;
+ return 0;
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) &
~VC5_HDMI_SCRAMBLER_CTL_ENABLE);
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
- drm_scdc_set_scrambling(connector, false);
- drm_scdc_set_high_tmds_clock_ratio(connector, false);
-
drm_dev_exit(idx);
-}
-static void vc4_hdmi_scrambling_wq(struct work_struct *work)
-{
- struct vc4_hdmi *vc4_hdmi = container_of(to_delayed_work(work),
- struct vc4_hdmi,
- scrambling_work);
- struct drm_connector *connector = &vc4_hdmi->connector;
-
- if (drm_scdc_get_scrambling_status(connector))
- return;
-
- drm_scdc_set_high_tmds_clock_ratio(connector, true);
- drm_scdc_set_scrambling(connector, true);
-
- queue_delayed_work(system_percpu_wq, &vc4_hdmi->scrambling_work,
- msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS));
+ return 0;
}
static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder,
@@ -931,7 +766,7 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder,
if (vc4_hdmi->variant->phy_disable)
vc4_hdmi->variant->phy_disable(vc4_hdmi);
- vc4_hdmi_disable_scrambling(encoder);
+ drm_connector_hdmi_disable_scrambling(&vc4_hdmi->connector);
clk_disable_unprepare(vc4_hdmi->pixel_bvb_clock);
clk_disable_unprepare(vc4_hdmi->pixel_clock);
@@ -1540,7 +1375,7 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
goto err_disable_pixel_clock;
}
- vc4_hdmi_enable_scrambling(encoder);
+ drm_connector_hdmi_enable_scrambling(connector, conn_state);
if (vc4_hdmi->variant->phy_init)
vc4_hdmi->variant->phy_init(vc4_hdmi, conn_state);
@@ -1691,8 +1526,6 @@ static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder,
mutex_lock(&vc4_hdmi->mutex);
drm_mode_copy(&vc4_hdmi->saved_adjusted_mode,
&crtc_state->adjusted_mode);
- vc4_hdmi->output_bpc = conn_state->hdmi.output_bpc;
- vc4_hdmi->output_format = conn_state->hdmi.output_format;
mutex_unlock(&vc4_hdmi->mutex);
}
@@ -1762,8 +1595,9 @@ static const struct drm_connector_hdmi_funcs vc4_hdmi_connector_funcs_hdmi14 = {
static const struct drm_connector_hdmi_funcs vc4_hdmi_connector_funcs_hdmi20 = {
VC4_HDMI_CONNECTOR_FUNCS_COMMON,
.max_bpc = 12,
- /* TODO: set HDMI_VERSION_2_0 and convert to common scrambler infra */
- .supported_hdmi_ver = HDMI_VERSION_UNKNOWN,
+ .supported_hdmi_ver = HDMI_VERSION_2_0,
+ .scrambler_enable = vc4_hdmi_scrambler_enable,
+ .scrambler_disable = vc4_hdmi_scrambler_disable,
};
#define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL
@@ -3245,7 +3079,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
return ret;
spin_lock_init(&vc4_hdmi->hw_lock);
- INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq);
dev_set_drvdata(dev, vc4_hdmi);
encoder = &vc4_hdmi->encoder.base;
@@ -3258,15 +3091,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
vc4_hdmi->pdev = pdev;
vc4_hdmi->variant = variant;
- /*
- * Since we don't know the state of the controller and its
- * display (if any), let's assume it's always enabled.
- * vc4_hdmi_disable_scrambling() will thus run at boot, make
- * sure it's disabled, and avoid any inconsistency.
- */
- if (variant->max_pixel_clock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ)
- vc4_hdmi->scdc_enabled = true;
-
ret = variant->init_resources(drm, vc4_hdmi);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
index b2879c29d02e..e6cd40acc843 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
@@ -118,8 +118,6 @@ struct vc4_hdmi {
struct vc4_encoder encoder;
struct drm_connector connector;
- struct delayed_work scrambling_work;
-
struct i2c_adapter *ddc;
void __iomem *hdmicore_regs;
void __iomem *hd_regs;
@@ -187,25 +185,6 @@ struct vc4_hdmi {
*/
struct drm_display_mode saved_adjusted_mode;
- /**
- * @scdc_enabled: Is the HDMI controller currently running with
- * the scrambler on? Protected by @mutex.
- */
- bool scdc_enabled;
-
- /**
- * @output_bpc: Copy of @drm_connector_state.hdmi.output_bpc for
- * use outside of KMS hooks. Protected by @mutex.
- */
- unsigned int output_bpc;
-
- /**
- * @output_format: Copy of
- * @drm_connector_state.hdmi.output_format for use outside of
- * KMS hooks. Protected by @mutex.
- */
- enum drm_output_color_format output_format;
-
/**
* @hdmi_jack: Represents the connection state of the HDMI plug, for
* ALSA jack detection.
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH v4 6/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure
2026-11-10 0:13 ` [PATCH v4 6/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
@ 2026-11-10 12:11 ` Maíra Canal
2026-11-10 12:51 ` Cristian Ciocaltea
0 siblings, 1 reply; 15+ messages in thread
From: Maíra Canal @ 2026-11-10 12:11 UTC (permalink / raw)
To: Cristian Ciocaltea, Maxime Ripard, Dave Stevenson,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
On 09/11/26 21:13, Cristian Ciocaltea wrote:
> Replace driver local scrambling logic with the newly introduced common
> HDMI 2.0 scrambling support:
>
> - Advertise source-side scrambling capability by setting supported HDMI
> version based on the variant's max_pixel_clock.
>
> - Provide minimal .scrambler_{enable,disable} connector callbacks that
> only toggle the VC5 HDMI_SCRAMBLER_CTL register, while delegating
> sink-side SCDC programming and periodic status monitoring to
> drm_connector_hdmi_{enable,disable}_scrambling().
>
> - Drop vc4_hdmi_reset_link() and vc4_hdmi_handle_hotplug(), switching
> the .detect_ctx() path to drm_atomic_helper_connector_hdmi_hotplug()
> which internally calls drm_connector_hdmi_sync_scdc() to trigger a
> CRTC reset on reconnection.
>
> - Drop the local scrambling_work delayed workqueue and scdc_enabled
> flag, now tracked by the common drm_connector_hdmi layer, as well as
> the now unused output_{bpc,color_format} driver data members.
>
> No functional change intended.
>
> Acked-by: Maxime Ripard <mripard@kernel.org>
> Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Reviewed-by: Maíra Canal <mcanal@igalia.com>
Feel free to push the series to drm-misc-next. Thank you!
Best regards,
- Maíra
> ---
> drivers/gpu/drm/vc4/vc4_hdmi.c | 226 +++++------------------------------------
> drivers/gpu/drm/vc4/vc4_hdmi.h | 21 ----
> 2 files changed, 25 insertions(+), 222 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> index c3636ed60d56..d7ef47dc4b02 100644
> --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> @@ -35,7 +35,6 @@
> #include <drm/display/drm_hdmi_cec_helper.h>
> #include <drm/display/drm_hdmi_helper.h>
> #include <drm/display/drm_hdmi_state_helper.h>
> -#include <drm/display/drm_scdc_helper.h>
> #include <drm/drm_atomic_helper.h>
> #include <drm/drm_drv.h>
> #include <drm/drm_edid.h>
> @@ -113,22 +112,6 @@
> #define HSM_MIN_CLOCK_FREQ 120000000
> #define CEC_CLOCK_FREQ 40000
>
> -static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi)
> -{
> - struct drm_display_info *display = &vc4_hdmi->connector.display_info;
> -
> - lockdep_assert_held(&vc4_hdmi->mutex);
> -
> - if (!display->is_hdmi)
> - return false;
> -
> - if (!display->hdmi.scdc.supported ||
> - !display->hdmi.scdc.scrambling.supported)
> - return false;
> -
> - return true;
> -}
> -
> static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
> {
> struct drm_debugfs_entry *entry = m->private;
> @@ -262,115 +245,6 @@ static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi)
> static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {}
> #endif
>
> -static int vc4_hdmi_reset_link(struct drm_connector *connector,
> - struct drm_modeset_acquire_ctx *ctx)
> -{
> - struct drm_device *drm;
> - struct vc4_hdmi *vc4_hdmi;
> - struct drm_connector_state *conn_state;
> - struct drm_crtc_state *crtc_state;
> - struct drm_crtc *crtc;
> - bool scrambling_needed;
> - u8 config;
> - int ret;
> -
> - if (!connector)
> - return 0;
> -
> - drm = connector->dev;
> - ret = drm_modeset_lock(&drm->mode_config.connection_mutex, ctx);
> - if (ret)
> - return ret;
> -
> - conn_state = connector->state;
> - crtc = conn_state->crtc;
> - if (!crtc)
> - return 0;
> -
> - ret = drm_modeset_lock(&crtc->mutex, ctx);
> - if (ret)
> - return ret;
> -
> - crtc_state = crtc->state;
> - if (!crtc_state->active)
> - return 0;
> -
> - vc4_hdmi = connector_to_vc4_hdmi(connector);
> - mutex_lock(&vc4_hdmi->mutex);
> -
> - if (!vc4_hdmi_supports_scrambling(vc4_hdmi)) {
> - mutex_unlock(&vc4_hdmi->mutex);
> - return 0;
> - }
> -
> - scrambling_needed = drm_hdmi_mode_needs_scrambling(&vc4_hdmi->saved_adjusted_mode,
> - vc4_hdmi->output_bpc,
> - vc4_hdmi->output_format);
> - if (!scrambling_needed) {
> - mutex_unlock(&vc4_hdmi->mutex);
> - return 0;
> - }
> -
> - if (conn_state->commit &&
> - !try_wait_for_completion(&conn_state->commit->hw_done)) {
> - mutex_unlock(&vc4_hdmi->mutex);
> - return 0;
> - }
> -
> - ret = drm_scdc_readb(connector->ddc, SCDC_TMDS_CONFIG, &config);
> - if (ret < 0) {
> - drm_err(drm, "Failed to read TMDS config: %d\n", ret);
> - mutex_unlock(&vc4_hdmi->mutex);
> - return 0;
> - }
> -
> - if (!!(config & SCDC_SCRAMBLING_ENABLE) == scrambling_needed) {
> - mutex_unlock(&vc4_hdmi->mutex);
> - return 0;
> - }
> -
> - mutex_unlock(&vc4_hdmi->mutex);
> -
> - /*
> - * HDMI 2.0 says that one should not send scrambled data
> - * prior to configuring the sink scrambling, and that
> - * TMDS clock/data transmission should be suspended when
> - * changing the TMDS clock rate in the sink. So let's
> - * just do a full modeset here, even though some sinks
> - * would be perfectly happy if were to just reconfigure
> - * the SCDC settings on the fly.
> - */
> - return drm_atomic_helper_reset_crtc(crtc, ctx);
> -}
> -
> -static int vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
> - struct drm_modeset_acquire_ctx *ctx,
> - enum drm_connector_status status)
> -{
> - struct drm_connector *connector = &vc4_hdmi->connector;
> -
> - /*
> - * NOTE: This function should really be called with vc4_hdmi->mutex
> - * held, but doing so results in reentrancy issues since
> - * cec_s_phys_addr() might call .adap_enable, which leads to that
> - * funtion being called with our mutex held.
> - *
> - * A similar situation occurs with vc4_hdmi_reset_link() that
> - * will call into our KMS hooks if the scrambling was enabled.
> - *
> - * Concurrency isn't an issue at the moment since we don't share
> - * any state with any of the other frameworks so we can ignore
> - * the lock for now.
> - */
> -
> - drm_atomic_helper_connector_hdmi_hotplug(connector, ctx, status);
> -
> - if (status != connector_status_connected)
> - return 0;
> -
> - return vc4_hdmi_reset_link(connector, ctx);
> -}
> -
> static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
> struct drm_modeset_acquire_ctx *ctx,
> bool force)
> @@ -382,8 +256,8 @@ static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
> /*
> * NOTE: This function should really take vc4_hdmi->mutex, but
> * doing so results in reentrancy issues since
> - * vc4_hdmi_handle_hotplug() can call into other functions that
> - * would take the mutex while it's held here.
> + * drm_atomic_helper_connector_hdmi_hotplug() can call into other
> + * functions that would take the mutex while it's held here.
> *
> * Concurrency isn't an issue at the moment since we don't share
> * any state with any of the other frameworks so we can ignore
> @@ -406,7 +280,8 @@ static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
> status = connector_status_connected;
> }
>
> - ret = vc4_hdmi_handle_hotplug(vc4_hdmi, ctx, status);
> + ret = drm_atomic_helper_connector_hdmi_hotplug(connector, ctx, status);
> +
> pm_runtime_put(&vc4_hdmi->pdev->dev);
>
> return ret == -EDEADLK ? ret : status;
> @@ -544,6 +419,14 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
>
> drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs);
>
> + /*
> + * Since we don't know the state of the controller and its
> + * display (if any), let's assume it's always enabled.
> + * drm_connector_hdmi_disable_scrambling() will thus run at boot,
> + * make sure it's disabled, and avoid any inconsistency.
> + */
> + connector->hdmi.scrambler_enabled = drm_connector_hdmi_scrambler_supported(connector);
> +
> /*
> * Some of the properties below require access to state, like bpc.
> * Allocate some default initial connector state with our reset helper.
> @@ -776,32 +659,15 @@ static int vc4_hdmi_write_spd_infoframe(struct drm_connector *connector,
> buffer, len);
> }
>
> -#define SCRAMBLING_POLLING_DELAY_MS 1000
> -
> -static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
> +static int vc4_hdmi_scrambler_enable(struct drm_connector *connector)
> {
> - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> - struct drm_connector *connector = &vc4_hdmi->connector;
> + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
> struct drm_device *drm = connector->dev;
> - const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
> unsigned long flags;
> int idx;
>
> - lockdep_assert_held(&vc4_hdmi->mutex);
> -
> - if (!vc4_hdmi_supports_scrambling(vc4_hdmi))
> - return;
> -
> - if (!drm_hdmi_mode_needs_scrambling(mode,
> - vc4_hdmi->output_bpc,
> - vc4_hdmi->output_format))
> - return;
> -
> if (!drm_dev_enter(drm, &idx))
> - return;
> -
> - drm_scdc_set_high_tmds_clock_ratio(connector, true);
> - drm_scdc_set_scrambling(connector, true);
> + return 0;
>
> spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
> HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) |
> @@ -810,58 +676,27 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
>
> drm_dev_exit(idx);
>
> - vc4_hdmi->scdc_enabled = true;
> -
> - queue_delayed_work(system_percpu_wq, &vc4_hdmi->scrambling_work,
> - msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS));
> + return 0;
> }
>
> -static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder)
> +static int vc4_hdmi_scrambler_disable(struct drm_connector *connector)
> {
> - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> - struct drm_connector *connector = &vc4_hdmi->connector;
> + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
> struct drm_device *drm = connector->dev;
> unsigned long flags;
> int idx;
>
> - lockdep_assert_held(&vc4_hdmi->mutex);
> -
> - if (!vc4_hdmi->scdc_enabled)
> - return;
> -
> - vc4_hdmi->scdc_enabled = false;
> -
> - cancel_delayed_work_sync(&vc4_hdmi->scrambling_work);
> -
> if (!drm_dev_enter(drm, &idx))
> - return;
> + return 0;
>
> spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
> HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) &
> ~VC5_HDMI_SCRAMBLER_CTL_ENABLE);
> spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
>
> - drm_scdc_set_scrambling(connector, false);
> - drm_scdc_set_high_tmds_clock_ratio(connector, false);
> -
> drm_dev_exit(idx);
> -}
>
> -static void vc4_hdmi_scrambling_wq(struct work_struct *work)
> -{
> - struct vc4_hdmi *vc4_hdmi = container_of(to_delayed_work(work),
> - struct vc4_hdmi,
> - scrambling_work);
> - struct drm_connector *connector = &vc4_hdmi->connector;
> -
> - if (drm_scdc_get_scrambling_status(connector))
> - return;
> -
> - drm_scdc_set_high_tmds_clock_ratio(connector, true);
> - drm_scdc_set_scrambling(connector, true);
> -
> - queue_delayed_work(system_percpu_wq, &vc4_hdmi->scrambling_work,
> - msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS));
> + return 0;
> }
>
> static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder,
> @@ -931,7 +766,7 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder,
> if (vc4_hdmi->variant->phy_disable)
> vc4_hdmi->variant->phy_disable(vc4_hdmi);
>
> - vc4_hdmi_disable_scrambling(encoder);
> + drm_connector_hdmi_disable_scrambling(&vc4_hdmi->connector);
>
> clk_disable_unprepare(vc4_hdmi->pixel_bvb_clock);
> clk_disable_unprepare(vc4_hdmi->pixel_clock);
> @@ -1540,7 +1375,7 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
> goto err_disable_pixel_clock;
> }
>
> - vc4_hdmi_enable_scrambling(encoder);
> + drm_connector_hdmi_enable_scrambling(connector, conn_state);
>
> if (vc4_hdmi->variant->phy_init)
> vc4_hdmi->variant->phy_init(vc4_hdmi, conn_state);
> @@ -1691,8 +1526,6 @@ static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder,
> mutex_lock(&vc4_hdmi->mutex);
> drm_mode_copy(&vc4_hdmi->saved_adjusted_mode,
> &crtc_state->adjusted_mode);
> - vc4_hdmi->output_bpc = conn_state->hdmi.output_bpc;
> - vc4_hdmi->output_format = conn_state->hdmi.output_format;
> mutex_unlock(&vc4_hdmi->mutex);
> }
>
> @@ -1762,8 +1595,9 @@ static const struct drm_connector_hdmi_funcs vc4_hdmi_connector_funcs_hdmi14 = {
> static const struct drm_connector_hdmi_funcs vc4_hdmi_connector_funcs_hdmi20 = {
> VC4_HDMI_CONNECTOR_FUNCS_COMMON,
> .max_bpc = 12,
> - /* TODO: set HDMI_VERSION_2_0 and convert to common scrambler infra */
> - .supported_hdmi_ver = HDMI_VERSION_UNKNOWN,
> + .supported_hdmi_ver = HDMI_VERSION_2_0,
> + .scrambler_enable = vc4_hdmi_scrambler_enable,
> + .scrambler_disable = vc4_hdmi_scrambler_disable,
> };
>
> #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL
> @@ -3245,7 +3079,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
> return ret;
>
> spin_lock_init(&vc4_hdmi->hw_lock);
> - INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq);
>
> dev_set_drvdata(dev, vc4_hdmi);
> encoder = &vc4_hdmi->encoder.base;
> @@ -3258,15 +3091,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
> vc4_hdmi->pdev = pdev;
> vc4_hdmi->variant = variant;
>
> - /*
> - * Since we don't know the state of the controller and its
> - * display (if any), let's assume it's always enabled.
> - * vc4_hdmi_disable_scrambling() will thus run at boot, make
> - * sure it's disabled, and avoid any inconsistency.
> - */
> - if (variant->max_pixel_clock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ)
> - vc4_hdmi->scdc_enabled = true;
> -
> ret = variant->init_resources(drm, vc4_hdmi);
> if (ret)
> return ret;
> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
> index b2879c29d02e..e6cd40acc843 100644
> --- a/drivers/gpu/drm/vc4/vc4_hdmi.h
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
> @@ -118,8 +118,6 @@ struct vc4_hdmi {
> struct vc4_encoder encoder;
> struct drm_connector connector;
>
> - struct delayed_work scrambling_work;
> -
> struct i2c_adapter *ddc;
> void __iomem *hdmicore_regs;
> void __iomem *hd_regs;
> @@ -187,25 +185,6 @@ struct vc4_hdmi {
> */
> struct drm_display_mode saved_adjusted_mode;
>
> - /**
> - * @scdc_enabled: Is the HDMI controller currently running with
> - * the scrambler on? Protected by @mutex.
> - */
> - bool scdc_enabled;
> -
> - /**
> - * @output_bpc: Copy of @drm_connector_state.hdmi.output_bpc for
> - * use outside of KMS hooks. Protected by @mutex.
> - */
> - unsigned int output_bpc;
> -
> - /**
> - * @output_format: Copy of
> - * @drm_connector_state.hdmi.output_format for use outside of
> - * KMS hooks. Protected by @mutex.
> - */
> - enum drm_output_color_format output_format;
> -
> /**
> * @hdmi_jack: Represents the connection state of the HDMI plug, for
> * ALSA jack detection.
>
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH v4 6/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure
2026-11-10 12:11 ` Maíra Canal
@ 2026-11-10 12:51 ` Cristian Ciocaltea
0 siblings, 0 replies; 15+ messages in thread
From: Cristian Ciocaltea @ 2026-11-10 12:51 UTC (permalink / raw)
To: Maíra Canal, Maxime Ripard, Dave Stevenson,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
Hi Maíra,
On 11/10/26 2:11 PM, Maíra Canal wrote:
>
>
> On 09/11/26 21:13, Cristian Ciocaltea wrote:
>> Replace driver local scrambling logic with the newly introduced common
>> HDMI 2.0 scrambling support:
>>
>> - Advertise source-side scrambling capability by setting supported HDMI
>> version based on the variant's max_pixel_clock.
>>
>> - Provide minimal .scrambler_{enable,disable} connector callbacks that
>> only toggle the VC5 HDMI_SCRAMBLER_CTL register, while delegating
>> sink-side SCDC programming and periodic status monitoring to
>> drm_connector_hdmi_{enable,disable}_scrambling().
>>
>> - Drop vc4_hdmi_reset_link() and vc4_hdmi_handle_hotplug(), switching
>> the .detect_ctx() path to drm_atomic_helper_connector_hdmi_hotplug()
>> which internally calls drm_connector_hdmi_sync_scdc() to trigger a
>> CRTC reset on reconnection.
>>
>> - Drop the local scrambling_work delayed workqueue and scdc_enabled
>> flag, now tracked by the common drm_connector_hdmi layer, as well as
>> the now unused output_{bpc,color_format} driver data members.
>>
>> No functional change intended.
>>
>> Acked-by: Maxime Ripard <mripard@kernel.org>
>> Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
>
> Reviewed-by: Maíra Canal <mcanal@igalia.com>
Thank you for your quick review!
> Feel free to push the series to drm-misc-next. Thank you!
Done.
Kind regards,
Cristian
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 7/9] drm/vc4: hdmi: Clamp CEC clock divider to its register field width
2026-11-10 0:13 [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
` (5 preceding siblings ...)
2026-11-10 0:13 ` [PATCH v4 6/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
@ 2026-11-10 0:13 ` Cristian Ciocaltea
2026-11-10 0:13 ` [PATCH v4 8/9] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz Cristian Ciocaltea
` (2 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Cristian Ciocaltea @ 2026-11-10 0:13 UTC (permalink / raw)
To: Maxime Ripard, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure, Sashiko
The CEC clock divider computed in vc4_hdmi_cec_update_clk_div() is
written unmasked into the 12-bit DIV_CLK_CNT field of the CEC_CNTRL_1
register. On BCM2835, the CEC clock is obtained by dividing the HSM
clock, which in turn is scaled to 101% of the pixel clock. For HSM
clock rates above 163.8 MHz, the divider exceeds the field limit (4095),
corrupting the adjacent CEC_ADDR field and truncating the effective
divider, which breaks CEC entirely.
The BCM2835 maximum pixel clock of 162 MHz yields an HSM clock of 163.62
MHz, so the overflow cannot currently be triggered.
Clamp the divider to the field maximum. This is a prerequisite for
raising the BCM2835 maximum pixel clock to 165 MHz, which pushes the HSM
clock up to 166.65 MHz. The CEC bit clock will then run up to ~1.75%
faster than the nominal 40 kHz, i.e. ~40.7 kHz, which is still within
the CEC bit-timing tolerances.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/sashiko-outbox-168686@kernel.org/
Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index d7ef47dc4b02..04712e975051 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -230,9 +230,14 @@ static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi)
/*
* Set the clock divider: the hsm_clock rate and this divider
- * setting will give a 40 kHz CEC clock.
+ * setting will give a 40 kHz CEC clock. Clamp the divider to
+ * its 12-bit field to avoid corrupting CEC_ADDR; for HSM rates
+ * above 163.8 MHz, the CEC clock runs marginally faster, but
+ * still within CEC timing tolerances.
*/
- clk_cnt = cec_rate / CEC_CLOCK_FREQ;
+ clk_cnt = min_t(unsigned long, cec_rate / CEC_CLOCK_FREQ,
+ VC4_HDMI_CEC_DIV_CLK_CNT_MASK >>
+ VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
value |= clk_cnt << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT;
HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread* [PATCH v4 8/9] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz
2026-11-10 0:13 [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
` (6 preceding siblings ...)
2026-11-10 0:13 ` [PATCH v4 7/9] drm/vc4: hdmi: Clamp CEC clock divider to its register field width Cristian Ciocaltea
@ 2026-11-10 0:13 ` Cristian Ciocaltea
2026-11-10 0:13 ` [PATCH v4 9/9] drm/vc4: hdmi: Defer pixel clock validation to HDMI helpers Cristian Ciocaltea
2026-11-10 12:48 ` [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
9 siblings, 0 replies; 15+ messages in thread
From: Cristian Ciocaltea @ 2026-11-10 0:13 UTC (permalink / raw)
To: Maxime Ripard, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
Historically, the HSM clock for BCM2835 was fixed at 163.7 MHz. Since
the pixel clock had to be at least 1% below the HSM clock, it was capped
at 162 MHz.
Since commit cd4cb49dc5bb ("drm/vc4: hdmi: Adjust HSM clock rate
depending on pixel rate"), the HSM clock rate is set to pixel clock *
101 / 100. For a 165 MHz pixel clock, this gives an HSM rate of 166.65
MHz, which, per Dave Stevenson, the hardware can handle without issues.
Increase the max pixel clock to 165 MHz, which matches the maximum TMDS
character rate of HDMI 1.0 through 1.2.
Suggested-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 04712e975051..29e608e4d610 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -1333,7 +1333,7 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
* users are able to use pixel clocks closer to HSM's, namely for
* 1920x1200@60Hz. So it was decided to have leave a 1% margin between
* both clocks. Which, for RPi0-3 implies a maximum pixel clock of
- * 162MHz.
+ * 165MHz.
*
* Additionally, the AXI clock needs to be at least 25% of
* pixel clock, but HSM ends up being the limiting factor.
@@ -3203,7 +3203,7 @@ static const struct vc4_hdmi_variant bcm2835_variant = {
.encoder_type = VC4_ENCODER_TYPE_HDMI0,
.debugfs_name = "hdmi_regs",
.card_name = "vc4-hdmi",
- .max_pixel_clock = 162000000,
+ .max_pixel_clock = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ,
.registers = vc4_hdmi_fields,
.num_registers = ARRAY_SIZE(vc4_hdmi_fields),
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread* [PATCH v4 9/9] drm/vc4: hdmi: Defer pixel clock validation to HDMI helpers
2026-11-10 0:13 [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
` (7 preceding siblings ...)
2026-11-10 0:13 ` [PATCH v4 8/9] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz Cristian Ciocaltea
@ 2026-11-10 0:13 ` Cristian Ciocaltea
2026-11-10 12:48 ` [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
9 siblings, 0 replies; 15+ messages in thread
From: Cristian Ciocaltea @ 2026-11-10 0:13 UTC (permalink / raw)
To: Maxime Ripard, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure
drmm_connector_hdmi_init() now derives the maximum TMDS character rate
from the supported HDMI version, and
drm_atomic_helper_connector_hdmi_check() rejects modes exceeding it
before invoking the driver's .tmds_char_rate_valid() hook.
Since each of the chip variants reports a supported HDMI version
matching its hardware limit, the max_pixel_clock check in
vc4_hdmi_connector_clock_valid() is redundant. Drop it together with
the max_pixel_clock field of struct vc4_hdmi_variant.
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/vc4/vc4_hdmi.c | 9 ---------
drivers/gpu/drm/vc4/vc4_hdmi.h | 3 ---
2 files changed, 12 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 29e608e4d610..f3ec65d86078 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -1539,12 +1539,8 @@ vc4_hdmi_connector_clock_valid(const struct drm_connector *connector,
const struct drm_display_mode *mode,
unsigned long long clock)
{
- const struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
struct vc4_dev *vc4 = to_vc4_dev(connector->dev);
- if (clock > vc4_hdmi->variant->max_pixel_clock)
- return MODE_CLOCK_HIGH;
-
if (!vc4->hvs->vc5_hdmi_enable_hdmi_20 && clock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ)
return MODE_CLOCK_HIGH;
@@ -3203,7 +3199,6 @@ static const struct vc4_hdmi_variant bcm2835_variant = {
.encoder_type = VC4_ENCODER_TYPE_HDMI0,
.debugfs_name = "hdmi_regs",
.card_name = "vc4-hdmi",
- .max_pixel_clock = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ,
.registers = vc4_hdmi_fields,
.num_registers = ARRAY_SIZE(vc4_hdmi_fields),
@@ -3223,7 +3218,6 @@ static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = {
.encoder_type = VC4_ENCODER_TYPE_HDMI0,
.debugfs_name = "hdmi0_regs",
.card_name = "vc4-hdmi-0",
- .max_pixel_clock = HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ,
.registers = vc5_hdmi_hdmi0_fields,
.num_registers = ARRAY_SIZE(vc5_hdmi_hdmi0_fields),
.phy_lane_mapping = {
@@ -3252,7 +3246,6 @@ static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
.encoder_type = VC4_ENCODER_TYPE_HDMI1,
.debugfs_name = "hdmi1_regs",
.card_name = "vc4-hdmi-1",
- .max_pixel_clock = HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ,
.registers = vc5_hdmi_hdmi1_fields,
.num_registers = ARRAY_SIZE(vc5_hdmi_hdmi1_fields),
.phy_lane_mapping = {
@@ -3281,7 +3274,6 @@ static const struct vc4_hdmi_variant bcm2712_hdmi0_variant = {
.encoder_type = VC4_ENCODER_TYPE_HDMI0,
.debugfs_name = "hdmi0_regs",
.card_name = "vc4-hdmi-0",
- .max_pixel_clock = HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ,
.registers = vc6_hdmi_hdmi0_fields,
.num_registers = ARRAY_SIZE(vc6_hdmi_hdmi0_fields),
.phy_lane_mapping = {
@@ -3308,7 +3300,6 @@ static const struct vc4_hdmi_variant bcm2712_hdmi1_variant = {
.encoder_type = VC4_ENCODER_TYPE_HDMI1,
.debugfs_name = "hdmi1_regs",
.card_name = "vc4-hdmi-1",
- .max_pixel_clock = HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ,
.registers = vc6_hdmi_hdmi1_fields,
.num_registers = ARRAY_SIZE(vc6_hdmi_hdmi1_fields),
.phy_lane_mapping = {
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
index e6cd40acc843..b1f41d52919b 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
@@ -29,9 +29,6 @@ struct vc4_hdmi_variant {
/* Filename to expose the registers in debugfs */
const char *debugfs_name;
- /* Maximum pixel clock supported by the controller (in Hz) */
- unsigned long long max_pixel_clock;
-
/* List of the registers available on that variant */
const struct vc4_hdmi_register *registers;
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure
2026-11-10 0:13 [PATCH v4 0/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
` (8 preceding siblings ...)
2026-11-10 0:13 ` [PATCH v4 9/9] drm/vc4: hdmi: Defer pixel clock validation to HDMI helpers Cristian Ciocaltea
@ 2026-11-10 12:48 ` Cristian Ciocaltea
9 siblings, 0 replies; 15+ messages in thread
From: Cristian Ciocaltea @ 2026-11-10 12:48 UTC (permalink / raw)
To: Maxime Ripard, Dave Stevenson, Maíra Canal,
Raspberry Pi Kernel Maintenance, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter,
Cristian Ciocaltea
Cc: kernel, dri-devel, linux-kernel, Lucas Tanure, Sashiko
On Tue, 10 Nov 2026 02:13:37 +0200, Cristian Ciocaltea wrote:
> This series includes patches 42-48 from "[PATCH v11 00/74] Add HDMI 2.0
> support to DW HDMI QP TX" [1], which convert the VC4 HDMI driver to the
> common HDMI 2.0 scrambling infrastructure as a proof of concept for
> reusing the shared code. It adopts the shared TMDS character rate
> constants, switches to drm_hdmi_mode_needs_scrambling() and force_ctx(),
> fixes -EDEADLK handling, and replaces the driver-local scrambling
> implementation with the common SCDC scrambling helpers.
>
> [...]
Applied, thanks!
[1/9] drm/vc4: hdmi: Use common TMDS char rate constants
commit: 946fe5be9731d31e703f8d003c735dd85fcdc0fa
[2/9] drm/vc4: hdmi: Switch to drm_hdmi_mode_needs_scrambling()
commit: 0ed2309a731a53c712442ba7379723023b6b7582
[3/9] drm/vc4: hdmi: Switch to .force_ctx() connector helper
commit: 2cc4f77ac0a865a4bb0a7a0d2408545d43222d67
[4/9] drm/vc4: hdmi: Propagate -EDEADLK to the top level
commit: 64d51eeec09f14af2fbadff3db1c9fa67403da07
[5/9] drm/vc4: hdmi: Convert to drmm_connector_hdmi_init()
commit: 9a20b50406fd58bb557ba4cde8dfdbbb415acd34
[6/9] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure
commit: 1ab014b2002d05d68b8f86eeae7e4c1da7148b2f
[7/9] drm/vc4: hdmi: Clamp CEC clock divider to its register field width
commit: 2d371302c633487482dd3ca510dbb26d01383a3f
[8/9] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz
commit: 797dbdf80f7fa104b32216891aec9184308fe95b
[9/9] drm/vc4: hdmi: Defer pixel clock validation to HDMI helpers
commit: 1116f4aee916ca13d3e40ce87e67835f51576fcb
Best regards,
--
Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
^ permalink raw reply [flat|nested] 15+ messages in thread