diff options
| author | Clarence Ip <cip@codeaurora.org> | 2016-08-16 14:44:41 -0400 |
|---|---|---|
| committer | Clarence Ip <cip@codeaurora.org> | 2016-10-11 09:48:33 -0400 |
| commit | e3e50f0cbe50419ac8008cd30791ebf55c6a3aa3 (patch) | |
| tree | 059118711eba953f5186aa2c9028c4f7e125318e /drivers/gpu | |
| parent | f02f23f17f82c10eacd5da289df1d76ef19e13df (diff) | |
drm/msm/sde: enable proper support for split flush
Enable atomic hardware flushing of two data paths via a single
flush register. Previously, different data paths were flushed
separately from software, which could potentially result in
the flushes happening on different frames.
Change-Id: I858a0f737743d48404f47af85b407e1cddf0a0a7
Signed-off-by: Clarence Ip <cip@codeaurora.org>
Diffstat (limited to 'drivers/gpu')
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_crtc.c | 51 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_encoder.c | 138 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_encoder_phys.h | 14 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c | 18 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c | 17 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c | 13 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_hw_ctl.c | 9 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_hw_ctl.h | 7 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_hw_top.c | 11 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde/sde_kms.h | 14 |
10 files changed, 170 insertions, 122 deletions
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index a0ba371aecaa..08ec3c0d6ffc 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -380,54 +380,6 @@ void sde_crtc_complete_commit(struct drm_crtc *crtc) } /** - * _sde_crtc_trigger_kickoff - Iterate through the control paths and trigger - * the hw_ctl object to flush any pending flush mask, and trigger - * control start if the interface types require it. - * - * This is currently designed to be called only once per crtc, per flush. - * It should be called from the encoder, through the - * sde_encoder_schedule_kickoff callflow, after all the encoders are ready - * to have CTL_START triggered. - * - * It is called from the commit thread context. - * @data: crtc pointer - */ -static void _sde_crtc_trigger_kickoff(void *data) -{ - struct drm_crtc *crtc = (struct drm_crtc *)data; - struct sde_crtc *sde_crtc = to_sde_crtc(crtc); - struct sde_crtc_mixer *mixer; - struct sde_hw_ctl *ctl; - int i; - - if (!data) { - SDE_ERROR("invalid argument\n"); - return; - } - - MSM_EVT(crtc->dev, crtc->base.id, 0); - - /* Commit all pending flush masks to hardware */ - for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) { - ctl = sde_crtc->mixers[i].hw_ctl; - if (ctl) { - ctl->ops.trigger_flush(ctl); - MSM_EVT(crtc->dev, crtc->base.id, ctl->idx); - } - } - - /* Signal start to any interface types that require it */ - for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) { - mixer = &sde_crtc->mixers[i]; - ctl = mixer->hw_ctl; - if (ctl && sde_encoder_needs_ctl_start(mixer->encoder)) { - ctl->ops.trigger_start(ctl); - MSM_EVT(crtc->dev, crtc->base.id, ctl->idx); - } - } -} - -/** * _sde_crtc_set_input_fence_timeout - update ns version of in fence timeout * @cstate: Pointer to sde crtc state */ @@ -724,8 +676,7 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc) * Encoder will flush/start now, unless it has a tx pending. * If so, it may delay and flush at an irq event (e.g. ppdone) */ - sde_encoder_schedule_kickoff(encoder, _sde_crtc_trigger_kickoff, - crtc); + sde_encoder_schedule_kickoff(encoder); } } diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 77a0b031793b..7a43e4496843 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -208,24 +208,6 @@ void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc, } } -bool sde_encoder_needs_ctl_start(struct drm_encoder *drm_enc) -{ - struct sde_encoder_virt *sde_enc = NULL; - struct sde_encoder_phys *phys; - - if (!drm_enc) { - SDE_ERROR("invalid pointer\n"); - return false; - } - sde_enc = to_sde_encoder_virt(drm_enc); - phys = sde_enc->cur_master; - - if (phys && phys->ops.needs_ctl_start) - return phys->ops.needs_ctl_start(phys); - - return false; -} - static void sde_encoder_destroy(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc = NULL; @@ -550,8 +532,121 @@ static void sde_encoder_handle_phys_enc_ready_for_kickoff( wake_up_all(&sde_enc->pending_kickoff_wq); } -void sde_encoder_schedule_kickoff(struct drm_encoder *drm_enc, - void (*kickoff_cb)(void *), void *kickoff_data) +/** + * _sde_encoder_trigger_flush - trigger flush for a physical encoder + * drm_enc: Pointer to drm encoder structure + * phys: Pointer to physical encoder structure + * extra_flush_bits: Additional bit mask to include in flush trigger + */ +static inline void _sde_encoder_trigger_flush(struct drm_encoder *drm_enc, + struct sde_encoder_phys *phys, uint32_t extra_flush_bits) +{ + struct sde_hw_ctl *ctl; + + if (!drm_enc || !phys) { + SDE_ERROR("invalid argument(s), drm_enc %d, phys_enc %d\n", + drm_enc != 0, phys != 0); + return; + } + + ctl = phys->hw_ctl; + if (!ctl || !ctl->ops.trigger_flush) { + SDE_ERROR("missing trigger cb\n"); + return; + } + + if (extra_flush_bits && ctl->ops.update_pending_flush) + ctl->ops.update_pending_flush(ctl, extra_flush_bits); + + ctl->ops.trigger_flush(ctl); + MSM_EVT(drm_enc->dev, drm_enc->base.id, ctl->idx); +} + +/** + * _sde_encoder_trigger_start - trigger start for a physical encoder + * phys: Pointer to physical encoder structure + */ +static inline void _sde_encoder_trigger_start(struct sde_encoder_phys *phys) +{ + if (!phys) { + SDE_ERROR("invalid encoder\n"); + return; + } + + if (phys->ops.trigger_start && phys->enable_state != SDE_ENC_DISABLED) + phys->ops.trigger_start(phys); +} + +void sde_encoder_helper_trigger_start(struct sde_encoder_phys *phys_enc) +{ + struct sde_hw_ctl *ctl; + int ctl_idx = -1; + + if (!phys_enc) { + SDE_ERROR("invalid encoder\n"); + return; + } + + ctl = phys_enc->hw_ctl; + if (ctl && ctl->ops.trigger_start) { + ctl->ops.trigger_start(ctl); + ctl_idx = ctl->idx; + } + + if (phys_enc && phys_enc->parent) + MSM_EVT(phys_enc->parent->dev, + phys_enc->parent->base.id, + ctl_idx); +} + +/** + * _sde_encoder_kickoff_phys - handle physical encoder kickoff + * Iterate through the physical encoders and perform consolidated flush + * and/or control start triggering as needed. This is done in the virtual + * encoder rather than the individual physical ones in order to handle + * use cases that require visibility into multiple physical encoders at + * a time. + * sde_enc: Pointer to virtual encoder structure + */ +static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc) +{ + struct sde_hw_ctl *ctl; + uint32_t i, pending_flush; + + if (!sde_enc) { + SDE_ERROR("invalid encoder\n"); + return; + } + + pending_flush = 0x0; + + /* don't perform flush/start operations for slave encoders */ + for (i = 0; i < sde_enc->num_phys_encs; i++) { + struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; + + ctl = phys->hw_ctl; + if (!ctl || phys->enable_state == SDE_ENC_DISABLED) + continue; + + if (!phys->ops.needs_split_flush || + !phys->ops.needs_split_flush(phys)) + _sde_encoder_trigger_flush(&sde_enc->base, phys, 0x0); + else if (ctl->ops.get_pending_flush) + pending_flush |= ctl->ops.get_pending_flush(ctl); + } + + /* for split flush, combine pending flush masks and send to master */ + if (pending_flush && sde_enc->cur_master) { + _sde_encoder_trigger_flush( + &sde_enc->base, + sde_enc->cur_master, + pending_flush); + } + + _sde_encoder_trigger_start(sde_enc->cur_master); +} + +void sde_encoder_schedule_kickoff(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc; struct sde_encoder_phys *phys; @@ -607,8 +702,7 @@ void sde_encoder_schedule_kickoff(struct drm_encoder *drm_enc, } /* All phys encs are ready to go, trigger the kickoff */ - if (kickoff_cb) - kickoff_cb(kickoff_data); + _sde_encoder_kickoff_phys(sde_enc); /* Allow phys encs to handle any post-kickoff business */ for (i = 0; i < sde_enc->num_phys_encs; i++) { diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h index 026c6118a4de..7dbc629eb513 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h @@ -79,7 +79,8 @@ struct sde_encoder_virt_ops { * triggering the next kickoff * (ie for previous tx to complete) * @handle_post_kickoff: Do any work necessary post-kickoff work - * @needs_ctl_start: Whether encoder type needs ctl_start + * @trigger_start: Process start event on physical encoder + * @needs_split_flush: Whether encoder type needs split flush */ struct sde_encoder_phys_ops { @@ -104,7 +105,8 @@ struct sde_encoder_phys_ops { void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc, bool *wait_until_ready); void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc); - bool (*needs_ctl_start)(struct sde_encoder_phys *phys_enc); + void (*trigger_start)(struct sde_encoder_phys *phys_enc); + bool (*needs_split_flush)(struct sde_encoder_phys *phys_enc); }; /** @@ -301,4 +303,12 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc, struct drm_framebuffer *fb, const struct sde_format *format, struct sde_rect *wb_roi); +/** + * sde_encoder_helper_trigger_start - control start helper function + * This helper function may be optionally specified by physical + * encoders if they require ctl_start triggering. + * @phys_enc: Pointer to physical encoder structure + */ +void sde_encoder_helper_trigger_start(struct sde_encoder_phys *phys_enc); + #endif /* __sde_encoder_phys_H__ */ diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c index 477afcf91aea..e7a7d85bb758 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c @@ -335,6 +335,12 @@ static void sde_encoder_phys_cmd_pingpong_config( sde_encoder_phys_cmd_tearcheck_config(phys_enc); } +static bool sde_encoder_phys_cmd_needs_split_flush( + struct sde_encoder_phys *phys_enc) +{ + return false; +} + static void sde_encoder_phys_cmd_split_config( struct sde_encoder_phys *phys_enc, bool enable) { @@ -352,7 +358,8 @@ static void sde_encoder_phys_cmd_split_config( cfg.en = enable; cfg.mode = INTF_MODE_CMD; cfg.intf = cmd_enc->intf_idx; - cfg.split_flush_en = enable; + cfg.split_flush_en = enable && + sde_encoder_phys_cmd_needs_split_flush(phys_enc); if (hw_mdptop && hw_mdptop->ops.setup_split_pipe) hw_mdptop->ops.setup_split_pipe(hw_mdptop, &cfg); @@ -539,12 +546,6 @@ static void sde_encoder_phys_cmd_prepare_for_kickoff( MSM_EVT(DEV(phys_enc), cmd_enc->hw_pp->idx, new_pending_cnt); } -static bool sde_encoder_phys_cmd_needs_ctl_start( - struct sde_encoder_phys *phys_enc) -{ - return true; -} - static void sde_encoder_phys_cmd_init_ops( struct sde_encoder_phys_ops *ops) { @@ -558,7 +559,8 @@ static void sde_encoder_phys_cmd_init_ops( ops->control_vblank_irq = sde_encoder_phys_cmd_control_vblank_irq; ops->wait_for_commit_done = sde_encoder_phys_cmd_wait_for_commit_done; ops->prepare_for_kickoff = sde_encoder_phys_cmd_prepare_for_kickoff; - ops->needs_ctl_start = sde_encoder_phys_cmd_needs_ctl_start; + ops->trigger_start = sde_encoder_helper_trigger_start; + ops->needs_split_flush = sde_encoder_phys_cmd_needs_split_flush; } struct sde_encoder_phys *sde_encoder_phys_cmd_init( diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c index 9f1127349b1e..8ea6daaf26db 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c @@ -315,6 +315,12 @@ static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx) complete_all(&vid_enc->vblank_completion); } +static bool sde_encoder_phys_vid_needs_split_flush( + struct sde_encoder_phys *phys_enc) +{ + return phys_enc && phys_enc->split_role != ENC_ROLE_SOLO; +} + static void _sde_encoder_phys_vid_split_config( struct sde_encoder_phys *phys_enc, bool enable) { @@ -328,7 +334,8 @@ static void _sde_encoder_phys_vid_split_config( cfg.en = enable; cfg.mode = INTF_MODE_VIDEO; cfg.intf = vid_enc->hw_intf->idx; - cfg.split_flush_en = enable; + cfg.split_flush_en = enable && + sde_encoder_phys_vid_needs_split_flush(phys_enc); /* Configure split pipe control to handle master/slave triggering */ if (hw_mdptop && hw_mdptop->ops.setup_split_pipe) { @@ -664,12 +671,6 @@ static void sde_encoder_phys_vid_handle_post_kickoff( } } -static bool sde_encoder_phys_vid_needs_ctl_start( - struct sde_encoder_phys *phys_enc) -{ - return false; -} - static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops) { ops->is_master = sde_encoder_phys_vid_is_master; @@ -683,7 +684,7 @@ static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops) ops->wait_for_commit_done = sde_encoder_phys_vid_wait_for_commit_done; ops->prepare_for_kickoff = sde_encoder_phys_vid_prepare_for_kickoff; ops->handle_post_kickoff = sde_encoder_phys_vid_handle_post_kickoff; - ops->needs_ctl_start = sde_encoder_phys_vid_needs_ctl_start; + ops->needs_split_flush = sde_encoder_phys_vid_needs_split_flush; } struct sde_encoder_phys *sde_encoder_phys_vid_init( diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c index 6e3d39d15d0f..9bc8914fd493 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c @@ -882,17 +882,6 @@ static void sde_encoder_phys_wb_get_hw_resources( hw_res->needs_cdm); } -/** - * sde_encoder_phys_wb_needs_ctl_start - Whether encoder needs ctl_start - * @phys_enc: Pointer to physical encoder - * @Return: Whether encoder needs ctl_start - */ -static bool sde_encoder_phys_wb_needs_ctl_start( - struct sde_encoder_phys *phys_enc) -{ - return true; -} - #ifdef CONFIG_DEBUG_FS /** * sde_encoder_phys_wb_init_debugfs - initialize writeback encoder debugfs @@ -994,7 +983,7 @@ static void sde_encoder_phys_wb_init_ops(struct sde_encoder_phys_ops *ops) ops->wait_for_commit_done = sde_encoder_phys_wb_wait_for_commit_done; ops->prepare_for_kickoff = sde_encoder_phys_wb_prepare_for_kickoff; ops->handle_post_kickoff = sde_encoder_phys_wb_handle_post_kickoff; - ops->needs_ctl_start = sde_encoder_phys_wb_needs_ctl_start; + ops->trigger_start = sde_encoder_helper_trigger_start; } /** diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c index ea0567a24b82..4e8033a0b8f6 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c @@ -78,6 +78,14 @@ static inline void sde_hw_ctl_update_pending_flush(struct sde_hw_ctl *ctx, ctx->pending_flush_mask |= flushbits; } +static u32 sde_hw_ctl_get_pending_flush(struct sde_hw_ctl *ctx) +{ + if (!ctx) + return 0x0; + + return ctx->pending_flush_mask; +} + static inline void sde_hw_ctl_trigger_flush(struct sde_hw_ctl *ctx) { SDE_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask); @@ -377,6 +385,7 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops, { ops->clear_pending_flush = sde_hw_ctl_clear_pending_flush; ops->update_pending_flush = sde_hw_ctl_update_pending_flush; + ops->get_pending_flush = sde_hw_ctl_get_pending_flush; ops->trigger_flush = sde_hw_ctl_trigger_flush; ops->trigger_start = sde_hw_ctl_trigger_start; ops->setup_intf_cfg = sde_hw_ctl_intf_cfg; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h index 46f9a24de5cf..2f9ff5b76c3b 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h @@ -72,6 +72,13 @@ struct sde_hw_ctl_ops { void (*clear_pending_flush)(struct sde_hw_ctl *ctx); /** + * Query the value of the cached pending_flush_mask + * No effect on hardware + * @ctx : ctl path ctx pointer + */ + u32 (*get_pending_flush)(struct sde_hw_ctl *ctx); + + /** * OR in the given flushbits to the cached pending_flush_mask * No effect on hardware * @ctx : ctl path ctx pointer diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c index 9e9fb042ad6a..8950a60c0d68 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_top.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c @@ -14,17 +14,14 @@ #include "sde_hw_catalog.h" #include "sde_hw_top.h" -#define SSPP_SPARE 0x24 -#define SPLIT_DISPLAY_ENABLE 0x2F4 +#define SSPP_SPARE 0x28 -#define LOWER_PIPE_CTRL 0x2F8 #define FLD_SPLIT_DISPLAY_CMD BIT(1) #define FLD_SMART_PANEL_FREE_RUN BIT(2) #define FLD_INTF_1_SW_TRG_MUX BIT(4) #define FLD_INTF_2_SW_TRG_MUX BIT(8) #define FLD_TE_LINE_INTER_WATERLEVEL_MASK 0xFFFF -#define UPPER_PIPE_CTRL 0x3F0 #define TE_LINE_INTERVAL 0x3F4 #define TRAFFIC_SHAPER_EN BIT(31) @@ -65,9 +62,9 @@ static void sde_hw_setup_split_pipe_control(struct sde_hw_mdp *mdp, } SDE_REG_WRITE(c, SSPP_SPARE, (cfg->split_flush_en) ? 0x1 : 0x0); - SDE_REG_WRITE(c, LOWER_PIPE_CTRL, lower_pipe); - SDE_REG_WRITE(c, UPPER_PIPE_CTRL, upper_pipe); - SDE_REG_WRITE(c, SPLIT_DISPLAY_ENABLE, cfg->en & 0x1); + SDE_REG_WRITE(c, SPLIT_DISPLAY_LOWER_PIPE_CTRL, lower_pipe); + SDE_REG_WRITE(c, SPLIT_DISPLAY_UPPER_PIPE_CTRL, upper_pipe); + SDE_REG_WRITE(c, SPLIT_DISPLAY_EN, cfg->en & 0x1); } static void sde_hw_setup_cdm_output(struct sde_hw_mdp *mdp, diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h index 8fb3dfe67c4d..7f2c7f3cf3b3 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.h +++ b/drivers/gpu/drm/msm/sde/sde_kms.h @@ -460,14 +460,6 @@ void sde_encoder_get_hw_resources(struct drm_encoder *encoder, struct drm_connector_state *conn_state); /** - * sde_encoder_needs_ctl_start - Get whether encoder type requires ctl_start - * CMD and WB encoders need ctl_start, video encs do not. - * @encoder: encoder pointer - * @Return: true if the encoder type requires ctl_start issued - */ -bool sde_encoder_needs_ctl_start(struct drm_encoder *encoder); - -/** * sde_encoder_register_vblank_callback - provide callback to encoder that * will be called on the next vblank. * @encoder: encoder pointer @@ -485,13 +477,9 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *encoder, * Delayed: Save the callback, and return. Does not block. Callback will * be triggered later. E.g. cmd encoder will trigger at pp_done irq * irq if it outstanding. - * Callback registered is expected to flush _all_ ctl paths of the crtc * @encoder: encoder pointer - * @cb: callback pointer, provide NULL to deregister - * @data: user data provided to callback */ -void sde_encoder_schedule_kickoff(struct drm_encoder *encoder, - void (*cb)(void *), void *data); +void sde_encoder_schedule_kickoff(struct drm_encoder *encoder); /** * sde_encoder_wait_nxt_committed - Wait for hardware to have flushed the |
