diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 8ac63aaaefce..75cc5e207e4d 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -109,6 +109,9 @@ * QSEED3 parameters needs to be updated. * @DS_ENHANCER_UPDATE: Setting this bit indicates current Desitnation Scaler * QSEED3 Detial enhancer parameters need to be updated. + * @DS_VALIDATE: Indicate destination data structure parameters are validated + * and can be used for programming the HW and perform a flush. + * @DS_DIRTY_UPDATE: Mark for dirty update for Power resume usecase. */ #define DS_ENABLE BIT(0) #define DS_DUAL_MODE BIT(1) @@ -116,6 +119,8 @@ #define DS_RIGHT BIT(3) #define DS_SCALE_UPDATE BIT(4) #define DS_ENHANCER_UPDATE BIT(5) +#define DS_VALIDATE BIT(6) +#define DS_DIRTY_UPDATE BIT(7) /** * Destination Scaler DUAL mode overfetch pixel count @@ -370,6 +375,8 @@ struct mdss_mdp_destination_scaler { char __iomem *lut_base; u16 src_width; u16 src_height; + u16 last_mixer_width; + u16 last_mixer_height; u32 flags; struct mdp_scale_data_v2 scaler; }; @@ -1706,6 +1713,7 @@ void mdss_mdp_pp_term(struct device *dev); int mdss_mdp_pp_overlay_init(struct msm_fb_data_type *mfd); int mdss_mdp_pp_resume(struct msm_fb_data_type *mfd); +void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl); int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl); int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index eb1e0b5c47a6..17255ed385fe 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -3508,7 +3508,9 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) if (is_dest_scaling_enable(ctl->mixer_left)) { width = get_ds_input_width(ctl->mixer_left); height = get_ds_input_height(ctl->mixer_left); - if (ctl->panel_data->next && is_pingpong_split(ctl->mfd)) + if (is_dual_lm_single_display(ctl->mfd) || + (ctl->panel_data->next && + is_pingpong_split(ctl->mfd))) width *= 2; } else { width = get_panel_width(ctl); @@ -3548,9 +3550,13 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) } if (split_fb) { - width = ctl->mfd->split_fb_left; - width += (pinfo->lcdc.border_left + - pinfo->lcdc.border_right); + if (is_dest_scaling_enable(ctl->mixer_left)) { + width = get_ds_input_width(ctl->mixer_left); + } else { + width = ctl->mfd->split_fb_left; + width += (pinfo->lcdc.border_left + + pinfo->lcdc.border_right); + } } else if (width > max_mixer_width) { width /= 2; } @@ -3576,8 +3582,12 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) return 0; } - if (split_fb) - width = ctl->mfd->split_fb_right; + if (split_fb) { + if (is_dest_scaling_enable(ctl->mixer_left)) + width = get_ds_input_width(ctl->mixer_left); + else + width = ctl->mfd->split_fb_right; + } if (width < ctl->width) { if (ctl->mixer_right == NULL) { @@ -4038,6 +4048,7 @@ static void mdss_mdp_ctl_restore_sub(struct mdss_mdp_ctl *ctl) if (ctl->mfd && ctl->panel_data) { ctl->mfd->ipc_resume = true; mdss_mdp_pp_resume(ctl->mfd); + mdss_mdp_pp_dest_scaler_resume(ctl); if (is_dsc_compression(&ctl->panel_data->panel_info)) { /* diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index e26b3843d7b0..353d07ad64ac 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -67,13 +67,89 @@ static inline void *u64_to_ptr(uint64_t address) return (void *)(uintptr_t)address; } +static void mdss_mdp_disable_destination_scaler_setup(struct mdss_mdp_ctl *ctl) +{ + struct mdss_data_type *mdata = ctl->mdata; + struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info; + + if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map)) { + if (ctl->mixer_left && ctl->mixer_right && + ctl->mixer_left->ds && ctl->mixer_right->ds && + ctl->mixer_left->ds->scaler.enable && + ctl->mixer_right->ds->scaler.enable) { + /* + * DUAL mode disable + */ + ctl->mixer_left->width = get_panel_width(ctl); + ctl->mixer_left->height = get_panel_yres(pinfo); + ctl->mixer_left->width /= 2; + ctl->mixer_right->width = ctl->mixer_left->width; + ctl->mixer_right->height = ctl->mixer_left->height; + ctl->mixer_left->roi = (struct mdss_rect) { 0, 0, + ctl->mixer_left->width, + ctl->mixer_left->height }; + ctl->mixer_right->roi = (struct mdss_rect) { 0, 0, + ctl->mixer_right->width, + ctl->mixer_right->height }; + + /* + * Disable destination scaler by resetting the control + * flags and also need to disable in the QSEED3 + * settings. + */ + ctl->mixer_left->ds->flags = DS_SCALE_UPDATE | + DS_VALIDATE; + ctl->mixer_right->ds->flags = DS_SCALE_UPDATE | + DS_VALIDATE; + ctl->mixer_left->ds->scaler.enable = 0; + ctl->mixer_left->ds->scaler.detail_enhance.enable = 0; + ctl->mixer_right->ds->scaler.enable = 0; + ctl->mixer_right->ds->scaler.detail_enhance.enable = 0; + + pr_debug("DS-Left+Right disable: left:%dx%d, right:%dx%d\n", + ctl->mixer_left->width, + ctl->mixer_left->height, + ctl->mixer_right->width, + ctl->mixer_right->height); + MDSS_XLOG(ctl->mixer_left->width, + ctl->mixer_left->height, + ctl->mixer_right->width, + ctl->mixer_right->height); + } else if (ctl->mixer_left && ctl->mixer_left->ds && + ctl->mixer_left->ds->scaler.enable) { + /* + * Single DS disable + */ + ctl->mixer_left->width = get_panel_width(ctl); + ctl->mixer_left->height = get_panel_yres(pinfo); + ctl->mixer_left->roi = (struct mdss_rect) { 0, 0, + ctl->mixer_left->width, + ctl->mixer_left->height }; + + ctl->mixer_left->ds->flags = DS_SCALE_UPDATE | + DS_VALIDATE; + ctl->mixer_left->ds->scaler.enable = 0; + ctl->mixer_left->ds->scaler.detail_enhance.enable = 0; + + pr_debug("DS-left disable: wxh=%dx%d\n", + ctl->mixer_left->width, + ctl->mixer_left->height); + MDSS_XLOG(ctl->mixer_left->width, + ctl->mixer_left->height); + } + } +} + static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data, struct mdss_mdp_destination_scaler *ds, u32 max_input_width, u32 max_output_width) { struct mdp_scale_data_v2 *scale; - ds->flags = (ds_data->flags & MDP_DESTSCALER_ENABLE) ? DS_ENABLE : 0; + if (ds_data->flags & MDP_DESTSCALER_ENABLE) + ds->flags |= DS_ENABLE; + else + ds->flags &= ~DS_ENABLE; if (ds_data->flags & (MDP_DESTSCALER_SCALE_UPDATE | MDP_DESTSCALER_ENHANCER_UPDATE)) { @@ -101,8 +177,12 @@ static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data, ds->flags |= DS_SCALE_UPDATE; if (ds_data->flags & MDP_DESTSCALER_ENHANCER_UPDATE) ds->flags |= DS_ENHANCER_UPDATE; - ds->src_width = scale->src_width[0]; - ds->src_height = scale->src_height[0]; + + /* + * Update with LM resolution + */ + ds->src_width = ds_data->lm_width; + ds->src_height = ds_data->lm_height; } if (ds_data->flags == 0) { @@ -110,6 +190,11 @@ static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data, ds_data->dest_scaler_ndx); } + /* + * Confirm all check pass validation, and to be cleared in CTL after + * flush is issued. + */ + ds->flags |= DS_VALIDATE; return 0; } @@ -118,7 +203,7 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, { struct mdss_data_type *mdata; struct mdss_panel_info *pinfo; - + u16 mxleft_w = 0, mxleft_h = 0, mxright_w = 0, mxright_h = 0; mdata = ctl->mdata; /* @@ -134,7 +219,7 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, * height. */ pinfo = &ctl->panel_data->panel_info; - if ((ds_data->lm_width > get_panel_xres(pinfo)) || + if ((ds_data->lm_width > get_panel_width(ctl)) || (ds_data->lm_height > get_panel_yres(pinfo)) || (ds_data->lm_width == 0) || (ds_data->lm_height == 0)) { @@ -142,14 +227,8 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, ds_data->lm_width, ds_data->lm_height); return -EINVAL; } - - ctl->width = ds_data->lm_width; - ctl->height = ds_data->lm_height; - - ctl->mixer_left->width = ds_data->lm_width; - ctl->mixer_left->height = ds_data->lm_height; - pr_debug("Update mixer-left width/height: %dx%d\n", - ds_data->lm_width, ds_data->lm_width); + mxleft_w = ds_data->lm_width; + mxleft_h = ds_data->lm_height; } if (ctl->mixer_right && ctl->mixer_right->ds) { @@ -174,25 +253,51 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, * Split display both left and right should have the * same width and height */ - ctl->mixer_right->width = ds_data->lm_width; - ctl->mixer_right->height = ds_data->lm_height; - pr_debug("Update mixer-right width/height: %dx%d\n", - ds_data->lm_width, ds_data->lm_height); + mxright_w = ds_data->lm_width; + mxright_h = ds_data->lm_height; if (ctl->mixer_left && - ((ctl->mixer_right->width != - ctl->mixer_left->width) || - (ctl->mixer_right->height != - ctl->mixer_left->height))) { + ((mxright_w != mxleft_w) || + (mxright_h != mxleft_h))) { pr_err("Mismatch width/heigth in LM for split display\n"); return -EINVAL; } + } + + /* + * Update mixer and control dimension after successful + * pre-validation + */ + if (mxleft_w && mxleft_h) { + ctl->mixer_left->ds->last_mixer_width = + ctl->mixer_left->width; + ctl->mixer_left->ds->last_mixer_height = + ctl->mixer_left->height; + + ctl->width = mxleft_w; + ctl->height = mxleft_h; + ctl->mixer_left->width = mxleft_w; + ctl->mixer_left->height = mxleft_h; + pr_debug("Update mixer-left width/height: %dx%d\n", + mxleft_w, mxleft_h); + } + + if (mxright_w && mxright_h) { + ctl->mixer_right->ds->last_mixer_width = + ctl->mixer_right->width; + ctl->mixer_right->ds->last_mixer_height = + ctl->mixer_right->height; + + ctl->mixer_right->width = mxright_w; + ctl->mixer_right->height = mxright_h; + pr_debug("Update mixer-right width/height: %dx%d\n", + mxright_w, mxright_h); /* * For split display, CTL width should be equal to * whole panel size */ - ctl->width += ds_data->lm_width; + ctl->width += mxright_w; } pr_debug("Updated CTL width:%d, height:%d\n", @@ -236,19 +341,23 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd, MDSS_MDP_DS_OVERFETCH_SIZE, mdata->max_dest_scaler_output_width); if (ret) - return ret; + goto reset_mixer; ret = __dest_scaler_data_setup(&ds_data[1], ds_right, mdata->max_dest_scaler_input_width - MDSS_MDP_DS_OVERFETCH_SIZE, mdata->max_dest_scaler_output_width); if (ret) - return ret; + goto reset_mixer; ds_left->flags &= ~(DS_LEFT|DS_RIGHT); ds_left->flags |= DS_DUAL_MODE; ds_right->flags &= ~(DS_LEFT|DS_RIGHT); ds_right->flags |= DS_DUAL_MODE; + MDSS_XLOG(ds_left->num, ds_left->src_width, + ds_left->src_height, ds_left->flags, + ds_right->num, ds_right->src_width, + ds_right->src_height, ds_right->flags); break; case DS_LEFT: @@ -262,6 +371,11 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd, ret = __dest_scaler_data_setup(&ds_data[0], ds_left, mdata->max_dest_scaler_input_width, mdata->max_dest_scaler_output_width); + if (ret) + goto reset_mixer; + + MDSS_XLOG(ds_left->num, ds_left->src_width, + ds_left->src_height, ds_left->flags); break; case DS_RIGHT: @@ -276,6 +390,11 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd, ret = __dest_scaler_data_setup(&ds_data[0], ds_right, mdata->max_dest_scaler_input_width, mdata->max_dest_scaler_output_width); + if (ret) + goto reset_mixer; + + MDSS_XLOG(ds_right->num, ds_right->src_width, + ds_right->src_height, ds_right->flags); break; } @@ -312,6 +431,40 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd, pr_err("Invalid Dest-scaler output width/height: %d/%d\n", scaler_width, scaler_height); ret = -EINVAL; + goto reset_mixer; + } + + return ret; + +reset_mixer: + /* reverting mixer and control dimension */ + if (ctl->mixer_left && ctl->mixer_left->ds && + ctl->mixer_left->ds->last_mixer_width) { + ctl->width = ctl->mixer_left->ds->last_mixer_width; + ctl->height = ctl->mixer_left->ds->last_mixer_height; + ctl->mixer_left->width = + ctl->mixer_left->ds->last_mixer_width; + ctl->mixer_left->height = + ctl->mixer_left->ds->last_mixer_height; + if (ds_left) + ds_left->flags &= ~DS_ENABLE; + MDSS_XLOG(ctl->width, ctl->height, + ctl->mixer_left->width, + ctl->mixer_left->height); + } + + if (ctl->mixer_right && ctl->mixer_right->ds && + ctl->mixer_right->ds->last_mixer_width) { + ctl->width += ctl->mixer_right->ds->last_mixer_width; + ctl->mixer_right->width = + ctl->mixer_right->ds->last_mixer_width; + ctl->mixer_right->height = + ctl->mixer_right->ds->last_mixer_height; + if (ds_right) + ds_right->flags &= ~DS_ENABLE; + MDSS_XLOG(ctl->width, ctl->height, + ctl->mixer_right->width, + ctl->mixer_right->height); } return ret; @@ -1939,6 +2092,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd, enum layer_pipe_q pipe_q_type; enum layer_zorder_used zorder_used[MDSS_MDP_MAX_STAGE] = {0}; enum mdss_mdp_pipe_rect rect_num; + struct mdp_destination_scaler_data *ds_data; ret = mutex_lock_interruptible(&mdp5_data->ov_lock); if (ret) @@ -2194,11 +2348,10 @@ static int __validate_layers(struct msm_fb_data_type *mfd, layer->z_order -= MDSS_MDP_STAGE_0; } + ds_data = commit->dest_scaler; if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) && - commit->dest_scaler && + ds_data && (ds_data->flags & MDP_DESTSCALER_ENABLE) && commit->dest_scaler_cnt) { - struct mdp_destination_scaler_data *ds_data = - commit->dest_scaler; /* * Find out which DS block to use based on DS commit info @@ -2217,8 +2370,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd, } ret = mdss_mdp_validate_destination_scaler(mfd, - commit->dest_scaler, - ds_mode); + ds_data, ds_mode); if (ret) { pr_err("fail to validate destination scaler\n"); layer->error_code = ret; @@ -2472,6 +2624,7 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd, struct file *file, struct mdp_layer_commit_v1 *commit) { struct mdss_overlay_private *mdp5_data; + struct mdp_destination_scaler_data *ds_data; int rc = 0; if (!mfd || !commit) { @@ -2505,15 +2658,17 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd, } } - if (commit->dest_scaler && commit->dest_scaler_cnt) { + ds_data = commit->dest_scaler; + if (ds_data && commit->dest_scaler_cnt && + (ds_data->flags & MDP_DESTSCALER_ENABLE)) { rc = mdss_mdp_destination_scaler_pre_validate(mdp5_data->ctl, - commit->dest_scaler, - commit->dest_scaler_cnt); + ds_data, commit->dest_scaler_cnt); if (IS_ERR_VALUE(rc)) { pr_err("Destination scaler pre-validate failed\n"); return -EINVAL; } - } + } else + mdss_mdp_disable_destination_scaler_setup(mdp5_data->ctl); rc = mdss_mdp_avr_validate(mfd, commit); if (IS_ERR_VALUE(rc)) { diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index ee1cd8fd623e..47edc320233a 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -2480,6 +2480,28 @@ static int pp_dest_scaler_setup(struct mdss_mdp_mixer *mixer) if (!test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) || !ds) return 0; + /* + * Non-validated DS data will be related to PM event. It is required + * to send out last setup to match the mixer and panel configuration. + */ + if (!(ds->flags & DS_VALIDATE)) { + pr_debug("Apply old DS[%d] for non validate data\n", ds->num); + if (ds->flags & DS_ENABLE) + ds->flags |= (DS_SCALE_UPDATE | DS_ENHANCER_UPDATE); + ds->flags |= DS_VALIDATE; + } + + /* + * If mark for dirty update, force update to scaler and detail + * enhancer. + */ + if (ds->flags & DS_DIRTY_UPDATE) { + pr_debug("Scale dirty update requested\n"); + ds->flags |= (DS_SCALE_UPDATE | DS_ENHANCER_UPDATE | + DS_VALIDATE); + ds->flags &= ~DS_DIRTY_UPDATE; + } + ds_offset = ds->ds_base; op_mode = readl_relaxed(MDSS_MDP_REG_DEST_SCALER_OP_MODE + ds_offset); @@ -2519,12 +2541,37 @@ static int pp_dest_scaler_setup(struct mdss_mdp_mixer *mixer) } /* Destinations scaler shared the flush with DSPP in control */ - if (ds->flags & DS_ENABLE) + if (ds->flags & (DS_ENABLE | DS_VALIDATE)) { + pr_debug("FLUSH[%d]: flags:%X, op_mode:%x\n", + ds->num, ds->flags, op_mode); ctl->flush_bits |= BIT(13 + ds->num); + } + ds->flags &= ~DS_VALIDATE; return 0; } +void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl) +{ + if (!ctl || !ctl->mdata) { + pr_err("Invalid ctl\n"); + return; + } + + if (!test_bit(MDSS_CAPS_DEST_SCALER, ctl->mdata->mdss_caps_map)) + return; + + if (ctl->mixer_left && ctl->mixer_left->ds) { + ctl->mixer_left->ds->flags |= DS_DIRTY_UPDATE; + pr_debug("DS left mark dirty\n"); + } + + if (ctl->mixer_right && ctl->mixer_right->ds) { + ctl->mixer_right->ds->flags |= DS_DIRTY_UPDATE; + pr_debug("DS right mark dirty\n"); + } +} + int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl) { int ret = 0;