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) \
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);

View file

@ -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;