forked from Minki/linux
drm/vc4: hdmi: Drop devm interrupt handler for hotplug interrupts
The hotplugs interrupt handlers are registered through the
devm_request_threaded_irq function. However, while free_irq is indeed
called properly when the device is unbound or bind fails, it's called
after unbind or bind is done.
In our particular case, it means that on failure it creates a window
where our interrupt handler can be called, but we're freeing every
resource (CEC adapter, DRM objects, etc.) it might need.
In order to address this, let's switch to the non-devm variant to
control better when the handler will be unregistered and allow us to
make it safe.
Fixes: f4790083c7
("drm/vc4: hdmi: Rely on interrupts to handle hotplug")
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210707095112.1469670-3-maxime@cerno.tech
This commit is contained in:
parent
868d043f05
commit
776efe800f
@ -1590,25 +1590,27 @@ static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi)
|
|||||||
{
|
{
|
||||||
struct drm_connector *connector = &vc4_hdmi->connector;
|
struct drm_connector *connector = &vc4_hdmi->connector;
|
||||||
struct platform_device *pdev = vc4_hdmi->pdev;
|
struct platform_device *pdev = vc4_hdmi->pdev;
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (vc4_hdmi->variant->external_irq_controller) {
|
if (vc4_hdmi->variant->external_irq_controller) {
|
||||||
ret = devm_request_threaded_irq(dev,
|
unsigned int hpd_con = platform_get_irq_byname(pdev, "hpd-connected");
|
||||||
platform_get_irq_byname(pdev, "hpd-connected"),
|
unsigned int hpd_rm = platform_get_irq_byname(pdev, "hpd-removed");
|
||||||
NULL,
|
|
||||||
vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
|
ret = request_threaded_irq(hpd_con,
|
||||||
"vc4 hdmi hpd connected", vc4_hdmi);
|
NULL,
|
||||||
|
vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
|
||||||
|
"vc4 hdmi hpd connected", vc4_hdmi);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = devm_request_threaded_irq(dev,
|
ret = request_threaded_irq(hpd_rm,
|
||||||
platform_get_irq_byname(pdev, "hpd-removed"),
|
NULL,
|
||||||
NULL,
|
vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
|
||||||
vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
|
"vc4 hdmi hpd disconnected", vc4_hdmi);
|
||||||
"vc4 hdmi hpd disconnected", vc4_hdmi);
|
if (ret) {
|
||||||
if (ret)
|
free_irq(hpd_con, vc4_hdmi);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||||
}
|
}
|
||||||
@ -1616,6 +1618,16 @@ static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vc4_hdmi_hotplug_exit(struct vc4_hdmi *vc4_hdmi)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = vc4_hdmi->pdev;
|
||||||
|
|
||||||
|
if (vc4_hdmi->variant->external_irq_controller) {
|
||||||
|
free_irq(platform_get_irq_byname(pdev, "hpd-connected"), vc4_hdmi);
|
||||||
|
free_irq(platform_get_irq_byname(pdev, "hpd-removed"), vc4_hdmi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_DRM_VC4_HDMI_CEC
|
#ifdef CONFIG_DRM_VC4_HDMI_CEC
|
||||||
static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv)
|
static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv)
|
||||||
{
|
{
|
||||||
@ -2180,7 +2192,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
|
|||||||
|
|
||||||
ret = vc4_hdmi_cec_init(vc4_hdmi);
|
ret = vc4_hdmi_cec_init(vc4_hdmi);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_destroy_conn;
|
goto err_free_hotplug;
|
||||||
|
|
||||||
ret = vc4_hdmi_audio_init(vc4_hdmi);
|
ret = vc4_hdmi_audio_init(vc4_hdmi);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -2194,6 +2206,8 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
|
|||||||
|
|
||||||
err_free_cec:
|
err_free_cec:
|
||||||
vc4_hdmi_cec_exit(vc4_hdmi);
|
vc4_hdmi_cec_exit(vc4_hdmi);
|
||||||
|
err_free_hotplug:
|
||||||
|
vc4_hdmi_hotplug_exit(vc4_hdmi);
|
||||||
err_destroy_conn:
|
err_destroy_conn:
|
||||||
vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
|
vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
|
||||||
err_destroy_encoder:
|
err_destroy_encoder:
|
||||||
@ -2235,6 +2249,7 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
|
|||||||
kfree(vc4_hdmi->hd_regset.regs);
|
kfree(vc4_hdmi->hd_regset.regs);
|
||||||
|
|
||||||
vc4_hdmi_cec_exit(vc4_hdmi);
|
vc4_hdmi_cec_exit(vc4_hdmi);
|
||||||
|
vc4_hdmi_hotplug_exit(vc4_hdmi);
|
||||||
vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
|
vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
|
||||||
drm_encoder_cleanup(&vc4_hdmi->encoder.base.base);
|
drm_encoder_cleanup(&vc4_hdmi->encoder.base.base);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user