diff options
Diffstat (limited to 'drivers/media/platform/vimc')
-rw-r--r-- | drivers/media/platform/vimc/Kconfig | 15 | ||||
-rw-r--r-- | drivers/media/platform/vimc/Makefile | 6 | ||||
-rw-r--r-- | drivers/media/platform/vimc/vimc-capture.c | 480 | ||||
-rw-r--r-- | drivers/media/platform/vimc/vimc-common.c | 369 | ||||
-rw-r--r-- | drivers/media/platform/vimc/vimc-common.h | 224 | ||||
-rw-r--r-- | drivers/media/platform/vimc/vimc-core.c | 381 | ||||
-rw-r--r-- | drivers/media/platform/vimc/vimc-debayer.c | 581 | ||||
-rw-r--r-- | drivers/media/platform/vimc/vimc-scaler.c | 511 | ||||
-rw-r--r-- | drivers/media/platform/vimc/vimc-sensor.c | 376 | ||||
-rw-r--r-- | drivers/media/platform/vimc/vimc-streamer.c | 238 | ||||
-rw-r--r-- | drivers/media/platform/vimc/vimc-streamer.h | 44 |
11 files changed, 0 insertions, 3225 deletions
diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig deleted file mode 100644 index bd221d3e1a4a..000000000000 --- a/drivers/media/platform/vimc/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_VIMC - tristate "Virtual Media Controller Driver (VIMC)" - depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API - select VIDEOBUF2_VMALLOC - select VIDEO_V4L2_TPG - help - Skeleton driver for Virtual Media Controller - - This driver can be compared to the vivid driver for emulating - a media node that exposes a complex media topology. The topology - is hard coded for now but is meant to be highly configurable in - the future. - - When in doubt, say N. diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile deleted file mode 100644 index a53b2b532e9f..000000000000 --- a/drivers/media/platform/vimc/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -vimc-y := vimc-core.o vimc-common.o vimc-streamer.o vimc-capture.o \ - vimc-debayer.o vimc-scaler.o vimc-sensor.o - -obj-$(CONFIG_VIDEO_VIMC) += vimc.o - diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c deleted file mode 100644 index 23e740c1c5c0..000000000000 --- a/drivers/media/platform/vimc/vimc-capture.c +++ /dev/null @@ -1,480 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-capture.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> - */ - -#include <media/v4l2-ioctl.h> -#include <media/videobuf2-core.h> -#include <media/videobuf2-vmalloc.h> - -#include "vimc-common.h" -#include "vimc-streamer.h" - -struct vimc_cap_device { - struct vimc_ent_device ved; - struct video_device vdev; - struct v4l2_pix_format format; - struct vb2_queue queue; - struct list_head buf_list; - /* - * NOTE: in a real driver, a spin lock must be used to access the - * queue because the frames are generated from a hardware interruption - * and the isr is not allowed to sleep. - * Even if it is not necessary a spinlock in the vimc driver, we - * use it here as a code reference - */ - spinlock_t qlock; - struct mutex lock; - u32 sequence; - struct vimc_stream stream; - struct media_pad pad; -}; - -static const struct v4l2_pix_format fmt_default = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_RGB24, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_DEFAULT, -}; - -struct vimc_cap_buffer { - /* - * struct vb2_v4l2_buffer must be the first element - * the videobuf2 framework will allocate this struct based on - * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of - * memory as a vb2_buffer - */ - struct vb2_v4l2_buffer vb2; - struct list_head list; -}; - -static int vimc_cap_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, VIMC_PDEV_NAME, sizeof(cap->driver)); - strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", VIMC_PDEV_NAME); - - return 0; -} - -static void vimc_cap_get_format(struct vimc_ent_device *ved, - struct v4l2_pix_format *fmt) -{ - struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, - ved); - - *fmt = vcap->format; -} - -static int vimc_cap_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct vimc_cap_device *vcap = video_drvdata(file); - - f->fmt.pix = vcap->format; - - return 0; -} - -static int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct v4l2_pix_format *format = &f->fmt.pix; - const struct vimc_pix_map *vpix; - - format->width = clamp_t(u32, format->width, VIMC_FRAME_MIN_WIDTH, - VIMC_FRAME_MAX_WIDTH) & ~1; - format->height = clamp_t(u32, format->height, VIMC_FRAME_MIN_HEIGHT, - VIMC_FRAME_MAX_HEIGHT) & ~1; - - /* Don't accept a pixelformat that is not on the table */ - vpix = vimc_pix_map_by_pixelformat(format->pixelformat); - if (!vpix) { - format->pixelformat = fmt_default.pixelformat; - vpix = vimc_pix_map_by_pixelformat(format->pixelformat); - } - /* TODO: Add support for custom bytesperline values */ - format->bytesperline = format->width * vpix->bpp; - format->sizeimage = format->bytesperline * format->height; - - if (format->field == V4L2_FIELD_ANY) - format->field = fmt_default.field; - - vimc_colorimetry_clamp(format); - - return 0; -} - -static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct vimc_cap_device *vcap = video_drvdata(file); - int ret; - - /* Do not change the format while stream is on */ - if (vb2_is_busy(&vcap->queue)) - return -EBUSY; - - ret = vimc_cap_try_fmt_vid_cap(file, priv, f); - if (ret) - return ret; - - dev_dbg(vcap->ved.dev, "%s: format update: " - "old:%dx%d (0x%x, %d, %d, %d, %d) " - "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcap->vdev.name, - /* old */ - vcap->format.width, vcap->format.height, - vcap->format.pixelformat, vcap->format.colorspace, - vcap->format.quantization, vcap->format.xfer_func, - vcap->format.ycbcr_enc, - /* new */ - f->fmt.pix.width, f->fmt.pix.height, - f->fmt.pix.pixelformat, f->fmt.pix.colorspace, - f->fmt.pix.quantization, f->fmt.pix.xfer_func, - f->fmt.pix.ycbcr_enc); - - vcap->format = f->fmt.pix; - - return 0; -} - -static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - const struct vimc_pix_map *vpix = vimc_pix_map_by_index(f->index); - - if (!vpix) - return -EINVAL; - - f->pixelformat = vpix->pixelformat; - - return 0; -} - -static int vimc_cap_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - const struct vimc_pix_map *vpix; - - if (fsize->index) - return -EINVAL; - - /* Only accept code in the pix map table */ - vpix = vimc_pix_map_by_code(fsize->pixel_format); - if (!vpix) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = VIMC_FRAME_MIN_WIDTH; - fsize->stepwise.max_width = VIMC_FRAME_MAX_WIDTH; - fsize->stepwise.min_height = VIMC_FRAME_MIN_HEIGHT; - fsize->stepwise.max_height = VIMC_FRAME_MAX_HEIGHT; - fsize->stepwise.step_width = 1; - fsize->stepwise.step_height = 1; - - return 0; -} - -static const struct v4l2_file_operations vimc_cap_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = { - .vidioc_querycap = vimc_cap_querycap, - - .vidioc_g_fmt_vid_cap = vimc_cap_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vimc_cap_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vimc_cap_try_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap, - .vidioc_enum_framesizes = vimc_cap_enum_framesizes, - - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, -}; - -static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap, - enum vb2_buffer_state state) -{ - struct vimc_cap_buffer *vbuf, *node; - - spin_lock(&vcap->qlock); - - list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) { - list_del(&vbuf->list); - vb2_buffer_done(&vbuf->vb2.vb2_buf, state); - } - - spin_unlock(&vcap->qlock); -} - -static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); - struct media_entity *entity = &vcap->vdev.entity; - int ret; - - vcap->sequence = 0; - - /* Start the media pipeline */ - ret = media_pipeline_start(entity, &vcap->stream.pipe); - if (ret) { - vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); - return ret; - } - - ret = vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 1); - if (ret) { - media_pipeline_stop(entity); - vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); - return ret; - } - - return 0; -} - -/* - * Stop the stream engine. Any remaining buffers in the stream queue are - * dequeued and passed on to the vb2 framework marked as STATE_ERROR. - */ -static void vimc_cap_stop_streaming(struct vb2_queue *vq) -{ - struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); - - vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 0); - - /* Stop the media pipeline */ - media_pipeline_stop(&vcap->vdev.entity); - - /* Release all active buffers */ - vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR); -} - -static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf) -{ - struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue); - struct vimc_cap_buffer *buf = container_of(vb2_buf, - struct vimc_cap_buffer, - vb2.vb2_buf); - - spin_lock(&vcap->qlock); - list_add_tail(&buf->list, &vcap->buf_list); - spin_unlock(&vcap->qlock); -} - -static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); - - if (*nplanes) - return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0; - /* We don't support multiplanes for now */ - *nplanes = 1; - sizes[0] = vcap->format.sizeimage; - - return 0; -} - -static int vimc_cap_buffer_prepare(struct vb2_buffer *vb) -{ - struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue); - unsigned long size = vcap->format.sizeimage; - - if (vb2_plane_size(vb, 0) < size) { - dev_err(vcap->ved.dev, "%s: buffer too small (%lu < %lu)\n", - vcap->vdev.name, vb2_plane_size(vb, 0), size); - return -EINVAL; - } - return 0; -} - -static const struct vb2_ops vimc_cap_qops = { - .start_streaming = vimc_cap_start_streaming, - .stop_streaming = vimc_cap_stop_streaming, - .buf_queue = vimc_cap_buf_queue, - .queue_setup = vimc_cap_queue_setup, - .buf_prepare = vimc_cap_buffer_prepare, - /* - * Since q->lock is set we can use the standard - * vb2_ops_wait_prepare/finish helper functions. - */ - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static const struct media_entity_operations vimc_cap_mops = { - .link_validate = vimc_vdev_link_validate, -}; - -void vimc_cap_release(struct vimc_ent_device *ved) -{ - struct vimc_cap_device *vcap = - container_of(ved, struct vimc_cap_device, ved); - - media_entity_cleanup(vcap->ved.ent); - kfree(vcap); -} - -void vimc_cap_unregister(struct vimc_ent_device *ved) -{ - struct vimc_cap_device *vcap = - container_of(ved, struct vimc_cap_device, ved); - - vb2_queue_release(&vcap->queue); - video_unregister_device(&vcap->vdev); -} - -static void *vimc_cap_process_frame(struct vimc_ent_device *ved, - const void *frame) -{ - struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, - ved); - struct vimc_cap_buffer *vimc_buf; - void *vbuf; - - spin_lock(&vcap->qlock); - - /* Get the first entry of the list */ - vimc_buf = list_first_entry_or_null(&vcap->buf_list, - typeof(*vimc_buf), list); - if (!vimc_buf) { - spin_unlock(&vcap->qlock); - return ERR_PTR(-EAGAIN); - } - - /* Remove this entry from the list */ - list_del(&vimc_buf->list); - - spin_unlock(&vcap->qlock); - - /* Fill the buffer */ - vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns(); - vimc_buf->vb2.sequence = vcap->sequence++; - vimc_buf->vb2.field = vcap->format.field; - - vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0); - - memcpy(vbuf, frame, vcap->format.sizeimage); - - /* Set it as ready */ - vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0, - vcap->format.sizeimage); - vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE); - return NULL; -} - -struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, - const char *vcfg_name) -{ - struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; - const struct vimc_pix_map *vpix; - struct vimc_cap_device *vcap; - struct video_device *vdev; - struct vb2_queue *q; - int ret; - - /* Allocate the vimc_cap_device struct */ - vcap = kzalloc(sizeof(*vcap), GFP_KERNEL); - if (!vcap) - return NULL; - - /* Initialize the media entity */ - vcap->vdev.entity.name = vcfg_name; - vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L; - vcap->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vcap->vdev.entity, - 1, &vcap->pad); - if (ret) - goto err_free_vcap; - - /* Initialize the lock */ - mutex_init(&vcap->lock); - - /* Initialize the vb2 queue */ - q = &vcap->queue; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; - q->drv_priv = vcap; - q->buf_struct_size = sizeof(struct vimc_cap_buffer); - q->ops = &vimc_cap_qops; - q->mem_ops = &vb2_vmalloc_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 2; - q->lock = &vcap->lock; - - ret = vb2_queue_init(q); - if (ret) { - dev_err(vimc->mdev.dev, "%s: vb2 queue init failed (err=%d)\n", - vcfg_name, ret); - goto err_clean_m_ent; - } - - /* Initialize buffer list and its lock */ - INIT_LIST_HEAD(&vcap->buf_list); - spin_lock_init(&vcap->qlock); - - /* Set default frame format */ - vcap->format = fmt_default; - vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat); - vcap->format.bytesperline = vcap->format.width * vpix->bpp; - vcap->format.sizeimage = vcap->format.bytesperline * - vcap->format.height; - - /* Fill the vimc_ent_device struct */ - vcap->ved.ent = &vcap->vdev.entity; - vcap->ved.process_frame = vimc_cap_process_frame; - vcap->ved.vdev_get_format = vimc_cap_get_format; - vcap->ved.dev = vimc->mdev.dev; - - /* Initialize the video_device struct */ - vdev = &vcap->vdev; - vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - vdev->entity.ops = &vimc_cap_mops; - vdev->release = video_device_release_empty; - vdev->fops = &vimc_cap_fops; - vdev->ioctl_ops = &vimc_cap_ioctl_ops; - vdev->lock = &vcap->lock; - vdev->queue = q; - vdev->v4l2_dev = v4l2_dev; - vdev->vfl_dir = VFL_DIR_RX; - strscpy(vdev->name, vcfg_name, sizeof(vdev->name)); - video_set_drvdata(vdev, &vcap->ved); - - /* Register the video_device with the v4l2 and the media framework */ - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret) { - dev_err(vimc->mdev.dev, "%s: video register failed (err=%d)\n", - vcap->vdev.name, ret); - goto err_release_queue; - } - - return &vcap->ved; - -err_release_queue: - vb2_queue_release(q); -err_clean_m_ent: - media_entity_cleanup(&vcap->vdev.entity); -err_free_vcap: - kfree(vcap); - - return NULL; -} diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c deleted file mode 100644 index c95c17c048f2..000000000000 --- a/drivers/media/platform/vimc/vimc-common.c +++ /dev/null @@ -1,369 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-common.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> - */ - -#include <linux/init.h> -#include <linux/module.h> - -#include "vimc-common.h" - -/* - * NOTE: non-bayer formats need to come first (necessary for enum_mbus_code - * in the scaler) - */ -static const struct vimc_pix_map vimc_pix_map_list[] = { - /* TODO: add all missing formats */ - - /* RGB formats */ - { - .code = MEDIA_BUS_FMT_BGR888_1X24, - .pixelformat = V4L2_PIX_FMT_BGR24, - .bpp = 3, - .bayer = false, - }, - { - .code = MEDIA_BUS_FMT_RGB888_1X24, - .pixelformat = V4L2_PIX_FMT_RGB24, - .bpp = 3, - .bayer = false, - }, - { - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .pixelformat = V4L2_PIX_FMT_ARGB32, - .bpp = 4, - .bayer = false, - }, - - /* Bayer formats */ - { - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .bpp = 1, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .pixelformat = V4L2_PIX_FMT_SGBRG8, - .bpp = 1, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .pixelformat = V4L2_PIX_FMT_SGRBG8, - .bpp = 1, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .pixelformat = V4L2_PIX_FMT_SRGGB8, - .bpp = 1, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .pixelformat = V4L2_PIX_FMT_SBGGR10, - .bpp = 2, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .pixelformat = V4L2_PIX_FMT_SGBRG10, - .bpp = 2, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .pixelformat = V4L2_PIX_FMT_SGRBG10, - .bpp = 2, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .pixelformat = V4L2_PIX_FMT_SRGGB10, - .bpp = 2, - .bayer = true, - }, - - /* 10bit raw bayer a-law compressed to 8 bits */ - { - .code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, - .pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8, - .bpp = 1, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, - .pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8, - .bpp = 1, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, - .pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8, - .bpp = 1, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, - .pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8, - .bpp = 1, - .bayer = true, - }, - - /* 10bit raw bayer DPCM compressed to 8 bits */ - { - .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, - .pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8, - .bpp = 1, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, - .pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8, - .bpp = 1, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, - .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, - .bpp = 1, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, - .pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8, - .bpp = 1, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .pixelformat = V4L2_PIX_FMT_SBGGR12, - .bpp = 2, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .pixelformat = V4L2_PIX_FMT_SGBRG12, - .bpp = 2, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .pixelformat = V4L2_PIX_FMT_SGRBG12, - .bpp = 2, - .bayer = true, - }, - { - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .pixelformat = V4L2_PIX_FMT_SRGGB12, - .bpp = 2, - .bayer = true, - }, -}; - -bool vimc_is_source(struct media_entity *ent) -{ - unsigned int i; - - for (i = 0; i < ent->num_pads; i++) - if (ent->pads[i].flags & MEDIA_PAD_FL_SINK) - return false; - return true; -} - -const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i) -{ - if (i >= ARRAY_SIZE(vimc_pix_map_list)) - return NULL; - - return &vimc_pix_map_list[i]; -} - -const struct vimc_pix_map *vimc_pix_map_by_code(u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) { - if (vimc_pix_map_list[i].code == code) - return &vimc_pix_map_list[i]; - } - return NULL; -} - -const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) { - if (vimc_pix_map_list[i].pixelformat == pixelformat) - return &vimc_pix_map_list[i]; - } - return NULL; -} - -static int vimc_get_pix_format(struct media_pad *pad, - struct v4l2_pix_format *fmt) -{ - if (is_media_entity_v4l2_subdev(pad->entity)) { - struct v4l2_subdev *sd = - media_entity_to_v4l2_subdev(pad->entity); - struct v4l2_subdev_format sd_fmt; - const struct vimc_pix_map *pix_map; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = pad->index; - - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - v4l2_fill_pix_format(fmt, &sd_fmt.format); - pix_map = vimc_pix_map_by_code(sd_fmt.format.code); - fmt->pixelformat = pix_map->pixelformat; - } else if (is_media_entity_v4l2_video_device(pad->entity)) { - struct video_device *vdev = container_of(pad->entity, - struct video_device, - entity); - struct vimc_ent_device *ved = video_get_drvdata(vdev); - - if (!ved->vdev_get_format) - return -ENOIOCTLCMD; - - ved->vdev_get_format(ved, fmt); - } else { - return -EINVAL; - } - - return 0; -} - -int vimc_vdev_link_validate(struct media_link *link) -{ - struct v4l2_pix_format source_fmt, sink_fmt; - int ret; - - ret = vimc_get_pix_format(link->source, &source_fmt); - if (ret) - return ret; - - ret = vimc_get_pix_format(link->sink, &sink_fmt); - if (ret) - return ret; - - pr_info("vimc link validate: " - "%s:src:%dx%d (0x%x, %d, %d, %d, %d) " - "%s:snk:%dx%d (0x%x, %d, %d, %d, %d)\n", - /* src */ - link->source->entity->name, - source_fmt.width, source_fmt.height, - source_fmt.pixelformat, source_fmt.colorspace, - source_fmt.quantization, source_fmt.xfer_func, - source_fmt.ycbcr_enc, - /* sink */ - link->sink->entity->name, - sink_fmt.width, sink_fmt.height, - sink_fmt.pixelformat, sink_fmt.colorspace, - sink_fmt.quantization, sink_fmt.xfer_func, - sink_fmt.ycbcr_enc); - - /* The width, height and pixelformat must match. */ - if (source_fmt.width != sink_fmt.width || - source_fmt.height != sink_fmt.height || - source_fmt.pixelformat != sink_fmt.pixelformat) - return -EPIPE; - - /* - * The field order must match, or the sink field order must be NONE - * to support interlaced hardware connected to bridges that support - * progressive formats only. - */ - if (source_fmt.field != sink_fmt.field && - sink_fmt.field != V4L2_FIELD_NONE) - return -EPIPE; - - /* - * If colorspace is DEFAULT, then assume all the colorimetry is also - * DEFAULT, return 0 to skip comparing the other colorimetry parameters - */ - if (source_fmt.colorspace == V4L2_COLORSPACE_DEFAULT || - sink_fmt.colorspace == V4L2_COLORSPACE_DEFAULT) - return 0; - - /* Colorspace must match. */ - if (source_fmt.colorspace != sink_fmt.colorspace) - return -EPIPE; - - /* Colorimetry must match if they are not set to DEFAULT */ - if (source_fmt.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT && - sink_fmt.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT && - source_fmt.ycbcr_enc != sink_fmt.ycbcr_enc) - return -EPIPE; - - if (source_fmt.quantization != V4L2_QUANTIZATION_DEFAULT && - sink_fmt.quantization != V4L2_QUANTIZATION_DEFAULT && - source_fmt.quantization != sink_fmt.quantization) - return -EPIPE; - - if (source_fmt.xfer_func != V4L2_XFER_FUNC_DEFAULT && - sink_fmt.xfer_func != V4L2_XFER_FUNC_DEFAULT && - source_fmt.xfer_func != sink_fmt.xfer_func) - return -EPIPE; - - return 0; -} - -static const struct media_entity_operations vimc_ent_sd_mops = { - .link_validate = v4l2_subdev_link_validate, -}; - -int vimc_ent_sd_register(struct vimc_ent_device *ved, - struct v4l2_subdev *sd, - struct v4l2_device *v4l2_dev, - const char *const name, - u32 function, - u16 num_pads, - struct media_pad *pads, - const struct v4l2_subdev_ops *sd_ops) -{ - int ret; - - /* Fill the vimc_ent_device struct */ - ved->ent = &sd->entity; - - /* Initialize the subdev */ - v4l2_subdev_init(sd, sd_ops); - sd->entity.function = function; - sd->entity.ops = &vimc_ent_sd_mops; - sd->owner = THIS_MODULE; - strscpy(sd->name, name, sizeof(sd->name)); - v4l2_set_subdevdata(sd, ved); - - /* Expose this subdev to user space */ - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - if (sd->ctrl_handler) - sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS; - - /* Initialize the media entity */ - ret = media_entity_pads_init(&sd->entity, num_pads, pads); - if (ret) - return ret; - - /* Register the subdev with the v4l2 and the media framework */ - ret = v4l2_device_register_subdev(v4l2_dev, sd); - if (ret) { - dev_err(v4l2_dev->dev, - "%s: subdev register failed (err=%d)\n", - name, ret); - goto err_clean_m_ent; - } - - return 0; - -err_clean_m_ent: - media_entity_cleanup(&sd->entity); - return ret; -} diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h deleted file mode 100644 index 616d5a6b0754..000000000000 --- a/drivers/media/platform/vimc/vimc-common.h +++ /dev/null @@ -1,224 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * vimc-common.h Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> - */ - -#ifndef _VIMC_COMMON_H_ -#define _VIMC_COMMON_H_ - -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <media/media-device.h> -#include <media/v4l2-device.h> - -#define VIMC_PDEV_NAME "vimc" - -/* VIMC-specific controls */ -#define VIMC_CID_VIMC_BASE (0x00f00000 | 0xf000) -#define VIMC_CID_VIMC_CLASS (0x00f00000 | 1) -#define VIMC_CID_TEST_PATTERN (VIMC_CID_VIMC_BASE + 0) -#define VIMC_CID_MEAN_WIN_SIZE (VIMC_CID_VIMC_BASE + 1) - -#define VIMC_FRAME_MAX_WIDTH 4096 -#define VIMC_FRAME_MAX_HEIGHT 2160 -#define VIMC_FRAME_MIN_WIDTH 16 -#define VIMC_FRAME_MIN_HEIGHT 16 - -#define VIMC_FRAME_INDEX(lin, col, width, bpp) ((lin * width + col) * bpp) - -/* Source and sink pad checks */ -#define VIMC_IS_SRC(pad) (pad) -#define VIMC_IS_SINK(pad) (!(pad)) - -/** - * struct vimc_colorimetry_clamp - Adjust colorimetry parameters - * - * @fmt: the pointer to struct v4l2_pix_format or - * struct v4l2_mbus_framefmt - * - * Entities must check if colorimetry given by the userspace is valid, if not - * then set them as DEFAULT - */ -#define vimc_colorimetry_clamp(fmt) \ -do { \ - if ((fmt)->colorspace == V4L2_COLORSPACE_DEFAULT \ - || (fmt)->colorspace > V4L2_COLORSPACE_DCI_P3) { \ - (fmt)->colorspace = V4L2_COLORSPACE_DEFAULT; \ - (fmt)->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; \ - (fmt)->quantization = V4L2_QUANTIZATION_DEFAULT; \ - (fmt)->xfer_func = V4L2_XFER_FUNC_DEFAULT; \ - } \ - if ((fmt)->ycbcr_enc > V4L2_YCBCR_ENC_SMPTE240M) \ - (fmt)->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; \ - if ((fmt)->quantization > V4L2_QUANTIZATION_LIM_RANGE) \ - (fmt)->quantization = V4L2_QUANTIZATION_DEFAULT; \ - if ((fmt)->xfer_func > V4L2_XFER_FUNC_SMPTE2084) \ - (fmt)->xfer_func = V4L2_XFER_FUNC_DEFAULT; \ -} while (0) - -/** - * struct vimc_pix_map - maps media bus code with v4l2 pixel format - * - * @code: media bus format code defined by MEDIA_BUS_FMT_* macros - * @bbp: number of bytes each pixel occupies - * @pixelformat: pixel format devined by V4L2_PIX_FMT_* macros - * - * Struct which matches the MEDIA_BUS_FMT_* codes with the corresponding - * V4L2_PIX_FMT_* fourcc pixelformat and its bytes per pixel (bpp) - */ -struct vimc_pix_map { - unsigned int code; - unsigned int bpp; - u32 pixelformat; - bool bayer; -}; - -/** - * struct vimc_ent_device - core struct that represents an entity in the - * topology - * - * @dev: a pointer of the device struct of the driver - * @ent: the pointer to struct media_entity for the node - * @process_frame: callback send a frame to that node - * @vdev_get_format: callback that returns the current format a pad, used - * only when is_media_entity_v4l2_video_device(ent) returns - * true - * - * Each node of the topology must create a vimc_ent_device struct. Depending on - * the node it will be of an instance of v4l2_subdev or video_device struct - * where both contains a struct media_entity. - * Those structures should embedded the vimc_ent_device struct through - * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the - * vimc_ent_device struct to be retrieved from the corresponding struct - * media_entity - */ -struct vimc_ent_device { - struct device *dev; - struct media_entity *ent; - void * (*process_frame)(struct vimc_ent_device *ved, - const void *frame); - void (*vdev_get_format)(struct vimc_ent_device *ved, - struct v4l2_pix_format *fmt); -}; - -/** - * struct vimc_device - main device for vimc driver - * - * @pipe_cfg pointer to the vimc pipeline configuration structure - * @ent_devs array of vimc_ent_device pointers - * @mdev the associated media_device parent - * @v4l2_dev Internal v4l2 parent device - */ -struct vimc_device { - const struct vimc_pipeline_config *pipe_cfg; - struct vimc_ent_device **ent_devs; - struct media_device mdev; - struct v4l2_device v4l2_dev; -}; - -/** - * struct vimc_ent_config Structure which describes individual - * configuration for each entity - * - * @name entity name - * @ved pointer to vimc_ent_device (a node in the - * topology) - * @add initializes and registers - * vim entity - called from vimc-core - * @unregister unregisters vimc entity - called from vimc-core - * @release releases vimc entity - called from the v4l2_dev - * release callback - */ -struct vimc_ent_config { - const char *name; - struct vimc_ent_device *(*add)(struct vimc_device *vimc, - const char *vcfg_name); - void (*unregister)(struct vimc_ent_device *ved); - void (*release)(struct vimc_ent_device *ved); -}; - -/** - * vimc_is_source - returns true if the entity has only source pads - * - * @ent: pointer to &struct media_entity - * - */ -bool vimc_is_source(struct media_entity *ent); - -/* prototypes for vimc_ent_config hooks */ -struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_cap_unregister(struct vimc_ent_device *ved); -void vimc_cap_release(struct vimc_ent_device *ved); - -struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_deb_release(struct vimc_ent_device *ved); - -struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_sca_release(struct vimc_ent_device *ved); - -struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, - const char *vcfg_name); -void vimc_sen_release(struct vimc_ent_device *ved); - -/** - * vimc_pix_map_by_index - get vimc_pix_map struct by its index - * - * @i: index of the vimc_pix_map struct in vimc_pix_map_list - */ -const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i); - -/** - * vimc_pix_map_by_code - get vimc_pix_map struct by media bus code - * - * @code: media bus format code defined by MEDIA_BUS_FMT_* macros - */ -const struct vimc_pix_map *vimc_pix_map_by_code(u32 code); - -/** - * vimc_pix_map_by_pixelformat - get vimc_pix_map struct by v4l2 pixel format - * - * @pixelformat: pixel format devined by V4L2_PIX_FMT_* macros - */ -const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat); - -/** - * vimc_ent_sd_register - initialize and register a subdev node - * - * @ved: the vimc_ent_device struct to be initialize - * @sd: the v4l2_subdev struct to be initialize and registered - * @v4l2_dev: the v4l2 device to register the v4l2_subdev - * @name: name of the sub-device. Please notice that the name must be - * unique. - * @function: media entity function defined by MEDIA_ENT_F_* macros - * @num_pads: number of pads to initialize - * @pads: the array of pads of the entity, the caller should set the - flags of the pads - * @sd_ops: pointer to &struct v4l2_subdev_ops. - * - * Helper function initialize and register the struct vimc_ent_device and struct - * v4l2_subdev which represents a subdev node in the topology - */ -int vimc_ent_sd_register(struct vimc_ent_device *ved, - struct v4l2_subdev *sd, - struct v4l2_device *v4l2_dev, - const char *const name, - u32 function, - u16 num_pads, - struct media_pad *pads, - const struct v4l2_subdev_ops *sd_ops); - -/** - * vimc_vdev_link_validate - validates a media link - * - * @link: pointer to &struct media_link - * - * This function calls validates if a media link is valid for streaming. - */ -int vimc_vdev_link_validate(struct media_link *link); - -#endif diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c deleted file mode 100644 index 339126e565dc..000000000000 --- a/drivers/media/platform/vimc/vimc-core.c +++ /dev/null @@ -1,381 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-core.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <media/media-device.h> -#include <media/v4l2-device.h> - -#include "vimc-common.h" - -#define VIMC_MDEV_MODEL_NAME "VIMC MDEV" - -#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \ - .src_ent = src, \ - .src_pad = srcpad, \ - .sink_ent = sink, \ - .sink_pad = sinkpad, \ - .flags = link_flags, \ -} - -/* Structure which describes links between entities */ -struct vimc_ent_link { - unsigned int src_ent; - u16 src_pad; - unsigned int sink_ent; - u16 sink_pad; - u32 flags; -}; - -/* Structure which describes the whole topology */ -struct vimc_pipeline_config { - const struct vimc_ent_config *ents; - size_t num_ents; - const struct vimc_ent_link *links; - size_t num_links; -}; - -/* -------------------------------------------------------------------------- - * Topology Configuration - */ - -static struct vimc_ent_config ent_config[] = { - { - .name = "Sensor A", - .add = vimc_sen_add, - .release = vimc_sen_release, - }, - { - .name = "Sensor B", - .add = vimc_sen_add, - .release = vimc_sen_release, - }, - { - .name = "Debayer A", - .add = vimc_deb_add, - .release = vimc_deb_release, - }, - { - .name = "Debayer B", - .add = vimc_deb_add, - .release = vimc_deb_release, - }, - { - .name = "Raw Capture 0", - .add = vimc_cap_add, - .unregister = vimc_cap_unregister, - .release = vimc_cap_release, - }, - { - .name = "Raw Capture 1", - .add = vimc_cap_add, - .unregister = vimc_cap_unregister, - .release = vimc_cap_release, - }, - { - /* TODO: change this to vimc-input when it is implemented */ - .name = "RGB/YUV Input", - .add = vimc_sen_add, - .release = vimc_sen_release, - }, - { - .name = "Scaler", - .add = vimc_sca_add, - .release = vimc_sca_release, - }, - { - .name = "RGB/YUV Capture", - .add = vimc_cap_add, - .unregister = vimc_cap_unregister, - .release = vimc_cap_release, - }, -}; - -static const struct vimc_ent_link ent_links[] = { - /* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */ - VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), - /* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */ - VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), - /* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */ - VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), - /* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */ - VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), - /* Link: Debayer A (Pad 1)->(Pad 0) Scaler */ - VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED), - /* Link: Debayer B (Pad 1)->(Pad 0) Scaler */ - VIMC_ENT_LINK(3, 1, 7, 0, 0), - /* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */ - VIMC_ENT_LINK(6, 0, 7, 0, 0), - /* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */ - VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), -}; - -static struct vimc_pipeline_config pipe_cfg = { - .ents = ent_config, - .num_ents = ARRAY_SIZE(ent_config), - .links = ent_links, - .num_links = ARRAY_SIZE(ent_links) -}; - -/* -------------------------------------------------------------------------- */ - -static void vimc_rm_links(struct vimc_device *vimc) -{ - unsigned int i; - - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) - media_entity_remove_links(vimc->ent_devs[i]->ent); -} - -static int vimc_create_links(struct vimc_device *vimc) -{ - unsigned int i; - int ret; - - /* Initialize the links between entities */ - for (i = 0; i < vimc->pipe_cfg->num_links; i++) { - const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i]; - - struct vimc_ent_device *ved_src = - vimc->ent_devs[link->src_ent]; - struct vimc_ent_device *ved_sink = - vimc->ent_devs[link->sink_ent]; - - ret = media_create_pad_link(ved_src->ent, link->src_pad, - ved_sink->ent, link->sink_pad, - link->flags); - if (ret) - goto err_rm_links; - } - - return 0; - -err_rm_links: - vimc_rm_links(vimc); - return ret; -} - -static int vimc_add_subdevs(struct vimc_device *vimc) -{ - unsigned int i; - - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { - dev_dbg(vimc->mdev.dev, "new entity for %s\n", - vimc->pipe_cfg->ents[i].name); - vimc->ent_devs[i] = vimc->pipe_cfg->ents[i].add(vimc, - vimc->pipe_cfg->ents[i].name); - if (!vimc->ent_devs[i]) { - dev_err(vimc->mdev.dev, "add new entity for %s\n", - vimc->pipe_cfg->ents[i].name); - return -EINVAL; - } - } - return 0; -} - -static void vimc_release_subdevs(struct vimc_device *vimc) -{ - unsigned int i; - - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) - if (vimc->ent_devs[i]) - vimc->pipe_cfg->ents[i].release(vimc->ent_devs[i]); -} - -static void vimc_unregister_subdevs(struct vimc_device *vimc) -{ - unsigned int i; - - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) - if (vimc->ent_devs[i] && vimc->pipe_cfg->ents[i].unregister) - vimc->pipe_cfg->ents[i].unregister(vimc->ent_devs[i]); -} - -static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev) -{ - struct vimc_device *vimc = - container_of(v4l2_dev, struct vimc_device, v4l2_dev); - - vimc_release_subdevs(vimc); - media_device_cleanup(&vimc->mdev); - kfree(vimc->ent_devs); - kfree(vimc); -} - -static int vimc_register_devices(struct vimc_device *vimc) -{ - int ret; - - /* Register the v4l2 struct */ - ret = v4l2_device_register(vimc->mdev.dev, &vimc->v4l2_dev); - if (ret) { - dev_err(vimc->mdev.dev, - "v4l2 device register failed (err=%d)\n", ret); - return ret; - } - /* allocate ent_devs */ - vimc->ent_devs = kcalloc(vimc->pipe_cfg->num_ents, - sizeof(*vimc->ent_devs), GFP_KERNEL); - if (!vimc->ent_devs) { - ret = -ENOMEM; - goto err_v4l2_unregister; - } - - /* Invoke entity config hooks to initialize and register subdevs */ - ret = vimc_add_subdevs(vimc); - if (ret) - /* remove sundevs that got added */ - goto err_rm_subdevs; - - /* Initialize links */ - ret = vimc_create_links(vimc); - if (ret) - goto err_rm_subdevs; - - /* Register the media device */ - ret = media_device_register(&vimc->mdev); - if (ret) { - dev_err(vimc->mdev.dev, - "media device register failed (err=%d)\n", ret); - goto err_rm_subdevs; - } - - /* Expose all subdev's nodes*/ - ret = v4l2_device_register_subdev_nodes(&vimc->v4l2_dev); - if (ret) { - dev_err(vimc->mdev.dev, - "vimc subdev nodes registration failed (err=%d)\n", - ret); - goto err_mdev_unregister; - } - - return 0; - -err_mdev_unregister: - media_device_unregister(&vimc->mdev); -err_rm_subdevs: - vimc_unregister_subdevs(vimc); - vimc_release_subdevs(vimc); - kfree(vimc->ent_devs); -err_v4l2_unregister: - v4l2_device_unregister(&vimc->v4l2_dev); - - return ret; -} - -static void vimc_unregister(struct vimc_device *vimc) -{ - vimc_unregister_subdevs(vimc); - media_device_unregister(&vimc->mdev); - v4l2_device_unregister(&vimc->v4l2_dev); -} - -static int vimc_probe(struct platform_device *pdev) -{ - struct vimc_device *vimc; - int ret; - - dev_dbg(&pdev->dev, "probe"); - - vimc = kzalloc(sizeof(*vimc), GFP_KERNEL); - if (!vimc) - return -ENOMEM; - - vimc->pipe_cfg = &pipe_cfg; - - /* Link the media device within the v4l2_device */ - vimc->v4l2_dev.mdev = &vimc->mdev; - - /* Initialize media device */ - strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME, - sizeof(vimc->mdev.model)); - snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info), - "platform:%s", VIMC_PDEV_NAME); - vimc->mdev.dev = &pdev->dev; - media_device_init(&vimc->mdev); - - ret = vimc_register_devices(vimc); - if (ret) { - media_device_cleanup(&vimc->mdev); - kfree(vimc); - return ret; - } - /* - * the release cb is set only after successful registration. - * if the registration fails, we release directly from probe - */ - - vimc->v4l2_dev.release = vimc_v4l2_dev_release; - platform_set_drvdata(pdev, vimc); - return 0; -} - -static int vimc_remove(struct platform_device *pdev) -{ - struct vimc_device *vimc = platform_get_drvdata(pdev); - - dev_dbg(&pdev->dev, "remove"); - - vimc_unregister(vimc); - v4l2_device_put(&vimc->v4l2_dev); - - return 0; -} - -static void vimc_dev_release(struct device *dev) -{ -} - -static struct platform_device vimc_pdev = { - .name = VIMC_PDEV_NAME, - .dev.release = vimc_dev_release, -}; - -static struct platform_driver vimc_pdrv = { - .probe = vimc_probe, - .remove = vimc_remove, - .driver = { - .name = VIMC_PDEV_NAME, - }, -}; - -static int __init vimc_init(void) -{ - int ret; - - ret = platform_device_register(&vimc_pdev); - if (ret) { - dev_err(&vimc_pdev.dev, - "platform device registration failed (err=%d)\n", ret); - return ret; - } - - ret = platform_driver_register(&vimc_pdrv); - if (ret) { - dev_err(&vimc_pdev.dev, - "platform driver registration failed (err=%d)\n", ret); - platform_driver_unregister(&vimc_pdrv); - return ret; - } - - return 0; -} - -static void __exit vimc_exit(void) -{ - platform_driver_unregister(&vimc_pdrv); - - platform_device_unregister(&vimc_pdev); -} - -module_init(vimc_init); -module_exit(vimc_exit); - -MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)"); -MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c deleted file mode 100644 index baf6bf9f65b5..000000000000 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ /dev/null @@ -1,581 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-debayer.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> - */ - -#include <linux/moduleparam.h> -#include <linux/platform_device.h> -#include <linux/vmalloc.h> -#include <linux/v4l2-mediabus.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-event.h> -#include <media/v4l2-subdev.h> - -#include "vimc-common.h" - -enum vimc_deb_rgb_colors { - VIMC_DEB_RED = 0, - VIMC_DEB_GREEN = 1, - VIMC_DEB_BLUE = 2, -}; - -struct vimc_deb_pix_map { - u32 code; - enum vimc_deb_rgb_colors order[2][2]; -}; - -struct vimc_deb_device { - struct vimc_ent_device ved; - struct v4l2_subdev sd; - /* The active format */ - struct v4l2_mbus_framefmt sink_fmt; - u32 src_code; - void (*set_rgb_src)(struct vimc_deb_device *vdeb, unsigned int lin, - unsigned int col, unsigned int rgb[3]); - /* Values calculated when the stream starts */ - u8 *src_frame; - const struct vimc_deb_pix_map *sink_pix_map; - unsigned int sink_bpp; - unsigned int mean_win_size; - struct v4l2_ctrl_handler hdl; - struct media_pad pads[2]; -}; - -static const struct v4l2_mbus_framefmt sink_fmt_default = { - .width = 640, - .height = 480, - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_DEFAULT, -}; - -static const struct vimc_deb_pix_map vimc_deb_pix_map_list[] = { - { - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_RED } } - }, - { - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE }, - { VIMC_DEB_RED, VIMC_DEB_GREEN } } - }, - { - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED }, - { VIMC_DEB_BLUE, VIMC_DEB_GREEN } } - }, - { - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_BLUE } } - }, - { - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_RED } } - }, - { - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE }, - { VIMC_DEB_RED, VIMC_DEB_GREEN } } - }, - { - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED }, - { VIMC_DEB_BLUE, VIMC_DEB_GREEN } } - }, - { - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_BLUE } } - }, - { - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .order = { { VIMC_DEB_BLUE, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_RED } } - }, - { - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_BLUE }, - { VIMC_DEB_RED, VIMC_DEB_GREEN } } - }, - { - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .order = { { VIMC_DEB_GREEN, VIMC_DEB_RED }, - { VIMC_DEB_BLUE, VIMC_DEB_GREEN } } - }, - { - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .order = { { VIMC_DEB_RED, VIMC_DEB_GREEN }, - { VIMC_DEB_GREEN, VIMC_DEB_BLUE } } - }, -}; - -static const struct vimc_deb_pix_map *vimc_deb_pix_map_by_code(u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vimc_deb_pix_map_list); i++) - if (vimc_deb_pix_map_list[i].code == code) - return &vimc_deb_pix_map_list[i]; - - return NULL; -} - -static int vimc_deb_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) -{ - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *mf; - unsigned int i; - - mf = v4l2_subdev_get_try_format(sd, cfg, 0); - *mf = sink_fmt_default; - - for (i = 1; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_get_try_format(sd, cfg, i); - *mf = sink_fmt_default; - mf->code = vdeb->src_code; - } - - return 0; -} - -static int vimc_deb_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - /* We only support one format for source pads */ - if (VIMC_IS_SRC(code->pad)) { - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); - - if (code->index) - return -EINVAL; - - code->code = vdeb->src_code; - } else { - if (code->index >= ARRAY_SIZE(vimc_deb_pix_map_list)) - return -EINVAL; - - code->code = vimc_deb_pix_map_list[code->index].code; - } - - return 0; -} - -static int vimc_deb_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); - - if (fse->index) - return -EINVAL; - - if (VIMC_IS_SINK(fse->pad)) { - const struct vimc_deb_pix_map *vpix = - vimc_deb_pix_map_by_code(fse->code); - - if (!vpix) - return -EINVAL; - } else if (fse->code != vdeb->src_code) { - return -EINVAL; - } - - fse->min_width = VIMC_FRAME_MIN_WIDTH; - fse->max_width = VIMC_FRAME_MAX_WIDTH; - fse->min_height = VIMC_FRAME_MIN_HEIGHT; - fse->max_height = VIMC_FRAME_MAX_HEIGHT; - - return 0; -} - -static int vimc_deb_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); - - /* Get the current sink format */ - fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_get_try_format(sd, cfg, 0) : - vdeb->sink_fmt; - - /* Set the right code for the source pad */ - if (VIMC_IS_SRC(fmt->pad)) - fmt->format.code = vdeb->src_code; - - return 0; -} - -static void vimc_deb_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt) -{ - const struct vimc_deb_pix_map *vpix; - - /* Don't accept a code that is not on the debayer table */ - vpix = vimc_deb_pix_map_by_code(fmt->code); - if (!vpix) - fmt->code = sink_fmt_default.code; - - fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH, - VIMC_FRAME_MAX_WIDTH) & ~1; - fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT, - VIMC_FRAME_MAX_HEIGHT) & ~1; - - if (fmt->field == V4L2_FIELD_ANY) - fmt->field = sink_fmt_default.field; - - vimc_colorimetry_clamp(fmt); -} - -static int vimc_deb_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *sink_fmt; - - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - /* Do not change the format while stream is on */ - if (vdeb->src_frame) - return -EBUSY; - - sink_fmt = &vdeb->sink_fmt; - } else { - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); - } - - /* - * Do not change the format of the source pad, - * it is propagated from the sink - */ - if (VIMC_IS_SRC(fmt->pad)) { - fmt->format = *sink_fmt; - /* TODO: Add support for other formats */ - fmt->format.code = vdeb->src_code; - } else { - /* Set the new format in the sink pad */ - vimc_deb_adjust_sink_fmt(&fmt->format); - - dev_dbg(vdeb->ved.dev, "%s: sink format update: " - "old:%dx%d (0x%x, %d, %d, %d, %d) " - "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdeb->sd.name, - /* old */ - sink_fmt->width, sink_fmt->height, sink_fmt->code, - sink_fmt->colorspace, sink_fmt->quantization, - sink_fmt->xfer_func, sink_fmt->ycbcr_enc, - /* new */ - fmt->format.width, fmt->format.height, fmt->format.code, - fmt->format.colorspace, fmt->format.quantization, - fmt->format.xfer_func, fmt->format.ycbcr_enc); - - *sink_fmt = fmt->format; - } - - return 0; -} - -static const struct v4l2_subdev_pad_ops vimc_deb_pad_ops = { - .init_cfg = vimc_deb_init_cfg, - .enum_mbus_code = vimc_deb_enum_mbus_code, - .enum_frame_size = vimc_deb_enum_frame_size, - .get_fmt = vimc_deb_get_fmt, - .set_fmt = vimc_deb_set_fmt, -}; - -static void vimc_deb_set_rgb_mbus_fmt_rgb888_1x24(struct vimc_deb_device *vdeb, - unsigned int lin, - unsigned int col, - unsigned int rgb[3]) -{ - unsigned int i, index; - - index = VIMC_FRAME_INDEX(lin, col, vdeb->sink_fmt.width, 3); - for (i = 0; i < 3; i++) - vdeb->src_frame[index + i] = rgb[i]; -} - -static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); - - if (enable) { - const struct vimc_pix_map *vpix; - unsigned int frame_size; - - if (vdeb->src_frame) - return 0; - - /* Calculate the frame size of the source pad */ - vpix = vimc_pix_map_by_code(vdeb->src_code); - frame_size = vdeb->sink_fmt.width * vdeb->sink_fmt.height * - vpix->bpp; - - /* Save the bytes per pixel of the sink */ - vpix = vimc_pix_map_by_code(vdeb->sink_fmt.code); - vdeb->sink_bpp = vpix->bpp; - - /* Get the corresponding pixel map from the table */ - vdeb->sink_pix_map = - vimc_deb_pix_map_by_code(vdeb->sink_fmt.code); - - /* - * Allocate the frame buffer. Use vmalloc to be able to - * allocate a large amount of memory - */ - vdeb->src_frame = vmalloc(frame_size); - if (!vdeb->src_frame) - return -ENOMEM; - - } else { - if (!vdeb->src_frame) - return 0; - - vfree(vdeb->src_frame); - vdeb->src_frame = NULL; - } - - return 0; -} - -static const struct v4l2_subdev_core_ops vimc_deb_core_ops = { - .log_status = v4l2_ctrl_subdev_log_status, - .subscribe_event = v4l2_ctrl_subdev_subscribe_event, - .unsubscribe_event = v4l2_event_subdev_unsubscribe, -}; - -static const struct v4l2_subdev_video_ops vimc_deb_video_ops = { - .s_stream = vimc_deb_s_stream, -}; - -static const struct v4l2_subdev_ops vimc_deb_ops = { - .core = &vimc_deb_core_ops, - .pad = &vimc_deb_pad_ops, - .video = &vimc_deb_video_ops, -}; - -static unsigned int vimc_deb_get_val(const u8 *bytes, - const unsigned int n_bytes) -{ - unsigned int i; - unsigned int acc = 0; - - for (i = 0; i < n_bytes; i++) - acc = acc + (bytes[i] << (8 * i)); - - return acc; -} - -static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb, - const u8 *frame, - const unsigned int lin, - const unsigned int col, - unsigned int rgb[3]) -{ - unsigned int i, seek, wlin, wcol; - unsigned int n_rgb[3] = {0, 0, 0}; - - for (i = 0; i < 3; i++) - rgb[i] = 0; - - /* - * Calculate how many we need to subtract to get to the pixel in - * the top left corner of the mean window (considering the current - * pixel as the center) - */ - seek = vdeb->mean_win_size / 2; - - /* Sum the values of the colors in the mean window */ - - dev_dbg(vdeb->ved.dev, - "deb: %s: --- Calc pixel %dx%d, window mean %d, seek %d ---\n", - vdeb->sd.name, lin, col, vdeb->sink_fmt.height, seek); - - /* - * Iterate through all the lines in the mean window, start - * with zero if the pixel is outside the frame and don't pass - * the height when the pixel is in the bottom border of the - * frame - */ - for (wlin = seek > lin ? 0 : lin - seek; - wlin < lin + seek + 1 && wlin < vdeb->sink_fmt.height; - wlin++) { - - /* - * Iterate through all the columns in the mean window, start - * with zero if the pixel is outside the frame and don't pass - * the width when the pixel is in the right border of the - * frame - */ - for (wcol = seek > col ? 0 : col - seek; - wcol < col + seek + 1 && wcol < vdeb->sink_fmt.width; - wcol++) { - enum vimc_deb_rgb_colors color; - unsigned int index; - - /* Check which color this pixel is */ - color = vdeb->sink_pix_map->order[wlin % 2][wcol % 2]; - - index = VIMC_FRAME_INDEX(wlin, wcol, - vdeb->sink_fmt.width, - vdeb->sink_bpp); - - dev_dbg(vdeb->ved.dev, - "deb: %s: RGB CALC: frame index %d, win pos %dx%d, color %d\n", - vdeb->sd.name, index, wlin, wcol, color); - - /* Get its value */ - rgb[color] = rgb[color] + - vimc_deb_get_val(&frame[index], vdeb->sink_bpp); - - /* Save how many values we already added */ - n_rgb[color]++; - - dev_dbg(vdeb->ved.dev, "deb: %s: RGB CALC: val %d, n %d\n", - vdeb->sd.name, rgb[color], n_rgb[color]); - } - } - - /* Calculate the mean */ - for (i = 0; i < 3; i++) { - dev_dbg(vdeb->ved.dev, - "deb: %s: PRE CALC: %dx%d Color %d, val %d, n %d\n", - vdeb->sd.name, lin, col, i, rgb[i], n_rgb[i]); - - if (n_rgb[i]) - rgb[i] = rgb[i] / n_rgb[i]; - - dev_dbg(vdeb->ved.dev, - "deb: %s: FINAL CALC: %dx%d Color %d, val %d\n", - vdeb->sd.name, lin, col, i, rgb[i]); - } -} - -static void *vimc_deb_process_frame(struct vimc_ent_device *ved, - const void *sink_frame) -{ - struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device, - ved); - unsigned int rgb[3]; - unsigned int i, j; - - /* If the stream in this node is not active, just return */ - if (!vdeb->src_frame) - return ERR_PTR(-EINVAL); - - for (i = 0; i < vdeb->sink_fmt.height; i++) - for (j = 0; j < vdeb->sink_fmt.width; j++) { - vimc_deb_calc_rgb_sink(vdeb, sink_frame, i, j, rgb); - vdeb->set_rgb_src(vdeb, i, j, rgb); - } - - return vdeb->src_frame; -} - -static int vimc_deb_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vimc_deb_device *vdeb = - container_of(ctrl->handler, struct vimc_deb_device, hdl); - - switch (ctrl->id) { - case VIMC_CID_MEAN_WIN_SIZE: - vdeb->mean_win_size = ctrl->val; - break; - default: - return -EINVAL; - } - return 0; -} - -static const struct v4l2_ctrl_ops vimc_deb_ctrl_ops = { - .s_ctrl = vimc_deb_s_ctrl, -}; - -void vimc_deb_release(struct vimc_ent_device *ved) -{ - struct vimc_deb_device *vdeb = - container_of(ved, struct vimc_deb_device, ved); - - v4l2_ctrl_handler_free(&vdeb->hdl); - media_entity_cleanup(vdeb->ved.ent); - kfree(vdeb); -} - -static const struct v4l2_ctrl_config vimc_deb_ctrl_class = { - .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY, - .id = VIMC_CID_VIMC_CLASS, - .name = "VIMC Controls", - .type = V4L2_CTRL_TYPE_CTRL_CLASS, -}; - -static const struct v4l2_ctrl_config vimc_deb_ctrl_mean_win_size = { - .ops = &vimc_deb_ctrl_ops, - .id = VIMC_CID_MEAN_WIN_SIZE, - .name = "Debayer Mean Window Size", - .type = V4L2_CTRL_TYPE_INTEGER, - .min = 1, - .max = 25, - .step = 2, - .def = 3, -}; - -struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, - const char *vcfg_name) -{ - struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; - struct vimc_deb_device *vdeb; - int ret; - - /* Allocate the vdeb struct */ - vdeb = kzalloc(sizeof(*vdeb), GFP_KERNEL); - if (!vdeb) - return NULL; - - /* Create controls: */ - v4l2_ctrl_handler_init(&vdeb->hdl, 2); - v4l2_ctrl_new_custom(&vdeb->hdl, &vimc_deb_ctrl_class, NULL); - v4l2_ctrl_new_custom(&vdeb->hdl, &vimc_deb_ctrl_mean_win_size, NULL); - vdeb->sd.ctrl_handler = &vdeb->hdl; - if (vdeb->hdl.error) { - ret = vdeb->hdl.error; - goto err_free_vdeb; - } - - /* Initialize ved and sd */ - vdeb->pads[0].flags = MEDIA_PAD_FL_SINK; - vdeb->pads[1].flags = MEDIA_PAD_FL_SOURCE; - - ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev, - vcfg_name, - MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, - vdeb->pads, &vimc_deb_ops); - if (ret) - goto err_free_hdl; - - vdeb->ved.process_frame = vimc_deb_process_frame; - vdeb->ved.dev = vimc->mdev.dev; - vdeb->mean_win_size = vimc_deb_ctrl_mean_win_size.def; - - /* Initialize the frame format */ - vdeb->sink_fmt = sink_fmt_default; - /* - * TODO: Add support for more output formats, we only support - * RGB888 for now - * NOTE: the src format is always the same as the sink, except - * for the code - */ - vdeb->src_code = MEDIA_BUS_FMT_RGB888_1X24; - vdeb->set_rgb_src = vimc_deb_set_rgb_mbus_fmt_rgb888_1x24; - - return &vdeb->ved; - -err_free_hdl: - v4l2_ctrl_handler_free(&vdeb->hdl); -err_free_vdeb: - kfree(vdeb); - - return NULL; -} diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c deleted file mode 100644 index 7521439747c5..000000000000 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ /dev/null @@ -1,511 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-scaler.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> - */ - -#include <linux/moduleparam.h> -#include <linux/vmalloc.h> -#include <linux/v4l2-mediabus.h> -#include <media/v4l2-rect.h> -#include <media/v4l2-subdev.h> - -#include "vimc-common.h" - -static unsigned int sca_mult = 3; -module_param(sca_mult, uint, 0000); -MODULE_PARM_DESC(sca_mult, " the image size multiplier"); - -#define MAX_ZOOM 8 - -#define VIMC_SCA_FMT_WIDTH_DEFAULT 640 -#define VIMC_SCA_FMT_HEIGHT_DEFAULT 480 - -struct vimc_sca_device { - struct vimc_ent_device ved; - struct v4l2_subdev sd; - /* NOTE: the source fmt is the same as the sink - * with the width and hight multiplied by mult - */ - struct v4l2_mbus_framefmt sink_fmt; - struct v4l2_rect crop_rect; - /* Values calculated when the stream starts */ - u8 *src_frame; - unsigned int src_line_size; - unsigned int bpp; - struct media_pad pads[2]; -}; - -static const struct v4l2_mbus_framefmt sink_fmt_default = { - .width = VIMC_SCA_FMT_WIDTH_DEFAULT, - .height = VIMC_SCA_FMT_HEIGHT_DEFAULT, - .code = MEDIA_BUS_FMT_RGB888_1X24, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_DEFAULT, -}; - -static const struct v4l2_rect crop_rect_default = { - .width = VIMC_SCA_FMT_WIDTH_DEFAULT, - .height = VIMC_SCA_FMT_HEIGHT_DEFAULT, - .top = 0, - .left = 0, -}; - -static const struct v4l2_rect crop_rect_min = { - .width = VIMC_FRAME_MIN_WIDTH, - .height = VIMC_FRAME_MIN_HEIGHT, - .top = 0, - .left = 0, -}; - -static struct v4l2_rect -vimc_sca_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt) -{ - /* Get the crop bounds to clamp the crop rectangle correctly */ - struct v4l2_rect r = { - .left = 0, - .top = 0, - .width = sink_fmt->width, - .height = sink_fmt->height, - }; - return r; -} - -static void vimc_sca_adjust_sink_crop(struct v4l2_rect *r, - const struct v4l2_mbus_framefmt *sink_fmt) -{ - const struct v4l2_rect sink_rect = - vimc_sca_get_crop_bound_sink(sink_fmt); - - /* Disallow rectangles smaller than the minimal one. */ - v4l2_rect_set_min_size(r, &crop_rect_min); - v4l2_rect_map_inside(r, &sink_rect); -} - -static int vimc_sca_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) -{ - struct v4l2_mbus_framefmt *mf; - struct v4l2_rect *r; - unsigned int i; - - mf = v4l2_subdev_get_try_format(sd, cfg, 0); - *mf = sink_fmt_default; - - r = v4l2_subdev_get_try_crop(sd, cfg, 0); - *r = crop_rect_default; - - for (i = 1; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_get_try_format(sd, cfg, i); - *mf = sink_fmt_default; - mf->width = mf->width * sca_mult; - mf->height = mf->height * sca_mult; - } - - return 0; -} - -static int vimc_sca_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - const struct vimc_pix_map *vpix = vimc_pix_map_by_index(code->index); - - /* We don't support bayer format */ - if (!vpix || vpix->bayer) - return -EINVAL; - - code->code = vpix->code; - - return 0; -} - -static int vimc_sca_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) -{ - const struct vimc_pix_map *vpix; - - if (fse->index) - return -EINVAL; - - /* Only accept code in the pix map table in non bayer format */ - vpix = vimc_pix_map_by_code(fse->code); - if (!vpix || vpix->bayer) - return -EINVAL; - - fse->min_width = VIMC_FRAME_MIN_WIDTH; - fse->min_height = VIMC_FRAME_MIN_HEIGHT; - - if (VIMC_IS_SINK(fse->pad)) { - fse->max_width = VIMC_FRAME_MAX_WIDTH; - fse->max_height = VIMC_FRAME_MAX_HEIGHT; - } else { - fse->max_width = VIMC_FRAME_MAX_WIDTH * MAX_ZOOM; - fse->max_height = VIMC_FRAME_MAX_HEIGHT * MAX_ZOOM; - } - - return 0; -} - -static int vimc_sca_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); - struct v4l2_rect *crop_rect; - - /* Get the current sink format */ - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - format->format = *v4l2_subdev_get_try_format(sd, cfg, 0); - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); - } else { - format->format = vsca->sink_fmt; - crop_rect = &vsca->crop_rect; - } - - /* Scale the frame size for the source pad */ - if (VIMC_IS_SRC(format->pad)) { - format->format.width = crop_rect->width * sca_mult; - format->format.height = crop_rect->height * sca_mult; - } - - return 0; -} - -static void vimc_sca_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt) -{ - const struct vimc_pix_map *vpix; - - /* Only accept code in the pix map table in non bayer format */ - vpix = vimc_pix_map_by_code(fmt->code); - if (!vpix || vpix->bayer) - fmt->code = sink_fmt_default.code; - - fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH, - VIMC_FRAME_MAX_WIDTH) & ~1; - fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT, - VIMC_FRAME_MAX_HEIGHT) & ~1; - - if (fmt->field == V4L2_FIELD_ANY) - fmt->field = sink_fmt_default.field; - - vimc_colorimetry_clamp(fmt); -} - -static int vimc_sca_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *sink_fmt; - struct v4l2_rect *crop_rect; - - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - /* Do not change the format while stream is on */ - if (vsca->src_frame) - return -EBUSY; - - sink_fmt = &vsca->sink_fmt; - crop_rect = &vsca->crop_rect; - } else { - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); - } - - /* - * Do not change the format of the source pad, - * it is propagated from the sink - */ - if (VIMC_IS_SRC(fmt->pad)) { - fmt->format = *sink_fmt; - fmt->format.width = crop_rect->width * sca_mult; - fmt->format.height = crop_rect->height * sca_mult; - } else { - /* Set the new format in the sink pad */ - vimc_sca_adjust_sink_fmt(&fmt->format); - - dev_dbg(vsca->ved.dev, "%s: sink format update: " - "old:%dx%d (0x%x, %d, %d, %d, %d) " - "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsca->sd.name, - /* old */ - sink_fmt->width, sink_fmt->height, sink_fmt->code, - sink_fmt->colorspace, sink_fmt->quantization, - sink_fmt->xfer_func, sink_fmt->ycbcr_enc, - /* new */ - fmt->format.width, fmt->format.height, fmt->format.code, - fmt->format.colorspace, fmt->format.quantization, - fmt->format.xfer_func, fmt->format.ycbcr_enc); - - *sink_fmt = fmt->format; - - /* Do the crop, but respect the current bounds */ - vimc_sca_adjust_sink_crop(crop_rect, sink_fmt); - } - - return 0; -} - -static int vimc_sca_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *sink_fmt; - struct v4l2_rect *crop_rect; - - if (VIMC_IS_SRC(sel->pad)) - return -EINVAL; - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sink_fmt = &vsca->sink_fmt; - crop_rect = &vsca->crop_rect; - } else { - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); - } - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - sel->r = *crop_rect; - break; - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r = vimc_sca_get_crop_bound_sink(sink_fmt); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vimc_sca_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *sink_fmt; - struct v4l2_rect *crop_rect; - - if (VIMC_IS_SRC(sel->pad)) - return -EINVAL; - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - /* Do not change the format while stream is on */ - if (vsca->src_frame) - return -EBUSY; - - crop_rect = &vsca->crop_rect; - sink_fmt = &vsca->sink_fmt; - } else { - crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0); - sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); - } - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - /* Do the crop, but respect the current bounds */ - vimc_sca_adjust_sink_crop(&sel->r, sink_fmt); - *crop_rect = sel->r; - break; - default: - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_subdev_pad_ops vimc_sca_pad_ops = { - .init_cfg = vimc_sca_init_cfg, - .enum_mbus_code = vimc_sca_enum_mbus_code, - .enum_frame_size = vimc_sca_enum_frame_size, - .get_fmt = vimc_sca_get_fmt, - .set_fmt = vimc_sca_set_fmt, - .get_selection = vimc_sca_get_selection, - .set_selection = vimc_sca_set_selection, -}; - -static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); - - if (enable) { - const struct vimc_pix_map *vpix; - unsigned int frame_size; - - if (vsca->src_frame) - return 0; - - /* Save the bytes per pixel of the sink */ - vpix = vimc_pix_map_by_code(vsca->sink_fmt.code); - vsca->bpp = vpix->bpp; - - /* Calculate the width in bytes of the src frame */ - vsca->src_line_size = vsca->crop_rect.width * - sca_mult * vsca->bpp; - - /* Calculate the frame size of the source pad */ - frame_size = vsca->src_line_size * vsca->crop_rect.height * - sca_mult; - - /* Allocate the frame buffer. Use vmalloc to be able to - * allocate a large amount of memory - */ - vsca->src_frame = vmalloc(frame_size); - if (!vsca->src_frame) - return -ENOMEM; - - } else { - if (!vsca->src_frame) - return 0; - - vfree(vsca->src_frame); - vsca->src_frame = NULL; - } - - return 0; -} - -static const struct v4l2_subdev_video_ops vimc_sca_video_ops = { - .s_stream = vimc_sca_s_stream, -}; - -static const struct v4l2_subdev_ops vimc_sca_ops = { - .pad = &vimc_sca_pad_ops, - .video = &vimc_sca_video_ops, -}; - -static void vimc_sca_fill_pix(u8 *const ptr, - const u8 *const pixel, - const unsigned int bpp) -{ - unsigned int i; - - /* copy the pixel to the pointer */ - for (i = 0; i < bpp; i++) - ptr[i] = pixel[i]; -} - -static void vimc_sca_scale_pix(const struct vimc_sca_device *const vsca, - unsigned int lin, unsigned int col, - const u8 *const sink_frame) -{ - const struct v4l2_rect crop_rect = vsca->crop_rect; - unsigned int i, j, index; - const u8 *pixel; - - /* Point to the pixel value in position (lin, col) in the sink frame */ - index = VIMC_FRAME_INDEX(lin, col, - vsca->sink_fmt.width, - vsca->bpp); - pixel = &sink_frame[index]; - - dev_dbg(vsca->ved.dev, - "sca: %s: --- scale_pix sink pos %dx%d, index %d ---\n", - vsca->sd.name, lin, col, index); - - /* point to the place we are going to put the first pixel - * in the scaled src frame - */ - lin -= crop_rect.top; - col -= crop_rect.left; - index = VIMC_FRAME_INDEX(lin * sca_mult, col * sca_mult, - crop_rect.width * sca_mult, vsca->bpp); - - dev_dbg(vsca->ved.dev, "sca: %s: scale_pix src pos %dx%d, index %d\n", - vsca->sd.name, lin * sca_mult, col * sca_mult, index); - - /* Repeat this pixel mult times */ - for (i = 0; i < sca_mult; i++) { - /* Iterate through each beginning of a - * pixel repetition in a line - */ - for (j = 0; j < sca_mult * vsca->bpp; j += vsca->bpp) { - dev_dbg(vsca->ved.dev, - "sca: %s: sca: scale_pix src pos %d\n", - vsca->sd.name, index + j); - - /* copy the pixel to the position index + j */ - vimc_sca_fill_pix(&vsca->src_frame[index + j], - pixel, vsca->bpp); - } - - /* move the index to the next line */ - index += vsca->src_line_size; - } -} - -static void vimc_sca_fill_src_frame(const struct vimc_sca_device *const vsca, - const u8 *const sink_frame) -{ - const struct v4l2_rect r = vsca->crop_rect; - unsigned int i, j; - - /* Scale each pixel from the original sink frame */ - /* TODO: implement scale down, only scale up is supported for now */ - for (i = r.top; i < r.top + r.height; i++) - for (j = r.left; j < r.left + r.width; j++) - vimc_sca_scale_pix(vsca, i, j, sink_frame); -} - -static void *vimc_sca_process_frame(struct vimc_ent_device *ved, - const void *sink_frame) -{ - struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device, - ved); - - /* If the stream in this node is not active, just return */ - if (!vsca->src_frame) - return ERR_PTR(-EINVAL); - - vimc_sca_fill_src_frame(vsca, sink_frame); - - return vsca->src_frame; -}; - -void vimc_sca_release(struct vimc_ent_device *ved) -{ - struct vimc_sca_device *vsca = - container_of(ved, struct vimc_sca_device, ved); - - media_entity_cleanup(vsca->ved.ent); - kfree(vsca); -} - -struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, - const char *vcfg_name) -{ - struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; - struct vimc_sca_device *vsca; - int ret; - - /* Allocate the vsca struct */ - vsca = kzalloc(sizeof(*vsca), GFP_KERNEL); - if (!vsca) - return NULL; - - /* Initialize ved and sd */ - vsca->pads[0].flags = MEDIA_PAD_FL_SINK; - vsca->pads[1].flags = MEDIA_PAD_FL_SOURCE; - - ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev, - vcfg_name, - MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, - vsca->pads, &vimc_sca_ops); - if (ret) { - kfree(vsca); - return NULL; - } - - vsca->ved.process_frame = vimc_sca_process_frame; - vsca->ved.dev = vimc->mdev.dev; - - /* Initialize the frame format */ - vsca->sink_fmt = sink_fmt_default; - - /* Initialize the crop selection */ - vsca->crop_rect = crop_rect_default; - - return &vsca->ved; -} diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c deleted file mode 100644 index 92daee58209e..000000000000 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ /dev/null @@ -1,376 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * vimc-sensor.c Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> - */ - -#include <linux/v4l2-mediabus.h> -#include <linux/vmalloc.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-event.h> -#include <media/v4l2-subdev.h> -#include <media/tpg/v4l2-tpg.h> - -#include "vimc-common.h" - -struct vimc_sen_device { - struct vimc_ent_device ved; - struct v4l2_subdev sd; - struct tpg_data tpg; - u8 *frame; - /* The active format */ - struct v4l2_mbus_framefmt mbus_format; - struct v4l2_ctrl_handler hdl; - struct media_pad pad; -}; - -static const struct v4l2_mbus_framefmt fmt_default = { - .width = 640, - .height = 480, - .code = MEDIA_BUS_FMT_RGB888_1X24, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_DEFAULT, -}; - -static int vimc_sen_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg) -{ - unsigned int i; - - for (i = 0; i < sd->entity.num_pads; i++) { - struct v4l2_mbus_framefmt *mf; - - mf = v4l2_subdev_get_try_format(sd, cfg, i); - *mf = fmt_default; - } - - return 0; -} - -static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - const struct vimc_pix_map *vpix = vimc_pix_map_by_index(code->index); - - if (!vpix) - return -EINVAL; - - code->code = vpix->code; - - return 0; -} - -static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_frame_size_enum *fse) -{ - const struct vimc_pix_map *vpix; - - if (fse->index) - return -EINVAL; - - /* Only accept code in the pix map table */ - vpix = vimc_pix_map_by_code(fse->code); - if (!vpix) - return -EINVAL; - - fse->min_width = VIMC_FRAME_MIN_WIDTH; - fse->max_width = VIMC_FRAME_MAX_WIDTH; - fse->min_height = VIMC_FRAME_MIN_HEIGHT; - fse->max_height = VIMC_FRAME_MAX_HEIGHT; - - return 0; -} - -static int vimc_sen_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vimc_sen_device *vsen = - container_of(sd, struct vimc_sen_device, sd); - - fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) : - vsen->mbus_format; - - return 0; -} - -static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen) -{ - const struct vimc_pix_map *vpix = - vimc_pix_map_by_code(vsen->mbus_format.code); - - tpg_reset_source(&vsen->tpg, vsen->mbus_format.width, - vsen->mbus_format.height, vsen->mbus_format.field); - tpg_s_bytesperline(&vsen->tpg, 0, vsen->mbus_format.width * vpix->bpp); - tpg_s_buf_height(&vsen->tpg, vsen->mbus_format.height); - tpg_s_fourcc(&vsen->tpg, vpix->pixelformat); - /* TODO: add support for V4L2_FIELD_ALTERNATE */ - tpg_s_field(&vsen->tpg, vsen->mbus_format.field, false); - tpg_s_colorspace(&vsen->tpg, vsen->mbus_format.colorspace); - tpg_s_ycbcr_enc(&vsen->tpg, vsen->mbus_format.ycbcr_enc); - tpg_s_quantization(&vsen->tpg, vsen->mbus_format.quantization); - tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func); -} - -static void vimc_sen_adjust_fmt(struct v4l2_mbus_framefmt *fmt) -{ - const struct vimc_pix_map *vpix; - - /* Only accept code in the pix map table */ - vpix = vimc_pix_map_by_code(fmt->code); - if (!vpix) - fmt->code = fmt_default.code; - - fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH, - VIMC_FRAME_MAX_WIDTH) & ~1; - fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT, - VIMC_FRAME_MAX_HEIGHT) & ~1; - - /* TODO: add support for V4L2_FIELD_ALTERNATE */ - if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) - fmt->field = fmt_default.field; - - vimc_colorimetry_clamp(fmt); -} - -static int vimc_sen_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *mf; - - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - /* Do not change the format while stream is on */ - if (vsen->frame) - return -EBUSY; - - mf = &vsen->mbus_format; - } else { - mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); - } - - /* Set the new format */ - vimc_sen_adjust_fmt(&fmt->format); - - dev_dbg(vsen->ved.dev, "%s: format update: " - "old:%dx%d (0x%x, %d, %d, %d, %d) " - "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsen->sd.name, - /* old */ - mf->width, mf->height, mf->code, - mf->colorspace, mf->quantization, - mf->xfer_func, mf->ycbcr_enc, - /* new */ - fmt->format.width, fmt->format.height, fmt->format.code, - fmt->format.colorspace, fmt->format.quantization, - fmt->format.xfer_func, fmt->format.ycbcr_enc); - - *mf = fmt->format; - - return 0; -} - -static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = { - .init_cfg = vimc_sen_init_cfg, - .enum_mbus_code = vimc_sen_enum_mbus_code, - .enum_frame_size = vimc_sen_enum_frame_size, - .get_fmt = vimc_sen_get_fmt, - .set_fmt = vimc_sen_set_fmt, -}; - -static void *vimc_sen_process_frame(struct vimc_ent_device *ved, - const void *sink_frame) -{ - struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, - ved); - - tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); - return vsen->frame; -} - -static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct vimc_sen_device *vsen = - container_of(sd, struct vimc_sen_device, sd); - - if (enable) { - const struct vimc_pix_map *vpix; - unsigned int frame_size; - - /* Calculate the frame size */ - vpix = vimc_pix_map_by_code(vsen->mbus_format.code); - frame_size = vsen->mbus_format.width * vpix->bpp * - vsen->mbus_format.height; - - /* - * Allocate the frame buffer. Use vmalloc to be able to - * allocate a large amount of memory - */ - vsen->frame = vmalloc(frame_size); - if (!vsen->frame) - return -ENOMEM; - - /* configure the test pattern generator */ - vimc_sen_tpg_s_format(vsen); - - } else { - - vfree(vsen->frame); - vsen->frame = NULL; - } - - return 0; -} - -static const struct v4l2_subdev_core_ops vimc_sen_core_ops = { - .log_status = v4l2_ctrl_subdev_log_status, - .subscribe_event = v4l2_ctrl_subdev_subscribe_event, - .unsubscribe_event = v4l2_event_subdev_unsubscribe, -}; - -static const struct v4l2_subdev_video_ops vimc_sen_video_ops = { - .s_stream = vimc_sen_s_stream, -}; - -static const struct v4l2_subdev_ops vimc_sen_ops = { - .core = &vimc_sen_core_ops, - .pad = &vimc_sen_pad_ops, - .video = &vimc_sen_video_ops, -}; - -static int vimc_sen_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vimc_sen_device *vsen = - container_of(ctrl->handler, struct vimc_sen_device, hdl); - - switch (ctrl->id) { - case VIMC_CID_TEST_PATTERN: - tpg_s_pattern(&vsen->tpg, ctrl->val); - break; - case V4L2_CID_HFLIP: - tpg_s_hflip(&vsen->tpg, ctrl->val); - break; - case V4L2_CID_VFLIP: - tpg_s_vflip(&vsen->tpg, ctrl->val); - break; - case V4L2_CID_BRIGHTNESS: - tpg_s_brightness(&vsen->tpg, ctrl->val); - break; - case V4L2_CID_CONTRAST: - tpg_s_contrast(&vsen->tpg, ctrl->val); - break; - case V4L2_CID_HUE: - tpg_s_hue(&vsen->tpg, ctrl->val); - break; - case V4L2_CID_SATURATION: - tpg_s_saturation(&vsen->tpg, ctrl->val); - break; - default: - return -EINVAL; - } - return 0; -} - -static const struct v4l2_ctrl_ops vimc_sen_ctrl_ops = { - .s_ctrl = vimc_sen_s_ctrl, -}; - -void vimc_sen_release(struct vimc_ent_device *ved) -{ - struct vimc_sen_device *vsen = - container_of(ved, struct vimc_sen_device, ved); - - v4l2_ctrl_handler_free(&vsen->hdl); - tpg_free(&vsen->tpg); - media_entity_cleanup(vsen->ved.ent); - kfree(vsen); -} - -/* Image Processing Controls */ -static const struct v4l2_ctrl_config vimc_sen_ctrl_class = { - .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY, - .id = VIMC_CID_VIMC_CLASS, - .name = "VIMC Controls", - .type = V4L2_CTRL_TYPE_CTRL_CLASS, -}; - -static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = { - .ops = &vimc_sen_ctrl_ops, - .id = VIMC_CID_TEST_PATTERN, - .name = "Test Pattern", - .type = V4L2_CTRL_TYPE_MENU, - .max = TPG_PAT_NOISE, - .qmenu = tpg_pattern_strings, -}; - -struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, - const char *vcfg_name) -{ - struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; - struct vimc_sen_device *vsen; - int ret; - - /* Allocate the vsen struct */ - vsen = kzalloc(sizeof(*vsen), GFP_KERNEL); - if (!vsen) - return NULL; - - v4l2_ctrl_handler_init(&vsen->hdl, 4); - - v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_class, NULL); - v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_test_pattern, NULL); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 128); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 128); - vsen->sd.ctrl_handler = &vsen->hdl; - if (vsen->hdl.error) { - ret = vsen->hdl.error; - goto err_free_vsen; - } - - /* Initialize the test pattern generator */ - tpg_init(&vsen->tpg, vsen->mbus_format.width, - vsen->mbus_format.height); - ret = tpg_alloc(&vsen->tpg, VIMC_FRAME_MAX_WIDTH); - if (ret) - goto err_free_hdl; - - /* Initialize ved and sd */ - vsen->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, - vcfg_name, - MEDIA_ENT_F_CAM_SENSOR, 1, &vsen->pad, - &vimc_sen_ops); - if (ret) - goto err_free_tpg; - - vsen->ved.process_frame = vimc_sen_process_frame; - vsen->ved.dev = vimc->mdev.dev; - - /* Initialize the frame format */ - vsen->mbus_format = fmt_default; - - return &vsen->ved; - -err_free_tpg: - tpg_free(&vsen->tpg); -err_free_hdl: - v4l2_ctrl_handler_free(&vsen->hdl); -err_free_vsen: - kfree(vsen); - - return NULL; -} diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c deleted file mode 100644 index 65feb3c596db..000000000000 --- a/drivers/media/platform/vimc/vimc-streamer.c +++ /dev/null @@ -1,238 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * vimc-streamer.c Virtual Media Controller Driver - * - * Copyright (C) 2018 Lucas A. M. Magalhães <lucmaga@gmail.com> - * - */ - -#include <linux/init.h> -#include <linux/freezer.h> -#include <linux/kthread.h> - -#include "vimc-streamer.h" - -/** - * vimc_get_source_entity - get the entity connected with the first sink pad - * - * @ent: reference media_entity - * - * Helper function that returns the media entity containing the source pad - * linked with the first sink pad from the given media entity pad list. - * - * Return: The source pad or NULL, if it wasn't found. - */ -static struct media_entity *vimc_get_source_entity(struct media_entity *ent) -{ - struct media_pad *pad; - int i; - - for (i = 0; i < ent->num_pads; i++) { - if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE) - continue; - pad = media_entity_remote_pad(&ent->pads[i]); - return pad ? pad->entity : NULL; - } - return NULL; -} - -/** - * vimc_streamer_pipeline_terminate - Disable stream in all ved in stream - * - * @stream: the pointer to the stream structure with the pipeline to be - * disabled. - * - * Calls s_stream to disable the stream in each entity of the pipeline - * - */ -static void vimc_streamer_pipeline_terminate(struct vimc_stream *stream) -{ - struct vimc_ent_device *ved; - struct v4l2_subdev *sd; - - while (stream->pipe_size) { - stream->pipe_size--; - ved = stream->ved_pipeline[stream->pipe_size]; - stream->ved_pipeline[stream->pipe_size] = NULL; - - if (!is_media_entity_v4l2_subdev(ved->ent)) - continue; - - sd = media_entity_to_v4l2_subdev(ved->ent); - v4l2_subdev_call(sd, video, s_stream, 0); - } -} - -/** - * vimc_streamer_pipeline_init - Initializes the stream structure - * - * @stream: the pointer to the stream structure to be initialized - * @ved: the pointer to the vimc entity initializing the stream - * - * Initializes the stream structure. Walks through the entity graph to - * construct the pipeline used later on the streamer thread. - * Calls vimc_streamer_s_stream() to enable stream in all entities of - * the pipeline. - * - * Return: 0 if success, error code otherwise. - */ -static int vimc_streamer_pipeline_init(struct vimc_stream *stream, - struct vimc_ent_device *ved) -{ - struct media_entity *entity; - struct video_device *vdev; - struct v4l2_subdev *sd; - int ret = 0; - - stream->pipe_size = 0; - while (stream->pipe_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) { - if (!ved) { - vimc_streamer_pipeline_terminate(stream); - return -EINVAL; - } - stream->ved_pipeline[stream->pipe_size++] = ved; - - if (is_media_entity_v4l2_subdev(ved->ent)) { - sd = media_entity_to_v4l2_subdev(ved->ent); - ret = v4l2_subdev_call(sd, video, s_stream, 1); - if (ret && ret != -ENOIOCTLCMD) { - dev_err(ved->dev, "subdev_call error %s\n", - ved->ent->name); - vimc_streamer_pipeline_terminate(stream); - return ret; - } - } - - entity = vimc_get_source_entity(ved->ent); - /* Check if the end of the pipeline was reached */ - if (!entity) { - /* the first entity of the pipe should be source only */ - if (!vimc_is_source(ved->ent)) { - dev_err(ved->dev, - "first entity in the pipe '%s' is not a source\n", - ved->ent->name); - vimc_streamer_pipeline_terminate(stream); - return -EPIPE; - } - return 0; - } - - /* Get the next device in the pipeline */ - if (is_media_entity_v4l2_subdev(entity)) { - sd = media_entity_to_v4l2_subdev(entity); - ved = v4l2_get_subdevdata(sd); - } else { - vdev = container_of(entity, - struct video_device, - entity); - ved = video_get_drvdata(vdev); - } - } - - vimc_streamer_pipeline_terminate(stream); - return -EINVAL; -} - -/** - * vimc_streamer_thread - Process frames through the pipeline - * - * @data: vimc_stream struct of the current stream - * - * From the source to the sink, gets a frame from each subdevice and send to - * the next one of the pipeline at a fixed framerate. - * - * Return: - * Always zero (created as ``int`` instead of ``void`` to comply with - * kthread API). - */ -static int vimc_streamer_thread(void *data) -{ - struct vimc_stream *stream = data; - u8 *frame = NULL; - int i; - - set_freezable(); - - for (;;) { - try_to_freeze(); - if (kthread_should_stop()) - break; - - for (i = stream->pipe_size - 1; i >= 0; i--) { - frame = stream->ved_pipeline[i]->process_frame( - stream->ved_pipeline[i], frame); - if (!frame || IS_ERR(frame)) - break; - } - //wait for 60hz - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ / 60); - } - - return 0; -} - -/** - * vimc_streamer_s_stream - Start/stop the streaming on the media pipeline - * - * @stream: the pointer to the stream structure of the current stream - * @ved: pointer to the vimc entity of the entity of the stream - * @enable: flag to determine if stream should start/stop - * - * When starting, check if there is no ``stream->kthread`` allocated. This - * should indicate that a stream is already running. Then, it initializes the - * pipeline, creates and runs a kthread to consume buffers through the pipeline. - * When stopping, analogously check if there is a stream running, stop the - * thread and terminates the pipeline. - * - * Return: 0 if success, error code otherwise. - */ -int vimc_streamer_s_stream(struct vimc_stream *stream, - struct vimc_ent_device *ved, - int enable) -{ - int ret; - - if (!stream || !ved) - return -EINVAL; - - if (enable) { - if (stream->kthread) - return 0; - - ret = vimc_streamer_pipeline_init(stream, ved); - if (ret) - return ret; - - stream->kthread = kthread_run(vimc_streamer_thread, stream, - "vimc-streamer thread"); - - if (IS_ERR(stream->kthread)) { - ret = PTR_ERR(stream->kthread); - dev_err(ved->dev, "kthread_run failed with %d\n", ret); - vimc_streamer_pipeline_terminate(stream); - stream->kthread = NULL; - return ret; - } - - } else { - if (!stream->kthread) - return 0; - - ret = kthread_stop(stream->kthread); - /* - * kthread_stop returns -EINTR in cases when streamon was - * immediately followed by streamoff, and the thread didn't had - * a chance to run. Ignore errors to stop the stream in the - * pipeline. - */ - if (ret) - dev_dbg(ved->dev, "kthread_stop returned '%d'\n", ret); - - stream->kthread = NULL; - - vimc_streamer_pipeline_terminate(stream); - } - - return 0; -} diff --git a/drivers/media/platform/vimc/vimc-streamer.h b/drivers/media/platform/vimc/vimc-streamer.h deleted file mode 100644 index fe3c51f15fad..000000000000 --- a/drivers/media/platform/vimc/vimc-streamer.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * vimc-streamer.h Virtual Media Controller Driver - * - * Copyright (C) 2018 Lucas A. M. Magalhães <lucmaga@gmail.com> - * - */ - -#ifndef _VIMC_STREAMER_H_ -#define _VIMC_STREAMER_H_ - -#include <media/media-device.h> - -#include "vimc-common.h" - -#define VIMC_STREAMER_PIPELINE_MAX_SIZE 16 - -/** - * struct vimc_stream - struct that represents a stream in the pipeline - * - * @pipe: the media pipeline object associated with this stream - * @ved_pipeline: array containing all the entities participating in the - * stream. The order is from a video device (usually a capture device) where - * stream_on was called, to the entity generating the first base image to be - * processed in the pipeline. - * @pipe_size: size of @ved_pipeline - * @kthread: thread that generates the frames of the stream. - * - * When the user call stream_on in a video device, struct vimc_stream is - * used to keep track of all entities and subdevices that generates and - * process frames for the stream. - */ -struct vimc_stream { - struct media_pipeline pipe; - struct vimc_ent_device *ved_pipeline[VIMC_STREAMER_PIPELINE_MAX_SIZE]; - unsigned int pipe_size; - struct task_struct *kthread; -}; - -int vimc_streamer_s_stream(struct vimc_stream *stream, - struct vimc_ent_device *ved, - int enable); - -#endif //_VIMC_STREAMER_H_ |