[PATCH 2/2 v2] drm/mcde: Fix display data flow control

Stephan Gerhold stephan at gerhold.net
Wed Jul 29 10:10:01 UTC 2020


On Wed, Jul 29, 2020 at 11:09:15AM +0200, Linus Walleij wrote:
> Revamp the way that the flow of data to the display is
> defined.
> 
> I realized that the hardware supports something like
> 5 different modes of flow: oneshot, command with TE IRQ,
> command with BTA (bus turn around) and TE IRQ, video
> with TE IRQ and video without TE IRQ instead synchronizing
> to the output of the MCDE DSI formatter.
> 
> Like before the selection of the type of flow is done
> frome the DSI driver when we attach it to the MCDE and we
> get to know what the display wants.
> 
> The new video mode synchronization method from the MCDE DSI
> formatter is used on some upstream devices such as Golden.
> This is the new default for video mode: stateless panels
> do not as a rule generate TE IRQs.
> 
> Another semantic change is that we stop sending
> a TE request before every command when sending data to
> a display in command mode: this should only be explicitly
> requested when using BTA, according to the vendor driver.
> 
> This has been tested and works fine with the command mode
> displays I have. (All that are supported upstream.)
> 
> Reported-by: Stephan Gerhold <stephan at gerhold.net>
> Cc: Stephan Gerhold <stephan at gerhold.net>
> Signed-off-by: Linus Walleij <linus.walleij at linaro.org>
> ---
> ChangeLog v1->v2:
> - Select the formatter as synchronization source for the
>   video flow, this should be the most common case.
> - Set the formatter as sync source for BTA+TE mode as in
>   the vendor driver, and insert a comment to help developers.
> ---
>  drivers/gpu/drm/mcde/mcde_display.c | 113 ++++++++++++++++++----------
>  drivers/gpu/drm/mcde/mcde_drm.h     |  26 ++++++-
>  drivers/gpu/drm/mcde/mcde_drv.c     |   3 -
>  drivers/gpu/drm/mcde/mcde_dsi.c     |  19 ++++-
>  4 files changed, 111 insertions(+), 50 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
> index 363fa5ca4b45..cac660ac8803 100644
> --- a/drivers/gpu/drm/mcde/mcde_display.c
> +++ b/drivers/gpu/drm/mcde/mcde_display.c
> @@ -89,7 +89,7 @@ void mcde_display_irq(struct mcde *mcde)
>  		 * the update function is called, then we disable the
>  		 * flow on the channel once we get the TE IRQ.
>  		 */
> -		if (mcde->oneshot_mode) {
> +		if (mcde->flow_mode == MCDE_COMMAND_ONESHOT_FLOW) {
>  			spin_lock(&mcde->flow_lock);
>  			if (--mcde->flow_active == 0) {
>  				dev_dbg(mcde->dev, "TE0 IRQ\n");
> @@ -498,19 +498,47 @@ static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch,
>  	}
>  
>  	/* Set up channel 0 sync (based on chnl_update_registers()) */
> -	if (mcde->video_mode || mcde->te_sync)
> +	switch (mcde->flow_mode) {
> +	case MCDE_COMMAND_ONESHOT_FLOW:
> +		/* Oneshot is achieved with software sync */
> +		val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SOFTWARE
> +			<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
> +		break;
> +	case MCDE_COMMAND_TE_FLOW:
>  		val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
>  			<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
> -	else
> -		val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SOFTWARE
> +		val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0
> +			<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
> +		break;
> +	case MCDE_COMMAND_BTA_TE_FLOW:
> +		val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
> +			<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
> +		/*
> +		 * TODO:
> +		 * The vendor driver uses the formatter as sync source
> +		 * for BTA TE mode. Test to use TE if you have a panel
> +		 * that uses this mode.
> +		 */
> +		val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER
> +			<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
> +		break;
> +	case MCDE_VIDEO_TE_FLOW:
> +		val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
>  			<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
> -
> -	if (mcde->te_sync)
>  		val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0
>  			<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
> -	else
> +		break;
> +	case MCDE_VIDEO_FORMATTER_FLOW:
> +		val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
> +			<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
>  		val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER
>  			<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
> +		break;
> +	default:
> +		dev_err(mcde->dev, "unknown flow mode %d\n",
> +			mcde->flow_mode);
> +		break;
> +	}
>  
>  	writel(val, mcde->regs + sync);
>  
> @@ -920,7 +948,11 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
>  	mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0,
>  				     formatter_frame, pkt_size);
>  
> -	if (mcde->te_sync) {
> +	switch (mcde->flow_mode) {
> +	case MCDE_COMMAND_TE_FLOW:
> +	case MCDE_COMMAND_BTA_TE_FLOW:
> +	case MCDE_VIDEO_TE_FLOW:
> +		/* We are using TE in some comination */

Typo: You meant combination I guess?

Otherwise this looks good to me, feel free to fix this
when applying the patch:

Reviewed-by: Stephan Gerhold <stephan at gerhold.net>

>  		if (mode->flags & DRM_MODE_FLAG_NVSYNC)
>  			val = MCDE_VSCRC_VSPOL;
>  		else
> @@ -930,16 +962,22 @@ 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);
> +		break;
> +	default:
> +		/* No TE capture */
> +		break;
>  	}
>  
>  	drm_crtc_vblank_on(crtc);
>  
> -	if (mcde->video_mode)
> +	if (mcde_flow_is_video(mcde)) {
>  		/*
>  		 * Keep FIFO permanently enabled in video mode,
>  		 * otherwise MCDE will stop feeding data to the panel.
>  		 */
>  		mcde_enable_fifo(mcde, MCDE_FIFO_A);
> +		dev_dbg(mcde->dev, "started MCDE video FIFO flow\n");
> +	}
>  
>  	dev_info(drm->dev, "MCDE display is enabled\n");
>  }
> @@ -970,38 +1008,36 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
>  
>  static void mcde_start_flow(struct mcde *mcde)
>  {
> -	/* Request a TE ACK */
> -	if (mcde->te_sync)
> +	/* Request a TE ACK only in TE+BTA mode */
> +	if (mcde->flow_mode == MCDE_COMMAND_BTA_TE_FLOW)
>  		mcde_dsi_te_request(mcde->mdsi);
>  
>  	/* Enable FIFO A flow */
>  	mcde_enable_fifo(mcde, MCDE_FIFO_A);
>  
> -	if (mcde->te_sync) {
> +	/*
> +	 * If oneshot mode is enabled, the flow will be disabled
> +	 * when the TE0 IRQ arrives in the interrupt handler. Otherwise
> +	 * updates are continuously streamed to the display after this
> +	 * point.
> +	 */
> +
> +	if (mcde->flow_mode == MCDE_COMMAND_ONESHOT_FLOW) {
> +		/* Trigger a software sync out on channel 0 */
> +		writel(MCDE_CHNLXSYNCHSW_SW_TRIG,
> +		       mcde->regs + MCDE_CHNL0SYNCHSW);
> +
>  		/*
> -		 * If oneshot mode is enabled, the flow will be disabled
> -		 * when the TE0 IRQ arrives in the interrupt handler. Otherwise
> -		 * updates are continuously streamed to the display after this
> -		 * point.
> +		 * Disable FIFO A flow again: since we are using TE sync we
> +		 * need to wait for the FIFO to drain before we continue
> +		 * so repeated calls to this function will not cause a mess
> +		 * in the hardware by pushing updates will updates are going
> +		 * on already.
>  		 */
> -		dev_dbg(mcde->dev, "sent TE0 framebuffer update\n");
> -		return;
> +		mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
>  	}
>  
> -	/* Trigger a software sync out on channel 0 */
> -	writel(MCDE_CHNLXSYNCHSW_SW_TRIG,
> -	       mcde->regs + MCDE_CHNL0SYNCHSW);
> -
> -	/*
> -	 * Disable FIFO A flow again: since we are using TE sync we
> -	 * need to wait for the FIFO to drain before we continue
> -	 * so repeated calls to this function will not cause a mess
> -	 * in the hardware by pushing updates will updates are going
> -	 * on already.
> -	 */
> -	mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
> -
> -	dev_dbg(mcde->dev, "sent SW framebuffer update\n");
> +	dev_dbg(mcde->dev, "started MCDE FIFO flow\n");
>  }
>  
>  static void mcde_set_extsrc(struct mcde *mcde, u32 buffer_address)
> @@ -1060,15 +1096,10 @@ 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));
> -		if (!mcde->video_mode) {
> -			/*
> -			 * Send a single frame using software sync if the flow
> -			 * is not active yet.
> -			 */
> -			if (mcde->flow_active == 0)
> -				mcde_start_flow(mcde);
> -		}
> -		dev_info_once(mcde->dev, "sent first display update\n");
> +		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)
> +			mcde_start_flow(mcde);
>  	} else {
>  		/*
>  		 * If an update is receieved before the MCDE is enabled
> diff --git a/drivers/gpu/drm/mcde/mcde_drm.h b/drivers/gpu/drm/mcde/mcde_drm.h
> index 679c2c4e6d9d..3e406d783465 100644
> --- a/drivers/gpu/drm/mcde/mcde_drm.h
> +++ b/drivers/gpu/drm/mcde/mcde_drm.h
> @@ -9,6 +9,22 @@
>  #ifndef _MCDE_DRM_H_
>  #define _MCDE_DRM_H_
>  
> +enum mcde_flow_mode {
> +	/* One-shot mode: flow stops after one frame */
> +	MCDE_COMMAND_ONESHOT_FLOW,
> +	/* Command mode with tearing effect (TE) IRQ sync */
> +	MCDE_COMMAND_TE_FLOW,
> +	/*
> +	 * Command mode with bus turn-around (BTA) and tearing effect
> +	 * (TE) IRQ sync.
> +	 */
> +	MCDE_COMMAND_BTA_TE_FLOW,
> +	/* Video mode with tearing effect (TE) sync IRQ */
> +	MCDE_VIDEO_TE_FLOW,
> +	/* Video mode with the formatter itself as sync source */
> +	MCDE_VIDEO_FORMATTER_FLOW,
> +};
> +
>  struct mcde {
>  	struct drm_device drm;
>  	struct device *dev;
> @@ -18,9 +34,7 @@ struct mcde {
>  	struct drm_simple_display_pipe pipe;
>  	struct mipi_dsi_device *mdsi;
>  	s16 stride;
> -	bool te_sync;
> -	bool video_mode;
> -	bool oneshot_mode;
> +	enum mcde_flow_mode flow_mode;
>  	unsigned int flow_active;
>  	spinlock_t flow_lock; /* Locks the channel flow control */
>  
> @@ -36,6 +50,12 @@ struct mcde {
>  
>  #define to_mcde(dev) container_of(dev, struct mcde, drm)
>  
> +static inline bool mcde_flow_is_video(struct mcde *mcde)
> +{
> +	return (mcde->flow_mode == MCDE_VIDEO_TE_FLOW ||
> +		mcde->flow_mode == MCDE_VIDEO_FORMATTER_FLOW);
> +}
> +
>  bool mcde_dsi_irq(struct mipi_dsi_device *mdsi);
>  void mcde_dsi_te_request(struct mipi_dsi_device *mdsi);
>  extern struct platform_driver mcde_dsi_driver;
> diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c
> index 80082d6dce3a..1671af101cf2 100644
> --- a/drivers/gpu/drm/mcde/mcde_drv.c
> +++ b/drivers/gpu/drm/mcde/mcde_drv.c
> @@ -315,9 +315,6 @@ static int mcde_probe(struct platform_device *pdev)
>  	mcde->dev = dev;
>  	platform_set_drvdata(pdev, drm);
>  
> -	/* Enable continuous updates: this is what Linux' framebuffer expects */
> -	mcde->oneshot_mode = false;
> -
>  	/* First obtain and turn on the main power */
>  	mcde->epod = devm_regulator_get(dev, "epod");
>  	if (IS_ERR(mcde->epod)) {
> diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
> index f303369305a3..337c4c5e3947 100644
> --- a/drivers/gpu/drm/mcde/mcde_dsi.c
> +++ b/drivers/gpu/drm/mcde/mcde_dsi.c
> @@ -148,9 +148,22 @@ 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;
> +	/*
> +	 * Select the way the DSI data flow is pushing to the display:
> +	 * currently we just support video or command mode depending
> +	 * on the type of display. Video mode defaults to using the
> +	 * formatter itself for synchronization (stateless video panel).
> +	 *
> +	 * FIXME: add flags to struct mipi_dsi_device .flags to indicate
> +	 * displays that require BTA (bus turn around) so we can handle
> +	 * such displays as well. Figure out how to properly handle
> +	 * single frame on-demand updates with DRM for command mode
> +	 * displays (MCDE_COMMAND_ONESHOT_FLOW).
> +	 */
> +	if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO)
> +		d->mcde->flow_mode = MCDE_VIDEO_FORMATTER_FLOW;
> +	else
> +		d->mcde->flow_mode = MCDE_COMMAND_TE_FLOW;
>  }
>  
>  static int mcde_dsi_host_attach(struct mipi_dsi_host *host,
> -- 
> 2.26.2
> 


More information about the dri-devel mailing list