Loading rotator/sde_rotator_r3.c +248 −229 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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_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) 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 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 */ 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, 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) SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_EN, 0); SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR, ROT_DONE_MASK); 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); 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) Loading Loading @@ -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); Loading Loading @@ -2526,7 +2742,6 @@ 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); SDEROT_DBG("New rotator resource:%pK, priority:%d\n", Loading Loading @@ -2555,7 +2770,6 @@ 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); devm_kfree(&mgr->pdev->dev, resinfo); Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading
rotator/sde_rotator_r3.c +248 −229 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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_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) 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 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 */ 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, 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) SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_EN, 0); SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_INTR_CLEAR, ROT_DONE_MASK); 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); 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) Loading Loading @@ -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); Loading Loading @@ -2526,7 +2742,6 @@ 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); SDEROT_DBG("New rotator resource:%pK, priority:%d\n", Loading Loading @@ -2555,7 +2770,6 @@ 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); devm_kfree(&mgr->pdev->dev, resinfo); Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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; Loading