From d025801a2e8dd8e279aabd494303948563ae3ba3 Mon Sep 17 00:00:00 2001 From: Raphael <68374617+raphaelscholle@users.noreply.github.com> Date: Sun, 8 Sep 2024 15:53:24 +0200 Subject: [PATCH] nopatch --- .../boot/dts/rockchip/rk3566-radxa-cm3-io.dts | 14 +- .../rockchip/rk3566-radxa-cm3-rpi-cm4-io.dts | 6 + .../boot/dts/rockchip/rk3566-radxa-cm3.dtsi | 6 + .../rk3566-radxa-zero-3w-aic8800ds2.dts | 63 +- .../rockchip/rk3566-radxa-zero-3w-ap6212.dts | 1137 +++++++++-- .../boot/dts/rockchip/rk3566-radxa-zero3.dts | 8 +- .../boot/dts/rockchip/rk3566-radxa-zero3.dtsi | 10 +- .../boot/dts/rockchip/rk3588-rock-5b.dts | 5 + .../boot/dts/rockchip/rk3588s-rock-5a.dts | 5 + arch/arm64/configs/rockchip_linux_defconfig | 5 + drivers/gpu/drm/drm_gem.c | 6 +- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 8 + drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 5 + drivers/gpu/drm/rockchip/rockchip_vop2_reg.c | 16 +- drivers/media/i2c/Kconfig | 29 + drivers/media/i2c/Makefile | 4 + drivers/media/i2c/arducam-pivariety.c | 1746 ++++++++++++++++ drivers/media/i2c/arducam-pivariety.h | 110 ++ drivers/media/i2c/ds90ub954.c | 1748 +++++++++++++++++ drivers/media/i2c/ds90ub954.h | 1200 +++++++++++ drivers/media/i2c/veye,vbyone.txt | 78 + drivers/media/i2c/veye_mvcam.c | 1696 ++++++++++++++++ drivers/media/i2c/veye_mvcam.h | 233 +++ drivers/media/i2c/veye_vbyone.c | 767 ++++++++ drivers/media/i2c/veye_vbyone.h | 88 + drivers/media/i2c/veyecam2m.c | 1259 ++++++++++++ drivers/net/wireless/Kconfig | 3 + drivers/net/wireless/Makefile | 3 + drivers/net/wireless/realtek/Kconfig | 2 - drivers/net/wireless/realtek/Makefile | 3 - 30 files changed, 10090 insertions(+), 173 deletions(-) create mode 100644 drivers/media/i2c/arducam-pivariety.c create mode 100644 drivers/media/i2c/arducam-pivariety.h create mode 100644 drivers/media/i2c/ds90ub954.c create mode 100644 drivers/media/i2c/ds90ub954.h create mode 100644 drivers/media/i2c/veye,vbyone.txt create mode 100644 drivers/media/i2c/veye_mvcam.c create mode 100644 drivers/media/i2c/veye_mvcam.h create mode 100644 drivers/media/i2c/veye_vbyone.c create mode 100644 drivers/media/i2c/veye_vbyone.h create mode 100644 drivers/media/i2c/veyecam2m.c diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-io.dts b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-io.dts index fb73884b401e5..b0a918af4dab0 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-io.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-io.dts @@ -191,7 +191,9 @@ }; &usbhost_dwc3 { - status = "okay"; + dr_mode = "host"; + extcon = <&usb2phy0>; + status = "okay"; }; &usbhost30 { @@ -221,14 +223,8 @@ &i2c2 { status = "okay"; - hym8563: hym8563@51 { - compatible = "haoyu,hym8563"; - status = "okay"; - reg = <0x51>; - #clock-cells = <0>; - clock-frequency = <32768>; - clock-output-names = "hym8563"; - }; + pinctrl-names = "default"; + pinctrl-0 = <&i2c2m0_xfer>; }; &rk817 { diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-rpi-cm4-io.dts b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-rpi-cm4-io.dts index dbcabaf5bebc4..33b7d0bfde67d 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-rpi-cm4-io.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-rpi-cm4-io.dts @@ -145,6 +145,12 @@ }; }; +&i2c2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c2m0_xfer>; +}; + &threshold { temperature = <60000>; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi index 68312e16cefb5..740397f1720b9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi @@ -711,6 +711,12 @@ }; }; +&uart2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&uart2m0_xfer>; +}; + &pinctrl { pmic { pmic_int: pmic_int { diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts index e11ba6772eea6..3c26bf516f6f2 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts @@ -26,7 +26,7 @@ pinctrl-names = "default", "rts_gpio"; pinctrl-0 = <&uart1m0_rtsn>; pinctrl-1 = <&uart1_gpios>; - BT,reset_gpio = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; + // BT,reset_gpio = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; BT,wake_gpio = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>; BT,wake_host_irq = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; status = "okay"; @@ -39,6 +39,48 @@ reset-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_LOW>; }; + keys: gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&pwr_key>; + autorepeat; + up-key { + gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_LOW>; + linux,code = <103>; + label = "key up"; + }; + down-key { + gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>; + linux,code = <108>; + label = "key down"; + }; + left-key { + gpios = <&gpio4 RK_PB3 GPIO_ACTIVE_LOW>; + linux,code = <106>; + label = "key left"; + }; + right-key { + gpios = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>; + linux,code = <107>; + label = "key right"; + }; + enter-key { + gpios = <&gpio3 RK_PA7 GPIO_ACTIVE_LOW>; + linux,code = <29>; + label = "key enter"; + }; + back-key { + gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_LOW>; + linux,code = <159>; + label = "key back"; + }; + reset-key { + gpios = <&gpio3 RK_PA5 GPIO_ACTIVE_LOW>; + linux,code = <2>; + label = "key reset"; + }; + }; + board_antenna: board-antenna { status = "okay"; compatible = "regulator-fixed"; @@ -58,6 +100,12 @@ pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn>; }; +&uart2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&uart2m0_xfer>; +}; + &sdmmc1 { supports-sdio; bus-width = <4>; @@ -134,6 +182,19 @@ }; }; + keys { + pwr_key: pwr-key { + rockchip,pins = + <3 RK_PC1 RK_FUNC_GPIO &pcfg_pull_up>, + <4 RK_PB3 RK_FUNC_GPIO &pcfg_pull_up>, + <3 RK_PA7 RK_FUNC_GPIO &pcfg_pull_up>, + <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>, + <3 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>, + <3 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>, + <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + antenna { ant_1: ant-1 { rockchip,pins = <3 RK_PD3 RK_FUNC_GPIO &pcfg_pull_down>; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts index 0abc6e60db507..22d964319fdda 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts @@ -5,153 +5,1000 @@ * */ -/dts-v1/; + /dts-v1/; -#include "rk3566-radxa-zero3.dtsi" + #include + #include + #include + #include + #include + #include + #include + #include + #include "rk3566.dtsi" + + / { + model = "Radxa ZERO 3"; + compatible = "radxa,zero3", "rockchip,rk3566"; + + fiq_debugger: fiq-debugger { + compatible = "rockchip,fiq-debugger"; + rockchip,serial-id = <2>; + rockchip,wake-irq = <0>; + /* If enable uart uses irq instead of fiq */ + rockchip,irq-mode-enable = <1>; + rockchip,baudrate = <1500000>; /* Only 115200 and 1500000 */ + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&uart2m0_xfer>; + status = "okay"; + }; + + debug: debug@fd904000 { + compatible = "rockchip,debug"; + reg = <0x0 0xfd904000 0x0 0x1000>, + <0x0 0xfd905000 0x0 0x1000>, + <0x0 0xfd906000 0x0 0x1000>, + <0x0 0xfd907000 0x0 0x1000>; + }; + + cspmu: cspmu@fd90c000 { + compatible = "rockchip,cspmu"; + reg = <0x0 0xfd90c000 0x0 0x1000>, + <0x0 0xfd90d000 0x0 0x1000>, + <0x0 0xfd90e000 0x0 0x1000>, + <0x0 0xfd90f000 0x0 0x1000>; + }; + + vcc_sys: vcc-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + vcc3v3_sys: vcc3v3-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_sys>; + }; + + vcc5v0_sys: vcc5v0-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_sys>; + }; + + wireless_wlan: wireless-wlan { + compatible = "wlan-platdata"; + wifi_chip_type = "ap6212"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_host_wake_irq>; + WIFI,host_wake_irq = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + clocks = <&rk817 1>; + clock-names = "ext_clock"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_enable_h>; + reset-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_LOW>; + }; + + keys: gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&pwr_key>; + autorepeat; + up-key { + gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_LOW>; + linux,code = <103>; + label = "key up"; + }; + down-key { + gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>; + linux,code = <108>; + label = "key down"; + }; + left-key { + gpios = <&gpio4 RK_PB3 GPIO_ACTIVE_LOW>; + linux,code = <106>; + label = "key left"; + }; + right-key { + gpios = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>; + linux,code = <107>; + label = "key right"; + }; + enter-key { + gpios = <&gpio3 RK_PA7 GPIO_ACTIVE_LOW>; + linux,code = <29>; + label = "key enter"; + }; + back-key { + gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_LOW>; + linux,code = <159>; + label = "key back"; + }; + reset-key { + gpios = <&gpio3 RK_PA5 GPIO_ACTIVE_LOW>; + linux,code = <2>; + label = "key reset"; + }; + }; + + gpio_leds: gpio-leds { + compatible = "gpio-leds"; + status = "okay"; + + board-led { + gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&board_led>; + linux,default-trigger = "heartbeat"; + }; + }; + + board_antenna: board-antenna { + status = "okay"; + compatible = "regulator-fixed"; + enable-active-low; + gpio = <&gpio3 RK_PD3 GPIO_ACTIVE_LOW>; + regulator-always-on; + regulator-boot-on; + pinctrl-0 = <&ant_1>; + pinctrl-names = "default"; + regulator-name = "board_antenna"; + }; + + hdmi_sound: hdmi-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <128>; + simple-audio-card,name = "rockchip-hdmi0"; + + simple-audio-card,cpu { + sound-dai = <&i2s0_8ch>; + }; + simple-audio-card,codec { + sound-dai = <&hdmi>; + }; + }; + }; + + &cpu0 { + cpu-supply = <&vdd_cpu>; + }; + + &dfi { + status = "okay"; + }; + + &dmc { + center-supply = <&vdd_logic>; + status = "okay"; + }; + + &gpu { + mali-supply = <&vdd_gpu>; + status = "okay"; + }; + + &pmu_io_domains { + status = "okay"; + pmuio2-supply = <&vcc_3v3>; + vccio1-supply = <&vccio_acodec>; + vccio2-supply = <&vcc_1v8>; + vccio3-supply = <&vccio_sd>; + vccio4-supply = <&vcc_1v8>; + vccio5-supply = <&vcc_3v3>; + vccio6-supply = <&vcc_3v3>; + vccio7-supply = <&vcc_3v3>; + }; + + &i2c0 { + status = "okay"; + + vdd_cpu: rk860x@40 { + status = "okay"; + compatible = "silergy,syr827"; + reg = <0x40>; + vin-supply = <&vcc5v0_sys>; + regulator-compatible = "fan53555-reg"; + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1390000>; + regulator-init-microvolt = <1000000>; + regulator-ramp-delay = <2300>; + fcs,suspend-voltage-selector = <1>; + regulator-boot-on; + regulator-always-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + rk817: pmic@20 { + compatible = "rockchip,rk817"; + reg = <0x20>; + interrupt-parent = <&gpio0>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-names = "default", "pmic-sleep", + "pmic-power-off", "pmic-reset"; + pinctrl-0 = <&pmic_int>; + pinctrl-1 = <&soc_slppin_slp>, <&rk817_slppin_slp>; + pinctrl-2 = <&soc_slppin_gpio>, <&rk817_slppin_pwrdn>; + pinctrl-3 = <&soc_slppin_gpio>, <&rk817_slppin_rst>; + rockchip,system-power-controller; + wakeup-source; + #clock-cells = <1>; + clock-output-names = "rk808-clkout1", "rk808-clkout2"; + //fb-inner-reg-idxs = <2>; + /* 1: rst regs (default in codes), 0: rst the pmic */ + pmic-reset-func = <0>; + /* not save the PMIC_POWER_EN register in uboot */ + not-save-power-en = <1>; + + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc5-supply = <&vcc_sys>; + vcc6-supply = <&vcc_sys>; + vcc7-supply = <&vcc_sys>; + vcc8-supply = <&vcc_sys>; + vcc9-supply = <&dcdc_boost>; + + pwrkey { + status = "okay"; + }; + + pinctrl_rk8xx: pinctrl_rk8xx { + gpio-controller; + #gpio-cells = <2>; + + rk817_slppin_null: rk817_slppin_null { + pins = "gpio_slp"; + function = "pin_fun0"; + }; + + rk817_slppin_slp: rk817_slppin_slp { + pins = "gpio_slp"; + function = "pin_fun1"; + }; + + rk817_slppin_pwrdn: rk817_slppin_pwrdn { + pins = "gpio_slp"; + function = "pin_fun2"; + }; + + rk817_slppin_rst: rk817_slppin_rst { + pins = "gpio_slp"; + function = "pin_fun3"; + }; + }; + + regulators { + vdd_logic: DCDC_REG1 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1350000>; + regulator-init-microvolt = <900000>; + regulator-ramp-delay = <6001>; + regulator-initial-mode = <0x2>; + regulator-name = "vdd_logic"; + regulator-state-mem { + regulator-off-in-suspend; + regulator-suspend-microvolt = <900000>; + }; + }; + + vdd_gpu: DCDC_REG2 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1350000>; + regulator-init-microvolt = <900000>; + regulator-ramp-delay = <6001>; + regulator-initial-mode = <0x2>; + regulator-name = "vdd_gpu"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_ddr: DCDC_REG3 { + regulator-always-on; + regulator-boot-on; + regulator-initial-mode = <0x2>; + regulator-name = "vcc_ddr"; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc_3v3: DCDC_REG4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = <0x2>; + regulator-name = "vcc_3v3"; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3300000>; + }; + }; + + vcca1v8_pmu: LDO_REG1 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcca1v8_pmu"; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vdda_0v9: LDO_REG2 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + regulator-name = "vdda_0v9"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdda0v9_pmu: LDO_REG3 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + regulator-name = "vdda0v9_pmu"; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <900000>; + }; + }; + + vccio_acodec: LDO_REG4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vccio_acodec"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vccio_sd: LDO_REG5 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vccio_sd"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc3v3_pmu: LDO_REG6 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc3v3_pmu"; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3000000>; + }; + }; + + vcc_1v8: LDO_REG7 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc_1v8"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc1v8_dvp: LDO_REG8 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc1v8_dvp"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc2v8_dvp: LDO_REG9 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-name = "vcc2v8_dvp"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + dcdc_boost: BOOST { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "boost"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + otg_switch: OTG_SWITCH { + regulator-name = "otg_switch"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + }; + + rk817_codec: codec { + #sound-dai-cells = <0>; + compatible = "rockchip,rk817-codec"; + clocks = <&cru I2S1_MCLKOUT>; + clock-names = "mclk"; + assigned-clocks = <&cru I2S1_MCLKOUT>, <&cru I2S1_MCLK_TX_IOE>; + assigned-clock-rates = <12288000>; + assigned-clock-parents = <&cru I2S1_MCLKOUT_TX>, <&cru I2S1_MCLKOUT_TX>; + pinctrl-names = "default"; + pinctrl-0 = <&i2s1m0_mclk>; + hp-volume = <20>; + spk-volume = <3>; + out-l2spk-r2hp; + //spk-ctl-gpios = <&gpio0 RK_PA6 GPIO_ACTIVE_HIGH>; + status = "disabled"; + }; + }; + }; + + &i2s1_8ch { + status = "okay"; + rockchip,clk-trcm = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&i2s1m0_sclktx + &i2s1m0_lrcktx + &i2s1m0_sdi0 + &i2s1m0_sdo0>; + }; + + &iep { + status = "okay"; + }; + + &iep_mmu { + status = "okay"; + }; + + &jpegd { + status = "okay"; + }; + + &jpegd_mmu { + status = "okay"; + }; + + &mpp_srv { + status = "okay"; + }; + + &rk_rga { + status = "okay"; + }; + + &rkvdec { + status = "okay"; + }; + + &rkvdec_mmu { + status = "okay"; + }; + + &rkvenc { + venc-supply = <&vdd_logic>; + status = "okay"; + }; + + &rkvenc_mmu { + status = "okay"; + }; + + &rknpu { + rknpu-supply = <&vdd_gpu>; + }; + + &saradc { + status = "okay"; + vref-supply = <&vcc_1v8>; + }; + + &sdhci { + bus-width = <8>; + supports-emmc; + non-removable; + max-frequency = <200000000>; + status = "okay"; + }; + + &sdmmc1 { + max-frequency = <150000000>; + supports-sdio; + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + cap-sdio-irq; + keep-power-in-suspend; + non-removable; + rockchip,default-sample-phase = <90>; + mmc-pwrseq = <&sdio_pwrseq>; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc1_bus4 &sdmmc1_cmd &sdmmc1_clk>; + sd-uhs-sdr104; + status = "okay"; + }; + + &uart1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn &uart1m0_rtsn>; + + bluetooth { + compatible = "brcm,bcm43438-bt"; + clocks = <&rk817 1>; + clock-names = "lpo"; + device-wakeup-gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>; + host-wakeup-gpios = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; + shutdown-gpios = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; + max-speed = <1500000>; + pinctrl-names = "default"; + pinctrl-0 = <&bt_host_wake_l &bt_wake_l &bt_enable_h>; + vbat-supply = <&vcc3v3_sys>; + vddio-supply = <&vcc_1v8>; + }; + }; -/ { - model = "Radxa ZERO 3"; - compatible ="radxa,zero3w-ap6212", "radxa,zero3", "rockchip,rk3566"; - - wireless_wlan: wireless-wlan { - compatible = "wlan-platdata"; - wifi_chip_type = "ap6212"; - pinctrl-names = "default"; - pinctrl-0 = <&wifi_host_wake_irq>; - WIFI,host_wake_irq = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>; - status = "okay"; - }; - - sdio_pwrseq: sdio-pwrseq { - compatible = "mmc-pwrseq-simple"; - pinctrl-names = "default"; - pinctrl-0 = <&wifi_enable_h>; - reset-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_LOW>; - }; - - board_antenna: board-antenna { - status = "okay"; - compatible = "regulator-fixed"; - enable-active-low; - gpio = <&gpio3 RK_PD3 GPIO_ACTIVE_LOW>; - regulator-always-on; - regulator-boot-on; - pinctrl-0 = <&ant_1>; - pinctrl-names = "default"; - regulator-name = "board_antenna"; - }; -}; - -&uart1 { + &uart2 { status = "okay"; pinctrl-names = "default"; - pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn &uart1m0_rtsn>; - - bluetooth { - compatible = "brcm,bcm43438-bt"; - clocks = <&rk817 1>; - clock-names = "lpo"; - device-wakeup-gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>; - host-wakeup-gpios = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; - shutdown-gpios = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; - max-speed = <1500000>; - pinctrl-names = "default"; - pinctrl-0 = <&bt_host_wake_l &bt_wake_l &bt_enable_h>; - vbat-supply = <&vcc3v3_sys>; - vddio-supply = <&vcc_1v8>; - }; -}; - -&sdmmc1 { - supports-sdio; - bus-width = <4>; - disable-wp; - cap-sd-highspeed; - cap-sdio-irq; - keep-power-in-suspend; - non-removable; - rockchip,default-sample-phase = <180>; - mmc-pwrseq = <&sdio_pwrseq>; - non-removable; - no-sd; - no-mmc; - pinctrl-names = "default"; - pinctrl-0 = <&sdmmc1_bus4 &sdmmc1_cmd &sdmmc1_clk>; - sd-uhs-sdr104; - status = "okay"; -}; - -&pinctrl { - wifi { - wifi_enable_h: wifi-enable-h { - rockchip,pins = <0 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>; - }; - - wifi_host_wake_irq: wifi-host-wake-irq { - rockchip,pins = <0 RK_PB7 RK_FUNC_GPIO &pcfg_pull_down>; - }; - }; - - bt { - bt_enable_h: bt-enable-h { - rockchip,pins = <0 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>; - }; - - bt_host_wake_l: bt-host-wake-l { - rockchip,pins = <0 RK_PB3 RK_FUNC_GPIO &pcfg_pull_none>; - }; - - bt_wake_l: bt-wake-l { - rockchip,pins = <0 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - - sdmmc1 { - /omit-if-no-ref/ - sdmmc1_bus4: sdmmc1-bus4 { - rockchip,pins = - /* sdmmc1_d0 */ - <2 RK_PA3 1 &pcfg_pull_up_drv_level_12>, - /* sdmmc1_d1 */ - <2 RK_PA4 1 &pcfg_pull_up_drv_level_12>, - /* sdmmc1_d2 */ - <2 RK_PA5 1 &pcfg_pull_up_drv_level_12>, - /* sdmmc1_d3 */ - <2 RK_PA6 1 &pcfg_pull_up_drv_level_12>; - }; - - /omit-if-no-ref/ - sdmmc1_clk: sdmmc1-clk { - rockchip,pins = - /* sdmmc1_clk */ - <2 RK_PB0 1 &pcfg_pull_up_drv_level_12>; - }; - - /omit-if-no-ref/ - sdmmc1_cmd: sdmmc1-cmd { - rockchip,pins = - /* sdmmc1_cmd */ - <2 RK_PA7 1 &pcfg_pull_up_drv_level_12>; - }; - - /omit-if-no-ref/ - sdmmc1_det: sdmmc1-det { - rockchip,pins = - /* sdmmc1_det */ - <2 RK_PB2 1 &pcfg_pull_up>; - }; - - /omit-if-no-ref/ - sdmmc1_pwren: sdmmc1-pwren { - rockchip,pins = - /* sdmmc1_pwren */ - <2 RK_PB1 1 &pcfg_pull_none>; - }; - }; - - antenna { - ant_1: ant-1 { - rockchip,pins = <3 RK_PD3 RK_FUNC_GPIO &pcfg_pull_down>; - }; - }; + pinctrl-0 = <&uart2m0_xfer>; }; + + &gmac1 { + status = "disabled"; + }; + + &mdio1 { + status = "disabled"; + }; + + &sfc { + status = "okay"; + }; + + &tsadc { + status = "okay"; + }; + + &usb2phy0 { + status = "okay"; + }; + + &u2phy0_host { + status = "okay"; + }; + + &u2phy0_otg { + status = "okay"; + }; + + &u2phy1_host { + status = "okay"; + }; + + &u2phy1_otg { + status = "okay"; + }; + + &usb_host0_ehci { + status = "okay"; + }; + + &usb_host0_ohci { + status = "okay"; + }; + + &usb2phy1 { + status = "okay"; + }; + + &usb_host1_ehci { + status = "okay"; + }; + + &usb_host1_ohci { + status = "okay"; + }; + + &combphy1_usq { + status = "okay"; + }; + + &usbdrd30 { + status = "okay"; + }; + + &usbhost_dwc3 { + status = "okay"; + }; + + &usbhost30 { + status = "okay"; + }; + + &u2phy0_otg { + status = "okay"; + }; + + &usbdrd_dwc3 { + dr_mode = "host"; + extcon = <&usb2phy0>; + status = "okay"; + }; + + &vdpu { + status = "okay"; + }; + + &vdpu_mmu { + status = "okay"; + }; + + &vepu { + status = "okay"; + }; + + &vepu_mmu { + status = "okay"; + }; + + &vop { + status = "okay"; + assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>; + assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>; + }; + + &vop_mmu { + status = "okay"; + }; + + &i2c3 { + status = "okay"; + pinctrl-0 = <&i2c3m0_xfer>; + + usbc0: fusb302@22 { + compatible = "fcs,fusb302"; + reg = <0x22>; + interrupt-parent = <&gpio3>; + interrupts = ; + sel-gpios= <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usbc0_int>; + }; + }; + + &reserved_memory { + ramoops: ramoops@110000 { + compatible = "ramoops"; + reg = <0x0 0x110000 0x0 0xf0000>; + record-size = <0x20000>; + console-size = <0x80000>; + ftrace-size = <0x00000>; + pmsg-size = <0x50000>; + }; + }; + + &rng { + status = "okay"; + }; + + &rockchip_suspend { + status = "okay"; + }; + + &gpio0 { + gpio-line-names = + /* GPIO0_A0-A3 */ + "", "", "", "", + /* GPIO0_A4-A7 */ + "", "", "", "", + + /* GPIO0_B0-B3 */ + "", "", "", "", + /* GPIO0_B4-B7 */ + "", "", "", "", + + /* GPIO0_C0-C3 */ + "", "", "", "", + /* GPIO0_C4-C7 */ + "", "", "", "", + + /* GPIO0_D0-D3 */ + "PIN_10", "PIN_8", "", "", + /* GPIO0_D4-D7 */ + "", "", "", ""; + }; + + &gpio1 { + gpio-line-names = + /* GPIO1_A0-A3 */ + "PIN_3", "PIN_5", "", "", + /* GPIO1_A4-A7 */ + "PIN_37", "", "", "", + + /* GPIO1_B0-B3 */ + "", "", "", "", + /* GPIO1_B4-B7 */ + "", "", "", "", + + /* GPIO1_C0-C3 */ + "", "", "", "", + /* GPIO1_C4-C7 */ + "", "", "", "", + + /* GPIO1_D0-D3 */ + "", "", "", "", + /* GPIO1_D4-D7 */ + "", "", "", ""; + }; + + &gpio2 { + gpio-line-names = + /* GPIO2_A0-A3 */ + "", "", "", "", + /* GPIO2_A4-A7 */ + "", "", "", "", + + /* GPIO2_B0-B3 */ + "", "", "", "", + /* GPIO2_B4-B7 */ + "", "", "", "", + + /* GPIO2_C0-C3 */ + "", "", "", "", + /* GPIO2_C4-C7 */ + "", "", "", "", + + /* GPIO2_D0-D3 */ + "", "", "", "", + /* GPIO2_D4-D7 */ + "", "", "", ""; + }; + + &gpio3 { + gpio-line-names = + /* GPIO3_A0-A3 */ + "", "PIN_11", "PIN_13", "PIN_12", + /* GPIO3_A4-A7 */ + "PIN_35", "PIN_40", "PIN_38", "PIN_36", + + /* GPIO3_B0-B3 */ + "PIN_15", "PIN_16", "PIN_18", "PIN_29", + /* GPIO3_B4-B7 */ + "PIN_31", "", "", "", + + /* GPIO3_C0-C3 */ + "", "PIN_22", "PIN_32", "PIN_33", + /* GPIO3_C4-C7 */ + "PIN_7", "", "", "", + + /* GPIO3_D0-D3 */ + "", "", "", "", + /* GPIO3_D4-D7 */ + "", "", "", ""; + }; + + &gpio4 { + gpio-line-names = + /* GPIO4_A0-A3 */ + "", "", "", "", + /* GPIO4_A4-A7 */ + "", "", "", "", + + /* GPIO4_B0-B3 */ + "", "", "PIN_27", "PIN_28", + /* GPIO4_B4-B7 */ + "", "", "", "", + + /* GPIO4_C0-C3 */ + "", "", "PIN_23", "PIN_19", + /* GPIO4_C4-C7 */ + "", "PIN_21", "PIN_24", "", + + /* GPIO4_D0-D3 */ + "", "PIN_26", "", "", + /* GPIO4_D4-D7 */ + "", "", "", ""; + }; + + &i2s0_8ch { + status = "okay"; + }; + + &display_subsystem { + status = "okay"; + }; + + &hdmi { + status = "okay"; + // preset_as_preferred; + // dirty fixing to 720p for now + preset_max_hdisplay = <1280>; + preset_max_vdisplay = <720>; + }; + + &hdmi_in_vp0 { + status = "okay"; + }; + + &hdmi_in_vp1 { + status = "disabled"; + }; + + &hdmi_sound { + status = "okay"; + }; + + &route_hdmi { + status = "okay"; + connect = <&vp0_out_hdmi>; + }; + + &sdmmc0 { + max-frequency = <150000000>; + supports-sd; + no-sdio; + no-mmc; + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + num-slots = <1>; + sd-uhs-sdr104; + vmmc-supply = <&vcc3v3_sys>; + vqmmc-supply = <&vccio_sd>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0_bus4 &sdmmc0_clk &sdmmc0_cmd &sdmmc0_det>; + status = "okay"; + }; + + &rockchip_suspend { + status = "okay"; + rockchip,sleep-debug-en = <1>; + rockchip,sleep-mode-config = < + (0 + | RKPM_SLP_CENTER_OFF + | RKPM_SLP_HW_PLLS_OFF + | RKPM_SLP_PMUALIVE_32K + | RKPM_SLP_32K_PVTM + ) + >; + rockchip,wakeup-config = < + (0 + | RKPM_GPIO_WKUP_EN + | RKPM_USB_WKUP_EN + ) + >; + }; + + &pinctrl { + pmic { + pmic_int: pmic_int { + rockchip,pins = + <0 RK_PA3 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + soc_slppin_gpio: soc_slppin_gpio { + rockchip,pins = + <0 RK_PA2 RK_FUNC_GPIO &pcfg_output_low_pull_down>; + }; + + soc_slppin_slp: soc_slppin_slp { + rockchip,pins = + <0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + soc_slppin_rst: soc_slppin_rst { + rockchip,pins = + <0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + wifi { + wifi_enable_h: wifi-enable-h { + rockchip,pins = <0 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + wifi_host_wake_irq: wifi-host-wake-irq { + rockchip,pins = <0 RK_PB7 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; + + keys { + pwr_key: pwr-key { + rockchip,pins = + <3 RK_PC1 RK_FUNC_GPIO &pcfg_pull_up>, + <4 RK_PB3 RK_FUNC_GPIO &pcfg_pull_up>, + <3 RK_PA7 RK_FUNC_GPIO &pcfg_pull_up>, + <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>, + <3 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>, + <3 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>, + <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + bt { + bt_enable_h: bt-enable-h { + rockchip,pins = <0 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + bt_host_wake_l: bt-host-wake-l { + rockchip,pins = <0 RK_PB3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + bt_wake_l: bt-wake-l { + rockchip,pins = <0 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + leds { + board_led: board-led { + rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + antenna { + ant_1: ant-1 { + rockchip,pins = <3 RK_PD3 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; + + usb-typec { + usbc0_int: usbc0-int { + rockchip,pins = <3 RK_PC7 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + }; + \ No newline at end of file diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dts b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dts index a8a02c1572c2f..d04a7f62065d2 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dts @@ -537,6 +537,12 @@ }; }; +&uart2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&uart2m0_xfer>; +}; + &gmac1 { phy-mode = "rgmii"; clock_in_out = "output"; @@ -675,7 +681,7 @@ &i2c3 { status = "okay"; - pinctrl-0 = <&i2c3m1_xfer>; + pinctrl-0 = <&i2c3m0_xfer>; usbc0: fusb302@22 { compatible = "fcs,fusb302"; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dtsi index 0245f92ca3d65..64efcd4aa2ebf 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dtsi @@ -557,7 +557,9 @@ }; &usbdrd_dwc3 { - status = "okay"; + dr_mode = "host"; + extcon = <&usb2phy0>; + status = "okay"; }; &usbdrd30 { @@ -577,7 +579,9 @@ }; &usbdrd_dwc3 { - status = "okay"; + dr_mode = "host"; + extcon = <&usb2phy0>; + status = "okay"; }; &vdpu { @@ -608,7 +612,7 @@ &i2c3 { status = "okay"; - pinctrl-0 = <&i2c3m1_xfer>; + pinctrl-0 = <&i2c3m0_xfer>; usbc0: fusb302@22 { compatible = "fcs,fusb302"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts index 5edf60c77bb9b..e9a566d13b478 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts @@ -323,6 +323,11 @@ }; }; +&i2c7 { + status = "okay"; + pinctrl-0 = <&i2c7m3_xfer>; +}; + &dmc { center-supply = <&vdd_ddr_s0>; mem-supply = <&vdd_log_s0>; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts index 245cc0462a64c..8584937a1bb73 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts @@ -232,6 +232,11 @@ }; }; +&i2c8 { + status = "okay"; + pinctrl-0 = <&i2c8m2_xfer>; +}; + /* CPU */ &cpu_l0 { diff --git a/arch/arm64/configs/rockchip_linux_defconfig b/arch/arm64/configs/rockchip_linux_defconfig index a792f479958aa..c3c1f0aa9a672 100644 --- a/arch/arm64/configs/rockchip_linux_defconfig +++ b/arch/arm64/configs/rockchip_linux_defconfig @@ -690,6 +690,9 @@ CONFIG_WIFI_BUILD_MODULE=y CONFIG_AP6XXX=m CONFIG_RTL8852BE=m CONFIG_RTL8852BU=m +CONFIG_RTL8812AU=m +CONFIG_RTL8822BU=m +CONFIG_RTL8822EU=m CONFIG_USB_NET_RNDIS_WLAN=y # CONFIG_IEEE802154_DRIVERS is not set CONFIG_LTE=y @@ -831,7 +834,9 @@ CONFIG_VIDEO_GC2093=y CONFIG_VIDEO_GC8034=y CONFIG_VIDEO_IMX219=y CONFIG_VIDEO_IMX415=y +CONFIG_VIDEO_ARDUCAM_PIVARIETY=m CONFIG_VIDEO_IMX464=y +CONFIG_VIDEO_VEYECAM2M=y CONFIG_VIDEO_OS04A10=y CONFIG_VIDEO_OV4689=y CONFIG_VIDEO_OV5647=y diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 8b30e8d83fbcf..8e7e20cb87f89 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -304,11 +304,15 @@ int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, if (!obj) return -ENOENT; - /* Don't allow imported objects to be mapped */ + /* + * Enable mapping for imported objects, for debug only + */ + /* if (obj->import_attach) { ret = -EINVAL; goto out; } + */ ret = drm_gem_create_mmap_offset(obj); if (ret) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index d5b93d57fd896..3f7069a2d3deb 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -371,6 +371,11 @@ static const struct drm_display_mode rockchip_drm_default_modes[] = { 798, 858, 0, 480, 489, 495, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, + /* Consti10 Added - paste from 16 - 1920x1080@60Hz 16:9 */ + { DRM_MODE("2560x1440", DRM_MODE_TYPE_DRIVER, 148500, 2560, 2008, + 2052, 2200, 0, 1440, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, }; int rockchip_drm_add_modes_noedid(struct drm_connector *connector) @@ -416,6 +421,9 @@ u32 rockchip_drm_get_dclk_by_width(int width) int i = 0; u32 dclk_khz; + // Consti10 + if(true)return 594000; + for (i = 0; i < ARRAY_SIZE(rockchip_drm_dclk); i++) { if (width == rockchip_drm_dclk[i].width) { dclk_khz = rockchip_drm_dclk[i].dclk_khz; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index f86109b5420a9..950ffd54aa8e0 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -5375,7 +5375,11 @@ rockchip_atomic_helper_update_plane(struct drm_plane *plane, if (plane == crtc->cursor || vpstate->async_commit) state->legacy_cursor_update = true; + // Consti10 + state->legacy_cursor_update = true; + //ret = drm_atomic_nonblocking_commit(state); ret = drm_atomic_commit(state); + ret=0; fail: drm_atomic_state_put(state); return ret; @@ -9726,6 +9730,7 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state */ vop2_wait_for_irq_handler(crtc); + /** * move here is to make sure current fs call function is complete, * so when layer_sel_update is true, we can skip current vblank correctly. diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index 31069d9aebf80..b888d41bddff4 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -2388,7 +2388,7 @@ static const struct vop2_win_data rk3528_vop_win_data[] = { .regs = &rk3568_esmart_win_data, .area = rk3568_area_data, .area_size = ARRAY_SIZE(rk3568_area_data), - .type = DRM_PLANE_TYPE_OVERLAY, + .type = DRM_PLANE_TYPE_PRIMARY, .axi_id = 0, .axi_yrgb_id = 0x08, .axi_uv_id = 0x09, @@ -2652,8 +2652,8 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .name = "Smart0-win0", .phys_id = ROCKCHIP_VOP2_SMART0, .base = 0x400, - .formats = formats_for_smart, - .nformats = ARRAY_SIZE(formats_for_smart), + .formats = formats_for_rk356x_esmart, + .nformats = ARRAY_SIZE(formats_for_rk356x_esmart), .format_modifiers = format_modifiers, .layer_sel_id = { 3, 3, 3, 0xff }, .supported_rotations = DRM_MODE_REFLECT_Y, @@ -2664,7 +2664,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .regs = &rk3568_esmart_win_data, .area = rk3568_area_data, .area_size = ARRAY_SIZE(rk3568_area_data), - .type = DRM_PLANE_TYPE_PRIMARY, + .type = DRM_PLANE_TYPE_OVERLAY, .max_upscale_factor = 8, .max_downscale_factor = 8, .dly = { 20, 47, 41 }, @@ -2674,8 +2674,8 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { { .name = "Smart1-win0", .phys_id = ROCKCHIP_VOP2_SMART1, - .formats = formats_for_smart, - .nformats = ARRAY_SIZE(formats_for_smart), + .formats = formats_for_rk356x_esmart, + .nformats = ARRAY_SIZE(formats_for_rk356x_esmart), .format_modifiers = format_modifiers, .base = 0x600, .layer_sel_id = { 7, 7, 7, 0xff }, @@ -2687,7 +2687,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .regs = &rk3568_esmart_win_data, .area = rk3568_area_data, .area_size = ARRAY_SIZE(rk3568_area_data), - .type = DRM_PLANE_TYPE_PRIMARY, + .type = DRM_PLANE_TYPE_OVERLAY, .max_upscale_factor = 8, .max_downscale_factor = 8, .dly = { 20, 47, 41 }, @@ -2733,7 +2733,7 @@ static const struct vop2_win_data rk3568_vop_win_data[] = { .regs = &rk3568_esmart_win_data, .area = rk3568_area_data, .area_size = ARRAY_SIZE(rk3568_area_data), - .type = DRM_PLANE_TYPE_OVERLAY, + .type = DRM_PLANE_TYPE_PRIMARY, .max_upscale_factor = 8, .max_downscale_factor = 8, .dly = { 20, 47, 41 }, diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index d66d1b1f29991..61d8ee4ec7fbe 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1299,7 +1299,16 @@ config VIDEO_IMX415 To compile this driver as a module, choose M here: the module will be called imx415. +config VIDEO_ARDUCAM_PIVARIETY + tristate "Arducam pivariety camera support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the Arducam + pivariety camera. + To compile this driver as a module, choose M here: the + module will be called arducam_pivariety. config VIDEO_IMX464 tristate "Sony IMX464 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API @@ -1377,6 +1386,26 @@ config VIDEO_OS02G10 This is a Video4Linux2 sensor driver for the OmniVision OS02G10 camera. +config VIDEO_DS90UB954 + tristate "TI FDPLINK III support." + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + help + Driver for TI FDPLINK III . + +config VIDEO_VEYECAM2M + tristate "VEYE 2M camera support." + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + help + Driver for veye 2m camera. + +config VIDEO_VEYEMVCAM + tristate "VEYE MV series camera support." + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + help + Driver for veye mv series camera. + config VIDEO_OS03B10 tristate "OmniVision OS03B10 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index eb2cad76f4fa6..3101b2eaad844 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -212,6 +212,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o obj-$(CONFIG_VIDEO_IMX347) += imx347.o obj-$(CONFIG_VIDEO_IMX378) += imx378.o obj-$(CONFIG_VIDEO_IMX415) += imx415.o +obj-$(CONFIG_VIDEO_ARDUCAM_PIVARIETY) += arducam-pivariety.o obj-$(CONFIG_VIDEO_IMX464) += imx464.o obj-$(CONFIG_VIDEO_IMX492) += imx492.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o @@ -227,6 +228,9 @@ obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o obj-$(CONFIG_SDR_MAX2175) += max2175.o +obj-$(CONFIG_VIDEO_DS90UB954) += ds90ub954.o +obj-$(CONFIG_VIDEO_VEYECAM2M) += veyecam2m.o +obj-$(CONFIG_VIDEO_VEYEMVCAM) += veye_mvcam.o obj-$(CONFIG_VIDEO_OTP_EEPROM) += otp_eeprom.o obj-$(CONFIG_VIDEO_PREISP_DUMMY_SENSOR) += preisp-dummy.o diff --git a/drivers/media/i2c/arducam-pivariety.c b/drivers/media/i2c/arducam-pivariety.c new file mode 100644 index 0000000000000..0daf0b53b77b8 --- /dev/null +++ b/drivers/media/i2c/arducam-pivariety.c @@ -0,0 +1,1746 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for Arducam Pivariety Cameras + * Copyright (C) 2022 Arducam Technology co., Ltd. + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + * + * I2C read and write method is taken from the OV9281 driver + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../platform/rockchip/isp/rkisp_tb_helper.h" +#include "arducam-pivariety.h" +#include "../platform/rockchip/isp/rkisp_tb_helper.h" + +static int debug = 0; +module_param(debug, int, 0644); + +#define PIVARIETY_NAME "pivariety" +#define DRIVER_VERSION KERNEL_VERSION(0, 0x00, 0x10) + +/* regulator supplies */ +static const char * const pivariety_supply_name[] = { + /* Supplies can be enabled in any order */ + "VANA", /* Analog (2.8V) supply */ + "VDIG", /* Digital Core (1.8V) supply */ + "VDDL", /* IF (1.2V) supply */ +}; + +/* The supported raw formats. */ +static const u32 codes[] = { + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_Y8_1X8, + + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_Y10_1X10, + + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_Y12_1X12, +}; + +#define ARDUCAM_NUM_SUPPLIES ARRAY_SIZE(pivariety_supply_name) + +#define ARDUCAM_XCLR_MIN_DELAY_US 10000 +#define ARDUCAM_XCLR_DELAY_RANGE_US 1000 + +#define MAX_CTRLS 32 + +struct pivariety { + struct v4l2_subdev sd; + struct media_pad pad; + + //struct v4l2_mbus_config_mipi_csi2 bus; + struct clk *xclk; + u32 xclk_freq; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARDUCAM_NUM_SUPPLIES]; + + struct arducam_format *supported_formats; + int num_supported_formats; + int current_format_idx; + int current_resolution_idx; + int lanes; + int bayer_order_volatile; + bool wait_until_free; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *ctrls[MAX_CTRLS]; + /* V4L2 Controls */ + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + + struct v4l2_rect crop; + /* + * Mutex for serialized access: + * Protect sensor module set pad format and start/stop streaming safely. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; + bool power_on; + bool is_thunderboot; + bool is_thunderboot_ng; + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +static const s64 link_freq_menu_items[] = { + 456000000, +}; + +static inline struct pivariety *to_pivariety(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct pivariety, sd); +} + +/* Write registers up to 4 at a time */ +static int pivariety_write_reg(struct i2c_client *client, u16 reg, u32 val) +{ + unsigned int len = sizeof(u32); + u32 buf_i, val_i = 0; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/* Read registers up to 4 at a time */ +static int pivariety_read_reg(struct i2c_client *client, u16 reg, u32 *val) +{ + struct i2c_msg msgs[2]; + unsigned int len = sizeof(u32); + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = data_be_p; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int +pivariety_read(struct pivariety *pivariety, u16 addr, u32 *value) +{ + struct v4l2_subdev *sd = &pivariety->sd; + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret, count = 0; + + while (count++ < I2C_READ_RETRY_COUNT) { + ret = pivariety_read_reg(client, addr, value); + if (!ret) { + v4l2_dbg(2, debug, sd, "%s: 0x%02x 0x%04x\n", + __func__, addr, *value); + return ret; + } + } + + v4l2_err(sd, "%s: Reading register 0x%02x failed\n", + __func__, addr); + + return ret; +} + +static int pivariety_write(struct pivariety *pivariety, u16 addr, u32 value) +{ + struct v4l2_subdev *sd = &pivariety->sd; + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret, count = 0; + + while (count++ < I2C_WRITE_RETRY_COUNT) { + ret = pivariety_write_reg(client, addr, value); + if (!ret) + return ret; + } + + v4l2_err(sd, "%s: Write 0x%04x to register 0x%02x failed\n", + __func__, value, addr); + + return ret; +} + +static int wait_for_free(struct pivariety *pivariety, int interval) +{ + u32 value; + u32 count = 0; + + while (count++ < (1000 / interval)) { + int ret = pivariety_read(pivariety, SYSTEM_IDLE_REG, &value); + + if (!ret && !value) + break; + msleep(interval); + } + + v4l2_dbg(2, debug, &pivariety->sd, "%s: End wait, Count: %d.\n", + __func__, count); + + return 0; +} + +static int is_raw(int pixformat) +{ + return pixformat >= 0x28 && pixformat <= 0x2D; +} + +static u32 bayer_to_mbus_code(int data_type, int bayer_order) +{ + const u32 depth8[] = { + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_Y8_1X8, + }; + + const u32 depth10[] = { + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_Y10_1X10, + }; + + const u32 depth12[] = { + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_Y12_1X12, + }; + + if (bayer_order < 0 || bayer_order > 4) + return 0; + + switch (data_type) { + case IMAGE_DT_RAW8: + return depth8[bayer_order]; + case IMAGE_DT_RAW10: + return depth10[bayer_order]; + case IMAGE_DT_RAW12: + return depth12[bayer_order]; + } + + return 0; +} + +static u32 yuv422_to_mbus_code(int data_type, int order) +{ + const u32 depth8[] = { + MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_YVYU8_1X16, + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_VYUY8_1X16, + }; + + const u32 depth10[] = { + MEDIA_BUS_FMT_YUYV10_1X20, + MEDIA_BUS_FMT_YVYU10_1X20, + MEDIA_BUS_FMT_UYVY10_1X20, + MEDIA_BUS_FMT_VYUY10_1X20, + }; + + if (order < 0 || order > 3) + return 0; + + switch (data_type) { + case IMAGE_DT_YUV422_8: + return depth8[order]; + case IMAGE_DT_YUV422_10: + return depth10[order]; + } + + return 0; +} + +static u32 data_type_to_mbus_code(int data_type, int bayer_order) +{ + if (is_raw(data_type)) + return bayer_to_mbus_code(data_type, bayer_order); + + switch (data_type) { + case IMAGE_DT_YUV422_8: + case IMAGE_DT_YUV422_10: + return yuv422_to_mbus_code(data_type, bayer_order); + case IMAGE_DT_RGB565: + return MEDIA_BUS_FMT_RGB565_2X8_LE; + case IMAGE_DT_RGB888: + return MEDIA_BUS_FMT_RGB888_1X24; + } + + return 0; +} + +/* Get bayer order based on flip setting. */ +static u32 pivariety_get_format_code(struct pivariety *pivariety, + struct arducam_format *format) +{ + unsigned int order, origin_order; + + lockdep_assert_held(&pivariety->mutex); + + /* + * Only the bayer format needs to transform the format. + */ + if (!is_raw(format->data_type) || + !pivariety->bayer_order_volatile || + format->bayer_order == BAYER_ORDER_GRAY) + return data_type_to_mbus_code(format->data_type, + format->bayer_order); + + order = format->bayer_order; + + origin_order = order; + + order = (pivariety->hflip && pivariety->hflip->val ? order ^ 1 : order); + order = (pivariety->vflip && pivariety->vflip->val ? order ^ 2 : order); + + v4l2_dbg(1, debug, &pivariety->sd, "%s: before: %d, after: %d.\n", + __func__, origin_order, order); + + return data_type_to_mbus_code(format->data_type, order); +} + +/* Power/clock management functions */ +static int pivariety_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct pivariety *pivariety = to_pivariety(sd); + int ret; + + ret = regulator_bulk_enable(ARDUCAM_NUM_SUPPLIES, + pivariety->supplies); + if (ret) { + dev_err(dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + + ret = clk_prepare_enable(pivariety->xclk); + if (ret) { + dev_err(dev, "%s: failed to enable clock\n", + __func__); + goto reg_off; + } + + gpiod_set_value_cansleep(pivariety->reset_gpio, 1); + usleep_range(ARDUCAM_XCLR_MIN_DELAY_US, + ARDUCAM_XCLR_MIN_DELAY_US + ARDUCAM_XCLR_DELAY_RANGE_US); + + return 0; + +reg_off: + regulator_bulk_disable(ARDUCAM_NUM_SUPPLIES, pivariety->supplies); + + return ret; +} + +static int pivariety_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct pivariety *pivariety = to_pivariety(sd); + + gpiod_set_value_cansleep(pivariety->reset_gpio, 0); + regulator_bulk_disable(ARDUCAM_NUM_SUPPLIES, pivariety->supplies); + clk_disable_unprepare(pivariety->xclk); + + return 0; +} + +static int pivariety_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct pivariety *pivariety = to_pivariety(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + struct arducam_format *def_fmt = &pivariety->supported_formats[0]; + + /* Initialize try_fmt */ + try_fmt->width = def_fmt->resolution_set->width; + try_fmt->height = def_fmt->resolution_set->height; + try_fmt->code = def_fmt->mbus_code; + try_fmt->field = V4L2_FIELD_NONE; + + return 0; +} + +static int pivariety_s_ctrl(struct v4l2_ctrl *ctrl) +{ + int ret, i; + struct pivariety *pivariety = + container_of(ctrl->handler, struct pivariety, + ctrl_handler); + struct arducam_format *supported_fmts = pivariety->supported_formats; + int num_supported_formats = pivariety->num_supported_formats; + + v4l2_dbg(3, debug, &pivariety->sd, "%s: cid = (0x%X), value = (%d).\n", + __func__, ctrl->id, ctrl->val); + + ret = pivariety_write(pivariety, CTRL_ID_REG, ctrl->id); + ret += pivariety_write(pivariety, CTRL_VALUE_REG, ctrl->val); + if (ret < 0) + return -EINVAL; + + /* When flip is set, modify all bayer formats */ + if (ctrl->id == V4L2_CID_VFLIP || ctrl->id == V4L2_CID_HFLIP) { + for (i = 0; i < num_supported_formats; i++) { + supported_fmts[i].mbus_code = + pivariety_get_format_code(pivariety, + &supported_fmts[i]); + } + } + + /* + * When starting streaming, controls are set in batches, + * and the short interval will cause some controls to be unsuccessfully + * set. + */ + if (pivariety->wait_until_free) + wait_for_free(pivariety, 1); + else + usleep_range(200, 210); + + return 0; +} + +static const struct v4l2_ctrl_ops pivariety_ctrl_ops = { + .s_ctrl = pivariety_s_ctrl, +}; + +static int pivariety_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct pivariety *pivariety = to_pivariety(sd); + struct arducam_format *supported_formats = pivariety->supported_formats; + int num_supported_formats = pivariety->num_supported_formats; + + v4l2_dbg(1, debug, sd, "%s: index = (%d)\n", __func__, code->index); + + if (code->index >= num_supported_formats) + return -EINVAL; + + code->code = supported_formats[code->index].mbus_code; + + return 0; +} + +static int pivariety_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + int i; + struct pivariety *pivariety = to_pivariety(sd); + struct arducam_format *supported_formats = pivariety->supported_formats; + int num_supported_formats = pivariety->num_supported_formats; + struct arducam_format *format; + struct arducam_resolution *resolution; + + v4l2_dbg(1, debug, sd, "%s: code = (0x%X), index = (%d)\n", + __func__, fse->code, fse->index); + + for (i = 0; i < num_supported_formats; i++) { + format = &supported_formats[i]; + if (fse->code == format->mbus_code) { + if (fse->index >= format->num_resolution_set) + return -EINVAL; + + resolution = &format->resolution_set[fse->index]; + fse->min_width = resolution->width; + fse->max_width = resolution->width; + fse->min_height = resolution->height; + fse->max_height = resolution->height; + + return 0; + } + } + + return -EINVAL; +} + +static int pivariety_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + int i; + struct pivariety *pivariety = to_pivariety(sd); + struct arducam_format *supported_formats = pivariety->supported_formats; + int num_supported_formats = pivariety->num_supported_formats; + struct arducam_format *format; + struct arducam_resolution *resolution; + + struct v4l2_fract tmp = { + .numerator = 10000, + .denominator = 600000, + }; + + v4l2_dbg(1, debug, sd, "%s: code = (0x%X), index = (%d)\n", + __func__, fie->code, fie->index); + + for (i = 0; i < num_supported_formats; i++) { + format = &supported_formats[i]; + if (fie->code == format->mbus_code) { + // if (fie->index >= format->num_resolution_set) + // return -EINVAL; + fie->code = MEDIA_BUS_FMT_SRGGB10_1X10; + resolution = &format->resolution_set[fie->index]; + fie->width = resolution->width; + fie->height = resolution->height; + fie->interval = tmp; + fie->reserved[0] = NO_HDR; + + return 0; + } + } + + return -EINVAL; +} + +static int pivariety_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct pivariety *pivariety = to_pivariety(sd); + struct arducam_format *current_format = + &pivariety->supported_formats[pivariety->current_format_idx]; + struct v4l2_mbus_framefmt *fmt = &format->format; + int cur_res_idx = pivariety->current_resolution_idx; + + // if (format->pad != 0) + // return -EINVAL; + + // mutex_lock(&pivariety->mutex); + + // current_format = + // &pivariety->supported_formats[pivariety->current_format_idx]; + // cur_res_idx = pivariety->current_resolution_idx; + format->format.width = + current_format->resolution_set[cur_res_idx].width; + format->format.height = + current_format->resolution_set[cur_res_idx].height; + format->format.code = current_format->mbus_code; + format->format.field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + fmt->colorspace, + fmt->ycbcr_enc); + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + + v4l2_dbg(1, debug, sd, "%s: width: (%d) height: (%d) code: (0x%X)\n", + __func__, format->format.width, format->format.height, + format->format.code); + + // mutex_unlock(&pivariety->mutex); + return 0; +} + +static int pivariety_get_fmt_idx_by_code(struct pivariety *pivariety, + u32 mbus_code) +{ + int i; + u32 data_type; + struct arducam_format *formats = pivariety->supported_formats; + + for (i = 0; i < pivariety->num_supported_formats; i++) { + if (formats[i].mbus_code == mbus_code) + return i; + } + + /* + * If the specified format is not found in the list of supported + * formats, try to find a format of the same data type. + */ + for (i = 0; i < ARRAY_SIZE(codes); i++) + if (codes[i] == mbus_code) + break; + + if (i >= ARRAY_SIZE(codes)) + return -EINVAL; + + data_type = i / 5 + IMAGE_DT_RAW8; + + for (i = 0; i < pivariety->num_supported_formats; i++) { + if (formats[i].data_type == data_type) + return i; + } + + return -EINVAL; +} + +static struct v4l2_ctrl *get_control(struct pivariety *pivariety, + u32 id) +{ + int index = 0; + + while (index < MAX_CTRLS && pivariety->ctrls[index]) { + if (pivariety->ctrls[index]->id == id) + return pivariety->ctrls[index]; + index++; + } + + return NULL; +} + +static int update_control(struct pivariety *pivariety, u32 id) +{ + struct v4l2_subdev *sd = &pivariety->sd; + struct v4l2_ctrl *ctrl; + u32 min, max, step, def, id2; + int ret = 0; + + pivariety_write(pivariety, CTRL_ID_REG, id); + pivariety_read(pivariety, CTRL_ID_REG, &id2); + + v4l2_dbg(1, debug, sd, "%s: Write ID: 0x%08X Read ID: 0x%08X\n", + __func__, id, id2); + + pivariety_write(pivariety, CTRL_VALUE_REG, 0); + wait_for_free(pivariety, 1); + + ret += pivariety_read(pivariety, CTRL_MAX_REG, &max); + ret += pivariety_read(pivariety, CTRL_MIN_REG, &min); + ret += pivariety_read(pivariety, CTRL_DEF_REG, &def); + ret += pivariety_read(pivariety, CTRL_STEP_REG, &step); + + if (ret < 0) + goto err; + + if (id == NO_DATA_AVAILABLE || max == NO_DATA_AVAILABLE || + min == NO_DATA_AVAILABLE || def == NO_DATA_AVAILABLE || + step == NO_DATA_AVAILABLE) + goto err; + + v4l2_dbg(1, debug, sd, "%s: min: %d, max: %d, step: %d, def: %d\n", + __func__, min, max, step, def); + + ctrl = get_control(pivariety, id); + return __v4l2_ctrl_modify_range(ctrl, min, max, step, def); + +err: + return -EINVAL; +} + +static int update_controls(struct pivariety *pivariety) +{ + int ret = 0; + int index = 0; + + wait_for_free(pivariety, 5); + + while (index < MAX_CTRLS && pivariety->ctrls[index]) { + ret += update_control(pivariety, pivariety->ctrls[index]->id); + index++; + } + + return ret; +} + +static int pivariety_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + int i, j; + struct pivariety *pivariety = to_pivariety(sd); + struct arducam_format *supported_formats = pivariety->supported_formats; + + if (format->pad != 0) + return -EINVAL; + + mutex_lock(&pivariety->mutex); + + format->format.colorspace = V4L2_COLORSPACE_RAW; + format->format.field = V4L2_FIELD_NONE; + + v4l2_dbg(1, debug, sd, "%s: code: 0x%X, width: %d, height: %d\n", + __func__, format->format.code, format->format.width, + format->format.height); + + i = pivariety_get_fmt_idx_by_code(pivariety, format->format.code); + if (i < 0) + i = 0; + + format->format.code = supported_formats[i].mbus_code; + + for (j = 0; j < supported_formats[i].num_resolution_set; j++) { + if (supported_formats[i].resolution_set[j].width == + format->format.width && + supported_formats[i].resolution_set[j].height == + format->format.height) { + v4l2_dbg(1, debug, sd, + "%s: format match.\n", __func__); + v4l2_dbg(1, debug, sd, + "%s: set format to device: %d %d.\n", + __func__, supported_formats[i].index, j); + + pivariety_write(pivariety, PIXFORMAT_INDEX_REG, + supported_formats[i].index); + pivariety_write(pivariety, RESOLUTION_INDEX_REG, j); + + pivariety->current_format_idx = i; + pivariety->current_resolution_idx = j; + + update_controls(pivariety); + + goto unlock; + } + } + + format->format.width = supported_formats[i].resolution_set[0].width; + format->format.height = supported_formats[i].resolution_set[0].height; + + pivariety_write(pivariety, PIXFORMAT_INDEX_REG, + supported_formats[i].index); + pivariety_write(pivariety, RESOLUTION_INDEX_REG, 0); + + pivariety->current_format_idx = i; + pivariety->current_resolution_idx = 0; + update_controls(pivariety); + +unlock: + + mutex_unlock(&pivariety->mutex); + + return 0; +} + +static int pivariety_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + // struct pivariety *pivariety = to_pivariety(sd); + + struct v4l2_fract tmp = { + .numerator = 10000, + .denominator = 300000, + }; + // mutex_lock(&pivariety->mutex); + fi->interval = tmp; + // mutex_unlock(&pivariety->mutex); + + return 0; +} + +/* Start streaming */ +static int pivariety_start_streaming(struct pivariety *pivariety) +{ + int ret; + + /* set stream on register */ + ret = pivariety_write(pivariety, MODE_SELECT_REG, + ARDUCAM_MODE_STREAMING); + + if (ret) + return ret; + + wait_for_free(pivariety, 2); + + /* + * When starting streaming, controls are set in batches, + * and the short interval will cause some controls to be unsuccessfully + * set. + */ + pivariety->wait_until_free = true; + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(pivariety->sd.ctrl_handler); + + pivariety->wait_until_free = false; + if (ret) + return ret; + + wait_for_free(pivariety, 2); + + return ret; +} + +static int pivariety_read_sel(struct pivariety *pivariety, + struct v4l2_rect *rect) +{ + int ret = 0; + + ret += pivariety_read(pivariety, IPC_SEL_TOP_REG, &rect->top); + ret += pivariety_read(pivariety, IPC_SEL_LEFT_REG, &rect->left); + ret += pivariety_read(pivariety, IPC_SEL_WIDTH_REG, &rect->width); + ret += pivariety_read(pivariety, IPC_SEL_HEIGHT_REG, &rect->height); + + if (ret || rect->top == NO_DATA_AVAILABLE || + rect->left == NO_DATA_AVAILABLE || + rect->width == NO_DATA_AVAILABLE || + rect->height == NO_DATA_AVAILABLE) { + v4l2_err(&pivariety->sd, "%s: Failed to read selection.\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_rect * +__pivariety_get_pad_crop(struct pivariety *pivariety, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + int ret; + + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&pivariety->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + ret = pivariety_read_sel(pivariety, &pivariety->crop); + if (ret) + return NULL; + return &pivariety->crop; + } + + return NULL; +} + +static int pivariety_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + int ret = 0; + struct v4l2_rect rect; + struct pivariety *pivariety = to_pivariety(sd); + + ret = pivariety_write(pivariety, IPC_SEL_TARGET_REG, sel->target); + if (ret) { + v4l2_err(sd, "%s: Write register 0x%02x failed\n", + __func__, IPC_SEL_TARGET_REG); + return -EINVAL; + } + + wait_for_free(pivariety, 2); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + mutex_lock(&pivariety->mutex); + sel->r = *__pivariety_get_pad_crop(pivariety, cfg, + sel->pad, + sel->which); + mutex_unlock(&pivariety->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + ret = pivariety_read_sel(pivariety, &rect); + if (ret) + return -EINVAL; + + sel->r = rect; + return 0; + } + + return -EINVAL; +} + +/* Stop streaming */ +static int pivariety_stop_streaming(struct pivariety *pivariety) +{ + int ret; + + /* set stream off register */ + ret = pivariety_write(pivariety, MODE_SELECT_REG, ARDUCAM_MODE_STANDBY); + if (ret) + v4l2_err(&pivariety->sd, "%s failed to set stream\n", __func__); + + /* + * Return success even if it was an error, as there is nothing the + * caller can do about it. + */ + return 0; +} + +static int pivariety_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct pivariety *pivariety = to_pivariety(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&pivariety->mutex); + + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto err_unlock; + } + + /* + * Apply default & customized values + * and then start streaming. + */ + ret = pivariety_start_streaming(pivariety); + + if (ret) + goto err_rpm_put; + } else { + pivariety_stop_streaming(pivariety); + + pm_runtime_put(&client->dev); + } + + pivariety->streaming = enable; + + /* + * vflip and hflip cannot change during streaming + * Pivariety may not implement flip control. + */ + if (pivariety->vflip) + __v4l2_ctrl_grab(pivariety->vflip, enable); + + if (pivariety->hflip) + __v4l2_ctrl_grab(pivariety->hflip, enable); + + mutex_unlock(&pivariety->mutex); + + return ret; + +err_rpm_put: + pm_runtime_put(&client->dev); +err_unlock: + mutex_unlock(&pivariety->mutex); + + return ret; +} + +static int __maybe_unused pivariety_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct pivariety *pivariety = to_pivariety(sd); + + if (pivariety->streaming) + pivariety_stop_streaming(pivariety); + + return 0; +} + +static int __maybe_unused pivariety_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct pivariety *pivariety = to_pivariety(sd); + int ret; + + if (pivariety->streaming) { + ret = pivariety_start_streaming(pivariety); + if (ret) + goto error; + } + + return 0; + +error: + pivariety_stop_streaming(pivariety); + pivariety->streaming = 0; + return ret; +} + +static int pivariety_get_regulators(struct pivariety *pivariety) +{ + struct i2c_client *client = v4l2_get_subdevdata(&pivariety->sd); + int i; + + for (i = 0; i < ARDUCAM_NUM_SUPPLIES; i++) + pivariety->supplies[i].supply = pivariety_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, + ARDUCAM_NUM_SUPPLIES, + pivariety->supplies); +} + +static int pivariety_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *cfg) +{ + struct pivariety *pivariety = to_pivariety(sd); + u32 val = 0; + + // if (pivariety->lanes > pivariety->bus.num_data_lanes) + // return -EINVAL; + + val = 1 << (pivariety->lanes - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + val |= V4L2_MBUS_CSI2_CHANNEL_1; + cfg->type = V4L2_MBUS_CSI2_DPHY; + cfg->flags = val; + // cfg->bus.mipi_csi2.flags = pivariety->bus.flags; + // cfg->bus.mipi_csi2.num_data_lanes = pivariety->lanes; + + return 0; +} + +static void pivariety_get_module_inf(struct pivariety *pivariety, + struct rkmodule_inf *inf) +{ + // struct i2c_client *client = v4l2_get_subdevdata(&pivariety->sd); + + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, PIVARIETY_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, pivariety->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, pivariety->len_name, sizeof(inf->base.lens)); + + // v4l2_dbg(1, debug, pivariety->sd,"%s: get_module_inf:%s, %s, %s.\n", __func__, + // inf->base.sensor, inf->base.module, inf->base.lens); +} + +static int pivariety_get_channel_info(struct pivariety *pivariety, struct rkmodule_channel_info *ch_info) +{ + struct arducam_format *current_format; + int cur_res_idx; + + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + + current_format = + &pivariety->supported_formats[pivariety->current_format_idx]; + cur_res_idx = pivariety->current_resolution_idx; + ch_info->width = current_format->resolution_set[cur_res_idx].width; + ch_info->height = + current_format->resolution_set[cur_res_idx].height; + ch_info->bus_fmt = current_format->mbus_code; + ch_info->vc = V4L2_MBUS_CSI2_CHANNEL_0; + // pivariety->cur_mode->vc[ch_info->index]; + + return 0; +} + +static long pivariety_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct pivariety *pivariety = to_pivariety(sd); + struct rkmodule_channel_info *ch_info; + long ret = 0; + switch (cmd) + { + case RKMODULE_GET_MODULE_INFO: + pivariety_get_module_inf(pivariety, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = pivariety_get_channel_info(pivariety, ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long pivariety_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + struct rkmodule_channel_info *ch_info; + long ret; + + switch (cmd) + { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = pivariety_ioctl(sd, cmd, inf); + if(!ret) { + if (copy_to_user(up, inf, sizeof(*inf))) { + kfree (inf); + return -EFAULT; + } + } + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(cfg, up, sizeof(*cfg))) { + kfree(cfg); + return -EFAULT; + } + ret = pivariety_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + + ret = pivariety_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + return ret; +} +#endif + +static const struct v4l2_subdev_core_ops pivariety_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + // .unsubscribe_event = v4l2_event_subdev_unsubscribe, + .ioctl = pivariety_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = pivariety_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops pivariety_video_ops = { + .s_stream = pivariety_set_stream, + .g_frame_interval = pivariety_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops pivariety_pad_ops = { + .enum_mbus_code = pivariety_enum_mbus_code, + .get_fmt = pivariety_get_fmt, + .set_fmt = pivariety_set_fmt, + .enum_frame_interval = pivariety_enum_frame_interval, + .enum_frame_size = pivariety_enum_framesizes, + .get_selection = pivariety_get_selection, + .get_mbus_config = pivariety_get_mbus_config, +}; + +static const struct v4l2_subdev_ops pivariety_subdev_ops = { + .core = &pivariety_core_ops, + .video = &pivariety_video_ops, + .pad = &pivariety_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops pivariety_internal_ops = { + .open = pivariety_open, +}; + +static void pivariety_free_controls(struct pivariety *pivariety) +{ + v4l2_ctrl_handler_free(pivariety->sd.ctrl_handler); + mutex_destroy(&pivariety->mutex); +} + +static int pivariety_get_length_of_set(struct pivariety *pivariety, + u16 idx_reg, u16 val_reg) +{ + int ret; + int index = 0; + u32 val; + + while (1) { + ret = pivariety_write(pivariety, idx_reg, index); + ret += pivariety_read(pivariety, val_reg, &val); + + if (ret < 0) + return -1; + + if (val == NO_DATA_AVAILABLE) + break; + index++; + } + pivariety_write(pivariety, idx_reg, 0); + return index; +} + +static int pivariety_enum_resolution(struct pivariety *pivariety, + struct arducam_format *format) +{ + struct i2c_client *client = v4l2_get_subdevdata(&pivariety->sd); + int index = 0; + u32 width, height; + int num_resolution = 0; + int ret; + + num_resolution = pivariety_get_length_of_set(pivariety, + RESOLUTION_INDEX_REG, + FORMAT_WIDTH_REG); + if (num_resolution < 0) + goto err; + + format->resolution_set = devm_kzalloc(&client->dev, + sizeof(*format->resolution_set) * + num_resolution, + GFP_KERNEL); + while (1) { + ret = pivariety_write(pivariety, RESOLUTION_INDEX_REG, index); + ret += pivariety_read(pivariety, FORMAT_WIDTH_REG, &width); + ret += pivariety_read(pivariety, FORMAT_HEIGHT_REG, &height); + + if (ret < 0) + goto err; + + if (width == NO_DATA_AVAILABLE || height == NO_DATA_AVAILABLE) + break; + + format->resolution_set[index].width = width; + format->resolution_set[index].height = height; + + index++; + } + + format->num_resolution_set = index; + pivariety_write(pivariety, RESOLUTION_INDEX_REG, 0); + return 0; +err: + return -ENODEV; +} + +static int pivariety_enum_pixformat(struct pivariety *pivariety) +{ + int ret = 0; + u32 mbus_code = 0; + int pixfmt_type; + int bayer_order; + int bayer_order_not_volatile; + int lanes; + int index = 0; + int num_pixformat = 0; + struct arducam_format *arducam_fmt; + struct i2c_client *client = v4l2_get_subdevdata(&pivariety->sd); + + num_pixformat = pivariety_get_length_of_set(pivariety, + PIXFORMAT_INDEX_REG, + PIXFORMAT_TYPE_REG); + + if (num_pixformat < 0) + goto err; + + ret = pivariety_read(pivariety, FLIPS_DONT_CHANGE_ORDER_REG, + &bayer_order_not_volatile); + if (bayer_order_not_volatile == NO_DATA_AVAILABLE) + pivariety->bayer_order_volatile = 1; + else + pivariety->bayer_order_volatile = !bayer_order_not_volatile; + + if (ret < 0) + goto err; + + pivariety->supported_formats = + devm_kzalloc(&client->dev, + sizeof(*pivariety->supported_formats) * + num_pixformat, + GFP_KERNEL); + + while (1) { + ret = pivariety_write(pivariety, PIXFORMAT_INDEX_REG, index); + ret += pivariety_read(pivariety, PIXFORMAT_TYPE_REG, + &pixfmt_type); + + if (pixfmt_type == NO_DATA_AVAILABLE) + break; + + ret += pivariety_read(pivariety, MIPI_LANES_REG, &lanes); + if (lanes == NO_DATA_AVAILABLE) + break; + + ret += pivariety_read(pivariety, PIXFORMAT_ORDER_REG, + &bayer_order); + if (ret < 0) + goto err; + + mbus_code = data_type_to_mbus_code(pixfmt_type, bayer_order); + arducam_fmt = &pivariety->supported_formats[index]; + arducam_fmt->index = index; + arducam_fmt->mbus_code = mbus_code; + arducam_fmt->bayer_order = bayer_order; + arducam_fmt->data_type = pixfmt_type; + if (pivariety_enum_resolution(pivariety, arducam_fmt)) + goto err; + + index++; + } + + pivariety_write(pivariety, PIXFORMAT_INDEX_REG, 0); + pivariety->num_supported_formats = index; + pivariety->current_format_idx = 0; + pivariety->current_resolution_idx = 0; + pivariety->lanes = lanes; + + return 0; + +err: + return -ENODEV; +} + +static const char *pivariety_ctrl_get_name(u32 id) +{ + switch (id) { + case V4L2_CID_ARDUCAM_EXT_TRI: + return "trigger_mode"; + case V4L2_CID_ARDUCAM_IRCUT: + return "ircut"; + case V4L2_CID_ARDUCAM_STROBE_SHIFT: + return "strobe_shift"; + case V4L2_CID_ARDUCAM_STROBE_WIDTH: + return "strobe_width"; + case V4L2_CID_ARDUCAM_MODE: + return "mode"; + default: + return NULL; + } +} + +enum v4l2_ctrl_type pivariety_get_v4l2_ctrl_type(u32 id) +{ + switch (id) { + case V4L2_CID_ARDUCAM_EXT_TRI: + return V4L2_CTRL_TYPE_BOOLEAN; + case V4L2_CID_ARDUCAM_IRCUT: + return V4L2_CTRL_TYPE_BOOLEAN; + default: + return V4L2_CTRL_TYPE_INTEGER; + } +} + +static struct v4l2_ctrl *v4l2_ctrl_new_arducam(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, + u32 id, s64 min, s64 max, + u64 step, s64 def) +{ + struct v4l2_ctrl_config ctrl_cfg = { + .ops = ops, + .id = id, + .name = NULL, + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = 0, + .min = min, + .max = max, + .def = def, + .step = step, + }; + + ctrl_cfg.name = pivariety_ctrl_get_name(id); + ctrl_cfg.type = pivariety_get_v4l2_ctrl_type(id); + + return v4l2_ctrl_new_custom(hdl, &ctrl_cfg, NULL); +} + +static int pivariety_enum_controls(struct pivariety *pivariety) +{ + struct v4l2_subdev *sd = &pivariety->sd; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_ctrl_handler *ctrl_hdlr = &pivariety->ctrl_handler; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl **ctrl = pivariety->ctrls; + int ret, index, num_ctrls; + u32 id, min, max, def, step; + + num_ctrls = pivariety_get_length_of_set(pivariety, CTRL_INDEX_REG, + CTRL_ID_REG); + if (num_ctrls < 0) + goto err; + + v4l2_dbg(1, debug, sd, "%s: num_ctrls = %d\n", + __func__, num_ctrls); + + ret = v4l2_ctrl_handler_init(ctrl_hdlr, num_ctrls); + if (ret) + return ret; + + index = 0; + while (1) { + ret = pivariety_write(pivariety, CTRL_INDEX_REG, index); + pivariety_write(pivariety, CTRL_VALUE_REG, 0); + wait_for_free(pivariety, 1); + + ret += pivariety_read(pivariety, CTRL_ID_REG, &id); + ret += pivariety_read(pivariety, CTRL_MAX_REG, &max); + ret += pivariety_read(pivariety, CTRL_MIN_REG, &min); + ret += pivariety_read(pivariety, CTRL_DEF_REG, &def); + ret += pivariety_read(pivariety, CTRL_STEP_REG, &step); + if (ret < 0) + goto err; + + if (id == NO_DATA_AVAILABLE || max == NO_DATA_AVAILABLE || + min == NO_DATA_AVAILABLE || def == NO_DATA_AVAILABLE || + step == NO_DATA_AVAILABLE) + break; + + v4l2_dbg(1, debug, sd, + "%s: index = %d, id = 0x%x, max = %d, min = %d, def = %d, step = %d\n", + __func__, index, id, max, min, def, step); + + if (v4l2_ctrl_get_name(id)) { + *ctrl = v4l2_ctrl_new_std(ctrl_hdlr, + &pivariety_ctrl_ops, + id, min, + max, step, + def); + v4l2_dbg(1, debug, sd, "%s: ctrl: 0x%p\n", + __func__, *ctrl); + } else if (pivariety_ctrl_get_name(id)) { + *ctrl = v4l2_ctrl_new_arducam(ctrl_hdlr, + &pivariety_ctrl_ops, + id, min, max, step, def); + + v4l2_dbg(1, debug, sd, + "%s: new custom ctrl, ctrl: 0x%p.\n", + __func__, *ctrl); + } else { + index++; + continue; + } + + if (!*ctrl) + goto err; + + switch (id) { + case V4L2_CID_HFLIP: + pivariety->hflip = *ctrl; + if (pivariety->bayer_order_volatile) + pivariety->hflip->flags |= + V4L2_CTRL_FLAG_MODIFY_LAYOUT; + break; + + case V4L2_CID_VFLIP: + pivariety->vflip = *ctrl; + if (pivariety->bayer_order_volatile) + pivariety->vflip->flags |= + V4L2_CTRL_FLAG_MODIFY_LAYOUT; + break; + + case V4L2_CID_HBLANK: + (*ctrl)->flags |= V4L2_CTRL_FLAG_READ_ONLY; + break; + } + + ctrl++; + index++; + } + /* freq */ + v4l2_ctrl_new_int_menu(ctrl_hdlr, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + + pivariety_write(pivariety, CTRL_INDEX_REG, 0); + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto err; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, + &pivariety_ctrl_ops, + &props); + if (ret) + goto err; + + pivariety->sd.ctrl_handler = ctrl_hdlr; + v4l2_ctrl_handler_setup(ctrl_hdlr); + return 0; +err: + return -ENODEV; +} + +static int pivariety_parse_dt(struct pivariety *pivariety, struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; + + /* Get CSI2 bus config */ + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { + dev_err(dev, "could not parse endpoint\n"); + goto error_out; + } + + //pivariety->bus = ep_cfg.bus.mipi_csi2; + + ret = 0; + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int pivariety_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct pivariety *pivariety; + char facing[2]; + u32 device_id, firmware_version; + u32 sensor_id; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + pivariety = devm_kzalloc(&client->dev, sizeof(*pivariety), GFP_KERNEL); + if (!pivariety) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &pivariety->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &pivariety->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &pivariety->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &pivariety->len_name); + /* Initialize subdev */ + v4l2_i2c_subdev_init(&pivariety->sd, client, + &pivariety_subdev_ops); + + if (pivariety_parse_dt(pivariety, dev)) + return -EINVAL; + + /* Get system clock (xclk) */ + pivariety->xclk = devm_clk_get(dev, "xclk"); + if (IS_ERR(pivariety->xclk)) { + dev_err(dev, "failed to get xclk\n"); + return PTR_ERR(pivariety->xclk); + } + + pivariety->xclk_freq = clk_get_rate(pivariety->xclk); + if (pivariety->xclk_freq != 24000000) { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + pivariety->xclk_freq); + return -EINVAL; + } + + ret = pivariety_get_regulators(pivariety); + if (ret) + return ret; + + /* Request optional enable pin */ + pivariety->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + + ret = pivariety_power_on(dev); + if (ret) + return ret; + + ret = pivariety_read(pivariety, DEVICE_ID_REG, &device_id); + if (ret || device_id != DEVICE_ID) { + dev_err(dev, "probe failed\n"); + ret = -ENODEV; + goto error_power_off; + } + + + ret = pivariety_read(pivariety, DEVICE_VERSION_REG, &firmware_version); + if (ret) + dev_err(dev, "read firmware version failed\n"); + + dev_info(dev, "firmware version: 0x%04X\n", firmware_version); + + ret = pivariety_read(pivariety, SENSOR_ID_REG, &sensor_id); + if (ret) + dev_err(dev, "read sensor id failed\n"); + + if (pivariety_enum_pixformat(pivariety)) { + dev_err(dev, "enum pixformat failed.\n"); + ret = -ENODEV; + goto error_power_off; + } + + if (pivariety_enum_controls(pivariety)) { + dev_err(dev, "enum controls failed.\n"); + ret = -ENODEV; + goto error_power_off; + } +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + /* Initialize subdev */ + pivariety->sd.internal_ops = &pivariety_internal_ops; + pivariety->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + /* Initialize source pad */ + pivariety->pad.flags = MEDIA_PAD_FL_SOURCE; + pivariety->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&pivariety->sd.entity, 1, &pivariety->pad); + if (ret) + goto error_handler_free; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(pivariety->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(pivariety->sd.name, sizeof(pivariety->sd.name), + "m%02x_%s_%s_%04x %s", pivariety->module_index, facing, + PIVARIETY_NAME, sensor_id, dev_name(pivariety->sd.dev)); + + + ret = v4l2_async_register_subdev_sensor_common(&pivariety->sd); + if (ret < 0) + goto error_media_entity; + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&pivariety->sd.entity); + +error_handler_free: + pivariety_free_controls(pivariety); + +error_power_off: + pivariety_power_off(dev); + + return ret; +} + +static int pivariety_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct pivariety *pivariety = to_pivariety(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + pivariety_free_controls(pivariety); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + return 0; +} + +static const struct dev_pm_ops pivariety_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pivariety_suspend, pivariety_resume) + SET_RUNTIME_PM_OPS(pivariety_power_off, pivariety_power_on, NULL) +}; + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id arducam_pivariety_dt_ids[] = { + { .compatible = "arducam,arducam-pivariety" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, arducam_pivariety_dt_ids); +#endif + +// static const struct i2c_device_id arducam_pivariety_match_id[] = { +// { "arducam,pivariety", 0 }, +// { }, +// }; + +static struct i2c_driver arducam_pivariety_i2c_driver = { + .driver = { + .name = PIVARIETY_NAME, + .of_match_table = of_match_ptr(arducam_pivariety_dt_ids), + .pm = &pivariety_pm_ops, + }, + .probe = pivariety_probe, + .remove = pivariety_remove, + // .id_table = arducam_pivariety_match_id, +}; + +// module_i2c_driver(arducam_pivariety_i2c_driver); + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&arducam_pivariety_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&arducam_pivariety_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_AUTHOR("Lee Jackson "); +MODULE_DESCRIPTION("Arducam Pivariety v4l2 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/arducam-pivariety.h b/drivers/media/i2c/arducam-pivariety.h new file mode 100644 index 0000000000000..99d5ada309e8c --- /dev/null +++ b/drivers/media/i2c/arducam-pivariety.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ARDUCAM_PIVARIETY_H_ +#define _ARDUCAM_PIVARIETY_H_ + +#define DEVICE_REG_BASE 0x0100 +#define PIXFORMAT_REG_BASE 0x0200 +#define FORMAT_REG_BASE 0x0300 +#define CTRL_REG_BASE 0x0400 +#define IPC_REG_BASE 0x0600 + +#define ARDUCAM_MODE_STANDBY 0x00 +#define ARDUCAM_MODE_STREAMING 0x01 + +#define MODE_SELECT_REG (DEVICE_REG_BASE | 0x0000) +#define DEVICE_VERSION_REG (DEVICE_REG_BASE | 0x0001) +#define SENSOR_ID_REG (DEVICE_REG_BASE | 0x0002) +#define DEVICE_ID_REG (DEVICE_REG_BASE | 0x0003) +#define SYSTEM_IDLE_REG (DEVICE_REG_BASE | 0x0007) + +#define PIXFORMAT_INDEX_REG (PIXFORMAT_REG_BASE | 0x0000) +#define PIXFORMAT_TYPE_REG (PIXFORMAT_REG_BASE | 0x0001) +#define PIXFORMAT_ORDER_REG (PIXFORMAT_REG_BASE | 0x0002) +#define MIPI_LANES_REG (PIXFORMAT_REG_BASE | 0x0003) +#define FLIPS_DONT_CHANGE_ORDER_REG (PIXFORMAT_REG_BASE | 0x0004) + +#define RESOLUTION_INDEX_REG (FORMAT_REG_BASE | 0x0000) +#define FORMAT_WIDTH_REG (FORMAT_REG_BASE | 0x0001) +#define FORMAT_HEIGHT_REG (FORMAT_REG_BASE | 0x0002) + +#define CTRL_INDEX_REG (CTRL_REG_BASE | 0x0000) +#define CTRL_ID_REG (CTRL_REG_BASE | 0x0001) +#define CTRL_MIN_REG (CTRL_REG_BASE | 0x0002) +#define CTRL_MAX_REG (CTRL_REG_BASE | 0x0003) +#define CTRL_STEP_REG (CTRL_REG_BASE | 0x0004) +#define CTRL_DEF_REG (CTRL_REG_BASE | 0x0005) +#define CTRL_VALUE_REG (CTRL_REG_BASE | 0x0006) + +#define IPC_SEL_TARGET_REG (IPC_REG_BASE | 0x0000) +#define IPC_SEL_TOP_REG (IPC_REG_BASE | 0x0001) +#define IPC_SEL_LEFT_REG (IPC_REG_BASE | 0x0002) +#define IPC_SEL_WIDTH_REG (IPC_REG_BASE | 0x0003) +#define IPC_SEL_HEIGHT_REG (IPC_REG_BASE | 0x0004) +#define IPC_DELAY_REG (IPC_REG_BASE | 0x0005) + +#define NO_DATA_AVAILABLE 0xFFFFFFFE + +#define DEVICE_ID 0x0030 + +#define I2C_READ_RETRY_COUNT 3 +#define I2C_WRITE_RETRY_COUNT 2 + +#define V4L2_CID_ARDUCAM_BASE (V4L2_CID_USER_BASE + 0x1000) +#define V4L2_CID_ARDUCAM_EXT_TRI (V4L2_CID_ARDUCAM_BASE + 1) +#define V4L2_CID_ARDUCAM_IRCUT (V4L2_CID_ARDUCAM_BASE + 8) +#define V4L2_CID_ARDUCAM_STROBE_SHIFT (V4L2_CID_ARDUCAM_BASE + 14) +#define V4L2_CID_ARDUCAM_STROBE_WIDTH (V4L2_CID_ARDUCAM_BASE + 15) +#define V4L2_CID_ARDUCAM_MODE (V4L2_CID_ARDUCAM_BASE + 16) + +enum image_dt { + IMAGE_DT_YUV420_8 = 0x18, + IMAGE_DT_YUV420_10, + + IMAGE_DT_YUV420CSPS_8 = 0x1C, + IMAGE_DT_YUV420CSPS_10, + IMAGE_DT_YUV422_8, + IMAGE_DT_YUV422_10, + IMAGE_DT_RGB444, + IMAGE_DT_RGB555, + IMAGE_DT_RGB565, + IMAGE_DT_RGB666, + IMAGE_DT_RGB888, + + IMAGE_DT_RAW6 = 0x28, + IMAGE_DT_RAW7, + IMAGE_DT_RAW8, + IMAGE_DT_RAW10, + IMAGE_DT_RAW12, + IMAGE_DT_RAW14, +}; + +enum bayer_order { + BAYER_ORDER_BGGR = 0, + BAYER_ORDER_GBRG = 1, + BAYER_ORDER_GRBG = 2, + BAYER_ORDER_RGGB = 3, + BAYER_ORDER_GRAY = 4, +}; + +enum yuv_order { + YUV_ORDER_YUYV = 0, + YUV_ORDER_YVYU = 1, + YUV_ORDER_UYVY = 2, + YUV_ORDER_VYUY = 3, +}; + +struct arducam_resolution { + u32 width; + u32 height; +}; + +struct arducam_format { + u32 index; + u32 mbus_code; + u32 bayer_order; + u32 data_type; + u32 num_resolution_set; + struct arducam_resolution *resolution_set; +}; + +#endif diff --git a/drivers/media/i2c/ds90ub954.c b/drivers/media/i2c/ds90ub954.c new file mode 100644 index 0000000000000..7885b6c640594 --- /dev/null +++ b/drivers/media/i2c/ds90ub954.c @@ -0,0 +1,1748 @@ +/* + * ds90ub954.c - TI DS90UB954 deserializer and DS90UB953 serializer driver + * + * Copyright (c) 2020, Institut of Embedded Systems ZHAW + * + * This program is for the DS90UB954 FDP Link III deserializer in connection + * with the DS90UB953 serializer from Texas Instruments + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ds90ub954.h" + +#define ENABLE_SYSFS_TP /* /sys/bus/i2c/devices/0-0018 */ + +static const struct of_device_id ds90ub954_of_match[] = { + { + .compatible = "ti,ds90ub954", + }, + {/* sentinel */}, +}; + +const struct regmap_config ds90ub954_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +const struct regmap_config ds90ub953_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +/* 4096x2160 */ +static const u8 ds90ub95x_tp_reg_val[] = { + /* Indirect Pattern Gen Registers */ + 0xB0, 0x00, + 0xB1, TI954_REG_IA_PGEN_CTL, + 0xB2, (1<regmap, reg, val); + if(err) { + dev_err(&priv->client->dev, + "Cannot read register 0x%02x (%d)!\n", reg, err); + } + return err; +} + +static int ds90ub954_write(const struct ds90ub954_priv *priv, unsigned int reg, + unsigned int val) +{ + int err; + + err = regmap_write(priv->regmap, reg, val); + if(err) { + dev_err(&priv->client->dev, + "Cannot write register 0x%02x (%d)!\n", reg, err); + } + return err; +} + +static int ds90ub954_write_rx_port(struct ds90ub954_priv *priv, int rx_port, + int addr, int val) +{ + struct device *dev = &priv->client->dev; + int err = 0; + int port_reg = 0; + + /* rx_port = 0 -> choose rx_port 0 + * rx_port = 1 -> choose rx_port 1 + * rx_port = 2 -> choose rx_port 0 and 1 */ + if(rx_port > 2 || rx_port < 0) { + dev_err(dev, "invalid port number %d. Cannot be selected\n", + rx_port); + err = -EINVAL; + goto write_rx_port_err; + } + + /* Check if port is selected, select port if needed */ + if(priv->sel_rx_port != rx_port) { + /* Set RX_WRITE_PORT_1 if rx_port is 1, + * set RX_WRITE_PORT_0 if rx_port is 0, + * set RX_WRITE_PORT_0 & _1 if rx_port is 2 */ + if(rx_port == 2) { + port_reg |= (0b11); + } else { + /* Setting RX_WRITE_PORT_1 or _0 */ + port_reg |= (1<client->dev, + "error writing register TI954_REG_FPD3_PORT_SEL (0x%02x)\n", + TI954_REG_FPD3_PORT_SEL); + goto write_rx_port_err; + } + priv->sel_rx_port = rx_port; + } + err = ds90ub954_write(priv, addr, val); + if(unlikely(err)) { + dev_err(&priv->client->dev, "error writing register (0x%02x)\n", + addr); + goto write_rx_port_err; + } + +write_rx_port_err: + return err; +} + +#ifdef DEBUG +static int ds90ub954_read_rx_port(struct ds90ub954_priv *priv, int rx_port, + int addr, int *val) +{ + struct device *dev = &priv->client->dev; + int err = 0; + int port_reg = 0; + + /* rx_port = 0 -> choose rx_port 0 + * rx_port = 1 -> choose rx_port 1 */ + if(rx_port > 2 || rx_port < 0) { + dev_err(dev, "invalid port number %d. Cannot be selected\n", + rx_port); + err = -EINVAL; + goto read_rx_port_err; + } + + /* Check if port is selected, select port if needed */ + if(priv->sel_rx_port != rx_port) { + /* Setting RX_WRITE_PORT_1 or _0 */ + port_reg |= (1<client->dev, + "error writing register TI954_REG_FPD3_PORT_SEL (0x%02x)\n", + TI954_REG_FPD3_PORT_SEL); + goto read_rx_port_err; + } + priv->sel_rx_port = rx_port; + } + err = ds90ub954_read(priv, addr, val); + if(unlikely(err)) { + dev_err(&priv->client->dev, "error read register (0x%02x)\n", + addr); + goto read_rx_port_err; + } + +read_rx_port_err: + return err; +} + +static int ds90ub954_read_ia_reg(struct ds90ub954_priv *priv, int reg, int *val, + int ia_config) +{ + int err = 0; + + /* ia_configs: + * 0000: CSI-2 Pattern Generator & Timing Registers + * 0001: FPD-Link III RX Port 0 Reserved Registers + * 0010: FPD-Link III RX Port 1 Reserved Registers + * 0101: FPD-Link III RX Shared Reserved Registers + * 0110: Simultaneous write to FPD-Link III RX Reserved */ + + /* Check if bank is selected, select bank if needed */ + if(priv->sel_ia_config != ia_config) { + err = ds90ub954_write(priv, TI954_REG_IND_ACC_CTL, ia_config); + if(unlikely(err)) { + dev_err(&priv->client->dev, + "error writing register TI954_REG_IND_ACC_CTL (0x%02x)\n", + TI954_REG_IND_ACC_CTL); + goto read_ia_reg_err; + } + priv->sel_ia_config = ia_config; + } + err = ds90ub954_write(priv, TI954_REG_IND_ACC_ADDR, reg); + if(unlikely(err)) { + dev_err(&priv->client->dev, + "error writing register TI954_REG_IND_ACC_ADDR (0x%02x)\n", + TI954_REG_IND_ACC_ADDR); + goto read_ia_reg_err; + } + + err = ds90ub954_read(priv, TI954_REG_IND_ACC_DATA, val); + if(unlikely(err)) { + dev_err(&priv->client->dev, + "error reading register TI954_REG_IND_ACC_DATA (0x%02x)\n", + TI954_REG_IND_ACC_DATA); + goto read_ia_reg_err; + } + +read_ia_reg_err: + return err; +} +#endif + +static int ds90ub954_disable_testpattern(struct ds90ub954_priv *priv) +{ + struct device *dev = &priv->client->dev; + int err = 0; + /* Indirect Pattern Gen Registers */ + err = ds90ub954_write(priv, 0xB0, 0x00); + if(err) + goto init_err; + err = ds90ub954_write(priv, 0xB1, TI954_REG_IA_PGEN_CTL); + if(err) + goto init_err; + err = ds90ub954_write(priv, 0xB2, (0<client->dev; + int i, err = 0; + for(i = 0; i < ARRAY_SIZE(ds90ub95x_tp_reg_val); i += 2) { + + err = ds90ub954_write(priv, ds90ub95x_tp_reg_val[i], + ds90ub95x_tp_reg_val[i+1]); + if(unlikely(err)) { + dev_info(dev, "%s: enable test pattern failed\n", __func__); + return err; + } + } + dev_info(dev, "%s: enable test pattern successful\n", __func__); + return err; +} + +#ifdef DEBUG +static int ds90ub954_debug_prints(struct ds90ub954_priv *priv) +{ + int i, val, ia_config = 0, err = 0; + /* print CSI timing of tx port 0 */ + dev_info(dev, "%s: CSI timing\n", __func__); + err = ds90ub954_read_ia_reg(priv, TI954_REG_IA_CSI0_TCK_PREP, &val, + ia_config); + if(unlikely(err)) + return err; + dev_info(dev, "%s: TCK_PREP: 0x%02x\n", __func__, val); + + err = ds90ub954_read_ia_reg(priv, TI954_REG_IA_CSI0_TCK_ZERO, &val, + ia_config); + if(unlikely(err)) + return err; + dev_info(dev, "%s: TCK_ZERO: 0x%02x\n", __func__, val); + + err = ds90ub954_read_ia_reg(priv, TI954_REG_IA_CSI0_TCK_TRAIL, &val, + ia_config); + if(unlikely(err)) + return err; + dev_info(dev, "%s: TCK_TRAIL: 0x%02x\n", __func__, val); + + err = ds90ub954_read_ia_reg(priv, TI954_REG_IA_CSI0_TCK_POST, &val, + ia_config); + if(unlikely(err)) + return err; + dev_info(dev, "%s: TCK_POST: 0x%02x\n", __func__, val); + + err = ds90ub954_read_ia_reg(priv, TI954_REG_IA_CSI0_THS_PREP, &val, + ia_config); + if(unlikely(err)) + return err; + dev_info(dev, "%s: THS_PREP: 0x%02x\n", __func__, val); + + err = ds90ub954_read_ia_reg(priv, TI954_REG_IA_CSI0_THS_ZERO, &val, + ia_config); + if(unlikely(err)) + return err; + dev_info(dev, "%s: THS_ZERO: 0x%02x\n", __func__, val); + + err = ds90ub954_read_ia_reg(priv, TI954_REG_IA_CSI0_THS_TRAIL, &val, + ia_config); + if(unlikely(err)) + return err; + dev_info(dev, "%s: THS_TRAIL: 0x%02x\n", __func__, val); + + err = ds90ub954_read_ia_reg(priv, TI954_REG_IA_CSI0_THS_EXIT, &val, + ia_config); + if(unlikely(err)) + return err; + dev_info(dev, "%s: THS_EXIT: 0x%02x\n", __func__, val); + + err = ds90ub954_read_ia_reg(priv, TI954_REG_IA_CSI0_TPLX, &val, + ia_config); + if(unlikely(err)) + return err; + dev_info(dev, "%s: CSI0_TPLX: 0x%02x\n", __func__, val); + + /* measure refclk */ + for(i = 0; i < 5; i++) { + err = ds90ub954_read(priv, TI954_REG_REFCLK_FREQ, &val); + if(err) + return err; + dev_info(dev, "%s: REFCLK_FREQ measurement %d, %d\n", __func__, + i, val); + } + return err; +}; +#endif + +static int ds90ub954_init(struct ds90ub954_priv *priv, int rx_port) +{ + struct device *dev = &priv->client->dev; + unsigned char dev_id, rev; + int i, val; + int err = 0; + char id_code[TI954_RX_ID_LENGTH + 1]; + int ser_nr = 0; + struct ds90ub953_priv *ds90ub953; + + /*---------------------------------------------------------------------- + * init deserializer + *--------------------------------------------------------------------*/ + dev_info(dev, "%s starting\n", __func__); + + /* Read device id of deserializer */ + err = ds90ub954_read(priv, TI954_REG_I2C_DEV_ID, &val); + if(unlikely(err)) + goto init_err; + + dev_id = (unsigned char)val; + + /* Read revision ID of deserializer */ + err = ds90ub954_read(priv, TI954_REG_REVISION, &val); + if(unlikely(err)) + goto init_err; + + rev = (unsigned char)val; + + /* Read FPD3_RX_ID3 registers */ + memset(id_code, 0, sizeof(id_code)); + for(i = 0; i < TI954_RX_ID_LENGTH; i++) { + err = ds90ub954_read(priv, TI954_REG_FPD3_RX_ID0 + i, &val); + if(err) { + goto init_err; + } + id_code[i] = (char)val; + } + dev_info(dev, "%s: device ID: 0x%x, code:%s, revision: 0x%x\n", + __func__, dev_id, id_code, rev); + + /* disable BuiltIn Self Test */ + err = ds90ub954_write(priv, TI954_REG_BIST_CONTROL, 0); + if(unlikely(err)) + goto init_err; + + /* set CSI speed (REFCLK 25 MHz) + * 00 : 1.6 Gbps serial rate + * 01 : Reserved + * 10 : 800 Mbps serial rate + * 11 : 400 Mbps serial rate */ + switch(priv->csi_lane_speed) { + case 400: + val=0x3; + break; + case 800: + val=0x2; + break; + default: + val=0x0; + break; + } + + err = ds90ub954_write(priv, TI954_REG_CSI_PLL_CTL, + (val<csi_lane_count) { + case 1: + val = TI954_CSI_1_LANE; + break; + case 2: + val = TI954_CSI_2_LANE; + break; + case 3: + val = TI954_CSI_3_LANE; + break; + default: + val = TI954_CSI_4_LANE; + break; + } + + err = ds90ub954_write(priv, TI954_REG_CSI_CTL, + (1<conts_clk<test_pattern == 1) { + dev_info(dev, "%s: deserializer init testpattern\n", __func__); + err = ds90ub954_init_testpattern(priv); + if(unlikely(err)) { + dev_info(dev, + "%s: deserializer init testpattern failed\n", + __func__); + } + } + + /* Setting PASS and LOCK to "all enabled receiver ports */ + val = 0b00111100; + err = ds90ub954_write(priv, TI954_REG_RX_PORT_CTL, val); + if(unlikely(err)) + goto init_err; + + /* for loop goes through each serializer */ + for( ; ser_nr < priv->num_ser; ser_nr++) { + ds90ub953 = priv->ser[ser_nr]; + if(ds90ub953->initialized == 0) { + continue; + } + rx_port = ds90ub953->rx_channel; + + dev_info(dev, "%s: start init of serializer rx_port %i\n", + __func__, rx_port); + + /* Get TI954_REG_RX_PORT_CTL and enable receiver rx_port */ + err = ds90ub954_read(priv, TI954_REG_RX_PORT_CTL, &val); + if(unlikely(err)) + goto ser_init_failed; + + val |= (1<<(TI954_PORT0_EN+rx_port)); + err = ds90ub954_write(priv, TI954_REG_RX_PORT_CTL, val); + if(unlikely(err)) + goto ser_init_failed; + + /* wait for receiver to calibrate link */ + msleep(400); + + /* enable csi forwarding */ + err = ds90ub954_read(priv, TI954_REG_FWD_CTL1, &val); + if(unlikely(err)) + goto ser_init_failed; + + val &= (0xEF<i2c_pt<i2c_address<gpio0_oc<gpio1_oc<gpio2_oc<gpio3_oc<i2c_alias_num) && (i < NUM_ALIAS); i++) { + val = ds90ub953->i2c_slave[i]; + if(val == 0) { + continue; + } + err = ds90ub954_write_rx_port(priv, rx_port, + TI954_REG_SLAVE_ID0+i, + (val<i2c_alias[i]; + if(val == 0) { + continue; + } + err = ds90ub954_write_rx_port(priv, rx_port, + TI954_REG_ALIAS_ID0+i, + (val<vc_map); + if(unlikely(err)) + goto ser_init_failed; + else { + val = ds90ub953->vc_map & 0b11; + dev_info(dev, "%s: VC-ID 0 mapped to %i\n", __func__, val); + val = ((ds90ub953->vc_map & 0b1100)>>2); + dev_info(dev, "%s: VC-ID 1 mapped to %i\n", __func__, val); + val = ((ds90ub953->vc_map & 0b110000)>>4); + dev_info(dev, "%s: VC-ID 2 mapped to %i\n", __func__, val); + val = ((ds90ub953->vc_map & 0b11000000)>>6); + dev_info(dev, "%s: VC-ID 3 mapped to %i\n", __func__, val); + } + + /* all rx_port specific registers set for rx_port X */ + dev_info(dev, "%s: init of deserializer rx_port %i successful\n", + __func__, rx_port); + continue; +ser_init_failed: + dev_err(dev, "%s: init deserializer rx_port %i failed\n", + __func__, rx_port); + dev_err(dev, "%s: deserializer rx_port %i is deactivated\n", + __func__, rx_port); + + ds90ub953->initialized = 0; + + /* DISABLE RX PORT */ + err = ds90ub954_read(priv, TI954_REG_RX_PORT_CTL, &val); + if(err) + continue; + val &= (0xFF^(1<<(TI954_PORT0_EN+rx_port))); + err = ds90ub954_write(priv, TI954_REG_RX_PORT_CTL, val); + if(err) + continue; + /* DISABLE CSI FORWARDING */ + err = ds90ub954_read(priv, TI954_REG_FWD_CTL1, &val); + if(err) + continue; + val |= (1<<(TI954_FWD_PORT0_DIS+rx_port)); + err = ds90ub954_write(priv, TI954_REG_FWD_CTL1, val); + if(err) + continue; + continue; + } + + /* setup gpio forwarding, default all input */ + err = ds90ub954_write(priv, TI954_REG_GPIO_INPUT_CTL, + (1<client->dev; + int err = 0; + + if(gpio_is_valid(priv->pass_gpio)) { + err = gpio_request(priv->pass_gpio, "ds90ub954_pass_gpio"); + if(unlikely(err < 0)) { + dev_err(dev, "unable to request pass_gpio (%d)\n", err); + goto done; + } + err = gpio_direction_input(priv->pass_gpio); + if(unlikely(err < 0)) { + dev_err(dev, "unable to configure pass_gpio as input (%d)\n", err); + goto done; + } + } + + if(gpio_is_valid(priv->lock_gpio)) { + err = gpio_request(priv->lock_gpio, "ds90ub954_lock_gpio"); + if(unlikely(err < 0)) { + dev_err(dev, "unable to request lock_gpio (%d)\n", err); + goto done; + } + err = gpio_direction_input(priv->lock_gpio); + if(unlikely(err < 0)) { + dev_err(dev, "unable to configure lock_gpio as input (%d)\n", err); + goto done; + } + } + + if(gpio_is_valid(priv->pdb_gpio)) { + err = gpio_request(priv->pdb_gpio, "ds90ub954_pdb_gpio"); + if(unlikely(err < 0)) { + dev_err(dev, "unable to request pdb_gpio (%d)\n", err); + goto done; + } + err = gpio_direction_output(priv->pdb_gpio, 0); + if(unlikely(err < 0)) { + dev_err(dev, "unable to configure pdb_gpio as output (%d)\n", err); + goto done; + } + } + +done: + return err; +} + +static void ds90ub954_free_gpio(const struct ds90ub954_priv *priv) +{ + + if(priv->pass_gpio >= 0) { + gpio_free(priv->pass_gpio); + } + if(priv->lock_gpio >= 0) { + gpio_free(priv->lock_gpio); + } + if(priv->pdb_gpio >= 0) { + gpio_free(priv->pdb_gpio); + } +} + +static void ds90ub954_pwr_enable(const struct ds90ub954_priv *priv) +{ + if(priv->pdb_gpio >= 0) { + gpio_set_value_cansleep(priv->pdb_gpio, 1); + } +} + +static void ds90ub954_pwr_disable(const struct ds90ub954_priv *priv) +{ + if(priv->pdb_gpio >= 0) { + gpio_set_value_cansleep(priv->pdb_gpio, 0); + } +} + +static int ds90ub954_parse_dt(struct ds90ub954_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *np = dev->of_node; + const struct of_device_id *match; + int gpio; + int err = 0; + int val = 0; + + if(!np) + return -ENODEV; + + dev_info(dev, "%s: deserializer:\n", __func__); + match = of_match_device(ds90ub954_of_match, dev); + if(!match) { + dev_err(dev, "Failed to find matching dt id\n"); + return -ENODEV; + } + + gpio = of_get_named_gpio(np, "pass-gpio", 0); + if(gpio < 0) { + if(gpio == -EPROBE_DEFER) { + err = gpio; + dev_err(dev, "pass-gpio read failed: (%d)\n", err); + return err; + } + dev_info(dev, "pass-gpio not found, ignoring\n"); + } + priv->pass_gpio = gpio; + + gpio = of_get_named_gpio(np, "lock-gpio", 0); + if(gpio < 0) { + if(gpio == -EPROBE_DEFER) { + err = gpio; + dev_err(dev, "lock-gpio read failed: (%d)\n", err); + return err; + } + dev_info(dev, "lock-gpio not found, ignoring\n"); + } + priv->lock_gpio = gpio; + + gpio = of_get_named_gpio(np, "pdb-gpio", 0); + if(gpio < 0) { + if(gpio == -EPROBE_DEFER) { + err = gpio; + dev_err(dev, "pdb-gpio read failed: (%d)\n", err); + return err; + } + dev_info(dev, "pdb-gpio not found, ignoring\n"); + } + priv->pdb_gpio = gpio; + + err = of_property_read_u32(np, "csi-lane-count", &val); + if(err) { + dev_info(dev, "%s: - csi-lane-count property not found\n", __func__); + + /* default value: 4 */ + priv->csi_lane_count = 4; + dev_info(dev, "%s: - csi-lane-count set to default val: 4\n", __func__); + } else { + /* set csi-lane-count*/ + priv->csi_lane_count = val; + dev_info(dev, "%s: - csi-lane-count %i\n", __func__, val); + } + + err = of_property_read_u32(np, "csi-lane-speed", &val); + if(err) { + dev_info(dev, "%s: - csi-lane-speed property not found\n", __func__); + + /* default value: 4 */ + priv->csi_lane_speed = 1600; + dev_info(dev, "%s: - csi-lane-speed set to default val: 4\n", __func__); + } else { + /* set csi-lane-speed*/ + priv->csi_lane_speed = val; + dev_info(dev, "%s: - csi-lane-speed %i\n", __func__, val); + } + + if(of_property_read_bool(np, "test-pattern")) { + dev_info(dev, "%s: - test-pattern enabled\n", __func__); + priv->test_pattern = 1; + } else { + /* default value: 0 */ + priv->test_pattern = 0; + dev_info(dev, "%s: - test-pattern disabled\n", __func__); + } + + if(of_property_read_bool(np, "continuous-clock")) { + dev_info(dev, "%s: - continuous clock enabled\n", __func__); + priv->conts_clk = 1; + } else { + /* default value: 0 */ + priv->conts_clk = 0; + dev_info(dev, "%s: - discontinuous clock used\n", __func__); + } + + return 0; + +} + +#ifdef ENABLE_SYSFS_TP +static ssize_t test_pattern_show_des(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ds90ub954_priv *priv; + priv = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, + "Test Pattern is set to: %i\n", priv->test_pattern); +} + +static ssize_t test_pattern_set_des(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ds90ub954_priv *priv; + int testpat; + + priv = dev_get_drvdata(dev); + sscanf(buf, "%d", &testpat); + if(testpat > 1 || testpat < 0) { + dev_info(dev, + "Invalid value: %i for test pattern (0/1)\n", testpat); + return PAGE_SIZE; + } + if(testpat == 1) { + dev_info(dev, "enabling testpattern for deserializer\n"); + priv->test_pattern = 1; + ds90ub954_init_testpattern(priv); + } else { + dev_info(dev, "disabling testpattern for deserializer\n"); + ds90ub954_disable_testpattern(priv); + } + return PAGE_SIZE; +} +static DEVICE_ATTR(test_pattern_des, 0664,test_pattern_show_des, + test_pattern_set_des); +#endif + +/*------------------------------------------------------------------------------ + * DS90UB953 FUNCTIONS + *----------------------------------------------------------------------------*/ + +static int ds90ub953_read(struct ds90ub953_priv *priv, unsigned int reg, + unsigned int *val) +{ + int err; + err = regmap_read(priv->regmap, reg, val); + if(err) { + dev_err(&priv->client->dev, + "Cannot read subdev 0x%02x register 0x%02x (%d)!\n", + priv->client->addr, reg, err); + } + return err; +} + +static int ds90ub953_write(const struct ds90ub953_priv *priv, unsigned int reg, + unsigned int val) +{ + int err; + err = regmap_write(priv->regmap, reg, val); + if(err) { + dev_err(&priv->parent->client->dev, + "Cannot write subdev 0x%02x register 0x%02x (%d)!\n", + priv->client->addr, reg, err); + } + return err; +} + +static int ds90ub953_disable_testpattern(struct ds90ub953_priv *priv) +{ + struct device *dev = &priv->client->dev; + int err = 0; + /* Indirect Pattern Gen Registers */ + err = ds90ub953_write(priv, 0xB0, 0x00); + if(err) + goto init_err; + err = ds90ub953_write(priv, 0xB1, TI954_REG_IA_PGEN_CTL); + if(err) + goto init_err; + err = ds90ub953_write(priv, 0xB2, (0<client->dev; + int err = 0; + int i; + for(i = 0; i < ARRAY_SIZE(ds90ub95x_tp_reg_val); i += 2) { + + err = ds90ub953_write(priv, ds90ub95x_tp_reg_val[i], + ds90ub95x_tp_reg_val[i+1]); + if(unlikely(err)) { + dev_info(dev, "%s: enable test pattern failed\n", __func__); + return err; + } + } + dev_info(dev, "%s: enable test pattern successful\n", __func__); + return err; +} + +#ifdef ENABLE_SYSFS_TP +static ssize_t test_pattern_show_ser(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ds90ub953_priv *priv; + priv = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, + "Test Pattern is set to: %i\n", priv->test_pattern); +} + +static ssize_t test_pattern_set_ser(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ds90ub953_priv *priv; + int testpat; + + priv = dev_get_drvdata(dev); + + sscanf(buf, "%d", &testpat); + if(testpat > 1 || testpat < 0) { + dev_info(dev, + "Invalid value: %i for test pattern (0/1)\n", testpat); + return PAGE_SIZE; + } + if(testpat == 1) { + dev_info(dev, "enabling testpattern for deserializer\n"); + priv->test_pattern = 1; + ds90ub953_init_testpattern(priv); + } else { + dev_info(dev, "disabling testpattern for deserializer\n"); + ds90ub953_disable_testpattern(priv); + } + return PAGE_SIZE; +} +static DEVICE_ATTR(test_pattern_ser, 0664, test_pattern_show_ser, + test_pattern_set_ser); + +#endif + +static int ds90ub953_init(struct ds90ub953_priv *priv) +{ + struct device *dev = &priv->client->dev; + int val, dev_id, i; + int err = 0; + char id_code[TI953_RX_ID_LENGTH + 1]; + + dev_info(dev, "%s: start\n", __func__); + + err = ds90ub953_read(priv, TI953_REG_I2C_DEV_ID, &val); + if(unlikely(err)) + goto init_err; + + dev_id = (unsigned char)val; + + memset(id_code, 0, sizeof(id_code)); + for(i = 0; i < TI953_RX_ID_LENGTH; i++) { + err = ds90ub953_read(priv, TI953_REG_FPD3_RX_ID0 + i, &val); + if(unlikely(err)) + goto init_err; + id_code[i] = (char)val; + } + dev_info(dev, "%s: device ID: 0x%x, code:%s\n", __func__, dev_id, id_code); + + /* set to csi lanes */ + switch(priv->csi_lane_count) { + case 1: + val = TI953_CSI_LANE_SEL1; + break; + case 2: + val = TI953_CSI_LANE_SEL2; + break; + default: + val = TI953_CSI_LANE_SEL4; + break; + } + err = ds90ub953_write(priv, TI953_REG_GENERAL_CFG, + (1<conts_clk<hs_clk_div<div_m_val<div_n_val<gpio0_oe) + val |= 0b00010000; + else + val |= 0b00000001; + + if(priv->gpio1_oe) + val |= 0b00100000; + else + val |= 0b00000010; + + if(priv->gpio2_oe) + val |= 0b01000000; + else + val |= 0b00000100; + + if(priv->gpio3_oe) + val |= 0b10000000; + else + val |= 0b00001000; + + err = ds90ub953_write(priv, TI953_REG_GPIO_CTRL, val); + if(unlikely(err)) + goto init_err; + + err = ds90ub953_write(priv, TI953_REG_LOCAL_GPIO_DATA, + (0xf<test_pattern == 1) { + dev_info(dev,"%s: serializer rx_port %i init testpattern\n", + __func__, priv->rx_channel); + err = ds90ub953_init_testpattern(priv); + if(unlikely(err)) + dev_info(dev, + "%s: serializer rx_port %i init testpattern failed\n", + __func__, priv->rx_channel); + } + +#ifdef ENABLE_SYSFS_TP + /* device attribute on sysfs */ + dev_set_drvdata(dev, priv); + err = device_create_file(dev, &dev_attr_test_pattern_ser); + if(unlikely(err < 0)) + dev_err(dev, "serializer %i cant create device attribute %s\n", + priv->rx_channel, dev_attr_test_pattern_ser.attr.name); +#endif + dev_info(dev, "%s: successful\n", __func__); + +init_err: + return err; +} + +static void ds90ub953_free(struct ds90ub954_priv *priv) +{ + int i; + for(i = 0; i < priv->num_ser; i++) { + if(priv->ser[i]) + i2c_unregister_device(priv->ser[i]->client); + } +} + +static int ds90ub953_regmap_init(struct ds90ub954_priv *priv, int ser_nr) +{ + struct regmap *new_regmap = NULL; + struct device *dev = &priv->client->dev; + int err = 0; + + /* setup now regmap */ + new_regmap = devm_regmap_init_i2c(priv->ser[ser_nr]->client, + &ds90ub953_regmap_config); + if(IS_ERR_VALUE(priv->regmap)) { + err = PTR_ERR(priv->regmap); + dev_err(dev, "regmap init of subdevice failed (%d)\n", err); + return err; + } + dev_info(dev, "%s init regmap done\n", __func__); + + priv->ser[ser_nr]->regmap = new_regmap; + return err; +} + +static int ds90ub953_alloc(struct ds90ub954_priv *priv, int ser_nr) +{ + struct ds90ub953_priv *priv_ser; + struct device *dev = &priv->client->dev; + + priv_ser = devm_kzalloc(dev, sizeof(struct ds90ub953_priv), GFP_KERNEL); + if(!priv) + return -ENOMEM; + + priv->ser[ser_nr] = priv_ser; + priv->ser[ser_nr]->initialized = 0; + return 0; +} + +static int ds90ub953_i2c_client(struct ds90ub954_priv *priv, int ser_nr, + int addr) +{ + struct i2c_client *new_client = NULL; + struct device *dev = &priv->client->dev; + + struct i2c_board_info *ser_board_info; + ser_board_info = devm_kzalloc(dev, sizeof(struct i2c_board_info), GFP_KERNEL); + ser_board_info->addr = addr; +//i2c_new_device ----> i2c_new_client_device +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0) + new_client = i2c_new_device(priv->client->adapter, ser_board_info); +#else + new_client = i2c_new_client_device(priv->client->adapter, ser_board_info); +#endif + + if(!new_client) { + dev_warn(dev, "failed to add i2c client\n"); + return -1; + } + + priv->ser[ser_nr]->client = new_client; + dev_info(dev, "%s init client done\n", __func__); + return 0; +} + +static int ds90ub953_parse_dt(struct i2c_client *client, + struct ds90ub954_priv *priv) +{ + struct device *dev = &client->dev; + struct device_node *des = dev->of_node; + struct device_node *ser; + struct device_node *sers; + struct of_phandle_args i2c_addresses; + struct ds90ub953_priv *ds90ub953; + int i = 0; + + u32 val = 0; + int err = 0; + int counter = 0; + priv->num_ser = 0; + + /* get serializers device_node from dt */ + sers = of_get_child_by_name(des, "serializers"); + if(!sers) { + dev_info(dev, "%s: no serializers found in device tree\n", + __func__); + return 0; + } + + dev_info(dev, "%s: parsing serializers device tree:\n", __func__); + + /* go through all serializers in list */ + for_each_child_of_node(sers, ser) { + + if(counter >= NUM_SERIALIZER) { + dev_info(dev, "%s: too many serializers found in device tree\n", + __func__); + break; + } + + /* allocate memory for serializer */ + err = ds90ub953_alloc(priv, counter); + if(err) { + dev_info(dev, "%s: - allocating ds90ub953 failed\n", + __func__); + goto next; + } + ds90ub953 = priv->ser[counter]; + + /* get rx-channel */ + err = of_property_read_u32(ser, "rx-channel", &val); + if(err) { + dev_info(dev, "%s: - rx-channel property not found\n", + __func__); + /* default value: 0 */ + ds90ub953->rx_channel = 0; + dev_info(dev, "%s: rx-channel set to default val: 0\n", + __func__); + } else { + /* set rx-channel*/ + ds90ub953->rx_channel = val; + dev_info(dev,"%s: - serializer rx-channel: %i\n", + __func__, val); + } + + if(of_property_read_bool(ser, "test-pattern")) { + dev_info(dev, "%s: - test-pattern enabled\n", __func__); + ds90ub953->test_pattern = 1; + } else { + /* default value: 0 */ + ds90ub953->test_pattern = 0; + dev_info(dev,"%s: -test-pattern disabled\n", __func__); + } + + err = of_property_read_u32(ser, "csi-lane-count", &val); + if(err) { + dev_info(dev, "%s: - csi-lane-count property not found\n", + __func__); + /* default value: 4 */ + ds90ub953->csi_lane_count = 4; + dev_info(dev, "%s: csi-lane-count set to default val: 4\n", + __func__); + } else { + /* set csi-lane-count*/ + ds90ub953->csi_lane_count = val; + dev_info(dev, "%s: - csi-lane-count %i\n", __func__, val); + } + + /* GPIO output enable */ + err = of_property_read_u32(ser, "gpio0-output-enable", &val); + if(err) { + dev_info(dev, "%s: - gpio0-output-enable property not found\n", + __func__); + /* default value: 0 */ + ds90ub953->gpio0_oe = 0; + dev_info(dev, "%s: gpio0-output-enable to default val: 0\n", + __func__); + } else { + /* set gpio0-output-enable*/ + ds90ub953->gpio0_oe = val; + dev_info(dev, "%s: - gpio0-output-enable %i\n", + __func__, val); + } + + err = of_property_read_u32(ser, "gpio1-output-enable", &val); + if(err) { + dev_info(dev, "%s: - gpio1-output-enable property not found\n", + __func__); + + /* default value: 0 */ + ds90ub953->gpio1_oe = 0; + dev_info(dev, "%s: gpio1-output-enable to default val: 0\n", + __func__); + } else { + /* set gpio1-output-enable*/ + ds90ub953->gpio1_oe = val; + dev_info(dev, "%s: - gpio1-output-enable %i\n", + __func__, val); + } + + err = of_property_read_u32(ser, "gpio2-output-enable", &val); + if(err) { + dev_info(dev, "%s: - gpio2-output-enable property not found\n", + __func__); + /* default value: 0 */ + ds90ub953->gpio2_oe = 0; + dev_info(dev, "%s: gpio2-output-enable to default val: 0\n", + __func__); + } else { + /* set gpio2-output-enable*/ + ds90ub953->gpio2_oe = val; + dev_info(dev,"%s: - gpio2-output-enable %i\n", __func__, + val); + } + + err = of_property_read_u32(ser, "gpio3-output-enable", &val); + if(err) { + dev_info(dev, "%s: - gpio3-output-enable property not found\n", + __func__); + /* default value: 0 */ + ds90ub953->gpio3_oe = 0; + dev_info(dev, "%s: gpio3-output-enable to default val: 0\n", + __func__); + } else { + /* set gpio3-output-enable*/ + ds90ub953->gpio3_oe = val; + dev_info(dev, "%s: - gpio3-output-enable %i\n", + __func__, val); + } + + /* GPIO output control */ + err = of_property_read_u32(ser, "gpio0-control", &val); + if(err) { + dev_info(dev, "%s: - gpio0-control property not found\n", + __func__); + /* default value: 0b1000 */ + ds90ub953->gpio0_oc = 0b1000; + dev_info(dev, "%s: gpio0-control to default val: 0b1000\n", + __func__); + } else { + /* set gpio0-control*/ + ds90ub953->gpio0_oc = val; + dev_info(dev,"%s: - gpio0-control %i\n", + __func__, val); + } + + err = of_property_read_u32(ser, "gpio1-control", &val); + if(err) { + dev_info(dev, "%s: - gpio1-control property not found\n", + __func__); + + /* default value: 0b1000 */ + ds90ub953->gpio1_oc = 0b1000; + dev_info(dev, "%s: gpio1-control to default val: 0b1000\n", + __func__); + } else { + /* set gpio1-control*/ + ds90ub953->gpio1_oc = val; + dev_info(dev, "%s: - gpio1-control %i\n", + __func__, val); + } + + err = of_property_read_u32(ser, "gpio2-control", &val); + if(err) { + dev_info(dev, "%s: - gpio2-control property not found\n", + __func__); + /* default value: 0b1000 */ + ds90ub953->gpio2_oc = 0b1000; + dev_info(dev, "%s: gpio2-control to default val: 0b1000\n", + __func__); + } else { + /* set gpio2-control*/ + ds90ub953->gpio2_oc = val; + dev_info(dev, "%s: - gpio2-control %i\n", + __func__, val); + } + + err = of_property_read_u32(ser, "gpio3-control", &val); + if(err) { + dev_info(dev, "%s: - gpio3-control property not found\n", + __func__); + /* default value: 0b1000 */ + ds90ub953->gpio3_oc = 0b1000; + dev_info(dev, "%s: gpio3-control to default val: 0b1000\n", + __func__); + } else { + /* set gpio3-control*/ + ds90ub953->gpio3_oc = val; + dev_info(dev, "%s: - gpio3-control %i\n", + __func__, val); + } + + err = of_property_read_u32(ser, "hs-clk-div", &val); + if(err) { + dev_info(dev, "%s: - hs-clk-div property not found\n", + __func__); + + /* default value: 0x2 */ + ds90ub953->hs_clk_div = 0x2; + dev_info(dev, "%s: - hs-clk-div set to default val: 0x2 (div by 4)\n", + __func__); + } else { + switch(val) { + case 1: + ds90ub953->hs_clk_div = 0b000; + break; + case 2: + ds90ub953->hs_clk_div = 0b001; + break; + case 4: + ds90ub953->hs_clk_div = 0b010; + break; + case 8: + ds90ub953->hs_clk_div = 0b011; + break; + case 16: + ds90ub953->hs_clk_div = 0b100; + break; + default: + ds90ub953->hs_clk_div = 0b010; + dev_info(dev, "%s: - %i no valid value for hs-clk-div\n", + __func__, val); + break; + } + dev_info(dev,"%s: - hs-clk-div set to val: %i (div by %i)\n", + __func__, ds90ub953->hs_clk_div, val); + } + + err = of_property_read_u32(ser, "div-m-val", &val); + if(err) { + dev_info(dev, "%s: - div-m-val property not found\n", + __func__); + /* default value: 1 */ + ds90ub953->div_m_val = 1; + dev_info(dev, "%s: - div-m-val set to default val: 1\n", + __func__); + } else { + /* set div-m-val*/ + ds90ub953->div_m_val = val; + dev_info(dev, "%s: - div-m-val %i\n", __func__, val); + } + + err = of_property_read_u32(ser, "div-n-val", &val); + if(err) { + dev_info(dev, "%s: - div-n-val property not found\n", + __func__); + /* default value: 0x28 */ + ds90ub953->div_n_val = 0x28; + dev_info(dev, "%s: - div-n-val set to default val: 0x28\n", + __func__); + } else { + /* set div-n-val*/ + ds90ub953->div_n_val = val; + dev_info(dev, "%s: - div-n-val %i\n", __func__, val); + } + + /* get i2c address */ + err = of_property_read_u32(ser, "i2c-address", &val); + if(err) { + dev_info(dev, "%s: - i2c-address not found\n", __func__); + ds90ub953->i2c_address = 0x18; + dev_info(dev, "%s: - i2c-address set to default val: 0x18\n", + __func__); + } else { + dev_info(dev, "%s: - i2c-address: 0x%X \n", __func__, val); + ds90ub953->i2c_address=val; + } + + err = ds90ub953_i2c_client(priv, counter, val); + if(err) { + dev_info(dev, "%s: - ds90ub953_i2c_client failed\n", + __func__); + goto next; + } + + err = ds90ub953_regmap_init(priv, counter); + if(err) { + dev_info(dev, "%s: - ds90ub953_regmap_init failed\n", + __func__); + goto next; + } + + /* get i2c-slave addresses*/ + err = of_parse_phandle_with_args(ser, "i2c-slave", "list-cells", + 0, &i2c_addresses); + if(err) { + dev_info(dev, "%s: - reading i2c-slave addresses failed\n", + __func__); + ds90ub953->i2c_alias_num = 0; + } else { + ds90ub953->i2c_alias_num = i2c_addresses.args_count; + /* writting i2c slave addresses into array*/ + for(i = 0; (i < i2c_addresses.args_count) && + (ii2c_slave[i] = i2c_addresses.args[i]; + } + } + + /* get slave-aliases */ + err = of_parse_phandle_with_args(ser, "slave-alias", + "list-cells", 0, &i2c_addresses); + if(err) { + dev_info(dev, "%s: - reading i2c slave-alias addresses failed\n", + __func__); + ds90ub953->i2c_alias_num = 0; + } else { + dev_info(dev, "%s: - num of slave alias pairs: %i\n", + __func__, i2c_addresses.args_count); + /* writting i2c alias addresses into array*/ + for(i=0; (ii2c_alias[i] = i2c_addresses.args[i]; + dev_info(dev, "%s: - slave addr: 0x%X, alias addr: 0x%X\n", + __func__, ds90ub953->i2c_slave[i], + ds90ub953->i2c_alias[i]); + } + } + + if(of_property_read_bool(ser, "continuous-clock")) { + dev_info(dev, "%s: - continuous clock enabled\n", + __func__); + ds90ub953->conts_clk = 1; + } else { + /* default value: 0 */ + ds90ub953->conts_clk = 0; + dev_info(dev, "%s: - discontinuous clock used\n", + __func__); + } + + if(of_property_read_bool(ser, "i2c-pass-through-all")) { + dev_info(dev, "%s: - i2c-pass-through-all enabled\n", + __func__); + ds90ub953->i2c_pt = 1; + } else { + /* default value: 0 */ + ds90ub953->i2c_pt = 0; + dev_info(dev, "%s: - i2c-pass-through-all disabled\n", + __func__); + } + + err = of_property_read_u32(ser, "virtual-channel-map", &val); + if(err) { + dev_info(dev, "%s: - virtual-channel-map property not found\n", + __func__); + ds90ub953->vc_map = 0xE4; + dev_info(dev, "%s: - virtual-channel-map set to default val: 0xE4\n", + __func__); + } else { + /* set vc_map*/ + ds90ub953->vc_map = val; + dev_info(dev, "%s: - virtual-channel-map 0x%x\n", __func__, val); + } + + /* all initialization of this serializer complete */ + ds90ub953->initialized = 1; + priv->num_ser += 1; + dev_info(dev, "%s: serializer %i successfully parsed\n", __func__, + counter); +next: + counter +=1; + } + dev_info(dev, "%s: done\n", __func__); + return 0; + +} + +/*------------------------------------------------------------------------------ + * PROBE FUNCTION + *----------------------------------------------------------------------------*/ + +static int ds90ub954_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ds90ub954_priv *priv; + struct device *dev = &client->dev; + int err; + int i = 0; + + dev_info(dev, "%s: start\n", __func__); + + priv = devm_kzalloc(dev, sizeof(struct ds90ub954_priv), GFP_KERNEL); + if(!priv) + return -ENOMEM; + + priv->client = client; + i2c_set_clientdata(client, priv); + + /* force to select the rx port the first time */ + priv->sel_rx_port = -1; + + /* force to set ia config the first time */ + priv->sel_ia_config = -1; + + err = ds90ub954_parse_dt(priv); + if(unlikely(err < 0)) { + dev_err(dev, "%s: error parsing device tree\n", __func__); + goto err_parse_dt; + } + + err = ds90ub954_init_gpio(priv); + if(unlikely(err < 0)) { + dev_err(dev, "%s: error initializing gpios\n", __func__); + goto err_init_gpio; + } + + priv->regmap = devm_regmap_init_i2c(client, &ds90ub954_regmap_config); + if(IS_ERR_VALUE(priv->regmap)) { + err = PTR_ERR(priv->regmap); + dev_err(dev, "%s: regmap init failed (%d)\n", __func__, err); + goto err_regmap; + } + + ds90ub953_parse_dt(client, priv); + + /* turn on deserializer */ + ds90ub954_pwr_enable(priv); + + msleep(6); // wait for sensor to start + + /* init deserializer */ + err = ds90ub954_init(priv, 0); + if(unlikely(err)) { + dev_err(dev, "%s: error initializing ds90ub954\n", __func__); + goto err_regmap; + } + dev_info(dev, "%s: init ds90ub954_done\n", __func__); + + msleep(500); + + /* init serializers */ + for( ; inum_ser; i++) { + /* check if serializer is initialized */ + if(priv->ser[i]->initialized == 0) + continue; + /*init serializer*/ + err = ds90ub953_init(priv->ser[i]); + if(err) { + dev_info(dev, + "serializer %i init_serializer failed\n", i); + continue; + } + } + + msleep(500); + +#ifdef ENABLE_SYSFS_TP + /* device attribute on sysfs */ + dev_set_drvdata(dev, priv); + err = device_create_file(dev, &dev_attr_test_pattern_des); + if(unlikely(err < 0)) + dev_err(dev, "deserializer cant create device attribute %s\n", + dev_attr_test_pattern_des.attr.name); +#endif + + return 0; + +err_regmap: + ds90ub953_free(priv); + ds90ub954_pwr_disable(priv); + ds90ub954_free_gpio(priv); +err_init_gpio: +err_parse_dt: + devm_kfree(dev, priv); + return err; +} + +static int ds90ub954_remove(struct i2c_client *client) +{ + struct ds90ub954_priv *priv = dev_get_drvdata(&client->dev); + + ds90ub953_free(priv); + ds90ub954_pwr_disable(priv); + ds90ub954_free_gpio(priv); + + dev_info(&client->dev, "ds90ub954 removed\n"); + return 0; +} + +static const struct i2c_device_id ds90ub954_id[] = { + {"ti,ds90ub954", 0}, + {/* sentinel */}, +}; + +static struct i2c_driver ds90ub954_driver = { + .driver = + { + .name = "i2c-ds90ub954", + .of_match_table = of_match_ptr(ds90ub954_of_match), + }, + .probe = ds90ub954_probe, + .remove = ds90ub954_remove, + .id_table = ds90ub954_id, +}; + +MODULE_DEVICE_TABLE(of, ds90ub954_of_match); + +MODULE_DEVICE_TABLE(i2c, ds90ub954_id); + +module_i2c_driver(ds90ub954_driver); + +MODULE_AUTHOR("Philipp Huber "); +MODULE_AUTHOR("Simone Schwizer "); +MODULE_DESCRIPTION("i2c ds90ub954 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ds90ub954.h b/drivers/media/i2c/ds90ub954.h new file mode 100644 index 0000000000000..2d794ef0f80b6 --- /dev/null +++ b/drivers/media/i2c/ds90ub954.h @@ -0,0 +1,1200 @@ +/* + * ds90ub954.c - TI DS90UB954 deserializer and DS90UB953 serializer driver + * + * Copyright (c) 2020, Institut of Embedded Systems ZHAW + * + * This program is for the DS90UB954 FDP Link III deserializer in connection + * with the DS90UB953 serializer from Texas Instruments + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef I2C_DS90UB954_H +#define I2C_DS90UB954_H + +#include + +/*------------------------------------------------------------------------------ + * Deserializer registers + *----------------------------------------------------------------------------*/ +#define TI954_REG_I2C_DEV_ID 0x00 +#define TI954_DES_ID 0 +#define TI954_DEVICE_ID 1 + +#define TI954_REG_RESET 0x01 +#define TI954_DIGITAL_RESET0 0 +#define TI954_DIGITAL_RESET1 1 +#define TI954_RESTART_AUTOLOAD 2 + +#define TI954_REG_GENERAL_CFG 0x2 +#define TI954_FORCE_REFCLK_DET 0 +#define TI954_RX_PARITY_CHECKER_ENABLE 1 +#define TI954_OUTPUT_SLEEP_STATE_SELECT 2 +#define TI954_OUTPUT_ENABLE 3 +#define TI954_OUTPUT_EN_MODE 4 +#define TI954_I2C_MASTER_EN 5 + +#define TI954_REG_REVISION 0x03 +#define TI954_MASK_ID 0 + +#define TI954_REG_DEVICE_STS 0x04 +#define TI954_LOCK 2 +#define TI954_PASS 3 +#define TI954_REFCLK_VALID 4 +#define TI954_CFG_INIT_DONE 6 +#define TI954_CFG_CKSUM_STS 7 + +#define TI954_REG_PAR_ERR_THOLD_HI 0x5 +#define TI954_PAR_ERR_THOLD_HI 0 + +#define TI954_REG_PAR_ERR_THOLD_LO 0x6 +#define TI954_PAR_ERR_THOLD_LO 0 + +#define TI954_REG_BCC_WD_CTL 0x07 +#define TI954_BCC_WATCHDOG_TIMER_DISABLE 0 +#define TI954_BCC_WATCHDOG_TIMER 1 + +#define TI954_REG_I2C_CTL1 0x08 +#define TI954_I2C_FILTER_DEPTH 0 +#define TI954_I2C_SDA_HOLD 4 +#define TI954_LOCAL_WRITE_DISABLE 7 + +#define TI954_REG_I2C_CTL2 0x09 +#define TI954_I2C_BUS_TIMER_DISABLE 0 +#define TI954_I2C_BUS_TIMER_SPEEDUP 1 +#define TI954_SDA_OUTPUT_DELAY 2 +#define TI954_SDA_OUTPUT_SETUP 4 + +#define TI954_REG_SCL_HIGH_TIME 0x0a +#define TI954_SCL_HIGH_TIME 0 + +#define TI954_REG_SCL_LOW_TIME 0x0b +#define TI954_SCL_LOW_TIME 0 + +#define TI954_REG_RX_PORT_CTL 0x0c +#define TI954_PORT0_EN 0 +#define TI954_PORT1_ER 1 +#define TI954_LOCK_SEL 2 +#define TI954_PASS_SEL 4 + +#define TI954_REG_IO_CTL 0x0d +#define TI954_IO_SUPPLY_MODE 4 +#define TI954_IO_SUPPLY_MODE_OV 6 +#define TI954_SEL3P3V 7 + +#define TI954_REG_GPIO_PIN_STS 0x0e +#define TI954_GPIO_STS 0 +#define TI954_GPIO0_STS 0 +#define TI954_GPIO1_STS 1 +#define TI954_GPIO2_STS 2 +#define TI954_GPIO3_STS 3 +#define TI954_GPIO4_STS 4 +#define TI954_GPIO5_STS 5 +#define TI954_GPIO6_STS 6 + +#define TI954_REG_GPIO_INPUT_CTL 0x0f +#define TI954_GPIO_INPUT_EN 0 +#define TI954_GPIO0_INPUT_EN 0 +#define TI954_GPIO1_INPUT_EN 1 +#define TI954_GPIO2_INPUT_EN 2 +#define TI954_GPIO3_INPUT_EN 3 +#define TI954_GPIO4_INPUT_EN 4 +#define TI954_GPIO5_INPUT_EN 5 +#define TI954_GPIO6_INPUT_EN 6 + +#define TI954_REG_GPIO0_PIN_CTL 0x10 +#define TI954_GPIO0_OUT_EN 0 +#define TI954_GPIO0_OUT_VAL 1 +#define TI954_GPIO0_OUT_SRC 2 +#define TI954_GPIO0_OUT_SEL 5 + +#define TI954_REG_GPIO1_PIN_CTL 0x11 +#define TI954_GPIO1_OUT_EN 0 +#define TI954_GPIO1_OUT_VAL 1 +#define TI954_GPIO1_OUT_SRC 2 +#define TI954_GPIO1_OUT_SEL 5 + +#define TI954_REG_GPIO2_PIN_CTL 0x12 +#define TI954_GPIO2_OUT_EN 0 +#define TI954_GPIO2_OUT_VAL 1 +#define TI954_GPIO2_OUT_SRC 2 +#define TI954_GPIO2_OUT_SEL 5 + +#define TI954_REG_GPIO3_PIN_CTL 0x13 +#define TI954_GPIO3_OUT_EN 0 +#define TI954_GPIO3_OUT_VAL 1 +#define TI954_GPIO3_OUT_SRC 2 +#define TI954_GPIO3_OUT_SEL 5 + +#define TI954_REG_GPIO4_PIN_CTL 0x14 +#define TI954_GPIO4_OUT_EN 0 +#define TI954_GPIO4_OUT_VAL 1 +#define TI954_GPIO4_OUT_SRC 2 +#define TI954_GPIO4_OUT_SEL 5 + +#define TI954_REG_GPIO5_PIN_CTL 0x15 +#define TI954_GPIO5_OUT_EN 0 +#define TI954_GPIO5_OUT_VAL 1 +#define TI954_GPIO5_OUT_SRC 2 +#define TI954_GPIO5_OUT_SEL 5 + +#define TI954_REG_GPIO6_PIN_CTL 0x16 +#define TI954_GPIO6_OUT_EN 0 +#define TI954_GPIO6_OUT_VAL 1 +#define TI954_GPIO6_OUT_SRC 2 +#define TI954_GPIO6_OUT_SEL 5 + +#define TI954_REG_RESERVED 0x17 + +#define TI954_REG_FS_CTL 0x18 +#define TI954_FS_GEN_ENABLE 0 +#define TI954_FS_GEN_MODE 1 +#define TI954_FS_INIT_STATE 2 +#define TI954_FS_SINGLE 3 +#define TI954_FS_MODE 4 + +#define TI954_REG_FS_HIGH_TIME_1 0x19 +#define TI954_FRAMESYNC_HIGH_TIME_1 0 + +#define TI954_REG_FS_HIGH_TIME_0 0x1A +#define TI954_FRAMESYNC_HIGH_TIME_0 0 + +#define TI954_REG_FS_LOW_TIME_1 0x1B +#define TI954_FRAMESYNC_LOW_TIME_1 0 + +#define TI954_REG_FS_LOW_TIME_0 0x1C +#define TI954_FRAMESYNC_LOW_TIME_0 0 + +#define TI954_REG_MAX_FRM_HI 0x1d +#define TI954_MAX_FRAME_HI 0 + +#define TI954_REG_MAX_FRM_LO 0x1e +#define TI954_MAX_FRAME_LO 0 + +#define TI954_REG_CSI_PLL_CTL 0x1f +#define TI954_CSI_TX_SPEED 0 + +#define TI954_REG_FWD_CTL1 0x20 +#define TI954_FWD_PORT0_DIS 4 +#define TI954_FWD_PORT1_DIS 6 + +#define TI_954_FWD_CTL2 0x21 +#define TI954_CSI0_RR_RWD 0 +#define TI954_CSI0_SYNC_FWD 2 +#define TI954_FWD_SYNC_AS_AVAIL 6 +#define TI954_CSI_REPLICATE 7 + +#define TI954_REG_FWD_STS 0x22 +#define TI954_FWD_SYNC0 0 +#define TI954_FWD_SYNC_FAIL0 2 + +#define TI954_REG_INTERRUPT_CTL 0x23 +#define TI954_IE_RX0 0 +#define TI954_IE_RX1 1 +#define TI954_IE_CSI_TX0 4 +#define TI954_INT_EN 7 + +#define TI954_REG_INTERRUPT_STS 0x24 +#define TI954_IS_RX0 0 +#define TI954_IS_RX1 1 +#define TI954_IS_CSI_TX0 4 +#define TI954_INTERRUPT_STS 7 + +#define TI954_REG_TS_CONFIG 0x25 +#define TI954_TS_MODE 0 +#define TI954_TS_FREERUN 1 +#define TI954_TS_AS_AVAIL 3 +#define TI954_TS_RES_CTL 4 +#define TI954_FS_POLARITY 6 + +#define TI954_REG_TS_CONTROL 0x26 +#define TI954_TS_ENABLE0 0 +#define TI954_TS_ENABLE1 1 +#define TI954_TS_FREEZE 4 + +#define TS954_TS_LINE_HI 0x27 +#define TI954_TS_LINE_HI 0 + +#define TI954_REG_TS_LINE_LO 0x28 +#define TI954_TS_LINE_LO 0 + +#define TI954_REG_TS_STATUS 0x29 +#define TI954_TS_VALID0 0 +#define TI954_TS_VALID1 1 +#define TI954_TS_READY 42 + +#define TI954_TI954_REG_TIMESTAMP_P0_HI 0x2a +#define TI954_TIMESTAMP_P0_HI 03 + +#define TI954_TI954_REG_TIMESTAMP_P0_LO 0x2b +#define TI954_TIMESTAMP_P0_LO 04 + +#define TI954_TI954_REG_TIMESTAMP_P1_HI 0x2c +#define TI954_TIMESTAMP_P1_HI 0 + +#define TI954_REG_TIMESTAMP_P1_LO 0x2d +#define TI954_TIMESTAMP_P1_LO 0 + +#define TI954_REG_CSI_CTL 0x33 +#define TI954_CSI_ENABLE 0 +#define TI954_CSI_CONTS_CLOCK 1 +#define TI954_CSI_ULP 2 +#define TI954_CSI_LANE_COUNT 4 +#define TI954_CSI_CAL_EN 6 +#define TI954_CSI_4_LANE 0 +#define TI954_CSI_3_LANE 1 +#define TI954_CSI_2_LANE 2 +#define TI954_CSI_1_LANE 3 + +#define TI954_REG_CSI_CTL2 0x34 +#define TI954_CSI_CAL_PERIODIC 0 +#define TI954_CSI_CAL_SINGLE 1 +#define TI954_CSI_CAL_INV 2 +#define TI954_CSI_PASS_MODE 3 + +#define TI954_REG_CSI_STS 0x35 +#define TI954_TX_PORT_PASS 0 +#define TI954_TX_PORT_SYNC 1 + +#define TI954_REG_CSI_TX_ICR 0x36 +#define TI954_IE_CSI_PASS 0 +#define TI954_IE_SCI_PASS_ERROR 1 +#define TI954_IE_CSI_SYNC 2 +#define TI954_IE_CSI_SYNC_ERROR 3 + +#define TI954_REG_CSI_TX_ISR 0x37 +#define TI954_IS_CSI_PASS 0 +#define TI954_IS_CSI_PASS_ERR_OR 1 +#define TI954_IS_CSI_SYNC 2 +#define TI954_IS_CSI_SYNC_ERR_OR 3 +#define TI954_IS_RX_PORT_INT 4 + +#define TI954_REG_CSI_TEST_CTL 0x38 + +#define TI954_REG_CSI_TEST_PATT_HI 0x39 +#define TI954_CSI_TEST_PATT_HI 0 + +#define TI954_REG_CSI_TEST_PATT_LO 0x3a +#define TI954_CSI_TEST_PATT_LO 0 + +#define TI954_REG_SFILTER_CFG 0x41 +#define TI954_SFILTER_MIN 0 +#define TI954_SFILTER_MAX 4 + +#define TI954_REG_AEQ_CTL1 0x42 +#define TI954_AEQ_SFILTER_EN 0 +#define TI954_AEQ_OUTER_LOOP 1 +#define TI954_AEQ_2STEP_EN 2 +#define TI954_AEQ_ERR_CTL 4 + +#define TI954_REG_AEQ_ERR_THOLD 0x43 +#define TI954_AEQ_ERR_THRESHOLD 0 + +#define TI954_REG_FPD3_CAP 0x4a +#define TI954_FPD3_ENC_CRC_CAP 4 + +#define TI954_REG_RAQ_EMBED_DTYPE 0x4b +#define TI954_EMBED_DTYPE_ID 0 +#define TI954_EMBED_DTYPE_EN 6 + +#define TI954_REG_FPD3_PORT_SEL 0x4c +#define TI954_RX_WRITE_PORT_0 0 +#define TI954_RX_WRITE_PORT_1 1 +#define TI954_RX_READ_PORT 4 +#define TI954_PHYS_PORT_NUM 6 + +#define TI954_REG_RX_PORT_STS1 0x4d +#define TI954_LOCK_STS 0 +#define TI954_PORT_PASS 1 +#define TI954_PARITY_ERROR 2 +#define TI954_BCC_SEQ_ERROR 3 +#define TI954_LOCK_STS_CHG 4 +#define TI954_BCC_CRC_ERROR 5 +#define TI954_RX_PORT_NUM 6 + +#define TI954_REG_RX_PORT_STS2 0x4e +#define TI954_LINE_CNT_CHG 0 +#define TI954_CABLE_FAULT 1 +#define TI954_FREQ_STABLE 2 +#define TI954_CSI_ERROR 3 +#define TI954_BUFFER_ERROR 4 +#define TI954_FPD3_ENCODE_ERROR 5 +#define TI954_LINE_LEN_CHG 6 +#define TI954_LINE_LEN_UNSTABLE 7 + +#define TI954_REG_RX_FREQ_HIGH 0x4f +#define TI954_FREQ_CNT_HIGH 0 + +#define TI954_REG_RX_FERQ_LOQ 0x50 +#define TI954_FREQ_CNT_LOW 0 + +#define TI954_REG_SENSOR_STS_0 0x51 +#define TI954_VOLT0_SENSE_ALARM 0 +#define TI954_VOLT1_SENSE_ALARM 1 +#define TI954_TEMP_SENSE_ALARM 2 +#define TI954_LINK_DETECT_ALARM 3 +#define TI954_BCC_ALARM 4 +#define TI954_CSI_ALARM 5 + +#define TI954_REG_SENSOR_STS_1 0x52 +#define TI954_VOLT0_SENSE_LEVEL 0 +#define TI954_VOLT1_SENSE_LEVEL 4 + +#define TI954_REG_SENSOR_STS_2 0x53 +#define TI954_TEMP_SENSE_LEVEL 0 + +#define TI954_REG_SENSOR_ST_3 0x54 +#define TI954_CSI_CNTRL_ERR 0 +#define TI954_CSI_SYNC_ERR 1 +#define TI954_CSI_SOT_ERR 2 +#define TI954_CSI_CHKSUM_ERR 3 +#define TI954_CSI_ECC_2BIT_ERR 4 + +#define TI954_REG_RX_PAR_ERR_HI 0x55 +#define TI954_PAR_ERROR_BYTE_1 0 + +#define TI954_REG_RX_PAR_ERR_LO 0x56 +#define TI954_PAR_ERROR_BYTE_0 0 + +#define TI954_REG_BIST_ERR_COUNT 0x57 +#define TI954_BIST_ERROR_COUNT 0 + +#define TI954_REG_BCC_CONFIG 0x58 +#define TI954_BC_FREQ_SELECT 0 +#define TI954_BC_CRC_GENERAOTR_ENABLE 3 +#define TI954_BC_ALWAYS_ON 4 +#define TI954_AUTO_ACK_ALL 5 +#define TI954_I2C_PASS_THROUGH 6 +#define TI954_I2C_PASS_THROUGH_ALL 7 +#define TI954_BC_FREQ_2M5 0 +#define TI954_BC_FREQ_1M 2 +#define TI954_BC_FREQ_25M 5 +#define TI954_BC_FREQ_50M 6 +#define TI954_BC_FREQ_250 7 + + +#define TI954_REG_DATAPATH_CTL1 0x59 +#define TI954_FC_GPIO_EN 0 +#define TI954_OVERRIDE_FC_CONFIG 7 + +#define TI965_REG_DATAPATH_CTL2 0x5a + +#define TI954_REG_SER_ID 0x5b +#define TI954_FREEZE_DEVICE_ID 0 +#define TI954_SER_ID 1 + +#define TI954_REG_SER_ALIAS_ID 0x5c +#define TI954_SER_AUTO_ACK 0 +#define TI954_SER_ALIAS_ID 1 + +#define TI954_REG_SLAVE_ID0 0x5d +#define TI954_SLAVE_ID0 1 +#define TI954_REG_SLAVE_ID1 0x5e +#define TI954_SLAVE_ID1 1 +#define TI954_REG_SLAVE_ID2 0x5f +#define TI954_SLAVE_ID2 1 +#define TI954_REG_SLAVE_ID3 0x60 +#define TI954_SLAVE_ID3 1 +#define TI954_REG_SLAVE_ID4 0x61 +#define TI954_SLAVE_ID4 1 +#define TI954_REG_SLAVE_ID5 0x62 +#define TI954_SLAVE_ID5 1 +#define TI954_REG_SLAVE_ID6 0x63 +#define TI954_SLAVE_ID6 1 +#define TI954_REG_SLAVE_ID7 0x64 +#define TI954_SLAVE_ID7 1 + +#define TI954_REG_ALIAS_ID0 0x65 +#define TI954_ALIAS_ID0 1 +#define TI954_REG_ALIAS_ID1 0x66 +#define TI954_ALIAS_ID1 1 +#define TI954_REG_ALIAS_ID2 0x67 +#define TI954_ALIAS_ID2 1 +#define TI954_REG_ALIAS_ID3 0x68 +#define TI954_ALIAS_ID3 1 +#define TI954_REG_ALIAS_ID4 0x644 +#define TI954_ALIAS_ID4 1 +#define TI954_REG_ALIAS_ID5 0x6a +#define TI954_ALIAS_ID5 1 +#define TI954_REG_ALIAS_ID6 0x6b +#define TI954_ALIAS_ID6 1 +#define TI954_REG_ALIAS_ID7 0x6c +#define TI954_ALIAS_ID7 1 + +#define TI954_REG_PORT_CONFIG 0x6d +#define TI954_FPD3_MODE 0 +#define TI954_COAX_MODE 2 +#define TI954_CSI_FWD_LEN 3 +#define TI954_CSI_FWD_ECC 4 +#define TI954_CSI_FWD_CKSUM 5 +#define TI954_CSI_WAIT_FS 6 +#define TI954_CSI_WAIT_FS1 7 + +#define TI954_REG_BC_GPIO_CTL0 0x6e +#define TI954_BC_GPIO0_SEL 0 +#define TI954_BC_GPIO1_SEL 4 + +#define TI954_REG_BC_GPIO_CTL1 0x6f +#define TI954_BC_GPIO2_SEL 0 +#define TI954_BC_GPIO3_SEL 4 + +#define TI954_REG_RAW10_ID 0x70 +#define TI954_RAW10_DT 0 +#define TI954_RAW10_VC 6 + +#define TI954_REG_RAW12_ID 0x71 +#define TI954_RAW12_DT 0 +#define TI954_RAW12_VC 6 + +#define TI954_REG_CSI_VC_MAP 0x72 +#define TI954_CSI_VC_MAP 0 + +#define TI954_REG_LINE_COUNT_HI 0x73 +#define TI954_LINE_COUNT_HI 0 + +#define TI954_REG_LINE_COUNT_LO 0x74 +#define TI954_LINE_COUNT_LO 0 + +#define TI954_REG_LINE_LEN_1 0x750 +#define TI954_LINE_LEN_HI 0 + +#define TI954_REG_LINE_LEN_0 0x76 +#define TI954_LINE_LEN_LO 0 + +#define TI954_REG_FREQ_DET_CTL 0x77 +#define TI954_FREW_LO_THR 0 +#define TI954_FREQ_STABLE_THR 4 +#define TI954_FREQ_HYST 6 + +#define TI954_REG_MAILBOX_1 0x78 +#define TI954_MAILBOX_0 0 + +#define TI954_REG_MAILBOX_2 0x79 +#define TI954_MAILBOX_1 0 + +#define TI954_REG_CSI_RX_STS 0x7a +#define TI954_ECC1_ERR 0 +#define TI954_ECC2_ERR 1 +#define TI954_CKSUM_ERR 2 +#define TI954_LENGTH_ERR 3 + +#define TI954_REG_CSI_ERR_COUNTER 0x7b +#define TI954_CSI_ERR_CNT 0 + +#define TI954_REG_PORT_CONFIG2 0x7c +#define TI954_FV_POLARITY 0 +#define TI954_LV_POLARITY 1 +#define TI954_DISCARD_ON_FRAME_SIZE 3 +#define TI954_DISCARD_ON_LINE_SIZE 4 +#define TI954_DISCARD_ON_PAR_ERR 5 +#define TI954_RAW10_8BIT_CTL 6 + +#define TI954_REG_PORT_PASS_CTL 0x7d +#define TI954_PASS_THRESHOLD 0 +#define TI954_PASS_WDOG_DIS 2 +#define TI954_PASS_PARITY_ERR 3 +#define TI954_PASS_LINE_SIZE 4 +#define TI954_PASS_LINE_CNT 5 +#define TI954_PASS_DISCARD_EN 7 + +#define TI954_REG_SEN_INT_RISE_CTL 0x7e +#define TI954_SEN_INT_RISE_MASK 0 + +#define TI954_REG_SEN_INT_FALL_CTL 0x7f +#define TI954_SEN_INT_FALL_MASK 0 + +#define TI954_REG_REFCLK_FREQ 0xa5 +#define TI954_REFCLK_FREQ 0 + +#define TI954_REG_IND_ACC_CTL 0xb0 +#define TI954_IA_READ 0 +#define TI954_IA_AUTO_INC 1 +#define TI954_IA_SEL 2 + +#define TI954_REG_IND_ACC_ADDR 0xb1 +#define TI954_IA_ADDR 0 + +#define TI954_REG_IND_ACC_DATA 0xb2 +#define TI954_IA_DATA 0 + + +#define TI954_REG_BIST_CONTROL 0xb3 +#define TI954_BIST_EN 0 +#define TI954_BIST_CLOCK_SOURCE 1 +#define TI954_BIST_PIN_CONFIG 3 +#define TI954_BIST_OUT_MODE 6 + +#define TI954_REG_MODE_IDX_STS 0xb8 +#define TI954_MODE 0 +#define TI954_MODE_DONE 1 +#define TI954_IDX 4 +#define TI954_IDX_DONE 7 + +#define TI954_REG_LINK_ERROR_COUNT 0xb9 +#define TI954_LINK_ERR_THRESH 0 +#define TI954_LINK_ERR_COUNT_EN 4 +#define TI954_LINK_SFIL_WAIT 5 + +#define TI954_REG_FPD3_ENC_CTL 0xba +#define TI954_FPD3_ENC_CRC_DIS 7 + +#define TI954_REG_FV_MIN_TIME 0xbc +#define TI954_FRAME_VALID_MIN 0 + +#define TI954_REG_GPIO_PD_CTL 0xbe +#define TI954_GPIO0_PD_DIS 0 +#define TI954_GPIO1_PD_DIS 1 +#define TI954_GPIO2_PD_DIS 2 +#define TI954_GPIO3_PD_DIS 3 +#define TI954_GPIO4_PD_DIS 4 +#define TI954_GPIO5_PD_DIS 5 +#define TI954_GPIO6_PD_DIS 6 + +#define TI954_REG_PORT_DEBUG 0xd0 +#define TI954_FORCE_1_BC_ERROR 0 +#define TI954_FORCE_BC_ERRORS 1 +#define TI954_SER_BIST_ACT 5 + +#define TI954_REG_AEQ_CTL2 0xd2 +#define TI954_SET_AEQ_FLOOR 2 +#define TI954_AEQ_RESTART 3 +#define TI954_AEQ_1ST_LOCK_MODE 4 +#define TI954_ADAPTIVE_EQ_RELOCK_TIME 5 + +#define TI954_REG_AEQ_STATUS 0xd3 +#define TI954_EQ_STATUS 0 + +#define TI954_REG_ADAPTIVE_EQ_BYPASS 0xd4 +#define TI954_ADAPTIVE_EQ_BYPASS 0 +#define TI954_EQ_STAGE_2_SELECT_VALUE 1 +#define TI954_AE_LOCK_MODE 4 +#define TI954_EQ_STAGE_1_SELECT_VALUE 5 + +#define TI954_REG_AEQ_MIN_MAX 0xd5 +#define TI954_ADAPTIVE_EQ_FLOOR_VALUE 0 +#define TI954_AEQ_MAX 4 + +#define TI954_REG_PRT_ICR_HI 0xd8 +#define TI954_IE_BC_CRC_ERR 0 +#define TI954_IE_BCC_SEQ_ERR 1 +#define TI954_IE_FPD3_ENC_ERR 2 + +#define TI954_REG_PORT_ICR_LO 0xd9 +#define TI954_IE_LOCK_STS 0 +#define TI954_IE_PORT_PASS 1 +#define TI954_IE_FPD3_PAR_ERR 2 +#define TI954_IE_CSI_RX_ERR 3 +#define TI954_IE_BUFFER_ERR 4 +#define TI954_IE_LINE_CNT_CHG 5 +#define TI954_IE_LINE_LNE_CHG 6 + +#define TI954_REG_PORT_ISR_HI 0xda +#define TI954_IS_BCC_CRC_ERR 0 +#define TI954_IS_BCC_CEQ_ERR 1 +#define TI954_IS_FPD3_ENC_ERR 2 +#define TI954_IS_FC_SENS_STS 3 +#define TI954_IE_FC_GPIO 4 + +#define TI954_REG_PORT_ISR_LO 0xdb +#define TI954_IS_LOCK_STS 0 +#define TI954_IS_PORT_PASS 1 +#define TI954_IS_PFD3_PAR_ERR 2 +#define TI954_IS_SCI_RX_ERR 3 +#define TI954_IS_BUFFER_ERR 4 +#define TI954_IS_LINE_CNT_CHG 5 +#define TI954_IS_LINE_LEN_CHG 6 + +#define TI954_REG_FC_GPIO_STS 0xdc +#define TI954_FC_GPIO0_STS 0 +#define TI954_FC_GPIO1_STS 1 +#define TI954_FC_GPIO2_STS 2 +#define TI954_FC_GPIO3_STS 3 +#define TI954_GPIO0_INT_STS 4 +#define TI954_GPIO1_INT_STS 5 +#define TI954_GPIO2_INT_STS 6 +#define TI954_GPIO3_INT_STS 7 + +#define TI954_REG_FC_GPIO_ICR 0xdd +#define TI954_GPIO0_RISE_IE 0 +#define TI954_GPIO0_FALL_IE 1 +#define TI954_GPIO1_RISE_IE 2 +#define TI954_GPIO1_FALL_IE 3 +#define TI954_GPIO2_RISE_IE 4 +#define TI954_GPIO2_FALL_IE 5 +#define TI954_GPIO3_RISE_IE 6 +#define TI954_GPIO3_FALL_IE 7 + +#define TI954_REG_SEN_INT_RISE_STS 0xde +#define TI954_SEN_INT_RISE 0 + +#define TI954_REG_SEN_INT_FALL_STS 0xdf +#define TI954_SEN_INT_FALL 0 + +#define TI954_REG_FPD3_RX_ID0 0xf0 +#define TI954_FPD3_RX_ID0 0 +#define TI954_REG_FPD3_RX_ID1 0xf1 +#define TI954_FPD3_RX_ID1 0 +#define TI954_REG_FPD3_RX_ID2 0xf2 +#define TI954_FPD3_RX_ID2 0 +#define TI954_REG_FPD3_RX_ID3 0xf3 +#define TI954_FPD3_RX_ID3 0 +#define TI954_REG_FPD3_RX_ID4 0xf4 +#define TI954_FPD3_RX_ID4 0 +#define TI954_REG_FPD3_RX_ID5 0xf5 +#define TI954_FPD3_RX_ID5 0 +#define TI954_RX_ID_LENGTH 6 + +#define TI954_REG_I2C_RX0_ID 0xf8 +#define TI954_RX_PORT0_ID 1 + +#define TI954_REG_I2C_RX1_ID 0xf9 +#define TI954_RX_PORT1_ID 1 + +/* Indirect Register Map Description */ +#define TI954_REG_IA_PATTERN_GEN_PAGE_BLOCK_SELECT 0x0 + +#define TI954_REG_IA_PGEN_CTL 0x01 +#define TI954_PGEB_ENABLE 0 + +#define TI954_REG_IA_PGEB_CFG 0x02 +#define TI954_BLOCK_SIZE 0 +#define TI954_NUM_CBARS 4 +#define TI954_PGEN_FIXED_EN 7 + +#define TI954_REG_IA_PGEN_CSI_DI 0x03 +#define TI954_PGEN_CSI_DT 0 +#define TI954_PGEN_CSI_VC 6 + +#define TI954_REG_IA_PGEN_LINE_SIZE1 0x04 +#define TI954_PGEN_LINE_SIZE1 0 + +#define TI954_REG_IA_PGEN_LINE_SIZE0 0x05 +#define TI954_PGEN_LINE_SIZE0 0 + +#define TI954_REG_IA_PGEN_BAR_SIZE1 0x06 +#define TI954_PGEN_BAR_SIZE1 0 + +#define TI954_REG_IA_PGEN_BAR_SIZE0 0x07 +#define TI954_PGEN_BAR_SIZE0 0 + +#define TI954_REG_IA_PGEN_ACT_LPF1 0x08 +#define TI954_PGEN_ACT_LPF1 0 + +#define TI954_REG_IA_PGEN_ACT_LPF0 0x09 +#define TI954_PGEN_ACT_LPF0 0 + +#define TI954_REG_IA_PGEN_TOT_LPF1 0x0a +#define TI954_PGEN_TOT_LPF1 0 + +#define TI954_REG_IA_PGEN_TOT_LPF0 0x0b +#define TI954_PGEN_TOT_LPF0 0 + +#define TI954_REG_IA_PGEN_LINE_PD1 0x0c +#define TI954_PGEN_LINE_PD1 0 + +#define TI954_REG_IA_PGEN_LINE_PD0 0x0d +#define TI954_PGEN_LINE_PD0 0 + +#define TI954_REG_IA_PGEN_VBP 0x0e +#define TI954_PGEN_VBP 0 + +#define TI954_REG_IA_PGEN_VFP 0x0f +#define TI954_PGEN_VFP 0 + +#define TI954_REG_IA_PGEN_COLOR0 0x10 +#define TI954_PGEN_COLOR0 0 +#define TI954_REG_IA_PGEN_COLOR1 0x11 +#define TI954_PGEN_COLOR1 0 +#define TI954_REG_IA_PGEN_COLOR2 0x12 +#define TI954_PGEN_COLOR2 0 +#define TI954_REG_IA_PGEN_COLOR3 0x13 +#define TI954_PGEN_COLOR3 0 +#define TI954_REG_IA_PGEN_COLOR4 0x14 +#define TI954_PGEN_COLOR4 0 +#define TI954_REG_IA_PGEN_COLOR5 0x15 +#define TI954_PGEN_COLOR5 0 +#define TI954_REG_IA_PGEN_COLOR6 0x16 +#define TI954_PGEN_COLOR6 0 +#define TI954_REG_IA_PGEN_COLOR7 0x17 +#define TI954_PGEN_COLOR7 0 +#define TI954_REG_IA_PGEN_COLOR8 0x18 +#define TI954_PGEN_COLOR8 0 +#define TI954_REG_IA_PGEN_COLOR9 0x19 +#define TI954_PGEN_COLOR9 0 +#define TI954_REG_IA_PGEN_COLOR10 0x1a +#define TI954_PGEN_COLOR10 0 +#define TI954_REG_IA_PGEN_COLOR11 0x1b +#define TI954_PGEN_COLOR11 0 +#define TI954_REG_IA_PGEN_COLOR12 0x1c +#define TI954_PGEN_COLOR12 0 +#define TI954_REG_IA_PGEN_COLOR13 0x1d +#define TI954_PGEN_COLOR13 0 +#define TI954_REG_IA_PGEN_COLOR14 0x1e +#define TI954_PGEN_COLOR14 0 + +#define TI954_REG_IA_CSI0_TCK_PREP 0x40 +#define TI954_MC_TCK_PREP 0 +#define TI954_MC_TCK_PREP_OV 7 + +#define TI954_REG_IA_CSI0_TCK_ZERO 0x41 +#define TI954_MC_TCK_ZERO 0 +#define TI954_MC_TCK_ZERO_OV 7 + +#define TI954_REG_IA_CSI0_TCK_TRAIL 0x42 +#define TI954_MR_TCK_TRAIL 0 +#define TI954_MR_TCK_TRAIL_OV 7 + +#define TI954_REG_IA_CSI0_TCK_POST 0x43 +#define TI954_MR_TCK_POST 0 +#define TI954_MR_TCK_POST_OV 7 + +#define TI954_REG_IA_CSI0_THS_PREP 0x44 +#define TI954_MR_THS_PREP 0 +#define TI954_MR_THS_PREP_OV 7 + +#define TI954_REG_IA_CSI0_THS_ZERO 0x45 +#define TI954_MR_THS_ZERO 0 +#define TI954_MR_THS_ZERO_OV 7 + +#define TI954_REG_IA_CSI0_THS_TRAIL 0x46 +#define TI954_MR_THS_TRAIL 0 +#define TI954_MR_THS_TRIAL_OV 7 + +#define TI954_REG_IA_CSI0_THS_EXIT 0x47 +#define TI954_MR_THS_EXIT 0 +#define TI954_MR_THS_EXIT_OV 7 + +#define TI954_REG_IA_CSI0_TPLX 0x48 +#define TI954_MR_TPLX 0 +#define TI954_MR_TPLX_OV 7 + +/* IA test and debug registers not now defined */ + +/*------------------------------------------------------------------------------ + * Serializer registers + *----------------------------------------------------------------------------*/ +#define TI953_REG_I2C_DEV_ID 0x00 +#define TI953_SER_ID_OVERRIDE 0 +#define TI953_DEVICE_ID 1 + +#define TI953_REG_RESET 0x01 +#define TI953_DIGITAL_RESET_0 0 +#define TI953_DIGITAL_RESET_1 1 +#define TI953_RESTART_AUTOLOAD 2 + +#define TI953_REG_GENERAL_CFG 0x02 +#define TI953_I2C_STRAP_MODE 0 +#define TI953_CRC_TX_GEN_ENABLE 1 +#define TI953_CSI_LANE_SEL 4 +#define TI953_CONTS_CLK 6 +#define TI953_CSI_LANE_SEL1 0 +#define TI953_CSI_LANE_SEL2 1 +#define TI953_CSI_LANE_SEL4 3 + +#define TI953_REG_MODE_SEL 0x03 +#define TI953_MODE 0 +#define TI953_MODE_DONE 3 +#define TI953_MODE_OV 4 + +#define TI953_REG_BC_MODE_SELECT 0x04 +#define TI953_DVP_MODE_OVER_EN 0 +#define TI953_MODE_OVERWRITE_75M 1 +#define TI953_MODE_OVERWRITE_100M 2 + +#define TI953_REG_PLLCLK_CTL 0x05 +#define TI953_OSCCLO_SEL 3 +#define TI953_CLKIN_DIV 4 + +#define TI953_REG_CLKOUT_CTRL0 0x06 +#define TI953_DIV_M_VAL 0 +#define TI953_HS_CLK_DIV 5 +#define TI953_HS_CLK_DIV_1 0 +#define TI953_HS_CLK_DIV_2 1 +#define TI953_HS_CLK_DIV_4 2 +#define TI953_HS_CLK_DIV_8 3 +#define TI953_HS_CLK_DIV_16 4 + + +#define TI953_REG_CLKOUT_CTRL1 0x07 +#define TI953_DIV_N_VAL 0 + +#define TI953_REG_BBC_WATCHDOG 0x08 +#define TI953_BCC_WD_TIMER_DISABLE 0 +#define TI953_BCC_WD_TIMER 1 + +#define TI953_REG_I2C_CONTROL1 0x09 +#define TI953_I2C_FILTER_DEPTH 0 +#define TI953_I2C_SDA_HOLD 4 +#define TI953_LCL_WRITE_DISABLE 7 + +#define TI953_REG_I2C_CONTROL2 0x0a +#define TI953_I2C_BUS_TIMER_DISABLE 0 +#define TI953_I2C_BUS_TIMER_SPEEDUP 1 +#define TI953_SDA_OUTPUT_DELAY 2 +#define TI953_SDA_OUTPUT_SETUP 4 + +#define TI953_REG_SCL_HIGH_TIME 0x0b +#define TI953_SCL_HIGH_TIME 0 + +#define TI953_REG_SCL_LOW_TIME 0x0c +#define TI953_SCL_LOW_TIME 0 + +#define TI953_REG_LOCAL_GPIO_DATA 0x0d +#define TI953_GPIO_OUT_SRC 0 +#define TI953_GPIO_RMTEN 4 + +#define TI953_REG_GPIO_CTRL 0x0e +#define TI953_GPIO0_INPUT_EN 0 +#define TI953_GPIO1_INPUT_EN 1 +#define TI953_GPIO2_INPUT_EN 2 +#define TI953_GPIO3_INPUT_EN 3 +#define TI953_GPIO0_OUT_EN 4 +#define TI953_GPIO1_OUT_EN 5 +#define TI953_GPIO2_OUT_EN 6 +#define TI953_GPIO3_OUT_EN 7 + +#define TI953_REG_DVP_CFG 0x10 +#define TI953_DVP_LV_INV 0 +#define TI953_DVP_FV_IN 1 +#define TI953_DVP_DT_YUV_EN 2 +#define TI953_DVP_DT_MATH_EN 3 +#define TI953_DVP_DT_ANY_EN 4 + +#define TI953_REG_DVP_DT 0x11 +#define TI953_DVP_DT_MATCH_VAL 0 + +#define TI953_REG_FORCE_BIST_EN 0x13 +#define TI953_FORCE_FC_CNT 0 +#define TI953_FORCE_FC_ERR 7 + +#define TI953_REG_REMOTE_BIST_CTRL 0x14 +#define TI953_REMOTE_BIST_EN 0 +#define TI953_BIST_CLOCK 1 +#define TI953_LOCAL_BIST_EN 3 +#define TI953_FORCE_ERR_CNT 4 + +#define TI953_REG_SENSOR_VGAIN 0x15 +#define TI953_VOLT_GAIN 0 + +#define TI953_REG_SENSOR_CTRL0 0x17 +#define TI953_SENSE_V_GPIO 0 +#define TI953_SENSOR_ENABLE 2 + +#define TI953_REG_SENSOR_CTRL1 0x18 +#define TI953_SENSE_GAIN_EN 7 + +#define TI953_REG_SENSOR_V0_THRESH 0x19 +#define TI953_SENSE_V0_LO 0 +#define TI953_SENSE_V0_HI 4 + +#define TI953_REG_SENSOR_V1_THRESH 0x1a +#define TI953_SENSE_V1_LO 0 +#define TI953_SENSE_V1_HI 4 + +#define TI953_REG_SENSOR_T_THRESH 0x1b +#define TI953_SENSE_T_LO 0 +#define TI953_SENSE_T_HI 4 + +#define TI953_REG_ALARM_CSI_EN 0x1c +#define TI953_CSI_LENGTH_ERR_EN 0 +#define TI953_CSI_CHKSUM_ERR_EN 1 +#define TI953_CSI_ECC_2_EN 2 +#define TI953_DPHY_CTRL_ERR_EN 3 +#define TI953_CSI_NO_FV_EN 5 + +#define TI953_REG_SENSE_EN 0x1d +#define TI953_V0_UNDER 0 +#define TI953_V0_OVER 1 +#define TI953_V1_UNSER 2 +#define TI953_V1_OVER 3 +#define TI953_T_UNDER 4 +#define TI953_T_OVER 5 + +#define TI953_REG_ALARM_BC_EN 0x1e +#define TI953_LINK_DETECT_EN 0 +#define TI953_CRC_ER_EN 1 + +#define TI953_REG_CSI_POL_SEL 0x20 +#define TI953_POLARITY_D0 0 +#define TI953_POLARITY_D1 1 +#define TI953_POLARITY_D2 2 +#define TI953_POLARITY_D3 3 +#define TI953_POLARITY_CK0 4 + +#define TI953_REG_CSI_LP_POLARITY 0x21 +#define TI953_POL_LP_DATA 0 +#define TI953_POL_LP_CLK0 4 + +#define TI953_REG_CSI_EN_RXTERM 0x24 +#define TI953_EN_RXTERM_D0 0 +#define TI953_EN_RXTERM_D1 1 +#define TI953_EN_RXTERM_D2 2 +#define TI953_EN_RXTERM_D3 3 + +#define TI953_REG_CSI_PKT_HDR_TINT_CTRL 0x31 +#define TI953_TINIT_TIME 0 +#define TI953_PKT_HDR_VCI_ENABLE 4 +#define TI953_PKT_HDR_CORRECTED 5 +#define TI953_PKT_HDR_SEL_VC 6 + +#define TI953_REG_BCC_CONFIG 0x32 +#define TI953_RX_PARITY_CHECKER_ENABLE 3 +#define TI953_AUTO_ACK_ALL 5 +#define TI953_I2C_PASS_THROUGH 6 +#define TI953_I2C_PASS_THROUGH_ALL 7 + +#define TI953_REG_DATAPATH_CTL1 0x33 +#define TI953_FC_GPIO_EN 0 +#define TI953_DCA_CRC_EN 2 + +#define TI953_REG_DES_PAR_CAP1 0x35 +#define TI953_PORT_NUM 0 +#define TI953_MPORT 4 +#define TI953_BIST_EN 5 +#define TI953_FREEZE_DES_CAP 7 + +#define TI953_REG_DES_ID 0x37 +#define TI953_FREEZE_DEVICE_ID 0 +#define TI953_DES_ID 1 + +#define TI953_REG_SLAVE_ID_0 0x39 +#define TI953_SLAVE_ID_0 1 +#define TI953_REG_SLAVE_ID_1 0x3a +#define TI953_SLAVE_ID_1 1 +#define TI953_REG_SLAVE_ID_2 0x3b +#define TI953_SLAVE_ID_2 1 +#define TI953_REG_SLAVE_ID_3 0x3c +#define TI953_SLAVE_ID_3 1 +#define TI953_REG_SLAVE_ID_4 0x3d +#define TI953_SLAVE_ID_4 1 +#define TI953_REG_SLAVE_ID_5 0x3e +#define TI953_SLAVE_ID_5 1 +#define TI953_REG_SLAVE_ID_6 0x3f +#define TI953_SLAVE_ID_6 1 +#define TI953_REG_SLAVE_ID_7 0x40 +#define TI953_SLAVE_ID_7 1 + +#define TI953_REG_SLAVE_ID_ALIAS_0 0x41 +#define TI953_SLAVE_ID_ALIAS_0 1 +#define TI953_REG_SLAVE_ID_ALIAS_1 0x42 +#define TI953_SLAVE_ID_ALIAS_1 1 +#define TI953_REG_SLAVE_ID_ALIAS_2 0x43 +#define TI953_SLAVE_ID_ALIAS_2 1 +#define TI953_REG_SLAVE_ID_ALIAS_3 0x44 +#define TI953_SLAVE_ID_ALIAS_3 1 +#define TI953_REG_SLAVE_ID_ALIAS_4 0x45 +#define TI953_SLAVE_ID_ALIAS_4 1 +#define TI953_REG_SLAVE_ID_ALIAS_5 0x46 +#define TI953_SLAVE_ID_ALIAS_5 1 +#define TI953_REG_SLAVE_ID_ALIAS_6 0x47 +#define TI953_SLAVE_ID_ALIAS_6 1 +#define TI953_REG_SLAVE_ID_ALIAS_7 0x48 +#define TI953_SLAVE_ID_ALIAS_7 1 + +#define TI953_REG_CB_CTRL 0x49 +#define TI953_LINK_DET_TIMER 0 +#define TI953_CRC_ERR_CLR 3 +#define TI953_BIST_CRC_ERR_CLR 5 + +#define TI953_REG_REV_MASK_ID 0x50 +#define TI953_MASK_ID 0 +#define TI953_REVISION_ID 4 + +#define TI953_REG_DEVICE_STS 0x51 +#define TI953_CFG_INIT_DONE 6 +#define TI953_CFG_CKSUM_STS 7 + +#define TI953_REG_GENERAL_STATUS 0x52 +#define TI953_LINK_DET 0 +#define TI953_CRC_ERR 1 + +#define TI953_REG_GPIO_PIN_STS 0x53 +#define TI953_GPIO_STS 0 + +#define TI953_REG_BIST_ERR_CNT 0x54 +#define TI953_BIST_BC_ERRCNT 0 + +#define TI953_REG_CRC_ERR_CNT1 0x55 +#define TI953_CRC_ERR_CNT1 0 + +#define TI953_REG_CRC_ERR_CNT2 0x56 +#define TI953_CRC_ERR_CNT2 0 + +#define TI953_REG_SENSOR_STATUS 0x57 +#define TI953_V0_SENSOR_LOW 0 +#define TI953_V0_SENOSR_HI 1 +#define TI953_V1_SENSOR_LOW 2 +#define TI953_V1_SENSOR_HI 3 +#define TI953_T_SENSOR_LOW 4 +#define TI953_T_SENSOR_HI 5 + +#define TI953_REG_SENSOR_V0 0x58 +#define TI953_VOLTAGE_SENSOR_V0_MIN 0 +#define TI953_VOLTAGE_SENSOR_V0_MAX 4 + +#define TI953_REG_SENSOR_V1 0x59 +#define TI953_VOLTAGE_SENOSR_V1_MIN 0 +#define TI953_VOLTAGE_SENSOR_V1_MAX 4 + +#define TI953_REG_SENSOR_T 0x5a +#define TI953_TEMP_MIN 0 +#define TI953_TMEP_MAX 4 + +#define TI953_REG_CSI_ERR_CNT 0x5c +#define TI953_CSI_ERR_CNT 0 + +#define TI953_REG_CSI_ERR_STATUS 0x5d +#define TI953_ECC_1BIT_ERR 0 +#define TI953_ECC_2BIT_ERR 1 +#define TI953_CHKSUM_ERR 2 +#define TI953_LINE_LEN_MISMATCH 3 + +#define TI953_REG_CSI_ERR_DLANE01 0x5e +#define TI953_CNTRL_ERR_HSRQST_0 1 +#define TI953_SOT_SYNC_ERROR_0 2 +#define TI953_SOT_ERROR_0 3 +#define TI953_CNTRL_ERR_HSRQST_1 5 +#define TI953_SOT_SYNC_ERROR_1 6 +#define TI953_SOT_ERROR_1 7 + +#define TI953_REG_CSI_ERR_DLANE23 0x5f +#define TI953_CNTRL_ERR_HSRQST_2 1 +#define TI953_SOT_SYNC_ERROR_2 2 +#define TI953_SOT_ERROR_2 3 +#define TI953_CNTRL_ERR_HSRQST_3 5 +#define TI953_SOT_SYNC_ERROR_3 6 +#define TI953_SOT_ERROR_3 7 + +#define TI953_REG_CSI_ERR_CLK_LANE 0x60 +#define TI953_CNTRL_ERR_HSRQST_CK0 1 + +#define TI953_REG_CSI_PKT_HDR_VC_ID 0x61 +#define TI953_LONG_PKT_DATA_ID 0 +#define TI953_LONG_PKT_VCHNL_ID 6 + +#define TI953_REG_PKT_HDR_WC_LSB 0x62 +#define TI953_LONG_PKT_WRD_CNT_LSB 0 + +#define TI953_REG_PKT_HDR_WC_MSB 0x63 +#define TI953_LONG_PKT_WRD_CNT_MSB 0 + +#define TI953_REG_CSI_ECC 0x64 +#define TI953_CSI2_ECC 0 +#define TI953_LINE_LENGTH_CHANGE 7 + +#define TI953_REG_IND_ACC_CTL 0xb0 +#define TI953_IA_READ 0 +#define TI953_IA_AUTO_INC 1 +#define TI953_IA_SEL 2 + +#define TI953_REG_IND_ACC_ADDR 0xb1 +#define TI953_IND_ACC_ADDR 0 + +#define TI953_REG_IND_ACC_DATA 0xb2 +#define TI953_IND_ACC_DATA 0 + +#define TI953_REG_FPD3_RX_ID0 0xf0 +#define TI953_FPD3_RX_ID0 0 +#define TI953_REG_FPD3_RX_ID1 0xf1 +#define TI953_FPD3_RX_ID1 0 +#define TI953_REG_FPD3_RX_ID2 0xf2 +#define TI953_FPD3_RX_ID2 0 +#define TI953_REG_FPD3_RX_ID3 0xf3 +#define TI953_FPD3_RX_ID3 0 +#define TI953_REG_FPD3_RX_ID4 0xf4 +#define TI953_FPD3_RX_ID4 0 +#define TI953_REG_FPD3_RX_ID5 0xf5 +#define TI953_FPD3_RX_ID5 0 +#define TI953_RX_ID_LENGTH 6 + +/*------------------------------------------------------------------------------ + * DEFINES + *----------------------------------------------------------------------------*/ +// GPIO +#define CAM_ENABLE 0x01 /* Assumes bit 0 of GPIO is connected to CAM_ENABLE */ +#define GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID +#define CAM_ENABLE_CHANNEL 1 +// IIC +#define IIC_BASEADDR XPAR_AXI_IIC_0_BASEADDR +#define IIC_DEVICE_ID XPAR_IIC_0_DEVICE_ID +// DESERIALIZER +#define TI954_I2C 0x30 // ID of deserializer +// SERIALIZER +#define TI953_I2C_0 0x41 // alias of serializer on port 0 +#define TI953_I2C_1 0x42 // alias of serializer on port 0 +// sensor +#define SENSOR_I2C_0 0x43 +#define SENSOR_I2C_1 0x44 +// #define SENSOR_I2C_ALIAS 0x20 +#define SENSOR_ID 0x1A //0x50 +// EEPROM +#define EEPROM_ID 0x55 +#define EEPROM_I2C_0 0x45 +#define EEPROM_I2C_1 0x46 + +#define NUM_SERIALIZER 2 +#define NUM_ALIAS 8 + +struct ds90ub953_priv { + struct i2c_client *client; + struct regmap *regmap; + struct ds90ub954_priv *parent; + int rx_channel; + int test_pattern; + int i2c_address; + int csi_lane_count; + int i2c_alias_num; // number of slave alias pairs + int i2c_slave[NUM_ALIAS]; // array with the i2c slave addresses + int i2c_alias[NUM_ALIAS]; // array with the i2c alias addresses + int conts_clk; // continuous clock (0: discontinuous, 1: continuous) + int i2c_pt; // i2c-pass-through-all + + int initialized; + + int gpio0_oe; // gpio0_output_enable + int gpio1_oe; // gpio1_output_enable + int gpio2_oe; // gpio2_output_enable + int gpio3_oe; // gpio3_output_enable + + int gpio0_oc; // gpio0_output_control + int gpio1_oc; // gpio1_output_control + int gpio2_oc; // gpio2_output_control + int gpio3_oc; // gpio3_output_control + + /* reference output clock control parameters */ + int hs_clk_div; + int div_m_val; + int div_n_val; + + int vc_map; // virtual channel mapping +}; + + +struct ds90ub954_priv { + struct i2c_client *client; + struct regmap *regmap; + struct ds90ub953_priv *ser[NUM_SERIALIZER]; //serializers + int pass_gpio; + int lock_gpio; + int pdb_gpio; + int sel_rx_port; // selected rx port + int sel_ia_config; // selected ia configuration + int csi_lane_count; + int csi_lane_speed; + int test_pattern; + int num_ser; // number of serializers connected + int conts_clk; // continuous clock (0: discontinuous, 1: continuous) +}; + +#endif /* I2C_DS90UB954_H */ diff --git a/drivers/media/i2c/veye,vbyone.txt b/drivers/media/i2c/veye,vbyone.txt new file mode 100644 index 0000000000000..b5f8b7d35e367 --- /dev/null +++ b/drivers/media/i2c/veye,vbyone.txt @@ -0,0 +1,78 @@ +Device tree configuration for the V-by-ONE deserializer THCV242A and +serializer THCV241A from Thine + +The deserializer THCV242A can have up to one serializers (THCV241A) connected. +In the device tree, the serializer nodes are subnodes of the deserializer. + +/*------------------------------------------------------------------------------ +* ------------------------------------------------------------------------------ +* +* Options for DESERIALIZER (THCV242A): +* +* ------------------------------------------------------------------------------ +*-----------------------------------------------------------------------------*/ + +Integer values: +- reg: I2C address of deserializer +- csi-lane-count Number of CSI lanes(2,4) default value: 2 +- csi-lane-speed CSI lane speed in Mbps (900, 1118 or 1500) + default vaule: 1500 +- coax-num Number of Coax cable +- cam-i2c-pt-setting Pass through setting i2c + +String values: +- pdb-gpio Power-down gpio(no use or polling) +- trgin-gpio Trigger gpio(no use or polling) +- out1-gpio out1 gpio(no use or polling) +- out2-gpio out2 gpio(no use or polling) + + + +/*------------------------------------------------------------------------------ +* ------------------------------------------------------------------------------ +* +* Options for SERIALIZER (THCV241A): +* +* ------------------------------------------------------------------------------ +*-----------------------------------------------------------------------------*/ + +Integer values: +- i2c-address I2C address of serializer(0x36) +- csi-lane-count Number of CSI lanes default value: 2 +- csi-lane-speed CSI lane speed in Mbps (900, 1118 or 1500) +- camera-i2c-address I2C address of camera(0x3b) + +/*------------------------------------------------------------------------------ +* ------------------------------------------------------------------------------ +* +* Device Tree Examples +* +* ------------------------------------------------------------------------------ +*-----------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------------ +* Example 1 (SIMPLE) +*-----------------------------------------------------------------------------*/ + +veye_vbyone: vbyone@65 { + compatible = "veye,vbyone"; + priority = <(-1)>; + reg = <0x65>; + status = "okay"; + csi-lane-count = <2>; + csi-lane-speed = <1500>; + coax-num = <1>; + cam-i2c-pt-setting = <0x13>; + + pdb-gpio-mode = <0>; + trgin-gpio-mode = <1>; + out1-gpio-mode = <1>; + out2-gpio-mode = <1>; + + serializer { + i2c-address=<0x34>; + csi-lane-count = <2>; + csi-lane-speed = <1500>; + camera-i2c-address=<0x3b>; + }; +}; \ No newline at end of file diff --git a/drivers/media/i2c/veye_mvcam.c b/drivers/media/i2c/veye_mvcam.c new file mode 100644 index 0000000000000..136f25234a287 --- /dev/null +++ b/drivers/media/i2c/veye_mvcam.c @@ -0,0 +1,1696 @@ +/* + * + */ +#include "veye_mvcam.h" +#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 +/* +versionlog: v.1.1.04 -20230830 +add support for RAW_MIPI_IMX462M and RAW_MIPI_AR0234M +*/ +#define DRIVER_VERSION KERNEL_VERSION(1, 0x01, 0x04) + + +#define mvcam_NAME "mvcam" +//reserved +/* Embedded metadata stream structure */ +#define VEYE_MV_EMBEDDED_LINE_WIDTH 16384 +#define VEYE_MV_NUM_EMBEDDED_LINES 1 + +//#define DEBUG_PRINTK + +#ifndef DEBUG_PRINTK +static int debug = 0; +#define debug_printk(s , ... ) +#define VEYE_TRACE +#else +static int debug = 1; +#define debug_printk printk +#define VEYE_TRACE +//#define VEYE_TRACE printk(KERN_INFO"%s %s %d \n",__FILE__,__FUNCTION__,__LINE__); +#endif + +module_param(debug, int, 0644); + +#define STARTUP_MIN_DELAY_US 500*1000//500ms +#define STARTUP_DELAY_RANGE_US 1000 + +struct reg_mv { + u16 addr; + u32 val; +}; + +struct mvcam_reg_list { + unsigned int num_of_regs; + const struct reg_mv *regs; +}; + +struct mvcam_format { + u32 index; + u32 mbus_code;//mbus format + u32 data_type;//mv data format +}; + +struct mvcam_roi +{ + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; +}; + +struct mvcam_mode { + u32 width; + u32 height; +}; + +static s64 link_freq_menu_items[] = { + MVCAM_DEFAULT_LINK_FREQ, +}; + + +struct mvcam { + struct v4l2_subdev sd; + struct media_pad pad; + + u32 model_id; + struct gpio_desc *reset_gpio; //ADP-MV2 do not use this + struct gpio_desc *pwdn_gpio; + + struct i2c_client *client; + //data format + struct mvcam_format *supported_formats; + int num_supported_formats; + int current_format_idx; + u32 max_width; + u32 max_height; + u32 min_width; + u32 min_height; + struct v4l2_rect roi;//the same as roi + //max fps @ current roi format + u32 max_fps; + u32 cur_fps; + u32 h_flip; + u32 v_flip; + u32 lane_num; + u32 mipi_datarate; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *ctrls[MVCAM_MAX_CTRLS]; + /* V4L2 Controls */ + struct v4l2_ctrl *frmrate; + + /* + * Mutex for serialized access: + * Protect sensor module set pad format and start/stop streaming safely. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + +}; + + +static inline struct mvcam *to_mvcam(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct mvcam, sd); +} + +static int mvcam_readl_reg(struct i2c_client *client, + u16 addr, u32 *val) +{ + u16 buf = htons(addr); + u32 data; + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .flags= 0, + .len = 2, + .buf = (u8 *)&buf, + }, + { + .addr = client->addr, + .flags= I2C_M_RD, + .len = 4, + .buf = (u8 *)&data, + }, + }; + + if(i2c_transfer(client->adapter, msgs, 2) != 2){ + return -1; + } + + *val = ntohl(data); + + return 0; +} + +static int mvcam_writel_reg(struct i2c_client *client, + u16 addr, u32 val) +{ + u8 data[6]; + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .flags= 0, + .len = 6, + .buf = data, + }, + }; + debug_printk("mvcam write 0x%x val 0x%x\n",addr,val); + addr = htons(addr); + val = htonl(val); + memcpy(data, &addr, 2); + memcpy(data + 2, &val, 4); + if(i2c_transfer(client->adapter, msgs, 1) != 1) + return -1; + + return 0; +} + +static int mvcam_read(struct i2c_client *client, u16 addr, u32 *value) +{ + int ret; + int count = 0; + while (count++ < I2C_READ_RETRY_COUNT) { + ret = mvcam_readl_reg(client, addr, value); + if(!ret) { + //v4l2_dbg(1, debug, client, "%s: 0x%02x 0x%04x\n", + // __func__, addr, *value); + return ret; + } + } + + v4l2_err(client, "%s: Reading register 0x%02x failed\n", + __func__, addr); + return ret; +} + +static int mvcam_write(struct i2c_client *client, u16 addr, u32 value) +{ + int ret; + int count = 0; + while (count++ < I2C_WRITE_RETRY_COUNT) { + ret = mvcam_writel_reg(client, addr, value); + if(!ret) + return ret; + } + v4l2_err(client, "%s: Write 0x%04x to register 0x%02x failed\n", + __func__, value, addr); + return ret; +} + +/* Write a list of registers */ +static int __maybe_unused mvcam_write_regs(struct i2c_client *client, + const struct reg_mv *regs, u32 len) +{ + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = mvcam_write(client, regs[i].addr,regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error = %d\n", + regs[i].addr, ret); + + return ret; + } + } + return 0; +} + +static u32 bit_count(u32 n) +{ + n = (n &0x55555555) + ((n >>1) &0x55555555) ; + n = (n &0x33333333) + ((n >>2) &0x33333333) ; + n = (n &0x0f0f0f0f) + ((n >>4) &0x0f0f0f0f) ; + n = (n &0x00ff00ff) + ((n >>8) &0x00ff00ff) ; + n = (n &0x0000ffff) + ((n >>16) &0x0000ffff) ; + + return n ; +} + +static int mvcam_getroi(struct mvcam *mvcam) +{ + // int ret; + struct i2c_client *client = mvcam->client; + mvcam_read(client, ROI_Offset_X,&mvcam->roi.left); + mvcam_read(client, ROI_Offset_Y,&mvcam->roi.top); + mvcam_read(client, ROI_Width,&mvcam->roi.width); + mvcam_read(client, ROI_Height,&mvcam->roi.height); + v4l2_dbg(1, debug, mvcam->client, "%s:get roi(%d,%d,%d,%d)\n", + __func__, mvcam->roi.left,mvcam->roi.top,mvcam->roi.width,mvcam->roi.height); + return 0; +} + +static int mvcam_setroi(struct mvcam *mvcam) +{ + // int ret; + u32 fps_reg; + struct i2c_client *client = mvcam->client; + v4l2_dbg(1, debug, mvcam->client, "%s:set roi(%d,%d,%d,%d)\n", + __func__, mvcam->roi.left,mvcam->roi.top,mvcam->roi.width,mvcam->roi.height); + mvcam_write(client, ROI_Offset_X,mvcam->roi.left); + msleep(1); + mvcam_write(client, ROI_Offset_Y,mvcam->roi.top); + msleep(1); + mvcam_write(client, ROI_Width,mvcam->roi.width); + msleep(1); + mvcam_write(client, ROI_Height,mvcam->roi.height); + msleep(8); + //get sensor max framerate + mvcam_read(client, MaxFrame_Rate,&fps_reg); + mvcam->max_fps = fps_reg/100; + mvcam_read(client, Framerate,&fps_reg); + mvcam->cur_fps = fps_reg/100; + v4l2_ctrl_modify_range(mvcam->frmrate, 1, mvcam->max_fps, 1, mvcam->cur_fps); + +// dev_info(&client->dev, +// "max fps is %d,cur fps %d\n", +// mvcam->max_fps,mvcam->cur_fps); + return 0; +} + +static int mvcam_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + int ret; + struct mvcam *mvcam = + container_of(ctrl->handler, struct mvcam, ctrl_handler); + struct i2c_client *client = mvcam->client; + + switch (ctrl->id) { + case V4L2_CID_VEYE_MV_TRIGGER_MODE: + ret = mvcam_read(client, Trigger_Mode,&ctrl->val); + break; + case V4L2_CID_VEYE_MV_TRIGGER_SRC: + ret = mvcam_read(client, Trigger_Source,&ctrl->val); + break; + case V4L2_CID_VEYE_MV_FRAME_RATE: + ret = mvcam_read(client, Framerate,&ctrl->val); + ctrl->val = ctrl->val/100; + mvcam->cur_fps = ctrl->val; + break; + default: + dev_info(&client->dev, + "mvcam_g_volatile_ctrl ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + + v4l2_dbg(1, debug, mvcam->client, "%s: cid = (0x%X), value = (%d).\n", + __func__, ctrl->id, ctrl->val); + + return ret; +} + +static int mvcam_s_ctrl(struct v4l2_ctrl *ctrl) +{ + int ret; + struct mvcam *mvcam = + container_of(ctrl->handler, struct mvcam, ctrl_handler); + struct i2c_client *client = mvcam->client; + + v4l2_dbg(1, debug, mvcam->client, "%s: cid = (0x%X), value = (%d).\n", + __func__, ctrl->id, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_VEYE_MV_TRIGGER_MODE: + ret = mvcam_write(client, Trigger_Mode,ctrl->val); + break; + case V4L2_CID_VEYE_MV_TRIGGER_SRC: + ret = mvcam_write(client, Trigger_Source,ctrl->val); + break; + case V4L2_CID_VEYE_MV_SOFT_TRGONE: + ret = mvcam_write(client, Trigger_Software,1); + break; + case V4L2_CID_VEYE_MV_FRAME_RATE: + ret = mvcam_write(client, Framerate,ctrl->val*100); + mvcam->cur_fps = ctrl->val; + break; + case V4L2_CID_VEYE_MV_ROI_X: + mvcam->roi.left = rounddown(ctrl->val, MV_CAM_ROI_W_ALIGN); + v4l2_dbg(1, debug, mvcam->client, "set roi_x %d round to %d.\n", + ctrl->val, mvcam->roi.left); + ret = 0; + break; + case V4L2_CID_VEYE_MV_ROI_Y: + mvcam->roi.top = rounddown(ctrl->val, MV_CAM_ROI_H_ALIGN); + v4l2_dbg(1, debug, mvcam->client, "set roi_y %d round to %d.\n", + ctrl->val, mvcam->roi.top); + ret = 0; + break; + default: + dev_info(&client->dev, + "mvcam_s_ctrl ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + return ret; +} + +static const struct v4l2_ctrl_ops mvcam_ctrl_ops = { + .g_volatile_ctrl = mvcam_g_volatile_ctrl, + .s_ctrl = mvcam_s_ctrl, +}; + +static struct v4l2_ctrl_config mvcam_v4l2_ctrls[] = { + //standard v4l2_ctrls + { + .ops = NULL, + .id = V4L2_CID_LINK_FREQ, + .name = NULL,//kernel will fill it + .type = V4L2_CTRL_TYPE_MENU , + .def = 0, + .min = 0, + .max = ARRAY_SIZE(link_freq_menu_items) - 1, + .step = 0, + .qmenu_int = link_freq_menu_items, + .flags = V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .ops = &mvcam_ctrl_ops, + .id = V4L2_CID_PIXEL_RATE, + .name = NULL,//kernel will fill it + .type = V4L2_CTRL_TYPE_INTEGER, + .def = MV_CAM_PIXEL_RATE, + .min = MV_CAM_PIXEL_RATE, + .max = MV_CAM_PIXEL_RATE, + .step = 1, + .flags = V4L2_CTRL_FLAG_READ_ONLY, + }, + //custom v4l2-ctrls + { + .ops = &mvcam_ctrl_ops, + .id = V4L2_CID_VEYE_MV_TRIGGER_MODE, + .name = "trigger_mode", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = Image_Continues, + .min = 0, + .max = Image_trigger_mode_num-1, + .step = 1, + .flags = V4L2_CTRL_FLAG_VOLATILE|V4L2_CTRL_FLAG_EXECUTE_ON_WRITE, + }, + { + .ops = &mvcam_ctrl_ops, + .id = V4L2_CID_VEYE_MV_TRIGGER_SRC, + .name = "trigger_src", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = Trg_Hard, + .min = 0, + .max = Trg_Hard_src_num-1, + .step = 1, + .flags = V4L2_CTRL_FLAG_VOLATILE|V4L2_CTRL_FLAG_EXECUTE_ON_WRITE, + }, + { + .ops = &mvcam_ctrl_ops, + .id = V4L2_CID_VEYE_MV_SOFT_TRGONE, + .name = "soft_trgone", + .type = V4L2_CTRL_TYPE_BUTTON, + .def = 0, + .min = 0, + .max = 0, + .step = 0, + }, + { + .ops = &mvcam_ctrl_ops, + .id = V4L2_CID_VEYE_MV_FRAME_RATE, + .name = "frame_rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = MV_CAM_DEF_FPS, + .min = 0, + .max = MV_CAM_DEF_FPS, + .step = 1, + .flags = V4L2_CTRL_FLAG_VOLATILE|V4L2_CTRL_FLAG_EXECUTE_ON_WRITE, + }, + { + .ops = &mvcam_ctrl_ops, + .id = V4L2_CID_VEYE_MV_ROI_X, + .name = "roi_x", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 0, + .min = 0, + .max = 0,//to read from camera + .step = MV_CAM_ROI_W_ALIGN, + .flags = 0, + }, + { + .ops = &mvcam_ctrl_ops, + .id = V4L2_CID_VEYE_MV_ROI_Y, + .name = "roi_y", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 0, + .min = 0, + .max = 0,//to read from camera + .step = MV_CAM_ROI_H_ALIGN, + .flags = 0, + }, +}; +//grab some ctrls while streaming +static void mvcam_v4l2_ctrl_grab(struct mvcam *mvcam,bool grabbed) +{ + int i = 0; + for (i = 0; i < ARRAY_SIZE(mvcam_v4l2_ctrls); ++i) { + switch(mvcam->ctrls[i]->id) + { + case V4L2_CID_VEYE_MV_TRIGGER_MODE: + case V4L2_CID_VEYE_MV_TRIGGER_SRC: + case V4L2_CID_VEYE_MV_FRAME_RATE: + case V4L2_CID_VEYE_MV_ROI_X: + case V4L2_CID_VEYE_MV_ROI_Y: + v4l2_ctrl_grab(mvcam->ctrls[i], grabbed); + break; + + default: + break; + } + } +} + +static void mvcam_v4l2_ctrl_init(struct mvcam *mvcam) +{ + int i = 0; + u32 value = 0; + struct i2c_client *client = mvcam->client; + for (i = 0; i < ARRAY_SIZE(mvcam_v4l2_ctrls); ++i) { + switch(mvcam_v4l2_ctrls[i].id) + { + case V4L2_CID_VEYE_MV_TRIGGER_MODE: + mvcam_read(client, Trigger_Mode,&value); + mvcam_v4l2_ctrls[i].def = value; + v4l2_dbg(1, debug, mvcam->client, "%s:default trigger mode %d\n", __func__, value); + break; + case V4L2_CID_VEYE_MV_TRIGGER_SRC: + mvcam_read(client, Trigger_Source,&value); + mvcam_v4l2_ctrls[i].def = value; + v4l2_dbg(1, debug, mvcam->client, "%s:default trigger source %d\n", __func__, value); + break; + case V4L2_CID_VEYE_MV_FRAME_RATE: + mvcam_read(client, Framerate,&value); + mvcam_v4l2_ctrls[i].def = value/100; + mvcam_read(client, MaxFrame_Rate,&value); + mvcam_v4l2_ctrls[i].max = value/100; + v4l2_dbg(1, debug, mvcam->client, "%s:default framerate %lld , max fps %lld \n", __func__, \ + mvcam_v4l2_ctrls[i].def,mvcam_v4l2_ctrls[i].max); + break; + case V4L2_CID_VEYE_MV_ROI_X: + //mvcam_read(client, ROI_Offset_X,value); + //mvcam_v4l2_ctrls[i].def = value; + mvcam_v4l2_ctrls[i].max = mvcam->max_width - mvcam->min_width; + break; + case V4L2_CID_VEYE_MV_ROI_Y: + //mvcam_read(client, ROI_Offset_Y,value); + //mvcam_v4l2_ctrls[i].def = value; + mvcam_v4l2_ctrls[i].max = mvcam->max_height - mvcam->min_height; + break; + default: + break; + } + } +} + +static int mvcam_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + u32 val = 0; + struct mvcam *mvcam = to_mvcam(sd); + val = 1 << (mvcam->lane_num - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + VEYE_TRACE + return 0; +} + +static int mvcam_csi2_enum_mbus_code( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mvcam *mvcam = to_mvcam(sd); + struct mvcam_format *supported_formats = mvcam->supported_formats; + int num_supported_formats = mvcam->num_supported_formats; + VEYE_TRACE + if (code->index != 0) + return -EINVAL; + + if (code->index >= num_supported_formats) + return -EINVAL; + code->code = supported_formats[code->index].mbus_code; + v4l2_dbg(1, debug, sd, "%s: index = (%d) mbus code (%x)\n", __func__, code->index,code->code); + + return 0; +} + +static int mvcam_csi2_enum_framesizes( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct mvcam *mvcam = to_mvcam(sd); +VEYE_TRACE + v4l2_dbg(1, debug, sd, "%s: code = (0x%X), index = (%d)\n", + __func__, fse->code, fse->index); + if (fse->index != 0) + return -EINVAL; + fse->min_width = fse->max_width = + mvcam->roi.width; + fse->min_height = fse->max_height = + mvcam->roi.height; + return 0; +} + +static int mvcam_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + /* max framerate */ + struct v4l2_fract fract_fps; + struct mvcam *mvcam = to_mvcam(sd); + VEYE_TRACE + mutex_lock(&mvcam->mutex); + fract_fps.numerator = 100; + fract_fps.denominator = mvcam->cur_fps*100; + fi->interval = fract_fps; + mutex_unlock(&mvcam->mutex); + + return 0; +} + +static int mvcam_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + + struct mvcam *mvcam = to_mvcam(sd); + VEYE_TRACE + if(fi->interval.numerator == 0) + return -EINVAL; + + v4l2_dbg(1, debug, sd, "%s: numerator %d, denominator %d\n", + __func__, fi->interval.numerator,fi->interval.denominator); + + mutex_lock(&mvcam->mutex); + mvcam->cur_fps = fi->interval.denominator/fi->interval.numerator; + mvcam_write(mvcam->client, Framerate,mvcam->cur_fps*100); + mutex_unlock(&mvcam->mutex); + + return 0; +} + + +static int mvcam_csi2_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct mvcam *mvcam = to_mvcam(sd); + struct mvcam_format *current_format; +VEYE_TRACE + mutex_lock(&mvcam->mutex); + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + format->format = *v4l2_subdev_get_try_format(&mvcam->sd, cfg, format->pad); + #else + debug_printk("=V4L2_SUBDEV_FORMAT_TRY==err"); + mutex_unlock(&mvcam->mutex); + return -ENOTTY; + #endif + } else { + current_format = &mvcam->supported_formats[mvcam->current_format_idx]; + format->format.width = mvcam->roi.width; + format->format.height = mvcam->roi.height; + + format->format.code = current_format->mbus_code; + format->format.field = V4L2_FIELD_NONE; + //for uyvy gstreamer + //format->format.colorspace = V4L2_COLORSPACE_SRGB;//V4L2_COLORSPACE_REC709; +/* format->format.ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(format->format.colorspace); + format->format.quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + format->format.colorspace, + format->format.ycbcr_enc); + format->format.xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(format->format.colorspace); +*/ + v4l2_dbg(1, debug, sd, "%s: width: (%d) height: (%d) code: (0x%X)\n", + __func__, format->format.width,format->format.height, + format->format.code); + } + + mutex_unlock(&mvcam->mutex); + return 0; +} + +static int mvcam_csi2_get_fmt_idx_by_code(struct mvcam *mvcam, + u32 mbus_code) +{ + int i; + struct mvcam_format *formats = mvcam->supported_formats; + for (i = 0; i < mvcam->num_supported_formats; i++) { + if (formats[i].mbus_code == mbus_code) + return i; + } + return -EINVAL; +} +/* +static int mvcam_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct mvcam *mvcam = to_mvcam(sd); +VEYE_TRACE + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + //sel->r = *__mvcam_get_pad_crop(mvcam, cfg, sel->pad, + // sel->which); + sel->r = mvcam->roi; + break; + } + + //active area + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = mvcam->max_width; + sel->r.height = mvcam->max_height; + break; + default: + return -EINVAL; + } + sel->flags = V4L2_SEL_FLAG_LE; + v4l2_dbg(1, debug, sd, "%s: target %d\n", __func__,V4L2_SEL_TGT_CROP); + return 0; +}*/ +static int mvcam_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + // struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mvcam *mvcam = to_mvcam(sd); + VEYE_TRACE + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + mvcam->roi.left = clamp(rounddown(sel->r.left, MV_CAM_ROI_W_ALIGN), 0U, (mvcam->max_width-mvcam->min_width)); + mvcam->roi.top = clamp(rounddown(sel->r.top, MV_CAM_ROI_H_ALIGN), 0U, (mvcam->max_height-mvcam->min_height)); + mvcam->roi.width = clamp(rounddown(sel->r.width, MV_CAM_ROI_W_ALIGN), mvcam->min_width, mvcam->max_width); + mvcam->roi.height = clamp(rounddown(sel->r.height, MV_CAM_ROI_H_ALIGN), mvcam->min_height, mvcam->max_height); + mvcam_setroi(mvcam); + break; + default: + return -EINVAL; + } + v4l2_dbg(1, debug, sd, "%s: target %d\n", __func__,V4L2_SEL_TGT_CROP); + return 0; +} +static int mvcam_frm_supported(int roi_x,int wmin, int wmax, int ws, + int roi_y,int hmin, int hmax, int hs, + int w, int h) +{ + if ( + (roi_x+w) > wmax || w < wmin || + (roi_y+h) > hmax || h < hmin || + (h) % hs != 0 || + (w) % ws != 0 + ) + return -EINVAL; + + return 0; +} + +static int mvcam_csi2_try_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct mvcam *mvcam = to_mvcam(sd); + int ret = 0; +VEYE_TRACE + ret = mvcam_frm_supported( + mvcam->roi.left,mvcam->min_width, mvcam->max_width, MV_CAM_ROI_W_ALIGN, + mvcam->roi.top,mvcam->min_height, mvcam->max_height, MV_CAM_ROI_H_ALIGN, + format->format.width, format->format.height); + + if (ret < 0) { + v4l2_err(sd, "Not supported size!\n"); + return ret; + } + + return 0; +} + +static int mvcam_csi2_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + int i; + struct mvcam *mvcam = to_mvcam(sd); + //struct v4l2_mbus_framefmt *framefmt; + struct v4l2_subdev_selection sel; + + /*if ((format->format.width != mvcam->roi.width || + format->format.height != mvcam->roi.height)) + { + v4l2_info(sd, "Changing the resolution is not supported with VIDIOC_S_FMT! \n Pls use VIDIOC_S_SELECTION.\n"); + v4l2_info(sd,"%d,%d,%d,%d\n",format->format.width,mvcam->roi.width,format->format.height,mvcam->roi.height); + return -EINVAL; + }*/ + +VEYE_TRACE + //format->format.colorspace = V4L2_COLORSPACE_SRGB; + format->format.field = V4L2_FIELD_NONE; + + v4l2_dbg(1, debug, sd, "%s: code: 0x%X", + __func__, format->format.code); + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + //framefmt = v4l2_subdev_get_try_format(sd, cfg, + // format->pad); + // *framefmt = format->format; + return mvcam_csi2_try_fmt(sd, cfg, format); + } else { + i = mvcam_csi2_get_fmt_idx_by_code(mvcam, format->format.code); + if (i < 0) + return -EINVAL; + mvcam->current_format_idx = i; + mvcam_write(mvcam->client,Pixel_Format,mvcam->supported_formats[i].data_type); + + mvcam->roi.width = format->format.width; + mvcam->roi.height = format->format.height; + sel.target = V4L2_SEL_TGT_CROP; + sel.r = mvcam->roi; + mvcam_set_selection(sd, NULL, &sel); + + //format->format.width = mvcam->roi.width; + } + //update_controls(mvcam); + return 0; +} + + + +static void mvcam_get_module_inf(struct mvcam *mvcam, + struct rkmodule_inf *inf) +{ + VEYE_TRACE + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, mvcam_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, mvcam->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, mvcam->len_name, sizeof(inf->base.lens)); +} + +static int mvcam_get_channel_info(struct mvcam *mvcam, struct rkmodule_channel_info *ch_info) +{ + struct mvcam_format *current_format; + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + VEYE_TRACE + ch_info->vc = V4L2_MBUS_CSI2_CHANNEL_0; + ch_info->width = mvcam->roi.width; + ch_info->height = mvcam->roi.height; + current_format = &mvcam->supported_formats[mvcam->current_format_idx]; + ch_info->bus_fmt = current_format->mbus_code; + return 0; +} + +static long mvcam_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct mvcam *mvcam = to_mvcam(sd); + long ret = 0; + struct rkmodule_channel_info *ch_info; + VEYE_TRACE + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + mvcam_get_module_inf(mvcam, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = mvcam_get_channel_info(mvcam, ch_info); + break; + case RKMODULE_GET_CSI_DSI_INFO: + *(int *)arg = RKMODULE_CSI_INPUT; + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long mvcam_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_channel_info *ch_info; + long ret; + VEYE_TRACE + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + ret = mvcam_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) + ret = -EFAULT; + kfree(inf); + break; + /*case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = mvcam_ioctl(sd, cmd, cfg); + kfree(cfg); + break;*/ + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + ret = mvcam_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + case RKMODULE_GET_CSI_DSI_INFO: + *(int *)arg = RKMODULE_CSI_INPUT; + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + + + +static void mvcam_free_controls(struct mvcam *mvcam) +{ + VEYE_TRACE + v4l2_ctrl_handler_free(mvcam->sd.ctrl_handler); + //mutex_destroy(&mvcam->mutex); +} + +static u32 mvdatatype_to_mbus_code(int data_type) +{ + VEYE_TRACE + // debug_printk("%s: data type %d\n", + // __func__, data_type); + switch(data_type) { + case MV_DT_Mono8: + return MEDIA_BUS_FMT_Y8_1X8; + case MV_DT_Mono10: + return MEDIA_BUS_FMT_Y10_1X10; + case MV_DT_Mono12: + return MEDIA_BUS_FMT_Y12_1X12; + case MV_DT_Mono14: + return MEDIA_BUS_FMT_Y14_1X14; + case MV_DT_UYVY: + return MEDIA_BUS_FMT_UYVY8_2X8; + } + return 0; +} + +int get_fmt_index(struct mvcam *mvcam,u32 datatype) +{ + int i = 0; + for(;i < mvcam->num_supported_formats;++i) + { + if((mvcam->supported_formats[i].data_type) == datatype) + return i; + } + return -1; +} + +static int mvcam_enum_pixformat(struct mvcam *mvcam) +{ + int ret = 0; + u32 mbus_code = 0; + int pixformat_type; + int index = 0; + int bitindex = 0; + int num_pixformat = 0; + u32 fmtcap = 0; + u32 cur_fmt; + struct i2c_client *client = mvcam->client; +VEYE_TRACE + ret = mvcam_read(client, Format_cap, &fmtcap); + if (ret < 0) + goto err; + num_pixformat = bit_count(fmtcap); + if (num_pixformat < 0) + goto err; + + v4l2_dbg(1, debug, mvcam->client, "%s: format count: %d; format cap 0x%x\n", + __func__, num_pixformat,fmtcap); + + mvcam->supported_formats = devm_kzalloc(&client->dev, + sizeof(*(mvcam->supported_formats)) * (num_pixformat+1), GFP_KERNEL); + while(fmtcap){ + if(fmtcap&1){ + //which bit is set? + pixformat_type = bitindex; + fmtcap >>= 1; + bitindex++; + } + else{ + fmtcap >>= 1; + bitindex++; + continue; + } + mbus_code = mvdatatype_to_mbus_code(pixformat_type); + mvcam->supported_formats[index].index = index; + mvcam->supported_formats[index].mbus_code = mbus_code; + mvcam->supported_formats[index].data_type = pixformat_type; + v4l2_dbg(1, debug, mvcam->client, "%s support format index %d mbuscode %d datatype: %d\n", + __func__, index,mbus_code,pixformat_type); + index++; + } + mvcam->num_supported_formats = num_pixformat; + + mvcam_read(client, Pixel_Format, &cur_fmt); + mvcam->current_format_idx = get_fmt_index(mvcam,cur_fmt); + v4l2_dbg(1, debug, mvcam->client, "%s: cur format: %d\n", + __func__, cur_fmt); + // mvcam_add_extension_pixformat(mvcam); + return 0; +VEYE_TRACE +err: + return -ENODEV; +} + +static void mvcam_get_mipifeature(struct mvcam *mvcam) +{ + u32 lane_num; + u32 mipi_datarate; + struct i2c_client *client = mvcam->client; + mvcam_read(client, Lane_Num, &lane_num); + if(lane_num == 4){ + mvcam->lane_num = 4; + }else{ + mvcam->lane_num = 2; + } + + mvcam_read(client, MIPI_DataRate, &mipi_datarate); + if(mipi_datarate == 0xFFFFFFFF) + mipi_datarate = MVCAM_DEFAULT_MIPI_DATARATE; + else + mipi_datarate *=1000;//register value is kbps + + mvcam->mipi_datarate = mipi_datarate; + + link_freq_menu_items[0] = mvcam->mipi_datarate>>1;//hz is half of datarate + dev_info(&client->dev, "%s: lane num %d, datarate %d bps\n", + __func__, mvcam->lane_num,mvcam->mipi_datarate); + return; +} +/* Start streaming */ +static int mvcam_start_streaming(struct mvcam *mvcam) +{ + struct i2c_client *client = mvcam->client; + int ret; + VEYE_TRACE + /* Apply customized values from user */ + // ret = __v4l2_ctrl_handler_setup(mvcam->sd.ctrl_handler); + debug_printk("mvcam_start_streaming \n"); + /* set stream on register */ + ret = mvcam_write(client, Image_Acquisition,1); + if (ret) + return ret; + + /* some v4l2 ctrls cannot change during streaming */ + mvcam_v4l2_ctrl_grab(mvcam,true); + return ret; +} + +/* Stop streaming */ +static int mvcam_stop_streaming(struct mvcam *mvcam) +{ + struct i2c_client *client = mvcam->client; + int ret; +VEYE_TRACE + /* set stream off register */ + ret = mvcam_write(client, Image_Acquisition,0); + if (ret) + dev_err(&client->dev, "%s failed to set stream\n", __func__); + debug_printk("mvcam_stop_streaming \n"); + + mvcam_v4l2_ctrl_grab(mvcam,false); + + /* + * Return success even if it was an error, as there is nothing the + * caller can do about it. + */ + return 0; +} + +static int mvcam_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct mvcam *mvcam = to_mvcam(sd); + struct i2c_client *client = mvcam->client; + int ret = 0; + enable = !!enable; + + if (mvcam->streaming == enable) { + dev_info(&client->dev, "%s already streamed!\n", __func__); + return 0; + } +VEYE_TRACE + if (enable) { + + /* + * Apply default & customized values + * and then start streaming. + */ + ret = mvcam_start_streaming(mvcam); + if (ret) + goto end; + } else { + mvcam_stop_streaming(mvcam); + } + mvcam->streaming = enable; + return ret; +end: + return ret; +} + +/* Power management functions */ +static int mvcam_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mvcam *mvcam = to_mvcam(sd); +VEYE_TRACE + + gpiod_set_value_cansleep(mvcam->pwdn_gpio, 1); + usleep_range(STARTUP_MIN_DELAY_US, + STARTUP_MIN_DELAY_US + STARTUP_DELAY_RANGE_US); + + gpiod_set_value_cansleep(mvcam->reset_gpio, 1); + usleep_range(STARTUP_MIN_DELAY_US, + STARTUP_MIN_DELAY_US + STARTUP_DELAY_RANGE_US); + debug_printk("mvcam_power_on\n"); + return 0; +} + +static int mvcam_power_off(struct device *dev) +{ + + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mvcam *mvcam = to_mvcam(sd); + VEYE_TRACE + //do not really power off, because we might use i2c script at any time + gpiod_set_value_cansleep(mvcam->pwdn_gpio, 1);//still use 1 + gpiod_set_value_cansleep(mvcam->reset_gpio, 0); + + debug_printk("mvcam_power_off, not really off\n"); + return 0; +} + + +static int mvcam_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + VEYE_TRACE + /* max framerate */ + struct v4l2_fract fract_fps; + struct mvcam *mvcam = to_mvcam(sd); + mutex_lock(&mvcam->mutex); + fie->width = mvcam->roi.width; + fie->height = mvcam->roi.height; + fract_fps.numerator = 100; + fract_fps.denominator = mvcam->max_fps*100; + fie->interval = fract_fps; + mutex_unlock(&mvcam->mutex); + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int mvcam_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct mvcam *mvcam = to_mvcam(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + +// struct v4l2_mbus_framefmt *try_fmt_meta = +// v4l2_subdev_get_try_format(sd, fh->pad, METADATA_PAD); + struct v4l2_rect *try_crop; + VEYE_TRACE + mutex_lock(&mvcam->mutex); + /* Initialize try_fmt */ + try_fmt->width = mvcam->max_width; + try_fmt->height = mvcam->max_height; + try_fmt->code = mvcam->supported_formats[0].mbus_code; + try_fmt->field = V4L2_FIELD_NONE; + + /* Initialize try_fmt for the embedded metadata pad */ +/* try_fmt_meta->width = MVCAM_EMBEDDED_LINE_WIDTH; + try_fmt_meta->height = MVCAM_NUM_EMBEDDED_LINES; + try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; + try_fmt_meta->field = V4L2_FIELD_NONE; +*/ + /* Initialize try_crop rectangle. */ + try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0); + try_crop->top = 0; + try_crop->left = 0; + try_crop->width = mvcam->max_width; + try_crop->height = mvcam->max_height; + + mutex_unlock(&mvcam->mutex); + + return 0; +} +#endif + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops mvcam_internal_ops = { + .open = mvcam_open, +}; +#endif + +static const struct v4l2_subdev_core_ops mvcam_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, + + //.s_power = mvcam_s_power, + .ioctl = mvcam_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = mvcam_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops mvcam_video_ops = { + .s_stream = mvcam_set_stream, + .g_frame_interval = mvcam_g_frame_interval, + .s_frame_interval = mvcam_s_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops mvcam_pad_ops = { + .enum_mbus_code = mvcam_csi2_enum_mbus_code, + .get_fmt = mvcam_csi2_get_fmt, + .set_fmt = mvcam_csi2_set_fmt, + .enum_frame_size = mvcam_csi2_enum_framesizes, + + //.get_selection = mvcam_get_selection, + //.set_selection = mvcam_set_selection, + .get_mbus_config = mvcam_g_mbus_config, + .enum_frame_interval = mvcam_enum_frame_interval, +}; + +static const struct v4l2_subdev_ops mvcam_subdev_ops = { + .core = &mvcam_core_ops, + .video = &mvcam_video_ops, + .pad = &mvcam_pad_ops, +}; + +static int __maybe_unused mvcam_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mvcam *mvcam = to_mvcam(sd); +VEYE_TRACE + if (mvcam->streaming) + mvcam_stop_streaming(mvcam); + + return 0; +} + +static int __maybe_unused mvcam_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mvcam *mvcam = to_mvcam(sd); + int ret; + + if (mvcam->streaming) { + ret = mvcam_start_streaming(mvcam); + if (ret) + goto error; + } + VEYE_TRACE + return 0; + +error: + mvcam_stop_streaming(mvcam); + mvcam->streaming = 0; + return ret; +} + +static int mvcam_enum_controls(struct mvcam *mvcam) +{ + struct i2c_client *client = mvcam->client; + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_fwnode_device_properties props; + int ret; + int i; + struct v4l2_ctrl *ctrl; + ctrl_hdlr = &mvcam->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, ARRAY_SIZE(mvcam_v4l2_ctrls)); + if (ret) + return ret; +VEYE_TRACE + // mutex_init(&mvcam->mutex); + ctrl_hdlr->lock = &mvcam->mutex; + + for (i = 0; i < ARRAY_SIZE(mvcam_v4l2_ctrls); ++i) { + ctrl = v4l2_ctrl_new_custom( + ctrl_hdlr, + &mvcam_v4l2_ctrls[i], + NULL); + if (ctrl == NULL) { + dev_err(&client->dev, "Failed to init %d ctrl\n",i); + continue; + } + mvcam->ctrls[i] = ctrl; + if(mvcam->ctrls[i]->id == V4L2_CID_VEYE_MV_FRAME_RATE){ + mvcam->frmrate = mvcam->ctrls[i]; + } + dev_dbg(&client->dev, "init control %s success\n",mvcam_v4l2_ctrls[i].name); + } + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + goto error; + } + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &mvcam_ctrl_ops, + &props); + if (ret) + goto error; + + mvcam->sd.ctrl_handler = ctrl_hdlr; + v4l2_ctrl_handler_setup(ctrl_hdlr); +VEYE_TRACE + dev_info(&client->dev, "mvcam_enum_controls success\n"); + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + //mutex_destroy(&mvcam->mutex); + + return ret; + +} + +/* Verify chip ID */ +static int mvcam_identify_module(struct mvcam * mvcam) +{ + int ret; + u32 device_id; + u32 firmware_version; + struct i2c_client *client = v4l2_get_subdevdata(&mvcam->sd); + + ret = mvcam_read(client, Model_Name, &device_id); + if (ret ) { + dev_err(&client->dev, "failed to read chip id\n"); + ret = -ENODEV; + return ret; + } + switch (device_id) + { + case MV_MIPI_IMX178M: + mvcam->model_id = device_id; + dev_info(&client->dev, "camera is: MV-MIPI-IMX178M\n"); + break; + case MV_MIPI_IMX296M: + mvcam->model_id = device_id; + dev_info(&client->dev, "camera is:MV-MIPI-IMX296M\n"); + break; + case MV_MIPI_SC130M: + mvcam->model_id = device_id; + dev_info(&client->dev, "camera is: MV-MIPI-SC130M\n"); + break; + case MV_MIPI_IMX265M: + mvcam->model_id = device_id; + dev_info(&client->dev, "camera is: MV-MIPI-IMX265M\n"); + break; + case MV_MIPI_IMX264M: + mvcam->model_id = device_id; + dev_info(&client->dev, "camera is: MV-MIPI-IMX264M\n"); + break; + case RAW_MIPI_SC132M: + mvcam->model_id = device_id; + dev_info(&client->dev, "camera is: RAW-MIPI-SC132M\n"); + break; + case MV_MIPI_IMX287M: + mvcam->model_id = device_id; + dev_info(&client->dev, "camera is: MV_MIPI_IMX287M\n"); + break; + case RAW_MIPI_IMX462M: + mvcam->model_id = device_id; + dev_info(&client->dev, "camera is: RAW_MIPI_IMX462M\n"); + break; + case RAW_MIPI_AR0234M: + mvcam->model_id = device_id; + dev_info(&client->dev, "camera is: RAW_MIPI_AR0234M\n"); + break; + default: + dev_err(&client->dev, "camera id do not support: %x \n",device_id); + return -EIO; + } + + ret = mvcam_read(client, Device_Version, &firmware_version); + if (ret) { + dev_err(&client->dev, "read firmware version failed\n"); + } + dev_info(&client->dev, "firmware version: 0x%04X\n", firmware_version); + return 0; +} + +/* +static int mvcam_init_mode(struct v4l2_subdev *sd) +{ + struct mvcam *mvcam = to_mvcam(sd); + struct i2c_client *client = mvcam->client; + + struct v4l2_subdev_selection sel; + //stop acquitsition + mvcam_write(client, Image_Acquisition,0); + //set to video stream mode + //mvcam_write(client, Trigger_Mode,0); + //set roi + //todo : read current roi from camera and set to media node. + //because RK3588's VICAP open the crop capbility by default, it will intercept the roi setting. + mvcam->roi.left = 0; + mvcam->roi.top = 0; + mvcam->roi.width = mvcam->max_width; + mvcam->roi.height = mvcam->max_height; + sel.target = V4L2_SEL_TGT_CROP; + sel.r = mvcam->roi; + mvcam_set_selection(sd, NULL, &sel); + return 0; +} +*/ +static void free_gpio(struct mvcam *mvcam) +{ + if (!IS_ERR(mvcam->pwdn_gpio)) + gpio_free(desc_to_gpio(mvcam->pwdn_gpio)); + if (!IS_ERR(mvcam->reset_gpio)) + gpio_free(desc_to_gpio(mvcam->reset_gpio)); + // if (!IS_ERR(mvcam->mipi_pwr_gpio)) + // gpio_free(desc_to_gpio(mvcam->mipi_pwr_gpio)); +} + +static int mvcam_check_hwcfg(struct device *dev) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mvcam *mvcam = to_mvcam(sd); + + struct device_node *node = dev->of_node; + struct device_node *endpoint_node = NULL; + struct v4l2_fwnode_endpoint vep = {0}; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &mvcam->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &mvcam->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &mvcam->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &mvcam->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + endpoint_node = of_find_node_by_name(node,"endpoint"); + if(endpoint_node != NULL){ + //printk("mvcam get endpoint node success\n"); + ret=v4l2_fwnode_endpoint_parse(&endpoint_node->fwnode, &vep); + if(ret){ + dev_info(dev, "Failed to get mvcam endpoint data lanes, set use lane num from camera %d\n",mvcam->lane_num); + }else{ + dev_info(dev, "Success to get mvcam endpoint data lanes, dts uses %d lanes\n", vep.bus.mipi_csi2.num_data_lanes); + /* Check the number of MIPI CSI2 data lanes */ + if (vep.bus.mipi_csi2.num_data_lanes != mvcam->lane_num) { + dev_err(dev, "dts lane num %d mismatch camera data lane num %d\n",vep.bus.mipi_csi2.num_data_lanes,mvcam->lane_num); + return -ENOENT; + } + } + + }else{ + dev_info(dev,"mvcam get endpoint node failed\n"); + return -ENOENT; + } + return 0; +} +static int mvcam_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + + + struct mvcam *mvcam; + char facing[2]; + int ret; + + dev_info(dev, "veye mv series camera driver version: %02x.%02x.%02x\n", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + mvcam = devm_kzalloc(&client->dev, sizeof(struct mvcam), GFP_KERNEL); + if (!mvcam) + return -ENOMEM; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&mvcam->sd, client, &mvcam_subdev_ops); + mvcam->client = client; + + mvcam->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(mvcam->reset_gpio)) { + dev_info(dev, "Failed to get reset-gpios, maybe no use\n"); + } + + mvcam->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_HIGH); + if (IS_ERR(mvcam->pwdn_gpio)) { + dev_info(dev, "Failed to get pwdn-gpios, maybe no use\n"); + } + + mutex_init(&mvcam->mutex); + + ret = mvcam_power_on(dev); + if (ret) + goto err_destroy_mutex; + + ret = mvcam_identify_module(mvcam); + if (ret){ + goto error_power_off; + } + + if (mvcam_enum_pixformat(mvcam)) { + dev_err(dev, "enum pixformat failed.\n"); + ret = -ENODEV; + goto error_power_off; + } + mvcam_get_mipifeature(mvcam); + /* Check the hardware configuration in device tree */ + if(mvcam_check_hwcfg(dev)) + goto error_power_off; + + mvcam_read(client, Sensor_Width, &mvcam->max_width); + mvcam_read(client, Sensor_Height, &mvcam->max_height); + if(mvcam->model_id == MV_MIPI_IMX178M){ + mvcam->min_width = MV_IMX178M_ROI_W_MIN; + mvcam->min_height = MV_IMX178M_ROI_H_MIN; + }else if(mvcam->model_id == MV_MIPI_SC130M){ + mvcam->min_width = MV_SC130M_ROI_W_MIN; + mvcam->min_height = MV_SC130M_ROI_H_MIN; + }else if(mvcam->model_id == MV_MIPI_IMX296M){ + mvcam->min_width = MV_IMX296M_ROI_W_MIN; + mvcam->min_height = MV_IMX296M_ROI_H_MIN; + }else if(mvcam->model_id == MV_MIPI_IMX265M){ + mvcam->min_width = MV_IMX265M_ROI_W_MIN; + mvcam->min_height = MV_IMX265M_ROI_H_MIN; + }else if(mvcam->model_id == MV_MIPI_IMX264M){ + mvcam->min_width = MV_IMX264M_ROI_W_MIN; + mvcam->min_height = MV_IMX264M_ROI_H_MIN; + }else if(mvcam->model_id == RAW_MIPI_SC132M){ + mvcam->min_width = RAW_SC132M_ROI_W_MIN; + mvcam->min_height = RAW_SC132M_ROI_H_MIN; + }else if(mvcam->model_id == MV_MIPI_IMX287M){ + mvcam->min_width = MV_IMX287M_ROI_W_MIN; + mvcam->min_height = MV_IMX287M_ROI_H_MIN; + }else if(mvcam->model_id == RAW_MIPI_IMX462M){ + mvcam->min_width = RAW_IMX462M_ROI_W_MIN; + mvcam->min_height = RAW_IMX462M_ROI_H_MIN; + }else if(mvcam->model_id == RAW_MIPI_AR0234M){ + mvcam->min_width = RAW_AR0234M_ROI_W_MIN; + mvcam->min_height = RAW_AR0234M_ROI_H_MIN; + } + v4l2_dbg(1, debug, mvcam->client, "%s: max width %d; max height %d\n", + __func__, mvcam->max_width,mvcam->max_height); + //read roi + mvcam_getroi(mvcam); + + mvcam_v4l2_ctrl_init(mvcam); + + if (mvcam_enum_controls(mvcam)) { + dev_err(dev, "enum controls failed.\n"); + ret = -ENODEV; + goto error_power_off; + } + + //mvcam_init_mode(&mvcam->sd); + //stop acquitsition + mvcam_write(client, Image_Acquisition,0); + + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + mvcam->sd.internal_ops = &mvcam_internal_ops; + mvcam->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + mvcam->pad.flags = MEDIA_PAD_FL_SOURCE; + mvcam->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&mvcam->sd.entity, 1, &mvcam->pad); + if (ret < 0){ + dev_err(dev, "media_entity_pads_init failed\n"); + goto error_power_off; + } + +#endif + memset(facing, 0, sizeof(facing)); + if (strcmp(mvcam->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(mvcam->sd.name, sizeof(mvcam->sd.name), "m%02d_%s_%s %s", + mvcam->module_index, facing, + mvcam_NAME, dev_name(mvcam->sd.dev)); + + ret = v4l2_async_register_subdev_sensor_common(&mvcam->sd); + if (ret){ + dev_err(dev, "v4l2 async register subdev failed\n"); + goto error_media_entity; + } +VEYE_TRACE + return 0; + +error_media_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&mvcam->sd.entity); +#endif +//error_handler_free: +error_power_off: + mvcam_power_off(dev); + mvcam_free_controls(mvcam); + free_gpio(mvcam); +err_destroy_mutex: + mutex_destroy(&mvcam->mutex); + + return ret; +} + +static int mvcam_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mvcam *mvcam = to_mvcam(sd); +VEYE_TRACE + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + mvcam_free_controls(mvcam); + + mutex_destroy(&mvcam->mutex); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id mvcam_of_match[] = { + { .compatible = "veye,mvcam" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mvcam_of_match); +#endif + +static const struct i2c_device_id mvcam_match_id[] = { + { "veye,mvcam", 0 }, + { }, +}; + +static struct i2c_driver veyemv_cam_i2c_driver = { + .driver = { + .name = "mvcam", + //.pm = &mvcam_pm_ops, + .of_match_table = of_match_ptr(mvcam_of_match), + }, + .probe = &mvcam_probe, + .remove = &mvcam_remove, + .id_table = mvcam_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&veyemv_cam_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&veyemv_cam_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_AUTHOR("xumm "); +MODULE_DESCRIPTION("VEYE MV series mipi camera v4l2 driver"); +MODULE_LICENSE("GPL v2"); \ No newline at end of file diff --git a/drivers/media/i2c/veye_mvcam.h b/drivers/media/i2c/veye_mvcam.h new file mode 100644 index 0000000000000..a856e32274282 --- /dev/null +++ b/drivers/media/i2c/veye_mvcam.h @@ -0,0 +1,233 @@ +#ifndef _VEYE_MVCAM_H_ +#define _VEYE_MVCAM_H_ +//typedef unsigned long u32; +#include +/* mv series registers begin */ +#define BaseCommon 0x0000 +#define ImageAcqu 0x0400 +#define ImageFeature 0x0800 +#define ImageSigProc 0x0c00 +#define IOCtrl 0x1000 +#define FPGACmd 0x1400 +#define FPGABlock 0x1800 + +#define Manufacturer_Name 0x0000 +#define Model_Name 0x0004 +#define Sensor_Name 0x0008 +#define Product_Info 0x000C +#define Device_Version 0x0010 +#define System_reset 0x0014 +#define Param_save_to_flash 0x0018 +#define System_reboot 0x001C +#define Time_stamp 0x0020 +#define Error_code 0x0024 +#define Format_cap 0x0028 + + +#define Image_Acquisition 0x400 +#define Trigger_Mode 0x404 +#define Trigger_Source 0x408 +#define Trigger_Num 0x40C +#define Trigger_Inerval 0x410 +#define Trigger_Software 0x414 +#define Trigger_Count 0x418 +#define I2C_Addr 0x41C +#define I2C_Port_Sel 0x420 +#define Reserved2 0x424 +#define User_overlay_enable 0x428 +#define User_overlay_zone0 0x42C +#define User_overlay_zone1 0x430 +#define User_overlay_zone2 0x434 +#define User_overlay_zone3 0x438 +#define User_define_zone0 0x43C +#define User_define_zone1 0x440 +#define User_define_zone2 0x444 +#define User_define_zone3 0x448 +#define Slave_mode 0x460 + +#define Test_Image_Selector 0x800 +#define Pixel_Format 0x804 +#define Sensor_Width 0x808 +#define Sensor_Height 0x80C +#define MaxFrame_Rate 0x810 +#define Framerate 0x814 +#define ROI_Width 0x818 +#define ROI_Height 0x81C +#define ROI_Offset_X 0x820 +#define ROI_Offset_Y 0x824 +#define Image_Direction 0x828 +#define Data_shift 0x82C +#define Black_Level 0x830 +#define ReadOut_Mode 0x834 +#define Lane_Num 0x83C +#define MIPI_DataRate 0x840 + +#define ISP_module_ctrl 0xC00 +#define Exposure_Mode 0xC04 +#define Target_Brightness 0xC08 +#define Exposure_Time_Source 0xC0C +#define ME_Time 0xC10 +#define AE_MAX_Time 0xC14 +#define Exp_Time 0xC18 +#define Gain_Mode 0xC1C +#define Manual_Gain 0xC20 +#define AG_Max_Gain 0xC24 +#define Cur_Gain 0xC28 +#define AAROIOffsetX 0xC2C +#define AAROIOffsetY 0xC30 +#define AAROIWidth 0xC34 +#define AAROIHeight 0xC38 +#define WB_Mode 0xC3C +#define MWB_Rgain 0xC40 +#define MWB_Bgain 0xC44 +#define AWBROIOffsetX 0xC48 +#define AWBROIOffsetY 0xC4C +#define AWBROIWidth 0xC50 +#define AWBROIHeight 0xC54 +#define AWB_Rgain 0xC58 +#define AWB_Bgain 0xC5C +#define Gamma 0xC60 +#define DPC_Start 0xC64 +#define DPC_Status 0xC68 +#define DPC_Count 0xC6C +#define AAROI_enable 0xC80 + +#define Trigger_Delay 0x1000 +#define Trigger_Activation 0x1004 +#define Trigger_Filter_Enable 0x1008 +#define Trigger_Filter_Width 0x100C +#define Trigger_Exp_Delay 0x1010 +#define GPIOIN_Status 0x1014 + +#define GPIO1_OutSelect 0x1020 +#define GPIO1_Useroutput 0x1024 +#define GPIO1_Reverse 0x1028 +#define GPIO1_OutStatus 0x102C + +#define GPIO2_OutSelect 0x1030 +#define GPIO2_Useroutput 0x1034 +#define GPIO2_Reverse 0x1038 +#define GPIO2_OutStatus 0x103C +/* register ends*/ + +#define MVCAM_MAX_CTRLS 40 + +/* user define v4l2 controls*/ +#define V4L2_CID_VEYE_MV_BASE (V4L2_CID_USER_BASE + 0x1000) +#define V4L2_CID_VEYE_MV_TRIGGER_MODE (V4L2_CID_VEYE_MV_BASE + 1) +#define V4L2_CID_VEYE_MV_TRIGGER_SRC (V4L2_CID_VEYE_MV_BASE + 2) +#define V4L2_CID_VEYE_MV_SOFT_TRGONE (V4L2_CID_VEYE_MV_BASE + 3) + +#define V4L2_CID_VEYE_MV_FRAME_RATE (V4L2_CID_VEYE_MV_BASE + 4) +#define V4L2_CID_VEYE_MV_ROI_X (V4L2_CID_VEYE_MV_BASE + 5) +#define V4L2_CID_VEYE_MV_ROI_Y (V4L2_CID_VEYE_MV_BASE + 6) + +enum enum_TriggerMode{ + Image_Continues = 0, + Image_Trigger = 1, + Image_Speed_Trigger = 2, + Image_trigger_mode_num, +}; + +enum enum_TriggerSrc{ + Trg_Soft = 0, + Trg_Hard = 1, + Trg_Hard_src_num, +}; + +#define I2C_READ_RETRY_COUNT 1 +#define I2C_WRITE_RETRY_COUNT 1 + +/* device id list of mv series */ +#define MV_MIPI_IMX178M 0x0178 +#define MV_MIPI_IMX296M 0x0296 +#define MV_MIPI_SC130M 0x0130 +#define MV_MIPI_IMX265M 0x0265 +#define MV_MIPI_IMX264M 0x0264 +#define MV_MIPI_IMX287M 0x0287 +#define RAW_MIPI_SC132M 0x8132 +#define RAW_MIPI_IMX462M 0x8462 +#define RAW_MIPI_AR0234M 0x8234 + +/* MV mipi datarate is 1.5Gbps */ +#define MVCAM_DEFAULT_MIPI_DATARATE 1500000000 +/* MV mipi clk is 742.5Mhz */ +#define MVCAM_DEFAULT_LINK_FREQ 742500000 +//pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample +#define MV_CAM_PIXEL_RATE 750000000 //1.5Gbps*2*2/8=750M + +#define MV_CAM_DEF_FPS 22U + +#define MV_IMX178M_ROI_W_MIN 376U +#define MV_IMX178M_ROI_H_MIN 320U +#define MV_SC130M_ROI_W_MIN 64U +#define MV_SC130M_ROI_H_MIN 64U +#define MV_IMX296M_ROI_W_MIN 80U +#define MV_IMX296M_ROI_H_MIN 64U +#define MV_IMX265M_ROI_W_MIN 264U +#define MV_IMX265M_ROI_H_MIN 64U +#define MV_IMX264M_ROI_W_MIN 264U +#define MV_IMX264M_ROI_H_MIN 64U +#define RAW_SC132M_ROI_W_MIN 64U +#define RAW_SC132M_ROI_H_MIN 64U +#define MV_IMX287M_ROI_W_MIN 264U +#define MV_IMX287M_ROI_H_MIN 64U +#define RAW_IMX462M_ROI_W_MIN 368U +#define RAW_IMX462M_ROI_H_MIN 304U +#define RAW_AR0234M_ROI_W_MIN 64U +#define RAW_AR0234M_ROI_H_MIN 64U + +#define MV_CAM_ROI_W_ALIGN 8U +#define MV_CAM_ROI_H_ALIGN 4U + +#define MV_MIPI_DATA_LANS 2 + +enum mipi_datatype { + IMAGE_DT_YUV420_8 = 0x18, + IMAGE_DT_YUV420_10, + + IMAGE_DT_YUV420CSPS_8 = 0x1C, + IMAGE_DT_YUV420CSPS_10, + IMAGE_DT_YUV422_8, + IMAGE_DT_YUV422_10, + IMAGE_DT_RGB444, + IMAGE_DT_RGB555, + IMAGE_DT_RGB565, + IMAGE_DT_RGB666, + IMAGE_DT_RGB888, + + IMAGE_DT_RAW6 = 0x28, + IMAGE_DT_RAW7, + IMAGE_DT_RAW8, + IMAGE_DT_RAW10, + IMAGE_DT_RAW12, + IMAGE_DT_RAW14, +}; + +enum mv_datatype_index{ + MV_DT_Mono8 = 0, + MV_DT_Mono10 = 1, + MV_DT_Mono12 = 2, + MV_DT_Mono14 = 3, + MV_DT_UYVY = 4, +}; + +enum yuv_order { + YUV_ORDER_YUYV = 0, + YUV_ORDER_YVYU = 1, + YUV_ORDER_UYVY = 2, + YUV_ORDER_VYUY = 3, +}; + +enum bayer_order { + //Carefully ordered so that an hflip is ^1, + //and a vflip is ^2. + BAYER_ORDER_BGGR = 0, + BAYER_ORDER_GBRG = 1, + BAYER_ORDER_GRBG = 2, + BAYER_ORDER_RGGB = 3, + BAYER_ORDER_GRAY = 4, +}; + +#endif + diff --git a/drivers/media/i2c/veye_vbyone.c b/drivers/media/i2c/veye_vbyone.c new file mode 100644 index 0000000000000..44444a7651021 --- /dev/null +++ b/drivers/media/i2c/veye_vbyone.c @@ -0,0 +1,767 @@ +/* + * thcv242a.c - Thine THCV242A deserializer and THCV241A serializer driver + * + * Copyright (c) 2023, www.veye.cc, TIANJIN DATA IMAGING TECHNOLOGY CO.,LTD + * + * This program is for the THCV242A V-by-ONE deserializer in connection + * with the SHA241 serializer from Thine + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "veye_vbyone.h" + +const struct regmap_config thcv242a_regmap_config = { + .reg_bits = 16, + .val_bits = 8, +}; + +const struct regmap_config thcv241a_regmap_config_orig = { + .reg_bits = 16, + .val_bits = 8, +}; + +const struct regmap_config thcv241a_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +/*------------------------------------------------------------------------------ + * thcv242a FUNCTIONS + *----------------------------------------------------------------------------*/ +/* +static int thcv242a_read(struct thcv242a_priv *priv, unsigned int reg, + unsigned int *val) +{ + int err; + err = regmap_read(priv->regmap, reg, val); + if(err) { + dev_err(&priv->client->dev, + "Cannot read register 0x%02x (%d)!\n", reg, err); + } + return err; +} +*/ + +static int thcv242a_write(const struct thcv242a_priv *priv, unsigned int reg, + unsigned int val) +{ + int err; + + err = regmap_write(priv->regmap, reg, val); + if(err) { + dev_err(&priv->client->dev, + "Cannot write register 0x%02x (%d)!\n", reg, err); + } + //dev_err(&priv->client->dev,"W242 0x%x : 0x%x\n", reg,val); + return err; +} + +/* +static int thcv241a_read(struct thcv241a_priv *priv, unsigned int reg, + unsigned int *val) +{ + int err; + err = regmap_read(priv->regmap, reg, val); + if(err) { + dev_err(&priv->client->dev, + "Cannot read subdev 0x%02x register 0x%02x (%d)!\n", + priv->client->addr, reg, err); + } + return err; +} +*/ + +static int thcv241a_write(const struct thcv241a_priv *priv, unsigned int reg, + unsigned int val) +{ + int err; + err = regmap_write(priv->regmap, reg, val); + if(err) { + dev_err(&priv->client->dev, + "Cannot write subdev 0x%02x register 0x%02x (%d)!\n", + priv->client->addr, reg, err); + } + //dev_err(&priv->client->dev,"W241 0x%x : 0x%x\n", reg,val); + return err; +} +/* +void print_i2c_client_info(struct i2c_client *client) { + if (client) { + printk("i2c bus: %s\n", dev_name(&client->dev)); + + // Additional information you want to print + } else { + printk("Invalid i2c_client structure.\n"); + } +}*/ + +/* +void print_regmap_info(struct regmap *regmap) { + if (regmap) { + unsigned int reg_stride = regmap_get_reg_stride(regmap); + unsigned int val_bytes = regmap_get_val_bytes(regmap); + + printk("Register address length: %u bytes\n", reg_stride); + printk("Register value length: %u bytes\n", val_bytes); + + // Additional information you want to print + } else { + printk("Invalid regmap structure.\n"); + } +}*/ + +static int thcv242a_init_pre(struct thcv242a_priv *priv) +{ + int err = 0; + //unsigned int val; + struct device *dev = &priv->client->dev; + dev_info(dev, "%s: begin \n", __func__); + + err |= thcv242a_write(priv,R_2WIREPT1_PASS_ADRIN0,priv->ser[0]->i2c_address);//0x34 + err |= thcv242a_write(priv,0x0004,0x03); + err |= thcv242a_write(priv,0x0010,0x10); + err |= thcv242a_write(priv,0x1704,0x01); + err |= thcv242a_write(priv,0x0102,0x02); + + err |= thcv242a_write(priv,0x0103,0x02); + err |= thcv242a_write(priv,0x0104,0x02); + err |= thcv242a_write(priv,0x0105,0x02); + err |= thcv242a_write(priv,0x0100,0x03); + err |= thcv242a_write(priv,0x010F, 0x25); + err |= thcv242a_write(priv,0x010A, 0x15); + err |= thcv242a_write(priv,0x0031, 0x02); + + dev_info(dev, "%s: successfully \n", __func__); + + //err |= thcv242a_read(priv,0x010A, &val); + //dev_info(dev, "thcv242a_read 0x010A val is %x ,should be 0x15\n",val ); + + //err |= thcv242a_read(priv,0x0031, &val); + //dev_info(dev, "thcv242a_read 0x31 val is %x ,should be 0x2\n",val ); + return err; +} + +static int thcv241a_regmap_update(struct thcv242a_priv *priv, int ser_nr); + +static int thcv241a_init(struct thcv242a_priv *despriv,struct thcv241a_priv *serpriv) +{ + int err = 0; + //unsigned int val = 0; + unsigned int io_type = 0; + unsigned int io_dir = 0; + struct device *dev = &despriv->client->dev; + dev_info(dev, "%s: begin \n", __func__); + + //dev_info(dev, "=============des reg map info============\n"); + //print_i2c_client_info(despriv->client); + //print_regmap_info(despriv->regmap); + //dev_info(dev, "=============des reg map info==END==========\n"); + + //dev_info(dev, "=============ser reg map info============\n"); + //print_i2c_client_info(serpriv->client); + //print_regmap_info(serpriv->regmap); + //dev_info(dev, "=============ser reg map info==END==========\n"); + + err |= thcv242a_write(despriv,R_2WIREPT_WA_DATA_BYTE, 0x10); + + //err |= thcv242a_read(despriv,0x0032, &val); + //dev_info(dev, "thcv242a_read 0x0032 val is %x ,should be 0x10\n",val ); + + err |= thcv241a_write(serpriv,0x00FE, 0x11); + + // err |= thcv241a_read(serpriv,0x00FE, &val); + //dev_info(dev, "thcv241a_read 0x00FE val is %x ,should be 0x11\n",val ); + + + err |= thcv242a_write(despriv,R_2WIREPT_WA_DATA_BYTE, 0x00); + //change to 8bit add and 8bit val + err |= thcv241a_regmap_update(despriv,0); + dev_info(dev, "%s: change 241 addr to 8bit addr \n", __func__); + + err |= thcv241a_write(serpriv,0xF3,0x00); + err |= thcv241a_write(serpriv,0xF2,0x22); + + //err |= thcv241a_read(serpriv,0xF2, &val); + //dev_info(dev, "thcv241a_read 0xF2 val is %x ,should be 0x22\n",val ); + + err |= thcv241a_write(serpriv,0xF0,0x03); + + //err |= thcv241a_read(serpriv,0xF0, &val); + //dev_info(dev, "thcv241a_read 0xF0 val is %x ,should be 0x3\n",val ); + + err |= thcv241a_write(serpriv,0xFF,0x19); + err |= thcv241a_write(serpriv,0xF6,0x15); + err |= thcv241a_write(serpriv,0xC9,0x05); + err |= thcv241a_write(serpriv,0xCA,0x05); + err |= thcv241a_write(serpriv,0xFE,0x21); + err |= thcv241a_write(serpriv,0x76,0x10); + err |= thcv241a_write(serpriv,0x0F,0x01); + + if(serpriv->csi_lane_speed == 1500){ + err |= thcv241a_write(serpriv,0x11,0x29); + err |= thcv241a_write(serpriv,0x12,0xAA); + err |= thcv241a_write(serpriv,0x13,0xAA); + err |= thcv241a_write(serpriv,0x14,0xAA); + err |= thcv241a_write(serpriv,0x15,0x43); + }else if(serpriv->csi_lane_speed == 1188){ + err |= thcv241a_write(serpriv,0x11,0x2C); + err |= thcv241a_write(serpriv,0x12,0x00); + err |= thcv241a_write(serpriv,0x13,0x00); + err |= thcv241a_write(serpriv,0x14,0x00); + err |= thcv241a_write(serpriv,0x15,0x44); + }else{ + dev_info(dev, "thcv241a_init csi lan speed not supported\n" ); + } + err |= thcv241a_write(serpriv,0x16,0x01); + err |= thcv241a_write(serpriv,0x00,0x00); + err |= thcv241a_write(serpriv,0x01,0x00); + err |= thcv241a_write(serpriv,0x02,0x00); + err |= thcv241a_write(serpriv,0x55,0x00); + err |= thcv241a_write(serpriv,0x04,0x00); + err |= thcv241a_write(serpriv,0x2B,0x04); + err |= thcv241a_write(serpriv,0x2F,0x00); + err |= thcv241a_write(serpriv,0x2D,0x11); + err |= thcv241a_write(serpriv,0x2C,0x01); + err |= thcv241a_write(serpriv,0x05,0x01); + err |= thcv241a_write(serpriv,0x06,0x01); + err |= thcv241a_write(serpriv,0x27,0x00); + err |= thcv241a_write(serpriv,0x1D,0x00); + err |= thcv241a_write(serpriv,0x1E,0x00); + + if(despriv->trgin_gpio_mode){ + io_type |= 0x1; //polling + io_dir &= 0xFE;// output bit0-->0 + } + if(despriv->out1_gpio_mode){ + io_type |= 0x4;//polling + io_dir |= 0x4;// input + }else{ + io_dir |= 0x4;//Inconsistent with the datasheet, consistent with the tool. + } + if(despriv->out2_gpio_mode){ + io_type |= 0x8;//polling + io_dir |= 0x8;// input + } + io_dir |= 0x20; + + err |= thcv241a_write(serpriv,R_GPIO_TYP,io_type); + + err |= thcv241a_write(serpriv,R_GPIO_OEN,io_dir); + + err |= thcv241a_write(serpriv,R_GPIO_CMOSEN,0x0F);//CMOS mode + + dev_info(dev, "Set 241 IO config 0x%x: 0x%x; 0x%x:0x%x; 0x%x:0x%x \n",R_GPIO_TYP,io_type,R_GPIO_OEN,io_dir,R_GPIO_CMOSEN,0x0F ); + + dev_info(dev, "%s: successfully \n", __func__); + + // err |= thcv241a_read(serpriv,0x3F, &val); + // dev_info(dev, "thcv241a_read 0x3F val is %x ,should be 0xF\n",val ); + return err; +} + +static int thcv242a_init_post(struct thcv242a_priv *priv) +{ + int err = 0; + unsigned int io_mode = 0; + struct thcv241a_priv * serpriv = priv->ser[0]; + struct device *dev = &priv->client->dev; + dev_info(dev, "%s: begin \n", __func__); + err |= thcv242a_write(priv,0x0010,0x11); + err |= thcv242a_write(priv,0x1010,0xA1); + err |= thcv242a_write(priv,0x1011,0x06); + err |= thcv242a_write(priv,0x1012,0x00); + err |= thcv242a_write(priv,0x1021,0x20); + err |= thcv242a_write(priv,0x1022,0x02); + err |= thcv242a_write(priv,0x1023,0x11); + err |= thcv242a_write(priv,0x1024,0x00); + err |= thcv242a_write(priv,0x1025,0x00); + err |= thcv242a_write(priv,0x1026,0x00); + err |= thcv242a_write(priv,0x1027,0x07); + err |= thcv242a_write(priv,0x1028,0x02); + err |= thcv242a_write(priv,0x1030,0x00); + err |= thcv242a_write(priv,0x1100,0x01); + err |= thcv242a_write(priv,0x1101,0x01); + err |= thcv242a_write(priv,0x1102,0x01); + err |= thcv242a_write(priv,0x1600,0x1A); + err |= thcv242a_write(priv,0x1605,0x29); + err |= thcv242a_write(priv,0x1606,0x44); + err |= thcv242a_write(priv,0x161F,0x00); + if(serpriv->csi_lane_speed == 1500){ + err |= thcv242a_write(priv,0x1609,0x0E); + err |= thcv242a_write(priv,0x160A,0x18); + err |= thcv242a_write(priv,0x160B,0x0C); + err |= thcv242a_write(priv,0x160D,0x11); + err |= thcv242a_write(priv,0x160E,0x06); + err |= thcv242a_write(priv,0x160F,0x09); + err |= thcv242a_write(priv,0x1610,0x05); + err |= thcv242a_write(priv,0x1611,0x1A); + err |= thcv242a_write(priv,0x1612,0x0D); + }else if(serpriv->csi_lane_speed == 1188){ + err |= thcv242a_write(priv,0x1609,0x0B); + err |= thcv242a_write(priv,0x160A,0x12); + err |= thcv242a_write(priv,0x160B,0x0A); + err |= thcv242a_write(priv,0x160D,0x0E); + err |= thcv242a_write(priv,0x160E,0x03); + err |= thcv242a_write(priv,0x160F,0x07); + err |= thcv242a_write(priv,0x1610,0x04); + err |= thcv242a_write(priv,0x1611,0x14); + err |= thcv242a_write(priv,0x1612,0x0B); + }else{ + dev_info(dev, "thcv241a_init csi lan speed not supported\n" ); + } + + err |= thcv242a_write(priv,0x1703,0x01); + err |= thcv242a_write(priv,0x1704,0x11); + + if(priv->out1_gpio_mode){ + io_mode |= 0x4; //Through GPo Mode + } + if(priv->out2_gpio_mode){ + io_mode |= 0x40;//Through GPo Mode + } + err |= thcv242a_write(priv,R_GPIO23_MODE,io_mode); + dev_info(dev, "write 242 --- 0x1003: 0x%x \n", io_mode); + if(priv->trgin_gpio_mode){ + io_mode |= 0x3; //Through GPI Mode + } + err |= thcv242a_write(priv,R_GPIO01_MODE,io_mode); + dev_info(dev, "write 242 --- 0x1004: 0x%x \n", io_mode); + + err |= thcv242a_write(priv,0x001B,0x18); + err |= thcv242a_write(priv,R_2WIREPT_WA_DATA_BYTE,0x10); + err |= thcv242a_write(priv,R_2WIREPT1_PASS_ADR000,priv->cam_i2c_address); + err |= thcv242a_write(priv,R_2WIREPT1_PASS_ADR001,priv->cam_i2c_address); + + err |= thcv242a_write(priv,R_2WIREPT_WA_DATA_BYTE,priv->cam_i2c_pt_setting); + dev_info(dev, "%s: successfully \n", __func__); + return err; +} + +static int thcv242a_init_gpio(const struct thcv242a_priv *priv) +{ + return 0; +} + +static void thcv242a_free_gpio(const struct thcv242a_priv *priv) +{ + +} + +static void thcv242a_pwr_enable(const struct thcv242a_priv *priv) +{ + +} + +static void thcv242a_pwr_disable(const struct thcv242a_priv *priv) +{ + +} + +static int thcv242a_parse_dt(struct thcv242a_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *np = dev->of_node; + + int err = 0; + int val = 0; + + if(!np) + return -ENODEV; + + dev_info(dev, "%s: deserializer:\n", __func__); + + err = of_property_read_u32(np, "csi-lane-count", &val); + if(err) { + dev_info(dev, "%s: - csi-lane-count property not found\n", __func__); + + /* default value: 2 */ + priv->csi_lane_count = 2; + dev_info(dev, "%s: - csi-lane-count set to default val: 4\n", __func__); + } else { + /* set csi-lane-count*/ + priv->csi_lane_count = val; + dev_info(dev, "%s: - csi-lane-count %i\n", __func__, val); + } + + err = of_property_read_u32(np, "coax-num", &val); + if(err) { + dev_info(dev, "%s: - coax-num property not found\n", __func__); + + priv->coax_num = 1; + dev_info(dev, "%s: - coax_num set to default val: 1\n", __func__); + } else { + priv->coax_num = val; + dev_info(dev, "%s: - coax_num %i\n", __func__, val); + } + + err = of_property_read_u32(np, "cam-i2c-pt-setting", &val); + if(err) { + dev_info(dev, "%s: - cam-i2c-pt-setting not found\n", __func__); + + priv->cam_i2c_pt_setting = 0x13; + dev_info(dev, "%s: - cam_i2c_pt_setting set to default val: 0x12\n", __func__); + } else { + priv->cam_i2c_pt_setting = val; + dev_info(dev, "%s: - cam_i2c_pt_setting %i\n", __func__, val); + } + + err = of_property_read_u32(np, "camera-i2c-address", &val); + if(err) { + dev_info(dev, "%s: - camera-i2c-address not found\n", __func__); + priv->cam_i2c_address = 0x3b; + dev_info(dev, "%s: - camera-i2c-address set to default val: 0x18\n", + __func__); + } else { + dev_info(dev, "%s: - camera-i2c-address: 0x%X \n", __func__, val); + priv->cam_i2c_address=val; + } + + err = of_property_read_u32(np, "trgin-gpio-mode", &val); + if (err) { + dev_info(dev, "Failed to read trgin-gpio: %d\n", err); + priv->trgin_gpio_mode = GPIO_MODE_NO_USE; + }else{ + priv->trgin_gpio_mode = val; + } + + err = of_property_read_u32(np, "out1-gpio-mode", &val); + if (err) { + dev_info(dev, "Failed to read out1-gpio: %d\n", err); + priv->out1_gpio_mode = GPIO_MODE_NO_USE; + }else{ + priv->out1_gpio_mode = val; + } + + err = of_property_read_u32(np, "out2-gpio-mode", &val); + if (err) { + dev_info(dev, "Failed to read trgin-gpio: %d\n", err); + priv->out2_gpio_mode = GPIO_MODE_NO_USE; + }else{ + priv->out2_gpio_mode = val; + } + + return 0; + +} + +/*------------------------------------------------------------------------------ + * THCV241A FUNCTIONS + *----------------------------------------------------------------------------*/ + +static void thcv241a_free(struct thcv242a_priv *priv) +{ + i2c_unregister_device(priv->ser[0]->client); +} + +//orignal 16bit addr,8bit val +static int thcv241a_regmap_init(struct thcv242a_priv *priv, int ser_nr) +{ + struct regmap *new_regmap = NULL; + struct device *dev = &priv->client->dev; + int err = 0; + + /* setup now regmap */ + new_regmap = devm_regmap_init_i2c(priv->ser[ser_nr]->client, + &thcv241a_regmap_config_orig); + if(IS_ERR_VALUE(priv->regmap)) { + err = PTR_ERR(priv->regmap); + dev_err(dev, "regmap init of subdevice failed (%d)\n", err); + return err; + } + dev_info(dev, "%s init regmap done\n", __func__); + + priv->ser[ser_nr]->regmap = new_regmap; + return err; +} + +//update to addr,8bit val +static int thcv241a_regmap_update(struct thcv242a_priv *priv, int ser_nr) +{ + struct regmap *new_regmap = NULL; + struct device *dev = &priv->client->dev; + int err = 0; + + if(priv->ser[ser_nr]->regmap){ + regmap_exit(priv->ser[ser_nr]->regmap); + } + /* setup now regmap */ + new_regmap = devm_regmap_init_i2c(priv->ser[ser_nr]->client, + &thcv241a_regmap_config); + if(IS_ERR_VALUE(priv->regmap)) { + err = PTR_ERR(priv->regmap); + dev_err(dev, "regmap init of subdevice failed (%d)\n", err); + return err; + } + priv->ser[ser_nr]->regmap = new_regmap; + dev_info(dev, "%s regmap done\n", __func__); + return err; +} + +static int thcv241a_alloc(struct thcv242a_priv *priv, int ser_nr) +{ + struct thcv241a_priv *priv_ser; + struct device *dev = &priv->client->dev; + + priv_ser = devm_kzalloc(dev, sizeof(struct thcv241a_priv), GFP_KERNEL); + if(!priv) + return -ENOMEM; + + priv->ser[ser_nr] = priv_ser; + priv->ser[ser_nr]->initialized = 0; + return 0; +} + +static int thcv241a_i2c_client(struct thcv242a_priv *priv, int ser_nr, + int addr) +{ + struct i2c_client *new_client = NULL; + struct device *dev = &priv->client->dev; + + struct i2c_board_info *ser_board_info; + ser_board_info = devm_kzalloc(dev, sizeof(struct i2c_board_info), GFP_KERNEL); + ser_board_info->addr = addr; + + new_client = i2c_new_client_device(priv->client->adapter,ser_board_info); + + if(!new_client) { + dev_warn(dev, "failed to add i2c client\n"); + return -1; + } + + priv->ser[ser_nr]->client = new_client; + dev_info(dev, "%s init client done\n", __func__); + return 0; +} + +static int thcv241a_parse_dt(struct i2c_client *client, + struct thcv242a_priv *priv) +{ + struct device *dev = &client->dev; + struct device_node *des = dev->of_node; + struct device_node *ser; + struct thcv241a_priv *thcv241a; + + u32 val = 0; + int err = 0; + + /* get serializers device_node from dt */ + ser = of_get_child_by_name(des, "serializer"); + if(!ser) { + dev_info(dev, "%s: no serializer found in device tree\n", + __func__); + return 0; + } + + dev_info(dev, "%s: parsing serializers device tree:\n", __func__); + + /* allocate memory for serializer */ + err = thcv241a_alloc(priv, 0); + if(err) { + dev_info(dev, "%s: - allocating thcv241a failed\n", + __func__); + goto ERR; + } + thcv241a = priv->ser[0]; + + /* get i2c address */ + err = of_property_read_u32(ser, "i2c-address", &val); + if(err) { + dev_info(dev, "%s: - i2c-address not found\n", __func__); + thcv241a->i2c_address = 0x34; + dev_info(dev, "%s: - i2c-address set to default val: 0x18\n", + __func__); + } else { + dev_info(dev, "%s: - i2c-address: 0x%X \n", __func__, val); + thcv241a->i2c_address=val; + } + + err = of_property_read_u32(ser, "csi-lane-count", &val); + if(err) { + dev_info(dev, "%s: - csi-lane-count property not found\n", + __func__); + /* default value: 2 */ + thcv241a->csi_lane_count = 2; + dev_info(dev, "%s: csi-lane-count set to default val: 2\n", + __func__); + } else { + /* set csi-lane-count*/ + thcv241a->csi_lane_count = val; + dev_info(dev, "%s: - csi-lane-count %i\n", __func__, val); + } + + err = of_property_read_u32(ser, "csi-lane-speed", &val); + if(err) { + dev_info(dev, "%s: - csi-lane-speed property not found\n", + __func__); + thcv241a->csi_lane_speed = 1500; + dev_info(dev, "%s: csi-lane-speed set to default val: 2\n", + __func__); + } else { + /* set csi-lane-count*/ + thcv241a->csi_lane_speed = val; + dev_info(dev, "%s: - csi-lane-speed %i\n", __func__, val); + } + if(thcv241a->csi_lane_speed != 1500 && thcv241a->csi_lane_speed != 1188) + { + dev_err(dev, "%s: - csi-lane-speed %i not supported,will exit!\n", __func__, val); + goto ERR; + } + + err = thcv241a_i2c_client(priv, 0, thcv241a->i2c_address); + if(err) { + dev_info(dev, "%s: - thcv241a_i2c_client failed\n", + __func__); + goto ERR; + } + + err = thcv241a_regmap_init(priv, 0); + if(err) { + dev_info(dev, "%s: - thcv241a_regmap_init failed\n", + __func__); + goto ERR; + } + + /* all initialization of this serializer complete */ + thcv241a->initialized = 1; + dev_info(dev, "%s: serializer %i successfully parsed\n", __func__,0); + + return 0; +ERR: + return -1; +} + +/*------------------------------------------------------------------------------ + * PROBE FUNCTION + *----------------------------------------------------------------------------*/ + +static int thcv242a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct thcv242a_priv *priv; + struct device *dev = &client->dev; + int err; + + dev_info(dev, "%s: start\n", __func__); + + priv = devm_kzalloc(dev, sizeof(struct thcv242a_priv), GFP_KERNEL); + if(!priv) + return -ENOMEM; + + priv->client = client; + i2c_set_clientdata(client, priv); + + err = thcv242a_parse_dt(priv); + if(unlikely(err < 0)) { + dev_err(dev, "%s: error parsing device tree\n", __func__); + goto err_parse_dt; + } + + err = thcv242a_init_gpio(priv); + if(unlikely(err < 0)) { + dev_err(dev, "%s: error initializing gpios\n", __func__); + goto err_init_gpio; + } + + priv->regmap = devm_regmap_init_i2c(client, &thcv242a_regmap_config); + if(IS_ERR_VALUE(priv->regmap)) { + err = PTR_ERR(priv->regmap); + dev_err(dev, "%s: regmap init failed (%d)\n", __func__, err); + goto err_regmap; + } + + thcv241a_parse_dt(client, priv); + + /* turn on deserializer */ + thcv242a_pwr_enable(priv); + + //msleep(6); // wait for sensor to start + + /* init deserializer */ + err = thcv242a_init_pre(priv); + if(unlikely(err)) { + dev_err(dev, "%s: error initializing thcv242a pre\n", __func__); + goto err_regmap; + } + msleep(6); + /*init serializer*/ + err = thcv241a_init(priv,priv->ser[0]); + + if(unlikely(err)) { + dev_err(dev, "%s: error initializing thcv242a\n", __func__); + goto err_regmap; + } + msleep(6); + /* init deserializer */ + err = thcv242a_init_post(priv); + if(unlikely(err)) { + dev_err(dev, "%s: error initializing thcv242a post\n", __func__); + goto err_regmap; + } + + return 0; + +err_regmap: + thcv241a_free(priv); + thcv242a_pwr_disable(priv); + thcv242a_free_gpio(priv); +err_init_gpio: +err_parse_dt: + devm_kfree(dev, priv); + return err; +} + +static int thcv242a_remove(struct i2c_client *client) +{ + struct thcv242a_priv *priv = dev_get_drvdata(&client->dev); + + thcv241a_free(priv); + thcv242a_pwr_disable(priv); + thcv242a_free_gpio(priv); + + dev_info(&client->dev, "thcv242a removed\n"); + return 0; +} + +static const struct of_device_id veye_vbyone_dt_ids[] = { + {.compatible = "veye,vbyone"}, + {/* sentinel */} +}; +MODULE_DEVICE_TABLE(of, veye_vbyone_dt_ids); + +static struct i2c_driver thcv242a_driver = { + .driver = { + .name = "veye_vbyone", + .of_match_table = veye_vbyone_dt_ids, + }, + .probe = thcv242a_probe, + .remove = thcv242a_remove, +}; + +module_i2c_driver(thcv242a_driver); + +MODULE_AUTHOR("Xu Mengmeng "); +MODULE_DESCRIPTION("V-by-ONE driver from VEYE IMAGING"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/veye_vbyone.h b/drivers/media/i2c/veye_vbyone.h new file mode 100644 index 0000000000000..2849b2ae5523d --- /dev/null +++ b/drivers/media/i2c/veye_vbyone.h @@ -0,0 +1,88 @@ +/* + * thcv242a.c - Thine THCV242A deserializer and THCV241A serializer driver + * + * Copyright (c) 2023, www.veye.cc, TIANJIN DATA IMAGING TECHNOLOGY CO.,LTD + * + * This program is for the THCV242A V-by-ONE deserializer in connection + * with the SHA241 serializer from Thine + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef I2C_THCV241A_H +#define I2C_THCV241A_H + +#include + +/*------------------------------------------------------------------------------ + * Deserializer registers + *----------------------------------------------------------------------------*/ +#define R_2WIREPT_WA_DATA_BYTE 0x0032 +#define R_2WIREPT1_PASS_ADR000 0x0040 +#define R_2WIREPT1_PASS_ADR001 0x0041 + +#define R_2WIREPT1_PASS_ADRIN0 0x0050 + +#define R_GPIO23_MODE 0x1003 +#define R_GPIO01_MODE 0x1004 + +/*------------------------------------------------------------------------------ + * Serializer registers + *----------------------------------------------------------------------------*/ + +#define R_GPIO_TYP 0x3D +#define R_GPIO_OEN 0x3E +#define R_GPIO_CMOSEN 0x3F + + +/*------------------------------------------------------------------------------ + * DEFINES + *----------------------------------------------------------------------------*/ + +#define NUM_SERIALIZER 1 + +#define GPIO_MODE_NO_USE 0 +#define GPIO_MODE_POLLING 1 +#define GPIO_MODE_I2C_CTL 2 + +struct thcv241a_priv { + struct i2c_client *client; + struct regmap *regmap; + //struct thcv242a_priv *parent; + + int i2c_address; + int csi_lane_count; + int csi_lane_speed; + + int initialized; +}; + + +struct thcv242a_priv { + struct i2c_client *client; + struct regmap *regmap; + + struct thcv241a_priv *ser[NUM_SERIALIZER]; //serializers + + int csi_lane_count; + int coax_num; + int cam_i2c_pt_setting; + int cam_i2c_address; + + int trgin_gpio_mode; // 0: no use ;1 : polling + int out1_gpio_mode; // 0: no use ;1 : polling + int out2_gpio_mode; // 0: no use ;1 : polling + +}; + +#endif /* I2C_DS90UB954_H */ diff --git a/drivers/media/i2c/veyecam2m.c b/drivers/media/i2c/veyecam2m.c new file mode 100644 index 0000000000000..576708d9357c2 --- /dev/null +++ b/drivers/media/i2c/veyecam2m.c @@ -0,0 +1,1259 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * veyecam2m driver + */ + +#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 + + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) +#define VEYECAM2M_MEDIA_BUS_FMT MEDIA_BUS_FMT_UYVY8_2X8 //MEDIA_BUS_FMT_YUYV8_2X8 +//#define DEBUG_PRINTK +#ifndef DEBUG_PRINTK +#define debug_printk(s , ... ) +#define VEYE_TRACE +#else +#define debug_printk printk +#define VEYE_TRACE printk("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__); +#endif + +/* External clock frequency is 24.0M */ +// we do not need it +#define VEYECAM2M_XCLK_FREQ 24000000 + +/* Pixel rate is fixed at 74.25M for all the modes */ +#define VEYECAM2M_PIXEL_RATE 74250000 +/*mipi clk is 297Mhz */ +#define VEYECAM2M_DEFAULT_LINK_FREQ 297000000 + + +#define VEYECAM2M_XCLR_MIN_DELAY_US 6000 +#define VEYECAM2M_XCLR_DELAY_RANGE_US 1000 + +/* veyecam2m model register address */ +#define VEYECAM2M_MODEL_ID_ADDR 0x0001 +#define VEYECAM2M_DEVICE_ID 0x06 + +#define SENSOR_TYPR_ADDR_L 0x20 +#define SENSOR_TYPR_ADDR_H 0x21 + +#define BOARD_TYPR_ADDR 0x25 + +/* registers */ +#define VEYECAM_STREAMING_ON 0x001D +#define VEYECAM_MODE_STANDBY 0x00 +#define VEYECAM_MODE_STREAMING 0x01 + + +static DEFINE_MUTEX(veyecam2m_power_mutex); + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define veyecam2m_NAME "veyecam2m" + +#define veyecam2m_XVCLK_FREQ 24000000 + +static const s64 link_freq_menu_items[] = { + VEYECAM2M_DEFAULT_LINK_FREQ, +}; +static u32 clkout_enabled_index = 0; + +static const char * const veyecam2m_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define veyecam2m_NUM_SUPPLIES ARRAY_SIZE(veyecam2m_supply_names) + +struct veyecam2m_reg { + u16 address; + u8 val; +}; + +struct veyecam2m_reg_list { + u32 num_of_regs; + const struct veyecam2m_reg *regs; +}; + +/* Mode : resolution and related config&values */ +struct veyecam2m_mode { + u32 bus_fmt; + /* Frame width */ + u32 width; + /* Frame height */ + u32 height; + /* max framerate */ + struct v4l2_fract max_fps; + /* V-timing */ + //u32 vts_def; + /* Default register values */ + struct veyecam2m_reg_list reg_list; + u32 vc[PAD_MAX]; +}; + +static const struct veyecam2m_reg mode_1920_1080_regs[] = { + +}; + +/* Mode configs */ +static const struct veyecam2m_mode supported_modes[] = { + { + /* 1080P 30fps */ + .width = 1920, + .height = 1080, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .bus_fmt = VEYECAM2M_MEDIA_BUS_FMT, + //.vts_def = veyecam2m_VTS_30FPS_1080P, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), + .regs = mode_1920_1080_regs, + }, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +}; + +struct veyecam2m { + struct i2c_client *client; + struct clk *xvclk; //todo delete + struct gpio_desc *mipi_pwr_gpio;//todo delete + struct gpio_desc *reset_gpio; //todo delete + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[veyecam2m_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_mbus_framefmt fmt; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern;//todo delete + struct mutex mutex; + bool streaming; + bool power_on; + + bool initial_status; //Whether the isp has been initialized + const struct veyecam2m_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + u32 lane_data_num; +}; + +#define to_veyecam2m(sd) container_of(sd, struct veyecam2m, subdev) + +static int veyecam2m_write_reg(struct veyecam2m *veyecam2m, u16 reg, u8 val) +{ + int ret; + unsigned char data[3] = { reg >> 8, reg & 0xff, val}; + + struct i2c_client *client = v4l2_get_subdevdata(&veyecam2m->subdev); + ret = i2c_master_send(client, data, 3); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 3) { + ret = 0; + } else { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + } + + return ret; +} + +static int veyecam2m_read_reg(struct veyecam2m *veyecam2m, u16 reg, u8 *val) +{ + int ret; + unsigned char data_w[2] = { reg >> 8, reg & 0xff }; + struct i2c_client *client = v4l2_get_subdevdata(&veyecam2m->subdev); + + ret = i2c_master_send(client, data_w, 2); + /* + * A negative return code, or sending the wrong number of bytes, both + * count as an error. + */ + if (ret != 2) { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + return ret; + } + + ret = i2c_master_recv(client, val, 1); + /* + * The only return value indicating success is 1. Anything else, even + * a non-negative value, indicates something went wrong. + */ + if (ret == 1) { + ret = 0; + } else { + dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + } + + return ret; +} + +/* Write a list of registers */ +static int veyecam2m_write_regs(struct veyecam2m *veyecam2m, + const struct veyecam2m_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&veyecam2m->subdev); + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = veyecam2m_write_reg(veyecam2m, regs[i].address, regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error = %d\n", + regs[i].address, ret); + return ret; + } + } + return 0; +} + +static void veyecam2m_set_default_format(struct veyecam2m *veyecam2m) +{ + struct v4l2_mbus_framefmt *fmt; + VEYE_TRACE + fmt = &veyecam2m->fmt; + fmt->code = supported_modes[0].bus_fmt; + fmt->colorspace = V4L2_COLORSPACE_SRGB; +/* fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + fmt->colorspace, + fmt->ycbcr_enc); + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); +*/ + fmt->width = supported_modes[0].width; + fmt->height = supported_modes[0].height; + fmt->field = V4L2_FIELD_NONE; +} + + +static int veyecam2m_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct veyecam2m *veyecam2m = to_veyecam2m(sd); + const struct veyecam2m_mode *new_mode; + int ret = 0,mode,flag=0; + const struct veyecam2m_reg_list *reg_list; + VEYE_TRACE + mutex_lock(&veyecam2m->mutex); + + for(mode=0;modeformat.width==supported_modes[mode].width)&& + (fmt->format.height==supported_modes[mode].height)){ + new_mode = &supported_modes[mode]; + flag=1; + break; + } + } + if(flag==0){ + //debug_printk("veyecam2m_set_pad_format=======error"); + ret = -EINVAL; + goto error; + } + + fmt->format.code = new_mode->bus_fmt; + fmt->format.width = new_mode->width; + fmt->format.height = new_mode->height; + fmt->format.field = V4L2_FIELD_NONE; + veyecam2m->cur_mode = new_mode; + /* Apply default values of current mode */ + reg_list = &veyecam2m->cur_mode->reg_list; + ret = veyecam2m_write_regs(veyecam2m, reg_list->regs, reg_list->num_of_regs); + if (ret) { + //dev_err(&client->dev, "%s failed to set mode\n", __func__); + goto error; + } + +error: + mutex_unlock(&veyecam2m->mutex); + + return ret; +} +static int __veyecam2m_get_pad_format(struct veyecam2m *veyecam2m, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + //struct veyecam2m *veyecam2m = to_veyecam2m(sd); + const struct veyecam2m_mode *mode = veyecam2m->cur_mode; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + //debug_printk("__veyecam2m_get_pad_format====V4L2_SUBDEV_FORMAT_TRY==="); +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(&veyecam2m->subdev, cfg, fmt->pad); + //debug_printk("=V4L2_SUBDEV_FORMAT_TRY==ok"); +#else + // debug_printk("=V4L2_SUBDEV_FORMAT_TRY==err"); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + } + return 0; +} +static int veyecam2m_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct veyecam2m *veyecam2m = to_veyecam2m(sd); + int ret; + VEYE_TRACE + mutex_lock(&veyecam2m->mutex); + ret = __veyecam2m_get_pad_format(veyecam2m, cfg, fmt); + mutex_unlock(&veyecam2m->mutex); + return ret; +} + +static int veyecam2m_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + VEYE_TRACE + if (code->index != 0) + return -EINVAL; + code->code = VEYECAM2M_MEDIA_BUS_FMT;//MEDIA_BUS_FMT_UYVY8_2X8; + return 0; +} + +static int veyecam2m_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + VEYE_TRACE + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != VEYECAM2M_MEDIA_BUS_FMT/*MEDIA_BUS_FMT_UYVY8_2X8*/) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + return 0; +} + +static int veyecam2m_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct veyecam2m *veyecam2m = to_veyecam2m(sd); + const struct veyecam2m_mode *mode = veyecam2m->cur_mode; + VEYE_TRACE + mutex_lock(&veyecam2m->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&veyecam2m->mutex); + + return 0; +} + +/* +//#define veyecam2m_LANES 2 +//Please note that depending on the kernel version differences, this function may need to be either disabled or enabled. +static int veyecam2m_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *config) +{ + u32 val = 0; + u32 veyecam2m_lanes = 0; + struct veyecam2m *veyecam2m = to_veyecam2m(sd); + VEYE_TRACE + //debug_printk("veye data lan num %d",veyecam2m->lane_data_num); + veyecam2m_lanes = veyecam2m->lane_data_num; + val = 1 << (veyecam2m_lanes - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK ;//discontinues mode + + config->type = V4L2_MBUS_CSI2; + config->flags = val; + + return 0; +} +*/ + +static void veyecam2m_get_module_inf(struct veyecam2m *veyecam2m, + struct rkmodule_inf *inf) +{ + VEYE_TRACE + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, veyecam2m_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, veyecam2m->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, veyecam2m->len_name, sizeof(inf->base.lens)); +} + +static int veyecam2m_get_channel_info(struct veyecam2m *veyecam2m, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = veyecam2m->cur_mode->vc[ch_info->index]; + ch_info->width = veyecam2m->cur_mode->width; + ch_info->height = veyecam2m->cur_mode->height; + ch_info->bus_fmt = veyecam2m->cur_mode->bus_fmt; + return 0; +} + +static long veyecam2m_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct veyecam2m *veyecam2m = to_veyecam2m(sd); + long ret = 0; + struct rkmodule_channel_info *ch_info; + VEYE_TRACE + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + veyecam2m_get_module_inf(veyecam2m, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = veyecam2m_get_channel_info(veyecam2m, ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long veyecam2m_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_channel_info *ch_info; + long ret; + VEYE_TRACE + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + ret = veyecam2m_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) + ret = -EFAULT; + kfree(inf); + break; + /*case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = veyecam2m_ioctl(sd, cmd, cfg); + kfree(cfg); + break;*/ + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + ret = veyecam2m_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int veyecam2m_start_streaming(struct veyecam2m *veyecam2m) +{ + struct i2c_client *client = v4l2_get_subdevdata(&veyecam2m->subdev); + const struct veyecam2m_reg_list *reg_list; + int ret; + VEYE_TRACE + /* Apply default values of current mode */ + reg_list = &veyecam2m->cur_mode->reg_list; + ret = veyecam2m_write_regs(veyecam2m, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + return ret; + } + /* In case these controls are set before streaming */ + /*mutex_unlock(&veyecam2m->mutex); + ret = v4l2_ctrl_handler_setup(&veyecam2m->ctrl_handler); + mutex_lock(&veyecam2m->mutex); + if (ret){ + debug_printk("veyecam2m_start_streaming=====v4l2_ctrl_handler_setup failed %d",ret); + return ret; + }*/ +// debug_printk("veyecam2m_start_streaming=====ok"); + /* set stream on register */ + return veyecam2m_write_reg(veyecam2m, VEYECAM_STREAMING_ON, VEYECAM_MODE_STREAMING); +} + +static void veyecam2m_stop_streaming(struct veyecam2m *veyecam2m) +{ + struct i2c_client *client = v4l2_get_subdevdata(&veyecam2m->subdev); + int ret; + VEYE_TRACE + /* set stream off register */ + ret = veyecam2m_write_reg(veyecam2m, VEYECAM_STREAMING_ON, VEYECAM_MODE_STANDBY); + if (ret) + dev_err(&client->dev, "%s failed to set stream\n", __func__); +} + +static int veyecam2m_s_stream(struct v4l2_subdev *sd, int on) +{ + struct veyecam2m *veyecam2m = to_veyecam2m(sd); + //struct i2c_client *client = veyecam2m->client; + int ret = 0; + VEYE_TRACE + /* export gpio */ + if (!IS_ERR(veyecam2m->reset_gpio)) + gpiod_export(veyecam2m->reset_gpio, false); + if (!IS_ERR(veyecam2m->pwdn_gpio)) + gpiod_export(veyecam2m->pwdn_gpio, false); + + mutex_lock(&veyecam2m->mutex); + on = !!on; + if (on == veyecam2m->streaming){ + goto unlock_and_return; + } + if (on) { + /*ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + }*/ + + ret = veyecam2m_start_streaming(veyecam2m); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + //pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + veyecam2m_stop_streaming(veyecam2m); + //pm_runtime_put(&client->dev); + } + + veyecam2m->streaming = on; + +unlock_and_return: + mutex_unlock(&veyecam2m->mutex); + + return ret; +} + +static int veyecam2m_read_model(struct veyecam2m *veyecam2m) +{ + struct i2c_client *client = v4l2_get_subdevdata(&veyecam2m->subdev); + int ret; + u8 snr_l; + u8 snr_h; + u8 board_no; + ret = veyecam2m_read_reg(veyecam2m, SENSOR_TYPR_ADDR_L, &snr_l); + if (ret) { + dev_err(&client->dev, "probe failed \n"); + return -ENODEV; + } + ret = veyecam2m_read_reg(veyecam2m, SENSOR_TYPR_ADDR_H, &snr_h); + if (ret) { + dev_err(&client->dev, "probe failed \n"); + return -ENODEV; + } + ret = veyecam2m_read_reg(veyecam2m, BOARD_TYPR_ADDR, &board_no); + if (ret) { + dev_err(&client->dev, "probe failed \n"); + return -ENODEV; + } + if(snr_l == 0x03 && snr_h == 0x27){ + dev_err(&client->dev, "sensor is IMX327\n"); + } + else if(snr_l == 0x04 && snr_h == 0x62){ + dev_err(&client->dev, "sensor is IMX462\n"); + } + else if(snr_l == 0x03 && snr_h == 0x85){ + dev_err(&client->dev, "sensor is IMX385\n"); + } + if(board_no == 0x4C){ + dev_err(&client->dev, "board type is ONE board\n"); + }else{ + dev_err(&client->dev, "board type is TWO board\n"); + } + return 0; +} + +/* Verify chip ID */ +static int veyecam2m_identify_module(struct veyecam2m *veyecam2m) +{ + struct i2c_client *client = v4l2_get_subdevdata(&veyecam2m->subdev); + int ret; + //u32 val; + int err; + u8 device_id; + VEYE_TRACE + ret = veyecam2m_read_reg(veyecam2m, VEYECAM2M_MODEL_ID_ADDR, &device_id); + if (ret) { + dev_err(&client->dev, "probe failed \n"); + return -ENODEV; + } + if (device_id == VEYECAM2M_DEVICE_ID) + { + err = 0; + dev_err(&client->dev, " camera id is veyecam2m\n"); + } + else + { + err = -ENODEV; + dev_err(&client->dev, "%s: invalid sensor model id: %d\n", + __func__, device_id); + } + veyecam2m_read_model(veyecam2m); + return err; +} + +static int __veyecam2m_power_on(struct veyecam2m *veyecam2m); +static void __veyecam2m_power_off(struct veyecam2m *veyecam2m); +static int veyecam2m_s_power(struct v4l2_subdev *sd, int on) +{ + struct veyecam2m *veyecam2m = to_veyecam2m(sd); + struct i2c_client *client = veyecam2m->client; + struct device *dev = &veyecam2m->client->dev; + int ret = 0; + VEYE_TRACE + mutex_lock(&veyecam2m->mutex); + + /* If the power state is not modified - no work to do. */ + if (veyecam2m->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __veyecam2m_power_on(veyecam2m); + if(ret){ + dev_err(dev, "veyecam2m power on failed\n"); + } + veyecam2m->power_on = true; + + /* export gpio */ + if (!IS_ERR(veyecam2m->reset_gpio)) + gpiod_export(veyecam2m->reset_gpio, false); + if (!IS_ERR(veyecam2m->pwdn_gpio)) + gpiod_export(veyecam2m->pwdn_gpio, false); + } else { + pm_runtime_put(&client->dev); + __veyecam2m_power_off(veyecam2m); + veyecam2m->power_on = false; + /* unexport gpio */ + if (!IS_ERR(veyecam2m->reset_gpio)) + gpiod_unexport(veyecam2m->reset_gpio); + if (!IS_ERR(veyecam2m->pwdn_gpio)) + gpiod_unexport(veyecam2m->pwdn_gpio); + } + +unlock_and_return: + + mutex_unlock(&veyecam2m->mutex); + + return ret; +} + + +static int __veyecam2m_power_on(struct veyecam2m *veyecam2m) +{ + int ret; + struct device *dev = &veyecam2m->client->dev; + VEYE_TRACE + if (!IS_ERR_OR_NULL(veyecam2m->pins_default)) { + ret = pinctrl_select_state(veyecam2m->pinctrl, + veyecam2m->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + if (!IS_ERR(veyecam2m->reset_gpio)) + gpiod_set_value_cansleep(veyecam2m->reset_gpio, 0); + + + if (!IS_ERR(veyecam2m->pwdn_gpio)) + gpiod_set_value_cansleep(veyecam2m->pwdn_gpio, 0); + + msleep(4); + + if (clkout_enabled_index){ + ret = clk_prepare_enable(veyecam2m->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + } + + ret = regulator_bulk_enable(veyecam2m_NUM_SUPPLIES, veyecam2m->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(veyecam2m->mipi_pwr_gpio)) + gpiod_set_value_cansleep(veyecam2m->mipi_pwr_gpio, 1); + + usleep_range(500, 1000); + if (!IS_ERR(veyecam2m->reset_gpio)) + gpiod_set_value_cansleep(veyecam2m->reset_gpio, 1); + + usleep_range(500, 1000); + if (!IS_ERR(veyecam2m->pwdn_gpio)) + gpiod_set_value_cansleep(veyecam2m->pwdn_gpio, 1); + + usleep_range(500, 1000); + //do not output data when power on , because the mipi rx is not ready. + veyecam2m_stop_streaming(veyecam2m); + return 0; + +disable_clk: + if (clkout_enabled_index) + clk_disable_unprepare(veyecam2m->xvclk); + + return ret; +} + +static void __veyecam2m_power_off(struct veyecam2m *veyecam2m) +{ + int ret; + struct device *dev = &veyecam2m->client->dev; + veyecam2m->initial_status = false; + VEYE_TRACE + if (!IS_ERR(veyecam2m->reset_gpio)) + gpiod_set_value_cansleep(veyecam2m->reset_gpio, 1); + if (!IS_ERR(veyecam2m->pwdn_gpio)) + gpiod_set_value_cansleep(veyecam2m->pwdn_gpio,0); + if (!IS_ERR(veyecam2m->mipi_pwr_gpio)) + gpiod_set_value_cansleep(veyecam2m->mipi_pwr_gpio,1); + if (clkout_enabled_index) + clk_disable_unprepare(veyecam2m->xvclk); + if (!IS_ERR_OR_NULL(veyecam2m->pins_sleep)) { + ret = pinctrl_select_state(veyecam2m->pinctrl, + veyecam2m->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(veyecam2m_NUM_SUPPLIES, veyecam2m->supplies); + +} +static int veyecam2m_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct veyecam2m *veyecam2m = to_veyecam2m(sd); + VEYE_TRACE + if(veyecam2m->power_on == false) + return __veyecam2m_power_on(veyecam2m); + else + printk("veyecam2m is power on, nothing to do\n"); + + return 0; +} + +static int veyecam2m_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct veyecam2m *veyecam2m = to_veyecam2m(sd); + VEYE_TRACE + if(veyecam2m->power_on == true){ + __veyecam2m_power_off(veyecam2m); + veyecam2m->power_on = false; + } + + return 0; +} + +static int veyecam2m_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + VEYE_TRACE + if (fie->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fie->code != VEYECAM2M_MEDIA_BUS_FMT) + return -EINVAL; + + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int veyecam2m_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct veyecam2m *veyecam2m = to_veyecam2m(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + VEYE_TRACE + mutex_lock(&veyecam2m->mutex); + /* Initialize try_fmt */ + try_fmt->width = supported_modes[0].width; + try_fmt->height = supported_modes[0].height; + try_fmt->code = supported_modes[0].bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&veyecam2m->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static const struct dev_pm_ops veyecam2m_pm_ops = { + SET_RUNTIME_PM_OPS(veyecam2m_runtime_suspend, + veyecam2m_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops veyecam2m_internal_ops = { + .open = veyecam2m_open, +}; +#endif + +static const struct v4l2_subdev_core_ops veyecam2m_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, + .s_power = veyecam2m_s_power, + .ioctl = veyecam2m_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = veyecam2m_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops veyecam2m_video_ops = { + .s_stream = veyecam2m_s_stream, + .g_frame_interval = veyecam2m_g_frame_interval, + //.g_mbus_config = veyecam2m_g_mbus_config, //rm by wxiaowei in 20240408 +}; + +static const struct v4l2_subdev_pad_ops veyecam2m_pad_ops = { + .enum_mbus_code = veyecam2m_enum_mbus_code, + .enum_frame_size = veyecam2m_enum_frame_sizes, + .enum_frame_interval = veyecam2m_enum_frame_interval, + .get_fmt = veyecam2m_get_pad_format, + .set_fmt = veyecam2m_set_pad_format, +}; + +static const struct v4l2_subdev_ops veyecam2m_subdev_ops = { + .core = &veyecam2m_core_ops, + .video = &veyecam2m_video_ops, + .pad = &veyecam2m_pad_ops, +}; +#if 0 +static int veyecam2m_set_ctrl(struct v4l2_ctrl *ctrl) +{ + VEYE_TRACE + return 0; + #if 0 + struct veyecam2m *veyecam2m = container_of(ctrl->handler, + struct veyecam2m, ctrl_handler); + struct i2c_client *client = veyecam2m->client; + s64 max; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = veyecam2m->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(veyecam2m->exposure, + veyecam2m->exposure->minimum, max, + veyecam2m->exposure->step, + veyecam2m->exposure->default_value); + break; + default: + break; + } + if (pm_runtime_get(&client->dev) <= 0) + return 0; + + pm_runtime_put(&client->dev); + return 0; + #endif +} + +static const struct v4l2_ctrl_ops veyecam2m_ctrl_ops = { + .s_ctrl = veyecam2m_set_ctrl, +}; +#endif +static int veyecam2m_initialize_controls(struct veyecam2m *veyecam2m) +{ + const struct veyecam2m_mode *mode; + struct v4l2_ctrl_handler *handler; + //struct v4l2_ctrl *ctrl; + int ret; + VEYE_TRACE + handler = &veyecam2m->ctrl_handler; + mode = veyecam2m->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 2); + if (ret) + return ret; + handler->lock = &veyecam2m->mutex; + + veyecam2m->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, 0, link_freq_menu_items); + + if (veyecam2m->link_freq) + veyecam2m->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* By default, PIXEL_RATE is read only */ + veyecam2m->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, + VEYECAM2M_PIXEL_RATE, + VEYECAM2M_PIXEL_RATE, 1, + VEYECAM2M_PIXEL_RATE); + + if (handler->error) { + ret = handler->error; + dev_err(&veyecam2m->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + veyecam2m->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int veyecam2m_configure_regulators(struct veyecam2m *veyecam2m) +{ + unsigned int i; + VEYE_TRACE + for (i = 0; i < veyecam2m_NUM_SUPPLIES; i++) + veyecam2m->supplies[i].supply = veyecam2m_supply_names[i]; + + return devm_regulator_bulk_get(&veyecam2m->client->dev, + veyecam2m_NUM_SUPPLIES, + veyecam2m->supplies); +} + +static void free_gpio(struct veyecam2m *veyecam2m) +{ + if (!IS_ERR(veyecam2m->pwdn_gpio)) + gpio_free(desc_to_gpio(veyecam2m->pwdn_gpio)); + if (!IS_ERR(veyecam2m->reset_gpio)) + gpio_free(desc_to_gpio(veyecam2m->reset_gpio)); + if (!IS_ERR(veyecam2m->mipi_pwr_gpio)) + gpio_free(desc_to_gpio(veyecam2m->mipi_pwr_gpio)); +} + +static int veyecam2m_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct device_node *endpoint_node = NULL; + struct v4l2_fwnode_endpoint vep = {0}; + struct veyecam2m *veyecam2m; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + //u32 val = 0; + + + dev_info(dev, "veye camera driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + veyecam2m = devm_kzalloc(dev, sizeof(*veyecam2m), GFP_KERNEL); + if (!veyecam2m) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &veyecam2m->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &veyecam2m->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &veyecam2m->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &veyecam2m->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + veyecam2m->client = client; + veyecam2m->cur_mode = &supported_modes[0]; + + if (clkout_enabled_index){ + veyecam2m->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(veyecam2m->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + ret = clk_set_rate(veyecam2m->xvclk, veyecam2m_XVCLK_FREQ); + if (ret < 0) { + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); + return ret; + } + if (clk_get_rate(veyecam2m->xvclk) != veyecam2m_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + } + + veyecam2m->mipi_pwr_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(veyecam2m->mipi_pwr_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); + + veyecam2m->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(veyecam2m->reset_gpio)) { + dev_info(dev, "Failed to get reset-gpios, maybe no use\n"); + } + + veyecam2m->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(veyecam2m->pwdn_gpio)) { + dev_info(dev, "Failed to get pwdn-gpios, maybe no use\n"); + } + + ret = veyecam2m_configure_regulators(veyecam2m); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + veyecam2m->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(veyecam2m->pinctrl)) { + veyecam2m->pins_default = + pinctrl_lookup_state(veyecam2m->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(veyecam2m->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + veyecam2m->pins_sleep = + pinctrl_lookup_state(veyecam2m->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(veyecam2m->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + + endpoint_node = of_find_node_by_name(node,"endpoint"); + if(endpoint_node != NULL){ + //printk("veyecam2m get endpoint node success\n"); + ret=v4l2_fwnode_endpoint_parse(&endpoint_node->fwnode, &vep); + if(ret){ + dev_info(dev, "Failed to get veyecam2m endpoint data lanes, set a default value\n"); + veyecam2m->lane_data_num = 2; + }else{ + dev_info(dev, "Success to get veyecam2m endpoint data lanes, dts uses %d lanes\n", vep.bus.mipi_csi2.num_data_lanes); + veyecam2m->lane_data_num = vep.bus.mipi_csi2.num_data_lanes; + } + }else{ + dev_info(dev,"veyecam2m get endpoint node failed\n"); + return -ENOENT; + } + //dev_info(dev,"veyecam2m num data lanes is %d\n", veyecam2m->lane_data_num); + + mutex_init(&veyecam2m->mutex); + + sd = &veyecam2m->subdev; + v4l2_i2c_subdev_init(sd, client, &veyecam2m_subdev_ops); + ret = veyecam2m_initialize_controls(veyecam2m); + if (ret) + goto err_destroy_mutex; + + ret = __veyecam2m_power_on(veyecam2m); + if (ret) { + dev_err(dev, "__veyecam2m_power_on failed\n"); + goto err_power_off; + } + + msleep(100); + + ret = veyecam2m_identify_module(veyecam2m); + if (ret) + goto err_power_off; + + //clk discontinues mode + veyecam2m_write_reg(veyecam2m,0x000b, 0xfe); + veyecam2m_stop_streaming(veyecam2m); + + /* Initialize default format */ + veyecam2m_set_default_format(veyecam2m); +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &veyecam2m_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + veyecam2m->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &veyecam2m->pad); + if (ret < 0) + goto err_power_off; + +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(veyecam2m->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + veyecam2m->module_index, facing, + veyecam2m_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __veyecam2m_power_off(veyecam2m); + free_gpio(veyecam2m); +//err_free_handler: + v4l2_ctrl_handler_free(&veyecam2m->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&veyecam2m->mutex); + + return ret; +} + +static int veyecam2m_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct veyecam2m *veyecam2m = to_veyecam2m(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&veyecam2m->ctrl_handler); + mutex_destroy(&veyecam2m->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __veyecam2m_power_off(veyecam2m); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id veyecam2m_of_match[] = { + { .compatible = "veye,veyecam2m" }, + {}, +}; +MODULE_DEVICE_TABLE(of, veyecam2m_of_match); +#endif + +static const struct i2c_device_id veyecam2m_match_id[] = { + { "veye,veyecam2m", 0 }, + { }, +}; + +static struct i2c_driver veyecam2m_i2c_driver = { + .driver = { + .name = "veyecam2m", + .pm = &veyecam2m_pm_ops, + .of_match_table = of_match_ptr(veyecam2m_of_match), + }, + .probe = &veyecam2m_probe, + .remove = &veyecam2m_remove, + .id_table = veyecam2m_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&veyecam2m_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&veyecam2m_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_AUTHOR("xumm "); +MODULE_DESCRIPTION("veyecam2m sensor v4l2 driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 46f5cc40788a0..0c20dd854372d 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -49,6 +49,9 @@ source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/zydas/Kconfig" source "drivers/net/wireless/quantenna/Kconfig" source "drivers/net/wireless/rockchip_wlan/Kconfig" +source "drivers/net/wireless/rtl8812au/Kconfig" +source "drivers/net/wireless/rtl88x2bu/Kconfig" +source "drivers/net/wireless/rtl88x2eu/Kconfig" config PCMCIA_RAYCS tristate "Aviator/Raytheon 2.4GHz wireless support" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 5671364ae98db..e88039ae12f49 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -31,3 +31,6 @@ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o obj-$(CONFIG_VIRT_WIFI) += virt_wifi.o obj-$(CONFIG_WL_ROCKCHIP) += rockchip_wlan/ +obj-m += rtl8812au/ +obj-m += rtl88x2bu/ +obj-m += rtl88x2eu/ diff --git a/drivers/net/wireless/realtek/Kconfig b/drivers/net/wireless/realtek/Kconfig index 474843277fa14..d8dbd0596395b 100644 --- a/drivers/net/wireless/realtek/Kconfig +++ b/drivers/net/wireless/realtek/Kconfig @@ -14,7 +14,5 @@ if WLAN_VENDOR_REALTEK source "drivers/net/wireless/realtek/rtl818x/Kconfig" source "drivers/net/wireless/realtek/rtlwifi/Kconfig" -source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig" -source "drivers/net/wireless/realtek/rtw88/Kconfig" endif # WLAN_VENDOR_REALTEK diff --git a/drivers/net/wireless/realtek/Makefile b/drivers/net/wireless/realtek/Makefile index 888b5d594e797..d5f822166ec78 100644 --- a/drivers/net/wireless/realtek/Makefile +++ b/drivers/net/wireless/realtek/Makefile @@ -6,6 +6,3 @@ obj-$(CONFIG_RTL8180) += rtl818x/ obj-$(CONFIG_RTL8187) += rtl818x/ obj-$(CONFIG_RTLWIFI) += rtlwifi/ -obj-$(CONFIG_RTL8XXXU) += rtl8xxxu/ -obj-$(CONFIG_RTW88) += rtw88/ -