diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig index 3315d4bdc29e..2b5a072d2cdd 100644 --- a/drivers/video/rockchip/Kconfig +++ b/drivers/video/rockchip/Kconfig @@ -6,3 +6,4 @@ source "drivers/video/rockchip/rve/Kconfig" source "drivers/video/rockchip/iep/Kconfig" source "drivers/video/rockchip/mpp/Kconfig" source "drivers/video/rockchip/dvbm/Kconfig" +source "drivers/video/rockchip/vehicle/Kconfig" diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index 69fb54a5676f..0b8a7bbb95e6 100644 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_ROCKCHIP_RVE) += rve/ obj-$(CONFIG_IEP) += iep/ obj-$(CONFIG_ROCKCHIP_MPP_SERVICE) += mpp/ obj-$(CONFIG_ROCKCHIP_DVBM) += dvbm/ +obj-$(CONFIG_VIDEO_REVERSE_IMAGE) += vehicle/ diff --git a/drivers/video/rockchip/vehicle/Kconfig b/drivers/video/rockchip/vehicle/Kconfig new file mode 100644 index 000000000000..e61c864842f2 --- /dev/null +++ b/drivers/video/rockchip/vehicle/Kconfig @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0 +config VIDEO_REVERSE_IMAGE + bool "Rockchip Fast Reverse Image driver" + depends on ARCH_ROCKCHIP && ROCKCHIP_DRM_DIRECT_SHOW + depends on VIDEO_ROCKCHIP_CIF && PHY_ROCKCHIP_CSI2_DPHY + help + fast reverse Image module. + +if VIDEO_REVERSE_IMAGE + +config VIDEO_REVERSE_NVP6324 + bool "nvp6324 for reverse sensor" + help + Say y if use nvp6324. + +config VIDEO_REVERSE_NVP6188 + bool "nvp6188 for reverse sensor" + help + Say y if use nvp6188. + +config VIDEO_REVERSE_MAX96714 + bool "max96714 for reverse sensor" + help + Say y if use max96714. + +config VIDEO_REVERSE_GC2145 + bool "gc2145 for reverse sensor" + help + Say y if use gc2145. + +config VIDEO_REVERSE_TP2825 + bool "tp2825 for reverse sensor" + help + Say y if use tp2825. + +config VIDEO_REVERSE_AD7181 + bool "ad7181 for reverse sensor" + help + Say y if use ad7181. + +endif diff --git a/drivers/video/rockchip/vehicle/Makefile b/drivers/video/rockchip/vehicle/Makefile new file mode 100644 index 000000000000..7da28b30548a --- /dev/null +++ b/drivers/video/rockchip/vehicle/Makefile @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_REVERSE_IMAGE) += video_rkvehicle.o +video_rkvehicle-objs += vehicle_flinger.o \ + vehicle_dev.o \ + vehicle_main.o \ + vehicle_cif.o \ + vehicle_generic_sensor.o \ + vehicle_gpio.o \ + +video_rkvehicle-$(CONFIG_VIDEO_REVERSE_NVP6324) += \ + vehicle_ad_nvp6324.o + +video_rkvehicle-$(CONFIG_VIDEO_REVERSE_NVP6188) += \ + vehicle_ad_nvp6188.o + +video_rkvehicle-$(CONFIG_VIDEO_REVERSE_MAX96714) += \ + vehicle_ad_max96714.o + +video_rkvehicle-$(CONFIG_VIDEO_REVERSE_GC2145) += \ + vehicle_ad_gc2145.o + +video_rkvehicle-$(CONFIG_VIDEO_REVERSE_TP2825) += \ + vehicle_ad_tp2825.o + +video_rkvehicle-$(CONFIG_VIDEO_REVERSE_AD7181) += \ + vehicle_ad_7181.o diff --git a/drivers/video/rockchip/vehicle/vehicle-csi2-dphy-common.h b/drivers/video/rockchip/vehicle/vehicle-csi2-dphy-common.h new file mode 100644 index 000000000000..2818b3bf0eb8 --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle-csi2-dphy-common.h @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Vehicle driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ + +#ifndef _VEHICLE_CSI2_DPHY_COMMON_H_ +#define _VEHICLE_CSI2_DPHY_COMMON_H_ + +#include +#include +#include +#include "vehicle_samsung_dcphy_common.h" +#include "../../../media/platform/rockchip/cif/mipi-csi2.h" + +/* GRF REG OFFSET */ +#define GRF_VI_CON0 (0x0340) +#define GRF_VI_CON1 (0x0344) + +/*RK3588 DPHY GRF REG OFFSET */ +#define GRF_DPHY_CON0 (0x0) +#define GRF_SOC_CON2 (0x0308) + +/*GRF REG BIT DEFINE */ +#define GRF_CSI2PHY_LANE_SEL_SPLIT (0x1) +#define GRF_CSI2PHY_SEL_SPLIT_0_1 (0x0) +#define GRF_CSI2PHY_SEL_SPLIT_2_3 BIT(0) + +/*RK3588 DCPHY GRF REG OFFSET */ +#define GRF_DCPHY_CON0 (0x0) + +/* PHY REG OFFSET */ +#define CSI2_DPHY_CTRL_INVALID_OFFSET (0xffff) +#define CSI2_DPHY_CTRL_PWRCTL \ + CSI2_DPHY_CTRL_INVALID_OFFSET +#define CSI2_DPHY_CTRL_LANE_ENABLE (0x00) +#define CSI2_DPHY_CLK1_LANE_EN (0x2C) +#define CSI2_DPHY_DUAL_CAL_EN (0x80) +#define CSI2_DPHY_CLK_WR_THS_SETTLE (0x160) +#define CSI2_DPHY_CLK_CALIB_EN (0x168) +#define CSI2_DPHY_LANE0_WR_THS_SETTLE (0x1e0) +#define CSI2_DPHY_LANE0_CALIB_EN (0x1e8) +#define CSI2_DPHY_LANE1_WR_THS_SETTLE (0x260) +#define CSI2_DPHY_LANE1_CALIB_EN (0x268) +#define CSI2_DPHY_LANE2_WR_THS_SETTLE (0x2e0) +#define CSI2_DPHY_LANE2_CALIB_EN (0x2e8) +#define CSI2_DPHY_LANE3_WR_THS_SETTLE (0x360) +#define CSI2_DPHY_LANE3_CALIB_EN (0x368) +#define CSI2_DPHY_CLK1_WR_THS_SETTLE (0x3e0) +#define CSI2_DPHY_CLK1_CALIB_EN (0x3e8) + +//DCPHY +#define CSI2_DCPHY_CLK_WR_THS_SETTLE (0x030) +#define CSI2_DCPHY_LANE0_WR_THS_SETTLE (0x130) +#define CSI2_DCPHY_LANE0_WR_ERR_SOT_SYNC (0x134) +#define CSI2_DCPHY_LANE1_WR_THS_SETTLE (0x230) +#define CSI2_DCPHY_LANE1_WR_ERR_SOT_SYNC (0x234) +#define CSI2_DCPHY_LANE2_WR_THS_SETTLE (0x330) +#define CSI2_DCPHY_LANE2_WR_ERR_SOT_SYNC (0x334) +#define CSI2_DCPHY_LANE3_WR_THS_SETTLE (0x430) +#define CSI2_DCPHY_LANE3_WR_ERR_SOT_SYNC (0x434) +#define CSI2_DCPHY_CLK_LANE_ENABLE (0x000) +#define CSI2_DCPHY_DATA_LANE0_ENABLE (0x100) +#define CSI2_DCPHY_DATA_LANE1_ENABLE (0x200) +#define CSI2_DCPHY_DATA_LANE2_ENABLE (0x300) +#define CSI2_DCPHY_DATA_LANE3_ENABLE (0x400) + +#define CSI2_DCPHY_S0C_GNR_CON1 (0x004) +#define CSI2_DCPHY_S0C_ANA_CON1 (0x00c) +#define CSI2_DCPHY_S0C_ANA_CON2 (0x010) +#define CSI2_DCPHY_S0C_ANA_CON3 (0x014) +#define CSI2_DCPHY_COMBO_S0D0_GNR_CON1 (0x104) +#define CSI2_DCPHY_COMBO_S0D0_ANA_CON1 (0x10c) +#define CSI2_DCPHY_COMBO_S0D0_ANA_CON2 (0x110) +#define CSI2_DCPHY_COMBO_S0D0_ANA_CON3 (0x114) +#define CSI2_DCPHY_COMBO_S0D0_ANA_CON6 (0x120) +#define CSI2_DCPHY_COMBO_S0D0_ANA_CON7 (0x124) +#define CSI2_DCPHY_COMBO_S0D0_DESKEW_CON0 (0x140) +#define CSI2_DCPHY_COMBO_S0D0_DESKEW_CON2 (0x148) +#define CSI2_DCPHY_COMBO_S0D0_DESKEW_CON4 (0x150) +#define CSI2_DCPHY_COMBO_S0D0_CRC_CON1 (0x164) +#define CSI2_DCPHY_COMBO_S0D0_CRC_CON2 (0x168) +#define CSI2_DCPHY_COMBO_S0D1_GNR_CON1 (0x204) +#define CSI2_DCPHY_COMBO_S0D1_ANA_CON1 (0x20c) +#define CSI2_DCPHY_COMBO_S0D1_ANA_CON2 (0x210) +#define CSI2_DCPHY_COMBO_S0D1_ANA_CON3 (0x214) +#define CSI2_DCPHY_COMBO_S0D1_ANA_CON6 (0x220) +#define CSI2_DCPHY_COMBO_S0D1_ANA_CON7 (0x224) +#define CSI2_DCPHY_COMBO_S0D1_DESKEW_CON0 (0x240) +#define CSI2_DCPHY_COMBO_S0D1_DESKEW_CON2 (0x248) +#define CSI2_DCPHY_COMBO_S0D1_DESKEW_CON4 (0x250) +#define CSI2_DCPHY_COMBO_S0D1_CRC_CON1 (0x264) +#define CSI2_DCPHY_COMBO_S0D1_CRC_CON2 (0x268) +#define CSI2_DCPHY_COMBO_S0D2_GNR_CON1 (0x304) +#define CSI2_DCPHY_COMBO_S0D2_ANA_CON1 (0x30c) +#define CSI2_DCPHY_COMBO_S0D2_ANA_CON2 (0x310) +#define CSI2_DCPHY_COMBO_S0D2_ANA_CON3 (0x314) +#define CSI2_DCPHY_COMBO_S0D2_ANA_CON6 (0x320) +#define CSI2_DCPHY_COMBO_S0D2_ANA_CON7 (0x324) +#define CSI2_DCPHY_COMBO_S0D2_DESKEW_CON0 (0x340) +#define CSI2_DCPHY_COMBO_S0D2_DESKEW_CON2 (0x348) +#define CSI2_DCPHY_COMBO_S0D2_DESKEW_CON4 (0x350) +#define CSI2_DCPHY_COMBO_S0D2_CRC_CON1 (0x364) +#define CSI2_DCPHY_COMBO_S0D2_CRC_CON2 (0x368) +#define CSI2_DCPHY_S0D3_GNR_CON1 (0x404) +#define CSI2_DCPHY_S0D3_ANA_CON1 (0x40c) +#define CSI2_DCPHY_S0D3_ANA_CON2 (0x410) +#define CSI2_DCPHY_S0D3_ANA_CON3 (0x414) +#define CSI2_DCPHY_S0D3_DESKEW_CON0 (0x440) +#define CSI2_DCPHY_S0D3_DESKEW_CON2 (0x448) +#define CSI2_DCPHY_S0D3_DESKEW_CON4 (0x450) + +/* PHY REG BIT DEFINE */ +#define CSI2_DPHY_LANE_MODE_FULL (0x4) +#define CSI2_DPHY_LANE_MODE_SPLIT (0x2) +#define CSI2_DPHY_LANE_SPLIT_TOP (0x1) +#define CSI2_DPHY_LANE_SPLIT_BOT (0x2) +#define CSI2_DPHY_LANE_SPLIT_LANE0_1 (0x3 << 2) +#define CSI2_DPHY_LANE_SPLIT_LANE2_3 (0x3 << 4) +#define CSI2_DPHY_LANE_DUAL_MODE_EN BIT(6) +#define CSI2_DPHY_LANE_PARA_ARR_NUM (0x2) + +#define CSI2_DPHY_CTRL_DATALANE_ENABLE_OFFSET_BIT 2 +#define CSI2_DPHY_CTRL_DATALANE_SPLIT_LANE2_3_OFFSET_BIT 4 +#define CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT 6 + +enum csi2_dphy_index { + DPHY0 = 0x0, + DPHY1, + DPHY2, +}; + +enum csi2_dphy_lane { + CSI2_DPHY_LANE_CLOCK = 0, + CSI2_DPHY_LANE_CLOCK1, + CSI2_DPHY_LANE_DATA0, + CSI2_DPHY_LANE_DATA1, + CSI2_DPHY_LANE_DATA2, + CSI2_DPHY_LANE_DATA3 +}; + +enum grf_reg_id { + GRF_DPHY_RX0_TURNDISABLE = 0, + GRF_DPHY_RX0_FORCERXMODE, + GRF_DPHY_RX0_FORCETXSTOPMODE, + GRF_DPHY_RX0_ENABLE, + GRF_DPHY_RX0_TESTCLR, + GRF_DPHY_RX0_TESTCLK, + GRF_DPHY_RX0_TESTEN, + GRF_DPHY_RX0_TESTDIN, + GRF_DPHY_RX0_TURNREQUEST, + GRF_DPHY_RX0_TESTDOUT, + GRF_DPHY_TX0_TURNDISABLE, + GRF_DPHY_TX0_FORCERXMODE, + GRF_DPHY_TX0_FORCETXSTOPMODE, + GRF_DPHY_TX0_TURNREQUEST, + GRF_DPHY_TX1RX1_TURNDISABLE, + GRF_DPHY_TX1RX1_FORCERXMODE, + GRF_DPHY_TX1RX1_FORCETXSTOPMODE, + GRF_DPHY_TX1RX1_ENABLE, + GRF_DPHY_TX1RX1_MASTERSLAVEZ, + GRF_DPHY_TX1RX1_BASEDIR, + GRF_DPHY_TX1RX1_ENABLECLK, + GRF_DPHY_TX1RX1_TURNREQUEST, + GRF_DPHY_RX1_SRC_SEL, + /* rk3288 only */ + GRF_CON_DISABLE_ISP, + GRF_CON_ISP_DPHY_SEL, + GRF_DSI_CSI_TESTBUS_SEL, + GRF_DVP_V18SEL, + /* rk1808 & rk3326 & rv1126 */ + GRF_DPHY_CSI2PHY_FORCERXMODE, + GRF_DPHY_CSI2PHY_CLKLANE_EN, + GRF_DPHY_CSI2PHY_DATALANE_EN, + /* rv1126 only */ + GRF_DPHY_CLK_INV_SEL, + GRF_DPHY_SEL, + /* rk3368 only */ + GRF_ISP_MIPI_CSI_HOST_SEL, + /* below is for rk3399 only */ + GRF_DPHY_RX0_CLK_INV_SEL, + GRF_DPHY_RX1_CLK_INV_SEL, + GRF_DPHY_TX1RX1_SRC_SEL, + /* below is for rk3568 only */ + GRF_DPHY_CSI2PHY_CLKLANE1_EN, + GRF_DPHY_CLK1_INV_SEL, + GRF_DPHY_ISP_CSI2PHY_SEL, + GRF_DPHY_CIF_CSI2PHY_SEL, + GRF_DPHY_CSI2PHY_LANE_SEL, + GRF_DPHY_CSI2PHY1_LANE_SEL, + GRF_DPHY_CSI2PHY_DATALANE_EN0, + GRF_DPHY_CSI2PHY_DATALANE_EN1, + GRF_CPHY_MODE, + GRF_DPHY_CSIHOST2_SEL, + GRF_DPHY_CSIHOST3_SEL, + GRF_DPHY_CSIHOST4_SEL, + GRF_DPHY_CSIHOST5_SEL, +}; + +enum csi2dphy_reg_id { + CSI2PHY_REG_CTRL_LANE_ENABLE = 0, + CSI2PHY_CTRL_PWRCTL, + CSI2PHY_CTRL_DIG_RST, + CSI2PHY_CLK_THS_SETTLE, + CSI2PHY_LANE0_THS_SETTLE, + CSI2PHY_LANE1_THS_SETTLE, + CSI2PHY_LANE2_THS_SETTLE, + CSI2PHY_LANE3_THS_SETTLE, + CSI2PHY_CLK_CALIB_ENABLE, + CSI2PHY_LANE0_CALIB_ENABLE, + CSI2PHY_LANE1_CALIB_ENABLE, + CSI2PHY_LANE2_CALIB_ENABLE, + CSI2PHY_LANE3_CALIB_ENABLE, + //rv1126 only + CSI2PHY_MIPI_LVDS_MODEL, + CSI2PHY_LVDS_MODE, + //rk3568 only + CSI2PHY_DUAL_CLK_EN, + CSI2PHY_CLK1_THS_SETTLE, + CSI2PHY_CLK1_CALIB_ENABLE, + //rk3588 + CSI2PHY_CLK_LANE_ENABLE, + CSI2PHY_CLK1_LANE_ENABLE, + CSI2PHY_DATA_LANE0_ENABLE, + CSI2PHY_DATA_LANE1_ENABLE, + CSI2PHY_DATA_LANE2_ENABLE, + CSI2PHY_DATA_LANE3_ENABLE, + CSI2PHY_LANE0_ERR_SOT_SYNC, + CSI2PHY_LANE1_ERR_SOT_SYNC, + CSI2PHY_LANE2_ERR_SOT_SYNC, + CSI2PHY_LANE3_ERR_SOT_SYNC, + CSI2PHY_S0C_GNR_CON1, + CSI2PHY_S0C_ANA_CON1, + CSI2PHY_S0C_ANA_CON2, + CSI2PHY_S0C_ANA_CON3, + CSI2PHY_COMBO_S0D0_GNR_CON1, + CSI2PHY_COMBO_S0D0_ANA_CON1, + CSI2PHY_COMBO_S0D0_ANA_CON2, + CSI2PHY_COMBO_S0D0_ANA_CON3, + CSI2PHY_COMBO_S0D0_ANA_CON6, + CSI2PHY_COMBO_S0D0_ANA_CON7, + CSI2PHY_COMBO_S0D0_DESKEW_CON0, + CSI2PHY_COMBO_S0D0_DESKEW_CON2, + CSI2PHY_COMBO_S0D0_DESKEW_CON4, + CSI2PHY_COMBO_S0D0_CRC_CON1, + CSI2PHY_COMBO_S0D0_CRC_CON2, + CSI2PHY_COMBO_S0D1_GNR_CON1, + CSI2PHY_COMBO_S0D1_ANA_CON1, + CSI2PHY_COMBO_S0D1_ANA_CON2, + CSI2PHY_COMBO_S0D1_ANA_CON3, + CSI2PHY_COMBO_S0D1_ANA_CON6, + CSI2PHY_COMBO_S0D1_ANA_CON7, + CSI2PHY_COMBO_S0D1_DESKEW_CON0, + CSI2PHY_COMBO_S0D1_DESKEW_CON2, + CSI2PHY_COMBO_S0D1_DESKEW_CON4, + CSI2PHY_COMBO_S0D1_CRC_CON1, + CSI2PHY_COMBO_S0D1_CRC_CON2, + CSI2PHY_COMBO_S0D2_GNR_CON1, + CSI2PHY_COMBO_S0D2_ANA_CON1, + CSI2PHY_COMBO_S0D2_ANA_CON2, + CSI2PHY_COMBO_S0D2_ANA_CON3, + CSI2PHY_COMBO_S0D2_ANA_CON6, + CSI2PHY_COMBO_S0D2_ANA_CON7, + CSI2PHY_COMBO_S0D2_DESKEW_CON0, + CSI2PHY_COMBO_S0D2_DESKEW_CON2, + CSI2PHY_COMBO_S0D2_DESKEW_CON4, + CSI2PHY_COMBO_S0D2_CRC_CON1, + CSI2PHY_COMBO_S0D2_CRC_CON2, + CSI2PHY_S0D3_GNR_CON1, + CSI2PHY_S0D3_ANA_CON1, + CSI2PHY_S0D3_ANA_CON2, + CSI2PHY_S0D3_ANA_CON3, + CSI2PHY_S0D3_DESKEW_CON0, + CSI2PHY_S0D3_DESKEW_CON2, + CSI2PHY_S0D3_DESKEW_CON4, +}; + +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +#define GRF_REG(_offset, _width, _shift) \ + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, } + +#define CSI2PHY_REG(_offset) \ + { .offset = _offset, } + +/* add new chip id in tail by time order */ +enum csi2_dphy_chip_id { + CHIP_ID_RK3568 = 0x0, + CHIP_ID_RK3588 = 0x1, + CHIP_ID_RK3588_DCPHY = 0x2, +}; + +enum csi2_dphy_rx_pads { + CSI2_DPHY_RX_PAD_SINK = 0, + CSI2_DPHY_RX_PAD_SOURCE, + CSI2_DPHY_RX_PADS_NUM, +}; + +enum csi2_dphy_lane_mode { + LANE_MODE_UNDEF = 0x0, + LANE_MODE_FULL, + LANE_MODE_SPLIT, +}; + +struct grf_reg { + u32 offset; + u32 mask; + u32 shift; +}; + +struct csi2dphy_reg { + u32 offset; +}; + +struct hsfreq_range { + u32 range_h; + u16 cfg_bit; +}; + +#define MAX_DPHY_SENSORS (2) +#define MAX_NUM_CSI2_DPHY (0x2) + +#define RKCSI2_MAX_RESET 8 +#define RKDPHY_MAX_RESET 8 +/* csi2 head */ + +struct csi2_dphy_hw { + struct clk_bulk_data *dphy_clks; + int num_dphy_clks; + struct clk_bulk_data *csi2_clks; + int num_csi2_clks; + const char * const *csi2_rsts; + struct reset_control *csi2_rst[RKCSI2_MAX_RESET]; + int num_csi2_rsts; + const char * const *dphy_rsts; + struct reset_control *dphy_rst[RKDPHY_MAX_RESET]; + int num_dphy_rsts; + // struct reset_control *rsts_bulk; + /* spinlock_t lock; */ + bool on; + const struct hsfreq_range *hsfreq_ranges; + int num_hsfreq_ranges; + const struct grf_reg *grf_regs; + const struct txrx_reg *txrx_regs; + const struct csi2dphy_reg *csi2dphy_regs; + enum csi2_dphy_chip_id chip_id; + struct device *dev; + struct regmap *regmap_grf; + struct regmap *regmap_sys_grf; + void __iomem *csi2_dphy_base; /*csi2_dphy base addr*/ + void __iomem *csi2_base; /*csi2 base addr*/ + struct mutex mutex; /* lock for updating protection */ + atomic_t stream_cnt; + struct csi2_err_stats err_list[RK_CSI2_ERR_MAX]; + u64 data_rate_mbps; + struct rkmodule_csi_dphy_param *dphy_param; + struct samsung_mipi_dcphy *samsung_phy; +}; + +#endif diff --git a/drivers/video/rockchip/vehicle/vehicle_ad.h b/drivers/video/rockchip/vehicle/vehicle_ad.h new file mode 100644 index 000000000000..66d156f9e778 --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Vehicle driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ +#ifndef __VEHICLE_AD_COMMON_H +#define __VEHICLE_AD_COMMON_H +#include +#include "vehicle_cfg.h" +#include + +enum vehicle_ad_fix_format { + AD_FIX_FORMAT_AUTO_DETECT = 0, + AD_FIX_FORMAT_PAL = 1, + AD_FIX_FORMAT_NTSC = 2, + AD_FIX_FORMAT_720P_50FPS = 3, + AD_FIX_FORMAT_720P_30FPS = 4, + AD_FIX_FORMAT_720P_25FPS = 5, + AD_FIX_FORMAT_1080P_30FPS = 6, + AD_FIX_FORMAT_1080P_25FPS = 7, +}; + +struct vehicle_camera_device_defrect { + unsigned int width; + unsigned int height; + unsigned int crop_x; + unsigned int crop_y; + unsigned int crop_width; + unsigned int crop_height; + const char *interface; +}; + +struct vehicle_state_check_work { + struct workqueue_struct *state_check_wq; + struct delayed_work work; +}; + +struct vehicle_ad_dev { + struct device *dev; + struct i2c_adapter *adapter; + const char *ad_name; + int resolution; + int mclk_rate; + int ad_chl; + int i2c_chl; + int i2c_add; +// int i2c_rate; + int powerdown; + int pwdn_active; + int power; + int pwr_active; + int reset; + int rst_active; + int cvstd; + int cvstd_irq_flag; + int irq; + int fix_format; + struct vehicle_camera_device_defrect defrects[4]; + struct vehicle_state_check_work state_check_work; + struct vehicle_cfg cfg; + int cif_error_last_line; + u32 channel_reso[PAD_MAX]; + u8 detect_status; + u8 last_detect_status; +}; + +int vehicle_generic_sensor_write(struct vehicle_ad_dev *ad, char reg, char *pval); +int vehicle_sensor_write(struct vehicle_ad_dev *ad, u8 reg, u8 val); +int vehicle_generic_sensor_read(struct vehicle_ad_dev *ad, char reg); +int vehicle_sensor_read(struct vehicle_ad_dev *ad, u8 reg, u8 *val); +int vehicle_parse_sensor(struct vehicle_ad_dev *ad); +void vehicle_ad_channel_set(struct vehicle_ad_dev *ad, int channel); + +int vehicle_ad_init(struct vehicle_ad_dev *ad); +int vehicle_ad_deinit(void); +int vehicle_ad_stream(struct vehicle_ad_dev *ad, int val); +struct vehicle_cfg *vehicle_ad_get_vehicle_cfg(void); +void vehicle_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line); +int vehicle_to_v4l2_drv_init(void); + +#endif diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_7181.c b/drivers/video/rockchip/vehicle/vehicle_ad_7181.c new file mode 100644 index 000000000000..9547e887031a --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_7181.c @@ -0,0 +1,608 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * vehicle sensor adv7181 + * + * Copyright (C) 2022 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vehicle_cfg.h" +#include "vehicle_main.h" +#include "vehicle_ad.h" +#include "vehicle_ad_7181.h" + +enum { + FORCE_PAL_WIDTH = 720, + FORCE_PAL_HEIGHT = 576, + FORCE_NTSC_WIDTH = 720, + FORCE_NTSC_HEIGHT = 480, + FORCE_CIF_OUTPUT_FORMAT = CIF_OUTPUT_FORMAT_420, +}; + +static struct vehicle_ad_dev *ad7181_g_addev; +static v4l2_std_id std_old = V4L2_STD_NTSC; + +#define SENSOR_REGISTER_LEN 1 /* sensor register address bytes*/ +#define SENSOR_VALUE_LEN 1 /* sensor register value bytes*/ + +struct rk_sensor_reg { + unsigned int reg; + unsigned int val; +}; + +#define ADV7181_STATUS1_REG 0x10 +#define ADV7181_STATUS1_IN_LOCK 0x01 +#define ADV7181_STATUS1_AUTOD_MASK 0x70 +#define ADV7181_STATUS1_AUTOD_NTSM_M_J 0x00 +#define ADV7181_STATUS1_AUTOD_NTSC_4_43 0x10 +#define ADV7181_STATUS1_AUTOD_PAL_M 0x20 +#define ADV7181_STATUS1_AUTOD_PAL_60 0x30 +#define ADV7181_STATUS1_AUTOD_PAL_B_G 0x40 +#define ADV7181_STATUS1_AUTOD_SECAM 0x50 +#define ADV7181_STATUS1_AUTOD_PAL_COMB 0x60 +#define ADV7181_STATUS1_AUTOD_SECAM_525 0x70 + +#define ADV7181_INPUT_CONTROL 0x00 +#define ADV7181_INPUT_DEFAULT 0x00 +#define ADV7181_INPUT_CVBS_AIN2 0x00 +#define ADV7181_INPUT_CVBS_AIN3 0x01 +#define ADV7181_INPUT_CVBS_AIN5 0x02 +#define ADV7181_INPUT_CVBS_AIN6 0x03 +#define ADV7181_INPUT_CVBS_AIN8 0x04 +#define ADV7181_INPUT_CVBS_AIN10 0x05 +#define ADV7181_INPUT_CVBS_AIN1 0x0B +#define ADV7181_INPUT_CVBS_AIN4 0x0D +#define ADV7181_INPUT_CVBS_AIN7 0x0F +#define ADV7181_INPUT_YPRPB_AIN6_8_10 0x00 + +#define SEQCMD_END 0xFF000000 +#define SensorEnd {SEQCMD_END, 0x00} + +#define SENSOR_DG VEHICLE_DG + +/* Preview resolution setting*/ +static struct rk_sensor_reg sensor_preview_data[] = { + /* autodetect cvbs in ntsc/pal/secam 8-bit 422 encode */ + {0x00, 0x0B}, /*cvbs in AIN1*/ + {0x04, 0x77}, + {0x17, 0x41}, + {0x1D, 0x47}, + {0x31, 0x02}, + {0x3A, 0x17}, + {0x3B, 0x81}, + {0x3D, 0xA2}, + {0x3E, 0x6A}, + {0x3F, 0xA0}, + {0x86, 0x0B}, + {0xF3, 0x01}, + {0xF9, 0x03}, + {0x0E, 0x80}, + {0x52, 0x46}, + {0x54, 0x80}, + {0x7F, 0xFF}, + {0x81, 0x30}, + {0x90, 0xC9}, + {0x91, 0x40}, + {0x92, 0x3C}, + {0x93, 0xCA}, + {0x94, 0xD5}, + {0xB1, 0xFF}, + {0xB6, 0x08}, + {0xC0, 0x9A}, + {0xCF, 0x50}, + {0xD0, 0x4E}, + {0xD1, 0xB9}, + {0xD6, 0xDD}, + {0xD7, 0xE2}, + {0xE5, 0x51}, + {0xF6, 0x3B}, + {0x0E, 0x00}, + {0x03, 0x4C}, //stream off + {0xDF, 0X46}, + {0xC9, 0x04}, + {0xC5, 0x81}, + {0xC4, 0x34}, + {0xBf, 0x02}, + {0xB5, 0x83}, + {0xB6, 0x00}, + {0xaf, 0x03}, + {0xae, 0x00}, + {0xac, 0x00}, + {0xAB, 0x00}, + {0xa1, 0xFF}, + {0xA2, 0x00}, + {0xA3, 0x00}, + {0xA4, 0x00}, + {0xa5, 0x01}, + {0xA6, 0x00}, + {0xA6, 0x00}, + {0xA7, 0x00}, + {0xA8, 0x00}, + {0xa0, 0x03}, + {0x98, 0X00}, + {0x97, 0X00}, + {0X90, 0X00}, + {0X85, 0X02}, + {0x7B, 0x1E}, + {0x74, 0x04}, + {0x75, 0x01}, + {0x76, 0x00}, + {0x6B, 0xC0}, + {0x67, 0x03}, + {0x3C, 0x58}, + {0x30, 0x4C}, + {0x2E, 0X9F}, + {0x12, 0XC0}, + {0x10, 0X0D}, + {0x05, 0X00}, + {0x06, 0X02}, + {0x60, 0x01}, + SensorEnd +}; + +static struct rk_sensor_reg sensor_preview_data_yprpb_p[] = { + {0x05, 0x01}, + {0x06, 0x06}, + {0xc3, 0x56}, + {0xc4, 0xb4}, + {0x1d, 0x47}, + {0x3a, 0x11}, + {0x3b, 0x81}, + {0x3c, 0x3b}, + {0x6b, 0x83}, + {0xc9, 0x00}, + {0x73, 0x10}, + {0x74, 0xa3}, + {0x75, 0xe8}, + {0x76, 0xfa}, + {0x7b, 0x1c}, + {0x85, 0x19}, + {0x86, 0x0b}, + {0xbf, 0x06}, + {0xc0, 0x40}, + {0xc1, 0xf0}, + {0xc2, 0x80}, + {0xc5, 0x01}, + {0xc9, 0x08}, + {0x0e, 0x80}, + {0x52, 0x46}, + {0x54, 0x80}, + {0x57, 0x01}, + {0xf6, 0x3b}, + {0x0e, 0x00}, + {0x67, 0x2f}, + {0x03, 0x4C}, //disable out put + SensorEnd +}; + +static v4l2_std_id adv7181_std_to_v4l2(u8 status1) +{ + /* in case V4L2_IN_ST_NO_SIGNAL */ + if (!(status1 & ADV7181_STATUS1_IN_LOCK)) + return V4L2_STD_UNKNOWN; + + switch (status1 & ADV7181_STATUS1_AUTOD_MASK) { + case ADV7181_STATUS1_AUTOD_PAL_M: + case ADV7181_STATUS1_AUTOD_NTSM_M_J: + return V4L2_STD_NTSC; + case ADV7181_STATUS1_AUTOD_NTSC_4_43: + return V4L2_STD_NTSC_443; + case ADV7181_STATUS1_AUTOD_PAL_60: + return V4L2_STD_PAL_60; + case ADV7181_STATUS1_AUTOD_PAL_B_G: + return V4L2_STD_PAL; + case ADV7181_STATUS1_AUTOD_SECAM: + return V4L2_STD_SECAM; + case ADV7181_STATUS1_AUTOD_PAL_COMB: + return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; + case ADV7181_STATUS1_AUTOD_SECAM_525: + return V4L2_STD_SECAM; + default: + return V4L2_STD_UNKNOWN; + } +} + +static u32 adv7181_status_to_v4l2(u8 status1) +{ + if (!(status1 & ADV7181_STATUS1_IN_LOCK)) + return V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int adv7181_vehicle_status(struct vehicle_ad_dev *ad, + u32 *status, + v4l2_std_id *std) +{ + unsigned char status1 = 0; + + status1 = vehicle_generic_sensor_read(ad, ADV7181_STATUS1_REG); + if (status1) + return status1; + + if (status) + *status = adv7181_status_to_v4l2(status1); + + if (std) + *std = adv7181_std_to_v4l2(status1); + + return 0; +} + +static void adv7181_reinit_parameter(struct vehicle_ad_dev *ad, v4l2_std_id std) +{ + int i; + + if (ad7181_g_addev->ad_chl == 0) { + ad->cfg.width = 1024; + ad->cfg.height = 500; + ad->cfg.start_x = 56; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 1; + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 60; + ad->cfg.type = V4L2_MBUS_PARALLEL; + ad->cfg.mbus_flags = V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_PCLK_SAMPLE_RISING; + } else if (std == V4L2_STD_PAL) { + ad->cfg.width = FORCE_PAL_WIDTH; + ad->cfg.height = FORCE_PAL_HEIGHT; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_PAL; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0; + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 25; + ad->cfg.type = V4L2_MBUS_PARALLEL; + ad->cfg.mbus_flags = V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_PCLK_SAMPLE_RISING; + } else { + ad->cfg.width = FORCE_NTSC_WIDTH; + ad->cfg.height = FORCE_NTSC_HEIGHT; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_NTSC; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 2; + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 30; + ad->cfg.type = V4L2_MBUS_PARALLEL; + ad->cfg.mbus_flags = V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_PCLK_SAMPLE_RISING; + } + + /* fix crop info from dts config */ + for (i = 0; i < 4; i++) { + if ((ad->defrects[i].width == ad->cfg.width) && + (ad->defrects[i].height == ad->cfg.height)) { + ad->cfg.start_x = ad->defrects[i].crop_x; + ad->cfg.start_y = ad->defrects[i].crop_y; + ad->cfg.width = ad->defrects[i].crop_width; + ad->cfg.height = ad->defrects[i].crop_height; + } + } + + SENSOR_DG("size %dx%d, crop(%d,%d)\n", + ad->cfg.width, ad->cfg.height, + ad->cfg.start_x, ad->cfg.start_y); +} + +static void adv7181_reg_init(struct vehicle_ad_dev *ad, unsigned char cvstd) +{ + struct rk_sensor_reg *sensor; + int i = 0; + unsigned char val[2]; + + switch (ad->ad_chl) { + case 0: + ad->ad_chl = ADV7181_INPUT_CVBS_AIN1; + break; + case 1: + ad->ad_chl = ADV7181_INPUT_CVBS_AIN6; + break; + case 2: + ad->ad_chl = ADV7181_INPUT_CVBS_AIN8; + break; + case 3: + ad->ad_chl = ADV7181_INPUT_CVBS_AIN10; + break; + case 4: + ad->ad_chl = ADV7181_INPUT_YPRPB_AIN6_8_10; + break; + default: + ad->ad_chl = ADV7181_INPUT_CVBS_AIN1; + } + val[0] = ad->ad_chl; + vehicle_generic_sensor_write(ad, ADV7181_INPUT_CONTROL, val); + + if (ad->ad_chl == ADV7181_INPUT_YPRPB_AIN6_8_10) { + SENSOR_DG("%s %d set sensor_preview_data_yprpb_p/p", __func__, __LINE__); + sensor = sensor_preview_data_yprpb_p; + } else { + SENSOR_DG("%s %d set n/p", __func__, __LINE__); + sensor = sensor_preview_data; + } + while ((sensor[i].reg != SEQCMD_END) && (sensor[i].reg != 0xFC000000)) { + if (sensor[i].reg == ADV7181_INPUT_CONTROL) { + SENSOR_DG("%s %d lkg test ad channel = %d\n", + __func__, __LINE__, ad->ad_chl); + } else { + val[0] = sensor[i].val; + vehicle_generic_sensor_write(ad, sensor[i].reg, val); + } + i++; + } + + val[0] = ad->ad_chl; + vehicle_generic_sensor_write(ad, ADV7181_INPUT_CONTROL, val); +} + +int adv7181_ad_get_cfg(struct vehicle_cfg **cfg) +{ + u32 status; + + if (!ad7181_g_addev) + return -1; + + adv7181_vehicle_status(ad7181_g_addev, &status, NULL); + + ad7181_g_addev->cfg.ad_ready = true; + + *cfg = &ad7181_g_addev->cfg; + + return 0; +} + +void adv7181_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line) +{ + SENSOR_DG("%s, last_line %d\n", __func__, last_line); + if (last_line < 1) + return; + + ad->cif_error_last_line = last_line; + if (std_old == V4L2_STD_PAL) { + if (last_line == FORCE_NTSC_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (std_old == V4L2_STD_NTSC) { + if (last_line == FORCE_PAL_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } +} + +int adv7181_check_id(struct vehicle_ad_dev *ad) +{ + int ret = 0; + int val; + + val = vehicle_generic_sensor_read(ad, 0x11); + SENSOR_DG("%s vehicle read 0x11 --> 0x%02x\n", ad->ad_name, val); + if (val != 0x20) { + SENSOR_DG("%s vehicle wrong camera ID, expected 0x20, detected 0x%02x\n", + ad->ad_name, val); + ret = -EINVAL; + } + + return ret; +} + +static int adv7181_check_std(struct vehicle_ad_dev *ad, v4l2_std_id *std) +{ + u32 status = 0; + + adv7181_vehicle_status(ad, &status, std); + + if (status != 0) { /* No signal */ + mdelay(30); + adv7181_vehicle_status(ad, &status, std); + SENSOR_DG("status 0x%x\n", status); + } + + return 0; +} +void adv7181_channel_set(struct vehicle_ad_dev *ad, int channel) +{ + static int channel_change = 11; + v4l2_std_id std = 0; + + ad->ad_chl = channel; + adv7181_reg_init(ad, std); + adv7181_check_std(ad, &std); + adv7181_reinit_parameter(ad, std); + if (channel_change != ad->ad_chl) { + SENSOR_DG("%s %d channel changed now channel = %d old_channel = %d\n", + __func__, __LINE__, ad->ad_chl, channel); + channel_change = ad->ad_chl; + vehicle_ad_stat_change_notify(); + } +} + +int adv7181_stream(struct vehicle_ad_dev *ad, int value) +{ + char val; + + if (value) + val = 0x0c; //on + else + val = 0x4c; + + SENSOR_DG("stream write 0x%x to reg 0x03\n", val); + vehicle_generic_sensor_write(ad, 0x03, &val); + if (value) + val = 0x47; //on + else + val = 0x87; + + SENSOR_DG("stream write 0x%x to reg 0x01d\n", val); + vehicle_generic_sensor_write(ad, 0x1d, &val); + + return 0; +} + +static void power_on(struct vehicle_ad_dev *ad) +{ + /* gpio_direction_output(ad->power, ad->pwr_active); */ + + if (gpio_is_valid(ad->powerdown)) { + gpio_request(ad->powerdown, "ad_powerdown"); + gpio_direction_output(ad->powerdown, !ad->pwdn_active); + /* gpio_set_value(ad->powerdown, !ad->pwdn_active); */ + } + + if (gpio_is_valid(ad->power)) { + gpio_request(ad->power, "ad_power"); + gpio_direction_output(ad->power, ad->pwr_active); + /* gpio_set_value(ad->power, ad->pwr_active); */ + } + + if (gpio_is_valid(ad->reset)) { + gpio_request(ad->reset, "ad_reset"); + gpio_direction_output(ad->reset, 0); + usleep_range(10000, 12000); + gpio_set_value(ad->reset, 1); + usleep_range(10000, 12000); + } +} + +static void power_off(struct vehicle_ad_dev *ad) +{ + if (gpio_is_valid(ad->powerdown)) + gpio_free(ad->powerdown); + + if (gpio_is_valid(ad->power)) + gpio_free(ad->power); + + if (gpio_is_valid(ad->reset)) + gpio_free(ad->reset); +} + +static void adv7181_check_state_work(struct work_struct *work) +{ + struct vehicle_ad_dev *ad; + v4l2_std_id std; + + ad = ad7181_g_addev; + + if (ad->cif_error_last_line > 0) + ad->cif_error_last_line = 0; + + adv7181_check_std(ad, &std); + SENSOR_DG("%s:new std(%llx), std_old(%llx)\n", __func__, std, std_old); + if (std != std_old) { + std_old = std; + adv7181_reinit_parameter(ad, std); + SENSOR_DG("%s:ad signal change notify\n", __func__); + vehicle_ad_stat_change_notify(); + } + + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(3000)); +} + +int adv7181_ad_deinit(void) +{ + struct vehicle_ad_dev *ad; + + ad = ad7181_g_addev; + + if (!ad) + return -ENODEV; + + if (ad->state_check_work.state_check_wq) { + cancel_delayed_work_sync(&ad->state_check_work.work); + flush_delayed_work(&ad->state_check_work.work); + flush_workqueue(ad->state_check_work.state_check_wq); + destroy_workqueue(ad->state_check_work.state_check_wq); + } + if (ad->irq) + free_irq(ad->irq, ad); + power_off(ad); + + return 0; +} + +int adv7181_ad_init(struct vehicle_ad_dev *ad) +{ + v4l2_std_id std = V4L2_STD_NTSC; + + if (!ad) + return -1; + + ad7181_g_addev = ad; + + /* 1. i2c init */ + while (ad->adapter == NULL) { + ad->adapter = i2c_get_adapter(ad->i2c_chl); + usleep_range(10000, 12000); + } + + if (!i2c_check_functionality(ad->adapter, I2C_FUNC_I2C)) + return -EIO; + + /* 2. ad power on sequence */ + power_on(ad); + + /* fix mode */ + adv7181_check_std(ad, &std); + std_old = std; + SENSOR_DG("std: %s\n", (std == V4L2_STD_NTSC) ? "ntsc" : "pal"); + SENSOR_DG("std_old: %s\n", (std_old == V4L2_STD_NTSC) ? "ntsc" : "pal"); + + /* 3 .init default format params */ + adv7181_reg_init(ad, std); + adv7181_reinit_parameter(ad, std); + vehicle_ad_stat_change_notify(); + + /* 5. create workqueue to detect signal change */ + INIT_DELAYED_WORK(&ad->state_check_work.work, adv7181_check_state_work); + ad->state_check_work.state_check_wq = + create_singlethread_workqueue("vehicle-ad-adv7181"); + + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(100)); + + return 0; +} + + diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_7181.h b/drivers/video/rockchip/vehicle/vehicle_ad_7181.h new file mode 100644 index 000000000000..b4e572ed0ef8 --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_7181.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Vehicle driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ +#ifndef __VEHICLE_AD_7181_H__ +#define __VEHICLE_AD_7181_H__ + +int adv7181_ad_init(struct vehicle_ad_dev *ad); +int adv7181_ad_deinit(void); +int adv7181_ad_get_cfg(struct vehicle_cfg **cfg); +int adv7181_stream(struct vehicle_ad_dev *ad, int value); +void adv7181_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line); +int adv7181_check_id(struct vehicle_ad_dev *ad); +void adv7181_channel_set(struct vehicle_ad_dev *ad, int channel); + +#endif + diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_gc2145.c b/drivers/video/rockchip/vehicle/vehicle_ad_gc2145.c new file mode 100644 index 000000000000..a015f0d7a204 --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_gc2145.c @@ -0,0 +1,1149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * vehicle sensor gc2145 + * + * Copyright (C) 2020 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vehicle_cfg.h" +#include "vehicle_main.h" +#include "vehicle_ad.h" +#include "vehicle_ad_gc2145.h" +#include + +enum { + CVSTD_720P60 = 0, + CVSTD_720P50, + CVSTD_1080P30, + CVSTD_1080P25, + CVSTD_720P30, + CVSTD_720P25, + CVSTD_SVGAP30, + CVSTD_SD, + CVSTD_NTSC, + CVSTD_PAL +}; + +enum { + FORCE_PAL_WIDTH = 960, + FORCE_PAL_HEIGHT = 576, + FORCE_NTSC_WIDTH = 960, + FORCE_NTSC_HEIGHT = 480, + FORCE_SVGA_WIDTH = 800, + FORCE_SVGA_HEIGHT = 600, + FORCE_CIF_OUTPUT_FORMAT = CIF_OUTPUT_FORMAT_422, +}; + +enum { + VIDEO_UNPLUG, + VIDEO_IN, + VIDEO_LOCKED, + VIDEO_UNLOCK +}; +#define FLAG_LOSS (0x1 << 7) +#define FLAG_V_LOCKED (0x1 << 6) +#define FLAG_H_LOCKED (0x1 << 5) +#define FLAG_CARRIER_PLL_LOCKED (0x1 << 4) +#define FLAG_VIDEO_DETECTED (0x1 << 3) +#define FLAG_EQ_SD_DETECTED (0x1 << 2) +#define FLAG_PROGRESSIVE (0x1 << 1) +#define FLAG_NO_CARRIER (0x1 << 0) +#define FLAG_LOCKED (FLAG_V_LOCKED | FLAG_H_LOCKED) + +static struct vehicle_ad_dev *gc2145_g_addev; +static int cvstd_mode = CVSTD_SVGAP30; +static int cvstd_old = CVSTD_NTSC; +//static int cvstd_sd = CVSTD_NTSC; +static int cvstd_state = VIDEO_UNPLUG; +static int cvstd_old_state = VIDEO_UNLOCK; + +#define SENSOR_REGISTER_LEN 1 /* sensor register address bytes*/ +#define SENSOR_VALUE_LEN 1 /* sensor register value bytes*/ + +struct rk_sensor_reg { + unsigned int reg; + unsigned int val; +}; + +#define SENSOR_CHANNEL_REG 0x41 + +#define SEQCMD_END 0xFF000000 +#define SensorEnd {SEQCMD_END, 0x00} + +#define SENSOR_DG VEHICLE_DG +#define SENSOR_ID(_msb, _lsb) ((_msb) << 8 | (_lsb)) + +/* Preview resolution setting*/ +static struct rk_sensor_reg sensor_preview_data_svga_30hz[] = { + {0xfe, 0xf0}, + {0xfe, 0xf0}, + {0xfe, 0xf0}, + {0xfc, 0x06}, + {0xf6, 0x00}, + {0xf7, 0x1d}, + {0xf8, 0x84}, + {0xfa, 0x00}, + {0xf9, 0xfe}, + {0xf2, 0x00}, + /*ISP reg*/ + {0xfe, 0x00}, + {0x03, 0x04}, + {0x04, 0xe2}, + {0x09, 0x00}, + {0x0a, 0x00}, + {0x0b, 0x00}, + {0x0c, 0x00}, + {0x0d, 0x04}, + {0x0e, 0xc0}, + {0x0f, 0x06}, + {0x10, 0x52}, + {0x12, 0x2e}, + {0x17, 0x14}, + {0x18, 0x22}, + {0x19, 0x0e}, + {0x1a, 0x01}, + {0x1b, 0x4b}, + {0x1c, 0x07}, + {0x1d, 0x10}, + {0x1e, 0x88}, + {0x1f, 0x78}, + {0x20, 0x03}, + {0x21, 0x40}, + {0x22, 0xa0}, + {0x24, 0x3f}, + {0x25, 0x01}, + {0x26, 0x10}, + {0x2d, 0x60}, + {0x30, 0x01}, + {0x31, 0x90}, + {0x33, 0x06}, + {0x34, 0x01}, + {0xfe, 0x00}, + {0x80, 0x7f}, + {0x81, 0x26}, + {0x82, 0xfa}, + {0x83, 0x00}, + {0x84, 0x00}, + {0x86, 0x02}, + {0x88, 0x03}, + {0x89, 0x03}, + {0x85, 0x08}, + {0x8a, 0x00}, + {0x8b, 0x00}, + {0xb0, 0x55}, + {0xc3, 0x00}, + {0xc4, 0x80}, + {0xc5, 0x90}, + {0xc6, 0x3b}, + {0xc7, 0x46}, + {0xec, 0x06}, + {0xed, 0x04}, + {0xee, 0x60}, + {0xef, 0x90}, + {0xb6, 0x01}, + {0x90, 0x01}, + {0x91, 0x00}, + {0x92, 0x00}, + {0x93, 0x00}, + {0x94, 0x00}, + {0x95, 0x04}, + {0x96, 0xb0}, + {0x97, 0x06}, + {0x98, 0x40}, + /*BLK*/ + {0xfe, 0x00}, + {0x40, 0x42}, + {0x41, 0x00}, + {0x43, 0x5b}, + {0x5e, 0x00}, + {0x5f, 0x00}, + {0x60, 0x00}, + {0x61, 0x00}, + {0x62, 0x00}, + {0x63, 0x00}, + {0x64, 0x00}, + {0x65, 0x00}, + {0x66, 0x20}, + {0x67, 0x20}, + {0x68, 0x20}, + {0x69, 0x20}, + {0x76, 0x00}, + {0x6a, 0x08}, + {0x6b, 0x08}, + {0x6c, 0x08}, + {0x6d, 0x08}, + {0x6e, 0x08}, + {0x6f, 0x08}, + {0x70, 0x08}, + {0x71, 0x08}, + {0x76, 0x00}, + {0x72, 0xf0}, + {0x7e, 0x3c}, + {0x7f, 0x00}, + {0xfe, 0x02}, + {0x48, 0x15}, + {0x49, 0x00}, + {0x4b, 0x0b}, + {0xfe, 0x00}, + /*AEC*/ + {0xfe, 0x01}, + {0x01, 0x04}, + {0x02, 0xc0}, + {0x03, 0x04}, + {0x04, 0x90}, + {0x05, 0x30}, + {0x06, 0x90}, + {0x07, 0x30}, + {0x08, 0x80}, + {0x09, 0x00}, + {0x0a, 0x82}, + {0x0b, 0x11}, + {0x0c, 0x10}, + {0x11, 0x10}, + {0x13, 0x7b}, + {0x17, 0x00}, + {0x1c, 0x11}, + {0x1e, 0x61}, + {0x1f, 0x35}, + {0x20, 0x40}, + {0x22, 0x40}, + {0x23, 0x20}, + {0xfe, 0x02}, + {0x0f, 0x04}, + {0xfe, 0x01}, + {0x12, 0x35}, + {0x15, 0xb0}, + {0x10, 0x31}, + {0x3e, 0x28}, + {0x3f, 0xb0}, + {0x40, 0x90}, + {0x41, 0x0f}, + + /*INTPEE*/ + {0xfe, 0x02}, + {0x90, 0x6c}, + {0x91, 0x03}, + {0x92, 0xcb}, + {0x94, 0x33}, + {0x95, 0x84}, + {0x97, 0x45}, + {0xa2, 0x11}, + {0xfe, 0x00}, + /*DNDD*/ + {0xfe, 0x02}, + {0x80, 0xc1}, + {0x81, 0x08}, + {0x82, 0x1f}, + {0x83, 0x10}, + {0x84, 0x0a}, + {0x86, 0xf0}, + {0x87, 0x50}, + {0x88, 0x15}, + {0x89, 0xb0}, + {0x8a, 0x30}, + {0x8b, 0x10}, + /*ASDE*/ + {0xfe, 0x01}, + {0x21, 0x04}, + {0xfe, 0x02}, + {0xa3, 0x50}, + {0xa4, 0x20}, + {0xa5, 0x40}, + {0xa6, 0x80}, + {0xab, 0x40}, + {0xae, 0x0c}, + {0xb3, 0x46}, + {0xb4, 0x64}, + {0xb6, 0x38}, + {0xb7, 0x01}, + {0xb9, 0x2b}, + {0x3c, 0x04}, + {0x3d, 0x15}, + {0x4b, 0x06}, + {0x4c, 0x20}, + {0xfe, 0x00}, + /*GAMMA*/ + /*gamma1*/ + {0xfe, 0x02}, + {0x10, 0x09}, + {0x11, 0x0d}, + {0x12, 0x13}, + {0x13, 0x19}, + {0x14, 0x27}, + {0x15, 0x37}, + {0x16, 0x45}, + {0x17, 0x53}, + {0x18, 0x69}, + {0x19, 0x7d}, + {0x1a, 0x8f}, + {0x1b, 0x9d}, + {0x1c, 0xa9}, + {0x1d, 0xbd}, + {0x1e, 0xcd}, + {0x1f, 0xd9}, + {0x20, 0xe3}, + {0x21, 0xea}, + {0x22, 0xef}, + {0x23, 0xf5}, + {0x24, 0xf9}, + {0x25, 0xff}, + {0xfe, 0x00}, + {0xc6, 0x20}, + {0xc7, 0x2b}, + /*gamma2*/ + {0xfe, 0x02}, + {0x26, 0x0f}, + {0x27, 0x14}, + {0x28, 0x19}, + {0x29, 0x1e}, + {0x2a, 0x27}, + {0x2b, 0x33}, + {0x2c, 0x3b}, + {0x2d, 0x45}, + {0x2e, 0x59}, + {0x2f, 0x69}, + {0x30, 0x7c}, + {0x31, 0x89}, + {0x32, 0x98}, + {0x33, 0xae}, + {0x34, 0xc0}, + {0x35, 0xcf}, + {0x36, 0xda}, + {0x37, 0xe2}, + {0x38, 0xe9}, + {0x39, 0xf3}, + {0x3a, 0xf9}, + {0x3b, 0xff}, + /*YCP*/ + {0xfe, 0x02}, + {0xd1, 0x40}, + {0xd2, 0x40}, + {0xd3, 0x48}, + {0xd6, 0xf0}, + {0xd7, 0x10}, + {0xd8, 0xda}, + {0xdd, 0x14}, + {0xde, 0x86}, + {0xed, 0x80}, + {0xee, 0x00}, + {0xef, 0x3f}, + {0xd8, 0xd8}, + /*abs*/ + {0xfe, 0x01}, + {0x9f, 0x40}, + /*LSC*/ + {0xfe, 0x01}, + {0xc2, 0x14}, + {0xc3, 0x0d}, + {0xc4, 0x0c}, + {0xc8, 0x15}, + {0xc9, 0x0d}, + {0xca, 0x0a}, + {0xbc, 0x24}, + {0xbd, 0x10}, + {0xbe, 0x0b}, + {0xb6, 0x25}, + {0xb7, 0x16}, + {0xb8, 0x15}, + {0xc5, 0x00}, + {0xc6, 0x00}, + {0xc7, 0x00}, + {0xcb, 0x00}, + {0xcc, 0x00}, + {0xcd, 0x00}, + {0xbf, 0x07}, + {0xc0, 0x00}, + {0xc1, 0x00}, + {0xb9, 0x00}, + {0xba, 0x00}, + {0xbb, 0x00}, + {0xaa, 0x01}, + {0xab, 0x01}, + {0xac, 0x00}, + {0xad, 0x05}, + {0xae, 0x06}, + {0xaf, 0x0e}, + {0xb0, 0x0b}, + {0xb1, 0x07}, + {0xb2, 0x06}, + {0xb3, 0x17}, + {0xb4, 0x0e}, + {0xb5, 0x0e}, + {0xd0, 0x09}, + {0xd1, 0x00}, + {0xd2, 0x00}, + {0xd6, 0x08}, + {0xd7, 0x00}, + {0xd8, 0x00}, + {0xd9, 0x00}, + {0xda, 0x00}, + {0xdb, 0x00}, + {0xd3, 0x0a}, + {0xd4, 0x00}, + {0xd5, 0x00}, + {0xa4, 0x00}, + {0xa5, 0x00}, + {0xa6, 0x77}, + {0xa7, 0x77}, + {0xa8, 0x77}, + {0xa9, 0x77}, + {0xa1, 0x80}, + {0xa2, 0x80}, + + {0xfe, 0x01}, + {0xdf, 0x0d}, + {0xdc, 0x25}, + {0xdd, 0x30}, + {0xe0, 0x77}, + {0xe1, 0x80}, + {0xe2, 0x77}, + {0xe3, 0x90}, + {0xe6, 0x90}, + {0xe7, 0xa0}, + {0xe8, 0x90}, + {0xe9, 0xa0}, + {0xfe, 0x00}, + /*AWB*/ + {0xfe, 0x01}, + {0x4f, 0x00}, + {0x4f, 0x00}, + {0x4b, 0x01}, + {0x4f, 0x00}, + + {0x4c, 0x01}, + {0x4d, 0x71}, + {0x4e, 0x01}, + {0x4c, 0x01}, + {0x4d, 0x91}, + {0x4e, 0x01}, + {0x4c, 0x01}, + {0x4d, 0x70}, + {0x4e, 0x01}, + {0x4c, 0x01}, + {0x4d, 0x90}, + {0x4e, 0x02}, + {0x4c, 0x01}, + {0x4d, 0xb0}, + {0x4e, 0x02}, + {0x4c, 0x01}, + {0x4d, 0x8f}, + {0x4e, 0x02}, + {0x4c, 0x01}, + {0x4d, 0x6f}, + {0x4e, 0x02}, + {0x4c, 0x01}, + {0x4d, 0xaf}, + {0x4e, 0x02}, + {0x4c, 0x01}, + {0x4d, 0xd0}, + {0x4e, 0x02}, + {0x4c, 0x01}, + {0x4d, 0xf0}, + {0x4e, 0x02}, + {0x4c, 0x01}, + {0x4d, 0xcf}, + {0x4e, 0x02}, + {0x4c, 0x01}, + {0x4d, 0xef}, + {0x4e, 0x02}, + {0x4c, 0x01}, + {0x4d, 0x6e}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0x8e}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0xae}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0xce}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0x4d}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0x6d}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0x8d}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0xad}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0xcd}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0x4c}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0x6c}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0x8c}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0xac}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0xcc}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0xcb}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0x4b}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0x6b}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0x8b}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0xab}, + {0x4e, 0x03}, + {0x4c, 0x01}, + {0x4d, 0x8a}, + {0x4e, 0x04}, + {0x4c, 0x01}, + {0x4d, 0xaa}, + {0x4e, 0x04}, + {0x4c, 0x01}, + {0x4d, 0xca}, + {0x4e, 0x04}, + {0x4c, 0x01}, + {0x4d, 0xca}, + {0x4e, 0x04}, + {0x4c, 0x01}, + {0x4d, 0xc9}, + {0x4e, 0x04}, + {0x4c, 0x01}, + {0x4d, 0x8a}, + {0x4e, 0x04}, + {0x4c, 0x01}, + {0x4d, 0x89}, + {0x4e, 0x04}, + {0x4c, 0x01}, + {0x4d, 0xa9}, + {0x4e, 0x04}, + {0x4c, 0x02}, + {0x4d, 0x0b}, + {0x4e, 0x05}, + {0x4c, 0x02}, + {0x4d, 0x0a}, + {0x4e, 0x05}, + {0x4c, 0x01}, + {0x4d, 0xeb}, + {0x4e, 0x05}, + {0x4c, 0x01}, + {0x4d, 0xea}, + {0x4e, 0x05}, + {0x4c, 0x02}, + {0x4d, 0x09}, + {0x4e, 0x05}, + {0x4c, 0x02}, + {0x4d, 0x29}, + {0x4e, 0x05}, + {0x4c, 0x02}, + {0x4d, 0x2a}, + {0x4e, 0x05}, + {0x4c, 0x02}, + {0x4d, 0x4a}, + {0x4e, 0x05}, + {0x4c, 0x02}, + {0x4d, 0x8a}, + {0x4e, 0x06}, + {0x4c, 0x02}, + {0x4d, 0x49}, + {0x4e, 0x06}, + {0x4c, 0x02}, + {0x4d, 0x69}, + {0x4e, 0x06}, + {0x4c, 0x02}, + {0x4d, 0x89}, + {0x4e, 0x06}, + {0x4c, 0x02}, + {0x4d, 0xa9}, + {0x4e, 0x06}, + {0x4c, 0x02}, + {0x4d, 0x48}, + {0x4e, 0x06}, + {0x4c, 0x02}, + {0x4d, 0x68}, + {0x4e, 0x06}, + {0x4c, 0x02}, + {0x4d, 0x69}, + {0x4e, 0x06}, + {0x4c, 0x02}, + {0x4d, 0xca}, + {0x4e, 0x07}, + {0x4c, 0x02}, + {0x4d, 0xc9}, + {0x4e, 0x07}, + {0x4c, 0x02}, + {0x4d, 0xe9}, + {0x4e, 0x07}, + {0x4c, 0x03}, + {0x4d, 0x09}, + {0x4e, 0x07}, + {0x4c, 0x02}, + {0x4d, 0xc8}, + {0x4e, 0x07}, + {0x4c, 0x02}, + {0x4d, 0xe8}, + {0x4e, 0x07}, + {0x4c, 0x02}, + {0x4d, 0xa7}, + {0x4e, 0x07}, + {0x4c, 0x02}, + {0x4d, 0xc7}, + {0x4e, 0x07}, + {0x4c, 0x02}, + {0x4d, 0xe7}, + {0x4e, 0x07}, + {0x4c, 0x03}, + {0x4d, 0x07}, + {0x4e, 0x07}, + + {0x4f, 0x01}, + {0x50, 0x80}, + {0x51, 0xa8}, + {0x52, 0x47}, + {0x53, 0x38}, + {0x54, 0xc7}, + {0x56, 0x0e}, + {0x58, 0x08}, + {0x5b, 0x00}, + {0x5c, 0x74}, + {0x5d, 0x8b}, + {0x61, 0xdb}, + {0x62, 0xb8}, + {0x63, 0x86}, + {0x64, 0xc0}, + {0x65, 0x04}, + {0x67, 0xa8}, + {0x68, 0xb0}, + {0x69, 0x00}, + {0x6a, 0xa8}, + {0x6b, 0xb0}, + {0x6c, 0xaf}, + {0x6d, 0x8b}, + {0x6e, 0x50}, + {0x6f, 0x18}, + {0x73, 0xf0}, + {0x70, 0x0d}, + {0x71, 0x60}, + {0x72, 0x80}, + {0x74, 0x01}, + {0x75, 0x01}, + {0x7f, 0x0c}, + {0x76, 0x70}, + {0x77, 0x58}, + {0x78, 0xa0}, + {0x79, 0x5e}, + {0x7a, 0x54}, + {0x7b, 0x58}, + {0xfe, 0x00}, + /*CC*/ + {0xfe, 0x02}, + {0xc0, 0x01}, + {0xc1, 0x44}, + {0xc2, 0xfd}, + {0xc3, 0x04}, + {0xc4, 0xF0}, + {0xc5, 0x48}, + {0xc6, 0xfd}, + {0xc7, 0x46}, + {0xc8, 0xfd}, + {0xc9, 0x02}, + {0xca, 0xe0}, + {0xcb, 0x45}, + {0xcc, 0xec}, + {0xcd, 0x48}, + {0xce, 0xf0}, + {0xcf, 0xf0}, + {0xe3, 0x0c}, + {0xe4, 0x4b}, + {0xe5, 0xe0}, + /*ABS*/ + {0xfe, 0x01}, + {0x9f, 0x40}, + {0xfe, 0x00}, + /*OUTPUT*/ + {0xfe, 0x00}, + {0xf2, 0x0f}, + /*dark sun*/ + {0xfe, 0x02}, + {0x40, 0xbf}, + {0x46, 0xcf}, + {0xfe, 0x00}, + + /*frame rate 50Hz*/ + {0xfe, 0x00}, + {0x05, 0x02}, + {0x06, 0x20}, + {0x07, 0x00}, + {0x08, 0x32}, + {0xfe, 0x01}, + {0x25, 0x00}, + {0x26, 0xfa}, + + {0x27, 0x04}, + {0x28, 0xe2}, + {0x29, 0x04}, + {0x2a, 0xe2}, + {0x2b, 0x04}, + {0x2c, 0xe2}, + {0x2d, 0x04}, + {0x2e, 0xe2}, + {0xfe, 0x00}, + + {0xfe, 0x00}, + {0xfd, 0x01}, + {0xfa, 0x00}, + /*crop window*/ + {0xfe, 0x00}, + {0x90, 0x01}, + {0x91, 0x00}, + {0x92, 0x00}, + {0x93, 0x00}, + {0x94, 0x00}, + {0x95, 0x02}, + {0x96, 0x58}, + {0x97, 0x03}, + {0x98, 0x20}, + {0x99, 0x11}, + {0x9a, 0x06}, + /*AWB*/ + {0xfe, 0x00}, + {0xec, 0x02}, + {0xed, 0x02}, + {0xee, 0x30}, + {0xef, 0x48}, + {0xfe, 0x02}, + {0x9d, 0x08}, + {0xfe, 0x01}, + {0x74, 0x00}, + /*AEC*/ + {0xfe, 0x01}, + {0x01, 0x04}, + {0x02, 0x60}, + {0x03, 0x02}, + {0x04, 0x48}, + {0x05, 0x18}, + {0x06, 0x50}, + {0x07, 0x10}, + {0x08, 0x38}, + {0x0a, 0x80}, + {0x21, 0x04}, + {0xfe, 0x00}, + {0x20, 0x03}, + {0xfe, 0x00}, + + {0xfe, 0x00}, + {0x05, 0x02}, + {0x06, 0x20}, + {0x07, 0x00}, + {0x08, 0x50}, + {0xfe, 0x01}, + {0x25, 0x00}, + {0x26, 0xfa}, + + {0x27, 0x04}, + {0x28, 0xe2}, + {0x29, 0x04}, + {0x2a, 0xe2}, + {0x2b, 0x04}, + {0x2c, 0xe2}, + {0x2d, 0x04}, + {0x2e, 0xe2}, + + {0xfe, 0x00}, + {0xb6, 0x01}, + {0xfd, 0x01}, + {0xfa, 0x00}, + {0x18, 0x22}, + /*crop window*/ + {0xfe, 0x00}, + {0x90, 0x01}, + {0x91, 0x00}, + {0x92, 0x00}, + {0x93, 0x00}, + {0x94, 0x00}, + {0x95, 0x02}, + {0x96, 0x58}, + {0x97, 0x03}, + {0x98, 0x20}, + {0x99, 0x11}, + {0x9a, 0x06}, + /*AWB*/ + {0xfe, 0x00}, + {0xec, 0x02}, + {0xed, 0x02}, + {0xee, 0x30}, + {0xef, 0x48}, + {0xfe, 0x02}, + {0x9d, 0x08}, + {0xfe, 0x01}, + {0x74, 0x00}, + /*AEC*/ + {0xfe, 0x01}, + {0x01, 0x04}, + {0x02, 0x60}, + {0x03, 0x02}, + {0x04, 0x48}, + {0x05, 0x18}, + {0x06, 0x50}, + {0x07, 0x10}, + {0x08, 0x38}, + {0x0a, 0x80}, + {0x21, 0x04}, + {0xfe, 0x00}, + {0x20, 0x03}, + {0xfe, 0x00}, + SensorEnd +}; + +static void gc2145_reinit_parameter(struct vehicle_ad_dev *ad, unsigned char cvstd) +{ + int i = 0; + + switch (cvstd) { + case CVSTD_PAL: + case CVSTD_NTSC: + case CVSTD_SVGAP30: + default: + ad->cfg.width = 800; + ad->cfg.height = 600; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0;/*00 - UYVY*/ + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 30; + ad->cfg.type = V4L2_MBUS_PARALLEL; + ad->cfg.mbus_flags = V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_PCLK_SAMPLE_RISING; + break; + } + + /* fix crop info from dts config */ + for (i = 0; i < 4; i++) { + if ((ad->defrects[i].width == ad->cfg.width) && + (ad->defrects[i].height == ad->cfg.height)) { + ad->cfg.start_x = ad->defrects[i].crop_x; + ad->cfg.start_y = ad->defrects[i].crop_y; + ad->cfg.width = ad->defrects[i].crop_width; + ad->cfg.height = ad->defrects[i].crop_height; + } + } + +#ifdef CVBS_DOUBLE_FPS_MODE +#endif + SENSOR_DG("%s,crop(%d,%d)", __func__, ad->cfg.start_x, ad->cfg.start_y); +} + +static void gc2145_reg_init(struct vehicle_ad_dev *ad, unsigned char cvstd) +{ + struct rk_sensor_reg *sensor; + int i; + unsigned char val[2]; + + switch (cvstd) { + case CVSTD_SVGAP30: + sensor = sensor_preview_data_svga_30hz; + break; + default: + sensor = sensor_preview_data_svga_30hz; + break; + } + i = 0; + while ((sensor[i].reg != SEQCMD_END) && (sensor[i].reg != 0xFC000000)) { + if (sensor[i].reg == SENSOR_CHANNEL_REG) + sensor[i].val = ad->ad_chl; + + val[0] = sensor[i].val; + vehicle_generic_sensor_write(ad, sensor[i].reg, val); + i++; + } +} + +void gc2145_channel_set(struct vehicle_ad_dev *ad, int channel) +{ + unsigned int reg = 0x41; + unsigned char val[0]; + + val[0] = channel; + ad->ad_chl = channel; + + vehicle_generic_sensor_write(ad, reg, val); +} + +int gc2145_ad_get_cfg(struct vehicle_cfg **cfg) +{ + if (!gc2145_g_addev) + return -1; + + switch (cvstd_state) { + case VIDEO_UNPLUG: + gc2145_g_addev->cfg.ad_ready = false; + break; + case VIDEO_LOCKED: + gc2145_g_addev->cfg.ad_ready = true; + break; + case VIDEO_IN: + gc2145_g_addev->cfg.ad_ready = false; + break; + } + + gc2145_g_addev->cfg.ad_ready = true; + *cfg = &gc2145_g_addev->cfg; + + return 0; +} + +void gc2145_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line) +{ + SENSOR_DG("%s, last_line %d\n", __func__, last_line); + + if (last_line < 1) + return; + + ad->cif_error_last_line = last_line; + if (cvstd_mode == CVSTD_PAL) { + if (last_line == FORCE_NTSC_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (cvstd_mode == CVSTD_NTSC) { + if (last_line == FORCE_PAL_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (cvstd_mode == CVSTD_SVGAP30) { + if (last_line == FORCE_SVGA_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } +} + +int gc2145_check_id(struct vehicle_ad_dev *ad) +{ + int ret = 0; + int pidh, pidl; + unsigned short id; + + pidh = vehicle_generic_sensor_read(ad, 0xf0); + pidl = vehicle_generic_sensor_read(ad, 0xf1); + if (pidh != 0x21 || pidl != 0x45) { + SENSOR_DG("%s: expected 0x2145, detected 0x%02x 0x%02x\n", + ad->ad_name, pidh, pidl); + ret = -EINVAL; + } else { + id = SENSOR_ID(pidh, pidl); + SENSOR_DG("%s Found GC%04X sensor OK!\n", __func__, id); + } + + return ret; +} + +static int gc2145_check_cvstd(struct vehicle_ad_dev *ad, bool activate_check) +{ + return 0; +} + +int gc2145_stream(struct vehicle_ad_dev *ad, int enable) +{ + char val; + + SENSOR_DG("%s on(%d)\n", __func__, enable); + + if (enable) + val = 0x0f; //stream on + else + val = 0x00; //stream off + vehicle_generic_sensor_write(ad, 0xf2, &val); + + return 0; +} + +static void power_on(struct vehicle_ad_dev *ad) +{ + /* gpio_direction_output(ad->power, ad->pwr_active); */ + SENSOR_DG("gpio: power(%d), powerdown(%d)", ad->power, ad->powerdown); + if (gpio_is_valid(ad->power)) { + gpio_request(ad->power, "ad_power"); + gpio_direction_output(ad->power, ad->pwr_active); + /* gpio_set_value(ad->power, ad->pwr_active); */ + } + + if (gpio_is_valid(ad->powerdown)) { + gpio_request(ad->powerdown, "ad_powerdown"); + gpio_direction_output(ad->powerdown, !ad->pwdn_active); + /* gpio_set_value(ad->powerdown, !ad->pwdn_active); */ + } +} + +static void power_off(struct vehicle_ad_dev *ad) +{ + if (gpio_is_valid(ad->power)) + gpio_free(ad->power); + if (gpio_is_valid(ad->powerdown)) + gpio_free(ad->powerdown); +} + +static void gc2145_check_state_work(struct work_struct *work) +{ + struct vehicle_ad_dev *ad; + static bool is_first = true; + + ad = gc2145_g_addev; + + if (ad->cif_error_last_line > 0) { + gc2145_check_cvstd(ad, true); + ad->cif_error_last_line = 0; + } else { + gc2145_check_cvstd(ad, false); + } + + if (is_first) { + SENSOR_DG("%s:cvstd_old(%d), cvstd_mode(%d)\n", __func__, cvstd_old, cvstd_mode); + is_first = false; + } + + if (cvstd_old != cvstd_mode || cvstd_old_state != cvstd_state) { + SENSOR_DG("%s:ad sensor std mode change, cvstd_old(%d), cvstd_mode(%d)\n", + __func__, cvstd_old, cvstd_mode); + cvstd_old = cvstd_mode; + cvstd_old_state = cvstd_state; + SENSOR_DG("ad signal change notify\n"); + vehicle_ad_stat_change_notify(); + } + + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(100)); +} + +int gc2145_ad_deinit(void) +{ + struct vehicle_ad_dev *ad; + + ad = gc2145_g_addev; + + if (!ad) + return -1; + + if (ad->state_check_work.state_check_wq) { + cancel_delayed_work_sync(&ad->state_check_work.work); + flush_delayed_work(&ad->state_check_work.work); + flush_workqueue(ad->state_check_work.state_check_wq); + destroy_workqueue(ad->state_check_work.state_check_wq); + } + if (ad->irq) + free_irq(ad->irq, ad); + power_off(ad); + + return 0; +} + +static __maybe_unused int get_ad_mode_from_fix_format(int fix_format) +{ + int mode = -1; + + switch (fix_format) { + case AD_FIX_FORMAT_PAL: + case AD_FIX_FORMAT_NTSC: + case AD_FIX_FORMAT_720P_50FPS: + case AD_FIX_FORMAT_720P_30FPS: + case AD_FIX_FORMAT_720P_25FPS: + default: + mode = CVSTD_SVGAP30; + break; + } + + return mode; +} + +int gc2145_ad_init(struct vehicle_ad_dev *ad) +{ + int val; + int i = 0; + + gc2145_g_addev = ad; + + /* 1. i2c init */ + while (ad->adapter == NULL) { + ad->adapter = i2c_get_adapter(ad->i2c_chl); + usleep_range(10000, 12000); + } + if (ad->adapter == NULL) + return -ENODEV; + + if (!i2c_check_functionality(ad->adapter, I2C_FUNC_I2C)) + return -EIO; + + /* 2. ad power on sequence */ + power_on(ad); + + while (++i < 5) { + usleep_range(1000, 1200); + val = vehicle_generic_sensor_read(ad, 0xf0); + if (val != 0xff) + break; + SENSOR_DG("gc2145_init i2c_reg_read fail\n"); + } + + /* 3 .init default format params */ + gc2145_reg_init(ad, cvstd_mode); + gc2145_reinit_parameter(ad, cvstd_mode); + SENSOR_DG("%s after reinit init\n", __func__); + + /* 5. create workqueue to detect signal change */ + INIT_DELAYED_WORK(&ad->state_check_work.work, gc2145_check_state_work); + ad->state_check_work.state_check_wq = + create_singlethread_workqueue("vehicle-ad-gc2145"); + + /* gc2145_check_cvstd(ad, true); */ + + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(100)); + + return 0; +} + + diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_gc2145.h b/drivers/video/rockchip/vehicle/vehicle_ad_gc2145.h new file mode 100644 index 000000000000..1934bc2172ad --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_gc2145.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Vehicle driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ +#ifndef __VEHICLE_AD_GC2145_H__ +#define __VEHICLE_AD_GC2145_H__ + +int gc2145_ad_init(struct vehicle_ad_dev *ad); +int gc2145_ad_deinit(void); +int gc2145_ad_get_cfg(struct vehicle_cfg **cfg); +void gc2145_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line); +int gc2145_check_id(struct vehicle_ad_dev *ad); +int gc2145_stream(struct vehicle_ad_dev *ad, int enable); +void gc2145_channel_set(struct vehicle_ad_dev *ad, int channel); + +#endif diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_max96714.c b/drivers/video/rockchip/vehicle/vehicle_ad_max96714.c new file mode 100644 index 000000000000..e3926a5139ae --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_max96714.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * vehicle sensor max96714 + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + * Authors: + * Jianwei Fan + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vehicle_cfg.h" +#include "vehicle_main.h" +#include "vehicle_ad.h" +#include "vehicle_ad_max96714.h" + +enum { + CVSTD_720P60 = 0, + CVSTD_720P50, + CVSTD_1080P30, + CVSTD_1080P25, + CVSTD_720P30, + CVSTD_720P25, + CVSTD_SVGAP30, + CVSTD_SD, + CVSTD_NTSC, + CVSTD_PAL +}; + +enum { + FORCE_PAL_WIDTH = 960, + FORCE_PAL_HEIGHT = 576, + FORCE_NTSC_WIDTH = 960, + FORCE_NTSC_HEIGHT = 480, + FORCE_SVGA_WIDTH = 800, + FORCE_SVGA_HEIGHT = 600, + FORCE_720P_WIDTH = 1280, + FORCE_720P_HEIGHT = 720, + FORCE_1080P_WIDTH = 1920, + FORCE_1080P_HEIGHT = 1080, + FORCE_CIF_OUTPUT_FORMAT = CIF_OUTPUT_FORMAT_420, +}; + +enum { + VIDEO_UNPLUG, + VIDEO_IN, + VIDEO_LOCKED, + VIDEO_UNLOCK +}; + +#define FLAG_LOCKED (0x1 << 3) +#define MAX96714_LINK_FREQ_150M 150000000UL + +static struct vehicle_ad_dev *max96714_g_addev; +static int cvstd_mode = CVSTD_1080P30; +//static int cvstd_old = CVSTD_720P25; +static int cvstd_old = CVSTD_NTSC; + +//static int cvstd_sd = CVSTD_NTSC; +static int cvstd_state = VIDEO_UNPLUG; +static int cvstd_old_state = VIDEO_UNLOCK; + +static bool g_max96714_streaming; + +#define SENSOR_VALUE_LEN 1 /* sensor register value bytes*/ +#define MAX96714_CHIP_ID 0xC9 +#define MAX96714_CHIP_ID_REG 0x0D +#define MAX96714_GMSL_STATE 0x0013 +#define MAX96714_STREAM_CTL 0x0313 +#define MAX96714_MODE_SW_STANDBY 0x0 +#define MAX96714_MODE_STREAMING BIT(1) + +struct regval { + u16 reg; + u8 val; +}; +#define REG_NULL 0xFFFF + +/* 1080p Preview resolution setting*/ +static struct regval sensor_preview_data_1080p_30hz[] = { + {0x0313, 0x00}, + {0x0001, 0x01}, + {0x0010, 0x21}, + {0x0320, 0x23}, + {0x0325, 0x80}, + {0x0313, 0x00}, + {REG_NULL, 0x00}, +}; + +static struct rkmodule_csi_dphy_param max96714_dcphy_param = { + .vendor = PHY_VENDOR_SAMSUNG, + .lp_vol_ref = 3, + .lp_hys_sw = {3, 0, 0, 0}, + .lp_escclk_pol_sel = {1, 0, 0, 0}, + .skew_data_cal_clk = {0, 3, 3, 3}, + .clk_hs_term_sel = 2, + .data_hs_term_sel = {2, 2, 2, 2}, + .reserved = {0}, +}; + +static int max96714_read_reg(struct vehicle_ad_dev *ad, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = ad->i2c_add; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = ad->i2c_add; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(ad->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int max96714_write_reg(struct vehicle_ad_dev *ad, u16 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[3]; + int ret; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + buf[2] = val; + + msg.addr = ad->i2c_add; + msg.flags = 0; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(ad->adapter, &msg, 1); + if (ret >= 0) + return 0; + + VEHICLE_DGERR( + "max96714 write reg(0x%x val:0x%x) failed !\n", reg, val); + + return ret; +} + +static int max96714_write_array(struct vehicle_ad_dev *ad, + const struct regval *regs) +{ + u32 i = 0; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].reg != REG_NULL; i++) + ret = max96714_write_reg(ad, regs[i].reg, regs[i].val); + + return ret; +} + +static void max96714_reinit_parameter(struct vehicle_ad_dev *ad, unsigned char cvstd) +{ + int i = 0; + + switch (cvstd) { + case CVSTD_1080P30: + ad->cfg.width = 1920; + ad->cfg.height = 1080; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0;/*00 - UYVY*/ + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 30; + ad->cfg.mipi_freq = MAX96714_LINK_FREQ_150M; + break; + + default: + ad->cfg.width = 1920; + ad->cfg.height = 1080; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0;/*00 - UYVY*/ + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 30; + ad->cfg.mipi_freq = MAX96714_LINK_FREQ_150M; + break; + } + ad->cfg.type = V4L2_MBUS_CSI2_DPHY; + ad->cfg.mbus_flags = V4L2_MBUS_CSI2_4_LANE | V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | + V4L2_MBUS_CSI2_CHANNEL_0; + ad->cfg.mbus_code = MEDIA_BUS_FMT_UYVY8_2X8; + ad->cfg.dphy_param = &max96714_dcphy_param; + + switch (ad->cfg.mbus_flags & V4L2_MBUS_CSI2_LANES) { + case V4L2_MBUS_CSI2_1_LANE: + ad->cfg.lanes = 1; + break; + case V4L2_MBUS_CSI2_2_LANE: + ad->cfg.lanes = 2; + break; + case V4L2_MBUS_CSI2_3_LANE: + ad->cfg.lanes = 3; + break; + case V4L2_MBUS_CSI2_4_LANE: + ad->cfg.lanes = 4; + break; + default: + ad->cfg.lanes = 1; + break; + } + + /* fix crop info from dts config */ + for (i = 0; i < 4; i++) { + if ((ad->defrects[i].width == ad->cfg.width) && + (ad->defrects[i].height == ad->cfg.height)) { + ad->cfg.start_x = ad->defrects[i].crop_x; + ad->cfg.start_y = ad->defrects[i].crop_y; + ad->cfg.width = ad->defrects[i].crop_width; + ad->cfg.height = ad->defrects[i].crop_height; + } + } + + VEHICLE_DG("crop(%d,%d)", ad->cfg.start_x, ad->cfg.start_y); +} + +static void max96714_reg_init(struct vehicle_ad_dev *ad, unsigned char cvstd) +{ + struct regval *sensor; + int ret = 0; + + switch (cvstd) { + case CVSTD_1080P30: + VEHICLE_INFO("%s, init CVSTD_1080P30 mode", __func__); + sensor = sensor_preview_data_1080p_30hz; + break; + default: + VEHICLE_INFO("%s, init CVSTD_1080P30 mode", __func__); + sensor = sensor_preview_data_1080p_30hz; + break; + } + + ret = max96714_write_array(ad, sensor); + if (ret) + VEHICLE_DGERR("%s, init sensor fail", __func__); +} + +void max96714_channel_set(struct vehicle_ad_dev *ad, int channel) +{ +} + +int max96714_ad_get_cfg(struct vehicle_cfg **cfg) +{ + if (!max96714_g_addev) + return -1; + + switch (cvstd_state) { + case VIDEO_UNPLUG: + max96714_g_addev->cfg.ad_ready = false; + break; + case VIDEO_LOCKED: + max96714_g_addev->cfg.ad_ready = true; + break; + case VIDEO_IN: + max96714_g_addev->cfg.ad_ready = false; + break; + } + + max96714_g_addev->cfg.ad_ready = true; + + *cfg = &max96714_g_addev->cfg; + + return 0; +} + +void max96714_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line) +{ + VEHICLE_DG("last_line %d\n", last_line); + + if (last_line < 1) + return; + + ad->cif_error_last_line = last_line; + if (cvstd_mode == CVSTD_PAL) { + if (last_line == FORCE_NTSC_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (cvstd_mode == CVSTD_NTSC) { + if (last_line == FORCE_PAL_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (cvstd_mode == CVSTD_1080P30) { + if (last_line == FORCE_1080P_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } +} + +int max96714_check_id(struct vehicle_ad_dev *ad) +{ + int ret = 0; + u32 pid = 0; + + ret = max96714_read_reg(ad, MAX96714_CHIP_ID_REG, SENSOR_VALUE_LEN, &pid); + if (pid != MAX96714_CHIP_ID) { + VEHICLE_DGERR("%s: expected 0xC9, detected: 0x%02x !", + ad->ad_name, pid); + ret = -EINVAL; + } else { + VEHICLE_INFO("Found MAX96714 sensor: id(0x%2x) !\n", pid); + } + + return ret; +} + +static int max96714_check_cvstd(struct vehicle_ad_dev *ad, bool activate_check) +{ + static int state = VIDEO_UNPLUG; + int ret = 0; + + ret = max96714_read_reg(ad, MAX96714_GMSL_STATE, SENSOR_VALUE_LEN, &state); + if (ret) + VEHICLE_DGERR("read GMSL2 link lock failed!\n"); + + if (state & FLAG_LOCKED) { + state = VIDEO_LOCKED; + VEHICLE_DG("GMSL2 link locked!\n"); + cvstd_mode = CVSTD_1080P30; + } else { + state = VIDEO_UNPLUG; + VEHICLE_DG("GMSL2 link not locked!\n"); + cvstd_mode = cvstd_old; + } + + return 0; +} + +int max96714_stream(struct vehicle_ad_dev *ad, int enable) +{ + VEHICLE_INFO("%s on(%d)\n", __func__, enable); + + g_max96714_streaming = (enable != 0); + if (g_max96714_streaming) { + max96714_write_reg(ad, MAX96714_STREAM_CTL, MAX96714_MODE_STREAMING); + if (ad->state_check_work.state_check_wq) + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(200)); + } else { + max96714_write_reg(ad, MAX96714_STREAM_CTL, MAX96714_MODE_SW_STANDBY); + if (ad->state_check_work.state_check_wq) + cancel_delayed_work_sync(&ad->state_check_work.work); + } + + return 0; +} + +static void max96714_power_on(struct vehicle_ad_dev *ad) +{ + /* gpio_direction_output(ad->power, ad->pwr_active); */ + if (gpio_is_valid(ad->power)) { + gpio_request(ad->power, "max96714_power"); + gpio_direction_output(ad->power, ad->pwr_active); + /* gpio_set_value(ad->power, ad->pwr_active); */ + } + + if (gpio_is_valid(ad->powerdown)) { + gpio_request(ad->powerdown, "max96714_pwd"); + gpio_direction_output(ad->powerdown, 1); + /* gpio_set_value(ad->powerdown, !ad->pwdn_active); */ + } + + if (gpio_is_valid(ad->reset)) { + gpio_request(ad->reset, "max96714_rst"); + gpio_direction_output(ad->reset, 0); + usleep_range(1500, 2000); + gpio_direction_output(ad->reset, 1); + } +} + +static void max96714_power_deinit(struct vehicle_ad_dev *ad) +{ + if (gpio_is_valid(ad->reset)) + gpio_free(ad->reset); + if (gpio_is_valid(ad->power)) + gpio_free(ad->power); + if (gpio_is_valid(ad->powerdown)) + gpio_free(ad->powerdown); +} + +static void max96714_check_state_work(struct work_struct *work) +{ + struct vehicle_ad_dev *ad; + + ad = max96714_g_addev; + + if (ad->cif_error_last_line > 0) { + max96714_check_cvstd(ad, true); + ad->cif_error_last_line = 0; + } else { + max96714_check_cvstd(ad, false); + } + + VEHICLE_DG("%s:cvstd_old(%d), cvstd_mode(%d)\n", __func__, cvstd_old, cvstd_mode); + if (cvstd_old != cvstd_mode || cvstd_old_state != cvstd_state) { + VEHICLE_INFO("%s:ad sensor std mode change, cvstd_old(%d), cvstd_mode(%d)\n", + __func__, cvstd_old, cvstd_mode); + cvstd_old = cvstd_mode; + cvstd_old_state = cvstd_state; + max96714_reinit_parameter(ad, cvstd_mode); + max96714_reg_init(ad, cvstd_mode); + vehicle_ad_stat_change_notify(); + } + if (g_max96714_streaming) { + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(100)); + } +} + +int max96714_ad_deinit(void) +{ + struct vehicle_ad_dev *ad; + + ad = max96714_g_addev; + + if (!ad) + return -ENODEV; + + if (ad->state_check_work.state_check_wq) { + cancel_delayed_work_sync(&ad->state_check_work.work); + flush_delayed_work(&ad->state_check_work.work); + flush_workqueue(ad->state_check_work.state_check_wq); + destroy_workqueue(ad->state_check_work.state_check_wq); + } + if (ad->irq) + free_irq(ad->irq, ad); + max96714_power_deinit(ad); + + return 0; +} + +static __maybe_unused int get_ad_mode_from_fix_format(int fix_format) +{ + int mode = -1; + + switch (fix_format) { + case AD_FIX_FORMAT_PAL: + case AD_FIX_FORMAT_NTSC: + case AD_FIX_FORMAT_720P_50FPS: + case AD_FIX_FORMAT_720P_30FPS: + case AD_FIX_FORMAT_720P_25FPS: + mode = CVSTD_720P25; + break; + case AD_FIX_FORMAT_1080P_30FPS: + case AD_FIX_FORMAT_1080P_25FPS: + + default: + mode = CVSTD_1080P30; + break; + } + + return mode; +} + +int max96714_ad_init(struct vehicle_ad_dev *ad) +{ + max96714_g_addev = ad; + + /* 1. i2c init */ + while (ad->adapter == NULL) { + ad->adapter = i2c_get_adapter(ad->i2c_chl); + usleep_range(10000, 12000); + } + if (ad->adapter == NULL) + return -ENODEV; + + if (!i2c_check_functionality(ad->adapter, I2C_FUNC_I2C)) + return -EIO; + + max96714_power_on(ad); + + max96714_reg_init(ad, cvstd_mode); + + max96714_reinit_parameter(ad, cvstd_mode); + + INIT_DELAYED_WORK(&ad->state_check_work.work, max96714_check_state_work); + ad->state_check_work.state_check_wq = + create_singlethread_workqueue("vehicle-ad-max96714"); + + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(100)); + + return 0; +} diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_max96714.h b/drivers/video/rockchip/vehicle/vehicle_ad_max96714.h new file mode 100644 index 000000000000..5f1ce6fcda6a --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_max96714.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Vehicle driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ +#ifndef __VEHICLE_AD_MAX96714_H__ +#define __VEHICLE_AD_MAX96714_H__ + +int max96714_ad_init(struct vehicle_ad_dev *ad); +int max96714_ad_deinit(void); +int max96714_ad_get_cfg(struct vehicle_cfg **cfg); +void max96714_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line); +int max96714_check_id(struct vehicle_ad_dev *ad); +int max96714_stream(struct vehicle_ad_dev *ad, int enable); +void max96714_channel_set(struct vehicle_ad_dev *ad, int channel); + +#endif diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_nvp6188.c b/drivers/video/rockchip/vehicle/vehicle_ad_nvp6188.c new file mode 100644 index 000000000000..3c67848320d8 --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_nvp6188.c @@ -0,0 +1,1206 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * vehicle sensor nvp6188 + * + * Copyright (C) 2022 Rockchip Electronics Co.Ltd + * Authors: + * wpzz + * Jianwei Fan + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vehicle_cfg.h" +#include "vehicle_main.h" +#include "vehicle_ad.h" +#include "vehicle_ad_nvp6188.h" + +enum { + CVSTD_720P60 = 0, + CVSTD_720P50, + CVSTD_1080P30, + CVSTD_1080P25, + CVSTD_720P30, + CVSTD_720P25, + CVSTD_SVGAP30, + CVSTD_SD, + CVSTD_NTSC, + CVSTD_PAL +}; + +enum { + FORCE_PAL_WIDTH = 960, + FORCE_PAL_HEIGHT = 576, + FORCE_NTSC_WIDTH = 960, + FORCE_NTSC_HEIGHT = 480, + FORCE_SVGA_WIDTH = 800, + FORCE_SVGA_HEIGHT = 600, + FORCE_720P_WIDTH = 1280, + FORCE_720P_HEIGHT = 720, + FORCE_1080P_WIDTH = 1920, + FORCE_1080P_HEIGHT = 1080, + FORCE_CIF_OUTPUT_FORMAT = CIF_OUTPUT_FORMAT_420, +}; + +enum { + VIDEO_UNPLUG, + VIDEO_IN, + VIDEO_LOCKED, + VIDEO_UNLOCK +}; + +#define NVP6188_LINK_FREQ_1458M (1458000000UL >> 1) + +static struct vehicle_ad_dev *nvp6188_g_addev; +static int cvstd_mode = CVSTD_1080P25; +//static int cvstd_old = CVSTD_720P25; +static int cvstd_state = VIDEO_UNPLUG; +// static int cvstd_old_state = VIDEO_UNLOCK; + +static bool g_nvp6188_streaming; + +#define NVP6188_CHIP_ID 0xD3 +#define NVP6188_CHIP_ID2 0xD0 + +#define _MIPI_PORT0_ +#ifdef _MIPI_PORT0_ +#define _MAR_BANK_ 0x20 +#define _MTX_BANK_ 0x23 +#else +#define _MAR_BANK_ 0x30 +#define _MTX_BANK_ 0x33 +#endif + +#define NVP_RESO_960H_NSTC_VALUE 0x00 +#define NVP_RESO_960H_PAL_VALUE 0x10 +#define NVP_RESO_720P_NSTC_VALUE 0x20 +#define NVP_RESO_720P_PAL_VALUE 0x21 +#define NVP_RESO_1080P_NSTC_VALUE 0x30 +#define NVP_RESO_1080P_PAL_VALUE 0x31 +#define NVP_RESO_960P_NSTC_VALUE 0xa0 +#define NVP_RESO_960P_PAL_VALUE 0xa1 + +enum nvp6188_support_reso { + NVP_RESO_UNKNOWN = 0, + NVP_RESO_960H_PAL, + NVP_RESO_720P_PAL, + NVP_RESO_960P_PAL, + NVP_RESO_1080P_PAL, + NVP_RESO_960H_NSTC, + NVP_RESO_720P_NSTC, + NVP_RESO_960P_NSTC, + NVP_RESO_1080P_NSTC, +}; + +struct regval { + u8 addr; + u8 val; +}; + +static __maybe_unused const struct regval common_setting_1458M_regs[] = { + {0xff, 0x00}, + {0x80, 0x0f}, + {0x00, 0x10}, + {0x01, 0x10}, + {0x02, 0x10}, + {0x03, 0x10}, + {0x22, 0x0b}, + {0x23, 0x41}, + {0x26, 0x0b}, + {0x27, 0x41}, + {0x2a, 0x0b}, + {0x2b, 0x41}, + {0x2e, 0x0b}, + {0x2f, 0x41}, + + {0xff, 0x01}, + {0x98, 0x30}, + {0xed, 0x00}, + + {0xff, 0x05+0}, + {0x00, 0xd0}, + {0x01, 0x22}, + {0x47, 0xee}, + {0x50, 0xc6}, + {0x57, 0x00}, + {0x58, 0x77}, + {0x5b, 0x41}, + {0x5c, 0x78}, + {0xB8, 0xB8}, + + {0xff, 0x05+1}, + {0x00, 0xd0}, + {0x01, 0x22}, + {0x47, 0xee}, + {0x50, 0xc6}, + {0x57, 0x00}, + {0x58, 0x77}, + {0x5b, 0x41}, + {0x5c, 0x78}, + {0xB8, 0xB8}, + + {0xff, 0x05+2}, + {0x00, 0xd0}, + {0x01, 0x22}, + {0x47, 0xee}, + {0x50, 0xc6}, + {0x57, 0x00}, + {0x58, 0x77}, + {0x5b, 0x41}, + {0x5c, 0x78}, + {0xB8, 0xB8}, + + {0xff, 0x05+3}, + {0x00, 0xd0}, + {0x01, 0x22}, + {0x47, 0xee}, + {0x50, 0xc6}, + {0x57, 0x00}, + {0x58, 0x77}, + {0x5b, 0x41}, + {0x5c, 0x78}, + {0xB8, 0xB8}, + + {0xff, 0x09}, + {0x50, 0x30}, + {0x51, 0x6f}, + {0x52, 0x67}, + {0x53, 0x48}, + {0x54, 0x30}, + {0x55, 0x6f}, + {0x56, 0x67}, + {0x57, 0x48}, + {0x58, 0x30}, + {0x59, 0x6f}, + {0x5a, 0x67}, + {0x5b, 0x48}, + {0x5c, 0x30}, + {0x5d, 0x6f}, + {0x5e, 0x67}, + {0x5f, 0x48}, + + {0xff, 0x0a}, + {0x25, 0x10}, + {0x27, 0x1e}, + {0x30, 0xac}, + {0x31, 0x78}, + {0x32, 0x17}, + {0x33, 0xc1}, + {0x34, 0x40}, + {0x35, 0x00}, + {0x36, 0xc3}, + {0x37, 0x0a}, + {0x38, 0x00}, + {0x39, 0x02}, + {0x3a, 0x00}, + {0x3b, 0xb2}, + {0xa5, 0x10}, + {0xa7, 0x1e}, + {0xb0, 0xac}, + {0xb1, 0x78}, + {0xb2, 0x17}, + {0xb3, 0xc1}, + {0xb4, 0x40}, + {0xb5, 0x00}, + {0xb6, 0xc3}, + {0xb7, 0x0a}, + {0xb8, 0x00}, + {0xb9, 0x02}, + {0xba, 0x00}, + {0xbb, 0xb2}, + {0xff, 0x0b}, + {0x25, 0x10}, + {0x27, 0x1e}, + {0x30, 0xac}, + {0x31, 0x78}, + {0x32, 0x17}, + {0x33, 0xc1}, + {0x34, 0x40}, + {0x35, 0x00}, + {0x36, 0xc3}, + {0x37, 0x0a}, + {0x38, 0x00}, + {0x39, 0x02}, + {0x3a, 0x00}, + {0x3b, 0xb2}, + {0xa5, 0x10}, + {0xa7, 0x1e}, + {0xb0, 0xac}, + {0xb1, 0x78}, + {0xb2, 0x17}, + {0xb3, 0xc1}, + {0xb4, 0x40}, + {0xb5, 0x00}, + {0xb6, 0xc3}, + {0xb7, 0x0a}, + {0xb8, 0x00}, + {0xb9, 0x02}, + {0xba, 0x00}, + {0xbb, 0xb2}, + + {0xff, 0x13}, + {0x05, 0xa0}, + {0x31, 0xff}, + {0x07, 0x47}, + {0x12, 0x04}, + {0x1e, 0x1f}, + {0x1f, 0x27}, + {0x2e, 0x10}, + {0x2f, 0xc8}, + {0x31, 0xff}, + {0x32, 0x00}, + {0x33, 0x00}, + {0x72, 0x05}, + {0x7a, 0xf0}, + {0xff, _MAR_BANK_}, + {0x10, 0xff}, + {0x11, 0xff}, + + {0x30, 0x0f}, + {0x32, 0xff}, + {0x34, 0xcd}, + {0x36, 0x04}, + {0x38, 0xff}, + {0x3c, 0x01}, + {0x3d, 0x11}, + {0x3e, 0x11}, + {0x45, 0x60}, + {0x46, 0x49}, + + {0xff, _MTX_BANK_}, + {0xe9, 0x03}, + {0x03, 0x02}, + {0x01, 0xe4}, + {0x00, 0x7d}, + {0x01, 0xe0}, + {0x02, 0xa0}, + {0x20, 0x1e}, + {0x20, 0x1f}, + {0x04, 0x6c}, + {0x45, 0xcd}, + {0x46, 0x42}, + {0x47, 0x36}, + {0x48, 0x0f}, + {0x65, 0xcd}, + {0x66, 0x42}, + {0x67, 0x0e}, + {0x68, 0x0f}, + {0x85, 0xcd}, + {0x86, 0x42}, + {0x87, 0x0e}, + {0x88, 0x0f}, + {0xa5, 0xcd}, + {0xa6, 0x42}, + {0xa7, 0x0e}, + {0xa8, 0x0f}, + {0xc5, 0xcd}, + {0xc6, 0x42}, + {0xc7, 0x0e}, + {0xc8, 0x0f}, + {0xeb, 0x8d}, + + {0xff, _MAR_BANK_}, + {0x00, 0xff}, + {0x40, 0x01}, + {0x40, 0x00}, + {0xff, 0x01}, + {0x97, 0x00}, + {0x97, 0x0f}, + + {0xff, 0x00}, //test pattern + {0x78, 0xba}, + {0x79, 0xac}, + {0xff, 0x05}, + {0x2c, 0x08}, + {0x6a, 0x80}, + {0xff, 0x06}, + {0x2c, 0x08}, + {0x6a, 0x80}, + {0xff, 0x07}, + {0x2c, 0x08}, + {0x6a, 0x80}, + {0xff, 0x08}, + {0x2c, 0x08}, + {0x6a, 0x80}, +}; + +static __maybe_unused const struct regval auto_detect_regs[] = { + {0xFF, 0x13}, + {0x30, 0x7f}, + {0x70, 0xf0}, + + {0xFF, 0x00}, + {0x00, 0x18}, + {0x01, 0x18}, + {0x02, 0x18}, + {0x03, 0x18}, + + {0x00, 0x10}, + {0x01, 0x10}, + {0x02, 0x10}, + {0x03, 0x10}, +}; + +static void nvp6188_reinit_parameter(struct vehicle_ad_dev *ad, unsigned char cvstd) +{ + int i = 0; + + switch (cvstd) { + case CVSTD_720P25: + ad->cfg.width = 1280; + ad->cfg.height = 720; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0;/*00 - UYVY*/ + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 25; + ad->cfg.mipi_freq = NVP6188_LINK_FREQ_1458M; + break; + + case CVSTD_1080P25: + ad->cfg.width = 1920; + ad->cfg.height = 1080; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0;/*00 - UYVY*/ + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 25; + ad->cfg.mipi_freq = NVP6188_LINK_FREQ_1458M; + break; + + case CVSTD_NTSC: + ad->cfg.width = 960; + ad->cfg.height = 480; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0;/*00 - UYVY*/ + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 25; + ad->cfg.mipi_freq = NVP6188_LINK_FREQ_1458M; + break; + + default: + ad->cfg.width = 1920; + ad->cfg.height = 1080; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0;/*00 - UYVY*/ + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 25; + ad->cfg.mipi_freq = NVP6188_LINK_FREQ_1458M; + break; + } + ad->cfg.type = V4L2_MBUS_CSI2_DPHY; + ad->cfg.mbus_flags = V4L2_MBUS_CSI2_4_LANE | V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK | + V4L2_MBUS_CSI2_CHANNELS; + ad->cfg.mbus_code = MEDIA_BUS_FMT_UYVY8_2X8; + + switch (ad->cfg.mbus_flags & V4L2_MBUS_CSI2_LANES) { + case V4L2_MBUS_CSI2_1_LANE: + ad->cfg.lanes = 1; + break; + case V4L2_MBUS_CSI2_2_LANE: + ad->cfg.lanes = 2; + break; + case V4L2_MBUS_CSI2_3_LANE: + ad->cfg.lanes = 3; + break; + case V4L2_MBUS_CSI2_4_LANE: + ad->cfg.lanes = 4; + break; + default: + ad->cfg.lanes = 1; + break; + } + + /* fix crop info from dts config */ + for (i = 0; i < 4; i++) { + if ((ad->defrects[i].width == ad->cfg.width) && + (ad->defrects[i].height == ad->cfg.height)) { + ad->cfg.start_x = ad->defrects[i].crop_x; + ad->cfg.start_y = ad->defrects[i].crop_y; + ad->cfg.width = ad->defrects[i].crop_width; + ad->cfg.height = ad->defrects[i].crop_height; + } + } +} + +/* sensor register write */ +static int nvp6188_write_reg(struct vehicle_ad_dev *ad, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + int ret; + + buf[0] = reg & 0xFF; + buf[1] = val; + + msg.addr = ad->i2c_add; + msg.flags = 0; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(ad->adapter, &msg, 1); + if (ret >= 0) { + usleep_range(300, 400); + return 0; + } + + VEHICLE_DGERR("nvp6188 write reg(0x%x val:0x%x) failed !\n", reg, val); + + return ret; +} + +static int nvp6188_write_array(struct vehicle_ad_dev *ad, + const struct regval *regs, int size) +{ + int i, ret = 0; + + i = 0; + while (i < size) { + ret = nvp6188_write_reg(ad, regs[i].addr, regs[i].val); + if (ret) { + VEHICLE_DGERR("%s failed !\n", __func__); + break; + } + i++; + } + + return ret; +} + +/* sensor register read */ +static int nvp6188_read_reg(struct vehicle_ad_dev *ad, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[1]; + int ret; + + buf[0] = reg & 0xFF; + + msg[0].addr = ad->i2c_add; + msg[0].flags = 0; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = ad->i2c_add; + msg[1].flags = 0 | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(ad->adapter, msg, 2); + if (ret >= 0) { + *val = buf[0]; + return 0; + } + + VEHICLE_DGERR("nvp6188 read reg(0x%x) failed !\n", reg); + + return ret; +} + +static unsigned char nv6188_read_vfc(struct vehicle_ad_dev *ad, unsigned char ch) +{ + unsigned char ch_vfc = 0xff; + + nvp6188_write_reg(ad, 0xff, 0x05 + ch); + nvp6188_read_reg(ad, 0xf0, &ch_vfc); + return ch_vfc; +} + +static __maybe_unused int nvp6188_read_all_vfc(struct vehicle_ad_dev *ad, + u8 *ch_vfc) +{ + int ret = 0; + int check_cnt = 0, ch = 0; + + ret = nvp6188_write_array(ad, + auto_detect_regs, ARRAY_SIZE(auto_detect_regs)); + if (ret) + VEHICLE_DGERR("write auto_detect_regs failed %d", ret); + + ret = -1; + while ((check_cnt++) < 50) { + for (ch = 0; ch < 4; ch++) + ch_vfc[ch] = nv6188_read_vfc(ad, ch); + + if (ch_vfc[0] != 0xff || ch_vfc[1] != 0xff || + ch_vfc[2] != 0xff || ch_vfc[3] != 0xff) { + ret = 0; + if (ch == 3) { + VEHICLE_DGERR("try check cnt %d", check_cnt); + break; + } + } else { + usleep_range(20 * 1000, 40 * 1000); + } + } + + if (ret) + VEHICLE_DGERR("read vfc failed %d", ret); + else + VEHICLE_INFO("read vfc 0x%2x 0x%2x 0x%2x 0x%2x", + ch_vfc[0], ch_vfc[1], ch_vfc[2], ch_vfc[3]); + + return ret; +} + +static __maybe_unused int nvp6188_auto_detect_fmt(struct vehicle_ad_dev *ad) +{ + int ret = 0; + int ch = 0; + unsigned char ch_vfc[4] = { 0xff, 0xff, 0xff, 0xff }; + unsigned char val_13x70 = 0, val_13x71 = 0; + + if (nvp6188_read_all_vfc(ad, ch_vfc)) + return -1; + ch = ad->ad_chl; + // for (ch = 0; ch < 4; ch++) { + nvp6188_write_reg(ad, 0xFF, 0x13); + nvp6188_read_reg(ad, 0x70, &val_13x70); + val_13x70 |= (0x01 << ch); + nvp6188_write_reg(ad, 0x70, val_13x70); + nvp6188_read_reg(ad, 0x71, &val_13x71); + val_13x71 |= (0x01 << ch); + nvp6188_write_reg(ad, 0x71, val_13x71); + switch (ch_vfc[ch]) { + case NVP_RESO_960H_NSTC_VALUE: + VEHICLE_INFO("channel %d det 960h nstc", ch); + ad->channel_reso[ch] = NVP_RESO_960H_NSTC; + break; + case NVP_RESO_960H_PAL_VALUE: + VEHICLE_INFO("channel %d det 960h pal", ch); + ad->channel_reso[ch] = NVP_RESO_960H_PAL; + break; + case NVP_RESO_720P_NSTC_VALUE: + VEHICLE_INFO("channel %d det 720p nstc", ch); + ad->channel_reso[ch] = NVP_RESO_720P_NSTC; + break; + case NVP_RESO_720P_PAL_VALUE: + VEHICLE_INFO("channel %d det 720p pal", ch); + ad->channel_reso[ch] = NVP_RESO_720P_PAL; + break; + case NVP_RESO_1080P_NSTC_VALUE: + VEHICLE_INFO("channel %d det 1080p nstc", ch); + ad->channel_reso[ch] = NVP_RESO_1080P_NSTC; + break; + case NVP_RESO_1080P_PAL_VALUE: + VEHICLE_INFO("channel %d det 1080p pal", ch); + ad->channel_reso[ch] = NVP_RESO_1080P_PAL; + break; + case NVP_RESO_960P_NSTC_VALUE: + VEHICLE_INFO("channel %d det 960p nstc", ch); + ad->channel_reso[ch] = NVP_RESO_960P_NSTC; + break; + case NVP_RESO_960P_PAL_VALUE: + VEHICLE_INFO("channel %d det 960p pal", ch); + ad->channel_reso[ch] = NVP_RESO_960P_PAL; + break; + default: + VEHICLE_INFO("channel %d not detect, def 1080p pal\n", ch); + ad->channel_reso[ch] = NVP_RESO_1080P_PAL; + break; + } + // } + return ret; +} + +//each channel setting +/* + * 960x480i + * ch : 0 ~ 3 + * ntpal: 1:25p, 0:30p + */ +static __maybe_unused void nv6188_set_chn_960h(struct vehicle_ad_dev *ad, u8 ch, + u8 ntpal) +{ + unsigned char val_0x54 = 0, val_20x01 = 0; + + VEHICLE_INFO("%s ch %d ntpal %d", __func__, ch, ntpal); + nvp6188_write_reg(ad, 0xff, 0x00); + nvp6188_write_reg(ad, 0x08 + ch, ntpal ? 0xdd : 0xa0); + nvp6188_write_reg(ad, 0x18 + ch, 0x08); + nvp6188_write_reg(ad, 0x22 + ch * 4, 0x0b); + nvp6188_write_reg(ad, 0x23 + ch * 4, 0x41); + nvp6188_write_reg(ad, 0x30 + ch, 0x12); + nvp6188_write_reg(ad, 0x34 + ch, 0x01); + nvp6188_read_reg(ad, 0x54, &val_0x54); + if (ntpal) + val_0x54 &= ~(0x10 << ch); + else + val_0x54 |= (0x10 << ch); + nvp6188_write_reg(ad, 0x54, val_0x54); + nvp6188_write_reg(ad, 0x58 + ch, ntpal ? 0x80 : 0x90); + nvp6188_write_reg(ad, 0x5c + ch, ntpal ? 0xbe : 0xbc); + nvp6188_write_reg(ad, 0x64 + ch, ntpal ? 0xa0 : 0x81); + nvp6188_write_reg(ad, 0x81 + ch, ntpal ? 0xf0 : 0xe0); + nvp6188_write_reg(ad, 0x85 + ch, 0x00); + nvp6188_write_reg(ad, 0x89 + ch, 0x00); + nvp6188_write_reg(ad, ch + 0x8e, 0x00); + nvp6188_write_reg(ad, 0xa0 + ch, 0x05); + + nvp6188_write_reg(ad, 0xff, 0x01); + nvp6188_write_reg(ad, 0x84 + ch, 0x02); + nvp6188_write_reg(ad, 0x88 + ch, 0x00); + nvp6188_write_reg(ad, 0x8c + ch, 0x40); + nvp6188_write_reg(ad, 0xa0 + ch, 0x20); + nvp6188_write_reg(ad, 0xed, 0x00); + + nvp6188_write_reg(ad, 0xff, 0x05 + ch); + nvp6188_write_reg(ad, 0x01, 0x22); + nvp6188_write_reg(ad, 0x05, 0x00); + nvp6188_write_reg(ad, 0x08, 0x55); + nvp6188_write_reg(ad, 0x25, 0xdc); + nvp6188_write_reg(ad, 0x28, 0x80); + nvp6188_write_reg(ad, 0x2f, 0x00); + nvp6188_write_reg(ad, 0x30, 0xe0); + nvp6188_write_reg(ad, 0x31, 0x43); + nvp6188_write_reg(ad, 0x32, 0xa2); + nvp6188_write_reg(ad, 0x47, 0x04); + nvp6188_write_reg(ad, 0x50, 0x84); + nvp6188_write_reg(ad, 0x57, 0x00); + nvp6188_write_reg(ad, 0x58, 0x77); + nvp6188_write_reg(ad, 0x5b, 0x43); + nvp6188_write_reg(ad, 0x5c, 0x78); + nvp6188_write_reg(ad, 0x5f, 0x00); + nvp6188_write_reg(ad, 0x62, 0x20); + nvp6188_write_reg(ad, 0x7b, 0x00); + nvp6188_write_reg(ad, 0x7c, 0x01); + nvp6188_write_reg(ad, 0x7d, 0x80); + nvp6188_write_reg(ad, 0x80, 0x00); + nvp6188_write_reg(ad, 0x90, 0x01); + nvp6188_write_reg(ad, 0xa9, 0x00); + nvp6188_write_reg(ad, 0xb5, 0x00); + nvp6188_write_reg(ad, 0xb8, 0xb9); + nvp6188_write_reg(ad, 0xb9, 0x72); + nvp6188_write_reg(ad, 0xd1, 0x00); + nvp6188_write_reg(ad, 0xd5, 0x80); + + nvp6188_write_reg(ad, 0xff, 0x09); + nvp6188_write_reg(ad, 0x96 + ch * 0x20, 0x10); + nvp6188_write_reg(ad, 0x98 + ch * 0x20, ntpal ? 0xc0 : 0xe0); + nvp6188_write_reg(ad, ch * 0x20 + 0x9e, 0x00); + + nvp6188_write_reg(ad, 0xff, _MAR_BANK_); + nvp6188_read_reg(ad, 0x01, &val_20x01); + val_20x01 &= (~(0x03 << (ch * 2))); + val_20x01 |= (0x02 << (ch * 2)); + nvp6188_write_reg(ad, 0x01, val_20x01); + nvp6188_write_reg(ad, 0x12 + ch * 2, 0xe0); + nvp6188_write_reg(ad, 0x13 + ch * 2, 0x01); +} + +//each channel setting +/* + * 1280x720p + * ch : 0 ~ 3 + * ntpal: 1:25p, 0:30p + */ +static __maybe_unused void nv6188_set_chn_720p(struct vehicle_ad_dev *ad, u8 ch, + u8 ntpal) +{ + unsigned char val_0x54 = 0, val_20x01 = 0; + + VEHICLE_INFO("%s ch %d ntpal %d", __func__, ch, ntpal); + nvp6188_write_reg(ad, 0xff, 0x00); + nvp6188_write_reg(ad, 0x08 + ch, 0x00); + nvp6188_write_reg(ad, 0x18 + ch, 0x3f); + nvp6188_write_reg(ad, 0x30 + ch, 0x12); + nvp6188_write_reg(ad, 0x34 + ch, 0x00); + nvp6188_read_reg(ad, 0x54, &val_0x54); + val_0x54 &= ~(0x10 << ch); + nvp6188_write_reg(ad, 0x54, val_0x54); + nvp6188_write_reg(ad, 0x58 + ch, ntpal ? 0x80 : 0x80); + nvp6188_write_reg(ad, 0x5c + ch, ntpal ? 0x00 : 0x00); + nvp6188_write_reg(ad, 0x64 + ch, ntpal ? 0x01 : 0x01); + nvp6188_write_reg(ad, 0x81 + ch, ntpal ? 0x0d : 0x0c); + nvp6188_write_reg(ad, 0x85 + ch, 0x00); + nvp6188_write_reg(ad, 0x89 + ch, 0x00); + nvp6188_write_reg(ad, ch + 0x8e, 0x00); + nvp6188_write_reg(ad, 0xa0 + ch, 0x05); + + nvp6188_write_reg(ad, 0xff, 0x01); + nvp6188_write_reg(ad, 0x84 + ch, 0x02); + nvp6188_write_reg(ad, 0x88 + ch, 0x00); + nvp6188_write_reg(ad, 0x8c + ch, 0x40); + nvp6188_write_reg(ad, 0xa0 + ch, 0x20); + + nvp6188_write_reg(ad, 0xff, 0x05 + ch); + nvp6188_write_reg(ad, 0x01, 0x22); + nvp6188_write_reg(ad, 0x05, 0x04); + nvp6188_write_reg(ad, 0x08, 0x55); + nvp6188_write_reg(ad, 0x25, 0xdc); + nvp6188_write_reg(ad, 0x28, 0x80); + nvp6188_write_reg(ad, 0x2f, 0x00); + nvp6188_write_reg(ad, 0x30, 0xe0); + nvp6188_write_reg(ad, 0x31, 0x43); + nvp6188_write_reg(ad, 0x32, 0xa2); + nvp6188_write_reg(ad, 0x47, 0xee); + nvp6188_write_reg(ad, 0x50, 0xc6); + nvp6188_write_reg(ad, 0x57, 0x00); + nvp6188_write_reg(ad, 0x58, 0x77); + nvp6188_write_reg(ad, 0x5b, 0x41); + nvp6188_write_reg(ad, 0x5c, 0x7C); + nvp6188_write_reg(ad, 0x5f, 0x00); + nvp6188_write_reg(ad, 0x62, 0x20); + nvp6188_write_reg(ad, 0x7b, 0x11); + nvp6188_write_reg(ad, 0x7c, 0x01); + nvp6188_write_reg(ad, 0x7d, 0x80); + nvp6188_write_reg(ad, 0x80, 0x00); + nvp6188_write_reg(ad, 0x90, 0x01); + nvp6188_write_reg(ad, 0xa9, 0x00); + nvp6188_write_reg(ad, 0xb5, 0x40); + nvp6188_write_reg(ad, 0xb8, 0x39); + nvp6188_write_reg(ad, 0xb9, 0x72); + nvp6188_write_reg(ad, 0xd1, 0x00); + nvp6188_write_reg(ad, 0xd5, 0x80); + + nvp6188_write_reg(ad, 0xff, 0x09); + nvp6188_write_reg(ad, 0x96 + ch * 0x20, 0x00); + nvp6188_write_reg(ad, 0x98 + ch * 0x20, 0x00); + nvp6188_write_reg(ad, ch * 0x20 + 0x9e, 0x00); + + nvp6188_write_reg(ad, 0xff, _MAR_BANK_); + nvp6188_read_reg(ad, 0x01, &val_20x01); + val_20x01 &= (~(0x03 << (ch * 2))); + val_20x01 |= (0x01 << (ch * 2)); + nvp6188_write_reg(ad, 0x01, val_20x01); + nvp6188_write_reg(ad, 0x12 + ch * 2, 0x80); + nvp6188_write_reg(ad, 0x13 + ch * 2, 0x02); +} + +//each channel setting +/* + * 1920x1080p + * ch : 0 ~ 3 + * ntpal: 1:25p, 0:30p + */ +static __maybe_unused void nv6188_set_chn_1080p(struct vehicle_ad_dev *ad, u8 ch, + u8 ntpal) +{ + unsigned char val_0x54 = 0, val_20x01 = 0; + + VEHICLE_INFO("%s ch %d ntpal %d", __func__, ch, ntpal); + nvp6188_write_reg(ad, 0xff, 0x00); + nvp6188_write_reg(ad, 0x08 + ch, 0x00); + nvp6188_write_reg(ad, 0x18 + ch, 0x3f); + nvp6188_write_reg(ad, 0x30 + ch, 0x12); + nvp6188_write_reg(ad, 0x34 + ch, 0x00); + nvp6188_read_reg(ad, 0x54, &val_0x54); + val_0x54 &= ~(0x10 << ch); + nvp6188_write_reg(ad, 0x54, val_0x54); + nvp6188_write_reg(ad, 0x58 + ch, ntpal ? 0x80 : 0x80); + nvp6188_write_reg(ad, 0x5c + ch, ntpal ? 0x00 : 0x00); + nvp6188_write_reg(ad, 0x64 + ch, ntpal ? 0x01 : 0x01); + nvp6188_write_reg(ad, 0x81 + ch, ntpal ? 0x03 : 0x02); + nvp6188_write_reg(ad, 0x85 + ch, 0x00); + nvp6188_write_reg(ad, 0x89 + ch, 0x10); + nvp6188_write_reg(ad, ch + 0x8e, 0x00); + nvp6188_write_reg(ad, 0xa0 + ch, 0x05); + + nvp6188_write_reg(ad, 0xff, 0x01); + nvp6188_write_reg(ad, 0x84 + ch, 0x02); + nvp6188_write_reg(ad, 0x88 + ch, 0x00); + nvp6188_write_reg(ad, 0x8c + ch, 0x40); + nvp6188_write_reg(ad, 0xa0 + ch, 0x20); + + nvp6188_write_reg(ad, 0xff, 0x05 + ch); + nvp6188_write_reg(ad, 0x01, 0x22); + nvp6188_write_reg(ad, 0x05, 0x04); + nvp6188_write_reg(ad, 0x08, 0x55); + nvp6188_write_reg(ad, 0x25, 0xdc); + nvp6188_write_reg(ad, 0x28, 0x80); + nvp6188_write_reg(ad, 0x2f, 0x00); + nvp6188_write_reg(ad, 0x30, 0xe0); + nvp6188_write_reg(ad, 0x31, 0x41); + nvp6188_write_reg(ad, 0x32, 0xa2); + nvp6188_write_reg(ad, 0x47, 0xee); + nvp6188_write_reg(ad, 0x50, 0xc6); + nvp6188_write_reg(ad, 0x57, 0x00); + nvp6188_write_reg(ad, 0x58, 0x77); + nvp6188_write_reg(ad, 0x5b, 0x41); + nvp6188_write_reg(ad, 0x5c, 0x7C); + nvp6188_write_reg(ad, 0x5f, 0x00); + nvp6188_write_reg(ad, 0x62, 0x20); + nvp6188_write_reg(ad, 0x7b, 0x11); + nvp6188_write_reg(ad, 0x7c, 0x01); + nvp6188_write_reg(ad, 0x7d, 0x80); + nvp6188_write_reg(ad, 0x80, 0x00); + nvp6188_write_reg(ad, 0x90, 0x01); + nvp6188_write_reg(ad, 0xa9, 0x00); + nvp6188_write_reg(ad, 0xb5, 0x40); + nvp6188_write_reg(ad, 0xb8, 0x39); + nvp6188_write_reg(ad, 0xb9, 0x72); + nvp6188_write_reg(ad, 0xd1, 0x00); + nvp6188_write_reg(ad, 0xd5, 0x80); + + nvp6188_write_reg(ad, 0xff, 0x09); + nvp6188_write_reg(ad, 0x96 + ch * 0x20, 0x00); + nvp6188_write_reg(ad, 0x98 + ch * 0x20, 0x00); + nvp6188_write_reg(ad, ch * 0x20 + 0x9e, 0x00); + + nvp6188_write_reg(ad, 0xff, _MAR_BANK_); + nvp6188_read_reg(ad, 0x01, &val_20x01); + val_20x01 &= (~(0x03 << (ch * 2))); + nvp6188_write_reg(ad, 0x01, val_20x01); + nvp6188_write_reg(ad, 0x12 + ch * 2, 0xc0); + nvp6188_write_reg(ad, 0x13 + ch * 2, 0x03); +} + +static __maybe_unused void nvp6188_manual_mode(struct vehicle_ad_dev *ad) +{ + int i, reso; + + for (i = 3; i >= 0; i--) { + reso = ad->channel_reso[i]; + switch (reso) { + case NVP_RESO_960H_PAL: + nv6188_set_chn_960h(ad, i, 1); + break; + case NVP_RESO_720P_PAL: + nv6188_set_chn_720p(ad, i, 1); + break; + case NVP_RESO_1080P_PAL: + nv6188_set_chn_1080p(ad, i, 1); + break; + case NVP_RESO_960H_NSTC: + nv6188_set_chn_960h(ad, i, 0); + break; + case NVP_RESO_720P_NSTC: + nv6188_set_chn_720p(ad, i, 0); + break; + case NVP_RESO_1080P_NSTC: + nv6188_set_chn_1080p(ad, i, 0); + break; + default: + nv6188_set_chn_1080p(ad, i, 1); + break; + } + } +} + +void nvp6188_channel_set(struct vehicle_ad_dev *ad, int channel) +{ + ad->ad_chl = channel; + VEHICLE_DG("%s, channel set(%d)", __func__, ad->ad_chl); +} + +int nvp6188_ad_get_cfg(struct vehicle_cfg **cfg) +{ + if (!nvp6188_g_addev) + return -1; + + switch (cvstd_state) { + case VIDEO_UNPLUG: + nvp6188_g_addev->cfg.ad_ready = false; + break; + case VIDEO_LOCKED: + nvp6188_g_addev->cfg.ad_ready = true; + break; + case VIDEO_IN: + nvp6188_g_addev->cfg.ad_ready = false; + break; + } + + nvp6188_g_addev->cfg.ad_ready = true; + + *cfg = &nvp6188_g_addev->cfg; + + return 0; +} + +void nvp6188_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line) +{ + VEHICLE_INFO("%s, last_line %d\n", __func__, last_line); + + if (last_line < 1) + return; + + ad->cif_error_last_line = last_line; + if (cvstd_mode == CVSTD_PAL) { + if (last_line == FORCE_NTSC_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (cvstd_mode == CVSTD_NTSC) { + if (last_line == FORCE_PAL_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (cvstd_mode == CVSTD_1080P25) { + if (last_line == FORCE_1080P_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (cvstd_mode == CVSTD_720P25) { + if (last_line == FORCE_720P_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } +} + +int nvp6188_check_id(struct vehicle_ad_dev *ad) +{ + int ret = 0; + u8 pid = 0; + + ret = vehicle_sensor_write(ad, 0xFF, 0x00); + ret |= vehicle_sensor_read(ad, 0xf4, &pid); + if (ret) + return ret; + + if (pid != NVP6188_CHIP_ID && pid != NVP6188_CHIP_ID2) { + VEHICLE_DGERR("%s: expected 0xd0/d3, detected: 0x%02x !", + ad->ad_name, pid); + ret = -EINVAL; + } else { + VEHICLE_INFO("%s Found NVP6188 sensor: id(0x%2x) !\n", __func__, pid); + } + + return ret; +} + +static int __nvp6188_start_stream(struct vehicle_ad_dev *ad) +{ + int ret; + int array_size = 0; + + array_size = ARRAY_SIZE(common_setting_1458M_regs); + + ret = nvp6188_write_array(ad, + common_setting_1458M_regs, array_size); + if (ret) { + VEHICLE_INFO(" nvp6188 start stream: wrote global reg failed"); + return ret; + } + + nvp6188_auto_detect_fmt(ad); + nvp6188_manual_mode(ad); + nvp6188_write_reg(ad, 0xff, 0x20); + nvp6188_write_reg(ad, 0xff, 0xff); + msleep(50); + + return 0; +} + +static int __nvp6188_stop_stream(struct vehicle_ad_dev *ad) +{ + nvp6188_write_reg(ad, 0xff, 0x20); + nvp6188_write_reg(ad, 0x00, 0x00); + nvp6188_write_reg(ad, 0x40, 0x01); + nvp6188_write_reg(ad, 0x40, 0x00); + + return 0; +} + +int nvp6188_stream(struct vehicle_ad_dev *ad, int enable) +{ + VEHICLE_INFO("%s on(%d)\n", __func__, enable); + + g_nvp6188_streaming = (enable != 0); + if (g_nvp6188_streaming) { + __nvp6188_start_stream(ad); + if (ad->state_check_work.state_check_wq) + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(200)); + } else { + __nvp6188_stop_stream(ad); + if (ad->state_check_work.state_check_wq) + cancel_delayed_work_sync(&ad->state_check_work.work); + VEHICLE_DG("%s(%d): cancel_queue_delayed_work!\n", __func__, __LINE__); + } + + return 0; +} + +static void nvp6188_power_on(struct vehicle_ad_dev *ad) +{ + if (gpio_is_valid(ad->power)) { + gpio_request(ad->power, "nvp6188_power"); + gpio_direction_output(ad->power, ad->pwr_active); + /* gpio_set_value(ad->power, ad->pwr_active); */ + } + + if (gpio_is_valid(ad->powerdown)) { + gpio_request(ad->powerdown, "nvp6188_pwd"); + gpio_direction_output(ad->powerdown, 1); + /* gpio_set_value(ad->powerdown, !ad->pwdn_active); */ + } + + if (gpio_is_valid(ad->reset)) { + gpio_request(ad->reset, "nvp6188_rst"); + gpio_direction_output(ad->reset, 0); + usleep_range(1500, 2000); + gpio_direction_output(ad->reset, 1); + } +} + +static void nvp6188_power_off(struct vehicle_ad_dev *ad) +{ + if (gpio_is_valid(ad->reset)) + gpio_free(ad->reset); + if (gpio_is_valid(ad->power)) + gpio_free(ad->power); + if (gpio_is_valid(ad->powerdown)) + gpio_free(ad->powerdown); +} + +static __maybe_unused int nvp6188_auto_detect_hotplug(struct vehicle_ad_dev *ad) +{ + nvp6188_write_reg(ad, 0xff, 0x00); + nvp6188_read_reg(ad, 0xa8, &ad->detect_status); + + ad->detect_status = ~ad->detect_status; + + return 0; +} + +static void nvp6188_check_state_work(struct work_struct *work) +{ + struct vehicle_ad_dev *ad; + + ad = nvp6188_g_addev; + nvp6188_auto_detect_hotplug(ad); + + if (ad->detect_status != ad->last_detect_status) { + ad->last_detect_status = ad->detect_status; + vehicle_ad_stat_change_notify(); + } + + if (g_nvp6188_streaming) { + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(100)); + } +} + +int nvp6188_ad_deinit(void) +{ + struct vehicle_ad_dev *ad; + + ad = nvp6188_g_addev; + + if (!ad) + return -1; + + if (ad->state_check_work.state_check_wq) { + cancel_delayed_work_sync(&ad->state_check_work.work); + flush_delayed_work(&ad->state_check_work.work); + flush_workqueue(ad->state_check_work.state_check_wq); + destroy_workqueue(ad->state_check_work.state_check_wq); + } + + nvp6188_power_off(ad); + + return 0; +} + +static __maybe_unused int get_ad_mode_from_fix_format(int fix_format) +{ + int mode = -1; + + switch (fix_format) { + case AD_FIX_FORMAT_PAL: + case AD_FIX_FORMAT_NTSC: + case AD_FIX_FORMAT_720P_50FPS: + case AD_FIX_FORMAT_720P_30FPS: + case AD_FIX_FORMAT_720P_25FPS: + mode = CVSTD_720P25; + break; + case AD_FIX_FORMAT_1080P_30FPS: + case AD_FIX_FORMAT_1080P_25FPS: + + default: + mode = CVSTD_720P25; + break; + } + + return mode; +} + +int nvp6188_ad_init(struct vehicle_ad_dev *ad) +{ + int val; + int i = 0; + + nvp6188_g_addev = ad; + + /* 1. i2c init */ + while (ad->adapter == NULL) { + ad->adapter = i2c_get_adapter(ad->i2c_chl); + usleep_range(10000, 12000); + } + if (ad->adapter == NULL) + return -ENODEV; + + if (!i2c_check_functionality(ad->adapter, I2C_FUNC_I2C)) + return -EIO; + + /* 2. ad power on sequence */ + nvp6188_power_on(ad); + + while (++i < 5) { + usleep_range(1000, 1200); + val = vehicle_generic_sensor_read(ad, 0xf0); + if (val != 0xff) + break; + VEHICLE_INFO("nvp6188_init i2c_reg_read fail\n"); + } + + nvp6188_reinit_parameter(ad, cvstd_mode); + ad->last_detect_status = true; + + /* create workqueue to detect signal change */ + INIT_DELAYED_WORK(&ad->state_check_work.work, nvp6188_check_state_work); + ad->state_check_work.state_check_wq = + create_singlethread_workqueue("vehicle-ad-nvp6188"); + + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(100)); + + return 0; +} diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_nvp6188.h b/drivers/video/rockchip/vehicle/vehicle_ad_nvp6188.h new file mode 100644 index 000000000000..4e9c6a61ed61 --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_nvp6188.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Vehicle driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ +#ifndef __VEHICLE_AD_NVP6188_H__ +#define __VEHICLE_AD_NVP6188_H__ + +int nvp6188_ad_init(struct vehicle_ad_dev *ad); +int nvp6188_ad_deinit(void); +int nvp6188_ad_get_cfg(struct vehicle_cfg **cfg); +void nvp6188_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line); +int nvp6188_check_id(struct vehicle_ad_dev *ad); +int nvp6188_stream(struct vehicle_ad_dev *ad, int enable); +void nvp6188_channel_set(struct vehicle_ad_dev *ad, int channel); + +#endif diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_nvp6324.c b/drivers/video/rockchip/vehicle/vehicle_ad_nvp6324.c new file mode 100644 index 000000000000..4c1b3da8176e --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_nvp6324.c @@ -0,0 +1,1359 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * vehicle sensor nvp6324 + * + * Copyright (C) 2020 Rockchip Electronics Co.Ltd + * Authors: + * wpzz + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vehicle_cfg.h" +#include "vehicle_main.h" +#include "vehicle_ad.h" +#include "vehicle_ad_nvp6324.h" + +enum { + CVSTD_720P60 = 0, + CVSTD_720P50, + CVSTD_1080P30, + CVSTD_1080P25, + CVSTD_720P30, + CVSTD_720P25, + CVSTD_SVGAP30, + CVSTD_SD, + CVSTD_NTSC, + CVSTD_PAL +}; + +enum { + FORCE_PAL_WIDTH = 960, + FORCE_PAL_HEIGHT = 576, + FORCE_NTSC_WIDTH = 960, + FORCE_NTSC_HEIGHT = 480, + FORCE_SVGA_WIDTH = 800, + FORCE_SVGA_HEIGHT = 600, + FORCE_720P_WIDTH = 1280, + FORCE_720P_HEIGHT = 720, + FORCE_1080P_WIDTH = 1920, + FORCE_1080P_HEIGHT = 1080, + FORCE_CIF_OUTPUT_FORMAT = CIF_OUTPUT_FORMAT_420, +}; + +enum { + VIDEO_UNPLUG, + VIDEO_IN, + VIDEO_LOCKED, + VIDEO_UNLOCK +}; + +#define JAGUAR1_LINK_FREQ_320M 320000000UL +#define JAGUAR1_LINK_FREQ_640M 640000000UL + +static struct vehicle_ad_dev *nvp6324_g_addev; +static int cvstd_mode = CVSTD_1080P25; +//static int cvstd_old = CVSTD_720P25; +static int cvstd_old = CVSTD_NTSC; + +//static int cvstd_sd = CVSTD_NTSC; +static int cvstd_state = VIDEO_UNPLUG; +static int cvstd_old_state = VIDEO_UNLOCK; +static int video_mode; +static int video_old; + +static bool g_nvp6324_streaming; + +#define SENSOR_REGISTER_LEN 1 /* sensor register address bytes*/ +#define SENSOR_VALUE_LEN 1 /* sensor register value bytes*/ +#define JAGUAR1_CHIP_ID 0xB0 + +struct rk_sensor_reg { + unsigned int reg; + unsigned int val; +}; + +#define SENSOR_CHANNEL_REG 0x41 + +#define SEQCMD_END 0xFF000000 +#define SensorEnd {SEQCMD_END, 0x00} + +#define SENSOR_ID(_msb, _lsb) ((_msb) << 8 | (_lsb)) + +/* 720p Preview resolution setting*/ +static struct rk_sensor_reg sensor_preview_data_720p_25hz[] = { + {0xff, 0x04}, + {0xa0, 0x24}, + {0xa1, 0x24}, + {0xa2, 0x24}, + {0xa3, 0x24}, + {0xa4, 0x24}, + {0xa5, 0x24}, + {0xa6, 0x24}, + {0xa7, 0x24}, + {0xa8, 0x24}, + {0xa9, 0x24}, + {0xaa, 0x24}, + {0xab, 0x24}, + {0xac, 0x24}, + {0xad, 0x24}, + {0xae, 0x24}, + {0xaf, 0x24}, + {0xb0, 0x24}, + {0xb1, 0x24}, + {0xb2, 0x24}, + {0xb3, 0x24}, + {0xb4, 0x24}, + {0xb5, 0x24}, + {0xb6, 0x24}, + {0xb7, 0x24}, + {0xb8, 0x24}, + {0xb9, 0x24}, + {0xba, 0x24}, + {0xbb, 0x24}, + {0xbc, 0x24}, + {0xbd, 0x24}, + {0xbe, 0x24}, + {0xbf, 0x24}, + {0xc0, 0x24}, + {0xc1, 0x24}, + {0xc2, 0x24}, + {0xc3, 0x24}, + {0xff, 0x21}, + {0x07, 0x80}, + {0x07, 0x00}, + {0xff, 0x0A}, + {0x77, 0x8F}, + {0xF7, 0x8F}, + {0xff, 0x0B}, + {0x77, 0x8F}, + {0xF7, 0x8F}, + {0xFF, 0x21}, + {0x40, 0xAC}, + {0x41, 0x10}, + {0x42, 0x03}, + {0x43, 0x43}, + {0x11, 0x04}, + {0x10, 0x0A}, + {0x12, 0x06}, + {0x13, 0x09}, + {0x17, 0x01}, + {0x18, 0x0D}, + {0x15, 0x04}, + {0x14, 0x16}, + {0x16, 0x05}, + {0x19, 0x05}, + {0x1A, 0x0A}, + {0x1B, 0x08}, + {0x1C, 0x07}, + {0x44, 0x00}, + {0x49, 0xF3}, + {0x49, 0xF0}, + {0x44, 0x02}, + {0x08, 0x40}, //0x40:non-continue;0x48:continuous + {0x0F, 0x01}, + {0x38, 0x1E}, + {0x39, 0x1E}, + {0x3A, 0x1E}, + {0x3B, 0x1E}, + {0x07, 0x0f}, //0x07:2lane;0x0f:4lane + {0x2D, 0x01}, //0x00:2lane;0x01:4lane + {0x45, 0x02}, + {0xFF, 0x13}, + {0x30, 0x00}, + {0x31, 0x00}, + {0x32, 0x00}, + {0xFF, 0x00}, + {0x00, 0x00}, + {0x01, 0x00}, + {0x02, 0x00}, + {0x03, 0x00}, + {0x04, 0x00}, //sd_mode + {0x05, 0x00}, + {0x06, 0x00}, + {0x07, 0x00}, + {0x08, 0x0d}, //ahd_mode + {0x09, 0x0d}, + {0x0a, 0x0d}, + {0x0b, 0x0d}, + {0x0c, 0x00}, + {0x0d, 0x00}, + {0x0e, 0x00}, + {0x0f, 0x00}, + {0x10, 0x20}, //video_format + {0x11, 0x20}, + {0x12, 0x20}, + {0x13, 0x20}, + {0x14, 0x00}, + {0x15, 0x00}, + {0x16, 0x00}, + {0x17, 0x00}, + {0x18, 0x13}, + {0x19, 0x13}, + {0x1a, 0x13}, + {0x1b, 0x13}, + {0x1c, 0x1a}, + {0x1d, 0x1a}, + {0x1e, 0x1a}, + {0x1f, 0x1a}, + {0x20, 0x00}, + {0x21, 0x00}, + {0x22, 0x00}, + {0x23, 0x00}, + {0x24, 0x88}, //contrast + {0x25, 0x88}, + {0x26, 0x88}, + {0x27, 0x88}, + {0x28, 0x84}, //black_level + {0x29, 0x84}, + {0x2a, 0x84}, + {0x2b, 0x84}, + {0x30, 0x03}, //y_peaking_mode + {0x31, 0x03}, + {0x32, 0x03}, + {0x33, 0x03}, + {0x34, 0x0f}, //y_fir_mode + {0x35, 0x0f}, + {0x36, 0x0f}, + {0x37, 0x0f}, + {0x40, 0x00}, + {0x41, 0x00}, + {0x42, 0x00}, + {0x43, 0x00}, + {0x44, 0x00}, + {0x45, 0x00}, + {0x46, 0x00}, + {0x47, 0x00}, + {0x48, 0x00}, + {0x49, 0x00}, + {0x4a, 0x00}, + {0x4b, 0x00}, + {0x4c, 0x00}, + {0x4d, 0x00}, + {0x4e, 0x00}, + {0x4f, 0x00}, + {0x50, 0x00}, + {0x51, 0x00}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x58, 0x80}, + {0x59, 0x80}, + {0x5a, 0x80}, + {0x5b, 0x80}, + {0x5c, 0x82}, //pal_cm_off + {0x5d, 0x82}, + {0x5e, 0x82}, + {0x5f, 0x82}, + {0x60, 0x10}, + {0x61, 0x10}, + {0x62, 0x10}, + {0x63, 0x10}, + {0x64, 0x05}, //y_delay + {0x65, 0x05}, + {0x66, 0x05}, + {0x67, 0x05}, + {0x68, 0x43}, //h_delay_a //h_delay_lsb + {0x69, 0x43}, + {0x6a, 0x43}, + {0x6b, 0x43}, + {0x6c, 0x00}, + {0x6d, 0x00}, + {0x6e, 0x00}, + {0x6f, 0x00}, + {0x78, 0x21}, + {0x79, 0x21}, + {0x7a, 0x21}, + {0x7b, 0x21}, + {0xFF, 0x01}, + {0x7C, 0x00}, + {0x84, 0x04}, + {0x85, 0x04}, + {0x86, 0x04}, + {0x87, 0x04}, + {0x88, 0x01}, + {0x89, 0x01}, + {0x8a, 0x01}, + {0x8b, 0x01}, + {0x8c, 0x02}, + {0x8d, 0x02}, + {0x8e, 0x02}, + {0x8f, 0x02}, + {0xEC, 0x00}, + {0xED, 0x00}, + {0xEE, 0x00}, + {0xEF, 0x00}, + {0xFF, 0x05}, + {0x00, 0xd0}, + {0x01, 0x2c}, + {0x05, 0x24}, //d_agc_option + {0x1d, 0x0c}, + {0x24, 0x2a}, + {0x25, 0xdc}, //fsc_lock_mode + {0x26, 0x40}, + {0x27, 0x57}, + {0x28, 0x80}, //s_point + {0x2b, 0xa8}, //saturation_b + {0x31, 0x82}, + {0x32, 0x10}, + {0x38, 0x00}, //burst_dec_b + {0x47, 0xEE}, + {0x50, 0xc6}, + {0x53, 0x00}, + {0x57, 0x00}, + {0x58, 0x77}, + {0x59, 0x00}, + {0x5C, 0x78}, + {0x5F, 0x00}, + {0x62, 0x20}, + {0x64, 0x00}, + {0x65, 0x00}, + {0x69, 0x00}, + {0x6E, 0x00}, //VBLK_EXT_EN + {0x6F, 0x00}, //VBLK_EXT_[7:0] + {0x90, 0x01}, //comb_mode + {0x92, 0x00}, + {0x94, 0x00}, + {0x95, 0x00}, + {0xa9, 0x00}, + {0xb5, 0x80}, + {0xb7, 0xfc}, + {0xb8, 0x39}, + {0xb9, 0x72}, + {0xbb, 0x0f}, + {0xd1, 0x30}, //burst_dec_c + {0xd5, 0x80}, + {0xFF, 0x09}, + {0x40, 0x00}, + {0x41, 0x00}, + {0x42, 0x00}, + {0x43, 0x00}, + {0x44, 0x00}, + {0x45, 0x00}, + {0x46, 0x00}, + {0x47, 0x00}, + {0x50, 0x30}, + {0x51, 0x6f}, + {0x52, 0x67}, + {0x53, 0x48}, + {0x54, 0x30}, + {0x55, 0x6f}, + {0x56, 0x67}, + {0x57, 0x48}, + {0x58, 0x30}, + {0x59, 0x6f}, + {0x5a, 0x67}, + {0x5b, 0x48}, + {0x5c, 0x30}, + {0x5d, 0x6f}, + {0x5e, 0x67}, + {0x5f, 0x48}, + {0x96, 0x00}, + {0x97, 0x00}, + {0x98, 0x00}, + {0x99, 0x00}, + {0x9a, 0x00}, + {0x9b, 0x00}, + {0x9c, 0x00}, + {0x9d, 0x00}, + {0x9e, 0x00}, + {0xb6, 0x00}, + {0xb7, 0x00}, + {0xb8, 0x00}, + {0xb9, 0x00}, + {0xba, 0x00}, + {0xbb, 0x00}, + {0xbc, 0x00}, + {0xbd, 0x00}, + {0xbe, 0x00}, + {0xd6, 0x00}, + {0xd7, 0x00}, + {0xd8, 0x00}, + {0xd9, 0x00}, + {0xda, 0x00}, + {0xdb, 0x00}, + {0xdc, 0x00}, + {0xdd, 0x00}, + {0xde, 0x00}, + {0xf6, 0x00}, + {0xf7, 0x00}, + {0xf8, 0x00}, + {0xf9, 0x00}, + {0xfa, 0x00}, + {0xfb, 0x00}, + {0xfc, 0x00}, + {0xfd, 0x00}, + {0xfe, 0x00}, + {0xff, 0x0a}, + {0x3d, 0x00}, + {0x3c, 0x00}, + {0x30, 0xac}, + {0x31, 0x78}, + {0x32, 0x17}, + {0x33, 0xc1}, + {0x34, 0x40}, + {0x35, 0x00}, + {0x36, 0xc3}, + {0x37, 0x0a}, + {0x38, 0x00}, + {0x39, 0x02}, + {0x3a, 0x00}, + {0x3b, 0xb2}, + {0x25, 0x10}, + {0x27, 0x1e}, + {0xbd, 0x00}, + {0xbc, 0x00}, + {0xb0, 0xac}, + {0xb1, 0x78}, + {0xb2, 0x17}, + {0xb3, 0xc1}, + {0xb4, 0x40}, + {0xb5, 0x00}, + {0xb6, 0xc3}, + {0xb7, 0x0a}, + {0xb8, 0x00}, + {0xb9, 0x02}, + {0xba, 0x00}, + {0xbb, 0xb2}, + {0xa5, 0x10}, + {0xa7, 0x1e}, + {0xff, 0x0b}, + {0x3d, 0x00}, + {0x3c, 0x00}, + {0x30, 0xac}, + {0x31, 0x78}, + {0x32, 0x17}, + {0x33, 0xc1}, + {0x34, 0x40}, + {0x35, 0x00}, + {0x36, 0xc3}, + {0x37, 0x0a}, + {0x38, 0x00}, + {0x39, 0x02}, + {0x3a, 0x00}, + {0x3b, 0xb2}, + {0x25, 0x10}, + {0x27, 0x1e}, + {0xbd, 0x00}, + {0xbc, 0x00}, + {0xb0, 0xac}, + {0xb1, 0x78}, + {0xb2, 0x17}, + {0xb3, 0xc1}, + {0xb4, 0x40}, + {0xb5, 0x00}, + {0xb6, 0xc3}, + {0xb7, 0x0a}, + {0xb8, 0x00}, + {0xb9, 0x02}, + {0xba, 0x00}, + {0xbb, 0xb2}, + {0xa5, 0x10}, + {0xa7, 0x1e}, + {0xFF, 0x21}, + {0x3E, 0x00}, + {0x3F, 0x00}, + {0xFF, 0x20}, + {0x01, 0x55}, + {0x00, 0x00}, + {0x40, 0x01}, + {0x0F, 0x00}, + {0x0D, 0x01}, //0x01:4lane;0x00:2lane + {0x40, 0x00}, + {0x00, 0xFF}, //ch1/2/3/4 enabled + //{0x00, 0x33}, //ch1/2 enabled + //{0x00, 0x11}, //ch1 enabled + {0xFF, 0x01}, + {0xC8, 0x00}, + {0xC9, 0x00}, + {0xCA, 0x00}, + {0xCB, 0x00}, + //pattern enabled + {0xFF, 0x00}, + {0x1C, 0x1A}, + {0x1D, 0x1A}, + {0x1E, 0x1A}, + {0x1F, 0x1A}, + {0xFF, 0x05}, + {0x6A, 0x80}, + {0xFF, 0x06}, + {0x6A, 0x80}, + {0xFF, 0x07}, + {0x6A, 0x80}, + {0xFF, 0x08}, + {0x6A, 0x80}, + SensorEnd +}; + +/* 1080p Preview resolution setting*/ +static struct rk_sensor_reg sensor_preview_data_1080p_25hz[] = { + {0xff, 0x04}, + {0xa0, 0x24}, + {0xa1, 0x24}, + {0xa2, 0x24}, + {0xa3, 0x24}, + {0xa4, 0x24}, + {0xa5, 0x24}, + {0xa6, 0x24}, + {0xa7, 0x24}, + {0xa8, 0x24}, + {0xa9, 0x24}, + {0xaa, 0x24}, + {0xab, 0x24}, + {0xac, 0x24}, + {0xad, 0x24}, + {0xae, 0x24}, + {0xaf, 0x24}, + {0xb0, 0x24}, + {0xb1, 0x24}, + {0xb2, 0x24}, + {0xb3, 0x24}, + {0xb4, 0x24}, + {0xb5, 0x24}, + {0xb6, 0x24}, + {0xb7, 0x24}, + {0xb8, 0x24}, + {0xb9, 0x24}, + {0xba, 0x24}, + {0xbb, 0x24}, + {0xbc, 0x24}, + {0xbd, 0x24}, + {0xbe, 0x24}, + {0xbf, 0x24}, + {0xc0, 0x24}, + {0xc1, 0x24}, + {0xc2, 0x24}, + {0xc3, 0x24}, + {0xff, 0x21}, + {0x07, 0x80}, + {0x07, 0x00}, + {0xff, 0x0A}, + {0x77, 0x8F}, + {0xF7, 0x8F}, + {0xff, 0x0B}, + {0x77, 0x8F}, + {0xF7, 0x8F}, + {0xFF, 0x21}, + {0x40, 0xB4}, + {0x41, 0x00}, + {0x42, 0x03}, + {0x43, 0x43}, + {0x11, 0x08}, + {0x10, 0x13}, + {0x12, 0x0B}, + {0x13, 0x12}, + {0x17, 0x02}, + {0x18, 0x12}, + {0x15, 0x07}, + {0x14, 0x2D}, + {0x16, 0x0B}, + {0x19, 0x09}, + {0x1A, 0x15}, + {0x1B, 0x11}, + {0x1C, 0x0E}, + {0x44, 0x00}, + {0x49, 0xF3}, + {0x49, 0xF0}, + {0x44, 0x02}, + {0x08, 0x40}, //0x40:non-continue;0x48:continuous + {0x0F, 0x01}, + {0x38, 0x1E}, + {0x39, 0x1E}, + {0x3A, 0x1E}, + {0x3B, 0x1E}, + {0x07, 0x0f}, //0x07:2lane;0x0f:4lane + {0x2D, 0x01}, //0x00:2lane;0x01:4lane + {0x45, 0x02}, + {0xFF, 0x13}, + {0x30, 0x00}, + {0x31, 0x00}, + {0x32, 0x00}, + {0xFF, 0x00}, + {0x00, 0x00}, + {0x01, 0x00}, + {0x02, 0x00}, + {0x03, 0x00}, + {0x04, 0x00}, //sd_mode + {0x05, 0x00}, + {0x06, 0x00}, + {0x07, 0x00}, + {0x08, 0x03}, //ahd_mode + {0x09, 0x03}, + {0x0a, 0x03}, + {0x0b, 0x03}, + {0x0c, 0x00}, + {0x0d, 0x00}, + {0x0e, 0x00}, + {0x0f, 0x00}, + {0x10, 0x20}, //video_format + {0x11, 0x20}, + {0x12, 0x20}, + {0x13, 0x20}, + {0x14, 0x00}, + {0x15, 0x00}, + {0x16, 0x00}, + {0x17, 0x00}, + {0x18, 0x13}, + {0x19, 0x13}, + {0x1a, 0x13}, + {0x1b, 0x13}, + {0x1c, 0x1a}, + {0x1d, 0x1a}, + {0x1e, 0x1a}, + {0x1f, 0x1a}, + {0x20, 0x00}, + {0x21, 0x00}, + {0x22, 0x00}, + {0x23, 0x00}, + {0x24, 0x86}, //contrast + {0x25, 0x86}, + {0x26, 0x86}, + {0x27, 0x86}, + {0x28, 0x80}, //black_level + {0x29, 0x80}, + {0x2a, 0x80}, + {0x2b, 0x80}, + {0x30, 0x00}, //y_peaking_mode + {0x31, 0x00}, + {0x32, 0x00}, + {0x33, 0x00}, + {0x34, 0x00}, //y_fir_mode + {0x35, 0x00}, + {0x36, 0x00}, + {0x37, 0x00}, + {0x40, 0x00}, + {0x41, 0x00}, + {0x42, 0x00}, + {0x43, 0x00}, + {0x44, 0x00}, + {0x45, 0x00}, + {0x46, 0x00}, + {0x47, 0x00}, + {0x48, 0x00}, + {0x49, 0x00}, + {0x4a, 0x00}, + {0x4b, 0x00}, + {0x4c, 0xfe}, + {0x4d, 0xfe}, + {0x4e, 0xfe}, + {0x4f, 0xfe}, + {0x50, 0xfb}, + {0x51, 0xfb}, + {0x52, 0xfb}, + {0x53, 0xfb}, + {0x58, 0x80}, + {0x59, 0x80}, + {0x5a, 0x80}, + {0x5b, 0x80}, + {0x5c, 0x82}, //pal_cm_off + {0x5d, 0x82}, + {0x5e, 0x82}, + {0x5f, 0x82}, + {0x60, 0x10}, + {0x61, 0x10}, + {0x62, 0x10}, + {0x63, 0x10}, + {0x64, 0x05}, //y_delay + {0x65, 0x05}, + {0x66, 0x05}, + {0x67, 0x05}, + {0x68, 0x48}, //h_delay_a //h_delay_lsb + {0x69, 0x48}, + {0x6a, 0x48}, + {0x6b, 0x48}, + {0x6c, 0x00}, + {0x6d, 0x00}, + {0x6e, 0x00}, + {0x6f, 0x00}, +// {0x78, 0x21}, +// {0x79, 0x21}, +// {0x7a, 0x21}, +// {0x7b, 0x21}, + {0x78, 0x22}, + {0x79, 0x22}, + {0x7a, 0x22}, + {0x7b, 0x22}, + {0xFF, 0x01}, + {0x7C, 0x00}, + {0x84, 0x04}, + {0x85, 0x04}, + {0x86, 0x04}, + {0x87, 0x04}, + {0x88, 0x01}, + {0x89, 0x01}, + {0x8a, 0x01}, + {0x8b, 0x01}, + {0x8c, 0x02}, + {0x8d, 0x02}, + {0x8e, 0x02}, + {0x8f, 0x02}, + {0xEC, 0x00}, + {0xED, 0x00}, + {0xEE, 0x00}, + {0xEF, 0x00}, + {0xFF, 0x05}, + {0x00, 0xd0}, + {0x01, 0x2c}, + {0x05, 0x24}, //d_agc_option + {0x1d, 0x0c}, + {0x24, 0x2a}, + {0x25, 0xdc}, //fsc_lock_mode + {0x26, 0x40}, + {0x27, 0x57}, + {0x28, 0x80}, //s_point + {0x2b, 0xa8}, //saturation_b + {0x31, 0x82}, + {0x32, 0x10}, + {0x38, 0x13}, + {0x47, 0xEE}, + {0x50, 0xc6}, + {0x53, 0x00}, + {0x57, 0x00}, + {0x58, 0x77}, + {0x59, 0x00}, + {0x5C, 0x78}, + {0x5F, 0x00}, + {0x62, 0x20}, + {0x64, 0x00}, + {0x65, 0x00}, + {0x69, 0x00}, + {0x6E, 0x00}, //VBLK_EXT_EN + {0x6F, 0x00}, //VBLK_EXT_[7:0] + {0x90, 0x01}, //comb_mode + {0x92, 0x00}, + {0x94, 0x00}, + {0x95, 0x00}, + {0xa9, 0x00}, + {0xb5, 0x80}, + {0xb7, 0xfc}, + {0xb8, 0x39}, + {0xb9, 0x72}, + {0xbb, 0x0f}, + {0xd1, 0x30}, //burst_dec_c + {0xd5, 0x80}, + {0xFF, 0x09}, + {0x40, 0x00}, + {0x41, 0x00}, + {0x42, 0x00}, + {0x43, 0x00}, + {0x44, 0x00}, + {0x45, 0x00}, + {0x46, 0x00}, + {0x47, 0x00}, + {0x50, 0x30}, + {0x51, 0x6f}, + {0x52, 0x67}, + {0x53, 0x48}, + {0x54, 0x30}, + {0x55, 0x6f}, + {0x56, 0x67}, + {0x57, 0x48}, + {0x58, 0x30}, + {0x59, 0x6f}, + {0x5a, 0x67}, + {0x5b, 0x48}, + {0x5c, 0x30}, + {0x5d, 0x6f}, + {0x5e, 0x67}, + {0x5f, 0x48}, + {0x96, 0x00}, + {0x97, 0x00}, + {0x98, 0x00}, + {0x99, 0x00}, + {0x9a, 0x00}, + {0x9b, 0x00}, + {0x9c, 0x00}, + {0x9d, 0x00}, + {0x9e, 0x00}, + {0xb6, 0x00}, + {0xb7, 0x00}, + {0xb8, 0x00}, + {0xb9, 0x00}, + {0xba, 0x00}, + {0xbb, 0x00}, + {0xbc, 0x00}, + {0xbd, 0x00}, + {0xbe, 0x00}, + {0xd6, 0x00}, + {0xd7, 0x00}, + {0xd8, 0x00}, + {0xd9, 0x00}, + {0xda, 0x00}, + {0xdb, 0x00}, + {0xdc, 0x00}, + {0xdd, 0x00}, + {0xde, 0x00}, + {0xf6, 0x00}, + {0xf7, 0x00}, + {0xf8, 0x00}, + {0xf9, 0x00}, + {0xfa, 0x00}, + {0xfb, 0x00}, + {0xfc, 0x00}, + {0xfd, 0x00}, + {0xfe, 0x00}, + {0xff, 0x0a}, + {0x3d, 0x00}, + {0x3c, 0x00}, + {0x30, 0xac}, + {0x31, 0x78}, + {0x32, 0x17}, + {0x33, 0xc1}, + {0x34, 0x40}, + {0x35, 0x00}, + {0x36, 0xc3}, + {0x37, 0x0a}, + {0x38, 0x00}, + {0x39, 0x02}, + {0x3a, 0x00}, + {0x3b, 0xb2}, + {0x25, 0x10}, + {0x27, 0x1e}, + {0xbd, 0x00}, + {0xbc, 0x00}, + {0xb0, 0xac}, + {0xb1, 0x78}, + {0xb2, 0x17}, + {0xb3, 0xc1}, + {0xb4, 0x40}, + {0xb5, 0x00}, + {0xb6, 0xc3}, + {0xb7, 0x0a}, + {0xb8, 0x00}, + {0xb9, 0x02}, + {0xba, 0x00}, + {0xbb, 0xb2}, + {0xa5, 0x10}, + {0xa7, 0x1e}, + {0xff, 0x0b}, + {0x3d, 0x00}, + {0x3c, 0x00}, + {0x30, 0xac}, + {0x31, 0x78}, + {0x32, 0x17}, + {0x33, 0xc1}, + {0x34, 0x40}, + {0x35, 0x00}, + {0x36, 0xc3}, + {0x37, 0x0a}, + {0x38, 0x00}, + {0x39, 0x02}, + {0x3a, 0x00}, + {0x3b, 0xb2}, + {0x25, 0x10}, + {0x27, 0x1e}, + {0xbd, 0x00}, + {0xbc, 0x00}, + {0xb0, 0xac}, + {0xb1, 0x78}, + {0xb2, 0x17}, + {0xb3, 0xc1}, + {0xb4, 0x40}, + {0xb5, 0x00}, + {0xb6, 0xc3}, + {0xb7, 0x0a}, + {0xb8, 0x00}, + {0xb9, 0x02}, + {0xba, 0x00}, + {0xbb, 0xb2}, + {0xa5, 0x10}, + {0xa7, 0x1e}, + {0xFF, 0x21}, + {0x3E, 0x00}, + {0x3F, 0x00}, + {0xFF, 0x20}, + {0x01, 0x00}, + {0x00, 0x00}, + {0x40, 0x01}, + {0x0F, 0x00}, + {0x0D, 0x01}, //0x01:4lane;0x00:2lane + {0x40, 0x00}, + {0x00, 0xFF}, //ch1/2/3/4 enabled + //{0x00, 0x33}, //ch1/2 enabled + //{0x00, 0x11}, //ch1 enabled + {0xFF, 0x01}, + {0xC8, 0x00}, + {0xC9, 0x00}, + {0xCA, 0x00}, + {0xCB, 0x00}, + //pattern enabled + {0xFF, 0x00}, + {0x1C, 0x1A}, + {0x1D, 0x1A}, + {0x1E, 0x1A}, + {0x1F, 0x1A}, + {0xFF, 0x05}, + {0x6A, 0x80}, + {0xFF, 0x06}, + {0x6A, 0x80}, + {0xFF, 0x07}, + {0x6A, 0x80}, + {0xFF, 0x08}, + {0x6A, 0x80}, + SensorEnd +}; + +/* format detect open*/ +static struct rk_sensor_reg sensor_open_format_detect[] = { + {0xff, 0x13}, + {0x1f, 0x23}, + {0x30, 0xff}, + {0x31, 0xff}, + {0x32, 0xff}, + SensorEnd +}; + +static void nvp6324_reinit_parameter(struct vehicle_ad_dev *ad, unsigned char cvstd) +{ + int i = 0; + + switch (cvstd) { + case CVSTD_720P25: + ad->cfg.width = 1280; + ad->cfg.height = 720; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0;/*00 - UYVY*/ + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 25; + ad->cfg.mipi_freq = JAGUAR1_LINK_FREQ_320M; + break; + + case CVSTD_1080P25: + ad->cfg.width = 1920; + ad->cfg.height = 1080; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0;/*00 - UYVY*/ + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 25; + ad->cfg.mipi_freq = JAGUAR1_LINK_FREQ_640M; + break; + + default: + ad->cfg.width = 1920; + ad->cfg.height = 1080; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0;/*00 - UYVY*/ + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 25; + ad->cfg.mipi_freq = JAGUAR1_LINK_FREQ_640M; + break; + } + ad->cfg.type = V4L2_MBUS_CSI2_DPHY; + ad->cfg.mbus_flags = V4L2_MBUS_CSI2_4_LANE | + V4L2_MBUS_CSI2_CHANNELS; + ad->cfg.mbus_code = MEDIA_BUS_FMT_UYVY8_2X8; + + switch (ad->cfg.mbus_flags & V4L2_MBUS_CSI2_LANES) { + case V4L2_MBUS_CSI2_1_LANE: + ad->cfg.lanes = 1; + break; + case V4L2_MBUS_CSI2_2_LANE: + ad->cfg.lanes = 2; + break; + case V4L2_MBUS_CSI2_3_LANE: + ad->cfg.lanes = 3; + break; + case V4L2_MBUS_CSI2_4_LANE: + ad->cfg.lanes = 4; + break; + default: + ad->cfg.lanes = 1; + break; + } + + /* fix crop info from dts config */ + for (i = 0; i < 4; i++) { + if ((ad->defrects[i].width == ad->cfg.width) && + (ad->defrects[i].height == ad->cfg.height)) { + ad->cfg.start_x = ad->defrects[i].crop_x; + ad->cfg.start_y = ad->defrects[i].crop_y; + ad->cfg.width = ad->defrects[i].crop_width; + ad->cfg.height = ad->defrects[i].crop_height; + } + } +} + +static void nvp6324_reg_init(struct vehicle_ad_dev *ad, unsigned char cvstd) +{ + struct rk_sensor_reg *sensor; + int i; + + switch (cvstd) { + case CVSTD_720P25: + VEHICLE_DG("%s, init CVSTD_720P25 mode)", __func__); + sensor = sensor_preview_data_720p_25hz; + break; + case CVSTD_1080P25: + VEHICLE_DG("%s, init CVSTD_1080P25 mode", __func__); + sensor = sensor_preview_data_1080p_25hz; + break; + default: + VEHICLE_DG("%s, init CVSTD_1080P25 mode", __func__); + sensor = sensor_preview_data_1080p_25hz; + break; + } + i = 0; + while ((sensor[i].reg != SEQCMD_END) && (sensor[i].reg != 0xFC000000)) { + vehicle_sensor_write(ad, sensor[i].reg, sensor[i].val); + i++; + } + /* open format detect*/ + sensor = sensor_open_format_detect; + i = 0; + while ((sensor[i].reg != SEQCMD_END) && (sensor[i].reg != 0xFC000000)) { + vehicle_sensor_write(ad, sensor[i].reg, sensor[i].val); + i++; + } + + vehicle_sensor_write(ad, 0xff, 0x05 + ad->ad_chl); + vehicle_sensor_write(ad, 0x82, 0xff); + vehicle_sensor_write(ad, 0xb8, 0xb9); +} + +void nvp6324_channel_set(struct vehicle_ad_dev *ad, int channel) +{ + unsigned int reg; + unsigned char val = 0x00; + + //detect interesting channel + reg = channel; + ad->ad_chl = channel; + VEHICLE_DG("%s, channel set(%d)", __func__, ad->ad_chl); + vehicle_sensor_write(ad, 0xff, 0x00); + vehicle_sensor_write(ad, reg, val); +} + +int nvp6324_ad_get_cfg(struct vehicle_cfg **cfg) +{ + + if (!nvp6324_g_addev) + return -ENODEV; + + switch (cvstd_state) { + case VIDEO_UNPLUG: + nvp6324_g_addev->cfg.ad_ready = false; + break; + case VIDEO_LOCKED: + nvp6324_g_addev->cfg.ad_ready = true; + break; + case VIDEO_IN: + nvp6324_g_addev->cfg.ad_ready = false; + break; + } + + nvp6324_g_addev->cfg.ad_ready = true; + + *cfg = &nvp6324_g_addev->cfg; + + return 0; +} + +void nvp6324_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line) +{ + VEHICLE_DG("%s, last_line %d\n", __func__, last_line); + + if (last_line < 1) + return; + + ad->cif_error_last_line = last_line; + if (cvstd_mode == CVSTD_PAL) { + if (last_line == FORCE_NTSC_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (cvstd_mode == CVSTD_NTSC) { + if (last_line == FORCE_PAL_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (cvstd_mode == CVSTD_1080P25) { + if (last_line == FORCE_1080P_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (cvstd_mode == CVSTD_720P25) { + if (last_line == FORCE_720P_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } +} + +int nvp6324_check_id(struct vehicle_ad_dev *ad) +{ + int ret = 0; + u8 pid; + + ret = vehicle_sensor_write(ad, 0xFF, 0x00); + ret |= vehicle_sensor_read(ad, 0xf4, &pid); + if (ret) + return ret; + if (pid != JAGUAR1_CHIP_ID) { + VEHICLE_DGERR("%s: expected 0xB0, detected: 0x%02x !", + ad->ad_name, pid); + ret = -EINVAL; + } else { + VEHICLE_INFO("%s Found NVP6324 sensor: id(0x%2x) !\n", __func__, pid); + } + + return ret; +} + +static int nvp6324_check_cvstd(struct vehicle_ad_dev *ad, bool activate_check) +{ + + u8 videoloss = 0; + int ret = 0; + unsigned char cvstd = 0; + + ret = vehicle_sensor_write(ad, 0xFF, 0x00); + ret |= vehicle_sensor_read(ad, 0xa4 + ad->ad_chl, &videoloss); + + video_mode = videoloss; + + ret |= vehicle_sensor_write(ad, 0xFF, 0x01); + ret |= vehicle_sensor_read(ad, 0x10 + (0x20 * (ad->ad_chl%4)), &cvstd); + + if (ret) + return ret; + + if (cvstd == 0x21) { + cvstd_mode = CVSTD_720P25; + VEHICLE_DG("%s(%d): 720P25\n", __func__, __LINE__); + } else if (cvstd == 0x31) { + cvstd_mode = CVSTD_1080P25; + VEHICLE_DG("%s(%d): 1080P25", __func__, __LINE__); + } else if (cvstd == 0x00) { + cvstd_mode = CVSTD_NTSC; + VEHICLE_DG("%s(%d): 720H NTSC\n", __func__, __LINE__); + } else if (cvstd == 0x10) { + cvstd_mode = CVSTD_PAL; + VEHICLE_DG("%s(%d): 720H PAL\n", __func__, __LINE__); + } else if (cvstd == 0xff) { + cvstd_mode = cvstd_old; + VEHICLE_DG("%s(%d): no ahd plugin!\n", __func__, __LINE__); + } else { + cvstd_mode = cvstd_old; + VEHICLE_DG("%s(%d): not support ahd mode!\n", __func__, __LINE__); + } + + return 0; +} + +int nvp6324_stream(struct vehicle_ad_dev *ad, int enable) +{ + VEHICLE_DG("%s on(%d)\n", __func__, enable); + + g_nvp6324_streaming = (enable != 0); + if (g_nvp6324_streaming) { + vehicle_sensor_write(ad, 0xff, 0x21); + vehicle_sensor_write(ad, 0x07, 0x0f); + if (ad->state_check_work.state_check_wq) + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(200)); + } else { + vehicle_sensor_write(ad, 0xff, 0x21); + vehicle_sensor_write(ad, 0x07, 0x8f); + if (ad->state_check_work.state_check_wq) + cancel_delayed_work_sync(&ad->state_check_work.work); + } + + return 0; +} + +static void nvp6324_power_on(struct vehicle_ad_dev *ad) +{ + /* gpio_direction_output(ad->power, ad->pwr_active); */ + VEHICLE_DG("gpio: power(%d), powerdown(%d), reset(%d)", + ad->power, ad->powerdown, ad->reset); + if (gpio_is_valid(ad->power)) { + gpio_request(ad->power, "nvp6324_power"); + gpio_direction_output(ad->power, ad->pwr_active); + /* gpio_set_value(ad->power, ad->pwr_active); */ + } + + if (gpio_is_valid(ad->powerdown)) { + gpio_request(ad->powerdown, "nvp6324_pwd"); + gpio_direction_output(ad->powerdown, 1); + /* gpio_set_value(ad->powerdown, !ad->pwdn_active); */ + } + + if (gpio_is_valid(ad->reset)) { + gpio_request(ad->reset, "nvp6324_rst"); + gpio_direction_output(ad->reset, 0); + usleep_range(1500, 2000); + gpio_direction_output(ad->reset, 1); + } +} + +static void nvp6324_power_deinit(struct vehicle_ad_dev *ad) +{ + if (gpio_is_valid(ad->reset)) + gpio_free(ad->reset); + if (gpio_is_valid(ad->power)) + gpio_free(ad->power); + if (gpio_is_valid(ad->powerdown)) + gpio_free(ad->powerdown); +} + +static void nvp6324_check_state_work(struct work_struct *work) +{ + struct vehicle_ad_dev *ad; + + ad = nvp6324_g_addev; + + if (ad->cif_error_last_line > 0) { + nvp6324_check_cvstd(ad, true); + ad->cif_error_last_line = 0; + } else { + nvp6324_check_cvstd(ad, false); + } + + if (cvstd_old != cvstd_mode || + cvstd_old_state != cvstd_state || (video_old != video_mode)) { + VEHICLE_INFO("%s:ad sensor std mode change, cvstd_old(%d), cvstd_mode(%d)\n", + __func__, cvstd_old, cvstd_mode); + cvstd_old = cvstd_mode; + cvstd_old_state = cvstd_state; + video_old = video_mode; + + nvp6324_reinit_parameter(ad, cvstd_mode); + nvp6324_reg_init(ad, cvstd_mode); + vehicle_ad_stat_change_notify(); + } + if (g_nvp6324_streaming) { + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(100)); + } +} + +int nvp6324_ad_deinit(void) +{ + struct vehicle_ad_dev *ad; + + ad = nvp6324_g_addev; + + if (!ad) + return -ENODEV; + + if (ad->state_check_work.state_check_wq) { + cancel_delayed_work_sync(&ad->state_check_work.work); + flush_delayed_work(&ad->state_check_work.work); + flush_workqueue(ad->state_check_work.state_check_wq); + destroy_workqueue(ad->state_check_work.state_check_wq); + } + if (ad->irq) + free_irq(ad->irq, ad); + nvp6324_power_deinit(ad); + + return 0; +} + +static __maybe_unused int get_ad_mode_from_fix_format(int fix_format) +{ + int mode = -1; + + switch (fix_format) { + case AD_FIX_FORMAT_PAL: + case AD_FIX_FORMAT_NTSC: + case AD_FIX_FORMAT_720P_50FPS: + case AD_FIX_FORMAT_720P_30FPS: + case AD_FIX_FORMAT_720P_25FPS: + mode = CVSTD_720P25; + break; + case AD_FIX_FORMAT_1080P_30FPS: + case AD_FIX_FORMAT_1080P_25FPS: + + default: + mode = CVSTD_720P25; + break; + } + + return mode; +} + +int nvp6324_ad_init(struct vehicle_ad_dev *ad) +{ + int val; + int i = 0; + + nvp6324_g_addev = ad; + + /* 1. i2c init */ + while (ad->adapter == NULL) { + ad->adapter = i2c_get_adapter(ad->i2c_chl); + usleep_range(10000, 12000); + } + if (ad->adapter == NULL) + return -ENODEV; + + if (!i2c_check_functionality(ad->adapter, I2C_FUNC_I2C)) + return -EIO; + + nvp6324_power_on(ad); + + while (++i < 5) { + usleep_range(10000, 12000); + val = vehicle_generic_sensor_read(ad, 0xf0); + if (val != 0xff) + break; + VEHICLE_DGERR("nvp6324_init i2c_reg_read fail\n"); + } + + nvp6324_reg_init(ad, cvstd_mode); + + nvp6324_reinit_parameter(ad, cvstd_mode); + + INIT_DELAYED_WORK(&ad->state_check_work.work, nvp6324_check_state_work); + ad->state_check_work.state_check_wq = + create_singlethread_workqueue("vehicle-ad-nvp6324"); + + /* nvp6324_check_cvstd(ad, true); */ + + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(100)); + + return 0; +} diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_nvp6324.h b/drivers/video/rockchip/vehicle/vehicle_ad_nvp6324.h new file mode 100644 index 000000000000..b28780737969 --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_nvp6324.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Vehicle driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ +#ifndef __VEHICLE_AD_NVP6324_H__ +#define __VEHICLE_AD_NVP6324_H__ + +int nvp6324_ad_init(struct vehicle_ad_dev *ad); +int nvp6324_ad_deinit(void); +int nvp6324_ad_get_cfg(struct vehicle_cfg **cfg); +void nvp6324_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line); +int nvp6324_check_id(struct vehicle_ad_dev *ad); +int nvp6324_stream(struct vehicle_ad_dev *ad, int enable); +void nvp6324_channel_set(struct vehicle_ad_dev *ad, int channel); + +#endif diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_tp2825.c b/drivers/video/rockchip/vehicle/vehicle_ad_tp2825.c new file mode 100644 index 000000000000..08888dbe6c1b --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_tp2825.c @@ -0,0 +1,1039 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * vehicle sensor tp2825 + * + * Copyright (C) 2020 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vehicle_cfg.h" +#include "vehicle_main.h" +#include "vehicle_ad.h" +#include "vehicle_ad_tp2825.h" + +enum { + CVSTD_720P60 = 0, + CVSTD_720P50, + CVSTD_1080P30, + CVSTD_1080P25, + CVSTD_720P30, + CVSTD_720P25, + CVSTD_SD, + CVSTD_NTSC, + CVSTD_PAL +}; + +enum { + FORCE_PAL_WIDTH = 960, + FORCE_PAL_HEIGHT = 576, + FORCE_NTSC_WIDTH = 960, + FORCE_NTSC_HEIGHT = 480, + FORCE_CIF_OUTPUT_FORMAT = CIF_OUTPUT_FORMAT_420, +}; + +enum { + VIDEO_UNPLUG, + VIDEO_IN, + VIDEO_LOCKED, + VIDEO_UNLOCK +}; +#define FLAG_LOSS (0x1 << 7) +#define FLAG_V_LOCKED (0x1 << 6) +#define FLAG_H_LOCKED (0x1 << 5) +#define FLAG_CARRIER_PLL_LOCKED (0x1 << 4) +#define FLAG_VIDEO_DETECTED (0x1 << 3) +#define FLAG_EQ_SD_DETECTED (0x1 << 2) +#define FLAG_PROGRESSIVE (0x1 << 1) +#define FLAG_NO_CARRIER (0x1 << 0) +#define FLAG_LOCKED (FLAG_V_LOCKED | FLAG_H_LOCKED) + +static struct vehicle_ad_dev *tp2825_g_addev; +static int cvstd_mode = CVSTD_720P50; +static int cvstd_old = CVSTD_720P50; +static int cvstd_sd = CVSTD_NTSC; +static int cvstd_state = VIDEO_UNPLUG; +static int cvstd_old_state = VIDEO_UNPLUG; + +#define SENSOR_REGISTER_LEN 1 /* sensor register address bytes*/ +#define SENSOR_VALUE_LEN 1 /* sensor register value bytes*/ + +struct rk_sensor_reg { + unsigned int reg; + unsigned int val; +}; + +#define SENSOR_CHANNEL_REG 0x41 + +#define SEQCMD_END 0xFF000000 +#define SensorEnd {SEQCMD_END, 0x00} + +#define SENSOR_DG VEHICLE_DG + +/* Preview resolution setting*/ +static struct rk_sensor_reg sensor_preview_data_ntsc[] = { + {0x02, 0xCF}, + {0x06, 0x32}, + {0x07, 0xC0}, + {0x08, 0x00}, + {0x09, 0x24}, + {0x0A, 0x48}, + {0x0B, 0xC0}, + {0x0C, 0x53}, + {0x0D, 0x10}, + {0x0E, 0x00}, + {0x0F, 0x00}, + {0x10, 0x5e}, + {0x11, 0x40}, + {0x12, 0x44}, + {0x13, 0x00}, + {0x14, 0x00}, + {0x15, 0x13}, + {0x16, 0x4E}, + {0x17, 0xBC}, + {0x18, 0x15}, + {0x19, 0xF0}, + {0x1A, 0x07}, + {0x1B, 0x00}, + {0x1C, 0x09}, + {0x1D, 0x38}, + {0x1E, 0x80}, + {0x1F, 0x80}, + {0x20, 0xA0}, + {0x21, 0x86}, + {0x22, 0x38}, + {0x23, 0x3C}, + {0x24, 0x56}, + {0x25, 0xFF}, + {0x26, 0x12}, + {0x27, 0x2D}, + {0x28, 0x00}, + {0x29, 0x48}, + {0x2A, 0x30}, + {0x2B, 0x70}, + {0x2C, 0x1A}, + {0x2D, 0x68}, + {0x2E, 0x5E}, + {0x2F, 0x00}, + {0x30, 0x62}, + {0x31, 0xBB}, + {0x32, 0x96}, + {0x33, 0xC0}, + {0x34, 0x00}, + {0x35, 0x65}, + {0x36, 0xDC}, + {0x37, 0x00}, + {0x38, 0x40}, + {0x39, 0x84}, + {0x3A, 0x00}, + {0x3B, 0x03}, + {0x3C, 0x00}, + {0x3D, 0x60}, + {0x3E, 0x00}, + {0x3F, 0x00}, + {0x40, 0x00}, + {0x41, 0x00}, + {0x42, 0x00}, + {0x43, 0x12}, + {0x44, 0x07}, + {0x45, 0x49}, + {0x46, 0x00}, + {0x47, 0x00}, + {0x48, 0x00}, + {0x49, 0x00}, + {0x4A, 0x00}, + {0x4B, 0x00}, + {0x4C, 0x03}, + {0x4D, 0x00}, + {0x4E, 0x37}, + {0x4F, 0x01}, + {0xB5, 0x01}, + {0xB8, 0x00}, + {0xBA, 0x00}, + {0xF3, 0x00}, + {0xF4, 0x00}, + {0xF5, 0x00}, + {0xF6, 0x00}, + {0xF7, 0x00}, + {0xF8, 0x00}, + {0xF9, 0x00}, + {0xFA, 0x00}, + {0xFB, 0x00}, + {0xFC, 0xC0}, + {0xFD, 0x00}, + SensorEnd +}; + +static struct rk_sensor_reg sensor_preview_data_pal[] = { + {0x02, 0xCE}, + {0x06, 0x32}, + {0x07, 0xC0}, + {0x08, 0x00}, + {0x09, 0x24}, + {0x0A, 0x48}, + {0x0B, 0xC0}, + {0x0C, 0x53}, + {0x0D, 0x11}, + {0x0E, 0x00}, + {0x0F, 0x00}, + {0x10, 0x70}, + {0x11, 0x4D}, + {0x12, 0x40}, + {0x13, 0x00}, + {0x14, 0x00}, + {0x15, 0x13}, + {0x16, 0x67}, + {0x17, 0xBC}, + {0x18, 0x16}, + {0x19, 0x20}, + {0x1A, 0x17}, + {0x1B, 0x00}, + {0x1C, 0x09}, + {0x1D, 0x48}, + {0x1E, 0x80}, + {0x1F, 0x80}, + {0x20, 0xB0}, + {0x21, 0x86}, + {0x22, 0x38}, + {0x23, 0x3C}, + {0x24, 0x56}, + {0x25, 0xFF}, + {0x26, 0x02}, + {0x27, 0x2D}, + {0x28, 0x00}, + {0x29, 0x48}, + {0x2A, 0x30}, + {0x2B, 0x70}, + {0x2C, 0x1A}, + {0x2D, 0x60}, + {0x2E, 0x5E}, + {0x2F, 0x00}, + {0x30, 0x7A}, + {0x31, 0x4A}, + {0x32, 0x4D}, + {0x33, 0xF0}, + {0x34, 0x00}, + {0x35, 0x65}, + {0x36, 0xDC}, + {0x37, 0x00}, + {0x38, 0x40}, + {0x39, 0x84}, + {0x3A, 0x00}, + {0x3B, 0x03}, + {0x3C, 0x00}, + {0x3D, 0x60}, + {0x3E, 0x00}, + {0x3F, 0x00}, + {0x40, 0x00}, + {0x41, 0x00}, + {0x42, 0x00}, + {0x43, 0x12}, + {0x44, 0x07}, + {0x45, 0x49}, + {0x46, 0x00}, + {0x47, 0x00}, + {0x48, 0x00}, + {0x49, 0x00}, + {0x4A, 0x00}, + {0x4B, 0x00}, + {0x4C, 0x03}, + {0x4D, 0x00}, + {0x4E, 0x37}, + {0x4F, 0x00}, + {0xB5, 0x01}, + {0xB8, 0x00}, + {0xBA, 0x00}, + {0xF3, 0x00}, + {0xF4, 0x00}, + {0xF5, 0x00}, + {0xF6, 0x00}, + {0xF7, 0x00}, + {0xF8, 0x00}, + {0xF9, 0x00}, + {0xFA, 0x00}, + {0xFB, 0x00}, + {0xFC, 0xC0}, + {0xFD, 0x00}, + SensorEnd +}; + +static struct rk_sensor_reg sensor_preview_data_720p_50hz[] = { + {0x02, 0xCA}, + {0x06, 0x32}, + {0x07, 0xC0}, + {0x08, 0x00}, + {0x09, 0x24}, + {0x0A, 0x48}, + {0x0B, 0xC0}, + {0x0C, 0x43}, + {0x0D, 0x10}, + {0x0E, 0x00}, + {0x0F, 0x00}, + {0x10, 0xf0}, + {0x11, 0x50}, + {0x12, 0x60}, + {0x13, 0x00}, + {0x14, 0x08}, + {0x15, 0x13}, + {0x16, 0x16}, + {0x17, 0x00}, + {0x18, 0x18}, + {0x19, 0xD0}, + {0x1A, 0x25}, + {0x1B, 0x00}, + {0x1C, 0x07}, + {0x1D, 0xBC}, + {0x1E, 0x80}, + {0x1F, 0x80}, + {0x20, 0x60}, + {0x21, 0x86}, + {0x22, 0x38}, + {0x23, 0x3C}, + {0x24, 0x56}, + {0x25, 0xFF}, + {0x26, 0x02}, + {0x27, 0x2D}, + {0x28, 0x00}, + {0x29, 0x48}, + {0x2A, 0x30}, + {0x2B, 0x4A}, + {0x2C, 0x1A}, + {0x2D, 0x30}, + {0x2E, 0x70}, + {0x2F, 0x00}, + {0x30, 0x48}, + {0x31, 0xBB}, + {0x32, 0x2E}, + {0x33, 0x90}, + {0x34, 0x00}, + {0x35, 0x05}, + {0x36, 0xDC}, + {0x37, 0x00}, + {0x38, 0x40}, + {0x39, 0x8C}, + {0x3A, 0x00}, + {0x3B, 0x03}, + {0x3C, 0x00}, + {0x3D, 0x60}, + {0x3E, 0x00}, + {0x3F, 0x00}, + {0x40, 0x00}, + {0x41, 0x00}, + {0x42, 0x00}, + {0x43, 0x12}, + {0x44, 0x07}, + {0x45, 0x49}, + {0x46, 0x00}, + {0x47, 0x00}, + {0x48, 0x00}, + {0x49, 0x00}, + {0x4A, 0x00}, + {0x4B, 0x00}, + {0x4C, 0x03}, + {0x4D, 0x00}, + {0x4E, 0x03}, + {0x4F, 0x01}, + {0xB5, 0x01}, + {0xB8, 0x00}, + {0xBA, 0x00}, + {0xF3, 0x00}, + {0xF4, 0x00}, + {0xF5, 0x00}, + {0xF6, 0x00}, + {0xF7, 0x00}, + {0xF8, 0x00}, + {0xF9, 0x00}, + {0xFA, 0x00}, + {0xFB, 0x00}, + {0xFC, 0xC0}, + {0xFD, 0x00}, + SensorEnd +}; + +static struct rk_sensor_reg sensor_preview_data_720p_30hz[] = { + {0x02, 0xDA}, + {0x06, 0x32}, + {0x07, 0xC0}, + {0x08, 0x00}, + {0x09, 0x24}, + {0x0A, 0x48}, + {0x0B, 0xC0}, + {0x0C, 0x53}, + {0x0D, 0x10}, + {0x0E, 0x00}, + {0x0F, 0x00}, + {0x10, 0xf0}, + {0x11, 0x50}, + {0x12, 0x60}, + {0x13, 0x00}, + {0x14, 0x08}, + {0x15, 0x13}, + {0x16, 0x16}, + {0x17, 0x00}, + {0x18, 0x19}, + {0x19, 0xD0}, + {0x1A, 0x25}, + {0x1B, 0x00}, + {0x1C, 0x06}, + {0x1D, 0x72}, + {0x1E, 0x80}, + {0x1F, 0x80}, + {0x20, 0x60}, + {0x21, 0x86}, + {0x22, 0x38}, + {0x23, 0x3C}, + {0x24, 0x56}, + {0x25, 0xFF}, + {0x26, 0x02}, + {0x27, 0x2D}, + {0x28, 0x00}, + {0x29, 0x48}, + {0x2A, 0x30}, + {0x2B, 0x4A}, + {0x2C, 0x1A}, + {0x2D, 0x30}, + {0x2E, 0x70}, + {0x2F, 0x00}, + {0x30, 0x48}, + {0x31, 0xBB}, + {0x32, 0x2E}, + {0x33, 0x90}, + {0x34, 0x00}, + {0x35, 0x25}, + {0x36, 0xDC}, + {0x37, 0x00}, + {0x38, 0x40}, + {0x39, 0x88}, + {0x3A, 0x00}, + {0x3B, 0x03}, + {0x3C, 0x00}, + {0x3D, 0x60}, + {0x3E, 0x00}, + {0x3F, 0x00}, + {0x40, 0x03}, + {0x41, 0x00}, + {0x42, 0x00}, + {0x43, 0x12}, + {0x44, 0x07}, + {0x45, 0x49}, + {0x46, 0x00}, + {0x47, 0x00}, + {0x48, 0x00}, + {0x49, 0x00}, + {0x4A, 0x00}, + {0x4B, 0x00}, + {0x4C, 0x03}, + {0x4D, 0x00}, + {0x4E, 0x17}, + {0x4F, 0x01}, + {0x85, 0x00}, + {0x88, 0x00}, + {0x8A, 0x00}, + {0xF3, 0x00}, + {0xF4, 0x00}, + {0xF5, 0x00}, + {0xF6, 0x00}, + {0xF7, 0x00}, + {0xF8, 0x00}, + {0xF9, 0x00}, + {0xFA, 0x00}, + {0xFB, 0x00}, + {0xFC, 0xC0}, + {0xFD, 0x00}, + SensorEnd +}; + +static struct rk_sensor_reg sensor_preview_data_720p_25hz[] = { + {0x02, 0xCA}, + {0x06, 0x32}, + {0x07, 0xC0}, + {0x08, 0x00}, + {0x09, 0x24}, + {0x0A, 0x48}, + {0x0B, 0xC0}, + {0x0C, 0x53}, + {0x0D, 0x10}, + {0x0E, 0x00}, + {0x0F, 0x00}, + {0x10, 0xf0}, + {0x11, 0x50}, + {0x12, 0x60}, + {0x13, 0x00}, + {0x14, 0x08}, + {0x15, 0x13}, + {0x16, 0x16}, + {0x17, 0x00}, + {0x18, 0x19}, + {0x19, 0xD0}, + {0x1A, 0x25}, + {0x1B, 0x00}, + {0x1C, 0x07}, + {0x1D, 0xBC}, + {0x1E, 0x80}, + {0x1F, 0x80}, + {0x20, 0x60}, + {0x21, 0x86}, + {0x22, 0x38}, + {0x23, 0x3C}, + {0x24, 0x56}, + {0x25, 0xFF}, + {0x26, 0x02}, + {0x27, 0x2D}, + {0x28, 0x00}, + {0x29, 0x48}, + {0x2A, 0x30}, + {0x2B, 0x70}, + {0x2C, 0x1A}, + {0x2D, 0x30}, + {0x2E, 0x70}, + {0x2F, 0x00}, + {0x30, 0x48}, + {0x31, 0xBB}, + {0x32, 0x2E}, + {0x33, 0x90}, + {0x34, 0x00}, + {0x35, 0x25}, + {0x36, 0xDC}, + {0x37, 0x00}, + {0x38, 0x40}, + {0x39, 0x88}, + {0x3A, 0x00}, + {0x3B, 0x03}, + {0x3C, 0x00}, + {0x3D, 0x60}, + {0x3E, 0x00}, + {0x3F, 0x00}, + {0x40, 0x00}, + {0x41, 0x00}, + {0x42, 0x00}, + {0x43, 0x12}, + {0x44, 0x07}, + {0x45, 0x49}, + {0x46, 0x00}, + {0x47, 0x00}, + {0x48, 0x00}, + {0x49, 0x00}, + {0x4A, 0x00}, + {0x4B, 0x00}, + {0x4C, 0x03}, + {0x4D, 0x00}, + {0x4E, 0x17}, + {0x4F, 0x01}, + {0xB5, 0x01}, + {0xB8, 0x00}, + {0xBA, 0x00}, + {0xF3, 0x00}, + {0xF4, 0x00}, + {0xF5, 0x00}, + {0xF6, 0x00}, + {0xF7, 0x00}, + {0xF8, 0x00}, + {0xF9, 0x00}, + {0xFA, 0x00}, + {0xFB, 0x00}, + {0xFC, 0xC0}, + {0xFD, 0x00}, + SensorEnd +}; + +static void tp2825_reinit_parameter(struct vehicle_ad_dev *ad, unsigned char cvstd) +{ + int i = 0, defrect_index = 0; + + switch (cvstd) { + case CVSTD_PAL: + ad->cfg.width = FORCE_PAL_WIDTH; + ad->cfg.height = FORCE_PAL_HEIGHT; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_PAL; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0; + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 25; + ad->cfg.type = V4L2_MBUS_PARALLEL; + ad->cfg.mbus_flags = V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_PCLK_SAMPLE_RISING; + break; + case CVSTD_NTSC: + ad->cfg.width = FORCE_NTSC_WIDTH; + ad->cfg.height = FORCE_NTSC_HEIGHT; + ad->cfg.start_x = 0; + ad->cfg.start_y = 0; + ad->cfg.input_format = CIF_INPUT_FORMAT_NTSC; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0; + ad->cfg.href = 0; + ad->cfg.vsync = 0; + ad->cfg.frame_rate = 30; + ad->cfg.type = V4L2_MBUS_PARALLEL; + ad->cfg.mbus_flags = V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_LOW | + V4L2_MBUS_PCLK_SAMPLE_RISING; + break; + default: + ad->cfg.width = 1280; + ad->cfg.height = 720; + ad->cfg.start_x = 8; + ad->cfg.start_y = 20; + ad->cfg.input_format = CIF_INPUT_FORMAT_YUV; + ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT; + ad->cfg.field_order = 0; + ad->cfg.yuv_order = 0;/*00 - UYVY*/ + ad->cfg.href = 0; + ad->cfg.vsync = 1; + ad->cfg.frame_rate = 50; + ad->cfg.type = V4L2_MBUS_PARALLEL; + ad->cfg.mbus_flags = V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_PCLK_SAMPLE_RISING; + break; + } + + /* fix crop info from dts config */ + for (i = 0; i < 4; i++) { + if ((ad->defrects[i].width == ad->cfg.width) && + (ad->defrects[i].height == ad->cfg.height)) { + ad->cfg.start_x = ad->defrects[i].crop_x; + ad->cfg.start_y = ad->defrects[i].crop_y; + ad->cfg.width = ad->defrects[i].crop_width; + ad->cfg.height = ad->defrects[i].crop_height; + defrect_index = i; + } + } + +#ifdef CVBS_DOUBLE_FPS_MODE + switch (cvstd) { + case CVSTD_PAL: + if (!strstr(ad->defrects[defrect_index].interface, "pal")) { + ad->cfg.height /= 2; + ad->cfg.input_format = + CIF_INPUT_FORMAT_PAL_SW_COMPOSITE; + ad->cfg.href = 0; + ad->cfg.vsync = 1; + ad->cfg.frame_rate = 50; + } + break; + case CVSTD_NTSC: + if (!strstr(ad->defrects[defrect_index].interface, "ntsc")) { + ad->cfg.height /= 2; + ad->cfg.input_format = + CIF_INPUT_FORMAT_NTSC_SW_COMPOSITE; + ad->cfg.href = 0; + ad->cfg.vsync = 1; + ad->cfg.frame_rate = 60; + } + break; + } +#endif + SENSOR_DG("%s,crop(%d,%d)", __func__, ad->cfg.start_x, ad->cfg.start_y); +} + +static void tp2825_reg_init(struct vehicle_ad_dev *ad, unsigned char cvstd) +{ + struct rk_sensor_reg *sensor; + int i; + unsigned char val[2]; + + switch (cvstd) { + case CVSTD_720P50: + sensor = sensor_preview_data_720p_50hz; + break; + case CVSTD_720P30: + sensor = sensor_preview_data_720p_30hz; + break; + case CVSTD_720P25: + sensor = sensor_preview_data_720p_25hz; + break; + case CVSTD_PAL: + sensor = sensor_preview_data_pal; + break; + case CVSTD_NTSC: + sensor = sensor_preview_data_ntsc; + break; + default: + sensor = sensor_preview_data_720p_50hz; + break; + } + i = 0; + while ((sensor[i].reg != SEQCMD_END) && (sensor[i].reg != 0xFC000000)) { + if (sensor[i].reg == SENSOR_CHANNEL_REG) + sensor[i].val = ad->ad_chl; + + val[0] = sensor[i].val; + vehicle_generic_sensor_write(ad, sensor[i].reg, val); + i++; + } +} + +void tp2825_channel_set(struct vehicle_ad_dev *ad, int channel) +{ + unsigned int reg = 0x41; + unsigned char val[0]; + + val[0] = channel; + ad->ad_chl = channel; + + vehicle_generic_sensor_write(ad, reg, val); +} + +int tp2825_ad_get_cfg(struct vehicle_cfg **cfg) +{ + if (!tp2825_g_addev) + return -1; + + switch (cvstd_state) { + case VIDEO_UNPLUG: + tp2825_g_addev->cfg.ad_ready = false; + break; + case VIDEO_LOCKED: + tp2825_g_addev->cfg.ad_ready = true; + break; + case VIDEO_IN: + tp2825_g_addev->cfg.ad_ready = false; + break; + } + + *cfg = &tp2825_g_addev->cfg; + + return 0; +} + +void tp2825_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line) +{ + SENSOR_DG("%s, last_line %d\n", __func__, last_line); + if (last_line < 1) + return; + + ad->cif_error_last_line = last_line; + if (cvstd_mode == CVSTD_PAL) { + if (last_line == FORCE_NTSC_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } else if (cvstd_mode == CVSTD_NTSC) { + if (last_line == FORCE_PAL_HEIGHT) { + if (ad->state_check_work.state_check_wq) + queue_delayed_work( + ad->state_check_work.state_check_wq, + &ad->state_check_work.work, + msecs_to_jiffies(0)); + } + } +} + +int tp2825_check_id(struct vehicle_ad_dev *ad) +{ + int ret = 0; + int pidh, pidl; + + pidh = vehicle_generic_sensor_read(ad, 0xfe); + pidl = vehicle_generic_sensor_read(ad, 0xff); + if (pidh != 0x28 || pidl != 0x25) { + SENSOR_DG("%s: expected 0x2825, detected 0x%02x 0x%02x\n", + ad->ad_name, pidh, pidl); + ret = -EINVAL; + } + + return ret; +} + +static int tp2825_check_cvstd(struct vehicle_ad_dev *ad, bool activate_check) +{ + unsigned char cvstd = 0; + unsigned char status = 0; + static bool is_first = true; + static int state = VIDEO_UNPLUG; + int check_count = 20; + unsigned char v[2]; + +check_continue: + status = vehicle_generic_sensor_read(ad, 0x01); + + if (status & FLAG_LOSS) { + state = VIDEO_UNPLUG; + v[0] = 0x01; + vehicle_generic_sensor_write(ad, 0x26, v); + } else if (FLAG_LOCKED == (status & FLAG_LOCKED)) { + /* video locked */ + state = VIDEO_LOCKED; + v[0] = 0x02; + vehicle_generic_sensor_write(ad, 0x26, v); + } else { + /* video in but unlocked */ + state = VIDEO_IN; + v[0] = 0x02; + vehicle_generic_sensor_write(ad, 0x26, v); + } + + if (state == VIDEO_IN) { + cvstd = vehicle_generic_sensor_read(ad, 0x03); + SENSOR_DG("%s(%d): cvstd_old %d, read 0x03 return 0x%x", + __func__, __LINE__, cvstd_old, cvstd); + + cvstd &= 0x07; + if (cvstd == cvstd_old) + goto check_end; + + if (cvstd == CVSTD_720P30) { + cvstd_mode = CVSTD_720P30; + SENSOR_DG("%s(%d): 720P30\n", __func__, __LINE__); + } else if (cvstd == CVSTD_720P25) { + cvstd_mode = CVSTD_720P25; + SENSOR_DG("%s(%d): 720P25\n", __func__, __LINE__); + } else if (cvstd == CVSTD_720P60) { + SENSOR_DG("%s(%d): 720P60", __func__, __LINE__); + } else if (cvstd == CVSTD_720P50) { + cvstd_mode = CVSTD_720P50; + SENSOR_DG("%s(%d): 720P50\n", __func__, __LINE__); + } else if (cvstd == CVSTD_1080P30) { + SENSOR_DG("%s(%d): 1080P30", __func__, __LINE__); + } else if (cvstd == CVSTD_1080P25) { + SENSOR_DG("%s(%d): 1080P25", __func__, __LINE__); + } else if (cvstd == CVSTD_SD) { + msleep(80); + status = vehicle_generic_sensor_read(ad, 0x01); + SENSOR_DG("%s(%d): read 0x01 return 0x%x\n", + __func__, __LINE__, status); + + /* + * 1: pal 0: ntsc + */ + if ((status >> 2) & 0x01) + cvstd_sd = CVSTD_PAL; + else + cvstd_sd = CVSTD_NTSC; + + SENSOR_DG("%s(%d): cvstd_sd is %s\n", + __func__, __LINE__, + (cvstd_sd == CVSTD_PAL) ? "PAL" : "NTSC"); + cvstd_mode = cvstd_sd; + } + tp2825_reinit_parameter(ad, cvstd_mode); + } else if (state == VIDEO_LOCKED) { + goto check_end; + } else { + SENSOR_DG("%s: check sensor statue failed!\n", __func__); + goto check_end; + } + + tp2825_reg_init(ad, cvstd_mode); +check_end: + if (check_count && is_first && (state != VIDEO_LOCKED)) { + check_count--; + if (cvstd == CVSTD_SD) + mdelay(100); + else + mdelay(100); + goto check_continue; + } + is_first = false; + cvstd_state = state; + + return 0; +} +int tp2825_stream(struct vehicle_ad_dev *ad, int enable) +{ + char val; + + if (enable) + val = 0x03; //stream on + else + val = 0x00; //stream off + SENSOR_DG("stream write 0x%x to reg 0x4D\n", val); + vehicle_generic_sensor_write(ad, 0x4D, &val); + + return 0; +} +static void power_on(struct vehicle_ad_dev *ad) +{ + /* gpio_direction_output(ad->power, ad->pwr_active); */ + + if (gpio_is_valid(ad->powerdown)) { + gpio_request(ad->powerdown, "ad_powerdown"); + gpio_direction_output(ad->powerdown, !ad->pwdn_active); + /* gpio_set_value(ad->powerdown, !ad->pwdn_active); */ + } + + if (gpio_is_valid(ad->power)) { + gpio_request(ad->power, "ad_power"); + gpio_direction_output(ad->power, ad->pwr_active); + /* gpio_set_value(ad->power, ad->pwr_active); */ + } +} + +static void power_off(struct vehicle_ad_dev *ad) +{ + if (gpio_is_valid(ad->power)) + gpio_free(ad->power); + if (gpio_is_valid(ad->powerdown)) + gpio_free(ad->powerdown); +} + +static void tp2825_check_state_work(struct work_struct *work) +{ + struct vehicle_ad_dev *ad; + + ad = tp2825_g_addev; + + if (ad->cif_error_last_line > 0) { + tp2825_check_cvstd(ad, true); + ad->cif_error_last_line = 0; + } else { + tp2825_check_cvstd(ad, false); + } + + if (cvstd_old != cvstd_mode || cvstd_old_state != cvstd_state) { + cvstd_old = cvstd_mode; + cvstd_old_state = cvstd_state; + SENSOR_DG("ad signal change notify\n"); + vehicle_ad_stat_change_notify(); + } + + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(100)); +} + +int tp2825_ad_deinit(void) +{ + struct vehicle_ad_dev *ad; + + ad = tp2825_g_addev; + + if (!ad) + return -1; + + if (ad->state_check_work.state_check_wq) { + cancel_delayed_work_sync(&ad->state_check_work.work); + flush_delayed_work(&ad->state_check_work.work); + flush_workqueue(ad->state_check_work.state_check_wq); + destroy_workqueue(ad->state_check_work.state_check_wq); + } + if (ad->irq) + free_irq(ad->irq, ad); + power_off(ad); + + return 0; +} + +static int get_ad_mode_from_fix_format(int fix_format) +{ + int mode = -1; + + switch (fix_format) { + case AD_FIX_FORMAT_PAL: + mode = CVSTD_PAL; + break; + case AD_FIX_FORMAT_NTSC: + mode = CVSTD_NTSC; + break; + case AD_FIX_FORMAT_720P_50FPS: + mode = CVSTD_720P50; + break; + case AD_FIX_FORMAT_720P_30FPS: + mode = CVSTD_720P30; + break; + case AD_FIX_FORMAT_720P_25FPS: + mode = CVSTD_720P25; + break; + default: + mode = -1; + break; + } + + return mode; +} + +int tp2825_ad_init(struct vehicle_ad_dev *ad) +{ + int val = 0; + int i = 0; + int mode; + + tp2825_g_addev = ad; + + /* 1. i2c init */ + while (ad->adapter == NULL) { + ad->adapter = i2c_get_adapter(ad->i2c_chl); + usleep_range(10000, 12000); + } + if (ad->adapter == NULL) + return -ENODEV; + + if (!i2c_check_functionality(ad->adapter, I2C_FUNC_I2C)) + return -EIO; + + /* 2. ad power on sequence */ + power_on(ad); + + while (++i < 5) { + usleep_range(1000, 1200); + val = vehicle_generic_sensor_read(ad, 0x12); + if (val != 0xff) + break; + SENSOR_DG("tp2825_init i2c_reg_read fail\n"); + } + + /* fix mode */ + mode = get_ad_mode_from_fix_format(ad->fix_format); + if (mode > 0) { + SENSOR_DG("fix format %d, fix cvxtd mode %d\n", ad->fix_format, mode); + tp2825_reg_init(ad, mode); + tp2825_reinit_parameter(ad, mode); + SENSOR_DG("%s after init\n", __func__); + /* wait for signal locked; */ + i = 0; + while (++i < 10) { + msleep(100); + val = vehicle_generic_sensor_read(ad, 0x01); + if ((FLAG_LOCKED == (val & FLAG_LOCKED))) + break; + } + cvstd_state = VIDEO_LOCKED; + return 0; + } + + /* 3 .init default format params */ + tp2825_reg_init(ad, cvstd_mode); + tp2825_reinit_parameter(ad, cvstd_mode); + SENSOR_DG("%s after reinit init\n", __func__); + + /* 5. create workqueue to detect signal change */ + INIT_DELAYED_WORK(&ad->state_check_work.work, tp2825_check_state_work); + ad->state_check_work.state_check_wq = + create_singlethread_workqueue("vehicle-ad-tp2825"); + + /* tp2825_check_cvstd(ad, true); */ + + queue_delayed_work(ad->state_check_work.state_check_wq, + &ad->state_check_work.work, msecs_to_jiffies(100)); + + return 0; +} + + diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_tp2825.h b/drivers/video/rockchip/vehicle/vehicle_ad_tp2825.h new file mode 100644 index 000000000000..451912580de8 --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_ad_tp2825.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Vehicle driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ +#ifndef __VEHICLE_AD_TP2825_H__ +#define __VEHICLE_AD_TP2825_H__ + +int tp2825_ad_init(struct vehicle_ad_dev *ad); +int tp2825_ad_deinit(void); +int tp2825_ad_get_cfg(struct vehicle_cfg **cfg); +void tp2825_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line); +int tp2825_check_id(struct vehicle_ad_dev *ad); +int tp2825_stream(struct vehicle_ad_dev *ad, int enable); +void tp2825_channel_set(struct vehicle_ad_dev *ad, int channel); + +#endif diff --git a/drivers/video/rockchip/vehicle/vehicle_cfg.h b/drivers/video/rockchip/vehicle/vehicle_cfg.h new file mode 100644 index 000000000000..b03b6a80b9a3 --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_cfg.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Vehicle driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ +#ifndef __VEHICLE_CFG +#define __VEHICLE_CFG +#include +#include + +/* Driver information */ +#define VEHICLE_DRIVER_NAME "Vehicle" + +static int vehicle_debug; +#define VEHICLE_DG(format, ...) do { \ + if (vehicle_debug) \ + pr_info("%s %s(%d): " format, __func__, __LINE__, ## __VA_ARGS__); \ + } while (0) + +#define VEHICLE_DGERR(format, ...) \ + pr_info("%s %s(%d):" format, VEHICLE_DRIVER_NAME, __func__, __LINE__, ## __VA_ARGS__) +#define VEHICLE_INFO(format, ...) \ + pr_info("%s %s(%d):" format, VEHICLE_DRIVER_NAME, __func__, __LINE__, ## __VA_ARGS__) + +#define MAX_BUF_NUM (6) + +#define CVBS_DOUBLE_FPS_MODE /*PAL 50fps; NTSC 60fps*/ + +enum { + CIF_INPUT_FORMAT_YUV = 0, + CIF_INPUT_FORMAT_PAL = 2, + CIF_INPUT_FORMAT_NTSC = 3, + CIF_INPUT_FORMAT_RAW = 4, + CIF_INPUT_FORMAT_JPEG = 5, + CIF_INPUT_FORMAT_MIPI = 6, + CIF_INPUT_FORMAT_PAL_SW_COMPOSITE = 0xff000000, + CIF_INPUT_FORMAT_NTSC_SW_COMPOSITE = 0xfe000000, +}; + +enum { + CIF_OUTPUT_FORMAT_422 = 0, + CIF_OUTPUT_FORMAT_420 = 1, +}; + +struct vehicle_cfg { + /* output */ + int width; + int height; + /* sensor output */ + int src_width; + int src_height; + /* + * action: source video data input format. + * 000 - YUV + * 010 - PAL + * 011 - NTSC + * 100 - RAW + * 101 - JPEG + * 110 - MIPI + */ + int input_format; + /* + * 0 - output is 422 + * 1 - output is 420 + */ + int output_format; + /* + * YUV input order + * 00 - UYVY + * 01 - YVYU + * 10 - VYUY + * 11 - YUYV + */ + int yuv_order; + /* + * ccir input order + * 0 : odd field first + * 1 : even field first + */ + int field_order; + + /* + * BT.656 not use + * BT.601 hsync polarity + * val: + * 0-low active + * 1-high active + */ + int href; + /* + * BT.656 not use + * BT.601 hsync polarity + * val : + * 0-low active + * 1-high active + */ + int vsync; + + /* + * enum v4l2_mbus_type - media bus type + * @V4L2_MBUS_PARALLEL: parallel interface with hsync and vsync + * @V4L2_MBUS_BT656: parallel interface with embedded synchronisation, can + * also be used for BT.1120 + * @V4L2_MBUS_CSI1: MIPI CSI-1 serial interface + * @V4L2_MBUS_CCP2: CCP2 (Compact Camera Port 2) + * @V4L2_MBUS_CSI2: MIPI CSI-2 serial interface + */ + enum v4l2_mbus_type type; + + /* + * Signal polarity flags + * Note: in BT.656 mode HSYNC, FIELD, and VSYNC are unused + * V4L2_MBUS_[HV]SYNC* flags should be also used for specifying + * configuration of hardware that uses [HV]REF signals + */ + unsigned int mbus_flags; + + /* + * Note: in BT.656/601 mode mipi_freq are unused + * only used when v4l2_mbus_type is V4L2_MBUS_CSI2 + */ + s64 mipi_freq; + /* + * Note: in BT.656/601 mode mipi_freq are unused + * only used when v4l2_mbus_type is V4L2_MBUS_CSI2 + */ + int lanes; + + u32 mbus_code; + + int start_x; + int start_y; + int frame_rate; + + unsigned int buf_phy_addr[MAX_BUF_NUM]; + unsigned int buf_num; + int ad_ready; + /*0:no, 1:90; 2:180; 4:270; 0x10:mirror-y; 0x20:mirror-x*/ + int rotate_mirror; + struct rkmodule_csi_dphy_param *dphy_param; +}; + +#endif diff --git a/drivers/video/rockchip/vehicle/vehicle_cif.c b/drivers/video/rockchip/vehicle/vehicle_cif.c new file mode 100644 index 000000000000..a5ac904d9cca --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_cif.c @@ -0,0 +1,4930 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/video/rockchip/video/vehicle_cif.c + * + * mipi_dphy/csi_host/vicap driver for vehicle + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + * Authors: + * Jianwei Fan + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vehicle-csi2-dphy-common.h" +#include "vehicle_cif.h" +#include "vehicle_flinger.h" +#include "vehicle_main.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vehicle_samsung_dcphy_common.h" + +#define CIF_DG VEHICLE_DG +#define CIF_ERR VEHICLE_DGERR + +static struct vehicle_cif *g_cif; + +#define write_reg(base, addr, val) \ + writel(val, (addr) + (base)) +#define read_reg(base, addr) \ + readl((addr) + (base)) + +#define vehicle_write_csihost_reg(base, addr, val) write_reg(base, addr, val) +#define vehicle_read_csihost_reg(base, addr) read_reg(base, addr) + +//define cif clk and rst +static const char * const rk3568_cif_clks[] = { + "aclk_cif", + "hclk_cif", + "dclk_cif", + "iclk_cif_g", +}; + +static const char * const rk3568_cif_rsts[] = { + "rst_cif_a", + "rst_cif_h", + "rst_cif_d", + "rst_cif_p", + "rst_cif_i", +}; + +static const char * const rk3588_cif_clks[] = { + "aclk_cif", + "hclk_cif", + "dclk_cif", +}; + +static const char * const rk3588_cif_rsts[] = { + "rst_cif_a", + "rst_cif_h", + "rst_cif_d", +}; + +//define dphy and csi clks/rst +static struct clk_bulk_data rk3568_csi2_dphy_hw_clks[] = { + { .id = "pclk" }, +}; + +static struct clk_bulk_data rk3568_csi2_clks[] = { + { .id = "pclk_csi2host" }, +}; + +static const char * const rk3568_csi2_rsts[] = { + "srst_csihost_p", +}; + +static struct clk_bulk_data rk3588_csi2_dphy_hw_clks[] = { + { .id = "pclk" }, +}; + +static const char * const rk3588_csi2_dphy_hw_rsts[] = { + "srst_csiphy", + "srst_p_csiphy", +}; + +static struct clk_bulk_data rk3588_csi2_clks[] = { + { .id = "pclk_csi2host" }, +}; + +static struct clk_bulk_data rk3588_csi2_dcphy_clks[] = { + { .id = "pclk_csi2host" }, + { .id = "iclk_csi2host" }, +}; + +static const char * const rk3588_csi2_rsts[] = { + "srst_csihost_p", + "srst_csihost_vicap", +}; + +//define cif regs +static const struct vehicle_cif_reg rk3568_cif_regs[] = { + [CIF_REG_DVP_CTRL] = CIF_REG_NAME(CIF_CTRL, "CIF_REG_DVP_CTRL"), + [CIF_REG_DVP_INTEN] = CIF_REG_NAME(CIF_INTEN, "CIF_REG_DVP_INTEN"), + [CIF_REG_DVP_INTSTAT] = CIF_REG_NAME(CIF_INTSTAT, "CIF_REG_DVP_INTSTAT"), + [CIF_REG_DVP_FOR] = CIF_REG_NAME(CIF_FOR, "CIF_REG_DVP_FOR"), + [CIF_REG_DVP_MULTI_ID] = CIF_REG_NAME(CIF_MULTI_ID, "CIF_REG_DVP_MULTI_ID"), + [CIF_REG_DVP_FRM0_ADDR_Y] = CIF_REG_NAME(CIF_FRM0_ADDR_Y, "CIF_REG_DVP_FRM0_ADDR_Y"), + [CIF_REG_DVP_FRM0_ADDR_UV] = CIF_REG_NAME(CIF_FRM0_ADDR_UV, "CIF_REG_DVP_FRM0_ADDR_UV"), + [CIF_REG_DVP_FRM1_ADDR_Y] = CIF_REG_NAME(CIF_FRM1_ADDR_Y, "CIF_REG_DVP_FRM1_ADDR_Y"), + [CIF_REG_DVP_FRM1_ADDR_UV] = CIF_REG_NAME(CIF_FRM1_ADDR_UV, "CIF_REG_DVP_FRM1_ADDR_UV"), + [CIF_REG_DVP_VIR_LINE_WIDTH] = CIF_REG_NAME(CIF_VIR_LINE_WIDTH, + "CIF_REG_DVP_VIR_LINE_WIDTH"), + [CIF_REG_DVP_SET_SIZE] = CIF_REG_NAME(CIF_SET_SIZE, "CIF_REG_DVP_SET_SIZE"), + [CIF_REG_DVP_LINE_INT_NUM] = CIF_REG_NAME(CIF_LINE_INT_NUM, "CIF_REG_DVP_LINE_INT_NUM"), + [CIF_REG_DVP_LINE_CNT] = CIF_REG_NAME(CIF_LINE_CNT, "CIF_REG_DVP_LINE_CNT"), + [CIF_REG_DVP_CROP] = CIF_REG_NAME(RV1126_CIF_CROP, "CIF_REG_DVP_CROP"), + [CIF_REG_DVP_FIFO_ENTRY] = CIF_REG_NAME(RK3568_CIF_FIFO_ENTRY, "CIF_REG_DVP_FIFO_ENTRY"), + [CIF_REG_DVP_FRAME_STATUS] = CIF_REG_NAME(RV1126_CIF_FRAME_STATUS, + "CIF_REG_DVP_FRAME_STATUS"), + [CIF_REG_DVP_CUR_DST] = CIF_REG_NAME(RV1126_CIF_CUR_DST, "CIF_REG_DVP_CUR_DST"), + [CIF_REG_DVP_LAST_LINE] = CIF_REG_NAME(RV1126_CIF_LAST_LINE, "CIF_REG_DVP_LAST_LINE"), + [CIF_REG_DVP_LAST_PIX] = CIF_REG_NAME(RV1126_CIF_LAST_PIX, "CIF_REG_DVP_LAST_PIX"), + [CIF_REG_DVP_FRM0_ADDR_Y_ID1] = CIF_REG_NAME(CIF_FRM0_ADDR_Y_ID1, + "CIF_REG_DVP_FRM0_ADDR_Y_ID1"), + [CIF_REG_DVP_FRM0_ADDR_UV_ID1] = CIF_REG_NAME(CIF_FRM0_ADDR_UV_ID1, + "CIF_REG_DVP_FRM0_ADDR_UV_ID1"), + [CIF_REG_DVP_FRM1_ADDR_Y_ID1] = CIF_REG_NAME(CIF_FRM1_ADDR_Y_ID1, + "CIF_REG_DVP_FRM1_ADDR_Y_ID1"), + [CIF_REG_DVP_FRM1_ADDR_UV_ID1] = CIF_REG_NAME(CIF_FRM1_ADDR_UV_ID1, + "CIF_REG_DVP_FRM1_ADDR_UV_ID1"), + [CIF_REG_DVP_FRM0_ADDR_Y_ID2] = CIF_REG_NAME(CIF_FRM0_ADDR_Y_ID2, + "CIF_REG_DVP_FRM0_ADDR_Y_ID2"), + [CIF_REG_DVP_FRM0_ADDR_UV_ID2] = CIF_REG_NAME(CIF_FRM0_ADDR_UV_ID2, + "CIF_REG_DVP_FRM0_ADDR_UV_ID2"), + [CIF_REG_DVP_FRM1_ADDR_Y_ID2] = CIF_REG_NAME(CIF_FRM1_ADDR_Y_ID2, + "CIF_REG_DVP_FRM1_ADDR_Y_ID2"), + [CIF_REG_DVP_FRM1_ADDR_UV_ID2] = CIF_REG_NAME(CIF_FRM1_ADDR_UV_ID2, + "CIF_REG_DVP_FRM1_ADDR_UV_ID2"), + [CIF_REG_DVP_FRM0_ADDR_Y_ID3] = CIF_REG_NAME(CIF_FRM0_ADDR_Y_ID3, + "CIF_REG_DVP_FRM0_ADDR_Y_ID3"), + [CIF_REG_DVP_FRM0_ADDR_UV_ID3] = CIF_REG_NAME(CIF_FRM0_ADDR_UV_ID3, + "CIF_REG_DVP_FRM0_ADDR_UV_ID3"), + [CIF_REG_DVP_FRM1_ADDR_Y_ID3] = CIF_REG_NAME(CIF_FRM1_ADDR_Y_ID3, + "CIF_REG_DVP_FRM1_ADDR_Y_ID3"), + [CIF_REG_DVP_FRM1_ADDR_UV_ID3] = CIF_REG_NAME(CIF_FRM1_ADDR_UV_ID3, + "CIF_REG_DVP_FRM1_ADDR_UV_ID3"), + [CIF_REG_MIPI_LVDS_ID0_CTRL0] = CIF_REG_NAME(CIF_CSI_ID0_CTRL0, + "CIF_REG_MIPI_LVDS_ID0_CTRL0"), + [CIF_REG_MIPI_LVDS_ID0_CTRL1] = CIF_REG_NAME(CIF_CSI_ID0_CTRL1, + "CIF_REG_MIPI_LVDS_ID0_CTRL1"), + [CIF_REG_MIPI_LVDS_ID1_CTRL0] = CIF_REG_NAME(CIF_CSI_ID1_CTRL0, + "CIF_REG_MIPI_LVDS_ID1_CTRL0"), + [CIF_REG_MIPI_LVDS_ID1_CTRL1] = CIF_REG_NAME(CIF_CSI_ID1_CTRL1, + "CIF_REG_MIPI_LVDS_ID1_CTRL1"), + [CIF_REG_MIPI_LVDS_ID2_CTRL0] = CIF_REG_NAME(CIF_CSI_ID2_CTRL0, + "CIF_REG_MIPI_LVDS_ID2_CTRL0"), + [CIF_REG_MIPI_LVDS_ID2_CTRL1] = CIF_REG_NAME(CIF_CSI_ID2_CTRL1, + "CIF_REG_MIPI_LVDS_ID2_CTRL1"), + [CIF_REG_MIPI_LVDS_ID3_CTRL0] = CIF_REG_NAME(CIF_CSI_ID3_CTRL0, + "CIF_REG_MIPI_LVDS_ID3_CTRL0"), + [CIF_REG_MIPI_LVDS_ID3_CTRL1] = CIF_REG_NAME(CIF_CSI_ID3_CTRL1, + "CIF_REG_MIPI_LVDS_ID3_CTRL1"), + [CIF_REG_MIPI_LVDS_CTRL] = CIF_REG_NAME(CIF_CSI_MIPI_LVDS_CTRL, + "CIF_REG_MIPI_LVDS_CTRL"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID0] = CIF_REG_NAME(CIF_CSI_FRM0_ADDR_Y_ID0, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID0"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID0] = CIF_REG_NAME(CIF_CSI_FRM1_ADDR_Y_ID0, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID0"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID0] = CIF_REG_NAME(CIF_CSI_FRM0_ADDR_UV_ID0, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID0"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID0] = CIF_REG_NAME(CIF_CSI_FRM1_ADDR_UV_ID0, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID0"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID0] = CIF_REG_NAME(CIF_CSI_FRM0_VLW_Y_ID0, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID0"), + [CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID0] = CIF_REG_NAME(CIF_CSI_FRM1_VLW_Y_ID0, + "CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID0"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID0] = CIF_REG_NAME(CIF_CSI_FRM0_VLW_UV_ID0, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID0"), + [CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID0] = CIF_REG_NAME(CIF_CSI_FRM1_VLW_UV_ID0, + "CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID0"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID1] = CIF_REG_NAME(CIF_CSI_FRM0_ADDR_Y_ID1, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID1"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID1] = CIF_REG_NAME(CIF_CSI_FRM1_ADDR_Y_ID1, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID1"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID1] = CIF_REG_NAME(CIF_CSI_FRM0_ADDR_UV_ID1, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID1"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID1] = CIF_REG_NAME(CIF_CSI_FRM1_ADDR_UV_ID1, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID1"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID1] = CIF_REG_NAME(CIF_CSI_FRM0_VLW_Y_ID1, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID1"), + [CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID1] = CIF_REG_NAME(CIF_CSI_FRM1_VLW_Y_ID1, + "CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID1"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID1] = CIF_REG_NAME(CIF_CSI_FRM0_VLW_UV_ID1, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID1"), + [CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID1] = CIF_REG_NAME(CIF_CSI_FRM1_VLW_UV_ID1, + "CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID1"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID2] = CIF_REG_NAME(CIF_CSI_FRM0_ADDR_Y_ID2, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID2"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID2] = CIF_REG_NAME(CIF_CSI_FRM1_ADDR_Y_ID2, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID2"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID2] = CIF_REG_NAME(CIF_CSI_FRM0_ADDR_UV_ID2, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID2"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID2] = CIF_REG_NAME(CIF_CSI_FRM1_ADDR_UV_ID2, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID2"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID2] = CIF_REG_NAME(CIF_CSI_FRM0_VLW_Y_ID2, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID2"), + [CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID2] = CIF_REG_NAME(CIF_CSI_FRM1_VLW_Y_ID2, + "CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID2"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID2] = CIF_REG_NAME(CIF_CSI_FRM0_VLW_UV_ID2, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID2"), + [CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID2] = CIF_REG_NAME(CIF_CSI_FRM1_VLW_UV_ID2, + "CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID2"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID3] = CIF_REG_NAME(CIF_CSI_FRM0_ADDR_Y_ID3, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID3"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID3] = CIF_REG_NAME(CIF_CSI_FRM1_ADDR_Y_ID3, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID3"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID3] = CIF_REG_NAME(CIF_CSI_FRM0_ADDR_UV_ID3, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID3"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID3] = CIF_REG_NAME(CIF_CSI_FRM1_ADDR_UV_ID3, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID3"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID3] = CIF_REG_NAME(CIF_CSI_FRM0_VLW_Y_ID3, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID3"), + [CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID3] = CIF_REG_NAME(CIF_CSI_FRM1_VLW_Y_ID3, + "CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID3"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID3] = CIF_REG_NAME(CIF_CSI_FRM0_VLW_UV_ID3, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID3"), + [CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID3] = CIF_REG_NAME(CIF_CSI_FRM1_VLW_UV_ID3, + "CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID3"), + [CIF_REG_MIPI_LVDS_INTEN] = CIF_REG_NAME(CIF_CSI_INTEN, "CIF_REG_MIPI_LVDS_INTEN"), + [CIF_REG_MIPI_LVDS_INTSTAT] = CIF_REG_NAME(CIF_CSI_INTSTAT, "CIF_REG_MIPI_LVDS_INTSTAT"), + [CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1] = CIF_REG_NAME(CIF_CSI_LINE_INT_NUM_ID0_1, + "CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1"), + [CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3] = CIF_REG_NAME(CIF_CSI_LINE_INT_NUM_ID2_3, + "CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3"), + [CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID0_1] = CIF_REG_NAME(CIF_CSI_LINE_CNT_ID0_1, + "CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID0_1"), + [CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID2_3] = CIF_REG_NAME(CIF_CSI_LINE_CNT_ID2_3, + "CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID2_3"), + [CIF_REG_MIPI_LVDS_ID0_CROP_START] = CIF_REG_NAME(CIF_CSI_ID0_CROP_START, + "CIF_REG_MIPI_LVDS_ID0_CROP_START"), + [CIF_REG_MIPI_LVDS_ID1_CROP_START] = CIF_REG_NAME(CIF_CSI_ID1_CROP_START, + "CIF_REG_MIPI_LVDS_ID1_CROP_START"), + [CIF_REG_MIPI_LVDS_ID2_CROP_START] = CIF_REG_NAME(CIF_CSI_ID2_CROP_START, + "CIF_REG_MIPI_LVDS_ID2_CROP_START"), + [CIF_REG_MIPI_LVDS_ID3_CROP_START] = CIF_REG_NAME(CIF_CSI_ID3_CROP_START, + "CIF_REG_MIPI_LVDS_ID3_CROP_START"), + [CIF_REG_MIPI_FRAME_NUM_VC0] = CIF_REG_NAME(CIF_CSI_FRAME_NUM_VC0, + "CIF_REG_MIPI_FRAME_NUM_VC0"), + [CIF_REG_MIPI_FRAME_NUM_VC1] = CIF_REG_NAME(CIF_CSI_FRAME_NUM_VC1, + "CIF_REG_MIPI_FRAME_NUM_VC1"), + [CIF_REG_MIPI_FRAME_NUM_VC2] = CIF_REG_NAME(CIF_CSI_FRAME_NUM_VC2, + "CIF_REG_MIPI_FRAME_NUM_VC2"), + [CIF_REG_MIPI_FRAME_NUM_VC3] = CIF_REG_NAME(CIF_CSI_FRAME_NUM_VC3, + "CIF_REG_MIPI_FRAME_NUM_VC3"), + [CIF_REG_Y_STAT_CONTROL] = CIF_REG_NAME(CIF_Y_STAT_CONTROL, + "CIF_REG_Y_STAT_CONTROL"), + [CIF_REG_Y_STAT_VALUE] = CIF_REG_NAME(CIF_Y_STAT_VALUE, "CIF_REG_Y_STAT_VALUE"), + [CIF_REG_MMU_DTE_ADDR] = CIF_REG_NAME(CIF_MMU_DTE_ADDR, "CIF_REG_MMU_DTE_ADDR"), + [CIF_REG_MMU_STATUS] = CIF_REG_NAME(CIF_MMU_STATUS, "CIF_REG_MMU_STATUS"), + [CIF_REG_MMU_COMMAND] = CIF_REG_NAME(CIF_MMU_COMMAND, "CIF_REG_MMU_COMMAND"), + [CIF_REG_MMU_PAGE_FAULT_ADDR] = CIF_REG_NAME(CIF_MMU_PAGE_FAULT_ADDR, + "CIF_REG_MMU_PAGE_FAULT_ADDR"), + [CIF_REG_MMU_ZAP_ONE_LINE] = CIF_REG_NAME(CIF_MMU_ZAP_ONE_LINE, "CIF_REG_MMU_ZAP_ONE_LINE"), + [CIF_REG_MMU_INT_RAWSTAT] = CIF_REG_NAME(CIF_MMU_INT_RAWSTAT, "CIF_REG_MMU_INT_RAWSTAT"), + [CIF_REG_MMU_INT_CLEAR] = CIF_REG_NAME(CIF_MMU_INT_CLEAR, "CIF_REG_MMU_INT_CLEAR"), + [CIF_REG_MMU_INT_MASK] = CIF_REG_NAME(CIF_MMU_INT_MASK, "CIF_REG_MMU_INT_MASK"), + [CIF_REG_MMU_INT_STATUS] = CIF_REG_NAME(CIF_MMU_INT_STATUS, "CIF_REG_MMU_INT_STATUS"), + [CIF_REG_MMU_AUTO_GATING] = CIF_REG_NAME(CIF_MMU_AUTO_GATING, "CIF_REG_MMU_AUTO_GATING"), + [CIF_REG_GRF_CIFIO_CON] = CIF_REG_NAME(CIF_GRF_VI_CON0, "CIF_REG_GRF_CIFIO_CON"), + [CIF_REG_GRF_CIFIO_CON1] = CIF_REG_NAME(CIF_GRF_VI_CON1, "CIF_REG_GRF_CIFIO_CON1"), +}; + +static const struct vehicle_cif_reg rk3588_cif_regs[] = { + [CIF_REG_DVP_CTRL] = CIF_REG_NAME(DVP_CTRL, "CIF_REG_DVP_CTRL"), + [CIF_REG_DVP_INTEN] = CIF_REG_NAME(DVP_INTEN, "CIF_REG_DVP_INTEN"), + [CIF_REG_DVP_INTSTAT] = CIF_REG_NAME(DVP_INTSTAT, "CIF_REG_DVP_INTSTAT"), + [CIF_REG_DVP_FOR] = CIF_REG_NAME(DVP_FOR, "CIF_REG_DVP_FOR"), + [CIF_REG_DVP_MULTI_ID] = CIF_REG_NAME(DVP_MULTI_ID, "CIF_REG_DVP_MULTI_ID"), + [CIF_REG_DVP_SAV_EAV] = CIF_REG_NAME(DVP_SAV_EAV, "CIF_REG_DVP_SAV_EAV"), + [CIF_REG_DVP_FRM0_ADDR_Y] = CIF_REG_NAME(DVP_FRM0_ADDR_Y_ID0, "CIF_REG_DVP_FRM0_ADDR_Y"), + [CIF_REG_DVP_FRM0_ADDR_UV] = CIF_REG_NAME(DVP_FRM0_ADDR_UV_ID0, "CIF_REG_DVP_FRM0_ADDR_UV"), + [CIF_REG_DVP_FRM1_ADDR_Y] = CIF_REG_NAME(DVP_FRM1_ADDR_Y_ID0, "CIF_REG_DVP_FRM1_ADDR_Y"), + [CIF_REG_DVP_FRM1_ADDR_UV] = CIF_REG_NAME(DVP_FRM1_ADDR_UV_ID0, "CIF_REG_DVP_FRM1_ADDR_UV"), + [CIF_REG_DVP_FRM0_ADDR_Y_ID1] = CIF_REG_NAME(DVP_FRM0_ADDR_Y_ID1, + "CIF_REG_DVP_FRM0_ADDR_Y_ID1"), + [CIF_REG_DVP_FRM0_ADDR_UV_ID1] = CIF_REG_NAME(DVP_FRM0_ADDR_UV_ID1, + "CIF_REG_DVP_FRM0_ADDR_UV_ID1"), + [CIF_REG_DVP_FRM1_ADDR_Y_ID1] = CIF_REG_NAME(DVP_FRM1_ADDR_Y_ID1, + "CIF_REG_DVP_FRM1_ADDR_Y_ID1"), + [CIF_REG_DVP_FRM1_ADDR_UV_ID1] = CIF_REG_NAME(DVP_FRM1_ADDR_UV_ID1, + "CIF_REG_DVP_FRM1_ADDR_UV_ID1"), + [CIF_REG_DVP_FRM0_ADDR_Y_ID2] = CIF_REG_NAME(DVP_FRM0_ADDR_Y_ID2, + "CIF_REG_DVP_FRM0_ADDR_Y_ID2"), + [CIF_REG_DVP_FRM0_ADDR_UV_ID2] = CIF_REG_NAME(DVP_FRM0_ADDR_UV_ID2, + "CIF_REG_DVP_FRM0_ADDR_UV_ID2"), + [CIF_REG_DVP_FRM1_ADDR_Y_ID2] = CIF_REG_NAME(DVP_FRM1_ADDR_Y_ID2, + "CIF_REG_DVP_FRM1_ADDR_Y_ID2"), + [CIF_REG_DVP_FRM1_ADDR_UV_ID2] = CIF_REG_NAME(DVP_FRM1_ADDR_UV_ID2, + "CIF_REG_DVP_FRM1_ADDR_UV_ID2"), + [CIF_REG_DVP_FRM0_ADDR_Y_ID3] = CIF_REG_NAME(DVP_FRM0_ADDR_Y_ID3, + "CIF_REG_DVP_FRM0_ADDR_Y_ID3"), + [CIF_REG_DVP_FRM0_ADDR_UV_ID3] = CIF_REG_NAME(DVP_FRM0_ADDR_UV_ID3, + "CIF_REG_DVP_FRM0_ADDR_UV_ID3"), + [CIF_REG_DVP_FRM1_ADDR_Y_ID3] = CIF_REG_NAME(DVP_FRM1_ADDR_Y_ID3, + "CIF_REG_DVP_FRM1_ADDR_Y_ID3"), + [CIF_REG_DVP_FRM1_ADDR_UV_ID3] = CIF_REG_NAME(DVP_FRM1_ADDR_UV_ID3, + "CIF_REG_DVP_FRM1_ADDR_UV_ID3"), + [CIF_REG_DVP_VIR_LINE_WIDTH] = CIF_REG_NAME(DVP_VIR_LINE_WIDTH, + "CIF_REG_DVP_VIR_LINE_WIDTH"), + [CIF_REG_DVP_SET_SIZE] = CIF_REG_NAME(DVP_CROP_SIZE, "CIF_REG_DVP_SET_SIZE"), + [CIF_REG_DVP_CROP] = CIF_REG_NAME(DVP_CROP, "CIF_REG_DVP_CROP"), + [CIF_REG_DVP_LINE_INT_NUM] = CIF_REG_NAME(DVP_LINE_INT_NUM_01, "CIF_REG_DVP_LINE_INT_NUM"), + [CIF_REG_DVP_LINE_INT_NUM1] = CIF_REG_NAME(DVP_LINE_INT_NUM_23, + "CIF_REG_DVP_LINE_INT_NUM1"), + [CIF_REG_DVP_LINE_CNT] = CIF_REG_NAME(DVP_LINE_INT_NUM_01, "CIF_REG_DVP_LINE_CNT"), + [CIF_REG_DVP_LINE_CNT1] = CIF_REG_NAME(DVP_LINE_INT_NUM_23, "CIF_REG_DVP_LINE_CNT1"), + [CIF_REG_MIPI_LVDS_ID0_CTRL0] = CIF_REG_NAME(CSI_MIPI0_ID0_CTRL0, + "CIF_REG_MIPI_LVDS_ID0_CTRL0"), + [CIF_REG_MIPI_LVDS_ID0_CTRL1] = CIF_REG_NAME(CSI_MIPI0_ID0_CTRL1, + "CIF_REG_MIPI_LVDS_ID0_CTRL1"), + [CIF_REG_MIPI_LVDS_ID1_CTRL0] = CIF_REG_NAME(CSI_MIPI0_ID1_CTRL0, + "CIF_REG_MIPI_LVDS_ID1_CTRL0"), + [CIF_REG_MIPI_LVDS_ID1_CTRL1] = CIF_REG_NAME(CSI_MIPI0_ID1_CTRL1, + "CIF_REG_MIPI_LVDS_ID1_CTRL1"), + [CIF_REG_MIPI_LVDS_ID2_CTRL0] = CIF_REG_NAME(CSI_MIPI0_ID2_CTRL0, + "CIF_REG_MIPI_LVDS_ID2_CTRL0"), + [CIF_REG_MIPI_LVDS_ID2_CTRL1] = CIF_REG_NAME(CSI_MIPI0_ID2_CTRL1, + "CIF_REG_MIPI_LVDS_ID2_CTRL1"), + [CIF_REG_MIPI_LVDS_ID3_CTRL0] = CIF_REG_NAME(CSI_MIPI0_ID3_CTRL0, + "CIF_REG_MIPI_LVDS_ID3_CTRL0"), + [CIF_REG_MIPI_LVDS_ID3_CTRL1] = CIF_REG_NAME(CSI_MIPI0_ID3_CTRL1, + "CIF_REG_MIPI_LVDS_ID3_CTRL1"), + [CIF_REG_MIPI_LVDS_CTRL] = CIF_REG_NAME(CSI_MIPI0_CTRL, "CIF_REG_MIPI_LVDS_CTRL"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID0] = CIF_REG_NAME(CSI_MIPI0_FRM0_ADDR_Y_ID0, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID0"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID0] = CIF_REG_NAME(CSI_MIPI0_FRM1_ADDR_Y_ID0, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID0"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID0] = CIF_REG_NAME(CSI_MIPI0_FRM0_ADDR_UV_ID0, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID0"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID0] = CIF_REG_NAME(CSI_MIPI0_FRM1_ADDR_UV_ID0, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID0"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID0] = CIF_REG_NAME(CSI_MIPI0_VLW_ID0, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID0"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID1] = CIF_REG_NAME(CSI_MIPI0_FRM0_ADDR_Y_ID1, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID1"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID1] = CIF_REG_NAME(CSI_MIPI0_FRM1_ADDR_Y_ID1, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID1"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID1] = CIF_REG_NAME(CSI_MIPI0_FRM0_ADDR_UV_ID1, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID1"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID1] = CIF_REG_NAME(CSI_MIPI0_FRM1_ADDR_UV_ID1, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID1"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID1] = CIF_REG_NAME(CSI_MIPI0_VLW_ID1, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID1"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID2] = CIF_REG_NAME(CSI_MIPI0_FRM0_ADDR_Y_ID2, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID2"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID2] = CIF_REG_NAME(CSI_MIPI0_FRM1_ADDR_Y_ID2, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID2"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID2] = CIF_REG_NAME(CSI_MIPI0_FRM0_ADDR_UV_ID2, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID2"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID2] = CIF_REG_NAME(CSI_MIPI0_FRM1_ADDR_UV_ID2, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID2"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID2] = CIF_REG_NAME(CSI_MIPI0_VLW_ID2, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID2"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID3] = CIF_REG_NAME(CSI_MIPI0_FRM0_ADDR_Y_ID3, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID3"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID3] = CIF_REG_NAME(CSI_MIPI0_FRM1_ADDR_Y_ID3, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID3"), + [CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID3] = CIF_REG_NAME(CSI_MIPI0_FRM0_ADDR_UV_ID3, + "CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID3"), + [CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID3] = CIF_REG_NAME(CSI_MIPI0_FRM1_ADDR_UV_ID3, + "CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID3"), + [CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID3] = CIF_REG_NAME(CSI_MIPI0_VLW_ID3, + "CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID3"), + [CIF_REG_MIPI_LVDS_INTEN] = CIF_REG_NAME(CSI_MIPI0_INTEN, "CIF_REG_MIPI_LVDS_INTEN"), + [CIF_REG_MIPI_LVDS_INTSTAT] = CIF_REG_NAME(CSI_MIPI0_INTSTAT, "CIF_REG_MIPI_LVDS_INTSTAT"), + [CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1] = CIF_REG_NAME(CSI_MIPI0_LINE_INT_NUM_ID0_1, + "CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1"), + [CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3] = CIF_REG_NAME(CSI_MIPI0_LINE_INT_NUM_ID2_3, + "CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3"), + [CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID0_1] = CIF_REG_NAME(CSI_MIPI0_LINE_CNT_ID0_1, + "CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID0_1"), + [CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID2_3] = CIF_REG_NAME(CSI_MIPI0_LINE_CNT_ID2_3, + "CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID2_3"), + [CIF_REG_MIPI_LVDS_ID0_CROP_START] = CIF_REG_NAME(CSI_MIPI0_ID0_CROP_START, + "CIF_REG_MIPI_LVDS_ID0_CROP_START"), + [CIF_REG_MIPI_LVDS_ID1_CROP_START] = CIF_REG_NAME(CSI_MIPI0_ID1_CROP_START, + "CIF_REG_MIPI_LVDS_ID1_CROP_START"), + [CIF_REG_MIPI_LVDS_ID2_CROP_START] = CIF_REG_NAME(CSI_MIPI0_ID2_CROP_START, + "CIF_REG_MIPI_LVDS_ID2_CROP_START"), + [CIF_REG_MIPI_LVDS_ID3_CROP_START] = CIF_REG_NAME(CSI_MIPI0_ID3_CROP_START, + "CIF_REG_MIPI_LVDS_ID3_CROP_START"), + [CIF_REG_MIPI_FRAME_NUM_VC0] = CIF_REG_NAME(CSI_MIPI0_FRAME_NUM_VC0, + "CIF_REG_MIPI_FRAME_NUM_VC0"), + [CIF_REG_MIPI_FRAME_NUM_VC1] = CIF_REG_NAME(CSI_MIPI0_FRAME_NUM_VC1, + "CIF_REG_MIPI_FRAME_NUM_VC1"), + [CIF_REG_MIPI_FRAME_NUM_VC2] = CIF_REG_NAME(CSI_MIPI0_FRAME_NUM_VC2, + "CIF_REG_MIPI_FRAME_NUM_VC2"), + [CIF_REG_MIPI_FRAME_NUM_VC3] = CIF_REG_NAME(CSI_MIPI0_FRAME_NUM_VC3, + "CIF_REG_MIPI_FRAME_NUM_VC3"), + [CIF_REG_MIPI_EFFECT_CODE_ID0] = CIF_REG_NAME(CSI_MIPI0_EFFECT_CODE_ID0, + "CIF_REG_MIPI_EFFECT_CODE_ID0"), + [CIF_REG_MIPI_EFFECT_CODE_ID1] = CIF_REG_NAME(CSI_MIPI0_EFFECT_CODE_ID1, + "CIF_REG_MIPI_EFFECT_CODE_ID1"), + [CIF_REG_MIPI_EFFECT_CODE_ID2] = CIF_REG_NAME(CSI_MIPI0_EFFECT_CODE_ID2, + "CIF_REG_MIPI_EFFECT_CODE_ID2"), + [CIF_REG_MIPI_EFFECT_CODE_ID3] = CIF_REG_NAME(CSI_MIPI0_EFFECT_CODE_ID3, + "CIF_REG_MIPI_EFFECT_CODE_ID3"), + [CIF_REG_MIPI_ON_PAD] = CIF_REG_NAME(CSI_MIPI0_ON_PAD, "CIF_REG_MIPI_ON_PAD"), + [CIF_REG_GLB_CTRL] = CIF_REG_NAME(GLB_CTRL, "CIF_REG_GLB_CTRL"), + [CIF_REG_GLB_INTEN] = CIF_REG_NAME(GLB_INTEN, "CIF_REG_GLB_INTEN"), + [CIF_REG_GLB_INTST] = CIF_REG_NAME(GLB_INTST, "CIF_REG_GLB_INTST"), + [CIF_REG_SCL_CH_CTRL] = CIF_REG_NAME(SCL_CH_CTRL, "CIF_REG_SCL_CH_CTRL"), + [CIF_REG_SCL_CTRL] = CIF_REG_NAME(SCL_CTRL, "CIF_REG_SCL_CTRL"), + [CIF_REG_SCL_FRM0_ADDR_CH0] = CIF_REG_NAME(SCL_FRM0_ADDR_CH0, + "CIF_REG_SCL_FRM0_ADDR_CH0"), + [CIF_REG_SCL_FRM1_ADDR_CH0] = CIF_REG_NAME(SCL_FRM1_ADDR_CH0, + "CIF_REG_SCL_FRM1_ADDR_CH0"), + [CIF_REG_SCL_VLW_CH0] = CIF_REG_NAME(SCL_VLW_CH0, "CIF_REG_SCL_VLW_CH0"), + [CIF_REG_SCL_FRM0_ADDR_CH1] = CIF_REG_NAME(SCL_FRM0_ADDR_CH1, + "CIF_REG_SCL_FRM0_ADDR_CH1"), + [CIF_REG_SCL_FRM1_ADDR_CH1] = CIF_REG_NAME(SCL_FRM1_ADDR_CH1, + "CIF_REG_SCL_FRM1_ADDR_CH1"), + [CIF_REG_SCL_VLW_CH1] = CIF_REG_NAME(SCL_VLW_CH1, "CIF_REG_SCL_VLW_CH1"), + [CIF_REG_SCL_FRM0_ADDR_CH2] = CIF_REG_NAME(SCL_FRM0_ADDR_CH2, + "CIF_REG_SCL_FRM0_ADDR_CH2"), + [CIF_REG_SCL_FRM1_ADDR_CH2] = CIF_REG_NAME(SCL_FRM1_ADDR_CH2, + "CIF_REG_SCL_FRM1_ADDR_CH2"), + [CIF_REG_SCL_VLW_CH2] = CIF_REG_NAME(SCL_VLW_CH2, "CIF_REG_SCL_VLW_CH2"), + [CIF_REG_SCL_FRM0_ADDR_CH3] = CIF_REG_NAME(SCL_FRM0_ADDR_CH3, "CIF_REG_SCL_FRM0_ADDR_CH3"), + [CIF_REG_SCL_FRM1_ADDR_CH3] = CIF_REG_NAME(SCL_FRM1_ADDR_CH3, "CIF_REG_SCL_FRM1_ADDR_CH3"), + [CIF_REG_SCL_VLW_CH3] = CIF_REG_NAME(SCL_VLW_CH3, "CIF_REG_SCL_VLW_CH3"), + [CIF_REG_SCL_BLC_CH0] = CIF_REG_NAME(SCL_BLC_CH0, "CIF_REG_SCL_BLC_CH0"), + [CIF_REG_SCL_BLC_CH1] = CIF_REG_NAME(SCL_BLC_CH1, "CIF_REG_SCL_BLC_CH1"), + [CIF_REG_SCL_BLC_CH2] = CIF_REG_NAME(SCL_BLC_CH2, "CIF_REG_SCL_BLC_CH2"), + [CIF_REG_SCL_BLC_CH3] = CIF_REG_NAME(SCL_BLC_CH3, "CIF_REG_SCL_BLC_CH3"), + [CIF_REG_TOISP0_CTRL] = CIF_REG_NAME(TOISP0_CH_CTRL, "CIF_REG_TOISP0_CTRL"), + [CIF_REG_TOISP0_SIZE] = CIF_REG_NAME(TOISP0_CROP_SIZE, "CIF_REG_TOISP0_SIZE"), + [CIF_REG_TOISP0_CROP] = CIF_REG_NAME(TOISP0_CROP, "CIF_REG_TOISP0_CROP"), + [CIF_REG_TOISP1_CTRL] = CIF_REG_NAME(TOISP1_CH_CTRL, "CIF_REG_TOISP1_CTRL"), + [CIF_REG_TOISP1_SIZE] = CIF_REG_NAME(TOISP1_CROP_SIZE, "CIF_REG_TOISP1_SIZE"), + [CIF_REG_TOISP1_CROP] = CIF_REG_NAME(TOISP1_CROP, "CIF_REG_TOISP1_CROP"), + [CIF_REG_GRF_CIFIO_CON] = CIF_REG_NAME(CIF_GRF_SOC_CON2, "CIF_REG_GRF_CIFIO_CON"), +}; + +//define dphy and csi regs +static const struct grf_reg rk3568_grf_dphy_regs[] = { + [GRF_DPHY_CSI2PHY_FORCERXMODE] = GRF_REG(GRF_VI_CON0, 4, 0), + [GRF_DPHY_CSI2PHY_DATALANE_EN] = GRF_REG(GRF_VI_CON0, 4, 4), + [GRF_DPHY_CSI2PHY_CLKLANE_EN] = GRF_REG(GRF_VI_CON0, 1, 8), + [GRF_DPHY_CLK_INV_SEL] = GRF_REG(GRF_VI_CON0, 1, 9), + [GRF_DPHY_CSI2PHY_CLKLANE1_EN] = GRF_REG(GRF_VI_CON0, 1, 10), + [GRF_DPHY_CLK1_INV_SEL] = GRF_REG(GRF_VI_CON0, 1, 11), + [GRF_DPHY_ISP_CSI2PHY_SEL] = GRF_REG(GRF_VI_CON1, 1, 12), + [GRF_DPHY_CIF_CSI2PHY_SEL] = GRF_REG(GRF_VI_CON1, 1, 11), + [GRF_DPHY_CSI2PHY_LANE_SEL] = GRF_REG(GRF_VI_CON1, 1, 7), +}; + +static const struct csi2dphy_reg rk3568_csi2dphy_regs[] = { + [CSI2PHY_REG_CTRL_LANE_ENABLE] = CSI2PHY_REG(CSI2_DPHY_CTRL_LANE_ENABLE), + [CSI2PHY_DUAL_CLK_EN] = CSI2PHY_REG(CSI2_DPHY_DUAL_CAL_EN), + [CSI2PHY_CLK_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_CLK_WR_THS_SETTLE), + [CSI2PHY_CLK_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_CLK_CALIB_EN), + [CSI2PHY_LANE0_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_LANE0_WR_THS_SETTLE), + [CSI2PHY_LANE0_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_LANE0_CALIB_EN), + [CSI2PHY_LANE1_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_LANE1_WR_THS_SETTLE), + [CSI2PHY_LANE1_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_LANE1_CALIB_EN), + [CSI2PHY_LANE2_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_LANE2_WR_THS_SETTLE), + [CSI2PHY_LANE2_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_LANE2_CALIB_EN), + [CSI2PHY_LANE3_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_LANE3_WR_THS_SETTLE), + [CSI2PHY_LANE3_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_LANE3_CALIB_EN), + [CSI2PHY_CLK1_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_CLK1_WR_THS_SETTLE), + [CSI2PHY_CLK1_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_CLK1_CALIB_EN), +}; + +static const struct grf_reg rk3588_grf_dphy_regs[] = { + [GRF_DPHY_CSI2PHY_FORCERXMODE] = GRF_REG(GRF_DPHY_CON0, 4, 0), + [GRF_DPHY_CSI2PHY_DATALANE_EN] = GRF_REG(GRF_DPHY_CON0, 4, 4), + [GRF_DPHY_CSI2PHY_DATALANE_EN0] = GRF_REG(GRF_DPHY_CON0, 2, 4), + [GRF_DPHY_CSI2PHY_DATALANE_EN1] = GRF_REG(GRF_DPHY_CON0, 2, 6), + [GRF_DPHY_CSI2PHY_CLKLANE_EN] = GRF_REG(GRF_DPHY_CON0, 1, 8), + [GRF_DPHY_CLK_INV_SEL] = GRF_REG(GRF_DPHY_CON0, 1, 9), + [GRF_DPHY_CSI2PHY_CLKLANE1_EN] = GRF_REG(GRF_DPHY_CON0, 1, 10), + [GRF_DPHY_CLK1_INV_SEL] = GRF_REG(GRF_DPHY_CON0, 1, 11), + [GRF_DPHY_CSI2PHY_LANE_SEL] = GRF_REG(GRF_SOC_CON2, 1, 6), + [GRF_DPHY_CSI2PHY1_LANE_SEL] = GRF_REG(GRF_SOC_CON2, 1, 7), + [GRF_DPHY_CSIHOST2_SEL] = GRF_REG(GRF_SOC_CON2, 1, 8), + [GRF_DPHY_CSIHOST3_SEL] = GRF_REG(GRF_SOC_CON2, 1, 9), + [GRF_DPHY_CSIHOST4_SEL] = GRF_REG(GRF_SOC_CON2, 1, 10), + [GRF_DPHY_CSIHOST5_SEL] = GRF_REG(GRF_SOC_CON2, 1, 11), +}; + +static const struct csi2dphy_reg rk3588_csi2dphy_regs[] = { + [CSI2PHY_REG_CTRL_LANE_ENABLE] = CSI2PHY_REG(CSI2_DPHY_CTRL_LANE_ENABLE), + [CSI2PHY_DUAL_CLK_EN] = CSI2PHY_REG(CSI2_DPHY_DUAL_CAL_EN), + [CSI2PHY_CLK_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_CLK_WR_THS_SETTLE), + [CSI2PHY_CLK_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_CLK_CALIB_EN), + [CSI2PHY_LANE0_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_LANE0_WR_THS_SETTLE), + [CSI2PHY_LANE0_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_LANE0_CALIB_EN), + [CSI2PHY_LANE1_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_LANE1_WR_THS_SETTLE), + [CSI2PHY_LANE1_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_LANE1_CALIB_EN), + [CSI2PHY_LANE2_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_LANE2_WR_THS_SETTLE), + [CSI2PHY_LANE2_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_LANE2_CALIB_EN), + [CSI2PHY_LANE3_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_LANE3_WR_THS_SETTLE), + [CSI2PHY_LANE3_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_LANE3_CALIB_EN), + [CSI2PHY_CLK1_THS_SETTLE] = CSI2PHY_REG(CSI2_DPHY_CLK1_WR_THS_SETTLE), + [CSI2PHY_CLK1_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_CLK1_CALIB_EN), + [CSI2PHY_CLK1_LANE_ENABLE] = CSI2PHY_REG(CSI2_DPHY_CLK1_LANE_EN), +}; + +static const struct grf_reg rk3588_grf_dcphy_regs[] = { + [GRF_CPHY_MODE] = GRF_REG(GRF_DCPHY_CON0, 9, 0), +}; + +static const struct csi2dphy_reg rk3588_csi2dcphy_regs[] = { + [CSI2PHY_CLK_THS_SETTLE] = CSI2PHY_REG(CSI2_DCPHY_CLK_WR_THS_SETTLE), + [CSI2PHY_LANE0_THS_SETTLE] = CSI2PHY_REG(CSI2_DCPHY_LANE0_WR_THS_SETTLE), + [CSI2PHY_LANE0_ERR_SOT_SYNC] = CSI2PHY_REG(CSI2_DCPHY_LANE0_WR_ERR_SOT_SYNC), + [CSI2PHY_LANE1_THS_SETTLE] = CSI2PHY_REG(CSI2_DCPHY_LANE1_WR_THS_SETTLE), + [CSI2PHY_LANE1_ERR_SOT_SYNC] = CSI2PHY_REG(CSI2_DCPHY_LANE1_WR_ERR_SOT_SYNC), + [CSI2PHY_LANE2_THS_SETTLE] = CSI2PHY_REG(CSI2_DCPHY_LANE2_WR_THS_SETTLE), + [CSI2PHY_LANE2_ERR_SOT_SYNC] = CSI2PHY_REG(CSI2_DCPHY_LANE2_WR_ERR_SOT_SYNC), + [CSI2PHY_LANE3_THS_SETTLE] = CSI2PHY_REG(CSI2_DCPHY_LANE3_WR_THS_SETTLE), + [CSI2PHY_LANE3_ERR_SOT_SYNC] = CSI2PHY_REG(CSI2_DCPHY_LANE3_WR_ERR_SOT_SYNC), + [CSI2PHY_CLK_LANE_ENABLE] = CSI2PHY_REG(CSI2_DCPHY_CLK_LANE_ENABLE), + [CSI2PHY_DATA_LANE0_ENABLE] = CSI2PHY_REG(CSI2_DCPHY_DATA_LANE0_ENABLE), + [CSI2PHY_DATA_LANE1_ENABLE] = CSI2PHY_REG(CSI2_DCPHY_DATA_LANE1_ENABLE), + [CSI2PHY_DATA_LANE2_ENABLE] = CSI2PHY_REG(CSI2_DCPHY_DATA_LANE2_ENABLE), + [CSI2PHY_DATA_LANE3_ENABLE] = CSI2PHY_REG(CSI2_DCPHY_DATA_LANE3_ENABLE), + [CSI2PHY_S0C_GNR_CON1] = CSI2PHY_REG(CSI2_DCPHY_S0C_GNR_CON1), + [CSI2PHY_S0C_ANA_CON1] = CSI2PHY_REG(CSI2_DCPHY_S0C_ANA_CON1), + [CSI2PHY_S0C_ANA_CON2] = CSI2PHY_REG(CSI2_DCPHY_S0C_ANA_CON2), + [CSI2PHY_S0C_ANA_CON3] = CSI2PHY_REG(CSI2_DCPHY_S0C_ANA_CON3), + [CSI2PHY_COMBO_S0D0_GNR_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_GNR_CON1), + [CSI2PHY_COMBO_S0D0_ANA_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_ANA_CON1), + [CSI2PHY_COMBO_S0D0_ANA_CON2] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_ANA_CON2), + [CSI2PHY_COMBO_S0D0_ANA_CON3] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_ANA_CON3), + [CSI2PHY_COMBO_S0D0_ANA_CON6] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_ANA_CON6), + [CSI2PHY_COMBO_S0D0_ANA_CON7] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_ANA_CON7), + [CSI2PHY_COMBO_S0D0_DESKEW_CON0] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_DESKEW_CON0), + [CSI2PHY_COMBO_S0D0_DESKEW_CON2] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_DESKEW_CON2), + [CSI2PHY_COMBO_S0D0_DESKEW_CON4] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_DESKEW_CON4), + [CSI2PHY_COMBO_S0D0_CRC_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_CRC_CON1), + [CSI2PHY_COMBO_S0D0_CRC_CON2] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_CRC_CON2), + [CSI2PHY_COMBO_S0D1_GNR_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_GNR_CON1), + [CSI2PHY_COMBO_S0D1_ANA_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_ANA_CON1), + [CSI2PHY_COMBO_S0D1_ANA_CON2] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_ANA_CON2), + [CSI2PHY_COMBO_S0D1_ANA_CON3] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_ANA_CON3), + [CSI2PHY_COMBO_S0D1_ANA_CON6] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_ANA_CON6), + [CSI2PHY_COMBO_S0D1_ANA_CON7] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_ANA_CON7), + [CSI2PHY_COMBO_S0D1_DESKEW_CON0] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_DESKEW_CON0), + [CSI2PHY_COMBO_S0D1_DESKEW_CON2] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_DESKEW_CON2), + [CSI2PHY_COMBO_S0D1_DESKEW_CON4] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_DESKEW_CON4), + [CSI2PHY_COMBO_S0D1_CRC_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_CRC_CON1), + [CSI2PHY_COMBO_S0D1_CRC_CON2] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_CRC_CON2), + [CSI2PHY_COMBO_S0D2_GNR_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_GNR_CON1), + [CSI2PHY_COMBO_S0D2_ANA_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_ANA_CON1), + [CSI2PHY_COMBO_S0D2_ANA_CON2] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_ANA_CON2), + [CSI2PHY_COMBO_S0D2_ANA_CON3] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_ANA_CON3), + [CSI2PHY_COMBO_S0D2_ANA_CON6] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_ANA_CON6), + [CSI2PHY_COMBO_S0D2_ANA_CON7] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_ANA_CON7), + [CSI2PHY_COMBO_S0D2_DESKEW_CON0] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_DESKEW_CON0), + [CSI2PHY_COMBO_S0D2_DESKEW_CON2] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_DESKEW_CON2), + [CSI2PHY_COMBO_S0D2_DESKEW_CON4] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_DESKEW_CON4), + [CSI2PHY_COMBO_S0D2_CRC_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_CRC_CON1), + [CSI2PHY_COMBO_S0D2_CRC_CON2] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_CRC_CON2), + [CSI2PHY_S0D3_GNR_CON1] = CSI2PHY_REG(CSI2_DCPHY_S0D3_GNR_CON1), + [CSI2PHY_S0D3_ANA_CON1] = CSI2PHY_REG(CSI2_DCPHY_S0D3_ANA_CON1), + [CSI2PHY_S0D3_ANA_CON2] = CSI2PHY_REG(CSI2_DCPHY_S0D3_ANA_CON2), + [CSI2PHY_S0D3_ANA_CON3] = CSI2PHY_REG(CSI2_DCPHY_S0D3_ANA_CON3), + [CSI2PHY_S0D3_DESKEW_CON0] = CSI2PHY_REG(CSI2_DCPHY_S0D3_DESKEW_CON0), + [CSI2PHY_S0D3_DESKEW_CON2] = CSI2PHY_REG(CSI2_DCPHY_S0D3_DESKEW_CON2), + [CSI2PHY_S0D3_DESKEW_CON4] = CSI2PHY_REG(CSI2_DCPHY_S0D3_DESKEW_CON4), +}; + +//define dcphy params +static struct rkmodule_csi_dphy_param rk3588_dcphy_param = { + .vendor = PHY_VENDOR_SAMSUNG, + .lp_vol_ref = 3, + .lp_hys_sw = {3, 0, 0, 0}, + .lp_escclk_pol_sel = {1, 0, 0, 0}, + .skew_data_cal_clk = {0, 3, 3, 3}, + .clk_hs_term_sel = 2, + .data_hs_term_sel = {2, 2, 2, 2}, + .reserved = {0}, +}; + +/* These tables must be sorted by .range_h ascending. */ +static const struct hsfreq_range rk3568_csi2_dphy_hw_hsfreq_ranges[] = { + { 109, 0x02}, { 149, 0x03}, { 199, 0x06}, { 249, 0x06}, + { 299, 0x06}, { 399, 0x08}, { 499, 0x0b}, { 599, 0x0e}, + { 699, 0x10}, { 799, 0x12}, { 999, 0x16}, {1199, 0x1e}, + {1399, 0x23}, {1599, 0x2d}, {1799, 0x32}, {1999, 0x37}, + {2199, 0x3c}, {2399, 0x41}, {2499, 0x46} +}; + +/* These tables must be sorted by .range_h ascending. */ +static const struct hsfreq_range rk3588_csi2_dcphy_d_hw_hsfreq_ranges[] = { + { 80, 0x105}, { 100, 0x106}, { 120, 0x107}, { 140, 0x108}, + { 160, 0x109}, { 180, 0x10a}, { 200, 0x10b}, { 220, 0x10c}, + { 240, 0x10d}, { 270, 0x10e}, { 290, 0x10f}, { 310, 0x110}, + { 330, 0x111}, { 350, 0x112}, { 370, 0x113}, { 390, 0x114}, + { 410, 0x115}, { 430, 0x116}, { 450, 0x117}, { 470, 0x118}, + { 490, 0x119}, { 510, 0x11a}, { 540, 0x11b}, { 560, 0x11c}, + { 580, 0x11d}, { 600, 0x11e}, { 620, 0x11f}, { 640, 0x120}, + { 660, 0x121}, { 680, 0x122}, { 700, 0x123}, { 720, 0x124}, + { 740, 0x125}, { 760, 0x126}, { 790, 0x127}, { 810, 0x128}, + { 830, 0x129}, { 850, 0x12a}, { 870, 0x12b}, { 890, 0x12c}, + { 910, 0x12d}, { 930, 0x12e}, { 950, 0x12f}, { 970, 0x130}, + { 990, 0x131}, {1010, 0x132}, {1030, 0x133}, {1060, 0x134}, + {1080, 0x135}, {1100, 0x136}, {1120, 0x137}, {1140, 0x138}, + {1160, 0x139}, {1180, 0x13a}, {1200, 0x13b}, {1220, 0x13c}, + {1240, 0x13d}, {1260, 0x13e}, {1280, 0x13f}, {1310, 0x140}, + {1330, 0x141}, {1350, 0x142}, {1370, 0x143}, {1390, 0x144}, + {1410, 0x145}, {1430, 0x146}, {1450, 0x147}, {1470, 0x148}, + {1490, 0x149}, {1580, 0x007}, {1740, 0x008}, {1910, 0x009}, + {2070, 0x00a}, {2240, 0x00b}, {2410, 0x00c}, {2570, 0x00d}, + {2740, 0x00e}, {2910, 0x00f}, {3070, 0x010}, {3240, 0x011}, + {3410, 0x012}, {3570, 0x013}, {3740, 0x014}, {3890, 0x015}, + {4070, 0x016}, {4240, 0x017}, {4400, 0x018}, {4500, 0x019}, +}; + +static struct csi2_dphy_hw rk3568_csi2_dphy_hw = { + .dphy_clks = rk3568_csi2_dphy_hw_clks, + .num_dphy_clks = ARRAY_SIZE(rk3568_csi2_dphy_hw_clks), + .csi2_clks = rk3568_csi2_clks, + .num_csi2_clks = ARRAY_SIZE(rk3568_csi2_clks), + .csi2_rsts = rk3568_csi2_rsts, + .num_csi2_rsts = ARRAY_SIZE(rk3568_csi2_rsts), + .hsfreq_ranges = rk3568_csi2_dphy_hw_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk3568_csi2_dphy_hw_hsfreq_ranges), + .csi2dphy_regs = rk3568_csi2dphy_regs, + .grf_regs = rk3568_grf_dphy_regs, + .chip_id = CHIP_ID_RK3568, +}; + +static struct csi2_dphy_hw rk3588_csi2_dphy_hw = { + .dphy_clks = rk3588_csi2_dphy_hw_clks, + .num_dphy_clks = ARRAY_SIZE(rk3588_csi2_dphy_hw_clks), + .dphy_rsts = rk3588_csi2_dphy_hw_rsts, + .num_dphy_rsts = ARRAY_SIZE(rk3588_csi2_dphy_hw_rsts), + .csi2_clks = rk3588_csi2_clks, + .num_csi2_clks = ARRAY_SIZE(rk3588_csi2_clks), + .csi2_rsts = rk3588_csi2_rsts, + .num_csi2_rsts = ARRAY_SIZE(rk3588_csi2_rsts), + .hsfreq_ranges = rk3568_csi2_dphy_hw_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk3568_csi2_dphy_hw_hsfreq_ranges), + .csi2dphy_regs = rk3588_csi2dphy_regs, + .grf_regs = rk3588_grf_dphy_regs, + .chip_id = CHIP_ID_RK3588, +}; + +static struct csi2_dphy_hw rk3588_csi2_dcphy_hw = { + .dphy_clks = rk3588_csi2_dphy_hw_clks, + .num_dphy_clks = ARRAY_SIZE(rk3588_csi2_dphy_hw_clks), + .csi2_clks = rk3588_csi2_dcphy_clks, + .num_csi2_clks = ARRAY_SIZE(rk3588_csi2_dcphy_clks), + .csi2_rsts = rk3588_csi2_rsts, + .num_csi2_rsts = ARRAY_SIZE(rk3588_csi2_rsts), + .hsfreq_ranges = rk3588_csi2_dcphy_d_hw_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk3588_csi2_dcphy_d_hw_hsfreq_ranges), + .csi2dphy_regs = rk3588_csi2dcphy_regs, + .grf_regs = rk3588_grf_dcphy_regs, + .chip_id = CHIP_ID_RK3588_DCPHY, +}; + +static const struct cif_input_fmt in_fmts[] = { + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YUYV, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, + .csi_yuv_order = CSI_YUV_INPUT_ORDER_YUYV, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YUYV, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, + .csi_yuv_order = CSI_YUV_INPUT_ORDER_YUYV, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, { + .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YVYU, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, + .csi_yuv_order = CSI_YUV_INPUT_ORDER_YVYU, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YVYU, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, + .csi_yuv_order = CSI_YUV_INPUT_ORDER_YVYU, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, { + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_UYVY, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, + .csi_yuv_order = CSI_YUV_INPUT_ORDER_UYVY, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_UYVY, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, + .csi_yuv_order = CSI_YUV_INPUT_ORDER_UYVY, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, { + .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_VYUY, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, + .csi_yuv_order = CSI_YUV_INPUT_ORDER_VYUY, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_VYUY, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, + .csi_yuv_order = CSI_YUV_INPUT_ORDER_VYUY, + .fmt_type = CIF_FMT_TYPE_YUV, + .field = V4L2_FIELD_INTERLACED, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .csi_fmt_val = CSI_WRDDR_TYPE_RGB888, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_Y10_1X10, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + }, { + .mbus_code = MEDIA_BUS_FMT_Y12_1X12, + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, + .fmt_type = CIF_FMT_TYPE_RAW, + .field = V4L2_FIELD_NONE, + } +}; + +static const struct cif_output_fmt out_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .cplanes = 2, + .mplanes = 1, + .fmt_val = YUV_OUTPUT_422 | UV_STORAGE_ORDER_UVUV, + .bpp = { 8, 16 }, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, + .fmt_type = CIF_FMT_TYPE_YUV, + }, { + .fourcc = V4L2_PIX_FMT_NV61, + .fmt_val = YUV_OUTPUT_422 | UV_STORAGE_ORDER_VUVU, + .cplanes = 2, + .mplanes = 1, + .bpp = { 8, 16 }, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422, + .fmt_type = CIF_FMT_TYPE_YUV, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .fmt_val = YUV_OUTPUT_420 | UV_STORAGE_ORDER_UVUV, + .cplanes = 2, + .mplanes = 1, + .bpp = { 8, 16 }, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV420SP, + .fmt_type = CIF_FMT_TYPE_YUV, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .fmt_val = YUV_OUTPUT_420 | UV_STORAGE_ORDER_VUVU, + .cplanes = 2, + .mplanes = 1, + .bpp = { 8, 16 }, + .csi_fmt_val = CSI_WRDDR_TYPE_YUV420SP, + .fmt_type = CIF_FMT_TYPE_YUV, + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + .cplanes = 2, + .mplanes = 1, + .bpp = { 8, 16 }, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_YUV, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .cplanes = 2, + .mplanes = 1, + .bpp = { 8, 16 }, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_YUV, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .cplanes = 2, + .mplanes = 1, + .bpp = { 8, 16 }, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_YUV, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .cplanes = 2, + .mplanes = 1, + .bpp = { 8, 16 }, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_YUV, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, + .cplanes = 1, + .mplanes = 1, + .bpp = { 24 }, + .csi_fmt_val = CSI_WRDDR_TYPE_RGB888, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_BGR666, + .cplanes = 1, + .mplanes = 1, + .bpp = { 18 }, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .cplanes = 1, + .mplanes = 1, + .bpp = { 8 }, + .raw_bpp = 8, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .cplanes = 1, + .mplanes = 1, + .bpp = { 8 }, + .raw_bpp = 8, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .cplanes = 1, + .mplanes = 1, + .bpp = { 8 }, + .raw_bpp = 8, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .cplanes = 1, + .mplanes = 1, + .bpp = { 8 }, + .raw_bpp = 8, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 10, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 10, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 10, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 10, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 12, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 12, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 12, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 12, + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .raw_bpp = 16, + .fmt_type = CIF_FMT_TYPE_RAW, + }, { + .fourcc = V4L2_PIX_FMT_Y16, + .cplanes = 1, + .mplanes = 1, + .bpp = { 16 }, + .fmt_type = CIF_FMT_TYPE_RAW, + } + + /* TODO: We can support NV12M/NV21M/NV16M/NV61M too */ +}; + +static void rkcif_write_reg(struct vehicle_cif *cif, + enum cif_reg_index index, u32 val) +{ + void __iomem *base = cif->base; + const struct vehicle_cif_reg *reg = &cif->cif_regs[index]; + int csi_offset = 0; + + if (cif->inf_id == RKCIF_MIPI_LVDS && + cif->chip_id == CHIP_RK3588_VEHICLE_CIF && + index >= CIF_REG_MIPI_LVDS_ID0_CTRL0 && + index <= CIF_REG_MIPI_ON_PAD) + csi_offset = cif->csi_host_idx * 0x100; + + if (index < CIF_REG_INDEX_MAX) { + if (index == CIF_REG_DVP_CTRL || + ((index != CIF_REG_DVP_CTRL) && (reg->offset != 0x0))) { + write_reg(base, reg->offset + csi_offset, val); + } else { + VEHICLE_INFO("write index(%d) reg[%s]: 0x%x failed, maybe useless!!!\n", + index, reg->name, val); + } + } + VEHICLE_DG("@%s register[%s] offset(0x%x) csi_offset(0x%x) value:0x%x !\n", + __func__, reg->name, reg->offset, csi_offset, val); +} + +static void rkcif_write_reg_or(struct vehicle_cif *cif, + enum cif_reg_index index, u32 val) +{ + void __iomem *base = cif->base; + const struct vehicle_cif_reg *reg = &cif->cif_regs[index]; + unsigned int reg_val = 0x0; + int csi_offset = 0; + + if (cif->inf_id == RKCIF_MIPI_LVDS && + cif->chip_id == CHIP_RK3588_VEHICLE_CIF && + index >= CIF_REG_MIPI_LVDS_ID0_CTRL0 && + index <= CIF_REG_MIPI_ON_PAD) + csi_offset = cif->csi_host_idx * 0x100; + + if (index < CIF_REG_INDEX_MAX) { + if (index == CIF_REG_DVP_CTRL || + ((index != CIF_REG_DVP_CTRL) && (reg->offset != 0x0))) { + reg_val = read_reg(base, reg->offset + csi_offset); + reg_val |= val; + write_reg(base, reg->offset + csi_offset, reg_val); + } else { + VEHICLE_INFO("write index(%d) reg[%s]: 0x%x failed, maybe useless!!!\n", + index, reg->name, val); + } + } + VEHICLE_DG("@%s register[%s] offset(0x%x) csi_offset(0x%x) value:0x%x !\n", + __func__, reg->name, reg->offset, csi_offset, reg_val); +} + +static void rkcif_write_reg_and(struct vehicle_cif *cif, + enum cif_reg_index index, u32 val) +{ + void __iomem *base = cif->base; + const struct vehicle_cif_reg *reg = &cif->cif_regs[index]; + unsigned int reg_val = 0x0; + int csi_offset = 0; + + if (cif->inf_id == RKCIF_MIPI_LVDS && + cif->chip_id == CHIP_RK3588_VEHICLE_CIF && + index >= CIF_REG_MIPI_LVDS_ID0_CTRL0 && + index <= CIF_REG_MIPI_ON_PAD) + csi_offset = cif->csi_host_idx * 0x100; + + if (index < CIF_REG_INDEX_MAX) { + if (index == CIF_REG_DVP_CTRL || + ((index != CIF_REG_DVP_CTRL) && (reg->offset != 0x0))) { + reg_val = read_reg(base, reg->offset + csi_offset); + reg_val &= val; + write_reg(base, reg->offset + csi_offset, reg_val); + } else { + VEHICLE_INFO("write index(%d) reg[%s]: 0x%x failed, maybe useless!!!\n", + index, reg->name, val); + } + } + VEHICLE_DG("@%s register[%s] offset(0x%x) csi_offset(0x%x) value:0x%x !\n", + __func__, reg->name, reg->offset, csi_offset, reg_val); +} + +static unsigned int rkcif_read_reg(struct vehicle_cif *cif, + enum cif_reg_index index) +{ + unsigned int val = 0x0; + void __iomem *base = cif->base; + const struct vehicle_cif_reg *reg = &cif->cif_regs[index]; + int csi_offset = 0; + + if (cif->inf_id == RKCIF_MIPI_LVDS && + cif->chip_id == CHIP_RK3588_VEHICLE_CIF && + index >= CIF_REG_MIPI_LVDS_ID0_CTRL0 && + index <= CIF_REG_MIPI_ON_PAD) + csi_offset = cif->csi_host_idx * 0x100; + + if (index < CIF_REG_INDEX_MAX) { + if (index == CIF_REG_DVP_CTRL || + ((index != CIF_REG_DVP_CTRL) && (reg->offset != 0x0))) + val = read_reg(base, reg->offset + csi_offset); + else + VEHICLE_INFO("read index(%d) reg[%s]: 0x%x failed, maybe useless!!!\n", + index, reg->name, val); + } + VEHICLE_DG("@%s register[%s] offset(0x%x) csi_offset(0x%x) value:0x%x !\n", + __func__, reg->name, reg->offset, csi_offset, val); + return val; +} + +static void rkvehicle_cif_write_grf_reg(struct vehicle_cif *cif, + enum cif_reg_index index, u32 val) +{ + const struct vehicle_cif_reg *reg = &cif->cif_regs[index]; + + if (index < CIF_REG_INDEX_MAX) { + if (index > CIF_REG_DVP_CTRL) { + if (!IS_ERR(cif->regmap_grf)) + regmap_write(cif->regmap_grf, reg->offset, val); + } else { + VEHICLE_INFO("write index(%d) reg[%s]: 0x%x failed, maybe useless!!!\n", + index, reg->name, val); + } + VEHICLE_DG("@%s reg[%s] offset(0x%x): 0x%x !\n", + __func__, reg->name, reg->offset, val); + } +} + +static u32 rkvehicle_cif_read_grf_reg(struct vehicle_cif *cif, + enum cif_reg_index index) +{ + const struct vehicle_cif_reg *reg = &cif->cif_regs[index]; + u32 val = 0xffff; + + if (index < CIF_REG_INDEX_MAX) { + if (index > CIF_REG_DVP_CTRL) { + if (!IS_ERR(cif->regmap_grf)) + regmap_read(cif->regmap_grf, reg->offset, &val); + } else { + VEHICLE_INFO("read index(%d) reg[%s]: 0x%x failed, maybe useless!!!\n", + index, reg->name, val); + } + VEHICLE_DG("@%s reg[%s] offset(0x%x): 0x%x !\n", + __func__, reg->name, reg->offset, val); + } + + return val; +} + +static inline void write_csi2_dphy_reg(struct csi2_dphy_hw *hw, + int index, u32 value) +{ + const struct csi2dphy_reg *reg = &hw->csi2dphy_regs[index]; + + if ((index == CSI2PHY_REG_CTRL_LANE_ENABLE) || + (index == CSI2PHY_CLK_LANE_ENABLE) || + ((index != CSI2PHY_REG_CTRL_LANE_ENABLE) && + (reg->offset != 0x0))) + writel(value, hw->csi2_dphy_base + reg->offset); + + VEHICLE_DG("@%s offset(0x%x) reg val: 0x%x !\n", + __func__, reg->offset, value); +} + +static inline void write_csi2_dphy_reg_mask(struct csi2_dphy_hw *hw, + int index, u32 value, u32 mask) +{ + const struct csi2dphy_reg *reg = &hw->csi2dphy_regs[index]; + u32 read_val = 0; + + read_val = readl(hw->csi2_dphy_base + reg->offset); + read_val &= ~mask; + read_val |= value; + writel(read_val, hw->csi2_dphy_base + reg->offset); +} + +static inline void read_csi2_dphy_reg(struct csi2_dphy_hw *hw, + int index, u32 *value) +{ + const struct csi2dphy_reg *reg = &hw->csi2dphy_regs[index]; + + if ((index == CSI2PHY_REG_CTRL_LANE_ENABLE) || + (index == CSI2PHY_CLK_LANE_ENABLE) || + ((index != CSI2PHY_REG_CTRL_LANE_ENABLE) && + (reg->offset != 0x0))) + *value = readl(hw->csi2_dphy_base + reg->offset); + + VEHICLE_DG("@%s offset(0x%x) reg val: 0x%x !\n", + __func__, reg->offset, *value); +} + +static void csi_mipidphy_wr_ths_settle(struct csi2_dphy_hw *hw, + int hsfreq, + enum csi2_dphy_lane lane) +{ + unsigned int val = 0; + unsigned int offset; + + switch (lane) { + case CSI2_DPHY_LANE_CLOCK: + offset = CSI2PHY_CLK_THS_SETTLE; + break; + case CSI2_DPHY_LANE_CLOCK1: + offset = CSI2PHY_CLK1_THS_SETTLE; + break; + case CSI2_DPHY_LANE_DATA0: + offset = CSI2PHY_LANE0_THS_SETTLE; + break; + case CSI2_DPHY_LANE_DATA1: + offset = CSI2PHY_LANE1_THS_SETTLE; + break; + case CSI2_DPHY_LANE_DATA2: + offset = CSI2PHY_LANE2_THS_SETTLE; + break; + case CSI2_DPHY_LANE_DATA3: + offset = CSI2PHY_LANE3_THS_SETTLE; + break; + default: + return; + } + + read_csi2_dphy_reg(hw, offset, &val); + val = (val & ~0x7f) | hsfreq; + write_csi2_dphy_reg(hw, offset, val); +} + +static void rkvehicle_cif_cfg_dvp_clk_sampling_edge(struct vehicle_cif *cif, + enum rkcif_clk_edge edge) +{ + u32 val = 0x0; + + if (!IS_ERR(cif->regmap_grf)) { + if (cif->chip_id == CHIP_RK3568_VEHICLE_CIF) { + if (edge == RKCIF_CLK_RISING) + val = RK3568_CIF_PCLK_SAMPLING_EDGE_RISING; + else + val = RK3568_CIF_PCLK_SAMPLING_EDGE_FALLING; + } + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) { + if (edge == RKCIF_CLK_RISING) + val = RK3588_CIF_PCLK_SAMPLING_EDGE_RISING; + else + val = RK3588_CIF_PCLK_SAMPLING_EDGE_FALLING; + } + rkvehicle_cif_write_grf_reg(cif, CIF_REG_GRF_CIFIO_CON, val); + } +} + +static int rkcif_dvp_get_input_yuv_order(struct vehicle_cfg *cfg) +{ + unsigned int mask; + + switch (cfg->mbus_code) { + case MEDIA_BUS_FMT_UYVY8_2X8: + mask = CSI_YUV_INPUT_ORDER_UYVY >> 11; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + mask = CSI_YUV_INPUT_ORDER_VYUY >> 11; + break; + case MEDIA_BUS_FMT_YUYV8_2X8: + mask = CSI_YUV_INPUT_ORDER_YUYV >> 11; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + mask = CSI_YUV_INPUT_ORDER_YVYU >> 11; + break; + default: + mask = CSI_YUV_INPUT_ORDER_UYVY >> 11; + break; + } + return mask; +} + +static int cif_stream_setup(struct vehicle_cif *cif) +{ + struct vehicle_cfg *cfg = &cif->cif_cfg; + u32 val, mbus_flags, + xfer_mode = 0, yc_swap = 0, + inputmode = 0, mipimode = 0, + input_format = 0, output_format = 0, crop = 0, + out_fmt_mask = 0, + multi_id_en = BT656_1120_MULTI_ID_DISABLE, + multi_id_mode = BT656_1120_MULTI_ID_MODE_1, + multi_id_sel = BT656_1120_MULTI_ID_SEL_LSB, + bt1120_edge_mode = BT1120_CLOCK_SINGLE_EDGES; + u32 sav_detect = BT656_DETECT_SAV; + u32 in_fmt_yuv_order = 0; + + mbus_flags = cfg->mbus_flags; + /* set dvp clk sample edge */ + if (mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + rkvehicle_cif_cfg_dvp_clk_sampling_edge(cif, RKCIF_CLK_RISING); + else + rkvehicle_cif_cfg_dvp_clk_sampling_edge(cif, RKCIF_CLK_FALLING); + + inputmode = cfg->input_format<<2; //INPUT_MODE_YUV or INPUT_MODE_BT656_YUV422 + //YUV_INPUT_ORDER_UYVY, MEDIA_BUS_FMT_UYVY8_2X8, CCIR_INPUT_ORDER_ODD + input_format = (cfg->yuv_order<<5) | YUV_INPUT_422 | (cfg->field_order<<9); + if (cfg->output_format == CIF_OUTPUT_FORMAT_420) + output_format = YUV_OUTPUT_420 | UV_STORAGE_ORDER_UVUV; + else + output_format = YUV_OUTPUT_422 | UV_STORAGE_ORDER_UVUV; + + if (cif->chip_id == CHIP_RK3568_VEHICLE_CIF) { + val = cfg->vsync | (cfg->href<<1) | inputmode | mipimode + | input_format | output_format + | xfer_mode | yc_swap | multi_id_en + | multi_id_sel | multi_id_mode | bt1120_edge_mode; + } else { + out_fmt_mask = (CSI_WRDDR_TYPE_YUV420SP_RK3588 << 11) | + (CSI_YUV_OUTPUT_ORDER_UYVY << 1); + in_fmt_yuv_order = rkcif_dvp_get_input_yuv_order(cfg); + val = cfg->vsync | (cfg->href<<1) | inputmode + | in_fmt_yuv_order | out_fmt_mask + | yc_swap | multi_id_en | multi_id_sel + | sav_detect | multi_id_mode | bt1120_edge_mode; + } + + rkcif_write_reg(cif, CIF_REG_DVP_FOR, val); + + rkcif_write_reg(cif, CIF_REG_DVP_VIR_LINE_WIDTH, cfg->width); + rkcif_write_reg(cif, CIF_REG_DVP_SET_SIZE, + cfg->width | (cfg->height << 16)); + + crop = (cfg->start_x | (cfg->start_y<<16)); + rkcif_write_reg(cif, CIF_REG_DVP_CROP, crop); + + rkcif_write_reg(cif, CIF_REG_DVP_FRAME_STATUS, FRAME_STAT_CLS); + + if (cif->chip_id < CHIP_RK3588_VEHICLE_CIF) { + rkcif_write_reg(cif, CIF_REG_DVP_INTSTAT, INTSTAT_CLS); + rkcif_write_reg(cif, CIF_REG_DVP_SCL_CTRL, ENABLE_YUV_16BIT_BYPASS); + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, + FRAME_END_EN | INTSTAT_ERR | + PST_INF_FRAME_END); + /* enable line int for sof */ + rkcif_write_reg(cif, CIF_REG_DVP_LINE_INT_NUM, 0x1); + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, LINE_INT_EN); + } else { + rkcif_write_reg(cif, CIF_REG_DVP_INTSTAT, 0x3c3ffff); + rkcif_write_reg_or(cif, CIF_REG_DVP_INTEN, 0x033ffff);//0x3c3ffff + } + + cif->interlaced_enable = false; + + return 0; +} + +static inline void csi2_dphy_write_sys_grf_reg(struct csi2_dphy_hw *hw, + int index, u8 value) +{ + const struct grf_reg *reg = &hw->grf_regs[index]; + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift); + + if (reg->shift) + regmap_write(hw->regmap_sys_grf, reg->offset, val); +} + +static inline void csi2_dphy_write_grf_reg(struct csi2_dphy_hw *hw, + int index, u8 value) +{ + const struct grf_reg *reg = &hw->grf_regs[index]; + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift); + + if (reg->shift) + regmap_write(hw->regmap_grf, reg->offset, val); +} + +static inline u32 csi2_dphy_read_grf_reg(struct csi2_dphy_hw *hw, int index) +{ + const struct grf_reg *reg = &hw->grf_regs[index]; + unsigned int val = 0; + + if (reg->shift) { + regmap_read(hw->regmap_grf, reg->offset, &val); + val = (val >> reg->shift) & reg->mask; + } + + return val; +} + +static void csi2_dphy_config_dual_mode(struct vehicle_cif *cif) +{ + struct csi2_dphy_hw *hw = cif->dphy_hw; + u32 val; + u32 phy_index = 0; //dphy0/dphy3 + + val = ~GRF_CSI2PHY_LANE_SEL_SPLIT; + if (phy_index < 3) { + csi2_dphy_write_grf_reg(hw, GRF_DPHY_CSI2PHY_DATALANE_EN, + GENMASK(cif->cif_cfg.lanes - 1, 0)); + csi2_dphy_write_grf_reg(hw, GRF_DPHY_CSI2PHY_CLKLANE_EN, 0x1); + if (cif->chip_id < CHIP_RK3588_VEHICLE_CIF) + csi2_dphy_write_grf_reg(hw, GRF_DPHY_CSI2PHY_LANE_SEL, val); + else + csi2_dphy_write_sys_grf_reg(hw, GRF_DPHY_CSI2PHY_LANE_SEL, val); + } else { + csi2_dphy_write_grf_reg(hw, GRF_DPHY_CSI2PHY_DATALANE_EN, + GENMASK(cif->cif_cfg.lanes - 1, 0)); + csi2_dphy_write_grf_reg(hw, GRF_DPHY_CSI2PHY_CLKLANE_EN, 0x1); + if (cif->chip_id < CHIP_RK3588_VEHICLE_CIF) + csi2_dphy_write_grf_reg(hw, GRF_DPHY_CSI2PHY1_LANE_SEL, val); + else + csi2_dphy_write_sys_grf_reg(hw, GRF_DPHY_CSI2PHY1_LANE_SEL, val); + } +} + +static int vehicle_csi2_dphy_stream_start(struct vehicle_cif *cif) +{ + struct csi2_dphy_hw *hw = cif->dphy_hw; + const struct hsfreq_range *hsfreq_ranges = hw->hsfreq_ranges; + int num_hsfreq_ranges = hw->num_hsfreq_ranges; + int i, hsfreq = 0; + u32 val = 0, pre_val; + + + mutex_lock(&hw->mutex); + + /* set data lane num and enable clock lane */ + /* + * for rk356x: dphy0 is used just for full mode, + * dphy1 is used just for split mode,uses lane0_1, + * dphy2 is used just for split mode,uses lane2_3 + */ + read_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, &pre_val); + val |= (GENMASK(cif->cif_cfg.lanes - 1, 0) << + CSI2_DPHY_CTRL_DATALANE_ENABLE_OFFSET_BIT) | + (0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT); + + val |= pre_val; + write_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, val); + + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + write_csi2_dphy_reg(hw, CSI2PHY_DUAL_CLK_EN, 0x1e); + write_csi2_dphy_reg(hw, CSI2PHY_DUAL_CLK_EN, 0x1f); + csi2_dphy_config_dual_mode(cif); + } + + /* not into receive mode/wait stopstate */ + csi2_dphy_write_grf_reg(hw, GRF_DPHY_CSI2PHY_FORCERXMODE, 0x0); + + /* enable calibration */ + if (hw->data_rate_mbps > 1500) { + write_csi2_dphy_reg(hw, CSI2PHY_CLK_CALIB_ENABLE, 0x80); + if (cif->cif_cfg.lanes > 0x00) + write_csi2_dphy_reg(hw, CSI2PHY_LANE0_CALIB_ENABLE, 0x80); + if (cif->cif_cfg.lanes > 0x01) + write_csi2_dphy_reg(hw, CSI2PHY_LANE1_CALIB_ENABLE, 0x80); + if (cif->cif_cfg.lanes > 0x02) + write_csi2_dphy_reg(hw, CSI2PHY_LANE2_CALIB_ENABLE, 0x80); + if (cif->cif_cfg.lanes > 0x03) + write_csi2_dphy_reg(hw, CSI2PHY_LANE3_CALIB_ENABLE, 0x80); + } + + /* set clock lane and data lane */ + for (i = 0; i < num_hsfreq_ranges; i++) { + if (hsfreq_ranges[i].range_h >= hw->data_rate_mbps) { + hsfreq = hsfreq_ranges[i].cfg_bit; + break; + } + } + + if (i == num_hsfreq_ranges) { + i = num_hsfreq_ranges - 1; + dev_warn(hw->dev, "data rate: %lld mbps, max support %d mbps", + hw->data_rate_mbps, hsfreq_ranges[i].range_h + 1); + hsfreq = hsfreq_ranges[i].cfg_bit; + } + + VEHICLE_DG("mipi data_rate_mbps %lld, matched bit(0x%0x), lanes(%d)\n", + hw->data_rate_mbps, hsfreq, cif->cif_cfg.lanes); + + csi_mipidphy_wr_ths_settle(hw, hsfreq, CSI2_DPHY_LANE_CLOCK); + if (cif->cif_cfg.lanes > 0x00) + csi_mipidphy_wr_ths_settle(hw, hsfreq, CSI2_DPHY_LANE_DATA0); + if (cif->cif_cfg.lanes > 0x01) + csi_mipidphy_wr_ths_settle(hw, hsfreq, CSI2_DPHY_LANE_DATA1); + if (cif->cif_cfg.lanes > 0x02) + csi_mipidphy_wr_ths_settle(hw, hsfreq, CSI2_DPHY_LANE_DATA2); + if (cif->cif_cfg.lanes > 0x03) + csi_mipidphy_wr_ths_settle(hw, hsfreq, CSI2_DPHY_LANE_DATA3); + + atomic_inc(&hw->stream_cnt); + + mutex_unlock(&hw->mutex); + + return 0; +} + +static void vehicle_samsung_dcphy_rx_config_settle(struct vehicle_cif *cif) +{ + struct csi2_dphy_hw *hw = cif->dphy_hw; + struct samsung_mipi_dcphy *samsung = hw->samsung_phy; + const struct hsfreq_range *hsfreq_ranges = NULL; + int num_hsfreq_ranges = 0; + int i, hsfreq = 0; + u32 sot_sync = 0; + + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + hsfreq_ranges = hw->hsfreq_ranges; + num_hsfreq_ranges = hw->num_hsfreq_ranges; + sot_sync = 0x03; + } + + /* set data lane */ + for (i = 0; i < num_hsfreq_ranges; i++) { + if (hsfreq_ranges[i].range_h >= hw->data_rate_mbps) { + hsfreq = hsfreq_ranges[i].cfg_bit; + break; + } + } + + /*clk settle fix to 0x301*/ + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) + regmap_write(samsung->regmap, RX_CLK_THS_SETTLE, 0x301); + + if (cif->cif_cfg.lanes > 0x00) { + regmap_update_bits(samsung->regmap, RX_LANE0_THS_SETTLE, 0x1ff, hsfreq); + regmap_update_bits(samsung->regmap, RX_LANE0_ERR_SOT_SYNC, 0xff, sot_sync); + } + if (cif->cif_cfg.lanes > 0x01) { + regmap_update_bits(samsung->regmap, RX_LANE1_THS_SETTLE, 0x1ff, hsfreq); + regmap_update_bits(samsung->regmap, RX_LANE1_ERR_SOT_SYNC, 0xff, sot_sync); + } + if (cif->cif_cfg.lanes > 0x02) { + regmap_update_bits(samsung->regmap, RX_LANE2_THS_SETTLE, 0x1ff, hsfreq); + regmap_update_bits(samsung->regmap, RX_LANE2_ERR_SOT_SYNC, 0xff, sot_sync); + } + if (cif->cif_cfg.lanes > 0x03) { + regmap_update_bits(samsung->regmap, RX_LANE3_THS_SETTLE, 0x1ff, hsfreq); + regmap_update_bits(samsung->regmap, RX_LANE3_ERR_SOT_SYNC, 0xff, sot_sync); + } +} + +static int vehicle_samsung_dcphy_rx_config_common(struct vehicle_cif *cif) +{ + struct csi2_dphy_hw *hw = cif->dphy_hw; + struct samsung_mipi_dcphy *samsung = hw->samsung_phy; + u32 dlysel = 0; + int i = 0; + + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + if (hw->data_rate_mbps < 1500) + dlysel = 0; + else if (hw->data_rate_mbps < 2000) + dlysel = 3 << 8; + else if (hw->data_rate_mbps < 3000) + dlysel = 2 << 8; + else if (hw->data_rate_mbps < 4000) + dlysel = 1 << 8; + else if (hw->data_rate_mbps < 6500) + dlysel = 0; + if (hw->dphy_param->clk_hs_term_sel > 0x7) { + dev_err(hw->dev, "clk_hs_term_sel error param %d\n", + hw->dphy_param->clk_hs_term_sel); + return -EINVAL; + } + for (i = 0; i < cif->cif_cfg.lanes; i++) { + if (hw->dphy_param->data_hs_term_sel[i] > 0x7) { + dev_err(hw->dev, "data_hs_term_sel[%d] error param %d\n", + i, + hw->dphy_param->data_hs_term_sel[i]); + return -EINVAL; + } + if (hw->dphy_param->lp_hys_sw[i] > 0x3) { + dev_err(hw->dev, "lp_hys_sw[%d] error param %d\n", + i, + hw->dphy_param->lp_hys_sw[i]); + return -EINVAL; + } + if (hw->dphy_param->lp_escclk_pol_sel[i] > 0x1) { + dev_err(hw->dev, "lp_escclk_pol_sel[%d] error param %d\n", + i, + hw->dphy_param->lp_escclk_pol_sel[i]); + return -EINVAL; + } + if (hw->dphy_param->skew_data_cal_clk[i] > 0x1f) { + dev_err(hw->dev, "skew_data_cal_clk[%d] error param %d\n", + i, + hw->dphy_param->skew_data_cal_clk[i]); + return -EINVAL; + } + } + regmap_write(samsung->regmap, RX_S0C_GNR_CON1, 0x1450); + regmap_write(samsung->regmap, RX_S0C_ANA_CON1, 0x8000); + regmap_write(samsung->regmap, RX_S0C_ANA_CON2, hw->dphy_param->clk_hs_term_sel); + regmap_write(samsung->regmap, RX_S0C_ANA_CON3, 0x0600); + if (cif->cif_cfg.lanes > 0x00) { + regmap_write(samsung->regmap, RX_COMBO_S0D0_GNR_CON1, 0x1450); + regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON1, 0x8000); + regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON2, dlysel | + hw->dphy_param->data_hs_term_sel[0]); + regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON3, 0x0600 | + (hw->dphy_param->lp_hys_sw[0] << 4) | + (hw->dphy_param->lp_escclk_pol_sel[0] << 11)); + regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON7, 0x40); + regmap_write(samsung->regmap, RX_COMBO_S0D0_DESKEW_CON2, + hw->dphy_param->skew_data_cal_clk[0]); + } + if (cif->cif_cfg.lanes > 0x01) { + regmap_write(samsung->regmap, RX_COMBO_S0D1_GNR_CON1, 0x1450); + regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON1, 0x8000); + regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON2, dlysel | + hw->dphy_param->data_hs_term_sel[1]); + regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON3, 0x0600 | + (hw->dphy_param->lp_hys_sw[1] << 4) | + (hw->dphy_param->lp_escclk_pol_sel[1] << 11)); + regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON7, 0x40); + regmap_write(samsung->regmap, RX_COMBO_S0D1_DESKEW_CON2, + hw->dphy_param->skew_data_cal_clk[1]); + } + if (cif->cif_cfg.lanes > 0x02) { + regmap_write(samsung->regmap, RX_COMBO_S0D2_GNR_CON1, 0x1450); + regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON1, 0x8000); + regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON2, dlysel | + hw->dphy_param->data_hs_term_sel[2]); + regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON3, 0x0600 | + (hw->dphy_param->lp_hys_sw[2] << 4) | + (hw->dphy_param->lp_escclk_pol_sel[2] << 11)); + regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON7, 0x40); + regmap_write(samsung->regmap, RX_COMBO_S0D2_DESKEW_CON2, + hw->dphy_param->skew_data_cal_clk[2]); + } + if (cif->cif_cfg.lanes > 0x03) { + regmap_write(samsung->regmap, RX_S0D3_GNR_CON1, 0x1450); + regmap_write(samsung->regmap, RX_S0D3_ANA_CON1, 0x8000); + regmap_write(samsung->regmap, RX_S0D3_ANA_CON2, dlysel | + hw->dphy_param->data_hs_term_sel[3]); + regmap_write(samsung->regmap, RX_S0D3_ANA_CON3, 0x0600 | + (hw->dphy_param->lp_hys_sw[3] << 4) | + (hw->dphy_param->lp_escclk_pol_sel[3] << 11)); + regmap_write(samsung->regmap, RX_S0D3_DESKEW_CON2, + hw->dphy_param->skew_data_cal_clk[3]); + } + } + + return 0; +} + +static int vehicle_samsung_dcphy_rx_lane_enable(struct vehicle_cif *cif) +{ + struct csi2_dphy_hw *hw = cif->dphy_hw; + struct samsung_mipi_dcphy *samsung = hw->samsung_phy; + u32 sts; + int ret = 0; + + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) + regmap_update_bits(samsung->regmap, RX_CLK_LANE_ENABLE, PHY_ENABLE, PHY_ENABLE); + + if (cif->cif_cfg.lanes > 0x00) + regmap_update_bits(samsung->regmap, RX_DATA_LANE0_ENABLE, PHY_ENABLE, PHY_ENABLE); + if (cif->cif_cfg.lanes > 0x01) + regmap_update_bits(samsung->regmap, RX_DATA_LANE1_ENABLE, PHY_ENABLE, PHY_ENABLE); + if (cif->cif_cfg.lanes > 0x02) + regmap_update_bits(samsung->regmap, RX_DATA_LANE2_ENABLE, PHY_ENABLE, PHY_ENABLE); + if (cif->cif_cfg.lanes > 0x03) + regmap_update_bits(samsung->regmap, RX_DATA_LANE3_ENABLE, PHY_ENABLE, PHY_ENABLE); + + /*wait for clk lane ready*/ + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + ret = regmap_read_poll_timeout(samsung->regmap, RX_CLK_LANE_ENABLE, + sts, (sts & PHY_READY), 200, 4000); + if (ret < 0) { + dev_err(samsung->dev, "phy rx clk lane is not locked\n"); + return -EINVAL; + } + } + + /*wait for data lane ready*/ + if (cif->cif_cfg.lanes > 0x00) { + ret = regmap_read_poll_timeout(samsung->regmap, RX_DATA_LANE0_ENABLE, + sts, (sts & PHY_READY), 200, 2000); + if (ret < 0) { + dev_err(samsung->dev, "phy rx data lane 0 is not locked\n"); + return -EINVAL; + } + } + if (cif->cif_cfg.lanes > 0x01) { + ret = regmap_read_poll_timeout(samsung->regmap, RX_DATA_LANE1_ENABLE, + sts, (sts & PHY_READY), 200, 2000); + if (ret < 0) { + dev_err(samsung->dev, "phy rx data lane 1 is not locked\n"); + return -EINVAL; + } + } + if (cif->cif_cfg.lanes > 0x02) { + ret = regmap_read_poll_timeout(samsung->regmap, RX_DATA_LANE2_ENABLE, + sts, (sts & PHY_READY), 200, 2000); + if (ret < 0) { + dev_err(samsung->dev, "phy rx data lane 2 is not locked\n"); + return -EINVAL; + } + } + + if (cif->cif_cfg.lanes > 0x03) { + ret = regmap_read_poll_timeout(samsung->regmap, RX_DATA_LANE3_ENABLE, + sts, (sts & PHY_READY), 200, 2000); + if (ret < 0) { + dev_err(samsung->dev, "phy rx data lane 3 is not locked\n"); + return -EINVAL; + } + } + return 0; +} + +static void vehicle_samsung_mipi_dcphy_bias_block_enable(struct vehicle_cif *cif) +{ + struct csi2_dphy_hw *hw = cif->dphy_hw; + struct samsung_mipi_dcphy *samsung = hw->samsung_phy; + struct csi2_dphy_hw *csi_dphy = samsung->dphy_vehicle[0]; + u32 bias_con2 = 0x3223; + + if (csi_dphy && + csi_dphy->dphy_param->lp_vol_ref != 3 && + csi_dphy->dphy_param->lp_vol_ref < 0x7) { + bias_con2 &= 0xfffffff8; + bias_con2 |= csi_dphy->dphy_param->lp_vol_ref; + dev_info(samsung->dev, + "rx change lp_vol_ref to %d, it may cause tx exception\n", + csi_dphy->dphy_param->lp_vol_ref); + } + regmap_write(samsung->regmap, BIAS_CON0, 0x0010); + regmap_write(samsung->regmap, BIAS_CON1, 0x0110); + regmap_write(samsung->regmap, BIAS_CON2, bias_con2); + + /* default output voltage select: + * dphy: 400mv + * cphy: 530mv + */ + if (samsung->c_option) + regmap_update_bits(samsung->regmap, BIAS_CON4, + I_MUX_SEL_MASK, I_MUX_SEL(2)); +} + +static int vehicle_csi2_dcphy_stream_start(struct vehicle_cif *cif) +{ + struct csi2_dphy_hw *hw = cif->dphy_hw; + struct samsung_mipi_dcphy *samsung = hw->samsung_phy; + int ret = 0; + + dev_info(hw->dev, "mipi dcphy stream on\n"); + mutex_lock(&hw->mutex); + + if (samsung->s_phy_rst) + reset_control_assert(samsung->s_phy_rst); + + vehicle_samsung_mipi_dcphy_bias_block_enable(cif); + ret = vehicle_samsung_dcphy_rx_config_common(cif); + if (ret) + goto out_streamon; + + vehicle_samsung_dcphy_rx_config_settle(cif); + + ret = vehicle_samsung_dcphy_rx_lane_enable(cif); + if (ret) + goto out_streamon; + + if (samsung->s_phy_rst) + reset_control_deassert(samsung->s_phy_rst); + atomic_inc(&hw->stream_cnt); + mutex_unlock(&hw->mutex); + + return 0; + +out_streamon: + if (samsung->s_phy_rst) + reset_control_deassert(samsung->s_phy_rst); + mutex_unlock(&hw->mutex); + dev_err(hw->dev, "stream on error\n"); + return -EINVAL; +} + +static int vehicle_csi2_dcphy_stream_stop(struct vehicle_cif *cif) +{ + struct csi2_dphy_hw *hw = cif->dphy_hw; + struct samsung_mipi_dcphy *samsung = hw->samsung_phy; + + dev_info(hw->dev, "mipi dcphy stream off\n"); + if (atomic_dec_return(&hw->stream_cnt)) + return 0; + + mutex_lock(&hw->mutex); + + if (samsung->s_phy_rst) + reset_control_assert(samsung->s_phy_rst); + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) + regmap_update_bits(samsung->regmap, RX_CLK_LANE_ENABLE, PHY_ENABLE, 0); + + if (cif->cif_cfg.lanes > 0x00) + regmap_update_bits(samsung->regmap, RX_DATA_LANE0_ENABLE, PHY_ENABLE, 0); + if (cif->cif_cfg.lanes > 0x01) + regmap_update_bits(samsung->regmap, RX_DATA_LANE1_ENABLE, PHY_ENABLE, 0); + if (cif->cif_cfg.lanes > 0x02) + regmap_update_bits(samsung->regmap, RX_DATA_LANE2_ENABLE, PHY_ENABLE, 0); + if (cif->cif_cfg.lanes > 0x03) + regmap_update_bits(samsung->regmap, RX_DATA_LANE3_ENABLE, PHY_ENABLE, 0); + + if (samsung->s_phy_rst) + reset_control_deassert(samsung->s_phy_rst); + usleep_range(500, 1000); + + mutex_unlock(&hw->mutex); + + return 0; +} + +static void vehicle_csi2_disable(struct vehicle_cif *cif) +{ + void __iomem *base = cif->csi2_base; + + vehicle_write_csihost_reg(base, CSIHOST_RESETN, 0); + vehicle_write_csihost_reg(base, CSIHOST_MSK1, 0xffffffff); + vehicle_write_csihost_reg(base, CSIHOST_MSK2, 0xffffffff); +} + +static void vehicle_csi2_enable(struct vehicle_cif *cif, + enum host_type_t host_type) +{ + struct csi2_dphy_hw *hw = cif->dphy_hw; + void __iomem *base = hw->csi2_base; + int lanes = cif->cif_cfg.lanes; + + vehicle_write_csihost_reg(base, CSIHOST_N_LANES, lanes - 1); + + if (host_type == RK_DSI_RXHOST) { + vehicle_write_csihost_reg(base, CSIHOST_CONTROL, + SW_CPHY_EN(0) | SW_DSI_EN(1) | + SW_DATATYPE_FS(0x01) | SW_DATATYPE_FE(0x11) | + SW_DATATYPE_LS(0x21) | SW_DATATYPE_LE(0x31)); + /* Disable some error interrupt when HOST work on DSI RX mode */ + vehicle_write_csihost_reg(base, CSIHOST_MSK1, 0xe00000f0); + vehicle_write_csihost_reg(base, CSIHOST_MSK2, 0xff00); + } else { + vehicle_write_csihost_reg(base, CSIHOST_CONTROL, + SW_CPHY_EN(0) | SW_DSI_EN(0) | + SW_DATATYPE_FS(0x0) | SW_DATATYPE_FE(0x01) | + SW_DATATYPE_LS(0x02) | SW_DATATYPE_LE(0x03)); + vehicle_write_csihost_reg(base, CSIHOST_MSK1, 0); + vehicle_write_csihost_reg(base, CSIHOST_MSK2, 0xf000); + } + + vehicle_write_csihost_reg(base, CSIHOST_RESETN, 1); +} + +static int vehicle_csi2_stream_start(struct vehicle_cif *cif) +{ + struct csi2_dphy_hw *hw = cif->dphy_hw; + enum host_type_t host_type; + int i; + + host_type = RK_CSI_RXHOST; + vehicle_csi2_enable(cif, host_type); + for (i = 0; i < RK_CSI2_ERR_MAX; i++) + hw->err_list[i].cnt = 0; + + return 0; +} + +static void vehicle_cif_csi_get_vc_num(struct vehicle_cif *cif) +{ + int vc_num = 0; + unsigned int mbus_flags = cif->cif_cfg.mbus_flags; + + for (vc_num = 0; vc_num < RKCIF_MAX_CSI_CHANNEL; vc_num++) { + if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_0) { + cif->channels[vc_num].vc = vc_num; + mbus_flags ^= V4L2_MBUS_CSI2_CHANNEL_0; + continue; + } + if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_1) { + cif->channels[vc_num].vc = vc_num; + mbus_flags ^= V4L2_MBUS_CSI2_CHANNEL_1; + continue; + } + + if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_2) { + cif->channels[vc_num].vc = vc_num; + mbus_flags ^= V4L2_MBUS_CSI2_CHANNEL_2; + continue; + } + + if (mbus_flags & V4L2_MBUS_CSI2_CHANNEL_3) { + cif->channels[vc_num].vc = vc_num; + mbus_flags ^= V4L2_MBUS_CSI2_CHANNEL_3; + continue; + } + } + + cif->num_channels = vc_num ? (vc_num - 1) : 1; + if (cif->num_channels == 1) + cif->channels[0].vc = 0; +} + +static const struct +cif_input_fmt *find_input_fmt(u32 mbus_code) +{ + const struct cif_input_fmt *fmt; + u32 i; + + for (i = 0; i < ARRAY_SIZE(in_fmts); i++) { + fmt = &in_fmts[i]; + if (mbus_code == fmt->mbus_code) + return fmt; + } + + return NULL; +} + +static const struct +cif_output_fmt *find_output_fmt(u32 pixelfmt) +{ + const struct cif_output_fmt *fmt; + u32 i; + + for (i = 0; i < ARRAY_SIZE(out_fmts); i++) { + fmt = &out_fmts[i]; + if (fmt->fourcc == pixelfmt) + return fmt; + } + + return NULL; +} + +static enum cif_reg_index get_reg_index_of_id_ctrl0(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_MIPI_LVDS_ID0_CTRL0; + break; + case 1: + index = CIF_REG_MIPI_LVDS_ID1_CTRL0; + break; + case 2: + index = CIF_REG_MIPI_LVDS_ID2_CTRL0; + break; + case 3: + index = CIF_REG_MIPI_LVDS_ID3_CTRL0; + break; + default: + index = CIF_REG_MIPI_LVDS_ID0_CTRL0; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_id_ctrl1(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_MIPI_LVDS_ID0_CTRL1; + break; + case 1: + index = CIF_REG_MIPI_LVDS_ID1_CTRL1; + break; + case 2: + index = CIF_REG_MIPI_LVDS_ID2_CTRL1; + break; + case 3: + index = CIF_REG_MIPI_LVDS_ID3_CTRL1; + break; + default: + index = CIF_REG_MIPI_LVDS_ID0_CTRL1; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_frm0_y_addr(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID0; + break; + case 1: + index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID1; + break; + case 2: + index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID2; + break; + case 3: + index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID3; + break; + default: + index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID0; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_frm1_y_addr(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID0; + break; + case 1: + index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID1; + break; + case 2: + index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID2; + break; + case 3: + index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID3; + break; + default: + index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID0; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_frm0_uv_addr(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID0; + break; + case 1: + index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID1; + break; + case 2: + index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID2; + break; + case 3: + index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID3; + break; + default: + index = CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID0; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_frm1_uv_addr(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID0; + break; + case 1: + index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID1; + break; + case 2: + index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID2; + break; + case 3: + index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID3; + break; + default: + index = CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID0; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_frm0_y_vlw(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID0; + break; + case 1: + index = CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID1; + break; + case 2: + index = CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID2; + break; + case 3: + index = CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID3; + break; + default: + index = CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID0; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_frm1_y_vlw(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID0; + break; + case 1: + index = CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID1; + break; + case 2: + index = CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID2; + break; + case 3: + index = CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID3; + break; + default: + index = CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID0; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_frm0_uv_vlw(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID0; + break; + case 1: + index = CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID1; + break; + case 2: + index = CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID2; + break; + case 3: + index = CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID3; + break; + default: + index = CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID0; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_frm1_uv_vlw(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID0; + break; + case 1: + index = CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID1; + break; + case 2: + index = CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID2; + break; + case 3: + index = CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID3; + break; + default: + index = CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID0; + break; + } + + return index; +} + +static enum cif_reg_index get_reg_index_of_id_crop_start(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_MIPI_LVDS_ID0_CROP_START; + break; + case 1: + index = CIF_REG_MIPI_LVDS_ID1_CROP_START; + break; + case 2: + index = CIF_REG_MIPI_LVDS_ID2_CROP_START; + break; + case 3: + index = CIF_REG_MIPI_LVDS_ID3_CROP_START; + break; + default: + index = CIF_REG_MIPI_LVDS_ID0_CROP_START; + break; + } + + return index; +} + +static enum cif_reg_index get_dvp_reg_index_of_frm0_y_addr(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_DVP_FRM0_ADDR_Y; + break; + case 1: + index = CIF_REG_DVP_FRM0_ADDR_Y_ID1; + break; + case 2: + index = CIF_REG_DVP_FRM0_ADDR_Y_ID2; + break; + case 3: + index = CIF_REG_DVP_FRM0_ADDR_Y_ID3; + break; + default: + index = CIF_REG_DVP_FRM0_ADDR_Y; + break; + } + + return index; +} + +static enum cif_reg_index get_dvp_reg_index_of_frm1_y_addr(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_DVP_FRM1_ADDR_Y; + break; + case 1: + index = CIF_REG_DVP_FRM1_ADDR_Y_ID1; + break; + case 2: + index = CIF_REG_DVP_FRM1_ADDR_Y_ID2; + break; + case 3: + index = CIF_REG_DVP_FRM1_ADDR_Y_ID3; + break; + default: + index = CIF_REG_DVP_FRM0_ADDR_Y; + break; + } + + return index; +} + +static enum cif_reg_index get_dvp_reg_index_of_frm0_uv_addr(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_DVP_FRM0_ADDR_UV; + break; + case 1: + index = CIF_REG_DVP_FRM0_ADDR_UV_ID1; + break; + case 2: + index = CIF_REG_DVP_FRM0_ADDR_UV_ID2; + break; + case 3: + index = CIF_REG_DVP_FRM0_ADDR_UV_ID3; + break; + default: + index = CIF_REG_DVP_FRM0_ADDR_UV; + break; + } + + return index; +} + +static enum cif_reg_index get_dvp_reg_index_of_frm1_uv_addr(int channel_id) +{ + enum cif_reg_index index; + + switch (channel_id) { + case 0: + index = CIF_REG_DVP_FRM1_ADDR_UV; + break; + case 1: + index = CIF_REG_DVP_FRM1_ADDR_UV_ID1; + break; + case 2: + index = CIF_REG_DVP_FRM1_ADDR_UV_ID2; + break; + case 3: + index = CIF_REG_DVP_FRM1_ADDR_UV_ID3; + break; + default: + index = CIF_REG_DVP_FRM1_ADDR_UV; + break; + } + + return index; +} + +static unsigned char get_data_type(u32 pixelformat, u8 cmd_mode_en) +{ + switch (pixelformat) { + /* csi raw8 */ + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + return 0x2a; + /* csi raw10 */ + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + return 0x2b; + /* csi raw12 */ + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + return 0x2c; + /* csi uyvy 422 */ + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_VYUY8_2X8: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: + return 0x1e; + case MEDIA_BUS_FMT_RGB888_1X24: { + if (cmd_mode_en) /* dsi command mode*/ + return 0x39; + else /* dsi video mode */ + return 0x3e; + } + + default: + return 0x2b; + } +} + +#define UV_OFFSET (cif->cif_cfg.width * cif->cif_cfg.height) + +static int vehicle_cif_init_buffer(struct vehicle_cif *cif, + int init, int csi_ch) +{ + struct vehicle_rkcif_dummy_buffer *dummy_buf = &cif->dummy_buf; + u32 frm0_addr_y, frm0_addr_uv; + u32 frm1_addr_y, frm1_addr_uv; + unsigned long y_addr, uv_addr; + int i; + + if (cif->cif_cfg.buf_num < 2) + return -EINVAL; + + if (cif->cif_cfg.buf_num > MAX_BUF_NUM) + cif->cif_cfg.buf_num = MAX_BUF_NUM; + + for (i = 0 ; i < cif->cif_cfg.buf_num; i++) { + cif->frame_buf[i] = cif->cif_cfg.buf_phy_addr[i]; + if (cif->frame_buf[i] == 0) + return -EINVAL; + } + + cif->last_buf_index = 0; + cif->current_buf_index = 1; + + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + frm0_addr_y = get_reg_index_of_frm0_y_addr(csi_ch); + frm0_addr_uv = get_reg_index_of_frm0_uv_addr(csi_ch); + frm1_addr_y = get_reg_index_of_frm1_y_addr(csi_ch); + frm1_addr_uv = get_reg_index_of_frm1_uv_addr(csi_ch); + } else { + frm0_addr_y = get_dvp_reg_index_of_frm0_y_addr(csi_ch); + frm0_addr_uv = get_dvp_reg_index_of_frm0_uv_addr(csi_ch); + frm1_addr_y = get_dvp_reg_index_of_frm1_y_addr(csi_ch); + frm1_addr_uv = get_dvp_reg_index_of_frm1_uv_addr(csi_ch); + } + + spin_lock(&cif->vbq_lock); + + y_addr = vehicle_flinger_request_cif_buffer(); + if (y_addr) { + uv_addr = y_addr + UV_OFFSET; + rkcif_write_reg(cif, frm0_addr_y, y_addr); + rkcif_write_reg(cif, frm0_addr_uv, uv_addr); + cif->active[0] = y_addr; + } else { + rkcif_write_reg(cif, frm0_addr_y, dummy_buf->dma_addr); + rkcif_write_reg(cif, frm0_addr_uv, dummy_buf->dma_addr); + cif->active[0] = y_addr; + } + + y_addr = vehicle_flinger_request_cif_buffer(); + if (y_addr) { + uv_addr = y_addr + UV_OFFSET; + rkcif_write_reg(cif, frm1_addr_y, y_addr); + rkcif_write_reg(cif, frm1_addr_uv, uv_addr); + cif->active[1] = y_addr; + } else { + rkcif_write_reg(cif, frm1_addr_y, dummy_buf->dma_addr); + rkcif_write_reg(cif, frm1_addr_uv, dummy_buf->dma_addr); + cif->active[1] = y_addr; + } + + if (cif->cif_cfg.type != V4L2_MBUS_CSI2_DPHY) { + int ch_id; + + for (ch_id = 0; ch_id < 4; ch_id++) { + if (ch_id == csi_ch) + continue; + + rkcif_write_reg(cif, get_dvp_reg_index_of_frm0_y_addr(ch_id), + dummy_buf->dma_addr); + rkcif_write_reg(cif, get_dvp_reg_index_of_frm1_y_addr(ch_id), + dummy_buf->dma_addr); + rkcif_write_reg(cif, get_dvp_reg_index_of_frm0_uv_addr(ch_id), + dummy_buf->dma_addr); + rkcif_write_reg(cif, get_dvp_reg_index_of_frm1_uv_addr(ch_id), + dummy_buf->dma_addr); + } + } + + spin_unlock(&cif->vbq_lock); + + return 0; +} + +static int vehicle_cif_csi_channel_init(struct vehicle_cif *cif, + struct vehicle_csi_channel_info *channel) +{ + struct vehicle_cfg *cfg = &cif->cif_cfg; + const struct cif_output_fmt *fmt; + u32 fourcc; + + channel->enable = 1; + channel->width = cfg->width; + channel->height = cfg->height; + cif->interlaced_enable = false; + channel->cmd_mode_en = 0; /* default use DSI Video Mode */ + + channel->crop_en = 1; + channel->crop_st_x = cfg->start_x; + channel->crop_st_y = cfg->start_y; + channel->width = cfg->width; + channel->height = cfg->height; + if (cfg->output_format == CIF_OUTPUT_FORMAT_420) { + fmt = find_output_fmt(V4L2_PIX_FMT_NV12); + if (!fmt) { + VEHICLE_DGERR("can not find output format: 0x%x", V4L2_PIX_FMT_NV12); + return -EINVAL; + } + } else { + fmt = find_output_fmt(V4L2_PIX_FMT_NV16); + if (!fmt) { + VEHICLE_DGERR("can not find output format: 0x%x", V4L2_PIX_FMT_NV16); + return -EINVAL; + } + } + // channel->fmt_val = fmt->csi_fmt_val; + /* set cif input format yuv422*/ + channel->fmt_val = CSI_WRDDR_TYPE_YUV422; + VEHICLE_INFO("%s, LINE=%d, channel->fmt_val = 0x%x, fmt->csi_fmt_val= 0x%x", + __func__, __LINE__, channel->fmt_val, fmt->csi_fmt_val); + /* + * for mipi or lvds, when enable compact, the virtual width of raw10/raw12 + * needs aligned with :ALIGN(bits_per_pixel * width / 8, 8), if enable 16bit mode + * needs aligned with :ALIGN(bits_per_pixel * width * 2, 8), to optimize reading and + * writing of ddr, aligned with 256 + */ + + if (fmt->fmt_type == CIF_FMT_TYPE_RAW && channel->fmt_val != CSI_WRDDR_TYPE_RAW8) + channel->virtual_width = ALIGN(channel->width * 2, 8); + else + channel->virtual_width = ALIGN(channel->width * fmt->bpp[0] / 8, 8); + + if (channel->fmt_val == CSI_WRDDR_TYPE_RGB888) + channel->width = channel->width * fmt->bpp[0] / 8; + /* + * rk cif don't support output yuyv fmt data + * if user request yuyv fmt, the input mode must be RAW8 + * and the width is double Because the real input fmt is + * yuyv + */ + fourcc = fmt->fourcc; + if (fourcc == V4L2_PIX_FMT_YUYV || fourcc == V4L2_PIX_FMT_YVYU || + fourcc == V4L2_PIX_FMT_UYVY || fourcc == V4L2_PIX_FMT_VYUY) { + channel->fmt_val = CSI_WRDDR_TYPE_RAW8; + channel->width *= 2; + channel->virtual_width *= 2; + } + VEHICLE_DG("%s, LINE=%d, channel->fmt_val = 0x%x", __func__, __LINE__, channel->fmt_val); + if (cfg->input_format == CIF_INPUT_FORMAT_PAL || + cfg->input_format == CIF_INPUT_FORMAT_NTSC) { + VEHICLE_DG("CVBS IN PAL or NTSC config."); + channel->virtual_width *= 2; + cif->interlaced_enable = true; + cif->interlaced_offset = channel->width; + cif->interlaced_counts = 0; + cif->interlaced_buffer = 0; + channel->height /= 2; + VEHICLE_DG("do denterlaced.\n"); + } + + channel->data_type = get_data_type(cfg->mbus_code, + channel->cmd_mode_en); + + return 0; +} + +static int vehicle_cif_csi_channel_set(struct vehicle_cif *cif, + struct vehicle_csi_channel_info *channel, + enum v4l2_mbus_type mbus_type) + +{ + unsigned int val = 0x0; + + if (channel->id >= 4) + return -EINVAL; + + if (!channel->enable) { + rkcif_write_reg(cif, get_reg_index_of_id_ctrl0(channel->id), + CSI_DISABLE_CAPTURE); + return 0; + } + + rkcif_write_reg_and(cif, CIF_REG_MIPI_LVDS_INTSTAT, + ~(CSI_START_INTSTAT(channel->id) | + CSI_DMA_END_INTSTAT(channel->id) | + CSI_LINE_INTSTAT(channel->id))); + + /* 0. need set CIF_CSI_INTEN to 0x0 first */ + rkcif_write_reg_and(cif, CIF_REG_MIPI_LVDS_INTEN, 0x0); + + /* enable id0 frame start int for sof(long frame, for hdr) + * vehicle don't need this + */ + if (channel->id == RKCIF_STREAM_MIPI_ID0) + rkcif_write_reg_or(cif, CIF_REG_MIPI_LVDS_INTEN, + CSI_START_INTEN(channel->id)); + + rkcif_write_reg(cif, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1, + 0x3fff << 16 | 0x3fff); + rkcif_write_reg(cif, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3, + 0x3fff << 16 | 0x3fff); + + rkcif_write_reg_or(cif, CIF_REG_MIPI_LVDS_INTEN, + CSI_DMA_END_INTEN(channel->id)); + + rkcif_write_reg(cif, CIF_REG_MIPI_WATER_LINE, + CIF_MIPI_LVDS_SW_WATER_LINE_25_RK1808 | + CIF_MIPI_LVDS_SW_WATER_LINE_ENABLE_RK1808 | + CIF_MIPI_LVDS_SW_HURRY_VALUE_RK1808(0x3) | + CIF_MIPI_LVDS_SW_HURRY_ENABLE_RK1808); + + val = CIF_MIPI_LVDS_SW_PRESS_VALUE(0x3) | + CIF_MIPI_LVDS_SW_PRESS_ENABLE | + CIF_MIPI_LVDS_SW_HURRY_VALUE(0x3) | + CIF_MIPI_LVDS_SW_HURRY_ENABLE | + CIF_MIPI_LVDS_SW_WATER_LINE_25 | + CIF_MIPI_LVDS_SW_WATER_LINE_ENABLE; + + val &= ~CIF_MIPI_LVDS_SW_SEL_LVDS; + + rkcif_write_reg(cif, CIF_REG_MIPI_LVDS_CTRL, val); + + rkcif_write_reg_or(cif, CIF_REG_MIPI_LVDS_INTEN, + CSI_ALL_ERROR_INTEN); + + rkcif_write_reg(cif, get_reg_index_of_id_ctrl1(channel->id), + channel->width | (channel->height << 16)); + + rkcif_write_reg(cif, get_reg_index_of_frm0_y_vlw(channel->id), + channel->virtual_width); + rkcif_write_reg(cif, get_reg_index_of_frm1_y_vlw(channel->id), + channel->virtual_width); + rkcif_write_reg(cif, get_reg_index_of_frm0_uv_vlw(channel->id), + channel->virtual_width); + rkcif_write_reg(cif, get_reg_index_of_frm1_uv_vlw(channel->id), + channel->virtual_width); + + if (channel->crop_en) + rkcif_write_reg(cif, get_reg_index_of_id_crop_start(channel->id), + channel->crop_st_y << 16 | channel->crop_st_x); + + return 0; +} + +/*config reg for rk3588*/ +static int vehicle_cif_csi_channel_set_v1(struct vehicle_cif *cif, + struct vehicle_csi_channel_info *channel, + enum v4l2_mbus_type mbus_type) +{ + unsigned int val = 0x0; + + if (channel->id >= 4) + return -EINVAL; + + if (!channel->enable) { + rkcif_write_reg(cif, get_reg_index_of_id_ctrl0(channel->id), + CSI_DISABLE_CAPTURE); + return 0; + } + + rkcif_write_reg_and(cif, CIF_REG_MIPI_LVDS_INTSTAT, + ~(CSI_START_INTSTAT(channel->id) | + CSI_DMA_END_INTSTAT(channel->id) | + CSI_LINE_INTSTAT_V1(channel->id))); + + /* enable id0 frame start int for sof(long frame, for hdr) + * vehicle don't need this + */ + if (channel->id == RKCIF_STREAM_MIPI_ID0) + rkcif_write_reg_or(cif, CIF_REG_MIPI_LVDS_INTEN, + CSI_START_INTEN(channel->id)); + + rkcif_write_reg(cif, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1, + 0x3fff << 16 | 0x3fff); + rkcif_write_reg(cif, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3, + 0x3fff << 16 | 0x3fff); + + rkcif_write_reg_or(cif, CIF_REG_MIPI_LVDS_INTEN, + CSI_DMA_END_INTEN(channel->id)); + + val = CIF_MIPI_LVDS_SW_PRESS_VALUE_RK3588(0x3) | + CIF_MIPI_LVDS_SW_PRESS_ENABLE | + CIF_MIPI_LVDS_SW_HURRY_VALUE_RK3588(0x3) | + CIF_MIPI_LVDS_SW_HURRY_ENABLE | + CIF_MIPI_LVDS_SW_WATER_LINE_25 | + CIF_MIPI_LVDS_SW_WATER_LINE_ENABLE; + + rkcif_write_reg(cif, CIF_REG_MIPI_LVDS_CTRL, val); + + rkcif_write_reg_or(cif, CIF_REG_MIPI_LVDS_INTEN, + CSI_ALL_ERROR_INTEN_V1); + + rkcif_write_reg(cif, get_reg_index_of_id_ctrl1(channel->id), + channel->width | (channel->height << 16)); + + rkcif_write_reg(cif, get_reg_index_of_frm0_y_vlw(channel->id), + channel->virtual_width); + + if (channel->crop_en) + rkcif_write_reg(cif, get_reg_index_of_id_crop_start(channel->id), + channel->crop_st_y << 16 | channel->crop_st_x); + + return 0; +} + +static int vehicle_cif_stream_start(struct vehicle_cif *cif) +{ + struct vehicle_csi_channel_info *channel; + + vehicle_cif_csi_get_vc_num(cif); + + /* just need init virtual channel 0 */ + channel = &cif->channels[0]; + channel->id = 0; + vehicle_cif_csi_channel_init(cif, channel); + if (cif->chip_id < CHIP_RK3588_VEHICLE_CIF) + vehicle_cif_csi_channel_set(cif, channel, V4L2_MBUS_CSI2_DPHY); + else + vehicle_cif_csi_channel_set_v1(cif, channel, V4L2_MBUS_CSI2_DPHY); + + return 0; +} + +static int cif_csi_stream_setup(struct vehicle_cif *cif) +{ + vehicle_csi2_stream_start(cif); + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588) + vehicle_csi2_dphy_stream_start(cif); + else + vehicle_csi2_dcphy_stream_start(cif); + vehicle_cif_stream_start(cif); + + return 0; +} + +static void vehicle_csi2_dphy_hw_do_reset(struct vehicle_cif *cif) +{ + unsigned int i; + struct csi2_dphy_hw *dphy_hw = cif->dphy_hw; + + for (i = 0; i < dphy_hw->num_dphy_rsts; i++) + if (dphy_hw->dphy_rst[i]) + reset_control_assert(dphy_hw->dphy_rst[i]); + udelay(5); + for (i = 0; i < dphy_hw->num_dphy_rsts; i++) + if (dphy_hw->dphy_rst[i]) + reset_control_deassert(dphy_hw->dphy_rst[i]); +} + +static void vehicle_csi2_hw_soft_reset(struct vehicle_cif *cif) +{ + unsigned int i; + struct csi2_dphy_hw *dphy_hw = cif->dphy_hw; + + for (i = 0; i < dphy_hw->num_csi2_rsts; i++) + if (dphy_hw->csi2_rst[i]) + reset_control_assert(dphy_hw->csi2_rst[i]); + udelay(5); + for (i = 0; i < dphy_hw->num_csi2_rsts; i++) + if (dphy_hw->csi2_rst[i]) + reset_control_deassert(dphy_hw->csi2_rst[i]); +} + +static int vehicle_csi2_dphy_stream_stop(struct vehicle_cif *cif) +{ + struct csi2_dphy_hw *hw = cif->dphy_hw; + + mutex_lock(&hw->mutex); + + write_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, 0x01); + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588) + vehicle_csi2_dphy_hw_do_reset(cif); + usleep_range(500, 1000); + + mutex_unlock(&hw->mutex); + + return 0; +} + +static void vehicle_rkcif_disable_sys_clk(struct rk_cif_clk *clk) +{ + int i; + + for (i = clk->clks_num - 1; i >= 0; i--) + clk_disable_unprepare(clk->clks[i]); +} + +static int vehicle_rkcif_enable_sys_clk(struct rk_cif_clk *clk) +{ + int i, ret = -EINVAL; + + for (i = 0; i < clk->clks_num; i++) { + ret = clk_prepare_enable(clk->clks[i]); + if (ret < 0) + goto err; + } + + return 0; +err: + for (--i; i >= 0; --i) + clk_disable_unprepare(clk->clks[i]); + + return ret; +} + +/* sensor mclk set */ +static void rkcif_s_mclk(struct vehicle_cif *cif, int on, int clk_rate) +{ + int err = 0; + struct device *dev = cif->dev; + struct rk_cif_clk *clk = &cif->clk; + + //return ; + if (on && !clk->on) { + if (!IS_ERR(clk->xvclk)) { + err = clk_set_rate(clk->xvclk, clk_rate); + if (err < 0) + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); + } + if (!IS_ERR(clk->xvclk)) { + err = clk_prepare_enable(clk->xvclk); + if (err < 0) + dev_err(dev, "Failed to enable xvclk\n"); + } + } else { + if (!IS_ERR(clk->xvclk)) + clk_disable_unprepare(clk->xvclk); + } + usleep_range(2000, 5000); +} + +static int rk_cif_mclk_ctrl(struct vehicle_cif *cif, int on, int clk_rate) +{ + int err = 0; + + struct rk_cif_clk *clk = &cif->clk; + + if (on && !clk->on) { + vehicle_rkcif_enable_sys_clk(clk); + clk->on = true; + } else if (!on && clk->on) { + vehicle_rkcif_disable_sys_clk(clk); + clk->on = false; + } + + return err; +} + +static void csi2_disable_dphy_clk(struct csi2_dphy_hw *hw) +{ + int i; + + for (i = hw->num_dphy_clks - 1; i >= 0; i--) { + clk_disable_unprepare(hw->dphy_clks[i].clk); + VEHICLE_INFO("%s(%d) disable dphy clk: %s\n", + __func__, __LINE__, hw->dphy_clks[i].id); + } +} + +static int csi2_enable_dphy_clk(struct csi2_dphy_hw *hw) +{ + int i, ret = -EINVAL; + + for (i = 0; i < hw->num_dphy_clks; i++) { + ret = clk_prepare_enable(hw->dphy_clks[i].clk); + if (ret < 0) + goto err; + VEHICLE_INFO("%s(%d) enable dphy clk: %s\n", + __func__, __LINE__, hw->dphy_clks[i].id); + } + + return 0; +err: + VEHICLE_DGERR("%s(%d) enable dphy clk: %s err\n", + __func__, __LINE__, hw->dphy_clks[i].id); + for (--i; i >= 0; --i) + clk_disable_unprepare(hw->dphy_clks[i].clk); + + return ret; +} + +static void csi2_disable_clk(struct csi2_dphy_hw *hw) +{ + int i; + + for (i = hw->num_csi2_clks - 1; i >= 0; i--) { + clk_disable_unprepare(hw->csi2_clks[i].clk); + VEHICLE_INFO("%s(%d) disable csi2 clk: %s\n", + __func__, __LINE__, hw->csi2_clks[i].id); + } +} + +static int csi2_enable_clk(struct csi2_dphy_hw *hw) +{ + int i, ret = -EINVAL; + + for (i = 0; i < hw->num_csi2_clks; i++) { + ret = clk_prepare_enable(hw->csi2_clks[i].clk); + if (ret < 0) + goto err; + VEHICLE_INFO("%s(%d) enable csi2 clk: %s\n", + __func__, __LINE__, hw->csi2_clks[i].id); + } + + return 0; +err: + VEHICLE_DGERR("%s(%d) enable csi2 clk: %s err\n", + __func__, __LINE__, hw->csi2_clks[i].id); + for (--i; i >= 0; --i) + clk_disable_unprepare(hw->csi2_clks[i].clk); + + return ret; +} + +static int vehicle_csi2_clk_ctrl(struct vehicle_cif *cif, int on) +{ + int ret = 0; + struct csi2_dphy_hw *dphy_hw = cif->dphy_hw; + + on = !!on; + if (on) { + ret = csi2_enable_dphy_clk(dphy_hw); + if (ret < 0) { + VEHICLE_DGERR("enable csi dphy clk failed!"); + goto err; + } + ret = csi2_enable_clk(dphy_hw); + if (ret < 0) { + VEHICLE_DGERR("enable csi dphy clk failed!"); + goto err; + } + dphy_hw->on = true; + } else { + csi2_disable_dphy_clk(dphy_hw); + csi2_disable_clk(dphy_hw); + dphy_hw->on = false; + } + + return 0; +err: + return ret; +} + +static int vehicle_csi2_stream_stop(struct vehicle_cif *cif) +{ + vehicle_csi2_disable(cif); + + return 0; +} + +static int vehicle_cif_stream_stop(struct vehicle_cif *cif) +{ + return 0; +} + +static int vehicle_cif_csi_stream_stop(struct vehicle_cif *cif) +{ + vehicle_cif_stream_stop(cif); + vehicle_csi2_stream_stop(cif); + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588) + vehicle_csi2_dphy_stream_stop(cif); + else + vehicle_csi2_dcphy_stream_stop(cif); + + return 0; +} + +static int vehicle_cif_csi2_s_stream(struct vehicle_cif *cif, + int enable, + enum v4l2_mbus_type mbus_type) + +{ + unsigned int val = 0x0; + const struct cif_input_fmt *infmt; + struct vehicle_csi_channel_info *channel; + int id; + + channel = &cif->channels[0]; + + if (enable) { + val = CSI_ENABLE_CAPTURE | channel->fmt_val | + channel->cmd_mode_en << 4 | channel->crop_en << 5 | + channel->id << 8 | channel->data_type << 10; + + val &= ~CSI_ENABLE_MIPI_COMPACT; + + infmt = find_input_fmt(cif->cif_cfg.mbus_code); + if (!infmt) { + VEHICLE_INFO("Input fmt is invalid, use default!\n"); + val |= CSI_YUV_INPUT_ORDER_UYVY; + } else { + val |= infmt->csi_yuv_order; + } + rkcif_write_reg(cif, get_reg_index_of_id_ctrl0(channel->id), val); + cif->state = RKCIF_STATE_STREAMING; + } else { + id = channel->id; + val = rkcif_read_reg(cif, get_reg_index_of_id_ctrl0(id)); + val &= ~CSI_ENABLE_CAPTURE; + + rkcif_write_reg(cif, get_reg_index_of_id_ctrl0(id), val); + + rkcif_write_reg_or(cif, CIF_REG_MIPI_LVDS_INTSTAT, + CSI_START_INTSTAT(id) | + CSI_DMA_END_INTSTAT(id) | + CSI_LINE_INTSTAT(id)); + + rkcif_write_reg_and(cif, CIF_REG_MIPI_LVDS_INTEN, + ~(CSI_START_INTEN(id) | + CSI_DMA_END_INTEN(id) | + CSI_LINE_INTEN(id))); + + rkcif_write_reg_and(cif, CIF_REG_MIPI_LVDS_INTEN, + ~CSI_ALL_ERROR_INTEN); + cif->state = RKCIF_STATE_READY; + } + + return 0; +} + +static int vehicle_cif_csi2_s_stream_v1(struct vehicle_cif *cif, + int enable, + enum v4l2_mbus_type mbus_type) + +{ + unsigned int val = 0x0; + const struct cif_input_fmt *infmt; + struct vehicle_csi_channel_info *channel; + struct vehicle_cfg *cfg = &cif->cif_cfg; + int id; + + channel = &cif->channels[0]; + + if (enable) { + val = CSI_ENABLE_CAPTURE | CSI_DMA_ENABLE | channel->fmt_val | + channel->cmd_mode_en << 26 | CSI_ENABLE_CROP_V1 | + channel->id << 8 | channel->data_type << 10; + + infmt = find_input_fmt(cif->cif_cfg.mbus_code); + if (!infmt) { + VEHICLE_INFO("Input fmt is invalid, use default!\n"); + val |= CSI_YUV_INPUT_ORDER_UYVY; + } else { + val |= infmt->csi_yuv_order; + } + + if (cfg->output_format == CIF_OUTPUT_FORMAT_420) { + if (find_output_fmt(V4L2_PIX_FMT_NV12)) + val |= CSI_WRDDR_TYPE_YUV420SP_RK3588 | CSI_YUV_OUTPUT_ORDER_UYVY; + } else { + if (find_output_fmt(V4L2_PIX_FMT_NV16)) + val |= CSI_WRDDR_TYPE_YUV422SP_RK3588 | CSI_YUV_OUTPUT_ORDER_UYVY; + } + + rkcif_write_reg(cif, get_reg_index_of_id_ctrl0(channel->id), val); + rkcif_write_reg(cif, CIF_REG_MIPI_EFFECT_CODE_ID0, 0x02410251); + rkcif_write_reg(cif, CIF_REG_MIPI_EFFECT_CODE_ID1, 0x02420252); + cif->state = RKCIF_STATE_STREAMING; + } else { + id = channel->id; + val = rkcif_read_reg(cif, get_reg_index_of_id_ctrl0(id)); + val &= ~CSI_ENABLE_CAPTURE; + + rkcif_write_reg(cif, get_reg_index_of_id_ctrl0(id), val); + + rkcif_write_reg_or(cif, CIF_REG_MIPI_LVDS_INTSTAT, + CSI_START_INTSTAT(id) | + CSI_DMA_END_INTSTAT(id) | + CSI_LINE_INTSTAT(id)); + + rkcif_write_reg_and(cif, CIF_REG_MIPI_LVDS_INTEN, + ~(CSI_START_INTEN(id) | + CSI_DMA_END_INTEN(id) | + CSI_LINE_INTEN(id))); + + rkcif_write_reg_and(cif, CIF_REG_MIPI_LVDS_INTEN, + ~CSI_ALL_ERROR_INTEN); + cif->state = RKCIF_STATE_READY; + } + + return 0; +} + +static int cif_interrupt_setup(struct vehicle_cif *cif) +{ + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, + FRAME_END_EN | INTSTAT_ERR | + PST_INF_FRAME_END); + + /* enable line int for sof */ + rkcif_write_reg(cif, CIF_REG_DVP_LINE_INT_NUM, 0x1); + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, LINE_INT_EN); + + return 0; +} + +static void vehicle_cif_dvp_dump_regs(struct vehicle_cif *cif) +{ + int val; + + if (!vehicle_debug) + return; + + val = rkcif_read_reg(cif, CIF_REG_DVP_CTRL); + VEHICLE_DG("CIF_REG_DVP_CTRL = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_INTEN); + VEHICLE_DG("CIF_REG_DVP_INTEN = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_INTSTAT); + VEHICLE_DG("CIF_REG_DVP_INTSTAT = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_FOR); + VEHICLE_DG("CIF_REG_DVP_FOR = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_MULTI_ID); + VEHICLE_DG("CIF_REG_DVP_MULTI_ID = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_LINE_NUM_ADDR); + VEHICLE_DG("CIF_REG_DVP_LINE_NUM_ADDR = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_FRM0_ADDR_Y); + VEHICLE_DG("CIF_REG_DVP_FRM0_ADDR_Y = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_FRM0_ADDR_UV); + VEHICLE_DG("CIF_REG_DVP_FRM0_ADDR_UV = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_FRM1_ADDR_Y); + VEHICLE_DG("CIF_REG_DVP_FRM1_ADDR_Y = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_FRM1_ADDR_UV); + VEHICLE_DG("CIF_REG_DVP_FRM1_ADDR_UV = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_VIR_LINE_WIDTH); + VEHICLE_DG("CIF_REG_DVP_VIR_LINE_WIDTH = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_SET_SIZE); + VEHICLE_DG("CIF_REG_DVP_SET_SIZE = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_LINE_INT_NUM); + VEHICLE_DG("CIF_REG_DVP_LINE_INT_NUM = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_LINE_CNT); + VEHICLE_DG("CIF_REG_DVP_LINE_CNT = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_CROP); + VEHICLE_DG("CIF_REG_DVP_CROP = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_SCL_CTRL); + VEHICLE_DG("CIF_REG_DVP_SCL_CTRL = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_FRAME_STATUS); + VEHICLE_DG("CIF_REG_DVP_FRAME_STATUS = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_CUR_DST); + VEHICLE_DG("CIF_REG_DVP_CUR_DST = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_LAST_LINE); + VEHICLE_DG("CIF_REG_DVP_LAST_LINE = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_LAST_PIX); + VEHICLE_DG("CIF_REG_DVP_LAST_PIX = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_SCL_VALID_NUM); + VEHICLE_DG("CIF_REG_DVP_SCL_VALID_NUM = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_DVP_LINE_NUM_ADDR); + VEHICLE_DG("CIF_REG_DVP_LINE_NUM_ADDR = 0X%x\r\n", val); + + /* read dvp clk sample edge */ + val = rkvehicle_cif_read_grf_reg(cif, CIF_REG_GRF_CIFIO_CON); + VEHICLE_DG("CIF_REG_GRF_CIFIO_CON = 0X%x\r\n", val); +} + +static void vehicle_cif_csi2_dump_regs(struct vehicle_cif *cif) +{ + int val = 0; + void __iomem *csi2_base = cif->csi2_base; + struct csi2_dphy_hw *hw = cif->dphy_hw; + + if (!vehicle_debug) + return; + + /* 1. dump csi2-dphy regs */ + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588) { + VEHICLE_DG("\n\n DUMP CSI-DPHY REGS: \r\n"); + read_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, &val); + VEHICLE_DG("CSI2PHY_REG_CTRL_LANE_ENABLE = 0x%x\r\n", val); + + read_csi2_dphy_reg(hw, CSI2PHY_DUAL_CLK_EN, &val); + VEHICLE_DG("CSI2PHY_DUAL_CLK_EN = 0x%x\r\n", val); + + val = csi2_dphy_read_grf_reg(hw, GRF_DPHY_CSI2PHY_FORCERXMODE); + VEHICLE_DG("GRF_DPHY_CSI2PHY_FORCERXMODE = 0x%x\r\n", val); + + val = csi2_dphy_read_grf_reg(hw, GRF_DPHY_CSI2PHY_LANE_SEL); + VEHICLE_DG("GRF_DPHY_CSI2PHY_LANE_SEL = 0x%x\r\n", val); + + val = csi2_dphy_read_grf_reg(hw, GRF_DPHY_CSI2PHY_DATALANE_EN); + VEHICLE_DG("GRF_DPHY_CSI2PHY_DATALANE_EN = 0x%x\r\n", val); + + val = csi2_dphy_read_grf_reg(hw, GRF_DPHY_CSI2PHY_CLKLANE_EN); + VEHICLE_DG("GRF_DPHY_CSI2PHY_CLKLANE_EN = 0x%x\r\n", val); + } + + /* 2. dump csi2 regs */ + VEHICLE_DG("\n\n DUMP CSI2 REGS: \r\n"); + val = vehicle_read_csihost_reg(csi2_base, CSIHOST_N_LANES); + VEHICLE_DG("CSIHOST_N_LANES = 0x%x\r\n", val); + + val = vehicle_read_csihost_reg(csi2_base, CSIHOST_CONTROL); + VEHICLE_DG("CSIHOST_CONTROL = 0x%x\r\n", val); + + val = vehicle_read_csihost_reg(csi2_base, CSIHOST_MSK1); + VEHICLE_DG("CSIHOST_MSK1 = 0x%x\r\n", val); + + val = vehicle_read_csihost_reg(csi2_base, CSIHOST_MSK2); + VEHICLE_DG("CSIHOST_MSK2 = 0x%x\r\n", val); + + val = vehicle_read_csihost_reg(csi2_base, CSIHOST_RESETN); + VEHICLE_DG("CSIHOST_RESETN = 0x%x\r\n", val); + + /* 3. dump cif regs */ + VEHICLE_DG("\n\n DUMP MIPI CIF REGS: \r\n"); + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_CTRL); + VEHICLE_DG("CIF_REG_MIPI_LVDS_CTRL = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_INTEN); + VEHICLE_DG("CIF_REG_MIPI_LVDS_INTEN = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_INTSTAT); + VEHICLE_DG("CIF_REG_MIPI_LVDS_INTSTAT = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_ID0_CTRL0); + VEHICLE_DG("CIF_REG_MIPI_LVDS_ID0_CTRL0 = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_ID0_CTRL1); + VEHICLE_DG("CIF_REG_MIPI_LVDS_ID0_CTRL1 = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1); + VEHICLE_DG("CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1 = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3); + VEHICLE_DG("CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3 = 0x%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID0); + VEHICLE_DG("CIF_REG_MIPI_LVDS_FRAME0_VLW_Y_ID0 = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID0); + VEHICLE_DG("CIF_REG_MIPI_LVDS_FRAME0_VLW_UV_ID0 = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID0); + VEHICLE_DG("CIF_REG_MIPI_LVDS_FRAME1_VLW_Y_ID0 = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID0); + VEHICLE_DG("CIF_REG_MIPI_LVDS_FRAME1_VLW_UV_ID0 = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID0); + VEHICLE_DG("CIF_REG_MIPI_LVDS_FRAME0_ADDR_Y_ID0 = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID0); + VEHICLE_DG("CIF_REG_MIPI_LVDS_FRAME0_ADDR_UV_ID0 = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID0); + VEHICLE_DG("CIF_REG_MIPI_LVDS_FRAME1_ADDR_Y_ID0 = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID0); + VEHICLE_DG("CIF_REG_MIPI_LVDS_FRAME1_ADDR_UV_ID0 = 0X%x\r\n", val); + + val = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_ID0_CROP_START); + VEHICLE_DG("CIF_REG_MIPI_LVDS_ID0_CROP_START = 0X%x\r\n", val); + + /* read dvp clk sample edge */ + val = rkvehicle_cif_read_grf_reg(cif, CIF_REG_GRF_CIFIO_CON); + VEHICLE_DG("CIF_REG_GRF_CIFIO_CON = 0X%x\r\n", val); +} + +static int vehicle_cif_s_stream(struct vehicle_cif *cif, int enable) +{ + int cif_ctrl_val; + unsigned int dma_en = 0; + + cif->is_enabled = enable; + + VEHICLE_INFO("%s enable=%d\n", __func__, enable); + + if (enable) { + cif->irqinfo.cifirq_idx = 0; + cif->irqinfo.cifirq_normal_idx = 0; + cif->irqinfo.cifirq_abnormal_idx = 0; + cif->irqinfo.dmairq_idx = 0; + cif->irqinfo.all_err_cnt = 0; + cif->irqinfo.dvp_bus_err_cnt = 0; + cif->irqinfo.dvp_overflow_cnt = 0; + cif->irqinfo.dvp_pix_err_cnt = 0; + cif->irqinfo.dvp_line_err_cnt = 0; + cif->irqinfo.dvp_size_err_cnt = 0; + cif->irqinfo.dvp_bwidth_lack_cnt = 0; + cif->irqinfo.csi_size_err_cnt = 0; + + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, + FRAME_END_EN | INTSTAT_ERR | + PST_INF_FRAME_END); + + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) { + rkcif_write_reg(cif, CIF_REG_DVP_LINE_INT_NUM, 0x1); + rkcif_write_reg_or(cif, CIF_REG_DVP_INTEN, 0x033ffff); + } + + dma_en = DVP_DMA_EN; + if (cif->chip_id < CHIP_RK3588_VEHICLE_CIF) + rkcif_write_reg(cif, CIF_REG_DVP_CTRL, + AXI_BURST_16 | MODE_PINGPONG | ENABLE_CAPTURE); + else + rkcif_write_reg(cif, CIF_REG_DVP_CTRL, + DVP_SW_WATER_LINE_25 + | dma_en + | DVP_PRESS_EN + | DVP_HURRY_EN + | DVP_SW_WATER_LINE_25 + | DVP_SW_PRESS_VALUE(3) + | DVP_SW_HURRY_VALUE(3) + | ENABLE_CAPTURE); + cif->frame_idx = 0; + cif->state = RKCIF_STATE_STREAMING; + } else { + cif_ctrl_val = rkcif_read_reg(cif, CIF_REG_DVP_CTRL); + cif_ctrl_val &= ~ENABLE_CAPTURE; + rkcif_write_reg(cif, CIF_REG_DVP_CTRL, cif_ctrl_val); + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, 0); + rkcif_write_reg(cif, CIF_REG_DVP_INTSTAT, 0x3ff); + rkcif_write_reg(cif, CIF_REG_DVP_FRAME_STATUS, 0x0); + cif->state = RKCIF_STATE_READY; + } + + return 0; +} + +static int vehicle_cif_create_dummy_buf(struct vehicle_cif *cif) +{ + struct vehicle_rkcif_dummy_buffer *dummy_buf = &cif->dummy_buf; + struct vehicle_cfg *cfg = &cif->cif_cfg; + + /* get a maximum plane size */ + dummy_buf->size = cfg->width * cfg->height * 2; + + dummy_buf->vaddr = dma_alloc_coherent(cif->dev, dummy_buf->size, + &dummy_buf->dma_addr, + GFP_KERNEL); + if (!dummy_buf->vaddr) { + VEHICLE_DGERR("Failed to allocate the memory for dummy buffer\n"); + return -ENOMEM; + } + + VEHICLE_INFO("Allocate dummy buffer, size: 0x%08x\n", dummy_buf->size); + + return 0; +} + +static void vehicle_cif_destroy_dummy_buf(struct vehicle_cif *cif) +{ + struct vehicle_rkcif_dummy_buffer *dummy_buf = &cif->dummy_buf; + + VEHICLE_INFO("Destroy dummy buffer, size: 0x%08x\n", dummy_buf->size); + + if (dummy_buf->vaddr) + dma_free_coherent(cif->dev, dummy_buf->size, + dummy_buf->vaddr, dummy_buf->dma_addr); + dummy_buf->dma_addr = 0; + dummy_buf->vaddr = NULL; +} + +static void vehicle_cif_hw_soft_reset(struct vehicle_cif *cif) +{ + unsigned int i; + struct rk_cif_clk *clk = &cif->clk; + + for (i = 0; i < clk->rsts_num; i++) + if (clk->cif_rst[i]) + reset_control_assert(clk->cif_rst[i]); + udelay(10); + for (i = 0; i < clk->rsts_num; i++) + if (clk->cif_rst[i]) + reset_control_deassert(clk->cif_rst[i]); +} + +static void vehicle_rkcif_do_soft_reset(struct vehicle_cif *cif) +{ + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) + rkcif_write_reg_or(cif, CIF_REG_MIPI_LVDS_CTRL, 0x000A0000); + else + rkcif_write_reg_or(cif, CIF_REG_DVP_CTRL, 0x000A0000); + usleep_range(10, 20); + VEHICLE_INFO("vicap do soft reset 0x%x\n", 0x000A0000); +} + +static int vehicle_cif_do_stop_stream(struct vehicle_cif *cif) +{ + if (!cif) + return -1; + + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) { + vehicle_cif_csi2_s_stream_v1(cif, 0, V4L2_MBUS_CSI2_DPHY); + vehicle_cif_csi_stream_stop(cif); + } else { + vehicle_cif_csi2_s_stream(cif, 0, V4L2_MBUS_CSI2_DPHY); + vehicle_cif_csi_stream_stop(cif); + } + } else { + vehicle_cif_s_stream(cif, 0); + } + if (cif->chip_id >= CHIP_RK3588_VEHICLE_CIF) + vehicle_rkcif_do_soft_reset(cif); + vehicle_cif_destroy_dummy_buf(cif); + + return 0; +} + +static int vehicle_cif_do_start_stream(struct vehicle_cif *cif) +{ + int ret = 0; + + if (!cif) + return -ENODEV; + + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + + /* 1. stream setup */ + cif_csi_stream_setup(cif); + + /* 2. create dummy buf */ + ret = vehicle_cif_create_dummy_buf(cif); + if (ret < 0) + VEHICLE_DGERR("Failed to create dummy_buf, %d\n", ret); + + /* 3. cif init buffer */ + if (vehicle_cif_init_buffer(cif, 1, cif->channels[0].id) < 0) + return -EINVAL; + + /* 4. dump cif regs */ + vehicle_cif_csi2_dump_regs(cif); + + /* 5. start stream */ + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) + vehicle_cif_csi2_s_stream_v1(cif, 1, V4L2_MBUS_CSI2_DPHY); + else + vehicle_cif_csi2_s_stream(cif, 1, V4L2_MBUS_CSI2_DPHY); + + } else { + /* 1. stream setup */ + cif_stream_setup(cif); + + /* 2. create dummy buf */ + ret = vehicle_cif_create_dummy_buf(cif); + if (ret < 0) + VEHICLE_DGERR("Failed to create dummy_buf, %d\n", ret); + + /* 3. cif init buffer */ + if (vehicle_cif_init_buffer(cif, 1, 0) < 0) + return -EINVAL; + + /* 4. enable interrupts */ + if (cif->chip_id < CHIP_RK3588_VEHICLE_CIF) + cif_interrupt_setup(cif); + + /* 5. dump cif regs */ + vehicle_cif_dvp_dump_regs(cif); + + /* 6. start stream */ + vehicle_cif_s_stream(cif, 1); + } + + return 0; +} + +static void vehicle_rkcif_disable_sys_clk(struct rk_cif_clk *clk); +static int vehicle_rkcif_enable_sys_clk(struct rk_cif_clk *clk); + +static void vehicle_cif_reset(struct vehicle_cif *cif, int only_rst) +{ + int ret = 0; + + mutex_lock(&cif->stream_lock); + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + VEHICLE_DG("%s enter, V4L2_MBUS_CSI2 reset need to do!\n", __func__); + + // goto unlock_reset; + if (only_rst == 1) { + vehicle_cif_hw_soft_reset(cif); + } else { + vehicle_cif_do_stop_stream(cif); + vehicle_cif_hw_soft_reset(cif); + vehicle_cif_do_start_stream(cif); + } + } else { + int ctrl_reg, inten_reg, crop_reg, set_size_reg, for_reg; + int vir_line_width_reg, scl_reg; + int y0_reg, uv0_reg, y1_reg, uv1_reg; + + VEHICLE_DG("%s enter, do reset!\n", __func__); + if (only_rst == 1) { + vehicle_cif_hw_soft_reset(cif); + } else { + ctrl_reg = rkcif_read_reg(cif, CIF_REG_DVP_CTRL); + if (ctrl_reg & ENABLE_CAPTURE) + rkcif_write_reg(cif, CIF_REG_DVP_CTRL, + ctrl_reg & ~ENABLE_CAPTURE); + + crop_reg = rkcif_read_reg(cif, CIF_REG_DVP_CROP); + set_size_reg = rkcif_read_reg(cif, CIF_REG_DVP_SET_SIZE); + inten_reg = rkcif_read_reg(cif, CIF_REG_DVP_INTEN); + for_reg = rkcif_read_reg(cif, CIF_REG_DVP_FOR); + vir_line_width_reg = rkcif_read_reg(cif, + CIF_REG_DVP_VIR_LINE_WIDTH); + scl_reg = rkcif_read_reg(cif, CIF_REG_DVP_SCL_CTRL); + y0_reg = rkcif_read_reg(cif, CIF_REG_DVP_FRM0_ADDR_Y); + uv0_reg = rkcif_read_reg(cif, CIF_REG_DVP_FRM0_ADDR_UV); + y1_reg = rkcif_read_reg(cif, CIF_REG_DVP_FRM1_ADDR_Y); + uv1_reg = rkcif_read_reg(cif, CIF_REG_DVP_FRM1_ADDR_UV); + + udelay(20); + vehicle_cif_hw_soft_reset(cif); + vehicle_rkcif_disable_sys_clk(&cif->clk); + udelay(5); + ret = vehicle_rkcif_enable_sys_clk(&cif->clk); + if (ret < 0) { + VEHICLE_DGERR("@%s, resume cif clk failed!\n", __func__); + goto unlock_reset; + } + + rkcif_write_reg(cif, CIF_REG_DVP_CTRL, + ctrl_reg & ~ENABLE_CAPTURE); + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, inten_reg); + rkcif_write_reg(cif, CIF_REG_DVP_CROP, crop_reg); + rkcif_write_reg(cif, CIF_REG_DVP_SET_SIZE, set_size_reg); + rkcif_write_reg(cif, CIF_REG_DVP_FOR, for_reg); + rkcif_write_reg(cif, CIF_REG_DVP_VIR_LINE_WIDTH, + vir_line_width_reg); + rkcif_write_reg(cif, CIF_REG_DVP_SCL_CTRL, scl_reg); + rkcif_write_reg(cif, CIF_REG_DVP_FRM0_ADDR_Y, y0_reg); + rkcif_write_reg(cif, CIF_REG_DVP_FRM0_ADDR_UV, uv0_reg); + rkcif_write_reg(cif, CIF_REG_DVP_FRM1_ADDR_Y, y1_reg); + rkcif_write_reg(cif, CIF_REG_DVP_FRM1_ADDR_UV, uv1_reg); + } + } +unlock_reset: + mutex_unlock(&cif->stream_lock); +} + +static void vehicle_cif_reset_delay(struct vehicle_cif *cif) +{ + mdelay(10); + vehicle_cif_reset(cif, 0); + mdelay(10); + vehicle_cif_s_stream(cif, 1); +} + +static void cif_capture_en(char *reg, int enable) +{ + int val = 0; + + val = read_reg(reg, CIF_REG_DVP_CTRL); + if (enable == 1) + write_reg(reg, CIF_REG_DVP_CTRL, val | ENABLE_CAPTURE); + else + write_reg(reg, CIF_REG_DVP_CTRL, val & (~ENABLE_CAPTURE)); +} + +static void vehicle_cif_reset_work_func(struct work_struct *work) +{ + struct vehicle_cif *cif = container_of(work, struct vehicle_cif, + work.work); + + if (cif->stopping) + return; + + atomic_set(&cif->reset_status, 1); + vehicle_cif_reset_delay(cif); + atomic_set(&cif->reset_status, 0); + wake_up(&cif->wq_stopped); +} + +int vehicle_wait_cif_reset_done(void) +{ + struct vehicle_cif *cif = g_cif; + int ret = 0, retry = 2; + + for (retry = 2; retry >= 0; retry--) { + ret = wait_event_timeout(cif->wq_stopped, + !atomic_read(&cif->reset_status), + msecs_to_jiffies(200)); + if (!ret) { + VEHICLE_DG("%s wait cif reset timeout, left try times(%d)!\n", + __func__, retry); + } else { + break; + } + } + + return 0; +} + +static int cif_irq_error_process(struct vehicle_cif *cif, unsigned int reg_intstat) +{ + VEHICLE_DG("%s cif->irqinfo.all_err_cnt(%lld)\n", __func__, + cif->irqinfo.all_err_cnt); + if (reg_intstat & INTSTAT_ERR) { + cif->irqinfo.all_err_cnt++; + + if (reg_intstat & BUS_ERR) { + cif->irqinfo.dvp_bus_err_cnt++; + VEHICLE_DGERR("dvp bus err\n"); + } + + if (reg_intstat & DVP_ALL_OVERFLOW) { + cif->irqinfo.dvp_overflow_cnt++; + VEHICLE_DGERR("dvp overflow err\n"); + } + + if (reg_intstat & LINE_ERR) { + cif->irqinfo.dvp_line_err_cnt++; + VEHICLE_DGERR("dvp line err\n"); + } + + if (reg_intstat & PIX_ERR) { + cif->irqinfo.dvp_pix_err_cnt++; + VEHICLE_DGERR("dvp pix err\n"); + } + + if (cif->irqinfo.all_err_cnt < 10) { + u32 mask; + + VEHICLE_DGERR("ERROR: DVP_ALL_ERROR:0x%x!!\n", reg_intstat); + mask = rkcif_read_reg(cif, CIF_REG_DVP_INTEN); + mask &= ~INTSTAT_ERR; + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, mask); + return -2; + } else if (cif->irqinfo.all_err_cnt >= 10) { + u32 mask; + + mask = rkcif_read_reg(cif, CIF_REG_DVP_INTEN); + mask &= ~INTSTAT_ERR; + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, mask); + VEHICLE_DGERR("ERROR: DVP_ALL_ERROR:0x%x!!\n", reg_intstat); + return -2; + } + } + + return 0; +} + +static int vehicle_cif_csi2_g_mipi_id(unsigned int intstat) +{ + if (intstat & CSI_FRAME_END_ID0) { + if ((intstat & CSI_FRAME_END_ID0) == CSI_FRAME_END_ID0) + VEHICLE_DG("frame0/1 trigger simultaneously in ID0\n"); + return RKCIF_STREAM_MIPI_ID0; + } + + if (intstat & CSI_FRAME_END_ID1) { + if ((intstat & CSI_FRAME_END_ID1) == CSI_FRAME_END_ID1) + VEHICLE_DG("frame0/1 trigger simultaneously in ID1\n"); + return RKCIF_STREAM_MIPI_ID1; + } + + if (intstat & CSI_FRAME_END_ID2) { + if ((intstat & CSI_FRAME_END_ID2) == CSI_FRAME_END_ID2) + VEHICLE_DG("frame0/1 trigger simultaneously in ID2\n"); + return RKCIF_STREAM_MIPI_ID2; + } + + if (intstat & CSI_FRAME_END_ID3) { + if ((intstat & CSI_FRAME_END_ID3) == CSI_FRAME_END_ID3) + VEHICLE_DG("frame0/1 trigger simultaneously in ID3\n"); + return RKCIF_STREAM_MIPI_ID3; + } + + return -EINVAL; +} + +static __maybe_unused int rkcif_dvp_g_ch_id_by_fe(unsigned int intstat) +{ + if (intstat & DVP_ALL_END_ID0) { + if ((intstat & DVP_ALL_END_ID0) == + DVP_ALL_END_ID0) + VEHICLE_DG("frame0/1 trigger simultaneously in DVP ID0\n"); + return RKCIF_STREAM_MIPI_ID0; + } + + if (intstat & DVP_ALL_END_ID1) { + if ((intstat & DVP_ALL_END_ID1) == + DVP_ALL_END_ID1) + VEHICLE_DG("frame0/1 trigger simultaneously in DVP ID1\n"); + return RKCIF_STREAM_MIPI_ID1; + } + + if (intstat & DVP_ALL_END_ID2) { + if ((intstat & DVP_ALL_END_ID2) == + DVP_ALL_END_ID2) + VEHICLE_DG("frame0/1 trigger simultaneously in DVP ID2\n"); + return RKCIF_STREAM_MIPI_ID2; + } + + if (intstat & DVP_ALL_END_ID3) { + if ((intstat & DVP_ALL_END_ID3) == + DVP_ALL_END_ID3) + VEHICLE_DG("frame0/1 trigger simultaneously in DVP ID3\n"); + return RKCIF_STREAM_MIPI_ID3; + } + + return -EINVAL; +} + +static int vehicle_cif_next_buffer(struct vehicle_cif *cif, u32 frame_ready, int mipi_id) +{ + u32 frm0_addr_y, frm0_addr_uv; + u32 frm1_addr_y, frm1_addr_uv; + unsigned long y_addr = 0, uv_addr = 0; + static unsigned long temp_y_addr, temp_uv_addr; + int commit_buf = 0; + struct vehicle_rkcif_dummy_buffer *dummy_buf = &cif->dummy_buf; + + VEHICLE_DG("@%s, enter, mipi_id(%d)\n", __func__, mipi_id); + + if ((frame_ready > 1) || (cif->cif_cfg.buf_num < 2) || + (cif->cif_cfg.buf_num > MAX_BUF_NUM)) + return 0; + + cif->last_buf_index = cif->current_buf_index; + cif->current_buf_index = (cif->current_buf_index + 1) % + cif->cif_cfg.buf_num; + + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + frm0_addr_y = get_reg_index_of_frm0_y_addr(mipi_id); + frm0_addr_uv = get_reg_index_of_frm0_uv_addr(mipi_id); + frm1_addr_y = get_reg_index_of_frm1_y_addr(mipi_id); + frm1_addr_uv = get_reg_index_of_frm1_uv_addr(mipi_id); + } else { + frm0_addr_y = get_dvp_reg_index_of_frm0_y_addr(mipi_id); + frm0_addr_uv = get_dvp_reg_index_of_frm0_uv_addr(mipi_id); + frm1_addr_y = get_dvp_reg_index_of_frm1_y_addr(mipi_id); + frm1_addr_uv = get_dvp_reg_index_of_frm1_uv_addr(mipi_id); + } + + spin_lock(&cif->vbq_lock); + + if (!cif->interlaced_enable) { + temp_y_addr = vehicle_flinger_request_cif_buffer(); + if (temp_y_addr == 0) { + VEHICLE_INFO("%s,warnning request buffer failed\n", __func__); + spin_unlock(&cif->vbq_lock); + if (dummy_buf->vaddr) { + if (frame_ready == 0) { + rkcif_write_reg(cif, frm0_addr_y, dummy_buf->dma_addr); + rkcif_write_reg(cif, frm0_addr_uv, dummy_buf->dma_addr); + } else { + rkcif_write_reg(cif, frm1_addr_y, dummy_buf->dma_addr); + rkcif_write_reg(cif, frm1_addr_uv, dummy_buf->dma_addr); + } + VEHICLE_INFO("frame Drop to dummy buf\n"); + } else { + VEHICLE_INFO("dummy buf is null!\n"); + } + return -1; + } + temp_uv_addr = temp_y_addr + UV_OFFSET; + y_addr = temp_y_addr; + uv_addr = temp_uv_addr; + commit_buf = 0; + } else { + if ((cif->interlaced_counts % 2) == 0) { + temp_y_addr = vehicle_flinger_request_cif_buffer(); + if (temp_y_addr == 0) { + VEHICLE_INFO("%s,warnning request buffer failed\n", __func__); + spin_unlock(&cif->vbq_lock); + return -1; + } + temp_uv_addr = temp_y_addr + UV_OFFSET; + y_addr = temp_y_addr; + uv_addr = temp_uv_addr; + commit_buf = -1; //not ok yet + } else { + y_addr = temp_y_addr + cif->interlaced_offset; + //uv_addr = temp_uv_addr; + uv_addr = temp_uv_addr + cif->interlaced_offset; + commit_buf = 0; //even & odd field add + } + WARN_ON(y_addr == cif->interlaced_offset); + WARN_ON(uv_addr == cif->interlaced_offset); + } + + if (frame_ready == 0) { + rkcif_write_reg(cif, frm0_addr_y, y_addr); + rkcif_write_reg(cif, frm0_addr_uv, uv_addr); + cif->active[0] = temp_y_addr; + } else { + rkcif_write_reg(cif, frm1_addr_y, y_addr); + rkcif_write_reg(cif, frm1_addr_uv, uv_addr); + cif->active[1] = temp_y_addr; + } + cif->interlaced_counts++; + spin_unlock(&cif->vbq_lock); + + return commit_buf; +} + +/***************************** irq operation ******************************/ +//discard the first few frames to solve display abnormality after different model camera switch +static int drop_frames_number; +static irqreturn_t rk_camera_irq(int irq, void *data) +{ + struct vehicle_cif *cif = (struct vehicle_cif *)data; + u32 lastline, lastpix, ctl; + u32 cif_frmst, frmid, int_en; + unsigned int intstat, i = 0xff; + int frame_ready = 0; + int frame_phase = 0; + unsigned long addr; + int mipi_id = 0; + + if (drop_frames_number > 0) { + VEHICLE_INFO("%s discard the first few frames!\n", __func__); + drop_frames_number--; + goto IRQ_EXIT; + } + + VEHICLE_DG("%s enter, cifirq_normal_idx(%ld) cif->frame_idx(%d)!\n", __func__, + cif->irqinfo.cifirq_normal_idx, cif->frame_idx); + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + if (!cif->stopping) { + if (cif->irqinfo.cifirq_normal_idx == cif->frame_idx) { + cif->irqinfo.cifirq_abnormal_idx++; + } else { + cif->irqinfo.cifirq_normal_idx = cif->frame_idx; + cif->irqinfo.cifirq_abnormal_idx = 0; + } + } + + intstat = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_INTSTAT); + lastline = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID0_1); + + /* clear all interrupts that has been triggered */ + rkcif_write_reg(cif, CIF_REG_MIPI_LVDS_INTSTAT, intstat); + + /* when not detect new FRAME_END continue over 5 irq, reset, it's abnormal */ + if (cif->irqinfo.cifirq_abnormal_idx >= 5) { + VEHICLE_DGERR( + "ERROR: cifirq_abnormal_idx reach(%ld) consecutive, do reset work!!\n", + cif->irqinfo.cifirq_abnormal_idx); +// mod_delayed_work(system_wq, &cif->work, +// msecs_to_jiffies(1)); + cif->irqinfo.cifirq_abnormal_idx = 0; + vehicle_cif_stat_change_notify(); + goto IRQ_EXIT; + } + + if (intstat & CSI_FIFO_OVERFLOW) { + cif->irqinfo.csi_overflow_cnt++; + VEHICLE_DGERR( + "ERROR: csi fifo overflow, intstat:0x%x, lastline:%d!!\n", + intstat, lastline); + goto IRQ_EXIT; + } + + if (intstat & CSI_BANDWIDTH_LACK) { + cif->irqinfo.csi_bwidth_lack_cnt++; + VEHICLE_DGERR( + "ERROR: csi bandwidth lack, intstat:0x%x!!\n", + intstat); + if (cif->irqinfo.csi_bwidth_lack_cnt >= 5) { + //do reset work +// mod_delayed_work(system_wq, &cif->work, +// msecs_to_jiffies(1)); + } + goto IRQ_EXIT; + } + + if (intstat & CSI_ALL_ERROR_INTEN) { + cif->irqinfo.all_err_cnt++; + VEHICLE_DGERR( + "ERROR: CSI_ALL_ERROR_INTEN:0x%x!!\n", intstat); + goto IRQ_EXIT; + } + + /* if do not reach frame dma end, return irq */ + mipi_id = vehicle_cif_csi2_g_mipi_id(intstat); + if (mipi_id < 0) + goto IRQ_EXIT; + + for (i = 0; i < RKCIF_MAX_STREAM_MIPI; i++) { + mipi_id = vehicle_cif_csi2_g_mipi_id(intstat); + + VEHICLE_DG(" i(%d) mipi_id(%d)\n", i, mipi_id); + if (mipi_id < 0) + continue; + + if (cif->stopping) { + vehicle_cif_csi2_s_stream(cif, 0, V4L2_MBUS_CSI2_DPHY); + cif->stopping = false; + wake_up(&cif->wq_stopped); + continue; + } + + if (cif->state != RKCIF_STATE_STREAMING) + continue; + + switch (mipi_id) { + case RKCIF_STREAM_MIPI_ID0: + frame_phase = SW_FRM_END_ID0(intstat); + intstat &= ~CSI_FRAME_END_ID0; + break; + case RKCIF_STREAM_MIPI_ID1: + frame_phase = SW_FRM_END_ID1(intstat); + intstat &= ~CSI_FRAME_END_ID1; + break; + case RKCIF_STREAM_MIPI_ID2: + frame_phase = SW_FRM_END_ID2(intstat); + intstat &= ~CSI_FRAME_END_ID2; + break; + case RKCIF_STREAM_MIPI_ID3: + frame_phase = SW_FRM_END_ID3(intstat); + intstat &= ~CSI_FRAME_END_ID3; + break; + } + + if (frame_phase & CIF_CSI_FRAME1_READY) + frame_ready = 1; + else if (frame_phase & CIF_CSI_FRAME0_READY) + frame_ready = 0; + + addr = cif->active[frame_ready]; + if (vehicle_cif_next_buffer(cif, frame_ready, mipi_id) < 0) + VEHICLE_DG("cif_nex_buffer error, do not commit %lx\n", addr); + else + vehicle_flinger_commit_cif_buffer(addr); + } + cif->frame_idx++; + } else { + intstat = rkcif_read_reg(cif, CIF_REG_DVP_INTSTAT); + cif_frmst = rkcif_read_reg(cif, CIF_REG_DVP_FRAME_STATUS); + lastline = rkcif_read_reg(cif, CIF_REG_DVP_LAST_LINE); + lastline = CIF_FETCH_Y_LAST_LINE(lastline); + lastpix = rkcif_read_reg(cif, CIF_REG_DVP_LAST_PIX); + lastpix = CIF_FETCH_Y_LAST_LINE(lastpix); + ctl = rkcif_read_reg(cif, CIF_REG_DVP_CTRL); + VEHICLE_DG("lastline:%d, lastpix:%d, ctl:%d\n", + lastline, lastpix, ctl); + + rkcif_write_reg(cif, CIF_REG_DVP_INTSTAT, intstat); + + if ((intstat & LINE_INT_END) && !(intstat & (FRAME_END))) { + if ((intstat & (PRE_INF_FRAME_END | PST_INF_FRAME_END)) == 0x0) { + if ((intstat & INTSTAT_ERR) == 0x0) { + int_en = rkcif_read_reg(cif, CIF_REG_DVP_INTEN); + int_en &= ~LINE_INT_EN; + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, int_en); + } + } + } + + /* 0. error process */ + if (cif_irq_error_process(cif, intstat) < 0) { + VEHICLE_DGERR("irq error, to do... reset, intstat=%x\n", intstat); +// mod_delayed_work(system_wq, &cif->work, +// msecs_to_jiffies(1)); + vehicle_cif_stat_change_notify(); + goto IRQ_EXIT; + } + + /* There are two irqs enabled: + * - PST_INF_FRAME_END: cif FIFO is ready, + * this is prior to FRAME_END + * - FRAME_END: cif has saved frame to memory, + * a frame ready + */ + if ((intstat & PST_INF_FRAME_END)) { + cif->irqinfo.cifirq_idx++; + if (cif->stopping) { + /* To stop CIF ASAP, before FRAME_END irq */ + vehicle_cif_s_stream(cif, 0); + cif->stopping = false; + wake_up(&cif->wq_stopped); + goto IRQ_EXIT; + } + } + + if ((intstat & FRAME_END)) { + int_en = rkcif_read_reg(cif, CIF_REG_DVP_INTEN); + int_en |= LINE_INT_EN; + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, int_en); + + if (cif->stopping) { + vehicle_cif_s_stream(cif, 0); + cif->stopping = false; + wake_up(&cif->wq_stopped); + goto IRQ_EXIT; + } + + frmid = CIF_GET_FRAME_ID(cif_frmst); + if ((cif_frmst == 0xfffd0002) || (cif_frmst == 0xfffe0002)) { + VEHICLE_DG("frmid:%d, frmstat:0x%x\n", + frmid, cif_frmst); + rkcif_write_reg(cif, CIF_REG_DVP_FRAME_STATUS, + FRAME_STAT_CLS); + } + + if ((!(cif_frmst & CIF_F0_READY) && !(cif_frmst & CIF_F1_READY))) { + VEHICLE_DG("err f0 && f1 not ready\n"); + cif_capture_en(cif->base, 0); + rkcif_write_reg(cif, CIF_REG_DVP_INTEN, 0); + mod_delayed_work(system_wq, &cif->work, + msecs_to_jiffies(1)); + goto IRQ_EXIT; + } + + if (cif_frmst & CIF_F0_READY) + frame_ready = 0; + else + frame_ready = 1; + addr = cif->active[frame_ready]; + if (vehicle_cif_next_buffer(cif, frame_ready, mipi_id) < 0) + CIF_DG("cif_nex_buffer error, do not commit %lx\n", addr); + else + vehicle_flinger_commit_cif_buffer(addr); + cif->frame_idx++; + } + } + cif->irqinfo.all_frm_end_cnt++; + +IRQ_EXIT: + return IRQ_HANDLED; +} + +static irqreturn_t rk_camera_irq_v1(int irq, void *data) +{ + struct vehicle_cif *cif = (struct vehicle_cif *)data; + u32 lastline; + unsigned int intstat, i = 0xff, bak_intstat = 0; + int frame_ready = 0; + int frame_phase = 0; + unsigned long addr; + int mipi_id = 0; + + if (drop_frames_number > 0) { + VEHICLE_INFO("%s discard the first few frames!\n", __func__); + drop_frames_number--; + goto IRQ_EXIT; + } + + VEHICLE_DG("%s enter, cifirq_normal_idx(%ld) cif->frame_idx(%d)!\n", __func__, + cif->irqinfo.cifirq_normal_idx, cif->frame_idx); + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + if (!cif->stopping) { + if (cif->irqinfo.cifirq_normal_idx == cif->frame_idx) { + cif->irqinfo.cifirq_abnormal_idx++; + } else { + cif->irqinfo.cifirq_normal_idx = cif->frame_idx; + cif->irqinfo.cifirq_abnormal_idx = 0; + } + } + + intstat = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_INTSTAT); + lastline = rkcif_read_reg(cif, CIF_REG_MIPI_LVDS_LINE_LINE_CNT_ID0_1); + + /* clear all interrupts that has been triggered */ + if (intstat) { + bak_intstat = intstat; + VEHICLE_DG("%s bak_intstat = %d!\n", __func__, bak_intstat); + rkcif_write_reg(cif, CIF_REG_MIPI_LVDS_INTSTAT, intstat); + } else { + goto IRQ_EXIT; + } + + /* when not detect new FRAME_END continue over 5 irq, reset, it's abnormal */ + if (cif->irqinfo.cifirq_abnormal_idx >= 5) { + VEHICLE_DGERR( + "ERROR: cifirq_abnormal_idx reach(%ld) consecutive, do reset work!!\n", + cif->irqinfo.cifirq_abnormal_idx); + cif->irqinfo.cifirq_abnormal_idx = 0; + vehicle_cif_stat_change_notify(); + goto IRQ_EXIT; + } + + if (intstat & CSI_SIZE_ERR) { + cif->irqinfo.csi_size_err_cnt++; + VEHICLE_DGERR("ERROR: csi size error, intstat:0x%x, lastline:%d!!\n", + intstat, lastline); + goto IRQ_EXIT; + } + + if (intstat & CSI_FIFO_OVERFLOW_V1) { + cif->irqinfo.csi_overflow_cnt++; + VEHICLE_DGERR("ERROR: csi fifo overflow, intstat:0x%x, lastline:%d!!\n", + intstat, lastline); + goto IRQ_EXIT; + } + + if (intstat & CSI_BANDWIDTH_LACK_V1) { + cif->irqinfo.csi_bwidth_lack_cnt++; + VEHICLE_DGERR("ERROR: csi bandwidth lack, intstat:0x%x!!\n", + intstat); + goto IRQ_EXIT; + } + + if (intstat & CSI_ALL_ERROR_INTEN_V1) { + cif->irqinfo.all_err_cnt++; + VEHICLE_DGERR("ERROR: CSI_ALL_ERROR_INTEN:0x%x!!\n", intstat); + goto IRQ_EXIT; + } + + /* if do not reach frame dma end, return irq */ + mipi_id = vehicle_cif_csi2_g_mipi_id(intstat); + if (mipi_id < 0) + goto IRQ_EXIT; + + for (i = 0; i < RKCIF_MAX_STREAM_MIPI; i++) { + mipi_id = vehicle_cif_csi2_g_mipi_id(intstat); + + VEHICLE_DG(" i(%d) mipi_id(%d)\n", i, mipi_id); + if (mipi_id < 0) + continue; + + if (cif->stopping) { + vehicle_cif_csi2_s_stream_v1(cif, 0, V4L2_MBUS_CSI2_DPHY); + cif->stopping = false; + wake_up(&cif->wq_stopped); + continue; + } + + if (cif->state != RKCIF_STATE_STREAMING) + continue; + + switch (mipi_id) { + case RKCIF_STREAM_MIPI_ID0: + frame_phase = SW_FRM_END_ID0(intstat); + intstat &= ~CSI_FRAME_END_ID0; + break; + case RKCIF_STREAM_MIPI_ID1: + frame_phase = SW_FRM_END_ID1(intstat); + intstat &= ~CSI_FRAME_END_ID1; + break; + case RKCIF_STREAM_MIPI_ID2: + frame_phase = SW_FRM_END_ID2(intstat); + intstat &= ~CSI_FRAME_END_ID2; + break; + case RKCIF_STREAM_MIPI_ID3: + frame_phase = SW_FRM_END_ID3(intstat); + intstat &= ~CSI_FRAME_END_ID3; + break; + } + + if (frame_phase & CIF_CSI_FRAME1_READY) + frame_ready = 1; + else if (frame_phase & CIF_CSI_FRAME0_READY) + frame_ready = 0; + + addr = cif->active[frame_ready]; + if (vehicle_cif_next_buffer(cif, frame_ready, mipi_id) < 0) + VEHICLE_DGERR("cif_nex_buffer error, do not commit %lx\n", addr); + else + vehicle_flinger_commit_cif_buffer(addr); + } + cif->frame_idx++; + } else { + int ch_id; + + intstat = rkcif_read_reg(cif, CIF_REG_DVP_INTSTAT); + + rkcif_write_reg(cif, CIF_REG_DVP_INTSTAT, intstat); + + if (intstat & DVP_SIZE_ERR) { + cif->irqinfo.dvp_size_err_cnt++; + VEHICLE_DGERR("dvp size err intstat 0x%x\n", intstat); + } + + if (intstat & DVP_FIFO_OVERFLOW) { + cif->irqinfo.dvp_overflow_cnt++; + VEHICLE_DGERR("dvp fifo overflow err intstat 0x%x\n", intstat); + } + + if (intstat & DVP_BANDWIDTH_LACK) { + cif->irqinfo.dvp_bwidth_lack_cnt++; + VEHICLE_DGERR("dvp bandwidth lack err intstat 0x%x\n", intstat); + } + + if (intstat & INTSTAT_ERR_RK3588) { + cif->irqinfo.all_err_cnt++; + VEHICLE_DGERR("ERROR: DVP_ALL_ERROR_INTEN:0x%x!!\n", intstat); + } + for (i = 0; i < RKCIF_MAX_STREAM_DVP; i++) { + ch_id = rkcif_dvp_g_ch_id_by_fe(intstat); + + if (ch_id < 0) + continue; + + if (cif->stopping) { + vehicle_cif_s_stream(cif, 0); + cif->stopping = false; + wake_up(&cif->wq_stopped); + continue; + } + + if (cif->state != RKCIF_STATE_STREAMING) + continue; + + switch (ch_id) { + case RKCIF_STREAM_MIPI_ID0: + frame_phase = SW_FRM_END_ID0(intstat); + intstat &= ~DVP_ALL_END_ID0; + break; + case RKCIF_STREAM_MIPI_ID1: + frame_phase = SW_FRM_END_ID1(intstat); + intstat &= ~DVP_ALL_END_ID1; + break; + case RKCIF_STREAM_MIPI_ID2: + frame_phase = SW_FRM_END_ID2(intstat); + intstat &= ~DVP_ALL_END_ID2; + break; + case RKCIF_STREAM_MIPI_ID3: + frame_phase = SW_FRM_END_ID3(intstat); + intstat &= ~DVP_ALL_END_ID3; + break; + } + + if (frame_phase & CIF_F0_READY) + frame_ready = 0; + else + frame_ready = 1; + + addr = cif->active[frame_ready]; + if (vehicle_cif_next_buffer(cif, frame_ready, ch_id) < 0) + VEHICLE_DGERR("cif_nex_buffer error, do not commit %lx\n", addr); + else + vehicle_flinger_commit_cif_buffer(addr); + + cif->frame_idx++; + } + } + cif->irqinfo.all_frm_end_cnt++; + +IRQ_EXIT: + return IRQ_HANDLED; +} + +static irqreturn_t vehicle_csirx_irq1(int irq, void *data) +{ + struct vehicle_cif *cif = (struct vehicle_cif *)data; + struct csi2_dphy_hw *hw = cif->dphy_hw; + struct csi2_err_stats *err_list = NULL; + unsigned long err_stat = 0; + u32 val; + + val = read_reg(hw->csi2_base, CSIHOST_ERR1); + if (val) { + write_reg(hw->csi2_base, + CSIHOST_ERR1, 0x0); + + if (val & CSIHOST_ERR1_PHYERR_SPTSYNCHS) { + err_list = &hw->err_list[RK_CSI2_ERR_SOTSYN]; + err_list->cnt++; + VEHICLE_DGERR( + "ERR1: start of transmission error, reg: 0x%x,cnt:%d\n", + val, err_list->cnt); + } + + if (val & CSIHOST_ERR1_ERR_BNDRY_MATCH) { + err_list = &hw->err_list[RK_CSI2_ERR_FS_FE_MIS]; + err_list->cnt++; + VEHICLE_DGERR( + "ERR1: error matching frame start with frame end, reg: 0x%x,cnt:%d\n", + val, err_list->cnt); + } + + if (val & CSIHOST_ERR1_ERR_SEQ) { + err_list = &hw->err_list[RK_CSI2_ERR_FRM_SEQ_ERR]; + err_list->cnt++; + VEHICLE_DGERR("ERR1: incorrect frame sequence detected, reg: 0x%x,cnt:%d\n", + val, err_list->cnt); + } + + if (val & CSIHOST_ERR1_ERR_FRM_DATA) { + err_list = &hw->err_list[RK_CSI2_ERR_CRC_ONCE]; + err_list->cnt++; + VEHICLE_DGERR("ERR1: at least one crc error, reg: 0x%x\n,cnt:%d", + val, err_list->cnt); + } + + if (val & CSIHOST_ERR1_ERR_CRC) { + err_list = &hw->err_list[RK_CSI2_ERR_CRC]; + err_list->cnt++; + VEHICLE_DGERR("ERR1: crc errors, reg: 0x%x, cnt:%d\n", + val, err_list->cnt); + } + + if (val & CSIHOST_ERR1_ERR_ECC2) { + err_list = &hw->err_list[RK_CSI2_ERR_CRC]; + err_list->cnt++; + VEHICLE_DGERR("ERR1: ecc errors, reg: 0x%x, cnt:%d\n", + val, err_list->cnt); + } + + hw->err_list[RK_CSI2_ERR_ALL].cnt++; + err_stat = ((hw->err_list[RK_CSI2_ERR_FS_FE_MIS].cnt & 0xff) << 8) | + ((hw->err_list[RK_CSI2_ERR_ALL].cnt) & 0xff); + VEHICLE_INFO("%s: err_stat: %x\n", err_stat); + + } + + return IRQ_HANDLED; +} + +static irqreturn_t vehicle_csirx_irq2(int irq, void *data) +{ + struct vehicle_cif *cif = (struct vehicle_cif *)data; + struct csi2_dphy_hw *hw = cif->dphy_hw; + u32 val; + + val = read_reg(hw->csi2_base, CSIHOST_ERR2); + if (val) { + if (val & CSIHOST_ERR2_PHYERR_ESC) + VEHICLE_DGERR("ERR2: escape entry error(ULPM), reg: 0x%x\n", val); + if (val & CSIHOST_ERR2_PHYERR_SOTHS) + VEHICLE_DGERR( + "ERR2: start of transmission error, reg: 0x%x\n", val); + if (val & CSIHOST_ERR2_ECC_CORRECTED) + VEHICLE_DGERR( + "ERR2: header error detected and corrected, reg: 0x%x\n", val); + if (val & CSIHOST_ERR2_ERR_ID) + VEHICLE_DGERR( + "ERR2: unrecognized data type detected, reg: 0x%x\n", val); + if (val & CSIHOST_ERR2_PHYERR_CODEHS) + VEHICLE_DGERR("ERR2: receive error code, reg: 0x%x\n", val); + } + + return IRQ_HANDLED; +} + +int vehicle_cif_reverse_open(struct vehicle_cfg *v_cfg) +{ + int ret = 0; + struct vehicle_cif *cif = g_cif; + + if (!cif) + return -ENODEV; + + mutex_lock(&cif->stream_lock); + memcpy(&cif->cif_cfg, v_cfg, sizeof(struct vehicle_cfg)); + ret = pm_runtime_get_sync(cif->dev); + if (ret < 0) { + pm_runtime_put_noidle(cif->dev); + VEHICLE_DGERR("%s pm_runtime_get_sync failed\n", __func__); + goto exit; + } + + /*get dcphy param*/ + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588_DCPHY) { + if (cif->cif_cfg.dphy_param) { + cif->dphy_hw->dphy_param = cif->cif_cfg.dphy_param; + dev_info(cif->dev, "-----get dphy param from sensor----\n"); + } else { + cif->dphy_hw->dphy_param = &rk3588_dcphy_param; + dev_info(cif->dev, "fail to get dphy param, used default value\n"); + } + } + /* set ddr fix freq */ + rockchip_set_system_status(SYS_STATUS_CIF0); + vehicle_cif_hw_soft_reset(cif); + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + /* 0. set mipi-dphy data rate */ + cif->dphy_hw->data_rate_mbps = cif->cif_cfg.mipi_freq * 2 / 1000 / 1000; + + /* 0. set csi2 & dphy clk */ + vehicle_csi2_hw_soft_reset(cif); + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588) + vehicle_csi2_dphy_hw_do_reset(cif); + + if (!cif->dphy_hw->on) + vehicle_csi2_clk_ctrl(cif, 1); + + /* 1. stream setup */ + cif_csi_stream_setup(cif); + + /* 2. create dummy buf */ + ret = vehicle_cif_create_dummy_buf(cif); + if (ret < 0) + VEHICLE_DGERR("Failed to create dummy_buf, %d\n", ret); + + /* 3. cif init buffer */ + if (vehicle_cif_init_buffer(cif, 1, cif->channels[0].id) < 0) + goto exit; + + /* 4. dump cif regs */ + vehicle_cif_csi2_dump_regs(cif); + + /* 5. start stream */ + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) + vehicle_cif_csi2_s_stream_v1(cif, 1, V4L2_MBUS_CSI2_DPHY); + else + vehicle_cif_csi2_s_stream(cif, 1, V4L2_MBUS_CSI2_DPHY); + + } else { + /* 1. stream setup */ + cif_stream_setup(cif); + + /* 2. create dummy buf */ + ret = vehicle_cif_create_dummy_buf(cif); + if (ret < 0) + VEHICLE_DGERR("Failed to create dummy_buf, %d\n", ret); + + /* 2. cif init buffer */ + if (vehicle_cif_init_buffer(cif, 1, 0) < 0) + goto exit; + + /* 3. enable interrupts */ + if (cif->chip_id < CHIP_RK3588_VEHICLE_CIF) + cif_interrupt_setup(cif); + + /* 4. dump cif regs */ + vehicle_cif_dvp_dump_regs(cif); + + /* 5. start stream */ + vehicle_cif_s_stream(cif, 1); + } + + cif->stopping = false; + drop_frames_number = cif->drop_frames; + + mutex_unlock(&cif->stream_lock); + + return 0; + +exit: + mutex_unlock(&cif->stream_lock); + return -1; +} + +int vehicle_cif_reverse_close(void) +{ + int ret = 0; + struct vehicle_cif *cif = g_cif; + + if (!cif) + return -ENODEV; + + mutex_lock(&cif->stream_lock); + + VEHICLE_DG("%s cif reverse start closing\n", __func__); + cif->stopping = true; + cancel_delayed_work_sync(&(cif->work)); + flush_delayed_work(&(cif->work)); + + ret = wait_event_timeout(cif->wq_stopped, + cif->state != RKCIF_STATE_STREAMING, + msecs_to_jiffies(100)); + if (!ret) { + VEHICLE_DGERR("%s wait stream stop timeout!\n", __func__); + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) + vehicle_cif_csi2_s_stream_v1(cif, 0, V4L2_MBUS_CSI2_DPHY); + else + vehicle_cif_csi2_s_stream(cif, 0, V4L2_MBUS_CSI2_DPHY); + } else { + vehicle_cif_s_stream(cif, 0); + } + //cif->stopping = false; + } + if (cif->cif_cfg.type == V4L2_MBUS_CSI2_DPHY) { + vehicle_cif_csi_stream_stop(cif); + vehicle_csi2_hw_soft_reset(cif); + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588) + vehicle_csi2_dphy_hw_do_reset(cif); + if (cif->dphy_hw->on) + vehicle_csi2_clk_ctrl(cif, 0); + } + + vehicle_cif_destroy_dummy_buf(cif); + //vehicle_csi2_hw_soft_reset(cif); + //vehicle_cif_hw_soft_reset(cif); + rockchip_clear_system_status(SYS_STATUS_CIF0); + mutex_unlock(&cif->stream_lock); + cif->stopping = false; + + return 0; +} + +static void vehicle_cif_dphy_get_node(struct vehicle_cif *cif) +{ + struct device_node *node = NULL; + struct device_node *cp = NULL; + struct device *dev = cif->dev; + const char *status = NULL; + + node = of_parse_phandle(dev->of_node, "rockchip,cif-phy", 0); + if (!node) { + VEHICLE_DGERR("get cif-phy dts failed\n"); + return; + } + + for_each_child_of_node(node, cp) { + of_property_read_string(cp, "status", &status); + if (status && !strcmp(status, "disabled")) + continue; + else + cif->phy_node = cp; + VEHICLE_INFO("status: %s %s\n", cp->name, status); + } +} + +static int cif_parse_dt(struct vehicle_cif *cif) +{ + struct device *dev = cif->dev; + struct device_node *node; + struct device_node *phy_node = cif->phy_node; + struct device_node *cif_node; + struct device_node *cis2_node; + + if (of_property_read_u32(dev->of_node, "cif,drop-frames", + &cif->drop_frames)) { + VEHICLE_INFO("%s:Get cif, drop-frames failed!\n", __func__); + cif->drop_frames = 0; //default drop frames; + } + + if (of_property_read_u32(dev->of_node, "cif,chip-id", + &cif->chip_id)) { + VEHICLE_INFO("%s:Get cif, chip_id failed!\n", __func__); + cif->chip_id = 1; //default rk3588; + } + + cif_node = of_parse_phandle(dev->of_node, "rockchip,cif", 0); + cif->base = (char *)of_iomap(cif_node, 0); + + node = of_parse_phandle(dev->of_node, "rockchip,cru", 0); + cif->cru_base = of_iomap(node, 0); + + node = of_parse_phandle(dev->of_node, "rockchip,grf", 0); + cif->grf_base = of_iomap(node, 0); + + cif->regmap_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); + if (IS_ERR(cif->regmap_grf)) + VEHICLE_DGERR("unable to get rockchip,grf\n"); + + cif->irq = irq_of_parse_and_map(cif_node, 0); + if (cif->irq < 0) { + VEHICLE_DGERR("%s: request cif irq failed\n", __func__); + iounmap(cif->base); + iounmap(cif->cru_base); + iounmap(cif->grf_base); + return -ENODEV; + } + + if (of_property_read_u32(phy_node, "csihost-idx", &cif->csi_host_idx)) { + VEHICLE_INFO("Get %s csihost-idx failed! sensor link to dvp!!\n", + phy_node->name); + cif->inf_id = RKCIF_DVP; + } else { + cif->inf_id = RKCIF_MIPI_LVDS; + VEHICLE_INFO("sensor link to %s!!\n", phy_node->name); + } + + if (cif->inf_id == RKCIF_MIPI_LVDS) { + if (!(cif->csi_host_idx == RKCIF_MIPI0_CSI2 || + cif->csi_host_idx == RKCIF_MIPI1_CSI2)) { + node = of_parse_phandle(phy_node, "rockchip,csi2-dphy", 0); + cif->csi2_dphy_base = of_iomap(node, 0); + + cif->regmap_dphy_grf = + syscon_regmap_lookup_by_phandle(phy_node, "rockchip,dphy-grf"); + if (IS_ERR(cif->regmap_dphy_grf)) + VEHICLE_INFO("unable to get rockchip,dphy-grf\n"); + } + + cis2_node = of_parse_phandle(phy_node, "rockchip,csi2", 0); + cif->csi2_base = of_iomap(cis2_node, 0); + + cif->csi2_irq1 = irq_of_parse_and_map(cis2_node, 0); + if (cif->csi2_irq1 < 0) { + VEHICLE_DGERR("%s: request csi-intr1 failed\n", __func__); + iounmap(cif->base); + iounmap(cif->cru_base); + iounmap(cif->grf_base); + iounmap(cif->csi2_dphy_base); + iounmap(cif->csi2_base); + return -ENODEV; + } + + cif->csi2_irq2 = irq_of_parse_and_map(cis2_node, 1); + if (cif->csi2_irq2 < 0) { + VEHICLE_DGERR("%s: request csi-intr2 failed\n", __func__); + iounmap(cif->base); + iounmap(cif->cru_base); + iounmap(cif->grf_base); + iounmap(cif->csi2_dphy_base); + iounmap(cif->csi2_base); + return -ENODEV; + } + } + + VEHICLE_DG("%s, drop_frames = %d\n", __func__, cif->drop_frames); + + return 0; +} + +int vehicle_cif_init_mclk(struct vehicle_cif *cif) +{ + struct device *dev = cif->dev; + struct rk_cif_clk *clk = &cif->clk; + + /* sensor MCLK: + * current use CLK_CIF_OUT + */ + vehicle_cif_dphy_get_node(cif); + clk->xvclk = of_clk_get_by_name(cif->phy_node, "xvclk"); + if (IS_ERR(clk->xvclk)) { + dev_err(dev, "Failed to get sensor xvclk\n"); + return -EINVAL; + } + + rkcif_s_mclk(cif, 1, 24000000); + VEHICLE_INFO("%s(%d): set sensor MCLK rate 24MHZ OK!\n", __func__, __LINE__); + + return 0; +} + +static int vehicle_cif_deinit_mclk(struct vehicle_cif *cif) +{ + struct rk_cif_clk *clk = &cif->clk; + + /* release sensor MCLK: + * current use CLK_CIF_OUT + */ + if (!IS_ERR(clk->xvclk)) + clk_disable_unprepare(clk->xvclk); + clk_put(clk->xvclk); + + return 0; +} + +int vehicle_cif_init(struct vehicle_cif *cif) +{ + int ret; + struct device *dev; + struct rk_cif_clk *clk; + struct csi2_dphy_hw *dphy_hw; + struct clk *tmp_cif_clk = NULL; + int i; + int inf_id; + + if (!cif) + return -ENODEV; + + dev = cif->dev; + clk = &cif->clk; + g_cif = cif; + + /* 0. dts parse */ + if (cif_parse_dt(cif) < -1) { + VEHICLE_DGERR("%s: cif_parse_dt failed\n", __func__); + return -ENODEV; + } + + inf_id = cif->inf_id; + if (inf_id == RKCIF_MIPI_LVDS) { + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) { + if (cif->csi_host_idx == RKCIF_MIPI0_CSI2 || + cif->csi_host_idx == RKCIF_MIPI1_CSI2) + dphy_hw = &rk3588_csi2_dcphy_hw; + else + dphy_hw = &rk3588_csi2_dphy_hw; + } else { + dphy_hw = &rk3568_csi2_dphy_hw; + } + } + + /* 1. cif/csi2-dphy/csi2 clk setup */ + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) { + clk->clks_num = ARRAY_SIZE(rk3588_cif_clks); + clk->rsts_num = ARRAY_SIZE(rk3588_cif_rsts); + } else { + clk->clks_num = ARRAY_SIZE(rk3568_cif_clks); + clk->rsts_num = ARRAY_SIZE(rk3568_cif_rsts); + } + + if (inf_id == RKCIF_MIPI_LVDS) { + cif->dphy_hw = dphy_hw; + dphy_hw->dev = cif->dev; + /*get mipi dcphy*/ + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588_DCPHY) { + struct phy *dcphy = NULL; + struct samsung_mipi_dcphy *dcphy_hw = NULL; + + dcphy = of_phy_get(cif->phy_node, "dcphy"); + if (IS_ERR(dcphy)) { + ret = PTR_ERR(dcphy); + dev_err(dev, "failed to get mipi dcphy: %d\n", ret); + return ret; + } + dcphy_hw = phy_get_drvdata(dcphy); + dcphy_hw->dphy_vehicle[dcphy_hw->dphy_vehicle_num] = cif->dphy_hw; + dcphy_hw->dphy_vehicle_num++; + cif->dphy_hw->samsung_phy = dcphy_hw; + } + /* csi2 mipidphy rsts */ + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588) { + for (i = 0; i < dphy_hw->num_dphy_rsts; i++) { + struct reset_control *rst = NULL; + + rst = of_reset_control_get(cif->phy_node, dphy_hw->dphy_rsts[i]); + if (IS_ERR(rst)) { + dev_err(dev, "failed to get %s\n", dphy_hw->dphy_rsts[i]); + return PTR_ERR(rst); + } + dphy_hw->dphy_rst[i] = rst; + } + } else { + dev_info(dev, "use mipi dcphy, no need request rst\n"); + } + + /* csi2 mipidphy clks */ + for (i = 0; i < dphy_hw->num_dphy_clks; i++) { + struct clk *tmp_clk = + of_clk_get_by_name(cif->phy_node, dphy_hw->dphy_clks[i].id); + + if (IS_ERR(tmp_clk)) { + dev_err(dev, "failed to get %s\n", dphy_hw->dphy_clks[i].id); + return PTR_ERR(tmp_clk); + } + dev_info(dev, "clk get %s\n", dphy_hw->dphy_clks[i].id); + dphy_hw->dphy_clks[i].clk = tmp_clk; + } + + /* csi2 clks */ + for (i = 0; i < dphy_hw->num_csi2_clks; i++) { + struct clk *tmp_clk = + of_clk_get_by_name(cif->phy_node, dphy_hw->csi2_clks[i].id); + + if (IS_ERR(tmp_clk)) { + dev_err(dev, "failed to get %s\n", dphy_hw->csi2_clks[i].id); + return PTR_ERR(tmp_clk); + } + dev_info(dev, "clk get %s\n", dphy_hw->csi2_clks[i].id); + dphy_hw->csi2_clks[i].clk = tmp_clk; + } + + /* csi2 rsts */ + for (i = 0; i < dphy_hw->num_csi2_rsts; i++) { + struct reset_control *rst = NULL; + + rst = of_reset_control_get(cif->phy_node, dphy_hw->csi2_rsts[i]); + if (IS_ERR(rst)) { + dev_err(dev, "failed to get %s\n", dphy_hw->csi2_rsts[i]); + return PTR_ERR(rst); + } + dphy_hw->csi2_rst[i] = rst; + } + dphy_hw->on = false; + } + /* vicap clks */ + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) { + for (i = 0; i < clk->clks_num; i++) { + tmp_cif_clk = devm_clk_get(dev, rk3588_cif_clks[i]); + + if (IS_ERR(tmp_cif_clk)) { + dev_err(dev, "failed to get %s\n", rk3588_cif_clks[i]); + return PTR_ERR(tmp_cif_clk); + } + clk->clks[i] = tmp_cif_clk; + clk->on = false; + } + } else { + for (i = 0; i < clk->clks_num; i++) { + tmp_cif_clk = devm_clk_get(dev, rk3568_cif_clks[i]); + + if (IS_ERR(tmp_cif_clk)) { + dev_err(dev, "failed to get %s\n", rk3568_cif_clks[i]); + return PTR_ERR(tmp_cif_clk); + } + clk->clks[i] = tmp_cif_clk; + clk->on = false; + } + } + + /* vicap rsts */ + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) { + for (i = 0; i < clk->rsts_num; i++) { + struct reset_control *rst = NULL; + + if (rk3568_cif_rsts[i]) + rst = devm_reset_control_get(dev, rk3588_cif_rsts[i]); + if (IS_ERR(rst)) { + dev_err(dev, "failed to get %s\n", rk3588_cif_rsts[i]); + return PTR_ERR(rst); + } + clk->cif_rst[i] = rst; + } + } else { + for (i = 0; i < clk->rsts_num; i++) { + struct reset_control *rst = NULL; + + if (rk3568_cif_rsts[i]) + rst = devm_reset_control_get(dev, rk3568_cif_rsts[i]); + if (IS_ERR(rst)) { + dev_err(dev, "failed to get %s\n", rk3568_cif_rsts[i]); + return PTR_ERR(rst); + } + clk->cif_rst[i] = rst; + } + } + + /* 2. set cif clk & sensor mclk */ + rk_cif_mclk_ctrl(cif, 1, 24000000); + INIT_DELAYED_WORK(&cif->work, vehicle_cif_reset_work_func); + + if (inf_id == RKCIF_MIPI_LVDS) + /* 2. set csi2 & dphy clk */ + if (!cif->dphy_hw->on) + vehicle_csi2_clk_ctrl(cif, 1); + + /* 3. request cif irq & mipi csi irq1-2 */ + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) { + ret = request_irq(cif->irq, rk_camera_irq_v1, IRQF_SHARED, "vehicle_cif", cif); + if (ret < 0) { + VEHICLE_DGERR("request cif irq failed!\n"); + return -EINVAL; + } + } else { + ret = request_irq(cif->irq, rk_camera_irq, IRQF_SHARED, "vehicle_cif", cif); + if (ret < 0) { + VEHICLE_DGERR("request cif irq failed!\n"); + return -EINVAL; + } + } + + VEHICLE_DG("%s(%d):\n", __func__, __LINE__); + + if (inf_id == RKCIF_MIPI_LVDS) { + ret = request_irq(cif->csi2_irq1, vehicle_csirx_irq1, + IRQF_SHARED, "vehicle_csi_intr1", cif); + if (ret < 0) { + VEHICLE_DGERR("request csirx irq1 failed!\n"); + return -EINVAL; + } + + ret = request_irq(cif->csi2_irq2, vehicle_csirx_irq2, + IRQF_SHARED, "vehicle_csi_intr2", cif); + if (ret < 0) { + VEHICLE_DGERR("request csirx irq2 failed!\n"); + return -EINVAL; + } + } + /* 4. set cif regs */ + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) + cif->cif_regs = rk3588_cif_regs; + else + cif->cif_regs = rk3568_cif_regs; + + if (inf_id == RKCIF_MIPI_LVDS) { + /* 5. set csi2-mipi-dphy reg */ + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588) + cif->dphy_hw->csi2_dphy_base = cif->csi2_dphy_base; + + /* 7. set mipi-csi2 reg */ + cif->dphy_hw->csi2_base = cif->csi2_base; + + /* 8. set dphy grf regmap */ + if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) { + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588) { + cif->dphy_hw->regmap_grf = cif->regmap_dphy_grf; + cif->dphy_hw->regmap_sys_grf = cif->regmap_grf; + } + } else { + cif->dphy_hw->regmap_grf = cif->regmap_grf; + } + mutex_init(&dphy_hw->mutex); + } + /* 9. init waitqueue */ + atomic_set(&cif->reset_status, 0); + init_waitqueue_head(&cif->wq_stopped); + + spin_lock_init(&cif->vbq_lock); + + return 0; +} + +int vehicle_cif_deinit(struct vehicle_cif *cif) +{ + struct rk_cif_clk *clk = &cif->clk; + struct device *dev = cif->dev; + int i; + struct csi2_dphy_hw *dphy_hw = cif->dphy_hw; + int inf_id = cif->inf_id; + + // vehicle_cif_s_stream(cif, 0); + // vehicle_cif_do_stop_stream(cif); + + /* set csi2-dphy csi cif clk & sensor mclk */ + rk_cif_mclk_ctrl(cif, 0, 0); + if (inf_id == RKCIF_MIPI_LVDS) + if (cif->dphy_hw->on) + vehicle_csi2_clk_ctrl(cif, 0); + + /* release sensor MCLK */ + vehicle_cif_deinit_mclk(cif); + + /* vicap rsts release */ + for (i = 0; i < clk->rsts_num; i++) + reset_control_put(clk->cif_rst[i]); + + /* vicap clk release */ + for (i = 0; i < clk->clks_num; i++) + devm_clk_put(dev, clk->clks[i]); + + if (inf_id == RKCIF_MIPI_LVDS) { + /*dcphy put*/ + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588_DCPHY) { + struct samsung_mipi_dcphy *dcphy_hw = cif->dphy_hw->samsung_phy; + struct csi2_dphy_hw *csi2_dphy = NULL; + + for (i = 0; i < dcphy_hw->dphy_vehicle_num; i++) { + csi2_dphy = dcphy_hw->dphy_vehicle[i]; + if (csi2_dphy) { + dcphy_hw->dphy_vehicle[i] = NULL; + dcphy_hw->dphy_vehicle_num--; + break; + } + } + } + /* dphy clks release */ + for (i = 0; i < dphy_hw->num_dphy_clks; i++) + clk_put(dphy_hw->dphy_clks[i].clk); + /* dphy rsts release */ + if (cif->dphy_hw->chip_id == CHIP_ID_RK3588) { + for (i = 0; i < dphy_hw->num_dphy_rsts; i++) + reset_control_put(dphy_hw->dphy_rst[i]); + } + /* csi2 clks release */ + for (i = 0; i < dphy_hw->num_csi2_clks; i++) + clk_put(dphy_hw->csi2_clks[i].clk); + /* csi2 resets release */ + for (i = 0; i < dphy_hw->num_csi2_rsts; i++) + reset_control_put(dphy_hw->csi2_rst[i]); + + mutex_destroy(&dphy_hw->mutex); + } + + free_irq(cif->irq, cif); + if (inf_id == RKCIF_MIPI_LVDS) { + free_irq(cif->csi2_irq1, cif); + free_irq(cif->csi2_irq2, cif); + } + + return 0; +} diff --git a/drivers/video/rockchip/vehicle/vehicle_cif.h b/drivers/video/rockchip/vehicle/vehicle_cif.h new file mode 100644 index 000000000000..3e158683f36e --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_cif.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Vehicle driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ +#ifndef __VEHICLE_CIF_H +#define __VEHICLE_CIF_H + +#include "vehicle_cfg.h" +#include "vehicle_cif_regs.h" +#include "../../../media/platform/rockchip/cif/hw.h" +#include "../../../media/platform/rockchip/cif/dev.h" +#include + +enum vehicle_rkcif_chip_id { + CHIP_RK3568_VEHICLE_CIF = 0x0, + CHIP_RK3588_VEHICLE_CIF, +}; + +enum rkcif_csi_host_idx { + RKCIF_MIPI0_CSI2 = 0x0, + RKCIF_MIPI1_CSI2, + RKCIF_MIPI2_CSI2, + RKCIF_MIPI3_CSI2, + RKCIF_MIPI4_CSI2, + RKCIF_MIPI5_CSI2, +}; + +struct vehicle_rkcif_dummy_buffer { + void *vaddr; + dma_addr_t dma_addr; + u32 size; +}; + +struct rk_cif_clk { + /************clk************/ + struct clk *clks[RKCIF_MAX_BUS_CLK]; + struct clk *xvclk; + int clks_num; + /************reset************/ + struct reset_control *cif_rst[RKCIF_MAX_RESET]; + int rsts_num; + /* spinlock_t lock; */ + bool on; +}; + +struct rk_cif_irqinfo { + unsigned int irq; + unsigned long cifirq_idx; + unsigned long cifirq_normal_idx; + unsigned long cifirq_abnormal_idx; + unsigned long dmairq_idx; + + /* @csi_overflow_cnt: count of csi overflow irq + * @csi_bwidth_lack_cnt: count of csi bandwidth lack irq + * @dvp_bus_err_cnt: count of dvp bus err irq + * @dvp_overflow_cnt: count dvp overflow irq + * @dvp_line_err_cnt: count dvp line err irq + * @dvp_pix_err_cnt: count dvp pix err irq + * @all_frm_end_cnt: raw frame end count + * @all_err_cnt: all err count + * @ + */ + + u64 csi_overflow_cnt; + u64 csi_bwidth_lack_cnt; + u64 dvp_bus_err_cnt; + u64 dvp_overflow_cnt; + u64 dvp_line_err_cnt; + u64 dvp_pix_err_cnt; + u64 all_frm_end_cnt; + u64 all_err_cnt; + u64 dvp_size_err_cnt; + u64 dvp_bwidth_lack_cnt; + u64 csi_size_err_cnt; +}; + +#define RKCIF_MAX_CSI_CHANNEL 4 +struct vehicle_csi_channel_info { + unsigned char id; + unsigned char enable; /* capture enable */ + unsigned char vc; + unsigned char data_type; + unsigned char crop_en; + unsigned char cmd_mode_en; + unsigned char fmt_val; + unsigned int width; + unsigned int height; + unsigned int virtual_width; + unsigned int crop_st_x; + unsigned int crop_st_y; +}; + +struct vehicle_cif { + struct device *dev; + struct device_node *phy_node; + struct rk_cif_clk clk; + struct vehicle_cfg cif_cfg; + char *base; /*cif base addr*/ + //unsigned long cru_base; + //unsigned long grf_base; + void __iomem *cru_base; /*cru base addr*/ + void __iomem *grf_base; /*grf base addr*/ + void __iomem *csi2_dphy_base; /*csi2_dphy base addr*/ + void __iomem *csi2_base; /*csi2 base addr*/ + struct delayed_work work; + + bool is_enabled; + u32 frame_buf[MAX_BUF_NUM]; + u32 current_buf_index; + u32 last_buf_index; + u32 active[2]; + int irq; + int csi2_irq1; + int csi2_irq2; + int drop_frames; + struct rk_cif_irqinfo irqinfo; + const struct vehicle_cif_reg *cif_regs; + struct regmap *regmap_grf; + struct regmap *regmap_dphy_grf; + unsigned int frame_idx; + struct vehicle_rkcif_dummy_buffer dummy_buf; + struct csi2_dphy_hw *dphy_hw; + int num_channels; + int chip_id; + int inf_id; + unsigned int csi_host_idx; + struct vehicle_csi_channel_info channels[RKCIF_MAX_CSI_CHANNEL]; + spinlock_t vbq_lock; /* vfd lock */ + bool interlaced_enable; + unsigned int interlaced_offset; + unsigned int interlaced_counts; + unsigned long *interlaced_buffer; + atomic_t reset_status; + wait_queue_head_t wq_stopped; + bool stopping; + struct mutex stream_lock; + enum rkcif_state state; +}; + +int vehicle_cif_init_mclk(struct vehicle_cif *cif); +int vehicle_cif_init(struct vehicle_cif *cif); +int vehicle_cif_deinit(struct vehicle_cif *cif); + +int vehicle_cif_reverse_open(struct vehicle_cfg *v_cfg); + +int vehicle_cif_reverse_close(void); +int vehicle_wait_cif_reset_done(void); + +/* CIF IRQ STAT*/ +#define DMA_FRAME_END (0x01 << 0) +#define LINE_END (0x01 << 1) +#define IFIFO_OF (0x01 << 4) +#define DFIFO_OF (0x01 << 5) +#define PRE_INF_FRAME_END (0x01 << 8) +#define PST_INF_FRAME_END (0x01 << 9) + +enum rk_camera_signal_polarity { + RK_CAMERA_DEVICE_SIGNAL_HIGH_LEVEL = 1, + RK_CAMERA_DEVICE_SIGNAL_LOW_LEVEL = 0, +}; + +enum rk_camera_device_type { + RK_CAMERA_DEVICE_BT601_8 = 0x10000011, + RK_CAMERA_DEVICE_BT601_10 = 0x10000012, + RK_CAMERA_DEVICE_BT601_12 = 0x10000014, + RK_CAMERA_DEVICE_BT601_16 = 0x10000018, + + RK_CAMERA_DEVICE_BT656_8 = 0x10000021, + RK_CAMERA_DEVICE_BT656_10 = 0x10000022, + RK_CAMERA_DEVICE_BT656_12 = 0x10000024, + RK_CAMERA_DEVICE_BT656_16 = 0x10000028, + + RK_CAMERA_DEVICE_CVBS_NTSC = 0x20000001, + RK_CAMERA_DEVICE_CVBS_PAL = 0x20000002 +}; + +#endif diff --git a/drivers/video/rockchip/vehicle/vehicle_cif_regs.h b/drivers/video/rockchip/vehicle/vehicle_cif_regs.h new file mode 100644 index 000000000000..08e1b1f892d2 --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_cif_regs.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Vehicle Driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ + +#ifndef _VEHICLE_RKCIF_REGS_H +#define _VEHICLE_RKCIF_REGS_H +#include "../../../media/platform/rockchip/cif/regs.h" + +struct vehicle_cif_reg { + u32 offset; + char *name; +}; + +#define CIF_REG_NAME(_offset, _name) { .offset = (_offset), .name = (_name), } + +#endif diff --git a/drivers/video/rockchip/vehicle/vehicle_dev.c b/drivers/video/rockchip/vehicle/vehicle_dev.c new file mode 100644 index 000000000000..7e9ece0ed9dc --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_dev.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/video/rockchip/video/vehicle_dev.c + * + * Copyright (C) 2022 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vehicle_main.h" +#include "vehicle_cfg.h" + +static int vechile_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int vechile_close(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t vechile_write(struct file *file, const char __user *buf, + size_t size, loff_t *ppos) +{ + int ret = 0; + char data[22] = ""; + + ret = copy_from_user(data, buf, 18); + + if (ret) + return -1; + if (memcmp(data, "88", 2) == 0) { + vehicle_android_is_ready_notify(); + VEHICLE_INFO("android already up, set vehicle in bottom\n"); + } else { + vehicle_apk_state_change(data); + VEHICLE_INFO("apk_state_change, open dvr\n"); + } + + return size; +} + +static ssize_t +vechile_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) +{ + return 1; +} + +static const struct file_operations vechile_fops = { + .owner = THIS_MODULE, + /*.compat_ioctl = vechile_ioctl,*/ + .open = vechile_open, + .release = vechile_close, + .write = vechile_write, + .read = vechile_read, +}; + +static struct miscdevice vechile_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vehicle", + .fops = &vechile_fops, +}; + +static int __init vechile_module_init(void) +{ + int ret = 0; + + /* register misc device*/ + ret = misc_register(&vechile_dev); + if (ret) { + VEHICLE_DGERR("ERROR: could not register vechile dev\n"); + return ret; + } + + return 0; +} + +static void __exit vechile_module_exit(void) +{ + misc_deregister(&vechile_dev); +} + +module_init(vechile_module_init); +module_exit(vechile_module_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/video/rockchip/vehicle/vehicle_flinger.c b/drivers/video/rockchip/vehicle/vehicle_flinger.c new file mode 100644 index 000000000000..22b9d2c09111 --- /dev/null +++ b/drivers/video/rockchip/vehicle/vehicle_flinger.c @@ -0,0 +1,1513 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/video/rockchip/video/vehicle_flinger.c + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + * Authors: + * Jianwei Fan + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#include +#include