On Wed 10 July 2013 12:19:32 Laurent Pinchart wrote: > The VSP1 is a video processing engine that includes a blender, scalers, > filters and statistics computation. Configurable data path routing logic > allows ordering the internal blocks in a flexible way. > > Due to the configurable nature of the pipeline the driver implements the > media controller API and doesn't use the V4L2 mem-to-mem framework, even > though the device usually operates in memory to memory mode. > > Only the read pixel formatters, up/down scalers, write pixel formatters > and LCDC interface are supported at this stage. > > Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@xxxxxxxxxxxxxxxx> > --- > drivers/media/platform/Kconfig | 10 + > drivers/media/platform/Makefile | 2 + > drivers/media/platform/vsp1/Makefile | 5 + > drivers/media/platform/vsp1/vsp1.h | 73 ++ > drivers/media/platform/vsp1/vsp1_drv.c | 475 ++++++++++++ > drivers/media/platform/vsp1/vsp1_entity.c | 186 +++++ > drivers/media/platform/vsp1/vsp1_entity.h | 68 ++ > drivers/media/platform/vsp1/vsp1_lif.c | 237 ++++++ > drivers/media/platform/vsp1/vsp1_lif.h | 38 + > drivers/media/platform/vsp1/vsp1_regs.h | 581 +++++++++++++++ > drivers/media/platform/vsp1/vsp1_rpf.c | 209 ++++++ > drivers/media/platform/vsp1/vsp1_rwpf.c | 124 ++++ > drivers/media/platform/vsp1/vsp1_rwpf.h | 56 ++ > drivers/media/platform/vsp1/vsp1_uds.c | 346 +++++++++ > drivers/media/platform/vsp1/vsp1_uds.h | 41 + > drivers/media/platform/vsp1/vsp1_video.c | 1154 +++++++++++++++++++++++++++++ > drivers/media/platform/vsp1/vsp1_video.h | 144 ++++ > drivers/media/platform/vsp1/vsp1_wpf.c | 233 ++++++ > include/linux/platform_data/vsp1.h | 25 + > 19 files changed, 4007 insertions(+) > create mode 100644 drivers/media/platform/vsp1/Makefile > create mode 100644 drivers/media/platform/vsp1/vsp1.h > create mode 100644 drivers/media/platform/vsp1/vsp1_drv.c > create mode 100644 drivers/media/platform/vsp1/vsp1_entity.c > create mode 100644 drivers/media/platform/vsp1/vsp1_entity.h > create mode 100644 drivers/media/platform/vsp1/vsp1_lif.c > create mode 100644 drivers/media/platform/vsp1/vsp1_lif.h > create mode 100644 drivers/media/platform/vsp1/vsp1_regs.h > create mode 100644 drivers/media/platform/vsp1/vsp1_rpf.c > create mode 100644 drivers/media/platform/vsp1/vsp1_rwpf.c > create mode 100644 drivers/media/platform/vsp1/vsp1_rwpf.h > create mode 100644 drivers/media/platform/vsp1/vsp1_uds.c > create mode 100644 drivers/media/platform/vsp1/vsp1_uds.h > create mode 100644 drivers/media/platform/vsp1/vsp1_video.c > create mode 100644 drivers/media/platform/vsp1/vsp1_video.h > create mode 100644 drivers/media/platform/vsp1/vsp1_wpf.c > create mode 100644 include/linux/platform_data/vsp1.h > Hi Laurent, It took some effort, but I finally did find some things to complain about :-) > diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c > new file mode 100644 > index 0000000..47a739a > --- /dev/null > +++ b/drivers/media/platform/vsp1/vsp1_video.c ... > +/* ----------------------------------------------------------------------------- > + * videobuf2 Queue Operations > + */ > + > +static int > +vsp1_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, > + unsigned int *nbuffers, unsigned int *nplanes, > + unsigned int sizes[], void *alloc_ctxs[]) > +{ > + struct vsp1_video *video = vb2_get_drv_priv(vq); > + struct v4l2_pix_format_mplane *format = &video->format; > + unsigned int i; > + > + *nplanes = format->num_planes; > + > + for (i = 0; i < format->num_planes; ++i) { > + sizes[i] = format->plane_fmt[i].sizeimage; > + alloc_ctxs[i] = video->alloc_ctx; > + } > + > + return 0; > +} > + > +static int vsp1_video_buffer_prepare(struct vb2_buffer *vb) > +{ > + struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); > + struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb); > + unsigned int i; > + > + buf->video = video; > + > + for (i = 0; i < vb->num_planes; ++i) { > + buf->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); > + buf->length[i] = vb2_plane_size(vb, i); > + } > + > + return 0; > +} > + > +static void vsp1_video_buffer_queue(struct vb2_buffer *vb) > +{ > + struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); > + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); > + struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb); > + unsigned long flags; > + bool empty; > + > + spin_lock_irqsave(&video->irqlock, flags); > + empty = list_empty(&video->irqqueue); > + list_add_tail(&buf->queue, &video->irqqueue); > + spin_unlock_irqrestore(&video->irqlock, flags); > + > + if (empty) { > + spin_lock_irqsave(&pipe->irqlock, flags); > + > + video->ops->queue(video, buf); > + pipe->buffers_ready |= 1 << video->pipe_index; > + > + if (vb2_is_streaming(&video->queue) && > + vsp1_pipeline_ready(pipe)) > + vsp1_pipeline_run(pipe); > + > + spin_unlock_irqrestore(&pipe->irqlock, flags); > + } > +} > + > +static void vsp1_video_wait_prepare(struct vb2_queue *vq) > +{ > + struct vsp1_video *video = vb2_get_drv_priv(vq); > + > + mutex_unlock(&video->lock); > +} > + > +static void vsp1_video_wait_finish(struct vb2_queue *vq) > +{ > + struct vsp1_video *video = vb2_get_drv_priv(vq); > + > + mutex_lock(&video->lock); > +} > + > +static void vsp1_entity_route_setup(struct vsp1_entity *source) > +{ > + struct vsp1_entity *sink; > + > + if (source->route == 0) > + return; > + > + sink = container_of(source->sink, struct vsp1_entity, subdev.entity); > + vsp1_write(source->vsp1, source->route, sink->id); > +} > + > +static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) > +{ > + struct vsp1_video *video = vb2_get_drv_priv(vq); > + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); > + struct vsp1_entity *entity; > + unsigned long flags; > + int ret; > + > + mutex_lock(&pipe->lock); > + if (pipe->stream_count == pipe->num_video - 1) { > + list_for_each_entry(entity, &pipe->entities, list_pipe) { > + vsp1_entity_route_setup(entity); > + > + ret = v4l2_subdev_call(&entity->subdev, video, > + s_stream, 1); > + if (ret < 0) { > + mutex_unlock(&pipe->lock); > + return ret; > + } > + } > + } > + > + pipe->stream_count++; > + mutex_unlock(&pipe->lock); > + > + spin_lock_irqsave(&pipe->irqlock, flags); > + if (vsp1_pipeline_ready(pipe)) > + vsp1_pipeline_run(pipe); > + spin_unlock_irqrestore(&pipe->irqlock, flags); > + > + return 0; > +} > + > +static int vsp1_video_stop_streaming(struct vb2_queue *vq) > +{ > + struct vsp1_video *video = vb2_get_drv_priv(vq); > + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); > + unsigned long flags; > + int ret; > + > + mutex_lock(&pipe->lock); > + if (--pipe->stream_count == 0) { > + /* Stop the pipeline. */ > + ret = vsp1_pipeline_stop(pipe); > + if (ret == -ETIMEDOUT) > + dev_err(video->vsp1->dev, "pipeline stop timeout\n"); > + } > + mutex_unlock(&pipe->lock); > + > + vsp1_pipeline_cleanup(pipe); > + media_entity_pipeline_stop(&video->video.entity); > + > + /* Remove all buffers from the IRQ queue. */ > + spin_lock_irqsave(&video->irqlock, flags); > + INIT_LIST_HEAD(&video->irqqueue); > + spin_unlock_irqrestore(&video->irqlock, flags); > + > + return 0; > +} > + > +static struct vb2_ops vsp1_video_queue_qops = { > + .queue_setup = vsp1_video_queue_setup, > + .buf_prepare = vsp1_video_buffer_prepare, > + .buf_queue = vsp1_video_buffer_queue, > + .wait_prepare = vsp1_video_wait_prepare, > + .wait_finish = vsp1_video_wait_finish, > + .start_streaming = vsp1_video_start_streaming, > + .stop_streaming = vsp1_video_stop_streaming, > +}; > + > +/* ----------------------------------------------------------------------------- > + * V4L2 ioctls > + */ > + > +static int > +vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + > + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) > + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE > + | V4L2_CAP_STREAMING; > + else > + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT_MPLANE > + | V4L2_CAP_STREAMING; cap->device_caps should be filled in here as well. > + > + strlcpy(cap->driver, "vsp1", sizeof(cap->driver)); > + strlcpy(cap->card, video->video.name, sizeof(cap->card)); > + strlcpy(cap->bus_info, "media", sizeof(cap->bus_info)); The bus_info for platform devices should be prefixed with "platform:" as per the spec. > + > + return 0; > +} > + > +static int > +vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + > + if (format->type != video->queue.type) > + return -EINVAL; > + > + mutex_lock(&video->lock); > + format->fmt.pix_mp = video->format; > + mutex_unlock(&video->lock); > + > + return 0; > +} > + > +static int __vsp1_video_try_format(struct vsp1_video *video, > + struct v4l2_pix_format_mplane *pix, > + const struct vsp1_format_info **fmtinfo) > +{ > + const struct vsp1_format_info *info; > + unsigned int width = pix->width; > + unsigned int height = pix->height; > + unsigned int i; > + > + /* Retrieve format information and select the default format if the > + * requested format isn't supported. > + */ > + info = vsp1_get_format_info(pix->pixelformat); > + if (info == NULL) > + info = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT); > + > + pix->pixelformat = info->fourcc; > + pix->colorspace = V4L2_COLORSPACE_SRGB; > + pix->field = V4L2_FIELD_NONE; pix->priv should be set to 0. v4l2-compliance catches such errors, BTW. > + > + /* Align the width and height for YUV 4:2:2 and 4:2:0 formats. */ > + width = round_down(width, info->hsub); > + height = round_down(height, info->vsub); > + > + /* Clamp the width and height. */ > + pix->width = clamp(width, VSP1_VIDEO_MIN_WIDTH, VSP1_VIDEO_MAX_WIDTH); > + pix->height = clamp(height, VSP1_VIDEO_MIN_HEIGHT, > + VSP1_VIDEO_MAX_HEIGHT); > + > + /* Compute and clamp the stride and image size. */ > + for (i = 0; i < max(info->planes, 2U); ++i) { > + unsigned int hsub = i > 0 ? info->hsub : 1; > + unsigned int vsub = i > 0 ? info->vsub : 1; > + unsigned int bpl; > + > + bpl = clamp_t(unsigned int, pix->plane_fmt[i].bytesperline, > + pix->width / hsub * info->bpp[i] / 8, > + round_down(65535U, 128)); > + > + pix->plane_fmt[i].bytesperline = round_up(bpl, 128); > + pix->plane_fmt[i].sizeimage = bpl * pix->height / vsub; > + } > + > + if (info->planes == 3) { > + /* The second and third planes must have the same stride. */ > + pix->plane_fmt[2].bytesperline = pix->plane_fmt[1].bytesperline; > + pix->plane_fmt[2].sizeimage = pix->plane_fmt[1].sizeimage; > + } > + > + pix->num_planes = info->planes; > + > + if (fmtinfo) > + *fmtinfo = info; > + > + return 0; > +} > + > +static int > +vsp1_video_try_format(struct file *file, void *fh, struct v4l2_format *format) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + > + if (format->type != video->queue.type) > + return -EINVAL; > + > + return __vsp1_video_try_format(video, &format->fmt.pix_mp, NULL); > +} > + > +static int > +vsp1_video_set_format(struct file *file, void *fh, struct v4l2_format *format) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + const struct vsp1_format_info *info; > + int ret; > + > + if (format->type != video->queue.type) > + return -EINVAL; > + > + ret = __vsp1_video_try_format(video, &format->fmt.pix_mp, &info); > + if (ret < 0) > + return ret; > + > + mutex_lock(&video->lock); > + > + if (vb2_is_streaming(&video->queue)) { That should be vb2_is_busy(): once buffers are allocated you can't change the format anymore. > + ret = -EBUSY; > + goto done; > + } > + > + video->format = format->fmt.pix_mp; > + video->fmtinfo = info; > + > +done: > + mutex_unlock(&video->lock); > + return ret; > +} > + > +static int > +vsp1_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + int ret; > + > + mutex_lock(&video->lock); > + > + if (video->queue.owner && video->queue.owner != vfh) { > + ret = -EBUSY; > + goto done; > + } > + > + ret = vb2_reqbufs(&video->queue, rb); > + if (ret < 0) > + goto done; > + > + video->queue.owner = vfh; > + > +done: > + mutex_unlock(&video->lock); > + return ret ? ret : rb->count; On success reqbufs should return 0, not the number of allocated buffers. Have you considered using the vb2 helper functions in videobuf2-core.c? They take care of the queue ownership and often simplify drivers considerably. > +} > + > +static int > +vsp1_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + int ret; > + > + mutex_lock(&video->lock); > + ret = vb2_querybuf(&video->queue, buf); > + mutex_unlock(&video->lock); > + > + return ret; > +} > + > +static int > +vsp1_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + int ret; > + > + mutex_lock(&video->lock); > + > + if (video->queue.owner && video->queue.owner != vfh) { > + ret = -EBUSY; > + goto done; > + } > + > + ret = vb2_qbuf(&video->queue, buf); > + > +done: > + mutex_unlock(&video->lock); > + return ret; > +} > + > +static int > +vsp1_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + int ret; > + > + mutex_lock(&video->lock); > + > + if (video->queue.owner && video->queue.owner != vfh) { > + ret = -EBUSY; > + goto done; > + } > + > + ret = vb2_dqbuf(&video->queue, buf, file->f_flags & O_NONBLOCK); > + > +done: > + mutex_unlock(&video->lock); > + return ret; > +} > + > +static int > +vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + struct vsp1_pipeline *pipe; > + int ret; > + > + mutex_lock(&video->lock); > + > + if (video->queue.owner && video->queue.owner != vfh) { > + ret = -EBUSY; > + goto err_unlock; > + } > + > + video->sequence = 0; > + > + /* Start streaming on the pipeline. No link touching an entity in the > + * pipeline can be activated or deactivated once streaming is started. > + * > + * Use the VSP1 pipeline object embedded in the first video object that > + * starts streaming. > + */ > + pipe = video->video.entity.pipe > + ? to_vsp1_pipeline(&video->video.entity) : &video->pipe; > + > + ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); > + if (ret < 0) > + goto err_unlock; > + > + /* Verify that the configured format matches the output of the connected > + * subdev. > + */ > + ret = vsp1_video_verify_format(video); > + if (ret < 0) > + goto err_stop; > + > + ret = vsp1_pipeline_init(pipe, video); > + if (ret < 0) > + goto err_stop; Shouldn't the code above be better placed in the vb2 start_streaming op? > + > + /* Start the queue. */ > + ret = vb2_streamon(&video->queue, type); > + if (ret < 0) > + goto err_cleanup; > + > + mutex_unlock(&video->lock); > + return 0; > + > +err_cleanup: > + vsp1_pipeline_cleanup(pipe); > +err_stop: > + media_entity_pipeline_stop(&video->video.entity); > +err_unlock: > + mutex_unlock(&video->lock); > + return ret; > + > +} > + > +static int > +vsp1_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + int ret; > + > + mutex_lock(&video->lock); > + > + if (video->queue.owner && video->queue.owner != vfh) { > + ret = -EBUSY; > + goto done; > + } > + > + ret = vb2_streamoff(&video->queue, type); > + > +done: > + mutex_unlock(&video->lock); > + return ret; > +} > + > +static const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = { > + .vidioc_querycap = vsp1_video_querycap, > + .vidioc_g_fmt_vid_cap_mplane = vsp1_video_get_format, > + .vidioc_s_fmt_vid_cap_mplane = vsp1_video_set_format, > + .vidioc_try_fmt_vid_cap_mplane = vsp1_video_try_format, > + .vidioc_g_fmt_vid_out_mplane = vsp1_video_get_format, > + .vidioc_s_fmt_vid_out_mplane = vsp1_video_set_format, > + .vidioc_try_fmt_vid_out_mplane = vsp1_video_try_format, > + .vidioc_reqbufs = vsp1_video_reqbufs, > + .vidioc_querybuf = vsp1_video_querybuf, > + .vidioc_qbuf = vsp1_video_qbuf, > + .vidioc_dqbuf = vsp1_video_dqbuf, > + .vidioc_streamon = vsp1_video_streamon, > + .vidioc_streamoff = vsp1_video_streamoff, > +}; > + > +/* ----------------------------------------------------------------------------- > + * V4L2 File Operations > + */ > + > +static int vsp1_video_open(struct file *file) > +{ > + struct vsp1_video *video = video_drvdata(file); > + struct v4l2_fh *vfh; > + int ret = 0; > + > + vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); > + if (vfh == NULL) > + return -ENOMEM; > + > + v4l2_fh_init(vfh, &video->video); > + v4l2_fh_add(vfh); > + > + file->private_data = vfh; > + > + if (!vsp1_device_get(video->vsp1)) { > + ret = -EBUSY; > + v4l2_fh_del(vfh); > + kfree(vfh); > + } > + > + return ret; > +} > + > +static int vsp1_video_release(struct file *file) > +{ > + struct vsp1_video *video = video_drvdata(file); > + struct v4l2_fh *vfh = file->private_data; > + > + mutex_lock(&video->lock); > + if (video->queue.owner == vfh) { > + vb2_queue_release(&video->queue); > + video->queue.owner = NULL; > + } > + mutex_unlock(&video->lock); > + > + vsp1_device_put(video->vsp1); > + > + v4l2_fh_release(file); > + > + file->private_data = NULL; > + > + return 0; > +} > + > +static unsigned int vsp1_video_poll(struct file *file, poll_table *wait) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + int ret; > + > + mutex_lock(&video->lock); > + ret = vb2_poll(&video->queue, file, wait); > + mutex_unlock(&video->lock); > + > + return ret; > +} > + > +static int vsp1_video_mmap(struct file *file, struct vm_area_struct *vma) > +{ > + struct v4l2_fh *vfh = file->private_data; > + struct vsp1_video *video = to_vsp1_video(vfh->vdev); > + int ret; > + > + mutex_lock(&video->lock); > + ret = vb2_mmap(&video->queue, vma); > + mutex_unlock(&video->lock); > + > + return ret; > +} > + > +static struct v4l2_file_operations vsp1_video_fops = { > + .owner = THIS_MODULE, > + .unlocked_ioctl = video_ioctl2, > + .open = vsp1_video_open, > + .release = vsp1_video_release, > + .poll = vsp1_video_poll, > + .mmap = vsp1_video_mmap, > +}; > + > +/* ----------------------------------------------------------------------------- > + * Initialization and Cleanup > + */ > + > +int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf) > +{ > + const char *direction; > + int ret; > + > + switch (video->type) { > + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: > + direction = "output"; > + video->pad.flags = MEDIA_PAD_FL_SINK; > + break; > + > + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: > + direction = "input"; > + video->pad.flags = MEDIA_PAD_FL_SOURCE; > + video->video.vfl_dir = VFL_DIR_TX; > + break; > + > + default: > + return -EINVAL; > + } > + > + video->rwpf = rwpf; > + > + mutex_init(&video->lock); > + spin_lock_init(&video->irqlock); > + INIT_LIST_HEAD(&video->irqqueue); > + > + mutex_init(&video->pipe.lock); > + spin_lock_init(&video->pipe.irqlock); > + INIT_LIST_HEAD(&video->pipe.entities); > + init_waitqueue_head(&video->pipe.wq); > + video->pipe.state = VSP1_PIPELINE_STOPPED; > + > + /* Initialize the media entity... */ > + ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); > + if (ret < 0) > + return ret; > + > + /* ... and the format ... */ > + video->fmtinfo = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT); > + video->format.pixelformat = video->fmtinfo->fourcc; > + video->format.colorspace = V4L2_COLORSPACE_SRGB; > + video->format.field = V4L2_FIELD_NONE; > + video->format.width = VSP1_VIDEO_DEF_WIDTH; > + video->format.height = VSP1_VIDEO_DEF_HEIGHT; > + video->format.num_planes = 1; > + video->format.plane_fmt[0].bytesperline = > + video->format.width * video->fmtinfo->bpp[0] / 8; > + video->format.plane_fmt[0].sizeimage = > + video->format.plane_fmt[0].bytesperline * video->format.height; > + > + /* ... and the video node... */ > + video->video.v4l2_dev = &video->vsp1->v4l2_dev; > + video->video.fops = &vsp1_video_fops; > + snprintf(video->video.name, sizeof(video->video.name), "%s %s", > + rwpf->subdev.name, direction); > + video->video.vfl_type = VFL_TYPE_GRABBER; > + video->video.release = video_device_release_empty; > + video->video.ioctl_ops = &vsp1_video_ioctl_ops; > + > + video_set_drvdata(&video->video, video); > + > + /* ... and the buffers queue... */ > + video->alloc_ctx = vb2_dma_contig_init_ctx(video->vsp1->dev); > + if (IS_ERR(video->alloc_ctx)) > + goto error; > + > + video->queue.type = video->type; > + video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; > + video->queue.drv_priv = video; > + video->queue.buf_struct_size = sizeof(struct vsp1_video_buffer); > + video->queue.ops = &vsp1_video_queue_qops; > + video->queue.mem_ops = &vb2_dma_contig_memops; > + video->queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; > + ret = vb2_queue_init(&video->queue); > + if (ret < 0) { > + dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n"); > + goto error; > + } > + > + /* ... and register the video device. */ > + ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); > + if (ret < 0) { > + dev_err(video->vsp1->dev, "failed to register video device\n"); > + goto error; > + } > + > + return 0; > + > +error: > + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); > + vsp1_video_cleanup(video); > + return ret; > +} > + > +void vsp1_video_cleanup(struct vsp1_video *video) > +{ > + if (video_is_registered(&video->video)) > + video_unregister_device(&video->video); > + > + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); > + media_entity_cleanup(&video->video.entity); > +} Regards, Hans -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html