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

This commit is contained in:
Linux Build Service Account 2016-08-03 21:12:41 -07:00 committed by Gerrit - the friendly Code Review server
commit 06e18514c2
2 changed files with 168 additions and 49 deletions

View file

@ -77,6 +77,142 @@
#define SDE_ROTREG_READ(base, off) \ #define SDE_ROTREG_READ(base, off) \
readl_relaxed(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 * sde_hw_rotator_get_ctx(): Retrieve rotator context from rotator HW based
* on provided session_id. Each rotator has a different session_id. * 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_EN, 1);
SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_CLEAR, 1); SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_CLEAR, 1);
reinit_completion(&ctx->rot_comp); reinit_completion(&ctx->rot_comp);
enable_irq(rot->irq_num); sde_hw_rotator_enable_irq(rot);
} }
SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, 1); 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); 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 * 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( SDEROT_WARN(
"Timeout waiting, but rotator job is done!!\n"); "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); spin_unlock_irqrestore(&rot->rotisr_lock, flags);
} else { } else {
@ -719,13 +852,15 @@ static u32 sde_hw_rotator_wait_done_regdma(
u32 last_isr; u32 last_isr;
u32 last_ts; u32 last_ts;
u32 int_id; u32 int_id;
u32 swts;
u32 sts = 0; u32 sts = 0;
unsigned long flags; unsigned long flags;
if (rot->irq_num >= 0) { if (rot->irq_num >= 0) {
SDEROT_DBG("Wait for REGDMA completion, ctx:%p, ts:%X\n", SDEROT_DBG("Wait for REGDMA completion, ctx:%p, ts:%X\n",
ctx, ctx->timestamp); 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); KOFF_TIMEOUT);
spin_lock_irqsave(&rot->rotisr_lock, flags); spin_lock_irqsave(&rot->rotisr_lock, flags);
@ -738,11 +873,12 @@ static u32 sde_hw_rotator_wait_done_regdma(
status, int_id, last_ts); status, int_id, last_ts);
if (rc == 0 || (status & REGDMA_INT_ERR_MASK)) { if (rc == 0 || (status & REGDMA_INT_ERR_MASK)) {
bool pending;
pending = sde_hw_rotator_pending_swts(rot, ctx, &swts);
SDEROT_ERR( SDEROT_ERR(
"Timeout wait for regdma interrupt status, ts:%X\n", "Timeout wait for regdma interrupt status, ts:0x%X/0x%X pending:%d\n",
ctx->timestamp); ctx->timestamp, swts, pending);
SDEROT_ERR("last_isr:0x%X, last_ts:0x%X, rc=%d\n",
last_isr, last_ts, rc);
if (status & REGDMA_WATCHDOG_INT) if (status & REGDMA_WATCHDOG_INT)
SDEROT_ERR("REGDMA watchdog interrupt\n"); SDEROT_ERR("REGDMA watchdog interrupt\n");
@ -753,24 +889,13 @@ static u32 sde_hw_rotator_wait_done_regdma(
else if (status & REGDMA_INVALID_CMD) else if (status & REGDMA_INVALID_CMD)
SDEROT_ERR("REGDMA invalid command\n"); SDEROT_ERR("REGDMA invalid command\n");
sde_hw_rotator_dump_status(rot);
status = ROT_ERROR_BIT; status = ROT_ERROR_BIT;
} else if (queue_id == ROT_QUEUE_HIGH_PRIORITY) { } else {
/* Got to match exactly with interrupt ID */ if (rc == 1)
int_id = REGDMA_QUEUE0_INT0 << int_id; SDEROT_WARN(
"REGDMA done but no irq, ts:0x%X/0x%X\n",
SDE_ROTREG_WRITE(rot->mdss_base, ctx->timestamp, swts);
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);
status = 0; status = 0;
} }
@ -1007,7 +1132,7 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext(
} }
if (resinfo->rot->irq_num >= 0) 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", SDEROT_DBG("New rotator resource:%p, priority:%d\n",
resinfo, wb_id); resinfo, wb_id);
@ -1036,7 +1161,7 @@ static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr,
hw->pending_count); hw->pending_count);
if (resinfo->rot->irq_num >= 0) 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); 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 + ctx->q_id * SDE_HW_ROT_REGDMA_TOTAL_CTX +
sde_hw_rotator_get_regdma_ctxidx(ctx)); sde_hw_rotator_get_regdma_ctxidx(ctx));
ctx->last_regdma_timestamp = SDE_REGDMA_SWTS_INVALID;
init_completion(&ctx->rot_comp); init_completion(&ctx->rot_comp);
init_completion(&ctx->regdma_comp); init_waitqueue_head(&ctx->regdma_waitq);
/* Store rotator context for lookup purpose */ /* Store rotator context for lookup purpose */
sde_hw_rotator_put_ctx(ctx); 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 (isr & ROT_DONE_MASK) {
if (rot->irq_num >= 0) if (rot->irq_num >= 0)
disable_irq_nosync(rot->irq_num); sde_hw_rotator_disable_irq(rot);
SDEROT_DBG("Notify rotator complete\n"); SDEROT_DBG("Notify rotator complete\n");
/* Normal rotator only 1 session, no need to lookup */ /* 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; u32 q_id;
isr = SDE_ROTREG_READ(rot->mdss_base, REGDMA_CSR_REGDMA_INT_STATUS); 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); ts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
SDEROT_DBG("intr_status = %8.8x, sw_TS:%X\n", isr, ts); 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]; 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 * Wake up all waiting context from the current and previous
* SW Timestamp. * SW Timestamp.
*/ */
do { while (ctx &&
sde_hw_rotator_elapsed_swts(ctx->timestamp, ts) >= 0) {
ctx->last_regdma_isr_status = isr; ctx->last_regdma_isr_status = isr;
ctx->last_regdma_timestamp = ts; ctx->last_regdma_timestamp = ts;
SDEROT_DBG( SDEROT_DBG(
"regdma complete: ctx:%p, ts:%X\n", ctx, ts); "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; ts = (ts - 1) & SDE_REGDMA_SWTS_MASK;
ctx = rot->rotCtx[q_id] ctx = rot->rotCtx[q_id]
[ts & SDE_HW_ROT_REGDMA_SEG_MASK]; [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); spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED; 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) { if (ctx && ctx->last_regdma_isr_status == 0) {
ctx->last_regdma_isr_status = isr; ctx->last_regdma_isr_status = isr;
ctx->last_regdma_timestamp = ts; ctx->last_regdma_timestamp = ts;
complete_all(&ctx->regdma_comp); wake_up_all(&ctx->regdma_waitq);
SDEROT_DBG("Wakeup rotctx[%d][%d]:%p\n", SDEROT_DBG("Wakeup rotctx[%d][%d]:%p\n",
i, j, ctx); i, j, ctx);
} }
} }
} }
SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_CLEAR,
isr);
spin_unlock(&rot->rotisr_lock); spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
} }
@ -1810,6 +1929,7 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
disable_irq(rot->irq_num); disable_irq(rot->irq_num);
} }
} }
atomic_set(&rot->irq_enabled, 0);
setup_rotator_ops(&rot->ops, rot->mode); setup_rotator_ops(&rot->ops, rot->mode);

View file

@ -202,7 +202,7 @@ struct sde_hw_rotator_context {
u32 *regdma_wrptr; u32 *regdma_wrptr;
u32 timestamp; u32 timestamp;
struct completion rot_comp; struct completion rot_comp;
struct completion regdma_comp; wait_queue_head_t regdma_waitq;
struct sde_dbg_buf src_dbgbuf; struct sde_dbg_buf src_dbgbuf;
struct sde_dbg_buf dst_dbgbuf; struct sde_dbg_buf dst_dbgbuf;
u32 last_regdma_isr_status; u32 last_regdma_isr_status;
@ -253,6 +253,7 @@ struct sde_hw_rotator {
/* logical interrupt number */ /* logical interrupt number */
int irq_num; int irq_num;
atomic_t irq_enabled;
/* internal ION memory for SW timestamp */ /* internal ION memory for SW timestamp */
struct ion_client *iclient; struct ion_client *iclient;
@ -260,8 +261,6 @@ struct sde_hw_rotator {
void *swts_buffer; void *swts_buffer;
u32 highest_bank; u32 highest_bank;
struct completion rot_comp;
struct completion regdma_comp;
spinlock_t rotctx_lock; spinlock_t rotctx_lock;
spinlock_t rotisr_lock; spinlock_t rotisr_lock;