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