diff options
-rw-r--r-- | drivers/gpu/drm/mcde/mcde_display.c | 113 | ||||
-rw-r--r-- | drivers/gpu/drm/mcde/mcde_drm.h | 26 | ||||
-rw-r--r-- | drivers/gpu/drm/mcde/mcde_drv.c | 3 | ||||
-rw-r--r-- | 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 */ 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 82137ab76cfc..13418a15f8df 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -305,9 +305,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 981923caa7e6..a342e898298b 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, |