diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 09bbd4c9e605..d207387f482a 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -1952,9 +1952,14 @@ static int mdss_dsi_set_stream_size(struct mdss_panel_data *pdata) /* DSI_COMMAND_MODE_MDP_STREAM_CTRL */ if (dsc) { - stream_ctrl = ((dsc->bytes_in_slice + 1) << 16) | - (pdata->panel_info.mipi.vc << 8) | DTYPE_DCS_LWRITE; - stream_total = roi->h << 16 | dsc->pclk_per_line; + u16 byte_num = dsc->bytes_per_pkt; + + if (pinfo->mipi.insert_dcs_cmd) + byte_num++; + + stream_ctrl = (byte_num << 16) | (pinfo->mipi.vc << 8) | + DTYPE_DCS_LWRITE; + stream_total = dsc->pic_height << 16 | dsc->pclk_per_line; } else { stream_ctrl = (((roi->w * 3) + 1) << 16) | @@ -1987,6 +1992,9 @@ static int mdss_dsi_set_stream_size(struct mdss_panel_data *pdata) MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x194, idle); + if (dsc) + mdss_dsi_dsc_config(ctrl_pdata, dsc); + return 0; } @@ -2058,11 +2066,13 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; int power_state; u32 mode; + struct mdss_panel_info *pinfo; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return -EINVAL; } + pinfo = &pdata->panel_info; ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); pr_debug("%s+: ctrl=%d event=%d\n", __func__, ctrl_pdata->ndx, event); @@ -2135,6 +2145,10 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, rc = mdss_dsi_blank(pdata, MDSS_PANEL_POWER_OFF); } break; + case MDSS_EVENT_DSC_PPS_SEND: + if (pinfo->compression_mode == COMPRESSION_DSC) + mdss_dsi_panel_dsc_pps_send(ctrl_pdata, pinfo); + break; case MDSS_EVENT_ENABLE_PARTIAL_ROI: rc = mdss_dsi_ctl_partial_roi(pdata); break; diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 0f5a3fae8fae..f96c3b584c94 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -633,8 +633,11 @@ int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing, int mdss_dsi_register_recovery_handler(struct mdss_dsi_ctrl_pdata *ctrl, struct mdss_intf_recovery *recovery); -void mdss_dsi_panel_dsc_pps_send(struct mdss_dsi_ctrl_pdata *ctrl); void mdss_dsi_unregister_bl_settings(struct mdss_dsi_ctrl_pdata *ctrl_pdata); +void mdss_dsi_panel_dsc_pps_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_panel_info *pinfo); +void mdss_dsi_dsc_config(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsc_desc *dsc); static inline const char *__mdss_dsi_pm_name(enum dsi_pm_type module) { diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index 852d232d00ca..ed5256e2f7cf 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -1089,8 +1089,7 @@ int mdss_dsi_reg_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) return ret; } -static void mdss_dsi_dsc_config(struct mdss_dsi_ctrl_pdata *ctrl, - struct dsc_desc *dsc) +void mdss_dsi_dsc_config(struct mdss_dsi_ctrl_pdata *ctrl, struct dsc_desc *dsc) { u32 data, offset; diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index a7f9a2cf38de..8357428471d7 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -709,7 +709,7 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) mdss_dsi_panel_cmds_send(ctrl, on_cmds, CMD_REQ_COMMIT); if (pinfo->compression_mode == COMPRESSION_DSC) - mdss_dsi_panel_dsc_pps_send(ctrl); + mdss_dsi_panel_dsc_pps_send(ctrl, pinfo); if (ctrl->ds_registered) { if (ctrl->dba_ops.video_on) @@ -1171,15 +1171,14 @@ static int mdss_dsc_to_buf(struct dsc_desc *dsc, char *buf, return DSC_PPS_LEN; /* 128 */ } -void mdss_dsi_panel_dsc_pps_send(struct mdss_dsi_ctrl_pdata *ctrl) +void mdss_dsi_panel_dsc_pps_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_panel_info *pinfo) { - struct mdss_panel_info *pinfo; struct dsc_desc *dsc; struct dsi_panel_cmds pcmds; struct dsi_cmd_desc cmd; - pinfo = &(ctrl->panel_data.panel_info); - if (pinfo->compression_mode != COMPRESSION_DSC) + if (!pinfo || (pinfo->compression_mode != COMPRESSION_DSC)) return; memset(&pcmds, 0, sizeof(pcmds)); @@ -1214,7 +1213,7 @@ int mdss_dsc_initial_line_calc(int bpc, int xmit_delay, return CEIL(total_pixels, slice_width); } -static void mdss_dsc_parameters_calc(struct mdss_panel_timing *timing) +void mdss_dsc_parameters_calc(struct dsc_desc *dsc, int width, int height) { int bpp, bpc; int mux_words_size; @@ -1227,7 +1226,20 @@ static void mdss_dsc_parameters_calc(struct mdss_panel_timing *timing) int data; int final_value, final_scale; int slice_per_line, bytes_in_slice, total_bytes; - struct dsc_desc *dsc = &timing->dsc; + + if (!dsc || !width || !height) + return; + + dsc->pic_width = width; + dsc->pic_height = height; + + if ((dsc->pic_width % dsc->slice_width) || + (dsc->pic_height % dsc->slice_height)) { + pr_err("Error: pic_dim=%dx%d has to be multiple of slice_dim=%dx%d\n", + dsc->pic_width, dsc->pic_height, + dsc->slice_width, dsc->slice_height); + return; + } dsc->rc_model_size = 8192; /* rate_buffer_size */ dsc->first_line_bpg_offset = 12; @@ -1246,11 +1258,6 @@ static void mdss_dsc_parameters_calc(struct mdss_panel_timing *timing) dsc->range_max_qp = dsc_rc_range_max_qp; dsc->range_bpg_offset = dsc_rc_range_bpg_offset; - dsc->initial_lines = 2; - - dsc->pic_width = timing->xres; - dsc->pic_height = timing->yres; - bpp = dsc->bpp; bpc = dsc->bpc; @@ -1304,7 +1311,6 @@ static void mdss_dsc_parameters_calc(struct mdss_panel_timing *timing) if ((dsc->slice_width * bpp) % 8) dsc->chunk_size++; - /* rbs-min */ min_rate_buffer_size = dsc->rc_model_size - dsc->initial_offset + dsc->initial_xmit_delay * bpp + @@ -1479,7 +1485,7 @@ static int mdss_dsi_parse_dsc_params(struct device_node *np, dsc->config_by_manufacture_cmd = of_property_read_bool(np, "qcom,mdss-dsc-config-by-manufacture-cmd"); - mdss_dsc_parameters_calc(timing); + mdss_dsc_parameters_calc(&timing->dsc, timing->xres, timing->yres); timing->compression_mode = COMPRESSION_DSC; diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 85b0d613f40f..d26926a16952 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -278,6 +278,12 @@ struct mdss_mdp_ctl { u32 slave_intf_num; /* ping-pong split */ u32 intf_type; + /* + * false: for sctl in DUAL_LM_DUAL_DISPLAY + * true: everything else + */ + bool is_master; + u32 opmode; u32 flush_bits; u32 flush_reg_data; @@ -328,10 +334,26 @@ struct mdss_mdp_ctl { struct work_struct recover_work; struct work_struct remove_underrun_handler; + /* + * This ROI is aligned to as per following guidelines and + * sent to the panel driver. + * + * 1. DUAL_LM_DUAL_DISPLAY + * Panel = 1440x2560 + * CTL0 = 720x2560 (LM0=720x2560) + * CTL1 = 720x2560 (LM1=720x2560) + * Both CTL's ROI will be (0-719)x(0-2599) + * 2. DUAL_LM_SINGLE_DISPLAY + * Panel = 1440x2560 + * CTL0 = 1440x2560 (LM0=720x2560 and LM1=720x2560) + * CTL0's ROI will be (0-1429)x(0-2599) + * 3. SINGLE_LM_SINGLE_DISPLAY + * Panel = 1080x1920 + * CTL0 = 1080x1920 (LM0=1080x1920) + * CTL0's ROI will be (0-1079)x(0-1919) + */ struct mdss_rect roi; struct mdss_rect roi_bkup; - u8 roi_changed; - u8 valid_roi; bool cmd_autorefresh_en; int autorefresh_frame_cnt; @@ -361,7 +383,11 @@ struct mdss_mdp_mixer { u8 params_changed; u16 width; u16 height; + + bool valid_roi; + bool roi_changed; struct mdss_rect roi; + u8 cursor_enabled; u16 cursor_hotx; u16 cursor_hoty; @@ -724,6 +750,21 @@ enum mdss_mdp_clt_intf_event_flags { #define mfd_to_wb(mfd) (((struct mdss_overlay_private *)\ (mfd->mdp.private1))->wb) +/** + * - mdss_mdp_is_both_lm_valid + * @main_ctl - pointer to a main ctl + * + * Function checks if both layer mixers are active or not. This can be useful + * when partial update is enabled on either MDP_DUAL_LM_SINGLE_DISPLAY or + * MDP_DUAL_LM_DUAL_DISPLAY . + */ +static inline bool mdss_mdp_is_both_lm_valid(struct mdss_mdp_ctl *main_ctl) +{ + return (main_ctl && main_ctl->is_master && + main_ctl->mixer_left && main_ctl->mixer_left->valid_roi && + main_ctl->mixer_right && main_ctl->mixer_right->valid_roi); +} + static inline struct mdss_mdp_ctl *mdss_mdp_get_split_ctl( struct mdss_mdp_ctl *ctl) { @@ -1346,4 +1387,8 @@ struct mdss_mdp_writeback *mdss_mdp_wb_assign(u32 id, u32 reg_index); struct mdss_mdp_writeback *mdss_mdp_wb_alloc(u32 caps, u32 reg_index); void mdss_mdp_wb_free(struct mdss_mdp_writeback *wb); +void mdss_dsc_parameters_calc(struct dsc_desc *dsc, int width, int height); +void mdss_mdp_ctl_dsc_setup(struct mdss_mdp_ctl *ctl, + struct mdss_panel_info *pinfo); + #endif /* MDSS_MDP_H */ diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index ecf59e4f81a0..df392a9b779d 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -2391,76 +2391,48 @@ static inline int mdss_mdp_set_split_ctl(struct mdss_mdp_ctl *ctl, return 0; } -static int __mdss_mdp_ctl_dsc_enable(bool enable, - struct mdss_mdp_mixer *mixer, struct mdss_panel_info *pinfo) +static inline void mdss_mdp_ctl_dsc_enable(struct mdss_mdp_mixer *mixer) { - u32 data; - u32 *lp; - char *cp; - int i, bpp, lsb; - char __iomem *offset, *off; - struct mdss_data_type *mdata = mdss_mdp_get_mdata(); - struct dsc_desc *dsc = &pinfo->dsc; - - if (!mixer || !pinfo) { - pr_err("invalid input\n"); - return -EINVAL; - } - - if (pinfo->compression_mode != COMPRESSION_DSC) { - pr_err("invalid compression mode = %d\n", - pinfo->compression_mode); - return -EPERM; - } - - if (mixer->num != MDSS_MDP_INTF_LAYERMIXER0 && - mixer->num != MDSS_MDP_INTF_LAYERMIXER1) { - pr_err("mix%d doesn't support DSC.\n", mixer->num); - return -EPERM; - } - - if (!enable) { - mdss_mdp_pingpong_write(mixer->pingpong_base, - MDSS_MDP_REG_PP_DSC_MODE, 0); - return 0; - } - - /* dsc enable */ mdss_mdp_pingpong_write(mixer->pingpong_base, MDSS_MDP_REG_PP_DSC_MODE, 1); +} + +static inline void mdss_mdp_ctl_dsc_disable(struct mdss_mdp_mixer *mixer) +{ + mdss_mdp_pingpong_write(mixer->pingpong_base, + MDSS_MDP_REG_PP_DSC_MODE, 0); +} + +static void mdss_mdp_ctl_dsc_config(struct mdss_mdp_mixer *mixer, + struct dsc_desc *dsc, u32 mode) +{ + u32 data; + int bpp, lsb; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + char __iomem *offset = mdata->mdp_base; data = mdss_mdp_pingpong_read(mixer->pingpong_base, MDSS_MDP_REG_PP_DCE_DATA_OUT_SWAP); - data |= BIT(18); /* endian flip */ + data |= BIT(18); /* endian flip */ mdss_mdp_pingpong_write(mixer->pingpong_base, MDSS_MDP_REG_PP_DCE_DATA_OUT_SWAP, data); - if (pinfo->type == MIPI_VIDEO_PANEL) - data = BIT(2); /* vieo mode */ - - /* split display with independent decoders is not handled yet */ - if (pinfo->is_split_display) - data |= BIT(0); - - /* need to handle a use-case of single_lm_pp_split_dsc_merge */ - if ((mixer->ctl->mfd->split_mode == MDP_DUAL_LM_SINGLE_DISPLAY) && - (pinfo->dsc_enc_total == 2)) - data |= (BIT(0) | BIT(1)); - - offset = mdata->mdp_base; - /* dce0_sel->pp0, dce1_sel->pp1 */ writel_relaxed(0x0, offset + MDSS_MDP_REG_DCE_SEL); - if (mixer->num == MDSS_MDP_INTF_LAYERMIXER0) + if (mixer->num == MDSS_MDP_INTF_LAYERMIXER0) { offset += MDSS_MDP_DSC_0_OFFSET; - else + } else if (mixer->num == MDSS_MDP_INTF_LAYERMIXER1) { offset += MDSS_MDP_DSC_1_OFFSET; + } else { + pr_err("invalid mixer numer=%d\n", mixer->num); + return; + } - writel_relaxed(data, offset + MDSS_MDP_REG_DSC_COMMON_MODE); + writel_relaxed(mode, offset + MDSS_MDP_REG_DSC_COMMON_MODE); - data = dsc->ich_reset_value | dsc->ich_reset_override; - data <<= 28; + data = dsc->ich_reset_override << 28; + data |= dsc->ich_reset_value << 29; data |= (dsc->initial_lines << 20); data |= ((dsc->slice_last_group_size - 1) << 18); /* bpp is 6.4 format, 4 LSBs bits are for fractional part */ @@ -2495,9 +2467,11 @@ static int __mdss_mdp_ctl_dsc_enable(bool enable, data = dsc->chunk_size << 16; writel_relaxed(data, offset + MDSS_MDP_REG_DSC_CHUNK_SIZE); - pr_debug("pic_w=%d pic_h=%d, slice_h=%d slice_w=%d, chunk=%d\n", - dsc->pic_width, dsc->pic_height, - dsc->slice_width, dsc->slice_height, dsc->chunk_size); + pr_debug("mix%d pic_w=%d pic_h=%d, slice_h=%d slice_w=%d, chunk=%d\n", + mixer->num, dsc->pic_width, dsc->pic_height, + dsc->slice_width, dsc->slice_height, dsc->chunk_size); + MDSS_XLOG(mixer->num, dsc->pic_width, dsc->pic_height, + dsc->slice_width, dsc->slice_height, dsc->chunk_size); data = dsc->initial_dec_delay << 16; data |= dsc->initial_xmit_delay; @@ -2538,6 +2512,28 @@ static int __mdss_mdp_ctl_dsc_enable(bool enable, data |= dsc->quant_incr_limit0 << 4; data |= dsc->edge_factor; writel_relaxed(data, offset + MDSS_MDP_REG_DSC_RC); +} + +static void mdss_mdp_ctl_dsc_config_thresh(struct mdss_mdp_mixer *mixer, + struct mdss_panel_info *pinfo) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + struct dsc_desc *dsc = &pinfo->dsc; + char __iomem *offset, *off; + u32 *lp; + char *cp; + int i; + + offset = mdata->mdp_base; + + if (mixer->num == MDSS_MDP_INTF_LAYERMIXER0) { + offset += MDSS_MDP_DSC_0_OFFSET; + } else if (mixer->num == MDSS_MDP_INTF_LAYERMIXER1) { + offset += MDSS_MDP_DSC_1_OFFSET; + } else { + pr_err("invalid mixer numer=%d\n", mixer->num); + return; + } lp = dsc->buf_thresh; off = offset + MDSS_MDP_REG_DSC_RC_BUF_THRESH; @@ -2566,25 +2562,97 @@ static int __mdss_mdp_ctl_dsc_enable(bool enable, writel_relaxed(*cp++, off); off += 4; } - - return 0; } -static int mdss_mdp_ctl_dsc_enable(bool enable, - struct mdss_mdp_ctl *ctl, struct mdss_panel_info *pinfo) +/* called for each ctl */ +void mdss_mdp_ctl_dsc_setup(struct mdss_mdp_ctl *ctl, + struct mdss_panel_info *pinfo) { - int rc; - struct mdss_mdp_mixer *mixer = ctl->mixer_left; + struct mdss_mdp_mixer *mixer_left = ctl->mixer_left; + struct mdss_mdp_mixer *mixer_right = NULL; + struct dsc_desc *dsc = &pinfo->dsc; + u32 pic_width = 0, pic_height = 0; + u32 mode = 0; + bool recalc_dsc_params = false; - rc = __mdss_mdp_ctl_dsc_enable(enable, mixer, pinfo); - if (rc) - return rc; + if (pinfo->type == MIPI_VIDEO_PANEL) + mode = BIT(2); - if (mixer->ctl->mfd->split_mode == MDP_DUAL_LM_SINGLE_DISPLAY) - rc = __mdss_mdp_ctl_dsc_enable(enable, - ctl->mixer_right, pinfo); + /* pingpong split with DSC needs to be handled */ + if (is_split_lm(ctl->mfd)) { + struct mdss_mdp_ctl *main_ctl; - return rc; + if (ctl->is_master) + main_ctl = ctl; + else + main_ctl = mdss_mdp_get_main_ctl(ctl); + + if (!main_ctl) { + pr_err("%pS: invalid input\n", + __builtin_return_address(0)); + return; + } + + /* + * two independent decoders on DDIC requires + * split 2p2d mode but it is not supported yet. + */ + if (mdss_mdp_is_both_lm_valid(main_ctl)) + mode |= BIT(0); /* assumming 1 decoder on panel side */ + + if (main_ctl->mfd->split_mode == MDP_DUAL_LM_SINGLE_DISPLAY) { + + mixer_right = main_ctl->mixer_right; + + /* DSC Merge */ + if ((pinfo->dsc_enc_total == 2) && + (mdss_mdp_is_both_lm_valid(main_ctl))) + mode |= BIT(1); + } + } + + if (mixer_left->valid_roi) { + pic_width = mixer_left->roi.w; + pic_height = mixer_left->roi.h; + recalc_dsc_params = true; + } + + if (mixer_right && mixer_right->valid_roi) { + pic_width += mixer_right->roi.w; + pic_height = mixer_right->roi.h; /* height on both lm is same */ + recalc_dsc_params = true; + } + + /* re-calculate DSC params before configuring them to MDP */ + if (recalc_dsc_params) + mdss_dsc_parameters_calc(dsc, pic_width, pic_height); + + if (mixer_left->valid_roi) { + mdss_mdp_ctl_dsc_config(mixer_left, dsc, mode); + mdss_mdp_ctl_dsc_config_thresh(mixer_left, pinfo); + mdss_mdp_ctl_dsc_enable(mixer_left); + } else { + mdss_mdp_ctl_dsc_disable(mixer_left); + } + + if (mixer_right) { + if (mixer_right->valid_roi) { + mdss_mdp_ctl_dsc_config(mixer_right, dsc, mode); + mdss_mdp_ctl_dsc_config_thresh(mixer_right, pinfo); + mdss_mdp_ctl_dsc_enable(mixer_right); + } else { + mdss_mdp_ctl_dsc_disable(mixer_right); + } + } + pr_debug("mix%d: valid_roi=%d mix%d: valid_roi=%d mode=%d, pic_dim:%dx%d\n", + mixer_left->num, mixer_left->valid_roi, + mixer_right ? mixer_right->num : -1, + mixer_right ? mixer_right->valid_roi : -1, + mode, pic_width, pic_height); + MDSS_XLOG(mixer_left->num, mixer_left->valid_roi, + mixer_right ? mixer_right->num : -1, + mixer_right ? mixer_right->valid_roi : -1, + mode, pic_width, pic_height); } static int mdss_mdp_ctl_fbc_enable(int enable, @@ -2711,7 +2779,8 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) ctl->mixer_left->width = width; ctl->mixer_left->height = height; ctl->mixer_left->roi = (struct mdss_rect) {0, 0, width, height}; - ctl->valid_roi = true; + ctl->mixer_left->valid_roi = true; + ctl->mixer_left->roi_changed = true; if (ctl->mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY) { pr_debug("dual display detected\n"); @@ -2737,7 +2806,11 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) ctl->mixer_right->height = height; ctl->mixer_right->roi = (struct mdss_rect) {0, 0, width, height}; + ctl->mixer_right->valid_roi = true; + ctl->mixer_right->roi_changed = true; } else if (ctl->mixer_right) { + ctl->mixer_right->valid_roi = false; + ctl->mixer_right->roi_changed = false; mdss_mdp_mixer_free(ctl->mixer_right); ctl->mixer_right = NULL; } @@ -2991,7 +3064,8 @@ int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl, mixer->height = sctl->height; mixer->roi = (struct mdss_rect) {0, 0, mixer->width, mixer->height}; - sctl->valid_roi = true; + mixer->valid_roi = true; + mixer->roi_changed = true; sctl->mixer_left = mixer; return mdss_mdp_set_split_ctl(ctl, sctl); @@ -3150,10 +3224,8 @@ static void mdss_mdp_ctl_restore_sub(struct mdss_mdp_ctl *ctl) if (ctl->panel_data->panel_info.compression_mode == COMPRESSION_DSC) { - ret = mdss_mdp_ctl_dsc_enable(1, ctl, + mdss_mdp_ctl_dsc_setup(ctl, &ctl->panel_data->panel_info); - if (ret) - pr_err("Failed to restore DSC mode\n"); } else if (ctl->panel_data->panel_info.compression_mode == COMPRESSION_FBC) { ret = mdss_mdp_ctl_fbc_enable(1, ctl->mixer_left, @@ -3258,9 +3330,7 @@ static int mdss_mdp_ctl_start_sub(struct mdss_mdp_ctl *ctl, bool handoff) mdp_mixer_write(mixer, MDSS_MDP_REG_LM_OUT_SIZE, outsize); if (pinfo->compression_mode == COMPRESSION_DSC) { - ret = mdss_mdp_ctl_dsc_enable(true, ctl, pinfo); - if (ret) - pr_err("dsc_enable failed. rc=%d\n", ret); + mdss_mdp_ctl_dsc_setup(ctl, pinfo); } else if (pinfo->compression_mode == COMPRESSION_FBC) { ret = mdss_mdp_ctl_fbc_enable(1, ctl->mixer_left, pinfo); @@ -3484,29 +3554,6 @@ int mdss_mdp_ctl_reset(struct mdss_mdp_ctl *ctl, bool is_recovery) return (!cnt) ? -EAGAIN : 0; } -static void mdss_mdp_set_mixer_roi(struct mdss_mdp_ctl *ctl, - struct mdss_rect *roi) -{ - struct mdss_rect mixer_roi; - - ctl->valid_roi = (roi->w && roi->h); - ctl->roi_changed = 0; - if (!mdss_rect_cmp(roi, &ctl->roi)) { - ctl->roi = *roi; - ctl->roi_changed++; - - mixer_roi = ctl->mixer_left->roi; - if ((mixer_roi.w != roi->w) || - (mixer_roi.h != roi->h)) { - ctl->mixer_left->roi = *roi; - ctl->mixer_left->params_changed++; - } - } - - pr_debug("ROI requested: [%d]: [%d, %d, %d, %d]\n", - ctl->num, ctl->roi.x, ctl->roi.y, ctl->roi.w, ctl->roi.h); -} - /* * mdss_mdp_mixer_update_pipe_map() - keep track of pipe configuration in mixer * @master_ctl: mdp controller. @@ -3529,9 +3576,31 @@ static void mdss_mdp_mixer_update_pipe_map(struct mdss_mdp_ctl *master_ctl, mixer->pipe_mapped = mixer->next_pipe_map; } +static void mdss_mdp_set_mixer_roi(struct mdss_mdp_mixer *mixer, + struct mdss_rect *roi) +{ + mixer->valid_roi = (roi->w && roi->h); + mixer->roi_changed = false; + + if (!mdss_rect_cmp(roi, &mixer->roi)) { + mixer->roi = *roi; + mixer->params_changed++; + mixer->roi_changed = true; + } + + pr_debug("mixer%d ROI %s: [%d, %d, %d, %d]\n", + mixer->num, mixer->roi_changed ? "changed" : "not changed", + mixer->roi.x, mixer->roi.y, mixer->roi.w, mixer->roi.h); + MDSS_XLOG(mixer->num, mixer->roi_changed, mixer->valid_roi, + mixer->roi.x, mixer->roi.y, mixer->roi.w, mixer->roi.h); +} + +/* only call from master ctl */ void mdss_mdp_set_roi(struct mdss_mdp_ctl *ctl, struct mdss_rect *l_roi, struct mdss_rect *r_roi) { + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + /* Reset ROI when we have (1) invalid ROI (2) feature disabled */ if ((!l_roi->w && l_roi->h) || (l_roi->w && !l_roi->h) || (!r_roi->w && r_roi->h) || (r_roi->w && !r_roi->h) || @@ -3549,10 +3618,45 @@ void mdss_mdp_set_roi(struct mdss_mdp_ctl *ctl, } } - mdss_mdp_set_mixer_roi(ctl, l_roi); + mdss_mdp_set_mixer_roi(ctl->mixer_left, l_roi); + ctl->roi = ctl->mixer_left->roi; - if (ctl->mixer_right) - mdss_mdp_set_mixer_roi(ctl->mixer_right->ctl, r_roi); + if (ctl->mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY) { + struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl); + + mdss_mdp_set_mixer_roi(sctl->mixer_left, r_roi); + sctl->roi = sctl->mixer_left->roi; + } else if (ctl->mfd->split_mode == MDP_DUAL_LM_SINGLE_DISPLAY) { + + mdss_mdp_set_mixer_roi(ctl->mixer_right, r_roi); + + /* in this case, CTL_ROI is a union of left+right ROIs. */ + ctl->roi.w += ctl->mixer_right->roi.w; + + /* right_only, update roi.x as per CTL ROI guidelines */ + if (!ctl->mixer_left->valid_roi) + ctl->roi.x = left_lm_w_from_mfd(ctl->mfd) + + ctl->mixer_right->roi.x; + } + + /* + * When source split is enabled, dst_x of all pipes staged on right + * layer-mixer (LM) has to be greater than left LM's width. + * Now when partial update is enabled, let's say frame N is updating + * full panel width so both LMs are valid thus source split is enabled + * and dst_x of all pipes is as per above requirement. Now when frame + * N+1 is right-only update without any geomatry changes, pipe's params + * are not changed and right LM's roi is also not changed. So if + * params are not changed then pipe's register programming is skipped. + * So for that right-only update, pipe's dst_x remains same + * as frame N, which not correct and can lead to unknown behaviour. + * Fix this by identifying this condition and forcing roi changed for + * right LM. + */ + if (is_split_lm(ctl->mfd) && mdata->has_src_split && + (!ctl->mixer_left->valid_roi && ctl->mixer_left->roi_changed) && + (ctl->mixer_right->valid_roi && !ctl->mixer_right->roi_changed)) + ctl->mixer_right->roi_changed = true; } u32 mdss_mdp_get_mixer_mask(u32 pipe_num, u32 stage) @@ -3628,8 +3732,21 @@ static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl, return; } - if (!ctl->valid_roi) + if (!mixer->valid_roi) { + /* + * resetting mixer config is specifically needed when split + * mode is MDP_DUAL_LM_SINGLE_DISPLAY but update is only on + * one side. + */ + off = __mdss_mdp_ctl_get_mixer_off(mixer); + mdss_mdp_ctl_write(ctl, off, 0); + /* Program ctl layer extension bits */ + mdss_mdp_ctl_write(ctl, + off + MDSS_MDP_REG_CTL_LAYER_EXTN_OFFSET, 0); + + MDSS_XLOG(mixer->num, XLOG_FUNC_EXIT); return; + } trace_mdp_mixer_update(mixer->num); pr_debug("setup mixer=%d\n", mixer->num); @@ -4415,8 +4532,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, { struct mdss_mdp_ctl *sctl = NULL; int ret = 0; - bool is_bw_released; - int split_enable; + bool is_bw_released, split_lm_valid; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); u32 ctl_flush_bits = 0, sctl_flush_bits = 0; @@ -4433,6 +4549,8 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, return 0; } + split_lm_valid = mdss_mdp_is_both_lm_valid(ctl); + sctl = mdss_mdp_get_split_ctl(ctl); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); @@ -4450,26 +4568,22 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, } /* left update */ - if (ctl->valid_roi) + if (ctl->mixer_left->valid_roi) mdss_mdp_ctl_perf_set_transaction_status(ctl, PERF_SW_COMMIT_STATE, PERF_STATUS_BUSY); /* right update */ - if (sctl && sctl->valid_roi) + if (sctl && sctl->mixer_left->valid_roi) mdss_mdp_ctl_perf_set_transaction_status(sctl, PERF_SW_COMMIT_STATE, PERF_STATUS_BUSY); - if (mdata->has_src_split) { - if (sctl) - sctl->mixer_left->src_split_req = - (ctl->valid_roi == sctl->valid_roi); - else if (ctl->mixer_right) /* single ctl, dual LM */ - ctl->mixer_right->src_split_req = ctl->valid_roi; - } + if (ctl->mixer_right) + ctl->mixer_right->src_split_req = + mdata->has_src_split && split_lm_valid; if (is_bw_released || ctl->force_screen_state || - (ctl->mixer_left && ctl->mixer_left->params_changed) || - (ctl->mixer_right && ctl->mixer_right->params_changed)) { + (ctl->mixer_left->params_changed) || + (ctl->mixer_right && ctl->mixer_right->params_changed)) { ATRACE_BEGIN("prepare_fnc"); if (ctl->ops.prepare_fnc) ret = ctl->ops.prepare_fnc(ctl, arg); @@ -4500,12 +4614,10 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, /* * With partial frame update, enable split display bit only - * when validity of ROI's on both the DSI's are identical + * when validity of ROI's on both the DSI's are identical. */ - if (sctl) { - split_enable = (ctl->valid_roi == sctl->valid_roi); - mdss_mdp_ctl_split_display_enable(split_enable, ctl, sctl); - } + if (sctl) + mdss_mdp_ctl_split_display_enable(split_lm_valid, ctl, sctl); ATRACE_BEGIN("postproc_programming"); if (ctl->mfd && ctl->mfd->dcm_state != DTM_ENTER) @@ -4629,7 +4741,8 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, sctl_flush_bits); sctl->flush_bits = 0; } - MDSS_XLOG(ctl->intf_num, ctl_flush_bits, sctl_flush_bits); + MDSS_XLOG(ctl->intf_num, ctl_flush_bits, sctl_flush_bits, + split_lm_valid); wmb(); ctl->flush_reg_data = ctl_flush_bits; ctl->flush_bits = 0; @@ -4637,7 +4750,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, mdss_mdp_mixer_update_pipe_map(ctl, MDSS_MDP_MIXER_MUX_LEFT); mdss_mdp_mixer_update_pipe_map(ctl, MDSS_MDP_MIXER_MUX_RIGHT); - if (sctl && !ctl->valid_roi && sctl->valid_roi) { + /* right-only kickoff */ + if (!ctl->mixer_left->valid_roi && + sctl && sctl->mixer_left->valid_roi) { /* * Seperate kickoff on DSI1 is needed only when we have * ONLY right half updating on a dual DSI panel @@ -4649,11 +4764,6 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, ret = ctl->ops.display_fnc(ctl, arg); /* DSI0 kickoff */ } - if (sctl) - sctl->valid_roi = 0; - - ctl->valid_roi = 0; - if (ret) pr_warn("ctl %d error displaying frame\n", ctl->num); diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 1e90a2bd852d..e39c4c5daab0 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -1510,11 +1510,40 @@ static void mdss_mdp_cmd_set_sync_ctx( } } +/* only master ctl is valid and pingpong split with DSC is pending */ +static void mdss_mdp_cmd_dsc_reconfig(struct mdss_mdp_ctl *ctl) +{ + struct mdss_panel_info *pinfo, *spinfo; + struct mdss_mdp_ctl *sctl = NULL; + bool changed = false; + + if (!ctl || !ctl->is_master) + return; + + pinfo = &ctl->panel_data->panel_info; + if (pinfo->compression_mode != COMPRESSION_DSC) + return; + + sctl = mdss_mdp_get_split_ctl(ctl); + + changed = ctl->mixer_left->roi_changed; + if (ctl->mfd->split_mode == MDP_DUAL_LM_SINGLE_DISPLAY) + changed |= ctl->mixer_right->roi_changed; + + if (changed) + mdss_mdp_ctl_dsc_setup(ctl, pinfo); + + if (sctl && sctl->mixer_left->roi_changed) { + spinfo = &sctl->panel_data->panel_info; + mdss_mdp_ctl_dsc_setup(sctl, spinfo); + } +} + static int mdss_mdp_cmd_set_partial_roi(struct mdss_mdp_ctl *ctl) { - int rc = 0; + int rc = -EINVAL; - if (!ctl->panel_data->panel_info.partial_update_supported) + if (!ctl->panel_data->panel_info.partial_update_enabled) return rc; /* set panel col and page addr */ @@ -1525,9 +1554,9 @@ static int mdss_mdp_cmd_set_partial_roi(struct mdss_mdp_ctl *ctl) static int mdss_mdp_cmd_set_stream_size(struct mdss_mdp_ctl *ctl) { - int rc = 0; + int rc = -EINVAL; - if (!ctl->panel_data->panel_info.partial_update_supported) + if (!ctl->panel_data->panel_info.partial_update_enabled) return rc; /* set dsi controller stream size */ @@ -1759,6 +1788,8 @@ int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) */ mdss_mdp_resource_control(ctl, MDP_RSRC_CTL_EVENT_KICKOFF); + mdss_mdp_cmd_dsc_reconfig(ctl); + mdss_mdp_cmd_set_partial_roi(ctl); /* @@ -1814,11 +1845,21 @@ int mdss_mdp_cmd_restore(struct mdss_mdp_ctl *ctl, bool locked) { struct mdss_mdp_cmd_ctx *ctx, *sctx = NULL; + if (!ctl) + return -EINVAL; + pr_debug("%s: called for ctl%d\n", __func__, ctl->num); ctx = (struct mdss_mdp_cmd_ctx *)ctl->intf_ctx[MASTER_CTX]; - if (is_pingpong_split(ctl->mfd)) + if (is_pingpong_split(ctl->mfd)) { sctx = (struct mdss_mdp_cmd_ctx *)ctl->intf_ctx[SLAVE_CTX]; + } else if (ctl->mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY) { + struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl); + + if (sctl) + sctx = (struct mdss_mdp_cmd_ctx *) + sctl->intf_ctx[MASTER_CTX]; + } if (mdss_mdp_cmd_tearcheck_setup(ctx, locked)) { pr_warn("%s: ctx%d tearcheck setup failed\n", __func__, @@ -2309,6 +2350,8 @@ void mdss_mdp_switch_roi_reset(struct mdss_mdp_ctl *ctl) if (sctl && sctl->panel_data) sctl->panel_data->panel_info.roi = sctl->roi; + mdss_mdp_cmd_dsc_reconfig(ctl); + mdss_mdp_cmd_set_partial_roi(ctl); } diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 1faa13be73eb..f8c41c4dc97b 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -1618,8 +1618,27 @@ static bool __is_roi_valid(struct mdss_mdp_pipe *pipe, struct mdss_data_type *mdata = mdss_mdp_get_mdata(); u32 left_lm_w = left_lm_w_from_mfd(pipe->mfd); - if (pipe->src_split_req) - roi.w += r_roi->w; + if (pipe->src_split_req) { + if (roi.w) { + /* left_roi is valid */ + roi.w += r_roi->w; + } else { + /* + * if we come here then left_roi is zero but pipe's + * output is crossing LM boundary if it was Full Screen + * update. In such case, if right ROI's (x+w) is less + * than pipe's dst_x then #2 check will fail even + * though in full coordinate system it is valid. + * ex: + * left_lm_w = 800; + * pipe->dst.x = 400; + * pipe->dst.w = 800; + * r_roi.x + r_roi.w = 300; + * To avoid such pitfall, extend ROI for comparison. + */ + roi.w += left_lm_w + r_roi->w; + } + } if (mdata->has_src_split && is_right_mixer) dst.x -= left_lm_w; @@ -1777,7 +1796,7 @@ static void __validate_and_set_roi(struct msm_fb_data_type *mfd, struct mdss_mdp_pipe *pipe; struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); - struct mdss_rect l_roi, r_roi; + struct mdss_rect l_roi = {0}, r_roi = {0}; struct mdp_rect tmp_roi = {0}; bool skip_partial_update = true; @@ -1851,6 +1870,13 @@ set_roi: ctl->mixer_right->height}; } } + + pr_debug("after processing: %s l_roi:-> %d %d %d %d r_roi:-> %d %d %d %d\n", + (l_roi.w && l_roi.h && r_roi.w && r_roi.h) ? "left+right" : + ((l_roi.w && l_roi.h) ? "left-only" : "right-only"), + l_roi.x, l_roi.y, l_roi.w, l_roi.h, + r_roi.x, r_roi.y, r_roi.w, r_roi.h); + mdss_mdp_set_roi(ctl, &l_roi, &r_roi); } @@ -4455,6 +4481,7 @@ static struct mdss_mdp_ctl *__mdss_mdp_overlay_ctl_init( rc = PTR_ERR(ctl); goto error; } + ctl->is_master = true; ctl->vsync_handler.vsync_handler = mdss_mdp_overlay_handle_vsync; ctl->vsync_handler.cmd_post_flush = false; diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c index 1cea4cc40ea3..b14dd179953e 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c @@ -1812,36 +1812,47 @@ static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe, if (!pipe->mixer_left->ctl->is_video_mode && (pipe->mixer_left->type != MDSS_MDP_MIXER_TYPE_WRITEBACK)) { - struct mdss_rect ctl_roi = pipe->mixer_left->ctl->roi; + struct mdss_rect roi = pipe->mixer_left->roi; bool is_right_mixer = pipe->mixer_left->is_right_mixer; - /* main_ctl can be NULL, check validity before use */ - struct mdss_mdp_ctl *main_ctl = - mdss_mdp_get_main_ctl(pipe->mixer_left->ctl); + struct mdss_mdp_ctl *main_ctl; - /* adjust roi or dst_x before crop is applied */ - if (pipe->src_split_req) { - int r_roi_w = ctl_roi.w; - struct mdss_mdp_ctl *sctl; + if (pipe->mixer_left->ctl->is_master) + main_ctl = pipe->mixer_left->ctl; + else + main_ctl = mdss_mdp_get_main_ctl(pipe->mixer_left->ctl); - if (pipe->mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY) { - sctl = mdss_mdp_get_split_ctl( - pipe->mixer_left->ctl); - if (sctl) - r_roi_w = sctl->roi.w; - } - - ctl_roi.w += r_roi_w; - } else if (mdata->has_src_split && is_right_mixer && main_ctl) { - dst.x -= main_ctl->mixer_left->width; + if (!main_ctl) { + pr_err("Error: couldn't find main_ctl for pipe%d\n", + pipe->num); + return -EINVAL; } - mdss_mdp_crop_rect(&src, &dst, &ctl_roi); + if (pipe->src_split_req && main_ctl->mixer_right->valid_roi) { + /* + * pipe is staged on both mixers, expand roi to span + * both mixers before cropping pipe's dimensions. + */ + roi.w += main_ctl->mixer_right->roi.w; + } else if (mdata->has_src_split && is_right_mixer) { + /* + * pipe is only on right mixer but since source-split + * is enabled, its dst_x is full panel coordinate + * aligned where as ROI is mixer coordinate aligned. + * Modify dst_x before applying ROI crop. + */ + dst.x -= left_lm_w_from_mfd(pipe->mfd); + } - /* re-adjust dst_x */ - if (mdata->has_src_split && is_right_mixer && main_ctl) { - /* update valid on left + right */ - if (main_ctl->valid_roi) - dst.x += main_ctl->roi.w; + mdss_mdp_crop_rect(&src, &dst, &roi); + + if (mdata->has_src_split && is_right_mixer) { + /* + * re-adjust dst_x only if both mixers are active, + * meaning right mixer will be working in source + * split mode. + */ + if (mdss_mdp_is_both_lm_valid(main_ctl)) + dst.x += main_ctl->mixer_left->roi.w; } if (pipe->flags & MDP_FLIP_LR) { @@ -2150,6 +2161,7 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe, u32 params_changed; u32 opmode = 0; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + bool roi_changed = false; if (!pipe) { pr_err("pipe not setup properly for queue\n"); @@ -2173,6 +2185,15 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe, mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); ctl = pipe->mixer_left->ctl; + roi_changed = pipe->mixer_left->roi_changed; + + /* + * if pipe is staged on 2 mixers then it is possible that only + * right mixer roi has changed. + */ + if (pipe->mixer_right) + roi_changed |= pipe->mixer_right->roi_changed; + /* * Reprogram the pipe when there is no dedicated wfd blk and * virtual mixer is allocated for the DMA pipe during concurrent @@ -2181,7 +2202,7 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe, params_changed = (pipe->params_changed) || ((pipe->type == MDSS_MDP_PIPE_TYPE_DMA) && (pipe->mixer_left->type == MDSS_MDP_MIXER_TYPE_WRITEBACK) && - (ctl->mdata->mixer_switched)) || ctl->roi_changed; + (ctl->mdata->mixer_switched)) || roi_changed; if (params_changed) { bool is_realtime = !((ctl->intf_num == MDSS_MDP_NO_INTF) diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index 737f4076ac57..8c232ce89ad9 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -198,6 +198,7 @@ struct mdss_intf_recovery { * - 1 clock enable * @MDSS_EVENT_DSI_CMDLIST_KOFF: acquire dsi_mdp_busy lock before kickoff. * @MDSS_EVENT_ENABLE_PARTIAL_ROI: Event to update ROI of the panel. + * @MDSS_EVENT_DSC_PPS_SEND: Event to send DSC PPS command to panel. * @MDSS_EVENT_DSI_STREAM_SIZE: Event to update DSI controller's stream size * @MDSS_EVENT_DSI_UPDATE_PANEL_DATA: Event to update the dsi driver structures * based on the dsi mode passed as argument. @@ -239,6 +240,7 @@ enum mdss_intf_events { MDSS_EVENT_PANEL_CLK_CTRL, MDSS_EVENT_DSI_CMDLIST_KOFF, MDSS_EVENT_ENABLE_PARTIAL_ROI, + MDSS_EVENT_DSC_PPS_SEND, MDSS_EVENT_DSI_STREAM_SIZE, MDSS_EVENT_DSI_UPDATE_PANEL_DATA, MDSS_EVENT_REGISTER_RECOVERY_HANDLER,