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

Commit 06e18514 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: sde: Correct rotator irq handler to acknowledge irq once"

parents 286b461b 2b608aa4
Loading
Loading
Loading
Loading
+166 −46
Original line number Diff line number Diff line
@@ -77,6 +77,142 @@
#define SDE_ROTREG_READ(base, off) \
	readl_relaxed(base + (off))

/* Invalid software timestamp value for initialization */
#define SDE_REGDMA_SWTS_INVALID	(~0)

/**
 * sde_hw_rotator_elapsed_swts - Find difference of 2 software timestamps
 * @ts_curr: current software timestamp
 * @ts_prev: previous software timestamp
 * @return: the amount ts_curr is ahead of ts_prev
 */
static int sde_hw_rotator_elapsed_swts(u32 ts_curr, u32 ts_prev)
{
	u32 diff = (ts_curr - ts_prev) & SDE_REGDMA_SWTS_MASK;

	return sign_extend32(diff, (SDE_REGDMA_SWTS_SHIFT - 1));
}

/**
 * sde_hw_rotator_pending_swts - Check if the given context is still pending
 * @rot: Pointer to hw rotator
 * @ctx: Pointer to rotator context
 * @pswts: Pointer to returned reference software timestamp, optional
 * @return: true if context has pending requests
 */
static int sde_hw_rotator_pending_swts(struct sde_hw_rotator *rot,
		struct sde_hw_rotator_context *ctx, u32 *pswts)
{
	u32 swts;
	int ts_diff;
	bool pending;

	if (ctx->last_regdma_timestamp == SDE_REGDMA_SWTS_INVALID)
		swts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
	else
		swts = ctx->last_regdma_timestamp;

	if (ctx->q_id == ROT_QUEUE_LOW_PRIORITY)
		swts >>= SDE_REGDMA_SWTS_SHIFT;

	swts &= SDE_REGDMA_SWTS_MASK;

	ts_diff = sde_hw_rotator_elapsed_swts(ctx->timestamp, swts);

	if (pswts)
		*pswts = swts;

	pending = (ts_diff > 0) ? true : false;

	SDEROT_DBG("ts:0x%x, queue_id:%d, swts:0x%x, pending:%d\n",
		ctx->timestamp, ctx->q_id, swts, pending);
	return pending;
}

/**
 * sde_hw_rotator_enable_irq - Enable hw rotator interrupt with ref. count
 *				Also, clear rotator/regdma irq status.
 * @rot: Pointer to hw rotator
 */
static void sde_hw_rotator_enable_irq(struct sde_hw_rotator *rot)
{
	SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
		atomic_read(&rot->irq_enabled));

	if (!atomic_read(&rot->irq_enabled)) {
		if (rot->mode == ROT_REGDMA_OFF)
			SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
				ROT_DONE_MASK);
		else
			SDE_ROTREG_WRITE(rot->mdss_base,
				REGDMA_CSR_REGDMA_INT_CLEAR, REGDMA_INT_MASK);

		enable_irq(rot->irq_num);
	}
	atomic_inc(&rot->irq_enabled);
}

/**
 * sde_hw_rotator_disable_irq - Disable hw rotator interrupt with ref. count
 *				Also, clear rotator/regdma irq enable masks.
 * @rot: Pointer to hw rotator
 */
static void sde_hw_rotator_disable_irq(struct sde_hw_rotator *rot)
{
	SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
		atomic_read(&rot->irq_enabled));

	if (!atomic_read(&rot->irq_enabled)) {
		SDEROT_ERR("irq %d is already disabled\n", rot->irq_num);
		return;
	}

	if (!atomic_dec_return(&rot->irq_enabled)) {
		if (rot->mode == ROT_REGDMA_OFF)
			SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_EN, 0);
		else
			SDE_ROTREG_WRITE(rot->mdss_base,
				REGDMA_CSR_REGDMA_INT_EN, 0);
		/* disable irq after last pending irq is handled, if any */
		synchronize_irq(rot->irq_num);
		disable_irq_nosync(rot->irq_num);
	}
}

/**
 * sde_hw_rotator_dump_status - Dump hw rotator status on error
 * @rot: Pointer to hw rotator
 */
static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot)
{
	SDEROT_ERR(
		"op_mode = %x, int_en = %x, int_status = %x\n",
		SDE_ROTREG_READ(rot->mdss_base,
			REGDMA_CSR_REGDMA_OP_MODE),
		SDE_ROTREG_READ(rot->mdss_base,
			REGDMA_CSR_REGDMA_INT_EN),
		SDE_ROTREG_READ(rot->mdss_base,
			REGDMA_CSR_REGDMA_INT_STATUS));

	SDEROT_ERR(
		"ts = %x, q0_status = %x, q1_status = %x, block_status = %x\n",
		SDE_ROTREG_READ(rot->mdss_base,
			REGDMA_TIMESTAMP_REG),
		SDE_ROTREG_READ(rot->mdss_base,
			REGDMA_CSR_REGDMA_QUEUE_0_STATUS),
		SDE_ROTREG_READ(rot->mdss_base,
			REGDMA_CSR_REGDMA_QUEUE_1_STATUS),
		SDE_ROTREG_READ(rot->mdss_base,
			REGDMA_CSR_REGDMA_BLOCK_STATUS));

	SDEROT_ERR(
		"invalid_cmd_offset = %x, fsm_state = %x\n",
		SDE_ROTREG_READ(rot->mdss_base,
			REGDMA_CSR_REGDMA_INVALID_CMD_RAM_OFFSET),
		SDE_ROTREG_READ(rot->mdss_base,
			REGDMA_CSR_REGDMA_FSM_STATE));
}

/**
 * sde_hw_rotator_get_ctx(): Retrieve rotator context from rotator HW based
 * on provided session_id. Each rotator has a different session_id.
@@ -476,7 +612,7 @@ static u32 sde_hw_rotator_start_no_regdma(struct sde_hw_rotator_context *ctx,
		SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_EN, 1);
		SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_CLEAR, 1);
		reinit_completion(&ctx->rot_comp);
		enable_irq(rot->irq_num);
		sde_hw_rotator_enable_irq(rot);
	}

	SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1);
@@ -572,9 +708,6 @@ static u32 sde_hw_rotator_start_regdma(struct sde_hw_rotator_context *ctx,

	wrptr = sde_hw_rotator_get_regdma_segment(ctx);

	if (rot->irq_num >= 0)
		reinit_completion(&ctx->regdma_comp);

	/*
	 * Last ROT command must be ROT_START before REGDMA start
	 */
@@ -676,7 +809,7 @@ static u32 sde_hw_rotator_wait_done_no_regdma(
				SDEROT_WARN(
					"Timeout waiting, but rotator job is done!!\n");

			disable_irq_nosync(rot->irq_num);
			sde_hw_rotator_disable_irq(rot);
		}
		spin_unlock_irqrestore(&rot->rotisr_lock, flags);
	} else {
@@ -719,13 +852,15 @@ static u32 sde_hw_rotator_wait_done_regdma(
	u32 last_isr;
	u32 last_ts;
	u32 int_id;
	u32 swts;
	u32 sts = 0;
	unsigned long flags;

	if (rot->irq_num >= 0) {
		SDEROT_DBG("Wait for REGDMA completion, ctx:%p, ts:%X\n",
				ctx, ctx->timestamp);
		rc = wait_for_completion_timeout(&ctx->regdma_comp,
		rc = wait_event_timeout(ctx->regdma_waitq,
				!sde_hw_rotator_pending_swts(rot, ctx, &swts),
				KOFF_TIMEOUT);

		spin_lock_irqsave(&rot->rotisr_lock, flags);
@@ -738,11 +873,12 @@ static u32 sde_hw_rotator_wait_done_regdma(
				status, int_id, last_ts);

		if (rc == 0 || (status & REGDMA_INT_ERR_MASK)) {
			bool pending;

			pending = sde_hw_rotator_pending_swts(rot, ctx, &swts);
			SDEROT_ERR(
				"Timeout wait for regdma interrupt status, ts:%X\n",
				ctx->timestamp);
			SDEROT_ERR("last_isr:0x%X, last_ts:0x%X, rc=%d\n",
					last_isr, last_ts, rc);
				"Timeout wait for regdma interrupt status, ts:0x%X/0x%X pending:%d\n",
				ctx->timestamp, swts, pending);

			if (status & REGDMA_WATCHDOG_INT)
				SDEROT_ERR("REGDMA watchdog interrupt\n");
@@ -753,24 +889,13 @@ static u32 sde_hw_rotator_wait_done_regdma(
			else if (status & REGDMA_INVALID_CMD)
				SDEROT_ERR("REGDMA invalid command\n");

			sde_hw_rotator_dump_status(rot);
			status = ROT_ERROR_BIT;
		} else if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
			/* Got to match exactly with interrupt ID */
			int_id = REGDMA_QUEUE0_INT0 << int_id;

			SDE_ROTREG_WRITE(rot->mdss_base,
					REGDMA_CSR_REGDMA_INT_CLEAR,
					int_id);

			status = 0;
		} else if (queue_id == ROT_QUEUE_LOW_PRIORITY) {
			/* Matching interrupt ID */
			int_id = REGDMA_QUEUE1_INT0 << int_id;

			SDE_ROTREG_WRITE(rot->mdss_base,
					REGDMA_CSR_REGDMA_INT_CLEAR,
					int_id);

		} else {
			if (rc == 1)
				SDEROT_WARN(
					"REGDMA done but no irq, ts:0x%X/0x%X\n",
					ctx->timestamp, swts);
			status = 0;
		}

@@ -1007,7 +1132,7 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext(
	}

	if (resinfo->rot->irq_num >= 0)
		enable_irq(resinfo->rot->irq_num);
		sde_hw_rotator_enable_irq(resinfo->rot);

	SDEROT_DBG("New rotator resource:%p, priority:%d\n",
			resinfo, wb_id);
@@ -1036,7 +1161,7 @@ static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr,
		hw->pending_count);

	if (resinfo->rot->irq_num >= 0)
		disable_irq(resinfo->rot->irq_num);
		sde_hw_rotator_disable_irq(resinfo->rot);

	devm_kfree(&mgr->pdev->dev, resinfo);
}
@@ -1078,8 +1203,10 @@ static struct sde_hw_rotator_context *sde_hw_rotator_alloc_rotctx(
		ctx->q_id * SDE_HW_ROT_REGDMA_TOTAL_CTX +
		sde_hw_rotator_get_regdma_ctxidx(ctx));

	ctx->last_regdma_timestamp = SDE_REGDMA_SWTS_INVALID;

	init_completion(&ctx->rot_comp);
	init_completion(&ctx->regdma_comp);
	init_waitqueue_head(&ctx->regdma_waitq);

	/* Store rotator context for lookup purpose */
	sde_hw_rotator_put_ctx(ctx);
@@ -1419,7 +1546,7 @@ static irqreturn_t sde_hw_rotator_rotirq_handler(int irq, void *ptr)

	if (isr & ROT_DONE_MASK) {
		if (rot->irq_num >= 0)
			disable_irq_nosync(rot->irq_num);
			sde_hw_rotator_disable_irq(rot);
		SDEROT_DBG("Notify rotator complete\n");

		/* Normal rotator only 1 session, no need to lookup */
@@ -1456,6 +1583,8 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
	u32 q_id;

	isr = SDE_ROTREG_READ(rot->mdss_base, REGDMA_CSR_REGDMA_INT_STATUS);
	/* acknowledge interrupt before reading latest timestamp */
	SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR, isr);
	ts  = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);

	SDEROT_DBG("intr_status = %8.8x, sw_TS:%X\n", isr, ts);
@@ -1480,30 +1609,23 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
		}

		ctx = rot->rotCtx[q_id][ts & SDE_HW_ROT_REGDMA_SEG_MASK];
		WARN_ON(ctx == NULL);

		/*
		 * Wake up all waiting context from the current and previous
		 * SW Timestamp.
		 */
		do {
		while (ctx &&
			sde_hw_rotator_elapsed_swts(ctx->timestamp, ts) >= 0) {
			ctx->last_regdma_isr_status = isr;
			ctx->last_regdma_timestamp  = ts;
			SDEROT_DBG(
				"regdma complete: ctx:%p, ts:%X\n", ctx, ts);
			complete_all(&ctx->regdma_comp);
			wake_up_all(&ctx->regdma_waitq);

			ts  = (ts - 1) & SDE_REGDMA_SWTS_MASK;
			ctx = rot->rotCtx[q_id]
				[ts & SDE_HW_ROT_REGDMA_SEG_MASK];
		} while (ctx && (ctx->last_regdma_timestamp == 0));

		/*
		 * Clear corresponding regdma interrupt because it is a level
		 * interrupt
		 */
		SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
				isr);
		};

		spin_unlock(&rot->rotisr_lock);
		ret = IRQ_HANDLED;
@@ -1526,16 +1648,13 @@ static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
				if (ctx && ctx->last_regdma_isr_status == 0) {
					ctx->last_regdma_isr_status = isr;
					ctx->last_regdma_timestamp  = ts;
					complete_all(&ctx->regdma_comp);
					wake_up_all(&ctx->regdma_waitq);
					SDEROT_DBG("Wakeup rotctx[%d][%d]:%p\n",
							i, j, ctx);
				}
			}
		}

		SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
				isr);

		spin_unlock(&rot->rotisr_lock);
		ret = IRQ_HANDLED;
	}
@@ -1810,6 +1929,7 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
			disable_irq(rot->irq_num);
		}
	}
	atomic_set(&rot->irq_enabled, 0);

	setup_rotator_ops(&rot->ops, rot->mode);

+2 −3
Original line number Diff line number Diff line
@@ -202,7 +202,7 @@ struct sde_hw_rotator_context {
	u32    *regdma_wrptr;
	u32    timestamp;
	struct completion rot_comp;
	struct completion regdma_comp;
	wait_queue_head_t regdma_waitq;
	struct sde_dbg_buf src_dbgbuf;
	struct sde_dbg_buf dst_dbgbuf;
	u32    last_regdma_isr_status;
@@ -253,6 +253,7 @@ struct sde_hw_rotator {

	/* logical interrupt number */
	int    irq_num;
	atomic_t irq_enabled;

	/* internal ION memory for SW timestamp */
	struct ion_client *iclient;
@@ -260,8 +261,6 @@ struct sde_hw_rotator {
	void *swts_buffer;

	u32    highest_bank;
	struct completion rot_comp;
	struct completion regdma_comp;

	spinlock_t rotctx_lock;
	spinlock_t rotisr_lock;