1*9f257f50SLaurent Pinchart // SPDX-License-Identifier: GPL-2.0 2*9f257f50SLaurent Pinchart /* 3*9f257f50SLaurent Pinchart * V4L2 Capture CSI Subdev for Freescale i.MX6UL/L / i.MX7 SOC 4*9f257f50SLaurent Pinchart * 5*9f257f50SLaurent Pinchart * Copyright (c) 2019 Linaro Ltd 6*9f257f50SLaurent Pinchart * 7*9f257f50SLaurent Pinchart */ 8*9f257f50SLaurent Pinchart 9*9f257f50SLaurent Pinchart #include <linux/clk.h> 10*9f257f50SLaurent Pinchart #include <linux/delay.h> 11*9f257f50SLaurent Pinchart #include <linux/interrupt.h> 12*9f257f50SLaurent Pinchart #include <linux/mfd/syscon.h> 13*9f257f50SLaurent Pinchart #include <linux/module.h> 14*9f257f50SLaurent Pinchart #include <linux/of_device.h> 15*9f257f50SLaurent Pinchart #include <linux/of_graph.h> 16*9f257f50SLaurent Pinchart #include <linux/pinctrl/consumer.h> 17*9f257f50SLaurent Pinchart #include <linux/platform_device.h> 18*9f257f50SLaurent Pinchart #include <linux/regmap.h> 19*9f257f50SLaurent Pinchart #include <linux/slab.h> 20*9f257f50SLaurent Pinchart #include <linux/spinlock.h> 21*9f257f50SLaurent Pinchart #include <linux/types.h> 22*9f257f50SLaurent Pinchart 23*9f257f50SLaurent Pinchart #include <media/v4l2-device.h> 24*9f257f50SLaurent Pinchart #include <media/v4l2-fwnode.h> 25*9f257f50SLaurent Pinchart #include <media/v4l2-ioctl.h> 26*9f257f50SLaurent Pinchart #include <media/v4l2-mc.h> 27*9f257f50SLaurent Pinchart #include <media/v4l2-subdev.h> 28*9f257f50SLaurent Pinchart #include <media/videobuf2-dma-contig.h> 29*9f257f50SLaurent Pinchart 30*9f257f50SLaurent Pinchart #define IMX7_CSI_PAD_SINK 0 31*9f257f50SLaurent Pinchart #define IMX7_CSI_PAD_SRC 1 32*9f257f50SLaurent Pinchart #define IMX7_CSI_PADS_NUM 2 33*9f257f50SLaurent Pinchart 34*9f257f50SLaurent Pinchart /* csi control reg 1 */ 35*9f257f50SLaurent Pinchart #define BIT_SWAP16_EN BIT(31) 36*9f257f50SLaurent Pinchart #define BIT_EXT_VSYNC BIT(30) 37*9f257f50SLaurent Pinchart #define BIT_EOF_INT_EN BIT(29) 38*9f257f50SLaurent Pinchart #define BIT_PRP_IF_EN BIT(28) 39*9f257f50SLaurent Pinchart #define BIT_CCIR_MODE BIT(27) 40*9f257f50SLaurent Pinchart #define BIT_COF_INT_EN BIT(26) 41*9f257f50SLaurent Pinchart #define BIT_SF_OR_INTEN BIT(25) 42*9f257f50SLaurent Pinchart #define BIT_RF_OR_INTEN BIT(24) 43*9f257f50SLaurent Pinchart #define BIT_SFF_DMA_DONE_INTEN BIT(22) 44*9f257f50SLaurent Pinchart #define BIT_STATFF_INTEN BIT(21) 45*9f257f50SLaurent Pinchart #define BIT_FB2_DMA_DONE_INTEN BIT(20) 46*9f257f50SLaurent Pinchart #define BIT_FB1_DMA_DONE_INTEN BIT(19) 47*9f257f50SLaurent Pinchart #define BIT_RXFF_INTEN BIT(18) 48*9f257f50SLaurent Pinchart #define BIT_SOF_POL BIT(17) 49*9f257f50SLaurent Pinchart #define BIT_SOF_INTEN BIT(16) 50*9f257f50SLaurent Pinchart #define BIT_MCLKDIV(n) ((n) << 12) 51*9f257f50SLaurent Pinchart #define BIT_MCLKDIV_MASK (0xf << 12) 52*9f257f50SLaurent Pinchart #define BIT_HSYNC_POL BIT(11) 53*9f257f50SLaurent Pinchart #define BIT_CCIR_EN BIT(10) 54*9f257f50SLaurent Pinchart #define BIT_MCLKEN BIT(9) 55*9f257f50SLaurent Pinchart #define BIT_FCC BIT(8) 56*9f257f50SLaurent Pinchart #define BIT_PACK_DIR BIT(7) 57*9f257f50SLaurent Pinchart #define BIT_CLR_STATFIFO BIT(6) 58*9f257f50SLaurent Pinchart #define BIT_CLR_RXFIFO BIT(5) 59*9f257f50SLaurent Pinchart #define BIT_GCLK_MODE BIT(4) 60*9f257f50SLaurent Pinchart #define BIT_INV_DATA BIT(3) 61*9f257f50SLaurent Pinchart #define BIT_INV_PCLK BIT(2) 62*9f257f50SLaurent Pinchart #define BIT_REDGE BIT(1) 63*9f257f50SLaurent Pinchart #define BIT_PIXEL_BIT BIT(0) 64*9f257f50SLaurent Pinchart 65*9f257f50SLaurent Pinchart /* control reg 2 */ 66*9f257f50SLaurent Pinchart #define BIT_DMA_BURST_TYPE_RFF_INCR4 (1 << 30) 67*9f257f50SLaurent Pinchart #define BIT_DMA_BURST_TYPE_RFF_INCR8 (2 << 30) 68*9f257f50SLaurent Pinchart #define BIT_DMA_BURST_TYPE_RFF_INCR16 (3 << 30) 69*9f257f50SLaurent Pinchart #define BIT_DMA_BURST_TYPE_RFF_MASK (3 << 30) 70*9f257f50SLaurent Pinchart 71*9f257f50SLaurent Pinchart /* control reg 3 */ 72*9f257f50SLaurent Pinchart #define BIT_FRMCNT(n) ((n) << 16) 73*9f257f50SLaurent Pinchart #define BIT_FRMCNT_MASK (0xffff << 16) 74*9f257f50SLaurent Pinchart #define BIT_FRMCNT_RST BIT(15) 75*9f257f50SLaurent Pinchart #define BIT_DMA_REFLASH_RFF BIT(14) 76*9f257f50SLaurent Pinchart #define BIT_DMA_REFLASH_SFF BIT(13) 77*9f257f50SLaurent Pinchart #define BIT_DMA_REQ_EN_RFF BIT(12) 78*9f257f50SLaurent Pinchart #define BIT_DMA_REQ_EN_SFF BIT(11) 79*9f257f50SLaurent Pinchart #define BIT_STATFF_LEVEL(n) ((n) << 8) 80*9f257f50SLaurent Pinchart #define BIT_STATFF_LEVEL_MASK (0x7 << 8) 81*9f257f50SLaurent Pinchart #define BIT_HRESP_ERR_EN BIT(7) 82*9f257f50SLaurent Pinchart #define BIT_RXFF_LEVEL(n) ((n) << 4) 83*9f257f50SLaurent Pinchart #define BIT_RXFF_LEVEL_MASK (0x7 << 4) 84*9f257f50SLaurent Pinchart #define BIT_TWO_8BIT_SENSOR BIT(3) 85*9f257f50SLaurent Pinchart #define BIT_ZERO_PACK_EN BIT(2) 86*9f257f50SLaurent Pinchart #define BIT_ECC_INT_EN BIT(1) 87*9f257f50SLaurent Pinchart #define BIT_ECC_AUTO_EN BIT(0) 88*9f257f50SLaurent Pinchart 89*9f257f50SLaurent Pinchart /* csi status reg */ 90*9f257f50SLaurent Pinchart #define BIT_ADDR_CH_ERR_INT BIT(28) 91*9f257f50SLaurent Pinchart #define BIT_FIELD0_INT BIT(27) 92*9f257f50SLaurent Pinchart #define BIT_FIELD1_INT BIT(26) 93*9f257f50SLaurent Pinchart #define BIT_SFF_OR_INT BIT(25) 94*9f257f50SLaurent Pinchart #define BIT_RFF_OR_INT BIT(24) 95*9f257f50SLaurent Pinchart #define BIT_DMA_TSF_DONE_SFF BIT(22) 96*9f257f50SLaurent Pinchart #define BIT_STATFF_INT BIT(21) 97*9f257f50SLaurent Pinchart #define BIT_DMA_TSF_DONE_FB2 BIT(20) 98*9f257f50SLaurent Pinchart #define BIT_DMA_TSF_DONE_FB1 BIT(19) 99*9f257f50SLaurent Pinchart #define BIT_RXFF_INT BIT(18) 100*9f257f50SLaurent Pinchart #define BIT_EOF_INT BIT(17) 101*9f257f50SLaurent Pinchart #define BIT_SOF_INT BIT(16) 102*9f257f50SLaurent Pinchart #define BIT_F2_INT BIT(15) 103*9f257f50SLaurent Pinchart #define BIT_F1_INT BIT(14) 104*9f257f50SLaurent Pinchart #define BIT_COF_INT BIT(13) 105*9f257f50SLaurent Pinchart #define BIT_HRESP_ERR_INT BIT(7) 106*9f257f50SLaurent Pinchart #define BIT_ECC_INT BIT(1) 107*9f257f50SLaurent Pinchart #define BIT_DRDY BIT(0) 108*9f257f50SLaurent Pinchart 109*9f257f50SLaurent Pinchart /* csi image parameter reg */ 110*9f257f50SLaurent Pinchart #define BIT_IMAGE_WIDTH(n) ((n) << 16) 111*9f257f50SLaurent Pinchart #define BIT_IMAGE_HEIGHT(n) (n) 112*9f257f50SLaurent Pinchart 113*9f257f50SLaurent Pinchart /* csi control reg 18 */ 114*9f257f50SLaurent Pinchart #define BIT_CSI_HW_ENABLE BIT(31) 115*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_RAW8 (0x2a << 25) 116*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_RAW10 (0x2b << 25) 117*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_RAW12 (0x2c << 25) 118*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_RAW14 (0x2d << 25) 119*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_YUV422_8B (0x1e << 25) 120*9f257f50SLaurent Pinchart #define BIT_MIPI_DATA_FORMAT_MASK (0x3f << 25) 121*9f257f50SLaurent Pinchart #define BIT_DATA_FROM_MIPI BIT(22) 122*9f257f50SLaurent Pinchart #define BIT_MIPI_YU_SWAP BIT(21) 123*9f257f50SLaurent Pinchart #define BIT_MIPI_DOUBLE_CMPNT BIT(20) 124*9f257f50SLaurent Pinchart #define BIT_MASK_OPTION_FIRST_FRAME (0 << 18) 125*9f257f50SLaurent Pinchart #define BIT_MASK_OPTION_CSI_EN (1 << 18) 126*9f257f50SLaurent Pinchart #define BIT_MASK_OPTION_SECOND_FRAME (2 << 18) 127*9f257f50SLaurent Pinchart #define BIT_MASK_OPTION_ON_DATA (3 << 18) 128*9f257f50SLaurent Pinchart #define BIT_BASEADDR_CHG_ERR_EN BIT(9) 129*9f257f50SLaurent Pinchart #define BIT_BASEADDR_SWITCH_SEL BIT(5) 130*9f257f50SLaurent Pinchart #define BIT_BASEADDR_SWITCH_EN BIT(4) 131*9f257f50SLaurent Pinchart #define BIT_PARALLEL24_EN BIT(3) 132*9f257f50SLaurent Pinchart #define BIT_DEINTERLACE_EN BIT(2) 133*9f257f50SLaurent Pinchart #define BIT_TVDECODER_IN_EN BIT(1) 134*9f257f50SLaurent Pinchart #define BIT_NTSC_EN BIT(0) 135*9f257f50SLaurent Pinchart 136*9f257f50SLaurent Pinchart #define CSI_MCLK_VF 1 137*9f257f50SLaurent Pinchart #define CSI_MCLK_ENC 2 138*9f257f50SLaurent Pinchart #define CSI_MCLK_RAW 4 139*9f257f50SLaurent Pinchart #define CSI_MCLK_I2C 8 140*9f257f50SLaurent Pinchart 141*9f257f50SLaurent Pinchart #define CSI_CSICR1 0x00 142*9f257f50SLaurent Pinchart #define CSI_CSICR2 0x04 143*9f257f50SLaurent Pinchart #define CSI_CSICR3 0x08 144*9f257f50SLaurent Pinchart #define CSI_STATFIFO 0x0c 145*9f257f50SLaurent Pinchart #define CSI_CSIRXFIFO 0x10 146*9f257f50SLaurent Pinchart #define CSI_CSIRXCNT 0x14 147*9f257f50SLaurent Pinchart #define CSI_CSISR 0x18 148*9f257f50SLaurent Pinchart 149*9f257f50SLaurent Pinchart #define CSI_CSIDBG 0x1c 150*9f257f50SLaurent Pinchart #define CSI_CSIDMASA_STATFIFO 0x20 151*9f257f50SLaurent Pinchart #define CSI_CSIDMATS_STATFIFO 0x24 152*9f257f50SLaurent Pinchart #define CSI_CSIDMASA_FB1 0x28 153*9f257f50SLaurent Pinchart #define CSI_CSIDMASA_FB2 0x2c 154*9f257f50SLaurent Pinchart #define CSI_CSIFBUF_PARA 0x30 155*9f257f50SLaurent Pinchart #define CSI_CSIIMAG_PARA 0x34 156*9f257f50SLaurent Pinchart 157*9f257f50SLaurent Pinchart #define CSI_CSICR18 0x48 158*9f257f50SLaurent Pinchart #define CSI_CSICR19 0x4c 159*9f257f50SLaurent Pinchart 160*9f257f50SLaurent Pinchart #define IMX7_CSI_VIDEO_NAME "imx-capture" 161*9f257f50SLaurent Pinchart /* In bytes, per queue */ 162*9f257f50SLaurent Pinchart #define IMX7_CSI_VIDEO_MEM_LIMIT SZ_512M 163*9f257f50SLaurent Pinchart #define IMX7_CSI_VIDEO_EOF_TIMEOUT 2000 164*9f257f50SLaurent Pinchart 165*9f257f50SLaurent Pinchart #define IMX7_CSI_DEF_MBUS_CODE MEDIA_BUS_FMT_UYVY8_2X8 166*9f257f50SLaurent Pinchart #define IMX7_CSI_DEF_PIX_FORMAT V4L2_PIX_FMT_UYVY 167*9f257f50SLaurent Pinchart #define IMX7_CSI_DEF_PIX_WIDTH 640 168*9f257f50SLaurent Pinchart #define IMX7_CSI_DEF_PIX_HEIGHT 480 169*9f257f50SLaurent Pinchart 170*9f257f50SLaurent Pinchart enum imx_csi_model { 171*9f257f50SLaurent Pinchart IMX7_CSI_IMX7 = 0, 172*9f257f50SLaurent Pinchart IMX7_CSI_IMX8MQ, 173*9f257f50SLaurent Pinchart }; 174*9f257f50SLaurent Pinchart 175*9f257f50SLaurent Pinchart struct imx7_csi_pixfmt { 176*9f257f50SLaurent Pinchart /* the in-memory FourCC pixel format */ 177*9f257f50SLaurent Pinchart u32 fourcc; 178*9f257f50SLaurent Pinchart /* 179*9f257f50SLaurent Pinchart * the set of equivalent media bus codes for the fourcc. 180*9f257f50SLaurent Pinchart * NOTE! codes pointer is NULL for in-memory-only formats. 181*9f257f50SLaurent Pinchart */ 182*9f257f50SLaurent Pinchart const u32 *codes; 183*9f257f50SLaurent Pinchart int bpp; /* total bpp */ 184*9f257f50SLaurent Pinchart bool yuv; 185*9f257f50SLaurent Pinchart }; 186*9f257f50SLaurent Pinchart 187*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer { 188*9f257f50SLaurent Pinchart struct vb2_v4l2_buffer vbuf; 189*9f257f50SLaurent Pinchart struct list_head list; 190*9f257f50SLaurent Pinchart }; 191*9f257f50SLaurent Pinchart 192*9f257f50SLaurent Pinchart static inline struct imx7_csi_vb2_buffer * 193*9f257f50SLaurent Pinchart to_imx7_csi_vb2_buffer(struct vb2_buffer *vb) 194*9f257f50SLaurent Pinchart { 195*9f257f50SLaurent Pinchart struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 196*9f257f50SLaurent Pinchart 197*9f257f50SLaurent Pinchart return container_of(vbuf, struct imx7_csi_vb2_buffer, vbuf); 198*9f257f50SLaurent Pinchart } 199*9f257f50SLaurent Pinchart 200*9f257f50SLaurent Pinchart struct imx7_csi_dma_buf { 201*9f257f50SLaurent Pinchart void *virt; 202*9f257f50SLaurent Pinchart dma_addr_t dma_addr; 203*9f257f50SLaurent Pinchart unsigned long len; 204*9f257f50SLaurent Pinchart }; 205*9f257f50SLaurent Pinchart 206*9f257f50SLaurent Pinchart struct imx7_csi { 207*9f257f50SLaurent Pinchart struct device *dev; 208*9f257f50SLaurent Pinchart 209*9f257f50SLaurent Pinchart /* Resources and locks */ 210*9f257f50SLaurent Pinchart void __iomem *regbase; 211*9f257f50SLaurent Pinchart int irq; 212*9f257f50SLaurent Pinchart struct clk *mclk; 213*9f257f50SLaurent Pinchart 214*9f257f50SLaurent Pinchart struct mutex lock; /* Protects is_streaming, format_mbus, cc */ 215*9f257f50SLaurent Pinchart spinlock_t irqlock; /* Protects last_eof */ 216*9f257f50SLaurent Pinchart 217*9f257f50SLaurent Pinchart /* Media and V4L2 device */ 218*9f257f50SLaurent Pinchart struct media_device mdev; 219*9f257f50SLaurent Pinchart struct v4l2_device v4l2_dev; 220*9f257f50SLaurent Pinchart struct v4l2_async_notifier notifier; 221*9f257f50SLaurent Pinchart struct media_pipeline pipe; 222*9f257f50SLaurent Pinchart 223*9f257f50SLaurent Pinchart struct v4l2_subdev *src_sd; 224*9f257f50SLaurent Pinchart bool is_csi2; 225*9f257f50SLaurent Pinchart 226*9f257f50SLaurent Pinchart /* V4L2 subdev */ 227*9f257f50SLaurent Pinchart struct v4l2_subdev sd; 228*9f257f50SLaurent Pinchart struct media_pad pad[IMX7_CSI_PADS_NUM]; 229*9f257f50SLaurent Pinchart 230*9f257f50SLaurent Pinchart struct v4l2_mbus_framefmt format_mbus[IMX7_CSI_PADS_NUM]; 231*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *cc[IMX7_CSI_PADS_NUM]; 232*9f257f50SLaurent Pinchart 233*9f257f50SLaurent Pinchart /* Video device */ 234*9f257f50SLaurent Pinchart struct video_device *vdev; /* Video device */ 235*9f257f50SLaurent Pinchart struct media_pad vdev_pad; /* Video device pad */ 236*9f257f50SLaurent Pinchart 237*9f257f50SLaurent Pinchart struct v4l2_pix_format vdev_fmt; /* The user format */ 238*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *vdev_cc; 239*9f257f50SLaurent Pinchart struct v4l2_rect vdev_compose; /* The compose rectangle */ 240*9f257f50SLaurent Pinchart 241*9f257f50SLaurent Pinchart struct mutex vdev_mutex; /* Protect vdev operations */ 242*9f257f50SLaurent Pinchart 243*9f257f50SLaurent Pinchart struct vb2_queue q; /* The videobuf2 queue */ 244*9f257f50SLaurent Pinchart struct list_head ready_q; /* List of queued buffers */ 245*9f257f50SLaurent Pinchart spinlock_t q_lock; /* Protect ready_q */ 246*9f257f50SLaurent Pinchart 247*9f257f50SLaurent Pinchart /* Buffers and streaming state */ 248*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer *active_vb2_buf[2]; 249*9f257f50SLaurent Pinchart struct imx7_csi_dma_buf underrun_buf; 250*9f257f50SLaurent Pinchart 251*9f257f50SLaurent Pinchart bool is_streaming; 252*9f257f50SLaurent Pinchart int buf_num; 253*9f257f50SLaurent Pinchart u32 frame_sequence; 254*9f257f50SLaurent Pinchart 255*9f257f50SLaurent Pinchart bool last_eof; 256*9f257f50SLaurent Pinchart struct completion last_eof_completion; 257*9f257f50SLaurent Pinchart 258*9f257f50SLaurent Pinchart enum imx_csi_model model; 259*9f257f50SLaurent Pinchart }; 260*9f257f50SLaurent Pinchart 261*9f257f50SLaurent Pinchart static struct imx7_csi * 262*9f257f50SLaurent Pinchart imx7_csi_notifier_to_dev(struct v4l2_async_notifier *n) 263*9f257f50SLaurent Pinchart { 264*9f257f50SLaurent Pinchart return container_of(n, struct imx7_csi, notifier); 265*9f257f50SLaurent Pinchart } 266*9f257f50SLaurent Pinchart 267*9f257f50SLaurent Pinchart /* ----------------------------------------------------------------------------- 268*9f257f50SLaurent Pinchart * Hardware Configuration 269*9f257f50SLaurent Pinchart */ 270*9f257f50SLaurent Pinchart 271*9f257f50SLaurent Pinchart static u32 imx7_csi_reg_read(struct imx7_csi *csi, unsigned int offset) 272*9f257f50SLaurent Pinchart { 273*9f257f50SLaurent Pinchart return readl(csi->regbase + offset); 274*9f257f50SLaurent Pinchart } 275*9f257f50SLaurent Pinchart 276*9f257f50SLaurent Pinchart static void imx7_csi_reg_write(struct imx7_csi *csi, unsigned int value, 277*9f257f50SLaurent Pinchart unsigned int offset) 278*9f257f50SLaurent Pinchart { 279*9f257f50SLaurent Pinchart writel(value, csi->regbase + offset); 280*9f257f50SLaurent Pinchart } 281*9f257f50SLaurent Pinchart 282*9f257f50SLaurent Pinchart static u32 imx7_csi_irq_clear(struct imx7_csi *csi) 283*9f257f50SLaurent Pinchart { 284*9f257f50SLaurent Pinchart u32 isr; 285*9f257f50SLaurent Pinchart 286*9f257f50SLaurent Pinchart isr = imx7_csi_reg_read(csi, CSI_CSISR); 287*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, isr, CSI_CSISR); 288*9f257f50SLaurent Pinchart 289*9f257f50SLaurent Pinchart return isr; 290*9f257f50SLaurent Pinchart } 291*9f257f50SLaurent Pinchart 292*9f257f50SLaurent Pinchart static void imx7_csi_init_default(struct imx7_csi *csi) 293*9f257f50SLaurent Pinchart { 294*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, BIT_SOF_POL | BIT_REDGE | BIT_GCLK_MODE | 295*9f257f50SLaurent Pinchart BIT_HSYNC_POL | BIT_FCC | BIT_MCLKDIV(1) | 296*9f257f50SLaurent Pinchart BIT_MCLKEN, CSI_CSICR1); 297*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, 0, CSI_CSICR2); 298*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, BIT_FRMCNT_RST, CSI_CSICR3); 299*9f257f50SLaurent Pinchart 300*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, BIT_IMAGE_WIDTH(IMX7_CSI_DEF_PIX_WIDTH) | 301*9f257f50SLaurent Pinchart BIT_IMAGE_HEIGHT(IMX7_CSI_DEF_PIX_HEIGHT), 302*9f257f50SLaurent Pinchart CSI_CSIIMAG_PARA); 303*9f257f50SLaurent Pinchart 304*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, BIT_DMA_REFLASH_RFF, CSI_CSICR3); 305*9f257f50SLaurent Pinchart } 306*9f257f50SLaurent Pinchart 307*9f257f50SLaurent Pinchart static void imx7_csi_hw_enable_irq(struct imx7_csi *csi) 308*9f257f50SLaurent Pinchart { 309*9f257f50SLaurent Pinchart u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); 310*9f257f50SLaurent Pinchart 311*9f257f50SLaurent Pinchart cr1 |= BIT_RFF_OR_INT; 312*9f257f50SLaurent Pinchart cr1 |= BIT_FB1_DMA_DONE_INTEN; 313*9f257f50SLaurent Pinchart cr1 |= BIT_FB2_DMA_DONE_INTEN; 314*9f257f50SLaurent Pinchart 315*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr1, CSI_CSICR1); 316*9f257f50SLaurent Pinchart } 317*9f257f50SLaurent Pinchart 318*9f257f50SLaurent Pinchart static void imx7_csi_hw_disable_irq(struct imx7_csi *csi) 319*9f257f50SLaurent Pinchart { 320*9f257f50SLaurent Pinchart u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); 321*9f257f50SLaurent Pinchart 322*9f257f50SLaurent Pinchart cr1 &= ~BIT_RFF_OR_INT; 323*9f257f50SLaurent Pinchart cr1 &= ~BIT_FB1_DMA_DONE_INTEN; 324*9f257f50SLaurent Pinchart cr1 &= ~BIT_FB2_DMA_DONE_INTEN; 325*9f257f50SLaurent Pinchart 326*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr1, CSI_CSICR1); 327*9f257f50SLaurent Pinchart } 328*9f257f50SLaurent Pinchart 329*9f257f50SLaurent Pinchart static void imx7_csi_hw_enable(struct imx7_csi *csi) 330*9f257f50SLaurent Pinchart { 331*9f257f50SLaurent Pinchart u32 cr = imx7_csi_reg_read(csi, CSI_CSICR18); 332*9f257f50SLaurent Pinchart 333*9f257f50SLaurent Pinchart cr |= BIT_CSI_HW_ENABLE; 334*9f257f50SLaurent Pinchart 335*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr, CSI_CSICR18); 336*9f257f50SLaurent Pinchart } 337*9f257f50SLaurent Pinchart 338*9f257f50SLaurent Pinchart static void imx7_csi_hw_disable(struct imx7_csi *csi) 339*9f257f50SLaurent Pinchart { 340*9f257f50SLaurent Pinchart u32 cr = imx7_csi_reg_read(csi, CSI_CSICR18); 341*9f257f50SLaurent Pinchart 342*9f257f50SLaurent Pinchart cr &= ~BIT_CSI_HW_ENABLE; 343*9f257f50SLaurent Pinchart 344*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr, CSI_CSICR18); 345*9f257f50SLaurent Pinchart } 346*9f257f50SLaurent Pinchart 347*9f257f50SLaurent Pinchart static void imx7_csi_dma_reflash(struct imx7_csi *csi) 348*9f257f50SLaurent Pinchart { 349*9f257f50SLaurent Pinchart u32 cr3; 350*9f257f50SLaurent Pinchart 351*9f257f50SLaurent Pinchart cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); 352*9f257f50SLaurent Pinchart cr3 |= BIT_DMA_REFLASH_RFF; 353*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr3, CSI_CSICR3); 354*9f257f50SLaurent Pinchart } 355*9f257f50SLaurent Pinchart 356*9f257f50SLaurent Pinchart static void imx7_csi_rx_fifo_clear(struct imx7_csi *csi) 357*9f257f50SLaurent Pinchart { 358*9f257f50SLaurent Pinchart u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1) & ~BIT_FCC; 359*9f257f50SLaurent Pinchart 360*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr1, CSI_CSICR1); 361*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr1 | BIT_CLR_RXFIFO, CSI_CSICR1); 362*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr1 | BIT_FCC, CSI_CSICR1); 363*9f257f50SLaurent Pinchart } 364*9f257f50SLaurent Pinchart 365*9f257f50SLaurent Pinchart static void imx7_csi_dmareq_rff_enable(struct imx7_csi *csi) 366*9f257f50SLaurent Pinchart { 367*9f257f50SLaurent Pinchart u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); 368*9f257f50SLaurent Pinchart 369*9f257f50SLaurent Pinchart cr3 |= BIT_DMA_REQ_EN_RFF; 370*9f257f50SLaurent Pinchart cr3 |= BIT_HRESP_ERR_EN; 371*9f257f50SLaurent Pinchart cr3 &= ~BIT_RXFF_LEVEL_MASK; 372*9f257f50SLaurent Pinchart cr3 |= BIT_RXFF_LEVEL(2); 373*9f257f50SLaurent Pinchart 374*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr3, CSI_CSICR3); 375*9f257f50SLaurent Pinchart } 376*9f257f50SLaurent Pinchart 377*9f257f50SLaurent Pinchart static void imx7_csi_dmareq_rff_disable(struct imx7_csi *csi) 378*9f257f50SLaurent Pinchart { 379*9f257f50SLaurent Pinchart u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); 380*9f257f50SLaurent Pinchart 381*9f257f50SLaurent Pinchart cr3 &= ~BIT_DMA_REQ_EN_RFF; 382*9f257f50SLaurent Pinchart cr3 &= ~BIT_HRESP_ERR_EN; 383*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr3, CSI_CSICR3); 384*9f257f50SLaurent Pinchart } 385*9f257f50SLaurent Pinchart 386*9f257f50SLaurent Pinchart static void imx7_csi_update_buf(struct imx7_csi *csi, dma_addr_t dma_addr, 387*9f257f50SLaurent Pinchart int buf_num) 388*9f257f50SLaurent Pinchart { 389*9f257f50SLaurent Pinchart if (buf_num == 1) 390*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, dma_addr, CSI_CSIDMASA_FB2); 391*9f257f50SLaurent Pinchart else 392*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, dma_addr, CSI_CSIDMASA_FB1); 393*9f257f50SLaurent Pinchart } 394*9f257f50SLaurent Pinchart 395*9f257f50SLaurent Pinchart static struct imx7_csi_vb2_buffer *imx7_csi_video_next_buf(struct imx7_csi *csi); 396*9f257f50SLaurent Pinchart 397*9f257f50SLaurent Pinchart static void imx7_csi_setup_vb2_buf(struct imx7_csi *csi) 398*9f257f50SLaurent Pinchart { 399*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer *buf; 400*9f257f50SLaurent Pinchart struct vb2_buffer *vb2_buf; 401*9f257f50SLaurent Pinchart int i; 402*9f257f50SLaurent Pinchart 403*9f257f50SLaurent Pinchart for (i = 0; i < 2; i++) { 404*9f257f50SLaurent Pinchart dma_addr_t dma_addr; 405*9f257f50SLaurent Pinchart 406*9f257f50SLaurent Pinchart buf = imx7_csi_video_next_buf(csi); 407*9f257f50SLaurent Pinchart if (buf) { 408*9f257f50SLaurent Pinchart csi->active_vb2_buf[i] = buf; 409*9f257f50SLaurent Pinchart vb2_buf = &buf->vbuf.vb2_buf; 410*9f257f50SLaurent Pinchart dma_addr = vb2_dma_contig_plane_dma_addr(vb2_buf, 0); 411*9f257f50SLaurent Pinchart } else { 412*9f257f50SLaurent Pinchart csi->active_vb2_buf[i] = NULL; 413*9f257f50SLaurent Pinchart dma_addr = csi->underrun_buf.dma_addr; 414*9f257f50SLaurent Pinchart } 415*9f257f50SLaurent Pinchart 416*9f257f50SLaurent Pinchart imx7_csi_update_buf(csi, dma_addr, i); 417*9f257f50SLaurent Pinchart } 418*9f257f50SLaurent Pinchart } 419*9f257f50SLaurent Pinchart 420*9f257f50SLaurent Pinchart static void imx7_csi_dma_unsetup_vb2_buf(struct imx7_csi *csi, 421*9f257f50SLaurent Pinchart enum vb2_buffer_state return_status) 422*9f257f50SLaurent Pinchart { 423*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer *buf; 424*9f257f50SLaurent Pinchart int i; 425*9f257f50SLaurent Pinchart 426*9f257f50SLaurent Pinchart /* return any remaining active frames with return_status */ 427*9f257f50SLaurent Pinchart for (i = 0; i < 2; i++) { 428*9f257f50SLaurent Pinchart buf = csi->active_vb2_buf[i]; 429*9f257f50SLaurent Pinchart if (buf) { 430*9f257f50SLaurent Pinchart struct vb2_buffer *vb = &buf->vbuf.vb2_buf; 431*9f257f50SLaurent Pinchart 432*9f257f50SLaurent Pinchart vb->timestamp = ktime_get_ns(); 433*9f257f50SLaurent Pinchart vb2_buffer_done(vb, return_status); 434*9f257f50SLaurent Pinchart csi->active_vb2_buf[i] = NULL; 435*9f257f50SLaurent Pinchart } 436*9f257f50SLaurent Pinchart } 437*9f257f50SLaurent Pinchart } 438*9f257f50SLaurent Pinchart 439*9f257f50SLaurent Pinchart static void imx7_csi_free_dma_buf(struct imx7_csi *csi, 440*9f257f50SLaurent Pinchart struct imx7_csi_dma_buf *buf) 441*9f257f50SLaurent Pinchart { 442*9f257f50SLaurent Pinchart if (buf->virt) 443*9f257f50SLaurent Pinchart dma_free_coherent(csi->dev, buf->len, buf->virt, buf->dma_addr); 444*9f257f50SLaurent Pinchart 445*9f257f50SLaurent Pinchart buf->virt = NULL; 446*9f257f50SLaurent Pinchart buf->dma_addr = 0; 447*9f257f50SLaurent Pinchart } 448*9f257f50SLaurent Pinchart 449*9f257f50SLaurent Pinchart static int imx7_csi_alloc_dma_buf(struct imx7_csi *csi, 450*9f257f50SLaurent Pinchart struct imx7_csi_dma_buf *buf, int size) 451*9f257f50SLaurent Pinchart { 452*9f257f50SLaurent Pinchart imx7_csi_free_dma_buf(csi, buf); 453*9f257f50SLaurent Pinchart 454*9f257f50SLaurent Pinchart buf->len = PAGE_ALIGN(size); 455*9f257f50SLaurent Pinchart buf->virt = dma_alloc_coherent(csi->dev, buf->len, &buf->dma_addr, 456*9f257f50SLaurent Pinchart GFP_DMA | GFP_KERNEL); 457*9f257f50SLaurent Pinchart if (!buf->virt) 458*9f257f50SLaurent Pinchart return -ENOMEM; 459*9f257f50SLaurent Pinchart 460*9f257f50SLaurent Pinchart return 0; 461*9f257f50SLaurent Pinchart } 462*9f257f50SLaurent Pinchart 463*9f257f50SLaurent Pinchart static int imx7_csi_dma_setup(struct imx7_csi *csi) 464*9f257f50SLaurent Pinchart { 465*9f257f50SLaurent Pinchart int ret; 466*9f257f50SLaurent Pinchart 467*9f257f50SLaurent Pinchart ret = imx7_csi_alloc_dma_buf(csi, &csi->underrun_buf, 468*9f257f50SLaurent Pinchart csi->vdev_fmt.sizeimage); 469*9f257f50SLaurent Pinchart if (ret < 0) { 470*9f257f50SLaurent Pinchart v4l2_warn(&csi->sd, "consider increasing the CMA area\n"); 471*9f257f50SLaurent Pinchart return ret; 472*9f257f50SLaurent Pinchart } 473*9f257f50SLaurent Pinchart 474*9f257f50SLaurent Pinchart csi->frame_sequence = 0; 475*9f257f50SLaurent Pinchart csi->last_eof = false; 476*9f257f50SLaurent Pinchart init_completion(&csi->last_eof_completion); 477*9f257f50SLaurent Pinchart 478*9f257f50SLaurent Pinchart imx7_csi_setup_vb2_buf(csi); 479*9f257f50SLaurent Pinchart 480*9f257f50SLaurent Pinchart return 0; 481*9f257f50SLaurent Pinchart } 482*9f257f50SLaurent Pinchart 483*9f257f50SLaurent Pinchart static void imx7_csi_dma_cleanup(struct imx7_csi *csi, 484*9f257f50SLaurent Pinchart enum vb2_buffer_state return_status) 485*9f257f50SLaurent Pinchart { 486*9f257f50SLaurent Pinchart imx7_csi_dma_unsetup_vb2_buf(csi, return_status); 487*9f257f50SLaurent Pinchart imx7_csi_free_dma_buf(csi, &csi->underrun_buf); 488*9f257f50SLaurent Pinchart } 489*9f257f50SLaurent Pinchart 490*9f257f50SLaurent Pinchart static void imx7_csi_dma_stop(struct imx7_csi *csi) 491*9f257f50SLaurent Pinchart { 492*9f257f50SLaurent Pinchart unsigned long timeout_jiffies; 493*9f257f50SLaurent Pinchart unsigned long flags; 494*9f257f50SLaurent Pinchart int ret; 495*9f257f50SLaurent Pinchart 496*9f257f50SLaurent Pinchart /* mark next EOF interrupt as the last before stream off */ 497*9f257f50SLaurent Pinchart spin_lock_irqsave(&csi->irqlock, flags); 498*9f257f50SLaurent Pinchart csi->last_eof = true; 499*9f257f50SLaurent Pinchart spin_unlock_irqrestore(&csi->irqlock, flags); 500*9f257f50SLaurent Pinchart 501*9f257f50SLaurent Pinchart /* 502*9f257f50SLaurent Pinchart * and then wait for interrupt handler to mark completion. 503*9f257f50SLaurent Pinchart */ 504*9f257f50SLaurent Pinchart timeout_jiffies = msecs_to_jiffies(IMX7_CSI_VIDEO_EOF_TIMEOUT); 505*9f257f50SLaurent Pinchart ret = wait_for_completion_timeout(&csi->last_eof_completion, 506*9f257f50SLaurent Pinchart timeout_jiffies); 507*9f257f50SLaurent Pinchart if (ret == 0) 508*9f257f50SLaurent Pinchart v4l2_warn(&csi->sd, "wait last EOF timeout\n"); 509*9f257f50SLaurent Pinchart 510*9f257f50SLaurent Pinchart imx7_csi_hw_disable_irq(csi); 511*9f257f50SLaurent Pinchart } 512*9f257f50SLaurent Pinchart 513*9f257f50SLaurent Pinchart static void imx7_csi_configure(struct imx7_csi *csi) 514*9f257f50SLaurent Pinchart { 515*9f257f50SLaurent Pinchart struct v4l2_pix_format *out_pix = &csi->vdev_fmt; 516*9f257f50SLaurent Pinchart int width = out_pix->width; 517*9f257f50SLaurent Pinchart u32 stride = 0; 518*9f257f50SLaurent Pinchart u32 cr3 = BIT_FRMCNT_RST; 519*9f257f50SLaurent Pinchart u32 cr1, cr18; 520*9f257f50SLaurent Pinchart 521*9f257f50SLaurent Pinchart cr18 = imx7_csi_reg_read(csi, CSI_CSICR18); 522*9f257f50SLaurent Pinchart 523*9f257f50SLaurent Pinchart cr18 &= ~(BIT_CSI_HW_ENABLE | BIT_MIPI_DATA_FORMAT_MASK | 524*9f257f50SLaurent Pinchart BIT_DATA_FROM_MIPI | BIT_MIPI_DOUBLE_CMPNT | 525*9f257f50SLaurent Pinchart BIT_BASEADDR_CHG_ERR_EN | BIT_BASEADDR_SWITCH_SEL | 526*9f257f50SLaurent Pinchart BIT_BASEADDR_SWITCH_EN | BIT_DEINTERLACE_EN); 527*9f257f50SLaurent Pinchart 528*9f257f50SLaurent Pinchart if (out_pix->field == V4L2_FIELD_INTERLACED) { 529*9f257f50SLaurent Pinchart cr18 |= BIT_DEINTERLACE_EN; 530*9f257f50SLaurent Pinchart stride = out_pix->width; 531*9f257f50SLaurent Pinchart } 532*9f257f50SLaurent Pinchart 533*9f257f50SLaurent Pinchart if (!csi->is_csi2) { 534*9f257f50SLaurent Pinchart cr1 = BIT_SOF_POL | BIT_REDGE | BIT_GCLK_MODE | BIT_HSYNC_POL 535*9f257f50SLaurent Pinchart | BIT_FCC | BIT_MCLKDIV(1) | BIT_MCLKEN; 536*9f257f50SLaurent Pinchart 537*9f257f50SLaurent Pinchart cr18 |= BIT_BASEADDR_SWITCH_EN | BIT_BASEADDR_SWITCH_SEL | 538*9f257f50SLaurent Pinchart BIT_BASEADDR_CHG_ERR_EN; 539*9f257f50SLaurent Pinchart 540*9f257f50SLaurent Pinchart if (out_pix->pixelformat == V4L2_PIX_FMT_UYVY || 541*9f257f50SLaurent Pinchart out_pix->pixelformat == V4L2_PIX_FMT_YUYV) 542*9f257f50SLaurent Pinchart width *= 2; 543*9f257f50SLaurent Pinchart } else { 544*9f257f50SLaurent Pinchart cr1 = BIT_SOF_POL | BIT_REDGE | BIT_HSYNC_POL | BIT_FCC 545*9f257f50SLaurent Pinchart | BIT_MCLKDIV(1) | BIT_MCLKEN; 546*9f257f50SLaurent Pinchart 547*9f257f50SLaurent Pinchart cr18 |= BIT_DATA_FROM_MIPI; 548*9f257f50SLaurent Pinchart 549*9f257f50SLaurent Pinchart switch (csi->format_mbus[IMX7_CSI_PAD_SINK].code) { 550*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_Y8_1X8: 551*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SBGGR8_1X8: 552*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SGBRG8_1X8: 553*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SGRBG8_1X8: 554*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SRGGB8_1X8: 555*9f257f50SLaurent Pinchart cr18 |= BIT_MIPI_DATA_FORMAT_RAW8; 556*9f257f50SLaurent Pinchart break; 557*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_Y10_1X10: 558*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SBGGR10_1X10: 559*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SGBRG10_1X10: 560*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SGRBG10_1X10: 561*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SRGGB10_1X10: 562*9f257f50SLaurent Pinchart cr3 |= BIT_TWO_8BIT_SENSOR; 563*9f257f50SLaurent Pinchart cr18 |= BIT_MIPI_DATA_FORMAT_RAW10; 564*9f257f50SLaurent Pinchart break; 565*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_Y12_1X12: 566*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SBGGR12_1X12: 567*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SGBRG12_1X12: 568*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SGRBG12_1X12: 569*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SRGGB12_1X12: 570*9f257f50SLaurent Pinchart cr3 |= BIT_TWO_8BIT_SENSOR; 571*9f257f50SLaurent Pinchart cr18 |= BIT_MIPI_DATA_FORMAT_RAW12; 572*9f257f50SLaurent Pinchart break; 573*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_Y14_1X14: 574*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SBGGR14_1X14: 575*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SGBRG14_1X14: 576*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SGRBG14_1X14: 577*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_SRGGB14_1X14: 578*9f257f50SLaurent Pinchart cr3 |= BIT_TWO_8BIT_SENSOR; 579*9f257f50SLaurent Pinchart cr18 |= BIT_MIPI_DATA_FORMAT_RAW14; 580*9f257f50SLaurent Pinchart break; 581*9f257f50SLaurent Pinchart 582*9f257f50SLaurent Pinchart /* 583*9f257f50SLaurent Pinchart * The CSI bridge has a 16-bit input bus. Depending on the 584*9f257f50SLaurent Pinchart * connected source, data may be transmitted with 8 or 10 bits 585*9f257f50SLaurent Pinchart * per clock sample (in bits [9:2] or [9:0] respectively) or 586*9f257f50SLaurent Pinchart * with 16 bits per clock sample (in bits [15:0]). The data is 587*9f257f50SLaurent Pinchart * then packed into a 32-bit FIFO (as shown in figure 13-11 of 588*9f257f50SLaurent Pinchart * the i.MX8MM reference manual rev. 3). 589*9f257f50SLaurent Pinchart * 590*9f257f50SLaurent Pinchart * The data packing in a 32-bit FIFO input word is controlled by 591*9f257f50SLaurent Pinchart * the CR3 TWO_8BIT_SENSOR field (also known as SENSOR_16BITS in 592*9f257f50SLaurent Pinchart * the i.MX8MM reference manual). When set to 0, data packing 593*9f257f50SLaurent Pinchart * groups four 8-bit input samples (bits [9:2]). When set to 1, 594*9f257f50SLaurent Pinchart * data packing groups two 16-bit input samples (bits [15:0]). 595*9f257f50SLaurent Pinchart * 596*9f257f50SLaurent Pinchart * The register field CR18 MIPI_DOUBLE_CMPNT also needs to be 597*9f257f50SLaurent Pinchart * configured according to the input format for YUV 4:2:2 data. 598*9f257f50SLaurent Pinchart * The field controls the gasket between the CSI-2 receiver and 599*9f257f50SLaurent Pinchart * the CSI bridge. On i.MX7 and i.MX8MM, the field must be set 600*9f257f50SLaurent Pinchart * to 1 when the CSIS outputs 16-bit samples. On i.MX8MQ, the 601*9f257f50SLaurent Pinchart * gasket ignores the MIPI_DOUBLE_CMPNT bit and YUV 4:2:2 always 602*9f257f50SLaurent Pinchart * uses 16-bit samples. Setting MIPI_DOUBLE_CMPNT in that case 603*9f257f50SLaurent Pinchart * has no effect, but doesn't cause any issue. 604*9f257f50SLaurent Pinchart */ 605*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_UYVY8_2X8: 606*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_YUYV8_2X8: 607*9f257f50SLaurent Pinchart cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B; 608*9f257f50SLaurent Pinchart break; 609*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_UYVY8_1X16: 610*9f257f50SLaurent Pinchart case MEDIA_BUS_FMT_YUYV8_1X16: 611*9f257f50SLaurent Pinchart cr3 |= BIT_TWO_8BIT_SENSOR; 612*9f257f50SLaurent Pinchart cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B | 613*9f257f50SLaurent Pinchart BIT_MIPI_DOUBLE_CMPNT; 614*9f257f50SLaurent Pinchart break; 615*9f257f50SLaurent Pinchart } 616*9f257f50SLaurent Pinchart } 617*9f257f50SLaurent Pinchart 618*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr1, CSI_CSICR1); 619*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, BIT_DMA_BURST_TYPE_RFF_INCR16, CSI_CSICR2); 620*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr3, CSI_CSICR3); 621*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr18, CSI_CSICR18); 622*9f257f50SLaurent Pinchart 623*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, (width * out_pix->height) >> 2, CSI_CSIRXCNT); 624*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, BIT_IMAGE_WIDTH(width) | 625*9f257f50SLaurent Pinchart BIT_IMAGE_HEIGHT(out_pix->height), 626*9f257f50SLaurent Pinchart CSI_CSIIMAG_PARA); 627*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, stride, CSI_CSIFBUF_PARA); 628*9f257f50SLaurent Pinchart } 629*9f257f50SLaurent Pinchart 630*9f257f50SLaurent Pinchart static int imx7_csi_init(struct imx7_csi *csi) 631*9f257f50SLaurent Pinchart { 632*9f257f50SLaurent Pinchart int ret; 633*9f257f50SLaurent Pinchart 634*9f257f50SLaurent Pinchart ret = clk_prepare_enable(csi->mclk); 635*9f257f50SLaurent Pinchart if (ret < 0) 636*9f257f50SLaurent Pinchart return ret; 637*9f257f50SLaurent Pinchart 638*9f257f50SLaurent Pinchart imx7_csi_configure(csi); 639*9f257f50SLaurent Pinchart 640*9f257f50SLaurent Pinchart ret = imx7_csi_dma_setup(csi); 641*9f257f50SLaurent Pinchart if (ret < 0) 642*9f257f50SLaurent Pinchart return ret; 643*9f257f50SLaurent Pinchart 644*9f257f50SLaurent Pinchart return 0; 645*9f257f50SLaurent Pinchart } 646*9f257f50SLaurent Pinchart 647*9f257f50SLaurent Pinchart static void imx7_csi_deinit(struct imx7_csi *csi, 648*9f257f50SLaurent Pinchart enum vb2_buffer_state return_status) 649*9f257f50SLaurent Pinchart { 650*9f257f50SLaurent Pinchart imx7_csi_dma_cleanup(csi, return_status); 651*9f257f50SLaurent Pinchart imx7_csi_init_default(csi); 652*9f257f50SLaurent Pinchart imx7_csi_dmareq_rff_disable(csi); 653*9f257f50SLaurent Pinchart clk_disable_unprepare(csi->mclk); 654*9f257f50SLaurent Pinchart } 655*9f257f50SLaurent Pinchart 656*9f257f50SLaurent Pinchart static void imx7_csi_baseaddr_switch_on_second_frame(struct imx7_csi *csi) 657*9f257f50SLaurent Pinchart { 658*9f257f50SLaurent Pinchart u32 cr18 = imx7_csi_reg_read(csi, CSI_CSICR18); 659*9f257f50SLaurent Pinchart 660*9f257f50SLaurent Pinchart cr18 |= BIT_BASEADDR_SWITCH_EN | BIT_BASEADDR_SWITCH_SEL | 661*9f257f50SLaurent Pinchart BIT_BASEADDR_CHG_ERR_EN; 662*9f257f50SLaurent Pinchart cr18 |= BIT_MASK_OPTION_SECOND_FRAME; 663*9f257f50SLaurent Pinchart imx7_csi_reg_write(csi, cr18, CSI_CSICR18); 664*9f257f50SLaurent Pinchart } 665*9f257f50SLaurent Pinchart 666*9f257f50SLaurent Pinchart static void imx7_csi_enable(struct imx7_csi *csi) 667*9f257f50SLaurent Pinchart { 668*9f257f50SLaurent Pinchart /* Clear the Rx FIFO and reflash the DMA controller. */ 669*9f257f50SLaurent Pinchart imx7_csi_rx_fifo_clear(csi); 670*9f257f50SLaurent Pinchart imx7_csi_dma_reflash(csi); 671*9f257f50SLaurent Pinchart 672*9f257f50SLaurent Pinchart usleep_range(2000, 3000); 673*9f257f50SLaurent Pinchart 674*9f257f50SLaurent Pinchart /* Clear and enable the interrupts. */ 675*9f257f50SLaurent Pinchart imx7_csi_irq_clear(csi); 676*9f257f50SLaurent Pinchart imx7_csi_hw_enable_irq(csi); 677*9f257f50SLaurent Pinchart 678*9f257f50SLaurent Pinchart /* Enable the RxFIFO DMA and the CSI. */ 679*9f257f50SLaurent Pinchart imx7_csi_dmareq_rff_enable(csi); 680*9f257f50SLaurent Pinchart imx7_csi_hw_enable(csi); 681*9f257f50SLaurent Pinchart 682*9f257f50SLaurent Pinchart if (csi->model == IMX7_CSI_IMX8MQ) 683*9f257f50SLaurent Pinchart imx7_csi_baseaddr_switch_on_second_frame(csi); 684*9f257f50SLaurent Pinchart } 685*9f257f50SLaurent Pinchart 686*9f257f50SLaurent Pinchart static void imx7_csi_disable(struct imx7_csi *csi) 687*9f257f50SLaurent Pinchart { 688*9f257f50SLaurent Pinchart imx7_csi_dma_stop(csi); 689*9f257f50SLaurent Pinchart 690*9f257f50SLaurent Pinchart imx7_csi_dmareq_rff_disable(csi); 691*9f257f50SLaurent Pinchart 692*9f257f50SLaurent Pinchart imx7_csi_hw_disable_irq(csi); 693*9f257f50SLaurent Pinchart 694*9f257f50SLaurent Pinchart imx7_csi_hw_disable(csi); 695*9f257f50SLaurent Pinchart } 696*9f257f50SLaurent Pinchart 697*9f257f50SLaurent Pinchart /* ----------------------------------------------------------------------------- 698*9f257f50SLaurent Pinchart * Interrupt Handling 699*9f257f50SLaurent Pinchart */ 700*9f257f50SLaurent Pinchart 701*9f257f50SLaurent Pinchart static void imx7_csi_error_recovery(struct imx7_csi *csi) 702*9f257f50SLaurent Pinchart { 703*9f257f50SLaurent Pinchart imx7_csi_hw_disable(csi); 704*9f257f50SLaurent Pinchart 705*9f257f50SLaurent Pinchart imx7_csi_rx_fifo_clear(csi); 706*9f257f50SLaurent Pinchart 707*9f257f50SLaurent Pinchart imx7_csi_dma_reflash(csi); 708*9f257f50SLaurent Pinchart 709*9f257f50SLaurent Pinchart imx7_csi_hw_enable(csi); 710*9f257f50SLaurent Pinchart } 711*9f257f50SLaurent Pinchart 712*9f257f50SLaurent Pinchart static void imx7_csi_vb2_buf_done(struct imx7_csi *csi) 713*9f257f50SLaurent Pinchart { 714*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer *done, *next; 715*9f257f50SLaurent Pinchart struct vb2_buffer *vb; 716*9f257f50SLaurent Pinchart dma_addr_t dma_addr; 717*9f257f50SLaurent Pinchart 718*9f257f50SLaurent Pinchart done = csi->active_vb2_buf[csi->buf_num]; 719*9f257f50SLaurent Pinchart if (done) { 720*9f257f50SLaurent Pinchart done->vbuf.field = csi->vdev_fmt.field; 721*9f257f50SLaurent Pinchart done->vbuf.sequence = csi->frame_sequence; 722*9f257f50SLaurent Pinchart vb = &done->vbuf.vb2_buf; 723*9f257f50SLaurent Pinchart vb->timestamp = ktime_get_ns(); 724*9f257f50SLaurent Pinchart vb2_buffer_done(vb, VB2_BUF_STATE_DONE); 725*9f257f50SLaurent Pinchart } 726*9f257f50SLaurent Pinchart csi->frame_sequence++; 727*9f257f50SLaurent Pinchart 728*9f257f50SLaurent Pinchart /* get next queued buffer */ 729*9f257f50SLaurent Pinchart next = imx7_csi_video_next_buf(csi); 730*9f257f50SLaurent Pinchart if (next) { 731*9f257f50SLaurent Pinchart dma_addr = vb2_dma_contig_plane_dma_addr(&next->vbuf.vb2_buf, 0); 732*9f257f50SLaurent Pinchart csi->active_vb2_buf[csi->buf_num] = next; 733*9f257f50SLaurent Pinchart } else { 734*9f257f50SLaurent Pinchart dma_addr = csi->underrun_buf.dma_addr; 735*9f257f50SLaurent Pinchart csi->active_vb2_buf[csi->buf_num] = NULL; 736*9f257f50SLaurent Pinchart } 737*9f257f50SLaurent Pinchart 738*9f257f50SLaurent Pinchart imx7_csi_update_buf(csi, dma_addr, csi->buf_num); 739*9f257f50SLaurent Pinchart } 740*9f257f50SLaurent Pinchart 741*9f257f50SLaurent Pinchart static irqreturn_t imx7_csi_irq_handler(int irq, void *data) 742*9f257f50SLaurent Pinchart { 743*9f257f50SLaurent Pinchart struct imx7_csi *csi = data; 744*9f257f50SLaurent Pinchart u32 status; 745*9f257f50SLaurent Pinchart 746*9f257f50SLaurent Pinchart spin_lock(&csi->irqlock); 747*9f257f50SLaurent Pinchart 748*9f257f50SLaurent Pinchart status = imx7_csi_irq_clear(csi); 749*9f257f50SLaurent Pinchart 750*9f257f50SLaurent Pinchart if (status & BIT_RFF_OR_INT) { 751*9f257f50SLaurent Pinchart dev_warn(csi->dev, "Rx fifo overflow\n"); 752*9f257f50SLaurent Pinchart imx7_csi_error_recovery(csi); 753*9f257f50SLaurent Pinchart } 754*9f257f50SLaurent Pinchart 755*9f257f50SLaurent Pinchart if (status & BIT_HRESP_ERR_INT) { 756*9f257f50SLaurent Pinchart dev_warn(csi->dev, "Hresponse error detected\n"); 757*9f257f50SLaurent Pinchart imx7_csi_error_recovery(csi); 758*9f257f50SLaurent Pinchart } 759*9f257f50SLaurent Pinchart 760*9f257f50SLaurent Pinchart if (status & BIT_ADDR_CH_ERR_INT) { 761*9f257f50SLaurent Pinchart imx7_csi_hw_disable(csi); 762*9f257f50SLaurent Pinchart 763*9f257f50SLaurent Pinchart imx7_csi_dma_reflash(csi); 764*9f257f50SLaurent Pinchart 765*9f257f50SLaurent Pinchart imx7_csi_hw_enable(csi); 766*9f257f50SLaurent Pinchart } 767*9f257f50SLaurent Pinchart 768*9f257f50SLaurent Pinchart if ((status & BIT_DMA_TSF_DONE_FB1) && 769*9f257f50SLaurent Pinchart (status & BIT_DMA_TSF_DONE_FB2)) { 770*9f257f50SLaurent Pinchart /* 771*9f257f50SLaurent Pinchart * For both FB1 and FB2 interrupter bits set case, 772*9f257f50SLaurent Pinchart * CSI DMA is work in one of FB1 and FB2 buffer, 773*9f257f50SLaurent Pinchart * but software can not know the state. 774*9f257f50SLaurent Pinchart * Skip it to avoid base address updated 775*9f257f50SLaurent Pinchart * when csi work in field0 and field1 will write to 776*9f257f50SLaurent Pinchart * new base address. 777*9f257f50SLaurent Pinchart */ 778*9f257f50SLaurent Pinchart } else if (status & BIT_DMA_TSF_DONE_FB1) { 779*9f257f50SLaurent Pinchart csi->buf_num = 0; 780*9f257f50SLaurent Pinchart } else if (status & BIT_DMA_TSF_DONE_FB2) { 781*9f257f50SLaurent Pinchart csi->buf_num = 1; 782*9f257f50SLaurent Pinchart } 783*9f257f50SLaurent Pinchart 784*9f257f50SLaurent Pinchart if ((status & BIT_DMA_TSF_DONE_FB1) || 785*9f257f50SLaurent Pinchart (status & BIT_DMA_TSF_DONE_FB2)) { 786*9f257f50SLaurent Pinchart imx7_csi_vb2_buf_done(csi); 787*9f257f50SLaurent Pinchart 788*9f257f50SLaurent Pinchart if (csi->last_eof) { 789*9f257f50SLaurent Pinchart complete(&csi->last_eof_completion); 790*9f257f50SLaurent Pinchart csi->last_eof = false; 791*9f257f50SLaurent Pinchart } 792*9f257f50SLaurent Pinchart } 793*9f257f50SLaurent Pinchart 794*9f257f50SLaurent Pinchart spin_unlock(&csi->irqlock); 795*9f257f50SLaurent Pinchart 796*9f257f50SLaurent Pinchart return IRQ_HANDLED; 797*9f257f50SLaurent Pinchart } 798*9f257f50SLaurent Pinchart 799*9f257f50SLaurent Pinchart /* ----------------------------------------------------------------------------- 800*9f257f50SLaurent Pinchart * Format Helpers 801*9f257f50SLaurent Pinchart */ 802*9f257f50SLaurent Pinchart 803*9f257f50SLaurent Pinchart #define IMX_BUS_FMTS(fmt...) (const u32[]) {fmt, 0} 804*9f257f50SLaurent Pinchart 805*9f257f50SLaurent Pinchart /* 806*9f257f50SLaurent Pinchart * List of supported pixel formats for the subdevs. Keep V4L2_PIX_FMT_UYVY and 807*9f257f50SLaurent Pinchart * MEDIA_BUS_FMT_UYVY8_2X8 first to match IMX7_CSI_DEF_PIX_FORMAT and 808*9f257f50SLaurent Pinchart * IMX7_CSI_DEF_MBUS_CODE. 809*9f257f50SLaurent Pinchart * 810*9f257f50SLaurent Pinchart * TODO: Restrict the supported formats list based on the SoC integration. 811*9f257f50SLaurent Pinchart * 812*9f257f50SLaurent Pinchart * The CSI bridge can be configured to sample pixel components from the Rx queue 813*9f257f50SLaurent Pinchart * in single (8bpp) or double (16bpp) component modes. Image format variants 814*9f257f50SLaurent Pinchart * with different sample sizes (ie YUYV_2X8 vs YUYV_1X16) determine the pixel 815*9f257f50SLaurent Pinchart * components sampling size per each clock cycle and their packing mode (see 816*9f257f50SLaurent Pinchart * imx7_csi_configure() for details). 817*9f257f50SLaurent Pinchart * 818*9f257f50SLaurent Pinchart * As the CSI bridge can be interfaced with different IP blocks depending on the 819*9f257f50SLaurent Pinchart * SoC model it is integrated on, the Rx queue sampling size should match the 820*9f257f50SLaurent Pinchart * size of the samples transferred by the transmitting IP block. To avoid 821*9f257f50SLaurent Pinchart * misconfigurations of the capture pipeline, the enumeration of the supported 822*9f257f50SLaurent Pinchart * formats should be restricted to match the pixel source transmitting mode. 823*9f257f50SLaurent Pinchart * 824*9f257f50SLaurent Pinchart * Example: i.MX8MM SoC integrates the CSI bridge with the Samsung CSIS CSI-2 825*9f257f50SLaurent Pinchart * receiver which operates in dual pixel sampling mode. The CSI bridge should 826*9f257f50SLaurent Pinchart * only expose the 1X16 formats variant which instructs it to operate in dual 827*9f257f50SLaurent Pinchart * pixel sampling mode. When the CSI bridge is instead integrated on an i.MX7, 828*9f257f50SLaurent Pinchart * which supports both serial and parallel input, it should expose both 829*9f257f50SLaurent Pinchart * variants. 830*9f257f50SLaurent Pinchart * 831*9f257f50SLaurent Pinchart * This currently only applies to YUYV formats, but other formats might need to 832*9f257f50SLaurent Pinchart * be handled in the same way. 833*9f257f50SLaurent Pinchart */ 834*9f257f50SLaurent Pinchart static const struct imx7_csi_pixfmt pixel_formats[] = { 835*9f257f50SLaurent Pinchart /*** YUV formats start here ***/ 836*9f257f50SLaurent Pinchart { 837*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_UYVY, 838*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS( 839*9f257f50SLaurent Pinchart MEDIA_BUS_FMT_UYVY8_2X8, 840*9f257f50SLaurent Pinchart MEDIA_BUS_FMT_UYVY8_1X16 841*9f257f50SLaurent Pinchart ), 842*9f257f50SLaurent Pinchart .yuv = true, 843*9f257f50SLaurent Pinchart .bpp = 16, 844*9f257f50SLaurent Pinchart }, { 845*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_YUYV, 846*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS( 847*9f257f50SLaurent Pinchart MEDIA_BUS_FMT_YUYV8_2X8, 848*9f257f50SLaurent Pinchart MEDIA_BUS_FMT_YUYV8_1X16 849*9f257f50SLaurent Pinchart ), 850*9f257f50SLaurent Pinchart .yuv = true, 851*9f257f50SLaurent Pinchart .bpp = 16, 852*9f257f50SLaurent Pinchart }, 853*9f257f50SLaurent Pinchart /*** raw bayer and grayscale formats start here ***/ 854*9f257f50SLaurent Pinchart { 855*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SBGGR8, 856*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR8_1X8), 857*9f257f50SLaurent Pinchart .bpp = 8, 858*9f257f50SLaurent Pinchart }, { 859*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SGBRG8, 860*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG8_1X8), 861*9f257f50SLaurent Pinchart .bpp = 8, 862*9f257f50SLaurent Pinchart }, { 863*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SGRBG8, 864*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG8_1X8), 865*9f257f50SLaurent Pinchart .bpp = 8, 866*9f257f50SLaurent Pinchart }, { 867*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SRGGB8, 868*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB8_1X8), 869*9f257f50SLaurent Pinchart .bpp = 8, 870*9f257f50SLaurent Pinchart }, { 871*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SBGGR10, 872*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR10_1X10), 873*9f257f50SLaurent Pinchart .bpp = 16, 874*9f257f50SLaurent Pinchart }, { 875*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SGBRG10, 876*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG10_1X10), 877*9f257f50SLaurent Pinchart .bpp = 16, 878*9f257f50SLaurent Pinchart }, { 879*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SGRBG10, 880*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG10_1X10), 881*9f257f50SLaurent Pinchart .bpp = 16, 882*9f257f50SLaurent Pinchart }, { 883*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SRGGB10, 884*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB10_1X10), 885*9f257f50SLaurent Pinchart .bpp = 16, 886*9f257f50SLaurent Pinchart }, { 887*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SBGGR12, 888*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR12_1X12), 889*9f257f50SLaurent Pinchart .bpp = 16, 890*9f257f50SLaurent Pinchart }, { 891*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SGBRG12, 892*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG12_1X12), 893*9f257f50SLaurent Pinchart .bpp = 16, 894*9f257f50SLaurent Pinchart }, { 895*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SGRBG12, 896*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG12_1X12), 897*9f257f50SLaurent Pinchart .bpp = 16, 898*9f257f50SLaurent Pinchart }, { 899*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SRGGB12, 900*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB12_1X12), 901*9f257f50SLaurent Pinchart .bpp = 16, 902*9f257f50SLaurent Pinchart }, { 903*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SBGGR14, 904*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SBGGR14_1X14), 905*9f257f50SLaurent Pinchart .bpp = 16, 906*9f257f50SLaurent Pinchart }, { 907*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SGBRG14, 908*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGBRG14_1X14), 909*9f257f50SLaurent Pinchart .bpp = 16, 910*9f257f50SLaurent Pinchart }, { 911*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SGRBG14, 912*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SGRBG14_1X14), 913*9f257f50SLaurent Pinchart .bpp = 16, 914*9f257f50SLaurent Pinchart }, { 915*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_SRGGB14, 916*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_SRGGB14_1X14), 917*9f257f50SLaurent Pinchart .bpp = 16, 918*9f257f50SLaurent Pinchart }, { 919*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_GREY, 920*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y8_1X8), 921*9f257f50SLaurent Pinchart .bpp = 8, 922*9f257f50SLaurent Pinchart }, { 923*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_Y10, 924*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y10_1X10), 925*9f257f50SLaurent Pinchart .bpp = 16, 926*9f257f50SLaurent Pinchart }, { 927*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_Y12, 928*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y12_1X12), 929*9f257f50SLaurent Pinchart .bpp = 16, 930*9f257f50SLaurent Pinchart }, { 931*9f257f50SLaurent Pinchart .fourcc = V4L2_PIX_FMT_Y14, 932*9f257f50SLaurent Pinchart .codes = IMX_BUS_FMTS(MEDIA_BUS_FMT_Y14_1X14), 933*9f257f50SLaurent Pinchart .bpp = 16, 934*9f257f50SLaurent Pinchart }, 935*9f257f50SLaurent Pinchart }; 936*9f257f50SLaurent Pinchart 937*9f257f50SLaurent Pinchart /* 938*9f257f50SLaurent Pinchart * Search in the pixel_formats[] array for an entry with the given fourcc 939*9f257f50SLaurent Pinchart * return it. 940*9f257f50SLaurent Pinchart */ 941*9f257f50SLaurent Pinchart static const struct imx7_csi_pixfmt *imx7_csi_find_pixel_format(u32 fourcc) 942*9f257f50SLaurent Pinchart { 943*9f257f50SLaurent Pinchart unsigned int i; 944*9f257f50SLaurent Pinchart 945*9f257f50SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { 946*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *fmt = &pixel_formats[i]; 947*9f257f50SLaurent Pinchart 948*9f257f50SLaurent Pinchart if (fmt->fourcc == fourcc) 949*9f257f50SLaurent Pinchart return fmt; 950*9f257f50SLaurent Pinchart } 951*9f257f50SLaurent Pinchart 952*9f257f50SLaurent Pinchart return NULL; 953*9f257f50SLaurent Pinchart } 954*9f257f50SLaurent Pinchart 955*9f257f50SLaurent Pinchart /* 956*9f257f50SLaurent Pinchart * Search in the pixel_formats[] array for an entry with the given media 957*9f257f50SLaurent Pinchart * bus code and return it. 958*9f257f50SLaurent Pinchart */ 959*9f257f50SLaurent Pinchart static const struct imx7_csi_pixfmt *imx7_csi_find_mbus_format(u32 code) 960*9f257f50SLaurent Pinchart { 961*9f257f50SLaurent Pinchart unsigned int i; 962*9f257f50SLaurent Pinchart 963*9f257f50SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { 964*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *fmt = &pixel_formats[i]; 965*9f257f50SLaurent Pinchart unsigned int j; 966*9f257f50SLaurent Pinchart 967*9f257f50SLaurent Pinchart if (!fmt->codes) 968*9f257f50SLaurent Pinchart continue; 969*9f257f50SLaurent Pinchart 970*9f257f50SLaurent Pinchart for (j = 0; fmt->codes[j]; j++) { 971*9f257f50SLaurent Pinchart if (code == fmt->codes[j]) 972*9f257f50SLaurent Pinchart return fmt; 973*9f257f50SLaurent Pinchart } 974*9f257f50SLaurent Pinchart } 975*9f257f50SLaurent Pinchart 976*9f257f50SLaurent Pinchart return NULL; 977*9f257f50SLaurent Pinchart } 978*9f257f50SLaurent Pinchart 979*9f257f50SLaurent Pinchart /* 980*9f257f50SLaurent Pinchart * Enumerate entries in the pixel_formats[] array that match the 981*9f257f50SLaurent Pinchart * requested search criteria. Return the media-bus code that matches 982*9f257f50SLaurent Pinchart * the search criteria at the requested match index. 983*9f257f50SLaurent Pinchart * 984*9f257f50SLaurent Pinchart * @code: The returned media-bus code that matches the search criteria at 985*9f257f50SLaurent Pinchart * the requested match index. 986*9f257f50SLaurent Pinchart * @index: The requested match index. 987*9f257f50SLaurent Pinchart */ 988*9f257f50SLaurent Pinchart static int imx7_csi_enum_mbus_formats(u32 *code, u32 index) 989*9f257f50SLaurent Pinchart { 990*9f257f50SLaurent Pinchart unsigned int i; 991*9f257f50SLaurent Pinchart 992*9f257f50SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { 993*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *fmt = &pixel_formats[i]; 994*9f257f50SLaurent Pinchart unsigned int j; 995*9f257f50SLaurent Pinchart 996*9f257f50SLaurent Pinchart if (!fmt->codes) 997*9f257f50SLaurent Pinchart continue; 998*9f257f50SLaurent Pinchart 999*9f257f50SLaurent Pinchart for (j = 0; fmt->codes[j]; j++) { 1000*9f257f50SLaurent Pinchart if (index == 0) { 1001*9f257f50SLaurent Pinchart *code = fmt->codes[j]; 1002*9f257f50SLaurent Pinchart return 0; 1003*9f257f50SLaurent Pinchart } 1004*9f257f50SLaurent Pinchart 1005*9f257f50SLaurent Pinchart index--; 1006*9f257f50SLaurent Pinchart } 1007*9f257f50SLaurent Pinchart } 1008*9f257f50SLaurent Pinchart 1009*9f257f50SLaurent Pinchart return -EINVAL; 1010*9f257f50SLaurent Pinchart } 1011*9f257f50SLaurent Pinchart 1012*9f257f50SLaurent Pinchart static int imx7_csi_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, 1013*9f257f50SLaurent Pinchart const struct v4l2_mbus_framefmt *mbus, 1014*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *cc) 1015*9f257f50SLaurent Pinchart { 1016*9f257f50SLaurent Pinchart u32 width; 1017*9f257f50SLaurent Pinchart u32 stride; 1018*9f257f50SLaurent Pinchart 1019*9f257f50SLaurent Pinchart if (!cc) { 1020*9f257f50SLaurent Pinchart cc = imx7_csi_find_mbus_format(mbus->code); 1021*9f257f50SLaurent Pinchart if (!cc) 1022*9f257f50SLaurent Pinchart return -EINVAL; 1023*9f257f50SLaurent Pinchart } 1024*9f257f50SLaurent Pinchart 1025*9f257f50SLaurent Pinchart /* Round up width for minimum burst size */ 1026*9f257f50SLaurent Pinchart width = round_up(mbus->width, 8); 1027*9f257f50SLaurent Pinchart 1028*9f257f50SLaurent Pinchart /* Round up stride for IDMAC line start address alignment */ 1029*9f257f50SLaurent Pinchart stride = round_up((width * cc->bpp) >> 3, 8); 1030*9f257f50SLaurent Pinchart 1031*9f257f50SLaurent Pinchart pix->width = width; 1032*9f257f50SLaurent Pinchart pix->height = mbus->height; 1033*9f257f50SLaurent Pinchart pix->pixelformat = cc->fourcc; 1034*9f257f50SLaurent Pinchart pix->colorspace = mbus->colorspace; 1035*9f257f50SLaurent Pinchart pix->xfer_func = mbus->xfer_func; 1036*9f257f50SLaurent Pinchart pix->ycbcr_enc = mbus->ycbcr_enc; 1037*9f257f50SLaurent Pinchart pix->quantization = mbus->quantization; 1038*9f257f50SLaurent Pinchart pix->field = mbus->field; 1039*9f257f50SLaurent Pinchart pix->bytesperline = stride; 1040*9f257f50SLaurent Pinchart pix->sizeimage = stride * pix->height; 1041*9f257f50SLaurent Pinchart 1042*9f257f50SLaurent Pinchart return 0; 1043*9f257f50SLaurent Pinchart } 1044*9f257f50SLaurent Pinchart 1045*9f257f50SLaurent Pinchart /* ----------------------------------------------------------------------------- 1046*9f257f50SLaurent Pinchart * Video Capture Device - IOCTLs 1047*9f257f50SLaurent Pinchart */ 1048*9f257f50SLaurent Pinchart 1049*9f257f50SLaurent Pinchart static int imx7_csi_video_querycap(struct file *file, void *fh, 1050*9f257f50SLaurent Pinchart struct v4l2_capability *cap) 1051*9f257f50SLaurent Pinchart { 1052*9f257f50SLaurent Pinchart struct imx7_csi *csi = video_drvdata(file); 1053*9f257f50SLaurent Pinchart 1054*9f257f50SLaurent Pinchart strscpy(cap->driver, IMX7_CSI_VIDEO_NAME, sizeof(cap->driver)); 1055*9f257f50SLaurent Pinchart strscpy(cap->card, IMX7_CSI_VIDEO_NAME, sizeof(cap->card)); 1056*9f257f50SLaurent Pinchart snprintf(cap->bus_info, sizeof(cap->bus_info), 1057*9f257f50SLaurent Pinchart "platform:%s", dev_name(csi->dev)); 1058*9f257f50SLaurent Pinchart 1059*9f257f50SLaurent Pinchart return 0; 1060*9f257f50SLaurent Pinchart } 1061*9f257f50SLaurent Pinchart 1062*9f257f50SLaurent Pinchart static int imx7_csi_video_enum_fmt_vid_cap(struct file *file, void *fh, 1063*9f257f50SLaurent Pinchart struct v4l2_fmtdesc *f) 1064*9f257f50SLaurent Pinchart { 1065*9f257f50SLaurent Pinchart unsigned int index = f->index; 1066*9f257f50SLaurent Pinchart unsigned int i; 1067*9f257f50SLaurent Pinchart 1068*9f257f50SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { 1069*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *fmt = &pixel_formats[i]; 1070*9f257f50SLaurent Pinchart 1071*9f257f50SLaurent Pinchart /* 1072*9f257f50SLaurent Pinchart * If a media bus code is specified, only consider formats that 1073*9f257f50SLaurent Pinchart * match it. 1074*9f257f50SLaurent Pinchart */ 1075*9f257f50SLaurent Pinchart if (f->mbus_code) { 1076*9f257f50SLaurent Pinchart unsigned int j; 1077*9f257f50SLaurent Pinchart 1078*9f257f50SLaurent Pinchart if (!fmt->codes) 1079*9f257f50SLaurent Pinchart continue; 1080*9f257f50SLaurent Pinchart 1081*9f257f50SLaurent Pinchart for (j = 0; fmt->codes[j]; j++) { 1082*9f257f50SLaurent Pinchart if (f->mbus_code == fmt->codes[j]) 1083*9f257f50SLaurent Pinchart break; 1084*9f257f50SLaurent Pinchart } 1085*9f257f50SLaurent Pinchart 1086*9f257f50SLaurent Pinchart if (!fmt->codes[j]) 1087*9f257f50SLaurent Pinchart continue; 1088*9f257f50SLaurent Pinchart } 1089*9f257f50SLaurent Pinchart 1090*9f257f50SLaurent Pinchart if (index == 0) { 1091*9f257f50SLaurent Pinchart f->pixelformat = fmt->fourcc; 1092*9f257f50SLaurent Pinchart return 0; 1093*9f257f50SLaurent Pinchart } 1094*9f257f50SLaurent Pinchart 1095*9f257f50SLaurent Pinchart index--; 1096*9f257f50SLaurent Pinchart } 1097*9f257f50SLaurent Pinchart 1098*9f257f50SLaurent Pinchart return -EINVAL; 1099*9f257f50SLaurent Pinchart } 1100*9f257f50SLaurent Pinchart 1101*9f257f50SLaurent Pinchart static int imx7_csi_video_enum_framesizes(struct file *file, void *fh, 1102*9f257f50SLaurent Pinchart struct v4l2_frmsizeenum *fsize) 1103*9f257f50SLaurent Pinchart { 1104*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *cc; 1105*9f257f50SLaurent Pinchart 1106*9f257f50SLaurent Pinchart if (fsize->index > 0) 1107*9f257f50SLaurent Pinchart return -EINVAL; 1108*9f257f50SLaurent Pinchart 1109*9f257f50SLaurent Pinchart cc = imx7_csi_find_pixel_format(fsize->pixel_format); 1110*9f257f50SLaurent Pinchart if (!cc) 1111*9f257f50SLaurent Pinchart return -EINVAL; 1112*9f257f50SLaurent Pinchart 1113*9f257f50SLaurent Pinchart /* 1114*9f257f50SLaurent Pinchart * TODO: The constraints are hardware-specific and may depend on the 1115*9f257f50SLaurent Pinchart * pixel format. This should come from the driver using 1116*9f257f50SLaurent Pinchart * imx_media_capture. 1117*9f257f50SLaurent Pinchart */ 1118*9f257f50SLaurent Pinchart fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; 1119*9f257f50SLaurent Pinchart fsize->stepwise.min_width = 1; 1120*9f257f50SLaurent Pinchart fsize->stepwise.max_width = 65535; 1121*9f257f50SLaurent Pinchart fsize->stepwise.min_height = 1; 1122*9f257f50SLaurent Pinchart fsize->stepwise.max_height = 65535; 1123*9f257f50SLaurent Pinchart fsize->stepwise.step_width = 1; 1124*9f257f50SLaurent Pinchart fsize->stepwise.step_height = 1; 1125*9f257f50SLaurent Pinchart 1126*9f257f50SLaurent Pinchart return 0; 1127*9f257f50SLaurent Pinchart } 1128*9f257f50SLaurent Pinchart 1129*9f257f50SLaurent Pinchart static int imx7_csi_video_g_fmt_vid_cap(struct file *file, void *fh, 1130*9f257f50SLaurent Pinchart struct v4l2_format *f) 1131*9f257f50SLaurent Pinchart { 1132*9f257f50SLaurent Pinchart struct imx7_csi *csi = video_drvdata(file); 1133*9f257f50SLaurent Pinchart 1134*9f257f50SLaurent Pinchart f->fmt.pix = csi->vdev_fmt; 1135*9f257f50SLaurent Pinchart 1136*9f257f50SLaurent Pinchart return 0; 1137*9f257f50SLaurent Pinchart } 1138*9f257f50SLaurent Pinchart 1139*9f257f50SLaurent Pinchart static const struct imx7_csi_pixfmt * 1140*9f257f50SLaurent Pinchart __imx7_csi_video_try_fmt(struct v4l2_pix_format *pixfmt, 1141*9f257f50SLaurent Pinchart struct v4l2_rect *compose) 1142*9f257f50SLaurent Pinchart { 1143*9f257f50SLaurent Pinchart struct v4l2_mbus_framefmt fmt_src; 1144*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *cc; 1145*9f257f50SLaurent Pinchart 1146*9f257f50SLaurent Pinchart /* 1147*9f257f50SLaurent Pinchart * Find the pixel format, default to the first supported format if not 1148*9f257f50SLaurent Pinchart * found. 1149*9f257f50SLaurent Pinchart */ 1150*9f257f50SLaurent Pinchart cc = imx7_csi_find_pixel_format(pixfmt->pixelformat); 1151*9f257f50SLaurent Pinchart if (!cc) { 1152*9f257f50SLaurent Pinchart pixfmt->pixelformat = IMX7_CSI_DEF_PIX_FORMAT; 1153*9f257f50SLaurent Pinchart cc = imx7_csi_find_pixel_format(pixfmt->pixelformat); 1154*9f257f50SLaurent Pinchart } 1155*9f257f50SLaurent Pinchart 1156*9f257f50SLaurent Pinchart /* Allow IDMAC interweave but enforce field order from source. */ 1157*9f257f50SLaurent Pinchart if (V4L2_FIELD_IS_INTERLACED(pixfmt->field)) { 1158*9f257f50SLaurent Pinchart switch (pixfmt->field) { 1159*9f257f50SLaurent Pinchart case V4L2_FIELD_SEQ_TB: 1160*9f257f50SLaurent Pinchart pixfmt->field = V4L2_FIELD_INTERLACED_TB; 1161*9f257f50SLaurent Pinchart break; 1162*9f257f50SLaurent Pinchart case V4L2_FIELD_SEQ_BT: 1163*9f257f50SLaurent Pinchart pixfmt->field = V4L2_FIELD_INTERLACED_BT; 1164*9f257f50SLaurent Pinchart break; 1165*9f257f50SLaurent Pinchart default: 1166*9f257f50SLaurent Pinchart break; 1167*9f257f50SLaurent Pinchart } 1168*9f257f50SLaurent Pinchart } 1169*9f257f50SLaurent Pinchart 1170*9f257f50SLaurent Pinchart v4l2_fill_mbus_format(&fmt_src, pixfmt, 0); 1171*9f257f50SLaurent Pinchart imx7_csi_mbus_fmt_to_pix_fmt(pixfmt, &fmt_src, cc); 1172*9f257f50SLaurent Pinchart 1173*9f257f50SLaurent Pinchart if (compose) { 1174*9f257f50SLaurent Pinchart compose->width = fmt_src.width; 1175*9f257f50SLaurent Pinchart compose->height = fmt_src.height; 1176*9f257f50SLaurent Pinchart } 1177*9f257f50SLaurent Pinchart 1178*9f257f50SLaurent Pinchart return cc; 1179*9f257f50SLaurent Pinchart } 1180*9f257f50SLaurent Pinchart 1181*9f257f50SLaurent Pinchart static int imx7_csi_video_try_fmt_vid_cap(struct file *file, void *fh, 1182*9f257f50SLaurent Pinchart struct v4l2_format *f) 1183*9f257f50SLaurent Pinchart { 1184*9f257f50SLaurent Pinchart __imx7_csi_video_try_fmt(&f->fmt.pix, NULL); 1185*9f257f50SLaurent Pinchart return 0; 1186*9f257f50SLaurent Pinchart } 1187*9f257f50SLaurent Pinchart 1188*9f257f50SLaurent Pinchart static int imx7_csi_video_s_fmt_vid_cap(struct file *file, void *fh, 1189*9f257f50SLaurent Pinchart struct v4l2_format *f) 1190*9f257f50SLaurent Pinchart { 1191*9f257f50SLaurent Pinchart struct imx7_csi *csi = video_drvdata(file); 1192*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *cc; 1193*9f257f50SLaurent Pinchart 1194*9f257f50SLaurent Pinchart if (vb2_is_busy(&csi->q)) { 1195*9f257f50SLaurent Pinchart dev_err(csi->dev, "%s queue busy\n", __func__); 1196*9f257f50SLaurent Pinchart return -EBUSY; 1197*9f257f50SLaurent Pinchart } 1198*9f257f50SLaurent Pinchart 1199*9f257f50SLaurent Pinchart cc = __imx7_csi_video_try_fmt(&f->fmt.pix, &csi->vdev_compose); 1200*9f257f50SLaurent Pinchart 1201*9f257f50SLaurent Pinchart csi->vdev_cc = cc; 1202*9f257f50SLaurent Pinchart csi->vdev_fmt = f->fmt.pix; 1203*9f257f50SLaurent Pinchart 1204*9f257f50SLaurent Pinchart return 0; 1205*9f257f50SLaurent Pinchart } 1206*9f257f50SLaurent Pinchart 1207*9f257f50SLaurent Pinchart static int imx7_csi_video_g_selection(struct file *file, void *fh, 1208*9f257f50SLaurent Pinchart struct v4l2_selection *s) 1209*9f257f50SLaurent Pinchart { 1210*9f257f50SLaurent Pinchart struct imx7_csi *csi = video_drvdata(file); 1211*9f257f50SLaurent Pinchart 1212*9f257f50SLaurent Pinchart switch (s->target) { 1213*9f257f50SLaurent Pinchart case V4L2_SEL_TGT_COMPOSE: 1214*9f257f50SLaurent Pinchart case V4L2_SEL_TGT_COMPOSE_DEFAULT: 1215*9f257f50SLaurent Pinchart case V4L2_SEL_TGT_COMPOSE_BOUNDS: 1216*9f257f50SLaurent Pinchart /* The compose rectangle is fixed to the source format. */ 1217*9f257f50SLaurent Pinchart s->r = csi->vdev_compose; 1218*9f257f50SLaurent Pinchart break; 1219*9f257f50SLaurent Pinchart case V4L2_SEL_TGT_COMPOSE_PADDED: 1220*9f257f50SLaurent Pinchart /* 1221*9f257f50SLaurent Pinchart * The hardware writes with a configurable but fixed DMA burst 1222*9f257f50SLaurent Pinchart * size. If the source format width is not burst size aligned, 1223*9f257f50SLaurent Pinchart * the written frame contains padding to the right. 1224*9f257f50SLaurent Pinchart */ 1225*9f257f50SLaurent Pinchart s->r.left = 0; 1226*9f257f50SLaurent Pinchart s->r.top = 0; 1227*9f257f50SLaurent Pinchart s->r.width = csi->vdev_fmt.width; 1228*9f257f50SLaurent Pinchart s->r.height = csi->vdev_fmt.height; 1229*9f257f50SLaurent Pinchart break; 1230*9f257f50SLaurent Pinchart default: 1231*9f257f50SLaurent Pinchart return -EINVAL; 1232*9f257f50SLaurent Pinchart } 1233*9f257f50SLaurent Pinchart 1234*9f257f50SLaurent Pinchart return 0; 1235*9f257f50SLaurent Pinchart } 1236*9f257f50SLaurent Pinchart 1237*9f257f50SLaurent Pinchart static const struct v4l2_ioctl_ops imx7_csi_video_ioctl_ops = { 1238*9f257f50SLaurent Pinchart .vidioc_querycap = imx7_csi_video_querycap, 1239*9f257f50SLaurent Pinchart 1240*9f257f50SLaurent Pinchart .vidioc_enum_fmt_vid_cap = imx7_csi_video_enum_fmt_vid_cap, 1241*9f257f50SLaurent Pinchart .vidioc_enum_framesizes = imx7_csi_video_enum_framesizes, 1242*9f257f50SLaurent Pinchart 1243*9f257f50SLaurent Pinchart .vidioc_g_fmt_vid_cap = imx7_csi_video_g_fmt_vid_cap, 1244*9f257f50SLaurent Pinchart .vidioc_try_fmt_vid_cap = imx7_csi_video_try_fmt_vid_cap, 1245*9f257f50SLaurent Pinchart .vidioc_s_fmt_vid_cap = imx7_csi_video_s_fmt_vid_cap, 1246*9f257f50SLaurent Pinchart 1247*9f257f50SLaurent Pinchart .vidioc_g_selection = imx7_csi_video_g_selection, 1248*9f257f50SLaurent Pinchart 1249*9f257f50SLaurent Pinchart .vidioc_reqbufs = vb2_ioctl_reqbufs, 1250*9f257f50SLaurent Pinchart .vidioc_create_bufs = vb2_ioctl_create_bufs, 1251*9f257f50SLaurent Pinchart .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 1252*9f257f50SLaurent Pinchart .vidioc_querybuf = vb2_ioctl_querybuf, 1253*9f257f50SLaurent Pinchart .vidioc_qbuf = vb2_ioctl_qbuf, 1254*9f257f50SLaurent Pinchart .vidioc_dqbuf = vb2_ioctl_dqbuf, 1255*9f257f50SLaurent Pinchart .vidioc_expbuf = vb2_ioctl_expbuf, 1256*9f257f50SLaurent Pinchart .vidioc_streamon = vb2_ioctl_streamon, 1257*9f257f50SLaurent Pinchart .vidioc_streamoff = vb2_ioctl_streamoff, 1258*9f257f50SLaurent Pinchart }; 1259*9f257f50SLaurent Pinchart 1260*9f257f50SLaurent Pinchart /* ----------------------------------------------------------------------------- 1261*9f257f50SLaurent Pinchart * Video Capture Device - Queue Operations 1262*9f257f50SLaurent Pinchart */ 1263*9f257f50SLaurent Pinchart 1264*9f257f50SLaurent Pinchart static int imx7_csi_video_queue_setup(struct vb2_queue *vq, 1265*9f257f50SLaurent Pinchart unsigned int *nbuffers, 1266*9f257f50SLaurent Pinchart unsigned int *nplanes, 1267*9f257f50SLaurent Pinchart unsigned int sizes[], 1268*9f257f50SLaurent Pinchart struct device *alloc_devs[]) 1269*9f257f50SLaurent Pinchart { 1270*9f257f50SLaurent Pinchart struct imx7_csi *csi = vb2_get_drv_priv(vq); 1271*9f257f50SLaurent Pinchart struct v4l2_pix_format *pix = &csi->vdev_fmt; 1272*9f257f50SLaurent Pinchart unsigned int count = *nbuffers; 1273*9f257f50SLaurent Pinchart 1274*9f257f50SLaurent Pinchart if (vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 1275*9f257f50SLaurent Pinchart return -EINVAL; 1276*9f257f50SLaurent Pinchart 1277*9f257f50SLaurent Pinchart if (*nplanes) { 1278*9f257f50SLaurent Pinchart if (*nplanes != 1 || sizes[0] < pix->sizeimage) 1279*9f257f50SLaurent Pinchart return -EINVAL; 1280*9f257f50SLaurent Pinchart count += vq->num_buffers; 1281*9f257f50SLaurent Pinchart } 1282*9f257f50SLaurent Pinchart 1283*9f257f50SLaurent Pinchart count = min_t(__u32, IMX7_CSI_VIDEO_MEM_LIMIT / pix->sizeimage, count); 1284*9f257f50SLaurent Pinchart 1285*9f257f50SLaurent Pinchart if (*nplanes) 1286*9f257f50SLaurent Pinchart *nbuffers = (count < vq->num_buffers) ? 0 : 1287*9f257f50SLaurent Pinchart count - vq->num_buffers; 1288*9f257f50SLaurent Pinchart else 1289*9f257f50SLaurent Pinchart *nbuffers = count; 1290*9f257f50SLaurent Pinchart 1291*9f257f50SLaurent Pinchart *nplanes = 1; 1292*9f257f50SLaurent Pinchart sizes[0] = pix->sizeimage; 1293*9f257f50SLaurent Pinchart 1294*9f257f50SLaurent Pinchart return 0; 1295*9f257f50SLaurent Pinchart } 1296*9f257f50SLaurent Pinchart 1297*9f257f50SLaurent Pinchart static int imx7_csi_video_buf_init(struct vb2_buffer *vb) 1298*9f257f50SLaurent Pinchart { 1299*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer *buf = to_imx7_csi_vb2_buffer(vb); 1300*9f257f50SLaurent Pinchart 1301*9f257f50SLaurent Pinchart INIT_LIST_HEAD(&buf->list); 1302*9f257f50SLaurent Pinchart 1303*9f257f50SLaurent Pinchart return 0; 1304*9f257f50SLaurent Pinchart } 1305*9f257f50SLaurent Pinchart 1306*9f257f50SLaurent Pinchart static int imx7_csi_video_buf_prepare(struct vb2_buffer *vb) 1307*9f257f50SLaurent Pinchart { 1308*9f257f50SLaurent Pinchart struct imx7_csi *csi = vb2_get_drv_priv(vb->vb2_queue); 1309*9f257f50SLaurent Pinchart struct v4l2_pix_format *pix = &csi->vdev_fmt; 1310*9f257f50SLaurent Pinchart 1311*9f257f50SLaurent Pinchart if (vb2_plane_size(vb, 0) < pix->sizeimage) { 1312*9f257f50SLaurent Pinchart dev_err(csi->dev, 1313*9f257f50SLaurent Pinchart "data will not fit into plane (%lu < %lu)\n", 1314*9f257f50SLaurent Pinchart vb2_plane_size(vb, 0), (long)pix->sizeimage); 1315*9f257f50SLaurent Pinchart return -EINVAL; 1316*9f257f50SLaurent Pinchart } 1317*9f257f50SLaurent Pinchart 1318*9f257f50SLaurent Pinchart vb2_set_plane_payload(vb, 0, pix->sizeimage); 1319*9f257f50SLaurent Pinchart 1320*9f257f50SLaurent Pinchart return 0; 1321*9f257f50SLaurent Pinchart } 1322*9f257f50SLaurent Pinchart 1323*9f257f50SLaurent Pinchart static bool imx7_csi_fast_track_buffer(struct imx7_csi *csi, 1324*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer *buf) 1325*9f257f50SLaurent Pinchart { 1326*9f257f50SLaurent Pinchart unsigned long flags; 1327*9f257f50SLaurent Pinchart dma_addr_t dma_addr; 1328*9f257f50SLaurent Pinchart int buf_num; 1329*9f257f50SLaurent Pinchart u32 isr; 1330*9f257f50SLaurent Pinchart 1331*9f257f50SLaurent Pinchart if (!csi->is_streaming) 1332*9f257f50SLaurent Pinchart return false; 1333*9f257f50SLaurent Pinchart 1334*9f257f50SLaurent Pinchart dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vbuf.vb2_buf, 0); 1335*9f257f50SLaurent Pinchart 1336*9f257f50SLaurent Pinchart /* 1337*9f257f50SLaurent Pinchart * buf_num holds the framebuffer ID of the most recently (*not* the 1338*9f257f50SLaurent Pinchart * next anticipated) triggered interrupt. Without loss of generality, 1339*9f257f50SLaurent Pinchart * if buf_num is 0, the hardware is capturing to FB2. If FB1 has been 1340*9f257f50SLaurent Pinchart * programmed with a dummy buffer (as indicated by active_vb2_buf[0] 1341*9f257f50SLaurent Pinchart * being NULL), then we can fast-track the new buffer by programming 1342*9f257f50SLaurent Pinchart * its address in FB1 before the hardware completes FB2, instead of 1343*9f257f50SLaurent Pinchart * adding it to the buffer queue and incurring a delay of one 1344*9f257f50SLaurent Pinchart * additional frame. 1345*9f257f50SLaurent Pinchart * 1346*9f257f50SLaurent Pinchart * The irqlock prevents races with the interrupt handler that updates 1347*9f257f50SLaurent Pinchart * buf_num when it programs the next buffer, but we can still race with 1348*9f257f50SLaurent Pinchart * the hardware if we program the buffer in FB1 just after the hardware 1349*9f257f50SLaurent Pinchart * completes FB2 and switches to FB1 and before buf_num can be updated 1350*9f257f50SLaurent Pinchart * by the interrupt handler for FB2. The fast-tracked buffer would 1351*9f257f50SLaurent Pinchart * then be ignored by the hardware while the driver would think it has 1352*9f257f50SLaurent Pinchart * successfully been processed. 1353*9f257f50SLaurent Pinchart * 1354*9f257f50SLaurent Pinchart * To avoid this problem, if we can't avoid the race, we can detect 1355*9f257f50SLaurent Pinchart * that we have lost it by checking, after programming the buffer in 1356*9f257f50SLaurent Pinchart * FB1, if the interrupt flag indicating completion of FB2 has been 1357*9f257f50SLaurent Pinchart * raised. If that is not the case, fast-tracking succeeded, and we can 1358*9f257f50SLaurent Pinchart * update active_vb2_buf[0]. Otherwise, we may or may not have lost the 1359*9f257f50SLaurent Pinchart * race (as the interrupt flag may have been raised just after 1360*9f257f50SLaurent Pinchart * programming FB1 and before we read the interrupt status register), 1361*9f257f50SLaurent Pinchart * and we need to assume the worst case of a race loss and queue the 1362*9f257f50SLaurent Pinchart * buffer through the slow path. 1363*9f257f50SLaurent Pinchart */ 1364*9f257f50SLaurent Pinchart 1365*9f257f50SLaurent Pinchart spin_lock_irqsave(&csi->irqlock, flags); 1366*9f257f50SLaurent Pinchart 1367*9f257f50SLaurent Pinchart buf_num = csi->buf_num; 1368*9f257f50SLaurent Pinchart if (csi->active_vb2_buf[buf_num]) { 1369*9f257f50SLaurent Pinchart spin_unlock_irqrestore(&csi->irqlock, flags); 1370*9f257f50SLaurent Pinchart return false; 1371*9f257f50SLaurent Pinchart } 1372*9f257f50SLaurent Pinchart 1373*9f257f50SLaurent Pinchart imx7_csi_update_buf(csi, dma_addr, buf_num); 1374*9f257f50SLaurent Pinchart 1375*9f257f50SLaurent Pinchart isr = imx7_csi_reg_read(csi, CSI_CSISR); 1376*9f257f50SLaurent Pinchart if (isr & (buf_num ? BIT_DMA_TSF_DONE_FB1 : BIT_DMA_TSF_DONE_FB2)) { 1377*9f257f50SLaurent Pinchart /* 1378*9f257f50SLaurent Pinchart * The interrupt for the /other/ FB just came (the isr hasn't 1379*9f257f50SLaurent Pinchart * run yet though, because we have the lock here); we can't be 1380*9f257f50SLaurent Pinchart * sure we've programmed buf_num FB in time, so queue the buffer 1381*9f257f50SLaurent Pinchart * to the buffer queue normally. No need to undo writing the FB 1382*9f257f50SLaurent Pinchart * register, since we won't return it as active_vb2_buf is NULL, 1383*9f257f50SLaurent Pinchart * so it's okay to potentially write it to both FB1 and FB2; 1384*9f257f50SLaurent Pinchart * only the one where it was queued normally will be returned. 1385*9f257f50SLaurent Pinchart */ 1386*9f257f50SLaurent Pinchart spin_unlock_irqrestore(&csi->irqlock, flags); 1387*9f257f50SLaurent Pinchart return false; 1388*9f257f50SLaurent Pinchart } 1389*9f257f50SLaurent Pinchart 1390*9f257f50SLaurent Pinchart csi->active_vb2_buf[buf_num] = buf; 1391*9f257f50SLaurent Pinchart 1392*9f257f50SLaurent Pinchart spin_unlock_irqrestore(&csi->irqlock, flags); 1393*9f257f50SLaurent Pinchart return true; 1394*9f257f50SLaurent Pinchart } 1395*9f257f50SLaurent Pinchart 1396*9f257f50SLaurent Pinchart static void imx7_csi_video_buf_queue(struct vb2_buffer *vb) 1397*9f257f50SLaurent Pinchart { 1398*9f257f50SLaurent Pinchart struct imx7_csi *csi = vb2_get_drv_priv(vb->vb2_queue); 1399*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer *buf = to_imx7_csi_vb2_buffer(vb); 1400*9f257f50SLaurent Pinchart unsigned long flags; 1401*9f257f50SLaurent Pinchart 1402*9f257f50SLaurent Pinchart if (imx7_csi_fast_track_buffer(csi, buf)) 1403*9f257f50SLaurent Pinchart return; 1404*9f257f50SLaurent Pinchart 1405*9f257f50SLaurent Pinchart spin_lock_irqsave(&csi->q_lock, flags); 1406*9f257f50SLaurent Pinchart 1407*9f257f50SLaurent Pinchart list_add_tail(&buf->list, &csi->ready_q); 1408*9f257f50SLaurent Pinchart 1409*9f257f50SLaurent Pinchart spin_unlock_irqrestore(&csi->q_lock, flags); 1410*9f257f50SLaurent Pinchart } 1411*9f257f50SLaurent Pinchart 1412*9f257f50SLaurent Pinchart static int imx7_csi_video_validate_fmt(struct imx7_csi *csi) 1413*9f257f50SLaurent Pinchart { 1414*9f257f50SLaurent Pinchart struct v4l2_subdev_format fmt_src; 1415*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *cc; 1416*9f257f50SLaurent Pinchart int ret; 1417*9f257f50SLaurent Pinchart 1418*9f257f50SLaurent Pinchart /* Retrieve the media bus format on the source subdev. */ 1419*9f257f50SLaurent Pinchart fmt_src.pad = IMX7_CSI_PAD_SRC; 1420*9f257f50SLaurent Pinchart fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; 1421*9f257f50SLaurent Pinchart ret = v4l2_subdev_call(&csi->sd, pad, get_fmt, NULL, &fmt_src); 1422*9f257f50SLaurent Pinchart if (ret) 1423*9f257f50SLaurent Pinchart return ret; 1424*9f257f50SLaurent Pinchart 1425*9f257f50SLaurent Pinchart /* 1426*9f257f50SLaurent Pinchart * Verify that the media bus size matches the size set on the video 1427*9f257f50SLaurent Pinchart * node. It is sufficient to check the compose rectangle size without 1428*9f257f50SLaurent Pinchart * checking the rounded size from pix_fmt, as the rounded size is 1429*9f257f50SLaurent Pinchart * derived directly from the compose rectangle size, and will thus 1430*9f257f50SLaurent Pinchart * always match if the compose rectangle matches. 1431*9f257f50SLaurent Pinchart */ 1432*9f257f50SLaurent Pinchart if (csi->vdev_compose.width != fmt_src.format.width || 1433*9f257f50SLaurent Pinchart csi->vdev_compose.height != fmt_src.format.height) 1434*9f257f50SLaurent Pinchart return -EPIPE; 1435*9f257f50SLaurent Pinchart 1436*9f257f50SLaurent Pinchart /* 1437*9f257f50SLaurent Pinchart * Verify that the media bus code is compatible with the pixel format 1438*9f257f50SLaurent Pinchart * set on the video node. 1439*9f257f50SLaurent Pinchart */ 1440*9f257f50SLaurent Pinchart cc = imx7_csi_find_mbus_format(fmt_src.format.code); 1441*9f257f50SLaurent Pinchart if (!cc || csi->vdev_cc->yuv != cc->yuv) 1442*9f257f50SLaurent Pinchart return -EPIPE; 1443*9f257f50SLaurent Pinchart 1444*9f257f50SLaurent Pinchart return 0; 1445*9f257f50SLaurent Pinchart } 1446*9f257f50SLaurent Pinchart 1447*9f257f50SLaurent Pinchart static int imx7_csi_video_start_streaming(struct vb2_queue *vq, 1448*9f257f50SLaurent Pinchart unsigned int count) 1449*9f257f50SLaurent Pinchart { 1450*9f257f50SLaurent Pinchart struct imx7_csi *csi = vb2_get_drv_priv(vq); 1451*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer *buf, *tmp; 1452*9f257f50SLaurent Pinchart unsigned long flags; 1453*9f257f50SLaurent Pinchart int ret; 1454*9f257f50SLaurent Pinchart 1455*9f257f50SLaurent Pinchart ret = imx7_csi_video_validate_fmt(csi); 1456*9f257f50SLaurent Pinchart if (ret) { 1457*9f257f50SLaurent Pinchart dev_err(csi->dev, "capture format not valid\n"); 1458*9f257f50SLaurent Pinchart goto err_buffers; 1459*9f257f50SLaurent Pinchart } 1460*9f257f50SLaurent Pinchart 1461*9f257f50SLaurent Pinchart mutex_lock(&csi->mdev.graph_mutex); 1462*9f257f50SLaurent Pinchart 1463*9f257f50SLaurent Pinchart ret = __video_device_pipeline_start(csi->vdev, &csi->pipe); 1464*9f257f50SLaurent Pinchart if (ret) 1465*9f257f50SLaurent Pinchart goto err_unlock; 1466*9f257f50SLaurent Pinchart 1467*9f257f50SLaurent Pinchart ret = v4l2_subdev_call(&csi->sd, video, s_stream, 1); 1468*9f257f50SLaurent Pinchart if (ret) 1469*9f257f50SLaurent Pinchart goto err_stop; 1470*9f257f50SLaurent Pinchart 1471*9f257f50SLaurent Pinchart mutex_unlock(&csi->mdev.graph_mutex); 1472*9f257f50SLaurent Pinchart 1473*9f257f50SLaurent Pinchart return 0; 1474*9f257f50SLaurent Pinchart 1475*9f257f50SLaurent Pinchart err_stop: 1476*9f257f50SLaurent Pinchart __video_device_pipeline_stop(csi->vdev); 1477*9f257f50SLaurent Pinchart err_unlock: 1478*9f257f50SLaurent Pinchart mutex_unlock(&csi->mdev.graph_mutex); 1479*9f257f50SLaurent Pinchart dev_err(csi->dev, "pipeline start failed with %d\n", ret); 1480*9f257f50SLaurent Pinchart err_buffers: 1481*9f257f50SLaurent Pinchart spin_lock_irqsave(&csi->q_lock, flags); 1482*9f257f50SLaurent Pinchart list_for_each_entry_safe(buf, tmp, &csi->ready_q, list) { 1483*9f257f50SLaurent Pinchart list_del(&buf->list); 1484*9f257f50SLaurent Pinchart vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_QUEUED); 1485*9f257f50SLaurent Pinchart } 1486*9f257f50SLaurent Pinchart spin_unlock_irqrestore(&csi->q_lock, flags); 1487*9f257f50SLaurent Pinchart return ret; 1488*9f257f50SLaurent Pinchart } 1489*9f257f50SLaurent Pinchart 1490*9f257f50SLaurent Pinchart static void imx7_csi_video_stop_streaming(struct vb2_queue *vq) 1491*9f257f50SLaurent Pinchart { 1492*9f257f50SLaurent Pinchart struct imx7_csi *csi = vb2_get_drv_priv(vq); 1493*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer *frame; 1494*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer *tmp; 1495*9f257f50SLaurent Pinchart unsigned long flags; 1496*9f257f50SLaurent Pinchart 1497*9f257f50SLaurent Pinchart mutex_lock(&csi->mdev.graph_mutex); 1498*9f257f50SLaurent Pinchart v4l2_subdev_call(&csi->sd, video, s_stream, 0); 1499*9f257f50SLaurent Pinchart __video_device_pipeline_stop(csi->vdev); 1500*9f257f50SLaurent Pinchart mutex_unlock(&csi->mdev.graph_mutex); 1501*9f257f50SLaurent Pinchart 1502*9f257f50SLaurent Pinchart /* release all active buffers */ 1503*9f257f50SLaurent Pinchart spin_lock_irqsave(&csi->q_lock, flags); 1504*9f257f50SLaurent Pinchart list_for_each_entry_safe(frame, tmp, &csi->ready_q, list) { 1505*9f257f50SLaurent Pinchart list_del(&frame->list); 1506*9f257f50SLaurent Pinchart vb2_buffer_done(&frame->vbuf.vb2_buf, VB2_BUF_STATE_ERROR); 1507*9f257f50SLaurent Pinchart } 1508*9f257f50SLaurent Pinchart spin_unlock_irqrestore(&csi->q_lock, flags); 1509*9f257f50SLaurent Pinchart } 1510*9f257f50SLaurent Pinchart 1511*9f257f50SLaurent Pinchart static const struct vb2_ops imx7_csi_video_qops = { 1512*9f257f50SLaurent Pinchart .queue_setup = imx7_csi_video_queue_setup, 1513*9f257f50SLaurent Pinchart .buf_init = imx7_csi_video_buf_init, 1514*9f257f50SLaurent Pinchart .buf_prepare = imx7_csi_video_buf_prepare, 1515*9f257f50SLaurent Pinchart .buf_queue = imx7_csi_video_buf_queue, 1516*9f257f50SLaurent Pinchart .wait_prepare = vb2_ops_wait_prepare, 1517*9f257f50SLaurent Pinchart .wait_finish = vb2_ops_wait_finish, 1518*9f257f50SLaurent Pinchart .start_streaming = imx7_csi_video_start_streaming, 1519*9f257f50SLaurent Pinchart .stop_streaming = imx7_csi_video_stop_streaming, 1520*9f257f50SLaurent Pinchart }; 1521*9f257f50SLaurent Pinchart 1522*9f257f50SLaurent Pinchart /* ----------------------------------------------------------------------------- 1523*9f257f50SLaurent Pinchart * Video Capture Device - File Operations 1524*9f257f50SLaurent Pinchart */ 1525*9f257f50SLaurent Pinchart 1526*9f257f50SLaurent Pinchart static int imx7_csi_video_open(struct file *file) 1527*9f257f50SLaurent Pinchart { 1528*9f257f50SLaurent Pinchart struct imx7_csi *csi = video_drvdata(file); 1529*9f257f50SLaurent Pinchart int ret; 1530*9f257f50SLaurent Pinchart 1531*9f257f50SLaurent Pinchart if (mutex_lock_interruptible(&csi->vdev_mutex)) 1532*9f257f50SLaurent Pinchart return -ERESTARTSYS; 1533*9f257f50SLaurent Pinchart 1534*9f257f50SLaurent Pinchart ret = v4l2_fh_open(file); 1535*9f257f50SLaurent Pinchart if (ret) { 1536*9f257f50SLaurent Pinchart dev_err(csi->dev, "v4l2_fh_open failed\n"); 1537*9f257f50SLaurent Pinchart goto out; 1538*9f257f50SLaurent Pinchart } 1539*9f257f50SLaurent Pinchart 1540*9f257f50SLaurent Pinchart ret = v4l2_pipeline_pm_get(&csi->vdev->entity); 1541*9f257f50SLaurent Pinchart if (ret) 1542*9f257f50SLaurent Pinchart v4l2_fh_release(file); 1543*9f257f50SLaurent Pinchart 1544*9f257f50SLaurent Pinchart out: 1545*9f257f50SLaurent Pinchart mutex_unlock(&csi->vdev_mutex); 1546*9f257f50SLaurent Pinchart return ret; 1547*9f257f50SLaurent Pinchart } 1548*9f257f50SLaurent Pinchart 1549*9f257f50SLaurent Pinchart static int imx7_csi_video_release(struct file *file) 1550*9f257f50SLaurent Pinchart { 1551*9f257f50SLaurent Pinchart struct imx7_csi *csi = video_drvdata(file); 1552*9f257f50SLaurent Pinchart struct vb2_queue *vq = &csi->q; 1553*9f257f50SLaurent Pinchart 1554*9f257f50SLaurent Pinchart mutex_lock(&csi->vdev_mutex); 1555*9f257f50SLaurent Pinchart 1556*9f257f50SLaurent Pinchart if (file->private_data == vq->owner) { 1557*9f257f50SLaurent Pinchart vb2_queue_release(vq); 1558*9f257f50SLaurent Pinchart vq->owner = NULL; 1559*9f257f50SLaurent Pinchart } 1560*9f257f50SLaurent Pinchart 1561*9f257f50SLaurent Pinchart v4l2_pipeline_pm_put(&csi->vdev->entity); 1562*9f257f50SLaurent Pinchart 1563*9f257f50SLaurent Pinchart v4l2_fh_release(file); 1564*9f257f50SLaurent Pinchart mutex_unlock(&csi->vdev_mutex); 1565*9f257f50SLaurent Pinchart return 0; 1566*9f257f50SLaurent Pinchart } 1567*9f257f50SLaurent Pinchart 1568*9f257f50SLaurent Pinchart static const struct v4l2_file_operations imx7_csi_video_fops = { 1569*9f257f50SLaurent Pinchart .owner = THIS_MODULE, 1570*9f257f50SLaurent Pinchart .open = imx7_csi_video_open, 1571*9f257f50SLaurent Pinchart .release = imx7_csi_video_release, 1572*9f257f50SLaurent Pinchart .poll = vb2_fop_poll, 1573*9f257f50SLaurent Pinchart .unlocked_ioctl = video_ioctl2, 1574*9f257f50SLaurent Pinchart .mmap = vb2_fop_mmap, 1575*9f257f50SLaurent Pinchart }; 1576*9f257f50SLaurent Pinchart 1577*9f257f50SLaurent Pinchart /* ----------------------------------------------------------------------------- 1578*9f257f50SLaurent Pinchart * Video Capture Device - Init & Cleanup 1579*9f257f50SLaurent Pinchart */ 1580*9f257f50SLaurent Pinchart 1581*9f257f50SLaurent Pinchart static struct imx7_csi_vb2_buffer *imx7_csi_video_next_buf(struct imx7_csi *csi) 1582*9f257f50SLaurent Pinchart { 1583*9f257f50SLaurent Pinchart struct imx7_csi_vb2_buffer *buf = NULL; 1584*9f257f50SLaurent Pinchart unsigned long flags; 1585*9f257f50SLaurent Pinchart 1586*9f257f50SLaurent Pinchart spin_lock_irqsave(&csi->q_lock, flags); 1587*9f257f50SLaurent Pinchart 1588*9f257f50SLaurent Pinchart /* get next queued buffer */ 1589*9f257f50SLaurent Pinchart if (!list_empty(&csi->ready_q)) { 1590*9f257f50SLaurent Pinchart buf = list_entry(csi->ready_q.next, struct imx7_csi_vb2_buffer, 1591*9f257f50SLaurent Pinchart list); 1592*9f257f50SLaurent Pinchart list_del(&buf->list); 1593*9f257f50SLaurent Pinchart } 1594*9f257f50SLaurent Pinchart 1595*9f257f50SLaurent Pinchart spin_unlock_irqrestore(&csi->q_lock, flags); 1596*9f257f50SLaurent Pinchart 1597*9f257f50SLaurent Pinchart return buf; 1598*9f257f50SLaurent Pinchart } 1599*9f257f50SLaurent Pinchart 1600*9f257f50SLaurent Pinchart static int imx7_csi_video_init_format(struct imx7_csi *csi) 1601*9f257f50SLaurent Pinchart { 1602*9f257f50SLaurent Pinchart struct v4l2_subdev_format fmt_src = { 1603*9f257f50SLaurent Pinchart .pad = IMX7_CSI_PAD_SRC, 1604*9f257f50SLaurent Pinchart .which = V4L2_SUBDEV_FORMAT_ACTIVE, 1605*9f257f50SLaurent Pinchart }; 1606*9f257f50SLaurent Pinchart fmt_src.format.code = IMX7_CSI_DEF_MBUS_CODE; 1607*9f257f50SLaurent Pinchart fmt_src.format.width = IMX7_CSI_DEF_PIX_WIDTH; 1608*9f257f50SLaurent Pinchart fmt_src.format.height = IMX7_CSI_DEF_PIX_HEIGHT; 1609*9f257f50SLaurent Pinchart 1610*9f257f50SLaurent Pinchart imx7_csi_mbus_fmt_to_pix_fmt(&csi->vdev_fmt, &fmt_src.format, NULL); 1611*9f257f50SLaurent Pinchart csi->vdev_compose.width = fmt_src.format.width; 1612*9f257f50SLaurent Pinchart csi->vdev_compose.height = fmt_src.format.height; 1613*9f257f50SLaurent Pinchart 1614*9f257f50SLaurent Pinchart csi->vdev_cc = imx7_csi_find_pixel_format(csi->vdev_fmt.pixelformat); 1615*9f257f50SLaurent Pinchart 1616*9f257f50SLaurent Pinchart return 0; 1617*9f257f50SLaurent Pinchart } 1618*9f257f50SLaurent Pinchart 1619*9f257f50SLaurent Pinchart static int imx7_csi_video_register(struct imx7_csi *csi) 1620*9f257f50SLaurent Pinchart { 1621*9f257f50SLaurent Pinchart struct v4l2_subdev *sd = &csi->sd; 1622*9f257f50SLaurent Pinchart struct v4l2_device *v4l2_dev = sd->v4l2_dev; 1623*9f257f50SLaurent Pinchart struct video_device *vdev = csi->vdev; 1624*9f257f50SLaurent Pinchart int ret; 1625*9f257f50SLaurent Pinchart 1626*9f257f50SLaurent Pinchart vdev->v4l2_dev = v4l2_dev; 1627*9f257f50SLaurent Pinchart 1628*9f257f50SLaurent Pinchart /* Initialize the default format and compose rectangle. */ 1629*9f257f50SLaurent Pinchart ret = imx7_csi_video_init_format(csi); 1630*9f257f50SLaurent Pinchart if (ret < 0) 1631*9f257f50SLaurent Pinchart return ret; 1632*9f257f50SLaurent Pinchart 1633*9f257f50SLaurent Pinchart /* Register the video device. */ 1634*9f257f50SLaurent Pinchart ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 1635*9f257f50SLaurent Pinchart if (ret) { 1636*9f257f50SLaurent Pinchart dev_err(csi->dev, "Failed to register video device\n"); 1637*9f257f50SLaurent Pinchart return ret; 1638*9f257f50SLaurent Pinchart } 1639*9f257f50SLaurent Pinchart 1640*9f257f50SLaurent Pinchart dev_info(csi->dev, "Registered %s as /dev/%s\n", vdev->name, 1641*9f257f50SLaurent Pinchart video_device_node_name(vdev)); 1642*9f257f50SLaurent Pinchart 1643*9f257f50SLaurent Pinchart /* Create the link from the CSI subdev to the video device. */ 1644*9f257f50SLaurent Pinchart ret = media_create_pad_link(&sd->entity, IMX7_CSI_PAD_SRC, 1645*9f257f50SLaurent Pinchart &vdev->entity, 0, MEDIA_LNK_FL_IMMUTABLE | 1646*9f257f50SLaurent Pinchart MEDIA_LNK_FL_ENABLED); 1647*9f257f50SLaurent Pinchart if (ret) { 1648*9f257f50SLaurent Pinchart dev_err(csi->dev, "failed to create link to device node\n"); 1649*9f257f50SLaurent Pinchart video_unregister_device(vdev); 1650*9f257f50SLaurent Pinchart return ret; 1651*9f257f50SLaurent Pinchart } 1652*9f257f50SLaurent Pinchart 1653*9f257f50SLaurent Pinchart return 0; 1654*9f257f50SLaurent Pinchart } 1655*9f257f50SLaurent Pinchart 1656*9f257f50SLaurent Pinchart static void imx7_csi_video_unregister(struct imx7_csi *csi) 1657*9f257f50SLaurent Pinchart { 1658*9f257f50SLaurent Pinchart media_entity_cleanup(&csi->vdev->entity); 1659*9f257f50SLaurent Pinchart video_unregister_device(csi->vdev); 1660*9f257f50SLaurent Pinchart } 1661*9f257f50SLaurent Pinchart 1662*9f257f50SLaurent Pinchart static int imx7_csi_video_init(struct imx7_csi *csi) 1663*9f257f50SLaurent Pinchart { 1664*9f257f50SLaurent Pinchart struct video_device *vdev; 1665*9f257f50SLaurent Pinchart struct vb2_queue *vq; 1666*9f257f50SLaurent Pinchart int ret; 1667*9f257f50SLaurent Pinchart 1668*9f257f50SLaurent Pinchart mutex_init(&csi->vdev_mutex); 1669*9f257f50SLaurent Pinchart INIT_LIST_HEAD(&csi->ready_q); 1670*9f257f50SLaurent Pinchart spin_lock_init(&csi->q_lock); 1671*9f257f50SLaurent Pinchart 1672*9f257f50SLaurent Pinchart /* Allocate and initialize the video device. */ 1673*9f257f50SLaurent Pinchart vdev = video_device_alloc(); 1674*9f257f50SLaurent Pinchart if (!vdev) 1675*9f257f50SLaurent Pinchart return -ENOMEM; 1676*9f257f50SLaurent Pinchart 1677*9f257f50SLaurent Pinchart vdev->fops = &imx7_csi_video_fops; 1678*9f257f50SLaurent Pinchart vdev->ioctl_ops = &imx7_csi_video_ioctl_ops; 1679*9f257f50SLaurent Pinchart vdev->minor = -1; 1680*9f257f50SLaurent Pinchart vdev->release = video_device_release; 1681*9f257f50SLaurent Pinchart vdev->vfl_dir = VFL_DIR_RX; 1682*9f257f50SLaurent Pinchart vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; 1683*9f257f50SLaurent Pinchart vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING 1684*9f257f50SLaurent Pinchart | V4L2_CAP_IO_MC; 1685*9f257f50SLaurent Pinchart vdev->lock = &csi->vdev_mutex; 1686*9f257f50SLaurent Pinchart vdev->queue = &csi->q; 1687*9f257f50SLaurent Pinchart 1688*9f257f50SLaurent Pinchart snprintf(vdev->name, sizeof(vdev->name), "%s capture", csi->sd.name); 1689*9f257f50SLaurent Pinchart 1690*9f257f50SLaurent Pinchart video_set_drvdata(vdev, csi); 1691*9f257f50SLaurent Pinchart csi->vdev = vdev; 1692*9f257f50SLaurent Pinchart 1693*9f257f50SLaurent Pinchart /* Initialize the video device pad. */ 1694*9f257f50SLaurent Pinchart csi->vdev_pad.flags = MEDIA_PAD_FL_SINK; 1695*9f257f50SLaurent Pinchart ret = media_entity_pads_init(&vdev->entity, 1, &csi->vdev_pad); 1696*9f257f50SLaurent Pinchart if (ret) { 1697*9f257f50SLaurent Pinchart video_device_release(vdev); 1698*9f257f50SLaurent Pinchart return ret; 1699*9f257f50SLaurent Pinchart } 1700*9f257f50SLaurent Pinchart 1701*9f257f50SLaurent Pinchart /* Initialize the vb2 queue. */ 1702*9f257f50SLaurent Pinchart vq = &csi->q; 1703*9f257f50SLaurent Pinchart vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1704*9f257f50SLaurent Pinchart vq->io_modes = VB2_MMAP | VB2_DMABUF; 1705*9f257f50SLaurent Pinchart vq->drv_priv = csi; 1706*9f257f50SLaurent Pinchart vq->buf_struct_size = sizeof(struct imx7_csi_vb2_buffer); 1707*9f257f50SLaurent Pinchart vq->ops = &imx7_csi_video_qops; 1708*9f257f50SLaurent Pinchart vq->mem_ops = &vb2_dma_contig_memops; 1709*9f257f50SLaurent Pinchart vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 1710*9f257f50SLaurent Pinchart vq->lock = &csi->vdev_mutex; 1711*9f257f50SLaurent Pinchart vq->min_buffers_needed = 2; 1712*9f257f50SLaurent Pinchart vq->dev = csi->dev; 1713*9f257f50SLaurent Pinchart 1714*9f257f50SLaurent Pinchart ret = vb2_queue_init(vq); 1715*9f257f50SLaurent Pinchart if (ret) { 1716*9f257f50SLaurent Pinchart dev_err(csi->dev, "vb2_queue_init failed\n"); 1717*9f257f50SLaurent Pinchart video_device_release(vdev); 1718*9f257f50SLaurent Pinchart return ret; 1719*9f257f50SLaurent Pinchart } 1720*9f257f50SLaurent Pinchart 1721*9f257f50SLaurent Pinchart return 0; 1722*9f257f50SLaurent Pinchart } 1723*9f257f50SLaurent Pinchart 1724*9f257f50SLaurent Pinchart /* ----------------------------------------------------------------------------- 1725*9f257f50SLaurent Pinchart * V4L2 Subdev Operations 1726*9f257f50SLaurent Pinchart */ 1727*9f257f50SLaurent Pinchart 1728*9f257f50SLaurent Pinchart static int imx7_csi_s_stream(struct v4l2_subdev *sd, int enable) 1729*9f257f50SLaurent Pinchart { 1730*9f257f50SLaurent Pinchart struct imx7_csi *csi = v4l2_get_subdevdata(sd); 1731*9f257f50SLaurent Pinchart int ret = 0; 1732*9f257f50SLaurent Pinchart 1733*9f257f50SLaurent Pinchart mutex_lock(&csi->lock); 1734*9f257f50SLaurent Pinchart 1735*9f257f50SLaurent Pinchart if (!csi->src_sd) { 1736*9f257f50SLaurent Pinchart ret = -EPIPE; 1737*9f257f50SLaurent Pinchart goto out_unlock; 1738*9f257f50SLaurent Pinchart } 1739*9f257f50SLaurent Pinchart 1740*9f257f50SLaurent Pinchart if (csi->is_streaming == !!enable) 1741*9f257f50SLaurent Pinchart goto out_unlock; 1742*9f257f50SLaurent Pinchart 1743*9f257f50SLaurent Pinchart if (enable) { 1744*9f257f50SLaurent Pinchart ret = imx7_csi_init(csi); 1745*9f257f50SLaurent Pinchart if (ret < 0) 1746*9f257f50SLaurent Pinchart goto out_unlock; 1747*9f257f50SLaurent Pinchart 1748*9f257f50SLaurent Pinchart ret = v4l2_subdev_call(csi->src_sd, video, s_stream, 1); 1749*9f257f50SLaurent Pinchart if (ret < 0) { 1750*9f257f50SLaurent Pinchart imx7_csi_deinit(csi, VB2_BUF_STATE_QUEUED); 1751*9f257f50SLaurent Pinchart goto out_unlock; 1752*9f257f50SLaurent Pinchart } 1753*9f257f50SLaurent Pinchart 1754*9f257f50SLaurent Pinchart imx7_csi_enable(csi); 1755*9f257f50SLaurent Pinchart } else { 1756*9f257f50SLaurent Pinchart imx7_csi_disable(csi); 1757*9f257f50SLaurent Pinchart 1758*9f257f50SLaurent Pinchart v4l2_subdev_call(csi->src_sd, video, s_stream, 0); 1759*9f257f50SLaurent Pinchart 1760*9f257f50SLaurent Pinchart imx7_csi_deinit(csi, VB2_BUF_STATE_ERROR); 1761*9f257f50SLaurent Pinchart } 1762*9f257f50SLaurent Pinchart 1763*9f257f50SLaurent Pinchart csi->is_streaming = !!enable; 1764*9f257f50SLaurent Pinchart 1765*9f257f50SLaurent Pinchart out_unlock: 1766*9f257f50SLaurent Pinchart mutex_unlock(&csi->lock); 1767*9f257f50SLaurent Pinchart 1768*9f257f50SLaurent Pinchart return ret; 1769*9f257f50SLaurent Pinchart } 1770*9f257f50SLaurent Pinchart 1771*9f257f50SLaurent Pinchart static struct v4l2_mbus_framefmt * 1772*9f257f50SLaurent Pinchart imx7_csi_get_format(struct imx7_csi *csi, 1773*9f257f50SLaurent Pinchart struct v4l2_subdev_state *sd_state, 1774*9f257f50SLaurent Pinchart unsigned int pad, 1775*9f257f50SLaurent Pinchart enum v4l2_subdev_format_whence which) 1776*9f257f50SLaurent Pinchart { 1777*9f257f50SLaurent Pinchart if (which == V4L2_SUBDEV_FORMAT_TRY) 1778*9f257f50SLaurent Pinchart return v4l2_subdev_get_try_format(&csi->sd, sd_state, pad); 1779*9f257f50SLaurent Pinchart 1780*9f257f50SLaurent Pinchart return &csi->format_mbus[pad]; 1781*9f257f50SLaurent Pinchart } 1782*9f257f50SLaurent Pinchart 1783*9f257f50SLaurent Pinchart static int imx7_csi_init_cfg(struct v4l2_subdev *sd, 1784*9f257f50SLaurent Pinchart struct v4l2_subdev_state *sd_state) 1785*9f257f50SLaurent Pinchart { 1786*9f257f50SLaurent Pinchart const enum v4l2_subdev_format_whence which = 1787*9f257f50SLaurent Pinchart sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; 1788*9f257f50SLaurent Pinchart struct imx7_csi *csi = v4l2_get_subdevdata(sd); 1789*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *cc; 1790*9f257f50SLaurent Pinchart int i; 1791*9f257f50SLaurent Pinchart 1792*9f257f50SLaurent Pinchart cc = imx7_csi_find_mbus_format(IMX7_CSI_DEF_MBUS_CODE); 1793*9f257f50SLaurent Pinchart 1794*9f257f50SLaurent Pinchart for (i = 0; i < IMX7_CSI_PADS_NUM; i++) { 1795*9f257f50SLaurent Pinchart struct v4l2_mbus_framefmt *mf = 1796*9f257f50SLaurent Pinchart imx7_csi_get_format(csi, sd_state, i, which); 1797*9f257f50SLaurent Pinchart 1798*9f257f50SLaurent Pinchart mf->code = IMX7_CSI_DEF_MBUS_CODE; 1799*9f257f50SLaurent Pinchart mf->width = IMX7_CSI_DEF_PIX_WIDTH; 1800*9f257f50SLaurent Pinchart mf->height = IMX7_CSI_DEF_PIX_HEIGHT; 1801*9f257f50SLaurent Pinchart mf->field = V4L2_FIELD_NONE; 1802*9f257f50SLaurent Pinchart 1803*9f257f50SLaurent Pinchart mf->colorspace = V4L2_COLORSPACE_SRGB; 1804*9f257f50SLaurent Pinchart mf->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(mf->colorspace); 1805*9f257f50SLaurent Pinchart mf->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(mf->colorspace); 1806*9f257f50SLaurent Pinchart mf->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(!cc->yuv, 1807*9f257f50SLaurent Pinchart mf->colorspace, mf->ycbcr_enc); 1808*9f257f50SLaurent Pinchart 1809*9f257f50SLaurent Pinchart csi->cc[i] = cc; 1810*9f257f50SLaurent Pinchart } 1811*9f257f50SLaurent Pinchart 1812*9f257f50SLaurent Pinchart return 0; 1813*9f257f50SLaurent Pinchart } 1814*9f257f50SLaurent Pinchart 1815*9f257f50SLaurent Pinchart static int imx7_csi_enum_mbus_code(struct v4l2_subdev *sd, 1816*9f257f50SLaurent Pinchart struct v4l2_subdev_state *sd_state, 1817*9f257f50SLaurent Pinchart struct v4l2_subdev_mbus_code_enum *code) 1818*9f257f50SLaurent Pinchart { 1819*9f257f50SLaurent Pinchart struct imx7_csi *csi = v4l2_get_subdevdata(sd); 1820*9f257f50SLaurent Pinchart struct v4l2_mbus_framefmt *in_fmt; 1821*9f257f50SLaurent Pinchart int ret = 0; 1822*9f257f50SLaurent Pinchart 1823*9f257f50SLaurent Pinchart mutex_lock(&csi->lock); 1824*9f257f50SLaurent Pinchart 1825*9f257f50SLaurent Pinchart in_fmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SINK, 1826*9f257f50SLaurent Pinchart code->which); 1827*9f257f50SLaurent Pinchart 1828*9f257f50SLaurent Pinchart switch (code->pad) { 1829*9f257f50SLaurent Pinchart case IMX7_CSI_PAD_SINK: 1830*9f257f50SLaurent Pinchart ret = imx7_csi_enum_mbus_formats(&code->code, code->index); 1831*9f257f50SLaurent Pinchart break; 1832*9f257f50SLaurent Pinchart case IMX7_CSI_PAD_SRC: 1833*9f257f50SLaurent Pinchart if (code->index != 0) { 1834*9f257f50SLaurent Pinchart ret = -EINVAL; 1835*9f257f50SLaurent Pinchart goto out_unlock; 1836*9f257f50SLaurent Pinchart } 1837*9f257f50SLaurent Pinchart 1838*9f257f50SLaurent Pinchart code->code = in_fmt->code; 1839*9f257f50SLaurent Pinchart break; 1840*9f257f50SLaurent Pinchart default: 1841*9f257f50SLaurent Pinchart ret = -EINVAL; 1842*9f257f50SLaurent Pinchart } 1843*9f257f50SLaurent Pinchart 1844*9f257f50SLaurent Pinchart out_unlock: 1845*9f257f50SLaurent Pinchart mutex_unlock(&csi->lock); 1846*9f257f50SLaurent Pinchart 1847*9f257f50SLaurent Pinchart return ret; 1848*9f257f50SLaurent Pinchart } 1849*9f257f50SLaurent Pinchart 1850*9f257f50SLaurent Pinchart static int imx7_csi_get_fmt(struct v4l2_subdev *sd, 1851*9f257f50SLaurent Pinchart struct v4l2_subdev_state *sd_state, 1852*9f257f50SLaurent Pinchart struct v4l2_subdev_format *sdformat) 1853*9f257f50SLaurent Pinchart { 1854*9f257f50SLaurent Pinchart struct imx7_csi *csi = v4l2_get_subdevdata(sd); 1855*9f257f50SLaurent Pinchart struct v4l2_mbus_framefmt *fmt; 1856*9f257f50SLaurent Pinchart int ret = 0; 1857*9f257f50SLaurent Pinchart 1858*9f257f50SLaurent Pinchart mutex_lock(&csi->lock); 1859*9f257f50SLaurent Pinchart 1860*9f257f50SLaurent Pinchart fmt = imx7_csi_get_format(csi, sd_state, sdformat->pad, 1861*9f257f50SLaurent Pinchart sdformat->which); 1862*9f257f50SLaurent Pinchart if (!fmt) { 1863*9f257f50SLaurent Pinchart ret = -EINVAL; 1864*9f257f50SLaurent Pinchart goto out_unlock; 1865*9f257f50SLaurent Pinchart } 1866*9f257f50SLaurent Pinchart 1867*9f257f50SLaurent Pinchart sdformat->format = *fmt; 1868*9f257f50SLaurent Pinchart 1869*9f257f50SLaurent Pinchart out_unlock: 1870*9f257f50SLaurent Pinchart mutex_unlock(&csi->lock); 1871*9f257f50SLaurent Pinchart 1872*9f257f50SLaurent Pinchart return ret; 1873*9f257f50SLaurent Pinchart } 1874*9f257f50SLaurent Pinchart 1875*9f257f50SLaurent Pinchart /* 1876*9f257f50SLaurent Pinchart * Default the colorspace in tryfmt to SRGB if set to an unsupported 1877*9f257f50SLaurent Pinchart * colorspace or not initialized. Then set the remaining colorimetry 1878*9f257f50SLaurent Pinchart * parameters based on the colorspace if they are uninitialized. 1879*9f257f50SLaurent Pinchart * 1880*9f257f50SLaurent Pinchart * tryfmt->code must be set on entry. 1881*9f257f50SLaurent Pinchart */ 1882*9f257f50SLaurent Pinchart static void imx7_csi_try_colorimetry(struct v4l2_mbus_framefmt *tryfmt) 1883*9f257f50SLaurent Pinchart { 1884*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *cc; 1885*9f257f50SLaurent Pinchart bool is_rgb = false; 1886*9f257f50SLaurent Pinchart 1887*9f257f50SLaurent Pinchart cc = imx7_csi_find_mbus_format(tryfmt->code); 1888*9f257f50SLaurent Pinchart if (cc && !cc->yuv) 1889*9f257f50SLaurent Pinchart is_rgb = true; 1890*9f257f50SLaurent Pinchart 1891*9f257f50SLaurent Pinchart switch (tryfmt->colorspace) { 1892*9f257f50SLaurent Pinchart case V4L2_COLORSPACE_SMPTE170M: 1893*9f257f50SLaurent Pinchart case V4L2_COLORSPACE_REC709: 1894*9f257f50SLaurent Pinchart case V4L2_COLORSPACE_JPEG: 1895*9f257f50SLaurent Pinchart case V4L2_COLORSPACE_SRGB: 1896*9f257f50SLaurent Pinchart case V4L2_COLORSPACE_BT2020: 1897*9f257f50SLaurent Pinchart case V4L2_COLORSPACE_OPRGB: 1898*9f257f50SLaurent Pinchart case V4L2_COLORSPACE_DCI_P3: 1899*9f257f50SLaurent Pinchart case V4L2_COLORSPACE_RAW: 1900*9f257f50SLaurent Pinchart break; 1901*9f257f50SLaurent Pinchart default: 1902*9f257f50SLaurent Pinchart tryfmt->colorspace = V4L2_COLORSPACE_SRGB; 1903*9f257f50SLaurent Pinchart break; 1904*9f257f50SLaurent Pinchart } 1905*9f257f50SLaurent Pinchart 1906*9f257f50SLaurent Pinchart if (tryfmt->xfer_func == V4L2_XFER_FUNC_DEFAULT) 1907*9f257f50SLaurent Pinchart tryfmt->xfer_func = 1908*9f257f50SLaurent Pinchart V4L2_MAP_XFER_FUNC_DEFAULT(tryfmt->colorspace); 1909*9f257f50SLaurent Pinchart 1910*9f257f50SLaurent Pinchart if (tryfmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) 1911*9f257f50SLaurent Pinchart tryfmt->ycbcr_enc = 1912*9f257f50SLaurent Pinchart V4L2_MAP_YCBCR_ENC_DEFAULT(tryfmt->colorspace); 1913*9f257f50SLaurent Pinchart 1914*9f257f50SLaurent Pinchart if (tryfmt->quantization == V4L2_QUANTIZATION_DEFAULT) 1915*9f257f50SLaurent Pinchart tryfmt->quantization = 1916*9f257f50SLaurent Pinchart V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, 1917*9f257f50SLaurent Pinchart tryfmt->colorspace, 1918*9f257f50SLaurent Pinchart tryfmt->ycbcr_enc); 1919*9f257f50SLaurent Pinchart } 1920*9f257f50SLaurent Pinchart 1921*9f257f50SLaurent Pinchart static int imx7_csi_try_fmt(struct imx7_csi *csi, 1922*9f257f50SLaurent Pinchart struct v4l2_subdev_state *sd_state, 1923*9f257f50SLaurent Pinchart struct v4l2_subdev_format *sdformat, 1924*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt **cc) 1925*9f257f50SLaurent Pinchart { 1926*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *in_cc; 1927*9f257f50SLaurent Pinchart struct v4l2_mbus_framefmt *in_fmt; 1928*9f257f50SLaurent Pinchart u32 code; 1929*9f257f50SLaurent Pinchart 1930*9f257f50SLaurent Pinchart in_fmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SINK, 1931*9f257f50SLaurent Pinchart sdformat->which); 1932*9f257f50SLaurent Pinchart if (!in_fmt) 1933*9f257f50SLaurent Pinchart return -EINVAL; 1934*9f257f50SLaurent Pinchart 1935*9f257f50SLaurent Pinchart switch (sdformat->pad) { 1936*9f257f50SLaurent Pinchart case IMX7_CSI_PAD_SRC: 1937*9f257f50SLaurent Pinchart in_cc = imx7_csi_find_mbus_format(in_fmt->code); 1938*9f257f50SLaurent Pinchart 1939*9f257f50SLaurent Pinchart sdformat->format.width = in_fmt->width; 1940*9f257f50SLaurent Pinchart sdformat->format.height = in_fmt->height; 1941*9f257f50SLaurent Pinchart sdformat->format.code = in_fmt->code; 1942*9f257f50SLaurent Pinchart sdformat->format.field = in_fmt->field; 1943*9f257f50SLaurent Pinchart *cc = in_cc; 1944*9f257f50SLaurent Pinchart 1945*9f257f50SLaurent Pinchart sdformat->format.colorspace = in_fmt->colorspace; 1946*9f257f50SLaurent Pinchart sdformat->format.xfer_func = in_fmt->xfer_func; 1947*9f257f50SLaurent Pinchart sdformat->format.quantization = in_fmt->quantization; 1948*9f257f50SLaurent Pinchart sdformat->format.ycbcr_enc = in_fmt->ycbcr_enc; 1949*9f257f50SLaurent Pinchart break; 1950*9f257f50SLaurent Pinchart case IMX7_CSI_PAD_SINK: 1951*9f257f50SLaurent Pinchart *cc = imx7_csi_find_mbus_format(sdformat->format.code); 1952*9f257f50SLaurent Pinchart if (!*cc) { 1953*9f257f50SLaurent Pinchart code = IMX7_CSI_DEF_MBUS_CODE; 1954*9f257f50SLaurent Pinchart *cc = imx7_csi_find_mbus_format(code); 1955*9f257f50SLaurent Pinchart sdformat->format.code = code; 1956*9f257f50SLaurent Pinchart } 1957*9f257f50SLaurent Pinchart 1958*9f257f50SLaurent Pinchart if (sdformat->format.field != V4L2_FIELD_INTERLACED) 1959*9f257f50SLaurent Pinchart sdformat->format.field = V4L2_FIELD_NONE; 1960*9f257f50SLaurent Pinchart break; 1961*9f257f50SLaurent Pinchart default: 1962*9f257f50SLaurent Pinchart return -EINVAL; 1963*9f257f50SLaurent Pinchart } 1964*9f257f50SLaurent Pinchart 1965*9f257f50SLaurent Pinchart imx7_csi_try_colorimetry(&sdformat->format); 1966*9f257f50SLaurent Pinchart 1967*9f257f50SLaurent Pinchart return 0; 1968*9f257f50SLaurent Pinchart } 1969*9f257f50SLaurent Pinchart 1970*9f257f50SLaurent Pinchart static int imx7_csi_set_fmt(struct v4l2_subdev *sd, 1971*9f257f50SLaurent Pinchart struct v4l2_subdev_state *sd_state, 1972*9f257f50SLaurent Pinchart struct v4l2_subdev_format *sdformat) 1973*9f257f50SLaurent Pinchart { 1974*9f257f50SLaurent Pinchart struct imx7_csi *csi = v4l2_get_subdevdata(sd); 1975*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *outcc; 1976*9f257f50SLaurent Pinchart struct v4l2_mbus_framefmt *outfmt; 1977*9f257f50SLaurent Pinchart const struct imx7_csi_pixfmt *cc; 1978*9f257f50SLaurent Pinchart struct v4l2_mbus_framefmt *fmt; 1979*9f257f50SLaurent Pinchart struct v4l2_subdev_format format; 1980*9f257f50SLaurent Pinchart int ret = 0; 1981*9f257f50SLaurent Pinchart 1982*9f257f50SLaurent Pinchart if (sdformat->pad >= IMX7_CSI_PADS_NUM) 1983*9f257f50SLaurent Pinchart return -EINVAL; 1984*9f257f50SLaurent Pinchart 1985*9f257f50SLaurent Pinchart mutex_lock(&csi->lock); 1986*9f257f50SLaurent Pinchart 1987*9f257f50SLaurent Pinchart if (csi->is_streaming) { 1988*9f257f50SLaurent Pinchart ret = -EBUSY; 1989*9f257f50SLaurent Pinchart goto out_unlock; 1990*9f257f50SLaurent Pinchart } 1991*9f257f50SLaurent Pinchart 1992*9f257f50SLaurent Pinchart ret = imx7_csi_try_fmt(csi, sd_state, sdformat, &cc); 1993*9f257f50SLaurent Pinchart if (ret < 0) 1994*9f257f50SLaurent Pinchart goto out_unlock; 1995*9f257f50SLaurent Pinchart 1996*9f257f50SLaurent Pinchart fmt = imx7_csi_get_format(csi, sd_state, sdformat->pad, 1997*9f257f50SLaurent Pinchart sdformat->which); 1998*9f257f50SLaurent Pinchart if (!fmt) { 1999*9f257f50SLaurent Pinchart ret = -EINVAL; 2000*9f257f50SLaurent Pinchart goto out_unlock; 2001*9f257f50SLaurent Pinchart } 2002*9f257f50SLaurent Pinchart 2003*9f257f50SLaurent Pinchart *fmt = sdformat->format; 2004*9f257f50SLaurent Pinchart 2005*9f257f50SLaurent Pinchart if (sdformat->pad == IMX7_CSI_PAD_SINK) { 2006*9f257f50SLaurent Pinchart /* propagate format to source pads */ 2007*9f257f50SLaurent Pinchart format.pad = IMX7_CSI_PAD_SRC; 2008*9f257f50SLaurent Pinchart format.which = sdformat->which; 2009*9f257f50SLaurent Pinchart format.format = sdformat->format; 2010*9f257f50SLaurent Pinchart if (imx7_csi_try_fmt(csi, sd_state, &format, &outcc)) { 2011*9f257f50SLaurent Pinchart ret = -EINVAL; 2012*9f257f50SLaurent Pinchart goto out_unlock; 2013*9f257f50SLaurent Pinchart } 2014*9f257f50SLaurent Pinchart outfmt = imx7_csi_get_format(csi, sd_state, IMX7_CSI_PAD_SRC, 2015*9f257f50SLaurent Pinchart sdformat->which); 2016*9f257f50SLaurent Pinchart *outfmt = format.format; 2017*9f257f50SLaurent Pinchart 2018*9f257f50SLaurent Pinchart if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) 2019*9f257f50SLaurent Pinchart csi->cc[IMX7_CSI_PAD_SRC] = outcc; 2020*9f257f50SLaurent Pinchart } 2021*9f257f50SLaurent Pinchart 2022*9f257f50SLaurent Pinchart if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) 2023*9f257f50SLaurent Pinchart csi->cc[sdformat->pad] = cc; 2024*9f257f50SLaurent Pinchart 2025*9f257f50SLaurent Pinchart out_unlock: 2026*9f257f50SLaurent Pinchart mutex_unlock(&csi->lock); 2027*9f257f50SLaurent Pinchart 2028*9f257f50SLaurent Pinchart return ret; 2029*9f257f50SLaurent Pinchart } 2030*9f257f50SLaurent Pinchart 2031*9f257f50SLaurent Pinchart static int imx7_csi_pad_link_validate(struct v4l2_subdev *sd, 2032*9f257f50SLaurent Pinchart struct media_link *link, 2033*9f257f50SLaurent Pinchart struct v4l2_subdev_format *source_fmt, 2034*9f257f50SLaurent Pinchart struct v4l2_subdev_format *sink_fmt) 2035*9f257f50SLaurent Pinchart { 2036*9f257f50SLaurent Pinchart struct imx7_csi *csi = v4l2_get_subdevdata(sd); 2037*9f257f50SLaurent Pinchart struct media_pad *pad = NULL; 2038*9f257f50SLaurent Pinchart unsigned int i; 2039*9f257f50SLaurent Pinchart int ret; 2040*9f257f50SLaurent Pinchart 2041*9f257f50SLaurent Pinchart if (!csi->src_sd) 2042*9f257f50SLaurent Pinchart return -EPIPE; 2043*9f257f50SLaurent Pinchart 2044*9f257f50SLaurent Pinchart /* 2045*9f257f50SLaurent Pinchart * Validate the source link, and record whether the source uses the 2046*9f257f50SLaurent Pinchart * parallel input or the CSI-2 receiver. 2047*9f257f50SLaurent Pinchart */ 2048*9f257f50SLaurent Pinchart ret = v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt); 2049*9f257f50SLaurent Pinchart if (ret) 2050*9f257f50SLaurent Pinchart return ret; 2051*9f257f50SLaurent Pinchart 2052*9f257f50SLaurent Pinchart switch (csi->src_sd->entity.function) { 2053*9f257f50SLaurent Pinchart case MEDIA_ENT_F_VID_IF_BRIDGE: 2054*9f257f50SLaurent Pinchart /* The input is the CSI-2 receiver. */ 2055*9f257f50SLaurent Pinchart csi->is_csi2 = true; 2056*9f257f50SLaurent Pinchart break; 2057*9f257f50SLaurent Pinchart 2058*9f257f50SLaurent Pinchart case MEDIA_ENT_F_VID_MUX: 2059*9f257f50SLaurent Pinchart /* The input is the mux, check its input. */ 2060*9f257f50SLaurent Pinchart for (i = 0; i < csi->src_sd->entity.num_pads; i++) { 2061*9f257f50SLaurent Pinchart struct media_pad *spad = &csi->src_sd->entity.pads[i]; 2062*9f257f50SLaurent Pinchart 2063*9f257f50SLaurent Pinchart if (!(spad->flags & MEDIA_PAD_FL_SINK)) 2064*9f257f50SLaurent Pinchart continue; 2065*9f257f50SLaurent Pinchart 2066*9f257f50SLaurent Pinchart pad = media_pad_remote_pad_first(spad); 2067*9f257f50SLaurent Pinchart if (pad) 2068*9f257f50SLaurent Pinchart break; 2069*9f257f50SLaurent Pinchart } 2070*9f257f50SLaurent Pinchart 2071*9f257f50SLaurent Pinchart if (!pad) 2072*9f257f50SLaurent Pinchart return -ENODEV; 2073*9f257f50SLaurent Pinchart 2074*9f257f50SLaurent Pinchart csi->is_csi2 = pad->entity->function == MEDIA_ENT_F_VID_IF_BRIDGE; 2075*9f257f50SLaurent Pinchart break; 2076*9f257f50SLaurent Pinchart 2077*9f257f50SLaurent Pinchart default: 2078*9f257f50SLaurent Pinchart /* 2079*9f257f50SLaurent Pinchart * The input is an external entity, it must use the parallel 2080*9f257f50SLaurent Pinchart * bus. 2081*9f257f50SLaurent Pinchart */ 2082*9f257f50SLaurent Pinchart csi->is_csi2 = false; 2083*9f257f50SLaurent Pinchart break; 2084*9f257f50SLaurent Pinchart } 2085*9f257f50SLaurent Pinchart 2086*9f257f50SLaurent Pinchart return 0; 2087*9f257f50SLaurent Pinchart } 2088*9f257f50SLaurent Pinchart 2089*9f257f50SLaurent Pinchart static int imx7_csi_registered(struct v4l2_subdev *sd) 2090*9f257f50SLaurent Pinchart { 2091*9f257f50SLaurent Pinchart struct imx7_csi *csi = v4l2_get_subdevdata(sd); 2092*9f257f50SLaurent Pinchart int ret; 2093*9f257f50SLaurent Pinchart 2094*9f257f50SLaurent Pinchart ret = imx7_csi_video_init(csi); 2095*9f257f50SLaurent Pinchart if (ret) 2096*9f257f50SLaurent Pinchart return ret; 2097*9f257f50SLaurent Pinchart 2098*9f257f50SLaurent Pinchart ret = imx7_csi_video_register(csi); 2099*9f257f50SLaurent Pinchart if (ret) 2100*9f257f50SLaurent Pinchart return ret; 2101*9f257f50SLaurent Pinchart 2102*9f257f50SLaurent Pinchart ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev); 2103*9f257f50SLaurent Pinchart if (ret) 2104*9f257f50SLaurent Pinchart goto err_unreg; 2105*9f257f50SLaurent Pinchart 2106*9f257f50SLaurent Pinchart ret = media_device_register(&csi->mdev); 2107*9f257f50SLaurent Pinchart if (ret) 2108*9f257f50SLaurent Pinchart goto err_unreg; 2109*9f257f50SLaurent Pinchart 2110*9f257f50SLaurent Pinchart return 0; 2111*9f257f50SLaurent Pinchart 2112*9f257f50SLaurent Pinchart err_unreg: 2113*9f257f50SLaurent Pinchart imx7_csi_video_unregister(csi); 2114*9f257f50SLaurent Pinchart return ret; 2115*9f257f50SLaurent Pinchart } 2116*9f257f50SLaurent Pinchart 2117*9f257f50SLaurent Pinchart static void imx7_csi_unregistered(struct v4l2_subdev *sd) 2118*9f257f50SLaurent Pinchart { 2119*9f257f50SLaurent Pinchart struct imx7_csi *csi = v4l2_get_subdevdata(sd); 2120*9f257f50SLaurent Pinchart 2121*9f257f50SLaurent Pinchart imx7_csi_video_unregister(csi); 2122*9f257f50SLaurent Pinchart } 2123*9f257f50SLaurent Pinchart 2124*9f257f50SLaurent Pinchart static const struct v4l2_subdev_video_ops imx7_csi_video_ops = { 2125*9f257f50SLaurent Pinchart .s_stream = imx7_csi_s_stream, 2126*9f257f50SLaurent Pinchart }; 2127*9f257f50SLaurent Pinchart 2128*9f257f50SLaurent Pinchart static const struct v4l2_subdev_pad_ops imx7_csi_pad_ops = { 2129*9f257f50SLaurent Pinchart .init_cfg = imx7_csi_init_cfg, 2130*9f257f50SLaurent Pinchart .enum_mbus_code = imx7_csi_enum_mbus_code, 2131*9f257f50SLaurent Pinchart .get_fmt = imx7_csi_get_fmt, 2132*9f257f50SLaurent Pinchart .set_fmt = imx7_csi_set_fmt, 2133*9f257f50SLaurent Pinchart .link_validate = imx7_csi_pad_link_validate, 2134*9f257f50SLaurent Pinchart }; 2135*9f257f50SLaurent Pinchart 2136*9f257f50SLaurent Pinchart static const struct v4l2_subdev_ops imx7_csi_subdev_ops = { 2137*9f257f50SLaurent Pinchart .video = &imx7_csi_video_ops, 2138*9f257f50SLaurent Pinchart .pad = &imx7_csi_pad_ops, 2139*9f257f50SLaurent Pinchart }; 2140*9f257f50SLaurent Pinchart 2141*9f257f50SLaurent Pinchart static const struct v4l2_subdev_internal_ops imx7_csi_internal_ops = { 2142*9f257f50SLaurent Pinchart .registered = imx7_csi_registered, 2143*9f257f50SLaurent Pinchart .unregistered = imx7_csi_unregistered, 2144*9f257f50SLaurent Pinchart }; 2145*9f257f50SLaurent Pinchart 2146*9f257f50SLaurent Pinchart /* ----------------------------------------------------------------------------- 2147*9f257f50SLaurent Pinchart * Media Entity Operations 2148*9f257f50SLaurent Pinchart */ 2149*9f257f50SLaurent Pinchart 2150*9f257f50SLaurent Pinchart static const struct media_entity_operations imx7_csi_entity_ops = { 2151*9f257f50SLaurent Pinchart .link_validate = v4l2_subdev_link_validate, 2152*9f257f50SLaurent Pinchart .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, 2153*9f257f50SLaurent Pinchart }; 2154*9f257f50SLaurent Pinchart 2155*9f257f50SLaurent Pinchart /* ----------------------------------------------------------------------------- 2156*9f257f50SLaurent Pinchart * Probe & Remove 2157*9f257f50SLaurent Pinchart */ 2158*9f257f50SLaurent Pinchart 2159*9f257f50SLaurent Pinchart static int imx7_csi_notify_bound(struct v4l2_async_notifier *notifier, 2160*9f257f50SLaurent Pinchart struct v4l2_subdev *sd, 2161*9f257f50SLaurent Pinchart struct v4l2_async_subdev *asd) 2162*9f257f50SLaurent Pinchart { 2163*9f257f50SLaurent Pinchart struct imx7_csi *csi = imx7_csi_notifier_to_dev(notifier); 2164*9f257f50SLaurent Pinchart struct media_pad *sink = &csi->sd.entity.pads[IMX7_CSI_PAD_SINK]; 2165*9f257f50SLaurent Pinchart 2166*9f257f50SLaurent Pinchart csi->src_sd = sd; 2167*9f257f50SLaurent Pinchart 2168*9f257f50SLaurent Pinchart return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED | 2169*9f257f50SLaurent Pinchart MEDIA_LNK_FL_IMMUTABLE); 2170*9f257f50SLaurent Pinchart } 2171*9f257f50SLaurent Pinchart 2172*9f257f50SLaurent Pinchart static int imx7_csi_notify_complete(struct v4l2_async_notifier *notifier) 2173*9f257f50SLaurent Pinchart { 2174*9f257f50SLaurent Pinchart struct imx7_csi *csi = imx7_csi_notifier_to_dev(notifier); 2175*9f257f50SLaurent Pinchart 2176*9f257f50SLaurent Pinchart return v4l2_device_register_subdev_nodes(&csi->v4l2_dev); 2177*9f257f50SLaurent Pinchart } 2178*9f257f50SLaurent Pinchart 2179*9f257f50SLaurent Pinchart static const struct v4l2_async_notifier_operations imx7_csi_notify_ops = { 2180*9f257f50SLaurent Pinchart .bound = imx7_csi_notify_bound, 2181*9f257f50SLaurent Pinchart .complete = imx7_csi_notify_complete, 2182*9f257f50SLaurent Pinchart }; 2183*9f257f50SLaurent Pinchart 2184*9f257f50SLaurent Pinchart static int imx7_csi_async_register(struct imx7_csi *csi) 2185*9f257f50SLaurent Pinchart { 2186*9f257f50SLaurent Pinchart struct v4l2_async_subdev *asd; 2187*9f257f50SLaurent Pinchart struct fwnode_handle *ep; 2188*9f257f50SLaurent Pinchart int ret; 2189*9f257f50SLaurent Pinchart 2190*9f257f50SLaurent Pinchart v4l2_async_nf_init(&csi->notifier); 2191*9f257f50SLaurent Pinchart 2192*9f257f50SLaurent Pinchart ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0, 2193*9f257f50SLaurent Pinchart FWNODE_GRAPH_ENDPOINT_NEXT); 2194*9f257f50SLaurent Pinchart if (ep) { 2195*9f257f50SLaurent Pinchart asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep, 2196*9f257f50SLaurent Pinchart struct v4l2_async_subdev); 2197*9f257f50SLaurent Pinchart 2198*9f257f50SLaurent Pinchart fwnode_handle_put(ep); 2199*9f257f50SLaurent Pinchart 2200*9f257f50SLaurent Pinchart if (IS_ERR(asd)) { 2201*9f257f50SLaurent Pinchart ret = PTR_ERR(asd); 2202*9f257f50SLaurent Pinchart /* OK if asd already exists */ 2203*9f257f50SLaurent Pinchart if (ret != -EEXIST) 2204*9f257f50SLaurent Pinchart return ret; 2205*9f257f50SLaurent Pinchart } 2206*9f257f50SLaurent Pinchart } 2207*9f257f50SLaurent Pinchart 2208*9f257f50SLaurent Pinchart csi->notifier.ops = &imx7_csi_notify_ops; 2209*9f257f50SLaurent Pinchart 2210*9f257f50SLaurent Pinchart ret = v4l2_async_nf_register(&csi->v4l2_dev, &csi->notifier); 2211*9f257f50SLaurent Pinchart if (ret) 2212*9f257f50SLaurent Pinchart return ret; 2213*9f257f50SLaurent Pinchart 2214*9f257f50SLaurent Pinchart return 0; 2215*9f257f50SLaurent Pinchart } 2216*9f257f50SLaurent Pinchart 2217*9f257f50SLaurent Pinchart static void imx7_csi_media_cleanup(struct imx7_csi *csi) 2218*9f257f50SLaurent Pinchart { 2219*9f257f50SLaurent Pinchart v4l2_device_unregister(&csi->v4l2_dev); 2220*9f257f50SLaurent Pinchart media_device_unregister(&csi->mdev); 2221*9f257f50SLaurent Pinchart media_device_cleanup(&csi->mdev); 2222*9f257f50SLaurent Pinchart } 2223*9f257f50SLaurent Pinchart 2224*9f257f50SLaurent Pinchart static const struct media_device_ops imx7_csi_media_ops = { 2225*9f257f50SLaurent Pinchart .link_notify = v4l2_pipeline_link_notify, 2226*9f257f50SLaurent Pinchart }; 2227*9f257f50SLaurent Pinchart 2228*9f257f50SLaurent Pinchart static int imx7_csi_media_dev_init(struct imx7_csi *csi) 2229*9f257f50SLaurent Pinchart { 2230*9f257f50SLaurent Pinchart int ret; 2231*9f257f50SLaurent Pinchart 2232*9f257f50SLaurent Pinchart strscpy(csi->mdev.model, "imx-media", sizeof(csi->mdev.model)); 2233*9f257f50SLaurent Pinchart csi->mdev.ops = &imx7_csi_media_ops; 2234*9f257f50SLaurent Pinchart csi->mdev.dev = csi->dev; 2235*9f257f50SLaurent Pinchart 2236*9f257f50SLaurent Pinchart csi->v4l2_dev.mdev = &csi->mdev; 2237*9f257f50SLaurent Pinchart strscpy(csi->v4l2_dev.name, "imx-media", 2238*9f257f50SLaurent Pinchart sizeof(csi->v4l2_dev.name)); 2239*9f257f50SLaurent Pinchart snprintf(csi->mdev.bus_info, sizeof(csi->mdev.bus_info), 2240*9f257f50SLaurent Pinchart "platform:%s", dev_name(csi->mdev.dev)); 2241*9f257f50SLaurent Pinchart 2242*9f257f50SLaurent Pinchart media_device_init(&csi->mdev); 2243*9f257f50SLaurent Pinchart 2244*9f257f50SLaurent Pinchart ret = v4l2_device_register(csi->dev, &csi->v4l2_dev); 2245*9f257f50SLaurent Pinchart if (ret < 0) { 2246*9f257f50SLaurent Pinchart v4l2_err(&csi->v4l2_dev, 2247*9f257f50SLaurent Pinchart "Failed to register v4l2_device: %d\n", ret); 2248*9f257f50SLaurent Pinchart goto cleanup; 2249*9f257f50SLaurent Pinchart } 2250*9f257f50SLaurent Pinchart 2251*9f257f50SLaurent Pinchart return 0; 2252*9f257f50SLaurent Pinchart 2253*9f257f50SLaurent Pinchart cleanup: 2254*9f257f50SLaurent Pinchart media_device_cleanup(&csi->mdev); 2255*9f257f50SLaurent Pinchart 2256*9f257f50SLaurent Pinchart return ret; 2257*9f257f50SLaurent Pinchart } 2258*9f257f50SLaurent Pinchart 2259*9f257f50SLaurent Pinchart static int imx7_csi_media_init(struct imx7_csi *csi) 2260*9f257f50SLaurent Pinchart { 2261*9f257f50SLaurent Pinchart unsigned int i; 2262*9f257f50SLaurent Pinchart int ret; 2263*9f257f50SLaurent Pinchart 2264*9f257f50SLaurent Pinchart /* add media device */ 2265*9f257f50SLaurent Pinchart ret = imx7_csi_media_dev_init(csi); 2266*9f257f50SLaurent Pinchart if (ret) 2267*9f257f50SLaurent Pinchart return ret; 2268*9f257f50SLaurent Pinchart 2269*9f257f50SLaurent Pinchart v4l2_subdev_init(&csi->sd, &imx7_csi_subdev_ops); 2270*9f257f50SLaurent Pinchart v4l2_set_subdevdata(&csi->sd, csi); 2271*9f257f50SLaurent Pinchart csi->sd.internal_ops = &imx7_csi_internal_ops; 2272*9f257f50SLaurent Pinchart csi->sd.entity.ops = &imx7_csi_entity_ops; 2273*9f257f50SLaurent Pinchart csi->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 2274*9f257f50SLaurent Pinchart csi->sd.dev = csi->dev; 2275*9f257f50SLaurent Pinchart csi->sd.owner = THIS_MODULE; 2276*9f257f50SLaurent Pinchart csi->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; 2277*9f257f50SLaurent Pinchart snprintf(csi->sd.name, sizeof(csi->sd.name), "csi"); 2278*9f257f50SLaurent Pinchart 2279*9f257f50SLaurent Pinchart for (i = 0; i < IMX7_CSI_PADS_NUM; i++) 2280*9f257f50SLaurent Pinchart csi->pad[i].flags = (i == IMX7_CSI_PAD_SINK) ? 2281*9f257f50SLaurent Pinchart MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; 2282*9f257f50SLaurent Pinchart 2283*9f257f50SLaurent Pinchart ret = media_entity_pads_init(&csi->sd.entity, IMX7_CSI_PADS_NUM, 2284*9f257f50SLaurent Pinchart csi->pad); 2285*9f257f50SLaurent Pinchart if (ret) 2286*9f257f50SLaurent Pinchart goto error; 2287*9f257f50SLaurent Pinchart 2288*9f257f50SLaurent Pinchart ret = v4l2_device_register_subdev(&csi->v4l2_dev, &csi->sd); 2289*9f257f50SLaurent Pinchart if (ret) 2290*9f257f50SLaurent Pinchart goto error; 2291*9f257f50SLaurent Pinchart 2292*9f257f50SLaurent Pinchart return 0; 2293*9f257f50SLaurent Pinchart 2294*9f257f50SLaurent Pinchart error: 2295*9f257f50SLaurent Pinchart imx7_csi_media_cleanup(csi); 2296*9f257f50SLaurent Pinchart return ret; 2297*9f257f50SLaurent Pinchart } 2298*9f257f50SLaurent Pinchart 2299*9f257f50SLaurent Pinchart static int imx7_csi_probe(struct platform_device *pdev) 2300*9f257f50SLaurent Pinchart { 2301*9f257f50SLaurent Pinchart struct device *dev = &pdev->dev; 2302*9f257f50SLaurent Pinchart struct imx7_csi *csi; 2303*9f257f50SLaurent Pinchart int ret; 2304*9f257f50SLaurent Pinchart 2305*9f257f50SLaurent Pinchart csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL); 2306*9f257f50SLaurent Pinchart if (!csi) 2307*9f257f50SLaurent Pinchart return -ENOMEM; 2308*9f257f50SLaurent Pinchart 2309*9f257f50SLaurent Pinchart csi->dev = dev; 2310*9f257f50SLaurent Pinchart platform_set_drvdata(pdev, csi); 2311*9f257f50SLaurent Pinchart 2312*9f257f50SLaurent Pinchart spin_lock_init(&csi->irqlock); 2313*9f257f50SLaurent Pinchart mutex_init(&csi->lock); 2314*9f257f50SLaurent Pinchart 2315*9f257f50SLaurent Pinchart /* Acquire resources and install interrupt handler. */ 2316*9f257f50SLaurent Pinchart csi->mclk = devm_clk_get(&pdev->dev, "mclk"); 2317*9f257f50SLaurent Pinchart if (IS_ERR(csi->mclk)) { 2318*9f257f50SLaurent Pinchart ret = PTR_ERR(csi->mclk); 2319*9f257f50SLaurent Pinchart dev_err(dev, "Failed to get mclk: %d", ret); 2320*9f257f50SLaurent Pinchart goto destroy_mutex; 2321*9f257f50SLaurent Pinchart } 2322*9f257f50SLaurent Pinchart 2323*9f257f50SLaurent Pinchart csi->irq = platform_get_irq(pdev, 0); 2324*9f257f50SLaurent Pinchart if (csi->irq < 0) { 2325*9f257f50SLaurent Pinchart ret = csi->irq; 2326*9f257f50SLaurent Pinchart goto destroy_mutex; 2327*9f257f50SLaurent Pinchart } 2328*9f257f50SLaurent Pinchart 2329*9f257f50SLaurent Pinchart csi->regbase = devm_platform_ioremap_resource(pdev, 0); 2330*9f257f50SLaurent Pinchart if (IS_ERR(csi->regbase)) { 2331*9f257f50SLaurent Pinchart ret = PTR_ERR(csi->regbase); 2332*9f257f50SLaurent Pinchart goto destroy_mutex; 2333*9f257f50SLaurent Pinchart } 2334*9f257f50SLaurent Pinchart 2335*9f257f50SLaurent Pinchart csi->model = (enum imx_csi_model)(uintptr_t)of_device_get_match_data(&pdev->dev); 2336*9f257f50SLaurent Pinchart 2337*9f257f50SLaurent Pinchart ret = devm_request_irq(dev, csi->irq, imx7_csi_irq_handler, 0, "csi", 2338*9f257f50SLaurent Pinchart (void *)csi); 2339*9f257f50SLaurent Pinchart if (ret < 0) { 2340*9f257f50SLaurent Pinchart dev_err(dev, "Request CSI IRQ failed.\n"); 2341*9f257f50SLaurent Pinchart goto destroy_mutex; 2342*9f257f50SLaurent Pinchart } 2343*9f257f50SLaurent Pinchart 2344*9f257f50SLaurent Pinchart /* Initialize all the media device infrastructure. */ 2345*9f257f50SLaurent Pinchart ret = imx7_csi_media_init(csi); 2346*9f257f50SLaurent Pinchart if (ret) 2347*9f257f50SLaurent Pinchart goto destroy_mutex; 2348*9f257f50SLaurent Pinchart 2349*9f257f50SLaurent Pinchart /* Set the default mbus formats. */ 2350*9f257f50SLaurent Pinchart ret = imx7_csi_init_cfg(&csi->sd, NULL); 2351*9f257f50SLaurent Pinchart if (ret) 2352*9f257f50SLaurent Pinchart goto media_cleanup; 2353*9f257f50SLaurent Pinchart 2354*9f257f50SLaurent Pinchart ret = imx7_csi_async_register(csi); 2355*9f257f50SLaurent Pinchart if (ret) 2356*9f257f50SLaurent Pinchart goto subdev_notifier_cleanup; 2357*9f257f50SLaurent Pinchart 2358*9f257f50SLaurent Pinchart return 0; 2359*9f257f50SLaurent Pinchart 2360*9f257f50SLaurent Pinchart subdev_notifier_cleanup: 2361*9f257f50SLaurent Pinchart v4l2_async_nf_unregister(&csi->notifier); 2362*9f257f50SLaurent Pinchart v4l2_async_nf_cleanup(&csi->notifier); 2363*9f257f50SLaurent Pinchart media_cleanup: 2364*9f257f50SLaurent Pinchart imx7_csi_media_cleanup(csi); 2365*9f257f50SLaurent Pinchart 2366*9f257f50SLaurent Pinchart destroy_mutex: 2367*9f257f50SLaurent Pinchart mutex_destroy(&csi->lock); 2368*9f257f50SLaurent Pinchart 2369*9f257f50SLaurent Pinchart return ret; 2370*9f257f50SLaurent Pinchart } 2371*9f257f50SLaurent Pinchart 2372*9f257f50SLaurent Pinchart static int imx7_csi_remove(struct platform_device *pdev) 2373*9f257f50SLaurent Pinchart { 2374*9f257f50SLaurent Pinchart struct imx7_csi *csi = platform_get_drvdata(pdev); 2375*9f257f50SLaurent Pinchart 2376*9f257f50SLaurent Pinchart imx7_csi_media_cleanup(csi); 2377*9f257f50SLaurent Pinchart 2378*9f257f50SLaurent Pinchart v4l2_async_nf_unregister(&csi->notifier); 2379*9f257f50SLaurent Pinchart v4l2_async_nf_cleanup(&csi->notifier); 2380*9f257f50SLaurent Pinchart v4l2_async_unregister_subdev(&csi->sd); 2381*9f257f50SLaurent Pinchart 2382*9f257f50SLaurent Pinchart mutex_destroy(&csi->lock); 2383*9f257f50SLaurent Pinchart 2384*9f257f50SLaurent Pinchart return 0; 2385*9f257f50SLaurent Pinchart } 2386*9f257f50SLaurent Pinchart 2387*9f257f50SLaurent Pinchart static const struct of_device_id imx7_csi_of_match[] = { 2388*9f257f50SLaurent Pinchart { .compatible = "fsl,imx8mq-csi", .data = (void *)IMX7_CSI_IMX8MQ }, 2389*9f257f50SLaurent Pinchart { .compatible = "fsl,imx7-csi", .data = (void *)IMX7_CSI_IMX7 }, 2390*9f257f50SLaurent Pinchart { .compatible = "fsl,imx6ul-csi", .data = (void *)IMX7_CSI_IMX7 }, 2391*9f257f50SLaurent Pinchart { }, 2392*9f257f50SLaurent Pinchart }; 2393*9f257f50SLaurent Pinchart MODULE_DEVICE_TABLE(of, imx7_csi_of_match); 2394*9f257f50SLaurent Pinchart 2395*9f257f50SLaurent Pinchart static struct platform_driver imx7_csi_driver = { 2396*9f257f50SLaurent Pinchart .probe = imx7_csi_probe, 2397*9f257f50SLaurent Pinchart .remove = imx7_csi_remove, 2398*9f257f50SLaurent Pinchart .driver = { 2399*9f257f50SLaurent Pinchart .of_match_table = imx7_csi_of_match, 2400*9f257f50SLaurent Pinchart .name = "imx7-csi", 2401*9f257f50SLaurent Pinchart }, 2402*9f257f50SLaurent Pinchart }; 2403*9f257f50SLaurent Pinchart module_platform_driver(imx7_csi_driver); 2404*9f257f50SLaurent Pinchart 2405*9f257f50SLaurent Pinchart MODULE_DESCRIPTION("i.MX7 CSI subdev driver"); 2406*9f257f50SLaurent Pinchart MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>"); 2407*9f257f50SLaurent Pinchart MODULE_LICENSE("GPL v2"); 2408*9f257f50SLaurent Pinchart MODULE_ALIAS("platform:imx7-csi"); 2409