107fc05bdSLad Prabhakar // SPDX-License-Identifier: GPL-2.0+ 207fc05bdSLad Prabhakar /* 307fc05bdSLad Prabhakar * Driver for Renesas RZ/G2L CRU 407fc05bdSLad Prabhakar * 507fc05bdSLad Prabhakar * Copyright (C) 2022 Renesas Electronics Corp. 607fc05bdSLad Prabhakar * 707fc05bdSLad Prabhakar * Based on Renesas R-Car VIN 807fc05bdSLad Prabhakar * Copyright (C) 2016 Renesas Electronics Corp. 907fc05bdSLad Prabhakar * Copyright (C) 2011-2013 Renesas Solutions Corp. 1007fc05bdSLad Prabhakar * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> 1107fc05bdSLad Prabhakar * Copyright (C) 2008 Magnus Damm 1207fc05bdSLad Prabhakar */ 1307fc05bdSLad Prabhakar 1407fc05bdSLad Prabhakar #include <linux/clk.h> 1507fc05bdSLad Prabhakar #include <linux/delay.h> 1607fc05bdSLad Prabhakar #include <linux/pm_runtime.h> 1707fc05bdSLad Prabhakar 1807fc05bdSLad Prabhakar #include <media/v4l2-ioctl.h> 1907fc05bdSLad Prabhakar #include <media/videobuf2-dma-contig.h> 2007fc05bdSLad Prabhakar 2107fc05bdSLad Prabhakar #include "rzg2l-cru.h" 2207fc05bdSLad Prabhakar 2307fc05bdSLad Prabhakar /* HW CRU Registers Definition */ 2407fc05bdSLad Prabhakar 2507fc05bdSLad Prabhakar /* CRU Control Register */ 2607fc05bdSLad Prabhakar #define CRUnCTRL 0x0 2707fc05bdSLad Prabhakar #define CRUnCTRL_VINSEL(x) ((x) << 0) 2807fc05bdSLad Prabhakar 2907fc05bdSLad Prabhakar /* CRU Interrupt Enable Register */ 3007fc05bdSLad Prabhakar #define CRUnIE 0x4 3107fc05bdSLad Prabhakar #define CRUnIE_EFE BIT(17) 3207fc05bdSLad Prabhakar 3307fc05bdSLad Prabhakar /* CRU Interrupt Status Register */ 3407fc05bdSLad Prabhakar #define CRUnINTS 0x8 3507fc05bdSLad Prabhakar #define CRUnINTS_SFS BIT(16) 3607fc05bdSLad Prabhakar 3707fc05bdSLad Prabhakar /* CRU Reset Register */ 3807fc05bdSLad Prabhakar #define CRUnRST 0xc 3907fc05bdSLad Prabhakar #define CRUnRST_VRESETN BIT(0) 4007fc05bdSLad Prabhakar 4107fc05bdSLad Prabhakar /* Memory Bank Base Address (Lower) Register for CRU Image Data */ 4207fc05bdSLad Prabhakar #define AMnMBxADDRL(x) (0x100 + ((x) * 8)) 4307fc05bdSLad Prabhakar 4407fc05bdSLad Prabhakar /* Memory Bank Base Address (Higher) Register for CRU Image Data */ 4507fc05bdSLad Prabhakar #define AMnMBxADDRH(x) (0x104 + ((x) * 8)) 4607fc05bdSLad Prabhakar 4707fc05bdSLad Prabhakar /* Memory Bank Enable Register for CRU Image Data */ 4807fc05bdSLad Prabhakar #define AMnMBVALID 0x148 4907fc05bdSLad Prabhakar #define AMnMBVALID_MBVALID(x) GENMASK(x, 0) 5007fc05bdSLad Prabhakar 5107fc05bdSLad Prabhakar /* Memory Bank Status Register for CRU Image Data */ 5207fc05bdSLad Prabhakar #define AMnMBS 0x14c 5307fc05bdSLad Prabhakar #define AMnMBS_MBSTS 0x7 5407fc05bdSLad Prabhakar 5507fc05bdSLad Prabhakar /* AXI Master FIFO Pointer Register for CRU Image Data */ 5607fc05bdSLad Prabhakar #define AMnFIFOPNTR 0x168 5707fc05bdSLad Prabhakar #define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0) 5807fc05bdSLad Prabhakar #define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16) 5907fc05bdSLad Prabhakar 6007fc05bdSLad Prabhakar /* AXI Master Transfer Stop Register for CRU Image Data */ 6107fc05bdSLad Prabhakar #define AMnAXISTP 0x174 6207fc05bdSLad Prabhakar #define AMnAXISTP_AXI_STOP BIT(0) 6307fc05bdSLad Prabhakar 6407fc05bdSLad Prabhakar /* AXI Master Transfer Stop Status Register for CRU Image Data */ 6507fc05bdSLad Prabhakar #define AMnAXISTPACK 0x178 6607fc05bdSLad Prabhakar #define AMnAXISTPACK_AXI_STOP_ACK BIT(0) 6707fc05bdSLad Prabhakar 6807fc05bdSLad Prabhakar /* CRU Image Processing Enable Register */ 6907fc05bdSLad Prabhakar #define ICnEN 0x200 7007fc05bdSLad Prabhakar #define ICnEN_ICEN BIT(0) 7107fc05bdSLad Prabhakar 7207fc05bdSLad Prabhakar /* CRU Image Processing Main Control Register */ 7307fc05bdSLad Prabhakar #define ICnMC 0x208 7407fc05bdSLad Prabhakar #define ICnMC_CSCTHR BIT(5) 7507fc05bdSLad Prabhakar #define ICnMC_INF_YUV8_422 (0x1e << 16) 7607fc05bdSLad Prabhakar #define ICnMC_INF_USER (0x30 << 16) 7707fc05bdSLad Prabhakar #define ICnMC_VCSEL(x) ((x) << 22) 7807fc05bdSLad Prabhakar #define ICnMC_INF_MASK GENMASK(21, 16) 7907fc05bdSLad Prabhakar 8007fc05bdSLad Prabhakar /* CRU Module Status Register */ 8107fc05bdSLad Prabhakar #define ICnMS 0x254 8207fc05bdSLad Prabhakar #define ICnMS_IA BIT(2) 8307fc05bdSLad Prabhakar 8407fc05bdSLad Prabhakar /* CRU Data Output Mode Register */ 8507fc05bdSLad Prabhakar #define ICnDMR 0x26c 8607fc05bdSLad Prabhakar #define ICnDMR_YCMODE_UYVY (1 << 4) 8707fc05bdSLad Prabhakar 8807fc05bdSLad Prabhakar #define RZG2L_TIMEOUT_MS 100 8907fc05bdSLad Prabhakar #define RZG2L_RETRIES 10 9007fc05bdSLad Prabhakar 9107fc05bdSLad Prabhakar #define RZG2L_CRU_DEFAULT_FORMAT V4L2_PIX_FMT_UYVY 9207fc05bdSLad Prabhakar #define RZG2L_CRU_DEFAULT_WIDTH RZG2L_CRU_MIN_INPUT_WIDTH 9307fc05bdSLad Prabhakar #define RZG2L_CRU_DEFAULT_HEIGHT RZG2L_CRU_MIN_INPUT_HEIGHT 9407fc05bdSLad Prabhakar #define RZG2L_CRU_DEFAULT_FIELD V4L2_FIELD_NONE 9507fc05bdSLad Prabhakar #define RZG2L_CRU_DEFAULT_COLORSPACE V4L2_COLORSPACE_SRGB 9607fc05bdSLad Prabhakar 9707fc05bdSLad Prabhakar struct rzg2l_cru_buffer { 9807fc05bdSLad Prabhakar struct vb2_v4l2_buffer vb; 9907fc05bdSLad Prabhakar struct list_head list; 10007fc05bdSLad Prabhakar }; 10107fc05bdSLad Prabhakar 10207fc05bdSLad Prabhakar #define to_buf_list(vb2_buffer) \ 10307fc05bdSLad Prabhakar (&container_of(vb2_buffer, struct rzg2l_cru_buffer, vb)->list) 10407fc05bdSLad Prabhakar 10507fc05bdSLad Prabhakar /* ----------------------------------------------------------------------------- 10607fc05bdSLad Prabhakar * DMA operations 10707fc05bdSLad Prabhakar */ 10807fc05bdSLad Prabhakar static void rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value) 10907fc05bdSLad Prabhakar { 11007fc05bdSLad Prabhakar iowrite32(value, cru->base + offset); 11107fc05bdSLad Prabhakar } 11207fc05bdSLad Prabhakar 11307fc05bdSLad Prabhakar static u32 rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset) 11407fc05bdSLad Prabhakar { 11507fc05bdSLad Prabhakar return ioread32(cru->base + offset); 11607fc05bdSLad Prabhakar } 11707fc05bdSLad Prabhakar 11807fc05bdSLad Prabhakar /* Need to hold qlock before calling */ 11907fc05bdSLad Prabhakar static void return_unused_buffers(struct rzg2l_cru_dev *cru, 12007fc05bdSLad Prabhakar enum vb2_buffer_state state) 12107fc05bdSLad Prabhakar { 12207fc05bdSLad Prabhakar struct rzg2l_cru_buffer *buf, *node; 12307fc05bdSLad Prabhakar unsigned long flags; 12407fc05bdSLad Prabhakar unsigned int i; 12507fc05bdSLad Prabhakar 12607fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 12707fc05bdSLad Prabhakar for (i = 0; i < cru->num_buf; i++) { 12807fc05bdSLad Prabhakar if (cru->queue_buf[i]) { 12907fc05bdSLad Prabhakar vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, 13007fc05bdSLad Prabhakar state); 13107fc05bdSLad Prabhakar cru->queue_buf[i] = NULL; 13207fc05bdSLad Prabhakar } 13307fc05bdSLad Prabhakar } 13407fc05bdSLad Prabhakar 13507fc05bdSLad Prabhakar list_for_each_entry_safe(buf, node, &cru->buf_list, list) { 13607fc05bdSLad Prabhakar vb2_buffer_done(&buf->vb.vb2_buf, state); 13707fc05bdSLad Prabhakar list_del(&buf->list); 13807fc05bdSLad Prabhakar } 13907fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 14007fc05bdSLad Prabhakar } 14107fc05bdSLad Prabhakar 14207fc05bdSLad Prabhakar static int rzg2l_cru_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, 14307fc05bdSLad Prabhakar unsigned int *nplanes, unsigned int sizes[], 14407fc05bdSLad Prabhakar struct device *alloc_devs[]) 14507fc05bdSLad Prabhakar { 14607fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 14707fc05bdSLad Prabhakar 14807fc05bdSLad Prabhakar /* Make sure the image size is large enough. */ 14907fc05bdSLad Prabhakar if (*nplanes) 15007fc05bdSLad Prabhakar return sizes[0] < cru->format.sizeimage ? -EINVAL : 0; 15107fc05bdSLad Prabhakar 15207fc05bdSLad Prabhakar *nplanes = 1; 15307fc05bdSLad Prabhakar sizes[0] = cru->format.sizeimage; 15407fc05bdSLad Prabhakar 15507fc05bdSLad Prabhakar return 0; 15607fc05bdSLad Prabhakar }; 15707fc05bdSLad Prabhakar 15807fc05bdSLad Prabhakar static int rzg2l_cru_buffer_prepare(struct vb2_buffer *vb) 15907fc05bdSLad Prabhakar { 16007fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); 16107fc05bdSLad Prabhakar unsigned long size = cru->format.sizeimage; 16207fc05bdSLad Prabhakar 16307fc05bdSLad Prabhakar if (vb2_plane_size(vb, 0) < size) { 16407fc05bdSLad Prabhakar dev_err(cru->dev, "buffer too small (%lu < %lu)\n", 16507fc05bdSLad Prabhakar vb2_plane_size(vb, 0), size); 16607fc05bdSLad Prabhakar return -EINVAL; 16707fc05bdSLad Prabhakar } 16807fc05bdSLad Prabhakar 16907fc05bdSLad Prabhakar vb2_set_plane_payload(vb, 0, size); 17007fc05bdSLad Prabhakar 17107fc05bdSLad Prabhakar return 0; 17207fc05bdSLad Prabhakar } 17307fc05bdSLad Prabhakar 17407fc05bdSLad Prabhakar static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb) 17507fc05bdSLad Prabhakar { 17607fc05bdSLad Prabhakar struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 17707fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); 17807fc05bdSLad Prabhakar unsigned long flags; 17907fc05bdSLad Prabhakar 18007fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 18107fc05bdSLad Prabhakar 18207fc05bdSLad Prabhakar list_add_tail(to_buf_list(vbuf), &cru->buf_list); 18307fc05bdSLad Prabhakar 18407fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 18507fc05bdSLad Prabhakar } 18607fc05bdSLad Prabhakar 18707fc05bdSLad Prabhakar static int rzg2l_cru_mc_validate_format(struct rzg2l_cru_dev *cru, 18807fc05bdSLad Prabhakar struct v4l2_subdev *sd, 18907fc05bdSLad Prabhakar struct media_pad *pad) 19007fc05bdSLad Prabhakar { 19107fc05bdSLad Prabhakar struct v4l2_subdev_format fmt = { 19207fc05bdSLad Prabhakar .which = V4L2_SUBDEV_FORMAT_ACTIVE, 19307fc05bdSLad Prabhakar }; 19407fc05bdSLad Prabhakar 19507fc05bdSLad Prabhakar fmt.pad = pad->index; 19607fc05bdSLad Prabhakar if (v4l2_subdev_call_state_active(sd, pad, get_fmt, &fmt)) 19707fc05bdSLad Prabhakar return -EPIPE; 19807fc05bdSLad Prabhakar 19907fc05bdSLad Prabhakar switch (fmt.format.code) { 20007fc05bdSLad Prabhakar case MEDIA_BUS_FMT_UYVY8_1X16: 20107fc05bdSLad Prabhakar break; 20207fc05bdSLad Prabhakar default: 20307fc05bdSLad Prabhakar return -EPIPE; 20407fc05bdSLad Prabhakar } 20507fc05bdSLad Prabhakar 20607fc05bdSLad Prabhakar switch (fmt.format.field) { 20707fc05bdSLad Prabhakar case V4L2_FIELD_TOP: 20807fc05bdSLad Prabhakar case V4L2_FIELD_BOTTOM: 20907fc05bdSLad Prabhakar case V4L2_FIELD_NONE: 21007fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED_TB: 21107fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED_BT: 21207fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED: 21307fc05bdSLad Prabhakar case V4L2_FIELD_SEQ_TB: 21407fc05bdSLad Prabhakar case V4L2_FIELD_SEQ_BT: 21507fc05bdSLad Prabhakar break; 21607fc05bdSLad Prabhakar default: 21707fc05bdSLad Prabhakar return -EPIPE; 21807fc05bdSLad Prabhakar } 21907fc05bdSLad Prabhakar 22007fc05bdSLad Prabhakar if (fmt.format.width != cru->format.width || 22107fc05bdSLad Prabhakar fmt.format.height != cru->format.height) 22207fc05bdSLad Prabhakar return -EPIPE; 22307fc05bdSLad Prabhakar 22407fc05bdSLad Prabhakar return 0; 22507fc05bdSLad Prabhakar } 22607fc05bdSLad Prabhakar 22707fc05bdSLad Prabhakar static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, 22807fc05bdSLad Prabhakar int slot, dma_addr_t addr) 22907fc05bdSLad Prabhakar { 23007fc05bdSLad Prabhakar /* 23107fc05bdSLad Prabhakar * The address needs to be 512 bytes aligned. Driver should never accept 23207fc05bdSLad Prabhakar * settings that do not satisfy this in the first place... 23307fc05bdSLad Prabhakar */ 23407fc05bdSLad Prabhakar if (WARN_ON((addr) & RZG2L_CRU_HW_BUFFER_MASK)) 23507fc05bdSLad Prabhakar return; 23607fc05bdSLad Prabhakar 23707fc05bdSLad Prabhakar /* Currently, we just use the buffer in 32 bits address */ 23807fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnMBxADDRL(slot), addr); 23907fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnMBxADDRH(slot), 0); 24007fc05bdSLad Prabhakar } 24107fc05bdSLad Prabhakar 24207fc05bdSLad Prabhakar /* 24307fc05bdSLad Prabhakar * Moves a buffer from the queue to the HW slot. If no buffer is 24407fc05bdSLad Prabhakar * available use the scratch buffer. The scratch buffer is never 24507fc05bdSLad Prabhakar * returned to userspace, its only function is to enable the capture 24607fc05bdSLad Prabhakar * loop to keep running. 24707fc05bdSLad Prabhakar */ 24807fc05bdSLad Prabhakar static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) 24907fc05bdSLad Prabhakar { 25007fc05bdSLad Prabhakar struct vb2_v4l2_buffer *vbuf; 25107fc05bdSLad Prabhakar struct rzg2l_cru_buffer *buf; 25207fc05bdSLad Prabhakar dma_addr_t phys_addr; 25307fc05bdSLad Prabhakar 25407fc05bdSLad Prabhakar /* A already populated slot shall never be overwritten. */ 25507fc05bdSLad Prabhakar if (WARN_ON(cru->queue_buf[slot])) 25607fc05bdSLad Prabhakar return; 25707fc05bdSLad Prabhakar 25807fc05bdSLad Prabhakar dev_dbg(cru->dev, "Filling HW slot: %d\n", slot); 25907fc05bdSLad Prabhakar 26007fc05bdSLad Prabhakar if (list_empty(&cru->buf_list)) { 26107fc05bdSLad Prabhakar cru->queue_buf[slot] = NULL; 26207fc05bdSLad Prabhakar phys_addr = cru->scratch_phys; 26307fc05bdSLad Prabhakar } else { 26407fc05bdSLad Prabhakar /* Keep track of buffer we give to HW */ 26507fc05bdSLad Prabhakar buf = list_entry(cru->buf_list.next, 26607fc05bdSLad Prabhakar struct rzg2l_cru_buffer, list); 26707fc05bdSLad Prabhakar vbuf = &buf->vb; 26807fc05bdSLad Prabhakar list_del_init(to_buf_list(vbuf)); 26907fc05bdSLad Prabhakar cru->queue_buf[slot] = vbuf; 27007fc05bdSLad Prabhakar 27107fc05bdSLad Prabhakar /* Setup DMA */ 27207fc05bdSLad Prabhakar phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); 27307fc05bdSLad Prabhakar } 27407fc05bdSLad Prabhakar 27507fc05bdSLad Prabhakar rzg2l_cru_set_slot_addr(cru, slot, phys_addr); 27607fc05bdSLad Prabhakar } 27707fc05bdSLad Prabhakar 27807fc05bdSLad Prabhakar static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) 27907fc05bdSLad Prabhakar { 28007fc05bdSLad Prabhakar unsigned int slot; 28107fc05bdSLad Prabhakar 28207fc05bdSLad Prabhakar /* 28307fc05bdSLad Prabhakar * Set image data memory banks. 28407fc05bdSLad Prabhakar * Currently, we will use maximum address. 28507fc05bdSLad Prabhakar */ 28607fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnMBVALID, AMnMBVALID_MBVALID(cru->num_buf - 1)); 28707fc05bdSLad Prabhakar 28807fc05bdSLad Prabhakar for (slot = 0; slot < cru->num_buf; slot++) 28907fc05bdSLad Prabhakar rzg2l_cru_fill_hw_slot(cru, slot); 29007fc05bdSLad Prabhakar } 29107fc05bdSLad Prabhakar 29207fc05bdSLad Prabhakar static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, bool *input_is_yuv, 29307fc05bdSLad Prabhakar struct v4l2_mbus_framefmt *ip_sd_fmt) 29407fc05bdSLad Prabhakar { 29507fc05bdSLad Prabhakar u32 icnmc; 29607fc05bdSLad Prabhakar 29707fc05bdSLad Prabhakar switch (ip_sd_fmt->code) { 29807fc05bdSLad Prabhakar case MEDIA_BUS_FMT_UYVY8_1X16: 29907fc05bdSLad Prabhakar icnmc = ICnMC_INF_YUV8_422; 30007fc05bdSLad Prabhakar *input_is_yuv = true; 30107fc05bdSLad Prabhakar break; 30207fc05bdSLad Prabhakar default: 30307fc05bdSLad Prabhakar *input_is_yuv = false; 30407fc05bdSLad Prabhakar icnmc = ICnMC_INF_USER; 30507fc05bdSLad Prabhakar break; 30607fc05bdSLad Prabhakar } 30707fc05bdSLad Prabhakar 30807fc05bdSLad Prabhakar icnmc |= (rzg2l_cru_read(cru, ICnMC) & ~ICnMC_INF_MASK); 30907fc05bdSLad Prabhakar 31007fc05bdSLad Prabhakar /* Set virtual channel CSI2 */ 31107fc05bdSLad Prabhakar icnmc |= ICnMC_VCSEL(cru->csi.channel); 31207fc05bdSLad Prabhakar 31307fc05bdSLad Prabhakar rzg2l_cru_write(cru, ICnMC, icnmc); 31407fc05bdSLad Prabhakar } 31507fc05bdSLad Prabhakar 31607fc05bdSLad Prabhakar static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, 31707fc05bdSLad Prabhakar struct v4l2_mbus_framefmt *ip_sd_fmt) 31807fc05bdSLad Prabhakar { 31907fc05bdSLad Prabhakar bool output_is_yuv = false; 32007fc05bdSLad Prabhakar bool input_is_yuv = false; 32107fc05bdSLad Prabhakar u32 icndmr; 32207fc05bdSLad Prabhakar 32307fc05bdSLad Prabhakar rzg2l_cru_csi2_setup(cru, &input_is_yuv, ip_sd_fmt); 32407fc05bdSLad Prabhakar 32507fc05bdSLad Prabhakar /* Output format */ 32607fc05bdSLad Prabhakar switch (cru->format.pixelformat) { 32707fc05bdSLad Prabhakar case V4L2_PIX_FMT_UYVY: 32807fc05bdSLad Prabhakar icndmr = ICnDMR_YCMODE_UYVY; 32907fc05bdSLad Prabhakar output_is_yuv = true; 33007fc05bdSLad Prabhakar break; 33107fc05bdSLad Prabhakar default: 33207fc05bdSLad Prabhakar dev_err(cru->dev, "Invalid pixelformat (0x%x)\n", 33307fc05bdSLad Prabhakar cru->format.pixelformat); 33407fc05bdSLad Prabhakar return -EINVAL; 33507fc05bdSLad Prabhakar } 33607fc05bdSLad Prabhakar 33707fc05bdSLad Prabhakar /* If input and output use same colorspace, do bypass mode */ 33807fc05bdSLad Prabhakar if (output_is_yuv == input_is_yuv) 33907fc05bdSLad Prabhakar rzg2l_cru_write(cru, ICnMC, 34007fc05bdSLad Prabhakar rzg2l_cru_read(cru, ICnMC) | ICnMC_CSCTHR); 34107fc05bdSLad Prabhakar else 34207fc05bdSLad Prabhakar rzg2l_cru_write(cru, ICnMC, 34307fc05bdSLad Prabhakar rzg2l_cru_read(cru, ICnMC) & (~ICnMC_CSCTHR)); 34407fc05bdSLad Prabhakar 34507fc05bdSLad Prabhakar /* Set output data format */ 34607fc05bdSLad Prabhakar rzg2l_cru_write(cru, ICnDMR, icndmr); 34707fc05bdSLad Prabhakar 34807fc05bdSLad Prabhakar return 0; 34907fc05bdSLad Prabhakar } 35007fc05bdSLad Prabhakar 35107fc05bdSLad Prabhakar void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) 35207fc05bdSLad Prabhakar { 35307fc05bdSLad Prabhakar u32 amnfifopntr, amnfifopntr_w, amnfifopntr_r_y; 35407fc05bdSLad Prabhakar unsigned int retries = 0; 35507fc05bdSLad Prabhakar unsigned long flags; 35607fc05bdSLad Prabhakar u32 icnms; 35707fc05bdSLad Prabhakar 35807fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 35907fc05bdSLad Prabhakar 36007fc05bdSLad Prabhakar /* Disable and clear the interrupt */ 36107fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnIE, 0); 36207fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnINTS, 0x001F0F0F); 36307fc05bdSLad Prabhakar 36407fc05bdSLad Prabhakar /* Stop the operation of image conversion */ 36507fc05bdSLad Prabhakar rzg2l_cru_write(cru, ICnEN, 0); 36607fc05bdSLad Prabhakar 36707fc05bdSLad Prabhakar /* Wait for streaming to stop */ 36807fc05bdSLad Prabhakar while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) { 36907fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 37007fc05bdSLad Prabhakar msleep(RZG2L_TIMEOUT_MS); 37107fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 37207fc05bdSLad Prabhakar } 37307fc05bdSLad Prabhakar 37407fc05bdSLad Prabhakar icnms = rzg2l_cru_read(cru, ICnMS) & ICnMS_IA; 37507fc05bdSLad Prabhakar if (icnms) 37607fc05bdSLad Prabhakar dev_err(cru->dev, "Failed stop HW, something is seriously broken\n"); 37707fc05bdSLad Prabhakar 37807fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STOPPED; 37907fc05bdSLad Prabhakar 38007fc05bdSLad Prabhakar /* Wait until the FIFO becomes empty */ 38107fc05bdSLad Prabhakar for (retries = 5; retries > 0; retries--) { 38207fc05bdSLad Prabhakar amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); 38307fc05bdSLad Prabhakar 38407fc05bdSLad Prabhakar amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR; 38507fc05bdSLad Prabhakar amnfifopntr_r_y = 38607fc05bdSLad Prabhakar (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16; 38707fc05bdSLad Prabhakar if (amnfifopntr_w == amnfifopntr_r_y) 38807fc05bdSLad Prabhakar break; 38907fc05bdSLad Prabhakar 39007fc05bdSLad Prabhakar usleep_range(10, 20); 39107fc05bdSLad Prabhakar } 39207fc05bdSLad Prabhakar 39307fc05bdSLad Prabhakar /* Notify that FIFO is not empty here */ 39407fc05bdSLad Prabhakar if (!retries) 39507fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to empty FIFO\n"); 39607fc05bdSLad Prabhakar 39707fc05bdSLad Prabhakar /* Stop AXI bus */ 39807fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnAXISTP, AMnAXISTP_AXI_STOP); 39907fc05bdSLad Prabhakar 40007fc05bdSLad Prabhakar /* Wait until the AXI bus stop */ 40107fc05bdSLad Prabhakar for (retries = 5; retries > 0; retries--) { 40207fc05bdSLad Prabhakar if (rzg2l_cru_read(cru, AMnAXISTPACK) & 40307fc05bdSLad Prabhakar AMnAXISTPACK_AXI_STOP_ACK) 40407fc05bdSLad Prabhakar break; 40507fc05bdSLad Prabhakar 40607fc05bdSLad Prabhakar usleep_range(10, 20); 407*7206fcc5SYang Li } 40807fc05bdSLad Prabhakar 40907fc05bdSLad Prabhakar /* Notify that AXI bus can not stop here */ 41007fc05bdSLad Prabhakar if (!retries) 41107fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to stop AXI bus\n"); 41207fc05bdSLad Prabhakar 41307fc05bdSLad Prabhakar /* Cancel the AXI bus stop request */ 41407fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnAXISTP, 0); 41507fc05bdSLad Prabhakar 41607fc05bdSLad Prabhakar /* Reset the CRU (AXI-master) */ 41707fc05bdSLad Prabhakar reset_control_assert(cru->aresetn); 41807fc05bdSLad Prabhakar 41907fc05bdSLad Prabhakar /* Resets the image processing module */ 42007fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnRST, 0); 42107fc05bdSLad Prabhakar 42207fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 42307fc05bdSLad Prabhakar } 42407fc05bdSLad Prabhakar 42507fc05bdSLad Prabhakar int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) 42607fc05bdSLad Prabhakar { 42707fc05bdSLad Prabhakar struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru); 42807fc05bdSLad Prabhakar unsigned long flags; 42907fc05bdSLad Prabhakar int ret; 43007fc05bdSLad Prabhakar 43107fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 43207fc05bdSLad Prabhakar 43307fc05bdSLad Prabhakar /* Initialize image convert */ 43407fc05bdSLad Prabhakar ret = rzg2l_cru_initialize_image_conv(cru, fmt); 43507fc05bdSLad Prabhakar if (ret) { 43607fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 43707fc05bdSLad Prabhakar return ret; 43807fc05bdSLad Prabhakar } 43907fc05bdSLad Prabhakar 44007fc05bdSLad Prabhakar /* Select a video input */ 44107fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnCTRL, CRUnCTRL_VINSEL(0)); 44207fc05bdSLad Prabhakar 44307fc05bdSLad Prabhakar /* Cancel the software reset for image processing block */ 44407fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnRST, CRUnRST_VRESETN); 44507fc05bdSLad Prabhakar 44607fc05bdSLad Prabhakar /* Disable and clear the interrupt before using */ 44707fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnIE, 0); 44807fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnINTS, 0x001f000f); 44907fc05bdSLad Prabhakar 45007fc05bdSLad Prabhakar /* Initialize the AXI master */ 45107fc05bdSLad Prabhakar rzg2l_cru_initialize_axi(cru); 45207fc05bdSLad Prabhakar 45307fc05bdSLad Prabhakar /* Enable interrupt */ 45407fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE); 45507fc05bdSLad Prabhakar 45607fc05bdSLad Prabhakar /* Enable image processing reception */ 45707fc05bdSLad Prabhakar rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN); 45807fc05bdSLad Prabhakar 45907fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 46007fc05bdSLad Prabhakar 46107fc05bdSLad Prabhakar return 0; 46207fc05bdSLad Prabhakar } 46307fc05bdSLad Prabhakar 46407fc05bdSLad Prabhakar void rzg2l_cru_vclk_unprepare(struct rzg2l_cru_dev *cru) 46507fc05bdSLad Prabhakar { 46607fc05bdSLad Prabhakar clk_disable_unprepare(cru->vclk); 46707fc05bdSLad Prabhakar } 46807fc05bdSLad Prabhakar 46907fc05bdSLad Prabhakar int rzg2l_cru_vclk_prepare(struct rzg2l_cru_dev *cru) 47007fc05bdSLad Prabhakar { 47107fc05bdSLad Prabhakar return clk_prepare_enable(cru->vclk); 47207fc05bdSLad Prabhakar } 47307fc05bdSLad Prabhakar 47407fc05bdSLad Prabhakar static int rzg2l_cru_set_stream(struct rzg2l_cru_dev *cru, int on) 47507fc05bdSLad Prabhakar { 47607fc05bdSLad Prabhakar struct media_pipeline *pipe; 47707fc05bdSLad Prabhakar struct v4l2_subdev *sd; 47807fc05bdSLad Prabhakar struct media_pad *pad; 47907fc05bdSLad Prabhakar int ret; 48007fc05bdSLad Prabhakar 48107fc05bdSLad Prabhakar pad = media_pad_remote_pad_first(&cru->pad); 48207fc05bdSLad Prabhakar if (!pad) 48307fc05bdSLad Prabhakar return -EPIPE; 48407fc05bdSLad Prabhakar 48507fc05bdSLad Prabhakar sd = media_entity_to_v4l2_subdev(pad->entity); 48607fc05bdSLad Prabhakar 48707fc05bdSLad Prabhakar if (!on) { 48807fc05bdSLad Prabhakar int stream_off_ret = 0; 48907fc05bdSLad Prabhakar 49007fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, s_stream, 0); 49107fc05bdSLad Prabhakar if (ret) 49207fc05bdSLad Prabhakar stream_off_ret = ret; 49307fc05bdSLad Prabhakar 49407fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, post_streamoff); 49507fc05bdSLad Prabhakar if (ret == -ENOIOCTLCMD) 49607fc05bdSLad Prabhakar ret = 0; 49707fc05bdSLad Prabhakar if (ret && !stream_off_ret) 49807fc05bdSLad Prabhakar stream_off_ret = ret; 49907fc05bdSLad Prabhakar 50007fc05bdSLad Prabhakar video_device_pipeline_stop(&cru->vdev); 50107fc05bdSLad Prabhakar 50207fc05bdSLad Prabhakar pm_runtime_put_sync(cru->dev); 50307fc05bdSLad Prabhakar clk_disable_unprepare(cru->vclk); 50407fc05bdSLad Prabhakar 50507fc05bdSLad Prabhakar return stream_off_ret; 50607fc05bdSLad Prabhakar } 50707fc05bdSLad Prabhakar 50807fc05bdSLad Prabhakar ret = pm_runtime_resume_and_get(cru->dev); 50907fc05bdSLad Prabhakar if (ret) 51007fc05bdSLad Prabhakar return ret; 51107fc05bdSLad Prabhakar 51207fc05bdSLad Prabhakar ret = clk_prepare_enable(cru->vclk); 51307fc05bdSLad Prabhakar if (ret) 51407fc05bdSLad Prabhakar goto err_pm_put; 51507fc05bdSLad Prabhakar 51607fc05bdSLad Prabhakar ret = rzg2l_cru_mc_validate_format(cru, sd, pad); 51707fc05bdSLad Prabhakar if (ret) 51807fc05bdSLad Prabhakar goto err_vclk_disable; 51907fc05bdSLad Prabhakar 52007fc05bdSLad Prabhakar pipe = media_entity_pipeline(&sd->entity) ? : &cru->vdev.pipe; 52107fc05bdSLad Prabhakar ret = video_device_pipeline_start(&cru->vdev, pipe); 52207fc05bdSLad Prabhakar if (ret) 52307fc05bdSLad Prabhakar goto err_vclk_disable; 52407fc05bdSLad Prabhakar 52507fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, pre_streamon, 0); 52607fc05bdSLad Prabhakar if (ret == -ENOIOCTLCMD) 52707fc05bdSLad Prabhakar ret = 0; 52807fc05bdSLad Prabhakar if (ret) 52907fc05bdSLad Prabhakar goto pipe_line_stop; 53007fc05bdSLad Prabhakar 53107fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, s_stream, 1); 53207fc05bdSLad Prabhakar if (ret == -ENOIOCTLCMD) 53307fc05bdSLad Prabhakar ret = 0; 53407fc05bdSLad Prabhakar if (ret) 53507fc05bdSLad Prabhakar goto err_s_stream; 53607fc05bdSLad Prabhakar 53707fc05bdSLad Prabhakar return 0; 53807fc05bdSLad Prabhakar 53907fc05bdSLad Prabhakar err_s_stream: 54007fc05bdSLad Prabhakar v4l2_subdev_call(sd, video, post_streamoff); 54107fc05bdSLad Prabhakar 54207fc05bdSLad Prabhakar pipe_line_stop: 54307fc05bdSLad Prabhakar video_device_pipeline_stop(&cru->vdev); 54407fc05bdSLad Prabhakar 54507fc05bdSLad Prabhakar err_vclk_disable: 54607fc05bdSLad Prabhakar clk_disable_unprepare(cru->vclk); 54707fc05bdSLad Prabhakar 54807fc05bdSLad Prabhakar err_pm_put: 54907fc05bdSLad Prabhakar pm_runtime_put_sync(cru->dev); 55007fc05bdSLad Prabhakar 55107fc05bdSLad Prabhakar return ret; 55207fc05bdSLad Prabhakar } 55307fc05bdSLad Prabhakar 55407fc05bdSLad Prabhakar static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru) 55507fc05bdSLad Prabhakar { 55607fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STOPPING; 55707fc05bdSLad Prabhakar 55807fc05bdSLad Prabhakar rzg2l_cru_set_stream(cru, 0); 55907fc05bdSLad Prabhakar } 56007fc05bdSLad Prabhakar 56107fc05bdSLad Prabhakar static irqreturn_t rzg2l_cru_irq(int irq, void *data) 56207fc05bdSLad Prabhakar { 56307fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = data; 56407fc05bdSLad Prabhakar unsigned int handled = 0; 56507fc05bdSLad Prabhakar unsigned long flags; 56607fc05bdSLad Prabhakar u32 irq_status; 56707fc05bdSLad Prabhakar u32 amnmbs; 56807fc05bdSLad Prabhakar int slot; 56907fc05bdSLad Prabhakar 57007fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 57107fc05bdSLad Prabhakar 57207fc05bdSLad Prabhakar irq_status = rzg2l_cru_read(cru, CRUnINTS); 57307fc05bdSLad Prabhakar if (!irq_status) 57407fc05bdSLad Prabhakar goto done; 57507fc05bdSLad Prabhakar 57607fc05bdSLad Prabhakar handled = 1; 57707fc05bdSLad Prabhakar 57807fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); 57907fc05bdSLad Prabhakar 58007fc05bdSLad Prabhakar /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ 58107fc05bdSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STOPPED) { 58207fc05bdSLad Prabhakar dev_dbg(cru->dev, "IRQ while state stopped\n"); 58307fc05bdSLad Prabhakar goto done; 58407fc05bdSLad Prabhakar } 58507fc05bdSLad Prabhakar 58607fc05bdSLad Prabhakar /* Increase stop retries if capture status is 'RZG2L_CRU_DMA_STOPPING' */ 58707fc05bdSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STOPPING) { 58807fc05bdSLad Prabhakar if (irq_status & CRUnINTS_SFS) 58907fc05bdSLad Prabhakar dev_dbg(cru->dev, "IRQ while state stopping\n"); 59007fc05bdSLad Prabhakar goto done; 59107fc05bdSLad Prabhakar } 59207fc05bdSLad Prabhakar 59307fc05bdSLad Prabhakar /* Prepare for capture and update state */ 59407fc05bdSLad Prabhakar amnmbs = rzg2l_cru_read(cru, AMnMBS); 59507fc05bdSLad Prabhakar slot = amnmbs & AMnMBS_MBSTS; 59607fc05bdSLad Prabhakar 59707fc05bdSLad Prabhakar /* 59807fc05bdSLad Prabhakar * AMnMBS.MBSTS indicates the destination of Memory Bank (MB). 59907fc05bdSLad Prabhakar * Recalculate to get the current transfer complete MB. 60007fc05bdSLad Prabhakar */ 60107fc05bdSLad Prabhakar if (slot == 0) 60207fc05bdSLad Prabhakar slot = cru->num_buf - 1; 60307fc05bdSLad Prabhakar else 60407fc05bdSLad Prabhakar slot--; 60507fc05bdSLad Prabhakar 60607fc05bdSLad Prabhakar /* 60707fc05bdSLad Prabhakar * To hand buffers back in a known order to userspace start 60807fc05bdSLad Prabhakar * to capture first from slot 0. 60907fc05bdSLad Prabhakar */ 61007fc05bdSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STARTING) { 61107fc05bdSLad Prabhakar if (slot != 0) { 61207fc05bdSLad Prabhakar dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); 61307fc05bdSLad Prabhakar goto done; 61407fc05bdSLad Prabhakar } 61507fc05bdSLad Prabhakar 61607fc05bdSLad Prabhakar dev_dbg(cru->dev, "Capture start synced!\n"); 61707fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_RUNNING; 61807fc05bdSLad Prabhakar } 61907fc05bdSLad Prabhakar 62007fc05bdSLad Prabhakar /* Capture frame */ 62107fc05bdSLad Prabhakar if (cru->queue_buf[slot]) { 62207fc05bdSLad Prabhakar cru->queue_buf[slot]->field = cru->format.field; 62307fc05bdSLad Prabhakar cru->queue_buf[slot]->sequence = cru->sequence; 62407fc05bdSLad Prabhakar cru->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); 62507fc05bdSLad Prabhakar vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, 62607fc05bdSLad Prabhakar VB2_BUF_STATE_DONE); 62707fc05bdSLad Prabhakar cru->queue_buf[slot] = NULL; 62807fc05bdSLad Prabhakar } else { 62907fc05bdSLad Prabhakar /* Scratch buffer was used, dropping frame. */ 63007fc05bdSLad Prabhakar dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); 63107fc05bdSLad Prabhakar } 63207fc05bdSLad Prabhakar 63307fc05bdSLad Prabhakar cru->sequence++; 63407fc05bdSLad Prabhakar 63507fc05bdSLad Prabhakar /* Prepare for next frame */ 63607fc05bdSLad Prabhakar rzg2l_cru_fill_hw_slot(cru, slot); 63707fc05bdSLad Prabhakar 63807fc05bdSLad Prabhakar done: 63907fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 64007fc05bdSLad Prabhakar 64107fc05bdSLad Prabhakar return IRQ_RETVAL(handled); 64207fc05bdSLad Prabhakar } 64307fc05bdSLad Prabhakar 64407fc05bdSLad Prabhakar static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count) 64507fc05bdSLad Prabhakar { 64607fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 64707fc05bdSLad Prabhakar int ret; 64807fc05bdSLad Prabhakar 64907fc05bdSLad Prabhakar /* Release reset state */ 65007fc05bdSLad Prabhakar ret = reset_control_deassert(cru->aresetn); 65107fc05bdSLad Prabhakar if (ret) { 65207fc05bdSLad Prabhakar dev_err(cru->dev, "failed to deassert aresetn\n"); 65307fc05bdSLad Prabhakar return ret; 65407fc05bdSLad Prabhakar } 65507fc05bdSLad Prabhakar 65607fc05bdSLad Prabhakar ret = reset_control_deassert(cru->presetn); 65707fc05bdSLad Prabhakar if (ret) { 65807fc05bdSLad Prabhakar reset_control_assert(cru->aresetn); 65907fc05bdSLad Prabhakar dev_err(cru->dev, "failed to deassert presetn\n"); 66007fc05bdSLad Prabhakar return ret; 66107fc05bdSLad Prabhakar } 66207fc05bdSLad Prabhakar 66307fc05bdSLad Prabhakar ret = request_irq(cru->image_conv_irq, rzg2l_cru_irq, 66407fc05bdSLad Prabhakar IRQF_SHARED, KBUILD_MODNAME, cru); 66507fc05bdSLad Prabhakar if (ret) { 66607fc05bdSLad Prabhakar dev_err(cru->dev, "failed to request irq\n"); 66707fc05bdSLad Prabhakar goto assert_resets; 66807fc05bdSLad Prabhakar } 66907fc05bdSLad Prabhakar 67007fc05bdSLad Prabhakar /* Allocate scratch buffer. */ 67107fc05bdSLad Prabhakar cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage, 67207fc05bdSLad Prabhakar &cru->scratch_phys, GFP_KERNEL); 67307fc05bdSLad Prabhakar if (!cru->scratch) { 67407fc05bdSLad Prabhakar return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); 67507fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to allocate scratch buffer\n"); 676e54334cdSJiapeng Chong ret = -ENOMEM; 67707fc05bdSLad Prabhakar goto free_image_conv_irq; 67807fc05bdSLad Prabhakar } 67907fc05bdSLad Prabhakar 68007fc05bdSLad Prabhakar cru->sequence = 0; 68107fc05bdSLad Prabhakar 68207fc05bdSLad Prabhakar ret = rzg2l_cru_set_stream(cru, 1); 68307fc05bdSLad Prabhakar if (ret) { 68407fc05bdSLad Prabhakar return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); 68507fc05bdSLad Prabhakar goto out; 68607fc05bdSLad Prabhakar } 68707fc05bdSLad Prabhakar 68807fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STARTING; 68907fc05bdSLad Prabhakar dev_dbg(cru->dev, "Starting to capture\n"); 69007fc05bdSLad Prabhakar return 0; 69107fc05bdSLad Prabhakar 69207fc05bdSLad Prabhakar out: 69307fc05bdSLad Prabhakar if (ret) 69407fc05bdSLad Prabhakar dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch, 69507fc05bdSLad Prabhakar cru->scratch_phys); 69607fc05bdSLad Prabhakar free_image_conv_irq: 69707fc05bdSLad Prabhakar free_irq(cru->image_conv_irq, cru); 69807fc05bdSLad Prabhakar 69907fc05bdSLad Prabhakar assert_resets: 70007fc05bdSLad Prabhakar reset_control_assert(cru->presetn); 70107fc05bdSLad Prabhakar reset_control_assert(cru->aresetn); 70207fc05bdSLad Prabhakar 70307fc05bdSLad Prabhakar return ret; 70407fc05bdSLad Prabhakar } 70507fc05bdSLad Prabhakar 70607fc05bdSLad Prabhakar static void rzg2l_cru_stop_streaming_vq(struct vb2_queue *vq) 70707fc05bdSLad Prabhakar { 70807fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 70907fc05bdSLad Prabhakar 71007fc05bdSLad Prabhakar rzg2l_cru_stop_streaming(cru); 71107fc05bdSLad Prabhakar 71207fc05bdSLad Prabhakar /* Free scratch buffer */ 71307fc05bdSLad Prabhakar dma_free_coherent(cru->dev, cru->format.sizeimage, 71407fc05bdSLad Prabhakar cru->scratch, cru->scratch_phys); 71507fc05bdSLad Prabhakar 71607fc05bdSLad Prabhakar free_irq(cru->image_conv_irq, cru); 71707fc05bdSLad Prabhakar reset_control_assert(cru->presetn); 71807fc05bdSLad Prabhakar 71907fc05bdSLad Prabhakar return_unused_buffers(cru, VB2_BUF_STATE_ERROR); 72007fc05bdSLad Prabhakar } 72107fc05bdSLad Prabhakar 72207fc05bdSLad Prabhakar static const struct vb2_ops rzg2l_cru_qops = { 72307fc05bdSLad Prabhakar .queue_setup = rzg2l_cru_queue_setup, 72407fc05bdSLad Prabhakar .buf_prepare = rzg2l_cru_buffer_prepare, 72507fc05bdSLad Prabhakar .buf_queue = rzg2l_cru_buffer_queue, 72607fc05bdSLad Prabhakar .start_streaming = rzg2l_cru_start_streaming_vq, 72707fc05bdSLad Prabhakar .stop_streaming = rzg2l_cru_stop_streaming_vq, 72807fc05bdSLad Prabhakar .wait_prepare = vb2_ops_wait_prepare, 72907fc05bdSLad Prabhakar .wait_finish = vb2_ops_wait_finish, 73007fc05bdSLad Prabhakar }; 73107fc05bdSLad Prabhakar 73207fc05bdSLad Prabhakar void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru) 73307fc05bdSLad Prabhakar { 73407fc05bdSLad Prabhakar mutex_destroy(&cru->lock); 73507fc05bdSLad Prabhakar 73607fc05bdSLad Prabhakar v4l2_device_unregister(&cru->v4l2_dev); 73707fc05bdSLad Prabhakar vb2_queue_release(&cru->queue); 73807fc05bdSLad Prabhakar } 73907fc05bdSLad Prabhakar 74007fc05bdSLad Prabhakar int rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru) 74107fc05bdSLad Prabhakar { 74207fc05bdSLad Prabhakar struct vb2_queue *q = &cru->queue; 74307fc05bdSLad Prabhakar unsigned int i; 74407fc05bdSLad Prabhakar int ret; 74507fc05bdSLad Prabhakar 74607fc05bdSLad Prabhakar /* Initialize the top-level structure */ 74707fc05bdSLad Prabhakar ret = v4l2_device_register(cru->dev, &cru->v4l2_dev); 74807fc05bdSLad Prabhakar if (ret) 74907fc05bdSLad Prabhakar return ret; 75007fc05bdSLad Prabhakar 75107fc05bdSLad Prabhakar mutex_init(&cru->lock); 75207fc05bdSLad Prabhakar INIT_LIST_HEAD(&cru->buf_list); 75307fc05bdSLad Prabhakar 75407fc05bdSLad Prabhakar spin_lock_init(&cru->qlock); 75507fc05bdSLad Prabhakar 75607fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STOPPED; 75707fc05bdSLad Prabhakar 75807fc05bdSLad Prabhakar for (i = 0; i < RZG2L_CRU_HW_BUFFER_MAX; i++) 75907fc05bdSLad Prabhakar cru->queue_buf[i] = NULL; 76007fc05bdSLad Prabhakar 76107fc05bdSLad Prabhakar /* buffer queue */ 76207fc05bdSLad Prabhakar q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 76307fc05bdSLad Prabhakar q->io_modes = VB2_MMAP | VB2_DMABUF; 76407fc05bdSLad Prabhakar q->lock = &cru->lock; 76507fc05bdSLad Prabhakar q->drv_priv = cru; 76607fc05bdSLad Prabhakar q->buf_struct_size = sizeof(struct rzg2l_cru_buffer); 76707fc05bdSLad Prabhakar q->ops = &rzg2l_cru_qops; 76807fc05bdSLad Prabhakar q->mem_ops = &vb2_dma_contig_memops; 76907fc05bdSLad Prabhakar q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 77007fc05bdSLad Prabhakar q->min_buffers_needed = 4; 77107fc05bdSLad Prabhakar q->dev = cru->dev; 77207fc05bdSLad Prabhakar 77307fc05bdSLad Prabhakar ret = vb2_queue_init(q); 77407fc05bdSLad Prabhakar if (ret < 0) { 77507fc05bdSLad Prabhakar dev_err(cru->dev, "failed to initialize VB2 queue\n"); 77607fc05bdSLad Prabhakar goto error; 77707fc05bdSLad Prabhakar } 77807fc05bdSLad Prabhakar 77907fc05bdSLad Prabhakar return 0; 78007fc05bdSLad Prabhakar 78107fc05bdSLad Prabhakar error: 78207fc05bdSLad Prabhakar mutex_destroy(&cru->lock); 78307fc05bdSLad Prabhakar v4l2_device_unregister(&cru->v4l2_dev); 78407fc05bdSLad Prabhakar return ret; 78507fc05bdSLad Prabhakar } 78607fc05bdSLad Prabhakar 78707fc05bdSLad Prabhakar /* ----------------------------------------------------------------------------- 78807fc05bdSLad Prabhakar * V4L2 stuff 78907fc05bdSLad Prabhakar */ 79007fc05bdSLad Prabhakar 79107fc05bdSLad Prabhakar static const struct v4l2_format_info rzg2l_cru_formats[] = { 79207fc05bdSLad Prabhakar { 79307fc05bdSLad Prabhakar .format = V4L2_PIX_FMT_UYVY, 79407fc05bdSLad Prabhakar .bpp[0] = 2, 79507fc05bdSLad Prabhakar }, 79607fc05bdSLad Prabhakar }; 79707fc05bdSLad Prabhakar 79807fc05bdSLad Prabhakar const struct v4l2_format_info *rzg2l_cru_format_from_pixel(u32 format) 79907fc05bdSLad Prabhakar { 80007fc05bdSLad Prabhakar unsigned int i; 80107fc05bdSLad Prabhakar 80207fc05bdSLad Prabhakar for (i = 0; i < ARRAY_SIZE(rzg2l_cru_formats); i++) 80307fc05bdSLad Prabhakar if (rzg2l_cru_formats[i].format == format) 80407fc05bdSLad Prabhakar return rzg2l_cru_formats + i; 80507fc05bdSLad Prabhakar 80607fc05bdSLad Prabhakar return NULL; 80707fc05bdSLad Prabhakar } 80807fc05bdSLad Prabhakar 80907fc05bdSLad Prabhakar static u32 rzg2l_cru_format_bytesperline(struct v4l2_pix_format *pix) 81007fc05bdSLad Prabhakar { 81107fc05bdSLad Prabhakar const struct v4l2_format_info *fmt; 81207fc05bdSLad Prabhakar 81307fc05bdSLad Prabhakar fmt = rzg2l_cru_format_from_pixel(pix->pixelformat); 81407fc05bdSLad Prabhakar 81507fc05bdSLad Prabhakar if (WARN_ON(!fmt)) 81607fc05bdSLad Prabhakar return -EINVAL; 81707fc05bdSLad Prabhakar 81807fc05bdSLad Prabhakar return pix->width * fmt->bpp[0]; 81907fc05bdSLad Prabhakar } 82007fc05bdSLad Prabhakar 82107fc05bdSLad Prabhakar static u32 rzg2l_cru_format_sizeimage(struct v4l2_pix_format *pix) 82207fc05bdSLad Prabhakar { 82307fc05bdSLad Prabhakar return pix->bytesperline * pix->height; 82407fc05bdSLad Prabhakar } 82507fc05bdSLad Prabhakar 82607fc05bdSLad Prabhakar static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, 82707fc05bdSLad Prabhakar struct v4l2_pix_format *pix) 82807fc05bdSLad Prabhakar { 82907fc05bdSLad Prabhakar if (!rzg2l_cru_format_from_pixel(pix->pixelformat)) 83007fc05bdSLad Prabhakar pix->pixelformat = RZG2L_CRU_DEFAULT_FORMAT; 83107fc05bdSLad Prabhakar 83207fc05bdSLad Prabhakar switch (pix->field) { 83307fc05bdSLad Prabhakar case V4L2_FIELD_TOP: 83407fc05bdSLad Prabhakar case V4L2_FIELD_BOTTOM: 83507fc05bdSLad Prabhakar case V4L2_FIELD_NONE: 83607fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED_TB: 83707fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED_BT: 83807fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED: 83907fc05bdSLad Prabhakar break; 84007fc05bdSLad Prabhakar default: 84107fc05bdSLad Prabhakar pix->field = RZG2L_CRU_DEFAULT_FIELD; 84207fc05bdSLad Prabhakar break; 84307fc05bdSLad Prabhakar } 84407fc05bdSLad Prabhakar 84507fc05bdSLad Prabhakar /* Limit to CRU capabilities */ 84607fc05bdSLad Prabhakar v4l_bound_align_image(&pix->width, 320, RZG2L_CRU_MAX_INPUT_WIDTH, 1, 84707fc05bdSLad Prabhakar &pix->height, 240, RZG2L_CRU_MAX_INPUT_HEIGHT, 2, 0); 84807fc05bdSLad Prabhakar 84907fc05bdSLad Prabhakar pix->bytesperline = rzg2l_cru_format_bytesperline(pix); 85007fc05bdSLad Prabhakar pix->sizeimage = rzg2l_cru_format_sizeimage(pix); 85107fc05bdSLad Prabhakar 85207fc05bdSLad Prabhakar dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n", 85307fc05bdSLad Prabhakar pix->width, pix->height, pix->bytesperline, pix->sizeimage); 85407fc05bdSLad Prabhakar } 85507fc05bdSLad Prabhakar 85607fc05bdSLad Prabhakar static void rzg2l_cru_try_format(struct rzg2l_cru_dev *cru, 85707fc05bdSLad Prabhakar struct v4l2_pix_format *pix) 85807fc05bdSLad Prabhakar { 85907fc05bdSLad Prabhakar /* 86007fc05bdSLad Prabhakar * The V4L2 specification clearly documents the colorspace fields 86107fc05bdSLad Prabhakar * as being set by drivers for capture devices. Using the values 86207fc05bdSLad Prabhakar * supplied by userspace thus wouldn't comply with the API. Until 86307fc05bdSLad Prabhakar * the API is updated force fixed values. 86407fc05bdSLad Prabhakar */ 86507fc05bdSLad Prabhakar pix->colorspace = RZG2L_CRU_DEFAULT_COLORSPACE; 86607fc05bdSLad Prabhakar pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); 86707fc05bdSLad Prabhakar pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); 86807fc05bdSLad Prabhakar pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, pix->colorspace, 86907fc05bdSLad Prabhakar pix->ycbcr_enc); 87007fc05bdSLad Prabhakar 87107fc05bdSLad Prabhakar rzg2l_cru_format_align(cru, pix); 87207fc05bdSLad Prabhakar } 87307fc05bdSLad Prabhakar 87407fc05bdSLad Prabhakar static int rzg2l_cru_querycap(struct file *file, void *priv, 87507fc05bdSLad Prabhakar struct v4l2_capability *cap) 87607fc05bdSLad Prabhakar { 87707fc05bdSLad Prabhakar strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); 87807fc05bdSLad Prabhakar strscpy(cap->card, "RZG2L_CRU", sizeof(cap->card)); 87907fc05bdSLad Prabhakar 88007fc05bdSLad Prabhakar return 0; 88107fc05bdSLad Prabhakar } 88207fc05bdSLad Prabhakar 88307fc05bdSLad Prabhakar static int rzg2l_cru_try_fmt_vid_cap(struct file *file, void *priv, 88407fc05bdSLad Prabhakar struct v4l2_format *f) 88507fc05bdSLad Prabhakar { 88607fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 88707fc05bdSLad Prabhakar 88807fc05bdSLad Prabhakar rzg2l_cru_try_format(cru, &f->fmt.pix); 88907fc05bdSLad Prabhakar 89007fc05bdSLad Prabhakar return 0; 89107fc05bdSLad Prabhakar } 89207fc05bdSLad Prabhakar 89307fc05bdSLad Prabhakar static int rzg2l_cru_s_fmt_vid_cap(struct file *file, void *priv, 89407fc05bdSLad Prabhakar struct v4l2_format *f) 89507fc05bdSLad Prabhakar { 89607fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 89707fc05bdSLad Prabhakar 89807fc05bdSLad Prabhakar if (vb2_is_busy(&cru->queue)) 89907fc05bdSLad Prabhakar return -EBUSY; 90007fc05bdSLad Prabhakar 90107fc05bdSLad Prabhakar rzg2l_cru_try_format(cru, &f->fmt.pix); 90207fc05bdSLad Prabhakar 90307fc05bdSLad Prabhakar cru->format = f->fmt.pix; 90407fc05bdSLad Prabhakar 90507fc05bdSLad Prabhakar return 0; 90607fc05bdSLad Prabhakar } 90707fc05bdSLad Prabhakar 90807fc05bdSLad Prabhakar static int rzg2l_cru_g_fmt_vid_cap(struct file *file, void *priv, 90907fc05bdSLad Prabhakar struct v4l2_format *f) 91007fc05bdSLad Prabhakar { 91107fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 91207fc05bdSLad Prabhakar 91307fc05bdSLad Prabhakar f->fmt.pix = cru->format; 91407fc05bdSLad Prabhakar 91507fc05bdSLad Prabhakar return 0; 91607fc05bdSLad Prabhakar } 91707fc05bdSLad Prabhakar 91807fc05bdSLad Prabhakar static int rzg2l_cru_enum_fmt_vid_cap(struct file *file, void *priv, 91907fc05bdSLad Prabhakar struct v4l2_fmtdesc *f) 92007fc05bdSLad Prabhakar { 92107fc05bdSLad Prabhakar if (f->index >= ARRAY_SIZE(rzg2l_cru_formats)) 92207fc05bdSLad Prabhakar return -EINVAL; 92307fc05bdSLad Prabhakar 92407fc05bdSLad Prabhakar f->pixelformat = rzg2l_cru_formats[f->index].format; 92507fc05bdSLad Prabhakar 92607fc05bdSLad Prabhakar return 0; 92707fc05bdSLad Prabhakar } 92807fc05bdSLad Prabhakar 92907fc05bdSLad Prabhakar static const struct v4l2_ioctl_ops rzg2l_cru_ioctl_ops = { 93007fc05bdSLad Prabhakar .vidioc_querycap = rzg2l_cru_querycap, 93107fc05bdSLad Prabhakar .vidioc_try_fmt_vid_cap = rzg2l_cru_try_fmt_vid_cap, 93207fc05bdSLad Prabhakar .vidioc_g_fmt_vid_cap = rzg2l_cru_g_fmt_vid_cap, 93307fc05bdSLad Prabhakar .vidioc_s_fmt_vid_cap = rzg2l_cru_s_fmt_vid_cap, 93407fc05bdSLad Prabhakar .vidioc_enum_fmt_vid_cap = rzg2l_cru_enum_fmt_vid_cap, 93507fc05bdSLad Prabhakar 93607fc05bdSLad Prabhakar .vidioc_reqbufs = vb2_ioctl_reqbufs, 93707fc05bdSLad Prabhakar .vidioc_create_bufs = vb2_ioctl_create_bufs, 93807fc05bdSLad Prabhakar .vidioc_querybuf = vb2_ioctl_querybuf, 93907fc05bdSLad Prabhakar .vidioc_qbuf = vb2_ioctl_qbuf, 94007fc05bdSLad Prabhakar .vidioc_dqbuf = vb2_ioctl_dqbuf, 94107fc05bdSLad Prabhakar .vidioc_expbuf = vb2_ioctl_expbuf, 94207fc05bdSLad Prabhakar .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 94307fc05bdSLad Prabhakar .vidioc_streamon = vb2_ioctl_streamon, 94407fc05bdSLad Prabhakar .vidioc_streamoff = vb2_ioctl_streamoff, 94507fc05bdSLad Prabhakar }; 94607fc05bdSLad Prabhakar 94707fc05bdSLad Prabhakar /* ----------------------------------------------------------------------------- 94807fc05bdSLad Prabhakar * Media controller file operations 94907fc05bdSLad Prabhakar */ 95007fc05bdSLad Prabhakar 95107fc05bdSLad Prabhakar static int rzg2l_cru_open(struct file *file) 95207fc05bdSLad Prabhakar { 95307fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 95407fc05bdSLad Prabhakar int ret; 95507fc05bdSLad Prabhakar 95607fc05bdSLad Prabhakar ret = mutex_lock_interruptible(&cru->lock); 95707fc05bdSLad Prabhakar if (ret) 95807fc05bdSLad Prabhakar return ret; 95907fc05bdSLad Prabhakar 96007fc05bdSLad Prabhakar file->private_data = cru; 96107fc05bdSLad Prabhakar ret = v4l2_fh_open(file); 96207fc05bdSLad Prabhakar if (ret) 96307fc05bdSLad Prabhakar goto err_unlock; 96407fc05bdSLad Prabhakar 96507fc05bdSLad Prabhakar mutex_unlock(&cru->lock); 96607fc05bdSLad Prabhakar 96707fc05bdSLad Prabhakar return 0; 96807fc05bdSLad Prabhakar 96907fc05bdSLad Prabhakar err_unlock: 97007fc05bdSLad Prabhakar mutex_unlock(&cru->lock); 97107fc05bdSLad Prabhakar 97207fc05bdSLad Prabhakar return ret; 97307fc05bdSLad Prabhakar } 97407fc05bdSLad Prabhakar 97507fc05bdSLad Prabhakar static int rzg2l_cru_release(struct file *file) 97607fc05bdSLad Prabhakar { 97707fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 97807fc05bdSLad Prabhakar int ret; 97907fc05bdSLad Prabhakar 98007fc05bdSLad Prabhakar mutex_lock(&cru->lock); 98107fc05bdSLad Prabhakar 98207fc05bdSLad Prabhakar /* the release helper will cleanup any on-going streaming. */ 98307fc05bdSLad Prabhakar ret = _vb2_fop_release(file, NULL); 98407fc05bdSLad Prabhakar 98507fc05bdSLad Prabhakar mutex_unlock(&cru->lock); 98607fc05bdSLad Prabhakar 98707fc05bdSLad Prabhakar return ret; 98807fc05bdSLad Prabhakar } 98907fc05bdSLad Prabhakar 99007fc05bdSLad Prabhakar static const struct v4l2_file_operations rzg2l_cru_fops = { 99107fc05bdSLad Prabhakar .owner = THIS_MODULE, 99207fc05bdSLad Prabhakar .unlocked_ioctl = video_ioctl2, 99307fc05bdSLad Prabhakar .open = rzg2l_cru_open, 99407fc05bdSLad Prabhakar .release = rzg2l_cru_release, 99507fc05bdSLad Prabhakar .poll = vb2_fop_poll, 99607fc05bdSLad Prabhakar .mmap = vb2_fop_mmap, 99707fc05bdSLad Prabhakar .read = vb2_fop_read, 99807fc05bdSLad Prabhakar }; 99907fc05bdSLad Prabhakar 100007fc05bdSLad Prabhakar static void rzg2l_cru_v4l2_init(struct rzg2l_cru_dev *cru) 100107fc05bdSLad Prabhakar { 100207fc05bdSLad Prabhakar struct video_device *vdev = &cru->vdev; 100307fc05bdSLad Prabhakar 100407fc05bdSLad Prabhakar vdev->v4l2_dev = &cru->v4l2_dev; 100507fc05bdSLad Prabhakar vdev->queue = &cru->queue; 100607fc05bdSLad Prabhakar snprintf(vdev->name, sizeof(vdev->name), "CRU output"); 100707fc05bdSLad Prabhakar vdev->release = video_device_release_empty; 100807fc05bdSLad Prabhakar vdev->lock = &cru->lock; 100907fc05bdSLad Prabhakar vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 101007fc05bdSLad Prabhakar vdev->device_caps |= V4L2_CAP_IO_MC; 101107fc05bdSLad Prabhakar vdev->fops = &rzg2l_cru_fops; 101207fc05bdSLad Prabhakar vdev->ioctl_ops = &rzg2l_cru_ioctl_ops; 101307fc05bdSLad Prabhakar 101407fc05bdSLad Prabhakar /* Set a default format */ 101507fc05bdSLad Prabhakar cru->format.pixelformat = RZG2L_CRU_DEFAULT_FORMAT; 101607fc05bdSLad Prabhakar cru->format.width = RZG2L_CRU_DEFAULT_WIDTH; 101707fc05bdSLad Prabhakar cru->format.height = RZG2L_CRU_DEFAULT_HEIGHT; 101807fc05bdSLad Prabhakar cru->format.field = RZG2L_CRU_DEFAULT_FIELD; 101907fc05bdSLad Prabhakar cru->format.colorspace = RZG2L_CRU_DEFAULT_COLORSPACE; 102007fc05bdSLad Prabhakar rzg2l_cru_format_align(cru, &cru->format); 102107fc05bdSLad Prabhakar } 102207fc05bdSLad Prabhakar 102307fc05bdSLad Prabhakar void rzg2l_cru_video_unregister(struct rzg2l_cru_dev *cru) 102407fc05bdSLad Prabhakar { 102507fc05bdSLad Prabhakar media_device_unregister(&cru->mdev); 102607fc05bdSLad Prabhakar video_unregister_device(&cru->vdev); 102707fc05bdSLad Prabhakar } 102807fc05bdSLad Prabhakar 102907fc05bdSLad Prabhakar int rzg2l_cru_video_register(struct rzg2l_cru_dev *cru) 103007fc05bdSLad Prabhakar { 103107fc05bdSLad Prabhakar struct video_device *vdev = &cru->vdev; 103207fc05bdSLad Prabhakar int ret; 103307fc05bdSLad Prabhakar 103407fc05bdSLad Prabhakar if (video_is_registered(&cru->vdev)) { 103507fc05bdSLad Prabhakar struct media_entity *entity; 103607fc05bdSLad Prabhakar 103707fc05bdSLad Prabhakar entity = &cru->vdev.entity; 103807fc05bdSLad Prabhakar if (!entity->graph_obj.mdev) 103907fc05bdSLad Prabhakar entity->graph_obj.mdev = &cru->mdev; 104007fc05bdSLad Prabhakar return 0; 104107fc05bdSLad Prabhakar } 104207fc05bdSLad Prabhakar 104307fc05bdSLad Prabhakar rzg2l_cru_v4l2_init(cru); 104407fc05bdSLad Prabhakar video_set_drvdata(vdev, cru); 104507fc05bdSLad Prabhakar ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 104607fc05bdSLad Prabhakar if (ret) { 104707fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to register video device\n"); 104807fc05bdSLad Prabhakar return ret; 104907fc05bdSLad Prabhakar } 105007fc05bdSLad Prabhakar 105107fc05bdSLad Prabhakar ret = media_device_register(&cru->mdev); 105207fc05bdSLad Prabhakar if (ret) { 105307fc05bdSLad Prabhakar video_unregister_device(&cru->vdev); 105407fc05bdSLad Prabhakar return ret; 105507fc05bdSLad Prabhakar } 105607fc05bdSLad Prabhakar 105707fc05bdSLad Prabhakar return 0; 105807fc05bdSLad Prabhakar } 1059