Unverified Commit 32a19de2 authored by Maxime Ripard's avatar Maxime Ripard
Browse files

drm/vc4: hdmi: Drop devm interrupt handler for CEC interrupts



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: 15b4511a ("drm/vc4: add HDMI CEC support")
Signed-off-by: default avatarMaxime Ripard <maxime@cerno.tech>
Reviewed-by: default avatarDave Stevenson <dave.stevenson@raspberrypi.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210707095112.1469670-2-maxime@cerno.tech
parent 9e5c7729
Loading
Loading
Loading
Loading
+33 −16
Original line number Diff line number Diff line
@@ -1857,25 +1857,23 @@ 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"),
		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"),
		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),
		ret = request_threaded_irq(platform_get_irq(pdev, 0),
					   vc4_cec_irq_handler,
					   vc4_cec_irq_handler_thread, 0,
					   "vc4 hdmi cec", vc4_hdmi);
@@ -1885,10 +1883,20 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)

	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);

@@ -1897,6 +1905,15 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)

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