summaryrefslogtreecommitdiffstats
path: root/drivers/media/platform/rcar-vin/rcar-dma.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-04-03 17:16:59 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2018-04-03 17:16:59 -0700
commitef1c4a6fa91bbbe9b09f770d28eba31a9edf770c (patch)
tree52f5d175031c553160d14890e876ffc5432d2467 /drivers/media/platform/rcar-vin/rcar-dma.c
parent147a89bc71e7db40f011454a40add7ff2d10f8d8 (diff)
parentf8a695c4b43d02c89b8bba9ba6058fd5db1bc71d (diff)
downloadlinux-ef1c4a6fa91bbbe9b09f770d28eba31a9edf770c.tar.bz2
Merge tag 'media/v4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - new CEC pin injection code for testing purposes - DVB frontend cxd2099 promoted from staging - new platform driver for Sony cxd2880 DVB devices - new sensor drivers: mt9t112, ov2685, ov5695, ov772x, tda1997x, tw9910.c - removal of unused cx18 and ivtv alsa mixers - the reneseas-ceu driver doesn't depend on soc_camera anymore and moved from staging - removed the mantis_vp3028 driver, unused since 2009 - s5p-mfc: add support for version 10 of the MSP - added a decoder for imon protocol - atomisp: lots of cleanups - imx074 and mt9t031: don't depend on soc_camera anymore, being promoted from staging - added helper functions to better support DVB I2C binding - lots of driver improvements and cleanups * tag 'media/v4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (438 commits) media: v4l2-ioctl: rename a temp var that stores _IOC_SIZE(cmd) media: fimc-capture: get rid of two warnings media: dvb-usb-v2: fix a missing dependency of I2C_MUX media: uvc: to the right check at uvc_ioctl_enum_framesizes() media: cec-core: fix a bug at cec_error_inj_write() media: tda9840: cleanup a warning media: tm6000: avoid casting just to print pointer address media: em28xx-input: improve error handling code media: zr364xx: avoid casting just to print pointer address media: vivid-radio-rx: add a cast to avoid a warning media: saa7134-alsa: don't use casts to print a buffer address media: solo6x10: get rid of an address space warning media: zoran: don't cast pointers to print them media: ir-kbd-i2c: change the if logic to avoid a warning media: ir-kbd-i2c: improve error handling code media: saa7134-input: improve error handling media: s2255drv: fix a casting warning media: ivtvfb: Cleanup some warnings media: videobuf-dma-sg: Fix a weird cast soc_camera: fix a weird cast on printk ...
Diffstat (limited to 'drivers/media/platform/rcar-vin/rcar-dma.c')
-rw-r--r--drivers/media/platform/rcar-vin/rcar-dma.c206
1 files changed, 70 insertions, 136 deletions
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index 23fdff7a7370..4a40e6ad1be7 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -168,12 +168,8 @@ static int rvin_setup(struct rvin_dev *vin)
break;
case V4L2_FIELD_ALTERNATE:
case V4L2_FIELD_NONE:
- if (vin->continuous) {
- vnmc = VNMC_IM_ODD_EVEN;
- progressive = true;
- } else {
- vnmc = VNMC_IM_ODD;
- }
+ vnmc = VNMC_IM_ODD_EVEN;
+ progressive = true;
break;
default:
vnmc = VNMC_IM_ODD;
@@ -298,14 +294,6 @@ static bool rvin_capture_active(struct rvin_dev *vin)
return rvin_read(vin, VNMS_REG) & VNMS_CA;
}
-static int rvin_get_active_slot(struct rvin_dev *vin, u32 vnms)
-{
- if (vin->continuous)
- return (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT;
-
- return 0;
-}
-
static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms)
{
if (vin->format.field == V4L2_FIELD_ALTERNATE) {
@@ -344,76 +332,47 @@ static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr)
rvin_write(vin, offset, VNMB_REG(slot));
}
-/* Moves a buffer from the queue to the HW slots */
-static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
+/*
+ * Moves a buffer from the queue to the HW slot. If no buffer is
+ * available use the scratch buffer. The scratch buffer is never
+ * returned to userspace, its only function is to enable the capture
+ * loop to keep running.
+ */
+static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
{
struct rvin_buffer *buf;
struct vb2_v4l2_buffer *vbuf;
- dma_addr_t phys_addr_top;
-
- if (vin->queue_buf[slot] != NULL)
- return true;
+ dma_addr_t phys_addr;
- if (list_empty(&vin->buf_list))
- return false;
+ /* A already populated slot shall never be overwritten. */
+ if (WARN_ON(vin->queue_buf[slot] != NULL))
+ return;
vin_dbg(vin, "Filling HW slot: %d\n", slot);
- /* Keep track of buffer we give to HW */
- buf = list_entry(vin->buf_list.next, struct rvin_buffer, list);
- vbuf = &buf->vb;
- list_del_init(to_buf_list(vbuf));
- vin->queue_buf[slot] = vbuf;
-
- /* Setup DMA */
- phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
- rvin_set_slot_addr(vin, slot, phys_addr_top);
-
- return true;
-}
-
-static bool rvin_fill_hw(struct rvin_dev *vin)
-{
- int slot, limit;
-
- limit = vin->continuous ? HW_BUFFER_NUM : 1;
-
- for (slot = 0; slot < limit; slot++)
- if (!rvin_fill_hw_slot(vin, slot))
- return false;
- return true;
-}
-
-static void rvin_capture_on(struct rvin_dev *vin)
-{
- vin_dbg(vin, "Capture on in %s mode\n",
- vin->continuous ? "continuous" : "single");
+ if (list_empty(&vin->buf_list)) {
+ vin->queue_buf[slot] = NULL;
+ phys_addr = vin->scratch_phys;
+ } else {
+ /* Keep track of buffer we give to HW */
+ buf = list_entry(vin->buf_list.next, struct rvin_buffer, list);
+ vbuf = &buf->vb;
+ list_del_init(to_buf_list(vbuf));
+ vin->queue_buf[slot] = vbuf;
+
+ /* Setup DMA */
+ phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
+ }
- if (vin->continuous)
- /* Continuous Frame Capture Mode */
- rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
- else
- /* Single Frame Capture Mode */
- rvin_write(vin, VNFC_S_FRAME, VNFC_REG);
+ rvin_set_slot_addr(vin, slot, phys_addr);
}
static int rvin_capture_start(struct rvin_dev *vin)
{
- struct rvin_buffer *buf, *node;
- int bufs, ret;
-
- /* Count number of free buffers */
- bufs = 0;
- list_for_each_entry_safe(buf, node, &vin->buf_list, list)
- bufs++;
+ int slot, ret;
- /* Continuous capture requires more buffers then there are HW slots */
- vin->continuous = bufs > HW_BUFFER_NUM;
-
- if (!rvin_fill_hw(vin)) {
- vin_err(vin, "HW not ready to start, not enough buffers available\n");
- return -EINVAL;
- }
+ for (slot = 0; slot < HW_BUFFER_NUM; slot++)
+ rvin_fill_hw_slot(vin, slot);
rvin_crop_scale_comp(vin);
@@ -421,7 +380,10 @@ static int rvin_capture_start(struct rvin_dev *vin)
if (ret)
return ret;
- rvin_capture_on(vin);
+ vin_dbg(vin, "Starting to capture\n");
+
+ /* Continuous Frame Capture Mode */
+ rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
vin->state = RUNNING;
@@ -904,7 +866,7 @@ static irqreturn_t rvin_irq(int irq, void *data)
struct rvin_dev *vin = data;
u32 int_status, vnms;
int slot;
- unsigned int i, sequence, handled = 0;
+ unsigned int handled = 0;
unsigned long flags;
spin_lock_irqsave(&vin->qlock, flags);
@@ -930,65 +892,25 @@ static irqreturn_t rvin_irq(int irq, void *data)
/* Prepare for capture and update state */
vnms = rvin_read(vin, VNMS_REG);
- slot = rvin_get_active_slot(vin, vnms);
- sequence = vin->sequence++;
-
- vin_dbg(vin, "IRQ %02d: %d\tbuf0: %c buf1: %c buf2: %c\tmore: %d\n",
- sequence, slot,
- slot == 0 ? 'x' : vin->queue_buf[0] != NULL ? '1' : '0',
- slot == 1 ? 'x' : vin->queue_buf[1] != NULL ? '1' : '0',
- slot == 2 ? 'x' : vin->queue_buf[2] != NULL ? '1' : '0',
- !list_empty(&vin->buf_list));
-
- /* HW have written to a slot that is not prepared we are in trouble */
- if (WARN_ON((vin->queue_buf[slot] == NULL)))
- goto done;
+ slot = (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT;
/* Capture frame */
- vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms);
- vin->queue_buf[slot]->sequence = sequence;
- vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns();
- vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE);
- vin->queue_buf[slot] = NULL;
-
- /* Prepare for next frame */
- if (!rvin_fill_hw(vin)) {
-
- /*
- * Can't supply HW with new buffers fast enough. Halt
- * capture until more buffers are available.
- */
- vin->state = STALLED;
-
- /*
- * The continuous capturing requires an explicit stop
- * operation when there is no buffer to be set into
- * the VnMBm registers.
- */
- if (vin->continuous) {
- rvin_capture_stop(vin);
- vin_dbg(vin, "IRQ %02d: hw not ready stop\n", sequence);
-
- /* Maybe we can continue in single capture mode */
- for (i = 0; i < HW_BUFFER_NUM; i++) {
- if (vin->queue_buf[i]) {
- list_add(to_buf_list(vin->queue_buf[i]),
- &vin->buf_list);
- vin->queue_buf[i] = NULL;
- }
- }
-
- if (!list_empty(&vin->buf_list))
- rvin_capture_start(vin);
- }
+ if (vin->queue_buf[slot]) {
+ vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms);
+ vin->queue_buf[slot]->sequence = vin->sequence;
+ vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf,
+ VB2_BUF_STATE_DONE);
+ vin->queue_buf[slot] = NULL;
} else {
- /*
- * The single capturing requires an explicit capture
- * operation to fetch the next frame.
- */
- if (!vin->continuous)
- rvin_capture_on(vin);
+ /* Scratch buffer was used, dropping frame. */
+ vin_dbg(vin, "Dropping frame %u\n", vin->sequence);
}
+
+ vin->sequence++;
+
+ /* Prepare for next frame */
+ rvin_fill_hw_slot(vin, slot);
done:
spin_unlock_irqrestore(&vin->qlock, flags);
@@ -1059,13 +981,6 @@ static void rvin_buffer_queue(struct vb2_buffer *vb)
list_add_tail(to_buf_list(vbuf), &vin->buf_list);
- /*
- * If capture is stalled add buffer to HW and restart
- * capturing if HW is ready to continue.
- */
- if (vin->state == STALLED)
- rvin_capture_start(vin);
-
spin_unlock_irqrestore(&vin->qlock, flags);
}
@@ -1076,6 +991,17 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
unsigned long flags;
int ret;
+ /* Allocate scratch buffer. */
+ vin->scratch = dma_alloc_coherent(vin->dev, vin->format.sizeimage,
+ &vin->scratch_phys, GFP_KERNEL);
+ if (!vin->scratch) {
+ spin_lock_irqsave(&vin->qlock, flags);
+ return_all_buffers(vin, VB2_BUF_STATE_QUEUED);
+ spin_unlock_irqrestore(&vin->qlock, flags);
+ vin_err(vin, "Failed to allocate scratch buffer\n");
+ return -ENOMEM;
+ }
+
sd = vin_to_source(vin);
v4l2_subdev_call(sd, video, s_stream, 1);
@@ -1091,6 +1017,10 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
spin_unlock_irqrestore(&vin->qlock, flags);
+ if (ret)
+ dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch,
+ vin->scratch_phys);
+
return ret;
}
@@ -1141,6 +1071,10 @@ static void rvin_stop_streaming(struct vb2_queue *vq)
/* disable interrupts */
rvin_disable_interrupts(vin);
+
+ /* Free scratch buffer. */
+ dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch,
+ vin->scratch_phys);
}
static const struct vb2_ops rvin_qops = {
@@ -1189,7 +1123,7 @@ int rvin_dma_probe(struct rvin_dev *vin, int irq)
q->ops = &rvin_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_buffers_needed = 1;
+ q->min_buffers_needed = 4;
q->dev = vin->dev;
ret = vb2_queue_init(q);