| From 57d3edaddb8132977dd5bfe585a6d8d823a8e7f0 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 7 Jul 2021 11:51:10 +0200 |
| Subject: drm/vc4: hdmi: Drop devm interrupt handler for CEC interrupts |
| |
| From: Maxime Ripard <maxime@cerno.tech> |
| |
| [ Upstream commit 32a19de21ae40f0601f48575b610dde4f518ccc6 ] |
| |
| The CEC 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: 15b4511a4af6 ("drm/vc4: add HDMI CEC support") |
| 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-2-maxime@cerno.tech |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/gpu/drm/vc4/vc4_hdmi.c | 49 +++++++++++++++++++++++----------- |
| 1 file changed, 33 insertions(+), 16 deletions(-) |
| |
| diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c |
| index 188b74c9e9ff..edee565334d8 100644 |
| --- a/drivers/gpu/drm/vc4/vc4_hdmi.c |
| +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c |
| @@ -1690,38 +1690,46 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) |
| vc4_hdmi_cec_update_clk_div(vc4_hdmi); |
| |
| if (vc4_hdmi->variant->external_irq_controller) { |
| - ret = devm_request_threaded_irq(&pdev->dev, |
| - platform_get_irq_byname(pdev, "cec-rx"), |
| - vc4_cec_irq_handler_rx_bare, |
| - vc4_cec_irq_handler_rx_thread, 0, |
| - "vc4 hdmi cec rx", vc4_hdmi); |
| + ret = request_threaded_irq(platform_get_irq_byname(pdev, "cec-rx"), |
| + vc4_cec_irq_handler_rx_bare, |
| + vc4_cec_irq_handler_rx_thread, 0, |
| + "vc4 hdmi cec rx", vc4_hdmi); |
| if (ret) |
| goto err_delete_cec_adap; |
| |
| - ret = devm_request_threaded_irq(&pdev->dev, |
| - platform_get_irq_byname(pdev, "cec-tx"), |
| - vc4_cec_irq_handler_tx_bare, |
| - vc4_cec_irq_handler_tx_thread, 0, |
| - "vc4 hdmi cec tx", vc4_hdmi); |
| + ret = request_threaded_irq(platform_get_irq_byname(pdev, "cec-tx"), |
| + vc4_cec_irq_handler_tx_bare, |
| + vc4_cec_irq_handler_tx_thread, 0, |
| + "vc4 hdmi cec tx", vc4_hdmi); |
| if (ret) |
| - goto err_delete_cec_adap; |
| + goto err_remove_cec_rx_handler; |
| } else { |
| HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff); |
| |
| - ret = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0), |
| - vc4_cec_irq_handler, |
| - vc4_cec_irq_handler_thread, 0, |
| - "vc4 hdmi cec", vc4_hdmi); |
| + ret = request_threaded_irq(platform_get_irq(pdev, 0), |
| + vc4_cec_irq_handler, |
| + vc4_cec_irq_handler_thread, 0, |
| + "vc4 hdmi cec", vc4_hdmi); |
| if (ret) |
| goto err_delete_cec_adap; |
| } |
| |
| ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev); |
| if (ret < 0) |
| - goto err_delete_cec_adap; |
| + goto err_remove_handlers; |
| |
| return 0; |
| |
| +err_remove_handlers: |
| + if (vc4_hdmi->variant->external_irq_controller) |
| + free_irq(platform_get_irq_byname(pdev, "cec-tx"), vc4_hdmi); |
| + else |
| + free_irq(platform_get_irq(pdev, 0), vc4_hdmi); |
| + |
| +err_remove_cec_rx_handler: |
| + if (vc4_hdmi->variant->external_irq_controller) |
| + free_irq(platform_get_irq_byname(pdev, "cec-rx"), vc4_hdmi); |
| + |
| err_delete_cec_adap: |
| cec_delete_adapter(vc4_hdmi->cec_adap); |
| |
| @@ -1730,6 +1738,15 @@ err_delete_cec_adap: |
| |
| static void vc4_hdmi_cec_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, "cec-rx"), vc4_hdmi); |
| + free_irq(platform_get_irq_byname(pdev, "cec-tx"), vc4_hdmi); |
| + } else { |
| + free_irq(platform_get_irq(pdev, 0), vc4_hdmi); |
| + } |
| + |
| cec_unregister_adapter(vc4_hdmi->cec_adap); |
| } |
| #else |
| -- |
| 2.30.2 |
| |