18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * vimc-capture.c Virtual Media Controller Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
98c2ecf20Sopenharmony_ci#include <media/videobuf2-core.h>
108c2ecf20Sopenharmony_ci#include <media/videobuf2-vmalloc.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "vimc-common.h"
138c2ecf20Sopenharmony_ci#include "vimc-streamer.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistruct vimc_cap_device {
168c2ecf20Sopenharmony_ci	struct vimc_ent_device ved;
178c2ecf20Sopenharmony_ci	struct video_device vdev;
188c2ecf20Sopenharmony_ci	struct v4l2_pix_format format;
198c2ecf20Sopenharmony_ci	struct vb2_queue queue;
208c2ecf20Sopenharmony_ci	struct list_head buf_list;
218c2ecf20Sopenharmony_ci	/*
228c2ecf20Sopenharmony_ci	 * NOTE: in a real driver, a spin lock must be used to access the
238c2ecf20Sopenharmony_ci	 * queue because the frames are generated from a hardware interruption
248c2ecf20Sopenharmony_ci	 * and the isr is not allowed to sleep.
258c2ecf20Sopenharmony_ci	 * Even if it is not necessary a spinlock in the vimc driver, we
268c2ecf20Sopenharmony_ci	 * use it here as a code reference
278c2ecf20Sopenharmony_ci	 */
288c2ecf20Sopenharmony_ci	spinlock_t qlock;
298c2ecf20Sopenharmony_ci	struct mutex lock;
308c2ecf20Sopenharmony_ci	u32 sequence;
318c2ecf20Sopenharmony_ci	struct vimc_stream stream;
328c2ecf20Sopenharmony_ci	struct media_pad pad;
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format fmt_default = {
368c2ecf20Sopenharmony_ci	.width = 640,
378c2ecf20Sopenharmony_ci	.height = 480,
388c2ecf20Sopenharmony_ci	.pixelformat = V4L2_PIX_FMT_RGB24,
398c2ecf20Sopenharmony_ci	.field = V4L2_FIELD_NONE,
408c2ecf20Sopenharmony_ci	.colorspace = V4L2_COLORSPACE_SRGB,
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct vimc_cap_buffer {
448c2ecf20Sopenharmony_ci	/*
458c2ecf20Sopenharmony_ci	 * struct vb2_v4l2_buffer must be the first element
468c2ecf20Sopenharmony_ci	 * the videobuf2 framework will allocate this struct based on
478c2ecf20Sopenharmony_ci	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
488c2ecf20Sopenharmony_ci	 * memory as a vb2_buffer
498c2ecf20Sopenharmony_ci	 */
508c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer vb2;
518c2ecf20Sopenharmony_ci	struct list_head list;
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic int vimc_cap_querycap(struct file *file, void *priv,
558c2ecf20Sopenharmony_ci			     struct v4l2_capability *cap)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	strscpy(cap->driver, VIMC_PDEV_NAME, sizeof(cap->driver));
588c2ecf20Sopenharmony_ci	strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
598c2ecf20Sopenharmony_ci	snprintf(cap->bus_info, sizeof(cap->bus_info),
608c2ecf20Sopenharmony_ci		 "platform:%s", VIMC_PDEV_NAME);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return 0;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void vimc_cap_get_format(struct vimc_ent_device *ved,
668c2ecf20Sopenharmony_ci				struct v4l2_pix_format *fmt)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
698c2ecf20Sopenharmony_ci						    ved);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	*fmt = vcap->format;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int vimc_cap_g_fmt_vid_cap(struct file *file, void *priv,
758c2ecf20Sopenharmony_ci				  struct v4l2_format *f)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap = video_drvdata(file);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	f->fmt.pix = vcap->format;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return 0;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv,
858c2ecf20Sopenharmony_ci				    struct v4l2_format *f)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct v4l2_pix_format *format = &f->fmt.pix;
888c2ecf20Sopenharmony_ci	const struct vimc_pix_map *vpix;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	format->width = clamp_t(u32, format->width, VIMC_FRAME_MIN_WIDTH,
918c2ecf20Sopenharmony_ci				VIMC_FRAME_MAX_WIDTH) & ~1;
928c2ecf20Sopenharmony_ci	format->height = clamp_t(u32, format->height, VIMC_FRAME_MIN_HEIGHT,
938c2ecf20Sopenharmony_ci				 VIMC_FRAME_MAX_HEIGHT) & ~1;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* Don't accept a pixelformat that is not on the table */
968c2ecf20Sopenharmony_ci	vpix = vimc_pix_map_by_pixelformat(format->pixelformat);
978c2ecf20Sopenharmony_ci	if (!vpix) {
988c2ecf20Sopenharmony_ci		format->pixelformat = fmt_default.pixelformat;
998c2ecf20Sopenharmony_ci		vpix = vimc_pix_map_by_pixelformat(format->pixelformat);
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci	/* TODO: Add support for custom bytesperline values */
1028c2ecf20Sopenharmony_ci	format->bytesperline = format->width * vpix->bpp;
1038c2ecf20Sopenharmony_ci	format->sizeimage = format->bytesperline * format->height;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (format->field == V4L2_FIELD_ANY)
1068c2ecf20Sopenharmony_ci		format->field = fmt_default.field;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	vimc_colorimetry_clamp(format);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (format->colorspace == V4L2_COLORSPACE_DEFAULT)
1118c2ecf20Sopenharmony_ci		format->colorspace = fmt_default.colorspace;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return 0;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv,
1178c2ecf20Sopenharmony_ci				  struct v4l2_format *f)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap = video_drvdata(file);
1208c2ecf20Sopenharmony_ci	int ret;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* Do not change the format while stream is on */
1238c2ecf20Sopenharmony_ci	if (vb2_is_busy(&vcap->queue))
1248c2ecf20Sopenharmony_ci		return -EBUSY;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	ret = vimc_cap_try_fmt_vid_cap(file, priv, f);
1278c2ecf20Sopenharmony_ci	if (ret)
1288c2ecf20Sopenharmony_ci		return ret;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	dev_dbg(vcap->ved.dev, "%s: format update: "
1318c2ecf20Sopenharmony_ci		"old:%dx%d (0x%x, %d, %d, %d, %d) "
1328c2ecf20Sopenharmony_ci		"new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcap->vdev.name,
1338c2ecf20Sopenharmony_ci		/* old */
1348c2ecf20Sopenharmony_ci		vcap->format.width, vcap->format.height,
1358c2ecf20Sopenharmony_ci		vcap->format.pixelformat, vcap->format.colorspace,
1368c2ecf20Sopenharmony_ci		vcap->format.quantization, vcap->format.xfer_func,
1378c2ecf20Sopenharmony_ci		vcap->format.ycbcr_enc,
1388c2ecf20Sopenharmony_ci		/* new */
1398c2ecf20Sopenharmony_ci		f->fmt.pix.width, f->fmt.pix.height,
1408c2ecf20Sopenharmony_ci		f->fmt.pix.pixelformat,	f->fmt.pix.colorspace,
1418c2ecf20Sopenharmony_ci		f->fmt.pix.quantization, f->fmt.pix.xfer_func,
1428c2ecf20Sopenharmony_ci		f->fmt.pix.ycbcr_enc);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	vcap->format = f->fmt.pix;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	return 0;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
1508c2ecf20Sopenharmony_ci				     struct v4l2_fmtdesc *f)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	const struct vimc_pix_map *vpix;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (f->mbus_code) {
1558c2ecf20Sopenharmony_ci		if (f->index > 0)
1568c2ecf20Sopenharmony_ci			return -EINVAL;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		vpix = vimc_pix_map_by_code(f->mbus_code);
1598c2ecf20Sopenharmony_ci	} else {
1608c2ecf20Sopenharmony_ci		vpix = vimc_pix_map_by_index(f->index);
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (!vpix)
1648c2ecf20Sopenharmony_ci		return -EINVAL;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	f->pixelformat = vpix->pixelformat;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int vimc_cap_enum_framesizes(struct file *file, void *fh,
1728c2ecf20Sopenharmony_ci				    struct v4l2_frmsizeenum *fsize)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	const struct vimc_pix_map *vpix;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (fsize->index)
1778c2ecf20Sopenharmony_ci		return -EINVAL;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* Only accept code in the pix map table */
1808c2ecf20Sopenharmony_ci	vpix = vimc_pix_map_by_code(fsize->pixel_format);
1818c2ecf20Sopenharmony_ci	if (!vpix)
1828c2ecf20Sopenharmony_ci		return -EINVAL;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
1858c2ecf20Sopenharmony_ci	fsize->stepwise.min_width = VIMC_FRAME_MIN_WIDTH;
1868c2ecf20Sopenharmony_ci	fsize->stepwise.max_width = VIMC_FRAME_MAX_WIDTH;
1878c2ecf20Sopenharmony_ci	fsize->stepwise.min_height = VIMC_FRAME_MIN_HEIGHT;
1888c2ecf20Sopenharmony_ci	fsize->stepwise.max_height = VIMC_FRAME_MAX_HEIGHT;
1898c2ecf20Sopenharmony_ci	fsize->stepwise.step_width = 1;
1908c2ecf20Sopenharmony_ci	fsize->stepwise.step_height = 1;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations vimc_cap_fops = {
1968c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1978c2ecf20Sopenharmony_ci	.open		= v4l2_fh_open,
1988c2ecf20Sopenharmony_ci	.release	= vb2_fop_release,
1998c2ecf20Sopenharmony_ci	.read           = vb2_fop_read,
2008c2ecf20Sopenharmony_ci	.poll		= vb2_fop_poll,
2018c2ecf20Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
2028c2ecf20Sopenharmony_ci	.mmap           = vb2_fop_mmap,
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
2068c2ecf20Sopenharmony_ci	.vidioc_querycap = vimc_cap_querycap,
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,
2098c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap = vimc_cap_s_fmt_vid_cap,
2108c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap = vimc_cap_try_fmt_vid_cap,
2118c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
2128c2ecf20Sopenharmony_ci	.vidioc_enum_framesizes = vimc_cap_enum_framesizes,
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	.vidioc_reqbufs = vb2_ioctl_reqbufs,
2158c2ecf20Sopenharmony_ci	.vidioc_create_bufs = vb2_ioctl_create_bufs,
2168c2ecf20Sopenharmony_ci	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
2178c2ecf20Sopenharmony_ci	.vidioc_querybuf = vb2_ioctl_querybuf,
2188c2ecf20Sopenharmony_ci	.vidioc_qbuf = vb2_ioctl_qbuf,
2198c2ecf20Sopenharmony_ci	.vidioc_dqbuf = vb2_ioctl_dqbuf,
2208c2ecf20Sopenharmony_ci	.vidioc_expbuf = vb2_ioctl_expbuf,
2218c2ecf20Sopenharmony_ci	.vidioc_streamon = vb2_ioctl_streamon,
2228c2ecf20Sopenharmony_ci	.vidioc_streamoff = vb2_ioctl_streamoff,
2238c2ecf20Sopenharmony_ci};
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
2268c2ecf20Sopenharmony_ci					enum vb2_buffer_state state)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct vimc_cap_buffer *vbuf, *node;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	spin_lock(&vcap->qlock);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
2338c2ecf20Sopenharmony_ci		list_del(&vbuf->list);
2348c2ecf20Sopenharmony_ci		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	spin_unlock(&vcap->qlock);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
2438c2ecf20Sopenharmony_ci	struct media_entity *entity = &vcap->vdev.entity;
2448c2ecf20Sopenharmony_ci	int ret;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	vcap->sequence = 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* Start the media pipeline */
2498c2ecf20Sopenharmony_ci	ret = media_pipeline_start(entity, &vcap->stream.pipe);
2508c2ecf20Sopenharmony_ci	if (ret) {
2518c2ecf20Sopenharmony_ci		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
2528c2ecf20Sopenharmony_ci		return ret;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	ret = vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 1);
2568c2ecf20Sopenharmony_ci	if (ret) {
2578c2ecf20Sopenharmony_ci		media_pipeline_stop(entity);
2588c2ecf20Sopenharmony_ci		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
2598c2ecf20Sopenharmony_ci		return ret;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return 0;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci/*
2668c2ecf20Sopenharmony_ci * Stop the stream engine. Any remaining buffers in the stream queue are
2678c2ecf20Sopenharmony_ci * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
2688c2ecf20Sopenharmony_ci */
2698c2ecf20Sopenharmony_cistatic void vimc_cap_stop_streaming(struct vb2_queue *vq)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 0);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* Stop the media pipeline */
2768c2ecf20Sopenharmony_ci	media_pipeline_stop(&vcap->vdev.entity);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* Release all active buffers */
2798c2ecf20Sopenharmony_ci	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
2858c2ecf20Sopenharmony_ci	struct vimc_cap_buffer *buf = container_of(vb2_buf,
2868c2ecf20Sopenharmony_ci						   struct vimc_cap_buffer,
2878c2ecf20Sopenharmony_ci						   vb2.vb2_buf);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	spin_lock(&vcap->qlock);
2908c2ecf20Sopenharmony_ci	list_add_tail(&buf->list, &vcap->buf_list);
2918c2ecf20Sopenharmony_ci	spin_unlock(&vcap->qlock);
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
2958c2ecf20Sopenharmony_ci				unsigned int *nplanes, unsigned int sizes[],
2968c2ecf20Sopenharmony_ci				struct device *alloc_devs[])
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (*nplanes)
3018c2ecf20Sopenharmony_ci		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
3028c2ecf20Sopenharmony_ci	/* We don't support multiplanes for now */
3038c2ecf20Sopenharmony_ci	*nplanes = 1;
3048c2ecf20Sopenharmony_ci	sizes[0] = vcap->format.sizeimage;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return 0;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
3128c2ecf20Sopenharmony_ci	unsigned long size = vcap->format.sizeimage;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size) {
3158c2ecf20Sopenharmony_ci		dev_err(vcap->ved.dev, "%s: buffer too small (%lu < %lu)\n",
3168c2ecf20Sopenharmony_ci			vcap->vdev.name, vb2_plane_size(vb, 0), size);
3178c2ecf20Sopenharmony_ci		return -EINVAL;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	return 0;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic const struct vb2_ops vimc_cap_qops = {
3238c2ecf20Sopenharmony_ci	.start_streaming	= vimc_cap_start_streaming,
3248c2ecf20Sopenharmony_ci	.stop_streaming		= vimc_cap_stop_streaming,
3258c2ecf20Sopenharmony_ci	.buf_queue		= vimc_cap_buf_queue,
3268c2ecf20Sopenharmony_ci	.queue_setup		= vimc_cap_queue_setup,
3278c2ecf20Sopenharmony_ci	.buf_prepare		= vimc_cap_buffer_prepare,
3288c2ecf20Sopenharmony_ci	/*
3298c2ecf20Sopenharmony_ci	 * Since q->lock is set we can use the standard
3308c2ecf20Sopenharmony_ci	 * vb2_ops_wait_prepare/finish helper functions.
3318c2ecf20Sopenharmony_ci	 */
3328c2ecf20Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
3338c2ecf20Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
3348c2ecf20Sopenharmony_ci};
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic const struct media_entity_operations vimc_cap_mops = {
3378c2ecf20Sopenharmony_ci	.link_validate		= vimc_vdev_link_validate,
3388c2ecf20Sopenharmony_ci};
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic void vimc_cap_release(struct vimc_ent_device *ved)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap =
3438c2ecf20Sopenharmony_ci		container_of(ved, struct vimc_cap_device, ved);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	media_entity_cleanup(vcap->ved.ent);
3468c2ecf20Sopenharmony_ci	kfree(vcap);
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic void vimc_cap_unregister(struct vimc_ent_device *ved)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap =
3528c2ecf20Sopenharmony_ci		container_of(ved, struct vimc_cap_device, ved);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	vb2_video_unregister_device(&vcap->vdev);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic void *vimc_cap_process_frame(struct vimc_ent_device *ved,
3588c2ecf20Sopenharmony_ci				    const void *frame)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
3618c2ecf20Sopenharmony_ci						    ved);
3628c2ecf20Sopenharmony_ci	struct vimc_cap_buffer *vimc_buf;
3638c2ecf20Sopenharmony_ci	void *vbuf;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	spin_lock(&vcap->qlock);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/* Get the first entry of the list */
3688c2ecf20Sopenharmony_ci	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
3698c2ecf20Sopenharmony_ci					    typeof(*vimc_buf), list);
3708c2ecf20Sopenharmony_ci	if (!vimc_buf) {
3718c2ecf20Sopenharmony_ci		spin_unlock(&vcap->qlock);
3728c2ecf20Sopenharmony_ci		return ERR_PTR(-EAGAIN);
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/* Remove this entry from the list */
3768c2ecf20Sopenharmony_ci	list_del(&vimc_buf->list);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	spin_unlock(&vcap->qlock);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* Fill the buffer */
3818c2ecf20Sopenharmony_ci	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
3828c2ecf20Sopenharmony_ci	vimc_buf->vb2.sequence = vcap->sequence++;
3838c2ecf20Sopenharmony_ci	vimc_buf->vb2.field = vcap->format.field;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	memcpy(vbuf, frame, vcap->format.sizeimage);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* Set it as ready */
3908c2ecf20Sopenharmony_ci	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
3918c2ecf20Sopenharmony_ci			      vcap->format.sizeimage);
3928c2ecf20Sopenharmony_ci	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
3938c2ecf20Sopenharmony_ci	return NULL;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
3978c2ecf20Sopenharmony_ci					    const char *vcfg_name)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct v4l2_device *v4l2_dev = &vimc->v4l2_dev;
4008c2ecf20Sopenharmony_ci	const struct vimc_pix_map *vpix;
4018c2ecf20Sopenharmony_ci	struct vimc_cap_device *vcap;
4028c2ecf20Sopenharmony_ci	struct video_device *vdev;
4038c2ecf20Sopenharmony_ci	struct vb2_queue *q;
4048c2ecf20Sopenharmony_ci	int ret;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	/* Allocate the vimc_cap_device struct */
4078c2ecf20Sopenharmony_ci	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
4088c2ecf20Sopenharmony_ci	if (!vcap)
4098c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* Initialize the media entity */
4128c2ecf20Sopenharmony_ci	vcap->vdev.entity.name = vcfg_name;
4138c2ecf20Sopenharmony_ci	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
4148c2ecf20Sopenharmony_ci	vcap->pad.flags = MEDIA_PAD_FL_SINK;
4158c2ecf20Sopenharmony_ci	ret = media_entity_pads_init(&vcap->vdev.entity,
4168c2ecf20Sopenharmony_ci				     1, &vcap->pad);
4178c2ecf20Sopenharmony_ci	if (ret)
4188c2ecf20Sopenharmony_ci		goto err_free_vcap;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* Initialize the lock */
4218c2ecf20Sopenharmony_ci	mutex_init(&vcap->lock);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* Initialize the vb2 queue */
4248c2ecf20Sopenharmony_ci	q = &vcap->queue;
4258c2ecf20Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
4268c2ecf20Sopenharmony_ci	q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR;
4278c2ecf20Sopenharmony_ci	q->drv_priv = vcap;
4288c2ecf20Sopenharmony_ci	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
4298c2ecf20Sopenharmony_ci	q->ops = &vimc_cap_qops;
4308c2ecf20Sopenharmony_ci	q->mem_ops = &vb2_vmalloc_memops;
4318c2ecf20Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
4328c2ecf20Sopenharmony_ci	q->min_buffers_needed = 2;
4338c2ecf20Sopenharmony_ci	q->lock = &vcap->lock;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	ret = vb2_queue_init(q);
4368c2ecf20Sopenharmony_ci	if (ret) {
4378c2ecf20Sopenharmony_ci		dev_err(vimc->mdev.dev, "%s: vb2 queue init failed (err=%d)\n",
4388c2ecf20Sopenharmony_ci			vcfg_name, ret);
4398c2ecf20Sopenharmony_ci		goto err_clean_m_ent;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	/* Initialize buffer list and its lock */
4438c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&vcap->buf_list);
4448c2ecf20Sopenharmony_ci	spin_lock_init(&vcap->qlock);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* Set default frame format */
4478c2ecf20Sopenharmony_ci	vcap->format = fmt_default;
4488c2ecf20Sopenharmony_ci	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
4498c2ecf20Sopenharmony_ci	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
4508c2ecf20Sopenharmony_ci	vcap->format.sizeimage = vcap->format.bytesperline *
4518c2ecf20Sopenharmony_ci				 vcap->format.height;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* Fill the vimc_ent_device struct */
4548c2ecf20Sopenharmony_ci	vcap->ved.ent = &vcap->vdev.entity;
4558c2ecf20Sopenharmony_ci	vcap->ved.process_frame = vimc_cap_process_frame;
4568c2ecf20Sopenharmony_ci	vcap->ved.vdev_get_format = vimc_cap_get_format;
4578c2ecf20Sopenharmony_ci	vcap->ved.dev = vimc->mdev.dev;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* Initialize the video_device struct */
4608c2ecf20Sopenharmony_ci	vdev = &vcap->vdev;
4618c2ecf20Sopenharmony_ci	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
4628c2ecf20Sopenharmony_ci			  | V4L2_CAP_IO_MC;
4638c2ecf20Sopenharmony_ci	vdev->entity.ops = &vimc_cap_mops;
4648c2ecf20Sopenharmony_ci	vdev->release = video_device_release_empty;
4658c2ecf20Sopenharmony_ci	vdev->fops = &vimc_cap_fops;
4668c2ecf20Sopenharmony_ci	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
4678c2ecf20Sopenharmony_ci	vdev->lock = &vcap->lock;
4688c2ecf20Sopenharmony_ci	vdev->queue = q;
4698c2ecf20Sopenharmony_ci	vdev->v4l2_dev = v4l2_dev;
4708c2ecf20Sopenharmony_ci	vdev->vfl_dir = VFL_DIR_RX;
4718c2ecf20Sopenharmony_ci	strscpy(vdev->name, vcfg_name, sizeof(vdev->name));
4728c2ecf20Sopenharmony_ci	video_set_drvdata(vdev, &vcap->ved);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* Register the video_device with the v4l2 and the media framework */
4758c2ecf20Sopenharmony_ci	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
4768c2ecf20Sopenharmony_ci	if (ret) {
4778c2ecf20Sopenharmony_ci		dev_err(vimc->mdev.dev, "%s: video register failed (err=%d)\n",
4788c2ecf20Sopenharmony_ci			vcap->vdev.name, ret);
4798c2ecf20Sopenharmony_ci		goto err_clean_m_ent;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	return &vcap->ved;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cierr_clean_m_ent:
4858c2ecf20Sopenharmony_ci	media_entity_cleanup(&vcap->vdev.entity);
4868c2ecf20Sopenharmony_cierr_free_vcap:
4878c2ecf20Sopenharmony_ci	kfree(vcap);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistruct vimc_ent_type vimc_cap_type = {
4938c2ecf20Sopenharmony_ci	.add = vimc_cap_add,
4948c2ecf20Sopenharmony_ci	.unregister = vimc_cap_unregister,
4958c2ecf20Sopenharmony_ci	.release = vimc_cap_release
4968c2ecf20Sopenharmony_ci};
497