* [PATCH v5 1/3] drm/bridge: ite-it66121: Switch to the HDMI connector helpers
2026-05-23 10:40 [PATCH v5 0/3] drm/bridge: ite-it66121: Switch to HDMI helpers and fix DVI output Javier Martinez Canillas
@ 2026-05-23 10:40 ` Javier Martinez Canillas
2026-05-25 7:50 ` Claude review: " Claude Code Review Bot
2026-05-23 10:40 ` [PATCH v5 2/3] drm/bridge: ite-it66121: Move .mode_set logic to .atomic_enable Javier Martinez Canillas
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Javier Martinez Canillas @ 2026-05-23 10:40 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Phong LE, Sen Wang
Cc: dri-devel, linux-kernel, Javier Martinez Canillas
Instead of open coding the HDMI AVI Infoframes buffer management, use the
helpers provided by the HDMI connector framework.
Also, add callbacks to implement HDMI Vendor Specific Infoframe and Audio
InfoFrame support. The driver was not sending these before, but they are
required when using the HDMI helpers.
These were implemented following the IT66121 Programming Guide.
Suggested-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
---
drivers/gpu/drm/bridge/Kconfig | 2 +
drivers/gpu/drm/bridge/ite-it66121.c | 211 ++++++++++++++++++++++++++---------
2 files changed, 162 insertions(+), 51 deletions(-)
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index f81b566c82a1..4a57d49b4c6d 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -205,6 +205,8 @@ config DRM_LONTIUM_LT8713SX
config DRM_ITE_IT66121
tristate "ITE IT66121 HDMI bridge"
depends on OF
+ select DRM_DISPLAY_HDMI_STATE_HELPER
+ select DRM_DISPLAY_HELPER
select DRM_KMS_HELPER
select REGMAP_I2C
help
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c
index 19e188fe6e3b..f41f51f300c9 100644
--- a/drivers/gpu/drm/bridge/ite-it66121.c
+++ b/drivers/gpu/drm/bridge/ite-it66121.c
@@ -20,6 +20,8 @@
#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
+#include <drm/display/drm_hdmi_helper.h>
+#include <drm/display/drm_hdmi_state_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h>
@@ -161,6 +163,14 @@
#define IT66121_PKT_GEN_CTRL_ON BIT(0)
#define IT66121_PKT_GEN_CTRL_RPT BIT(1)
+#define IT66121_PKT_NULL_CTRL_REG 0xC9
+#define IT66121_PKT_NULL_CTRL_ON BIT(0)
+#define IT66121_PKT_NULL_CTRL_RPT BIT(1)
+
+/* Null packet data registers (used for HDMI Vendor Specific InfoFrame) */
+#define IT66121_PKT_NULL_HB(n) (0x138 + (n))
+#define IT66121_PKT_NULL_PB(n) (0x13B + (n))
+
#define IT66121_AVIINFO_DB1_REG 0x158
#define IT66121_AVIINFO_DB2_REG 0x159
#define IT66121_AVIINFO_DB3_REG 0x15A
@@ -180,6 +190,13 @@
#define IT66121_AVI_INFO_PKT_ON BIT(0)
#define IT66121_AVI_INFO_PKT_RPT BIT(1)
+#define IT66121_AUD_INFO_PKT_REG 0xCE
+#define IT66121_AUD_INFO_PKT_ON BIT(0)
+#define IT66121_AUD_INFO_PKT_RPT BIT(1)
+
+#define IT66121_AUD_INFO_DB1_REG 0x168
+#define IT66121_AUD_INFO_CSUM_REG 0x16D
+
#define IT66121_HDMI_MODE_REG 0xC0
#define IT66121_HDMI_MODE_HDMI BIT(0)
@@ -304,7 +321,6 @@ struct it66121_ctx {
struct i2c_client *client;
u32 bus_width;
struct mutex lock; /* Protects fields below and device registers */
- struct hdmi_avi_infoframe hdmi_avi_infoframe;
struct {
u8 ch_enable;
u8 fs;
@@ -726,6 +742,10 @@ static void it66121_bridge_enable(struct drm_bridge *bridge,
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
ctx->connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ if (WARN_ON(!ctx->connector))
+ return;
+
+ drm_atomic_helper_connector_hdmi_update_infoframes(ctx->connector, state);
it66121_set_mute(ctx, false);
}
@@ -763,40 +783,10 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
- u8 buf[HDMI_INFOFRAME_SIZE(AVI)];
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
- int ret;
mutex_lock(&ctx->lock);
- ret = drm_hdmi_avi_infoframe_from_display_mode(&ctx->hdmi_avi_infoframe, ctx->connector,
- adjusted_mode);
- if (ret) {
- DRM_ERROR("Failed to setup AVI infoframe: %d\n", ret);
- goto unlock;
- }
-
- ret = hdmi_avi_infoframe_pack(&ctx->hdmi_avi_infoframe, buf, sizeof(buf));
- if (ret < 0) {
- DRM_ERROR("Failed to pack infoframe: %d\n", ret);
- goto unlock;
- }
-
- /* Write new AVI infoframe packet */
- ret = regmap_bulk_write(ctx->regmap, IT66121_AVIINFO_DB1_REG,
- &buf[HDMI_INFOFRAME_HEADER_SIZE],
- HDMI_AVI_INFOFRAME_SIZE);
- if (ret)
- goto unlock;
-
- if (regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buf[3]))
- goto unlock;
-
- /* Enable AVI infoframe */
- if (regmap_write(ctx->regmap, IT66121_AVI_INFO_PKT_REG,
- IT66121_AVI_INFO_PKT_ON | IT66121_AVI_INFO_PKT_RPT))
- goto unlock;
-
/* Set TX mode to HDMI */
if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI))
goto unlock;
@@ -824,24 +814,6 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge,
mutex_unlock(&ctx->lock);
}
-static enum drm_mode_status it66121_bridge_mode_valid(struct drm_bridge *bridge,
- const struct drm_display_info *info,
- const struct drm_display_mode *mode)
-{
- struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
- unsigned long max_clock;
-
- max_clock = (ctx->bus_width == 12) ? 74250 : 148500;
-
- if (mode->clock > max_clock)
- return MODE_CLOCK_HIGH;
-
- if (mode->clock < 25000)
- return MODE_CLOCK_LOW;
-
- return MODE_OK;
-}
-
static enum drm_connector_status
it66121_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
{
@@ -872,6 +844,128 @@ static void it66121_bridge_hpd_disable(struct drm_bridge *bridge)
dev_err(ctx->dev, "failed to disable HPD IRQ\n");
}
+static enum drm_mode_status
+it66121_bridge_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ unsigned long long tmds_rate)
+{
+ const struct it66121_ctx *ctx =
+ container_of(bridge, const struct it66121_ctx, bridge);
+ unsigned long long max_rate;
+
+ max_rate = (ctx->bus_width == 12) ? 74250000ULL : 148500000ULL;
+
+ if (tmds_rate > max_rate)
+ return MODE_CLOCK_HIGH;
+
+ if (tmds_rate < HDMI_TMDS_CHAR_RATE_MIN_HZ)
+ return MODE_CLOCK_LOW;
+
+ return MODE_OK;
+}
+
+static int it66121_bridge_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+
+ /* Clear both IT66121_AVI_INFO_PKT_ON and IT66121_AVI_INFO_PKT_RPT */
+ return regmap_write(ctx->regmap, IT66121_AVI_INFO_PKT_REG, 0);
+}
+
+static int it66121_bridge_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+ int ret;
+
+ mutex_lock(&ctx->lock);
+
+ /* Write new AVI infoframe packet */
+ ret = regmap_bulk_write(ctx->regmap, IT66121_AVIINFO_DB1_REG,
+ &buffer[HDMI_INFOFRAME_HEADER_SIZE],
+ HDMI_AVI_INFOFRAME_SIZE);
+ if (ret)
+ goto unlock;
+
+ ret = regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buffer[3]);
+ if (ret)
+ goto unlock;
+
+ /* Enable AVI infoframe */
+ ret = regmap_write(ctx->regmap, IT66121_AVI_INFO_PKT_REG,
+ IT66121_AVI_INFO_PKT_ON | IT66121_AVI_INFO_PKT_RPT);
+
+unlock:
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
+static int it66121_bridge_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+
+ /* Clear both IT66121_PKT_NULL_CTRL_ON and IT66121_PKT_NULL_CTRL_RPT */
+ return regmap_write(ctx->regmap, IT66121_PKT_NULL_CTRL_REG, 0);
+}
+
+static int it66121_bridge_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+ int ret;
+
+ mutex_lock(&ctx->lock);
+
+ /* Write new HDMI Vendor Specific Infoframe packet */
+ ret = regmap_bulk_write(ctx->regmap, IT66121_PKT_NULL_HB(0), buffer, len);
+ if (ret)
+ goto unlock;
+
+ /* Enable HDMI Vendor Specific Infoframe */
+ ret = regmap_write(ctx->regmap, IT66121_PKT_NULL_CTRL_REG,
+ IT66121_PKT_NULL_CTRL_ON | IT66121_PKT_NULL_CTRL_RPT);
+
+unlock:
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
+static int it66121_bridge_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+
+ /* Clear both IT66121_AUD_INFO_PKT_ON and IT66121_AUD_INFO_PKT_RPT */
+ return regmap_write(ctx->regmap, IT66121_AUD_INFO_PKT_REG, 0);
+}
+
+static int it66121_bridge_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+ int ret;
+
+ mutex_lock(&ctx->lock);
+
+ /* Write new Audio infoframe packet */
+ ret = regmap_bulk_write(ctx->regmap, IT66121_AUD_INFO_DB1_REG,
+ &buffer[HDMI_INFOFRAME_HEADER_SIZE],
+ min_t(size_t, len - HDMI_INFOFRAME_HEADER_SIZE, 5));
+ if (ret)
+ goto unlock;
+
+ ret = regmap_write(ctx->regmap, IT66121_AUD_INFO_CSUM_REG, buffer[3]);
+ if (ret)
+ goto unlock;
+
+ /* Enable Audio infoframe */
+ ret = regmap_write(ctx->regmap, IT66121_AUD_INFO_PKT_REG,
+ IT66121_AUD_INFO_PKT_ON | IT66121_AUD_INFO_PKT_RPT);
+
+unlock:
+ mutex_unlock(&ctx->lock);
+ return ret;
+}
+
static const struct drm_edid *it66121_bridge_edid_read(struct drm_bridge *bridge,
struct drm_connector *connector)
{
@@ -1359,6 +1453,10 @@ static int it66121_hdmi_audio_prepare(struct drm_bridge *bridge,
out:
mutex_unlock(&ctx->lock);
+ if (!ret)
+ ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector,
+ ¶ms->cea);
+
return ret;
}
@@ -1384,6 +1482,8 @@ static void it66121_hdmi_audio_shutdown(struct drm_bridge *bridge,
int ret;
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+ drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector);
+
mutex_lock(&ctx->lock);
ret = it661221_audio_output_enable(ctx, false);
if (ret)
@@ -1433,11 +1533,17 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = {
.atomic_disable = it66121_bridge_disable,
.atomic_check = it66121_bridge_check,
.mode_set = it66121_bridge_mode_set,
- .mode_valid = it66121_bridge_mode_valid,
.detect = it66121_bridge_detect,
.edid_read = it66121_bridge_edid_read,
.hpd_enable = it66121_bridge_hpd_enable,
.hpd_disable = it66121_bridge_hpd_disable,
+ .hdmi_tmds_char_rate_valid = it66121_bridge_hdmi_tmds_char_rate_valid,
+ .hdmi_clear_avi_infoframe = it66121_bridge_hdmi_clear_avi_infoframe,
+ .hdmi_write_avi_infoframe = it66121_bridge_hdmi_write_avi_infoframe,
+ .hdmi_clear_hdmi_infoframe = it66121_bridge_hdmi_clear_hdmi_infoframe,
+ .hdmi_write_hdmi_infoframe = it66121_bridge_hdmi_write_hdmi_infoframe,
+ .hdmi_clear_audio_infoframe = it66121_bridge_hdmi_clear_audio_infoframe,
+ .hdmi_write_audio_infoframe = it66121_bridge_hdmi_write_audio_infoframe,
.hdmi_audio_startup = it66121_hdmi_audio_startup,
.hdmi_audio_prepare = it66121_hdmi_audio_prepare,
.hdmi_audio_shutdown = it66121_hdmi_audio_shutdown,
@@ -1538,7 +1644,10 @@ static int it66121_probe(struct i2c_client *client)
ctx->bridge.of_node = dev->of_node;
ctx->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
- ctx->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
+ ctx->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
+ DRM_BRIDGE_OP_HDMI;
+ ctx->bridge.vendor = "ITE";
+ ctx->bridge.product = "IT66121";
if (client->irq > 0) {
ctx->bridge.ops |= DRM_BRIDGE_OP_HPD;
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v5 2/3] drm/bridge: ite-it66121: Move .mode_set logic to .atomic_enable
2026-05-23 10:40 [PATCH v5 0/3] drm/bridge: ite-it66121: Switch to HDMI helpers and fix DVI output Javier Martinez Canillas
2026-05-23 10:40 ` [PATCH v5 1/3] drm/bridge: ite-it66121: Switch to the HDMI connector helpers Javier Martinez Canillas
@ 2026-05-23 10:40 ` Javier Martinez Canillas
2026-05-25 7:50 ` Claude review: " Claude Code Review Bot
2026-05-23 10:41 ` [PATCH v5 3/3] drm/bridge: ite-it66121: Select HDMI or DVI mode based on sink type Javier Martinez Canillas
2026-05-25 7:50 ` Claude review: drm/bridge: ite-it66121: Switch to HDMI helpers and fix DVI output Claude Code Review Bot
3 siblings, 1 reply; 8+ messages in thread
From: Javier Martinez Canillas @ 2026-05-23 10:40 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Phong LE, Sen Wang
Cc: dri-devel, linux-kernel, Javier Martinez Canillas
Move the existing .mode_set logic to the .atomic_enable callback. The
former is deprecated and drivers are supposed to use the latter instead.
Also, drop the struct it66121_ctx.connector field because the connector
can be accessed through the atomic state and there is no need to store
it anymore.
Suggested-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
---
drivers/gpu/drm/bridge/ite-it66121.c | 100 ++++++++++++++++++++---------------
1 file changed, 57 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c
index f41f51f300c9..9725e5db5db3 100644
--- a/drivers/gpu/drm/bridge/ite-it66121.c
+++ b/drivers/gpu/drm/bridge/ite-it66121.c
@@ -315,7 +315,6 @@ struct it66121_chip_info {
struct it66121_ctx {
struct regmap *regmap;
struct drm_bridge bridge;
- struct drm_connector *connector;
struct device *dev;
struct gpio_desc *gpio_reset;
struct i2c_client *client;
@@ -668,6 +667,58 @@ static int it66121_bridge_attach(struct drm_bridge *bridge,
return 0;
}
+static void it66121_set_mode(struct it66121_ctx *ctx,
+ struct drm_connector *connector,
+ struct drm_atomic_commit *state)
+{
+ const struct drm_connector_state *conn_state;
+ const struct drm_crtc_state *crtc_state;
+ const struct drm_display_mode *mode;
+ struct drm_crtc *crtc;
+
+ conn_state = drm_atomic_get_new_connector_state(state, connector);
+ if (WARN_ON(!conn_state))
+ return;
+
+ crtc = conn_state->crtc;
+ if (WARN_ON(!crtc))
+ return;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ if (WARN_ON(!crtc_state))
+ return;
+
+ mode = &crtc_state->adjusted_mode;
+
+ mutex_lock(&ctx->lock);
+
+ /* Set TX mode to HDMI */
+ if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI))
+ goto unlock;
+
+ if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) &&
+ regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
+ IT66121_CLK_BANK_PWROFF_TXCLK,
+ IT66121_CLK_BANK_PWROFF_TXCLK)) {
+ goto unlock;
+ }
+
+ if (it66121_configure_input(ctx))
+ goto unlock;
+
+ if (it66121_configure_afe(ctx, mode))
+ goto unlock;
+
+ if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) &&
+ regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
+ IT66121_CLK_BANK_PWROFF_TXCLK, 0)) {
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&ctx->lock);
+}
+
static int it66121_set_mute(struct it66121_ctx *ctx, bool mute)
{
int ret;
@@ -740,13 +791,15 @@ static void it66121_bridge_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *state)
{
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+ struct drm_connector *connector;
- ctx->connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
- if (WARN_ON(!ctx->connector))
+ connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ if (WARN_ON(!connector))
return;
- drm_atomic_helper_connector_hdmi_update_infoframes(ctx->connector, state);
+ drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
+ it66121_set_mode(ctx, connector, state);
it66121_set_mute(ctx, false);
}
@@ -756,8 +809,6 @@ static void it66121_bridge_disable(struct drm_bridge *bridge,
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
it66121_set_mute(ctx, true);
-
- ctx->connector = NULL;
}
static int it66121_bridge_check(struct drm_bridge *bridge,
@@ -778,42 +829,6 @@ static int it66121_bridge_check(struct drm_bridge *bridge,
return 0;
}
-static
-void it66121_bridge_mode_set(struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- const struct drm_display_mode *adjusted_mode)
-{
- struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
-
- mutex_lock(&ctx->lock);
-
- /* Set TX mode to HDMI */
- if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI))
- goto unlock;
-
- if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) &&
- regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
- IT66121_CLK_BANK_PWROFF_TXCLK,
- IT66121_CLK_BANK_PWROFF_TXCLK)) {
- goto unlock;
- }
-
- if (it66121_configure_input(ctx))
- goto unlock;
-
- if (it66121_configure_afe(ctx, adjusted_mode))
- goto unlock;
-
- if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) &&
- regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
- IT66121_CLK_BANK_PWROFF_TXCLK, 0)) {
- goto unlock;
- }
-
-unlock:
- mutex_unlock(&ctx->lock);
-}
-
static enum drm_connector_status
it66121_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
{
@@ -1532,7 +1547,6 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = {
.atomic_enable = it66121_bridge_enable,
.atomic_disable = it66121_bridge_disable,
.atomic_check = it66121_bridge_check,
- .mode_set = it66121_bridge_mode_set,
.detect = it66121_bridge_detect,
.edid_read = it66121_bridge_edid_read,
.hpd_enable = it66121_bridge_hpd_enable,
--
2.54.0
^ permalink raw reply related [flat|nested] 8+ messages in thread