diff mbox series

[v1,13/14] drm/msm/disp/dpu1: add dsc supporting functions to dpu encoder

Message ID 1674498274-6010-14-git-send-email-quic_khsieh@quicinc.com
State New
Headers show
Series add display port DSC feature | expand

Commit Message

Kuogee Hsieh Jan. 23, 2023, 6:24 p.m. UTC
Since display Port is an external peripheral, runtime compression
detection is added to handle plug in and unplugged events. Currently
only DSC compression supported. Once DSC compression detected, topology
is static added and used to allocate system resources to accommodate
DSC requirement. DSC related parameters are calculated and committed to
DSC encoder. Also compression information are propagated to phy and
committed to timing engine at video mode. This patch completes DSC
implementation.

Signed-off-by: Kuogee Hsieh <quic_khsieh@quicinc.com>
---
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        | 314 ++++++++++++++++-----
 drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |   5 +-
 .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c   |  34 ++-
 drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h    |   3 +
 drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c             |  10 +-
 5 files changed, 292 insertions(+), 74 deletions(-)

Comments

Neil Armstrong Jan. 25, 2023, 9:01 a.m. UTC | #1
On 23/01/2023 19:24, Kuogee Hsieh wrote:
> Since display Port is an external peripheral, runtime compression
> detection is added to handle plug in and unplugged events. Currently
> only DSC compression supported. Once DSC compression detected, topology
> is static added and used to allocate system resources to accommodate
> DSC requirement. DSC related parameters are calculated and committed to
> DSC encoder. Also compression information are propagated to phy and
> committed to timing engine at video mode. This patch completes DSC
> implementation.
> 
> Signed-off-by: Kuogee Hsieh <quic_khsieh@quicinc.com>
> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c        | 314 ++++++++++++++++-----
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h   |   5 +-
>   .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c   |  34 ++-
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h    |   3 +
>   drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c             |  10 +-
>   5 files changed, 292 insertions(+), 74 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index d2625b3..d7f5f93 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -15,6 +15,7 @@
>   #include <drm/drm_crtc.h>
>   #include <drm/drm_file.h>
>   #include <drm/drm_probe_helper.h>
> +#include <drm/drm_bridge.h>
>   
>   #include "msm_drv.h"
>   #include "dpu_kms.h"
> @@ -30,6 +31,7 @@
>   #include "dpu_crtc.h"
>   #include "dpu_trace.h"
>   #include "dpu_core_irq.h"
> +#include "dpu_dsc_helper.h"
>   #include "disp/msm_disp_snapshot.h"
>   
>   #define DPU_DEBUG_ENC(e, fmt, ...) DRM_DEBUG_ATOMIC("enc%d " fmt,\
> @@ -542,12 +544,12 @@ bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc)
>   	return (num_dsc > 0) && (num_dsc > intf_count);
>   }
>   
> -static struct msm_display_topology dpu_encoder_get_topology(
> +static void dpu_encoder_get_topology(
>   			struct dpu_encoder_virt *dpu_enc,
>   			struct dpu_kms *dpu_kms,
> -			struct drm_display_mode *mode)
> +			struct drm_display_mode *mode,
> +			struct msm_display_topology *topology)
>   {
> -	struct msm_display_topology topology = {0};
>   	int i, intf_count = 0;
>   
>   	for (i = 0; i < MAX_PHYS_ENCODERS_PER_VIRTUAL; i++)
> @@ -567,19 +569,19 @@ static struct msm_display_topology dpu_encoder_get_topology(
>   	 * sufficient number
>   	 */
>   	if (intf_count == 2)
> -		topology.num_lm = 2;
> +		topology->num_lm = 2;
>   	else if (!dpu_kms->catalog->caps->has_3d_merge)
> -		topology.num_lm = 1;
> +		topology->num_lm = 1;
>   	else
> -		topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
> +		topology->num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
>   
>   	if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_DSI) {
>   		if (dpu_kms->catalog->dspp &&
> -			(dpu_kms->catalog->dspp_count >= topology.num_lm))
> -			topology.num_dspp = topology.num_lm;
> +			(dpu_kms->catalog->dspp_count >= topology->num_lm))
> +			topology->num_dspp = topology->num_lm;
>   	}
>   
> -	topology.num_intf = intf_count;
> +	topology->num_intf = intf_count;
>   
>   	if (dpu_enc->dsc) {
>   		/*
> @@ -588,12 +590,31 @@ static struct msm_display_topology dpu_encoder_get_topology(
>   		 * this is power optimal and can drive up to (including) 4k
>   		 * screens
>   		 */
> -		topology.num_dsc = 2;
> -		topology.num_lm = 2;
> -		topology.num_intf = 1;
> +		topology->num_dsc = 2;
> +		topology->num_intf = 1;
> +		topology->num_lm = 2;
>   	}
>   
> -	return topology;
> +	/*
> +	 * default topology for display port DSC implementation.
> +	 * TODO:
> +	 *	change to runtime resource calculation
> +	 */
> +	if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
> +		topology->num_dsc = 0;
> +		topology->num_intf = intf_count;
> +
> +		if (dpu_enc->comp_info) {
> +			/* In case of Display Stream Compression (DSC), we would use
> +			 * 2 encoders, 2 layer mixers and 1 interface
> +			 * this is power optimal and can drive up to (including) 4k
> +			 * screens
> +			 */
> +			topology->num_dsc = 1;
> +			topology->num_intf = 1;
> +			topology->num_lm = 1;
> +		}
> +	}
>   }
>   
>   static int dpu_encoder_virt_atomic_check(
> @@ -605,7 +626,7 @@ static int dpu_encoder_virt_atomic_check(
>   	struct msm_drm_private *priv;
>   	struct dpu_kms *dpu_kms;
>   	struct drm_display_mode *adj_mode;
> -	struct msm_display_topology topology;
> +	struct msm_display_topology *topology;
>   	struct dpu_global_state *global_state;
>   	int i = 0;
>   	int ret = 0;
> @@ -642,7 +663,27 @@ static int dpu_encoder_virt_atomic_check(
>   		}
>   	}
>   
> -	topology = dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode);
> +	/*
> +	 * For display port, at this moment we know panel had been plugged in
> +	 * and dsc supported is detected.
> +	 * however we do not know the details of resolution will be used
> +	 * until mode_set had been done.
> +	 *
> +	 * use default topology to reserve system resource for dsc
> +	 *
> +	 * TODO: run time calculation of topology instead of hardcode it now
> +	 */
> +	if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
> +		struct drm_bridge *bridge;
> +
> +		if (!dpu_enc->comp_info) {
> +			bridge = drm_bridge_chain_get_first_bridge(drm_enc);
> +			dpu_enc->comp_info = msm_dp_bridge_get_compression(bridge);
> +		}
> +	}
> +
> +	topology = &dpu_enc->topology;
> +	dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode, topology);
>   
>   	/* Reserve dynamic resources now. */
>   	if (!ret) {
> @@ -655,7 +696,7 @@ static int dpu_encoder_virt_atomic_check(
>   
>   			if (!crtc_state->active_changed || crtc_state->active)
>   				ret = dpu_rm_reserve(&dpu_kms->rm, global_state,
> -						drm_enc, crtc_state, topology);
> +						drm_enc, crtc_state, *topology);
>   		}
>   	}
>   
> @@ -1009,7 +1050,37 @@ void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc,
>   
>   		if (phys->ops.cleanup_wb_job)
>   			phys->ops.cleanup_wb_job(phys, job);
> +	}
> +}
> +
> +static void dpu_encoder_populate_encoder_phys(struct drm_encoder *drm_enc,
> +					struct dpu_encoder_virt *dpu_enc)
> +{
> +	struct msm_compression_info *comp_info;
> +	struct msm_display_dsc_info *dsc_info;
> +	int i;
> +
> +	if (!dpu_enc->comp_info)
> +		return;
> +
> +	comp_info = dpu_enc->comp_info;
> +	dsc_info = &comp_info->msm_dsc_info;
> +
> +	for (i = 0; i < dpu_enc->num_phys_encs; i++) {
> +		struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
> +
> +		if (!phys)
> +			continue;
> +
> +		phys->comp_type = comp_info->comp_type;
> +		phys->comp_ratio = comp_info->comp_ratio;
>   
> +		if (phys->comp_type == MSM_DISPLAY_COMPRESSION_DSC) {
> +			phys->dsc_extra_pclk_cycle_cnt = dsc_info->pclk_per_line;
> +			phys->dsc_extra_disp_width = dsc_info->extra_width;
> +			phys->dce_bytes_per_line =
> +				dsc_info->bytes_per_pkt * dsc_info->pkt_per_line;
> +		}
>   	}
>   }
>   
> @@ -1050,6 +1121,24 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>   
>   	trace_dpu_enc_mode_set(DRMID(drm_enc));
>   
> +	/*
> +	 * For display port, msm_dp_bridge_mode_set() will conver panel info
> +	 * into dp_mode. This including detail dsc information if it is enabled.
> +	 * after that, msm_dp_bridge_get_compression() will return detail
> +	 * dsc compression info
> +	 */
> +	if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
> +		struct drm_display_mode *mode, *adjusted_mode;
> +		struct drm_bridge *bridge;
> +
> +		mode = &crtc_state->mode;
> +		adjusted_mode = &crtc_state->adjusted_mode;
> +		bridge = drm_bridge_chain_get_first_bridge(drm_enc);
> +		msm_dp_bridge_mode_set(bridge, mode, adjusted_mode);
> +
> +		dpu_enc->comp_info = msm_dp_bridge_get_compression(bridge);
> +	}
> +
>   	/* Query resource that have been reserved in atomic check step. */
>   	num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
>   		drm_enc->base.id, DPU_HW_BLK_PINGPONG, hw_pp,
> @@ -1061,19 +1150,18 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>   	dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
>   		drm_enc->base.id, DPU_HW_BLK_DSPP, hw_dspp,
>   		ARRAY_SIZE(hw_dspp));
> +	num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
> +		drm_enc->base.id, DPU_HW_BLK_DSC,
> +		hw_dsc, ARRAY_SIZE(hw_dsc));
>   
>   	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++)
>   		dpu_enc->hw_pp[i] = i < num_pp ? to_dpu_hw_pingpong(hw_pp[i])
>   						: NULL;
>   
> -	if (dpu_enc->dsc) {
> -		num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
> -							drm_enc->base.id, DPU_HW_BLK_DSC,
> -							hw_dsc, ARRAY_SIZE(hw_dsc));
> -		for (i = 0; i < num_dsc; i++) {
> -			dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]);
> -			dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0);
> -		}
> +	for (i = 0; i < num_dsc; i++) {
> +		dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]);
> +		dpu_enc->hw_pp[i]->dsc = dpu_enc->hw_dsc[i]; /* bind dsc to pp */
> +		dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0);
>   	}
>   
>   	dpu_enc->dsc_mask = dsc_mask;
> @@ -1110,10 +1198,22 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
>   		phys->hw_pp = dpu_enc->hw_pp[i];
>   		phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]);
>   
> +		if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
> +			phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm, phys->intf_idx);
> +
> +		/* phys->hw_intf populated at dpu_encoder_setup_display() */
> +		if (!phys->hw_intf) {
> +			DPU_ERROR_ENC(dpu_enc,
> +				"no intf block assigned at idx: %d\n", i);
> +			return;
> +		}
> +
>   		phys->cached_mode = crtc_state->adjusted_mode;
>   		if (phys->ops.atomic_mode_set)
>   			phys->ops.atomic_mode_set(phys, crtc_state, conn_state);
>   	}
> +
> +	dpu_encoder_populate_encoder_phys(drm_enc, dpu_enc);
>   }
>   
>   static void _dpu_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
> @@ -1208,6 +1308,8 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
>   	mutex_unlock(&dpu_enc->enc_lock);
>   }
>   
> +static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc);
> +
>   static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
>   {
>   	struct dpu_encoder_virt *dpu_enc = NULL;
> @@ -1233,6 +1335,10 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
>   			phys->ops.disable(phys);
>   	}
>   
> +	if (dpu_enc->comp_info && (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS)) {
> +		dpu_encoder_unprep_dsc(dpu_enc);
> +		dpu_enc->comp_info = NULL;
> +	}
>   
>   	/* after phys waits for frame-done, should be no more frames pending */
>   	if (atomic_xchg(&dpu_enc->frame_done_timeout_ms, 0)) {
> @@ -1795,40 +1901,16 @@ static void dpu_encoder_vsync_event_work_handler(struct kthread_work *work)
>   			nsecs_to_jiffies(ktime_to_ns(wakeup_time)));
>   }
>   
> -static u32
> -dpu_encoder_dsc_initial_line_calc(struct drm_dsc_config *dsc,
> -				  u32 enc_ip_width)
> -{
> -	int ssm_delay, total_pixels, soft_slice_per_enc;
> -
> -	soft_slice_per_enc = enc_ip_width / dsc->slice_width;
> -
> -	/*
> -	 * minimum number of initial line pixels is a sum of:
> -	 * 1. sub-stream multiplexer delay (83 groups for 8bpc,
> -	 *    91 for 10 bpc) * 3
> -	 * 2. for two soft slice cases, add extra sub-stream multiplexer * 3
> -	 * 3. the initial xmit delay
> -	 * 4. total pipeline delay through the "lock step" of encoder (47)
> -	 * 5. 6 additional pixels as the output of the rate buffer is
> -	 *    48 bits wide
> -	 */
> -	ssm_delay = ((dsc->bits_per_component < 10) ? 84 : 92);
> -	total_pixels = ssm_delay * 3 + dsc->initial_xmit_delay + 47;
> -	if (soft_slice_per_enc > 1)
> -		total_pixels += (ssm_delay * 3);
> -	return DIV_ROUND_UP(total_pixels, dsc->slice_width);
> -}
> -
>   static void dpu_encoder_dsc_pipe_cfg(struct dpu_encoder_virt *dpu_enc,
>   				     struct dpu_hw_dsc *hw_dsc,
>   				     struct dpu_hw_pingpong *hw_pp,
> -				     struct drm_dsc_config *dsc,
> +				     struct msm_display_dsc_info *dsc_info,
>   				     u32 common_mode,
>   				     u32 initial_lines)
>   {
>   	struct dpu_encoder_phys *cur_master = dpu_enc->cur_master;
>   	struct dpu_hw_ctl *ctl;
> +	struct drm_dsc_config *dsc = &dsc_info->drm_dsc;
>   
>   	ctl = cur_master->hw_ctl;
>   
> @@ -1852,51 +1934,137 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_encoder_virt *dpu_enc,
>   
>   }
>   
> +static void dpu_encoder_dsc_disable(struct dpu_encoder_virt *dpu_enc,
> +				     struct dpu_hw_dsc *hw_dsc,
> +				     struct dpu_hw_pingpong *hw_pp)
> +{
> +	struct dpu_encoder_phys *cur_master = dpu_enc->cur_master;
> +	struct dpu_hw_ctl *ctl;
> +
> +	ctl = cur_master->hw_ctl;
> +
> +	if (hw_dsc->ops.dsc_disable)
> +		hw_dsc->ops.dsc_disable(hw_dsc);
> +
> +	if (hw_pp->ops.disable_dsc)
> +		hw_pp->ops.disable_dsc(hw_pp);
> +
> +}
> +
>   static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
> -				 struct drm_dsc_config *dsc)
> +				 struct msm_display_dsc_info *dsc_info)
>   {
>   	/* coding only for 2LM, 2enc, 1 dsc config */
>   	struct dpu_encoder_phys *enc_master = dpu_enc->cur_master;
>   	struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
>   	struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
> +	struct msm_display_topology *topology = &dpu_enc->topology;
> +	enum dpu_3d_blend_mode mode_3d;
>   	int this_frame_slices;
>   	int intf_ip_w, enc_ip_w;
> -	int dsc_common_mode;
> -	int pic_width;
> -	u32 initial_lines;
> +	int dsc_common_mode = 0;
> +	int dsc_pic_width;
> +	int num_lm, num_dsc, num_intf;
> +	bool dsc_merge, merge_3d, dsc_4hsmerge;
> +	bool disable_merge_3d = false;
> +	int ich_res;
>   	int i;
>   
>   	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
>   		hw_pp[i] = dpu_enc->hw_pp[i];
>   		hw_dsc[i] = dpu_enc->hw_dsc[i];
> +	}
>   
> -		if (!hw_pp[i] || !hw_dsc[i]) {
> -			DPU_ERROR_ENC(dpu_enc, "invalid params for DSC\n");
> -			return;
> -		}
> +	num_lm = topology->num_lm;
> +	num_dsc = topology->num_dsc;
> +	num_intf = topology->num_intf;
> +
> +
> +	mode_3d = (num_lm > num_dsc) ? BLEND_3D_H_ROW_INT : BLEND_3D_NONE;
> +	merge_3d = ((mode_3d != BLEND_3D_NONE) && !(enc_master->hw_intf->cfg.split_link_en)) ?
> +			true : false;
> +
> +	dsc_merge = ((num_dsc > num_intf) && !dsc_info->half_panel_pu &&
> +			!(enc_master->hw_intf->cfg.split_link_en)) ? true : false;
> +	disable_merge_3d = (merge_3d && dsc_info->half_panel_pu) ?  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.
> +	 */
> +	dsc_pic_width = dsc_info->drm_dsc.pic_width;
> +
> +	if (num_dsc > 2) {
> +		dsc_pic_width /= 2;
> +		dsc_info->dsc_4hsmerge_en = dsc_4hsmerge;
>   	}
>   
> -	dsc_common_mode = 0;
> -	pic_width = dsc->pic_width;
> +	this_frame_slices = dsc_pic_width / dsc_info->drm_dsc.slice_width;
> +	intf_ip_w = this_frame_slices * dsc_info->drm_dsc.slice_width;
> +	enc_ip_w = intf_ip_w;
> +
> +	if (!dsc_info->half_panel_pu)
> +		intf_ip_w /= num_intf;
> +	if (!dsc_info->half_panel_pu && (num_dsc > 1))
> +		dsc_common_mode |= DSC_MODE_SPLIT_PANEL;
> +	if (dsc_merge) {
> +		dsc_common_mode |= DSC_MODE_MULTIPLEX;
> +		/*
> +		 * in dsc merge case: when using 2 encoders for the same
> +		 * stream, no. of slices need to be same on both the
> +		 * encoders.
> +		 */
> +		enc_ip_w = intf_ip_w / 2;
> +	}
>   
> -	dsc_common_mode = DSC_MODE_MULTIPLEX | DSC_MODE_SPLIT_PANEL;
>   	if (enc_master->intf_mode == INTF_MODE_VIDEO)
>   		dsc_common_mode |= DSC_MODE_VIDEO;
>   
> -	this_frame_slices = pic_width / dsc->slice_width;
> -	intf_ip_w = this_frame_slices * dsc->slice_width;
> +	dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count;
> +
> +	if (dsc_info->dsc_4hsmerge_en)
> +		dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count >> 2;
> +	else if ((dsc_common_mode & DSC_MODE_MULTIPLEX) || (dsc_info->half_panel_pu))
> +		dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count >> 1;
> +
> +	dpu_dsc_populate_dsc_private_params(dsc_info, intf_ip_w);
> +
> +	dpu_dsc_initial_line_calc(dsc_info, enc_ip_w, dsc_common_mode);
>   
>   	/*
>   	 * dsc merge case: when using 2 encoders for the same stream,
>   	 * no. of slices need to be same on both the encoders.
>   	 */
> -	enc_ip_w = intf_ip_w / 2;
> -	initial_lines = dpu_encoder_dsc_initial_line_calc(dsc, enc_ip_w);
> +	ich_res = dpu_dsc_ich_reset_override_needed(dsc_info->half_panel_pu, dsc_info);
> +
> +	for (i = 0; i < num_dsc; i++) {
> +		dpu_encoder_dsc_pipe_cfg(dpu_enc, hw_dsc[i], hw_pp[i], dsc_info,
> +					dsc_common_mode, 0);
> +	}
> +}
> +
> +static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc)
> +{
> +	/* coding only for 2LM, 2enc, 1 dsc config */
> +	struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
> +	struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
> +	struct msm_display_topology *topology = &dpu_enc->topology;
> +	int i, num_dsc;
>   
>   	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
> -		dpu_encoder_dsc_pipe_cfg(dpu_enc, hw_dsc[i], hw_pp[i], dsc,
> -					dsc_common_mode, initial_lines);
> +		hw_pp[i] = dpu_enc->hw_pp[i];
> +		hw_dsc[i] = dpu_enc->hw_dsc[i];
>   	}
> +
> +	num_dsc = topology->num_dsc;
> +
> +	for (i = 0; i < num_dsc; i++)
> +		dpu_encoder_dsc_disable(dpu_enc, hw_dsc[i], hw_pp[i]);
>   }
>   
>   void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
> @@ -1904,6 +2072,7 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
>   	struct dpu_encoder_virt *dpu_enc;
>   	struct dpu_encoder_phys *phys;
>   	bool needs_hw_reset = false;
> +	bool needs_phy_enable = false;
>   	unsigned int i;
>   
>   	dpu_enc = to_dpu_encoder_virt(drm_enc);
> @@ -1918,6 +2087,9 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
>   			phys->ops.prepare_for_kickoff(phys);
>   		if (phys->enable_state == DPU_ENC_ERR_NEEDS_HW_RESET)
>   			needs_hw_reset = true;
> +
> +		if (phys->enable_state == DPU_ENC_ENABLING)
> +			needs_phy_enable = true;
>   	}
>   	DPU_ATRACE_END("enc_prepare_for_kickoff");
>   
> @@ -1931,8 +2103,10 @@ void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
>   		}
>   	}
>   
> -	if (dpu_enc->dsc)
> -		dpu_encoder_prep_dsc(dpu_enc, dpu_enc->dsc);
> +	if (needs_phy_enable && dpu_enc->comp_info)
> +		dpu_encoder_prep_dsc(dpu_enc, &dpu_enc->comp_info->msm_dsc_info);
> +
> +
>   }
>   
>   bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc)
> @@ -2295,7 +2469,7 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
>   				dpu_kms->catalog->caps->has_idle_pc;
>   
>   	dpu_enc->comp_info = disp_info->comp_info;
> -	if (dpu_enc->comp_info)
> +	if (dpu_enc->comp_info && dpu_enc->comp_info->enabled)
>   		dpu_enc->dsc = &dpu_enc->comp_info->msm_dsc_info.drm_dsc;
>   
>   	mutex_lock(&dpu_enc->enc_lock);
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> index 0569b36..ae4f6a8 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> @@ -1,6 +1,6 @@
>   /* SPDX-License-Identifier: GPL-2.0-only */
>   /*
> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
>    * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
>    */
>   
> @@ -202,6 +202,9 @@ struct dpu_encoder_phys {
>   	int irq[INTR_IDX_MAX];
>   	enum msm_display_compression_type comp_type;
>   	u32 comp_ratio;
> +	u32 dsc_extra_pclk_cycle_cnt;
> +	u32 dsc_extra_disp_width;
> +	u32 dce_bytes_per_line;
>   };
>   
>   static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> index 3330e185..6c7d791 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
> @@ -86,6 +86,11 @@ static void drm_mode_to_intf_timing_params(
>   	timing->underflow_clr = 0xff;
>   	timing->hsync_skew = mode->hskew;
>   
> +	if (phys_enc->comp_type != MSM_DISPLAY_COMPRESSION_NONE) {
> +		timing->compression_en = true;
> +		timing->dce_bytes_per_line = phys_enc->dce_bytes_per_line;
> +	}
> +
>   	/* DSI controller cannot handle active-low sync signals. */
>   	if (phys_enc->hw_intf->cap->type == INTF_DSI) {
>   		timing->hsync_polarity = 0;
> @@ -104,14 +109,36 @@ static void drm_mode_to_intf_timing_params(
>   
>   	/*
>   	 * for DP, divide the horizonal parameters by 2 when
> -	 * widebus is enabled
> +	 * widebus or compression is enabled, irrespective of
> +	 * compression ratio
>   	 */
> -	if (phys_enc->hw_intf->cap->type == INTF_DP && timing->wide_bus_en) {
> +	if (phys_enc->hw_intf->cap->type == INTF_DP &&
> +		(timing->wide_bus_en || (phys_enc->comp_ratio > 1))) {
>   		timing->width = timing->width >> 1;
>   		timing->xres = timing->xres >> 1;
>   		timing->h_back_porch = timing->h_back_porch >> 1;
>   		timing->h_front_porch = timing->h_front_porch >> 1;
>   		timing->hsync_pulse_width = timing->hsync_pulse_width >> 1;
> +
> +		if (phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC &&
> +				(phys_enc->comp_ratio > 1)) {
> +			timing->extra_dto_cycles =
> +					phys_enc->dsc_extra_pclk_cycle_cnt;
> +			timing->width += phys_enc->dsc_extra_disp_width;
> +			timing->h_back_porch +=
> +					phys_enc->dsc_extra_disp_width;
> +		}
> +	}
> +
> +	/*
> +	 * for DSI, if compression is enabled, then divide the horizonal active
> +	 * timing parameters by compression ratio.
> +	 */
> +	if ((phys_enc->hw_intf->cap->type != INTF_DP) &&
> +			((phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC))) {
> +		// adjust active dimensions
> +		timing->width = DIV_ROUND_UP(timing->width, phys_enc->comp_ratio);
> +		timing->xres = DIV_ROUND_UP(timing->xres, phys_enc->comp_ratio);
>   	}
>   }
>   
> @@ -281,6 +308,9 @@ static void dpu_encoder_phys_vid_setup_timing_engine(
>   
>   	phys_enc->hw_intf->hw_rev = phys_enc->dpu_kms->core_rev;
>   
> +	if (phys_enc->hw_pp->dsc)
> +		intf_cfg.dsc_num = phys_enc->hw_pp->dsc->idx;

Why ??? it breaks when in multiplex mode and we have multiple DSCs !

> +
>   	spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
>   	phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf,
>   			&timing_params, fmt);
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
> index c002234..ee71cee 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
> @@ -1,5 +1,6 @@
>   /* SPDX-License-Identifier: GPL-2.0-only */
>   /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
>    */
>   
>   #ifndef _DPU_HW_PINGPONG_H
> @@ -8,6 +9,7 @@
>   #include "dpu_hw_catalog.h"
>   #include "dpu_hw_mdss.h"
>   #include "dpu_hw_util.h"
> +#include "dpu_hw_dsc.h"
>   
>   #define DITHER_MATRIX_SZ 16
>   
> @@ -149,6 +151,7 @@ struct dpu_hw_pingpong {
>   	enum dpu_pingpong idx;
>   	const struct dpu_pingpong_cfg *caps;
>   	struct dpu_hw_merge_3d *merge_3d;
> +	struct dpu_hw_dsc *dsc;
>   
>   	/* ops */
>   	struct dpu_hw_pingpong_ops ops;
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
> index 396429e..bb22ec8 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
> @@ -1,6 +1,7 @@
>   // SPDX-License-Identifier: GPL-2.0-only
>   /*
>    * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
>    */
>   
>   #define pr_fmt(fmt)	"[drm:%s] " fmt, __func__
> @@ -246,6 +247,11 @@ int dpu_rm_init(struct dpu_rm *rm,
>   		struct dpu_hw_dsc *hw;
>   		const struct dpu_dsc_cfg *dsc = &cat->dsc[i];
>   
> +		if (dsc->id < DSC_0 || dsc->id >= DSC_MAX) {
> +			DPU_ERROR("skip dsc %d with invalid id\n", dsc->id);
> +			continue;
> +		}
> +
>   		hw = dpu_hw_dsc_init(dsc->id, mmio, cat);
>   		if (IS_ERR_OR_NULL(hw)) {
>   			rc = PTR_ERR(hw);
> @@ -535,8 +541,10 @@ static int _dpu_rm_make_reservation(
>   	}
>   
>   	ret  = _dpu_rm_reserve_dsc(rm, global_state, enc, &reqs->topology);
> -	if (ret)
> +	if (ret) {
> +		DPU_ERROR("unable to find appropriate DSC\n");
>   		return ret;
> +	}
>   
>   	return ret;
>   }
diff mbox series

Patch

diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index d2625b3..d7f5f93 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -15,6 +15,7 @@ 
 #include <drm/drm_crtc.h>
 #include <drm/drm_file.h>
 #include <drm/drm_probe_helper.h>
+#include <drm/drm_bridge.h>
 
 #include "msm_drv.h"
 #include "dpu_kms.h"
@@ -30,6 +31,7 @@ 
 #include "dpu_crtc.h"
 #include "dpu_trace.h"
 #include "dpu_core_irq.h"
+#include "dpu_dsc_helper.h"
 #include "disp/msm_disp_snapshot.h"
 
 #define DPU_DEBUG_ENC(e, fmt, ...) DRM_DEBUG_ATOMIC("enc%d " fmt,\
@@ -542,12 +544,12 @@  bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc)
 	return (num_dsc > 0) && (num_dsc > intf_count);
 }
 
-static struct msm_display_topology dpu_encoder_get_topology(
+static void dpu_encoder_get_topology(
 			struct dpu_encoder_virt *dpu_enc,
 			struct dpu_kms *dpu_kms,
-			struct drm_display_mode *mode)
+			struct drm_display_mode *mode,
+			struct msm_display_topology *topology)
 {
-	struct msm_display_topology topology = {0};
 	int i, intf_count = 0;
 
 	for (i = 0; i < MAX_PHYS_ENCODERS_PER_VIRTUAL; i++)
@@ -567,19 +569,19 @@  static struct msm_display_topology dpu_encoder_get_topology(
 	 * sufficient number
 	 */
 	if (intf_count == 2)
-		topology.num_lm = 2;
+		topology->num_lm = 2;
 	else if (!dpu_kms->catalog->caps->has_3d_merge)
-		topology.num_lm = 1;
+		topology->num_lm = 1;
 	else
-		topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
+		topology->num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
 
 	if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_DSI) {
 		if (dpu_kms->catalog->dspp &&
-			(dpu_kms->catalog->dspp_count >= topology.num_lm))
-			topology.num_dspp = topology.num_lm;
+			(dpu_kms->catalog->dspp_count >= topology->num_lm))
+			topology->num_dspp = topology->num_lm;
 	}
 
-	topology.num_intf = intf_count;
+	topology->num_intf = intf_count;
 
 	if (dpu_enc->dsc) {
 		/*
@@ -588,12 +590,31 @@  static struct msm_display_topology dpu_encoder_get_topology(
 		 * this is power optimal and can drive up to (including) 4k
 		 * screens
 		 */
-		topology.num_dsc = 2;
-		topology.num_lm = 2;
-		topology.num_intf = 1;
+		topology->num_dsc = 2;
+		topology->num_intf = 1;
+		topology->num_lm = 2;
 	}
 
-	return topology;
+	/*
+	 * default topology for display port DSC implementation.
+	 * TODO:
+	 *	change to runtime resource calculation
+	 */
+	if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
+		topology->num_dsc = 0;
+		topology->num_intf = intf_count;
+
+		if (dpu_enc->comp_info) {
+			/* In case of Display Stream Compression (DSC), we would use
+			 * 2 encoders, 2 layer mixers and 1 interface
+			 * this is power optimal and can drive up to (including) 4k
+			 * screens
+			 */
+			topology->num_dsc = 1;
+			topology->num_intf = 1;
+			topology->num_lm = 1;
+		}
+	}
 }
 
 static int dpu_encoder_virt_atomic_check(
@@ -605,7 +626,7 @@  static int dpu_encoder_virt_atomic_check(
 	struct msm_drm_private *priv;
 	struct dpu_kms *dpu_kms;
 	struct drm_display_mode *adj_mode;
-	struct msm_display_topology topology;
+	struct msm_display_topology *topology;
 	struct dpu_global_state *global_state;
 	int i = 0;
 	int ret = 0;
@@ -642,7 +663,27 @@  static int dpu_encoder_virt_atomic_check(
 		}
 	}
 
-	topology = dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode);
+	/*
+	 * For display port, at this moment we know panel had been plugged in
+	 * and dsc supported is detected.
+	 * however we do not know the details of resolution will be used
+	 * until mode_set had been done.
+	 *
+	 * use default topology to reserve system resource for dsc
+	 *
+	 * TODO: run time calculation of topology instead of hardcode it now
+	 */
+	if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
+		struct drm_bridge *bridge;
+
+		if (!dpu_enc->comp_info) {
+			bridge = drm_bridge_chain_get_first_bridge(drm_enc);
+			dpu_enc->comp_info = msm_dp_bridge_get_compression(bridge);
+		}
+	}
+
+	topology = &dpu_enc->topology;
+	dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode, topology);
 
 	/* Reserve dynamic resources now. */
 	if (!ret) {
@@ -655,7 +696,7 @@  static int dpu_encoder_virt_atomic_check(
 
 			if (!crtc_state->active_changed || crtc_state->active)
 				ret = dpu_rm_reserve(&dpu_kms->rm, global_state,
-						drm_enc, crtc_state, topology);
+						drm_enc, crtc_state, *topology);
 		}
 	}
 
@@ -1009,7 +1050,37 @@  void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc,
 
 		if (phys->ops.cleanup_wb_job)
 			phys->ops.cleanup_wb_job(phys, job);
+	}
+}
+
+static void dpu_encoder_populate_encoder_phys(struct drm_encoder *drm_enc,
+					struct dpu_encoder_virt *dpu_enc)
+{
+	struct msm_compression_info *comp_info;
+	struct msm_display_dsc_info *dsc_info;
+	int i;
+
+	if (!dpu_enc->comp_info)
+		return;
+
+	comp_info = dpu_enc->comp_info;
+	dsc_info = &comp_info->msm_dsc_info;
+
+	for (i = 0; i < dpu_enc->num_phys_encs; i++) {
+		struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
+
+		if (!phys)
+			continue;
+
+		phys->comp_type = comp_info->comp_type;
+		phys->comp_ratio = comp_info->comp_ratio;
 
+		if (phys->comp_type == MSM_DISPLAY_COMPRESSION_DSC) {
+			phys->dsc_extra_pclk_cycle_cnt = dsc_info->pclk_per_line;
+			phys->dsc_extra_disp_width = dsc_info->extra_width;
+			phys->dce_bytes_per_line =
+				dsc_info->bytes_per_pkt * dsc_info->pkt_per_line;
+		}
 	}
 }
 
@@ -1050,6 +1121,24 @@  static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
 
 	trace_dpu_enc_mode_set(DRMID(drm_enc));
 
+	/*
+	 * For display port, msm_dp_bridge_mode_set() will conver panel info
+	 * into dp_mode. This including detail dsc information if it is enabled.
+	 * after that, msm_dp_bridge_get_compression() will return detail
+	 * dsc compression info
+	 */
+	if (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS) {
+		struct drm_display_mode *mode, *adjusted_mode;
+		struct drm_bridge *bridge;
+
+		mode = &crtc_state->mode;
+		adjusted_mode = &crtc_state->adjusted_mode;
+		bridge = drm_bridge_chain_get_first_bridge(drm_enc);
+		msm_dp_bridge_mode_set(bridge, mode, adjusted_mode);
+
+		dpu_enc->comp_info = msm_dp_bridge_get_compression(bridge);
+	}
+
 	/* Query resource that have been reserved in atomic check step. */
 	num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
 		drm_enc->base.id, DPU_HW_BLK_PINGPONG, hw_pp,
@@ -1061,19 +1150,18 @@  static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
 	dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
 		drm_enc->base.id, DPU_HW_BLK_DSPP, hw_dspp,
 		ARRAY_SIZE(hw_dspp));
+	num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
+		drm_enc->base.id, DPU_HW_BLK_DSC,
+		hw_dsc, ARRAY_SIZE(hw_dsc));
 
 	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++)
 		dpu_enc->hw_pp[i] = i < num_pp ? to_dpu_hw_pingpong(hw_pp[i])
 						: NULL;
 
-	if (dpu_enc->dsc) {
-		num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state,
-							drm_enc->base.id, DPU_HW_BLK_DSC,
-							hw_dsc, ARRAY_SIZE(hw_dsc));
-		for (i = 0; i < num_dsc; i++) {
-			dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]);
-			dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0);
-		}
+	for (i = 0; i < num_dsc; i++) {
+		dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]);
+		dpu_enc->hw_pp[i]->dsc = dpu_enc->hw_dsc[i]; /* bind dsc to pp */
+		dsc_mask |= BIT(dpu_enc->hw_dsc[i]->idx - DSC_0);
 	}
 
 	dpu_enc->dsc_mask = dsc_mask;
@@ -1110,10 +1198,22 @@  static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
 		phys->hw_pp = dpu_enc->hw_pp[i];
 		phys->hw_ctl = to_dpu_hw_ctl(hw_ctl[i]);
 
+		if (phys->intf_idx >= INTF_0 && phys->intf_idx < INTF_MAX)
+			phys->hw_intf = dpu_rm_get_intf(&dpu_kms->rm, phys->intf_idx);
+
+		/* phys->hw_intf populated at dpu_encoder_setup_display() */
+		if (!phys->hw_intf) {
+			DPU_ERROR_ENC(dpu_enc,
+				"no intf block assigned at idx: %d\n", i);
+			return;
+		}
+
 		phys->cached_mode = crtc_state->adjusted_mode;
 		if (phys->ops.atomic_mode_set)
 			phys->ops.atomic_mode_set(phys, crtc_state, conn_state);
 	}
+
+	dpu_encoder_populate_encoder_phys(drm_enc, dpu_enc);
 }
 
 static void _dpu_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
@@ -1208,6 +1308,8 @@  static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
 	mutex_unlock(&dpu_enc->enc_lock);
 }
 
+static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc);
+
 static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
 {
 	struct dpu_encoder_virt *dpu_enc = NULL;
@@ -1233,6 +1335,10 @@  static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
 			phys->ops.disable(phys);
 	}
 
+	if (dpu_enc->comp_info && (dpu_enc->disp_info.intf_type == DRM_MODE_ENCODER_TMDS)) {
+		dpu_encoder_unprep_dsc(dpu_enc);
+		dpu_enc->comp_info = NULL;
+	}
 
 	/* after phys waits for frame-done, should be no more frames pending */
 	if (atomic_xchg(&dpu_enc->frame_done_timeout_ms, 0)) {
@@ -1795,40 +1901,16 @@  static void dpu_encoder_vsync_event_work_handler(struct kthread_work *work)
 			nsecs_to_jiffies(ktime_to_ns(wakeup_time)));
 }
 
-static u32
-dpu_encoder_dsc_initial_line_calc(struct drm_dsc_config *dsc,
-				  u32 enc_ip_width)
-{
-	int ssm_delay, total_pixels, soft_slice_per_enc;
-
-	soft_slice_per_enc = enc_ip_width / dsc->slice_width;
-
-	/*
-	 * minimum number of initial line pixels is a sum of:
-	 * 1. sub-stream multiplexer delay (83 groups for 8bpc,
-	 *    91 for 10 bpc) * 3
-	 * 2. for two soft slice cases, add extra sub-stream multiplexer * 3
-	 * 3. the initial xmit delay
-	 * 4. total pipeline delay through the "lock step" of encoder (47)
-	 * 5. 6 additional pixels as the output of the rate buffer is
-	 *    48 bits wide
-	 */
-	ssm_delay = ((dsc->bits_per_component < 10) ? 84 : 92);
-	total_pixels = ssm_delay * 3 + dsc->initial_xmit_delay + 47;
-	if (soft_slice_per_enc > 1)
-		total_pixels += (ssm_delay * 3);
-	return DIV_ROUND_UP(total_pixels, dsc->slice_width);
-}
-
 static void dpu_encoder_dsc_pipe_cfg(struct dpu_encoder_virt *dpu_enc,
 				     struct dpu_hw_dsc *hw_dsc,
 				     struct dpu_hw_pingpong *hw_pp,
-				     struct drm_dsc_config *dsc,
+				     struct msm_display_dsc_info *dsc_info,
 				     u32 common_mode,
 				     u32 initial_lines)
 {
 	struct dpu_encoder_phys *cur_master = dpu_enc->cur_master;
 	struct dpu_hw_ctl *ctl;
+	struct drm_dsc_config *dsc = &dsc_info->drm_dsc;
 
 	ctl = cur_master->hw_ctl;
 
@@ -1852,51 +1934,137 @@  static void dpu_encoder_dsc_pipe_cfg(struct dpu_encoder_virt *dpu_enc,
 
 }
 
+static void dpu_encoder_dsc_disable(struct dpu_encoder_virt *dpu_enc,
+				     struct dpu_hw_dsc *hw_dsc,
+				     struct dpu_hw_pingpong *hw_pp)
+{
+	struct dpu_encoder_phys *cur_master = dpu_enc->cur_master;
+	struct dpu_hw_ctl *ctl;
+
+	ctl = cur_master->hw_ctl;
+
+	if (hw_dsc->ops.dsc_disable)
+		hw_dsc->ops.dsc_disable(hw_dsc);
+
+	if (hw_pp->ops.disable_dsc)
+		hw_pp->ops.disable_dsc(hw_pp);
+
+}
+
 static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc,
-				 struct drm_dsc_config *dsc)
+				 struct msm_display_dsc_info *dsc_info)
 {
 	/* coding only for 2LM, 2enc, 1 dsc config */
 	struct dpu_encoder_phys *enc_master = dpu_enc->cur_master;
 	struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
 	struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
+	struct msm_display_topology *topology = &dpu_enc->topology;
+	enum dpu_3d_blend_mode mode_3d;
 	int this_frame_slices;
 	int intf_ip_w, enc_ip_w;
-	int dsc_common_mode;
-	int pic_width;
-	u32 initial_lines;
+	int dsc_common_mode = 0;
+	int dsc_pic_width;
+	int num_lm, num_dsc, num_intf;
+	bool dsc_merge, merge_3d, dsc_4hsmerge;
+	bool disable_merge_3d = false;
+	int ich_res;
 	int i;
 
 	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
 		hw_pp[i] = dpu_enc->hw_pp[i];
 		hw_dsc[i] = dpu_enc->hw_dsc[i];
+	}
 
-		if (!hw_pp[i] || !hw_dsc[i]) {
-			DPU_ERROR_ENC(dpu_enc, "invalid params for DSC\n");
-			return;
-		}
+	num_lm = topology->num_lm;
+	num_dsc = topology->num_dsc;
+	num_intf = topology->num_intf;
+
+
+	mode_3d = (num_lm > num_dsc) ? BLEND_3D_H_ROW_INT : BLEND_3D_NONE;
+	merge_3d = ((mode_3d != BLEND_3D_NONE) && !(enc_master->hw_intf->cfg.split_link_en)) ?
+			true : false;
+
+	dsc_merge = ((num_dsc > num_intf) && !dsc_info->half_panel_pu &&
+			!(enc_master->hw_intf->cfg.split_link_en)) ? true : false;
+	disable_merge_3d = (merge_3d && dsc_info->half_panel_pu) ?  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.
+	 */
+	dsc_pic_width = dsc_info->drm_dsc.pic_width;
+
+	if (num_dsc > 2) {
+		dsc_pic_width /= 2;
+		dsc_info->dsc_4hsmerge_en = dsc_4hsmerge;
 	}
 
-	dsc_common_mode = 0;
-	pic_width = dsc->pic_width;
+	this_frame_slices = dsc_pic_width / dsc_info->drm_dsc.slice_width;
+	intf_ip_w = this_frame_slices * dsc_info->drm_dsc.slice_width;
+	enc_ip_w = intf_ip_w;
+
+	if (!dsc_info->half_panel_pu)
+		intf_ip_w /= num_intf;
+	if (!dsc_info->half_panel_pu && (num_dsc > 1))
+		dsc_common_mode |= DSC_MODE_SPLIT_PANEL;
+	if (dsc_merge) {
+		dsc_common_mode |= DSC_MODE_MULTIPLEX;
+		/*
+		 * in dsc merge case: when using 2 encoders for the same
+		 * stream, no. of slices need to be same on both the
+		 * encoders.
+		 */
+		enc_ip_w = intf_ip_w / 2;
+	}
 
-	dsc_common_mode = DSC_MODE_MULTIPLEX | DSC_MODE_SPLIT_PANEL;
 	if (enc_master->intf_mode == INTF_MODE_VIDEO)
 		dsc_common_mode |= DSC_MODE_VIDEO;
 
-	this_frame_slices = pic_width / dsc->slice_width;
-	intf_ip_w = this_frame_slices * dsc->slice_width;
+	dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count;
+
+	if (dsc_info->dsc_4hsmerge_en)
+		dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count >> 2;
+	else if ((dsc_common_mode & DSC_MODE_MULTIPLEX) || (dsc_info->half_panel_pu))
+		dsc_info->num_active_ss_per_enc = dsc_info->drm_dsc.slice_count >> 1;
+
+	dpu_dsc_populate_dsc_private_params(dsc_info, intf_ip_w);
+
+	dpu_dsc_initial_line_calc(dsc_info, enc_ip_w, dsc_common_mode);
 
 	/*
 	 * dsc merge case: when using 2 encoders for the same stream,
 	 * no. of slices need to be same on both the encoders.
 	 */
-	enc_ip_w = intf_ip_w / 2;
-	initial_lines = dpu_encoder_dsc_initial_line_calc(dsc, enc_ip_w);
+	ich_res = dpu_dsc_ich_reset_override_needed(dsc_info->half_panel_pu, dsc_info);
+
+	for (i = 0; i < num_dsc; i++) {
+		dpu_encoder_dsc_pipe_cfg(dpu_enc, hw_dsc[i], hw_pp[i], dsc_info,
+					dsc_common_mode, 0);
+	}
+}
+
+static void dpu_encoder_unprep_dsc(struct dpu_encoder_virt *dpu_enc)
+{
+	/* coding only for 2LM, 2enc, 1 dsc config */
+	struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
+	struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
+	struct msm_display_topology *topology = &dpu_enc->topology;
+	int i, num_dsc;
 
 	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
-		dpu_encoder_dsc_pipe_cfg(dpu_enc, hw_dsc[i], hw_pp[i], dsc,
-					dsc_common_mode, initial_lines);
+		hw_pp[i] = dpu_enc->hw_pp[i];
+		hw_dsc[i] = dpu_enc->hw_dsc[i];
 	}
+
+	num_dsc = topology->num_dsc;
+
+	for (i = 0; i < num_dsc; i++)
+		dpu_encoder_dsc_disable(dpu_enc, hw_dsc[i], hw_pp[i]);
 }
 
 void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
@@ -1904,6 +2072,7 @@  void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
 	struct dpu_encoder_virt *dpu_enc;
 	struct dpu_encoder_phys *phys;
 	bool needs_hw_reset = false;
+	bool needs_phy_enable = false;
 	unsigned int i;
 
 	dpu_enc = to_dpu_encoder_virt(drm_enc);
@@ -1918,6 +2087,9 @@  void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
 			phys->ops.prepare_for_kickoff(phys);
 		if (phys->enable_state == DPU_ENC_ERR_NEEDS_HW_RESET)
 			needs_hw_reset = true;
+
+		if (phys->enable_state == DPU_ENC_ENABLING)
+			needs_phy_enable = true;
 	}
 	DPU_ATRACE_END("enc_prepare_for_kickoff");
 
@@ -1931,8 +2103,10 @@  void dpu_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
 		}
 	}
 
-	if (dpu_enc->dsc)
-		dpu_encoder_prep_dsc(dpu_enc, dpu_enc->dsc);
+	if (needs_phy_enable && dpu_enc->comp_info)
+		dpu_encoder_prep_dsc(dpu_enc, &dpu_enc->comp_info->msm_dsc_info);
+
+
 }
 
 bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc)
@@ -2295,7 +2469,7 @@  static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
 				dpu_kms->catalog->caps->has_idle_pc;
 
 	dpu_enc->comp_info = disp_info->comp_info;
-	if (dpu_enc->comp_info)
+	if (dpu_enc->comp_info && dpu_enc->comp_info->enabled)
 		dpu_enc->dsc = &dpu_enc->comp_info->msm_dsc_info.drm_dsc;
 
 	mutex_lock(&dpu_enc->enc_lock);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
index 0569b36..ae4f6a8 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -1,6 +1,6 @@ 
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
  */
 
@@ -202,6 +202,9 @@  struct dpu_encoder_phys {
 	int irq[INTR_IDX_MAX];
 	enum msm_display_compression_type comp_type;
 	u32 comp_ratio;
+	u32 dsc_extra_pclk_cycle_cnt;
+	u32 dsc_extra_disp_width;
+	u32 dce_bytes_per_line;
 };
 
 static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
index 3330e185..6c7d791 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
@@ -86,6 +86,11 @@  static void drm_mode_to_intf_timing_params(
 	timing->underflow_clr = 0xff;
 	timing->hsync_skew = mode->hskew;
 
+	if (phys_enc->comp_type != MSM_DISPLAY_COMPRESSION_NONE) {
+		timing->compression_en = true;
+		timing->dce_bytes_per_line = phys_enc->dce_bytes_per_line;
+	}
+
 	/* DSI controller cannot handle active-low sync signals. */
 	if (phys_enc->hw_intf->cap->type == INTF_DSI) {
 		timing->hsync_polarity = 0;
@@ -104,14 +109,36 @@  static void drm_mode_to_intf_timing_params(
 
 	/*
 	 * for DP, divide the horizonal parameters by 2 when
-	 * widebus is enabled
+	 * widebus or compression is enabled, irrespective of
+	 * compression ratio
 	 */
-	if (phys_enc->hw_intf->cap->type == INTF_DP && timing->wide_bus_en) {
+	if (phys_enc->hw_intf->cap->type == INTF_DP &&
+		(timing->wide_bus_en || (phys_enc->comp_ratio > 1))) {
 		timing->width = timing->width >> 1;
 		timing->xres = timing->xres >> 1;
 		timing->h_back_porch = timing->h_back_porch >> 1;
 		timing->h_front_porch = timing->h_front_porch >> 1;
 		timing->hsync_pulse_width = timing->hsync_pulse_width >> 1;
+
+		if (phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC &&
+				(phys_enc->comp_ratio > 1)) {
+			timing->extra_dto_cycles =
+					phys_enc->dsc_extra_pclk_cycle_cnt;
+			timing->width += phys_enc->dsc_extra_disp_width;
+			timing->h_back_porch +=
+					phys_enc->dsc_extra_disp_width;
+		}
+	}
+
+	/*
+	 * for DSI, if compression is enabled, then divide the horizonal active
+	 * timing parameters by compression ratio.
+	 */
+	if ((phys_enc->hw_intf->cap->type != INTF_DP) &&
+			((phys_enc->comp_type == MSM_DISPLAY_COMPRESSION_DSC))) {
+		// adjust active dimensions
+		timing->width = DIV_ROUND_UP(timing->width, phys_enc->comp_ratio);
+		timing->xres = DIV_ROUND_UP(timing->xres, phys_enc->comp_ratio);
 	}
 }
 
@@ -281,6 +308,9 @@  static void dpu_encoder_phys_vid_setup_timing_engine(
 
 	phys_enc->hw_intf->hw_rev = phys_enc->dpu_kms->core_rev;
 
+	if (phys_enc->hw_pp->dsc)
+		intf_cfg.dsc_num = phys_enc->hw_pp->dsc->idx;
+
 	spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
 	phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf,
 			&timing_params, fmt);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
index c002234..ee71cee 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h
@@ -1,5 +1,6 @@ 
 /* SPDX-License-Identifier: GPL-2.0-only */
 /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _DPU_HW_PINGPONG_H
@@ -8,6 +9,7 @@ 
 #include "dpu_hw_catalog.h"
 #include "dpu_hw_mdss.h"
 #include "dpu_hw_util.h"
+#include "dpu_hw_dsc.h"
 
 #define DITHER_MATRIX_SZ 16
 
@@ -149,6 +151,7 @@  struct dpu_hw_pingpong {
 	enum dpu_pingpong idx;
 	const struct dpu_pingpong_cfg *caps;
 	struct dpu_hw_merge_3d *merge_3d;
+	struct dpu_hw_dsc *dsc;
 
 	/* ops */
 	struct dpu_hw_pingpong_ops ops;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
index 396429e..bb22ec8 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
@@ -1,6 +1,7 @@ 
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #define pr_fmt(fmt)	"[drm:%s] " fmt, __func__
@@ -246,6 +247,11 @@  int dpu_rm_init(struct dpu_rm *rm,
 		struct dpu_hw_dsc *hw;
 		const struct dpu_dsc_cfg *dsc = &cat->dsc[i];
 
+		if (dsc->id < DSC_0 || dsc->id >= DSC_MAX) {
+			DPU_ERROR("skip dsc %d with invalid id\n", dsc->id);
+			continue;
+		}
+
 		hw = dpu_hw_dsc_init(dsc->id, mmio, cat);
 		if (IS_ERR_OR_NULL(hw)) {
 			rc = PTR_ERR(hw);
@@ -535,8 +541,10 @@  static int _dpu_rm_make_reservation(
 	}
 
 	ret  = _dpu_rm_reserve_dsc(rm, global_state, enc, &reqs->topology);
-	if (ret)
+	if (ret) {
+		DPU_ERROR("unable to find appropriate DSC\n");
 		return ret;
+	}
 
 	return ret;
 }