@@ -45,6 +45,7 @@ struct rcar_vin {
struct platform_device *pdev;
struct v4l2_device v4l2_dev;
+ struct video_device vdev;
struct mutex lock;
struct v4l2_pix_format format;
@@ -240,12 +241,154 @@ const struct rcar_vin_format *rcar_vin_get_format_by_fourcc(u32 fourcc)
}
/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int rcar_vin_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct rcar_vin *vin = video_drvdata(file);
+
+ strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(&vin->pdev->dev));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int rcar_vin_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_vin *vin = video_drvdata(file);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ const struct rcar_vin_format *format;
+
+ /* Only single-plane capture is supported so far */
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ format = rcar_vin_get_format_by_fourcc(pix->pixelformat);
+ if (IS_ERR(format))
+ return -EINVAL;
+
+ /* TODO */
+
+ return 0;
+
+}
+
+static int rcar_vin_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_vin *vin = video_drvdata(file);
+ int ret;
+
+ ret = rcar_vin_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ if (vb2_is_busy(&vin->queue))
+ return -EBUSY;
+
+ /* TODO: change format */
+ vin->format = f->fmt.pix;
+
+ return 0;
+}
+
+static int rcar_vin_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_vin *vin = video_drvdata(file);
+
+ f->fmt.pix = vin->format;
+ return 0;
+}
+
+static int rcar_vin_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(rcar_vin_formats))
+ return -EINVAL;
+
+ f->pixelformat = rcar_vin_formats[f->index].fourcc;
+ strlcpy(f->description, rcar_vin_formats[f->index].description,
+ sizeof(f->description));
+ return 0;
+}
+
+static int rcar_vin_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strlcpy(i->name, "Camera", sizeof(i->name));
+
+ return 0;
+}
+
+static int rcar_vin_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int rcar_vin_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops rcar_vin_ioctl_ops = {
+ .vidioc_querycap = rcar_vin_querycap,
+ .vidioc_try_fmt_vid_cap = rcar_vin_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = rcar_vin_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = rcar_vin_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = rcar_vin_enum_fmt_vid_cap,
+
+ .vidioc_enum_input = rcar_vin_enum_input,
+ .vidioc_g_input = rcar_vin_g_input,
+ .vidioc_s_input = rcar_vin_s_input,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations rcar_vin_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+
+/* -----------------------------------------------------------------------------
* Platform Device Driver
*/
static int rcar_vin_probe(struct platform_device *pdev)
{
struct rcar_vin *vin;
+ struct video_device *vdev;
struct vb2_queue *q;
struct resource *mem;
int irq, ret;
@@ -320,12 +463,30 @@ static int rcar_vin_probe(struct platform_device *pdev)
spin_lock_init(&vin->qlock);
/* Initialize the video_device structure */
- /* TODO */
+ vdev = &vin->vdev;
+ strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
+
+ /* FIXME: is empty releas OK */
+ vdev->release = video_device_release_empty;
+ vdev->fops = &rcar_vin_fops;
+ vdev->ioctl_ops = &rcar_vin_ioctl_ops;
+ vdev->lock = &vin->lock;
+ vdev->queue = q;
+ vdev->v4l2_dev = &vin->v4l2_dev;
+ video_set_drvdata(vdev, vin);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto free_ctx;
platform_set_drvdata(pdev, vin);
+ v4l2_info(&vin->v4l2_dev, "Device registered as /dev/video%d\n",
+ vdev->num);
return 0;
+free_ctx:
+ vb2_dma_contig_cleanup_ctx(vin->alloc_ctx);
free_hdl:
v4l2_device_unregister(&vin->v4l2_dev);
disable_dev:
@@ -336,6 +497,10 @@ static int rcar_vin_remove(struct platform_device *pdev)
{
struct rcar_vin *vin = platform_get_drvdata(pdev);
+ v4l2_info(&vin->v4l2_dev, "Removing /dev/video%d\n", vin->vdev.num);
+
+ video_unregister_device(&vin->vdev);
+
vb2_dma_contig_cleanup_ctx(vin->alloc_ctx);
v4l2_device_unregister(&vin->v4l2_dev);
return 0;