162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Renesas RZ/G2L CRU 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2022 Renesas Electronics Corp. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on Renesas R-Car VIN 862306a36Sopenharmony_ci * Copyright (C) 2016 Renesas Electronics Corp. 962306a36Sopenharmony_ci * Copyright (C) 2011-2013 Renesas Solutions Corp. 1062306a36Sopenharmony_ci * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> 1162306a36Sopenharmony_ci * Copyright (C) 2008 Magnus Damm 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/clk.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 1962306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "rzg2l-cru.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* HW CRU Registers Definition */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* CRU Control Register */ 2662306a36Sopenharmony_ci#define CRUnCTRL 0x0 2762306a36Sopenharmony_ci#define CRUnCTRL_VINSEL(x) ((x) << 0) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* CRU Interrupt Enable Register */ 3062306a36Sopenharmony_ci#define CRUnIE 0x4 3162306a36Sopenharmony_ci#define CRUnIE_EFE BIT(17) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* CRU Interrupt Status Register */ 3462306a36Sopenharmony_ci#define CRUnINTS 0x8 3562306a36Sopenharmony_ci#define CRUnINTS_SFS BIT(16) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* CRU Reset Register */ 3862306a36Sopenharmony_ci#define CRUnRST 0xc 3962306a36Sopenharmony_ci#define CRUnRST_VRESETN BIT(0) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Memory Bank Base Address (Lower) Register for CRU Image Data */ 4262306a36Sopenharmony_ci#define AMnMBxADDRL(x) (0x100 + ((x) * 8)) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Memory Bank Base Address (Higher) Register for CRU Image Data */ 4562306a36Sopenharmony_ci#define AMnMBxADDRH(x) (0x104 + ((x) * 8)) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Memory Bank Enable Register for CRU Image Data */ 4862306a36Sopenharmony_ci#define AMnMBVALID 0x148 4962306a36Sopenharmony_ci#define AMnMBVALID_MBVALID(x) GENMASK(x, 0) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Memory Bank Status Register for CRU Image Data */ 5262306a36Sopenharmony_ci#define AMnMBS 0x14c 5362306a36Sopenharmony_ci#define AMnMBS_MBSTS 0x7 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* AXI Master FIFO Pointer Register for CRU Image Data */ 5662306a36Sopenharmony_ci#define AMnFIFOPNTR 0x168 5762306a36Sopenharmony_ci#define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0) 5862306a36Sopenharmony_ci#define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* AXI Master Transfer Stop Register for CRU Image Data */ 6162306a36Sopenharmony_ci#define AMnAXISTP 0x174 6262306a36Sopenharmony_ci#define AMnAXISTP_AXI_STOP BIT(0) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* AXI Master Transfer Stop Status Register for CRU Image Data */ 6562306a36Sopenharmony_ci#define AMnAXISTPACK 0x178 6662306a36Sopenharmony_ci#define AMnAXISTPACK_AXI_STOP_ACK BIT(0) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* CRU Image Processing Enable Register */ 6962306a36Sopenharmony_ci#define ICnEN 0x200 7062306a36Sopenharmony_ci#define ICnEN_ICEN BIT(0) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* CRU Image Processing Main Control Register */ 7362306a36Sopenharmony_ci#define ICnMC 0x208 7462306a36Sopenharmony_ci#define ICnMC_CSCTHR BIT(5) 7562306a36Sopenharmony_ci#define ICnMC_INF_YUV8_422 (0x1e << 16) 7662306a36Sopenharmony_ci#define ICnMC_INF_USER (0x30 << 16) 7762306a36Sopenharmony_ci#define ICnMC_VCSEL(x) ((x) << 22) 7862306a36Sopenharmony_ci#define ICnMC_INF_MASK GENMASK(21, 16) 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* CRU Module Status Register */ 8162306a36Sopenharmony_ci#define ICnMS 0x254 8262306a36Sopenharmony_ci#define ICnMS_IA BIT(2) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* CRU Data Output Mode Register */ 8562306a36Sopenharmony_ci#define ICnDMR 0x26c 8662306a36Sopenharmony_ci#define ICnDMR_YCMODE_UYVY (1 << 4) 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define RZG2L_TIMEOUT_MS 100 8962306a36Sopenharmony_ci#define RZG2L_RETRIES 10 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define RZG2L_CRU_DEFAULT_FORMAT V4L2_PIX_FMT_UYVY 9262306a36Sopenharmony_ci#define RZG2L_CRU_DEFAULT_WIDTH RZG2L_CRU_MIN_INPUT_WIDTH 9362306a36Sopenharmony_ci#define RZG2L_CRU_DEFAULT_HEIGHT RZG2L_CRU_MIN_INPUT_HEIGHT 9462306a36Sopenharmony_ci#define RZG2L_CRU_DEFAULT_FIELD V4L2_FIELD_NONE 9562306a36Sopenharmony_ci#define RZG2L_CRU_DEFAULT_COLORSPACE V4L2_COLORSPACE_SRGB 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistruct rzg2l_cru_buffer { 9862306a36Sopenharmony_ci struct vb2_v4l2_buffer vb; 9962306a36Sopenharmony_ci struct list_head list; 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define to_buf_list(vb2_buffer) \ 10362306a36Sopenharmony_ci (&container_of(vb2_buffer, struct rzg2l_cru_buffer, vb)->list) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 10662306a36Sopenharmony_ci * DMA operations 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_cistatic void rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci iowrite32(value, cru->base + offset); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic u32 rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci return ioread32(cru->base + offset); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* Need to hold qlock before calling */ 11962306a36Sopenharmony_cistatic void return_unused_buffers(struct rzg2l_cru_dev *cru, 12062306a36Sopenharmony_ci enum vb2_buffer_state state) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct rzg2l_cru_buffer *buf, *node; 12362306a36Sopenharmony_ci unsigned long flags; 12462306a36Sopenharmony_ci unsigned int i; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci spin_lock_irqsave(&cru->qlock, flags); 12762306a36Sopenharmony_ci for (i = 0; i < cru->num_buf; i++) { 12862306a36Sopenharmony_ci if (cru->queue_buf[i]) { 12962306a36Sopenharmony_ci vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, 13062306a36Sopenharmony_ci state); 13162306a36Sopenharmony_ci cru->queue_buf[i] = NULL; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci list_for_each_entry_safe(buf, node, &cru->buf_list, list) { 13662306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 13762306a36Sopenharmony_ci list_del(&buf->list); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci spin_unlock_irqrestore(&cru->qlock, flags); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int rzg2l_cru_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, 14362306a36Sopenharmony_ci unsigned int *nplanes, unsigned int sizes[], 14462306a36Sopenharmony_ci struct device *alloc_devs[]) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Make sure the image size is large enough. */ 14962306a36Sopenharmony_ci if (*nplanes) 15062306a36Sopenharmony_ci return sizes[0] < cru->format.sizeimage ? -EINVAL : 0; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci *nplanes = 1; 15362306a36Sopenharmony_ci sizes[0] = cru->format.sizeimage; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int rzg2l_cru_buffer_prepare(struct vb2_buffer *vb) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); 16162306a36Sopenharmony_ci unsigned long size = cru->format.sizeimage; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 16462306a36Sopenharmony_ci dev_err(cru->dev, "buffer too small (%lu < %lu)\n", 16562306a36Sopenharmony_ci vb2_plane_size(vb, 0), size); 16662306a36Sopenharmony_ci return -EINVAL; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void rzg2l_cru_buffer_queue(struct vb2_buffer *vb) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 17762306a36Sopenharmony_ci struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); 17862306a36Sopenharmony_ci unsigned long flags; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci spin_lock_irqsave(&cru->qlock, flags); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci list_add_tail(to_buf_list(vbuf), &cru->buf_list); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci spin_unlock_irqrestore(&cru->qlock, flags); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int rzg2l_cru_mc_validate_format(struct rzg2l_cru_dev *cru, 18862306a36Sopenharmony_ci struct v4l2_subdev *sd, 18962306a36Sopenharmony_ci struct media_pad *pad) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct v4l2_subdev_format fmt = { 19262306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 19362306a36Sopenharmony_ci }; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci fmt.pad = pad->index; 19662306a36Sopenharmony_ci if (v4l2_subdev_call_state_active(sd, pad, get_fmt, &fmt)) 19762306a36Sopenharmony_ci return -EPIPE; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci switch (fmt.format.code) { 20062306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1X16: 20162306a36Sopenharmony_ci break; 20262306a36Sopenharmony_ci default: 20362306a36Sopenharmony_ci return -EPIPE; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci switch (fmt.format.field) { 20762306a36Sopenharmony_ci case V4L2_FIELD_TOP: 20862306a36Sopenharmony_ci case V4L2_FIELD_BOTTOM: 20962306a36Sopenharmony_ci case V4L2_FIELD_NONE: 21062306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_TB: 21162306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_BT: 21262306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED: 21362306a36Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 21462306a36Sopenharmony_ci case V4L2_FIELD_SEQ_BT: 21562306a36Sopenharmony_ci break; 21662306a36Sopenharmony_ci default: 21762306a36Sopenharmony_ci return -EPIPE; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (fmt.format.width != cru->format.width || 22162306a36Sopenharmony_ci fmt.format.height != cru->format.height) 22262306a36Sopenharmony_ci return -EPIPE; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, 22862306a36Sopenharmony_ci int slot, dma_addr_t addr) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci /* 23162306a36Sopenharmony_ci * The address needs to be 512 bytes aligned. Driver should never accept 23262306a36Sopenharmony_ci * settings that do not satisfy this in the first place... 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci if (WARN_ON((addr) & RZG2L_CRU_HW_BUFFER_MASK)) 23562306a36Sopenharmony_ci return; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Currently, we just use the buffer in 32 bits address */ 23862306a36Sopenharmony_ci rzg2l_cru_write(cru, AMnMBxADDRL(slot), addr); 23962306a36Sopenharmony_ci rzg2l_cru_write(cru, AMnMBxADDRH(slot), 0); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/* 24362306a36Sopenharmony_ci * Moves a buffer from the queue to the HW slot. If no buffer is 24462306a36Sopenharmony_ci * available use the scratch buffer. The scratch buffer is never 24562306a36Sopenharmony_ci * returned to userspace, its only function is to enable the capture 24662306a36Sopenharmony_ci * loop to keep running. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_cistatic void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf; 25162306a36Sopenharmony_ci struct rzg2l_cru_buffer *buf; 25262306a36Sopenharmony_ci dma_addr_t phys_addr; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* A already populated slot shall never be overwritten. */ 25562306a36Sopenharmony_ci if (WARN_ON(cru->queue_buf[slot])) 25662306a36Sopenharmony_ci return; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci dev_dbg(cru->dev, "Filling HW slot: %d\n", slot); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (list_empty(&cru->buf_list)) { 26162306a36Sopenharmony_ci cru->queue_buf[slot] = NULL; 26262306a36Sopenharmony_ci phys_addr = cru->scratch_phys; 26362306a36Sopenharmony_ci } else { 26462306a36Sopenharmony_ci /* Keep track of buffer we give to HW */ 26562306a36Sopenharmony_ci buf = list_entry(cru->buf_list.next, 26662306a36Sopenharmony_ci struct rzg2l_cru_buffer, list); 26762306a36Sopenharmony_ci vbuf = &buf->vb; 26862306a36Sopenharmony_ci list_del_init(to_buf_list(vbuf)); 26962306a36Sopenharmony_ci cru->queue_buf[slot] = vbuf; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Setup DMA */ 27262306a36Sopenharmony_ci phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci rzg2l_cru_set_slot_addr(cru, slot, phys_addr); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci unsigned int slot; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * Set image data memory banks. 28462306a36Sopenharmony_ci * Currently, we will use maximum address. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci rzg2l_cru_write(cru, AMnMBVALID, AMnMBVALID_MBVALID(cru->num_buf - 1)); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci for (slot = 0; slot < cru->num_buf; slot++) 28962306a36Sopenharmony_ci rzg2l_cru_fill_hw_slot(cru, slot); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, bool *input_is_yuv, 29362306a36Sopenharmony_ci struct v4l2_mbus_framefmt *ip_sd_fmt) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci u32 icnmc; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci switch (ip_sd_fmt->code) { 29862306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1X16: 29962306a36Sopenharmony_ci icnmc = ICnMC_INF_YUV8_422; 30062306a36Sopenharmony_ci *input_is_yuv = true; 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci default: 30362306a36Sopenharmony_ci *input_is_yuv = false; 30462306a36Sopenharmony_ci icnmc = ICnMC_INF_USER; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci icnmc |= (rzg2l_cru_read(cru, ICnMC) & ~ICnMC_INF_MASK); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Set virtual channel CSI2 */ 31162306a36Sopenharmony_ci icnmc |= ICnMC_VCSEL(cru->csi.channel); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci rzg2l_cru_write(cru, ICnMC, icnmc); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, 31762306a36Sopenharmony_ci struct v4l2_mbus_framefmt *ip_sd_fmt) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci bool output_is_yuv = false; 32062306a36Sopenharmony_ci bool input_is_yuv = false; 32162306a36Sopenharmony_ci u32 icndmr; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci rzg2l_cru_csi2_setup(cru, &input_is_yuv, ip_sd_fmt); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Output format */ 32662306a36Sopenharmony_ci switch (cru->format.pixelformat) { 32762306a36Sopenharmony_ci case V4L2_PIX_FMT_UYVY: 32862306a36Sopenharmony_ci icndmr = ICnDMR_YCMODE_UYVY; 32962306a36Sopenharmony_ci output_is_yuv = true; 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci default: 33262306a36Sopenharmony_ci dev_err(cru->dev, "Invalid pixelformat (0x%x)\n", 33362306a36Sopenharmony_ci cru->format.pixelformat); 33462306a36Sopenharmony_ci return -EINVAL; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* If input and output use same colorspace, do bypass mode */ 33862306a36Sopenharmony_ci if (output_is_yuv == input_is_yuv) 33962306a36Sopenharmony_ci rzg2l_cru_write(cru, ICnMC, 34062306a36Sopenharmony_ci rzg2l_cru_read(cru, ICnMC) | ICnMC_CSCTHR); 34162306a36Sopenharmony_ci else 34262306a36Sopenharmony_ci rzg2l_cru_write(cru, ICnMC, 34362306a36Sopenharmony_ci rzg2l_cru_read(cru, ICnMC) & (~ICnMC_CSCTHR)); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Set output data format */ 34662306a36Sopenharmony_ci rzg2l_cru_write(cru, ICnDMR, icndmr); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_civoid rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci u32 amnfifopntr, amnfifopntr_w, amnfifopntr_r_y; 35462306a36Sopenharmony_ci unsigned int retries = 0; 35562306a36Sopenharmony_ci unsigned long flags; 35662306a36Sopenharmony_ci u32 icnms; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci spin_lock_irqsave(&cru->qlock, flags); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Disable and clear the interrupt */ 36162306a36Sopenharmony_ci rzg2l_cru_write(cru, CRUnIE, 0); 36262306a36Sopenharmony_ci rzg2l_cru_write(cru, CRUnINTS, 0x001F0F0F); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Stop the operation of image conversion */ 36562306a36Sopenharmony_ci rzg2l_cru_write(cru, ICnEN, 0); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Wait for streaming to stop */ 36862306a36Sopenharmony_ci while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) { 36962306a36Sopenharmony_ci spin_unlock_irqrestore(&cru->qlock, flags); 37062306a36Sopenharmony_ci msleep(RZG2L_TIMEOUT_MS); 37162306a36Sopenharmony_ci spin_lock_irqsave(&cru->qlock, flags); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci icnms = rzg2l_cru_read(cru, ICnMS) & ICnMS_IA; 37562306a36Sopenharmony_ci if (icnms) 37662306a36Sopenharmony_ci dev_err(cru->dev, "Failed stop HW, something is seriously broken\n"); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci cru->state = RZG2L_CRU_DMA_STOPPED; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* Wait until the FIFO becomes empty */ 38162306a36Sopenharmony_ci for (retries = 5; retries > 0; retries--) { 38262306a36Sopenharmony_ci amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR; 38562306a36Sopenharmony_ci amnfifopntr_r_y = 38662306a36Sopenharmony_ci (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16; 38762306a36Sopenharmony_ci if (amnfifopntr_w == amnfifopntr_r_y) 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci usleep_range(10, 20); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* Notify that FIFO is not empty here */ 39462306a36Sopenharmony_ci if (!retries) 39562306a36Sopenharmony_ci dev_err(cru->dev, "Failed to empty FIFO\n"); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Stop AXI bus */ 39862306a36Sopenharmony_ci rzg2l_cru_write(cru, AMnAXISTP, AMnAXISTP_AXI_STOP); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* Wait until the AXI bus stop */ 40162306a36Sopenharmony_ci for (retries = 5; retries > 0; retries--) { 40262306a36Sopenharmony_ci if (rzg2l_cru_read(cru, AMnAXISTPACK) & 40362306a36Sopenharmony_ci AMnAXISTPACK_AXI_STOP_ACK) 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci usleep_range(10, 20); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Notify that AXI bus can not stop here */ 41062306a36Sopenharmony_ci if (!retries) 41162306a36Sopenharmony_ci dev_err(cru->dev, "Failed to stop AXI bus\n"); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Cancel the AXI bus stop request */ 41462306a36Sopenharmony_ci rzg2l_cru_write(cru, AMnAXISTP, 0); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* Reset the CRU (AXI-master) */ 41762306a36Sopenharmony_ci reset_control_assert(cru->aresetn); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Resets the image processing module */ 42062306a36Sopenharmony_ci rzg2l_cru_write(cru, CRUnRST, 0); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci spin_unlock_irqrestore(&cru->qlock, flags); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ciint rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru); 42862306a36Sopenharmony_ci unsigned long flags; 42962306a36Sopenharmony_ci int ret; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci spin_lock_irqsave(&cru->qlock, flags); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Initialize image convert */ 43462306a36Sopenharmony_ci ret = rzg2l_cru_initialize_image_conv(cru, fmt); 43562306a36Sopenharmony_ci if (ret) { 43662306a36Sopenharmony_ci spin_unlock_irqrestore(&cru->qlock, flags); 43762306a36Sopenharmony_ci return ret; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* Select a video input */ 44162306a36Sopenharmony_ci rzg2l_cru_write(cru, CRUnCTRL, CRUnCTRL_VINSEL(0)); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* Cancel the software reset for image processing block */ 44462306a36Sopenharmony_ci rzg2l_cru_write(cru, CRUnRST, CRUnRST_VRESETN); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* Disable and clear the interrupt before using */ 44762306a36Sopenharmony_ci rzg2l_cru_write(cru, CRUnIE, 0); 44862306a36Sopenharmony_ci rzg2l_cru_write(cru, CRUnINTS, 0x001f000f); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Initialize the AXI master */ 45162306a36Sopenharmony_ci rzg2l_cru_initialize_axi(cru); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* Enable interrupt */ 45462306a36Sopenharmony_ci rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Enable image processing reception */ 45762306a36Sopenharmony_ci rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci spin_unlock_irqrestore(&cru->qlock, flags); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_civoid rzg2l_cru_vclk_unprepare(struct rzg2l_cru_dev *cru) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci clk_disable_unprepare(cru->vclk); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ciint rzg2l_cru_vclk_prepare(struct rzg2l_cru_dev *cru) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci return clk_prepare_enable(cru->vclk); 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int rzg2l_cru_set_stream(struct rzg2l_cru_dev *cru, int on) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct media_pipeline *pipe; 47762306a36Sopenharmony_ci struct v4l2_subdev *sd; 47862306a36Sopenharmony_ci struct media_pad *pad; 47962306a36Sopenharmony_ci int ret; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci pad = media_pad_remote_pad_first(&cru->pad); 48262306a36Sopenharmony_ci if (!pad) 48362306a36Sopenharmony_ci return -EPIPE; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci sd = media_entity_to_v4l2_subdev(pad->entity); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (!on) { 48862306a36Sopenharmony_ci int stream_off_ret = 0; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci ret = v4l2_subdev_call(sd, video, s_stream, 0); 49162306a36Sopenharmony_ci if (ret) 49262306a36Sopenharmony_ci stream_off_ret = ret; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci ret = v4l2_subdev_call(sd, video, post_streamoff); 49562306a36Sopenharmony_ci if (ret == -ENOIOCTLCMD) 49662306a36Sopenharmony_ci ret = 0; 49762306a36Sopenharmony_ci if (ret && !stream_off_ret) 49862306a36Sopenharmony_ci stream_off_ret = ret; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci video_device_pipeline_stop(&cru->vdev); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci pm_runtime_put_sync(cru->dev); 50362306a36Sopenharmony_ci clk_disable_unprepare(cru->vclk); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci return stream_off_ret; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(cru->dev); 50962306a36Sopenharmony_ci if (ret) 51062306a36Sopenharmony_ci return ret; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci ret = clk_prepare_enable(cru->vclk); 51362306a36Sopenharmony_ci if (ret) 51462306a36Sopenharmony_ci goto err_pm_put; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci ret = rzg2l_cru_mc_validate_format(cru, sd, pad); 51762306a36Sopenharmony_ci if (ret) 51862306a36Sopenharmony_ci goto err_vclk_disable; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci pipe = media_entity_pipeline(&sd->entity) ? : &cru->vdev.pipe; 52162306a36Sopenharmony_ci ret = video_device_pipeline_start(&cru->vdev, pipe); 52262306a36Sopenharmony_ci if (ret) 52362306a36Sopenharmony_ci goto err_vclk_disable; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ret = v4l2_subdev_call(sd, video, pre_streamon, 0); 52662306a36Sopenharmony_ci if (ret == -ENOIOCTLCMD) 52762306a36Sopenharmony_ci ret = 0; 52862306a36Sopenharmony_ci if (ret) 52962306a36Sopenharmony_ci goto pipe_line_stop; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci ret = v4l2_subdev_call(sd, video, s_stream, 1); 53262306a36Sopenharmony_ci if (ret == -ENOIOCTLCMD) 53362306a36Sopenharmony_ci ret = 0; 53462306a36Sopenharmony_ci if (ret) 53562306a36Sopenharmony_ci goto err_s_stream; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci return 0; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cierr_s_stream: 54062306a36Sopenharmony_ci v4l2_subdev_call(sd, video, post_streamoff); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cipipe_line_stop: 54362306a36Sopenharmony_ci video_device_pipeline_stop(&cru->vdev); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cierr_vclk_disable: 54662306a36Sopenharmony_ci clk_disable_unprepare(cru->vclk); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cierr_pm_put: 54962306a36Sopenharmony_ci pm_runtime_put_sync(cru->dev); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return ret; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci cru->state = RZG2L_CRU_DMA_STOPPING; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci rzg2l_cru_set_stream(cru, 0); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic irqreturn_t rzg2l_cru_irq(int irq, void *data) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct rzg2l_cru_dev *cru = data; 56462306a36Sopenharmony_ci unsigned int handled = 0; 56562306a36Sopenharmony_ci unsigned long flags; 56662306a36Sopenharmony_ci u32 irq_status; 56762306a36Sopenharmony_ci u32 amnmbs; 56862306a36Sopenharmony_ci int slot; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci spin_lock_irqsave(&cru->qlock, flags); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci irq_status = rzg2l_cru_read(cru, CRUnINTS); 57362306a36Sopenharmony_ci if (!irq_status) 57462306a36Sopenharmony_ci goto done; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci handled = 1; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ 58162306a36Sopenharmony_ci if (cru->state == RZG2L_CRU_DMA_STOPPED) { 58262306a36Sopenharmony_ci dev_dbg(cru->dev, "IRQ while state stopped\n"); 58362306a36Sopenharmony_ci goto done; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Increase stop retries if capture status is 'RZG2L_CRU_DMA_STOPPING' */ 58762306a36Sopenharmony_ci if (cru->state == RZG2L_CRU_DMA_STOPPING) { 58862306a36Sopenharmony_ci if (irq_status & CRUnINTS_SFS) 58962306a36Sopenharmony_ci dev_dbg(cru->dev, "IRQ while state stopping\n"); 59062306a36Sopenharmony_ci goto done; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Prepare for capture and update state */ 59462306a36Sopenharmony_ci amnmbs = rzg2l_cru_read(cru, AMnMBS); 59562306a36Sopenharmony_ci slot = amnmbs & AMnMBS_MBSTS; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* 59862306a36Sopenharmony_ci * AMnMBS.MBSTS indicates the destination of Memory Bank (MB). 59962306a36Sopenharmony_ci * Recalculate to get the current transfer complete MB. 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_ci if (slot == 0) 60262306a36Sopenharmony_ci slot = cru->num_buf - 1; 60362306a36Sopenharmony_ci else 60462306a36Sopenharmony_ci slot--; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * To hand buffers back in a known order to userspace start 60862306a36Sopenharmony_ci * to capture first from slot 0. 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci if (cru->state == RZG2L_CRU_DMA_STARTING) { 61162306a36Sopenharmony_ci if (slot != 0) { 61262306a36Sopenharmony_ci dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); 61362306a36Sopenharmony_ci goto done; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci dev_dbg(cru->dev, "Capture start synced!\n"); 61762306a36Sopenharmony_ci cru->state = RZG2L_CRU_DMA_RUNNING; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* Capture frame */ 62162306a36Sopenharmony_ci if (cru->queue_buf[slot]) { 62262306a36Sopenharmony_ci cru->queue_buf[slot]->field = cru->format.field; 62362306a36Sopenharmony_ci cru->queue_buf[slot]->sequence = cru->sequence; 62462306a36Sopenharmony_ci cru->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); 62562306a36Sopenharmony_ci vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, 62662306a36Sopenharmony_ci VB2_BUF_STATE_DONE); 62762306a36Sopenharmony_ci cru->queue_buf[slot] = NULL; 62862306a36Sopenharmony_ci } else { 62962306a36Sopenharmony_ci /* Scratch buffer was used, dropping frame. */ 63062306a36Sopenharmony_ci dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci cru->sequence++; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* Prepare for next frame */ 63662306a36Sopenharmony_ci rzg2l_cru_fill_hw_slot(cru, slot); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cidone: 63962306a36Sopenharmony_ci spin_unlock_irqrestore(&cru->qlock, flags); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return IRQ_RETVAL(handled); 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 64762306a36Sopenharmony_ci int ret; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* Release reset state */ 65062306a36Sopenharmony_ci ret = reset_control_deassert(cru->aresetn); 65162306a36Sopenharmony_ci if (ret) { 65262306a36Sopenharmony_ci dev_err(cru->dev, "failed to deassert aresetn\n"); 65362306a36Sopenharmony_ci return ret; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci ret = reset_control_deassert(cru->presetn); 65762306a36Sopenharmony_ci if (ret) { 65862306a36Sopenharmony_ci reset_control_assert(cru->aresetn); 65962306a36Sopenharmony_ci dev_err(cru->dev, "failed to deassert presetn\n"); 66062306a36Sopenharmony_ci return ret; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci ret = request_irq(cru->image_conv_irq, rzg2l_cru_irq, 66462306a36Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, cru); 66562306a36Sopenharmony_ci if (ret) { 66662306a36Sopenharmony_ci dev_err(cru->dev, "failed to request irq\n"); 66762306a36Sopenharmony_ci goto assert_resets; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* Allocate scratch buffer. */ 67162306a36Sopenharmony_ci cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage, 67262306a36Sopenharmony_ci &cru->scratch_phys, GFP_KERNEL); 67362306a36Sopenharmony_ci if (!cru->scratch) { 67462306a36Sopenharmony_ci return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); 67562306a36Sopenharmony_ci dev_err(cru->dev, "Failed to allocate scratch buffer\n"); 67662306a36Sopenharmony_ci ret = -ENOMEM; 67762306a36Sopenharmony_ci goto free_image_conv_irq; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci cru->sequence = 0; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci ret = rzg2l_cru_set_stream(cru, 1); 68362306a36Sopenharmony_ci if (ret) { 68462306a36Sopenharmony_ci return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); 68562306a36Sopenharmony_ci goto out; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci cru->state = RZG2L_CRU_DMA_STARTING; 68962306a36Sopenharmony_ci dev_dbg(cru->dev, "Starting to capture\n"); 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ciout: 69362306a36Sopenharmony_ci if (ret) 69462306a36Sopenharmony_ci dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch, 69562306a36Sopenharmony_ci cru->scratch_phys); 69662306a36Sopenharmony_cifree_image_conv_irq: 69762306a36Sopenharmony_ci free_irq(cru->image_conv_irq, cru); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ciassert_resets: 70062306a36Sopenharmony_ci reset_control_assert(cru->presetn); 70162306a36Sopenharmony_ci reset_control_assert(cru->aresetn); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return ret; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic void rzg2l_cru_stop_streaming_vq(struct vb2_queue *vq) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci rzg2l_cru_stop_streaming(cru); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* Free scratch buffer */ 71362306a36Sopenharmony_ci dma_free_coherent(cru->dev, cru->format.sizeimage, 71462306a36Sopenharmony_ci cru->scratch, cru->scratch_phys); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci free_irq(cru->image_conv_irq, cru); 71762306a36Sopenharmony_ci reset_control_assert(cru->presetn); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return_unused_buffers(cru, VB2_BUF_STATE_ERROR); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic const struct vb2_ops rzg2l_cru_qops = { 72362306a36Sopenharmony_ci .queue_setup = rzg2l_cru_queue_setup, 72462306a36Sopenharmony_ci .buf_prepare = rzg2l_cru_buffer_prepare, 72562306a36Sopenharmony_ci .buf_queue = rzg2l_cru_buffer_queue, 72662306a36Sopenharmony_ci .start_streaming = rzg2l_cru_start_streaming_vq, 72762306a36Sopenharmony_ci .stop_streaming = rzg2l_cru_stop_streaming_vq, 72862306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 72962306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 73062306a36Sopenharmony_ci}; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_civoid rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci mutex_destroy(&cru->lock); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci v4l2_device_unregister(&cru->v4l2_dev); 73762306a36Sopenharmony_ci vb2_queue_release(&cru->queue); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ciint rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct vb2_queue *q = &cru->queue; 74362306a36Sopenharmony_ci unsigned int i; 74462306a36Sopenharmony_ci int ret; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Initialize the top-level structure */ 74762306a36Sopenharmony_ci ret = v4l2_device_register(cru->dev, &cru->v4l2_dev); 74862306a36Sopenharmony_ci if (ret) 74962306a36Sopenharmony_ci return ret; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci mutex_init(&cru->lock); 75262306a36Sopenharmony_ci INIT_LIST_HEAD(&cru->buf_list); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci spin_lock_init(&cru->qlock); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci cru->state = RZG2L_CRU_DMA_STOPPED; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci for (i = 0; i < RZG2L_CRU_HW_BUFFER_MAX; i++) 75962306a36Sopenharmony_ci cru->queue_buf[i] = NULL; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* buffer queue */ 76262306a36Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 76362306a36Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_DMABUF; 76462306a36Sopenharmony_ci q->lock = &cru->lock; 76562306a36Sopenharmony_ci q->drv_priv = cru; 76662306a36Sopenharmony_ci q->buf_struct_size = sizeof(struct rzg2l_cru_buffer); 76762306a36Sopenharmony_ci q->ops = &rzg2l_cru_qops; 76862306a36Sopenharmony_ci q->mem_ops = &vb2_dma_contig_memops; 76962306a36Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 77062306a36Sopenharmony_ci q->min_buffers_needed = 4; 77162306a36Sopenharmony_ci q->dev = cru->dev; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci ret = vb2_queue_init(q); 77462306a36Sopenharmony_ci if (ret < 0) { 77562306a36Sopenharmony_ci dev_err(cru->dev, "failed to initialize VB2 queue\n"); 77662306a36Sopenharmony_ci goto error; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci return 0; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cierror: 78262306a36Sopenharmony_ci mutex_destroy(&cru->lock); 78362306a36Sopenharmony_ci v4l2_device_unregister(&cru->v4l2_dev); 78462306a36Sopenharmony_ci return ret; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 78862306a36Sopenharmony_ci * V4L2 stuff 78962306a36Sopenharmony_ci */ 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic const struct v4l2_format_info rzg2l_cru_formats[] = { 79262306a36Sopenharmony_ci { 79362306a36Sopenharmony_ci .format = V4L2_PIX_FMT_UYVY, 79462306a36Sopenharmony_ci .bpp[0] = 2, 79562306a36Sopenharmony_ci }, 79662306a36Sopenharmony_ci}; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ciconst struct v4l2_format_info *rzg2l_cru_format_from_pixel(u32 format) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci unsigned int i; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rzg2l_cru_formats); i++) 80362306a36Sopenharmony_ci if (rzg2l_cru_formats[i].format == format) 80462306a36Sopenharmony_ci return rzg2l_cru_formats + i; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci return NULL; 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic u32 rzg2l_cru_format_bytesperline(struct v4l2_pix_format *pix) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci const struct v4l2_format_info *fmt; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci fmt = rzg2l_cru_format_from_pixel(pix->pixelformat); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (WARN_ON(!fmt)) 81662306a36Sopenharmony_ci return -EINVAL; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci return pix->width * fmt->bpp[0]; 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_cistatic u32 rzg2l_cru_format_sizeimage(struct v4l2_pix_format *pix) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci return pix->bytesperline * pix->height; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, 82762306a36Sopenharmony_ci struct v4l2_pix_format *pix) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci if (!rzg2l_cru_format_from_pixel(pix->pixelformat)) 83062306a36Sopenharmony_ci pix->pixelformat = RZG2L_CRU_DEFAULT_FORMAT; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci switch (pix->field) { 83362306a36Sopenharmony_ci case V4L2_FIELD_TOP: 83462306a36Sopenharmony_ci case V4L2_FIELD_BOTTOM: 83562306a36Sopenharmony_ci case V4L2_FIELD_NONE: 83662306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_TB: 83762306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED_BT: 83862306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED: 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci default: 84162306a36Sopenharmony_ci pix->field = RZG2L_CRU_DEFAULT_FIELD; 84262306a36Sopenharmony_ci break; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci /* Limit to CRU capabilities */ 84662306a36Sopenharmony_ci v4l_bound_align_image(&pix->width, 320, RZG2L_CRU_MAX_INPUT_WIDTH, 1, 84762306a36Sopenharmony_ci &pix->height, 240, RZG2L_CRU_MAX_INPUT_HEIGHT, 2, 0); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci pix->bytesperline = rzg2l_cru_format_bytesperline(pix); 85062306a36Sopenharmony_ci pix->sizeimage = rzg2l_cru_format_sizeimage(pix); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n", 85362306a36Sopenharmony_ci pix->width, pix->height, pix->bytesperline, pix->sizeimage); 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic void rzg2l_cru_try_format(struct rzg2l_cru_dev *cru, 85762306a36Sopenharmony_ci struct v4l2_pix_format *pix) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci /* 86062306a36Sopenharmony_ci * The V4L2 specification clearly documents the colorspace fields 86162306a36Sopenharmony_ci * as being set by drivers for capture devices. Using the values 86262306a36Sopenharmony_ci * supplied by userspace thus wouldn't comply with the API. Until 86362306a36Sopenharmony_ci * the API is updated force fixed values. 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_ci pix->colorspace = RZG2L_CRU_DEFAULT_COLORSPACE; 86662306a36Sopenharmony_ci pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); 86762306a36Sopenharmony_ci pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); 86862306a36Sopenharmony_ci pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, pix->colorspace, 86962306a36Sopenharmony_ci pix->ycbcr_enc); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci rzg2l_cru_format_align(cru, pix); 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic int rzg2l_cru_querycap(struct file *file, void *priv, 87562306a36Sopenharmony_ci struct v4l2_capability *cap) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); 87862306a36Sopenharmony_ci strscpy(cap->card, "RZG2L_CRU", sizeof(cap->card)); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci return 0; 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic int rzg2l_cru_try_fmt_vid_cap(struct file *file, void *priv, 88462306a36Sopenharmony_ci struct v4l2_format *f) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci struct rzg2l_cru_dev *cru = video_drvdata(file); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci rzg2l_cru_try_format(cru, &f->fmt.pix); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic int rzg2l_cru_s_fmt_vid_cap(struct file *file, void *priv, 89462306a36Sopenharmony_ci struct v4l2_format *f) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci struct rzg2l_cru_dev *cru = video_drvdata(file); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (vb2_is_busy(&cru->queue)) 89962306a36Sopenharmony_ci return -EBUSY; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci rzg2l_cru_try_format(cru, &f->fmt.pix); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci cru->format = f->fmt.pix; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci return 0; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic int rzg2l_cru_g_fmt_vid_cap(struct file *file, void *priv, 90962306a36Sopenharmony_ci struct v4l2_format *f) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci struct rzg2l_cru_dev *cru = video_drvdata(file); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci f->fmt.pix = cru->format; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci return 0; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic int rzg2l_cru_enum_fmt_vid_cap(struct file *file, void *priv, 91962306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci if (f->index >= ARRAY_SIZE(rzg2l_cru_formats)) 92262306a36Sopenharmony_ci return -EINVAL; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci f->pixelformat = rzg2l_cru_formats[f->index].format; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci return 0; 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops rzg2l_cru_ioctl_ops = { 93062306a36Sopenharmony_ci .vidioc_querycap = rzg2l_cru_querycap, 93162306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = rzg2l_cru_try_fmt_vid_cap, 93262306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = rzg2l_cru_g_fmt_vid_cap, 93362306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = rzg2l_cru_s_fmt_vid_cap, 93462306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = rzg2l_cru_enum_fmt_vid_cap, 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 93762306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 93862306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 93962306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 94062306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 94162306a36Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 94262306a36Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 94362306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 94462306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 94562306a36Sopenharmony_ci}; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 94862306a36Sopenharmony_ci * Media controller file operations 94962306a36Sopenharmony_ci */ 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int rzg2l_cru_open(struct file *file) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct rzg2l_cru_dev *cru = video_drvdata(file); 95462306a36Sopenharmony_ci int ret; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci ret = mutex_lock_interruptible(&cru->lock); 95762306a36Sopenharmony_ci if (ret) 95862306a36Sopenharmony_ci return ret; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci file->private_data = cru; 96162306a36Sopenharmony_ci ret = v4l2_fh_open(file); 96262306a36Sopenharmony_ci if (ret) 96362306a36Sopenharmony_ci goto err_unlock; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci mutex_unlock(&cru->lock); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci return 0; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cierr_unlock: 97062306a36Sopenharmony_ci mutex_unlock(&cru->lock); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return ret; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic int rzg2l_cru_release(struct file *file) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct rzg2l_cru_dev *cru = video_drvdata(file); 97862306a36Sopenharmony_ci int ret; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci mutex_lock(&cru->lock); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* the release helper will cleanup any on-going streaming. */ 98362306a36Sopenharmony_ci ret = _vb2_fop_release(file, NULL); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci mutex_unlock(&cru->lock); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return ret; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic const struct v4l2_file_operations rzg2l_cru_fops = { 99162306a36Sopenharmony_ci .owner = THIS_MODULE, 99262306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 99362306a36Sopenharmony_ci .open = rzg2l_cru_open, 99462306a36Sopenharmony_ci .release = rzg2l_cru_release, 99562306a36Sopenharmony_ci .poll = vb2_fop_poll, 99662306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 99762306a36Sopenharmony_ci .read = vb2_fop_read, 99862306a36Sopenharmony_ci}; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_cistatic void rzg2l_cru_v4l2_init(struct rzg2l_cru_dev *cru) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci struct video_device *vdev = &cru->vdev; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci vdev->v4l2_dev = &cru->v4l2_dev; 100562306a36Sopenharmony_ci vdev->queue = &cru->queue; 100662306a36Sopenharmony_ci snprintf(vdev->name, sizeof(vdev->name), "CRU output"); 100762306a36Sopenharmony_ci vdev->release = video_device_release_empty; 100862306a36Sopenharmony_ci vdev->lock = &cru->lock; 100962306a36Sopenharmony_ci vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 101062306a36Sopenharmony_ci vdev->device_caps |= V4L2_CAP_IO_MC; 101162306a36Sopenharmony_ci vdev->fops = &rzg2l_cru_fops; 101262306a36Sopenharmony_ci vdev->ioctl_ops = &rzg2l_cru_ioctl_ops; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci /* Set a default format */ 101562306a36Sopenharmony_ci cru->format.pixelformat = RZG2L_CRU_DEFAULT_FORMAT; 101662306a36Sopenharmony_ci cru->format.width = RZG2L_CRU_DEFAULT_WIDTH; 101762306a36Sopenharmony_ci cru->format.height = RZG2L_CRU_DEFAULT_HEIGHT; 101862306a36Sopenharmony_ci cru->format.field = RZG2L_CRU_DEFAULT_FIELD; 101962306a36Sopenharmony_ci cru->format.colorspace = RZG2L_CRU_DEFAULT_COLORSPACE; 102062306a36Sopenharmony_ci rzg2l_cru_format_align(cru, &cru->format); 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_civoid rzg2l_cru_video_unregister(struct rzg2l_cru_dev *cru) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci media_device_unregister(&cru->mdev); 102662306a36Sopenharmony_ci video_unregister_device(&cru->vdev); 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ciint rzg2l_cru_video_register(struct rzg2l_cru_dev *cru) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci struct video_device *vdev = &cru->vdev; 103262306a36Sopenharmony_ci int ret; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (video_is_registered(&cru->vdev)) { 103562306a36Sopenharmony_ci struct media_entity *entity; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci entity = &cru->vdev.entity; 103862306a36Sopenharmony_ci if (!entity->graph_obj.mdev) 103962306a36Sopenharmony_ci entity->graph_obj.mdev = &cru->mdev; 104062306a36Sopenharmony_ci return 0; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci rzg2l_cru_v4l2_init(cru); 104462306a36Sopenharmony_ci video_set_drvdata(vdev, cru); 104562306a36Sopenharmony_ci ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 104662306a36Sopenharmony_ci if (ret) { 104762306a36Sopenharmony_ci dev_err(cru->dev, "Failed to register video device\n"); 104862306a36Sopenharmony_ci return ret; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci ret = media_device_register(&cru->mdev); 105262306a36Sopenharmony_ci if (ret) { 105362306a36Sopenharmony_ci video_unregister_device(&cru->vdev); 105462306a36Sopenharmony_ci return ret; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci return 0; 105862306a36Sopenharmony_ci} 1059