From 32f9d6b14143f306c10be797b98d8d705b15a141 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megous@megous.com>
Date: Sun, 17 Oct 2021 20:14:25 +0200
Subject: [PATCH 443/467] drm: bridge: dw-mipi-dsi: Fix enable/disable of dsi
 controller

The driver had it all wrong. mode_set is not for enabling the
DSI controller, that should be done in pre_enable so that
panel driver can initialize the panel (working dsi controller
is needed for that). Having dsi powerup in mode_set led to
all kind of fun, because disable would be called more often
than mode_set.

The whole panel/dsi enable/disable dance is such (for future
reference):

- dsi: mode set
- panel: prepare (we turn on panel power)
- dsi: pre enable (we enable and setup DSI)
- dsi: enable (we enable video data)
- panel: enable (we configure and turn on the display)

For disable:

- panel: disable (we turn off the display nicely)
- dsi: disable (we disable DSI)
- dsi: post disable
- panel: unprepare (we power off display power, panel should
                    be safely sleeping now)

Signed-off-by: Ondrej Jirman <megous@megous.com>
---
 drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 45 +++++++++++++------
 1 file changed, 32 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index e44e18a01..9cce2aba3 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -266,6 +266,7 @@ struct dw_mipi_dsi {
 	struct dw_mipi_dsi *master; /* dual-dsi master ptr */
 	struct dw_mipi_dsi *slave; /* dual-dsi slave ptr */
 
+	struct drm_display_mode mode;
 	const struct dw_mipi_dsi_plat_data *plat_data;
 };
 
@@ -597,6 +598,8 @@ static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
 {
 	u32 val;
 
+	dev_info(dsi->dev, "mode %d\n", (int)mode_flags);
+
 	dsi_write(dsi, DSI_PWR_UP, RESET);
 
 	if (mode_flags & MIPI_DSI_MODE_VIDEO) {
@@ -871,11 +874,20 @@ static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
 	dsi_write(dsi, DSI_INT_MSK1, 0);
 }
 
+static void dw_mipi_dsi_bridge_disable(struct drm_bridge *bridge)
+{
+	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
+
+	dev_info(dsi->dev, "disable\n");
+}
+
 static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
 {
 	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
 	const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
 
+	dev_info(dsi->dev, "post disable\n");
+
 	/*
 	 * Switch to command mode before panel-bridge post_disable &
 	 * panel unprepare.
@@ -884,15 +896,6 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
 	 */
 	dw_mipi_dsi_set_mode(dsi, 0);
 
-	/*
-	 * TODO Only way found to call panel-bridge post_disable &
-	 * panel unprepare before the dsi "final" disable...
-	 * This needs to be fixed in the drm_bridge framework and the API
-	 * needs to be updated to manage our own call chains...
-	 */
-	if (dsi->panel_bridge->funcs->post_disable)
-		dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge);
-
 	if (phy_ops->power_off)
 		phy_ops->power_off(dsi->plat_data->priv_data);
 
@@ -921,7 +924,7 @@ static unsigned int dw_mipi_dsi_get_lanes(struct dw_mipi_dsi *dsi)
 	return dsi->lanes;
 }
 
-static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
+static void dw_mipi_dsi_enable(struct dw_mipi_dsi *dsi,
 				 const struct drm_display_mode *adjusted_mode)
 {
 	const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
@@ -973,16 +976,30 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
 {
 	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
 
-	dw_mipi_dsi_mode_set(dsi, adjusted_mode);
+	dev_info(dsi->dev, "mode set\n");
+
+	/* Store the display mode for plugin/DKMS poweron events */
+	memcpy(&dsi->mode, mode, sizeof(dsi->mode));
+}
+
+static void dw_mipi_dsi_bridge_pre_enable(struct drm_bridge *bridge)
+{
+	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
+
+	dev_info(dsi->dev, "pre enable\n");
+
+	/* power up the dsi ctl into a command mode */
+	dw_mipi_dsi_enable(dsi, &dsi->mode);
 	if (dsi->slave)
-		dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode);
+		dw_mipi_dsi_enable(dsi->slave, &dsi->mode);
 }
 
 static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
 
-	/* Switch to video mode for panel-bridge enable & panel enable */
+	dev_info(dsi->dev, "enable\n");
+
 	dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
 	if (dsi->slave)
 		dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO);
@@ -1033,7 +1050,9 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge,
 
 static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
 	.mode_set     = dw_mipi_dsi_bridge_mode_set,
+	.pre_enable   = dw_mipi_dsi_bridge_pre_enable,
 	.enable	      = dw_mipi_dsi_bridge_enable,
+	.disable      = dw_mipi_dsi_bridge_disable,
 	.post_disable = dw_mipi_dsi_bridge_post_disable,
 	.mode_valid   = dw_mipi_dsi_bridge_mode_valid,
 	.attach	      = dw_mipi_dsi_bridge_attach,
-- 
2.34.0

