diff options
Diffstat (limited to 'drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c')
-rw-r--r-- | drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 233 |
1 files changed, 215 insertions, 18 deletions
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index e5dcd41a361f..0714936d8835 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -953,6 +953,45 @@ static int _dpu_crtc_wait_for_frame_done(struct drm_crtc *crtc) return rc; } +static int dpu_crtc_kickoff_clone_mode(struct drm_crtc *crtc) +{ + struct drm_encoder *encoder; + struct drm_encoder *rt_encoder = NULL, *wb_encoder = NULL; + struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); + + /* Find encoder for real time display */ + drm_for_each_encoder_mask(encoder, crtc->dev, + crtc->state->encoder_mask) { + if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) + wb_encoder = encoder; + else + rt_encoder = encoder; + } + + if (!rt_encoder || !wb_encoder) { + DRM_DEBUG_ATOMIC("real time or wb encoder not found\n"); + return -EINVAL; + } + + dpu_encoder_prepare_for_kickoff(wb_encoder); + dpu_encoder_prepare_for_kickoff(rt_encoder); + + dpu_vbif_clear_errors(dpu_kms); + + /* + * Kickoff real time encoder last as it's the encoder that + * will do the flush + */ + dpu_encoder_kickoff(wb_encoder); + dpu_encoder_kickoff(rt_encoder); + + /* Don't start frame done timers until the kickoffs have finished */ + dpu_encoder_start_frame_done_timer(wb_encoder); + dpu_encoder_start_frame_done_timer(rt_encoder); + + return 0; +} + /** * dpu_crtc_commit_kickoff - trigger kickoff of the commit for this crtc * @crtc: Pointer to drm crtc object @@ -981,13 +1020,27 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) goto end; } } - /* - * 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) - */ - drm_for_each_encoder_mask(encoder, crtc->dev, - crtc->state->encoder_mask) - dpu_encoder_prepare_for_kickoff(encoder); + + if (drm_crtc_in_clone_mode(crtc->state)) { + if (dpu_crtc_kickoff_clone_mode(crtc)) + goto end; + } else { + /* + * 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) + */ + drm_for_each_encoder_mask(encoder, crtc->dev, + crtc->state->encoder_mask) + dpu_encoder_prepare_for_kickoff(encoder); + + dpu_vbif_clear_errors(dpu_kms); + + drm_for_each_encoder_mask(encoder, crtc->dev, + crtc->state->encoder_mask) { + dpu_encoder_kickoff(encoder); + dpu_encoder_start_frame_done_timer(encoder); + } + } if (atomic_inc_return(&dpu_crtc->frame_pending) == 1) { /* acquire bandwidth and other resources */ @@ -997,11 +1050,6 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) dpu_crtc->play_count++; - dpu_vbif_clear_errors(dpu_kms); - - drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) - dpu_encoder_kickoff(encoder); - reinit_completion(&dpu_crtc->frame_done_comp); end: @@ -1230,6 +1278,151 @@ done: return ret; } +#define MAX_CHANNELS_PER_CRTC 2 +#define MAX_HDISPLAY_SPLIT 1080 + +static struct msm_display_topology dpu_crtc_get_topology( + struct drm_crtc *crtc, + struct dpu_kms *dpu_kms, + struct drm_crtc_state *crtc_state) +{ + struct drm_display_mode *mode = &crtc_state->adjusted_mode; + struct msm_display_topology topology = {0}; + struct drm_encoder *drm_enc; + + drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) + dpu_encoder_update_topology(drm_enc, &topology, crtc_state->state, + &crtc_state->adjusted_mode); + + topology.cwb_enabled = drm_crtc_in_clone_mode(crtc_state); + + /* + * Datapath topology selection + * + * Dual display + * 2 LM, 2 INTF ( Split display using 2 interfaces) + * + * Single display + * 1 LM, 1 INTF + * 2 LM, 1 INTF (stream merge to support high resolution interfaces) + * + * If DSC is enabled, use 2 LMs for 2:2:1 topology + * + * Add dspps to the reservation requirements if ctm is requested + * + * Only hardcode num_lm to 2 for cases where num_intf == 2 and CWB is not + * enabled. This is because in cases where CWB is enabled, num_intf will + * count both the WB and real-time phys encoders. + * + * For non-DSC CWB usecases, have the num_lm be decided by the + * (mode->hdisplay > MAX_HDISPLAY_SPLIT) check. + */ + + if (topology.num_intf == 2 && !topology.cwb_enabled) + topology.num_lm = 2; + else if (topology.num_dsc == 2) + topology.num_lm = 2; + else if (dpu_kms->catalog->caps->has_3d_merge) + topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1; + else + topology.num_lm = 1; + + if (crtc_state->ctm) + topology.num_dspp = topology.num_lm; + + return topology; +} + +static int dpu_crtc_assign_resources(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + struct dpu_hw_blk *hw_ctl[MAX_CHANNELS_PER_CRTC]; + struct dpu_hw_blk *hw_lm[MAX_CHANNELS_PER_CRTC]; + struct dpu_hw_blk *hw_dspp[MAX_CHANNELS_PER_CRTC]; + int i, num_lm, num_ctl, num_dspp; + struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); + struct dpu_global_state *global_state; + struct dpu_crtc_state *cstate; + struct msm_display_topology topology; + int ret; + + /* + * Release and Allocate resources on every modeset + */ + global_state = dpu_kms_get_global_state(crtc_state->state); + if (IS_ERR(global_state)) + return PTR_ERR(global_state); + + dpu_rm_release(global_state, crtc); + + if (!crtc_state->enable) + return 0; + + topology = dpu_crtc_get_topology(crtc, dpu_kms, crtc_state); + ret = dpu_rm_reserve(&dpu_kms->rm, global_state, + crtc_state->crtc, &topology); + if (ret) + return ret; + + cstate = to_dpu_crtc_state(crtc_state); + + num_ctl = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, + crtc_state->crtc, + DPU_HW_BLK_CTL, hw_ctl, + ARRAY_SIZE(hw_ctl)); + num_lm = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, + crtc_state->crtc, + DPU_HW_BLK_LM, hw_lm, + ARRAY_SIZE(hw_lm)); + num_dspp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, + crtc_state->crtc, + DPU_HW_BLK_DSPP, hw_dspp, + ARRAY_SIZE(hw_dspp)); + + for (i = 0; i < num_lm; i++) { + int ctl_idx = (i < num_ctl) ? i : (num_ctl-1); + + cstate->mixers[i].hw_lm = to_dpu_hw_mixer(hw_lm[i]); + cstate->mixers[i].lm_ctl = to_dpu_hw_ctl(hw_ctl[ctl_idx]); + if (i < num_dspp) + cstate->mixers[i].hw_dspp = to_dpu_hw_dspp(hw_dspp[i]); + } + + cstate->num_mixers = num_lm; + + return 0; +} + +/** + * dpu_crtc_check_mode_changed: check if full modeset is required + * @old_crtc_state: Previous CRTC state + * @new_crtc_state: Corresponding CRTC state to be checked + * + * Check if the changes in the object properties demand full mode set. + */ +int dpu_crtc_check_mode_changed(struct drm_crtc_state *old_crtc_state, + struct drm_crtc_state *new_crtc_state) +{ + struct drm_encoder *drm_enc; + struct drm_crtc *crtc = new_crtc_state->crtc; + bool clone_mode_enabled = drm_crtc_in_clone_mode(old_crtc_state); + bool clone_mode_requested = drm_crtc_in_clone_mode(new_crtc_state); + + DRM_DEBUG_ATOMIC("%d\n", crtc->base.id); + + /* there might be cases where encoder needs a modeset too */ + drm_for_each_encoder_mask(drm_enc, crtc->dev, new_crtc_state->encoder_mask) { + if (dpu_encoder_needs_modeset(drm_enc, new_crtc_state->state)) + new_crtc_state->mode_changed = true; + } + + if ((clone_mode_requested && !clone_mode_enabled) || + (!clone_mode_requested && clone_mode_enabled)) + new_crtc_state->mode_changed = true; + + return 0; +} + static int dpu_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) { @@ -1245,6 +1438,13 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc, bool needs_dirtyfb = dpu_crtc_needs_dirtyfb(crtc_state); + /* don't reallocate resources if only ACTIVE has beeen changed */ + if (crtc_state->mode_changed || crtc_state->connectors_changed) { + rc = dpu_crtc_assign_resources(crtc, crtc_state); + if (rc < 0) + return rc; + } + if (dpu_use_virtual_planes && (crtc_state->planes_changed || crtc_state->zpos_changed)) { rc = dpu_crtc_reassign_planes(crtc, crtc_state); @@ -1262,10 +1462,6 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc, DRM_DEBUG_ATOMIC("%s: check\n", dpu_crtc->name); - /* force a full mode set if active state changed */ - if (crtc_state->active_changed) - crtc_state->mode_changed = true; - if (cstate->num_mixers) { rc = _dpu_crtc_check_and_setup_lm_bounds(crtc, crtc_state); if (rc) @@ -1484,8 +1680,9 @@ static int dpu_crtc_debugfs_state_show(struct seq_file *s, void *v) seq_printf(s, "intf_mode: %d\n", dpu_crtc_get_intf_mode(crtc)); seq_printf(s, "core_clk_rate: %llu\n", dpu_crtc->cur_perf.core_clk_rate); - seq_printf(s, "bw_ctl: %llu\n", dpu_crtc->cur_perf.bw_ctl); - seq_printf(s, "max_per_pipe_ib: %llu\n", + seq_printf(s, "bw_ctl: %uk\n", + (u32)DIV_ROUND_UP_ULL(dpu_crtc->cur_perf.bw_ctl, 1000)); + seq_printf(s, "max_per_pipe_ib: %u\n", dpu_crtc->cur_perf.max_per_pipe_ib); return 0; |