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 183b506155SLad Prabhakar #include <media/mipi-csi2.h> 1907fc05bdSLad Prabhakar #include <media/v4l2-ioctl.h> 2007fc05bdSLad Prabhakar #include <media/videobuf2-dma-contig.h> 2107fc05bdSLad Prabhakar 2207fc05bdSLad Prabhakar #include "rzg2l-cru.h" 23c0fc8dd0SLad Prabhakar #include "rzg2l-cru-regs.h" 2407fc05bdSLad Prabhakar 2507fc05bdSLad Prabhakar #define RZG2L_TIMEOUT_MS 100 2607fc05bdSLad Prabhakar #define RZG2L_RETRIES 10 2707fc05bdSLad Prabhakar 2807fc05bdSLad Prabhakar #define RZG2L_CRU_DEFAULT_FORMAT V4L2_PIX_FMT_UYVY 2907fc05bdSLad Prabhakar #define RZG2L_CRU_DEFAULT_WIDTH RZG2L_CRU_MIN_INPUT_WIDTH 3007fc05bdSLad Prabhakar #define RZG2L_CRU_DEFAULT_HEIGHT RZG2L_CRU_MIN_INPUT_HEIGHT 3107fc05bdSLad Prabhakar #define RZG2L_CRU_DEFAULT_FIELD V4L2_FIELD_NONE 3207fc05bdSLad Prabhakar #define RZG2L_CRU_DEFAULT_COLORSPACE V4L2_COLORSPACE_SRGB 3307fc05bdSLad Prabhakar 34*1d1e564fSLad Prabhakar #define RZG2L_CRU_STRIDE_MAX 32640 35*1d1e564fSLad Prabhakar #define RZG2L_CRU_STRIDE_ALIGN 128 36*1d1e564fSLad Prabhakar 3707fc05bdSLad Prabhakar struct rzg2l_cru_buffer { 3807fc05bdSLad Prabhakar struct vb2_v4l2_buffer vb; 3907fc05bdSLad Prabhakar struct list_head list; 4007fc05bdSLad Prabhakar }; 4107fc05bdSLad Prabhakar 4207fc05bdSLad Prabhakar #define to_buf_list(vb2_buffer) \ 4307fc05bdSLad Prabhakar (&container_of(vb2_buffer, struct rzg2l_cru_buffer, vb)->list) 4407fc05bdSLad Prabhakar 4507fc05bdSLad Prabhakar /* ----------------------------------------------------------------------------- 4607fc05bdSLad Prabhakar * DMA operations 4707fc05bdSLad Prabhakar */ 48d9063dc5SLad Prabhakar static void __rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value) 4907fc05bdSLad Prabhakar { 50d9063dc5SLad Prabhakar const u16 *regs = cru->info->regs; 51d9063dc5SLad Prabhakar 52d9063dc5SLad Prabhakar /* 53d9063dc5SLad Prabhakar * CRUnCTRL is a first register on all CRU supported SoCs so validate 54d9063dc5SLad Prabhakar * rest of the registers have valid offset being set in cru->info->regs. 55d9063dc5SLad Prabhakar */ 56d9063dc5SLad Prabhakar if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) || 57d9063dc5SLad Prabhakar WARN_ON(offset != CRUnCTRL && regs[offset] == 0)) 58d9063dc5SLad Prabhakar return; 59d9063dc5SLad Prabhakar 60d9063dc5SLad Prabhakar iowrite32(value, cru->base + regs[offset]); 6107fc05bdSLad Prabhakar } 6207fc05bdSLad Prabhakar 63d9063dc5SLad Prabhakar static u32 __rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset) 6407fc05bdSLad Prabhakar { 65d9063dc5SLad Prabhakar const u16 *regs = cru->info->regs; 66d9063dc5SLad Prabhakar 67d9063dc5SLad Prabhakar /* 68d9063dc5SLad Prabhakar * CRUnCTRL is a first register on all CRU supported SoCs so validate 69d9063dc5SLad Prabhakar * rest of the registers have valid offset being set in cru->info->regs. 70d9063dc5SLad Prabhakar */ 71d9063dc5SLad Prabhakar if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) || 72d9063dc5SLad Prabhakar WARN_ON(offset != CRUnCTRL && regs[offset] == 0)) 73d9063dc5SLad Prabhakar return 0; 74d9063dc5SLad Prabhakar 75d9063dc5SLad Prabhakar return ioread32(cru->base + regs[offset]); 7607fc05bdSLad Prabhakar } 7707fc05bdSLad Prabhakar 78d9063dc5SLad Prabhakar static __always_inline void 79d9063dc5SLad Prabhakar __rzg2l_cru_write_constant(struct rzg2l_cru_dev *cru, u32 offset, u32 value) 80d9063dc5SLad Prabhakar { 81d9063dc5SLad Prabhakar const u16 *regs = cru->info->regs; 82d9063dc5SLad Prabhakar 83d9063dc5SLad Prabhakar BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG); 84d9063dc5SLad Prabhakar 85d9063dc5SLad Prabhakar iowrite32(value, cru->base + regs[offset]); 86d9063dc5SLad Prabhakar } 87d9063dc5SLad Prabhakar 88d9063dc5SLad Prabhakar static __always_inline u32 89d9063dc5SLad Prabhakar __rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset) 90d9063dc5SLad Prabhakar { 91d9063dc5SLad Prabhakar const u16 *regs = cru->info->regs; 92d9063dc5SLad Prabhakar 93d9063dc5SLad Prabhakar BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG); 94d9063dc5SLad Prabhakar 95d9063dc5SLad Prabhakar return ioread32(cru->base + regs[offset]); 96d9063dc5SLad Prabhakar } 97d9063dc5SLad Prabhakar 98d9063dc5SLad Prabhakar #define rzg2l_cru_write(cru, offset, value) \ 99d9063dc5SLad Prabhakar (__builtin_constant_p(offset) ? \ 100d9063dc5SLad Prabhakar __rzg2l_cru_write_constant(cru, offset, value) : \ 101d9063dc5SLad Prabhakar __rzg2l_cru_write(cru, offset, value)) 102d9063dc5SLad Prabhakar 103d9063dc5SLad Prabhakar #define rzg2l_cru_read(cru, offset) \ 104d9063dc5SLad Prabhakar (__builtin_constant_p(offset) ? \ 105d9063dc5SLad Prabhakar __rzg2l_cru_read_constant(cru, offset) : \ 106d9063dc5SLad Prabhakar __rzg2l_cru_read(cru, offset)) 107d9063dc5SLad Prabhakar 10807fc05bdSLad Prabhakar /* Need to hold qlock before calling */ 10907fc05bdSLad Prabhakar static void return_unused_buffers(struct rzg2l_cru_dev *cru, 11007fc05bdSLad Prabhakar enum vb2_buffer_state state) 11107fc05bdSLad Prabhakar { 11207fc05bdSLad Prabhakar struct rzg2l_cru_buffer *buf, *node; 11307fc05bdSLad Prabhakar unsigned long flags; 11407fc05bdSLad Prabhakar unsigned int i; 11507fc05bdSLad Prabhakar 11607fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 11707fc05bdSLad Prabhakar for (i = 0; i < cru->num_buf; i++) { 11807fc05bdSLad Prabhakar if (cru->queue_buf[i]) { 11907fc05bdSLad Prabhakar vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, 12007fc05bdSLad Prabhakar state); 12107fc05bdSLad Prabhakar cru->queue_buf[i] = NULL; 12207fc05bdSLad Prabhakar } 12307fc05bdSLad Prabhakar } 12407fc05bdSLad Prabhakar 12507fc05bdSLad Prabhakar list_for_each_entry_safe(buf, node, &cru->buf_list, list) { 12607fc05bdSLad Prabhakar vb2_buffer_done(&buf->vb.vb2_buf, state); 12707fc05bdSLad Prabhakar list_del(&buf->list); 12807fc05bdSLad Prabhakar } 12907fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 13007fc05bdSLad Prabhakar } 13107fc05bdSLad Prabhakar 13207fc05bdSLad Prabhakar static int rzg2l_cru_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, 13307fc05bdSLad Prabhakar unsigned int *nplanes, unsigned int sizes[], 13407fc05bdSLad Prabhakar struct device *alloc_devs[]) 13507fc05bdSLad Prabhakar { 13607fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 13707fc05bdSLad Prabhakar 13807fc05bdSLad Prabhakar /* Make sure the image size is large enough. */ 13907fc05bdSLad Prabhakar if (*nplanes) 14007fc05bdSLad Prabhakar return sizes[0] < cru->format.sizeimage ? -EINVAL : 0; 14107fc05bdSLad Prabhakar 14207fc05bdSLad Prabhakar *nplanes = 1; 14307fc05bdSLad Prabhakar sizes[0] = cru->format.sizeimage; 14407fc05bdSLad Prabhakar 14507fc05bdSLad Prabhakar return 0; 14607fc05bdSLad Prabhakar }; 14707fc05bdSLad Prabhakar 14807fc05bdSLad Prabhakar static int rzg2l_cru_buffer_prepare(struct vb2_buffer *vb) 14907fc05bdSLad Prabhakar { 15007fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); 15107fc05bdSLad Prabhakar unsigned long size = cru->format.sizeimage; 15207fc05bdSLad Prabhakar 15307fc05bdSLad Prabhakar if (vb2_plane_size(vb, 0) < size) { 15407fc05bdSLad Prabhakar dev_err(cru->dev, "buffer too small (%lu < %lu)\n", 15507fc05bdSLad Prabhakar vb2_plane_size(vb, 0), size); 15607fc05bdSLad Prabhakar return -EINVAL; 15707fc05bdSLad Prabhakar } 15807fc05bdSLad Prabhakar 15907fc05bdSLad Prabhakar vb2_set_plane_payload(vb, 0, size); 16007fc05bdSLad Prabhakar 16107fc05bdSLad Prabhakar return 0; 16207fc05bdSLad Prabhakar } 16307fc05bdSLad Prabhakar 16407fc05bdSLad Prabhakar static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb) 16507fc05bdSLad Prabhakar { 16607fc05bdSLad Prabhakar struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 16707fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); 16807fc05bdSLad Prabhakar unsigned long flags; 16907fc05bdSLad Prabhakar 17007fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 17107fc05bdSLad Prabhakar 17207fc05bdSLad Prabhakar list_add_tail(to_buf_list(vbuf), &cru->buf_list); 17307fc05bdSLad Prabhakar 17407fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 17507fc05bdSLad Prabhakar } 17607fc05bdSLad Prabhakar 17707fc05bdSLad Prabhakar static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, 17807fc05bdSLad Prabhakar int slot, dma_addr_t addr) 17907fc05bdSLad Prabhakar { 18007fc05bdSLad Prabhakar /* 18107fc05bdSLad Prabhakar * The address needs to be 512 bytes aligned. Driver should never accept 18207fc05bdSLad Prabhakar * settings that do not satisfy this in the first place... 18307fc05bdSLad Prabhakar */ 18407fc05bdSLad Prabhakar if (WARN_ON((addr) & RZG2L_CRU_HW_BUFFER_MASK)) 18507fc05bdSLad Prabhakar return; 18607fc05bdSLad Prabhakar 18707fc05bdSLad Prabhakar /* Currently, we just use the buffer in 32 bits address */ 18807fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnMBxADDRL(slot), addr); 18907fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnMBxADDRH(slot), 0); 190*1d1e564fSLad Prabhakar 191*1d1e564fSLad Prabhakar cru->buf_addr[slot] = addr; 19207fc05bdSLad Prabhakar } 19307fc05bdSLad Prabhakar 19407fc05bdSLad Prabhakar /* 19507fc05bdSLad Prabhakar * Moves a buffer from the queue to the HW slot. If no buffer is 19607fc05bdSLad Prabhakar * available use the scratch buffer. The scratch buffer is never 19707fc05bdSLad Prabhakar * returned to userspace, its only function is to enable the capture 19807fc05bdSLad Prabhakar * loop to keep running. 19907fc05bdSLad Prabhakar */ 20007fc05bdSLad Prabhakar static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) 20107fc05bdSLad Prabhakar { 20207fc05bdSLad Prabhakar struct vb2_v4l2_buffer *vbuf; 20307fc05bdSLad Prabhakar struct rzg2l_cru_buffer *buf; 20407fc05bdSLad Prabhakar dma_addr_t phys_addr; 20507fc05bdSLad Prabhakar 20607fc05bdSLad Prabhakar /* A already populated slot shall never be overwritten. */ 20707fc05bdSLad Prabhakar if (WARN_ON(cru->queue_buf[slot])) 20807fc05bdSLad Prabhakar return; 20907fc05bdSLad Prabhakar 21007fc05bdSLad Prabhakar dev_dbg(cru->dev, "Filling HW slot: %d\n", slot); 21107fc05bdSLad Prabhakar 21207fc05bdSLad Prabhakar if (list_empty(&cru->buf_list)) { 21307fc05bdSLad Prabhakar cru->queue_buf[slot] = NULL; 21407fc05bdSLad Prabhakar phys_addr = cru->scratch_phys; 21507fc05bdSLad Prabhakar } else { 21607fc05bdSLad Prabhakar /* Keep track of buffer we give to HW */ 21707fc05bdSLad Prabhakar buf = list_entry(cru->buf_list.next, 21807fc05bdSLad Prabhakar struct rzg2l_cru_buffer, list); 21907fc05bdSLad Prabhakar vbuf = &buf->vb; 22007fc05bdSLad Prabhakar list_del_init(to_buf_list(vbuf)); 22107fc05bdSLad Prabhakar cru->queue_buf[slot] = vbuf; 22207fc05bdSLad Prabhakar 22307fc05bdSLad Prabhakar /* Setup DMA */ 22407fc05bdSLad Prabhakar phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); 22507fc05bdSLad Prabhakar } 22607fc05bdSLad Prabhakar 22707fc05bdSLad Prabhakar rzg2l_cru_set_slot_addr(cru, slot, phys_addr); 22807fc05bdSLad Prabhakar } 22907fc05bdSLad Prabhakar 23007fc05bdSLad Prabhakar static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) 23107fc05bdSLad Prabhakar { 232*1d1e564fSLad Prabhakar const struct rzg2l_cru_info *info = cru->info; 23307fc05bdSLad Prabhakar unsigned int slot; 23412564e80SBiju Das u32 amnaxiattr; 23507fc05bdSLad Prabhakar 23607fc05bdSLad Prabhakar /* 23707fc05bdSLad Prabhakar * Set image data memory banks. 23807fc05bdSLad Prabhakar * Currently, we will use maximum address. 23907fc05bdSLad Prabhakar */ 24007fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnMBVALID, AMnMBVALID_MBVALID(cru->num_buf - 1)); 24107fc05bdSLad Prabhakar 24207fc05bdSLad Prabhakar for (slot = 0; slot < cru->num_buf; slot++) 24307fc05bdSLad Prabhakar rzg2l_cru_fill_hw_slot(cru, slot); 24412564e80SBiju Das 245*1d1e564fSLad Prabhakar if (info->has_stride) { 246*1d1e564fSLad Prabhakar u32 stride = cru->format.bytesperline; 247*1d1e564fSLad Prabhakar u32 amnis; 248*1d1e564fSLad Prabhakar 249*1d1e564fSLad Prabhakar stride /= RZG2L_CRU_STRIDE_ALIGN; 250*1d1e564fSLad Prabhakar amnis = rzg2l_cru_read(cru, AMnIS) & ~AMnIS_IS_MASK; 251*1d1e564fSLad Prabhakar rzg2l_cru_write(cru, AMnIS, amnis | AMnIS_IS(stride)); 252*1d1e564fSLad Prabhakar } 253*1d1e564fSLad Prabhakar 25412564e80SBiju Das /* Set AXI burst max length to recommended setting */ 25512564e80SBiju Das amnaxiattr = rzg2l_cru_read(cru, AMnAXIATTR) & ~AMnAXIATTR_AXILEN_MASK; 25612564e80SBiju Das amnaxiattr |= AMnAXIATTR_AXILEN; 25712564e80SBiju Das rzg2l_cru_write(cru, AMnAXIATTR, amnaxiattr); 25807fc05bdSLad Prabhakar } 25907fc05bdSLad Prabhakar 260*1d1e564fSLad Prabhakar void rzg3e_cru_csi2_setup(struct rzg2l_cru_dev *cru, 261*1d1e564fSLad Prabhakar const struct rzg2l_cru_ip_format *ip_fmt, 262*1d1e564fSLad Prabhakar u8 csi_vc) 263*1d1e564fSLad Prabhakar { 264*1d1e564fSLad Prabhakar const struct rzg2l_cru_info *info = cru->info; 265*1d1e564fSLad Prabhakar u32 icnmc = ICnMC_INF(ip_fmt->datatype); 266*1d1e564fSLad Prabhakar 267*1d1e564fSLad Prabhakar icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK; 268*1d1e564fSLad Prabhakar 269*1d1e564fSLad Prabhakar /* Set virtual channel CSI2 */ 270*1d1e564fSLad Prabhakar icnmc |= ICnMC_VCSEL(csi_vc); 271*1d1e564fSLad Prabhakar 272*1d1e564fSLad Prabhakar rzg2l_cru_write(cru, ICnSVCNUM, csi_vc); 273*1d1e564fSLad Prabhakar rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) | 274*1d1e564fSLad Prabhakar ICnSVC_SVC2(2) | ICnSVC_SVC3(3)); 275*1d1e564fSLad Prabhakar rzg2l_cru_write(cru, info->image_conv, icnmc); 276*1d1e564fSLad Prabhakar } 277*1d1e564fSLad Prabhakar 2783c3433c5SLad Prabhakar void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, 279b56dccafSLad Prabhakar const struct rzg2l_cru_ip_format *ip_fmt, 280b56dccafSLad Prabhakar u8 csi_vc) 28107fc05bdSLad Prabhakar { 28248ce5920SLad Prabhakar const struct rzg2l_cru_info *info = cru->info; 283b56dccafSLad Prabhakar u32 icnmc = ICnMC_INF(ip_fmt->datatype); 28407fc05bdSLad Prabhakar 28548ce5920SLad Prabhakar icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK; 28607fc05bdSLad Prabhakar 28707fc05bdSLad Prabhakar /* Set virtual channel CSI2 */ 288c7f3bd38SLad Prabhakar icnmc |= ICnMC_VCSEL(csi_vc); 28907fc05bdSLad Prabhakar 29048ce5920SLad Prabhakar rzg2l_cru_write(cru, info->image_conv, icnmc); 29107fc05bdSLad Prabhakar } 29207fc05bdSLad Prabhakar 29307fc05bdSLad Prabhakar static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, 294c7f3bd38SLad Prabhakar struct v4l2_mbus_framefmt *ip_sd_fmt, 295c7f3bd38SLad Prabhakar u8 csi_vc) 29607fc05bdSLad Prabhakar { 29748ce5920SLad Prabhakar const struct rzg2l_cru_info *info = cru->info; 298c6ed80fdSLad Prabhakar const struct rzg2l_cru_ip_format *cru_video_fmt; 299b56dccafSLad Prabhakar const struct rzg2l_cru_ip_format *cru_ip_fmt; 30007fc05bdSLad Prabhakar 301b56dccafSLad Prabhakar cru_ip_fmt = rzg2l_cru_ip_code_to_fmt(ip_sd_fmt->code); 3023c3433c5SLad Prabhakar info->csi_setup(cru, cru_ip_fmt, csi_vc); 30307fc05bdSLad Prabhakar 30407fc05bdSLad Prabhakar /* Output format */ 305c6ed80fdSLad Prabhakar cru_video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat); 306c6ed80fdSLad Prabhakar if (!cru_video_fmt) { 30707fc05bdSLad Prabhakar dev_err(cru->dev, "Invalid pixelformat (0x%x)\n", 30807fc05bdSLad Prabhakar cru->format.pixelformat); 30907fc05bdSLad Prabhakar return -EINVAL; 31007fc05bdSLad Prabhakar } 31107fc05bdSLad Prabhakar 31207fc05bdSLad Prabhakar /* If input and output use same colorspace, do bypass mode */ 3132269e399SLad Prabhakar if (cru_ip_fmt->yuv == cru_video_fmt->yuv) 31448ce5920SLad Prabhakar rzg2l_cru_write(cru, info->image_conv, 31548ce5920SLad Prabhakar rzg2l_cru_read(cru, info->image_conv) | ICnMC_CSCTHR); 31607fc05bdSLad Prabhakar else 31748ce5920SLad Prabhakar rzg2l_cru_write(cru, info->image_conv, 31848ce5920SLad Prabhakar rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_CSCTHR); 31907fc05bdSLad Prabhakar 32007fc05bdSLad Prabhakar /* Set output data format */ 321c6ed80fdSLad Prabhakar rzg2l_cru_write(cru, ICnDMR, cru_video_fmt->icndmr); 32207fc05bdSLad Prabhakar 32307fc05bdSLad Prabhakar return 0; 32407fc05bdSLad Prabhakar } 32507fc05bdSLad Prabhakar 326*1d1e564fSLad Prabhakar bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru) 327*1d1e564fSLad Prabhakar { 328*1d1e564fSLad Prabhakar u32 amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); 329*1d1e564fSLad Prabhakar 330*1d1e564fSLad Prabhakar if ((((amnfifopntr & AMnFIFOPNTR_FIFORPNTR_B1) >> 24) == 331*1d1e564fSLad Prabhakar ((amnfifopntr & AMnFIFOPNTR_FIFOWPNTR_B1) >> 8)) && 332*1d1e564fSLad Prabhakar (((amnfifopntr & AMnFIFOPNTR_FIFORPNTR_B0) >> 16) == 333*1d1e564fSLad Prabhakar (amnfifopntr & AMnFIFOPNTR_FIFOWPNTR_B0))) 334*1d1e564fSLad Prabhakar return true; 335*1d1e564fSLad Prabhakar 336*1d1e564fSLad Prabhakar return false; 337*1d1e564fSLad Prabhakar } 338*1d1e564fSLad Prabhakar 339446c645fSLad Prabhakar bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru) 34007fc05bdSLad Prabhakar { 34107fc05bdSLad Prabhakar u32 amnfifopntr, amnfifopntr_w, amnfifopntr_r_y; 342446c645fSLad Prabhakar 343446c645fSLad Prabhakar amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); 344446c645fSLad Prabhakar 345446c645fSLad Prabhakar amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR; 346446c645fSLad Prabhakar amnfifopntr_r_y = 347446c645fSLad Prabhakar (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16; 348446c645fSLad Prabhakar if (amnfifopntr_w == amnfifopntr_r_y) 349446c645fSLad Prabhakar return true; 350446c645fSLad Prabhakar 351446c645fSLad Prabhakar return amnfifopntr_w == amnfifopntr_r_y; 352446c645fSLad Prabhakar } 353446c645fSLad Prabhakar 354446c645fSLad Prabhakar void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) 355446c645fSLad Prabhakar { 35607fc05bdSLad Prabhakar unsigned int retries = 0; 35707fc05bdSLad Prabhakar unsigned long flags; 35807fc05bdSLad Prabhakar u32 icnms; 35907fc05bdSLad Prabhakar 36007fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 36107fc05bdSLad Prabhakar 36207fc05bdSLad Prabhakar /* Disable and clear the interrupt */ 3632d9e3eb7SLad Prabhakar cru->info->disable_interrupts(cru); 36407fc05bdSLad Prabhakar 36507fc05bdSLad Prabhakar /* Stop the operation of image conversion */ 36607fc05bdSLad Prabhakar rzg2l_cru_write(cru, ICnEN, 0); 36707fc05bdSLad Prabhakar 36807fc05bdSLad Prabhakar /* Wait for streaming to stop */ 36907fc05bdSLad Prabhakar while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) { 37007fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 37107fc05bdSLad Prabhakar msleep(RZG2L_TIMEOUT_MS); 37207fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 37307fc05bdSLad Prabhakar } 37407fc05bdSLad Prabhakar 37507fc05bdSLad Prabhakar icnms = rzg2l_cru_read(cru, ICnMS) & ICnMS_IA; 37607fc05bdSLad Prabhakar if (icnms) 37707fc05bdSLad Prabhakar dev_err(cru->dev, "Failed stop HW, something is seriously broken\n"); 37807fc05bdSLad Prabhakar 37907fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STOPPED; 38007fc05bdSLad Prabhakar 38107fc05bdSLad Prabhakar /* Wait until the FIFO becomes empty */ 38207fc05bdSLad Prabhakar for (retries = 5; retries > 0; retries--) { 383446c645fSLad Prabhakar if (cru->info->fifo_empty(cru)) 38407fc05bdSLad Prabhakar break; 38507fc05bdSLad Prabhakar 38607fc05bdSLad Prabhakar usleep_range(10, 20); 38707fc05bdSLad Prabhakar } 38807fc05bdSLad Prabhakar 38907fc05bdSLad Prabhakar /* Notify that FIFO is not empty here */ 39007fc05bdSLad Prabhakar if (!retries) 39107fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to empty FIFO\n"); 39207fc05bdSLad Prabhakar 39307fc05bdSLad Prabhakar /* Stop AXI bus */ 39407fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnAXISTP, AMnAXISTP_AXI_STOP); 39507fc05bdSLad Prabhakar 39607fc05bdSLad Prabhakar /* Wait until the AXI bus stop */ 39707fc05bdSLad Prabhakar for (retries = 5; retries > 0; retries--) { 39807fc05bdSLad Prabhakar if (rzg2l_cru_read(cru, AMnAXISTPACK) & 39907fc05bdSLad Prabhakar AMnAXISTPACK_AXI_STOP_ACK) 40007fc05bdSLad Prabhakar break; 40107fc05bdSLad Prabhakar 40207fc05bdSLad Prabhakar usleep_range(10, 20); 4037206fcc5SYang Li } 40407fc05bdSLad Prabhakar 40507fc05bdSLad Prabhakar /* Notify that AXI bus can not stop here */ 40607fc05bdSLad Prabhakar if (!retries) 40707fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to stop AXI bus\n"); 40807fc05bdSLad Prabhakar 40907fc05bdSLad Prabhakar /* Cancel the AXI bus stop request */ 41007fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnAXISTP, 0); 41107fc05bdSLad Prabhakar 41207fc05bdSLad Prabhakar /* Reset the CRU (AXI-master) */ 41307fc05bdSLad Prabhakar reset_control_assert(cru->aresetn); 41407fc05bdSLad Prabhakar 41507fc05bdSLad Prabhakar /* Resets the image processing module */ 41607fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnRST, 0); 41707fc05bdSLad Prabhakar 41807fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 41907fc05bdSLad Prabhakar } 42007fc05bdSLad Prabhakar 421d7d72daeSLad Prabhakar static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) 422d7d72daeSLad Prabhakar { 423d7d72daeSLad Prabhakar struct v4l2_mbus_frame_desc fd = { }; 424d7d72daeSLad Prabhakar struct media_pad *remote_pad; 425d7d72daeSLad Prabhakar int ret; 426d7d72daeSLad Prabhakar 427d7d72daeSLad Prabhakar remote_pad = media_pad_remote_pad_unique(&cru->ip.pads[RZG2L_CRU_IP_SINK]); 428d7d72daeSLad Prabhakar ret = v4l2_subdev_call(cru->ip.remote, pad, get_frame_desc, remote_pad->index, &fd); 429d7d72daeSLad Prabhakar if (ret < 0 && ret != -ENOIOCTLCMD) { 430d7d72daeSLad Prabhakar dev_err(cru->dev, "get_frame_desc failed on IP remote subdev\n"); 431d7d72daeSLad Prabhakar return ret; 432d7d72daeSLad Prabhakar } 433d7d72daeSLad Prabhakar /* If remote subdev does not implement .get_frame_desc default to VC0. */ 434d7d72daeSLad Prabhakar if (ret == -ENOIOCTLCMD) 435d7d72daeSLad Prabhakar return 0; 436d7d72daeSLad Prabhakar 437d7d72daeSLad Prabhakar if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { 438d7d72daeSLad Prabhakar dev_err(cru->dev, "get_frame_desc returned invalid bus type %d\n", fd.type); 439d7d72daeSLad Prabhakar return -EINVAL; 440d7d72daeSLad Prabhakar } 441d7d72daeSLad Prabhakar 442d7d72daeSLad Prabhakar if (!fd.num_entries) { 443d7d72daeSLad Prabhakar dev_err(cru->dev, "get_frame_desc returned zero entries\n"); 444d7d72daeSLad Prabhakar return -EINVAL; 445d7d72daeSLad Prabhakar } 446d7d72daeSLad Prabhakar 447d7d72daeSLad Prabhakar return fd.entry[0].bus.csi2.vc; 448d7d72daeSLad Prabhakar } 449d7d72daeSLad Prabhakar 450*1d1e564fSLad Prabhakar void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru) 451*1d1e564fSLad Prabhakar { 452*1d1e564fSLad Prabhakar rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FSxE(cru->svc_channel)); 453*1d1e564fSLad Prabhakar rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FExE(cru->svc_channel)); 454*1d1e564fSLad Prabhakar } 455*1d1e564fSLad Prabhakar 456*1d1e564fSLad Prabhakar void rzg3e_cru_disable_interrupts(struct rzg2l_cru_dev *cru) 457*1d1e564fSLad Prabhakar { 458*1d1e564fSLad Prabhakar rzg2l_cru_write(cru, CRUnIE, 0); 459*1d1e564fSLad Prabhakar rzg2l_cru_write(cru, CRUnIE2, 0); 460*1d1e564fSLad Prabhakar rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); 461*1d1e564fSLad Prabhakar rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); 462*1d1e564fSLad Prabhakar } 463*1d1e564fSLad Prabhakar 4642d9e3eb7SLad Prabhakar void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru) 4652d9e3eb7SLad Prabhakar { 4662d9e3eb7SLad Prabhakar rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE); 4672d9e3eb7SLad Prabhakar } 4682d9e3eb7SLad Prabhakar 4692d9e3eb7SLad Prabhakar void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru) 4702d9e3eb7SLad Prabhakar { 4712d9e3eb7SLad Prabhakar rzg2l_cru_write(cru, CRUnIE, 0); 4722d9e3eb7SLad Prabhakar rzg2l_cru_write(cru, CRUnINTS, 0x001f000f); 4732d9e3eb7SLad Prabhakar } 4742d9e3eb7SLad Prabhakar 47507fc05bdSLad Prabhakar int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) 47607fc05bdSLad Prabhakar { 47707fc05bdSLad Prabhakar struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru); 47807fc05bdSLad Prabhakar unsigned long flags; 479c7f3bd38SLad Prabhakar u8 csi_vc; 48007fc05bdSLad Prabhakar int ret; 48107fc05bdSLad Prabhakar 482d7d72daeSLad Prabhakar ret = rzg2l_cru_get_virtual_channel(cru); 483d7d72daeSLad Prabhakar if (ret < 0) 484d7d72daeSLad Prabhakar return ret; 485c7f3bd38SLad Prabhakar csi_vc = ret; 486*1d1e564fSLad Prabhakar cru->svc_channel = csi_vc; 487d7d72daeSLad Prabhakar 48807fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 48907fc05bdSLad Prabhakar 49007fc05bdSLad Prabhakar /* Select a video input */ 49107fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnCTRL, CRUnCTRL_VINSEL(0)); 49207fc05bdSLad Prabhakar 49307fc05bdSLad Prabhakar /* Cancel the software reset for image processing block */ 49407fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnRST, CRUnRST_VRESETN); 49507fc05bdSLad Prabhakar 49607fc05bdSLad Prabhakar /* Disable and clear the interrupt before using */ 4972d9e3eb7SLad Prabhakar cru->info->disable_interrupts(cru); 49807fc05bdSLad Prabhakar 49907fc05bdSLad Prabhakar /* Initialize the AXI master */ 50007fc05bdSLad Prabhakar rzg2l_cru_initialize_axi(cru); 50107fc05bdSLad Prabhakar 50227673948SBiju Das /* Initialize image convert */ 503c7f3bd38SLad Prabhakar ret = rzg2l_cru_initialize_image_conv(cru, fmt, csi_vc); 50427673948SBiju Das if (ret) { 50527673948SBiju Das spin_unlock_irqrestore(&cru->qlock, flags); 50627673948SBiju Das return ret; 50727673948SBiju Das } 50827673948SBiju Das 50907fc05bdSLad Prabhakar /* Enable interrupt */ 5102d9e3eb7SLad Prabhakar cru->info->enable_interrupts(cru); 51107fc05bdSLad Prabhakar 51207fc05bdSLad Prabhakar /* Enable image processing reception */ 51307fc05bdSLad Prabhakar rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN); 51407fc05bdSLad Prabhakar 51507fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 51607fc05bdSLad Prabhakar 51707fc05bdSLad Prabhakar return 0; 51807fc05bdSLad Prabhakar } 51907fc05bdSLad Prabhakar 52007fc05bdSLad Prabhakar static int rzg2l_cru_set_stream(struct rzg2l_cru_dev *cru, int on) 52107fc05bdSLad Prabhakar { 52207fc05bdSLad Prabhakar struct media_pipeline *pipe; 52307fc05bdSLad Prabhakar struct v4l2_subdev *sd; 52407fc05bdSLad Prabhakar struct media_pad *pad; 52507fc05bdSLad Prabhakar int ret; 52607fc05bdSLad Prabhakar 52707fc05bdSLad Prabhakar pad = media_pad_remote_pad_first(&cru->pad); 52807fc05bdSLad Prabhakar if (!pad) 52907fc05bdSLad Prabhakar return -EPIPE; 53007fc05bdSLad Prabhakar 53107fc05bdSLad Prabhakar sd = media_entity_to_v4l2_subdev(pad->entity); 53207fc05bdSLad Prabhakar 53307fc05bdSLad Prabhakar if (!on) { 53407fc05bdSLad Prabhakar int stream_off_ret = 0; 53507fc05bdSLad Prabhakar 53607fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, s_stream, 0); 53707fc05bdSLad Prabhakar if (ret) 53807fc05bdSLad Prabhakar stream_off_ret = ret; 53907fc05bdSLad Prabhakar 54007fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, post_streamoff); 54107fc05bdSLad Prabhakar if (ret == -ENOIOCTLCMD) 54207fc05bdSLad Prabhakar ret = 0; 54307fc05bdSLad Prabhakar if (ret && !stream_off_ret) 54407fc05bdSLad Prabhakar stream_off_ret = ret; 54507fc05bdSLad Prabhakar 54607fc05bdSLad Prabhakar video_device_pipeline_stop(&cru->vdev); 54707fc05bdSLad Prabhakar 54807fc05bdSLad Prabhakar return stream_off_ret; 54907fc05bdSLad Prabhakar } 55007fc05bdSLad Prabhakar 55107fc05bdSLad Prabhakar pipe = media_entity_pipeline(&sd->entity) ? : &cru->vdev.pipe; 55207fc05bdSLad Prabhakar ret = video_device_pipeline_start(&cru->vdev, pipe); 55307fc05bdSLad Prabhakar if (ret) 5546bcff5f9SBiju Das return ret; 55507fc05bdSLad Prabhakar 55607fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, pre_streamon, 0); 5576bcff5f9SBiju Das if (ret && ret != -ENOIOCTLCMD) 55807fc05bdSLad Prabhakar goto pipe_line_stop; 55907fc05bdSLad Prabhakar 56007fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, s_stream, 1); 5616bcff5f9SBiju Das if (ret && ret != -ENOIOCTLCMD) 56207fc05bdSLad Prabhakar goto err_s_stream; 56307fc05bdSLad Prabhakar 56407fc05bdSLad Prabhakar return 0; 56507fc05bdSLad Prabhakar 56607fc05bdSLad Prabhakar err_s_stream: 56707fc05bdSLad Prabhakar v4l2_subdev_call(sd, video, post_streamoff); 56807fc05bdSLad Prabhakar 56907fc05bdSLad Prabhakar pipe_line_stop: 57007fc05bdSLad Prabhakar video_device_pipeline_stop(&cru->vdev); 57107fc05bdSLad Prabhakar 57207fc05bdSLad Prabhakar return ret; 57307fc05bdSLad Prabhakar } 57407fc05bdSLad Prabhakar 57507fc05bdSLad Prabhakar static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru) 57607fc05bdSLad Prabhakar { 57707fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STOPPING; 57807fc05bdSLad Prabhakar 57907fc05bdSLad Prabhakar rzg2l_cru_set_stream(cru, 0); 58007fc05bdSLad Prabhakar } 58107fc05bdSLad Prabhakar 5820c200daaSBiju Das irqreturn_t rzg2l_cru_irq(int irq, void *data) 58307fc05bdSLad Prabhakar { 58407fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = data; 58507fc05bdSLad Prabhakar unsigned int handled = 0; 58607fc05bdSLad Prabhakar unsigned long flags; 58707fc05bdSLad Prabhakar u32 irq_status; 58807fc05bdSLad Prabhakar u32 amnmbs; 58907fc05bdSLad Prabhakar int slot; 59007fc05bdSLad Prabhakar 59107fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 59207fc05bdSLad Prabhakar 59307fc05bdSLad Prabhakar irq_status = rzg2l_cru_read(cru, CRUnINTS); 59407fc05bdSLad Prabhakar if (!irq_status) 59507fc05bdSLad Prabhakar goto done; 59607fc05bdSLad Prabhakar 59707fc05bdSLad Prabhakar handled = 1; 59807fc05bdSLad Prabhakar 59907fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); 60007fc05bdSLad Prabhakar 60107fc05bdSLad Prabhakar /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ 60207fc05bdSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STOPPED) { 60307fc05bdSLad Prabhakar dev_dbg(cru->dev, "IRQ while state stopped\n"); 60407fc05bdSLad Prabhakar goto done; 60507fc05bdSLad Prabhakar } 60607fc05bdSLad Prabhakar 60707fc05bdSLad Prabhakar /* Increase stop retries if capture status is 'RZG2L_CRU_DMA_STOPPING' */ 60807fc05bdSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STOPPING) { 60907fc05bdSLad Prabhakar if (irq_status & CRUnINTS_SFS) 61007fc05bdSLad Prabhakar dev_dbg(cru->dev, "IRQ while state stopping\n"); 61107fc05bdSLad Prabhakar goto done; 61207fc05bdSLad Prabhakar } 61307fc05bdSLad Prabhakar 61407fc05bdSLad Prabhakar /* Prepare for capture and update state */ 61507fc05bdSLad Prabhakar amnmbs = rzg2l_cru_read(cru, AMnMBS); 61607fc05bdSLad Prabhakar slot = amnmbs & AMnMBS_MBSTS; 61707fc05bdSLad Prabhakar 61807fc05bdSLad Prabhakar /* 61907fc05bdSLad Prabhakar * AMnMBS.MBSTS indicates the destination of Memory Bank (MB). 62007fc05bdSLad Prabhakar * Recalculate to get the current transfer complete MB. 62107fc05bdSLad Prabhakar */ 62207fc05bdSLad Prabhakar if (slot == 0) 62307fc05bdSLad Prabhakar slot = cru->num_buf - 1; 62407fc05bdSLad Prabhakar else 62507fc05bdSLad Prabhakar slot--; 62607fc05bdSLad Prabhakar 62707fc05bdSLad Prabhakar /* 62807fc05bdSLad Prabhakar * To hand buffers back in a known order to userspace start 62907fc05bdSLad Prabhakar * to capture first from slot 0. 63007fc05bdSLad Prabhakar */ 63107fc05bdSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STARTING) { 63207fc05bdSLad Prabhakar if (slot != 0) { 63307fc05bdSLad Prabhakar dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); 63407fc05bdSLad Prabhakar goto done; 63507fc05bdSLad Prabhakar } 63607fc05bdSLad Prabhakar 63707fc05bdSLad Prabhakar dev_dbg(cru->dev, "Capture start synced!\n"); 63807fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_RUNNING; 63907fc05bdSLad Prabhakar } 64007fc05bdSLad Prabhakar 64107fc05bdSLad Prabhakar /* Capture frame */ 64207fc05bdSLad Prabhakar if (cru->queue_buf[slot]) { 64307fc05bdSLad Prabhakar cru->queue_buf[slot]->field = cru->format.field; 64407fc05bdSLad Prabhakar cru->queue_buf[slot]->sequence = cru->sequence; 64507fc05bdSLad Prabhakar cru->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); 64607fc05bdSLad Prabhakar vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, 64707fc05bdSLad Prabhakar VB2_BUF_STATE_DONE); 64807fc05bdSLad Prabhakar cru->queue_buf[slot] = NULL; 64907fc05bdSLad Prabhakar } else { 65007fc05bdSLad Prabhakar /* Scratch buffer was used, dropping frame. */ 65107fc05bdSLad Prabhakar dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); 65207fc05bdSLad Prabhakar } 65307fc05bdSLad Prabhakar 65407fc05bdSLad Prabhakar cru->sequence++; 65507fc05bdSLad Prabhakar 65607fc05bdSLad Prabhakar /* Prepare for next frame */ 65707fc05bdSLad Prabhakar rzg2l_cru_fill_hw_slot(cru, slot); 65807fc05bdSLad Prabhakar 65907fc05bdSLad Prabhakar done: 66007fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 66107fc05bdSLad Prabhakar 66207fc05bdSLad Prabhakar return IRQ_RETVAL(handled); 66307fc05bdSLad Prabhakar } 66407fc05bdSLad Prabhakar 665*1d1e564fSLad Prabhakar static int rzg3e_cru_get_current_slot(struct rzg2l_cru_dev *cru) 666*1d1e564fSLad Prabhakar { 667*1d1e564fSLad Prabhakar u64 amnmadrs; 668*1d1e564fSLad Prabhakar int slot; 669*1d1e564fSLad Prabhakar 670*1d1e564fSLad Prabhakar /* 671*1d1e564fSLad Prabhakar * When AMnMADRSL is read, AMnMADRSH of the higher-order 672*1d1e564fSLad Prabhakar * address also latches the address. 673*1d1e564fSLad Prabhakar * 674*1d1e564fSLad Prabhakar * AMnMADRSH must be read after AMnMADRSL has been read. 675*1d1e564fSLad Prabhakar */ 676*1d1e564fSLad Prabhakar amnmadrs = rzg2l_cru_read(cru, AMnMADRSL); 677*1d1e564fSLad Prabhakar amnmadrs |= (u64)rzg2l_cru_read(cru, AMnMADRSH) << 32; 678*1d1e564fSLad Prabhakar 679*1d1e564fSLad Prabhakar /* Ensure amnmadrs is within this buffer range */ 680*1d1e564fSLad Prabhakar for (slot = 0; slot < cru->num_buf; slot++) { 681*1d1e564fSLad Prabhakar if (amnmadrs >= cru->buf_addr[slot] && 682*1d1e564fSLad Prabhakar amnmadrs < cru->buf_addr[slot] + cru->format.sizeimage) 683*1d1e564fSLad Prabhakar return slot; 684*1d1e564fSLad Prabhakar } 685*1d1e564fSLad Prabhakar 686*1d1e564fSLad Prabhakar dev_err(cru->dev, "Invalid MB address 0x%llx (out of range)\n", amnmadrs); 687*1d1e564fSLad Prabhakar return -EINVAL; 688*1d1e564fSLad Prabhakar } 689*1d1e564fSLad Prabhakar 690*1d1e564fSLad Prabhakar irqreturn_t rzg3e_cru_irq(int irq, void *data) 691*1d1e564fSLad Prabhakar { 692*1d1e564fSLad Prabhakar struct rzg2l_cru_dev *cru = data; 693*1d1e564fSLad Prabhakar u32 irq_status; 694*1d1e564fSLad Prabhakar int slot; 695*1d1e564fSLad Prabhakar 696*1d1e564fSLad Prabhakar scoped_guard(spinlock, &cru->qlock) { 697*1d1e564fSLad Prabhakar irq_status = rzg2l_cru_read(cru, CRUnINTS2); 698*1d1e564fSLad Prabhakar if (!irq_status) 699*1d1e564fSLad Prabhakar return IRQ_NONE; 700*1d1e564fSLad Prabhakar 701*1d1e564fSLad Prabhakar dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status); 702*1d1e564fSLad Prabhakar 703*1d1e564fSLad Prabhakar rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2)); 704*1d1e564fSLad Prabhakar 705*1d1e564fSLad Prabhakar /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ 706*1d1e564fSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STOPPED) { 707*1d1e564fSLad Prabhakar dev_dbg(cru->dev, "IRQ while state stopped\n"); 708*1d1e564fSLad Prabhakar return IRQ_HANDLED; 709*1d1e564fSLad Prabhakar } 710*1d1e564fSLad Prabhakar 711*1d1e564fSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STOPPING) { 712*1d1e564fSLad Prabhakar if (irq_status & CRUnINTS2_FSxS(0) || 713*1d1e564fSLad Prabhakar irq_status & CRUnINTS2_FSxS(1) || 714*1d1e564fSLad Prabhakar irq_status & CRUnINTS2_FSxS(2) || 715*1d1e564fSLad Prabhakar irq_status & CRUnINTS2_FSxS(3)) 716*1d1e564fSLad Prabhakar dev_dbg(cru->dev, "IRQ while state stopping\n"); 717*1d1e564fSLad Prabhakar return IRQ_HANDLED; 718*1d1e564fSLad Prabhakar } 719*1d1e564fSLad Prabhakar 720*1d1e564fSLad Prabhakar slot = rzg3e_cru_get_current_slot(cru); 721*1d1e564fSLad Prabhakar if (slot < 0) 722*1d1e564fSLad Prabhakar return IRQ_HANDLED; 723*1d1e564fSLad Prabhakar 724*1d1e564fSLad Prabhakar dev_dbg(cru->dev, "Current written slot: %d\n", slot); 725*1d1e564fSLad Prabhakar cru->buf_addr[slot] = 0; 726*1d1e564fSLad Prabhakar 727*1d1e564fSLad Prabhakar /* 728*1d1e564fSLad Prabhakar * To hand buffers back in a known order to userspace start 729*1d1e564fSLad Prabhakar * to capture first from slot 0. 730*1d1e564fSLad Prabhakar */ 731*1d1e564fSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STARTING) { 732*1d1e564fSLad Prabhakar if (slot != 0) { 733*1d1e564fSLad Prabhakar dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); 734*1d1e564fSLad Prabhakar return IRQ_HANDLED; 735*1d1e564fSLad Prabhakar } 736*1d1e564fSLad Prabhakar dev_dbg(cru->dev, "Capture start synced!\n"); 737*1d1e564fSLad Prabhakar cru->state = RZG2L_CRU_DMA_RUNNING; 738*1d1e564fSLad Prabhakar } 739*1d1e564fSLad Prabhakar 740*1d1e564fSLad Prabhakar /* Capture frame */ 741*1d1e564fSLad Prabhakar if (cru->queue_buf[slot]) { 742*1d1e564fSLad Prabhakar struct vb2_v4l2_buffer *buf = cru->queue_buf[slot]; 743*1d1e564fSLad Prabhakar 744*1d1e564fSLad Prabhakar buf->field = cru->format.field; 745*1d1e564fSLad Prabhakar buf->sequence = cru->sequence; 746*1d1e564fSLad Prabhakar buf->vb2_buf.timestamp = ktime_get_ns(); 747*1d1e564fSLad Prabhakar vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE); 748*1d1e564fSLad Prabhakar cru->queue_buf[slot] = NULL; 749*1d1e564fSLad Prabhakar } else { 750*1d1e564fSLad Prabhakar /* Scratch buffer was used, dropping frame. */ 751*1d1e564fSLad Prabhakar dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); 752*1d1e564fSLad Prabhakar } 753*1d1e564fSLad Prabhakar 754*1d1e564fSLad Prabhakar cru->sequence++; 755*1d1e564fSLad Prabhakar 756*1d1e564fSLad Prabhakar /* Prepare for next frame */ 757*1d1e564fSLad Prabhakar rzg2l_cru_fill_hw_slot(cru, slot); 758*1d1e564fSLad Prabhakar } 759*1d1e564fSLad Prabhakar 760*1d1e564fSLad Prabhakar return IRQ_HANDLED; 761*1d1e564fSLad Prabhakar } 762*1d1e564fSLad Prabhakar 76307fc05bdSLad Prabhakar static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count) 76407fc05bdSLad Prabhakar { 76507fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 76607fc05bdSLad Prabhakar int ret; 76707fc05bdSLad Prabhakar 7686bcff5f9SBiju Das ret = pm_runtime_resume_and_get(cru->dev); 7696bcff5f9SBiju Das if (ret) 7706bcff5f9SBiju Das return ret; 7716bcff5f9SBiju Das 7726bcff5f9SBiju Das ret = clk_prepare_enable(cru->vclk); 7736bcff5f9SBiju Das if (ret) 7746bcff5f9SBiju Das goto err_pm_put; 7756bcff5f9SBiju Das 77607fc05bdSLad Prabhakar /* Release reset state */ 77707fc05bdSLad Prabhakar ret = reset_control_deassert(cru->aresetn); 77807fc05bdSLad Prabhakar if (ret) { 77907fc05bdSLad Prabhakar dev_err(cru->dev, "failed to deassert aresetn\n"); 7806bcff5f9SBiju Das goto err_vclk_disable; 78107fc05bdSLad Prabhakar } 78207fc05bdSLad Prabhakar 78307fc05bdSLad Prabhakar ret = reset_control_deassert(cru->presetn); 78407fc05bdSLad Prabhakar if (ret) { 78507fc05bdSLad Prabhakar reset_control_assert(cru->aresetn); 78607fc05bdSLad Prabhakar dev_err(cru->dev, "failed to deassert presetn\n"); 7876bcff5f9SBiju Das goto assert_aresetn; 78807fc05bdSLad Prabhakar } 78907fc05bdSLad Prabhakar 79094794b5cSBiju Das /* Allocate scratch buffer */ 79107fc05bdSLad Prabhakar cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage, 79207fc05bdSLad Prabhakar &cru->scratch_phys, GFP_KERNEL); 79307fc05bdSLad Prabhakar if (!cru->scratch) { 79407fc05bdSLad Prabhakar return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); 79507fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to allocate scratch buffer\n"); 796e54334cdSJiapeng Chong ret = -ENOMEM; 7970c200daaSBiju Das goto assert_presetn; 79807fc05bdSLad Prabhakar } 79907fc05bdSLad Prabhakar 80007fc05bdSLad Prabhakar cru->sequence = 0; 80107fc05bdSLad Prabhakar 80207fc05bdSLad Prabhakar ret = rzg2l_cru_set_stream(cru, 1); 80307fc05bdSLad Prabhakar if (ret) { 80407fc05bdSLad Prabhakar return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); 80507fc05bdSLad Prabhakar goto out; 80607fc05bdSLad Prabhakar } 80707fc05bdSLad Prabhakar 80807fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STARTING; 80907fc05bdSLad Prabhakar dev_dbg(cru->dev, "Starting to capture\n"); 81007fc05bdSLad Prabhakar return 0; 81107fc05bdSLad Prabhakar 81207fc05bdSLad Prabhakar out: 81307fc05bdSLad Prabhakar if (ret) 81407fc05bdSLad Prabhakar dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch, 81507fc05bdSLad Prabhakar cru->scratch_phys); 8166bcff5f9SBiju Das assert_presetn: 81707fc05bdSLad Prabhakar reset_control_assert(cru->presetn); 8186bcff5f9SBiju Das 8196bcff5f9SBiju Das assert_aresetn: 82007fc05bdSLad Prabhakar reset_control_assert(cru->aresetn); 82107fc05bdSLad Prabhakar 8226bcff5f9SBiju Das err_vclk_disable: 8236bcff5f9SBiju Das clk_disable_unprepare(cru->vclk); 8246bcff5f9SBiju Das 8256bcff5f9SBiju Das err_pm_put: 8266bcff5f9SBiju Das pm_runtime_put_sync(cru->dev); 8276bcff5f9SBiju Das 82807fc05bdSLad Prabhakar return ret; 82907fc05bdSLad Prabhakar } 83007fc05bdSLad Prabhakar 83107fc05bdSLad Prabhakar static void rzg2l_cru_stop_streaming_vq(struct vb2_queue *vq) 83207fc05bdSLad Prabhakar { 83307fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 83407fc05bdSLad Prabhakar 83507fc05bdSLad Prabhakar rzg2l_cru_stop_streaming(cru); 83607fc05bdSLad Prabhakar 83707fc05bdSLad Prabhakar /* Free scratch buffer */ 83807fc05bdSLad Prabhakar dma_free_coherent(cru->dev, cru->format.sizeimage, 83907fc05bdSLad Prabhakar cru->scratch, cru->scratch_phys); 84007fc05bdSLad Prabhakar 84107fc05bdSLad Prabhakar return_unused_buffers(cru, VB2_BUF_STATE_ERROR); 8426bcff5f9SBiju Das 8436bcff5f9SBiju Das reset_control_assert(cru->presetn); 8446bcff5f9SBiju Das clk_disable_unprepare(cru->vclk); 8456bcff5f9SBiju Das pm_runtime_put_sync(cru->dev); 84607fc05bdSLad Prabhakar } 84707fc05bdSLad Prabhakar 84807fc05bdSLad Prabhakar static const struct vb2_ops rzg2l_cru_qops = { 84907fc05bdSLad Prabhakar .queue_setup = rzg2l_cru_queue_setup, 85007fc05bdSLad Prabhakar .buf_prepare = rzg2l_cru_buffer_prepare, 85107fc05bdSLad Prabhakar .buf_queue = rzg2l_cru_buffer_queue, 85207fc05bdSLad Prabhakar .start_streaming = rzg2l_cru_start_streaming_vq, 85307fc05bdSLad Prabhakar .stop_streaming = rzg2l_cru_stop_streaming_vq, 85407fc05bdSLad Prabhakar }; 85507fc05bdSLad Prabhakar 85607fc05bdSLad Prabhakar void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru) 85707fc05bdSLad Prabhakar { 85807fc05bdSLad Prabhakar mutex_destroy(&cru->lock); 85907fc05bdSLad Prabhakar 86007fc05bdSLad Prabhakar v4l2_device_unregister(&cru->v4l2_dev); 86107fc05bdSLad Prabhakar vb2_queue_release(&cru->queue); 86207fc05bdSLad Prabhakar } 86307fc05bdSLad Prabhakar 86407fc05bdSLad Prabhakar int rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru) 86507fc05bdSLad Prabhakar { 86607fc05bdSLad Prabhakar struct vb2_queue *q = &cru->queue; 86707fc05bdSLad Prabhakar unsigned int i; 86807fc05bdSLad Prabhakar int ret; 86907fc05bdSLad Prabhakar 87007fc05bdSLad Prabhakar /* Initialize the top-level structure */ 87107fc05bdSLad Prabhakar ret = v4l2_device_register(cru->dev, &cru->v4l2_dev); 87207fc05bdSLad Prabhakar if (ret) 87307fc05bdSLad Prabhakar return ret; 87407fc05bdSLad Prabhakar 87507fc05bdSLad Prabhakar mutex_init(&cru->lock); 87607fc05bdSLad Prabhakar INIT_LIST_HEAD(&cru->buf_list); 87707fc05bdSLad Prabhakar 87807fc05bdSLad Prabhakar spin_lock_init(&cru->qlock); 87907fc05bdSLad Prabhakar 88007fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STOPPED; 88107fc05bdSLad Prabhakar 88207fc05bdSLad Prabhakar for (i = 0; i < RZG2L_CRU_HW_BUFFER_MAX; i++) 88307fc05bdSLad Prabhakar cru->queue_buf[i] = NULL; 88407fc05bdSLad Prabhakar 88507fc05bdSLad Prabhakar /* buffer queue */ 88607fc05bdSLad Prabhakar q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 88707fc05bdSLad Prabhakar q->io_modes = VB2_MMAP | VB2_DMABUF; 88807fc05bdSLad Prabhakar q->lock = &cru->lock; 88907fc05bdSLad Prabhakar q->drv_priv = cru; 89007fc05bdSLad Prabhakar q->buf_struct_size = sizeof(struct rzg2l_cru_buffer); 89107fc05bdSLad Prabhakar q->ops = &rzg2l_cru_qops; 89207fc05bdSLad Prabhakar q->mem_ops = &vb2_dma_contig_memops; 89307fc05bdSLad Prabhakar q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 89480c2b40aSBenjamin Gaignard q->min_queued_buffers = 4; 89507fc05bdSLad Prabhakar q->dev = cru->dev; 89607fc05bdSLad Prabhakar 89707fc05bdSLad Prabhakar ret = vb2_queue_init(q); 89807fc05bdSLad Prabhakar if (ret < 0) { 89907fc05bdSLad Prabhakar dev_err(cru->dev, "failed to initialize VB2 queue\n"); 90007fc05bdSLad Prabhakar goto error; 90107fc05bdSLad Prabhakar } 90207fc05bdSLad Prabhakar 90307fc05bdSLad Prabhakar return 0; 90407fc05bdSLad Prabhakar 90507fc05bdSLad Prabhakar error: 90607fc05bdSLad Prabhakar mutex_destroy(&cru->lock); 90707fc05bdSLad Prabhakar v4l2_device_unregister(&cru->v4l2_dev); 90807fc05bdSLad Prabhakar return ret; 90907fc05bdSLad Prabhakar } 91007fc05bdSLad Prabhakar 91107fc05bdSLad Prabhakar /* ----------------------------------------------------------------------------- 91207fc05bdSLad Prabhakar * V4L2 stuff 91307fc05bdSLad Prabhakar */ 91407fc05bdSLad Prabhakar 9157e58132cSLad Prabhakar static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, 9167e58132cSLad Prabhakar struct v4l2_pix_format *pix) 91707fc05bdSLad Prabhakar { 9185f5ed645SLad Prabhakar const struct rzg2l_cru_info *info = cru->info; 9198853467cSLad Prabhakar const struct rzg2l_cru_ip_format *fmt; 92007fc05bdSLad Prabhakar 9218853467cSLad Prabhakar fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat); 9227e58132cSLad Prabhakar if (!fmt) { 92307fc05bdSLad Prabhakar pix->pixelformat = RZG2L_CRU_DEFAULT_FORMAT; 9247e58132cSLad Prabhakar fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat); 9257e58132cSLad Prabhakar } 92607fc05bdSLad Prabhakar 92707fc05bdSLad Prabhakar switch (pix->field) { 92807fc05bdSLad Prabhakar case V4L2_FIELD_TOP: 92907fc05bdSLad Prabhakar case V4L2_FIELD_BOTTOM: 93007fc05bdSLad Prabhakar case V4L2_FIELD_NONE: 93107fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED_TB: 93207fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED_BT: 93307fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED: 93407fc05bdSLad Prabhakar break; 93507fc05bdSLad Prabhakar default: 93607fc05bdSLad Prabhakar pix->field = RZG2L_CRU_DEFAULT_FIELD; 93707fc05bdSLad Prabhakar break; 93807fc05bdSLad Prabhakar } 93907fc05bdSLad Prabhakar 94007fc05bdSLad Prabhakar /* Limit to CRU capabilities */ 9415f5ed645SLad Prabhakar v4l_bound_align_image(&pix->width, 320, info->max_width, 1, 9425f5ed645SLad Prabhakar &pix->height, 240, info->max_height, 2, 0); 94307fc05bdSLad Prabhakar 944*1d1e564fSLad Prabhakar if (info->has_stride) { 945*1d1e564fSLad Prabhakar u32 stride = clamp(pix->bytesperline, pix->width * fmt->bpp, 946*1d1e564fSLad Prabhakar RZG2L_CRU_STRIDE_MAX); 947*1d1e564fSLad Prabhakar pix->bytesperline = round_up(stride, RZG2L_CRU_STRIDE_ALIGN); 948*1d1e564fSLad Prabhakar } else { 9497e58132cSLad Prabhakar pix->bytesperline = pix->width * fmt->bpp; 950*1d1e564fSLad Prabhakar } 951*1d1e564fSLad Prabhakar 952a8af02e8SLad Prabhakar pix->sizeimage = pix->bytesperline * pix->height; 95307fc05bdSLad Prabhakar 95407fc05bdSLad Prabhakar dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n", 95507fc05bdSLad Prabhakar pix->width, pix->height, pix->bytesperline, pix->sizeimage); 95607fc05bdSLad Prabhakar } 95707fc05bdSLad Prabhakar 95807fc05bdSLad Prabhakar static void rzg2l_cru_try_format(struct rzg2l_cru_dev *cru, 95907fc05bdSLad Prabhakar struct v4l2_pix_format *pix) 96007fc05bdSLad Prabhakar { 96107fc05bdSLad Prabhakar /* 96207fc05bdSLad Prabhakar * The V4L2 specification clearly documents the colorspace fields 96307fc05bdSLad Prabhakar * as being set by drivers for capture devices. Using the values 96407fc05bdSLad Prabhakar * supplied by userspace thus wouldn't comply with the API. Until 96507fc05bdSLad Prabhakar * the API is updated force fixed values. 96607fc05bdSLad Prabhakar */ 96707fc05bdSLad Prabhakar pix->colorspace = RZG2L_CRU_DEFAULT_COLORSPACE; 96807fc05bdSLad Prabhakar pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); 96907fc05bdSLad Prabhakar pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); 97007fc05bdSLad Prabhakar pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, pix->colorspace, 97107fc05bdSLad Prabhakar pix->ycbcr_enc); 97207fc05bdSLad Prabhakar 97307fc05bdSLad Prabhakar rzg2l_cru_format_align(cru, pix); 97407fc05bdSLad Prabhakar } 97507fc05bdSLad Prabhakar 97607fc05bdSLad Prabhakar static int rzg2l_cru_querycap(struct file *file, void *priv, 97707fc05bdSLad Prabhakar struct v4l2_capability *cap) 97807fc05bdSLad Prabhakar { 97907fc05bdSLad Prabhakar strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); 98007fc05bdSLad Prabhakar strscpy(cap->card, "RZG2L_CRU", sizeof(cap->card)); 98107fc05bdSLad Prabhakar 98207fc05bdSLad Prabhakar return 0; 98307fc05bdSLad Prabhakar } 98407fc05bdSLad Prabhakar 98507fc05bdSLad Prabhakar static int rzg2l_cru_try_fmt_vid_cap(struct file *file, void *priv, 98607fc05bdSLad Prabhakar struct v4l2_format *f) 98707fc05bdSLad Prabhakar { 98807fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 98907fc05bdSLad Prabhakar 99007fc05bdSLad Prabhakar rzg2l_cru_try_format(cru, &f->fmt.pix); 99107fc05bdSLad Prabhakar 99207fc05bdSLad Prabhakar return 0; 99307fc05bdSLad Prabhakar } 99407fc05bdSLad Prabhakar 99507fc05bdSLad Prabhakar static int rzg2l_cru_s_fmt_vid_cap(struct file *file, void *priv, 99607fc05bdSLad Prabhakar struct v4l2_format *f) 99707fc05bdSLad Prabhakar { 99807fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 99907fc05bdSLad Prabhakar 100007fc05bdSLad Prabhakar if (vb2_is_busy(&cru->queue)) 100107fc05bdSLad Prabhakar return -EBUSY; 100207fc05bdSLad Prabhakar 100307fc05bdSLad Prabhakar rzg2l_cru_try_format(cru, &f->fmt.pix); 100407fc05bdSLad Prabhakar 100507fc05bdSLad Prabhakar cru->format = f->fmt.pix; 100607fc05bdSLad Prabhakar 100707fc05bdSLad Prabhakar return 0; 100807fc05bdSLad Prabhakar } 100907fc05bdSLad Prabhakar 101007fc05bdSLad Prabhakar static int rzg2l_cru_g_fmt_vid_cap(struct file *file, void *priv, 101107fc05bdSLad Prabhakar struct v4l2_format *f) 101207fc05bdSLad Prabhakar { 101307fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 101407fc05bdSLad Prabhakar 101507fc05bdSLad Prabhakar f->fmt.pix = cru->format; 101607fc05bdSLad Prabhakar 101707fc05bdSLad Prabhakar return 0; 101807fc05bdSLad Prabhakar } 101907fc05bdSLad Prabhakar 102007fc05bdSLad Prabhakar static int rzg2l_cru_enum_fmt_vid_cap(struct file *file, void *priv, 102107fc05bdSLad Prabhakar struct v4l2_fmtdesc *f) 102207fc05bdSLad Prabhakar { 10238853467cSLad Prabhakar const struct rzg2l_cru_ip_format *fmt; 10248853467cSLad Prabhakar 10258853467cSLad Prabhakar fmt = rzg2l_cru_ip_index_to_fmt(f->index); 10268853467cSLad Prabhakar if (!fmt) 102707fc05bdSLad Prabhakar return -EINVAL; 102807fc05bdSLad Prabhakar 10298853467cSLad Prabhakar f->pixelformat = fmt->format; 103007fc05bdSLad Prabhakar 103107fc05bdSLad Prabhakar return 0; 103207fc05bdSLad Prabhakar } 103307fc05bdSLad Prabhakar 103407fc05bdSLad Prabhakar static const struct v4l2_ioctl_ops rzg2l_cru_ioctl_ops = { 103507fc05bdSLad Prabhakar .vidioc_querycap = rzg2l_cru_querycap, 103607fc05bdSLad Prabhakar .vidioc_try_fmt_vid_cap = rzg2l_cru_try_fmt_vid_cap, 103707fc05bdSLad Prabhakar .vidioc_g_fmt_vid_cap = rzg2l_cru_g_fmt_vid_cap, 103807fc05bdSLad Prabhakar .vidioc_s_fmt_vid_cap = rzg2l_cru_s_fmt_vid_cap, 103907fc05bdSLad Prabhakar .vidioc_enum_fmt_vid_cap = rzg2l_cru_enum_fmt_vid_cap, 104007fc05bdSLad Prabhakar 104107fc05bdSLad Prabhakar .vidioc_reqbufs = vb2_ioctl_reqbufs, 104207fc05bdSLad Prabhakar .vidioc_create_bufs = vb2_ioctl_create_bufs, 104307fc05bdSLad Prabhakar .vidioc_querybuf = vb2_ioctl_querybuf, 104407fc05bdSLad Prabhakar .vidioc_qbuf = vb2_ioctl_qbuf, 104507fc05bdSLad Prabhakar .vidioc_dqbuf = vb2_ioctl_dqbuf, 104607fc05bdSLad Prabhakar .vidioc_expbuf = vb2_ioctl_expbuf, 104707fc05bdSLad Prabhakar .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 104807fc05bdSLad Prabhakar .vidioc_streamon = vb2_ioctl_streamon, 104907fc05bdSLad Prabhakar .vidioc_streamoff = vb2_ioctl_streamoff, 105007fc05bdSLad Prabhakar }; 105107fc05bdSLad Prabhakar 105207fc05bdSLad Prabhakar /* ----------------------------------------------------------------------------- 105307fc05bdSLad Prabhakar * Media controller file operations 105407fc05bdSLad Prabhakar */ 105507fc05bdSLad Prabhakar 105607fc05bdSLad Prabhakar static int rzg2l_cru_open(struct file *file) 105707fc05bdSLad Prabhakar { 105807fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 105907fc05bdSLad Prabhakar int ret; 106007fc05bdSLad Prabhakar 106107fc05bdSLad Prabhakar ret = mutex_lock_interruptible(&cru->lock); 106207fc05bdSLad Prabhakar if (ret) 106307fc05bdSLad Prabhakar return ret; 106407fc05bdSLad Prabhakar 106507fc05bdSLad Prabhakar file->private_data = cru; 106607fc05bdSLad Prabhakar ret = v4l2_fh_open(file); 106707fc05bdSLad Prabhakar if (ret) 106807fc05bdSLad Prabhakar goto err_unlock; 106907fc05bdSLad Prabhakar 107007fc05bdSLad Prabhakar mutex_unlock(&cru->lock); 107107fc05bdSLad Prabhakar 107207fc05bdSLad Prabhakar return 0; 107307fc05bdSLad Prabhakar 107407fc05bdSLad Prabhakar err_unlock: 107507fc05bdSLad Prabhakar mutex_unlock(&cru->lock); 107607fc05bdSLad Prabhakar 107707fc05bdSLad Prabhakar return ret; 107807fc05bdSLad Prabhakar } 107907fc05bdSLad Prabhakar 108007fc05bdSLad Prabhakar static int rzg2l_cru_release(struct file *file) 108107fc05bdSLad Prabhakar { 108207fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 108307fc05bdSLad Prabhakar int ret; 108407fc05bdSLad Prabhakar 108507fc05bdSLad Prabhakar mutex_lock(&cru->lock); 108607fc05bdSLad Prabhakar 108707fc05bdSLad Prabhakar /* the release helper will cleanup any on-going streaming. */ 108807fc05bdSLad Prabhakar ret = _vb2_fop_release(file, NULL); 108907fc05bdSLad Prabhakar 109007fc05bdSLad Prabhakar mutex_unlock(&cru->lock); 109107fc05bdSLad Prabhakar 109207fc05bdSLad Prabhakar return ret; 109307fc05bdSLad Prabhakar } 109407fc05bdSLad Prabhakar 109507fc05bdSLad Prabhakar static const struct v4l2_file_operations rzg2l_cru_fops = { 109607fc05bdSLad Prabhakar .owner = THIS_MODULE, 109707fc05bdSLad Prabhakar .unlocked_ioctl = video_ioctl2, 109807fc05bdSLad Prabhakar .open = rzg2l_cru_open, 109907fc05bdSLad Prabhakar .release = rzg2l_cru_release, 110007fc05bdSLad Prabhakar .poll = vb2_fop_poll, 110107fc05bdSLad Prabhakar .mmap = vb2_fop_mmap, 110207fc05bdSLad Prabhakar .read = vb2_fop_read, 110307fc05bdSLad Prabhakar }; 110407fc05bdSLad Prabhakar 1105f7b55b77SLad Prabhakar /* ----------------------------------------------------------------------------- 1106f7b55b77SLad Prabhakar * Media entity operations 1107f7b55b77SLad Prabhakar */ 1108f7b55b77SLad Prabhakar 1109f7b55b77SLad Prabhakar static int rzg2l_cru_video_link_validate(struct media_link *link) 1110f7b55b77SLad Prabhakar { 1111f7b55b77SLad Prabhakar struct v4l2_subdev_format fmt = { 1112f7b55b77SLad Prabhakar .which = V4L2_SUBDEV_FORMAT_ACTIVE, 1113f7b55b77SLad Prabhakar }; 1114f7b55b77SLad Prabhakar const struct rzg2l_cru_ip_format *video_fmt; 1115f7b55b77SLad Prabhakar struct v4l2_subdev *subdev; 1116f7b55b77SLad Prabhakar struct rzg2l_cru_dev *cru; 1117f7b55b77SLad Prabhakar int ret; 1118f7b55b77SLad Prabhakar 1119f7b55b77SLad Prabhakar subdev = media_entity_to_v4l2_subdev(link->source->entity); 1120f7b55b77SLad Prabhakar fmt.pad = link->source->index; 1121f7b55b77SLad Prabhakar ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 1122f7b55b77SLad Prabhakar if (ret < 0) 1123f7b55b77SLad Prabhakar return ret == -ENOIOCTLCMD ? -EINVAL : ret; 1124f7b55b77SLad Prabhakar 1125f7b55b77SLad Prabhakar cru = container_of(media_entity_to_video_device(link->sink->entity), 1126f7b55b77SLad Prabhakar struct rzg2l_cru_dev, vdev); 1127f7b55b77SLad Prabhakar video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat); 1128f7b55b77SLad Prabhakar 1129f7b55b77SLad Prabhakar if (fmt.format.width != cru->format.width || 1130f7b55b77SLad Prabhakar fmt.format.height != cru->format.height || 1131f7b55b77SLad Prabhakar fmt.format.field != cru->format.field || 1132f7b55b77SLad Prabhakar video_fmt->code != fmt.format.code) 1133f7b55b77SLad Prabhakar return -EPIPE; 1134f7b55b77SLad Prabhakar 1135f7b55b77SLad Prabhakar return 0; 1136f7b55b77SLad Prabhakar } 1137f7b55b77SLad Prabhakar 1138f7b55b77SLad Prabhakar static const struct media_entity_operations rzg2l_cru_video_media_ops = { 1139f7b55b77SLad Prabhakar .link_validate = rzg2l_cru_video_link_validate, 1140f7b55b77SLad Prabhakar }; 1141f7b55b77SLad Prabhakar 114207fc05bdSLad Prabhakar static void rzg2l_cru_v4l2_init(struct rzg2l_cru_dev *cru) 114307fc05bdSLad Prabhakar { 114407fc05bdSLad Prabhakar struct video_device *vdev = &cru->vdev; 114507fc05bdSLad Prabhakar 114607fc05bdSLad Prabhakar vdev->v4l2_dev = &cru->v4l2_dev; 114707fc05bdSLad Prabhakar vdev->queue = &cru->queue; 114807fc05bdSLad Prabhakar snprintf(vdev->name, sizeof(vdev->name), "CRU output"); 114907fc05bdSLad Prabhakar vdev->release = video_device_release_empty; 115007fc05bdSLad Prabhakar vdev->lock = &cru->lock; 115107fc05bdSLad Prabhakar vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 115207fc05bdSLad Prabhakar vdev->device_caps |= V4L2_CAP_IO_MC; 1153f7b55b77SLad Prabhakar vdev->entity.ops = &rzg2l_cru_video_media_ops; 115407fc05bdSLad Prabhakar vdev->fops = &rzg2l_cru_fops; 115507fc05bdSLad Prabhakar vdev->ioctl_ops = &rzg2l_cru_ioctl_ops; 115607fc05bdSLad Prabhakar 115707fc05bdSLad Prabhakar /* Set a default format */ 115807fc05bdSLad Prabhakar cru->format.pixelformat = RZG2L_CRU_DEFAULT_FORMAT; 115907fc05bdSLad Prabhakar cru->format.width = RZG2L_CRU_DEFAULT_WIDTH; 116007fc05bdSLad Prabhakar cru->format.height = RZG2L_CRU_DEFAULT_HEIGHT; 116107fc05bdSLad Prabhakar cru->format.field = RZG2L_CRU_DEFAULT_FIELD; 116207fc05bdSLad Prabhakar cru->format.colorspace = RZG2L_CRU_DEFAULT_COLORSPACE; 116307fc05bdSLad Prabhakar rzg2l_cru_format_align(cru, &cru->format); 116407fc05bdSLad Prabhakar } 116507fc05bdSLad Prabhakar 116607fc05bdSLad Prabhakar void rzg2l_cru_video_unregister(struct rzg2l_cru_dev *cru) 116707fc05bdSLad Prabhakar { 116807fc05bdSLad Prabhakar media_device_unregister(&cru->mdev); 116907fc05bdSLad Prabhakar video_unregister_device(&cru->vdev); 117007fc05bdSLad Prabhakar } 117107fc05bdSLad Prabhakar 117207fc05bdSLad Prabhakar int rzg2l_cru_video_register(struct rzg2l_cru_dev *cru) 117307fc05bdSLad Prabhakar { 117407fc05bdSLad Prabhakar struct video_device *vdev = &cru->vdev; 117507fc05bdSLad Prabhakar int ret; 117607fc05bdSLad Prabhakar 117707fc05bdSLad Prabhakar if (video_is_registered(&cru->vdev)) { 117807fc05bdSLad Prabhakar struct media_entity *entity; 117907fc05bdSLad Prabhakar 118007fc05bdSLad Prabhakar entity = &cru->vdev.entity; 118107fc05bdSLad Prabhakar if (!entity->graph_obj.mdev) 118207fc05bdSLad Prabhakar entity->graph_obj.mdev = &cru->mdev; 118307fc05bdSLad Prabhakar return 0; 118407fc05bdSLad Prabhakar } 118507fc05bdSLad Prabhakar 118607fc05bdSLad Prabhakar rzg2l_cru_v4l2_init(cru); 118707fc05bdSLad Prabhakar video_set_drvdata(vdev, cru); 118807fc05bdSLad Prabhakar ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 118907fc05bdSLad Prabhakar if (ret) { 119007fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to register video device\n"); 119107fc05bdSLad Prabhakar return ret; 119207fc05bdSLad Prabhakar } 119307fc05bdSLad Prabhakar 119407fc05bdSLad Prabhakar ret = media_device_register(&cru->mdev); 119507fc05bdSLad Prabhakar if (ret) { 119607fc05bdSLad Prabhakar video_unregister_device(&cru->vdev); 119707fc05bdSLad Prabhakar return ret; 119807fc05bdSLad Prabhakar } 119907fc05bdSLad Prabhakar 120007fc05bdSLad Prabhakar return 0; 120107fc05bdSLad Prabhakar } 1202