[PATCH v5 01/11] drm/bridge: it6505: quiesce event sources and work on remove()

From: Daniel Golle

Date: Wed Jul 22 2026 - 11:46:46 EST


struct it6505 is freed by devres right after remove() returns, but
remove() cancels none of the driver's work items, so link_works,
hdcp_wait_ksv_list, hdcp_work and extcon_wq can still run afterwards
and dereference freed memory. Cancelling alone would not be enough:
the threaded IRQ and the extcon notifier stay live until devres
teardown and can requeue the works.

Unregister the extcon notifier and disable the IRQ first, then cancel
all work. Make it6505_remove_notifier_module() idempotent, tracking
registration in event_nb.notifier_call under extcon_lock, as both
.detach() and remove() call it now. Also initialise extcon_wq in
probe: cancel_work_sync() on a never-initialised work item trips
WARN_ON(!work->func).

Fixes: b5c84a9edcd4 ("drm/bridge: add it6505 driver")
Signed-off-by: Daniel Golle <daniel@xxxxxxxxxxxxxx>
---
v5: serialise notifier registration state with extcon_lock

v4:
* quiesce event sources before cancelling work; retitled
* initialise extcon_wq in probe to avoid WARN_ON(!work->func)

v3: new patch

drivers/gpu/drm/bridge/ite-it6505.c | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
index 8ecb43611dba..e9c84e4fb3a0 100644
--- a/drivers/gpu/drm/bridge/ite-it6505.c
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -2934,15 +2934,18 @@ static int it6505_use_notifier_module(struct it6505 *it6505)
int ret;
struct device *dev = it6505->dev;

+ mutex_lock(&it6505->extcon_lock);
it6505->event_nb.notifier_call = it6505_extcon_notifier;
- INIT_WORK(&it6505->extcon_wq, it6505_extcon_work);
ret = devm_extcon_register_notifier(it6505->dev,
it6505->extcon, EXTCON_DISP_DP,
&it6505->event_nb);
if (ret) {
+ it6505->event_nb.notifier_call = NULL;
+ mutex_unlock(&it6505->extcon_lock);
dev_err(dev, "failed to register notifier for DP");
return ret;
}
+ mutex_unlock(&it6505->extcon_lock);

schedule_work(&it6505->extcon_wq);

@@ -2951,13 +2954,16 @@ static int it6505_use_notifier_module(struct it6505 *it6505)

static void it6505_remove_notifier_module(struct it6505 *it6505)
{
- if (it6505->extcon) {
- devm_extcon_unregister_notifier(it6505->dev,
- it6505->extcon, EXTCON_DISP_DP,
+ mutex_lock(&it6505->extcon_lock);
+ if (it6505->extcon && it6505->event_nb.notifier_call) {
+ devm_extcon_unregister_notifier(it6505->dev, it6505->extcon,
+ EXTCON_DISP_DP,
&it6505->event_nb);
-
- flush_work(&it6505->extcon_wq);
+ it6505->event_nb.notifier_call = NULL;
}
+ mutex_unlock(&it6505->extcon_lock);
+
+ flush_work(&it6505->extcon_wq);
}

static void __maybe_unused it6505_delayed_audio(struct work_struct *work)
@@ -3615,6 +3621,7 @@ static int it6505_i2c_probe(struct i2c_client *client)
INIT_WORK(&it6505->link_works, it6505_link_training_work);
INIT_WORK(&it6505->hdcp_wait_ksv_list, it6505_hdcp_wait_ksv_list);
INIT_DELAYED_WORK(&it6505->hdcp_work, it6505_hdcp_work);
+ INIT_WORK(&it6505->extcon_wq, it6505_extcon_work);
init_completion(&it6505->extcon_completion);
memset(it6505->dpcd, 0, sizeof(it6505->dpcd));
it6505->powered = false;
@@ -3647,6 +3654,12 @@ static void it6505_i2c_remove(struct i2c_client *client)
drm_bridge_remove(&it6505->bridge);
drm_dp_aux_unregister(&it6505->aux);
it6505_debugfs_remove(it6505);
+ it6505_remove_notifier_module(it6505);
+ disable_irq(it6505->irq);
+ cancel_work_sync(&it6505->link_works);
+ cancel_work_sync(&it6505->hdcp_wait_ksv_list);
+ cancel_delayed_work_sync(&it6505->hdcp_work);
+ cancel_work_sync(&it6505->extcon_wq);
it6505_poweroff(it6505);
it6505_remove_edid(it6505);
}
--
2.55.0