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