linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/4 v2] drm/mcde: Improve pixel fetcher FIFO depth setting
@ 2020-08-08 22:31 Linus Walleij
  2020-08-08 22:31 ` [PATCH 2/4 v2] drm/mcde: Support using DSI in LP mode Linus Walleij
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Linus Walleij @ 2020-08-08 22:31 UTC (permalink / raw)
  To: dri-devel, Maarten Lankhorst, Maxime Ripard, Sean Paul
  Cc: Linus Walleij, newbytee, Stephan Gerhold, linux-arm-kernel

The pixel fetcher FIFO depth was just hardcoded to 48
which works fine as long as the framebuffer is 32BPP
and the DSI output is RGB888.

We will need more elaborate handling for some buffer
formats and displays, so start to improve this function
by setting reasonable defaults for 32, 24 and 16 BPP
framebuffers.

Cc: newbytee@protonmail.com
Cc: Stephan Gerhold <stephan@gerhold.net>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/gpu/drm/mcde/mcde_display.c | 34 +++++++++++++++++++++++++----
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
index cac660ac8803..cbc7c0c4955a 100644
--- a/drivers/gpu/drm/mcde/mcde_display.c
+++ b/drivers/gpu/drm/mcde/mcde_display.c
@@ -333,7 +333,7 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
 				   enum mcde_extsrc src,
 				   enum mcde_channel ch,
 				   const struct drm_display_mode *mode,
-				   u32 format)
+				   u32 format, int cpp)
 {
 	u32 val;
 	u32 conf1;
@@ -342,6 +342,7 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
 	u32 ljinc;
 	u32 cr;
 	u32 comp;
+	u32 pixel_fetcher_watermark;
 
 	switch (ovl) {
 	case MCDE_OVERLAY_0:
@@ -426,8 +427,33 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
 			format);
 		break;
 	}
-	/* The default watermark level for overlay 0 is 48 */
-	val |= 48 << MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT;
+
+	/*
+	 * Pixel fetch watermark level is max 0x1FFF pixels.
+	 * Two basic rules should be followed:
+	 * 1. The value should be at least 256 bits.
+	 * 2. The sum of all active overlays pixelfetch watermark level
+	 *    multiplied with bits per pixel, should be lower than the
+	 *    size of input_fifo_size in bits.
+	 * 3. The value should be a multiple of a line (256 bits).
+	 */
+	switch (cpp) {
+	case 2:
+		pixel_fetcher_watermark = 128;
+		break;
+	case 3:
+		pixel_fetcher_watermark = 96;
+		break;
+	case 4:
+		pixel_fetcher_watermark = 48;
+		break;
+	default:
+		pixel_fetcher_watermark = 48;
+		break;
+	}
+	dev_dbg(mcde->dev, "pixel fetcher watermark level %d pixels\n",
+		pixel_fetcher_watermark);
+	val |= pixel_fetcher_watermark << MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT;
 	writel(val, mcde->regs + conf2);
 
 	/* Number of bytes to fetch per line */
@@ -932,7 +958,7 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
 	 * channel 0
 	 */
 	mcde_configure_overlay(mcde, MCDE_OVERLAY_0, MCDE_EXTSRC_0,
-			       MCDE_CHANNEL_0, mode, format);
+			       MCDE_CHANNEL_0, mode, format, cpp);
 
 	/*
 	 * Configure pixel-per-line and line-per-frame for channel 0 and then
-- 
2.26.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4 v2] drm/mcde: Support using DSI in LP mode
  2020-08-08 22:31 [PATCH 1/4 v2] drm/mcde: Improve pixel fetcher FIFO depth setting Linus Walleij
@ 2020-08-08 22:31 ` Linus Walleij
  2020-08-08 22:31 ` [PATCH 3/4 v2] drm/mcde: Fix display pipeline restart Linus Walleij
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Linus Walleij @ 2020-08-08 22:31 UTC (permalink / raw)
  To: dri-devel, Maarten Lankhorst, Maxime Ripard, Sean Paul
  Cc: Linus Walleij, newbytee, Stephan Gerhold, linux-arm-kernel

It is possible to set a flag in the struct mipi_dsi_device
so the panel is handled in low power (LP) mode. Some displays
only support this mode and it is also good for testing.

Cc: newbytee@protonmail.com
Cc: Stephan Gerhold <stephan@gerhold.net>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/gpu/drm/mcde/mcde_dsi.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index 76fecd7ab658..e335041e6e90 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -830,10 +830,11 @@ static void mcde_dsi_start(struct mcde_dsi *d)
 	/* Command mode, clear IF1 ID */
 	val = readl(d->regs + DSI_CMD_MODE_CTL);
 	/*
-	 * If we enable low-power mode here, with
-	 * val |= DSI_CMD_MODE_CTL_IF1_LP_EN
+	 * If we enable low-power mode here,
 	 * then display updates become really slow.
 	 */
+	if (d->mdsi->mode_flags & MIPI_DSI_MODE_LPM)
+		val |= DSI_CMD_MODE_CTL_IF1_LP_EN;
 	val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK;
 	writel(val, d->regs + DSI_CMD_MODE_CTL);
 
@@ -922,10 +923,11 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
 		/* Command mode, clear IF1 ID */
 		val = readl(d->regs + DSI_CMD_MODE_CTL);
 		/*
-		 * If we enable low-power mode here with
-		 * val |= DSI_CMD_MODE_CTL_IF1_LP_EN
+		 * If we enable low-power mode here
 		 * the display updates become really slow.
 		 */
+		if (d->mdsi->mode_flags & MIPI_DSI_MODE_LPM)
+			val |= DSI_CMD_MODE_CTL_IF1_LP_EN;
 		val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK;
 		writel(val, d->regs + DSI_CMD_MODE_CTL);
 	}
-- 
2.26.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4 v2] drm/mcde: Fix display pipeline restart
  2020-08-08 22:31 [PATCH 1/4 v2] drm/mcde: Improve pixel fetcher FIFO depth setting Linus Walleij
  2020-08-08 22:31 ` [PATCH 2/4 v2] drm/mcde: Support using DSI in LP mode Linus Walleij
@ 2020-08-08 22:31 ` Linus Walleij
  2020-08-08 22:31 ` [PATCH 4/4 v2] drm/mcde: Enable the DSI link with display Linus Walleij
  2020-08-10 12:54 ` [PATCH 1/4 v2] drm/mcde: Improve pixel fetcher FIFO depth setting Daniel Vetter
  3 siblings, 0 replies; 6+ messages in thread
From: Linus Walleij @ 2020-08-08 22:31 UTC (permalink / raw)
  To: dri-devel, Maarten Lankhorst, Maxime Ripard, Sean Paul
  Cc: Linus Walleij, newbytee, Stephan Gerhold, linux-arm-kernel

To make sure that the MCDE is in a reasonable state during
set-up, perform a reset by power cycling the block by dropping
the on-chip regulator reference after probe. The display
subsystem (DSS) has no dedicated reset line so dropping
the EPOD regulator is the only real way of resetting it.

We introduce code to enable and disable the regulator in
the display enable/disable callbacks.

We move the generic MCDE setup such as muxing of DPI
signals and masking of interrupts to the display
handling.

When we drop the power to the whole display subsystem, not
only MCDE but also the DSI links lose their state. Therefore
we move the DSI block reset and hardware initialization
code to the mcde_dsi_bridge_pre_enable() callback so this
happens every time we start up the bridge, as we may have
lost the power.

We move the final disablement of the interrupts and
clocks to the mcde_dsi_bridge_post_disable() callback
rather than have it in the mcde_dsi_bridge_disable()
callback, as some control messages may still be sent
over the DSI host after the bridge has been shut down.

This (together with a patch for the corresponding
panel) makes the Samsung GT-S7710 successfully disable
and re-enable its display, cutting all power while
disabled and re-initializing the hardware when coming
back up.

Cc: newbytee@protonmail.com
Cc: Stephan Gerhold <stephan@gerhold.net>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- Put the EPOD regulator disable call after registering
  the subcompontents (the DSI ports) but before
  registering the master so we keep the power to the
  MCDE enabled while scanning he hardware but cut
  the power before we fire up the master.
- Increase the power toggle reset time to 50 ms.
- Move the set up of the video mode to the bridge
  pre_enable() callback as well, as we have no clock
  runing when mode_set() is called on the bridge and
  cannot write to the hardware yet.
---
 drivers/gpu/drm/mcde/mcde_display.c | 41 +++++++++++++++
 drivers/gpu/drm/mcde/mcde_drm.h     | 39 +++++++++++++++
 drivers/gpu/drm/mcde/mcde_drv.c     | 78 +++++++----------------------
 drivers/gpu/drm/mcde/mcde_dsi.c     | 57 +++++++++++++--------
 4 files changed, 133 insertions(+), 82 deletions(-)

diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
index cbc7c0c4955a..d608cc894e01 100644
--- a/drivers/gpu/drm/mcde/mcde_display.c
+++ b/drivers/gpu/drm/mcde/mcde_display.c
@@ -7,6 +7,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/dma-buf.h>
+#include <linux/regulator/consumer.h>
 
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
@@ -879,6 +880,14 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
 	u32 formatter_frame;
 	u32 pkt_div;
 	u32 val;
+	int ret;
+
+	/* This powers up the entire MCDE block and the DSI hardware */
+	ret = regulator_enable(mcde->epod);
+	if (ret) {
+		dev_err(drm->dev, "can't re-enable EPOD regulator\n");
+		return;
+	}
 
 	dev_info(drm->dev, "enable MCDE, %d x %d format %s\n",
 		 mode->hdisplay, mode->vdisplay,
@@ -889,6 +898,26 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
 		return;
 	}
 
+	/* Set up the main control, watermark level at 7 */
+	val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT;
+	/* 24 bits DPI: connect LSB Ch B to D[0:7] */
+	val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT;
+	/* TV out: connect LSB Ch B to D[8:15] */
+	val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT;
+	/* Don't care about this muxing */
+	val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT;
+	/* 24 bits DPI: connect MID Ch B to D[24:31] */
+	val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT;
+	/* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */
+	val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT;
+	/* Syncmux bits zero: DPI channel A and B on output pins A and B resp */
+	writel(val, mcde->regs + MCDE_CONF0);
+
+	/* Clear any pending interrupts */
+	mcde_display_disable_irqs(mcde);
+	writel(0, mcde->regs + MCDE_IMSCERR);
+	writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR);
+
 	dev_info(drm->dev, "output in %s mode, format %dbpp\n",
 		 (mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ?
 		 "VIDEO" : "CMD",
@@ -1005,6 +1034,11 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
 		dev_dbg(mcde->dev, "started MCDE video FIFO flow\n");
 	}
 
+	/* Enable automatic clock gating */
+	val = readl(mcde->regs + MCDE_CR);
+	val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN;
+	writel(val, mcde->regs + MCDE_CR);
+
 	dev_info(drm->dev, "MCDE display is enabled\n");
 }
 
@@ -1014,6 +1048,7 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
 	struct drm_device *drm = crtc->dev;
 	struct mcde *mcde = to_mcde(drm);
 	struct drm_pending_vblank_event *event;
+	int ret;
 
 	drm_crtc_vblank_off(crtc);
 
@@ -1029,6 +1064,12 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
 		spin_unlock_irq(&crtc->dev->event_lock);
 	}
 
+	ret = regulator_disable(mcde->epod);
+	if (ret)
+		dev_err(drm->dev, "can't disable EPOD regulator\n");
+	/* Make sure we are powered down (before we may power up again) */
+	usleep_range(50000, 70000);
+
 	dev_info(drm->dev, "MCDE display is disabled\n");
 }
 
diff --git a/drivers/gpu/drm/mcde/mcde_drm.h b/drivers/gpu/drm/mcde/mcde_drm.h
index 3e406d783465..9f197f4e9ced 100644
--- a/drivers/gpu/drm/mcde/mcde_drm.h
+++ b/drivers/gpu/drm/mcde/mcde_drm.h
@@ -9,6 +9,45 @@
 #ifndef _MCDE_DRM_H_
 #define _MCDE_DRM_H_
 
+/* Shared basic registers */
+#define MCDE_CR 0x00000000
+#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_SHIFT 0
+#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_MASK 0x0000003F
+#define MCDE_CR_IFIFOCTRLEN BIT(15)
+#define MCDE_CR_UFRECOVERY_MODE_V422 BIT(16)
+#define MCDE_CR_WRAP_MODE_V422_SHIFT BIT(17)
+#define MCDE_CR_AUTOCLKG_EN BIT(30)
+#define MCDE_CR_MCDEEN BIT(31)
+
+#define MCDE_CONF0 0x00000004
+#define MCDE_CONF0_SYNCMUX0 BIT(0)
+#define MCDE_CONF0_SYNCMUX1 BIT(1)
+#define MCDE_CONF0_SYNCMUX2 BIT(2)
+#define MCDE_CONF0_SYNCMUX3 BIT(3)
+#define MCDE_CONF0_SYNCMUX4 BIT(4)
+#define MCDE_CONF0_SYNCMUX5 BIT(5)
+#define MCDE_CONF0_SYNCMUX6 BIT(6)
+#define MCDE_CONF0_SYNCMUX7 BIT(7)
+#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT 12
+#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_MASK 0x00007000
+#define MCDE_CONF0_OUTMUX0_SHIFT 16
+#define MCDE_CONF0_OUTMUX0_MASK 0x00070000
+#define MCDE_CONF0_OUTMUX1_SHIFT 19
+#define MCDE_CONF0_OUTMUX1_MASK 0x00380000
+#define MCDE_CONF0_OUTMUX2_SHIFT 22
+#define MCDE_CONF0_OUTMUX2_MASK 0x01C00000
+#define MCDE_CONF0_OUTMUX3_SHIFT 25
+#define MCDE_CONF0_OUTMUX3_MASK 0x0E000000
+#define MCDE_CONF0_OUTMUX4_SHIFT 28
+#define MCDE_CONF0_OUTMUX4_MASK 0x70000000
+
+#define MCDE_SSP 0x00000008
+#define MCDE_AIS 0x00000100
+#define MCDE_IMSCERR 0x00000110
+#define MCDE_RISERR 0x00000120
+#define MCDE_MISERR 0x00000130
+#define MCDE_SISERR 0x00000140
+
 enum mcde_flow_mode {
 	/* One-shot mode: flow stops after one frame */
 	MCDE_COMMAND_ONESHOT_FLOW,
diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c
index 1671af101cf2..5be558d33553 100644
--- a/drivers/gpu/drm/mcde/mcde_drv.c
+++ b/drivers/gpu/drm/mcde/mcde_drv.c
@@ -63,6 +63,7 @@
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
+#include <linux/delay.h>
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
@@ -82,44 +83,6 @@
 
 #define DRIVER_DESC	"DRM module for MCDE"
 
-#define MCDE_CR 0x00000000
-#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_SHIFT 0
-#define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_MASK 0x0000003F
-#define MCDE_CR_IFIFOCTRLEN BIT(15)
-#define MCDE_CR_UFRECOVERY_MODE_V422 BIT(16)
-#define MCDE_CR_WRAP_MODE_V422_SHIFT BIT(17)
-#define MCDE_CR_AUTOCLKG_EN BIT(30)
-#define MCDE_CR_MCDEEN BIT(31)
-
-#define MCDE_CONF0 0x00000004
-#define MCDE_CONF0_SYNCMUX0 BIT(0)
-#define MCDE_CONF0_SYNCMUX1 BIT(1)
-#define MCDE_CONF0_SYNCMUX2 BIT(2)
-#define MCDE_CONF0_SYNCMUX3 BIT(3)
-#define MCDE_CONF0_SYNCMUX4 BIT(4)
-#define MCDE_CONF0_SYNCMUX5 BIT(5)
-#define MCDE_CONF0_SYNCMUX6 BIT(6)
-#define MCDE_CONF0_SYNCMUX7 BIT(7)
-#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT 12
-#define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_MASK 0x00007000
-#define MCDE_CONF0_OUTMUX0_SHIFT 16
-#define MCDE_CONF0_OUTMUX0_MASK 0x00070000
-#define MCDE_CONF0_OUTMUX1_SHIFT 19
-#define MCDE_CONF0_OUTMUX1_MASK 0x00380000
-#define MCDE_CONF0_OUTMUX2_SHIFT 22
-#define MCDE_CONF0_OUTMUX2_MASK 0x01C00000
-#define MCDE_CONF0_OUTMUX3_SHIFT 25
-#define MCDE_CONF0_OUTMUX3_MASK 0x0E000000
-#define MCDE_CONF0_OUTMUX4_SHIFT 28
-#define MCDE_CONF0_OUTMUX4_MASK 0x70000000
-
-#define MCDE_SSP 0x00000008
-#define MCDE_AIS 0x00000100
-#define MCDE_IMSCERR 0x00000110
-#define MCDE_RISERR 0x00000120
-#define MCDE_MISERR 0x00000130
-#define MCDE_SISERR 0x00000140
-
 #define MCDE_PID 0x000001FC
 #define MCDE_PID_METALFIX_VERSION_SHIFT 0
 #define MCDE_PID_METALFIX_VERSION_MASK 0x000000FF
@@ -303,7 +266,6 @@ static int mcde_probe(struct platform_device *pdev)
 	struct component_match *match = NULL;
 	struct resource *res;
 	u32 pid;
-	u32 val;
 	int irq;
 	int ret;
 	int i;
@@ -412,27 +374,7 @@ static int mcde_probe(struct platform_device *pdev)
 		goto clk_disable;
 	}
 
-	/* Set up the main control, watermark level at 7 */
-	val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT;
-	/* 24 bits DPI: connect LSB Ch B to D[0:7] */
-	val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT;
-	/* TV out: connect LSB Ch B to D[8:15] */
-	val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT;
-	/* Don't care about this muxing */
-	val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT;
-	/* 24 bits DPI: connect MID Ch B to D[24:31] */
-	val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT;
-	/* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */
-	val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT;
-	/* Syncmux bits zero: DPI channel A and B on output pins A and B resp */
-	writel(val, mcde->regs + MCDE_CONF0);
-
-	/* Enable automatic clock gating */
-	val = readl(mcde->regs + MCDE_CR);
-	val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN;
-	writel(val, mcde->regs + MCDE_CR);
-
-	/* Clear any pending interrupts */
+	/* Disable and clear any pending interrupts */
 	mcde_display_disable_irqs(mcde);
 	writel(0, mcde->regs + MCDE_IMSCERR);
 	writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR);
@@ -462,12 +404,28 @@ static int mcde_probe(struct platform_device *pdev)
 		ret = PTR_ERR(match);
 		goto clk_disable;
 	}
+
+	/*
+	 * Perform an invasive reset of the MCDE and all blocks by
+	 * cutting the power to the subsystem, then bring it back up
+	 * later when we enable the display as a result of
+	 * component_master_add_with_match().
+	 */
+	ret = regulator_disable(mcde->epod);
+	if (ret) {
+		dev_err(dev, "can't disable EPOD regulator\n");
+		return ret;
+	}
+	/* Wait 50 ms so we are sure we cut the power */
+	usleep_range(50000, 70000);
+
 	ret = component_master_add_with_match(&pdev->dev, &mcde_drm_comp_ops,
 					      match);
 	if (ret) {
 		dev_err(dev, "failed to add component master\n");
 		goto clk_disable;
 	}
+
 	return 0;
 
 clk_disable:
diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index e335041e6e90..0d7ebb59b727 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -43,6 +43,7 @@ struct mcde_dsi {
 	struct drm_bridge *bridge_out;
 	struct mipi_dsi_host dsi_host;
 	struct mipi_dsi_device *mdsi;
+	const struct drm_display_mode *mode;
 	struct clk *hs_clk;
 	struct clk *lp_clk;
 	unsigned long hs_freq;
@@ -903,7 +904,25 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
 		dev_info(d->dev, "DSI HS clock rate %lu Hz\n",
 			 d->hs_freq);
 
+	/* Assert RESET through the PRCMU, active low */
+	/* FIXME: which DSI block? */
+	regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
+			   PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
+
+	usleep_range(100, 200);
+
+	/* De-assert RESET again */
+	regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
+			   PRCM_DSI_SW_RESET_DSI0_SW_RESETN,
+			   PRCM_DSI_SW_RESET_DSI0_SW_RESETN);
+
+	/* Start up the hardware */
+	mcde_dsi_start(d);
+
 	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		/* Set up the video mode from the DRM mode */
+		mcde_dsi_setup_video_mode(d, d->mode);
+
 		/* Put IF1 into video mode */
 		val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
 		val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE;
@@ -944,13 +963,12 @@ static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
 		return;
 	}
 
+	d->mode = mode;
+
 	dev_info(d->dev, "set DSI master to %dx%d %u Hz %s mode\n",
 		 mode->hdisplay, mode->vdisplay, mode->clock * 1000,
 		 (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD"
 		);
-
-	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO)
-		mcde_dsi_setup_video_mode(d, mode);
 }
 
 static void mcde_dsi_wait_for_command_mode_stop(struct mcde_dsi *d)
@@ -999,9 +1017,6 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
 	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
 	u32 val;
 
-	/* Disable all error interrupts */
-	writel(0, d->regs + DSI_VID_MODE_STS_CTL);
-
 	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
 		/* Stop video mode */
 		val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
@@ -1012,8 +1027,20 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
 		/* Stop command mode */
 		mcde_dsi_wait_for_command_mode_stop(d);
 	}
+}
 
-	/* Stop clocks */
+static void mcde_dsi_bridge_post_disable(struct drm_bridge *bridge)
+{
+	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
+
+	/*
+	 * Stop clocks and terminate any DSI traffic here so the panel can
+	 * send commands to shut down the display using DSI direct write until
+	 * this point.
+	 */
+
+	/* Disable all error interrupts */
+	writel(0, d->regs + DSI_VID_MODE_STS_CTL);
 	clk_disable_unprepare(d->hs_clk);
 	clk_disable_unprepare(d->lp_clk);
 }
@@ -1046,6 +1073,7 @@ static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = {
 	.disable = mcde_dsi_bridge_disable,
 	.enable = mcde_dsi_bridge_enable,
 	.pre_enable = mcde_dsi_bridge_pre_enable,
+	.post_disable = mcde_dsi_bridge_post_disable,
 };
 
 static int mcde_dsi_bind(struct device *dev, struct device *master,
@@ -1081,21 +1109,6 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
 		return PTR_ERR(d->lp_clk);
 	}
 
-	/* Assert RESET through the PRCMU, active low */
-	/* FIXME: which DSI block? */
-	regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
-			   PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
-
-	usleep_range(100, 200);
-
-	/* De-assert RESET again */
-	regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
-			   PRCM_DSI_SW_RESET_DSI0_SW_RESETN,
-			   PRCM_DSI_SW_RESET_DSI0_SW_RESETN);
-
-	/* Start up the hardware */
-	mcde_dsi_start(d);
-
 	/* Look for a panel as a child to this node */
 	for_each_available_child_of_node(dev->of_node, child) {
 		panel = of_drm_find_panel(child);
-- 
2.26.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4 v2] drm/mcde: Enable the DSI link with display
  2020-08-08 22:31 [PATCH 1/4 v2] drm/mcde: Improve pixel fetcher FIFO depth setting Linus Walleij
  2020-08-08 22:31 ` [PATCH 2/4 v2] drm/mcde: Support using DSI in LP mode Linus Walleij
  2020-08-08 22:31 ` [PATCH 3/4 v2] drm/mcde: Fix display pipeline restart Linus Walleij
@ 2020-08-08 22:31 ` Linus Walleij
  2020-08-10 13:03   ` Daniel Vetter
  2020-08-10 12:54 ` [PATCH 1/4 v2] drm/mcde: Improve pixel fetcher FIFO depth setting Daniel Vetter
  3 siblings, 1 reply; 6+ messages in thread
From: Linus Walleij @ 2020-08-08 22:31 UTC (permalink / raw)
  To: dri-devel, Maarten Lankhorst, Maxime Ripard, Sean Paul
  Cc: Linus Walleij, newbytee, Stephan Gerhold, linux-arm-kernel

The MCDE DSI link hardware which is modeled like a bridge
in DRM, connected further to the panel bridge, creating
a pipeline.

We have been using the .pre_enable(), .enable(),
.disable() and .post_disable() callbacks from the bridge
to set this up in a chained manner: first the display
controller goes online and then in successive order
each bridge in the pipeline. Inside DRM it works
like this:

drm_atomic_helper_commit_tail()
  drm_atomic_helper_commit_modeset_enables()
    struct drm_crtc_helper_funcs .atomic_enable()
      struct drm_simple_display_pipe_funcs .enable()
        MCDE display enable call
    drm_atomic_bridge_chain_enable()
      struct drm_bridge_funcs .pre_enable()
        mcde_dsi_bridge_pre_enable()
        panel_bridge_pre_enable()
          struct drm_panel_funcs .prepare()
      struct drm_bridge_funcs .enable()
        mcde_dsi_bridge_enable()
        panel_bridge_enable()
          struct drm_panel_funcs .enable()

A similar sequence is executed for disabling.

Unfortunately this is not what the hardware needs: at
a certain stage in the enablement of the display
controller the DSI link needs to come up to support
video mode, else something (like a FIFO flow) locks
up the hardware and we never get picture.

Fix this by simply leaving the pre|enable and
post|disable callbacks unused, and establish two
cross-calls from the display controller to bring up
the DSI link at the right place in the display
bring-up sequence and vice versa in the shutdown
sequence.

For command mode displays, it works just fine to
also enable the display flow early. The only time
we hold it back right now is in one-shot mode,
on-demand display updates.

When combined with the previous patch and some patches
for the S6E63M0 display controller to support DSI
mode, this gives working display on the Samsung
GT-I8190 (Golden) phone. It has also been tested working
on the Samsung GT-S7710 (Skomer) phone.

Cc: newbytee@protonmail.com
Cc: Stephan Gerhold <stephan@gerhold.net>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/gpu/drm/mcde/mcde_display.c | 36 +++++++++++++++++------
 drivers/gpu/drm/mcde/mcde_drm.h     |  2 ++
 drivers/gpu/drm/mcde/mcde_dsi.c     | 44 +++++++++++------------------
 3 files changed, 47 insertions(+), 35 deletions(-)

diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
index d608cc894e01..c271e5bf042e 100644
--- a/drivers/gpu/drm/mcde/mcde_display.c
+++ b/drivers/gpu/drm/mcde/mcde_display.c
@@ -999,6 +999,16 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
 	mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DSI_FORMATTER_0,
 			    fifo_wtrmrk);
 
+	/*
+	 * This brings up the DSI bridge which is tightly connected
+	 * to the MCDE DSI formatter.
+	 *
+	 * FIXME: if we want to use another formatter, such as DPI,
+	 * we need to be more elaborate here and select the appropriate
+	 * bridge.
+	 */
+	mcde_dsi_enable(mcde->bridge);
+
 	/* Configure the DSI formatter 0 for the DSI panel output */
 	mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0,
 				     formatter_frame, pkt_size);
@@ -1025,16 +1035,20 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
 
 	drm_crtc_vblank_on(crtc);
 
-	if (mcde_flow_is_video(mcde)) {
-		/*
-		 * Keep FIFO permanently enabled in video mode,
-		 * otherwise MCDE will stop feeding data to the panel.
-		 */
+	/*
+	 * If we're using oneshot mode we don't start the flow
+	 * until each time the display is given an update, and
+	 * then we disable it immediately after. For all other
+	 * modes (command or video) we start the FIFO flow
+	 * right here. This is necessary for the hardware to
+	 * behave right.
+	 */
+	if (mcde->flow_mode != MCDE_COMMAND_ONESHOT_FLOW) {
 		mcde_enable_fifo(mcde, MCDE_FIFO_A);
 		dev_dbg(mcde->dev, "started MCDE video FIFO flow\n");
 	}
 
-	/* Enable automatic clock gating */
+	/* Enable MCDE with automatic clock gating */
 	val = readl(mcde->regs + MCDE_CR);
 	val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN;
 	writel(val, mcde->regs + MCDE_CR);
@@ -1055,6 +1069,9 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
 	/* Disable FIFO A flow */
 	mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
 
+	/* This disables the DSI bridge */
+	mcde_dsi_disable(mcde->bridge);
+
 	event = crtc->state->event;
 	if (event) {
 		crtc->state->event = NULL;
@@ -1164,8 +1181,11 @@ static void mcde_display_update(struct drm_simple_display_pipe *pipe,
 	if (fb) {
 		mcde_set_extsrc(mcde, drm_fb_cma_get_gem_addr(fb, pstate, 0));
 		dev_info_once(mcde->dev, "first update of display contents\n");
-		/* The flow is already active in video mode */
-		if (!mcde_flow_is_video(mcde) && mcde->flow_active == 0)
+		/*
+		 * Usually the flow is already active, unless we are in
+		 * oneshot mode, then we need to kick the flow right here.
+		 */
+		if (mcde->flow_active == 0)
 			mcde_start_flow(mcde);
 	} else {
 		/*
diff --git a/drivers/gpu/drm/mcde/mcde_drm.h b/drivers/gpu/drm/mcde/mcde_drm.h
index 9f197f4e9ced..8253e2f9993e 100644
--- a/drivers/gpu/drm/mcde/mcde_drm.h
+++ b/drivers/gpu/drm/mcde/mcde_drm.h
@@ -97,6 +97,8 @@ static inline bool mcde_flow_is_video(struct mcde *mcde)
 
 bool mcde_dsi_irq(struct mipi_dsi_device *mdsi);
 void mcde_dsi_te_request(struct mipi_dsi_device *mdsi);
+void mcde_dsi_enable(struct drm_bridge *bridge);
+void mcde_dsi_disable(struct drm_bridge *bridge);
 extern struct platform_driver mcde_dsi_driver;
 
 void mcde_display_irq(struct mcde *mcde);
diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index 0d7ebb59b727..a46a45c5cd52 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -844,23 +844,11 @@ static void mcde_dsi_start(struct mcde_dsi *d)
 	dev_info(d->dev, "DSI link enabled\n");
 }
 
-
-static void mcde_dsi_bridge_enable(struct drm_bridge *bridge)
-{
-	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
-	u32 val;
-
-	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
-		/* Enable video mode */
-		val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
-		val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
-		writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
-	}
-
-	dev_info(d->dev, "enable DSI master\n");
-};
-
-static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
+/*
+ * Notice that this is called from inside the display controller
+ * and not from the bridge callbacks.
+ */
+void mcde_dsi_enable(struct drm_bridge *bridge)
 {
 	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
 	unsigned long hs_freq, lp_freq;
@@ -938,6 +926,11 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
 		val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC;
 		val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA;
 		writel(val, d->regs + DSI_VID_MODE_STS_CTL);
+
+		/* Enable video mode */
+		val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
+		val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
+		writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
 	} else {
 		/* Command mode, clear IF1 ID */
 		val = readl(d->regs + DSI_CMD_MODE_CTL);
@@ -950,6 +943,8 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
 		val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK;
 		writel(val, d->regs + DSI_CMD_MODE_CTL);
 	}
+
+	dev_info(d->dev, "enabled MCDE DSI master\n");
 }
 
 static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
@@ -1012,7 +1007,11 @@ static void mcde_dsi_wait_for_video_mode_stop(struct mcde_dsi *d)
 	}
 }
 
-static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
+/*
+ * Notice that this is called from inside the display controller
+ * and not from the bridge callbacks.
+ */
+void mcde_dsi_disable(struct drm_bridge *bridge)
 {
 	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
 	u32 val;
@@ -1027,11 +1026,6 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
 		/* Stop command mode */
 		mcde_dsi_wait_for_command_mode_stop(d);
 	}
-}
-
-static void mcde_dsi_bridge_post_disable(struct drm_bridge *bridge)
-{
-	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
 
 	/*
 	 * Stop clocks and terminate any DSI traffic here so the panel can
@@ -1070,10 +1064,6 @@ static int mcde_dsi_bridge_attach(struct drm_bridge *bridge,
 static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = {
 	.attach = mcde_dsi_bridge_attach,
 	.mode_set = mcde_dsi_bridge_mode_set,
-	.disable = mcde_dsi_bridge_disable,
-	.enable = mcde_dsi_bridge_enable,
-	.pre_enable = mcde_dsi_bridge_pre_enable,
-	.post_disable = mcde_dsi_bridge_post_disable,
 };
 
 static int mcde_dsi_bind(struct device *dev, struct device *master,
-- 
2.26.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/4 v2] drm/mcde: Improve pixel fetcher FIFO depth setting
  2020-08-08 22:31 [PATCH 1/4 v2] drm/mcde: Improve pixel fetcher FIFO depth setting Linus Walleij
                   ` (2 preceding siblings ...)
  2020-08-08 22:31 ` [PATCH 4/4 v2] drm/mcde: Enable the DSI link with display Linus Walleij
@ 2020-08-10 12:54 ` Daniel Vetter
  3 siblings, 0 replies; 6+ messages in thread
From: Daniel Vetter @ 2020-08-10 12:54 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Stephan Gerhold, newbytee, Maarten Lankhorst, Maxime Ripard,
	dri-devel, Sean Paul, linux-arm-kernel

On Sun, Aug 09, 2020 at 12:31:19AM +0200, Linus Walleij wrote:
> The pixel fetcher FIFO depth was just hardcoded to 48
> which works fine as long as the framebuffer is 32BPP
> and the DSI output is RGB888.
> 
> We will need more elaborate handling for some buffer
> formats and displays, so start to improve this function
> by setting reasonable defaults for 32, 24 and 16 BPP
> framebuffers.
> 
> Cc: newbytee@protonmail.com
> Cc: Stephan Gerhold <stephan@gerhold.net>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
>  drivers/gpu/drm/mcde/mcde_display.c | 34 +++++++++++++++++++++++++----
>  1 file changed, 30 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
> index cac660ac8803..cbc7c0c4955a 100644
> --- a/drivers/gpu/drm/mcde/mcde_display.c
> +++ b/drivers/gpu/drm/mcde/mcde_display.c
> @@ -333,7 +333,7 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
>  				   enum mcde_extsrc src,
>  				   enum mcde_channel ch,
>  				   const struct drm_display_mode *mode,
> -				   u32 format)
> +				   u32 format, int cpp)

Note that format->cpp is a bit outdated, since it doesn't work for planar
stuff. But I guess not a problem for you (for now at least). Blocky
formats like yuv don't set format->cpp, or at least not in the way you'd
expect.

Anyway now idea on the hw, but patch looks reasonable.

Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>

>  {
>  	u32 val;
>  	u32 conf1;
> @@ -342,6 +342,7 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
>  	u32 ljinc;
>  	u32 cr;
>  	u32 comp;
> +	u32 pixel_fetcher_watermark;
>  
>  	switch (ovl) {
>  	case MCDE_OVERLAY_0:
> @@ -426,8 +427,33 @@ static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl,
>  			format);
>  		break;
>  	}
> -	/* The default watermark level for overlay 0 is 48 */
> -	val |= 48 << MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT;
> +
> +	/*
> +	 * Pixel fetch watermark level is max 0x1FFF pixels.
> +	 * Two basic rules should be followed:
> +	 * 1. The value should be at least 256 bits.
> +	 * 2. The sum of all active overlays pixelfetch watermark level
> +	 *    multiplied with bits per pixel, should be lower than the
> +	 *    size of input_fifo_size in bits.
> +	 * 3. The value should be a multiple of a line (256 bits).
> +	 */
> +	switch (cpp) {
> +	case 2:
> +		pixel_fetcher_watermark = 128;
> +		break;
> +	case 3:
> +		pixel_fetcher_watermark = 96;
> +		break;
> +	case 4:
> +		pixel_fetcher_watermark = 48;
> +		break;
> +	default:
> +		pixel_fetcher_watermark = 48;
> +		break;
> +	}
> +	dev_dbg(mcde->dev, "pixel fetcher watermark level %d pixels\n",
> +		pixel_fetcher_watermark);
> +	val |= pixel_fetcher_watermark << MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT;
>  	writel(val, mcde->regs + conf2);
>  
>  	/* Number of bytes to fetch per line */
> @@ -932,7 +958,7 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
>  	 * channel 0
>  	 */
>  	mcde_configure_overlay(mcde, MCDE_OVERLAY_0, MCDE_EXTSRC_0,
> -			       MCDE_CHANNEL_0, mode, format);
> +			       MCDE_CHANNEL_0, mode, format, cpp);
>  
>  	/*
>  	 * Configure pixel-per-line and line-per-frame for channel 0 and then
> -- 
> 2.26.2
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 4/4 v2] drm/mcde: Enable the DSI link with display
  2020-08-08 22:31 ` [PATCH 4/4 v2] drm/mcde: Enable the DSI link with display Linus Walleij
@ 2020-08-10 13:03   ` Daniel Vetter
  0 siblings, 0 replies; 6+ messages in thread
From: Daniel Vetter @ 2020-08-10 13:03 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Stephan Gerhold, newbytee, Maarten Lankhorst, Maxime Ripard,
	dri-devel, Sean Paul, linux-arm-kernel

On Sun, Aug 09, 2020 at 12:31:22AM +0200, Linus Walleij wrote:
> The MCDE DSI link hardware which is modeled like a bridge
> in DRM, connected further to the panel bridge, creating
> a pipeline.
> 
> We have been using the .pre_enable(), .enable(),
> .disable() and .post_disable() callbacks from the bridge
> to set this up in a chained manner: first the display
> controller goes online and then in successive order
> each bridge in the pipeline. Inside DRM it works
> like this:
> 
> drm_atomic_helper_commit_tail()
>   drm_atomic_helper_commit_modeset_enables()
>     struct drm_crtc_helper_funcs .atomic_enable()
>       struct drm_simple_display_pipe_funcs .enable()
>         MCDE display enable call
>     drm_atomic_bridge_chain_enable()
>       struct drm_bridge_funcs .pre_enable()
>         mcde_dsi_bridge_pre_enable()
>         panel_bridge_pre_enable()
>           struct drm_panel_funcs .prepare()
>       struct drm_bridge_funcs .enable()
>         mcde_dsi_bridge_enable()
>         panel_bridge_enable()
>           struct drm_panel_funcs .enable()
> 
> A similar sequence is executed for disabling.
> 
> Unfortunately this is not what the hardware needs: at
> a certain stage in the enablement of the display
> controller the DSI link needs to come up to support
> video mode, else something (like a FIFO flow) locks
> up the hardware and we never get picture.
> 
> Fix this by simply leaving the pre|enable and
> post|disable callbacks unused, and establish two
> cross-calls from the display controller to bring up
> the DSI link at the right place in the display
> bring-up sequence and vice versa in the shutdown
> sequence.
> 
> For command mode displays, it works just fine to
> also enable the display flow early. The only time
> we hold it back right now is in one-shot mode,
> on-demand display updates.
> 
> When combined with the previous patch and some patches
> for the S6E63M0 display controller to support DSI
> mode, this gives working display on the Samsung
> GT-I8190 (Golden) phone. It has also been tested working
> on the Samsung GT-S7710 (Skomer) phone.
> 
> Cc: newbytee@protonmail.com
> Cc: Stephan Gerhold <stephan@gerhold.net>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

So the idea kinda was that if you need this, you'd write your own modeset
code. That's why atomic helpers are fairly modular. Then you can place the
various drm_bridge_enable/disable calls exactly where you need them to
make your upstream blocks happy.

You could even do that with simple pipe helpers by overwriting the
atomic_commit_tail function with your special enable/disable sequence.
Since you have simple hw this would look something like:

static void mcde_helper_commit_tail(struct drm_atomic_state *old_state)
{
	struct drm_device *dev = old_state->dev;
	bool fence_cookie = dma_fence_begin_signalling();

	mcde_display_disable(...)

	drm_atomic_helper_commit_planes(dev, old_state, 0);

	mcde_display_enable(....)

	drm_atomic_helper_fake_vblank(old_state);

	drm_atomic_helper_commit_hw_done(old_state);

	dma_fence_end_signalling(fence_cookie);

	drm_atomic_helper_wait_for_vblanks(dev, old_state);

	drm_atomic_helper_cleanup_planes(dev, old_state);
}

And mcde_display_enable/disable would then call into bridges and anything
else you need to be called.

Anyway just an idea, for patches 2-4:

Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> ---
>  drivers/gpu/drm/mcde/mcde_display.c | 36 +++++++++++++++++------
>  drivers/gpu/drm/mcde/mcde_drm.h     |  2 ++
>  drivers/gpu/drm/mcde/mcde_dsi.c     | 44 +++++++++++------------------
>  3 files changed, 47 insertions(+), 35 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
> index d608cc894e01..c271e5bf042e 100644
> --- a/drivers/gpu/drm/mcde/mcde_display.c
> +++ b/drivers/gpu/drm/mcde/mcde_display.c
> @@ -999,6 +999,16 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
>  	mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DSI_FORMATTER_0,
>  			    fifo_wtrmrk);
>  
> +	/*
> +	 * This brings up the DSI bridge which is tightly connected
> +	 * to the MCDE DSI formatter.
> +	 *
> +	 * FIXME: if we want to use another formatter, such as DPI,
> +	 * we need to be more elaborate here and select the appropriate
> +	 * bridge.
> +	 */
> +	mcde_dsi_enable(mcde->bridge);
> +
>  	/* Configure the DSI formatter 0 for the DSI panel output */
>  	mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0,
>  				     formatter_frame, pkt_size);
> @@ -1025,16 +1035,20 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
>  
>  	drm_crtc_vblank_on(crtc);
>  
> -	if (mcde_flow_is_video(mcde)) {
> -		/*
> -		 * Keep FIFO permanently enabled in video mode,
> -		 * otherwise MCDE will stop feeding data to the panel.
> -		 */
> +	/*
> +	 * If we're using oneshot mode we don't start the flow
> +	 * until each time the display is given an update, and
> +	 * then we disable it immediately after. For all other
> +	 * modes (command or video) we start the FIFO flow
> +	 * right here. This is necessary for the hardware to
> +	 * behave right.
> +	 */
> +	if (mcde->flow_mode != MCDE_COMMAND_ONESHOT_FLOW) {
>  		mcde_enable_fifo(mcde, MCDE_FIFO_A);
>  		dev_dbg(mcde->dev, "started MCDE video FIFO flow\n");
>  	}
>  
> -	/* Enable automatic clock gating */
> +	/* Enable MCDE with automatic clock gating */
>  	val = readl(mcde->regs + MCDE_CR);
>  	val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN;
>  	writel(val, mcde->regs + MCDE_CR);
> @@ -1055,6 +1069,9 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
>  	/* Disable FIFO A flow */
>  	mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
>  
> +	/* This disables the DSI bridge */
> +	mcde_dsi_disable(mcde->bridge);
> +
>  	event = crtc->state->event;
>  	if (event) {
>  		crtc->state->event = NULL;
> @@ -1164,8 +1181,11 @@ static void mcde_display_update(struct drm_simple_display_pipe *pipe,
>  	if (fb) {
>  		mcde_set_extsrc(mcde, drm_fb_cma_get_gem_addr(fb, pstate, 0));
>  		dev_info_once(mcde->dev, "first update of display contents\n");
> -		/* The flow is already active in video mode */
> -		if (!mcde_flow_is_video(mcde) && mcde->flow_active == 0)
> +		/*
> +		 * Usually the flow is already active, unless we are in
> +		 * oneshot mode, then we need to kick the flow right here.
> +		 */
> +		if (mcde->flow_active == 0)
>  			mcde_start_flow(mcde);
>  	} else {
>  		/*
> diff --git a/drivers/gpu/drm/mcde/mcde_drm.h b/drivers/gpu/drm/mcde/mcde_drm.h
> index 9f197f4e9ced..8253e2f9993e 100644
> --- a/drivers/gpu/drm/mcde/mcde_drm.h
> +++ b/drivers/gpu/drm/mcde/mcde_drm.h
> @@ -97,6 +97,8 @@ static inline bool mcde_flow_is_video(struct mcde *mcde)
>  
>  bool mcde_dsi_irq(struct mipi_dsi_device *mdsi);
>  void mcde_dsi_te_request(struct mipi_dsi_device *mdsi);
> +void mcde_dsi_enable(struct drm_bridge *bridge);
> +void mcde_dsi_disable(struct drm_bridge *bridge);
>  extern struct platform_driver mcde_dsi_driver;
>  
>  void mcde_display_irq(struct mcde *mcde);
> diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
> index 0d7ebb59b727..a46a45c5cd52 100644
> --- a/drivers/gpu/drm/mcde/mcde_dsi.c
> +++ b/drivers/gpu/drm/mcde/mcde_dsi.c
> @@ -844,23 +844,11 @@ static void mcde_dsi_start(struct mcde_dsi *d)
>  	dev_info(d->dev, "DSI link enabled\n");
>  }
>  
> -
> -static void mcde_dsi_bridge_enable(struct drm_bridge *bridge)
> -{
> -	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
> -	u32 val;
> -
> -	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> -		/* Enable video mode */
> -		val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
> -		val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
> -		writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
> -	}
> -
> -	dev_info(d->dev, "enable DSI master\n");
> -};
> -
> -static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
> +/*
> + * Notice that this is called from inside the display controller
> + * and not from the bridge callbacks.
> + */
> +void mcde_dsi_enable(struct drm_bridge *bridge)
>  {
>  	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
>  	unsigned long hs_freq, lp_freq;
> @@ -938,6 +926,11 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
>  		val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC;
>  		val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA;
>  		writel(val, d->regs + DSI_VID_MODE_STS_CTL);
> +
> +		/* Enable video mode */
> +		val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
> +		val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN;
> +		writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
>  	} else {
>  		/* Command mode, clear IF1 ID */
>  		val = readl(d->regs + DSI_CMD_MODE_CTL);
> @@ -950,6 +943,8 @@ static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
>  		val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK;
>  		writel(val, d->regs + DSI_CMD_MODE_CTL);
>  	}
> +
> +	dev_info(d->dev, "enabled MCDE DSI master\n");
>  }
>  
>  static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
> @@ -1012,7 +1007,11 @@ static void mcde_dsi_wait_for_video_mode_stop(struct mcde_dsi *d)
>  	}
>  }
>  
> -static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
> +/*
> + * Notice that this is called from inside the display controller
> + * and not from the bridge callbacks.
> + */
> +void mcde_dsi_disable(struct drm_bridge *bridge)
>  {
>  	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
>  	u32 val;
> @@ -1027,11 +1026,6 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
>  		/* Stop command mode */
>  		mcde_dsi_wait_for_command_mode_stop(d);
>  	}
> -}
> -
> -static void mcde_dsi_bridge_post_disable(struct drm_bridge *bridge)
> -{
> -	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
>  
>  	/*
>  	 * Stop clocks and terminate any DSI traffic here so the panel can
> @@ -1070,10 +1064,6 @@ static int mcde_dsi_bridge_attach(struct drm_bridge *bridge,
>  static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = {
>  	.attach = mcde_dsi_bridge_attach,
>  	.mode_set = mcde_dsi_bridge_mode_set,
> -	.disable = mcde_dsi_bridge_disable,
> -	.enable = mcde_dsi_bridge_enable,
> -	.pre_enable = mcde_dsi_bridge_pre_enable,
> -	.post_disable = mcde_dsi_bridge_post_disable,
>  };
>  
>  static int mcde_dsi_bind(struct device *dev, struct device *master,
> -- 
> 2.26.2
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-08-10 13:05 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-08 22:31 [PATCH 1/4 v2] drm/mcde: Improve pixel fetcher FIFO depth setting Linus Walleij
2020-08-08 22:31 ` [PATCH 2/4 v2] drm/mcde: Support using DSI in LP mode Linus Walleij
2020-08-08 22:31 ` [PATCH 3/4 v2] drm/mcde: Fix display pipeline restart Linus Walleij
2020-08-08 22:31 ` [PATCH 4/4 v2] drm/mcde: Enable the DSI link with display Linus Walleij
2020-08-10 13:03   ` Daniel Vetter
2020-08-10 12:54 ` [PATCH 1/4 v2] drm/mcde: Improve pixel fetcher FIFO depth setting Daniel Vetter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).