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

Commit ddb4395e authored by Chandan Uddaraju's avatar Chandan Uddaraju Committed by Gerrit - the friendly Code Review server
Browse files

drm/msm/sde: add check for interrupt while disabling auto-refresh



When cont-splash feature is enabled, we were adding delay
when auto-refresh is disabled so that any pending
frames will be transferred before attaching SMMU.
Instead of adding usleep_range(), add check for Ping-Pong
done interrupt when auto-refresh is disabled.
Add support for polling the read-pointer since we don't
have interrupt handler enabled during probe sequence.

Change-Id: I08f807b321eb6980aea881d4885d38f29bf7789d
Signed-off-by: default avatarChandan Uddaraju <chandanu@codeaurora.org>
parent 9f8fa3ac
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -1061,6 +1061,27 @@ static void sde_hw_intr_get_interrupt_statuses(struct sde_hw_intr *intr)
	spin_unlock_irqrestore(&intr->irq_lock, irq_flags);
}

static void sde_hw_intr_clear_intr_status_force_mask(struct sde_hw_intr *intr,
						 int irq_idx, u32 irq_mask)
{
	int reg_idx;

	if (!intr)
		return;

	if (irq_idx >= ARRAY_SIZE(sde_irq_map) || irq_idx < 0) {
		pr_err("invalid IRQ index: [%d]\n", irq_idx);
		return;
	}

	reg_idx = sde_irq_map[irq_idx].reg_idx;
	SDE_REG_WRITE(&intr->hw, sde_intr_set[reg_idx].clr_off,
			irq_mask);

	/* ensure register writes go through */
	wmb();
}

static void sde_hw_intr_clear_intr_status_nolock(struct sde_hw_intr *intr,
		int irq_idx)
{
@@ -1151,6 +1172,31 @@ static u32 sde_hw_intr_get_interrupt_status(struct sde_hw_intr *intr,
	return intr_status;
}

static u32 sde_hw_intr_get_intr_status_nomask(struct sde_hw_intr *intr,
		int irq_idx, bool clear)
{
	int reg_idx;
	unsigned long irq_flags;
	u32 intr_status;

	if (!intr)
		return 0;

	if (irq_idx >= ARRAY_SIZE(sde_irq_map) || irq_idx < 0) {
		pr_err("invalid IRQ index: [%d]\n", irq_idx);
		return 0;
	}

	spin_lock_irqsave(&intr->irq_lock, irq_flags);

	reg_idx = sde_irq_map[irq_idx].reg_idx;
	intr_status = SDE_REG_READ(&intr->hw,
			sde_intr_set[reg_idx].status_off);
	spin_unlock_irqrestore(&intr->irq_lock, irq_flags);

	return intr_status;
}

static void __setup_intr_ops(struct sde_hw_intr_ops *ops)
{
	ops->set_mask = sde_hw_intr_set_mask;
@@ -1166,8 +1212,11 @@ static void __setup_intr_ops(struct sde_hw_intr_ops *ops)
	ops->get_interrupt_statuses = sde_hw_intr_get_interrupt_statuses;
	ops->clear_interrupt_status = sde_hw_intr_clear_interrupt_status;
	ops->clear_intr_status_nolock = sde_hw_intr_clear_intr_status_nolock;
	ops->clear_intr_status_force_mask =
				sde_hw_intr_clear_intr_status_force_mask;
	ops->get_interrupt_status = sde_hw_intr_get_interrupt_status;
	ops->get_intr_status_nolock = sde_hw_intr_get_intr_status_nolock;
	ops->get_intr_status_nomask = sde_hw_intr_get_intr_status_nomask;
}

static struct sde_mdss_base_cfg *__intr_offset(struct sde_mdss_cfg *m,
+22 −0
Original line number Diff line number Diff line
@@ -205,6 +205,17 @@ struct sde_hw_intr_ops {
			struct sde_hw_intr *intr,
			int irq_idx);

	/**
	 * clear_intr_status_force_mask() - clear the HW interrupts
	 * @intr:	HW interrupt handle
	 * @irq_idx:	Lookup irq index return from irq_idx_lookup
	 * @irq_mask:	irq mask to clear
	 */
	void (*clear_intr_status_force_mask)(
			struct sde_hw_intr *intr,
			int irq_idx,
			u32 irq_mask);

	/**
	 * get_interrupt_status - Gets HW interrupt status, and clear if set,
	 *                        based on given lookup IRQ index.
@@ -228,6 +239,17 @@ struct sde_hw_intr_ops {
			int irq_idx,
			bool clear);

	/**
	 * get_intr_status_nomask - nolock version of get_interrupt_status
	 * @intr:	HW interrupt handle
	 * @irq_idx:	Lookup irq index return from irq_idx_lookup
	 * @clear:	True to clear irq after read
	 */
	u32 (*get_intr_status_nomask)(
			struct sde_hw_intr *intr,
			int irq_idx,
			bool clear);

	/**
	 * get_valid_interrupts - Gets a mask of all valid interrupt sources
	 *                        within SDE. These are actually status bits
+9 −9
Original line number Diff line number Diff line
@@ -2613,12 +2613,20 @@ static int sde_kms_hw_init(struct msm_kms *kms)

	sde_kms->rm_init = true;

	sde_kms->hw_intr = sde_hw_intr_init(sde_kms->mmio, sde_kms->catalog);
	if (IS_ERR_OR_NULL(sde_kms->hw_intr)) {
		rc = PTR_ERR(sde_kms->hw_intr);
		SDE_ERROR("hw_intr init failed: %d\n", rc);
		sde_kms->hw_intr = NULL;
		goto hw_intr_init_err;
	}

	/*
	 * Attempt continuous splash handoff only if reserved
	 * splash memory is found.
	 */
	if (sde_kms->splash_data.splash_base)
		sde_rm_cont_splash_res_init(&sde_kms->rm,
		sde_rm_cont_splash_res_init(priv, &sde_kms->rm,
					&sde_kms->splash_data,
					sde_kms->catalog);

@@ -2675,14 +2683,6 @@ static int sde_kms_hw_init(struct msm_kms *kms)
		goto perf_err;
	}

	sde_kms->hw_intr = sde_hw_intr_init(sde_kms->mmio, sde_kms->catalog);
	if (IS_ERR_OR_NULL(sde_kms->hw_intr)) {
		rc = PTR_ERR(sde_kms->hw_intr);
		SDE_ERROR("hw_intr init failed: %d\n", rc);
		sde_kms->hw_intr = NULL;
		goto hw_intr_init_err;
	}

	/*
	 * _sde_kms_drm_obj_init should create the DRM related objects
	 * i.e. CRTCs, planes, encoders, connectors and so forth
+115 −9
Original line number Diff line number Diff line
@@ -1129,23 +1129,63 @@ static int _sde_rm_make_next_rsvp(
	return ret;
}

/**
 * poll_intr_status - Gets HW interrupt status based on
 *			given lookup IRQ index.
 * @intr:	HW interrupt handle
 * @irq_idx:	Lookup irq index return from irq_idx_lookup
 * @msec:	Maximum delay allowed to check intr status
 * return:	return zero on success.
 */
static u32 _sde_rm_poll_intr_status_for_cont_splash
			(struct sde_hw_intr *intr,
			int irq_idx, u32 const msec)
{
	int i;
	u32 status = 0;
	u32 const delay_us = 500;
	u32 const timeout_us = msec * 1000;
	/* Make sure the status is checked atleast once */
	int loop = max((u32)1, (u32)(timeout_us / delay_us));

	if (!intr)
		return 0;

	for (i = 0; i < loop; i++) {
		status = intr->ops.get_intr_status_nomask
				(intr, irq_idx, false);

		if (status & BIT(irq_idx)) {
			SDE_DEBUG(" Poll success. i=%d, status=0x%x\n",
							i, status);
			return 0;
		}
		usleep_range(delay_us, delay_us + 10);
	}
	SDE_ERROR("polling timed out. status = 0x%x\n", status);
	return -ETIMEDOUT;
}

/**
 * sde_rm_get_pp_dsc_for_cont_splash - retrieve the current dsc enabled blocks
 *	and disable autorefresh if enabled.
 * @mmio: mapped register io address of MDP
 * @rm:	Pointer to resource manager structure
 * @sde_kms: Pointer to sde kms structure
 * @max_dsc_cnt: number of DSC blocks supported in the hw
 * @dsc_ids: pointer to store the active DSC block IDs
 * return: number of active DSC blocks
 */
static int _sde_rm_get_pp_dsc_for_cont_splash(struct sde_rm *rm,
					struct sde_kms *sde_kms,
					int max_dsc_cnt, u8 *dsc_ids)
{
	int index = 0;
	int value, dsc_cnt = 0;
	struct sde_hw_autorefresh cfg;
	struct sde_rm_hw_iter iter_pp;
	int irq_idx_pp_done = -1;

	if (!rm || !dsc_ids) {
	if (!rm || !sde_kms || !dsc_ids) {
		SDE_ERROR("invalid input parameters\n");
		return 0;
	}
@@ -1155,11 +1195,21 @@ static int _sde_rm_get_pp_dsc_for_cont_splash(struct sde_rm *rm,
	while (_sde_rm_get_hw_locked(rm, &iter_pp)) {
		struct sde_hw_pingpong *pp =
				to_sde_hw_pingpong(iter_pp.blk->hw);
		u32 intr_value = 0;
		u32 const timeout_ms = 35; /* Max two vsyncs delay */
		int rc = 0, i, loop = 2;
		struct sde_hw_intr *hw_intr = NULL;
		struct sde_hw_pp_vsync_info info;

		if (!pp->ops.get_dsc_status) {
			SDE_ERROR("get_dsc_status ops not initialized\n");
			return 0;
		}
		hw_intr = sde_kms->hw_intr;
		if (!hw_intr) {
			SDE_ERROR("hw_intr handler not initialized\n");
			return 0;
		}
		value = pp->ops.get_dsc_status(pp);
		SDE_DEBUG("DSC[%d]=0x%x, dsc_cnt = %d\n",
				index, value, dsc_cnt);
@@ -1177,14 +1227,61 @@ static int _sde_rm_get_pp_dsc_for_cont_splash(struct sde_rm *rm,
		if (!pp->ops.get_autorefresh(pp, &cfg)
				&& (cfg.enable)
				&& (pp->ops.setup_autorefresh)) {
			if (hw_intr->ops.irq_idx_lookup) {
				irq_idx_pp_done = hw_intr->ops.irq_idx_lookup
					(SDE_IRQ_TYPE_PING_PONG_COMP,
								pp->idx);
				SDE_DEBUG(" itr_idx = %d\n", irq_idx_pp_done);
			}

			if ((irq_idx_pp_done >= 0) &&
					(hw_intr->ops.get_intr_status_nomask)) {
				intr_value = hw_intr->ops.get_intr_status_nomask
					(hw_intr, irq_idx_pp_done, false);
				hw_intr->ops.clear_intr_status_force_mask
					(hw_intr, irq_idx_pp_done, intr_value);
			}
			cfg.enable = false;
			SDE_DEBUG("Disabling autoreferesh\n");
			SDE_DEBUG("Disabling autorefresh\n");
			pp->ops.setup_autorefresh(pp, &cfg);

			/*
			 * Check the line count again if
			 * the line count is equal to the active
			 * height to make sure their is no
			 * additional frame updates
			 */
			for (i = 0; i < loop; i++) {
				info.wr_ptr_line_count = 0;
				info.rd_ptr_init_val = 0;
				if (pp->ops.get_vsync_info)
					pp->ops.get_vsync_info(pp, &info);
				/*
				 * For cmd-mode using external-TE logic,
				 * the rd_ptr_init_val is equal to
				 * active-height. Use this init_val to
				 * compare that with lane count. Need
				 * to implement a different check
				 * if external-TE is not used.
				 */
				if (info.wr_ptr_line_count
						< info.rd_ptr_init_val) {
					/* wait for read ptr intr */
					rc =
					_sde_rm_poll_intr_status_for_cont_splash
					(hw_intr, irq_idx_pp_done, timeout_ms);
					if (!rc)
						break;
				}
				SDE_DEBUG("i=%d, line count=%d\n",
						i, info.wr_ptr_line_count);
				/*
			 * Wait for one frame update so that
			 * auto refresh disable is through
				 * Wait for few milli seconds for line count
				 * to increase if any frame transfer is
				 * pending.
				 */
			usleep_range(16000, 20000);
				usleep_range(3000, 4000);
			}
		}
	}

@@ -1266,14 +1363,16 @@ static void _sde_rm_get_ctl_top_for_cont_splash(struct sde_hw_ctl *ctl,
				top->dspp_sel, top->intf_sel);
}

int sde_rm_cont_splash_res_init(struct sde_rm *rm,
int sde_rm_cont_splash_res_init(struct msm_drm_private *priv,
				struct sde_rm *rm,
				struct sde_splash_data *splash_data,
				struct sde_mdss_cfg *cat)
{
	struct sde_rm_hw_iter iter_c;
	int index = 0, ctl_top_cnt;
	struct sde_kms *sde_kms = NULL;

	if (!rm || !cat || !splash_data) {
	if (!priv || !rm || !cat || !splash_data) {
		SDE_ERROR("invalid input parameters\n");
		return -EINVAL;
	}
@@ -1285,6 +1384,12 @@ int sde_rm_cont_splash_res_init(struct sde_rm *rm,

	ctl_top_cnt = cat->ctl_count;

	if (!priv->kms) {
		SDE_ERROR("invalid kms\n");
		return -EINVAL;
	}
	sde_kms = to_sde_kms(priv->kms);

	if (ctl_top_cnt > ARRAY_SIZE(splash_data->top)) {
		SDE_ERROR("Mismatch in ctl_top array size\n");
		return -EINVAL;
@@ -1318,6 +1423,7 @@ int sde_rm_cont_splash_res_init(struct sde_rm *rm,

	splash_data->dsc_cnt =
		_sde_rm_get_pp_dsc_for_cont_splash(rm,
				sde_kms,
				cat->dsc_count,
				splash_data->dsc_ids);
	SDE_DEBUG("splash_data: ctl_top_cnt=%d, lm_cnt=%d, dsc_cnt=%d\n",
+3 −1
Original line number Diff line number Diff line
@@ -210,12 +210,14 @@ int sde_rm_check_property_topctl(uint64_t val);
 * sde_rm_cont_splash_res_init - Read the current MDSS configuration
 *	to update the splash data structure with the topology
 *	configured by the bootloader.
 * @priv: DRM private structure handle
 * @rm: SDE Resource Manager handle
 * @splash_data: Pointer to the splash_data structure to be updated.
 * @cat: Pointer to the SDE catalog
 * @Return: 0 on success or error
 */
int sde_rm_cont_splash_res_init(struct sde_rm *rm,
int sde_rm_cont_splash_res_init(struct msm_drm_private *priv,
				struct sde_rm *rm,
				struct sde_splash_data *splash_data,
				struct sde_mdss_cfg *cat);