|
@@ -1,6 +1,6 @@
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
// 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__
|
|
#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;
|
|
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
|
|
* sde_hw_rotator_elapsed_swts - Find difference of 2 software timestamps
|
|
* @ts_curr: current software timestamp
|
|
* @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));
|
|
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
|
|
* sde_hw_rotator_pending_hwts - Check if the given context is still pending
|
|
* @rot: Pointer to hw rotator
|
|
* @rot: Pointer to hw rotator
|
|
@@ -695,54 +890,76 @@ static void sde_hw_rotator_update_swts(struct sde_hw_rotator *rot,
|
|
SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG, swts);
|
|
SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG, swts);
|
|
}
|
|
}
|
|
|
|
|
|
-/**
|
|
|
|
- * sde_hw_rotator_enable_irq - Enable hw rotator interrupt with ref. count
|
|
|
|
- * Also, clear rotator/regdma irq status.
|
|
|
|
- * @rot: Pointer to hw rotator
|
|
|
|
|
|
+/*
|
|
|
|
+ * sde_hw_rotator_irq_setup - setup rotator irq
|
|
|
|
+ * @mgr: Pointer to rotator manager
|
|
|
|
+ * return: none
|
|
*/
|
|
*/
|
|
-static void sde_hw_rotator_enable_irq(struct sde_hw_rotator *rot)
|
|
|
|
|
|
+static int sde_hw_rotator_irq_setup(struct sde_hw_rotator *rot)
|
|
{
|
|
{
|
|
- SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
|
|
|
|
- atomic_read(&rot->irq_enabled));
|
|
|
|
|
|
+ int rc = 0;
|
|
|
|
|
|
- if (!atomic_read(&rot->irq_enabled)) {
|
|
|
|
|
|
+ /* 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)
|
|
if (rot->mode == ROT_REGDMA_OFF)
|
|
- SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
|
|
|
|
- ROT_DONE_MASK);
|
|
|
|
|
|
+ rc = devm_request_threaded_irq(&rot->pdev->dev,
|
|
|
|
+ rot->irq_num,
|
|
|
|
+ sde_hw_rotator_rotirq_handler,
|
|
|
|
+ NULL, 0, "sde_rotator_r3", rot);
|
|
else
|
|
else
|
|
- SDE_ROTREG_WRITE(rot->mdss_base,
|
|
|
|
- REGDMA_CSR_REGDMA_INT_CLEAR, REGDMA_INT_MASK);
|
|
|
|
-
|
|
|
|
- enable_irq(rot->irq_num);
|
|
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- atomic_inc(&rot->irq_enabled);
|
|
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * sde_hw_rotator_disable_irq - Disable hw rotator interrupt with ref. count
|
|
|
|
- * Also, clear rotator/regdma irq enable masks.
|
|
|
|
|
|
+ * sde_hw_rotator_enable_irq - Enable hw rotator interrupt with ref. count
|
|
|
|
+ * Also, clear rotator/regdma irq status.
|
|
* @rot: Pointer to hw rotator
|
|
* @rot: Pointer to hw rotator
|
|
*/
|
|
*/
|
|
-static void sde_hw_rotator_disable_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,
|
|
SDEROT_DBG("irq_num:%d enabled:%d\n", rot->irq_num,
|
|
atomic_read(&rot->irq_enabled));
|
|
atomic_read(&rot->irq_enabled));
|
|
|
|
|
|
- if (!atomic_read(&rot->irq_enabled)) {
|
|
|
|
- SDEROT_ERR("irq %d is already disabled\n", rot->irq_num);
|
|
|
|
- return;
|
|
|
|
|
|
+ ret = sde_hw_rotator_irq_setup(rot);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ SDEROT_ERR("Rotator irq setup failed %d\n", ret);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
- if (!atomic_dec_return(&rot->irq_enabled)) {
|
|
|
|
|
|
+ if (!atomic_read(&rot->irq_enabled)) {
|
|
|
|
+
|
|
if (rot->mode == ROT_REGDMA_OFF)
|
|
if (rot->mode == ROT_REGDMA_OFF)
|
|
- SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_EN, 0);
|
|
|
|
|
|
+ SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR,
|
|
|
|
+ ROT_DONE_MASK);
|
|
else
|
|
else
|
|
SDE_ROTREG_WRITE(rot->mdss_base,
|
|
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);
|
|
|
|
|
|
+ REGDMA_CSR_REGDMA_INT_CLEAR, REGDMA_INT_MASK);
|
|
|
|
+
|
|
|
|
+ enable_irq(rot->irq_num);
|
|
}
|
|
}
|
|
|
|
+ atomic_inc(&rot->irq_enabled);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
static int sde_hw_rotator_halt_vbif_xin_client(void)
|
|
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);
|
|
mem_rdptr = sde_hw_rotator_get_regdma_segment_base(ctx);
|
|
wrptr = sde_hw_rotator_get_regdma_segment(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_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);
|
|
- sde_hw_rotator_enable_irq(rot);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
SDE_REGDMA_WRITE(wrptr, ROTTOP_START_CTRL, ctx->start_ctrl);
|
|
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);
|
|
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",
|
|
SDEROT_DBG("New rotator resource:%pK, priority:%d\n",
|
|
resinfo, wb_id);
|
|
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),
|
|
resinfo, hw->wb_id, atomic_read(&hw->num_active),
|
|
hw->pending_count);
|
|
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);
|
|
devm_kfree(&mgr->pdev->dev, resinfo);
|
|
}
|
|
}
|
|
@@ -3303,176 +3517,6 @@ static int sde_rotator_hw_rev_init(struct sde_hw_rotator *rot)
|
|
return 0;
|
|
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
|
|
* sde_hw_rotator_validate_entry - validate rotation entry
|
|
* @mgr: Pointer to rotator manager
|
|
* @mgr: Pointer to rotator manager
|
|
@@ -3985,30 +4029,7 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
|
|
if (ret)
|
|
if (ret)
|
|
goto error_parse_dt;
|
|
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);
|
|
atomic_set(&rot->irq_enabled, 0);
|
|
|
|
|
|
ret = sde_rotator_hw_rev_init(rot);
|
|
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;
|
|
mdata->sde_rot_hw = rot;
|
|
return 0;
|
|
return 0;
|
|
error_hw_rev_init:
|
|
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);
|
|
devm_kfree(&mgr->pdev->dev, mgr->hw_data);
|
|
error_parse_dt:
|
|
error_parse_dt:
|
|
return ret;
|
|
return ret;
|