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