|
@@ -43,6 +43,111 @@ static const struct tw686x_format formats[] = {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+static void tw686x_buf_done(struct tw686x_video_channel *vc,
|
|
|
+ unsigned int pb)
|
|
|
+{
|
|
|
+ struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
|
|
|
+ struct tw686x_dev *dev = vc->dev;
|
|
|
+ struct vb2_v4l2_buffer *vb;
|
|
|
+ struct vb2_buffer *vb2_buf;
|
|
|
+
|
|
|
+ if (vc->curr_bufs[pb]) {
|
|
|
+ vb = &vc->curr_bufs[pb]->vb;
|
|
|
+
|
|
|
+ vb->field = dev->dma_ops->field;
|
|
|
+ vb->sequence = vc->sequence++;
|
|
|
+ vb2_buf = &vb->vb2_buf;
|
|
|
+
|
|
|
+ if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY)
|
|
|
+ memcpy(vb2_plane_vaddr(vb2_buf, 0), desc->virt,
|
|
|
+ desc->size);
|
|
|
+ vb2_buf->timestamp = ktime_get_ns();
|
|
|
+ vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE);
|
|
|
+ }
|
|
|
+
|
|
|
+ vc->pb = !pb;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * We can call this even when alloc_dma failed for the given channel
|
|
|
+ */
|
|
|
+static void tw686x_memcpy_dma_free(struct tw686x_video_channel *vc,
|
|
|
+ unsigned int pb)
|
|
|
+{
|
|
|
+ struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
|
|
|
+ struct tw686x_dev *dev = vc->dev;
|
|
|
+ struct pci_dev *pci_dev;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ /* Check device presence. Shouldn't really happen! */
|
|
|
+ spin_lock_irqsave(&dev->lock, flags);
|
|
|
+ pci_dev = dev->pci_dev;
|
|
|
+ spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
+ if (!pci_dev) {
|
|
|
+ WARN(1, "trying to deallocate on missing device\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (desc->virt) {
|
|
|
+ pci_free_consistent(dev->pci_dev, desc->size,
|
|
|
+ desc->virt, desc->phys);
|
|
|
+ desc->virt = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int tw686x_memcpy_dma_alloc(struct tw686x_video_channel *vc,
|
|
|
+ unsigned int pb)
|
|
|
+{
|
|
|
+ struct tw686x_dev *dev = vc->dev;
|
|
|
+ u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch];
|
|
|
+ unsigned int len;
|
|
|
+ void *virt;
|
|
|
+
|
|
|
+ WARN(vc->dma_descs[pb].virt,
|
|
|
+ "Allocating buffer but previous still here\n");
|
|
|
+
|
|
|
+ len = (vc->width * vc->height * vc->format->depth) >> 3;
|
|
|
+ virt = pci_alloc_consistent(dev->pci_dev, len,
|
|
|
+ &vc->dma_descs[pb].phys);
|
|
|
+ if (!virt) {
|
|
|
+ v4l2_err(&dev->v4l2_dev,
|
|
|
+ "dma%d: unable to allocate %s-buffer\n",
|
|
|
+ vc->ch, pb ? "B" : "P");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ vc->dma_descs[pb].size = len;
|
|
|
+ vc->dma_descs[pb].virt = virt;
|
|
|
+ reg_write(dev, reg, vc->dma_descs[pb].phys);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void tw686x_memcpy_buf_refill(struct tw686x_video_channel *vc,
|
|
|
+ unsigned int pb)
|
|
|
+{
|
|
|
+ struct tw686x_v4l2_buf *buf;
|
|
|
+
|
|
|
+ while (!list_empty(&vc->vidq_queued)) {
|
|
|
+
|
|
|
+ buf = list_first_entry(&vc->vidq_queued,
|
|
|
+ struct tw686x_v4l2_buf, list);
|
|
|
+ list_del(&buf->list);
|
|
|
+
|
|
|
+ vc->curr_bufs[pb] = buf;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ vc->curr_bufs[pb] = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+const struct tw686x_dma_ops memcpy_dma_ops = {
|
|
|
+ .alloc = tw686x_memcpy_dma_alloc,
|
|
|
+ .free = tw686x_memcpy_dma_free,
|
|
|
+ .buf_refill = tw686x_memcpy_buf_refill,
|
|
|
+ .mem_ops = &vb2_vmalloc_memops,
|
|
|
+ .hw_dma_mode = TW686X_FRAME_MODE,
|
|
|
+ .field = V4L2_FIELD_INTERLACED,
|
|
|
+};
|
|
|
+
|
|
|
static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps)
|
|
|
{
|
|
|
static const unsigned int map[15] = {
|
|
@@ -123,6 +228,7 @@ static int tw686x_queue_setup(struct vb2_queue *vq,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ alloc_ctxs[0] = vc->dev->alloc_ctx;
|
|
|
sizes[0] = szimage;
|
|
|
*nplanes = 1;
|
|
|
return 0;
|
|
@@ -152,75 +258,6 @@ static void tw686x_buf_queue(struct vb2_buffer *vb)
|
|
|
spin_unlock_irqrestore(&vc->qlock, flags);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * We can call this even when alloc_dma failed for the given channel
|
|
|
- */
|
|
|
-static void tw686x_free_dma(struct tw686x_video_channel *vc, unsigned int pb)
|
|
|
-{
|
|
|
- struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
|
|
|
- struct tw686x_dev *dev = vc->dev;
|
|
|
- struct pci_dev *pci_dev;
|
|
|
- unsigned long flags;
|
|
|
-
|
|
|
- /* Check device presence. Shouldn't really happen! */
|
|
|
- spin_lock_irqsave(&dev->lock, flags);
|
|
|
- pci_dev = dev->pci_dev;
|
|
|
- spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
- if (!pci_dev) {
|
|
|
- WARN(1, "trying to deallocate on missing device\n");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (desc->virt) {
|
|
|
- pci_free_consistent(dev->pci_dev, desc->size,
|
|
|
- desc->virt, desc->phys);
|
|
|
- desc->virt = NULL;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static int tw686x_alloc_dma(struct tw686x_video_channel *vc, unsigned int pb)
|
|
|
-{
|
|
|
- struct tw686x_dev *dev = vc->dev;
|
|
|
- u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch];
|
|
|
- unsigned int len;
|
|
|
- void *virt;
|
|
|
-
|
|
|
- WARN(vc->dma_descs[pb].virt,
|
|
|
- "Allocating buffer but previous still here\n");
|
|
|
-
|
|
|
- len = (vc->width * vc->height * vc->format->depth) >> 3;
|
|
|
- virt = pci_alloc_consistent(dev->pci_dev, len,
|
|
|
- &vc->dma_descs[pb].phys);
|
|
|
- if (!virt) {
|
|
|
- v4l2_err(&dev->v4l2_dev,
|
|
|
- "dma%d: unable to allocate %s-buffer\n",
|
|
|
- vc->ch, pb ? "B" : "P");
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
- vc->dma_descs[pb].size = len;
|
|
|
- vc->dma_descs[pb].virt = virt;
|
|
|
- reg_write(dev, reg, vc->dma_descs[pb].phys);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static void tw686x_buffer_refill(struct tw686x_video_channel *vc,
|
|
|
- unsigned int pb)
|
|
|
-{
|
|
|
- struct tw686x_v4l2_buf *buf;
|
|
|
-
|
|
|
- while (!list_empty(&vc->vidq_queued)) {
|
|
|
-
|
|
|
- buf = list_first_entry(&vc->vidq_queued,
|
|
|
- struct tw686x_v4l2_buf, list);
|
|
|
- list_del(&buf->list);
|
|
|
-
|
|
|
- vc->curr_bufs[pb] = buf;
|
|
|
- return;
|
|
|
- }
|
|
|
- vc->curr_bufs[pb] = NULL;
|
|
|
-}
|
|
|
-
|
|
|
static void tw686x_clear_queue(struct tw686x_video_channel *vc,
|
|
|
enum vb2_buffer_state state)
|
|
|
{
|
|
@@ -262,7 +299,8 @@ static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
|
spin_lock_irqsave(&vc->qlock, flags);
|
|
|
|
|
|
/* Sanity check */
|
|
|
- if (!vc->dma_descs[0].virt || !vc->dma_descs[1].virt) {
|
|
|
+ if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY &&
|
|
|
+ (!vc->dma_descs[0].virt || !vc->dma_descs[1].virt)) {
|
|
|
spin_unlock_irqrestore(&vc->qlock, flags);
|
|
|
v4l2_err(&dev->v4l2_dev,
|
|
|
"video%d: refusing to start without DMA buffers\n",
|
|
@@ -272,7 +310,7 @@ static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
|
}
|
|
|
|
|
|
for (pb = 0; pb < 2; pb++)
|
|
|
- tw686x_buffer_refill(vc, pb);
|
|
|
+ dev->dma_ops->buf_refill(vc, pb);
|
|
|
spin_unlock_irqrestore(&vc->qlock, flags);
|
|
|
|
|
|
vc->sequence = 0;
|
|
@@ -375,10 +413,11 @@ static int tw686x_g_fmt_vid_cap(struct file *file, void *priv,
|
|
|
struct v4l2_format *f)
|
|
|
{
|
|
|
struct tw686x_video_channel *vc = video_drvdata(file);
|
|
|
+ struct tw686x_dev *dev = vc->dev;
|
|
|
|
|
|
f->fmt.pix.width = vc->width;
|
|
|
f->fmt.pix.height = vc->height;
|
|
|
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
|
|
|
+ f->fmt.pix.field = dev->dma_ops->field;
|
|
|
f->fmt.pix.pixelformat = vc->format->fourcc;
|
|
|
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
|
|
|
f->fmt.pix.bytesperline = (f->fmt.pix.width * vc->format->depth) / 8;
|
|
@@ -390,6 +429,7 @@ static int tw686x_try_fmt_vid_cap(struct file *file, void *priv,
|
|
|
struct v4l2_format *f)
|
|
|
{
|
|
|
struct tw686x_video_channel *vc = video_drvdata(file);
|
|
|
+ struct tw686x_dev *dev = vc->dev;
|
|
|
unsigned int video_height = TW686X_VIDEO_HEIGHT(vc->video_standard);
|
|
|
const struct tw686x_format *format;
|
|
|
|
|
@@ -412,7 +452,7 @@ static int tw686x_try_fmt_vid_cap(struct file *file, void *priv,
|
|
|
f->fmt.pix.bytesperline = (f->fmt.pix.width * format->depth) / 8;
|
|
|
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
|
|
|
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
|
|
|
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
|
|
|
+ f->fmt.pix.field = dev->dma_ops->field;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -421,6 +461,7 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
|
|
|
struct v4l2_format *f)
|
|
|
{
|
|
|
struct tw686x_video_channel *vc = video_drvdata(file);
|
|
|
+ struct tw686x_dev *dev = vc->dev;
|
|
|
u32 val, width, line_width, height;
|
|
|
unsigned long bitsperframe;
|
|
|
int err, pb;
|
|
@@ -438,15 +479,16 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
|
|
|
vc->height = f->fmt.pix.height;
|
|
|
|
|
|
/* We need new DMA buffers if the framesize has changed */
|
|
|
- if (bitsperframe != vc->width * vc->height * vc->format->depth) {
|
|
|
+ if (dev->dma_ops->alloc &&
|
|
|
+ bitsperframe != vc->width * vc->height * vc->format->depth) {
|
|
|
for (pb = 0; pb < 2; pb++)
|
|
|
- tw686x_free_dma(vc, pb);
|
|
|
+ dev->dma_ops->free(vc, pb);
|
|
|
|
|
|
for (pb = 0; pb < 2; pb++) {
|
|
|
- err = tw686x_alloc_dma(vc, pb);
|
|
|
+ err = dev->dma_ops->alloc(vc, pb);
|
|
|
if (err) {
|
|
|
if (pb > 0)
|
|
|
- tw686x_free_dma(vc, 0);
|
|
|
+ dev->dma_ops->free(vc, 0);
|
|
|
return err;
|
|
|
}
|
|
|
}
|
|
@@ -713,26 +755,11 @@ static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = {
|
|
|
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
|
|
};
|
|
|
|
|
|
-static void tw686x_buffer_copy(struct tw686x_video_channel *vc,
|
|
|
- unsigned int pb, struct vb2_v4l2_buffer *vb)
|
|
|
-{
|
|
|
- struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
|
|
|
- struct vb2_buffer *vb2_buf = &vb->vb2_buf;
|
|
|
-
|
|
|
- vb->field = V4L2_FIELD_INTERLACED;
|
|
|
- vb->sequence = vc->sequence++;
|
|
|
-
|
|
|
- memcpy(vb2_plane_vaddr(vb2_buf, 0), desc->virt, desc->size);
|
|
|
- vb2_buf->timestamp = ktime_get_ns();
|
|
|
- vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE);
|
|
|
-}
|
|
|
-
|
|
|
void tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests,
|
|
|
unsigned int pb_status, unsigned int fifo_status,
|
|
|
unsigned int *reset_ch)
|
|
|
{
|
|
|
struct tw686x_video_channel *vc;
|
|
|
- struct vb2_v4l2_buffer *vb;
|
|
|
unsigned long flags;
|
|
|
unsigned int ch, pb;
|
|
|
|
|
@@ -781,14 +808,9 @@ void tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests,
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- /* handle video stream */
|
|
|
spin_lock_irqsave(&vc->qlock, flags);
|
|
|
- if (vc->curr_bufs[pb]) {
|
|
|
- vb = &vc->curr_bufs[pb]->vb;
|
|
|
- tw686x_buffer_copy(vc, pb, vb);
|
|
|
- }
|
|
|
- vc->pb = !pb;
|
|
|
- tw686x_buffer_refill(vc, pb);
|
|
|
+ tw686x_buf_done(vc, pb);
|
|
|
+ dev->dma_ops->buf_refill(vc, pb);
|
|
|
spin_unlock_irqrestore(&vc->qlock, flags);
|
|
|
}
|
|
|
}
|
|
@@ -803,9 +825,13 @@ void tw686x_video_free(struct tw686x_dev *dev)
|
|
|
if (vc->device)
|
|
|
video_unregister_device(vc->device);
|
|
|
|
|
|
- for (pb = 0; pb < 2; pb++)
|
|
|
- tw686x_free_dma(vc, pb);
|
|
|
+ if (dev->dma_ops->free)
|
|
|
+ for (pb = 0; pb < 2; pb++)
|
|
|
+ dev->dma_ops->free(vc, pb);
|
|
|
}
|
|
|
+
|
|
|
+ if (dev->dma_ops->cleanup)
|
|
|
+ dev->dma_ops->cleanup(dev);
|
|
|
}
|
|
|
|
|
|
int tw686x_video_init(struct tw686x_dev *dev)
|
|
@@ -813,10 +839,21 @@ int tw686x_video_init(struct tw686x_dev *dev)
|
|
|
unsigned int ch, val, pb;
|
|
|
int err;
|
|
|
|
|
|
+ if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY)
|
|
|
+ dev->dma_ops = &memcpy_dma_ops;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
|
+ if (dev->dma_ops->setup) {
|
|
|
+ err = dev->dma_ops->setup(dev);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
for (ch = 0; ch < max_channels(dev); ch++) {
|
|
|
struct tw686x_video_channel *vc = &dev->video_channels[ch];
|
|
|
struct video_device *vdev;
|
|
@@ -842,10 +879,12 @@ int tw686x_video_init(struct tw686x_dev *dev)
|
|
|
reg_write(dev, HACTIVE_LO[ch], 0xd0);
|
|
|
reg_write(dev, VIDEO_SIZE[ch], 0);
|
|
|
|
|
|
- for (pb = 0; pb < 2; pb++) {
|
|
|
- err = tw686x_alloc_dma(vc, pb);
|
|
|
- if (err)
|
|
|
- goto error;
|
|
|
+ if (dev->dma_ops->alloc) {
|
|
|
+ for (pb = 0; pb < 2; pb++) {
|
|
|
+ err = dev->dma_ops->alloc(vc, pb);
|
|
|
+ if (err)
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
vc->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
|
|
@@ -853,7 +892,7 @@ int tw686x_video_init(struct tw686x_dev *dev)
|
|
|
vc->vidq.drv_priv = vc;
|
|
|
vc->vidq.buf_struct_size = sizeof(struct tw686x_v4l2_buf);
|
|
|
vc->vidq.ops = &tw686x_video_qops;
|
|
|
- vc->vidq.mem_ops = &vb2_vmalloc_memops;
|
|
|
+ vc->vidq.mem_ops = dev->dma_ops->mem_ops;
|
|
|
vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
|
|
|
vc->vidq.min_buffers_needed = 2;
|
|
|
vc->vidq.lock = &vc->vb_mutex;
|
|
@@ -915,10 +954,9 @@ int tw686x_video_init(struct tw686x_dev *dev)
|
|
|
vc->num = vdev->num;
|
|
|
}
|
|
|
|
|
|
- /* Set DMA frame mode on all channels. Only supported mode for now. */
|
|
|
val = TW686X_DEF_PHASE_REF;
|
|
|
for (ch = 0; ch < max_channels(dev); ch++)
|
|
|
- val |= TW686X_FRAME_MODE << (16 + ch * 2);
|
|
|
+ val |= dev->dma_ops->hw_dma_mode << (16 + ch * 2);
|
|
|
reg_write(dev, PHASE_REF, val);
|
|
|
|
|
|
reg_write(dev, MISC2[0], 0xe7);
|