Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 68aa09e9 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/msm/sde: CCN Cleanup for sde_rm"

parents 28f2b66a 390d3e06
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -110,6 +110,16 @@ static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
	return to_sde_kms(priv->kms);
}

static inline struct drm_encoder *_sde_crtc_get_encoder(struct drm_crtc *crtc)
{
	struct drm_encoder *enc;

	drm_for_each_encoder_mask(enc, crtc->dev, crtc->state->encoder_mask)
		return enc;

	return NULL;
}

static inline int _sde_crtc_power_enable(struct sde_crtc *sde_crtc, bool enable)
{
	struct drm_crtc *crtc;
@@ -3107,6 +3117,8 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc,
{
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *cstate;
	struct drm_encoder *enc;
	struct sde_kms *sde_kms;

	if (!crtc || !state) {
		SDE_ERROR("invalid argument(s)\n");
@@ -3115,9 +3127,14 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc,

	sde_crtc = to_sde_crtc(crtc);
	cstate = to_sde_crtc_state(state);
	enc = _sde_crtc_get_encoder(crtc);
	sde_kms = _sde_crtc_get_kms(crtc);

	SDE_DEBUG("crtc%d\n", crtc->base.id);

	if (sde_kms && enc)
		sde_rm_release(&sde_kms->rm, enc, true);

	__drm_atomic_helper_crtc_destroy_state(state);

	/* destroy value helper */
+1 −1
Original line number Diff line number Diff line
@@ -3287,7 +3287,7 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)

	SDE_DEBUG_ENC(sde_enc, "encoder disabled\n");

	sde_rm_release(&sde_kms->rm, drm_enc);
	sde_rm_release(&sde_kms->rm, drm_enc, false);
}

void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc,
+289 −171
Original line number Diff line number Diff line
@@ -18,7 +18,8 @@
#include "sde_hw_dsc.h"

#define RESERVED_BY_OTHER(h, r) \
	((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id))
	(((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) ||\
		((h)->rsvp_nxt && ((h)->rsvp_nxt->enc_id != (r)->enc_id)))

#define RM_RQ_LOCK(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_RESERVE_LOCK))
#define RM_RQ_CLEAR(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_RESERVE_CLEAR))
@@ -390,72 +391,11 @@ static int _sde_rm_hw_blk_create(
	return 0;
}

int sde_rm_init(struct sde_rm *rm,
static int _sde_rm_hw_blk_create_new(struct sde_rm *rm,
			struct sde_mdss_cfg *cat,
		void __iomem *mmio,
		struct drm_device *dev)
			void __iomem *mmio)
{
	int i, rc = 0;
	enum sde_hw_blk_type type;

	if (!rm || !cat || !mmio || !dev) {
		SDE_ERROR("invalid kms\n");
		return -EINVAL;
	}

	/* Clear, setup lists */
	memset(rm, 0, sizeof(*rm));

	mutex_init(&rm->rm_lock);

	INIT_LIST_HEAD(&rm->rsvps);
	for (type = 0; type < SDE_HW_BLK_MAX; type++)
		INIT_LIST_HEAD(&rm->hw_blks[type]);

	rm->dev = dev;

	if (IS_SDE_CTL_REV_100(cat->ctl_rev))
		rm->topology_tbl = g_ctl_ver_1_top_table;
	else
		rm->topology_tbl = g_top_table;

	/* Some of the sub-blocks require an mdptop to be created */
	rm->hw_mdp = sde_hw_mdptop_init(MDP_TOP, mmio, cat);
	if (IS_ERR_OR_NULL(rm->hw_mdp)) {
		rc = PTR_ERR(rm->hw_mdp);
		rm->hw_mdp = NULL;
		SDE_ERROR("failed: mdp hw not available\n");
		goto fail;
	}

	/* Interrogate HW catalog and create tracking items for hw blocks */
	for (i = 0; i < cat->mixer_count; i++) {
		struct sde_lm_cfg *lm = &cat->mixer[i];

		if (lm->pingpong == PINGPONG_MAX) {
			SDE_ERROR("mixer %d without pingpong\n", lm->id);
			goto fail;
		}

		rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_LM,
				cat->mixer[i].id, &cat->mixer[i]);
		if (rc) {
			SDE_ERROR("failed: lm hw not available\n");
			goto fail;
		}

		if (!rm->lm_max_width) {
			rm->lm_max_width = lm->sblk->maxwidth;
		} else if (rm->lm_max_width != lm->sblk->maxwidth) {
			/*
			 * Don't expect to have hw where lm max widths differ.
			 * If found, take the min.
			 */
			SDE_ERROR("unsupported: lm maxwidth differs\n");
			if (rm->lm_max_width > lm->sblk->maxwidth)
				rm->lm_max_width = lm->sblk->maxwidth;
		}
	}

	for (i = 0; i < cat->dspp_count; i++) {
		rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_DSPP,
@@ -536,6 +476,79 @@ int sde_rm_init(struct sde_rm *rm,
		}
	}

fail:
	return rc;
}

int sde_rm_init(struct sde_rm *rm,
		struct sde_mdss_cfg *cat,
		void __iomem *mmio,
		struct drm_device *dev)
{
	int i, rc = 0;
	enum sde_hw_blk_type type;

	if (!rm || !cat || !mmio || !dev) {
		SDE_ERROR("invalid input params\n");
		return -EINVAL;
	}

	/* Clear, setup lists */
	memset(rm, 0, sizeof(*rm));

	mutex_init(&rm->rm_lock);

	INIT_LIST_HEAD(&rm->rsvps);
	for (type = 0; type < SDE_HW_BLK_MAX; type++)
		INIT_LIST_HEAD(&rm->hw_blks[type]);

	rm->dev = dev;

	if (IS_SDE_CTL_REV_100(cat->ctl_rev))
		rm->topology_tbl = g_ctl_ver_1_top_table;
	else
		rm->topology_tbl = g_top_table;

	/* Some of the sub-blocks require an mdptop to be created */
	rm->hw_mdp = sde_hw_mdptop_init(MDP_TOP, mmio, cat);
	if (IS_ERR_OR_NULL(rm->hw_mdp)) {
		rc = PTR_ERR(rm->hw_mdp);
		rm->hw_mdp = NULL;
		SDE_ERROR("failed: mdp hw not available\n");
		goto fail;
	}

	/* Interrogate HW catalog and create tracking items for hw blocks */
	for (i = 0; i < cat->mixer_count; i++) {
		struct sde_lm_cfg *lm = &cat->mixer[i];

		if (lm->pingpong == PINGPONG_MAX) {
			SDE_ERROR("mixer %d without pingpong\n", lm->id);
			goto fail;
		}

		rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_LM,
				cat->mixer[i].id, &cat->mixer[i]);
		if (rc) {
			SDE_ERROR("failed: lm hw not available\n");
			goto fail;
		}

		if (!rm->lm_max_width) {
			rm->lm_max_width = lm->sblk->maxwidth;
		} else if (rm->lm_max_width != lm->sblk->maxwidth) {
			/*
			 * Don't expect to have hw where lm max widths differ.
			 * If found, take the min.
			 */
			SDE_ERROR("unsupported: lm maxwidth differs\n");
			if (rm->lm_max_width > lm->sblk->maxwidth)
				rm->lm_max_width = lm->sblk->maxwidth;
		}
	}

	rc = _sde_rm_hw_blk_create_new(rm, cat, mmio);
	if (!rc)
		return 0;

fail:
@@ -544,63 +557,18 @@ int sde_rm_init(struct sde_rm *rm,
	return rc;
}

/**
 * _sde_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets
 *	proposed use case requirements, incl. hardwired dependent blocks like
 *	pingpong, and dspp.
 * @rm: sde resource manager handle
 * @rsvp: reservation currently being created
 * @reqs: proposed use case requirements
 * @lm: proposed layer mixer, function checks if lm, and all other hardwired
 *      blocks connected to the lm (pp, dspp) are available and appropriate
 * @dspp: output parameter, dspp block attached to the layer mixer.
 *        NULL if dspp was not available, or not matching requirements.
 * @pp: output parameter, pingpong block attached to the layer mixer.
 *      NULL if dspp was not available, or not matching requirements.
 * @primary_lm: if non-null, this function check if lm is compatible primary_lm
 *              as well as satisfying all other requirements
 * @Return: true if lm matches all requirements, false otherwise
 */
static bool _sde_rm_check_lm_and_get_connected_blks(
static bool _sde_rm_check_lm(
		struct sde_rm *rm,
		struct sde_rm_rsvp *rsvp,
		struct sde_rm_requirements *reqs,
		const struct sde_lm_cfg *lm_cfg,
		struct sde_rm_hw_blk *lm,
		struct sde_rm_hw_blk **dspp,
		struct sde_rm_hw_blk **ds,
		struct sde_rm_hw_blk **pp,
		struct sde_rm_hw_blk *primary_lm)
		struct sde_rm_hw_blk **pp)
{
	const struct sde_lm_cfg *lm_cfg = to_sde_hw_mixer(lm->hw)->cap;
	const struct sde_pingpong_cfg *pp_cfg;
	struct sde_rm_hw_iter iter;
	bool is_valid_dspp, is_valid_ds, ret;
	u32 display_pref, cwb_pref;

	*dspp = NULL;
	*ds = NULL;
	*pp = NULL;
	display_pref = lm_cfg->features & BIT(SDE_DISP_PRIMARY_PREF);
	cwb_pref = lm_cfg->features & BIT(SDE_DISP_CWB_PREF);

	SDE_DEBUG("check lm %d: dspp %d ds %d pp %d disp_pref: %d cwb_pref%d\n",
		lm_cfg->id, lm_cfg->dspp, lm_cfg->ds,
		lm_cfg->pingpong, display_pref, cwb_pref);

	/* Check if this layer mixer is a peer of the proposed primary LM */
	if (primary_lm) {
		const struct sde_lm_cfg *prim_lm_cfg =
				to_sde_hw_mixer(primary_lm->hw)->cap;

		if (!test_bit(lm_cfg->id, &prim_lm_cfg->lm_pair_mask)) {
			SDE_DEBUG("lm %d not peer of lm %d\n", lm_cfg->id,
					prim_lm_cfg->id);
			return false;
		}
	}

	/* bypass rest of the checks if LM for primary display is found */
	if (!display_pref) {
	is_valid_dspp = (lm_cfg->dspp != DSPP_MAX) ? true : false;
	is_valid_ds = (lm_cfg->ds != DS_MAX) ? true : false;

@@ -625,30 +593,20 @@ static bool _sde_rm_check_lm_and_get_connected_blks(
			lm_cfg->id, (bool)(RM_RQ_DSPP(reqs)),
			lm_cfg->dspp, (bool)(RM_RQ_DS(reqs)),
			lm_cfg->ds);
			return ret;
		}

		/**
		 * If CWB is enabled and LM is not CWB supported
		 * then return false.
		 */
		if (RM_RQ_CWB(reqs) && !cwb_pref) {
			SDE_DEBUG("fail: cwb supported lm not allocated\n");
			return false;
		return ret;
	}

	} else if (!(reqs->hw_res.is_primary && display_pref)) {
		SDE_DEBUG(
			"display preference is not met. is_primary: %d display_pref: %d\n",
			(int)reqs->hw_res.is_primary, (int)display_pref);
		return false;
	return true;
}

	/* Already reserved? */
	if (RESERVED_BY_OTHER(lm, rsvp)) {
		SDE_DEBUG("lm %d already reserved\n", lm_cfg->id);
		return false;
	}
static bool _sde_rm_reserve_dspp(
		struct sde_rm *rm,
		struct sde_rm_rsvp *rsvp,
		const struct sde_lm_cfg *lm_cfg,
		struct sde_rm_hw_blk *lm,
		struct sde_rm_hw_blk **dspp)
{
	struct sde_rm_hw_iter iter;

	if (lm_cfg->dspp != DSPP_MAX) {
		sde_rm_init_hw_iter(&iter, 0, SDE_HW_BLK_DSPP);
@@ -672,6 +630,19 @@ static bool _sde_rm_check_lm_and_get_connected_blks(
		}
	}

	return true;
}


static bool _sde_rm_reserve_ds(
		struct sde_rm *rm,
		struct sde_rm_rsvp *rsvp,
		const struct sde_lm_cfg *lm_cfg,
		struct sde_rm_hw_blk *lm,
		struct sde_rm_hw_blk **ds)
{
	struct sde_rm_hw_iter iter;

	if (lm_cfg->ds != DS_MAX) {
		sde_rm_init_hw_iter(&iter, 0, SDE_HW_BLK_DS);
		while (_sde_rm_get_hw_locked(rm, &iter)) {
@@ -694,6 +665,22 @@ static bool _sde_rm_check_lm_and_get_connected_blks(
		}
	}

	return true;
}

static bool _sde_rm_reserve_pp(
		struct sde_rm *rm,
		struct sde_rm_rsvp *rsvp,
		struct sde_rm_requirements *reqs,
		const struct sde_lm_cfg *lm_cfg,
		const struct sde_pingpong_cfg *pp_cfg,
		struct sde_rm_hw_blk *lm,
		struct sde_rm_hw_blk **dspp,
		struct sde_rm_hw_blk **ds,
		struct sde_rm_hw_blk **pp)
{
	struct sde_rm_hw_iter iter;

	sde_rm_init_hw_iter(&iter, 0, SDE_HW_BLK_PINGPONG);
	while (_sde_rm_get_hw_locked(rm, &iter)) {
		if (iter.blk->id == lm_cfg->pingpong) {
@@ -723,6 +710,107 @@ static bool _sde_rm_check_lm_and_get_connected_blks(
		*ds = NULL;
		return false;
	}
	return true;
}

/**
 * _sde_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets
 *	proposed use case requirements, incl. hardwired dependent blocks like
 *	pingpong, and dspp.
 * @rm: sde resource manager handle
 * @rsvp: reservation currently being created
 * @reqs: proposed use case requirements
 * @lm: proposed layer mixer, function checks if lm, and all other hardwired
 *      blocks connected to the lm (pp, dspp) are available and appropriate
 * @dspp: output parameter, dspp block attached to the layer mixer.
 *        NULL if dspp was not available, or not matching requirements.
 * @pp: output parameter, pingpong block attached to the layer mixer.
 *      NULL if dspp was not available, or not matching requirements.
 * @primary_lm: if non-null, this function check if lm is compatible primary_lm
 *              as well as satisfying all other requirements
 * @Return: true if lm matches all requirements, false otherwise
 */
static bool _sde_rm_check_lm_and_get_connected_blks(
		struct sde_rm *rm,
		struct sde_rm_rsvp *rsvp,
		struct sde_rm_requirements *reqs,
		struct sde_rm_hw_blk *lm,
		struct sde_rm_hw_blk **dspp,
		struct sde_rm_hw_blk **ds,
		struct sde_rm_hw_blk **pp,
		struct sde_rm_hw_blk *primary_lm)
{
	const struct sde_lm_cfg *lm_cfg = to_sde_hw_mixer(lm->hw)->cap;
	const struct sde_pingpong_cfg *pp_cfg;
	bool ret;
	u32 display_pref, cwb_pref;

	*dspp = NULL;
	*ds = NULL;
	*pp = NULL;
	display_pref = lm_cfg->features & BIT(SDE_DISP_PRIMARY_PREF);
	cwb_pref = lm_cfg->features & BIT(SDE_DISP_CWB_PREF);

	SDE_DEBUG("check lm %d: dspp %d ds %d pp %d disp_pref: %d cwb_pref%d\n",
		lm_cfg->id, lm_cfg->dspp, lm_cfg->ds,
		lm_cfg->pingpong, display_pref, cwb_pref);

	/* Check if this layer mixer is a peer of the proposed primary LM */
	if (primary_lm) {
		const struct sde_lm_cfg *prim_lm_cfg =
				to_sde_hw_mixer(primary_lm->hw)->cap;

		if (!test_bit(lm_cfg->id, &prim_lm_cfg->lm_pair_mask)) {
			SDE_DEBUG("lm %d not peer of lm %d\n", lm_cfg->id,
					prim_lm_cfg->id);
			return false;
		}
	}

	/* bypass rest of the checks if LM for primary display is found */
	if (!display_pref) {
		/* Check lm for valid requirements */
		ret = _sde_rm_check_lm(rm, rsvp, reqs, lm_cfg, lm,
				dspp, ds, pp);
		if (!ret)
			return ret;

		/**
		 * If CWB is enabled and LM is not CWB supported
		 * then return false.
		 */
		if (RM_RQ_CWB(reqs) && !cwb_pref) {
			SDE_DEBUG("fail: cwb supported lm not allocated\n");
			return false;
		}
	} else if (!(reqs->hw_res.is_primary && display_pref)) {
		SDE_DEBUG(
			"display preference is not met. is_primary: %d display_pref: %d\n",
			(int)reqs->hw_res.is_primary, (int)display_pref);
		return false;
	}

	/* Already reserved? */
	if (RESERVED_BY_OTHER(lm, rsvp)) {
		SDE_DEBUG("lm %d already reserved\n", lm_cfg->id);
		return false;
	}

	/* Reserve dspp */
	ret = _sde_rm_reserve_dspp(rm, rsvp, lm_cfg, lm, dspp);
	if (!ret)
		return ret;

	/* Reserve ds */
	ret = _sde_rm_reserve_ds(rm, rsvp, lm_cfg, lm, ds);
	if (!ret)
		return ret;

	/* Reserve pp */
	ret = _sde_rm_reserve_pp(rm, rsvp, reqs, lm_cfg, pp_cfg, lm,
			dspp, ds, pp);
	if (!ret)
		return ret;

	return true;
}
@@ -1452,6 +1540,26 @@ static struct sde_rm_rsvp *_sde_rm_get_rsvp(
	return NULL;
}

static struct sde_rm_rsvp *_sde_rm_get_rsvp_nxt(
		struct sde_rm *rm,
		struct drm_encoder *enc)
{
	struct sde_rm_rsvp *i;

	if (list_empty(&rm->rsvps))
		return NULL;

	list_for_each_entry(i, &rm->rsvps, list)
		if (i->enc_id == enc->base.id)
			break;

	list_for_each_entry_continue(i, &rm->rsvps, list)
		if (i->enc_id == enc->base.id)
			return i;

	return NULL;
}

static struct drm_connector *_sde_rm_get_connector(
		struct drm_encoder *enc)
{
@@ -1539,7 +1647,7 @@ static void _sde_rm_release_rsvp(
	kfree(rsvp);
}

void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc)
void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc, bool nxt)
{
	struct sde_rm_rsvp *rsvp;
	struct drm_connector *conn;
@@ -1552,15 +1660,20 @@ void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc)

	mutex_lock(&rm->rm_lock);

	if (nxt)
		rsvp = _sde_rm_get_rsvp_nxt(rm, enc);
	else
		rsvp = _sde_rm_get_rsvp(rm, enc);
	if (!rsvp) {
		SDE_ERROR("failed to find rsvp for enc %d\n", enc->base.id);
		SDE_DEBUG("failed to find rsvp for enc %d, nxt %d",
				enc->base.id, nxt);
		goto end;
	}

	conn = _sde_rm_get_connector(enc);
	if (!conn) {
		SDE_ERROR("failed to get connector for enc %d\n", enc->base.id);
		SDE_ERROR("failed to get connector for enc %d, nxt %d",
				enc->base.id, nxt);
		goto end;
	}

@@ -1651,6 +1764,12 @@ int sde_rm_reserve(

	_sde_rm_print_rsvps(rm, SDE_RM_STAGE_BEGIN);

	rsvp_cur = _sde_rm_get_rsvp(rm, enc);
	rsvp_nxt = _sde_rm_get_rsvp_nxt(rm, enc);

	if (!test_only && rsvp_nxt)
		goto commit_rsvp;

	ret = _sde_rm_populate_requirements(rm, enc, crtc_state,
			conn_state, &reqs);
	if (ret) {
@@ -1675,8 +1794,6 @@ int sde_rm_reserve(
		goto end;
	}

	rsvp_cur = _sde_rm_get_rsvp(rm, enc);

	/*
	 * User can request that we clear out any reservation during the
	 * atomic_check phase by using this CLEAR bit
@@ -1696,30 +1813,31 @@ int sde_rm_reserve(
	_sde_rm_print_rsvps(rm, SDE_RM_STAGE_AFTER_RSVPNEXT);

	if (ret) {
		SDE_ERROR("failed to reserve hw resources: %d\n", ret);
		SDE_ERROR("failed to reserve hw resources: %d, test_only %d\n",
				ret, test_only);
		_sde_rm_release_rsvp(rm, rsvp_nxt, conn_state->connector);
		goto end;
	} else if (test_only && !RM_RQ_LOCK(&reqs)) {
		/*
		 * Normally, if test_only, test the reservation and then undo
		 * However, if the user requests LOCK, then keep the reservation
		 * made during the atomic_check phase.
		 */
		SDE_DEBUG("test_only: discard test rsvp[s%de%d]\n",
		SDE_DEBUG("test_only: rsvp[s%de%d]\n",
				rsvp_nxt->seq, rsvp_nxt->enc_id);
		_sde_rm_release_rsvp(rm, rsvp_nxt, conn_state->connector);
		goto end;
	} else {
		if (test_only && RM_RQ_LOCK(&reqs))
			SDE_DEBUG("test_only & LOCK: lock rsvp[s%de%d]\n",
					rsvp_nxt->seq, rsvp_nxt->enc_id);
	}

commit_rsvp:
	_sde_rm_release_rsvp(rm, rsvp_cur, conn_state->connector);

	ret = _sde_rm_commit_rsvp(rm, rsvp_nxt, conn_state);
	}

	_sde_rm_print_rsvps(rm, SDE_RM_STAGE_FINAL);

end:
	_sde_rm_print_rsvps(rm, SDE_RM_STAGE_FINAL);
	mutex_unlock(&rm->rm_lock);

	return ret;
+3 −2
Original line number Diff line number Diff line
@@ -184,13 +184,14 @@ int sde_rm_reserve(struct sde_rm *rm,
		bool test_only);

/**
 * sde_rm_reserve - Given the encoder for the display chain, release any
 * sde_rm_release - Given the encoder for the display chain, release any
 *	HW blocks previously reserved for that use case.
 * @rm: SDE Resource Manager handle
 * @enc: DRM Encoder handle
 * @nxt: Choose option to release rsvp_nxt
 * @Return: 0 on Success otherwise -ERROR
 */
void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc);
void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc, bool nxt);

/**
 * sde_rm_get_mdp - Retrieve HW block for MDP TOP.