1 From fda46a52e84a5160d7277e55e1c1be376b0ba579 Mon Sep 17 00:00:00 2001
2 From: Maxime Ripard <maxime@cerno.tech>
3 Date: Mon, 5 Jul 2021 17:31:48 +0200
4 Subject: [PATCH] drm/vc4: hdmi: Drop devm interrupt handler for
7 The hotplugs interrupt handlers are registered through the
8 devm_request_threaded_irq function. However, while free_irq is indeed
9 called properly when the device is unbound or bind fails, it's called
10 after unbind or bind is done.
12 In our particular case, it means that on failure it creates a window
13 where our interrupt handler can be called, but we're freeing every
14 resource (CEC adapter, DRM objects, etc.) it might need.
16 In order to address this, let's switch to the non-devm variant to
17 control better when the handler will be unregistered and allow us to
20 Fixes: f4790083c7c2 ("drm/vc4: hdmi: Rely on interrupts to handle hotplug")
21 Signed-off-by: Maxime Ripard <maxime@cerno.tech>
23 drivers/gpu/drm/vc4/vc4_hdmi.c | 41 +++++++++++++++++++++++-----------
24 1 file changed, 28 insertions(+), 13 deletions(-)
26 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
27 +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
28 @@ -1611,26 +1611,28 @@ static irqreturn_t vc4_hdmi_hpd_irq_thre
29 static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi)
31 struct platform_device *pdev = vc4_hdmi->pdev;
32 - struct device *dev = &pdev->dev;
33 struct drm_connector *connector = &vc4_hdmi->connector;
36 if (vc4_hdmi->variant->external_irq_controller) {
37 - ret = devm_request_threaded_irq(dev,
38 - platform_get_irq_byname(pdev, "hpd-connected"),
40 - vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
41 - "vc4 hdmi hpd connected", vc4_hdmi);
42 + unsigned int hpd_con = platform_get_irq_byname(pdev, "hpd-connected");
43 + unsigned int hpd_rm = platform_get_irq_byname(pdev, "hpd-removed");
45 + ret = request_threaded_irq(hpd_con,
47 + vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
48 + "vc4 hdmi hpd connected", vc4_hdmi);
52 - ret = devm_request_threaded_irq(dev,
53 - platform_get_irq_byname(pdev, "hpd-removed"),
55 - vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
56 - "vc4 hdmi hpd disconnected", vc4_hdmi);
58 + ret = request_threaded_irq(hpd_rm,
60 + vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
61 + "vc4 hdmi hpd disconnected", vc4_hdmi);
63 + free_irq(hpd_con, vc4_hdmi);
67 connector->polled = DRM_CONNECTOR_POLL_HPD;
69 @@ -1638,6 +1640,16 @@ static int vc4_hdmi_hotplug_init(struct
73 +static void vc4_hdmi_hotplug_exit(struct vc4_hdmi *vc4_hdmi)
75 + struct platform_device *pdev = vc4_hdmi->pdev;
77 + if (vc4_hdmi->variant->external_irq_controller) {
78 + free_irq(platform_get_irq_byname(pdev, "hpd-connected"), vc4_hdmi);
79 + free_irq(platform_get_irq_byname(pdev, "hpd-removed"), vc4_hdmi);
83 #ifdef CONFIG_DRM_VC4_HDMI_CEC
84 static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv)
86 @@ -2299,7 +2311,7 @@ static int vc4_hdmi_bind(struct device *
88 ret = vc4_hdmi_cec_init(vc4_hdmi);
90 - goto err_destroy_conn;
91 + goto err_free_hotplug;
93 ret = vc4_hdmi_audio_init(vc4_hdmi);
95 @@ -2313,6 +2325,8 @@ static int vc4_hdmi_bind(struct device *
98 vc4_hdmi_cec_exit(vc4_hdmi);
100 + vc4_hdmi_hotplug_exit(vc4_hdmi);
102 vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
104 @@ -2354,6 +2368,7 @@ static void vc4_hdmi_unbind(struct devic
105 kfree(vc4_hdmi->hd_regset.regs);
107 vc4_hdmi_cec_exit(vc4_hdmi);
108 + vc4_hdmi_hotplug_exit(vc4_hdmi);
109 vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
110 drm_encoder_cleanup(&vc4_hdmi->encoder.base.base);