162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * vimc-capture.c Virtual Media Controller Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 962306a36Sopenharmony_ci#include <media/videobuf2-core.h> 1062306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 1162306a36Sopenharmony_ci#include <media/videobuf2-vmalloc.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "vimc-common.h" 1462306a36Sopenharmony_ci#include "vimc-streamer.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct vimc_capture_device { 1762306a36Sopenharmony_ci struct vimc_ent_device ved; 1862306a36Sopenharmony_ci struct video_device vdev; 1962306a36Sopenharmony_ci struct v4l2_pix_format format; 2062306a36Sopenharmony_ci struct vb2_queue queue; 2162306a36Sopenharmony_ci struct list_head buf_list; 2262306a36Sopenharmony_ci /* 2362306a36Sopenharmony_ci * NOTE: in a real driver, a spin lock must be used to access the 2462306a36Sopenharmony_ci * queue because the frames are generated from a hardware interruption 2562306a36Sopenharmony_ci * and the isr is not allowed to sleep. 2662306a36Sopenharmony_ci * Even if it is not necessary a spinlock in the vimc driver, we 2762306a36Sopenharmony_ci * use it here as a code reference 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci spinlock_t qlock; 3062306a36Sopenharmony_ci struct mutex lock; 3162306a36Sopenharmony_ci u32 sequence; 3262306a36Sopenharmony_ci struct vimc_stream stream; 3362306a36Sopenharmony_ci struct media_pad pad; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic const struct v4l2_pix_format fmt_default = { 3762306a36Sopenharmony_ci .width = 640, 3862306a36Sopenharmony_ci .height = 480, 3962306a36Sopenharmony_ci .pixelformat = V4L2_PIX_FMT_RGB24, 4062306a36Sopenharmony_ci .field = V4L2_FIELD_NONE, 4162306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct vimc_capture_buffer { 4562306a36Sopenharmony_ci /* 4662306a36Sopenharmony_ci * struct vb2_v4l2_buffer must be the first element 4762306a36Sopenharmony_ci * the videobuf2 framework will allocate this struct based on 4862306a36Sopenharmony_ci * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of 4962306a36Sopenharmony_ci * memory as a vb2_buffer 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci struct vb2_v4l2_buffer vb2; 5262306a36Sopenharmony_ci struct list_head list; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int vimc_capture_querycap(struct file *file, void *priv, 5662306a36Sopenharmony_ci struct v4l2_capability *cap) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci strscpy(cap->driver, VIMC_PDEV_NAME, sizeof(cap->driver)); 5962306a36Sopenharmony_ci strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card)); 6062306a36Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), 6162306a36Sopenharmony_ci "platform:%s", VIMC_PDEV_NAME); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void vimc_capture_get_format(struct vimc_ent_device *ved, 6762306a36Sopenharmony_ci struct v4l2_pix_format *fmt) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct vimc_capture_device *vcapture = container_of(ved, struct vimc_capture_device, 7062306a36Sopenharmony_ci ved); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci *fmt = vcapture->format; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int vimc_capture_g_fmt_vid_cap(struct file *file, void *priv, 7662306a36Sopenharmony_ci struct v4l2_format *f) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct vimc_capture_device *vcapture = video_drvdata(file); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci f->fmt.pix = vcapture->format; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int vimc_capture_try_fmt_vid_cap(struct file *file, void *priv, 8662306a36Sopenharmony_ci struct v4l2_format *f) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct v4l2_pix_format *format = &f->fmt.pix; 8962306a36Sopenharmony_ci const struct vimc_pix_map *vpix; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci format->width = clamp_t(u32, format->width, VIMC_FRAME_MIN_WIDTH, 9262306a36Sopenharmony_ci VIMC_FRAME_MAX_WIDTH) & ~1; 9362306a36Sopenharmony_ci format->height = clamp_t(u32, format->height, VIMC_FRAME_MIN_HEIGHT, 9462306a36Sopenharmony_ci VIMC_FRAME_MAX_HEIGHT) & ~1; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* Don't accept a pixelformat that is not on the table */ 9762306a36Sopenharmony_ci vpix = vimc_pix_map_by_pixelformat(format->pixelformat); 9862306a36Sopenharmony_ci if (!vpix) { 9962306a36Sopenharmony_ci format->pixelformat = fmt_default.pixelformat; 10062306a36Sopenharmony_ci vpix = vimc_pix_map_by_pixelformat(format->pixelformat); 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci /* TODO: Add support for custom bytesperline values */ 10362306a36Sopenharmony_ci format->bytesperline = format->width * vpix->bpp; 10462306a36Sopenharmony_ci format->sizeimage = format->bytesperline * format->height; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (format->field == V4L2_FIELD_ANY) 10762306a36Sopenharmony_ci format->field = fmt_default.field; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci vimc_colorimetry_clamp(format); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (format->colorspace == V4L2_COLORSPACE_DEFAULT) 11262306a36Sopenharmony_ci format->colorspace = fmt_default.colorspace; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int vimc_capture_s_fmt_vid_cap(struct file *file, void *priv, 11862306a36Sopenharmony_ci struct v4l2_format *f) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct vimc_capture_device *vcapture = video_drvdata(file); 12162306a36Sopenharmony_ci int ret; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Do not change the format while stream is on */ 12462306a36Sopenharmony_ci if (vb2_is_busy(&vcapture->queue)) 12562306a36Sopenharmony_ci return -EBUSY; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ret = vimc_capture_try_fmt_vid_cap(file, priv, f); 12862306a36Sopenharmony_ci if (ret) 12962306a36Sopenharmony_ci return ret; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci dev_dbg(vcapture->ved.dev, "%s: format update: " 13262306a36Sopenharmony_ci "old:%dx%d (0x%x, %d, %d, %d, %d) " 13362306a36Sopenharmony_ci "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcapture->vdev.name, 13462306a36Sopenharmony_ci /* old */ 13562306a36Sopenharmony_ci vcapture->format.width, vcapture->format.height, 13662306a36Sopenharmony_ci vcapture->format.pixelformat, vcapture->format.colorspace, 13762306a36Sopenharmony_ci vcapture->format.quantization, vcapture->format.xfer_func, 13862306a36Sopenharmony_ci vcapture->format.ycbcr_enc, 13962306a36Sopenharmony_ci /* new */ 14062306a36Sopenharmony_ci f->fmt.pix.width, f->fmt.pix.height, 14162306a36Sopenharmony_ci f->fmt.pix.pixelformat, f->fmt.pix.colorspace, 14262306a36Sopenharmony_ci f->fmt.pix.quantization, f->fmt.pix.xfer_func, 14362306a36Sopenharmony_ci f->fmt.pix.ycbcr_enc); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci vcapture->format = f->fmt.pix; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int vimc_capture_enum_fmt_vid_cap(struct file *file, void *priv, 15162306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci const struct vimc_pix_map *vpix; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (f->mbus_code) { 15662306a36Sopenharmony_ci if (f->index > 0) 15762306a36Sopenharmony_ci return -EINVAL; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci vpix = vimc_pix_map_by_code(f->mbus_code); 16062306a36Sopenharmony_ci } else { 16162306a36Sopenharmony_ci vpix = vimc_pix_map_by_index(f->index); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (!vpix) 16562306a36Sopenharmony_ci return -EINVAL; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci f->pixelformat = vpix->pixelformat; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int vimc_capture_enum_framesizes(struct file *file, void *fh, 17362306a36Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci const struct vimc_pix_map *vpix; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (fsize->index) 17862306a36Sopenharmony_ci return -EINVAL; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Only accept code in the pix map table */ 18162306a36Sopenharmony_ci vpix = vimc_pix_map_by_code(fsize->pixel_format); 18262306a36Sopenharmony_ci if (!vpix) 18362306a36Sopenharmony_ci return -EINVAL; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; 18662306a36Sopenharmony_ci fsize->stepwise.min_width = VIMC_FRAME_MIN_WIDTH; 18762306a36Sopenharmony_ci fsize->stepwise.max_width = VIMC_FRAME_MAX_WIDTH; 18862306a36Sopenharmony_ci fsize->stepwise.min_height = VIMC_FRAME_MIN_HEIGHT; 18962306a36Sopenharmony_ci fsize->stepwise.max_height = VIMC_FRAME_MAX_HEIGHT; 19062306a36Sopenharmony_ci fsize->stepwise.step_width = 1; 19162306a36Sopenharmony_ci fsize->stepwise.step_height = 1; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic const struct v4l2_file_operations vimc_capture_fops = { 19762306a36Sopenharmony_ci .owner = THIS_MODULE, 19862306a36Sopenharmony_ci .open = v4l2_fh_open, 19962306a36Sopenharmony_ci .release = vb2_fop_release, 20062306a36Sopenharmony_ci .read = vb2_fop_read, 20162306a36Sopenharmony_ci .poll = vb2_fop_poll, 20262306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 20362306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops vimc_capture_ioctl_ops = { 20762306a36Sopenharmony_ci .vidioc_querycap = vimc_capture_querycap, 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = vimc_capture_g_fmt_vid_cap, 21062306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = vimc_capture_s_fmt_vid_cap, 21162306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = vimc_capture_try_fmt_vid_cap, 21262306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = vimc_capture_enum_fmt_vid_cap, 21362306a36Sopenharmony_ci .vidioc_enum_framesizes = vimc_capture_enum_framesizes, 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 21662306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 21762306a36Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 21862306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 21962306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 22062306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 22162306a36Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 22262306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 22362306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void vimc_capture_return_all_buffers(struct vimc_capture_device *vcapture, 22762306a36Sopenharmony_ci enum vb2_buffer_state state) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct vimc_capture_buffer *vbuf, *node; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci spin_lock(&vcapture->qlock); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci list_for_each_entry_safe(vbuf, node, &vcapture->buf_list, list) { 23462306a36Sopenharmony_ci list_del(&vbuf->list); 23562306a36Sopenharmony_ci vb2_buffer_done(&vbuf->vb2.vb2_buf, state); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci spin_unlock(&vcapture->qlock); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int vimc_capture_start_streaming(struct vb2_queue *vq, unsigned int count) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct vimc_capture_device *vcapture = vb2_get_drv_priv(vq); 24462306a36Sopenharmony_ci int ret; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci vcapture->sequence = 0; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Start the media pipeline */ 24962306a36Sopenharmony_ci ret = video_device_pipeline_start(&vcapture->vdev, &vcapture->stream.pipe); 25062306a36Sopenharmony_ci if (ret) { 25162306a36Sopenharmony_ci vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_QUEUED); 25262306a36Sopenharmony_ci return ret; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci ret = vimc_streamer_s_stream(&vcapture->stream, &vcapture->ved, 1); 25662306a36Sopenharmony_ci if (ret) { 25762306a36Sopenharmony_ci video_device_pipeline_stop(&vcapture->vdev); 25862306a36Sopenharmony_ci vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_QUEUED); 25962306a36Sopenharmony_ci return ret; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* 26662306a36Sopenharmony_ci * Stop the stream engine. Any remaining buffers in the stream queue are 26762306a36Sopenharmony_ci * dequeued and passed on to the vb2 framework marked as STATE_ERROR. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_cistatic void vimc_capture_stop_streaming(struct vb2_queue *vq) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct vimc_capture_device *vcapture = vb2_get_drv_priv(vq); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci vimc_streamer_s_stream(&vcapture->stream, &vcapture->ved, 0); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Stop the media pipeline */ 27662306a36Sopenharmony_ci video_device_pipeline_stop(&vcapture->vdev); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Release all active buffers */ 27962306a36Sopenharmony_ci vimc_capture_return_all_buffers(vcapture, VB2_BUF_STATE_ERROR); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic void vimc_capture_buf_queue(struct vb2_buffer *vb2_buf) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct vimc_capture_device *vcapture = vb2_get_drv_priv(vb2_buf->vb2_queue); 28562306a36Sopenharmony_ci struct vimc_capture_buffer *buf = container_of(vb2_buf, 28662306a36Sopenharmony_ci struct vimc_capture_buffer, 28762306a36Sopenharmony_ci vb2.vb2_buf); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci spin_lock(&vcapture->qlock); 29062306a36Sopenharmony_ci list_add_tail(&buf->list, &vcapture->buf_list); 29162306a36Sopenharmony_ci spin_unlock(&vcapture->qlock); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int vimc_capture_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, 29562306a36Sopenharmony_ci unsigned int *nplanes, unsigned int sizes[], 29662306a36Sopenharmony_ci struct device *alloc_devs[]) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct vimc_capture_device *vcapture = vb2_get_drv_priv(vq); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (*nplanes) 30162306a36Sopenharmony_ci return sizes[0] < vcapture->format.sizeimage ? -EINVAL : 0; 30262306a36Sopenharmony_ci /* We don't support multiplanes for now */ 30362306a36Sopenharmony_ci *nplanes = 1; 30462306a36Sopenharmony_ci sizes[0] = vcapture->format.sizeimage; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int vimc_capture_buffer_prepare(struct vb2_buffer *vb) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct vimc_capture_device *vcapture = vb2_get_drv_priv(vb->vb2_queue); 31262306a36Sopenharmony_ci unsigned long size = vcapture->format.sizeimage; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 31562306a36Sopenharmony_ci dev_err(vcapture->ved.dev, "%s: buffer too small (%lu < %lu)\n", 31662306a36Sopenharmony_ci vcapture->vdev.name, vb2_plane_size(vb, 0), size); 31762306a36Sopenharmony_ci return -EINVAL; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic const struct vb2_ops vimc_capture_qops = { 32362306a36Sopenharmony_ci .start_streaming = vimc_capture_start_streaming, 32462306a36Sopenharmony_ci .stop_streaming = vimc_capture_stop_streaming, 32562306a36Sopenharmony_ci .buf_queue = vimc_capture_buf_queue, 32662306a36Sopenharmony_ci .queue_setup = vimc_capture_queue_setup, 32762306a36Sopenharmony_ci .buf_prepare = vimc_capture_buffer_prepare, 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * Since q->lock is set we can use the standard 33062306a36Sopenharmony_ci * vb2_ops_wait_prepare/finish helper functions. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 33362306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 33462306a36Sopenharmony_ci}; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic const struct media_entity_operations vimc_capture_mops = { 33762306a36Sopenharmony_ci .link_validate = vimc_vdev_link_validate, 33862306a36Sopenharmony_ci}; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void vimc_capture_release(struct vimc_ent_device *ved) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct vimc_capture_device *vcapture = 34362306a36Sopenharmony_ci container_of(ved, struct vimc_capture_device, ved); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci media_entity_cleanup(vcapture->ved.ent); 34662306a36Sopenharmony_ci kfree(vcapture); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void vimc_capture_unregister(struct vimc_ent_device *ved) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct vimc_capture_device *vcapture = 35262306a36Sopenharmony_ci container_of(ved, struct vimc_capture_device, ved); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci vb2_video_unregister_device(&vcapture->vdev); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic void *vimc_capture_process_frame(struct vimc_ent_device *ved, 35862306a36Sopenharmony_ci const void *frame) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct vimc_capture_device *vcapture = container_of(ved, struct vimc_capture_device, 36162306a36Sopenharmony_ci ved); 36262306a36Sopenharmony_ci struct vimc_capture_buffer *vimc_buf; 36362306a36Sopenharmony_ci void *vbuf; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci spin_lock(&vcapture->qlock); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Get the first entry of the list */ 36862306a36Sopenharmony_ci vimc_buf = list_first_entry_or_null(&vcapture->buf_list, 36962306a36Sopenharmony_ci typeof(*vimc_buf), list); 37062306a36Sopenharmony_ci if (!vimc_buf) { 37162306a36Sopenharmony_ci spin_unlock(&vcapture->qlock); 37262306a36Sopenharmony_ci return ERR_PTR(-EAGAIN); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Remove this entry from the list */ 37662306a36Sopenharmony_ci list_del(&vimc_buf->list); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci spin_unlock(&vcapture->qlock); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* Fill the buffer */ 38162306a36Sopenharmony_ci vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns(); 38262306a36Sopenharmony_ci vimc_buf->vb2.sequence = vcapture->sequence++; 38362306a36Sopenharmony_ci vimc_buf->vb2.field = vcapture->format.field; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci memcpy(vbuf, frame, vcapture->format.sizeimage); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Set it as ready */ 39062306a36Sopenharmony_ci vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0, 39162306a36Sopenharmony_ci vcapture->format.sizeimage); 39262306a36Sopenharmony_ci vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE); 39362306a36Sopenharmony_ci return NULL; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic struct vimc_ent_device *vimc_capture_add(struct vimc_device *vimc, 39762306a36Sopenharmony_ci const char *vcfg_name) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; 40062306a36Sopenharmony_ci const struct vimc_pix_map *vpix; 40162306a36Sopenharmony_ci struct vimc_capture_device *vcapture; 40262306a36Sopenharmony_ci struct video_device *vdev; 40362306a36Sopenharmony_ci struct vb2_queue *q; 40462306a36Sopenharmony_ci int ret; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Allocate the vimc_capture_device struct */ 40762306a36Sopenharmony_ci vcapture = kzalloc(sizeof(*vcapture), GFP_KERNEL); 40862306a36Sopenharmony_ci if (!vcapture) 40962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* Initialize the media entity */ 41262306a36Sopenharmony_ci vcapture->vdev.entity.name = vcfg_name; 41362306a36Sopenharmony_ci vcapture->vdev.entity.function = MEDIA_ENT_F_IO_V4L; 41462306a36Sopenharmony_ci vcapture->pad.flags = MEDIA_PAD_FL_SINK; 41562306a36Sopenharmony_ci ret = media_entity_pads_init(&vcapture->vdev.entity, 41662306a36Sopenharmony_ci 1, &vcapture->pad); 41762306a36Sopenharmony_ci if (ret) 41862306a36Sopenharmony_ci goto err_free_vcapture; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* Initialize the lock */ 42162306a36Sopenharmony_ci mutex_init(&vcapture->lock); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Initialize the vb2 queue */ 42462306a36Sopenharmony_ci q = &vcapture->queue; 42562306a36Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 42662306a36Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_DMABUF; 42762306a36Sopenharmony_ci if (vimc_allocator == VIMC_ALLOCATOR_VMALLOC) 42862306a36Sopenharmony_ci q->io_modes |= VB2_USERPTR; 42962306a36Sopenharmony_ci q->drv_priv = vcapture; 43062306a36Sopenharmony_ci q->buf_struct_size = sizeof(struct vimc_capture_buffer); 43162306a36Sopenharmony_ci q->ops = &vimc_capture_qops; 43262306a36Sopenharmony_ci q->mem_ops = vimc_allocator == VIMC_ALLOCATOR_DMA_CONTIG 43362306a36Sopenharmony_ci ? &vb2_dma_contig_memops : &vb2_vmalloc_memops; 43462306a36Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 43562306a36Sopenharmony_ci q->min_buffers_needed = 2; 43662306a36Sopenharmony_ci q->lock = &vcapture->lock; 43762306a36Sopenharmony_ci q->dev = v4l2_dev->dev; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci ret = vb2_queue_init(q); 44062306a36Sopenharmony_ci if (ret) { 44162306a36Sopenharmony_ci dev_err(vimc->mdev.dev, "%s: vb2 queue init failed (err=%d)\n", 44262306a36Sopenharmony_ci vcfg_name, ret); 44362306a36Sopenharmony_ci goto err_clean_m_ent; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* Initialize buffer list and its lock */ 44762306a36Sopenharmony_ci INIT_LIST_HEAD(&vcapture->buf_list); 44862306a36Sopenharmony_ci spin_lock_init(&vcapture->qlock); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Set default frame format */ 45162306a36Sopenharmony_ci vcapture->format = fmt_default; 45262306a36Sopenharmony_ci vpix = vimc_pix_map_by_pixelformat(vcapture->format.pixelformat); 45362306a36Sopenharmony_ci vcapture->format.bytesperline = vcapture->format.width * vpix->bpp; 45462306a36Sopenharmony_ci vcapture->format.sizeimage = vcapture->format.bytesperline * 45562306a36Sopenharmony_ci vcapture->format.height; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Fill the vimc_ent_device struct */ 45862306a36Sopenharmony_ci vcapture->ved.ent = &vcapture->vdev.entity; 45962306a36Sopenharmony_ci vcapture->ved.process_frame = vimc_capture_process_frame; 46062306a36Sopenharmony_ci vcapture->ved.vdev_get_format = vimc_capture_get_format; 46162306a36Sopenharmony_ci vcapture->ved.dev = vimc->mdev.dev; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Initialize the video_device struct */ 46462306a36Sopenharmony_ci vdev = &vcapture->vdev; 46562306a36Sopenharmony_ci vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING 46662306a36Sopenharmony_ci | V4L2_CAP_IO_MC; 46762306a36Sopenharmony_ci vdev->entity.ops = &vimc_capture_mops; 46862306a36Sopenharmony_ci vdev->release = video_device_release_empty; 46962306a36Sopenharmony_ci vdev->fops = &vimc_capture_fops; 47062306a36Sopenharmony_ci vdev->ioctl_ops = &vimc_capture_ioctl_ops; 47162306a36Sopenharmony_ci vdev->lock = &vcapture->lock; 47262306a36Sopenharmony_ci vdev->queue = q; 47362306a36Sopenharmony_ci vdev->v4l2_dev = v4l2_dev; 47462306a36Sopenharmony_ci vdev->vfl_dir = VFL_DIR_RX; 47562306a36Sopenharmony_ci strscpy(vdev->name, vcfg_name, sizeof(vdev->name)); 47662306a36Sopenharmony_ci video_set_drvdata(vdev, &vcapture->ved); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* Register the video_device with the v4l2 and the media framework */ 47962306a36Sopenharmony_ci ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 48062306a36Sopenharmony_ci if (ret) { 48162306a36Sopenharmony_ci dev_err(vimc->mdev.dev, "%s: video register failed (err=%d)\n", 48262306a36Sopenharmony_ci vcapture->vdev.name, ret); 48362306a36Sopenharmony_ci goto err_clean_m_ent; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return &vcapture->ved; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cierr_clean_m_ent: 48962306a36Sopenharmony_ci media_entity_cleanup(&vcapture->vdev.entity); 49062306a36Sopenharmony_cierr_free_vcapture: 49162306a36Sopenharmony_ci kfree(vcapture); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return ERR_PTR(ret); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistruct vimc_ent_type vimc_capture_type = { 49762306a36Sopenharmony_ci .add = vimc_capture_add, 49862306a36Sopenharmony_ci .unregister = vimc_capture_unregister, 49962306a36Sopenharmony_ci .release = vimc_capture_release 50062306a36Sopenharmony_ci}; 501