drm: rcar-du: Fix DSI enable & disable sequence
authorTomi Valkeinen <tomi.valkeinen+renesas@ideasonboard.com>
Wed, 24 Aug 2022 12:47:25 +0000 (15:47 +0300)
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Wed, 7 Sep 2022 20:48:42 +0000 (23:48 +0300)
The rcar crtc depends on the clock provided from the rcar DSI bridge.
When the DSI bridge is disabled, the clock is stopped, which causes the
crtc disable to timeout.

Also, while I have no issue with the enable, the documentation suggests
to enable the DSI before the crtc so that the crtc has its clock enabled
at enable time. This is also not done by the current driver.

To fix this, we need to keep the DSI bridge enabled until the crtc has
disabled itself, and enable the DSI bridge before crtc enables itself.

Add functions rcar_mipi_dsi_pclk_enable and rcar_mipi_dsi_pclk_disable
to the rcar DSI bridge driver which the rcar driver can use to
enable/disable the DSI clock when needed.  This is similar to what is
already done with the rcar LVDS bridge.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen+renesas@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
drivers/gpu/drm/rcar-du/rcar_du_drv.h
drivers/gpu/drm/rcar-du/rcar_du_encoder.c
drivers/gpu/drm/rcar-du/rcar_mipi_dsi.c
drivers/gpu/drm/rcar-du/rcar_mipi_dsi.h [new file with mode: 0644]

index d38092d85df78b86d6fb8c940f5ab756a1ab9f53..3619e1ddeb620e460c9f1a9f00fcd71720d9db50 100644 (file)
@@ -29,6 +29,7 @@
 #include "rcar_du_regs.h"
 #include "rcar_du_vsp.h"
 #include "rcar_lvds.h"
+#include "rcar_mipi_dsi.h"
 
 static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
 {
@@ -747,6 +748,18 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
                rcar_lvds_pclk_enable(bridge, mode->clock * 1000);
        }
 
+       /*
+        * Similarly to LVDS, on V3U the dot clock is provided by the DSI
+        * encoder, and we need to enable the DSI clocks before enabling the CRTC.
+        */
+       if ((rcdu->info->dsi_clk_mask & BIT(rcrtc->index)) &&
+           (rstate->outputs &
+            (BIT(RCAR_DU_OUTPUT_DSI0) | BIT(RCAR_DU_OUTPUT_DSI1)))) {
+               struct drm_bridge *bridge = rcdu->dsi[rcrtc->index];
+
+               rcar_mipi_dsi_pclk_enable(bridge, state);
+       }
+
        rcar_du_crtc_start(rcrtc);
 
        /*
@@ -780,6 +793,19 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
                rcar_lvds_pclk_disable(bridge);
        }
 
+       if ((rcdu->info->dsi_clk_mask & BIT(rcrtc->index)) &&
+           (rstate->outputs &
+            (BIT(RCAR_DU_OUTPUT_DSI0) | BIT(RCAR_DU_OUTPUT_DSI1)))) {
+               struct drm_bridge *bridge = rcdu->dsi[rcrtc->index];
+
+               /*
+                * Disable the DSI clock output, see
+                * rcar_du_crtc_atomic_enable().
+                */
+
+               rcar_mipi_dsi_pclk_disable(bridge);
+       }
+
        spin_lock_irq(&crtc->dev->event_lock);
        if (crtc->state->event) {
                drm_crtc_send_vblank_event(crtc, crtc->state->event);
index 712389c7b3d0a90a05d1ba6370e76cb7a8eafac8..5cfa2bb7ad93dc2453a361934bb48406b1605a50 100644 (file)
@@ -92,6 +92,7 @@ struct rcar_du_device_info {
 #define RCAR_DU_MAX_GROUPS             DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
 #define RCAR_DU_MAX_VSPS               4
 #define RCAR_DU_MAX_LVDS               2
+#define RCAR_DU_MAX_DSI                        2
 
 struct rcar_du_device {
        struct device *dev;
@@ -108,6 +109,7 @@ struct rcar_du_device {
        struct platform_device *cmms[RCAR_DU_MAX_CRTCS];
        struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS];
        struct drm_bridge *lvds[RCAR_DU_MAX_LVDS];
+       struct drm_bridge *dsi[RCAR_DU_MAX_DSI];
 
        struct {
                struct drm_property *colorkey;
index bfd5c087eb0a507df826a625db4f23e692b1eec5..b1787be31e92c600164e20d5d98dbbd2db751714 100644 (file)
@@ -79,6 +79,10 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
                if (output == RCAR_DU_OUTPUT_LVDS0 ||
                    output == RCAR_DU_OUTPUT_LVDS1)
                        rcdu->lvds[output - RCAR_DU_OUTPUT_LVDS0] = bridge;
+
+               if (output == RCAR_DU_OUTPUT_DSI0 ||
+                   output == RCAR_DU_OUTPUT_DSI1)
+                       rcdu->dsi[output - RCAR_DU_OUTPUT_DSI0] = bridge;
        }
 
        /*
index 9c79fe2fc70bd219cf74fc5303f233b2614d878f..0da3caabfe24387b6f16ab5885ce360a873b5b6f 100644 (file)
@@ -25,6 +25,7 @@
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
 
+#include "rcar_mipi_dsi.h"
 #include "rcar_mipi_dsi_regs.h"
 
 struct rcar_mipi_dsi {
@@ -598,7 +599,22 @@ static int rcar_mipi_dsi_attach(struct drm_bridge *bridge,
 static void rcar_mipi_dsi_atomic_enable(struct drm_bridge *bridge,
                                        struct drm_bridge_state *old_bridge_state)
 {
-       struct drm_atomic_state *state = old_bridge_state->base.state;
+       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
+
+       rcar_mipi_dsi_start_video(dsi);
+}
+
+static void rcar_mipi_dsi_atomic_disable(struct drm_bridge *bridge,
+                                        struct drm_bridge_state *old_bridge_state)
+{
+       struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
+
+       rcar_mipi_dsi_stop_video(dsi);
+}
+
+void rcar_mipi_dsi_pclk_enable(struct drm_bridge *bridge,
+                              struct drm_atomic_state *state)
+{
        struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
        const struct drm_display_mode *mode;
        struct drm_connector *connector;
@@ -626,8 +642,6 @@ static void rcar_mipi_dsi_atomic_enable(struct drm_bridge *bridge,
        if (ret < 0)
                goto err_dsi_start_hs;
 
-       rcar_mipi_dsi_start_video(dsi);
-
        return;
 
 err_dsi_start_hs:
@@ -635,16 +649,16 @@ err_dsi_start_hs:
 err_dsi_startup:
        rcar_mipi_dsi_clk_disable(dsi);
 }
+EXPORT_SYMBOL_GPL(rcar_mipi_dsi_pclk_enable);
 
-static void rcar_mipi_dsi_atomic_disable(struct drm_bridge *bridge,
-                                        struct drm_bridge_state *old_bridge_state)
+void rcar_mipi_dsi_pclk_disable(struct drm_bridge *bridge)
 {
        struct rcar_mipi_dsi *dsi = bridge_to_rcar_mipi_dsi(bridge);
 
-       rcar_mipi_dsi_stop_video(dsi);
        rcar_mipi_dsi_shutdown(dsi);
        rcar_mipi_dsi_clk_disable(dsi);
 }
+EXPORT_SYMBOL_GPL(rcar_mipi_dsi_pclk_disable);
 
 static enum drm_mode_status
 rcar_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge,
diff --git a/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.h b/drivers/gpu/drm/rcar-du/rcar_mipi_dsi.h
new file mode 100644 (file)
index 0000000..528a196
--- /dev/null
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * R-Car DSI Encoder
+ *
+ * Copyright (C) 2022 Renesas Electronics Corporation
+ *
+ * Contact: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+ */
+
+#ifndef __RCAR_MIPI_DSI_H__
+#define __RCAR_MIPI_DSI_H__
+
+struct drm_atomic_state;
+struct drm_bridge;
+
+#if IS_ENABLED(CONFIG_DRM_RCAR_MIPI_DSI)
+void rcar_mipi_dsi_pclk_enable(struct drm_bridge *bridge,
+                              struct drm_atomic_state *state);
+void rcar_mipi_dsi_pclk_disable(struct drm_bridge *bridge);
+#else
+static inline void rcar_mipi_dsi_pclk_enable(struct drm_bridge *bridge,
+                                            struct drm_atomic_state *state)
+{
+}
+
+static inline void rcar_mipi_dsi_pclk_disable(struct drm_bridge *bridge)
+{
+}
+#endif /* CONFIG_DRM_RCAR_MIPI_DSI */
+
+#endif /* __RCAR_MIPI_DSI_H__ */