OSDN Git Service

media: cxusb: add raw mode support for Medion MD95700
authorMaciej S. Szmigiero <mail@maciej.szmigiero.name>
Mon, 29 Apr 2019 16:16:59 +0000 (12:16 -0400)
committerMauro Carvalho Chehab <mchehab+samsung@kernel.org>
Tue, 28 May 2019 20:04:44 +0000 (16:04 -0400)
This adds raw (unprocessed) BT.656 stream capturing support for the analog
part of Medion 95700.
It can be enabled by setting CXUSB_EXTENDEDMODE_CAPTURE_RAW flag in
parm.capture.extendedmode passed to VIDIOC_S_PARM.

Signed-off-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
drivers/media/usb/dvb-usb/cxusb-analog.c
drivers/media/usb/dvb-usb/cxusb.h
drivers/media/v4l2-core/v4l2-ioctl.c

index 8df6c33..68e0973 100644 (file)
@@ -44,7 +44,9 @@ static int cxusb_medion_v_queue_setup(struct vb2_queue *q,
 {
        struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q);
        struct cxusb_medion_dev *cxdev = dvbdev->priv;
-       unsigned int size = cxdev->width * cxdev->height * 2;
+       unsigned int size = cxdev->raw_mode ?
+               CXUSB_VIDEO_MAX_FRAME_SIZE :
+               cxdev->width * cxdev->height * 2;
 
        if (*num_planes > 0) {
                if (*num_planes != 1)
@@ -67,8 +69,13 @@ static int cxusb_medion_v_buf_init(struct vb2_buffer *vb)
 
        cxusb_vprintk(dvbdev, OPS, "buffer init\n");
 
-       if (vb2_plane_size(vb, 0) < cxdev->width * cxdev->height * 2)
-               return -ENOMEM;
+       if (cxdev->raw_mode) {
+               if (vb2_plane_size(vb, 0) < CXUSB_VIDEO_MAX_FRAME_SIZE)
+                       return -ENOMEM;
+       } else {
+               if (vb2_plane_size(vb, 0) < cxdev->width * cxdev->height * 2)
+                       return -ENOMEM;
+       }
 
        cxusb_vprintk(dvbdev, OPS, "buffer OK\n");
 
@@ -442,6 +449,45 @@ static bool cxusb_medion_copy_field(struct dvb_usb_device *dvbdev,
        return true;
 }
 
+static void cxusb_medion_v_process_urb_raw(struct cxusb_medion_dev *cxdev,
+                                          struct urb *urb)
+{
+       struct dvb_usb_device *dvbdev = cxdev->dvbdev;
+       u8 *buf;
+       struct cxusb_medion_vbuffer *vbuf;
+       int i;
+       unsigned long len;
+
+       if (list_empty(&cxdev->buflist)) {
+               dev_warn(&dvbdev->udev->dev, "no free buffers\n");
+               cxdev->vbuf_sequence++;
+               return;
+       }
+
+       vbuf = list_first_entry(&cxdev->buflist, struct cxusb_medion_vbuffer,
+                               list);
+       list_del(&vbuf->list);
+
+       vbuf->vb2.field = V4L2_FIELD_NONE;
+       vbuf->vb2.sequence = cxdev->vbuf_sequence++;
+       vbuf->vb2.vb2_buf.timestamp = ktime_get_ns();
+
+       buf = vb2_plane_vaddr(&vbuf->vb2.vb2_buf, 0);
+
+       for (i = 0, len = 0; i < urb->number_of_packets; i++) {
+               memcpy(buf, urb->transfer_buffer +
+                      urb->iso_frame_desc[i].offset,
+                      urb->iso_frame_desc[i].actual_length);
+
+               buf += urb->iso_frame_desc[i].actual_length;
+               len += urb->iso_frame_desc[i].actual_length;
+       }
+
+       vb2_set_plane_payload(&vbuf->vb2.vb2_buf, 0, len);
+
+       vb2_buffer_done(&vbuf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
 static bool cxusb_medion_v_process_auxbuf(struct cxusb_medion_dev *cxdev,
                                          bool reset)
 {
@@ -563,22 +609,26 @@ static bool cxusb_medion_v_complete_handle_urb(struct cxusb_medion_dev *cxdev,
                              len);
 
                if (len > 0) {
-                       cxusb_vprintk(dvbdev, URB, "appending URB\n");
-
-                       /*
-                        * append new data to auxbuf while
-                        * overwriting old data if necessary
-                        *
-                        * if any overwrite happens then we can no
-                        * longer rely on consistency of the whole
-                        * data so let's start again the current
-                        * auxbuf frame assembling process from
-                        * the beginning
-                        */
-                       *auxbuf_reset =
-                               !cxusb_auxbuf_append_urb(dvbdev,
-                                                        &cxdev->auxbuf,
-                                                        urb);
+                       if (cxdev->raw_mode)
+                               cxusb_medion_v_process_urb_raw(cxdev, urb);
+                       else {
+                               cxusb_vprintk(dvbdev, URB, "appending URB\n");
+
+                               /*
+                                * append new data to auxbuf while
+                                * overwriting old data if necessary
+                                *
+                                * if any overwrite happens then we can no
+                                * longer rely on consistency of the whole
+                                * data so let's start again the current
+                                * auxbuf frame assembling process from
+                                * the beginning
+                                */
+                               *auxbuf_reset =
+                                       !cxusb_auxbuf_append_urb(dvbdev,
+                                                                &cxdev->auxbuf,
+                                                                urb);
+                       }
                }
        }
 
@@ -613,7 +663,8 @@ static void cxusb_medion_v_complete_work(struct work_struct *work)
 
        reschedule = cxusb_medion_v_complete_handle_urb(cxdev, &auxbuf_reset);
 
-       if (cxusb_medion_v_process_auxbuf(cxdev, auxbuf_reset))
+       if (!cxdev->raw_mode && cxusb_medion_v_process_auxbuf(cxdev,
+                                                             auxbuf_reset))
                /* reschedule us until auxbuf no longer can produce any frame */
                reschedule = true;
 
@@ -802,9 +853,13 @@ static int cxusb_medion_v_start_streaming(struct vb2_queue *q,
                goto ret_unstream_cx;
        }
 
-       ret = cxusb_medion_v_ss_auxbuf_alloc(cxdev, &npackets);
-       if (ret != 0)
-               goto ret_unstream_md;
+       if (cxdev->raw_mode)
+               npackets = CXUSB_VIDEO_MAX_FRAME_PKTS;
+       else {
+               ret = cxusb_medion_v_ss_auxbuf_alloc(cxdev, &npackets);
+               if (ret != 0)
+                       goto ret_unstream_md;
+       }
 
        for (i = 0; i < CXUSB_VIDEO_URBS; i++) {
                int framen;
@@ -860,9 +915,11 @@ static int cxusb_medion_v_start_streaming(struct vb2_queue *q,
        cxdev->nexturb = 0;
        cxdev->vbuf_sequence = 0;
 
-       cxdev->vbuf = NULL;
-       cxdev->bt656.mode = NEW_FRAME;
-       cxdev->bt656.buf = NULL;
+       if (!cxdev->raw_mode) {
+               cxdev->vbuf = NULL;
+               cxdev->bt656.mode = NEW_FRAME;
+               cxdev->bt656.buf = NULL;
+       }
 
        for (i = 0; i < CXUSB_VIDEO_URBS; i++)
                if (cxdev->streamurbs[i] != NULL) {
@@ -880,7 +937,8 @@ ret_freeu:
        cxusb_medion_urbs_free(cxdev);
 
 ret_freeab:
-       vfree(cxdev->auxbuf.buf);
+       if (!cxdev->raw_mode)
+               vfree(cxdev->auxbuf.buf);
 
 ret_unstream_md:
        cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0);
@@ -927,7 +985,8 @@ static void cxusb_medion_v_stop_streaming(struct vb2_queue *q)
        mutex_lock(cxdev->videodev->lock);
 
        /* free transfer buffer and URB */
-       vfree(cxdev->auxbuf.buf);
+       if (!cxdev->raw_mode)
+               vfree(cxdev->auxbuf.buf);
 
        cxusb_medion_urbs_free(cxdev);
 
@@ -1000,9 +1059,11 @@ static int cxusb_medion_g_fmt_vid_cap(struct file *file, void *fh,
        f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
        f->fmt.pix.field = vb2_start_streaming_called(&cxdev->videoqueue) ?
                cxdev->field_order : cxusb_medion_field_order(cxdev);
-       f->fmt.pix.bytesperline = cxdev->width * 2;
+       f->fmt.pix.bytesperline = cxdev->raw_mode ? 0 : cxdev->width * 2;
        f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-       f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+       f->fmt.pix.sizeimage =
+               cxdev->raw_mode ? CXUSB_VIDEO_MAX_FRAME_SIZE :
+               f->fmt.pix.bytesperline * f->fmt.pix.height;
 
        return 0;
 }
@@ -1040,8 +1101,10 @@ static int cxusb_medion_try_s_fmt_vid_cap(struct file *file,
        f->fmt.pix.height = subfmt.format.height;
        f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
        f->fmt.pix.field = field;
-       f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
-       f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+       f->fmt.pix.bytesperline = cxdev->raw_mode ? 0 : f->fmt.pix.width * 2;
+       f->fmt.pix.sizeimage =
+               cxdev->raw_mode ? CXUSB_VIDEO_MAX_FRAME_SIZE :
+               f->fmt.pix.bytesperline * f->fmt.pix.height;
        f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
 
        if (isset) {
@@ -1397,6 +1460,67 @@ static int cxusb_medion_querystd(struct file *file, void *fh,
        return 0;
 }
 
+static int cxusb_medion_g_s_parm(struct file *file, void *fh,
+                                struct v4l2_streamparm *param)
+{
+       v4l2_std_id std;
+
+       if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       param->parm.capture.readbuffers = 2;
+
+       if (cxusb_medion_g_std(file, fh, &std) == 0)
+               v4l2_video_std_frame_period(std,
+                                           &param->parm.capture.timeperframe);
+
+       return 0;
+}
+
+static int cxusb_medion_g_parm(struct file *file, void *fh,
+                              struct v4l2_streamparm *param)
+{
+       struct dvb_usb_device *dvbdev = video_drvdata(file);
+       struct cxusb_medion_dev *cxdev = dvbdev->priv;
+       int ret;
+
+       ret = cxusb_medion_g_s_parm(file, fh, param);
+       if (ret != 0)
+               return ret;
+
+       if (cxdev->raw_mode)
+               param->parm.capture.extendedmode |=
+                       CXUSB_EXTENDEDMODE_CAPTURE_RAW;
+
+       return 0;
+}
+
+static int cxusb_medion_s_parm(struct file *file, void *fh,
+                              struct v4l2_streamparm *param)
+{
+       struct dvb_usb_device *dvbdev = video_drvdata(file);
+       struct cxusb_medion_dev *cxdev = dvbdev->priv;
+       int ret;
+       bool want_raw;
+
+       ret = cxusb_medion_g_s_parm(file, fh, param);
+       if (ret != 0)
+               return ret;
+
+       want_raw = param->parm.capture.extendedmode &
+               CXUSB_EXTENDEDMODE_CAPTURE_RAW;
+
+       if (want_raw != cxdev->raw_mode) {
+               if (vb2_start_streaming_called(&cxdev->videoqueue) ||
+                   cxdev->stop_streaming)
+                       return -EBUSY;
+
+               cxdev->raw_mode = want_raw;
+       }
+
+       return 0;
+}
+
 static int cxusb_medion_log_status(struct file *file, void *fh)
 {
        struct dvb_usb_device *dvbdev = video_drvdata(file);
@@ -1416,6 +1540,8 @@ static const struct v4l2_ioctl_ops cxusb_video_ioctl = {
        .vidioc_enum_input = cxusb_medion_enum_input,
        .vidioc_g_input = cxusb_medion_g_input,
        .vidioc_s_input = cxusb_medion_s_input,
+       .vidioc_g_parm = cxusb_medion_g_parm,
+       .vidioc_s_parm = cxusb_medion_s_parm,
        .vidioc_g_tuner = cxusb_medion_g_tuner,
        .vidioc_s_tuner = cxusb_medion_s_tuner,
        .vidioc_g_frequency = cxusb_medion_g_frequency,
index 9636f95..35e72f5 100644 (file)
@@ -131,6 +131,7 @@ struct cxusb_medion_dev {
        bool stop_streaming;
        u32 width, height;
        u32 field_order;
+       bool raw_mode;
        struct cxusb_medion_auxbuf auxbuf;
        v4l2_std_id norm;
 
@@ -154,6 +155,9 @@ struct cxusb_medion_vbuffer {
        struct list_head list;
 };
 
+/* Capture streaming parameters extendedmode field flags */
+#define CXUSB_EXTENDEDMODE_CAPTURE_RAW 1
+
 /* defines for "debug" module parameter */
 #define CXUSB_DBG_RC BIT(0)
 #define CXUSB_DBG_I2C BIT(1)
index ac87c3e..0b9ca5a 100644 (file)
@@ -2023,7 +2023,7 @@ static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
        if (ret)
                return ret;
 
-       /* Note: extendedmode is never used in drivers */
+       /* Note: extendedmode is never used in output drivers */
        if (V4L2_TYPE_IS_OUTPUT(p->type)) {
                memset(p->parm.output.reserved, 0,
                       sizeof(p->parm.output.reserved));
@@ -2032,7 +2032,6 @@ static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
        } else {
                memset(p->parm.capture.reserved, 0,
                       sizeof(p->parm.capture.reserved));
-               p->parm.capture.extendedmode = 0;
                p->parm.capture.capturemode &= V4L2_MODE_HIGHQUALITY;
        }
        return ops->vidioc_s_parm(file, fh, p);