msm:disp:rotator: setup irq during first rotator commit

Move the rotator irq registration from probe to first commit, as the
irq is available only after bind on the mdss master device.
The late bind is causing rotator driver to get defered multiple times
and on some occasions rotator probe is not called as its stuck in
the deferred probe list.

Change-Id: Ieff99b31c42d2c9cbc0a4097de7afc9f1b29df77
Signed-off-by: Kalyan Thota <kalyant@codeaurora.org>
This commit is contained in:
Kalyan Thota 2019-11-28 17:32:18 +05:30 committed by Gerrit - the friendly Code Review server
parent bceea4e1fe
commit 0380f4bd90

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s:%d: " fmt, __func__, __LINE__
@ -569,6 +569,33 @@ static u32 __sde_hw_rotator_get_timestamp(struct sde_hw_rotator *rot, u32 q_id)
return ts & SDE_REGDMA_SWTS_MASK;
}
/**
* 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_elapsed_swts - Find difference of 2 software timestamps
* @ts_curr: current software timestamp
@ -582,6 +609,174 @@ static int sde_hw_rotator_elapsed_swts(u32 ts_curr, u32 ts_prev)
return sign_extend32(diff, (SDE_REGDMA_SWTS_SHIFT - 1));
}
/*
* sde_hw_rotator_rotirq_handler - non-regdma interrupt handler
* @irq: Interrupt number
* @ptr: Pointer to private handle provided during registration
*
* This function services rotator interrupt and wakes up waiting client
* with pending rotation requests already submitted to h/w.
*/
static irqreturn_t sde_hw_rotator_rotirq_handler(int irq, void *ptr)
{
struct sde_hw_rotator *rot = ptr;
struct sde_hw_rotator_context *ctx;
irqreturn_t ret = IRQ_NONE;
u32 isr;
isr = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_INTR_STATUS);
SDEROT_DBG("intr_status = %8.8x\n", isr);
if (isr & ROT_DONE_MASK) {
sde_hw_rotator_disable_irq(rot);
SDEROT_DBG("Notify rotator complete\n");
/* Normal rotator only 1 session, no need to lookup */
ctx = rot->rotCtx[0][0];
WARN_ON(ctx == NULL);
complete_all(&ctx->rot_comp);
spin_lock(&rot->rotisr_lock);
SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
ROT_DONE_CLEAR);
spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED;
}
return ret;
}
/*
* sde_hw_rotator_regdmairq_handler - regdma interrupt handler
* @irq: Interrupt number
* @ptr: Pointer to private handle provided during registration
*
* This function services rotator interrupt, decoding the source of
* events (high/low priority queue), and wakes up all waiting clients
* with pending rotation requests already submitted to h/w.
*/
static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
{
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
struct sde_hw_rotator *rot = ptr;
struct sde_hw_rotator_context *ctx, *tmp;
irqreturn_t ret = IRQ_NONE;
u32 isr, isr_tmp;
u32 ts;
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);
SDEROT_DBG("intr_status = %8.8x\n", isr);
/* Any REGDMA status, including error and watchdog timer, should
* trigger and wake up waiting thread
*/
if (isr & (REGDMA_INT_HIGH_MASK | REGDMA_INT_LOW_MASK)) {
spin_lock(&rot->rotisr_lock);
/*
* Obtain rotator context based on timestamp from regdma
* and low/high interrupt status
*/
if (isr & REGDMA_INT_HIGH_MASK) {
q_id = ROT_QUEUE_HIGH_PRIORITY;
} else if (isr & REGDMA_INT_LOW_MASK) {
q_id = ROT_QUEUE_LOW_PRIORITY;
} else {
SDEROT_ERR("unknown ISR status: isr=0x%X\n", isr);
goto done_isr_handle;
}
ts = __sde_hw_rotator_get_timestamp(rot, q_id);
/*
* Timestamp packet is not available in sbuf mode.
* Simulate timestamp update in the handler instead.
*/
if (test_bit(SDE_CAPS_HW_TIMESTAMP, mdata->sde_caps_map) ||
list_empty(&rot->sbuf_ctx[q_id]))
goto skip_sbuf;
ctx = NULL;
isr_tmp = isr;
list_for_each_entry(tmp, &rot->sbuf_ctx[q_id], list) {
u32 mask;
mask = tmp->timestamp & 0x1 ? REGDMA_INT_1_MASK :
REGDMA_INT_0_MASK;
if (isr_tmp & mask) {
isr_tmp &= ~mask;
ctx = tmp;
ts = ctx->timestamp;
rot->ops.update_ts(rot, ctx->q_id, ts);
SDEROT_DBG("update swts:0x%X\n", ts);
}
SDEROT_EVTLOG(isr, tmp->timestamp);
}
if (ctx == NULL)
SDEROT_ERR("invalid swts ctx\n");
skip_sbuf:
ctx = rot->rotCtx[q_id][ts & SDE_HW_ROT_REGDMA_SEG_MASK];
/*
* Wake up all waiting context from the current and previous
* SW Timestamp.
*/
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:%pK, ts:%X\n", ctx, ts);
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];
};
done_isr_handle:
spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED;
} else if (isr & REGDMA_INT_ERR_MASK) {
/*
* For REGDMA Err, we save the isr info and wake up
* all waiting contexts
*/
int i, j;
SDEROT_ERR(
"regdma err isr:%X, wake up all waiting contexts\n",
isr);
spin_lock(&rot->rotisr_lock);
for (i = 0; i < ROT_QUEUE_MAX; i++) {
for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX; j++) {
ctx = rot->rotCtx[i][j];
if (ctx && ctx->last_regdma_isr_status == 0) {
ts = __sde_hw_rotator_get_timestamp(
rot, i);
ctx->last_regdma_isr_status = isr;
ctx->last_regdma_timestamp = ts;
wake_up_all(&ctx->regdma_waitq);
SDEROT_DBG("Wake rotctx[%d][%d]:%pK\n",
i, j, ctx);
}
}
}
spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED;
}
return ret;
}
/**
* sde_hw_rotator_pending_hwts - Check if the given context is still pending
* @rot: Pointer to hw rotator
@ -695,17 +890,64 @@ static void sde_hw_rotator_update_swts(struct sde_hw_rotator *rot,
SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG, swts);
}
/*
* sde_hw_rotator_irq_setup - setup rotator irq
* @mgr: Pointer to rotator manager
* return: none
*/
static int sde_hw_rotator_irq_setup(struct sde_hw_rotator *rot)
{
int rc = 0;
/* return early if irq is already setup */
if (rot->irq_num >= 0)
return 0;
rot->irq_num = platform_get_irq(rot->pdev, 0);
if (rot->irq_num < 0) {
rc = rot->irq_num;
SDEROT_ERR("fail to get rot irq, fallback to poll %d\n", rc);
} else {
if (rot->mode == ROT_REGDMA_OFF)
rc = devm_request_threaded_irq(&rot->pdev->dev,
rot->irq_num,
sde_hw_rotator_rotirq_handler,
NULL, 0, "sde_rotator_r3", rot);
else
rc = devm_request_threaded_irq(&rot->pdev->dev,
rot->irq_num,
sde_hw_rotator_regdmairq_handler,
NULL, 0, "sde_rotator_r3", rot);
if (rc) {
SDEROT_ERR("fail to request irq r:%d\n", rc);
rot->irq_num = -1;
} else {
disable_irq(rot->irq_num);
}
}
return rc;
}
/**
* 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)
static int sde_hw_rotator_enable_irq(struct sde_hw_rotator *rot)
{
int ret = 0;
SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
atomic_read(&rot->irq_enabled));
ret = sde_hw_rotator_irq_setup(rot);
if (ret < 0) {
SDEROT_ERR("Rotator irq setup failed %d\n", ret);
return ret;
}
if (!atomic_read(&rot->irq_enabled)) {
if (rot->mode == ROT_REGDMA_OFF)
SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
ROT_DONE_MASK);
@ -716,33 +958,8 @@ static void sde_hw_rotator_enable_irq(struct sde_hw_rotator *rot)
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);
}
return ret;
}
static int sde_hw_rotator_halt_vbif_xin_client(void)
@ -1814,11 +2031,10 @@ static u32 sde_hw_rotator_start_no_regdma(struct sde_hw_rotator_context *ctx,
mem_rdptr = sde_hw_rotator_get_regdma_segment_base(ctx);
wrptr = sde_hw_rotator_get_regdma_segment(ctx);
if (rot->irq_num >= 0) {
if (!sde_hw_rotator_enable_irq(rot)) {
SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_EN, 1);
SDE_REGDMA_WRITE(wrptr, ROTTOP_INTR_CLEAR, 1);
reinit_completion(&ctx->rot_comp);
sde_hw_rotator_enable_irq(rot);
}
SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, ctx->start_ctrl);
@ -2526,8 +2742,7 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext(
sde_hw_rotator_swts_create(resinfo->rot);
}
if (resinfo->rot->irq_num >= 0)
sde_hw_rotator_enable_irq(resinfo->rot);
sde_hw_rotator_enable_irq(resinfo->rot);
SDEROT_DBG("New rotator resource:%pK, priority:%d\n",
resinfo, wb_id);
@ -2555,8 +2770,7 @@ static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr,
resinfo, hw->wb_id, atomic_read(&hw->num_active),
hw->pending_count);
if (resinfo->rot->irq_num >= 0)
sde_hw_rotator_disable_irq(resinfo->rot);
sde_hw_rotator_disable_irq(resinfo->rot);
devm_kfree(&mgr->pdev->dev, resinfo);
}
@ -3303,176 +3517,6 @@ static int sde_rotator_hw_rev_init(struct sde_hw_rotator *rot)
return 0;
}
/*
* sde_hw_rotator_rotirq_handler - non-regdma interrupt handler
* @irq: Interrupt number
* @ptr: Pointer to private handle provided during registration
*
* This function services rotator interrupt and wakes up waiting client
* with pending rotation requests already submitted to h/w.
*/
static irqreturn_t sde_hw_rotator_rotirq_handler(int irq, void *ptr)
{
struct sde_hw_rotator *rot = ptr;
struct sde_hw_rotator_context *ctx;
irqreturn_t ret = IRQ_NONE;
u32 isr;
isr = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_INTR_STATUS);
SDEROT_DBG("intr_status = %8.8x\n", isr);
if (isr & ROT_DONE_MASK) {
if (rot->irq_num >= 0)
sde_hw_rotator_disable_irq(rot);
SDEROT_DBG("Notify rotator complete\n");
/* Normal rotator only 1 session, no need to lookup */
ctx = rot->rotCtx[0][0];
WARN_ON(ctx == NULL);
complete_all(&ctx->rot_comp);
spin_lock(&rot->rotisr_lock);
SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
ROT_DONE_CLEAR);
spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED;
}
return ret;
}
/*
* sde_hw_rotator_regdmairq_handler - regdma interrupt handler
* @irq: Interrupt number
* @ptr: Pointer to private handle provided during registration
*
* This function services rotator interrupt, decoding the source of
* events (high/low priority queue), and wakes up all waiting clients
* with pending rotation requests already submitted to h/w.
*/
static irqreturn_t sde_hw_rotator_regdmairq_handler(int irq, void *ptr)
{
struct sde_rot_data_type *mdata = sde_rot_get_mdata();
struct sde_hw_rotator *rot = ptr;
struct sde_hw_rotator_context *ctx, *tmp;
irqreturn_t ret = IRQ_NONE;
u32 isr, isr_tmp;
u32 ts;
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);
SDEROT_DBG("intr_status = %8.8x\n", isr);
/* Any REGDMA status, including error and watchdog timer, should
* trigger and wake up waiting thread
*/
if (isr & (REGDMA_INT_HIGH_MASK | REGDMA_INT_LOW_MASK)) {
spin_lock(&rot->rotisr_lock);
/*
* Obtain rotator context based on timestamp from regdma
* and low/high interrupt status
*/
if (isr & REGDMA_INT_HIGH_MASK) {
q_id = ROT_QUEUE_HIGH_PRIORITY;
} else if (isr & REGDMA_INT_LOW_MASK) {
q_id = ROT_QUEUE_LOW_PRIORITY;
} else {
SDEROT_ERR("unknown ISR status: isr=0x%X\n", isr);
goto done_isr_handle;
}
ts = __sde_hw_rotator_get_timestamp(rot, q_id);
/*
* Timestamp packet is not available in sbuf mode.
* Simulate timestamp update in the handler instead.
*/
if (test_bit(SDE_CAPS_HW_TIMESTAMP, mdata->sde_caps_map) ||
list_empty(&rot->sbuf_ctx[q_id]))
goto skip_sbuf;
ctx = NULL;
isr_tmp = isr;
list_for_each_entry(tmp, &rot->sbuf_ctx[q_id], list) {
u32 mask;
mask = tmp->timestamp & 0x1 ? REGDMA_INT_1_MASK :
REGDMA_INT_0_MASK;
if (isr_tmp & mask) {
isr_tmp &= ~mask;
ctx = tmp;
ts = ctx->timestamp;
rot->ops.update_ts(rot, ctx->q_id, ts);
SDEROT_DBG("update swts:0x%X\n", ts);
}
SDEROT_EVTLOG(isr, tmp->timestamp);
}
if (ctx == NULL)
SDEROT_ERR("invalid swts ctx\n");
skip_sbuf:
ctx = rot->rotCtx[q_id][ts & SDE_HW_ROT_REGDMA_SEG_MASK];
/*
* Wake up all waiting context from the current and previous
* SW Timestamp.
*/
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:%pK, ts:%X\n", ctx, ts);
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];
}
done_isr_handle:
spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED;
} else if (isr & REGDMA_INT_ERR_MASK) {
/*
* For REGDMA Err, we save the isr info and wake up
* all waiting contexts
*/
int i, j;
SDEROT_ERR(
"regdma err isr:%X, wake up all waiting contexts\n",
isr);
spin_lock(&rot->rotisr_lock);
for (i = 0; i < ROT_QUEUE_MAX; i++) {
for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX; j++) {
ctx = rot->rotCtx[i][j];
if (ctx && ctx->last_regdma_isr_status == 0) {
ts = __sde_hw_rotator_get_timestamp(
rot, i);
ctx->last_regdma_isr_status = isr;
ctx->last_regdma_timestamp = ts;
wake_up_all(&ctx->regdma_waitq);
SDEROT_DBG(
"Wakeup rotctx[%d][%d]:%pK\n",
i, j, ctx);
}
}
}
spin_unlock(&rot->rotisr_lock);
ret = IRQ_HANDLED;
}
return ret;
}
/*
* sde_hw_rotator_validate_entry - validate rotation entry
* @mgr: Pointer to rotator manager
@ -3985,30 +4029,7 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
if (ret)
goto error_parse_dt;
rot->irq_num = platform_get_irq(mgr->pdev, 0);
if (rot->irq_num == -EPROBE_DEFER) {
SDEROT_INFO("irq master master not ready, defer probe\n");
return -EPROBE_DEFER;
} else if (rot->irq_num < 0) {
SDEROT_ERR("fail to get rotator irq, fallback to polling\n");
} else {
if (rot->mode == ROT_REGDMA_OFF)
ret = devm_request_threaded_irq(&mgr->pdev->dev,
rot->irq_num,
sde_hw_rotator_rotirq_handler,
NULL, 0, "sde_rotator_r3", rot);
else
ret = devm_request_threaded_irq(&mgr->pdev->dev,
rot->irq_num,
sde_hw_rotator_regdmairq_handler,
NULL, 0, "sde_rotator_r3", rot);
if (ret) {
SDEROT_ERR("fail to request irq r:%d\n", ret);
rot->irq_num = -1;
} else {
disable_irq(rot->irq_num);
}
}
rot->irq_num = -EINVAL;
atomic_set(&rot->irq_enabled, 0);
ret = sde_rotator_hw_rev_init(rot);
@ -4056,8 +4077,6 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
mdata->sde_rot_hw = rot;
return 0;
error_hw_rev_init:
if (rot->irq_num >= 0)
devm_free_irq(&mgr->pdev->dev, rot->irq_num, mdata);
devm_kfree(&mgr->pdev->dev, mgr->hw_data);
error_parse_dt:
return ret;