[PATCH 1/2] [RFC] media: rcar-vin: send a V4L2 event to vdev if no frame captured after a timeout

From: Michael Rodin
Date: Fri Jun 19 2020 - 13:47:20 EST


Data flow from an upstream subdevice can stop permanently due to:
- CSI2 transmission errors
- silent failure of the source subdevice
- disconnection of the source subdevice
In those cases userspace waits for new buffers for an infinitely long time.
In order to address this issue, use a timer to monitor, that rvin_irq() is
capturing at least one frame within a IRQ_TIMEOUT_MS period. Otherwise send
a new private v4l2 event to userspace. This event is exported to userspace
via a new uapi header.

Signed-off-by: Michael Rodin <mrodin@xxxxxxxxxxxxxx>
---
drivers/media/platform/rcar-vin/rcar-dma.c | 21 +++++++++++++++++++++
drivers/media/platform/rcar-vin/rcar-v4l2.c | 1 +
drivers/media/platform/rcar-vin/rcar-vin.h | 6 ++++++
include/uapi/linux/rcar-vin.h | 10 ++++++++++
4 files changed, 38 insertions(+)
create mode 100644 include/uapi/linux/rcar-vin.h

diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index 1a30cd0..bf8d733 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -937,6 +937,20 @@ static void rvin_capture_stop(struct rvin_dev *vin)
#define RVIN_TIMEOUT_MS 100
#define RVIN_RETRIES 10

+static const struct v4l2_event rvin_irq_timeout = {
+ .type = V4L2_EVENT_RCAR_VIN_IRQ_TIMEOUT,
+};
+
+static void rvin_irq_timer_function(struct timer_list *timer)
+{
+ struct rvin_dev *vin = container_of(timer, struct rvin_dev,
+ irq_timer);
+
+ vin_err(vin, "%s: frame completion timeout after %i ms!\n",
+ __func__, IRQ_TIMEOUT_MS);
+ v4l2_event_queue(&vin->vdev, &rvin_irq_timeout);
+}
+
static irqreturn_t rvin_irq(int irq, void *data)
{
struct rvin_dev *vin = data;
@@ -1008,6 +1022,8 @@ static irqreturn_t rvin_irq(int irq, void *data)
vin_dbg(vin, "Dropping frame %u\n", vin->sequence);
}

+ mod_timer(&vin->irq_timer, jiffies + msecs_to_jiffies(IRQ_TIMEOUT_MS));
+
vin->sequence++;

/* Prepare for next frame */
@@ -1252,6 +1268,8 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
if (ret)
dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch,
vin->scratch_phys);
+ else
+ mod_timer(&vin->irq_timer, jiffies + msecs_to_jiffies(IRQ_TIMEOUT_MS));

return ret;
}
@@ -1305,6 +1323,8 @@ static void rvin_stop_streaming(struct vb2_queue *vq)
/* Free scratch buffer. */
dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch,
vin->scratch_phys);
+
+ del_timer_sync(&vin->irq_timer);
}

static const struct vb2_ops rvin_qops = {
@@ -1370,6 +1390,7 @@ int rvin_dma_register(struct rvin_dev *vin, int irq)
goto error;
}

+ timer_setup(&vin->irq_timer, rvin_irq_timer_function, 0);
return 0;
error:
rvin_dma_unregister(vin);
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
index f421e25..c644134 100644
--- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
@@ -581,6 +581,7 @@ static int rvin_subscribe_event(struct v4l2_fh *fh,
{
switch (sub->type) {
case V4L2_EVENT_SOURCE_CHANGE:
+ case V4L2_EVENT_RCAR_VIN_IRQ_TIMEOUT:
return v4l2_event_subscribe(fh, sub, 4, NULL);
}
return v4l2_ctrl_subscribe_event(fh, sub);
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
index c19d077..7408f67 100644
--- a/drivers/media/platform/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/rcar-vin/rcar-vin.h
@@ -14,12 +14,14 @@
#define __RCAR_VIN__

#include <linux/kref.h>
+#include <linux/rcar-vin.h>

#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-event.h>

/* Number of HW buffers */
#define HW_BUFFER_NUM 3
@@ -30,6 +32,8 @@
/* Max number on VIN instances that can be in a system */
#define RCAR_VIN_NUM 8

+#define IRQ_TIMEOUT_MS 1000
+
struct rvin_group;

enum model_id {
@@ -196,6 +200,7 @@ struct rvin_info {
* @compose: active composing
* @src_rect: active size of the video source
* @std: active video standard of the video source
+ * @irq_timer: monitors regular capturing of frames in rvin_irq()
*
* @alpha: Alpha component to fill in for supported pixel formats
*/
@@ -240,6 +245,7 @@ struct rvin_dev {
struct v4l2_rect src_rect;
v4l2_std_id std;

+ struct timer_list irq_timer;
unsigned int alpha;
};

diff --git a/include/uapi/linux/rcar-vin.h b/include/uapi/linux/rcar-vin.h
new file mode 100644
index 00000000..4eb7f5e
--- /dev/null
+++ b/include/uapi/linux/rcar-vin.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef RCAR_VIN_USER_H
+#define RCAR_VIN_USER_H
+
+/* class for events sent by the rcar-vin driver */
+#define V4L2_EVENT_RCAR_VIN_CLASS V4L2_EVENT_PRIVATE_START
+#define V4L2_EVENT_RCAR_VIN_IRQ_TIMEOUT (V4L2_EVENT_RCAR_VIN_CLASS | 0x1)
+
+#endif /* RCAR_VIN_USER_H */
--
2.7.4