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

Commit 9557ee14 authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/msm/sde: move crtc towards multi-encoder support" into msm-4.8

parents 48b909a2 e7bcdd27
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