* [PATCH v2 0/3] Add SCDC information to connector debugfs
@ 2026-05-20 13:35 Nicolas Frattaroli
2026-05-20 13:35 ` [PATCH v2 1/3] drm/scdc-helper: Add scdc_status debugfs entry Nicolas Frattaroli
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Nicolas Frattaroli @ 2026-05-20 13:35 UTC (permalink / raw)
To: 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, Nicolas Frattaroli
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 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: 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>
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 (3):
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 | 341 +++++++++++++++++++++++++
include/drm/display/drm_scdc.h | 9 +
include/drm/display/drm_scdc_helper.h | 113 ++++++++
4 files changed, 467 insertions(+)
---
base-commit: 7d39df83e93eba14a23b1fb6ffff076eb191f27b
change-id: 20260413-scdc-link-health-89326013d96c
Best regards,
--
Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v2 1/3] drm/scdc-helper: Add scdc_status debugfs entry 2026-05-20 13:35 [PATCH v2 0/3] Add SCDC information to connector debugfs Nicolas Frattaroli @ 2026-05-20 13:35 ` Nicolas Frattaroli 2026-05-25 11:49 ` Claude review: " Claude Code Review Bot 2026-05-20 13:35 ` [PATCH v2 2/3] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli ` (2 subsequent siblings) 3 siblings, 1 reply; 11+ messages in thread From: Nicolas Frattaroli @ 2026-05-20 13:35 UTC (permalink / raw) To: 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, 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 | 229 ++++++++++++++++++++++++++++++ include/drm/display/drm_scdc_helper.h | 56 ++++++++ 2 files changed, 285 insertions(+) diff --git a/drivers/gpu/drm/display/drm_scdc_helper.c b/drivers/gpu/drm/display/drm_scdc_helper.c index df878aad4a36..d582ba38a610 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,224 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, return true; } EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio); + +/** + * drm_scdc_read_status0_flags - Read SCDC "Status Flags" Register + * @connector: pointer to &struct drm_connector to issue the scdc request on + * @flags: pointer to the caller's &struct drm_scdc_status_flags to output to + * + * Reads the SCDC Status Flags 0 register, and outputs its contents to the + * destination @flags. Contents of @flags.status0 is only valid if function + * returns 0. + * + * Returns: %0 on success, negative errno on error. + */ +int drm_scdc_read_status0_flags(struct drm_connector *connector, + struct drm_scdc_status_flags *flags) +{ + int ret; + + ret = drm_scdc_writeb(connector->ddc, SCDC_UPDATE_0, SCDC_STATUS_UPDATE); + if (ret) + return ret; + + return drm_scdc_readb(connector->ddc, SCDC_STATUS_FLAGS_0, &flags->status0.data); +} +EXPORT_SYMBOL(drm_scdc_read_status0_flags); + +/** + * drm_scdc_read_error_counters - Read and clear SCDC error counters + * @connector: pointer to &struct drm_connector to issue the scdc request on + * @counter: Caller's u16 array with 3 elements to write the counter values into + * + * Read the SCDC channel error counters. If the count of channel *n* is valid, + * write it into counter[n]. Otherwise, set counter[n] to 0. Reads all counters + * in one read chunk, then clears every counter, as is mandated. + * + * Returns: %0 on success, negative errno on error. + */ +int drm_scdc_read_error_counters(struct drm_connector *connector, u16 counter[3]) +{ + u8 buf[7] = { 0 }; + ssize_t ret; + u8 sum = 0; + int i; + + ret = drm_scdc_writeb(connector->ddc, SCDC_UPDATE_0, SCDC_CED_UPDATE); + if (ret) + return ret; + + /* why in the fucking fuck does this return a ssize_t */ + ret = drm_scdc_read(connector->ddc, SCDC_ERR_DET_0_L, buf, ARRAY_SIZE(buf)); + if (ret) + return ret; + + /* + * Verify the "checksum", i.e. sum up everything including the checksum + * register as a wrapping unsigned 8-bit addition and verify it's 0. + */ + for (i = 0; i < ARRAY_SIZE(buf); i++) + sum = wrapping_add(u8, sum, buf[i]); + + if (sum) + return -EPROTO; + + for (i = 0; i < ARRAY_SIZE(buf) - 1; i += 2) { + if (buf[i + 1] & SCDC_CHANNEL_VALID) + counter[i / 2] = buf[i] | (buf[i + 1] & ~SCDC_CHANNEL_VALID) << 8; + else + counter[i / 2] = 0; + + buf[i] = 0; + buf[i + 1] = 0; + } + buf[ARRAY_SIZE(buf) - 1] = 0; + + return drm_scdc_write(connector->ddc, SCDC_ERR_DET_0_L, buf, ARRAY_SIZE(buf)); +} +EXPORT_SYMBOL(drm_scdc_read_error_counters); + +/** + * 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 update flags from SCDC, and updates the parts of @state that SCDC + * claims have changed, as well as populating those where such a distinction + * can't be made. + * + * Returns: %0 on success, negative errno on failure. + */ +int drm_scdc_read_state(struct drm_connector *connector, struct drm_scdc_state *state) +{ + u8 upd_flags[2] = { 0 }; + struct i2c_adapter *ddc; + struct drm_scdc *scdc; + int ret; + u8 val; + + if (!state || !connector) + return -ENODEV; + + scdc = &connector->display_info.hdmi.scdc; + ddc = connector->ddc; + + if (!scdc->supported) + return -EOPNOTSUPP; + + ret = drm_scdc_readb(ddc, SCDC_TMDS_CONFIG, &val); + if (ret) + return ret; + + state->scrambling_enabled = !!(val & SCDC_SCRAMBLING_ENABLE); + state->tmds_bclk_x40 = !!(val & SCDC_TMDS_BIT_CLOCK_RATIO_BY_40); + + state->scrambling_detected = drm_scdc_get_scrambling_status(connector); + + ret = drm_scdc_read(ddc, SCDC_UPDATE_0, &upd_flags, sizeof(upd_flags)); + if (ret) + return ret; + + if (upd_flags[0] & SCDC_STATUS_UPDATE) { + ret = drm_scdc_read_status0_flags(connector, &state->stf); + if (ret) + return ret; + } + + if (upd_flags[0] & SCDC_CED_UPDATE) { + ret = drm_scdc_read_error_counters(connector, 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 ret; + + drm_connector_get(connector); + + if (connector->status != connector_status_connected) { + ret = -ENODEV; + goto err_conn_put; + } + + 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); + + ret = drm_scdc_read_state(connector, st); + drm_connector_put(connector); + if (ret) + return ret; + + 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.status0.flags.clock_detected); + scdc_print_flag(m, "Channel 0 Locked", st->stf.status0.flags.ch0_locked); + scdc_print_flag(m, "Channel 1 Locked", st->stf.status0.flags.ch1_locked); + scdc_print_flag(m, "Channel 2 Locked", st->stf.status0.flags.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 34600476a1b9..b597e24a2976 100644 --- a/include/drm/display/drm_scdc_helper.h +++ b/include/drm/display/drm_scdc_helper.h @@ -30,6 +30,55 @@ struct drm_connector; struct i2c_adapter; +struct dentry; + +struct drm_scdc_status_flags { + /* Status Register 0 */ + union { + struct { + bool clock_detected : 1; + bool ch0_locked : 1; + bool ch1_locked : 1; + bool ch2_locked : 1; + bool rsvd_4 : 1; + bool rsvd_5 : 1; + bool rsvd_6 : 1; + bool rsvd_7 : 1; + } flags __packed; + u8 data; + } status0; + + /* Status Register 1 */ + union { + struct { + bool rsvd_0 : 1; + bool rsvd_1 : 1; + bool rsvd_2 : 1; + bool rsvd_3 : 1; + bool rsvd_4 : 1; + bool rsvd_5 : 1; + bool rsvd_6 : 1; + bool rsvd_7 : 1; + } flags __packed; + u8 data; + } status1; +}; + +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]; +}; ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, size_t size); @@ -77,4 +126,11 @@ 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_status0_flags(struct drm_connector *connector, + struct drm_scdc_status_flags *flags); +int drm_scdc_read_state(struct drm_connector *connector, + struct drm_scdc_state *state); +int drm_scdc_read_error_counters(struct drm_connector *connector, u16 counter[3]); +void drm_scdc_debugfs_init(struct drm_connector *connector, struct dentry *root); + #endif -- 2.54.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Claude review: drm/scdc-helper: Add scdc_status debugfs entry 2026-05-20 13:35 ` [PATCH v2 1/3] drm/scdc-helper: Add scdc_status debugfs entry Nicolas Frattaroli @ 2026-05-25 11:49 ` Claude Code Review Bot 0 siblings, 0 replies; 11+ messages in thread From: Claude Code Review Bot @ 2026-05-25 11:49 UTC (permalink / raw) To: dri-devel-reviews Patch Review This is the bulk of the series — adds `drm_scdc_read_state`, status flag structs, error counter reading, and the debugfs show function. Generally well done, but a few issues: **1. Inappropriate comment (medium)** ```c /* why in the fucking fuck does this return a ssize_t */ ret = drm_scdc_read(connector->ddc, SCDC_ERR_DET_0_L, buf, ARRAY_SIZE(buf)); ``` This comment is not appropriate for the kernel tree. If you want to note the type mismatch, something like `/* drm_scdc_read returns ssize_t but 0 on success */` would be acceptable. **2. Bitfield union portability on big-endian (medium)** ```c struct drm_scdc_status_flags { union { struct { bool clock_detected : 1; bool ch0_locked : 1; bool ch1_locked : 1; bool ch2_locked : 1; ... } flags __packed; u8 data; } status0; ``` GCC orders bitfields from LSB on little-endian targets but from MSB on big-endian targets. On a big-endian system, `clock_detected` would map to bit 7 instead of bit 0 (where `SCDC_CLOCK_DETECT` is defined). This means the bitfield-to-register mapping is wrong on big-endian. Consider using explicit bit masks (the header already defines `SCDC_CLOCK_DETECT`, `SCDC_CH0_LOCK`, etc.) and accessor functions instead, or adding an endianness guard/comment explaining why this is acceptable. **3. Doc typo** ```c /** @scramling_enabled: true if TMDS scrambling is on */ ``` Should be `@scrambling_enabled`. **4. Status reading clears update flag before reading the register** In `drm_scdc_read_status0_flags`: ```c ret = drm_scdc_writeb(connector->ddc, SCDC_UPDATE_0, SCDC_STATUS_UPDATE); if (ret) return ret; return drm_scdc_readb(connector->ddc, SCDC_STATUS_FLAGS_0, &flags->status0.data); ``` The update flag is cleared (by writing 1 to it) *before* reading the status register. There's a small race window where the sink could update the status between the clear and the read, meaning the source wouldn't know to re-read later. For debugfs this is fine, but since `drm_scdc_read_status0_flags` is exported as a general-purpose API, consider documenting this behavior or swapping the order (read first, then clear). The same pattern appears in `drm_scdc_read_error_counters` with `SCDC_CED_UPDATE`, though for CED clearing before reading is the spec-mandated behavior, so that one is correct. **5. Minor: `status1` bitfields are all reserved and unused** The `status1` member in `drm_scdc_status_flags` has 8 reserved bool bitfields but is never read or displayed in this patch. This is fine since patch 3 replaces them, but in isolation patch 1 adds dead code. --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 2/3] drm/display: bridge_connector: init scdc debugfs for HDMI 2026-05-20 13:35 [PATCH v2 0/3] Add SCDC information to connector debugfs Nicolas Frattaroli 2026-05-20 13:35 ` [PATCH v2 1/3] drm/scdc-helper: Add scdc_status debugfs entry Nicolas Frattaroli @ 2026-05-20 13:35 ` Nicolas Frattaroli 2026-05-25 11:49 ` Claude review: " Claude Code Review Bot 2026-05-20 13:35 ` [PATCH v2 3/3] drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields Nicolas Frattaroli 2026-05-25 11:49 ` Claude review: Add SCDC information to connector debugfs Claude Code Review Bot 3 siblings, 1 reply; 11+ messages in thread From: Nicolas Frattaroli @ 2026-05-20 13:35 UTC (permalink / raw) To: 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, Nicolas Frattaroli On drm_bridge_connectors that contain an HDMI bridge, initialise the SCDC debugfs entry under the connector's debugfs root. 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 649969fca141..8c2c890bd95d 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 void drm_bridge_connector_reset(struct drm_connector *connector) -- 2.54.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Claude review: drm/display: bridge_connector: init scdc debugfs for HDMI 2026-05-20 13:35 ` [PATCH v2 2/3] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli @ 2026-05-25 11:49 ` Claude Code Review Bot 0 siblings, 0 replies; 11+ messages in thread From: Claude Code Review Bot @ 2026-05-25 11:49 UTC (permalink / raw) To: dri-devel-reviews Patch Review Clean and correct. Just 4 lines adding the SCDC debugfs init call when a bridge has HDMI support. ```c if (bridge_connector->bridge_hdmi) drm_scdc_debugfs_init(connector, root); ``` This is placed after the per-bridge `debugfs_init` loop, which is the right location. The `drm_scdc_debugfs_init` function already handles NULL checks and allocation failure gracefully. No issues with this patch. --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 3/3] drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields 2026-05-20 13:35 [PATCH v2 0/3] Add SCDC information to connector debugfs Nicolas Frattaroli 2026-05-20 13:35 ` [PATCH v2 1/3] drm/scdc-helper: Add scdc_status debugfs entry Nicolas Frattaroli 2026-05-20 13:35 ` [PATCH v2 2/3] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli @ 2026-05-20 13:35 ` Nicolas Frattaroli 2026-05-22 17:38 ` Daniel Stone 2026-05-25 11:49 ` Claude review: " Claude Code Review Bot 2026-05-25 11:49 ` Claude review: Add SCDC information to connector debugfs Claude Code Review Bot 3 siblings, 2 replies; 11+ messages in thread From: Nicolas Frattaroli @ 2026-05-20 13:35 UTC (permalink / raw) To: 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, 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 | 130 +++++++++++++++++++++++++++--- include/drm/display/drm_scdc.h | 9 +++ include/drm/display/drm_scdc_helper.h | 85 +++++++++++++++---- 3 files changed, 201 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/display/drm_scdc_helper.c b/drivers/gpu/drm/display/drm_scdc_helper.c index d582ba38a610..e9e59c7553a4 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 __pure 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 @@ -309,10 +342,41 @@ int drm_scdc_read_status0_flags(struct drm_connector *connector, } EXPORT_SYMBOL(drm_scdc_read_status0_flags); +/** + * drm_scdc_read_status1_2_flags - Read SCDC "Status Flags" 1 and 2 Registers + * @connector: pointer to &struct drm_connector to issue the scdc request on + * @flags: pointer to the caller's &struct drm_scdc_status_flags to output to + * + * Reads the SCDC Status Flags 1 and 2 registers, and outputs their contents to + * the destination @flags. Contents of @flags.status1 and @flags.status2 are + * only valid if function returns 0. + * + * Returns 0 on success, negative errno on error. + */ +int drm_scdc_read_status1_2_flags(struct drm_connector *connector, + struct drm_scdc_status_flags *flags) +{ + int ret; + + ret = drm_scdc_writeb(connector->ddc, SCDC_UPDATE_0, SCDC_FLT_UPDATE); + if (ret) + return ret; + + ret = drm_scdc_readb(connector->ddc, SCDC_STATUS_FLAGS_1, &flags->status1.data); + if (ret) + return ret; + + return drm_scdc_readb(connector->ddc, SCDC_STATUS_FLAGS_2, &flags->status2.data); +} +EXPORT_SYMBOL(drm_scdc_read_status1_2_flags); + +#define ERR_DET_OFF(x) ((x) - SCDC_ERR_DET_0_L) + /** * drm_scdc_read_error_counters - Read and clear SCDC error counters * @connector: pointer to &struct drm_connector to issue the scdc request on - * @counter: Caller's u16 array with 3 elements to write the counter values into + * @counter: Caller's u16 array with 4 elements to write the counter values into + * @num_lanes: number of active lanes, either 3 or 4 * * Read the SCDC channel error counters. If the count of channel *n* is valid, * write it into counter[n]. Otherwise, set counter[n] to 0. Reads all counters @@ -320,19 +384,33 @@ EXPORT_SYMBOL(drm_scdc_read_status0_flags); * * Returns: %0 on success, negative errno on error. */ -int drm_scdc_read_error_counters(struct drm_connector *connector, u16 counter[3]) +int drm_scdc_read_error_counters(struct drm_connector *connector, u16 counter[4], + unsigned int num_lanes) { - u8 buf[7] = { 0 }; + unsigned int buf_sz; + u8 buf[9] = { 0 }; ssize_t ret; u8 sum = 0; int i; + switch (num_lanes) { + case 0: + case 3: + buf_sz = 7; + break; + case 4: + buf_sz = 9; + break; + default: + return -EINVAL; + } + ret = drm_scdc_writeb(connector->ddc, SCDC_UPDATE_0, SCDC_CED_UPDATE); if (ret) return ret; /* why in the fucking fuck does this return a ssize_t */ - ret = drm_scdc_read(connector->ddc, SCDC_ERR_DET_0_L, buf, ARRAY_SIZE(buf)); + ret = drm_scdc_read(connector->ddc, SCDC_ERR_DET_0_L, buf, buf_sz); if (ret) return ret; @@ -340,13 +418,13 @@ int drm_scdc_read_error_counters(struct drm_connector *connector, u16 counter[3] * Verify the "checksum", i.e. sum up everything including the checksum * register as a wrapping unsigned 8-bit addition and verify it's 0. */ - for (i = 0; i < ARRAY_SIZE(buf); i++) + for (i = 0; i < buf_sz; i++) sum = wrapping_add(u8, sum, buf[i]); if (sum) return -EPROTO; - for (i = 0; i < ARRAY_SIZE(buf) - 1; i += 2) { + for (i = 0; i <= ERR_DET_OFF(SCDC_ERR_DET_2_H); i += 2) { if (buf[i + 1] & SCDC_CHANNEL_VALID) counter[i / 2] = buf[i] | (buf[i + 1] & ~SCDC_CHANNEL_VALID) << 8; else @@ -355,9 +433,15 @@ int drm_scdc_read_error_counters(struct drm_connector *connector, u16 counter[3] buf[i] = 0; buf[i + 1] = 0; } - buf[ARRAY_SIZE(buf) - 1] = 0; + buf[ERR_DET_OFF(SCDC_ERR_DET_CHECKSUM)] = 0; + + if (num_lanes == 4) + counter[3] = buf[ERR_DET_OFF(SCDC_ERR_DET_3_L)] | + (buf[ERR_DET_OFF(SCDC_ERR_DET_3_H)] & ~SCDC_CHANNEL_VALID) << 8; + else + counter[3] = 0; - return drm_scdc_write(connector->ddc, SCDC_ERR_DET_0_L, buf, ARRAY_SIZE(buf)); + return drm_scdc_write(connector->ddc, SCDC_ERR_DET_0_L, buf, buf_sz); } EXPORT_SYMBOL(drm_scdc_read_error_counters); @@ -377,6 +461,7 @@ int drm_scdc_read_state(struct drm_connector *connector, struct drm_scdc_state * u8 upd_flags[2] = { 0 }; struct i2c_adapter *ddc; struct drm_scdc *scdc; + int num_lanes; int ret; u8 val; @@ -398,6 +483,17 @@ int drm_scdc_read_state(struct drm_connector *connector, struct drm_scdc_state * state->scrambling_detected = drm_scdc_get_scrambling_status(connector); + ret = drm_scdc_readb(ddc, SCDC_CONFIG_1, &val); + if (ret) + return ret; + + state->rate = FIELD_GET(SCDC_FRL_RATE, val); + num_lanes = drm_scdc_num_frl_lanes(state->rate); + if (num_lanes < 0) + return num_lanes; + + state->ffe_levels = FIELD_GET(SCDC_FFE_LEVELS, val); + ret = drm_scdc_read(ddc, SCDC_UPDATE_0, &upd_flags, sizeof(upd_flags)); if (ret) return ret; @@ -408,8 +504,15 @@ int drm_scdc_read_state(struct drm_connector *connector, struct drm_scdc_state * return ret; } + if (upd_flags[0] & SCDC_FLT_UPDATE) { + ret = drm_scdc_read_status1_2_flags(connector, &state->stf); + if (ret) + return ret; + } + if (upd_flags[0] & SCDC_CED_UPDATE) { - ret = drm_scdc_read_error_counters(connector, state->error_count); + ret = drm_scdc_read_error_counters(connector, state->error_count, + num_lanes); if (ret) return ret; } @@ -457,6 +560,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"); @@ -467,10 +572,17 @@ static int scdc_status_show(struct seq_file *m, void *data) scdc_print_flag(m, "Channel 0 Locked", st->stf.status0.flags.ch0_locked); scdc_print_flag(m, "Channel 1 Locked", st->stf.status0.flags.ch1_locked); scdc_print_flag(m, "Channel 2 Locked", st->stf.status0.flags.ch2_locked); + if (drm_scdc_num_frl_lanes(st->rate) == 4) + scdc_print_flag(m, "Lane 3 Locked", st->stf.status0.flags.ln3_locked); + + scdc_print_flag(m, "Sink Ready For Link Training", st->stf.status0.flags.flt_ready); + scdc_print_flag(m, "Sink Failed To Decode DSC", st->stf.status0.flags.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]); return 0; diff --git a/include/drm/display/drm_scdc.h b/include/drm/display/drm_scdc.h index 3d58f37e8ed8..56cd909edac9 100644 --- a/include/drm/display/drm_scdc.h +++ b/include/drm/display/drm_scdc.h @@ -29,6 +29,7 @@ #define SCDC_SOURCE_VERSION 0x02 #define SCDC_UPDATE_0 0x10 +#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,6 +47,10 @@ #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_CH2_LOCK (1 << 3) #define SCDC_CH1_LOCK (1 << 2) @@ -54,6 +59,7 @@ #define SCDC_CLOCK_DETECT (1 << 0) #define SCDC_STATUS_FLAGS_1 0x41 +#define SCDC_STATUS_FLAGS_2 0x42 #define SCDC_ERR_DET_0_L 0x50 #define SCDC_ERR_DET_0_H 0x51 @@ -65,6 +71,9 @@ #define SCDC_ERR_DET_CHECKSUM 0x56 +#define SCDC_ERR_DET_3_L 0x57 +#define SCDC_ERR_DET_3_H 0x58 + #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 b597e24a2976..1a95da3da5fe 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> @@ -40,10 +41,10 @@ struct drm_scdc_status_flags { bool ch0_locked : 1; bool ch1_locked : 1; bool ch2_locked : 1; - bool rsvd_4 : 1; + bool ln3_locked : 1; bool rsvd_5 : 1; - bool rsvd_6 : 1; - bool rsvd_7 : 1; + bool flt_ready : 1; + bool dsc_fail : 1; } flags __packed; u8 data; } status0; @@ -51,19 +52,68 @@ struct drm_scdc_status_flags { /* Status Register 1 */ union { struct { - bool rsvd_0 : 1; - bool rsvd_1 : 1; - bool rsvd_2 : 1; - bool rsvd_3 : 1; - bool rsvd_4 : 1; - bool rsvd_5 : 1; - bool rsvd_6 : 1; - bool rsvd_7 : 1; + u8 ln0_training_pattern : 4; + u8 ln1_training_pattern : 4; } flags __packed; u8 data; } status1; + + /* Status Register 2 */ + union { + struct { + u8 ln2_training_pattern : 4; + u8 ln3_training_pattern : 4; + } flags __packed; + u8 data; + } status2; +}; + +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; @@ -76,8 +126,12 @@ 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]; }; ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, @@ -128,9 +182,12 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, bool se int drm_scdc_read_status0_flags(struct drm_connector *connector, struct drm_scdc_status_flags *flags); +int drm_scdc_read_status1_2_flags(struct drm_connector *connector, + struct drm_scdc_status_flags *flags); int drm_scdc_read_state(struct drm_connector *connector, struct drm_scdc_state *state); -int drm_scdc_read_error_counters(struct drm_connector *connector, u16 counter[3]); +int drm_scdc_read_error_counters(struct drm_connector *connector, u16 counter[4], + unsigned int num_lanes); void drm_scdc_debugfs_init(struct drm_connector *connector, struct dentry *root); #endif -- 2.54.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2 3/3] drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields 2026-05-20 13:35 ` [PATCH v2 3/3] drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields Nicolas Frattaroli @ 2026-05-22 17:38 ` Daniel Stone 2026-05-25 11:49 ` Claude review: " Claude Code Review Bot 1 sibling, 0 replies; 11+ messages in thread From: Daniel Stone @ 2026-05-22 17:38 UTC (permalink / raw) To: Nicolas Frattaroli Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli, dri-devel, linux-kernel, kernel Hi Nicolas, On Wed, 20 May 2026 at 14:36, Nicolas Frattaroli <nicolas.frattaroli@collabora.com> wrote: > - for (i = 0; i < ARRAY_SIZE(buf) - 1; i += 2) { > + for (i = 0; i <= ERR_DET_OFF(SCDC_ERR_DET_2_H); i += 2) { > if (buf[i + 1] & SCDC_CHANNEL_VALID) > counter[i / 2] = buf[i] | (buf[i + 1] & ~SCDC_CHANNEL_VALID) << 8; > else > @@ -355,9 +433,15 @@ int drm_scdc_read_error_counters(struct drm_connector *connector, u16 counter[3] > buf[i] = 0; > buf[i + 1] = 0; > } > - buf[ARRAY_SIZE(buf) - 1] = 0; > + buf[ERR_DET_OFF(SCDC_ERR_DET_CHECKSUM)] = 0; > + > + if (num_lanes == 4) > + counter[3] = buf[ERR_DET_OFF(SCDC_ERR_DET_3_L)] | > + (buf[ERR_DET_OFF(SCDC_ERR_DET_3_H)] & ~SCDC_CHANNEL_VALID) << 8; > + else > + counter[3] = 0; I get that this is separate from the loop above because it's discontiguous, but is it also missing the validity check? I mean, lane_count == 3 means 'fsvo 3 which may be 1 or 2' so have to check SCDC_CHANNEL_VALID there, but if we have 4 lanes explicitly specified then we are we missing the check for the valid bit, or is it just always valid / we shouldn't check? tbh having it separately is a bit messy. I half-wonder if unrolling the loop wouldn't be cleaner, e.g.: #define GET_SCDC_ERR_CNT(c) { \ bool valid = (buf[(c) * 2 + 1] & SCDC_CHANNEL_VALID; \ if (valid) { \ counter[c] = buf[ERR_DET_OFF(SCDC_ERR_DET_ ##x _L)]; \ counter[c] |= (buf[ERR_DET_OFF(SCDC_ERR_DET_ ##x _H)] & ~SCDC_CHANNEL_VALID) << 8; \ } else { counter[c] = 0; } \ } GET_SCDC_ERR_CNT(0); GET_SCDC_ERR_CNT(1); GET_SCDC_ERR_CNT(2); if (num_lanes == 4) GET_SCDC_ERR_CNT(3); else counter[3] = 0; Up to you though. It's a messy format, with no objectively great solution. With or without that suggestion, series is: Reviewed-by: Daniel Stone <daniels@collabora.com> Thanks for pulling all this together! Cheers, Daniel ^ permalink raw reply [flat|nested] 11+ messages in thread
* Claude review: drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields 2026-05-20 13:35 ` [PATCH v2 3/3] drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields Nicolas Frattaroli 2026-05-22 17:38 ` Daniel Stone @ 2026-05-25 11:49 ` Claude Code Review Bot 1 sibling, 0 replies; 11+ messages in thread From: Claude Code Review Bot @ 2026-05-25 11:49 UTC (permalink / raw) To: dri-devel-reviews Patch Review This patch extends for HDMI 2.1's FRL mode, adding lane 3 support, FRL rate, FFE levels, and status flags 1/2. Several bugs here: **1. BUG: Lane 3 error counter missing `SCDC_CHANNEL_VALID` check** ```c if (num_lanes == 4) counter[3] = buf[ERR_DET_OFF(SCDC_ERR_DET_3_L)] | (buf[ERR_DET_OFF(SCDC_ERR_DET_3_H)] & ~SCDC_CHANNEL_VALID) << 8; else counter[3] = 0; ``` Channels 0–2 check `if (buf[i + 1] & SCDC_CHANNEL_VALID)` before using the counter value, setting it to 0 if invalid. Lane 3 unconditionally uses the value. This should mirror the existing pattern: ```c if (num_lanes == 4) { if (buf[ERR_DET_OFF(SCDC_ERR_DET_3_H)] & SCDC_CHANNEL_VALID) counter[3] = buf[ERR_DET_OFF(SCDC_ERR_DET_3_L)] | (buf[ERR_DET_OFF(SCDC_ERR_DET_3_H)] & ~SCDC_CHANNEL_VALID) << 8; else counter[3] = 0; } ``` **2. BUG: Lane 3 error counters not cleared on write-back** After reading, the loop zeroes `buf[0..5]` (channels 0–2) and `buf[6]` (checksum), but `buf[7]` and `buf[8]` (lane 3) are left with their original read values. The subsequent write-back: ```c return drm_scdc_write(connector->ddc, SCDC_ERR_DET_0_L, buf, buf_sz); ``` writes those un-cleared values back, so lane 3 counters are never reset. Add: ```c buf[ERR_DET_OFF(SCDC_ERR_DET_3_L)] = 0; buf[ERR_DET_OFF(SCDC_ERR_DET_3_H)] = 0; ``` before the write-back (and after extracting the counter value). **3. Checksum scope for 4-lane case — verify against HDMI 2.1 spec** ```c for (i = 0; i < buf_sz; i++) sum = wrapping_add(u8, sum, buf[i]); ``` For `buf_sz = 9` (4 lanes), this sums registers 0x50–0x58 including the lane 3 bytes. The HDMI 2.0 checksum at register 0x56 was designed to make bytes 0x50–0x56 sum to zero. If the HDMI 2.1 spec extends the checksum to cover 0x50–0x58, this is correct. If it doesn't, this will produce false `EPROTO` failures whenever lane 3 has non-zero error counts. Please verify this matches the HDMI 2.1 spec — the code should have a comment citing the relevant spec section. **4. `drm_scdc_frl_rate_str` returns NULL in default case** ```c default: return NULL; ``` This value is passed to `seq_printf` via `scdc_print_str` with `%s`. While the default case should be unreachable (all 4-bit values are covered by the enum), returning NULL for `%s` is a latent kernel-oops-in-waiting. Return `"(Unknown)"` instead, or add a `WARN_ON` and return a string. **5. `drm_scdc_read_status1_2_flags` — same clear-before-read pattern** ```c ret = drm_scdc_writeb(connector->ddc, SCDC_UPDATE_0, SCDC_FLT_UPDATE); ``` Same concern as patch 1's `drm_scdc_read_status0_flags` — the FLT_UPDATE flag is cleared before reading STATUS_FLAGS_1/2. For debugfs, fine; as a public API, document the behavior. **6. Minor: `__pure` on `static inline` function** ```c static inline __pure int drm_scdc_num_frl_lanes(enum drm_scdc_frl_rate rate) ``` `__pure` on a `static inline` is redundant — the compiler already has full visibility of the function body. It's harmless but unnecessary. --- **Summary**: The series needs fixes for the lane 3 error counter bugs (missing valid check, missing clear-on-writeback), removal of the inappropriate comment, and ideally a spec citation for the 4-lane checksum scope. The bitfield portability concern should at least be acknowledged. The overall feature is useful and the design is sound. --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 11+ messages in thread
* Claude review: Add SCDC information to connector debugfs 2026-05-20 13:35 [PATCH v2 0/3] Add SCDC information to connector debugfs Nicolas Frattaroli ` (2 preceding siblings ...) 2026-05-20 13:35 ` [PATCH v2 3/3] drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields Nicolas Frattaroli @ 2026-05-25 11:49 ` Claude Code Review Bot 3 siblings, 0 replies; 11+ messages in thread From: Claude Code Review Bot @ 2026-05-25 11:49 UTC (permalink / raw) To: dri-devel-reviews Overall Series Review Subject: Add SCDC information to connector debugfs Author: Nicolas Frattaroli <nicolas.frattaroli@collabora.com> Patches: 5 Reviewed: 2026-05-25T21:49:32.850673 --- This is a well-structured 3-patch series that exposes SCDC (Status and Control Data Channel) link health information via connector debugfs, useful for diagnosing HDMI cable/link problems. The overall design is clean: patch 1 adds the core HDMI 2.0 infrastructure, patch 2 wires it into bridge connectors, and patch 3 extends for HDMI 2.1 FRL fields. However, there are several issues ranging from a real bug in the error counter handling for lane 3, to a portability concern with bitfield unions, to an inappropriate comment. The series should be revised before merging. --- --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 0/4] Add SCDC information to connector debugfs
@ 2026-05-26 10:19 Nicolas Frattaroli
2026-05-26 10:19 ` [PATCH v3 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli
0 siblings, 1 reply; 11+ messages in thread
From: Nicolas Frattaroli @ 2026-05-26 10:19 UTC (permalink / raw)
To: 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, 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 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: 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>
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 | 358 ++++++++++++++++++++++++-
include/drm/display/drm_scdc.h | 9 +
include/drm/display/drm_scdc_helper.h | 121 ++++++++-
4 files changed, 484 insertions(+), 8 deletions(-)
---
base-commit: 587c82eb2067eff6f13385e1b7cf49fc6c3febbc
change-id: 20260413-scdc-link-health-89326013d96c
Best regards,
--
Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI 2026-05-26 10:19 [PATCH v3 0/4] " Nicolas Frattaroli @ 2026-05-26 10:19 ` Nicolas Frattaroli 2026-05-27 5:01 ` Claude review: " Claude Code Review Bot 0 siblings, 1 reply; 11+ messages in thread From: Nicolas Frattaroli @ 2026-05-26 10:19 UTC (permalink / raw) To: 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, 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 649969fca141..8c2c890bd95d 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 void drm_bridge_connector_reset(struct drm_connector *connector) -- 2.54.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Claude review: drm/display: bridge_connector: init scdc debugfs for HDMI 2026-05-26 10:19 ` [PATCH v3 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli @ 2026-05-27 5:01 ` Claude Code Review Bot 0 siblings, 0 replies; 11+ messages in thread From: Claude Code Review Bot @ 2026-05-27 5:01 UTC (permalink / raw) To: dri-devel-reviews Patch Review Simple and correct. Adds one call to `drm_scdc_debugfs_init` when `bridge_connector->bridge_hdmi` is set: ```c if (bridge_connector->bridge_hdmi) drm_scdc_debugfs_init(connector, root); ``` The placement after the per-bridge `debugfs_init` loop is logical. No issues. --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4 0/4] Add SCDC information to connector debugfs
@ 2026-05-27 14:03 Nicolas Frattaroli
2026-05-27 14:03 ` [PATCH v4 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli
0 siblings, 1 reply; 11+ messages in thread
From: Nicolas Frattaroli @ 2026-05-27 14:03 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
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 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>
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 | 377 ++++++++++++++++++++++++-
include/drm/display/drm_scdc.h | 16 +-
include/drm/display/drm_scdc_helper.h | 105 ++++++-
4 files changed, 493 insertions(+), 9 deletions(-)
---
base-commit: 971288d0e77f7a0d356287121ce549ab5dad570a
change-id: 20260413-scdc-link-health-89326013d96c
Best regards,
--
Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v4 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI 2026-05-27 14:03 [PATCH v4 0/4] Add SCDC information to connector debugfs Nicolas Frattaroli @ 2026-05-27 14:03 ` Nicolas Frattaroli 2026-05-28 2:16 ` Claude review: " Claude Code Review Bot 0 siblings, 1 reply; 11+ messages in thread From: Nicolas Frattaroli @ 2026-05-27 14:03 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 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 649969fca141..8c2c890bd95d 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 void drm_bridge_connector_reset(struct drm_connector *connector) -- 2.54.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Claude review: drm/display: bridge_connector: init scdc debugfs for HDMI 2026-05-27 14:03 ` [PATCH v4 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli @ 2026-05-28 2:16 ` Claude Code Review Bot 0 siblings, 0 replies; 11+ messages in thread From: Claude Code Review Bot @ 2026-05-28 2:16 UTC (permalink / raw) To: dri-devel-reviews Patch Review **Verdict: Looks good.** Trivially correct -- four lines that call `drm_scdc_debugfs_init` when `bridge_connector->bridge_hdmi` is present. The placement inside `drm_bridge_connector_debugfs_init` after the per-bridge `debugfs_init` calls is appropriate. --- Generated by Claude Code Patch Reviewer ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-05-28 2:16 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-20 13:35 [PATCH v2 0/3] Add SCDC information to connector debugfs Nicolas Frattaroli 2026-05-20 13:35 ` [PATCH v2 1/3] drm/scdc-helper: Add scdc_status debugfs entry Nicolas Frattaroli 2026-05-25 11:49 ` Claude review: " Claude Code Review Bot 2026-05-20 13:35 ` [PATCH v2 2/3] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli 2026-05-25 11:49 ` Claude review: " Claude Code Review Bot 2026-05-20 13:35 ` [PATCH v2 3/3] drm/scdc-helper: Implement parsing and printing HDMI 2.1 fields Nicolas Frattaroli 2026-05-22 17:38 ` Daniel Stone 2026-05-25 11:49 ` Claude review: " Claude Code Review Bot 2026-05-25 11:49 ` Claude review: Add SCDC information to connector debugfs Claude Code Review Bot -- strict thread matches above, loose matches on Subject: below -- 2026-05-26 10:19 [PATCH v3 0/4] " Nicolas Frattaroli 2026-05-26 10:19 ` [PATCH v3 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli 2026-05-27 5:01 ` Claude review: " Claude Code Review Bot 2026-05-27 14:03 [PATCH v4 0/4] Add SCDC information to connector debugfs Nicolas Frattaroli 2026-05-27 14:03 ` [PATCH v4 3/4] drm/display: bridge_connector: init scdc debugfs for HDMI Nicolas Frattaroli 2026-05-28 2:16 ` Claude review: " Claude Code Review Bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox