162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * FIMC-IS ISP video input and video output DMA interface driver 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2013 Samsung Electronics Co., Ltd. 862306a36Sopenharmony_ci * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * The hardware handling code derived from a driver written by 1162306a36Sopenharmony_ci * Younghwan Joo <yhwan.joo@samsung.com>. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/bitops.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/errno.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/types.h> 2162306a36Sopenharmony_ci#include <linux/printk.h> 2262306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/videodev2.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <media/v4l2-device.h> 2762306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2862306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 2962306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 3062306a36Sopenharmony_ci#include <media/drv-intf/exynos-fimc.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "common.h" 3362306a36Sopenharmony_ci#include "media-dev.h" 3462306a36Sopenharmony_ci#include "fimc-is.h" 3562306a36Sopenharmony_ci#include "fimc-isp-video.h" 3662306a36Sopenharmony_ci#include "fimc-is-param.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int isp_video_capture_queue_setup(struct vb2_queue *vq, 3962306a36Sopenharmony_ci unsigned int *num_buffers, unsigned int *num_planes, 4062306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct fimc_isp *isp = vb2_get_drv_priv(vq); 4362306a36Sopenharmony_ci struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt; 4462306a36Sopenharmony_ci const struct fimc_fmt *fmt = isp->video_capture.format; 4562306a36Sopenharmony_ci unsigned int wh, i; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci wh = vid_fmt->width * vid_fmt->height; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (fmt == NULL) 5062306a36Sopenharmony_ci return -EINVAL; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci *num_buffers = clamp_t(u32, *num_buffers, FIMC_ISP_REQ_BUFS_MIN, 5362306a36Sopenharmony_ci FIMC_ISP_REQ_BUFS_MAX); 5462306a36Sopenharmony_ci if (*num_planes) { 5562306a36Sopenharmony_ci if (*num_planes != fmt->memplanes) 5662306a36Sopenharmony_ci return -EINVAL; 5762306a36Sopenharmony_ci for (i = 0; i < *num_planes; i++) 5862306a36Sopenharmony_ci if (sizes[i] < (wh * fmt->depth[i]) / 8) 5962306a36Sopenharmony_ci return -EINVAL; 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci *num_planes = fmt->memplanes; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci for (i = 0; i < fmt->memplanes; i++) 6662306a36Sopenharmony_ci sizes[i] = (wh * fmt->depth[i]) / 8; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic inline struct param_dma_output *__get_isp_dma2(struct fimc_is *is) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return &__get_curr_is_config(is)->isp.dma2_output; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int isp_video_capture_start_streaming(struct vb2_queue *q, 7762306a36Sopenharmony_ci unsigned int count) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct fimc_isp *isp = vb2_get_drv_priv(q); 8062306a36Sopenharmony_ci struct fimc_is *is = fimc_isp_to_is(isp); 8162306a36Sopenharmony_ci struct param_dma_output *dma = __get_isp_dma2(is); 8262306a36Sopenharmony_ci struct fimc_is_video *video = &isp->video_capture; 8362306a36Sopenharmony_ci int ret; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (!test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state) || 8662306a36Sopenharmony_ci test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state)) 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci dma->cmd = DMA_OUTPUT_COMMAND_ENABLE; 9162306a36Sopenharmony_ci dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE; 9262306a36Sopenharmony_ci dma->buffer_address = is->is_dma_p_region + 9362306a36Sopenharmony_ci DMA2_OUTPUT_ADDR_ARRAY_OFFS; 9462306a36Sopenharmony_ci dma->buffer_number = video->reqbufs_count; 9562306a36Sopenharmony_ci dma->dma_out_mask = video->buf_mask; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci isp_dbg(2, &video->ve.vdev, 9862306a36Sopenharmony_ci "buf_count: %d, planes: %d, dma addr table: %#x\n", 9962306a36Sopenharmony_ci video->buf_count, video->format->memplanes, 10062306a36Sopenharmony_ci dma->buffer_address); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci fimc_is_mem_barrier(); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT); 10562306a36Sopenharmony_ci __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci ret = fimc_is_itf_s_param(is, false); 10862306a36Sopenharmony_ci if (ret < 0) 10962306a36Sopenharmony_ci return ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci ret = fimc_pipeline_call(&video->ve, set_stream, 1); 11262306a36Sopenharmony_ci if (ret < 0) 11362306a36Sopenharmony_ci return ret; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci set_bit(ST_ISP_VID_CAP_STREAMING, &isp->state); 11662306a36Sopenharmony_ci return ret; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void isp_video_capture_stop_streaming(struct vb2_queue *q) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct fimc_isp *isp = vb2_get_drv_priv(q); 12262306a36Sopenharmony_ci struct fimc_is *is = fimc_isp_to_is(isp); 12362306a36Sopenharmony_ci struct param_dma_output *dma = __get_isp_dma2(is); 12462306a36Sopenharmony_ci int ret; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0); 12762306a36Sopenharmony_ci if (ret < 0) 12862306a36Sopenharmony_ci return; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci dma->cmd = DMA_OUTPUT_COMMAND_DISABLE; 13162306a36Sopenharmony_ci dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE; 13262306a36Sopenharmony_ci dma->buffer_number = 0; 13362306a36Sopenharmony_ci dma->buffer_address = 0; 13462306a36Sopenharmony_ci dma->dma_out_mask = 0; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT); 13762306a36Sopenharmony_ci __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ret = fimc_is_itf_s_param(is, false); 14062306a36Sopenharmony_ci if (ret < 0) 14162306a36Sopenharmony_ci dev_warn(&is->pdev->dev, "%s: DMA stop failed\n", __func__); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci fimc_is_hw_set_isp_buf_mask(is, 0); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci clear_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state); 14662306a36Sopenharmony_ci clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci isp->video_capture.buf_count = 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int isp_video_capture_buffer_prepare(struct vb2_buffer *vb) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue); 15462306a36Sopenharmony_ci struct fimc_is_video *video = &isp->video_capture; 15562306a36Sopenharmony_ci int i; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (video->format == NULL) 15862306a36Sopenharmony_ci return -EINVAL; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci for (i = 0; i < video->format->memplanes; i++) { 16162306a36Sopenharmony_ci unsigned long size = video->pixfmt.plane_fmt[i].sizeimage; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (vb2_plane_size(vb, i) < size) { 16462306a36Sopenharmony_ci v4l2_err(&video->ve.vdev, 16562306a36Sopenharmony_ci "User buffer too small (%ld < %ld)\n", 16662306a36Sopenharmony_ci vb2_plane_size(vb, i), size); 16762306a36Sopenharmony_ci return -EINVAL; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci vb2_set_plane_payload(vb, i, size); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* Check if we get one of the already known buffers. */ 17362306a36Sopenharmony_ci if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) { 17462306a36Sopenharmony_ci dma_addr_t dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); 17562306a36Sopenharmony_ci int i; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci for (i = 0; i < video->buf_count; i++) 17862306a36Sopenharmony_ci if (video->buffers[i]->dma_addr[0] == dma_addr) 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci return -ENXIO; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic void isp_video_capture_buffer_queue(struct vb2_buffer *vb) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 18962306a36Sopenharmony_ci struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue); 19062306a36Sopenharmony_ci struct fimc_is_video *video = &isp->video_capture; 19162306a36Sopenharmony_ci struct fimc_is *is = fimc_isp_to_is(isp); 19262306a36Sopenharmony_ci struct isp_video_buf *ivb = to_isp_video_buf(vbuf); 19362306a36Sopenharmony_ci unsigned long flags; 19462306a36Sopenharmony_ci unsigned int i; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) { 19762306a36Sopenharmony_ci spin_lock_irqsave(&is->slock, flags); 19862306a36Sopenharmony_ci video->buf_mask |= BIT(ivb->index); 19962306a36Sopenharmony_ci spin_unlock_irqrestore(&is->slock, flags); 20062306a36Sopenharmony_ci } else { 20162306a36Sopenharmony_ci unsigned int num_planes = video->format->memplanes; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci ivb->index = video->buf_count; 20462306a36Sopenharmony_ci video->buffers[ivb->index] = ivb; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci for (i = 0; i < num_planes; i++) { 20762306a36Sopenharmony_ci int buf_index = ivb->index * num_planes + i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci ivb->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); 21062306a36Sopenharmony_ci is->is_p_region->shared[32 + buf_index] = 21162306a36Sopenharmony_ci ivb->dma_addr[i]; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci isp_dbg(2, &video->ve.vdev, 21462306a36Sopenharmony_ci "dma_buf %d (%d/%d/%d) addr: %pad\n", 21562306a36Sopenharmony_ci buf_index, ivb->index, i, vb->index, 21662306a36Sopenharmony_ci &ivb->dma_addr[i]); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (++video->buf_count < video->reqbufs_count) 22062306a36Sopenharmony_ci return; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci video->buf_mask = (1UL << video->buf_count) - 1; 22362306a36Sopenharmony_ci set_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (!test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state)) 22762306a36Sopenharmony_ci isp_video_capture_start_streaming(vb->vb2_queue, 0); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* 23162306a36Sopenharmony_ci * FIMC-IS ISP input and output DMA interface interrupt handler. 23262306a36Sopenharmony_ci * Locking: called with is->slock spinlock held. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_civoid fimc_isp_video_irq_handler(struct fimc_is *is) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct fimc_is_video *video = &is->isp.video_capture; 23762306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf; 23862306a36Sopenharmony_ci int buf_index; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* TODO: Ensure the DMA is really stopped in stop_streaming callback */ 24162306a36Sopenharmony_ci if (!test_bit(ST_ISP_VID_CAP_STREAMING, &is->isp.state)) 24262306a36Sopenharmony_ci return; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci buf_index = (is->i2h_cmd.args[1] - 1) % video->buf_count; 24562306a36Sopenharmony_ci vbuf = &video->buffers[buf_index]->vb; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci vbuf->vb2_buf.timestamp = ktime_get_ns(); 24862306a36Sopenharmony_ci vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci video->buf_mask &= ~BIT(buf_index); 25162306a36Sopenharmony_ci fimc_is_hw_set_isp_buf_mask(is, video->buf_mask); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic const struct vb2_ops isp_video_capture_qops = { 25562306a36Sopenharmony_ci .queue_setup = isp_video_capture_queue_setup, 25662306a36Sopenharmony_ci .buf_prepare = isp_video_capture_buffer_prepare, 25762306a36Sopenharmony_ci .buf_queue = isp_video_capture_buffer_queue, 25862306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 25962306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 26062306a36Sopenharmony_ci .start_streaming = isp_video_capture_start_streaming, 26162306a36Sopenharmony_ci .stop_streaming = isp_video_capture_stop_streaming, 26262306a36Sopenharmony_ci}; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int isp_video_open(struct file *file) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct fimc_isp *isp = video_drvdata(file); 26762306a36Sopenharmony_ci struct exynos_video_entity *ve = &isp->video_capture.ve; 26862306a36Sopenharmony_ci struct media_entity *me = &ve->vdev.entity; 26962306a36Sopenharmony_ci int ret; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (mutex_lock_interruptible(&isp->video_lock)) 27262306a36Sopenharmony_ci return -ERESTARTSYS; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci ret = v4l2_fh_open(file); 27562306a36Sopenharmony_ci if (ret < 0) 27662306a36Sopenharmony_ci goto unlock; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(&isp->pdev->dev); 27962306a36Sopenharmony_ci if (ret < 0) 28062306a36Sopenharmony_ci goto rel_fh; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (v4l2_fh_is_singular_file(file)) { 28362306a36Sopenharmony_ci mutex_lock(&me->graph_obj.mdev->graph_mutex); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ret = fimc_pipeline_call(ve, open, me, true); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* Mark the video pipeline as in use. */ 28862306a36Sopenharmony_ci if (ret == 0) 28962306a36Sopenharmony_ci me->use_count++; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci mutex_unlock(&me->graph_obj.mdev->graph_mutex); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci if (!ret) 29462306a36Sopenharmony_ci goto unlock; 29562306a36Sopenharmony_cirel_fh: 29662306a36Sopenharmony_ci v4l2_fh_release(file); 29762306a36Sopenharmony_ciunlock: 29862306a36Sopenharmony_ci mutex_unlock(&isp->video_lock); 29962306a36Sopenharmony_ci return ret; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int isp_video_release(struct file *file) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct fimc_isp *isp = video_drvdata(file); 30562306a36Sopenharmony_ci struct fimc_is_video *ivc = &isp->video_capture; 30662306a36Sopenharmony_ci struct media_entity *entity = &ivc->ve.vdev.entity; 30762306a36Sopenharmony_ci struct media_device *mdev = entity->graph_obj.mdev; 30862306a36Sopenharmony_ci bool is_singular_file; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci mutex_lock(&isp->video_lock); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci is_singular_file = v4l2_fh_is_singular_file(file); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (is_singular_file && ivc->streaming) { 31562306a36Sopenharmony_ci video_device_pipeline_stop(&ivc->ve.vdev); 31662306a36Sopenharmony_ci ivc->streaming = 0; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci _vb2_fop_release(file, NULL); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (is_singular_file) { 32262306a36Sopenharmony_ci fimc_pipeline_call(&ivc->ve, close); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci mutex_lock(&mdev->graph_mutex); 32562306a36Sopenharmony_ci entity->use_count--; 32662306a36Sopenharmony_ci mutex_unlock(&mdev->graph_mutex); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci pm_runtime_put(&isp->pdev->dev); 33062306a36Sopenharmony_ci mutex_unlock(&isp->video_lock); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic const struct v4l2_file_operations isp_video_fops = { 33662306a36Sopenharmony_ci .owner = THIS_MODULE, 33762306a36Sopenharmony_ci .open = isp_video_open, 33862306a36Sopenharmony_ci .release = isp_video_release, 33962306a36Sopenharmony_ci .poll = vb2_fop_poll, 34062306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 34162306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 34262306a36Sopenharmony_ci}; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci/* 34562306a36Sopenharmony_ci * Video node ioctl operations 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_cistatic int isp_video_querycap(struct file *file, void *priv, 34862306a36Sopenharmony_ci struct v4l2_capability *cap) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct fimc_isp *isp = video_drvdata(file); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci __fimc_vidioc_querycap(&isp->pdev->dev, cap); 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int isp_video_enum_fmt(struct file *file, void *priv, 35762306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci const struct fimc_fmt *fmt; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (f->index >= FIMC_ISP_NUM_FORMATS) 36262306a36Sopenharmony_ci return -EINVAL; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci fmt = fimc_isp_find_format(NULL, NULL, f->index); 36562306a36Sopenharmony_ci if (WARN_ON(fmt == NULL)) 36662306a36Sopenharmony_ci return -EINVAL; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci f->pixelformat = fmt->fourcc; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int isp_video_g_fmt_mplane(struct file *file, void *fh, 37462306a36Sopenharmony_ci struct v4l2_format *f) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct fimc_isp *isp = video_drvdata(file); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci f->fmt.pix_mp = isp->video_capture.pixfmt; 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic void __isp_video_try_fmt(struct fimc_isp *isp, 38362306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pixm, 38462306a36Sopenharmony_ci const struct fimc_fmt **fmt) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci const struct fimc_fmt *__fmt; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci __fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (fmt) 39162306a36Sopenharmony_ci *fmt = __fmt; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci pixm->colorspace = V4L2_COLORSPACE_SRGB; 39462306a36Sopenharmony_ci pixm->field = V4L2_FIELD_NONE; 39562306a36Sopenharmony_ci pixm->num_planes = __fmt->memplanes; 39662306a36Sopenharmony_ci pixm->pixelformat = __fmt->fourcc; 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * TODO: double check with the docmentation these width/height 39962306a36Sopenharmony_ci * constraints are correct. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci v4l_bound_align_image(&pixm->width, FIMC_ISP_SOURCE_WIDTH_MIN, 40262306a36Sopenharmony_ci FIMC_ISP_SOURCE_WIDTH_MAX, 3, 40362306a36Sopenharmony_ci &pixm->height, FIMC_ISP_SOURCE_HEIGHT_MIN, 40462306a36Sopenharmony_ci FIMC_ISP_SOURCE_HEIGHT_MAX, 0, 0); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int isp_video_try_fmt_mplane(struct file *file, void *fh, 40862306a36Sopenharmony_ci struct v4l2_format *f) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct fimc_isp *isp = video_drvdata(file); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci __isp_video_try_fmt(isp, &f->fmt.pix_mp, NULL); 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int isp_video_s_fmt_mplane(struct file *file, void *priv, 41762306a36Sopenharmony_ci struct v4l2_format *f) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct fimc_isp *isp = video_drvdata(file); 42062306a36Sopenharmony_ci struct fimc_is *is = fimc_isp_to_is(isp); 42162306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; 42262306a36Sopenharmony_ci const struct fimc_fmt *ifmt = NULL; 42362306a36Sopenharmony_ci struct param_dma_output *dma = __get_isp_dma2(is); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci __isp_video_try_fmt(isp, pixm, &ifmt); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (WARN_ON(ifmt == NULL)) 42862306a36Sopenharmony_ci return -EINVAL; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci dma->format = DMA_OUTPUT_FORMAT_BAYER; 43162306a36Sopenharmony_ci dma->order = DMA_OUTPUT_ORDER_GB_BG; 43262306a36Sopenharmony_ci dma->plane = ifmt->memplanes; 43362306a36Sopenharmony_ci dma->bitwidth = ifmt->depth[0]; 43462306a36Sopenharmony_ci dma->width = pixm->width; 43562306a36Sopenharmony_ci dma->height = pixm->height; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci fimc_is_mem_barrier(); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci isp->video_capture.format = ifmt; 44062306a36Sopenharmony_ci isp->video_capture.pixfmt = *pixm; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/* 44662306a36Sopenharmony_ci * Check for source/sink format differences at each link. 44762306a36Sopenharmony_ci * Return 0 if the formats match or -EPIPE otherwise. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_cistatic int isp_video_pipeline_validate(struct fimc_isp *isp) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct v4l2_subdev *sd = &isp->subdev; 45262306a36Sopenharmony_ci struct media_pad *pad; 45362306a36Sopenharmony_ci int ret; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci while (1) { 45662306a36Sopenharmony_ci struct v4l2_subdev_format sink_fmt = { 45762306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 45862306a36Sopenharmony_ci }; 45962306a36Sopenharmony_ci struct v4l2_subdev_format src_fmt = { 46062306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 46162306a36Sopenharmony_ci }; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Retrieve format at the sink pad */ 46462306a36Sopenharmony_ci pad = &sd->entity.pads[0]; 46562306a36Sopenharmony_ci if (!(pad->flags & MEDIA_PAD_FL_SINK)) 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci sink_fmt.pad = pad->index; 46862306a36Sopenharmony_ci ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); 46962306a36Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 47062306a36Sopenharmony_ci return -EPIPE; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Retrieve format at the source pad */ 47362306a36Sopenharmony_ci pad = media_pad_remote_pad_first(pad); 47462306a36Sopenharmony_ci if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci sd = media_entity_to_v4l2_subdev(pad->entity); 47862306a36Sopenharmony_ci src_fmt.pad = pad->index; 47962306a36Sopenharmony_ci ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); 48062306a36Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 48162306a36Sopenharmony_ci return -EPIPE; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (src_fmt.format.width != sink_fmt.format.width || 48462306a36Sopenharmony_ci src_fmt.format.height != sink_fmt.format.height || 48562306a36Sopenharmony_ci src_fmt.format.code != sink_fmt.format.code) 48662306a36Sopenharmony_ci return -EPIPE; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int isp_video_streamon(struct file *file, void *priv, 49362306a36Sopenharmony_ci enum v4l2_buf_type type) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct fimc_isp *isp = video_drvdata(file); 49662306a36Sopenharmony_ci struct exynos_video_entity *ve = &isp->video_capture.ve; 49762306a36Sopenharmony_ci int ret; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci ret = video_device_pipeline_start(&ve->vdev, &ve->pipe->mp); 50062306a36Sopenharmony_ci if (ret < 0) 50162306a36Sopenharmony_ci return ret; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ret = isp_video_pipeline_validate(isp); 50462306a36Sopenharmony_ci if (ret < 0) 50562306a36Sopenharmony_ci goto p_stop; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci ret = vb2_ioctl_streamon(file, priv, type); 50862306a36Sopenharmony_ci if (ret < 0) 50962306a36Sopenharmony_ci goto p_stop; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci isp->video_capture.streaming = 1; 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_cip_stop: 51462306a36Sopenharmony_ci video_device_pipeline_stop(&ve->vdev); 51562306a36Sopenharmony_ci return ret; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int isp_video_streamoff(struct file *file, void *priv, 51962306a36Sopenharmony_ci enum v4l2_buf_type type) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct fimc_isp *isp = video_drvdata(file); 52262306a36Sopenharmony_ci struct fimc_is_video *video = &isp->video_capture; 52362306a36Sopenharmony_ci int ret; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ret = vb2_ioctl_streamoff(file, priv, type); 52662306a36Sopenharmony_ci if (ret < 0) 52762306a36Sopenharmony_ci return ret; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci video_device_pipeline_stop(&video->ve.vdev); 53062306a36Sopenharmony_ci video->streaming = 0; 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int isp_video_reqbufs(struct file *file, void *priv, 53562306a36Sopenharmony_ci struct v4l2_requestbuffers *rb) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct fimc_isp *isp = video_drvdata(file); 53862306a36Sopenharmony_ci int ret; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci ret = vb2_ioctl_reqbufs(file, priv, rb); 54162306a36Sopenharmony_ci if (ret < 0) 54262306a36Sopenharmony_ci return ret; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (rb->count && rb->count < FIMC_ISP_REQ_BUFS_MIN) { 54562306a36Sopenharmony_ci rb->count = 0; 54662306a36Sopenharmony_ci vb2_ioctl_reqbufs(file, priv, rb); 54762306a36Sopenharmony_ci ret = -ENOMEM; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci isp->video_capture.reqbufs_count = rb->count; 55162306a36Sopenharmony_ci return ret; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops isp_video_ioctl_ops = { 55562306a36Sopenharmony_ci .vidioc_querycap = isp_video_querycap, 55662306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = isp_video_enum_fmt, 55762306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = isp_video_try_fmt_mplane, 55862306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = isp_video_s_fmt_mplane, 55962306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = isp_video_g_fmt_mplane, 56062306a36Sopenharmony_ci .vidioc_reqbufs = isp_video_reqbufs, 56162306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 56262306a36Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 56362306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 56462306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 56562306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 56662306a36Sopenharmony_ci .vidioc_streamon = isp_video_streamon, 56762306a36Sopenharmony_ci .vidioc_streamoff = isp_video_streamoff, 56862306a36Sopenharmony_ci}; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ciint fimc_isp_video_device_register(struct fimc_isp *isp, 57162306a36Sopenharmony_ci struct v4l2_device *v4l2_dev, 57262306a36Sopenharmony_ci enum v4l2_buf_type type) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct vb2_queue *q = &isp->video_capture.vb_queue; 57562306a36Sopenharmony_ci struct fimc_is_video *iv; 57662306a36Sopenharmony_ci struct video_device *vdev; 57762306a36Sopenharmony_ci int ret; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) 58062306a36Sopenharmony_ci iv = &isp->video_capture; 58162306a36Sopenharmony_ci else 58262306a36Sopenharmony_ci return -ENOSYS; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci mutex_init(&isp->video_lock); 58562306a36Sopenharmony_ci INIT_LIST_HEAD(&iv->pending_buf_q); 58662306a36Sopenharmony_ci INIT_LIST_HEAD(&iv->active_buf_q); 58762306a36Sopenharmony_ci iv->format = fimc_isp_find_format(NULL, NULL, 0); 58862306a36Sopenharmony_ci iv->pixfmt.width = IS_DEFAULT_WIDTH; 58962306a36Sopenharmony_ci iv->pixfmt.height = IS_DEFAULT_HEIGHT; 59062306a36Sopenharmony_ci iv->pixfmt.pixelformat = iv->format->fourcc; 59162306a36Sopenharmony_ci iv->pixfmt.colorspace = V4L2_COLORSPACE_SRGB; 59262306a36Sopenharmony_ci iv->reqbufs_count = 0; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci memset(q, 0, sizeof(*q)); 59562306a36Sopenharmony_ci q->type = type; 59662306a36Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_USERPTR; 59762306a36Sopenharmony_ci q->ops = &isp_video_capture_qops; 59862306a36Sopenharmony_ci q->mem_ops = &vb2_dma_contig_memops; 59962306a36Sopenharmony_ci q->buf_struct_size = sizeof(struct isp_video_buf); 60062306a36Sopenharmony_ci q->drv_priv = isp; 60162306a36Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 60262306a36Sopenharmony_ci q->lock = &isp->video_lock; 60362306a36Sopenharmony_ci q->dev = &isp->pdev->dev; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci ret = vb2_queue_init(q); 60662306a36Sopenharmony_ci if (ret < 0) 60762306a36Sopenharmony_ci return ret; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci vdev = &iv->ve.vdev; 61062306a36Sopenharmony_ci memset(vdev, 0, sizeof(*vdev)); 61162306a36Sopenharmony_ci strscpy(vdev->name, "fimc-is-isp.capture", sizeof(vdev->name)); 61262306a36Sopenharmony_ci vdev->queue = q; 61362306a36Sopenharmony_ci vdev->fops = &isp_video_fops; 61462306a36Sopenharmony_ci vdev->ioctl_ops = &isp_video_ioctl_ops; 61562306a36Sopenharmony_ci vdev->v4l2_dev = v4l2_dev; 61662306a36Sopenharmony_ci vdev->minor = -1; 61762306a36Sopenharmony_ci vdev->release = video_device_release_empty; 61862306a36Sopenharmony_ci vdev->lock = &isp->video_lock; 61962306a36Sopenharmony_ci vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci iv->pad.flags = MEDIA_PAD_FL_SINK; 62262306a36Sopenharmony_ci ret = media_entity_pads_init(&vdev->entity, 1, &iv->pad); 62362306a36Sopenharmony_ci if (ret < 0) 62462306a36Sopenharmony_ci return ret; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci video_set_drvdata(vdev, isp); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 62962306a36Sopenharmony_ci if (ret < 0) { 63062306a36Sopenharmony_ci media_entity_cleanup(&vdev->entity); 63162306a36Sopenharmony_ci return ret; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", 63562306a36Sopenharmony_ci vdev->name, video_device_node_name(vdev)); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return 0; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_civoid fimc_isp_video_device_unregister(struct fimc_isp *isp, 64162306a36Sopenharmony_ci enum v4l2_buf_type type) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct exynos_video_entity *ve; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) 64662306a36Sopenharmony_ci ve = &isp->video_capture.ve; 64762306a36Sopenharmony_ci else 64862306a36Sopenharmony_ci return; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci mutex_lock(&isp->video_lock); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (video_is_registered(&ve->vdev)) { 65362306a36Sopenharmony_ci video_unregister_device(&ve->vdev); 65462306a36Sopenharmony_ci media_entity_cleanup(&ve->vdev.entity); 65562306a36Sopenharmony_ci ve->pipe = NULL; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci mutex_unlock(&isp->video_lock); 65962306a36Sopenharmony_ci} 660