Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/bridge/lontium-lt9611uxc: move HPD notification out of IRQ handler

drm hotplug handling code (drm_client_dev_hotplug()) can wait on mutex,
thus delaying further lt9611uxc IRQ events processing. It was observed
occasionally during bootups, when drm_client_modeset_probe() was waiting
for EDID ready event, which was delayed because IRQ handler was stuck
trying to deliver hotplug event.
Move hotplug notifications from IRQ handler to separate work to be able
to process IRQ events without delays.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Fixes: 0cbbd5b1a012 ("drm: bridge: add support for lontium LT9611UXC bridge")
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210121233303.1221784-4-dmitry.baryshkov@linaro.org

authored by

Dmitry Baryshkov and committed by
Andrzej Hajda
bc6fa867 1bb7ab40

+37 -9
+37 -9
drivers/gpu/drm/bridge/lontium-lt9611uxc.c
··· 14 14 #include <linux/regmap.h> 15 15 #include <linux/regulator/consumer.h> 16 16 #include <linux/wait.h> 17 + #include <linux/workqueue.h> 17 18 18 19 #include <sound/hdmi-codec.h> 19 20 ··· 37 36 struct mutex ocm_lock; 38 37 39 38 struct wait_queue_head wq; 39 + struct work_struct work; 40 40 41 41 struct device_node *dsi0_node; 42 42 struct device_node *dsi1_node; ··· 54 52 55 53 bool hpd_supported; 56 54 bool edid_read; 55 + /* can be accessed from different threads, so protect this with ocm_lock */ 56 + bool hdmi_connected; 57 57 uint8_t fw_version; 58 58 }; 59 59 ··· 147 143 if (irq_status) 148 144 regmap_write(lt9611uxc->regmap, 0xb022, 0); 149 145 150 - lt9611uxc_unlock(lt9611uxc); 151 - 152 146 if (irq_status & BIT(0)) { 153 147 lt9611uxc->edid_read = !!(hpd_status & BIT(0)); 154 148 wake_up_all(&lt9611uxc->wq); 155 149 } 156 150 157 151 if (irq_status & BIT(1)) { 158 - if (lt9611uxc->connector.dev) 159 - drm_kms_helper_hotplug_event(lt9611uxc->connector.dev); 160 - else 161 - drm_bridge_hpd_notify(&lt9611uxc->bridge, !!(hpd_status & BIT(1))); 152 + lt9611uxc->hdmi_connected = hpd_status & BIT(1); 153 + schedule_work(&lt9611uxc->work); 162 154 } 163 155 156 + lt9611uxc_unlock(lt9611uxc); 157 + 164 158 return IRQ_HANDLED; 159 + } 160 + 161 + static void lt9611uxc_hpd_work(struct work_struct *work) 162 + { 163 + struct lt9611uxc *lt9611uxc = container_of(work, struct lt9611uxc, work); 164 + bool connected; 165 + 166 + if (lt9611uxc->connector.dev) 167 + drm_kms_helper_hotplug_event(lt9611uxc->connector.dev); 168 + else { 169 + 170 + mutex_lock(&lt9611uxc->ocm_lock); 171 + connected = lt9611uxc->hdmi_connected; 172 + mutex_unlock(&lt9611uxc->ocm_lock); 173 + 174 + drm_bridge_hpd_notify(&lt9611uxc->bridge, 175 + connected ? 176 + connector_status_connected : 177 + connector_status_disconnected); 178 + } 165 179 } 166 180 167 181 static void lt9611uxc_reset(struct lt9611uxc *lt9611uxc) ··· 469 447 struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); 470 448 unsigned int reg_val = 0; 471 449 int ret; 472 - int connected = 1; 450 + bool connected = true; 451 + 452 + lt9611uxc_lock(lt9611uxc); 473 453 474 454 if (lt9611uxc->hpd_supported) { 475 - lt9611uxc_lock(lt9611uxc); 476 455 ret = regmap_read(lt9611uxc->regmap, 0xb023, &reg_val); 477 - lt9611uxc_unlock(lt9611uxc); 478 456 479 457 if (ret) 480 458 dev_err(lt9611uxc->dev, "failed to read hpd status: %d\n", ret); 481 459 else 482 460 connected = reg_val & BIT(1); 483 461 } 462 + lt9611uxc->hdmi_connected = connected; 463 + 464 + lt9611uxc_unlock(lt9611uxc); 484 465 485 466 return connected ? connector_status_connected : 486 467 connector_status_disconnected; ··· 956 931 lt9611uxc->fw_version = ret; 957 932 958 933 init_waitqueue_head(&lt9611uxc->wq); 934 + INIT_WORK(&lt9611uxc->work, lt9611uxc_hpd_work); 935 + 959 936 ret = devm_request_threaded_irq(dev, client->irq, NULL, 960 937 lt9611uxc_irq_thread_handler, 961 938 IRQF_ONESHOT, "lt9611uxc", lt9611uxc); ··· 994 967 struct lt9611uxc *lt9611uxc = i2c_get_clientdata(client); 995 968 996 969 disable_irq(client->irq); 970 + flush_scheduled_work(); 997 971 lt9611uxc_audio_exit(lt9611uxc); 998 972 drm_bridge_remove(&lt9611uxc->bridge); 999 973