18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * s3c24xx/s3c64xx SoC series Camera Interface (CAMIF) driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on drivers/media/platform/s5p-fimc, 98c2ecf20Sopenharmony_ci * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. 108c2ecf20Sopenharmony_ci*/ 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/bug.h> 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/i2c.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/list.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 258c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <linux/types.h> 288c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <media/media-device.h> 318c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 328c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 338c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 348c2ecf20Sopenharmony_ci#include <media/videobuf2-v4l2.h> 358c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "camif-core.h" 388c2ecf20Sopenharmony_ci#include "camif-regs.h" 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int debug; 418c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Locking: called with vp->camif->slock spinlock held */ 448c2ecf20Sopenharmony_cistatic void camif_cfg_video_path(struct camif_vp *vp) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci WARN_ON(s3c_camif_get_scaler_config(vp, &vp->scaler)); 478c2ecf20Sopenharmony_ci camif_hw_set_scaler(vp); 488c2ecf20Sopenharmony_ci camif_hw_set_flip(vp); 498c2ecf20Sopenharmony_ci camif_hw_set_target_format(vp); 508c2ecf20Sopenharmony_ci camif_hw_set_output_dma(vp); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void camif_prepare_dma_offset(struct camif_vp *vp) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct camif_frame *f = &vp->out_frame; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci f->dma_offset.initial = f->rect.top * f->f_width + f->rect.left; 588c2ecf20Sopenharmony_ci f->dma_offset.line = f->f_width - (f->rect.left + f->rect.width); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci pr_debug("dma_offset: initial: %d, line: %d\n", 618c2ecf20Sopenharmony_ci f->dma_offset.initial, f->dma_offset.line); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Locking: called with camif->slock spinlock held */ 658c2ecf20Sopenharmony_cistatic int s3c_camif_hw_init(struct camif_dev *camif, struct camif_vp *vp) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci const struct s3c_camif_variant *variant = camif->variant; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (camif->sensor.sd == NULL || vp->out_fmt == NULL) 708c2ecf20Sopenharmony_ci return -EINVAL; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (variant->ip_revision == S3C244X_CAMIF_IP_REV) 738c2ecf20Sopenharmony_ci camif_hw_clear_fifo_overflow(vp); 748c2ecf20Sopenharmony_ci camif_hw_set_camera_bus(camif); 758c2ecf20Sopenharmony_ci camif_hw_set_source_format(camif); 768c2ecf20Sopenharmony_ci camif_hw_set_camera_crop(camif); 778c2ecf20Sopenharmony_ci camif_hw_set_test_pattern(camif, camif->test_pattern); 788c2ecf20Sopenharmony_ci if (variant->has_img_effect) 798c2ecf20Sopenharmony_ci camif_hw_set_effect(camif, camif->colorfx, 808c2ecf20Sopenharmony_ci camif->colorfx_cr, camif->colorfx_cb); 818c2ecf20Sopenharmony_ci if (variant->ip_revision == S3C6410_CAMIF_IP_REV) 828c2ecf20Sopenharmony_ci camif_hw_set_input_path(vp); 838c2ecf20Sopenharmony_ci camif_cfg_video_path(vp); 848c2ecf20Sopenharmony_ci vp->state &= ~ST_VP_CONFIG; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* 908c2ecf20Sopenharmony_ci * Initialize the video path, only up from the scaler stage. The camera 918c2ecf20Sopenharmony_ci * input interface set up is skipped. This is useful to enable one of the 928c2ecf20Sopenharmony_ci * video paths when the other is already running. 938c2ecf20Sopenharmony_ci * Locking: called with camif->slock spinlock held. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_cistatic int s3c_camif_hw_vp_init(struct camif_dev *camif, struct camif_vp *vp) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci unsigned int ip_rev = camif->variant->ip_revision; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (vp->out_fmt == NULL) 1008c2ecf20Sopenharmony_ci return -EINVAL; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci camif_prepare_dma_offset(vp); 1038c2ecf20Sopenharmony_ci if (ip_rev == S3C244X_CAMIF_IP_REV) 1048c2ecf20Sopenharmony_ci camif_hw_clear_fifo_overflow(vp); 1058c2ecf20Sopenharmony_ci camif_cfg_video_path(vp); 1068c2ecf20Sopenharmony_ci vp->state &= ~ST_VP_CONFIG; 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int sensor_set_power(struct camif_dev *camif, int on) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct cam_sensor *sensor = &camif->sensor; 1138c2ecf20Sopenharmony_ci int err = 0; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (camif->sensor.power_count == !on) 1168c2ecf20Sopenharmony_ci err = v4l2_subdev_call(sensor->sd, core, s_power, on); 1178c2ecf20Sopenharmony_ci if (err == -ENOIOCTLCMD) 1188c2ecf20Sopenharmony_ci err = 0; 1198c2ecf20Sopenharmony_ci if (!err) 1208c2ecf20Sopenharmony_ci sensor->power_count += on ? 1 : -1; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci pr_debug("on: %d, power_count: %d, err: %d\n", 1238c2ecf20Sopenharmony_ci on, sensor->power_count, err); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return err; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int sensor_set_streaming(struct camif_dev *camif, int on) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct cam_sensor *sensor = &camif->sensor; 1318c2ecf20Sopenharmony_ci int err = 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (camif->sensor.stream_count == !on) 1348c2ecf20Sopenharmony_ci err = v4l2_subdev_call(sensor->sd, video, s_stream, on); 1358c2ecf20Sopenharmony_ci if (!err) 1368c2ecf20Sopenharmony_ci sensor->stream_count += on ? 1 : -1; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci pr_debug("on: %d, stream_count: %d, err: %d\n", 1398c2ecf20Sopenharmony_ci on, sensor->stream_count, err); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return err; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * Reinitialize the driver so it is ready to start streaming again. 1468c2ecf20Sopenharmony_ci * Return any buffers to vb2, perform CAMIF software reset and 1478c2ecf20Sopenharmony_ci * turn off streaming at the data pipeline (sensor) if required. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_cistatic int camif_reinitialize(struct camif_vp *vp) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 1528c2ecf20Sopenharmony_ci struct camif_buffer *buf; 1538c2ecf20Sopenharmony_ci unsigned long flags; 1548c2ecf20Sopenharmony_ci bool streaming; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 1578c2ecf20Sopenharmony_ci streaming = vp->state & ST_VP_SENSOR_STREAMING; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci vp->state &= ~(ST_VP_PENDING | ST_VP_RUNNING | ST_VP_OFF | 1608c2ecf20Sopenharmony_ci ST_VP_ABORTING | ST_VP_STREAMING | 1618c2ecf20Sopenharmony_ci ST_VP_SENSOR_STREAMING | ST_VP_LASTIRQ); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Release unused buffers */ 1648c2ecf20Sopenharmony_ci while (!list_empty(&vp->pending_buf_q)) { 1658c2ecf20Sopenharmony_ci buf = camif_pending_queue_pop(vp); 1668c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci while (!list_empty(&vp->active_buf_q)) { 1708c2ecf20Sopenharmony_ci buf = camif_active_queue_pop(vp); 1718c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (!streaming) 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return sensor_set_streaming(camif, 0); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic bool s3c_vp_active(struct camif_vp *vp) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 1858c2ecf20Sopenharmony_ci unsigned long flags; 1868c2ecf20Sopenharmony_ci bool ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 1898c2ecf20Sopenharmony_ci ret = (vp->state & ST_VP_RUNNING) || (vp->state & ST_VP_PENDING); 1908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic bool camif_is_streaming(struct camif_dev *camif) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci unsigned long flags; 1988c2ecf20Sopenharmony_ci bool status; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 2018c2ecf20Sopenharmony_ci status = camif->stream_count > 0; 2028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return status; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int camif_stop_capture(struct camif_vp *vp) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 2108c2ecf20Sopenharmony_ci unsigned long flags; 2118c2ecf20Sopenharmony_ci int ret; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (!s3c_vp_active(vp)) 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 2178c2ecf20Sopenharmony_ci vp->state &= ~(ST_VP_OFF | ST_VP_LASTIRQ); 2188c2ecf20Sopenharmony_ci vp->state |= ST_VP_ABORTING; 2198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ret = wait_event_timeout(vp->irq_queue, 2228c2ecf20Sopenharmony_ci !(vp->state & ST_VP_ABORTING), 2238c2ecf20Sopenharmony_ci msecs_to_jiffies(CAMIF_STOP_TIMEOUT)); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (ret == 0 && !(vp->state & ST_VP_OFF)) { 2288c2ecf20Sopenharmony_ci /* Timed out, forcibly stop capture */ 2298c2ecf20Sopenharmony_ci vp->state &= ~(ST_VP_OFF | ST_VP_ABORTING | 2308c2ecf20Sopenharmony_ci ST_VP_LASTIRQ); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci camif_hw_disable_capture(vp); 2338c2ecf20Sopenharmony_ci camif_hw_enable_scaler(vp, false); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return camif_reinitialize(vp); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int camif_prepare_addr(struct camif_vp *vp, struct vb2_buffer *vb, 2428c2ecf20Sopenharmony_ci struct camif_addr *paddr) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct camif_frame *frame = &vp->out_frame; 2458c2ecf20Sopenharmony_ci u32 pix_size; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (vb == NULL || frame == NULL) 2488c2ecf20Sopenharmony_ci return -EINVAL; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci pix_size = frame->rect.width * frame->rect.height; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci pr_debug("colplanes: %d, pix_size: %u\n", 2538c2ecf20Sopenharmony_ci vp->out_fmt->colplanes, pix_size); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci switch (vp->out_fmt->colplanes) { 2588c2ecf20Sopenharmony_ci case 1: 2598c2ecf20Sopenharmony_ci paddr->cb = 0; 2608c2ecf20Sopenharmony_ci paddr->cr = 0; 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci case 2: 2638c2ecf20Sopenharmony_ci /* decompose Y into Y/Cb */ 2648c2ecf20Sopenharmony_ci paddr->cb = (u32)(paddr->y + pix_size); 2658c2ecf20Sopenharmony_ci paddr->cr = 0; 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci case 3: 2688c2ecf20Sopenharmony_ci paddr->cb = (u32)(paddr->y + pix_size); 2698c2ecf20Sopenharmony_ci /* decompose Y into Y/Cb/Cr */ 2708c2ecf20Sopenharmony_ci if (vp->out_fmt->color == IMG_FMT_YCBCR422P) 2718c2ecf20Sopenharmony_ci paddr->cr = (u32)(paddr->cb + (pix_size >> 1)); 2728c2ecf20Sopenharmony_ci else /* 420 */ 2738c2ecf20Sopenharmony_ci paddr->cr = (u32)(paddr->cb + (pix_size >> 2)); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (vp->out_fmt->color == IMG_FMT_YCRCB420) 2768c2ecf20Sopenharmony_ci swap(paddr->cb, paddr->cr); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci default: 2798c2ecf20Sopenharmony_ci return -EINVAL; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci pr_debug("DMA address: y: %pad cb: %pad cr: %pad\n", 2838c2ecf20Sopenharmony_ci &paddr->y, &paddr->cb, &paddr->cr); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ciirqreturn_t s3c_camif_irq_handler(int irq, void *priv) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct camif_vp *vp = priv; 2918c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 2928c2ecf20Sopenharmony_ci unsigned int ip_rev = camif->variant->ip_revision; 2938c2ecf20Sopenharmony_ci unsigned int status; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci spin_lock(&camif->slock); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (ip_rev == S3C6410_CAMIF_IP_REV) 2988c2ecf20Sopenharmony_ci camif_hw_clear_pending_irq(vp); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci status = camif_hw_get_status(vp); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (ip_rev == S3C244X_CAMIF_IP_REV && (status & CISTATUS_OVF_MASK)) { 3038c2ecf20Sopenharmony_ci camif_hw_clear_fifo_overflow(vp); 3048c2ecf20Sopenharmony_ci goto unlock; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (vp->state & ST_VP_ABORTING) { 3088c2ecf20Sopenharmony_ci if (vp->state & ST_VP_OFF) { 3098c2ecf20Sopenharmony_ci /* Last IRQ */ 3108c2ecf20Sopenharmony_ci vp->state &= ~(ST_VP_OFF | ST_VP_ABORTING | 3118c2ecf20Sopenharmony_ci ST_VP_LASTIRQ); 3128c2ecf20Sopenharmony_ci wake_up(&vp->irq_queue); 3138c2ecf20Sopenharmony_ci goto unlock; 3148c2ecf20Sopenharmony_ci } else if (vp->state & ST_VP_LASTIRQ) { 3158c2ecf20Sopenharmony_ci camif_hw_disable_capture(vp); 3168c2ecf20Sopenharmony_ci camif_hw_enable_scaler(vp, false); 3178c2ecf20Sopenharmony_ci camif_hw_set_lastirq(vp, false); 3188c2ecf20Sopenharmony_ci vp->state |= ST_VP_OFF; 3198c2ecf20Sopenharmony_ci } else { 3208c2ecf20Sopenharmony_ci /* Disable capture, enable last IRQ */ 3218c2ecf20Sopenharmony_ci camif_hw_set_lastirq(vp, true); 3228c2ecf20Sopenharmony_ci vp->state |= ST_VP_LASTIRQ; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!list_empty(&vp->pending_buf_q) && (vp->state & ST_VP_RUNNING) && 3278c2ecf20Sopenharmony_ci !list_empty(&vp->active_buf_q)) { 3288c2ecf20Sopenharmony_ci unsigned int index; 3298c2ecf20Sopenharmony_ci struct camif_buffer *vbuf; 3308c2ecf20Sopenharmony_ci /* 3318c2ecf20Sopenharmony_ci * Get previous DMA write buffer index: 3328c2ecf20Sopenharmony_ci * 0 => DMA buffer 0, 2; 3338c2ecf20Sopenharmony_ci * 1 => DMA buffer 1, 3. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci index = (CISTATUS_FRAMECNT(status) + 2) & 1; 3368c2ecf20Sopenharmony_ci vbuf = camif_active_queue_peek(vp, index); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!WARN_ON(vbuf == NULL)) { 3398c2ecf20Sopenharmony_ci /* Dequeue a filled buffer */ 3408c2ecf20Sopenharmony_ci vbuf->vb.vb2_buf.timestamp = ktime_get_ns(); 3418c2ecf20Sopenharmony_ci vbuf->vb.sequence = vp->frame_sequence++; 3428c2ecf20Sopenharmony_ci vb2_buffer_done(&vbuf->vb.vb2_buf, VB2_BUF_STATE_DONE); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* Set up an empty buffer at the DMA engine */ 3458c2ecf20Sopenharmony_ci vbuf = camif_pending_queue_pop(vp); 3468c2ecf20Sopenharmony_ci vbuf->index = index; 3478c2ecf20Sopenharmony_ci camif_hw_set_output_addr(vp, &vbuf->paddr, index); 3488c2ecf20Sopenharmony_ci camif_hw_set_output_addr(vp, &vbuf->paddr, index + 2); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Scheduled in H/W, add to the queue */ 3518c2ecf20Sopenharmony_ci camif_active_queue_add(vp, vbuf); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci } else if (!(vp->state & ST_VP_ABORTING) && 3548c2ecf20Sopenharmony_ci (vp->state & ST_VP_PENDING)) { 3558c2ecf20Sopenharmony_ci vp->state |= ST_VP_RUNNING; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (vp->state & ST_VP_CONFIG) { 3598c2ecf20Sopenharmony_ci camif_prepare_dma_offset(vp); 3608c2ecf20Sopenharmony_ci camif_hw_set_camera_crop(camif); 3618c2ecf20Sopenharmony_ci camif_hw_set_scaler(vp); 3628c2ecf20Sopenharmony_ci camif_hw_set_flip(vp); 3638c2ecf20Sopenharmony_ci camif_hw_set_test_pattern(camif, camif->test_pattern); 3648c2ecf20Sopenharmony_ci if (camif->variant->has_img_effect) 3658c2ecf20Sopenharmony_ci camif_hw_set_effect(camif, camif->colorfx, 3668c2ecf20Sopenharmony_ci camif->colorfx_cr, camif->colorfx_cb); 3678c2ecf20Sopenharmony_ci vp->state &= ~ST_VP_CONFIG; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ciunlock: 3708c2ecf20Sopenharmony_ci spin_unlock(&camif->slock); 3718c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct camif_vp *vp = vb2_get_drv_priv(vq); 3778c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 3788c2ecf20Sopenharmony_ci unsigned long flags; 3798c2ecf20Sopenharmony_ci int ret; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * We assume the codec capture path is always activated 3838c2ecf20Sopenharmony_ci * first, before the preview path starts streaming. 3848c2ecf20Sopenharmony_ci * This is required to avoid internal FIFO overflow and 3858c2ecf20Sopenharmony_ci * a need for CAMIF software reset. 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (camif->stream_count == 0) { 3908c2ecf20Sopenharmony_ci camif_hw_reset(camif); 3918c2ecf20Sopenharmony_ci ret = s3c_camif_hw_init(camif, vp); 3928c2ecf20Sopenharmony_ci } else { 3938c2ecf20Sopenharmony_ci ret = s3c_camif_hw_vp_init(camif, vp); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (ret < 0) { 3988c2ecf20Sopenharmony_ci camif_reinitialize(vp); 3998c2ecf20Sopenharmony_ci return ret; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 4038c2ecf20Sopenharmony_ci vp->frame_sequence = 0; 4048c2ecf20Sopenharmony_ci vp->state |= ST_VP_PENDING; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (!list_empty(&vp->pending_buf_q) && 4078c2ecf20Sopenharmony_ci (!(vp->state & ST_VP_STREAMING) || 4088c2ecf20Sopenharmony_ci !(vp->state & ST_VP_SENSOR_STREAMING))) { 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci camif_hw_enable_scaler(vp, vp->scaler.enable); 4118c2ecf20Sopenharmony_ci camif_hw_enable_capture(vp); 4128c2ecf20Sopenharmony_ci vp->state |= ST_VP_STREAMING; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (!(vp->state & ST_VP_SENSOR_STREAMING)) { 4158c2ecf20Sopenharmony_ci vp->state |= ST_VP_SENSOR_STREAMING; 4168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 4178c2ecf20Sopenharmony_ci ret = sensor_set_streaming(camif, 1); 4188c2ecf20Sopenharmony_ci if (ret) 4198c2ecf20Sopenharmony_ci v4l2_err(&vp->vdev, "Sensor s_stream failed\n"); 4208c2ecf20Sopenharmony_ci if (debug) 4218c2ecf20Sopenharmony_ci camif_hw_dump_regs(camif, __func__); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return ret; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct camif_vp *vp = vb2_get_drv_priv(vq); 4348c2ecf20Sopenharmony_ci camif_stop_capture(vp); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq, 4388c2ecf20Sopenharmony_ci unsigned int *num_buffers, unsigned int *num_planes, 4398c2ecf20Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct camif_vp *vp = vb2_get_drv_priv(vq); 4428c2ecf20Sopenharmony_ci struct camif_frame *frame = &vp->out_frame; 4438c2ecf20Sopenharmony_ci const struct camif_fmt *fmt = vp->out_fmt; 4448c2ecf20Sopenharmony_ci unsigned int size; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (fmt == NULL) 4478c2ecf20Sopenharmony_ci return -EINVAL; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci size = (frame->f_width * frame->f_height * fmt->depth) / 8; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (*num_planes) 4528c2ecf20Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci *num_planes = 1; 4558c2ecf20Sopenharmony_ci sizes[0] = size; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci pr_debug("size: %u\n", sizes[0]); 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct camif_vp *vp = vb2_get_drv_priv(vb->vb2_queue); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (vp->out_fmt == NULL) 4668c2ecf20Sopenharmony_ci return -EINVAL; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (vb2_plane_size(vb, 0) < vp->payload) { 4698c2ecf20Sopenharmony_ci v4l2_err(&vp->vdev, "buffer too small: %lu, required: %u\n", 4708c2ecf20Sopenharmony_ci vb2_plane_size(vb, 0), vp->payload); 4718c2ecf20Sopenharmony_ci return -EINVAL; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, 0, vp->payload); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 4818c2ecf20Sopenharmony_ci struct camif_buffer *buf = container_of(vbuf, struct camif_buffer, vb); 4828c2ecf20Sopenharmony_ci struct camif_vp *vp = vb2_get_drv_priv(vb->vb2_queue); 4838c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 4848c2ecf20Sopenharmony_ci unsigned long flags; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 4878c2ecf20Sopenharmony_ci WARN_ON(camif_prepare_addr(vp, &buf->vb.vb2_buf, &buf->paddr)); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (!(vp->state & ST_VP_STREAMING) && vp->active_buffers < 2) { 4908c2ecf20Sopenharmony_ci /* Schedule an empty buffer in H/W */ 4918c2ecf20Sopenharmony_ci buf->index = vp->buf_index; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci camif_hw_set_output_addr(vp, &buf->paddr, buf->index); 4948c2ecf20Sopenharmony_ci camif_hw_set_output_addr(vp, &buf->paddr, buf->index + 2); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci camif_active_queue_add(vp, buf); 4978c2ecf20Sopenharmony_ci vp->buf_index = !vp->buf_index; 4988c2ecf20Sopenharmony_ci } else { 4998c2ecf20Sopenharmony_ci camif_pending_queue_add(vp, buf); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (vb2_is_streaming(&vp->vb_queue) && !list_empty(&vp->pending_buf_q) 5038c2ecf20Sopenharmony_ci && !(vp->state & ST_VP_STREAMING)) { 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci vp->state |= ST_VP_STREAMING; 5068c2ecf20Sopenharmony_ci camif_hw_enable_scaler(vp, vp->scaler.enable); 5078c2ecf20Sopenharmony_ci camif_hw_enable_capture(vp); 5088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (!(vp->state & ST_VP_SENSOR_STREAMING)) { 5118c2ecf20Sopenharmony_ci if (sensor_set_streaming(camif, 1) == 0) 5128c2ecf20Sopenharmony_ci vp->state |= ST_VP_SENSOR_STREAMING; 5138c2ecf20Sopenharmony_ci else 5148c2ecf20Sopenharmony_ci v4l2_err(&vp->vdev, "Sensor s_stream failed\n"); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (debug) 5178c2ecf20Sopenharmony_ci camif_hw_dump_regs(camif, __func__); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci return; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic const struct vb2_ops s3c_camif_qops = { 5258c2ecf20Sopenharmony_ci .queue_setup = queue_setup, 5268c2ecf20Sopenharmony_ci .buf_prepare = buffer_prepare, 5278c2ecf20Sopenharmony_ci .buf_queue = buffer_queue, 5288c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 5298c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 5308c2ecf20Sopenharmony_ci .start_streaming = start_streaming, 5318c2ecf20Sopenharmony_ci .stop_streaming = stop_streaming, 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int s3c_camif_open(struct file *file) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 5378c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 5388c2ecf20Sopenharmony_ci int ret; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci pr_debug("[vp%d] state: %#x, owner: %p, pid: %d\n", vp->id, 5418c2ecf20Sopenharmony_ci vp->state, vp->owner, task_pid_nr(current)); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&camif->lock)) 5448c2ecf20Sopenharmony_ci return -ERESTARTSYS; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci ret = v4l2_fh_open(file); 5478c2ecf20Sopenharmony_ci if (ret < 0) 5488c2ecf20Sopenharmony_ci goto unlock; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(camif->dev); 5518c2ecf20Sopenharmony_ci if (ret < 0) 5528c2ecf20Sopenharmony_ci goto err_pm; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci ret = sensor_set_power(camif, 1); 5558c2ecf20Sopenharmony_ci if (!ret) 5568c2ecf20Sopenharmony_ci goto unlock; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci pm_runtime_put(camif->dev); 5598c2ecf20Sopenharmony_cierr_pm: 5608c2ecf20Sopenharmony_ci v4l2_fh_release(file); 5618c2ecf20Sopenharmony_ciunlock: 5628c2ecf20Sopenharmony_ci mutex_unlock(&camif->lock); 5638c2ecf20Sopenharmony_ci return ret; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic int s3c_camif_close(struct file *file) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 5698c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 5708c2ecf20Sopenharmony_ci int ret; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci pr_debug("[vp%d] state: %#x, owner: %p, pid: %d\n", vp->id, 5738c2ecf20Sopenharmony_ci vp->state, vp->owner, task_pid_nr(current)); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci mutex_lock(&camif->lock); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (vp->owner == file->private_data) { 5788c2ecf20Sopenharmony_ci camif_stop_capture(vp); 5798c2ecf20Sopenharmony_ci vb2_queue_release(&vp->vb_queue); 5808c2ecf20Sopenharmony_ci vp->owner = NULL; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci sensor_set_power(camif, 0); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci pm_runtime_put(camif->dev); 5868c2ecf20Sopenharmony_ci ret = v4l2_fh_release(file); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci mutex_unlock(&camif->lock); 5898c2ecf20Sopenharmony_ci return ret; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic __poll_t s3c_camif_poll(struct file *file, 5938c2ecf20Sopenharmony_ci struct poll_table_struct *wait) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 5968c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 5978c2ecf20Sopenharmony_ci __poll_t ret; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci mutex_lock(&camif->lock); 6008c2ecf20Sopenharmony_ci if (vp->owner && vp->owner != file->private_data) 6018c2ecf20Sopenharmony_ci ret = EPOLLERR; 6028c2ecf20Sopenharmony_ci else 6038c2ecf20Sopenharmony_ci ret = vb2_poll(&vp->vb_queue, file, wait); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci mutex_unlock(&camif->lock); 6068c2ecf20Sopenharmony_ci return ret; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int s3c_camif_mmap(struct file *file, struct vm_area_struct *vma) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 6128c2ecf20Sopenharmony_ci int ret; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (vp->owner && vp->owner != file->private_data) 6158c2ecf20Sopenharmony_ci ret = -EBUSY; 6168c2ecf20Sopenharmony_ci else 6178c2ecf20Sopenharmony_ci ret = vb2_mmap(&vp->vb_queue, vma); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return ret; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations s3c_camif_fops = { 6238c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6248c2ecf20Sopenharmony_ci .open = s3c_camif_open, 6258c2ecf20Sopenharmony_ci .release = s3c_camif_close, 6268c2ecf20Sopenharmony_ci .poll = s3c_camif_poll, 6278c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 6288c2ecf20Sopenharmony_ci .mmap = s3c_camif_mmap, 6298c2ecf20Sopenharmony_ci}; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci/* 6328c2ecf20Sopenharmony_ci * Video node IOCTLs 6338c2ecf20Sopenharmony_ci */ 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic int s3c_camif_vidioc_querycap(struct file *file, void *priv, 6368c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci strscpy(cap->driver, S3C_CAMIF_DRIVER_NAME, sizeof(cap->driver)); 6418c2ecf20Sopenharmony_ci strscpy(cap->card, S3C_CAMIF_DRIVER_NAME, sizeof(cap->card)); 6428c2ecf20Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s.%d", 6438c2ecf20Sopenharmony_ci dev_name(vp->camif->dev), vp->id); 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic int s3c_camif_vidioc_enum_input(struct file *file, void *priv, 6488c2ecf20Sopenharmony_ci struct v4l2_input *input) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 6518c2ecf20Sopenharmony_ci struct v4l2_subdev *sensor = vp->camif->sensor.sd; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (input->index || sensor == NULL) 6548c2ecf20Sopenharmony_ci return -EINVAL; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci input->type = V4L2_INPUT_TYPE_CAMERA; 6578c2ecf20Sopenharmony_ci strscpy(input->name, sensor->name, sizeof(input->name)); 6588c2ecf20Sopenharmony_ci return 0; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic int s3c_camif_vidioc_s_input(struct file *file, void *priv, 6628c2ecf20Sopenharmony_ci unsigned int i) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci return i == 0 ? 0 : -EINVAL; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic int s3c_camif_vidioc_g_input(struct file *file, void *priv, 6688c2ecf20Sopenharmony_ci unsigned int *i) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci *i = 0; 6718c2ecf20Sopenharmony_ci return 0; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic int s3c_camif_vidioc_enum_fmt(struct file *file, void *priv, 6758c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 6788c2ecf20Sopenharmony_ci const struct camif_fmt *fmt; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci fmt = s3c_camif_find_format(vp, NULL, f->index); 6818c2ecf20Sopenharmony_ci if (!fmt) 6828c2ecf20Sopenharmony_ci return -EINVAL; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci f->pixelformat = fmt->fourcc; 6858c2ecf20Sopenharmony_ci return 0; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic int s3c_camif_vidioc_g_fmt(struct file *file, void *priv, 6898c2ecf20Sopenharmony_ci struct v4l2_format *f) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 6928c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 6938c2ecf20Sopenharmony_ci struct camif_frame *frame = &vp->out_frame; 6948c2ecf20Sopenharmony_ci const struct camif_fmt *fmt = vp->out_fmt; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci pix->bytesperline = frame->f_width * fmt->ybpp; 6978c2ecf20Sopenharmony_ci pix->sizeimage = vp->payload; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci pix->pixelformat = fmt->fourcc; 7008c2ecf20Sopenharmony_ci pix->width = frame->f_width; 7018c2ecf20Sopenharmony_ci pix->height = frame->f_height; 7028c2ecf20Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 7038c2ecf20Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_JPEG; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci return 0; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic int __camif_video_try_format(struct camif_vp *vp, 7098c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix, 7108c2ecf20Sopenharmony_ci const struct camif_fmt **ffmt) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 7138c2ecf20Sopenharmony_ci struct v4l2_rect *crop = &camif->camif_crop; 7148c2ecf20Sopenharmony_ci unsigned int wmin, hmin, sc_hrmax, sc_vrmax; 7158c2ecf20Sopenharmony_ci const struct vp_pix_limits *pix_lim; 7168c2ecf20Sopenharmony_ci const struct camif_fmt *fmt; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci fmt = s3c_camif_find_format(vp, &pix->pixelformat, 0); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (WARN_ON(fmt == NULL)) 7218c2ecf20Sopenharmony_ci return -EINVAL; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (ffmt) 7248c2ecf20Sopenharmony_ci *ffmt = fmt; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci pix_lim = &camif->variant->vp_pix_limits[vp->id]; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci pr_debug("fmt: %ux%u, crop: %ux%u, bytesperline: %u\n", 7298c2ecf20Sopenharmony_ci pix->width, pix->height, crop->width, crop->height, 7308c2ecf20Sopenharmony_ci pix->bytesperline); 7318c2ecf20Sopenharmony_ci /* 7328c2ecf20Sopenharmony_ci * Calculate minimum width and height according to the configured 7338c2ecf20Sopenharmony_ci * camera input interface crop rectangle and the resizer's capabilities. 7348c2ecf20Sopenharmony_ci */ 7358c2ecf20Sopenharmony_ci sc_hrmax = min(SCALER_MAX_RATIO, 1 << (ffs(crop->width) - 3)); 7368c2ecf20Sopenharmony_ci sc_vrmax = min(SCALER_MAX_RATIO, 1 << (ffs(crop->height) - 1)); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci wmin = max_t(u32, pix_lim->min_out_width, crop->width / sc_hrmax); 7398c2ecf20Sopenharmony_ci wmin = round_up(wmin, pix_lim->out_width_align); 7408c2ecf20Sopenharmony_ci hmin = max_t(u32, 8, crop->height / sc_vrmax); 7418c2ecf20Sopenharmony_ci hmin = round_up(hmin, 8); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci v4l_bound_align_image(&pix->width, wmin, pix_lim->max_sc_out_width, 7448c2ecf20Sopenharmony_ci ffs(pix_lim->out_width_align) - 1, 7458c2ecf20Sopenharmony_ci &pix->height, hmin, pix_lim->max_height, 0, 0); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci pix->bytesperline = pix->width * fmt->ybpp; 7488c2ecf20Sopenharmony_ci pix->sizeimage = (pix->width * pix->height * fmt->depth) / 8; 7498c2ecf20Sopenharmony_ci pix->pixelformat = fmt->fourcc; 7508c2ecf20Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_JPEG; 7518c2ecf20Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci pr_debug("%ux%u, wmin: %d, hmin: %d, sc_hrmax: %d, sc_vrmax: %d\n", 7548c2ecf20Sopenharmony_ci pix->width, pix->height, wmin, hmin, sc_hrmax, sc_vrmax); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci return 0; 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic int s3c_camif_vidioc_try_fmt(struct file *file, void *priv, 7608c2ecf20Sopenharmony_ci struct v4l2_format *f) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 7638c2ecf20Sopenharmony_ci return __camif_video_try_format(vp, &f->fmt.pix, NULL); 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic int s3c_camif_vidioc_s_fmt(struct file *file, void *priv, 7678c2ecf20Sopenharmony_ci struct v4l2_format *f) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 7708c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 7718c2ecf20Sopenharmony_ci struct camif_frame *out_frame = &vp->out_frame; 7728c2ecf20Sopenharmony_ci const struct camif_fmt *fmt = NULL; 7738c2ecf20Sopenharmony_ci int ret; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci pr_debug("[vp%d]\n", vp->id); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (vb2_is_busy(&vp->vb_queue)) 7788c2ecf20Sopenharmony_ci return -EBUSY; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci ret = __camif_video_try_format(vp, &f->fmt.pix, &fmt); 7818c2ecf20Sopenharmony_ci if (ret < 0) 7828c2ecf20Sopenharmony_ci return ret; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci vp->out_fmt = fmt; 7858c2ecf20Sopenharmony_ci vp->payload = pix->sizeimage; 7868c2ecf20Sopenharmony_ci out_frame->f_width = pix->width; 7878c2ecf20Sopenharmony_ci out_frame->f_height = pix->height; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* Reset composition rectangle */ 7908c2ecf20Sopenharmony_ci out_frame->rect.width = pix->width; 7918c2ecf20Sopenharmony_ci out_frame->rect.height = pix->height; 7928c2ecf20Sopenharmony_ci out_frame->rect.left = 0; 7938c2ecf20Sopenharmony_ci out_frame->rect.top = 0; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (vp->owner == NULL) 7968c2ecf20Sopenharmony_ci vp->owner = priv; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci pr_debug("%ux%u. payload: %u. fmt: 0x%08x. %d %d. sizeimage: %d. bpl: %d\n", 7998c2ecf20Sopenharmony_ci out_frame->f_width, out_frame->f_height, vp->payload, 8008c2ecf20Sopenharmony_ci fmt->fourcc, pix->width * pix->height * fmt->depth, 8018c2ecf20Sopenharmony_ci fmt->depth, pix->sizeimage, pix->bytesperline); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci return 0; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci/* Only check pixel formats at the sensor and the camif subdev pads */ 8078c2ecf20Sopenharmony_cistatic int camif_pipeline_validate(struct camif_dev *camif) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct v4l2_subdev_format src_fmt; 8108c2ecf20Sopenharmony_ci struct media_pad *pad; 8118c2ecf20Sopenharmony_ci int ret; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci /* Retrieve format at the sensor subdev source pad */ 8148c2ecf20Sopenharmony_ci pad = media_entity_remote_pad(&camif->pads[0]); 8158c2ecf20Sopenharmony_ci if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 8168c2ecf20Sopenharmony_ci return -EPIPE; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci src_fmt.pad = pad->index; 8198c2ecf20Sopenharmony_ci src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 8208c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(camif->sensor.sd, pad, get_fmt, NULL, &src_fmt); 8218c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 8228c2ecf20Sopenharmony_ci return -EPIPE; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if (src_fmt.format.width != camif->mbus_fmt.width || 8258c2ecf20Sopenharmony_ci src_fmt.format.height != camif->mbus_fmt.height || 8268c2ecf20Sopenharmony_ci src_fmt.format.code != camif->mbus_fmt.code) 8278c2ecf20Sopenharmony_ci return -EPIPE; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci return 0; 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_cistatic int s3c_camif_streamon(struct file *file, void *priv, 8338c2ecf20Sopenharmony_ci enum v4l2_buf_type type) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 8368c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 8378c2ecf20Sopenharmony_ci struct media_entity *sensor = &camif->sensor.sd->entity; 8388c2ecf20Sopenharmony_ci int ret; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci pr_debug("[vp%d]\n", vp->id); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 8438c2ecf20Sopenharmony_ci return -EINVAL; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (vp->owner && vp->owner != priv) 8468c2ecf20Sopenharmony_ci return -EBUSY; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (s3c_vp_active(vp)) 8498c2ecf20Sopenharmony_ci return 0; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci ret = media_pipeline_start(sensor, camif->m_pipeline); 8528c2ecf20Sopenharmony_ci if (ret < 0) 8538c2ecf20Sopenharmony_ci return ret; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci ret = camif_pipeline_validate(camif); 8568c2ecf20Sopenharmony_ci if (ret < 0) { 8578c2ecf20Sopenharmony_ci media_pipeline_stop(sensor); 8588c2ecf20Sopenharmony_ci return ret; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci return vb2_streamon(&vp->vb_queue, type); 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic int s3c_camif_streamoff(struct file *file, void *priv, 8658c2ecf20Sopenharmony_ci enum v4l2_buf_type type) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 8688c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 8698c2ecf20Sopenharmony_ci int ret; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci pr_debug("[vp%d]\n", vp->id); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 8748c2ecf20Sopenharmony_ci return -EINVAL; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (vp->owner && vp->owner != priv) 8778c2ecf20Sopenharmony_ci return -EBUSY; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci ret = vb2_streamoff(&vp->vb_queue, type); 8808c2ecf20Sopenharmony_ci if (ret == 0) 8818c2ecf20Sopenharmony_ci media_pipeline_stop(&camif->sensor.sd->entity); 8828c2ecf20Sopenharmony_ci return ret; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic int s3c_camif_reqbufs(struct file *file, void *priv, 8868c2ecf20Sopenharmony_ci struct v4l2_requestbuffers *rb) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 8898c2ecf20Sopenharmony_ci int ret; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci pr_debug("[vp%d] rb count: %d, owner: %p, priv: %p\n", 8928c2ecf20Sopenharmony_ci vp->id, rb->count, vp->owner, priv); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (vp->owner && vp->owner != priv) 8958c2ecf20Sopenharmony_ci return -EBUSY; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (rb->count) 8988c2ecf20Sopenharmony_ci rb->count = max_t(u32, CAMIF_REQ_BUFS_MIN, rb->count); 8998c2ecf20Sopenharmony_ci else 9008c2ecf20Sopenharmony_ci vp->owner = NULL; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci ret = vb2_reqbufs(&vp->vb_queue, rb); 9038c2ecf20Sopenharmony_ci if (ret < 0) 9048c2ecf20Sopenharmony_ci return ret; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (rb->count && rb->count < CAMIF_REQ_BUFS_MIN) { 9078c2ecf20Sopenharmony_ci rb->count = 0; 9088c2ecf20Sopenharmony_ci vb2_reqbufs(&vp->vb_queue, rb); 9098c2ecf20Sopenharmony_ci ret = -ENOMEM; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci vp->reqbufs_count = rb->count; 9138c2ecf20Sopenharmony_ci if (vp->owner == NULL && rb->count > 0) 9148c2ecf20Sopenharmony_ci vp->owner = priv; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci return ret; 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic int s3c_camif_querybuf(struct file *file, void *priv, 9208c2ecf20Sopenharmony_ci struct v4l2_buffer *buf) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 9238c2ecf20Sopenharmony_ci return vb2_querybuf(&vp->vb_queue, buf); 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic int s3c_camif_qbuf(struct file *file, void *priv, 9278c2ecf20Sopenharmony_ci struct v4l2_buffer *buf) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci pr_debug("[vp%d]\n", vp->id); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if (vp->owner && vp->owner != priv) 9348c2ecf20Sopenharmony_ci return -EBUSY; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci return vb2_qbuf(&vp->vb_queue, vp->vdev.v4l2_dev->mdev, buf); 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic int s3c_camif_dqbuf(struct file *file, void *priv, 9408c2ecf20Sopenharmony_ci struct v4l2_buffer *buf) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci pr_debug("[vp%d] sequence: %d\n", vp->id, vp->frame_sequence); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (vp->owner && vp->owner != priv) 9478c2ecf20Sopenharmony_ci return -EBUSY; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci return vb2_dqbuf(&vp->vb_queue, buf, file->f_flags & O_NONBLOCK); 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic int s3c_camif_create_bufs(struct file *file, void *priv, 9538c2ecf20Sopenharmony_ci struct v4l2_create_buffers *create) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 9568c2ecf20Sopenharmony_ci int ret; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (vp->owner && vp->owner != priv) 9598c2ecf20Sopenharmony_ci return -EBUSY; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci create->count = max_t(u32, 1, create->count); 9628c2ecf20Sopenharmony_ci ret = vb2_create_bufs(&vp->vb_queue, create); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (!ret && vp->owner == NULL) 9658c2ecf20Sopenharmony_ci vp->owner = priv; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return ret; 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic int s3c_camif_prepare_buf(struct file *file, void *priv, 9718c2ecf20Sopenharmony_ci struct v4l2_buffer *b) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 9748c2ecf20Sopenharmony_ci return vb2_prepare_buf(&vp->vb_queue, vp->vdev.v4l2_dev->mdev, b); 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cistatic int s3c_camif_g_selection(struct file *file, void *priv, 9788c2ecf20Sopenharmony_ci struct v4l2_selection *sel) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 9838c2ecf20Sopenharmony_ci return -EINVAL; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci switch (sel->target) { 9868c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 9878c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 9888c2ecf20Sopenharmony_ci sel->r.left = 0; 9898c2ecf20Sopenharmony_ci sel->r.top = 0; 9908c2ecf20Sopenharmony_ci sel->r.width = vp->out_frame.f_width; 9918c2ecf20Sopenharmony_ci sel->r.height = vp->out_frame.f_height; 9928c2ecf20Sopenharmony_ci return 0; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 9958c2ecf20Sopenharmony_ci sel->r = vp->out_frame.rect; 9968c2ecf20Sopenharmony_ci return 0; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci return -EINVAL; 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic void __camif_try_compose(struct camif_dev *camif, struct camif_vp *vp, 10038c2ecf20Sopenharmony_ci struct v4l2_rect *r) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci /* s3c244x doesn't support composition */ 10068c2ecf20Sopenharmony_ci if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV) { 10078c2ecf20Sopenharmony_ci *r = vp->out_frame.rect; 10088c2ecf20Sopenharmony_ci return; 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* TODO: s3c64xx */ 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_cistatic int s3c_camif_s_selection(struct file *file, void *priv, 10158c2ecf20Sopenharmony_ci struct v4l2_selection *sel) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 10188c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 10198c2ecf20Sopenharmony_ci struct v4l2_rect rect = sel->r; 10208c2ecf20Sopenharmony_ci unsigned long flags; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || 10238c2ecf20Sopenharmony_ci sel->target != V4L2_SEL_TGT_COMPOSE) 10248c2ecf20Sopenharmony_ci return -EINVAL; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci __camif_try_compose(camif, vp, &rect); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci sel->r = rect; 10298c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 10308c2ecf20Sopenharmony_ci vp->out_frame.rect = rect; 10318c2ecf20Sopenharmony_ci vp->state |= ST_VP_CONFIG; 10328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci pr_debug("type: %#x, target: %#x, flags: %#x, (%d,%d)/%dx%d\n", 10358c2ecf20Sopenharmony_ci sel->type, sel->target, sel->flags, 10368c2ecf20Sopenharmony_ci sel->r.left, sel->r.top, sel->r.width, sel->r.height); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci return 0; 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops s3c_camif_ioctl_ops = { 10428c2ecf20Sopenharmony_ci .vidioc_querycap = s3c_camif_vidioc_querycap, 10438c2ecf20Sopenharmony_ci .vidioc_enum_input = s3c_camif_vidioc_enum_input, 10448c2ecf20Sopenharmony_ci .vidioc_g_input = s3c_camif_vidioc_g_input, 10458c2ecf20Sopenharmony_ci .vidioc_s_input = s3c_camif_vidioc_s_input, 10468c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = s3c_camif_vidioc_enum_fmt, 10478c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = s3c_camif_vidioc_try_fmt, 10488c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = s3c_camif_vidioc_s_fmt, 10498c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = s3c_camif_vidioc_g_fmt, 10508c2ecf20Sopenharmony_ci .vidioc_g_selection = s3c_camif_g_selection, 10518c2ecf20Sopenharmony_ci .vidioc_s_selection = s3c_camif_s_selection, 10528c2ecf20Sopenharmony_ci .vidioc_reqbufs = s3c_camif_reqbufs, 10538c2ecf20Sopenharmony_ci .vidioc_querybuf = s3c_camif_querybuf, 10548c2ecf20Sopenharmony_ci .vidioc_prepare_buf = s3c_camif_prepare_buf, 10558c2ecf20Sopenharmony_ci .vidioc_create_bufs = s3c_camif_create_bufs, 10568c2ecf20Sopenharmony_ci .vidioc_qbuf = s3c_camif_qbuf, 10578c2ecf20Sopenharmony_ci .vidioc_dqbuf = s3c_camif_dqbuf, 10588c2ecf20Sopenharmony_ci .vidioc_streamon = s3c_camif_streamon, 10598c2ecf20Sopenharmony_ci .vidioc_streamoff = s3c_camif_streamoff, 10608c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 10618c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 10628c2ecf20Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 10638c2ecf20Sopenharmony_ci}; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci/* 10668c2ecf20Sopenharmony_ci * Video node controls 10678c2ecf20Sopenharmony_ci */ 10688c2ecf20Sopenharmony_cistatic int s3c_camif_video_s_ctrl(struct v4l2_ctrl *ctrl) 10698c2ecf20Sopenharmony_ci{ 10708c2ecf20Sopenharmony_ci struct camif_vp *vp = ctrl->priv; 10718c2ecf20Sopenharmony_ci struct camif_dev *camif = vp->camif; 10728c2ecf20Sopenharmony_ci unsigned long flags; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci pr_debug("[vp%d] ctrl: %s, value: %d\n", vp->id, 10758c2ecf20Sopenharmony_ci ctrl->name, ctrl->val); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci switch (ctrl->id) { 10808c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 10818c2ecf20Sopenharmony_ci vp->hflip = ctrl->val; 10828c2ecf20Sopenharmony_ci break; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci case V4L2_CID_VFLIP: 10858c2ecf20Sopenharmony_ci vp->vflip = ctrl->val; 10868c2ecf20Sopenharmony_ci break; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci vp->state |= ST_VP_CONFIG; 10908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 10918c2ecf20Sopenharmony_ci return 0; 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci/* Codec and preview video node control ops */ 10958c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops s3c_camif_video_ctrl_ops = { 10968c2ecf20Sopenharmony_ci .s_ctrl = s3c_camif_video_s_ctrl, 10978c2ecf20Sopenharmony_ci}; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ciint s3c_camif_register_video_node(struct camif_dev *camif, int idx) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci struct camif_vp *vp = &camif->vp[idx]; 11028c2ecf20Sopenharmony_ci struct vb2_queue *q = &vp->vb_queue; 11038c2ecf20Sopenharmony_ci struct video_device *vfd = &vp->vdev; 11048c2ecf20Sopenharmony_ci struct v4l2_ctrl *ctrl; 11058c2ecf20Sopenharmony_ci int ret; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci memset(vfd, 0, sizeof(*vfd)); 11088c2ecf20Sopenharmony_ci snprintf(vfd->name, sizeof(vfd->name), "camif-%s", 11098c2ecf20Sopenharmony_ci vp->id == 0 ? "codec" : "preview"); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci vfd->fops = &s3c_camif_fops; 11128c2ecf20Sopenharmony_ci vfd->ioctl_ops = &s3c_camif_ioctl_ops; 11138c2ecf20Sopenharmony_ci vfd->v4l2_dev = &camif->v4l2_dev; 11148c2ecf20Sopenharmony_ci vfd->minor = -1; 11158c2ecf20Sopenharmony_ci vfd->release = video_device_release_empty; 11168c2ecf20Sopenharmony_ci vfd->lock = &camif->lock; 11178c2ecf20Sopenharmony_ci vp->reqbufs_count = 0; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vp->pending_buf_q); 11208c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vp->active_buf_q); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci memset(q, 0, sizeof(*q)); 11238c2ecf20Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 11248c2ecf20Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_USERPTR; 11258c2ecf20Sopenharmony_ci q->ops = &s3c_camif_qops; 11268c2ecf20Sopenharmony_ci q->mem_ops = &vb2_dma_contig_memops; 11278c2ecf20Sopenharmony_ci q->buf_struct_size = sizeof(struct camif_buffer); 11288c2ecf20Sopenharmony_ci q->drv_priv = vp; 11298c2ecf20Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 11308c2ecf20Sopenharmony_ci q->lock = &vp->camif->lock; 11318c2ecf20Sopenharmony_ci q->dev = camif->v4l2_dev.dev; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci ret = vb2_queue_init(q); 11348c2ecf20Sopenharmony_ci if (ret) 11358c2ecf20Sopenharmony_ci return ret; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci vp->pad.flags = MEDIA_PAD_FL_SINK; 11388c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&vfd->entity, 1, &vp->pad); 11398c2ecf20Sopenharmony_ci if (ret) 11408c2ecf20Sopenharmony_ci return ret; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci video_set_drvdata(vfd, vp); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&vp->ctrl_handler, 1); 11458c2ecf20Sopenharmony_ci ctrl = v4l2_ctrl_new_std(&vp->ctrl_handler, &s3c_camif_video_ctrl_ops, 11468c2ecf20Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 11478c2ecf20Sopenharmony_ci if (ctrl) 11488c2ecf20Sopenharmony_ci ctrl->priv = vp; 11498c2ecf20Sopenharmony_ci ctrl = v4l2_ctrl_new_std(&vp->ctrl_handler, &s3c_camif_video_ctrl_ops, 11508c2ecf20Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 11518c2ecf20Sopenharmony_ci if (ctrl) 11528c2ecf20Sopenharmony_ci ctrl->priv = vp; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci ret = vp->ctrl_handler.error; 11558c2ecf20Sopenharmony_ci if (ret < 0) 11568c2ecf20Sopenharmony_ci goto err_me_cleanup; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci vfd->ctrl_handler = &vp->ctrl_handler; 11598c2ecf20Sopenharmony_ci vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); 11628c2ecf20Sopenharmony_ci if (ret) 11638c2ecf20Sopenharmony_ci goto err_ctrlh_free; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci v4l2_info(&camif->v4l2_dev, "registered %s as /dev/%s\n", 11668c2ecf20Sopenharmony_ci vfd->name, video_device_node_name(vfd)); 11678c2ecf20Sopenharmony_ci return 0; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_cierr_ctrlh_free: 11708c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&vp->ctrl_handler); 11718c2ecf20Sopenharmony_cierr_me_cleanup: 11728c2ecf20Sopenharmony_ci media_entity_cleanup(&vfd->entity); 11738c2ecf20Sopenharmony_ci return ret; 11748c2ecf20Sopenharmony_ci} 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_civoid s3c_camif_unregister_video_node(struct camif_dev *camif, int idx) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci struct video_device *vfd = &camif->vp[idx].vdev; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci if (video_is_registered(vfd)) { 11818c2ecf20Sopenharmony_ci video_unregister_device(vfd); 11828c2ecf20Sopenharmony_ci media_entity_cleanup(&vfd->entity); 11838c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(vfd->ctrl_handler); 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci/* Media bus pixel formats supported at the camif input */ 11888c2ecf20Sopenharmony_cistatic const u32 camif_mbus_formats[] = { 11898c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 11908c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 11918c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 11928c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 11938c2ecf20Sopenharmony_ci}; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci/* 11968c2ecf20Sopenharmony_ci * Camera input interface subdev operations 11978c2ecf20Sopenharmony_ci */ 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic int s3c_camif_subdev_enum_mbus_code(struct v4l2_subdev *sd, 12008c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 12018c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci if (code->index >= ARRAY_SIZE(camif_mbus_formats)) 12048c2ecf20Sopenharmony_ci return -EINVAL; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci code->code = camif_mbus_formats[code->index]; 12078c2ecf20Sopenharmony_ci return 0; 12088c2ecf20Sopenharmony_ci} 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_cistatic int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, 12118c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 12128c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 12138c2ecf20Sopenharmony_ci{ 12148c2ecf20Sopenharmony_ci struct camif_dev *camif = v4l2_get_subdevdata(sd); 12158c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &fmt->format; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 12188c2ecf20Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 12198c2ecf20Sopenharmony_ci fmt->format = *mf; 12208c2ecf20Sopenharmony_ci return 0; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci mutex_lock(&camif->lock); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci switch (fmt->pad) { 12268c2ecf20Sopenharmony_ci case CAMIF_SD_PAD_SINK: 12278c2ecf20Sopenharmony_ci /* full camera input pixel size */ 12288c2ecf20Sopenharmony_ci *mf = camif->mbus_fmt; 12298c2ecf20Sopenharmony_ci break; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P: 12328c2ecf20Sopenharmony_ci /* crop rectangle at camera interface input */ 12338c2ecf20Sopenharmony_ci mf->width = camif->camif_crop.width; 12348c2ecf20Sopenharmony_ci mf->height = camif->camif_crop.height; 12358c2ecf20Sopenharmony_ci mf->code = camif->mbus_fmt.code; 12368c2ecf20Sopenharmony_ci break; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci mutex_unlock(&camif->lock); 12408c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 12418c2ecf20Sopenharmony_ci mf->colorspace = V4L2_COLORSPACE_JPEG; 12428c2ecf20Sopenharmony_ci return 0; 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic void __camif_subdev_try_format(struct camif_dev *camif, 12468c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf, int pad) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci const struct s3c_camif_variant *variant = camif->variant; 12498c2ecf20Sopenharmony_ci const struct vp_pix_limits *pix_lim; 12508c2ecf20Sopenharmony_ci unsigned int i; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci /* FIXME: constraints against codec or preview path ? */ 12538c2ecf20Sopenharmony_ci pix_lim = &variant->vp_pix_limits[VP_CODEC]; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(camif_mbus_formats); i++) 12568c2ecf20Sopenharmony_ci if (camif_mbus_formats[i] == mf->code) 12578c2ecf20Sopenharmony_ci break; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(camif_mbus_formats)) 12608c2ecf20Sopenharmony_ci mf->code = camif_mbus_formats[0]; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci if (pad == CAMIF_SD_PAD_SINK) { 12638c2ecf20Sopenharmony_ci v4l_bound_align_image(&mf->width, 8, CAMIF_MAX_PIX_WIDTH, 12648c2ecf20Sopenharmony_ci ffs(pix_lim->out_width_align) - 1, 12658c2ecf20Sopenharmony_ci &mf->height, 8, CAMIF_MAX_PIX_HEIGHT, 0, 12668c2ecf20Sopenharmony_ci 0); 12678c2ecf20Sopenharmony_ci } else { 12688c2ecf20Sopenharmony_ci struct v4l2_rect *crop = &camif->camif_crop; 12698c2ecf20Sopenharmony_ci v4l_bound_align_image(&mf->width, 8, crop->width, 12708c2ecf20Sopenharmony_ci ffs(pix_lim->out_width_align) - 1, 12718c2ecf20Sopenharmony_ci &mf->height, 8, crop->height, 12728c2ecf20Sopenharmony_ci 0, 0); 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, &camif->subdev, "%ux%u\n", mf->width, mf->height); 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, 12798c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 12808c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 12818c2ecf20Sopenharmony_ci{ 12828c2ecf20Sopenharmony_ci struct camif_dev *camif = v4l2_get_subdevdata(sd); 12838c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &fmt->format; 12848c2ecf20Sopenharmony_ci struct v4l2_rect *crop = &camif->camif_crop; 12858c2ecf20Sopenharmony_ci int i; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %ux%u\n", 12888c2ecf20Sopenharmony_ci fmt->pad, mf->code, mf->width, mf->height); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 12918c2ecf20Sopenharmony_ci mf->colorspace = V4L2_COLORSPACE_JPEG; 12928c2ecf20Sopenharmony_ci mutex_lock(&camif->lock); 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci /* 12958c2ecf20Sopenharmony_ci * No pixel format change at the camera input is allowed 12968c2ecf20Sopenharmony_ci * while streaming. 12978c2ecf20Sopenharmony_ci */ 12988c2ecf20Sopenharmony_ci if (vb2_is_busy(&camif->vp[VP_CODEC].vb_queue) || 12998c2ecf20Sopenharmony_ci vb2_is_busy(&camif->vp[VP_PREVIEW].vb_queue)) { 13008c2ecf20Sopenharmony_ci mutex_unlock(&camif->lock); 13018c2ecf20Sopenharmony_ci return -EBUSY; 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci __camif_subdev_try_format(camif, mf, fmt->pad); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 13078c2ecf20Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 13088c2ecf20Sopenharmony_ci *mf = fmt->format; 13098c2ecf20Sopenharmony_ci mutex_unlock(&camif->lock); 13108c2ecf20Sopenharmony_ci return 0; 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci switch (fmt->pad) { 13148c2ecf20Sopenharmony_ci case CAMIF_SD_PAD_SINK: 13158c2ecf20Sopenharmony_ci camif->mbus_fmt = *mf; 13168c2ecf20Sopenharmony_ci /* Reset sink crop rectangle. */ 13178c2ecf20Sopenharmony_ci crop->width = mf->width; 13188c2ecf20Sopenharmony_ci crop->height = mf->height; 13198c2ecf20Sopenharmony_ci crop->left = 0; 13208c2ecf20Sopenharmony_ci crop->top = 0; 13218c2ecf20Sopenharmony_ci /* 13228c2ecf20Sopenharmony_ci * Reset source format (the camif's crop rectangle) 13238c2ecf20Sopenharmony_ci * and the video output resolution. 13248c2ecf20Sopenharmony_ci */ 13258c2ecf20Sopenharmony_ci for (i = 0; i < CAMIF_VP_NUM; i++) { 13268c2ecf20Sopenharmony_ci struct camif_frame *frame = &camif->vp[i].out_frame; 13278c2ecf20Sopenharmony_ci frame->rect = *crop; 13288c2ecf20Sopenharmony_ci frame->f_width = mf->width; 13298c2ecf20Sopenharmony_ci frame->f_height = mf->height; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci break; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P: 13348c2ecf20Sopenharmony_ci /* Pixel format can be only changed on the sink pad. */ 13358c2ecf20Sopenharmony_ci mf->code = camif->mbus_fmt.code; 13368c2ecf20Sopenharmony_ci mf->width = crop->width; 13378c2ecf20Sopenharmony_ci mf->height = crop->height; 13388c2ecf20Sopenharmony_ci break; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci mutex_unlock(&camif->lock); 13428c2ecf20Sopenharmony_ci return 0; 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, 13468c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 13478c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 13488c2ecf20Sopenharmony_ci{ 13498c2ecf20Sopenharmony_ci struct camif_dev *camif = v4l2_get_subdevdata(sd); 13508c2ecf20Sopenharmony_ci struct v4l2_rect *crop = &camif->camif_crop; 13518c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci if ((sel->target != V4L2_SEL_TGT_CROP && 13548c2ecf20Sopenharmony_ci sel->target != V4L2_SEL_TGT_CROP_BOUNDS) || 13558c2ecf20Sopenharmony_ci sel->pad != CAMIF_SD_PAD_SINK) 13568c2ecf20Sopenharmony_ci return -EINVAL; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 13598c2ecf20Sopenharmony_ci sel->r = *v4l2_subdev_get_try_crop(sd, cfg, sel->pad); 13608c2ecf20Sopenharmony_ci return 0; 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci mutex_lock(&camif->lock); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci if (sel->target == V4L2_SEL_TGT_CROP) { 13668c2ecf20Sopenharmony_ci sel->r = *crop; 13678c2ecf20Sopenharmony_ci } else { /* crop bounds */ 13688c2ecf20Sopenharmony_ci sel->r.width = mf->width; 13698c2ecf20Sopenharmony_ci sel->r.height = mf->height; 13708c2ecf20Sopenharmony_ci sel->r.left = 0; 13718c2ecf20Sopenharmony_ci sel->r.top = 0; 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci mutex_unlock(&camif->lock); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: crop: (%d,%d) %dx%d, size: %ux%u\n", 13778c2ecf20Sopenharmony_ci __func__, crop->left, crop->top, crop->width, 13788c2ecf20Sopenharmony_ci crop->height, mf->width, mf->height); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci return 0; 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic void __camif_try_crop(struct camif_dev *camif, struct v4l2_rect *r) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; 13868c2ecf20Sopenharmony_ci const struct camif_pix_limits *pix_lim = &camif->variant->pix_limits; 13878c2ecf20Sopenharmony_ci unsigned int left = 2 * r->left; 13888c2ecf20Sopenharmony_ci unsigned int top = 2 * r->top; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci /* 13918c2ecf20Sopenharmony_ci * Following constraints must be met: 13928c2ecf20Sopenharmony_ci * - r->width + 2 * r->left = mf->width; 13938c2ecf20Sopenharmony_ci * - r->height + 2 * r->top = mf->height; 13948c2ecf20Sopenharmony_ci * - crop rectangle size and position must be aligned 13958c2ecf20Sopenharmony_ci * to 8 or 2 pixels, depending on SoC version. 13968c2ecf20Sopenharmony_ci */ 13978c2ecf20Sopenharmony_ci v4l_bound_align_image(&r->width, 0, mf->width, 13988c2ecf20Sopenharmony_ci ffs(pix_lim->win_hor_offset_align) - 1, 13998c2ecf20Sopenharmony_ci &r->height, 0, mf->height, 1, 0); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci v4l_bound_align_image(&left, 0, mf->width - r->width, 14028c2ecf20Sopenharmony_ci ffs(pix_lim->win_hor_offset_align), 14038c2ecf20Sopenharmony_ci &top, 0, mf->height - r->height, 2, 0); 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci r->left = left / 2; 14068c2ecf20Sopenharmony_ci r->top = top / 2; 14078c2ecf20Sopenharmony_ci r->width = mf->width - left; 14088c2ecf20Sopenharmony_ci r->height = mf->height - top; 14098c2ecf20Sopenharmony_ci /* 14108c2ecf20Sopenharmony_ci * Make sure we either downscale or upscale both the pixel 14118c2ecf20Sopenharmony_ci * width and height. Just return current crop rectangle if 14128c2ecf20Sopenharmony_ci * this scaler constraint is not met. 14138c2ecf20Sopenharmony_ci */ 14148c2ecf20Sopenharmony_ci if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV && 14158c2ecf20Sopenharmony_ci camif_is_streaming(camif)) { 14168c2ecf20Sopenharmony_ci unsigned int i; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci for (i = 0; i < CAMIF_VP_NUM; i++) { 14198c2ecf20Sopenharmony_ci struct v4l2_rect *or = &camif->vp[i].out_frame.rect; 14208c2ecf20Sopenharmony_ci if ((or->width > r->width) == (or->height > r->height)) 14218c2ecf20Sopenharmony_ci continue; 14228c2ecf20Sopenharmony_ci *r = camif->camif_crop; 14238c2ecf20Sopenharmony_ci pr_debug("Width/height scaling direction limitation\n"); 14248c2ecf20Sopenharmony_ci break; 14258c2ecf20Sopenharmony_ci } 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, &camif->v4l2_dev, "crop: (%d,%d)/%dx%d, fmt: %ux%u\n", 14298c2ecf20Sopenharmony_ci r->left, r->top, r->width, r->height, mf->width, mf->height); 14308c2ecf20Sopenharmony_ci} 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_cistatic int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, 14338c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 14348c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 14358c2ecf20Sopenharmony_ci{ 14368c2ecf20Sopenharmony_ci struct camif_dev *camif = v4l2_get_subdevdata(sd); 14378c2ecf20Sopenharmony_ci struct v4l2_rect *crop = &camif->camif_crop; 14388c2ecf20Sopenharmony_ci struct camif_scaler scaler; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != CAMIF_SD_PAD_SINK) 14418c2ecf20Sopenharmony_ci return -EINVAL; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci mutex_lock(&camif->lock); 14448c2ecf20Sopenharmony_ci __camif_try_crop(camif, &sel->r); 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 14478c2ecf20Sopenharmony_ci *v4l2_subdev_get_try_crop(sd, cfg, sel->pad) = sel->r; 14488c2ecf20Sopenharmony_ci } else { 14498c2ecf20Sopenharmony_ci unsigned long flags; 14508c2ecf20Sopenharmony_ci unsigned int i; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 14538c2ecf20Sopenharmony_ci *crop = sel->r; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci for (i = 0; i < CAMIF_VP_NUM; i++) { 14568c2ecf20Sopenharmony_ci struct camif_vp *vp = &camif->vp[i]; 14578c2ecf20Sopenharmony_ci scaler = vp->scaler; 14588c2ecf20Sopenharmony_ci if (s3c_camif_get_scaler_config(vp, &scaler)) 14598c2ecf20Sopenharmony_ci continue; 14608c2ecf20Sopenharmony_ci vp->scaler = scaler; 14618c2ecf20Sopenharmony_ci vp->state |= ST_VP_CONFIG; 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci mutex_unlock(&camif->lock); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %u, f_h: %u\n", 14698c2ecf20Sopenharmony_ci __func__, crop->left, crop->top, crop->width, crop->height, 14708c2ecf20Sopenharmony_ci camif->mbus_fmt.width, camif->mbus_fmt.height); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci return 0; 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops s3c_camif_subdev_pad_ops = { 14768c2ecf20Sopenharmony_ci .enum_mbus_code = s3c_camif_subdev_enum_mbus_code, 14778c2ecf20Sopenharmony_ci .get_selection = s3c_camif_subdev_get_selection, 14788c2ecf20Sopenharmony_ci .set_selection = s3c_camif_subdev_set_selection, 14798c2ecf20Sopenharmony_ci .get_fmt = s3c_camif_subdev_get_fmt, 14808c2ecf20Sopenharmony_ci .set_fmt = s3c_camif_subdev_set_fmt, 14818c2ecf20Sopenharmony_ci}; 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops s3c_camif_subdev_ops = { 14848c2ecf20Sopenharmony_ci .pad = &s3c_camif_subdev_pad_ops, 14858c2ecf20Sopenharmony_ci}; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_cistatic int s3c_camif_subdev_s_ctrl(struct v4l2_ctrl *ctrl) 14888c2ecf20Sopenharmony_ci{ 14898c2ecf20Sopenharmony_ci struct camif_dev *camif = container_of(ctrl->handler, struct camif_dev, 14908c2ecf20Sopenharmony_ci ctrl_handler); 14918c2ecf20Sopenharmony_ci unsigned long flags; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci switch (ctrl->id) { 14968c2ecf20Sopenharmony_ci case V4L2_CID_COLORFX: 14978c2ecf20Sopenharmony_ci camif->colorfx = camif->ctrl_colorfx->val; 14988c2ecf20Sopenharmony_ci /* Set Cb, Cr */ 14998c2ecf20Sopenharmony_ci switch (ctrl->val) { 15008c2ecf20Sopenharmony_ci case V4L2_COLORFX_SEPIA: 15018c2ecf20Sopenharmony_ci camif->colorfx_cb = 115; 15028c2ecf20Sopenharmony_ci camif->colorfx_cr = 145; 15038c2ecf20Sopenharmony_ci break; 15048c2ecf20Sopenharmony_ci case V4L2_COLORFX_SET_CBCR: 15058c2ecf20Sopenharmony_ci camif->colorfx_cb = camif->ctrl_colorfx_cbcr->val >> 8; 15068c2ecf20Sopenharmony_ci camif->colorfx_cr = camif->ctrl_colorfx_cbcr->val & 0xff; 15078c2ecf20Sopenharmony_ci break; 15088c2ecf20Sopenharmony_ci default: 15098c2ecf20Sopenharmony_ci /* for V4L2_COLORFX_BW and others */ 15108c2ecf20Sopenharmony_ci camif->colorfx_cb = 128; 15118c2ecf20Sopenharmony_ci camif->colorfx_cr = 128; 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci break; 15148c2ecf20Sopenharmony_ci case V4L2_CID_TEST_PATTERN: 15158c2ecf20Sopenharmony_ci camif->test_pattern = camif->ctrl_test_pattern->val; 15168c2ecf20Sopenharmony_ci break; 15178c2ecf20Sopenharmony_ci default: 15188c2ecf20Sopenharmony_ci WARN_ON(1); 15198c2ecf20Sopenharmony_ci } 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci camif->vp[VP_CODEC].state |= ST_VP_CONFIG; 15228c2ecf20Sopenharmony_ci camif->vp[VP_PREVIEW].state |= ST_VP_CONFIG; 15238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci return 0; 15268c2ecf20Sopenharmony_ci} 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops s3c_camif_subdev_ctrl_ops = { 15298c2ecf20Sopenharmony_ci .s_ctrl = s3c_camif_subdev_s_ctrl, 15308c2ecf20Sopenharmony_ci}; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic const char * const s3c_camif_test_pattern_menu[] = { 15338c2ecf20Sopenharmony_ci "Disabled", 15348c2ecf20Sopenharmony_ci "Color bars", 15358c2ecf20Sopenharmony_ci "Horizontal increment", 15368c2ecf20Sopenharmony_ci "Vertical increment", 15378c2ecf20Sopenharmony_ci}; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ciint s3c_camif_create_subdev(struct camif_dev *camif) 15408c2ecf20Sopenharmony_ci{ 15418c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *handler = &camif->ctrl_handler; 15428c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &camif->subdev; 15438c2ecf20Sopenharmony_ci int ret; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci v4l2_subdev_init(sd, &s3c_camif_subdev_ops); 15468c2ecf20Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 15478c2ecf20Sopenharmony_ci strscpy(sd->name, "S3C-CAMIF", sizeof(sd->name)); 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci camif->pads[CAMIF_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 15508c2ecf20Sopenharmony_ci camif->pads[CAMIF_SD_PAD_SOURCE_C].flags = MEDIA_PAD_FL_SOURCE; 15518c2ecf20Sopenharmony_ci camif->pads[CAMIF_SD_PAD_SOURCE_P].flags = MEDIA_PAD_FL_SOURCE; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, CAMIF_SD_PADS_NUM, 15548c2ecf20Sopenharmony_ci camif->pads); 15558c2ecf20Sopenharmony_ci if (ret) 15568c2ecf20Sopenharmony_ci return ret; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(handler, 3); 15598c2ecf20Sopenharmony_ci camif->ctrl_test_pattern = v4l2_ctrl_new_std_menu_items(handler, 15608c2ecf20Sopenharmony_ci &s3c_camif_subdev_ctrl_ops, V4L2_CID_TEST_PATTERN, 15618c2ecf20Sopenharmony_ci ARRAY_SIZE(s3c_camif_test_pattern_menu) - 1, 0, 0, 15628c2ecf20Sopenharmony_ci s3c_camif_test_pattern_menu); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci if (camif->variant->has_img_effect) { 15658c2ecf20Sopenharmony_ci camif->ctrl_colorfx = v4l2_ctrl_new_std_menu(handler, 15668c2ecf20Sopenharmony_ci &s3c_camif_subdev_ctrl_ops, 15678c2ecf20Sopenharmony_ci V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR, 15688c2ecf20Sopenharmony_ci ~0x981f, V4L2_COLORFX_NONE); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci camif->ctrl_colorfx_cbcr = v4l2_ctrl_new_std(handler, 15718c2ecf20Sopenharmony_ci &s3c_camif_subdev_ctrl_ops, 15728c2ecf20Sopenharmony_ci V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0); 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci if (handler->error) { 15768c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(handler); 15778c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 15788c2ecf20Sopenharmony_ci return handler->error; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (camif->variant->has_img_effect) 15828c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &camif->ctrl_colorfx, 15838c2ecf20Sopenharmony_ci V4L2_COLORFX_SET_CBCR, false); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci sd->ctrl_handler = handler; 15868c2ecf20Sopenharmony_ci v4l2_set_subdevdata(sd, camif); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci return 0; 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_civoid s3c_camif_unregister_subdev(struct camif_dev *camif) 15928c2ecf20Sopenharmony_ci{ 15938c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &camif->subdev; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci /* Return if not registered */ 15968c2ecf20Sopenharmony_ci if (v4l2_get_subdevdata(sd) == NULL) 15978c2ecf20Sopenharmony_ci return; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 16008c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 16018c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&camif->ctrl_handler); 16028c2ecf20Sopenharmony_ci v4l2_set_subdevdata(sd, NULL); 16038c2ecf20Sopenharmony_ci} 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ciint s3c_camif_set_defaults(struct camif_dev *camif) 16068c2ecf20Sopenharmony_ci{ 16078c2ecf20Sopenharmony_ci unsigned int ip_rev = camif->variant->ip_revision; 16088c2ecf20Sopenharmony_ci int i; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci for (i = 0; i < CAMIF_VP_NUM; i++) { 16118c2ecf20Sopenharmony_ci struct camif_vp *vp = &camif->vp[i]; 16128c2ecf20Sopenharmony_ci struct camif_frame *f = &vp->out_frame; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci vp->camif = camif; 16158c2ecf20Sopenharmony_ci vp->id = i; 16168c2ecf20Sopenharmony_ci vp->offset = camif->variant->vp_offset; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci if (ip_rev == S3C244X_CAMIF_IP_REV) 16198c2ecf20Sopenharmony_ci vp->fmt_flags = i ? FMT_FL_S3C24XX_PREVIEW : 16208c2ecf20Sopenharmony_ci FMT_FL_S3C24XX_CODEC; 16218c2ecf20Sopenharmony_ci else 16228c2ecf20Sopenharmony_ci vp->fmt_flags = FMT_FL_S3C64XX; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci vp->out_fmt = s3c_camif_find_format(vp, NULL, 0); 16258c2ecf20Sopenharmony_ci BUG_ON(vp->out_fmt == NULL); 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci memset(f, 0, sizeof(*f)); 16288c2ecf20Sopenharmony_ci f->f_width = CAMIF_DEF_WIDTH; 16298c2ecf20Sopenharmony_ci f->f_height = CAMIF_DEF_HEIGHT; 16308c2ecf20Sopenharmony_ci f->rect.width = CAMIF_DEF_WIDTH; 16318c2ecf20Sopenharmony_ci f->rect.height = CAMIF_DEF_HEIGHT; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci /* Scaler is always enabled */ 16348c2ecf20Sopenharmony_ci vp->scaler.enable = 1; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci vp->payload = (f->f_width * f->f_height * 16378c2ecf20Sopenharmony_ci vp->out_fmt->depth) / 8; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci memset(&camif->mbus_fmt, 0, sizeof(camif->mbus_fmt)); 16418c2ecf20Sopenharmony_ci camif->mbus_fmt.width = CAMIF_DEF_WIDTH; 16428c2ecf20Sopenharmony_ci camif->mbus_fmt.height = CAMIF_DEF_HEIGHT; 16438c2ecf20Sopenharmony_ci camif->mbus_fmt.code = camif_mbus_formats[0]; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci memset(&camif->camif_crop, 0, sizeof(camif->camif_crop)); 16468c2ecf20Sopenharmony_ci camif->camif_crop.width = CAMIF_DEF_WIDTH; 16478c2ecf20Sopenharmony_ci camif->camif_crop.height = CAMIF_DEF_HEIGHT; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci return 0; 16508c2ecf20Sopenharmony_ci} 1651