1*ba07fd2fSBen Hoff // SPDX-License-Identifier: GPL-2.0-only 2*ba07fd2fSBen Hoff #include <linux/compiler.h> 3*ba07fd2fSBen Hoff #include <linux/moduleparam.h> 4*ba07fd2fSBen Hoff #include <linux/io.h> 5*ba07fd2fSBen Hoff #include <linux/dma-mapping.h> 6*ba07fd2fSBen Hoff #include <linux/interrupt.h> 7*ba07fd2fSBen Hoff #include <linux/minmax.h> 8*ba07fd2fSBen Hoff #include <linux/string.h> 9*ba07fd2fSBen Hoff 10*ba07fd2fSBen Hoff #include <media/videobuf2-dma-contig.h> 11*ba07fd2fSBen Hoff 12*ba07fd2fSBen Hoff #include "hws_irq.h" 13*ba07fd2fSBen Hoff #include "hws_reg.h" 14*ba07fd2fSBen Hoff #include "hws_video.h" 15*ba07fd2fSBen Hoff #include "hws.h" 16*ba07fd2fSBen Hoff 17*ba07fd2fSBen Hoff #define MAX_INT_LOOPS 100 18*ba07fd2fSBen Hoff 19*ba07fd2fSBen Hoff static bool hws_toggle_debug; 20*ba07fd2fSBen Hoff module_param_named(toggle_debug, hws_toggle_debug, bool, 0644); 21*ba07fd2fSBen Hoff MODULE_PARM_DESC(toggle_debug, 22*ba07fd2fSBen Hoff "Read toggle registers in IRQ handler for debug logging"); 23*ba07fd2fSBen Hoff 24*ba07fd2fSBen Hoff static int hws_arm_next(struct hws_pcie_dev *hws, u32 ch) 25*ba07fd2fSBen Hoff { 26*ba07fd2fSBen Hoff struct hws_video *v = &hws->video[ch]; 27*ba07fd2fSBen Hoff unsigned long flags; 28*ba07fd2fSBen Hoff struct hwsvideo_buffer *buf; 29*ba07fd2fSBen Hoff 30*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, 31*ba07fd2fSBen Hoff "arm_next(ch=%u): stop=%d cap=%d queued=%d\n", 32*ba07fd2fSBen Hoff ch, READ_ONCE(v->stop_requested), READ_ONCE(v->cap_active), 33*ba07fd2fSBen Hoff !list_empty(&v->capture_queue)); 34*ba07fd2fSBen Hoff 35*ba07fd2fSBen Hoff if (READ_ONCE(hws->suspended)) { 36*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): suspended\n", ch); 37*ba07fd2fSBen Hoff return -EBUSY; 38*ba07fd2fSBen Hoff } 39*ba07fd2fSBen Hoff 40*ba07fd2fSBen Hoff if (READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) { 41*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, 42*ba07fd2fSBen Hoff "arm_next(ch=%u): stop=%d cap=%d -> cancel\n", ch, 43*ba07fd2fSBen Hoff v->stop_requested, v->cap_active); 44*ba07fd2fSBen Hoff return -ECANCELED; 45*ba07fd2fSBen Hoff } 46*ba07fd2fSBen Hoff 47*ba07fd2fSBen Hoff spin_lock_irqsave(&v->irq_lock, flags); 48*ba07fd2fSBen Hoff if (list_empty(&v->capture_queue)) { 49*ba07fd2fSBen Hoff spin_unlock_irqrestore(&v->irq_lock, flags); 50*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): queue empty\n", ch); 51*ba07fd2fSBen Hoff return -EAGAIN; 52*ba07fd2fSBen Hoff } 53*ba07fd2fSBen Hoff 54*ba07fd2fSBen Hoff buf = list_first_entry(&v->capture_queue, struct hwsvideo_buffer, list); 55*ba07fd2fSBen Hoff list_del_init(&buf->list); /* keep buffer safe for later cleanup */ 56*ba07fd2fSBen Hoff if (v->queued_count) 57*ba07fd2fSBen Hoff v->queued_count--; 58*ba07fd2fSBen Hoff v->active = buf; 59*ba07fd2fSBen Hoff spin_unlock_irqrestore(&v->irq_lock, flags); 60*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): picked buffer %p\n", ch, 61*ba07fd2fSBen Hoff buf); 62*ba07fd2fSBen Hoff 63*ba07fd2fSBen Hoff /* Publish descriptor(s) before doorbell/MMIO kicks. */ 64*ba07fd2fSBen Hoff wmb(); 65*ba07fd2fSBen Hoff 66*ba07fd2fSBen Hoff /* Avoid MMIO during suspend */ 67*ba07fd2fSBen Hoff if (READ_ONCE(hws->suspended)) { 68*ba07fd2fSBen Hoff unsigned long f; 69*ba07fd2fSBen Hoff 70*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, 71*ba07fd2fSBen Hoff "arm_next(ch=%u): suspended after pick\n", ch); 72*ba07fd2fSBen Hoff spin_lock_irqsave(&v->irq_lock, f); 73*ba07fd2fSBen Hoff if (v->active) { 74*ba07fd2fSBen Hoff list_add(&buf->list, &v->capture_queue); 75*ba07fd2fSBen Hoff v->queued_count++; 76*ba07fd2fSBen Hoff v->active = NULL; 77*ba07fd2fSBen Hoff } 78*ba07fd2fSBen Hoff spin_unlock_irqrestore(&v->irq_lock, f); 79*ba07fd2fSBen Hoff return -EBUSY; 80*ba07fd2fSBen Hoff } 81*ba07fd2fSBen Hoff 82*ba07fd2fSBen Hoff /* Also program the DMA address register directly */ 83*ba07fd2fSBen Hoff { 84*ba07fd2fSBen Hoff dma_addr_t dma_addr = 85*ba07fd2fSBen Hoff vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); 86*ba07fd2fSBen Hoff hws_program_dma_for_addr(hws, ch, dma_addr); 87*ba07fd2fSBen Hoff iowrite32(lower_32_bits(dma_addr), 88*ba07fd2fSBen Hoff hws->bar0_base + HWS_REG_DMA_ADDR(ch)); 89*ba07fd2fSBen Hoff } 90*ba07fd2fSBen Hoff 91*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): programmed buffer %p\n", ch, 92*ba07fd2fSBen Hoff buf); 93*ba07fd2fSBen Hoff spin_lock_irqsave(&v->irq_lock, flags); 94*ba07fd2fSBen Hoff hws_prime_next_locked(v); 95*ba07fd2fSBen Hoff spin_unlock_irqrestore(&v->irq_lock, flags); 96*ba07fd2fSBen Hoff return 0; 97*ba07fd2fSBen Hoff } 98*ba07fd2fSBen Hoff 99*ba07fd2fSBen Hoff static void hws_video_handle_vdone(struct hws_video *v) 100*ba07fd2fSBen Hoff { 101*ba07fd2fSBen Hoff struct hws_pcie_dev *hws = v->parent; 102*ba07fd2fSBen Hoff unsigned int ch = v->channel_index; 103*ba07fd2fSBen Hoff struct hwsvideo_buffer *done; 104*ba07fd2fSBen Hoff unsigned long flags; 105*ba07fd2fSBen Hoff bool promoted = false; 106*ba07fd2fSBen Hoff 107*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, 108*ba07fd2fSBen Hoff "bh_video(ch=%u): stop=%d cap=%d active=%p\n", 109*ba07fd2fSBen Hoff ch, READ_ONCE(v->stop_requested), READ_ONCE(v->cap_active), 110*ba07fd2fSBen Hoff v->active); 111*ba07fd2fSBen Hoff 112*ba07fd2fSBen Hoff int ret; 113*ba07fd2fSBen Hoff 114*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, 115*ba07fd2fSBen Hoff "bh_video(ch=%u): entry stop=%d cap=%d\n", ch, 116*ba07fd2fSBen Hoff v->stop_requested, v->cap_active); 117*ba07fd2fSBen Hoff if (READ_ONCE(hws->suspended)) 118*ba07fd2fSBen Hoff return; 119*ba07fd2fSBen Hoff 120*ba07fd2fSBen Hoff if (READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) 121*ba07fd2fSBen Hoff return; 122*ba07fd2fSBen Hoff 123*ba07fd2fSBen Hoff spin_lock_irqsave(&v->irq_lock, flags); 124*ba07fd2fSBen Hoff done = v->active; 125*ba07fd2fSBen Hoff if (done && v->next_prepared) { 126*ba07fd2fSBen Hoff v->active = v->next_prepared; 127*ba07fd2fSBen Hoff v->next_prepared = NULL; 128*ba07fd2fSBen Hoff promoted = true; 129*ba07fd2fSBen Hoff } 130*ba07fd2fSBen Hoff spin_unlock_irqrestore(&v->irq_lock, flags); 131*ba07fd2fSBen Hoff 132*ba07fd2fSBen Hoff /* 1) Complete the buffer the HW just finished (if any) */ 133*ba07fd2fSBen Hoff if (done) { 134*ba07fd2fSBen Hoff struct vb2_v4l2_buffer *vb2v = &done->vb; 135*ba07fd2fSBen Hoff size_t expected = v->pix.sizeimage; 136*ba07fd2fSBen Hoff size_t plane_size = vb2_plane_size(&vb2v->vb2_buf, 0); 137*ba07fd2fSBen Hoff 138*ba07fd2fSBen Hoff if (expected > plane_size) { 139*ba07fd2fSBen Hoff dev_warn_ratelimited(&hws->pdev->dev, 140*ba07fd2fSBen Hoff "bh_video(ch=%u): sizeimage %zu > plane %zu, dropping seq=%u\n", 141*ba07fd2fSBen Hoff ch, expected, plane_size, 142*ba07fd2fSBen Hoff (u32)atomic_read(&v->sequence_number) + 1); 143*ba07fd2fSBen Hoff vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_ERROR); 144*ba07fd2fSBen Hoff goto arm_next; 145*ba07fd2fSBen Hoff } 146*ba07fd2fSBen Hoff vb2_set_plane_payload(&vb2v->vb2_buf, 0, expected); 147*ba07fd2fSBen Hoff 148*ba07fd2fSBen Hoff dma_rmb(); /* device writes visible before userspace sees it */ 149*ba07fd2fSBen Hoff 150*ba07fd2fSBen Hoff vb2v->sequence = (u32)atomic_inc_return(&v->sequence_number); 151*ba07fd2fSBen Hoff vb2v->vb2_buf.timestamp = ktime_get_ns(); 152*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, 153*ba07fd2fSBen Hoff "bh_video(ch=%u): DONE buf=%p seq=%u half_seen=%d toggle=%u\n", 154*ba07fd2fSBen Hoff ch, done, vb2v->sequence, v->half_seen, 155*ba07fd2fSBen Hoff v->last_buf_half_toggle); 156*ba07fd2fSBen Hoff 157*ba07fd2fSBen Hoff if (!promoted) 158*ba07fd2fSBen Hoff v->active = NULL; /* channel no longer owns this buffer */ 159*ba07fd2fSBen Hoff vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_DONE); 160*ba07fd2fSBen Hoff } 161*ba07fd2fSBen Hoff 162*ba07fd2fSBen Hoff if (READ_ONCE(hws->suspended)) 163*ba07fd2fSBen Hoff return; 164*ba07fd2fSBen Hoff 165*ba07fd2fSBen Hoff if (promoted) { 166*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, 167*ba07fd2fSBen Hoff "bh_video(ch=%u): promoted pre-armed buffer active=%p\n", 168*ba07fd2fSBen Hoff ch, v->active); 169*ba07fd2fSBen Hoff spin_lock_irqsave(&v->irq_lock, flags); 170*ba07fd2fSBen Hoff hws_prime_next_locked(v); 171*ba07fd2fSBen Hoff spin_unlock_irqrestore(&v->irq_lock, flags); 172*ba07fd2fSBen Hoff return; 173*ba07fd2fSBen Hoff } 174*ba07fd2fSBen Hoff 175*ba07fd2fSBen Hoff arm_next: 176*ba07fd2fSBen Hoff /* 2) Immediately arm the next queued buffer (if present) */ 177*ba07fd2fSBen Hoff ret = hws_arm_next(hws, ch); 178*ba07fd2fSBen Hoff if (ret == -EAGAIN) { 179*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, 180*ba07fd2fSBen Hoff "bh_video(ch=%u): no queued buffer to arm\n", ch); 181*ba07fd2fSBen Hoff return; 182*ba07fd2fSBen Hoff } 183*ba07fd2fSBen Hoff dev_dbg(&hws->pdev->dev, 184*ba07fd2fSBen Hoff "bh_video(ch=%u): armed next buffer, active=%p\n", ch, 185*ba07fd2fSBen Hoff v->active); 186*ba07fd2fSBen Hoff /* On success the engine now points at v->active's DMA address */ 187*ba07fd2fSBen Hoff } 188*ba07fd2fSBen Hoff 189*ba07fd2fSBen Hoff irqreturn_t hws_irq_handler(int irq, void *info) 190*ba07fd2fSBen Hoff { 191*ba07fd2fSBen Hoff struct hws_pcie_dev *pdx = info; 192*ba07fd2fSBen Hoff u32 int_state; 193*ba07fd2fSBen Hoff 194*ba07fd2fSBen Hoff dev_dbg(&pdx->pdev->dev, "irq: entry\n"); 195*ba07fd2fSBen Hoff if (pdx->bar0_base) { 196*ba07fd2fSBen Hoff dev_dbg(&pdx->pdev->dev, 197*ba07fd2fSBen Hoff "irq: INT_EN=0x%08x INT_STATUS=0x%08x\n", 198*ba07fd2fSBen Hoff readl(pdx->bar0_base + INT_EN_REG_BASE), 199*ba07fd2fSBen Hoff readl(pdx->bar0_base + HWS_REG_INT_STATUS)); 200*ba07fd2fSBen Hoff } 201*ba07fd2fSBen Hoff 202*ba07fd2fSBen Hoff /* Fast path: if suspended, quietly ack and exit */ 203*ba07fd2fSBen Hoff if (READ_ONCE(pdx->suspended)) { 204*ba07fd2fSBen Hoff int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); 205*ba07fd2fSBen Hoff if (int_state) { 206*ba07fd2fSBen Hoff writel(int_state, pdx->bar0_base + HWS_REG_INT_STATUS); 207*ba07fd2fSBen Hoff (void)readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); 208*ba07fd2fSBen Hoff } 209*ba07fd2fSBen Hoff return int_state ? IRQ_HANDLED : IRQ_NONE; 210*ba07fd2fSBen Hoff } 211*ba07fd2fSBen Hoff int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); 212*ba07fd2fSBen Hoff if (!int_state || int_state == 0xFFFFFFFF) { 213*ba07fd2fSBen Hoff dev_dbg(&pdx->pdev->dev, 214*ba07fd2fSBen Hoff "irq: spurious or device-gone int_state=0x%08x\n", 215*ba07fd2fSBen Hoff int_state); 216*ba07fd2fSBen Hoff return IRQ_NONE; 217*ba07fd2fSBen Hoff } 218*ba07fd2fSBen Hoff dev_dbg(&pdx->pdev->dev, "irq: entry INT_STATUS=0x%08x\n", int_state); 219*ba07fd2fSBen Hoff 220*ba07fd2fSBen Hoff /* Loop until all pending bits are serviced (max 100 iterations) */ 221*ba07fd2fSBen Hoff for (u32 cnt = 0; int_state && cnt < MAX_INT_LOOPS; ++cnt) { 222*ba07fd2fSBen Hoff for (unsigned int ch = 0; ch < pdx->cur_max_video_ch; ++ch) { 223*ba07fd2fSBen Hoff u32 vbit = HWS_INT_VDONE_BIT(ch); 224*ba07fd2fSBen Hoff 225*ba07fd2fSBen Hoff if (!(int_state & vbit)) 226*ba07fd2fSBen Hoff continue; 227*ba07fd2fSBen Hoff 228*ba07fd2fSBen Hoff if (READ_ONCE(pdx->video[ch].cap_active) && 229*ba07fd2fSBen Hoff !READ_ONCE(pdx->video[ch].stop_requested)) { 230*ba07fd2fSBen Hoff if (hws_toggle_debug) { 231*ba07fd2fSBen Hoff u32 toggle = 232*ba07fd2fSBen Hoff readl_relaxed(pdx->bar0_base + 233*ba07fd2fSBen Hoff HWS_REG_VBUF_TOGGLE(ch)) & 0x01; 234*ba07fd2fSBen Hoff WRITE_ONCE(pdx->video[ch].last_buf_half_toggle, 235*ba07fd2fSBen Hoff toggle); 236*ba07fd2fSBen Hoff } 237*ba07fd2fSBen Hoff dma_rmb(); 238*ba07fd2fSBen Hoff WRITE_ONCE(pdx->video[ch].half_seen, true); 239*ba07fd2fSBen Hoff dev_dbg(&pdx->pdev->dev, 240*ba07fd2fSBen Hoff "irq: VDONE ch=%u toggle=%u handling inline (cap=%d)\n", 241*ba07fd2fSBen Hoff ch, 242*ba07fd2fSBen Hoff READ_ONCE(pdx->video[ch].last_buf_half_toggle), 243*ba07fd2fSBen Hoff READ_ONCE(pdx->video[ch].cap_active)); 244*ba07fd2fSBen Hoff hws_video_handle_vdone(&pdx->video[ch]); 245*ba07fd2fSBen Hoff } else { 246*ba07fd2fSBen Hoff dev_dbg(&pdx->pdev->dev, 247*ba07fd2fSBen Hoff "irq: VDONE ch=%u ignored (cap=%d stop=%d)\n", 248*ba07fd2fSBen Hoff ch, 249*ba07fd2fSBen Hoff READ_ONCE(pdx->video[ch].cap_active), 250*ba07fd2fSBen Hoff READ_ONCE(pdx->video[ch].stop_requested)); 251*ba07fd2fSBen Hoff } 252*ba07fd2fSBen Hoff 253*ba07fd2fSBen Hoff writel(vbit, pdx->bar0_base + HWS_REG_INT_STATUS); 254*ba07fd2fSBen Hoff (void)readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); 255*ba07fd2fSBen Hoff } 256*ba07fd2fSBen Hoff 257*ba07fd2fSBen Hoff /* Re-read in case new interrupt bits popped while processing */ 258*ba07fd2fSBen Hoff int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS); 259*ba07fd2fSBen Hoff dev_dbg(&pdx->pdev->dev, 260*ba07fd2fSBen Hoff "irq: loop cnt=%u new INT_STATUS=0x%08x\n", cnt, 261*ba07fd2fSBen Hoff int_state); 262*ba07fd2fSBen Hoff if (cnt + 1 == MAX_INT_LOOPS) 263*ba07fd2fSBen Hoff dev_warn_ratelimited(&pdx->pdev->dev, 264*ba07fd2fSBen Hoff "IRQ storm? status=0x%08x\n", 265*ba07fd2fSBen Hoff int_state); 266*ba07fd2fSBen Hoff } 267*ba07fd2fSBen Hoff 268*ba07fd2fSBen Hoff return IRQ_HANDLED; 269*ba07fd2fSBen Hoff } 270