From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Cc: Linux Media Mailing List <linux-media@vger.kernel.org>,
Hans Verkuil <hverkuil@xs4all.nl>
Subject: Re: [PATCH v2 3/3] uvcvideo: add a metadata device node
Date: Mon, 05 Dec 2016 12:53:30 +0200 [thread overview]
Message-ID: <2361420.98YnhAaLcS@avalon> (raw)
In-Reply-To: <Pine.LNX.4.64.1612021141110.11357@axis700.grange>
Hi Guennadi,
Thank you for the patch.
On Friday 02 Dec 2016 11:53:23 Guennadi Liakhovetski wrote:
> Some UVC video cameras contain metadata in their payload headers. This
> patch extracts that data, skipping the standard part of the header, on
> both bulk and isochronous endpoints and makes it available to the user
> space on a separate video node, using the V4L2_CAP_META_CAPTURE
> capability and the V4L2_BUF_TYPE_META_CAPTURE buffer queue type. Even
> though different cameras will have different metadata formats, we use
> the same V4L2_META_FMT_UVC pixel format for all of them. Users have to
> parse data, based on the specific camera model information.
>
> Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@intel.com>
> ---
>
> v2:
> - updated to the current media/master
> - removed superfluous META capability from capture nodes
> - now the complete UVC payload header is copied to buffers, including
> standard fields
>
> Still open for discussion: is this really OK to always create an
> additional metadata node for each UVC camera or UVC video interface.
>
> IIUC, Laurent's metadata node patch
> https://patchwork.linuxtv.org/patch/36810/ has been acked by Hans and the
> only thing, that's preventing it from being merged it the lack of
> documentation. While waiting for documentation, I'd appreciate some
> discussion of this patch to beat it into shape soon enough and have it
> ready for merge soon after Laurent's patches are pulled in.
>
> Thanks
> Guennadi
>
> drivers/media/usb/uvc/Makefile | 2 +-
> drivers/media/usb/uvc/uvc_driver.c | 10 ++
> drivers/media/usb/uvc/uvc_isight.c | 2 +-
> drivers/media/usb/uvc/uvc_metadata.c | 228 +++++++++++++++++++++++++++++++
> drivers/media/usb/uvc/uvc_video.c | 47 ++++++--
> drivers/media/usb/uvc/uvcvideo.h | 12 +-
> drivers/media/v4l2-core/v4l2-ioctl.c | 1 +
> include/uapi/linux/uvcvideo.h | 10 ++
> include/uapi/linux/videodev2.h | 3 +
> 9 files changed, 304 insertions(+), 11 deletions(-)
> create mode 100644 drivers/media/usb/uvc/uvc_metadata.c
[snip]
> diff --git a/drivers/media/usb/uvc/uvc_driver.c
> b/drivers/media/usb/uvc/uvc_driver.c index 04bf350..edb67ac 100644
> --- a/drivers/media/usb/uvc/uvc_driver.c
> +++ b/drivers/media/usb/uvc/uvc_driver.c
> @@ -1866,6 +1866,9 @@ static void uvc_unregister_video(struct uvc_device
> *dev)
>
> video_unregister_device(&stream->vdev);
>
> + if (video_is_registered(&stream->meta.vdev))
> + video_unregister_device(&stream->meta.vdev);
video_unregister_device() contains a video_is_registered(), no need to
duplicate it.
> +
> uvc_debugfs_cleanup_stream(stream);
> }
>
> @@ -1926,6 +1929,13 @@ static int uvc_register_video(struct uvc_device *dev,
> return ret;
> }
>
> + /*
> + * Register a metadata node. TODO: shall this only be enabled for some
> + * cameras?
> + */
> + if (!(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT))
> + uvc_meta_register(stream);
> +
I think so, only for the cameras that can produce metadata.
> if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE;
> else
[snip]
> diff --git a/drivers/media/usb/uvc/uvc_metadata.c
> b/drivers/media/usb/uvc/uvc_metadata.c new file mode 100644
> index 0000000..ddf77d9
> --- /dev/null
> +++ b/drivers/media/usb/uvc/uvc_metadata.c
> @@ -0,0 +1,228 @@
> +/*
> + * uvc_metadata.c -- USB Video Class driver - Metadata handling
> + *
> + * Copyright (C) 2016
> + * Guennadi Liakhovetski (guennadi.liakhovetski@intel.com)
> + *
> + * This program is free software; you can redistribute it and/or
> modify
> + * it under the terms of the GNU General Public License as published
> by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-v4l2.h>
> +#include <media/videobuf2-vmalloc.h>
> +
> +#include "uvcvideo.h"
> +
> +static inline struct uvc_buffer *to_uvc_buffer(struct vb2_v4l2_buffer
> *vbuf)
> +{
> + return container_of(vbuf, struct uvc_buffer, buf);
> +}
You can move this function to uvcvideo.h and use it in uvc_queue.c (a separate
patch would be best).
> +/* ------------------------------------------------------------------------
> + * videobuf2 Queue Operations
> + */
> +
> +static int meta_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
> + unsigned int *nplanes, unsigned int sizes[],
> + struct device *alloc_devs[])
> +{
> + if (*nplanes) {
> + if (*nplanes != 1)
> + return -EINVAL;
> +
> + if (sizes[0] < UVC_PAYLOAD_HEADER_MAX_SIZE)
> + return -EINVAL;
> +
> + return 0;
> + }
> +
> + *nplanes = 1;
> + sizes[0] = UVC_PAYLOAD_HEADER_MAX_SIZE;
> +
> + return 0;
> +}
> +
> +static int meta_buffer_prepare(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct uvc_buffer *buf = to_uvc_buffer(vbuf);
> +
> + if (vb->num_planes != 1)
> + return -EINVAL;
> +
> + if (vb2_plane_size(vb, 0) < UVC_PAYLOAD_HEADER_MAX_SIZE)
> + return -EINVAL;
> +
> + buf->state = UVC_BUF_STATE_QUEUED;
> + buf->error = 0;
> + buf->mem = vb2_plane_vaddr(vb, 0);
> + buf->length = vb2_plane_size(vb, 0);
> + buf->bytesused = 0;
> +
> + return 0;
> +}
> +
> +static void meta_buffer_queue(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
> + struct uvc_buffer *buf = to_uvc_buffer(vbuf);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&queue->irqlock, flags);
Shouldn't you handle the UVC_QUEUE_DISCONNECTED flag here ?
> + list_add_tail(&buf->queue, &queue->irqqueue);
> + spin_unlock_irqrestore(&queue->irqlock, flags);
> +}
> +
> +static int meta_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> + return 0;
> +}
> +
> +static void meta_stop_streaming(struct vb2_queue *vq)
> +{
> + struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
> + struct uvc_buffer *buffer;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&queue->irqlock, flags);
> +
> + /* Remove all buffers from the IRQ queue. */
> + list_for_each_entry(buffer, &queue->irqqueue, queue)
> + vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR);
> + INIT_LIST_HEAD(&queue->irqqueue);
> +
> + spin_unlock_irqrestore(&queue->irqlock, flags);
> +}
> +
> +static struct vb2_ops uvc_meta_queue_ops = {
> + .queue_setup = meta_queue_setup,
> + .buf_prepare = meta_buffer_prepare,
> + .buf_queue = meta_buffer_queue,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> + .start_streaming = meta_start_streaming,
> + .stop_streaming = meta_stop_streaming,
> +};
How about reusing the uvc_queue.c implementation, with a few new checks for
metadata buffers where needed, instead of duplicating code ? At first sight
the changes don't seem to be too intrusive (but I might have overlooked
something).
> +/* ------------------------------------------------------------------------
> + * V4L2 ioctls
> + */
> +
> +static int meta_v4l2_querycap(struct file *file, void *fh,
> + struct v4l2_capability *cap)
> +{
> + struct v4l2_fh *vfh = file->private_data;
> + struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
> +
> + cap->device_caps = V4L2_CAP_META_CAPTURE
> + | V4L2_CAP_STREAMING;
> + cap->capabilities = V4L2_CAP_DEVICE_CAPS | cap->device_caps
> + | stream->chain->caps;
If you set the device_caps field of struct video_device you won't need the
above four lines.
> + strlcpy(cap->driver, "uvcvideo", sizeof(cap->driver));
> + strlcpy(cap->card, vfh->vdev->name, sizeof(cap->card));
> + usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap-
>bus_info));
> +
> + return 0;
> +}
> +
> +static int meta_v4l2_get_format(struct file *file, void *fh,
> + struct v4l2_format *format)
> +{
> + struct v4l2_fh *vfh = file->private_data;
> + struct v4l2_meta_format *fmt = &format->fmt.meta;
> +
> + if (format->type != vfh->vdev->queue->type)
> + return -EINVAL;
> +
> + memset(fmt, 0, sizeof(*fmt));
> +
> + fmt->dataformat = V4L2_META_FMT_UVC;
> + fmt->buffersize = UVC_PAYLOAD_HEADER_MAX_SIZE;
> +
> + return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops uvc_meta_ioctl_ops = {
> + .vidioc_querycap = meta_v4l2_querycap,
> + .vidioc_g_fmt_meta_cap = meta_v4l2_get_format,
> + .vidioc_s_fmt_meta_cap = meta_v4l2_get_format,
> + .vidioc_try_fmt_meta_cap = meta_v4l2_get_format,
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> +};
> +
> +/* ------------------------------------------------------------------------
> + * V4L2 File Operations
> + */
> +
> +static struct v4l2_file_operations uvc_meta_fops = {
> + .owner = THIS_MODULE,
> + .unlocked_ioctl = video_ioctl2,
> + .open = v4l2_fh_open,
> + .release = vb2_fop_release,
> + .poll = vb2_fop_poll,
> + .mmap = vb2_fop_mmap,
> +};
> +
> +int uvc_meta_register(struct uvc_streaming *stream)
> +{
> + struct uvc_device *dev = stream->dev;
> + struct uvc_meta_dev *meta = &stream->meta;
> + struct video_device *vdev = &meta->vdev;
> + struct uvc_video_queue *quvc = &meta->queue;
> + struct vb2_queue *queue = &quvc->queue;
> + int ret;
> +
> + vdev->v4l2_dev = &dev->vdev;
> + vdev->fops = &uvc_meta_fops;
> + vdev->ioctl_ops = &uvc_meta_ioctl_ops;
> + vdev->release = video_device_release_empty;
> + vdev->prio = &stream->chain->prio;
> + vdev->vfl_dir = VFL_DIR_RX;
> + strlcpy(vdev->name, dev->name, sizeof(vdev->name));
> +
> + video_set_drvdata(vdev, stream);
> +
> + /* Initialize the video buffer queue. */
> + queue->type = V4L2_BUF_TYPE_META_CAPTURE;
> + queue->io_modes = VB2_MMAP | VB2_USERPTR;
> + queue->drv_priv = quvc;
> + queue->buf_struct_size = sizeof(struct uvc_buffer);
> + queue->ops = &uvc_meta_queue_ops;
> + queue->mem_ops = &vb2_vmalloc_memops;
> + queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
> + | V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
> + queue->lock = &quvc->mutex;
> + ret = vb2_queue_init(queue);
> + if (ret < 0)
> + return ret;
> +
> + mutex_init(&quvc->mutex);
> + spin_lock_init(&quvc->irqlock);
> + INIT_LIST_HEAD(&quvc->irqqueue);
> +
> + vdev->queue = queue;
You can set vdev->queue above with the rest of the vdev initialization.
> + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> + if (ret < 0)
> + uvc_printk(KERN_ERR, "Failed to register metadata device (%d).
\n", ret);
> +
> + return ret;
> +}
> diff --git a/drivers/media/usb/uvc/uvc_video.c
> b/drivers/media/usb/uvc/uvc_video.c index b5589d5..1bda8e1 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -1158,8 +1158,30 @@ static void uvc_video_validate_buffer(const struct
> uvc_streaming *stream, /*
> * Completion handler for video URBs.
> */
> +static void uvc_video_decode_meta(struct uvc_streaming *stream,
> + struct uvc_buffer *buf, struct uvc_buffer *meta_buf,
> + u8 *mem, int length)
I don't think length can be negative, you can make it an unsigned int and
replace min_t with min below.
> +{
> + size_t nbytes;
> +
> + if (!meta_buf || !length)
> + return;
> +
> + nbytes = min_t(unsigned int, length, meta_buf->length);
> +
> + meta_buf->buf.sequence = buf->buf.sequence;
> + meta_buf->buf.field = buf->buf.field;
> + meta_buf->buf.vb2_buf.timestamp = buf->buf.vb2_buf.timestamp;
> +
> + memcpy(meta_buf->mem, mem, nbytes);
Do you need the whole header ? Shouldn't you strip the part already handled by
the driver ?
> + meta_buf->bytesused = nbytes;
> + meta_buf->state = UVC_BUF_STATE_READY;
> +
> + uvc_queue_next_buffer(&stream->meta.queue, meta_buf);
This essentially means that you'll need one buffer per isochronous packet.
Given the number of isochronous packets that make a complete image the cost
seem prohibitive to me. You should instead gather metadata from all headers
into a single buffer.
> +}
> +
> static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming
> *stream,
> - struct uvc_buffer *buf)
> + struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
> {
> u8 *mem;
> int ret, i;
> @@ -1189,6 +1211,8 @@ static void uvc_video_decode_isoc(struct urb *urb,
> struct uvc_streaming *stream, if (ret < 0)
> continue;
>
> + uvc_video_decode_meta(stream, buf, meta_buf, mem, ret);
> +
> /* Decode the payload data. */
> uvc_video_decode_data(stream, buf, mem + ret,
> urb->iso_frame_desc[i].actual_length - ret);
> @@ -1205,7 +1229,7 @@ static void uvc_video_decode_isoc(struct urb *urb,
> struct uvc_streaming *stream, }
>
> static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming
> *stream,
> - struct uvc_buffer *buf)
> + struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
> {
> u8 *mem;
> int len, ret;
> @@ -1239,6 +1263,8 @@ static void uvc_video_decode_bulk(struct urb *urb,
> struct uvc_streaming *stream, memcpy(stream->bulk.header, mem, ret);
> stream->bulk.header_size = ret;
>
> + uvc_video_decode_meta(stream, buf, meta_buf, mem,
ret);
> +
> mem += ret;
> len -= ret;
> }
> @@ -1262,8 +1288,7 @@ static void uvc_video_decode_bulk(struct urb *urb,
> struct uvc_streaming *stream, uvc_video_decode_end(stream, buf,
> stream->bulk.header,
> stream->bulk.payload_size);
> if (buf->state == UVC_BUF_STATE_READY)
> - buf = uvc_queue_next_buffer(&stream->queue,
> - buf);
> + uvc_queue_next_buffer(&stream->queue, buf);
This could be split to a separate patch.
> }
>
> stream->bulk.header_size = 0;
> @@ -1273,7 +1298,7 @@ static void uvc_video_decode_bulk(struct urb *urb,
> struct uvc_streaming *stream, }
>
> static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming
> *stream,
> - struct uvc_buffer *buf)
> + struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
> {
> u8 *mem = urb->transfer_buffer;
> int len = stream->urb_size, ret;
> @@ -1319,7 +1344,8 @@ static void uvc_video_complete(struct urb *urb)
> {
> struct uvc_streaming *stream = urb->context;
> struct uvc_video_queue *queue = &stream->queue;
> - struct uvc_buffer *buf = NULL;
> + struct uvc_video_queue *qmeta = &stream->meta.queue;
> + struct uvc_buffer *buf = NULL, *buf_meta = NULL;
One variable per line please.
> unsigned long flags;
> int ret;
>
> @@ -1338,6 +1364,7 @@ static void uvc_video_complete(struct urb *urb)
> case -ECONNRESET: /* usb_unlink_urb() called. */
> case -ESHUTDOWN: /* The endpoint is being disabled. */
> uvc_queue_cancel(queue, urb->status == -ESHUTDOWN);
> + uvc_queue_cancel(qmeta, urb->status == -ESHUTDOWN);
> return;
> }
>
> @@ -1347,7 +1374,13 @@ static void uvc_video_complete(struct urb *urb)
> queue);
> spin_unlock_irqrestore(&queue->irqlock, flags);
>
> - stream->decode(urb, stream, buf);
> + spin_lock_irqsave(&qmeta->irqlock, flags);
> + if (!list_empty(&qmeta->irqqueue))
> + buf_meta = list_first_entry(&qmeta->irqqueue, struct
uvc_buffer,
> + queue);
> + spin_unlock_irqrestore(&qmeta->irqlock, flags);
> +
> + stream->decode(urb, stream, buf, buf_meta);
>
> if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
> uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
> diff --git a/drivers/media/usb/uvc/uvcvideo.h
> b/drivers/media/usb/uvc/uvcvideo.h index 3d6cc62..ebff4b6 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -464,6 +464,11 @@ struct uvc_stats_stream {
> unsigned int max_sof; /* Maximum STC.SOF value */
> };
>
> +struct uvc_meta_dev {
All other structures use _device, not _dev. Let's be consistent.
> + struct video_device vdev;
> + struct uvc_video_queue queue;
> +};
> +
> struct uvc_streaming {
> struct list_head list;
> struct uvc_device *dev;
> @@ -495,7 +500,9 @@ struct uvc_streaming {
> unsigned int frozen : 1;
> struct uvc_video_queue queue;
> void (*decode) (struct urb *urb, struct uvc_streaming *video,
> - struct uvc_buffer *buf);
> + struct uvc_buffer *buf, struct uvc_buffer *meta_buf);
> +
> + struct uvc_meta_dev meta;
>
> /* Context data used by the bulk completion handler. */
> struct {
> @@ -700,6 +707,7 @@ extern int uvc_query_ctrl(struct uvc_device *dev, __u8
> query, __u8 unit, void uvc_video_clock_update(struct uvc_streaming *stream,
> struct vb2_v4l2_buffer *vbuf,
> struct uvc_buffer *buf);
> +int uvc_meta_register(struct uvc_streaming *stream);
>
> /* Status */
> extern int uvc_status_init(struct uvc_device *dev);
> @@ -754,7 +762,7 @@ extern struct usb_host_endpoint *uvc_find_endpoint(
>
> /* Quirks support */
> void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
> - struct uvc_buffer *buf);
> + struct uvc_buffer *buf, struct uvc_buffer *meta_buf);
>
> /* debugfs and statistics */
> int uvc_debugfs_init(void);
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c
> b/drivers/media/v4l2-core/v4l2-ioctl.c index 44a29af..1618be4 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -1232,6 +1232,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
> case V4L2_TCH_FMT_DELTA_TD08: descr = "8-bit signed deltas"; break; case
> V4L2_TCH_FMT_TU16: descr = "16-bit unsigned touch data"; break;
case
> V4L2_TCH_FMT_TU08: descr = "8-bit unsigned touch data"; break;
> + case
> V4L2_META_FMT_UVC: descr = "UVC payload header metadata"; break;
>
> default:
> /* Compressed formats */
> diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h
> index 3b08186..e98de14 100644
> --- a/include/uapi/linux/uvcvideo.h
> +++ b/include/uapi/linux/uvcvideo.h
> @@ -67,4 +67,14 @@ struct uvc_xu_control_query {
> #define UVCIOC_CTRL_MAP _IOWR('u', 0x20, struct
uvc_xu_control_mapping)
> #define UVCIOC_CTRL_QUERY _IOWR('u', 0x21, struct uvc_xu_control_query)
>
> +/*
> + * Metadata node
> + */
> +
> +/*
> + * Actually 255 bytes, but 256 is just a nicer number. We keep the buffer
> size
> + * constant and just set .usedbytes accordingly
> + */
> +#define UVC_PAYLOAD_HEADER_MAX_SIZE 256
Why is lying about the size better ?
> #endif
--
Regards,
Laurent Pinchart
next prev parent reply other threads:[~2016-12-05 10:53 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-06-24 11:28 [PATCH 0/3] uvcvideo: a cosmetic fix and 2 new features Guennadi Liakhovetski
2016-06-24 11:28 ` [PATCH 1/3] uvcvideo: initialise the entity function field Guennadi Liakhovetski
2016-06-24 13:26 ` Laurent Pinchart
2016-06-24 11:29 ` [PATCH 2/3] uvcvideo: send a control event when a Control Change interrupt arrives Guennadi Liakhovetski
2016-06-24 11:29 ` [PATCH 3/3] uvcvideo: add a metadata device node Guennadi Liakhovetski
2016-06-24 12:46 ` kbuild test robot
2016-06-24 13:12 ` kbuild test robot
2016-12-02 10:53 ` [PATCH v2 " Guennadi Liakhovetski
2016-12-05 10:53 ` Laurent Pinchart [this message]
2016-12-05 15:35 ` Guennadi Liakhovetski
2016-12-05 22:06 ` Laurent Pinchart
2016-12-05 22:13 ` Guennadi Liakhovetski
2016-12-05 22:25 ` Laurent Pinchart
2016-12-06 10:39 ` Guennadi Liakhovetski
2016-12-06 15:56 ` Laurent Pinchart
2016-12-08 13:34 ` Guennadi Liakhovetski
2016-12-08 13:39 ` Laurent Pinchart
2016-12-08 15:18 ` Guennadi Liakhovetski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2361420.98YnhAaLcS@avalon \
--to=laurent.pinchart@ideasonboard.com \
--cc=g.liakhovetski@gmx.de \
--cc=hverkuil@xs4all.nl \
--cc=linux-media@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.