162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Samsung EXYNOS FIMC-LITE (camera host interface) driver 462306a36Sopenharmony_ci* 562306a36Sopenharmony_ci * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bug.h> 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/list.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/types.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/videodev2.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <media/v4l2-device.h> 2662306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2762306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h> 2862306a36Sopenharmony_ci#include <media/v4l2-rect.h> 2962306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 3062306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 3162306a36Sopenharmony_ci#include <media/drv-intf/exynos-fimc.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "common.h" 3462306a36Sopenharmony_ci#include "fimc-core.h" 3562306a36Sopenharmony_ci#include "fimc-lite.h" 3662306a36Sopenharmony_ci#include "fimc-lite-reg.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int debug; 3962306a36Sopenharmony_cimodule_param(debug, int, 0644); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic const struct fimc_fmt fimc_lite_formats[] = { 4262306a36Sopenharmony_ci { 4362306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 4462306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_JPEG, 4562306a36Sopenharmony_ci .depth = { 16 }, 4662306a36Sopenharmony_ci .color = FIMC_FMT_YCBYCR422, 4762306a36Sopenharmony_ci .memplanes = 1, 4862306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, 4962306a36Sopenharmony_ci .flags = FMT_FLAGS_YUV, 5062306a36Sopenharmony_ci }, { 5162306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 5262306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_JPEG, 5362306a36Sopenharmony_ci .depth = { 16 }, 5462306a36Sopenharmony_ci .color = FIMC_FMT_CBYCRY422, 5562306a36Sopenharmony_ci .memplanes = 1, 5662306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, 5762306a36Sopenharmony_ci .flags = FMT_FLAGS_YUV, 5862306a36Sopenharmony_ci }, { 5962306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_VYUY, 6062306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_JPEG, 6162306a36Sopenharmony_ci .depth = { 16 }, 6262306a36Sopenharmony_ci .color = FIMC_FMT_CRYCBY422, 6362306a36Sopenharmony_ci .memplanes = 1, 6462306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, 6562306a36Sopenharmony_ci .flags = FMT_FLAGS_YUV, 6662306a36Sopenharmony_ci }, { 6762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YVYU, 6862306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_JPEG, 6962306a36Sopenharmony_ci .depth = { 16 }, 7062306a36Sopenharmony_ci .color = FIMC_FMT_YCRYCB422, 7162306a36Sopenharmony_ci .memplanes = 1, 7262306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, 7362306a36Sopenharmony_ci .flags = FMT_FLAGS_YUV, 7462306a36Sopenharmony_ci }, { 7562306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGRBG8, 7662306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 7762306a36Sopenharmony_ci .depth = { 8 }, 7862306a36Sopenharmony_ci .color = FIMC_FMT_RAW8, 7962306a36Sopenharmony_ci .memplanes = 1, 8062306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, 8162306a36Sopenharmony_ci .flags = FMT_FLAGS_RAW_BAYER, 8262306a36Sopenharmony_ci }, { 8362306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGRBG10, 8462306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 8562306a36Sopenharmony_ci .depth = { 16 }, 8662306a36Sopenharmony_ci .color = FIMC_FMT_RAW10, 8762306a36Sopenharmony_ci .memplanes = 1, 8862306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, 8962306a36Sopenharmony_ci .flags = FMT_FLAGS_RAW_BAYER, 9062306a36Sopenharmony_ci }, { 9162306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGRBG12, 9262306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 9362306a36Sopenharmony_ci .depth = { 16 }, 9462306a36Sopenharmony_ci .color = FIMC_FMT_RAW12, 9562306a36Sopenharmony_ci .memplanes = 1, 9662306a36Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, 9762306a36Sopenharmony_ci .flags = FMT_FLAGS_RAW_BAYER, 9862306a36Sopenharmony_ci }, 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/** 10262306a36Sopenharmony_ci * fimc_lite_find_format - lookup fimc color format by fourcc or media bus code 10362306a36Sopenharmony_ci * @pixelformat: fourcc to match, ignored if null 10462306a36Sopenharmony_ci * @mbus_code: media bus code to match, ignored if null 10562306a36Sopenharmony_ci * @mask: the color format flags to match 10662306a36Sopenharmony_ci * @index: index to the fimc_lite_formats array, ignored if negative 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_cistatic const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, 10962306a36Sopenharmony_ci const u32 *mbus_code, unsigned int mask, int index) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci const struct fimc_fmt *fmt, *def_fmt = NULL; 11262306a36Sopenharmony_ci unsigned int i; 11362306a36Sopenharmony_ci int id = 0; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (index >= (int)ARRAY_SIZE(fimc_lite_formats)) 11662306a36Sopenharmony_ci return NULL; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fimc_lite_formats); ++i) { 11962306a36Sopenharmony_ci fmt = &fimc_lite_formats[i]; 12062306a36Sopenharmony_ci if (mask && !(fmt->flags & mask)) 12162306a36Sopenharmony_ci continue; 12262306a36Sopenharmony_ci if (pixelformat && fmt->fourcc == *pixelformat) 12362306a36Sopenharmony_ci return fmt; 12462306a36Sopenharmony_ci if (mbus_code && fmt->mbus_code == *mbus_code) 12562306a36Sopenharmony_ci return fmt; 12662306a36Sopenharmony_ci if (index == id) 12762306a36Sopenharmony_ci def_fmt = fmt; 12862306a36Sopenharmony_ci id++; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci return def_fmt; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct fimc_source_info *si; 13662306a36Sopenharmony_ci unsigned long flags; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (fimc->sensor == NULL) 13962306a36Sopenharmony_ci return -ENXIO; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (fimc->inp_frame.fmt == NULL || fimc->out_frame.fmt == NULL) 14262306a36Sopenharmony_ci return -EINVAL; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* Get sensor configuration data from the sensor subdev */ 14562306a36Sopenharmony_ci si = v4l2_get_subdev_hostdata(fimc->sensor); 14662306a36Sopenharmony_ci if (!si) 14762306a36Sopenharmony_ci return -EINVAL; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci flite_hw_set_camera_bus(fimc, si); 15262306a36Sopenharmony_ci flite_hw_set_source_format(fimc, &fimc->inp_frame); 15362306a36Sopenharmony_ci flite_hw_set_window_offset(fimc, &fimc->inp_frame); 15462306a36Sopenharmony_ci flite_hw_set_dma_buf_mask(fimc, 0); 15562306a36Sopenharmony_ci flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output); 15662306a36Sopenharmony_ci flite_hw_set_interrupt_mask(fimc); 15762306a36Sopenharmony_ci flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (debug > 0) 16062306a36Sopenharmony_ci flite_hw_dump_regs(fimc, __func__); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* 16762306a36Sopenharmony_ci * Reinitialize the driver so it is ready to start the streaming again. 16862306a36Sopenharmony_ci * Set fimc->state to indicate stream off and the hardware shut down state. 16962306a36Sopenharmony_ci * If not suspending (@suspend is false), return any buffers to videobuf2. 17062306a36Sopenharmony_ci * Otherwise put any owned buffers onto the pending buffers queue, so they 17162306a36Sopenharmony_ci * can be re-spun when the device is being resumed. Also perform FIMC 17262306a36Sopenharmony_ci * software reset and disable streaming on the whole pipeline if required. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_cistatic int fimc_lite_reinit(struct fimc_lite *fimc, bool suspend) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct flite_buffer *buf; 17762306a36Sopenharmony_ci unsigned long flags; 17862306a36Sopenharmony_ci bool streaming; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 18162306a36Sopenharmony_ci streaming = fimc->state & (1 << ST_SENSOR_STREAM); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci fimc->state &= ~(1 << ST_FLITE_RUN | 1 << ST_FLITE_OFF | 18462306a36Sopenharmony_ci 1 << ST_FLITE_STREAM | 1 << ST_SENSOR_STREAM); 18562306a36Sopenharmony_ci if (suspend) 18662306a36Sopenharmony_ci fimc->state |= (1 << ST_FLITE_SUSPENDED); 18762306a36Sopenharmony_ci else 18862306a36Sopenharmony_ci fimc->state &= ~(1 << ST_FLITE_PENDING | 18962306a36Sopenharmony_ci 1 << ST_FLITE_SUSPENDED); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Release unused buffers */ 19262306a36Sopenharmony_ci while (!suspend && !list_empty(&fimc->pending_buf_q)) { 19362306a36Sopenharmony_ci buf = fimc_lite_pending_queue_pop(fimc); 19462306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci /* If suspending put unused buffers onto pending queue */ 19762306a36Sopenharmony_ci while (!list_empty(&fimc->active_buf_q)) { 19862306a36Sopenharmony_ci buf = fimc_lite_active_queue_pop(fimc); 19962306a36Sopenharmony_ci if (suspend) 20062306a36Sopenharmony_ci fimc_lite_pending_queue_add(fimc, buf); 20162306a36Sopenharmony_ci else 20262306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci flite_hw_reset(fimc); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (!streaming) 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return fimc_pipeline_call(&fimc->ve, set_stream, 0); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int fimc_lite_stop_capture(struct fimc_lite *fimc, bool suspend) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci unsigned long flags; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (!fimc_lite_active(fimc)) 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 22362306a36Sopenharmony_ci set_bit(ST_FLITE_OFF, &fimc->state); 22462306a36Sopenharmony_ci flite_hw_capture_stop(fimc); 22562306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci wait_event_timeout(fimc->irq_queue, 22862306a36Sopenharmony_ci !test_bit(ST_FLITE_OFF, &fimc->state), 22962306a36Sopenharmony_ci (2*HZ/10)); /* 200 ms */ 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return fimc_lite_reinit(fimc, suspend); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* Must be called with fimc.slock spinlock held. */ 23562306a36Sopenharmony_cistatic void fimc_lite_config_update(struct fimc_lite *fimc) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci flite_hw_set_window_offset(fimc, &fimc->inp_frame); 23862306a36Sopenharmony_ci flite_hw_set_dma_window(fimc, &fimc->out_frame); 23962306a36Sopenharmony_ci flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); 24062306a36Sopenharmony_ci clear_bit(ST_FLITE_CONFIG, &fimc->state); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic irqreturn_t flite_irq_handler(int irq, void *priv) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct fimc_lite *fimc = priv; 24662306a36Sopenharmony_ci struct flite_buffer *vbuf; 24762306a36Sopenharmony_ci unsigned long flags; 24862306a36Sopenharmony_ci u32 intsrc; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci intsrc = flite_hw_get_interrupt_source(fimc); 25362306a36Sopenharmony_ci flite_hw_clear_pending_irq(fimc); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (test_and_clear_bit(ST_FLITE_OFF, &fimc->state)) { 25662306a36Sopenharmony_ci wake_up(&fimc->irq_queue); 25762306a36Sopenharmony_ci goto done; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW) { 26162306a36Sopenharmony_ci clear_bit(ST_FLITE_RUN, &fimc->state); 26262306a36Sopenharmony_ci fimc->events.data_overflow++; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND) { 26662306a36Sopenharmony_ci flite_hw_clear_last_capture_end(fimc); 26762306a36Sopenharmony_ci clear_bit(ST_FLITE_STREAM, &fimc->state); 26862306a36Sopenharmony_ci wake_up(&fimc->irq_queue); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) 27262306a36Sopenharmony_ci goto done; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) && 27562306a36Sopenharmony_ci test_bit(ST_FLITE_RUN, &fimc->state) && 27662306a36Sopenharmony_ci !list_empty(&fimc->pending_buf_q)) { 27762306a36Sopenharmony_ci vbuf = fimc_lite_pending_queue_pop(fimc); 27862306a36Sopenharmony_ci flite_hw_set_dma_buffer(fimc, vbuf); 27962306a36Sopenharmony_ci fimc_lite_active_queue_add(fimc, vbuf); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMEND) && 28362306a36Sopenharmony_ci test_bit(ST_FLITE_RUN, &fimc->state) && 28462306a36Sopenharmony_ci !list_empty(&fimc->active_buf_q)) { 28562306a36Sopenharmony_ci vbuf = fimc_lite_active_queue_pop(fimc); 28662306a36Sopenharmony_ci vbuf->vb.vb2_buf.timestamp = ktime_get_ns(); 28762306a36Sopenharmony_ci vbuf->vb.sequence = fimc->frame_count++; 28862306a36Sopenharmony_ci flite_hw_mask_dma_buffer(fimc, vbuf->index); 28962306a36Sopenharmony_ci vb2_buffer_done(&vbuf->vb.vb2_buf, VB2_BUF_STATE_DONE); 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (test_bit(ST_FLITE_CONFIG, &fimc->state)) 29362306a36Sopenharmony_ci fimc_lite_config_update(fimc); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (list_empty(&fimc->pending_buf_q)) { 29662306a36Sopenharmony_ci flite_hw_capture_stop(fimc); 29762306a36Sopenharmony_ci clear_bit(ST_FLITE_STREAM, &fimc->state); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_cidone: 30062306a36Sopenharmony_ci set_bit(ST_FLITE_RUN, &fimc->state); 30162306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 30262306a36Sopenharmony_ci return IRQ_HANDLED; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *q, unsigned int count) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct fimc_lite *fimc = q->drv_priv; 30862306a36Sopenharmony_ci unsigned long flags; 30962306a36Sopenharmony_ci int ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci fimc->buf_index = 0; 31462306a36Sopenharmony_ci fimc->frame_count = 0; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ret = fimc_lite_hw_init(fimc, false); 31962306a36Sopenharmony_ci if (ret) { 32062306a36Sopenharmony_ci fimc_lite_reinit(fimc, false); 32162306a36Sopenharmony_ci return ret; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci set_bit(ST_FLITE_PENDING, &fimc->state); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (!list_empty(&fimc->active_buf_q) && 32762306a36Sopenharmony_ci !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) { 32862306a36Sopenharmony_ci flite_hw_capture_start(fimc); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state)) 33162306a36Sopenharmony_ci fimc_pipeline_call(&fimc->ve, set_stream, 1); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci if (debug > 0) 33462306a36Sopenharmony_ci flite_hw_dump_regs(fimc, __func__); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *q) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct fimc_lite *fimc = q->drv_priv; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (!fimc_lite_active(fimc)) 34462306a36Sopenharmony_ci return; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci fimc_lite_stop_capture(fimc, false); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq, 35062306a36Sopenharmony_ci unsigned int *num_buffers, unsigned int *num_planes, 35162306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct fimc_lite *fimc = vq->drv_priv; 35462306a36Sopenharmony_ci struct flite_frame *frame = &fimc->out_frame; 35562306a36Sopenharmony_ci const struct fimc_fmt *fmt = frame->fmt; 35662306a36Sopenharmony_ci unsigned long wh = frame->f_width * frame->f_height; 35762306a36Sopenharmony_ci int i; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (fmt == NULL) 36062306a36Sopenharmony_ci return -EINVAL; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (*num_planes) { 36362306a36Sopenharmony_ci if (*num_planes != fmt->memplanes) 36462306a36Sopenharmony_ci return -EINVAL; 36562306a36Sopenharmony_ci for (i = 0; i < *num_planes; i++) 36662306a36Sopenharmony_ci if (sizes[i] < (wh * fmt->depth[i]) / 8) 36762306a36Sopenharmony_ci return -EINVAL; 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci *num_planes = fmt->memplanes; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci for (i = 0; i < fmt->memplanes; i++) 37462306a36Sopenharmony_ci sizes[i] = (wh * fmt->depth[i]) / 8; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct vb2_queue *vq = vb->vb2_queue; 38262306a36Sopenharmony_ci struct fimc_lite *fimc = vq->drv_priv; 38362306a36Sopenharmony_ci int i; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (fimc->out_frame.fmt == NULL) 38662306a36Sopenharmony_ci return -EINVAL; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci for (i = 0; i < fimc->out_frame.fmt->memplanes; i++) { 38962306a36Sopenharmony_ci unsigned long size = fimc->payload[i]; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (vb2_plane_size(vb, i) < size) { 39262306a36Sopenharmony_ci v4l2_err(&fimc->ve.vdev, 39362306a36Sopenharmony_ci "User buffer too small (%ld < %ld)\n", 39462306a36Sopenharmony_ci vb2_plane_size(vb, i), size); 39562306a36Sopenharmony_ci return -EINVAL; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci vb2_set_plane_payload(vb, i, size); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 40662306a36Sopenharmony_ci struct flite_buffer *buf 40762306a36Sopenharmony_ci = container_of(vbuf, struct flite_buffer, vb); 40862306a36Sopenharmony_ci struct fimc_lite *fimc = vb2_get_drv_priv(vb->vb2_queue); 40962306a36Sopenharmony_ci unsigned long flags; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 41262306a36Sopenharmony_ci buf->addr = vb2_dma_contig_plane_dma_addr(vb, 0); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci buf->index = fimc->buf_index++; 41562306a36Sopenharmony_ci if (fimc->buf_index >= fimc->reqbufs_count) 41662306a36Sopenharmony_ci fimc->buf_index = 0; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (!test_bit(ST_FLITE_SUSPENDED, &fimc->state) && 41962306a36Sopenharmony_ci !test_bit(ST_FLITE_STREAM, &fimc->state) && 42062306a36Sopenharmony_ci list_empty(&fimc->active_buf_q)) { 42162306a36Sopenharmony_ci flite_hw_set_dma_buffer(fimc, buf); 42262306a36Sopenharmony_ci fimc_lite_active_queue_add(fimc, buf); 42362306a36Sopenharmony_ci } else { 42462306a36Sopenharmony_ci fimc_lite_pending_queue_add(fimc, buf); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (vb2_is_streaming(&fimc->vb_queue) && 42862306a36Sopenharmony_ci !list_empty(&fimc->pending_buf_q) && 42962306a36Sopenharmony_ci !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) { 43062306a36Sopenharmony_ci flite_hw_capture_start(fimc); 43162306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state)) 43462306a36Sopenharmony_ci fimc_pipeline_call(&fimc->ve, set_stream, 1); 43562306a36Sopenharmony_ci return; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic const struct vb2_ops fimc_lite_qops = { 44162306a36Sopenharmony_ci .queue_setup = queue_setup, 44262306a36Sopenharmony_ci .buf_prepare = buffer_prepare, 44362306a36Sopenharmony_ci .buf_queue = buffer_queue, 44462306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 44562306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 44662306a36Sopenharmony_ci .start_streaming = start_streaming, 44762306a36Sopenharmony_ci .stop_streaming = stop_streaming, 44862306a36Sopenharmony_ci}; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic void fimc_lite_clear_event_counters(struct fimc_lite *fimc) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci unsigned long flags; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 45562306a36Sopenharmony_ci memset(&fimc->events, 0, sizeof(fimc->events)); 45662306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int fimc_lite_open(struct file *file) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct fimc_lite *fimc = video_drvdata(file); 46262306a36Sopenharmony_ci struct media_entity *me = &fimc->ve.vdev.entity; 46362306a36Sopenharmony_ci int ret; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci mutex_lock(&fimc->lock); 46662306a36Sopenharmony_ci if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) { 46762306a36Sopenharmony_ci ret = -EBUSY; 46862306a36Sopenharmony_ci goto unlock; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci set_bit(ST_FLITE_IN_USE, &fimc->state); 47262306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(&fimc->pdev->dev); 47362306a36Sopenharmony_ci if (ret < 0) 47462306a36Sopenharmony_ci goto err_in_use; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci ret = v4l2_fh_open(file); 47762306a36Sopenharmony_ci if (ret < 0) 47862306a36Sopenharmony_ci goto err_pm; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (!v4l2_fh_is_singular_file(file) || 48162306a36Sopenharmony_ci atomic_read(&fimc->out_path) != FIMC_IO_DMA) 48262306a36Sopenharmony_ci goto unlock; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci mutex_lock(&me->graph_obj.mdev->graph_mutex); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci ret = fimc_pipeline_call(&fimc->ve, open, me, true); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Mark video pipeline ending at this video node as in use. */ 48962306a36Sopenharmony_ci if (ret == 0) 49062306a36Sopenharmony_ci me->use_count++; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci mutex_unlock(&me->graph_obj.mdev->graph_mutex); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (!ret) { 49562306a36Sopenharmony_ci fimc_lite_clear_event_counters(fimc); 49662306a36Sopenharmony_ci goto unlock; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci v4l2_fh_release(file); 50062306a36Sopenharmony_cierr_pm: 50162306a36Sopenharmony_ci pm_runtime_put_sync(&fimc->pdev->dev); 50262306a36Sopenharmony_cierr_in_use: 50362306a36Sopenharmony_ci clear_bit(ST_FLITE_IN_USE, &fimc->state); 50462306a36Sopenharmony_ciunlock: 50562306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 50662306a36Sopenharmony_ci return ret; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int fimc_lite_release(struct file *file) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct fimc_lite *fimc = video_drvdata(file); 51262306a36Sopenharmony_ci struct media_entity *entity = &fimc->ve.vdev.entity; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci mutex_lock(&fimc->lock); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (v4l2_fh_is_singular_file(file) && 51762306a36Sopenharmony_ci atomic_read(&fimc->out_path) == FIMC_IO_DMA) { 51862306a36Sopenharmony_ci if (fimc->streaming) { 51962306a36Sopenharmony_ci video_device_pipeline_stop(&fimc->ve.vdev); 52062306a36Sopenharmony_ci fimc->streaming = false; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci fimc_lite_stop_capture(fimc, false); 52362306a36Sopenharmony_ci fimc_pipeline_call(&fimc->ve, close); 52462306a36Sopenharmony_ci clear_bit(ST_FLITE_IN_USE, &fimc->state); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci mutex_lock(&entity->graph_obj.mdev->graph_mutex); 52762306a36Sopenharmony_ci entity->use_count--; 52862306a36Sopenharmony_ci mutex_unlock(&entity->graph_obj.mdev->graph_mutex); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci _vb2_fop_release(file, NULL); 53262306a36Sopenharmony_ci pm_runtime_put(&fimc->pdev->dev); 53362306a36Sopenharmony_ci clear_bit(ST_FLITE_SUSPENDED, &fimc->state); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 53662306a36Sopenharmony_ci return 0; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic const struct v4l2_file_operations fimc_lite_fops = { 54062306a36Sopenharmony_ci .owner = THIS_MODULE, 54162306a36Sopenharmony_ci .open = fimc_lite_open, 54262306a36Sopenharmony_ci .release = fimc_lite_release, 54362306a36Sopenharmony_ci .poll = vb2_fop_poll, 54462306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 54562306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 54662306a36Sopenharmony_ci}; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci/* 54962306a36Sopenharmony_ci * Format and crop negotiation helpers 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc, 55362306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 55462306a36Sopenharmony_ci struct v4l2_subdev_format *format) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct flite_drvdata *dd = fimc->dd; 55762306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &format->format; 55862306a36Sopenharmony_ci const struct fimc_fmt *fmt = NULL; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (format->pad == FLITE_SD_PAD_SINK) { 56162306a36Sopenharmony_ci v4l_bound_align_image(&mf->width, 8, dd->max_width, 56262306a36Sopenharmony_ci ffs(dd->out_width_align) - 1, 56362306a36Sopenharmony_ci &mf->height, 0, dd->max_height, 0, 0); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci fmt = fimc_lite_find_format(NULL, &mf->code, 0, 0); 56662306a36Sopenharmony_ci if (WARN_ON(!fmt)) 56762306a36Sopenharmony_ci return NULL; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci mf->colorspace = fmt->colorspace; 57062306a36Sopenharmony_ci mf->code = fmt->mbus_code; 57162306a36Sopenharmony_ci } else { 57262306a36Sopenharmony_ci struct flite_frame *sink = &fimc->inp_frame; 57362306a36Sopenharmony_ci struct v4l2_mbus_framefmt *sink_fmt; 57462306a36Sopenharmony_ci struct v4l2_rect *rect; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 57762306a36Sopenharmony_ci sink_fmt = v4l2_subdev_get_try_format(&fimc->subdev, 57862306a36Sopenharmony_ci sd_state, 57962306a36Sopenharmony_ci FLITE_SD_PAD_SINK); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci mf->code = sink_fmt->code; 58262306a36Sopenharmony_ci mf->colorspace = sink_fmt->colorspace; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci rect = v4l2_subdev_get_try_crop(&fimc->subdev, 58562306a36Sopenharmony_ci sd_state, 58662306a36Sopenharmony_ci FLITE_SD_PAD_SINK); 58762306a36Sopenharmony_ci } else { 58862306a36Sopenharmony_ci mf->code = sink->fmt->mbus_code; 58962306a36Sopenharmony_ci mf->colorspace = sink->fmt->colorspace; 59062306a36Sopenharmony_ci rect = &sink->rect; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Allow changing format only on sink pad */ 59462306a36Sopenharmony_ci mf->width = rect->width; 59562306a36Sopenharmony_ci mf->height = rect->height; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci mf->field = V4L2_FIELD_NONE; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci v4l2_dbg(1, debug, &fimc->subdev, "code: %#x (%d), %dx%d\n", 60162306a36Sopenharmony_ci mf->code, mf->colorspace, mf->width, mf->height); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return fmt; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct flite_frame *frame = &fimc->inp_frame; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci v4l_bound_align_image(&r->width, 0, frame->f_width, 0, 61162306a36Sopenharmony_ci &r->height, 0, frame->f_height, 0, 0); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* Adjust left/top if cropping rectangle got out of bounds */ 61462306a36Sopenharmony_ci r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); 61562306a36Sopenharmony_ci r->left = round_down(r->left, fimc->dd->win_hor_offs_align); 61662306a36Sopenharmony_ci r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d\n", 61962306a36Sopenharmony_ci r->left, r->top, r->width, r->height, 62062306a36Sopenharmony_ci frame->f_width, frame->f_height); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct flite_frame *frame = &fimc->out_frame; 62662306a36Sopenharmony_ci struct v4l2_rect *crop_rect = &fimc->inp_frame.rect; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Scaling is not supported so we enforce compose rectangle size 62962306a36Sopenharmony_ci same as size of the sink crop rectangle. */ 63062306a36Sopenharmony_ci r->width = crop_rect->width; 63162306a36Sopenharmony_ci r->height = crop_rect->height; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Adjust left/top if the composing rectangle got out of bounds */ 63462306a36Sopenharmony_ci r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); 63562306a36Sopenharmony_ci r->left = round_down(r->left, fimc->dd->out_hor_offs_align); 63662306a36Sopenharmony_ci r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d\n", 63962306a36Sopenharmony_ci r->left, r->top, r->width, r->height, 64062306a36Sopenharmony_ci frame->f_width, frame->f_height); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci/* 64462306a36Sopenharmony_ci * Video node ioctl operations 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_cistatic int fimc_lite_querycap(struct file *file, void *priv, 64762306a36Sopenharmony_ci struct v4l2_capability *cap) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci strscpy(cap->driver, FIMC_LITE_DRV_NAME, sizeof(cap->driver)); 65062306a36Sopenharmony_ci strscpy(cap->card, FIMC_LITE_DRV_NAME, sizeof(cap->card)); 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic int fimc_lite_enum_fmt(struct file *file, void *priv, 65562306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci const struct fimc_fmt *fmt; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (f->index >= ARRAY_SIZE(fimc_lite_formats)) 66062306a36Sopenharmony_ci return -EINVAL; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci fmt = &fimc_lite_formats[f->index]; 66362306a36Sopenharmony_ci f->pixelformat = fmt->fourcc; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return 0; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic int fimc_lite_g_fmt_mplane(struct file *file, void *fh, 66962306a36Sopenharmony_ci struct v4l2_format *f) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct fimc_lite *fimc = video_drvdata(file); 67262306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; 67362306a36Sopenharmony_ci struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0]; 67462306a36Sopenharmony_ci struct flite_frame *frame = &fimc->out_frame; 67562306a36Sopenharmony_ci const struct fimc_fmt *fmt = frame->fmt; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci plane_fmt->bytesperline = (frame->f_width * fmt->depth[0]) / 8; 67862306a36Sopenharmony_ci plane_fmt->sizeimage = plane_fmt->bytesperline * frame->f_height; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci pixm->num_planes = fmt->memplanes; 68162306a36Sopenharmony_ci pixm->pixelformat = fmt->fourcc; 68262306a36Sopenharmony_ci pixm->width = frame->f_width; 68362306a36Sopenharmony_ci pixm->height = frame->f_height; 68462306a36Sopenharmony_ci pixm->field = V4L2_FIELD_NONE; 68562306a36Sopenharmony_ci pixm->colorspace = fmt->colorspace; 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic int fimc_lite_try_fmt(struct fimc_lite *fimc, 69062306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pixm, 69162306a36Sopenharmony_ci const struct fimc_fmt **ffmt) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci u32 bpl = pixm->plane_fmt[0].bytesperline; 69462306a36Sopenharmony_ci struct flite_drvdata *dd = fimc->dd; 69562306a36Sopenharmony_ci const struct fimc_fmt *inp_fmt = fimc->inp_frame.fmt; 69662306a36Sopenharmony_ci const struct fimc_fmt *fmt; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (WARN_ON(inp_fmt == NULL)) 69962306a36Sopenharmony_ci return -EINVAL; 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * We allow some flexibility only for YUV formats. In case of raw 70262306a36Sopenharmony_ci * raw Bayer the FIMC-LITE's output format must match its camera 70362306a36Sopenharmony_ci * interface input format. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_ci if (inp_fmt->flags & FMT_FLAGS_YUV) 70662306a36Sopenharmony_ci fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 70762306a36Sopenharmony_ci inp_fmt->flags, 0); 70862306a36Sopenharmony_ci else 70962306a36Sopenharmony_ci fmt = inp_fmt; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (WARN_ON(fmt == NULL)) 71262306a36Sopenharmony_ci return -EINVAL; 71362306a36Sopenharmony_ci if (ffmt) 71462306a36Sopenharmony_ci *ffmt = fmt; 71562306a36Sopenharmony_ci v4l_bound_align_image(&pixm->width, 8, dd->max_width, 71662306a36Sopenharmony_ci ffs(dd->out_width_align) - 1, 71762306a36Sopenharmony_ci &pixm->height, 0, dd->max_height, 0, 0); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if ((bpl == 0 || ((bpl * 8) / fmt->depth[0]) < pixm->width)) 72062306a36Sopenharmony_ci pixm->plane_fmt[0].bytesperline = (pixm->width * 72162306a36Sopenharmony_ci fmt->depth[0]) / 8; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (pixm->plane_fmt[0].sizeimage == 0) 72462306a36Sopenharmony_ci pixm->plane_fmt[0].sizeimage = (pixm->width * pixm->height * 72562306a36Sopenharmony_ci fmt->depth[0]) / 8; 72662306a36Sopenharmony_ci pixm->num_planes = fmt->memplanes; 72762306a36Sopenharmony_ci pixm->pixelformat = fmt->fourcc; 72862306a36Sopenharmony_ci pixm->colorspace = fmt->colorspace; 72962306a36Sopenharmony_ci pixm->field = V4L2_FIELD_NONE; 73062306a36Sopenharmony_ci return 0; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic int fimc_lite_try_fmt_mplane(struct file *file, void *fh, 73462306a36Sopenharmony_ci struct v4l2_format *f) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct fimc_lite *fimc = video_drvdata(file); 73762306a36Sopenharmony_ci return fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, NULL); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic int fimc_lite_s_fmt_mplane(struct file *file, void *priv, 74162306a36Sopenharmony_ci struct v4l2_format *f) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; 74462306a36Sopenharmony_ci struct fimc_lite *fimc = video_drvdata(file); 74562306a36Sopenharmony_ci struct flite_frame *frame = &fimc->out_frame; 74662306a36Sopenharmony_ci const struct fimc_fmt *fmt = NULL; 74762306a36Sopenharmony_ci int ret; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (vb2_is_busy(&fimc->vb_queue)) 75062306a36Sopenharmony_ci return -EBUSY; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci ret = fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, &fmt); 75362306a36Sopenharmony_ci if (ret < 0) 75462306a36Sopenharmony_ci return ret; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci frame->fmt = fmt; 75762306a36Sopenharmony_ci fimc->payload[0] = max((pixm->width * pixm->height * fmt->depth[0]) / 8, 75862306a36Sopenharmony_ci pixm->plane_fmt[0].sizeimage); 75962306a36Sopenharmony_ci frame->f_width = pixm->width; 76062306a36Sopenharmony_ci frame->f_height = pixm->height; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return 0; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic int fimc_pipeline_validate(struct fimc_lite *fimc) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct v4l2_subdev *sd = &fimc->subdev; 76862306a36Sopenharmony_ci struct v4l2_subdev_format sink_fmt = { 76962306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 77062306a36Sopenharmony_ci }; 77162306a36Sopenharmony_ci struct v4l2_subdev_format src_fmt = { 77262306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 77362306a36Sopenharmony_ci }; 77462306a36Sopenharmony_ci struct media_pad *pad; 77562306a36Sopenharmony_ci int ret; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci while (1) { 77862306a36Sopenharmony_ci /* Retrieve format at the sink pad */ 77962306a36Sopenharmony_ci pad = &sd->entity.pads[0]; 78062306a36Sopenharmony_ci if (!(pad->flags & MEDIA_PAD_FL_SINK)) 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci /* Don't call FIMC subdev operation to avoid nested locking */ 78362306a36Sopenharmony_ci if (sd == &fimc->subdev) { 78462306a36Sopenharmony_ci struct flite_frame *ff = &fimc->out_frame; 78562306a36Sopenharmony_ci sink_fmt.format.width = ff->f_width; 78662306a36Sopenharmony_ci sink_fmt.format.height = ff->f_height; 78762306a36Sopenharmony_ci sink_fmt.format.code = fimc->inp_frame.fmt->mbus_code; 78862306a36Sopenharmony_ci } else { 78962306a36Sopenharmony_ci sink_fmt.pad = pad->index; 79062306a36Sopenharmony_ci ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, 79162306a36Sopenharmony_ci &sink_fmt); 79262306a36Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 79362306a36Sopenharmony_ci return -EPIPE; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci /* Retrieve format at the source pad */ 79662306a36Sopenharmony_ci pad = media_pad_remote_pad_first(pad); 79762306a36Sopenharmony_ci if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 79862306a36Sopenharmony_ci break; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci sd = media_entity_to_v4l2_subdev(pad->entity); 80162306a36Sopenharmony_ci src_fmt.pad = pad->index; 80262306a36Sopenharmony_ci ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); 80362306a36Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 80462306a36Sopenharmony_ci return -EPIPE; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (src_fmt.format.width != sink_fmt.format.width || 80762306a36Sopenharmony_ci src_fmt.format.height != sink_fmt.format.height || 80862306a36Sopenharmony_ci src_fmt.format.code != sink_fmt.format.code) 80962306a36Sopenharmony_ci return -EPIPE; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic int fimc_lite_streamon(struct file *file, void *priv, 81562306a36Sopenharmony_ci enum v4l2_buf_type type) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci struct fimc_lite *fimc = video_drvdata(file); 81862306a36Sopenharmony_ci int ret; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (fimc_lite_active(fimc)) 82162306a36Sopenharmony_ci return -EBUSY; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci ret = video_device_pipeline_start(&fimc->ve.vdev, &fimc->ve.pipe->mp); 82462306a36Sopenharmony_ci if (ret < 0) 82562306a36Sopenharmony_ci return ret; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ret = fimc_pipeline_validate(fimc); 82862306a36Sopenharmony_ci if (ret < 0) 82962306a36Sopenharmony_ci goto err_p_stop; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci fimc->sensor = fimc_find_remote_sensor(&fimc->subdev.entity); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci ret = vb2_ioctl_streamon(file, priv, type); 83462306a36Sopenharmony_ci if (!ret) { 83562306a36Sopenharmony_ci fimc->streaming = true; 83662306a36Sopenharmony_ci return ret; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cierr_p_stop: 84062306a36Sopenharmony_ci video_device_pipeline_stop(&fimc->ve.vdev); 84162306a36Sopenharmony_ci return 0; 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cistatic int fimc_lite_streamoff(struct file *file, void *priv, 84562306a36Sopenharmony_ci enum v4l2_buf_type type) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct fimc_lite *fimc = video_drvdata(file); 84862306a36Sopenharmony_ci int ret; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci ret = vb2_ioctl_streamoff(file, priv, type); 85162306a36Sopenharmony_ci if (ret < 0) 85262306a36Sopenharmony_ci return ret; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci video_device_pipeline_stop(&fimc->ve.vdev); 85562306a36Sopenharmony_ci fimc->streaming = false; 85662306a36Sopenharmony_ci return 0; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic int fimc_lite_reqbufs(struct file *file, void *priv, 86062306a36Sopenharmony_ci struct v4l2_requestbuffers *reqbufs) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct fimc_lite *fimc = video_drvdata(file); 86362306a36Sopenharmony_ci int ret; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci reqbufs->count = max_t(u32, FLITE_REQ_BUFS_MIN, reqbufs->count); 86662306a36Sopenharmony_ci ret = vb2_ioctl_reqbufs(file, priv, reqbufs); 86762306a36Sopenharmony_ci if (!ret) 86862306a36Sopenharmony_ci fimc->reqbufs_count = reqbufs->count; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return ret; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic int fimc_lite_g_selection(struct file *file, void *fh, 87462306a36Sopenharmony_ci struct v4l2_selection *sel) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci struct fimc_lite *fimc = video_drvdata(file); 87762306a36Sopenharmony_ci struct flite_frame *f = &fimc->out_frame; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 88062306a36Sopenharmony_ci return -EINVAL; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci switch (sel->target) { 88362306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 88462306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 88562306a36Sopenharmony_ci sel->r.left = 0; 88662306a36Sopenharmony_ci sel->r.top = 0; 88762306a36Sopenharmony_ci sel->r.width = f->f_width; 88862306a36Sopenharmony_ci sel->r.height = f->f_height; 88962306a36Sopenharmony_ci return 0; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 89262306a36Sopenharmony_ci sel->r = f->rect; 89362306a36Sopenharmony_ci return 0; 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci return -EINVAL; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic int fimc_lite_s_selection(struct file *file, void *fh, 90062306a36Sopenharmony_ci struct v4l2_selection *sel) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci struct fimc_lite *fimc = video_drvdata(file); 90362306a36Sopenharmony_ci struct flite_frame *f = &fimc->out_frame; 90462306a36Sopenharmony_ci struct v4l2_rect rect = sel->r; 90562306a36Sopenharmony_ci unsigned long flags; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || 90862306a36Sopenharmony_ci sel->target != V4L2_SEL_TGT_COMPOSE) 90962306a36Sopenharmony_ci return -EINVAL; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci fimc_lite_try_compose(fimc, &rect); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if ((sel->flags & V4L2_SEL_FLAG_LE) && 91462306a36Sopenharmony_ci !v4l2_rect_enclosed(&rect, &sel->r)) 91562306a36Sopenharmony_ci return -ERANGE; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if ((sel->flags & V4L2_SEL_FLAG_GE) && 91862306a36Sopenharmony_ci !v4l2_rect_enclosed(&sel->r, &rect)) 91962306a36Sopenharmony_ci return -ERANGE; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci sel->r = rect; 92262306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 92362306a36Sopenharmony_ci f->rect = rect; 92462306a36Sopenharmony_ci set_bit(ST_FLITE_CONFIG, &fimc->state); 92562306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci return 0; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { 93162306a36Sopenharmony_ci .vidioc_querycap = fimc_lite_querycap, 93262306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = fimc_lite_enum_fmt, 93362306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane, 93462306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane, 93562306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = fimc_lite_g_fmt_mplane, 93662306a36Sopenharmony_ci .vidioc_g_selection = fimc_lite_g_selection, 93762306a36Sopenharmony_ci .vidioc_s_selection = fimc_lite_s_selection, 93862306a36Sopenharmony_ci .vidioc_reqbufs = fimc_lite_reqbufs, 93962306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 94062306a36Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 94162306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 94262306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 94362306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 94462306a36Sopenharmony_ci .vidioc_streamon = fimc_lite_streamon, 94562306a36Sopenharmony_ci .vidioc_streamoff = fimc_lite_streamoff, 94662306a36Sopenharmony_ci}; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci/* Capture subdev media entity operations */ 94962306a36Sopenharmony_cistatic int fimc_lite_link_setup(struct media_entity *entity, 95062306a36Sopenharmony_ci const struct media_pad *local, 95162306a36Sopenharmony_ci const struct media_pad *remote, u32 flags) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); 95462306a36Sopenharmony_ci struct fimc_lite *fimc = v4l2_get_subdevdata(sd); 95562306a36Sopenharmony_ci int ret = 0; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (WARN_ON(fimc == NULL)) 95862306a36Sopenharmony_ci return 0; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x\n", 96162306a36Sopenharmony_ci __func__, remote->entity->name, local->entity->name, 96262306a36Sopenharmony_ci flags, fimc->source_subdev_grp_id); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci switch (local->index) { 96562306a36Sopenharmony_ci case FLITE_SD_PAD_SINK: 96662306a36Sopenharmony_ci if (flags & MEDIA_LNK_FL_ENABLED) { 96762306a36Sopenharmony_ci if (fimc->source_subdev_grp_id == 0) 96862306a36Sopenharmony_ci fimc->source_subdev_grp_id = sd->grp_id; 96962306a36Sopenharmony_ci else 97062306a36Sopenharmony_ci ret = -EBUSY; 97162306a36Sopenharmony_ci } else { 97262306a36Sopenharmony_ci fimc->source_subdev_grp_id = 0; 97362306a36Sopenharmony_ci fimc->sensor = NULL; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci break; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci case FLITE_SD_PAD_SOURCE_DMA: 97862306a36Sopenharmony_ci if (!(flags & MEDIA_LNK_FL_ENABLED)) 97962306a36Sopenharmony_ci atomic_set(&fimc->out_path, FIMC_IO_NONE); 98062306a36Sopenharmony_ci else 98162306a36Sopenharmony_ci atomic_set(&fimc->out_path, FIMC_IO_DMA); 98262306a36Sopenharmony_ci break; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci case FLITE_SD_PAD_SOURCE_ISP: 98562306a36Sopenharmony_ci if (!(flags & MEDIA_LNK_FL_ENABLED)) 98662306a36Sopenharmony_ci atomic_set(&fimc->out_path, FIMC_IO_NONE); 98762306a36Sopenharmony_ci else 98862306a36Sopenharmony_ci atomic_set(&fimc->out_path, FIMC_IO_ISP); 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci default: 99262306a36Sopenharmony_ci v4l2_err(sd, "Invalid pad index\n"); 99362306a36Sopenharmony_ci ret = -EINVAL; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci mb(); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci return ret; 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_cistatic const struct media_entity_operations fimc_lite_subdev_media_ops = { 100162306a36Sopenharmony_ci .link_setup = fimc_lite_link_setup, 100262306a36Sopenharmony_ci}; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, 100562306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 100662306a36Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci const struct fimc_fmt *fmt; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci fmt = fimc_lite_find_format(NULL, NULL, 0, code->index); 101162306a36Sopenharmony_ci if (!fmt) 101262306a36Sopenharmony_ci return -EINVAL; 101362306a36Sopenharmony_ci code->code = fmt->mbus_code; 101462306a36Sopenharmony_ci return 0; 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cistatic struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt( 101862306a36Sopenharmony_ci struct v4l2_subdev *sd, 101962306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, unsigned int pad) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci if (pad != FLITE_SD_PAD_SINK) 102262306a36Sopenharmony_ci pad = FLITE_SD_PAD_SOURCE_DMA; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci return v4l2_subdev_get_try_format(sd, sd_state, pad); 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, 102862306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 102962306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci struct fimc_lite *fimc = v4l2_get_subdevdata(sd); 103262306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &fmt->format; 103362306a36Sopenharmony_ci struct flite_frame *f = &fimc->inp_frame; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 103662306a36Sopenharmony_ci mf = __fimc_lite_subdev_get_try_fmt(sd, sd_state, fmt->pad); 103762306a36Sopenharmony_ci fmt->format = *mf; 103862306a36Sopenharmony_ci return 0; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci mutex_lock(&fimc->lock); 104262306a36Sopenharmony_ci mf->colorspace = f->fmt->colorspace; 104362306a36Sopenharmony_ci mf->code = f->fmt->mbus_code; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci if (fmt->pad == FLITE_SD_PAD_SINK) { 104662306a36Sopenharmony_ci /* full camera input frame size */ 104762306a36Sopenharmony_ci mf->width = f->f_width; 104862306a36Sopenharmony_ci mf->height = f->f_height; 104962306a36Sopenharmony_ci } else { 105062306a36Sopenharmony_ci /* crop size */ 105162306a36Sopenharmony_ci mf->width = f->rect.width; 105262306a36Sopenharmony_ci mf->height = f->rect.height; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 105562306a36Sopenharmony_ci return 0; 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, 105962306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 106062306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 106162306a36Sopenharmony_ci{ 106262306a36Sopenharmony_ci struct fimc_lite *fimc = v4l2_get_subdevdata(sd); 106362306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &fmt->format; 106462306a36Sopenharmony_ci struct flite_frame *sink = &fimc->inp_frame; 106562306a36Sopenharmony_ci struct flite_frame *source = &fimc->out_frame; 106662306a36Sopenharmony_ci const struct fimc_fmt *ffmt; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d\n", 106962306a36Sopenharmony_ci fmt->pad, mf->code, mf->width, mf->height); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci mutex_lock(&fimc->lock); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci if ((atomic_read(&fimc->out_path) == FIMC_IO_ISP && 107462306a36Sopenharmony_ci media_entity_is_streaming(&sd->entity)) || 107562306a36Sopenharmony_ci (atomic_read(&fimc->out_path) == FIMC_IO_DMA && 107662306a36Sopenharmony_ci vb2_is_busy(&fimc->vb_queue))) { 107762306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 107862306a36Sopenharmony_ci return -EBUSY; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci ffmt = fimc_lite_subdev_try_fmt(fimc, sd_state, fmt); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 108462306a36Sopenharmony_ci struct v4l2_mbus_framefmt *src_fmt; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci mf = __fimc_lite_subdev_get_try_fmt(sd, sd_state, fmt->pad); 108762306a36Sopenharmony_ci *mf = fmt->format; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (fmt->pad == FLITE_SD_PAD_SINK) { 109062306a36Sopenharmony_ci unsigned int pad = FLITE_SD_PAD_SOURCE_DMA; 109162306a36Sopenharmony_ci src_fmt = __fimc_lite_subdev_get_try_fmt(sd, sd_state, 109262306a36Sopenharmony_ci pad); 109362306a36Sopenharmony_ci *src_fmt = *mf; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 109762306a36Sopenharmony_ci return 0; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (fmt->pad == FLITE_SD_PAD_SINK) { 110162306a36Sopenharmony_ci sink->f_width = mf->width; 110262306a36Sopenharmony_ci sink->f_height = mf->height; 110362306a36Sopenharmony_ci sink->fmt = ffmt; 110462306a36Sopenharmony_ci /* Set sink crop rectangle */ 110562306a36Sopenharmony_ci sink->rect.width = mf->width; 110662306a36Sopenharmony_ci sink->rect.height = mf->height; 110762306a36Sopenharmony_ci sink->rect.left = 0; 110862306a36Sopenharmony_ci sink->rect.top = 0; 110962306a36Sopenharmony_ci /* Reset source format and crop rectangle */ 111062306a36Sopenharmony_ci source->rect = sink->rect; 111162306a36Sopenharmony_ci source->f_width = mf->width; 111262306a36Sopenharmony_ci source->f_height = mf->height; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 111662306a36Sopenharmony_ci return 0; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_cistatic int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, 112062306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 112162306a36Sopenharmony_ci struct v4l2_subdev_selection *sel) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci struct fimc_lite *fimc = v4l2_get_subdevdata(sd); 112462306a36Sopenharmony_ci struct flite_frame *f = &fimc->inp_frame; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if ((sel->target != V4L2_SEL_TGT_CROP && 112762306a36Sopenharmony_ci sel->target != V4L2_SEL_TGT_CROP_BOUNDS) || 112862306a36Sopenharmony_ci sel->pad != FLITE_SD_PAD_SINK) 112962306a36Sopenharmony_ci return -EINVAL; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 113262306a36Sopenharmony_ci sel->r = *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad); 113362306a36Sopenharmony_ci return 0; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci mutex_lock(&fimc->lock); 113762306a36Sopenharmony_ci if (sel->target == V4L2_SEL_TGT_CROP) { 113862306a36Sopenharmony_ci sel->r = f->rect; 113962306a36Sopenharmony_ci } else { 114062306a36Sopenharmony_ci sel->r.left = 0; 114162306a36Sopenharmony_ci sel->r.top = 0; 114262306a36Sopenharmony_ci sel->r.width = f->f_width; 114362306a36Sopenharmony_ci sel->r.height = f->f_height; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n", 114862306a36Sopenharmony_ci __func__, f->rect.left, f->rect.top, f->rect.width, 114962306a36Sopenharmony_ci f->rect.height, f->f_width, f->f_height); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci return 0; 115262306a36Sopenharmony_ci} 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, 115562306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 115662306a36Sopenharmony_ci struct v4l2_subdev_selection *sel) 115762306a36Sopenharmony_ci{ 115862306a36Sopenharmony_ci struct fimc_lite *fimc = v4l2_get_subdevdata(sd); 115962306a36Sopenharmony_ci struct flite_frame *f = &fimc->inp_frame; 116062306a36Sopenharmony_ci int ret = 0; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != FLITE_SD_PAD_SINK) 116362306a36Sopenharmony_ci return -EINVAL; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci mutex_lock(&fimc->lock); 116662306a36Sopenharmony_ci fimc_lite_try_crop(fimc, &sel->r); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { 116962306a36Sopenharmony_ci *v4l2_subdev_get_try_crop(sd, sd_state, sel->pad) = sel->r; 117062306a36Sopenharmony_ci } else { 117162306a36Sopenharmony_ci unsigned long flags; 117262306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 117362306a36Sopenharmony_ci f->rect = sel->r; 117462306a36Sopenharmony_ci /* Same crop rectangle on the source pad */ 117562306a36Sopenharmony_ci fimc->out_frame.rect = sel->r; 117662306a36Sopenharmony_ci set_bit(ST_FLITE_CONFIG, &fimc->state); 117762306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n", 118262306a36Sopenharmony_ci __func__, f->rect.left, f->rect.top, f->rect.width, 118362306a36Sopenharmony_ci f->rect.height, f->f_width, f->f_height); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci return ret; 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_cistatic int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci struct fimc_lite *fimc = v4l2_get_subdevdata(sd); 119162306a36Sopenharmony_ci unsigned long flags; 119262306a36Sopenharmony_ci int ret; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci /* 119562306a36Sopenharmony_ci * Find sensor subdev linked to FIMC-LITE directly or through 119662306a36Sopenharmony_ci * MIPI-CSIS. This is required for configuration where FIMC-LITE 119762306a36Sopenharmony_ci * is used as a subdev only and feeds data internally to FIMC-IS. 119862306a36Sopenharmony_ci * The pipeline links are protected through entity.pipe so there is no 119962306a36Sopenharmony_ci * need to take the media graph mutex here. 120062306a36Sopenharmony_ci */ 120162306a36Sopenharmony_ci fimc->sensor = fimc_find_remote_sensor(&sd->entity); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (atomic_read(&fimc->out_path) != FIMC_IO_ISP) 120462306a36Sopenharmony_ci return -ENOIOCTLCMD; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci mutex_lock(&fimc->lock); 120762306a36Sopenharmony_ci if (on) { 120862306a36Sopenharmony_ci flite_hw_reset(fimc); 120962306a36Sopenharmony_ci ret = fimc_lite_hw_init(fimc, true); 121062306a36Sopenharmony_ci if (!ret) { 121162306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 121262306a36Sopenharmony_ci flite_hw_capture_start(fimc); 121362306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci } else { 121662306a36Sopenharmony_ci set_bit(ST_FLITE_OFF, &fimc->state); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 121962306a36Sopenharmony_ci flite_hw_capture_stop(fimc); 122062306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci ret = wait_event_timeout(fimc->irq_queue, 122362306a36Sopenharmony_ci !test_bit(ST_FLITE_OFF, &fimc->state), 122462306a36Sopenharmony_ci msecs_to_jiffies(200)); 122562306a36Sopenharmony_ci if (ret == 0) 122662306a36Sopenharmony_ci v4l2_err(sd, "s_stream(0) timeout\n"); 122762306a36Sopenharmony_ci clear_bit(ST_FLITE_RUN, &fimc->state); 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 123162306a36Sopenharmony_ci return ret; 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_cistatic int fimc_lite_log_status(struct v4l2_subdev *sd) 123562306a36Sopenharmony_ci{ 123662306a36Sopenharmony_ci struct fimc_lite *fimc = v4l2_get_subdevdata(sd); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci flite_hw_dump_regs(fimc, __func__); 123962306a36Sopenharmony_ci return 0; 124062306a36Sopenharmony_ci} 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_cistatic int fimc_lite_subdev_registered(struct v4l2_subdev *sd) 124362306a36Sopenharmony_ci{ 124462306a36Sopenharmony_ci struct fimc_lite *fimc = v4l2_get_subdevdata(sd); 124562306a36Sopenharmony_ci struct vb2_queue *q = &fimc->vb_queue; 124662306a36Sopenharmony_ci struct video_device *vfd = &fimc->ve.vdev; 124762306a36Sopenharmony_ci int ret; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci memset(vfd, 0, sizeof(*vfd)); 125062306a36Sopenharmony_ci atomic_set(&fimc->out_path, FIMC_IO_DMA); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture", 125362306a36Sopenharmony_ci fimc->index); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci vfd->fops = &fimc_lite_fops; 125662306a36Sopenharmony_ci vfd->ioctl_ops = &fimc_lite_ioctl_ops; 125762306a36Sopenharmony_ci vfd->v4l2_dev = sd->v4l2_dev; 125862306a36Sopenharmony_ci vfd->minor = -1; 125962306a36Sopenharmony_ci vfd->release = video_device_release_empty; 126062306a36Sopenharmony_ci vfd->queue = q; 126162306a36Sopenharmony_ci vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; 126262306a36Sopenharmony_ci fimc->reqbufs_count = 0; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci INIT_LIST_HEAD(&fimc->pending_buf_q); 126562306a36Sopenharmony_ci INIT_LIST_HEAD(&fimc->active_buf_q); 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci memset(q, 0, sizeof(*q)); 126862306a36Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 126962306a36Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_USERPTR; 127062306a36Sopenharmony_ci q->ops = &fimc_lite_qops; 127162306a36Sopenharmony_ci q->mem_ops = &vb2_dma_contig_memops; 127262306a36Sopenharmony_ci q->buf_struct_size = sizeof(struct flite_buffer); 127362306a36Sopenharmony_ci q->drv_priv = fimc; 127462306a36Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 127562306a36Sopenharmony_ci q->lock = &fimc->lock; 127662306a36Sopenharmony_ci q->dev = &fimc->pdev->dev; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci ret = vb2_queue_init(q); 127962306a36Sopenharmony_ci if (ret < 0) 128062306a36Sopenharmony_ci return ret; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci fimc->vd_pad.flags = MEDIA_PAD_FL_SINK; 128362306a36Sopenharmony_ci ret = media_entity_pads_init(&vfd->entity, 1, &fimc->vd_pad); 128462306a36Sopenharmony_ci if (ret < 0) 128562306a36Sopenharmony_ci return ret; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci video_set_drvdata(vfd, fimc); 128862306a36Sopenharmony_ci fimc->ve.pipe = v4l2_get_subdev_hostdata(sd); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); 129162306a36Sopenharmony_ci if (ret < 0) { 129262306a36Sopenharmony_ci media_entity_cleanup(&vfd->entity); 129362306a36Sopenharmony_ci fimc->ve.pipe = NULL; 129462306a36Sopenharmony_ci return ret; 129562306a36Sopenharmony_ci } 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci v4l2_info(sd->v4l2_dev, "Registered %s as /dev/%s\n", 129862306a36Sopenharmony_ci vfd->name, video_device_node_name(vfd)); 129962306a36Sopenharmony_ci return 0; 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic void fimc_lite_subdev_unregistered(struct v4l2_subdev *sd) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci struct fimc_lite *fimc = v4l2_get_subdevdata(sd); 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci if (fimc == NULL) 130762306a36Sopenharmony_ci return; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci mutex_lock(&fimc->lock); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (video_is_registered(&fimc->ve.vdev)) { 131262306a36Sopenharmony_ci video_unregister_device(&fimc->ve.vdev); 131362306a36Sopenharmony_ci media_entity_cleanup(&fimc->ve.vdev.entity); 131462306a36Sopenharmony_ci fimc->ve.pipe = NULL; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci mutex_unlock(&fimc->lock); 131862306a36Sopenharmony_ci} 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_cistatic const struct v4l2_subdev_internal_ops fimc_lite_subdev_internal_ops = { 132162306a36Sopenharmony_ci .registered = fimc_lite_subdev_registered, 132262306a36Sopenharmony_ci .unregistered = fimc_lite_subdev_unregistered, 132362306a36Sopenharmony_ci}; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops fimc_lite_subdev_pad_ops = { 132662306a36Sopenharmony_ci .enum_mbus_code = fimc_lite_subdev_enum_mbus_code, 132762306a36Sopenharmony_ci .get_selection = fimc_lite_subdev_get_selection, 132862306a36Sopenharmony_ci .set_selection = fimc_lite_subdev_set_selection, 132962306a36Sopenharmony_ci .get_fmt = fimc_lite_subdev_get_fmt, 133062306a36Sopenharmony_ci .set_fmt = fimc_lite_subdev_set_fmt, 133162306a36Sopenharmony_ci}; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops fimc_lite_subdev_video_ops = { 133462306a36Sopenharmony_ci .s_stream = fimc_lite_subdev_s_stream, 133562306a36Sopenharmony_ci}; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops fimc_lite_core_ops = { 133862306a36Sopenharmony_ci .log_status = fimc_lite_log_status, 133962306a36Sopenharmony_ci}; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_cistatic const struct v4l2_subdev_ops fimc_lite_subdev_ops = { 134262306a36Sopenharmony_ci .core = &fimc_lite_core_ops, 134362306a36Sopenharmony_ci .video = &fimc_lite_subdev_video_ops, 134462306a36Sopenharmony_ci .pad = &fimc_lite_subdev_pad_ops, 134562306a36Sopenharmony_ci}; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_cistatic int fimc_lite_s_ctrl(struct v4l2_ctrl *ctrl) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct fimc_lite *fimc = container_of(ctrl->handler, struct fimc_lite, 135062306a36Sopenharmony_ci ctrl_handler); 135162306a36Sopenharmony_ci set_bit(ST_FLITE_CONFIG, &fimc->state); 135262306a36Sopenharmony_ci return 0; 135362306a36Sopenharmony_ci} 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops fimc_lite_ctrl_ops = { 135662306a36Sopenharmony_ci .s_ctrl = fimc_lite_s_ctrl, 135762306a36Sopenharmony_ci}; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_cistatic const struct v4l2_ctrl_config fimc_lite_ctrl = { 136062306a36Sopenharmony_ci .ops = &fimc_lite_ctrl_ops, 136162306a36Sopenharmony_ci .id = V4L2_CTRL_CLASS_USER | 0x1001, 136262306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 136362306a36Sopenharmony_ci .name = "Test Pattern 640x480", 136462306a36Sopenharmony_ci .step = 1, 136562306a36Sopenharmony_ci}; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_cistatic void fimc_lite_set_default_config(struct fimc_lite *fimc) 136862306a36Sopenharmony_ci{ 136962306a36Sopenharmony_ci struct flite_frame *sink = &fimc->inp_frame; 137062306a36Sopenharmony_ci struct flite_frame *source = &fimc->out_frame; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci sink->fmt = &fimc_lite_formats[0]; 137362306a36Sopenharmony_ci sink->f_width = FLITE_DEFAULT_WIDTH; 137462306a36Sopenharmony_ci sink->f_height = FLITE_DEFAULT_HEIGHT; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci sink->rect.width = FLITE_DEFAULT_WIDTH; 137762306a36Sopenharmony_ci sink->rect.height = FLITE_DEFAULT_HEIGHT; 137862306a36Sopenharmony_ci sink->rect.left = 0; 137962306a36Sopenharmony_ci sink->rect.top = 0; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci *source = *sink; 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci struct v4l2_ctrl_handler *handler = &fimc->ctrl_handler; 138762306a36Sopenharmony_ci struct v4l2_subdev *sd = &fimc->subdev; 138862306a36Sopenharmony_ci int ret; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci v4l2_subdev_init(sd, &fimc_lite_subdev_ops); 139162306a36Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 139262306a36Sopenharmony_ci snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 139562306a36Sopenharmony_ci fimc->subdev_pads[FLITE_SD_PAD_SOURCE_DMA].flags = MEDIA_PAD_FL_SOURCE; 139662306a36Sopenharmony_ci fimc->subdev_pads[FLITE_SD_PAD_SOURCE_ISP].flags = MEDIA_PAD_FL_SOURCE; 139762306a36Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, FLITE_SD_PADS_NUM, 139862306a36Sopenharmony_ci fimc->subdev_pads); 139962306a36Sopenharmony_ci if (ret) 140062306a36Sopenharmony_ci return ret; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci v4l2_ctrl_handler_init(handler, 1); 140362306a36Sopenharmony_ci fimc->test_pattern = v4l2_ctrl_new_custom(handler, &fimc_lite_ctrl, 140462306a36Sopenharmony_ci NULL); 140562306a36Sopenharmony_ci if (handler->error) { 140662306a36Sopenharmony_ci media_entity_cleanup(&sd->entity); 140762306a36Sopenharmony_ci return handler->error; 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci sd->ctrl_handler = handler; 141162306a36Sopenharmony_ci sd->internal_ops = &fimc_lite_subdev_internal_ops; 141262306a36Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; 141362306a36Sopenharmony_ci sd->entity.ops = &fimc_lite_subdev_media_ops; 141462306a36Sopenharmony_ci sd->owner = THIS_MODULE; 141562306a36Sopenharmony_ci v4l2_set_subdevdata(sd, fimc); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci return 0; 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic void fimc_lite_unregister_capture_subdev(struct fimc_lite *fimc) 142162306a36Sopenharmony_ci{ 142262306a36Sopenharmony_ci struct v4l2_subdev *sd = &fimc->subdev; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci v4l2_device_unregister_subdev(sd); 142562306a36Sopenharmony_ci media_entity_cleanup(&sd->entity); 142662306a36Sopenharmony_ci v4l2_ctrl_handler_free(&fimc->ctrl_handler); 142762306a36Sopenharmony_ci v4l2_set_subdevdata(sd, NULL); 142862306a36Sopenharmony_ci} 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_cistatic void fimc_lite_clk_put(struct fimc_lite *fimc) 143162306a36Sopenharmony_ci{ 143262306a36Sopenharmony_ci if (IS_ERR(fimc->clock)) 143362306a36Sopenharmony_ci return; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci clk_put(fimc->clock); 143662306a36Sopenharmony_ci fimc->clock = ERR_PTR(-EINVAL); 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_cistatic int fimc_lite_clk_get(struct fimc_lite *fimc) 144062306a36Sopenharmony_ci{ 144162306a36Sopenharmony_ci fimc->clock = clk_get(&fimc->pdev->dev, FLITE_CLK_NAME); 144262306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(fimc->clock); 144362306a36Sopenharmony_ci} 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_cistatic const struct of_device_id flite_of_match[]; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cistatic int fimc_lite_probe(struct platform_device *pdev) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci struct flite_drvdata *drv_data = NULL; 145062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 145162306a36Sopenharmony_ci const struct of_device_id *of_id; 145262306a36Sopenharmony_ci struct fimc_lite *fimc; 145362306a36Sopenharmony_ci int ret; 145462306a36Sopenharmony_ci int irq; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci if (!dev->of_node) 145762306a36Sopenharmony_ci return -ENODEV; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL); 146062306a36Sopenharmony_ci if (!fimc) 146162306a36Sopenharmony_ci return -ENOMEM; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci of_id = of_match_node(flite_of_match, dev->of_node); 146462306a36Sopenharmony_ci if (of_id) 146562306a36Sopenharmony_ci drv_data = (struct flite_drvdata *)of_id->data; 146662306a36Sopenharmony_ci fimc->index = of_alias_get_id(dev->of_node, "fimc-lite"); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (!drv_data || fimc->index >= drv_data->num_instances || 146962306a36Sopenharmony_ci fimc->index < 0) { 147062306a36Sopenharmony_ci dev_err(dev, "Wrong %pOF node alias\n", dev->of_node); 147162306a36Sopenharmony_ci return -EINVAL; 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci fimc->dd = drv_data; 147562306a36Sopenharmony_ci fimc->pdev = pdev; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci init_waitqueue_head(&fimc->irq_queue); 147862306a36Sopenharmony_ci spin_lock_init(&fimc->slock); 147962306a36Sopenharmony_ci mutex_init(&fimc->lock); 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci fimc->regs = devm_platform_ioremap_resource(pdev, 0); 148262306a36Sopenharmony_ci if (IS_ERR(fimc->regs)) 148362306a36Sopenharmony_ci return PTR_ERR(fimc->regs); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 148662306a36Sopenharmony_ci if (irq < 0) 148762306a36Sopenharmony_ci return irq; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci ret = fimc_lite_clk_get(fimc); 149062306a36Sopenharmony_ci if (ret) 149162306a36Sopenharmony_ci return ret; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, flite_irq_handler, 149462306a36Sopenharmony_ci 0, dev_name(dev), fimc); 149562306a36Sopenharmony_ci if (ret) { 149662306a36Sopenharmony_ci dev_err(dev, "Failed to install irq (%d)\n", ret); 149762306a36Sopenharmony_ci goto err_clk_put; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci /* The video node will be created within the subdev's registered() op */ 150162306a36Sopenharmony_ci ret = fimc_lite_create_capture_subdev(fimc); 150262306a36Sopenharmony_ci if (ret) 150362306a36Sopenharmony_ci goto err_clk_put; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci platform_set_drvdata(pdev, fimc); 150662306a36Sopenharmony_ci pm_runtime_enable(dev); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci if (!pm_runtime_enabled(dev)) { 150962306a36Sopenharmony_ci ret = clk_prepare_enable(fimc->clock); 151062306a36Sopenharmony_ci if (ret < 0) 151162306a36Sopenharmony_ci goto err_sd; 151262306a36Sopenharmony_ci } 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci fimc_lite_set_default_config(fimc); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci dev_dbg(dev, "FIMC-LITE.%d registered successfully\n", 151962306a36Sopenharmony_ci fimc->index); 152062306a36Sopenharmony_ci return 0; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_cierr_sd: 152362306a36Sopenharmony_ci fimc_lite_unregister_capture_subdev(fimc); 152462306a36Sopenharmony_cierr_clk_put: 152562306a36Sopenharmony_ci fimc_lite_clk_put(fimc); 152662306a36Sopenharmony_ci return ret; 152762306a36Sopenharmony_ci} 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci#ifdef CONFIG_PM 153062306a36Sopenharmony_cistatic int fimc_lite_runtime_resume(struct device *dev) 153162306a36Sopenharmony_ci{ 153262306a36Sopenharmony_ci struct fimc_lite *fimc = dev_get_drvdata(dev); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci clk_prepare_enable(fimc->clock); 153562306a36Sopenharmony_ci return 0; 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_cistatic int fimc_lite_runtime_suspend(struct device *dev) 153962306a36Sopenharmony_ci{ 154062306a36Sopenharmony_ci struct fimc_lite *fimc = dev_get_drvdata(dev); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci clk_disable_unprepare(fimc->clock); 154362306a36Sopenharmony_ci return 0; 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci#endif 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 154862306a36Sopenharmony_cistatic int fimc_lite_resume(struct device *dev) 154962306a36Sopenharmony_ci{ 155062306a36Sopenharmony_ci struct fimc_lite *fimc = dev_get_drvdata(dev); 155162306a36Sopenharmony_ci struct flite_buffer *buf; 155262306a36Sopenharmony_ci unsigned long flags; 155362306a36Sopenharmony_ci int i; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci spin_lock_irqsave(&fimc->slock, flags); 155662306a36Sopenharmony_ci if (!test_and_clear_bit(ST_LPM, &fimc->state) || 155762306a36Sopenharmony_ci !test_bit(ST_FLITE_IN_USE, &fimc->state)) { 155862306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 155962306a36Sopenharmony_ci return 0; 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci flite_hw_reset(fimc); 156262306a36Sopenharmony_ci spin_unlock_irqrestore(&fimc->slock, flags); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci if (!test_and_clear_bit(ST_FLITE_SUSPENDED, &fimc->state)) 156562306a36Sopenharmony_ci return 0; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci INIT_LIST_HEAD(&fimc->active_buf_q); 156862306a36Sopenharmony_ci fimc_pipeline_call(&fimc->ve, open, 156962306a36Sopenharmony_ci &fimc->ve.vdev.entity, false); 157062306a36Sopenharmony_ci fimc_lite_hw_init(fimc, atomic_read(&fimc->out_path) == FIMC_IO_ISP); 157162306a36Sopenharmony_ci clear_bit(ST_FLITE_SUSPENDED, &fimc->state); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci for (i = 0; i < fimc->reqbufs_count; i++) { 157462306a36Sopenharmony_ci if (list_empty(&fimc->pending_buf_q)) 157562306a36Sopenharmony_ci break; 157662306a36Sopenharmony_ci buf = fimc_lite_pending_queue_pop(fimc); 157762306a36Sopenharmony_ci buffer_queue(&buf->vb.vb2_buf); 157862306a36Sopenharmony_ci } 157962306a36Sopenharmony_ci return 0; 158062306a36Sopenharmony_ci} 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_cistatic int fimc_lite_suspend(struct device *dev) 158362306a36Sopenharmony_ci{ 158462306a36Sopenharmony_ci struct fimc_lite *fimc = dev_get_drvdata(dev); 158562306a36Sopenharmony_ci bool suspend = test_bit(ST_FLITE_IN_USE, &fimc->state); 158662306a36Sopenharmony_ci int ret; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci if (test_and_set_bit(ST_LPM, &fimc->state)) 158962306a36Sopenharmony_ci return 0; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci ret = fimc_lite_stop_capture(fimc, suspend); 159262306a36Sopenharmony_ci if (ret < 0 || !fimc_lite_active(fimc)) 159362306a36Sopenharmony_ci return ret; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci return fimc_pipeline_call(&fimc->ve, close); 159662306a36Sopenharmony_ci} 159762306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_cistatic void fimc_lite_remove(struct platform_device *pdev) 160062306a36Sopenharmony_ci{ 160162306a36Sopenharmony_ci struct fimc_lite *fimc = platform_get_drvdata(pdev); 160262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (!pm_runtime_enabled(dev)) 160562306a36Sopenharmony_ci clk_disable_unprepare(fimc->clock); 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci pm_runtime_disable(dev); 160862306a36Sopenharmony_ci pm_runtime_set_suspended(dev); 160962306a36Sopenharmony_ci fimc_lite_unregister_capture_subdev(fimc); 161062306a36Sopenharmony_ci vb2_dma_contig_clear_max_seg_size(dev); 161162306a36Sopenharmony_ci fimc_lite_clk_put(fimc); 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci dev_info(dev, "Driver unloaded\n"); 161462306a36Sopenharmony_ci} 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_cistatic const struct dev_pm_ops fimc_lite_pm_ops = { 161762306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume) 161862306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume, 161962306a36Sopenharmony_ci NULL) 162062306a36Sopenharmony_ci}; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci/* EXYNOS4212, EXYNOS4412 */ 162362306a36Sopenharmony_cistatic struct flite_drvdata fimc_lite_drvdata_exynos4 = { 162462306a36Sopenharmony_ci .max_width = 8192, 162562306a36Sopenharmony_ci .max_height = 8192, 162662306a36Sopenharmony_ci .out_width_align = 8, 162762306a36Sopenharmony_ci .win_hor_offs_align = 2, 162862306a36Sopenharmony_ci .out_hor_offs_align = 8, 162962306a36Sopenharmony_ci .max_dma_bufs = 1, 163062306a36Sopenharmony_ci .num_instances = 2, 163162306a36Sopenharmony_ci}; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci/* EXYNOS5250 */ 163462306a36Sopenharmony_cistatic struct flite_drvdata fimc_lite_drvdata_exynos5 = { 163562306a36Sopenharmony_ci .max_width = 8192, 163662306a36Sopenharmony_ci .max_height = 8192, 163762306a36Sopenharmony_ci .out_width_align = 8, 163862306a36Sopenharmony_ci .win_hor_offs_align = 2, 163962306a36Sopenharmony_ci .out_hor_offs_align = 8, 164062306a36Sopenharmony_ci .max_dma_bufs = 32, 164162306a36Sopenharmony_ci .num_instances = 3, 164262306a36Sopenharmony_ci}; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_cistatic const struct of_device_id flite_of_match[] = { 164562306a36Sopenharmony_ci { 164662306a36Sopenharmony_ci .compatible = "samsung,exynos4212-fimc-lite", 164762306a36Sopenharmony_ci .data = &fimc_lite_drvdata_exynos4, 164862306a36Sopenharmony_ci }, 164962306a36Sopenharmony_ci { 165062306a36Sopenharmony_ci .compatible = "samsung,exynos5250-fimc-lite", 165162306a36Sopenharmony_ci .data = &fimc_lite_drvdata_exynos5, 165262306a36Sopenharmony_ci }, 165362306a36Sopenharmony_ci { /* sentinel */ }, 165462306a36Sopenharmony_ci}; 165562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, flite_of_match); 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_cistatic struct platform_driver fimc_lite_driver = { 165862306a36Sopenharmony_ci .probe = fimc_lite_probe, 165962306a36Sopenharmony_ci .remove_new = fimc_lite_remove, 166062306a36Sopenharmony_ci .driver = { 166162306a36Sopenharmony_ci .of_match_table = flite_of_match, 166262306a36Sopenharmony_ci .name = FIMC_LITE_DRV_NAME, 166362306a36Sopenharmony_ci .pm = &fimc_lite_pm_ops, 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci}; 166662306a36Sopenharmony_cimodule_platform_driver(fimc_lite_driver); 166762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 166862306a36Sopenharmony_ciMODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME); 1669