Merge "msm: sde: Correct rotator irq handler to acknowledge irq once"
This commit is contained in:
commit
06e18514c2
2 changed files with 168 additions and 49 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue