linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/7] drm/mcde: DSI video mode fixes
@ 2019-11-06 16:58 Stephan Gerhold
  2019-11-06 16:58 ` [PATCH 1/7] drm/mcde: Provide vblank handling unconditionally Stephan Gerhold
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Stephan Gerhold @ 2019-11-06 16:58 UTC (permalink / raw)
  To: Linus Walleij
  Cc: David Airlie, Daniel Vetter, dri-devel, linux-kernel, Stephan Gerhold

This is a collection of fixes to make DSI video mode work better
using the MCDE driver. With these changes, MCDE appears to work
properly for the video mode panel I have been testing with.

Note: The patch that fixes the DSI link register setup for
video mode [1] is still necessary; but we still need to finish it
and actually make it initialize a panel correctly.

This series contains only patches for the other parts in MCDE.
I have tested it by disabling most of the register setup in the
DSI driver, which makes it re-use the properly working DSI register
set by the bootloader.

[1]: https://lists.freedesktop.org/archives/dri-devel/2019-October/238175.html

Stephan Gerhold (7):
  drm/mcde: Provide vblank handling unconditionally
  drm/mcde: Fix frame sync setup for video mode panels
  drm/mcde: dsi: Make video mode errors more verbose
  drm/mcde: dsi: Delay start of video stream generator
  drm/mcde: dsi: Fix duplicated DSI connector
  drm/mcde: dsi: Enable clocks in pre_enable() instead of mode_set()
  drm/mcde: Handle pending vblank while disabling display

 drivers/gpu/drm/mcde/mcde_display.c  |  57 +++++----
 drivers/gpu/drm/mcde/mcde_drm.h      |   1 +
 drivers/gpu/drm/mcde/mcde_drv.c      |  18 +--
 drivers/gpu/drm/mcde/mcde_dsi.c      | 167 +++++++++++++--------------
 drivers/gpu/drm/mcde/mcde_dsi_regs.h |  10 ++
 5 files changed, 126 insertions(+), 127 deletions(-)

-- 
2.23.0


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

* [PATCH 1/7] drm/mcde: Provide vblank handling unconditionally
  2019-11-06 16:58 [PATCH 0/7] drm/mcde: DSI video mode fixes Stephan Gerhold
@ 2019-11-06 16:58 ` Stephan Gerhold
  2019-11-06 16:58 ` [PATCH 2/7] drm/mcde: Fix frame sync setup for video mode panels Stephan Gerhold
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Stephan Gerhold @ 2019-11-06 16:58 UTC (permalink / raw)
  To: Linus Walleij
  Cc: David Airlie, Daniel Vetter, dri-devel, linux-kernel, Stephan Gerhold

At the moment, vblank handling is only enabled together with
TE synchronization. However, the vblank IRQ is also working with
on displays without TE synchronization (e.g. DSI video mode panels).
It seems like the vblank IRQ is actually generated by the
MCDE hardware for the channel.

Therefore, the vblank handling should be working correctly in
all the cases and we can enable it unconditionally.

Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
---
 drivers/gpu/drm/mcde/mcde_display.c | 15 +++++----------
 drivers/gpu/drm/mcde/mcde_drv.c     | 16 ++++------------
 2 files changed, 9 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
index 751454ae3cd1..65522481b367 100644
--- a/drivers/gpu/drm/mcde/mcde_display.c
+++ b/drivers/gpu/drm/mcde/mcde_display.c
@@ -934,10 +934,10 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
 		val = readl(mcde->regs + MCDE_CRC);
 		val |= MCDE_CRC_SYCEN0;
 		writel(val, mcde->regs + MCDE_CRC);
-
-		drm_crtc_vblank_on(crtc);
 	}
 
+	drm_crtc_vblank_on(crtc);
+
 	dev_info(drm->dev, "MCDE display is enabled\n");
 }
 
@@ -947,8 +947,7 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
 	struct drm_device *drm = crtc->dev;
 	struct mcde *mcde = drm->dev_private;
 
-	if (mcde->te_sync)
-		drm_crtc_vblank_off(crtc);
+	drm_crtc_vblank_off(crtc);
 
 	/* Disable FIFO A flow */
 	mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
@@ -1097,6 +1096,8 @@ static struct drm_simple_display_pipe_funcs mcde_display_funcs = {
 	.enable = mcde_display_enable,
 	.disable = mcde_display_disable,
 	.update = mcde_display_update,
+	.enable_vblank = mcde_display_enable_vblank,
+	.disable_vblank = mcde_display_disable_vblank,
 	.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
@@ -1123,12 +1124,6 @@ int mcde_display_init(struct drm_device *drm)
 		DRM_FORMAT_YUV422,
 	};
 
-	/* Provide vblank only when we have TE enabled */
-	if (mcde->te_sync) {
-		mcde_display_funcs.enable_vblank = mcde_display_enable_vblank;
-		mcde_display_funcs.disable_vblank = mcde_display_disable_vblank;
-	}
-
 	ret = drm_simple_display_pipe_init(drm, &mcde->pipe,
 					   &mcde_display_funcs,
 					   formats, ARRAY_SIZE(formats),
diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c
index 5649887d2b90..0ccd3b0308c2 100644
--- a/drivers/gpu/drm/mcde/mcde_drv.c
+++ b/drivers/gpu/drm/mcde/mcde_drv.c
@@ -179,18 +179,10 @@ static int mcde_modeset_init(struct drm_device *drm)
 	mode_config->min_height = 1;
 	mode_config->max_height = 1080;
 
-	/*
-	 * Currently we only support vblank handling on the DSI bridge, using
-	 * TE synchronization. If TE sync is not set up, it is still possible
-	 * to push out a single update on demand, but this is hard for DRM to
-	 * exploit.
-	 */
-	if (mcde->te_sync) {
-		ret = drm_vblank_init(drm, 1);
-		if (ret) {
-			dev_err(drm->dev, "failed to init vblank\n");
-			goto out_config;
-		}
+	ret = drm_vblank_init(drm, 1);
+	if (ret) {
+		dev_err(drm->dev, "failed to init vblank\n");
+		goto out_config;
 	}
 
 	ret = mcde_display_init(drm);
-- 
2.23.0


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

* [PATCH 2/7] drm/mcde: Fix frame sync setup for video mode panels
  2019-11-06 16:58 [PATCH 0/7] drm/mcde: DSI video mode fixes Stephan Gerhold
  2019-11-06 16:58 ` [PATCH 1/7] drm/mcde: Provide vblank handling unconditionally Stephan Gerhold
@ 2019-11-06 16:58 ` Stephan Gerhold
  2019-11-06 16:58 ` [PATCH 3/7] drm/mcde: dsi: Make video mode errors more verbose Stephan Gerhold
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Stephan Gerhold @ 2019-11-06 16:58 UTC (permalink / raw)
  To: Linus Walleij
  Cc: David Airlie, Daniel Vetter, dri-devel, linux-kernel, Stephan Gerhold

The MCDE driver differentiates only between "te_sync"
(for hardware TE0 sync) and software sync
(i.e. manually triggered updates) at the moment.

However, none of these options work correctly for video mode panels.
Therefore, we need to make some changes to make them work correctly:

  - Select hardware sync coming from the (DSI) formatter.
  - Keep the FIFO permanently enabled (otherwise MCDE will stop
    feeding data to the panel).
  - Skip manual software sync (this is not necessary in video mode).

Automatically detect if the connected panel is using video mode
and enable the necessary changes in that case.

Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
---
 drivers/gpu/drm/mcde/mcde_display.c | 32 ++++++++++++++++-------------
 drivers/gpu/drm/mcde/mcde_drm.h     |  1 +
 drivers/gpu/drm/mcde/mcde_drv.c     |  2 --
 drivers/gpu/drm/mcde/mcde_dsi.c     | 13 ++++++++++--
 4 files changed, 30 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
index 65522481b367..a3375a974caf 100644
--- a/drivers/gpu/drm/mcde/mcde_display.c
+++ b/drivers/gpu/drm/mcde/mcde_display.c
@@ -498,24 +498,20 @@ static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch,
 	}
 
 	/* Set up channel 0 sync (based on chnl_update_registers()) */
-	if (mcde->te_sync) {
-		/*
-		 * Turn on hardware TE0 synchronization
-		 */
+	if (mcde->video_mode || mcde->te_sync)
 		val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
 			<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
-		val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0
-			<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
-	} else {
-		/*
-		 * Set up sync source to software, out sync formatter
-		 * Code mostly from mcde_hw.c chnl_update_registers()
-		 */
+	else
 		val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SOFTWARE
 			<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
+
+	if (mcde->te_sync)
+		val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0
+			<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
+	else
 		val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER
 			<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
-	}
+
 	writel(val, mcde->regs + sync);
 
 	/* Set up pixels per line and lines per frame */
@@ -938,6 +934,13 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
 
 	drm_crtc_vblank_on(crtc);
 
+	if (mcde->video_mode)
+		/*
+		 * Keep FIFO permanently enabled in video mode,
+		 * otherwise MCDE will stop feeding data to the panel.
+		 */
+		mcde_enable_fifo(mcde, MCDE_FIFO_A);
+
 	dev_info(drm->dev, "MCDE display is enabled\n");
 }
 
@@ -1047,8 +1050,9 @@ 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));
-		/* Send a single frame using software sync */
-		mcde_display_send_one_frame(mcde);
+		if (!mcde->video_mode)
+			/* Send a single frame using software sync */
+			mcde_display_send_one_frame(mcde);
 		dev_info_once(mcde->dev, "sent first display update\n");
 	} else {
 		/*
diff --git a/drivers/gpu/drm/mcde/mcde_drm.h b/drivers/gpu/drm/mcde/mcde_drm.h
index dab4db021231..80edd6628979 100644
--- a/drivers/gpu/drm/mcde/mcde_drm.h
+++ b/drivers/gpu/drm/mcde/mcde_drm.h
@@ -19,6 +19,7 @@ struct mcde {
 	struct mipi_dsi_device *mdsi;
 	s16 stride;
 	bool te_sync;
+	bool video_mode;
 	bool oneshot_mode;
 	unsigned int flow_active;
 	spinlock_t flow_lock; /* Locks the channel flow control */
diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c
index 0ccd3b0308c2..9008ddcfc528 100644
--- a/drivers/gpu/drm/mcde/mcde_drv.c
+++ b/drivers/gpu/drm/mcde/mcde_drv.c
@@ -331,8 +331,6 @@ static int mcde_probe(struct platform_device *pdev)
 	drm->dev_private = mcde;
 	platform_set_drvdata(pdev, drm);
 
-	/* Enable use of the TE signal and interrupt */
-	mcde->te_sync = true;
 	/* Enable continuous updates: this is what Linux' framebuffer expects */
 	mcde->oneshot_mode = false;
 	drm->dev_private = mcde;
diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index d6214d3c8b33..ffd2e0b64628 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -130,6 +130,15 @@ bool mcde_dsi_irq(struct mipi_dsi_device *mdsi)
 	return te_received;
 }
 
+static void mcde_dsi_attach_to_mcde(struct mcde_dsi *d)
+{
+	d->mcde->mdsi = d->mdsi;
+
+	d->mcde->video_mode = !!(d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO);
+	/* Enable use of the TE signal for all command mode panels */
+	d->mcde->te_sync = !d->mcde->video_mode;
+}
+
 static int mcde_dsi_host_attach(struct mipi_dsi_host *host,
 				struct mipi_dsi_device *mdsi)
 {
@@ -148,7 +157,7 @@ static int mcde_dsi_host_attach(struct mipi_dsi_host *host,
 
 	d->mdsi = mdsi;
 	if (d->mcde)
-		d->mcde->mdsi = mdsi;
+		mcde_dsi_attach_to_mcde(d);
 
 	return 0;
 }
@@ -901,7 +910,7 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
 	d->mcde = mcde;
 	/* If the display attached before binding, set this up */
 	if (d->mdsi)
-		d->mcde->mdsi = d->mdsi;
+		mcde_dsi_attach_to_mcde(d);
 
 	/* Obtain the clocks */
 	d->hs_clk = devm_clk_get(dev, "hs");
-- 
2.23.0


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

* [PATCH 3/7] drm/mcde: dsi: Make video mode errors more verbose
  2019-11-06 16:58 [PATCH 0/7] drm/mcde: DSI video mode fixes Stephan Gerhold
  2019-11-06 16:58 ` [PATCH 1/7] drm/mcde: Provide vblank handling unconditionally Stephan Gerhold
  2019-11-06 16:58 ` [PATCH 2/7] drm/mcde: Fix frame sync setup for video mode panels Stephan Gerhold
@ 2019-11-06 16:58 ` Stephan Gerhold
  2019-11-06 16:58 ` [PATCH 4/7] drm/mcde: dsi: Delay start of video stream generator Stephan Gerhold
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Stephan Gerhold @ 2019-11-06 16:58 UTC (permalink / raw)
  To: Linus Walleij
  Cc: David Airlie, Daniel Vetter, dri-devel, linux-kernel, Stephan Gerhold

Triggering an error conditions in DSI video mode only results in
a very generic "some video mode error status" error message
at the moment.

Make this more clear by adding separate error messages for each bit.

Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
---
 drivers/gpu/drm/mcde/mcde_dsi.c      | 22 +++++++++++++++++++++-
 drivers/gpu/drm/mcde/mcde_dsi_regs.h | 10 ++++++++++
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index ffd2e0b64628..c7956c92b51b 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -124,7 +124,27 @@ bool mcde_dsi_irq(struct mipi_dsi_device *mdsi)
 
 	val = readl(d->regs + DSI_VID_MODE_STS_FLAG);
 	if (val)
-		dev_err(d->dev, "some video mode error status\n");
+		dev_dbg(d->dev, "DSI_VID_MODE_STS_FLAG = %08x\n", val);
+	if (val & DSI_VID_MODE_STS_VSG_RUNNING)
+		dev_dbg(d->dev, "VID mode VSG running\n");
+	if (val & DSI_VID_MODE_STS_ERR_MISSING_DATA)
+		dev_err(d->dev, "VID mode missing data\n");
+	if (val & DSI_VID_MODE_STS_ERR_MISSING_HSYNC)
+		dev_err(d->dev, "VID mode missing HSYNC\n");
+	if (val & DSI_VID_MODE_STS_ERR_MISSING_VSYNC)
+		dev_err(d->dev, "VID mode missing VSYNC\n");
+	if (val & DSI_VID_MODE_STS_REG_ERR_SMALL_LENGTH)
+		dev_err(d->dev, "VID mode less bytes than expected between two HSYNC\n");
+	if (val & DSI_VID_MODE_STS_REG_ERR_SMALL_HEIGHT)
+		dev_err(d->dev, "VID mode less lines than expected between two VSYNC\n");
+	if (val & (DSI_VID_MODE_STS_ERR_BURSTWRITE |
+		   DSI_VID_MODE_STS_ERR_LINEWRITE |
+		   DSI_VID_MODE_STS_ERR_LONGREAD))
+		dev_err(d->dev, "VID mode read/write error\n");
+	if (val & DSI_VID_MODE_STS_ERR_VRS_WRONG_LENGTH)
+		dev_err(d->dev, "VID mode received packets differ from expected size\n");
+	if (val & DSI_VID_MODE_STS_VSG_RECOVERY)
+		dev_err(d->dev, "VID mode VSG in recovery mode\n");
 	writel(val, d->regs + DSI_VID_MODE_STS_CLR);
 
 	return te_received;
diff --git a/drivers/gpu/drm/mcde/mcde_dsi_regs.h b/drivers/gpu/drm/mcde/mcde_dsi_regs.h
index c9253321a3be..b03a336c235f 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi_regs.h
+++ b/drivers/gpu/drm/mcde/mcde_dsi_regs.h
@@ -248,6 +248,16 @@
 
 #define DSI_VID_MODE_STS 0x000000BC
 #define DSI_VID_MODE_STS_VSG_RUNNING BIT(0)
+#define DSI_VID_MODE_STS_ERR_MISSING_DATA BIT(1)
+#define DSI_VID_MODE_STS_ERR_MISSING_HSYNC BIT(2)
+#define DSI_VID_MODE_STS_ERR_MISSING_VSYNC BIT(3)
+#define DSI_VID_MODE_STS_REG_ERR_SMALL_LENGTH BIT(4)
+#define DSI_VID_MODE_STS_REG_ERR_SMALL_HEIGHT BIT(5)
+#define DSI_VID_MODE_STS_ERR_BURSTWRITE BIT(6)
+#define DSI_VID_MODE_STS_ERR_LINEWRITE BIT(7)
+#define DSI_VID_MODE_STS_ERR_LONGREAD BIT(8)
+#define DSI_VID_MODE_STS_ERR_VRS_WRONG_LENGTH BIT(9)
+#define DSI_VID_MODE_STS_VSG_RECOVERY BIT(10)
 
 #define DSI_VID_VCA_SETTING1 0x000000C0
 #define DSI_VID_VCA_SETTING1_MAX_BURST_LIMIT_SHIFT 0
-- 
2.23.0


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

* [PATCH 4/7] drm/mcde: dsi: Delay start of video stream generator
  2019-11-06 16:58 [PATCH 0/7] drm/mcde: DSI video mode fixes Stephan Gerhold
                   ` (2 preceding siblings ...)
  2019-11-06 16:58 ` [PATCH 3/7] drm/mcde: dsi: Make video mode errors more verbose Stephan Gerhold
@ 2019-11-06 16:58 ` Stephan Gerhold
  2019-11-06 16:58 ` [PATCH 5/7] drm/mcde: dsi: Fix duplicated DSI connector Stephan Gerhold
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Stephan Gerhold @ 2019-11-06 16:58 UTC (permalink / raw)
  To: Linus Walleij
  Cc: David Airlie, Daniel Vetter, dri-devel, linux-kernel, Stephan Gerhold

The initialization order for DSI video mode is important - if we
enable the video stream generator (VSG) before the MCDE DSI formatter
starts sending pixel data, it will immediately run into an error and
disable itself again.

Avoid this problem by delaying the activation of the VSG
until the MCDE DSI formatter is properly set up and running
(i.e. when mcde_dsi_bridge_enable() is called).

Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
---
 drivers/gpu/drm/mcde/mcde_dsi.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index c7956c92b51b..4710f23b2966 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -583,11 +583,6 @@ static void mcde_dsi_setup_video_mode(struct mcde_dsi *d,
 	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);
 }
 
 static void mcde_dsi_start(struct mcde_dsi *d)
@@ -699,6 +694,14 @@ static void mcde_dsi_start(struct mcde_dsi *d)
 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");
 };
-- 
2.23.0


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

* [PATCH 5/7] drm/mcde: dsi: Fix duplicated DSI connector
  2019-11-06 16:58 [PATCH 0/7] drm/mcde: DSI video mode fixes Stephan Gerhold
                   ` (3 preceding siblings ...)
  2019-11-06 16:58 ` [PATCH 4/7] drm/mcde: dsi: Delay start of video stream generator Stephan Gerhold
@ 2019-11-06 16:58 ` Stephan Gerhold
  2019-11-06 16:58 ` [PATCH 6/7] drm/mcde: dsi: Enable clocks in pre_enable() instead of mode_set() Stephan Gerhold
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Stephan Gerhold @ 2019-11-06 16:58 UTC (permalink / raw)
  To: Linus Walleij
  Cc: David Airlie, Daniel Vetter, dri-devel, linux-kernel, Stephan Gerhold

Using a (single) DSI display with MCDE currently results in
two "connected" connectors:

  Connector: DSI-1
          id             : 34
          encoder id     : 0
          conn           : connected
          size           : 0x0 (mm)
          count_modes    : 0
          count_props    : 5
          props          : 1 2 5 6 4
          count_encoders : 1
          encoders       : 33
  Connector: DSI-2
          id             : 35
          encoder id     : 33
          conn           : connected
          size           : 53x89 (mm)
          count_modes    : 1
          count_props    : 5
          props          : 1 2 5 6 4
          count_encoders : 1
          encoders       : 33
    Mode: "480x800" 480x800 60

Although both show up as connected, the first one does not have
any size and no available modes. This confuses userspace tools
(e.g. kmscube) who look for available modes for the first connector.

The reason for the duplicated connector is that mcde_dsi.c and the
DRM panel bridge helper both set up a DSI connector, with more or less
the same code. The connector set up by the DRM panel bridge is the
one that is correctly set up in the example above.

Therefore we can just remove the connector setup from mcde_dsi.c
and let the DRM core handle all the hard work.

Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
---
 drivers/gpu/drm/mcde/mcde_dsi.c | 52 +--------------------------------
 1 file changed, 1 insertion(+), 51 deletions(-)

diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index 4710f23b2966..df963e078c35 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -39,7 +39,6 @@ struct mcde_dsi {
 	struct device *dev;
 	struct mcde *mcde;
 	struct drm_bridge bridge;
-	struct drm_connector connector;
 	struct drm_panel *panel;
 	struct drm_bridge *bridge_out;
 	struct mipi_dsi_host dsi_host;
@@ -64,11 +63,6 @@ static inline struct mcde_dsi *host_to_mcde_dsi(struct mipi_dsi_host *h)
 	return container_of(h, struct mcde_dsi, dsi_host);
 }
 
-static inline struct mcde_dsi *connector_to_mcde_dsi(struct drm_connector *c)
-{
-	return container_of(c, struct mcde_dsi, connector);
-}
-
 bool mcde_dsi_irq(struct mipi_dsi_device *mdsi)
 {
 	struct mcde_dsi *d;
@@ -843,67 +837,23 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge)
 	clk_disable_unprepare(d->lp_clk);
 }
 
-/*
- * This connector needs no special handling, just use the default
- * helpers for everything. It's pretty dummy.
- */
-static const struct drm_connector_funcs mcde_dsi_connector_funcs = {
-	.reset = drm_atomic_helper_connector_reset,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = drm_connector_cleanup,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static int mcde_dsi_get_modes(struct drm_connector *connector)
-{
-	struct mcde_dsi *d = connector_to_mcde_dsi(connector);
-
-	/* Just pass the question to the panel */
-	if (d->panel)
-		return drm_panel_get_modes(d->panel);
-
-	/* TODO: deal with bridges */
-
-	return 0;
-}
-
-static const struct drm_connector_helper_funcs
-mcde_dsi_connector_helper_funcs = {
-	.get_modes = mcde_dsi_get_modes,
-};
-
 static int mcde_dsi_bridge_attach(struct drm_bridge *bridge)
 {
 	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
 	struct drm_device *drm = bridge->dev;
 	int ret;
 
-	drm_connector_helper_add(&d->connector,
-				 &mcde_dsi_connector_helper_funcs);
-
 	if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
 		dev_err(d->dev, "we need atomic updates\n");
 		return -ENOTSUPP;
 	}
 
-	ret = drm_connector_init(drm, &d->connector,
-				 &mcde_dsi_connector_funcs,
-				 DRM_MODE_CONNECTOR_DSI);
-	if (ret) {
-		dev_err(d->dev, "failed to initialize DSI bridge connector\n");
-		return ret;
-	}
-	d->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
-	/* The encoder in the bridge attached to the DSI bridge */
-	drm_connector_attach_encoder(&d->connector, bridge->encoder);
-	/* Then we attach the DSI bridge to the output (panel etc) bridge */
+	/* Attach the DSI bridge to the output (panel etc) bridge */
 	ret = drm_bridge_attach(bridge->encoder, d->bridge_out, bridge);
 	if (ret) {
 		dev_err(d->dev, "failed to attach the DSI bridge\n");
 		return ret;
 	}
-	d->connector.status = connector_status_connected;
 
 	return 0;
 }
-- 
2.23.0


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

* [PATCH 6/7] drm/mcde: dsi: Enable clocks in pre_enable() instead of mode_set()
  2019-11-06 16:58 [PATCH 0/7] drm/mcde: DSI video mode fixes Stephan Gerhold
                   ` (4 preceding siblings ...)
  2019-11-06 16:58 ` [PATCH 5/7] drm/mcde: dsi: Fix duplicated DSI connector Stephan Gerhold
@ 2019-11-06 16:58 ` Stephan Gerhold
  2019-11-06 16:58 ` [PATCH 7/7] drm/mcde: Handle pending vblank while disabling display Stephan Gerhold
  2019-11-08 15:52 ` [PATCH 0/7] drm/mcde: DSI video mode fixes Linus Walleij
  7 siblings, 0 replies; 9+ messages in thread
From: Stephan Gerhold @ 2019-11-06 16:58 UTC (permalink / raw)
  To: Linus Walleij
  Cc: David Airlie, Daniel Vetter, dri-devel, linux-kernel, Stephan Gerhold

The DSI initialization sequence incorrectly assumes that the mode_set()
function of the DRM bridge is always called when (re-)enabling the display.
This is not necessarily the case.

Keeping the device idle in the framebuffer console for a while results
in the display being turned off using the disable() function. However,
as soon as any key is pressed only (pre_)enable() are called.
mode_set() is skipped because the mode has not been changed.

In this case, the DSI HS/LP clocks are never turned back on,
preventing the display from working.

Fix this by moving a part of the initialization sequence from
mode_set() to pre_enable(). Keep most of the video mode setup in
mode_set() since most of the registers are only dependent on the mode
that is set for the panel - there is no need to write them again each
time we re-enable the display.

Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
---
 drivers/gpu/drm/mcde/mcde_dsi.c | 67 ++++++++++++++++++---------------
 1 file changed, 37 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index df963e078c35..03896a1f339a 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -562,21 +562,6 @@ static void mcde_dsi_setup_video_mode(struct mcde_dsi *d,
 		DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_SHIFT;
 	writel(val, d->regs + DSI_VID_VCA_SETTING2);
 
-	/* Put IF1 into video mode */
-	val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
-	val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE;
-	writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
-
-	/* Disable command mode on IF1 */
-	val = readl(d->regs + DSI_CMD_MODE_CTL);
-	val &= ~DSI_CMD_MODE_CTL_IF1_LP_EN;
-	writel(val, d->regs + DSI_CMD_MODE_CTL);
-
-	/* Enable some error interrupts */
-	val = readl(d->regs + DSI_VID_MODE_STS_CTL);
-	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);
 }
 
 static void mcde_dsi_start(struct mcde_dsi *d)
@@ -700,26 +685,13 @@ static void mcde_dsi_bridge_enable(struct drm_bridge *bridge)
 	dev_info(d->dev, "enable DSI master\n");
 };
 
-static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
-				     const struct drm_display_mode *mode,
-				     const struct drm_display_mode *adj)
+static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge)
 {
 	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
-	unsigned long pixel_clock_hz = mode->clock * 1000;
 	unsigned long hs_freq, lp_freq;
 	u32 val;
 	int ret;
 
-	if (!d->mdsi) {
-		dev_err(d->dev, "no DSI device attached to encoder!\n");
-		return;
-	}
-
-	dev_info(d->dev, "set DSI master to %dx%d %lu Hz %s mode\n",
-		 mode->hdisplay, mode->vdisplay, pixel_clock_hz,
-		 (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD"
-		);
-
 	/* Copy maximum clock frequencies */
 	if (d->mdsi->lp_rate)
 		lp_freq = d->mdsi->lp_rate;
@@ -758,7 +730,21 @@ static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
 			 d->hs_freq);
 
 	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
-		mcde_dsi_setup_video_mode(d, mode);
+		/* Put IF1 into video mode */
+		val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL);
+		val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE;
+		writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL);
+
+		/* Disable command mode on IF1 */
+		val = readl(d->regs + DSI_CMD_MODE_CTL);
+		val &= ~DSI_CMD_MODE_CTL_IF1_LP_EN;
+		writel(val, d->regs + DSI_CMD_MODE_CTL);
+
+		/* Enable some error interrupts */
+		val = readl(d->regs + DSI_VID_MODE_STS_CTL);
+		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);
 	} else {
 		/* Command mode, clear IF1 ID */
 		val = readl(d->regs + DSI_CMD_MODE_CTL);
@@ -772,6 +758,26 @@ static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
 	}
 }
 
+static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge,
+				     const struct drm_display_mode *mode,
+				     const struct drm_display_mode *adj)
+{
+	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
+
+	if (!d->mdsi) {
+		dev_err(d->dev, "no DSI device attached to encoder!\n");
+		return;
+	}
+
+	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)
 {
 	u32 val;
@@ -863,6 +869,7 @@ static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = {
 	.mode_set = mcde_dsi_bridge_mode_set,
 	.disable = mcde_dsi_bridge_disable,
 	.enable = mcde_dsi_bridge_enable,
+	.pre_enable = mcde_dsi_bridge_pre_enable,
 };
 
 static int mcde_dsi_bind(struct device *dev, struct device *master,
-- 
2.23.0


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

* [PATCH 7/7] drm/mcde: Handle pending vblank while disabling display
  2019-11-06 16:58 [PATCH 0/7] drm/mcde: DSI video mode fixes Stephan Gerhold
                   ` (5 preceding siblings ...)
  2019-11-06 16:58 ` [PATCH 6/7] drm/mcde: dsi: Enable clocks in pre_enable() instead of mode_set() Stephan Gerhold
@ 2019-11-06 16:58 ` Stephan Gerhold
  2019-11-08 15:52 ` [PATCH 0/7] drm/mcde: DSI video mode fixes Linus Walleij
  7 siblings, 0 replies; 9+ messages in thread
From: Stephan Gerhold @ 2019-11-06 16:58 UTC (permalink / raw)
  To: Linus Walleij
  Cc: David Airlie, Daniel Vetter, dri-devel, linux-kernel, Stephan Gerhold

Disabling the display using MCDE currently results in a warning
together with a delay caused by some timeouts:

    mcde a0350000.mcde: MCDE display is disabled
    ------------[ cut here ]------------
    WARNING: CPU: 0 PID: 20 at drivers/gpu/drm/drm_atomic_helper.c:2258 drm_atomic_helper_commit_hw_done+0xe0/0xe4
    Hardware name: ST-Ericsson Ux5x0 platform (Device Tree Support)
    Workqueue: events drm_mode_rmfb_work_fn
    [<c010f468>] (unwind_backtrace) from [<c010b54c>] (show_stack+0x10/0x14)
    [<c010b54c>] (show_stack) from [<c079dd90>] (dump_stack+0x84/0x98)
    [<c079dd90>] (dump_stack) from [<c011d1b0>] (__warn+0xb8/0xd4)
    [<c011d1b0>] (__warn) from [<c011d230>] (warn_slowpath_fmt+0x64/0xc4)
    [<c011d230>] (warn_slowpath_fmt) from [<c0413048>] (drm_atomic_helper_commit_hw_done+0xe0/0xe4)
    [<c0413048>] (drm_atomic_helper_commit_hw_done) from [<c04159cc>] (drm_atomic_helper_commit_tail_rpm+0x44/0x6c)
    [<c04159cc>] (drm_atomic_helper_commit_tail_rpm) from [<c0415f5c>] (commit_tail+0x50/0x10c)
    [<c0415f5c>] (commit_tail) from [<c04160dc>] (drm_atomic_helper_commit+0xbc/0x128)
    [<c04160dc>] (drm_atomic_helper_commit) from [<c0430790>] (drm_framebuffer_remove+0x390/0x428)
    [<c0430790>] (drm_framebuffer_remove) from [<c0430860>] (drm_mode_rmfb_work_fn+0x38/0x48)
    [<c0430860>] (drm_mode_rmfb_work_fn) from [<c01368a8>] (process_one_work+0x1f0/0x43c)
    [<c01368a8>] (process_one_work) from [<c0136d48>] (worker_thread+0x254/0x55c)
    [<c0136d48>] (worker_thread) from [<c013c014>] (kthread+0x124/0x150)
    [<c013c014>] (kthread) from [<c01010e8>] (ret_from_fork+0x14/0x2c)
    Exception stack(0xeb14dfb0 to 0xeb14dff8)
    dfa0:                                     00000000 00000000 00000000 00000000
    dfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    dfe0: 00000000 00000000 00000000 00000000 00000013 00000000
    ---[ end trace 314909bcd4c7d50c ]---
    [drm:drm_atomic_helper_wait_for_dependencies] *ERROR* [CRTC:32:crtc-0] flip_done timed out
    [drm:drm_atomic_helper_wait_for_dependencies] *ERROR* [CONNECTOR:34:DSI-1] flip_done timed out
    [drm:drm_atomic_helper_wait_for_dependencies] *ERROR* [PLANE:31:plane-0] flip_done timed out

The reason for this is that there is a vblank event pending, but we
never handle it after disabling the vblank interrupts.

Check if there is an vblank event pending when disabling the display,
and clear it by sending a fake vblank event in that case.

Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
---
 drivers/gpu/drm/mcde/mcde_display.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
index a3375a974caf..e59907e68854 100644
--- a/drivers/gpu/drm/mcde/mcde_display.c
+++ b/drivers/gpu/drm/mcde/mcde_display.c
@@ -949,12 +949,22 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
 	struct drm_crtc *crtc = &pipe->crtc;
 	struct drm_device *drm = crtc->dev;
 	struct mcde *mcde = drm->dev_private;
+	struct drm_pending_vblank_event *event;
 
 	drm_crtc_vblank_off(crtc);
 
 	/* Disable FIFO A flow */
 	mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
 
+	event = crtc->state->event;
+	if (event) {
+		crtc->state->event = NULL;
+
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_send_vblank_event(crtc, event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+	}
+
 	dev_info(drm->dev, "MCDE display is disabled\n");
 }
 
-- 
2.23.0


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

* Re: [PATCH 0/7] drm/mcde: DSI video mode fixes
  2019-11-06 16:58 [PATCH 0/7] drm/mcde: DSI video mode fixes Stephan Gerhold
                   ` (6 preceding siblings ...)
  2019-11-06 16:58 ` [PATCH 7/7] drm/mcde: Handle pending vblank while disabling display Stephan Gerhold
@ 2019-11-08 15:52 ` Linus Walleij
  7 siblings, 0 replies; 9+ messages in thread
From: Linus Walleij @ 2019-11-08 15:52 UTC (permalink / raw)
  To: Stephan Gerhold
  Cc: David Airlie, Daniel Vetter, open list:DRM PANEL DRIVERS, linux-kernel

On Wed, Nov 6, 2019 at 6:01 PM Stephan Gerhold <stephan@gerhold.net> wrote:

> This is a collection of fixes to make DSI video mode work better
> using the MCDE driver. With these changes, MCDE appears to work
> properly for the video mode panel I have been testing with.
>
> Note: The patch that fixes the DSI link register setup for
> video mode [1] is still necessary; but we still need to finish it
> and actually make it initialize a panel correctly.
>
> This series contains only patches for the other parts in MCDE.
> I have tested it by disabling most of the register setup in the
> DSI driver, which makes it re-use the properly working DSI register
> set by the bootloader.
>
> [1]: https://lists.freedesktop.org/archives/dri-devel/2019-October/238175.html
>
> Stephan Gerhold (7):
>   drm/mcde: Provide vblank handling unconditionally
>   drm/mcde: Fix frame sync setup for video mode panels
>   drm/mcde: dsi: Make video mode errors more verbose
>   drm/mcde: dsi: Delay start of video stream generator
>   drm/mcde: dsi: Fix duplicated DSI connector
>   drm/mcde: dsi: Enable clocks in pre_enable() instead of mode_set()
>   drm/mcde: Handle pending vblank while disabling display

Tested all 7 on the Ux500 HREFv60plus with the command mode
panel without problems.
Tested-by: Linus Walleij <linus.walleij@linaro.org>

I will apply and push to DRM misc for-next with my Tested-by tag.

Yours,
Linus Walleij

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

end of thread, other threads:[~2019-11-08 15:52 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-06 16:58 [PATCH 0/7] drm/mcde: DSI video mode fixes Stephan Gerhold
2019-11-06 16:58 ` [PATCH 1/7] drm/mcde: Provide vblank handling unconditionally Stephan Gerhold
2019-11-06 16:58 ` [PATCH 2/7] drm/mcde: Fix frame sync setup for video mode panels Stephan Gerhold
2019-11-06 16:58 ` [PATCH 3/7] drm/mcde: dsi: Make video mode errors more verbose Stephan Gerhold
2019-11-06 16:58 ` [PATCH 4/7] drm/mcde: dsi: Delay start of video stream generator Stephan Gerhold
2019-11-06 16:58 ` [PATCH 5/7] drm/mcde: dsi: Fix duplicated DSI connector Stephan Gerhold
2019-11-06 16:58 ` [PATCH 6/7] drm/mcde: dsi: Enable clocks in pre_enable() instead of mode_set() Stephan Gerhold
2019-11-06 16:58 ` [PATCH 7/7] drm/mcde: Handle pending vblank while disabling display Stephan Gerhold
2019-11-08 15:52 ` [PATCH 0/7] drm/mcde: DSI video mode fixes Linus Walleij

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