162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * s3c24xx/s3c64xx SoC series Camera Interface (CAMIF) driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> 662306a36Sopenharmony_ci * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on drivers/media/platform/s5p-fimc, 962306a36Sopenharmony_ci * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. 1062306a36Sopenharmony_ci*/ 1162306a36Sopenharmony_ci#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/bug.h> 1462306a36Sopenharmony_ci#include <linux/clk.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/i2c.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/list.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/platform_device.h> 2462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2562306a36Sopenharmony_ci#include <linux/ratelimit.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/types.h> 2862306a36Sopenharmony_ci#include <linux/videodev2.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <media/media-device.h> 3162306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 3262306a36Sopenharmony_ci#include <media/v4l2-event.h> 3362306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3462306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 3562306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "camif-core.h" 3862306a36Sopenharmony_ci#include "camif-regs.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int debug; 4162306a36Sopenharmony_cimodule_param(debug, int, 0644); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Locking: called with vp->camif->slock spinlock held */ 4462306a36Sopenharmony_cistatic void camif_cfg_video_path(struct camif_vp *vp) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci WARN_ON(s3c_camif_get_scaler_config(vp, &vp->scaler)); 4762306a36Sopenharmony_ci camif_hw_set_scaler(vp); 4862306a36Sopenharmony_ci camif_hw_set_flip(vp); 4962306a36Sopenharmony_ci camif_hw_set_target_format(vp); 5062306a36Sopenharmony_ci camif_hw_set_output_dma(vp); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void camif_prepare_dma_offset(struct camif_vp *vp) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct camif_frame *f = &vp->out_frame; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci f->dma_offset.initial = f->rect.top * f->f_width + f->rect.left; 5862306a36Sopenharmony_ci f->dma_offset.line = f->f_width - (f->rect.left + f->rect.width); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci pr_debug("dma_offset: initial: %d, line: %d\n", 6162306a36Sopenharmony_ci f->dma_offset.initial, f->dma_offset.line); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Locking: called with camif->slock spinlock held */ 6562306a36Sopenharmony_cistatic int s3c_camif_hw_init(struct camif_dev *camif, struct camif_vp *vp) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci const struct s3c_camif_variant *variant = camif->variant; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (camif->sensor.sd == NULL || vp->out_fmt == NULL) 7062306a36Sopenharmony_ci return -EINVAL; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (variant->ip_revision == S3C244X_CAMIF_IP_REV) 7362306a36Sopenharmony_ci camif_hw_clear_fifo_overflow(vp); 7462306a36Sopenharmony_ci camif_hw_set_camera_bus(camif); 7562306a36Sopenharmony_ci camif_hw_set_source_format(camif); 7662306a36Sopenharmony_ci camif_hw_set_camera_crop(camif); 7762306a36Sopenharmony_ci camif_hw_set_test_pattern(camif, camif->test_pattern); 7862306a36Sopenharmony_ci if (variant->has_img_effect) 7962306a36Sopenharmony_ci camif_hw_set_effect(camif, camif->colorfx, 8062306a36Sopenharmony_ci camif->colorfx_cr, camif->colorfx_cb); 8162306a36Sopenharmony_ci if (variant->ip_revision == S3C6410_CAMIF_IP_REV) 8262306a36Sopenharmony_ci camif_hw_set_input_path(vp); 8362306a36Sopenharmony_ci camif_cfg_video_path(vp); 8462306a36Sopenharmony_ci vp->state &= ~ST_VP_CONFIG; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * Initialize the video path, only up from the scaler stage. The camera 9162306a36Sopenharmony_ci * input interface set up is skipped. This is useful to enable one of the 9262306a36Sopenharmony_ci * video paths when the other is already running. 9362306a36Sopenharmony_ci * Locking: called with camif->slock spinlock held. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cistatic int s3c_camif_hw_vp_init(struct camif_dev *camif, struct camif_vp *vp) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci unsigned int ip_rev = camif->variant->ip_revision; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (vp->out_fmt == NULL) 10062306a36Sopenharmony_ci return -EINVAL; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci camif_prepare_dma_offset(vp); 10362306a36Sopenharmony_ci if (ip_rev == S3C244X_CAMIF_IP_REV) 10462306a36Sopenharmony_ci camif_hw_clear_fifo_overflow(vp); 10562306a36Sopenharmony_ci camif_cfg_video_path(vp); 10662306a36Sopenharmony_ci vp->state &= ~ST_VP_CONFIG; 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int sensor_set_power(struct camif_dev *camif, int on) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct cam_sensor *sensor = &camif->sensor; 11362306a36Sopenharmony_ci int err = 0; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (camif->sensor.power_count == !on) 11662306a36Sopenharmony_ci err = v4l2_subdev_call(sensor->sd, core, s_power, on); 11762306a36Sopenharmony_ci if (err == -ENOIOCTLCMD) 11862306a36Sopenharmony_ci err = 0; 11962306a36Sopenharmony_ci if (!err) 12062306a36Sopenharmony_ci sensor->power_count += on ? 1 : -1; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci pr_debug("on: %d, power_count: %d, err: %d\n", 12362306a36Sopenharmony_ci on, sensor->power_count, err); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return err; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int sensor_set_streaming(struct camif_dev *camif, int on) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct cam_sensor *sensor = &camif->sensor; 13162306a36Sopenharmony_ci int err = 0; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (camif->sensor.stream_count == !on) 13462306a36Sopenharmony_ci err = v4l2_subdev_call(sensor->sd, video, s_stream, on); 13562306a36Sopenharmony_ci if (!err) 13662306a36Sopenharmony_ci sensor->stream_count += on ? 1 : -1; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci pr_debug("on: %d, stream_count: %d, err: %d\n", 13962306a36Sopenharmony_ci on, sensor->stream_count, err); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return err; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* 14562306a36Sopenharmony_ci * Reinitialize the driver so it is ready to start streaming again. 14662306a36Sopenharmony_ci * Return any buffers to vb2, perform CAMIF software reset and 14762306a36Sopenharmony_ci * turn off streaming at the data pipeline (sensor) if required. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistatic int camif_reinitialize(struct camif_vp *vp) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 15262306a36Sopenharmony_ci struct camif_buffer *buf; 15362306a36Sopenharmony_ci unsigned long flags; 15462306a36Sopenharmony_ci bool streaming; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 15762306a36Sopenharmony_ci streaming = vp->state & ST_VP_SENSOR_STREAMING; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci vp->state &= ~(ST_VP_PENDING | ST_VP_RUNNING | ST_VP_OFF | 16062306a36Sopenharmony_ci ST_VP_ABORTING | ST_VP_STREAMING | 16162306a36Sopenharmony_ci ST_VP_SENSOR_STREAMING | ST_VP_LASTIRQ); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Release unused buffers */ 16462306a36Sopenharmony_ci while (!list_empty(&vp->pending_buf_q)) { 16562306a36Sopenharmony_ci buf = camif_pending_queue_pop(vp); 16662306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci while (!list_empty(&vp->active_buf_q)) { 17062306a36Sopenharmony_ci buf = camif_active_queue_pop(vp); 17162306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!streaming) 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return sensor_set_streaming(camif, 0); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic bool s3c_vp_active(struct camif_vp *vp) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 18562306a36Sopenharmony_ci unsigned long flags; 18662306a36Sopenharmony_ci bool ret; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 18962306a36Sopenharmony_ci ret = (vp->state & ST_VP_RUNNING) || (vp->state & ST_VP_PENDING); 19062306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return ret; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic bool camif_is_streaming(struct camif_dev *camif) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci unsigned long flags; 19862306a36Sopenharmony_ci bool status; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 20162306a36Sopenharmony_ci status = camif->stream_count > 0; 20262306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return status; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int camif_stop_capture(struct camif_vp *vp) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 21062306a36Sopenharmony_ci unsigned long flags; 21162306a36Sopenharmony_ci int ret; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (!s3c_vp_active(vp)) 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 21762306a36Sopenharmony_ci vp->state &= ~(ST_VP_OFF | ST_VP_LASTIRQ); 21862306a36Sopenharmony_ci vp->state |= ST_VP_ABORTING; 21962306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ret = wait_event_timeout(vp->irq_queue, 22262306a36Sopenharmony_ci !(vp->state & ST_VP_ABORTING), 22362306a36Sopenharmony_ci msecs_to_jiffies(CAMIF_STOP_TIMEOUT)); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (ret == 0 && !(vp->state & ST_VP_OFF)) { 22862306a36Sopenharmony_ci /* Timed out, forcibly stop capture */ 22962306a36Sopenharmony_ci vp->state &= ~(ST_VP_OFF | ST_VP_ABORTING | 23062306a36Sopenharmony_ci ST_VP_LASTIRQ); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci camif_hw_disable_capture(vp); 23362306a36Sopenharmony_ci camif_hw_enable_scaler(vp, false); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return camif_reinitialize(vp); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int camif_prepare_addr(struct camif_vp *vp, struct vb2_buffer *vb, 24262306a36Sopenharmony_ci struct camif_addr *paddr) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct camif_frame *frame = &vp->out_frame; 24562306a36Sopenharmony_ci u32 pix_size; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (vb == NULL || frame == NULL) 24862306a36Sopenharmony_ci return -EINVAL; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci pix_size = frame->rect.width * frame->rect.height; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci pr_debug("colplanes: %d, pix_size: %u\n", 25362306a36Sopenharmony_ci vp->out_fmt->colplanes, pix_size); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci switch (vp->out_fmt->colplanes) { 25862306a36Sopenharmony_ci case 1: 25962306a36Sopenharmony_ci paddr->cb = 0; 26062306a36Sopenharmony_ci paddr->cr = 0; 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case 2: 26362306a36Sopenharmony_ci /* decompose Y into Y/Cb */ 26462306a36Sopenharmony_ci paddr->cb = (u32)(paddr->y + pix_size); 26562306a36Sopenharmony_ci paddr->cr = 0; 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci case 3: 26862306a36Sopenharmony_ci paddr->cb = (u32)(paddr->y + pix_size); 26962306a36Sopenharmony_ci /* decompose Y into Y/Cb/Cr */ 27062306a36Sopenharmony_ci if (vp->out_fmt->color == IMG_FMT_YCBCR422P) 27162306a36Sopenharmony_ci paddr->cr = (u32)(paddr->cb + (pix_size >> 1)); 27262306a36Sopenharmony_ci else /* 420 */ 27362306a36Sopenharmony_ci paddr->cr = (u32)(paddr->cb + (pix_size >> 2)); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (vp->out_fmt->color == IMG_FMT_YCRCB420) 27662306a36Sopenharmony_ci swap(paddr->cb, paddr->cr); 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci default: 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci pr_debug("DMA address: y: %pad cb: %pad cr: %pad\n", 28362306a36Sopenharmony_ci &paddr->y, &paddr->cb, &paddr->cr); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciirqreturn_t s3c_camif_irq_handler(int irq, void *priv) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct camif_vp *vp = priv; 29162306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 29262306a36Sopenharmony_ci unsigned int ip_rev = camif->variant->ip_revision; 29362306a36Sopenharmony_ci unsigned int status; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci spin_lock(&camif->slock); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (ip_rev == S3C6410_CAMIF_IP_REV) 29862306a36Sopenharmony_ci camif_hw_clear_pending_irq(vp); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci status = camif_hw_get_status(vp); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (ip_rev == S3C244X_CAMIF_IP_REV && (status & CISTATUS_OVF_MASK)) { 30362306a36Sopenharmony_ci camif_hw_clear_fifo_overflow(vp); 30462306a36Sopenharmony_ci goto unlock; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (vp->state & ST_VP_ABORTING) { 30862306a36Sopenharmony_ci if (vp->state & ST_VP_OFF) { 30962306a36Sopenharmony_ci /* Last IRQ */ 31062306a36Sopenharmony_ci vp->state &= ~(ST_VP_OFF | ST_VP_ABORTING | 31162306a36Sopenharmony_ci ST_VP_LASTIRQ); 31262306a36Sopenharmony_ci wake_up(&vp->irq_queue); 31362306a36Sopenharmony_ci goto unlock; 31462306a36Sopenharmony_ci } else if (vp->state & ST_VP_LASTIRQ) { 31562306a36Sopenharmony_ci camif_hw_disable_capture(vp); 31662306a36Sopenharmony_ci camif_hw_enable_scaler(vp, false); 31762306a36Sopenharmony_ci camif_hw_set_lastirq(vp, false); 31862306a36Sopenharmony_ci vp->state |= ST_VP_OFF; 31962306a36Sopenharmony_ci } else { 32062306a36Sopenharmony_ci /* Disable capture, enable last IRQ */ 32162306a36Sopenharmony_ci camif_hw_set_lastirq(vp, true); 32262306a36Sopenharmony_ci vp->state |= ST_VP_LASTIRQ; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (!list_empty(&vp->pending_buf_q) && (vp->state & ST_VP_RUNNING) && 32762306a36Sopenharmony_ci !list_empty(&vp->active_buf_q)) { 32862306a36Sopenharmony_ci unsigned int index; 32962306a36Sopenharmony_ci struct camif_buffer *vbuf; 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * Get previous DMA write buffer index: 33262306a36Sopenharmony_ci * 0 => DMA buffer 0, 2; 33362306a36Sopenharmony_ci * 1 => DMA buffer 1, 3. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci index = (CISTATUS_FRAMECNT(status) + 2) & 1; 33662306a36Sopenharmony_ci vbuf = camif_active_queue_peek(vp, index); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!WARN_ON(vbuf == NULL)) { 33962306a36Sopenharmony_ci /* Dequeue a filled buffer */ 34062306a36Sopenharmony_ci vbuf->vb.vb2_buf.timestamp = ktime_get_ns(); 34162306a36Sopenharmony_ci vbuf->vb.sequence = vp->frame_sequence++; 34262306a36Sopenharmony_ci vb2_buffer_done(&vbuf->vb.vb2_buf, VB2_BUF_STATE_DONE); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Set up an empty buffer at the DMA engine */ 34562306a36Sopenharmony_ci vbuf = camif_pending_queue_pop(vp); 34662306a36Sopenharmony_ci vbuf->index = index; 34762306a36Sopenharmony_ci camif_hw_set_output_addr(vp, &vbuf->paddr, index); 34862306a36Sopenharmony_ci camif_hw_set_output_addr(vp, &vbuf->paddr, index + 2); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Scheduled in H/W, add to the queue */ 35162306a36Sopenharmony_ci camif_active_queue_add(vp, vbuf); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci } else if (!(vp->state & ST_VP_ABORTING) && 35462306a36Sopenharmony_ci (vp->state & ST_VP_PENDING)) { 35562306a36Sopenharmony_ci vp->state |= ST_VP_RUNNING; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (vp->state & ST_VP_CONFIG) { 35962306a36Sopenharmony_ci camif_prepare_dma_offset(vp); 36062306a36Sopenharmony_ci camif_hw_set_camera_crop(camif); 36162306a36Sopenharmony_ci camif_hw_set_scaler(vp); 36262306a36Sopenharmony_ci camif_hw_set_flip(vp); 36362306a36Sopenharmony_ci camif_hw_set_test_pattern(camif, camif->test_pattern); 36462306a36Sopenharmony_ci if (camif->variant->has_img_effect) 36562306a36Sopenharmony_ci camif_hw_set_effect(camif, camif->colorfx, 36662306a36Sopenharmony_ci camif->colorfx_cr, camif->colorfx_cb); 36762306a36Sopenharmony_ci vp->state &= ~ST_VP_CONFIG; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ciunlock: 37062306a36Sopenharmony_ci spin_unlock(&camif->slock); 37162306a36Sopenharmony_ci return IRQ_HANDLED; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct camif_vp *vp = vb2_get_drv_priv(vq); 37762306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 37862306a36Sopenharmony_ci unsigned long flags; 37962306a36Sopenharmony_ci int ret; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * We assume the codec capture path is always activated 38362306a36Sopenharmony_ci * first, before the preview path starts streaming. 38462306a36Sopenharmony_ci * This is required to avoid internal FIFO overflow and 38562306a36Sopenharmony_ci * a need for CAMIF software reset. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (camif->stream_count == 0) { 39062306a36Sopenharmony_ci camif_hw_reset(camif); 39162306a36Sopenharmony_ci ret = s3c_camif_hw_init(camif, vp); 39262306a36Sopenharmony_ci } else { 39362306a36Sopenharmony_ci ret = s3c_camif_hw_vp_init(camif, vp); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (ret < 0) { 39862306a36Sopenharmony_ci camif_reinitialize(vp); 39962306a36Sopenharmony_ci return ret; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 40362306a36Sopenharmony_ci vp->frame_sequence = 0; 40462306a36Sopenharmony_ci vp->state |= ST_VP_PENDING; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (!list_empty(&vp->pending_buf_q) && 40762306a36Sopenharmony_ci (!(vp->state & ST_VP_STREAMING) || 40862306a36Sopenharmony_ci !(vp->state & ST_VP_SENSOR_STREAMING))) { 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci camif_hw_enable_scaler(vp, vp->scaler.enable); 41162306a36Sopenharmony_ci camif_hw_enable_capture(vp); 41262306a36Sopenharmony_ci vp->state |= ST_VP_STREAMING; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (!(vp->state & ST_VP_SENSOR_STREAMING)) { 41562306a36Sopenharmony_ci vp->state |= ST_VP_SENSOR_STREAMING; 41662306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 41762306a36Sopenharmony_ci ret = sensor_set_streaming(camif, 1); 41862306a36Sopenharmony_ci if (ret) 41962306a36Sopenharmony_ci v4l2_err(&vp->vdev, "Sensor s_stream failed\n"); 42062306a36Sopenharmony_ci if (debug) 42162306a36Sopenharmony_ci camif_hw_dump_regs(camif, __func__); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return ret; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct camif_vp *vp = vb2_get_drv_priv(vq); 43462306a36Sopenharmony_ci camif_stop_capture(vp); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq, 43862306a36Sopenharmony_ci unsigned int *num_buffers, unsigned int *num_planes, 43962306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct camif_vp *vp = vb2_get_drv_priv(vq); 44262306a36Sopenharmony_ci struct camif_frame *frame = &vp->out_frame; 44362306a36Sopenharmony_ci const struct camif_fmt *fmt = vp->out_fmt; 44462306a36Sopenharmony_ci unsigned int size; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (fmt == NULL) 44762306a36Sopenharmony_ci return -EINVAL; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci size = (frame->f_width * frame->f_height * fmt->depth) / 8; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (*num_planes) 45262306a36Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci *num_planes = 1; 45562306a36Sopenharmony_ci sizes[0] = size; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci pr_debug("size: %u\n", sizes[0]); 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct camif_vp *vp = vb2_get_drv_priv(vb->vb2_queue); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (vp->out_fmt == NULL) 46662306a36Sopenharmony_ci return -EINVAL; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < vp->payload) { 46962306a36Sopenharmony_ci v4l2_err(&vp->vdev, "buffer too small: %lu, required: %u\n", 47062306a36Sopenharmony_ci vb2_plane_size(vb, 0), vp->payload); 47162306a36Sopenharmony_ci return -EINVAL; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, vp->payload); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 48162306a36Sopenharmony_ci struct camif_buffer *buf = container_of(vbuf, struct camif_buffer, vb); 48262306a36Sopenharmony_ci struct camif_vp *vp = vb2_get_drv_priv(vb->vb2_queue); 48362306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 48462306a36Sopenharmony_ci unsigned long flags; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 48762306a36Sopenharmony_ci WARN_ON(camif_prepare_addr(vp, &buf->vb.vb2_buf, &buf->paddr)); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (!(vp->state & ST_VP_STREAMING) && vp->active_buffers < 2) { 49062306a36Sopenharmony_ci /* Schedule an empty buffer in H/W */ 49162306a36Sopenharmony_ci buf->index = vp->buf_index; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci camif_hw_set_output_addr(vp, &buf->paddr, buf->index); 49462306a36Sopenharmony_ci camif_hw_set_output_addr(vp, &buf->paddr, buf->index + 2); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci camif_active_queue_add(vp, buf); 49762306a36Sopenharmony_ci vp->buf_index = !vp->buf_index; 49862306a36Sopenharmony_ci } else { 49962306a36Sopenharmony_ci camif_pending_queue_add(vp, buf); 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (vb2_is_streaming(&vp->vb_queue) && !list_empty(&vp->pending_buf_q) 50362306a36Sopenharmony_ci && !(vp->state & ST_VP_STREAMING)) { 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci vp->state |= ST_VP_STREAMING; 50662306a36Sopenharmony_ci camif_hw_enable_scaler(vp, vp->scaler.enable); 50762306a36Sopenharmony_ci camif_hw_enable_capture(vp); 50862306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (!(vp->state & ST_VP_SENSOR_STREAMING)) { 51162306a36Sopenharmony_ci if (sensor_set_streaming(camif, 1) == 0) 51262306a36Sopenharmony_ci vp->state |= ST_VP_SENSOR_STREAMING; 51362306a36Sopenharmony_ci else 51462306a36Sopenharmony_ci v4l2_err(&vp->vdev, "Sensor s_stream failed\n"); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (debug) 51762306a36Sopenharmony_ci camif_hw_dump_regs(camif, __func__); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci return; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic const struct vb2_ops s3c_camif_qops = { 52562306a36Sopenharmony_ci .queue_setup = queue_setup, 52662306a36Sopenharmony_ci .buf_prepare = buffer_prepare, 52762306a36Sopenharmony_ci .buf_queue = buffer_queue, 52862306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 52962306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 53062306a36Sopenharmony_ci .start_streaming = start_streaming, 53162306a36Sopenharmony_ci .stop_streaming = stop_streaming, 53262306a36Sopenharmony_ci}; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int s3c_camif_open(struct file *file) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 53762306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 53862306a36Sopenharmony_ci int ret; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci pr_debug("[vp%d] state: %#x, owner: %p, pid: %d\n", vp->id, 54162306a36Sopenharmony_ci vp->state, vp->owner, task_pid_nr(current)); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (mutex_lock_interruptible(&camif->lock)) 54462306a36Sopenharmony_ci return -ERESTARTSYS; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci ret = v4l2_fh_open(file); 54762306a36Sopenharmony_ci if (ret < 0) 54862306a36Sopenharmony_ci goto unlock; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(camif->dev); 55162306a36Sopenharmony_ci if (ret < 0) 55262306a36Sopenharmony_ci goto err_pm; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ret = sensor_set_power(camif, 1); 55562306a36Sopenharmony_ci if (!ret) 55662306a36Sopenharmony_ci goto unlock; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci pm_runtime_put(camif->dev); 55962306a36Sopenharmony_cierr_pm: 56062306a36Sopenharmony_ci v4l2_fh_release(file); 56162306a36Sopenharmony_ciunlock: 56262306a36Sopenharmony_ci mutex_unlock(&camif->lock); 56362306a36Sopenharmony_ci return ret; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic int s3c_camif_close(struct file *file) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 56962306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 57062306a36Sopenharmony_ci int ret; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci pr_debug("[vp%d] state: %#x, owner: %p, pid: %d\n", vp->id, 57362306a36Sopenharmony_ci vp->state, vp->owner, task_pid_nr(current)); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci mutex_lock(&camif->lock); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (vp->owner == file->private_data) { 57862306a36Sopenharmony_ci camif_stop_capture(vp); 57962306a36Sopenharmony_ci vb2_queue_release(&vp->vb_queue); 58062306a36Sopenharmony_ci vp->owner = NULL; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci sensor_set_power(camif, 0); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci pm_runtime_put(camif->dev); 58662306a36Sopenharmony_ci ret = v4l2_fh_release(file); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci mutex_unlock(&camif->lock); 58962306a36Sopenharmony_ci return ret; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic __poll_t s3c_camif_poll(struct file *file, 59362306a36Sopenharmony_ci struct poll_table_struct *wait) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 59662306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 59762306a36Sopenharmony_ci __poll_t ret; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci mutex_lock(&camif->lock); 60062306a36Sopenharmony_ci if (vp->owner && vp->owner != file->private_data) 60162306a36Sopenharmony_ci ret = EPOLLERR; 60262306a36Sopenharmony_ci else 60362306a36Sopenharmony_ci ret = vb2_poll(&vp->vb_queue, file, wait); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci mutex_unlock(&camif->lock); 60662306a36Sopenharmony_ci return ret; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int s3c_camif_mmap(struct file *file, struct vm_area_struct *vma) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 61262306a36Sopenharmony_ci int ret; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (vp->owner && vp->owner != file->private_data) 61562306a36Sopenharmony_ci ret = -EBUSY; 61662306a36Sopenharmony_ci else 61762306a36Sopenharmony_ci ret = vb2_mmap(&vp->vb_queue, vma); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return ret; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic const struct v4l2_file_operations s3c_camif_fops = { 62362306a36Sopenharmony_ci .owner = THIS_MODULE, 62462306a36Sopenharmony_ci .open = s3c_camif_open, 62562306a36Sopenharmony_ci .release = s3c_camif_close, 62662306a36Sopenharmony_ci .poll = s3c_camif_poll, 62762306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 62862306a36Sopenharmony_ci .mmap = s3c_camif_mmap, 62962306a36Sopenharmony_ci}; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci/* 63262306a36Sopenharmony_ci * Video node IOCTLs 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic int s3c_camif_vidioc_querycap(struct file *file, void *priv, 63662306a36Sopenharmony_ci struct v4l2_capability *cap) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci strscpy(cap->driver, S3C_CAMIF_DRIVER_NAME, sizeof(cap->driver)); 64162306a36Sopenharmony_ci strscpy(cap->card, S3C_CAMIF_DRIVER_NAME, sizeof(cap->card)); 64262306a36Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s.%d", 64362306a36Sopenharmony_ci dev_name(vp->camif->dev), vp->id); 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int s3c_camif_vidioc_enum_input(struct file *file, void *priv, 64862306a36Sopenharmony_ci struct v4l2_input *input) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 65162306a36Sopenharmony_ci struct v4l2_subdev *sensor = vp->camif->sensor.sd; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (input->index || sensor == NULL) 65462306a36Sopenharmony_ci return -EINVAL; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci input->type = V4L2_INPUT_TYPE_CAMERA; 65762306a36Sopenharmony_ci strscpy(input->name, sensor->name, sizeof(input->name)); 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int s3c_camif_vidioc_s_input(struct file *file, void *priv, 66262306a36Sopenharmony_ci unsigned int i) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci return i == 0 ? 0 : -EINVAL; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic int s3c_camif_vidioc_g_input(struct file *file, void *priv, 66862306a36Sopenharmony_ci unsigned int *i) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci *i = 0; 67162306a36Sopenharmony_ci return 0; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic int s3c_camif_vidioc_enum_fmt(struct file *file, void *priv, 67562306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 67862306a36Sopenharmony_ci const struct camif_fmt *fmt; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci fmt = s3c_camif_find_format(vp, NULL, f->index); 68162306a36Sopenharmony_ci if (!fmt) 68262306a36Sopenharmony_ci return -EINVAL; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci f->pixelformat = fmt->fourcc; 68562306a36Sopenharmony_ci return 0; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int s3c_camif_vidioc_g_fmt(struct file *file, void *priv, 68962306a36Sopenharmony_ci struct v4l2_format *f) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 69262306a36Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 69362306a36Sopenharmony_ci struct camif_frame *frame = &vp->out_frame; 69462306a36Sopenharmony_ci const struct camif_fmt *fmt = vp->out_fmt; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci pix->bytesperline = frame->f_width * fmt->ybpp; 69762306a36Sopenharmony_ci pix->sizeimage = vp->payload; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci pix->pixelformat = fmt->fourcc; 70062306a36Sopenharmony_ci pix->width = frame->f_width; 70162306a36Sopenharmony_ci pix->height = frame->f_height; 70262306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 70362306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_JPEG; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci return 0; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic int __camif_video_try_format(struct camif_vp *vp, 70962306a36Sopenharmony_ci struct v4l2_pix_format *pix, 71062306a36Sopenharmony_ci const struct camif_fmt **ffmt) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 71362306a36Sopenharmony_ci struct v4l2_rect *crop = &camif->camif_crop; 71462306a36Sopenharmony_ci unsigned int wmin, hmin, sc_hrmax, sc_vrmax; 71562306a36Sopenharmony_ci const struct vp_pix_limits *pix_lim; 71662306a36Sopenharmony_ci const struct camif_fmt *fmt; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci fmt = s3c_camif_find_format(vp, &pix->pixelformat, 0); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (WARN_ON(fmt == NULL)) 72162306a36Sopenharmony_ci return -EINVAL; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (ffmt) 72462306a36Sopenharmony_ci *ffmt = fmt; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci pix_lim = &camif->variant->vp_pix_limits[vp->id]; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci pr_debug("fmt: %ux%u, crop: %ux%u, bytesperline: %u\n", 72962306a36Sopenharmony_ci pix->width, pix->height, crop->width, crop->height, 73062306a36Sopenharmony_ci pix->bytesperline); 73162306a36Sopenharmony_ci /* 73262306a36Sopenharmony_ci * Calculate minimum width and height according to the configured 73362306a36Sopenharmony_ci * camera input interface crop rectangle and the resizer's capabilities. 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_ci sc_hrmax = min(SCALER_MAX_RATIO, 1 << (ffs(crop->width) - 3)); 73662306a36Sopenharmony_ci sc_vrmax = min(SCALER_MAX_RATIO, 1 << (ffs(crop->height) - 1)); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci wmin = max_t(u32, pix_lim->min_out_width, crop->width / sc_hrmax); 73962306a36Sopenharmony_ci wmin = round_up(wmin, pix_lim->out_width_align); 74062306a36Sopenharmony_ci hmin = max_t(u32, 8, crop->height / sc_vrmax); 74162306a36Sopenharmony_ci hmin = round_up(hmin, 8); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci v4l_bound_align_image(&pix->width, wmin, pix_lim->max_sc_out_width, 74462306a36Sopenharmony_ci ffs(pix_lim->out_width_align) - 1, 74562306a36Sopenharmony_ci &pix->height, hmin, pix_lim->max_height, 0, 0); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci pix->bytesperline = pix->width * fmt->ybpp; 74862306a36Sopenharmony_ci pix->sizeimage = (pix->width * pix->height * fmt->depth) / 8; 74962306a36Sopenharmony_ci pix->pixelformat = fmt->fourcc; 75062306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_JPEG; 75162306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci pr_debug("%ux%u, wmin: %d, hmin: %d, sc_hrmax: %d, sc_vrmax: %d\n", 75462306a36Sopenharmony_ci pix->width, pix->height, wmin, hmin, sc_hrmax, sc_vrmax); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci return 0; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic int s3c_camif_vidioc_try_fmt(struct file *file, void *priv, 76062306a36Sopenharmony_ci struct v4l2_format *f) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 76362306a36Sopenharmony_ci return __camif_video_try_format(vp, &f->fmt.pix, NULL); 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic int s3c_camif_vidioc_s_fmt(struct file *file, void *priv, 76762306a36Sopenharmony_ci struct v4l2_format *f) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 77062306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 77162306a36Sopenharmony_ci struct camif_frame *out_frame = &vp->out_frame; 77262306a36Sopenharmony_ci const struct camif_fmt *fmt = NULL; 77362306a36Sopenharmony_ci int ret; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci pr_debug("[vp%d]\n", vp->id); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (vb2_is_busy(&vp->vb_queue)) 77862306a36Sopenharmony_ci return -EBUSY; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci ret = __camif_video_try_format(vp, &f->fmt.pix, &fmt); 78162306a36Sopenharmony_ci if (ret < 0) 78262306a36Sopenharmony_ci return ret; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci vp->out_fmt = fmt; 78562306a36Sopenharmony_ci vp->payload = pix->sizeimage; 78662306a36Sopenharmony_ci out_frame->f_width = pix->width; 78762306a36Sopenharmony_ci out_frame->f_height = pix->height; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* Reset composition rectangle */ 79062306a36Sopenharmony_ci out_frame->rect.width = pix->width; 79162306a36Sopenharmony_ci out_frame->rect.height = pix->height; 79262306a36Sopenharmony_ci out_frame->rect.left = 0; 79362306a36Sopenharmony_ci out_frame->rect.top = 0; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (vp->owner == NULL) 79662306a36Sopenharmony_ci vp->owner = priv; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci pr_debug("%ux%u. payload: %u. fmt: 0x%08x. %d %d. sizeimage: %d. bpl: %d\n", 79962306a36Sopenharmony_ci out_frame->f_width, out_frame->f_height, vp->payload, 80062306a36Sopenharmony_ci fmt->fourcc, pix->width * pix->height * fmt->depth, 80162306a36Sopenharmony_ci fmt->depth, pix->sizeimage, pix->bytesperline); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci return 0; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci/* Only check pixel formats at the sensor and the camif subdev pads */ 80762306a36Sopenharmony_cistatic int camif_pipeline_validate(struct camif_dev *camif) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct v4l2_subdev_format src_fmt = { 81062306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 81162306a36Sopenharmony_ci }; 81262306a36Sopenharmony_ci struct media_pad *pad; 81362306a36Sopenharmony_ci int ret; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* Retrieve format at the sensor subdev source pad */ 81662306a36Sopenharmony_ci pad = media_pad_remote_pad_first(&camif->pads[0]); 81762306a36Sopenharmony_ci if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 81862306a36Sopenharmony_ci return -EPIPE; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci src_fmt.pad = pad->index; 82162306a36Sopenharmony_ci ret = v4l2_subdev_call(camif->sensor.sd, pad, get_fmt, NULL, &src_fmt); 82262306a36Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 82362306a36Sopenharmony_ci return -EPIPE; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (src_fmt.format.width != camif->mbus_fmt.width || 82662306a36Sopenharmony_ci src_fmt.format.height != camif->mbus_fmt.height || 82762306a36Sopenharmony_ci src_fmt.format.code != camif->mbus_fmt.code) 82862306a36Sopenharmony_ci return -EPIPE; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return 0; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic int s3c_camif_streamon(struct file *file, void *priv, 83462306a36Sopenharmony_ci enum v4l2_buf_type type) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 83762306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 83862306a36Sopenharmony_ci struct media_entity *sensor = &camif->sensor.sd->entity; 83962306a36Sopenharmony_ci int ret; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci pr_debug("[vp%d]\n", vp->id); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 84462306a36Sopenharmony_ci return -EINVAL; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (vp->owner && vp->owner != priv) 84762306a36Sopenharmony_ci return -EBUSY; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (s3c_vp_active(vp)) 85062306a36Sopenharmony_ci return 0; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci ret = media_pipeline_start(sensor->pads, camif->m_pipeline); 85362306a36Sopenharmony_ci if (ret < 0) 85462306a36Sopenharmony_ci return ret; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci ret = camif_pipeline_validate(camif); 85762306a36Sopenharmony_ci if (ret < 0) { 85862306a36Sopenharmony_ci media_pipeline_stop(sensor->pads); 85962306a36Sopenharmony_ci return ret; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci return vb2_streamon(&vp->vb_queue, type); 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic int s3c_camif_streamoff(struct file *file, void *priv, 86662306a36Sopenharmony_ci enum v4l2_buf_type type) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 86962306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 87062306a36Sopenharmony_ci int ret; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci pr_debug("[vp%d]\n", vp->id); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 87562306a36Sopenharmony_ci return -EINVAL; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (vp->owner && vp->owner != priv) 87862306a36Sopenharmony_ci return -EBUSY; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci ret = vb2_streamoff(&vp->vb_queue, type); 88162306a36Sopenharmony_ci if (ret == 0) 88262306a36Sopenharmony_ci media_pipeline_stop(camif->sensor.sd->entity.pads); 88362306a36Sopenharmony_ci return ret; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic int s3c_camif_reqbufs(struct file *file, void *priv, 88762306a36Sopenharmony_ci struct v4l2_requestbuffers *rb) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 89062306a36Sopenharmony_ci int ret; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci pr_debug("[vp%d] rb count: %d, owner: %p, priv: %p\n", 89362306a36Sopenharmony_ci vp->id, rb->count, vp->owner, priv); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (vp->owner && vp->owner != priv) 89662306a36Sopenharmony_ci return -EBUSY; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (rb->count) 89962306a36Sopenharmony_ci rb->count = max_t(u32, CAMIF_REQ_BUFS_MIN, rb->count); 90062306a36Sopenharmony_ci else 90162306a36Sopenharmony_ci vp->owner = NULL; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci ret = vb2_reqbufs(&vp->vb_queue, rb); 90462306a36Sopenharmony_ci if (ret < 0) 90562306a36Sopenharmony_ci return ret; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (rb->count && rb->count < CAMIF_REQ_BUFS_MIN) { 90862306a36Sopenharmony_ci rb->count = 0; 90962306a36Sopenharmony_ci vb2_reqbufs(&vp->vb_queue, rb); 91062306a36Sopenharmony_ci ret = -ENOMEM; 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci vp->reqbufs_count = rb->count; 91462306a36Sopenharmony_ci if (vp->owner == NULL && rb->count > 0) 91562306a36Sopenharmony_ci vp->owner = priv; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci return ret; 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic int s3c_camif_querybuf(struct file *file, void *priv, 92162306a36Sopenharmony_ci struct v4l2_buffer *buf) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 92462306a36Sopenharmony_ci return vb2_querybuf(&vp->vb_queue, buf); 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_cistatic int s3c_camif_qbuf(struct file *file, void *priv, 92862306a36Sopenharmony_ci struct v4l2_buffer *buf) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci pr_debug("[vp%d]\n", vp->id); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (vp->owner && vp->owner != priv) 93562306a36Sopenharmony_ci return -EBUSY; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci return vb2_qbuf(&vp->vb_queue, vp->vdev.v4l2_dev->mdev, buf); 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic int s3c_camif_dqbuf(struct file *file, void *priv, 94162306a36Sopenharmony_ci struct v4l2_buffer *buf) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci pr_debug("[vp%d] sequence: %d\n", vp->id, vp->frame_sequence); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (vp->owner && vp->owner != priv) 94862306a36Sopenharmony_ci return -EBUSY; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci return vb2_dqbuf(&vp->vb_queue, buf, file->f_flags & O_NONBLOCK); 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic int s3c_camif_create_bufs(struct file *file, void *priv, 95462306a36Sopenharmony_ci struct v4l2_create_buffers *create) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 95762306a36Sopenharmony_ci int ret; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci if (vp->owner && vp->owner != priv) 96062306a36Sopenharmony_ci return -EBUSY; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci create->count = max_t(u32, 1, create->count); 96362306a36Sopenharmony_ci ret = vb2_create_bufs(&vp->vb_queue, create); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (!ret && vp->owner == NULL) 96662306a36Sopenharmony_ci vp->owner = priv; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci return ret; 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic int s3c_camif_prepare_buf(struct file *file, void *priv, 97262306a36Sopenharmony_ci struct v4l2_buffer *b) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 97562306a36Sopenharmony_ci return vb2_prepare_buf(&vp->vb_queue, vp->vdev.v4l2_dev->mdev, b); 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic int s3c_camif_g_selection(struct file *file, void *priv, 97962306a36Sopenharmony_ci struct v4l2_selection *sel) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 98462306a36Sopenharmony_ci return -EINVAL; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci switch (sel->target) { 98762306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 98862306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 98962306a36Sopenharmony_ci sel->r.left = 0; 99062306a36Sopenharmony_ci sel->r.top = 0; 99162306a36Sopenharmony_ci sel->r.width = vp->out_frame.f_width; 99262306a36Sopenharmony_ci sel->r.height = vp->out_frame.f_height; 99362306a36Sopenharmony_ci return 0; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 99662306a36Sopenharmony_ci sel->r = vp->out_frame.rect; 99762306a36Sopenharmony_ci return 0; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci return -EINVAL; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic void __camif_try_compose(struct camif_dev *camif, struct camif_vp *vp, 100462306a36Sopenharmony_ci struct v4l2_rect *r) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci /* s3c244x doesn't support composition */ 100762306a36Sopenharmony_ci if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV) { 100862306a36Sopenharmony_ci *r = vp->out_frame.rect; 100962306a36Sopenharmony_ci return; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci /* TODO: s3c64xx */ 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic int s3c_camif_s_selection(struct file *file, void *priv, 101662306a36Sopenharmony_ci struct v4l2_selection *sel) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci struct camif_vp *vp = video_drvdata(file); 101962306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 102062306a36Sopenharmony_ci struct v4l2_rect rect = sel->r; 102162306a36Sopenharmony_ci unsigned long flags; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || 102462306a36Sopenharmony_ci sel->target != V4L2_SEL_TGT_COMPOSE) 102562306a36Sopenharmony_ci return -EINVAL; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci __camif_try_compose(camif, vp, &rect); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci sel->r = rect; 103062306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 103162306a36Sopenharmony_ci vp->out_frame.rect = rect; 103262306a36Sopenharmony_ci vp->state |= ST_VP_CONFIG; 103362306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci pr_debug("type: %#x, target: %#x, flags: %#x, (%d,%d)/%dx%d\n", 103662306a36Sopenharmony_ci sel->type, sel->target, sel->flags, 103762306a36Sopenharmony_ci sel->r.left, sel->r.top, sel->r.width, sel->r.height); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci return 0; 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops s3c_camif_ioctl_ops = { 104362306a36Sopenharmony_ci .vidioc_querycap = s3c_camif_vidioc_querycap, 104462306a36Sopenharmony_ci .vidioc_enum_input = s3c_camif_vidioc_enum_input, 104562306a36Sopenharmony_ci .vidioc_g_input = s3c_camif_vidioc_g_input, 104662306a36Sopenharmony_ci .vidioc_s_input = s3c_camif_vidioc_s_input, 104762306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = s3c_camif_vidioc_enum_fmt, 104862306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = s3c_camif_vidioc_try_fmt, 104962306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = s3c_camif_vidioc_s_fmt, 105062306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = s3c_camif_vidioc_g_fmt, 105162306a36Sopenharmony_ci .vidioc_g_selection = s3c_camif_g_selection, 105262306a36Sopenharmony_ci .vidioc_s_selection = s3c_camif_s_selection, 105362306a36Sopenharmony_ci .vidioc_reqbufs = s3c_camif_reqbufs, 105462306a36Sopenharmony_ci .vidioc_querybuf = s3c_camif_querybuf, 105562306a36Sopenharmony_ci .vidioc_prepare_buf = s3c_camif_prepare_buf, 105662306a36Sopenharmony_ci .vidioc_create_bufs = s3c_camif_create_bufs, 105762306a36Sopenharmony_ci .vidioc_qbuf = s3c_camif_qbuf, 105862306a36Sopenharmony_ci .vidioc_dqbuf = s3c_camif_dqbuf, 105962306a36Sopenharmony_ci .vidioc_streamon = s3c_camif_streamon, 106062306a36Sopenharmony_ci .vidioc_streamoff = s3c_camif_streamoff, 106162306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 106262306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 106362306a36Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 106462306a36Sopenharmony_ci}; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci/* 106762306a36Sopenharmony_ci * Video node controls 106862306a36Sopenharmony_ci */ 106962306a36Sopenharmony_cistatic int s3c_camif_video_s_ctrl(struct v4l2_ctrl *ctrl) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci struct camif_vp *vp = ctrl->priv; 107262306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 107362306a36Sopenharmony_ci unsigned long flags; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci pr_debug("[vp%d] ctrl: %s, value: %d\n", vp->id, 107662306a36Sopenharmony_ci ctrl->name, ctrl->val); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci switch (ctrl->id) { 108162306a36Sopenharmony_ci case V4L2_CID_HFLIP: 108262306a36Sopenharmony_ci vp->hflip = ctrl->val; 108362306a36Sopenharmony_ci break; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci case V4L2_CID_VFLIP: 108662306a36Sopenharmony_ci vp->vflip = ctrl->val; 108762306a36Sopenharmony_ci break; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci vp->state |= ST_VP_CONFIG; 109162306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 109262306a36Sopenharmony_ci return 0; 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci/* Codec and preview video node control ops */ 109662306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops s3c_camif_video_ctrl_ops = { 109762306a36Sopenharmony_ci .s_ctrl = s3c_camif_video_s_ctrl, 109862306a36Sopenharmony_ci}; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ciint s3c_camif_register_video_node(struct camif_dev *camif, int idx) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci struct camif_vp *vp = &camif->vp[idx]; 110362306a36Sopenharmony_ci struct vb2_queue *q = &vp->vb_queue; 110462306a36Sopenharmony_ci struct video_device *vfd = &vp->vdev; 110562306a36Sopenharmony_ci struct v4l2_ctrl *ctrl; 110662306a36Sopenharmony_ci int ret; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci memset(vfd, 0, sizeof(*vfd)); 110962306a36Sopenharmony_ci snprintf(vfd->name, sizeof(vfd->name), "camif-%s", 111062306a36Sopenharmony_ci vp->id == 0 ? "codec" : "preview"); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci vfd->fops = &s3c_camif_fops; 111362306a36Sopenharmony_ci vfd->ioctl_ops = &s3c_camif_ioctl_ops; 111462306a36Sopenharmony_ci vfd->v4l2_dev = &camif->v4l2_dev; 111562306a36Sopenharmony_ci vfd->minor = -1; 111662306a36Sopenharmony_ci vfd->release = video_device_release_empty; 111762306a36Sopenharmony_ci vfd->lock = &camif->lock; 111862306a36Sopenharmony_ci vp->reqbufs_count = 0; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci INIT_LIST_HEAD(&vp->pending_buf_q); 112162306a36Sopenharmony_ci INIT_LIST_HEAD(&vp->active_buf_q); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci memset(q, 0, sizeof(*q)); 112462306a36Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 112562306a36Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_USERPTR; 112662306a36Sopenharmony_ci q->ops = &s3c_camif_qops; 112762306a36Sopenharmony_ci q->mem_ops = &vb2_dma_contig_memops; 112862306a36Sopenharmony_ci q->buf_struct_size = sizeof(struct camif_buffer); 112962306a36Sopenharmony_ci q->drv_priv = vp; 113062306a36Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 113162306a36Sopenharmony_ci q->lock = &vp->camif->lock; 113262306a36Sopenharmony_ci q->dev = camif->v4l2_dev.dev; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci ret = vb2_queue_init(q); 113562306a36Sopenharmony_ci if (ret) 113662306a36Sopenharmony_ci return ret; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci vp->pad.flags = MEDIA_PAD_FL_SINK; 113962306a36Sopenharmony_ci ret = media_entity_pads_init(&vfd->entity, 1, &vp->pad); 114062306a36Sopenharmony_ci if (ret) 114162306a36Sopenharmony_ci return ret; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci video_set_drvdata(vfd, vp); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci v4l2_ctrl_handler_init(&vp->ctrl_handler, 1); 114662306a36Sopenharmony_ci ctrl = v4l2_ctrl_new_std(&vp->ctrl_handler, &s3c_camif_video_ctrl_ops, 114762306a36Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 114862306a36Sopenharmony_ci if (ctrl) 114962306a36Sopenharmony_ci ctrl->priv = vp; 115062306a36Sopenharmony_ci ctrl = v4l2_ctrl_new_std(&vp->ctrl_handler, &s3c_camif_video_ctrl_ops, 115162306a36Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 115262306a36Sopenharmony_ci if (ctrl) 115362306a36Sopenharmony_ci ctrl->priv = vp; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci ret = vp->ctrl_handler.error; 115662306a36Sopenharmony_ci if (ret < 0) 115762306a36Sopenharmony_ci goto err_me_cleanup; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci vfd->ctrl_handler = &vp->ctrl_handler; 116062306a36Sopenharmony_ci vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); 116362306a36Sopenharmony_ci if (ret) 116462306a36Sopenharmony_ci goto err_ctrlh_free; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci v4l2_info(&camif->v4l2_dev, "registered %s as /dev/%s\n", 116762306a36Sopenharmony_ci vfd->name, video_device_node_name(vfd)); 116862306a36Sopenharmony_ci return 0; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cierr_ctrlh_free: 117162306a36Sopenharmony_ci v4l2_ctrl_handler_free(&vp->ctrl_handler); 117262306a36Sopenharmony_cierr_me_cleanup: 117362306a36Sopenharmony_ci media_entity_cleanup(&vfd->entity); 117462306a36Sopenharmony_ci return ret; 117562306a36Sopenharmony_ci} 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_civoid s3c_camif_unregister_video_node(struct camif_dev *camif, int idx) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci struct video_device *vfd = &camif->vp[idx].vdev; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (video_is_registered(vfd)) { 118262306a36Sopenharmony_ci video_unregister_device(vfd); 118362306a36Sopenharmony_ci media_entity_cleanup(&vfd->entity); 118462306a36Sopenharmony_ci v4l2_ctrl_handler_free(vfd->ctrl_handler); 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci/* Media bus pixel formats supported at the camif input */ 118962306a36Sopenharmony_cistatic const u32 camif_mbus_formats[] = { 119062306a36Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 119162306a36Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 119262306a36Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 119362306a36Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 119462306a36Sopenharmony_ci}; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci/* 119762306a36Sopenharmony_ci * Camera input interface subdev operations 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic int s3c_camif_subdev_enum_mbus_code(struct v4l2_subdev *sd, 120162306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 120262306a36Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci if (code->index >= ARRAY_SIZE(camif_mbus_formats)) 120562306a36Sopenharmony_ci return -EINVAL; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci code->code = camif_mbus_formats[code->index]; 120862306a36Sopenharmony_ci return 0; 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd, 121262306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 121362306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct camif_dev *camif = v4l2_get_subdevdata(sd); 121662306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &fmt->format; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 121962306a36Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); 122062306a36Sopenharmony_ci fmt->format = *mf; 122162306a36Sopenharmony_ci return 0; 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci mutex_lock(&camif->lock); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci switch (fmt->pad) { 122762306a36Sopenharmony_ci case CAMIF_SD_PAD_SINK: 122862306a36Sopenharmony_ci /* full camera input pixel size */ 122962306a36Sopenharmony_ci *mf = camif->mbus_fmt; 123062306a36Sopenharmony_ci break; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P: 123362306a36Sopenharmony_ci /* crop rectangle at camera interface input */ 123462306a36Sopenharmony_ci mf->width = camif->camif_crop.width; 123562306a36Sopenharmony_ci mf->height = camif->camif_crop.height; 123662306a36Sopenharmony_ci mf->code = camif->mbus_fmt.code; 123762306a36Sopenharmony_ci break; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci mutex_unlock(&camif->lock); 124162306a36Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 124262306a36Sopenharmony_ci mf->colorspace = V4L2_COLORSPACE_JPEG; 124362306a36Sopenharmony_ci return 0; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_cistatic void __camif_subdev_try_format(struct camif_dev *camif, 124762306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf, int pad) 124862306a36Sopenharmony_ci{ 124962306a36Sopenharmony_ci const struct s3c_camif_variant *variant = camif->variant; 125062306a36Sopenharmony_ci const struct vp_pix_limits *pix_lim; 125162306a36Sopenharmony_ci unsigned int i; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* FIXME: constraints against codec or preview path ? */ 125462306a36Sopenharmony_ci pix_lim = &variant->vp_pix_limits[VP_CODEC]; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(camif_mbus_formats); i++) 125762306a36Sopenharmony_ci if (camif_mbus_formats[i] == mf->code) 125862306a36Sopenharmony_ci break; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci if (i == ARRAY_SIZE(camif_mbus_formats)) 126162306a36Sopenharmony_ci mf->code = camif_mbus_formats[0]; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (pad == CAMIF_SD_PAD_SINK) { 126462306a36Sopenharmony_ci v4l_bound_align_image(&mf->width, 8, CAMIF_MAX_PIX_WIDTH, 126562306a36Sopenharmony_ci ffs(pix_lim->out_width_align) - 1, 126662306a36Sopenharmony_ci &mf->height, 8, CAMIF_MAX_PIX_HEIGHT, 0, 126762306a36Sopenharmony_ci 0); 126862306a36Sopenharmony_ci } else { 126962306a36Sopenharmony_ci struct v4l2_rect *crop = &camif->camif_crop; 127062306a36Sopenharmony_ci v4l_bound_align_image(&mf->width, 8, crop->width, 127162306a36Sopenharmony_ci ffs(pix_lim->out_width_align) - 1, 127262306a36Sopenharmony_ci &mf->height, 8, crop->height, 127362306a36Sopenharmony_ci 0, 0); 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci v4l2_dbg(1, debug, &camif->subdev, "%ux%u\n", mf->width, mf->height); 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_cistatic int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd, 128062306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 128162306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci struct camif_dev *camif = v4l2_get_subdevdata(sd); 128462306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &fmt->format; 128562306a36Sopenharmony_ci struct v4l2_rect *crop = &camif->camif_crop; 128662306a36Sopenharmony_ci int i; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %ux%u\n", 128962306a36Sopenharmony_ci fmt->pad, mf->code, mf->width, mf->height); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 129262306a36Sopenharmony_ci mf->colorspace = V4L2_COLORSPACE_JPEG; 129362306a36Sopenharmony_ci mutex_lock(&camif->lock); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci /* 129662306a36Sopenharmony_ci * No pixel format change at the camera input is allowed 129762306a36Sopenharmony_ci * while streaming. 129862306a36Sopenharmony_ci */ 129962306a36Sopenharmony_ci if (vb2_is_busy(&camif->vp[VP_CODEC].vb_queue) || 130062306a36Sopenharmony_ci vb2_is_busy(&camif->vp[VP_PREVIEW].vb_queue)) { 130162306a36Sopenharmony_ci mutex_unlock(&camif->lock); 130262306a36Sopenharmony_ci return -EBUSY; 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci __camif_subdev_try_format(camif, mf, fmt->pad); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 130862306a36Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); 130962306a36Sopenharmony_ci *mf = fmt->format; 131062306a36Sopenharmony_ci mutex_unlock(&camif->lock); 131162306a36Sopenharmony_ci return 0; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci switch (fmt->pad) { 131562306a36Sopenharmony_ci case CAMIF_SD_PAD_SINK: 131662306a36Sopenharmony_ci camif->mbus_fmt = *mf; 131762306a36Sopenharmony_ci /* Reset sink crop rectangle. */ 131862306a36Sopenharmony_ci crop->width = mf->width; 131962306a36Sopenharmony_ci crop->height = mf->height; 132062306a36Sopenharmony_ci crop->left = 0; 132162306a36Sopenharmony_ci crop->top = 0; 132262306a36Sopenharmony_ci /* 132362306a36Sopenharmony_ci * Reset source format (the camif's crop rectangle) 132462306a36Sopenharmony_ci * and the video output resolution. 132562306a36Sopenharmony_ci */ 132662306a36Sopenharmony_ci for (i = 0; i < CAMIF_VP_NUM; i++) { 132762306a36Sopenharmony_ci struct camif_frame *frame = &camif->vp[i].out_frame; 132862306a36Sopenharmony_ci frame->rect = *crop; 132962306a36Sopenharmony_ci frame->f_width = mf->width; 133062306a36Sopenharmony_ci frame->f_height = mf->height; 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci break; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P: 133562306a36Sopenharmony_ci /* Pixel format can be only changed on the sink pad. */ 133662306a36Sopenharmony_ci mf->code = camif->mbus_fmt.code; 133762306a36Sopenharmony_ci mf->width = crop->width; 133862306a36Sopenharmony_ci mf->height = crop->height; 133962306a36Sopenharmony_ci break; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci mutex_unlock(&camif->lock); 134362306a36Sopenharmony_ci return 0; 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistatic int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd, 134762306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 134862306a36Sopenharmony_ci struct v4l2_subdev_selection *sel) 134962306a36Sopenharmony_ci{ 135062306a36Sopenharmony_ci struct camif_dev *camif = v4l2_get_subdevdata(sd); 135162306a36Sopenharmony_ci struct v4l2_rect *crop = &camif->camif_crop; 135262306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci if ((sel->target != V4L2_SEL_TGT_CROP && 135562306a36Sopenharmony_ci sel->target != V4L2_SEL_TGT_CROP_BOUNDS) || 135662306a36Sopenharmony_ci sel->pad != CAMIF_SD_PAD_SINK) 135762306a36Sopenharmony_ci return -EINVAL; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 136062306a36Sopenharmony_ci sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); 136162306a36Sopenharmony_ci return 0; 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci mutex_lock(&camif->lock); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci if (sel->target == V4L2_SEL_TGT_CROP) { 136762306a36Sopenharmony_ci sel->r = *crop; 136862306a36Sopenharmony_ci } else { /* crop bounds */ 136962306a36Sopenharmony_ci sel->r.width = mf->width; 137062306a36Sopenharmony_ci sel->r.height = mf->height; 137162306a36Sopenharmony_ci sel->r.left = 0; 137262306a36Sopenharmony_ci sel->r.top = 0; 137362306a36Sopenharmony_ci } 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci mutex_unlock(&camif->lock); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: crop: (%d,%d) %dx%d, size: %ux%u\n", 137862306a36Sopenharmony_ci __func__, crop->left, crop->top, crop->width, 137962306a36Sopenharmony_ci crop->height, mf->width, mf->height); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci return 0; 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic void __camif_try_crop(struct camif_dev *camif, struct v4l2_rect *r) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; 138762306a36Sopenharmony_ci const struct camif_pix_limits *pix_lim = &camif->variant->pix_limits; 138862306a36Sopenharmony_ci unsigned int left = 2 * r->left; 138962306a36Sopenharmony_ci unsigned int top = 2 * r->top; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci /* 139262306a36Sopenharmony_ci * Following constraints must be met: 139362306a36Sopenharmony_ci * - r->width + 2 * r->left = mf->width; 139462306a36Sopenharmony_ci * - r->height + 2 * r->top = mf->height; 139562306a36Sopenharmony_ci * - crop rectangle size and position must be aligned 139662306a36Sopenharmony_ci * to 8 or 2 pixels, depending on SoC version. 139762306a36Sopenharmony_ci */ 139862306a36Sopenharmony_ci v4l_bound_align_image(&r->width, 0, mf->width, 139962306a36Sopenharmony_ci ffs(pix_lim->win_hor_offset_align) - 1, 140062306a36Sopenharmony_ci &r->height, 0, mf->height, 1, 0); 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci v4l_bound_align_image(&left, 0, mf->width - r->width, 140362306a36Sopenharmony_ci ffs(pix_lim->win_hor_offset_align), 140462306a36Sopenharmony_ci &top, 0, mf->height - r->height, 2, 0); 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci r->left = left / 2; 140762306a36Sopenharmony_ci r->top = top / 2; 140862306a36Sopenharmony_ci r->width = mf->width - left; 140962306a36Sopenharmony_ci r->height = mf->height - top; 141062306a36Sopenharmony_ci /* 141162306a36Sopenharmony_ci * Make sure we either downscale or upscale both the pixel 141262306a36Sopenharmony_ci * width and height. Just return current crop rectangle if 141362306a36Sopenharmony_ci * this scaler constraint is not met. 141462306a36Sopenharmony_ci */ 141562306a36Sopenharmony_ci if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV && 141662306a36Sopenharmony_ci camif_is_streaming(camif)) { 141762306a36Sopenharmony_ci unsigned int i; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci for (i = 0; i < CAMIF_VP_NUM; i++) { 142062306a36Sopenharmony_ci struct v4l2_rect *or = &camif->vp[i].out_frame.rect; 142162306a36Sopenharmony_ci if ((or->width > r->width) == (or->height > r->height)) 142262306a36Sopenharmony_ci continue; 142362306a36Sopenharmony_ci *r = camif->camif_crop; 142462306a36Sopenharmony_ci pr_debug("Width/height scaling direction limitation\n"); 142562306a36Sopenharmony_ci break; 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci v4l2_dbg(1, debug, &camif->v4l2_dev, "crop: (%d,%d)/%dx%d, fmt: %ux%u\n", 143062306a36Sopenharmony_ci r->left, r->top, r->width, r->height, mf->width, mf->height); 143162306a36Sopenharmony_ci} 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_cistatic int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd, 143462306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 143562306a36Sopenharmony_ci struct v4l2_subdev_selection *sel) 143662306a36Sopenharmony_ci{ 143762306a36Sopenharmony_ci struct camif_dev *camif = v4l2_get_subdevdata(sd); 143862306a36Sopenharmony_ci struct v4l2_rect *crop = &camif->camif_crop; 143962306a36Sopenharmony_ci struct camif_scaler scaler; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != CAMIF_SD_PAD_SINK) 144262306a36Sopenharmony_ci return -EINVAL; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci mutex_lock(&camif->lock); 144562306a36Sopenharmony_ci __camif_try_crop(camif, &sel->r); 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 144862306a36Sopenharmony_ci *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; 144962306a36Sopenharmony_ci } else { 145062306a36Sopenharmony_ci unsigned long flags; 145162306a36Sopenharmony_ci unsigned int i; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 145462306a36Sopenharmony_ci *crop = sel->r; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci for (i = 0; i < CAMIF_VP_NUM; i++) { 145762306a36Sopenharmony_ci struct camif_vp *vp = &camif->vp[i]; 145862306a36Sopenharmony_ci scaler = vp->scaler; 145962306a36Sopenharmony_ci if (s3c_camif_get_scaler_config(vp, &scaler)) 146062306a36Sopenharmony_ci continue; 146162306a36Sopenharmony_ci vp->scaler = scaler; 146262306a36Sopenharmony_ci vp->state |= ST_VP_CONFIG; 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci mutex_unlock(&camif->lock); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %u, f_h: %u\n", 147062306a36Sopenharmony_ci __func__, crop->left, crop->top, crop->width, crop->height, 147162306a36Sopenharmony_ci camif->mbus_fmt.width, camif->mbus_fmt.height); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci return 0; 147462306a36Sopenharmony_ci} 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops s3c_camif_subdev_pad_ops = { 147762306a36Sopenharmony_ci .enum_mbus_code = s3c_camif_subdev_enum_mbus_code, 147862306a36Sopenharmony_ci .get_selection = s3c_camif_subdev_get_selection, 147962306a36Sopenharmony_ci .set_selection = s3c_camif_subdev_set_selection, 148062306a36Sopenharmony_ci .get_fmt = s3c_camif_subdev_get_fmt, 148162306a36Sopenharmony_ci .set_fmt = s3c_camif_subdev_set_fmt, 148262306a36Sopenharmony_ci}; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_cistatic const struct v4l2_subdev_ops s3c_camif_subdev_ops = { 148562306a36Sopenharmony_ci .pad = &s3c_camif_subdev_pad_ops, 148662306a36Sopenharmony_ci}; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_cistatic int s3c_camif_subdev_s_ctrl(struct v4l2_ctrl *ctrl) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci struct camif_dev *camif = container_of(ctrl->handler, struct camif_dev, 149162306a36Sopenharmony_ci ctrl_handler); 149262306a36Sopenharmony_ci unsigned long flags; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci spin_lock_irqsave(&camif->slock, flags); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci switch (ctrl->id) { 149762306a36Sopenharmony_ci case V4L2_CID_COLORFX: 149862306a36Sopenharmony_ci camif->colorfx = camif->ctrl_colorfx->val; 149962306a36Sopenharmony_ci /* Set Cb, Cr */ 150062306a36Sopenharmony_ci switch (ctrl->val) { 150162306a36Sopenharmony_ci case V4L2_COLORFX_SEPIA: 150262306a36Sopenharmony_ci camif->colorfx_cb = 115; 150362306a36Sopenharmony_ci camif->colorfx_cr = 145; 150462306a36Sopenharmony_ci break; 150562306a36Sopenharmony_ci case V4L2_COLORFX_SET_CBCR: 150662306a36Sopenharmony_ci camif->colorfx_cb = camif->ctrl_colorfx_cbcr->val >> 8; 150762306a36Sopenharmony_ci camif->colorfx_cr = camif->ctrl_colorfx_cbcr->val & 0xff; 150862306a36Sopenharmony_ci break; 150962306a36Sopenharmony_ci default: 151062306a36Sopenharmony_ci /* for V4L2_COLORFX_BW and others */ 151162306a36Sopenharmony_ci camif->colorfx_cb = 128; 151262306a36Sopenharmony_ci camif->colorfx_cr = 128; 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci break; 151562306a36Sopenharmony_ci case V4L2_CID_TEST_PATTERN: 151662306a36Sopenharmony_ci camif->test_pattern = camif->ctrl_test_pattern->val; 151762306a36Sopenharmony_ci break; 151862306a36Sopenharmony_ci default: 151962306a36Sopenharmony_ci WARN_ON(1); 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci camif->vp[VP_CODEC].state |= ST_VP_CONFIG; 152362306a36Sopenharmony_ci camif->vp[VP_PREVIEW].state |= ST_VP_CONFIG; 152462306a36Sopenharmony_ci spin_unlock_irqrestore(&camif->slock, flags); 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci return 0; 152762306a36Sopenharmony_ci} 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops s3c_camif_subdev_ctrl_ops = { 153062306a36Sopenharmony_ci .s_ctrl = s3c_camif_subdev_s_ctrl, 153162306a36Sopenharmony_ci}; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_cistatic const char * const s3c_camif_test_pattern_menu[] = { 153462306a36Sopenharmony_ci "Disabled", 153562306a36Sopenharmony_ci "Color bars", 153662306a36Sopenharmony_ci "Horizontal increment", 153762306a36Sopenharmony_ci "Vertical increment", 153862306a36Sopenharmony_ci}; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ciint s3c_camif_create_subdev(struct camif_dev *camif) 154162306a36Sopenharmony_ci{ 154262306a36Sopenharmony_ci struct v4l2_ctrl_handler *handler = &camif->ctrl_handler; 154362306a36Sopenharmony_ci struct v4l2_subdev *sd = &camif->subdev; 154462306a36Sopenharmony_ci int ret; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci v4l2_subdev_init(sd, &s3c_camif_subdev_ops); 154762306a36Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 154862306a36Sopenharmony_ci strscpy(sd->name, "S3C-CAMIF", sizeof(sd->name)); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci camif->pads[CAMIF_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 155162306a36Sopenharmony_ci camif->pads[CAMIF_SD_PAD_SOURCE_C].flags = MEDIA_PAD_FL_SOURCE; 155262306a36Sopenharmony_ci camif->pads[CAMIF_SD_PAD_SOURCE_P].flags = MEDIA_PAD_FL_SOURCE; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, CAMIF_SD_PADS_NUM, 155562306a36Sopenharmony_ci camif->pads); 155662306a36Sopenharmony_ci if (ret) 155762306a36Sopenharmony_ci return ret; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci v4l2_ctrl_handler_init(handler, 3); 156062306a36Sopenharmony_ci camif->ctrl_test_pattern = v4l2_ctrl_new_std_menu_items(handler, 156162306a36Sopenharmony_ci &s3c_camif_subdev_ctrl_ops, V4L2_CID_TEST_PATTERN, 156262306a36Sopenharmony_ci ARRAY_SIZE(s3c_camif_test_pattern_menu) - 1, 0, 0, 156362306a36Sopenharmony_ci s3c_camif_test_pattern_menu); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (camif->variant->has_img_effect) { 156662306a36Sopenharmony_ci camif->ctrl_colorfx = v4l2_ctrl_new_std_menu(handler, 156762306a36Sopenharmony_ci &s3c_camif_subdev_ctrl_ops, 156862306a36Sopenharmony_ci V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR, 156962306a36Sopenharmony_ci ~0x981f, V4L2_COLORFX_NONE); 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci camif->ctrl_colorfx_cbcr = v4l2_ctrl_new_std(handler, 157262306a36Sopenharmony_ci &s3c_camif_subdev_ctrl_ops, 157362306a36Sopenharmony_ci V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0); 157462306a36Sopenharmony_ci } 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (handler->error) { 157762306a36Sopenharmony_ci v4l2_ctrl_handler_free(handler); 157862306a36Sopenharmony_ci media_entity_cleanup(&sd->entity); 157962306a36Sopenharmony_ci return handler->error; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci if (camif->variant->has_img_effect) 158362306a36Sopenharmony_ci v4l2_ctrl_auto_cluster(2, &camif->ctrl_colorfx, 158462306a36Sopenharmony_ci V4L2_COLORFX_SET_CBCR, false); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci sd->ctrl_handler = handler; 158762306a36Sopenharmony_ci v4l2_set_subdevdata(sd, camif); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci return 0; 159062306a36Sopenharmony_ci} 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_civoid s3c_camif_unregister_subdev(struct camif_dev *camif) 159362306a36Sopenharmony_ci{ 159462306a36Sopenharmony_ci struct v4l2_subdev *sd = &camif->subdev; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci /* Return if not registered */ 159762306a36Sopenharmony_ci if (v4l2_get_subdevdata(sd) == NULL) 159862306a36Sopenharmony_ci return; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci v4l2_device_unregister_subdev(sd); 160162306a36Sopenharmony_ci media_entity_cleanup(&sd->entity); 160262306a36Sopenharmony_ci v4l2_ctrl_handler_free(&camif->ctrl_handler); 160362306a36Sopenharmony_ci v4l2_set_subdevdata(sd, NULL); 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ciint s3c_camif_set_defaults(struct camif_dev *camif) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci unsigned int ip_rev = camif->variant->ip_revision; 160962306a36Sopenharmony_ci int i; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci for (i = 0; i < CAMIF_VP_NUM; i++) { 161262306a36Sopenharmony_ci struct camif_vp *vp = &camif->vp[i]; 161362306a36Sopenharmony_ci struct camif_frame *f = &vp->out_frame; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci vp->camif = camif; 161662306a36Sopenharmony_ci vp->id = i; 161762306a36Sopenharmony_ci vp->offset = camif->variant->vp_offset; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci if (ip_rev == S3C244X_CAMIF_IP_REV) 162062306a36Sopenharmony_ci vp->fmt_flags = i ? FMT_FL_S3C24XX_PREVIEW : 162162306a36Sopenharmony_ci FMT_FL_S3C24XX_CODEC; 162262306a36Sopenharmony_ci else 162362306a36Sopenharmony_ci vp->fmt_flags = FMT_FL_S3C64XX; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci vp->out_fmt = s3c_camif_find_format(vp, NULL, 0); 162662306a36Sopenharmony_ci BUG_ON(vp->out_fmt == NULL); 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci memset(f, 0, sizeof(*f)); 162962306a36Sopenharmony_ci f->f_width = CAMIF_DEF_WIDTH; 163062306a36Sopenharmony_ci f->f_height = CAMIF_DEF_HEIGHT; 163162306a36Sopenharmony_ci f->rect.width = CAMIF_DEF_WIDTH; 163262306a36Sopenharmony_ci f->rect.height = CAMIF_DEF_HEIGHT; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci /* Scaler is always enabled */ 163562306a36Sopenharmony_ci vp->scaler.enable = 1; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci vp->payload = (f->f_width * f->f_height * 163862306a36Sopenharmony_ci vp->out_fmt->depth) / 8; 163962306a36Sopenharmony_ci } 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci memset(&camif->mbus_fmt, 0, sizeof(camif->mbus_fmt)); 164262306a36Sopenharmony_ci camif->mbus_fmt.width = CAMIF_DEF_WIDTH; 164362306a36Sopenharmony_ci camif->mbus_fmt.height = CAMIF_DEF_HEIGHT; 164462306a36Sopenharmony_ci camif->mbus_fmt.code = camif_mbus_formats[0]; 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci memset(&camif->camif_crop, 0, sizeof(camif->camif_crop)); 164762306a36Sopenharmony_ci camif->camif_crop.width = CAMIF_DEF_WIDTH; 164862306a36Sopenharmony_ci camif->camif_crop.height = CAMIF_DEF_HEIGHT; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci return 0; 165162306a36Sopenharmony_ci} 1652