// SPDX-License-Identifier: GPL-2.0 /* * Intersil ISL7998x analog to MIPI CSI-2 or BT.656 decoder driver. * * Copyright (C) 2018-2019 Marek Vasut * Copyright (C) 2021 Michael Tretter */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * This control allows to activate and deactivate the test pattern on * selected output channels. * This value is ISL7998x specific. */ #define V4L2_CID_TEST_PATTERN_CHANNELS (V4L2_CID_USER_ISL7998X_BASE + 0) /* * This control allows to specify the color of the test pattern. * This value is ISL7998x specific. */ #define V4L2_CID_TEST_PATTERN_COLOR (V4L2_CID_USER_ISL7998X_BASE + 1) /* * This control allows to specify the bar pattern in the test pattern. * This value is ISL7998x specific. */ #define V4L2_CID_TEST_PATTERN_BARS (V4L2_CID_USER_ISL7998X_BASE + 2) #define ISL7998X_INPUTS 4 #define ISL7998X_REG(page, reg) (((page) << 8) | (reg)) #define ISL7998X_REG_PN_SIZE 256 #define ISL7998X_REG_PN_BASE(n) ((n) * ISL7998X_REG_PN_SIZE) #define ISL7998X_REG_PX_DEC_PAGE(page) ISL7998X_REG((page), 0xff) #define ISL7998X_REG_PX_DEC_PAGE_MASK 0xf #define ISL7998X_REG_P0_PRODUCT_ID_CODE ISL7998X_REG(0, 0x00) #define ISL7998X_REG_P0_PRODUCT_REV_CODE ISL7998X_REG(0, 0x01) #define ISL7998X_REG_P0_SW_RESET_CTL ISL7998X_REG(0, 0x02) #define ISL7998X_REG_P0_IO_BUFFER_CTL ISL7998X_REG(0, 0x03) #define ISL7998X_REG_P0_IO_BUFFER_CTL_1_1 ISL7998X_REG(0, 0x04) #define ISL7998X_REG_P0_IO_PAD_PULL_EN_CTL ISL7998X_REG(0, 0x05) #define ISL7998X_REG_P0_IO_BUFFER_CTL_1_2 ISL7998X_REG(0, 0x06) #define ISL7998X_REG_P0_VIDEO_IN_CHAN_CTL ISL7998X_REG(0, 0x07) #define ISL7998X_REG_P0_CLK_CTL_1 ISL7998X_REG(0, 0x08) #define ISL7998X_REG_P0_CLK_CTL_2 ISL7998X_REG(0, 0x09) #define ISL7998X_REG_P0_CLK_CTL_3 ISL7998X_REG(0, 0x0a) #define ISL7998X_REG_P0_CLK_CTL_4 ISL7998X_REG(0, 0x0b) #define ISL7998X_REG_P0_MPP1_SYNC_CTL ISL7998X_REG(0, 0x0c) #define ISL7998X_REG_P0_MPP2_SYNC_CTL ISL7998X_REG(0, 0x0d) #define ISL7998X_REG_P0_IRQ_SYNC_CTL ISL7998X_REG(0, 0x0e) #define ISL7998X_REG_P0_INTERRUPT_STATUS ISL7998X_REG(0, 0x10) #define ISL7998X_REG_P0_CHAN_1_IRQ ISL7998X_REG(0, 0x11) #define ISL7998X_REG_P0_CHAN_2_IRQ ISL7998X_REG(0, 0x12) #define ISL7998X_REG_P0_CHAN_3_IRQ ISL7998X_REG(0, 0x13) #define ISL7998X_REG_P0_CHAN_4_IRQ ISL7998X_REG(0, 0x14) #define ISL7998X_REG_P0_SHORT_DIAG_IRQ ISL7998X_REG(0, 0x15) #define ISL7998X_REG_P0_CHAN_1_IRQ_EN ISL7998X_REG(0, 0x16) #define ISL7998X_REG_P0_CHAN_2_IRQ_EN ISL7998X_REG(0, 0x17) #define ISL7998X_REG_P0_CHAN_3_IRQ_EN ISL7998X_REG(0, 0x18) #define ISL7998X_REG_P0_CHAN_4_IRQ_EN ISL7998X_REG(0, 0x19) #define ISL7998X_REG_P0_SHORT_DIAG_IRQ_EN ISL7998X_REG(0, 0x1a) #define ISL7998X_REG_P0_CHAN_1_STATUS ISL7998X_REG(0, 0x1b) #define ISL7998X_REG_P0_CHAN_2_STATUS ISL7998X_REG(0, 0x1c) #define ISL7998X_REG_P0_CHAN_3_STATUS ISL7998X_REG(0, 0x1d) #define ISL7998X_REG_P0_CHAN_4_STATUS ISL7998X_REG(0, 0x1e) #define ISL7998X_REG_P0_SHORT_DIAG_STATUS ISL7998X_REG(0, 0x1f) #define ISL7998X_REG_P0_CLOCK_DELAY ISL7998X_REG(0, 0x20) #define ISL7998X_REG_PX_DEC_INPUT_FMT(pg) ISL7998X_REG((pg), 0x02) #define ISL7998X_REG_PX_DEC_STATUS_1(pg) ISL7998X_REG((pg), 0x03) #define ISL7998X_REG_PX_DEC_STATUS_1_VDLOSS BIT(7) #define ISL7998X_REG_PX_DEC_STATUS_1_HLOCK BIT(6) #define ISL7998X_REG_PX_DEC_STATUS_1_VLOCK BIT(3) #define ISL7998X_REG_PX_DEC_HS_DELAY_CTL(pg) ISL7998X_REG((pg), 0x04) #define ISL7998X_REG_PX_DEC_ANCTL(pg) ISL7998X_REG((pg), 0x06) #define ISL7998X_REG_PX_DEC_CROP_HI(pg) ISL7998X_REG((pg), 0x07) #define ISL7998X_REG_PX_DEC_VDELAY_LO(pg) ISL7998X_REG((pg), 0x08) #define ISL7998X_REG_PX_DEC_VACTIVE_LO(pg) ISL7998X_REG((pg), 0x09) #define ISL7998X_REG_PX_DEC_HDELAY_LO(pg) ISL7998X_REG((pg), 0x0a) #define ISL7998X_REG_PX_DEC_HACTIVE_LO(pg) ISL7998X_REG((pg), 0x0b) #define ISL7998X_REG_PX_DEC_CNTRL1(pg) ISL7998X_REG((pg), 0x0c) #define ISL7998X_REG_PX_DEC_CSC_CTL(pg) ISL7998X_REG((pg), 0x0d) #define ISL7998X_REG_PX_DEC_BRIGHT(pg) ISL7998X_REG((pg), 0x10) #define ISL7998X_REG_PX_DEC_CONTRAST(pg) ISL7998X_REG((pg), 0x11) #define ISL7998X_REG_PX_DEC_SHARPNESS(pg) ISL7998X_REG((pg), 0x12) #define ISL7998X_REG_PX_DEC_SAT_U(pg) ISL7998X_REG((pg), 0x13) #define ISL7998X_REG_PX_DEC_SAT_V(pg) ISL7998X_REG((pg), 0x14) #define ISL7998X_REG_PX_DEC_HUE(pg) ISL7998X_REG((pg), 0x15) #define ISL7998X_REG_PX_DEC_VERT_PEAK(pg) ISL7998X_REG((pg), 0x17) #define ISL7998X_REG_PX_DEC_CORING(pg) ISL7998X_REG((pg), 0x18) #define ISL7998X_REG_PX_DEC_SDT(pg) ISL7998X_REG((pg), 0x1c) #define ISL7998X_REG_PX_DEC_SDT_DET BIT(7) #define ISL7998X_REG_PX_DEC_SDT_NOW GENMASK(6, 4) #define ISL7998X_REG_PX_DEC_SDT_STANDARD GENMASK(2, 0) #define ISL7998X_REG_PX_DEC_SDT_STANDARD_NTSC_M 0 #define ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL 1 #define ISL7998X_REG_PX_DEC_SDT_STANDARD_SECAM 2 #define ISL7998X_REG_PX_DEC_SDT_STANDARD_NTSC_443 3 #define ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_M 4 #define ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_CN 5 #define ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_60 6 #define ISL7998X_REG_PX_DEC_SDT_STANDARD_UNKNOWN 7 #define ISL7998X_REG_PX_DEC_SDTR(pg) ISL7998X_REG((pg), 0x1d) #define ISL7998X_REG_PX_DEC_SDTR_ATSTART BIT(7) #define ISL7998X_REG_PX_DEC_CLMPG(pg) ISL7998X_REG((pg), 0x20) #define ISL7998X_REG_PX_DEC_IAGC(pg) ISL7998X_REG((pg), 0x21) #define ISL7998X_REG_PX_DEC_AGCGAIN(pg) ISL7998X_REG((pg), 0x22) #define ISL7998X_REG_PX_DEC_PEAKWT(pg) ISL7998X_REG((pg), 0x23) #define ISL7998X_REG_PX_DEC_CLMPL(pg) ISL7998X_REG((pg), 0x24) #define ISL7998X_REG_PX_DEC_SYNCT(pg) ISL7998X_REG((pg), 0x25) #define ISL7998X_REG_PX_DEC_MISSCNT(pg) ISL7998X_REG((pg), 0x26) #define ISL7998X_REG_PX_DEC_PCLAMP(pg) ISL7998X_REG((pg), 0x27) #define ISL7998X_REG_PX_DEC_VERT_CTL_1(pg) ISL7998X_REG((pg), 0x28) #define ISL7998X_REG_PX_DEC_VERT_CTL_2(pg) ISL7998X_REG((pg), 0x29) #define ISL7998X_REG_PX_DEC_CLR_KILL_LVL(pg) ISL7998X_REG((pg), 0x2a) #define ISL7998X_REG_PX_DEC_COMB_FILTER_CTL(pg) ISL7998X_REG((pg), 0x2b) #define ISL7998X_REG_PX_DEC_LUMA_DELAY(pg) ISL7998X_REG((pg), 0x2c) #define ISL7998X_REG_PX_DEC_MISC1(pg) ISL7998X_REG((pg), 0x2d) #define ISL7998X_REG_PX_DEC_MISC2(pg) ISL7998X_REG((pg), 0x2e) #define ISL7998X_REG_PX_DEC_MISC3(pg) ISL7998X_REG((pg), 0x2f) #define ISL7998X_REG_PX_DEC_MVSN(pg) ISL7998X_REG((pg), 0x30) #define ISL7998X_REG_PX_DEC_CSTATUS2(pg) ISL7998X_REG((pg), 0x31) #define ISL7998X_REG_PX_DEC_HFREF(pg) ISL7998X_REG((pg), 0x32) #define ISL7998X_REG_PX_DEC_CLMD(pg) ISL7998X_REG((pg), 0x33) #define ISL7998X_REG_PX_DEC_ID_DET_CTL(pg) ISL7998X_REG((pg), 0x34) #define ISL7998X_REG_PX_DEC_CLCNTL(pg) ISL7998X_REG((pg), 0x35) #define ISL7998X_REG_PX_DEC_DIFF_CLMP_CTL_1(pg) ISL7998X_REG((pg), 0x36) #define ISL7998X_REG_PX_DEC_DIFF_CLMP_CTL_2(pg) ISL7998X_REG((pg), 0x37) #define ISL7998X_REG_PX_DEC_DIFF_CLMP_CTL_3(pg) ISL7998X_REG((pg), 0x38) #define ISL7998X_REG_PX_DEC_DIFF_CLMP_CTL_4(pg) ISL7998X_REG((pg), 0x39) #define ISL7998X_REG_PX_DEC_SHORT_DET_CTL(pg) ISL7998X_REG((pg), 0x3a) #define ISL7998X_REG_PX_DEC_SHORT_DET_CTL_1(pg) ISL7998X_REG((pg), 0x3b) #define ISL7998X_REG_PX_DEC_AFE_TST_MUX_CTL(pg) ISL7998X_REG((pg), 0x3c) #define ISL7998X_REG_PX_DEC_DATA_CONV(pg) ISL7998X_REG((pg), 0x3d) #define ISL7998X_REG_PX_DEC_INTERNAL_TEST(pg) ISL7998X_REG((pg), 0x3f) #define ISL7998X_REG_PX_DEC_H_DELAY_CTL(pg) ISL7998X_REG((pg), 0x43) #define ISL7998X_REG_PX_DEC_H_DELAY_II_HI(pg) ISL7998X_REG((pg), 0x44) #define ISL7998X_REG_PX_DEC_H_DELAY_II_LOW(pg) ISL7998X_REG((pg), 0x45) #define ISL7998X_REG_PX_ACA_CTL_1(pg) ISL7998X_REG((pg), 0x80) #define ISL7998X_REG_PX_ACA_GAIN_CTL(pg) ISL7998X_REG((pg), 0x81) #define ISL7998X_REG_PX_ACA_Y_AVG_HI_LIMIT(pg) ISL7998X_REG((pg), 0x82) #define ISL7998X_REG_PX_ACA_Y_AVG_LO_LIMIT(pg) ISL7998X_REG((pg), 0x83) #define ISL7998X_REG_PX_ACA_Y_DET_THRESHOLD(pg) ISL7998X_REG((pg), 0x84) #define ISL7998X_REG_PX_ACA_BLACK_LVL(pg) ISL7998X_REG((pg), 0x85) #define ISL7998X_REG_PX_ACA_CENTER_LVL(pg) ISL7998X_REG((pg), 0x86) #define ISL7998X_REG_PX_ACA_WHITE_LVL(pg) ISL7998X_REG((pg), 0x87) #define ISL7998X_REG_PX_ACA_MEAN_OFF_LIMIT(pg) ISL7998X_REG((pg), 0x88) #define ISL7998X_REG_PX_ACA_MEAN_OFF_UPGAIN(pg) ISL7998X_REG((pg), 0x89) #define ISL7998X_REG_PX_ACA_MEAN_OFF_SLOPE(pg) ISL7998X_REG((pg), 0x8a) #define ISL7998X_REG_PX_ACA_MEAN_OFF_DNGAIN(pg) ISL7998X_REG((pg), 0x8b) #define ISL7998X_REG_PX_ACA_DELTA_CO_THRES(pg) ISL7998X_REG((pg), 0x8c) #define ISL7998X_REG_PX_ACA_DELTA_SLOPE(pg) ISL7998X_REG((pg), 0x8d) #define ISL7998X_REG_PX_ACA_LO_HI_AVG_THRES(pg) ISL7998X_REG((pg), 0x8e) #define ISL7998X_REG_PX_ACA_LO_MAX_LVL_CTL(pg) ISL7998X_REG((pg), 0x8f) #define ISL7998X_REG_PX_ACA_HI_MAX_LVL_CTL(pg) ISL7998X_REG((pg), 0x90) #define ISL7998X_REG_PX_ACA_LO_UPGAIN_CTL(pg) ISL7998X_REG((pg), 0x91) #define ISL7998X_REG_PX_ACA_LO_DNGAIN_CTL(pg) ISL7998X_REG((pg), 0x92) #define ISL7998X_REG_PX_ACA_HI_UPGAIN_CTL(pg) ISL7998X_REG((pg), 0x93) #define ISL7998X_REG_PX_ACA_HI_DNGAIN_CTL(pg) ISL7998X_REG((pg), 0x94) #define ISL7998X_REG_PX_ACA_LOPASS_FLT_COEF(pg) ISL7998X_REG((pg), 0x95) #define ISL7998X_REG_PX_ACA_PDF_INDEX(pg) ISL7998X_REG((pg), 0x96) #define ISL7998X_REG_PX_ACA_HIST_WIN_H_STT(pg) ISL7998X_REG((pg), 0x97) #define ISL7998X_REG_PX_ACA_HIST_WIN_H_SZ1(pg) ISL7998X_REG((pg), 0x98) #define ISL7998X_REG_PX_ACA_HIST_WIN_H_SZ2(pg) ISL7998X_REG((pg), 0x99) #define ISL7998X_REG_PX_ACA_HIST_WIN_V_STT(pg) ISL7998X_REG((pg), 0x9a) #define ISL7998X_REG_PX_ACA_HIST_WIN_V_SZ1(pg) ISL7998X_REG((pg), 0x9b) #define ISL7998X_REG_PX_ACA_HIST_WIN_V_SZ2(pg) ISL7998X_REG((pg), 0x9c) #define ISL7998X_REG_PX_ACA_Y_AVG(pg) ISL7998X_REG((pg), 0xa0) #define ISL7998X_REG_PX_ACA_Y_AVG_LIM(pg) ISL7998X_REG((pg), 0xa1) #define ISL7998X_REG_PX_ACA_LO_AVG(pg) ISL7998X_REG((pg), 0xa2) #define ISL7998X_REG_PX_ACA_HI_AVG(pg) ISL7998X_REG((pg), 0xa3) #define ISL7998X_REG_PX_ACA_Y_MAX(pg) ISL7998X_REG((pg), 0xa4) #define ISL7998X_REG_PX_ACA_Y_MIN(pg) ISL7998X_REG((pg), 0xa5) #define ISL7998X_REG_PX_ACA_MOFFSET(pg) ISL7998X_REG((pg), 0xa6) #define ISL7998X_REG_PX_ACA_LO_GAIN(pg) ISL7998X_REG((pg), 0xa7) #define ISL7998X_REG_PX_ACA_HI_GAIN(pg) ISL7998X_REG((pg), 0xa8) #define ISL7998X_REG_PX_ACA_LL_SLOPE(pg) ISL7998X_REG((pg), 0xa9) #define ISL7998X_REG_PX_ACA_LH_SLOPE(pg) ISL7998X_REG((pg), 0xaa) #define ISL7998X_REG_PX_ACA_HL_SLOPE(pg) ISL7998X_REG((pg), 0xab) #define ISL7998X_REG_PX_ACA_HH_SLOPE(pg) ISL7998X_REG((pg), 0xac) #define ISL7998X_REG_PX_ACA_X_LOW(pg) ISL7998X_REG((pg), 0xad) #define ISL7998X_REG_PX_ACA_X_MEAN(pg) ISL7998X_REG((pg), 0xae) #define ISL7998X_REG_PX_ACA_X_HIGH(pg) ISL7998X_REG((pg), 0xaf) #define ISL7998X_REG_PX_ACA_Y_LOW(pg) ISL7998X_REG((pg), 0xb0) #define ISL7998X_REG_PX_ACA_Y_MEAN(pg) ISL7998X_REG((pg), 0xb1) #define ISL7998X_REG_PX_ACA_Y_HIGH(pg) ISL7998X_REG((pg), 0xb2) #define ISL7998X_REG_PX_ACA_CTL_2(pg) ISL7998X_REG((pg), 0xb3) #define ISL7998X_REG_PX_ACA_CTL_3(pg) ISL7998X_REG((pg), 0xb4) #define ISL7998X_REG_PX_ACA_CTL_4(pg) ISL7998X_REG((pg), 0xb5) #define ISL7998X_REG_PX_ACA_FLEX_WIN_HIST(pg) ISL7998X_REG((pg), 0xc0) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TL_H(pg) ISL7998X_REG((pg), 0xc1) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TL_L(pg) ISL7998X_REG((pg), 0xc2) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TL_H(pg) ISL7998X_REG((pg), 0xc3) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TL_L(pg) ISL7998X_REG((pg), 0xc4) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TR_H(pg) ISL7998X_REG((pg), 0xc5) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TR_L(pg) ISL7998X_REG((pg), 0xc6) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TR_H(pg) ISL7998X_REG((pg), 0xc7) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TR_L(pg) ISL7998X_REG((pg), 0xc8) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BL_H(pg) ISL7998X_REG((pg), 0xc9) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BL_L(pg) ISL7998X_REG((pg), 0xca) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BL_H(pg) ISL7998X_REG((pg), 0xcb) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BL_L(pg) ISL7998X_REG((pg), 0xcc) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BR_H(pg) ISL7998X_REG((pg), 0xcd) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BR_L(pg) ISL7998X_REG((pg), 0xce) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BR_H(pg) ISL7998X_REG((pg), 0xcf) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BR_L(pg) ISL7998X_REG((pg), 0xd0) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_LM_H(pg) ISL7998X_REG((pg), 0xd1) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_LM_L(pg) ISL7998X_REG((pg), 0xd2) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_LM_H(pg) ISL7998X_REG((pg), 0xd3) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_LM_L(pg) ISL7998X_REG((pg), 0xd4) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TM_H(pg) ISL7998X_REG((pg), 0xd5) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_TM_L(pg) ISL7998X_REG((pg), 0xd6) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TM_H(pg) ISL7998X_REG((pg), 0xd7) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_TM_L(pg) ISL7998X_REG((pg), 0xd8) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BM_H(pg) ISL7998X_REG((pg), 0xd9) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_BM_L(pg) ISL7998X_REG((pg), 0xda) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BM_H(pg) ISL7998X_REG((pg), 0xdb) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_BM_L(pg) ISL7998X_REG((pg), 0xdc) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_RM_H(pg) ISL7998X_REG((pg), 0xdd) #define ISL7998X_REG_PX_ACA_FLEX_WIN_X_RM_L(pg) ISL7998X_REG((pg), 0xde) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_RM_H(pg) ISL7998X_REG((pg), 0xdf) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_RM_L(pg) ISL7998X_REG((pg), 0xe0) #define ISL7998X_REG_PX_ACA_HIST_DATA_LO(pg) ISL7998X_REG((pg), 0xe1) #define ISL7998X_REG_PX_ACA_HIST_DATA_MID(pg) ISL7998X_REG((pg), 0xe2) #define ISL7998X_REG_PX_ACA_HIST_DATA_HI(pg) ISL7998X_REG((pg), 0xe3) #define ISL7998X_REG_PX_ACA_FLEX_WIN_Y_CLR(pg) ISL7998X_REG((pg), 0xe4) #define ISL7998X_REG_PX_ACA_FLEX_WIN_CB_CLR(pg) ISL7998X_REG((pg), 0xe5) #define ISL7998X_REG_PX_ACA_FLEX_WIN_CR_CLR(pg) ISL7998X_REG((pg), 0xe6) #define ISL7998X_REG_PX_ACA_XFER_HIST_HOST(pg) ISL7998X_REG((pg), 0xe7) #define ISL7998X_REG_P5_LI_ENGINE_CTL ISL7998X_REG(5, 0x00) #define ISL7998X_REG_P5_LI_ENGINE_LINE_CTL ISL7998X_REG(5, 0x01) #define ISL7998X_REG_P5_LI_ENGINE_PIC_WIDTH ISL7998X_REG(5, 0x02) #define ISL7998X_REG_P5_LI_ENGINE_SYNC_CTL ISL7998X_REG(5, 0x03) #define ISL7998X_REG_P5_LI_ENGINE_VC_ASSIGNMENT ISL7998X_REG(5, 0x04) #define ISL7998X_REG_P5_LI_ENGINE_TYPE_CTL ISL7998X_REG(5, 0x05) #define ISL7998X_REG_P5_LI_ENGINE_FIFO_CTL ISL7998X_REG(5, 0x06) #define ISL7998X_REG_P5_MIPI_READ_START_CTL ISL7998X_REG(5, 0x07) #define ISL7998X_REG_P5_PSEUDO_FRM_FIELD_CTL ISL7998X_REG(5, 0x08) #define ISL7998X_REG_P5_ONE_FIELD_MODE_CTL ISL7998X_REG(5, 0x09) #define ISL7998X_REG_P5_MIPI_INT_HW_TST_CTR ISL7998X_REG(5, 0x0a) #define ISL7998X_REG_P5_TP_GEN_BAR_PATTERN ISL7998X_REG(5, 0x0b) #define ISL7998X_REG_P5_MIPI_PCNT_PSFRM ISL7998X_REG(5, 0x0c) #define ISL7998X_REG_P5_LI_ENGINE_TP_GEN_CTL ISL7998X_REG(5, 0x0d) #define ISL7998X_REG_P5_MIPI_VBLANK_PSFRM ISL7998X_REG(5, 0x0e) #define ISL7998X_REG_P5_LI_ENGINE_CTL_2 ISL7998X_REG(5, 0x0f) #define ISL7998X_REG_P5_MIPI_WCNT_1 ISL7998X_REG(5, 0x10) #define ISL7998X_REG_P5_MIPI_WCNT_2 ISL7998X_REG(5, 0x11) #define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_1 ISL7998X_REG(5, 0x12) #define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_2 ISL7998X_REG(5, 0x13) #define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_3 ISL7998X_REG(5, 0x14) #define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_4 ISL7998X_REG(5, 0x15) #define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_5 ISL7998X_REG(5, 0x16) #define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_6 ISL7998X_REG(5, 0x17) #define ISL7998X_REG_P5_MIPI_DPHY_PARAMS_1 ISL7998X_REG(5, 0x18) #define ISL7998X_REG_P5_MIPI_DPHY_SOT_PERIOD ISL7998X_REG(5, 0x19) #define ISL7998X_REG_P5_MIPI_DPHY_EOT_PERIOD ISL7998X_REG(5, 0x1a) #define ISL7998X_REG_P5_MIPI_DPHY_PARAMS_2 ISL7998X_REG(5, 0x1b) #define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_7 ISL7998X_REG(5, 0x1c) #define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_8 ISL7998X_REG(5, 0x1d) #define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_9 ISL7998X_REG(5, 0x1e) #define ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_10 ISL7998X_REG(5, 0x1f) #define ISL7998X_REG_P5_TP_GEN_MIPI ISL7998X_REG(5, 0x20) #define ISL7998X_REG_P5_ESC_MODE_TIME_CTL ISL7998X_REG(5, 0x21) #define ISL7998X_REG_P5_AUTO_TEST_ERR_DET ISL7998X_REG(5, 0x22) #define ISL7998X_REG_P5_MIPI_TIMING ISL7998X_REG(5, 0x23) #define ISL7998X_REG_P5_PIC_HEIGHT_HIGH ISL7998X_REG(5, 0x24) #define ISL7998X_REG_P5_PIC_HEIGHT_LOW ISL7998X_REG(5, 0x25) #define ISL7998X_REG_P5_MIPI_SP_HS_TRL_CTL ISL7998X_REG(5, 0x26) #define ISL7998X_REG_P5_FIFO_THRSH_CNT_1 ISL7998X_REG(5, 0x28) #define ISL7998X_REG_P5_FIFO_THRSH_CNT_2 ISL7998X_REG(5, 0x29) #define ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_1 ISL7998X_REG(5, 0x2a) #define ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_2 ISL7998X_REG(5, 0x2b) #define ISL7998X_REG_P5_PSF_FIELD_END_CTL_1 ISL7998X_REG(5, 0x2c) #define ISL7998X_REG_P5_PSF_FIELD_END_CTL_2 ISL7998X_REG(5, 0x2d) #define ISL7998X_REG_P5_PSF_FIELD_END_CTL_3 ISL7998X_REG(5, 0x2e) #define ISL7998X_REG_P5_PSF_FIELD_END_CTL_4 ISL7998X_REG(5, 0x2f) #define ISL7998X_REG_P5_MIPI_ANA_DATA_CTL_1 ISL7998X_REG(5, 0x30) #define ISL7998X_REG_P5_MIPI_ANA_DATA_CTL_2 ISL7998X_REG(5, 0x31) #define ISL7998X_REG_P5_MIPI_ANA_CLK_CTL ISL7998X_REG(5, 0x32) #define ISL7998X_REG_P5_PLL_ANA_STATUS ISL7998X_REG(5, 0x33) #define ISL7998X_REG_P5_PLL_ANA_MISC_CTL ISL7998X_REG(5, 0x34) #define ISL7998X_REG_P5_MIPI_ANA ISL7998X_REG(5, 0x35) #define ISL7998X_REG_P5_PLL_ANA ISL7998X_REG(5, 0x36) #define ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_1 ISL7998X_REG(5, 0x38) #define ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_2 ISL7998X_REG(5, 0x39) #define ISL7998X_REG_P5_H_LINE_CNT_1 ISL7998X_REG(5, 0x3a) #define ISL7998X_REG_P5_H_LINE_CNT_2 ISL7998X_REG(5, 0x3b) #define ISL7998X_REG_P5_HIST_LINE_CNT_1 ISL7998X_REG(5, 0x3c) #define ISL7998X_REG_P5_HIST_LINE_CNT_2 ISL7998X_REG(5, 0x3d) static const struct reg_sequence isl7998x_init_seq_1[] = { { ISL7998X_REG_P0_SHORT_DIAG_IRQ_EN, 0xff }, { ISL7998X_REG_PX_DEC_SDT(0x1), 0x00 }, { ISL7998X_REG_PX_DEC_SHORT_DET_CTL_1(0x1), 0x03 }, { ISL7998X_REG_PX_DEC_SDT(0x2), 0x00 }, { ISL7998X_REG_PX_DEC_SHORT_DET_CTL_1(0x2), 0x03 }, { ISL7998X_REG_PX_DEC_SDT(0x3), 0x00 }, { ISL7998X_REG_PX_DEC_SHORT_DET_CTL_1(0x3), 0x03 }, { ISL7998X_REG_PX_DEC_SDT(0x4), 0x00 }, { ISL7998X_REG_PX_DEC_SHORT_DET_CTL_1(0x4), 0x03 }, { ISL7998X_REG_P5_LI_ENGINE_CTL, 0x00 }, { ISL7998X_REG_P0_SW_RESET_CTL, 0x1f, 10 }, { ISL7998X_REG_P0_IO_BUFFER_CTL, 0x00 }, { ISL7998X_REG_P0_MPP2_SYNC_CTL, 0xc9 }, { ISL7998X_REG_P0_IRQ_SYNC_CTL, 0xc9 }, { ISL7998X_REG_P0_CHAN_1_IRQ, 0x03 }, { ISL7998X_REG_P0_CHAN_2_IRQ, 0x00 }, { ISL7998X_REG_P0_CHAN_3_IRQ, 0x00 }, { ISL7998X_REG_P0_CHAN_4_IRQ, 0x00 }, { ISL7998X_REG_P5_LI_ENGINE_CTL, 0x02 }, { ISL7998X_REG_P5_LI_ENGINE_LINE_CTL, 0x85 }, { ISL7998X_REG_P5_LI_ENGINE_PIC_WIDTH, 0xa0 }, { ISL7998X_REG_P5_LI_ENGINE_SYNC_CTL, 0x18 }, { ISL7998X_REG_P5_LI_ENGINE_TYPE_CTL, 0x40 }, { ISL7998X_REG_P5_LI_ENGINE_FIFO_CTL, 0x40 }, { ISL7998X_REG_P5_MIPI_WCNT_1, 0x05 }, { ISL7998X_REG_P5_MIPI_WCNT_2, 0xa0 }, { ISL7998X_REG_P5_TP_GEN_MIPI, 0x00 }, { ISL7998X_REG_P5_ESC_MODE_TIME_CTL, 0x0c }, { ISL7998X_REG_P5_MIPI_SP_HS_TRL_CTL, 0x00 }, { ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_1, 0x00 }, { ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_2, 0x19 }, { ISL7998X_REG_P5_PSF_FIELD_END_CTL_1, 0x18 }, { ISL7998X_REG_P5_PSF_FIELD_END_CTL_2, 0xf1 }, { ISL7998X_REG_P5_PSF_FIELD_END_CTL_3, 0x00 }, { ISL7998X_REG_P5_PSF_FIELD_END_CTL_4, 0xf1 }, { ISL7998X_REG_P5_MIPI_ANA_DATA_CTL_1, 0x00 }, { ISL7998X_REG_P5_MIPI_ANA_DATA_CTL_2, 0x00 }, { ISL7998X_REG_P5_MIPI_ANA_CLK_CTL, 0x00 }, { ISL7998X_REG_P5_PLL_ANA_STATUS, 0xc0 }, { ISL7998X_REG_P5_PLL_ANA_MISC_CTL, 0x18 }, { ISL7998X_REG_P5_PLL_ANA, 0x00 }, { ISL7998X_REG_P0_SW_RESET_CTL, 0x10, 10 }, /* Page 0xf means write to all of pages 1,2,3,4 */ { ISL7998X_REG_PX_DEC_VDELAY_LO(0xf), 0x14 }, { ISL7998X_REG_PX_DEC_MISC3(0xf), 0xe6 }, { ISL7998X_REG_PX_DEC_CLMD(0xf), 0x85 }, { ISL7998X_REG_PX_DEC_H_DELAY_II_LOW(0xf), 0x11 }, { ISL7998X_REG_PX_ACA_XFER_HIST_HOST(0xf), 0x00 }, { ISL7998X_REG_P0_CLK_CTL_1, 0x1f }, { ISL7998X_REG_P0_CLK_CTL_2, 0x43 }, { ISL7998X_REG_P0_CLK_CTL_3, 0x4f }, }; static const struct reg_sequence isl7998x_init_seq_2[] = { { ISL7998X_REG_P5_LI_ENGINE_SYNC_CTL, 0x10 }, { ISL7998X_REG_P5_LI_ENGINE_VC_ASSIGNMENT, 0xe4 }, { ISL7998X_REG_P5_LI_ENGINE_TYPE_CTL, 0x00 }, { ISL7998X_REG_P5_LI_ENGINE_FIFO_CTL, 0x60 }, { ISL7998X_REG_P5_MIPI_READ_START_CTL, 0x2b }, { ISL7998X_REG_P5_PSEUDO_FRM_FIELD_CTL, 0x02 }, { ISL7998X_REG_P5_ONE_FIELD_MODE_CTL, 0x00 }, { ISL7998X_REG_P5_MIPI_INT_HW_TST_CTR, 0x62 }, { ISL7998X_REG_P5_TP_GEN_BAR_PATTERN, 0x02 }, { ISL7998X_REG_P5_MIPI_PCNT_PSFRM, 0x36 }, { ISL7998X_REG_P5_LI_ENGINE_TP_GEN_CTL, 0x00 }, { ISL7998X_REG_P5_MIPI_VBLANK_PSFRM, 0x6c }, { ISL7998X_REG_P5_LI_ENGINE_CTL_2, 0x00 }, { ISL7998X_REG_P5_MIPI_WCNT_1, 0x05 }, { ISL7998X_REG_P5_MIPI_WCNT_2, 0xa0 }, { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_1, 0x77 }, { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_2, 0x17 }, { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_3, 0x08 }, { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_4, 0x38 }, { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_5, 0x14 }, { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_6, 0xf6 }, { ISL7998X_REG_P5_MIPI_DPHY_PARAMS_1, 0x00 }, { ISL7998X_REG_P5_MIPI_DPHY_SOT_PERIOD, 0x17 }, { ISL7998X_REG_P5_MIPI_DPHY_EOT_PERIOD, 0x0a }, { ISL7998X_REG_P5_MIPI_DPHY_PARAMS_2, 0x71 }, { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_7, 0x7a }, { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_8, 0x0f }, { ISL7998X_REG_P5_MIPI_DPHY_TIMING_CTL_9, 0x8c }, { ISL7998X_REG_P5_MIPI_SP_HS_TRL_CTL, 0x08 }, { ISL7998X_REG_P5_FIFO_THRSH_CNT_1, 0x01 }, { ISL7998X_REG_P5_FIFO_THRSH_CNT_2, 0x0e }, { ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_1, 0x00 }, { ISL7998X_REG_P5_TP_GEN_RND_SYNC_CTL_2, 0x00 }, { ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_1, 0x03 }, { ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_2, 0xc0 }, { ISL7998X_REG_P5_H_LINE_CNT_1, 0x06 }, { ISL7998X_REG_P5_H_LINE_CNT_2, 0xb3 }, { ISL7998X_REG_P5_HIST_LINE_CNT_1, 0x00 }, { ISL7998X_REG_P5_HIST_LINE_CNT_2, 0xf1 }, { ISL7998X_REG_P5_LI_ENGINE_FIFO_CTL, 0x00 }, { ISL7998X_REG_P5_MIPI_ANA, 0x00 }, /* * Wait a bit after reset so that the chip can capture a frame * and update internal line counters. */ { ISL7998X_REG_P0_SW_RESET_CTL, 0x00, 50 }, }; enum isl7998x_pads { ISL7998X_PAD_OUT, ISL7998X_PAD_VIN1, ISL7998X_PAD_VIN2, ISL7998X_PAD_VIN3, ISL7998X_PAD_VIN4, ISL7998X_NUM_PADS }; struct isl7998x_datafmt { u32 code; enum v4l2_colorspace colorspace; }; static const struct isl7998x_datafmt isl7998x_colour_fmts[] = { { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB }, }; /* Menu items for LINK_FREQ V4L2 control */ static const s64 link_freq_menu_items[] = { /* 1 channel, 1 lane or 2 channels, 2 lanes */ 108000000, /* 2 channels, 1 lane or 4 channels, 2 lanes */ 216000000, /* 4 channels, 1 lane */ 432000000, }; /* Menu items for TEST_PATTERN V4L2 control */ static const char * const isl7998x_test_pattern_menu[] = { "Disabled", "Enabled", }; static const char * const isl7998x_test_pattern_bars[] = { "bbbbwb", "bbbwwb", "bbwbwb", "bbwwwb", }; static const char * const isl7998x_test_pattern_colors[] = { "Yellow", "Blue", "Green", "Pink", }; struct isl7998x_mode { unsigned int width; unsigned int height; enum v4l2_field field; }; static const struct isl7998x_mode supported_modes[] = { { .width = 720, .height = 576, .field = V4L2_FIELD_SEQ_TB, }, { .width = 720, .height = 480, .field = V4L2_FIELD_SEQ_BT, }, }; static const struct isl7998x_video_std { const v4l2_std_id norm; unsigned int id; const struct isl7998x_mode *mode; } isl7998x_std_res[] = { { V4L2_STD_NTSC_443, ISL7998X_REG_PX_DEC_SDT_STANDARD_NTSC_443, &supported_modes[1] }, { V4L2_STD_PAL_M, ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_M, &supported_modes[1] }, { V4L2_STD_PAL_Nc, ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_CN, &supported_modes[0] }, { V4L2_STD_PAL_N, ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL, &supported_modes[0] }, { V4L2_STD_PAL_60, ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL_60, &supported_modes[1] }, { V4L2_STD_NTSC, ISL7998X_REG_PX_DEC_SDT_STANDARD_NTSC_M, &supported_modes[1] }, { V4L2_STD_PAL, ISL7998X_REG_PX_DEC_SDT_STANDARD_PAL, &supported_modes[0] }, { V4L2_STD_SECAM, ISL7998X_REG_PX_DEC_SDT_STANDARD_SECAM, &supported_modes[0] }, { V4L2_STD_UNKNOWN, ISL7998X_REG_PX_DEC_SDT_STANDARD_UNKNOWN, &supported_modes[1] }, }; struct isl7998x { struct v4l2_subdev subdev; struct regmap *regmap; struct gpio_desc *pd_gpio; struct gpio_desc *rstb_gpio; unsigned int nr_mipi_lanes; u32 nr_inputs; const struct isl7998x_datafmt *fmt; v4l2_std_id norm; struct media_pad pads[ISL7998X_NUM_PADS]; int enabled; /* protect fmt, norm, enabled */ struct mutex lock; struct v4l2_ctrl_handler ctrl_handler; /* protect ctrl_handler */ struct mutex ctrl_mutex; /* V4L2 Controls */ struct v4l2_ctrl *link_freq; u8 test_pattern; u8 test_pattern_bars; u8 test_pattern_chans; u8 test_pattern_color; }; static struct isl7998x *sd_to_isl7998x(struct v4l2_subdev *sd) { return container_of(sd, struct isl7998x, subdev); } static struct isl7998x *i2c_to_isl7998x(const struct i2c_client *client) { return sd_to_isl7998x(i2c_get_clientdata(client)); } static unsigned int isl7998x_norm_to_val(v4l2_std_id norm) { unsigned int i; for (i = 0; i < ARRAY_SIZE(isl7998x_std_res); i++) if (isl7998x_std_res[i].norm & norm) break; if (i == ARRAY_SIZE(isl7998x_std_res)) return ISL7998X_REG_PX_DEC_SDT_STANDARD_UNKNOWN; return isl7998x_std_res[i].id; } static const struct isl7998x_mode *isl7998x_norm_to_mode(v4l2_std_id norm) { unsigned int i; for (i = 0; i < ARRAY_SIZE(isl7998x_std_res); i++) if (isl7998x_std_res[i].norm & norm) break; /* Use NTSC default resolution during standard detection */ if (i == ARRAY_SIZE(isl7998x_std_res)) return &supported_modes[1]; return isl7998x_std_res[i].mode; } static int isl7998x_get_nr_inputs(struct device_node *of_node) { struct device_node *port; unsigned int inputs = 0; unsigned int i; if (of_graph_get_endpoint_count(of_node) > ISL7998X_NUM_PADS) return -EINVAL; /* * The driver does not provide means to remap the input ports. It * always configures input ports to start from VID1. Ensure that the * device tree is correct. */ for (i = ISL7998X_PAD_VIN1; i <= ISL7998X_PAD_VIN4; i++) { port = of_graph_get_port_by_id(of_node, i); if (!port) continue; inputs |= BIT(i); of_node_put(port); } switch (inputs) { case BIT(ISL7998X_PAD_VIN1): return 1; case BIT(ISL7998X_PAD_VIN1) | BIT(ISL7998X_PAD_VIN2): return 2; case BIT(ISL7998X_PAD_VIN1) | BIT(ISL7998X_PAD_VIN2) | BIT(ISL7998X_PAD_VIN3) | BIT(ISL7998X_PAD_VIN4): return 4; default: return -EINVAL; } } static int isl7998x_wait_power_on(struct isl7998x *isl7998x) { struct device *dev = isl7998x->subdev.dev; u32 chip_id; int ret; int err; ret = read_poll_timeout(regmap_read, err, !err, 2000, 20000, false, isl7998x->regmap, ISL7998X_REG_P0_PRODUCT_ID_CODE, &chip_id); if (ret) { dev_err(dev, "timeout while waiting for ISL7998X\n"); return ret; } dev_dbg(dev, "Found ISL799%x\n", chip_id); return ret; } static int isl7998x_set_standard(struct isl7998x *isl7998x, v4l2_std_id norm) { const struct isl7998x_mode *mode = isl7998x_norm_to_mode(norm); unsigned int val = isl7998x_norm_to_val(norm); unsigned int width = mode->width; unsigned int i; int ret; for (i = 0; i < ISL7998X_INPUTS; i++) { ret = regmap_write_bits(isl7998x->regmap, ISL7998X_REG_PX_DEC_SDT(i + 1), ISL7998X_REG_PX_DEC_SDT_STANDARD, val); if (ret) return ret; } ret = regmap_write(isl7998x->regmap, ISL7998X_REG_P5_LI_ENGINE_LINE_CTL, 0x20 | ((width >> 7) & 0x1f)); if (ret) return ret; ret = regmap_write(isl7998x->regmap, ISL7998X_REG_P5_LI_ENGINE_PIC_WIDTH, (width << 1) & 0xff); if (ret) return ret; return 0; } static int isl7998x_init(struct isl7998x *isl7998x) { const unsigned int lanes = isl7998x->nr_mipi_lanes; static const u32 isl7998x_video_in_chan_map[] = { 0x00, 0x11, 0x02, 0x02 }; const struct reg_sequence isl7998x_init_seq_custom[] = { { ISL7998X_REG_P0_VIDEO_IN_CHAN_CTL, isl7998x_video_in_chan_map[isl7998x->nr_inputs - 1] }, { ISL7998X_REG_P0_CLK_CTL_4, (lanes == 1) ? 0x40 : 0x41 }, { ISL7998X_REG_P5_LI_ENGINE_CTL, (lanes == 1) ? 0x01 : 0x02 }, }; struct device *dev = isl7998x->subdev.dev; struct regmap *regmap = isl7998x->regmap; int ret; dev_dbg(dev, "configuring %d lanes for %d inputs (norm %s)\n", isl7998x->nr_mipi_lanes, isl7998x->nr_inputs, v4l2_norm_to_name(isl7998x->norm)); ret = regmap_register_patch(regmap, isl7998x_init_seq_1, ARRAY_SIZE(isl7998x_init_seq_1)); if (ret) return ret; mutex_lock(&isl7998x->lock); ret = isl7998x_set_standard(isl7998x, isl7998x->norm); mutex_unlock(&isl7998x->lock); if (ret) return ret; ret = regmap_register_patch(regmap, isl7998x_init_seq_custom, ARRAY_SIZE(isl7998x_init_seq_custom)); if (ret) return ret; return regmap_register_patch(regmap, isl7998x_init_seq_2, ARRAY_SIZE(isl7998x_init_seq_2)); } static int isl7998x_set_test_pattern(struct isl7998x *isl7998x) { const struct reg_sequence isl7998x_init_seq_tpg_off[] = { { ISL7998X_REG_P5_LI_ENGINE_TP_GEN_CTL, 0 }, { ISL7998X_REG_P5_LI_ENGINE_CTL_2, 0 } }; const struct reg_sequence isl7998x_init_seq_tpg_on[] = { { ISL7998X_REG_P5_TP_GEN_BAR_PATTERN, isl7998x->test_pattern_bars << 6 }, { ISL7998X_REG_P5_LI_ENGINE_CTL_2, isl7998x->norm & V4L2_STD_PAL ? BIT(2) : 0 }, { ISL7998X_REG_P5_LI_ENGINE_TP_GEN_CTL, (isl7998x->test_pattern_chans << 4) | (isl7998x->test_pattern_color << 2) } }; struct device *dev = isl7998x->subdev.dev; struct regmap *regmap = isl7998x->regmap; int ret; if (pm_runtime_get_if_in_use(dev) <= 0) return 0; if (isl7998x->test_pattern != 0) { dev_dbg(dev, "enabling test pattern: channels 0x%x, %s, %s\n", isl7998x->test_pattern_chans, isl7998x_test_pattern_bars[isl7998x->test_pattern_bars], isl7998x_test_pattern_colors[isl7998x->test_pattern_color]); ret = regmap_register_patch(regmap, isl7998x_init_seq_tpg_on, ARRAY_SIZE(isl7998x_init_seq_tpg_on)); } else { ret = regmap_register_patch(regmap, isl7998x_init_seq_tpg_off, ARRAY_SIZE(isl7998x_init_seq_tpg_off)); } pm_runtime_put(dev); return ret; } #ifdef CONFIG_VIDEO_ADV_DEBUG static int isl7998x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct isl7998x *isl7998x = sd_to_isl7998x(sd); int ret; u32 val; ret = regmap_read(isl7998x->regmap, reg->reg, &val); if (ret) return ret; reg->size = 1; reg->val = val; return 0; } static int isl7998x_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct isl7998x *isl7998x = sd_to_isl7998x(sd); return regmap_write(isl7998x->regmap, reg->reg, reg->val); } #endif static int isl7998x_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) { struct isl7998x *isl7998x = sd_to_isl7998x(sd); mutex_lock(&isl7998x->lock); *norm = isl7998x->norm; mutex_unlock(&isl7998x->lock); return 0; } static int isl7998x_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) { struct isl7998x *isl7998x = sd_to_isl7998x(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; int ret = 0; mutex_lock(&isl7998x->lock); if (isl7998x->enabled) { ret = -EBUSY; mutex_unlock(&isl7998x->lock); return ret; } isl7998x->norm = norm; mutex_unlock(&isl7998x->lock); if (pm_runtime_get_if_in_use(dev) <= 0) return ret; ret = isl7998x_set_standard(isl7998x, norm); pm_runtime_put(dev); return ret; } static int isl7998x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) { struct isl7998x *isl7998x = sd_to_isl7998x(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; unsigned int std_id[ISL7998X_INPUTS]; unsigned int i; int ret; u32 reg; ret = pm_runtime_resume_and_get(dev); if (ret) return ret; dev_dbg(dev, "starting video standard detection\n"); mutex_lock(&isl7998x->lock); if (isl7998x->enabled) { ret = -EBUSY; goto out_unlock; } ret = isl7998x_set_standard(isl7998x, V4L2_STD_UNKNOWN); if (ret) goto out_unlock; for (i = 0; i < ISL7998X_INPUTS; i++) { ret = regmap_write(isl7998x->regmap, ISL7998X_REG_PX_DEC_SDTR(i + 1), ISL7998X_REG_PX_DEC_SDTR_ATSTART); if (ret) goto out_reset_std; } for (i = 0; i < ISL7998X_INPUTS; i++) { ret = regmap_read_poll_timeout(isl7998x->regmap, ISL7998X_REG_PX_DEC_SDT(i + 1), reg, !(reg & ISL7998X_REG_PX_DEC_SDT_DET), 2000, 500 * USEC_PER_MSEC); if (ret) goto out_reset_std; std_id[i] = FIELD_GET(ISL7998X_REG_PX_DEC_SDT_NOW, reg); } /* * According to Renesas FAE, all input cameras must have the * same standard on this chip. */ for (i = 0; i < isl7998x->nr_inputs; i++) { dev_dbg(dev, "input %d: detected %s\n", i, v4l2_norm_to_name(isl7998x_std_res[std_id[i]].norm)); if (std_id[0] != std_id[i]) dev_warn(dev, "incompatible standards: %s on input %d (expected %s)\n", v4l2_norm_to_name(isl7998x_std_res[std_id[i]].norm), i, v4l2_norm_to_name(isl7998x_std_res[std_id[0]].norm)); } *std = isl7998x_std_res[std_id[0]].norm; out_reset_std: isl7998x_set_standard(isl7998x, isl7998x->norm); out_unlock: mutex_unlock(&isl7998x->lock); pm_runtime_put(dev); return ret; } static int isl7998x_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *std) { *std = V4L2_STD_ALL; return 0; } static int isl7998x_g_input_status(struct v4l2_subdev *sd, u32 *status) { struct isl7998x *isl7998x = sd_to_isl7998x(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; unsigned int i; int ret = 0; u32 reg; if (!pm_runtime_active(dev)) { *status |= V4L2_IN_ST_NO_POWER; return 0; } for (i = 0; i < isl7998x->nr_inputs; i++) { ret = regmap_read(isl7998x->regmap, ISL7998X_REG_PX_DEC_STATUS_1(i + 1), ®); if (!ret) { if (reg & ISL7998X_REG_PX_DEC_STATUS_1_VDLOSS) *status |= V4L2_IN_ST_NO_SIGNAL; if (!(reg & ISL7998X_REG_PX_DEC_STATUS_1_HLOCK)) *status |= V4L2_IN_ST_NO_H_LOCK; if (!(reg & ISL7998X_REG_PX_DEC_STATUS_1_VLOCK)) *status |= V4L2_IN_ST_NO_V_LOCK; } } return ret; } static int isl7998x_s_stream(struct v4l2_subdev *sd, int enable) { struct isl7998x *isl7998x = sd_to_isl7998x(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; int ret = 0; u32 reg; dev_dbg(dev, "stream %s\n", enable ? "ON" : "OFF"); mutex_lock(&isl7998x->lock); if (isl7998x->enabled == enable) goto out; isl7998x->enabled = enable; if (enable) { ret = isl7998x_set_test_pattern(isl7998x); if (ret) goto out; } regmap_read(isl7998x->regmap, ISL7998X_REG_P5_LI_ENGINE_CTL, ®); if (enable) reg &= ~BIT(7); else reg |= BIT(7); ret = regmap_write(isl7998x->regmap, ISL7998X_REG_P5_LI_ENGINE_CTL, reg); out: mutex_unlock(&isl7998x->lock); return ret; } static int isl7998x_pre_streamon(struct v4l2_subdev *sd, u32 flags) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; return pm_runtime_resume_and_get(dev); } static int isl7998x_post_streamoff(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; pm_runtime_put(dev); return 0; } static int isl7998x_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { if (code->index >= ARRAY_SIZE(isl7998x_colour_fmts)) return -EINVAL; code->code = isl7998x_colour_fmts[code->index].code; return 0; } static int isl7998x_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; if (fse->code != isl7998x_colour_fmts[0].code) return -EINVAL; fse->min_width = supported_modes[fse->index].width; fse->max_width = fse->min_width; fse->min_height = supported_modes[fse->index].height; fse->max_height = fse->min_height; return 0; } static int isl7998x_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct isl7998x *isl7998x = sd_to_isl7998x(sd); struct v4l2_mbus_framefmt *mf = &format->format; const struct isl7998x_mode *mode; mutex_lock(&isl7998x->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { format->format = *v4l2_subdev_state_get_format(sd_state, format->pad); goto out; } mode = isl7998x_norm_to_mode(isl7998x->norm); mf->width = mode->width; mf->height = mode->height; mf->code = isl7998x->fmt->code; mf->field = mode->field; mf->colorspace = 0; out: mutex_unlock(&isl7998x->lock); return 0; } static int isl7998x_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { struct isl7998x *isl7998x = sd_to_isl7998x(sd); struct v4l2_mbus_framefmt *mf = &format->format; const struct isl7998x_mode *mode; mutex_lock(&isl7998x->lock); mode = isl7998x_norm_to_mode(isl7998x->norm); mf->width = mode->width; mf->height = mode->height; mf->code = isl7998x->fmt->code; mf->field = mode->field; if (format->which == V4L2_SUBDEV_FORMAT_TRY) *v4l2_subdev_state_get_format(sd_state, format->pad) = format->format; mutex_unlock(&isl7998x->lock); return 0; } static int isl7998x_set_ctrl(struct v4l2_ctrl *ctrl) { struct isl7998x *isl7998x = container_of(ctrl->handler, struct isl7998x, ctrl_handler); int ret = 0; switch (ctrl->id) { case V4L2_CID_TEST_PATTERN_BARS: mutex_lock(&isl7998x->lock); isl7998x->test_pattern_bars = ctrl->val & 0x3; ret = isl7998x_set_test_pattern(isl7998x); mutex_unlock(&isl7998x->lock); break; case V4L2_CID_TEST_PATTERN_CHANNELS: mutex_lock(&isl7998x->lock); isl7998x->test_pattern_chans = ctrl->val & 0xf; ret = isl7998x_set_test_pattern(isl7998x); mutex_unlock(&isl7998x->lock); break; case V4L2_CID_TEST_PATTERN_COLOR: mutex_lock(&isl7998x->lock); isl7998x->test_pattern_color = ctrl->val & 0x3; ret = isl7998x_set_test_pattern(isl7998x); mutex_unlock(&isl7998x->lock); break; case V4L2_CID_TEST_PATTERN: mutex_lock(&isl7998x->lock); isl7998x->test_pattern = ctrl->val; ret = isl7998x_set_test_pattern(isl7998x); mutex_unlock(&isl7998x->lock); break; } return ret; } static const struct v4l2_subdev_core_ops isl7998x_subdev_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = isl7998x_g_register, .s_register = isl7998x_s_register, #endif }; static const struct v4l2_subdev_video_ops isl7998x_subdev_video_ops = { .g_std = isl7998x_g_std, .s_std = isl7998x_s_std, .querystd = isl7998x_querystd, .g_tvnorms = isl7998x_g_tvnorms, .g_input_status = isl7998x_g_input_status, .s_stream = isl7998x_s_stream, .pre_streamon = isl7998x_pre_streamon, .post_streamoff = isl7998x_post_streamoff, }; static const struct v4l2_subdev_pad_ops isl7998x_subdev_pad_ops = { .enum_mbus_code = isl7998x_enum_mbus_code, .enum_frame_size = isl7998x_enum_frame_size, .get_fmt = isl7998x_get_fmt, .set_fmt = isl7998x_set_fmt, }; static const struct v4l2_subdev_ops isl7998x_subdev_ops = { .core = &isl7998x_subdev_core_ops, .video = &isl7998x_subdev_video_ops, .pad = &isl7998x_subdev_pad_ops, }; static const struct media_entity_operations isl7998x_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; static const struct v4l2_ctrl_ops isl7998x_ctrl_ops = { .s_ctrl = isl7998x_set_ctrl, }; static const struct v4l2_ctrl_config isl7998x_ctrls[] = { { .ops = &isl7998x_ctrl_ops, .id = V4L2_CID_TEST_PATTERN_BARS, .type = V4L2_CTRL_TYPE_MENU, .name = "Test Pattern Bars", .max = ARRAY_SIZE(isl7998x_test_pattern_bars) - 1, .def = 0, .qmenu = isl7998x_test_pattern_bars, }, { .ops = &isl7998x_ctrl_ops, .id = V4L2_CID_TEST_PATTERN_CHANNELS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Test Pattern Channels", .min = 0, .max = 0xf, .step = 1, .def = 0xf, .flags = 0, }, { .ops = &isl7998x_ctrl_ops, .id = V4L2_CID_TEST_PATTERN_COLOR, .type = V4L2_CTRL_TYPE_MENU, .name = "Test Pattern Color", .max = ARRAY_SIZE(isl7998x_test_pattern_colors) - 1, .def = 0, .qmenu = isl7998x_test_pattern_colors, }, }; #define ISL7998X_REG_DECODER_ACA_READABLE_RANGE(page) \ /* Decoder range */ \ regmap_reg_range(ISL7998X_REG_PX_DEC_INPUT_FMT(page), \ ISL7998X_REG_PX_DEC_HS_DELAY_CTL(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_ANCTL(page), \ ISL7998X_REG_PX_DEC_CSC_CTL(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_BRIGHT(page), \ ISL7998X_REG_PX_DEC_HUE(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_VERT_PEAK(page), \ ISL7998X_REG_PX_DEC_CORING(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_SDT(page), \ ISL7998X_REG_PX_DEC_SDTR(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_CLMPG(page), \ ISL7998X_REG_PX_DEC_DATA_CONV(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_INTERNAL_TEST(page), \ ISL7998X_REG_PX_DEC_INTERNAL_TEST(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_H_DELAY_CTL(page), \ ISL7998X_REG_PX_DEC_H_DELAY_II_LOW(page)), \ /* ACA range */ \ regmap_reg_range(ISL7998X_REG_PX_ACA_CTL_1(page), \ ISL7998X_REG_PX_ACA_HIST_WIN_V_SZ2(page)), \ regmap_reg_range(ISL7998X_REG_PX_ACA_Y_AVG(page), \ ISL7998X_REG_PX_ACA_CTL_4(page)), \ regmap_reg_range(ISL7998X_REG_PX_ACA_FLEX_WIN_HIST(page), \ ISL7998X_REG_PX_ACA_XFER_HIST_HOST(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(page), \ ISL7998X_REG_PX_DEC_PAGE(page)) #define ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(page) \ /* Decoder range */ \ regmap_reg_range(ISL7998X_REG_PX_DEC_INPUT_FMT(page), \ ISL7998X_REG_PX_DEC_INPUT_FMT(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_HS_DELAY_CTL(page), \ ISL7998X_REG_PX_DEC_HS_DELAY_CTL(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_ANCTL(page), \ ISL7998X_REG_PX_DEC_CSC_CTL(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_BRIGHT(page), \ ISL7998X_REG_PX_DEC_HUE(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_VERT_PEAK(page), \ ISL7998X_REG_PX_DEC_CORING(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_SDT(page), \ ISL7998X_REG_PX_DEC_SDTR(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_CLMPG(page), \ ISL7998X_REG_PX_DEC_MISC3(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_CLMD(page), \ ISL7998X_REG_PX_DEC_DATA_CONV(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_INTERNAL_TEST(page), \ ISL7998X_REG_PX_DEC_INTERNAL_TEST(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_H_DELAY_CTL(page), \ ISL7998X_REG_PX_DEC_H_DELAY_II_LOW(page)), \ /* ACA range */ \ regmap_reg_range(ISL7998X_REG_PX_ACA_CTL_1(page), \ ISL7998X_REG_PX_ACA_HIST_WIN_V_SZ2(page)), \ regmap_reg_range(ISL7998X_REG_PX_ACA_CTL_2(page), \ ISL7998X_REG_PX_ACA_CTL_4(page)), \ regmap_reg_range(ISL7998X_REG_PX_ACA_FLEX_WIN_HIST(page), \ ISL7998X_REG_PX_ACA_HIST_DATA_LO(page)), \ regmap_reg_range(ISL7998X_REG_PX_ACA_XFER_HIST_HOST(page), \ ISL7998X_REG_PX_ACA_XFER_HIST_HOST(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(page), \ ISL7998X_REG_PX_DEC_PAGE(page)) #define ISL7998X_REG_DECODER_ACA_VOLATILE_RANGE(page) \ /* Decoder range */ \ regmap_reg_range(ISL7998X_REG_PX_DEC_STATUS_1(page), \ ISL7998X_REG_PX_DEC_STATUS_1(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_SDT(page), \ ISL7998X_REG_PX_DEC_SDT(page)), \ regmap_reg_range(ISL7998X_REG_PX_DEC_MVSN(page), \ ISL7998X_REG_PX_DEC_HFREF(page)), \ /* ACA range */ \ regmap_reg_range(ISL7998X_REG_PX_ACA_Y_AVG(page), \ ISL7998X_REG_PX_ACA_Y_HIGH(page)), \ regmap_reg_range(ISL7998X_REG_PX_ACA_HIST_DATA_LO(page), \ ISL7998X_REG_PX_ACA_FLEX_WIN_CR_CLR(page)) static const struct regmap_range isl7998x_readable_ranges[] = { regmap_reg_range(ISL7998X_REG_P0_PRODUCT_ID_CODE, ISL7998X_REG_P0_IRQ_SYNC_CTL), regmap_reg_range(ISL7998X_REG_P0_INTERRUPT_STATUS, ISL7998X_REG_P0_CLOCK_DELAY), regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(0), ISL7998X_REG_PX_DEC_PAGE(0)), ISL7998X_REG_DECODER_ACA_READABLE_RANGE(1), ISL7998X_REG_DECODER_ACA_READABLE_RANGE(2), ISL7998X_REG_DECODER_ACA_READABLE_RANGE(3), ISL7998X_REG_DECODER_ACA_READABLE_RANGE(4), regmap_reg_range(ISL7998X_REG_P5_LI_ENGINE_CTL, ISL7998X_REG_P5_MIPI_SP_HS_TRL_CTL), regmap_reg_range(ISL7998X_REG_P5_FIFO_THRSH_CNT_1, ISL7998X_REG_P5_PLL_ANA), regmap_reg_range(ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_1, ISL7998X_REG_P5_HIST_LINE_CNT_2), regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(5), ISL7998X_REG_PX_DEC_PAGE(5)), }; static const struct regmap_range isl7998x_writeable_ranges[] = { regmap_reg_range(ISL7998X_REG_P0_SW_RESET_CTL, ISL7998X_REG_P0_IRQ_SYNC_CTL), regmap_reg_range(ISL7998X_REG_P0_CHAN_1_IRQ, ISL7998X_REG_P0_SHORT_DIAG_IRQ_EN), regmap_reg_range(ISL7998X_REG_P0_CLOCK_DELAY, ISL7998X_REG_P0_CLOCK_DELAY), regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(0), ISL7998X_REG_PX_DEC_PAGE(0)), ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(1), ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(2), ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(3), ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(4), regmap_reg_range(ISL7998X_REG_P5_LI_ENGINE_CTL, ISL7998X_REG_P5_ESC_MODE_TIME_CTL), regmap_reg_range(ISL7998X_REG_P5_MIPI_SP_HS_TRL_CTL, ISL7998X_REG_P5_PLL_ANA), regmap_reg_range(ISL7998X_REG_P5_TOTAL_PF_LINE_CNT_1, ISL7998X_REG_P5_HIST_LINE_CNT_2), regmap_reg_range(ISL7998X_REG_PX_DEC_PAGE(5), ISL7998X_REG_PX_DEC_PAGE(5)), ISL7998X_REG_DECODER_ACA_WRITEABLE_RANGE(0xf), }; static const struct regmap_range isl7998x_volatile_ranges[] = { /* Product id code register is used to check availability */ regmap_reg_range(ISL7998X_REG_P0_PRODUCT_ID_CODE, ISL7998X_REG_P0_PRODUCT_ID_CODE), regmap_reg_range(ISL7998X_REG_P0_MPP1_SYNC_CTL, ISL7998X_REG_P0_IRQ_SYNC_CTL), regmap_reg_range(ISL7998X_REG_P0_INTERRUPT_STATUS, ISL7998X_REG_P0_INTERRUPT_STATUS), regmap_reg_range(ISL7998X_REG_P0_CHAN_1_STATUS, ISL7998X_REG_P0_SHORT_DIAG_STATUS), ISL7998X_REG_DECODER_ACA_VOLATILE_RANGE(1), ISL7998X_REG_DECODER_ACA_VOLATILE_RANGE(2), ISL7998X_REG_DECODER_ACA_VOLATILE_RANGE(3), ISL7998X_REG_DECODER_ACA_VOLATILE_RANGE(4), regmap_reg_range(ISL7998X_REG_P5_AUTO_TEST_ERR_DET, ISL7998X_REG_P5_PIC_HEIGHT_LOW), }; static const struct regmap_access_table isl7998x_readable_table = { .yes_ranges = isl7998x_readable_ranges, .n_yes_ranges = ARRAY_SIZE(isl7998x_readable_ranges), }; static const struct regmap_access_table isl7998x_writeable_table = { .yes_ranges = isl7998x_writeable_ranges, .n_yes_ranges = ARRAY_SIZE(isl7998x_writeable_ranges), }; static const struct regmap_access_table isl7998x_volatile_table = { .yes_ranges = isl7998x_volatile_ranges, .n_yes_ranges = ARRAY_SIZE(isl7998x_volatile_ranges), }; static const struct regmap_range_cfg isl7998x_ranges[] = { { .range_min = ISL7998X_REG_PN_BASE(0), .range_max = ISL7998X_REG_PX_ACA_XFER_HIST_HOST(0xf), .selector_reg = ISL7998X_REG_PX_DEC_PAGE(0), .selector_mask = ISL7998X_REG_PX_DEC_PAGE_MASK, .window_start = 0, .window_len = 256, } }; static const struct regmap_config isl7998x_regmap = { .reg_bits = 8, .val_bits = 8, .max_register = ISL7998X_REG_PX_ACA_XFER_HIST_HOST(0xf), .ranges = isl7998x_ranges, .num_ranges = ARRAY_SIZE(isl7998x_ranges), .rd_table = &isl7998x_readable_table, .wr_table = &isl7998x_writeable_table, .volatile_table = &isl7998x_volatile_table, .cache_type = REGCACHE_MAPLE, }; static int isl7998x_mc_init(struct isl7998x *isl7998x) { unsigned int i; isl7998x->subdev.entity.ops = &isl7998x_entity_ops; isl7998x->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; isl7998x->pads[ISL7998X_PAD_OUT].flags = MEDIA_PAD_FL_SOURCE; for (i = ISL7998X_PAD_VIN1; i < ISL7998X_NUM_PADS; i++) isl7998x->pads[i].flags = MEDIA_PAD_FL_SINK; return media_entity_pads_init(&isl7998x->subdev.entity, ISL7998X_NUM_PADS, isl7998x->pads); } static int get_link_freq_menu_index(unsigned int lanes, unsigned int inputs) { int ret = -EINVAL; switch (lanes) { case 1: if (inputs == 1) ret = 0; if (inputs == 2) ret = 1; if (inputs == 4) ret = 2; break; case 2: if (inputs == 2) ret = 0; if (inputs == 4) ret = 1; break; default: break; } return ret; } static void isl7998x_remove_controls(struct isl7998x *isl7998x) { v4l2_ctrl_handler_free(&isl7998x->ctrl_handler); mutex_destroy(&isl7998x->ctrl_mutex); } static int isl7998x_init_controls(struct isl7998x *isl7998x) { struct v4l2_subdev *sd = &isl7998x->subdev; int link_freq_index; unsigned int i; int ret; ret = v4l2_ctrl_handler_init(&isl7998x->ctrl_handler, 2 + ARRAY_SIZE(isl7998x_ctrls)); if (ret) return ret; mutex_init(&isl7998x->ctrl_mutex); isl7998x->ctrl_handler.lock = &isl7998x->ctrl_mutex; link_freq_index = get_link_freq_menu_index(isl7998x->nr_mipi_lanes, isl7998x->nr_inputs); if (link_freq_index < 0 || link_freq_index >= ARRAY_SIZE(link_freq_menu_items)) { dev_err(sd->dev, "failed to find MIPI link freq: %d lanes, %d inputs\n", isl7998x->nr_mipi_lanes, isl7998x->nr_inputs); ret = -EINVAL; goto err; } isl7998x->link_freq = v4l2_ctrl_new_int_menu(&isl7998x->ctrl_handler, &isl7998x_ctrl_ops, V4L2_CID_LINK_FREQ, ARRAY_SIZE(link_freq_menu_items) - 1, link_freq_index, link_freq_menu_items); if (isl7998x->link_freq) isl7998x->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; for (i = 0; i < ARRAY_SIZE(isl7998x_ctrls); i++) v4l2_ctrl_new_custom(&isl7998x->ctrl_handler, &isl7998x_ctrls[i], NULL); v4l2_ctrl_new_std_menu_items(&isl7998x->ctrl_handler, &isl7998x_ctrl_ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(isl7998x_test_pattern_menu) - 1, 0, 0, isl7998x_test_pattern_menu); ret = isl7998x->ctrl_handler.error; if (ret) goto err; isl7998x->subdev.ctrl_handler = &isl7998x->ctrl_handler; v4l2_ctrl_handler_setup(&isl7998x->ctrl_handler); return 0; err: isl7998x_remove_controls(isl7998x); return ret; } static int isl7998x_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct v4l2_fwnode_endpoint endpoint = { .bus_type = V4L2_MBUS_CSI2_DPHY, }; struct fwnode_handle *ep; struct isl7998x *isl7998x; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int nr_inputs; int ret; ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA); if (!ret) { dev_warn(&adapter->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); return -EIO; } isl7998x = devm_kzalloc(dev, sizeof(*isl7998x), GFP_KERNEL); if (!isl7998x) return -ENOMEM; isl7998x->pd_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(isl7998x->pd_gpio)) return dev_err_probe(dev, PTR_ERR(isl7998x->pd_gpio), "Failed to retrieve/request PD GPIO\n"); isl7998x->rstb_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(isl7998x->rstb_gpio)) return dev_err_probe(dev, PTR_ERR(isl7998x->rstb_gpio), "Failed to retrieve/request RSTB GPIO\n"); isl7998x->regmap = devm_regmap_init_i2c(client, &isl7998x_regmap); if (IS_ERR(isl7998x->regmap)) return dev_err_probe(dev, PTR_ERR(isl7998x->regmap), "Failed to allocate register map\n"); ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), ISL7998X_PAD_OUT, 0, 0); if (!ep) return dev_err_probe(dev, -EINVAL, "Missing endpoint node\n"); ret = v4l2_fwnode_endpoint_parse(ep, &endpoint); fwnode_handle_put(ep); if (ret) return dev_err_probe(dev, ret, "Failed to parse endpoint\n"); if (endpoint.bus.mipi_csi2.num_data_lanes == 0 || endpoint.bus.mipi_csi2.num_data_lanes > 2) return dev_err_probe(dev, -EINVAL, "Invalid number of MIPI lanes\n"); isl7998x->nr_mipi_lanes = endpoint.bus.mipi_csi2.num_data_lanes; nr_inputs = isl7998x_get_nr_inputs(dev->of_node); if (nr_inputs < 0) return dev_err_probe(dev, nr_inputs, "Invalid number of input ports\n"); isl7998x->nr_inputs = nr_inputs; v4l2_i2c_subdev_init(&isl7998x->subdev, client, &isl7998x_subdev_ops); isl7998x->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ret = isl7998x_mc_init(isl7998x); if (ret < 0) return ret; isl7998x->fmt = &isl7998x_colour_fmts[0]; isl7998x->norm = V4L2_STD_NTSC; isl7998x->enabled = 0; mutex_init(&isl7998x->lock); ret = isl7998x_init_controls(isl7998x); if (ret) goto err_entity_cleanup; ret = v4l2_async_register_subdev(&isl7998x->subdev); if (ret < 0) goto err_controls_cleanup; pm_runtime_enable(dev); return 0; err_controls_cleanup: isl7998x_remove_controls(isl7998x); err_entity_cleanup: media_entity_cleanup(&isl7998x->subdev.entity); return ret; } static void isl7998x_remove(struct i2c_client *client) { struct isl7998x *isl7998x = i2c_to_isl7998x(client); pm_runtime_disable(&client->dev); v4l2_async_unregister_subdev(&isl7998x->subdev); isl7998x_remove_controls(isl7998x); media_entity_cleanup(&isl7998x->subdev.entity); } static const struct of_device_id isl7998x_of_match[] = { { .compatible = "isil,isl79987", }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, isl7998x_of_match); static const struct i2c_device_id isl7998x_id[] = { { "isl79987", 0 }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(i2c, isl7998x_id); static int __maybe_unused isl7998x_runtime_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct isl7998x *isl7998x = sd_to_isl7998x(sd); int ret; gpiod_set_value(isl7998x->rstb_gpio, 1); gpiod_set_value(isl7998x->pd_gpio, 0); gpiod_set_value(isl7998x->rstb_gpio, 0); ret = isl7998x_wait_power_on(isl7998x); if (ret) goto err; ret = isl7998x_init(isl7998x); if (ret) goto err; return 0; err: gpiod_set_value(isl7998x->pd_gpio, 1); return ret; } static int __maybe_unused isl7998x_runtime_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct isl7998x *isl7998x = sd_to_isl7998x(sd); gpiod_set_value(isl7998x->pd_gpio, 1); return 0; } static const struct dev_pm_ops isl7998x_pm_ops = { SET_RUNTIME_PM_OPS(isl7998x_runtime_suspend, isl7998x_runtime_resume, NULL) }; static struct i2c_driver isl7998x_i2c_driver = { .driver = { .name = "isl7998x", .of_match_table = isl7998x_of_match, .pm = &isl7998x_pm_ops, }, .probe = isl7998x_probe, .remove = isl7998x_remove, .id_table = isl7998x_id, }; module_i2c_driver(isl7998x_i2c_driver); MODULE_DESCRIPTION("Intersil ISL7998x Analog to MIPI CSI-2/BT656 decoder"); MODULE_AUTHOR("Marek Vasut "); MODULE_LICENSE("GPL v2");