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 3407fc05bdSLad Prabhakar struct rzg2l_cru_buffer { 3507fc05bdSLad Prabhakar struct vb2_v4l2_buffer vb; 3607fc05bdSLad Prabhakar struct list_head list; 3707fc05bdSLad Prabhakar }; 3807fc05bdSLad Prabhakar 3907fc05bdSLad Prabhakar #define to_buf_list(vb2_buffer) \ 4007fc05bdSLad Prabhakar (&container_of(vb2_buffer, struct rzg2l_cru_buffer, vb)->list) 4107fc05bdSLad Prabhakar 4207fc05bdSLad Prabhakar /* ----------------------------------------------------------------------------- 4307fc05bdSLad Prabhakar * DMA operations 4407fc05bdSLad Prabhakar */ 45d9063dc5SLad Prabhakar static void __rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value) 4607fc05bdSLad Prabhakar { 47d9063dc5SLad Prabhakar const u16 *regs = cru->info->regs; 48d9063dc5SLad Prabhakar 49d9063dc5SLad Prabhakar /* 50d9063dc5SLad Prabhakar * CRUnCTRL is a first register on all CRU supported SoCs so validate 51d9063dc5SLad Prabhakar * rest of the registers have valid offset being set in cru->info->regs. 52d9063dc5SLad Prabhakar */ 53d9063dc5SLad Prabhakar if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) || 54d9063dc5SLad Prabhakar WARN_ON(offset != CRUnCTRL && regs[offset] == 0)) 55d9063dc5SLad Prabhakar return; 56d9063dc5SLad Prabhakar 57d9063dc5SLad Prabhakar iowrite32(value, cru->base + regs[offset]); 5807fc05bdSLad Prabhakar } 5907fc05bdSLad Prabhakar 60d9063dc5SLad Prabhakar static u32 __rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset) 6107fc05bdSLad Prabhakar { 62d9063dc5SLad Prabhakar const u16 *regs = cru->info->regs; 63d9063dc5SLad Prabhakar 64d9063dc5SLad Prabhakar /* 65d9063dc5SLad Prabhakar * CRUnCTRL is a first register on all CRU supported SoCs so validate 66d9063dc5SLad Prabhakar * rest of the registers have valid offset being set in cru->info->regs. 67d9063dc5SLad Prabhakar */ 68d9063dc5SLad Prabhakar if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) || 69d9063dc5SLad Prabhakar WARN_ON(offset != CRUnCTRL && regs[offset] == 0)) 70d9063dc5SLad Prabhakar return 0; 71d9063dc5SLad Prabhakar 72d9063dc5SLad Prabhakar return ioread32(cru->base + regs[offset]); 7307fc05bdSLad Prabhakar } 7407fc05bdSLad Prabhakar 75d9063dc5SLad Prabhakar static __always_inline void 76d9063dc5SLad Prabhakar __rzg2l_cru_write_constant(struct rzg2l_cru_dev *cru, u32 offset, u32 value) 77d9063dc5SLad Prabhakar { 78d9063dc5SLad Prabhakar const u16 *regs = cru->info->regs; 79d9063dc5SLad Prabhakar 80d9063dc5SLad Prabhakar BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG); 81d9063dc5SLad Prabhakar 82d9063dc5SLad Prabhakar iowrite32(value, cru->base + regs[offset]); 83d9063dc5SLad Prabhakar } 84d9063dc5SLad Prabhakar 85d9063dc5SLad Prabhakar static __always_inline u32 86d9063dc5SLad Prabhakar __rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset) 87d9063dc5SLad Prabhakar { 88d9063dc5SLad Prabhakar const u16 *regs = cru->info->regs; 89d9063dc5SLad Prabhakar 90d9063dc5SLad Prabhakar BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG); 91d9063dc5SLad Prabhakar 92d9063dc5SLad Prabhakar return ioread32(cru->base + regs[offset]); 93d9063dc5SLad Prabhakar } 94d9063dc5SLad Prabhakar 95d9063dc5SLad Prabhakar #define rzg2l_cru_write(cru, offset, value) \ 96d9063dc5SLad Prabhakar (__builtin_constant_p(offset) ? \ 97d9063dc5SLad Prabhakar __rzg2l_cru_write_constant(cru, offset, value) : \ 98d9063dc5SLad Prabhakar __rzg2l_cru_write(cru, offset, value)) 99d9063dc5SLad Prabhakar 100d9063dc5SLad Prabhakar #define rzg2l_cru_read(cru, offset) \ 101d9063dc5SLad Prabhakar (__builtin_constant_p(offset) ? \ 102d9063dc5SLad Prabhakar __rzg2l_cru_read_constant(cru, offset) : \ 103d9063dc5SLad Prabhakar __rzg2l_cru_read(cru, offset)) 104d9063dc5SLad Prabhakar 10507fc05bdSLad Prabhakar /* Need to hold qlock before calling */ 10607fc05bdSLad Prabhakar static void return_unused_buffers(struct rzg2l_cru_dev *cru, 10707fc05bdSLad Prabhakar enum vb2_buffer_state state) 10807fc05bdSLad Prabhakar { 10907fc05bdSLad Prabhakar struct rzg2l_cru_buffer *buf, *node; 11007fc05bdSLad Prabhakar unsigned long flags; 11107fc05bdSLad Prabhakar unsigned int i; 11207fc05bdSLad Prabhakar 11307fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 11407fc05bdSLad Prabhakar for (i = 0; i < cru->num_buf; i++) { 11507fc05bdSLad Prabhakar if (cru->queue_buf[i]) { 11607fc05bdSLad Prabhakar vb2_buffer_done(&cru->queue_buf[i]->vb2_buf, 11707fc05bdSLad Prabhakar state); 11807fc05bdSLad Prabhakar cru->queue_buf[i] = NULL; 11907fc05bdSLad Prabhakar } 12007fc05bdSLad Prabhakar } 12107fc05bdSLad Prabhakar 12207fc05bdSLad Prabhakar list_for_each_entry_safe(buf, node, &cru->buf_list, list) { 12307fc05bdSLad Prabhakar vb2_buffer_done(&buf->vb.vb2_buf, state); 12407fc05bdSLad Prabhakar list_del(&buf->list); 12507fc05bdSLad Prabhakar } 12607fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 12707fc05bdSLad Prabhakar } 12807fc05bdSLad Prabhakar 12907fc05bdSLad Prabhakar static int rzg2l_cru_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, 13007fc05bdSLad Prabhakar unsigned int *nplanes, unsigned int sizes[], 13107fc05bdSLad Prabhakar struct device *alloc_devs[]) 13207fc05bdSLad Prabhakar { 13307fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 13407fc05bdSLad Prabhakar 13507fc05bdSLad Prabhakar /* Make sure the image size is large enough. */ 13607fc05bdSLad Prabhakar if (*nplanes) 13707fc05bdSLad Prabhakar return sizes[0] < cru->format.sizeimage ? -EINVAL : 0; 13807fc05bdSLad Prabhakar 13907fc05bdSLad Prabhakar *nplanes = 1; 14007fc05bdSLad Prabhakar sizes[0] = cru->format.sizeimage; 14107fc05bdSLad Prabhakar 14207fc05bdSLad Prabhakar return 0; 14307fc05bdSLad Prabhakar }; 14407fc05bdSLad Prabhakar 14507fc05bdSLad Prabhakar static int rzg2l_cru_buffer_prepare(struct vb2_buffer *vb) 14607fc05bdSLad Prabhakar { 14707fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); 14807fc05bdSLad Prabhakar unsigned long size = cru->format.sizeimage; 14907fc05bdSLad Prabhakar 15007fc05bdSLad Prabhakar if (vb2_plane_size(vb, 0) < size) { 15107fc05bdSLad Prabhakar dev_err(cru->dev, "buffer too small (%lu < %lu)\n", 15207fc05bdSLad Prabhakar vb2_plane_size(vb, 0), size); 15307fc05bdSLad Prabhakar return -EINVAL; 15407fc05bdSLad Prabhakar } 15507fc05bdSLad Prabhakar 15607fc05bdSLad Prabhakar vb2_set_plane_payload(vb, 0, size); 15707fc05bdSLad Prabhakar 15807fc05bdSLad Prabhakar return 0; 15907fc05bdSLad Prabhakar } 16007fc05bdSLad Prabhakar 16107fc05bdSLad Prabhakar static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb) 16207fc05bdSLad Prabhakar { 16307fc05bdSLad Prabhakar struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 16407fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue); 16507fc05bdSLad Prabhakar unsigned long flags; 16607fc05bdSLad Prabhakar 16707fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 16807fc05bdSLad Prabhakar 16907fc05bdSLad Prabhakar list_add_tail(to_buf_list(vbuf), &cru->buf_list); 17007fc05bdSLad Prabhakar 17107fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 17207fc05bdSLad Prabhakar } 17307fc05bdSLad Prabhakar 17407fc05bdSLad Prabhakar static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru, 17507fc05bdSLad Prabhakar int slot, dma_addr_t addr) 17607fc05bdSLad Prabhakar { 17707fc05bdSLad Prabhakar /* 17807fc05bdSLad Prabhakar * The address needs to be 512 bytes aligned. Driver should never accept 17907fc05bdSLad Prabhakar * settings that do not satisfy this in the first place... 18007fc05bdSLad Prabhakar */ 18107fc05bdSLad Prabhakar if (WARN_ON((addr) & RZG2L_CRU_HW_BUFFER_MASK)) 18207fc05bdSLad Prabhakar return; 18307fc05bdSLad Prabhakar 18407fc05bdSLad Prabhakar /* Currently, we just use the buffer in 32 bits address */ 18507fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnMBxADDRL(slot), addr); 18607fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnMBxADDRH(slot), 0); 18707fc05bdSLad Prabhakar } 18807fc05bdSLad Prabhakar 18907fc05bdSLad Prabhakar /* 19007fc05bdSLad Prabhakar * Moves a buffer from the queue to the HW slot. If no buffer is 19107fc05bdSLad Prabhakar * available use the scratch buffer. The scratch buffer is never 19207fc05bdSLad Prabhakar * returned to userspace, its only function is to enable the capture 19307fc05bdSLad Prabhakar * loop to keep running. 19407fc05bdSLad Prabhakar */ 19507fc05bdSLad Prabhakar static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot) 19607fc05bdSLad Prabhakar { 19707fc05bdSLad Prabhakar struct vb2_v4l2_buffer *vbuf; 19807fc05bdSLad Prabhakar struct rzg2l_cru_buffer *buf; 19907fc05bdSLad Prabhakar dma_addr_t phys_addr; 20007fc05bdSLad Prabhakar 20107fc05bdSLad Prabhakar /* A already populated slot shall never be overwritten. */ 20207fc05bdSLad Prabhakar if (WARN_ON(cru->queue_buf[slot])) 20307fc05bdSLad Prabhakar return; 20407fc05bdSLad Prabhakar 20507fc05bdSLad Prabhakar dev_dbg(cru->dev, "Filling HW slot: %d\n", slot); 20607fc05bdSLad Prabhakar 20707fc05bdSLad Prabhakar if (list_empty(&cru->buf_list)) { 20807fc05bdSLad Prabhakar cru->queue_buf[slot] = NULL; 20907fc05bdSLad Prabhakar phys_addr = cru->scratch_phys; 21007fc05bdSLad Prabhakar } else { 21107fc05bdSLad Prabhakar /* Keep track of buffer we give to HW */ 21207fc05bdSLad Prabhakar buf = list_entry(cru->buf_list.next, 21307fc05bdSLad Prabhakar struct rzg2l_cru_buffer, list); 21407fc05bdSLad Prabhakar vbuf = &buf->vb; 21507fc05bdSLad Prabhakar list_del_init(to_buf_list(vbuf)); 21607fc05bdSLad Prabhakar cru->queue_buf[slot] = vbuf; 21707fc05bdSLad Prabhakar 21807fc05bdSLad Prabhakar /* Setup DMA */ 21907fc05bdSLad Prabhakar phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); 22007fc05bdSLad Prabhakar } 22107fc05bdSLad Prabhakar 22207fc05bdSLad Prabhakar rzg2l_cru_set_slot_addr(cru, slot, phys_addr); 22307fc05bdSLad Prabhakar } 22407fc05bdSLad Prabhakar 22507fc05bdSLad Prabhakar static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru) 22607fc05bdSLad Prabhakar { 22707fc05bdSLad Prabhakar unsigned int slot; 22812564e80SBiju Das u32 amnaxiattr; 22907fc05bdSLad Prabhakar 23007fc05bdSLad Prabhakar /* 23107fc05bdSLad Prabhakar * Set image data memory banks. 23207fc05bdSLad Prabhakar * Currently, we will use maximum address. 23307fc05bdSLad Prabhakar */ 23407fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnMBVALID, AMnMBVALID_MBVALID(cru->num_buf - 1)); 23507fc05bdSLad Prabhakar 23607fc05bdSLad Prabhakar for (slot = 0; slot < cru->num_buf; slot++) 23707fc05bdSLad Prabhakar rzg2l_cru_fill_hw_slot(cru, slot); 23812564e80SBiju Das 23912564e80SBiju Das /* Set AXI burst max length to recommended setting */ 24012564e80SBiju Das amnaxiattr = rzg2l_cru_read(cru, AMnAXIATTR) & ~AMnAXIATTR_AXILEN_MASK; 24112564e80SBiju Das amnaxiattr |= AMnAXIATTR_AXILEN; 24212564e80SBiju Das rzg2l_cru_write(cru, AMnAXIATTR, amnaxiattr); 24307fc05bdSLad Prabhakar } 24407fc05bdSLad Prabhakar 245*3c3433c5SLad Prabhakar void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, 246b56dccafSLad Prabhakar const struct rzg2l_cru_ip_format *ip_fmt, 247b56dccafSLad Prabhakar u8 csi_vc) 24807fc05bdSLad Prabhakar { 24948ce5920SLad Prabhakar const struct rzg2l_cru_info *info = cru->info; 250b56dccafSLad Prabhakar u32 icnmc = ICnMC_INF(ip_fmt->datatype); 25107fc05bdSLad Prabhakar 25248ce5920SLad Prabhakar icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK; 25307fc05bdSLad Prabhakar 25407fc05bdSLad Prabhakar /* Set virtual channel CSI2 */ 255c7f3bd38SLad Prabhakar icnmc |= ICnMC_VCSEL(csi_vc); 25607fc05bdSLad Prabhakar 25748ce5920SLad Prabhakar rzg2l_cru_write(cru, info->image_conv, icnmc); 25807fc05bdSLad Prabhakar } 25907fc05bdSLad Prabhakar 26007fc05bdSLad Prabhakar static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, 261c7f3bd38SLad Prabhakar struct v4l2_mbus_framefmt *ip_sd_fmt, 262c7f3bd38SLad Prabhakar u8 csi_vc) 26307fc05bdSLad Prabhakar { 26448ce5920SLad Prabhakar const struct rzg2l_cru_info *info = cru->info; 265c6ed80fdSLad Prabhakar const struct rzg2l_cru_ip_format *cru_video_fmt; 266b56dccafSLad Prabhakar const struct rzg2l_cru_ip_format *cru_ip_fmt; 26707fc05bdSLad Prabhakar 268b56dccafSLad Prabhakar cru_ip_fmt = rzg2l_cru_ip_code_to_fmt(ip_sd_fmt->code); 269*3c3433c5SLad Prabhakar info->csi_setup(cru, cru_ip_fmt, csi_vc); 27007fc05bdSLad Prabhakar 27107fc05bdSLad Prabhakar /* Output format */ 272c6ed80fdSLad Prabhakar cru_video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat); 273c6ed80fdSLad Prabhakar if (!cru_video_fmt) { 27407fc05bdSLad Prabhakar dev_err(cru->dev, "Invalid pixelformat (0x%x)\n", 27507fc05bdSLad Prabhakar cru->format.pixelformat); 27607fc05bdSLad Prabhakar return -EINVAL; 27707fc05bdSLad Prabhakar } 27807fc05bdSLad Prabhakar 27907fc05bdSLad Prabhakar /* If input and output use same colorspace, do bypass mode */ 2802269e399SLad Prabhakar if (cru_ip_fmt->yuv == cru_video_fmt->yuv) 28148ce5920SLad Prabhakar rzg2l_cru_write(cru, info->image_conv, 28248ce5920SLad Prabhakar rzg2l_cru_read(cru, info->image_conv) | ICnMC_CSCTHR); 28307fc05bdSLad Prabhakar else 28448ce5920SLad Prabhakar rzg2l_cru_write(cru, info->image_conv, 28548ce5920SLad Prabhakar rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_CSCTHR); 28607fc05bdSLad Prabhakar 28707fc05bdSLad Prabhakar /* Set output data format */ 288c6ed80fdSLad Prabhakar rzg2l_cru_write(cru, ICnDMR, cru_video_fmt->icndmr); 28907fc05bdSLad Prabhakar 29007fc05bdSLad Prabhakar return 0; 29107fc05bdSLad Prabhakar } 29207fc05bdSLad Prabhakar 293446c645fSLad Prabhakar bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru) 29407fc05bdSLad Prabhakar { 29507fc05bdSLad Prabhakar u32 amnfifopntr, amnfifopntr_w, amnfifopntr_r_y; 296446c645fSLad Prabhakar 297446c645fSLad Prabhakar amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); 298446c645fSLad Prabhakar 299446c645fSLad Prabhakar amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR; 300446c645fSLad Prabhakar amnfifopntr_r_y = 301446c645fSLad Prabhakar (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16; 302446c645fSLad Prabhakar if (amnfifopntr_w == amnfifopntr_r_y) 303446c645fSLad Prabhakar return true; 304446c645fSLad Prabhakar 305446c645fSLad Prabhakar return amnfifopntr_w == amnfifopntr_r_y; 306446c645fSLad Prabhakar } 307446c645fSLad Prabhakar 308446c645fSLad Prabhakar void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru) 309446c645fSLad Prabhakar { 31007fc05bdSLad Prabhakar unsigned int retries = 0; 31107fc05bdSLad Prabhakar unsigned long flags; 31207fc05bdSLad Prabhakar u32 icnms; 31307fc05bdSLad Prabhakar 31407fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 31507fc05bdSLad Prabhakar 31607fc05bdSLad Prabhakar /* Disable and clear the interrupt */ 3172d9e3eb7SLad Prabhakar cru->info->disable_interrupts(cru); 31807fc05bdSLad Prabhakar 31907fc05bdSLad Prabhakar /* Stop the operation of image conversion */ 32007fc05bdSLad Prabhakar rzg2l_cru_write(cru, ICnEN, 0); 32107fc05bdSLad Prabhakar 32207fc05bdSLad Prabhakar /* Wait for streaming to stop */ 32307fc05bdSLad Prabhakar while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) { 32407fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 32507fc05bdSLad Prabhakar msleep(RZG2L_TIMEOUT_MS); 32607fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 32707fc05bdSLad Prabhakar } 32807fc05bdSLad Prabhakar 32907fc05bdSLad Prabhakar icnms = rzg2l_cru_read(cru, ICnMS) & ICnMS_IA; 33007fc05bdSLad Prabhakar if (icnms) 33107fc05bdSLad Prabhakar dev_err(cru->dev, "Failed stop HW, something is seriously broken\n"); 33207fc05bdSLad Prabhakar 33307fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STOPPED; 33407fc05bdSLad Prabhakar 33507fc05bdSLad Prabhakar /* Wait until the FIFO becomes empty */ 33607fc05bdSLad Prabhakar for (retries = 5; retries > 0; retries--) { 337446c645fSLad Prabhakar if (cru->info->fifo_empty(cru)) 33807fc05bdSLad Prabhakar break; 33907fc05bdSLad Prabhakar 34007fc05bdSLad Prabhakar usleep_range(10, 20); 34107fc05bdSLad Prabhakar } 34207fc05bdSLad Prabhakar 34307fc05bdSLad Prabhakar /* Notify that FIFO is not empty here */ 34407fc05bdSLad Prabhakar if (!retries) 34507fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to empty FIFO\n"); 34607fc05bdSLad Prabhakar 34707fc05bdSLad Prabhakar /* Stop AXI bus */ 34807fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnAXISTP, AMnAXISTP_AXI_STOP); 34907fc05bdSLad Prabhakar 35007fc05bdSLad Prabhakar /* Wait until the AXI bus stop */ 35107fc05bdSLad Prabhakar for (retries = 5; retries > 0; retries--) { 35207fc05bdSLad Prabhakar if (rzg2l_cru_read(cru, AMnAXISTPACK) & 35307fc05bdSLad Prabhakar AMnAXISTPACK_AXI_STOP_ACK) 35407fc05bdSLad Prabhakar break; 35507fc05bdSLad Prabhakar 35607fc05bdSLad Prabhakar usleep_range(10, 20); 3577206fcc5SYang Li } 35807fc05bdSLad Prabhakar 35907fc05bdSLad Prabhakar /* Notify that AXI bus can not stop here */ 36007fc05bdSLad Prabhakar if (!retries) 36107fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to stop AXI bus\n"); 36207fc05bdSLad Prabhakar 36307fc05bdSLad Prabhakar /* Cancel the AXI bus stop request */ 36407fc05bdSLad Prabhakar rzg2l_cru_write(cru, AMnAXISTP, 0); 36507fc05bdSLad Prabhakar 36607fc05bdSLad Prabhakar /* Reset the CRU (AXI-master) */ 36707fc05bdSLad Prabhakar reset_control_assert(cru->aresetn); 36807fc05bdSLad Prabhakar 36907fc05bdSLad Prabhakar /* Resets the image processing module */ 37007fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnRST, 0); 37107fc05bdSLad Prabhakar 37207fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 37307fc05bdSLad Prabhakar } 37407fc05bdSLad Prabhakar 375d7d72daeSLad Prabhakar static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru) 376d7d72daeSLad Prabhakar { 377d7d72daeSLad Prabhakar struct v4l2_mbus_frame_desc fd = { }; 378d7d72daeSLad Prabhakar struct media_pad *remote_pad; 379d7d72daeSLad Prabhakar int ret; 380d7d72daeSLad Prabhakar 381d7d72daeSLad Prabhakar remote_pad = media_pad_remote_pad_unique(&cru->ip.pads[RZG2L_CRU_IP_SINK]); 382d7d72daeSLad Prabhakar ret = v4l2_subdev_call(cru->ip.remote, pad, get_frame_desc, remote_pad->index, &fd); 383d7d72daeSLad Prabhakar if (ret < 0 && ret != -ENOIOCTLCMD) { 384d7d72daeSLad Prabhakar dev_err(cru->dev, "get_frame_desc failed on IP remote subdev\n"); 385d7d72daeSLad Prabhakar return ret; 386d7d72daeSLad Prabhakar } 387d7d72daeSLad Prabhakar /* If remote subdev does not implement .get_frame_desc default to VC0. */ 388d7d72daeSLad Prabhakar if (ret == -ENOIOCTLCMD) 389d7d72daeSLad Prabhakar return 0; 390d7d72daeSLad Prabhakar 391d7d72daeSLad Prabhakar if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { 392d7d72daeSLad Prabhakar dev_err(cru->dev, "get_frame_desc returned invalid bus type %d\n", fd.type); 393d7d72daeSLad Prabhakar return -EINVAL; 394d7d72daeSLad Prabhakar } 395d7d72daeSLad Prabhakar 396d7d72daeSLad Prabhakar if (!fd.num_entries) { 397d7d72daeSLad Prabhakar dev_err(cru->dev, "get_frame_desc returned zero entries\n"); 398d7d72daeSLad Prabhakar return -EINVAL; 399d7d72daeSLad Prabhakar } 400d7d72daeSLad Prabhakar 401d7d72daeSLad Prabhakar return fd.entry[0].bus.csi2.vc; 402d7d72daeSLad Prabhakar } 403d7d72daeSLad Prabhakar 4042d9e3eb7SLad Prabhakar void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru) 4052d9e3eb7SLad Prabhakar { 4062d9e3eb7SLad Prabhakar rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE); 4072d9e3eb7SLad Prabhakar } 4082d9e3eb7SLad Prabhakar 4092d9e3eb7SLad Prabhakar void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru) 4102d9e3eb7SLad Prabhakar { 4112d9e3eb7SLad Prabhakar rzg2l_cru_write(cru, CRUnIE, 0); 4122d9e3eb7SLad Prabhakar rzg2l_cru_write(cru, CRUnINTS, 0x001f000f); 4132d9e3eb7SLad Prabhakar } 4142d9e3eb7SLad Prabhakar 41507fc05bdSLad Prabhakar int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru) 41607fc05bdSLad Prabhakar { 41707fc05bdSLad Prabhakar struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru); 41807fc05bdSLad Prabhakar unsigned long flags; 419c7f3bd38SLad Prabhakar u8 csi_vc; 42007fc05bdSLad Prabhakar int ret; 42107fc05bdSLad Prabhakar 422d7d72daeSLad Prabhakar ret = rzg2l_cru_get_virtual_channel(cru); 423d7d72daeSLad Prabhakar if (ret < 0) 424d7d72daeSLad Prabhakar return ret; 425c7f3bd38SLad Prabhakar csi_vc = ret; 426d7d72daeSLad Prabhakar 42707fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 42807fc05bdSLad Prabhakar 42907fc05bdSLad Prabhakar /* Select a video input */ 43007fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnCTRL, CRUnCTRL_VINSEL(0)); 43107fc05bdSLad Prabhakar 43207fc05bdSLad Prabhakar /* Cancel the software reset for image processing block */ 43307fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnRST, CRUnRST_VRESETN); 43407fc05bdSLad Prabhakar 43507fc05bdSLad Prabhakar /* Disable and clear the interrupt before using */ 4362d9e3eb7SLad Prabhakar cru->info->disable_interrupts(cru); 43707fc05bdSLad Prabhakar 43807fc05bdSLad Prabhakar /* Initialize the AXI master */ 43907fc05bdSLad Prabhakar rzg2l_cru_initialize_axi(cru); 44007fc05bdSLad Prabhakar 44127673948SBiju Das /* Initialize image convert */ 442c7f3bd38SLad Prabhakar ret = rzg2l_cru_initialize_image_conv(cru, fmt, csi_vc); 44327673948SBiju Das if (ret) { 44427673948SBiju Das spin_unlock_irqrestore(&cru->qlock, flags); 44527673948SBiju Das return ret; 44627673948SBiju Das } 44727673948SBiju Das 44807fc05bdSLad Prabhakar /* Enable interrupt */ 4492d9e3eb7SLad Prabhakar cru->info->enable_interrupts(cru); 45007fc05bdSLad Prabhakar 45107fc05bdSLad Prabhakar /* Enable image processing reception */ 45207fc05bdSLad Prabhakar rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN); 45307fc05bdSLad Prabhakar 45407fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 45507fc05bdSLad Prabhakar 45607fc05bdSLad Prabhakar return 0; 45707fc05bdSLad Prabhakar } 45807fc05bdSLad Prabhakar 45907fc05bdSLad Prabhakar static int rzg2l_cru_set_stream(struct rzg2l_cru_dev *cru, int on) 46007fc05bdSLad Prabhakar { 46107fc05bdSLad Prabhakar struct media_pipeline *pipe; 46207fc05bdSLad Prabhakar struct v4l2_subdev *sd; 46307fc05bdSLad Prabhakar struct media_pad *pad; 46407fc05bdSLad Prabhakar int ret; 46507fc05bdSLad Prabhakar 46607fc05bdSLad Prabhakar pad = media_pad_remote_pad_first(&cru->pad); 46707fc05bdSLad Prabhakar if (!pad) 46807fc05bdSLad Prabhakar return -EPIPE; 46907fc05bdSLad Prabhakar 47007fc05bdSLad Prabhakar sd = media_entity_to_v4l2_subdev(pad->entity); 47107fc05bdSLad Prabhakar 47207fc05bdSLad Prabhakar if (!on) { 47307fc05bdSLad Prabhakar int stream_off_ret = 0; 47407fc05bdSLad Prabhakar 47507fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, s_stream, 0); 47607fc05bdSLad Prabhakar if (ret) 47707fc05bdSLad Prabhakar stream_off_ret = ret; 47807fc05bdSLad Prabhakar 47907fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, post_streamoff); 48007fc05bdSLad Prabhakar if (ret == -ENOIOCTLCMD) 48107fc05bdSLad Prabhakar ret = 0; 48207fc05bdSLad Prabhakar if (ret && !stream_off_ret) 48307fc05bdSLad Prabhakar stream_off_ret = ret; 48407fc05bdSLad Prabhakar 48507fc05bdSLad Prabhakar video_device_pipeline_stop(&cru->vdev); 48607fc05bdSLad Prabhakar 48707fc05bdSLad Prabhakar return stream_off_ret; 48807fc05bdSLad Prabhakar } 48907fc05bdSLad Prabhakar 49007fc05bdSLad Prabhakar pipe = media_entity_pipeline(&sd->entity) ? : &cru->vdev.pipe; 49107fc05bdSLad Prabhakar ret = video_device_pipeline_start(&cru->vdev, pipe); 49207fc05bdSLad Prabhakar if (ret) 4936bcff5f9SBiju Das return ret; 49407fc05bdSLad Prabhakar 49507fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, pre_streamon, 0); 4966bcff5f9SBiju Das if (ret && ret != -ENOIOCTLCMD) 49707fc05bdSLad Prabhakar goto pipe_line_stop; 49807fc05bdSLad Prabhakar 49907fc05bdSLad Prabhakar ret = v4l2_subdev_call(sd, video, s_stream, 1); 5006bcff5f9SBiju Das if (ret && ret != -ENOIOCTLCMD) 50107fc05bdSLad Prabhakar goto err_s_stream; 50207fc05bdSLad Prabhakar 50307fc05bdSLad Prabhakar return 0; 50407fc05bdSLad Prabhakar 50507fc05bdSLad Prabhakar err_s_stream: 50607fc05bdSLad Prabhakar v4l2_subdev_call(sd, video, post_streamoff); 50707fc05bdSLad Prabhakar 50807fc05bdSLad Prabhakar pipe_line_stop: 50907fc05bdSLad Prabhakar video_device_pipeline_stop(&cru->vdev); 51007fc05bdSLad Prabhakar 51107fc05bdSLad Prabhakar return ret; 51207fc05bdSLad Prabhakar } 51307fc05bdSLad Prabhakar 51407fc05bdSLad Prabhakar static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru) 51507fc05bdSLad Prabhakar { 51607fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STOPPING; 51707fc05bdSLad Prabhakar 51807fc05bdSLad Prabhakar rzg2l_cru_set_stream(cru, 0); 51907fc05bdSLad Prabhakar } 52007fc05bdSLad Prabhakar 5210c200daaSBiju Das irqreturn_t rzg2l_cru_irq(int irq, void *data) 52207fc05bdSLad Prabhakar { 52307fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = data; 52407fc05bdSLad Prabhakar unsigned int handled = 0; 52507fc05bdSLad Prabhakar unsigned long flags; 52607fc05bdSLad Prabhakar u32 irq_status; 52707fc05bdSLad Prabhakar u32 amnmbs; 52807fc05bdSLad Prabhakar int slot; 52907fc05bdSLad Prabhakar 53007fc05bdSLad Prabhakar spin_lock_irqsave(&cru->qlock, flags); 53107fc05bdSLad Prabhakar 53207fc05bdSLad Prabhakar irq_status = rzg2l_cru_read(cru, CRUnINTS); 53307fc05bdSLad Prabhakar if (!irq_status) 53407fc05bdSLad Prabhakar goto done; 53507fc05bdSLad Prabhakar 53607fc05bdSLad Prabhakar handled = 1; 53707fc05bdSLad Prabhakar 53807fc05bdSLad Prabhakar rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS)); 53907fc05bdSLad Prabhakar 54007fc05bdSLad Prabhakar /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */ 54107fc05bdSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STOPPED) { 54207fc05bdSLad Prabhakar dev_dbg(cru->dev, "IRQ while state stopped\n"); 54307fc05bdSLad Prabhakar goto done; 54407fc05bdSLad Prabhakar } 54507fc05bdSLad Prabhakar 54607fc05bdSLad Prabhakar /* Increase stop retries if capture status is 'RZG2L_CRU_DMA_STOPPING' */ 54707fc05bdSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STOPPING) { 54807fc05bdSLad Prabhakar if (irq_status & CRUnINTS_SFS) 54907fc05bdSLad Prabhakar dev_dbg(cru->dev, "IRQ while state stopping\n"); 55007fc05bdSLad Prabhakar goto done; 55107fc05bdSLad Prabhakar } 55207fc05bdSLad Prabhakar 55307fc05bdSLad Prabhakar /* Prepare for capture and update state */ 55407fc05bdSLad Prabhakar amnmbs = rzg2l_cru_read(cru, AMnMBS); 55507fc05bdSLad Prabhakar slot = amnmbs & AMnMBS_MBSTS; 55607fc05bdSLad Prabhakar 55707fc05bdSLad Prabhakar /* 55807fc05bdSLad Prabhakar * AMnMBS.MBSTS indicates the destination of Memory Bank (MB). 55907fc05bdSLad Prabhakar * Recalculate to get the current transfer complete MB. 56007fc05bdSLad Prabhakar */ 56107fc05bdSLad Prabhakar if (slot == 0) 56207fc05bdSLad Prabhakar slot = cru->num_buf - 1; 56307fc05bdSLad Prabhakar else 56407fc05bdSLad Prabhakar slot--; 56507fc05bdSLad Prabhakar 56607fc05bdSLad Prabhakar /* 56707fc05bdSLad Prabhakar * To hand buffers back in a known order to userspace start 56807fc05bdSLad Prabhakar * to capture first from slot 0. 56907fc05bdSLad Prabhakar */ 57007fc05bdSLad Prabhakar if (cru->state == RZG2L_CRU_DMA_STARTING) { 57107fc05bdSLad Prabhakar if (slot != 0) { 57207fc05bdSLad Prabhakar dev_dbg(cru->dev, "Starting sync slot: %d\n", slot); 57307fc05bdSLad Prabhakar goto done; 57407fc05bdSLad Prabhakar } 57507fc05bdSLad Prabhakar 57607fc05bdSLad Prabhakar dev_dbg(cru->dev, "Capture start synced!\n"); 57707fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_RUNNING; 57807fc05bdSLad Prabhakar } 57907fc05bdSLad Prabhakar 58007fc05bdSLad Prabhakar /* Capture frame */ 58107fc05bdSLad Prabhakar if (cru->queue_buf[slot]) { 58207fc05bdSLad Prabhakar cru->queue_buf[slot]->field = cru->format.field; 58307fc05bdSLad Prabhakar cru->queue_buf[slot]->sequence = cru->sequence; 58407fc05bdSLad Prabhakar cru->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); 58507fc05bdSLad Prabhakar vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, 58607fc05bdSLad Prabhakar VB2_BUF_STATE_DONE); 58707fc05bdSLad Prabhakar cru->queue_buf[slot] = NULL; 58807fc05bdSLad Prabhakar } else { 58907fc05bdSLad Prabhakar /* Scratch buffer was used, dropping frame. */ 59007fc05bdSLad Prabhakar dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence); 59107fc05bdSLad Prabhakar } 59207fc05bdSLad Prabhakar 59307fc05bdSLad Prabhakar cru->sequence++; 59407fc05bdSLad Prabhakar 59507fc05bdSLad Prabhakar /* Prepare for next frame */ 59607fc05bdSLad Prabhakar rzg2l_cru_fill_hw_slot(cru, slot); 59707fc05bdSLad Prabhakar 59807fc05bdSLad Prabhakar done: 59907fc05bdSLad Prabhakar spin_unlock_irqrestore(&cru->qlock, flags); 60007fc05bdSLad Prabhakar 60107fc05bdSLad Prabhakar return IRQ_RETVAL(handled); 60207fc05bdSLad Prabhakar } 60307fc05bdSLad Prabhakar 60407fc05bdSLad Prabhakar static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count) 60507fc05bdSLad Prabhakar { 60607fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 60707fc05bdSLad Prabhakar int ret; 60807fc05bdSLad Prabhakar 6096bcff5f9SBiju Das ret = pm_runtime_resume_and_get(cru->dev); 6106bcff5f9SBiju Das if (ret) 6116bcff5f9SBiju Das return ret; 6126bcff5f9SBiju Das 6136bcff5f9SBiju Das ret = clk_prepare_enable(cru->vclk); 6146bcff5f9SBiju Das if (ret) 6156bcff5f9SBiju Das goto err_pm_put; 6166bcff5f9SBiju Das 61707fc05bdSLad Prabhakar /* Release reset state */ 61807fc05bdSLad Prabhakar ret = reset_control_deassert(cru->aresetn); 61907fc05bdSLad Prabhakar if (ret) { 62007fc05bdSLad Prabhakar dev_err(cru->dev, "failed to deassert aresetn\n"); 6216bcff5f9SBiju Das goto err_vclk_disable; 62207fc05bdSLad Prabhakar } 62307fc05bdSLad Prabhakar 62407fc05bdSLad Prabhakar ret = reset_control_deassert(cru->presetn); 62507fc05bdSLad Prabhakar if (ret) { 62607fc05bdSLad Prabhakar reset_control_assert(cru->aresetn); 62707fc05bdSLad Prabhakar dev_err(cru->dev, "failed to deassert presetn\n"); 6286bcff5f9SBiju Das goto assert_aresetn; 62907fc05bdSLad Prabhakar } 63007fc05bdSLad Prabhakar 63194794b5cSBiju Das /* Allocate scratch buffer */ 63207fc05bdSLad Prabhakar cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage, 63307fc05bdSLad Prabhakar &cru->scratch_phys, GFP_KERNEL); 63407fc05bdSLad Prabhakar if (!cru->scratch) { 63507fc05bdSLad Prabhakar return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); 63607fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to allocate scratch buffer\n"); 637e54334cdSJiapeng Chong ret = -ENOMEM; 6380c200daaSBiju Das goto assert_presetn; 63907fc05bdSLad Prabhakar } 64007fc05bdSLad Prabhakar 64107fc05bdSLad Prabhakar cru->sequence = 0; 64207fc05bdSLad Prabhakar 64307fc05bdSLad Prabhakar ret = rzg2l_cru_set_stream(cru, 1); 64407fc05bdSLad Prabhakar if (ret) { 64507fc05bdSLad Prabhakar return_unused_buffers(cru, VB2_BUF_STATE_QUEUED); 64607fc05bdSLad Prabhakar goto out; 64707fc05bdSLad Prabhakar } 64807fc05bdSLad Prabhakar 64907fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STARTING; 65007fc05bdSLad Prabhakar dev_dbg(cru->dev, "Starting to capture\n"); 65107fc05bdSLad Prabhakar return 0; 65207fc05bdSLad Prabhakar 65307fc05bdSLad Prabhakar out: 65407fc05bdSLad Prabhakar if (ret) 65507fc05bdSLad Prabhakar dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch, 65607fc05bdSLad Prabhakar cru->scratch_phys); 6576bcff5f9SBiju Das assert_presetn: 65807fc05bdSLad Prabhakar reset_control_assert(cru->presetn); 6596bcff5f9SBiju Das 6606bcff5f9SBiju Das assert_aresetn: 66107fc05bdSLad Prabhakar reset_control_assert(cru->aresetn); 66207fc05bdSLad Prabhakar 6636bcff5f9SBiju Das err_vclk_disable: 6646bcff5f9SBiju Das clk_disable_unprepare(cru->vclk); 6656bcff5f9SBiju Das 6666bcff5f9SBiju Das err_pm_put: 6676bcff5f9SBiju Das pm_runtime_put_sync(cru->dev); 6686bcff5f9SBiju Das 66907fc05bdSLad Prabhakar return ret; 67007fc05bdSLad Prabhakar } 67107fc05bdSLad Prabhakar 67207fc05bdSLad Prabhakar static void rzg2l_cru_stop_streaming_vq(struct vb2_queue *vq) 67307fc05bdSLad Prabhakar { 67407fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq); 67507fc05bdSLad Prabhakar 67607fc05bdSLad Prabhakar rzg2l_cru_stop_streaming(cru); 67707fc05bdSLad Prabhakar 67807fc05bdSLad Prabhakar /* Free scratch buffer */ 67907fc05bdSLad Prabhakar dma_free_coherent(cru->dev, cru->format.sizeimage, 68007fc05bdSLad Prabhakar cru->scratch, cru->scratch_phys); 68107fc05bdSLad Prabhakar 68207fc05bdSLad Prabhakar return_unused_buffers(cru, VB2_BUF_STATE_ERROR); 6836bcff5f9SBiju Das 6846bcff5f9SBiju Das reset_control_assert(cru->presetn); 6856bcff5f9SBiju Das clk_disable_unprepare(cru->vclk); 6866bcff5f9SBiju Das pm_runtime_put_sync(cru->dev); 68707fc05bdSLad Prabhakar } 68807fc05bdSLad Prabhakar 68907fc05bdSLad Prabhakar static const struct vb2_ops rzg2l_cru_qops = { 69007fc05bdSLad Prabhakar .queue_setup = rzg2l_cru_queue_setup, 69107fc05bdSLad Prabhakar .buf_prepare = rzg2l_cru_buffer_prepare, 69207fc05bdSLad Prabhakar .buf_queue = rzg2l_cru_buffer_queue, 69307fc05bdSLad Prabhakar .start_streaming = rzg2l_cru_start_streaming_vq, 69407fc05bdSLad Prabhakar .stop_streaming = rzg2l_cru_stop_streaming_vq, 69507fc05bdSLad Prabhakar }; 69607fc05bdSLad Prabhakar 69707fc05bdSLad Prabhakar void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru) 69807fc05bdSLad Prabhakar { 69907fc05bdSLad Prabhakar mutex_destroy(&cru->lock); 70007fc05bdSLad Prabhakar 70107fc05bdSLad Prabhakar v4l2_device_unregister(&cru->v4l2_dev); 70207fc05bdSLad Prabhakar vb2_queue_release(&cru->queue); 70307fc05bdSLad Prabhakar } 70407fc05bdSLad Prabhakar 70507fc05bdSLad Prabhakar int rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru) 70607fc05bdSLad Prabhakar { 70707fc05bdSLad Prabhakar struct vb2_queue *q = &cru->queue; 70807fc05bdSLad Prabhakar unsigned int i; 70907fc05bdSLad Prabhakar int ret; 71007fc05bdSLad Prabhakar 71107fc05bdSLad Prabhakar /* Initialize the top-level structure */ 71207fc05bdSLad Prabhakar ret = v4l2_device_register(cru->dev, &cru->v4l2_dev); 71307fc05bdSLad Prabhakar if (ret) 71407fc05bdSLad Prabhakar return ret; 71507fc05bdSLad Prabhakar 71607fc05bdSLad Prabhakar mutex_init(&cru->lock); 71707fc05bdSLad Prabhakar INIT_LIST_HEAD(&cru->buf_list); 71807fc05bdSLad Prabhakar 71907fc05bdSLad Prabhakar spin_lock_init(&cru->qlock); 72007fc05bdSLad Prabhakar 72107fc05bdSLad Prabhakar cru->state = RZG2L_CRU_DMA_STOPPED; 72207fc05bdSLad Prabhakar 72307fc05bdSLad Prabhakar for (i = 0; i < RZG2L_CRU_HW_BUFFER_MAX; i++) 72407fc05bdSLad Prabhakar cru->queue_buf[i] = NULL; 72507fc05bdSLad Prabhakar 72607fc05bdSLad Prabhakar /* buffer queue */ 72707fc05bdSLad Prabhakar q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 72807fc05bdSLad Prabhakar q->io_modes = VB2_MMAP | VB2_DMABUF; 72907fc05bdSLad Prabhakar q->lock = &cru->lock; 73007fc05bdSLad Prabhakar q->drv_priv = cru; 73107fc05bdSLad Prabhakar q->buf_struct_size = sizeof(struct rzg2l_cru_buffer); 73207fc05bdSLad Prabhakar q->ops = &rzg2l_cru_qops; 73307fc05bdSLad Prabhakar q->mem_ops = &vb2_dma_contig_memops; 73407fc05bdSLad Prabhakar q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 73580c2b40aSBenjamin Gaignard q->min_queued_buffers = 4; 73607fc05bdSLad Prabhakar q->dev = cru->dev; 73707fc05bdSLad Prabhakar 73807fc05bdSLad Prabhakar ret = vb2_queue_init(q); 73907fc05bdSLad Prabhakar if (ret < 0) { 74007fc05bdSLad Prabhakar dev_err(cru->dev, "failed to initialize VB2 queue\n"); 74107fc05bdSLad Prabhakar goto error; 74207fc05bdSLad Prabhakar } 74307fc05bdSLad Prabhakar 74407fc05bdSLad Prabhakar return 0; 74507fc05bdSLad Prabhakar 74607fc05bdSLad Prabhakar error: 74707fc05bdSLad Prabhakar mutex_destroy(&cru->lock); 74807fc05bdSLad Prabhakar v4l2_device_unregister(&cru->v4l2_dev); 74907fc05bdSLad Prabhakar return ret; 75007fc05bdSLad Prabhakar } 75107fc05bdSLad Prabhakar 75207fc05bdSLad Prabhakar /* ----------------------------------------------------------------------------- 75307fc05bdSLad Prabhakar * V4L2 stuff 75407fc05bdSLad Prabhakar */ 75507fc05bdSLad Prabhakar 7567e58132cSLad Prabhakar static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, 7577e58132cSLad Prabhakar struct v4l2_pix_format *pix) 75807fc05bdSLad Prabhakar { 7595f5ed645SLad Prabhakar const struct rzg2l_cru_info *info = cru->info; 7608853467cSLad Prabhakar const struct rzg2l_cru_ip_format *fmt; 76107fc05bdSLad Prabhakar 7628853467cSLad Prabhakar fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat); 7637e58132cSLad Prabhakar if (!fmt) { 76407fc05bdSLad Prabhakar pix->pixelformat = RZG2L_CRU_DEFAULT_FORMAT; 7657e58132cSLad Prabhakar fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat); 7667e58132cSLad Prabhakar } 76707fc05bdSLad Prabhakar 76807fc05bdSLad Prabhakar switch (pix->field) { 76907fc05bdSLad Prabhakar case V4L2_FIELD_TOP: 77007fc05bdSLad Prabhakar case V4L2_FIELD_BOTTOM: 77107fc05bdSLad Prabhakar case V4L2_FIELD_NONE: 77207fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED_TB: 77307fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED_BT: 77407fc05bdSLad Prabhakar case V4L2_FIELD_INTERLACED: 77507fc05bdSLad Prabhakar break; 77607fc05bdSLad Prabhakar default: 77707fc05bdSLad Prabhakar pix->field = RZG2L_CRU_DEFAULT_FIELD; 77807fc05bdSLad Prabhakar break; 77907fc05bdSLad Prabhakar } 78007fc05bdSLad Prabhakar 78107fc05bdSLad Prabhakar /* Limit to CRU capabilities */ 7825f5ed645SLad Prabhakar v4l_bound_align_image(&pix->width, 320, info->max_width, 1, 7835f5ed645SLad Prabhakar &pix->height, 240, info->max_height, 2, 0); 78407fc05bdSLad Prabhakar 7857e58132cSLad Prabhakar pix->bytesperline = pix->width * fmt->bpp; 786a8af02e8SLad Prabhakar pix->sizeimage = pix->bytesperline * pix->height; 78707fc05bdSLad Prabhakar 78807fc05bdSLad Prabhakar dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n", 78907fc05bdSLad Prabhakar pix->width, pix->height, pix->bytesperline, pix->sizeimage); 79007fc05bdSLad Prabhakar } 79107fc05bdSLad Prabhakar 79207fc05bdSLad Prabhakar static void rzg2l_cru_try_format(struct rzg2l_cru_dev *cru, 79307fc05bdSLad Prabhakar struct v4l2_pix_format *pix) 79407fc05bdSLad Prabhakar { 79507fc05bdSLad Prabhakar /* 79607fc05bdSLad Prabhakar * The V4L2 specification clearly documents the colorspace fields 79707fc05bdSLad Prabhakar * as being set by drivers for capture devices. Using the values 79807fc05bdSLad Prabhakar * supplied by userspace thus wouldn't comply with the API. Until 79907fc05bdSLad Prabhakar * the API is updated force fixed values. 80007fc05bdSLad Prabhakar */ 80107fc05bdSLad Prabhakar pix->colorspace = RZG2L_CRU_DEFAULT_COLORSPACE; 80207fc05bdSLad Prabhakar pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); 80307fc05bdSLad Prabhakar pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); 80407fc05bdSLad Prabhakar pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, pix->colorspace, 80507fc05bdSLad Prabhakar pix->ycbcr_enc); 80607fc05bdSLad Prabhakar 80707fc05bdSLad Prabhakar rzg2l_cru_format_align(cru, pix); 80807fc05bdSLad Prabhakar } 80907fc05bdSLad Prabhakar 81007fc05bdSLad Prabhakar static int rzg2l_cru_querycap(struct file *file, void *priv, 81107fc05bdSLad Prabhakar struct v4l2_capability *cap) 81207fc05bdSLad Prabhakar { 81307fc05bdSLad Prabhakar strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); 81407fc05bdSLad Prabhakar strscpy(cap->card, "RZG2L_CRU", sizeof(cap->card)); 81507fc05bdSLad Prabhakar 81607fc05bdSLad Prabhakar return 0; 81707fc05bdSLad Prabhakar } 81807fc05bdSLad Prabhakar 81907fc05bdSLad Prabhakar static int rzg2l_cru_try_fmt_vid_cap(struct file *file, void *priv, 82007fc05bdSLad Prabhakar struct v4l2_format *f) 82107fc05bdSLad Prabhakar { 82207fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 82307fc05bdSLad Prabhakar 82407fc05bdSLad Prabhakar rzg2l_cru_try_format(cru, &f->fmt.pix); 82507fc05bdSLad Prabhakar 82607fc05bdSLad Prabhakar return 0; 82707fc05bdSLad Prabhakar } 82807fc05bdSLad Prabhakar 82907fc05bdSLad Prabhakar static int rzg2l_cru_s_fmt_vid_cap(struct file *file, void *priv, 83007fc05bdSLad Prabhakar struct v4l2_format *f) 83107fc05bdSLad Prabhakar { 83207fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 83307fc05bdSLad Prabhakar 83407fc05bdSLad Prabhakar if (vb2_is_busy(&cru->queue)) 83507fc05bdSLad Prabhakar return -EBUSY; 83607fc05bdSLad Prabhakar 83707fc05bdSLad Prabhakar rzg2l_cru_try_format(cru, &f->fmt.pix); 83807fc05bdSLad Prabhakar 83907fc05bdSLad Prabhakar cru->format = f->fmt.pix; 84007fc05bdSLad Prabhakar 84107fc05bdSLad Prabhakar return 0; 84207fc05bdSLad Prabhakar } 84307fc05bdSLad Prabhakar 84407fc05bdSLad Prabhakar static int rzg2l_cru_g_fmt_vid_cap(struct file *file, void *priv, 84507fc05bdSLad Prabhakar struct v4l2_format *f) 84607fc05bdSLad Prabhakar { 84707fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 84807fc05bdSLad Prabhakar 84907fc05bdSLad Prabhakar f->fmt.pix = cru->format; 85007fc05bdSLad Prabhakar 85107fc05bdSLad Prabhakar return 0; 85207fc05bdSLad Prabhakar } 85307fc05bdSLad Prabhakar 85407fc05bdSLad Prabhakar static int rzg2l_cru_enum_fmt_vid_cap(struct file *file, void *priv, 85507fc05bdSLad Prabhakar struct v4l2_fmtdesc *f) 85607fc05bdSLad Prabhakar { 8578853467cSLad Prabhakar const struct rzg2l_cru_ip_format *fmt; 8588853467cSLad Prabhakar 8598853467cSLad Prabhakar fmt = rzg2l_cru_ip_index_to_fmt(f->index); 8608853467cSLad Prabhakar if (!fmt) 86107fc05bdSLad Prabhakar return -EINVAL; 86207fc05bdSLad Prabhakar 8638853467cSLad Prabhakar f->pixelformat = fmt->format; 86407fc05bdSLad Prabhakar 86507fc05bdSLad Prabhakar return 0; 86607fc05bdSLad Prabhakar } 86707fc05bdSLad Prabhakar 86807fc05bdSLad Prabhakar static const struct v4l2_ioctl_ops rzg2l_cru_ioctl_ops = { 86907fc05bdSLad Prabhakar .vidioc_querycap = rzg2l_cru_querycap, 87007fc05bdSLad Prabhakar .vidioc_try_fmt_vid_cap = rzg2l_cru_try_fmt_vid_cap, 87107fc05bdSLad Prabhakar .vidioc_g_fmt_vid_cap = rzg2l_cru_g_fmt_vid_cap, 87207fc05bdSLad Prabhakar .vidioc_s_fmt_vid_cap = rzg2l_cru_s_fmt_vid_cap, 87307fc05bdSLad Prabhakar .vidioc_enum_fmt_vid_cap = rzg2l_cru_enum_fmt_vid_cap, 87407fc05bdSLad Prabhakar 87507fc05bdSLad Prabhakar .vidioc_reqbufs = vb2_ioctl_reqbufs, 87607fc05bdSLad Prabhakar .vidioc_create_bufs = vb2_ioctl_create_bufs, 87707fc05bdSLad Prabhakar .vidioc_querybuf = vb2_ioctl_querybuf, 87807fc05bdSLad Prabhakar .vidioc_qbuf = vb2_ioctl_qbuf, 87907fc05bdSLad Prabhakar .vidioc_dqbuf = vb2_ioctl_dqbuf, 88007fc05bdSLad Prabhakar .vidioc_expbuf = vb2_ioctl_expbuf, 88107fc05bdSLad Prabhakar .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 88207fc05bdSLad Prabhakar .vidioc_streamon = vb2_ioctl_streamon, 88307fc05bdSLad Prabhakar .vidioc_streamoff = vb2_ioctl_streamoff, 88407fc05bdSLad Prabhakar }; 88507fc05bdSLad Prabhakar 88607fc05bdSLad Prabhakar /* ----------------------------------------------------------------------------- 88707fc05bdSLad Prabhakar * Media controller file operations 88807fc05bdSLad Prabhakar */ 88907fc05bdSLad Prabhakar 89007fc05bdSLad Prabhakar static int rzg2l_cru_open(struct file *file) 89107fc05bdSLad Prabhakar { 89207fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 89307fc05bdSLad Prabhakar int ret; 89407fc05bdSLad Prabhakar 89507fc05bdSLad Prabhakar ret = mutex_lock_interruptible(&cru->lock); 89607fc05bdSLad Prabhakar if (ret) 89707fc05bdSLad Prabhakar return ret; 89807fc05bdSLad Prabhakar 89907fc05bdSLad Prabhakar file->private_data = cru; 90007fc05bdSLad Prabhakar ret = v4l2_fh_open(file); 90107fc05bdSLad Prabhakar if (ret) 90207fc05bdSLad Prabhakar goto err_unlock; 90307fc05bdSLad Prabhakar 90407fc05bdSLad Prabhakar mutex_unlock(&cru->lock); 90507fc05bdSLad Prabhakar 90607fc05bdSLad Prabhakar return 0; 90707fc05bdSLad Prabhakar 90807fc05bdSLad Prabhakar err_unlock: 90907fc05bdSLad Prabhakar mutex_unlock(&cru->lock); 91007fc05bdSLad Prabhakar 91107fc05bdSLad Prabhakar return ret; 91207fc05bdSLad Prabhakar } 91307fc05bdSLad Prabhakar 91407fc05bdSLad Prabhakar static int rzg2l_cru_release(struct file *file) 91507fc05bdSLad Prabhakar { 91607fc05bdSLad Prabhakar struct rzg2l_cru_dev *cru = video_drvdata(file); 91707fc05bdSLad Prabhakar int ret; 91807fc05bdSLad Prabhakar 91907fc05bdSLad Prabhakar mutex_lock(&cru->lock); 92007fc05bdSLad Prabhakar 92107fc05bdSLad Prabhakar /* the release helper will cleanup any on-going streaming. */ 92207fc05bdSLad Prabhakar ret = _vb2_fop_release(file, NULL); 92307fc05bdSLad Prabhakar 92407fc05bdSLad Prabhakar mutex_unlock(&cru->lock); 92507fc05bdSLad Prabhakar 92607fc05bdSLad Prabhakar return ret; 92707fc05bdSLad Prabhakar } 92807fc05bdSLad Prabhakar 92907fc05bdSLad Prabhakar static const struct v4l2_file_operations rzg2l_cru_fops = { 93007fc05bdSLad Prabhakar .owner = THIS_MODULE, 93107fc05bdSLad Prabhakar .unlocked_ioctl = video_ioctl2, 93207fc05bdSLad Prabhakar .open = rzg2l_cru_open, 93307fc05bdSLad Prabhakar .release = rzg2l_cru_release, 93407fc05bdSLad Prabhakar .poll = vb2_fop_poll, 93507fc05bdSLad Prabhakar .mmap = vb2_fop_mmap, 93607fc05bdSLad Prabhakar .read = vb2_fop_read, 93707fc05bdSLad Prabhakar }; 93807fc05bdSLad Prabhakar 939f7b55b77SLad Prabhakar /* ----------------------------------------------------------------------------- 940f7b55b77SLad Prabhakar * Media entity operations 941f7b55b77SLad Prabhakar */ 942f7b55b77SLad Prabhakar 943f7b55b77SLad Prabhakar static int rzg2l_cru_video_link_validate(struct media_link *link) 944f7b55b77SLad Prabhakar { 945f7b55b77SLad Prabhakar struct v4l2_subdev_format fmt = { 946f7b55b77SLad Prabhakar .which = V4L2_SUBDEV_FORMAT_ACTIVE, 947f7b55b77SLad Prabhakar }; 948f7b55b77SLad Prabhakar const struct rzg2l_cru_ip_format *video_fmt; 949f7b55b77SLad Prabhakar struct v4l2_subdev *subdev; 950f7b55b77SLad Prabhakar struct rzg2l_cru_dev *cru; 951f7b55b77SLad Prabhakar int ret; 952f7b55b77SLad Prabhakar 953f7b55b77SLad Prabhakar subdev = media_entity_to_v4l2_subdev(link->source->entity); 954f7b55b77SLad Prabhakar fmt.pad = link->source->index; 955f7b55b77SLad Prabhakar ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 956f7b55b77SLad Prabhakar if (ret < 0) 957f7b55b77SLad Prabhakar return ret == -ENOIOCTLCMD ? -EINVAL : ret; 958f7b55b77SLad Prabhakar 959f7b55b77SLad Prabhakar cru = container_of(media_entity_to_video_device(link->sink->entity), 960f7b55b77SLad Prabhakar struct rzg2l_cru_dev, vdev); 961f7b55b77SLad Prabhakar video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat); 962f7b55b77SLad Prabhakar 963f7b55b77SLad Prabhakar if (fmt.format.width != cru->format.width || 964f7b55b77SLad Prabhakar fmt.format.height != cru->format.height || 965f7b55b77SLad Prabhakar fmt.format.field != cru->format.field || 966f7b55b77SLad Prabhakar video_fmt->code != fmt.format.code) 967f7b55b77SLad Prabhakar return -EPIPE; 968f7b55b77SLad Prabhakar 969f7b55b77SLad Prabhakar return 0; 970f7b55b77SLad Prabhakar } 971f7b55b77SLad Prabhakar 972f7b55b77SLad Prabhakar static const struct media_entity_operations rzg2l_cru_video_media_ops = { 973f7b55b77SLad Prabhakar .link_validate = rzg2l_cru_video_link_validate, 974f7b55b77SLad Prabhakar }; 975f7b55b77SLad Prabhakar 97607fc05bdSLad Prabhakar static void rzg2l_cru_v4l2_init(struct rzg2l_cru_dev *cru) 97707fc05bdSLad Prabhakar { 97807fc05bdSLad Prabhakar struct video_device *vdev = &cru->vdev; 97907fc05bdSLad Prabhakar 98007fc05bdSLad Prabhakar vdev->v4l2_dev = &cru->v4l2_dev; 98107fc05bdSLad Prabhakar vdev->queue = &cru->queue; 98207fc05bdSLad Prabhakar snprintf(vdev->name, sizeof(vdev->name), "CRU output"); 98307fc05bdSLad Prabhakar vdev->release = video_device_release_empty; 98407fc05bdSLad Prabhakar vdev->lock = &cru->lock; 98507fc05bdSLad Prabhakar vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 98607fc05bdSLad Prabhakar vdev->device_caps |= V4L2_CAP_IO_MC; 987f7b55b77SLad Prabhakar vdev->entity.ops = &rzg2l_cru_video_media_ops; 98807fc05bdSLad Prabhakar vdev->fops = &rzg2l_cru_fops; 98907fc05bdSLad Prabhakar vdev->ioctl_ops = &rzg2l_cru_ioctl_ops; 99007fc05bdSLad Prabhakar 99107fc05bdSLad Prabhakar /* Set a default format */ 99207fc05bdSLad Prabhakar cru->format.pixelformat = RZG2L_CRU_DEFAULT_FORMAT; 99307fc05bdSLad Prabhakar cru->format.width = RZG2L_CRU_DEFAULT_WIDTH; 99407fc05bdSLad Prabhakar cru->format.height = RZG2L_CRU_DEFAULT_HEIGHT; 99507fc05bdSLad Prabhakar cru->format.field = RZG2L_CRU_DEFAULT_FIELD; 99607fc05bdSLad Prabhakar cru->format.colorspace = RZG2L_CRU_DEFAULT_COLORSPACE; 99707fc05bdSLad Prabhakar rzg2l_cru_format_align(cru, &cru->format); 99807fc05bdSLad Prabhakar } 99907fc05bdSLad Prabhakar 100007fc05bdSLad Prabhakar void rzg2l_cru_video_unregister(struct rzg2l_cru_dev *cru) 100107fc05bdSLad Prabhakar { 100207fc05bdSLad Prabhakar media_device_unregister(&cru->mdev); 100307fc05bdSLad Prabhakar video_unregister_device(&cru->vdev); 100407fc05bdSLad Prabhakar } 100507fc05bdSLad Prabhakar 100607fc05bdSLad Prabhakar int rzg2l_cru_video_register(struct rzg2l_cru_dev *cru) 100707fc05bdSLad Prabhakar { 100807fc05bdSLad Prabhakar struct video_device *vdev = &cru->vdev; 100907fc05bdSLad Prabhakar int ret; 101007fc05bdSLad Prabhakar 101107fc05bdSLad Prabhakar if (video_is_registered(&cru->vdev)) { 101207fc05bdSLad Prabhakar struct media_entity *entity; 101307fc05bdSLad Prabhakar 101407fc05bdSLad Prabhakar entity = &cru->vdev.entity; 101507fc05bdSLad Prabhakar if (!entity->graph_obj.mdev) 101607fc05bdSLad Prabhakar entity->graph_obj.mdev = &cru->mdev; 101707fc05bdSLad Prabhakar return 0; 101807fc05bdSLad Prabhakar } 101907fc05bdSLad Prabhakar 102007fc05bdSLad Prabhakar rzg2l_cru_v4l2_init(cru); 102107fc05bdSLad Prabhakar video_set_drvdata(vdev, cru); 102207fc05bdSLad Prabhakar ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 102307fc05bdSLad Prabhakar if (ret) { 102407fc05bdSLad Prabhakar dev_err(cru->dev, "Failed to register video device\n"); 102507fc05bdSLad Prabhakar return ret; 102607fc05bdSLad Prabhakar } 102707fc05bdSLad Prabhakar 102807fc05bdSLad Prabhakar ret = media_device_register(&cru->mdev); 102907fc05bdSLad Prabhakar if (ret) { 103007fc05bdSLad Prabhakar video_unregister_device(&cru->vdev); 103107fc05bdSLad Prabhakar return ret; 103207fc05bdSLad Prabhakar } 103307fc05bdSLad Prabhakar 103407fc05bdSLad Prabhakar return 0; 103507fc05bdSLad Prabhakar } 1036