From: sashiko-bot@kernel.org
To: "Hermes Wu" <Hermes.wu@ite.com.tw>
Cc: dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org,
robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v5 2/2] drm/bridge: Add ITE IT6162 MIPI DSI to HDMI bridge driver
Date: Thu, 29 Oct 2026 10:46:07 +0000 [thread overview]
Message-ID: <sashiko-outbox-189150@kernel.org> (raw)
In-Reply-To: <20261029-upstream-6162-v5-2-8369f9e5afe6@ite.com.tw>
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] drm/bridge: it6162: select DRM_DISPLAY_HDMI_STATE_HELPER in Kconfig
- [High] drm/bridge: it6162: fix bridge.ops flags and set supported_hdmi_ver
- [High] drm/bridge: it6162: avoid HPD notify self-deadlock in IRQ thread
- [High] drm/bridge: it6162: initialize hardware before attaching DSI and bridge
- [High] drm/bridge: it6162: fix OF node refcount underflow in it6162_attach_dsi()
- [High] drm/bridge: it6162: missing port@3 next_bridge and attach flags handling
- [Medium] drm/bridge: it6162: unreachable HDCP code and off-by-one KSV list read
- [Medium] drm/bridge: it6162: missing single-DSI rate limit in it6162_hdmi_tmds_char_rate_valid()
--
commit bb2197e5059c8f22ed7c430b26d564ec316ac54c
Author: Hermes Wu <Hermes.wu@ite.com.tw>
drm/bridge: Add ITE IT6162 MIPI DSI to HDMI bridge driver
This commit adds a DRM bridge driver for the ITE IT6162 MIPI DSI to HDMI 2.0
bridge chip, supporting single and dual MIPI DSI inputs, mode setting, EDID
retrieval, HDMI audio, and HPD handling.
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index c7da6b02d7b6b..59d4911222747 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -106,6 +106,23 @@ config DRM_INNO_HDMI
> select DRM_DISPLAY_HELPER
> select DRM_KMS_HELPER
>
> +config DRM_ITE_IT6162
> + tristate "iTE IT6162 DSI to HDMI bridge"
> + depends on OF
> + select REGMAP_I2C
> + select DRM_MIPI_DSI
> + select DRM_PANEL_BRIDGE
> + select DRM_KMS_HELPER
> + select DRM_HDMI_HELPER
> + select DRM_DISPLAY_HDMI_HELPER
> + select DRM_DISPLAY_HDCP_HELPER
> + select DRM_DISPLAY_HELPER
[Severity: High]
Should this select DRM_DISPLAY_HDMI_STATE_HELPER instead of DRM_HDMI_HELPER?
There is no DRM_HDMI_HELPER Kconfig symbol in the kernel tree. Meanwhile,
it6162_bridge_atomic_enable(), it6162_bridge_hdmi_audio_prepare(), and
it6162_bridge_hdmi_audio_shutdown() call:
drm_atomic_helper_connector_hdmi_update_infoframes()
drm_atomic_helper_connector_hdmi_update_audio_infoframe()
drm_atomic_helper_connector_hdmi_clear_audio_infoframe()
which are compiled only when CONFIG_DRM_DISPLAY_HDMI_STATE_HELPER is enabled.
Without selecting DRM_DISPLAY_HDMI_STATE_HELPER, building the driver when no
other enabled driver selects that symbol fails at link time with undefined
references.
[ ... ]
> diff --git a/drivers/gpu/drm/bridge/ite-it6162.c b/drivers/gpu/drm/bridge/ite-it6162.c
> new file mode 100644
> index 0000000000000..5f83722ff4e61
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/ite-it6162.c
[ ... ]
> +static int it6162_hdcp_read_list(struct it6162 *it6162,
> + u8 *ksv_list,
> + int dev_count)
> +{
> + u8 buf[DATA_BUFFER_DEPTH];
> + int i, j, ret;
> +
> + if (!ksv_list || dev_count <= 0)
> + return -EINVAL;
> +
> + guard(mutex)(&it6162->lock);
> + for (i = 0; i < (dev_count / 6 + 1); i++) {
> + ret = it6162_infoblock_request_data(it6162, HOST_SET_HDCP_R,
> + i + 1, buf);
> + if (ret < 0)
> + return ret;
> +
> + for (j = 0; j < 30; j += 5) {
> + if ((i * 6 + j / 5) >= dev_count)
> + break;
> +
> + memcpy(&ksv_list[(i * 6 + j / 5) * 5], &buf[j], 5);
> + }
> + }
> + return dev_count;
> +}
> +
> +static void it6162_update_hdcp14(struct it6162 *it6162)
> +{
> + struct drm_device *drm = it6162->drm;
> + int dev_count;
> + u8 bksv[5];
> + u8 bstatus[2];
> + u8 ksvlist[5 * 30];
> + int ret;
> +
> + ret = it6162_hdcp_read_information(it6162, bstatus, bksv);
> + if (ret < 0) {
> + drm_err(drm, "fail to read Bstatus");
> + return;
> + }
> +
> + dev_count = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
> + if (dev_count == 0)
> + return;
> +
> + if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus[0]) ||
> + DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus[1])) {
> + drm_err(drm, "HDCP1.4 Max Topology Limit Exceeded");
> + return;
> + }
> +
> + dev_count = dev_count > 30 ? 30 : dev_count;
> + ret = it6162_hdcp_read_list(it6162, ksvlist, dev_count);
> + if (ret < 0) {
> + drm_err(drm, "fail to read KSV list");
> + return;
> + }
> +}
[Severity: Medium]
Is the HDCP support in this driver incomplete or unreachable?
In it6162_config_default(), it6162->en_hdcp is initialized to false and is
never set to true anywhere in the driver, while it6162->bridge.support_hdcp
is also never set. In addition, it6162_bridge_atomic_enable() stores
conn_state->content_protection into it6162->content_protection, which is
never read, and it6162_tx_enable() never calls it6162_tx_hdcp_setup().
Also, in it6162_update_hdcp14() and it6162_update_hdcp23(), the read
bstatus/bksv/ksvlist and rxinfo/rxid/rxid_list buffers are unused local
arrays, and topology or read failures are not propagated back to
it6162_hdcp_handler(), which still sets:
cp_status = DRM_MODE_CONTENT_PROTECTION_ENABLED;
Finally, in it6162_hdcp_read_list(), the loop bound (dev_count / 6 + 1)
instead of DIV_ROUND_UP(dev_count, 6) performs an extra iteration when
dev_count is an exact multiple of 6 (such as 6 or the clamped maximum of 30),
requesting an out-of-range block index via it6162_infoblock_request_data().
Should the unused HDCP code be removed until it is wired up, or completed and
fixed to use DIV_ROUND_UP(dev_count, 6)?
[ ... ]
> +static void it6162_interrupt_handler(struct it6162 *it6162)
> +{
> + unsigned int int_status, tx_status;
> + enum drm_connector_status connector_status;
> + struct device *dev = it6162->dev;
> + struct regmap *regmap = it6162->regmap;
> + int err;
> +
> + err = regmap_read(regmap, OFFSET_EVENT_CHG, &int_status);
> + if (err < 0) {
> + dev_err(dev, "read failed rx reg[0x%x] err: %d",
> + OFFSET_EVENT_CHG, err);
> + return;
> + }
> +
> + regmap_write(it6162->regmap, OFFSET_EVENT_CHG, 0xFF);
> +
> + if (!!GET_BUFFER_STATUS(int_status)) {
> + regmap_write(it6162->regmap, OFFSET_DRIVER_DATA, int_status);
> + it6162->data_buf_sts = GET_BUFFER_STATUS(int_status);
> + wake_up(&it6162->data_buf_wait);
> + }
> +
> + if (!(int_status & EVENT_CHG))
> + return;
> +
> + err = regmap_read(regmap, OFFSET_TX_STATUS, &tx_status);
> + if (err < 0) {
> + dev_err(dev, "read failed rx reg[0x%x] err: %d",
> + OFFSET_TX_STATUS, err);
> + return;
> + }
> +
> + connector_status = GET_TX_HPD_STATUS(tx_status) ?
> + connector_status_connected :
> + connector_status_disconnected;
> +
> + if (it6162->connector_status != connector_status) {
> + it6162->connector_status = connector_status;
> + drm_bridge_hpd_notify(&it6162->bridge,
> + it6162->connector_status);
> + }
[Severity: High]
Won't calling drm_bridge_hpd_notify() synchronously inside
it6162_interrupt_handler() deadlock when HPD processing reads the EDID?
it6162_probe() registers it6162_int_threaded_handler() with IRQF_ONESHOT, so
the IRQ line stays masked and the handler cannot re-enter until
it6162_int_threaded_handler() returns.
However, reading the EDID in it6162_get_edid_block() calls
it6162_infoblock_request_data(), which blocks in wait_event_timeout() on
it6162->data_buf_wait waiting for it6162_int_threaded_handler() to service the
buffer status interrupt and call wake_up(&it6162->data_buf_wait):
it6162_int_threaded_handler()
it6162_interrupt_handler()
drm_bridge_hpd_notify()
drm_bridge_connector_hpd_cb()
drm_kms_helper_connector_hotplug_event()
drm_client_dev_hotplug()
drm_helper_probe_single_connector_modes()
it6162_bridge_read_edid()
it6162_get_edid_block()
it6162_infoblock_request_data()
wait_event_timeout(it6162->data_buf_wait, ...)
Because this runs on the same IRQF_ONESHOT thread that is supposed to wake
data_buf_wait, wait_event_timeout() will always stall for
TIMEOUT_INFOBLOCK_MS (1800 ms) and fail with -ETIMEDOUT.
Similarly, if another thread holds dev->mode_config.mutex and calls
it6162_bridge_read_edid() -> it6162_infoblock_request_data() while
it6162_interrupt_handler() processes EVENT_CHG and calls
drm_bridge_hpd_notify(), the IRQ thread will block waiting for
dev->mode_config.mutex and never process the buffer-ready interrupt to wake
data_buf_wait.
[ ... ]
> +static int it6162_attach_dsi(struct it6162 *it6162)
> +{
> + struct device *dev = it6162->dev;
> + struct device_node *np = dev->of_node;
> + const struct mipi_dsi_device_info info = {"it6162-mipi", 0, np};
> + struct mipi_dsi_device *dsi;
> + struct mipi_dsi_host *host;
> + struct it6162_mipi_cfg *mipi_cfg = &it6162->mipi_cfg;
> + int ret = 0;
> +
> + it6162->dsi = NULL;
> + for (int port = 0; port < 2; port++) {
> + host = it6162_of_get_dsi_host_by_port(it6162, port);
> + if (IS_ERR(host))
> + continue;
> +
> + mipi_cfg->en_port[port] = true;
> +
> + dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
[Severity: High]
Does passing np in mipi_dsi_device_info without taking a reference underflow
the OF node refcount of dev->of_node on teardown?
devm_mipi_dsi_device_register_full() calls mipi_dsi_device_register_full(),
which assigns dsi->dev.of_node = info->node via device_set_node() without
calling of_node_get(). When the DSI device is released on probe failure or
driver unbind, mipi_dsi_dev_release() unconditionally calls:
of_node_put(dev->of_node);
Because no matching reference was acquired for each registered DSI device
(one in single-DSI mode, two in dual-DSI mode), this drops references owned by
the I2C client's device_node.
[ ... ]
> +static int it6162_bridge_attach(struct drm_bridge *bridge,
> + struct drm_encoder *encoder,
> + enum drm_bridge_attach_flags flags)
> +{
> + struct it6162 *it6162 = bridge_to_it6162(bridge);
> + struct drm_device *drm = bridge->dev;
> +
> + it6162->drm = drm;
> + if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
> + drm_dbg(drm, "it6162 driver only copes with atomic updates");
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
[Severity: High]
Should it6162_bridge_attach() check DRM_BRIDGE_ATTACH_NO_CONNECTOR and attach
the downstream HDMI output bridge from port@3?
In it6162_bridge_attach(), the flags parameter is ignored: the function
neither requires DRM_BRIDGE_ATTACH_NO_CONNECTOR nor creates a connector. In
addition, the device tree binding defines port@3 as the HDMI output port
(typically connected to an hdmi-connector node), but it6162_probe() only
checks ports 0, 1, and 2, and never acquires or attaches the next bridge on
port@3.
> +
> +static enum drm_mode_status
> +it6162_bridge_mode_valid(struct drm_bridge *bridge,
> + const struct drm_display_info *info,
> + const struct drm_display_mode *mode)
> +{
> + unsigned long long rate;
> +
> + rate = drm_hdmi_compute_mode_clock(mode, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444);
> + if (rate == 0)
> + return MODE_NOCLOCK;
> +
> + return bridge->funcs->hdmi_tmds_char_rate_valid(bridge, mode, rate);
> +}
[ ... ]
> +static enum drm_mode_status
> +it6162_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
> + const struct drm_display_mode *mode,
> + unsigned long long tmds_rate)
> +{
> + /*IT6162 hdmi supports HDMI2.0 600Mhz*/
> + if (tmds_rate > 600000000)
> + return MODE_CLOCK_HIGH;
> +
> + return MODE_OK;
> +}
[Severity: Medium]
Should it6162_hdmi_tmds_char_rate_valid() or it6162_bridge_mode_valid() also
limit the maximum rate when only a single MIPI DSI port is enabled?
The commit description notes that single MIPI DSI input supports up to
4K @ 30Hz (~300 MHz) while dual MIPI DSI input is required for 4K @ 60Hz
(600 MHz), but it6162_hdmi_tmds_char_rate_valid() allows up to 600 MHz without
checking whether both ports in mipi_cfg.en_port[] are enabled.
[ ... ]
> +static int it6162_probe(struct i2c_client *client)
> +{
[ ... ]
> + it6162->bridge.of_node = np;
> + it6162->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
> + DRM_BRIDGE_OP_HDMI | DRM_BRIDGE_OP_MODES;
> +
> + it6162->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
> +
> + it6162->bridge.vendor = "ITE";
> + it6162->bridge.product = "IT6162";
[Severity: High]
Are the bridge.ops flags and HDMI version fields configured as intended here?
1. DRM_BRIDGE_OP_MODES is set in it6162->bridge.ops, which tells
drm_bridge_connector to call bridge->funcs->get_modes(). Because
it6162_bridge_funcs does not implement .get_modes (it only implements
.edid_read), drm_bridge_get_modes() will dereference a NULL function pointer:
if (!(bridge->ops & DRM_BRIDGE_OP_MODES))
return 0;
return bridge->funcs->get_modes(bridge, connector);
2. it6162_interrupt_handler() calls drm_bridge_hpd_notify(), but
DRM_BRIDGE_OP_HPD is not set in it6162->bridge.ops, so drm_bridge_connector
never registers an HPD callback on this bridge and drm_bridge_hpd_notify() is
a no-op.
3. it6162_bridge_funcs implements .hdmi_write_spd_infoframe and
.hdmi_clear_spd_infoframe, and bridge.vendor and bridge.product are set, but
DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME is omitted from it6162->bridge.ops.
4. DRM_BRIDGE_OP_HDMI is set without setting it6162->bridge.supported_hdmi_ver
(for example to HDMI_VERSION_2_0), which triggers a DRM_WARN("HDMI bridge
misses supported HDMI version\n") in drm_bridge_add().
> +
> + if (it6162_of_get_audio(it6162)) {
> + it6162->bridge.ops |= DRM_BRIDGE_OP_HDMI_AUDIO;
> + it6162->bridge.hdmi_audio_dev = dev;
> + it6162->bridge.hdmi_audio_max_i2s_playback_channels = 8;
> + it6162->bridge.hdmi_audio_dai_port = 2;
> + }
> +
> + /*
> + * devres releases in reverse order: the IRQ is freed and the HDCP
> + * works are cancelled before the bridge is removed, which destroys
> + * the HPD notification mutex, and before the supplies and the driver
> + * mutex are released.
> + */
> + ret = devm_drm_bridge_add(dev, &it6162->bridge);
> + if (ret)
> + return ret;
> +
> + ret = devm_add_action_or_reset(dev, it6162_cancel_work_action, it6162);
> + if (ret)
> + return ret;
> +
> + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
> + it6162_int_threaded_handler,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT |
> + IRQF_NO_AUTOEN,
> + "it6162-intp", it6162);
> + if (ret)
> + return ret;
> +
> + return it6162_attach_dsi(it6162);
> +}
[Severity: High]
Does registering the bridge and attaching the DSI devices before
it6162_hw_start() expose the bridge before the hardware and IRQ are ready?
In it6162_probe(), devm_drm_bridge_add() is called before
devm_request_threaded_irq() and it6162_attach_dsi(). Then, inside
it6162_attach_dsi(), devm_mipi_dsi_attach() is called inside the port loop
before it6162_hw_start() runs:
it6162_attach_dsi()
for (int port = 0; port < 2; port++) {
...
devm_mipi_dsi_attach(dev, dsi);
}
return it6162_hw_start(it6162);
When a DSI host's .attach callback binds the DRM pipeline synchronously during
devm_mipi_dsi_attach(), the bridge is already active while it6162->hw_ready is
still false (causing it6162_bridge_detect() to return
connector_status_disconnected) and client->irq is still disabled with
IRQF_NO_AUTOEN (causing any EDID read via it6162_infoblock_request_data() to
time out). Once it6162_hw_start() subsequently runs, it calls
it6162_hw_reset() and resets the chip after the DSI host has attached.
Also, in it6162_attach_dsi(), if it6162_load_mipi_pars_from_port() were to
return 0, the check:
if (ret <= 0)
return ret;
would return 0 from it6162_probe() without attaching DSI or starting the
hardware.
Should the MIPI DT parameters be parsed and it6162_hw_start() be completed
before publishing the bridge and attaching the DSI host(s)?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261029-upstream-6162-v5-0-8369f9e5afe6@ite.com.tw?part=2
prev parent reply other threads:[~2026-10-29 10:46 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-29 10:12 [PATCH v5 0/2] Add ITE IT6162 MIPI DSI to HDMI bridge driver Hermes Wu via B4 Relay
2026-10-29 10:12 ` [PATCH v5 1/2] dt-bindings: display: bridge: Add ITE IT6162 MIPI DSI to HDMI bridge Hermes Wu via B4 Relay
2026-10-29 10:12 ` [PATCH v5 2/2] drm/bridge: Add ITE IT6162 MIPI DSI to HDMI bridge driver Hermes Wu via B4 Relay
2026-10-29 10:46 ` sashiko-bot [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-189150@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=Hermes.wu@ite.com.tw \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox