diff --git a/msm/sde/sde_encoder_phys_wb.c b/msm/sde/sde_encoder_phys_wb.c index a85619143a..f00cfe24be 100644 --- a/msm/sde/sde_encoder_phys_wb.c +++ b/msm/sde/sde_encoder_phys_wb.c @@ -1200,6 +1200,25 @@ static int sde_encoder_phys_wb_frame_timeout(struct sde_encoder_phys *phys_enc) return event; } +static bool _sde_encoder_phys_wb_is_idle( + struct sde_encoder_phys *phys_enc) +{ + bool ret = false; + struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc); + struct sde_hw_wb *hw_wb = wb_enc->hw_wb; + struct sde_vbif_get_xin_status_params xin_status = {0}; + + xin_status.vbif_idx = hw_wb->caps->vbif_idx; + xin_status.xin_id = hw_wb->caps->xin_id; + xin_status.clk_ctrl = hw_wb->caps->clk_ctrl; + if (sde_vbif_get_xin_status(phys_enc->sde_kms, &xin_status)) { + _sde_encoder_phys_wb_frame_done_helper(wb_enc, false); + ret = true; + } + + return ret; +} + static int _sde_encoder_phys_wb_wait_for_commit_done( struct sde_encoder_phys *phys_enc, bool is_disable) { @@ -1235,7 +1254,9 @@ static int _sde_encoder_phys_wb_wait_for_commit_done( KICKOFF_TIMEOUT_MS); rc = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_WB_DONE, &wait_info); - if (rc == -ETIMEDOUT) { + if (rc == -ETIMEDOUT && _sde_encoder_phys_wb_is_idle(phys_enc)) { + rc = 0; + } else if (rc == -ETIMEDOUT) { SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), wb_enc->frame_count, SDE_EVTLOG_ERROR); SDE_ERROR("wb:%d kickoff timed out\n", WBID(wb_enc)); diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index 711bcd7c66..7f00f6381c 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -441,6 +441,7 @@ enum { WB_ID, WB_XIN_ID, WB_CLK_CTRL, + WB_CLK_STATUS, WB_PROP_MAX, }; @@ -832,6 +833,8 @@ static struct sde_prop_type wb_prop[] = { {WB_XIN_ID, "qcom,sde-wb-xin-id", false, PROP_TYPE_U32_ARRAY}, {WB_CLK_CTRL, "qcom,sde-wb-clk-ctrl", false, PROP_TYPE_BIT_OFFSET_ARRAY}, + {WB_CLK_STATUS, "qcom,sde-wb-clk-status", false, + PROP_TYPE_BIT_OFFSET_ARRAY}, }; static struct sde_prop_type vbif_prop[] = { @@ -881,7 +884,7 @@ static struct sde_prop_type reg_dma_prop[REG_DMA_PROP_MAX] = { "qcom,sde-reg-dma-broadcast-disabled", false, PROP_TYPE_BOOL}, [REG_DMA_XIN_ID] = {REG_DMA_XIN_ID, "qcom,sde-reg-dma-xin-id", false, PROP_TYPE_U32}, - [REG_DMA_CLK_CTRL] = {REG_DMA_XIN_ID, + [REG_DMA_CLK_CTRL] = {REG_DMA_CLK_CTRL, "qcom,sde-reg-dma-clk-ctrl", false, PROP_TYPE_BIT_OFFSET_ARRAY}, }; @@ -1842,6 +1845,12 @@ static int _sde_sspp_setup_cmn(struct device_node *np, sde_cfg->mdp[j].clk_ctrls[sspp->clk_ctrl].bit_off = PROP_BITVALUE_ACCESS(props->values, SSPP_CLK_CTRL, i, 1); + sde_cfg->mdp[j].clk_status[sspp->clk_ctrl].reg_off = + PROP_BITVALUE_ACCESS(props->values, + SSPP_CLK_STATUS, i, 0); + sde_cfg->mdp[j].clk_status[sspp->clk_ctrl].bit_off = + PROP_BITVALUE_ACCESS(props->values, + SSPP_CLK_STATUS, i, 1); } SDE_DEBUG("xin:%d ram:%d clk%d:%x/%d\n", @@ -2354,6 +2363,12 @@ static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) sde_cfg->mdp[j].clk_ctrls[wb->clk_ctrl].bit_off = PROP_BITVALUE_ACCESS(prop_value, WB_CLK_CTRL, i, 1); + sde_cfg->mdp[j].clk_status[wb->clk_ctrl].reg_off = + PROP_BITVALUE_ACCESS(prop_value, + WB_CLK_STATUS, i, 0); + sde_cfg->mdp[j].clk_status[wb->clk_ctrl].bit_off = + PROP_BITVALUE_ACCESS(prop_value, + WB_CLK_STATUS, i, 1); } wb->format_list = sde_cfg->wb_formats; diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 6246699f9e..b55158af40 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -868,6 +868,7 @@ struct sde_clk_ctrl_reg { * @has_dest_scaler: indicates support of destination scaler * @smart_panel_align_mode: split display smart panel align modes * @clk_ctrls clock control register definition + * @clk_status clock status register definition */ struct sde_mdp_cfg { SDE_HW_BLK_INFO; @@ -877,6 +878,7 @@ struct sde_mdp_cfg { bool has_dest_scaler; u32 smart_panel_align_mode; struct sde_clk_ctrl_reg clk_ctrls[SDE_CLK_CTRL_MAX]; + struct sde_clk_ctrl_reg clk_status[SDE_CLK_CTRL_MAX]; }; /* struct sde_uidle_cfg : MDP TOP-BLK instance info diff --git a/msm/sde/sde_hw_top.c b/msm/sde/sde_hw_top.c index a1a610efad..975b1c5cab 100644 --- a/msm/sde/sde_hw_top.c +++ b/msm/sde/sde_hw_top.c @@ -219,6 +219,27 @@ static bool sde_hw_setup_clk_force_ctrl(struct sde_hw_mdp *mdp, return clk_forced_on; } +static int sde_hw_get_clk_ctrl_status(struct sde_hw_mdp *mdp, + enum sde_clk_ctrl_type clk_ctrl, bool *status) +{ + struct sde_hw_blk_reg_map *c; + u32 reg_off, bit_off; + + if (!mdp) + return -EINVAL; + + c = &mdp->hw; + + if (clk_ctrl <= SDE_CLK_CTRL_NONE || clk_ctrl >= SDE_CLK_CTRL_MAX || + !mdp->caps->clk_status[clk_ctrl].reg_off) + return -EINVAL; + + reg_off = mdp->caps->clk_status[clk_ctrl].reg_off; + bit_off = mdp->caps->clk_status[clk_ctrl].bit_off; + + *status = SDE_REG_READ(c, reg_off) & BIT(bit_off); + return 0; +} static void sde_hw_get_danger_status(struct sde_hw_mdp *mdp, struct sde_danger_safe_status *status) @@ -600,6 +621,7 @@ static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops, ops->setup_pp_split = sde_hw_setup_pp_split; ops->setup_cdm_output = sde_hw_setup_cdm_output; ops->setup_clk_force_ctrl = sde_hw_setup_clk_force_ctrl; + ops->get_clk_ctrl_status = sde_hw_get_clk_ctrl_status; ops->get_danger_status = sde_hw_get_danger_status; ops->setup_vsync_source = sde_hw_setup_vsync_source; ops->set_cwb_ppb_cntl = sde_hw_program_cwb_ppb_ctrl; diff --git a/msm/sde/sde_hw_top.h b/msm/sde/sde_hw_top.h index 4de27e8263..e27d366666 100644 --- a/msm/sde/sde_hw_top.h +++ b/msm/sde/sde_hw_top.h @@ -140,6 +140,16 @@ struct sde_hw_mdp_ops { bool (*setup_clk_force_ctrl)(struct sde_hw_mdp *mdp, enum sde_clk_ctrl_type clk_ctrl, bool enable); + /** + * get_clk_ctrl_status - get clock control status + * @mdp: mdp top context driver + * @clk_ctrl: clock to be controlled + * @status: returns true if clock is on + * @return: 0 if success, otherwise return code + */ + int (*get_clk_ctrl_status)(struct sde_hw_mdp *mdp, + enum sde_clk_ctrl_type clk_ctrl, bool *status); + /** * setup_dce - set DCE mux for DSC ctrl path * @mdp: mdp top context driver diff --git a/msm/sde/sde_vbif.c b/msm/sde/sde_vbif.c index daea2eb463..49c74a5ff7 100644 --- a/msm/sde/sde_vbif.c +++ b/msm/sde/sde_vbif.c @@ -406,6 +406,58 @@ bool sde_vbif_set_xin_halt(struct sde_kms *sde_kms, return forced_on; } +bool sde_vbif_get_xin_status(struct sde_kms *sde_kms, + struct sde_vbif_get_xin_status_params *params) +{ + struct sde_hw_vbif *vbif = NULL; + struct sde_hw_mdp *mdp; + int i, rc; + bool status; + + if (!sde_kms || !params) { + SDE_ERROR("invalid arguments\n"); + return false; + } + + if (!sde_kms_is_vbif_operation_allowed(sde_kms)) { + SDE_DEBUG("vbif operations not permitted\n"); + return true; + } + + mdp = sde_kms->hw_mdp; + + for (i = 0; i < ARRAY_SIZE(sde_kms->hw_vbif); i++) { + if (sde_kms->hw_vbif[i] && + sde_kms->hw_vbif[i]->idx == params->vbif_idx) { + vbif = sde_kms->hw_vbif[i]; + break; + } + } + + if (!vbif || !mdp) { + SDE_DEBUG("invalid arguments vbif:%d mdp:%d vbif idx:%d\n", + vbif != NULL, mdp != NULL, params->vbif_idx); + return false; + } + + if (!mdp->ops.get_clk_ctrl_status || + !vbif->ops.get_xin_halt_status) + return false; + + mutex_lock(&vbif->mutex); + SDE_EVT32_VERBOSE(vbif->idx, params->xin_id); + status = vbif->ops.get_xin_halt_status(vbif, params->xin_id); + if (status) { + rc = !mdp->ops.get_clk_ctrl_status(mdp, params->clk_ctrl, + &status); + if (rc) + status = false; + } + mutex_unlock(&vbif->mutex); + + return status; +} + void sde_vbif_set_qos_remap(struct sde_kms *sde_kms, struct sde_vbif_set_qos_params *params) { diff --git a/msm/sde/sde_vbif.h b/msm/sde/sde_vbif.h index ba21c0a5e9..dc6c74c086 100644 --- a/msm/sde/sde_vbif.h +++ b/msm/sde/sde_vbif.h @@ -44,6 +44,18 @@ struct sde_vbif_set_xin_halt_params { bool enable; }; +/** + * struct sde_vbif_get_xin_status_params - xin halt parameters + * @vbif_idx: vbif identifier + * @xin_id: client interface identifier + * @clk_ctrl: clock control identifier of the xin + */ +struct sde_vbif_get_xin_status_params { + u32 vbif_idx; + u32 xin_id; + u32 clk_ctrl; +}; + /** * struct sde_vbif_set_qos_params - QoS remapper parameter * @vbif_idx: vbif identifier @@ -78,6 +90,16 @@ void sde_vbif_set_ot_limit(struct sde_kms *sde_kms, bool sde_vbif_set_xin_halt(struct sde_kms *sde_kms, struct sde_vbif_set_xin_halt_params *params); +/** + * sde_vbif_get_xin_status - halt one of the xin ports + * This function isn't thread safe. + * @sde_kms: SDE handler + * @params: Pointer to xin status parameters + * Returns: true if xin client is idle, false otherwise + */ +bool sde_vbif_get_xin_status(struct sde_kms *sde_kms, + struct sde_vbif_get_xin_status_params *params); + /** * sde_vbif_set_qos_remap - set QoS priority level remap * @sde_kms: SDE handler