18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for STM32 Digital Camera Memory Interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2017 68c2ecf20Sopenharmony_ci * Authors: Yannick Fertre <yannick.fertre@st.com> 78c2ecf20Sopenharmony_ci * Hugues Fruchet <hugues.fruchet@st.com> 88c2ecf20Sopenharmony_ci * for STMicroelectronics. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This driver is based on atmel_isi.c 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/completion.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/of.h> 238c2ecf20Sopenharmony_ci#include <linux/of_device.h> 248c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 258c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 268c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 278c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 288c2ecf20Sopenharmony_ci#include <linux/reset.h> 298c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 328c2ecf20Sopenharmony_ci#include <media/v4l2-dev.h> 338c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 348c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 358c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 368c2ecf20Sopenharmony_ci#include <media/v4l2-image-sizes.h> 378c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 388c2ecf20Sopenharmony_ci#include <media/v4l2-rect.h> 398c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define DRV_NAME "stm32-dcmi" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Registers offset for DCMI */ 448c2ecf20Sopenharmony_ci#define DCMI_CR 0x00 /* Control Register */ 458c2ecf20Sopenharmony_ci#define DCMI_SR 0x04 /* Status Register */ 468c2ecf20Sopenharmony_ci#define DCMI_RIS 0x08 /* Raw Interrupt Status register */ 478c2ecf20Sopenharmony_ci#define DCMI_IER 0x0C /* Interrupt Enable Register */ 488c2ecf20Sopenharmony_ci#define DCMI_MIS 0x10 /* Masked Interrupt Status register */ 498c2ecf20Sopenharmony_ci#define DCMI_ICR 0x14 /* Interrupt Clear Register */ 508c2ecf20Sopenharmony_ci#define DCMI_ESCR 0x18 /* Embedded Synchronization Code Register */ 518c2ecf20Sopenharmony_ci#define DCMI_ESUR 0x1C /* Embedded Synchronization Unmask Register */ 528c2ecf20Sopenharmony_ci#define DCMI_CWSTRT 0x20 /* Crop Window STaRT */ 538c2ecf20Sopenharmony_ci#define DCMI_CWSIZE 0x24 /* Crop Window SIZE */ 548c2ecf20Sopenharmony_ci#define DCMI_DR 0x28 /* Data Register */ 558c2ecf20Sopenharmony_ci#define DCMI_IDR 0x2C /* IDentifier Register */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* Bits definition for control register (DCMI_CR) */ 588c2ecf20Sopenharmony_ci#define CR_CAPTURE BIT(0) 598c2ecf20Sopenharmony_ci#define CR_CM BIT(1) 608c2ecf20Sopenharmony_ci#define CR_CROP BIT(2) 618c2ecf20Sopenharmony_ci#define CR_JPEG BIT(3) 628c2ecf20Sopenharmony_ci#define CR_ESS BIT(4) 638c2ecf20Sopenharmony_ci#define CR_PCKPOL BIT(5) 648c2ecf20Sopenharmony_ci#define CR_HSPOL BIT(6) 658c2ecf20Sopenharmony_ci#define CR_VSPOL BIT(7) 668c2ecf20Sopenharmony_ci#define CR_FCRC_0 BIT(8) 678c2ecf20Sopenharmony_ci#define CR_FCRC_1 BIT(9) 688c2ecf20Sopenharmony_ci#define CR_EDM_0 BIT(10) 698c2ecf20Sopenharmony_ci#define CR_EDM_1 BIT(11) 708c2ecf20Sopenharmony_ci#define CR_ENABLE BIT(14) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* Bits definition for status register (DCMI_SR) */ 738c2ecf20Sopenharmony_ci#define SR_HSYNC BIT(0) 748c2ecf20Sopenharmony_ci#define SR_VSYNC BIT(1) 758c2ecf20Sopenharmony_ci#define SR_FNE BIT(2) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* 788c2ecf20Sopenharmony_ci * Bits definition for interrupt registers 798c2ecf20Sopenharmony_ci * (DCMI_RIS, DCMI_IER, DCMI_MIS, DCMI_ICR) 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci#define IT_FRAME BIT(0) 828c2ecf20Sopenharmony_ci#define IT_OVR BIT(1) 838c2ecf20Sopenharmony_ci#define IT_ERR BIT(2) 848c2ecf20Sopenharmony_ci#define IT_VSYNC BIT(3) 858c2ecf20Sopenharmony_ci#define IT_LINE BIT(4) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cienum state { 888c2ecf20Sopenharmony_ci STOPPED = 0, 898c2ecf20Sopenharmony_ci WAIT_FOR_BUFFER, 908c2ecf20Sopenharmony_ci RUNNING, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define MIN_WIDTH 16U 948c2ecf20Sopenharmony_ci#define MAX_WIDTH 2592U 958c2ecf20Sopenharmony_ci#define MIN_HEIGHT 16U 968c2ecf20Sopenharmony_ci#define MAX_HEIGHT 2592U 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define TIMEOUT_MS 1000 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define OVERRUN_ERROR_THRESHOLD 3 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct dcmi_graph_entity { 1038c2ecf20Sopenharmony_ci struct v4l2_async_subdev asd; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci struct device_node *remote_node; 1068c2ecf20Sopenharmony_ci struct v4l2_subdev *source; 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistruct dcmi_format { 1108c2ecf20Sopenharmony_ci u32 fourcc; 1118c2ecf20Sopenharmony_ci u32 mbus_code; 1128c2ecf20Sopenharmony_ci u8 bpp; 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistruct dcmi_framesize { 1168c2ecf20Sopenharmony_ci u32 width; 1178c2ecf20Sopenharmony_ci u32 height; 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistruct dcmi_buf { 1218c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer vb; 1228c2ecf20Sopenharmony_ci bool prepared; 1238c2ecf20Sopenharmony_ci dma_addr_t paddr; 1248c2ecf20Sopenharmony_ci size_t size; 1258c2ecf20Sopenharmony_ci struct list_head list; 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistruct stm32_dcmi { 1298c2ecf20Sopenharmony_ci /* Protects the access of variables shared within the interrupt */ 1308c2ecf20Sopenharmony_ci spinlock_t irqlock; 1318c2ecf20Sopenharmony_ci struct device *dev; 1328c2ecf20Sopenharmony_ci void __iomem *regs; 1338c2ecf20Sopenharmony_ci struct resource *res; 1348c2ecf20Sopenharmony_ci struct reset_control *rstc; 1358c2ecf20Sopenharmony_ci int sequence; 1368c2ecf20Sopenharmony_ci struct list_head buffers; 1378c2ecf20Sopenharmony_ci struct dcmi_buf *active; 1388c2ecf20Sopenharmony_ci int irq; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci struct v4l2_device v4l2_dev; 1418c2ecf20Sopenharmony_ci struct video_device *vdev; 1428c2ecf20Sopenharmony_ci struct v4l2_async_notifier notifier; 1438c2ecf20Sopenharmony_ci struct dcmi_graph_entity entity; 1448c2ecf20Sopenharmony_ci struct v4l2_format fmt; 1458c2ecf20Sopenharmony_ci struct v4l2_rect crop; 1468c2ecf20Sopenharmony_ci bool do_crop; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci const struct dcmi_format **sd_formats; 1498c2ecf20Sopenharmony_ci unsigned int num_of_sd_formats; 1508c2ecf20Sopenharmony_ci const struct dcmi_format *sd_format; 1518c2ecf20Sopenharmony_ci struct dcmi_framesize *sd_framesizes; 1528c2ecf20Sopenharmony_ci unsigned int num_of_sd_framesizes; 1538c2ecf20Sopenharmony_ci struct dcmi_framesize sd_framesize; 1548c2ecf20Sopenharmony_ci struct v4l2_rect sd_bounds; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Protect this data structure */ 1578c2ecf20Sopenharmony_ci struct mutex lock; 1588c2ecf20Sopenharmony_ci struct vb2_queue queue; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci struct v4l2_fwnode_bus_parallel bus; 1618c2ecf20Sopenharmony_ci struct completion complete; 1628c2ecf20Sopenharmony_ci struct clk *mclk; 1638c2ecf20Sopenharmony_ci enum state state; 1648c2ecf20Sopenharmony_ci struct dma_chan *dma_chan; 1658c2ecf20Sopenharmony_ci dma_cookie_t dma_cookie; 1668c2ecf20Sopenharmony_ci u32 misr; 1678c2ecf20Sopenharmony_ci int errors_count; 1688c2ecf20Sopenharmony_ci int overrun_count; 1698c2ecf20Sopenharmony_ci int buffers_count; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Ensure DMA operations atomicity */ 1728c2ecf20Sopenharmony_ci struct mutex dma_lock; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci struct media_device mdev; 1758c2ecf20Sopenharmony_ci struct media_pad vid_cap_pad; 1768c2ecf20Sopenharmony_ci struct media_pipeline pipeline; 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic inline struct stm32_dcmi *notifier_to_dcmi(struct v4l2_async_notifier *n) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci return container_of(n, struct stm32_dcmi, notifier); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic inline u32 reg_read(void __iomem *base, u32 reg) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci return readl_relaxed(base + reg); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic inline void reg_write(void __iomem *base, u32 reg, u32 val) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci writel_relaxed(val, base + reg); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic inline void reg_set(void __iomem *base, u32 reg, u32 mask) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci reg_write(base, reg, reg_read(base, reg) | mask); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic inline void reg_clear(void __iomem *base, u32 reg, u32 mask) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci reg_write(base, reg, reg_read(base, reg) & ~mask); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int dcmi_start_capture(struct stm32_dcmi *dcmi, struct dcmi_buf *buf); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void dcmi_buffer_done(struct stm32_dcmi *dcmi, 2078c2ecf20Sopenharmony_ci struct dcmi_buf *buf, 2088c2ecf20Sopenharmony_ci size_t bytesused, 2098c2ecf20Sopenharmony_ci int err) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (!buf) 2148c2ecf20Sopenharmony_ci return; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci list_del_init(&buf->list); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci vbuf = &buf->vb; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci vbuf->sequence = dcmi->sequence++; 2218c2ecf20Sopenharmony_ci vbuf->field = V4L2_FIELD_NONE; 2228c2ecf20Sopenharmony_ci vbuf->vb2_buf.timestamp = ktime_get_ns(); 2238c2ecf20Sopenharmony_ci vb2_set_plane_payload(&vbuf->vb2_buf, 0, bytesused); 2248c2ecf20Sopenharmony_ci vb2_buffer_done(&vbuf->vb2_buf, 2258c2ecf20Sopenharmony_ci err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); 2268c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "buffer[%d] done seq=%d, bytesused=%zu\n", 2278c2ecf20Sopenharmony_ci vbuf->vb2_buf.index, vbuf->sequence, bytesused); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci dcmi->buffers_count++; 2308c2ecf20Sopenharmony_ci dcmi->active = NULL; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int dcmi_restart_capture(struct stm32_dcmi *dcmi) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct dcmi_buf *buf; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci spin_lock_irq(&dcmi->irqlock); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (dcmi->state != RUNNING) { 2408c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 2418c2ecf20Sopenharmony_ci return -EINVAL; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Restart a new DMA transfer with next buffer */ 2458c2ecf20Sopenharmony_ci if (list_empty(&dcmi->buffers)) { 2468c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Capture restart is deferred to next buffer queueing\n"); 2478c2ecf20Sopenharmony_ci dcmi->state = WAIT_FOR_BUFFER; 2488c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci buf = list_entry(dcmi->buffers.next, struct dcmi_buf, list); 2528c2ecf20Sopenharmony_ci dcmi->active = buf; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return dcmi_start_capture(dcmi, buf); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void dcmi_dma_callback(void *param) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param; 2628c2ecf20Sopenharmony_ci struct dma_tx_state state; 2638c2ecf20Sopenharmony_ci enum dma_status status; 2648c2ecf20Sopenharmony_ci struct dcmi_buf *buf = dcmi->active; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci spin_lock_irq(&dcmi->irqlock); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Check DMA status */ 2698c2ecf20Sopenharmony_ci status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci switch (status) { 2728c2ecf20Sopenharmony_ci case DMA_IN_PROGRESS: 2738c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "%s: Received DMA_IN_PROGRESS\n", __func__); 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case DMA_PAUSED: 2768c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: Received DMA_PAUSED\n", __func__); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci case DMA_ERROR: 2798c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: Received DMA_ERROR\n", __func__); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* Return buffer to V4L2 in error state */ 2828c2ecf20Sopenharmony_ci dcmi_buffer_done(dcmi, buf, 0, -EIO); 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case DMA_COMPLETE: 2858c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* Return buffer to V4L2 */ 2888c2ecf20Sopenharmony_ci dcmi_buffer_done(dcmi, buf, buf->size, 0); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Restart capture */ 2938c2ecf20Sopenharmony_ci if (dcmi_restart_capture(dcmi)) 2948c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n", 2958c2ecf20Sopenharmony_ci __func__); 2968c2ecf20Sopenharmony_ci return; 2978c2ecf20Sopenharmony_ci default: 2988c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: Received unknown status\n", __func__); 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int dcmi_start_dma(struct stm32_dcmi *dcmi, 3068c2ecf20Sopenharmony_ci struct dcmi_buf *buf) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc = NULL; 3098c2ecf20Sopenharmony_ci struct dma_slave_config config; 3108c2ecf20Sopenharmony_ci int ret; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci memset(&config, 0, sizeof(config)); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci config.src_addr = (dma_addr_t)dcmi->res->start + DCMI_DR; 3158c2ecf20Sopenharmony_ci config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 3168c2ecf20Sopenharmony_ci config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 3178c2ecf20Sopenharmony_ci config.dst_maxburst = 4; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Configure DMA channel */ 3208c2ecf20Sopenharmony_ci ret = dmaengine_slave_config(dcmi->dma_chan, &config); 3218c2ecf20Sopenharmony_ci if (ret < 0) { 3228c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: DMA channel config failed (%d)\n", 3238c2ecf20Sopenharmony_ci __func__, ret); 3248c2ecf20Sopenharmony_ci return ret; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * Avoid call of dmaengine_terminate_all() between 3298c2ecf20Sopenharmony_ci * dmaengine_prep_slave_single() and dmaengine_submit() 3308c2ecf20Sopenharmony_ci * by locking the whole DMA submission sequence 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci mutex_lock(&dcmi->dma_lock); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* Prepare a DMA transaction */ 3358c2ecf20Sopenharmony_ci desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr, 3368c2ecf20Sopenharmony_ci buf->size, 3378c2ecf20Sopenharmony_ci DMA_DEV_TO_MEM, 3388c2ecf20Sopenharmony_ci DMA_PREP_INTERRUPT); 3398c2ecf20Sopenharmony_ci if (!desc) { 3408c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer phy=%pad size=%zu\n", 3418c2ecf20Sopenharmony_ci __func__, &buf->paddr, buf->size); 3428c2ecf20Sopenharmony_ci mutex_unlock(&dcmi->dma_lock); 3438c2ecf20Sopenharmony_ci return -EINVAL; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Set completion callback routine for notification */ 3478c2ecf20Sopenharmony_ci desc->callback = dcmi_dma_callback; 3488c2ecf20Sopenharmony_ci desc->callback_param = dcmi; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Push current DMA transaction in the pending queue */ 3518c2ecf20Sopenharmony_ci dcmi->dma_cookie = dmaengine_submit(desc); 3528c2ecf20Sopenharmony_ci if (dma_submit_error(dcmi->dma_cookie)) { 3538c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: DMA submission failed\n", __func__); 3548c2ecf20Sopenharmony_ci mutex_unlock(&dcmi->dma_lock); 3558c2ecf20Sopenharmony_ci return -ENXIO; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci mutex_unlock(&dcmi->dma_lock); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci dma_async_issue_pending(dcmi->dma_chan); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic int dcmi_start_capture(struct stm32_dcmi *dcmi, struct dcmi_buf *buf) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci int ret; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (!buf) 3708c2ecf20Sopenharmony_ci return -EINVAL; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ret = dcmi_start_dma(dcmi, buf); 3738c2ecf20Sopenharmony_ci if (ret) { 3748c2ecf20Sopenharmony_ci dcmi->errors_count++; 3758c2ecf20Sopenharmony_ci return ret; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Enable capture */ 3798c2ecf20Sopenharmony_ci reg_set(dcmi->regs, DCMI_CR, CR_CAPTURE); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic void dcmi_set_crop(struct stm32_dcmi *dcmi) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci u32 size, start; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* Crop resolution */ 3898c2ecf20Sopenharmony_ci size = ((dcmi->crop.height - 1) << 16) | 3908c2ecf20Sopenharmony_ci ((dcmi->crop.width << 1) - 1); 3918c2ecf20Sopenharmony_ci reg_write(dcmi->regs, DCMI_CWSIZE, size); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* Crop start point */ 3948c2ecf20Sopenharmony_ci start = ((dcmi->crop.top) << 16) | 3958c2ecf20Sopenharmony_ci ((dcmi->crop.left << 1)); 3968c2ecf20Sopenharmony_ci reg_write(dcmi->regs, DCMI_CWSTRT, start); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Cropping to %ux%u@%u:%u\n", 3998c2ecf20Sopenharmony_ci dcmi->crop.width, dcmi->crop.height, 4008c2ecf20Sopenharmony_ci dcmi->crop.left, dcmi->crop.top); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* Enable crop */ 4038c2ecf20Sopenharmony_ci reg_set(dcmi->regs, DCMI_CR, CR_CROP); 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic void dcmi_process_jpeg(struct stm32_dcmi *dcmi) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct dma_tx_state state; 4098c2ecf20Sopenharmony_ci enum dma_status status; 4108c2ecf20Sopenharmony_ci struct dcmi_buf *buf = dcmi->active; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (!buf) 4138c2ecf20Sopenharmony_ci return; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* 4168c2ecf20Sopenharmony_ci * Because of variable JPEG buffer size sent by sensor, 4178c2ecf20Sopenharmony_ci * DMA transfer never completes due to transfer size never reached. 4188c2ecf20Sopenharmony_ci * In order to ensure that all the JPEG data are transferred 4198c2ecf20Sopenharmony_ci * in active buffer memory, DMA is drained. 4208c2ecf20Sopenharmony_ci * Then DMA tx status gives the amount of data transferred 4218c2ecf20Sopenharmony_ci * to memory, which is then returned to V4L2 through the active 4228c2ecf20Sopenharmony_ci * buffer payload. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* Drain DMA */ 4268c2ecf20Sopenharmony_ci dmaengine_synchronize(dcmi->dma_chan); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* Get DMA residue to get JPEG size */ 4298c2ecf20Sopenharmony_ci status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state); 4308c2ecf20Sopenharmony_ci if (status != DMA_ERROR && state.residue < buf->size) { 4318c2ecf20Sopenharmony_ci /* Return JPEG buffer to V4L2 with received JPEG buffer size */ 4328c2ecf20Sopenharmony_ci dcmi_buffer_done(dcmi, buf, buf->size - state.residue, 0); 4338c2ecf20Sopenharmony_ci } else { 4348c2ecf20Sopenharmony_ci dcmi->errors_count++; 4358c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: Cannot get JPEG size from DMA\n", 4368c2ecf20Sopenharmony_ci __func__); 4378c2ecf20Sopenharmony_ci /* Return JPEG buffer to V4L2 in ERROR state */ 4388c2ecf20Sopenharmony_ci dcmi_buffer_done(dcmi, buf, 0, -EIO); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Abort DMA operation */ 4428c2ecf20Sopenharmony_ci dmaengine_terminate_all(dcmi->dma_chan); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Restart capture */ 4458c2ecf20Sopenharmony_ci if (dcmi_restart_capture(dcmi)) 4468c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: Cannot restart capture on JPEG received\n", 4478c2ecf20Sopenharmony_ci __func__); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic irqreturn_t dcmi_irq_thread(int irq, void *arg) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = arg; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci spin_lock_irq(&dcmi->irqlock); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (dcmi->misr & IT_OVR) { 4578c2ecf20Sopenharmony_ci dcmi->overrun_count++; 4588c2ecf20Sopenharmony_ci if (dcmi->overrun_count > OVERRUN_ERROR_THRESHOLD) 4598c2ecf20Sopenharmony_ci dcmi->errors_count++; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci if (dcmi->misr & IT_ERR) 4628c2ecf20Sopenharmony_ci dcmi->errors_count++; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG && 4658c2ecf20Sopenharmony_ci dcmi->misr & IT_FRAME) { 4668c2ecf20Sopenharmony_ci /* JPEG received */ 4678c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 4688c2ecf20Sopenharmony_ci dcmi_process_jpeg(dcmi); 4698c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 4738c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic irqreturn_t dcmi_irq_callback(int irq, void *arg) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = arg; 4798c2ecf20Sopenharmony_ci unsigned long flags; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci spin_lock_irqsave(&dcmi->irqlock, flags); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci dcmi->misr = reg_read(dcmi->regs, DCMI_MIS); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Clear interrupt */ 4868c2ecf20Sopenharmony_ci reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dcmi->irqlock, flags); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int dcmi_queue_setup(struct vb2_queue *vq, 4948c2ecf20Sopenharmony_ci unsigned int *nbuffers, 4958c2ecf20Sopenharmony_ci unsigned int *nplanes, 4968c2ecf20Sopenharmony_ci unsigned int sizes[], 4978c2ecf20Sopenharmony_ci struct device *alloc_devs[]) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); 5008c2ecf20Sopenharmony_ci unsigned int size; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci size = dcmi->fmt.fmt.pix.sizeimage; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* Make sure the image size is large enough */ 5058c2ecf20Sopenharmony_ci if (*nplanes) 5068c2ecf20Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci *nplanes = 1; 5098c2ecf20Sopenharmony_ci sizes[0] = size; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Setup queue, count=%d, size=%d\n", 5128c2ecf20Sopenharmony_ci *nbuffers, size); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic int dcmi_buf_init(struct vb2_buffer *vb) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 5208c2ecf20Sopenharmony_ci struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&buf->list); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return 0; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic int dcmi_buf_prepare(struct vb2_buffer *vb) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue); 5308c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 5318c2ecf20Sopenharmony_ci struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb); 5328c2ecf20Sopenharmony_ci unsigned long size; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci size = dcmi->fmt.fmt.pix.sizeimage; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 5378c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s data will not fit into plane (%lu < %lu)\n", 5388c2ecf20Sopenharmony_ci __func__, vb2_plane_size(vb, 0), size); 5398c2ecf20Sopenharmony_ci return -EINVAL; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (!buf->prepared) { 5458c2ecf20Sopenharmony_ci /* Get memory addresses */ 5468c2ecf20Sopenharmony_ci buf->paddr = 5478c2ecf20Sopenharmony_ci vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); 5488c2ecf20Sopenharmony_ci buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0); 5498c2ecf20Sopenharmony_ci buf->prepared = true; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "buffer[%d] phy=%pad size=%zu\n", 5548c2ecf20Sopenharmony_ci vb->index, &buf->paddr, buf->size); 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci return 0; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic void dcmi_buf_queue(struct vb2_buffer *vb) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = vb2_get_drv_priv(vb->vb2_queue); 5638c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 5648c2ecf20Sopenharmony_ci struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci spin_lock_irq(&dcmi->irqlock); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* Enqueue to video buffers list */ 5698c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &dcmi->buffers); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (dcmi->state == WAIT_FOR_BUFFER) { 5728c2ecf20Sopenharmony_ci dcmi->state = RUNNING; 5738c2ecf20Sopenharmony_ci dcmi->active = buf; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n", 5768c2ecf20Sopenharmony_ci buf->vb.vb2_buf.index); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 5798c2ecf20Sopenharmony_ci if (dcmi_start_capture(dcmi, buf)) 5808c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n", 5818c2ecf20Sopenharmony_ci __func__); 5828c2ecf20Sopenharmony_ci return; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic struct media_entity *dcmi_find_source(struct stm32_dcmi *dcmi) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct media_entity *entity = &dcmi->vdev->entity; 5918c2ecf20Sopenharmony_ci struct media_pad *pad; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* Walk searching for entity having no sink */ 5948c2ecf20Sopenharmony_ci while (1) { 5958c2ecf20Sopenharmony_ci pad = &entity->pads[0]; 5968c2ecf20Sopenharmony_ci if (!(pad->flags & MEDIA_PAD_FL_SINK)) 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci pad = media_entity_remote_pad(pad); 6008c2ecf20Sopenharmony_ci if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 6018c2ecf20Sopenharmony_ci break; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci entity = pad->entity; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return entity; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi, 6108c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *pad_cfg, 6118c2ecf20Sopenharmony_ci struct v4l2_subdev_format *format) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct media_entity *entity = &dcmi->entity.source->entity; 6148c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev; 6158c2ecf20Sopenharmony_ci struct media_pad *sink_pad = NULL; 6168c2ecf20Sopenharmony_ci struct media_pad *src_pad = NULL; 6178c2ecf20Sopenharmony_ci struct media_pad *pad = NULL; 6188c2ecf20Sopenharmony_ci struct v4l2_subdev_format fmt = *format; 6198c2ecf20Sopenharmony_ci bool found = false; 6208c2ecf20Sopenharmony_ci int ret; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* 6238c2ecf20Sopenharmony_ci * Starting from sensor subdevice, walk within 6248c2ecf20Sopenharmony_ci * pipeline and set format on each subdevice 6258c2ecf20Sopenharmony_ci */ 6268c2ecf20Sopenharmony_ci while (1) { 6278c2ecf20Sopenharmony_ci unsigned int i; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* Search if current entity has a source pad */ 6308c2ecf20Sopenharmony_ci for (i = 0; i < entity->num_pads; i++) { 6318c2ecf20Sopenharmony_ci pad = &entity->pads[i]; 6328c2ecf20Sopenharmony_ci if (pad->flags & MEDIA_PAD_FL_SOURCE) { 6338c2ecf20Sopenharmony_ci src_pad = pad; 6348c2ecf20Sopenharmony_ci found = true; 6358c2ecf20Sopenharmony_ci break; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci if (!found) 6398c2ecf20Sopenharmony_ci break; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci subdev = media_entity_to_v4l2_subdev(entity); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* Propagate format on sink pad if any, otherwise source pad */ 6448c2ecf20Sopenharmony_ci if (sink_pad) 6458c2ecf20Sopenharmony_ci pad = sink_pad; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "\"%s\":%d pad format set to 0x%x %ux%u\n", 6488c2ecf20Sopenharmony_ci subdev->name, pad->index, format->format.code, 6498c2ecf20Sopenharmony_ci format->format.width, format->format.height); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci fmt.pad = pad->index; 6528c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, set_fmt, pad_cfg, &fmt); 6538c2ecf20Sopenharmony_ci if (ret < 0) { 6548c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: Failed to set format 0x%x %ux%u on \"%s\":%d pad (%d)\n", 6558c2ecf20Sopenharmony_ci __func__, format->format.code, 6568c2ecf20Sopenharmony_ci format->format.width, format->format.height, 6578c2ecf20Sopenharmony_ci subdev->name, pad->index, ret); 6588c2ecf20Sopenharmony_ci return ret; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (fmt.format.code != format->format.code || 6628c2ecf20Sopenharmony_ci fmt.format.width != format->format.width || 6638c2ecf20Sopenharmony_ci fmt.format.height != format->format.height) { 6648c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "\"%s\":%d pad format has been changed to 0x%x %ux%u\n", 6658c2ecf20Sopenharmony_ci subdev->name, pad->index, fmt.format.code, 6668c2ecf20Sopenharmony_ci fmt.format.width, fmt.format.height); 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* Walk to next entity */ 6708c2ecf20Sopenharmony_ci sink_pad = media_entity_remote_pad(src_pad); 6718c2ecf20Sopenharmony_ci if (!sink_pad || !is_media_entity_v4l2_subdev(sink_pad->entity)) 6728c2ecf20Sopenharmony_ci break; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci entity = sink_pad->entity; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci *format = fmt; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic int dcmi_pipeline_s_stream(struct stm32_dcmi *dcmi, int state) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci struct media_entity *entity = &dcmi->vdev->entity; 6848c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev; 6858c2ecf20Sopenharmony_ci struct media_pad *pad; 6868c2ecf20Sopenharmony_ci int ret; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* Start/stop all entities within pipeline */ 6898c2ecf20Sopenharmony_ci while (1) { 6908c2ecf20Sopenharmony_ci pad = &entity->pads[0]; 6918c2ecf20Sopenharmony_ci if (!(pad->flags & MEDIA_PAD_FL_SINK)) 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci pad = media_entity_remote_pad(pad); 6958c2ecf20Sopenharmony_ci if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 6968c2ecf20Sopenharmony_ci break; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci entity = pad->entity; 6998c2ecf20Sopenharmony_ci subdev = media_entity_to_v4l2_subdev(entity); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, video, s_stream, state); 7028c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) { 7038c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: \"%s\" failed to %s streaming (%d)\n", 7048c2ecf20Sopenharmony_ci __func__, subdev->name, 7058c2ecf20Sopenharmony_ci state ? "start" : "stop", ret); 7068c2ecf20Sopenharmony_ci return ret; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "\"%s\" is %s\n", 7108c2ecf20Sopenharmony_ci subdev->name, state ? "started" : "stopped"); 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci return 0; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int dcmi_pipeline_start(struct stm32_dcmi *dcmi) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci return dcmi_pipeline_s_stream(dcmi, 1); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic void dcmi_pipeline_stop(struct stm32_dcmi *dcmi) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci dcmi_pipeline_s_stream(dcmi, 0); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); 7298c2ecf20Sopenharmony_ci struct dcmi_buf *buf, *node; 7308c2ecf20Sopenharmony_ci u32 val = 0; 7318c2ecf20Sopenharmony_ci int ret; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dcmi->dev); 7348c2ecf20Sopenharmony_ci if (ret < 0) { 7358c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: Failed to start streaming, cannot get sync (%d)\n", 7368c2ecf20Sopenharmony_ci __func__, ret); 7378c2ecf20Sopenharmony_ci goto err_pm_put; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci ret = media_pipeline_start(&dcmi->vdev->entity, &dcmi->pipeline); 7418c2ecf20Sopenharmony_ci if (ret < 0) { 7428c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: Failed to start streaming, media pipeline start error (%d)\n", 7438c2ecf20Sopenharmony_ci __func__, ret); 7448c2ecf20Sopenharmony_ci goto err_pm_put; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci ret = dcmi_pipeline_start(dcmi); 7488c2ecf20Sopenharmony_ci if (ret) 7498c2ecf20Sopenharmony_ci goto err_media_pipeline_stop; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci spin_lock_irq(&dcmi->irqlock); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* Set bus width */ 7548c2ecf20Sopenharmony_ci switch (dcmi->bus.bus_width) { 7558c2ecf20Sopenharmony_ci case 14: 7568c2ecf20Sopenharmony_ci val |= CR_EDM_0 | CR_EDM_1; 7578c2ecf20Sopenharmony_ci break; 7588c2ecf20Sopenharmony_ci case 12: 7598c2ecf20Sopenharmony_ci val |= CR_EDM_1; 7608c2ecf20Sopenharmony_ci break; 7618c2ecf20Sopenharmony_ci case 10: 7628c2ecf20Sopenharmony_ci val |= CR_EDM_0; 7638c2ecf20Sopenharmony_ci break; 7648c2ecf20Sopenharmony_ci default: 7658c2ecf20Sopenharmony_ci /* Set bus width to 8 bits by default */ 7668c2ecf20Sopenharmony_ci break; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci /* Set vertical synchronization polarity */ 7708c2ecf20Sopenharmony_ci if (dcmi->bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) 7718c2ecf20Sopenharmony_ci val |= CR_VSPOL; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* Set horizontal synchronization polarity */ 7748c2ecf20Sopenharmony_ci if (dcmi->bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) 7758c2ecf20Sopenharmony_ci val |= CR_HSPOL; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci /* Set pixel clock polarity */ 7788c2ecf20Sopenharmony_ci if (dcmi->bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) 7798c2ecf20Sopenharmony_ci val |= CR_PCKPOL; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci reg_write(dcmi->regs, DCMI_CR, val); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci /* Set crop */ 7848c2ecf20Sopenharmony_ci if (dcmi->do_crop) 7858c2ecf20Sopenharmony_ci dcmi_set_crop(dcmi); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* Enable jpeg capture */ 7888c2ecf20Sopenharmony_ci if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) 7898c2ecf20Sopenharmony_ci reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */ 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* Enable dcmi */ 7928c2ecf20Sopenharmony_ci reg_set(dcmi->regs, DCMI_CR, CR_ENABLE); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci dcmi->sequence = 0; 7958c2ecf20Sopenharmony_ci dcmi->errors_count = 0; 7968c2ecf20Sopenharmony_ci dcmi->overrun_count = 0; 7978c2ecf20Sopenharmony_ci dcmi->buffers_count = 0; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* 8008c2ecf20Sopenharmony_ci * Start transfer if at least one buffer has been queued, 8018c2ecf20Sopenharmony_ci * otherwise transfer is deferred at buffer queueing 8028c2ecf20Sopenharmony_ci */ 8038c2ecf20Sopenharmony_ci if (list_empty(&dcmi->buffers)) { 8048c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Start streaming is deferred to next buffer queueing\n"); 8058c2ecf20Sopenharmony_ci dcmi->state = WAIT_FOR_BUFFER; 8068c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 8078c2ecf20Sopenharmony_ci return 0; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci buf = list_entry(dcmi->buffers.next, struct dcmi_buf, list); 8118c2ecf20Sopenharmony_ci dcmi->active = buf; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci dcmi->state = RUNNING; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Start streaming, starting capture\n"); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 8188c2ecf20Sopenharmony_ci ret = dcmi_start_capture(dcmi, buf); 8198c2ecf20Sopenharmony_ci if (ret) { 8208c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n", 8218c2ecf20Sopenharmony_ci __func__); 8228c2ecf20Sopenharmony_ci goto err_pipeline_stop; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* Enable interruptions */ 8268c2ecf20Sopenharmony_ci if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) 8278c2ecf20Sopenharmony_ci reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); 8288c2ecf20Sopenharmony_ci else 8298c2ecf20Sopenharmony_ci reg_set(dcmi->regs, DCMI_IER, IT_OVR | IT_ERR); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cierr_pipeline_stop: 8348c2ecf20Sopenharmony_ci dcmi_pipeline_stop(dcmi); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cierr_media_pipeline_stop: 8378c2ecf20Sopenharmony_ci media_pipeline_stop(&dcmi->vdev->entity); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cierr_pm_put: 8408c2ecf20Sopenharmony_ci pm_runtime_put(dcmi->dev); 8418c2ecf20Sopenharmony_ci spin_lock_irq(&dcmi->irqlock); 8428c2ecf20Sopenharmony_ci /* 8438c2ecf20Sopenharmony_ci * Return all buffers to vb2 in QUEUED state. 8448c2ecf20Sopenharmony_ci * This will give ownership back to userspace 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, node, &dcmi->buffers, list) { 8478c2ecf20Sopenharmony_ci list_del_init(&buf->list); 8488c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci dcmi->active = NULL; 8518c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci return ret; 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cistatic void dcmi_stop_streaming(struct vb2_queue *vq) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); 8598c2ecf20Sopenharmony_ci struct dcmi_buf *buf, *node; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci dcmi_pipeline_stop(dcmi); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci media_pipeline_stop(&dcmi->vdev->entity); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci spin_lock_irq(&dcmi->irqlock); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* Disable interruptions */ 8688c2ecf20Sopenharmony_ci reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci /* Disable DCMI */ 8718c2ecf20Sopenharmony_ci reg_clear(dcmi->regs, DCMI_CR, CR_ENABLE); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* Return all queued buffers to vb2 in ERROR state */ 8748c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, node, &dcmi->buffers, list) { 8758c2ecf20Sopenharmony_ci list_del_init(&buf->list); 8768c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci dcmi->active = NULL; 8808c2ecf20Sopenharmony_ci dcmi->state = STOPPED; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci spin_unlock_irq(&dcmi->irqlock); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* Stop all pending DMA operations */ 8858c2ecf20Sopenharmony_ci mutex_lock(&dcmi->dma_lock); 8868c2ecf20Sopenharmony_ci dmaengine_terminate_all(dcmi->dma_chan); 8878c2ecf20Sopenharmony_ci mutex_unlock(&dcmi->dma_lock); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci pm_runtime_put(dcmi->dev); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (dcmi->errors_count) 8928c2ecf20Sopenharmony_ci dev_warn(dcmi->dev, "Some errors found while streaming: errors=%d (overrun=%d), buffers=%d\n", 8938c2ecf20Sopenharmony_ci dcmi->errors_count, dcmi->overrun_count, 8948c2ecf20Sopenharmony_ci dcmi->buffers_count); 8958c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Stop streaming, errors=%d (overrun=%d), buffers=%d\n", 8968c2ecf20Sopenharmony_ci dcmi->errors_count, dcmi->overrun_count, 8978c2ecf20Sopenharmony_ci dcmi->buffers_count); 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic const struct vb2_ops dcmi_video_qops = { 9018c2ecf20Sopenharmony_ci .queue_setup = dcmi_queue_setup, 9028c2ecf20Sopenharmony_ci .buf_init = dcmi_buf_init, 9038c2ecf20Sopenharmony_ci .buf_prepare = dcmi_buf_prepare, 9048c2ecf20Sopenharmony_ci .buf_queue = dcmi_buf_queue, 9058c2ecf20Sopenharmony_ci .start_streaming = dcmi_start_streaming, 9068c2ecf20Sopenharmony_ci .stop_streaming = dcmi_stop_streaming, 9078c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 9088c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 9098c2ecf20Sopenharmony_ci}; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cistatic int dcmi_g_fmt_vid_cap(struct file *file, void *priv, 9128c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci *fmt = dcmi->fmt; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci return 0; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic const struct dcmi_format *find_format_by_fourcc(struct stm32_dcmi *dcmi, 9228c2ecf20Sopenharmony_ci unsigned int fourcc) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci unsigned int num_formats = dcmi->num_of_sd_formats; 9258c2ecf20Sopenharmony_ci const struct dcmi_format *fmt; 9268c2ecf20Sopenharmony_ci unsigned int i; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci for (i = 0; i < num_formats; i++) { 9298c2ecf20Sopenharmony_ci fmt = dcmi->sd_formats[i]; 9308c2ecf20Sopenharmony_ci if (fmt->fourcc == fourcc) 9318c2ecf20Sopenharmony_ci return fmt; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci return NULL; 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic void __find_outer_frame_size(struct stm32_dcmi *dcmi, 9388c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix, 9398c2ecf20Sopenharmony_ci struct dcmi_framesize *framesize) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci struct dcmi_framesize *match = NULL; 9428c2ecf20Sopenharmony_ci unsigned int i; 9438c2ecf20Sopenharmony_ci unsigned int min_err = UINT_MAX; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci for (i = 0; i < dcmi->num_of_sd_framesizes; i++) { 9468c2ecf20Sopenharmony_ci struct dcmi_framesize *fsize = &dcmi->sd_framesizes[i]; 9478c2ecf20Sopenharmony_ci int w_err = (fsize->width - pix->width); 9488c2ecf20Sopenharmony_ci int h_err = (fsize->height - pix->height); 9498c2ecf20Sopenharmony_ci int err = w_err + h_err; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (w_err >= 0 && h_err >= 0 && err < min_err) { 9528c2ecf20Sopenharmony_ci min_err = err; 9538c2ecf20Sopenharmony_ci match = fsize; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci if (!match) 9578c2ecf20Sopenharmony_ci match = &dcmi->sd_framesizes[0]; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci *framesize = *match; 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_cistatic int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, 9638c2ecf20Sopenharmony_ci const struct dcmi_format **sd_format, 9648c2ecf20Sopenharmony_ci struct dcmi_framesize *sd_framesize) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci const struct dcmi_format *sd_fmt; 9678c2ecf20Sopenharmony_ci struct dcmi_framesize sd_fsize; 9688c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 9698c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config pad_cfg; 9708c2ecf20Sopenharmony_ci struct v4l2_subdev_format format = { 9718c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_TRY, 9728c2ecf20Sopenharmony_ci }; 9738c2ecf20Sopenharmony_ci bool do_crop; 9748c2ecf20Sopenharmony_ci int ret; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci sd_fmt = find_format_by_fourcc(dcmi, pix->pixelformat); 9778c2ecf20Sopenharmony_ci if (!sd_fmt) { 9788c2ecf20Sopenharmony_ci if (!dcmi->num_of_sd_formats) 9798c2ecf20Sopenharmony_ci return -ENODATA; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci sd_fmt = dcmi->sd_formats[dcmi->num_of_sd_formats - 1]; 9828c2ecf20Sopenharmony_ci pix->pixelformat = sd_fmt->fourcc; 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci /* Limit to hardware capabilities */ 9868c2ecf20Sopenharmony_ci pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); 9878c2ecf20Sopenharmony_ci pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci /* No crop if JPEG is requested */ 9908c2ecf20Sopenharmony_ci do_crop = dcmi->do_crop && (pix->pixelformat != V4L2_PIX_FMT_JPEG); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (do_crop && dcmi->num_of_sd_framesizes) { 9938c2ecf20Sopenharmony_ci struct dcmi_framesize outer_sd_fsize; 9948c2ecf20Sopenharmony_ci /* 9958c2ecf20Sopenharmony_ci * If crop is requested and sensor have discrete frame sizes, 9968c2ecf20Sopenharmony_ci * select the frame size that is just larger than request 9978c2ecf20Sopenharmony_ci */ 9988c2ecf20Sopenharmony_ci __find_outer_frame_size(dcmi, pix, &outer_sd_fsize); 9998c2ecf20Sopenharmony_ci pix->width = outer_sd_fsize.width; 10008c2ecf20Sopenharmony_ci pix->height = outer_sd_fsize.height; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); 10048c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt, 10058c2ecf20Sopenharmony_ci &pad_cfg, &format); 10068c2ecf20Sopenharmony_ci if (ret < 0) 10078c2ecf20Sopenharmony_ci return ret; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci /* Update pix regarding to what sensor can do */ 10108c2ecf20Sopenharmony_ci v4l2_fill_pix_format(pix, &format.format); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci /* Save resolution that sensor can actually do */ 10138c2ecf20Sopenharmony_ci sd_fsize.width = pix->width; 10148c2ecf20Sopenharmony_ci sd_fsize.height = pix->height; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if (do_crop) { 10178c2ecf20Sopenharmony_ci struct v4l2_rect c = dcmi->crop; 10188c2ecf20Sopenharmony_ci struct v4l2_rect max_rect; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci /* 10218c2ecf20Sopenharmony_ci * Adjust crop by making the intersection between 10228c2ecf20Sopenharmony_ci * format resolution request and crop request 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_ci max_rect.top = 0; 10258c2ecf20Sopenharmony_ci max_rect.left = 0; 10268c2ecf20Sopenharmony_ci max_rect.width = pix->width; 10278c2ecf20Sopenharmony_ci max_rect.height = pix->height; 10288c2ecf20Sopenharmony_ci v4l2_rect_map_inside(&c, &max_rect); 10298c2ecf20Sopenharmony_ci c.top = clamp_t(s32, c.top, 0, pix->height - c.height); 10308c2ecf20Sopenharmony_ci c.left = clamp_t(s32, c.left, 0, pix->width - c.width); 10318c2ecf20Sopenharmony_ci dcmi->crop = c; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* Adjust format resolution request to crop */ 10348c2ecf20Sopenharmony_ci pix->width = dcmi->crop.width; 10358c2ecf20Sopenharmony_ci pix->height = dcmi->crop.height; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 10398c2ecf20Sopenharmony_ci pix->bytesperline = pix->width * sd_fmt->bpp; 10408c2ecf20Sopenharmony_ci pix->sizeimage = pix->bytesperline * pix->height; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (sd_format) 10438c2ecf20Sopenharmony_ci *sd_format = sd_fmt; 10448c2ecf20Sopenharmony_ci if (sd_framesize) 10458c2ecf20Sopenharmony_ci *sd_framesize = sd_fsize; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci return 0; 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f) 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci struct v4l2_subdev_format format = { 10538c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 10548c2ecf20Sopenharmony_ci }; 10558c2ecf20Sopenharmony_ci const struct dcmi_format *sd_format; 10568c2ecf20Sopenharmony_ci struct dcmi_framesize sd_framesize; 10578c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &format.format; 10588c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 10598c2ecf20Sopenharmony_ci int ret; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* 10628c2ecf20Sopenharmony_ci * Try format, fmt.width/height could have been changed 10638c2ecf20Sopenharmony_ci * to match sensor capability or crop request 10648c2ecf20Sopenharmony_ci * sd_format & sd_framesize will contain what subdev 10658c2ecf20Sopenharmony_ci * can do for this request. 10668c2ecf20Sopenharmony_ci */ 10678c2ecf20Sopenharmony_ci ret = dcmi_try_fmt(dcmi, f, &sd_format, &sd_framesize); 10688c2ecf20Sopenharmony_ci if (ret) 10698c2ecf20Sopenharmony_ci return ret; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* Disable crop if JPEG is requested */ 10728c2ecf20Sopenharmony_ci if (pix->pixelformat == V4L2_PIX_FMT_JPEG) 10738c2ecf20Sopenharmony_ci dcmi->do_crop = false; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci /* pix to mbus format */ 10768c2ecf20Sopenharmony_ci v4l2_fill_mbus_format(mf, pix, 10778c2ecf20Sopenharmony_ci sd_format->mbus_code); 10788c2ecf20Sopenharmony_ci mf->width = sd_framesize.width; 10798c2ecf20Sopenharmony_ci mf->height = sd_framesize.height; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci ret = dcmi_pipeline_s_fmt(dcmi, NULL, &format); 10828c2ecf20Sopenharmony_ci if (ret < 0) 10838c2ecf20Sopenharmony_ci return ret; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Sensor format set to 0x%x %ux%u\n", 10868c2ecf20Sopenharmony_ci mf->code, mf->width, mf->height); 10878c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Buffer format set to %4.4s %ux%u\n", 10888c2ecf20Sopenharmony_ci (char *)&pix->pixelformat, 10898c2ecf20Sopenharmony_ci pix->width, pix->height); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci dcmi->fmt = *f; 10928c2ecf20Sopenharmony_ci dcmi->sd_format = sd_format; 10938c2ecf20Sopenharmony_ci dcmi->sd_framesize = sd_framesize; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci return 0; 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic int dcmi_s_fmt_vid_cap(struct file *file, void *priv, 10998c2ecf20Sopenharmony_ci struct v4l2_format *f) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci if (vb2_is_streaming(&dcmi->queue)) 11048c2ecf20Sopenharmony_ci return -EBUSY; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci return dcmi_set_fmt(dcmi, f); 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_cistatic int dcmi_try_fmt_vid_cap(struct file *file, void *priv, 11108c2ecf20Sopenharmony_ci struct v4l2_format *f) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci return dcmi_try_fmt(dcmi, f, NULL, NULL); 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic int dcmi_enum_fmt_vid_cap(struct file *file, void *priv, 11188c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci if (f->index >= dcmi->num_of_sd_formats) 11238c2ecf20Sopenharmony_ci return -EINVAL; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci f->pixelformat = dcmi->sd_formats[f->index]->fourcc; 11268c2ecf20Sopenharmony_ci return 0; 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic int dcmi_get_sensor_format(struct stm32_dcmi *dcmi, 11308c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct v4l2_subdev_format fmt = { 11338c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 11348c2ecf20Sopenharmony_ci }; 11358c2ecf20Sopenharmony_ci int ret; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(dcmi->entity.source, pad, get_fmt, NULL, &fmt); 11388c2ecf20Sopenharmony_ci if (ret) 11398c2ecf20Sopenharmony_ci return ret; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci v4l2_fill_pix_format(pix, &fmt.format); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci return 0; 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cistatic int dcmi_set_sensor_format(struct stm32_dcmi *dcmi, 11478c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci const struct dcmi_format *sd_fmt; 11508c2ecf20Sopenharmony_ci struct v4l2_subdev_format format = { 11518c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_TRY, 11528c2ecf20Sopenharmony_ci }; 11538c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config pad_cfg; 11548c2ecf20Sopenharmony_ci int ret; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci sd_fmt = find_format_by_fourcc(dcmi, pix->pixelformat); 11578c2ecf20Sopenharmony_ci if (!sd_fmt) { 11588c2ecf20Sopenharmony_ci if (!dcmi->num_of_sd_formats) 11598c2ecf20Sopenharmony_ci return -ENODATA; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci sd_fmt = dcmi->sd_formats[dcmi->num_of_sd_formats - 1]; 11628c2ecf20Sopenharmony_ci pix->pixelformat = sd_fmt->fourcc; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code); 11668c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(dcmi->entity.source, pad, set_fmt, 11678c2ecf20Sopenharmony_ci &pad_cfg, &format); 11688c2ecf20Sopenharmony_ci if (ret < 0) 11698c2ecf20Sopenharmony_ci return ret; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci return 0; 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_cistatic int dcmi_get_sensor_bounds(struct stm32_dcmi *dcmi, 11758c2ecf20Sopenharmony_ci struct v4l2_rect *r) 11768c2ecf20Sopenharmony_ci{ 11778c2ecf20Sopenharmony_ci struct v4l2_subdev_selection bounds = { 11788c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 11798c2ecf20Sopenharmony_ci .target = V4L2_SEL_TGT_CROP_BOUNDS, 11808c2ecf20Sopenharmony_ci }; 11818c2ecf20Sopenharmony_ci unsigned int max_width, max_height, max_pixsize; 11828c2ecf20Sopenharmony_ci struct v4l2_pix_format pix; 11838c2ecf20Sopenharmony_ci unsigned int i; 11848c2ecf20Sopenharmony_ci int ret; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* 11878c2ecf20Sopenharmony_ci * Get sensor bounds first 11888c2ecf20Sopenharmony_ci */ 11898c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(dcmi->entity.source, pad, get_selection, 11908c2ecf20Sopenharmony_ci NULL, &bounds); 11918c2ecf20Sopenharmony_ci if (!ret) 11928c2ecf20Sopenharmony_ci *r = bounds.r; 11938c2ecf20Sopenharmony_ci if (ret != -ENOIOCTLCMD) 11948c2ecf20Sopenharmony_ci return ret; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci /* 11978c2ecf20Sopenharmony_ci * If selection is not implemented, 11988c2ecf20Sopenharmony_ci * fallback by enumerating sensor frame sizes 11998c2ecf20Sopenharmony_ci * and take the largest one 12008c2ecf20Sopenharmony_ci */ 12018c2ecf20Sopenharmony_ci max_width = 0; 12028c2ecf20Sopenharmony_ci max_height = 0; 12038c2ecf20Sopenharmony_ci max_pixsize = 0; 12048c2ecf20Sopenharmony_ci for (i = 0; i < dcmi->num_of_sd_framesizes; i++) { 12058c2ecf20Sopenharmony_ci struct dcmi_framesize *fsize = &dcmi->sd_framesizes[i]; 12068c2ecf20Sopenharmony_ci unsigned int pixsize = fsize->width * fsize->height; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci if (pixsize > max_pixsize) { 12098c2ecf20Sopenharmony_ci max_pixsize = pixsize; 12108c2ecf20Sopenharmony_ci max_width = fsize->width; 12118c2ecf20Sopenharmony_ci max_height = fsize->height; 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci if (max_pixsize > 0) { 12158c2ecf20Sopenharmony_ci r->top = 0; 12168c2ecf20Sopenharmony_ci r->left = 0; 12178c2ecf20Sopenharmony_ci r->width = max_width; 12188c2ecf20Sopenharmony_ci r->height = max_height; 12198c2ecf20Sopenharmony_ci return 0; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* 12238c2ecf20Sopenharmony_ci * If frame sizes enumeration is not implemented, 12248c2ecf20Sopenharmony_ci * fallback by getting current sensor frame size 12258c2ecf20Sopenharmony_ci */ 12268c2ecf20Sopenharmony_ci ret = dcmi_get_sensor_format(dcmi, &pix); 12278c2ecf20Sopenharmony_ci if (ret) 12288c2ecf20Sopenharmony_ci return ret; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci r->top = 0; 12318c2ecf20Sopenharmony_ci r->left = 0; 12328c2ecf20Sopenharmony_ci r->width = pix.width; 12338c2ecf20Sopenharmony_ci r->height = pix.height; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci return 0; 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_cistatic int dcmi_g_selection(struct file *file, void *fh, 12398c2ecf20Sopenharmony_ci struct v4l2_selection *s) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 12448c2ecf20Sopenharmony_ci return -EINVAL; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci switch (s->target) { 12478c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 12488c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 12498c2ecf20Sopenharmony_ci s->r = dcmi->sd_bounds; 12508c2ecf20Sopenharmony_ci return 0; 12518c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 12528c2ecf20Sopenharmony_ci if (dcmi->do_crop) { 12538c2ecf20Sopenharmony_ci s->r = dcmi->crop; 12548c2ecf20Sopenharmony_ci } else { 12558c2ecf20Sopenharmony_ci s->r.top = 0; 12568c2ecf20Sopenharmony_ci s->r.left = 0; 12578c2ecf20Sopenharmony_ci s->r.width = dcmi->fmt.fmt.pix.width; 12588c2ecf20Sopenharmony_ci s->r.height = dcmi->fmt.fmt.pix.height; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci break; 12618c2ecf20Sopenharmony_ci default: 12628c2ecf20Sopenharmony_ci return -EINVAL; 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci return 0; 12668c2ecf20Sopenharmony_ci} 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_cistatic int dcmi_s_selection(struct file *file, void *priv, 12698c2ecf20Sopenharmony_ci struct v4l2_selection *s) 12708c2ecf20Sopenharmony_ci{ 12718c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 12728c2ecf20Sopenharmony_ci struct v4l2_rect r = s->r; 12738c2ecf20Sopenharmony_ci struct v4l2_rect max_rect; 12748c2ecf20Sopenharmony_ci struct v4l2_pix_format pix; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || 12778c2ecf20Sopenharmony_ci s->target != V4L2_SEL_TGT_CROP) 12788c2ecf20Sopenharmony_ci return -EINVAL; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci /* Reset sensor resolution to max resolution */ 12818c2ecf20Sopenharmony_ci pix.pixelformat = dcmi->fmt.fmt.pix.pixelformat; 12828c2ecf20Sopenharmony_ci pix.width = dcmi->sd_bounds.width; 12838c2ecf20Sopenharmony_ci pix.height = dcmi->sd_bounds.height; 12848c2ecf20Sopenharmony_ci dcmi_set_sensor_format(dcmi, &pix); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci /* 12878c2ecf20Sopenharmony_ci * Make the intersection between 12888c2ecf20Sopenharmony_ci * sensor resolution 12898c2ecf20Sopenharmony_ci * and crop request 12908c2ecf20Sopenharmony_ci */ 12918c2ecf20Sopenharmony_ci max_rect.top = 0; 12928c2ecf20Sopenharmony_ci max_rect.left = 0; 12938c2ecf20Sopenharmony_ci max_rect.width = pix.width; 12948c2ecf20Sopenharmony_ci max_rect.height = pix.height; 12958c2ecf20Sopenharmony_ci v4l2_rect_map_inside(&r, &max_rect); 12968c2ecf20Sopenharmony_ci r.top = clamp_t(s32, r.top, 0, pix.height - r.height); 12978c2ecf20Sopenharmony_ci r.left = clamp_t(s32, r.left, 0, pix.width - r.width); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (!(r.top == dcmi->sd_bounds.top && 13008c2ecf20Sopenharmony_ci r.left == dcmi->sd_bounds.left && 13018c2ecf20Sopenharmony_ci r.width == dcmi->sd_bounds.width && 13028c2ecf20Sopenharmony_ci r.height == dcmi->sd_bounds.height)) { 13038c2ecf20Sopenharmony_ci /* Crop if request is different than sensor resolution */ 13048c2ecf20Sopenharmony_ci dcmi->do_crop = true; 13058c2ecf20Sopenharmony_ci dcmi->crop = r; 13068c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "s_selection: crop %ux%u@(%u,%u) from %ux%u\n", 13078c2ecf20Sopenharmony_ci r.width, r.height, r.left, r.top, 13088c2ecf20Sopenharmony_ci pix.width, pix.height); 13098c2ecf20Sopenharmony_ci } else { 13108c2ecf20Sopenharmony_ci /* Disable crop */ 13118c2ecf20Sopenharmony_ci dcmi->do_crop = false; 13128c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "s_selection: crop is disabled\n"); 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci s->r = r; 13168c2ecf20Sopenharmony_ci return 0; 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic int dcmi_querycap(struct file *file, void *priv, 13208c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 13218c2ecf20Sopenharmony_ci{ 13228c2ecf20Sopenharmony_ci strscpy(cap->driver, DRV_NAME, sizeof(cap->driver)); 13238c2ecf20Sopenharmony_ci strscpy(cap->card, "STM32 Camera Memory Interface", 13248c2ecf20Sopenharmony_ci sizeof(cap->card)); 13258c2ecf20Sopenharmony_ci strscpy(cap->bus_info, "platform:dcmi", sizeof(cap->bus_info)); 13268c2ecf20Sopenharmony_ci return 0; 13278c2ecf20Sopenharmony_ci} 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_cistatic int dcmi_enum_input(struct file *file, void *priv, 13308c2ecf20Sopenharmony_ci struct v4l2_input *i) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci if (i->index != 0) 13338c2ecf20Sopenharmony_ci return -EINVAL; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 13368c2ecf20Sopenharmony_ci strscpy(i->name, "Camera", sizeof(i->name)); 13378c2ecf20Sopenharmony_ci return 0; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic int dcmi_g_input(struct file *file, void *priv, unsigned int *i) 13418c2ecf20Sopenharmony_ci{ 13428c2ecf20Sopenharmony_ci *i = 0; 13438c2ecf20Sopenharmony_ci return 0; 13448c2ecf20Sopenharmony_ci} 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_cistatic int dcmi_s_input(struct file *file, void *priv, unsigned int i) 13478c2ecf20Sopenharmony_ci{ 13488c2ecf20Sopenharmony_ci if (i > 0) 13498c2ecf20Sopenharmony_ci return -EINVAL; 13508c2ecf20Sopenharmony_ci return 0; 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_cistatic int dcmi_enum_framesizes(struct file *file, void *fh, 13548c2ecf20Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 13558c2ecf20Sopenharmony_ci{ 13568c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 13578c2ecf20Sopenharmony_ci const struct dcmi_format *sd_fmt; 13588c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum fse = { 13598c2ecf20Sopenharmony_ci .index = fsize->index, 13608c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 13618c2ecf20Sopenharmony_ci }; 13628c2ecf20Sopenharmony_ci int ret; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci sd_fmt = find_format_by_fourcc(dcmi, fsize->pixel_format); 13658c2ecf20Sopenharmony_ci if (!sd_fmt) 13668c2ecf20Sopenharmony_ci return -EINVAL; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci fse.code = sd_fmt->mbus_code; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(dcmi->entity.source, pad, enum_frame_size, 13718c2ecf20Sopenharmony_ci NULL, &fse); 13728c2ecf20Sopenharmony_ci if (ret) 13738c2ecf20Sopenharmony_ci return ret; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 13768c2ecf20Sopenharmony_ci fsize->discrete.width = fse.max_width; 13778c2ecf20Sopenharmony_ci fsize->discrete.height = fse.max_height; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci return 0; 13808c2ecf20Sopenharmony_ci} 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_cistatic int dcmi_g_parm(struct file *file, void *priv, 13838c2ecf20Sopenharmony_ci struct v4l2_streamparm *p) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.source, p); 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic int dcmi_s_parm(struct file *file, void *priv, 13918c2ecf20Sopenharmony_ci struct v4l2_streamparm *p) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.source, p); 13968c2ecf20Sopenharmony_ci} 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_cistatic int dcmi_enum_frameintervals(struct file *file, void *fh, 13998c2ecf20Sopenharmony_ci struct v4l2_frmivalenum *fival) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 14028c2ecf20Sopenharmony_ci const struct dcmi_format *sd_fmt; 14038c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval_enum fie = { 14048c2ecf20Sopenharmony_ci .index = fival->index, 14058c2ecf20Sopenharmony_ci .width = fival->width, 14068c2ecf20Sopenharmony_ci .height = fival->height, 14078c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 14088c2ecf20Sopenharmony_ci }; 14098c2ecf20Sopenharmony_ci int ret; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci sd_fmt = find_format_by_fourcc(dcmi, fival->pixel_format); 14128c2ecf20Sopenharmony_ci if (!sd_fmt) 14138c2ecf20Sopenharmony_ci return -EINVAL; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci fie.code = sd_fmt->mbus_code; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(dcmi->entity.source, pad, 14188c2ecf20Sopenharmony_ci enum_frame_interval, NULL, &fie); 14198c2ecf20Sopenharmony_ci if (ret) 14208c2ecf20Sopenharmony_ci return ret; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 14238c2ecf20Sopenharmony_ci fival->discrete = fie.interval; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci return 0; 14268c2ecf20Sopenharmony_ci} 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_cistatic const struct of_device_id stm32_dcmi_of_match[] = { 14298c2ecf20Sopenharmony_ci { .compatible = "st,stm32-dcmi"}, 14308c2ecf20Sopenharmony_ci { /* end node */ }, 14318c2ecf20Sopenharmony_ci}; 14328c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, stm32_dcmi_of_match); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_cistatic int dcmi_open(struct file *file) 14358c2ecf20Sopenharmony_ci{ 14368c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 14378c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = dcmi->entity.source; 14388c2ecf20Sopenharmony_ci int ret; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&dcmi->lock)) 14418c2ecf20Sopenharmony_ci return -ERESTARTSYS; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci ret = v4l2_fh_open(file); 14448c2ecf20Sopenharmony_ci if (ret < 0) 14458c2ecf20Sopenharmony_ci goto unlock; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (!v4l2_fh_is_singular_file(file)) 14488c2ecf20Sopenharmony_ci goto fh_rel; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(sd, core, s_power, 1); 14518c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 14528c2ecf20Sopenharmony_ci goto fh_rel; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci ret = dcmi_set_fmt(dcmi, &dcmi->fmt); 14558c2ecf20Sopenharmony_ci if (ret) 14568c2ecf20Sopenharmony_ci v4l2_subdev_call(sd, core, s_power, 0); 14578c2ecf20Sopenharmony_cifh_rel: 14588c2ecf20Sopenharmony_ci if (ret) 14598c2ecf20Sopenharmony_ci v4l2_fh_release(file); 14608c2ecf20Sopenharmony_ciunlock: 14618c2ecf20Sopenharmony_ci mutex_unlock(&dcmi->lock); 14628c2ecf20Sopenharmony_ci return ret; 14638c2ecf20Sopenharmony_ci} 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_cistatic int dcmi_release(struct file *file) 14668c2ecf20Sopenharmony_ci{ 14678c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = video_drvdata(file); 14688c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = dcmi->entity.source; 14698c2ecf20Sopenharmony_ci bool fh_singular; 14708c2ecf20Sopenharmony_ci int ret; 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci mutex_lock(&dcmi->lock); 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci fh_singular = v4l2_fh_is_singular_file(file); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci ret = _vb2_fop_release(file, NULL); 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci if (fh_singular) 14798c2ecf20Sopenharmony_ci v4l2_subdev_call(sd, core, s_power, 0); 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci mutex_unlock(&dcmi->lock); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci return ret; 14848c2ecf20Sopenharmony_ci} 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops dcmi_ioctl_ops = { 14878c2ecf20Sopenharmony_ci .vidioc_querycap = dcmi_querycap, 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = dcmi_try_fmt_vid_cap, 14908c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = dcmi_g_fmt_vid_cap, 14918c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = dcmi_s_fmt_vid_cap, 14928c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = dcmi_enum_fmt_vid_cap, 14938c2ecf20Sopenharmony_ci .vidioc_g_selection = dcmi_g_selection, 14948c2ecf20Sopenharmony_ci .vidioc_s_selection = dcmi_s_selection, 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci .vidioc_enum_input = dcmi_enum_input, 14978c2ecf20Sopenharmony_ci .vidioc_g_input = dcmi_g_input, 14988c2ecf20Sopenharmony_ci .vidioc_s_input = dcmi_s_input, 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci .vidioc_g_parm = dcmi_g_parm, 15018c2ecf20Sopenharmony_ci .vidioc_s_parm = dcmi_s_parm, 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci .vidioc_enum_framesizes = dcmi_enum_framesizes, 15048c2ecf20Sopenharmony_ci .vidioc_enum_frameintervals = dcmi_enum_frameintervals, 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 15078c2ecf20Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 15088c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 15098c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 15108c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 15118c2ecf20Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 15128c2ecf20Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 15138c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 15148c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 15178c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 15188c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 15198c2ecf20Sopenharmony_ci}; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations dcmi_fops = { 15228c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 15238c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 15248c2ecf20Sopenharmony_ci .open = dcmi_open, 15258c2ecf20Sopenharmony_ci .release = dcmi_release, 15268c2ecf20Sopenharmony_ci .poll = vb2_fop_poll, 15278c2ecf20Sopenharmony_ci .mmap = vb2_fop_mmap, 15288c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU 15298c2ecf20Sopenharmony_ci .get_unmapped_area = vb2_fop_get_unmapped_area, 15308c2ecf20Sopenharmony_ci#endif 15318c2ecf20Sopenharmony_ci .read = vb2_fop_read, 15328c2ecf20Sopenharmony_ci}; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_cistatic int dcmi_set_default_fmt(struct stm32_dcmi *dcmi) 15358c2ecf20Sopenharmony_ci{ 15368c2ecf20Sopenharmony_ci struct v4l2_format f = { 15378c2ecf20Sopenharmony_ci .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, 15388c2ecf20Sopenharmony_ci .fmt.pix = { 15398c2ecf20Sopenharmony_ci .width = CIF_WIDTH, 15408c2ecf20Sopenharmony_ci .height = CIF_HEIGHT, 15418c2ecf20Sopenharmony_ci .field = V4L2_FIELD_NONE, 15428c2ecf20Sopenharmony_ci .pixelformat = dcmi->sd_formats[0]->fourcc, 15438c2ecf20Sopenharmony_ci }, 15448c2ecf20Sopenharmony_ci }; 15458c2ecf20Sopenharmony_ci int ret; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci ret = dcmi_try_fmt(dcmi, &f, NULL, NULL); 15488c2ecf20Sopenharmony_ci if (ret) 15498c2ecf20Sopenharmony_ci return ret; 15508c2ecf20Sopenharmony_ci dcmi->sd_format = dcmi->sd_formats[0]; 15518c2ecf20Sopenharmony_ci dcmi->fmt = f; 15528c2ecf20Sopenharmony_ci return 0; 15538c2ecf20Sopenharmony_ci} 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci/* 15568c2ecf20Sopenharmony_ci * FIXME: For the time being we only support subdevices 15578c2ecf20Sopenharmony_ci * which expose RGB & YUV "parallel form" mbus code (_2X8). 15588c2ecf20Sopenharmony_ci * Nevertheless, this allows to support serial source subdevices 15598c2ecf20Sopenharmony_ci * and serial to parallel bridges which conform to this. 15608c2ecf20Sopenharmony_ci */ 15618c2ecf20Sopenharmony_cistatic const struct dcmi_format dcmi_formats[] = { 15628c2ecf20Sopenharmony_ci { 15638c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 15648c2ecf20Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, 15658c2ecf20Sopenharmony_ci .bpp = 2, 15668c2ecf20Sopenharmony_ci }, { 15678c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 15688c2ecf20Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, 15698c2ecf20Sopenharmony_ci .bpp = 2, 15708c2ecf20Sopenharmony_ci }, { 15718c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 15728c2ecf20Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, 15738c2ecf20Sopenharmony_ci .bpp = 2, 15748c2ecf20Sopenharmony_ci }, { 15758c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_JPEG, 15768c2ecf20Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, 15778c2ecf20Sopenharmony_ci .bpp = 1, 15788c2ecf20Sopenharmony_ci }, 15798c2ecf20Sopenharmony_ci}; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_cistatic int dcmi_formats_init(struct stm32_dcmi *dcmi) 15828c2ecf20Sopenharmony_ci{ 15838c2ecf20Sopenharmony_ci const struct dcmi_format *sd_fmts[ARRAY_SIZE(dcmi_formats)]; 15848c2ecf20Sopenharmony_ci unsigned int num_fmts = 0, i, j; 15858c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev = dcmi->entity.source; 15868c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum mbus_code = { 15878c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 15888c2ecf20Sopenharmony_ci }; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, 15918c2ecf20Sopenharmony_ci NULL, &mbus_code)) { 15928c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dcmi_formats); i++) { 15938c2ecf20Sopenharmony_ci if (dcmi_formats[i].mbus_code != mbus_code.code) 15948c2ecf20Sopenharmony_ci continue; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci /* Code supported, have we got this fourcc yet? */ 15978c2ecf20Sopenharmony_ci for (j = 0; j < num_fmts; j++) 15988c2ecf20Sopenharmony_ci if (sd_fmts[j]->fourcc == 15998c2ecf20Sopenharmony_ci dcmi_formats[i].fourcc) { 16008c2ecf20Sopenharmony_ci /* Already available */ 16018c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Skipping fourcc/code: %4.4s/0x%x\n", 16028c2ecf20Sopenharmony_ci (char *)&sd_fmts[j]->fourcc, 16038c2ecf20Sopenharmony_ci mbus_code.code); 16048c2ecf20Sopenharmony_ci break; 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci if (j == num_fmts) { 16078c2ecf20Sopenharmony_ci /* New */ 16088c2ecf20Sopenharmony_ci sd_fmts[num_fmts++] = dcmi_formats + i; 16098c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Supported fourcc/code: %4.4s/0x%x\n", 16108c2ecf20Sopenharmony_ci (char *)&sd_fmts[num_fmts - 1]->fourcc, 16118c2ecf20Sopenharmony_ci sd_fmts[num_fmts - 1]->mbus_code); 16128c2ecf20Sopenharmony_ci } 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci mbus_code.index++; 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci if (!num_fmts) 16188c2ecf20Sopenharmony_ci return -ENXIO; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci dcmi->num_of_sd_formats = num_fmts; 16218c2ecf20Sopenharmony_ci dcmi->sd_formats = devm_kcalloc(dcmi->dev, 16228c2ecf20Sopenharmony_ci num_fmts, sizeof(struct dcmi_format *), 16238c2ecf20Sopenharmony_ci GFP_KERNEL); 16248c2ecf20Sopenharmony_ci if (!dcmi->sd_formats) { 16258c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Could not allocate memory\n"); 16268c2ecf20Sopenharmony_ci return -ENOMEM; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci memcpy(dcmi->sd_formats, sd_fmts, 16308c2ecf20Sopenharmony_ci num_fmts * sizeof(struct dcmi_format *)); 16318c2ecf20Sopenharmony_ci dcmi->sd_format = dcmi->sd_formats[0]; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci return 0; 16348c2ecf20Sopenharmony_ci} 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_cistatic int dcmi_framesizes_init(struct stm32_dcmi *dcmi) 16378c2ecf20Sopenharmony_ci{ 16388c2ecf20Sopenharmony_ci unsigned int num_fsize = 0; 16398c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev = dcmi->entity.source; 16408c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum fse = { 16418c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 16428c2ecf20Sopenharmony_ci .code = dcmi->sd_format->mbus_code, 16438c2ecf20Sopenharmony_ci }; 16448c2ecf20Sopenharmony_ci unsigned int ret; 16458c2ecf20Sopenharmony_ci unsigned int i; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci /* Allocate discrete framesizes array */ 16488c2ecf20Sopenharmony_ci while (!v4l2_subdev_call(subdev, pad, enum_frame_size, 16498c2ecf20Sopenharmony_ci NULL, &fse)) 16508c2ecf20Sopenharmony_ci fse.index++; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci num_fsize = fse.index; 16538c2ecf20Sopenharmony_ci if (!num_fsize) 16548c2ecf20Sopenharmony_ci return 0; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci dcmi->num_of_sd_framesizes = num_fsize; 16578c2ecf20Sopenharmony_ci dcmi->sd_framesizes = devm_kcalloc(dcmi->dev, num_fsize, 16588c2ecf20Sopenharmony_ci sizeof(struct dcmi_framesize), 16598c2ecf20Sopenharmony_ci GFP_KERNEL); 16608c2ecf20Sopenharmony_ci if (!dcmi->sd_framesizes) { 16618c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Could not allocate memory\n"); 16628c2ecf20Sopenharmony_ci return -ENOMEM; 16638c2ecf20Sopenharmony_ci } 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci /* Fill array with sensor supported framesizes */ 16668c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Sensor supports %u frame sizes:\n", num_fsize); 16678c2ecf20Sopenharmony_ci for (i = 0; i < dcmi->num_of_sd_framesizes; i++) { 16688c2ecf20Sopenharmony_ci fse.index = i; 16698c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, enum_frame_size, 16708c2ecf20Sopenharmony_ci NULL, &fse); 16718c2ecf20Sopenharmony_ci if (ret) 16728c2ecf20Sopenharmony_ci return ret; 16738c2ecf20Sopenharmony_ci dcmi->sd_framesizes[fse.index].width = fse.max_width; 16748c2ecf20Sopenharmony_ci dcmi->sd_framesizes[fse.index].height = fse.max_height; 16758c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "%ux%u\n", fse.max_width, fse.max_height); 16768c2ecf20Sopenharmony_ci } 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci return 0; 16798c2ecf20Sopenharmony_ci} 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_cistatic int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier) 16828c2ecf20Sopenharmony_ci{ 16838c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier); 16848c2ecf20Sopenharmony_ci int ret; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci /* 16878c2ecf20Sopenharmony_ci * Now that the graph is complete, 16888c2ecf20Sopenharmony_ci * we search for the source subdevice 16898c2ecf20Sopenharmony_ci * in order to expose it through V4L2 interface 16908c2ecf20Sopenharmony_ci */ 16918c2ecf20Sopenharmony_ci dcmi->entity.source = 16928c2ecf20Sopenharmony_ci media_entity_to_v4l2_subdev(dcmi_find_source(dcmi)); 16938c2ecf20Sopenharmony_ci if (!dcmi->entity.source) { 16948c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Source subdevice not found\n"); 16958c2ecf20Sopenharmony_ci return -ENODEV; 16968c2ecf20Sopenharmony_ci } 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci dcmi->vdev->ctrl_handler = dcmi->entity.source->ctrl_handler; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci ret = dcmi_formats_init(dcmi); 17018c2ecf20Sopenharmony_ci if (ret) { 17028c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "No supported mediabus format found\n"); 17038c2ecf20Sopenharmony_ci return ret; 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci ret = dcmi_framesizes_init(dcmi); 17078c2ecf20Sopenharmony_ci if (ret) { 17088c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Could not initialize framesizes\n"); 17098c2ecf20Sopenharmony_ci return ret; 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci ret = dcmi_get_sensor_bounds(dcmi, &dcmi->sd_bounds); 17138c2ecf20Sopenharmony_ci if (ret) { 17148c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Could not get sensor bounds\n"); 17158c2ecf20Sopenharmony_ci return ret; 17168c2ecf20Sopenharmony_ci } 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci ret = dcmi_set_default_fmt(dcmi); 17198c2ecf20Sopenharmony_ci if (ret) { 17208c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Could not set default format\n"); 17218c2ecf20Sopenharmony_ci return ret; 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(dcmi->dev, dcmi->irq, dcmi_irq_callback, 17258c2ecf20Sopenharmony_ci dcmi_irq_thread, IRQF_ONESHOT, 17268c2ecf20Sopenharmony_ci dev_name(dcmi->dev), dcmi); 17278c2ecf20Sopenharmony_ci if (ret) { 17288c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Unable to request irq %d\n", dcmi->irq); 17298c2ecf20Sopenharmony_ci return ret; 17308c2ecf20Sopenharmony_ci } 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci return 0; 17338c2ecf20Sopenharmony_ci} 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_cistatic void dcmi_graph_notify_unbind(struct v4l2_async_notifier *notifier, 17368c2ecf20Sopenharmony_ci struct v4l2_subdev *sd, 17378c2ecf20Sopenharmony_ci struct v4l2_async_subdev *asd) 17388c2ecf20Sopenharmony_ci{ 17398c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier); 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Removing %s\n", video_device_node_name(dcmi->vdev)); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci /* Checks internally if vdev has been init or not */ 17448c2ecf20Sopenharmony_ci video_unregister_device(dcmi->vdev); 17458c2ecf20Sopenharmony_ci} 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_cistatic int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier, 17488c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev, 17498c2ecf20Sopenharmony_ci struct v4l2_async_subdev *asd) 17508c2ecf20Sopenharmony_ci{ 17518c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier); 17528c2ecf20Sopenharmony_ci unsigned int ret; 17538c2ecf20Sopenharmony_ci int src_pad; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Subdev \"%s\" bound\n", subdev->name); 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci /* 17588c2ecf20Sopenharmony_ci * Link this sub-device to DCMI, it could be 17598c2ecf20Sopenharmony_ci * a parallel camera sensor or a bridge 17608c2ecf20Sopenharmony_ci */ 17618c2ecf20Sopenharmony_ci src_pad = media_entity_get_fwnode_pad(&subdev->entity, 17628c2ecf20Sopenharmony_ci subdev->fwnode, 17638c2ecf20Sopenharmony_ci MEDIA_PAD_FL_SOURCE); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci ret = media_create_pad_link(&subdev->entity, src_pad, 17668c2ecf20Sopenharmony_ci &dcmi->vdev->entity, 0, 17678c2ecf20Sopenharmony_ci MEDIA_LNK_FL_IMMUTABLE | 17688c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 17698c2ecf20Sopenharmony_ci if (ret) 17708c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Failed to create media pad link with subdev \"%s\"\n", 17718c2ecf20Sopenharmony_ci subdev->name); 17728c2ecf20Sopenharmony_ci else 17738c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "DCMI is now linked to \"%s\"\n", 17748c2ecf20Sopenharmony_ci subdev->name); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci return ret; 17778c2ecf20Sopenharmony_ci} 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_cistatic const struct v4l2_async_notifier_operations dcmi_graph_notify_ops = { 17808c2ecf20Sopenharmony_ci .bound = dcmi_graph_notify_bound, 17818c2ecf20Sopenharmony_ci .unbind = dcmi_graph_notify_unbind, 17828c2ecf20Sopenharmony_ci .complete = dcmi_graph_notify_complete, 17838c2ecf20Sopenharmony_ci}; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_cistatic int dcmi_graph_parse(struct stm32_dcmi *dcmi, struct device_node *node) 17868c2ecf20Sopenharmony_ci{ 17878c2ecf20Sopenharmony_ci struct device_node *ep = NULL; 17888c2ecf20Sopenharmony_ci struct device_node *remote; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci ep = of_graph_get_next_endpoint(node, ep); 17918c2ecf20Sopenharmony_ci if (!ep) 17928c2ecf20Sopenharmony_ci return -EINVAL; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci remote = of_graph_get_remote_port_parent(ep); 17958c2ecf20Sopenharmony_ci of_node_put(ep); 17968c2ecf20Sopenharmony_ci if (!remote) 17978c2ecf20Sopenharmony_ci return -EINVAL; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci /* Remote node to connect */ 18008c2ecf20Sopenharmony_ci dcmi->entity.remote_node = remote; 18018c2ecf20Sopenharmony_ci dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; 18028c2ecf20Sopenharmony_ci dcmi->entity.asd.match.fwnode = of_fwnode_handle(remote); 18038c2ecf20Sopenharmony_ci return 0; 18048c2ecf20Sopenharmony_ci} 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_cistatic int dcmi_graph_init(struct stm32_dcmi *dcmi) 18078c2ecf20Sopenharmony_ci{ 18088c2ecf20Sopenharmony_ci int ret; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci /* Parse the graph to extract a list of subdevice DT nodes. */ 18118c2ecf20Sopenharmony_ci ret = dcmi_graph_parse(dcmi, dcmi->dev->of_node); 18128c2ecf20Sopenharmony_ci if (ret < 0) { 18138c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Failed to parse graph\n"); 18148c2ecf20Sopenharmony_ci return ret; 18158c2ecf20Sopenharmony_ci } 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci v4l2_async_notifier_init(&dcmi->notifier); 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci ret = v4l2_async_notifier_add_subdev(&dcmi->notifier, 18208c2ecf20Sopenharmony_ci &dcmi->entity.asd); 18218c2ecf20Sopenharmony_ci if (ret) { 18228c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Failed to add subdev notifier\n"); 18238c2ecf20Sopenharmony_ci of_node_put(dcmi->entity.remote_node); 18248c2ecf20Sopenharmony_ci return ret; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci dcmi->notifier.ops = &dcmi_graph_notify_ops; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci ret = v4l2_async_notifier_register(&dcmi->v4l2_dev, &dcmi->notifier); 18308c2ecf20Sopenharmony_ci if (ret < 0) { 18318c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Failed to register notifier\n"); 18328c2ecf20Sopenharmony_ci v4l2_async_notifier_cleanup(&dcmi->notifier); 18338c2ecf20Sopenharmony_ci return ret; 18348c2ecf20Sopenharmony_ci } 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci return 0; 18378c2ecf20Sopenharmony_ci} 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_cistatic int dcmi_probe(struct platform_device *pdev) 18408c2ecf20Sopenharmony_ci{ 18418c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 18428c2ecf20Sopenharmony_ci const struct of_device_id *match = NULL; 18438c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; 18448c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi; 18458c2ecf20Sopenharmony_ci struct vb2_queue *q; 18468c2ecf20Sopenharmony_ci struct dma_chan *chan; 18478c2ecf20Sopenharmony_ci struct clk *mclk; 18488c2ecf20Sopenharmony_ci int irq; 18498c2ecf20Sopenharmony_ci int ret = 0; 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci match = of_match_device(of_match_ptr(stm32_dcmi_of_match), &pdev->dev); 18528c2ecf20Sopenharmony_ci if (!match) { 18538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not find a match in devicetree\n"); 18548c2ecf20Sopenharmony_ci return -ENODEV; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci dcmi = devm_kzalloc(&pdev->dev, sizeof(struct stm32_dcmi), GFP_KERNEL); 18588c2ecf20Sopenharmony_ci if (!dcmi) 18598c2ecf20Sopenharmony_ci return -ENOMEM; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci dcmi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 18628c2ecf20Sopenharmony_ci if (IS_ERR(dcmi->rstc)) { 18638c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not get reset control\n"); 18648c2ecf20Sopenharmony_ci return PTR_ERR(dcmi->rstc); 18658c2ecf20Sopenharmony_ci } 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci /* Get bus characteristics from devicetree */ 18688c2ecf20Sopenharmony_ci np = of_graph_get_next_endpoint(np, NULL); 18698c2ecf20Sopenharmony_ci if (!np) { 18708c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not find the endpoint\n"); 18718c2ecf20Sopenharmony_ci return -ENODEV; 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep); 18758c2ecf20Sopenharmony_ci of_node_put(np); 18768c2ecf20Sopenharmony_ci if (ret) { 18778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not parse the endpoint\n"); 18788c2ecf20Sopenharmony_ci return ret; 18798c2ecf20Sopenharmony_ci } 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci if (ep.bus_type == V4L2_MBUS_CSI2_DPHY) { 18828c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "CSI bus not supported\n"); 18838c2ecf20Sopenharmony_ci return -ENODEV; 18848c2ecf20Sopenharmony_ci } 18858c2ecf20Sopenharmony_ci dcmi->bus.flags = ep.bus.parallel.flags; 18868c2ecf20Sopenharmony_ci dcmi->bus.bus_width = ep.bus.parallel.bus_width; 18878c2ecf20Sopenharmony_ci dcmi->bus.data_shift = ep.bus.parallel.data_shift; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 18908c2ecf20Sopenharmony_ci if (irq <= 0) 18918c2ecf20Sopenharmony_ci return irq ? irq : -ENXIO; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci dcmi->irq = irq; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci dcmi->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 18968c2ecf20Sopenharmony_ci if (!dcmi->res) { 18978c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not get resource\n"); 18988c2ecf20Sopenharmony_ci return -ENODEV; 18998c2ecf20Sopenharmony_ci } 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci dcmi->regs = devm_ioremap_resource(&pdev->dev, dcmi->res); 19028c2ecf20Sopenharmony_ci if (IS_ERR(dcmi->regs)) { 19038c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not map registers\n"); 19048c2ecf20Sopenharmony_ci return PTR_ERR(dcmi->regs); 19058c2ecf20Sopenharmony_ci } 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci mclk = devm_clk_get(&pdev->dev, "mclk"); 19088c2ecf20Sopenharmony_ci if (IS_ERR(mclk)) { 19098c2ecf20Sopenharmony_ci if (PTR_ERR(mclk) != -EPROBE_DEFER) 19108c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to get mclk\n"); 19118c2ecf20Sopenharmony_ci return PTR_ERR(mclk); 19128c2ecf20Sopenharmony_ci } 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci chan = dma_request_chan(&pdev->dev, "tx"); 19158c2ecf20Sopenharmony_ci if (IS_ERR(chan)) { 19168c2ecf20Sopenharmony_ci ret = PTR_ERR(chan); 19178c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 19188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 19198c2ecf20Sopenharmony_ci "Failed to request DMA channel: %d\n", ret); 19208c2ecf20Sopenharmony_ci return ret; 19218c2ecf20Sopenharmony_ci } 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci spin_lock_init(&dcmi->irqlock); 19248c2ecf20Sopenharmony_ci mutex_init(&dcmi->lock); 19258c2ecf20Sopenharmony_ci mutex_init(&dcmi->dma_lock); 19268c2ecf20Sopenharmony_ci init_completion(&dcmi->complete); 19278c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dcmi->buffers); 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci dcmi->dev = &pdev->dev; 19308c2ecf20Sopenharmony_ci dcmi->mclk = mclk; 19318c2ecf20Sopenharmony_ci dcmi->state = STOPPED; 19328c2ecf20Sopenharmony_ci dcmi->dma_chan = chan; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci q = &dcmi->queue; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci dcmi->v4l2_dev.mdev = &dcmi->mdev; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci /* Initialize media device */ 19398c2ecf20Sopenharmony_ci strscpy(dcmi->mdev.model, DRV_NAME, sizeof(dcmi->mdev.model)); 19408c2ecf20Sopenharmony_ci snprintf(dcmi->mdev.bus_info, sizeof(dcmi->mdev.bus_info), 19418c2ecf20Sopenharmony_ci "platform:%s", DRV_NAME); 19428c2ecf20Sopenharmony_ci dcmi->mdev.dev = &pdev->dev; 19438c2ecf20Sopenharmony_ci media_device_init(&dcmi->mdev); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci /* Initialize the top-level structure */ 19468c2ecf20Sopenharmony_ci ret = v4l2_device_register(&pdev->dev, &dcmi->v4l2_dev); 19478c2ecf20Sopenharmony_ci if (ret) 19488c2ecf20Sopenharmony_ci goto err_media_device_cleanup; 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci dcmi->vdev = video_device_alloc(); 19518c2ecf20Sopenharmony_ci if (!dcmi->vdev) { 19528c2ecf20Sopenharmony_ci ret = -ENOMEM; 19538c2ecf20Sopenharmony_ci goto err_device_unregister; 19548c2ecf20Sopenharmony_ci } 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci /* Video node */ 19578c2ecf20Sopenharmony_ci dcmi->vdev->fops = &dcmi_fops; 19588c2ecf20Sopenharmony_ci dcmi->vdev->v4l2_dev = &dcmi->v4l2_dev; 19598c2ecf20Sopenharmony_ci dcmi->vdev->queue = &dcmi->queue; 19608c2ecf20Sopenharmony_ci strscpy(dcmi->vdev->name, KBUILD_MODNAME, sizeof(dcmi->vdev->name)); 19618c2ecf20Sopenharmony_ci dcmi->vdev->release = video_device_release; 19628c2ecf20Sopenharmony_ci dcmi->vdev->ioctl_ops = &dcmi_ioctl_ops; 19638c2ecf20Sopenharmony_ci dcmi->vdev->lock = &dcmi->lock; 19648c2ecf20Sopenharmony_ci dcmi->vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | 19658c2ecf20Sopenharmony_ci V4L2_CAP_READWRITE; 19668c2ecf20Sopenharmony_ci video_set_drvdata(dcmi->vdev, dcmi); 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci /* Media entity pads */ 19698c2ecf20Sopenharmony_ci dcmi->vid_cap_pad.flags = MEDIA_PAD_FL_SINK; 19708c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&dcmi->vdev->entity, 19718c2ecf20Sopenharmony_ci 1, &dcmi->vid_cap_pad); 19728c2ecf20Sopenharmony_ci if (ret) { 19738c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Failed to init media entity pad\n"); 19748c2ecf20Sopenharmony_ci goto err_device_release; 19758c2ecf20Sopenharmony_ci } 19768c2ecf20Sopenharmony_ci dcmi->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci ret = video_register_device(dcmi->vdev, VFL_TYPE_VIDEO, -1); 19798c2ecf20Sopenharmony_ci if (ret) { 19808c2ecf20Sopenharmony_ci dev_err(dcmi->dev, "Failed to register video device\n"); 19818c2ecf20Sopenharmony_ci goto err_media_entity_cleanup; 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci dev_dbg(dcmi->dev, "Device registered as %s\n", 19858c2ecf20Sopenharmony_ci video_device_node_name(dcmi->vdev)); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* Buffer queue */ 19888c2ecf20Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 19898c2ecf20Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; 19908c2ecf20Sopenharmony_ci q->lock = &dcmi->lock; 19918c2ecf20Sopenharmony_ci q->drv_priv = dcmi; 19928c2ecf20Sopenharmony_ci q->buf_struct_size = sizeof(struct dcmi_buf); 19938c2ecf20Sopenharmony_ci q->ops = &dcmi_video_qops; 19948c2ecf20Sopenharmony_ci q->mem_ops = &vb2_dma_contig_memops; 19958c2ecf20Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 19968c2ecf20Sopenharmony_ci q->min_buffers_needed = 2; 19978c2ecf20Sopenharmony_ci q->dev = &pdev->dev; 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci ret = vb2_queue_init(q); 20008c2ecf20Sopenharmony_ci if (ret < 0) { 20018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to initialize vb2 queue\n"); 20028c2ecf20Sopenharmony_ci goto err_media_entity_cleanup; 20038c2ecf20Sopenharmony_ci } 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci ret = dcmi_graph_init(dcmi); 20068c2ecf20Sopenharmony_ci if (ret < 0) 20078c2ecf20Sopenharmony_ci goto err_media_entity_cleanup; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci /* Reset device */ 20108c2ecf20Sopenharmony_ci ret = reset_control_assert(dcmi->rstc); 20118c2ecf20Sopenharmony_ci if (ret) { 20128c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to assert the reset line\n"); 20138c2ecf20Sopenharmony_ci goto err_cleanup; 20148c2ecf20Sopenharmony_ci } 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci usleep_range(3000, 5000); 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci ret = reset_control_deassert(dcmi->rstc); 20198c2ecf20Sopenharmony_ci if (ret) { 20208c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to deassert the reset line\n"); 20218c2ecf20Sopenharmony_ci goto err_cleanup; 20228c2ecf20Sopenharmony_ci } 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Probe done\n"); 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dcmi); 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci return 0; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_cierr_cleanup: 20338c2ecf20Sopenharmony_ci v4l2_async_notifier_cleanup(&dcmi->notifier); 20348c2ecf20Sopenharmony_cierr_media_entity_cleanup: 20358c2ecf20Sopenharmony_ci media_entity_cleanup(&dcmi->vdev->entity); 20368c2ecf20Sopenharmony_cierr_device_release: 20378c2ecf20Sopenharmony_ci video_device_release(dcmi->vdev); 20388c2ecf20Sopenharmony_cierr_device_unregister: 20398c2ecf20Sopenharmony_ci v4l2_device_unregister(&dcmi->v4l2_dev); 20408c2ecf20Sopenharmony_cierr_media_device_cleanup: 20418c2ecf20Sopenharmony_ci media_device_cleanup(&dcmi->mdev); 20428c2ecf20Sopenharmony_ci dma_release_channel(dcmi->dma_chan); 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci return ret; 20458c2ecf20Sopenharmony_ci} 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_cistatic int dcmi_remove(struct platform_device *pdev) 20488c2ecf20Sopenharmony_ci{ 20498c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = platform_get_drvdata(pdev); 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci v4l2_async_notifier_unregister(&dcmi->notifier); 20548c2ecf20Sopenharmony_ci v4l2_async_notifier_cleanup(&dcmi->notifier); 20558c2ecf20Sopenharmony_ci media_entity_cleanup(&dcmi->vdev->entity); 20568c2ecf20Sopenharmony_ci v4l2_device_unregister(&dcmi->v4l2_dev); 20578c2ecf20Sopenharmony_ci media_device_cleanup(&dcmi->mdev); 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci dma_release_channel(dcmi->dma_chan); 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci return 0; 20628c2ecf20Sopenharmony_ci} 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_cistatic __maybe_unused int dcmi_runtime_suspend(struct device *dev) 20658c2ecf20Sopenharmony_ci{ 20668c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = dev_get_drvdata(dev); 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci clk_disable_unprepare(dcmi->mclk); 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci return 0; 20718c2ecf20Sopenharmony_ci} 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_cistatic __maybe_unused int dcmi_runtime_resume(struct device *dev) 20748c2ecf20Sopenharmony_ci{ 20758c2ecf20Sopenharmony_ci struct stm32_dcmi *dcmi = dev_get_drvdata(dev); 20768c2ecf20Sopenharmony_ci int ret; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci ret = clk_prepare_enable(dcmi->mclk); 20798c2ecf20Sopenharmony_ci if (ret) 20808c2ecf20Sopenharmony_ci dev_err(dev, "%s: Failed to prepare_enable clock\n", __func__); 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci return ret; 20838c2ecf20Sopenharmony_ci} 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_cistatic __maybe_unused int dcmi_suspend(struct device *dev) 20868c2ecf20Sopenharmony_ci{ 20878c2ecf20Sopenharmony_ci /* disable clock */ 20888c2ecf20Sopenharmony_ci pm_runtime_force_suspend(dev); 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci /* change pinctrl state */ 20918c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(dev); 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci return 0; 20948c2ecf20Sopenharmony_ci} 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_cistatic __maybe_unused int dcmi_resume(struct device *dev) 20978c2ecf20Sopenharmony_ci{ 20988c2ecf20Sopenharmony_ci /* restore pinctl default state */ 20998c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(dev); 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci /* clock enable */ 21028c2ecf20Sopenharmony_ci pm_runtime_force_resume(dev); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci return 0; 21058c2ecf20Sopenharmony_ci} 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_cistatic const struct dev_pm_ops dcmi_pm_ops = { 21088c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(dcmi_suspend, dcmi_resume) 21098c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(dcmi_runtime_suspend, 21108c2ecf20Sopenharmony_ci dcmi_runtime_resume, NULL) 21118c2ecf20Sopenharmony_ci}; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_cistatic struct platform_driver stm32_dcmi_driver = { 21148c2ecf20Sopenharmony_ci .probe = dcmi_probe, 21158c2ecf20Sopenharmony_ci .remove = dcmi_remove, 21168c2ecf20Sopenharmony_ci .driver = { 21178c2ecf20Sopenharmony_ci .name = DRV_NAME, 21188c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(stm32_dcmi_of_match), 21198c2ecf20Sopenharmony_ci .pm = &dcmi_pm_ops, 21208c2ecf20Sopenharmony_ci }, 21218c2ecf20Sopenharmony_ci}; 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_cimodule_platform_driver(stm32_dcmi_driver); 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); 21268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hugues Fruchet <hugues.fruchet@st.com>"); 21278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics STM32 Digital Camera Memory Interface driver"); 21288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 21298c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("video"); 2130