Эх сурвалжийг харах

Merge "disp: msm: sde: enable dedicated CWB feature on Waipio"

qctecmdr 4 жил өмнө
parent
commit
1393447cd5

+ 12 - 1
msm/sde/sde_crtc.c

@@ -5454,6 +5454,12 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
 		{CAPTURE_DSPP_OUT, "capture_pp_out"},
 		{CAPTURE_DSPP_OUT, "capture_pp_out"},
 	};
 	};
 
 
+	static const struct drm_prop_enum_list e_dcwb_data_points[] = {
+		{CAPTURE_MIXER_OUT, "capture_mixer_out"},
+		{CAPTURE_DSPP_OUT, "capture_pp_out"},
+		{CAPTURE_DEMURA_OUT, "capture_demura_out"},
+	};
+
 	static const struct drm_prop_enum_list e_idle_pc_state[] = {
 	static const struct drm_prop_enum_list e_idle_pc_state[] = {
 		{IDLE_PC_NONE, "idle_pc_none"},
 		{IDLE_PC_NONE, "idle_pc_none"},
 		{IDLE_PC_ENABLE, "idle_pc_enable"},
 		{IDLE_PC_ENABLE, "idle_pc_enable"},
@@ -5524,7 +5530,12 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
 			ARRAY_SIZE(e_idle_pc_state), 0,
 			ARRAY_SIZE(e_idle_pc_state), 0,
 			CRTC_PROP_IDLE_PC_STATE);
 			CRTC_PROP_IDLE_PC_STATE);
 
 
-	if (catalog->has_cwb_support)
+	if (catalog->has_dedicated_cwb_support)
+		msm_property_install_enum(&sde_crtc->property_info,
+				"capture_mode", 0, 0, e_dcwb_data_points,
+				ARRAY_SIZE(e_dcwb_data_points), 0,
+				CRTC_PROP_CAPTURE_OUTPUT);
+	else if (catalog->has_cwb_support)
 		msm_property_install_enum(&sde_crtc->property_info,
 		msm_property_install_enum(&sde_crtc->property_info,
 				"capture_mode", 0, 0, e_cwb_data_points,
 				"capture_mode", 0, 0, e_cwb_data_points,
 				ARRAY_SIZE(e_cwb_data_points), 0,
 				ARRAY_SIZE(e_cwb_data_points), 0,

+ 3 - 1
msm/sde/sde_crtc.h

@@ -56,10 +56,12 @@ enum sde_crtc_client_type {
  * enum sde_crtc_output_capture_point
  * enum sde_crtc_output_capture_point
  * @MIXER_OUT : capture mixer output
  * @MIXER_OUT : capture mixer output
  * @DSPP_OUT : capture output of dspp
  * @DSPP_OUT : capture output of dspp
+ * @CAPTURE_DEMURA_OUT : capture output of demura
  */
  */
 enum sde_crtc_output_capture_point {
 enum sde_crtc_output_capture_point {
 	CAPTURE_MIXER_OUT,
 	CAPTURE_MIXER_OUT,
-	CAPTURE_DSPP_OUT
+	CAPTURE_DSPP_OUT,
+	CAPTURE_DEMURA_OUT
 };
 };
 
 
 /**
 /**

+ 2 - 0
msm/sde/sde_encoder_phys.h

@@ -201,6 +201,7 @@ struct sde_encoder_phys_ops {
  * @INTR_IDX_PP3_OVFL: Pingpong overflow interrupt on PP3 for Concurrent WB
  * @INTR_IDX_PP3_OVFL: Pingpong overflow interrupt on PP3 for Concurrent WB
  * @INTR_IDX_PP4_OVFL: Pingpong overflow interrupt on PP4 for Concurrent WB
  * @INTR_IDX_PP4_OVFL: Pingpong overflow interrupt on PP4 for Concurrent WB
  * @INTR_IDX_PP5_OVFL: Pingpong overflow interrupt on PP5 for Concurrent WB
  * @INTR_IDX_PP5_OVFL: Pingpong overflow interrupt on PP5 for Concurrent WB
+ * @INTR_IDX_PP_CWB_OVFL: Pingpong overflow interrupt on PP_CWB0/1 for Concurrent WB
  * @INTR_IDX_AUTOREFRESH_DONE:  Autorefresh done for cmd mode panel meaning
  * @INTR_IDX_AUTOREFRESH_DONE:  Autorefresh done for cmd mode panel meaning
  *                              autorefresh has triggered a double buffer flip
  *                              autorefresh has triggered a double buffer flip
  * @INTR_IDX_WRPTR:    Writepointer start interrupt for cmd mode panel
  * @INTR_IDX_WRPTR:    Writepointer start interrupt for cmd mode panel
@@ -218,6 +219,7 @@ enum sde_intr_idx {
 	INTR_IDX_PP3_OVFL,
 	INTR_IDX_PP3_OVFL,
 	INTR_IDX_PP4_OVFL,
 	INTR_IDX_PP4_OVFL,
 	INTR_IDX_PP5_OVFL,
 	INTR_IDX_PP5_OVFL,
+	INTR_IDX_PP_CWB_OVFL,
 	INTR_IDX_WRPTR,
 	INTR_IDX_WRPTR,
 	INTR_IDX_MAX,
 	INTR_IDX_MAX,
 };
 };

+ 126 - 70
msm/sde/sde_encoder_phys_wb.c

@@ -30,7 +30,7 @@
 
 
 static const u32 cwb_irq_tbl[PINGPONG_MAX] = {SDE_NONE, INTR_IDX_PP1_OVFL,
 static const u32 cwb_irq_tbl[PINGPONG_MAX] = {SDE_NONE, INTR_IDX_PP1_OVFL,
 	INTR_IDX_PP2_OVFL, INTR_IDX_PP3_OVFL, INTR_IDX_PP4_OVFL,
 	INTR_IDX_PP2_OVFL, INTR_IDX_PP3_OVFL, INTR_IDX_PP4_OVFL,
-	INTR_IDX_PP5_OVFL, SDE_NONE, SDE_NONE};
+	INTR_IDX_PP5_OVFL, INTR_IDX_PP_CWB_OVFL, SDE_NONE};
 
 
 /**
 /**
  * sde_rgb2yuv_601l - rgb to yuv color space conversion matrix
  * sde_rgb2yuv_601l - rgb to yuv color space conversion matrix
@@ -467,12 +467,14 @@ static void _sde_encoder_phys_wb_setup_cwb(struct sde_encoder_phys *phys_enc,
 
 
 	hw_ctl = crtc->mixers[0].hw_ctl;
 	hw_ctl = crtc->mixers[0].hw_ctl;
 	if (hw_ctl && hw_ctl->ops.setup_intf_cfg_v1 &&
 	if (hw_ctl && hw_ctl->ops.setup_intf_cfg_v1 &&
-			test_bit(SDE_WB_CWB_CTRL, &hw_wb->caps->features)) {
+			test_bit(SDE_WB_CWB_CTRL | SDE_WB_DCWB_CTRL,
+					&hw_wb->caps->features)) {
 		struct sde_hw_intf_cfg_v1 intf_cfg = { 0, };
 		struct sde_hw_intf_cfg_v1 intf_cfg = { 0, };
 
 
 		for (i = 0; i < crtc->num_mixers; i++)
 		for (i = 0; i < crtc->num_mixers; i++)
-			intf_cfg.cwb[intf_cfg.cwb_count++] =
-				(enum sde_cwb)(hw_pp->idx + i);
+			intf_cfg.cwb[intf_cfg.cwb_count++] = (enum sde_cwb)
+				(test_bit(SDE_WB_DCWB_CTRL, &hw_wb->caps->features) ?
+					((hw_pp->idx % 2) + i) : (hw_pp->idx + i));
 
 
 		if (hw_pp->merge_3d && (intf_cfg.merge_3d_count <
 		if (hw_pp->merge_3d && (intf_cfg.merge_3d_count <
 				MAX_MERGE_3D_PER_CTL_V1) && need_merge)
 				MAX_MERGE_3D_PER_CTL_V1) && need_merge)
@@ -483,12 +485,17 @@ static void _sde_encoder_phys_wb_setup_cwb(struct sde_encoder_phys *phys_enc,
 			hw_pp->ops.setup_3d_mode(hw_pp, (enable && need_merge) ?
 			hw_pp->ops.setup_3d_mode(hw_pp, (enable && need_merge) ?
 					BLEND_3D_H_ROW_INT : 0);
 					BLEND_3D_H_ROW_INT : 0);
 
 
-		if (hw_wb->ops.bind_pingpong_blk)
+		if ((hw_wb->ops.bind_pingpong_blk) &&
+				test_bit(SDE_WB_CWB_CTRL, &hw_wb->caps->features))
 			hw_wb->ops.bind_pingpong_blk(hw_wb, enable, hw_pp->idx);
 			hw_wb->ops.bind_pingpong_blk(hw_wb, enable, hw_pp->idx);
 
 
+		if ((hw_wb->ops.bind_dcwb_pp_blk) &&
+				test_bit(SDE_WB_DCWB_CTRL, &hw_wb->caps->features))
+			hw_wb->ops.bind_dcwb_pp_blk(hw_wb, enable, hw_pp->idx);
+
 		if (hw_ctl->ops.update_intf_cfg) {
 		if (hw_ctl->ops.update_intf_cfg) {
 			hw_ctl->ops.update_intf_cfg(hw_ctl, &intf_cfg, enable);
 			hw_ctl->ops.update_intf_cfg(hw_ctl, &intf_cfg, enable);
-			SDE_DEBUG("in CWB mode on CTL_%d PP-%d merge3d:%d\n",
+			SDE_DEBUG("in CWB/DCWB mode on CTL_%d PP-%d merge3d:%d\n",
 					hw_ctl->idx - CTL_0,
 					hw_ctl->idx - CTL_0,
 					hw_pp->idx - PINGPONG_0,
 					hw_pp->idx - PINGPONG_0,
 					hw_pp->merge_3d ?
 					hw_pp->merge_3d ?
@@ -503,7 +510,7 @@ static void _sde_encoder_phys_wb_setup_cwb(struct sde_encoder_phys *phys_enc,
 
 
 		if (hw_ctl && hw_ctl->ops.update_wb_cfg) {
 		if (hw_ctl && hw_ctl->ops.update_wb_cfg) {
 			hw_ctl->ops.update_wb_cfg(hw_ctl, intf_cfg, enable);
 			hw_ctl->ops.update_wb_cfg(hw_ctl, intf_cfg, enable);
-			SDE_DEBUG("in CWB mode adding WB for CTL_%d\n",
+			SDE_DEBUG("in CWB/DCWB mode adding WB for CTL_%d\n",
 					hw_ctl->idx - CTL_0);
 					hw_ctl->idx - CTL_0);
 		}
 		}
 	}
 	}
@@ -593,13 +600,14 @@ static void _sde_enc_phys_wb_detect_cwb(struct sde_encoder_phys *phys_enc,
 	u32 encoder_mask = 0;
 	u32 encoder_mask = 0;
 
 
 	/* Check if WB has CWB support */
 	/* Check if WB has CWB support */
-	if (wb_cfg->features & BIT(SDE_WB_HAS_CWB)) {
+	if ((wb_cfg->features & BIT(SDE_WB_HAS_CWB))
+			|| (wb_cfg->features & BIT(SDE_WB_HAS_DCWB))) {
 		encoder_mask = crtc_state->encoder_mask;
 		encoder_mask = crtc_state->encoder_mask;
 		encoder_mask &= ~drm_encoder_mask(phys_enc->parent);
 		encoder_mask &= ~drm_encoder_mask(phys_enc->parent);
 	}
 	}
 	phys_enc->in_clone_mode = encoder_mask ? true : false;
 	phys_enc->in_clone_mode = encoder_mask ? true : false;
 
 
-	SDE_DEBUG("detect CWB - status:%d\n", phys_enc->in_clone_mode);
+	SDE_DEBUG("detect CWB(OR)DCWB - status:%d\n", phys_enc->in_clone_mode);
 }
 }
 
 
 static int _sde_enc_phys_wb_validate_cwb(struct sde_encoder_phys *phys_enc,
 static int _sde_enc_phys_wb_validate_cwb(struct sde_encoder_phys *phys_enc,
@@ -863,6 +871,7 @@ static void _sde_encoder_phys_wb_update_cwb_flush(
 	int i = 0;
 	int i = 0;
 	int cwb_capture_mode = 0;
 	int cwb_capture_mode = 0;
 	enum sde_cwb cwb_idx = 0;
 	enum sde_cwb cwb_idx = 0;
+	enum sde_dcwb dcwb_idx = 0;
 	enum sde_cwb src_pp_idx = 0;
 	enum sde_cwb src_pp_idx = 0;
 	bool dspp_out = false;
 	bool dspp_out = false;
 	bool need_merge = false;
 	bool need_merge = false;
@@ -895,9 +904,19 @@ static void _sde_encoder_phys_wb_update_cwb_flush(
 	dspp_out = (cwb_capture_mode == CAPTURE_DSPP_OUT);
 	dspp_out = (cwb_capture_mode == CAPTURE_DSPP_OUT);
 	need_merge = (crtc->num_mixers > 1) ? true : false;
 	need_merge = (crtc->num_mixers > 1) ? true : false;
 
 
-	if (src_pp_idx > CWB_0 ||  ((cwb_idx + crtc->num_mixers) > CWB_MAX)) {
-		SDE_ERROR("invalid hw config for CWB\n");
-		return;
+	if (test_bit(SDE_WB_DCWB_CTRL, &hw_wb->caps->features)) {
+		dcwb_idx = (enum sde_dcwb) ((hw_pp->idx % 2) + i);
+		if ((dcwb_idx + crtc->num_mixers) > DCWB_MAX) {
+			SDE_ERROR("invalid hw config for DCWB. dcwb_idx=%d, num_mixers=%d\n",
+				dcwb_idx, crtc->num_mixers);
+			return;
+		}
+	} else {
+		if (src_pp_idx > CWB_0 ||  ((cwb_idx + crtc->num_mixers) > CWB_MAX)) {
+			SDE_ERROR("invalid hw config for CWB. pp_idx-%d, cwb_idx=%d, num_mixers=%d\n",
+				src_pp_idx, dcwb_idx, crtc->num_mixers);
+			return;
+		}
 	}
 	}
 
 
 	if (hw_ctl->ops.update_bitmask)
 	if (hw_ctl->ops.update_bitmask)
@@ -908,18 +927,30 @@ static void _sde_encoder_phys_wb_update_cwb_flush(
 		hw_ctl->ops.update_bitmask(hw_ctl, SDE_HW_FLUSH_CDM,
 		hw_ctl->ops.update_bitmask(hw_ctl, SDE_HW_FLUSH_CDM,
 				hw_cdm->idx, 1);
 				hw_cdm->idx, 1);
 
 
-	if (test_bit(SDE_WB_CWB_CTRL, &hw_wb->caps->features)) {
+	if (test_bit(SDE_WB_CWB_CTRL | SDE_WB_DCWB_CTRL,
+					&hw_wb->caps->features)) {
 		for (i = 0; i < crtc->num_mixers; i++) {
 		for (i = 0; i < crtc->num_mixers; i++) {
-			cwb_idx = (enum sde_cwb) (hw_pp->idx + i);
 			src_pp_idx = (enum sde_cwb) (src_pp_idx + i);
 			src_pp_idx = (enum sde_cwb) (src_pp_idx + i);
 
 
-			if (hw_wb->ops.program_cwb_ctrl)
-				hw_wb->ops.program_cwb_ctrl(hw_wb, cwb_idx,
+			if (test_bit(SDE_WB_DCWB_CTRL, &hw_wb->caps->features)) {
+				dcwb_idx = (enum sde_dcwb) ((hw_pp->idx % 2) + i);
+				if (hw_wb->ops.program_dcwb_ctrl)
+					hw_wb->ops.program_dcwb_ctrl(hw_wb, dcwb_idx,
+						src_pp_idx, cwb_capture_mode,
+						enable);
+				if (hw_ctl->ops.update_bitmask)
+					hw_ctl->ops.update_bitmask(hw_ctl,
+						SDE_HW_FLUSH_CWB, dcwb_idx, 1);
+
+			} else if (test_bit(SDE_WB_CWB_CTRL, &hw_wb->caps->features)) {
+				cwb_idx = (enum sde_cwb) (hw_pp->idx + i);
+				if (hw_wb->ops.program_cwb_ctrl)
+					hw_wb->ops.program_cwb_ctrl(hw_wb, cwb_idx,
 						src_pp_idx, dspp_out, enable);
 						src_pp_idx, dspp_out, enable);
-
-			if (hw_ctl->ops.update_bitmask)
-				hw_ctl->ops.update_bitmask(hw_ctl,
+				if (hw_ctl->ops.update_bitmask)
+					hw_ctl->ops.update_bitmask(hw_ctl,
 						SDE_HW_FLUSH_CWB, cwb_idx, 1);
 						SDE_HW_FLUSH_CWB, cwb_idx, 1);
+			}
 		}
 		}
 
 
 		if (need_merge && hw_ctl->ops.update_bitmask
 		if (need_merge && hw_ctl->ops.update_bitmask
@@ -1133,8 +1164,10 @@ static void sde_encoder_phys_wb_irq_ctrl(
 {
 {
 
 
 	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys);
 	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys);
+	const struct sde_wb_cfg *wb_cfg;
 	int index = 0, refcount;
 	int index = 0, refcount;
 	int ret = 0, pp = 0;
 	int ret = 0, pp = 0;
+	u32 max_num_of_irqs = 0;
 
 
 	if (!wb_enc)
 	if (!wb_enc)
 		return;
 		return;
@@ -1147,15 +1180,23 @@ static void sde_encoder_phys_wb_irq_ctrl(
 		SDE_ERROR("invalid pingpong index for WB or CWB\n");
 		SDE_ERROR("invalid pingpong index for WB or CWB\n");
 		return;
 		return;
 	}
 	}
-
 	refcount = atomic_read(&phys->wbirq_refcount);
 	refcount = atomic_read(&phys->wbirq_refcount);
 
 
+	/*
+	 * For Dedicated CWB, only one overflow IRQ is used for
+	 * both the PP_CWB blks. Make sure only one IRQ is registered
+	 * when D-CWB is enabled.
+	 */
+	wb_cfg = wb_enc->hw_wb->caps;
+	max_num_of_irqs = (wb_cfg->features & BIT(SDE_WB_HAS_DCWB)) ?
+					1 : CRTC_DUAL_MIXERS_ONLY;
+
 	if (enable && atomic_inc_return(&phys->wbirq_refcount) == 1) {
 	if (enable && atomic_inc_return(&phys->wbirq_refcount) == 1) {
 		sde_encoder_helper_register_irq(phys, INTR_IDX_WB_DONE);
 		sde_encoder_helper_register_irq(phys, INTR_IDX_WB_DONE);
 		if (ret)
 		if (ret)
 			atomic_dec_return(&phys->wbirq_refcount);
 			atomic_dec_return(&phys->wbirq_refcount);
 
 
-		for (index = 0; index < CRTC_DUAL_MIXERS_ONLY; index++)
+		for (index = 0; index < max_num_of_irqs; index++)
 			if (cwb_irq_tbl[index + pp] != SDE_NONE)
 			if (cwb_irq_tbl[index + pp] != SDE_NONE)
 				sde_encoder_helper_register_irq(phys,
 				sde_encoder_helper_register_irq(phys,
 					cwb_irq_tbl[index + pp]);
 					cwb_irq_tbl[index + pp]);
@@ -1165,7 +1206,7 @@ static void sde_encoder_phys_wb_irq_ctrl(
 		if (ret)
 		if (ret)
 			atomic_inc_return(&phys->wbirq_refcount);
 			atomic_inc_return(&phys->wbirq_refcount);
 
 
-		for (index = 0; index < CRTC_DUAL_MIXERS_ONLY; index++)
+		for (index = 0; index < max_num_of_irqs; index++)
 			if (cwb_irq_tbl[index + pp] != SDE_NONE)
 			if (cwb_irq_tbl[index + pp] != SDE_NONE)
 				sde_encoder_helper_unregister_irq(phys,
 				sde_encoder_helper_unregister_irq(phys,
 					cwb_irq_tbl[index + pp]);
 					cwb_irq_tbl[index + pp]);
@@ -1839,6 +1880,7 @@ struct sde_encoder_phys *sde_encoder_phys_wb_init(
 {
 {
 	struct sde_encoder_phys *phys_enc;
 	struct sde_encoder_phys *phys_enc;
 	struct sde_encoder_phys_wb *wb_enc;
 	struct sde_encoder_phys_wb *wb_enc;
+	const struct sde_wb_cfg *wb_cfg;
 	struct sde_hw_mdp *hw_mdp;
 	struct sde_hw_mdp *hw_mdp;
 	struct sde_encoder_irq *irq;
 	struct sde_encoder_irq *irq;
 	int ret = 0;
 	int ret = 0;
@@ -1921,6 +1963,7 @@ struct sde_encoder_phys *sde_encoder_phys_wb_init(
 	atomic_set(&phys_enc->pending_retire_fence_cnt, 0);
 	atomic_set(&phys_enc->pending_retire_fence_cnt, 0);
 	atomic_set(&phys_enc->wbirq_refcount, 0);
 	atomic_set(&phys_enc->wbirq_refcount, 0);
 	init_waitqueue_head(&phys_enc->pending_kickoff_wq);
 	init_waitqueue_head(&phys_enc->pending_kickoff_wq);
+	wb_cfg = wb_enc->hw_wb->caps;
 
 
 	irq = &phys_enc->irq[INTR_IDX_WB_DONE];
 	irq = &phys_enc->irq[INTR_IDX_WB_DONE];
 	INIT_LIST_HEAD(&irq->cb.list);
 	INIT_LIST_HEAD(&irq->cb.list);
@@ -1932,55 +1975,68 @@ struct sde_encoder_phys *sde_encoder_phys_wb_init(
 	irq->cb.arg = wb_enc;
 	irq->cb.arg = wb_enc;
 	irq->cb.func = sde_encoder_phys_wb_done_irq;
 	irq->cb.func = sde_encoder_phys_wb_done_irq;
 
 
-	irq = &phys_enc->irq[INTR_IDX_PP1_OVFL];
-	INIT_LIST_HEAD(&irq->cb.list);
-	irq->name = "pp1_overflow";
-	irq->hw_idx = CWB_1;
-	irq->irq_idx = -1;
-	irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
-	irq->intr_idx = INTR_IDX_PP1_OVFL;
-	irq->cb.arg = wb_enc;
-	irq->cb.func = sde_encoder_phys_cwb_ovflow;
+	if (wb_cfg && (wb_cfg->features & BIT(SDE_WB_HAS_DCWB))) {
+		irq = &phys_enc->irq[INTR_IDX_PP_CWB_OVFL];
+		INIT_LIST_HEAD(&irq->cb.list);
+		irq->name = "pp_cwb0_overflow";
+		irq->hw_idx = PINGPONG_CWB_0;
+		irq->irq_idx = -1;
+		irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
+		irq->intr_idx = INTR_IDX_PP_CWB_OVFL;
+		irq->cb.arg = wb_enc;
+		irq->cb.func = sde_encoder_phys_cwb_ovflow;
 
 
-	irq = &phys_enc->irq[INTR_IDX_PP2_OVFL];
-	INIT_LIST_HEAD(&irq->cb.list);
-	irq->name = "pp2_overflow";
-	irq->hw_idx = CWB_2;
-	irq->irq_idx = -1;
-	irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
-	irq->intr_idx = INTR_IDX_PP2_OVFL;
-	irq->cb.arg = wb_enc;
-	irq->cb.func = sde_encoder_phys_cwb_ovflow;
-
-	irq = &phys_enc->irq[INTR_IDX_PP3_OVFL];
-	INIT_LIST_HEAD(&irq->cb.list);
-	irq->name = "pp3_overflow";
-	irq->hw_idx = CWB_3;
-	irq->irq_idx = -1;
-	irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
-	irq->intr_idx = INTR_IDX_PP3_OVFL;
-	irq->cb.arg = wb_enc;
-	irq->cb.func = sde_encoder_phys_cwb_ovflow;
-
-	irq = &phys_enc->irq[INTR_IDX_PP4_OVFL];
-	INIT_LIST_HEAD(&irq->cb.list);
-	irq->name = "pp4_overflow";
-	irq->hw_idx = CWB_4;
-	irq->irq_idx = -1;
-	irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
-	irq->intr_idx = INTR_IDX_PP4_OVFL;
-	irq->cb.arg = wb_enc;
-	irq->cb.func = sde_encoder_phys_cwb_ovflow;
-
-	irq = &phys_enc->irq[INTR_IDX_PP5_OVFL];
-	INIT_LIST_HEAD(&irq->cb.list);
-	irq->name = "pp5_overflow";
-	irq->hw_idx = CWB_5;
-	irq->irq_idx = -1;
-	irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
-	irq->intr_idx = INTR_IDX_PP5_OVFL;
-	irq->cb.arg = wb_enc;
-	irq->cb.func = sde_encoder_phys_cwb_ovflow;
+	} else {
+		irq = &phys_enc->irq[INTR_IDX_PP1_OVFL];
+		INIT_LIST_HEAD(&irq->cb.list);
+		irq->name = "pp1_overflow";
+		irq->hw_idx = CWB_1;
+		irq->irq_idx = -1;
+		irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
+		irq->intr_idx = INTR_IDX_PP1_OVFL;
+		irq->cb.arg = wb_enc;
+		irq->cb.func = sde_encoder_phys_cwb_ovflow;
+
+		irq = &phys_enc->irq[INTR_IDX_PP2_OVFL];
+		INIT_LIST_HEAD(&irq->cb.list);
+		irq->name = "pp2_overflow";
+		irq->hw_idx = CWB_2;
+		irq->irq_idx = -1;
+		irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
+		irq->intr_idx = INTR_IDX_PP2_OVFL;
+		irq->cb.arg = wb_enc;
+		irq->cb.func = sde_encoder_phys_cwb_ovflow;
+
+		irq = &phys_enc->irq[INTR_IDX_PP3_OVFL];
+		INIT_LIST_HEAD(&irq->cb.list);
+		irq->name = "pp3_overflow";
+		irq->hw_idx = CWB_3;
+		irq->irq_idx = -1;
+		irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
+		irq->intr_idx = INTR_IDX_PP3_OVFL;
+		irq->cb.arg = wb_enc;
+		irq->cb.func = sde_encoder_phys_cwb_ovflow;
+
+		irq = &phys_enc->irq[INTR_IDX_PP4_OVFL];
+		INIT_LIST_HEAD(&irq->cb.list);
+		irq->name = "pp4_overflow";
+		irq->hw_idx = CWB_4;
+		irq->irq_idx = -1;
+		irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
+		irq->intr_idx = INTR_IDX_PP4_OVFL;
+		irq->cb.arg = wb_enc;
+		irq->cb.func = sde_encoder_phys_cwb_ovflow;
+
+		irq = &phys_enc->irq[INTR_IDX_PP5_OVFL];
+		INIT_LIST_HEAD(&irq->cb.list);
+		irq->name = "pp5_overflow";
+		irq->hw_idx = CWB_5;
+		irq->irq_idx = -1;
+		irq->intr_type = SDE_IRQ_TYPE_CWB_OVERFLOW;
+		irq->intr_idx = INTR_IDX_PP5_OVFL;
+		irq->cb.arg = wb_enc;
+		irq->cb.func = sde_encoder_phys_cwb_ovflow;
+	}
 
 
 	/* create internal buffer for disable logic */
 	/* create internal buffer for disable logic */
 	if (_sde_encoder_phys_wb_init_internal_fb(wb_enc,
 	if (_sde_encoder_phys_wb_init_internal_fb(wb_enc,

+ 33 - 2
msm/sde/sde_hw_catalog.c

@@ -320,6 +320,7 @@ enum {
 enum {
 enum {
 	PP_OFF,
 	PP_OFF,
 	PP_LEN,
 	PP_LEN,
+	PP_CWB,
 	TE_OFF,
 	TE_OFF,
 	TE_LEN,
 	TE_LEN,
 	TE2_OFF,
 	TE2_OFF,
@@ -439,6 +440,7 @@ enum {
 	MIXER_BLOCKS,
 	MIXER_BLOCKS,
 	MIXER_DISP,
 	MIXER_DISP,
 	MIXER_CWB,
 	MIXER_CWB,
+	MIXER_DCWB,
 	MIXER_PROP_MAX,
 	MIXER_PROP_MAX,
 };
 };
 
 
@@ -759,6 +761,8 @@ static struct sde_prop_type mixer_prop[] = {
 		PROP_TYPE_STRING_ARRAY},
 		PROP_TYPE_STRING_ARRAY},
 	{MIXER_CWB, "qcom,sde-mixer-cwb-pref", false,
 	{MIXER_CWB, "qcom,sde-mixer-cwb-pref", false,
 		PROP_TYPE_STRING_ARRAY},
 		PROP_TYPE_STRING_ARRAY},
+	{MIXER_DCWB, "qcom,sde-mixer-dcwb-pref", false,
+		PROP_TYPE_STRING_ARRAY},
 };
 };
 
 
 static struct sde_prop_type mixer_blocks_prop[] = {
 static struct sde_prop_type mixer_blocks_prop[] = {
@@ -831,6 +835,7 @@ static struct sde_prop_type ds_prop[] = {
 static struct sde_prop_type pp_prop[] = {
 static struct sde_prop_type pp_prop[] = {
 	{PP_OFF, "qcom,sde-pp-off", true, PROP_TYPE_U32_ARRAY},
 	{PP_OFF, "qcom,sde-pp-off", true, PROP_TYPE_U32_ARRAY},
 	{PP_LEN, "qcom,sde-pp-size", false, PROP_TYPE_U32},
 	{PP_LEN, "qcom,sde-pp-size", false, PROP_TYPE_U32},
+	{PP_CWB, "qcom,sde-pp-cwb", false, PROP_TYPE_U32_ARRAY},
 	{TE_OFF, "qcom,sde-te-off", false, PROP_TYPE_U32_ARRAY},
 	{TE_OFF, "qcom,sde-te-off", false, PROP_TYPE_U32_ARRAY},
 	{TE_LEN, "qcom,sde-te-size", false, PROP_TYPE_U32},
 	{TE_LEN, "qcom,sde-te-size", false, PROP_TYPE_U32},
 	{TE2_OFF, "qcom,sde-te2-off", false, PROP_TYPE_U32_ARRAY},
 	{TE2_OFF, "qcom,sde-te2-off", false, PROP_TYPE_U32_ARRAY},
@@ -2224,6 +2229,8 @@ static int sde_mixer_parse_dt(struct device_node *np,
 			ds_idx = 0; i < off_count; i++) {
 			ds_idx = 0; i < off_count; i++) {
 		const char *disp_pref = NULL;
 		const char *disp_pref = NULL;
 		const char *cwb_pref = NULL;
 		const char *cwb_pref = NULL;
+		const char *dcwb_pref = NULL;
+		u32 dummy_mixer_base = 0x0f0f;
 
 
 		mixer_base = PROP_VALUE_ACCESS(props->values, MIXER_OFF, i);
 		mixer_base = PROP_VALUE_ACCESS(props->values, MIXER_OFF, i);
 		if (!mixer_base)
 		if (!mixer_base)
@@ -2277,6 +2284,16 @@ static int sde_mixer_parse_dt(struct device_node *np,
 		if (cwb_pref && !strcmp(cwb_pref, "cwb"))
 		if (cwb_pref && !strcmp(cwb_pref, "cwb"))
 			set_bit(SDE_DISP_CWB_PREF, &mixer->features);
 			set_bit(SDE_DISP_CWB_PREF, &mixer->features);
 
 
+		of_property_read_string_index(np,
+			mixer_prop[MIXER_DCWB].prop_name, i, &dcwb_pref);
+		if (dcwb_pref && !strcmp(dcwb_pref, "dcwb")) {
+			set_bit(SDE_DISP_DCWB_PREF, &mixer->features);
+			if (mixer->base == dummy_mixer_base) {
+				mixer->base = 0x0;
+				mixer->len = 0;
+			}
+		}
+
 		mixer->pingpong = pp_count > 0 ? pp_idx + PINGPONG_0
 		mixer->pingpong = pp_count > 0 ? pp_idx + PINGPONG_0
 							: PINGPONG_MAX;
 							: PINGPONG_MAX;
 		mixer->dspp = dspp_count > 0 ? dspp_idx + DSPP_0
 		mixer->dspp = dspp_count > 0 ? dspp_idx + DSPP_0
@@ -2518,7 +2535,18 @@ static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg)
 		if (IS_SDE_CTL_REV_100(sde_cfg->ctl_rev))
 		if (IS_SDE_CTL_REV_100(sde_cfg->ctl_rev))
 			set_bit(SDE_WB_INPUT_CTRL, &wb->features);
 			set_bit(SDE_WB_INPUT_CTRL, &wb->features);
 
 
-		if (sde_cfg->has_cwb_support) {
+		if (sde_cfg->has_dedicated_cwb_support) {
+			set_bit(SDE_WB_HAS_DCWB, &wb->features);
+			if (IS_SDE_CTL_REV_100(sde_cfg->ctl_rev))
+				set_bit(SDE_WB_DCWB_CTRL, &wb->features);
+			if (major_version >= SDE_HW_MAJOR(SDE_HW_VER_810)) {
+				sde_cfg->cwb_blk_off = 0x66A00;
+				sde_cfg->cwb_blk_stride = 0x400;
+			} else {
+				sde_cfg->cwb_blk_off = 0x83000;
+				sde_cfg->cwb_blk_stride = 0x100;
+			}
+		} else if (sde_cfg->has_cwb_support) {
 			set_bit(SDE_WB_HAS_CWB, &wb->features);
 			set_bit(SDE_WB_HAS_CWB, &wb->features);
 			if (IS_SDE_CTL_REV_100(sde_cfg->ctl_rev))
 			if (IS_SDE_CTL_REV_100(sde_cfg->ctl_rev))
 				set_bit(SDE_WB_CWB_CTRL, &wb->features);
 				set_bit(SDE_WB_CWB_CTRL, &wb->features);
@@ -3798,6 +3826,9 @@ static int sde_pp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg)
 		if (PROP_VALUE_ACCESS(prop_value, PP_SLAVE, i))
 		if (PROP_VALUE_ACCESS(prop_value, PP_SLAVE, i))
 			set_bit(SDE_PINGPONG_SLAVE, &pp->features);
 			set_bit(SDE_PINGPONG_SLAVE, &pp->features);
 
 
+		if (PROP_VALUE_ACCESS(prop_value, PP_CWB, i))
+			set_bit(SDE_PINGPONG_CWB, &pp->features);
+
 		if (major_version < SDE_HW_MAJOR(SDE_HW_VER_700)) {
 		if (major_version < SDE_HW_MAJOR(SDE_HW_VER_700)) {
 			sblk->dsc.base = PROP_VALUE_ACCESS(prop_value,
 			sblk->dsc.base = PROP_VALUE_ACCESS(prop_value,
 					DSC_OFF, i);
 					DSC_OFF, i);
@@ -5005,7 +5036,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
 		sde_cfg->has_trusted_vm_support = true;
 		sde_cfg->has_trusted_vm_support = true;
 		sde_cfg->syscache_supported = true;
 		sde_cfg->syscache_supported = true;
 	} else if (IS_WAIPIO_TARGET(hw_rev)) {
 	} else if (IS_WAIPIO_TARGET(hw_rev)) {
-		sde_cfg->has_cwb_support = true;
+		sde_cfg->has_dedicated_cwb_support = true;
 		sde_cfg->has_wb_ubwc = true;
 		sde_cfg->has_wb_ubwc = true;
 		sde_cfg->has_qsync = true;
 		sde_cfg->has_qsync = true;
 		sde_cfg->perf.min_prefill_lines = 40;
 		sde_cfg->perf.min_prefill_lines = 40;

+ 10 - 0
msm/sde/sde_hw_catalog.h

@@ -333,6 +333,7 @@ enum {
  * @SDE_MIXER_GC              Gamma correction block
  * @SDE_MIXER_GC              Gamma correction block
  * @SDE_DIM_LAYER             Layer mixer supports dim layer
  * @SDE_DIM_LAYER             Layer mixer supports dim layer
  * @SDE_DISP_CWB_PREF         Layer mixer preferred for CWB
  * @SDE_DISP_CWB_PREF         Layer mixer preferred for CWB
+ * @SDE_DISP_DCWB_PREF        Layer mixer preferred for Dedicated CWB
  * @SDE_DISP_PRIMARY_PREF     Layer mixer preferred for primary display
  * @SDE_DISP_PRIMARY_PREF     Layer mixer preferred for primary display
  * @SDE_DISP_SECONDARY_PREF   Layer mixer preferred for secondary display
  * @SDE_DISP_SECONDARY_PREF   Layer mixer preferred for secondary display
  * @SDE_MIXER_COMBINED_ALPHA  Layer mixer bg and fg alpha in single register
  * @SDE_MIXER_COMBINED_ALPHA  Layer mixer bg and fg alpha in single register
@@ -346,6 +347,7 @@ enum {
 	SDE_DISP_PRIMARY_PREF,
 	SDE_DISP_PRIMARY_PREF,
 	SDE_DISP_SECONDARY_PREF,
 	SDE_DISP_SECONDARY_PREF,
 	SDE_DISP_CWB_PREF,
 	SDE_DISP_CWB_PREF,
+	SDE_DISP_DCWB_PREF,
 	SDE_MIXER_COMBINED_ALPHA,
 	SDE_MIXER_COMBINED_ALPHA,
 	SDE_MIXER_MAX
 	SDE_MIXER_MAX
 };
 };
@@ -414,6 +416,7 @@ enum {
  * @SDE_PINGPONG_DITHER,         Dither blocks
  * @SDE_PINGPONG_DITHER,         Dither blocks
  * @SDE_PINGPONG_DITHER_LUMA,    Dither sub-blocks and features
  * @SDE_PINGPONG_DITHER_LUMA,    Dither sub-blocks and features
  * @SDE_PINGPONG_MERGE_3D,  Separate MERGE_3D block exists
  * @SDE_PINGPONG_MERGE_3D,  Separate MERGE_3D block exists
+ * @SDE_PINGPONG_CWB,           PP block supports CWB
  * @SDE_PINGPONG_MAX
  * @SDE_PINGPONG_MAX
  */
  */
 enum {
 enum {
@@ -425,6 +428,7 @@ enum {
 	SDE_PINGPONG_DITHER,
 	SDE_PINGPONG_DITHER,
 	SDE_PINGPONG_DITHER_LUMA,
 	SDE_PINGPONG_DITHER_LUMA,
 	SDE_PINGPONG_MERGE_3D,
 	SDE_PINGPONG_MERGE_3D,
+	SDE_PINGPONG_CWB,
 	SDE_PINGPONG_MAX
 	SDE_PINGPONG_MAX
 };
 };
 
 
@@ -523,7 +527,9 @@ enum {
  * @SDE_WB_INPUT_CTRL       Writeback supports from which pp block input pixel
  * @SDE_WB_INPUT_CTRL       Writeback supports from which pp block input pixel
  *                          data arrives.
  *                          data arrives.
  * @SDE_WB_HAS_CWB          Writeback block supports concurrent writeback
  * @SDE_WB_HAS_CWB          Writeback block supports concurrent writeback
+ * @SDE_WB_HAS_DCWB         Writeback block supports dedicated CWB
  * @SDE_WB_CWB_CTRL         Separate CWB control is available for configuring
  * @SDE_WB_CWB_CTRL         Separate CWB control is available for configuring
+ * @SDE_WB_DCWB_CTRL        Separate DCWB control is available for configuring
  * @SDE_WB_MAX              maximum value
  * @SDE_WB_MAX              maximum value
  */
  */
 enum {
 enum {
@@ -544,7 +550,9 @@ enum {
 	SDE_WB_CDP,
 	SDE_WB_CDP,
 	SDE_WB_INPUT_CTRL,
 	SDE_WB_INPUT_CTRL,
 	SDE_WB_HAS_CWB,
 	SDE_WB_HAS_CWB,
+	SDE_WB_HAS_DCWB,
 	SDE_WB_CWB_CTRL,
 	SDE_WB_CWB_CTRL,
+	SDE_WB_DCWB_CTRL,
 	SDE_WB_MAX
 	SDE_WB_MAX
 };
 };
 
 
@@ -1440,6 +1448,7 @@ struct sde_perf_cfg {
  * @has_cdp            Client driven prefetch feature status
  * @has_cdp            Client driven prefetch feature status
  * @has_wb_ubwc        UBWC feature supported on WB
  * @has_wb_ubwc        UBWC feature supported on WB
  * @has_cwb_support    indicates if device supports primary capture through CWB
  * @has_cwb_support    indicates if device supports primary capture through CWB
+ * @has_dedicated_cwb_support    indicates if device supports dedicated path for CWB capture
  * @cwb_blk_off        CWB offset address
  * @cwb_blk_off        CWB offset address
  * @cwb_blk_stride     offset between each CWB blk
  * @cwb_blk_stride     offset between each CWB blk
  * @ubwc_version       UBWC feature version (0x0 for not supported)
  * @ubwc_version       UBWC feature version (0x0 for not supported)
@@ -1522,6 +1531,7 @@ struct sde_mdss_cfg {
 	bool has_dim_layer;
 	bool has_dim_layer;
 	bool has_wb_ubwc;
 	bool has_wb_ubwc;
 	bool has_cwb_support;
 	bool has_cwb_support;
+	bool has_dedicated_cwb_support;
 	u32 cwb_blk_off;
 	u32 cwb_blk_off;
 	u32 cwb_blk_stride;
 	u32 cwb_blk_stride;
 	u32 ubwc_version;
 	u32 ubwc_version;

+ 5 - 0
msm/sde/sde_hw_ctl.c

@@ -161,6 +161,11 @@ static const u32 cdm_flush_tbl[CDM_MAX] = {SDE_NONE, 0};
 static const u32 cwb_flush_tbl[CWB_MAX] = {SDE_NONE, SDE_NONE, 1, 2, 3,
 static const u32 cwb_flush_tbl[CWB_MAX] = {SDE_NONE, SDE_NONE, 1, 2, 3,
 	4, 5};
 	4, 5};
 
 
+/**
+ * list of CWB bits in CTL_CWB_FLUSH for dedicated cwb
+ */
+static const u32 dcwb_flush_tbl[CWB_MAX] = {SDE_NONE, SDE_NONE, 0, 1};
+
 /**
 /**
  * list of DSPP sub-blk flush bits in CTL_DSPP_x_FLUSH
  * list of DSPP sub-blk flush bits in CTL_DSPP_x_FLUSH
  */
  */

+ 3 - 0
msm/sde/sde_hw_interrupts.c

@@ -121,6 +121,7 @@
 #define SDE_INTR_CWB_3_OVERFLOW BIT(15)
 #define SDE_INTR_CWB_3_OVERFLOW BIT(15)
 #define SDE_INTR_CWB_4_OVERFLOW BIT(20)
 #define SDE_INTR_CWB_4_OVERFLOW BIT(20)
 #define SDE_INTR_CWB_5_OVERFLOW BIT(21)
 #define SDE_INTR_CWB_5_OVERFLOW BIT(21)
+#define SDE_INTR_CWB_OVERFLOW BIT(29)
 
 
 /**
 /**
  * Histogram VIG done interrupt status bit definitions
  * Histogram VIG done interrupt status bit definitions
@@ -345,6 +346,8 @@ static struct sde_irq_type sde_irq_intr2_map[] = {
 	{ SDE_IRQ_TYPE_PING_PONG_TE_CHECK, PINGPONG_S0,
 	{ SDE_IRQ_TYPE_PING_PONG_TE_CHECK, PINGPONG_S0,
 		SDE_INTR_PING_PONG_S0_TE_DETECTED, -1},
 		SDE_INTR_PING_PONG_S0_TE_DETECTED, -1},
 
 
+	{ SDE_IRQ_TYPE_CWB_OVERFLOW, PINGPONG_CWB_0, SDE_INTR_CWB_OVERFLOW, -1},
+
 	{ SDE_IRQ_TYPE_PING_PONG_COMP, PINGPONG_4,
 	{ SDE_IRQ_TYPE_PING_PONG_COMP, PINGPONG_4,
 		SDE_INTR_PING_PONG_4_DONE, -1},
 		SDE_INTR_PING_PONG_4_DONE, -1},
 	{ SDE_IRQ_TYPE_PING_PONG_COMP, PINGPONG_5,
 	{ SDE_IRQ_TYPE_PING_PONG_COMP, PINGPONG_5,

+ 11 - 0
msm/sde/sde_hw_mdss.h

@@ -168,6 +168,8 @@ enum sde_lm {
 	LM_3,
 	LM_3,
 	LM_4,
 	LM_4,
 	LM_5,
 	LM_5,
+	LM_DCWB_DUMMY_0,
+	LM_DCWB_DUMMY_1,
 	LM_6,
 	LM_6,
 	LM_MAX
 	LM_MAX
 };
 };
@@ -238,6 +240,8 @@ enum sde_pingpong {
 	PINGPONG_3,
 	PINGPONG_3,
 	PINGPONG_4,
 	PINGPONG_4,
 	PINGPONG_5,
 	PINGPONG_5,
+	PINGPONG_CWB_0,
+	PINGPONG_CWB_1,
 	PINGPONG_S0,
 	PINGPONG_S0,
 	PINGPONG_MAX
 	PINGPONG_MAX
 };
 };
@@ -317,6 +321,12 @@ enum sde_cwb {
 	CWB_MAX
 	CWB_MAX
 };
 };
 
 
+enum sde_dcwb {
+	DCWB_0 = 0x1,
+	DCWB_1,
+	DCWB_MAX
+};
+
 enum sde_wd_timer {
 enum sde_wd_timer {
 	WD_TIMER_0 = 0x1,
 	WD_TIMER_0 = 0x1,
 	WD_TIMER_1,
 	WD_TIMER_1,
@@ -350,6 +360,7 @@ enum sde_merge_3d {
 	MERGE_3D_0 = 1,
 	MERGE_3D_0 = 1,
 	MERGE_3D_1,
 	MERGE_3D_1,
 	MERGE_3D_2,
 	MERGE_3D_2,
+	MERGE_3D_CWB_0,
 	MERGE_3D_MAX
 	MERGE_3D_MAX
 };
 };
 
 

+ 74 - 1
msm/sde/sde_hw_wb.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 // SPDX-License-Identifier: GPL-2.0-only
 /*
 /*
- * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
  */
  */
 
 
 #include "sde_hw_mdss.h"
 #include "sde_hw_mdss.h"
@@ -99,6 +99,31 @@ static void _sde_hw_cwb_ctrl_init(struct sde_mdss_cfg *m,
 	}
 	}
 }
 }
 
 
+static void _sde_hw_dcwb_ctrl_init(struct sde_mdss_cfg *m,
+		void __iomem *addr, struct sde_hw_blk_reg_map *b)
+{
+	int i;
+	u32 blk_off;
+	char name[64] = {0};
+
+	if (!b)
+		return;
+
+	b->base_off = addr;
+	b->blk_off = m->cwb_blk_off;
+	b->length = 0x20;
+	b->hwversion = m->hwversion;
+	b->log_mask = SDE_DBG_MASK_WB;
+
+	for (i = 0; i < m->pingpong_count; i++) {
+		snprintf(name, sizeof(name), "dcwb%d", i);
+		blk_off = b->blk_off + (m->cwb_blk_stride * i);
+
+		sde_dbg_reg_register_dump_range(SDE_DBG_NAME, name,
+				blk_off, blk_off + b->length, 0xff);
+	}
+}
+
 static void sde_hw_wb_setup_outaddress(struct sde_hw_wb *ctx,
 static void sde_hw_wb_setup_outaddress(struct sde_hw_wb *ctx,
 		struct sde_hw_wb_cfg *data)
 		struct sde_hw_wb_cfg *data)
 {
 {
@@ -270,6 +295,46 @@ static void sde_hw_wb_bind_pingpong_blk(
 	SDE_REG_WRITE(c, WB_MUX, mux_cfg);
 	SDE_REG_WRITE(c, WB_MUX, mux_cfg);
 }
 }
 
 
+static void sde_hw_wb_bind_dcwb_pp_blk(
+		struct sde_hw_wb *ctx,
+		bool enable,
+		const enum sde_pingpong pp)
+{
+	struct sde_hw_blk_reg_map *c;
+	int mux_cfg = 0xF;
+
+	if (!ctx)
+		return;
+
+	c = &ctx->hw;
+	if (enable)
+		mux_cfg = 0xd;
+
+	SDE_REG_WRITE(c, WB_MUX, mux_cfg);
+}
+
+static void sde_hw_wb_program_dcwb_ctrl(struct sde_hw_wb *ctx,
+	const enum sde_dcwb cur_idx, const enum sde_cwb data_src,
+	int tap_location, bool enable)
+{
+	struct sde_hw_blk_reg_map *c;
+	u32 blk_base;
+
+	if (!ctx)
+		return;
+
+	c = &ctx->dcwb_hw;
+	blk_base  = ctx->catalog->cwb_blk_stride * (cur_idx - DCWB_0);
+
+	if (enable) {
+		SDE_REG_WRITE(c, blk_base + CWB_CTRL_SRC_SEL, data_src - CWB_0);
+		SDE_REG_WRITE(c, blk_base + CWB_CTRL_MODE, tap_location);
+	} else {
+		SDE_REG_WRITE(c, blk_base + CWB_CTRL_SRC_SEL, 0xf);
+		SDE_REG_WRITE(c, blk_base + CWB_CTRL_MODE, 0x0);
+	}
+}
+
 static void sde_hw_wb_program_cwb_ctrl(struct sde_hw_wb *ctx,
 static void sde_hw_wb_program_cwb_ctrl(struct sde_hw_wb *ctx,
 	const enum sde_cwb cur_idx, const enum sde_cwb data_src,
 	const enum sde_cwb cur_idx, const enum sde_cwb data_src,
 	bool dspp_out, bool enable)
 	bool dspp_out, bool enable)
@@ -312,6 +377,11 @@ static void _setup_wb_ops(struct sde_hw_wb_ops *ops,
 
 
 	if (test_bit(SDE_WB_CWB_CTRL, &features))
 	if (test_bit(SDE_WB_CWB_CTRL, &features))
 		ops->program_cwb_ctrl = sde_hw_wb_program_cwb_ctrl;
 		ops->program_cwb_ctrl = sde_hw_wb_program_cwb_ctrl;
+
+	if (test_bit(SDE_WB_DCWB_CTRL, &features)) {
+		ops->program_dcwb_ctrl = sde_hw_wb_program_dcwb_ctrl;
+		ops->bind_dcwb_pp_blk = sde_hw_wb_bind_dcwb_pp_blk;
+	}
 }
 }
 
 
 static struct sde_hw_blk_ops sde_hw_ops = {
 static struct sde_hw_blk_ops sde_hw_ops = {
@@ -362,6 +432,9 @@ struct sde_hw_wb *sde_hw_wb_init(enum sde_wb idx,
 	if (test_bit(SDE_WB_CWB_CTRL, &cfg->features))
 	if (test_bit(SDE_WB_CWB_CTRL, &cfg->features))
 		_sde_hw_cwb_ctrl_init(m, addr, &c->cwb_hw);
 		_sde_hw_cwb_ctrl_init(m, addr, &c->cwb_hw);
 
 
+	if (test_bit(SDE_WB_DCWB_CTRL, &cfg->features))
+		_sde_hw_dcwb_ctrl_init(m, addr, &c->dcwb_hw);
+
 	return c;
 	return c;
 
 
 blk_init_error:
 blk_init_error:

+ 24 - 1
msm/sde/sde_hw_wb.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
 /*
- * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
  */
  */
 
 
 #ifndef _SDE_HW_WB_H
 #ifndef _SDE_HW_WB_H
@@ -114,6 +114,16 @@ struct sde_hw_wb_ops {
 			bool enable,
 			bool enable,
 			const enum sde_pingpong pp);
 			const enum sde_pingpong pp);
 
 
+	/**
+	 * bind_dcwb_pp_blk - enable/disable the connection with cwb pp
+	 * @ctx: Pointer to wb context
+	 * @enable: enable/disable connection
+	 * @pp: pingpong blk id
+	 */
+	void (*bind_dcwb_pp_blk)(struct sde_hw_wb *ctx,
+			bool enable,
+			const enum sde_pingpong pp);
+
 	/**
 	/**
 	 * program_cwb_ctrl - program cwb block configp
 	 * program_cwb_ctrl - program cwb block configp
 	 * @ctx: Pointer to wb context
 	 * @ctx: Pointer to wb context
@@ -124,6 +134,17 @@ struct sde_hw_wb_ops {
 	 */
 	 */
 	void (*program_cwb_ctrl)(struct sde_hw_wb *ctx, const enum sde_cwb cwb,
 	void (*program_cwb_ctrl)(struct sde_hw_wb *ctx, const enum sde_cwb cwb,
 		const enum sde_cwb data_src, bool dspp_out, bool enable);
 		const enum sde_cwb data_src, bool dspp_out, bool enable);
+
+	/**
+	 * program_dcwb_ctrl - program cwb block configp
+	 * @ctx: Pointer to wb context
+	 * @pp_idx: Current CWB block index to poram
+	 * @data_src: Source CWB/PingPong block index
+	 * @tap_location: Tap LM output, dspp output or Demura output
+	 * @enable: enable or disable the CWB path to tap the output
+	 */
+	void (*program_dcwb_ctrl)(struct sde_hw_wb *ctx, const enum sde_dcwb cwb,
+		const enum sde_cwb data_src, int tap_location, bool enable);
 };
 };
 
 
 /**
 /**
@@ -137,6 +158,7 @@ struct sde_hw_wb_ops {
  * @ops: function pointers
  * @ops: function pointers
  * @hw_mdp: MDP top level hardware block
  * @hw_mdp: MDP top level hardware block
  * @cwb_hw: CWB control hwio details
  * @cwb_hw: CWB control hwio details
+ * @dcwb_hw: DCWB control hwio details
  */
  */
 struct sde_hw_wb {
 struct sde_hw_wb {
 	struct sde_hw_blk base;
 	struct sde_hw_blk base;
@@ -153,6 +175,7 @@ struct sde_hw_wb {
 
 
 	struct sde_hw_mdp *hw_mdp;
 	struct sde_hw_mdp *hw_mdp;
 	struct sde_hw_blk_reg_map cwb_hw;
 	struct sde_hw_blk_reg_map cwb_hw;
+	struct sde_hw_blk_reg_map dcwb_hw;
 };
 };
 
 
 /**
 /**

+ 14 - 6
msm/sde/sde_rm.c

@@ -29,6 +29,7 @@
 #define RM_RQ_DSPP(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_DSPP))
 #define RM_RQ_DSPP(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_DSPP))
 #define RM_RQ_DS(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_DS))
 #define RM_RQ_DS(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_DS))
 #define RM_RQ_CWB(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_CWB))
 #define RM_RQ_CWB(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_CWB))
+#define RM_RQ_DCWB(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_DCWB))
 #define RM_IS_TOPOLOGY_MATCH(t, r) ((t).num_lm == (r).num_lm && \
 #define RM_IS_TOPOLOGY_MATCH(t, r) ((t).num_lm == (r).num_lm && \
 				(t).num_comp_enc == (r).num_enc && \
 				(t).num_comp_enc == (r).num_enc && \
 				(t).num_intf == (r).num_intf && \
 				(t).num_intf == (r).num_intf && \
@@ -984,7 +985,7 @@ static bool _sde_rm_check_lm_and_get_connected_blks(
 	const struct sde_lm_cfg *lm_cfg = to_sde_hw_mixer(lm->hw)->cap;
 	const struct sde_lm_cfg *lm_cfg = to_sde_hw_mixer(lm->hw)->cap;
 	const struct sde_pingpong_cfg *pp_cfg;
 	const struct sde_pingpong_cfg *pp_cfg;
 	bool ret, is_conn_primary, is_conn_secondary;
 	bool ret, is_conn_primary, is_conn_secondary;
-	u32 lm_primary_pref, lm_secondary_pref, cwb_pref;
+	u32 lm_primary_pref, lm_secondary_pref, cwb_pref, dcwb_pref;
 
 
 	*dspp = NULL;
 	*dspp = NULL;
 	*ds = NULL;
 	*ds = NULL;
@@ -993,6 +994,7 @@ static bool _sde_rm_check_lm_and_get_connected_blks(
 	lm_primary_pref = lm_cfg->features & BIT(SDE_DISP_PRIMARY_PREF);
 	lm_primary_pref = lm_cfg->features & BIT(SDE_DISP_PRIMARY_PREF);
 	lm_secondary_pref = lm_cfg->features & BIT(SDE_DISP_SECONDARY_PREF);
 	lm_secondary_pref = lm_cfg->features & BIT(SDE_DISP_SECONDARY_PREF);
 	cwb_pref = lm_cfg->features & BIT(SDE_DISP_CWB_PREF);
 	cwb_pref = lm_cfg->features & BIT(SDE_DISP_CWB_PREF);
+	dcwb_pref = lm_cfg->features & BIT(SDE_DISP_DCWB_PREF);
 	is_conn_primary = (reqs->hw_res.display_type ==
 	is_conn_primary = (reqs->hw_res.display_type ==
 				 SDE_CONNECTOR_PRIMARY) ? true : false;
 				 SDE_CONNECTOR_PRIMARY) ? true : false;
 	is_conn_secondary = (reqs->hw_res.display_type ==
 	is_conn_secondary = (reqs->hw_res.display_type ==
@@ -1026,8 +1028,9 @@ static bool _sde_rm_check_lm_and_get_connected_blks(
 		 * If CWB is enabled and LM is not CWB supported
 		 * If CWB is enabled and LM is not CWB supported
 		 * then return false.
 		 * then return false.
 		 */
 		 */
-		if (RM_RQ_CWB(reqs) && !cwb_pref) {
-			SDE_DEBUG("fail: cwb supported lm not allocated\n");
+		if ((RM_RQ_CWB(reqs) && !cwb_pref) ||
+		    (RM_RQ_DCWB(reqs) && !dcwb_pref)) {
+			SDE_DEBUG("fail: cwb/dcwb supported lm not allocated\n");
 			return false;
 			return false;
 		}
 		}
 	} else if ((!is_conn_primary && lm_primary_pref) ||
 	} else if ((!is_conn_primary && lm_primary_pref) ||
@@ -1992,6 +1995,7 @@ static int _sde_rm_populate_requirements(
 		struct drm_encoder *enc,
 		struct drm_encoder *enc,
 		struct drm_crtc_state *crtc_state,
 		struct drm_crtc_state *crtc_state,
 		struct drm_connector_state *conn_state,
 		struct drm_connector_state *conn_state,
+		struct sde_mdss_cfg *cfg,
 		struct sde_rm_requirements *reqs)
 		struct sde_rm_requirements *reqs)
 {
 {
 	const struct drm_display_mode *mode = &crtc_state->mode;
 	const struct drm_display_mode *mode = &crtc_state->mode;
@@ -2031,8 +2035,12 @@ static int _sde_rm_populate_requirements(
 	 * Set the requirement for LM which has CWB support if CWB is
 	 * Set the requirement for LM which has CWB support if CWB is
 	 * found enabled.
 	 * found enabled.
 	 */
 	 */
-	if (!RM_RQ_CWB(reqs) && sde_encoder_in_clone_mode(enc)) {
-		reqs->top_ctrl |= BIT(SDE_RM_TOPCTL_CWB);
+	if ((!RM_RQ_CWB(reqs) || !RM_RQ_DCWB(reqs))
+				&& sde_encoder_in_clone_mode(enc)) {
+		if (cfg->has_dedicated_cwb_support)
+			reqs->top_ctrl |= BIT(SDE_RM_TOPCTL_DCWB);
+		else
+			reqs->top_ctrl |= BIT(SDE_RM_TOPCTL_CWB);
 
 
 		/*
 		/*
 		 * topology selection based on conn mode is not valid for CWB
 		 * topology selection based on conn mode is not valid for CWB
@@ -2476,7 +2484,7 @@ int sde_rm_reserve(
 
 
 	reqs.hw_res.comp_info = comp_info;
 	reqs.hw_res.comp_info = comp_info;
 	ret = _sde_rm_populate_requirements(rm, enc, crtc_state,
 	ret = _sde_rm_populate_requirements(rm, enc, crtc_state,
-			conn_state, &reqs);
+			conn_state, sde_kms->catalog, &reqs);
 	if (ret) {
 	if (ret) {
 		SDE_ERROR("failed to populate hw requirements\n");
 		SDE_ERROR("failed to populate hw requirements\n");
 		goto end;
 		goto end;

+ 3 - 1
msm/sde/sde_rm.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
 /*
- * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
  */
  */
 
 
 #ifndef __SDE_RM_H__
 #ifndef __SDE_RM_H__
@@ -104,6 +104,7 @@ enum sde_rm_topology_group {
  * @SDE_RM_TOPCTL_DSPP: Require layer mixers with DSPP capabilities
  * @SDE_RM_TOPCTL_DSPP: Require layer mixers with DSPP capabilities
  * @SDE_RM_TOPCTL_DS  : Require layer mixers with DS capabilities
  * @SDE_RM_TOPCTL_DS  : Require layer mixers with DS capabilities
  * @SDE_RM_TOPCTL_CWB  : Require layer mixers with CWB capabilities
  * @SDE_RM_TOPCTL_CWB  : Require layer mixers with CWB capabilities
+ * @SDE_RM_TOPCTL_DCWB : Require layer mixers with DCWB capabilities
  */
  */
 enum sde_rm_topology_control {
 enum sde_rm_topology_control {
 	SDE_RM_TOPCTL_RESERVE_LOCK,
 	SDE_RM_TOPCTL_RESERVE_LOCK,
@@ -111,6 +112,7 @@ enum sde_rm_topology_control {
 	SDE_RM_TOPCTL_DSPP,
 	SDE_RM_TOPCTL_DSPP,
 	SDE_RM_TOPCTL_DS,
 	SDE_RM_TOPCTL_DS,
 	SDE_RM_TOPCTL_CWB,
 	SDE_RM_TOPCTL_CWB,
+	SDE_RM_TOPCTL_DCWB,
 };
 };
 
 
 /**
 /**