diff --git a/msm/msm_drv.h b/msm/msm_drv.h index d1f6d440a0..b18e379ce3 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -364,6 +364,9 @@ struct msm_roi_caps { * @det_thresh_flatness: Flatness threshold. * @extra_width: Extra width required in timing calculations. * @pps_delay_ms: Post PPS command delay in milliseconds. + * @dsc_4hsmerge_en: Using DSC 4HS merge topology + * @dsc_4hsmerge_padding 4HS merge DSC pair padding value in bytes + * @dsc_4hsmerge_alignment 4HS merge DSC alignment value in bytes */ struct msm_display_dsc_info { struct drm_dsc_config config; @@ -382,6 +385,9 @@ struct msm_display_dsc_info { int det_thresh_flatness; u32 extra_width; u32 pps_delay_ms; + bool dsc_4hsmerge_en; + u32 dsc_4hsmerge_padding; + u32 dsc_4hsmerge_alignment; }; diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 0d2c27badc..56f45d37b4 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -678,25 +678,6 @@ static int _sde_crtc_set_roi_v1(struct drm_crtc_state *state, return 0; } -static bool _sde_crtc_setup_is_3dmux_dsc(struct drm_crtc_state *state) -{ - int i; - struct sde_crtc_state *cstate; - bool is_3dmux_dsc = false; - - cstate = to_sde_crtc_state(state); - - for (i = 0; i < cstate->num_connectors; i++) { - struct drm_connector *conn = cstate->connectors[i]; - - if (sde_connector_get_topology_name(conn) == - SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC) - is_3dmux_dsc = true; - } - - return is_3dmux_dsc; -} - static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc, struct drm_crtc_state *state) { @@ -836,6 +817,7 @@ static int _sde_crtc_check_autorefresh(struct drm_crtc *crtc, static int _sde_crtc_set_lm_roi(struct drm_crtc *crtc, struct drm_crtc_state *state, int lm_idx) { + struct sde_kms *sde_kms; struct sde_crtc *sde_crtc; struct sde_crtc_state *crtc_state; const struct sde_rect *crtc_roi; @@ -845,6 +827,12 @@ static int _sde_crtc_set_lm_roi(struct drm_crtc *crtc, if (!crtc || !state || lm_idx >= ARRAY_SIZE(crtc_state->lm_bounds)) return -EINVAL; + sde_kms = _sde_crtc_get_kms(crtc); + if (!sde_kms || !sde_kms->catalog) { + SDE_ERROR("invalid parameters\n"); + return -EINVAL; + } + sde_crtc = to_sde_crtc(crtc); crtc_state = to_sde_crtc_state(state); crtc_roi = &crtc_state->crtc_roi; @@ -864,7 +852,7 @@ static int _sde_crtc_set_lm_roi(struct drm_crtc *crtc, * hence, crtc roi must match the mixer dimensions. */ if (crtc_state->num_ds_enabled || - _sde_crtc_setup_is_3dmux_dsc(state)) { + sde_rm_topology_is_3dmux_dsc(&sde_kms->rm, state)) { if (memcmp(lm_roi, lm_bounds, sizeof(struct sde_rect))) { SDE_ERROR("Unsupported: Dest scaler/3d mux DSC + PU\n"); return -EINVAL; diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index dce9026a61..513dbf6e98 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -71,12 +71,6 @@ /* Maximum number of VSYNC wait attempts for RSC state transition */ #define MAX_RSC_WAIT 5 -#define TOPOLOGY_DUALPIPE_MERGE_MODE(x) \ - (((x) == SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE) || \ - ((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE) || \ - ((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC) || \ - ((x) == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC)) - /** * enum sde_enc_rc_events - events for resource control state machine * @SDE_ENC_RC_EVENT_KICKOFF: @@ -2220,6 +2214,7 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, struct sde_kms *sde_kms; struct drm_connector *conn; int i = 0, ret; + int num_lm, num_intf, num_pp_per_intf; if (!drm_enc) { SDE_ERROR("invalid encoder\n"); @@ -2281,17 +2276,25 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, /* assign the reserved HW blocks to this encoder */ _sde_encoder_virt_populate_hw_res(drm_enc); + /* determine left HW PP block to map to INTF */ + num_lm = sde_enc->mode_info.topology.num_lm; + num_intf = sde_enc->mode_info.topology.num_intf; + num_pp_per_intf = num_lm / num_intf; + if (!num_pp_per_intf) + num_pp_per_intf = 1; + /* perform mode_set on phys_encs */ for (i = 0; i < sde_enc->num_phys_encs; i++) { struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; if (phys) { - if (!sde_enc->hw_pp[i] && sde_enc->topology.num_intf) { - SDE_ERROR_ENC(sde_enc, - "invalid pingpong block for the encoder\n"); + if (!sde_enc->hw_pp[i * num_pp_per_intf] && + sde_enc->topology.num_intf) { + SDE_ERROR_ENC(sde_enc, "invalid hw_pp[%d]\n", + i * num_pp_per_intf); return; } - phys->hw_pp = sde_enc->hw_pp[i]; + phys->hw_pp = sde_enc->hw_pp[i * num_pp_per_intf]; phys->connector = conn->state->connector; if (phys->ops.mode_set) phys->ops.mode_set(phys, mode, adj_mode); @@ -2498,6 +2501,7 @@ static void _sde_encoder_virt_enable_helper(struct drm_encoder *drm_enc) static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys) { + struct sde_kms *sde_kms; void *dither_cfg = NULL; int ret = 0, i = 0; size_t len = 0; @@ -2507,11 +2511,16 @@ static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys) struct sde_encoder_virt *sde_enc; struct sde_hw_pingpong *hw_pp; u32 bpp, bpc; + int num_lm; if (!phys || !phys->connector || !phys->hw_pp || !phys->hw_pp->ops.setup_dither || !phys->parent) return; + sde_kms = sde_encoder_get_kms(phys->parent); + if (!sde_kms) + return; + topology = sde_connector_get_topology_name(phys->connector); if ((topology == SDE_RM_TOPOLOGY_PPSPLIT) && (phys->split_role == ENC_ROLE_SLAVE)) @@ -2538,15 +2547,10 @@ static void _sde_encoder_setup_dither(struct sde_encoder_phys *phys) if (ret && ret == -ENODATA) return; - if (TOPOLOGY_DUALPIPE_MERGE_MODE(topology)) { - for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { - hw_pp = sde_enc->hw_pp[i]; - phys->hw_pp->ops.setup_dither(hw_pp, - dither_cfg, len); - } - - } else { - phys->hw_pp->ops.setup_dither(phys->hw_pp, + num_lm = sde_rm_topology_get_num_lm(&sde_kms->rm, topology); + for (i = 0; i < num_lm; i++) { + hw_pp = sde_enc->hw_pp[i]; + phys->hw_pp->ops.setup_dither(hw_pp, dither_cfg, len); } } diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index 4ad3e1fc34..17e0481797 100644 --- a/msm/sde/sde_encoder.h +++ b/msm/sde/sde_encoder.h @@ -38,7 +38,7 @@ #define MAX_PHYS_ENCODERS_PER_VIRTUAL \ (MAX_H_TILES_PER_DISPLAY * NUM_PHYS_ENCODER_TYPES) -#define MAX_CHANNELS_PER_ENC 2 +#define MAX_CHANNELS_PER_ENC 4 #define SDE_ENCODER_FRAME_EVENT_DONE BIT(0) #define SDE_ENCODER_FRAME_EVENT_ERROR BIT(1) diff --git a/msm/sde/sde_encoder_dce.c b/msm/sde/sde_encoder_dce.c index 0226ceb4b5..394c63f769 100644 --- a/msm/sde/sde_encoder_dce.c +++ b/msm/sde/sde_encoder_dce.c @@ -306,7 +306,8 @@ static int _dce_dsc_setup_single(struct sde_encoder_virt *sde_enc, unsigned long affected_displays, int index, const struct sde_rect *roi, int dsc_common_mode, bool merge_3d, bool disable_merge_3d, bool mode_3d, - bool half_panel_partial_update, int ich_res) + bool dsc_4hsmerge, bool half_panel_partial_update, + int ich_res) { struct sde_hw_ctl *hw_ctl; struct sde_hw_dsc *hw_dsc; @@ -338,8 +339,9 @@ static int _dce_dsc_setup_single(struct sde_encoder_virt *sde_enc, return -EINVAL; } - SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, dsc_common_mode, - index, active, merge_3d, disable_merge_3d); + SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, + dsc_common_mode, index, active, merge_3d, + disable_merge_3d, dsc_4hsmerge); _dce_dsc_pipe_cfg(hw_dsc, hw_pp, dsc, dsc_common_mode, ich_res, hw_dsc_pp, mode_3d, disable_merge_3d, active); @@ -393,15 +395,15 @@ static int _dce_dsc_setup_helper(struct sde_encoder_virt *sde_enc, const struct sde_rm_topology_def *def; const struct sde_rect *roi; enum sde_3d_blend_mode mode_3d; - bool half_panel_partial_update, dsc_merge, merge_3d; + bool half_panel_partial_update, dsc_merge, merge_3d, dsc_4hsmerge; bool disable_merge_3d = false; int this_frame_slices; int intf_ip_w, enc_ip_w; int num_intf, num_dsc, num_lm; int ich_res; + int dsc_pic_width; int dsc_common_mode = 0; - int i; - int rc = 0; + int i, rc = 0; sde_kms = sde_encoder_get_kms(&sde_enc->base); @@ -412,33 +414,43 @@ static int _dce_dsc_setup_helper(struct sde_encoder_virt *sde_enc, enc_master = sde_enc->cur_master; roi = &sde_enc->cur_conn_roi; dsc = &sde_enc->mode_info.comp_info.dsc_info; + num_lm = def->num_lm; num_dsc = def->num_comp_enc; num_intf = def->num_intf; - mode_3d = (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC) ? - BLEND_3D_H_ROW_INT : BLEND_3D_NONE; - num_lm = def->num_lm; + mode_3d = (num_lm > num_dsc) ? BLEND_3D_H_ROW_INT : BLEND_3D_NONE; + merge_3d = (mode_3d != BLEND_3D_NONE) ? true : false; half_panel_partial_update = _dce_check_half_panel_update(num_lm, affected_displays); - merge_3d = (mode_3d != BLEND_3D_NONE) ? true : false; dsc_merge = ((num_dsc > num_intf) && !half_panel_partial_update) ? true : false; disable_merge_3d = (merge_3d && half_panel_partial_update) ? false : true; + dsc_4hsmerge = (dsc_merge && num_dsc == 4 && num_intf == 1) ? + true : false; /* * If this encoder is driving more than one DSC encoder, they * operate in tandem, same pic dimension needs to be used by * each of them.(pp-split is assumed to be not supported) + * + * If encoder is driving more than 2 DSCs, each DSC pair will operate + * on half of the picture in tandem. */ - _dce_dsc_update_pic_dim(dsc, roi->w, roi->h); + if (num_dsc > 2) { + dsc_pic_width = roi->w / 2; + dsc->dsc_4hsmerge_en = dsc_4hsmerge; + } else + dsc_pic_width = roi->w; + + _dce_dsc_update_pic_dim(dsc, dsc_pic_width, roi->h); this_frame_slices = roi->w / dsc->config.slice_width; intf_ip_w = this_frame_slices * dsc->config.slice_width; enc_ip_w = intf_ip_w; if (!half_panel_partial_update) - intf_ip_w /= def->num_intf; + intf_ip_w /= num_intf; if (!half_panel_partial_update && (num_dsc > 1)) dsc_common_mode |= DSC_MODE_SPLIT_PANEL; if (dsc_merge) { @@ -470,7 +482,7 @@ static int _dce_dsc_setup_helper(struct sde_encoder_virt *sde_enc, for (i = 0; i < num_dsc; i++) { rc = _dce_dsc_setup_single(sde_enc, dsc, affected_displays, i, roi, dsc_common_mode, merge_3d, - disable_merge_3d, mode_3d, + disable_merge_3d, mode_3d, dsc_4hsmerge, half_panel_partial_update, ich_res); if (rc) break; diff --git a/msm/sde/sde_encoder_phys.h b/msm/sde/sde_encoder_phys.h index 8e870b6c04..a172bf536f 100644 --- a/msm/sde/sde_encoder_phys.h +++ b/msm/sde/sde_encoder_phys.h @@ -603,16 +603,28 @@ void sde_encoder_helper_hw_reset(struct sde_encoder_phys *phys_enc); static inline enum sde_3d_blend_mode sde_encoder_helper_get_3d_blend_mode( struct sde_encoder_phys *phys_enc) { + struct sde_kms *sde_kms; enum sde_rm_topology_name topology; + const struct sde_rm_topology_def *def; + enum sde_enc_split_role split_role; + int num_lm; + bool mode_3d; if (!phys_enc || phys_enc->enable_state == SDE_ENC_DISABLING) return BLEND_3D_NONE; + sde_kms = sde_encoder_get_kms(phys_enc->parent); topology = sde_connector_get_topology_name(phys_enc->connector); - if (phys_enc->split_role == ENC_ROLE_SOLO && - (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE || - topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC || - topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC)) + def = sde_rm_topology_get_topology_def(&sde_kms->rm, topology); + num_lm = def->num_lm; + mode_3d = (num_lm > def->num_comp_enc) ? true : false; + split_role = phys_enc->split_role; + + if (split_role == ENC_ROLE_SOLO && num_lm == 2 && mode_3d) + return BLEND_3D_H_ROW_INT; + + if ((split_role == ENC_ROLE_MASTER || split_role == ENC_ROLE_SLAVE) + && num_lm == 4 && mode_3d) return BLEND_3D_H_ROW_INT; return BLEND_3D_NONE; diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 9366ac2bae..f3a7c330ef 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -87,7 +87,7 @@ #define SDE_CTL_CFG_VERSION_1_0_0 0x100 #define MAX_INTF_PER_CTL_V1 2 -#define MAX_DSC_PER_CTL_V1 2 +#define MAX_DSC_PER_CTL_V1 4 #define MAX_CWB_PER_CTL_V1 2 #define MAX_MERGE_3D_PER_CTL_V1 2 #define MAX_WB_PER_CTL_V1 1 diff --git a/msm/sde/sde_hw_dsc_1_2.c b/msm/sde/sde_hw_dsc_1_2.c index 9558be6660..38a2824fc6 100644 --- a/msm/sde/sde_hw_dsc_1_2.c +++ b/msm/sde/sde_hw_dsc_1_2.c @@ -141,7 +141,11 @@ static void sde_hw_dsc_config(struct sde_hw_dsc *hw_dsc, data |= BIT(0); if (mode & DSC_MODE_MULTIPLEX) { - slice_count_per_enc = dsc->config.slice_count >> 1; + if (dsc->dsc_4hsmerge_en) + slice_count_per_enc = dsc->config.slice_count >> 2; + else + slice_count_per_enc = dsc->config.slice_count >> 1; + data |= BIT(1); } @@ -253,6 +257,11 @@ static void sde_hw_dsc_config(struct sde_hw_dsc *hw_dsc, data |= BIT(13); if (!(mode & DSC_MODE_VIDEO)) data |= BIT(17); + if (dsc->dsc_4hsmerge_en) { + data |= dsc->dsc_4hsmerge_padding << 18; + data |= dsc->dsc_4hsmerge_alignment << 22; + data |= BIT(16); + } SDE_REG_WRITE(dsc_c, DSC_CFG + idx, data); } diff --git a/msm/sde/sde_rm.c b/msm/sde/sde_rm.c index 0cd0697201..852287f835 100644 --- a/msm/sde/sde_rm.c +++ b/msm/sde/sde_rm.c @@ -2133,6 +2133,63 @@ bool sde_rm_topology_is_quad_pipe(struct sde_rm *rm, return false; } +bool sde_rm_topology_is_dual_pipe(struct sde_rm *rm, + struct drm_crtc_state *state) +{ + int i; + struct sde_crtc_state *cstate; + uint64_t topology = SDE_RM_TOPOLOGY_NONE; + + if ((!rm) || (!state)) { + pr_err("invalid arguments: rm:%d state:%d\n", + rm == NULL, state == NULL); + return false; + } + + cstate = to_sde_crtc_state(state); + + for (i = 0; i < cstate->num_connectors; i++) { + struct drm_connector *conn = cstate->connectors[i]; + + topology = sde_connector_get_topology_name(conn); + if (TOPOLOGY_DUALPIPE_MERGE_MODE(topology)) + return true; + } + + return false; +} + +bool sde_rm_topology_is_3dmux_dsc(struct sde_rm *rm, + struct drm_crtc_state *state) +{ + int i; + struct sde_crtc_state *cstate; + uint64_t topology = SDE_RM_TOPOLOGY_NONE; + const struct sde_rm_topology_def *def; + int num_lm, num_enc; + + if ((!rm) || (!state)) { + pr_err("invalid arguments: rm:%d state:%d\n", + rm == NULL, state == NULL); + return false; + } + + cstate = to_sde_crtc_state(state); + + for (i = 0; i < cstate->num_connectors; i++) { + struct drm_connector *conn = cstate->connectors[i]; + + topology = sde_connector_get_topology_name(conn); + def = sde_rm_topology_get_topology_def(rm, topology); + num_lm = def->num_lm; + num_enc = def->num_comp_enc; + if (num_lm > num_enc && num_enc) + return true; + } + + return false; +} + /** * _sde_rm_release_rsvp - release resources and release a reservation * @rm: KMS handle diff --git a/msm/sde/sde_rm.h b/msm/sde/sde_rm.h index e0b3bea292..49485635f5 100644 --- a/msm/sde/sde_rm.h +++ b/msm/sde/sde_rm.h @@ -20,6 +20,12 @@ x == SDE_RM_TOPOLOGY_QUADPIPE_DSCMERGE ||\ x == SDE_RM_TOPOLOGY_QUADPIPE_DSC4HSMERGE) +#define TOPOLOGY_DUALPIPE_MERGE_MODE(x) \ + (x == SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE || \ + x == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE || \ + x == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC || \ + x == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC) + /** * enum sde_rm_topology_name - HW resource use case in use by connector * @SDE_RM_TOPOLOGY_NONE: No topology in use currently @@ -343,6 +349,26 @@ static inline int sde_rm_topology_get_num_lm(struct sde_rm *rm, return rm->topology_tbl[topology].num_lm; } +/** + * sde_rm_topology_is_dual_pipe - check if the topology used + * is a dual-pipe mode one + * @rm: SDE Resource Manager handle + * @state: drm state of the crtc + * @return: true if attached connector is in dual-pipe mode + */ +bool sde_rm_topology_is_dual_pipe(struct sde_rm *rm, + struct drm_crtc_state *state); + +/** + * sde_rm_topology_is_3dmux_dsc - check if the topology used + * is a 3dmerge dsc mode one + * @rm: SDE Resource Manager handle + * @state: drm state of the crtc + * @return: true if attached connector is in 3DMERGE DSC mode + */ +bool sde_rm_topology_is_3dmux_dsc(struct sde_rm *rm, + struct drm_crtc_state *state); + /** * sde_rm_topology_is_quad_pipe - check if the topology used * is a quad-pipe mode one diff --git a/msm/sde_dsc_helper.c b/msm/sde_dsc_helper.c index 66654f913e..933d4670bc 100644 --- a/msm/sde_dsc_helper.c +++ b/msm/sde_dsc_helper.c @@ -295,6 +295,8 @@ int sde_dsc_populate_dsc_private_params(struct msm_display_dsc_info *dsc_info, int slice_per_pkt, slice_per_intf; int bytes_in_slice, total_bytes_per_intf; u16 bpp; + u32 bytes_in_dsc_pair; + u32 total_bytes_in_dsc_pair; if (!dsc_info || !dsc_info->config.slice_width || !dsc_info->config.slice_height || @@ -346,6 +348,16 @@ int sde_dsc_populate_dsc_private_params(struct msm_display_dsc_info *dsc_info, dsc_info->bytes_per_pkt = bytes_in_slice * slice_per_pkt; dsc_info->pkt_per_line = slice_per_intf / slice_per_pkt; + bytes_in_dsc_pair = DIV_ROUND_UP(bytes_in_slice * 2, 3); + if (bytes_in_dsc_pair % 8) { + dsc_info->dsc_4hsmerge_padding = 8 - (bytes_in_dsc_pair % 8); + total_bytes_in_dsc_pair = bytes_in_dsc_pair + + dsc_info->dsc_4hsmerge_padding; + if (total_bytes_in_dsc_pair % 16) + dsc_info->dsc_4hsmerge_alignment = 16 - + (total_bytes_in_dsc_pair % 16); + } + return 0; }