1*f0d2d806SKeke Li // SPDX-License-Identifier: (GPL-2.0-only OR MIT) 2*f0d2d806SKeke Li /* 3*f0d2d806SKeke Li * Copyright (C) 2024 Amlogic, Inc. All rights reserved 4*f0d2d806SKeke Li */ 5*f0d2d806SKeke Li 6*f0d2d806SKeke Li #include <linux/clk.h> 7*f0d2d806SKeke Li #include <linux/device.h> 8*f0d2d806SKeke Li #include <linux/module.h> 9*f0d2d806SKeke Li #include <linux/mutex.h> 10*f0d2d806SKeke Li #include <linux/platform_device.h> 11*f0d2d806SKeke Li #include <linux/pm_runtime.h> 12*f0d2d806SKeke Li 13*f0d2d806SKeke Li #include <media/mipi-csi2.h> 14*f0d2d806SKeke Li #include <media/v4l2-async.h> 15*f0d2d806SKeke Li #include <media/v4l2-common.h> 16*f0d2d806SKeke Li #include <media/v4l2-device.h> 17*f0d2d806SKeke Li #include <media/v4l2-fwnode.h> 18*f0d2d806SKeke Li #include <media/v4l2-mc.h> 19*f0d2d806SKeke Li #include <media/v4l2-subdev.h> 20*f0d2d806SKeke Li 21*f0d2d806SKeke Li /* 22*f0d2d806SKeke Li * Adapter Block Diagram 23*f0d2d806SKeke Li * --------------------- 24*f0d2d806SKeke Li * 25*f0d2d806SKeke Li * +--------------------------------------------+ 26*f0d2d806SKeke Li * | Adapter | 27*f0d2d806SKeke Li * |--------------------------------------------| 28*f0d2d806SKeke Li * +------------+ | | | | | +-----+ 29*f0d2d806SKeke Li * | MIPI CSI-2 |--->| Frontend -> DDR_RD0 -> PIXEL0 -> ALIGNMENT |--->| ISP | 30*f0d2d806SKeke Li * +------------+ | | | | | +-----+ 31*f0d2d806SKeke Li * +--------------------------------------------+ 32*f0d2d806SKeke Li * 33*f0d2d806SKeke Li */ 34*f0d2d806SKeke Li 35*f0d2d806SKeke Li /* C3 adapter submodule definition */ 36*f0d2d806SKeke Li enum { 37*f0d2d806SKeke Li SUBMD_TOP, 38*f0d2d806SKeke Li SUBMD_FD, 39*f0d2d806SKeke Li SUBMD_RD, 40*f0d2d806SKeke Li }; 41*f0d2d806SKeke Li 42*f0d2d806SKeke Li #define ADAP_SUBMD_MASK GENMASK(17, 16) 43*f0d2d806SKeke Li #define ADAP_SUBMD_SHIFT 16 44*f0d2d806SKeke Li #define ADAP_SUBMD(x) (((x) & (ADAP_SUBMD_MASK)) >> (ADAP_SUBMD_SHIFT)) 45*f0d2d806SKeke Li #define ADAP_REG_ADDR_MASK GENMASK(15, 0) 46*f0d2d806SKeke Li #define ADAP_REG_ADDR(x) ((x) & (ADAP_REG_ADDR_MASK)) 47*f0d2d806SKeke Li #define ADAP_REG_T(x) ((SUBMD_TOP << ADAP_SUBMD_SHIFT) | (x)) 48*f0d2d806SKeke Li #define ADAP_REG_F(x) ((SUBMD_FD << ADAP_SUBMD_SHIFT) | (x)) 49*f0d2d806SKeke Li #define ADAP_REG_R(x) ((SUBMD_RD << ADAP_SUBMD_SHIFT) | (x)) 50*f0d2d806SKeke Li 51*f0d2d806SKeke Li #define MIPI_ADAP_CLOCK_NUM_MAX 3 52*f0d2d806SKeke Li #define MIPI_ADAP_SUBDEV_NAME "c3-mipi-adapter" 53*f0d2d806SKeke Li 54*f0d2d806SKeke Li /* C3 MIPI adapter TOP register */ 55*f0d2d806SKeke Li #define MIPI_TOP_CTRL0 ADAP_REG_T(0x00) 56*f0d2d806SKeke Li #define MIPI_TOP_CTRL0_RST_ADAPTER_MASK BIT(1) 57*f0d2d806SKeke Li #define MIPI_TOP_CTRL0_RST_ADAPTER_APPLY BIT(1) 58*f0d2d806SKeke Li #define MIPI_TOP_CTRL0_RST_ADAPTER_EXIT (0 << 1) 59*f0d2d806SKeke Li 60*f0d2d806SKeke Li #define MIPI_ADAPT_DE_CTRL0 ADAP_REG_T(0x40) 61*f0d2d806SKeke Li #define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_MASK BIT(3) 62*f0d2d806SKeke Li #define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_EN BIT(3) 63*f0d2d806SKeke Li #define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_DIS (0 << 3) 64*f0d2d806SKeke Li #define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_MASK BIT(7) 65*f0d2d806SKeke Li #define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_EN BIT(7) 66*f0d2d806SKeke Li #define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_DIS (0 << 7) 67*f0d2d806SKeke Li 68*f0d2d806SKeke Li /* C3 MIPI adapter FRONTEND register */ 69*f0d2d806SKeke Li #define CSI2_CLK_RESET ADAP_REG_F(0x00) 70*f0d2d806SKeke Li #define CSI2_CLK_RESET_SW_RESET_MASK BIT(0) 71*f0d2d806SKeke Li #define CSI2_CLK_RESET_SW_RESET_APPLY BIT(0) 72*f0d2d806SKeke Li #define CSI2_CLK_RESET_SW_RESET_RELEASE (0 << 0) 73*f0d2d806SKeke Li #define CSI2_CLK_RESET_CLK_ENABLE_MASK BIT(1) 74*f0d2d806SKeke Li #define CSI2_CLK_RESET_CLK_ENABLE_EN BIT(1) 75*f0d2d806SKeke Li #define CSI2_CLK_RESET_CLK_ENABLE_DIS (0 << 1) 76*f0d2d806SKeke Li 77*f0d2d806SKeke Li #define CSI2_GEN_CTRL0 ADAP_REG_F(0x04) 78*f0d2d806SKeke Li #define CSI2_GEN_CTRL0_VC0_MASK BIT(0) 79*f0d2d806SKeke Li #define CSI2_GEN_CTRL0_VC0_EN BIT(0) 80*f0d2d806SKeke Li #define CSI2_GEN_CTRL0_VC0_DIS (0 << 0) 81*f0d2d806SKeke Li #define CSI2_GEN_CTRL0_ENABLE_PACKETS_MASK GENMASK(20, 16) 82*f0d2d806SKeke Li #define CSI2_GEN_CTRL0_ENABLE_PACKETS_RAW BIT(16) 83*f0d2d806SKeke Li #define CSI2_GEN_CTRL0_ENABLE_PACKETS_YUV (2 << 16) 84*f0d2d806SKeke Li 85*f0d2d806SKeke Li #define CSI2_X_START_END_ISP ADAP_REG_F(0x0c) 86*f0d2d806SKeke Li #define CSI2_X_START_END_ISP_X_START_MASK GENMASK(15, 0) 87*f0d2d806SKeke Li #define CSI2_X_START_END_ISP_X_START(x) ((x) << 0) 88*f0d2d806SKeke Li #define CSI2_X_START_END_ISP_X_END_MASK GENMASK(31, 16) 89*f0d2d806SKeke Li #define CSI2_X_START_END_ISP_X_END(x) (((x) - 1) << 16) 90*f0d2d806SKeke Li 91*f0d2d806SKeke Li #define CSI2_Y_START_END_ISP ADAP_REG_F(0x10) 92*f0d2d806SKeke Li #define CSI2_Y_START_END_ISP_Y_START_MASK GENMASK(15, 0) 93*f0d2d806SKeke Li #define CSI2_Y_START_END_ISP_Y_START(x) ((x) << 0) 94*f0d2d806SKeke Li #define CSI2_Y_START_END_ISP_Y_END_MASK GENMASK(31, 16) 95*f0d2d806SKeke Li #define CSI2_Y_START_END_ISP_Y_END(x) (((x) - 1) << 16) 96*f0d2d806SKeke Li 97*f0d2d806SKeke Li #define CSI2_VC_MODE ADAP_REG_F(0x1c) 98*f0d2d806SKeke Li #define CSI2_VC_MODE_VS_ISP_SEL_VC_MASK GENMASK(19, 16) 99*f0d2d806SKeke Li #define CSI2_VC_MODE_VS_ISP_SEL_VC_0 BIT(16) 100*f0d2d806SKeke Li #define CSI2_VC_MODE_VS_ISP_SEL_VC_1 (2 << 16) 101*f0d2d806SKeke Li #define CSI2_VC_MODE_VS_ISP_SEL_VC_2 (4 << 16) 102*f0d2d806SKeke Li #define CSI2_VC_MODE_VS_ISP_SEL_VC_3 (8 << 16) 103*f0d2d806SKeke Li #define CSI2_VC_MODE_HS_ISP_SEL_VC_MASK GENMASK(23, 20) 104*f0d2d806SKeke Li #define CSI2_VC_MODE_HS_ISP_SEL_VC_0 BIT(20) 105*f0d2d806SKeke Li #define CSI2_VC_MODE_HS_ISP_SEL_VC_1 (2 << 20) 106*f0d2d806SKeke Li #define CSI2_VC_MODE_HS_ISP_SEL_VC_2 (4 << 20) 107*f0d2d806SKeke Li #define CSI2_VC_MODE_HS_ISP_SEL_VC_3 (8 << 20) 108*f0d2d806SKeke Li 109*f0d2d806SKeke Li /* C3 MIPI adapter READER register */ 110*f0d2d806SKeke Li #define MIPI_ADAPT_DDR_RD0_CNTL0 ADAP_REG_R(0x00) 111*f0d2d806SKeke Li #define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN_MASK BIT(0) 112*f0d2d806SKeke Li #define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN BIT(0) 113*f0d2d806SKeke Li #define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_DIS (0 << 0) 114*f0d2d806SKeke Li 115*f0d2d806SKeke Li #define MIPI_ADAPT_DDR_RD0_CNTL1 ADAP_REG_R(0x04) 116*f0d2d806SKeke Li #define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_MASK GENMASK(31, 30) 117*f0d2d806SKeke Li #define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DIRECT_MODE (0 << 30) 118*f0d2d806SKeke Li #define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DDR_MODE BIT(30) 119*f0d2d806SKeke Li 120*f0d2d806SKeke Li #define MIPI_ADAPT_PIXEL0_CNTL0 ADAP_REG_R(0x80) 121*f0d2d806SKeke Li #define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_MASK GENMASK(17, 16) 122*f0d2d806SKeke Li #define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DDR (0 << 16) 123*f0d2d806SKeke Li #define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DIRECT BIT(16) 124*f0d2d806SKeke Li #define MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE_MASK GENMASK(25, 20) 125*f0d2d806SKeke Li #define MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE(x) ((x) << 20) 126*f0d2d806SKeke Li #define MIPI_ADAPT_PIXEL0_CNTL0_START_EN_MASK BIT(31) 127*f0d2d806SKeke Li #define MIPI_ADAPT_PIXEL0_CNTL0_START_EN BIT(31) 128*f0d2d806SKeke Li 129*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL0 ADAP_REG_R(0x100) 130*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL0_H_NUM_MASK GENMASK(15, 0) 131*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL0_H_NUM(x) ((x) << 0) 132*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL0_V_NUM_MASK GENMASK(31, 16) 133*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL0_V_NUM(x) ((x) << 16) 134*f0d2d806SKeke Li 135*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL1 ADAP_REG_R(0x104) 136*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL1_HPE_NUM_MASK GENMASK(31, 16) 137*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL1_HPE_NUM(x) ((x) << 16) 138*f0d2d806SKeke Li 139*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL2 ADAP_REG_R(0x108) 140*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL2_VPE_NUM_MASK GENMASK(31, 16) 141*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL2_VPE_NUM(x) ((x) << 16) 142*f0d2d806SKeke Li 143*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL6 ADAP_REG_R(0x118) 144*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL6_PATH0_EN_MASK BIT(0) 145*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL6_PATH0_EN BIT(0) 146*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL6_PATH0_DIS (0 << 0) 147*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_MASK BIT(4) 148*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DDR (0 << 4) 149*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DIRECT BIT(4) 150*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL6_DATA0_EN_MASK BIT(12) 151*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL6_DATA0_EN BIT(12) 152*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL6_DATA0_DIS (0 << 12) 153*f0d2d806SKeke Li 154*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL8 ADAP_REG_R(0x120) 155*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_MASK BIT(5) 156*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_EN BIT(5) 157*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_DIS (0 << 5) 158*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL8_EXCEED_DIS_MASK BIT(12) 159*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL8_EXCEED_HOLD (0 << 12) 160*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL8_EXCEED_NOT_HOLD BIT(12) 161*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL8_START_EN_MASK BIT(31) 162*f0d2d806SKeke Li #define MIPI_ADAPT_ALIG_CNTL8_START_EN BIT(31) 163*f0d2d806SKeke Li 164*f0d2d806SKeke Li #define MIPI_ADAP_MAX_WIDTH 2888 165*f0d2d806SKeke Li #define MIPI_ADAP_MIN_WIDTH 160 166*f0d2d806SKeke Li #define MIPI_ADAP_MAX_HEIGHT 2240 167*f0d2d806SKeke Li #define MIPI_ADAP_MIN_HEIGHT 120 168*f0d2d806SKeke Li #define MIPI_ADAP_DEFAULT_WIDTH 1920 169*f0d2d806SKeke Li #define MIPI_ADAP_DEFAULT_HEIGHT 1080 170*f0d2d806SKeke Li #define MIPI_ADAP_DEFAULT_FMT MEDIA_BUS_FMT_SRGGB10_1X10 171*f0d2d806SKeke Li 172*f0d2d806SKeke Li /* C3 MIPI adapter pad list */ 173*f0d2d806SKeke Li enum { 174*f0d2d806SKeke Li C3_MIPI_ADAP_PAD_SINK, 175*f0d2d806SKeke Li C3_MIPI_ADAP_PAD_SRC, 176*f0d2d806SKeke Li C3_MIPI_ADAP_PAD_MAX 177*f0d2d806SKeke Li }; 178*f0d2d806SKeke Li 179*f0d2d806SKeke Li /* 180*f0d2d806SKeke Li * struct c3_adap_info - mipi adapter information 181*f0d2d806SKeke Li * 182*f0d2d806SKeke Li * @clocks: array of mipi adapter clock names 183*f0d2d806SKeke Li * @clock_num: actual clock number 184*f0d2d806SKeke Li */ 185*f0d2d806SKeke Li struct c3_adap_info { 186*f0d2d806SKeke Li char *clocks[MIPI_ADAP_CLOCK_NUM_MAX]; 187*f0d2d806SKeke Li u32 clock_num; 188*f0d2d806SKeke Li }; 189*f0d2d806SKeke Li 190*f0d2d806SKeke Li /* 191*f0d2d806SKeke Li * struct c3_adap_device - mipi adapter platform device 192*f0d2d806SKeke Li * 193*f0d2d806SKeke Li * @dev: pointer to the struct device 194*f0d2d806SKeke Li * @top: mipi adapter top register address 195*f0d2d806SKeke Li * @fd: mipi adapter frontend register address 196*f0d2d806SKeke Li * @rd: mipi adapter reader register address 197*f0d2d806SKeke Li * @clks: array of MIPI adapter clocks 198*f0d2d806SKeke Li * @sd: mipi adapter sub-device 199*f0d2d806SKeke Li * @pads: mipi adapter sub-device pads 200*f0d2d806SKeke Li * @notifier: notifier to register on the v4l2-async API 201*f0d2d806SKeke Li * @src_sd: source sub-device pad 202*f0d2d806SKeke Li * @info: version-specific MIPI adapter information 203*f0d2d806SKeke Li */ 204*f0d2d806SKeke Li struct c3_adap_device { 205*f0d2d806SKeke Li struct device *dev; 206*f0d2d806SKeke Li void __iomem *top; 207*f0d2d806SKeke Li void __iomem *fd; 208*f0d2d806SKeke Li void __iomem *rd; 209*f0d2d806SKeke Li struct clk_bulk_data clks[MIPI_ADAP_CLOCK_NUM_MAX]; 210*f0d2d806SKeke Li 211*f0d2d806SKeke Li struct v4l2_subdev sd; 212*f0d2d806SKeke Li struct media_pad pads[C3_MIPI_ADAP_PAD_MAX]; 213*f0d2d806SKeke Li struct v4l2_async_notifier notifier; 214*f0d2d806SKeke Li struct media_pad *src_pad; 215*f0d2d806SKeke Li 216*f0d2d806SKeke Li const struct c3_adap_info *info; 217*f0d2d806SKeke Li }; 218*f0d2d806SKeke Li 219*f0d2d806SKeke Li /* Format helpers */ 220*f0d2d806SKeke Li 221*f0d2d806SKeke Li struct c3_adap_pix_format { 222*f0d2d806SKeke Li u32 code; 223*f0d2d806SKeke Li u8 type; 224*f0d2d806SKeke Li }; 225*f0d2d806SKeke Li 226*f0d2d806SKeke Li static const struct c3_adap_pix_format c3_mipi_adap_formats[] = { 227*f0d2d806SKeke Li { MEDIA_BUS_FMT_SBGGR10_1X10, MIPI_CSI2_DT_RAW10 }, 228*f0d2d806SKeke Li { MEDIA_BUS_FMT_SGBRG10_1X10, MIPI_CSI2_DT_RAW10 }, 229*f0d2d806SKeke Li { MEDIA_BUS_FMT_SGRBG10_1X10, MIPI_CSI2_DT_RAW10 }, 230*f0d2d806SKeke Li { MEDIA_BUS_FMT_SRGGB10_1X10, MIPI_CSI2_DT_RAW10 }, 231*f0d2d806SKeke Li { MEDIA_BUS_FMT_SBGGR12_1X12, MIPI_CSI2_DT_RAW12 }, 232*f0d2d806SKeke Li { MEDIA_BUS_FMT_SGBRG12_1X12, MIPI_CSI2_DT_RAW12 }, 233*f0d2d806SKeke Li { MEDIA_BUS_FMT_SGRBG12_1X12, MIPI_CSI2_DT_RAW12 }, 234*f0d2d806SKeke Li { MEDIA_BUS_FMT_SRGGB12_1X12, MIPI_CSI2_DT_RAW12 }, 235*f0d2d806SKeke Li }; 236*f0d2d806SKeke Li 237*f0d2d806SKeke Li static const struct c3_adap_pix_format *c3_mipi_adap_find_format(u32 code) 238*f0d2d806SKeke Li { 239*f0d2d806SKeke Li for (unsigned int i = 0; i < ARRAY_SIZE(c3_mipi_adap_formats); i++) 240*f0d2d806SKeke Li if (code == c3_mipi_adap_formats[i].code) 241*f0d2d806SKeke Li return &c3_mipi_adap_formats[i]; 242*f0d2d806SKeke Li 243*f0d2d806SKeke Li return NULL; 244*f0d2d806SKeke Li } 245*f0d2d806SKeke Li 246*f0d2d806SKeke Li /* Hardware configuration */ 247*f0d2d806SKeke Li 248*f0d2d806SKeke Li static void c3_mipi_adap_update_bits(struct c3_adap_device *adap, u32 reg, 249*f0d2d806SKeke Li u32 mask, u32 val) 250*f0d2d806SKeke Li { 251*f0d2d806SKeke Li void __iomem *addr; 252*f0d2d806SKeke Li u32 orig, tmp; 253*f0d2d806SKeke Li 254*f0d2d806SKeke Li switch (ADAP_SUBMD(reg)) { 255*f0d2d806SKeke Li case SUBMD_TOP: 256*f0d2d806SKeke Li addr = adap->top + ADAP_REG_ADDR(reg); 257*f0d2d806SKeke Li break; 258*f0d2d806SKeke Li case SUBMD_FD: 259*f0d2d806SKeke Li addr = adap->fd + ADAP_REG_ADDR(reg); 260*f0d2d806SKeke Li break; 261*f0d2d806SKeke Li case SUBMD_RD: 262*f0d2d806SKeke Li addr = adap->rd + ADAP_REG_ADDR(reg); 263*f0d2d806SKeke Li break; 264*f0d2d806SKeke Li default: 265*f0d2d806SKeke Li dev_err(adap->dev, 266*f0d2d806SKeke Li "Invalid sub-module: %lu\n", ADAP_SUBMD(reg)); 267*f0d2d806SKeke Li return; 268*f0d2d806SKeke Li } 269*f0d2d806SKeke Li 270*f0d2d806SKeke Li orig = readl(addr); 271*f0d2d806SKeke Li tmp = orig & ~mask; 272*f0d2d806SKeke Li tmp |= val & mask; 273*f0d2d806SKeke Li 274*f0d2d806SKeke Li if (tmp != orig) 275*f0d2d806SKeke Li writel(tmp, addr); 276*f0d2d806SKeke Li } 277*f0d2d806SKeke Li 278*f0d2d806SKeke Li /* Configure adapter top sub module */ 279*f0d2d806SKeke Li static void c3_mipi_adap_cfg_top(struct c3_adap_device *adap) 280*f0d2d806SKeke Li { 281*f0d2d806SKeke Li /* Reset adapter */ 282*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_TOP_CTRL0, 283*f0d2d806SKeke Li MIPI_TOP_CTRL0_RST_ADAPTER_MASK, 284*f0d2d806SKeke Li MIPI_TOP_CTRL0_RST_ADAPTER_APPLY); 285*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_TOP_CTRL0, 286*f0d2d806SKeke Li MIPI_TOP_CTRL0_RST_ADAPTER_MASK, 287*f0d2d806SKeke Li MIPI_TOP_CTRL0_RST_ADAPTER_EXIT); 288*f0d2d806SKeke Li 289*f0d2d806SKeke Li /* Bypass decompress */ 290*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DE_CTRL0, 291*f0d2d806SKeke Li MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_MASK, 292*f0d2d806SKeke Li MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_EN); 293*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DE_CTRL0, 294*f0d2d806SKeke Li MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_MASK, 295*f0d2d806SKeke Li MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_EN); 296*f0d2d806SKeke Li } 297*f0d2d806SKeke Li 298*f0d2d806SKeke Li /* Configure adapter frontend sub module */ 299*f0d2d806SKeke Li static void c3_mipi_adap_cfg_frontend(struct c3_adap_device *adap, 300*f0d2d806SKeke Li struct v4l2_mbus_framefmt *fmt) 301*f0d2d806SKeke Li { 302*f0d2d806SKeke Li /* Reset frontend module */ 303*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET, 304*f0d2d806SKeke Li CSI2_CLK_RESET_SW_RESET_MASK, 305*f0d2d806SKeke Li CSI2_CLK_RESET_SW_RESET_APPLY); 306*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET, 307*f0d2d806SKeke Li CSI2_CLK_RESET_SW_RESET_MASK, 308*f0d2d806SKeke Li CSI2_CLK_RESET_SW_RESET_RELEASE); 309*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET, 310*f0d2d806SKeke Li CSI2_CLK_RESET_CLK_ENABLE_MASK, 311*f0d2d806SKeke Li CSI2_CLK_RESET_CLK_ENABLE_EN); 312*f0d2d806SKeke Li 313*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, CSI2_X_START_END_ISP, 314*f0d2d806SKeke Li CSI2_X_START_END_ISP_X_START_MASK, 315*f0d2d806SKeke Li CSI2_X_START_END_ISP_X_START(0)); 316*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, CSI2_X_START_END_ISP, 317*f0d2d806SKeke Li CSI2_X_START_END_ISP_X_END_MASK, 318*f0d2d806SKeke Li CSI2_X_START_END_ISP_X_END(fmt->width)); 319*f0d2d806SKeke Li 320*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, CSI2_Y_START_END_ISP, 321*f0d2d806SKeke Li CSI2_Y_START_END_ISP_Y_START_MASK, 322*f0d2d806SKeke Li CSI2_Y_START_END_ISP_Y_START(0)); 323*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, CSI2_Y_START_END_ISP, 324*f0d2d806SKeke Li CSI2_Y_START_END_ISP_Y_END_MASK, 325*f0d2d806SKeke Li CSI2_Y_START_END_ISP_Y_END(fmt->height)); 326*f0d2d806SKeke Li 327*f0d2d806SKeke Li /* Select VS and HS signal for direct path */ 328*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, CSI2_VC_MODE, 329*f0d2d806SKeke Li CSI2_VC_MODE_VS_ISP_SEL_VC_MASK, 330*f0d2d806SKeke Li CSI2_VC_MODE_VS_ISP_SEL_VC_0); 331*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, CSI2_VC_MODE, 332*f0d2d806SKeke Li CSI2_VC_MODE_HS_ISP_SEL_VC_MASK, 333*f0d2d806SKeke Li CSI2_VC_MODE_HS_ISP_SEL_VC_0); 334*f0d2d806SKeke Li 335*f0d2d806SKeke Li /* Enable to receive RAW packet */ 336*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, CSI2_GEN_CTRL0, 337*f0d2d806SKeke Li CSI2_GEN_CTRL0_ENABLE_PACKETS_MASK, 338*f0d2d806SKeke Li CSI2_GEN_CTRL0_ENABLE_PACKETS_RAW); 339*f0d2d806SKeke Li 340*f0d2d806SKeke Li /* Enable virtual channel 0 */ 341*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, CSI2_GEN_CTRL0, 342*f0d2d806SKeke Li CSI2_GEN_CTRL0_VC0_MASK, 343*f0d2d806SKeke Li CSI2_GEN_CTRL0_VC0_EN); 344*f0d2d806SKeke Li } 345*f0d2d806SKeke Li 346*f0d2d806SKeke Li static void c3_mipi_adap_cfg_rd0(struct c3_adap_device *adap) 347*f0d2d806SKeke Li { 348*f0d2d806SKeke Li /* Select direct mode for DDR_RD0 mode */ 349*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DDR_RD0_CNTL1, 350*f0d2d806SKeke Li MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_MASK, 351*f0d2d806SKeke Li MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DIRECT_MODE); 352*f0d2d806SKeke Li 353*f0d2d806SKeke Li /* Data can't bypass DDR_RD0 in direct mode, so enable DDR_RD0 here */ 354*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DDR_RD0_CNTL0, 355*f0d2d806SKeke Li MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN_MASK, 356*f0d2d806SKeke Li MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN); 357*f0d2d806SKeke Li } 358*f0d2d806SKeke Li 359*f0d2d806SKeke Li static void c3_mipi_adap_cfg_pixel0(struct c3_adap_device *adap, 360*f0d2d806SKeke Li struct v4l2_mbus_framefmt *fmt) 361*f0d2d806SKeke Li { 362*f0d2d806SKeke Li const struct c3_adap_pix_format *pix; 363*f0d2d806SKeke Li 364*f0d2d806SKeke Li pix = c3_mipi_adap_find_format(fmt->code); 365*f0d2d806SKeke Li 366*f0d2d806SKeke Li /* Set work mode and data type for PIXEL0 module */ 367*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0, 368*f0d2d806SKeke Li MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_MASK, 369*f0d2d806SKeke Li MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DIRECT); 370*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0, 371*f0d2d806SKeke Li MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE_MASK, 372*f0d2d806SKeke Li MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE(pix->type)); 373*f0d2d806SKeke Li 374*f0d2d806SKeke Li /* Start PIXEL0 module */ 375*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0, 376*f0d2d806SKeke Li MIPI_ADAPT_PIXEL0_CNTL0_START_EN_MASK, 377*f0d2d806SKeke Li MIPI_ADAPT_PIXEL0_CNTL0_START_EN); 378*f0d2d806SKeke Li } 379*f0d2d806SKeke Li 380*f0d2d806SKeke Li static void c3_mipi_adap_cfg_alig(struct c3_adap_device *adap, 381*f0d2d806SKeke Li struct v4l2_mbus_framefmt *fmt) 382*f0d2d806SKeke Li { 383*f0d2d806SKeke Li /* 384*f0d2d806SKeke Li * ISP hardware requires the number of horizonal blanks greater than 385*f0d2d806SKeke Li * 64 cycles, so adding 64 here. 386*f0d2d806SKeke Li */ 387*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL0, 388*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL0_H_NUM_MASK, 389*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL0_H_NUM(fmt->width + 64)); 390*f0d2d806SKeke Li 391*f0d2d806SKeke Li /* 392*f0d2d806SKeke Li * ISP hardware requires the number of vertical blanks greater than 393*f0d2d806SKeke Li * 40 lines, so adding 40 here. 394*f0d2d806SKeke Li */ 395*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL0, 396*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL0_V_NUM_MASK, 397*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL0_V_NUM(fmt->height + 40)); 398*f0d2d806SKeke Li 399*f0d2d806SKeke Li /* End pixel in a line */ 400*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL1, 401*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL1_HPE_NUM_MASK, 402*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL1_HPE_NUM(fmt->width)); 403*f0d2d806SKeke Li 404*f0d2d806SKeke Li /* End line in a frame */ 405*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL2, 406*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL2_VPE_NUM_MASK, 407*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL2_VPE_NUM(fmt->height)); 408*f0d2d806SKeke Li 409*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6, 410*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL6_PATH0_EN_MASK, 411*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL6_PATH0_EN); 412*f0d2d806SKeke Li 413*f0d2d806SKeke Li /* Select direct mode for ALIG module */ 414*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6, 415*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_MASK, 416*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DIRECT); 417*f0d2d806SKeke Li 418*f0d2d806SKeke Li /* Enable to send raw data */ 419*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6, 420*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL6_DATA0_EN_MASK, 421*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL6_DATA0_EN); 422*f0d2d806SKeke Li 423*f0d2d806SKeke Li /* Set continue mode and disable hold counter */ 424*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8, 425*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_MASK, 426*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_EN); 427*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8, 428*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL8_EXCEED_DIS_MASK, 429*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL8_EXCEED_NOT_HOLD); 430*f0d2d806SKeke Li 431*f0d2d806SKeke Li /* Start ALIG module */ 432*f0d2d806SKeke Li c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8, 433*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL8_START_EN_MASK, 434*f0d2d806SKeke Li MIPI_ADAPT_ALIG_CNTL8_START_EN); 435*f0d2d806SKeke Li } 436*f0d2d806SKeke Li 437*f0d2d806SKeke Li /* V4L2 subdev operations */ 438*f0d2d806SKeke Li 439*f0d2d806SKeke Li static int c3_mipi_adap_enable_streams(struct v4l2_subdev *sd, 440*f0d2d806SKeke Li struct v4l2_subdev_state *state, 441*f0d2d806SKeke Li u32 pad, u64 streams_mask) 442*f0d2d806SKeke Li { 443*f0d2d806SKeke Li struct c3_adap_device *adap = v4l2_get_subdevdata(sd); 444*f0d2d806SKeke Li struct v4l2_mbus_framefmt *fmt; 445*f0d2d806SKeke Li struct media_pad *sink_pad; 446*f0d2d806SKeke Li struct v4l2_subdev *src_sd; 447*f0d2d806SKeke Li int ret; 448*f0d2d806SKeke Li 449*f0d2d806SKeke Li sink_pad = &adap->pads[C3_MIPI_ADAP_PAD_SINK]; 450*f0d2d806SKeke Li adap->src_pad = media_pad_remote_pad_unique(sink_pad); 451*f0d2d806SKeke Li if (IS_ERR(adap->src_pad)) { 452*f0d2d806SKeke Li dev_dbg(adap->dev, "Failed to get source pad for MIPI adap\n"); 453*f0d2d806SKeke Li return -EPIPE; 454*f0d2d806SKeke Li } 455*f0d2d806SKeke Li 456*f0d2d806SKeke Li src_sd = media_entity_to_v4l2_subdev(adap->src_pad->entity); 457*f0d2d806SKeke Li 458*f0d2d806SKeke Li pm_runtime_resume_and_get(adap->dev); 459*f0d2d806SKeke Li 460*f0d2d806SKeke Li fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SINK); 461*f0d2d806SKeke Li 462*f0d2d806SKeke Li c3_mipi_adap_cfg_top(adap); 463*f0d2d806SKeke Li c3_mipi_adap_cfg_frontend(adap, fmt); 464*f0d2d806SKeke Li c3_mipi_adap_cfg_rd0(adap); 465*f0d2d806SKeke Li c3_mipi_adap_cfg_pixel0(adap, fmt); 466*f0d2d806SKeke Li c3_mipi_adap_cfg_alig(adap, fmt); 467*f0d2d806SKeke Li 468*f0d2d806SKeke Li ret = v4l2_subdev_enable_streams(src_sd, adap->src_pad->index, BIT(0)); 469*f0d2d806SKeke Li if (ret) { 470*f0d2d806SKeke Li pm_runtime_put(adap->dev); 471*f0d2d806SKeke Li return ret; 472*f0d2d806SKeke Li } 473*f0d2d806SKeke Li 474*f0d2d806SKeke Li return 0; 475*f0d2d806SKeke Li } 476*f0d2d806SKeke Li 477*f0d2d806SKeke Li static int c3_mipi_adap_disable_streams(struct v4l2_subdev *sd, 478*f0d2d806SKeke Li struct v4l2_subdev_state *state, 479*f0d2d806SKeke Li u32 pad, u64 streams_mask) 480*f0d2d806SKeke Li { 481*f0d2d806SKeke Li struct c3_adap_device *adap = v4l2_get_subdevdata(sd); 482*f0d2d806SKeke Li struct v4l2_subdev *src_sd; 483*f0d2d806SKeke Li 484*f0d2d806SKeke Li if (adap->src_pad) { 485*f0d2d806SKeke Li src_sd = media_entity_to_v4l2_subdev(adap->src_pad->entity); 486*f0d2d806SKeke Li v4l2_subdev_disable_streams(src_sd, adap->src_pad->index, 487*f0d2d806SKeke Li BIT(0)); 488*f0d2d806SKeke Li } 489*f0d2d806SKeke Li adap->src_pad = NULL; 490*f0d2d806SKeke Li 491*f0d2d806SKeke Li pm_runtime_put(adap->dev); 492*f0d2d806SKeke Li 493*f0d2d806SKeke Li return 0; 494*f0d2d806SKeke Li } 495*f0d2d806SKeke Li 496*f0d2d806SKeke Li static int c3_mipi_adap_enum_mbus_code(struct v4l2_subdev *sd, 497*f0d2d806SKeke Li struct v4l2_subdev_state *state, 498*f0d2d806SKeke Li struct v4l2_subdev_mbus_code_enum *code) 499*f0d2d806SKeke Li { 500*f0d2d806SKeke Li struct v4l2_mbus_framefmt *fmt; 501*f0d2d806SKeke Li 502*f0d2d806SKeke Li switch (code->pad) { 503*f0d2d806SKeke Li case C3_MIPI_ADAP_PAD_SINK: 504*f0d2d806SKeke Li if (code->index >= ARRAY_SIZE(c3_mipi_adap_formats)) 505*f0d2d806SKeke Li return -EINVAL; 506*f0d2d806SKeke Li 507*f0d2d806SKeke Li code->code = c3_mipi_adap_formats[code->index].code; 508*f0d2d806SKeke Li break; 509*f0d2d806SKeke Li case C3_MIPI_ADAP_PAD_SRC: 510*f0d2d806SKeke Li if (code->index) 511*f0d2d806SKeke Li return -EINVAL; 512*f0d2d806SKeke Li 513*f0d2d806SKeke Li fmt = v4l2_subdev_state_get_format(state, code->pad); 514*f0d2d806SKeke Li code->code = fmt->code; 515*f0d2d806SKeke Li break; 516*f0d2d806SKeke Li default: 517*f0d2d806SKeke Li return -EINVAL; 518*f0d2d806SKeke Li } 519*f0d2d806SKeke Li 520*f0d2d806SKeke Li return 0; 521*f0d2d806SKeke Li } 522*f0d2d806SKeke Li 523*f0d2d806SKeke Li static int c3_mipi_adap_set_fmt(struct v4l2_subdev *sd, 524*f0d2d806SKeke Li struct v4l2_subdev_state *state, 525*f0d2d806SKeke Li struct v4l2_subdev_format *format) 526*f0d2d806SKeke Li { 527*f0d2d806SKeke Li struct v4l2_mbus_framefmt *fmt; 528*f0d2d806SKeke Li const struct c3_adap_pix_format *pix_format; 529*f0d2d806SKeke Li 530*f0d2d806SKeke Li if (format->pad != C3_MIPI_ADAP_PAD_SINK) 531*f0d2d806SKeke Li return v4l2_subdev_get_fmt(sd, state, format); 532*f0d2d806SKeke Li 533*f0d2d806SKeke Li pix_format = c3_mipi_adap_find_format(format->format.code); 534*f0d2d806SKeke Li if (!pix_format) 535*f0d2d806SKeke Li pix_format = &c3_mipi_adap_formats[0]; 536*f0d2d806SKeke Li 537*f0d2d806SKeke Li fmt = v4l2_subdev_state_get_format(state, format->pad); 538*f0d2d806SKeke Li fmt->code = pix_format->code; 539*f0d2d806SKeke Li fmt->width = clamp_t(u32, format->format.width, 540*f0d2d806SKeke Li MIPI_ADAP_MIN_WIDTH, MIPI_ADAP_MAX_WIDTH); 541*f0d2d806SKeke Li fmt->height = clamp_t(u32, format->format.height, 542*f0d2d806SKeke Li MIPI_ADAP_MIN_HEIGHT, MIPI_ADAP_MAX_HEIGHT); 543*f0d2d806SKeke Li fmt->colorspace = V4L2_COLORSPACE_RAW; 544*f0d2d806SKeke Li fmt->xfer_func = V4L2_XFER_FUNC_NONE; 545*f0d2d806SKeke Li fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; 546*f0d2d806SKeke Li fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; 547*f0d2d806SKeke Li 548*f0d2d806SKeke Li format->format = *fmt; 549*f0d2d806SKeke Li 550*f0d2d806SKeke Li /* Synchronize the format to source pad */ 551*f0d2d806SKeke Li fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SRC); 552*f0d2d806SKeke Li *fmt = format->format; 553*f0d2d806SKeke Li 554*f0d2d806SKeke Li return 0; 555*f0d2d806SKeke Li } 556*f0d2d806SKeke Li 557*f0d2d806SKeke Li static int c3_mipi_adap_init_state(struct v4l2_subdev *sd, 558*f0d2d806SKeke Li struct v4l2_subdev_state *state) 559*f0d2d806SKeke Li { 560*f0d2d806SKeke Li struct v4l2_mbus_framefmt *sink_fmt; 561*f0d2d806SKeke Li struct v4l2_mbus_framefmt *src_fmt; 562*f0d2d806SKeke Li 563*f0d2d806SKeke Li sink_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SINK); 564*f0d2d806SKeke Li src_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SRC); 565*f0d2d806SKeke Li 566*f0d2d806SKeke Li sink_fmt->width = MIPI_ADAP_DEFAULT_WIDTH; 567*f0d2d806SKeke Li sink_fmt->height = MIPI_ADAP_DEFAULT_HEIGHT; 568*f0d2d806SKeke Li sink_fmt->field = V4L2_FIELD_NONE; 569*f0d2d806SKeke Li sink_fmt->code = MIPI_ADAP_DEFAULT_FMT; 570*f0d2d806SKeke Li sink_fmt->colorspace = V4L2_COLORSPACE_RAW; 571*f0d2d806SKeke Li sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE; 572*f0d2d806SKeke Li sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; 573*f0d2d806SKeke Li sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; 574*f0d2d806SKeke Li 575*f0d2d806SKeke Li *src_fmt = *sink_fmt; 576*f0d2d806SKeke Li 577*f0d2d806SKeke Li return 0; 578*f0d2d806SKeke Li } 579*f0d2d806SKeke Li 580*f0d2d806SKeke Li static const struct v4l2_subdev_pad_ops c3_mipi_adap_pad_ops = { 581*f0d2d806SKeke Li .enum_mbus_code = c3_mipi_adap_enum_mbus_code, 582*f0d2d806SKeke Li .get_fmt = v4l2_subdev_get_fmt, 583*f0d2d806SKeke Li .set_fmt = c3_mipi_adap_set_fmt, 584*f0d2d806SKeke Li .enable_streams = c3_mipi_adap_enable_streams, 585*f0d2d806SKeke Li .disable_streams = c3_mipi_adap_disable_streams, 586*f0d2d806SKeke Li }; 587*f0d2d806SKeke Li 588*f0d2d806SKeke Li static const struct v4l2_subdev_ops c3_mipi_adap_subdev_ops = { 589*f0d2d806SKeke Li .pad = &c3_mipi_adap_pad_ops, 590*f0d2d806SKeke Li }; 591*f0d2d806SKeke Li 592*f0d2d806SKeke Li static const struct v4l2_subdev_internal_ops c3_mipi_adap_internal_ops = { 593*f0d2d806SKeke Li .init_state = c3_mipi_adap_init_state, 594*f0d2d806SKeke Li }; 595*f0d2d806SKeke Li 596*f0d2d806SKeke Li /* Media entity operations */ 597*f0d2d806SKeke Li static const struct media_entity_operations c3_mipi_adap_entity_ops = { 598*f0d2d806SKeke Li .link_validate = v4l2_subdev_link_validate, 599*f0d2d806SKeke Li }; 600*f0d2d806SKeke Li 601*f0d2d806SKeke Li /* PM runtime */ 602*f0d2d806SKeke Li 603*f0d2d806SKeke Li static int c3_mipi_adap_runtime_suspend(struct device *dev) 604*f0d2d806SKeke Li { 605*f0d2d806SKeke Li struct c3_adap_device *adap = dev_get_drvdata(dev); 606*f0d2d806SKeke Li 607*f0d2d806SKeke Li clk_bulk_disable_unprepare(adap->info->clock_num, adap->clks); 608*f0d2d806SKeke Li 609*f0d2d806SKeke Li return 0; 610*f0d2d806SKeke Li } 611*f0d2d806SKeke Li 612*f0d2d806SKeke Li static int c3_mipi_adap_runtime_resume(struct device *dev) 613*f0d2d806SKeke Li { 614*f0d2d806SKeke Li struct c3_adap_device *adap = dev_get_drvdata(dev); 615*f0d2d806SKeke Li 616*f0d2d806SKeke Li return clk_bulk_prepare_enable(adap->info->clock_num, adap->clks); 617*f0d2d806SKeke Li } 618*f0d2d806SKeke Li 619*f0d2d806SKeke Li static const struct dev_pm_ops c3_mipi_adap_pm_ops = { 620*f0d2d806SKeke Li SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 621*f0d2d806SKeke Li pm_runtime_force_resume) 622*f0d2d806SKeke Li RUNTIME_PM_OPS(c3_mipi_adap_runtime_suspend, 623*f0d2d806SKeke Li c3_mipi_adap_runtime_resume, NULL) 624*f0d2d806SKeke Li }; 625*f0d2d806SKeke Li 626*f0d2d806SKeke Li /* Probe/remove & platform driver */ 627*f0d2d806SKeke Li 628*f0d2d806SKeke Li static int c3_mipi_adap_subdev_init(struct c3_adap_device *adap) 629*f0d2d806SKeke Li { 630*f0d2d806SKeke Li struct v4l2_subdev *sd = &adap->sd; 631*f0d2d806SKeke Li int ret; 632*f0d2d806SKeke Li 633*f0d2d806SKeke Li v4l2_subdev_init(sd, &c3_mipi_adap_subdev_ops); 634*f0d2d806SKeke Li sd->owner = THIS_MODULE; 635*f0d2d806SKeke Li sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 636*f0d2d806SKeke Li sd->internal_ops = &c3_mipi_adap_internal_ops; 637*f0d2d806SKeke Li snprintf(sd->name, sizeof(sd->name), "%s", MIPI_ADAP_SUBDEV_NAME); 638*f0d2d806SKeke Li 639*f0d2d806SKeke Li sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 640*f0d2d806SKeke Li sd->entity.ops = &c3_mipi_adap_entity_ops; 641*f0d2d806SKeke Li 642*f0d2d806SKeke Li sd->dev = adap->dev; 643*f0d2d806SKeke Li v4l2_set_subdevdata(sd, adap); 644*f0d2d806SKeke Li 645*f0d2d806SKeke Li adap->pads[C3_MIPI_ADAP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 646*f0d2d806SKeke Li adap->pads[C3_MIPI_ADAP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; 647*f0d2d806SKeke Li ret = media_entity_pads_init(&sd->entity, C3_MIPI_ADAP_PAD_MAX, 648*f0d2d806SKeke Li adap->pads); 649*f0d2d806SKeke Li if (ret) 650*f0d2d806SKeke Li return ret; 651*f0d2d806SKeke Li 652*f0d2d806SKeke Li ret = v4l2_subdev_init_finalize(sd); 653*f0d2d806SKeke Li if (ret) { 654*f0d2d806SKeke Li media_entity_cleanup(&sd->entity); 655*f0d2d806SKeke Li return ret; 656*f0d2d806SKeke Li } 657*f0d2d806SKeke Li 658*f0d2d806SKeke Li return 0; 659*f0d2d806SKeke Li } 660*f0d2d806SKeke Li 661*f0d2d806SKeke Li static void c3_mipi_adap_subdev_deinit(struct c3_adap_device *adap) 662*f0d2d806SKeke Li { 663*f0d2d806SKeke Li v4l2_subdev_cleanup(&adap->sd); 664*f0d2d806SKeke Li media_entity_cleanup(&adap->sd.entity); 665*f0d2d806SKeke Li } 666*f0d2d806SKeke Li 667*f0d2d806SKeke Li /* Subdev notifier register */ 668*f0d2d806SKeke Li static int c3_mipi_adap_notify_bound(struct v4l2_async_notifier *notifier, 669*f0d2d806SKeke Li struct v4l2_subdev *sd, 670*f0d2d806SKeke Li struct v4l2_async_connection *asc) 671*f0d2d806SKeke Li { 672*f0d2d806SKeke Li struct c3_adap_device *adap = v4l2_get_subdevdata(notifier->sd); 673*f0d2d806SKeke Li struct media_pad *sink = &adap->sd.entity.pads[C3_MIPI_ADAP_PAD_SINK]; 674*f0d2d806SKeke Li 675*f0d2d806SKeke Li return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED | 676*f0d2d806SKeke Li MEDIA_LNK_FL_IMMUTABLE); 677*f0d2d806SKeke Li } 678*f0d2d806SKeke Li 679*f0d2d806SKeke Li static const struct v4l2_async_notifier_operations c3_mipi_adap_notify_ops = { 680*f0d2d806SKeke Li .bound = c3_mipi_adap_notify_bound, 681*f0d2d806SKeke Li }; 682*f0d2d806SKeke Li 683*f0d2d806SKeke Li static int c3_mipi_adap_async_register(struct c3_adap_device *adap) 684*f0d2d806SKeke Li { 685*f0d2d806SKeke Li struct v4l2_async_connection *asc; 686*f0d2d806SKeke Li struct fwnode_handle *ep; 687*f0d2d806SKeke Li int ret; 688*f0d2d806SKeke Li 689*f0d2d806SKeke Li v4l2_async_subdev_nf_init(&adap->notifier, &adap->sd); 690*f0d2d806SKeke Li 691*f0d2d806SKeke Li ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(adap->dev), 0, 0, 692*f0d2d806SKeke Li FWNODE_GRAPH_ENDPOINT_NEXT); 693*f0d2d806SKeke Li if (!ep) 694*f0d2d806SKeke Li return -ENOTCONN; 695*f0d2d806SKeke Li 696*f0d2d806SKeke Li asc = v4l2_async_nf_add_fwnode_remote(&adap->notifier, ep, 697*f0d2d806SKeke Li struct v4l2_async_connection); 698*f0d2d806SKeke Li if (IS_ERR(asc)) { 699*f0d2d806SKeke Li ret = PTR_ERR(asc); 700*f0d2d806SKeke Li goto err_put_handle; 701*f0d2d806SKeke Li } 702*f0d2d806SKeke Li 703*f0d2d806SKeke Li adap->notifier.ops = &c3_mipi_adap_notify_ops; 704*f0d2d806SKeke Li ret = v4l2_async_nf_register(&adap->notifier); 705*f0d2d806SKeke Li if (ret) 706*f0d2d806SKeke Li goto err_cleanup_nf; 707*f0d2d806SKeke Li 708*f0d2d806SKeke Li ret = v4l2_async_register_subdev(&adap->sd); 709*f0d2d806SKeke Li if (ret) 710*f0d2d806SKeke Li goto err_unregister_nf; 711*f0d2d806SKeke Li 712*f0d2d806SKeke Li fwnode_handle_put(ep); 713*f0d2d806SKeke Li 714*f0d2d806SKeke Li return 0; 715*f0d2d806SKeke Li 716*f0d2d806SKeke Li err_unregister_nf: 717*f0d2d806SKeke Li v4l2_async_nf_unregister(&adap->notifier); 718*f0d2d806SKeke Li err_cleanup_nf: 719*f0d2d806SKeke Li v4l2_async_nf_cleanup(&adap->notifier); 720*f0d2d806SKeke Li err_put_handle: 721*f0d2d806SKeke Li fwnode_handle_put(ep); 722*f0d2d806SKeke Li return ret; 723*f0d2d806SKeke Li } 724*f0d2d806SKeke Li 725*f0d2d806SKeke Li static void c3_mipi_adap_async_unregister(struct c3_adap_device *adap) 726*f0d2d806SKeke Li { 727*f0d2d806SKeke Li v4l2_async_unregister_subdev(&adap->sd); 728*f0d2d806SKeke Li v4l2_async_nf_unregister(&adap->notifier); 729*f0d2d806SKeke Li v4l2_async_nf_cleanup(&adap->notifier); 730*f0d2d806SKeke Li } 731*f0d2d806SKeke Li 732*f0d2d806SKeke Li static int c3_mipi_adap_ioremap_resource(struct c3_adap_device *adap) 733*f0d2d806SKeke Li { 734*f0d2d806SKeke Li struct device *dev = adap->dev; 735*f0d2d806SKeke Li struct platform_device *pdev = to_platform_device(dev); 736*f0d2d806SKeke Li 737*f0d2d806SKeke Li adap->top = devm_platform_ioremap_resource_byname(pdev, "top"); 738*f0d2d806SKeke Li if (IS_ERR(adap->top)) 739*f0d2d806SKeke Li return PTR_ERR(adap->top); 740*f0d2d806SKeke Li 741*f0d2d806SKeke Li adap->fd = devm_platform_ioremap_resource_byname(pdev, "fd"); 742*f0d2d806SKeke Li if (IS_ERR(adap->fd)) 743*f0d2d806SKeke Li return PTR_ERR(adap->fd); 744*f0d2d806SKeke Li 745*f0d2d806SKeke Li adap->rd = devm_platform_ioremap_resource_byname(pdev, "rd"); 746*f0d2d806SKeke Li if (IS_ERR(adap->rd)) 747*f0d2d806SKeke Li return PTR_ERR(adap->rd); 748*f0d2d806SKeke Li 749*f0d2d806SKeke Li return 0; 750*f0d2d806SKeke Li } 751*f0d2d806SKeke Li 752*f0d2d806SKeke Li static int c3_mipi_adap_get_clocks(struct c3_adap_device *adap) 753*f0d2d806SKeke Li { 754*f0d2d806SKeke Li const struct c3_adap_info *info = adap->info; 755*f0d2d806SKeke Li 756*f0d2d806SKeke Li for (unsigned int i = 0; i < info->clock_num; i++) 757*f0d2d806SKeke Li adap->clks[i].id = info->clocks[i]; 758*f0d2d806SKeke Li 759*f0d2d806SKeke Li return devm_clk_bulk_get(adap->dev, info->clock_num, adap->clks); 760*f0d2d806SKeke Li } 761*f0d2d806SKeke Li 762*f0d2d806SKeke Li static int c3_mipi_adap_probe(struct platform_device *pdev) 763*f0d2d806SKeke Li { 764*f0d2d806SKeke Li struct device *dev = &pdev->dev; 765*f0d2d806SKeke Li struct c3_adap_device *adap; 766*f0d2d806SKeke Li int ret; 767*f0d2d806SKeke Li 768*f0d2d806SKeke Li adap = devm_kzalloc(dev, sizeof(*adap), GFP_KERNEL); 769*f0d2d806SKeke Li if (!adap) 770*f0d2d806SKeke Li return -ENOMEM; 771*f0d2d806SKeke Li 772*f0d2d806SKeke Li adap->info = of_device_get_match_data(dev); 773*f0d2d806SKeke Li adap->dev = dev; 774*f0d2d806SKeke Li 775*f0d2d806SKeke Li ret = c3_mipi_adap_ioremap_resource(adap); 776*f0d2d806SKeke Li if (ret) 777*f0d2d806SKeke Li return dev_err_probe(dev, ret, "Failed to ioremap resource\n"); 778*f0d2d806SKeke Li 779*f0d2d806SKeke Li ret = c3_mipi_adap_get_clocks(adap); 780*f0d2d806SKeke Li if (ret) 781*f0d2d806SKeke Li return dev_err_probe(dev, ret, "Failed to get clocks\n"); 782*f0d2d806SKeke Li 783*f0d2d806SKeke Li platform_set_drvdata(pdev, adap); 784*f0d2d806SKeke Li 785*f0d2d806SKeke Li pm_runtime_enable(dev); 786*f0d2d806SKeke Li 787*f0d2d806SKeke Li ret = c3_mipi_adap_subdev_init(adap); 788*f0d2d806SKeke Li if (ret) 789*f0d2d806SKeke Li goto err_disable_runtime_pm; 790*f0d2d806SKeke Li 791*f0d2d806SKeke Li ret = c3_mipi_adap_async_register(adap); 792*f0d2d806SKeke Li if (ret) 793*f0d2d806SKeke Li goto err_deinit_subdev; 794*f0d2d806SKeke Li 795*f0d2d806SKeke Li return 0; 796*f0d2d806SKeke Li 797*f0d2d806SKeke Li err_deinit_subdev: 798*f0d2d806SKeke Li c3_mipi_adap_subdev_deinit(adap); 799*f0d2d806SKeke Li err_disable_runtime_pm: 800*f0d2d806SKeke Li pm_runtime_disable(dev); 801*f0d2d806SKeke Li return ret; 802*f0d2d806SKeke Li }; 803*f0d2d806SKeke Li 804*f0d2d806SKeke Li static void c3_mipi_adap_remove(struct platform_device *pdev) 805*f0d2d806SKeke Li { 806*f0d2d806SKeke Li struct c3_adap_device *adap = platform_get_drvdata(pdev); 807*f0d2d806SKeke Li 808*f0d2d806SKeke Li c3_mipi_adap_async_unregister(adap); 809*f0d2d806SKeke Li c3_mipi_adap_subdev_deinit(adap); 810*f0d2d806SKeke Li 811*f0d2d806SKeke Li pm_runtime_disable(&pdev->dev); 812*f0d2d806SKeke Li }; 813*f0d2d806SKeke Li 814*f0d2d806SKeke Li static const struct c3_adap_info c3_mipi_adap_info = { 815*f0d2d806SKeke Li .clocks = {"vapb", "isp0"}, 816*f0d2d806SKeke Li .clock_num = 2 817*f0d2d806SKeke Li }; 818*f0d2d806SKeke Li 819*f0d2d806SKeke Li static const struct of_device_id c3_mipi_adap_of_match[] = { 820*f0d2d806SKeke Li { 821*f0d2d806SKeke Li .compatible = "amlogic,c3-mipi-adapter", 822*f0d2d806SKeke Li .data = &c3_mipi_adap_info 823*f0d2d806SKeke Li }, 824*f0d2d806SKeke Li { }, 825*f0d2d806SKeke Li }; 826*f0d2d806SKeke Li MODULE_DEVICE_TABLE(of, c3_mipi_adap_of_match); 827*f0d2d806SKeke Li 828*f0d2d806SKeke Li static struct platform_driver c3_mipi_adap_driver = { 829*f0d2d806SKeke Li .probe = c3_mipi_adap_probe, 830*f0d2d806SKeke Li .remove = c3_mipi_adap_remove, 831*f0d2d806SKeke Li .driver = { 832*f0d2d806SKeke Li .name = "c3-mipi-adapter", 833*f0d2d806SKeke Li .of_match_table = c3_mipi_adap_of_match, 834*f0d2d806SKeke Li .pm = pm_ptr(&c3_mipi_adap_pm_ops), 835*f0d2d806SKeke Li }, 836*f0d2d806SKeke Li }; 837*f0d2d806SKeke Li 838*f0d2d806SKeke Li module_platform_driver(c3_mipi_adap_driver); 839*f0d2d806SKeke Li 840*f0d2d806SKeke Li MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>"); 841*f0d2d806SKeke Li MODULE_DESCRIPTION("Amlogic C3 MIPI adapter"); 842*f0d2d806SKeke Li MODULE_LICENSE("GPL"); 843