drm/vc4: hdmi: Make sure the device is powered with CEC
authorMaxime Ripard <maxime@cerno.tech>
Thu, 19 Aug 2021 13:59:30 +0000 (15:59 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 5 Feb 2022 11:38:57 +0000 (12:38 +0100)
Commit 20b0dfa86bef0e80b41b0e5ac38b92f23b6f27f9 upstream.

The original commit depended on a rework commit (724fc856c09e ("drm/vc4:
hdmi: Split the CEC disable / enable functions in two")) that
(rightfully) didn't reach stable.

However, probably because the context changed, when the patch was
applied to stable the pm_runtime_put called got moved to the end of the
vc4_hdmi_cec_adap_enable function (that would have become
vc4_hdmi_cec_disable with the rework) to vc4_hdmi_cec_init.

This means that at probe time, we now drop our reference to the clocks
and power domains and thus end up with a CPU hang when the CPU tries to
access registers.

The call to pm_runtime_resume_and_get() is also problematic since the
.adap_enable CEC hook is called both to enable and to disable the
controller. That means that we'll now call pm_runtime_resume_and_get()
at disable time as well, messing with the reference counting.

The behaviour we should have though would be to have
pm_runtime_resume_and_get() called when the CEC controller is enabled,
and pm_runtime_put when it's disabled.

We need to move things around a bit to behave that way, but it aligns
stable with upstream.

Cc: <stable@vger.kernel.org> # 5.10.x
Cc: <stable@vger.kernel.org> # 5.15.x
Cc: <stable@vger.kernel.org> # 5.16.x
Reported-by: Michael Stapelberg <michael+drm@stapelberg.ch>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/gpu/drm/vc4/vc4_hdmi.c

index 5580267fb3624c93c6b9a93996240ac6bb53928d..2d532c0fe81910104d6c7e59c71f42dc0a592e85 100644 (file)
@@ -1738,18 +1738,18 @@ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
        u32 val;
        int ret;
 
-       ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
-       if (ret)
-               return ret;
+       if (enable) {
+               ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
+               if (ret)
+                       return ret;
 
-       val = HDMI_READ(HDMI_CEC_CNTRL_5);
-       val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET |
-                VC4_HDMI_CEC_CNT_TO_4700_US_MASK |
-                VC4_HDMI_CEC_CNT_TO_4500_US_MASK);
-       val |= ((4700 / usecs) << VC4_HDMI_CEC_CNT_TO_4700_US_SHIFT) |
-              ((4500 / usecs) << VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT);
+               val = HDMI_READ(HDMI_CEC_CNTRL_5);
+               val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET |
+                        VC4_HDMI_CEC_CNT_TO_4700_US_MASK |
+                        VC4_HDMI_CEC_CNT_TO_4500_US_MASK);
+               val |= ((4700 / usecs) << VC4_HDMI_CEC_CNT_TO_4700_US_SHIFT) |
+                       ((4500 / usecs) << VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT);
 
-       if (enable) {
                HDMI_WRITE(HDMI_CEC_CNTRL_5, val |
                           VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
                HDMI_WRITE(HDMI_CEC_CNTRL_5, val);
@@ -1777,7 +1777,10 @@ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
                        HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
                HDMI_WRITE(HDMI_CEC_CNTRL_5, val |
                           VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
+
+               pm_runtime_put(&vc4_hdmi->pdev->dev);
        }
+
        return 0;
 }
 
@@ -1888,8 +1891,6 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
        if (ret < 0)
                goto err_remove_handlers;
 
-       pm_runtime_put(&vc4_hdmi->pdev->dev);
-
        return 0;
 
 err_remove_handlers: