diff --git a/bcwc_drv.c b/bcwc_drv.c index 21aa84f..ada43aa 100644 --- a/bcwc_drv.c +++ b/bcwc_drv.c @@ -178,13 +178,9 @@ static void io_t2h_handler(struct bcwc_private *dev_priv, static void bcwc_handle_irq(struct bcwc_private *dev_priv, struct fw_channel *chan) { struct bcwc_ringbuf_entry *entry; - int i = 0; - - pr_debug("Interrupt from channel source %d, type %d [%s]\n", chan->source, chan->type, chan->name); - - while(bcwc_channel_ringbuf_entry_available(dev_priv, chan) && i++ < 500) { - entry = bcwc_channel_ringbuf_get_entry(dev_priv, chan); + while((entry = bcwc_channel_ringbuf_receive(dev_priv, chan))) { + pr_debug("channel %s: message available, address %08x\n", chan->name, entry->address_flags); if (chan == dev_priv->channel_shared_malloc) { sharedmalloc_handler(dev_priv, chan, entry); } else if (chan == dev_priv->channel_terminal) { @@ -338,7 +334,6 @@ static int bcwc_pci_probe(struct pci_dev *pdev, } spin_lock_init(&dev_priv->io_lock); - spin_lock_init(&dev_priv->rb_lock); mutex_init(&dev_priv->vb2_queue_lock); mutex_init(&dev_priv->ioctl_lock); diff --git a/bcwc_ringbuf.c b/bcwc_ringbuf.c index a635340..1495eca 100644 --- a/bcwc_ringbuf.c +++ b/bcwc_ringbuf.c @@ -65,14 +65,14 @@ void bcwc_channel_ringbuf_init(struct bcwc_private *dev_priv, struct fw_channel entry = (struct bcwc_ringbuf_entry *)chan->ringbuf.virt_addr; pr_debug("clearing ringbuf %s at %p (size %d)\n", chan->name, entry, chan->size); - spin_lock_irq(&dev_priv->rb_lock); + spin_lock_irq(&chan->lock); for(i = 0; i < chan->size; i++) { entry->address_flags = 1; entry->request_size = 0; entry->response_size = 0; entry++; } - spin_unlock_irq(&dev_priv->rb_lock); + spin_unlock_irq(&chan->lock); } } @@ -80,52 +80,57 @@ struct bcwc_ringbuf_entry *bcwc_channel_ringbuf_send(struct bcwc_private *dev_pr u32 data_offset, u32 request_size, u32 response_size) { struct bcwc_ringbuf_entry *entry; - int pos = chan->ringbuf.idx; - entry = get_entry_addr(dev_priv, chan, chan->ringbuf.idx++); - if (chan->ringbuf.idx >= chan->size) { - pr_debug("%s: reset tx pointer\n", chan->name); - chan->ringbuf.idx = 0; + pr_debug("send %08x\n", data_offset); + + spin_lock_irq(&chan->lock); + entry = get_entry_addr(dev_priv, chan, chan->ringbuf.idx); + + if (chan->tx_lock) { + spin_unlock_irq(&chan->lock); + return NULL; } - pr_debug("%s: send entry %p offset %08x pos %d\n", chan->name, entry, data_offset, pos); - spin_lock_irq(&dev_priv->rb_lock); + if (chan->type != FW_CHAN_TYPE_OUT && ++chan->ringbuf.idx >= chan->size) + chan->ringbuf.idx = 0; + + chan->tx_lock = 1; + chan->rx_lock = 0; + entry->request_size = request_size; entry->response_size = response_size; - entry->address_flags = data_offset | (chan->type == 0 ? 0 : 1); - spin_unlock_irq(&dev_priv->rb_lock); - - // pr_debug("address_flags %x, request size %x response size %x\n", - // entry->address_flags, entry->request_size, entry->response_size); wmb(); + entry->address_flags = data_offset | (chan->type == 0 ? 0 : 1); + spin_unlock_irq(&chan->lock); + spin_lock_irq(&dev_priv->io_lock); BCWC_ISP_REG_WRITE(0x10 << chan->source, ISP_REG_41020); spin_unlock_irq(&dev_priv->io_lock); return entry; } -struct bcwc_ringbuf_entry *bcwc_channel_ringbuf_get_entry(struct bcwc_private *dev_priv, +struct bcwc_ringbuf_entry *bcwc_channel_ringbuf_receive(struct bcwc_private *dev_priv, struct fw_channel *chan) { - struct bcwc_ringbuf_entry *entry; + struct bcwc_ringbuf_entry *entry, *ret = NULL; + + spin_lock_irq(&chan->lock); + if (chan->rx_lock) + goto out; - spin_lock_irq(&dev_priv->rb_lock); entry = get_entry_addr(dev_priv, chan, chan->ringbuf.idx); - if (chan->ringbuf.idx > chan->size) + + if (!(entry->address_flags & 1) ^ (chan->type != 0)) + goto out; + + ret = entry; + + if (chan->type == FW_CHAN_TYPE_OUT && ++chan->ringbuf.idx >= chan->size) chan->ringbuf.idx = 0; - spin_unlock_irq(&dev_priv->rb_lock); - return entry; -} -int bcwc_channel_ringbuf_entry_available(struct bcwc_private *dev_priv, - struct fw_channel *chan) -{ - struct bcwc_ringbuf_entry *entry; - int ret; - - spin_lock_irq(&dev_priv->rb_lock); - entry = get_entry_addr(dev_priv, chan, chan->ringbuf.idx); - ret = !(entry->address_flags & 1) ^ (chan->type == 0); - spin_unlock_irq(&dev_priv->rb_lock); + chan->rx_lock = 1; + chan->tx_lock = 0; +out: + spin_unlock_irq(&chan->lock); return ret; } diff --git a/bcwc_ringbuf.h b/bcwc_ringbuf.h index 3c2aa07..e2889d0 100644 --- a/bcwc_ringbuf.h +++ b/bcwc_ringbuf.h @@ -48,7 +48,7 @@ extern struct bcwc_ringbuf_entry *bcwc_channel_ringbuf_get_entry(struct bcwc_pri extern struct bcwc_ringbuf_entry *bcwc_channel_ringbuf_send(struct bcwc_private *dev_priv, struct fw_channel *chan, u32 data_offset, u32 request_size, u32 response_size); -extern int bcwc_channel_ringbuf_entry_available(struct bcwc_private *dev_priv, - struct fw_channel *chan); +struct bcwc_ringbuf_entry *bcwc_channel_ringbuf_receive(struct bcwc_private *dev_priv, + struct fw_channel *chan); #endif diff --git a/bcwc_v4l2.c b/bcwc_v4l2.c index a51b4c9..d4c73c9 100644 --- a/bcwc_v4l2.c +++ b/bcwc_v4l2.c @@ -112,6 +112,32 @@ static void bcwc_buffer_cleanup(struct vb2_buffer *vb) ctx->dma_desc_obj = NULL; } +static int bcwc_send_h2t_buffer(struct bcwc_private *dev_priv, struct h2t_buf_ctx *ctx) +{ + volatile struct bcwc_ringbuf_entry *entry; + + // pr_debug("sending buffer %p\n", ctx->vb); + entry = bcwc_channel_ringbuf_send(dev_priv, dev_priv->channel_buf_h2t, + ctx->dma_desc_obj->offset, 0x180, 0x30000000); + + if (!entry) + return -EIO; + + if (wait_event_interruptible_timeout(ctx->wq, ctx->done, HZ) <= 0) { + dev_err(&dev_priv->pdev->dev, "timeout wait for buffer %p\n", ctx->vb); + return -ETIMEDOUT; + } + ctx->done = 0; + return 0; +} + +void bcwc_buffer_queued_handler(struct bcwc_private *dev_priv, struct dma_descriptor_list *list) +{ + struct h2t_buf_ctx *ctx = (struct h2t_buf_ctx *)list->desc[0].tag; + ctx->done = 1; + wake_up_interruptible(&ctx->wq); +} + static void bcwc_buffer_queue(struct vb2_buffer *vb) { struct bcwc_private *dev_priv = vb2_get_drv_priv(vb->vb2_queue); @@ -149,10 +175,12 @@ static void bcwc_buffer_queue(struct vb2_buffer *vb) pr_debug("%d: field0: %d, count %d, pool %d, addr0 0x%08x, addr1 0x%08x tag 0x%08llx vb = %p\n", i, list->field0, list->desc[i].count, list->desc[i].pool, list->desc[i].addr0, list->desc[i].addr1, list->desc[i].tag, ctx->vb); - bcwc_channel_ringbuf_send(dev_priv, dev_priv->channel_buf_h2t, - ctx->dma_desc_obj->offset, 0x180, 0x30000000); + if (bcwc_send_h2t_buffer(dev_priv, ctx)) { + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + ctx->state = BUF_ALLOC; + } } - + return; } static int bcwc_buffer_prepare(struct vb2_buffer *vb) @@ -209,6 +237,7 @@ static int bcwc_buffer_prepare(struct vb2_buffer *vb) dma_list->desc[0].addr2 = (ctx->plane[2]->offset << 12) | 0xc0000000; dma_list->desc[0].tag = (u64)ctx; + init_waitqueue_head(&ctx->wq); return 0; } @@ -270,9 +299,11 @@ static int bcwc_start_streaming(struct vb2_queue *vq, unsigned int count) if (ctx->state != BUF_DRV_QUEUED) continue; - ctx->state = BUF_HW_QUEUED; - bcwc_channel_ringbuf_send(dev_priv, dev_priv->channel_buf_h2t, - ctx->dma_desc_obj->offset, 0x180, 0x30000000); + if (bcwc_send_h2t_buffer(dev_priv, ctx)) { + vb2_buffer_done(ctx->vb, VB2_BUF_STATE_ERROR); + ctx->state = BUF_ALLOC; + } + ctx->state = BUF_HW_QUEUED; } return 0; @@ -281,14 +312,25 @@ static int bcwc_start_streaming(struct vb2_queue *vq, unsigned int count) static void bcwc_stop_streaming(struct vb2_queue *vq) { struct bcwc_private *dev_priv = vb2_get_drv_priv(vq); + struct h2t_buf_ctx *ctx; + int ret, i; - pr_debug("%s\n", __FUNCTION__); - - bcwc_isp_cmd_channel_buffer_return(dev_priv, 0); - pr_debug("waiting for buffers...\n"); - vb2_wait_for_all_buffers(vq); - pr_debug("done\n"); - bcwc_isp_cmd_channel_stop(dev_priv); + ret = bcwc_stop_channel(dev_priv, 0); + if (!ret) { + pr_debug("waiting for buffers...\n"); + vb2_wait_for_all_buffers(vq); + pr_debug("done\n"); + } else { + /* Firmware doesn't respond. */ + for(i = 0; i < BCWC_BUFFERS;i++) { + ctx = dev_priv->h2t_bufs + i; + if (ctx->state == BUF_DRV_QUEUED || ctx->state == BUF_HW_QUEUED) { + vb2_buffer_done(ctx->vb, VB2_BUF_STATE_DONE); + ctx->vb = NULL; + ctx->state = BUF_ALLOC; + } + } + } } static struct vb2_ops vb2_queue_ops = {