All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure
@ 2026-10-12 22:37 Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 1/8] drm/vc4: hdmi: Use common TMDS char rate constants Cristian Ciocaltea
                   ` (7 more replies)
  0 siblings, 8 replies; 13+ messages in thread
From: Cristian Ciocaltea @ 2026-10-12 22:37 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

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.

The infrastructure patches have been merged and there are no remaining
dependencies, so the vc4 patches are now sent as a separate series,
rebased on top of the latest drm-misc-next.

Tested on a Raspberry Pi 5 Model B Rev 1.1 (BCM2712 D0).

[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>
---
Changes in v2:
- Added new patch "Bump BCM2835 max pixel clock to 165 MHz" (Dave)
- Simplified the last patch "Defer pixel clock validation to HDMI
  helpers" by dropping the now unneeded adjustment of
  supported_tmds_char_rate in vc4_hdmi_connector_funcs_hdmi12
- Rebased series onto latest drm-misc-next
- Link to v1: https://patch.msgid.link/20261001-vc4-common-scramb-v1-0-51280460194b@collabora.com

---
Cristian Ciocaltea (8):
      drm/vc4: hdmi: Use common TMDS char rate constants
      drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz
      drm/vc4: hdmi: Switch to drm_hdmi_mode_needs_scrambling()
      drm/vc4: hdmi: Switch to .force_ctx() connector helper
      drm/vc4: hdmi: Propagate -EDEADLK to the top level
      drm/vc4: hdmi: Convert to drmm_connector_hdmi_init()
      drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure
      drm/vc4: hdmi: Defer pixel clock validation to HDMI helpers

 drivers/gpu/drm/vc4/vc4_hdmi.c | 360 ++++++++++-------------------------------
 drivers/gpu/drm/vc4/vc4_hdmi.h |  30 +---
 2 files changed, 89 insertions(+), 301 deletions(-)
---
base-commit: 3e32932d3626d0279f9b3531bd648f8c0a16cb08
change-id: 20261001-vc4-common-scramb-798b2580fa81


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 1/8] drm/vc4: hdmi: Use common TMDS char rate constants
  2026-10-12 22:37 [PATCH v2 0/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
@ 2026-10-12 22:37 ` Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 2/8] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz Cristian Ciocaltea
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Cristian Ciocaltea @ 2026-10-12 22:37 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

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 3a90dbb61f4e..8e4623ccc4f8 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -45,6 +45,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>
@@ -113,8 +114,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;
@@ -137,7 +136,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)
@@ -1728,7 +1727,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 */
@@ -3254,7 +3253,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);
@@ -3384,7 +3383,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	= {
@@ -3413,7 +3412,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	= {
@@ -3442,7 +3441,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	= {
@@ -3469,7 +3468,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 related	[flat|nested] 13+ messages in thread

* [PATCH v2 2/8] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz
  2026-10-12 22:37 [PATCH v2 0/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 1/8] drm/vc4: hdmi: Use common TMDS char rate constants Cristian Ciocaltea
@ 2026-10-12 22:37 ` Cristian Ciocaltea
  2026-10-12 22:48   ` sashiko-bot
  2026-10-13  0:16   ` Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 3/8] drm/vc4: hdmi: Switch to drm_hdmi_mode_needs_scrambling() Cristian Ciocaltea
                   ` (5 subsequent siblings)
  7 siblings, 2 replies; 13+ messages in thread
From: Cristian Ciocaltea @ 2026-10-12 22:37 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

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>
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 8e4623ccc4f8..e61f1b694dbe 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -1507,7 +1507,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.
@@ -3363,7 +3363,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 related	[flat|nested] 13+ messages in thread

* [PATCH v2 3/8] drm/vc4: hdmi: Switch to drm_hdmi_mode_needs_scrambling()
  2026-10-12 22:37 [PATCH v2 0/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 1/8] drm/vc4: hdmi: Use common TMDS char rate constants Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 2/8] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz Cristian Ciocaltea
@ 2026-10-12 22:37 ` Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 4/8] drm/vc4: hdmi: Switch to .force_ctx() connector helper Cristian Ciocaltea
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Cristian Ciocaltea @ 2026-10-12 22:37 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

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 e61f1b694dbe..60c2c655b2e9 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -130,15 +130,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;
@@ -313,7 +304,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) {
@@ -443,7 +434,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.");
 			}
@@ -803,7 +795,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 related	[flat|nested] 13+ messages in thread

* [PATCH v2 4/8] drm/vc4: hdmi: Switch to .force_ctx() connector helper
  2026-10-12 22:37 [PATCH v2 0/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
                   ` (2 preceding siblings ...)
  2026-10-12 22:37 ` [PATCH v2 3/8] drm/vc4: hdmi: Switch to drm_hdmi_mode_needs_scrambling() Cristian Ciocaltea
@ 2026-10-12 22:37 ` Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 5/8] drm/vc4: hdmi: Propagate -EDEADLK to the top level Cristian Ciocaltea
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Cristian Ciocaltea @ 2026-10-12 22:37 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

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 60c2c655b2e9..922a6b99891d 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -505,7 +505,6 @@ static void vc4_hdmi_connector_reset(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,
 	.reset = vc4_hdmi_connector_reset,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -514,6 +513,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 related	[flat|nested] 13+ messages in thread

* [PATCH v2 5/8] drm/vc4: hdmi: Propagate -EDEADLK to the top level
  2026-10-12 22:37 [PATCH v2 0/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
                   ` (3 preceding siblings ...)
  2026-10-12 22:37 ` [PATCH v2 4/8] drm/vc4: hdmi: Switch to .force_ctx() connector helper Cristian Ciocaltea
@ 2026-10-12 22:37 ` Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 6/8] drm/vc4: hdmi: Convert to drmm_connector_hdmi_init() Cristian Ciocaltea
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Cristian Ciocaltea @ 2026-10-12 22:37 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

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 922a6b99891d..59af0d068457 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -344,12 +344,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
@@ -368,17 +367,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,
@@ -416,10 +407,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 related	[flat|nested] 13+ messages in thread

* [PATCH v2 6/8] drm/vc4: hdmi: Convert to drmm_connector_hdmi_init()
  2026-10-12 22:37 [PATCH v2 0/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
                   ` (4 preceding siblings ...)
  2026-10-12 22:37 ` [PATCH v2 5/8] drm/vc4: hdmi: Propagate -EDEADLK to the top level Cristian Ciocaltea
@ 2026-10-12 22:37 ` Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 7/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 8/8] drm/vc4: hdmi: Defer pixel clock validation to HDMI helpers Cristian Ciocaltea
  7 siblings, 0 replies; 13+ messages in thread
From: Cristian Ciocaltea @ 2026-10-12 22:37 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

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 59af0d068457..d40517b1b3e5 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -510,7 +510,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,
@@ -518,22 +517,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;
 
@@ -1722,28 +1712,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
@@ -3359,7 +3372,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 = {
@@ -3387,8 +3400,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 = {
@@ -3416,8 +3429,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 = {
@@ -3443,8 +3456,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 = {
@@ -3470,8 +3483,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 29d461d4ee49..d5ffcc17de32 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 related	[flat|nested] 13+ messages in thread

* [PATCH v2 7/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure
  2026-10-12 22:37 [PATCH v2 0/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
                   ` (5 preceding siblings ...)
  2026-10-12 22:37 ` [PATCH v2 6/8] drm/vc4: hdmi: Convert to drmm_connector_hdmi_init() Cristian Ciocaltea
@ 2026-10-12 22:37 ` Cristian Ciocaltea
  2026-10-12 22:37 ` [PATCH v2 8/8] drm/vc4: hdmi: Defer pixel clock validation to HDMI helpers Cristian Ciocaltea
  7 siblings, 0 replies; 13+ messages in thread
From: Cristian Ciocaltea @ 2026-10-12 22:37 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

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 | 230 +++++------------------------------------
 drivers/gpu/drm/vc4/vc4_hdmi.h |  21 ----
 2 files changed, 28 insertions(+), 223 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index d40517b1b3e5..11414025fe7b 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>
@@ -114,22 +113,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;
@@ -263,115 +246,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)
@@ -383,8 +257,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
@@ -407,7 +281,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;
@@ -535,6 +410,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.
@@ -760,32 +643,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) |
@@ -794,59 +660,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;
-
-	if (delayed_work_pending(&vc4_hdmi->scrambling_work))
-		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,
@@ -891,7 +725,7 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder,
 		spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 	}
 
-	vc4_hdmi_disable_scrambling(encoder);
+	drm_connector_hdmi_disable_scrambling(&vc4_hdmi->connector);
 
 	drm_dev_exit(idx);
 
@@ -1599,6 +1433,7 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
 	struct drm_display_info *display = &vc4_hdmi->connector.display_info;
 	bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
 	bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
+	struct drm_connector_state *conn_state;
 	unsigned long flags;
 	int ret;
 	int idx;
@@ -1667,7 +1502,9 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
 	}
 
 	vc4_hdmi_recenter_fifo(vc4_hdmi);
-	vc4_hdmi_enable_scrambling(encoder);
+
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+	drm_connector_hdmi_enable_scrambling(connector, conn_state);
 
 	drm_dev_exit(idx);
 
@@ -1684,8 +1521,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);
 }
 
@@ -1755,8 +1590,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
@@ -3230,7 +3066,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;
@@ -3243,15 +3078,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 d5ffcc17de32..f6159c9e6144 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;
@@ -193,25 +191,6 @@ struct vc4_hdmi {
 	 */
 	bool packet_ram_enabled;
 
-	/**
-	 * @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 related	[flat|nested] 13+ messages in thread

* [PATCH v2 8/8] drm/vc4: hdmi: Defer pixel clock validation to HDMI helpers
  2026-10-12 22:37 [PATCH v2 0/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
                   ` (6 preceding siblings ...)
  2026-10-12 22:37 ` [PATCH v2 7/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
@ 2026-10-12 22:37 ` Cristian Ciocaltea
  7 siblings, 0 replies; 13+ messages in thread
From: Cristian Ciocaltea @ 2026-10-12 22:37 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

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 11414025fe7b..ca8712b49ffb 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -1529,12 +1529,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;
 
@@ -3185,7 +3181,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),
 
@@ -3205,7 +3200,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	= {
@@ -3234,7 +3228,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	= {
@@ -3263,7 +3256,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	= {
@@ -3290,7 +3282,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 f6159c9e6144..61486e7b4ba2 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 related	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/8] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz
  2026-10-12 22:37 ` [PATCH v2 2/8] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz Cristian Ciocaltea
@ 2026-10-12 22:48   ` sashiko-bot
  2026-10-13  0:16   ` Cristian Ciocaltea
  1 sibling, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-10-12 22:48 UTC (permalink / raw)
  To: Cristian Ciocaltea; +Cc: dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] drm/vc4: CEC register corruption via HSM clock divider overflow
--

commit ea5ddad9978e31ee47e604996aa2db29025b7ffb
Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>

drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz

This commit increases the maximum pixel clock for BCM2835 HDMI from 162 MHz
to 165 MHz. It allows users to use pixel clocks closer to the HSM clock,
which is now scaled to 101% of the pixel clock rate.

> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> index 8e4623ccc4f8d..e61f1b694dbe2 100644
> --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
[ ... ]
> @@ -3363,7 +3363,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,

[Severity: High]
Does increasing the max pixel clock to 165 MHz cause an unmasked integer
overflow that corrupts the CEC register?

When modesetting at 165 MHz, the HSM clock is scaled to 166.65 MHz (101%).
This execution flows through vc4_hdmi_encoder_pre_crtc_configure() down to
vc4_hdmi_cec_update_clk_div():

drivers/gpu/drm/vc4/vc4_hdmi.c:vc4_hdmi_cec_update_clk_div():
    clk_cnt = cec_rate / CEC_CLOCK_FREQ;
    value |= clk_cnt << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT;
    HDMI_WRITE(HDMI_CEC_CNTRL_1, value);

The CEC clock divider calculation (166.65 MHz / 40 kHz) yields 4166 (0x1046),
which exceeds the 12-bit VC4_HDMI_CEC_DIV_CLK_CNT limit (4095). Since the
value is unmasked, it spills directly into the adjacent
VC4_HDMI_CEC_ADDR_MASK field, silently breaking CEC functionality on 165 MHz
modesets.

>  	.registers		= vc4_hdmi_fields,
>  	.num_registers		= ARRAY_SIZE(vc4_hdmi_fields),

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-vc4-common-scramb-v2-0-a41d15e131ad@collabora.com?part=2

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/8] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz
  2026-10-12 22:37 ` [PATCH v2 2/8] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz Cristian Ciocaltea
  2026-10-12 22:48   ` sashiko-bot
@ 2026-10-13  0:16   ` Cristian Ciocaltea
  2026-10-13 15:59     ` Dave Stevenson
  1 sibling, 1 reply; 13+ messages in thread
From: Cristian Ciocaltea @ 2026-10-13  0:16 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

Hi Dave,

On 10/13/26 1:37 AM, Cristian Ciocaltea wrote:
> 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.

It seems this will cause CEC register corruption:

https://sashiko.dev/#/patchset/20261013-vc4-common-scramb-v2-0-a41d15e131ad@collabora.com?part=2

We can easily address this by clamping the CEC clock divider to its register
field width:

-       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);

The consequence is that the CEC clock would run slightly faster than the nominal
40 kHz, i.e. (165000 × 101/100) / (2^12 - 1) = 40.6959 kHz.

I think this is still within the CEC bit-timing tolerances and shouldn't cause
any issues.  Could you please confirm?

Regards,
Cristian

> 
> Suggested-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 8e4623ccc4f8..e61f1b694dbe 100644
> --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> @@ -1507,7 +1507,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.
> @@ -3363,7 +3363,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),
>  
> 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/8] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz
  2026-10-13  0:16   ` Cristian Ciocaltea
@ 2026-10-13 15:59     ` Dave Stevenson
  2026-10-13 16:58       ` Cristian Ciocaltea
  0 siblings, 1 reply; 13+ messages in thread
From: Dave Stevenson @ 2026-10-13 15:59 UTC (permalink / raw)
  To: Cristian Ciocaltea
  Cc: Maxime Ripard, Maíra Canal, Raspberry Pi Kernel Maintenance,
	Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
	kernel, dri-devel, linux-kernel

Hi Cristian

On Tue, 13 Oct 2026 at 01:16, Cristian Ciocaltea
<cristian.ciocaltea@collabora.com> wrote:
>
> Hi Dave,
>
> On 10/13/26 1:37 AM, Cristian Ciocaltea wrote:
> > 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.
>
> It seems this will cause CEC register corruption:
>
> https://sashiko.dev/#/patchset/20261013-vc4-common-scramb-v2-0-a41d15e131ad@collabora.com?part=2
>
> We can easily address this by clamping the CEC clock divider to its register
> field width:
>
> -       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);
>
> The consequence is that the CEC clock would run slightly faster than the nominal
> 40 kHz, i.e. (165000 × 101/100) / (2^12 - 1) = 40.6959 kHz.
>
> I think this is still within the CEC bit-timing tolerances and shouldn't cause
> any issues.  Could you please confirm?

My reading of the CEC spec agrees.

All the timings have tolerance of +/- 0.2ms on values up to 4.5ms, so
that would allow up to a 4.3% tolerance on frequency.
The error from clipping the clock divider is 1.74%, so fairly
comfortably within tolerance.

It's only video modes with clocks in the range 162-165MHz that will
have any inaccuracy, so pretty limited in scope anyway.

Working backwards, achieving a 40kHz output with the max divider works
out to be a 163.8MHz HSM clock, so that would appear to be why the
limit was introduced in the first place.

Thanks
  Dave

> Regards,
> Cristian
>
> >
> > Suggested-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 8e4623ccc4f8..e61f1b694dbe 100644
> > --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> > +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> > @@ -1507,7 +1507,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.
> > @@ -3363,7 +3363,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),
> >
> >

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/8] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz
  2026-10-13 15:59     ` Dave Stevenson
@ 2026-10-13 16:58       ` Cristian Ciocaltea
  0 siblings, 0 replies; 13+ messages in thread
From: Cristian Ciocaltea @ 2026-10-13 16:58 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: Maxime Ripard, Maíra Canal, Raspberry Pi Kernel Maintenance,
	Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
	kernel, dri-devel, linux-kernel

Hi Dave,

On 10/13/26 6:59 PM, Dave Stevenson wrote:
> Hi Cristian
> 
> On Tue, 13 Oct 2026 at 01:16, Cristian Ciocaltea
> <cristian.ciocaltea@collabora.com> wrote:
>>
>> Hi Dave,
>>
>> On 10/13/26 1:37 AM, Cristian Ciocaltea wrote:
>>> 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.
>>
>> It seems this will cause CEC register corruption:
>>
>> https://sashiko.dev/#/patchset/20261013-vc4-common-scramb-v2-0-a41d15e131ad@collabora.com?part=2
>>
>> We can easily address this by clamping the CEC clock divider to its register
>> field width:
>>
>> -       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);
>>
>> The consequence is that the CEC clock would run slightly faster than the nominal
>> 40 kHz, i.e. (165000 × 101/100) / (2^12 - 1) = 40.6959 kHz.
>>
>> I think this is still within the CEC bit-timing tolerances and shouldn't cause
>> any issues.  Could you please confirm?
> 
> My reading of the CEC spec agrees.
> 
> All the timings have tolerance of +/- 0.2ms on values up to 4.5ms, so
> that would allow up to a 4.3% tolerance on frequency.
> The error from clipping the clock divider is 1.74%, so fairly
> comfortably within tolerance.
> 
> It's only video modes with clocks in the range 162-165MHz that will
> have any inaccuracy, so pretty limited in scope anyway.

Yep, the clamp would trigger only for pixel clocks above 162.178 MHz.

> Working backwards, achieving a 40kHz output with the max divider works
> out to be a 163.8MHz HSM clock, so that would appear to be why the
> limit was introduced in the first place.

Quite possible, indeed.

So I'll prepare v3, which adds yet another prerequisite patch to clamp the CEC
clock divider.

Thanks,
Cristian

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-10-13 16:59 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-12 22:37 [PATCH v2 0/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
2026-10-12 22:37 ` [PATCH v2 1/8] drm/vc4: hdmi: Use common TMDS char rate constants Cristian Ciocaltea
2026-10-12 22:37 ` [PATCH v2 2/8] drm/vc4: hdmi: Bump BCM2835 max pixel clock to 165 MHz Cristian Ciocaltea
2026-10-12 22:48   ` sashiko-bot
2026-10-13  0:16   ` Cristian Ciocaltea
2026-10-13 15:59     ` Dave Stevenson
2026-10-13 16:58       ` Cristian Ciocaltea
2026-10-12 22:37 ` [PATCH v2 3/8] drm/vc4: hdmi: Switch to drm_hdmi_mode_needs_scrambling() Cristian Ciocaltea
2026-10-12 22:37 ` [PATCH v2 4/8] drm/vc4: hdmi: Switch to .force_ctx() connector helper Cristian Ciocaltea
2026-10-12 22:37 ` [PATCH v2 5/8] drm/vc4: hdmi: Propagate -EDEADLK to the top level Cristian Ciocaltea
2026-10-12 22:37 ` [PATCH v2 6/8] drm/vc4: hdmi: Convert to drmm_connector_hdmi_init() Cristian Ciocaltea
2026-10-12 22:37 ` [PATCH v2 7/8] drm/vc4: hdmi: Convert to common HDMI 2.0 scrambling infrastructure Cristian Ciocaltea
2026-10-12 22:37 ` [PATCH v2 8/8] drm/vc4: hdmi: Defer pixel clock validation to HDMI helpers 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.