* [PATCH v6 0/4] Add SCDC information to connector debugfs
@ 2026-06-11 12:57 Nicolas Frattaroli
2026-06-11 12:57 ` [PATCH v6 1/4] drm/scdc-helper: Don't use ssize_t return type for scdc_read/write Nicolas Frattaroli
` (4 more replies)
0 siblings, 5 replies; 10+ messages in thread
From: Nicolas Frattaroli @ 2026-06-11 12:57 UTC (permalink / raw)
To: Jani Nikula, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Daniel Stone, Hans Verkuil
Cc: dri-devel, linux-kernel, kernel, Nicolas Frattaroli, Daniel Stone
HDMI uses the DDC I2C bus for communicating various bits of link status
out of band with the actual HDMI video signal. This information can be
useful for debugging issues like questionable cables sabotaged by feline
teeth, Enthusiast Grade cables made of cow fencing wire, and other such
problems that ruin one's media viewing plans.
Consequently, this series exposes various bits of pertinent information
from the SCDC protocol in an HDMI connector's debugfs. To continually
poll the link status, userspace can poll the debugfs file.
---
Changes in v6:
- Fix off-by-one error in drm_scdc_read_state
- Link to v5: https://patch.msgid.link/20260604-scdc-link-health-v5-0-11173b0ac3de@collabora.com
Changes in v5:
- Read all SCDC data regardless of update flags
- Dump SCDC data as hex before the human-readable output. It's separated
with "\n----------------\n\n".
- No longer write 0 to read-only registers
- Add Reed-Solomon Corrections counter parsing
- Parsing has been kept. A desire was expressed to get this data without
any external userspace tooling, and the kernel will need to parse it
eventually anyway to set the link status.
- Functions have been made static as of right now, since external users
may do another pass over the function signatures anyway.
- Link to v4: https://patch.msgid.link/20260527-scdc-link-health-v4-0-622ea40a1f59@collabora.com
Changes in v4:
- Don't use C struct bitfields for parsing status flags. Switch to
bitwise AND for boolean flags, and FIELD_GET for multi-bit values.
- Drop the superfluous !! and parens
- Drop the __pure attributes on static functions
- Initialise stack local arrays with {}, not { 0 }.
- I've kept the print macros and %-30s format. Reason being that I don't
want to repeat the format specifier and str_yes_no(foo) a bunch, and I
like the %-30s format because it means all values are aligned with the
value of the longest field, which is 30 chars long.
- Link to v3: https://patch.msgid.link/20260526-scdc-link-health-v3-0-59e4a4aaead1@collabora.com
Changes in v3:
- Add patch to change return type of drm_scdc_read/write.
- Rework error counter reading to duplicate less code.
- Also check lane 3 counter valid flag when reading its error counter.
- Use memset to clear buf for error counters, rather than doing it in
the loop.
- Make read_error_counters not accept 0 as num_lanes; fix it up in the
caller instead.
- Link to v2: https://patch.msgid.link/20260520-scdc-link-health-v2-0-511af18cd64b@collabora.com
Changes in v2:
- Add HDMI 2.1 SCDC status reporting
- Link to v1: https://patch.msgid.link/20260415-scdc-link-health-v1-0-8e731e88eaf0@collabora.com
To: Jani Nikula <jani.nikula@linux.intel.com>
To: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
To: Maxime Ripard <mripard@kernel.org>
To: Thomas Zimmermann <tzimmermann@suse.de>
To: David Airlie <airlied@gmail.com>
To: Simona Vetter <simona@ffwll.ch>
To: Andrzej Hajda <andrzej.hajda@intel.com>
To: Neil Armstrong <neil.armstrong@linaro.org>
To: Robert Foss <rfoss@kernel.org>
To: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
To: Jonas Karlman <jonas@kwiboo.se>
To: Jernej Skrabec <jernej.skrabec@gmail.com>
To: Luca Ceresoli <luca.ceresoli@bootlin.com>
To: Daniel Stone <daniel@fooishbar.org>
To: Hans Verkuil <hverkuil+cisco@kernel.org>
Cc: dri-devel@lists.freedesktop.org
Cc: linux-kernel@vger.kernel.org
Cc: kernel@collabora.com
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
Nicolas Frattaroli (4):
drm/scdc-helper: Don't use ssize_t return type for scdc_read/write
drm/scdc-helper: Add scdc_status debugfs entry
drm/display: bridge_connector: init scdc debugfs for HDMI
drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields
drivers/gpu/drm/display/drm_bridge_connector.c | 4 +
drivers/gpu/drm/display/drm_scdc_helper.c | 285 ++++++++++++++++++++++++-
include/drm/display/drm_scdc.h | 21 +-
include/drm/display/drm_scdc_helper.h | 103 ++++++++-
4 files changed, 404 insertions(+), 9 deletions(-)
---
base-commit: 4fdfaadba04dc0f2f2490dbc91922caa290463a5
change-id: 20260413-scdc-link-health-89326013d96c
Best regards,
--
Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v6 1/4] drm/scdc-helper: Don't use ssize_t return type for scdc_read/write
2026-06-11 12:57 [PATCH v6 0/4] Add SCDC information to connector debugfs Nicolas Frattaroli
@ 2026-06-11 12:57 ` Nicolas Frattaroli
2026-06-11 12:57 ` [PATCH v6 2/4] drm/scdc-helper: Add scdc_status debugfs entry Nicolas Frattaroli
` (3 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Nicolas Frattaroli @ 2026-06-11 12:57 UTC (permalink / raw)
To: Jani Nikula, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Daniel Stone, Hans Verkuil
Cc: dri-devel, linux-kernel, kernel, Nicolas Frattaroli
drm_scdc_read and drm_scdc_write, both of which are only used within
drm_scdc_helper (although exported), use a ssize_t as their return type.
This would make sense if they returned the number of bytes read/written
on success, and negative errno otherwise. However, they return 0 on
success.
Demote them to "int" as their return type, in order to avoid needlessly
using 64 bits when less suffices.
No functional change.
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Reviewed-by: Hans Verkuil <hverkuil+cisco@kernel.org>
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
drivers/gpu/drm/display/drm_scdc_helper.c | 8 ++++----
include/drm/display/drm_scdc_helper.h | 8 ++++----
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_scdc_helper.c b/drivers/gpu/drm/display/drm_scdc_helper.c
index df878aad4a36..8403f2390ab6 100644
--- a/drivers/gpu/drm/display/drm_scdc_helper.c
+++ b/drivers/gpu/drm/display/drm_scdc_helper.c
@@ -67,8 +67,8 @@
* Returns:
* 0 on success, negative error code on failure.
*/
-ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
- size_t size)
+int drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
+ size_t size)
{
int ret;
struct i2c_msg msgs[2] = {
@@ -107,8 +107,8 @@ EXPORT_SYMBOL(drm_scdc_read);
* Returns:
* 0 on success, negative error code on failure.
*/
-ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset,
- const void *buffer, size_t size)
+int drm_scdc_write(struct i2c_adapter *adapter, u8 offset, const void *buffer,
+ size_t size)
{
struct i2c_msg msg = {
.addr = SCDC_I2C_SLAVE_ADDRESS,
diff --git a/include/drm/display/drm_scdc_helper.h b/include/drm/display/drm_scdc_helper.h
index 34600476a1b9..e9ccaeba56dd 100644
--- a/include/drm/display/drm_scdc_helper.h
+++ b/include/drm/display/drm_scdc_helper.h
@@ -31,10 +31,10 @@
struct drm_connector;
struct i2c_adapter;
-ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
- size_t size);
-ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset,
- const void *buffer, size_t size);
+int drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
+ size_t size);
+int drm_scdc_write(struct i2c_adapter *adapter, u8 offset, const void *buffer,
+ size_t size);
/**
* drm_scdc_readb - read a single byte from SCDC
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v6 2/4] drm/scdc-helper: Add scdc_status debugfs entry
2026-06-11 12:57 [PATCH v6 0/4] Add SCDC information to connector debugfs Nicolas Frattaroli
2026-06-11 12:57 ` [PATCH v6 1/4] drm/scdc-helper: Don't use ssize_t return type for scdc_read/write Nicolas Frattaroli
@ 2026-06-11 12:57 ` Nicolas Frattaroli
2026-06-11 12:57 ` [PATCH v6 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli
` (2 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Nicolas Frattaroli @ 2026-06-11 12:57 UTC (permalink / raw)
To: Jani Nikula, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Daniel Stone, Hans Verkuil
Cc: dri-devel, linux-kernel, kernel, Nicolas Frattaroli
SCDC provides status information on the current display link. At the
very least, it may be useful to expose this info through debugfs.
Add a debugfs entry for it under the connector, which displays a few
more details parsed out of the SCDC registers. A new
drm_scdc_debugfs_init function can be called by the connector
implementation to initialise the debugfs file.
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
drivers/gpu/drm/display/drm_scdc_helper.c | 184 ++++++++++++++++++++++++++++++
include/drm/display/drm_scdc_helper.h | 32 ++++++
2 files changed, 216 insertions(+)
diff --git a/drivers/gpu/drm/display/drm_scdc_helper.c b/drivers/gpu/drm/display/drm_scdc_helper.c
index 8403f2390ab6..5871fc101815 100644
--- a/drivers/gpu/drm/display/drm_scdc_helper.c
+++ b/drivers/gpu/drm/display/drm_scdc_helper.c
@@ -24,11 +24,14 @@
#include <linux/export.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/overflow.h>
#include <drm/display/drm_scdc_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_device.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
/**
@@ -55,6 +58,11 @@
#define SCDC_I2C_SLAVE_ADDRESS 0x54
+struct scdc_debugfs_priv {
+ struct drm_connector *connector;
+ struct drm_scdc_state state;
+};
+
/**
* drm_scdc_read - read a block of data from SCDC
* @adapter: I2C controller
@@ -276,3 +284,179 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector,
return true;
}
EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio);
+
+static void
+drm_scdc_parse_status0_flags(u8 val, struct drm_scdc_status_flags *flags)
+{
+ flags->clock_detected = val & SCDC_CLOCK_DETECT;
+ flags->ch0_locked = val & SCDC_CH0_LOCK;
+ flags->ch1_locked = val & SCDC_CH1_LOCK;
+ flags->ch2_locked = val & SCDC_CH2_LOCK;
+}
+
+static int drm_scdc_parse_error_counters(const u8 scdc[256], u16 counter[3])
+{
+ u8 sum = 0;
+ int i;
+
+ for (i = SCDC_ERR_DET_0_L; i <= SCDC_ERR_DET_CHECKSUM ; i++)
+ sum = wrapping_add(u8, sum, scdc[i]);
+
+ if (sum)
+ return -EPROTO;
+
+ for (i = 0; i < 3; i++) {
+ if (scdc[SCDC_ERR_DET_0_H + i * 2] & SCDC_CHANNEL_VALID)
+ counter[i] = (scdc[SCDC_ERR_DET_0_H + i * 2] &
+ ~SCDC_CHANNEL_VALID) << 8 |
+ scdc[SCDC_ERR_DET_0_L + i * 2];
+ else
+ counter[i] = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * drm_scdc_read_state - Update state from SCDC
+ * @connector: pointer to a &struct drm_connector on which to operate on
+ * @state: pointer to a &struct drm_scdc_state to fill
+ *
+ * Reads the entire 256 byte SCDC state and parses it.
+ *
+ * Returns: %0 on success, negative errno on failure.
+ */
+int drm_scdc_read_state(struct drm_connector *connector, struct drm_scdc_state *state)
+{
+ struct i2c_adapter *ddc;
+ struct drm_scdc *scdc;
+ u8 *buf = state->scdc;
+ int ret;
+
+ if (!state || !connector)
+ return -ENODEV;
+
+ scdc = &connector->display_info.hdmi.scdc;
+ ddc = connector->ddc;
+
+ if (!scdc->supported)
+ return -EOPNOTSUPP;
+
+ /* Read in 128-byte chunks, to work around DP<->HDMI converters with issues. */
+ ret = drm_scdc_read(ddc, 0, buf, 128);
+ if (ret)
+ return ret;
+
+ ret = drm_scdc_read(ddc, 128, &buf[128], 128);
+ if (ret)
+ return ret;
+
+ state->scrambling_enabled = buf[SCDC_TMDS_CONFIG] & SCDC_SCRAMBLING_ENABLE;
+ state->tmds_bclk_x40 = buf[SCDC_TMDS_CONFIG] & SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
+
+ state->scrambling_detected = buf[SCDC_SCRAMBLER_STATUS] & SCDC_SCRAMBLING_STATUS;
+
+ drm_scdc_parse_status0_flags(buf[SCDC_STATUS_FLAGS_0], &state->stf);
+ ret = drm_scdc_parse_error_counters(buf, state->error_count);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_scdc_read_state);
+
+#define scdc_print_str(_f, key, s) \
+ (seq_printf((_f), "%-30s: %s\n", (key), (s)))
+#define scdc_print_flag(_f, key, val) \
+ (scdc_print_str((_f), (key), str_yes_no((val))))
+#define scdc_print_dec(_f, key, val) \
+ (seq_printf((_f), "%-30s: %d\n", (key), (val)))
+
+static int scdc_status_show(struct seq_file *m, void *data)
+{
+ struct scdc_debugfs_priv *priv = m->private;
+ struct drm_scdc_state *st = &priv->state;
+ struct drm_connector *connector = priv->connector;
+ struct drm_scdc *scdc = &connector->display_info.hdmi.scdc;
+ int i, ret;
+
+ drm_connector_get(connector);
+
+ if (connector->status != connector_status_connected) {
+ ret = -ENODEV;
+ goto err_conn_put;
+ }
+
+ if (scdc->supported) {
+ ret = drm_scdc_read_state(connector, st);
+ if (ret)
+ goto err_conn_put;
+
+ for (i = 0; i < ARRAY_SIZE(st->scdc); i += 16)
+ seq_printf(m, "%*ph\n", 16, &st->scdc[i]);
+
+ seq_puts(m, "\n----------------\n\n");
+ }
+
+ scdc_print_flag(m, "SCDC Supported", scdc->supported);
+ if (!scdc->supported) {
+ ret = 0;
+ goto err_conn_put;
+ }
+
+ scdc_print_flag(m, "Sink Read Request Capable", scdc->read_request);
+ scdc_print_flag(m, "Scrambling Supported", scdc->scrambling.supported);
+ scdc_print_flag(m, "Low Rate Scrambling Supported", scdc->scrambling.low_rates);
+
+ drm_connector_put(connector);
+
+ scdc_print_flag(m, "Scrambling Enabled", st->scrambling_enabled);
+ scdc_print_flag(m, "Scrambling Detected", st->scrambling_detected);
+
+ if (st->tmds_bclk_x40)
+ scdc_print_str(m, "TMDS Bit Clock Ratio", "1/40");
+ else
+ scdc_print_str(m, "TMDS Bit Clock Ratio", "1/10");
+
+ scdc_print_flag(m, "Clock Detected", st->stf.clock_detected);
+ scdc_print_flag(m, "Channel 0 Locked", st->stf.ch0_locked);
+ scdc_print_flag(m, "Channel 1 Locked", st->stf.ch1_locked);
+ scdc_print_flag(m, "Channel 2 Locked", st->stf.ch2_locked);
+
+ scdc_print_dec(m, "Channel 0 Errors", st->error_count[0]);
+ scdc_print_dec(m, "Channel 1 Errors", st->error_count[1]);
+ scdc_print_dec(m, "Channel 2 Errors", st->error_count[2]);
+
+ return 0;
+
+err_conn_put:
+ drm_connector_put(connector);
+
+ return ret;
+}
+DEFINE_SHOW_ATTRIBUTE(scdc_status);
+
+/**
+ * drm_scdc_debugfs_init - Initialize scdc files in connector debugfs
+ * @connector: pointer to &struct drm_connector to operate on
+ * @root: debugfs &struct dentry for the debugfs root of @connector
+ *
+ * Creates SCDC-related debugfs files for @connector. Must be called after
+ * @root is already created.
+ */
+void drm_scdc_debugfs_init(struct drm_connector *connector, struct dentry *root)
+{
+ struct scdc_debugfs_priv *priv;
+
+ if (!root || !connector)
+ return;
+
+ priv = drmm_kzalloc(connector->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return;
+
+ priv->connector = connector;
+
+ debugfs_create_file("scdc_status", 0444, root, priv, &scdc_status_fops);
+}
+EXPORT_SYMBOL(drm_scdc_debugfs_init);
diff --git a/include/drm/display/drm_scdc_helper.h b/include/drm/display/drm_scdc_helper.h
index e9ccaeba56dd..e0b79d79e1ff 100644
--- a/include/drm/display/drm_scdc_helper.h
+++ b/include/drm/display/drm_scdc_helper.h
@@ -30,6 +30,34 @@
struct drm_connector;
struct i2c_adapter;
+struct dentry;
+
+struct drm_scdc_status_flags {
+ /* Status Register 0 */
+ bool clock_detected;
+ bool ch0_locked;
+ bool ch1_locked;
+ bool ch2_locked;
+};
+
+struct drm_scdc_state {
+ /** @stf: contents of the status flag registers */
+ struct drm_scdc_status_flags stf;
+ /** @scramling_enabled: true if TMDS scrambling is on */
+ bool scrambling_enabled;
+ /** @scrambling_detected: true if the sink actually detected scrambling */
+ bool scrambling_detected;
+ /**
+ * @tmds_bclk_x40: true if TMDS bit period is 1/40th of the TMDS
+ * clock period, false if it's 1/10th of the clock period.
+ */
+ bool tmds_bclk_x40;
+ /** @error_count: character error counts for each channel */
+ u16 error_count[3];
+
+ /** @scdc: raw SCDC data buffer */
+ u8 scdc[256];
+};
int drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
size_t size);
@@ -77,4 +105,8 @@ bool drm_scdc_get_scrambling_status(struct drm_connector *connector);
bool drm_scdc_set_scrambling(struct drm_connector *connector, bool enable);
bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, bool set);
+int drm_scdc_read_state(struct drm_connector *connector,
+ struct drm_scdc_state *state);
+void drm_scdc_debugfs_init(struct drm_connector *connector, struct dentry *root);
+
#endif
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v6 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI
2026-06-11 12:57 [PATCH v6 0/4] Add SCDC information to connector debugfs Nicolas Frattaroli
2026-06-11 12:57 ` [PATCH v6 1/4] drm/scdc-helper: Don't use ssize_t return type for scdc_read/write Nicolas Frattaroli
2026-06-11 12:57 ` [PATCH v6 2/4] drm/scdc-helper: Add scdc_status debugfs entry Nicolas Frattaroli
@ 2026-06-11 12:57 ` Nicolas Frattaroli
2026-06-11 12:58 ` [PATCH v6 4/4] drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields Nicolas Frattaroli
2026-06-13 6:57 ` [PATCH v6 0/4] Add SCDC information to connector debugfs Hans Verkuil
4 siblings, 0 replies; 10+ messages in thread
From: Nicolas Frattaroli @ 2026-06-11 12:57 UTC (permalink / raw)
To: Jani Nikula, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Daniel Stone, Hans Verkuil
Cc: dri-devel, linux-kernel, kernel, Nicolas Frattaroli, Daniel Stone
On drm_bridge_connectors that contain an HDMI bridge, initialise the
SCDC debugfs entry under the connector's debugfs root.
Reviewed-by: Daniel Stone <daniels@collabora.com>
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 92f8a2d7aab4..c081a4880317 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -25,6 +25,7 @@
#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>
/**
* DOC: overview
@@ -263,6 +264,9 @@ static void drm_bridge_connector_debugfs_init(struct drm_connector *connector,
if (bridge->funcs->debugfs_init)
bridge->funcs->debugfs_init(bridge, root);
}
+
+ if (bridge_connector->bridge_hdmi)
+ drm_scdc_debugfs_init(connector, root);
}
static struct drm_connector_state *
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v6 4/4] drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields
2026-06-11 12:57 [PATCH v6 0/4] Add SCDC information to connector debugfs Nicolas Frattaroli
` (2 preceding siblings ...)
2026-06-11 12:57 ` [PATCH v6 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli
@ 2026-06-11 12:58 ` Nicolas Frattaroli
2026-06-13 6:57 ` [PATCH v6 0/4] Add SCDC information to connector debugfs Hans Verkuil
4 siblings, 0 replies; 10+ messages in thread
From: Nicolas Frattaroli @ 2026-06-11 12:58 UTC (permalink / raw)
To: Jani Nikula, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Daniel Stone, Hans Verkuil
Cc: dri-devel, linux-kernel, kernel, Nicolas Frattaroli
HDMI 2.1 redefines previously reserved fields in SCDC for various new
uses. No version check needs to be performed, as an HDMI 2.0 sink's
reserved SCDC fields are well-defined to be 0, and any zero-ness of
these fields for an HDMI 2.0 sink is not a surprise for SCDC parsers for
HDMI 2.1.
Implement reading and outputting these fields over debugfs.
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
drivers/gpu/drm/display/drm_scdc_helper.c | 99 ++++++++++++++++++++++++++++++-
include/drm/display/drm_scdc.h | 21 ++++++-
include/drm/display/drm_scdc_helper.h | 69 ++++++++++++++++++++-
3 files changed, 182 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_scdc_helper.c b/drivers/gpu/drm/display/drm_scdc_helper.c
index 5871fc101815..7e17272d52f2 100644
--- a/drivers/gpu/drm/display/drm_scdc_helper.c
+++ b/drivers/gpu/drm/display/drm_scdc_helper.c
@@ -21,6 +21,7 @@
* DEALINGS IN THE SOFTWARE.
*/
+#include <linux/bitfield.h>
#include <linux/export.h>
#include <linux/i2c.h>
#include <linux/slab.h>
@@ -63,6 +64,38 @@ struct scdc_debugfs_priv {
struct drm_scdc_state state;
};
+static const char *drm_scdc_frl_rate_str(enum drm_scdc_frl_rate rate)
+{
+ switch (rate) {
+ case SCDC_FRL_RATE_OFF:
+ return "Off";
+ case SCDC_FRL_RATE_3X3:
+ return "3 Gbit/s x 3 lanes";
+ case SCDC_FRL_RATE_6X3:
+ return "6 Gbit/s x 3 lanes";
+ case SCDC_FRL_RATE_6X4:
+ return "6 Gbit/s x 4 lanes";
+ case SCDC_FRL_RATE_8X4:
+ return "8 Gbit/s x 4 lanes";
+ case SCDC_FRL_RATE_10X4:
+ return "10 Gbit/s x 4 lanes";
+ case SCDC_FRL_RATE_12X4:
+ return "12 Gbit/s x 4 lanes";
+ case SCDC_FRL_RATE_RESV_7:
+ case SCDC_FRL_RATE_RESV_8:
+ case SCDC_FRL_RATE_RESV_9:
+ case SCDC_FRL_RATE_RESV_10:
+ case SCDC_FRL_RATE_RESV_11:
+ case SCDC_FRL_RATE_RESV_12:
+ case SCDC_FRL_RATE_RESV_13:
+ case SCDC_FRL_RATE_RESV_14:
+ case SCDC_FRL_RATE_RESV_15:
+ return "(Reserved)";
+ default:
+ return NULL;
+ }
+}
+
/**
* drm_scdc_read - read a block of data from SCDC
* @adapter: I2C controller
@@ -292,14 +325,41 @@ drm_scdc_parse_status0_flags(u8 val, struct drm_scdc_status_flags *flags)
flags->ch0_locked = val & SCDC_CH0_LOCK;
flags->ch1_locked = val & SCDC_CH1_LOCK;
flags->ch2_locked = val & SCDC_CH2_LOCK;
+ flags->ln3_locked = val & SCDC_LN3_LOCK;
+ flags->flt_ready = val & SCDC_FLT_READY;
+ flags->dsc_fail = val & SCDC_DSC_FAIL;
+}
+
+static void
+drm_scdc_parse_status1_2_flags(u8 val_flag1, u8 val_flag2,
+ struct drm_scdc_status_flags *flags)
+{
+ flags->ln0_training_pattern = FIELD_GET(SCDC_LN_EVEN_TRAIN_PTRN, val_flag1);
+ flags->ln1_training_pattern = FIELD_GET(SCDC_LN_ODD_TRAIN_PTRN, val_flag1);
+
+ flags->ln2_training_pattern = FIELD_GET(SCDC_LN_EVEN_TRAIN_PTRN, val_flag2);
+ flags->ln3_training_pattern = FIELD_GET(SCDC_LN_ODD_TRAIN_PTRN, val_flag2);
}
-static int drm_scdc_parse_error_counters(const u8 scdc[256], u16 counter[3])
+static int drm_scdc_parse_error_counters(const u8 scdc[256], u16 counter[4],
+ unsigned int num_lanes)
{
+ u8 end_reg;
u8 sum = 0;
int i;
- for (i = SCDC_ERR_DET_0_L; i <= SCDC_ERR_DET_CHECKSUM ; i++)
+ switch (num_lanes) {
+ case 3:
+ end_reg = SCDC_ERR_DET_CHECKSUM;
+ break;
+ case 4:
+ end_reg = SCDC_ERR_DET_3_H;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = SCDC_ERR_DET_0_L; i <= end_reg; i++)
sum = wrapping_add(u8, sum, scdc[i]);
if (sum)
@@ -314,6 +374,12 @@ static int drm_scdc_parse_error_counters(const u8 scdc[256], u16 counter[3])
counter[i] = 0;
}
+ if (num_lanes == 4 && scdc[SCDC_ERR_DET_3_H] & SCDC_CHANNEL_VALID)
+ counter[3] = (scdc[SCDC_ERR_DET_3_H] & ~SCDC_CHANNEL_VALID) << 8 |
+ scdc[SCDC_ERR_DET_3_L];
+ else
+ counter[3] = 0;
+
return 0;
}
@@ -331,6 +397,7 @@ int drm_scdc_read_state(struct drm_connector *connector, struct drm_scdc_state *
struct i2c_adapter *ddc;
struct drm_scdc *scdc;
u8 *buf = state->scdc;
+ int num_lanes;
int ret;
if (!state || !connector)
@@ -356,11 +423,26 @@ int drm_scdc_read_state(struct drm_connector *connector, struct drm_scdc_state *
state->scrambling_detected = buf[SCDC_SCRAMBLER_STATUS] & SCDC_SCRAMBLING_STATUS;
+ state->rate = FIELD_GET(SCDC_FRL_RATE, buf[SCDC_CONFIG_1]);
+ num_lanes = drm_scdc_num_frl_lanes(state->rate);
+ if (num_lanes < 0)
+ return num_lanes;
+ if (!num_lanes)
+ num_lanes = 3;
+
+ state->ffe_levels = FIELD_GET(SCDC_FFE_LEVELS, buf[SCDC_CONFIG_1]);
+
drm_scdc_parse_status0_flags(buf[SCDC_STATUS_FLAGS_0], &state->stf);
- ret = drm_scdc_parse_error_counters(buf, state->error_count);
+ drm_scdc_parse_status1_2_flags(buf[SCDC_STATUS_FLAGS_1],
+ buf[SCDC_STATUS_FLAGS_2], &state->stf);
+ ret = drm_scdc_parse_error_counters(buf, state->error_count, num_lanes);
if (ret)
return ret;
+ if (num_lanes == 4 && (buf[SCDC_ERR_DET_RS_H] & SCDC_CHANNEL_VALID))
+ state->rs_corrections = (buf[SCDC_ERR_DET_RS_H] & ~SCDC_CHANNEL_VALID) << 8 |
+ buf[SCDC_ERR_DET_RS_L];
+
return 0;
}
EXPORT_SYMBOL(drm_scdc_read_state);
@@ -412,6 +494,8 @@ static int scdc_status_show(struct seq_file *m, void *data)
scdc_print_flag(m, "Scrambling Enabled", st->scrambling_enabled);
scdc_print_flag(m, "Scrambling Detected", st->scrambling_detected);
+ scdc_print_str(m, "FRL Rate", drm_scdc_frl_rate_str(st->rate));
+ scdc_print_dec(m, "FFE Levels", st->ffe_levels);
if (st->tmds_bclk_x40)
scdc_print_str(m, "TMDS Bit Clock Ratio", "1/40");
@@ -422,10 +506,19 @@ static int scdc_status_show(struct seq_file *m, void *data)
scdc_print_flag(m, "Channel 0 Locked", st->stf.ch0_locked);
scdc_print_flag(m, "Channel 1 Locked", st->stf.ch1_locked);
scdc_print_flag(m, "Channel 2 Locked", st->stf.ch2_locked);
+ if (drm_scdc_num_frl_lanes(st->rate) == 4)
+ scdc_print_flag(m, "Lane 3 Locked", st->stf.ln3_locked);
+
+ scdc_print_flag(m, "Sink Ready For Link Training", st->stf.flt_ready);
+ scdc_print_flag(m, "Sink Failed To Decode DSC", st->stf.dsc_fail);
scdc_print_dec(m, "Channel 0 Errors", st->error_count[0]);
scdc_print_dec(m, "Channel 1 Errors", st->error_count[1]);
scdc_print_dec(m, "Channel 2 Errors", st->error_count[2]);
+ if (drm_scdc_num_frl_lanes(st->rate) == 4) {
+ scdc_print_dec(m, "Lane 3 Errors", st->error_count[3]);
+ scdc_print_dec(m, "Reed-Solomon Corrections", st->rs_corrections);
+ }
return 0;
diff --git a/include/drm/display/drm_scdc.h b/include/drm/display/drm_scdc.h
index 3d58f37e8ed8..7f0b05b2f280 100644
--- a/include/drm/display/drm_scdc.h
+++ b/include/drm/display/drm_scdc.h
@@ -29,6 +29,8 @@
#define SCDC_SOURCE_VERSION 0x02
#define SCDC_UPDATE_0 0x10
+#define SCDC_RSED_UPDATE (1 << 6)
+#define SCDC_FLT_UPDATE (1 << 5)
#define SCDC_READ_REQUEST_TEST (1 << 2)
#define SCDC_CED_UPDATE (1 << 1)
#define SCDC_STATUS_UPDATE (1 << 0)
@@ -46,14 +48,25 @@
#define SCDC_CONFIG_0 0x30
#define SCDC_READ_REQUEST_ENABLE (1 << 0)
+#define SCDC_CONFIG_1 0x31
+#define SCDC_FRL_RATE 0x0f
+#define SCDC_FFE_LEVELS 0xf0
+
#define SCDC_STATUS_FLAGS_0 0x40
+#define SCDC_DSC_FAIL (1 << 7)
+#define SCDC_FLT_READY (1 << 6)
+#define SCDC_LN3_LOCK (1 << 4)
#define SCDC_CH2_LOCK (1 << 3)
#define SCDC_CH1_LOCK (1 << 2)
#define SCDC_CH0_LOCK (1 << 1)
-#define SCDC_CH_LOCK_MASK (SCDC_CH2_LOCK | SCDC_CH1_LOCK | SCDC_CH0_LOCK)
+#define SCDC_CH_LOCK_MASK (SCDC_LN3_LOCK | SCDC_CH2_LOCK | SCDC_CH1_LOCK | \
+ SCDC_CH0_LOCK)
#define SCDC_CLOCK_DETECT (1 << 0)
#define SCDC_STATUS_FLAGS_1 0x41
+#define SCDC_LN_EVEN_TRAIN_PTRN 0x0f
+#define SCDC_LN_ODD_TRAIN_PTRN 0xf0
+#define SCDC_STATUS_FLAGS_2 0x42
#define SCDC_ERR_DET_0_L 0x50
#define SCDC_ERR_DET_0_H 0x51
@@ -65,6 +78,12 @@
#define SCDC_ERR_DET_CHECKSUM 0x56
+#define SCDC_ERR_DET_3_L 0x57
+#define SCDC_ERR_DET_3_H 0x58
+
+#define SCDC_ERR_DET_RS_L 0x59
+#define SCDC_ERR_DET_RS_H 0x5a
+
#define SCDC_TEST_CONFIG_0 0xc0
#define SCDC_TEST_READ_REQUEST (1 << 7)
#define SCDC_TEST_READ_REQUEST_DELAY(x) ((x) & 0x7f)
diff --git a/include/drm/display/drm_scdc_helper.h b/include/drm/display/drm_scdc_helper.h
index e0b79d79e1ff..a3b20adaac7e 100644
--- a/include/drm/display/drm_scdc_helper.h
+++ b/include/drm/display/drm_scdc_helper.h
@@ -24,6 +24,7 @@
#ifndef DRM_SCDC_HELPER_H
#define DRM_SCDC_HELPER_H
+#include <linux/errno.h>
#include <linux/types.h>
#include <drm/display/drm_scdc.h>
@@ -38,8 +39,65 @@ struct drm_scdc_status_flags {
bool ch0_locked;
bool ch1_locked;
bool ch2_locked;
+ bool ln3_locked;
+ bool flt_ready;
+ bool dsc_fail;
+
+ /* Status Register 1 */
+ u8 ln0_training_pattern : 4;
+ u8 ln1_training_pattern : 4;
+
+ /* Status Register 2 */
+ u8 ln2_training_pattern : 4;
+ u8 ln3_training_pattern : 4;
+};
+
+enum drm_scdc_frl_rate {
+ SCDC_FRL_RATE_OFF = 0,
+ SCDC_FRL_RATE_3X3 = 1,
+ SCDC_FRL_RATE_6X3 = 2,
+ SCDC_FRL_RATE_6X4 = 3,
+ SCDC_FRL_RATE_8X4 = 4,
+ SCDC_FRL_RATE_10X4 = 5,
+ SCDC_FRL_RATE_12X4 = 6,
+ SCDC_FRL_RATE_RESV_7 = 7,
+ SCDC_FRL_RATE_RESV_8 = 8,
+ SCDC_FRL_RATE_RESV_9 = 9,
+ SCDC_FRL_RATE_RESV_10 = 10,
+ SCDC_FRL_RATE_RESV_11 = 11,
+ SCDC_FRL_RATE_RESV_12 = 12,
+ SCDC_FRL_RATE_RESV_13 = 13,
+ SCDC_FRL_RATE_RESV_14 = 14,
+ SCDC_FRL_RATE_RESV_15 = 15
};
+/**
+ * drm_scdc_num_frl_lanes - get number of lanes for a given FRL rate
+ * @rate: one of &enum drm_scdc_frl_rate
+ *
+ * For a given @rate, return the number of lanes it uses.
+ *
+ * Returns: %-EINVAL if @rate is not a valid FRL rate, or the number of lanes
+ * for a given &enum drm_scdc_frl_rate on success (including %0 for "off")
+ */
+static inline __pure int drm_scdc_num_frl_lanes(enum drm_scdc_frl_rate rate)
+{
+ switch (rate) {
+ case SCDC_FRL_RATE_OFF:
+ return 0;
+ case SCDC_FRL_RATE_3X3:
+ case SCDC_FRL_RATE_6X3:
+ return 3;
+ case SCDC_FRL_RATE_6X4:
+ case SCDC_FRL_RATE_8X4:
+ case SCDC_FRL_RATE_10X4:
+ case SCDC_FRL_RATE_12X4:
+ return 4;
+ default:
+ return -EINVAL;
+ }
+}
+
struct drm_scdc_state {
/** @stf: contents of the status flag registers */
struct drm_scdc_status_flags stf;
@@ -52,9 +110,14 @@ struct drm_scdc_state {
* clock period, false if it's 1/10th of the clock period.
*/
bool tmds_bclk_x40;
- /** @error_count: character error counts for each channel */
- u16 error_count[3];
-
+ /** @rate: FRL rate set by the source */
+ enum drm_scdc_frl_rate rate : 4;
+ /** @ffe_levels: The FFE levels for @rate set by the source */
+ u8 ffe_levels : 4;
+ /** @error_count: character error counts for each channel/link */
+ u16 error_count[4];
+ /** @rs_corrections: number of Reed-Solomon Corrections */
+ u16 rs_corrections;
/** @scdc: raw SCDC data buffer */
u8 scdc[256];
};
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v6 0/4] Add SCDC information to connector debugfs
2026-06-11 12:57 [PATCH v6 0/4] Add SCDC information to connector debugfs Nicolas Frattaroli
` (3 preceding siblings ...)
2026-06-11 12:58 ` [PATCH v6 4/4] drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields Nicolas Frattaroli
@ 2026-06-13 6:57 ` Hans Verkuil
2026-06-15 8:07 ` Nicolas Frattaroli
4 siblings, 1 reply; 10+ messages in thread
From: Hans Verkuil @ 2026-06-13 6:57 UTC (permalink / raw)
To: Nicolas Frattaroli, Jani Nikula, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Daniel Stone
Cc: dri-devel, linux-kernel, kernel, Daniel Stone
Hi Nicolas,
On 11/06/2026 14:57, Nicolas Frattaroli wrote:
> HDMI uses the DDC I2C bus for communicating various bits of link status
> out of band with the actual HDMI video signal. This information can be
> useful for debugging issues like questionable cables sabotaged by feline
> teeth, Enthusiast Grade cables made of cow fencing wire, and other such
> problems that ruin one's media viewing plans.
>
> Consequently, this series exposes various bits of pertinent information
> from the SCDC protocol in an HDMI connector's debugfs. To continually
> poll the link status, userspace can poll the debugfs file.
Something is not quite right: I've been testing this series with my i915
based laptop with HDMI connector, and I never see the scdc_status file.
And that's because CONFIG_DRM_BRIDGE_CONNECTOR is not set for my configuration.
So I think you are creating the debugfs entry in the wrong place.
I can read the SCDC from the display using edid-decode with the right /dev/i2c-X
device, so it's definitely there.
Regards,
Hans
>
> ---
> Changes in v6:
> - Fix off-by-one error in drm_scdc_read_state
> - Link to v5: https://patch.msgid.link/20260604-scdc-link-health-v5-0-11173b0ac3de@collabora.com
>
> Changes in v5:
> - Read all SCDC data regardless of update flags
> - Dump SCDC data as hex before the human-readable output. It's separated
> with "\n----------------\n\n".
> - No longer write 0 to read-only registers
> - Add Reed-Solomon Corrections counter parsing
> - Parsing has been kept. A desire was expressed to get this data without
> any external userspace tooling, and the kernel will need to parse it
> eventually anyway to set the link status.
> - Functions have been made static as of right now, since external users
> may do another pass over the function signatures anyway.
> - Link to v4: https://patch.msgid.link/20260527-scdc-link-health-v4-0-622ea40a1f59@collabora.com
>
> Changes in v4:
> - Don't use C struct bitfields for parsing status flags. Switch to
> bitwise AND for boolean flags, and FIELD_GET for multi-bit values.
> - Drop the superfluous !! and parens
> - Drop the __pure attributes on static functions
> - Initialise stack local arrays with {}, not { 0 }.
> - I've kept the print macros and %-30s format. Reason being that I don't
> want to repeat the format specifier and str_yes_no(foo) a bunch, and I
> like the %-30s format because it means all values are aligned with the
> value of the longest field, which is 30 chars long.
> - Link to v3: https://patch.msgid.link/20260526-scdc-link-health-v3-0-59e4a4aaead1@collabora.com
>
> Changes in v3:
> - Add patch to change return type of drm_scdc_read/write.
> - Rework error counter reading to duplicate less code.
> - Also check lane 3 counter valid flag when reading its error counter.
> - Use memset to clear buf for error counters, rather than doing it in
> the loop.
> - Make read_error_counters not accept 0 as num_lanes; fix it up in the
> caller instead.
> - Link to v2: https://patch.msgid.link/20260520-scdc-link-health-v2-0-511af18cd64b@collabora.com
>
> Changes in v2:
> - Add HDMI 2.1 SCDC status reporting
> - Link to v1: https://patch.msgid.link/20260415-scdc-link-health-v1-0-8e731e88eaf0@collabora.com
>
> To: Jani Nikula <jani.nikula@linux.intel.com>
> To: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> To: Maxime Ripard <mripard@kernel.org>
> To: Thomas Zimmermann <tzimmermann@suse.de>
> To: David Airlie <airlied@gmail.com>
> To: Simona Vetter <simona@ffwll.ch>
> To: Andrzej Hajda <andrzej.hajda@intel.com>
> To: Neil Armstrong <neil.armstrong@linaro.org>
> To: Robert Foss <rfoss@kernel.org>
> To: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
> To: Jonas Karlman <jonas@kwiboo.se>
> To: Jernej Skrabec <jernej.skrabec@gmail.com>
> To: Luca Ceresoli <luca.ceresoli@bootlin.com>
> To: Daniel Stone <daniel@fooishbar.org>
> To: Hans Verkuil <hverkuil+cisco@kernel.org>
> Cc: dri-devel@lists.freedesktop.org
> Cc: linux-kernel@vger.kernel.org
> Cc: kernel@collabora.com
> Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
>
> ---
> Nicolas Frattaroli (4):
> drm/scdc-helper: Don't use ssize_t return type for scdc_read/write
> drm/scdc-helper: Add scdc_status debugfs entry
> drm/display: bridge_connector: init scdc debugfs for HDMI
> drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields
>
> drivers/gpu/drm/display/drm_bridge_connector.c | 4 +
> drivers/gpu/drm/display/drm_scdc_helper.c | 285 ++++++++++++++++++++++++-
> include/drm/display/drm_scdc.h | 21 +-
> include/drm/display/drm_scdc_helper.h | 103 ++++++++-
> 4 files changed, 404 insertions(+), 9 deletions(-)
> ---
> base-commit: 4fdfaadba04dc0f2f2490dbc91922caa290463a5
> change-id: 20260413-scdc-link-health-89326013d96c
>
> Best regards,
> --
> Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v6 0/4] Add SCDC information to connector debugfs
2026-06-13 6:57 ` [PATCH v6 0/4] Add SCDC information to connector debugfs Hans Verkuil
@ 2026-06-15 8:07 ` Nicolas Frattaroli
2026-06-15 16:33 ` Maxime Ripard
0 siblings, 1 reply; 10+ messages in thread
From: Nicolas Frattaroli @ 2026-06-15 8:07 UTC (permalink / raw)
To: Jani Nikula, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Daniel Stone, Hans Verkuil
Cc: dri-devel, linux-kernel, kernel, Daniel Stone
On Saturday, 13 June 2026 08:57:29 Central European Summer Time Hans Verkuil wrote:
> Hi Nicolas,
>
> On 11/06/2026 14:57, Nicolas Frattaroli wrote:
> > HDMI uses the DDC I2C bus for communicating various bits of link status
> > out of band with the actual HDMI video signal. This information can be
> > useful for debugging issues like questionable cables sabotaged by feline
> > teeth, Enthusiast Grade cables made of cow fencing wire, and other such
> > problems that ruin one's media viewing plans.
> >
> > Consequently, this series exposes various bits of pertinent information
> > from the SCDC protocol in an HDMI connector's debugfs. To continually
> > poll the link status, userspace can poll the debugfs file.
>
> Something is not quite right: I've been testing this series with my i915
> based laptop with HDMI connector, and I never see the scdc_status file.
> And that's because CONFIG_DRM_BRIDGE_CONNECTOR is not set for my configuration.
That's to be expected. i915 does not use bridge connectors, and neither
does amdgpu iirc. I am working on embedded boards that do use bridge
connectors, along with all the rest of the HDMI state helpers.
Implementing something new in DRM usually involves having to triplicate
certain parts of the work in order to have i915 and amdgpu behave the
same as everyone else.
> So I think you are creating the debugfs entry in the wrong place.
I can't create it for all drm_connectors as Maxime suggested due to the
cyclical dependency that would create. If someone has any idea on how
to break that dependency cycle, I'm open to suggestions.
> I can read the SCDC from the display using edid-decode with the right /dev/i2c-X
> device, so it's definitely there.
>
> Regards,
>
> Hans
>
> >
> > ---
> > Changes in v6:
> > - Fix off-by-one error in drm_scdc_read_state
> > - Link to v5: https://patch.msgid.link/20260604-scdc-link-health-v5-0-11173b0ac3de@collabora.com
> >
> > Changes in v5:
> > - Read all SCDC data regardless of update flags
> > - Dump SCDC data as hex before the human-readable output. It's separated
> > with "\n----------------\n\n".
> > - No longer write 0 to read-only registers
> > - Add Reed-Solomon Corrections counter parsing
> > - Parsing has been kept. A desire was expressed to get this data without
> > any external userspace tooling, and the kernel will need to parse it
> > eventually anyway to set the link status.
> > - Functions have been made static as of right now, since external users
> > may do another pass over the function signatures anyway.
> > - Link to v4: https://patch.msgid.link/20260527-scdc-link-health-v4-0-622ea40a1f59@collabora.com
> >
> > Changes in v4:
> > - Don't use C struct bitfields for parsing status flags. Switch to
> > bitwise AND for boolean flags, and FIELD_GET for multi-bit values.
> > - Drop the superfluous !! and parens
> > - Drop the __pure attributes on static functions
> > - Initialise stack local arrays with {}, not { 0 }.
> > - I've kept the print macros and %-30s format. Reason being that I don't
> > want to repeat the format specifier and str_yes_no(foo) a bunch, and I
> > like the %-30s format because it means all values are aligned with the
> > value of the longest field, which is 30 chars long.
> > - Link to v3: https://patch.msgid.link/20260526-scdc-link-health-v3-0-59e4a4aaead1@collabora.com
> >
> > Changes in v3:
> > - Add patch to change return type of drm_scdc_read/write.
> > - Rework error counter reading to duplicate less code.
> > - Also check lane 3 counter valid flag when reading its error counter.
> > - Use memset to clear buf for error counters, rather than doing it in
> > the loop.
> > - Make read_error_counters not accept 0 as num_lanes; fix it up in the
> > caller instead.
> > - Link to v2: https://patch.msgid.link/20260520-scdc-link-health-v2-0-511af18cd64b@collabora.com
> >
> > Changes in v2:
> > - Add HDMI 2.1 SCDC status reporting
> > - Link to v1: https://patch.msgid.link/20260415-scdc-link-health-v1-0-8e731e88eaf0@collabora.com
> >
> > To: Jani Nikula <jani.nikula@linux.intel.com>
> > To: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> > To: Maxime Ripard <mripard@kernel.org>
> > To: Thomas Zimmermann <tzimmermann@suse.de>
> > To: David Airlie <airlied@gmail.com>
> > To: Simona Vetter <simona@ffwll.ch>
> > To: Andrzej Hajda <andrzej.hajda@intel.com>
> > To: Neil Armstrong <neil.armstrong@linaro.org>
> > To: Robert Foss <rfoss@kernel.org>
> > To: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
> > To: Jonas Karlman <jonas@kwiboo.se>
> > To: Jernej Skrabec <jernej.skrabec@gmail.com>
> > To: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > To: Daniel Stone <daniel@fooishbar.org>
> > To: Hans Verkuil <hverkuil+cisco@kernel.org>
> > Cc: dri-devel@lists.freedesktop.org
> > Cc: linux-kernel@vger.kernel.org
> > Cc: kernel@collabora.com
> > Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
> >
> > ---
> > Nicolas Frattaroli (4):
> > drm/scdc-helper: Don't use ssize_t return type for scdc_read/write
> > drm/scdc-helper: Add scdc_status debugfs entry
> > drm/display: bridge_connector: init scdc debugfs for HDMI
> > drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields
> >
> > drivers/gpu/drm/display/drm_bridge_connector.c | 4 +
> > drivers/gpu/drm/display/drm_scdc_helper.c | 285 ++++++++++++++++++++++++-
> > include/drm/display/drm_scdc.h | 21 +-
> > include/drm/display/drm_scdc_helper.h | 103 ++++++++-
> > 4 files changed, 404 insertions(+), 9 deletions(-)
> > ---
> > base-commit: 4fdfaadba04dc0f2f2490dbc91922caa290463a5
> > change-id: 20260413-scdc-link-health-89326013d96c
> >
> > Best regards,
> > --
> > Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
> >
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v6 0/4] Add SCDC information to connector debugfs
2026-06-15 8:07 ` Nicolas Frattaroli
@ 2026-06-15 16:33 ` Maxime Ripard
2026-06-16 12:04 ` Nicolas Frattaroli
0 siblings, 1 reply; 10+ messages in thread
From: Maxime Ripard @ 2026-06-15 16:33 UTC (permalink / raw)
To: Nicolas Frattaroli
Cc: Jani Nikula, Maarten Lankhorst, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Daniel Stone, Hans Verkuil, dri-devel, linux-kernel, kernel,
Daniel Stone
[-- Attachment #1: Type: text/plain, Size: 1996 bytes --]
On Mon, Jun 15, 2026 at 10:07:02AM +0200, Nicolas Frattaroli wrote:
> On Saturday, 13 June 2026 08:57:29 Central European Summer Time Hans Verkuil wrote:
> > Hi Nicolas,
> >
> > On 11/06/2026 14:57, Nicolas Frattaroli wrote:
> > > HDMI uses the DDC I2C bus for communicating various bits of link status
> > > out of band with the actual HDMI video signal. This information can be
> > > useful for debugging issues like questionable cables sabotaged by feline
> > > teeth, Enthusiast Grade cables made of cow fencing wire, and other such
> > > problems that ruin one's media viewing plans.
> > >
> > > Consequently, this series exposes various bits of pertinent information
> > > from the SCDC protocol in an HDMI connector's debugfs. To continually
> > > poll the link status, userspace can poll the debugfs file.
> >
> > Something is not quite right: I've been testing this series with my i915
> > based laptop with HDMI connector, and I never see the scdc_status file.
> > And that's because CONFIG_DRM_BRIDGE_CONNECTOR is not set for my configuration.
>
> That's to be expected. i915 does not use bridge connectors, and neither
> does amdgpu iirc. I am working on embedded boards that do use bridge
> connectors, along with all the rest of the HDMI state helpers.
>
> Implementing something new in DRM usually involves having to triplicate
> certain parts of the work in order to have i915 and amdgpu behave the
> same as everyone else.
>
> > So I think you are creating the debugfs entry in the wrong place.
>
> I can't create it for all drm_connectors as Maxime suggested due to the
> cyclical dependency that would create. If someone has any idea on how
> to break that dependency cycle, I'm open to suggestions.
Back when we introduce the audio support, we floated the idea to
de-midlayer this and turn it into a debugfs_init helper. We don't have a
lot of users yet so it might be the best time to do so, and would solve
your issue.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v6 0/4] Add SCDC information to connector debugfs
2026-06-15 16:33 ` Maxime Ripard
@ 2026-06-16 12:04 ` Nicolas Frattaroli
2026-06-25 16:19 ` Maxime Ripard
0 siblings, 1 reply; 10+ messages in thread
From: Nicolas Frattaroli @ 2026-06-16 12:04 UTC (permalink / raw)
To: Maxime Ripard
Cc: Jani Nikula, Maarten Lankhorst, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Daniel Stone, Hans Verkuil, dri-devel, linux-kernel, kernel,
Daniel Stone
On Monday, 15 June 2026 18:33:11 Central European Summer Time Maxime Ripard wrote:
> On Mon, Jun 15, 2026 at 10:07:02AM +0200, Nicolas Frattaroli wrote:
> > On Saturday, 13 June 2026 08:57:29 Central European Summer Time Hans Verkuil wrote:
> > > Hi Nicolas,
> > >
> > > On 11/06/2026 14:57, Nicolas Frattaroli wrote:
> > > > HDMI uses the DDC I2C bus for communicating various bits of link status
> > > > out of band with the actual HDMI video signal. This information can be
> > > > useful for debugging issues like questionable cables sabotaged by feline
> > > > teeth, Enthusiast Grade cables made of cow fencing wire, and other such
> > > > problems that ruin one's media viewing plans.
> > > >
> > > > Consequently, this series exposes various bits of pertinent information
> > > > from the SCDC protocol in an HDMI connector's debugfs. To continually
> > > > poll the link status, userspace can poll the debugfs file.
> > >
> > > Something is not quite right: I've been testing this series with my i915
> > > based laptop with HDMI connector, and I never see the scdc_status file.
> > > And that's because CONFIG_DRM_BRIDGE_CONNECTOR is not set for my configuration.
> >
> > That's to be expected. i915 does not use bridge connectors, and neither
> > does amdgpu iirc. I am working on embedded boards that do use bridge
> > connectors, along with all the rest of the HDMI state helpers.
> >
> > Implementing something new in DRM usually involves having to triplicate
> > certain parts of the work in order to have i915 and amdgpu behave the
> > same as everyone else.
> >
> > > So I think you are creating the debugfs entry in the wrong place.
> >
> > I can't create it for all drm_connectors as Maxime suggested due to the
> > cyclical dependency that would create. If someone has any idea on how
> > to break that dependency cycle, I'm open to suggestions.
>
> Back when we introduce the audio support, we floated the idea to
> de-midlayer this and turn it into a debugfs_init helper. We don't have a
> lot of users yet so it might be the best time to do so, and would solve
> your issue.
I agree and will be happy to do that. I need some more concrete info
though on what "de-midlayer" means here. From what I can tell, the
core problem is that drm_connector.c directly calls into
drm_debugfs_connector_add of drm_debugfs.c, which if it did also
do a direct call into drm_scdc_helper.c would mean drm_scdc_helper.c
needs drm_connector.c but drm_connector.c needs drm_scdc_helper.c.
So I guess the de-midlayer and turn it into helper part is that
drm_connector does an indirect call to a function pointer instead,
which is default-filled at runtime with the helper debugfs_init
function? I don't know if we can then do this by default for all
drivers in drm_connector.c or if it needs to be done per-driver
like some of the other helpers. If the latter, then we don't
really gain much over what I'm doing with bridge connector.
The other maybe less intrusive change is that
drm_scdc_debugfs_init and scdc_status_fops travels to drm_debugfs.c,
while the actual implementation of the read op remains in
drm_scdc_helper.c. I think that would solve it without a big refactor?
Kind regards,
Nicolas Frattaroli
>
> Maxime
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v6 0/4] Add SCDC information to connector debugfs
2026-06-16 12:04 ` Nicolas Frattaroli
@ 2026-06-25 16:19 ` Maxime Ripard
0 siblings, 0 replies; 10+ messages in thread
From: Maxime Ripard @ 2026-06-25 16:19 UTC (permalink / raw)
To: Nicolas Frattaroli
Cc: Jani Nikula, Maarten Lankhorst, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Daniel Stone, Hans Verkuil, dri-devel, linux-kernel, kernel,
Daniel Stone
[-- Attachment #1: Type: text/plain, Size: 3883 bytes --]
Hi,
On Tue, Jun 16, 2026 at 02:04:13PM +0200, Nicolas Frattaroli wrote:
> On Monday, 15 June 2026 18:33:11 Central European Summer Time Maxime Ripard wrote:
> > On Mon, Jun 15, 2026 at 10:07:02AM +0200, Nicolas Frattaroli wrote:
> > > On Saturday, 13 June 2026 08:57:29 Central European Summer Time Hans Verkuil wrote:
> > > > Hi Nicolas,
> > > >
> > > > On 11/06/2026 14:57, Nicolas Frattaroli wrote:
> > > > > HDMI uses the DDC I2C bus for communicating various bits of link status
> > > > > out of band with the actual HDMI video signal. This information can be
> > > > > useful for debugging issues like questionable cables sabotaged by feline
> > > > > teeth, Enthusiast Grade cables made of cow fencing wire, and other such
> > > > > problems that ruin one's media viewing plans.
> > > > >
> > > > > Consequently, this series exposes various bits of pertinent information
> > > > > from the SCDC protocol in an HDMI connector's debugfs. To continually
> > > > > poll the link status, userspace can poll the debugfs file.
> > > >
> > > > Something is not quite right: I've been testing this series with my i915
> > > > based laptop with HDMI connector, and I never see the scdc_status file.
> > > > And that's because CONFIG_DRM_BRIDGE_CONNECTOR is not set for my configuration.
> > >
> > > That's to be expected. i915 does not use bridge connectors, and neither
> > > does amdgpu iirc. I am working on embedded boards that do use bridge
> > > connectors, along with all the rest of the HDMI state helpers.
> > >
> > > Implementing something new in DRM usually involves having to triplicate
> > > certain parts of the work in order to have i915 and amdgpu behave the
> > > same as everyone else.
> > >
> > > > So I think you are creating the debugfs entry in the wrong place.
> > >
> > > I can't create it for all drm_connectors as Maxime suggested due to the
> > > cyclical dependency that would create. If someone has any idea on how
> > > to break that dependency cycle, I'm open to suggestions.
> >
> > Back when we introduce the audio support, we floated the idea to
> > de-midlayer this and turn it into a debugfs_init helper. We don't have a
> > lot of users yet so it might be the best time to do so, and would solve
> > your issue.
>
> I agree and will be happy to do that. I need some more concrete info
> though on what "de-midlayer" means here. From what I can tell, the
> core problem is that drm_connector.c directly calls into
> drm_debugfs_connector_add of drm_debugfs.c, which if it did also
> do a direct call into drm_scdc_helper.c would mean drm_scdc_helper.c
> needs drm_connector.c but drm_connector.c needs drm_scdc_helper.c.
>
> So I guess the de-midlayer and turn it into helper part is that
> drm_connector does an indirect call to a function pointer instead,
> which is default-filled at runtime with the helper debugfs_init
> function? I don't know if we can then do this by default for all
> drivers in drm_connector.c or if it needs to be done per-driver
> like some of the other helpers. If the latter, then we don't
> really gain much over what I'm doing with bridge connector.
I'd promote the hdmi_debugfs_add() call in drm_debugfs_connector_add to
an debugfs_init helper for drivers that use the HDMI state helpers,
because it's only useful for those anyway.
> The other maybe less intrusive change is that
> drm_scdc_debugfs_init and scdc_status_fops travels to drm_debugfs.c,
> while the actual implementation of the read op remains in
> drm_scdc_helper.c. I think that would solve it without a big refactor?
Then, depending on what makes the most sense, you can either create
another debugfs_init helper for scdc that would be called by the new
hdmi state debugfs_init helper or any other relevant driver, or put it
directly into that new helper.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-06-25 16:19 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-11 12:57 [PATCH v6 0/4] Add SCDC information to connector debugfs Nicolas Frattaroli
2026-06-11 12:57 ` [PATCH v6 1/4] drm/scdc-helper: Don't use ssize_t return type for scdc_read/write Nicolas Frattaroli
2026-06-11 12:57 ` [PATCH v6 2/4] drm/scdc-helper: Add scdc_status debugfs entry Nicolas Frattaroli
2026-06-11 12:57 ` [PATCH v6 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli
2026-06-11 12:58 ` [PATCH v6 4/4] drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields Nicolas Frattaroli
2026-06-13 6:57 ` [PATCH v6 0/4] Add SCDC information to connector debugfs Hans Verkuil
2026-06-15 8:07 ` Nicolas Frattaroli
2026-06-15 16:33 ` Maxime Ripard
2026-06-16 12:04 ` Nicolas Frattaroli
2026-06-25 16:19 ` Maxime Ripard
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome