Loading drivers/gpu/drm/msm/sde/sde_encoder.c +75 −15 Original line number Diff line number Diff line Loading @@ -177,6 +177,7 @@ enum sde_enc_rc_states { * @hw_pp Handle to the pingpong blocks used for the display. No. * pingpong blocks can be different than num_phys_encs. * @hw_dsc: Array of DSC block handles used for the display. * @dirty_dsc_ids: Cached dsc indexes for dirty DSC blocks needing flush * @intfs_swapped Whether or not the phys_enc interfaces have been swapped * for partial update right-only cases, such as pingpong * split where virtual pingpong does not generate IRQs Loading Loading @@ -236,6 +237,7 @@ struct sde_encoder_virt { struct sde_encoder_phys *cur_master; struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; enum sde_dsc dirty_dsc_ids[MAX_CHANNELS_PER_ENC]; bool intfs_swapped; Loading Loading @@ -1188,8 +1190,20 @@ static void _sde_encoder_dsc_pipe_cfg(struct sde_hw_dsc *hw_dsc, u32 common_mode, bool ich_reset, bool enable) { if (!enable) { if (hw_pp->ops.disable_dsc) if (hw_pp && hw_pp->ops.disable_dsc) hw_pp->ops.disable_dsc(hw_pp); if (hw_dsc && hw_dsc->ops.dsc_disable) hw_dsc->ops.dsc_disable(hw_dsc); if (hw_dsc && hw_dsc->ops.bind_pingpong_blk) hw_dsc->ops.bind_pingpong_blk(hw_dsc, false, PINGPONG_MAX); return; } if (!dsc || !hw_dsc || !hw_pp) { SDE_ERROR("invalid params %d %d %d\n", !dsc, !hw_dsc, !hw_pp); return; } Loading Loading @@ -1279,10 +1293,6 @@ static int _sde_encoder_dsc_n_lm_1_enc_1_intf(struct sde_encoder_virt *sde_enc) _sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, dsc, dsc_common_mode, ich_res, true); if (cfg.dsc_count >= MAX_DSC_PER_CTL_V1) { pr_err("Invalid dsc count:%d\n", cfg.dsc_count); return -EINVAL; } cfg.dsc[cfg.dsc_count++] = hw_dsc->idx; /* setup dsc active configuration in the control path */ Loading Loading @@ -1402,8 +1412,7 @@ static int _sde_encoder_dsc_2_lm_2_enc_2_intf(struct sde_encoder_virt *sde_enc, cfg.dsc_count); return -EINVAL; } cfg.dsc[i] = hw_dsc[i]->idx; cfg.dsc_count++; cfg.dsc[cfg.dsc_count++] = hw_dsc[i]->idx; if (hw_ctl->ops.update_bitmask_dsc) hw_ctl->ops.update_bitmask_dsc(hw_ctl, Loading Loading @@ -1713,32 +1722,47 @@ static void _sde_encoder_update_vsync_source(struct sde_encoder_virt *sde_enc, } } static int _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc) static void _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc) { int i, ret = 0; int i; struct sde_hw_pingpong *hw_pp = NULL; struct sde_hw_dsc *hw_dsc = NULL; struct sde_hw_ctl *hw_ctl = NULL; struct sde_ctl_dsc_cfg cfg; if (!sde_enc || !sde_enc->phys_encs[0] || !sde_enc->phys_encs[0]->connector) { SDE_ERROR("invalid params %d %d\n", !sde_enc, sde_enc ? !sde_enc->phys_encs[0] : -1); return -EINVAL; return; } if (sde_enc->cur_master) hw_ctl = sde_enc->cur_master->hw_ctl; /* Disable DSC for all the pp's present in this topology */ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { hw_pp = sde_enc->hw_pp[i]; hw_dsc = sde_enc->hw_dsc[i]; if (hw_pp && hw_pp->ops.disable_dsc) hw_pp->ops.disable_dsc(hw_pp); _sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, NULL, 0, 0, 0); if (hw_dsc && hw_dsc->ops.dsc_disable) hw_dsc->ops.dsc_disable(hw_dsc); if (hw_dsc) sde_enc->dirty_dsc_ids[i] = hw_dsc->idx; } return ret; /* Clear the DSC ACTIVE config for this CTL */ if (hw_ctl && hw_ctl->ops.setup_dsc_cfg) { memset(&cfg, 0, sizeof(cfg)); hw_ctl->ops.setup_dsc_cfg(hw_ctl, &cfg); } /** * Since pending flushes from previous commit get cleared * sometime after this point, setting DSC flush bits now * will have no effect. Therefore dirty_dsc_ids track which * DSC blocks must be flushed for the next trigger. */ } static int _sde_encoder_switch_to_watchdog_vsync(struct drm_encoder *drm_enc) Loading Loading @@ -4335,6 +4359,40 @@ static int _helper_flush_qsync(struct sde_encoder_phys *phys_enc) return 0; } static bool _sde_encoder_dsc_is_dirty(struct sde_encoder_virt *sde_enc) { int i; for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { /** * This dirty_dsc_hw field is set during DSC disable to * indicate which DSC blocks need to be flushed */ if (sde_enc->dirty_dsc_ids[i]) return true; } return false; } static void _helper_flush_dsc(struct sde_encoder_virt *sde_enc) { int i; struct sde_hw_ctl *hw_ctl = NULL; enum sde_dsc dsc_idx; if (sde_enc->cur_master) hw_ctl = sde_enc->cur_master->hw_ctl; for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { dsc_idx = sde_enc->dirty_dsc_ids[i]; if (dsc_idx && hw_ctl && hw_ctl->ops.update_bitmask_dsc) hw_ctl->ops.update_bitmask_dsc(hw_ctl, dsc_idx, 1); sde_enc->dirty_dsc_ids[i] = DSC_NONE; } } int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, struct sde_encoder_kickoff_params *params) { Loading Loading @@ -4444,6 +4502,8 @@ int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, SDE_ERROR_ENC(sde_enc, "failed to setup DSC: %d\n", rc); ret = rc; } } else if (_sde_encoder_dsc_is_dirty(sde_enc)) { _helper_flush_dsc(sde_enc); } end: Loading Loading
drivers/gpu/drm/msm/sde/sde_encoder.c +75 −15 Original line number Diff line number Diff line Loading @@ -177,6 +177,7 @@ enum sde_enc_rc_states { * @hw_pp Handle to the pingpong blocks used for the display. No. * pingpong blocks can be different than num_phys_encs. * @hw_dsc: Array of DSC block handles used for the display. * @dirty_dsc_ids: Cached dsc indexes for dirty DSC blocks needing flush * @intfs_swapped Whether or not the phys_enc interfaces have been swapped * for partial update right-only cases, such as pingpong * split where virtual pingpong does not generate IRQs Loading Loading @@ -236,6 +237,7 @@ struct sde_encoder_virt { struct sde_encoder_phys *cur_master; struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; enum sde_dsc dirty_dsc_ids[MAX_CHANNELS_PER_ENC]; bool intfs_swapped; Loading Loading @@ -1188,8 +1190,20 @@ static void _sde_encoder_dsc_pipe_cfg(struct sde_hw_dsc *hw_dsc, u32 common_mode, bool ich_reset, bool enable) { if (!enable) { if (hw_pp->ops.disable_dsc) if (hw_pp && hw_pp->ops.disable_dsc) hw_pp->ops.disable_dsc(hw_pp); if (hw_dsc && hw_dsc->ops.dsc_disable) hw_dsc->ops.dsc_disable(hw_dsc); if (hw_dsc && hw_dsc->ops.bind_pingpong_blk) hw_dsc->ops.bind_pingpong_blk(hw_dsc, false, PINGPONG_MAX); return; } if (!dsc || !hw_dsc || !hw_pp) { SDE_ERROR("invalid params %d %d %d\n", !dsc, !hw_dsc, !hw_pp); return; } Loading Loading @@ -1279,10 +1293,6 @@ static int _sde_encoder_dsc_n_lm_1_enc_1_intf(struct sde_encoder_virt *sde_enc) _sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, dsc, dsc_common_mode, ich_res, true); if (cfg.dsc_count >= MAX_DSC_PER_CTL_V1) { pr_err("Invalid dsc count:%d\n", cfg.dsc_count); return -EINVAL; } cfg.dsc[cfg.dsc_count++] = hw_dsc->idx; /* setup dsc active configuration in the control path */ Loading Loading @@ -1402,8 +1412,7 @@ static int _sde_encoder_dsc_2_lm_2_enc_2_intf(struct sde_encoder_virt *sde_enc, cfg.dsc_count); return -EINVAL; } cfg.dsc[i] = hw_dsc[i]->idx; cfg.dsc_count++; cfg.dsc[cfg.dsc_count++] = hw_dsc[i]->idx; if (hw_ctl->ops.update_bitmask_dsc) hw_ctl->ops.update_bitmask_dsc(hw_ctl, Loading Loading @@ -1713,32 +1722,47 @@ static void _sde_encoder_update_vsync_source(struct sde_encoder_virt *sde_enc, } } static int _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc) static void _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc) { int i, ret = 0; int i; struct sde_hw_pingpong *hw_pp = NULL; struct sde_hw_dsc *hw_dsc = NULL; struct sde_hw_ctl *hw_ctl = NULL; struct sde_ctl_dsc_cfg cfg; if (!sde_enc || !sde_enc->phys_encs[0] || !sde_enc->phys_encs[0]->connector) { SDE_ERROR("invalid params %d %d\n", !sde_enc, sde_enc ? !sde_enc->phys_encs[0] : -1); return -EINVAL; return; } if (sde_enc->cur_master) hw_ctl = sde_enc->cur_master->hw_ctl; /* Disable DSC for all the pp's present in this topology */ for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { hw_pp = sde_enc->hw_pp[i]; hw_dsc = sde_enc->hw_dsc[i]; if (hw_pp && hw_pp->ops.disable_dsc) hw_pp->ops.disable_dsc(hw_pp); _sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, NULL, 0, 0, 0); if (hw_dsc && hw_dsc->ops.dsc_disable) hw_dsc->ops.dsc_disable(hw_dsc); if (hw_dsc) sde_enc->dirty_dsc_ids[i] = hw_dsc->idx; } return ret; /* Clear the DSC ACTIVE config for this CTL */ if (hw_ctl && hw_ctl->ops.setup_dsc_cfg) { memset(&cfg, 0, sizeof(cfg)); hw_ctl->ops.setup_dsc_cfg(hw_ctl, &cfg); } /** * Since pending flushes from previous commit get cleared * sometime after this point, setting DSC flush bits now * will have no effect. Therefore dirty_dsc_ids track which * DSC blocks must be flushed for the next trigger. */ } static int _sde_encoder_switch_to_watchdog_vsync(struct drm_encoder *drm_enc) Loading Loading @@ -4335,6 +4359,40 @@ static int _helper_flush_qsync(struct sde_encoder_phys *phys_enc) return 0; } static bool _sde_encoder_dsc_is_dirty(struct sde_encoder_virt *sde_enc) { int i; for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { /** * This dirty_dsc_hw field is set during DSC disable to * indicate which DSC blocks need to be flushed */ if (sde_enc->dirty_dsc_ids[i]) return true; } return false; } static void _helper_flush_dsc(struct sde_encoder_virt *sde_enc) { int i; struct sde_hw_ctl *hw_ctl = NULL; enum sde_dsc dsc_idx; if (sde_enc->cur_master) hw_ctl = sde_enc->cur_master->hw_ctl; for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { dsc_idx = sde_enc->dirty_dsc_ids[i]; if (dsc_idx && hw_ctl && hw_ctl->ops.update_bitmask_dsc) hw_ctl->ops.update_bitmask_dsc(hw_ctl, dsc_idx, 1); sde_enc->dirty_dsc_ids[i] = DSC_NONE; } } int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, struct sde_encoder_kickoff_params *params) { Loading Loading @@ -4444,6 +4502,8 @@ int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, SDE_ERROR_ENC(sde_enc, "failed to setup DSC: %d\n", rc); ret = rc; } } else if (_sde_encoder_dsc_is_dirty(sde_enc)) { _helper_flush_dsc(sde_enc); } end: Loading