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

Commit e7bcdd27 authored by Lloyd Atkinson's avatar Lloyd Atkinson Committed by Narendra Muppalla
Browse files

drm/msm/sde: move crtc towards multi-encoder support



SDE CRTC currently assumes one encoder per CRTC associated at
initialization time. Need to break this assumption and move
towards tracking multiple encoders, though full dynamic
assignment is not provided by this patch.

Change-Id: I47f42f5ef57f4df2f19f66bdef737cc86906ab1d
Signed-off-by: default avatarLloyd Atkinson <latkinso@codeaurora.org>
parent 6b3b9dd8
Loading
Loading
Loading
Loading
+87 −113
Original line number Diff line number Diff line
@@ -42,8 +42,7 @@ static struct sde_kms *get_kms(struct drm_crtc *crtc)
	return to_sde_kms(priv->kms);
}

static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc,
		struct drm_encoder *encoder)
static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc)
{
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	struct sde_kms *sde_kms = get_kms(crtc);
@@ -71,14 +70,14 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc,
	}

	/* query encoder resources */
	sde_encoder_get_hw_resources(sde_crtc->encoder, &enc_hw_res);
	sde_encoder_get_hw_resources(sde_crtc->mixers[0].encoder, &enc_hw_res);

	/* parse encoder hw resources, find CTL paths */
	for (i = CTL_0; i <= sde_kms->catalog->ctl_count; i++) {
		WARN_ON(sde_crtc->num_ctls > CRTC_DUAL_MIXERS);
		if (enc_hw_res.ctls[i]) {
			struct sde_crtc_mixer *mixer  =
				&sde_crtc->mixer[sde_crtc->num_ctls];
				&sde_crtc->mixers[sde_crtc->num_ctls];
			mixer->hw_ctl = sde_rm_get_ctl_path(sde_kms, i);
			if (IS_ERR_OR_NULL(mixer->hw_ctl)) {
				DRM_ERROR("Invalid ctl_path\n");
@@ -99,7 +98,7 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc,
	for (i = INTF_0; i <= sde_kms->catalog->intf_count; i++) {
		if (enc_hw_res.intfs[i]) {
			struct sde_crtc_mixer *mixer  =
				&sde_crtc->mixer[sde_crtc->num_mixers];
				&sde_crtc->mixers[sde_crtc->num_mixers];
			plat_hw_res_map = sde_rm_get_res_map(sde_kms,
					i, SDE_NONE);

@@ -107,15 +106,12 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc,
			if (!lm_idx && unused_lm_count)
				lm_idx = unused_lm_id[--unused_lm_count];

			DBG("Acquiring LM %d", lm_idx);
			DBG("intf %d acquiring lm %d", i, lm_idx);
			mixer->hw_lm = sde_rm_acquire_mixer(sde_kms, lm_idx);
			if (IS_ERR_OR_NULL(mixer->hw_lm)) {
				DRM_ERROR("Invalid mixer\n");
				return -EACCES;
			}
			/* interface info */
			mixer->intf_idx = i;
			mixer->mode = enc_hw_res.intfs[i];
			sde_crtc->num_mixers++;
		}
	}
@@ -127,7 +123,7 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc,
	for (i = WB_0; i < WB_MAX; i++) {
		if (enc_hw_res.wbs[i]) {
			struct sde_crtc_mixer *mixer =
				&sde_crtc->mixer[sde_crtc->num_mixers];
				&sde_crtc->mixers[sde_crtc->num_mixers];
			plat_hw_res_map = sde_rm_get_res_map(sde_kms,
					SDE_NONE, i);

@@ -135,27 +131,24 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc,
			if (!lm_idx && unused_lm_count)
				lm_idx = unused_lm_id[--unused_lm_count];

			DBG("Acquiring LM %d", lm_idx);
			DBG("wb %d acquiring lm %d", i, lm_idx);
			mixer->hw_lm = sde_rm_acquire_mixer(sde_kms, lm_idx);
			if (IS_ERR_OR_NULL(mixer->hw_lm)) {
				DRM_ERROR("Invalid mixer\n");
				return -EACCES;
			}
			/* interface info */
			mixer->wb_idx = i;
			mixer->mode = enc_hw_res.wbs[i];
			sde_crtc->num_mixers++;
		}
	}

	DBG("control paths %d, num_mixers %d, lm[0] %d, ctl[0] %d ",
			sde_crtc->num_ctls, sde_crtc->num_mixers,
			sde_crtc->mixer[0].hw_lm->idx,
			sde_crtc->mixer[0].hw_ctl->idx);
			sde_crtc->mixers[0].hw_lm->idx,
			sde_crtc->mixers[0].hw_ctl->idx);
	if (sde_crtc->num_mixers > 1)
		DBG("lm[1] %d, ctl[1], %d",
			sde_crtc->mixer[1].hw_lm->idx,
			sde_crtc->mixer[1].hw_ctl->idx);
			sde_crtc->mixers[1].hw_lm->idx,
			sde_crtc->mixers[1].hw_ctl->idx);
	return 0;
}

@@ -271,7 +264,7 @@ static void sde_crtc_get_blend_cfg(struct sde_hw_blend_cfg *cfg,
static void blend_setup(struct drm_crtc *crtc)
{
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	struct sde_crtc_mixer *mixer = sde_crtc->mixer;
	struct sde_crtc_mixer *mixer = sde_crtc->mixers;
	struct drm_plane *plane;
	struct sde_plane_state *pstate;
	struct sde_hw_blend_cfg blend;
@@ -306,7 +299,7 @@ static void blend_setup(struct drm_crtc *crtc)
				sde_plane_pipe(plane);
			DBG("crtc_id %d - mixer %d pipe %d at stage %d",
					i,
					sde_crtc->id,
					crtc->base.id,
					sde_plane_pipe(plane),
					pstate->stage);
			plane_cnt++;
@@ -370,7 +363,7 @@ void sde_crtc_prepare_fence(struct drm_crtc *crtc)

	sde_crtc = to_sde_crtc(crtc);

	MSM_EVT(crtc->dev, sde_crtc->id, crtc->enabled);
	MSM_EVT(crtc->dev, crtc->base.id, 0);

	sde_fence_prepare(&sde_crtc->output_fence);
}
@@ -392,8 +385,9 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
		 */
		if (!file || (event->base.file_priv == file)) {
			sde_crtc->event = NULL;
			DBG("%s: send event: %pK", sde_crtc->name, event);
			drm_send_vblank_event(dev, sde_crtc->id, event);
			SDE_DEBUG("%s: send event: %pK", sde_crtc->name, event);
			drm_send_vblank_event(dev, sde_crtc->drm_crtc_id,
					event);
		}
	}
	spin_unlock_irqrestore(&dev->event_lock, flags);
@@ -415,10 +409,10 @@ static void sde_crtc_vblank_cb(void *data)
		drm_crtc_vblank_put(crtc);
	}

	if (sde_crtc->drm_requested_vblank) {
		drm_handle_vblank(dev, sde_crtc->id);
	if (atomic_read(&sde_crtc->drm_requested_vblank)) {
		drm_handle_vblank(dev, sde_crtc->drm_crtc_id);
		DBG_IRQ("");
		MSM_EVT(crtc->dev, sde_crtc->id, 0);
		MSM_EVT(crtc->dev, crtc->base.id, 0);
	}
}

@@ -434,30 +428,13 @@ static u32 _sde_crtc_update_ctl_flush_mask(struct drm_crtc *crtc)
		return -EINVAL;
	}

	MSM_EVT(crtc->dev, sde_crtc->id, 0);
	MSM_EVT(crtc->dev, crtc->base.id, 0);

	DBG("");

	for (i = 0; i < sde_crtc->num_ctls; i++) {
		mixer = &sde_crtc->mixer[i];
		mixer = &sde_crtc->mixers[i];
		ctl = mixer->hw_ctl;

		switch (mixer->mode) {
		case INTF_MODE_CMD:
		case INTF_MODE_VIDEO:
			ctl->ops.get_bitmask_intf(ctl, &mixer->flush_mask,
					mixer->intf_idx);
			break;
		case INTF_MODE_WB_LINE:
			ctl->ops.get_bitmask_wb(ctl, &mixer->flush_mask,
					mixer->wb_idx);
			break;
		default:
			DBG("Invalid ctl %d interface mode %d", ctl->idx,
					mixer->mode);
			return -EINVAL;
		}

		ctl->ops.update_pending_flush(ctl, mixer->flush_mask);
		DBG("added CTL_ID %d mask 0x%x to pending flush", ctl->idx,
						mixer->flush_mask);
@@ -494,55 +471,37 @@ static void _sde_crtc_trigger_kickoff(void *data)
{
	struct drm_crtc *crtc = (struct drm_crtc *)data;
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	struct sde_crtc_mixer *mixer;
	struct sde_hw_ctl *ctl;
	u32 i;
	int i;

	if (!data) {
		DRM_ERROR("invalid argument\n");
		return;
	}

	MSM_EVT(crtc->dev, sde_crtc->id, 0);
	MSM_EVT(crtc->dev, crtc->base.id, 0);

	/* Commit all pending flush masks to hardware */
	for (i = 0; i < sde_crtc->num_ctls; i++) {
		ctl = sde_crtc->mixer[i].hw_ctl;
	for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) {
		ctl = sde_crtc->mixers[i].hw_ctl;
		if (ctl) {
			ctl->ops.trigger_flush(ctl);
			MSM_EVT(crtc->dev, crtc->base.id, ctl->idx);
		}
	}

	/* Signal start to any interface types that require it */
	for (i = 0; i < sde_crtc->num_ctls; i++) {
		ctl = sde_crtc->mixer[i].hw_ctl;
		if (sde_crtc->mixer[i].mode != INTF_MODE_VIDEO) {
	for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) {
		mixer = &sde_crtc->mixers[i];
		ctl = mixer->hw_ctl;
		if (ctl && sde_encoder_needs_ctl_start(mixer->encoder)) {
			ctl->ops.trigger_start(ctl);
			DBG("trigger start on ctl %d", ctl->idx);
			MSM_EVT(crtc->dev, crtc->base.id, ctl->idx);
		}
	}
}

void sde_crtc_wait_for_commit_done(struct drm_crtc *crtc)
{
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	int ret;

	/* ref count the vblank event and interrupts while we wait for it */
	if (drm_crtc_vblank_get(crtc))
		return;

	/*
	 * Wait post-flush if necessary to delay before plane_cleanup
	 * For example, wait for vsync in case of video mode panels
	 * This should be a no-op for command mode panels
	 */
	MSM_EVT(crtc->dev, sde_crtc->id, 0);
	ret = sde_encoder_wait_for_commit_done(sde_crtc->encoder);
	if (ret)
		DBG("sde_encoder_wait_post_flush returned %d", ret);

	/* release vblank event ref count */
	drm_crtc_vblank_put(crtc);
}

/**
 * _sde_crtc_set_input_fence_timeout - update ns version of in fence timeout
 * @cstate: Pointer to sde crtc state
@@ -630,9 +589,9 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc,

	/* Reset flush mask from previous commit */
	for (i = 0; i < sde_crtc->num_ctls; i++) {
		struct sde_hw_ctl *ctl = sde_crtc->mixer[i].hw_ctl;
		struct sde_hw_ctl *ctl = sde_crtc->mixers[i].hw_ctl;

		sde_crtc->mixer[i].flush_mask = 0;
		sde_crtc->mixers[i].flush_mask = 0;
		ctl->ops.clear_pending_flush(ctl);
	}

@@ -751,19 +710,26 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc,

void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
{
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	struct drm_encoder *encoder;
	struct drm_device *dev;

	if (!crtc) {
		DRM_ERROR("invalid argument\n");
		return;
	}
	dev = crtc->dev;

	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
		if (encoder->crtc != crtc)
			continue;

		/*
	 * Encoder will flush/start now, unless it has a tx pending
	 * in which case it may delay and flush at an irq event (e.g. ppdone)
		 * Encoder will flush/start now, unless it has a tx pending.
		 * If so, it may delay and flush at an irq event (e.g. ppdone)
		 */
	sde_encoder_schedule_kickoff(sde_crtc->encoder,
			_sde_crtc_trigger_kickoff, crtc);
		sde_encoder_schedule_kickoff(encoder, _sde_crtc_trigger_kickoff,
				crtc);
	}
}

/**
@@ -876,7 +842,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc)
	DBG("");

	sde_crtc = to_sde_crtc(crtc);
	mixer = sde_crtc->mixer;
	mixer = sde_crtc->mixers;

	if (WARN_ON(!crtc->state))
		return;
@@ -892,7 +858,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc)
	 */
	if ((sde_crtc->num_ctls == 0) ||
		(sde_crtc->num_mixers == 0)) {
		rc = sde_crtc_reserve_hw_resources(crtc, sde_crtc->encoder);
		rc = sde_crtc_reserve_hw_resources(crtc);
		if (rc) {
			DRM_ERROR("error reserving HW resource for CRTC\n");
			return;
@@ -999,25 +965,30 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
int sde_crtc_vblank(struct drm_crtc *crtc, bool en)
{
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	struct drm_encoder *encoder;
	struct drm_device *dev = crtc->dev;

	DBG("%d", en);

	MSM_EVT(crtc->dev, en, 0);

	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
		if (encoder->crtc != crtc)
			continue;
		/*
		 * Mark that framework requested vblank,
		 * as opposed to enabling vblank only for our internal purposes
	 * Currently this variable isn't required, but may be useful for future
	 * features
		 * Currently this variable isn't required, but may be useful for
		 * future features
		 */
	sde_crtc->drm_requested_vblank = en;
		atomic_set(&sde_crtc->drm_requested_vblank, en);
		MSM_EVT(crtc->dev, crtc->base.id, en);

		if (en)
		sde_encoder_register_vblank_callback(sde_crtc->encoder,
			sde_encoder_register_vblank_callback(encoder,
					sde_crtc_vblank_cb, (void *)crtc);
		else
		sde_encoder_register_vblank_callback(sde_crtc->encoder,
				NULL, NULL);
			sde_encoder_register_vblank_callback(encoder, NULL,
					NULL);
	}

	return 0;
}
@@ -1153,17 +1124,15 @@ static int _sde_debugfs_mixer_read(struct seq_file *s, void *data)

	sde_crtc = s->private;
	for (i = 0; i < sde_crtc->num_mixers; ++i) {
		m = &sde_crtc->mixer[i];
		m = &sde_crtc->mixers[i];
		if (!m->hw_lm) {
			seq_printf(s, "Mixer[%d] has no LM\n", i);
		} else if (!m->hw_ctl) {
			seq_printf(s, "Mixer[%d] has no CTL\n", i);
		} else {
			seq_printf(s, "LM_%d/CTL_%d -> INTF_%d, WB_%d\n",
			seq_printf(s, "LM_%d/CTL_%d\n",
					m->hw_lm->idx - LM_0,
					m->hw_ctl->idx - CTL_0,
					m->intf_idx - INTF_0,
					m->wb_idx - WB_0);
					m->hw_ctl->idx - CTL_0);
		}
	}
	seq_printf(s, "Border: %d\n", sde_crtc->stage_cfg.border_enable);
@@ -1232,13 +1201,14 @@ static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
/* initialize crtc */
struct drm_crtc *sde_crtc_init(struct drm_device *dev,
		struct drm_encoder *encoder,
		struct drm_plane *plane, int id)
		struct drm_plane *plane,
		int drm_crtc_id)
{
	struct drm_crtc *crtc = NULL;
	struct sde_crtc *sde_crtc = NULL;
	struct msm_drm_private *priv = NULL;
	struct sde_kms *kms = NULL;
	int rc;
	int i, rc;

	priv = dev->dev_private;
	kms = to_sde_kms(priv->kms);
@@ -1249,8 +1219,9 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev,

	crtc = &sde_crtc->base;

	sde_crtc->id = id;
	sde_crtc->encoder = encoder;
	sde_crtc->drm_crtc_id = drm_crtc_id;
	atomic_set(&sde_crtc->drm_requested_vblank, 0);

	spin_lock_init(&sde_crtc->lm_lock);

	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &sde_crtc_funcs);
@@ -1258,7 +1229,10 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev,
	drm_crtc_helper_add(crtc, &sde_crtc_helper_funcs);
	plane->crtc = crtc;

	rc = sde_crtc_reserve_hw_resources(crtc, encoder);
	for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++)
		sde_crtc->mixers[i].encoder = encoder;

	rc = sde_crtc_reserve_hw_resources(crtc);
	if (rc) {
		DRM_ERROR(" error reserving HW resource for this CRTC\n");
		return ERR_PTR(-EINVAL);
+12 −18
Original line number Diff line number Diff line
@@ -26,31 +26,26 @@
#define CRTC_HW_MIXER_MAXSTAGES(c, idx) ((c)->mixer[idx].sblk->maxblendstages)

/**
 * struct sde_crtc_mixer - stores the map for each virtual pipeline in the CRTC
 * @hw_dspp     : DSPP HW Driver context
 * struct sde_crtc_mixer: stores the map for each virtual pipeline in the CRTC
 * @hw_lm:	LM HW Driver context
 * @hw_ctl:	CTL Path HW driver context
 * @intf_idx    : Interface idx
 * @wb_idx      : Writeback idx
 * @mode        : Interface mode Active/CMD
 * @flush_mask:	Flush mask value for this commit
 * @encoder:	Encoder attached to this lm & ctl
 */
struct sde_crtc_mixer {
	struct sde_hw_dspp  *hw_dspp;
	struct sde_hw_mixer *hw_lm;
	struct sde_hw_ctl   *hw_ctl;
	enum sde_intf       intf_idx;
	enum sde_wb         wb_idx;
	enum sde_intf_mode  mode;
	u32 flush_mask;
	struct drm_encoder *encoder;
};

/**
 * struct sde_crtc - virtualized CRTC data structure
 * @base          : Base drm crtc structure
 * @name          : ASCII description of this crtc
 * @encoder       : Associated drm encoder object
 * @id            : Unique crtc identifier
 * @drm_crtc_id   : Id for reporting vblank. Id is relative init order into
 *                  mode_config.crtc_list and used by user space to identify
 *                  specific crtc in apis such as drm_wait_vblank
 * @lm_lock       : LM register access spinlock
 * @num_ctls      : Number of ctl paths in use
 * @num_mixers    : Number of mixers in use
@@ -67,21 +62,20 @@ struct sde_crtc_mixer {
struct sde_crtc {
	struct drm_crtc base;
	char name[SDE_CRTC_NAME_SIZE];
	struct drm_encoder *encoder;
	int id;
	int drm_crtc_id;

	spinlock_t lm_lock;	/* protect registers */

	/* HW Resources reserved for the crtc */
	u32  num_ctls;
	u32  num_mixers;
	struct sde_crtc_mixer mixer[CRTC_DUAL_MIXERS];
	struct sde_crtc_mixer mixers[CRTC_DUAL_MIXERS];

	/*if there is a pending flip, these will be non-null */
	struct drm_pending_vblank_event *event;
	atomic_t pending;
	u32 vsync_count;
	bool drm_requested_vblank;
	atomic_t drm_requested_vblank;

	struct msm_property_info property_info;
	struct msm_property_data property_data[CRTC_PROP_COUNT];
+18 −0
Original line number Diff line number Diff line
@@ -179,6 +179,24 @@ void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc,
	}
}

bool sde_encoder_needs_ctl_start(struct drm_encoder *drm_enc)
{
	struct sde_encoder_virt *sde_enc = NULL;
	struct sde_encoder_phys *phys;

	if (!drm_enc) {
		DRM_ERROR("Invalid pointer");
		return false;
	}
	sde_enc = to_sde_encoder_virt(drm_enc);
	phys = sde_enc->cur_master;

	if (phys && phys->ops.needs_ctl_start)
		return phys->ops.needs_ctl_start(phys);

	return false;
}

static void sde_encoder_destroy(struct drm_encoder *drm_enc)
{
	struct sde_encoder_virt *sde_enc = NULL;
+2 −0
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ struct sde_encoder_virt_ops {
 *				triggering the next kickoff
 *				(ie for previous tx to complete)
 * @handle_post_kickoff:	Do any work necessary post-kickoff work
 * @needs_ctl_start:		Whether encoder type needs ctl_start
 */

struct sde_encoder_phys_ops {
@@ -102,6 +103,7 @@ struct sde_encoder_phys_ops {
	void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc,
			bool *wait_until_ready);
	void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc);
	bool (*needs_ctl_start)(struct sde_encoder_phys *phys_enc);
};

/**
+7 −0
Original line number Diff line number Diff line
@@ -423,6 +423,12 @@ static void sde_encoder_phys_cmd_prepare_for_kickoff(
	MSM_EVT(DEV(phys_enc), cmd_enc->hw_pp->idx, new_pending_cnt);
}

static bool sde_encoder_phys_cmd_needs_ctl_start(
		struct sde_encoder_phys *phys_enc)
{
	return true;
}

static void sde_encoder_phys_cmd_init_ops(
		struct sde_encoder_phys_ops *ops)
{
@@ -436,6 +442,7 @@ static void sde_encoder_phys_cmd_init_ops(
	ops->control_vblank_irq = sde_encoder_phys_cmd_control_vblank_irq;
	ops->wait_for_commit_done = sde_encoder_phys_cmd_wait_for_commit_done;
	ops->prepare_for_kickoff = sde_encoder_phys_cmd_prepare_for_kickoff;
	ops->needs_ctl_start = sde_encoder_phys_cmd_needs_ctl_start;
}

struct sde_encoder_phys *sde_encoder_phys_cmd_init(
Loading