]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/bridge: ite-it66121: Switch to the HDMI connector helpers
authorJavier Martinez Canillas <javierm@redhat.com>
Sat, 23 May 2026 10:40:58 +0000 (12:40 +0200)
committerJavier Martinez Canillas <javierm@redhat.com>
Tue, 26 May 2026 10:27:56 +0000 (12:27 +0200)
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>
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Link: https://patch.msgid.link/20260523-it66121-fix-dvi-mode-v5-v5-1-33b4468162f9@redhat.com
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
drivers/gpu/drm/bridge/Kconfig
drivers/gpu/drm/bridge/ite-it66121.c

index f81b566c82a182880b064240bb2305f8b23f46c9..4a57d49b4c6d3ab4b965228835b372d191647197 100644 (file)
@@ -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
index 19e188fe6e3b2139119afa9f115c4e0c4214d54d..f41f51f300c98b6ffb9a688cb8075e39eca9d399 100644 (file)
@@ -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>
 #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
 #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 @@ unlock:
        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,
+                                                                             &params->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;