From 928de8842130513b3e6ca501f8b2ea2af6be180b Mon Sep 17 00:00:00 2001 From: Philippe Baetens Date: Thu, 19 Dec 2024 17:28:42 +0100 Subject: [PATCH] add ams-osram image sensors. --- arch/arm/boot/dts/overlays/Makefile | 6 + .../arm/boot/dts/overlays/mira016-overlay.dts | 13 + .../overlays/mira016_mono_color-overlay.dtsi | 118 + .../arm/boot/dts/overlays/mira050-overlay.dts | 13 + .../overlays/mira050_mono_color-overlay.dtsi | 118 + .../dts/overlays/mira050color-overlay.dts | 13 + .../arm/boot/dts/overlays/mira130-overlay.dts | 13 + .../overlays/mira130_mono_color-overlay.dtsi | 113 + .../arm/boot/dts/overlays/mira220-overlay.dts | 13 + .../overlays/mira220_mono_color-overlay.dtsi | 116 + .../dts/overlays/mira220color-overlay.dts | 13 + arch/arm/configs/bcm2709_defconfig | 6 + arch/arm/configs/bcm2711_defconfig | 6 + arch/arm/configs/bcmrpi_defconfig | 6 + arch/arm64/configs/bcm2711_defconfig | 8 +- arch/arm64/configs/bcm2712_defconfig | 6 + arch/arm64/configs/bcmrpi3_defconfig | 8 +- drivers/media/i2c/Kconfig | 79 + drivers/media/i2c/Makefile | 6 + drivers/media/i2c/mira016.c | 40 + drivers/media/i2c/mira016.inl | 6501 +++++++++++++++++ drivers/media/i2c/mira050.c | 40 + drivers/media/i2c/mira050.inl | 5513 ++++++++++++++ drivers/media/i2c/mira050color.c | 41 + drivers/media/i2c/mira130.c | 39 + drivers/media/i2c/mira130.inl | 2449 +++++++ drivers/media/i2c/mira220.c | 39 + drivers/media/i2c/mira220.inl | 3906 ++++++++++ drivers/media/i2c/mira220color.c | 39 + 29 files changed, 19279 insertions(+), 2 deletions(-) create mode 100644 arch/arm/boot/dts/overlays/mira016-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/mira016_mono_color-overlay.dtsi create mode 100644 arch/arm/boot/dts/overlays/mira050-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/mira050_mono_color-overlay.dtsi create mode 100644 arch/arm/boot/dts/overlays/mira050color-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/mira130-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/mira130_mono_color-overlay.dtsi create mode 100644 arch/arm/boot/dts/overlays/mira220-overlay.dts create mode 100644 arch/arm/boot/dts/overlays/mira220_mono_color-overlay.dtsi create mode 100644 arch/arm/boot/dts/overlays/mira220color-overlay.dts create mode 100644 drivers/media/i2c/mira016.c create mode 100644 drivers/media/i2c/mira016.inl create mode 100644 drivers/media/i2c/mira050.c create mode 100644 drivers/media/i2c/mira050.inl create mode 100644 drivers/media/i2c/mira050color.c create mode 100644 drivers/media/i2c/mira130.c create mode 100644 drivers/media/i2c/mira130.inl create mode 100644 drivers/media/i2c/mira220.c create mode 100644 drivers/media/i2c/mira220.inl create mode 100644 drivers/media/i2c/mira220color.c diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile index 367cbf437e1b0f..047dec65ad391b 100644 --- a/arch/arm/boot/dts/overlays/Makefile +++ b/arch/arm/boot/dts/overlays/Makefile @@ -129,6 +129,12 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ i2s-gpio28-31.dtbo \ i2s-master-dac.dtbo \ ilitek251x.dtbo \ + mira220.dtbo \ + mira220color.dtbo \ + mira050.dtbo \ + mira050color.dtbo \ + mira016.dtbo \ + mira130.dtbo \ imx219.dtbo \ imx258.dtbo \ imx290.dtbo \ diff --git a/arch/arm/boot/dts/overlays/mira016-overlay.dts b/arch/arm/boot/dts/overlays/mira016-overlay.dts new file mode 100644 index 00000000000000..05aa514e00d8b6 --- /dev/null +++ b/arch/arm/boot/dts/overlays/mira016-overlay.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for MIRA016 camera module on VC I2C bus +/dts-v1/; +/plugin/; + +#include +#include "mira016_mono_color-overlay.dtsi" + +&mira016 { + compatible = "ams,mira016"; + reg-name = "mira016"; +}; + diff --git a/arch/arm/boot/dts/overlays/mira016_mono_color-overlay.dtsi b/arch/arm/boot/dts/overlays/mira016_mono_color-overlay.dtsi new file mode 100644 index 00000000000000..0acf6a97671c1d --- /dev/null +++ b/arch/arm/boot/dts/overlays/mira016_mono_color-overlay.dtsi @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for MIRA016 camera module on VC I2C bus +/dts-v1/; +/plugin/; + +#include + +/{ + compatible = "brcm,bcm2835"; + + fragment@0 { + target = <&i2c0if>; + __overlay__ { + status = "okay"; + }; + }; + + clk_frag: fragment@1 { + target = <&cam1_clk>; + __overlay__ { + status = "okay"; + clock-frequency = <24000000>; + }; + }; + + fragment@2 { + target = <&i2c0mux>; + __overlay__ { + status = "okay"; + }; + }; + + fragment@5 { + target = <&cam1_reg>; + __overlay__ { + status = "okay"; + regulator-name = "mira016_vana"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + }; + + i2c_frag: fragment@100 { + target = <&i2c_csi_dsi>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + mira016: mira016@36 { + // compatible = "ams,mira016"; + // I2C addresses (7-bit address) + // The interface board uses 0x36 by default + // MIRA016CSP: 0x36; PMIC: 0x2D; INA3221: 0x40; LM2759: 0x53; + // dsPIC33EP32MC503: 0x0A + reg = <0x36>; + // reg-names = "mira016"; + status = "okay"; + + //clocks = <&mira016_clk>; + clocks = <&cam1_clk>; + clock-names = "xclk"; + + // TODO(jalv): Add here camera supplies based on the + // TPS65912 pmic. + VANA-supply = <&cam1_reg>; /* 2.8v */ + VDIG-supply = <&cam_dummy_reg>; /* 1.8v */ + VDDL-supply = <&cam_dummy_reg>; /* 1.2v */ + + // FIXME(jalv): Make sure orientation is correct + rotation = <0>; + orientation = <2>; + skip-reg-upload = <0>; + + port { + mira016_0: endpoint { + remote-endpoint = <&csi_ep>; + clock-lanes = <0>; + data-lanes = <1>; + // clock-noncontinuous; // mira016 reg seq use continuous clk + link-frequencies = + /bits/ 64 <456000000>; + }; + }; + + }; + }; + }; + + csi_frag: fragment@101 { + target = <&csi1>; + csi: __overlay__ { + status = "okay"; + brcm,media-controller; + + port { + csi_ep: endpoint { + remote-endpoint = <&mira016_0>; + clock-lanes = <0>; + data-lanes = <1>; + // clock-noncontinuous; // mira016 reg seq use continous clk + }; + }; + }; + }; + + __overrides__ { + rotation = <&mira016>,"rotation:0"; + orientation = <&mira016>,"orientation:0"; + skip-reg-upload = <&mira016>,"skip-reg-upload:0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&mira016>, "clocks:0=",<&cam0_clk>, + <&mira016>, "VANA-supply:0=",<&cam0_reg>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/mira050-overlay.dts b/arch/arm/boot/dts/overlays/mira050-overlay.dts new file mode 100644 index 00000000000000..68c1e8c468cc05 --- /dev/null +++ b/arch/arm/boot/dts/overlays/mira050-overlay.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for MIRA050 camera module on VC I2C bus +/dts-v1/; +/plugin/; + +#include +#include "mira050_mono_color-overlay.dtsi" + +&mira050 { + compatible = "ams,mira050"; + reg-name = "mira050"; +}; + diff --git a/arch/arm/boot/dts/overlays/mira050_mono_color-overlay.dtsi b/arch/arm/boot/dts/overlays/mira050_mono_color-overlay.dtsi new file mode 100644 index 00000000000000..00c3a5b7e7ea3f --- /dev/null +++ b/arch/arm/boot/dts/overlays/mira050_mono_color-overlay.dtsi @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for MIRA050 camera module on VC I2C bus +/dts-v1/; +/plugin/; + +#include + +/{ + compatible = "brcm,bcm2835"; + + fragment@0 { + target = <&i2c0if>; + __overlay__ { + status = "okay"; + }; + }; + + clk_frag: fragment@1 { + target = <&cam1_clk>; + __overlay__ { + status = "okay"; + clock-frequency = <24000000>; + }; + }; + + fragment@2 { + target = <&i2c0mux>; + __overlay__ { + status = "okay"; + }; + }; + + fragment@5 { + target = <&cam1_reg>; + __overlay__ { + status = "okay"; + regulator-name = "mira050_vana"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + }; + + i2c_frag: fragment@100 { + target = <&i2c_csi_dsi>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + mira050: mira050@36 { + // compatible = "ams,mira050"; + // I2C addresses (7-bit address) + // The interface board uses 0x36 by default + // MIRA050CSP: 0x36; PMIC: 0x2D; INA3221: 0x40; LM2759: 0x53; + // dsPIC33EP32MC503: 0x0A + reg = <0x36>; + // reg-names = "mira050"; + status = "okay"; + + //clocks = <&mira050_clk>; + clocks = <&cam1_clk>; + clock-names = "xclk"; + + // TODO(jalv): Add here camera supplies based on the + // TPS65912 pmic. + VANA-supply = <&cam1_reg>; /* 2.8v */ + VDIG-supply = <&cam_dummy_reg>; /* 1.8v */ + VDDL-supply = <&cam_dummy_reg>; /* 1.2v */ + + // FIXME(jalv): Make sure orientation is correct + rotation = <0>; + orientation = <2>; + skip-reg-upload = <0>; + + port { + mira050_0: endpoint { + remote-endpoint = <&csi_ep>; + clock-lanes = <0>; + data-lanes = <1>; + clock-noncontinuous; + link-frequencies = + /bits/ 64 <456000000>; + }; + }; + + }; + }; + }; + + csi_frag: fragment@101 { + target = <&csi1>; + csi: __overlay__ { + status = "okay"; + brcm,media-controller; + + port { + csi_ep: endpoint { + remote-endpoint = <&mira050_0>; + clock-lanes = <0>; + data-lanes = <1>; + clock-noncontinuous; + }; + }; + }; + }; + + __overrides__ { + rotation = <&mira050>,"rotation:0"; + orientation = <&mira050>,"orientation:0"; + skip-reg-upload = <&mira050>,"skip-reg-upload:0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&mira050>, "clocks:0=",<&cam0_clk>, + <&mira050>, "VANA-supply:0=",<&cam0_reg>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/mira050color-overlay.dts b/arch/arm/boot/dts/overlays/mira050color-overlay.dts new file mode 100644 index 00000000000000..4698c6c7fd4f82 --- /dev/null +++ b/arch/arm/boot/dts/overlays/mira050color-overlay.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for MIRA050 camera module on VC I2C bus +/dts-v1/; +/plugin/; + +#include +#include "mira050_mono_color-overlay.dtsi" + +&mira050 { + compatible = "ams,mira050color"; + reg-name = "mira050color"; +}; + diff --git a/arch/arm/boot/dts/overlays/mira130-overlay.dts b/arch/arm/boot/dts/overlays/mira130-overlay.dts new file mode 100644 index 00000000000000..facce459d9efdb --- /dev/null +++ b/arch/arm/boot/dts/overlays/mira130-overlay.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for MIRA130 camera module on VC I2C bus +/dts-v1/; +/plugin/; + +#include +#include "mira130_mono_color-overlay.dtsi" + +&mira130 { + compatible = "ams,mira130"; + reg-name = "mira130"; +}; + diff --git a/arch/arm/boot/dts/overlays/mira130_mono_color-overlay.dtsi b/arch/arm/boot/dts/overlays/mira130_mono_color-overlay.dtsi new file mode 100644 index 00000000000000..e8707bde277d5f --- /dev/null +++ b/arch/arm/boot/dts/overlays/mira130_mono_color-overlay.dtsi @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for MIRA130 camera module on VC I2C bus + +#include + +/{ + compatible = "brcm,bcm2835"; + + fragment@0 { + target = <&i2c0if>; + __overlay__ { + status = "okay"; + }; + }; + + clk_frag: fragment@1 { + target = <&cam1_clk>; + __overlay__ { + status = "okay"; + clock-frequency = <24000000>; + }; + }; + + fragment@2 { + target = <&i2c0mux>; + __overlay__ { + status = "okay"; + }; + }; + + fragment@5 { + target = <&cam1_reg>; + __overlay__ { + status = "okay"; + regulator-name = "mira130_vana"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + }; + + i2c_frag: fragment@100 { + target = <&i2c_csi_dsi>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + mira130: mira130@30 { + // compatible = "ams,mira130"; + // The newer Mira130 borads have 0x30 + reg = <0x30>; + // reg-names = "mira130"; + status = "okay"; + + //clocks = <&mira130_clk>; + clocks = <&cam1_clk>; + clock-names = "xclk"; + + // TODO(jalv): Add here camera supplies based on the + // TPS65912 pmic. + VANA-supply = <&cam1_reg>; /* 2.8v */ + VDIG-supply = <&cam_dummy_reg>; /* 1.8v */ + VDDL-supply = <&cam_dummy_reg>; /* 1.2v */ + + // FIXME(jalv): Make sure orientation is correct + rotation = <0>; + orientation = <2>; + skip-reg-upload = <0>; + + port { + mira130_0: endpoint { + remote-endpoint = <&csi_ep>; + clock-lanes = <0>; + data-lanes = <1 2>; + // clock-noncontinuous; // mira130 reg seq use continuois clk + link-frequencies = + /bits/ 64 <456000000>; + }; + }; + + }; + }; + }; + + csi_frag: fragment@101 { + target = <&csi1>; + csi: __overlay__ { + status = "okay"; + brcm,media-controller; + + port { + csi_ep: endpoint { + remote-endpoint = <&mira130_0>; + clock-lanes = <0>; + data-lanes = <1 2>; + // clock-noncontinuous; // mira130 reg seq use continous clk + }; + }; + }; + }; + + __overrides__ { + rotation = <&mira130>,"rotation:0"; + orientation = <&mira130>,"orientation:0"; + skip-reg-upload = <&mira130>,"skip-reg-upload:0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&mira130>, "clocks:0=",<&cam0_clk>, + <&mira130>, "VANA-supply:0=",<&cam0_reg>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/mira220-overlay.dts b/arch/arm/boot/dts/overlays/mira220-overlay.dts new file mode 100644 index 00000000000000..bb8f7977ef92ed --- /dev/null +++ b/arch/arm/boot/dts/overlays/mira220-overlay.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for MIRA220 camera module on VC I2C bus +/dts-v1/; +/plugin/; + +#include +#include "mira220_mono_color-overlay.dtsi" + +&mira220 { + compatible = "ams,mira220"; + reg-name = "mira220"; +}; + diff --git a/arch/arm/boot/dts/overlays/mira220_mono_color-overlay.dtsi b/arch/arm/boot/dts/overlays/mira220_mono_color-overlay.dtsi new file mode 100644 index 00000000000000..7f820eeaf00a13 --- /dev/null +++ b/arch/arm/boot/dts/overlays/mira220_mono_color-overlay.dtsi @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for MIRA220 camera module on VC I2C bus + +#include + +/{ + compatible = "brcm,bcm2835"; + + fragment@0 { + target = <&i2c0if>; + __overlay__ { + status = "okay"; + }; + }; + + clk_frag: fragment@1 { + target = <&cam1_clk>; + __overlay__ { + status = "okay"; + clock-frequency = <24000000>; + }; + }; + + fragment@2 { + target = <&i2c0mux>; + __overlay__ { + status = "okay"; + }; + }; + + fragment@5 { + target = <&cam1_reg>; + __overlay__ { + status = "okay"; + regulator-name = "mira220_vana"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + }; + + i2c_frag: fragment@100 { + target = <&i2c_csi_dsi>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + mira220: mira220@54 { + // compatible = "ams,mira220"; + // I2C addresses range from 0x54 to 0x57 (7-bit address) + // The interface board uses 0x54 by default + // MIRA220CSP: 0x54; PMIC: 0x2D; LM86: 0x4C; LM2759: 0x53; + // dsPIC33EP32MC503: 0x0A + reg = <0x54>; + // reg-names = "mira220"; + status = "okay"; + + //clocks = <&mira220_clk>; + clocks = <&cam1_clk>; + clock-names = "xclk"; + + // TODO(jalv): Add here camera supplies based on the + // TPS65912 pmic. + VANA-supply = <&cam1_reg>; /* 2.8v */ + VDIG-supply = <&cam_dummy_reg>; /* 1.8v */ + VDDL-supply = <&cam_dummy_reg>; /* 1.2v */ + + // FIXME(jalv): Make sure orientation is correct + rotation = <0>; + orientation = <2>; + skip-reg-upload = <0>; + + port { + mira220_0: endpoint { + remote-endpoint = <&csi_ep>; + clock-lanes = <0>; + data-lanes = <1 2>; + // clock-noncontinuous; // mira220 reg seq use continuois clk + link-frequencies = + /bits/ 64 <456000000>; + }; + }; + + }; + }; + }; + + csi_frag: fragment@101 { + target = <&csi1>; + csi: __overlay__ { + status = "okay"; + brcm,media-controller; + + port { + csi_ep: endpoint { + remote-endpoint = <&mira220_0>; + clock-lanes = <0>; + data-lanes = <1 2>; + // clock-noncontinuous; // mira220 reg seq use continous clk + }; + }; + }; + }; + + __overrides__ { + rotation = <&mira220>,"rotation:0"; + orientation = <&mira220>,"orientation:0"; + skip-reg-upload = <&mira220>,"skip-reg-upload:0"; + media-controller = <&csi>,"brcm,media-controller?"; + cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>, + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&mira220>, "clocks:0=",<&cam0_clk>, + <&mira220>, "VANA-supply:0=",<&cam0_reg>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/mira220color-overlay.dts b/arch/arm/boot/dts/overlays/mira220color-overlay.dts new file mode 100644 index 00000000000000..bc892b2a6e195f --- /dev/null +++ b/arch/arm/boot/dts/overlays/mira220color-overlay.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Definitions for MIRA220 camera module on VC I2C bus +/dts-v1/; +/plugin/; + +#include +#include "mira220_mono_color-overlay.dtsi" + +&mira220 { + compatible = "ams,mira220color"; + reg-name = "mira220color"; +}; + diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig index e9820d205607fe..5dfc7462e7ff2f 100644 --- a/arch/arm/configs/bcm2709_defconfig +++ b/arch/arm/configs/bcm2709_defconfig @@ -929,6 +929,12 @@ CONFIG_VIDEO_MUX=m CONFIG_VIDEO_BCM2835_UNICAM=m CONFIG_VIDEO_ARDUCAM_64MP=m CONFIG_VIDEO_ARDUCAM_PIVARIETY=m +CONFIG_VIDEO_MIRA220=m +CONFIG_VIDEO_MIRA220COLOR=m +CONFIG_VIDEO_MIRA050=m +CONFIG_VIDEO_MIRA050COLOR=m +CONFIG_VIDEO_MIRA016=m +CONFIG_VIDEO_MIRA130=m CONFIG_VIDEO_IMX219=m CONFIG_VIDEO_IMX258=m CONFIG_VIDEO_IMX290=m diff --git a/arch/arm/configs/bcm2711_defconfig b/arch/arm/configs/bcm2711_defconfig index 3d61292e2b8e71..41ad02d100718f 100644 --- a/arch/arm/configs/bcm2711_defconfig +++ b/arch/arm/configs/bcm2711_defconfig @@ -956,6 +956,12 @@ CONFIG_VIDEO_MUX=m CONFIG_VIDEO_BCM2835_UNICAM=m CONFIG_VIDEO_ARDUCAM_64MP=m CONFIG_VIDEO_ARDUCAM_PIVARIETY=m +CONFIG_VIDEO_MIRA220=m +CONFIG_VIDEO_MIRA220COLOR=m +CONFIG_VIDEO_MIRA050=m +CONFIG_VIDEO_MIRA050COLOR=m +CONFIG_VIDEO_MIRA016=m +CONFIG_VIDEO_MIRA130=m CONFIG_VIDEO_IMX219=m CONFIG_VIDEO_IMX258=m CONFIG_VIDEO_IMX290=m diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig index 0f49dac914b8d0..02d43358c6f5cd 100644 --- a/arch/arm/configs/bcmrpi_defconfig +++ b/arch/arm/configs/bcmrpi_defconfig @@ -923,6 +923,12 @@ CONFIG_VIDEO_MUX=m CONFIG_VIDEO_BCM2835_UNICAM=m CONFIG_VIDEO_ARDUCAM_64MP=m CONFIG_VIDEO_ARDUCAM_PIVARIETY=m +CONFIG_VIDEO_MIRA220=m +CONFIG_VIDEO_MIRA220COLOR=m +CONFIG_VIDEO_MIRA050=m +CONFIG_VIDEO_MIRA050COLOR=m +CONFIG_VIDEO_MIRA016=m +CONFIG_VIDEO_MIRA130=m CONFIG_VIDEO_IMX219=m CONFIG_VIDEO_IMX258=m CONFIG_VIDEO_IMX290=m diff --git a/arch/arm64/configs/bcm2711_defconfig b/arch/arm64/configs/bcm2711_defconfig index 221ae9410f2443..e6b30374ad5234 100644 --- a/arch/arm64/configs/bcm2711_defconfig +++ b/arch/arm64/configs/bcm2711_defconfig @@ -1,4 +1,4 @@ -CONFIG_LOCALVERSION="-v8" +CONFIG_LOCALVERSION="-v8+" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y @@ -996,6 +996,12 @@ CONFIG_VIDEO_VIMC=m CONFIG_VIDEO_VIVID=m CONFIG_VIDEO_ARDUCAM_64MP=m CONFIG_VIDEO_ARDUCAM_PIVARIETY=m +CONFIG_VIDEO_MIRA220=m +CONFIG_VIDEO_MIRA220COLOR=m +CONFIG_VIDEO_MIRA050=m +CONFIG_VIDEO_MIRA050COLOR=m +CONFIG_VIDEO_MIRA016=m +CONFIG_VIDEO_MIRA130=m CONFIG_VIDEO_IMX219=m CONFIG_VIDEO_IMX258=m CONFIG_VIDEO_IMX290=m diff --git a/arch/arm64/configs/bcm2712_defconfig b/arch/arm64/configs/bcm2712_defconfig index 3373df51f285ee..478f82fb2c15b6 100644 --- a/arch/arm64/configs/bcm2712_defconfig +++ b/arch/arm64/configs/bcm2712_defconfig @@ -998,6 +998,12 @@ CONFIG_VIDEO_VIMC=m CONFIG_VIDEO_VIVID=m CONFIG_VIDEO_ARDUCAM_64MP=m CONFIG_VIDEO_ARDUCAM_PIVARIETY=m +CONFIG_VIDEO_MIRA220=m +CONFIG_VIDEO_MIRA220COLOR=m +CONFIG_VIDEO_MIRA050=m +CONFIG_VIDEO_MIRA050COLOR=m +CONFIG_VIDEO_MIRA016=m +CONFIG_VIDEO_MIRA130=m CONFIG_VIDEO_IMX219=m CONFIG_VIDEO_IMX258=m CONFIG_VIDEO_IMX290=m diff --git a/arch/arm64/configs/bcmrpi3_defconfig b/arch/arm64/configs/bcmrpi3_defconfig index 07aff374afa0ab..1fd8a9ff7509fe 100644 --- a/arch/arm64/configs/bcmrpi3_defconfig +++ b/arch/arm64/configs/bcmrpi3_defconfig @@ -1,4 +1,4 @@ -CONFIG_LOCALVERSION="-v8" +CONFIG_LOCALVERSION="-v8+" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y @@ -899,6 +899,12 @@ CONFIG_VIDEO_VIMC=m CONFIG_VIDEO_VIVID=m CONFIG_VIDEO_ARDUCAM_64MP=m CONFIG_VIDEO_ARDUCAM_PIVARIETY=m +CONFIG_VIDEO_MIRA220=m +CONFIG_VIDEO_MIRA220COLOR=m +CONFIG_VIDEO_MIRA050=m +CONFIG_VIDEO_MIRA050COLOR=m +CONFIG_VIDEO_MIRA016=m +CONFIG_VIDEO_MIRA130=m CONFIG_VIDEO_IMX219=m CONFIG_VIDEO_IMX258=m CONFIG_VIDEO_IMX290=m diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 388906984e06ad..dfdef7beeb9755 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -99,6 +99,85 @@ config VIDEO_HI847 To compile this driver as a module, choose M here: the module will be called hi847. +config VIDEO_MIRA220 + tristate "ams MIRA220 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the ams + MIRA220 camera. + + To compile this driver as a module, choose M here: the + module will be called mira220. + +config VIDEO_MIRA220COLOR + tristate "ams MIRA220 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the ams + MIRA220 camera. + + To compile this driver as a module, choose M here: the + module will be called mira220. + +config VIDEO_MIRA050 + tristate "ams MIRA050 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the ams + MIRA050 camera. + + To compile this driver as a module, choose M here: the + module will be called mira050. + +config VIDEO_MIRA050COLOR + tristate "ams MIRA050 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the ams + MIRA050 camera. + + To compile this driver as a module, choose M here: the + module will be called mira050. + +config VIDEO_MIRA016 + tristate "ams MIRA016 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the ams + MIRA016 camera. + + To compile this driver as a module, choose M here: the + module will be called mira016. + +config VIDEO_MIRA130 + tristate "ams MIRA130 sensor support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the ams + MIRA130 camera. + + To compile this driver as a module, choose M here: the + module will be called mira130. + + config VIDEO_IMX208 tristate "Sony IMX208 sensor support" help diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 4159a650462466..f7887132d912fc 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -44,6 +44,12 @@ obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_HI846) += hi846.o obj-$(CONFIG_VIDEO_HI847) += hi847.o obj-$(CONFIG_VIDEO_I2C) += video-i2c.o +obj-$(CONFIG_VIDEO_MIRA220) += mira220.o +obj-$(CONFIG_VIDEO_MIRA220COLOR) += mira220color.o +obj-$(CONFIG_VIDEO_MIRA050) += mira050.o +obj-$(CONFIG_VIDEO_MIRA050COLOR) += mira050color.o +obj-$(CONFIG_VIDEO_MIRA016) += mira016.o +obj-$(CONFIG_VIDEO_MIRA130) += mira130.o obj-$(CONFIG_VIDEO_IMX208) += imx208.o obj-$(CONFIG_VIDEO_IMX214) += imx214.o obj-$(CONFIG_VIDEO_IMX219) += imx219.o diff --git a/drivers/media/i2c/mira016.c b/drivers/media/i2c/mira016.c new file mode 100644 index 00000000000000..7a4ef9ee368f51 --- /dev/null +++ b/drivers/media/i2c/mira016.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ams MIRA016 cameras. + * Copyright (C) 2022, ams-OSRAM + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + */ + +#include "mira016.inl" + +static const struct of_device_id mira016_dt_ids[] = { + { .compatible = "ams,mira016" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mira016_dt_ids); + +static const struct i2c_device_id mira016_ids[] = { + { "mira016", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mira016_ids); + +static struct i2c_driver mira016_i2c_driver = { + .driver = { + .name = "mira016", + .of_match_table = mira016_dt_ids, + .pm = &mira016_pm_ops, + }, + .probe = mira016_probe, + .remove = mira016_remove, + .id_table = mira016_ids, +}; + +module_i2c_driver(mira016_i2c_driver); + +MODULE_AUTHOR("Zhenyu Ye "); +MODULE_DESCRIPTION("ams MIRA016 sensor driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/i2c/mira016.inl b/drivers/media/i2c/mira016.inl new file mode 100644 index 00000000000000..eadadc01239cdc --- /dev/null +++ b/drivers/media/i2c/mira016.inl @@ -0,0 +1,6501 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ams MIRA016 cameras. + * Copyright (C) 2022, ams-OSRAM + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + */ + +#ifndef __MIRA016_INL__ +#define __MIRA016_INL__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Introduce new v4l2 control + */ +#include +#define AMS_CAMERA_CID_BASE (V4L2_CTRL_CLASS_CAMERA | 0x2000) +#define AMS_CAMERA_CID_MIRA_REG_W (AMS_CAMERA_CID_BASE + 0) +#define AMS_CAMERA_CID_MIRA_REG_R (AMS_CAMERA_CID_BASE + 1) + +/* Most significant Byte is flag, and most significant bit is unused. */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_FOR_READ 0b00000001 +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_USE_BANK 0b00000010 +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_BANK 0b00000100 +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_CONTEXT 0b00001000 +/* Use bit 5 to indicate special command, bit 1,2,3,4 for command. */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_CMD_SEL 0b00010000 +/* Special command for sleep. The other 3 Bytes (addr+val) is sleep values in us. */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_SLEEP_US 0b00010000 +/* Special command to enable power on (/off) when stream on (/off). */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_RESET_ON 0b00010010 +/* Special command to disable power on (/off) when stream on (/off). */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_RESET_OFF 0b00010100 +/* Special command to enable base register sequence upload, overwrite skip-reg-upload in dtoverlay */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_REG_UP_ON 0b00010110 +/* Special command to disable base register sequence upload, overwrite skip-reg-upload in dtoverlay */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_REG_UP_OFF 0b00011000 +/* Special command to manually power on */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_POWER_ON 0b00011010 +/* Special command to manually power off */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_POWER_OFF 0b00011100 +/* Special command to turn illumination trigger on */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_TRIG_ON 0b00011110 +/* Special command to turn illumination trigger off */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_TRIG_OFF 0b00010001 +/* Special command to set ILLUM_WIDTH. The other 3 Bytes (addr+val) is width value. */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_WIDTH 0b00010011 +/* Special command to set ILLUM_DELAY. The other 3 Bytes (addr+val) is width value. */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_DELAY 0b00010101 +/* Special command to enable ILLUM_WIDTH automatically tracking exposure time */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_EXP_T_ON 0b00010111 +/* Special command to disable ILLUM_WIDTH automatically tracking exposure time */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_EXP_T_OFF 0b00011001 +/* Special command to enable force_stream_ctrl */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_STREAM_CTRL_ON 0b00011011 +/* Special command to disable force_stream_ctrl */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_STREAM_CTRL_OFF 0b00011101 +/* + * Bit 6&7 of flag are combined to specify I2C dev (default is Mira). + * If bit 6&7 is 0b01, the reg_addr and reg_val are for a TBD I2C address. + * The TBD I2C address is default to MIRA016LED_I2C_ADDR. + * To change the TBD I2C address, set bit 6&7 to 0b10, + * then the reg_val will become TBD I2C address. + * The TBD I2C address is stored in mira016->tbd_client_i2c_addr. + */ +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_SEL 0b01100000 +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_MIRA 0b00000000 +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_TBD 0b00100000 +#define AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_SET_TBD 0b01000000 + +/* Pre-allocated i2c_client */ +#define MIRA016PMIC_I2C_ADDR 0x2D +#define MIRA016UC_I2C_ADDR 0x0A +#define MIRA016LED_I2C_ADDR 0x53 + +#define MIRA016_NATIVE_WIDTH 400U +#define MIRA016_NATIVE_HEIGHT 400U + +#define MIRA016_PIXEL_ARRAY_LEFT 0U +#define MIRA016_PIXEL_ARRAY_TOP 0U +#define MIRA016_PIXEL_ARRAY_WIDTH 400U +#define MIRA016_PIXEL_ARRAY_HEIGHT 400U + +/* Set analog gain min and max to 0 to avoid user changing it. */ +#define MIRA016_ANALOG_GAIN_MAX 1 +#define MIRA016_ANALOG_GAIN_MIN 0 +#define MIRA016_ANALOG_GAIN_STEP 1 +#define MIRA016_ANALOG_GAIN_DEFAULT MIRA016_ANALOG_GAIN_MIN + +#define MIRA016_BANK_SEL_REG 0xE000 +#define MIRA016_RW_CONTEXT_REG 0xE004 +#define MIRA016_CMD_REQ_1_REG 0x000A +#define MIRA016_CMD_HALT_BLOCK_REG 0x000C + +// Exposure time is indicated in us +#define MIRA016_EXP_TIME_L_REG 0x000E +#define MIRA016_EXP_TIME_S_REG 0x0012 + +// Target frame time is indicated in us +#define MIRA016_TARGET_FRAME_TIME_REG 0x0008 + +#define MIRA016_SUPPORTED_XCLK_FREQ 24000000 + +// Some timings +#define MIRA016_DATA_RATE 1500 // Mbit/s +#define MIRA016_SEQ_TIME_BASE 8 / MIRA016_DATA_RATE +#define MIRA016_LPS_CYCLE_TIME 1145 +#define MIRA016_GLOB_TIME 68 +#define MIRA016_ROW_LENGTH 1504 // 12b +#define MIRA016_LPS_DISABLED 0 +#define MIRA016_TROW_US MIRA016_ROW_LENGTH * 8 / MIRA016_DATA_RATE + +#define MIRA016_READOUT_TIME MIRA016_TROW_US * (11 + MIRA016_PIXEL_ARRAY_HEIGHT) + +// Default exposure is adjusted to 1 ms +#define MIRA016_LUT_DEL_008 0 +#define MIRA016_GRAN_TG 1500 * 50 / MIRA016_DATA_RATE +#define MIRA016_MIN_ROW_LENGTH MIRA016_ROW_LENGTH // 1042 for 8 bit +#define MIRA016_MIN_ROW_LENGTH_US (MIRA016_MIN_ROW_LENGTH * 8 / MIRA016_DATA_RATE) +#define MIRA016_EXPOSURE_MIN_US (int)(1 + (151 + MIRA016_LUT_DEL_008) * MIRA016_GRAN_TG * 8 / MIRA016_DATA_RATE) +#define MIRA016_EXPOSURE_MAX_US (1000000) +#define MIRA016_DEFAULT_EXPOSURE_US 1000 +// Default exposure for V4L2 is in row time + +// #define MIRA016_MIN_VBLANK 11 // for 10b or 8b, 360fps +#define MIRA016_MIN_VBLANK_200 4610 // 200 fps +#define MIRA016_MIN_VBLANK_360 2400 // 200 fps +#define MIRA016_MAX_VBLANK 1000000 + +#define MIRA016_DEFAULT_VBLANK_60 16000 // 200 fps + +// Power on function timing +#define MIRA016_XCLR_MIN_DELAY_US 150000 +#define MIRA016_XCLR_DELAY_RANGE_US 3000 + +// pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample +// 0.9Gb/s * 2 * 1 / 12 = 157286400 +// 1.5 Gbit/s * 2 * 1 / 12 = 250 000 000 +#define MIRA016_PIXEL_RATE (400000000) +/* Should match device tree link freq */ +#define MIRA016_DEFAULT_LINK_FREQ 456000000 + +/* Trick the libcamera with achievable fps via hblank */ + +/* Formular in libcamera to derive TARGET_FPS: + * TARGET_FPS=1/((1/MIRA016_PIXEL_RATE)*(WIDTH+HBLANK)*(HEIGHT+MIRA016_MIN_VBLANK)) + * Example with HBLANK=0 and MIRA016_MIN_VBLANK=12 + * TARGET_FPS=1/((1/157286400)*400*(400+12))=954 + * + * Inverse the above formula to derive HBLANK from TARGET_FPS: + * HBLANK=1/((1/MIRA016_PIXEL_RATE)*TARGET_FPS*(HEIGHT+MIRA016_MIN_VBLANK))-WIDTH + * Example with TARGET_FPS of 100 fps + * HBLANK=1/((1/157286400)*100*(400+12))-400=3418 + */ +// #define MIRA016_HBLANK_100FPS 3418 +// #define MIRA016_HBLANK_360FPS 1290 +// #define MIRA016_HBLANK_200FPS 2650 +#define MIRA016_HBLANK 0 + +// For test pattern with fixed data +#define MIRA016_TRAINING_WORD_REG 0x0060 +// For test pattern with 2D gradiant +#define MIRA016_DELTA_TEST_IMG_REG 0x0056 +// For setting test pattern type +#define MIRA016_TEST_PATTERN_REG 0x0062 +#define MIRA016_TEST_PATTERN_DISABLE 0x00 +#define MIRA016_TEST_PATTERN_FIXED_DATA 0x01 +#define MIRA016_TEST_PATTERN_2D_GRADIENT 0x02 + +/* Embedded metadata stream structure */ +#define MIRA016_EMBEDDED_LINE_WIDTH 0 +#define MIRA016_NUM_EMBEDDED_LINES 0 + +/* From Jetson driver */ + + +#define MIRA016_CURRENT_ACTIVE_CONTEXT 0x4002 + +#define MIRA016_GDIG_AMP 0x0024 +#define MIRA016_BIAS_RG_ADCGAIN 0x01F0 +#define MIRA016_BIAS_RG_MULT 0x01F3 +#define MIRA016_OFFSET_CLIPPING 0x0193 + +#define MIRA016_OTP_COMMAND 0x0066 +#define MIRA016_OTP_ADDR 0x0067 +#define MIRA016_OTP_START 0x0064 +#define MIRA016_OTP_BUSY 0x0065 +#define MIRA016_OTP_DOUT 0x006C + + +/* Illumination trigger */ +#define MIRA016_EN_TRIG_ILLUM 0x001C +#define MIRA016_ILLUM_WIDTH_REG 0x0019 +#define MIRA016_ILLUM_DELAY_REG 0x0016 +#define MIRA016_ILLUM_WIDTH_DEFAULT (MIRA016_DEFAULT_EXPOSURE_US * MIRA016_DATA_RATE / 8) +#define MIRA016_ILLUM_DELAY_DEFAULT (1 << 19) +#define MIRA016_ILLUM_ENABLE_DEFAULT 1 +#define MIRA016_ILLUM_SYNC_DEFAULT 1 + +enum pad_types +{ + IMAGE_PAD, + METADATA_PAD, + NUM_PADS +}; + +struct mira016_reg +{ + u16 address; + u8 val; +}; + +struct mira016_fine_gain_lut +{ + u8 gdig_amp; + u8 rg_adcgain; + u8 rg_mult; +}; + +struct mira016_reg_list +{ + unsigned int num_of_regs; + const struct mira016_reg *regs; +}; + +struct mira016_v4l2_reg +{ + u32 val; +}; + +/* Mode : resolution and related config&values */ +struct mira016_mode +{ + /* Frame width */ + unsigned int width; + /* Frame height */ + unsigned int height; + + /* Analog crop rectangle. */ + struct v4l2_rect crop; + + /* Default register values */ + struct mira016_reg_list reg_list_pre_soft_reset; + struct mira016_reg_list reg_list_post_soft_reset; + + u32 min_vblank; + u32 max_vblank; + u32 hblank; + u32 row_length; + /* Format code */ + u32 code; + + /* bit_depth needed for analog gain selection */ + u8 bit_depth; +}; + +// converted_Draco_i2c_configuration_sequence_hex_10bit_1x_360fps_Version3 +static const struct mira016_reg full_400_400_100fps_10b_1lane_reg_pre_soft_reset[] = { + // Sensor Operating Mode + {57344, 0}, + {57726, 1}, + {57344, 0}, + {57344, 0}, + {484, 0}, + {485, 19}, + {482, 23}, + {483, 168}, + {486, 0}, + {487, 202}, + {364, 1}, + {363, 1}, + {520, 1}, + {521, 240}, + {522, 3}, + {523, 77}, + {524, 2}, + {525, 16}, + {526, 3}, + {527, 1}, + {528, 0}, + {529, 19}, + {530, 0}, + {531, 3}, + {532, 3}, + {533, 239}, + {534, 3}, + {535, 243}, + {536, 3}, + {537, 244}, + {538, 0}, + {539, 1}, + {540, 3}, + {541, 248}, + {542, 0}, + {543, 2}, + {544, 1}, + {545, 242}, + {546, 3}, + {547, 27}, + {548, 0}, + {549, 33}, + {550, 3}, + {551, 240}, + {552, 3}, + {553, 241}, + {554, 3}, + {555, 242}, + {556, 3}, + {557, 245}, + {558, 3}, + {559, 246}, + {560, 0}, + {561, 193}, + {562, 0}, + {563, 2}, + {564, 1}, + {565, 242}, + {566, 3}, + {567, 107}, + {568, 3}, + {569, 255}, + {570, 3}, + {571, 49}, + {572, 1}, + {573, 240}, + {574, 3}, + {575, 135}, + {576, 0}, + {577, 10}, + {578, 0}, + {579, 11}, + {580, 1}, + {581, 249}, + {582, 3}, + {583, 13}, + {584, 0}, + {585, 7}, + {586, 3}, + {587, 239}, + {588, 3}, + {589, 243}, + {590, 3}, + {591, 244}, + {592, 3}, + {593, 0}, + {594, 0}, + {595, 7}, + {596, 0}, + {597, 12}, + {598, 1}, + {599, 241}, + {600, 3}, + {601, 67}, + {602, 1}, + {603, 248}, + {604, 3}, + {605, 16}, + {606, 0}, + {607, 7}, + {608, 3}, + {609, 240}, + {610, 3}, + {611, 241}, + {612, 3}, + {613, 242}, + {614, 3}, + {615, 245}, + {616, 3}, + {617, 246}, + {618, 3}, + {619, 0}, + {620, 2}, + {621, 135}, + {622, 0}, + {623, 1}, + {624, 3}, + {625, 255}, + {626, 3}, + {627, 0}, + {628, 3}, + {629, 255}, + {630, 2}, + {631, 135}, + {632, 3}, + {633, 2}, + {634, 3}, + {635, 15}, + {636, 3}, + {637, 247}, + {638, 0}, + {639, 22}, + {640, 0}, + {641, 51}, + {642, 0}, + {643, 4}, + {644, 0}, + {645, 17}, + {646, 3}, + {647, 9}, + {648, 0}, + {649, 2}, + {650, 0}, + {651, 32}, + {652, 0}, + {653, 181}, + {654, 0}, + {655, 229}, + {656, 0}, + {657, 18}, + {658, 0}, + {659, 181}, + {660, 0}, + {661, 229}, + {662, 0}, + {663, 16}, + {664, 0}, + {665, 2}, + {666, 0}, + {667, 32}, + {668, 0}, + {669, 181}, + {670, 0}, + {671, 229}, + {672, 0}, + {673, 18}, + {674, 0}, + {675, 181}, + {676, 0}, + {677, 229}, + {678, 0}, + {679, 0}, + {680, 0}, + {681, 18}, + {682, 0}, + {683, 18}, + {684, 0}, + {685, 32}, + {686, 0}, + {687, 181}, + {688, 0}, + {689, 229}, + {690, 0}, + {691, 0}, + {692, 0}, + {693, 18}, + {694, 0}, + {695, 18}, + {696, 0}, + {697, 32}, + {698, 0}, + {699, 71}, + {700, 0}, + {701, 39}, + {702, 0}, + {703, 181}, + {704, 0}, + {705, 229}, + {706, 0}, + {707, 0}, + {708, 0}, + {709, 4}, + {710, 0}, + {711, 67}, + {712, 0}, + {713, 1}, + {714, 3}, + {715, 2}, + {716, 0}, + {717, 8}, + {718, 3}, + {719, 255}, + {720, 2}, + {721, 135}, + {722, 3}, + {723, 199}, + {724, 3}, + {725, 247}, + {726, 0}, + {727, 119}, + {728, 0}, + {729, 23}, + {730, 0}, + {731, 8}, + {732, 3}, + {733, 255}, + {734, 0}, + {735, 56}, + {736, 0}, + {737, 23}, + {738, 0}, + {739, 8}, + {740, 3}, + {741, 255}, + {742, 3}, + {743, 255}, + {744, 3}, + {745, 255}, + {746, 3}, + {747, 255}, + {748, 3}, + {749, 255}, + {750, 3}, + {751, 255}, + {752, 3}, + {753, 255}, + {754, 3}, + {755, 255}, + {756, 3}, + {757, 255}, + {758, 3}, + {759, 255}, + {760, 3}, + {761, 255}, + {762, 3}, + {763, 255}, + {764, 3}, + {765, 255}, + {766, 3}, + {767, 255}, + {768, 3}, + {769, 255}, + {770, 3}, + {771, 255}, + {489, 0}, + {488, 25}, + {490, 53}, + {491, 55}, + {492, 100}, + {493, 107}, + {504, 15}, + {472, 1}, + {476, 1}, + {478, 1}, + {393, 1}, + {439, 1}, + {449, 7}, + {450, 246}, + {451, 255}, + {457, 7}, + {805, 0}, + {57689, 0}, + {826, 0}, + {440, 1}, + {442, 51}, + {446, 116}, + {447, 54}, + {448, 83}, + {239, 0}, + {806, 0}, + {240, 0}, + {241, 0}, + {807, 0}, + {242, 0}, + {113, 1}, + {436, 1}, + {437, 1}, + {497, 1}, + {500, 1}, + {501, 1}, + {788, 1}, + {789, 1}, + {790, 1}, + {519, 0}, + {16903, 2}, + {8711, 2}, + {57480, 1}, + {57485, 0}, + {57486, 48}, + {57487, 212}, + {57481, 86}, + {57482, 16}, + {57483, 31}, + {57484, 15}, + {57510, 0}, + {57513, 16}, + {57514, 0}, + {57517, 10}, + {57512, 48}, + {57511, 15}, + {57516, 16}, + {57515, 15}, + {8349, 0}, + {808, 0}, + {99, 1}, + {503, 15}, + {57571, 1}, + {57575, 3}, + {58171, 0}, + {58166, 0}, + {58167, 0}, + {58168, 0}, + {58169, 0}, + {233, 1}, + {234, 254}, + {777, 3}, + {778, 2}, + {779, 2}, + {780, 5}, + {782, 21}, + {781, 20}, + {783, 1}, + {784, 13}, + {464, 31}, + {465, 31}, + {461, 17}, + {22, 0}, + {23, 5}, + {232, 4}, + {498, 0}, + {362, 1}, + {57536, 0}, + {57537, 16}, + {57538, 0}, + {57539, 16}, + {360, 43}, + {8192, 0}, + {57344, 0}, + {57506, 0}, + {57467, 0}, + {57464, 0}, + {57465, 1}, + {8311, 0}, + {8310, 189}, + {206, 1}, + {112, 9}, + {365, 50}, + {374, 0}, + {57654, 0}, + {57542, 0}, + {57543, 0}, + {57544, 1}, + {57545, 0}, + {57546, 0}, + {57547, 1}, + {57548, 128}, + {57549, 128}, + {57534, 2}, + {57535, 2}, + {57540, 8}, + {57541, 8}, + {8309, 0}, + {8192, 0}, + {57344, 0}, + {30, 1}, + {57344, 0}, + {8318, 0}, + {57476, 0}, + {57477, 0}, + {57478, 1}, + {57479, 0}, + {8319, 0}, + {8320, 0}, + {8321, 3}, + {8322, 0}, + {8323, 2}, + {144, 0}, + {8343, 0}, + {57344, 0}, + {17, 3}, + {285, 1}, + {57344, 0}, + {18, 0}, + {19, 24}, + {346, 0}, + {347, 68}, + {348, 0}, + {349, 68}, + {350, 0}, + {351, 68}, + {354, 0}, + {355, 5}, + {356, 4}, + {357, 121}, + {358, 4}, + {359, 121}, + {57344, 0}, + {443, 180}, + {444, 172}, + {208, 0}, + {496, 7}, + {499, 2}, + {366, 125}, + {370, 0}, + {371, 0}, + {367, 254}, + {368, 0}, + {369, 125}, + {372, 0}, + {373, 0}, + {395, 2}, + {396, 14}, + {397, 2}, + {398, 86}, + {399, 6}, + {400, 14}, + {375, 120}, + {494, 27}, + {495, 70}, + {418, 0}, + {419, 1}, + {799, 0}, + {800, 10}, + {422, 0}, + {423, 152}, + {420, 3}, + {421, 167}, + {801, 3}, + {802, 176}, + {424, 4}, + {425, 62}, + {416, 0}, + {417, 200}, + {434, 0}, + {435, 226}, + {432, 0}, + {433, 219}, + {428, 0}, + {429, 230}, + {57344, 1}, + {57344, 1}, + {57380, 7}, + {57344, 0}, + {57344, 0}, + {92, 0}, + {93, 48}, + {57344, 0}, + {403, 4}, + {404, 22}, + {57344, 0}, + {57353, 1}, + {8495, 1}, + {8496, 1}, + {8497, 1}, + {8498, 1}, + {8499, 1}, + {8500, 1}, + {8501, 1}, + {57569, 1}, + {394, 1}, + {224, 1}, + {58158, 1}, + {58176, 1}, + {57344, 0}, + {57817, 1}, + {57344, 0}, + {57579, 16}, + {57344, 0}, + {57821, 1}, + {57344, 0}, + {57581, 14}, + {57344, 0}, + {57823, 1}, + {57344, 0}, + {57582, 4}, + {57344, 0}, + {58165, 1}, + {57344, 0}, + {58148, 42}, + {57344, 0}, + {57850, 1}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57348, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57344, 0}, + {57344, 0}, + {31, 0}, + {32, 0}, + {35, 0}, + {36, 1}, + {37, 144}, + {38, 0}, + {39, 14}, + {40, 0}, + {41, 1}, + {42, 144}, + {43, 0}, + {44, 14}, + {45, 0}, + {46, 0}, + {47, 0}, + {48, 0}, + {49, 0}, + {50, 0}, + {51, 0}, + {52, 0}, + {53, 0}, + {54, 0}, + {55, 0}, + {56, 0}, + {57, 0}, + {58, 0}, + {59, 0}, + {60, 0}, + {61, 0}, + {62, 0}, + {63, 0}, + {64, 0}, + {65, 0}, + {66, 0}, + {67, 0}, + {68, 0}, + {69, 0}, + {70, 0}, + {71, 0}, + {72, 0}, + {73, 0}, + {74, 0}, + {75, 0}, + {76, 0}, + {77, 0}, + {78, 0}, + {79, 0}, + {80, 0}, + {81, 0}, + {82, 0}, + {83, 0}, + {84, 0}, + {85, 0}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {57388, 0}, + {57389, 14}, + {57390, 1}, + {57391, 157}, + {57392, 0}, + {57381, 0}, + {57386, 0}, + {8233, 40}, + {52, 0}, + {53, 200}, + {57348, 0}, + {30, 0}, + {31, 2}, + {43, 0}, + // { 57348, 0}, + // { 14, 0}, + // { 15, 0}, + // { 16, 3}, + // { 17, 232}, + // { 18, 0}, + // { 19, 0}, + // { 20, 0}, + // { 21, 0}, + {57348, 0}, + {50, 4}, + {51, 68}, + {57348, 0}, + {7, 1}, + {8, 0}, + {9, 0}, + {10, 10}, + {11, 217}, + {57348, 0}, + {49, 0}, + {57348, 0}, + {38, 0}, + {57348, 0}, + // { 28, 0}, + // { 25, 0}, + // { 26, 7}, + // { 27, 83}, + // { 22, 8}, + // { 23, 0}, + // { 24, 0}, + {57348, 0}, + // { 29, 0}, + {57344, 0}, + {57344, 0}, + {58156, 1}, + {57344, 0}, + {58157, 1}, + {57344, 0}, + {57678, 0}, + {57344, 0}, + {58130, 127}, + {57344, 0}, + {58153, 3}, + {57344, 0}, + {58155, 0}, + {57344, 0}, + {58161, 15}, + {57344, 0}, + {58162, 0}, + {57344, 0}, + {58154, 0}, + {57344, 0}, + {57630, 1}, + {0, 0}, + {0, 0}, + {434, 0}, + {435, 234}, + + + {0x33D, 1} + }; + +static const struct mira016_reg full_400_400_100fps_10b_1lane_reg_post_soft_reset[] = { + {0xE000, 0}, + {0xE004, 0}, + // Below are manually added after reg seq txt + {0x0335, 1}, // iref sel + {0x0324, 43}, // iref val + + {0x1d9, 1}, // #vddana sel + {0x0EB, 15}, // #vddana val trim + + {0x1dd, 1}, // # vsspc sel + {0x1ed, 14}, // # vsspc val + + {0x1df, 1}, // #cp sel + {0x0ee, 4}, // #cp trim, + }; + +// converted_Draco_i2c_configuration_sequence_hex_12bit_1x_200fps_Version3 +static const struct mira016_reg full_400_400_100fps_12b_1lane_reg_pre_soft_reset[] = { + // Sensor Operating Mode + {57344, 0}, + {57726, 1}, + {57344, 0}, + {57344, 0}, + {484, 0}, + {485, 19}, + {482, 23}, + {483, 168}, + {486, 0}, + {487, 202}, + {364, 1}, + {363, 1}, + {520, 1}, + {521, 240}, + {522, 3}, + {523, 77}, + {524, 2}, + {525, 16}, + {526, 3}, + {527, 1}, + {528, 0}, + {529, 19}, + {530, 0}, + {531, 3}, + {532, 3}, + {533, 239}, + {534, 3}, + {535, 243}, + {536, 3}, + {537, 244}, + {538, 0}, + {539, 1}, + {540, 3}, + {541, 248}, + {542, 0}, + {543, 2}, + {544, 1}, + {545, 242}, + {546, 3}, + {547, 27}, + {548, 0}, + {549, 33}, + {550, 3}, + {551, 240}, + {552, 3}, + {553, 241}, + {554, 3}, + {555, 242}, + {556, 3}, + {557, 245}, + {558, 3}, + {559, 246}, + {560, 0}, + {561, 193}, + {562, 0}, + {563, 2}, + {564, 1}, + {565, 242}, + {566, 3}, + {567, 107}, + {568, 3}, + {569, 255}, + {570, 3}, + {571, 49}, + {572, 1}, + {573, 240}, + {574, 3}, + {575, 135}, + {576, 0}, + {577, 10}, + {578, 0}, + {579, 11}, + {580, 1}, + {581, 249}, + {582, 3}, + {583, 13}, + {584, 0}, + {585, 7}, + {586, 3}, + {587, 239}, + {588, 3}, + {589, 243}, + {590, 3}, + {591, 244}, + {592, 3}, + {593, 0}, + {594, 0}, + {595, 7}, + {596, 0}, + {597, 12}, + {598, 1}, + {599, 241}, + {600, 3}, + {601, 67}, + {602, 1}, + {603, 248}, + {604, 3}, + {605, 16}, + {606, 0}, + {607, 7}, + {608, 3}, + {609, 240}, + {610, 3}, + {611, 241}, + {612, 3}, + {613, 242}, + {614, 3}, + {615, 245}, + {616, 3}, + {617, 246}, + {618, 3}, + {619, 0}, + {620, 2}, + {621, 135}, + {622, 0}, + {623, 1}, + {624, 3}, + {625, 255}, + {626, 3}, + {627, 0}, + {628, 3}, + {629, 255}, + {630, 2}, + {631, 135}, + {632, 3}, + {633, 2}, + {634, 3}, + {635, 15}, + {636, 3}, + {637, 247}, + {638, 0}, + {639, 22}, + {640, 0}, + {641, 51}, + {642, 0}, + {643, 4}, + {644, 0}, + {645, 17}, + {646, 3}, + {647, 9}, + {648, 0}, + {649, 2}, + {650, 0}, + {651, 32}, + {652, 0}, + {653, 181}, + {654, 0}, + {655, 229}, + {656, 0}, + {657, 18}, + {658, 0}, + {659, 181}, + {660, 0}, + {661, 229}, + {662, 0}, + {663, 16}, + {664, 0}, + {665, 2}, + {666, 0}, + {667, 32}, + {668, 0}, + {669, 181}, + {670, 0}, + {671, 229}, + {672, 0}, + {673, 18}, + {674, 0}, + {675, 181}, + {676, 0}, + {677, 229}, + {678, 0}, + {679, 0}, + {680, 0}, + {681, 18}, + {682, 0}, + {683, 18}, + {684, 0}, + {685, 32}, + {686, 0}, + {687, 181}, + {688, 0}, + {689, 229}, + {690, 0}, + {691, 0}, + {692, 0}, + {693, 18}, + {694, 0}, + {695, 18}, + {696, 0}, + {697, 32}, + {698, 0}, + {699, 71}, + {700, 0}, + {701, 39}, + {702, 0}, + {703, 181}, + {704, 0}, + {705, 229}, + {706, 0}, + {707, 0}, + {708, 0}, + {709, 4}, + {710, 0}, + {711, 67}, + {712, 0}, + {713, 1}, + {714, 3}, + {715, 2}, + {716, 0}, + {717, 8}, + {718, 3}, + {719, 255}, + {720, 2}, + {721, 135}, + {722, 3}, + {723, 199}, + {724, 3}, + {725, 247}, + {726, 0}, + {727, 119}, + {728, 0}, + {729, 23}, + {730, 0}, + {731, 8}, + {732, 3}, + {733, 255}, + {734, 0}, + {735, 56}, + {736, 0}, + {737, 23}, + {738, 0}, + {739, 8}, + {740, 3}, + {741, 255}, + {742, 3}, + {743, 255}, + {744, 3}, + {745, 255}, + {746, 3}, + {747, 255}, + {748, 3}, + {749, 255}, + {750, 3}, + {751, 255}, + {752, 3}, + {753, 255}, + {754, 3}, + {755, 255}, + {756, 3}, + {757, 255}, + {758, 3}, + {759, 255}, + {760, 3}, + {761, 255}, + {762, 3}, + {763, 255}, + {764, 3}, + {765, 255}, + {766, 3}, + {767, 255}, + {768, 3}, + {769, 255}, + {770, 3}, + {771, 255}, + {489, 0}, + {488, 25}, + {490, 53}, + {491, 55}, + {492, 100}, + {493, 107}, + {504, 15}, + {472, 1}, + {476, 1}, + {478, 1}, + {393, 1}, + {439, 1}, + {449, 7}, + {450, 246}, + {451, 255}, + {457, 7}, + {805, 0}, + {57689, 0}, + {826, 0}, + {440, 1}, + {442, 51}, + {446, 116}, + {447, 54}, + {448, 83}, + {239, 0}, + {806, 0}, + {240, 0}, + {241, 0}, + {807, 0}, + {242, 0}, + {113, 1}, + {436, 1}, + {437, 1}, + {497, 1}, + {500, 1}, + {501, 1}, + {788, 1}, + {789, 1}, + {790, 1}, + {519, 0}, + {16903, 2}, + {8711, 2}, + {57480, 1}, + {57485, 0}, + {57486, 48}, + {57487, 212}, + {57481, 86}, + {57482, 16}, + {57483, 31}, + {57484, 15}, + {57510, 0}, + {57513, 16}, + {57514, 0}, + {57517, 10}, + {57512, 48}, + {57511, 15}, + {57516, 16}, + {57515, 15}, + {8349, 0}, + {808, 0}, + {99, 1}, + {503, 15}, + {57571, 1}, + {57575, 3}, + {58171, 0}, + {58166, 0}, + {58167, 0}, + {58168, 0}, + {58169, 0}, + {233, 1}, + {234, 254}, + {777, 3}, + {778, 2}, + {779, 2}, + {780, 5}, + {782, 21}, + {781, 20}, + {783, 1}, + {784, 13}, + {464, 31}, + {465, 31}, + {461, 17}, + {22, 0}, + {23, 5}, + {232, 4}, + {498, 0}, + {362, 2}, + {57536, 0}, + {57537, 32}, + {57538, 0}, + {57539, 32}, + {360, 44}, + {8192, 0}, + {57344, 0}, + {57506, 0}, + {57467, 0}, + {57464, 0}, + {57465, 1}, + {8311, 0}, + {8310, 189}, + {206, 1}, + {112, 9}, + {365, 50}, + {374, 0}, + {57654, 0}, + {57542, 0}, + {57543, 0}, + {57544, 1}, + {57545, 0}, + {57546, 0}, + {57547, 1}, + {57548, 128}, + {57549, 128}, + {57534, 2}, + {57535, 2}, + {57540, 8}, + {57541, 8}, + {8309, 0}, + {8192, 0}, + {57344, 0}, + {30, 1}, + {57344, 0}, + {8318, 0}, + {57476, 0}, + {57477, 0}, + {57478, 1}, + {57479, 0}, + {8319, 0}, + {8320, 0}, + {8321, 3}, + {8322, 0}, + {8323, 2}, + {144, 0}, + {8343, 0}, + {57344, 0}, + {17, 3}, + {285, 1}, + {57344, 0}, + {18, 0}, + {19, 24}, + {346, 0}, + {347, 68}, + {348, 0}, + {349, 68}, + {350, 0}, + {351, 68}, + {354, 0}, + {355, 5}, + {356, 4}, + {357, 121}, + {358, 4}, + {359, 121}, + {57344, 0}, + {443, 180}, + {444, 172}, + {208, 0}, + {496, 7}, + {499, 1}, + {366, 253}, + {370, 0}, + {371, 0}, + {367, 255}, + {368, 255}, + {369, 253}, + {372, 0}, + {373, 0}, + {395, 4}, + {396, 14}, + {397, 2}, + {398, 86}, + {399, 12}, + {400, 14}, + {375, 220}, + {494, 27}, + {495, 70}, + {418, 0}, + {419, 1}, + {799, 0}, + {800, 10}, + {422, 0}, + {423, 152}, + {420, 5}, + {421, 167}, + {801, 5}, + {802, 176}, + {424, 6}, + {425, 62}, + {416, 0}, + {417, 243}, + {434, 1}, + {435, 13}, + {432, 1}, + {433, 6}, + {428, 1}, + {429, 17}, + {57344, 1}, + {57344, 1}, + {57380, 15}, + {57344, 0}, + {57344, 0}, + {92, 0}, + {93, 96}, + {57344, 0}, + {403, 8}, + {404, 3}, + {57344, 0}, + {57353, 1}, + {8495, 1}, + {8496, 1}, + {8497, 1}, + {8498, 1}, + {8499, 1}, + {8500, 1}, + {8501, 1}, + {57569, 1}, + {394, 1}, + {224, 1}, + {58158, 1}, + {58176, 1}, + {57344, 0}, + {57817, 1}, + {57344, 0}, + {57579, 16}, + {57344, 0}, + {57821, 1}, + {57344, 0}, + {57581, 14}, + {57344, 0}, + {57823, 1}, + {57344, 0}, + {57582, 4}, + {57344, 0}, + {58165, 1}, + {57344, 0}, + {58148, 42}, + {57344, 0}, + {57850, 1}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57348, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57344, 0}, + {57344, 0}, + {31, 0}, + {32, 0}, + {35, 0}, + {36, 1}, + {37, 144}, + {38, 0}, + {39, 14}, + {40, 0}, + {41, 1}, + {42, 144}, + {43, 0}, + {44, 14}, + {45, 0}, + {46, 0}, + {47, 0}, + {48, 0}, + {49, 0}, + {50, 0}, + {51, 0}, + {52, 0}, + {53, 0}, + {54, 0}, + {55, 0}, + {56, 0}, + {57, 0}, + {58, 0}, + {59, 0}, + {60, 0}, + {61, 0}, + {62, 0}, + {63, 0}, + {64, 0}, + {65, 0}, + {66, 0}, + {67, 0}, + {68, 0}, + {69, 0}, + {70, 0}, + {71, 0}, + {72, 0}, + {73, 0}, + {74, 0}, + {75, 0}, + {76, 0}, + {77, 0}, + {78, 0}, + {79, 0}, + {80, 0}, + {81, 0}, + {82, 0}, + {83, 0}, + {84, 0}, + {85, 0}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {57388, 0}, + {57389, 14}, + {57390, 1}, + {57391, 157}, + {57392, 0}, + {57381, 0}, + {57386, 0}, + {8233, 40}, + {52, 0}, + {53, 200}, + {57348, 0}, + {30, 0}, + {31, 2}, + {43, 0}, + {57348, 0}, + {14, 0}, + {15, 0}, + {16, 3}, + {17, 232}, + {18, 0}, + {19, 0}, + {20, 0}, + {21, 0}, + {57348, 0}, + {50, 5}, + {51, 224}, + {57348, 0}, + {7, 1}, + {8, 0}, + {9, 0}, + {10, 19}, + {11, 136}, + {57348, 0}, + {49, 0}, + {57348, 0}, + {38, 0}, + {57348, 0}, + // { 28, 0}, + // { 25, 0}, + // { 26, 7}, + // { 27, 83}, + // { 22, 8}, + // { 23, 0}, + // { 24, 0}, + // { 57348, 0}, + // { 29, 0}, + {57344, 0}, + {57344, 0}, + {58156, 1}, + {57344, 0}, + {58157, 1}, + {57344, 0}, + {57678, 0}, + {57344, 0}, + {58130, 127}, + {57344, 0}, + {58153, 3}, + {57344, 0}, + {58155, 0}, + {57344, 0}, + {58161, 15}, + {57344, 0}, + {58162, 0}, + {57344, 0}, + {58154, 0}, + {57344, 0}, + {57630, 1}, + {0, 0}, + {0, 0}, + {434, 1}, + {435, 21}, + + + + {0xE000, 0}, + {0xE004, 0}, + {0x33D, 1}, +}; + +static const struct mira016_reg full_400_400_100fps_12b_1lane_reg_post_soft_reset[] = { + {0xE000, 0}, + {0xE004, 0}, + // Below are manually added after reg seq txt + {0x0335, 1}, // iref sel + {0x0324, 43}, // iref val + + {0x1d9, 1}, // #vddana sel + {0x0EB, 15}, // #vddana val trim + + {0x1dd, 1}, // # vsspc sel + {0x1ed, 14}, // # vsspc val + + {0x1df, 1}, // #cp sel + {0x0ee, 4}, // #cp trim, +}; + +static const struct mira016_reg partial_analog_gain_x1_12bit[] = { + {57344, 0}, + {443, 180}, + {444, 172}, + {208, 0}, + {496, 7}, + {499, 1}, + {366, 253}, + {370, 0}, + {371, 0}, + {367, 255}, + {368, 255}, + {369, 253}, + {372, 0}, + {373, 0}, + {395, 4}, + {396, 14}, + {397, 2}, + {398, 86}, + {399, 12}, + {400, 14}, + {375, 220}, + {494, 27}, + {495, 70}, + {418, 0}, + {419, 1}, + {799, 0}, + {800, 10}, + {422, 0}, + {423, 152}, + {420, 5}, + {421, 167}, + {801, 5}, + {802, 176}, + {424, 6}, + {425, 62}, + {416, 0}, + {417, 243}, + {434, 1}, + {435, 13}, + {432, 1}, + {433, 6}, + {428, 1}, + {429, 17}, + {57344, 1}, + {57344, 1}, + {57380, 15}, + {57344, 0}, + {57344, 0}, + {92, 0}, + {93, 96}, + {57344, 0}, + {403, 8}, + {404, 3}, + {57344, 0}, + {57353, 1}, + {8495, 1}, + {8496, 1}, + {8497, 1}, + {8498, 1}, + {8499, 1}, + {8500, 1}, + {8501, 1}, + {57569, 1}, + {394, 1}, + {224, 1}, + {58158, 1}, + {58176, 1}, + {57344, 0}, + {57817, 1}, + {57344, 0}, + {57579, 16}, + {57344, 0}, + {57821, 1}, + {57344, 0}, + {57581, 14}, + {57344, 0}, + {57823, 1}, + {57344, 0}, + {57582, 4}, + {57344, 0}, + {58165, 1}, + {57344, 0}, + {58148, 42}, + {57344, 0}, + {57850, 1}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57348, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57344, 0}, + {57344, 0}, + {31, 0}, + {32, 0}, + {35, 0}, + {36, 1}, + {37, 144}, + {38, 0}, + {39, 14}, + {40, 0}, + {41, 1}, + {42, 144}, + {43, 0}, + {44, 14}, + {45, 0}, + {46, 0}, + {47, 0}, + {48, 0}, + {49, 0}, + {50, 0}, + {51, 0}, + {52, 0}, + {53, 0}, + {54, 0}, + {55, 0}, + {56, 0}, + {57, 0}, + {58, 0}, + {59, 0}, + {60, 0}, + {61, 0}, + {62, 0}, + {63, 0}, + {64, 0}, + {65, 0}, + {66, 0}, + {67, 0}, + {68, 0}, + {69, 0}, + {70, 0}, + {71, 0}, + {72, 0}, + {73, 0}, + {74, 0}, + {75, 0}, + {76, 0}, + {77, 0}, + {78, 0}, + {79, 0}, + {80, 0}, + {81, 0}, + {82, 0}, + {83, 0}, + {84, 0}, + {85, 0}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {57388, 0}, + {57389, 14}, + {57390, 1}, + {57391, 157}, + {57392, 0}, + {57381, 0}, + {57386, 0}, + {8233, 40}, + {52, 0}, + {53, 200}, + {57348, 0}, + {30, 0}, + {31, 2}, + {43, 0}, + // { 57348, 0}, + // { 14, 0}, + // { 15, 0}, + // { 16, 3}, + // { 17, 232}, + // { 18, 0}, + // { 19, 0}, + // { 20, 0}, + // { 21, 0}, + // frame time + {57348, 0}, + {50, 5}, + {51, 224}, + {57348, 0}, + {7, 1}, + // { 8, 0}, + // { 9, 0}, + // { 10, 19}, + // { 11, 136}, + {57348, 0}, + {49, 0}, + {57348, 0}, + {38, 0}, + {57348, 0}, + // { 28, 0}, + // { 25, 0}, + // { 26, 7}, + // { 27, 83}, + // { 22, 8}, + // { 23, 0}, + // { 24, 0}, + {57348, 0}, + // { 29, 0}, + {57344, 0}, + {57344, 0}, + {58156, 1}, + {57344, 0}, + {58157, 1}, + {57344, 0}, + {57678, 0}, + {57344, 0}, + {58130, 127}, + {57344, 0}, + {58153, 3}, + {57344, 0}, + {58155, 0}, + {57344, 0}, + {58161, 15}, + {57344, 0}, + {58162, 0}, + {57344, 0}, + {58154, 0}, + {57344, 0}, + {57630, 1}, + {0, 0}, + {0, 0}, + {434, 1}, + {435, 21}, + +}; + +static const struct mira016_reg partial_analog_gain_x2_12bit[] = { + {57344, 0}, + {443, 156}, + {444, 148}, + {208, 0}, + {496, 7}, + {499, 0}, + {366, 255}, + {370, 254}, + {371, 0}, + {367, 255}, + {368, 255}, + {369, 255}, + {372, 254}, + {373, 0}, + {395, 8}, + {396, 14}, + {397, 2}, + {398, 86}, + {399, 16}, + {400, 14}, + {375, 220}, + {494, 27}, + {495, 70}, + {418, 0}, + {419, 1}, + {799, 0}, + {800, 10}, + {422, 0}, + {423, 152}, + {420, 7}, + {421, 167}, + {801, 7}, + {802, 176}, + {424, 8}, + {425, 62}, + {416, 1}, + {417, 72}, + {434, 1}, + {435, 98}, + {432, 1}, + {433, 91}, + {428, 1}, + {429, 102}, + {57344, 1}, + {57344, 1}, + {57380, 15}, + {57344, 0}, + {57344, 0}, + {92, 0}, + {93, 96}, + {57344, 0}, + {403, 15}, + {404, 234}, + {57344, 0}, + {57353, 1}, + {8495, 1}, + {8496, 1}, + {8497, 1}, + {8498, 1}, + {8499, 1}, + {8500, 1}, + {8501, 1}, + {57569, 1}, + {394, 1}, + {224, 1}, + {58158, 1}, + {58176, 1}, + {57344, 0}, + {57817, 1}, + {57344, 0}, + {57579, 16}, + {57344, 0}, + {57821, 1}, + {57344, 0}, + {57581, 14}, + {57344, 0}, + {57823, 1}, + {57344, 0}, + {57582, 4}, + {57344, 0}, + {58165, 1}, + {57344, 0}, + {58148, 42}, + {57344, 0}, + {57850, 1}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57348, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57344, 0}, + {57344, 0}, + {31, 0}, + {32, 0}, + {35, 0}, + {36, 1}, + {37, 144}, + {38, 0}, + {39, 14}, + {40, 0}, + {41, 1}, + {42, 144}, + {43, 0}, + {44, 14}, + {45, 0}, + {46, 0}, + {47, 0}, + {48, 0}, + {49, 0}, + {50, 0}, + {51, 0}, + {52, 0}, + {53, 0}, + {54, 0}, + {55, 0}, + {56, 0}, + {57, 0}, + {58, 0}, + {59, 0}, + {60, 0}, + {61, 0}, + {62, 0}, + {63, 0}, + {64, 0}, + {65, 0}, + {66, 0}, + {67, 0}, + {68, 0}, + {69, 0}, + {70, 0}, + {71, 0}, + {72, 0}, + {73, 0}, + {74, 0}, + {75, 0}, + {76, 0}, + {77, 0}, + {78, 0}, + {79, 0}, + {80, 0}, + {81, 0}, + {82, 0}, + {83, 0}, + {84, 0}, + {85, 0}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {57388, 0}, + {57389, 14}, + {57390, 1}, + {57391, 157}, + {57392, 0}, + {57381, 0}, + {57386, 0}, + {8233, 40}, + {52, 0}, + {53, 200}, + {57348, 0}, + {30, 0}, + {31, 2}, + {43, 0}, + // { 57348, 0}, + // { 14, 0}, + // { 15, 0}, + // { 16, 3}, + // { 17, 232}, + // { 18, 0}, + // { 19, 0}, + // { 20, 0}, + // { 21, 0}, + {57348, 0}, + {50, 8}, + {51, 8}, + {57348, 0}, + {7, 1}, + // { 8, 0}, + // { 9, 0}, + // { 10, 19}, + // { 11, 136}, + {57348, 0}, + {49, 0}, + {57348, 0}, + {38, 0}, + {57348, 0}, + // { 28, 0}, + // { 25, 0}, + // { 26, 7}, + // { 27, 83}, + // { 22, 8}, + // { 23, 0}, + // { 24, 0}, + {57348, 0}, + // { 29, 0}, + {57344, 0}, + {57344, 0}, + {58156, 1}, + {57344, 0}, + {58157, 1}, + {57344, 0}, + {57678, 0}, + {57344, 0}, + {58130, 127}, + {57344, 0}, + {58153, 3}, + {57344, 0}, + {58155, 0}, + {57344, 0}, + {58161, 15}, + {57344, 0}, + {58162, 0}, + {57344, 0}, + {58154, 0}, + {57344, 0}, + {57630, 1}, + {0, 0}, + {0, 0}, + {434, 1}, + {435, 106}, + +}; + +static const struct mira016_reg partial_analog_gain_x1_10bit[] = { + {57344, 0}, + {443, 180}, + {444, 172}, + {208, 0}, + {496, 7}, + {499, 2}, + {366, 125}, + {370, 0}, + {371, 0}, + {367, 254}, + {368, 0}, + {369, 125}, + {372, 0}, + {373, 0}, + {395, 2}, + {396, 14}, + {397, 2}, + {398, 86}, + {399, 6}, + {400, 14}, + {375, 120}, + {494, 27}, + {495, 70}, + {418, 0}, + {419, 1}, + {799, 0}, + {800, 10}, + {422, 0}, + {423, 152}, + {420, 3}, + {421, 167}, + {801, 3}, + {802, 176}, + {424, 4}, + {425, 62}, + {416, 0}, + {417, 200}, + {434, 0}, + {435, 226}, + {432, 0}, + {433, 219}, + {428, 0}, + {429, 230}, + {57344, 1}, + {57344, 1}, + {57380, 7}, + {57344, 0}, + {57344, 0}, + {92, 0}, + {93, 48}, + {57344, 0}, + {403, 4}, + {404, 22}, + {57344, 0}, + {57353, 1}, + {8495, 1}, + {8496, 1}, + {8497, 1}, + {8498, 1}, + {8499, 1}, + {8500, 1}, + {8501, 1}, + {57569, 1}, + {394, 1}, + {224, 1}, + {58158, 1}, + {58176, 1}, + {57344, 0}, + {57817, 1}, + {57344, 0}, + {57579, 16}, + {57344, 0}, + {57821, 1}, + {57344, 0}, + {57581, 14}, + {57344, 0}, + {57823, 1}, + {57344, 0}, + {57582, 4}, + {57344, 0}, + {58165, 1}, + {57344, 0}, + {58148, 42}, + {57344, 0}, + {57850, 1}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57348, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57344, 0}, + {57344, 0}, + {31, 0}, + {32, 0}, + {35, 0}, + {36, 1}, + {37, 144}, + {38, 0}, + {39, 14}, + {40, 0}, + {41, 1}, + {42, 144}, + {43, 0}, + {44, 14}, + {45, 0}, + {46, 0}, + {47, 0}, + {48, 0}, + {49, 0}, + {50, 0}, + {51, 0}, + {52, 0}, + {53, 0}, + {54, 0}, + {55, 0}, + {56, 0}, + {57, 0}, + {58, 0}, + {59, 0}, + {60, 0}, + {61, 0}, + {62, 0}, + {63, 0}, + {64, 0}, + {65, 0}, + {66, 0}, + {67, 0}, + {68, 0}, + {69, 0}, + {70, 0}, + {71, 0}, + {72, 0}, + {73, 0}, + {74, 0}, + {75, 0}, + {76, 0}, + {77, 0}, + {78, 0}, + {79, 0}, + {80, 0}, + {81, 0}, + {82, 0}, + {83, 0}, + {84, 0}, + {85, 0}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {57388, 0}, + {57389, 14}, + {57390, 1}, + {57391, 157}, + {57392, 0}, + {57381, 0}, + {57386, 0}, + {8233, 40}, + {52, 0}, + {53, 200}, + {57348, 0}, + {30, 0}, + {31, 2}, + {43, 0}, + // { 57348, 0}, + // { 14, 0}, + // { 15, 0}, + // { 16, 3}, + // { 17, 232}, + // { 18, 0}, + // { 19, 0}, + // { 20, 0}, + // { 21, 0}, + {57348, 0}, + {50, 4}, + {51, 68}, + + {57348, 0}, + {49, 0}, + {57348, 0}, + {38, 0}, + {57348, 0}, + // { 28, 0}, + // { 25, 0}, + // { 26, 7}, + // { 27, 83}, + // { 22, 8}, + // { 23, 0}, + // { 24, 0}, + {57348, 0}, + // { 29, 0}, + {57344, 0}, + {57344, 0}, + {58156, 1}, + {57344, 0}, + {58157, 1}, + {57344, 0}, + {57678, 0}, + {57344, 0}, + {58130, 127}, + {57344, 0}, + {58153, 3}, + {57344, 0}, + {58155, 0}, + {57344, 0}, + {58161, 15}, + {57344, 0}, + {58162, 0}, + {57344, 0}, + {58154, 0}, + {57344, 0}, + {57630, 1}, + {0, 0}, + {0, 0}, + {434, 0}, + {435, 234}, + +}; + +static const struct mira016_reg partial_analog_gain_x2_10bit[] = { + {57344, 0}, + {443, 156}, + {444, 148}, + {208, 0}, + {496, 37}, + {499, 1}, + {366, 125}, + {370, 0}, + {371, 0}, + {367, 126}, + {368, 0}, + {369, 125}, + {372, 0}, + {373, 0}, + {395, 2}, + {396, 14}, + {397, 2}, + {398, 86}, + {399, 4}, + {400, 14}, + {375, 120}, + {494, 27}, + {495, 70}, + {418, 0}, + {419, 1}, + {799, 0}, + {800, 10}, + {422, 0}, + {423, 152}, + {420, 3}, + {421, 39}, + {801, 3}, + {802, 48}, + {424, 3}, + {425, 190}, + {416, 0}, + {417, 200}, + {434, 0}, + {435, 226}, + {432, 0}, + {433, 219}, + {428, 0}, + {429, 230}, + {57344, 1}, + {57344, 1}, + {57380, 15}, + {57344, 0}, + {57344, 0}, + {92, 0}, + {93, 24}, + {57344, 0}, + {403, 4}, + {404, 16}, + {57344, 0}, + {57353, 1}, + {8495, 1}, + {8496, 1}, + {8497, 1}, + {8498, 1}, + {8499, 1}, + {8500, 1}, + {8501, 1}, + {57569, 1}, + {394, 1}, + {224, 1}, + {58158, 1}, + {58176, 1}, + {57344, 0}, + {57817, 1}, + {57344, 0}, + {57579, 16}, + {57344, 0}, + {57821, 1}, + {57344, 0}, + {57581, 14}, + {57344, 0}, + {57823, 1}, + {57344, 0}, + {57582, 4}, + {57344, 0}, + {58165, 1}, + {57344, 0}, + {58148, 42}, + {57344, 0}, + {57850, 1}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57348, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57344, 0}, + {57344, 0}, + {31, 0}, + {32, 0}, + {35, 0}, + {36, 1}, + {37, 144}, + {38, 0}, + {39, 14}, + {40, 0}, + {41, 1}, + {42, 144}, + {43, 0}, + {44, 14}, + {45, 0}, + {46, 0}, + {47, 0}, + {48, 0}, + {49, 0}, + {50, 0}, + {51, 0}, + {52, 0}, + {53, 0}, + {54, 0}, + {55, 0}, + {56, 0}, + {57, 0}, + {58, 0}, + {59, 0}, + {60, 0}, + {61, 0}, + {62, 0}, + {63, 0}, + {64, 0}, + {65, 0}, + {66, 0}, + {67, 0}, + {68, 0}, + {69, 0}, + {70, 0}, + {71, 0}, + {72, 0}, + {73, 0}, + {74, 0}, + {75, 0}, + {76, 0}, + {77, 0}, + {78, 0}, + {79, 0}, + {80, 0}, + {81, 0}, + {82, 0}, + {83, 0}, + {84, 0}, + {85, 0}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {57388, 0}, + {57389, 14}, + {57390, 1}, + {57391, 157}, + {57392, 0}, + {57381, 0}, + {57386, 0}, + {8233, 40}, + {52, 0}, + {53, 200}, + {57348, 0}, + {30, 0}, + {31, 2}, + {43, 0}, + + {57348, 0}, + {50, 4}, + {51, 70}, + {57348, 0}, + {49, 0}, + {57348, 0}, + {38, 0}, + {57348, 0}, + // { 28, 0}, + // { 25, 0}, + // { 26, 7}, + // { 27, 83}, + // { 22, 8}, + // { 23, 0}, + // { 24, 0}, + {57348, 0}, + // { 29, 0}, + {57344, 0}, + {57344, 0}, + {58156, 1}, + {57344, 0}, + {58157, 1}, + {57344, 0}, + {57678, 0}, + {57344, 0}, + {58130, 127}, + {57344, 0}, + {58153, 3}, + {57344, 0}, + {58155, 0}, + {57344, 0}, + {58161, 15}, + {57344, 0}, + {58162, 0}, + {57344, 0}, + {58154, 0}, + {57344, 0}, + {57630, 1}, + {0, 0}, + {0, 0}, + {434, 0}, + {435, 234}, + +}; + +static const struct mira016_reg partial_analog_gain_x1_8bit[] = { + {57344, 0}, + {443, 180}, + {444, 172}, + {208, 0}, + {496, 7}, + {499, 2}, + {366, 125}, + {370, 0}, + {371, 0}, + {367, 254}, + {368, 0}, + {369, 125}, + {372, 0}, + {373, 0}, + {395, 2}, + {396, 14}, + {397, 2}, + {398, 86}, + {399, 6}, + {400, 14}, + {375, 120}, + {494, 27}, + {495, 70}, + {418, 0}, + {419, 1}, + {799, 0}, + {800, 10}, + {422, 0}, + {423, 152}, + {420, 3}, + {421, 167}, + {801, 3}, + {802, 176}, + {424, 4}, + {425, 62}, + {416, 0}, + {417, 250}, + {434, 1}, + {435, 20}, + {432, 1}, + {433, 13}, + {428, 1}, + {429, 24}, + {57344, 1}, + {57344, 1}, + {57380, 1}, + {57344, 0}, + {57344, 0}, + {92, 0}, + {93, 48}, + {57344, 0}, + {403, 4}, + {404, 26}, + {57344, 0}, + {57353, 1}, + {8495, 1}, + {8496, 1}, + {8497, 1}, + {8498, 1}, + {8499, 1}, + {8500, 1}, + {8501, 1}, + {57569, 1}, + {394, 1}, + {224, 1}, + {58158, 1}, + {58176, 1}, + {57344, 0}, + {57817, 1}, + {57344, 0}, + {57579, 16}, + {57344, 0}, + {57821, 1}, + {57344, 0}, + {57581, 14}, + {57344, 0}, + {57823, 1}, + {57344, 0}, + {57582, 4}, + {57344, 0}, + {58165, 1}, + {57344, 0}, + {58148, 42}, + {57344, 0}, + {57850, 1}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57348, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57344, 0}, + {57344, 0}, + {31, 0}, + {32, 0}, + {35, 0}, + {36, 1}, + {37, 144}, + {38, 0}, + {39, 14}, + {40, 0}, + {41, 1}, + {42, 144}, + {43, 0}, + {44, 14}, + {45, 0}, + {46, 0}, + {47, 0}, + {48, 0}, + {49, 0}, + {50, 0}, + {51, 0}, + {52, 0}, + {53, 0}, + {54, 0}, + {55, 0}, + {56, 0}, + {57, 0}, + {58, 0}, + {59, 0}, + {60, 0}, + {61, 0}, + {62, 0}, + {63, 0}, + {64, 0}, + {65, 0}, + {66, 0}, + {67, 0}, + {68, 0}, + {69, 0}, + {70, 0}, + {71, 0}, + {72, 0}, + {73, 0}, + {74, 0}, + {75, 0}, + {76, 0}, + {77, 0}, + {78, 0}, + {79, 0}, + {80, 0}, + {81, 0}, + {82, 0}, + {83, 0}, + {84, 0}, + {85, 0}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {57388, 0}, + {57389, 14}, + {57390, 1}, + {57391, 157}, + {57392, 0}, + {57381, 0}, + {57386, 0}, + {8233, 40}, + {52, 0}, + {53, 200}, + {57348, 0}, + {30, 0}, + {31, 2}, + {43, 0}, + // { 57348, 0}, + // { 14, 0}, + // { 15, 0}, + // { 16, 3}, + // { 17, 232}, + // { 18, 0}, + // { 19, 0}, + // { 20, 0}, + // { 21, 0}, + {57348, 0}, + {50, 4}, + {51, 18}, + + {57348, 0}, + {49, 0}, + {57348, 0}, + {38, 0}, + {57348, 0}, + // { 28, 0}, + // { 25, 0}, + // { 26, 7}, + // { 27, 83}, + // { 22, 8}, + // { 23, 0}, + // { 24, 0}, + {57348, 0}, + // { 29, 0}, + {57344, 0}, + {57344, 0}, + {58156, 1}, + {57344, 0}, + {58157, 1}, + {57344, 0}, + {57678, 0}, + {57344, 0}, + {58130, 127}, + {57344, 0}, + {58153, 3}, + {57344, 0}, + {58155, 0}, + {57344, 0}, + {58161, 15}, + {57344, 0}, + {58162, 0}, + {57344, 0}, + {58154, 0}, + {57344, 0}, + {57630, 1}, + {0, 0}, + {0, 0}, + {434, 1}, + {435, 28}, + +}; + +static const struct mira016_reg partial_analog_gain_x2_8bit[] = { + {57344, 0}, + {443, 156}, + {444, 148}, + {208, 2}, + {496, 37}, + {499, 1}, + {366, 125}, + {370, 0}, + {371, 0}, + {367, 126}, + {368, 0}, + {369, 125}, + {372, 0}, + {373, 0}, + {395, 0}, + {396, 139}, + {397, 0}, + {398, 142}, + {399, 1}, + {400, 11}, + {375, 120}, + {494, 27}, + {495, 70}, + {418, 0}, + {419, 1}, + {799, 0}, + {800, 10}, + {422, 0}, + {423, 152}, + {420, 3}, + {421, 39}, + {801, 3}, + {802, 48}, + {424, 3}, + {425, 190}, + {416, 0}, + {417, 250}, + {434, 1}, + {435, 20}, + {432, 1}, + {433, 13}, + {428, 1}, + {429, 24}, + {57344, 1}, + {57344, 1}, + {57380, 15}, + {57344, 0}, + {57344, 0}, + {92, 0}, + {93, 6}, + {57344, 0}, + {403, 1}, + {404, 19}, + {57344, 0}, + {57353, 1}, + {8495, 1}, + {8496, 1}, + {8497, 1}, + {8498, 1}, + {8499, 1}, + {8500, 1}, + {8501, 1}, + {57569, 1}, + {394, 1}, + {224, 1}, + {58158, 1}, + {58176, 1}, + {57344, 0}, + {57817, 1}, + {57344, 0}, + {57579, 16}, + {57344, 0}, + {57821, 1}, + {57344, 0}, + {57581, 14}, + {57344, 0}, + {57823, 1}, + {57344, 0}, + {57582, 4}, + {57344, 0}, + {58165, 1}, + {57344, 0}, + {58148, 42}, + {57344, 0}, + {57850, 1}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57348, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57344, 0}, + {57344, 0}, + {31, 0}, + {32, 0}, + {35, 0}, + {36, 1}, + {37, 144}, + {38, 0}, + {39, 14}, + {40, 0}, + {41, 1}, + {42, 144}, + {43, 0}, + {44, 14}, + {45, 0}, + {46, 0}, + {47, 0}, + {48, 0}, + {49, 0}, + {50, 0}, + {51, 0}, + {52, 0}, + {53, 0}, + {54, 0}, + {55, 0}, + {56, 0}, + {57, 0}, + {58, 0}, + {59, 0}, + {60, 0}, + {61, 0}, + {62, 0}, + {63, 0}, + {64, 0}, + {65, 0}, + {66, 0}, + {67, 0}, + {68, 0}, + {69, 0}, + {70, 0}, + {71, 0}, + {72, 0}, + {73, 0}, + {74, 0}, + {75, 0}, + {76, 0}, + {77, 0}, + {78, 0}, + {79, 0}, + {80, 0}, + {81, 0}, + {82, 0}, + {83, 0}, + {84, 0}, + {85, 0}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {57388, 0}, + {57389, 14}, + {57390, 1}, + {57391, 157}, + {57392, 0}, + {57381, 0}, + {57386, 0}, + {8233, 40}, + {52, 0}, + {53, 200}, + {57348, 0}, + {30, 0}, + {31, 2}, + {43, 0}, + // { 57348, 0}, + // { 14, 0}, + // { 15, 0}, + // { 16, 3}, + // { 17, 232}, + // { 18, 0}, + // { 19, 0}, + // { 20, 0}, + // { 21, 0}, + {57348, 0}, + {50, 3}, + {51, 236}, + {57348, 0}, + {7, 1}, + // { 8, 0}, + // { 9, 0}, + // { 10, 10}, + // { 11, 217}, + {57348, 0}, + {49, 0}, + {57348, 0}, + {38, 0}, + {57348, 0}, + // { 28, 0}, + // { 25, 0}, + // { 26, 7}, + // { 27, 83}, + // { 22, 8}, + // { 23, 0}, + // { 24, 0}, + {57348, 0}, + // { 29, 0}, + {57344, 0}, + {57344, 0}, + {58156, 1}, + {57344, 0}, + {58157, 1}, + {57344, 0}, + {57678, 0}, + {57344, 0}, + {58130, 127}, + {57344, 0}, + {58153, 3}, + {57344, 0}, + {58155, 0}, + {57344, 0}, + {58161, 15}, + {57344, 0}, + {58162, 0}, + {57344, 0}, + {58154, 0}, + {57344, 0}, + {57630, 1}, + {0, 0}, + {0, 0}, + {434, 1}, + {435, 28}, + +}; + +// converted_Draco_i2c_configuration_sequence_hex_8bit_1x_360fps_Version3 +static const struct mira016_reg full_400_400_100fps_8b_1lane_reg_pre_soft_reset[] = { + // Sensor Operating Mode + {57344, 0}, + {57726, 1}, + {57344, 0}, + {57344, 0}, + {484, 0}, + {485, 19}, + {482, 23}, + {483, 168}, + {486, 0}, + {487, 202}, + {364, 1}, + {363, 1}, + {520, 1}, + {521, 240}, + {522, 3}, + {523, 77}, + {524, 2}, + {525, 16}, + {526, 3}, + {527, 1}, + {528, 0}, + {529, 19}, + {530, 0}, + {531, 3}, + {532, 3}, + {533, 239}, + {534, 3}, + {535, 243}, + {536, 3}, + {537, 244}, + {538, 0}, + {539, 1}, + {540, 3}, + {541, 248}, + {542, 0}, + {543, 2}, + {544, 1}, + {545, 242}, + {546, 3}, + {547, 27}, + {548, 0}, + {549, 33}, + {550, 3}, + {551, 240}, + {552, 3}, + {553, 241}, + {554, 3}, + {555, 242}, + {556, 3}, + {557, 245}, + {558, 3}, + {559, 246}, + {560, 0}, + {561, 193}, + {562, 0}, + {563, 2}, + {564, 1}, + {565, 242}, + {566, 3}, + {567, 107}, + {568, 3}, + {569, 255}, + {570, 3}, + {571, 49}, + {572, 1}, + {573, 240}, + {574, 3}, + {575, 135}, + {576, 0}, + {577, 10}, + {578, 0}, + {579, 11}, + {580, 1}, + {581, 249}, + {582, 3}, + {583, 13}, + {584, 0}, + {585, 7}, + {586, 3}, + {587, 239}, + {588, 3}, + {589, 243}, + {590, 3}, + {591, 244}, + {592, 3}, + {593, 0}, + {594, 0}, + {595, 7}, + {596, 0}, + {597, 12}, + {598, 1}, + {599, 241}, + {600, 3}, + {601, 67}, + {602, 1}, + {603, 248}, + {604, 3}, + {605, 16}, + {606, 0}, + {607, 7}, + {608, 3}, + {609, 240}, + {610, 3}, + {611, 241}, + {612, 3}, + {613, 242}, + {614, 3}, + {615, 245}, + {616, 3}, + {617, 246}, + {618, 3}, + {619, 0}, + {620, 2}, + {621, 135}, + {622, 0}, + {623, 1}, + {624, 3}, + {625, 255}, + {626, 3}, + {627, 0}, + {628, 3}, + {629, 255}, + {630, 2}, + {631, 135}, + {632, 3}, + {633, 2}, + {634, 3}, + {635, 15}, + {636, 3}, + {637, 247}, + {638, 0}, + {639, 22}, + {640, 0}, + {641, 51}, + {642, 0}, + {643, 4}, + {644, 0}, + {645, 17}, + {646, 3}, + {647, 9}, + {648, 0}, + {649, 2}, + {650, 0}, + {651, 32}, + {652, 0}, + {653, 181}, + {654, 0}, + {655, 229}, + {656, 0}, + {657, 18}, + {658, 0}, + {659, 181}, + {660, 0}, + {661, 229}, + {662, 0}, + {663, 16}, + {664, 0}, + {665, 2}, + {666, 0}, + {667, 32}, + {668, 0}, + {669, 181}, + {670, 0}, + {671, 229}, + {672, 0}, + {673, 18}, + {674, 0}, + {675, 181}, + {676, 0}, + {677, 229}, + {678, 0}, + {679, 0}, + {680, 0}, + {681, 18}, + {682, 0}, + {683, 18}, + {684, 0}, + {685, 32}, + {686, 0}, + {687, 181}, + {688, 0}, + {689, 229}, + {690, 0}, + {691, 0}, + {692, 0}, + {693, 18}, + {694, 0}, + {695, 18}, + {696, 0}, + {697, 32}, + {698, 0}, + {699, 71}, + {700, 0}, + {701, 39}, + {702, 0}, + {703, 181}, + {704, 0}, + {705, 229}, + {706, 0}, + {707, 0}, + {708, 0}, + {709, 4}, + {710, 0}, + {711, 67}, + {712, 0}, + {713, 1}, + {714, 3}, + {715, 2}, + {716, 0}, + {717, 8}, + {718, 3}, + {719, 255}, + {720, 2}, + {721, 135}, + {722, 3}, + {723, 199}, + {724, 3}, + {725, 247}, + {726, 0}, + {727, 119}, + {728, 0}, + {729, 23}, + {730, 0}, + {731, 8}, + {732, 3}, + {733, 255}, + {734, 0}, + {735, 56}, + {736, 0}, + {737, 23}, + {738, 0}, + {739, 8}, + {740, 3}, + {741, 255}, + {742, 3}, + {743, 255}, + {744, 3}, + {745, 255}, + {746, 3}, + {747, 255}, + {748, 3}, + {749, 255}, + {750, 3}, + {751, 255}, + {752, 3}, + {753, 255}, + {754, 3}, + {755, 255}, + {756, 3}, + {757, 255}, + {758, 3}, + {759, 255}, + {760, 3}, + {761, 255}, + {762, 3}, + {763, 255}, + {764, 3}, + {765, 255}, + {766, 3}, + {767, 255}, + {768, 3}, + {769, 255}, + {770, 3}, + {771, 255}, + {489, 0}, + {488, 25}, + {490, 53}, + {491, 55}, + {492, 100}, + {493, 107}, + {504, 15}, + {472, 1}, + {476, 1}, + {478, 1}, + {393, 1}, + {439, 1}, + {449, 7}, + {450, 246}, + {451, 255}, + {457, 7}, + {805, 0}, + {57689, 0}, + {826, 0}, + {440, 1}, + {442, 51}, + {446, 116}, + {447, 54}, + {448, 83}, + {239, 0}, + {806, 0}, + {240, 0}, + {241, 0}, + {807, 0}, + {242, 0}, + {113, 1}, + {436, 1}, + {437, 1}, + {497, 1}, + {500, 1}, + {501, 1}, + {788, 1}, + {789, 1}, + {790, 1}, + {519, 0}, + {16903, 2}, + {8711, 2}, + {57480, 1}, + {57485, 0}, + {57486, 48}, + {57487, 212}, + {57481, 86}, + {57482, 16}, + {57483, 31}, + {57484, 15}, + {57510, 0}, + {57513, 16}, + {57514, 0}, + {57517, 10}, + {57512, 48}, + {57511, 15}, + {57516, 16}, + {57515, 15}, + {8349, 0}, + {808, 0}, + {99, 1}, + {503, 15}, + {57571, 1}, + {57575, 3}, + {58171, 0}, + {58166, 0}, + {58167, 0}, + {58168, 0}, + {58169, 0}, + {233, 1}, + {234, 254}, + {777, 3}, + {778, 2}, + {779, 2}, + {780, 5}, + {782, 21}, + {781, 20}, + {783, 1}, + {784, 13}, + {464, 31}, + {465, 31}, + {461, 17}, + {22, 0}, + {23, 5}, + {232, 4}, + {498, 0}, + {362, 0}, + {57536, 0}, + {57537, 8}, + {57538, 0}, + {57539, 8}, + {360, 42}, + {8192, 0}, + {57344, 0}, + {57506, 0}, + {57467, 0}, + {57464, 0}, + {57465, 1}, + {8311, 0}, + {8310, 189}, + {206, 1}, + {112, 9}, + {365, 50}, + {374, 0}, + {57654, 0}, + {57542, 0}, + {57543, 0}, + {57544, 1}, + {57545, 0}, + {57546, 0}, + {57547, 1}, + {57548, 128}, + {57549, 128}, + {57534, 2}, + {57535, 2}, + {57540, 8}, + {57541, 8}, + {8309, 0}, + {8192, 0}, + {57344, 0}, + {30, 1}, + {57344, 0}, + {8318, 0}, + {57476, 0}, + {57477, 0}, + {57478, 1}, + {57479, 0}, + {8319, 0}, + {8320, 0}, + {8321, 3}, + {8322, 0}, + {8323, 2}, + {144, 0}, + {8343, 0}, + {57344, 0}, + {17, 3}, + {285, 1}, + {57344, 0}, + {18, 0}, + {19, 24}, + {346, 0}, + {347, 68}, + {348, 0}, + {349, 68}, + {350, 0}, + {351, 68}, + {354, 0}, + {355, 5}, + {356, 4}, + {357, 121}, + {358, 4}, + {359, 121}, + {57344, 0}, + {443, 180}, + {444, 172}, + {208, 0}, + {496, 7}, + {499, 2}, + {366, 125}, + {370, 0}, + {371, 0}, + {367, 254}, + {368, 0}, + {369, 125}, + {372, 0}, + {373, 0}, + {395, 2}, + {396, 14}, + {397, 2}, + {398, 86}, + {399, 6}, + {400, 14}, + {375, 120}, + {494, 27}, + {495, 70}, + {418, 0}, + {419, 1}, + {799, 0}, + {800, 10}, + {422, 0}, + {423, 152}, + {420, 3}, + {421, 167}, + {801, 3}, + {802, 176}, + {424, 4}, + {425, 62}, + {416, 0}, + {417, 250}, + {434, 1}, + {435, 20}, + {432, 1}, + {433, 13}, + {428, 1}, + {429, 24}, + {57344, 1}, + {57344, 1}, + {57380, 1}, + {57344, 0}, + {57344, 0}, + {92, 0}, + {93, 48}, + {57344, 0}, + {403, 4}, + {404, 26}, + {57344, 0}, + {57353, 1}, + {8495, 1}, + {8496, 1}, + {8497, 1}, + {8498, 1}, + {8499, 1}, + {8500, 1}, + {8501, 1}, + {57569, 1}, + {394, 1}, + {224, 1}, + {58158, 1}, + {58176, 1}, + {57344, 0}, + {57817, 1}, + {57344, 0}, + {57579, 16}, + {57344, 0}, + {57821, 1}, + {57344, 0}, + {57581, 14}, + {57344, 0}, + {57823, 1}, + {57344, 0}, + {57582, 4}, + {57344, 0}, + {58165, 1}, + {57344, 0}, + {58148, 42}, + {57344, 0}, + {57850, 1}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57348, 1}, + {30, 0}, + {31, 2}, + {43, 0}, + {57344, 0}, + {57344, 0}, + {31, 0}, + {32, 0}, + {35, 0}, + {36, 1}, + {37, 144}, + {38, 0}, + {39, 14}, + {40, 0}, + {41, 1}, + {42, 144}, + {43, 0}, + {44, 14}, + {45, 0}, + {46, 0}, + {47, 0}, + {48, 0}, + {49, 0}, + {50, 0}, + {51, 0}, + {52, 0}, + {53, 0}, + {54, 0}, + {55, 0}, + {56, 0}, + {57, 0}, + {58, 0}, + {59, 0}, + {60, 0}, + {61, 0}, + {62, 0}, + {63, 0}, + {64, 0}, + {65, 0}, + {66, 0}, + {67, 0}, + {68, 0}, + {69, 0}, + {70, 0}, + {71, 0}, + {72, 0}, + {73, 0}, + {74, 0}, + {75, 0}, + {76, 0}, + {77, 0}, + {78, 0}, + {79, 0}, + {80, 0}, + {81, 0}, + {82, 0}, + {83, 0}, + {84, 0}, + {85, 0}, + {57344, 1}, + {57348, 0}, + {57344, 1}, + {57388, 0}, + {57389, 14}, + {57390, 1}, + {57391, 157}, + {57392, 0}, + {57381, 0}, + {57386, 0}, + {8233, 40}, + {52, 0}, + {53, 200}, + {57348, 0}, + {30, 0}, + {31, 2}, + {43, 0}, + // { 57348, 0}, + // { 14, 0}, + // { 15, 0}, + // { 16, 3}, + // { 17, 232}, + // { 18, 0}, + // { 19, 0}, + // { 20, 0}, + // { 21, 0}, + {57348, 0}, + {50, 4}, + {51, 18}, + {57348, 0}, + {7, 1}, + // { 8, 0}, + // { 9, 0}, + // { 10, 10}, + // { 11, 217}, + {57348, 0}, + {49, 0}, + {57348, 0}, + {38, 0}, + {57348, 0}, + // { 28, 0}, + // { 25, 0}, + // { 26, 7}, + // { 27, 83}, + // { 22, 8}, + // { 23, 0}, + // { 24, 0}, + {57348, 0}, + // { 29, 0}, + {57344, 0}, + {57344, 0}, + {58156, 1}, + {57344, 0}, + {58157, 1}, + {57344, 0}, + {57678, 0}, + {57344, 0}, + {58130, 127}, + {57344, 0}, + {58153, 3}, + {57344, 0}, + {58155, 0}, + {57344, 0}, + {58161, 15}, + {57344, 0}, + {58162, 0}, + {57344, 0}, + {58154, 0}, + {57344, 0}, + {57630, 1}, + {0, 0}, + {0, 0}, + {434, 1}, + {435, 28}, + + + + {0xE000, 0}, + {0xE004, 0}, + {0x33D, 1}, +}; + +static const struct mira016_reg full_400_400_100fps_8b_1lane_reg_post_soft_reset[] = { + {0xE000, 0}, + {0xE004, 0}, + // Below are manually added after reg seq txt + {0x0335, 1}, // iref sel + {0x0324, 43}, // iref val + + {0x1d9, 1}, // #vddana sel + {0x0EB, 15}, // #vddana val trim + + {0x1dd, 1}, // # vsspc sel + {0x1ed, 14}, // # vsspc val + + {0x1df, 1}, // #cp sel + {0x0ee, 4}, // #cp trim, +}; + +static const char *const mira016_test_pattern_menu[] = { + "Disabled", + "Fixed Data", + "2D Gradient", +}; + +static const int mira016_test_pattern_val[] = { + MIRA016_TEST_PATTERN_DISABLE, + MIRA016_TEST_PATTERN_FIXED_DATA, + MIRA016_TEST_PATTERN_2D_GRADIENT, +}; + +/* regulator supplies */ +static const char *const mira016_supply_name[] = { + // TODO(jalv): Check supply names + /* Supplies can be enabled in any order */ + "VANA", /* Analog (2.8V) supply */ + "VDIG", /* Digital Core (1.8V) supply */ + "VDDL", /* IF (1.2V) supply */ +}; + +#define MIRA016_NUM_SUPPLIES ARRAY_SIZE(mira016_supply_name) + +/* + * The supported formats. All flip/mirror combinations have the same byte order because the sensor + * is monochrome + */ +static const u32 codes[] = { + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SGRBG8_1X8, +}; + +/* Mode configs */ +/* + * Only one mode is exposed to the public (400x400 at 10 bit). + * One code (10 bit) is exposed to public. + * The public user specifies the code. + * That is used to specify which internal supported_mode to use. + */ +#define MIRA016_SUPPORTED_MODE_SIZE_PUBLIC 1 +static const struct mira016_mode supported_modes[] = { + { + /* 400x400 10bit 100fps mode */ + .width = 400, + .height = 400, + .crop = { + .left = MIRA016_PIXEL_ARRAY_LEFT, + .top = MIRA016_PIXEL_ARRAY_TOP, + .width = 400, + .height = 400}, + .reg_list_pre_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_400_400_100fps_10b_1lane_reg_pre_soft_reset), + .regs = full_400_400_100fps_10b_1lane_reg_pre_soft_reset, + }, + .reg_list_post_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_400_400_100fps_10b_1lane_reg_post_soft_reset), + .regs = full_400_400_100fps_10b_1lane_reg_post_soft_reset, + }, + .min_vblank = MIRA016_MIN_VBLANK_360, + .max_vblank = MIRA016_MAX_VBLANK, + .hblank = MIRA016_HBLANK, // TODO + .bit_depth = 10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + }, + { + /* 400x400 12 bit 100fps mode */ + .width = 400, + .height = 400, + .crop = {.left = MIRA016_PIXEL_ARRAY_LEFT, .top = MIRA016_PIXEL_ARRAY_TOP, .width = 400, .height = 400}, + .reg_list_pre_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_400_400_100fps_12b_1lane_reg_pre_soft_reset), + .regs = full_400_400_100fps_12b_1lane_reg_pre_soft_reset, + }, + .reg_list_post_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_400_400_100fps_12b_1lane_reg_post_soft_reset), + .regs = full_400_400_100fps_12b_1lane_reg_post_soft_reset, + }, + .min_vblank = MIRA016_MIN_VBLANK_200, + .max_vblank = MIRA016_MAX_VBLANK, + .hblank = MIRA016_HBLANK, // TODO + .bit_depth = 12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + }, + { + /* 400x400 8 bit 100fps mode */ + .width = 400, + .height = 400, + .crop = {.left = MIRA016_PIXEL_ARRAY_LEFT, .top = MIRA016_PIXEL_ARRAY_TOP, .width = 400, .height = 400}, + .reg_list_pre_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_400_400_100fps_8b_1lane_reg_pre_soft_reset), + .regs = full_400_400_100fps_8b_1lane_reg_pre_soft_reset, + }, + .reg_list_post_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_400_400_100fps_8b_1lane_reg_post_soft_reset), + .regs = full_400_400_100fps_8b_1lane_reg_post_soft_reset, + }, + .min_vblank = MIRA016_MIN_VBLANK_360, + .max_vblank = MIRA016_MAX_VBLANK, + .hblank = MIRA016_HBLANK, // TODO + .bit_depth = 8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + }, +}; + +struct mira016 +{ + struct v4l2_subdev sd; + struct media_pad pad[NUM_PADS]; + + struct v4l2_mbus_framefmt fmt; + + struct clk *xclk; /* system clock to MIRA016 */ + u32 xclk_freq; + + // struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[MIRA016_NUM_SUPPLIES]; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + // custom v4l2 control + struct v4l2_ctrl *mira016_reg_w; + struct v4l2_ctrl *mira016_reg_r; + u16 mira016_reg_w_cached_addr; + u8 mira016_reg_w_cached_flag; + + /* Current mode */ + const struct mira016_mode *mode; + /* current bit depth, may defer from mode->bit_depth */ + u8 bit_depth; + /* OTP_CALIBRATION_VALUE stored in OTP memory */ + u32 otp_cal_val; + /* Whether to skip base register sequence upload */ + u32 skip_reg_upload; + /* Whether to reset sensor when stream on/off */ + u32 skip_reset; + /* Whether regulator and clk are powered on */ + u32 powered; + /* Illumination trigger enable */ + u8 illum_enable; + /* Illumination trigger width. Use [23:0] for 24-bit register. */ + u32 illum_width; + /* Illumination trigger delay. Use [19:0] for 20-bit register */ + u32 illum_delay; + /* Illumination trigger width automatically set to exposure time */ + u8 illum_width_auto; + /* A flag to force write_start/stop_streaming_regs even if (skip_reg_upload==1) */ + u32 target_frame_time_us; + u32 row_length; + u8 force_stream_ctrl; + + /* + * Mutex for serialized access: + * Protect sensor module set pad format and start/stop streaming safely. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; + + /* pmic and uC */ + struct i2c_client *pmic_client; + struct i2c_client *uc_client; + struct i2c_client *led_client; + /* User specified I2C device address */ + u32 tbd_client_i2c_addr; +}; + +static inline struct mira016 *to_mira016(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct mira016, sd); +} + +static int mira016_read(struct mira016 *mira016, u16 reg, u8 *val) +{ + int ret; + unsigned char data_w[2] = {reg >> 8, reg & 0xff}; + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + + 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; +} + +static int mira016_write(struct mira016 *mira016, u16 reg, u8 val) +{ + int ret; + unsigned char data[3] = {reg >> 8, reg & 0xff, val}; + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + + 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; + } + + /* + * The code below is for debug purpose. + * It reads back the written values. + * Some registers have different read and write addresses. + * These registers typically have WR addr 0xE... but RD addr 0x4... + */ + /* + { + usleep_range(50, 300); + u8 ret_val; + u16 ret_reg; + if (((reg >>12) & 0x000F) == 0x000E) { + ret_reg = ((reg & 0x0FFF) | 0x4000); + } else { + ret_reg = reg; + } + ret = mira016_read(mira016, ret_reg, &ret_val); + printk(KERN_INFO "[MIRA016]: Write reg 0x%4.4x, Read ret_reg 0x%4.4x, val = 0x%x.\n", + reg, ret_reg, ret_val); + if (val != ret_val) { + printk(KERN_INFO "[MIRA016]: WARNING Write reg 0x%4.4x, val = 0x%x, read ret_reg = 0x%4.4x, ret_val = 0x%x.\n", + reg, val, ret_reg, ret_val); + } + } + */ + + return ret; +} + +/* + * mira016 is big-endian: msb of val goes to lower reg addr + */ +/* Temporary commented out, because it is not yet used. */ +/* +static int mira016_write_be16(struct mira016 *mira016, u16 reg, u16 val) +{ + int ret; + unsigned char data[4] = { reg >> 8, reg & 0xff, (val >> 8) & 0xff, val & 0xff }; + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + + ret = i2c_master_send(client, data, 4); + // + // Writing the wrong number of bytes also needs to be flagged as an + // error. Success needs to produce a 0 return code. + // + if (ret == 4) { + ret = 0; + } else { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + } + + return ret; +} +*/ + +/* + * mira016 is big-endian: msb of val goes to lower reg addr + */ +static int mira016_write_be24(struct mira016 *mira016, u16 reg, u32 val) +{ + int ret; + unsigned char data[5] = {reg >> 8, reg & 0xff, (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff}; + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + + ret = i2c_master_send(client, data, 5); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 5) + { + ret = 0; + } + else + { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + } + + return ret; +} + +/* + * mira016 is big-endian: msb of val goes to lower reg addr + */ +static int mira016_write_be32(struct mira016 *mira016, u16 reg, u32 val) +{ + int ret; + unsigned char data[6] = {reg >> 8, reg & 0xff, (val >> 24) & 0xff, (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff}; + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + + ret = i2c_master_send(client, data, 6); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 6) + { + ret = 0; + } + else + { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + } + + return ret; +} + +/* + * mira016 OTP 32-bit val on I2C is big-endian. However, val content can be little-endian. + */ +static int mira016_read_be32(struct mira016 *mira016, u16 reg, u32 *val) +{ + int ret; + unsigned char data_w[2] = {reg >> 8, reg & 0xff}; + /* Big-endian 32-bit buffer. */ + unsigned char data_r[4]; + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + + 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, data_r, 4); + *val = (u32)((data_r[0] << 24) | (data_r[1] << 16) | (data_r[2] << 8) | data_r[3]); + /* + * The only return value indicating success is 4. Anything else, even + * a non-negative value, indicates something went wrong. + */ + if (ret == 4) + { + 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 mira016_write_regs(struct mira016 *mira016, + const struct mira016_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + unsigned int i; + int ret; + + for (i = 0; i < len; i++) + { + ret = mira016_write(mira016, 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; + } + else + { + // Debug code below + // u8 val; + // ret = mira016_read(mira016, regs[i].address, &val); + // printk(KERN_INFO "[MIRA016]: Read reg 0x%4.4x, val = 0x%x.\n", + // regs[i].address, val); + } + } + + return 0; +} + +/* + * Read OTP memory: 8-bit addr and 32-bit value + */ +static int mira016_otp_read(struct mira016 *mira016, u8 addr, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + u8 busy_status = 1; + int poll_cnt = 0; + int poll_cnt_max = 5; + int ret; + u8 temp; + mira016_write(mira016, MIRA016_BANK_SEL_REG, 0); + mira016_write(mira016, MIRA016_OTP_COMMAND, 0); + mira016_write(mira016, MIRA016_OTP_ADDR, addr); + mira016_write(mira016, MIRA016_OTP_START, 1); + usleep_range(5, 10); + mira016_write(mira016, MIRA016_OTP_START, 0); + for (poll_cnt = 0; poll_cnt < poll_cnt_max; poll_cnt++) + { + mira016_read(mira016, MIRA016_OTP_BUSY, &busy_status); + // printk(KERN_INFO "[MIRA016]: otp busy status : %u.\n", busy_status); + + if (busy_status == 0) + { + break; + } + } + if (poll_cnt < poll_cnt_max && busy_status == 0) + { + ret = mira016_read_be32(mira016, MIRA016_OTP_DOUT, val); + // printk(KERN_INFO "[MIRA016]: otp read success.\n"); + // printk(KERN_INFO "[MIRA016]: otp read success, anyways readout val : %u.\n", *val); + // ret = mira016_read(mira016, MIRA016_OTP_DOUT, &temp); + // printk(KERN_INFO "[MIRA016]: otp read success.\n"); + printk(KERN_INFO "[MIRA016]: otp read success, anyways 8bit readout val : %u.\n", temp); + + + + } + else + { + printk(KERN_INFO "[MIRA016]: otp read fail.\n"); + ret = mira016_read_be32(mira016, MIRA016_OTP_DOUT, val); + // printk(KERN_INFO "[MIRA016]: otp read fail, anyways readout val : %u.\n", *val); + + dev_dbg(&client->dev, "%s: OTP memory busy, skip raeding addr: 0x%X\n", + __func__, addr); + ret = -EINVAL; + } + + return ret; +} + +/* Write PMIC registers, and can be reused to write microcontroller reg. */ +static int mira016pmic_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret; + unsigned char data[2] = {reg & 0xff, val}; + + ret = i2c_master_send(client, data, 2); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 2) + { + 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 mira016pmic_read(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msgs[2]; + u8 addr_buf[1] = {reg & 0xff}; + u8 data_buf[1] = {0}; + int ret; + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = &data_buf[0]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = (u8)(data_buf[0]); + + return 0; +} + +/* Power/clock management functions */ +static int mira016_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira016 *mira016 = to_mira016(sd); + int ret = -EINVAL; + + printk(KERN_INFO "[MIRA016]: Entering power on function.\n"); + + if (mira016->powered == 0) + { + ret = regulator_bulk_enable(MIRA016_NUM_SUPPLIES, mira016->supplies); + if (ret) + { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + + ret = clk_prepare_enable(mira016->xclk); + if (ret) + { + dev_err(&client->dev, "%s: failed to enable clock\n", + __func__); + goto reg_off; + } + usleep_range(MIRA016_XCLR_MIN_DELAY_US, + MIRA016_XCLR_MIN_DELAY_US + MIRA016_XCLR_DELAY_RANGE_US); + mira016->powered = 1; + } + else + { + printk(KERN_INFO "[MIRA016]: Skip regulator and clk enable, because mira016->powered == %d.\n", mira016->powered); + } + + return 0; + +reg_off: + ret = regulator_bulk_disable(MIRA016_NUM_SUPPLIES, mira016->supplies); + return ret; +} + +static int mira016_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira016 *mira016 = to_mira016(sd); + + printk(KERN_INFO "[MIRA016]: Entering power off function.\n"); + + if (mira016->skip_reset == 0) + { + if (mira016->powered == 1) + { + regulator_bulk_disable(MIRA016_NUM_SUPPLIES, mira016->supplies); + clk_disable_unprepare(mira016->xclk); + mira016->powered = 0; + } + else + { + printk(KERN_INFO "[MIRA016]: Skip disabling regulator and clk due to mira016->powered == %d.\n", mira016->powered); + } + } + else + { + printk(KERN_INFO "[MIRA016]: Skip disabling regulator and clk due to mira016->skip_reset=%u.\n", mira016->skip_reset); + } + + return 0; +} + +static int mira016_write_illum_trig_regs(struct mira016 *mira016) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira016->sd); + int ret = 0; + u32 lps_time = 0; + u32 width_adjust = 0; + + // Set context bank 1A or bank 1B + ret = mira016_write(mira016, MIRA016_RW_CONTEXT_REG, 0); + if (ret) + { + dev_err(&client->dev, "Error setting RW_CONTEXT."); + return ret; + } + + // Set conetxt bank 0 or 1 + ret = mira016_write(mira016, MIRA016_BANK_SEL_REG, 1); + if (ret) + { + dev_err(&client->dev, "Error setting BANK_SEL_REG."); + return ret; + } + + // Enable or disable illumination trigger + printk(KERN_INFO "[MIRA016]: Writing EN_TRIG_ILLUM to %d.\n", mira016->illum_enable); + ret = mira016_write(mira016, MIRA016_EN_TRIG_ILLUM, mira016->illum_enable); + if (ret) + { + dev_err(&client->dev, "Error setting EN_TRIG_ILLUM to %d.", mira016->illum_enable); + return ret; + } + + if (MIRA016_LPS_DISABLED) + { + // Set illumination width. Write 24 bits. All 24 bits are valid. + printk(KERN_INFO "[MIRA016]: LPS DISABLED. Writing ILLUM_WIDTH to %u.\n", mira016->illum_width); + ret = mira016_write_be24(mira016, MIRA016_ILLUM_WIDTH_REG, mira016->illum_width); + if (ret) + { + dev_err(&client->dev, "LPS DISABLED. Error setting ILLUM_WIDTH to %u.", mira016->illum_width); + return ret; + } + } + else + { + // LSP active, adjust pulse with to compensate for dead time during exposure + // INPUT PARAMS: LPS_CYCLE_TIME, EXP_TIME, FRAME_TIME, GLOB_TIME, READOUT_TIME + // + // + // printk(KERN_INFO "[MIRA016]: LPS DISABLED. Exposure name is to %u.\n", mira016->exposure->name); + u32 cur_exposure = (mira016->exposure->val) ; + + printk(KERN_INFO "[MIRA016]: LPS ENABLED. Exposure cur is to %u.\n", mira016->exposure->val); + printk(KERN_INFO "[MIRA016]: LPS ENABLED. Exposure cur IN US is to %u.\n", cur_exposure); + + u32 readout_time = (11 + MIRA016_PIXEL_ARRAY_HEIGHT) * mira016->row_length * 8 / MIRA016_DATA_RATE; + + printk(KERN_INFO "[MIRA016]: LPS ENABLED. MIRA016_LPS_CYCLE_TIME is to %u.\n", MIRA016_LPS_CYCLE_TIME); + printk(KERN_INFO "[MIRA016]: LPS ENABLED. MIRA016_GLOB_TIME is to %u.\n", MIRA016_GLOB_TIME); + printk(KERN_INFO "[MIRA016]: LPS ENABLED. frame time is to %u.\n", mira016->target_frame_time_us); + printk(KERN_INFO "[MIRA016]: LPS ENABLED. glob time is to %u.\n", MIRA016_GLOB_TIME); + printk(KERN_INFO "[MIRA016]: LPS ENABLED. read time is to %u.\n", MIRA016_READOUT_TIME); + printk(KERN_INFO "[MIRA016]: LPS ENABLED. new read time is to %u.\n", readout_time); + printk(KERN_INFO "[MIRA016]: LPS ENABLED. mira016->target_frame_time_us - MIRA016_GLOB_TIME - readout_time is to %u.\n", mira016->target_frame_time_us - MIRA016_GLOB_TIME - MIRA016_READOUT_TIME); + + // case 1: EXP_TIME < LPS_CYCLE_TIME + if (cur_exposure < MIRA016_LPS_CYCLE_TIME) + { + printk(KERN_INFO "[MIRA016]: LPS CASE 1 to %u.\n", mira016->illum_width); + lps_time = 0; + } + // case 2: LPS_ CYCLE_ TIMEtarget_frame_time_us - MIRA016_GLOB_TIME - readout_time))) + { + lps_time = cur_exposure - MIRA016_LPS_CYCLE_TIME; + printk(KERN_INFO "[MIRA016]: LPS CASE 2 - LPS TIME is %u.\n", lps_time); + } + // case 3: LPS_ CYCLE_ TIME≤FRAME_ TIME-GLOB_ TIME-READOUT_TIMEtarget_frame_time_us - MIRA016_GLOB_TIME - readout_time)) && ((mira016->target_frame_time_us - MIRA016_GLOB_TIME - readout_time) < cur_exposure)) + { + lps_time = (mira016->target_frame_time_us - MIRA016_GLOB_TIME - readout_time) - MIRA016_LPS_CYCLE_TIME; + printk(KERN_INFO "[MIRA016]: LPS CASE 3 - LPS TIME is %u.\n", lps_time); + } + // case 4: FRAME_ TIME-GLOB_ TIME-READOUT_ TIME≤LPS_ CYCLE_ TIMEtarget_frame_time_us - MIRA016_GLOB_TIME - readout_time) < MIRA016_LPS_CYCLE_TIME) && (MIRA016_LPS_CYCLE_TIME < cur_exposure)) + { + printk(KERN_INFO "[MIRA016]: LPS CASE 4 to %u.\n", mira016->illum_width); + lps_time = 0; + } + else + { + printk(KERN_INFO "[MIRA016]: LPS CASE 5 invalid to %u.\n", mira016->illum_width); + } + + width_adjust = (lps_time>0 ? lps_time * 1500 / 8 - 30 : 0); + printk(KERN_INFO "[MIRA016]: LPS ENABLE -s width adjust is %u.\n", width_adjust); + + ret = mira016_write_be24(mira016, MIRA016_ILLUM_WIDTH_REG, mira016->illum_width - width_adjust); + + if (ret) + { + dev_err(&client->dev, "LPS ENABLED. Error setting ILLUM_WIDTH to %u.", mira016->illum_width - width_adjust); + return ret; + } + } + return ret; +} + +static int mira016_v4l2_reg_w(struct mira016 *mira016, u32 value) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira016->sd); + u32 ret = 0; + u32 tmp_flag; + + u16 reg_addr = (value >> 8) & 0xFFFF; + u8 reg_val = value & 0xFF; + u8 reg_flag = (value >> 24) & 0xFF; + + // printk(KERN_INFO "[MIRA016]: %s reg_flag: 0x%02X; reg_addr: 0x%04X; reg_val: 0x%02X.\n", + // __func__, reg_flag, reg_addr, reg_val); + + if (reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_CMD_SEL) + { + if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_SLEEP_US) + { + // If it is for sleep, combine all 24 bits of reg_addr and reg_val as sleep us. + u32 sleep_us_val = value & 0x00FFFFFF; + // Sleep range needs an interval, default to 1/8 of the sleep value. + u32 sleep_us_interval = sleep_us_val >> 3; + printk(KERN_INFO "[MIRA016]: %s sleep_us: %u.\n", __func__, sleep_us_val); + usleep_range(sleep_us_val, sleep_us_val + sleep_us_interval); + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_RESET_ON) + { + printk(KERN_INFO "[MIRA016]: %s Enable reset at stream on/off.\n", __func__); + mira016->skip_reset = 0; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_RESET_OFF) + { + printk(KERN_INFO "[MIRA016]: %s Disable reset at stream on/off.\n", __func__); + mira016->skip_reset = 1; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_REG_UP_ON) + { + printk(KERN_INFO "[MIRA016]: %s Enable base register sequence upload.\n", __func__); + mira016->skip_reg_upload = 0; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_REG_UP_OFF) + { + printk(KERN_INFO "[MIRA016]: %s Disable base register sequence upload.\n", __func__); + mira016->skip_reg_upload = 1; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_POWER_ON) + { + printk(KERN_INFO "[MIRA016]: %s Call power on function mira016_power_on().\n", __func__); + /* Temporarily disable skip_reset if manually doing power on/off */ + tmp_flag = mira016->skip_reset; + mira016->skip_reset = 0; + mira016_power_on(&client->dev); + mira016->skip_reset = tmp_flag; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_POWER_OFF) + { + printk(KERN_INFO "[MIRA016]: %s Call power off function mira016_power_off().\n", __func__); + /* Temporarily disable skip_reset if manually doing power on/off */ + tmp_flag = mira016->skip_reset; + mira016->skip_reset = 0; + mira016_power_off(&client->dev); + mira016->skip_reset = tmp_flag; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_TRIG_ON) + { + printk(KERN_INFO "[MIRA016]: %s Enable illumination trigger.\n", __func__); + mira016->illum_enable = 1; + mira016_write_illum_trig_regs(mira016); + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_TRIG_OFF) + { + printk(KERN_INFO "[MIRA016]: %s Disable illumination trigger.\n", __func__); + mira016->illum_enable = 0; + mira016_write_illum_trig_regs(mira016); + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_WIDTH) + { + // Combine all 24 bits of reg_addr and reg_val as ILLUM_WIDTH. + u32 illum_width = value & 0x00FFFFFF; + printk(KERN_INFO "[MIRA016]: %s Set ILLUM_WIDTH to 0x%X.\n", __func__, illum_width); + mira016->illum_width = illum_width; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_DELAY) + { + // Combine reg_addr and reg_val, then select 20 bits from [19:0] as ILLUM_DELAY. + u32 illum_delay = value & 0x000FFFFF; + printk(KERN_INFO "[MIRA016]: %s Set ILLUM_DELAY to 0x%X.\n", __func__, illum_delay); + mira016->illum_delay = illum_delay; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_EXP_T_ON) + { + printk(KERN_INFO "[MIRA016]: %s enable ILLUM_WIDTH to automatically track exposure time.\n", __func__); + mira016->illum_width_auto = 1; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_ILLUM_EXP_T_OFF) + { + printk(KERN_INFO "[MIRA016]: %s disable ILLUM_WIDTH to automatically track exposure time.\n", __func__); + mira016->illum_width_auto = 0; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_STREAM_CTRL_ON) + { + printk(KERN_INFO "[MIRA016]: %s Force stream control even if (skip_reg_upload == 1).\n", __func__); + mira016->force_stream_ctrl = 1; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA016_REG_FLAG_STREAM_CTRL_OFF) + { + printk(KERN_INFO "[MIRA016]: %s Disable stream control if (skip_reg_upload == 1).\n", __func__); + mira016->force_stream_ctrl = 0; + } + else + { + printk(KERN_INFO "[MIRA016]: %s unknown command from flag %u, ignored.\n", __func__, reg_flag); + } + } + else if (reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_FOR_READ) + { + // If it is for read, skip reagister write, cache addr and flag for read. + mira016->mira016_reg_w_cached_addr = reg_addr; + mira016->mira016_reg_w_cached_flag = reg_flag; + } + else + { + // If it is for write, select which I2C device by the flag "I2C_SEL". + if ((reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_MIRA) + { + // Before writing Mira016 register, first optionally select BANK and CONTEXT + if (reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_USE_BANK) + { + u8 bank; + u8 context; + // Set conetxt bank 0 or 1 + if (reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_BANK) + { + bank = 1; + } + else + { + bank = 0; + } + // printk(KERN_INFO "[MIRA016]: %s select bank: %u.\n", __func__, bank); + ret = mira016_write(mira016, MIRA016_BANK_SEL_REG, bank); + if (ret) + { + dev_err(&client->dev, "Error setting BANK_SEL_REG."); + return ret; + } + // Set context bank 1A or bank 1B + if (reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_CONTEXT) + { + context = 1; + } + else + { + context = 0; + } + // printk(KERN_INFO "[MIRA016]: %s select context: %u.\n", __func__, context); + ret = mira016_write(mira016, MIRA016_RW_CONTEXT_REG, context); + if (ret) + { + dev_err(&client->dev, "Error setting RW_CONTEXT."); + return ret; + } + } + // Writing the actual Mira016 register + // printk(KERN_INFO "[MIRA016]: %s write reg_addr: 0x%04X; reg_val: 0x%02X.\n", __func__, reg_addr, reg_val); + ret = mira016_write(mira016, reg_addr, reg_val); + if (ret) + { + dev_err_ratelimited(&client->dev, "Error AMS_CAMERA_CID_MIRA_REG_W reg_addr %X.\n", reg_addr); + return -EINVAL; + } + } + else if ((reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_SET_TBD) + { + /* User tries to set TBD I2C address, store reg_val to mira016->tbd_client_i2c_addr. Skip write. */ + printk(KERN_INFO "[MIRA016]: mira016->tbd_client_i2c_addr = 0x%X.\n", reg_val); + mira016->tbd_client_i2c_addr = reg_val; + } + else if ((reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_TBD) + { + if (mira016->tbd_client_i2c_addr == MIRA016PMIC_I2C_ADDR) + { + // Write PMIC. Use pre-allocated mira016->pmic_client. + printk(KERN_INFO "[MIRA016]: write pmic_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira016pmic_write(mira016->pmic_client, (u8)(reg_addr & 0xFF), reg_val); + } + else if (mira016->tbd_client_i2c_addr == MIRA016UC_I2C_ADDR) + { + // Write micro-controller. Use pre-allocated mira016->uc_client. + printk(KERN_INFO "[MIRA016]: write uc_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira016pmic_write(mira016->uc_client, (u8)(reg_addr & 0xFF), reg_val); + } + else if (mira016->tbd_client_i2c_addr == MIRA016LED_I2C_ADDR) + { + // Write LED driver. Use pre-allocated mira016->led_client. + printk(KERN_INFO "[MIRA016]: write led_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira016pmic_write(mira016->led_client, (u8)(reg_addr & 0xFF), reg_val); + } + else + { + /* Write other TBD I2C address. + * The TBD I2C address is set via AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_SET_TBD. + * The TBD I2C address is stored in mira016->tbd_client_i2c_addr. + * A temporary I2C client, tmp_client, is created and then destroyed (unregistered). + */ + struct i2c_client *tmp_client; + tmp_client = i2c_new_dummy_device(client->adapter, mira016->tbd_client_i2c_addr); + if (IS_ERR(tmp_client)) + return PTR_ERR(tmp_client); + printk(KERN_INFO "[MIRA016]: write tbd_client, i2c_addr %u, reg_addr 0x%X, reg_val 0x%X.\n", + mira016->tbd_client_i2c_addr, (u8)(reg_addr & 0xFF), reg_val); + ret = mira016pmic_write(tmp_client, (u8)(reg_addr & 0xFF), reg_val); + i2c_unregister_device(tmp_client); + } + } + } + + return 0; +} + +static int mira016_v4l2_reg_r(struct mira016 *mira016, u32 *value) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira016->sd); + u32 ret = 0; + + u16 reg_addr = mira016->mira016_reg_w_cached_addr; + u8 reg_flag = mira016->mira016_reg_w_cached_flag; + u8 reg_val = 0; + + *value = 0; + + if ((reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_MIRA) + { + if (reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_USE_BANK) + { + u8 bank; + u8 context; + // Set conetxt bank 0 or 1 + if (reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_BANK) + { + bank = 1; + } + else + { + bank = 0; + } + // printk(KERN_INFO "[MIRA016]: %s select bank: %u.\n", __func__, bank); + ret = mira016_write(mira016, MIRA016_BANK_SEL_REG, bank); + if (ret) + { + dev_err(&client->dev, "Error setting BANK_SEL_REG."); + return ret; + } + // Set context bank 1A or bank 1B + if (reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_CONTEXT) + { + context = 1; + } + else + { + context = 0; + } + // printk(KERN_INFO "[MIRA016]: %s select context: %u.\n", __func__, context); + ret = mira016_write(mira016, MIRA016_RW_CONTEXT_REG, context); + if (ret) + { + dev_err(&client->dev, "Error setting RW_CONTEXT."); + return ret; + } + } + ret = mira016_read(mira016, reg_addr, ®_val); + if (ret) + { + dev_err_ratelimited(&client->dev, "Error AMS_CAMERA_CID_MIRA_REG_R reg_addr %X.\n", reg_addr); + return -EINVAL; + } + } + else if ((reg_flag & AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_TBD) + { + if (mira016->tbd_client_i2c_addr == MIRA016PMIC_I2C_ADDR) + { + // Read PMIC. Use pre-allocated mira016->pmic_client. + ret = mira016pmic_read(mira016->pmic_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA016]: read pmic_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } + else if (mira016->tbd_client_i2c_addr == MIRA016UC_I2C_ADDR) + { + // Read micro-controller. Use pre-allocated mira016->uc_client. + ret = mira016pmic_read(mira016->uc_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA016]: read uc_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } + else if (mira016->tbd_client_i2c_addr == MIRA016LED_I2C_ADDR) + { + // Read LED driver. Use pre-allocated mira016->led_client. + ret = mira016pmic_read(mira016->led_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA016]: read led_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } + else + { + /* Read other TBD I2C address. + * The TBD I2C address is set via AMS_CAMERA_CID_MIRA016_REG_FLAG_I2C_SET_TBD. + * The TBD I2C address is stored in mira016->tbd_client_i2c_addr. + * A temporary I2C client, tmp_client, is created and then destroyed (unregistered). + */ + struct i2c_client *tmp_client; + tmp_client = i2c_new_dummy_device(client->adapter, mira016->tbd_client_i2c_addr); + if (IS_ERR(tmp_client)) + return PTR_ERR(tmp_client); + ret = mira016pmic_read(tmp_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA016]: read tbd_client, i2c_addr %u, reg_addr 0x%X, reg_val 0x%X.\n", + mira016->tbd_client_i2c_addr, (u8)(reg_addr & 0xFF), reg_val); + i2c_unregister_device(tmp_client); + } + } + + // Return 32-bit value that includes flags, addr, and register value + *value = ((u32)reg_flag << 24) | ((u32)reg_addr << 8) | (u32)reg_val; + + // printk(KERN_INFO "[MIRA016]: mira016_v4l2_reg_r() reg_flag: 0x%02X; reg_addr: 0x%04X, reg_val: 0x%02X.\n", + // reg_flag, reg_addr, reg_val); + + return 0; +} + +// Returns the maximum exposure time in microseconds (reg value) +static u32 mira016_calculate_max_exposure_time(u32 row_length, u32 vsize, + u32 vblank) +{ + (void)(row_length); + (void)(vsize); + (void)(vblank); + /* Mira016 does not have a max exposure limit besides register bits */ + // return row_length * (vsize + vblank) - MIRA016_GLOB_NUM_CLK_CYCLES; + return MIRA016_EXPOSURE_MAX_US; +} + +static int mira016_write_exposure_reg(struct mira016 *mira016, u32 exposure) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira016->sd); + const u32 min_exposure = MIRA016_EXPOSURE_MIN_US; + u32 max_exposure = mira016->exposure->maximum; + u32 ret = 0; + + if (exposure < min_exposure) + { + exposure = min_exposure; + } + if (exposure > max_exposure) + { + exposure = max_exposure; + } + + /* Write Bank 1 context 0 */ + ret = mira016_write(mira016, MIRA016_RW_CONTEXT_REG, 0); + ret = mira016_write(mira016, MIRA016_BANK_SEL_REG, 1); + ret = mira016_write_be32(mira016, MIRA016_EXP_TIME_L_REG, exposure); + /* Write Bank 1 context 1 */ + ret = mira016_write(mira016, MIRA016_RW_CONTEXT_REG, 1); + ret = mira016_write_be32(mira016, MIRA016_EXP_TIME_L_REG, exposure); + if (ret) + { + dev_err_ratelimited(&client->dev, "Error setting exposure time to %d", exposure); + return -EINVAL; + } + if (mira016->illum_width_auto == 1) + { + mira016->illum_width = exposure * MIRA016_DATA_RATE / 8; + mira016_write_illum_trig_regs(mira016); + } + + return 0; +} + +static int mira016_write_target_frame_time_reg(struct mira016 *mira016, u32 target_frame_time_us) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira016->sd); + u32 ret = 0; + + /* Write Bank 1 context 0 */ + ret = mira016_write(mira016, MIRA016_RW_CONTEXT_REG, 0); + ret = mira016_write(mira016, MIRA016_BANK_SEL_REG, 1); + ret = mira016_write_be32(mira016, MIRA016_TARGET_FRAME_TIME_REG, target_frame_time_us); + /* Write Bank 1 context 1 */ + ret = mira016_write(mira016, MIRA016_RW_CONTEXT_REG, 1); + ret = mira016_write_be32(mira016, MIRA016_TARGET_FRAME_TIME_REG, target_frame_time_us); + if (ret) + { + dev_err_ratelimited(&client->dev, "Error setting target frame time to %d", target_frame_time_us); + return -EINVAL; + } + + return 0; +} + +static int mira016_write_start_streaming_regs(struct mira016 *mira016) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira016->sd); + int ret = 0; + + // Set conetxt bank 0 or 1 + ret = mira016_write(mira016, MIRA016_BANK_SEL_REG, 0); + if (ret) + { + dev_err(&client->dev, "Error setting BANK_SEL_REG."); + return ret; + } + + // Set context bank 1A or bank 1B + ret = mira016_write(mira016, MIRA016_RW_CONTEXT_REG, 0); + if (ret) + { + dev_err(&client->dev, "Error setting RW_CONTEXT."); + return ret; + } + + // Raising CMD_REQ_1 to 1 for REQ_EXP + ret = mira016_write(mira016, MIRA016_CMD_REQ_1_REG, + 1); + if (ret) + { + dev_err(&client->dev, "Error setting CMD_REQ_1 to 1 for REQ_EXP."); + return ret; + } + + usleep_range(10, 20); + + // Setting CMD_REQ_1 tp 0 for REQ_EXP + ret = mira016_write(mira016, MIRA016_CMD_REQ_1_REG, + 0); + if (ret) + { + dev_err(&client->dev, "Error setting CMD_REQ_1 to 0 for REQ_EXP."); + return ret; + } + usleep_range(10, 20); + + + + + // u8 busy_status = 1; + // int poll_cnt = 0; + // int poll_cnt_max = 5; + // u8 temp; + // u32 val; + // u8 addr; + // addr=0X10; + // mira016_write(mira016, MIRA016_BANK_SEL_REG, 0); + // usleep_range(5, 10); + // mira016_write(mira016, MIRA016_OTP_COMMAND, 0); + // usleep_range(5, 10); + // mira016_write(mira016, MIRA016_OTP_ADDR, addr); + // usleep_range(5, 10); + // mira016_write(mira016, MIRA016_OTP_START, 1); + // usleep_range(5, 10); + // mira016_write(mira016, MIRA016_OTP_START, 0); + // usleep_range(5, 10); + // mira016_read(mira016, MIRA016_OTP_BUSY, &busy_status); + // usleep_range(50, 10); + // mira016_read_be32(mira016, MIRA016_OTP_DOUT, &val); + // printk(KERN_INFO "[MIRA016]: OTP reg_addr 0x%X, OTP reg_val 0x%X.\n",addr, val); + + // addr=0X11; + // mira016_write(mira016, MIRA016_BANK_SEL_REG, 0); + // usleep_range(5, 10); + // mira016_write(mira016, MIRA016_OTP_COMMAND, 0); + // usleep_range(5, 10); + // mira016_write(mira016, MIRA016_OTP_ADDR, addr); + // usleep_range(5, 10); + // mira016_write(mira016, MIRA016_OTP_START, 1); + // usleep_range(5, 10); + // mira016_write(mira016, MIRA016_OTP_START, 0); + // usleep_range(5, 10); + // mira016_read(mira016, MIRA016_OTP_BUSY, &busy_status); + // usleep_range(50, 10); + // mira016_read_be32(mira016, MIRA016_OTP_DOUT, &val); + // printk(KERN_INFO "[MIRA016]: OTP reg_addr 0x%X, OTP reg_val 0x%X.\n", addr, val); + + + + + return ret; +} + +static int mira016_write_stop_streaming_regs(struct mira016 *mira016) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira016->sd); + int ret = 0; + + // Set conetxt bank 0 or 1 + ret = mira016_write(mira016, MIRA016_BANK_SEL_REG, 0); + if (ret) + { + dev_err(&client->dev, "Error setting BANK_SEL_REG."); + return ret; + } + + // Raising CMD_HALT_BLOCK to 1 to stop streaming + ret = mira016_write(mira016, MIRA016_CMD_HALT_BLOCK_REG, + 1); + if (ret) + { + dev_err(&client->dev, "Error setting CMD_HALT_BLOCK to 1."); + return ret; + } + + usleep_range(10, 20); + + // Setting CMD_HALT_BLOCK to 0 to stop streaming + ret = mira016_write(mira016, MIRA016_CMD_HALT_BLOCK_REG, + 0); + if (ret) + { + dev_err(&client->dev, "Error setting CMD_HALT_BLOCK to 0."); + return ret; + } + usleep_range(10, 20); + + /* + * Wait for one frame to make sure sensor is set to + * software standby in V-blank + * + * frame_time = frame length rows * Tline + * Tline = line length / pixel clock (in MHz) + */ + /* + u32 frame_time; + frame_time = MIRA016_DEFAULT_FRAME_LENGTH * + MIRA016_DEFAULT_LINE_LENGTH / MIRA016_DEFAULT_PIXEL_CLOCK; + + usleep_range(frame_time, frame_time + 1000); + */ + + return ret; +} + +static int mira016_write_analog_gain_reg(struct mira016 *mira016, u8 gain) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira016->sd); + u32 num_of_regs; + u32 ret = 0; + u32 wait_us = 20000; + + // Select partial register sequence according to bit depth + if (mira016->bit_depth == 12) + { + // Select register sequence according to gain value + if (gain == 0) + { + mira016_write_stop_streaming_regs(mira016); + usleep_range(wait_us, wait_us + 100); + printk(KERN_INFO "[mira016]: Write reg sequence for analog gain x1 in 12 bit mode"); + num_of_regs = ARRAY_SIZE(partial_analog_gain_x1_12bit); + ret = mira016_write_regs(mira016, partial_analog_gain_x1_12bit, num_of_regs); + mira016_write_start_streaming_regs(mira016); + mira016->row_length = 1504; + } + else if (gain == 1) + { + mira016_write_stop_streaming_regs(mira016); + usleep_range(wait_us, wait_us + 100); + printk(KERN_INFO "[mira016]: Write reg sequence for analog gain x2 in 12 bit mode"); + num_of_regs = ARRAY_SIZE(partial_analog_gain_x2_12bit); + ret = mira016_write_regs(mira016, partial_analog_gain_x2_12bit, num_of_regs); + mira016_write_start_streaming_regs(mira016); + mira016->row_length = 2056; + } + else + { + // Other gains are not supported + printk(KERN_INFO "[mira016]: Ignore analog gain %d in 12 bit mode", gain); + } + } + else if (mira016->bit_depth == 10) + { + // Select register sequence according to gain value + if (gain == 0) + { + mira016_write_stop_streaming_regs(mira016); + usleep_range(wait_us, wait_us + 100); + // printk(KERN_INFO "[mira016]: Write reg sequence for analog gain x1 in 12 bit mode"); + num_of_regs = ARRAY_SIZE(partial_analog_gain_x1_10bit); + ret = mira016_write_regs(mira016, partial_analog_gain_x1_10bit, num_of_regs); + mira016_write_start_streaming_regs(mira016); + mira016->row_length = 1092; + } + else if (gain == 1) + { + mira016_write_stop_streaming_regs(mira016); + usleep_range(wait_us, wait_us + 100); + // printk(KERN_INFO "[mira016]: Write reg sequence for analog gain x2 in 12 bit mode"); + num_of_regs = ARRAY_SIZE(partial_analog_gain_x2_10bit); + ret = mira016_write_regs(mira016, partial_analog_gain_x2_10bit, num_of_regs); + mira016_write_start_streaming_regs(mira016); + mira016->row_length = 1094; + } + else + { + // Other gains are not supported + printk(KERN_INFO "[mira016]: Ignore analog gain %d in 12 bit mode", gain); + } + } + else if (mira016->bit_depth == 8) + { + // Select register sequence according to gain value + if (gain == 0) + { + mira016_write_stop_streaming_regs(mira016); + usleep_range(wait_us, wait_us + 100); + // printk(KERN_INFO "[mira016]: Write reg sequence for analog gain x1 in 12 bit mode"); + num_of_regs = ARRAY_SIZE(partial_analog_gain_x1_8bit); + ret = mira016_write_regs(mira016, partial_analog_gain_x1_8bit, num_of_regs); + mira016_write_start_streaming_regs(mira016); + mira016->row_length = 1042; + } + else if (gain == 1) + { + mira016_write_stop_streaming_regs(mira016); + usleep_range(wait_us, wait_us + 100); + // printk(KERN_INFO "[mira016]: Write reg sequence for analog gain x2 in 12 bit mode"); + num_of_regs = ARRAY_SIZE(partial_analog_gain_x2_8bit); + ret = mira016_write_regs(mira016, partial_analog_gain_x2_8bit, num_of_regs); + mira016_write_start_streaming_regs(mira016); + mira016->row_length = 1004; + } + else + { + // Other gains are not supported + printk(KERN_INFO "[mira016]: Ignore analog gain %d in 8 bit mode", gain); + } + } + else + { + // Other bit depths are not supported + printk(KERN_INFO "[mira016]: Ignore analog gain in %u bit mode", mira016->mode->bit_depth); + } + + if (ret) + { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + } + + // Always return 0 even if it fails + return 0; +} + +// Gets the format code if supported. Otherwise returns the default format code `codes[0]` +static u32 mira016_validate_format_code_or_default(struct mira016 *mira016, u32 code) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + unsigned int i; + printk(KERN_INFO "[MIRA016]: validate format code or default. .\n"); + + lockdep_assert_held(&mira016->mutex); + + for (i = 0; i < ARRAY_SIZE(codes); i++) + if (codes[i] == code) + break; + + if (i >= ARRAY_SIZE(codes)) + { + dev_err_ratelimited(&client->dev, "Could not set requested format code %u", code); + dev_err_ratelimited(&client->dev, "Using default format %u", codes[0]); + i = 0; + } + + return codes[i]; +} + +static void mira016_set_default_format(struct mira016 *mira016) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = &mira016->fmt; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; // MEDIA_BUS_FMT_Y10_1X10; + mira016->bit_depth = 10; + 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); + fmt->width = supported_modes[0].width; + fmt->height = supported_modes[0].height; + fmt->field = V4L2_FIELD_NONE; +} + +static int mira016_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct mira016 *mira016 = to_mira016(sd); + struct v4l2_mbus_framefmt *try_fmt_img = + v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); + struct v4l2_mbus_framefmt *try_fmt_meta = + v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); + struct v4l2_rect *try_crop; + + mutex_lock(&mira016->mutex); + + /* Initialize try_fmt for the image pad */ + try_fmt_img->width = supported_modes[0].width; + try_fmt_img->height = supported_modes[0].height; + try_fmt_img->code = mira016_validate_format_code_or_default(mira016, + MEDIA_BUS_FMT_SGRBG10_1X10); + try_fmt_img->field = V4L2_FIELD_NONE; + + /* TODO(jalv): Initialize try_fmt for the embedded metadata pad */ + try_fmt_meta->width = MIRA016_EMBEDDED_LINE_WIDTH; + try_fmt_meta->height = MIRA016_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->state, 0); + try_crop->top = MIRA016_PIXEL_ARRAY_TOP; + try_crop->left = MIRA016_PIXEL_ARRAY_LEFT; + try_crop->width = MIRA016_PIXEL_ARRAY_WIDTH; + try_crop->height = MIRA016_PIXEL_ARRAY_HEIGHT; + + mutex_unlock(&mira016->mutex); + + return 0; +} + +static int mira016_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira016 *mira016 = + container_of(ctrl->handler, struct mira016, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + int ret = 0; + // u32 target_frame_time_us; + + // Debug print + // printk(KERN_INFO "[MIRA016]: mira016_set_ctrl() id: 0x%X value: 0x%X.\n", ctrl->id, ctrl->val); + + if (ctrl->id == V4L2_CID_VBLANK) + { + int exposure_max, exposure_def; + + /* Update max exposure while meeting expected vblanking */ + exposure_max = mira016_calculate_max_exposure_time(MIRA016_MIN_ROW_LENGTH, + mira016->mode->height, + ctrl->val); + exposure_def = (exposure_max < MIRA016_DEFAULT_EXPOSURE_US) ? exposure_max : MIRA016_DEFAULT_EXPOSURE_US; + __v4l2_ctrl_modify_range(mira016->exposure, + mira016->exposure->minimum, + (int)(1 + exposure_max / MIRA016_MIN_ROW_LENGTH_US), mira016->exposure->step, + (int)(1 + exposure_def / MIRA016_MIN_ROW_LENGTH_US)); + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (pm_runtime_get_if_in_use(&client->dev) == 0) + { + dev_info(&client->dev, + "device in use, ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + return 0; + } + + if (mira016->skip_reg_upload == 0) + { + switch (ctrl->id) + { + case V4L2_CID_ANALOGUE_GAIN: + ret = mira016_write_analog_gain_reg(mira016, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + printk(KERN_INFO "[MIRA016]: exposure line = %u, exposure us = %u.\n", ctrl->val, ctrl->val); + ret = mira016_write_exposure_reg(mira016, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = mira016_write(mira016, MIRA016_BANK_SEL_REG, 0); + // Fixed data is hard coded to 0xAB. + ret = mira016_write(mira016, MIRA016_TRAINING_WORD_REG, 0xAB); + // Gradient is hard coded to 45 degree. + ret = mira016_write(mira016, MIRA016_DELTA_TEST_IMG_REG, 0x01); + ret = mira016_write(mira016, MIRA016_TEST_PATTERN_REG, + mira016_test_pattern_val[ctrl->val]); + break; + case V4L2_CID_HFLIP: + // TODO: HFLIP requires multiple register writes + // ret = mira016_write(mira016, MIRA016_HFLIP_REG, + // ctrl->val); + break; + case V4L2_CID_VFLIP: + // TODO: VFLIP seems not supported in Mira016 + // ret = mira016_write(mira016, MIRA016_VFLIP_REG, + // ctrl->val); + break; + case V4L2_CID_VBLANK: + /* + * In libcamera, frame time (== 1/framerate) is controlled by VBLANK: + * TARGET_FRAME_TIME (us) = 1000000 * ((1/PIXEL_RATE)*(WIDTH+HBLANK)*(HEIGHT+VBLANK)) + */ + mira016->target_frame_time_us = (u32)((u64)(1000000 * (u64)(mira016->mode->width + mira016->mode->hblank) * (u64)(mira016->mode->height + ctrl->val)) / MIRA016_PIXEL_RATE); + // Debug print + printk(KERN_INFO "[MIRA016]: mira016_write_target_frame_time_reg target_frame_time_us = %u.\n", + mira016->target_frame_time_us); + printk(KERN_INFO "[MIRA016]: width %d, hblank %d, vblank %d, height %d, ctrl->val %d.\n", + mira016->mode->width, mira016->mode->hblank, mira016->mode->min_vblank, mira016->mode->height, ctrl->val); + ret = mira016_write_target_frame_time_reg(mira016, mira016->target_frame_time_us); + break; + case V4L2_CID_HBLANK: + printk(KERN_INFO "[MIRA016]: V4L2_CID_HBLANK CALLED = %d.\n", + ctrl->val); + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + } + + pm_runtime_put(&client->dev); + + // TODO: FIXIT + return ret; +} + +static int mira016_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira016 *mira016 = + container_of(ctrl->handler, struct mira016, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + int ret = 0; + + // printk(KERN_INFO "[MIRA016]: mira016_s_ctrl() id: %X value: %X.\n", ctrl->id, ctrl->val); + + /* Previously, register writes when powered off will be buffered. + * The buffer will be written to sensor when start_streaming. + * Now, register writes happens immediately, even powered off. + * Register writes when powered off will fail. + * Users need to make sure first power on then write register. + */ + + switch (ctrl->id) + { + case AMS_CAMERA_CID_MIRA_REG_W: + ret = mira016_v4l2_reg_w(mira016, ctrl->val); + break; + default: + dev_info(&client->dev, + "set ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + + // TODO: FIXIT + return ret; +} + +static int mira016_g_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira016 *mira016 = + container_of(ctrl->handler, struct mira016, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + int ret = 0; + + // printk(KERN_INFO "[MIRA016]: mira016_g_ctrl() id: %X.\n", ctrl->id); + + /* + * Ideally, V4L2 register read should happen only when powered on. + * However, perhaps there are use cases that, + * reading other I2C addr is desired when mira sensor is powered off. + * Therefore, the check of "powered" flag is disabled for now. + */ + + switch (ctrl->id) + { + case AMS_CAMERA_CID_MIRA_REG_R: + ret = mira016_v4l2_reg_r(mira016, (u32 *)&ctrl->cur.val); + ctrl->val = ctrl->cur.val; + break; + default: + dev_info(&client->dev, + "get ctrl(id:0x%x) is not handled\n", + ctrl->id); + ret = -EINVAL; + break; + } + + // TODO: FIXIT + return ret; +} + +static const struct v4l2_ctrl_ops mira016_ctrl_ops = { + .s_ctrl = mira016_set_ctrl, +}; + +static const struct v4l2_ctrl_ops mira016_custom_ctrl_ops = { + .g_volatile_ctrl = mira016_g_ctrl, + .s_ctrl = mira016_s_ctrl, +}; + +/* list of custom v4l2 ctls */ +static struct v4l2_ctrl_config custom_ctrl_config_list[] = { + /* Do not change the name field for the controls! */ + { + .ops = &mira016_custom_ctrl_ops, + .id = AMS_CAMERA_CID_MIRA_REG_W, + .name = "mira_reg_w", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = 0, + .min = 0, + .max = 0x7FFFFFFF, + .def = 0, + .step = 1, + }, + { + .ops = &mira016_custom_ctrl_ops, + .id = AMS_CAMERA_CID_MIRA_REG_R, + .name = "mira_reg_r", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = 0, + .min = 0, + .max = 0x7FFFFFFF, + .def = 0, + .step = 1, + }, + +}; + +// This function should enumerate all the media bus formats for the requested pads. If the requested +// format index is beyond the number of avaialble formats it shall return -EINVAL; +static int mira016_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mira016 *mira016 = to_mira016(sd); + + if (code->pad >= NUM_PADS) + return -EINVAL; + + if (code->pad == IMAGE_PAD) + { + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = mira016_validate_format_code_or_default(mira016, + codes[code->index]); + } + else + { + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SENSOR_DATA; + } + + return 0; +} + +static int mira016_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct mira016 *mira016 = to_mira016(sd); + + if (fse->pad >= NUM_PADS) + return -EINVAL; + + if (fse->pad == IMAGE_PAD) + { + /* Two options about how many modes to be exposed: + * - Expose all supported_modes by ARRAY_SIZE(supported_modes). + * - Expose less modes by MIRA016_SUPPORTED_MODE_SIZE_PUBLIC. + */ + /* if (fse->index >= ARRAY_SIZE(supported_modes)) */ + if (fse->index >= MIRA016_SUPPORTED_MODE_SIZE_PUBLIC) + return -EINVAL; + + if (fse->code != mira016_validate_format_code_or_default(mira016, fse->code)) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + } + else + { + if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) + return -EINVAL; + + fse->min_width = MIRA016_EMBEDDED_LINE_WIDTH; + fse->max_width = fse->min_width; + fse->min_height = MIRA016_NUM_EMBEDDED_LINES; + fse->max_height = fse->min_height; + } + + return 0; +} + +static void mira016_reset_colorspace(struct v4l2_mbus_framefmt *fmt) +{ + 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); +} + +static void mira016_update_image_pad_format(struct mira016 *mira016, + const struct mira016_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + mira016_reset_colorspace(&fmt->format); +} + +static void mira016_update_metadata_pad_format(struct v4l2_subdev_format *fmt) +{ + fmt->format.width = MIRA016_EMBEDDED_LINE_WIDTH; + fmt->format.height = MIRA016_NUM_EMBEDDED_LINES; + fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format.field = V4L2_FIELD_NONE; +} + +static int __mira016_get_pad_format(struct mira016 *mira016, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + if (fmt->pad >= NUM_PADS) + return -EINVAL; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(&mira016->sd, sd_state, fmt->pad); + + try_fmt->code = fmt->pad == IMAGE_PAD ? mira016_validate_format_code_or_default(mira016, try_fmt->code) : MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format = *try_fmt; + } + else + { + if (fmt->pad == IMAGE_PAD) + { + mira016_update_image_pad_format(mira016, mira016->mode, + fmt); + fmt->format.code = mira016_validate_format_code_or_default(mira016, + mira016->fmt.code); + } + else + { + mira016_update_metadata_pad_format(fmt); + } + } + + return 0; +} + +static int mira016_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mira016 *mira016 = to_mira016(sd); + int ret; + + mutex_lock(&mira016->mutex); + ret = __mira016_get_pad_format(mira016, sd_state, fmt); + mutex_unlock(&mira016->mutex); + + return ret; +} + +static int mira016_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mira016 *mira016 = to_mira016(sd); + const struct mira016_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + u32 max_exposure = 0, default_exp = 0; + int rc = 0; + printk(KERN_INFO "[MIRA016]: mira016_set_pad_format() .\n"); + + if (fmt->pad >= NUM_PADS) + return -EINVAL; + + mutex_lock(&mira016->mutex); + + if (fmt->pad == IMAGE_PAD) + { + printk(KERN_INFO "[MIRA016]: fmt format code = %d. \n", fmt->format.code); + printk(KERN_INFO "[MIRA016]: some code is = %d. \n", MEDIA_BUS_FMT_SGRBG10_1X10); + + /* Validate format or use default */ + fmt->format.code = mira016_validate_format_code_or_default(mira016, + fmt->format.code); + + switch (fmt->format.code) + { + case MEDIA_BUS_FMT_SGRBG10_1X10: + printk(KERN_INFO "[MIRA016]: fmt->format.code() selects 10 bit mode.\n"); + mira016->mode = &supported_modes[0]; + mira016->bit_depth = 10; + // return 0; + break; + + case MEDIA_BUS_FMT_SGRBG12_1X12: + printk(KERN_INFO "[MIRA016]: fmt->format.code() selects 12 bit mode.\n"); + mira016->mode = &supported_modes[1]; + mira016->bit_depth = 12; + // return 0; + break; + + case MEDIA_BUS_FMT_SGRBG8_1X8: + printk(KERN_INFO "[MIRA016]: fmt->format.code() selects 8 bit mode.\n"); + mira016->mode = &supported_modes[2]; + mira016->bit_depth = 8; + // return 0; + break; + default: + printk(KERN_ERR "Unknown format requested fmt->format.code() %d", fmt->format.code); + } + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, + fmt->format.height); + mira016_update_image_pad_format(mira016, mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + { + printk(KERN_INFO "[MIRA016]: = v4l2_subdev_get_try_format. \n"); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); + *framefmt = fmt->format; + } + else if (mira016->mode != mode || + mira016->fmt.code != fmt->format.code) + { + printk(KERN_INFO "[MIRA016]: Mira016 bitdepth = %d. \n", mira016->mode->bit_depth); + + printk(KERN_INFO "[MIRA016]: Mira016 mode = %d. mode is %d \n", mira016->mode->code, mode->code); + printk(KERN_INFO "[MIRA016]: Mira016 fmt = %d. fmt is %d \n", mira016->fmt.code, fmt->format.code); + + mira016->fmt = fmt->format; + // mira016->mode = mode; + + // Update controls based on new mode (range and current value). + max_exposure = mira016_calculate_max_exposure_time(MIRA016_MIN_ROW_LENGTH, + mira016->mode->height, + mira016->mode->min_vblank); + default_exp = MIRA016_DEFAULT_EXPOSURE_US > max_exposure ? max_exposure : MIRA016_DEFAULT_EXPOSURE_US; + rc = __v4l2_ctrl_modify_range(mira016->exposure, + mira016->exposure->minimum, + (int)(1 + max_exposure), mira016->exposure->step, + (int)(1 + default_exp)); + if (rc) + { + dev_err(&client->dev, "Error setting exposure range"); + } + + printk(KERN_INFO "[MIRA016]: Mira016 VBLANK = %u.\n", + mira016->mode->min_vblank); + + rc = __v4l2_ctrl_modify_range(mira016->vblank, + mira016->mode->min_vblank, + mira016->mode->max_vblank, + 1, + MIRA016_DEFAULT_VBLANK_60); + if (rc) + { + dev_err(&client->dev, "Error setting exposure range"); + } + + // Set the current vblank value + rc = __v4l2_ctrl_s_ctrl(mira016->vblank, MIRA016_DEFAULT_VBLANK_60); + if (rc) + { + dev_err(&client->dev, "Error setting vblank value to %u", + mira016->mode->min_vblank); + } + } + } + else + { + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + { + framefmt = v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); + *framefmt = fmt->format; + } + else + { + /* Only one embedded data mode is supported */ + mira016_update_metadata_pad_format(fmt); + } + } + + mutex_unlock(&mira016->mutex); + + return 0; +} + +static int mira016_set_framefmt(struct mira016 *mira016) +{ + // TODO: There is no easy way to change frame format + switch (mira016->fmt.code) + { + case MEDIA_BUS_FMT_SGRBG10_1X10: + printk(KERN_INFO "[MIRA016]: mira016_set_framefmt() selects 10 bit mode.\n"); + mira016->mode = &supported_modes[0]; + mira016->bit_depth = 10; + return 0; + case MEDIA_BUS_FMT_SGRBG12_1X12: + printk(KERN_INFO "[MIRA016]: mira016_set_framefmt() selects 12 bit mode.\n"); + mira016->mode = &supported_modes[1]; + mira016->bit_depth = 12; + return 0; + case MEDIA_BUS_FMT_SGRBG8_1X8: + printk(KERN_INFO "[MIRA016]: mira016_set_framefmt() selects 8 bit mode.\n"); + mira016->mode = &supported_modes[2]; + mira016->bit_depth = 8; + return 0; + default: + printk(KERN_ERR "Unknown format requested %d", mira016->fmt.code); + } + + return -EINVAL; +} +/* Verify chip ID */ +static int mira016_identify_module(struct mira016 *mira016) +{ + int ret; + u8 val; + u8 i; + u32 otp_cal_val; + u32 trim_data; + u32 empty; + + ret = mira016_read(mira016, 0x25, &val); + printk(KERN_INFO "[MIRA016]: Read reg 0x%4.4x, val = 0x%x.\n", + 0x25, val); + ret = mira016_read(mira016, 0x3, &val); + printk(KERN_INFO "[MIRA016]: Read reg 0x%4.4x, val = 0x%x.\n", + 0x3, val); + ret = mira016_read(mira016, 0x4, &val); + printk(KERN_INFO "[MIRA016]: Read reg 0x%4.4x, val = 0x%x.\n", + 0x4, val); + + //otp readout + // ret = mira016_otp_read(mira016, 0x10, &otp_cal_val); + // printk(KERN_INFO "[MIRA016]: otp wafermap val reg 0x%4.4x, val = 0x%x.\n", + // 0x10, otp_cal_val); + // ret = mira016_otp_read(mira016, 0x0, &trim_data); + // printk(KERN_INFO "[MIRA016]: otp trim data val reg 0x%4.4x, val = 0x%x.\n", + // 0x0, trim_data); + // ret = mira016_otp_read(mira016, 0x50, &empty); + // printk(KERN_INFO "[MIRA016]: otp empty data val reg 0x%4.4x, val = 0x%x.\n", + // 0x50, empty); + // ret = mira016_otp_read(mira016, 0x20, &empty); + // printk(KERN_INFO "[MIRA016]: otp empty data val reg 0x%4.4x, val = 0x%x.\n", + // 0x20, empty); + + // for (i = 0; i < 0x31; i++) + // { + // empty = 0; + // usleep_range(10, 20); + // ret = mira016_otp_read(mira016, i, &empty); + // printk(KERN_INFO "[MIRA016]: otp data val reg 0x%4.4x, val = 0x%x.\n", + // i, empty); + + // } + + + return 0; +} +static const struct v4l2_rect * +__mira016_get_pad_crop(struct mira016 *mira016, struct v4l2_subdev_state *sd_state, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) + { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&mira016->sd, sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mira016->mode->crop; + } + + return NULL; +} + +static int mira016_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) + { + case V4L2_SEL_TGT_CROP: + { + struct mira016 *mira016 = to_mira016(sd); + + mutex_lock(&mira016->mutex); + sel->r = *__mira016_get_pad_crop(mira016, sd_state, sel->pad, + sel->which); + mutex_unlock(&mira016->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = MIRA016_NATIVE_WIDTH; + sel->r.height = MIRA016_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = MIRA016_PIXEL_ARRAY_TOP; + sel->r.left = MIRA016_PIXEL_ARRAY_LEFT; + sel->r.width = MIRA016_PIXEL_ARRAY_WIDTH; + sel->r.height = MIRA016_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + +static int mira016_start_streaming(struct mira016 *mira016) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + const struct mira016_reg_list *reg_list; + u32 otp_cal_val; + u32 otp_cal_val2; + + int ret; + + printk(KERN_INFO "[MIRA016]: Entering start streaming function.\n"); + + /* Follow examples of other camera driver, here use pm_runtime_resume_and_get */ + ret = pm_runtime_resume_and_get(&client->dev); + + if (ret < 0) + { + printk(KERN_INFO "[MIRA016]: get_sync failed, but continue.\n"); + pm_runtime_put_noidle(&client->dev); + return ret; + } + + /* Set current mode according to frame format bit depth */ + ret = mira016_set_framefmt(mira016); + if (ret) + { + dev_err(&client->dev, "%s failed to set frame format: %d\n", + __func__, ret); + goto err_rpm_put; + } + printk(KERN_INFO "[MIRA016]: Register sequence for %d bit mode will be used.\n", mira016->mode->bit_depth); + usleep_range(30000, 50000); + + if (mira016->skip_reg_upload == 0) + { + /* Apply pre soft reset default values of current mode */ + reg_list = &mira016->mode->reg_list_pre_soft_reset; + printk(KERN_INFO "[MIRA016]: Write %d regs.\n", reg_list->num_of_regs); + ret = mira016_write_regs(mira016, reg_list->regs, reg_list->num_of_regs); + if (ret) + { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + goto err_rpm_put; + } + + + } + else + { + printk(KERN_INFO "[MIRA016]: Skip base register sequence upload, due to mira016->skip_reg_upload=%u.\n", mira016->skip_reg_upload); + } + + printk(KERN_INFO "[MIRA016]: Entering v4l2 ctrl handler setup function.\n"); + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(mira016->sd.ctrl_handler); + printk(KERN_INFO "[MIRA016]: __v4l2_ctrl_handler_setup ret = %d.\n", ret); + if (ret) + goto err_rpm_put; + + usleep_range(80000, 100000); + + + + /* Read OTP memory for OTP_CALIBRATION_VALUE */ + ret = mira016_otp_read(mira016, 0x10, &otp_cal_val); + printk(KERN_INFO "[MIRA016]: Due to OTP trim val 0x10 : %u. pointer address %p\n", otp_cal_val, &otp_cal_val); + + + + // /* Read OTP memory for OTP_CALIBRATION_VALUE */ + // ret = mira016_otp_read(mira016, 0x0, &otp_cal_val2); + // printk(KERN_INFO "[MIRA016]: Due to OTP trim val 0x00 : %u.\n", otp_cal_val2); + + + /* OTP_CALIBRATION_VALUE is little-endian, LSB at [7:0], MSB at [15:8] */ + mira016->otp_cal_val = otp_cal_val ; + if (ret) + { + dev_err(&client->dev, "%s failed to read OTP addr 0x01.\n", __func__); + /* Even if OTP reading fails, continue with the rest. */ + printk(KERN_INFO "[MIRA016]: Due to OTP reading failure, use default mira016->otp_cal_val : %u.\n", mira016->otp_cal_val); + /* goto err_rpm_put; */ + } + else + { + printk(KERN_INFO "[MIRA016]: OTP_CALIBRATION_VALUE: %u, extracted from 32-bit 0x%X.\n", mira016->otp_cal_val, otp_cal_val); + if ((otp_cal_val ) < 0xFFFFF000 && otp_cal_val != 0xFFFFFFFF) + { + printk(KERN_INFO "[MIRA016]: UNPROGRAMMED OTP : %u.\n", mira016->otp_cal_val); + usleep_range(10, 50); + + /* Apply default trimming values */ + reg_list = &mira016->mode->reg_list_post_soft_reset; + printk(KERN_INFO "[MIRA016]: Write %d regs.\n", reg_list->num_of_regs); + ret = mira016_write_regs(mira016, reg_list->regs, reg_list->num_of_regs); + if (ret) + { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + goto err_rpm_put; + } + } + + } + + if (mira016->skip_reg_upload == 0 || + (mira016->skip_reg_upload == 1 && mira016->force_stream_ctrl == 1)) + { + printk(KERN_INFO "[MIRA016]: Writing start streaming regs.\n"); + ret = mira016_write_start_streaming_regs(mira016); + if (ret) + { + dev_err(&client->dev, "Could not write stream-on sequence"); + goto err_rpm_put; + } + } + else + { + printk(KERN_INFO "[MIRA016]: Skip write_start_streaming_regs due to skip_reg_upload == %d and force_stream_ctrl == %d.\n", + mira016->skip_reg_upload, mira016->force_stream_ctrl); + } + + /* vflip and hflip cannot change during streaming */ + printk(KERN_INFO "[MIRA016]: Entering v4l2 ctrl grab vflip grab vflip.\n"); + __v4l2_ctrl_grab(mira016->vflip, true); + printk(KERN_INFO "[MIRA016]: Entering v4l2 ctrl grab vflip grab hflip.\n"); + __v4l2_ctrl_grab(mira016->hflip, true); + + printk(KERN_INFO "[MIRA016]: %s Enable illumination trigger.\n", __func__); + mira016->illum_enable = 1; + mira016_write_illum_trig_regs(mira016); + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); + return ret; +} + +static void mira016_stop_streaming(struct mira016 *mira016) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + int ret = 0; + + /* Unlock controls for vflip and hflip */ + __v4l2_ctrl_grab(mira016->vflip, false); + __v4l2_ctrl_grab(mira016->hflip, false); + + if (mira016->skip_reset == 0) + { + if (mira016->skip_reg_upload == 0 || + (mira016->skip_reg_upload == 1 && mira016->force_stream_ctrl == 1)) + { + printk(KERN_INFO "[MIRA016]: Writing stop streaming regs.\n"); + ret = mira016_write_stop_streaming_regs(mira016); + if (ret) + { + dev_err(&client->dev, "Could not write the stream-off sequence"); + } + } + else + { + printk(KERN_INFO "[MIRA016]: Skip write_stop_streaming_regs due to skip_reg_upload == %d and force_stream_ctrl == %d.\n", + mira016->skip_reg_upload, mira016->force_stream_ctrl); + } + } + else + { + printk(KERN_INFO "[MIRA016]: Skip write_stop_streaming_regs due to mira016->skip_reset == %d.\n", mira016->skip_reset); + } + + pm_runtime_put(&client->dev); +} + +static int mira016_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct mira016 *mira016 = to_mira016(sd); + int ret = 0; + + mutex_lock(&mira016->mutex); + if (mira016->streaming == enable) + { + mutex_unlock(&mira016->mutex); + return 0; + } + + printk(KERN_INFO "[MIRA016]: Entering mira016_set_stream enable: %d.\n", enable); + + if (enable) + { + /* + * Apply default & customized values + * and then start streaming. + */ + ret = mira016_start_streaming(mira016); + if (ret) + goto err_unlock; + } + else + { + mira016_stop_streaming(mira016); + } + + mira016->streaming = enable; + + mutex_unlock(&mira016->mutex); + + printk(KERN_INFO "[MIRA016]: Returning mira016_set_stream with ret: %d.\n", ret); + + return ret; + +err_unlock: + mutex_unlock(&mira016->mutex); + + return ret; +} + +static int __maybe_unused mira016_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira016 *mira016 = to_mira016(sd); + + printk(KERN_INFO "[MIRA016]: Entering suspend function.\n"); + + if (mira016->streaming) + mira016_stop_streaming(mira016); + + return 0; +} + +static int __maybe_unused mira016_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira016 *mira016 = to_mira016(sd); + int ret; + + printk(KERN_INFO "[MIRA016]: Entering resume function.\n"); + + if (mira016->streaming) + { + ret = mira016_start_streaming(mira016); + if (ret) + goto error; + } + + return 0; + +error: + mira016_stop_streaming(mira016); + mira016->streaming = false; + + return ret; +} + +static int mira016_get_regulators(struct mira016 *mira016) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + unsigned int i; + + for (i = 0; i < MIRA016_NUM_SUPPLIES; i++) + mira016->supplies[i].supply = mira016_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, + MIRA016_NUM_SUPPLIES, + mira016->supplies); +} + + + +static const struct v4l2_subdev_core_ops mira016_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops mira016_video_ops = { + .s_stream = mira016_set_stream, +}; + +static const struct v4l2_subdev_pad_ops mira016_pad_ops = { + .enum_mbus_code = mira016_enum_mbus_code, + .get_fmt = mira016_get_pad_format, + .set_fmt = mira016_set_pad_format, + .get_selection = mira016_get_selection, + .enum_frame_size = mira016_enum_frame_size, +}; + +static const struct v4l2_subdev_ops mira016_subdev_ops = { + .core = &mira016_core_ops, + .video = &mira016_video_ops, + .pad = &mira016_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mira016_internal_ops = { + .open = mira016_open, +}; + +/* Initialize control handlers */ +static int mira016_init_controls(struct mira016 *mira016) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira016->sd); + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_fwnode_device_properties props; + int ret; + struct v4l2_ctrl_config *mira016_reg_w; + struct v4l2_ctrl_config *mira016_reg_r; + + ctrl_hdlr = &mira016->ctrl_handler; + /* v4l2_ctrl_handler_init gives a hint/guess of the number of v4l2_ctrl_new */ + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16); + if (ret) + return ret; + + mutex_init(&mira016->mutex); + ctrl_hdlr->lock = &mira016->mutex; + + printk(KERN_INFO "[MIRA016]: %s V4L2_CID_PIXEL_RATE %X.\n", __func__, V4L2_CID_PIXEL_RATE); + + /* By default, PIXEL_RATE is read only */ + mira016->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &mira016_ctrl_ops, + V4L2_CID_PIXEL_RATE, + MIRA016_PIXEL_RATE, + MIRA016_PIXEL_RATE, 1, + MIRA016_PIXEL_RATE); + + printk(KERN_INFO "[MIRA016]: %s V4L2_CID_VBLANK %X.\n", __func__, V4L2_CID_VBLANK); + + mira016->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &mira016_ctrl_ops, + V4L2_CID_VBLANK, mira016->mode->min_vblank, + mira016->mode->max_vblank, 1, + MIRA016_DEFAULT_VBLANK_60); + + printk(KERN_INFO "[MIRA016]: %s V4L2_CID_HBLANK %X.\n", __func__, V4L2_CID_HBLANK); + + mira016->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &mira016_ctrl_ops, + V4L2_CID_HBLANK, mira016->mode->hblank, + mira016->mode->hblank, 1, + mira016->mode->hblank); + + // Make the vblank control read only. This could be changed to allow changing framerate in + // runtime, but would require adapting other settings + mira016->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + // Exposure is indicated in number of lines here + // Max is determined by vblank + vsize and Tglob. + printk(KERN_INFO "[MIRA016]: %s V4L2_CID_EXPOSURE %X.\n", __func__, V4L2_CID_EXPOSURE); + + mira016->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &mira016_ctrl_ops, + V4L2_CID_EXPOSURE, + MIRA016_EXPOSURE_MIN_US, MIRA016_EXPOSURE_MAX_US, + 1, + MIRA016_DEFAULT_EXPOSURE_US); + + printk(KERN_INFO "[MIRA016]: %s V4L2_CID_ANALOGUE_GAIN %X.\n", __func__, V4L2_CID_ANALOGUE_GAIN); + + mira016->gain = v4l2_ctrl_new_std(ctrl_hdlr, &mira016_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + MIRA016_ANALOG_GAIN_MIN, MIRA016_ANALOG_GAIN_MAX, + MIRA016_ANALOG_GAIN_STEP, MIRA016_ANALOG_GAIN_DEFAULT); + + printk(KERN_INFO "[MIRA016]: %s V4L2_CID_HFLIP %X.\n", __func__, V4L2_CID_HFLIP); + + mira016->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &mira016_ctrl_ops, + V4L2_CID_HFLIP, 0, 0, 1, 0); + if (mira016->hflip) + mira016->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + printk(KERN_INFO "[MIRA016]: %s V4L2_CID_VFLIP %X.\n", __func__, V4L2_CID_VFLIP); + + mira016->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &mira016_ctrl_ops, + V4L2_CID_VFLIP, 0, 0, 1, 0); + if (mira016->vflip) + mira016->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + printk(KERN_INFO "[MIRA016]: %s V4L2_CID_TEST_PATTERN %X.\n", __func__, V4L2_CID_TEST_PATTERN); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &mira016_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(mira016_test_pattern_menu) - 1, + 0, 0, mira016_test_pattern_menu); + /* + * Custom op + */ + mira016_reg_w = &custom_ctrl_config_list[0]; + printk(KERN_INFO "[MIRA016]: %s AMS_CAMERA_CID_MIRA_REG_W %X.\n", __func__, AMS_CAMERA_CID_MIRA_REG_W); + mira016->mira016_reg_w = v4l2_ctrl_new_custom(ctrl_hdlr, mira016_reg_w, NULL); + + mira016_reg_r = &custom_ctrl_config_list[1]; + printk(KERN_INFO "[MIRA016]: %s AMS_CAMERA_CID_MIRA_REG_R %X.\n", __func__, AMS_CAMERA_CID_MIRA_REG_R); + mira016->mira016_reg_r = v4l2_ctrl_new_custom(ctrl_hdlr, mira016_reg_r, NULL); + if (mira016->mira016_reg_r) + mira016->mira016_reg_r->flags |= (V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY); + + 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, &mira016_ctrl_ops, + &props); + if (ret) + goto error; + + mira016->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + mutex_destroy(&mira016->mutex); + + return ret; +} + +static void mira016_free_controls(struct mira016 *mira016) +{ + v4l2_ctrl_handler_free(mira016->sd.ctrl_handler); + mutex_destroy(&mira016->mutex); +} + +static int mira016_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY}; + int ret = -EINVAL; + + 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; + } + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes != 1) + { + dev_err(dev, "only 1 data lanes are currently supported\n"); + goto error_out; + } + + /* Check the link frequency set in device tree */ + if (!ep_cfg.nr_of_link_frequencies) + { + dev_err(dev, "link-frequency property not found in DT\n"); + goto error_out; + } + + if (ep_cfg.nr_of_link_frequencies != 1 || + ep_cfg.link_frequencies[0] != MIRA016_DEFAULT_LINK_FREQ) + { + dev_err(dev, "Link frequency not supported: %lld\n", + ep_cfg.link_frequencies[0]); + goto error_out; + } + + // TODO(jalv): Check device tree configuration and make sure it is supported by the driver + ret = 0; + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int mira016pmic_init_controls(struct i2c_client *pmic_client, struct i2c_client *uc_client) +{ + int ret; + u8 val; + + // uC, set atb and jtag high + // according to old uC fw (svn rev41) + // 12[3] ldo en + // 11[4,5] atpg jtag + // 11/12 i/o direction, 15/16 output high/low + // uC, set atb and jtag high + // WARNING this only works on interposer v2 if R307 is not populated. otherwise, invert the bit for ldo + ret = mira016pmic_write(uc_client, 12, 0xF7); + ret = mira016pmic_write(uc_client, 16, 0xFF); // ldo en:1 + ret = mira016pmic_write(uc_client, 11, 0XCF); + ret = mira016pmic_write(uc_client, 15, 0xFF); + ret = mira016pmic_write(uc_client, 6, 1); // write + + // Disable master switch // + ret = mira016pmic_write(pmic_client, 0x62, 0x00); + + // Set all voltages to 0 + + // DCDC1=0V + ret = mira016pmic_write(pmic_client, 0x05, 0x00); + // DCDC4=0V + ret = mira016pmic_write(pmic_client, 0x0E, 0x0); + // LDO1=0V VDDLO_PLL + ret = mira016pmic_write(pmic_client, 0x11, 0x0); + // LDO2=0.0V + ret = mira016pmic_write(pmic_client, 0x14, 0x00); + // LDO3=0.0V + ret = mira016pmic_write(pmic_client, 0x17, 0x00); + // LDO4=0V + ret = mira016pmic_write(pmic_client, 0x1A, 0x00); + // LDO5=0.0V + ret = mira016pmic_write(pmic_client, 0x1C, 0x00); + // LDO6=0.0V + ret = mira016pmic_write(pmic_client, 0x1D, 0x00); + // LDO7=0V + ret = mira016pmic_write(pmic_client, 0x1E, 0x0); + // LDO8=0.0V + ret = mira016pmic_write(pmic_client, 0x1F, 0x00); + // Disable LDO9 Lock + ret = mira016pmic_write(pmic_client, 0x24, 0x48); + // LDO9=0V VDDHI + ret = mira016pmic_write(pmic_client, 0x20, 0x00); + // LDO10=0V VDDLO_ANA + ret = mira016pmic_write(pmic_client, 0x21, 0x0); + + // Enable master switch // + usleep_range(50, 60); + ret = mira016pmic_write(pmic_client, 0x62, 0x0D); // enable master switch + usleep_range(50, 60); + + // start PMIC + // Keep LDOs always on + ret = mira016pmic_write(pmic_client, 0x27, 0xFF); + ret = mira016pmic_write(pmic_client, 0x28, 0xFF); + ret = mira016pmic_write(pmic_client, 0x29, 0x00); + ret = mira016pmic_write(pmic_client, 0x2A, 0x00); + ret = mira016pmic_write(pmic_client, 0x2B, 0x00); + + // Unused LDO off // + usleep_range(50, 60); + // set GPIO1=0 + ret = mira016pmic_write(pmic_client, 0x41, 0x04); + // DCDC2=0.0V SPARE_PWR1 + ret = mira016pmic_write(pmic_client, 0x01, 0x00); + ret = mira016pmic_write(pmic_client, 0x08, 0x00); + // DCDC3=0V SPARE_PWR1 + ret = mira016pmic_write(pmic_client, 0x02, 0x00); + ret = mira016pmic_write(pmic_client, 0x0B, 0x00); + // LDO2=0.0V + ret = mira016pmic_write(pmic_client, 0x14, 0x00); + // LDO3=0.0V + ret = mira016pmic_write(pmic_client, 0x17, 0x00); + // LDO5=0.0V + ret = mira016pmic_write(pmic_client, 0x1C, 0x00); + // LDO6=0.0V + ret = mira016pmic_write(pmic_client, 0x1D, 0x00); + // LDO8=0.0V + ret = mira016pmic_write(pmic_client, 0x1F, 0x00); + + ret = mira016pmic_write(pmic_client, 0x42, 4); + + // Enable 1.80V // + usleep_range(50, 60); + // DCDC1=1.8V VINLDO1p8 >=1P8 + ret = mira016pmic_write(pmic_client, 0x00, 0x00); + ret = mira016pmic_write(pmic_client, 0x04, 0x34); + ret = mira016pmic_write(pmic_client, 0x06, 0xBF); + ret = mira016pmic_write(pmic_client, 0x05, 0xB4); + // DCDC4=1.8V VDDIO + ret = mira016pmic_write(pmic_client, 0x03, 0x00); + ret = mira016pmic_write(pmic_client, 0x0D, 0x34); + ret = mira016pmic_write(pmic_client, 0x0F, 0xBF); + ret = mira016pmic_write(pmic_client, 0x0E, 0xB4); + + // Enable 2.85V // + usleep_range(50, 60); + // LDO4=2.85V VDDHI alternativ + ret = mira016pmic_write(pmic_client, 0x1A, 0xB8); // Either 0x00 or 0xB8 + // Disable LDO9 Lock + ret = mira016pmic_write(pmic_client, 0x24, 0x48); + // LDO9=2.85V VDDHI + ret = mira016pmic_read(pmic_client, 0x20, &val); + dev_err(&pmic_client->dev, "Read 0x20 with val %x\n", val); + ret = mira016pmic_write(pmic_client, 0x20, 0xB9); + ret = mira016pmic_read(pmic_client, 0x20, &val); + dev_err(&pmic_client->dev, "Read 0x20 with val %x\n", val); + + // VPIXH on cob = vdd25A on interposer = LDO4 on pmic + // VPIXH should connect to VDD28 on pcb, or enable 4th supply + ret = mira016pmic_read(pmic_client, 0x19, &val); + dev_err(&pmic_client->dev, "Read 0x19 with val %x\n", val); + ret = mira016pmic_write(pmic_client, 0x19, 0x38); + ret = mira016pmic_read(pmic_client, 0x19, &val); + dev_err(&pmic_client->dev, "Read 0x19 with val %x\n", val); + + // Enable 1.2V // + usleep_range(700, 710); + // LDO1=1.2V VDDLO_PLL + ret = mira016pmic_write(pmic_client, 0x12, 0x16); + ret = mira016pmic_write(pmic_client, 0x10, 0x16); + ret = mira016pmic_write(pmic_client, 0x11, 0x90); + // LDO7=1.2V VDDLO_DIG + ret = mira016pmic_write(pmic_client, 0x1E, 0x90); + // LDO10=1.2V VDDLO_ANA + ret = mira016pmic_write(pmic_client, 0x21, 0x90); + + // Enable green LED // + usleep_range(50, 60); + ret = mira016pmic_write(pmic_client, 0x42, 0x15); // gpio2 + // ret = mira016pmic_write(pmic_client, 0x43, 0x40); // leda + // ret = mira016pmic_write(pmic_client, 0x44, 0x40); // ledb + ret = mira016pmic_write(pmic_client, 0x45, 0x40); // ledc + + // ret = mira016pmic_write(pmic_client, 0x47, 0x02); // leda ctrl1 + // ret = mira016pmic_write(pmic_client, 0x4F, 0x02); // ledb ctrl1 + ret = mira016pmic_write(pmic_client, 0x57, 0x02); // ledc ctrl1 + + // ret = mira016pmic_write(pmic_client, 0x4D, 0x01); // leda ctrl1 + // ret = mira016pmic_write(pmic_client, 0x55, 0x10); // ledb ctrl7 + ret = mira016pmic_write(pmic_client, 0x5D, 0x10); // ledc ctrl7 + ret = mira016pmic_write(pmic_client, 0x61, 0x10); // led seq -- use this to turn on leds. abc0000- 1110000 for all leds + + // uC, set atb and jtag high and ldo_en + ret = mira016pmic_write(uc_client, 12, 0xF7); + ret = mira016pmic_write(uc_client, 16, 0xF7); // ldo en:0 + /* + * In Mira016-bringup.py, write 11, 0xCF; 15: 0x30. + * In mira016.py, write 11, 0x8D; 15, 0xFD. + */ + ret = mira016pmic_write(uc_client, 11, 0X8D); + ret = mira016pmic_write(uc_client, 15, 0xFD); + ret = mira016pmic_write(uc_client, 6, 1); // write + + usleep_range(2000000, 2001000); + + return 0; +} + +static int mira016_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct mira016 *mira016; + int ret; + + printk(KERN_INFO "[MIRA016]: probing v4l2 sensor.\n"); + printk(KERN_INFO "[MIRA016]: Driver Version 0.0.\n"); + + dev_err(dev, "[MIRA016] name: %s.\n", client->name); + + mira016 = devm_kzalloc(&client->dev, sizeof(*mira016), GFP_KERNEL); + if (!mira016) + return -ENOMEM; + + v4l2_i2c_subdev_init(&mira016->sd, client, &mira016_subdev_ops); + + /* Check the hardware configuration in device tree */ + if (mira016_check_hwcfg(dev)) + return -EINVAL; + + /* Parse device tree to check if dtoverlay has param skip-reg-upload=1 */ + device_property_read_u32(dev, "skip-reg-upload", &mira016->skip_reg_upload); + printk(KERN_INFO "[MIRA016]: skip-reg-upload %d.\n", mira016->skip_reg_upload); + /* Set default TBD I2C device address to LED I2C Address*/ + mira016->tbd_client_i2c_addr = MIRA016LED_I2C_ADDR; + printk(KERN_INFO "[MIRA016]: User defined I2C device address defaults to LED driver I2C address 0x%X.\n", mira016->tbd_client_i2c_addr); + + /* Get system clock (xclk) */ + mira016->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(mira016->xclk)) + { + dev_err(dev, "failed to get xclk\n"); + return PTR_ERR(mira016->xclk); + } + + mira016->xclk_freq = clk_get_rate(mira016->xclk); + if (mira016->xclk_freq != MIRA016_SUPPORTED_XCLK_FREQ) + { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + mira016->xclk_freq); + return -EINVAL; + } + + ret = mira016_get_regulators(mira016); + if (ret) + { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + { + printk(KERN_INFO "[MIRA016]: Init PMIC and uC and led driver.\n"); + mira016->pmic_client = i2c_new_dummy_device(client->adapter, + MIRA016PMIC_I2C_ADDR); + if (IS_ERR(mira016->pmic_client)) + return PTR_ERR(mira016->pmic_client); + mira016->uc_client = i2c_new_dummy_device(client->adapter, + MIRA016UC_I2C_ADDR); + if (IS_ERR(mira016->uc_client)) + return PTR_ERR(mira016->uc_client); + mira016->led_client = i2c_new_dummy_device(client->adapter, + MIRA016LED_I2C_ADDR); + if (IS_ERR(mira016->led_client)) + return PTR_ERR(mira016->led_client); + mira016pmic_init_controls(mira016->pmic_client, mira016->uc_client); + } + + dev_err(dev, "[MIRA016] Sleep for 1 second to let PMIC driver complete init.\n"); + // set some defaults + + /* + * The sensor must be powered for mira016_identify_module() + * to be able to read the CHIP_ID register + */ + ret = mira016_power_on(dev); + if (ret) + return ret; + usleep_range(100000, 100000 + 500); + + printk(KERN_INFO "[MIRA016]: Entering identify function.\n"); + + // ret = mira016_identify_module(mira016); + if (ret) + goto error_power_off; + + printk(KERN_INFO "[MIRA016]: Setting support function.\n"); + + /* Initialize default illumination trigger parameters */ + /* ILLUM_WIDTH is in unit of SEQ_TIME_BASE, equal to (8/MIRA016_DATA_RATE) us. */ + mira016->illum_width = MIRA016_ILLUM_WIDTH_DEFAULT; + /* ILLUM_WIDTH AUTO will match illum to exposure pulse width*/ + mira016->illum_width_auto = MIRA016_ILLUM_SYNC_DEFAULT; + /* ILLUM_ENABLE is True or False, enabling it will activate illum trig. */ + mira016->illum_enable = MIRA016_ILLUM_ENABLE_DEFAULT; + /* ILLUM_DELAY is in unit of TIME_UNIT, equal to 1 us. In continuous stream mode, zero delay is 1<<19. */ + mira016->illum_delay = MIRA016_ILLUM_DELAY_DEFAULT; + /* Set default mode to max resolution */ + mira016->mode = &supported_modes[1]; + + mira016->row_length = MIRA016_ROW_LENGTH; + printk(KERN_INFO "[MIRA016]: Entering init controls function.\n"); + + ret = mira016_init_controls(mira016); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + mira016->sd.internal_ops = &mira016_internal_ops; + mira016->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + mira016->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pads */ + mira016->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; + mira016->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; + + printk(KERN_INFO "[MIRA016]: Entering set default format function.\n"); + + /* Initialize default format */ + mira016_set_default_format(mira016); + + printk(KERN_INFO "[MIRA016]: Entering pads init function.\n"); + + ret = media_entity_pads_init(&mira016->sd.entity, NUM_PADS, mira016->pad); + if (ret) + { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + printk(KERN_INFO "[MIRA016]: Entering subdev sensor common function.\n"); + + ret = v4l2_async_register_subdev_sensor(&mira016->sd); + if (ret < 0) + { + dev_err(dev, "failed to register sensor sub-device: %d\n", ret); + goto error_media_entity; + } + + /* For debug purpose */ + // mira016_start_streaming(mira016); + // mira016_identify_module(mira016); + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&mira016->sd.entity); + +error_handler_free: + mira016_free_controls(mira016); + +error_power_off: + mira016_power_off(dev); + + i2c_unregister_device(mira016->pmic_client); + i2c_unregister_device(mira016->uc_client); + i2c_unregister_device(mira016->led_client); + + return ret; +} + +static void mira016_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira016 *mira016 = to_mira016(sd); + + i2c_unregister_device(mira016->pmic_client); + i2c_unregister_device(mira016->uc_client); + i2c_unregister_device(mira016->led_client); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + mira016_free_controls(mira016); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + mira016_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct dev_pm_ops mira016_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mira016_suspend, mira016_resume) + SET_RUNTIME_PM_OPS(mira016_power_off, mira016_power_on, NULL)}; + +#endif // __MIRA016_INL__ diff --git a/drivers/media/i2c/mira050.c b/drivers/media/i2c/mira050.c new file mode 100644 index 00000000000000..19d6ad76eb562b --- /dev/null +++ b/drivers/media/i2c/mira050.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ams MIRA050 cameras. + * Copyright (C) 2022, ams-OSRAM + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + */ + +#include "mira050.inl" + +static const struct of_device_id mira050_dt_ids[] = { + { .compatible = "ams,mira050" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mira050_dt_ids); + +static const struct i2c_device_id mira050_ids[] = { + { "mira050", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mira050_ids); + +static struct i2c_driver mira050_i2c_driver = { + .driver = { + .name = "mira050", + .of_match_table = mira050_dt_ids, + .pm = &mira050_pm_ops, + }, + .probe = mira050_probe, + .remove = mira050_remove, + .id_table = mira050_ids, +}; + +module_i2c_driver(mira050_i2c_driver); + +MODULE_AUTHOR("Zhenyu Ye "); +MODULE_DESCRIPTION("ams MIRA050 sensor driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/i2c/mira050.inl b/drivers/media/i2c/mira050.inl new file mode 100644 index 00000000000000..81460221126588 --- /dev/null +++ b/drivers/media/i2c/mira050.inl @@ -0,0 +1,5513 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ams MIRA050 cameras. + * Copyright (C) 2022, ams-OSRAM + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + */ + +#ifndef __MIRA050_INL__ +#define __MIRA050_INL__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Introduce new v4l2 control + */ +#include +#define AMS_CAMERA_CID_BASE (V4L2_CTRL_CLASS_CAMERA | 0x2000) +#define AMS_CAMERA_CID_MIRA_REG_W (AMS_CAMERA_CID_BASE + 0) +#define AMS_CAMERA_CID_MIRA_REG_R (AMS_CAMERA_CID_BASE + 1) + +/* Most significant Byte is flag, and most significant bit is unused. */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_FOR_READ 0b00000001 +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_USE_BANK 0b00000010 +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_BANK 0b00000100 +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_CONTEXT 0b00001000 +/* Use bit 5 to indicate special command, bit 1,2,3,4 for command. */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_CMD_SEL 0b00010000 +/* Special command for sleep. The other 3 Bytes (addr+val) is sleep values in us. */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_SLEEP_US 0b00010000 +/* Special command to enable power on (/off) when stream on (/off). */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_RESET_ON 0b00010010 +/* Special command to disable power on (/off) when stream on (/off). */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_RESET_OFF 0b00010100 +/* Special command to enable base register sequence upload, overwrite skip-reg-upload in dtoverlay */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_REG_UP_ON 0b00010110 +/* Special command to disable base register sequence upload, overwrite skip-reg-upload in dtoverlay */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_REG_UP_OFF 0b00011000 +/* Special command to manually power on */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_POWER_ON 0b00011010 +/* Special command to manually power off */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_POWER_OFF 0b00011100 +/* Special command to turn illumination trigger on */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_TRIG_ON 0b00011110 +/* Special command to turn illumination trigger off */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_TRIG_OFF 0b00010001 +/* Special command to set ILLUM_WIDTH. The other 3 Bytes (addr+val) is width value. */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_WIDTH 0b00010011 +/* Special command to set ILLUM_DELAY. The other 3 Bytes (addr+val) is width value. */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_DELAY 0b00010101 +/* Special command to enable ILLUM_WIDTH automatically tracking exposure time */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_EXP_T_ON 0b00010111 +/* Special command to disable ILLUM_WIDTH automatically tracking exposure time */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_EXP_T_OFF 0b00011001 +/* Special command to enable force_stream_ctrl */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_STREAM_CTRL_ON 0b00011011 +/* Special command to disable force_stream_ctrl */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_STREAM_CTRL_OFF 0b00011101 + +/* + * Bit 6&7 of flag are combined to specify I2C dev (default is Mira). + * If bit 6&7 is 0b01, the reg_addr and reg_val are for a TBD I2C address. + * The TBD I2C address is default to MIRA050LED_I2C_ADDR. + * To change the TBD I2C address, set bit 6&7 to 0b10, + * then the reg_val will become TBD I2C address. + * The TBD I2C address is stored in mira050->tbd_client_i2c_addr. + */ +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_SEL 0b01100000 +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_MIRA 0b00000000 +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_TBD 0b00100000 +#define AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_SET_TBD 0b01000000 + +/* Pre-allocated i2c_client */ +#define MIRA050PMIC_I2C_ADDR 0x2D +#define MIRA050UC_I2C_ADDR 0x0A +#define MIRA050LED_I2C_ADDR 0x53 + +#define MIRA050_NATIVE_WIDTH 576U +#define MIRA050_NATIVE_HEIGHT 768U + +#define MIRA050_PIXEL_ARRAY_LEFT 0U +#define MIRA050_PIXEL_ARRAY_TOP 0U +#define MIRA050_PIXEL_ARRAY_WIDTH 576U +#define MIRA050_PIXEL_ARRAY_HEIGHT 768U + +#define MIRA050_ANALOG_GAIN_MAX 2 +#define MIRA050_ANALOG_GAIN_MIN 0 +#define MIRA050_ANALOG_GAIN_STEP 1 +#define MIRA050_ANALOG_GAIN_DEFAULT MIRA050_ANALOG_GAIN_MIN + +#define MIRA050_BANK_SEL_REG 0xE000 +#define MIRA050_RW_CONTEXT_REG 0xE004 +#define MIRA050_CMD_REQ_1_REG 0x000A +#define MIRA050_CMD_HALT_BLOCK_REG 0x000C + +// Exposure time is indicated in us +#define MIRA050_EXP_TIME_L_REG 0x000E +#define MIRA050_EXP_TIME_S_REG 0x0012 + +// Target frame time is indicated in us +#define MIRA050_TARGET_FRAME_TIME_REG 0x0008 +#define MIRA050_GLOB_NUM_CLK_CYCLES 1928 + +#define MIRA050_SUPPORTED_XCLK_FREQ 24000000 + +// Some timings +#define MIRA050_DATA_RATE 1500 // Mbit/s +#define MIRA050_SEQ_TIME_BASE 8 / MIRA050_DATA_RATE +#define MIRA050_LUT_DEL_008 66 // for 12bit, #TODO +#define MIRA050_GRAN_TG 1500 * 50 / MIRA050_DATA_RATE +#define MIRA050_LPS_CYCLE_TIME 12600 // 12500 + 100 +#define MIRA050_GLOB_TIME (int)((190 + MIRA050_LUT_DEL_008) * MIRA050_GRAN_TG * MIRA050_SEQ_TIME_BASE) +#define MIRA050_ROW_LENGTH 1842 // 12b +#define MIRA050_LPS_DISABLED 0 +#define MIRA050_TROW_US MIRA050_ROW_LENGTH * 8 / MIRA050_DATA_RATE + +// Default exposure is adjusted to 1 ms + +#define MIRA050_MIN_ROW_LENGTH MIRA050_ROW_LENGTH // 1042 for 8 bit +#define MIRA050_MIN_ROW_LENGTH_US (MIRA050_MIN_ROW_LENGTH * 8 / MIRA050_DATA_RATE) +#define MIRA050_EXPOSURE_MIN_US (int)(1 + (151 + MIRA050_LUT_DEL_008) * MIRA050_GRAN_TG * 8 / MIRA050_DATA_RATE) +#define MIRA050_EXPOSURE_MAX_US (1000000) +#define MIRA050_EXPOSURE_MIN_LINES (MIRA050_EXPOSURE_MIN_US/MIRA050_DEFAULT_LINE_LENGTH) +#define MIRA050_EXPOSURE_MAX_LINES (MIRA050_EXPOSURE_MAX_US/MIRA050_DEFAULT_LINE_LENGTH) + +#define MIRA050_DEFAULT_EXPOSURE_LINES 1000 +#define MIRA050_DEFAULT_EXPOSURE_US MIRA050_DEFAULT_EXPOSURE_LINES*MIRA050_DEFAULT_LINE_LENGTH +// Default exposure for V4L2 is in row time + +// #define MIRA050_MIN_VBLANK 11 // for 10b or 8b, 360fps +// 50 fps +#define MIRA050_MIN_VBLANK_60 900 + +// 50 fps +#define MIRA050_MIN_VBLANK_120 65 + +// 120 fps + +#define MIRA050_MAX_VBLANK 100000 + +// Default exposure is adjusted to 1 ms +// #define MIRA050_LUT_DEL_008 66 +// #define MIRA050_GRAN_TG 34 +// #define MIRA050_DATA_RATE 1000 // Mbit/s +// #define MIRA050_MIN_ROW_LENGTH 1842 +// #define MIRA050_MIN_ROW_LENGTH_US (MIRA050_MIN_ROW_LENGTH * 8 / MIRA050_DATA_RATE) +// #define MIRA050_EXPOSURE_MIN_US (int)(1 + (151 + MIRA050_LUT_DEL_008) * MIRA050_GRAN_TG * 8 / MIRA050_DATA_RATE) +// #define MIRA050_EXPOSURE_MIN_RT (int)(1 + (151 + MIRA050_LUT_DEL_008) * MIRA050_GRAN_TG / MIRA050_MIN_ROW_LENGTH) +// #define MIRA050_EXPOSURE_MAX_US (16601) +// #define MIRA050_EXPOSURE_MAX_RT (int)(1 + MIRA050_EXPOSURE_MAX_US / MIRA050_MIN_ROW_LENGTH_US) +// #define MIRA050_DEFAULT_EXPOSURE_US 1000 +// #define MIRA050_DEFAULT_EXPOSURE_RT (int)(1 + MIRA050_DEFAULT_EXPOSURE_US / MIRA050_MIN_ROW_LENGTH_US) + +// Power on function timing +#define MIRA050_XCLR_MIN_DELAY_US 150000 +#define MIRA050_XCLR_DELAY_RANGE_US 3000 + +// set pixel rate equal to width. such that 1 row time is 1 us. +// pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample +// 1.0Gb/s * 2 * 1 / 12 = 178956970 +#define MIRA050_PIXEL_RATE (57600000) //reduce by factor 10, otherwise driver complains... +/* Should match device tree link freq */ +#define MIRA050_DEFAULT_LINK_FREQ 456000000 +#define MIRA050_DEFAULT_LINE_LENGTH (10) // pixel rate / (HSIZE+HBLANK) + +/* Trick the libcamera with achievable fps via hblank */ + +/* Formular in libcamera to derive TARGET_FPS: + * TARGET_FPS=1/((1/MIRA050_PIXEL_RATE)*(WIDTH+HBLANK)*(HEIGHT+MIRA050_MIN_VBLANK)) + * Example with HBLANK=0 and MIRA050_MIN_VBLANK=12 + * TARGET_FPS=1/((1/178956970)*576*(768+12))=398 + * + * Inverse the above formula to derive HBLANK from TARGET_FPS: + * HBLANK=1/((1/MIRA050_PIXEL_RATE)*TARGET_FPS*(HEIGHT+MIRA050_MIN_VBLANK))-WIDTH + * Example with TARGET_FPS of 50 fps + * HBLANK=1/((1/178956970)*50*(768+12))-576=4013 + */ + +// For test pattern with fixed data +#define MIRA050_TRAINING_WORD_REG 0x0060 +// For test pattern with 2D gradiant +#define MIRA050_DELTA_TEST_IMG_REG 0x0056 +// For setting test pattern type +#define MIRA050_TEST_PATTERN_REG 0x0062 +#define MIRA050_TEST_PATTERN_DISABLE 0x00 +#define MIRA050_TEST_PATTERN_FIXED_DATA 0x01 +#define MIRA050_TEST_PATTERN_2D_GRADIENT 0x02 + +/* Embedded metadata stream structure */ +#define MIRA050_EMBEDDED_LINE_WIDTH 16384 +#define MIRA050_NUM_EMBEDDED_LINES 1 + + +#define MIRA050_GDIG_PREAMP 0x0024 +#define MIRA050_BIAS_RG_ADCGAIN 0x01F0 +#define MIRA050_BIAS_RG_MULT 0x01F3 +#define MIRA050_OFFSET_CLIPPING 0x0193 + +#define MIRA050_OTP_COMMAND 0x0066 +#define MIRA050_OTP_ADDR 0x0067 +#define MIRA050_OTP_START 0x0064 +#define MIRA050_OTP_BUSY 0x0065 +#define MIRA050_OTP_DOUT 0x006C +#define MIRA050_OTP_CAL_VALUE_DEFAULT 2250 +#define MIRA050_OTP_CAL_FINE_VALUE_DEFAULT 35 +#define MIRA050_OTP_CAL_FINE_VALUE_MIN 1 +#define MIRA050_OTP_CAL_FINE_VALUE_MAX 60 // TODO + +/* Illumination trigger */ +#define MIRA050_EN_TRIG_SYNC 0x001D // bank 1 +#define MIRA050_TRIG_SYNC_DELAY 0x001A // bank 0 +#define MIRA050_DMUX0_SEL 0x00F3 // bank 0 +#define MIRA050_TRIG_SYNC_ON_REQ_1 0x001D // bank 0 + +#define MIRA050_EN_TRIG_ILLUM 0x001C +#define MIRA050_ILLUM_WIDTH_REG 0x0019 +#define MIRA050_ILLUM_DELAY_REG 0x0016 +#define MIRA050_ILLUM_WIDTH_DEFAULT (MIRA050_DEFAULT_EXPOSURE_US * MIRA050_DATA_RATE / 8) +#define MIRA050_ILLUM_DELAY_DEFAULT (1 << 19) +#define MIRA050_ILLUM_WIDTH_AUTO_DEFAULT 1; +#define MIRA050_ILLUM_ENABLE_DEFAULT 1; +enum pad_types +{ + IMAGE_PAD, + METADATA_PAD, + NUM_PADS +}; + +struct mira050_reg +{ + u16 address; + u8 val; +}; + +// struct mira050_fine_gain_lut +// { +// u8 gdig_preamp; +// u8 rg_adcgain; +// u8 rg_mult; +// }; +struct mira050_fine_gain_lut_new +{ + u32 analog_gain; + u8 gdig_preamp; + u8 rg_adcgain; + u8 rg_mult; +}; +struct mira050_reg_list +{ + unsigned int num_of_regs; + const struct mira050_reg *regs; +}; + +struct mira050_v4l2_reg +{ + u32 val; +}; + +/* Mode : resolution and related config&values */ +struct mira050_mode +{ + /* Frame width */ + unsigned int width; + /* Frame height */ + unsigned int height; + + /* Analog crop rectangle. */ + struct v4l2_rect crop; + + /* Default register values */ + struct mira050_reg_list reg_list_pre_soft_reset; + struct mira050_reg_list reg_list_post_soft_reset; + u32 gain_min; + u32 gain_max; + u32 min_vblank; + u32 max_vblank; + u32 hblank; + u32 row_length; + + /* Format code */ + u32 code; + + /* bit_depth needed for analog gain selection */ + u8 bit_depth; +}; + +// 576_768_50fps_12b_1lane +// Taken from generated_api\CSP_orig_12-bit mode_anagain1_60fps_exp2000us_datarate_1500.0_mclk_24.0.txt +static const struct mira050_reg full_576_768_50fps_12b_1lane_reg_pre_soft_reset[] = { +{0xE000,0x0},//,Base Configuration.BANK_SEL +{0x01E4,0x0},//,Base Configuration +{0x01E5,0x13},//,Base Configuration +{0x01E2,0x17},//,Base Configuration +{0x01E3,0x88},//,Base Configuration +{0x01E6,0x0},//,Base Configuration +{0x01E7,0xCA},//,Base Configuration +{0x016C,0x1},//,Base Configuration +{0x016B,0x1},//,Base Configuration +{0x0208,0x1},//,Base Configuration +{0x0209,0xF0},//,Base Configuration +{0x020A,0x3},//,Base Configuration +{0x020B,0x4D},//,Base Configuration +{0x020C,0x2},//,Base Configuration +{0x020D,0x10},//,Base Configuration +{0x020E,0x3},//,Base Configuration +{0x020F,0x1},//,Base Configuration +{0x0210,0x0},//,Base Configuration +{0x0211,0x13},//,Base Configuration +{0x0212,0x0},//,Base Configuration +{0x0213,0x3},//,Base Configuration +{0x0214,0x3},//,Base Configuration +{0x0215,0xEF},//,Base Configuration +{0x0216,0x3},//,Base Configuration +{0x0217,0xF3},//,Base Configuration +{0x0218,0x3},//,Base Configuration +{0x0219,0xF4},//,Base Configuration +{0x021A,0x1},//,Base Configuration +{0x021B,0xF1},//,Base Configuration +{0x021C,0x3},//,Base Configuration +{0x021D,0x24},//,Base Configuration +{0x021E,0x0},//,Base Configuration +{0x021F,0x2},//,Base Configuration +{0x0220,0x1},//,Base Configuration +{0x0221,0xF2},//,Base Configuration +{0x0222,0x3},//,Base Configuration +{0x0223,0x2F},//,Base Configuration +{0x0224,0x0},//,Base Configuration +{0x0225,0x21},//,Base Configuration +{0x0226,0x3},//,Base Configuration +{0x0227,0xF0},//,Base Configuration +{0x0228,0x3},//,Base Configuration +{0x0229,0xF1},//,Base Configuration +{0x022A,0x3},//,Base Configuration +{0x022B,0xF2},//,Base Configuration +{0x022C,0x3},//,Base Configuration +{0x022D,0xF5},//,Base Configuration +{0x022E,0x3},//,Base Configuration +{0x022F,0xF6},//,Base Configuration +{0x0230,0x0},//,Base Configuration +{0x0231,0xC1},//,Base Configuration +{0x0232,0x0},//,Base Configuration +{0x0233,0x2},//,Base Configuration +{0x0234,0x1},//,Base Configuration +{0x0235,0xF2},//,Base Configuration +{0x0236,0x3},//,Base Configuration +{0x0237,0x6B},//,Base Configuration +{0x0238,0x3},//,Base Configuration +{0x0239,0xFF},//,Base Configuration +{0x023A,0x3},//,Base Configuration +{0x023B,0x31},//,Base Configuration +{0x023C,0x1},//,Base Configuration +{0x023D,0xF0},//,Base Configuration +{0x023E,0x3},//,Base Configuration +{0x023F,0x87},//,Base Configuration +{0x0240,0x2},//,Base Configuration +{0x0241,0x3A},//,Base Configuration +{0x0242,0x0},//,Base Configuration +{0x0243,0xB},//,Base Configuration +{0x0244,0x1},//,Base Configuration +{0x0245,0xF9},//,Base Configuration +{0x0246,0x3},//,Base Configuration +{0x0247,0xD},//,Base Configuration +{0x0248,0x0},//,Base Configuration +{0x0249,0x7},//,Base Configuration +{0x024A,0x3},//,Base Configuration +{0x024B,0xEF},//,Base Configuration +{0x024C,0x3},//,Base Configuration +{0x024D,0xF3},//,Base Configuration +{0x024E,0x3},//,Base Configuration +{0x024F,0xF4},//,Base Configuration +{0x0250,0x3},//,Base Configuration +{0x0251,0x0},//,Base Configuration +{0x0252,0x0},//,Base Configuration +{0x0253,0x7},//,Base Configuration +{0x0254,0x0},//,Base Configuration +{0x0255,0xC},//,Base Configuration +{0x0256,0x1},//,Base Configuration +{0x0257,0xF1},//,Base Configuration +{0x0258,0x3},//,Base Configuration +{0x0259,0x43},//,Base Configuration +{0x025A,0x1},//,Base Configuration +{0x025B,0xF8},//,Base Configuration +{0x025C,0x3},//,Base Configuration +{0x025D,0x10},//,Base Configuration +{0x025E,0x0},//,Base Configuration +{0x025F,0x7},//,Base Configuration +{0x0260,0x3},//,Base Configuration +{0x0261,0xF0},//,Base Configuration +{0x0262,0x3},//,Base Configuration +{0x0263,0xF1},//,Base Configuration +{0x0264,0x3},//,Base Configuration +{0x0265,0xF2},//,Base Configuration +{0x0266,0x3},//,Base Configuration +{0x0267,0xF5},//,Base Configuration +{0x0268,0x3},//,Base Configuration +{0x0269,0xF6},//,Base Configuration +{0x026A,0x3},//,Base Configuration +{0x026B,0x0},//,Base Configuration +{0x026C,0x2},//,Base Configuration +{0x026D,0x87},//,Base Configuration +{0x026E,0x2},//,Base Configuration +{0x026F,0x31},//,Base Configuration +{0x0270,0x3},//,Base Configuration +{0x0271,0xFF},//,Base Configuration +{0x0272,0x3},//,Base Configuration +{0x0273,0x0},//,Base Configuration +{0x0274,0x3},//,Base Configuration +{0x0275,0xFF},//,Base Configuration +{0x0276,0x2},//,Base Configuration +{0x0277,0x87},//,Base Configuration +{0x0278,0x3},//,Base Configuration +{0x0279,0x2},//,Base Configuration +{0x027A,0x3},//,Base Configuration +{0x027B,0x9},//,Base Configuration +{0x027C,0x3},//,Base Configuration +{0x027D,0xF7},//,Base Configuration +{0x027E,0x0},//,Base Configuration +{0x027F,0x16},//,Base Configuration +{0x0280,0x0},//,Base Configuration +{0x0281,0x33},//,Base Configuration +{0x0282,0x0},//,Base Configuration +{0x0283,0x4},//,Base Configuration +{0x0284,0x0},//,Base Configuration +{0x0285,0x11},//,Base Configuration +{0x0286,0x3},//,Base Configuration +{0x0287,0x9},//,Base Configuration +{0x0288,0x0},//,Base Configuration +{0x0289,0x2},//,Base Configuration +{0x028A,0x0},//,Base Configuration +{0x028B,0x20},//,Base Configuration +{0x028C,0x0},//,Base Configuration +{0x028D,0xB5},//,Base Configuration +{0x028E,0x1},//,Base Configuration +{0x028F,0x5},//,Base Configuration +{0x0290,0x0},//,Base Configuration +{0x0291,0x12},//,Base Configuration +{0x0292,0x0},//,Base Configuration +{0x0293,0xB5},//,Base Configuration +{0x0294,0x1},//,Base Configuration +{0x0295,0x5},//,Base Configuration +{0x0296,0x0},//,Base Configuration +{0x0297,0x0},//,Base Configuration +{0x0298,0x0},//,Base Configuration +{0x0299,0x12},//,Base Configuration +{0x029A,0x0},//,Base Configuration +{0x029B,0x12},//,Base Configuration +{0x029C,0x0},//,Base Configuration +{0x029D,0x20},//,Base Configuration +{0x029E,0x0},//,Base Configuration +{0x029F,0xB5},//,Base Configuration +{0x02A0,0x1},//,Base Configuration +{0x02A1,0x5},//,Base Configuration +{0x02A2,0x0},//,Base Configuration +{0x02A3,0x0},//,Base Configuration +{0x02A4,0x0},//,Base Configuration +{0x02A5,0x12},//,Base Configuration +{0x02A6,0x0},//,Base Configuration +{0x02A7,0x12},//,Base Configuration +{0x02A8,0x0},//,Base Configuration +{0x02A9,0x20},//,Base Configuration +{0x02AA,0x0},//,Base Configuration +{0x02AB,0x47},//,Base Configuration +{0x02AC,0x0},//,Base Configuration +{0x02AD,0x27},//,Base Configuration +{0x02AE,0x0},//,Base Configuration +{0x02AF,0xB5},//,Base Configuration +{0x02B0,0x0},//,Base Configuration +{0x02B1,0xE5},//,Base Configuration +{0x02B2,0x0},//,Base Configuration +{0x02B3,0x0},//,Base Configuration +{0x02B4,0x0},//,Base Configuration +{0x02B5,0x4},//,Base Configuration +{0x02B6,0x0},//,Base Configuration +{0x02B7,0x43},//,Base Configuration +{0x02B8,0x0},//,Base Configuration +{0x02B9,0x1},//,Base Configuration +{0x02BA,0x3},//,Base Configuration +{0x02BB,0x2},//,Base Configuration +{0x02BC,0x0},//,Base Configuration +{0x02BD,0x8},//,Base Configuration +{0x02BE,0x3},//,Base Configuration +{0x02BF,0xFF},//,Base Configuration +{0x02C0,0x2},//,Base Configuration +{0x02C1,0x87},//,Base Configuration +{0x02C2,0x3},//,Base Configuration +{0x02C3,0x89},//,Base Configuration +{0x02C4,0x3},//,Base Configuration +{0x02C5,0xF7},//,Base Configuration +{0x02C6,0x0},//,Base Configuration +{0x02C7,0x77},//,Base Configuration +{0x02C8,0x0},//,Base Configuration +{0x02C9,0x17},//,Base Configuration +{0x02CA,0x0},//,Base Configuration +{0x02CB,0x8},//,Base Configuration +{0x02CC,0x3},//,Base Configuration +{0x02CD,0xFF},//,Base Configuration +{0x02CE,0x0},//,Base Configuration +{0x02CF,0x38},//,Base Configuration +{0x02D0,0x0},//,Base Configuration +{0x02D1,0x17},//,Base Configuration +{0x02D2,0x0},//,Base Configuration +{0x02D3,0x8},//,Base Configuration +{0x02D4,0x3},//,Base Configuration +{0x02D5,0xFF},//,Base Configuration +{0x02D6,0x3},//,Base Configuration +{0x02D7,0xFF},//,Base Configuration +{0x02D8,0x3},//,Base Configuration +{0x02D9,0xFF},//,Base Configuration +{0x02DA,0x3},//,Base Configuration +{0x02DB,0xFF},//,Base Configuration +{0x02DC,0x3},//,Base Configuration +{0x02DD,0xFF},//,Base Configuration +{0x02DE,0x3},//,Base Configuration +{0x02DF,0xFF},//,Base Configuration +{0x02E0,0x3},//,Base Configuration +{0x02E1,0xFF},//,Base Configuration +{0x02E2,0x3},//,Base Configuration +{0x02E3,0xFF},//,Base Configuration +{0x02E4,0x3},//,Base Configuration +{0x02E5,0xFF},//,Base Configuration +{0x02E6,0x3},//,Base Configuration +{0x02E7,0xFF},//,Base Configuration +{0x02E8,0x3},//,Base Configuration +{0x02E9,0xFF},//,Base Configuration +{0x02EA,0x3},//,Base Configuration +{0x02EB,0xFF},//,Base Configuration +{0x02EC,0x3},//,Base Configuration +{0x02ED,0xFF},//,Base Configuration +{0x02EE,0x3},//,Base Configuration +{0x02EF,0xFF},//,Base Configuration +{0x02F0,0x3},//,Base Configuration +{0x02F1,0xFF},//,Base Configuration +{0x02F2,0x3},//,Base Configuration +{0x02F3,0xFF},//,Base Configuration +{0x02F4,0x3},//,Base Configuration +{0x02F5,0xFF},//,Base Configuration +{0x02F6,0x3},//,Base Configuration +{0x02F7,0xFF},//,Base Configuration +{0x02F8,0x3},//,Base Configuration +{0x02F9,0xFF},//,Base Configuration +{0x02FA,0x3},//,Base Configuration +{0x02FB,0xFF},//,Base Configuration +{0x02FC,0x3},//,Base Configuration +{0x02FD,0xFF},//,Base Configuration +{0x02FE,0x3},//,Base Configuration +{0x02FF,0xFF},//,Base Configuration +{0x0300,0x3},//,Base Configuration +{0x0301,0xFF},//,Base Configuration +{0x0302,0x3},//,Base Configuration +{0x0303,0xFF},//,Base Configuration +{0x01E9,0x0},//,Base Configuration +{0x01E8,0x19},//,Base Configuration +{0x01EA,0x35},//,Base Configuration +{0x01EB,0x37},//,Base Configuration +{0x01EC,0x5C},//,Base Configuration +{0x01ED,0x63},//,Base Configuration +{0x01F8,0xF},//,Base Configuration +{0x005C,0x0},//,Base Configuration +{0x005D,0x0},//,Base Configuration +{0x01DA,0x1},//,Base Configuration +{0x01DC,0x1},//,Base Configuration +{0x01DE,0x1},//,Base Configuration +{0x0189,0x1},//,Base Configuration +{0x01B7,0x1},//,Base Configuration +{0x01C1,0xE},//,Base Configuration +{0x01C2,0xF6},//,Base Configuration +{0x01C3,0xFF},//,Base Configuration +{0x01B8,0x1},//,Base Configuration +{0x01BA,0x32},//,Base Configuration +{0x01BD,0x8},//,Base Configuration +{0x01CA,0x1E},//,Base Configuration +{0x01C9,0x1E},//,Base Configuration +{0x01BF,0x3C},//,Base Configuration +{0x01C0,0x5C},//,Base Configuration +{0x0071,0x1},//,Base Configuration +{0x01B4,0x1},//,Base Configuration +{0x01B5,0x1},//,Base Configuration +{0x01F1,0x1},//,Base Configuration +{0x01F4,0x1},//,Base Configuration +{0x01F5,0x1},//,Base Configuration +{0x0314,0x1},//,Base Configuration +{0x0315,0x1},//,Base Configuration +{0x0316,0x1},//,Base Configuration +{0x0207,0x0},//,Base Configuration +{0x4207,0x2},//,Base Configuration +{0x2207,0x2},//,Base Configuration +{0x209D,0x0},//,Base Configuration +{0x0063,0x1},//,Base Configuration +{0x01F7,0xF},//,Base Configuration +{0x00E9,0x3},//,Base Configuration +{0x00EA,0x28},//,Base Configuration +{0x0309,0x7},//,Base Configuration +{0x030A,0x4},//,Base Configuration +{0x030B,0xD},//,Base Configuration +{0x030C,0x7},//,Base Configuration +{0x030E,0x15},//,Base Configuration +{0x030D,0xA},//,Base Configuration +{0x030F,0x1},//,Base Configuration +{0x0310,0xF},//,Base Configuration +{0x01D0,0x1F},//,Base Configuration +{0x01D1,0x12},//,Base Configuration +{0x0016,0x0},//,Base Configuration +{0x0017,0x5},//,Base Configuration +{0x00E8,0x3},//,Base Configuration +{0xE0C0,0x0},//,Base Configuration +{0xE0C1,0x20},//,Base Configuration +{0xE0C2,0x0},//,Base Configuration +{0xE0C3,0x20},//,Base Configuration +{0x016A,0x2},//,Base Configuration.DPATH_BITMODE +{0x0168,0x2C},//,Base Configuration.CSI2_DATA_TYPE +{0xE000,0x0},//,PLL.BANK_SEL +{0x2077,0x0},//,PLL.PLL_DIV_N +{0x2076,0xBD},//,PLL.PLL_DIV_M +{0x00CE,0x1},//,PLL.CLKGEN_CP_DIV +{0x0070,0x9},//,PLL.OTP_GRANULARITY +{0x016D,0x32},//,PLL.GRAN_TG +{0x0176,0x0},//,PLL.LUT_DEL_008 +{0x20C6,0x0},//,PLL.CLKGEN_TX_ESC_H +{0x20C7,0x0},//,PLL.CLKGEN_TX_ESC_H +{0x20C8,0x1},//,PLL.CLKGEN_TX_ESC_H +{0x20C9,0x0},//,PLL.CLKGEN_TX_ESC_L +{0x20CA,0x0},//,PLL.CLKGEN_TX_ESC_L +{0x20CB,0x1},//,PLL.CLKGEN_TX_ESC_L +{0x2075,0x0},//,PLL.PLL_PD +{0xE000,0x0},//,Low Power State.BANK_SEL +{0x001E,0x1},//,Low Power State.EN_AUTO_LPS_TRANS +{0xE000,0x0},//,MIPI.BANK_SEL +{0x207E,0x0},//,MIPI.MIPI_VC_ID +{0x207F,0x0},//,MIPI.MIPI_FRAME_MODE_PIX +{0x2080,0x0},//,MIPI.MIPI_FRAME_MODE_EMBED +{0x2081,0x3},//,MIPI.MIPI_LINE_COUNT_EN +{0x2082,0x0},//,MIPI.MIPI_FRAME_COUNT_WRAP +{0x2083,0x2},//,MIPI.MIPI_FRAME_COUNT_WRAP +{0x0090,0x0},//,MIPI.CSI2_CONT_CLOCK_MODE +{0x2097,0x0},//,MIPI.CSI2_SCRAMBLING_EN +{0xE000,0x0},//,Sensor Control Mode.BANK_SEL +{0x0011,0x3},//,Sensor Control Mode.CTRL_MODE +{0x011D,0x0},//,Sensor Control Mode.SINGLE_PIN_CTRL +{0xE000,0x0},//,Time Bases.BANK_SEL +{0x0012,0x0},//,Time Bases.CLOCKS_PER_TIME_UNIT +{0x0013,0x18},//,Time Bases.CLOCKS_PER_TIME_UNIT +{0x015A,0x0},//,Time Bases.GLOB_TIME +{0x015B,0x33},//,Time Bases.GLOB_TIME +{0x015C,0x0},//,Time Bases.GLOB_TIME_A +{0x015D,0x33},//,Time Bases.GLOB_TIME_A +{0x015E,0x0},//,Time Bases.GLOB_TIME_B +{0x015F,0x33},//,Time Bases.GLOB_TIME_B +{0x0162,0x0},//,Time Bases.ENTER_LPS_TIME +{0x0163,0x5},//,Time Bases.ENTER_LPS_TIME +{0x0164,0x4},//,Time Bases.EXIT_LPS_TIME +{0x0165,0x4C},//,Time Bases.EXIT_LPS_TIME +{0x0166,0x4},//,Time Bases.LPS_CYCLE_TIME +{0x0167,0x4C},//,Time Bases.LPS_CYCLE_TIME +{0xE000,0x0},//,Analog Gain.BANK_SEL +{0x01BB,0x99},//,Analog Gain +{0x01BC,0x91},//,Analog Gain +{0x00D0,0x0},//,Analog Gain +{0x01F0,0x8},//,Analog Gain +{0x01F3,0x0},//,Analog Gain +{0x016E,0xFF},//,Analog Gain.LUT_DEL_000 +{0x0172,0xFF},//,Analog Gain.LUT_DEL_004 +{0x0173,0x2E},//,Analog Gain.LUT_DEL_005 +{0x016F,0xFF},//,Analog Gain.LUT_DEL_001 +{0x0170,0xFF},//,Analog Gain.LUT_DEL_002 +{0x0171,0xFF},//,Analog Gain.LUT_DEL_003 +{0x0174,0xFF},//,Analog Gain.LUT_DEL_006 +{0x0175,0xAB},//,Analog Gain.LUT_DEL_007 +{0x018B,0x8},//,Analog Gain +{0x018C,0xCA},//,Analog Gain +{0x018D,0x2},//,Analog Gain +{0x018E,0x56},//,Analog Gain +{0x018F,0x12},//,Analog Gain +{0x0190,0xBE},//,Analog Gain +{0x01EE,0x14},//,Analog Gain.SHUTTER_LAG +{0x01EF,0xA2},//,Analog Gain.SHUTTER_LAG +{0x01A2,0x6},//,Analog Gain.POS_ANACOL_TRIGGER +{0x01A3,0xA5},//,Analog Gain.POS_ANACOL_TRIGGER +{0x031F,0x6},//,Analog Gain.POS_YADDR_TRIGGER +{0x0320,0xAE},//,Analog Gain.POS_YADDR_TRIGGER +{0x01A6,0x7},//,Analog Gain.POS_ADC_TRIGGER +{0x01A7,0x3C},//,Analog Gain.POS_ADC_TRIGGER +{0x01A4,0xF},//,Analog Gain.POS_ANACOL_YBIN_TRIGGER +{0x01A5,0x27},//,Analog Gain.POS_ANACOL_YBIN_TRIGGER +{0x0321,0xF},//,Analog Gain.POS_YADDR_YBIN_TRIGGER +{0x0322,0x30},//,Analog Gain.POS_YADDR_YBIN_TRIGGER +{0x01A8,0xF},//,Analog Gain.POS_ADC_YBIN_TRIGGER +{0x01A9,0xBE},//,Analog Gain.POS_ADC_YBIN_TRIGGER +{0x01A0,0x1},//,Analog Gain.POS_VIS_TRIGGER +{0x01A1,0x25},//,Analog Gain.POS_VIS_TRIGGER +{0x01B2,0x1},//,Analog Gain.POS_HSYNC_RISE +{0x01B3,0x3D},//,Analog Gain.POS_HSYNC_RISE +{0x01B0,0x1},//,Analog Gain.POS_STAT_TRIGGER +{0x01B1,0x38},//,Analog Gain.POS_STAT_TRIGGER +{0x01AC,0x1},//,Analog Gain.POS_SVAL_TRIGGER +{0x01AD,0x43},//,Analog Gain.POS_SVAL_TRIGGER +{0xE000,0x0},//,Black Level.BANK_SEL +{0x0193,0x38},//,Black Level.OFFSET_CLIPPING +{0x0194,0xA6},//,Black Level.OFFSET_CLIPPING +{0xE000,0x0},//,Release Soft Reset.BANK_SEL +{0xE009,0x1},//,Release Soft Reset +{0x212F,0x1},//,Release Soft Reset +{0x2130,0x1},//,Release Soft Reset +{0x2131,0x1},//,Release Soft Reset +{0x2132,0x1},//,Release Soft Reset +{0x2133,0x1},//,Release Soft Reset +{0x2134,0x1},//,Release Soft Reset +{0x2135,0x1},//,Release Soft Reset +{0xE0E1,0x1},//,Release Soft Reset +{0x018A,0x1},//,Release Soft Reset +{0x00E0,0x1},//,Release Soft Reset +{0xE004,0x0},//,Horizontal ROI.RW_CONTEXT +{0xE000,0x1},//,Horizontal ROI.BANK_SEL +{0xE02C,0x0},//,Horizontal ROI.XWIN_LEFT +{0xE02D,0xC},//,Horizontal ROI.XWIN_LEFT +{0xE02E,0x2},//,Horizontal ROI.XWIN_RIGHT +{0xE02F,0x4B},//,Horizontal ROI.XWIN_RIGHT +{0xE030,0x0},//,Horizontal ROI.XMIRROR +{0xE025,0x0},//,Horizontal ROI.XSUBSAMPLING +{0xE02A,0x0},//,Horizontal ROI.XBINNING +{0x2029,0x46},//,Horizontal ROI.MIPI_DATA_FIFO_THRESHOLD +{0x0034,0x1},//,Horizontal ROI.HSYNC_LENGTH +{0x0035,0x20},//,Horizontal ROI.HSYNC_LENGTH +{0xE004,0x1},//,Horizontal ROI.RW_CONTEXT +{0xE02C,0x0},//,Horizontal ROI.XWIN_LEFT +{0xE02D,0xC},//,Horizontal ROI.XWIN_LEFT +{0xE02E,0x2},//,Horizontal ROI.XWIN_RIGHT +{0xE02F,0x4B},//,Horizontal ROI.XWIN_RIGHT +{0xE030,0x0},//,Horizontal ROI.XMIRROR +{0xE025,0x0},//,Horizontal ROI.XSUBSAMPLING +{0xE02A,0x0},//,Horizontal ROI.XBINNING +{0x2029,0x46},//,Horizontal ROI.MIPI_DATA_FIFO_THRESHOLD +{0x0034,0x1},//,Horizontal ROI.HSYNC_LENGTH +{0x0035,0x20},//,Horizontal ROI.HSYNC_LENGTH +{0xE004,0x0},//,Vertical ROI.RW_CONTEXT +{0xE000,0x1},//,Vertical ROI.BANK_SEL +{0x001E,0x0},//,Vertical ROI.YWIN_ENA +{0x001F,0x1},//,Vertical ROI.YWIN_ENA +{0x002B,0x0},//,Vertical ROI.YBINNING +{0xE004,0x1},//,Vertical ROI.RW_CONTEXT +{0x001E,0x0},//,Vertical ROI.YWIN_ENA +{0x001F,0x1},//,Vertical ROI.YWIN_ENA +{0x002B,0x0},//,Vertical ROI.YBINNING +{0xE000,0x0},//,Vertical ROI.BANK_SEL +{0x001F,0x0},//,Vertical ROI.YWIN_BLACK +{0x0020,0x0},//,Vertical ROI.YWIN_BLACK +{0x0023,0x0},//,Vertical ROI.YWIN_DIR +{0x0024,0x3},//,Vertical ROI.YWIN0_SIZE +{0x0025,0x0},//,Vertical ROI.YWIN0_SIZE +{0x0026,0x0},//,Vertical ROI.YWIN0_START +{0x0027,0x18},//,Vertical ROI.YWIN0_START +{0x0028,0x0},//,Vertical ROI.YWIN0_SUBS +{0x0029,0x0},//,Vertical ROI.YWIN1_SIZE +{0x002A,0x0},//,Vertical ROI.YWIN1_SIZE +{0x002B,0x0},//,Vertical ROI.YWIN1_START +{0x002C,0x0},//,Vertical ROI.YWIN1_START +{0x002D,0x0},//,Vertical ROI.YWIN1_SUBS +{0x002E,0x0},//,Vertical ROI.YWIN2_SIZE +{0x002F,0x0},//,Vertical ROI.YWIN2_SIZE +{0x0030,0x0},//,Vertical ROI.YWIN2_START +{0x0031,0x0},//,Vertical ROI.YWIN2_START +{0x0032,0x0},//,Vertical ROI.YWIN2_SUBS +{0x0033,0x0},//,Vertical ROI.YWIN3_SIZE +{0x0034,0x0},//,Vertical ROI.YWIN3_SIZE +{0x0035,0x0},//,Vertical ROI.YWIN3_START +{0x0036,0x0},//,Vertical ROI.YWIN3_START +{0x0037,0x0},//,Vertical ROI.YWIN3_SUBS +{0x0038,0x0},//,Vertical ROI.YWIN4_SIZE +{0x0039,0x0},//,Vertical ROI.YWIN4_SIZE +{0x003A,0x0},//,Vertical ROI.YWIN4_START +{0x003B,0x0},//,Vertical ROI.YWIN4_START +{0x003C,0x0},//,Vertical ROI.YWIN4_SUBS +{0x003D,0x0},//,Vertical ROI.YWIN5_SIZE +{0x003E,0x0},//,Vertical ROI.YWIN5_SIZE +{0x003F,0x0},//,Vertical ROI.YWIN5_START +{0x0040,0x0},//,Vertical ROI.YWIN5_START +{0x0041,0x0},//,Vertical ROI.YWIN5_SUBS +{0x0042,0x0},//,Vertical ROI.YWIN6_SIZE +{0x0043,0x0},//,Vertical ROI.YWIN6_SIZE +{0x0044,0x0},//,Vertical ROI.YWIN6_START +{0x0045,0x0},//,Vertical ROI.YWIN6_START +{0x0046,0x0},//,Vertical ROI.YWIN6_SUBS +{0x0047,0x0},//,Vertical ROI.YWIN7_SIZE +{0x0048,0x0},//,Vertical ROI.YWIN7_SIZE +{0x0049,0x0},//,Vertical ROI.YWIN7_START +{0x004A,0x0},//,Vertical ROI.YWIN7_START +{0x004B,0x0},//,Vertical ROI.YWIN7_SUBS +{0x004C,0x0},//,Vertical ROI.YWIN8_SIZE +{0x004D,0x0},//,Vertical ROI.YWIN8_SIZE +{0x004E,0x0},//,Vertical ROI.YWIN8_START +{0x004F,0x0},//,Vertical ROI.YWIN8_START +{0x0050,0x0},//,Vertical ROI.YWIN8_SUBS +{0x0051,0x0},//,Vertical ROI.YWIN9_SIZE +{0x0052,0x0},//,Vertical ROI.YWIN9_SIZE +{0x0053,0x0},//,Vertical ROI.YWIN9_START +{0x0054,0x0},//,Vertical ROI.YWIN9_START +{0x0055,0x0},//,Vertical ROI.YWIN9_SUBS +{0xE004,0x0},//,Frame && Exposure Control.RW_CONTEXT +{0xE000,0x1},//,Frame && Exposure Control.BANK_SEL +{0x000E,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x000F,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x0010,0x3},//,Frame && Exposure Control.EXP_TIME_L +{0x0011,0xE8},//,Frame && Exposure Control.EXP_TIME_L +{0x0012,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0013,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0014,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0015,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0xE004,0x1},//,Frame && Exposure Control.RW_CONTEXT +{0x000E,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x000F,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x0010,0x3},//,Frame && Exposure Control.EXP_TIME_L +{0x0011,0xE8},//,Frame && Exposure Control.EXP_TIME_L +{0x0012,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0013,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0014,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0015,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0xE004,0x0},//,Frame && Exposure Control.RW_CONTEXT +{0x0032,0xB},//,Frame && Exposure Control.ROW_LENGTH +{0x0033,0xFD},//,Frame && Exposure Control.ROW_LENGTH +{0xE004,0x1},//,Frame && Exposure Control.RW_CONTEXT +{0x0032,0xB},//,Frame && Exposure Control.ROW_LENGTH +{0x0033,0xFD},//,Frame && Exposure Control.ROW_LENGTH +{0xE004,0x0},//,Frame && Exposure Control.RW_CONTEXT +{0x0007,0x1},//,Frame && Exposure Control.NROF_FRAMES +{0x0008,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x0009,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000A,0x41},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000B,0x1B},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0xE004,0x1},//,Frame && Exposure Control.RW_CONTEXT +{0x0007,0x1},//,Frame && Exposure Control.NROF_FRAMES +{0x0008,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x0009,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000A,0x41},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000B,0x1B},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0xE004,0x0},//,Digital Gain.RW_CONTEXT +{0xE000,0x1},//,Digital Gain.BANK_SEL +{0x0024,0xF},//,Digital Gain.GDIG_AMP +{0xE004,0x1},//,Digital Gain.RW_CONTEXT +{0x0024,0xF},//,Digital Gain.GDIG_AMP +{0xE000,0x0},//,Defect Pixel Correction.BANK_SEL +{0x0057,0x0},//,Defect Pixel Correction.DEFECT_ON +{0x0058,0x0},//,Defect Pixel Correction.DEFECT_MODE +{0x0059,0x2},//,Defect Pixel Correction.DEFECT_LIMIT_HIGH +{0x005A,0x2},//,Defect Pixel Correction.DEFECT_LIMIT_LOW +{0x005B,0x0},//,Defect Pixel Correction.DEFECT_LIMIT_HIGH_MODE +{0xE000,0x0},//,Context Switching.BANK_SEL +{0xE008,0x0},//,Context Switching.DISABLE_CONTEXTSYNC +{0x0006,0x1},//,Context Switching.PARAM_HOLD +{0xE003,0x0},//,Context Switching.NEXT_ACTIVE_CONTEXT +{0x0006,0x0},//,Context Switching.PARAM_HOLD +{0xE008,0x0},//,Context Switching.DISABLE_CONTEXTSYNC +{0xE004,0x0},//,Event Detection.RW_CONTEXT +{0xE000,0x1},//,Event Detection.BANK_SEL +{0x0031,0x0},//,Event Detection.EN_EVENT_DETECTION +{0xE004,0x1},//,Event Detection.RW_CONTEXT +{0x0031,0x0},//,Event Detection.EN_EVENT_DETECTION +{0xE000,0x0},//,Event Detection.BANK_SEL +{0x0138,0x0},//,Event Detection.EN_AUTO_CNTXT_SWITCH +{0xE005,0x0},//,Event Detection.AUTO_SWITCHTO_CNTXT +{0x0139,0x0},//,Event Detection.TILE_XSTART +{0x013A,0x0},//,Event Detection.TILE_XSTART +{0x013B,0x96},//,Event Detection.TILE_WIDTH +{0x013C,0x0},//,Event Detection.TILE_YSTART +{0x013D,0x0},//,Event Detection.TILE_YSTART +{0x013E,0xA0},//,Event Detection.TILE_HEIGHT +{0x013F,0x6},//,Event Detection.TILE_DISCARD_NR_BITS +{0x0140,0x1},//,Event Detection.MIN_FLAGGED_TILES +{0x0141,0x14},//,Event Detection.MAX_FLAGGED_TILES +{0x0142,0x1},//,Event Detection.TILE_THRESHOLD +{0x0143,0x0},//,Event Detection.TILE_MIN_THRESHOLD +{0x0144,0x0},//,Event Detection.TILE_MIN_THRESHOLD +{0x0145,0x0},//,Event Detection.TILE_DISABLE_MASK +{0x0146,0x0},//,Event Detection.TILE_DISABLE_MASK +{0x0147,0x0},//,Event Detection.TILE_DISABLE_MASK +{0x0148,0x0},//,Event Detection.CPY_TILES_INTERVAL +{0xE004,0x0},//,Image Statistics.RW_CONTEXT +{0xE000,0x1},//,Image Statistics.BANK_SEL +{0x0026,0x0},//,Image Statistics.STAT_NROF_HIST +{0xE004,0x1},//,Image Statistics.RW_CONTEXT +{0x0026,0x0},//,Image Statistics.STAT_NROF_HIST +{0xE000,0x0},//,Image Statistics.BANK_SEL +{0x0169,0x12},//,Image Statistics.CSI2_EMBED_TYPE +{0xE004,0x0},//,Illumination Trigger.RW_CONTEXT +{0xE000,0x1},//,Illumination Trigger.BANK_SEL +{0x001C,0x0},//,Illumination Trigger.EN_TRIG_ILLUM +{0x001D,0x0},//,Illumination Trigger.EN_TRIG_SYNC +{0x0019,0x0},//,Illumination Trigger.ILLUM_WIDTH +{0x001A,0x7},//,Illumination Trigger.ILLUM_WIDTH +{0x001B,0x53},//,Illumination Trigger.ILLUM_WIDTH +{0x0016,0x8},//,Illumination Trigger.ILLUM_DELAY +{0x0017,0x0},//,Illumination Trigger.ILLUM_DELAY +{0x0018,0x0},//,Illumination Trigger.ILLUM_DELAY +{0xE004,0x1},//,Illumination Trigger.RW_CONTEXT +{0x001C,0x0},//,Illumination Trigger.EN_TRIG_ILLUM +{0x001D,0x0},//,Illumination Trigger.EN_TRIG_SYNC +{0x0019,0x0},//,Illumination Trigger.ILLUM_WIDTH +{0x001A,0x7},//,Illumination Trigger.ILLUM_WIDTH +{0x001B,0x53},//,Illumination Trigger.ILLUM_WIDTH +{0x0016,0x8},//,Illumination Trigger.ILLUM_DELAY +{0x0017,0x0},//,Illumination Trigger.ILLUM_DELAY +{0x0018,0x0},//,Illumination Trigger.ILLUM_DELAY +{0xE000,0x0},//,Illumination Trigger.BANK_SEL +{0x001A,0x0},//,Illumination Trigger.TRIG_SYNC_DELAY +{0x001B,0x0},//,Illumination Trigger.TRIG_SYNC_DELAY +{0x001C,0x0},//,Illumination Trigger.TRIG_SYNC_DELAY +{0x00F3,0x27},//,Illumination Trigger.DMUX0_SEL +{0xE004,0x0},//,Synchronization Trigger.RW_CONTEXT +{0xE000,0x1},//,Synchronization Trigger.BANK_SEL +{0x001D,0x0},//,Synchronization Trigger.EN_TRIG_SYNC +{0xE004,0x1},//,Synchronization Trigger.RW_CONTEXT +{0x001D,0x0},//,Synchronization Trigger.EN_TRIG_SYNC +{0xE000,0x0},//,Synchronization Trigger.BANK_SEL +{0x001A,0x0},//,Synchronization Trigger.TRIG_SYNC_DELAY +{0x001B,0x0},//,Synchronization Trigger.TRIG_SYNC_DELAY +{0x001C,0x0},//,Synchronization Trigger.TRIG_SYNC_DELAY +{0x001D,0x0},//,Synchronization Trigger.TRIG_SYNC_ON_REQ_1 + + + +}; + +static const struct mira050_reg full_576_768_50fps_12b_1lane_reg_post_soft_reset[] = { + +}; +// from API 1.5.1 +static const struct mira050_reg full_576_768_50fps_10b_hs_1lane_reg_pre_soft_reset[] = { +{0xE000,0x0},//,Base Configuration.BANK_SEL +{0x01E4,0x0},//,Base Configuration +{0x01E5,0x13},//,Base Configuration +{0x01E2,0x17},//,Base Configuration +{0x01E3,0x88},//,Base Configuration +{0x01E6,0x0},//,Base Configuration +{0x01E7,0xCA},//,Base Configuration +{0x016C,0x1},//,Base Configuration +{0x016B,0x1},//,Base Configuration +{0x0208,0x1},//,Base Configuration +{0x0209,0xF0},//,Base Configuration +{0x020A,0x3},//,Base Configuration +{0x020B,0x4D},//,Base Configuration +{0x020C,0x2},//,Base Configuration +{0x020D,0x10},//,Base Configuration +{0x020E,0x3},//,Base Configuration +{0x020F,0x1},//,Base Configuration +{0x0210,0x0},//,Base Configuration +{0x0211,0x13},//,Base Configuration +{0x0212,0x0},//,Base Configuration +{0x0213,0x3},//,Base Configuration +{0x0214,0x3},//,Base Configuration +{0x0215,0xEF},//,Base Configuration +{0x0216,0x3},//,Base Configuration +{0x0217,0xF3},//,Base Configuration +{0x0218,0x3},//,Base Configuration +{0x0219,0xF4},//,Base Configuration +{0x021A,0x1},//,Base Configuration +{0x021B,0xF1},//,Base Configuration +{0x021C,0x3},//,Base Configuration +{0x021D,0x24},//,Base Configuration +{0x021E,0x0},//,Base Configuration +{0x021F,0x2},//,Base Configuration +{0x0220,0x1},//,Base Configuration +{0x0221,0xF2},//,Base Configuration +{0x0222,0x3},//,Base Configuration +{0x0223,0x2F},//,Base Configuration +{0x0224,0x0},//,Base Configuration +{0x0225,0x21},//,Base Configuration +{0x0226,0x3},//,Base Configuration +{0x0227,0xF0},//,Base Configuration +{0x0228,0x3},//,Base Configuration +{0x0229,0xF1},//,Base Configuration +{0x022A,0x3},//,Base Configuration +{0x022B,0xF2},//,Base Configuration +{0x022C,0x3},//,Base Configuration +{0x022D,0xF5},//,Base Configuration +{0x022E,0x3},//,Base Configuration +{0x022F,0xF6},//,Base Configuration +{0x0230,0x0},//,Base Configuration +{0x0231,0xC1},//,Base Configuration +{0x0232,0x0},//,Base Configuration +{0x0233,0x2},//,Base Configuration +{0x0234,0x1},//,Base Configuration +{0x0235,0xF2},//,Base Configuration +{0x0236,0x3},//,Base Configuration +{0x0237,0x6B},//,Base Configuration +{0x0238,0x3},//,Base Configuration +{0x0239,0xFF},//,Base Configuration +{0x023A,0x3},//,Base Configuration +{0x023B,0x31},//,Base Configuration +{0x023C,0x1},//,Base Configuration +{0x023D,0xF0},//,Base Configuration +{0x023E,0x3},//,Base Configuration +{0x023F,0x87},//,Base Configuration +{0x0240,0x2},//,Base Configuration +{0x0241,0x3A},//,Base Configuration +{0x0242,0x0},//,Base Configuration +{0x0243,0xB},//,Base Configuration +{0x0244,0x1},//,Base Configuration +{0x0245,0xF9},//,Base Configuration +{0x0246,0x3},//,Base Configuration +{0x0247,0xD},//,Base Configuration +{0x0248,0x0},//,Base Configuration +{0x0249,0x7},//,Base Configuration +{0x024A,0x3},//,Base Configuration +{0x024B,0xEF},//,Base Configuration +{0x024C,0x3},//,Base Configuration +{0x024D,0xF3},//,Base Configuration +{0x024E,0x3},//,Base Configuration +{0x024F,0xF4},//,Base Configuration +{0x0250,0x3},//,Base Configuration +{0x0251,0x0},//,Base Configuration +{0x0252,0x0},//,Base Configuration +{0x0253,0x7},//,Base Configuration +{0x0254,0x0},//,Base Configuration +{0x0255,0xC},//,Base Configuration +{0x0256,0x1},//,Base Configuration +{0x0257,0xF1},//,Base Configuration +{0x0258,0x3},//,Base Configuration +{0x0259,0x43},//,Base Configuration +{0x025A,0x1},//,Base Configuration +{0x025B,0xF8},//,Base Configuration +{0x025C,0x3},//,Base Configuration +{0x025D,0x10},//,Base Configuration +{0x025E,0x0},//,Base Configuration +{0x025F,0x7},//,Base Configuration +{0x0260,0x3},//,Base Configuration +{0x0261,0xF0},//,Base Configuration +{0x0262,0x3},//,Base Configuration +{0x0263,0xF1},//,Base Configuration +{0x0264,0x3},//,Base Configuration +{0x0265,0xF2},//,Base Configuration +{0x0266,0x3},//,Base Configuration +{0x0267,0xF5},//,Base Configuration +{0x0268,0x3},//,Base Configuration +{0x0269,0xF6},//,Base Configuration +{0x026A,0x3},//,Base Configuration +{0x026B,0x0},//,Base Configuration +{0x026C,0x2},//,Base Configuration +{0x026D,0x87},//,Base Configuration +{0x026E,0x2},//,Base Configuration +{0x026F,0x31},//,Base Configuration +{0x0270,0x3},//,Base Configuration +{0x0271,0xFF},//,Base Configuration +{0x0272,0x3},//,Base Configuration +{0x0273,0x0},//,Base Configuration +{0x0274,0x3},//,Base Configuration +{0x0275,0xFF},//,Base Configuration +{0x0276,0x2},//,Base Configuration +{0x0277,0x87},//,Base Configuration +{0x0278,0x3},//,Base Configuration +{0x0279,0x2},//,Base Configuration +{0x027A,0x3},//,Base Configuration +{0x027B,0x9},//,Base Configuration +{0x027C,0x3},//,Base Configuration +{0x027D,0xF7},//,Base Configuration +{0x027E,0x0},//,Base Configuration +{0x027F,0x16},//,Base Configuration +{0x0280,0x0},//,Base Configuration +{0x0281,0x33},//,Base Configuration +{0x0282,0x0},//,Base Configuration +{0x0283,0x4},//,Base Configuration +{0x0284,0x0},//,Base Configuration +{0x0285,0x11},//,Base Configuration +{0x0286,0x3},//,Base Configuration +{0x0287,0x9},//,Base Configuration +{0x0288,0x0},//,Base Configuration +{0x0289,0x2},//,Base Configuration +{0x028A,0x0},//,Base Configuration +{0x028B,0x20},//,Base Configuration +{0x028C,0x0},//,Base Configuration +{0x028D,0xB5},//,Base Configuration +{0x028E,0x1},//,Base Configuration +{0x028F,0x5},//,Base Configuration +{0x0290,0x0},//,Base Configuration +{0x0291,0x12},//,Base Configuration +{0x0292,0x0},//,Base Configuration +{0x0293,0xB5},//,Base Configuration +{0x0294,0x1},//,Base Configuration +{0x0295,0x5},//,Base Configuration +{0x0296,0x0},//,Base Configuration +{0x0297,0x0},//,Base Configuration +{0x0298,0x0},//,Base Configuration +{0x0299,0x12},//,Base Configuration +{0x029A,0x0},//,Base Configuration +{0x029B,0x12},//,Base Configuration +{0x029C,0x0},//,Base Configuration +{0x029D,0x20},//,Base Configuration +{0x029E,0x0},//,Base Configuration +{0x029F,0xB5},//,Base Configuration +{0x02A0,0x1},//,Base Configuration +{0x02A1,0x5},//,Base Configuration +{0x02A2,0x0},//,Base Configuration +{0x02A3,0x0},//,Base Configuration +{0x02A4,0x0},//,Base Configuration +{0x02A5,0x12},//,Base Configuration +{0x02A6,0x0},//,Base Configuration +{0x02A7,0x12},//,Base Configuration +{0x02A8,0x0},//,Base Configuration +{0x02A9,0x20},//,Base Configuration +{0x02AA,0x0},//,Base Configuration +{0x02AB,0x47},//,Base Configuration +{0x02AC,0x0},//,Base Configuration +{0x02AD,0x27},//,Base Configuration +{0x02AE,0x0},//,Base Configuration +{0x02AF,0xB5},//,Base Configuration +{0x02B0,0x0},//,Base Configuration +{0x02B1,0xE5},//,Base Configuration +{0x02B2,0x0},//,Base Configuration +{0x02B3,0x0},//,Base Configuration +{0x02B4,0x0},//,Base Configuration +{0x02B5,0x4},//,Base Configuration +{0x02B6,0x0},//,Base Configuration +{0x02B7,0x43},//,Base Configuration +{0x02B8,0x0},//,Base Configuration +{0x02B9,0x1},//,Base Configuration +{0x02BA,0x3},//,Base Configuration +{0x02BB,0x2},//,Base Configuration +{0x02BC,0x0},//,Base Configuration +{0x02BD,0x8},//,Base Configuration +{0x02BE,0x3},//,Base Configuration +{0x02BF,0xFF},//,Base Configuration +{0x02C0,0x2},//,Base Configuration +{0x02C1,0x87},//,Base Configuration +{0x02C2,0x3},//,Base Configuration +{0x02C3,0x89},//,Base Configuration +{0x02C4,0x3},//,Base Configuration +{0x02C5,0xF7},//,Base Configuration +{0x02C6,0x0},//,Base Configuration +{0x02C7,0x77},//,Base Configuration +{0x02C8,0x0},//,Base Configuration +{0x02C9,0x17},//,Base Configuration +{0x02CA,0x0},//,Base Configuration +{0x02CB,0x8},//,Base Configuration +{0x02CC,0x3},//,Base Configuration +{0x02CD,0xFF},//,Base Configuration +{0x02CE,0x0},//,Base Configuration +{0x02CF,0x38},//,Base Configuration +{0x02D0,0x0},//,Base Configuration +{0x02D1,0x17},//,Base Configuration +{0x02D2,0x0},//,Base Configuration +{0x02D3,0x8},//,Base Configuration +{0x02D4,0x3},//,Base Configuration +{0x02D5,0xFF},//,Base Configuration +{0x02D6,0x3},//,Base Configuration +{0x02D7,0xFF},//,Base Configuration +{0x02D8,0x3},//,Base Configuration +{0x02D9,0xFF},//,Base Configuration +{0x02DA,0x3},//,Base Configuration +{0x02DB,0xFF},//,Base Configuration +{0x02DC,0x3},//,Base Configuration +{0x02DD,0xFF},//,Base Configuration +{0x02DE,0x3},//,Base Configuration +{0x02DF,0xFF},//,Base Configuration +{0x02E0,0x3},//,Base Configuration +{0x02E1,0xFF},//,Base Configuration +{0x02E2,0x3},//,Base Configuration +{0x02E3,0xFF},//,Base Configuration +{0x02E4,0x3},//,Base Configuration +{0x02E5,0xFF},//,Base Configuration +{0x02E6,0x3},//,Base Configuration +{0x02E7,0xFF},//,Base Configuration +{0x02E8,0x3},//,Base Configuration +{0x02E9,0xFF},//,Base Configuration +{0x02EA,0x3},//,Base Configuration +{0x02EB,0xFF},//,Base Configuration +{0x02EC,0x3},//,Base Configuration +{0x02ED,0xFF},//,Base Configuration +{0x02EE,0x3},//,Base Configuration +{0x02EF,0xFF},//,Base Configuration +{0x02F0,0x3},//,Base Configuration +{0x02F1,0xFF},//,Base Configuration +{0x02F2,0x3},//,Base Configuration +{0x02F3,0xFF},//,Base Configuration +{0x02F4,0x3},//,Base Configuration +{0x02F5,0xFF},//,Base Configuration +{0x02F6,0x3},//,Base Configuration +{0x02F7,0xFF},//,Base Configuration +{0x02F8,0x3},//,Base Configuration +{0x02F9,0xFF},//,Base Configuration +{0x02FA,0x3},//,Base Configuration +{0x02FB,0xFF},//,Base Configuration +{0x02FC,0x3},//,Base Configuration +{0x02FD,0xFF},//,Base Configuration +{0x02FE,0x3},//,Base Configuration +{0x02FF,0xFF},//,Base Configuration +{0x0300,0x3},//,Base Configuration +{0x0301,0xFF},//,Base Configuration +{0x0302,0x3},//,Base Configuration +{0x0303,0xFF},//,Base Configuration +{0x01E9,0x0},//,Base Configuration +{0x01E8,0x19},//,Base Configuration +{0x01EA,0x35},//,Base Configuration +{0x01EB,0x37},//,Base Configuration +{0x01EC,0x5C},//,Base Configuration +{0x01ED,0x63},//,Base Configuration +{0x01F8,0xF},//,Base Configuration +{0x005C,0x0},//,Base Configuration +{0x005D,0x0},//,Base Configuration +{0x01DA,0x1},//,Base Configuration +{0x01DC,0x1},//,Base Configuration +{0x01DE,0x1},//,Base Configuration +{0x0189,0x1},//,Base Configuration +{0x01B7,0x1},//,Base Configuration +{0x01C1,0xE},//,Base Configuration +{0x01C2,0xF6},//,Base Configuration +{0x01C3,0xFF},//,Base Configuration +{0x01B8,0x1},//,Base Configuration +{0x01BA,0x32},//,Base Configuration +{0x01BD,0x8},//,Base Configuration +{0x01CA,0x1E},//,Base Configuration +{0x01C9,0x1E},//,Base Configuration +{0x01BF,0x3C},//,Base Configuration +{0x01C0,0x5C},//,Base Configuration +{0x0071,0x1},//,Base Configuration +{0x01B4,0x1},//,Base Configuration +{0x01B5,0x1},//,Base Configuration +{0x01F1,0x1},//,Base Configuration +{0x01F4,0x1},//,Base Configuration +{0x01F5,0x1},//,Base Configuration +{0x0314,0x1},//,Base Configuration +{0x0315,0x1},//,Base Configuration +{0x0316,0x1},//,Base Configuration +{0x0207,0x0},//,Base Configuration +{0x4207,0x2},//,Base Configuration +{0x2207,0x2},//,Base Configuration +{0x209D,0x0},//,Base Configuration +{0x0063,0x1},//,Base Configuration +{0x01F7,0xF},//,Base Configuration +{0x00E9,0x3},//,Base Configuration +{0x00EA,0x28},//,Base Configuration +{0x0309,0x7},//,Base Configuration +{0x030A,0x4},//,Base Configuration +{0x030B,0xD},//,Base Configuration +{0x030C,0x7},//,Base Configuration +{0x030E,0x15},//,Base Configuration +{0x030D,0xA},//,Base Configuration +{0x030F,0x1},//,Base Configuration +{0x0310,0xF},//,Base Configuration +{0x01D0,0x1F},//,Base Configuration +{0x01D1,0x12},//,Base Configuration +{0x0016,0x0},//,Base Configuration +{0x0017,0x5},//,Base Configuration +{0x00E8,0x3},//,Base Configuration +{0xE0C0,0x0},//,Base Configuration +{0xE0C1,0x10},//,Base Configuration +{0xE0C2,0x0},//,Base Configuration +{0xE0C3,0x10},//,Base Configuration +{0x016A,0x1},//,Base Configuration.DPATH_BITMODE +{0x0168,0x2B},//,Base Configuration.CSI2_DATA_TYPE +{0xE000,0x0},//,PLL.BANK_SEL +{0x2077,0x0},//,PLL.PLL_DIV_N +{0x2076,0xBD},//,PLL.PLL_DIV_M +{0x00CE,0x1},//,PLL.CLKGEN_CP_DIV +{0x0070,0x9},//,PLL.OTP_GRANULARITY +{0x016D,0x32},//,PLL.GRAN_TG +{0x0176,0x0},//,PLL.LUT_DEL_008 +{0x20C6,0x0},//,PLL.CLKGEN_TX_ESC_H +{0x20C7,0x0},//,PLL.CLKGEN_TX_ESC_H +{0x20C8,0x1},//,PLL.CLKGEN_TX_ESC_H +{0x20C9,0x0},//,PLL.CLKGEN_TX_ESC_L +{0x20CA,0x0},//,PLL.CLKGEN_TX_ESC_L +{0x20CB,0x1},//,PLL.CLKGEN_TX_ESC_L +{0x2075,0x0},//,PLL.PLL_PD +{0xE000,0x0},//,Low Power State.BANK_SEL +{0x001E,0x1},//,Low Power State.EN_AUTO_LPS_TRANS +{0xE000,0x0},//,MIPI.BANK_SEL +{0x207E,0x0},//,MIPI.MIPI_VC_ID +{0x207F,0x0},//,MIPI.MIPI_FRAME_MODE_PIX +{0x2080,0x0},//,MIPI.MIPI_FRAME_MODE_EMBED +{0x2081,0x3},//,MIPI.MIPI_LINE_COUNT_EN +{0x2082,0x0},//,MIPI.MIPI_FRAME_COUNT_WRAP +{0x2083,0x2},//,MIPI.MIPI_FRAME_COUNT_WRAP +{0x0090,0x0},//,MIPI.CSI2_CONT_CLOCK_MODE +{0x2097,0x0},//,MIPI.CSI2_SCRAMBLING_EN +{0xE000,0x0},//,Sensor Control Mode.BANK_SEL +{0x0011,0x3},//,Sensor Control Mode.CTRL_MODE +{0x011D,0x0},//,Sensor Control Mode.SINGLE_PIN_CTRL +{0xE000,0x0},//,Time Bases.BANK_SEL +{0x0012,0x0},//,Time Bases.CLOCKS_PER_TIME_UNIT +{0x0013,0x18},//,Time Bases.CLOCKS_PER_TIME_UNIT +{0x015A,0x0},//,Time Bases.GLOB_TIME +{0x015B,0x33},//,Time Bases.GLOB_TIME +{0x015C,0x0},//,Time Bases.GLOB_TIME_A +{0x015D,0x33},//,Time Bases.GLOB_TIME_A +{0x015E,0x0},//,Time Bases.GLOB_TIME_B +{0x015F,0x33},//,Time Bases.GLOB_TIME_B +{0x0162,0x0},//,Time Bases.ENTER_LPS_TIME +{0x0163,0x5},//,Time Bases.ENTER_LPS_TIME +{0x0164,0x4},//,Time Bases.EXIT_LPS_TIME +{0x0165,0x4C},//,Time Bases.EXIT_LPS_TIME +{0x0166,0x4},//,Time Bases.LPS_CYCLE_TIME +{0x0167,0x4C},//,Time Bases.LPS_CYCLE_TIME +{0xE000,0x0},//,Analog Gain.BANK_SEL +{0x01BB,0xC8},//,Analog Gain +{0x01BC,0xC0},//,Analog Gain +{0x00D0,0x0},//,Analog Gain +{0x016E,0xBA},//,Analog Gain.LUT_DEL_000 +{0x0172,0x0},//,Analog Gain.LUT_DEL_004 +{0x0173,0x0},//,Analog Gain.LUT_DEL_005 +{0x016F,0x7E},//,Analog Gain.LUT_DEL_001 +{0x0170,0x0},//,Analog Gain.LUT_DEL_002 +{0x0171,0xBA},//,Analog Gain.LUT_DEL_003 +{0x0174,0x0},//,Analog Gain.LUT_DEL_006 +{0x0175,0x20},//,Analog Gain.LUT_DEL_007 +{0x018B,0x3},//,Analog Gain +{0x018C,0x2},//,Analog Gain +{0x018D,0x2},//,Analog Gain +{0x018E,0x56},//,Analog Gain +{0x018F,0x5},//,Analog Gain +{0x0190,0x7F},//,Analog Gain +{0x01EE,0x15},//,Analog Gain.SHUTTER_LAG +{0x01EF,0xD8},//,Analog Gain.SHUTTER_LAG +{0x01A2,0x5},//,Analog Gain.POS_ANACOL_TRIGGER +{0x01A3,0x6F},//,Analog Gain.POS_ANACOL_TRIGGER +{0x031F,0x5},//,Analog Gain.POS_YADDR_TRIGGER +{0x0320,0x78},//,Analog Gain.POS_YADDR_TRIGGER +{0x01A6,0x6},//,Analog Gain.POS_ADC_TRIGGER +{0x01A7,0x6},//,Analog Gain.POS_ADC_TRIGGER +{0x01A4,0x9},//,Analog Gain.POS_ANACOL_YBIN_TRIGGER +{0x01A5,0x30},//,Analog Gain.POS_ANACOL_YBIN_TRIGGER +{0x0321,0x9},//,Analog Gain.POS_YADDR_YBIN_TRIGGER +{0x0322,0x39},//,Analog Gain.POS_YADDR_YBIN_TRIGGER +{0x01A8,0x9},//,Analog Gain.POS_ADC_YBIN_TRIGGER +{0x01A9,0xC7},//,Analog Gain.POS_ADC_YBIN_TRIGGER +{0x01A0,0x0},//,Analog Gain.POS_VIS_TRIGGER +{0x01A1,0xCC},//,Analog Gain.POS_VIS_TRIGGER +{0x01B2,0x0},//,Analog Gain.POS_HSYNC_RISE +{0x01B3,0xE4},//,Analog Gain.POS_HSYNC_RISE +{0x01B0,0x0},//,Analog Gain.POS_STAT_TRIGGER +{0x01B1,0xDF},//,Analog Gain.POS_STAT_TRIGGER +{0x01AC,0x0},//,Analog Gain.POS_SVAL_TRIGGER +{0x01AD,0xEA},//,Analog Gain.POS_SVAL_TRIGGER +{0x01F0,0x24},//,Analog Gain +{0x01F3,0x1},//,Analog Gain +{0xE000,0x0},//,Black Level.BANK_SEL +{0x0193,0xF},//,Black Level.OFFSET_CLIPPING +{0x0194,0xA8},//,Black Level.OFFSET_CLIPPING +{0xE000,0x0},//,Release Soft Reset.BANK_SEL +{0xE009,0x1},//,Release Soft Reset +{0x212F,0x1},//,Release Soft Reset +{0x2130,0x1},//,Release Soft Reset +{0x2131,0x1},//,Release Soft Reset +{0x2132,0x1},//,Release Soft Reset +{0x2133,0x1},//,Release Soft Reset +{0x2134,0x1},//,Release Soft Reset +{0x2135,0x1},//,Release Soft Reset +{0xE0E1,0x1},//,Release Soft Reset +{0x018A,0x1},//,Release Soft Reset +{0x00E0,0x1},//,Release Soft Reset +{0xE004,0x0},//,Horizontal ROI.RW_CONTEXT +{0xE000,0x1},//,Horizontal ROI.BANK_SEL +{0xE02C,0x0},//,Horizontal ROI.XWIN_LEFT +{0xE02D,0xC},//,Horizontal ROI.XWIN_LEFT +{0xE02E,0x2},//,Horizontal ROI.XWIN_RIGHT +{0xE02F,0x4B},//,Horizontal ROI.XWIN_RIGHT +{0xE030,0x0},//,Horizontal ROI.XMIRROR +{0xE025,0x0},//,Horizontal ROI.XSUBSAMPLING +{0xE02A,0x0},//,Horizontal ROI.XBINNING +{0x2029,0x46},//,Horizontal ROI.MIPI_DATA_FIFO_THRESHOLD +{0x0034,0x1},//,Horizontal ROI.HSYNC_LENGTH +{0x0035,0x20},//,Horizontal ROI.HSYNC_LENGTH +{0xE004,0x1},//,Horizontal ROI.RW_CONTEXT +{0xE02C,0x0},//,Horizontal ROI.XWIN_LEFT +{0xE02D,0x0},//,Horizontal ROI.XWIN_LEFT +{0xE02E,0x2},//,Horizontal ROI.XWIN_RIGHT +{0xE02F,0x57},//,Horizontal ROI.XWIN_RIGHT +{0xE030,0x0},//,Horizontal ROI.XMIRROR +{0xE025,0x0},//,Horizontal ROI.XSUBSAMPLING +{0xE02A,0x0},//,Horizontal ROI.XBINNING +{0x2029,0x46},//,Horizontal ROI.MIPI_DATA_FIFO_THRESHOLD +{0x0034,0x1},//,Horizontal ROI.HSYNC_LENGTH +{0x0035,0x2C},//,Horizontal ROI.HSYNC_LENGTH +{0xE004,0x0},//,Vertical ROI.RW_CONTEXT +{0xE000,0x1},//,Vertical ROI.BANK_SEL +{0x001E,0x0},//,Vertical ROI.YWIN_ENA +{0x001F,0x1},//,Vertical ROI.YWIN_ENA +{0x002B,0x0},//,Vertical ROI.YBINNING +{0xE004,0x1},//,Vertical ROI.RW_CONTEXT +{0x001E,0x0},//,Vertical ROI.YWIN_ENA +{0x001F,0x1},//,Vertical ROI.YWIN_ENA +{0x002B,0x0},//,Vertical ROI.YBINNING +{0xE000,0x0},//,Vertical ROI.BANK_SEL +{0x001F,0x0},//,Vertical ROI.YWIN_BLACK +{0x0020,0x0},//,Vertical ROI.YWIN_BLACK +{0x0023,0x0},//,Vertical ROI.YWIN_DIR +{0x0024,0x3},//,Vertical ROI.YWIN0_SIZE +{0x0025,0x0},//,Vertical ROI.YWIN0_SIZE +{0x0026,0x0},//,Vertical ROI.YWIN0_START +{0x0027,0x18},//,Vertical ROI.YWIN0_START +{0x0028,0x0},//,Vertical ROI.YWIN0_SUBS +{0x0029,0x0},//,Vertical ROI.YWIN1_SIZE +{0x002A,0x0},//,Vertical ROI.YWIN1_SIZE +{0x002B,0x0},//,Vertical ROI.YWIN1_START +{0x002C,0x0},//,Vertical ROI.YWIN1_START +{0x002D,0x0},//,Vertical ROI.YWIN1_SUBS +{0x002E,0x0},//,Vertical ROI.YWIN2_SIZE +{0x002F,0x0},//,Vertical ROI.YWIN2_SIZE +{0x0030,0x0},//,Vertical ROI.YWIN2_START +{0x0031,0x0},//,Vertical ROI.YWIN2_START +{0x0032,0x0},//,Vertical ROI.YWIN2_SUBS +{0x0033,0x0},//,Vertical ROI.YWIN3_SIZE +{0x0034,0x0},//,Vertical ROI.YWIN3_SIZE +{0x0035,0x0},//,Vertical ROI.YWIN3_START +{0x0036,0x0},//,Vertical ROI.YWIN3_START +{0x0037,0x0},//,Vertical ROI.YWIN3_SUBS +{0x0038,0x0},//,Vertical ROI.YWIN4_SIZE +{0x0039,0x0},//,Vertical ROI.YWIN4_SIZE +{0x003A,0x0},//,Vertical ROI.YWIN4_START +{0x003B,0x0},//,Vertical ROI.YWIN4_START +{0x003C,0x0},//,Vertical ROI.YWIN4_SUBS +{0x003D,0x0},//,Vertical ROI.YWIN5_SIZE +{0x003E,0x0},//,Vertical ROI.YWIN5_SIZE +{0x003F,0x0},//,Vertical ROI.YWIN5_START +{0x0040,0x0},//,Vertical ROI.YWIN5_START +{0x0041,0x0},//,Vertical ROI.YWIN5_SUBS +{0x0042,0x0},//,Vertical ROI.YWIN6_SIZE +{0x0043,0x0},//,Vertical ROI.YWIN6_SIZE +{0x0044,0x0},//,Vertical ROI.YWIN6_START +{0x0045,0x0},//,Vertical ROI.YWIN6_START +{0x0046,0x0},//,Vertical ROI.YWIN6_SUBS +{0x0047,0x0},//,Vertical ROI.YWIN7_SIZE +{0x0048,0x0},//,Vertical ROI.YWIN7_SIZE +{0x0049,0x0},//,Vertical ROI.YWIN7_START +{0x004A,0x0},//,Vertical ROI.YWIN7_START +{0x004B,0x0},//,Vertical ROI.YWIN7_SUBS +{0x004C,0x0},//,Vertical ROI.YWIN8_SIZE +{0x004D,0x0},//,Vertical ROI.YWIN8_SIZE +{0x004E,0x0},//,Vertical ROI.YWIN8_START +{0x004F,0x0},//,Vertical ROI.YWIN8_START +{0x0050,0x0},//,Vertical ROI.YWIN8_SUBS +{0x0051,0x0},//,Vertical ROI.YWIN9_SIZE +{0x0052,0x0},//,Vertical ROI.YWIN9_SIZE +{0x0053,0x0},//,Vertical ROI.YWIN9_START +{0x0054,0x0},//,Vertical ROI.YWIN9_START +{0x0055,0x0},//,Vertical ROI.YWIN9_SUBS +{0xE004,0x0},//,Frame && Exposure Control.RW_CONTEXT +{0xE000,0x1},//,Frame && Exposure Control.BANK_SEL +{0x000E,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x000F,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x0010,0x3},//,Frame && Exposure Control.EXP_TIME_L +{0x0011,0xE8},//,Frame && Exposure Control.EXP_TIME_L +{0x0012,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0013,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0014,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0015,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0xE004,0x1},//,Frame && Exposure Control.RW_CONTEXT +{0x000E,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x000F,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x0010,0x3},//,Frame && Exposure Control.EXP_TIME_L +{0x0011,0xE8},//,Frame && Exposure Control.EXP_TIME_L +{0x0012,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0013,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0014,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0015,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0xE004,0x0},//,Frame && Exposure Control.RW_CONTEXT +{0x0032,0x7},//,Frame && Exposure Control.ROW_LENGTH +{0x0033,0x78},//,Frame && Exposure Control.ROW_LENGTH +{0xE004,0x1},//,Frame && Exposure Control.RW_CONTEXT +{0x0032,0x7},//,Frame && Exposure Control.ROW_LENGTH +{0x0033,0x78},//,Frame && Exposure Control.ROW_LENGTH +{0xE004,0x0},//,Frame && Exposure Control.RW_CONTEXT +{0x0007,0x1},//,Frame && Exposure Control.NROF_FRAMES +{0x0008,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x0009,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000A,0x41},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000B,0x1B},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0xE004,0x1},//,Frame && Exposure Control.RW_CONTEXT +{0x0007,0x1},//,Frame && Exposure Control.NROF_FRAMES +{0x0008,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x0009,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000A,0x41},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000B,0x1B},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0xE004,0x0},//,Digital Gain.RW_CONTEXT +{0xE000,0x1},//,Digital Gain.BANK_SEL +{0x0024,0xF},//,Digital Gain.GDIG_AMP +{0xE004,0x1},//,Digital Gain.RW_CONTEXT +{0x0024,0xF},//,Digital Gain.GDIG_AMP +{0xE000,0x0},//,Defect Pixel Correction.BANK_SEL +{0x0057,0x0},//,Defect Pixel Correction.DEFECT_ON +{0x0058,0x0},//,Defect Pixel Correction.DEFECT_MODE +{0x0059,0x2},//,Defect Pixel Correction.DEFECT_LIMIT_HIGH +{0x005A,0x2},//,Defect Pixel Correction.DEFECT_LIMIT_LOW +{0x005B,0x0},//,Defect Pixel Correction.DEFECT_LIMIT_HIGH_MODE +{0xE000,0x0},//,Context Switching.BANK_SEL +{0xE008,0x0},//,Context Switching.DISABLE_CONTEXTSYNC +{0x0006,0x1},//,Context Switching.PARAM_HOLD +{0xE003,0x0},//,Context Switching.NEXT_ACTIVE_CONTEXT +{0x0006,0x0},//,Context Switching.PARAM_HOLD +{0xE008,0x0},//,Context Switching.DISABLE_CONTEXTSYNC +{0xE004,0x0},//,Event Detection.RW_CONTEXT +{0xE000,0x1},//,Event Detection.BANK_SEL +{0x0031,0x0},//,Event Detection.EN_EVENT_DETECTION +{0xE004,0x1},//,Event Detection.RW_CONTEXT +{0x0031,0x0},//,Event Detection.EN_EVENT_DETECTION +{0xE000,0x0},//,Event Detection.BANK_SEL +{0x0138,0x0},//,Event Detection.EN_AUTO_CNTXT_SWITCH +{0xE005,0x0},//,Event Detection.AUTO_SWITCHTO_CNTXT +{0x0139,0x0},//,Event Detection.TILE_XSTART +{0x013A,0x0},//,Event Detection.TILE_XSTART +{0x013B,0x96},//,Event Detection.TILE_WIDTH +{0x013C,0x0},//,Event Detection.TILE_YSTART +{0x013D,0x0},//,Event Detection.TILE_YSTART +{0x013E,0xA0},//,Event Detection.TILE_HEIGHT +{0x013F,0x6},//,Event Detection.TILE_DISCARD_NR_BITS +{0x0140,0x1},//,Event Detection.MIN_FLAGGED_TILES +{0x0141,0x14},//,Event Detection.MAX_FLAGGED_TILES +{0x0142,0x1},//,Event Detection.TILE_THRESHOLD +{0x0143,0x0},//,Event Detection.TILE_MIN_THRESHOLD +{0x0144,0x0},//,Event Detection.TILE_MIN_THRESHOLD +{0x0145,0x0},//,Event Detection.TILE_DISABLE_MASK +{0x0146,0x0},//,Event Detection.TILE_DISABLE_MASK +{0x0147,0x0},//,Event Detection.TILE_DISABLE_MASK +{0x0148,0x0},//,Event Detection.CPY_TILES_INTERVAL +{0xE004,0x0},//,Image Statistics.RW_CONTEXT +{0xE000,0x1},//,Image Statistics.BANK_SEL +{0x0026,0x0},//,Image Statistics.STAT_NROF_HIST +{0xE004,0x1},//,Image Statistics.RW_CONTEXT +{0x0026,0x0},//,Image Statistics.STAT_NROF_HIST +{0xE000,0x0},//,Image Statistics.BANK_SEL +{0x0169,0x12},//,Image Statistics.CSI2_EMBED_TYPE +{0xE004,0x0},//,Illumination Trigger.RW_CONTEXT +{0xE000,0x1},//,Illumination Trigger.BANK_SEL +{0x001C,0x0},//,Illumination Trigger.EN_TRIG_ILLUM +{0x001D,0x0},//,Illumination Trigger.EN_TRIG_SYNC +{0x0019,0x0},//,Illumination Trigger.ILLUM_WIDTH +{0x001A,0x7},//,Illumination Trigger.ILLUM_WIDTH +{0x001B,0x53},//,Illumination Trigger.ILLUM_WIDTH +{0x0016,0x8},//,Illumination Trigger.ILLUM_DELAY +{0x0017,0x0},//,Illumination Trigger.ILLUM_DELAY +{0x0018,0x0},//,Illumination Trigger.ILLUM_DELAY +{0xE004,0x1},//,Illumination Trigger.RW_CONTEXT +{0x001C,0x0},//,Illumination Trigger.EN_TRIG_ILLUM +{0x001D,0x0},//,Illumination Trigger.EN_TRIG_SYNC +{0x0019,0x0},//,Illumination Trigger.ILLUM_WIDTH +{0x001A,0x7},//,Illumination Trigger.ILLUM_WIDTH +{0x001B,0x53},//,Illumination Trigger.ILLUM_WIDTH +{0x0016,0x8},//,Illumination Trigger.ILLUM_DELAY +{0x0017,0x0},//,Illumination Trigger.ILLUM_DELAY +{0x0018,0x0},//,Illumination Trigger.ILLUM_DELAY +{0xE000,0x0},//,Illumination Trigger.BANK_SEL +{0x001A,0x0},//,Illumination Trigger.TRIG_SYNC_DELAY +{0x001B,0x0},//,Illumination Trigger.TRIG_SYNC_DELAY +{0x001C,0x0},//,Illumination Trigger.TRIG_SYNC_DELAY +{0x00F3,0x27},//,Illumination Trigger.DMUX0_SEL +{0xE004,0x0},//,Synchronization Trigger.RW_CONTEXT +{0xE000,0x1},//,Synchronization Trigger.BANK_SEL +{0x001D,0x0},//,Synchronization Trigger.EN_TRIG_SYNC +{0xE004,0x1},//,Synchronization Trigger.RW_CONTEXT +{0x001D,0x0},//,Synchronization Trigger.EN_TRIG_SYNC +{0xE000,0x0},//,Synchronization Trigger.BANK_SEL +{0x001A,0x0},//,Synchronization Trigger.TRIG_SYNC_DELAY +{0x001B,0x0},//,Synchronization Trigger.TRIG_SYNC_DELAY +{0x001C,0x0},//,Synchronization Trigger.TRIG_SYNC_DELAY +{0x001D,0x0},//,Synchronization Trigger.TRIG_SYNC_ON_REQ_1 + + +}; + +static const struct mira050_reg full_576_768_50fps_10b_hs_1lane_reg_post_soft_reset[] = { + +}; + +// api 1.5.1, 1500Mbit/s 60 fps +static const struct mira050_reg full_576_768_50fps_8b_1lane_reg_pre_soft_reset[] = { + {0xE000,0x0},//,Base Configuration.BANK_SEL +{0x01E4,0x0},//,Base Configuration +{0x01E5,0x13},//,Base Configuration +{0x01E2,0x17},//,Base Configuration +{0x01E3,0x88},//,Base Configuration +{0x01E6,0x0},//,Base Configuration +{0x01E7,0xCA},//,Base Configuration +{0x016C,0x1},//,Base Configuration +{0x016B,0x1},//,Base Configuration +{0x0208,0x1},//,Base Configuration +{0x0209,0xF0},//,Base Configuration +{0x020A,0x3},//,Base Configuration +{0x020B,0x4D},//,Base Configuration +{0x020C,0x2},//,Base Configuration +{0x020D,0x10},//,Base Configuration +{0x020E,0x3},//,Base Configuration +{0x020F,0x1},//,Base Configuration +{0x0210,0x0},//,Base Configuration +{0x0211,0x13},//,Base Configuration +{0x0212,0x0},//,Base Configuration +{0x0213,0x3},//,Base Configuration +{0x0214,0x3},//,Base Configuration +{0x0215,0xEF},//,Base Configuration +{0x0216,0x3},//,Base Configuration +{0x0217,0xF3},//,Base Configuration +{0x0218,0x3},//,Base Configuration +{0x0219,0xF4},//,Base Configuration +{0x021A,0x1},//,Base Configuration +{0x021B,0xF1},//,Base Configuration +{0x021C,0x3},//,Base Configuration +{0x021D,0x24},//,Base Configuration +{0x021E,0x0},//,Base Configuration +{0x021F,0x2},//,Base Configuration +{0x0220,0x1},//,Base Configuration +{0x0221,0xF2},//,Base Configuration +{0x0222,0x3},//,Base Configuration +{0x0223,0x2F},//,Base Configuration +{0x0224,0x0},//,Base Configuration +{0x0225,0x21},//,Base Configuration +{0x0226,0x3},//,Base Configuration +{0x0227,0xF0},//,Base Configuration +{0x0228,0x3},//,Base Configuration +{0x0229,0xF1},//,Base Configuration +{0x022A,0x3},//,Base Configuration +{0x022B,0xF2},//,Base Configuration +{0x022C,0x3},//,Base Configuration +{0x022D,0xF5},//,Base Configuration +{0x022E,0x3},//,Base Configuration +{0x022F,0xF6},//,Base Configuration +{0x0230,0x0},//,Base Configuration +{0x0231,0xC1},//,Base Configuration +{0x0232,0x0},//,Base Configuration +{0x0233,0x2},//,Base Configuration +{0x0234,0x1},//,Base Configuration +{0x0235,0xF2},//,Base Configuration +{0x0236,0x3},//,Base Configuration +{0x0237,0x6B},//,Base Configuration +{0x0238,0x3},//,Base Configuration +{0x0239,0xFF},//,Base Configuration +{0x023A,0x3},//,Base Configuration +{0x023B,0x31},//,Base Configuration +{0x023C,0x1},//,Base Configuration +{0x023D,0xF0},//,Base Configuration +{0x023E,0x3},//,Base Configuration +{0x023F,0x87},//,Base Configuration +{0x0240,0x2},//,Base Configuration +{0x0241,0x3A},//,Base Configuration +{0x0242,0x0},//,Base Configuration +{0x0243,0xB},//,Base Configuration +{0x0244,0x1},//,Base Configuration +{0x0245,0xF9},//,Base Configuration +{0x0246,0x3},//,Base Configuration +{0x0247,0xD},//,Base Configuration +{0x0248,0x0},//,Base Configuration +{0x0249,0x7},//,Base Configuration +{0x024A,0x3},//,Base Configuration +{0x024B,0xEF},//,Base Configuration +{0x024C,0x3},//,Base Configuration +{0x024D,0xF3},//,Base Configuration +{0x024E,0x3},//,Base Configuration +{0x024F,0xF4},//,Base Configuration +{0x0250,0x3},//,Base Configuration +{0x0251,0x0},//,Base Configuration +{0x0252,0x0},//,Base Configuration +{0x0253,0x7},//,Base Configuration +{0x0254,0x0},//,Base Configuration +{0x0255,0xC},//,Base Configuration +{0x0256,0x1},//,Base Configuration +{0x0257,0xF1},//,Base Configuration +{0x0258,0x3},//,Base Configuration +{0x0259,0x43},//,Base Configuration +{0x025A,0x1},//,Base Configuration +{0x025B,0xF8},//,Base Configuration +{0x025C,0x3},//,Base Configuration +{0x025D,0x10},//,Base Configuration +{0x025E,0x0},//,Base Configuration +{0x025F,0x7},//,Base Configuration +{0x0260,0x3},//,Base Configuration +{0x0261,0xF0},//,Base Configuration +{0x0262,0x3},//,Base Configuration +{0x0263,0xF1},//,Base Configuration +{0x0264,0x3},//,Base Configuration +{0x0265,0xF2},//,Base Configuration +{0x0266,0x3},//,Base Configuration +{0x0267,0xF5},//,Base Configuration +{0x0268,0x3},//,Base Configuration +{0x0269,0xF6},//,Base Configuration +{0x026A,0x3},//,Base Configuration +{0x026B,0x0},//,Base Configuration +{0x026C,0x2},//,Base Configuration +{0x026D,0x87},//,Base Configuration +{0x026E,0x2},//,Base Configuration +{0x026F,0x31},//,Base Configuration +{0x0270,0x3},//,Base Configuration +{0x0271,0xFF},//,Base Configuration +{0x0272,0x3},//,Base Configuration +{0x0273,0x0},//,Base Configuration +{0x0274,0x3},//,Base Configuration +{0x0275,0xFF},//,Base Configuration +{0x0276,0x2},//,Base Configuration +{0x0277,0x87},//,Base Configuration +{0x0278,0x3},//,Base Configuration +{0x0279,0x2},//,Base Configuration +{0x027A,0x3},//,Base Configuration +{0x027B,0x9},//,Base Configuration +{0x027C,0x3},//,Base Configuration +{0x027D,0xF7},//,Base Configuration +{0x027E,0x0},//,Base Configuration +{0x027F,0x16},//,Base Configuration +{0x0280,0x0},//,Base Configuration +{0x0281,0x33},//,Base Configuration +{0x0282,0x0},//,Base Configuration +{0x0283,0x4},//,Base Configuration +{0x0284,0x0},//,Base Configuration +{0x0285,0x11},//,Base Configuration +{0x0286,0x3},//,Base Configuration +{0x0287,0x9},//,Base Configuration +{0x0288,0x0},//,Base Configuration +{0x0289,0x2},//,Base Configuration +{0x028A,0x0},//,Base Configuration +{0x028B,0x20},//,Base Configuration +{0x028C,0x0},//,Base Configuration +{0x028D,0xB5},//,Base Configuration +{0x028E,0x1},//,Base Configuration +{0x028F,0x5},//,Base Configuration +{0x0290,0x0},//,Base Configuration +{0x0291,0x12},//,Base Configuration +{0x0292,0x0},//,Base Configuration +{0x0293,0xB5},//,Base Configuration +{0x0294,0x1},//,Base Configuration +{0x0295,0x5},//,Base Configuration +{0x0296,0x0},//,Base Configuration +{0x0297,0x0},//,Base Configuration +{0x0298,0x0},//,Base Configuration +{0x0299,0x12},//,Base Configuration +{0x029A,0x0},//,Base Configuration +{0x029B,0x12},//,Base Configuration +{0x029C,0x0},//,Base Configuration +{0x029D,0x20},//,Base Configuration +{0x029E,0x0},//,Base Configuration +{0x029F,0xB5},//,Base Configuration +{0x02A0,0x1},//,Base Configuration +{0x02A1,0x5},//,Base Configuration +{0x02A2,0x0},//,Base Configuration +{0x02A3,0x0},//,Base Configuration +{0x02A4,0x0},//,Base Configuration +{0x02A5,0x12},//,Base Configuration +{0x02A6,0x0},//,Base Configuration +{0x02A7,0x12},//,Base Configuration +{0x02A8,0x0},//,Base Configuration +{0x02A9,0x20},//,Base Configuration +{0x02AA,0x0},//,Base Configuration +{0x02AB,0x47},//,Base Configuration +{0x02AC,0x0},//,Base Configuration +{0x02AD,0x27},//,Base Configuration +{0x02AE,0x0},//,Base Configuration +{0x02AF,0xB5},//,Base Configuration +{0x02B0,0x0},//,Base Configuration +{0x02B1,0xE5},//,Base Configuration +{0x02B2,0x0},//,Base Configuration +{0x02B3,0x0},//,Base Configuration +{0x02B4,0x0},//,Base Configuration +{0x02B5,0x4},//,Base Configuration +{0x02B6,0x0},//,Base Configuration +{0x02B7,0x43},//,Base Configuration +{0x02B8,0x0},//,Base Configuration +{0x02B9,0x1},//,Base Configuration +{0x02BA,0x3},//,Base Configuration +{0x02BB,0x2},//,Base Configuration +{0x02BC,0x0},//,Base Configuration +{0x02BD,0x8},//,Base Configuration +{0x02BE,0x3},//,Base Configuration +{0x02BF,0xFF},//,Base Configuration +{0x02C0,0x2},//,Base Configuration +{0x02C1,0x87},//,Base Configuration +{0x02C2,0x3},//,Base Configuration +{0x02C3,0x89},//,Base Configuration +{0x02C4,0x3},//,Base Configuration +{0x02C5,0xF7},//,Base Configuration +{0x02C6,0x0},//,Base Configuration +{0x02C7,0x77},//,Base Configuration +{0x02C8,0x0},//,Base Configuration +{0x02C9,0x17},//,Base Configuration +{0x02CA,0x0},//,Base Configuration +{0x02CB,0x8},//,Base Configuration +{0x02CC,0x3},//,Base Configuration +{0x02CD,0xFF},//,Base Configuration +{0x02CE,0x0},//,Base Configuration +{0x02CF,0x38},//,Base Configuration +{0x02D0,0x0},//,Base Configuration +{0x02D1,0x17},//,Base Configuration +{0x02D2,0x0},//,Base Configuration +{0x02D3,0x8},//,Base Configuration +{0x02D4,0x3},//,Base Configuration +{0x02D5,0xFF},//,Base Configuration +{0x02D6,0x3},//,Base Configuration +{0x02D7,0xFF},//,Base Configuration +{0x02D8,0x3},//,Base Configuration +{0x02D9,0xFF},//,Base Configuration +{0x02DA,0x3},//,Base Configuration +{0x02DB,0xFF},//,Base Configuration +{0x02DC,0x3},//,Base Configuration +{0x02DD,0xFF},//,Base Configuration +{0x02DE,0x3},//,Base Configuration +{0x02DF,0xFF},//,Base Configuration +{0x02E0,0x3},//,Base Configuration +{0x02E1,0xFF},//,Base Configuration +{0x02E2,0x3},//,Base Configuration +{0x02E3,0xFF},//,Base Configuration +{0x02E4,0x3},//,Base Configuration +{0x02E5,0xFF},//,Base Configuration +{0x02E6,0x3},//,Base Configuration +{0x02E7,0xFF},//,Base Configuration +{0x02E8,0x3},//,Base Configuration +{0x02E9,0xFF},//,Base Configuration +{0x02EA,0x3},//,Base Configuration +{0x02EB,0xFF},//,Base Configuration +{0x02EC,0x3},//,Base Configuration +{0x02ED,0xFF},//,Base Configuration +{0x02EE,0x3},//,Base Configuration +{0x02EF,0xFF},//,Base Configuration +{0x02F0,0x3},//,Base Configuration +{0x02F1,0xFF},//,Base Configuration +{0x02F2,0x3},//,Base Configuration +{0x02F3,0xFF},//,Base Configuration +{0x02F4,0x3},//,Base Configuration +{0x02F5,0xFF},//,Base Configuration +{0x02F6,0x3},//,Base Configuration +{0x02F7,0xFF},//,Base Configuration +{0x02F8,0x3},//,Base Configuration +{0x02F9,0xFF},//,Base Configuration +{0x02FA,0x3},//,Base Configuration +{0x02FB,0xFF},//,Base Configuration +{0x02FC,0x3},//,Base Configuration +{0x02FD,0xFF},//,Base Configuration +{0x02FE,0x3},//,Base Configuration +{0x02FF,0xFF},//,Base Configuration +{0x0300,0x3},//,Base Configuration +{0x0301,0xFF},//,Base Configuration +{0x0302,0x3},//,Base Configuration +{0x0303,0xFF},//,Base Configuration +{0x01E9,0x0},//,Base Configuration +{0x01E8,0x19},//,Base Configuration +{0x01EA,0x35},//,Base Configuration +{0x01EB,0x37},//,Base Configuration +{0x01EC,0x5C},//,Base Configuration +{0x01ED,0x63},//,Base Configuration +{0x01F8,0xF},//,Base Configuration +{0x005C,0x0},//,Base Configuration +{0x005D,0x0},//,Base Configuration +{0x01DA,0x1},//,Base Configuration +{0x01DC,0x1},//,Base Configuration +{0x01DE,0x1},//,Base Configuration +{0x0189,0x1},//,Base Configuration +{0x01B7,0x1},//,Base Configuration +{0x01C1,0xE},//,Base Configuration +{0x01C2,0xF6},//,Base Configuration +{0x01C3,0xFF},//,Base Configuration +{0x01B8,0x1},//,Base Configuration +{0x01BA,0x32},//,Base Configuration +{0x01BD,0x8},//,Base Configuration +{0x01CA,0x1E},//,Base Configuration +{0x01C9,0x1E},//,Base Configuration +{0x01BF,0x3C},//,Base Configuration +{0x01C0,0x5C},//,Base Configuration +{0x0071,0x1},//,Base Configuration +{0x01B4,0x1},//,Base Configuration +{0x01B5,0x1},//,Base Configuration +{0x01F1,0x1},//,Base Configuration +{0x01F4,0x1},//,Base Configuration +{0x01F5,0x1},//,Base Configuration +{0x0314,0x1},//,Base Configuration +{0x0315,0x1},//,Base Configuration +{0x0316,0x1},//,Base Configuration +{0x0207,0x0},//,Base Configuration +{0x4207,0x2},//,Base Configuration +{0x2207,0x2},//,Base Configuration +{0x209D,0x0},//,Base Configuration +{0x0063,0x1},//,Base Configuration +{0x01F7,0xF},//,Base Configuration +{0x00E9,0x3},//,Base Configuration +{0x00EA,0x28},//,Base Configuration +{0x0309,0x7},//,Base Configuration +{0x030A,0x4},//,Base Configuration +{0x030B,0xD},//,Base Configuration +{0x030C,0x7},//,Base Configuration +{0x030E,0x15},//,Base Configuration +{0x030D,0xA},//,Base Configuration +{0x030F,0x1},//,Base Configuration +{0x0310,0xF},//,Base Configuration +{0x01D0,0x1F},//,Base Configuration +{0x01D1,0x12},//,Base Configuration +{0x0016,0x0},//,Base Configuration +{0x0017,0x5},//,Base Configuration +{0x00E8,0x3},//,Base Configuration +{0xE0C0,0x0},//,Base Configuration +{0xE0C1,0x8},//,Base Configuration +{0xE0C2,0x0},//,Base Configuration +{0xE0C3,0x8},//,Base Configuration +{0x016A,0x0},//,Base Configuration.DPATH_BITMODE +{0x0168,0x2A},//,Base Configuration.CSI2_DATA_TYPE +{0xE000,0x0},//,PLL.BANK_SEL +{0x2077,0x0},//,PLL.PLL_DIV_N +{0x2076,0xBD},//,PLL.PLL_DIV_M +{0x00CE,0x1},//,PLL.CLKGEN_CP_DIV +{0x0070,0x9},//,PLL.OTP_GRANULARITY +{0x016D,0x32},//,PLL.GRAN_TG +{0x0176,0x0},//,PLL.LUT_DEL_008 +{0x20C6,0x0},//,PLL.CLKGEN_TX_ESC_H +{0x20C7,0x0},//,PLL.CLKGEN_TX_ESC_H +{0x20C8,0x1},//,PLL.CLKGEN_TX_ESC_H +{0x20C9,0x0},//,PLL.CLKGEN_TX_ESC_L +{0x20CA,0x0},//,PLL.CLKGEN_TX_ESC_L +{0x20CB,0x1},//,PLL.CLKGEN_TX_ESC_L +{0x2075,0x0},//,PLL.PLL_PD +{0xE000,0x0},//,Low Power State.BANK_SEL +{0x001E,0x1},//,Low Power State.EN_AUTO_LPS_TRANS +{0xE000,0x0},//,MIPI.BANK_SEL +{0x207E,0x0},//,MIPI.MIPI_VC_ID +{0x207F,0x0},//,MIPI.MIPI_FRAME_MODE_PIX +{0x2080,0x0},//,MIPI.MIPI_FRAME_MODE_EMBED +{0x2081,0x3},//,MIPI.MIPI_LINE_COUNT_EN +{0x2082,0x0},//,MIPI.MIPI_FRAME_COUNT_WRAP +{0x2083,0x2},//,MIPI.MIPI_FRAME_COUNT_WRAP +{0x0090,0x0},//,MIPI.CSI2_CONT_CLOCK_MODE +{0x2097,0x0},//,MIPI.CSI2_SCRAMBLING_EN +{0xE000,0x0},//,Sensor Control Mode.BANK_SEL +{0x0011,0x3},//,Sensor Control Mode.CTRL_MODE +{0x011D,0x0},//,Sensor Control Mode.SINGLE_PIN_CTRL +{0xE000,0x0},//,Time Bases.BANK_SEL +{0x0012,0x0},//,Time Bases.CLOCKS_PER_TIME_UNIT +{0x0013,0x18},//,Time Bases.CLOCKS_PER_TIME_UNIT +{0x015A,0x0},//,Time Bases.GLOB_TIME +{0x015B,0x33},//,Time Bases.GLOB_TIME +{0x015C,0x0},//,Time Bases.GLOB_TIME_A +{0x015D,0x33},//,Time Bases.GLOB_TIME_A +{0x015E,0x0},//,Time Bases.GLOB_TIME_B +{0x015F,0x33},//,Time Bases.GLOB_TIME_B +{0x0162,0x0},//,Time Bases.ENTER_LPS_TIME +{0x0163,0x5},//,Time Bases.ENTER_LPS_TIME +{0x0164,0x4},//,Time Bases.EXIT_LPS_TIME +{0x0165,0x4C},//,Time Bases.EXIT_LPS_TIME +{0x0166,0x4},//,Time Bases.LPS_CYCLE_TIME +{0x0167,0x4C},//,Time Bases.LPS_CYCLE_TIME +{0xE000,0x0},//,Analog Gain.BANK_SEL +{0x01BB,0xC8},//,Analog Gain +{0x01BC,0xC0},//,Analog Gain +{0x00D0,0x0},//,Analog Gain +{0x016E,0xBA},//,Analog Gain.LUT_DEL_000 +{0x0172,0x0},//,Analog Gain.LUT_DEL_004 +{0x0173,0x0},//,Analog Gain.LUT_DEL_005 +{0x016F,0x7E},//,Analog Gain.LUT_DEL_001 +{0x0170,0x0},//,Analog Gain.LUT_DEL_002 +{0x0171,0xBA},//,Analog Gain.LUT_DEL_003 +{0x0174,0x0},//,Analog Gain.LUT_DEL_006 +{0x0175,0x20},//,Analog Gain.LUT_DEL_007 +{0x018B,0x3},//,Analog Gain +{0x018C,0x2},//,Analog Gain +{0x018D,0x2},//,Analog Gain +{0x018E,0x56},//,Analog Gain +{0x018F,0x5},//,Analog Gain +{0x0190,0x7F},//,Analog Gain +{0x01EE,0x16},//,Analog Gain.SHUTTER_LAG +{0x01EF,0x6E},//,Analog Gain.SHUTTER_LAG +{0x01A2,0x4},//,Analog Gain.POS_ANACOL_TRIGGER +{0x01A3,0xD9},//,Analog Gain.POS_ANACOL_TRIGGER +{0x031F,0x4},//,Analog Gain.POS_YADDR_TRIGGER +{0x0320,0xE2},//,Analog Gain.POS_YADDR_TRIGGER +{0x01A6,0x5},//,Analog Gain.POS_ADC_TRIGGER +{0x01A7,0x70},//,Analog Gain.POS_ADC_TRIGGER +{0x01A4,0x8},//,Analog Gain.POS_ANACOL_YBIN_TRIGGER +{0x01A5,0x9A},//,Analog Gain.POS_ANACOL_YBIN_TRIGGER +{0x0321,0x8},//,Analog Gain.POS_YADDR_YBIN_TRIGGER +{0x0322,0xA3},//,Analog Gain.POS_YADDR_YBIN_TRIGGER +{0x01A8,0x9},//,Analog Gain.POS_ADC_YBIN_TRIGGER +{0x01A9,0x31},//,Analog Gain.POS_ADC_YBIN_TRIGGER +{0x01A0,0x0},//,Analog Gain.POS_VIS_TRIGGER +{0x01A1,0xFF},//,Analog Gain.POS_VIS_TRIGGER +{0x01B2,0x1},//,Analog Gain.POS_HSYNC_RISE +{0x01B3,0x17},//,Analog Gain.POS_HSYNC_RISE +{0x01B0,0x1},//,Analog Gain.POS_STAT_TRIGGER +{0x01B1,0x12},//,Analog Gain.POS_STAT_TRIGGER +{0x01AC,0x1},//,Analog Gain.POS_SVAL_TRIGGER +{0x01AD,0x1D},//,Analog Gain.POS_SVAL_TRIGGER +{0x01F0,0x24},//,Analog Gain +{0x01F3,0x1},//,Analog Gain +{0xE195,0xF},//,Analog Gain +{0xE000,0x0},//,Black Level.BANK_SEL +{0x0193,0xF},//,Black Level.OFFSET_CLIPPING +{0x0194,0xA8},//,Black Level.OFFSET_CLIPPING +{0xE000,0x0},//,Release Soft Reset.BANK_SEL +{0xE009,0x1},//,Release Soft Reset +{0x212F,0x1},//,Release Soft Reset +{0x2130,0x1},//,Release Soft Reset +{0x2131,0x1},//,Release Soft Reset +{0x2132,0x1},//,Release Soft Reset +{0x2133,0x1},//,Release Soft Reset +{0x2134,0x1},//,Release Soft Reset +{0x2135,0x1},//,Release Soft Reset +{0xE0E1,0x1},//,Release Soft Reset +{0x018A,0x1},//,Release Soft Reset +{0x00E0,0x1},//,Release Soft Reset +{0xE004,0x0},//,Horizontal ROI.RW_CONTEXT +{0xE000,0x1},//,Horizontal ROI.BANK_SEL +{0xE02C,0x0},//,Horizontal ROI.XWIN_LEFT +{0xE02D,0xC},//,Horizontal ROI.XWIN_LEFT +{0xE02E,0x2},//,Horizontal ROI.XWIN_RIGHT +{0xE02F,0x4B},//,Horizontal ROI.XWIN_RIGHT +{0xE030,0x0},//,Horizontal ROI.XMIRROR +{0xE025,0x0},//,Horizontal ROI.XSUBSAMPLING +{0xE02A,0x0},//,Horizontal ROI.XBINNING +{0x2029,0x46},//,Horizontal ROI.MIPI_DATA_FIFO_THRESHOLD +{0x0034,0x1},//,Horizontal ROI.HSYNC_LENGTH +{0x0035,0x20},//,Horizontal ROI.HSYNC_LENGTH +{0xE004,0x1},//,Horizontal ROI.RW_CONTEXT +{0xE02C,0x0},//,Horizontal ROI.XWIN_LEFT +{0xE02D,0x0},//,Horizontal ROI.XWIN_LEFT +{0xE02E,0x2},//,Horizontal ROI.XWIN_RIGHT +{0xE02F,0x57},//,Horizontal ROI.XWIN_RIGHT +{0xE030,0x0},//,Horizontal ROI.XMIRROR +{0xE025,0x0},//,Horizontal ROI.XSUBSAMPLING +{0xE02A,0x0},//,Horizontal ROI.XBINNING +{0x2029,0x46},//,Horizontal ROI.MIPI_DATA_FIFO_THRESHOLD +{0x0034,0x1},//,Horizontal ROI.HSYNC_LENGTH +{0x0035,0x2C},//,Horizontal ROI.HSYNC_LENGTH +{0xE004,0x0},//,Vertical ROI.RW_CONTEXT +{0xE000,0x1},//,Vertical ROI.BANK_SEL +{0x001E,0x0},//,Vertical ROI.YWIN_ENA +{0x001F,0x1},//,Vertical ROI.YWIN_ENA +{0x002B,0x0},//,Vertical ROI.YBINNING +{0xE004,0x1},//,Vertical ROI.RW_CONTEXT +{0x001E,0x0},//,Vertical ROI.YWIN_ENA +{0x001F,0x1},//,Vertical ROI.YWIN_ENA +{0x002B,0x0},//,Vertical ROI.YBINNING +{0xE000,0x0},//,Vertical ROI.BANK_SEL +{0x001F,0x0},//,Vertical ROI.YWIN_BLACK +{0x0020,0x0},//,Vertical ROI.YWIN_BLACK +{0x0023,0x0},//,Vertical ROI.YWIN_DIR +{0x0024,0x3},//,Vertical ROI.YWIN0_SIZE +{0x0025,0x0},//,Vertical ROI.YWIN0_SIZE +{0x0026,0x0},//,Vertical ROI.YWIN0_START +{0x0027,0x18},//,Vertical ROI.YWIN0_START +{0x0028,0x0},//,Vertical ROI.YWIN0_SUBS +{0x0029,0x0},//,Vertical ROI.YWIN1_SIZE +{0x002A,0x0},//,Vertical ROI.YWIN1_SIZE +{0x002B,0x0},//,Vertical ROI.YWIN1_START +{0x002C,0x0},//,Vertical ROI.YWIN1_START +{0x002D,0x0},//,Vertical ROI.YWIN1_SUBS +{0x002E,0x0},//,Vertical ROI.YWIN2_SIZE +{0x002F,0x0},//,Vertical ROI.YWIN2_SIZE +{0x0030,0x0},//,Vertical ROI.YWIN2_START +{0x0031,0x0},//,Vertical ROI.YWIN2_START +{0x0032,0x0},//,Vertical ROI.YWIN2_SUBS +{0x0033,0x0},//,Vertical ROI.YWIN3_SIZE +{0x0034,0x0},//,Vertical ROI.YWIN3_SIZE +{0x0035,0x0},//,Vertical ROI.YWIN3_START +{0x0036,0x0},//,Vertical ROI.YWIN3_START +{0x0037,0x0},//,Vertical ROI.YWIN3_SUBS +{0x0038,0x0},//,Vertical ROI.YWIN4_SIZE +{0x0039,0x0},//,Vertical ROI.YWIN4_SIZE +{0x003A,0x0},//,Vertical ROI.YWIN4_START +{0x003B,0x0},//,Vertical ROI.YWIN4_START +{0x003C,0x0},//,Vertical ROI.YWIN4_SUBS +{0x003D,0x0},//,Vertical ROI.YWIN5_SIZE +{0x003E,0x0},//,Vertical ROI.YWIN5_SIZE +{0x003F,0x0},//,Vertical ROI.YWIN5_START +{0x0040,0x0},//,Vertical ROI.YWIN5_START +{0x0041,0x0},//,Vertical ROI.YWIN5_SUBS +{0x0042,0x0},//,Vertical ROI.YWIN6_SIZE +{0x0043,0x0},//,Vertical ROI.YWIN6_SIZE +{0x0044,0x0},//,Vertical ROI.YWIN6_START +{0x0045,0x0},//,Vertical ROI.YWIN6_START +{0x0046,0x0},//,Vertical ROI.YWIN6_SUBS +{0x0047,0x0},//,Vertical ROI.YWIN7_SIZE +{0x0048,0x0},//,Vertical ROI.YWIN7_SIZE +{0x0049,0x0},//,Vertical ROI.YWIN7_START +{0x004A,0x0},//,Vertical ROI.YWIN7_START +{0x004B,0x0},//,Vertical ROI.YWIN7_SUBS +{0x004C,0x0},//,Vertical ROI.YWIN8_SIZE +{0x004D,0x0},//,Vertical ROI.YWIN8_SIZE +{0x004E,0x0},//,Vertical ROI.YWIN8_START +{0x004F,0x0},//,Vertical ROI.YWIN8_START +{0x0050,0x0},//,Vertical ROI.YWIN8_SUBS +{0x0051,0x0},//,Vertical ROI.YWIN9_SIZE +{0x0052,0x0},//,Vertical ROI.YWIN9_SIZE +{0x0053,0x0},//,Vertical ROI.YWIN9_START +{0x0054,0x0},//,Vertical ROI.YWIN9_START +{0x0055,0x0},//,Vertical ROI.YWIN9_SUBS +{0xE004,0x0},//,Frame && Exposure Control.RW_CONTEXT +{0xE000,0x1},//,Frame && Exposure Control.BANK_SEL +{0x000E,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x000F,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x0010,0x3},//,Frame && Exposure Control.EXP_TIME_L +{0x0011,0xE8},//,Frame && Exposure Control.EXP_TIME_L +{0x0012,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0013,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0014,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0015,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0xE004,0x1},//,Frame && Exposure Control.RW_CONTEXT +{0x000E,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x000F,0x0},//,Frame && Exposure Control.EXP_TIME_L +{0x0010,0x3},//,Frame && Exposure Control.EXP_TIME_L +{0x0011,0xE8},//,Frame && Exposure Control.EXP_TIME_L +{0x0012,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0013,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0014,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0x0015,0x0},//,Frame && Exposure Control.EXP_TIME_S +{0xE004,0x0},//,Frame && Exposure Control.RW_CONTEXT +{0x0032,0x6},//,Frame && Exposure Control.ROW_LENGTH +{0x0033,0xE2},//,Frame && Exposure Control.ROW_LENGTH +{0xE004,0x1},//,Frame && Exposure Control.RW_CONTEXT +{0x0032,0x6},//,Frame && Exposure Control.ROW_LENGTH +{0x0033,0xE2},//,Frame && Exposure Control.ROW_LENGTH +{0xE004,0x0},//,Frame && Exposure Control.RW_CONTEXT +{0x0007,0x1},//,Frame && Exposure Control.NROF_FRAMES +{0x0008,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x0009,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000A,0x41},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000B,0x1B},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0xE004,0x1},//,Frame && Exposure Control.RW_CONTEXT +{0x0007,0x1},//,Frame && Exposure Control.NROF_FRAMES +{0x0008,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x0009,0x0},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000A,0x41},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0x000B,0x1B},//,Frame && Exposure Control.TARGET_FRAME_TIME +{0xE004,0x0},//,Digital Gain.RW_CONTEXT +{0xE000,0x1},//,Digital Gain.BANK_SEL +{0x0024,0xF},//,Digital Gain.GDIG_AMP +{0xE004,0x1},//,Digital Gain.RW_CONTEXT +{0x0024,0xF},//,Digital Gain.GDIG_AMP +{0xE000,0x0},//,Defect Pixel Correction.BANK_SEL +{0x0057,0x0},//,Defect Pixel Correction.DEFECT_ON +{0x0058,0x0},//,Defect Pixel Correction.DEFECT_MODE +{0x0059,0x2},//,Defect Pixel Correction.DEFECT_LIMIT_HIGH +{0x005A,0x2},//,Defect Pixel Correction.DEFECT_LIMIT_LOW +{0x005B,0x0},//,Defect Pixel Correction.DEFECT_LIMIT_HIGH_MODE +{0xE000,0x0},//,Context Switching.BANK_SEL +{0xE008,0x0},//,Context Switching.DISABLE_CONTEXTSYNC +{0x0006,0x1},//,Context Switching.PARAM_HOLD +{0xE003,0x0},//,Context Switching.NEXT_ACTIVE_CONTEXT +{0x0006,0x0},//,Context Switching.PARAM_HOLD +{0xE008,0x0},//,Context Switching.DISABLE_CONTEXTSYNC +{0xE004,0x0},//,Event Detection.RW_CONTEXT +{0xE000,0x1},//,Event Detection.BANK_SEL +{0x0031,0x0},//,Event Detection.EN_EVENT_DETECTION +{0xE004,0x1},//,Event Detection.RW_CONTEXT +{0x0031,0x0},//,Event Detection.EN_EVENT_DETECTION +{0xE000,0x0},//,Event Detection.BANK_SEL +{0x0138,0x0},//,Event Detection.EN_AUTO_CNTXT_SWITCH +{0xE005,0x0},//,Event Detection.AUTO_SWITCHTO_CNTXT +{0x0139,0x0},//,Event Detection.TILE_XSTART +{0x013A,0x0},//,Event Detection.TILE_XSTART +{0x013B,0x96},//,Event Detection.TILE_WIDTH +{0x013C,0x0},//,Event Detection.TILE_YSTART +{0x013D,0x0},//,Event Detection.TILE_YSTART +{0x013E,0xA0},//,Event Detection.TILE_HEIGHT +{0x013F,0x6},//,Event Detection.TILE_DISCARD_NR_BITS +{0x0140,0x1},//,Event Detection.MIN_FLAGGED_TILES +{0x0141,0x14},//,Event Detection.MAX_FLAGGED_TILES +{0x0142,0x1},//,Event Detection.TILE_THRESHOLD +{0x0143,0x0},//,Event Detection.TILE_MIN_THRESHOLD +{0x0144,0x0},//,Event Detection.TILE_MIN_THRESHOLD +{0x0145,0x0},//,Event Detection.TILE_DISABLE_MASK +{0x0146,0x0},//,Event Detection.TILE_DISABLE_MASK +{0x0147,0x0},//,Event Detection.TILE_DISABLE_MASK +{0x0148,0x0},//,Event Detection.CPY_TILES_INTERVAL +{0xE004,0x0},//,Image Statistics.RW_CONTEXT +{0xE000,0x1},//,Image Statistics.BANK_SEL +{0x0026,0x0},//,Image Statistics.STAT_NROF_HIST +{0xE004,0x1},//,Image Statistics.RW_CONTEXT +{0x0026,0x0},//,Image Statistics.STAT_NROF_HIST +{0xE000,0x0},//,Image Statistics.BANK_SEL +{0x0169,0x12},//,Image Statistics.CSI2_EMBED_TYPE +{0xE004,0x0},//,Illumination Trigger.RW_CONTEXT +{0xE000,0x1},//,Illumination Trigger.BANK_SEL +{0x001C,0x0},//,Illumination Trigger.EN_TRIG_ILLUM +{0x001D,0x0},//,Illumination Trigger.EN_TRIG_SYNC +{0x0019,0x0},//,Illumination Trigger.ILLUM_WIDTH +{0x001A,0x7},//,Illumination Trigger.ILLUM_WIDTH +{0x001B,0x53},//,Illumination Trigger.ILLUM_WIDTH +{0x0016,0x8},//,Illumination Trigger.ILLUM_DELAY +{0x0017,0x0},//,Illumination Trigger.ILLUM_DELAY +{0x0018,0x0},//,Illumination Trigger.ILLUM_DELAY +{0xE004,0x1},//,Illumination Trigger.RW_CONTEXT +{0x001C,0x0},//,Illumination Trigger.EN_TRIG_ILLUM +{0x001D,0x0},//,Illumination Trigger.EN_TRIG_SYNC +{0x0019,0x0},//,Illumination Trigger.ILLUM_WIDTH +{0x001A,0x7},//,Illumination Trigger.ILLUM_WIDTH +{0x001B,0x53},//,Illumination Trigger.ILLUM_WIDTH +{0x0016,0x8},//,Illumination Trigger.ILLUM_DELAY +{0x0017,0x0},//,Illumination Trigger.ILLUM_DELAY +{0x0018,0x0},//,Illumination Trigger.ILLUM_DELAY +{0xE000,0x0},//,Illumination Trigger.BANK_SEL +{0x001A,0x0},//,Illumination Trigger.TRIG_SYNC_DELAY +{0x001B,0x0},//,Illumination Trigger.TRIG_SYNC_DELAY +{0x001C,0x0},//,Illumination Trigger.TRIG_SYNC_DELAY +{0x00F3,0x27},//,Illumination Trigger.DMUX0_SEL +{0xE004,0x0},//,Synchronization Trigger.RW_CONTEXT +{0xE000,0x1},//,Synchronization Trigger.BANK_SEL +{0x001D,0x0},//,Synchronization Trigger.EN_TRIG_SYNC +{0xE004,0x1},//,Synchronization Trigger.RW_CONTEXT +{0x001D,0x0},//,Synchronization Trigger.EN_TRIG_SYNC +{0xE000,0x0},//,Synchronization Trigger.BANK_SEL +{0x001A,0x0},//,Synchronization Trigger.TRIG_SYNC_DELAY +{0x001B,0x0},//,Synchronization Trigger.TRIG_SYNC_DELAY +{0x001C,0x0},//,Synchronization Trigger.TRIG_SYNC_DELAY +{0x001D,0x0},//,Synchronization Trigger.TRIG_SYNC_ON_REQ_1 + + +}; + +static const struct mira050_reg full_576_768_50fps_8b_1lane_reg_post_soft_reset[] = { + +}; + +static const struct mira050_reg partial_analog_gain_x1_12bit[] = { + {0xE000,0x0}, //,Analog Gain.BANK_SEL + {0x01BB,0xC8}, //,Analog Gain + {0x01BC,0xC0}, //,Analog Gain + {0x00D0,0x0}, //,Analog Gain + {0x01F0,0x8}, //,Analog Gain + {0x01F3,0x2}, //,Analog Gain + {0x016E,0xCE}, //,Analog Gain.LUT_DEL_000 + {0x0172,0x0}, //,Analog Gain.LUT_DEL_004 + {0x0173,0x0}, //,Analog Gain.LUT_DEL_005 + {0x016F,0xFF}, //,Analog Gain.LUT_DEL_001 + {0x0170,0xFF}, //,Analog Gain.LUT_DEL_002 + {0x0171,0xCE}, //,Analog Gain.LUT_DEL_003 + {0x0174,0x0}, //,Analog Gain.LUT_DEL_006 + {0x0175,0x20}, //,Analog Gain.LUT_DEL_007 + {0x018B,0x3}, //,Analog Gain + {0x018C,0x52}, //,Analog Gain + {0x018D,0x2}, //,Analog Gain + {0x018E,0x56}, //,Analog Gain + {0x018F,0xB}, //,Analog Gain + {0x0190,0xCF}, //,Analog Gain + {0x01EE,0x15}, //,Analog Gain.SHUTTER_LAG + {0x01EF,0x6A}, //,Analog Gain.SHUTTER_LAG + {0x01A2,0x5}, //,Analog Gain.POS_ANACOL_TRIGGER + {0x01A3,0xDD}, //,Analog Gain.POS_ANACOL_TRIGGER + {0x031F,0x5}, //,Analog Gain.POS_YADDR_TRIGGER + {0x0320,0xE6}, //,Analog Gain.POS_YADDR_TRIGGER + {0x01A6,0x6}, //,Analog Gain.POS_ADC_TRIGGER + {0x01A7,0x74}, //,Analog Gain.POS_ADC_TRIGGER + {0x01A4,0xB}, //,Analog Gain.POS_ANACOL_YBIN_TRIGGER + {0x01A5,0x46}, //,Analog Gain.POS_ANACOL_YBIN_TRIGGER + {0x0321,0xB}, //,Analog Gain.POS_YADDR_YBIN_TRIGGER + {0x0322,0x4F}, //,Analog Gain.POS_YADDR_YBIN_TRIGGER + {0x01A8,0xB}, //,Analog Gain.POS_ADC_YBIN_TRIGGER + {0x01A9,0xDD}, //,Analog Gain.POS_ADC_YBIN_TRIGGER + {0x01A0,0x0}, //,Analog Gain.POS_VIS_TRIGGER + {0x01A1,0xB1}, //,Analog Gain.POS_VIS_TRIGGER + {0x01B2,0x0}, //,Analog Gain.POS_HSYNC_RISE + {0x01B3,0xC9}, //,Analog Gain.POS_HSYNC_RISE + {0x01B0,0x0}, //,Analog Gain.POS_STAT_TRIGGER + {0x01B1,0xC4}, //,Analog Gain.POS_STAT_TRIGGER + {0x01AC,0x0}, //,Analog Gain.POS_SVAL_TRIGGER + {0x01AD,0xCF}, //,Analog Gain.POS_SVAL_TRIGGER + +}; + +static const struct mira050_reg partial_analog_gain_x2_12bit[] = { + {0xE000,0x0},//,Analog Gain.BANK_SEL + {0x01BB,0xAF},//,Analog Gain + {0x01BC,0xA7},//,Analog Gain + {0x00D0,0x0},//,Analog Gain + {0x01F0,0x8},//,Analog Gain + {0x01F3,0x1},//,Analog Gain + {0x016E,0xFF},//,Analog Gain.LUT_DEL_000 + {0x0172,0x4D},//,Analog Gain.LUT_DEL_004 + {0x0173,0x0},//,Analog Gain.LUT_DEL_005 + {0x016F,0xFF},//,Analog Gain.LUT_DEL_001 + {0x0170,0xFF},//,Analog Gain.LUT_DEL_002 + {0x0171,0xFF},//,Analog Gain.LUT_DEL_003 + {0x0174,0x4D},//,Analog Gain.LUT_DEL_006 + {0x0175,0x3F},//,Analog Gain.LUT_DEL_007 + {0x018B,0x5},//,Analog Gain + {0x018C,0x4A},//,Analog Gain + {0x018D,0x2},//,Analog Gain + {0x018E,0x56},//,Analog Gain + {0x018F,0xE},//,Analog Gain + {0x0190,0x44},//,Analog Gain + {0x01EE,0x15},//,Analog Gain.SHUTTER_LAG + {0x01EF,0x6},//,Analog Gain.SHUTTER_LAG + {0x01A2,0x6},//,Analog Gain.POS_ANACOL_TRIGGER + {0x01A3,0x41},//,Analog Gain.POS_ANACOL_TRIGGER + {0x031F,0x6},//,Analog Gain.POS_YADDR_TRIGGER + {0x0320,0x4A},//,Analog Gain.POS_YADDR_TRIGGER + {0x01A6,0x6},//,Analog Gain.POS_ADC_TRIGGER + {0x01A7,0xD8},//,Analog Gain.POS_ADC_TRIGGER + {0x01A4,0xC},//,Analog Gain.POS_ANACOL_YBIN_TRIGGER + {0x01A5,0xC5},//,Analog Gain.POS_ANACOL_YBIN_TRIGGER + {0x0321,0xC},//,Analog Gain.POS_YADDR_YBIN_TRIGGER + {0x0322,0xCE},//,Analog Gain.POS_YADDR_YBIN_TRIGGER + {0x01A8,0xD},//,Analog Gain.POS_ADC_YBIN_TRIGGER + {0x01A9,0x5C},//,Analog Gain.POS_ADC_YBIN_TRIGGER + {0x01A0,0x0},//,Analog Gain.POS_VIS_TRIGGER + {0x01A1,0xDB},//,Analog Gain.POS_VIS_TRIGGER + {0x01B2,0x0},//,Analog Gain.POS_HSYNC_RISE + {0x01B3,0xF3},//,Analog Gain.POS_HSYNC_RISE + {0x01B0,0x0},//,Analog Gain.POS_STAT_TRIGGER + {0x01B1,0xEE},//,Analog Gain.POS_STAT_TRIGGER + {0x01AC,0x0},//,Analog Gain.POS_SVAL_TRIGGER + {0x01AD,0xF9},//,Analog Gain.POS_SVAL_TRIGGER + +}; + +static const struct mira050_reg partial_analog_gain_x4_12bit[] = { + // # Analog Gain + {0xE000, 0x00}, + {0x01BB, 0x99}, + {0x01BC, 0x91}, + {0x00D0, 0x00}, + {0x01F0, 0x08}, + {0x01F3, 0x00}, + {0x016E, 0xFF}, + {0x0172, 0xFF}, + {0x0173, 0x2E}, + {0x016F, 0xFF}, + {0x0170, 0xFF}, + {0x0171, 0xFF}, + {0x0174, 0xFF}, + {0x0175, 0xAB}, + {0x018B, 0x08}, + {0x018C, 0xCA}, + {0x018D, 0x02}, + {0x018E, 0x56}, + {0x018F, 0x12}, + {0x0190, 0xBE}, + {0x01EE, 0x14}, + {0x01EF, 0xA2}, + {0x01A2, 0x06}, + {0x01A3, 0xA5}, + {0x031F, 0x06}, + {0x0320, 0xAE}, + {0x01A6, 0x07}, + {0x01A7, 0x3C}, + {0x01A4, 0x0F}, + {0x01A5, 0x27}, + {0x0321, 0x0F}, + {0x0322, 0x30}, + {0x01A8, 0x0F}, + {0x01A9, 0xBE}, + {0x01A0, 0x01}, + {0x01A1, 0x25}, + {0x01B2, 0x01}, + {0x01B3, 0x3D}, + {0x01B0, 0x01}, + {0x01B1, 0x38}, + {0x01AC, 0x01}, + {0x01AD, 0x43}, + // # Black Level + {0xE000, 0x00}, + {0x0193, 0x34}, + {0x0194, 0x5E}, + +}; + +static const struct mira050_fine_gain_lut_new fine_gain_lut_10bit_hs_4x[] = { + {256, 15, 36, 3}, + {261, 15, 35, 3}, + {270, 15, 33, 3}, + {275, 15, 32, 3}, + {286, 15, 30, 3}, + {292, 15, 29, 3}, + {304, 15, 27, 3}, + {310, 15, 26, 3}, + {324, 15, 24, 3}, + {332, 15, 23, 3}, + {339, 15, 22, 3}, + {352, 15, 62, 2}, + {365, 15, 59, 2}, + {374, 15, 57, 2}, + {384, 15, 55, 2}, + {394, 15, 53, 2}, + {405, 15, 51, 2}, + {423, 15, 48, 2}, + {436, 15, 46, 2}, + {442, 15, 45, 2}, + {456, 15, 43, 2}, + {471, 15, 41, 2}, + {486, 15, 39, 2}, + {503, 15, 37, 2}, + {512, 15, 36, 2}, + {531, 15, 34, 2}, + {551, 15, 32, 2}, + {561, 15, 31, 2}, + {584, 15, 29, 2}, + {596, 15, 28, 2}, + {621, 15, 26, 2}, + {634, 15, 25, 2}, + {649, 15, 24, 2}, + {679, 15, 22, 2}, + {695, 15, 63, 1}, + {712, 15, 61, 1}, + {739, 15, 58, 1}, + {758, 15, 56, 1}, + {778, 15, 54, 1}, + {811, 15, 51, 1}, + {834, 15, 49, 1}, + {858, 15, 47, 1}, + {884, 15, 45, 1}, + {912, 15, 43, 1}, + {926, 15, 42, 1}, + {957, 15, 40, 1}, + {989, 15, 38, 1}, + {1024, 15, 36, 1}, + +}; +static const struct mira050_fine_gain_lut_new fine_gain_lut_8bit_16x[] = { + // gain_256,gdig_preamp,rg_bias,rg_mult + {256, 3, 36, 3}, + {261, 3, 35, 3}, + {270, 3, 33, 3}, + {275, 3, 32, 3}, + {286, 3, 30, 3}, + {292, 3, 29, 3}, + {304, 3, 27, 3}, + {310, 3, 26, 3}, + {324, 3, 24, 3}, + {332, 3, 23, 3}, + {339, 3, 22, 3}, + {352, 3, 62, 2}, + {365, 3, 59, 2}, + {374, 3, 57, 2}, + {384, 3, 55, 2}, + {394, 3, 53, 2}, + {405, 3, 51, 2}, + {423, 3, 48, 2}, + {436, 3, 46, 2}, + {442, 3, 45, 2}, + {456, 3, 43, 2}, + {471, 3, 41, 2}, + {486, 3, 39, 2}, + {503, 3, 37, 2}, + {512, 3, 36, 2}, + {531, 3, 34, 2}, + {551, 3, 32, 2}, + {561, 3, 31, 2}, + {584, 3, 29, 2}, + {596, 3, 28, 2}, + {621, 3, 26, 2}, + {634, 3, 25, 2}, + {649, 3, 24, 2}, + {679, 3, 22, 2}, + {695, 3, 63, 1}, + {712, 3, 61, 1}, + {739, 3, 58, 1}, + {758, 3, 56, 1}, + {778, 3, 54, 1}, + {811, 3, 51, 1}, + {834, 3, 49, 1}, + {858, 3, 47, 1}, + {884, 3, 45, 1}, + {912, 3, 43, 1}, + {926, 3, 42, 1}, + {957, 3, 40, 1}, + {989, 3, 38, 1}, + {1024, 7, 36, 2}, + {1042, 7, 35, 2}, + {1081, 7, 33, 2}, + {1101, 7, 32, 2}, + {1144, 7, 30, 2}, + {1167, 7, 29, 2}, + {1216, 7, 27, 2}, + {1242, 7, 26, 2}, + {1297, 7, 24, 2}, + {1327, 7, 23, 2}, + {1357, 7, 22, 2}, + {1406, 7, 62, 1}, + {1459, 7, 59, 1}, + {1497, 7, 57, 1}, + {1536, 7, 55, 1}, + {1599, 7, 52, 1}, + {1644, 7, 50, 1}, + {1692, 7, 48, 1}, + {1742, 7, 46, 1}, + {1796, 7, 44, 1}, + {1853, 7, 42, 1}, + {1883, 7, 41, 1}, + {1946, 7, 39, 1}, + {2013, 7, 37, 1}, + {2085, 15, 35, 2}, + {2122, 15, 34, 2}, + {2203, 15, 32, 2}, + {2245, 15, 31, 2}, + {2335, 15, 29, 2}, + {2382, 15, 28, 2}, + {2484, 15, 26, 2}, + {2538, 15, 25, 2}, + {2594, 15, 24, 2}, + {2715, 15, 22, 2}, + {2779, 15, 63, 1}, + {2882, 15, 60, 1}, + {2955, 15, 58, 1}, + {3032, 15, 56, 1}, + {3155, 15, 53, 1}, + {3243, 15, 51, 1}, + {3335, 15, 49, 1}, + {3433, 15, 47, 1}, + {3537, 15, 45, 1}, + {3648, 15, 43, 1}, + {3766, 15, 41, 1}, + {3827, 15, 40, 1}, + {3957, 15, 38, 1}, + {4096, 15, 36, 1}, + +}; + +static const char *const mira050_test_pattern_menu[] = { + "Disabled", + "Fixed Data", + "2D Gradient", +}; + +static const int mira050_test_pattern_val[] = { + MIRA050_TEST_PATTERN_DISABLE, + MIRA050_TEST_PATTERN_FIXED_DATA, + MIRA050_TEST_PATTERN_2D_GRADIENT, +}; + +/* regulator supplies */ +static const char *const mira050_supply_name[] = { + // TODO(jalv): Check supply names + /* Supplies can be enabled in any order */ + "VANA", /* Analog (2.8V) supply */ + "VDIG", /* Digital Core (1.8V) supply */ + "VDDL", /* IF (1.2V) supply */ +}; + +#define MIRA050_NUM_SUPPLIES ARRAY_SIZE(mira050_supply_name) + +/* + * The supported formats. All flip/mirror combinations have the same byte order because the sensor + * is monochrome + */ +static const u32 codes[] = { + // MEDIA_BUS_FMT_Y8_1X8, + // MEDIA_BUS_FMT_Y10_1X10, + // MEDIA_BUS_FMT_Y12_1X12, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGRBG12_1X12, +}; + +/* Mode configs */ +/* + * Only one mode is exposed to the public (576x768 at 12 bit). + * Three codes (8/10/12 bit) are exposed to public. + * The public user specifies the code. + * That is used to specify which internal supported_mode to use. + */ +#define MIRA050_SUPPORTED_MODE_SIZE_PUBLIC 1 +static const struct mira050_mode supported_modes[] = { + { + /* 12 bit mode */ + .width = 576, + .height = 768, + .crop = { + .left = MIRA050_PIXEL_ARRAY_LEFT, + .top = MIRA050_PIXEL_ARRAY_TOP, + .width = 576, + .height = 768}, + .reg_list_pre_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_576_768_50fps_12b_1lane_reg_pre_soft_reset), + .regs = full_576_768_50fps_12b_1lane_reg_pre_soft_reset, + }, + .reg_list_post_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_576_768_50fps_12b_1lane_reg_post_soft_reset), + .regs = full_576_768_50fps_12b_1lane_reg_post_soft_reset, + }, + .min_vblank = MIRA050_MIN_VBLANK_60, + .max_vblank = MIRA050_MAX_VBLANK, + + .hblank = 0, + .bit_depth = 12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .gain_min = 0, + .gain_max = 2, // this is means 0,1,2 correspond to 1x 2x 4x gain + }, + { + /* 10 bit highspeed / low power mode */ + .width = 576, + .height = 768, + .crop = {.left = MIRA050_PIXEL_ARRAY_LEFT, .top = MIRA050_PIXEL_ARRAY_TOP, .width = 576, .height = 768}, + .reg_list_pre_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_576_768_50fps_10b_hs_1lane_reg_pre_soft_reset), + .regs = full_576_768_50fps_10b_hs_1lane_reg_pre_soft_reset, + }, + .reg_list_post_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_576_768_50fps_10b_hs_1lane_reg_post_soft_reset), + .regs = full_576_768_50fps_10b_hs_1lane_reg_post_soft_reset, + }, + .min_vblank = MIRA050_MIN_VBLANK_120, + .max_vblank = MIRA050_MAX_VBLANK, + .hblank = 0, + .bit_depth = 10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .gain_min = 0, + .gain_max = ARRAY_SIZE(fine_gain_lut_10bit_hs_4x) - 1, + }, + { + /* 8 bit mode */ + .width = 576, + .height = 768, + .crop = {.left = MIRA050_PIXEL_ARRAY_LEFT, .top = MIRA050_PIXEL_ARRAY_TOP, .width = 576, .height = 768}, + .reg_list_pre_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_576_768_50fps_8b_1lane_reg_pre_soft_reset), + .regs = full_576_768_50fps_8b_1lane_reg_pre_soft_reset, + }, + .reg_list_post_soft_reset = { + .num_of_regs = ARRAY_SIZE(full_576_768_50fps_8b_1lane_reg_post_soft_reset), + .regs = full_576_768_50fps_8b_1lane_reg_post_soft_reset, + }, + .min_vblank = MIRA050_MIN_VBLANK_120, + .max_vblank = MIRA050_MAX_VBLANK, + .hblank = 0, + .bit_depth = 8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .gain_min = 0, + .gain_max = ARRAY_SIZE(fine_gain_lut_8bit_16x) - 1, + }, + +}; + +struct mira050 +{ + struct v4l2_subdev sd; + struct media_pad pad[NUM_PADS]; + + struct v4l2_mbus_framefmt fmt; + + struct clk *xclk; /* system clock to MIRA050 */ + u32 xclk_freq; + + // struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[MIRA050_NUM_SUPPLIES]; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + // custom v4l2 control + struct v4l2_ctrl *mira050_reg_w; + struct v4l2_ctrl *mira050_reg_r; + u16 mira050_reg_w_cached_addr; + u8 mira050_reg_w_cached_flag; + + /* Current mode */ + const struct mira050_mode *mode; + /* current bit depth, may defer from mode->bit_depth */ + u8 bit_depth; + /* OTP_CALIBRATION_VALUE stored in OTP memory */ + + u16 otp_dark_cal_8bit; + u16 otp_dark_cal_10bit_hs; + u16 otp_dark_cal_10bit; + u16 otp_dark_cal_12bit; + + /* Whether to skip base register sequence upload */ + u32 skip_reg_upload; + /* Whether to reset sensor when stream on/off */ + u32 skip_reset; + /* Whether regulator and clk are powered on */ + u32 powered; + /* Illumination trigger enable */ + u8 illum_enable; + /* Illumination trigger width. Use [23:0] for 24-bit register. */ + u32 illum_width; + /* Illumination trigger delay. Use [19:0] for 20-bit register */ + u32 illum_delay; + /* Illumination trigger width automatically set to exposure time */ + u8 illum_width_auto; + /* A flag to force write_start/stop_streaming_regs even if (skip_reg_upload==1) */ + u8 force_stream_ctrl; + u32 target_frame_time_us; + u32 row_length; + /* + * Mutex for serialized access: + * Protect sensor module set pad format and start/stop streaming safely. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; + + /* pmic, uC, LED */ + struct i2c_client *pmic_client; + struct i2c_client *uc_client; + struct i2c_client *led_client; + /* User specified I2C device address */ + u32 tbd_client_i2c_addr; +}; + +static inline struct mira050 *to_mira050(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct mira050, sd); +} + +static int mira050_read(struct mira050 *mira050, u16 reg, u8 *val) +{ + int ret; + unsigned char data_w[2] = {reg >> 8, reg & 0xff}; + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + + 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; +} + +static int mira050_write(struct mira050 *mira050, u16 reg, u8 val) +{ + int ret; + unsigned char data[3] = {reg >> 8, reg & 0xff, val}; + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + + 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; + } + + /* + * The code below is for debug purpose. + * It reads back the written values. + * Some registers have different read and write addresses. + * These registers typically have WR addr 0xE... but RD addr 0x4... + */ + /* + { + usleep_range(50, 300); + u8 ret_val; + u16 ret_reg; + if (((reg >>12) & 0x000F) == 0x000E) { + ret_reg = ((reg & 0x0FFF) | 0x4000); + } else { + ret_reg = reg; + } + ret = mira050_read(mira050, ret_reg, &ret_val); + printk(KERN_INFO "[MIRA050]: Write reg 0x%4.4x, Read ret_reg 0x%4.4x, val = 0x%x.\n", + reg, ret_reg, ret_val); + if (val != ret_val) { + printk(KERN_INFO "[MIRA050]: WARNING Write reg 0x%4.4x, val = 0x%x, read ret_reg = 0x%4.4x, ret_val = 0x%x.\n", + reg, val, ret_reg, ret_val); + } + } + */ + + return ret; +} + +/* + * mira050 is big-endian: msb of val goes to lower reg addr + */ +static int mira050_write_be16(struct mira050 *mira050, u16 reg, u16 val) +{ + int ret; + unsigned char data[4] = {reg >> 8, reg & 0xff, (val >> 8) & 0xff, val & 0xff}; + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + + ret = i2c_master_send(client, data, 4); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 4) + { + ret = 0; + } + else + { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + } + + return ret; +} + +/* + * mira050 is big-endian: msb of val goes to lower reg addr + */ +static int mira050_write_be24(struct mira050 *mira050, u16 reg, u32 val) +{ + int ret; + unsigned char data[5] = {reg >> 8, reg & 0xff, (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff}; + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + + ret = i2c_master_send(client, data, 5); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 5) + { + ret = 0; + } + else + { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + } + + return ret; +} + +/* + * mira050 is big-endian: msb of val goes to lower reg addr + */ +static int mira050_write_be32(struct mira050 *mira050, u16 reg, u32 val) +{ + int ret; + unsigned char data[6] = {reg >> 8, reg & 0xff, (val >> 24) & 0xff, (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff}; + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + + ret = i2c_master_send(client, data, 6); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 6) + { + ret = 0; + } + else + { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + } + + return ret; +} + +/* + * mira050 OTP 32-bit val on I2C is big-endian. However, val content can be little-endian. + */ +static int mira050_read_be32(struct mira050 *mira050, u16 reg, u32 *val) +{ + int ret; + unsigned char data_w[2] = {reg >> 8, reg & 0xff}; + /* Big-endian 32-bit buffer. */ + unsigned char data_r[4]; + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + + 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, data_r, 4); + *val = (u32)((data_r[0] << 24) | (data_r[1] << 16) | (data_r[2] << 8) | data_r[3]); + /* + * The only return value indicating success is 4. Anything else, even + * a non-negative value, indicates something went wrong. + */ + if (ret == 4) + { + 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 mira050_write_regs(struct mira050 *mira050, + const struct mira050_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + unsigned int i; + int ret; + + for (i = 0; i < len; i++) + { + ret = mira050_write(mira050, 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; + } + else + { + // Debug code below + // u8 val; + // ret = mira050_read(mira050, regs[i].address, &val); + // printk(KERN_INFO "[MIRA050]: Read reg 0x%4.4x, val = 0x%x.\n", + // regs[i].address, val); + } + } + + return 0; +} + +/* + * Read OTP memory: 8-bit addr and 32-bit value + */ +static int mira050_otp_read(struct mira050 *mira050, u8 addr, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + u8 busy_status = 1; + int poll_cnt = 0; + int poll_cnt_max = 10; + int ret; + mira050_write(mira050, MIRA050_BANK_SEL_REG, 0); + mira050_write(mira050, MIRA050_OTP_COMMAND, 0); + mira050_write(mira050, MIRA050_OTP_ADDR, addr); + mira050_write(mira050, MIRA050_OTP_START, 1); + usleep_range(15, 50); + mira050_write(mira050, MIRA050_OTP_START, 0); + for (poll_cnt = 0; poll_cnt < poll_cnt_max; poll_cnt++) + { + mira050_read(mira050, MIRA050_OTP_BUSY, &busy_status); + if (busy_status == 0) + { + break; + } + else + { + usleep_range(5, 10); + } + } + if (poll_cnt < poll_cnt_max && busy_status == 0) + { + usleep_range(15, 50); + ret = mira050_read_be32(mira050, MIRA050_OTP_DOUT, val); + printk(KERN_INFO "[MIRA050]: Read OTP 0x%x, val = 0x%x.\n", + addr,*val); + } + else + { + dev_dbg(&client->dev, "%s: OTP memory busy, skip raeding addr: 0x%X\n", + __func__, addr); + ret = -EINVAL; + } + + return ret; +} + +/* Write PMIC registers, and can be reused to write microcontroller reg. */ +static int mira050pmic_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret; + unsigned char data[2] = {reg & 0xff, val}; + + ret = i2c_master_send(client, data, 2); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 2) + { + 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 mira050pmic_read(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msgs[2]; + u8 addr_buf[1] = {reg & 0xff}; + u8 data_buf[1] = {0}; + int ret; + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = &data_buf[0]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = (u8)(data_buf[0]); + + return 0; +} + +/* Power/clock management functions */ +static int mira050_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira050 *mira050 = to_mira050(sd); + int ret = -EINVAL; + + printk(KERN_INFO "[MIRA050]: Entering power on function.\n"); + + if (mira050->powered == 0) + { + ret = regulator_bulk_enable(MIRA050_NUM_SUPPLIES, mira050->supplies); + if (ret) + { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + + ret = clk_prepare_enable(mira050->xclk); + if (ret) + { + dev_err(&client->dev, "%s: failed to enable clock\n", + __func__); + goto reg_off; + } + + usleep_range(MIRA050_XCLR_MIN_DELAY_US, + MIRA050_XCLR_MIN_DELAY_US + MIRA050_XCLR_DELAY_RANGE_US); + mira050->powered = 1; + } + else + { + printk(KERN_INFO "[MIRA050]: Skip regulator and clk enable, because mira015->powered == %d.\n", mira050->powered); + } + return 0; + +reg_off: + ret = regulator_bulk_disable(MIRA050_NUM_SUPPLIES, mira050->supplies); + return ret; +} + +static int mira050_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira050 *mira050 = to_mira050(sd); + + printk(KERN_INFO "[MIRA050]: Entering power off function.\n"); + + if (mira050->skip_reset == 0) + { + if (mira050->powered == 1) + { + regulator_bulk_disable(MIRA050_NUM_SUPPLIES, mira050->supplies); + clk_disable_unprepare(mira050->xclk); + mira050->powered = 0; + } + else + { + printk(KERN_INFO "[MIRA050]: Skip disabling regulator and clk due to mira015->powered == %d.\n", mira050->powered); + } + } + else + { + printk(KERN_INFO "[MIRA050]: Skip disabling regulator and clk due to mira050->skip_reset=%u.\n", mira050->skip_reset); + } + + return 0; +} + +static int mira050_write_illum_trig_regs(struct mira050 *mira050) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira050->sd); + int ret = 0; + int en_trig_illum = 0; + int en_trig_sync = 1; + int dmux0_sel = 40; + // Set context bank 1A or bank 1B + ret = mira050_write(mira050, MIRA050_RW_CONTEXT_REG, 0); + if (ret) + { + dev_err(&client->dev, "Error setting RW_CONTEXT."); + return ret; + } + // Set reg bank 0 or 1 + ret = mira050_write(mira050, MIRA050_BANK_SEL_REG, 1); + if (ret) + { + dev_err(&client->dev, "Error setting BANK_SEL_REG."); + return ret; + } + + /* + if mira050->illum_enable + if illum_width_auto + use sync trig + else: + use illum trig + else: + all off + */ + + if (mira050->illum_enable) + { + if (mira050->illum_width_auto) + { + dmux0_sel = 40; + en_trig_illum = 0; + en_trig_sync = 1; + } + else + { + dmux0_sel = 39; + en_trig_illum = 1; + en_trig_sync = 0; + } + } + else + { + en_trig_illum = 0; + en_trig_sync = 0; + } + + // Enable or disable illumination trigger + printk(KERN_INFO "[MIRA050]: Writing EN_TRIG_ILLUM to %d.\n", en_trig_illum); + ret = mira050_write(mira050, MIRA050_EN_TRIG_ILLUM, en_trig_illum); + if (ret) + { + dev_err(&client->dev, "Error setting EN_TRIG_ILLUM to %d.", en_trig_illum); + return ret; + } + // Enable or disable illumination trigger + printk(KERN_INFO "[MIRA050]: Writing MIRA050_TRIG_SYNC_ON_REQ_1 to %d.\n", en_trig_sync); + ret = mira050_write(mira050, MIRA050_TRIG_SYNC_ON_REQ_1, en_trig_sync); + if (ret) + { + dev_err(&client->dev, "Error setting MIRA050_TRIG_SYNC_ON_REQ_1 to %d.", en_trig_sync); + return ret; + } + + // Enable or disable sync trigger + printk(KERN_INFO "[MIRA050]: Writing EN_TRIG_SYNC to %d.\n", en_trig_sync); + ret = mira050_write(mira050, MIRA050_EN_TRIG_SYNC, en_trig_sync); + if (ret) + { + dev_err(&client->dev, "Error setting MIRA050_EN_TRIG_SYNC to %d.", en_trig_sync); + return ret; + } + // Set illumination width. Write 24 bits. All 24 bits are valid. + printk(KERN_INFO "[MIRA050]: Writing ILLUM_WIDTH to %u.\n", mira050->illum_width); + ret = mira050_write_be24(mira050, MIRA050_ILLUM_WIDTH_REG, mira050->illum_width); + if (ret) + { + dev_err(&client->dev, "Error setting ILLUM_WIDTH to %u.", mira050->illum_width); + return ret; + } + + // Set illumination delay. Write 24 bits. Only 20 bits, [19:0], are valid. + printk(KERN_INFO "[MIRA050]: Writing ILLUM_DELAY to %u.\n", mira050->illum_delay); + ret = mira050_write_be24(mira050, MIRA050_ILLUM_DELAY_REG, mira050->illum_delay); + if (ret) + { + dev_err(&client->dev, "Error setting ILLUM_DELAY to %u.", mira050->illum_delay); + return ret; + } + + return ret; + + // Set reg bank 0 or 1 + ret = mira050_write(mira050, MIRA050_BANK_SEL_REG, 0); + if (ret) + { + dev_err(&client->dev, "Error setting BANK_SEL_REG."); + return ret; + } + + // DMUX SEL (set bank) + printk(KERN_INFO "[MIRA050]: Writing DMUX0SEL to %d.\n", dmux0_sel); + ret = mira050_write(mira050, MIRA050_DMUX0_SEL, dmux0_sel); + if (ret) + { + dev_err(&client->dev, "Error setting MIRA050_DMUX0_SEL to %d.", dmux0_sel); + return ret; + } +} + +static int mira050_v4l2_reg_w(struct mira050 *mira050, u32 value) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira050->sd); + u32 ret = 0; + u32 tmp_flag; + + u16 reg_addr = (value >> 8) & 0xFFFF; + u8 reg_val = value & 0xFF; + u8 reg_flag = (value >> 24) & 0xFF; + + // printk(KERN_INFO "[MIRA050]: %s reg_flag: 0x%02X; reg_addr: 0x%04X; reg_val: 0x%02X.\n", + // __func__, reg_flag, reg_addr, reg_val); + + if (reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_CMD_SEL) + { + if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_SLEEP_US) + { + // If it is for sleep, combine all 24 bits of reg_addr and reg_val as sleep us. + u32 sleep_us_val = value & 0x00FFFFFF; + // Sleep range needs an interval, default to 1/8 of the sleep value. + u32 sleep_us_interval = sleep_us_val >> 3; + printk(KERN_INFO "[MIRA050]: %s sleep_us: %u.\n", __func__, sleep_us_val); + usleep_range(sleep_us_val, sleep_us_val + sleep_us_interval); + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_RESET_ON) + { + printk(KERN_INFO "[MIRA050]: %s Enable reset at stream on/off.\n", __func__); + mira050->skip_reset = 0; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_RESET_OFF) + { + printk(KERN_INFO "[MIRA050]: %s Disable reset at stream on/off.\n", __func__); + mira050->skip_reset = 1; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_REG_UP_ON) + { + printk(KERN_INFO "[MIRA050]: %s Enable base register sequence upload.\n", __func__); + mira050->skip_reg_upload = 0; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_REG_UP_OFF) + { + printk(KERN_INFO "[MIRA050]: %s Disable base register sequence upload.\n", __func__); + mira050->skip_reg_upload = 1; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_POWER_ON) + { + printk(KERN_INFO "[MIRA050]: %s Call power on function mira050_power_on().\n", __func__); + /* Temporarily disable skip_reset if manually doing power on/off */ + tmp_flag = mira050->skip_reset; + mira050->skip_reset = 0; + mira050_power_on(&client->dev); + mira050->skip_reset = tmp_flag; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_POWER_OFF) + { + printk(KERN_INFO "[MIRA050]: %s Call power off function mira050_power_off().\n", __func__); + /* Temporarily disable skip_reset if manually doing power on/off */ + tmp_flag = mira050->skip_reset; + mira050->skip_reset = 0; + mira050_power_off(&client->dev); + mira050->skip_reset = tmp_flag; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_TRIG_ON) + { + printk(KERN_INFO "[MIRA050]: %s Enable illumination trigger.\n", __func__); + mira050->illum_enable = 1; + mira050_write_illum_trig_regs(mira050); + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_TRIG_OFF) + { + printk(KERN_INFO "[MIRA050]: %s Disable illumination trigger.\n", __func__); + mira050->illum_enable = 0; + mira050_write_illum_trig_regs(mira050); + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_WIDTH) + { + // Combine all 24 bits of reg_addr and reg_val as ILLUM_WIDTH. + u32 illum_width = value & 0x00FFFFFF; + printk(KERN_INFO "[MIRA050]: %s Set ILLUM_WIDTH to 0x%X.\n", __func__, illum_width); + mira050->illum_width = illum_width; + mira050_write_illum_trig_regs(mira050); + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_DELAY) + { + // Combine reg_addr and reg_val, then select 20 bits from [19:0] as ILLUM_DELAY. + u32 illum_delay = value & 0x000FFFFF; + printk(KERN_INFO "[MIRA050]: %s Set ILLUM_DELAY to 0x%X.\n", __func__, illum_delay); + mira050->illum_delay = illum_delay; + mira050_write_illum_trig_regs(mira050); + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_EXP_T_ON) + { + printk(KERN_INFO "[MIRA050]: %s enable ILLUM_WIDTH to automatically track exposure time.\n", __func__); + mira050->illum_width_auto = 1; + mira050_write_illum_trig_regs(mira050); + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_ILLUM_EXP_T_OFF) + { + printk(KERN_INFO "[MIRA050]: %s disable ILLUM_WIDTH to automatically track exposure time.\n", __func__); + mira050->illum_width_auto = 0; + mira050_write_illum_trig_regs(mira050); + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_STREAM_CTRL_ON) + { + printk(KERN_INFO "[MIRA050]: %s Force stream control even if (skip_reg_upload == 1).\n", __func__); + mira050->force_stream_ctrl = 1; + } + else if (reg_flag == AMS_CAMERA_CID_MIRA050_REG_FLAG_STREAM_CTRL_OFF) + { + printk(KERN_INFO "[MIRA050]: %s Disable stream control if (skip_reg_upload == 1).\n", __func__); + mira050->force_stream_ctrl = 0; + } + else + { + printk(KERN_INFO "[MIRA050]: %s unknown command from flag %u, ignored.\n", __func__, reg_flag); + } + } + else if (reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_FOR_READ) + { + // If it is for read, skip reagister write, cache addr and flag for read. + mira050->mira050_reg_w_cached_addr = reg_addr; + mira050->mira050_reg_w_cached_flag = reg_flag; + } + else + { + // If it is for write, select which I2C device by the flag "I2C_SEL". + if ((reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_MIRA) + { + // Before writing Mira050 register, first optionally select BANK and CONTEXT + if (reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_USE_BANK) + { + u8 bank; + u8 context; + // Set conetxt bank 0 or 1 + if (reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_BANK) + { + bank = 1; + } + else + { + bank = 0; + } + // printk(KERN_INFO "[MIRA050]: %s select bank: %u.\n", __func__, bank); + ret = mira050_write(mira050, MIRA050_BANK_SEL_REG, bank); + if (ret) + { + dev_err(&client->dev, "Error setting BANK_SEL_REG."); + return ret; + } + // Set context bank 1A or bank 1B + if (reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_CONTEXT) + { + context = 1; + } + else + { + context = 0; + } + // printk(KERN_INFO "[MIRA050]: %s select context: %u.\n", __func__, context); + ret = mira050_write(mira050, MIRA050_RW_CONTEXT_REG, context); + if (ret) + { + dev_err(&client->dev, "Error setting RW_CONTEXT."); + return ret; + } + } + // Writing the actual Mira050 register + // printk(KERN_INFO "[MIRA050]: %s write reg_addr: 0x%04X; reg_val: 0x%02X.\n", __func__, reg_addr, reg_val); + ret = mira050_write(mira050, reg_addr, reg_val); + if (ret) + { + dev_err_ratelimited(&client->dev, "Error AMS_CAMERA_CID_MIRA_REG_W reg_addr %X.\n", reg_addr); + return -EINVAL; + } + } + else if ((reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_SET_TBD) + { + /* User tries to set TBD I2C address, store reg_val to mira050->tbd_client_i2c_addr. Skip write. */ + printk(KERN_INFO "[MIRA050]: mira050->tbd_client_i2c_addr = 0x%X.\n", reg_val); + mira050->tbd_client_i2c_addr = reg_val; + } + else if ((reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_TBD) + { + if (mira050->tbd_client_i2c_addr == MIRA050PMIC_I2C_ADDR) + { + // Write PMIC. Use pre-allocated mira050->pmic_client. + printk(KERN_INFO "[MIRA050]: write pmic_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira050pmic_write(mira050->pmic_client, (u8)(reg_addr & 0xFF), reg_val); + } + else if (mira050->tbd_client_i2c_addr == MIRA050UC_I2C_ADDR) + { + // Write micro-controller. Use pre-allocated mira050->uc_client. + printk(KERN_INFO "[MIRA050]: write uc_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira050pmic_write(mira050->uc_client, (u8)(reg_addr & 0xFF), reg_val); + } + else if (mira050->tbd_client_i2c_addr == MIRA050LED_I2C_ADDR) + { + // Write LED driver. Use pre-allocated mira050->led_client. + printk(KERN_INFO "[MIRA050]: write led_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira050pmic_write(mira050->led_client, (u8)(reg_addr & 0xFF), reg_val); + } + else + { + /* Write other TBD I2C address. + * The TBD I2C address is set via AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_SET_TBD. + * The TBD I2C address is stored in mira050->tbd_client_i2c_addr. + * A temporary I2C client, tmp_client, is created and then destroyed (unregistered). + */ + struct i2c_client *tmp_client; + tmp_client = i2c_new_dummy_device(client->adapter, mira050->tbd_client_i2c_addr); + if (IS_ERR(tmp_client)) + return PTR_ERR(tmp_client); + printk(KERN_INFO "[MIRA050]: write tbd_client, i2c_addr %u, reg_addr 0x%X, reg_val 0x%X.\n", + mira050->tbd_client_i2c_addr, (u8)(reg_addr & 0xFF), reg_val); + ret = mira050pmic_write(tmp_client, (u8)(reg_addr & 0xFF), reg_val); + i2c_unregister_device(tmp_client); + } + } + } + + return 0; +} + +static int mira050_v4l2_reg_r(struct mira050 *mira050, u32 *value) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira050->sd); + u32 ret = 0; + + u16 reg_addr = mira050->mira050_reg_w_cached_addr; + u8 reg_flag = mira050->mira050_reg_w_cached_flag; + u8 reg_val = 0; + + *value = 0; + + if ((reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_MIRA) + { + if (reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_USE_BANK) + { + u8 bank; + u8 context; + // Set conetxt bank 0 or 1 + if (reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_BANK) + { + bank = 1; + } + else + { + bank = 0; + } + // printk(KERN_INFO "[MIRA050]: %s select bank: %u.\n", __func__, bank); + ret = mira050_write(mira050, MIRA050_BANK_SEL_REG, bank); + if (ret) + { + dev_err(&client->dev, "Error setting BANK_SEL_REG."); + return ret; + } + // Set context bank 1A or bank 1B + if (reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_CONTEXT) + { + context = 1; + } + else + { + context = 0; + } + // printk(KERN_INFO "[MIRA050]: %s select context: %u.\n", __func__, context); + ret = mira050_write(mira050, MIRA050_RW_CONTEXT_REG, context); + if (ret) + { + dev_err(&client->dev, "Error setting RW_CONTEXT."); + return ret; + } + } + ret = mira050_read(mira050, reg_addr, ®_val); + if (ret) + { + dev_err_ratelimited(&client->dev, "Error AMS_CAMERA_CID_MIRA_REG_R reg_addr %X.\n", reg_addr); + return -EINVAL; + } + } + else if ((reg_flag & AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_TBD) + { + if (mira050->tbd_client_i2c_addr == MIRA050PMIC_I2C_ADDR) + { + // Read PMIC. Use pre-allocated mira050->pmic_client. + ret = mira050pmic_read(mira050->pmic_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA050]: read pmic_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } + else if (mira050->tbd_client_i2c_addr == MIRA050UC_I2C_ADDR) + { + // Read micro-controller. Use pre-allocated mira050->uc_client. + ret = mira050pmic_read(mira050->uc_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA050]: read uc_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } + else if (mira050->tbd_client_i2c_addr == MIRA050LED_I2C_ADDR) + { + // Read LED driver. Use pre-allocated mira050->led_client. + ret = mira050pmic_read(mira050->led_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA050]: read led_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } + else + { + /* Read other TBD I2C address. + * The TBD I2C address is set via AMS_CAMERA_CID_MIRA050_REG_FLAG_I2C_SET_TBD. + * The TBD I2C address is stored in mira050->tbd_client_i2c_addr. + * A temporary I2C client, tmp_client, is created and then destroyed (unregistered). + */ + struct i2c_client *tmp_client; + tmp_client = i2c_new_dummy_device(client->adapter, mira050->tbd_client_i2c_addr); + if (IS_ERR(tmp_client)) + return PTR_ERR(tmp_client); + ret = mira050pmic_read(tmp_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA050]: read tbd_client, i2c_addr %u, reg_addr 0x%X, reg_val 0x%X.\n", + mira050->tbd_client_i2c_addr, (u8)(reg_addr & 0xFF), reg_val); + i2c_unregister_device(tmp_client); + } + } + + // Return 32-bit value that includes flags, addr, and register value + *value = ((u32)reg_flag << 24) | ((u32)reg_addr << 8) | (u32)reg_val; + + // printk(KERN_INFO "[MIRA050]: mira050_v4l2_reg_r() reg_flag: 0x%02X; reg_addr: 0x%04X, reg_val: 0x%02X.\n", + // reg_flag, reg_addr, reg_val); + + return 0; +} + +// Returns the maximum exposure time in microseconds (reg value) +static u32 mira050_calculate_max_exposure_time(u32 row_length, u32 vsize, + u32 vblank) +{ + (void)(row_length); + (void)(vsize); + (void)(vblank); + /* Mira050 does not have a max exposure limit besides register bits */ + // return row_length * (vsize + vblank) - MIRA050_GLOB_NUM_CLK_CYCLES; + return MIRA050_EXPOSURE_MAX_LINES; +} + +static int mira050_write_exposure_reg(struct mira050 *mira050, u32 exposure_lines) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira050->sd); + const u32 min_exposure = MIRA050_EXPOSURE_MIN_US; + u32 max_exposure = mira050->exposure->maximum * MIRA050_MIN_ROW_LENGTH_US; + u32 ret = 0; + u32 exposure = exposure_lines * MIRA050_DEFAULT_LINE_LENGTH; + + + if (exposure < min_exposure) + { + exposure = min_exposure; + } + if (exposure > max_exposure) + { + exposure = max_exposure; + } + + // printk(KERN_INFO "[MIRA050]: mira050_write_exposure_reg: exp us = %u.\n", exposure); + + /* Write Bank 1 context 0 */ + ret = mira050_write(mira050, MIRA050_RW_CONTEXT_REG, 0); + ret = mira050_write(mira050, MIRA050_BANK_SEL_REG, 1); + ret = mira050_write_be32(mira050, MIRA050_EXP_TIME_L_REG, exposure); + /* Write Bank 1 context 1 */ + ret = mira050_write(mira050, MIRA050_RW_CONTEXT_REG, 1); + ret = mira050_write_be32(mira050, MIRA050_EXP_TIME_L_REG, exposure); + if (ret) + { + dev_err_ratelimited(&client->dev, "Error setting exposure time to %d", exposure); + return -EINVAL; + } + + // if (mira050->illum_width_auto == 1) + // { + // mira050->illum_width = (exposure / 8) * MIRA050_DATA_RATE; + // mira050_write_illum_trig_regs(mira050); + // } + + return 0; +} + +static int mira050_write_target_frame_time_reg(struct mira050 *mira050, u32 target_frame_time_us) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira050->sd); + u32 ret = 0; + + /* Write Bank 1 context 0 */ + ret = mira050_write(mira050, MIRA050_RW_CONTEXT_REG, 0); + ret = mira050_write(mira050, MIRA050_BANK_SEL_REG, 1); + ret = mira050_write_be32(mira050, MIRA050_TARGET_FRAME_TIME_REG, target_frame_time_us); + /* Write Bank 1 context 1 */ + ret = mira050_write(mira050, MIRA050_RW_CONTEXT_REG, 1); + ret = mira050_write_be32(mira050, MIRA050_TARGET_FRAME_TIME_REG, target_frame_time_us); + if (ret) + { + dev_err_ratelimited(&client->dev, "Error setting target frame time to %d", target_frame_time_us); + return -EINVAL; + } + + return 0; +} + +static int mira050_write_start_streaming_regs(struct mira050 *mira050) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira050->sd); + int ret = 0; + + // Set conetxt bank 0 or 1 + ret = mira050_write(mira050, MIRA050_BANK_SEL_REG, 0); + if (ret) + { + dev_err(&client->dev, "Error setting BANK_SEL_REG."); + return ret; + } + + // Set context bank 1A or bank 1B + ret = mira050_write(mira050, MIRA050_RW_CONTEXT_REG, 0); + if (ret) + { + dev_err(&client->dev, "Error setting RW_CONTEXT."); + return ret; + } + + // Raising CMD_REQ_1 to 1 for REQ_EXP + ret = mira050_write(mira050, MIRA050_CMD_REQ_1_REG, + 1); + if (ret) + { + dev_err(&client->dev, "Error setting CMD_REQ_1 to 1 for REQ_EXP."); + return ret; + } + + usleep_range(10, 20); + + // Setting CMD_REQ_1 tp 0 for REQ_EXP + ret = mira050_write(mira050, MIRA050_CMD_REQ_1_REG, + 0); + if (ret) + { + dev_err(&client->dev, "Error setting CMD_REQ_1 to 0 for REQ_EXP."); + return ret; + } + usleep_range(10, 20); + + return ret; +} + +static int mira050_write_stop_streaming_regs(struct mira050 *mira050) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira050->sd); + int ret = 0; + + // Set conetxt bank 0 or 1 + ret = mira050_write(mira050, MIRA050_BANK_SEL_REG, 0); + if (ret) + { + dev_err(&client->dev, "Error setting BANK_SEL_REG."); + return ret; + } + + // Raising CMD_HALT_BLOCK to 1 to stop streaming + ret = mira050_write(mira050, MIRA050_CMD_HALT_BLOCK_REG, + 1); + if (ret) + { + dev_err(&client->dev, "Error setting CMD_HALT_BLOCK to 1."); + return ret; + } + + usleep_range(10, 20); + + // Setting CMD_HALT_BLOCK to 0 to stop streaming + ret = mira050_write(mira050, MIRA050_CMD_HALT_BLOCK_REG, + 0); + if (ret) + { + dev_err(&client->dev, "Error setting CMD_HALT_BLOCK to 0."); + return ret; + } + usleep_range(10, 20); + + /* + * Wait for one frame to make sure sensor is set to + * software standby in V-blank + * + * frame_time = frame length rows * Tline + * Tline = line length / pixel clock (in MHz) + */ + /* + u32 frame_time; + frame_time = MIRA050_DEFAULT_FRAME_LENGTH * + MIRA050_DEFAULT_LINE_LENGTH / MIRA050_DEFAULT_PIXEL_CLOCK; + + usleep_range(frame_time, frame_time + 1000); + */ + + return ret; +} + +static int mira050_write_analog_gain_reg(struct mira050 *mira050, u8 gain) +{ + struct i2c_client *const client = v4l2_get_subdevdata(&mira050->sd); + u32 num_of_regs; + u32 ret = 0; + u32 wait_us = 20000; + u16 target_black_level = 128; + u16 cds_offset = 1700; + u16 dark_offset_100 = 1794; // noncont clock + u16 scale_factor = 1; + u16 preamp_gain_inv = 1; + u16 preamp_gain = 1; + + u16 analog_gain = 1; + u16 offset_clipping = 0; + u16 scaled_offset = 0; + printk(KERN_INFO "[MIRA050]: Write analog gain %u",gain); + + // Select partial register sequence according to bit depth + if (mira050->bit_depth == 12) + { + mira050_write_stop_streaming_regs(mira050); + usleep_range(wait_us, wait_us + 100); + scale_factor = 1; + + // Select register sequence according to gain value + if (gain == 0) + { + analog_gain = 1; + // printk(KERN_INFO "[MIRA050]: Write reg sequence for analog gain x1 in 12 bit mode"); + num_of_regs = ARRAY_SIZE(partial_analog_gain_x1_12bit); + ret = mira050_write_regs(mira050, partial_analog_gain_x1_12bit, num_of_regs); + cds_offset = 1700; + } + else if (gain == 1) + { + analog_gain = 2; + // printk(KERN_INFO "[MIRA050]: Write reg sequence for analog gain x2 in 12 bit mode"); + num_of_regs = ARRAY_SIZE(partial_analog_gain_x2_12bit); + ret = mira050_write_regs(mira050, partial_analog_gain_x2_12bit, num_of_regs); + cds_offset = 2708; + } + else if (gain == 2) + { + analog_gain = 4; + + // printk(KERN_INFO "[MIRA050]: Write reg sequence for analog gain x4 in 12 bit mode"); + num_of_regs = ARRAY_SIZE(partial_analog_gain_x4_12bit); + ret = mira050_write_regs(mira050, partial_analog_gain_x4_12bit, num_of_regs); + cds_offset = 4500; + } + else + { + // Other gains are not supported + // printk(KERN_INFO "[MIRA050]: Ignore analog gain %u in 12 bit mode", gain); + } + u16 part1 = (mira050->otp_dark_cal_12bit + dark_offset_100) / 100; + u16 part3 = (dark_offset_100 / 100); + u16 part2 = analog_gain / (scale_factor * preamp_gain); + scaled_offset = (u16)((mira050->otp_dark_cal_12bit + dark_offset_100) / 100 * analog_gain / (scale_factor * preamp_gain)) - (u16)(dark_offset_100 / 100); + // printk(KERN_INFO "[MIRA050]: scaled offset 12 bit mode is %u dark cal is %u", scaled_offset, mira050->otp_dark_cal_12bit); + // printk(KERN_INFO "[MIRA050]: scaled offset 12 bit mode part1 %u part2 %u part3 %u ", part1, part2, part3); + + + + scaled_offset = (u16)(((mira050->otp_dark_cal_12bit + dark_offset_100) * analog_gain * preamp_gain_inv / (scale_factor)) - dark_offset_100) / 100; + // newly added. + // printk(KERN_INFO "[MIRA050]: scaled offset new 12 bit mode is %u dark cal is %u", scaled_offset, mira050->otp_dark_cal_12bit); + + /* Avoid negative offset_clipping value. */ + offset_clipping = ((int)(cds_offset - target_black_level * preamp_gain_inv + scaled_offset) < 0 ? 0 : (int)(cds_offset - target_black_level * preamp_gain_inv + scaled_offset)); + /* Stop streaming and wait for frame data transmission done */ + // mira050_write_stop_streaming_regs(mira050); + + + /* Avoid negative offset_clipping value. */ + // offset_clipping = ((int)(cds_offset - target_black_level / preamp_gain + scaled_offset) < 0 ? 0 : (int)(cds_offset - target_black_level / preamp_gain + scaled_offset)); + /* Stop streaming and wait for frame data transmission done */ + // mira050_write_stop_streaming_regs(mira050); + printk(KERN_INFO "[MIRA050]: offset clip 12 bit mode is %u", offset_clipping); + + usleep_range(wait_us, wait_us + 100); + /* Write fine gain registers */ + + mira050_write(mira050, MIRA050_BANK_SEL_REG, 0); + mira050_write_be16(mira050, MIRA050_OFFSET_CLIPPING, offset_clipping); + printk(KERN_INFO "[MIRA050]: Write offset clipping, val = 0x%x.\n", + offset_clipping); + mira050_write_start_streaming_regs(mira050); + + // mira050_write_start_streaming_regs(mira050); + } + else if (mira050->bit_depth == 10) // 10bit high speed mode gain 1-4 + { + dark_offset_100 = 291; // noncont clock + scale_factor = 4; + cds_offset = 1540; + target_black_level = 32; + + if (gain < ARRAY_SIZE(fine_gain_lut_10bit_hs_4x)) + { + u32 analog_gain = fine_gain_lut_10bit_hs_4x[gain].analog_gain; + u8 gdig_preamp = fine_gain_lut_10bit_hs_4x[gain].gdig_preamp; + u8 rg_adcgain = fine_gain_lut_10bit_hs_4x[gain].rg_adcgain; + u8 rg_mult = fine_gain_lut_10bit_hs_4x[gain].rg_mult; + /* otp_cal_val should come from OTP, but OTP may have incorrect value. */ + u16 preamp_gain_inv = 16 / (gdig_preamp + 1); // invert because fixed point arithmetic + + u16 scaled_offset = (u16)(((mira050->otp_dark_cal_10bit_hs + dark_offset_100) * analog_gain * preamp_gain_inv / (scale_factor)/256) - dark_offset_100) / 100; //div by 256 for analog gain + // newly added. + printk(KERN_INFO "[MIRA050]: scaled offset 10 bit mode is %u dark cal is %u", scaled_offset, mira050->otp_dark_cal_10bit_hs); + + /* Avoid negative offset_clipping value. */ + u16 offset_clipping = ((int)(cds_offset - target_black_level * preamp_gain_inv + scaled_offset) < 0 ? 0 : (int)(cds_offset - target_black_level * preamp_gain_inv + scaled_offset)); + /* Stop streaming and wait for frame data transmission done */ + // mira050_write_stop_streaming_regs(mira050); + printk(KERN_INFO "[MIRA050]: offset clip 10 bit mode is %u", offset_clipping); + + // int part1 = (int)(otp_cal_val + 2.5) ; + // int part2 = (int)(part1*analog_gain / (int)(gdig_preamp + 1)) ; + // int offset_clipping = (part2>>8) - 2; + // int scaled_offset = ((otp_cal_val - 1540) / 4 - target_black_level) * 16 / (gdig_preamp + 1); + /* Avoid negative offset_clipping value. */ + // int offset_clipping = (int)((otp_cal_fine_val + 4) * analog_gain / (int)(gdig_preamp + 1) / 256 - 4); + + // int offset_clipping_calc = (int)(cds_offset - (target_black_level - offset_clipping)); + /*preamp_gain, _, _ = gain_settings._gain_lut[gain] +scaled_offset = (calibration_value + 2.5) * gain/(16*preamp_gain) - 2.5 +mira_xs_api.write_register( +'OFFSET_CLIPPING', +2*act1 - round(target_black_level/preamp_gain - scaled_offset) + offset_clipping = (calibration_value + 2.5) * (analog_gain/4) - 2.5 +cds_offset = 1540 +offset_clip = int(cds_offset - round(target/digital_gain - offset_clipping)) +*/ + // if (offset_clipping_calc < 0) + // { + // offset_clipping = 0; + // } + // else + // { + // offset_clipping = (uint16_t)(offset_clipping_calc); + // } + // = (int)(cds_offset - (target_black_level*digital_gain - offset_clipping)) < 0 ? 0 : (int)(cds_offset - (target_black_level*digital_gain - offset_clipping)); + + // u16 offset_clipping = (offset_clipping_calc < 0) ? 0 : (int)(offset_clipping_calc); + /* Stop streaming and wait for frame data transmission done */ + mira050_write_stop_streaming_regs(mira050); + usleep_range(wait_us, wait_us + 100); + /* Write fine gain registers */ + printk(KERN_INFO "[MIRA050]: Write reg sequence for analog gain %u in 10 bit mode", gain); + printk(KERN_INFO "[MIRA050]: analoggain: %u,gdig_preamp: %u rg_adcgain: %u, rg_mult: %u, offset_clipping: %u, offset_clipping: %u\n", + analog_gain, gdig_preamp, rg_adcgain, rg_mult, offset_clipping, offset_clipping); + mira050_write(mira050, MIRA050_RW_CONTEXT_REG, 0); + mira050_write(mira050, MIRA050_BANK_SEL_REG, 1); + mira050_write(mira050, MIRA050_GDIG_PREAMP, gdig_preamp); + mira050_write(mira050, MIRA050_BANK_SEL_REG, 0); + mira050_write(mira050, MIRA050_BIAS_RG_ADCGAIN, rg_adcgain); + mira050_write(mira050, MIRA050_BIAS_RG_MULT, rg_mult); + mira050_write_be16(mira050, MIRA050_OFFSET_CLIPPING, offset_clipping); + /* Resume streaming */ + mira050_write_start_streaming_regs(mira050); + } + } + else if (mira050->bit_depth == 8) + { + dark_offset_100 = 72; // noncont clock + scale_factor = 16; + cds_offset = 1540; + target_black_level = 16; + + if (gain < ARRAY_SIZE(fine_gain_lut_8bit_16x)) + { + u32 analog_gain = fine_gain_lut_8bit_16x[gain].analog_gain; + u8 gdig_preamp = fine_gain_lut_8bit_16x[gain].gdig_preamp; + u8 rg_adcgain = fine_gain_lut_8bit_16x[gain].rg_adcgain; + u8 rg_mult = fine_gain_lut_8bit_16x[gain].rg_mult; + /* otp_cal_val should come from OTP, but OTP may have incorrect value. */ + u16 preamp_gain_inv = 16 / (gdig_preamp + 1); + + // int part1 = (int)(otp_cal_val + 2.5) ; + // int part2 = (int)(part1*analog_gain / (int)(gdig_preamp + 1)) ; + // int offset_clipping = (part2>>8) - 2; + // int scaled_offset = ((otp_cal_val - 1540) / 4 - target_black_level) * 16 / (gdig_preamp + 1); + /* Avoid negative offset_clipping value. */ + + // newly added. + u16 scaled_offset = (u16)(((mira050->otp_dark_cal_8bit + dark_offset_100) * analog_gain * preamp_gain_inv / (scale_factor)/256) - dark_offset_100) / 100; + printk(KERN_INFO "[MIRA050]: scaled offset 8 bit mode is %u dark cal is %u", scaled_offset, mira050->otp_dark_cal_8bit); + + /* Avoid negative offset_clipping value. */ + u16 offset_clipping = ((int)(cds_offset - target_black_level * preamp_gain_inv + scaled_offset) < 0 ? 0 : (int)(cds_offset - target_black_level * preamp_gain_inv + scaled_offset)); + /* Stop streaming and wait for frame data transmission done */ + // mira050_write_stop_streaming_regs(mira050); + printk(KERN_INFO "[MIRA050]: offset clip 8 bit mode is %u", offset_clipping); + + // int part1 = (int)(otp_cal_val + 2.5) ; + // int part2 = (int)(part1*analog_gain / (int)(gdig_preamp + 1)) ; + // int offset_clipping = (part2>>8) - 2; + // int scaled_offset = ((otp_cal_val - 1540) / 4 - target_black_level) * 16 / (gdig_preamp + 1); + /* Avoid negative offset_clipping value. */ + // int offset_clipping = (int)((otp_cal_fine_val + 4) * analog_gain / (int)(gdig_preamp + 1) / 256 - 4); + + // int offset_clipping_calc = (int)(cds_offset - (target_black_level - offset_clipping)); + /*preamp_gain, _, _ = gain_settings._gain_lut[gain] +scaled_offset = (calibration_value + 2.5) * gain/(16*preamp_gain) - 2.5 +mira_xs_api.write_register( +'OFFSET_CLIPPING', +2*act1 - round(target_black_level/preamp_gain - scaled_offset) + offset_clipping = (calibration_value + 2.5) * (analog_gain/4) - 2.5 +cds_offset = 1540 +offset_clip = int(cds_offset - round(target/digital_gain - offset_clipping)) +*/ + // if (offset_clipping_calc < 0) + // { + // offset_clipping = 0; + // } + // else + // { + // offset_clipping = (uint16_t)(offset_clipping_calc); + // } + printk(KERN_INFO "[MIRA050]: est offset: %u,offset_clipping_calc: %u rg_adcgain: %u, rg_mult: %u, offset_clipping: %u\n", + analog_gain / 256, gdig_preamp, rg_adcgain, rg_mult, offset_clipping); + // = (int)(cds_offset - (target_black_level*digital_gain - offset_clipping)) < 0 ? 0 : (int)(cds_offset - (target_black_level*digital_gain - offset_clipping)); + + // u16 offset_clipping = (offset_clipping_calc < 0) ? 0 : (int)(offset_clipping_calc); + /* Stop streaming and wait for frame data transmission done */ + mira050_write_stop_streaming_regs(mira050); + usleep_range(wait_us, wait_us + 100); + /* Write fine gain registers */ + printk(KERN_INFO "[MIRA050]: Write reg sequence for analog gain %u in 8 bit mode", gain); + printk(KERN_INFO "[MIRA050]: analoggain: %u,gdig_preamp: %u rg_adcgain: %u, rg_mult: %u, offset_clipping: %u, offset_clipping: %u\n", + analog_gain, gdig_preamp, rg_adcgain, rg_mult, offset_clipping, offset_clipping); + mira050_write(mira050, MIRA050_RW_CONTEXT_REG, 0); + mira050_write(mira050, MIRA050_BANK_SEL_REG, 1); + mira050_write(mira050, MIRA050_GDIG_PREAMP, gdig_preamp); + mira050_write(mira050, MIRA050_BANK_SEL_REG, 0); + mira050_write(mira050, MIRA050_BIAS_RG_ADCGAIN, rg_adcgain); + mira050_write(mira050, MIRA050_BIAS_RG_MULT, rg_mult); + mira050_write_be16(mira050, MIRA050_OFFSET_CLIPPING, offset_clipping); + /* Resume streaming */ + mira050_write_start_streaming_regs(mira050); + } + } + else + { + // Other bit depths are not supported + printk(KERN_INFO "[MIRA050]: Ignore analog gain in %u bit mode", mira050->mode->bit_depth); + } + + if (ret) + { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + } + + // Always return 0 even if it fails + return 0; +} + +// Gets the format code if supported. Otherwise returns the default format code `codes[0]` +static u32 mira050_validate_format_code_or_default(struct mira050 *mira050, u32 code) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + unsigned int i; + + lockdep_assert_held(&mira050->mutex); + + for (i = 0; i < ARRAY_SIZE(codes); i++) + if (codes[i] == code) + break; + + if (i >= ARRAY_SIZE(codes)) + { + dev_err_ratelimited(&client->dev, "Could not set requested format code %u", code); + dev_err_ratelimited(&client->dev, "Using default format %u", codes[0]); + i = 0; + } + + return codes[i]; +} + +static void mira050_set_default_format(struct mira050 *mira050) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = &mira050->fmt; + fmt->code = MEDIA_BUS_FMT_SGRBG12_1X12; // MEDIA_BUS_FMT_Y12_1X12; + mira050->bit_depth = 12; + 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); + fmt->width = supported_modes[0].width; + fmt->height = supported_modes[0].height; + fmt->field = V4L2_FIELD_NONE; +} + +static int mira050_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct mira050 *mira050 = to_mira050(sd); + struct v4l2_mbus_framefmt *try_fmt_img = + v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); + struct v4l2_mbus_framefmt *try_fmt_meta = + v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); + struct v4l2_rect *try_crop; + + mutex_lock(&mira050->mutex); + + /* Initialize try_fmt for the image pad */ + try_fmt_img->width = supported_modes[0].width; + try_fmt_img->height = supported_modes[0].height; + try_fmt_img->code = mira050_validate_format_code_or_default(mira050, + MEDIA_BUS_FMT_SGRBG12_1X12); + try_fmt_img->field = V4L2_FIELD_NONE; + + /* TODO(jalv): Initialize try_fmt for the embedded metadata pad */ + try_fmt_meta->width = MIRA050_EMBEDDED_LINE_WIDTH; + try_fmt_meta->height = MIRA050_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->state, 0); + try_crop->top = MIRA050_PIXEL_ARRAY_TOP; + try_crop->left = MIRA050_PIXEL_ARRAY_LEFT; + try_crop->width = MIRA050_PIXEL_ARRAY_WIDTH; + try_crop->height = MIRA050_PIXEL_ARRAY_HEIGHT; + + mutex_unlock(&mira050->mutex); + + return 0; +} + +static int mira050_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira050 *mira050 = + container_of(ctrl->handler, struct mira050, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + int ret = 0; + u32 target_frame_time_us; + + // Debug print + // printk(KERN_INFO "[MIRA050]: mira050_set_ctrl() id: 0x%X value: 0x%X.\n", ctrl->id, ctrl->val); + + if (ctrl->id == V4L2_CID_VBLANK) + { + int exposure_max, exposure_def; + + /* Update max exposure while meeting expected vblanking */ + exposure_max = mira050_calculate_max_exposure_time(MIRA050_MIN_ROW_LENGTH, + mira050->mode->height, + ctrl->val); + exposure_def = (exposure_max < MIRA050_DEFAULT_EXPOSURE_LINES) ? exposure_max : MIRA050_DEFAULT_EXPOSURE_LINES; + __v4l2_ctrl_modify_range(mira050->exposure, + mira050->exposure->minimum, + (int)( exposure_max ), mira050->exposure->step, + (int)( exposure_def )); + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (pm_runtime_get_if_in_use(&client->dev) == 0) + { + dev_info(&client->dev, + "device in use, ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + return 0; + } + + if (mira050->skip_reg_upload == 0) + { + switch (ctrl->id) + { + case V4L2_CID_ANALOGUE_GAIN: + printk(KERN_INFO "[MIRA050]: V4L2_CID_ANALOGUE_GAIN: = %u !!!!!!!!!!!!!\n", + ctrl->val); + ret = mira050_write_analog_gain_reg(mira050, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + // printk(KERN_INFO "[MIRA050]: V4L2_CID_EXPOSURE: exp line = %u, exp us = %u.\n", + // ctrl->val, ctrl->val * MIRA050_MIN_ROW_LENGTH_NS / 1000); + ret = mira050_write_exposure_reg(mira050, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = mira050_write(mira050, MIRA050_BANK_SEL_REG, 0); + // Fixed data is hard coded to 0xAB. + ret = mira050_write(mira050, MIRA050_TRAINING_WORD_REG, 0xAB); + // Gradient is hard coded to 45 degree. + ret = mira050_write(mira050, MIRA050_DELTA_TEST_IMG_REG, 0x01); + ret = mira050_write(mira050, MIRA050_TEST_PATTERN_REG, + mira050_test_pattern_val[ctrl->val]); + break; + case V4L2_CID_HFLIP: + // TODO: HFLIP requires multiple register writes + // ret = mira050_write(mira050, MIRA050_HFLIP_REG, + // ctrl->val); + break; + case V4L2_CID_VFLIP: + // TODO: VFLIP seems not supported in Mira050 + // ret = mira050_write(mira050, MIRA050_VFLIP_REG, + // ctrl->val); + break; + case V4L2_CID_VBLANK: + /* + * In libcamera, frame time (== 1/framerate) is controlled by VBLANK: + * TARGET_FRAME_TIME (us) = 1000000 * ((1/PIXEL_RATE)*(WIDTH+HBLANK)*(HEIGHT+VBLANK)) + */ + mira050->target_frame_time_us = (u32)((u64)(1000000 * (u64)(mira050->mode->width + mira050->mode->hblank) * (u64)(mira050->mode->height + ctrl->val)) / MIRA050_PIXEL_RATE); + // Debug print + printk(KERN_INFO "[MIRA050]: mira050_write_target_frame_time_reg target_frame_time_us = %u.\n", + mira050->target_frame_time_us); + printk(KERN_INFO "[MIRA050]: width %d, hblank %d, vblank %d, height %d, ctrl->val %d.\n", + mira050->mode->width, mira050->mode->hblank, mira050->mode->min_vblank, mira050->mode->height, ctrl->val); + ret = mira050_write_target_frame_time_reg(mira050, mira050->target_frame_time_us); + break; + case V4L2_CID_HBLANK: + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + } + + pm_runtime_put(&client->dev); + + // TODO: FIXIT + return ret; +} + +static int mira050_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira050 *mira050 = + container_of(ctrl->handler, struct mira050, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + int ret = 0; + + // printk(KERN_INFO "[MIRA050]: mira050_s_ctrl() id: %X value: %X.\n", ctrl->id, ctrl->val); + + /* Previously, register writes when powered off will be buffered. + * The buffer will be written to sensor when start_streaming. + * Now, register writes happens immediately, even powered off. + * Register writes when powered off will fail. + * Users need to make sure first power on then write register. + */ + + switch (ctrl->id) + { + case AMS_CAMERA_CID_MIRA_REG_W: + ret = mira050_v4l2_reg_w(mira050, ctrl->val); + break; + default: + dev_info(&client->dev, + "set ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + + // TODO: FIXIT + return ret; +} + +static int mira050_g_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira050 *mira050 = + container_of(ctrl->handler, struct mira050, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + int ret = 0; + + // printk(KERN_INFO "[MIRA050]: mira050_g_ctrl() id: %X.\n", ctrl->id); + + /* + * Ideally, V4L2 register read should happen only when powered on. + * However, perhaps there are use cases that, + * reading other I2C addr is desired when mira sensor is powered off. + * Therefore, the check of "powered" flag is disabled for now. + */ + + switch (ctrl->id) + { + case AMS_CAMERA_CID_MIRA_REG_R: + ret = mira050_v4l2_reg_r(mira050, (u32 *)&ctrl->cur.val); + ctrl->val = ctrl->cur.val; + break; + default: + dev_info(&client->dev, + "get ctrl(id:0x%x) is not handled\n", + ctrl->id); + ret = -EINVAL; + break; + } + + // TODO: FIXIT + return ret; +} + +static const struct v4l2_ctrl_ops mira050_ctrl_ops = { + .s_ctrl = mira050_set_ctrl, +}; + +static const struct v4l2_ctrl_ops mira050_custom_ctrl_ops = { + .g_volatile_ctrl = mira050_g_ctrl, + .s_ctrl = mira050_s_ctrl, +}; + +/* list of custom v4l2 ctls */ +static struct v4l2_ctrl_config custom_ctrl_config_list[] = { + /* Do not change the name field for the controls! */ + { + .ops = &mira050_custom_ctrl_ops, + .id = AMS_CAMERA_CID_MIRA_REG_W, + .name = "mira_reg_w", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = 0, + .min = 0, + .max = 0x7FFFFFFF, + .def = 0, + .step = 1, + }, + { + .ops = &mira050_custom_ctrl_ops, + .id = AMS_CAMERA_CID_MIRA_REG_R, + .name = "mira_reg_r", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = 0, + .min = 0, + .max = 0x7FFFFFFF, + .def = 0, + .step = 1, + }, + +}; + +// This function should enumerate all the media bus formats for the requested pads. If the requested +// format index is beyond the number of avaialble formats it shall return -EINVAL; +static int mira050_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mira050 *mira050 = to_mira050(sd); + + if (code->pad >= NUM_PADS) + return -EINVAL; + + if (code->pad == IMAGE_PAD) + { + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = mira050_validate_format_code_or_default(mira050, + codes[code->index]); + } + else + { + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SENSOR_DATA; + } + + return 0; +} + +static int mira050_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct mira050 *mira050 = to_mira050(sd); + + if (fse->pad >= NUM_PADS) + return -EINVAL; + + if (fse->pad == IMAGE_PAD) + { + /* Two options about how many modes to be exposed: + * - Expose all supported_modes by ARRAY_SIZE(supported_modes). + * - Expose less modes by MIRA050_SUPPORTED_MODE_SIZE_PUBLIC. + */ + /* if (fse->index >= ARRAY_SIZE(supported_modes)) */ + if (fse->index >= MIRA050_SUPPORTED_MODE_SIZE_PUBLIC) + return -EINVAL; + + if (fse->code != mira050_validate_format_code_or_default(mira050, fse->code)) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + } + else + { + if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) + return -EINVAL; + + fse->min_width = MIRA050_EMBEDDED_LINE_WIDTH; + fse->max_width = fse->min_width; + fse->min_height = MIRA050_NUM_EMBEDDED_LINES; + fse->max_height = fse->min_height; + } + + return 0; +} + +static void mira050_reset_colorspace(struct v4l2_mbus_framefmt *fmt) +{ + 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); +} + +static void mira050_update_image_pad_format(struct mira050 *mira050, + const struct mira050_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + mira050_reset_colorspace(&fmt->format); +} + +static void mira050_update_metadata_pad_format(struct v4l2_subdev_format *fmt) +{ + fmt->format.width = MIRA050_EMBEDDED_LINE_WIDTH; + fmt->format.height = MIRA050_NUM_EMBEDDED_LINES; + fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format.field = V4L2_FIELD_NONE; +} + +static int __mira050_get_pad_format(struct mira050 *mira050, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + if (fmt->pad >= NUM_PADS) + return -EINVAL; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(&mira050->sd, sd_state, fmt->pad); + + try_fmt->code = fmt->pad == IMAGE_PAD ? mira050_validate_format_code_or_default(mira050, try_fmt->code) : MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format = *try_fmt; + } + else + { + if (fmt->pad == IMAGE_PAD) + { + mira050_update_image_pad_format(mira050, mira050->mode, + fmt); + fmt->format.code = mira050_validate_format_code_or_default(mira050, + mira050->fmt.code); + } + else + { + mira050_update_metadata_pad_format(fmt); + } + } + + return 0; +} + +static int mira050_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mira050 *mira050 = to_mira050(sd); + int ret; + + mutex_lock(&mira050->mutex); + ret = __mira050_get_pad_format(mira050, sd_state, fmt); + mutex_unlock(&mira050->mutex); + + return ret; +} + +static int mira050_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mira050 *mira050 = to_mira050(sd); + const struct mira050_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + u32 max_exposure = 0, default_exp = 0; + int rc = 0; + + if (fmt->pad >= NUM_PADS) + return -EINVAL; + + mutex_lock(&mira050->mutex); + + if (fmt->pad == IMAGE_PAD) + { + /* Validate format or use default */ + fmt->format.code = mira050_validate_format_code_or_default(mira050, + fmt->format.code); + + switch (fmt->format.code) + { + case MEDIA_BUS_FMT_SGRBG10_1X10: + printk(KERN_INFO "[MIRA050]: fmt->format.code() selects 10 bit mode.\n"); + mira050->mode = &supported_modes[1]; + mira050->bit_depth = 10; + // return 0; + break; + + case MEDIA_BUS_FMT_SGRBG12_1X12: + printk(KERN_INFO "[MIRA050]: fmt->format.code() selects 12 bit mode.\n"); + mira050->mode = &supported_modes[0]; + mira050->bit_depth = 12; + // return 0; + break; + + case MEDIA_BUS_FMT_SGRBG8_1X8: + printk(KERN_INFO "[MIRA050]: fmt->format.code() selects 8 bit mode.\n"); + mira050->mode = &supported_modes[2]; + mira050->bit_depth = 8; + // return 0; + break; + default: + printk(KERN_ERR "Unknown format requested fmt->format.code() %d", fmt->format.code); + } + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, + fmt->format.height); + mira050_update_image_pad_format(mira050, mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + { + framefmt = v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); + *framefmt = fmt->format; + } + else if (mira050->mode != mode || + mira050->fmt.code != fmt->format.code) + { + + mira050->fmt = fmt->format; + // mira050->mode = mode; + + // Update controls based on new mode (range and current value). + max_exposure = mira050_calculate_max_exposure_time(MIRA050_MIN_ROW_LENGTH, + mira050->mode->height, + mira050->mode->min_vblank); + + default_exp = MIRA050_DEFAULT_EXPOSURE_LINES > max_exposure ? max_exposure : MIRA050_DEFAULT_EXPOSURE_LINES; + rc = __v4l2_ctrl_modify_range(mira050->exposure, + mira050->exposure->minimum, + (int)(1 + max_exposure), mira050->exposure->step, + (int)(1 + default_exp)); + if (rc) + { + dev_err(&client->dev, "Error setting exposure range"); + } + + printk(KERN_INFO "[MIRA050]: Mira050 SETTING ANA GAIN RANGE = %u.\n", + ARRAY_SIZE(fine_gain_lut_8bit_16x) - 1); + // #FIXME #TODO + // rc = __v4l2_ctrl_modify_range(mira050->gain, + // 0, ARRAY_SIZE(fine_gain_lut_8bit_16x) - 1, 1, 0); + rc = __v4l2_ctrl_modify_range(mira050->gain, + mira050->mode->gain_min, + mira050->mode->gain_max, + 1, + 0); + if (rc) + { + dev_err(&client->dev, "Error setting gain range"); + } + + printk(KERN_INFO "[MIRA050]: Mira050 VBLANK = %u.\n", + mira050->mode->min_vblank); + + rc = __v4l2_ctrl_modify_range(mira050->vblank, + mira050->mode->min_vblank, + mira050->mode->max_vblank, + 1, + MIRA050_MIN_VBLANK_60); + if (rc) + { + dev_err(&client->dev, "Error setting exposure range"); + } + // Set the current vblank value + rc = __v4l2_ctrl_s_ctrl(mira050->vblank, MIRA050_MIN_VBLANK_60); + if (rc) + { + dev_err(&client->dev, "Error setting vblank value to %u", + mira050->mode->min_vblank); + } + } + } + else + { + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + { + framefmt = v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); + *framefmt = fmt->format; + } + else + { + /* Only one embedded data mode is supported */ + mira050_update_metadata_pad_format(fmt); + } + } + + mutex_unlock(&mira050->mutex); + + return 0; +} + +static int mira050_set_framefmt(struct mira050 *mira050) +{ + // TODO: There is no easy way to change frame format + switch (mira050->fmt.code) + { + case MEDIA_BUS_FMT_SGRBG8_1X8: + printk(KERN_INFO "[MIRA050]: mira050_set_framefmt() selects 8 bit mode.\n"); + mira050->mode = &supported_modes[2]; + mira050->bit_depth = 8; + __v4l2_ctrl_modify_range(mira050->gain, + 0, ARRAY_SIZE(fine_gain_lut_8bit_16x) - 1, 1, 0); + return 0; + case MEDIA_BUS_FMT_SGRBG10_1X10: + printk(KERN_INFO "[MIRA050]: mira050_set_framefmt() selects 10 bit mode.\n"); + mira050->mode = &supported_modes[1]; + mira050->bit_depth = 10; + __v4l2_ctrl_modify_range(mira050->gain, + 0, ARRAY_SIZE(fine_gain_lut_10bit_hs_4x) - 1, 1, 0); + return 0; + case MEDIA_BUS_FMT_SGRBG12_1X12: + printk(KERN_INFO "[MIRA050]: mira050_set_framefmt() selects 12 bit mode.\n"); + mira050->mode = &supported_modes[0]; + mira050->bit_depth = 12; + __v4l2_ctrl_modify_range(mira050->gain, + mira050->mode->gain_min, mira050->mode->gain_max, + MIRA050_ANALOG_GAIN_STEP, MIRA050_ANALOG_GAIN_DEFAULT); + return 0; + default: + printk(KERN_ERR "Unknown format requested %d", mira050->fmt.code); + } + + return -EINVAL; +} + +static const struct v4l2_rect * +__mira050_get_pad_crop(struct mira050 *mira050, struct v4l2_subdev_state *sd_state, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) + { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&mira050->sd, sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mira050->mode->crop; + } + + return NULL; +} + +static int mira050_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) + { + case V4L2_SEL_TGT_CROP: + { + struct mira050 *mira050 = to_mira050(sd); + + mutex_lock(&mira050->mutex); + sel->r = *__mira050_get_pad_crop(mira050, sd_state, sel->pad, + sel->which); + mutex_unlock(&mira050->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = MIRA050_NATIVE_WIDTH; + sel->r.height = MIRA050_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = MIRA050_PIXEL_ARRAY_TOP; + sel->r.left = MIRA050_PIXEL_ARRAY_LEFT; + sel->r.width = MIRA050_PIXEL_ARRAY_WIDTH; + sel->r.height = MIRA050_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + +static int mira050_start_streaming(struct mira050 *mira050) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + const struct mira050_reg_list *reg_list; + + u32 otp_dark_cal_8bit; + u32 otp_dark_cal_10bit_hs; + u32 otp_dark_cal_10bit; + u32 otp_dark_cal_12bit; + int ret; + + printk(KERN_INFO "[MIRA050]: Entering START STREAMING function !!!!!!!!!!.\n"); + + /* Follow examples of other camera driver, here use pm_runtime_resume_and_get */ + ret = pm_runtime_resume_and_get(&client->dev); + + if (ret < 0) + { + printk(KERN_INFO "[MIRA050]: get_sync failed, but continue.\n"); + pm_runtime_put_noidle(&client->dev); + return ret; + } + + /* Set current mode according to frame format bit depth */ + ret = mira050_set_framefmt(mira050); + if (ret) + { + dev_err(&client->dev, "%s failed to set frame format: %d\n", + __func__, ret); + goto err_rpm_put; + } + printk(KERN_INFO "[MIRA050]: Register sequence for %d bit mode will be used.\n", mira050->mode->bit_depth); + + if (mira050->skip_reg_upload == 0) + { + /* Apply pre soft reset default values of current mode */ + reg_list = &mira050->mode->reg_list_pre_soft_reset; + printk(KERN_INFO "[MIRA050]: Write %d regs.\n", reg_list->num_of_regs); + ret = mira050_write_regs(mira050, reg_list->regs, reg_list->num_of_regs); + if (ret) + { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + goto err_rpm_put; + } + + usleep_range(10, 50); + + /* Apply post soft reset default values of current mode */ + reg_list = &mira050->mode->reg_list_post_soft_reset; + printk(KERN_INFO "[MIRA050]: Write %d regs.\n", reg_list->num_of_regs); + ret = mira050_write_regs(mira050, reg_list->regs, reg_list->num_of_regs); + if (ret) + { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + goto err_rpm_put; + } + } + else + { + printk(KERN_INFO "[MIRA050]: Skip base register sequence upload, due to mira050->skip_reg_upload=%u.\n", mira050->skip_reg_upload); + } + + printk(KERN_INFO "[MIRA050]: Entering v4l2 ctrl handler setup function.\n"); + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(mira050->sd.ctrl_handler); + printk(KERN_INFO "[MIRA050]: __v4l2_ctrl_handler_setup ret = %d.\n", ret); + if (ret) + goto err_rpm_put; + + + + + usleep_range(10, 50); + /* ********* READ OTP VALUES for revB - all modes ********** */ + ret = mira050_otp_read(mira050, 0x04, &otp_dark_cal_8bit); + /* OTP_CALIBRATION_VALUE is little-endian, LSB at [7:0], MSB at [15:8] */ + mira050->otp_dark_cal_8bit = (u16)(otp_dark_cal_8bit & 0x0000FFFF); + if (ret) + { + dev_err(&client->dev, "%s failed to read OTP addr 0x01.\n", __func__); + } + else + { + printk(KERN_INFO "[MIRA050]: OTP_CALIBRATION_VALUE 8b: %u, extracted from 32-bit 0x%X.\n", mira050->otp_dark_cal_8bit, otp_dark_cal_8bit); + } + ret = mira050_otp_read(mira050, 0x05, &otp_dark_cal_10bit_hs); + /* OTP_CALIBRATION_VALUE is little-endian, LSB at [7:0], MSB at [15:8] */ + mira050->otp_dark_cal_10bit_hs = (u16)(otp_dark_cal_10bit_hs & 0x0000FFFF); + if (ret) + { + dev_err(&client->dev, "%s failed to read OTP addr 0x01.\n", __func__); + } + else + { + printk(KERN_INFO "[MIRA050]: OTP_CALIBRATION_VALUE 10b hs: %u, extracted from 32-bit 0x%X.\n", mira050->otp_dark_cal_10bit_hs, otp_dark_cal_10bit_hs); + } + ret = mira050_otp_read(mira050, 0x06, &otp_dark_cal_10bit); + /* OTP_CALIBRATION_VALUE is little-endian, LSB at [7:0], MSB at [15:8] */ + mira050->otp_dark_cal_10bit = (u16)(otp_dark_cal_10bit & 0x0000FFFF); + if (ret) + { + dev_err(&client->dev, "%s failed to read OTP addr 0x01.\n", __func__); + } + else + { + printk(KERN_INFO "[MIRA050]: OTP_CALIBRATION_VALUE 10b: %u, extracted from 32-bit 0x%X.\n", mira050->otp_dark_cal_10bit, otp_dark_cal_10bit); + } + // 12 bit + usleep_range(10, 50); + ret = mira050_otp_read(mira050, 0x07, &otp_dark_cal_12bit); + /* OTP_CALIBRATION_VALUE is little-endian, LSB at [7:0], MSB at [15:8] */ + mira050->otp_dark_cal_12bit = (u16)(otp_dark_cal_12bit & 0x0000FFFF); + + if (ret) + { + dev_err(&client->dev, "%s failed to read OTP addr 0x07.\n", __func__); + } + else + { + printk(KERN_INFO "[MIRA050]: OTP_CALIBRATION_VALUE 12b: %u, extracted from 32-bit 0x%X.\n", mira050->otp_dark_cal_12bit, otp_dark_cal_12bit); + } + + + // ret = mira050_write_analog_gain_reg(mira050, 0); + if (mira050->skip_reg_upload == 0 || + (mira050->skip_reg_upload == 1 && mira050->force_stream_ctrl == 1)) + { + printk(KERN_INFO "[MIRA050]: Writing start streaming regs.\n"); + ret = mira050_write_start_streaming_regs(mira050); + if (ret) + { + dev_err(&client->dev, "Could not write stream-on sequence"); + goto err_rpm_put; + } + } + else + { + printk(KERN_INFO "[MIRA050]: Skip write_start_streaming_regs due to skip_reg_upload == %d and force_stream_ctrl == %d.\n", + mira050->skip_reg_upload, mira050->force_stream_ctrl); + } + + /* vflip and hflip cannot change during streaming */ + printk(KERN_INFO "[MIRA050]: Entering v4l2 ctrl grab vflip grab vflip.\n"); + __v4l2_ctrl_grab(mira050->vflip, true); + printk(KERN_INFO "[MIRA050]: Entering v4l2 ctrl grab vflip grab hflip.\n"); + __v4l2_ctrl_grab(mira050->hflip, true); + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); + return ret; +} + +static void mira050_stop_streaming(struct mira050 *mira050) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + int ret = 0; + + /* Unlock controls for vflip and hflip */ + __v4l2_ctrl_grab(mira050->vflip, false); + __v4l2_ctrl_grab(mira050->hflip, false); + + if (mira050->skip_reset == 0) + { + if (mira050->skip_reg_upload == 0 || + (mira050->skip_reg_upload == 1 && mira050->force_stream_ctrl == 1)) + { + printk(KERN_INFO "[MIRA050]: Writing stop streaming regs.\n"); + ret = mira050_write_stop_streaming_regs(mira050); + if (ret) + { + dev_err(&client->dev, "Could not write the stream-off sequence"); + } + } + else + { + printk(KERN_INFO "[MIRA050]: Skip write_stop_streaming_regs due to skip_reg_upload == %d and force_stream_ctrl == %d.\n", + mira050->skip_reg_upload, mira050->force_stream_ctrl); + } + } + else + { + printk(KERN_INFO "[MIRA050]: Skip write_stop_streaming_regs due to mira050->skip_reset == %d.\n", mira050->skip_reset); + } + + pm_runtime_put(&client->dev); +} + +static int mira050_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct mira050 *mira050 = to_mira050(sd); + int ret = 0; + + mutex_lock(&mira050->mutex); + if (mira050->streaming == enable) + { + mutex_unlock(&mira050->mutex); + return 0; + } + + printk(KERN_INFO "[MIRA050]: Entering mira050_set_stream enable: %d.\n", enable); + + if (enable) + { + /* + * Apply default & customized values + * and then start streaming. + */ + ret = mira050_start_streaming(mira050); + if (ret) + goto err_unlock; + } + else + { + mira050_stop_streaming(mira050); + } + + mira050->streaming = enable; + + mutex_unlock(&mira050->mutex); + + printk(KERN_INFO "[MIRA050]: Returning mira050_set_stream with ret: %d.\n", ret); + + return ret; + +err_unlock: + mutex_unlock(&mira050->mutex); + + return ret; +} + +static int __maybe_unused mira050_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira050 *mira050 = to_mira050(sd); + + printk(KERN_INFO "[MIRA050]: Entering suspend function.\n"); + + if (mira050->streaming) + mira050_stop_streaming(mira050); + + return 0; +} + +static int __maybe_unused mira050_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira050 *mira050 = to_mira050(sd); + int ret; + + printk(KERN_INFO "[MIRA050]: Entering resume function.\n"); + + if (mira050->streaming) + { + ret = mira050_start_streaming(mira050); + if (ret) + goto error; + } + + return 0; + +error: + mira050_stop_streaming(mira050); + mira050->streaming = false; + + return ret; +} + +static int mira050_get_regulators(struct mira050 *mira050) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + unsigned int i; + + for (i = 0; i < MIRA050_NUM_SUPPLIES; i++) + mira050->supplies[i].supply = mira050_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, + MIRA050_NUM_SUPPLIES, + mira050->supplies); +} + +/* Verify chip ID */ +static int mira050_identify_module(struct mira050 *mira050) +{ + int ret; + u8 val; + + ret = mira050_read(mira050, 0x25, &val); + printk(KERN_INFO "[MIRA050]: Read reg 0x%4.4x, val = 0x%x.\n", + 0x25, val); + ret = mira050_read(mira050, 0x3, &val); + printk(KERN_INFO "[MIRA050]: Read reg 0x%4.4x, val = 0x%x.\n", + 0x3, val); + ret = mira050_read(mira050, 0x4, &val); + printk(KERN_INFO "[MIRA050]: Read reg 0x%4.4x, val = 0x%x.\n", + 0x4, val); + + return 0; +} + +static const struct v4l2_subdev_core_ops mira050_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops mira050_video_ops = { + .s_stream = mira050_set_stream, +}; + +static const struct v4l2_subdev_pad_ops mira050_pad_ops = { + .enum_mbus_code = mira050_enum_mbus_code, + .get_fmt = mira050_get_pad_format, + .set_fmt = mira050_set_pad_format, + .get_selection = mira050_get_selection, + .enum_frame_size = mira050_enum_frame_size, +}; + +static const struct v4l2_subdev_ops mira050_subdev_ops = { + .core = &mira050_core_ops, + .video = &mira050_video_ops, + .pad = &mira050_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mira050_internal_ops = { + .open = mira050_open, +}; + +/* Initialize control handlers */ +static int mira050_init_controls(struct mira050 *mira050) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira050->sd); + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_fwnode_device_properties props; + int ret; + struct v4l2_ctrl_config *mira050_reg_w; + struct v4l2_ctrl_config *mira050_reg_r; + + ctrl_hdlr = &mira050->ctrl_handler; + /* v4l2_ctrl_handler_init gives a hint/guess of the number of v4l2_ctrl_new */ + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16); + if (ret) + return ret; + + mutex_init(&mira050->mutex); + ctrl_hdlr->lock = &mira050->mutex; + + printk(KERN_INFO "[MIRA050]: %s V4L2_CID_PIXEL_RATE %X.\n", __func__, V4L2_CID_PIXEL_RATE); + printk(KERN_INFO "[MIRA050]: %s INIT_CONTROLS bitmode %X.\n", __func__, mira050->mode->bit_depth); + + /* By default, PIXEL_RATE is read only */ + mira050->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &mira050_ctrl_ops, + V4L2_CID_PIXEL_RATE, + MIRA050_PIXEL_RATE, + MIRA050_PIXEL_RATE, 1, + MIRA050_PIXEL_RATE); + + printk(KERN_INFO "[MIRA050]: %s V4L2_CID_VBLANK %X.\n", __func__, V4L2_CID_VBLANK); + + mira050->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &mira050_ctrl_ops, + V4L2_CID_VBLANK, mira050->mode->min_vblank, + mira050->mode->max_vblank, 1, + MIRA050_MIN_VBLANK_60); + + printk(KERN_INFO "[MIRA050]: %s V4L2_CID_HBLANK %X.\n", __func__, V4L2_CID_HBLANK); + + mira050->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &mira050_ctrl_ops, + V4L2_CID_HBLANK, mira050->mode->hblank, + mira050->mode->hblank, 1, + mira050->mode->hblank); + + // Make the vblank control read only. This could be changed to allow changing framerate in + // runtime, but would require adapting other settings + // mira050->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + // Exposure is indicated in number of lines here + // Max is determined by vblank + vsize and Tglob. + printk(KERN_INFO "[MIRA050]: %s V4L2_CID_EXPOSURE %X.\n", __func__, V4L2_CID_EXPOSURE); + mira050->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &mira050_ctrl_ops, + V4L2_CID_EXPOSURE, + MIRA050_EXPOSURE_MIN_US/10, MIRA050_EXPOSURE_MAX_US/10, + 1, + MIRA050_DEFAULT_EXPOSURE_LINES); + printk(KERN_INFO "[MIRA050]: %s V4L2_CID_ANALOGUE_GAIN %X.\n", __func__, V4L2_CID_ANALOGUE_GAIN); + + mira050->gain = v4l2_ctrl_new_std(ctrl_hdlr, &mira050_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + mira050->mode->gain_min, mira050->mode->gain_max, + MIRA050_ANALOG_GAIN_STEP, MIRA050_ANALOG_GAIN_DEFAULT); + + printk(KERN_INFO "[MIRA050]: %s V4L2_CID_HFLIP %X.\n", __func__, V4L2_CID_HFLIP); + + mira050->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &mira050_ctrl_ops, + V4L2_CID_HFLIP, 0, 0, 1, 0); + if (mira050->hflip) + mira050->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + printk(KERN_INFO "[MIRA050]: %s V4L2_CID_VFLIP %X.\n", __func__, V4L2_CID_VFLIP); + + mira050->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &mira050_ctrl_ops, + V4L2_CID_VFLIP, 0, 0, 1, 0); + if (mira050->vflip) + mira050->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + printk(KERN_INFO "[MIRA050]: %s V4L2_CID_TEST_PATTERN %X.\n", __func__, V4L2_CID_TEST_PATTERN); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &mira050_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(mira050_test_pattern_menu) - 1, + 0, 0, mira050_test_pattern_menu); + + /* + * Custom op + */ + mira050_reg_w = &custom_ctrl_config_list[0]; + printk(KERN_INFO "[MIRA050]: %s AMS_CAMERA_CID_MIRA_REG_W %X.\n", __func__, AMS_CAMERA_CID_MIRA_REG_W); + mira050->mira050_reg_w = v4l2_ctrl_new_custom(ctrl_hdlr, mira050_reg_w, NULL); + + mira050_reg_r = &custom_ctrl_config_list[1]; + printk(KERN_INFO "[MIRA050]: %s AMS_CAMERA_CID_MIRA_REG_R %X.\n", __func__, AMS_CAMERA_CID_MIRA_REG_R); + mira050->mira050_reg_r = v4l2_ctrl_new_custom(ctrl_hdlr, mira050_reg_r, NULL); + if (mira050->mira050_reg_r) + mira050->mira050_reg_r->flags |= (V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY); + + 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, &mira050_ctrl_ops, + &props); + if (ret) + goto error; + + mira050->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + mutex_destroy(&mira050->mutex); + + return ret; +} + +static void mira050_free_controls(struct mira050 *mira050) +{ + v4l2_ctrl_handler_free(mira050->sd.ctrl_handler); + mutex_destroy(&mira050->mutex); +} + +static int mira050_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY}; + int ret = -EINVAL; + + 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; + } + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes != 1) + { + dev_err(dev, "only 1 data lanes are currently supported\n"); + goto error_out; + } + + /* Check the link frequency set in device tree */ + if (!ep_cfg.nr_of_link_frequencies) + { + dev_err(dev, "link-frequency property not found in DT\n"); + goto error_out; + } + + if (ep_cfg.nr_of_link_frequencies != 1 || + ep_cfg.link_frequencies[0] != MIRA050_DEFAULT_LINK_FREQ) + { + dev_err(dev, "Link frequency not supported: %lld\n", + ep_cfg.link_frequencies[0]); + goto error_out; + } + + // TODO(jalv): Check device tree configuration and make sure it is supported by the driver + ret = 0; + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int mira050pmic_init_controls(struct i2c_client *pmic_client, struct i2c_client *uc_client) +{ + int ret; + u8 val; + + // uC, set atb and jtag high + // according to old uC fw (svn rev41) + // 12[3] ldo en + // 11[4,5] atpg jtag + // 11/12 i/o direction, 15/16 output high/low + // uC, set atb and jtag high + // WARNING this only works on interposer v2 if R307 is not populated. otherwise, invert the bit for ldo + ret = mira050pmic_write(uc_client, 12, 0xF7); + ret = mira050pmic_write(uc_client, 16, 0xFF); // ldo en:1 + ret = mira050pmic_write(uc_client, 11, 0XCF); + ret = mira050pmic_write(uc_client, 15, 0xFF); + ret = mira050pmic_write(uc_client, 6, 1); // write + + // Disable master switch // + ret = mira050pmic_write(pmic_client, 0x62, 0x00); + + // Set all voltages to 0 + + // DCDC1=0V + ret = mira050pmic_write(pmic_client, 0x05, 0x00); + // DCDC4=0V + ret = mira050pmic_write(pmic_client, 0x0E, 0x0); + // LDO1=0V VDDLO_PLL + ret = mira050pmic_write(pmic_client, 0x11, 0x0); + // LDO2=0.0V + ret = mira050pmic_write(pmic_client, 0x14, 0x00); + // LDO3=0.0V + ret = mira050pmic_write(pmic_client, 0x17, 0x00); + // LDO4=0V + ret = mira050pmic_write(pmic_client, 0x1A, 0x00); + // LDO5=0.0V + ret = mira050pmic_write(pmic_client, 0x1C, 0x00); + // LDO6=0.0V + ret = mira050pmic_write(pmic_client, 0x1D, 0x00); + // LDO7=0V + ret = mira050pmic_write(pmic_client, 0x1E, 0x0); + // LDO8=0.0V + ret = mira050pmic_write(pmic_client, 0x1F, 0x00); + // Disable LDO9 Lock + ret = mira050pmic_write(pmic_client, 0x24, 0x48); + // LDO9=0V VDDHI + ret = mira050pmic_write(pmic_client, 0x20, 0x00); + // LDO10=0V VDDLO_ANA + ret = mira050pmic_write(pmic_client, 0x21, 0x0); + + // Enable master switch // + usleep_range(50, 60); + ret = mira050pmic_write(pmic_client, 0x62, 0x0D); // enable master switch + usleep_range(50, 60); + + // start PMIC + // Keep LDOs always on + ret = mira050pmic_write(pmic_client, 0x27, 0xFF); + ret = mira050pmic_write(pmic_client, 0x28, 0xFF); + ret = mira050pmic_write(pmic_client, 0x29, 0x00); + ret = mira050pmic_write(pmic_client, 0x2A, 0x00); + ret = mira050pmic_write(pmic_client, 0x2B, 0x00); + + // Unused LDO off // + usleep_range(50, 60); + // set GPIO1=0 + ret = mira050pmic_write(pmic_client, 0x41, 0x04); + // DCDC2=0.0V SPARE_PWR1 + ret = mira050pmic_write(pmic_client, 0x01, 0x00); + ret = mira050pmic_write(pmic_client, 0x08, 0x00); + // DCDC3=0V SPARE_PWR1 + ret = mira050pmic_write(pmic_client, 0x02, 0x00); + ret = mira050pmic_write(pmic_client, 0x0B, 0x00); + // LDO2=0.0V + ret = mira050pmic_write(pmic_client, 0x14, 0x00); + // LDO3=0.0V + ret = mira050pmic_write(pmic_client, 0x17, 0x00); + // LDO5=0.0V + ret = mira050pmic_write(pmic_client, 0x1C, 0x00); + // LDO6=0.0V + ret = mira050pmic_write(pmic_client, 0x1D, 0x00); + // LDO8=0.0V + ret = mira050pmic_write(pmic_client, 0x1F, 0x00); + + ret = mira050pmic_write(pmic_client, 0x42, 4); + + // Enable 1.80V // + usleep_range(50, 60); + // DCDC1=1.8V VINLDO1p8 >=1P8 + ret = mira050pmic_write(pmic_client, 0x00, 0x00); + ret = mira050pmic_write(pmic_client, 0x04, 0x34); + ret = mira050pmic_write(pmic_client, 0x06, 0xBF); + ret = mira050pmic_write(pmic_client, 0x05, 0xB4); + // DCDC4=1.8V VDDIO + ret = mira050pmic_write(pmic_client, 0x03, 0x00); + ret = mira050pmic_write(pmic_client, 0x0D, 0x34); + ret = mira050pmic_write(pmic_client, 0x0F, 0xBF); + ret = mira050pmic_write(pmic_client, 0x0E, 0xB4); + + // Enable 2.85V // + usleep_range(50, 60); + // LDO4=2.85V VDDHI alternativ + ret = mira050pmic_write(pmic_client, 0x1A, 0xB8); // Either 0x00 or 0xB8 + // Disable LDO9 Lock + ret = mira050pmic_write(pmic_client, 0x24, 0x48); + // LDO9=2.85V VDDHI + ret = mira050pmic_read(pmic_client, 0x20, &val); + dev_err(&pmic_client->dev, "Read 0x20 with val %x\n", val); + ret = mira050pmic_write(pmic_client, 0x20, 0xB9); + ret = mira050pmic_read(pmic_client, 0x20, &val); + dev_err(&pmic_client->dev, "Read 0x20 with val %x\n", val); + + // VPIXH on cob = vdd25A on interposer = LDO4 on pmic + // VPIXH should connect to VDD28 on pcb, or enable 4th supply + ret = mira050pmic_read(pmic_client, 0x19, &val); + dev_err(&pmic_client->dev, "Read 0x19 with val %x\n", val); + ret = mira050pmic_write(pmic_client, 0x19, 0x38); + ret = mira050pmic_read(pmic_client, 0x19, &val); + dev_err(&pmic_client->dev, "Read 0x19 with val %x\n", val); + + // Enable 1.2V // + usleep_range(700, 710); + // LDO1=1.2V VDDLO_PLL + ret = mira050pmic_write(pmic_client, 0x12, 0x16); + ret = mira050pmic_write(pmic_client, 0x10, 0x16); + ret = mira050pmic_write(pmic_client, 0x11, 0x90); + // LDO7=1.2V VDDLO_DIG + ret = mira050pmic_write(pmic_client, 0x1E, 0x90); + // LDO10=1.2V VDDLO_ANA + ret = mira050pmic_write(pmic_client, 0x21, 0x90); + + // Enable green LED // + usleep_range(50, 60); + ret = mira050pmic_write(pmic_client, 0x42, 0x15); // gpio2 + // ret = mira050pmic_write(pmic_client, 0x43, 0x40); // leda + // ret = mira050pmic_write(pmic_client, 0x44, 0x40); // ledb + ret = mira050pmic_write(pmic_client, 0x45, 0x40); // ledc + + // ret = mira050pmic_write(pmic_client, 0x47, 0x02); // leda ctrl1 + // ret = mira050pmic_write(pmic_client, 0x4F, 0x02); // ledb ctrl1 + ret = mira050pmic_write(pmic_client, 0x57, 0x02); // ledc ctrl1 + + // ret = mira050pmic_write(pmic_client, 0x4D, 0x01); // leda ctrl1 + // ret = mira050pmic_write(pmic_client, 0x55, 0x10); // ledb ctrl7 + ret = mira050pmic_write(pmic_client, 0x5D, 0x10); // ledc ctrl7 + ret = mira050pmic_write(pmic_client, 0x61, 0x10); // led seq -- use this to turn on leds. abc0000- 1110000 for all leds + + // uC, set atb and jtag high and ldo_en + ret = mira050pmic_write(uc_client, 12, 0xF7); + ret = mira050pmic_write(uc_client, 16, 0xF7); // ldo en:0 + /* + * In Mira050-bringup.py, write 11, 0xCF; 15: 0x30. + * In mira050.py, write 11, 0x8D; 15, 0xFD. + */ + ret = mira050pmic_write(uc_client, 11, 0X8D); + ret = mira050pmic_write(uc_client, 15, 0xFD); + ret = mira050pmic_write(uc_client, 6, 1); // write + + usleep_range(2000000, 2001000); + + return 0; +} + +static int mira050_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct mira050 *mira050; + int ret; + + printk(KERN_INFO "[MIRA050]: probing v4l2 sensor.\n"); + printk(KERN_INFO "[MIRA050]: Driver Version 0.0.\n"); + + dev_err(dev, "[MIRA050] name: %s.\n", client->name); + + mira050 = devm_kzalloc(&client->dev, sizeof(*mira050), GFP_KERNEL); + if (!mira050) + return -ENOMEM; + + v4l2_i2c_subdev_init(&mira050->sd, client, &mira050_subdev_ops); + + /* Check the hardware configuration in device tree */ + if (mira050_check_hwcfg(dev)) + return -EINVAL; + + /* Parse device tree to check if dtoverlay has param skip-reg-upload=1 */ + device_property_read_u32(dev, "skip-reg-upload", &mira050->skip_reg_upload); + printk(KERN_INFO "[MIRA050]: skip-reg-upload %d.\n", mira050->skip_reg_upload); + /* Set default TBD I2C device address to LED I2C Address*/ + mira050->tbd_client_i2c_addr = MIRA050LED_I2C_ADDR; + printk(KERN_INFO "[MIRA050]: User defined I2C device address defaults to LED driver I2C address 0x%X.\n", mira050->tbd_client_i2c_addr); + + /* Get system clock (xclk) */ + mira050->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(mira050->xclk)) + { + dev_err(dev, "failed to get xclk\n"); + return PTR_ERR(mira050->xclk); + } + + mira050->xclk_freq = clk_get_rate(mira050->xclk); + if (mira050->xclk_freq != MIRA050_SUPPORTED_XCLK_FREQ) + { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + mira050->xclk_freq); + return -EINVAL; + } + + ret = mira050_get_regulators(mira050); + if (ret) + { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + { + printk(KERN_INFO "[MIRA050]: Init PMIC and uC and led driver.\n"); + mira050->pmic_client = i2c_new_dummy_device(client->adapter, + MIRA050PMIC_I2C_ADDR); + if (IS_ERR(mira050->pmic_client)) + return PTR_ERR(mira050->pmic_client); + mira050->uc_client = i2c_new_dummy_device(client->adapter, + MIRA050UC_I2C_ADDR); + if (IS_ERR(mira050->uc_client)) + return PTR_ERR(mira050->uc_client); + mira050->led_client = i2c_new_dummy_device(client->adapter, + MIRA050LED_I2C_ADDR); + if (IS_ERR(mira050->led_client)) + return PTR_ERR(mira050->led_client); + mira050pmic_init_controls(mira050->pmic_client, mira050->uc_client); + } + + dev_err(dev, "[MIRA050] Sleep for 1 second to let PMIC driver complete init.\n"); + usleep_range(1000000, 1000000 + 100); + + /* + * The sensor must be powered for mira050_identify_module() + * to be able to read the CHIP_ID register + */ + ret = mira050_power_on(dev); + if (ret) + return ret; + + printk(KERN_INFO "[MIRA050]: Entering identify function.\n"); + + ret = mira050_identify_module(mira050); + if (ret) + goto error_power_off; + + printk(KERN_INFO "[MIRA050]: Setting support function.\n"); + + /* Initialize default illumination trigger parameters */ + /* ILLUM_WIDTH is in unit of SEQ_TIME_BASE, equal to (8/1000) us. */ + mira050->illum_width = MIRA050_ILLUM_WIDTH_DEFAULT; + /* ILLUM_DELAY is in unit of TIME_UNIT, equal to 1 us. In continuous stream mode, zero delay is 1<<19. */ + mira050->illum_delay = MIRA050_ILLUM_DELAY_DEFAULT; + mira050->illum_width_auto = MIRA050_ILLUM_WIDTH_AUTO_DEFAULT; + mira050->illum_enable = MIRA050_ILLUM_ENABLE_DEFAULT; + + /* Set default mode to max resolution */ + mira050->mode = &supported_modes[0]; + + printk(KERN_INFO "[MIRA050]: Entering init controls function.\n"); + + ret = mira050_init_controls(mira050); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + mira050->sd.internal_ops = &mira050_internal_ops; + mira050->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + mira050->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pads */ + mira050->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; + mira050->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; + + printk(KERN_INFO "[MIRA050]: Entering set default format function.\n"); + + /* Initialize default format */ + mira050_set_default_format(mira050); + + printk(KERN_INFO "[MIRA050]: Entering pads init function.\n"); + + ret = media_entity_pads_init(&mira050->sd.entity, NUM_PADS, mira050->pad); + if (ret) + { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + printk(KERN_INFO "[MIRA050]: Entering subdev sensor common function.\n"); + + ret = v4l2_async_register_subdev_sensor(&mira050->sd); + if (ret < 0) + { + dev_err(dev, "failed to register sensor sub-device: %d\n", ret); + goto error_media_entity; + } + + /* For debug purpose */ + // mira050_start_streaming(mira050); + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&mira050->sd.entity); + +error_handler_free: + mira050_free_controls(mira050); + +error_power_off: + mira050_power_off(dev); + + i2c_unregister_device(mira050->pmic_client); + i2c_unregister_device(mira050->uc_client); + i2c_unregister_device(mira050->led_client); + + return ret; +} + +static void mira050_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira050 *mira050 = to_mira050(sd); + + i2c_unregister_device(mira050->pmic_client); + i2c_unregister_device(mira050->uc_client); + i2c_unregister_device(mira050->led_client); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + mira050_free_controls(mira050); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + mira050_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct dev_pm_ops mira050_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mira050_suspend, mira050_resume) + SET_RUNTIME_PM_OPS(mira050_power_off, mira050_power_on, NULL)}; + +#endif // __MIRA050_INL__ diff --git a/drivers/media/i2c/mira050color.c b/drivers/media/i2c/mira050color.c new file mode 100644 index 00000000000000..42679c7ab73a14 --- /dev/null +++ b/drivers/media/i2c/mira050color.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ams MIRA050 cameras. + * Copyright (C) 2022, ams-OSRAM + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + */ + +#include "mira050.inl" + +static const struct of_device_id mira050_dt_ids[] = { + { .compatible = "ams,mira050color" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mira050_dt_ids); + +static const struct i2c_device_id mira050_ids[] = { + { "mira050color", 1 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mira050_ids); + +static struct i2c_driver mira050_i2c_driver = { + .driver = { + .name = "mira050color", + .of_match_table = mira050_dt_ids, + .pm = &mira050_pm_ops, + }, + + .probe = mira050_probe, + .remove = mira050_remove, + .id_table = mira050_ids, +}; + +module_i2c_driver(mira050_i2c_driver); + +MODULE_AUTHOR("Zhenyu Ye "); +MODULE_DESCRIPTION("ams MIRA050 sensor driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/i2c/mira130.c b/drivers/media/i2c/mira130.c new file mode 100644 index 00000000000000..20c4a6af6aa9f4 --- /dev/null +++ b/drivers/media/i2c/mira130.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ams MIRA130 cameras. + * Copyright (C) 2022, ams-OSRAM + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + */ + +#include "mira130.inl" + +static const struct of_device_id mira130_dt_ids[] = { + { .compatible = "ams,mira130" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mira130_dt_ids); + +static const struct i2c_device_id mira130_ids[] = { + { "mira130", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mira130_ids); + +static struct i2c_driver mira130_i2c_driver = { + .driver = { + .name = "mira130", + .of_match_table = mira130_dt_ids, + .pm = &mira130_pm_ops, + }, + .probe = mira130_probe, + .remove = mira130_remove, + .id_table = mira130_ids, +}; + +module_i2c_driver(mira130_i2c_driver); + +MODULE_AUTHOR("Zhenyu Ye "); +MODULE_DESCRIPTION("ams MIRA130 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/mira130.inl b/drivers/media/i2c/mira130.inl new file mode 100644 index 00000000000000..58e3c0a651a8e1 --- /dev/null +++ b/drivers/media/i2c/mira130.inl @@ -0,0 +1,2449 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ams MIRA130 cameras. + * Copyright (C) 2022, ams-OSRAM + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + */ + +#ifndef __MIRA130_INL__ +#define __MIRA130_INL__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Introduce new v4l2 control + */ +#include +#define AMS_CAMERA_CID_BASE (V4L2_CTRL_CLASS_CAMERA | 0x2000) +#define AMS_CAMERA_CID_MIRA_REG_W (AMS_CAMERA_CID_BASE+0) +#define AMS_CAMERA_CID_MIRA_REG_R (AMS_CAMERA_CID_BASE+1) + +/* Most significant Byte is flag, and most significant bit is unused. */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_FOR_READ 0b00000001 +/* Use bit 5 to indicate special command, bit 1,2,3,4 for command. */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_CMD_SEL 0b00010000 +/* Special command for sleep. The other 3 Bytes (addr+val) is sleep values in us. */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_SLEEP_US 0b00010000 +/* Special command to enable power on (/off) when stream on (/off). */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_RESET_ON 0b00010010 +/* Special command to disable power on (/off) when stream on (/off). */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_RESET_OFF 0b00010100 +/* Special command to enable base register sequence upload, overwrite skip-reg-upload in dtoverlay */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_REG_UP_ON 0b00010110 +/* Special command to disable base register sequence upload, overwrite skip-reg-upload in dtoverlay */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_REG_UP_OFF 0b00011000 +/* Special command to manually power on */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_POWER_ON 0b00011010 +/* Special command to manually power off */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_POWER_OFF 0b00011100 +/* Special command to turn illumination trigger on */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_ILLUM_TRIG_ON 0b00011110 +/* Special command to turn illumination trigger off */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_ILLUM_TRIG_OFF 0b00010001 +/* Special command to enable force_stream_ctrl */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_STREAM_CTRL_ON 0b00011011 +/* Special command to disable force_stream_ctrl */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_STREAM_CTRL_OFF 0b00011101 +/* + * Bit 6&7 of flag are combined to specify I2C dev (default is Mira). + * If bit 6&7 is 0b01, the reg_addr and reg_val are for a TBD I2C address. + * The TBD I2C address is default to MIRA130LED_I2C_ADDR. + * To change the TBD I2C address, set bit 6&7 to 0b10, + * then the reg_val will become TBD I2C address. + * The TBD I2C address is stored in mira130->tbd_client_i2c_addr. + */ +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_SEL 0b01100000 +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_MIRA 0b00000000 +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_TBD 0b00100000 +#define AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_SET_TBD 0b01000000 + + +/* Pre-allocated i2c_client */ +#define MIRA130PMIC_I2C_ADDR 0x2D +#define MIRA130UC_I2C_ADDR 0x0A +#define MIRA130LED_I2C_ADDR 0x53 + + +#define MIRA130_NATIVE_WIDTH 1080U +#define MIRA130_NATIVE_HEIGHT 1280U + +#define MIRA130_PIXEL_ARRAY_LEFT 0U +#define MIRA130_PIXEL_ARRAY_TOP 0U +#define MIRA130_PIXEL_ARRAY_WIDTH 1080U +#define MIRA130_PIXEL_ARRAY_HEIGHT 1280U + +#define MIRA130_BIT_DEPTH_REG 0x3031 +#define MIRA130_BIT_DEPTH_12_BIT 0x0C +#define MIRA130_BIT_DEPTH_10_BIT 0x0A +#define MIRA130_BIT_DEPTH_8_BIT 0x08 + +#define MIRA130_CSI_DATA_TYPE_REG 0x3037 +#define MIRA130_CSI_DATA_TYPE_12_BIT 0x02 +#define MIRA130_CSI_DATA_TYPE_10_BIT 0x01 +#define MIRA130_CSI_DATA_TYPE_8_BIT 0x00 + +#define MIRA130_IMAGER_STATE_REG 0x1003 +#define MIRA130_IMAGER_STATE_STOP_AT_ROW 0x02 +#define MIRA130_IMAGER_STATE_STOP_AT_FRAME 0x04 +#define MIRA130_IMAGER_STATE_MASTER_CONTROL 0x10 + +#define MIRA130_IMAGER_RUN_REG 0x10F0 +#define MIRA130_IMAGER_RUN_START 0x01 +#define MIRA130_IMAGER_RUN_STOP 0x00 + +#define MIRA130_IMAGER_RUN_CONT_REG 0x1002 +#define MIRA130_IMAGER_RUN_CONT_ENABLE 0x04 +#define MIRA130_IMAGER_RUN_CONT_DISABLE 0x00 + +#define MIRA130_NB_OF_FRAMES_LO_REG 0x10F2 +#define MIRA130_NB_OF_FRAMES_HI_REG 0x10F3 + +// Exposure time is indicated in number of rows +#define MIRA130_EXP_TIME_HI_REG 0x3E00 +#define MIRA130_EXP_TIME_LO_REG 0x3E02 + +#define MIRA130_AGC_MODE_REG 0x3E03 +#define MIRA130_ANA_GAIN_REG 0x3E08 +#define MIRA130_ANA_FINE_GAIN_REG 0x3E09 +#define MIRA130_HDR_ANA_GAIN_REG 0x3E12 +#define MIRA130_HDR_ANA_FINE_GAIN_REG 0x3E13 + +// VBLANK is indicated in number of rows +#define MIRA130_VBLANK_HI_REG 0x320E +#define MIRA130_VBLANK_LO_REG 0x320F + +// Sets the duration of the row length in clock cycles of CLK_IN +#define MIRA130_ROW_LENGTH_LO_REG 0x320D +#define MIRA130_ROW_LENGTH_HI_REG 0x320C +#define MIRA130_ROW_LENGTH_MIN 0x02EE + +#define MIRA130_HFLIP_REG 0x3221 +#define MIRA130_HFLIP_ENABLE_MIRROR 0b00000110 + +#define MIRA130_VFLIP_REG 0x3221 +#define MIRA130_VFLIP_ENABLE_FLIP 0b01100000 + +#define MIRA130_SUPPORTED_XCLK_FREQ 24000000 + +#define MIRA130_MIN_VBLANK 120 + +#define MIRA130_MIN_V_SIZE 1280 +#define MIRA130_DEFAULT_EXPOSURE (MIRA130_MIN_V_SIZE + MIRA130_MIN_VBLANK) +#define MIRA130_EXPOSURE_MIN 1 + +// Power on function timing +#define MIRA130_XCLR_MIN_DELAY_US 100000 +#define MIRA130_XCLR_DELAY_RANGE_US 30 + +// pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample +// 0.6Gb/s * 2 * 2 / 10 = 257698038 +#define MIRA130_PIXEL_RATE (257698037) +/* Should match device tree link freq */ +#define MIRA130_DEFAULT_LINK_FREQ 456000000 + +/* Trick the libcamera with achievable fps via hblank */ + +/* Formular in libcamera to derive TARGET_FPS: + * TARGET_FPS=1/((1/MIRA130_PIXEL_RATE)*(WIDTH+HBLANK)*(HEIGHT+MIRA130_MIN_VBLANK)) + * Example: 1080x1280 with HBLANK=0 and MIRA130_MIN_VBLANK=120 + * TARGET_FPS=1/((1/257698037)*1080*(1280+120))=170 + * + * Inverse the above formula to derive HBLANK from TARGET_FPS: + * HBLANK=1/((1/MIRA130_PIXEL_RATE)*TARGET_FPS*(HEIGHT+MIRA130_MIN_VBLANK))-WIDTH + * Example with TARGET_FPS of 60 fps for 1080x1280 + * HBLANK=1/((1/257698037)*60*(1280+120))-1080=1988 + */ +#define MIRA130_HBLANK_1080x1280_60FPS 1988 + +/* Set max VBLANK to be 2 fps */ +/* + * TARGET_FPS=1/((1/MIRA130_PIXEL_RATE)*(WIDTH+HBLANK)*(HEIGHT+VBLANK)) + * VBLANK=1/((1/MIRA130_PIXEL_RATE)*TARGET_FPS*(WIDTH+HBLANK))-HEIGHT + * Example with TARGET_FPS of 2 fps and HBLANK of 1988 + * VBLANK=1/((1/257698037)*2*(1080+1988))-1280=40717 + */ +#define MIRA130_VBLANK_1080x1280_2FPS 40717 +#define MIRA130_MAX_VBLANK MIRA130_VBLANK_1080x1280_2FPS + +#define MIRA130_REG_TEST_PATTERN 0x4501 +#define MIRA130_TEST_PATTERN_DISABLE 0x00 +#define MIRA130_TEST_PATTERN_VERTICAL_GRADIENT 0x01 + +/* Embedded metadata stream structure */ +#define MIRA130_EMBEDDED_LINE_WIDTH 16384 +#define MIRA130_NUM_EMBEDDED_LINES 1 + +/* From Jetson driver */ +#define MIRA130_DEFAULT_LINE_LENGTH (0x02EE) +#define MIRA130_DEFAULT_PIXEL_CLOCK (24) +#define MIRA130_DEFAULT_FRAME_LENGTH (0x0578) + +/* Illumination trigger */ +#define MIRA130_EN_TRIG_ILLUM_REG 0x3361 + +enum pad_types { + IMAGE_PAD, + METADATA_PAD, + NUM_PADS +}; + +struct mira130_reg { + u16 address; + u8 val; +}; + +struct mira130_analog_gain_lut { + u8 gain; + u8 fine_gain; +}; + + +struct mira130_reg_list { + unsigned int num_of_regs; + const struct mira130_reg *regs; +}; + +struct mira130_v4l2_reg { + u32 val; +}; + +/* Mode : resolution and related config&values */ +struct mira130_mode { + /* Frame width */ + unsigned int width; + /* Frame height */ + unsigned int height; + + /* Analog crop rectangle. */ + struct v4l2_rect crop; + + /* Default register values */ + struct mira130_reg_list reg_list; + + u32 row_length; + u32 vblank; + u32 hblank; + u32 code; +}; + +// 1080_1280_60fps_10b_2lanes +static const struct mira130_reg full_1080_1280_60fps_10b_2lanes_reg[] = { + {0x0103,0x01}, + {0x0100,0x00}, + {0x36e9,0x80}, + {0x36f9,0x80}, + {0x300a,0x64}, + {0x3018,0x32}, + {0x3019,0x0c}, + {0x301a,0xb4}, + {0x301f,0xbf}, + {0x3031,0x0a}, + {0x3032,0xa0}, + {0x3038,0x44}, + {0x3207,0x17}, + {0x320c,0x02}, + {0x320d,0xee}, + {0x320e,0x05}, + {0x320f,0x78}, + {0x3217,0x05}, + {0x3218,0x72}, + {0x3250,0xcc}, + {0x3251,0x02}, + {0x3252,0x05}, + {0x3253,0x73}, + {0x3254,0x05}, + {0x3255,0x3b}, + {0x3306,0x78}, + {0x330a,0x00}, + {0x330b,0xc8}, + {0x330f,0x24}, + {0x3314,0x80}, + {0x3315,0x40}, + {0x3317,0xf0}, + {0x331f,0x12}, + {0x3364,0x00}, + {0x3385,0x41}, + {0x3387,0x41}, + {0x3389,0x09}, + {0x33ab,0x00}, + {0x33ac,0x00}, + {0x33b1,0x03}, + {0x33b2,0x12}, + {0x33f8,0x02}, + {0x33fa,0x01}, + {0x3409,0x08}, + {0x34f0,0xc0}, + {0x34f1,0x20}, + {0x34f2,0x03}, + {0x3622,0xf5}, + {0x3630,0x5c}, + {0x3631,0x80}, + {0x3632,0xc8}, + {0x3633,0x32}, + {0x3638,0x2a}, + {0x3639,0x07}, + {0x363b,0x48}, + {0x363c,0x83}, + {0x363d,0x10}, + {0x36ea,0x36}, + {0x36eb,0x04}, + {0x36ec,0x03}, + {0x36ed,0x24}, + {0x36fa,0x2b}, + {0x36fb,0x0b}, + {0x36fc,0x01}, + {0x36fd,0x34}, + {0x3900,0x11}, + {0x3901,0x05}, + {0x3902,0xc5}, + {0x3904,0x04}, + {0x3908,0x91}, + {0x391e,0x00}, + {0x3e01,0x57}, + {0x3e02,0x00}, + {0x3e09,0x20}, + {0x3e0e,0xd2}, + {0x3e14,0xb0}, + {0x3e1e,0x7c}, + {0x3e26,0x20}, + {0x4418,0x38}, + {0x4503,0x10}, + {0x4800,0x24}, + {0x4837,0x1a}, + {0x5000,0x0e}, + {0x540c,0x51}, + {0x550f,0x38}, + {0x5780,0x67}, + {0x5784,0x10}, + {0x5785,0x06}, + {0x5787,0x02}, + {0x5788,0x00}, + {0x5789,0x00}, + {0x578a,0x02}, + {0x578b,0x00}, + {0x578c,0x00}, + {0x5790,0x00}, + {0x5791,0x00}, + {0x5792,0x00}, + {0x5793,0x00}, + {0x5794,0x00}, + {0x5795,0x00}, + {0x5799,0x04}, + {0x36e9,0x54}, + {0x36f9,0x50}, + {0x0100,0x01}, + {0x33fa,0x01}, + {0x3317,0xf0}, +}; + +static const struct mira130_analog_gain_lut analog_gain_lut[] = { + {0x03,0x20}, + {0x03,0x21}, + {0x03,0x22}, + {0x03,0x23}, + {0x03,0x24}, + {0x03,0x25}, + {0x03,0x26}, + {0x03,0x27}, + {0x03,0x28}, + {0x03,0x29}, + {0x03,0x2A}, + {0x03,0x2B}, + {0x03,0x2C}, + {0x03,0x2D}, + {0x03,0x2E}, + {0x03,0x2F}, + {0x03,0x30}, + {0x03,0x31}, + {0x03,0x32}, + {0x03,0x33}, + {0x03,0x34}, + {0x03,0x35}, + {0x03,0x36}, + {0x03,0x37}, + {0x03,0x38}, + {0x03,0x39}, + {0x23,0x20}, + {0x23,0x21}, + {0x23,0x22}, + {0x23,0x23}, + {0x23,0x24}, + {0x23,0x25}, + {0x23,0x26}, + {0x23,0x27}, + {0x23,0x28}, + {0x23,0x29}, + {0x23,0x2A}, + {0x23,0x2B}, + {0x23,0x2C}, + {0x23,0x2D}, + {0x23,0x2E}, + {0x23,0x2F}, + {0x23,0x30}, + {0x23,0x31}, + {0x23,0x32}, + {0x23,0x33}, + {0x23,0x34}, + {0x23,0x35}, + {0x23,0x36}, + {0x23,0x37}, + {0x23,0x38}, + {0x23,0x39}, + {0x23,0x3A}, + {0x23,0x3B}, + {0x23,0x3C}, + {0x23,0x3D}, + {0x23,0x3E}, + {0x23,0x3F}, + {0x27,0x20}, + {0x27,0x21}, + {0x27,0x22}, + {0x27,0x23}, + {0x27,0x24}, + {0x27,0x25}, + {0x27,0x26}, + {0x27,0x27}, + {0x27,0x28}, + {0x27,0x29}, + {0x27,0x2A}, + {0x27,0x2B}, + {0x27,0x2C}, + {0x27,0x2D}, + {0x27,0x2E}, + {0x27,0x2F}, + {0x27,0x30}, + {0x27,0x31}, + {0x27,0x32}, + {0x27,0x33}, + {0x27,0x34}, + {0x27,0x35}, + {0x27,0x36}, + {0x27,0x37}, + {0x27,0x38}, + {0x27,0x39}, + {0x27,0x3A}, + {0x27,0x3B}, + {0x27,0x3C}, + {0x27,0x3D}, + {0x27,0x3E}, + {0x27,0x3F}, + {0x2F,0x20}, + {0x2F,0x21}, + {0x2F,0x22}, + {0x2F,0x23}, + {0x2F,0x24}, + {0x2F,0x25}, + {0x2F,0x26}, + {0x2F,0x27}, + {0x2F,0x28}, + {0x2F,0x29}, + {0x2F,0x2A}, + {0x2F,0x2B}, + {0x2F,0x2C}, + {0x2F,0x2D}, + {0x2F,0x2E}, + {0x2F,0x2F}, + {0x2F,0x30}, + {0x2F,0x31}, + {0x2F,0x32}, + {0x2F,0x33}, + {0x2F,0x34}, + {0x2F,0x35}, + {0x2F,0x36}, + {0x2F,0x37}, + {0x2F,0x38}, + {0x2F,0x39}, + {0x2F,0x3A}, + {0x2F,0x3B}, + {0x2F,0x3C}, + {0x2F,0x3D}, + {0x2F,0x3E}, + {0x2F,0x3F}, + {0x3F,0x20}, + {0x3F,0x21}, + {0x3F,0x22}, + {0x3F,0x23}, + {0x3F,0x24}, + {0x3F,0x25}, + {0x3F,0x26}, + {0x3F,0x27}, + {0x3F,0x28}, + {0x3F,0x29}, + {0x3F,0x2A}, + {0x3F,0x2B}, + {0x3F,0x2C}, + {0x3F,0x2D}, + {0x3F,0x2E}, + {0x3F,0x2F}, + {0x3F,0x30}, + {0x3F,0x31}, + {0x3F,0x32}, + {0x3F,0x33}, + {0x3F,0x34}, + {0x3F,0x35}, + {0x3F,0x36}, + {0x3F,0x37}, + {0x3F,0x38}, + {0x3F,0x39}, + {0x3F,0x3A}, + {0x3F,0x3B}, + {0x3F,0x3C}, + {0x3F,0x3D}, + {0x3F,0x3E}, + {0x3F,0x3F}, +}; + +static const char * const mira130_test_pattern_menu[] = { + "Disabled", + "Vertial Gradient", +}; + +static const int mira130_test_pattern_val[] = { + MIRA130_TEST_PATTERN_DISABLE, + MIRA130_TEST_PATTERN_VERTICAL_GRADIENT, +}; + + +/* regulator supplies */ +static const char * const mira130_supply_name[] = { + // TODO(jalv): Check supply names + /* Supplies can be enabled in any order */ + "VANA", /* Analog (2.8V) supply */ + "VDIG", /* Digital Core (1.8V) supply */ + "VDDL", /* IF (1.2V) supply */ +}; + +#define MIRA130_NUM_SUPPLIES ARRAY_SIZE(mira130_supply_name) + +/* + * The supported formats. All flip/mirror combinations have the same byte order because the sensor + * is monochrome + */ +static const u32 codes[] = { + MEDIA_BUS_FMT_SGRBG10_1X10, +}; + +/* Mode configs */ +static const struct mira130_mode supported_modes[] = { + /* 60fps 10bpp mode */ + { + .width = 1080, + .height = 1280, + .crop = { + .left = MIRA130_PIXEL_ARRAY_LEFT, + .top = MIRA130_PIXEL_ARRAY_TOP, + .width = 1080, + .height = 1280 + }, + .reg_list = { + .num_of_regs = ARRAY_SIZE(full_1080_1280_60fps_10b_2lanes_reg), + .regs = full_1080_1280_60fps_10b_2lanes_reg, + }, + // ROW_LENGTH is configured by register 0x320C, 0x320D. + .row_length = MIRA130_ROW_LENGTH_MIN, + .vblank = MIRA130_MIN_VBLANK, + .hblank = MIRA130_HBLANK_1080x1280_60FPS, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + }, +}; + +struct mira130 { + struct v4l2_subdev sd; + struct media_pad pad[NUM_PADS]; + + struct v4l2_mbus_framefmt fmt; + + struct clk *xclk; /* system clock to MIRA130 */ + u32 xclk_freq; + + //struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[MIRA130_NUM_SUPPLIES]; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + // custom v4l2 control + struct v4l2_ctrl *mira130_reg_w; + struct v4l2_ctrl *mira130_reg_r; + u16 mira130_reg_w_cached_addr; + u8 mira130_reg_w_cached_flag; + + + /* Current mode */ + const struct mira130_mode *mode; + /* Whether to skip base register sequence upload */ + u32 skip_reg_upload; + /* Whether to reset sensor when stream on/off */ + u32 skip_reset; + /* Whether regulator and clk are powered on */ + u32 powered; + /* A flag to force write_start/stop_streaming_regs even if (skip_reg_upload==1) */ + u8 force_stream_ctrl; + + + /* + * Mutex for serialized access: + * Protect sensor module set pad format and start/stop streaming safely. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; + + /* pmic, uC, LED */ + struct i2c_client *pmic_client; + struct i2c_client *uc_client; + struct i2c_client *led_client; + /* User specified I2C device address */ + u32 tbd_client_i2c_addr; + +}; + +static inline struct mira130 *to_mira130(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct mira130, sd); +} + +static int mira130_read(struct mira130 *mira130, u16 reg, u8 *val) +{ + int ret; + unsigned char data_w[2] = { reg >> 8, reg & 0xff }; + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + + 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; +} + +static int mira130_write(struct mira130 *mira130, u16 reg, u8 val) +{ + int ret; + unsigned char data[3] = { reg >> 8, reg & 0xff, val}; + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + + 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; +} + +/* + * mira130 is big-endian: msb of val goes to lower reg addr + */ +static int mira130_write16(struct mira130 *mira130, u16 reg, u16 val) +{ + int ret; + unsigned char data[4] = { reg >> 8, reg & 0xff, (val >> 8) & 0xff, val & 0xff }; + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + + ret = i2c_master_send(client, data, 4); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 4) { + ret = 0; + } else { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + } + + return ret; +} + +/* + * mira130 is big-endian: msb of val goes to lower reg addr + */ +static int mira130_write24(struct mira130 *mira130, u16 reg, u32 val) +{ + int ret; + unsigned char data[5] = { reg >> 8, reg & 0xff, (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff }; + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + + ret = i2c_master_send(client, data, 5); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 5) { + ret = 0; + } else { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + } + + return ret; +} + + +/* Write a list of registers */ +static int mira130_write_regs(struct mira130 *mira130, + const struct mira130_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = mira130_write(mira130, 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; + } else { + // Debug code below + //u8 val; + //ret = mira130_read(mira130, regs[i].address, &val); + //printk(KERN_INFO "[MIRA130]: Read reg 0x%4.4x, val = 0x%x.\n", + // regs[i].address, val); + } + } + + return 0; +} + +static int mira130pmic_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret; + unsigned char data[2] = { reg & 0xff, val}; + + ret = i2c_master_send(client, data, 2); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 2) { + 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 mira130pmic_read(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msgs[2]; + u8 addr_buf[1] = { reg & 0xff }; + u8 data_buf[1] = { 0 }; + int ret; + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = &data_buf[0]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = (u8)(data_buf[0]); + + return 0; +} + +/* Power/clock management functions */ +static int mira130_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira130 *mira130 = to_mira130(sd); + int ret = -EINVAL; + + printk(KERN_INFO "[MIRA130]: Entering power on function.\n"); + + if (mira130->powered == 0) { + ret = regulator_bulk_enable(MIRA130_NUM_SUPPLIES, mira130->supplies); + if (ret) { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + ret = clk_prepare_enable(mira130->xclk); + if (ret) { + dev_err(&client->dev, "%s: failed to enable clock\n", + __func__); + goto reg_off; + } + usleep_range(MIRA130_XCLR_MIN_DELAY_US, + MIRA130_XCLR_MIN_DELAY_US + MIRA130_XCLR_DELAY_RANGE_US); + mira130->powered = 1; + } else { + printk(KERN_INFO "[MIRA130]: Skip regulator and clk enable, because mira130->powered == %d.\n", mira130->powered); + } + return 0; + +reg_off: + ret = regulator_bulk_disable(MIRA130_NUM_SUPPLIES, mira130->supplies); + mira130->powered = 0; + return ret; +} + +static int mira130_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira130 *mira130 = to_mira130(sd); + + printk(KERN_INFO "[MIRA130]: Entering power off function.\n"); + + if (mira130->skip_reset == 0) { + if (mira130->powered == 1) { + regulator_bulk_disable(MIRA130_NUM_SUPPLIES, mira130->supplies); + clk_disable_unprepare(mira130->xclk); + mira130->powered = 0; + } else { + printk(KERN_INFO "[MIRA130]: Skip disabling regulator and clk due to mira130->powered == %d.\n", mira130->powered); + } + } else { + printk(KERN_INFO "[MIRA130]: Skip disabling regulator and clk due to mira130->skip_reset=%u.\n", mira130->skip_reset); + } + + return 0; +} + +static int mira130_write_illum_trig_regs(struct mira130* mira130, u8 enable) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira130->sd); + int ret = 0; + u8 enable_reg; + + // Enable or disable illumination trigger + /* Enable: set bit [7:6] to 0b00. Disable: set bit [7:0] to 0b11. */ + if (enable != 0) { + enable_reg = 0b00000000; + } else { + enable_reg = 0b11000000; + } + printk(KERN_INFO "[MIRA130]: Writing EN_TRIG_ILLUM to %d.\n", enable_reg); + ret = mira130_write(mira130, MIRA130_EN_TRIG_ILLUM_REG, enable_reg); + if (ret) { + dev_err(&client->dev, "Error setting EN_TRIG_ILLUM to %d.", enable_reg); + return ret; + } + + return ret; +} + + + +static int mira130_write_start_streaming_regs(struct mira130* mira130) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira130->sd); + int ret = 0; + + (void)client; + // TODO: Check any register write is needed. + + return ret; +} + +static int mira130_write_stop_streaming_regs(struct mira130* mira130) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira130->sd); + int ret = 0; + u32 frame_time; + int try_cnt; + + (void)client; + (void)try_cnt; + + // TODO: check any register write is needed. + + /* + * Wait for one frame to make sure sensor is set to + * software standby in V-blank + * + * frame_time = frame length rows * Tline + * Tline = line length / pixel clock (in MHz) + */ + frame_time = MIRA130_DEFAULT_FRAME_LENGTH * + MIRA130_DEFAULT_LINE_LENGTH / MIRA130_DEFAULT_PIXEL_CLOCK; + + usleep_range(frame_time, frame_time + 1000); + + return ret; +} + + +static int mira130_v4l2_reg_w(struct mira130 *mira130, u32 value) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira130->sd); + u32 ret = 0; + u32 tmp_flag; + + u16 reg_addr = (value >> 8) & 0xFFFF; + u8 reg_val = value & 0xFF; + u8 reg_flag = (value >> 24) & 0xFF; + + // printk(KERN_INFO "[MIRA130]: %s reg_flag: 0x%02X; reg_addr: 0x%04X; reg_val: 0x%02X.\n", + // __func__, reg_flag, reg_addr, reg_val); + + if (reg_flag & AMS_CAMERA_CID_MIRA130_REG_FLAG_CMD_SEL) { + if (reg_flag == AMS_CAMERA_CID_MIRA130_REG_FLAG_SLEEP_US) { + // If it is for sleep, combine all 24 bits of reg_addr and reg_val as sleep us. + u32 sleep_us_val = value & 0x00FFFFFF; + // Sleep range needs an interval, default to 1/8 of the sleep value. + u32 sleep_us_interval = sleep_us_val >> 3; + printk(KERN_INFO "[MIRA130]: %s sleep_us: %u.\n", __func__, sleep_us_val); + usleep_range(sleep_us_val, sleep_us_val + sleep_us_interval); + } else if (reg_flag == AMS_CAMERA_CID_MIRA130_REG_FLAG_RESET_ON) { + printk(KERN_INFO "[MIRA130]: %s Enable reset at stream on/off.\n", __func__); + mira130->skip_reset = 0; + } else if (reg_flag == AMS_CAMERA_CID_MIRA130_REG_FLAG_RESET_OFF) { + printk(KERN_INFO "[MIRA130]: %s Disable reset at stream on/off.\n", __func__); + mira130->skip_reset = 1; + } else if (reg_flag == AMS_CAMERA_CID_MIRA130_REG_FLAG_REG_UP_ON) { + printk(KERN_INFO "[MIRA130]: %s Enable base register sequence upload.\n", __func__); + mira130->skip_reg_upload = 0; + } else if (reg_flag == AMS_CAMERA_CID_MIRA130_REG_FLAG_REG_UP_OFF) { + printk(KERN_INFO "[MIRA130]: %s Disable base register sequence upload.\n", __func__); + mira130->skip_reg_upload = 1; + } else if (reg_flag == AMS_CAMERA_CID_MIRA130_REG_FLAG_POWER_ON) { + printk(KERN_INFO "[MIRA130]: %s Call power on function mira130_power_on().\n", __func__); + /* Temporarily disable skip_reset if manually doing power on/off */ + tmp_flag = mira130->skip_reset; + mira130->skip_reset = 0; + mira130_power_on(&client->dev); + mira130->skip_reset = tmp_flag; + } else if (reg_flag == AMS_CAMERA_CID_MIRA130_REG_FLAG_POWER_OFF) { + printk(KERN_INFO "[MIRA130]: %s Call power off function mira130_power_off().\n", __func__); + /* Temporarily disable skip_reset if manually doing power on/off */ + tmp_flag = mira130->skip_reset; + mira130->skip_reset = 0; + mira130_power_off(&client->dev); + mira130->skip_reset = tmp_flag; + } else if (reg_flag == AMS_CAMERA_CID_MIRA130_REG_FLAG_ILLUM_TRIG_ON) { + printk(KERN_INFO "[MIRA130]: %s Enable illumination trigger.\n", __func__); + mira130_write_illum_trig_regs(mira130, 1); + } else if (reg_flag == AMS_CAMERA_CID_MIRA130_REG_FLAG_ILLUM_TRIG_OFF) { + printk(KERN_INFO "[MIRA130]: %s Disable illumination trigger.\n", __func__); + mira130_write_illum_trig_regs(mira130, 0); + } else if (reg_flag == AMS_CAMERA_CID_MIRA130_REG_FLAG_STREAM_CTRL_ON) { + printk(KERN_INFO "[MIRA130]: %s Force stream control even if (skip_reg_upload == 1).\n", __func__); + mira130->force_stream_ctrl = 1; + } else if (reg_flag == AMS_CAMERA_CID_MIRA130_REG_FLAG_STREAM_CTRL_OFF) { + printk(KERN_INFO "[MIRA130]: %s Disable stream control if (skip_reg_upload == 1).\n", __func__); + mira130->force_stream_ctrl = 0; + } else { + printk(KERN_INFO "[MIRA130]: %s unknown command from flag %u, ignored.\n", __func__, reg_flag); + } + } else if (reg_flag & AMS_CAMERA_CID_MIRA130_REG_FLAG_FOR_READ) { + // If it is for read, skip reagister write, cache addr and flag for read. + mira130->mira130_reg_w_cached_addr = reg_addr; + mira130->mira130_reg_w_cached_flag = reg_flag; + } else { + // If it is for write, select which I2C device by the flag "I2C_SEL". + if ((reg_flag & AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_MIRA) { + // Writing the actual Mira130 register + // printk(KERN_INFO "[MIRA130]: %s write reg_addr: 0x%04X; reg_val: 0x%02X.\n", __func__, reg_addr, reg_val); + ret = mira130_write(mira130, reg_addr, reg_val); + if (ret) { + dev_err_ratelimited(&client->dev, "Error AMS_CAMERA_CID_MIRA_REG_W reg_addr %X.\n", reg_addr); + return -EINVAL; + } + } else if ((reg_flag & AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_SET_TBD) { + /* User tries to set TBD I2C address, store reg_val to mira130->tbd_client_i2c_addr. Skip write. */ + printk(KERN_INFO "[MIRA130]: mira130->tbd_client_i2c_addr = 0x%X.\n", reg_val); + mira130->tbd_client_i2c_addr = reg_val; + } else if ((reg_flag & AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_TBD) { + if (mira130->tbd_client_i2c_addr == MIRA130PMIC_I2C_ADDR) { + // Write PMIC. Use pre-allocated mira130->pmic_client. + printk(KERN_INFO "[MIRA130]: write pmic_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira130pmic_write(mira130->pmic_client, (u8)(reg_addr & 0xFF), reg_val); + } else if (mira130->tbd_client_i2c_addr == MIRA130UC_I2C_ADDR) { + // Write micro-controller. Use pre-allocated mira130->uc_client. + printk(KERN_INFO "[MIRA130]: write uc_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira130pmic_write(mira130->uc_client, (u8)(reg_addr & 0xFF), reg_val); + } else if (mira130->tbd_client_i2c_addr == MIRA130LED_I2C_ADDR) { + // Write LED driver. Use pre-allocated mira130->led_client. + printk(KERN_INFO "[MIRA130]: write led_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira130pmic_write(mira130->led_client, (u8)(reg_addr & 0xFF), reg_val); + } else { + /* Write other TBD I2C address. + * The TBD I2C address is set via AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_SET_TBD. + * The TBD I2C address is stored in mira130->tbd_client_i2c_addr. + * A temporary I2C client, tmp_client, is created and then destroyed (unregistered). + */ + struct i2c_client *tmp_client; + tmp_client = i2c_new_dummy_device(client->adapter, mira130->tbd_client_i2c_addr); + if (IS_ERR(tmp_client)) + return PTR_ERR(tmp_client); + printk(KERN_INFO "[MIRA130]: write tbd_client, i2c_addr %u, reg_addr 0x%X, reg_val 0x%X.\n", + mira130->tbd_client_i2c_addr, (u8)(reg_addr & 0xFF), reg_val); + ret = mira130pmic_write(tmp_client, (u8)(reg_addr & 0xFF), reg_val); + i2c_unregister_device(tmp_client); + } + } + } + + return 0; +} + +static int mira130_v4l2_reg_r(struct mira130 *mira130, u32 *value) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira130->sd); + u32 ret = 0; + + u16 reg_addr = mira130->mira130_reg_w_cached_addr; + u8 reg_flag = mira130->mira130_reg_w_cached_flag; + u8 reg_val = 0; + + *value = 0; + + if ((reg_flag & AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_MIRA) { + ret = mira130_read(mira130, reg_addr, ®_val); + if (ret) { + dev_err_ratelimited(&client->dev, "Error AMS_CAMERA_CID_MIRA_REG_R reg_addr %X.\n", reg_addr); + return -EINVAL; + } + } else if ((reg_flag & AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_TBD) { + if (mira130->tbd_client_i2c_addr == MIRA130PMIC_I2C_ADDR) { + // Read PMIC. Use pre-allocated mira130->pmic_client. + ret = mira130pmic_read(mira130->pmic_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA130]: read pmic_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } else if (mira130->tbd_client_i2c_addr == MIRA130UC_I2C_ADDR) { + // Read micro-controller. Use pre-allocated mira130->uc_client. + ret = mira130pmic_read(mira130->uc_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA130]: read uc_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } else if (mira130->tbd_client_i2c_addr == MIRA130LED_I2C_ADDR) { + // Read LED driver. Use pre-allocated mira130->led_client. + ret = mira130pmic_read(mira130->led_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA130]: read led_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } else { + /* Read other TBD I2C address. + * The TBD I2C address is set via AMS_CAMERA_CID_MIRA130_REG_FLAG_I2C_SET_TBD. + * The TBD I2C address is stored in mira130->tbd_client_i2c_addr. + * A temporary I2C client, tmp_client, is created and then destroyed (unregistered). + */ + struct i2c_client *tmp_client; + tmp_client = i2c_new_dummy_device(client->adapter, mira130->tbd_client_i2c_addr); + if (IS_ERR(tmp_client)) + return PTR_ERR(tmp_client); + ret = mira130pmic_read(tmp_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA130]: read tbd_client, i2c_addr %u, reg_addr 0x%X, reg_val 0x%X.\n", + mira130->tbd_client_i2c_addr, (u8)(reg_addr & 0xFF), reg_val); + i2c_unregister_device(tmp_client); + } + } + + // Return 32-bit value that includes flags, addr, and register value + *value = ((u32)reg_flag << 24) | ((u32)reg_addr << 8) | (u32)reg_val; + + // printk(KERN_INFO "[MIRA130]: mira130_v4l2_reg_r() reg_flag: 0x%02X; reg_addr: 0x%04X, reg_val: 0x%02X.\n", + // reg_flag, reg_addr, reg_val); + + return 0; +} + +// Returns the maximum exposure time in row_length (reg value). +static u32 mira130_calculate_max_exposure_time(u32 row_length, u32 vsize, + u32 vblank) { + return (vsize + vblank); +} + +static int mira130_write_analog_gain_reg(struct mira130 *mira130, u8 gain) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira130->sd); + u32 ret = 0; + + if (gain < ARRAY_SIZE(analog_gain_lut)) { + u8 lut_gain = analog_gain_lut[gain].gain; + u8 lut_fine_gain = analog_gain_lut[gain].fine_gain; + ret |= mira130_write(mira130, MIRA130_AGC_MODE_REG, 0x0B); + ret |= mira130_write(mira130, MIRA130_ANA_GAIN_REG, lut_gain); + ret |= mira130_write(mira130, MIRA130_ANA_FINE_GAIN_REG, lut_fine_gain); + ret |= mira130_write(mira130, MIRA130_HDR_ANA_GAIN_REG, lut_gain); + ret |= mira130_write(mira130, MIRA130_HDR_ANA_FINE_GAIN_REG, lut_fine_gain); + } + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + } + return 0; +} + +static int mira130_write_exposure_reg(struct mira130 *mira130, u32 exposure) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira130->sd); + const u32 max_exposure = mira130_calculate_max_exposure_time(mira130->mode->row_length, + mira130->mode->height, mira130->mode->vblank); + u32 ret = 0; + u32 capped_exposure = exposure; + + if (exposure > max_exposure) { + capped_exposure = max_exposure; + } + + // Mira130 exposure time register is in the unit of 1/16 line + ret = mira130_write24(mira130, MIRA130_EXP_TIME_HI_REG, capped_exposure << 4); + if (ret) { + dev_err_ratelimited(&client->dev, "Error setting exposure time to %d", capped_exposure); + return -EINVAL; + } + + return 0; +} + +// Gets the format code if supported. Otherwise returns the default format code `codes[0]` +static u32 mira130_validate_format_code_or_default(struct mira130 *mira130, u32 code) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + unsigned int i; + + lockdep_assert_held(&mira130->mutex); + + for (i = 0; i < ARRAY_SIZE(codes); i++) + if (codes[i] == code) + break; + + if (i >= ARRAY_SIZE(codes)) { + dev_err_ratelimited(&client->dev, "Could not set requested format code %u", code); + dev_err_ratelimited(&client->dev, "Using default format %u", codes[0]); + i = 0; + } + + return codes[i]; +} + +static void mira130_set_default_format(struct mira130 *mira130) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = &mira130->fmt; + fmt->code = supported_modes[0].code; + 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); + fmt->width = supported_modes[0].width; + fmt->height = supported_modes[0].height; + fmt->field = V4L2_FIELD_NONE; +} + +static int mira130_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct mira130 *mira130 = to_mira130(sd); + struct v4l2_mbus_framefmt *try_fmt_img = + v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); + struct v4l2_mbus_framefmt *try_fmt_meta = + v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); + struct v4l2_rect *try_crop; + + mutex_lock(&mira130->mutex); + + /* Initialize try_fmt for the image pad */ + try_fmt_img->width = supported_modes[0].width; + try_fmt_img->height = supported_modes[0].height; + try_fmt_img->code = mira130_validate_format_code_or_default(mira130, + supported_modes[0].code); + try_fmt_img->field = V4L2_FIELD_NONE; + + /* TODO(jalv): Initialize try_fmt for the embedded metadata pad */ + try_fmt_meta->width = MIRA130_EMBEDDED_LINE_WIDTH; + try_fmt_meta->height = MIRA130_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->state, 0); + try_crop->top = supported_modes[0].crop.top; + try_crop->left = supported_modes[0].crop.left; + try_crop->width = supported_modes[0].crop.width; + try_crop->height = supported_modes[0].crop.height; + + mutex_unlock(&mira130->mutex); + + return 0; +} + +static int mira130_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira130 *mira130 = + container_of(ctrl->handler, struct mira130, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + int ret = 0; + u8 val; + + if (ctrl->id == V4L2_CID_VBLANK) { + int exposure_max, exposure_def; + + /* Update max exposure while meeting expected vblanking */ + exposure_max = mira130_calculate_max_exposure_time(mira130->mode->row_length, + mira130->mode->height, ctrl->val); + exposure_def = (exposure_max < MIRA130_DEFAULT_EXPOSURE) ? + exposure_max : MIRA130_DEFAULT_EXPOSURE; + __v4l2_ctrl_modify_range(mira130->exposure, + mira130->exposure->minimum, + exposure_max, mira130->exposure->step, + exposure_def); + } + + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (pm_runtime_get_if_in_use(&client->dev) == 0) { + dev_info(&client->dev, + "device in use, ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + return 0; + } + + if (mira130->skip_reg_upload == 0) { + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = mira130_write_analog_gain_reg(mira130, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret = mira130_write_exposure_reg(mira130, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = mira130_write(mira130, MIRA130_REG_TEST_PATTERN, + mira130_test_pattern_val[ctrl->val]); + break; + case V4L2_CID_HFLIP: + if (ctrl->val != 0) { + val = mira130_read(mira130, MIRA130_HFLIP_REG, &val); + val = val | MIRA130_HFLIP_ENABLE_MIRROR; + ret = mira130_write(mira130, MIRA130_HFLIP_REG, + val); + } else { + val = mira130_read(mira130, MIRA130_HFLIP_REG, &val); + val = val & (~ MIRA130_HFLIP_ENABLE_MIRROR); + ret = mira130_write(mira130, MIRA130_HFLIP_REG, + val); + } + break; + case V4L2_CID_VFLIP: + if (ctrl->val != 0) { + val = mira130_read(mira130, MIRA130_VFLIP_REG, &val); + val = val | MIRA130_VFLIP_ENABLE_FLIP; + ret = mira130_write(mira130, MIRA130_VFLIP_REG, + val); + } else { + val = mira130_read(mira130, MIRA130_VFLIP_REG, &val); + val = val & (~ MIRA130_VFLIP_ENABLE_FLIP); + ret = mira130_write(mira130, MIRA130_VFLIP_REG, + val); + } + break; + case V4L2_CID_VBLANK: + ret = mira130_write16(mira130, MIRA130_VBLANK_HI_REG, + ctrl->val + mira130->mode->height); + break; + case V4L2_CID_HBLANK: + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + } + + pm_runtime_put(&client->dev); + + // TODO: FIXIT + return ret; +} + +static int mira130_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira130 *mira130 = + container_of(ctrl->handler, struct mira130, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + int ret = 0; + + // printk(KERN_INFO "[MIRA130]: mira130_s_ctrl() id: %X value: %X.\n", ctrl->id, ctrl->val); + + /* Previously, register writes when powered off will be buffered. + * The buffer will be written to sensor when start_streaming. + * Now, register writes happens immediately, even powered off. + * Register writes when powered off will fail. + * Users need to make sure first power on then write register. + */ + + switch (ctrl->id) { + case AMS_CAMERA_CID_MIRA_REG_W: + ret = mira130_v4l2_reg_w(mira130, ctrl->val); + break; + default: + dev_info(&client->dev, + "set ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + + // TODO: FIXIT + return ret; +} + +static int mira130_g_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira130 *mira130 = + container_of(ctrl->handler, struct mira130, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + int ret = 0; + + // printk(KERN_INFO "[MIRA130]: mira130_g_ctrl() id: %X.\n", ctrl->id); + + /* + * Ideally, V4L2 register read should happen only when powered on. + * However, perhaps there are use cases that, + * reading other I2C addr is desired when mira sensor is powered off. + * Therefore, the check of "powered" flag is disabled for now. + */ + + switch (ctrl->id) { + case AMS_CAMERA_CID_MIRA_REG_R: + ret = mira130_v4l2_reg_r(mira130, (u32 *)&ctrl->cur.val); + ctrl->val = ctrl->cur.val; + break; + default: + dev_info(&client->dev, + "get ctrl(id:0x%x) is not handled\n", + ctrl->id); + ret = -EINVAL; + break; + } + + // TODO: FIXIT + return ret; +} + + +static const struct v4l2_ctrl_ops mira130_ctrl_ops = { + .s_ctrl = mira130_set_ctrl, +}; + +static const struct v4l2_ctrl_ops mira130_custom_ctrl_ops = { + .g_volatile_ctrl = mira130_g_ctrl, + .s_ctrl = mira130_s_ctrl, +}; + + +/* list of custom v4l2 ctls */ +static struct v4l2_ctrl_config custom_ctrl_config_list[] = { + /* Do not change the name field for the controls! */ + { + .ops = &mira130_custom_ctrl_ops, + .id = AMS_CAMERA_CID_MIRA_REG_W, + .name = "mira_reg_w", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = 0, + .min = 0, + .max = 0x7FFFFFFF, + .def = 0, + .step = 1, + }, + { + .ops = &mira130_custom_ctrl_ops, + .id = AMS_CAMERA_CID_MIRA_REG_R, + .name = "mira_reg_r", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = 0, + .min = 0, + .max = 0x7FFFFFFF, + .def = 0, + .step = 1, + }, + +}; + + +// This function should enumerate all the media bus formats for the requested pads. If the requested +// format index is beyond the number of avaialble formats it shall return -EINVAL; +static int mira130_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mira130 *mira130 = to_mira130(sd); + + if (code->pad >= NUM_PADS) + return -EINVAL; + + if (code->pad == IMAGE_PAD) { + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = mira130_validate_format_code_or_default(mira130, + codes[code->index]); + } else { + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SENSOR_DATA; + } + + return 0; +} + +static int mira130_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct mira130 *mira130 = to_mira130(sd); + + if (fse->pad >= NUM_PADS) + return -EINVAL; + + if (fse->pad == IMAGE_PAD) { + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != mira130_validate_format_code_or_default(mira130, fse->code)) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + } else { + if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) + return -EINVAL; + + fse->min_width = MIRA130_EMBEDDED_LINE_WIDTH; + fse->max_width = fse->min_width; + fse->min_height = MIRA130_NUM_EMBEDDED_LINES; + fse->max_height = fse->min_height; + } + + return 0; +} + +static void mira130_reset_colorspace(struct v4l2_mbus_framefmt *fmt) +{ + 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); +} + +static void mira130_update_image_pad_format(struct mira130 *mira130, + const struct mira130_mode *mode, + struct v4l2_subdev_format *fmt) +{ + if (mode != NULL) { + printk(KERN_INFO "[MIRA130]: mira130_update_image_pad_format() width %d, height %d.\n", + mode->width, mode->height); + } else { + printk(KERN_ERR "[MIRA130]: mira130_update_image_pad_format() mode is NULL.\n"); + } + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + mira130_reset_colorspace(&fmt->format); +} + +static void mira130_update_metadata_pad_format(struct v4l2_subdev_format *fmt) +{ + fmt->format.width = MIRA130_EMBEDDED_LINE_WIDTH; + fmt->format.height = MIRA130_NUM_EMBEDDED_LINES; + fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format.field = V4L2_FIELD_NONE; + +} + +static int __mira130_get_pad_format(struct mira130 *mira130, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + if (fmt->pad >= NUM_PADS) + return -EINVAL; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(&mira130->sd, sd_state, fmt->pad); + + try_fmt->code = fmt->pad == IMAGE_PAD ? + mira130_validate_format_code_or_default(mira130, try_fmt->code) : + MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format = *try_fmt; + } else { + if (fmt->pad == IMAGE_PAD) { + mira130_update_image_pad_format(mira130, mira130->mode, + fmt); + fmt->format.code = mira130_validate_format_code_or_default(mira130, + mira130->fmt.code); + } else { + mira130_update_metadata_pad_format(fmt); + } + } + + return 0; +} + +static int mira130_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mira130 *mira130 = to_mira130(sd); + int ret; + + mutex_lock(&mira130->mutex); + ret = __mira130_get_pad_format(mira130, sd_state, fmt); + mutex_unlock(&mira130->mutex); + + return ret; +} + +static int mira130_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mira130 *mira130 = to_mira130(sd); + const struct mira130_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + u32 max_exposure = 0, default_exp = 0; + + if (fmt->pad >= NUM_PADS) + return -EINVAL; + + mutex_lock(&mira130->mutex); + + if (fmt->pad == IMAGE_PAD) { + /* Validate format or use default */ + fmt->format.code = mira130_validate_format_code_or_default(mira130, + fmt->format.code); + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, + fmt->format.height); + mira130_update_image_pad_format(mira130, mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + printk(KERN_INFO "[MIRA130]: mira130_set_pad_format() use try_format.\n"); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); + *framefmt = fmt->format; + } else if (mira130->mode != mode || + mira130->fmt.code != fmt->format.code) { + + printk(KERN_INFO "[MIRA130]: mira130_set_pad_format() use new mode.\n"); + printk(KERN_INFO "[MIRA130]: mira130->mode %p mode %p.\n", (void *)mira130->mode, (void *)mode); + printk(KERN_INFO "[MIRA130]: mira130->fmt.code 0x%x fmt->format.code 0x%x.\n", mira130->fmt.code, fmt->format.code); + + mira130->fmt = fmt->format; + mira130->mode = mode; + + // Update controls based on new mode (range and current value). + max_exposure = mira130_calculate_max_exposure_time(mira130->mode->row_length, + mira130->mode->height, + mira130->mode->vblank); + default_exp = MIRA130_DEFAULT_EXPOSURE > max_exposure ? max_exposure : MIRA130_DEFAULT_EXPOSURE; + printk(KERN_INFO "[MIRA130]: mira130_set_pad_format() min_exp %d max_exp %d, default_exp %d\n", + MIRA130_EXPOSURE_MIN, max_exposure, default_exp); + __v4l2_ctrl_modify_range(mira130->exposure, + MIRA130_EXPOSURE_MIN, + max_exposure, 1, + default_exp); + + // Set the current vblank value + printk(KERN_INFO "[MIRA130]: mira130_set_pad_format() mira130->mode->vblank %d\n", + mira130->mode->vblank); + + __v4l2_ctrl_s_ctrl(mira130->vblank, mira130->mode->vblank); + } + } else { + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); + *framefmt = fmt->format; + } else { + /* Only one embedded data mode is supported */ + mira130_update_metadata_pad_format(fmt); + } + } + + printk(KERN_INFO "[MIRA130]: mira130_set_pad_format() to unlock and return.\n"); + + mutex_unlock(&mira130->mutex); + + return 0; +} + +static int mira130_set_framefmt(struct mira130 *mira130) +{ + if (mira130->skip_reg_upload == 0) { + switch (mira130->fmt.code) { + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + printk(KERN_INFO "[MIRA130]: mira130_set_framefmt() write 10 bpp regs.\n"); + mira130_write(mira130, MIRA130_BIT_DEPTH_REG,MIRA130_BIT_DEPTH_10_BIT); + mira130_write(mira130, MIRA130_CSI_DATA_TYPE_REG, + MIRA130_CSI_DATA_TYPE_10_BIT); + return 0; + default: + printk(KERN_ERR "Unknown format requested %d", mira130->fmt.code); + } + } + + return -EINVAL; +} + +static const struct v4l2_rect * +__mira130_get_pad_crop(struct mira130 *mira130, struct v4l2_subdev_state *sd_state, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&mira130->sd, sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mira130->mode->crop; + } + + return NULL; +} + +static int mira130_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct mira130 *mira130 = to_mira130(sd); + + mutex_lock(&mira130->mutex); + sel->r = *__mira130_get_pad_crop(mira130, sd_state, sel->pad, + sel->which); + mutex_unlock(&mira130->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = MIRA130_NATIVE_WIDTH; + sel->r.height = MIRA130_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = MIRA130_PIXEL_ARRAY_TOP; + sel->r.left = MIRA130_PIXEL_ARRAY_LEFT; + sel->r.width = MIRA130_PIXEL_ARRAY_WIDTH; + sel->r.height = MIRA130_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + +static int mira130_start_streaming(struct mira130 *mira130) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + const struct mira130_reg_list *reg_list; + int ret; + + printk(KERN_INFO "[MIRA130]: Entering start streaming function.\n"); + + /* Follow examples of other camera driver, here use pm_runtime_resume_and_get */ + ret = pm_runtime_resume_and_get(&client->dev); + + if (ret < 0) { + //printk(KERN_INFO "[MIRA130]: get_sync failed, but continue.\n"); + pm_runtime_put_noidle(&client->dev); + return ret; + } + + /* Apply default values of current mode */ + if (mira130->skip_reg_upload == 0) { + /* Stop treaming before uploading register sequence */ + printk(KERN_INFO "[MIRA130]: Writing stop streaming regs.\n"); + ret = mira130_write_stop_streaming_regs(mira130); + if (ret) { + dev_err(&client->dev, "Could not write stream-on sequence"); + goto err_rpm_put; + } + + reg_list = &mira130->mode->reg_list; + printk(KERN_INFO "[MIRA130]: Write %d regs.\n", reg_list->num_of_regs); + ret = mira130_write_regs(mira130, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + goto err_rpm_put; + } + + ret = mira130_set_framefmt(mira130); + if (ret) { + dev_err(&client->dev, "%s failed to set frame format: %d\n", + __func__, ret); + goto err_rpm_put; + } + } else { + printk(KERN_INFO "[MIRA130]: Skip base register sequence upload, due to mira130->skip_reg_upload=%u.\n", mira130->skip_reg_upload); + } + + + printk(KERN_INFO "[MIRA130]: Entering v4l2 ctrl handler setup function.\n"); + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(mira130->sd.ctrl_handler); + printk(KERN_INFO "[MIRA130]: __v4l2_ctrl_handler_setup ret = %d.\n", ret); + if (ret) + goto err_rpm_put; + + if (mira130->skip_reg_upload == 0 || + (mira130->skip_reg_upload == 1 && mira130->force_stream_ctrl == 1) ) { + printk(KERN_INFO "[MIRA130]: Writing start streaming regs.\n"); + ret = mira130_write_start_streaming_regs(mira130); + if (ret) { + dev_err(&client->dev, "Could not write stream-on sequence"); + goto err_rpm_put; + } + } else { + printk(KERN_INFO "[MIRA130]: Skip write_start_streaming_regs due to skip_reg_upload == %d and force_stream_ctrl == %d.\n", + mira130->skip_reg_upload, mira130->force_stream_ctrl); + } + + /* vflip and hflip cannot change during streaming */ + printk(KERN_INFO "[MIRA130]: Entering v4l2 ctrl grab vflip grab vflip.\n"); + __v4l2_ctrl_grab(mira130->vflip, true); + printk(KERN_INFO "[MIRA130]: Entering v4l2 ctrl grab vflip grab hflip.\n"); + __v4l2_ctrl_grab(mira130->hflip, true); + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); + return ret; +} + +static void mira130_stop_streaming(struct mira130 *mira130) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + int ret = 0; + + /* Unlock controls for vflip and hflip */ + __v4l2_ctrl_grab(mira130->vflip, false); + __v4l2_ctrl_grab(mira130->hflip, false); + + if (mira130->skip_reset == 0) { + if (mira130->skip_reg_upload == 0 || + (mira130->skip_reg_upload == 1 && mira130->force_stream_ctrl == 1) ) { + + ret = mira130_write_stop_streaming_regs(mira130); + if (ret) { + dev_err(&client->dev, "Could not write the stream-off sequence"); + } + } else { + printk(KERN_INFO "[MIRA130]: Skip write_stop_streaming_regs due to skip_reg_upload == %d and force_stream_ctrl == %d.\n", + mira130->skip_reg_upload, mira130->force_stream_ctrl); + } + } else { + printk(KERN_INFO "[MIRA130]: Skip write_stop_streaming_regs due to mira130->skip_reset == %d.\n", mira130->skip_reset); + } + + pm_runtime_put(&client->dev); +} + +static int mira130_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct mira130 *mira130 = to_mira130(sd); + int ret = 0; + + mutex_lock(&mira130->mutex); + if (mira130->streaming == enable) { + mutex_unlock(&mira130->mutex); + return 0; + } + + printk(KERN_INFO "[MIRA130]: Entering mira130_set_stream enable: %d.\n", enable); + + if (enable) { + /* + * Apply default & customized values + * and then start streaming. + */ + ret = mira130_start_streaming(mira130); + if (ret) + goto err_unlock; + } else { + mira130_stop_streaming(mira130); + } + + mira130->streaming = enable; + + mutex_unlock(&mira130->mutex); + + printk(KERN_INFO "[MIRA130]: Returning mira130_set_stream with ret: %d.\n", ret); + + return ret; + +err_unlock: + mutex_unlock(&mira130->mutex); + + return ret; +} + +static int __maybe_unused mira130_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira130 *mira130 = to_mira130(sd); + + printk(KERN_INFO "[MIRA130]: Entering suspend function.\n"); + + if (mira130->streaming) + mira130_stop_streaming(mira130); + + return 0; +} + +static int __maybe_unused mira130_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira130 *mira130 = to_mira130(sd); + int ret; + + printk(KERN_INFO "[MIRA130]: Entering resume function.\n"); + + if (mira130->streaming) { + ret = mira130_start_streaming(mira130); + if (ret) + goto error; + } + + return 0; + +error: + mira130_stop_streaming(mira130); + mira130->streaming = false; + + return ret; +} + +static int mira130_get_regulators(struct mira130 *mira130) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + unsigned int i; + + for (i = 0; i < MIRA130_NUM_SUPPLIES; i++) + mira130->supplies[i].supply = mira130_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, + MIRA130_NUM_SUPPLIES, + mira130->supplies); +} + +/* OTP power on */ +static int mira130_otp_power_on(struct mira130 *mira130) +{ + int ret; + + ret = mira130_write(mira130, 0x0080, 0x04); + + return 0; +} + +/* OTP power off */ +static int mira130_otp_power_off(struct mira130 *mira130) +{ + int ret; + + ret = mira130_write(mira130, 0x0080, 0x08); + + return 0; +} + +/* OTP power on */ +static int mira130_otp_read(struct mira130 *mira130, u8 addr, u8 offset, u8 *val) +{ + int ret; + + ret = mira130_write(mira130, 0x0086, addr); + ret = mira130_write(mira130, 0x0080, 0x02); + ret = mira130_read(mira130, 0x0082 + offset, val); + return 0; +} + + +/* Verify chip ID */ +static int mira130_identify_module(struct mira130 *mira130) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + int ret; + u8 val; + + mira130_otp_power_on(mira130); + + usleep_range(100, 110); + + ret = mira130_otp_read(mira130, 0x0d, 0, &val); + dev_err(&client->dev, "Read OTP add 0x0d with val %x\n", val); + + mira130_otp_power_off(mira130); + + val = 0; + mira130_read(mira130, 0x3107, &val); + printk(KERN_INFO "[MIRA130]: %s Sensor ID high byte %X.\n", __func__, val); + mira130_read(mira130, 0x3108, &val); + printk(KERN_INFO "[MIRA130]: %s Sensor ID low byte %X.\n", __func__, val); + + return 0; +} + +static const struct v4l2_subdev_core_ops mira130_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops mira130_video_ops = { + .s_stream = mira130_set_stream, +}; + +static const struct v4l2_subdev_pad_ops mira130_pad_ops = { + .enum_mbus_code = mira130_enum_mbus_code, + .get_fmt = mira130_get_pad_format, + .set_fmt = mira130_set_pad_format, + .get_selection = mira130_get_selection, + .enum_frame_size = mira130_enum_frame_size, +}; + +static const struct v4l2_subdev_ops mira130_subdev_ops = { + .core = &mira130_core_ops, + .video = &mira130_video_ops, + .pad = &mira130_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mira130_internal_ops = { + .open = mira130_open, +}; + +/* Initialize control handlers */ +static int mira130_init_controls(struct mira130 *mira130) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira130->sd); + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_fwnode_device_properties props; + int ret; + struct v4l2_ctrl_config *mira130_reg_w; + struct v4l2_ctrl_config *mira130_reg_r; + + u32 max_exposure = 0; + + ctrl_hdlr = &mira130->ctrl_handler; + /* v4l2_ctrl_handler_init gives a hint/guess of the number of v4l2_ctrl_new */ + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16); + if (ret) + return ret; + + mutex_init(&mira130->mutex); + ctrl_hdlr->lock = &mira130->mutex; + + printk(KERN_INFO "[MIRA130]: %s V4L2_CID_PIXEL_RATE %X.\n", __func__, V4L2_CID_PIXEL_RATE); + + /* By default, PIXEL_RATE is read only */ + mira130->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &mira130_ctrl_ops, + V4L2_CID_PIXEL_RATE, + MIRA130_PIXEL_RATE, + MIRA130_PIXEL_RATE, 1, + MIRA130_PIXEL_RATE); + + printk(KERN_INFO "[MIRA130]: %s V4L2_CID_VBLANK %X.\n", __func__, V4L2_CID_VBLANK); + + mira130->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &mira130_ctrl_ops, + V4L2_CID_VBLANK, MIRA130_MIN_VBLANK, + MIRA130_MAX_VBLANK, 1, + mira130->mode->vblank); + + printk(KERN_INFO "[MIRA130]: %s V4L2_CID_HBLANK %X.\n", __func__, V4L2_CID_HBLANK); + + mira130->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &mira130_ctrl_ops, + V4L2_CID_HBLANK, mira130->mode->hblank, + mira130->mode->hblank, 1, + mira130->mode->hblank); + + // Make the vblank control read only. This could be changed to allow changing framerate in + // runtime, but would require adapting other settings + // mira130->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + // Exposure is indicated in number of lines here + // Max is determined by vblank + vsize and Tglob. + max_exposure = mira130_calculate_max_exposure_time(mira130->mode->row_length, + mira130->mode->height, + mira130->mode->vblank); + + printk(KERN_INFO "[MIRA130]: %s V4L2_CID_EXPOSURE %X.\n", __func__, V4L2_CID_EXPOSURE); + + mira130->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &mira130_ctrl_ops, + V4L2_CID_EXPOSURE, + MIRA130_EXPOSURE_MIN, max_exposure, + 1, + MIRA130_DEFAULT_EXPOSURE); + + printk(KERN_INFO "[MIRA130]: %s V4L2_CID_ANALOGUE_GAIN %X.\n", __func__, V4L2_CID_ANALOGUE_GAIN); + + mira130->gain = v4l2_ctrl_new_std(ctrl_hdlr, &mira130_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + 0, ARRAY_SIZE(analog_gain_lut) - 1, + 1, 0); + + printk(KERN_INFO "[MIRA130]: %s V4L2_CID_HFLIP %X.\n", __func__, V4L2_CID_HFLIP); + + mira130->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &mira130_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (mira130->hflip) + mira130->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + printk(KERN_INFO "[MIRA130]: %s V4L2_CID_VFLIP %X.\n", __func__, V4L2_CID_VFLIP); + + mira130->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &mira130_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (mira130->vflip) + mira130->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + printk(KERN_INFO "[MIRA130]: %s V4L2_CID_TEST_PATTERN %X.\n", __func__, V4L2_CID_TEST_PATTERN); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &mira130_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(mira130_test_pattern_menu) - 1, + 0, 0, mira130_test_pattern_menu); + /* + * Custom op + */ + mira130_reg_w = &custom_ctrl_config_list[0]; + printk(KERN_INFO "[MIRA130]: %s AMS_CAMERA_CID_MIRA_REG_W %X.\n", __func__, AMS_CAMERA_CID_MIRA_REG_W); + mira130->mira130_reg_w = v4l2_ctrl_new_custom(ctrl_hdlr, mira130_reg_w, NULL); + + mira130_reg_r = &custom_ctrl_config_list[1]; + printk(KERN_INFO "[MIRA130]: %s AMS_CAMERA_CID_MIRA_REG_R %X.\n", __func__, AMS_CAMERA_CID_MIRA_REG_R); + mira130->mira130_reg_r = v4l2_ctrl_new_custom(ctrl_hdlr, mira130_reg_r, NULL); + if (mira130->mira130_reg_r) + mira130->mira130_reg_r->flags |= (V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY); + + 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, &mira130_ctrl_ops, + &props); + if (ret) + goto error; + + mira130->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + mutex_destroy(&mira130->mutex); + + return ret; +} + +static void mira130_free_controls(struct mira130 *mira130) +{ + v4l2_ctrl_handler_free(mira130->sd.ctrl_handler); + mutex_destroy(&mira130->mutex); +} + +static int mira130_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; + + 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; + } + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) { + dev_err(dev, "only 2 data lanes are currently supported\n"); + goto error_out; + } + + /* Check the link frequency set in device tree */ + if (!ep_cfg.nr_of_link_frequencies) { + dev_err(dev, "link-frequency property not found in DT\n"); + goto error_out; + } + + if (ep_cfg.nr_of_link_frequencies != 1 || + ep_cfg.link_frequencies[0] != MIRA130_DEFAULT_LINK_FREQ) { + dev_err(dev, "Link frequency not supported: %lld\n", + ep_cfg.link_frequencies[0]); + goto error_out; + } + + + // TODO(jalv): Check device tree configuration and make sure it is supported by the driver + ret = 0; + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int mira130pmic_init_controls(struct i2c_client *client) +{ + int ret; + u8 val; + + ret = mira130pmic_write(client, 0x62, 0x00); + ret = mira130pmic_write(client, 0x61, 0x00); + + ret = mira130pmic_read(client, 0x61, &val); + dev_err(&client->dev, "Read 0x61 with val %x\n", val); + + + usleep_range(100, 110); + + ret = mira130pmic_write(client, 0x05, 0x00); + ret = mira130pmic_write(client, 0x0e, 0x00); + ret = mira130pmic_write(client, 0x11, 0x00); + ret = mira130pmic_write(client, 0x14, 0x00); + ret = mira130pmic_write(client, 0x17, 0x00); + ret = mira130pmic_write(client, 0x1a, 0x00); + ret = mira130pmic_write(client, 0x1c, 0x00); + ret = mira130pmic_write(client, 0x1d, 0x00); + ret = mira130pmic_write(client, 0x1e, 0x00); + ret = mira130pmic_write(client, 0x1f, 0x00); + + ret = mira130pmic_write(client, 0x24, 0x48); + ret = mira130pmic_write(client, 0x20, 0x00); + ret = mira130pmic_write(client, 0x21, 0x00); + ret = mira130pmic_write(client, 0x1a, 0x00); + ret = mira130pmic_write(client, 0x01, 0x00); + ret = mira130pmic_write(client, 0x08, 0x00); + ret = mira130pmic_write(client, 0x02, 0x00); + ret = mira130pmic_write(client, 0x0b, 0x00); + ret = mira130pmic_write(client, 0x14, 0x00); + ret = mira130pmic_write(client, 0x17, 0x00); + ret = mira130pmic_write(client, 0x1c, 0x00); + ret = mira130pmic_write(client, 0x1d, 0x00); + ret = mira130pmic_write(client, 0x1f, 0x00); + + usleep_range(50, 60); + + ret = mira130pmic_write(client, 0x62, 0x0d); + + usleep_range(50, 60); + usleep_range(50000, 50000+100); + + ret = mira130pmic_write(client, 0x27, 0xff); + ret = mira130pmic_write(client, 0x28, 0xff); + ret = mira130pmic_write(client, 0x29, 0xff); + ret = mira130pmic_write(client, 0x2a, 0xff); + ret = mira130pmic_write(client, 0x2b, 0xff); + + ret = mira130pmic_write(client, 0x41, 0x04); + usleep_range(50, 60); + + ret = mira130pmic_read(client, 0x20, &val); + dev_err(&client->dev, "Read 0x20 with val %x\n", val); + + // PCB V2.0 or above, enable LDO9=2.50V for VDD25 + ret = mira130pmic_write(client, 0x20, 0xb2); + // For PCB V1.0, VDD28 on 2.85V for older PCBs + // ret = mira130pmic_write(client, 0x20, 0xb9); + + ret = mira130pmic_read(client, 0x20, &val); + dev_err(&client->dev, "Read 0x20 with val %x\n", val); + + usleep_range(700, 710); + + ret = mira130pmic_write(client, 0x12, 0x16); + ret = mira130pmic_write(client, 0x10, 0x16); + ret = mira130pmic_write(client, 0x11, 0x96); + ret = mira130pmic_write(client, 0x1e, 0x96); + ret = mira130pmic_write(client, 0x21, 0x96); + usleep_range(50, 60); + + ret = mira130pmic_write(client, 0x00, 0x04); + ret = mira130pmic_write(client, 0x04, 0x34); + ret = mira130pmic_write(client, 0x06, 0xbf); + ret = mira130pmic_write(client, 0x05, 0xb4); + ret = mira130pmic_write(client, 0x03, 0x00); + ret = mira130pmic_write(client, 0x0d, 0x34); + ret = mira130pmic_write(client, 0x0f, 0xbf); + ret = mira130pmic_write(client, 0x0e, 0xb4); + usleep_range(50, 60); + + ret = mira130pmic_write(client, 0x42, 0x05); + usleep_range(50, 60); + + ret = mira130pmic_write(client, 0x45, 0x40); + ret = mira130pmic_write(client, 0x57, 0x02); + ret = mira130pmic_write(client, 0x5d, 0x10); + ret = mira130pmic_write(client, 0x61, 0x10); + + return 0; +} + + +static int mira130_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct mira130 *mira130; + int ret; + + printk(KERN_INFO "[MIRA130]: probing v4l2 sensor.\n"); + printk(KERN_INFO "[MIRA130]: Driver Version 0.0.\n"); + + dev_err(dev, "[MIRA130] name: %s.\n", client->name); + + mira130 = devm_kzalloc(&client->dev, sizeof(*mira130), GFP_KERNEL); + if (!mira130) + return -ENOMEM; + + v4l2_i2c_subdev_init(&mira130->sd, client, &mira130_subdev_ops); + + /* Check the hardware configuration in device tree */ + if (mira130_check_hwcfg(dev)) + return -EINVAL; + + /* Parse device tree to check if dtoverlay has param skip-reg-upload=1 */ + device_property_read_u32(dev, "skip-reg-upload", &mira130->skip_reg_upload); + printk(KERN_INFO "[MIRA130]: skip-reg-upload %d.\n", mira130->skip_reg_upload); + /* Set default TBD I2C device address to LED I2C Address*/ + mira130->tbd_client_i2c_addr = MIRA130LED_I2C_ADDR; + printk(KERN_INFO "[MIRA130]: User defined I2C device address defaults to LED driver I2C address 0x%X.\n", mira130->tbd_client_i2c_addr); + + + /* Get system clock (xclk) */ + mira130->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(mira130->xclk)) { + dev_err(dev, "failed to get xclk\n"); + return PTR_ERR(mira130->xclk); + } + + mira130->xclk_freq = clk_get_rate(mira130->xclk); + if (mira130->xclk_freq != MIRA130_SUPPORTED_XCLK_FREQ) { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + mira130->xclk_freq); + return -EINVAL; + } + + ret = mira130_get_regulators(mira130); + if (ret) { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + /* Request optional enable pin */ + // mira130->reset_gpio = devm_gpiod_get_optional(dev, "reset", + // GPIOD_OUT_HIGH); + + { + printk(KERN_INFO "[MIRA130]: Init PMIC.\n"); + mira130->pmic_client = i2c_new_dummy_device(client->adapter, + MIRA130PMIC_I2C_ADDR); + if (IS_ERR(mira130->pmic_client)) + return PTR_ERR(mira130->pmic_client); + mira130->uc_client = i2c_new_dummy_device(client->adapter, + MIRA130UC_I2C_ADDR); + if (IS_ERR(mira130->uc_client)) + return PTR_ERR(mira130->uc_client); + mira130->led_client = i2c_new_dummy_device(client->adapter, + MIRA130LED_I2C_ADDR); + if (IS_ERR(mira130->led_client)) + return PTR_ERR(mira130->led_client); + + mira130pmic_init_controls(mira130->pmic_client); + } + + dev_err(dev, "[MIRA130] Sleep for 1 second to let PMIC driver complete init.\n"); + usleep_range(1000000, 1000000+100); + + /* + * The sensor must be powered for mira130_identify_module() + * to be able to read the CHIP_ID register + */ + ret = mira130_power_on(dev); + if (ret) + return ret; + + printk(KERN_INFO "[MIRA130]: Entering identify function.\n"); + + ret = mira130_identify_module(mira130); + if (ret) + goto error_power_off; + + printk(KERN_INFO "[MIRA130]: Setting support function.\n"); + + /* Set default mode to max resolution */ + mira130->mode = &supported_modes[0]; + + printk(KERN_INFO "[MIRA130]: Entering init controls function.\n"); + + ret = mira130_init_controls(mira130); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + mira130->sd.internal_ops = &mira130_internal_ops; + mira130->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + mira130->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pads */ + mira130->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; + mira130->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; + + printk(KERN_INFO "[MIRA130]: Entering set default format function.\n"); + + /* Initialize default format */ + mira130_set_default_format(mira130); + + printk(KERN_INFO "[MIRA130]: Entering pads init function.\n"); + + ret = media_entity_pads_init(&mira130->sd.entity, NUM_PADS, mira130->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + printk(KERN_INFO "[MIRA130]: Entering subdev sensor common function.\n"); + + ret = v4l2_async_register_subdev_sensor(&mira130->sd); + if (ret < 0) { + dev_err(dev, "failed to register sensor sub-device: %d\n", ret); + goto error_media_entity; + } + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&mira130->sd.entity); + +error_handler_free: + mira130_free_controls(mira130); + +error_power_off: + mira130_power_off(dev); + + i2c_unregister_device(mira130->pmic_client); + i2c_unregister_device(mira130->uc_client); + i2c_unregister_device(mira130->led_client); + + return ret; +} + +static void mira130_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira130 *mira130 = to_mira130(sd); + + i2c_unregister_device(mira130->pmic_client); + i2c_unregister_device(mira130->uc_client); + i2c_unregister_device(mira130->led_client); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + mira130_free_controls(mira130); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + mira130_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + +} + +static const struct dev_pm_ops mira130_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mira130_suspend, mira130_resume) + SET_RUNTIME_PM_OPS(mira130_power_off, mira130_power_on, NULL) +}; + +#endif // __MIRA130_INL__ + diff --git a/drivers/media/i2c/mira220.c b/drivers/media/i2c/mira220.c new file mode 100644 index 00000000000000..226a4753b0aadd --- /dev/null +++ b/drivers/media/i2c/mira220.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ams MIRA220 cameras. + * Copyright (C) 2022, ams-OSRAM + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + */ + +#include "mira220.inl" + +static const struct of_device_id mira220_dt_ids[] = { + { .compatible = "ams,mira220" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mira220_dt_ids); + +static const struct i2c_device_id mira220_ids[] = { + { "mira220", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mira220_ids); + +static struct i2c_driver mira220_i2c_driver = { + .driver = { + .name = "mira220", + .of_match_table = mira220_dt_ids, + .pm = &mira220_pm_ops, + }, + .probe = mira220_probe, + .remove = mira220_remove, + .id_table = mira220_ids, +}; + +module_i2c_driver(mira220_i2c_driver); + +MODULE_AUTHOR("Zhenyu Ye "); +MODULE_DESCRIPTION("ams MIRA220 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/mira220.inl b/drivers/media/i2c/mira220.inl new file mode 100644 index 00000000000000..80c7349964008b --- /dev/null +++ b/drivers/media/i2c/mira220.inl @@ -0,0 +1,3906 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ams MIRA220 cameras. + * Copyright (C) 2022, ams-OSRAM + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + */ + +#ifndef __MIRA220_INL__ +#define __MIRA220_INL__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Introduce new v4l2 control + */ +#include +#define AMS_CAMERA_CID_BASE (V4L2_CTRL_CLASS_CAMERA | 0x2000) +#define AMS_CAMERA_CID_MIRA_REG_W (AMS_CAMERA_CID_BASE+0) +#define AMS_CAMERA_CID_MIRA_REG_R (AMS_CAMERA_CID_BASE+1) + +/* Most significant Byte is flag, and most significant bit is unused. */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_FOR_READ 0b00000001 +/* Use bit 5 to indicate special command, bit 1,2,3,4 for command. */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_CMD_SEL 0b00010000 +/* Special command for sleep. The other 3 Bytes (addr+val) is sleep values in us. */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_SLEEP_US 0b00010000 +/* Special command to enable power on (/off) when stream on (/off). */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_RESET_ON 0b00010010 +/* Special command to disable power on (/off) when stream on (/off). */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_RESET_OFF 0b00010100 +/* Special command to enable base register sequence upload, overwrite skip-reg-upload in dtoverlay */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_REG_UP_ON 0b00010110 +/* Special command to disable base register sequence upload, overwrite skip-reg-upload in dtoverlay */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_REG_UP_OFF 0b00011000 +/* Special command to manually power on */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_POWER_ON 0b00011010 +/* Special command to manually power off */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_POWER_OFF 0b00011100 +/* Special command to turn illumination trigger on */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_ILLUM_TRIG_ON 0b00011110 +/* Special command to turn illumination trigger off */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_ILLUM_TRIG_OFF 0b00010001 +/* Special command to set ILLUM_WIDTH. The other 3 Bytes (addr+val) is width value. */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_ILLUM_WIDTH 0b00010011 +/* Special command to set ILLUM_DELAY. The other 3 Bytes (addr+val) is width value. */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_ILLUM_DELAY 0b00010101 +/* Special command to enable force_stream_ctrl */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_STREAM_CTRL_ON 0b00011011 +/* Special command to disable force_stream_ctrl */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_STREAM_CTRL_OFF 0b00011101 + +/* + * Bit 6&7 of flag are combined to specify I2C dev (default is Mira). + * If bit 6&7 is 0b01, the reg_addr and reg_val are for a TBD I2C address. + * The TBD I2C address is default to MIRA220LED_I2C_ADDR. + * To change the TBD I2C address, set bit 6&7 to 0b10, + * then the reg_val will become TBD I2C address. + * The TBD I2C address is stored in mira220->tbd_client_i2c_addr. + */ +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_SEL 0b01100000 +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_MIRA 0b00000000 +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_TBD 0b00100000 +#define AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_SET_TBD 0b01000000 + +/* Pre-allocated i2c_client */ +#define MIRA220PMIC_I2C_ADDR 0x2D +#define MIRA220UC_I2C_ADDR 0x0A +#define MIRA220LED_I2C_ADDR 0x53 + + +#define MIRA220_NATIVE_WIDTH 1600U +#define MIRA220_NATIVE_HEIGHT 1400U + +#define MIRA220_PIXEL_ARRAY_LEFT 0U +#define MIRA220_PIXEL_ARRAY_TOP 0U +#define MIRA220_PIXEL_ARRAY_WIDTH 1600U +#define MIRA220_PIXEL_ARRAY_HEIGHT 1400U + +#define MIRA220_ANALOG_GAIN_REG 0x400A +#define MIRA220_ANALOG_GAIN_MIN 1 +#define MIRA220_ANALOG_GAIN_MAX 1 /* Fixed analog gain to 1, to avoid unexpected behavior. */ +#define MIRA220_ANALOG_GAIN_STEP 1 +#define MIRA220_ANALOG_GAIN_DEFAULT MIRA220_ANALOG_GAIN_MIN + +#define MIRA220_BIT_DEPTH_REG 0x209E +#define MIRA220_BIT_DEPTH_12_BIT 0x02 +#define MIRA220_BIT_DEPTH_10_BIT 0x04 +#define MIRA220_BIT_DEPTH_8_BIT 0x06 + +#define MIRA220_CSI_DATA_TYPE_REG 0x208D +#define MIRA220_CSI_DATA_TYPE_12_BIT 0x04 +#define MIRA220_CSI_DATA_TYPE_10_BIT 0x02 +#define MIRA220_CSI_DATA_TYPE_8_BIT 0x01 + +#define MIRA220_IMAGER_STATE_REG 0x1003 +#define MIRA220_IMAGER_STATE_STOP_AT_ROW 0x02 +#define MIRA220_IMAGER_STATE_STOP_AT_FRAME 0x04 +#define MIRA220_IMAGER_STATE_MASTER_CONTROL 0x10 + +#define MIRA220_IMAGER_RUN_REG 0x10F0 +#define MIRA220_IMAGER_RUN_START 0x01 +#define MIRA220_IMAGER_RUN_STOP 0x00 + +#define MIRA220_IMAGER_RUN_CONT_REG 0x1002 +#define MIRA220_IMAGER_RUN_CONT_ENABLE 0x04 +#define MIRA220_IMAGER_RUN_CONT_DISABLE 0x00 + +#define MIRA220_NB_OF_FRAMES_LO_REG 0x10F2 +#define MIRA220_NB_OF_FRAMES_HI_REG 0x10F3 + +#define MIRA220_POWER_MODE_REG 0x0043 +#define MIRA220_POWER_MODE_SLEEP 0x01 +#define MIRA220_POWER_MODE_IDLE 0x02 +#define MIRA220_POWER_MODE_ACTIVE 0x0C + +// Exposure time is indicated in number of rows +#define MIRA220_EXP_TIME_LO_REG 0x100C +#define MIRA220_EXP_TIME_HI_REG 0x100D + +// VBLANK is indicated in number of rows +#define MIRA220_VBLANK_LO_REG 0x1012 +#define MIRA220_VBLANK_HI_REG 0x1013 + +#define MIRA220_EXT_EXP_PW_SEL_REG 0x1001 +#define MIRA220_EXT_EXP_PW_SEL_USE_REG 1 +#define MIRA220_EXT_EXP_PW_SEL_USE_EXT 0 + +// Exposure delay is indicated in number of rows +#define MIRA220_EXT_EXP_DELAY_LO_REG 0x10D0 +#define MIRA220_EXT_EXP_DELAY_HI_REG 0x10D1 + +// Sets the duration of the row length in clock cycles of CLK_IN +#define MIRA220_ROW_LENGTH_LO_REG 0x102B +#define MIRA220_ROW_LENGTH_HI_REG 0x102C + +#define MIRA220_VSIZE1_LO_REG 0x1087 +#define MIRA220_VSIZE1_HI_REG 0x1088 +#define MIRA220_VSIZE1_MASK 0x7FF + +#define MIRA220_VSTART1_LO_REG 0x107D +#define MIRA220_VSTART1_HI_REG 0x107E +#define MIRA220_VSTART1_MASK 0x7FF + +// HSIZE units are number of columns / 2 +#define MIRA220_HSIZE_LO_REG 0x2008 +#define MIRA220_HSIZE_HI_REG 0x2009 +#define MIRA220_HSIZE_MASK 0x3FF + +// HSTART units are number of columns / 2 +#define MIRA220_HSTART_LO_REG 0x200A +#define MIRA220_HSTART_HI_REG 0x200B +#define MIRA220_HSTART_MASK 0x3FF + +// MIPI_HSIZE units are number of columns (HSIZE * 2) +#define MIRA220_MIPI_HSIZE_LO_REG 0x207D +#define MIRA220_MIPI_HSIZE_HI_REG 0x207E +#define MIRA220_MIPI_HSIZE_MASK 0xFFFF + +#define MIRA220_HFLIP_REG 0x209C +#define MIRA220_HFLIP_ENABLE_MIRROR 1 +#define MIRA220_HFLIP_DISABLE_MIRROR 0 + +#define MIRA220_VFLIP_REG 0x1095 +#define MIRA220_VFLIP_ENABLE_FLIP 1 +#define MIRA220_VFLIP_DISABLE_FLIP 0 + +#define MIRA220_BIT_ORDER_REG 0x2063 +#define MIRA220_BIT_ORDER_NORMAL 0 +#define MIRA220_BIT_ORDER_REVERSED 1 + +#define MIRA220_BSP_REG 0x4006 +#define MIRA220_BSP_ENABLE 0x08 +#define MIRA220_BSP_DISABLE 0x0F + +#define MIRA220_MIPI_SOFT_RESET_REG 0x5004 +#define MIRA220_MIPI_SOFT_RESET_DPHY 0x01 +#define MIRA220_MIPI_SOFT_RESET_NONE 0x00 + +#define MIRA220_FSYNC_EOF_MAX_CTR_LO_REG 0x2066 +#define MIRA220_FSYNC_EOF_MAX_CTR_HI_REG 0x2067 + +#define MIRA220_FSYNC_EOF_VEND_ST_LO_REG 0x206E +#define MIRA220_FSYNC_EOF_VEND_ST_HI_REG 0x206F + +#define MIRA220_FSYNC_EOF_HSTART_EMB_ST_LO_REG 0x2076 +#define MIRA220_FSYNC_EOF_HSTART_EMB_ST_HI_REG 0x2077 + +#define MIRA220_FSYNC_EOF_DSTART_EMB_ST_LO_REG 0x2078 +#define MIRA220_FSYNC_EOF_DSTART_EMB_ST_HI_REG 0x2079 + +#define MIRA220_FSYNC_EOF_HEND_EMB_ST_LO_REG 0x207A +#define MIRA220_FSYNC_EOF_HEND_EMB_ST_HI_REG 0x207B + +#define MIRA220_GLOB_NUM_CLK_CYCLES 1928 + +#define MIRA220_SUPPORTED_XCLK_FREQ 24000000 + +#define MIRA220_MIN_ROW_LENGTH 450 +#define MIRA220_MAX_ROW_LENGTH 1400 +#define MIRA220_MIN_VBLANK (1 + 11 + MIRA220_GLOB_NUM_CLK_CYCLES \ + / MIRA220_MAX_ROW_LENGTH) + +// Default exposure is adjusted to mode with smallest height +#define MIRA220_MIN_V_SIZE 300 +#define MIRA220_DEFAULT_EXPOSURE 100 //(MIRA220_MIN_V_SIZE + MIRA220_MIN_VBLANK - MIRA220_GLOB_NUM_CLK_CYCLES / MIRA220_MIN_ROW_LENGTH) //TODO +#define MIRA220_EXPOSURE_MIN 1 + +// Power on function timing +#define MIRA220_XCLR_MIN_DELAY_US 100000 +#define MIRA220_XCLR_DELAY_RANGE_US 30 + + + +// Outdated. See below. +// pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample +// 1.0Gb/s * 2 * 2 / 12 = 357913941 +// #define MIRA220_PIXEL_RATE (357913941) + +// Mira220 PIXEL_RATE is derived from ROW_LENGTH. See datasheet Section 9.2. +// ROW_LENGTH is set by registers: 0x102B, 0x102C. Unit is number of CLK_IN cycles. +// PIXEL_RATE = 1000000000 * WIDTH / (ROW_LENGTH * CLK_IN_PERIOD_NS) +// ROW_LENGTH_1600x1400_1000GBS=300 +// ROW_LENGTH_640x480_1000GBS=450 +// CLK_IN_PERIOD_NS = 1.0 s / 38.4 Mhz = 26.04 ns +// MIRA220_PIXEL_RATE = 1000000000 * 1600 / (300 * 26.04) = 204813108 +// MIRA220_PIXEL_RATE = 1000000000 * 640 / (450 * 26.04) = 54616828 +#define MIRA220_PIXEL_RATE 384000000 //384M (x10) +// Row time in microseconds. Not used in driver, but used in libcamera cam_helper. +// ROW_TIME_US = ROW_LENGTH * CLK_IN_PERIOD_NS / 1000 +// MIRA220_ROW_TIME_1600x1400_1000GBS_US=(300*26.04/1000)=7.8us +// MIRA220_ROW_TIME_640x480_1000GBS_US=(450*26.04/1000)=11.7us + +/* Should match device tree link freq */ +#define MIRA220_DEFAULT_LINK_FREQ 456000000 + +/* Trick the libcamera with achievable fps via hblank */ + +/* Formular in libcamera to derive TARGET_FPS: + * TARGET_FPS=1/((1/MIRA220_PIXEL_RATE)*(WIDTH+HBLANK)*(HEIGHT+MIRA220_MIN_VBLANK)) + * Example: 640x480 with HBLANK=0 and MIRA220_MIN_VBLANK=13 + * TARGET_FPS=1/((1/54616828)*640*(480+13))=173 + * Example: 1600x1400 with HBLANK=0 and MIRA220_MIN_VBLANK=13 + * TARGET_FPS=1/((1/204813108)*1600*(1400+13))=91 + * + * Inverse the above formula to derive HBLANK from TARGET_FPS: + * HBLANK=1/((1/MIRA220_PIXEL_RATE)*TARGET_FPS*(HEIGHT+MIRA220_MIN_VBLANK))-WIDTH + * Example with TARGET_FPS of 120 fps for 640x480 + * HBLANK=1/((1/54616828)*120*(480+13))-640=283 + * Example with TARGET_FPS of 30 fps for 1600x1400 + * HBLANK=1/((1/204813108)*30*(1400+13))-1600=3232 + */ +#define MIRA220_HBLANK_640x480_120FPS 3860 +#define MIRA220_HBLANK_1600x1400_30FPS 2900 + +#define MIRA220_REG_TEST_PATTERN 0x2091 +#define MIRA220_TEST_PATTERN_DISABLE 0x00 +#define MIRA220_TEST_PATTERN_VERTICAL_GRADIENT 0x01 + +/* Embedded metadata stream structure */ +#define MIRA220_EMBEDDED_LINE_WIDTH 16384 +#define MIRA220_NUM_EMBEDDED_LINES 1 + +/* From Jetson driver */ +#define MIRA220_DEFAULT_LINE_LENGTH (0xA80) +#define MIRA220_DEFAULT_PIXEL_CLOCK (160) +#define MIRA220_DEFAULT_FRAME_LENGTH (0x07C0) //TODO REMOVE THESE + +/* Illumination trigger */ +#define MIRA220_EN_TRIG_ILLUM_REG 0x10D7 +#define MIRA220_ILLUM_WIDTH_REG 0x10D5 +#define MIRA220_ILLUM_DELAY_REG 0x10D2 +#define MIRA220_ILLUM_DELAY_SIGN_REG 0x10D4 +#define MIRA220_ILLUM_WIDTH_DEFAULT (0) +#define MIRA220_ILLUM_DELAY_DEFAULT (0) + +enum pad_types { + IMAGE_PAD, + METADATA_PAD, + NUM_PADS +}; + +struct mira220_reg { + u16 address; + u8 val; +}; + +struct mira220_reg_list { + unsigned int num_of_regs; + const struct mira220_reg *regs; +}; + +struct mira220_v4l2_reg { + u32 val; +}; + +/* Mode : resolution and related config&values */ +struct mira220_mode { + /* Frame width */ + unsigned int width; + /* Frame height */ + unsigned int height; + + /* Analog crop rectangle. */ + struct v4l2_rect crop; + + /* Default register values */ + struct mira220_reg_list reg_list; + u32 row_length; + + u32 pixel_rate; + u32 min_vblank; + u32 max_vblank; + u32 hblank; + u32 code; +}; + +// 1600_1400_30fps_12b_2lanes +static const struct mira220_reg full_1600_1400_30fps_12b_2lanes_reg[] = { + // Disable internal LDOs connected to VDD25 + {0x401e, 0x02}, + {0x4038, 0x3b}, + // Sensor uploads + // Stop sensor at a row boundary (stop sensor in a known state) + // self.stop_img_capture('row') + // MIPI TX controller disable + {0x6006, 0x00}, + // MIPI 2 lane mode + {0x6012, 0x01}, + // MIPI continuous PHY clocking + {0x6013, 0x00}, + // MIPI TX controller enable + {0x6006, 0x01}, + // Disable statistics + {0x205d, 0x00}, + {0x2063, 0x00}, + // Defect pixel correction disabled + {0x24dc, 0x0}, + {0x24dd, 0x03}, + {0x24de, 0x03}, + {0x24df, 0x00}, + // 2.24MP window + {0x4006, 0x08}, + {0x401c, 0x6f}, + // Row noise correction enabled, with flat field target of 100 + {0x204b, 0x03}, + {0x205b, 0x64}, + {0x205c, 0x00}, + // Some default values for references (some trim values are in OTP, better to use those) + {0x4018, 0x3f}, + {0x403b, 0x0b}, + {0x403e, 0x0e}, + {0x402b, 0x06}, + // Misc. sensor settings that should not be touched + {0x1077, 0x00}, + {0x1078, 0x00}, + {0x1009, 0x08}, + {0x100a, 0x00}, + {0x110f, 0x08}, + {0x1110, 0x00}, + {0x1006, 0x02}, + {0x402c, 0x64}, + {0x3064, 0x00 }, + {0x3065, 0xf0}, + {0x4013, 0x13}, + {0x401f, 0x09}, + {0x4020, 0x13}, + {0x4044, 0x75}, + {0x4027, 0x00}, + {0x3215, 0x69}, + {0x3216, 0x0f}, + {0x322B, 0x69}, + {0x322C, 0x0f}, + {0x4051, 0x80}, + {0x4052, 0x10}, + {0x4057, 0x80}, + {0x4058, 0x10}, + {0x3212, 0x59}, + {0x4047, 0x8f}, + {0x4026, 0x10}, + {0x4032, 0x53}, + {0x4036, 0x17}, + {0x50b8, 0xf4}, + // Related to pixel timing, do not adjust these + {0x3016, 0x00}, + {0x3017, 0x2c}, + {0x3018, 0x8c}, + {0x3019, 0x45}, + {0x301a, 0x05}, + {0x3013, 0x0a}, + {0x301b, 0x00}, + {0x301c, 0x04}, + {0x301d, 0x88}, + {0x301e, 0x45}, + {0x301f, 0x05}, + {0x3020, 0x00}, + {0x3021, 0x04}, + {0x3022, 0x88}, + {0x3023, 0x45}, + {0x3024, 0x05}, + {0x3025, 0x00}, + {0x3026, 0x04}, + {0x3027, 0x88}, + {0x3028, 0x45}, + {0x3029, 0x05}, + {0x302f, 0x00}, + {0x3056, 0x00}, + {0x3057, 0x00}, + {0x3300, 0x01}, + {0x3301, 0x00}, + {0x3302, 0xb0}, + {0x3303, 0xb0}, + {0x3304, 0x16}, + {0x3305, 0x15}, + {0x3306, 0x01}, + {0x3307, 0x00}, + {0x3308, 0x30}, + {0x3309, 0xa0}, + {0x330a, 0x16}, + {0x330b, 0x15}, + {0x330c, 0x01}, + {0x330d, 0x00}, + {0x330e, 0x30}, + {0x330f, 0xa0}, + {0x3310, 0x16}, + {0x3311, 0x15}, + {0x3312, 0x01}, + {0x3313, 0x00}, + {0x3314, 0x30}, + {0x3315, 0xa0}, + {0x3316, 0x16}, + {0x3317, 0x15}, + {0x3318, 0x01}, + {0x3319, 0x00}, + {0x331a, 0x30}, + {0x331b, 0xa0}, + {0x331c, 0x16}, + {0x331d, 0x15}, + {0x331e, 0x01}, + {0x331f, 0x00}, + {0x3320, 0x30}, + {0x3321, 0xa0}, + {0x3322, 0x16}, + {0x3323, 0x15}, + {0x3324, 0x01}, + {0x3325, 0x00}, + {0x3326, 0x30}, + {0x3327, 0xa0}, + {0x3328, 0x16}, + {0x3329, 0x15}, + {0x332a, 0x2b}, + {0x332b, 0x00}, + {0x332c, 0x30}, + {0x332d, 0xa0}, + {0x332e, 0x16}, + {0x332f, 0x15}, + {0x3330, 0x01}, + {0x3331, 0x00}, + {0x3332, 0x10}, + {0x3333, 0xa0}, + {0x3334, 0x16}, + {0x3335, 0x15}, + {0x3058, 0x08}, + {0x3059, 0x00}, + {0x305a, 0x09}, + {0x305b, 0x00}, + {0x3336, 0x01}, + {0x3337, 0x00}, + {0x3338, 0x90}, + {0x3339, 0xb0}, + {0x333a, 0x16}, + {0x333b, 0x15}, + {0x333c, 0x1f}, + {0x333d, 0x00}, + {0x333e, 0x10}, + {0x333f, 0xa0}, + {0x3340, 0x16}, + {0x3341, 0x15}, + {0x3342, 0x52}, + {0x3343, 0x00}, + {0x3344, 0x10}, + {0x3345, 0x80}, + {0x3346, 0x16}, + {0x3347, 0x15}, + {0x3348, 0x01}, + {0x3349, 0x00}, + {0x334a, 0x10}, + {0x334b, 0x80}, + {0x334c, 0x16}, + {0x334d, 0x1d}, + {0x334e, 0x01}, + {0x334f, 0x00}, + {0x3350, 0x50}, + {0x3351, 0x84}, + {0x3352, 0x16}, + {0x3353, 0x1d}, + {0x3354, 0x18}, + {0x3355, 0x00}, + {0x3356, 0x10}, + {0x3357, 0x84}, + {0x3358, 0x16}, + {0x3359, 0x1d}, + {0x335a, 0x80}, + {0x335b, 0x02}, + {0x335c, 0x10}, + {0x335d, 0xc4}, + {0x335e, 0x14}, + {0x335f, 0x1d}, + {0x3360, 0xa5}, + {0x3361, 0x00}, + {0x3362, 0x10}, + {0x3363, 0x84}, + {0x3364, 0x16}, + {0x3365, 0x1d}, + {0x3366, 0x01}, + {0x3367, 0x00}, + {0x3368, 0x90}, + {0x3369, 0x84}, + {0x336a, 0x16}, + {0x336b, 0x1d}, + {0x336c, 0x12}, + {0x336d, 0x00}, + {0x336e, 0x10}, + {0x336f, 0x84}, + {0x3370, 0x16}, + {0x3371, 0x15}, + {0x3372, 0x32}, + {0x3373, 0x00}, + {0x3374, 0x30}, + {0x3375, 0x84}, + {0x3376, 0x16}, + {0x3377, 0x15}, + {0x3378, 0x26}, + {0x3379, 0x00}, + {0x337a, 0x10}, + {0x337b, 0x84}, + {0x337c, 0x16}, + {0x337d, 0x15}, + {0x337e, 0x80}, + {0x337f, 0x02}, + {0x3380, 0x10}, + {0x3381, 0xc4}, + {0x3382, 0x14}, + {0x3383, 0x15}, + {0x3384, 0xa9}, + {0x3385, 0x00}, + {0x3386, 0x10}, + {0x3387, 0x84}, + {0x3388, 0x16}, + {0x3389, 0x15}, + {0x338a, 0x41}, + {0x338b, 0x00}, + {0x338c, 0x10}, + {0x338d, 0x80}, + {0x338e, 0x16}, + {0x338f, 0x15}, + {0x3390, 0x02}, + {0x3391, 0x00}, + {0x3392, 0x10}, + {0x3393, 0xa0}, + {0x3394, 0x16}, + {0x3395, 0x15}, + {0x305c, 0x18}, + {0x305d, 0x00}, + {0x305e, 0x19}, + {0x305f, 0x00}, + {0x3396, 0x01}, + {0x3397, 0x00}, + {0x3398, 0x90}, + {0x3399, 0x30}, + {0x339a, 0x56}, + {0x339b, 0x57}, + {0x339c, 0x01}, + {0x339d, 0x00}, + {0x339e, 0x10}, + {0x339f, 0x20}, + {0x33a0, 0xd6}, + {0x33a1, 0x17}, + {0x33a2, 0x01}, + {0x33a3, 0x00}, + {0x33a4, 0x10}, + {0x33a5, 0x28}, + {0x33a6, 0xd6}, + {0x33a7, 0x17}, + {0x33a8, 0x03}, + {0x33a9, 0x00}, + {0x33aa, 0x10}, + {0x33ab, 0x20}, + {0x33ac, 0xd6}, + {0x33ad, 0x17}, + {0x33ae, 0x61}, + {0x33af, 0x00}, + {0x33b0, 0x10}, + {0x33b1, 0x20}, + {0x33b2, 0xd6}, + {0x33b3, 0x15}, + {0x33b4, 0x01}, + {0x33b5, 0x00}, + {0x33b6, 0x10}, + {0x33b7, 0x20}, + {0x33b8, 0xd6}, + {0x33b9, 0x1d}, + {0x33ba, 0x01}, + {0x33bb, 0x00}, + {0x33bc, 0x50}, + {0x33bd, 0x20}, + {0x33be, 0xd6}, + {0x33bf, 0x1d}, + {0x33c0, 0x2c}, + {0x33c1, 0x00}, + {0x33c2, 0x10}, + {0x33c3, 0x20}, + {0x33c4, 0xd6}, + {0x33c5, 0x1d}, + {0x33c6, 0x01}, + {0x33c7, 0x00}, + {0x33c8, 0x90}, + {0x33c9, 0x20}, + {0x33ca, 0xd6}, + {0x33cb, 0x1d}, + {0x33cc, 0x83}, + {0x33cd, 0x00}, + {0x33ce, 0x10}, + {0x33cf, 0x20}, + {0x33d0, 0xd6}, + {0x33d1, 0x15}, + {0x33d2, 0x01}, + {0x33d3, 0x00}, + {0x33d4, 0x10}, + {0x33d5, 0x30}, + {0x33d6, 0xd6}, + {0x33d7, 0x15}, + {0x33d8, 0x01}, + {0x33d9, 0x00}, + {0x33da, 0x10}, + {0x33db, 0x20}, + {0x33dc, 0xd6}, + {0x33dd, 0x15}, + {0x33de, 0x01}, + {0x33df, 0x00}, + {0x33e0, 0x10}, + {0x33e1, 0x20}, + {0x33e2, 0x56}, + {0x33e3, 0x15}, + {0x33e4, 0x07}, + {0x33e5, 0x00}, + {0x33e6, 0x10}, + {0x33e7, 0x20}, + {0x33e8, 0x16}, + {0x33e9, 0x15}, + {0x3060, 0x26}, + {0x3061, 0x00}, + {0x302a, 0xff}, + {0x302b, 0xff}, + {0x302c, 0xff}, + {0x302d, 0xff}, + {0x302e, 0x3f}, + {0x3013, 0x0b}, + // Related to ADC timing, do not adjust these + {0x102b,0xc2}, + {0x102c,0x1}, + {0x1035, 0x54}, + {0x1036, 0x00}, + {0x3090, 0x2a}, + {0x3091, 0x01}, + {0x30c6, 0x05}, + {0x30c7, 0x00}, + {0x30c8, 0x00}, + {0x30c9, 0x00}, + {0x30ca, 0x00}, + {0x30cb, 0x00}, + {0x30cc, 0x00}, + {0x30cd, 0x00}, + {0x30ce, 0x00}, + {0x30cf, 0x05}, + {0x30d0, 0x00}, + {0x30d1, 0x00}, + {0x30d2, 0x00}, + {0x30d3, 0x00}, + {0x30d4, 0x00}, + {0x30d5, 0x00}, + {0x30d6, 0x00}, + {0x30d7, 0x00}, + {0x30f3, 0x05}, + {0x30f4, 0x00}, + {0x30f5, 0x00}, + {0x30f6, 0x00}, + {0x30f7, 0x00}, + {0x30f8, 0x00}, + {0x30f9, 0x00}, + {0x30fa, 0x00}, + {0x30fb, 0x00}, + {0x30d8, 0x05}, + {0x30d9, 0x00}, + {0x30da, 0x00}, + {0x30db, 0x00}, + {0x30dc, 0x00}, + {0x30dd, 0x00}, + {0x30de, 0x00}, + {0x30df, 0x00}, + {0x30e0, 0x00}, + {0x30e1, 0x05}, + {0x30e2, 0x00}, + {0x30e3, 0x00}, + {0x30e4, 0x00}, + {0x30e5, 0x00}, + {0x30e6, 0x00}, + {0x30e7, 0x00}, + {0x30e8, 0x00}, + {0x30e9, 0x00}, + {0x30f3, 0x05}, + {0x30f4, 0x02}, + {0x30f5, 0x00}, + {0x30f6, 0x17}, + {0x30f7, 0x01}, + {0x30f8, 0x00}, + {0x30f9, 0x00}, + {0x30fa, 0x00}, + {0x30fb, 0x00}, + {0x30d8, 0x03}, + {0x30d9, 0x01}, + {0x30da, 0x00}, + {0x30db, 0x19}, + {0x30dc, 0x01}, + {0x30dd, 0x00}, + {0x30de, 0x00}, + {0x30df, 0x00}, + {0x30e0, 0x00}, + {0x30a2, 0x05}, + {0x30a3, 0x02}, + {0x30a4, 0x00}, + {0x30a5, 0x22}, + {0x30a6, 0x00}, + {0x30a7, 0x00}, + {0x30a8, 0x00}, + {0x30a9, 0x00}, + {0x30aa, 0x00}, + {0x30ab, 0x05}, + {0x30ac, 0x02}, + {0x30ad, 0x00}, + {0x30ae, 0x22}, + {0x30af, 0x00}, + {0x30b0, 0x00}, + {0x30b1, 0x00}, + {0x30b2, 0x00}, + {0x30b3, 0x00}, + {0x30bd, 0x05}, + {0x30be, 0x9f}, + {0x30bf, 0x00}, + {0x30c0, 0x7d}, + {0x30c1, 0x00}, + {0x30c2, 0x00}, + {0x30c3, 0x00}, + {0x30c4, 0x00}, + {0x30c5, 0x00}, + {0x30b4, 0x04}, + {0x30b5, 0x9c}, + {0x30b6, 0x00}, + {0x30b7, 0x7d}, + {0x30b8, 0x00}, + {0x30b9, 0x00}, + {0x30ba, 0x00}, + {0x30bb, 0x00}, + {0x30bc, 0x00}, + {0x30fc, 0x05}, + {0x30fd, 0x00}, + {0x30fe, 0x00}, + {0x30ff, 0x00}, + {0x3100, 0x00}, + {0x3101, 0x00}, + {0x3102, 0x00}, + {0x3103, 0x00}, + {0x3104, 0x00}, + {0x3105, 0x05}, + {0x3106, 0x00}, + {0x3107, 0x00}, + {0x3108, 0x00}, + {0x3109, 0x00}, + {0x310a, 0x00}, + {0x310b, 0x00}, + {0x310c, 0x00}, + {0x310d, 0x00}, + {0x3099, 0x05}, + {0x309a, 0x96}, + {0x309b, 0x00}, + {0x309c, 0x06}, + {0x309d, 0x00}, + {0x309e, 0x00}, + {0x309f, 0x00}, + {0x30a0, 0x00}, + {0x30a1, 0x00}, + {0x310e, 0x05}, + {0x310f, 0x02}, + {0x3110, 0x00}, + {0x3111, 0x2b}, + {0x3112, 0x00}, + {0x3113, 0x00}, + {0x3114, 0x00}, + {0x3115, 0x00}, + {0x3116, 0x00}, + {0x3117, 0x05}, + {0x3118, 0x02}, + {0x3119, 0x00}, + {0x311a, 0x2c}, + {0x311b, 0x00}, + {0x311c, 0x00}, + {0x311d, 0x00}, + {0x311e, 0x00}, + {0x311f, 0x00}, + {0x30ea, 0x00}, + {0x30eb, 0x00}, + {0x30ec, 0x00}, + {0x30ed, 0x00}, + {0x30ee, 0x00}, + {0x30ef, 0x00}, + {0x30f0, 0x00}, + {0x30f1, 0x00}, + {0x30f2, 0x00}, + {0x313b, 0x03}, + {0x313c, 0x31}, + {0x313d, 0x00}, + {0x313e, 0x07}, + {0x313f, 0x00}, + {0x3140, 0x68}, + {0x3141, 0x00}, + {0x3142, 0x34}, + {0x3143, 0x00}, + {0x31a0, 0x03}, + {0x31a1, 0x16}, + {0x31a2, 0x00}, + {0x31a3, 0x08}, + {0x31a4, 0x00}, + {0x31a5, 0x7e}, + {0x31a6, 0x00}, + {0x31a7, 0x08}, + {0x31a8, 0x00}, + {0x31a9, 0x03}, + {0x31aa, 0x16}, + {0x31ab, 0x00}, + {0x31ac, 0x08}, + {0x31ad, 0x00}, + {0x31ae, 0x7e}, + {0x31af, 0x00}, + {0x31b0, 0x08}, + {0x31b1, 0x00}, + {0x31b2, 0x03}, + {0x31b3, 0x16}, + {0x31b4, 0x00}, + {0x31b5, 0x08}, + {0x31b6, 0x00}, + {0x31b7, 0x7e}, + {0x31b8, 0x00}, + {0x31b9, 0x08}, + {0x31ba, 0x00}, + {0x3120, 0x05}, + {0x3121, 0x45}, + {0x3122, 0x00}, + {0x3123, 0x1d}, + {0x3124, 0x00}, + {0x3125, 0xa9}, + {0x3126, 0x00}, + {0x3127, 0x6d}, + {0x3128, 0x00}, + {0x3129, 0x05}, + {0x312a, 0x15}, + {0x312b, 0x00}, + {0x312c, 0x0a}, + {0x312d, 0x00}, + {0x312e, 0x45}, + {0x312f, 0x00}, + {0x3130, 0x1d}, + {0x3131, 0x00}, + {0x3132, 0x05}, + {0x3133, 0x7d}, + {0x3134, 0x00}, + {0x3135, 0x0a}, + {0x3136, 0x00}, + {0x3137, 0xa9}, + {0x3138, 0x00}, + {0x3139, 0x6d}, + {0x313a, 0x00}, + {0x3144, 0x05}, + {0x3145, 0x00}, + {0x3146, 0x00}, + {0x3147, 0x30}, + {0x3148, 0x00}, + {0x3149, 0x00}, + {0x314a, 0x00}, + {0x314b, 0x00}, + {0x314c, 0x00}, + {0x314d, 0x03}, + {0x314e, 0x00}, + {0x314f, 0x00}, + {0x3150, 0x31}, + {0x3151, 0x00}, + {0x3152, 0x00}, + {0x3153, 0x00}, + {0x3154, 0x00}, + {0x3155, 0x00}, + {0x31d8, 0x05}, + {0x31d9, 0x3a}, + {0x31da, 0x00}, + {0x31db, 0x2e}, + {0x31dc, 0x00}, + {0x31dd, 0x9e}, + {0x31de, 0x00}, + {0x31df, 0x7e}, + {0x31e0, 0x00}, + {0x31e1, 0x05}, + {0x31e2, 0x04}, + {0x31e3, 0x00}, + {0x31e4, 0x04}, + {0x31e5, 0x00}, + {0x31e6, 0x73}, + {0x31e7, 0x00}, + {0x31e8, 0x04}, + {0x31e9, 0x00}, + {0x31ea, 0x05}, + {0x31eb, 0x00}, + {0x31ec, 0x00}, + {0x31ed, 0x00}, + {0x31ee, 0x00}, + {0x31ef, 0x00}, + {0x31f0, 0x00}, + {0x31f1, 0x00}, + {0x31f2, 0x00}, + {0x31f3, 0x00}, + {0x31f4, 0x00}, + {0x31f5, 0x00}, + {0x31f6, 0x00}, + {0x31f7, 0x00}, + {0x31f8, 0x00}, + {0x31f9, 0x00}, + {0x31fa, 0x00}, + {0x31fb, 0x05}, + {0x31fc, 0x00}, + {0x31fd, 0x00}, + {0x31fe, 0x00}, + {0x31ff, 0x00}, + {0x3200, 0x00}, + {0x3201, 0x00}, + {0x3202, 0x00}, + {0x3203, 0x00}, + {0x3204, 0x00}, + {0x3205, 0x00}, + {0x3206, 0x00}, + {0x3207, 0x00}, + {0x3208, 0x00}, + {0x3209, 0x00}, + {0x320a, 0x00}, + {0x320b, 0x00}, + {0x3164, 0x05}, + {0x3165, 0x14}, + {0x3166, 0x00}, + {0x3167, 0x0c}, + {0x3168, 0x00}, + {0x3169, 0x44}, + {0x316a, 0x00}, + {0x316b, 0x1f}, + {0x316c, 0x00}, + {0x316d, 0x05}, + {0x316e, 0x7c}, + {0x316f, 0x00}, + {0x3170, 0x0c}, + {0x3171, 0x00}, + {0x3172, 0xa8}, + {0x3173, 0x00}, + {0x3174, 0x6f}, + {0x3175, 0x00}, + {0x31c4, 0x05}, + {0x31c5, 0x24}, + {0x31c6, 0x01}, + {0x31c7, 0x04}, + {0x31c8, 0x00}, + {0x31c9, 0x05}, + {0x31ca, 0x24}, + {0x31cb, 0x01}, + {0x31cc, 0x04}, + {0x31cd, 0x00}, + {0x31ce, 0x05}, + {0x31cf, 0x24}, + {0x31d0, 0x01}, + {0x31d1, 0x04}, + {0x31d2, 0x00}, + {0x31d3, 0x05}, + {0x31d4, 0x73}, + {0x31d5, 0x00}, + {0x31d6, 0xb1}, + {0x31d7, 0x00}, + {0x3176, 0x05}, + {0x3177, 0x10}, + {0x3178, 0x00}, + {0x3179, 0x56}, + {0x317a, 0x00}, + {0x317b, 0x00}, + {0x317c, 0x00}, + {0x317d, 0x00}, + {0x317e, 0x00}, + {0x317f, 0x05}, + {0x3180, 0x6a}, + {0x3181, 0x00}, + {0x3182, 0xad}, + {0x3183, 0x00}, + {0x3184, 0x00}, + {0x3185, 0x00}, + {0x3186, 0x00}, + {0x3187, 0x00}, + // Exposure time, in row lengths + {0x100c, 0x7e}, + {0x100d, 0x00}, + // Vertical blanking, in row lengths + {0x1012, 0x32}, + {0x1013, 0x0b}, + // Enable continuous running + {0x1002, 0x04}, + + // Context B settings + {0x110a, 0x78}, + {0x110b, 0x05}, + {0x110c, 0x00}, + {0x110d, 0x00}, + {0x1105, 0x00}, + {0x1106, 0x00}, + {0x209a, 0x00}, + {0x209b, 0x00}, + {0x401A, 0x08}, + {0x1103, 0x88}, + {0x1104, 0x2c}, + +}; + +// vga_640_480_120fps_12b_2lanes +static const struct mira220_reg vga_640_480_120fps_12b_2lanes_reg[] = { + // Disable internal LDOs connected to VDD25 + {0x401e,0x2}, + {0x4038,0x3b}, + // Sensor uploads + // Stop sensor at a row boundary (stop sensor in a known state) + // self.stop_img_capture('row') + // MIPI TX controller disable + // {0x1003,0x2}, + {0x6006,0x0}, + // MIPI 2 lane mode + {0x6012,0x1}, + {0x6013,0x0}, + {0x6006,0x1}, + {0x205d,0x0}, + {0x2063,0x0}, + {0x24dc,0x13}, + {0x24dd,0x3}, + {0x24de,0x3}, + {0x24df,0x0}, + // 2.24MP window + {0x4006,0x8}, + {0x401c,0x6f}, + // Row noise correction enabled, with flat field target of 100 + {0x204b,0x3}, + {0x205b,0x64}, + {0x205c,0x0}, + // Some default values for references (some trim values are in OTP, better to use those) + {0x4018,0x3f}, + {0x403b,0xb}, + {0x403e,0xe}, + {0x402b,0x6}, + // Misc. sensor settings that should not be touched + {0x1077,0x0}, + {0x1078,0x0}, + {0x1009,0x8}, + {0x100a,0x0}, + {0x110f,0x8}, + {0x1110,0x0}, + {0x1006,0x2}, + {0x402c,0x64}, + {0x3064,0x0}, + {0x3065,0xf0}, + {0x4013,0x13}, + {0x401f,0x9}, + {0x4020,0x13}, + {0x4044,0x75}, + {0x4027,0x0}, + {0x3215,0x69}, + {0x3216,0xf}, + {0x322b,0x69}, + {0x322c,0xf}, + {0x4051,0x80}, + {0x4052,0x10}, + {0x4057,0x80}, + {0x4058,0x10}, + {0x3212,0x59}, + {0x4047,0x8f}, + {0x4026,0x10}, + {0x4032,0x53}, + {0x4036,0x17}, + {0x50b8,0xf4}, + // Related to pixel timing, do not adjust these + {0x3016,0x0}, + {0x3017,0x2c}, + {0x3018,0x8c}, + {0x3019,0x45}, + {0x301a,0x5}, + {0x3013,0xa}, + {0x301b,0x0}, + {0x301c,0x4}, + {0x301d,0x88}, + {0x301e,0x45}, + {0x301f,0x5}, + {0x3020,0x0}, + {0x3021,0x4}, + {0x3022,0x88}, + {0x3023,0x45}, + {0x3024,0x5}, + {0x3025,0x0}, + {0x3026,0x4}, + {0x3027,0x88}, + {0x3028,0x45}, + {0x3029,0x5}, + {0x302f,0x0}, + {0x3056,0x0}, + {0x3057,0x0}, + {0x3300,0x1}, + {0x3301,0x0}, + {0x3302,0xb0}, + {0x3303,0xb0}, + {0x3304,0x16}, + {0x3305,0x15}, + {0x3306,0x1}, + {0x3307,0x0}, + {0x3308,0x30}, + {0x3309,0xa0}, + {0x330a,0x16}, + {0x330b,0x15}, + {0x330c,0x1}, + {0x330d,0x0}, + {0x330e,0x30}, + {0x330f,0xa0}, + {0x3310,0x16}, + {0x3311,0x15}, + {0x3312,0x1}, + {0x3313,0x0}, + {0x3314,0x30}, + {0x3315,0xa0}, + {0x3316,0x16}, + {0x3317,0x15}, + {0x3318,0x1}, + {0x3319,0x0}, + {0x331a,0x30}, + {0x331b,0xa0}, + {0x331c,0x16}, + {0x331d,0x15}, + {0x331e,0x1}, + {0x331f,0x0}, + {0x3320,0x30}, + {0x3321,0xa0}, + {0x3322,0x16}, + {0x3323,0x15}, + {0x3324,0x1}, + {0x3325,0x0}, + {0x3326,0x30}, + {0x3327,0xa0}, + {0x3328,0x16}, + {0x3329,0x15}, + {0x332a,0x2b}, + {0x332b,0x0}, + {0x332c,0x30}, + {0x332d,0xa0}, + {0x332e,0x16}, + {0x332f,0x15}, + {0x3330,0x1}, + {0x3331,0x0}, + {0x3332,0x10}, + {0x3333,0xa0}, + {0x3334,0x16}, + {0x3335,0x15}, + {0x3058,0x8}, + {0x3059,0x0}, + {0x305a,0x9}, + {0x305b,0x0}, + {0x3336,0x1}, + {0x3337,0x0}, + {0x3338,0x90}, + {0x3339,0xb0}, + {0x333a,0x16}, + {0x333b,0x15}, + {0x333c,0x1f}, + {0x333d,0x0}, + {0x333e,0x10}, + {0x333f,0xa0}, + {0x3340,0x16}, + {0x3341,0x15}, + {0x3342,0x52}, + {0x3343,0x0}, + {0x3344,0x10}, + {0x3345,0x80}, + {0x3346,0x16}, + {0x3347,0x15}, + {0x3348,0x1}, + {0x3349,0x0}, + {0x334a,0x10}, + {0x334b,0x80}, + {0x334c,0x16}, + {0x334d,0x1d}, + {0x334e,0x1}, + {0x334f,0x0}, + {0x3350,0x50}, + {0x3351,0x84}, + {0x3352,0x16}, + {0x3353,0x1d}, + {0x3354,0x18}, + {0x3355,0x0}, + {0x3356,0x10}, + {0x3357,0x84}, + {0x3358,0x16}, + {0x3359,0x1d}, + {0x335a,0x80}, + {0x335b,0x2}, + {0x335c,0x10}, + {0x335d,0xc4}, + {0x335e,0x14}, + {0x335f,0x1d}, + {0x3360,0xa5}, + {0x3361,0x0}, + {0x3362,0x10}, + {0x3363,0x84}, + {0x3364,0x16}, + {0x3365,0x1d}, + {0x3366,0x1}, + {0x3367,0x0}, + {0x3368,0x90}, + {0x3369,0x84}, + {0x336a,0x16}, + {0x336b,0x1d}, + {0x336c,0x12}, + {0x336d,0x0}, + {0x336e,0x10}, + {0x336f,0x84}, + {0x3370,0x16}, + {0x3371,0x15}, + {0x3372,0x32}, + {0x3373,0x0}, + {0x3374,0x30}, + {0x3375,0x84}, + {0x3376,0x16}, + {0x3377,0x15}, + {0x3378,0x26}, + {0x3379,0x0}, + {0x337a,0x10}, + {0x337b,0x84}, + {0x337c,0x16}, + {0x337d,0x15}, + {0x337e,0x80}, + {0x337f,0x2}, + {0x3380,0x10}, + {0x3381,0xc4}, + {0x3382,0x14}, + {0x3383,0x15}, + {0x3384,0xa9}, + {0x3385,0x0}, + {0x3386,0x10}, + {0x3387,0x84}, + {0x3388,0x16}, + {0x3389,0x15}, + {0x338a,0x41}, + {0x338b,0x0}, + {0x338c,0x10}, + {0x338d,0x80}, + {0x338e,0x16}, + {0x338f,0x15}, + {0x3390,0x2}, + {0x3391,0x0}, + {0x3392,0x10}, + {0x3393,0xa0}, + {0x3394,0x16}, + {0x3395,0x15}, + {0x305c,0x18}, + {0x305d,0x0}, + {0x305e,0x19}, + {0x305f,0x0}, + {0x3396,0x1}, + {0x3397,0x0}, + {0x3398,0x90}, + {0x3399,0x30}, + {0x339a,0x56}, + {0x339b,0x57}, + {0x339c,0x1}, + {0x339d,0x0}, + {0x339e,0x10}, + {0x339f,0x20}, + {0x33a0,0xd6}, + {0x33a1,0x17}, + {0x33a2,0x1}, + {0x33a3,0x0}, + {0x33a4,0x10}, + {0x33a5,0x28}, + {0x33a6,0xd6}, + {0x33a7,0x17}, + {0x33a8,0x3}, + {0x33a9,0x0}, + {0x33aa,0x10}, + {0x33ab,0x20}, + {0x33ac,0xd6}, + {0x33ad,0x17}, + {0x33ae,0x61}, + {0x33af,0x0}, + {0x33b0,0x10}, + {0x33b1,0x20}, + {0x33b2,0xd6}, + {0x33b3,0x15}, + {0x33b4,0x1}, + {0x33b5,0x0}, + {0x33b6,0x10}, + {0x33b7,0x20}, + {0x33b8,0xd6}, + {0x33b9,0x1d}, + {0x33ba,0x1}, + {0x33bb,0x0}, + {0x33bc,0x50}, + {0x33bd,0x20}, + {0x33be,0xd6}, + {0x33bf,0x1d}, + {0x33c0,0x2c}, + {0x33c1,0x0}, + {0x33c2,0x10}, + {0x33c3,0x20}, + {0x33c4,0xd6}, + {0x33c5,0x1d}, + {0x33c6,0x1}, + {0x33c7,0x0}, + {0x33c8,0x90}, + {0x33c9,0x20}, + {0x33ca,0xd6}, + {0x33cb,0x1d}, + {0x33cc,0x83}, + {0x33cd,0x0}, + {0x33ce,0x10}, + {0x33cf,0x20}, + {0x33d0,0xd6}, + {0x33d1,0x15}, + {0x33d2,0x1}, + {0x33d3,0x0}, + {0x33d4,0x10}, + {0x33d5,0x30}, + {0x33d6,0xd6}, + {0x33d7,0x15}, + {0x33d8,0x1}, + {0x33d9,0x0}, + {0x33da,0x10}, + {0x33db,0x20}, + {0x33dc,0xd6}, + {0x33dd,0x15}, + {0x33de,0x1}, + {0x33df,0x0}, + {0x33e0,0x10}, + {0x33e1,0x20}, + {0x33e2,0x56}, + {0x33e3,0x15}, + {0x33e4,0x7}, + {0x33e5,0x0}, + {0x33e6,0x10}, + {0x33e7,0x20}, + {0x33e8,0x16}, + {0x33e9,0x15}, + {0x3060,0x26}, + {0x3061,0x0}, + {0x302a,0xff}, + {0x302b,0xff}, + {0x302c,0xff}, + {0x302d,0xff}, + {0x302e,0x3f}, + {0x3013,0xb}, + // Related to ADC timing, do not adjust these + {0x102b,0xc2}, + {0x102c,0x1}, + {0x1035,0x54}, + {0x1036,0x0}, + {0x3090,0x2a}, + {0x3091,0x1}, + {0x30c6,0x5}, + {0x30c7,0x0}, + {0x30c8,0x0}, + {0x30c9,0x0}, + {0x30ca,0x0}, + {0x30cb,0x0}, + {0x30cc,0x0}, + {0x30cd,0x0}, + {0x30ce,0x0}, + {0x30cf,0x5}, + {0x30d0,0x0}, + {0x30d1,0x0}, + {0x30d2,0x0}, + {0x30d3,0x0}, + {0x30d4,0x0}, + {0x30d5,0x0}, + {0x30d6,0x0}, + {0x30d7,0x0}, + {0x30f3,0x5}, + {0x30f4,0x0}, + {0x30f5,0x0}, + {0x30f6,0x0}, + {0x30f7,0x0}, + {0x30f8,0x0}, + {0x30f9,0x0}, + {0x30fa,0x0}, + {0x30fb,0x0}, + {0x30d8,0x5}, + {0x30d9,0x0}, + {0x30da,0x0}, + {0x30db,0x0}, + {0x30dc,0x0}, + {0x30dd,0x0}, + {0x30de,0x0}, + {0x30df,0x0}, + {0x30e0,0x0}, + {0x30e1,0x5}, + {0x30e2,0x0}, + {0x30e3,0x0}, + {0x30e4,0x0}, + {0x30e5,0x0}, + {0x30e6,0x0}, + {0x30e7,0x0}, + {0x30e8,0x0}, + {0x30e9,0x0}, + {0x30f3,0x5}, + {0x30f4,0x2}, + {0x30f5,0x0}, + {0x30f6,0x17}, + {0x30f7,0x1}, + {0x30f8,0x0}, + {0x30f9,0x0}, + {0x30fa,0x0}, + {0x30fb,0x0}, + {0x30d8,0x3}, + {0x30d9,0x1}, + {0x30da,0x0}, + {0x30db,0x19}, + {0x30dc,0x1}, + {0x30dd,0x0}, + {0x30de,0x0}, + {0x30df,0x0}, + {0x30e0,0x0}, + {0x30a2,0x5}, + {0x30a3,0x2}, + {0x30a4,0x0}, + {0x30a5,0x22}, + {0x30a6,0x0}, + {0x30a7,0x0}, + {0x30a8,0x0}, + {0x30a9,0x0}, + {0x30aa,0x0}, + {0x30ab,0x5}, + {0x30ac,0x2}, + {0x30ad,0x0}, + {0x30ae,0x22}, + {0x30af,0x0}, + {0x30b0,0x0}, + {0x30b1,0x0}, + {0x30b2,0x0}, + {0x30b3,0x0}, + {0x30bd,0x5}, + {0x30be,0x9f}, + {0x30bf,0x0}, + {0x30c0,0x7d}, + {0x30c1,0x0}, + {0x30c2,0x0}, + {0x30c3,0x0}, + {0x30c4,0x0}, + {0x30c5,0x0}, + {0x30b4,0x4}, + {0x30b5,0x9c}, + {0x30b6,0x0}, + {0x30b7,0x7d}, + {0x30b8,0x0}, + {0x30b9,0x0}, + {0x30ba,0x0}, + {0x30bb,0x0}, + {0x30bc,0x0}, + {0x30fc,0x5}, + {0x30fd,0x0}, + {0x30fe,0x0}, + {0x30ff,0x0}, + {0x3100,0x0}, + {0x3101,0x0}, + {0x3102,0x0}, + {0x3103,0x0}, + {0x3104,0x0}, + {0x3105,0x5}, + {0x3106,0x0}, + {0x3107,0x0}, + {0x3108,0x0}, + {0x3109,0x0}, + {0x310a,0x0}, + {0x310b,0x0}, + {0x310c,0x0}, + {0x310d,0x0}, + {0x3099,0x5}, + {0x309a,0x96}, + {0x309b,0x0}, + {0x309c,0x6}, + {0x309d,0x0}, + {0x309e,0x0}, + {0x309f,0x0}, + {0x30a0,0x0}, + {0x30a1,0x0}, + {0x310e,0x5}, + {0x310f,0x2}, + {0x3110,0x0}, + {0x3111,0x2b}, + {0x3112,0x0}, + {0x3113,0x0}, + {0x3114,0x0}, + {0x3115,0x0}, + {0x3116,0x0}, + {0x3117,0x5}, + {0x3118,0x2}, + {0x3119,0x0}, + {0x311a,0x2c}, + {0x311b,0x0}, + {0x311c,0x0}, + {0x311d,0x0}, + {0x311e,0x0}, + {0x311f,0x0}, + {0x30ea,0x0}, + {0x30eb,0x0}, + {0x30ec,0x0}, + {0x30ed,0x0}, + {0x30ee,0x0}, + {0x30ef,0x0}, + {0x30f0,0x0}, + {0x30f1,0x0}, + {0x30f2,0x0}, + {0x313b,0x3}, + {0x313c,0x31}, + {0x313d,0x0}, + {0x313e,0x7}, + {0x313f,0x0}, + {0x3140,0x68}, + {0x3141,0x0}, + {0x3142,0x34}, + {0x3143,0x0}, + {0x31a0,0x3}, + {0x31a1,0x16}, + {0x31a2,0x0}, + {0x31a3,0x8}, + {0x31a4,0x0}, + {0x31a5,0x7e}, + {0x31a6,0x0}, + {0x31a7,0x8}, + {0x31a8,0x0}, + {0x31a9,0x3}, + {0x31aa,0x16}, + {0x31ab,0x0}, + {0x31ac,0x8}, + {0x31ad,0x0}, + {0x31ae,0x7e}, + {0x31af,0x0}, + {0x31b0,0x8}, + {0x31b1,0x0}, + {0x31b2,0x3}, + {0x31b3,0x16}, + {0x31b4,0x0}, + {0x31b5,0x8}, + {0x31b6,0x0}, + {0x31b7,0x7e}, + {0x31b8,0x0}, + {0x31b9,0x8}, + {0x31ba,0x0}, + {0x3120,0x5}, + {0x3121,0x45}, + {0x3122,0x0}, + {0x3123,0x1d}, + {0x3124,0x0}, + {0x3125,0xa9}, + {0x3126,0x0}, + {0x3127,0x6d}, + {0x3128,0x0}, + {0x3129,0x5}, + {0x312a,0x15}, + {0x312b,0x0}, + {0x312c,0xa}, + {0x312d,0x0}, + {0x312e,0x45}, + {0x312f,0x0}, + {0x3130,0x1d}, + {0x3131,0x0}, + {0x3132,0x5}, + {0x3133,0x7d}, + {0x3134,0x0}, + {0x3135,0xa}, + {0x3136,0x0}, + {0x3137,0xa9}, + {0x3138,0x0}, + {0x3139,0x6d}, + {0x313a,0x0}, + {0x3144,0x5}, + {0x3145,0x0}, + {0x3146,0x0}, + {0x3147,0x30}, + {0x3148,0x0}, + {0x3149,0x0}, + {0x314a,0x0}, + {0x314b,0x0}, + {0x314c,0x0}, + {0x314d,0x3}, + {0x314e,0x0}, + {0x314f,0x0}, + {0x3150,0x31}, + {0x3151,0x0}, + {0x3152,0x0}, + {0x3153,0x0}, + {0x3154,0x0}, + {0x3155,0x0}, + {0x31d8,0x5}, + {0x31d9,0x3a}, + {0x31da,0x0}, + {0x31db,0x2e}, + {0x31dc,0x0}, + {0x31dd,0x9e}, + {0x31de,0x0}, + {0x31df,0x7e}, + {0x31e0,0x0}, + {0x31e1,0x5}, + {0x31e2,0x4}, + {0x31e3,0x0}, + {0x31e4,0x4}, + {0x31e5,0x0}, + {0x31e6,0x73}, + {0x31e7,0x0}, + {0x31e8,0x4}, + {0x31e9,0x0}, + {0x31ea,0x5}, + {0x31eb,0x0}, + {0x31ec,0x0}, + {0x31ed,0x0}, + {0x31ee,0x0}, + {0x31ef,0x0}, + {0x31f0,0x0}, + {0x31f1,0x0}, + {0x31f2,0x0}, + {0x31f3,0x0}, + {0x31f4,0x0}, + {0x31f5,0x0}, + {0x31f6,0x0}, + {0x31f7,0x0}, + {0x31f8,0x0}, + {0x31f9,0x0}, + {0x31fa,0x0}, + {0x31fb,0x5}, + {0x31fc,0x0}, + {0x31fd,0x0}, + {0x31fe,0x0}, + {0x31ff,0x0}, + {0x3200,0x0}, + {0x3201,0x0}, + {0x3202,0x0}, + {0x3203,0x0}, + {0x3204,0x0}, + {0x3205,0x0}, + {0x3206,0x0}, + {0x3207,0x0}, + {0x3208,0x0}, + {0x3209,0x0}, + {0x320a,0x0}, + {0x320b,0x0}, + {0x3164,0x5}, + {0x3165,0x14}, + {0x3166,0x0}, + {0x3167,0xc}, + {0x3168,0x0}, + {0x3169,0x44}, + {0x316a,0x0}, + {0x316b,0x1f}, + {0x316c,0x0}, + {0x316d,0x5}, + {0x316e,0x7c}, + {0x316f,0x0}, + {0x3170,0xc}, + {0x3171,0x0}, + {0x3172,0xa8}, + {0x3173,0x0}, + {0x3174,0x6f}, + {0x3175,0x0}, + {0x31c4,0x5}, + {0x31c5,0x24}, + {0x31c6,0x1}, + {0x31c7,0x4}, + {0x31c8,0x0}, + {0x31c9,0x5}, + {0x31ca,0x24}, + {0x31cb,0x1}, + {0x31cc,0x4}, + {0x31cd,0x0}, + {0x31ce,0x5}, + {0x31cf,0x24}, + {0x31d0,0x1}, + {0x31d1,0x4}, + {0x31d2,0x0}, + {0x31d3,0x5}, + {0x31d4,0x73}, + {0x31d5,0x0}, + {0x31d6,0xb1}, + {0x31d7,0x0}, + {0x3176,0x5}, + {0x3177,0x10}, + {0x3178,0x0}, + {0x3179,0x56}, + {0x317a,0x0}, + {0x317b,0x0}, + {0x317c,0x0}, + {0x317d,0x0}, + {0x317e,0x0}, + {0x317f,0x5}, + {0x3180,0x6a}, + {0x3181,0x0}, + {0x3182,0xad}, + {0x3183,0x0}, + {0x3184,0x0}, + {0x3185,0x0}, + {0x3186,0x0}, + {0x3187,0x0}, + // Exposure time, in row lengths + {0x100c,0x7e}, + {0x100d,0x0}, + // Vertical blanking, in row lengths + {0x1012,0x32}, + {0x1013,0xb}, + // Enable continuous running + {0x1002,0x4}, + // TODO: check the 2 writes below + // {0x3013,0xa}, + // {0x3013,0xb}, + // Context B settings + {0x110a,0x78}, + {0x110b,0x5}, + {0x110c,0x0}, + {0x110d,0x0}, + {0x1105,0x0}, + {0x1106,0x0}, + {0x209a,0x0}, + {0x209b,0x0}, + // TODO: check the 2 writes below + // {0x1113,0x2c}, + // {0x1114,0x1}, + {0x401a,0x8}, + {0x1103,0x88}, + {0x1104,0x2c}, + // TODO: check the 2 writes below + // {0x1003,0x10}, + // {0x1002,0x4}, + // {0x10f0,0x1}, + // setting bpp 12bit + // {0x1003,0x4}, + {0x209e,0x2}, + {0x208d,0x4}, + // {0x1003,0x10}, + // {0x1002,0x4}, + // {0x10f0,0x1}, + {0x1003,0x4}, + // setting roi w640 h480 + {0x1087,0xe0}, + {0x1088,0x1}, + {0x107d,0xcc}, + {0x107e,0x1}, + {0x2008,0x40}, + {0x2009,0x1}, + {0x200a,0xf0}, + {0x200b,0x0}, + {0x207d,0x80}, + {0x207e,0x2}, + // {0x1003,0x10}, + // {0x1002,0x4}, + // {0x10f0,0x1}, + // setting fps 120 + {0x1012,0x4a}, + {0x1013,0x2}, + // setting analog gain to 1 + {0x400a,0x8}, + {0x4009,0x1e}, + // setting mipi speed to 1000 + // {0x1003,0x4}, + {0x6006,0x0}, + {0x5004,0x1}, + {0x5086,0x2}, + {0x5087,0x34}, + {0x5088,0x0}, + {0x5090,0x0}, + {0x5091,0x5}, + {0x5092,0xe}, + {0x5093,0xb}, + {0x5094,0x4}, + {0x5095,0x22}, + {0x5096,0xb}, + {0x5097,0x0}, + {0x5098,0xd}, + {0x5004,0x0}, + {0x2066,0x0}, + {0x2067,0xc}, + {0x206e,0x80}, + {0x206f,0xb}, + {0x20ac,0x80}, + {0x20ad,0xb}, + {0x2076,0x0}, + {0x2077,0x6}, + {0x20b4,0x0}, + {0x20b5,0x6}, + {0x2078,0x1e}, + {0x2079,0x6}, + {0x20b6,0x1e}, + {0x20b7,0x6}, + {0x207a,0xd4}, + {0x207b,0x6}, + {0x20b8,0xd4}, + {0x20b9,0x6}, + {0x6006,0x1}, + {0x102b,0xc2}, + {0x102c,0x1}, + // {0x1003,0x10}, + // {0x1002,0x4}, + // {0x10f0,0x1}, +}; + +static const char * const mira220_test_pattern_menu[] = { + "Disabled", + "Vertial Gradient", +}; + +static const int mira220_test_pattern_val[] = { + MIRA220_TEST_PATTERN_DISABLE, + MIRA220_TEST_PATTERN_VERTICAL_GRADIENT, +}; + + +/* regulator supplies */ +static const char * const mira220_supply_name[] = { + // TODO(jalv): Check supply names + /* Supplies can be enabled in any order */ + "VANA", /* Analog (2.8V) supply */ + "VDIG", /* Digital Core (1.8V) supply */ + "VDDL", /* IF (1.2V) supply */ +}; + +#define MIRA220_NUM_SUPPLIES ARRAY_SIZE(mira220_supply_name) + +/* + * The supported formats. All flip/mirror combinations have the same byte order because the sensor + * is monochrome + */ +static const u32 codes[] = { + //MEDIA_BUS_FMT_Y8_1X8, + //MEDIA_BUS_FMT_Y10_1X10, + //MEDIA_BUS_FMT_Y12_1X12, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGRBG12_1X12, +}; + +/* Mode configs */ +static const struct mira220_mode supported_modes[] = { + + /* 2 MPx 30fps 12bpp mode */ + { + .width = 1600, + .height = 1400, + .crop = { + .left = MIRA220_PIXEL_ARRAY_LEFT, + .top = MIRA220_PIXEL_ARRAY_TOP, + .width = 1600, + .height = 1400 + }, + .reg_list = { + .num_of_regs = ARRAY_SIZE(full_1600_1400_30fps_12b_2lanes_reg), + .regs = full_1600_1400_30fps_12b_2lanes_reg, + }, + // vblank is ceil(MIRA220_GLOB_NUM_CLK_CYCLES / ROW_LENGTH) + 11 + // ROW_LENGTH is configured by register 0x102B, 0x102C. + .row_length = 450, + .pixel_rate = MIRA220_PIXEL_RATE, + .min_vblank = 16, // ceil(1928 / 300) + 11 + .max_vblank = 50000, // ceil(1928 / 300) + 11 + .hblank = MIRA220_HBLANK_1600x1400_30FPS, // TODO + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + }, + + /* VGA 120fps 8bpp mode */ + { + .width = 640, + .height = 480, + .crop = { + .left = 480, + .top = 460, + .width = 640, + .height = 480 + }, + .reg_list = { + .num_of_regs = ARRAY_SIZE(vga_640_480_120fps_12b_2lanes_reg), + .regs = vga_640_480_120fps_12b_2lanes_reg, + }, + // vblank is ceil(MIRA220_GLOB_NUM_CLK_CYCLES / ROW_LENGTH) + 11 + // ROW_LENGTH is configured by register 0x102B, 0x102C. + .row_length = 450, + .pixel_rate = MIRA220_PIXEL_RATE, + .min_vblank = 16, // ceil(1928 / 300) + 11 + .max_vblank = 50000, // ceil(1928 / 300) + 11 + .hblank = MIRA220_HBLANK_640x480_120FPS, // TODO + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + } + +}; + +struct mira220 { + struct v4l2_subdev sd; + struct media_pad pad[NUM_PADS]; + + struct v4l2_mbus_framefmt fmt; + + struct clk *xclk; /* system clock to MIRA220 */ + u32 xclk_freq; + + //struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[MIRA220_NUM_SUPPLIES]; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + // custom v4l2 control + struct v4l2_ctrl *mira220_reg_w; + struct v4l2_ctrl *mira220_reg_r; + u16 mira220_reg_w_cached_addr; + u8 mira220_reg_w_cached_flag; + + + /* Current mode */ + const struct mira220_mode *mode; + /* Whether to skip base register sequence upload */ + u32 skip_reg_upload; + /* Whether to reset sensor when stream on/off */ + u32 skip_reset; + /* Whether regulator and clk are powered on */ + u32 powered; + /* A flag to temporarily force power off */ + u32 force_power_off; + /* A flag to force write_start/stop_streaming_regs even if (skip_reg_upload==1) */ + u8 force_stream_ctrl; + /* Illumination trigger width/length. Use [15:0] for 16-bit register, use bit [16] for sign. */ + u32 illum_width; + /* Illumination trigger delay. Use [15:0] for 16-bit register. */ + u32 illum_delay; + + /* + * Mutex for serialized access: + * Protect sensor module set pad format and start/stop streaming safely. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; + + /* pmic, uC, LED */ + struct i2c_client *pmic_client; + struct i2c_client *uc_client; + struct i2c_client *led_client; + /* User specified I2C device address */ + u32 tbd_client_i2c_addr; + +}; + +static inline struct mira220 *to_mira220(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct mira220, sd); +} + +static int mira220_read(struct mira220 *mira220, u16 reg, u8 *val) +{ + int ret; + unsigned char data_w[2] = { reg >> 8, reg & 0xff }; + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + + 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; +} + +static int mira220_write(struct mira220 *mira220, u16 reg, u8 val) +{ + int ret; + unsigned char data[3] = { reg >> 8, reg & 0xff, val}; + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + + 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 mira220_write16(struct mira220 *mira220, u16 reg, u16 val) +{ + int ret; + unsigned char data[4] = { reg >> 8, reg & 0xff, val & 0xff, val >> 8}; + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + + ret = i2c_master_send(client, data, 4); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 4) { + ret = 0; + } else { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + if (ret >= 0) + ret = -EINVAL; + } + + return ret; +} + +/* Write a list of registers */ +static int mira220_write_regs(struct mira220 *mira220, + const struct mira220_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = mira220_write(mira220, 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; + } else { + // Debug code below + //u8 val; + //ret = mira220_read(mira220, regs[i].address, &val); + //printk(KERN_INFO "[MIRA220]: Read reg 0x%4.4x, val = 0x%x.\n", + // regs[i].address, val); + } + } + + return 0; +} + +static int mira220pmic_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret; + unsigned char data[2] = { reg & 0xff, val}; + + ret = i2c_master_send(client, data, 2); + /* + * Writing the wrong number of bytes also needs to be flagged as an + * error. Success needs to produce a 0 return code. + */ + if (ret == 2) { + 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 mira220pmic_read(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msgs[2]; + u8 addr_buf[1] = { reg & 0xff }; + u8 data_buf[1] = { 0 }; + int ret; + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = &data_buf[0]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = (u8)(data_buf[0]); + + return 0; +} + +/* Power/clock management functions */ +static int mira220_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira220 *mira220 = to_mira220(sd); + int ret = -EINVAL; + + printk(KERN_INFO "[MIRA220]: Entering power on function.\n"); + + /* The mira220_power_on() function is called at three places: + * (1) by mira220_probe when driver is loaded + * (2) by POWER_ON command when manually issued by user + * (3) by mira050_start_streaming when user starts capturing + * Reset must happen at (1). + * Reset must not happen at (3) if user skip_reg_upload, + * because that invalidas user register upload prior to (3). + * Therefore, avoid reset if (skip_reg_upload == 1), + * or if (skip_reset == 1). + */ + if (mira220->skip_reset == 0 && mira220->skip_reg_upload == 0) { + /* Pull reset to low if it is high */ + if (mira220->powered == 1) { + ret = regulator_bulk_disable(MIRA220_NUM_SUPPLIES, mira220->supplies); + if (ret) { + dev_err(&client->dev, "%s: failed to disable regulators\n", + __func__); + return ret; + } + clk_disable_unprepare(mira220->xclk); + usleep_range(MIRA220_XCLR_MIN_DELAY_US, + MIRA220_XCLR_MIN_DELAY_US + MIRA220_XCLR_DELAY_RANGE_US); + mira220->powered = 0; + } else { + printk(KERN_INFO "[MIRA220]: Skip disabling regulator and clk due to mira220->powered == %d.\n", mira220->powered); + } + } else { + printk(KERN_INFO "[MIRA220]: Skip pulling reset to low due to mira220->skip_reset=%u.\n", mira220->skip_reset); + } + + /* Alway enable regulator even if (skip_reset == 1) */ + if (mira220->powered == 0) { + ret = regulator_bulk_enable(MIRA220_NUM_SUPPLIES, mira220->supplies); + if (ret) { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + ret = clk_prepare_enable(mira220->xclk); + if (ret) { + dev_err(&client->dev, "%s: failed to enable clock\n", + __func__); + goto reg_off; + } + // gpiod_set_value_cansleep(mira220->reset_gpio, 1); + usleep_range(MIRA220_XCLR_MIN_DELAY_US, + MIRA220_XCLR_MIN_DELAY_US + MIRA220_XCLR_DELAY_RANGE_US); + mira220->powered = 1; + } else { + printk(KERN_INFO "[MIRA220]: Skip regulator and clk enable, because mira220->powered == %d.\n", mira220->powered); + } + + return 0; + +reg_off: + ret = regulator_bulk_disable(MIRA220_NUM_SUPPLIES, mira220->supplies); + mira220->powered = 0; + return ret; +} + +static int mira220_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira220 *mira220 = to_mira220(sd); + (void)mira220; + printk(KERN_INFO "[MIRA220]: Entering power off function.\n"); + + /* Keep reset pin high, due to mira220 consums max power when reset pin is low */ + if (mira220->force_power_off == 1) { + if (mira220->powered == 1) { + regulator_bulk_disable(MIRA220_NUM_SUPPLIES, mira220->supplies); + clk_disable_unprepare(mira220->xclk); + mira220->powered = 0; + } else { + printk(KERN_INFO "[MIRA220]: Skip disabling regulator and clk due to mira220->powered == %d.\n", mira220->powered); + } + } else { + printk(KERN_INFO "[MIRA220]: Skip disabling regulator and clk due to mira220->force_power_off=%u.\n", mira220->force_power_off); + } + + return 0; +} + +static int mira220_write_illum_trig_regs(struct mira220* mira220, u8 enable) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira220->sd); + int ret = 0; + u16 illum_width_reg; + u16 illum_delay_reg; + u8 illum_delay_sign; + + // Enable or disable illumination trigger + printk(KERN_INFO "[MIRA220]: Writing EN_TRIG_ILLUM to %d.\n", enable); + ret = mira220_write(mira220, MIRA220_EN_TRIG_ILLUM_REG, enable); + if (ret) { + dev_err(&client->dev, "Error setting EN_TRIG_ILLUM to %d.", enable); + return ret; + } + + // Set illumination width. Write 16 bits [15:0]. + illum_width_reg = (u16)(mira220->illum_width & 0x0000FFFF); + printk(KERN_INFO "[MIRA220]: Writing ILLUM_WIDTH to %u.\n", illum_width_reg); + ret = mira220_write16(mira220, MIRA220_ILLUM_WIDTH_REG, illum_width_reg); + if (ret) { + dev_err(&client->dev, "Error setting ILLUM_WIDTH to %u.", illum_width_reg); + return ret; + } + + // Set illumination delay. Write 16 bits [15:0] as absolute delay, and bit [16] as sign. + illum_delay_reg = (u16)(mira220->illum_delay & 0x0000FFFF); + printk(KERN_INFO "[MIRA220]: Writing ILLUM_DELAY to %u.\n", illum_delay_reg); + ret = mira220_write16(mira220, MIRA220_ILLUM_DELAY_REG, illum_delay_reg); + if (ret) { + dev_err(&client->dev, "Error setting ILLUM_DELAY to %u.", illum_delay_reg); + return ret; + } + // Set illumination delay sign. Extract bit [16] as sign. + illum_delay_sign = (u8)((mira220->illum_delay >> 16) & 0x1); + printk(KERN_INFO "[MIRA220]: Writing ILLUM_DELAY_SIGN to %u.\n", illum_delay_sign); + ret = mira220_write(mira220, MIRA220_ILLUM_DELAY_SIGN_REG, illum_delay_sign); + if (ret) { + dev_err(&client->dev, "Error setting ILLUM_DELAY_SIGN to %u.", illum_delay_sign); + return ret; + } + + return ret; +} + + +static int mira220_write_start_streaming_regs(struct mira220* mira220) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira220->sd); + int ret = 0; + + // Setting master control + ret = mira220_write(mira220, MIRA220_IMAGER_STATE_REG, + MIRA220_IMAGER_STATE_MASTER_CONTROL); + if (ret) { + dev_err(&client->dev, "Error setting master control"); + return ret; + } + + // Enable continuous streaming + ret = mira220_write(mira220, MIRA220_IMAGER_RUN_CONT_REG, + MIRA220_IMAGER_RUN_CONT_ENABLE); + if (ret) { + dev_err(&client->dev, "Error enabling continuous streaming"); + return ret; + } + + ret = mira220_write(mira220, MIRA220_IMAGER_RUN_REG, + MIRA220_IMAGER_RUN_START); + if (ret) { + dev_err(&client->dev, "Error setting internal trigger"); + return ret; + } + + return ret; +} + +static int mira220_write_stop_streaming_regs(struct mira220* mira220) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira220->sd); + int ret = 0; + u32 frame_time; + int try_cnt; + + for (try_cnt = 0; try_cnt < 5; try_cnt++) { + ret = mira220_write(mira220, MIRA220_IMAGER_STATE_REG, + MIRA220_IMAGER_STATE_STOP_AT_ROW); + if (ret) { + dev_err(&client->dev, "Error setting stop-at-row imager state at try %d", try_cnt); + usleep_range(1000, 1100); + } else { + break; + } + } + if (ret) { + dev_err(&client->dev, "Error setting stop-at-row imager state after multiple attempts. Exiting."); + return ret; + } + + ret = mira220_write(mira220, MIRA220_IMAGER_RUN_REG, + MIRA220_IMAGER_RUN_STOP); + if (ret) { + dev_err(&client->dev, "Error setting run reg to stop"); + return ret; + } + + /* + * Wait for one frame to make sure sensor is set to + * software standby in V-blank + * + * frame_time = frame length rows * Tline + * Tline = line length / pixel clock (in MHz) + */ + frame_time = MIRA220_DEFAULT_FRAME_LENGTH * + MIRA220_DEFAULT_LINE_LENGTH / MIRA220_DEFAULT_PIXEL_CLOCK; + + usleep_range(frame_time, frame_time + 1000); //TODO, set to 1 frametime + + return ret; +} + + +static int mira220_v4l2_reg_w(struct mira220 *mira220, u32 value) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira220->sd); + u32 ret = 0; + u32 tmp_flag; + + u16 reg_addr = (value >> 8) & 0xFFFF; + u8 reg_val = value & 0xFF; + u8 reg_flag = (value >> 24) & 0xFF; + + // printk(KERN_INFO "[MIRA220]: %s reg_flag: 0x%02X; reg_addr: 0x%04X; reg_val: 0x%02X.\n", + // __func__, reg_flag, reg_addr, reg_val); + + if (reg_flag & AMS_CAMERA_CID_MIRA220_REG_FLAG_CMD_SEL) { + if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_SLEEP_US) { + // If it is for sleep, combine all 24 bits of reg_addr and reg_val as sleep us. + u32 sleep_us_val = value & 0x00FFFFFF; + // Sleep range needs an interval, default to 1/8 of the sleep value. + u32 sleep_us_interval = sleep_us_val >> 3; + printk(KERN_INFO "[MIRA220]: %s sleep_us: %u.\n", __func__, sleep_us_val); + usleep_range(sleep_us_val, sleep_us_val + sleep_us_interval); + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_RESET_ON) { + printk(KERN_INFO "[MIRA220]: %s Enable reset at stream on/off.\n", __func__); + mira220->skip_reset = 0; + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_RESET_OFF) { + printk(KERN_INFO "[MIRA220]: %s Disable reset at stream on/off.\n", __func__); + mira220->skip_reset = 1; + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_REG_UP_ON) { + printk(KERN_INFO "[MIRA220]: %s Enable base register sequence upload.\n", __func__); + mira220->skip_reg_upload = 0; + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_REG_UP_OFF) { + printk(KERN_INFO "[MIRA220]: %s Disable base register sequence upload.\n", __func__); + mira220->skip_reg_upload = 1; + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_POWER_ON) { + printk(KERN_INFO "[MIRA220]: %s Call power on function mira220_power_on().\n", __func__); + /* Temporarily disable skip_reset if manually doing power on/off */ + tmp_flag = mira220->skip_reset; + mira220->skip_reset = 0; + mira220_power_on(&client->dev); + mira220->skip_reset = tmp_flag; + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_POWER_OFF) { + printk(KERN_INFO "[MIRA220]: %s Call power off function mira220_power_off().\n", __func__); + /* Temporarily disable skip_reset if manually doing power on/off */ + mira220->force_power_off = 1; + mira220_power_off(&client->dev); + mira220->force_power_off = 0; + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_ILLUM_TRIG_ON) { + printk(KERN_INFO "[MIRA220]: %s Enable illumination trigger.\n", __func__); + mira220_write_illum_trig_regs(mira220, 1); + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_ILLUM_TRIG_OFF) { + printk(KERN_INFO "[MIRA220]: %s Disable illumination trigger.\n", __func__); + mira220_write_illum_trig_regs(mira220, 0); + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_ILLUM_WIDTH) { + // Combine 16 bits, [15:0], of reg_addr and reg_val as ILLUM_WIDTH. + u32 illum_width = value & 0x0000FFFF; + printk(KERN_INFO "[MIRA220]: %s Set ILLUM_WIDTH to 0x%X.\n", __func__, illum_width); + mira220->illum_width = illum_width; + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_ILLUM_DELAY) { + // Combine 17 bits, [16:0], of reg_addr and reg_val as ILLUM_DELAY. Bit [16] is sign. + u32 illum_delay = value & 0x0001FFFF; + printk(KERN_INFO "[MIRA220]: %s Set ILLUM_DELAY with sign bit to 0x%X.\n", __func__, illum_delay); + mira220->illum_delay = illum_delay; + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_STREAM_CTRL_ON) { + printk(KERN_INFO "[MIRA220]: %s Force stream control even if (skip_reg_upload == 1).\n", __func__); + mira220->force_stream_ctrl = 1; + } else if (reg_flag == AMS_CAMERA_CID_MIRA220_REG_FLAG_STREAM_CTRL_OFF) { + printk(KERN_INFO "[MIRA220]: %s Disable stream control if (skip_reg_upload == 1).\n", __func__); + mira220->force_stream_ctrl = 0; + } else { + printk(KERN_INFO "[MIRA220]: %s unknown command from flag %u, ignored.\n", __func__, reg_flag); + } + } else if (reg_flag & AMS_CAMERA_CID_MIRA220_REG_FLAG_FOR_READ) { + // If it is for read, skip reagister write, cache addr and flag for read. + mira220->mira220_reg_w_cached_addr = reg_addr; + mira220->mira220_reg_w_cached_flag = reg_flag; + } else { + // If it is for write, select which I2C device by the flag "I2C_SEL". + if ((reg_flag & AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_MIRA) { + // Writing the actual Mira220 register + // printk(KERN_INFO "[MIRA220]: %s write reg_addr: 0x%04X; reg_val: 0x%02X.\n", __func__, reg_addr, reg_val); + ret = mira220_write(mira220, reg_addr, reg_val); + if (ret) { + dev_err_ratelimited(&client->dev, "Error AMS_CAMERA_CID_MIRA_REG_W reg_addr %X.\n", reg_addr); + return -EINVAL; + } + } else if ((reg_flag & AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_SET_TBD) { + /* User tries to set TBD I2C address, store reg_val to mira220->tbd_client_i2c_addr. Skip write. */ + printk(KERN_INFO "[MIRA220]: mira220->tbd_client_i2c_addr = 0x%X.\n", reg_val); + mira220->tbd_client_i2c_addr = reg_val; + } else if ((reg_flag & AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_TBD) { + if (mira220->tbd_client_i2c_addr == MIRA220PMIC_I2C_ADDR) { + // Write PMIC. Use pre-allocated mira220->pmic_client. + printk(KERN_INFO "[MIRA220]: write pmic_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira220pmic_write(mira220->pmic_client, (u8)(reg_addr & 0xFF), reg_val); + } else if (mira220->tbd_client_i2c_addr == MIRA220UC_I2C_ADDR) { + // Write micro-controller. Use pre-allocated mira220->uc_client. + printk(KERN_INFO "[MIRA220]: write uc_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira220pmic_write(mira220->uc_client, (u8)(reg_addr & 0xFF), reg_val); + } else if (mira220->tbd_client_i2c_addr == MIRA220LED_I2C_ADDR) { + // Write LED driver. Use pre-allocated mira220->led_client. + printk(KERN_INFO "[MIRA220]: write led_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + ret = mira220pmic_write(mira220->led_client, (u8)(reg_addr & 0xFF), reg_val); + } else { + /* Write other TBD I2C address. + * The TBD I2C address is set via AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_SET_TBD. + * The TBD I2C address is stored in mira220->tbd_client_i2c_addr. + * A temporary I2C client, tmp_client, is created and then destroyed (unregistered). + */ + struct i2c_client *tmp_client; + tmp_client = i2c_new_dummy_device(client->adapter, mira220->tbd_client_i2c_addr); + if (IS_ERR(tmp_client)) + return PTR_ERR(tmp_client); + printk(KERN_INFO "[MIRA220]: write tbd_client, i2c_addr %u, reg_addr 0x%X, reg_val 0x%X.\n", + mira220->tbd_client_i2c_addr, (u8)(reg_addr & 0xFF), reg_val); + ret = mira220pmic_write(tmp_client, (u8)(reg_addr & 0xFF), reg_val); + i2c_unregister_device(tmp_client); + } + } + } + + return 0; +} + +static int mira220_v4l2_reg_r(struct mira220 *mira220, u32 *value) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira220->sd); + u32 ret = 0; + + u16 reg_addr = mira220->mira220_reg_w_cached_addr; + u8 reg_flag = mira220->mira220_reg_w_cached_flag; + u8 reg_val = 0; + + *value = 0; + + if ((reg_flag & AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_MIRA) { + ret = mira220_read(mira220, reg_addr, ®_val); + if (ret) { + dev_err_ratelimited(&client->dev, "Error AMS_CAMERA_CID_MIRA_REG_R reg_addr %X.\n", reg_addr); + return -EINVAL; + } + } else if ((reg_flag & AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_SEL) == AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_TBD) { + if (mira220->tbd_client_i2c_addr == MIRA220PMIC_I2C_ADDR) { + // Read PMIC. Use pre-allocated mira220->pmic_client. + ret = mira220pmic_read(mira220->pmic_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA220]: read pmic_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } else if (mira220->tbd_client_i2c_addr == MIRA220UC_I2C_ADDR) { + // Read micro-controller. Use pre-allocated mira220->uc_client. + ret = mira220pmic_read(mira220->uc_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA220]: read uc_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } else if (mira220->tbd_client_i2c_addr == MIRA220LED_I2C_ADDR) { + // Read LED driver. Use pre-allocated mira220->led_client. + ret = mira220pmic_read(mira220->led_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA220]: read led_client, reg_addr 0x%X, reg_val 0x%X.\n", (u8)(reg_addr & 0xFF), reg_val); + } else { + /* Read other TBD I2C address. + * The TBD I2C address is set via AMS_CAMERA_CID_MIRA220_REG_FLAG_I2C_SET_TBD. + * The TBD I2C address is stored in mira220->tbd_client_i2c_addr. + * A temporary I2C client, tmp_client, is created and then destroyed (unregistered). + */ + struct i2c_client *tmp_client; + tmp_client = i2c_new_dummy_device(client->adapter, mira220->tbd_client_i2c_addr); + if (IS_ERR(tmp_client)) + return PTR_ERR(tmp_client); + ret = mira220pmic_read(tmp_client, (u8)(reg_addr & 0xFF), ®_val); + printk(KERN_INFO "[MIRA220]: read tbd_client, i2c_addr %u, reg_addr 0x%X, reg_val 0x%X.\n", + mira220->tbd_client_i2c_addr, (u8)(reg_addr & 0xFF), reg_val); + i2c_unregister_device(tmp_client); + } + } + + // Return 32-bit value that includes flags, addr, and register value + *value = ((u32)reg_flag << 24) | ((u32)reg_addr << 8) | (u32)reg_val; + + // printk(KERN_INFO "[MIRA220]: mira220_v4l2_reg_r() reg_flag: 0x%02X; reg_addr: 0x%04X, reg_val: 0x%02X.\n", + // reg_flag, reg_addr, reg_val); + + return 0; +} + +// Returns the maximum exposure time in row_length (reg value). +// Calculation is baded on Mira220 datasheet Section 9.2. +static u32 mira220_calculate_max_exposure_time(u32 vsize, + u32 vblank) { + return (vsize + vblank) - (int)(MIRA220_GLOB_NUM_CLK_CYCLES / MIRA220_MIN_ROW_LENGTH); +} + +static int mira220_write_analog_gain_reg(struct mira220 *mira220, u8 gain) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira220->sd); + u8 reg_value; + u32 ret; + + if ((gain < MIRA220_ANALOG_GAIN_MIN) || (gain > MIRA220_ANALOG_GAIN_MAX)) { + return -EINVAL; + } + + reg_value = (u8)(8 / gain); + + ret = mira220_write(mira220, MIRA220_ANALOG_GAIN_REG, reg_value); + + if (ret) { + dev_err_ratelimited(&client->dev, "Error setting analog gain register to %d", + reg_value); + } + + return ret; +} + +static int mira220_write_exposure_reg(struct mira220 *mira220, u32 exposure) { + struct i2c_client* const client = v4l2_get_subdevdata(&mira220->sd); + const u32 max_exposure = mira220_calculate_max_exposure_time( + mira220->mode->height, mira220->mode->min_vblank); + u32 ret = 0; + u32 capped_exposure = exposure; + + if (exposure > max_exposure) { + capped_exposure = max_exposure; + } + + printk(KERN_INFO "[MIRA220]: exposure fun width %d, hblank %d, vblank %d, row len %d, ctrl->val %d.\n", + mira220->mode->width, mira220->hblank->val, mira220->vblank->val, MIRA220_MIN_ROW_LENGTH, exposure); + ret = mira220_write16(mira220, MIRA220_EXP_TIME_LO_REG, capped_exposure); + if (ret) { + dev_err_ratelimited(&client->dev, "Error setting exposure time to %d", capped_exposure); + return -EINVAL; + } + + return 0; +} + +// Gets the format code if supported. Otherwise returns the default format code `codes[0]` +static u32 mira220_validate_format_code_or_default(struct mira220 *mira220, u32 code) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + unsigned int i; + + lockdep_assert_held(&mira220->mutex); + + for (i = 0; i < ARRAY_SIZE(codes); i++) + if (codes[i] == code) + break; + + if (i >= ARRAY_SIZE(codes)) { + dev_err_ratelimited(&client->dev, "Could not set requested format code %u", code); + dev_err_ratelimited(&client->dev, "Using default format %u", codes[0]); + i = 0; + } + + return codes[i]; +} + +static void mira220_set_default_format(struct mira220 *mira220) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = &mira220->fmt; + fmt->code = supported_modes[0].code; + 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); + fmt->width = supported_modes[0].width; + fmt->height = supported_modes[0].height; + fmt->field = V4L2_FIELD_NONE; +} + +static int mira220_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct mira220 *mira220 = to_mira220(sd); + struct v4l2_mbus_framefmt *try_fmt_img = + v4l2_subdev_get_try_format(sd, fh->state, IMAGE_PAD); + struct v4l2_mbus_framefmt *try_fmt_meta = + v4l2_subdev_get_try_format(sd, fh->state, METADATA_PAD); + struct v4l2_rect *try_crop; + + mutex_lock(&mira220->mutex); + + /* Initialize try_fmt for the image pad */ + try_fmt_img->width = supported_modes[0].width; + try_fmt_img->height = supported_modes[0].height; + try_fmt_img->code = mira220_validate_format_code_or_default(mira220, + supported_modes[0].code); + try_fmt_img->field = V4L2_FIELD_NONE; + + /* TODO(jalv): Initialize try_fmt for the embedded metadata pad */ + try_fmt_meta->width = MIRA220_EMBEDDED_LINE_WIDTH; + try_fmt_meta->height = MIRA220_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->state, 0); + try_crop->top = supported_modes[0].crop.top; + try_crop->left = supported_modes[0].crop.left; + try_crop->width = supported_modes[0].crop.width; + try_crop->height = supported_modes[0].crop.height; + + mutex_unlock(&mira220->mutex); + + return 0; +} + +static int mira220_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira220 *mira220 = + container_of(ctrl->handler, struct mira220, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + int ret = 0; + + if (ctrl->id == V4L2_CID_VBLANK) { + int exposure_max, exposure_def; + + /* Update max exposure while meeting expected vblanking */ + exposure_max = mira220_calculate_max_exposure_time( + mira220->mode->height, ctrl->val); + exposure_def = (exposure_max < MIRA220_DEFAULT_EXPOSURE) ? + exposure_max : MIRA220_DEFAULT_EXPOSURE; + __v4l2_ctrl_modify_range(mira220->exposure, + mira220->exposure->minimum, + exposure_max, mira220->exposure->step, + exposure_def); + } + + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (pm_runtime_get_if_in_use(&client->dev) == 0) { + dev_info(&client->dev, + "device in use, ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + return 0; + } + + if (mira220->skip_reg_upload == 0) { + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + // ret = mira220_write_analog_gain_reg(mira220, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret = mira220_write_exposure_reg(mira220, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = mira220_write(mira220, MIRA220_REG_TEST_PATTERN, + mira220_test_pattern_val[ctrl->val]); + break; + case V4L2_CID_HFLIP: + ret = mira220_write(mira220, MIRA220_HFLIP_REG, + ctrl->val); + break; + case V4L2_CID_VFLIP: + ret = mira220_write(mira220, MIRA220_VFLIP_REG, + ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = mira220_write16(mira220, MIRA220_VBLANK_LO_REG, + ctrl->val); + printk(KERN_INFO "[MIRA220]: width %d, hblank %d, vblank %d, height %d, ctrl->val %d.\n", + mira220->mode->width, mira220->mode->hblank, mira220->mode->min_vblank, mira220->mode->height, ctrl->val); + break; + case V4L2_CID_HBLANK: + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + } + + pm_runtime_put(&client->dev); + + // TODO: FIXIT + return ret; +} + +static int mira220_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira220 *mira220 = + container_of(ctrl->handler, struct mira220, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + int ret = 0; + + // printk(KERN_INFO "[MIRA220]: mira220_s_ctrl() id: %X value: %X.\n", ctrl->id, ctrl->val); + + /* Previously, register writes when powered off will be buffered. + * The buffer will be written to sensor when start_streaming. + * Now, register writes happens immediately, even powered off. + * Register writes when powered off will fail. + * Users need to make sure first power on then write register. + */ + + switch (ctrl->id) { + case AMS_CAMERA_CID_MIRA_REG_W: + ret = mira220_v4l2_reg_w(mira220, ctrl->val); + break; + default: + dev_info(&client->dev, + "set ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + + // TODO: FIXIT + return ret; +} + +static int mira220_g_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mira220 *mira220 = + container_of(ctrl->handler, struct mira220, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + int ret = 0; + + // printk(KERN_INFO "[MIRA220]: mira220_g_ctrl() id: %X.\n", ctrl->id); + + /* + * Ideally, V4L2 register read should happen only when powered on. + * However, perhaps there are use cases that, + * reading other I2C addr is desired when mira sensor is powered off. + * Therefore, the check of "powered" flag is disabled for now. + */ + + switch (ctrl->id) { + case AMS_CAMERA_CID_MIRA_REG_R: + ret = mira220_v4l2_reg_r(mira220, (u32 *)&ctrl->cur.val); + ctrl->val = ctrl->cur.val; + break; + default: + dev_info(&client->dev, + "get ctrl(id:0x%x) is not handled\n", + ctrl->id); + ret = -EINVAL; + break; + } + + // TODO: FIXIT + return ret; +} + + +static const struct v4l2_ctrl_ops mira220_ctrl_ops = { + .s_ctrl = mira220_set_ctrl, +}; + +static const struct v4l2_ctrl_ops mira220_custom_ctrl_ops = { + .g_volatile_ctrl = mira220_g_ctrl, + .s_ctrl = mira220_s_ctrl, +}; + + +/* list of custom v4l2 ctls */ +static struct v4l2_ctrl_config custom_ctrl_config_list[] = { + /* Do not change the name field for the controls! */ + { + .ops = &mira220_custom_ctrl_ops, + .id = AMS_CAMERA_CID_MIRA_REG_W, + .name = "mira_reg_w", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = 0, + .min = 0, + .max = 0x7FFFFFFF, + .def = 0, + .step = 1, + }, + { + .ops = &mira220_custom_ctrl_ops, + .id = AMS_CAMERA_CID_MIRA_REG_R, + .name = "mira_reg_r", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = 0, + .min = 0, + .max = 0x7FFFFFFF, + .def = 0, + .step = 1, + }, + +}; + + +// This function should enumerate all the media bus formats for the requested pads. If the requested +// format index is beyond the number of avaialble formats it shall return -EINVAL; +static int mira220_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mira220 *mira220 = to_mira220(sd); + + if (code->pad >= NUM_PADS) + return -EINVAL; + + if (code->pad == IMAGE_PAD) { + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = mira220_validate_format_code_or_default(mira220, + codes[code->index]); + } else { + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SENSOR_DATA; + } + + return 0; +} + +static int mira220_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct mira220 *mira220 = to_mira220(sd); + + if (fse->pad >= NUM_PADS) + return -EINVAL; + + if (fse->pad == IMAGE_PAD) { + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != mira220_validate_format_code_or_default(mira220, fse->code)) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + } else { + if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) + return -EINVAL; + + fse->min_width = MIRA220_EMBEDDED_LINE_WIDTH; + fse->max_width = fse->min_width; + fse->min_height = MIRA220_NUM_EMBEDDED_LINES; + fse->max_height = fse->min_height; + } + + return 0; +} + +static void mira220_reset_colorspace(struct v4l2_mbus_framefmt *fmt) +{ + 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); +} + +static void mira220_update_image_pad_format(struct mira220 *mira220, + const struct mira220_mode *mode, + struct v4l2_subdev_format *fmt) +{ + if (mode != NULL) { + printk(KERN_INFO "[MIRA220]: mira220_update_image_pad_format() width %d, height %d.\n", + mode->width, mode->height); + } else { + printk(KERN_ERR "[MIRA220]: mira220_update_image_pad_format() mode is NULL.\n"); + } + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + mira220_reset_colorspace(&fmt->format); +} + +static void mira220_update_metadata_pad_format(struct v4l2_subdev_format *fmt) +{ + fmt->format.width = MIRA220_EMBEDDED_LINE_WIDTH; + fmt->format.height = MIRA220_NUM_EMBEDDED_LINES; + fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format.field = V4L2_FIELD_NONE; + +} + +static int __mira220_get_pad_format(struct mira220 *mira220, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + if (fmt->pad >= NUM_PADS) + return -EINVAL; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(&mira220->sd, sd_state, fmt->pad); + + try_fmt->code = fmt->pad == IMAGE_PAD ? + mira220_validate_format_code_or_default(mira220, try_fmt->code) : + MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format = *try_fmt; + } else { + if (fmt->pad == IMAGE_PAD) { + mira220_update_image_pad_format(mira220, mira220->mode, + fmt); + fmt->format.code = mira220_validate_format_code_or_default(mira220, + mira220->fmt.code); + } else { + mira220_update_metadata_pad_format(fmt); + } + } + + return 0; +} + +static int mira220_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mira220 *mira220 = to_mira220(sd); + int ret; + + mutex_lock(&mira220->mutex); + ret = __mira220_get_pad_format(mira220, sd_state, fmt); + mutex_unlock(&mira220->mutex); + + return ret; +} + +static int mira220_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mira220 *mira220 = to_mira220(sd); + const struct mira220_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + u32 max_exposure = 0, default_exp = 0; + + if (fmt->pad >= NUM_PADS) + return -EINVAL; + + mutex_lock(&mira220->mutex); + + if (fmt->pad == IMAGE_PAD) { + /* Validate format or use default */ + fmt->format.code = mira220_validate_format_code_or_default(mira220, + fmt->format.code); + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, + fmt->format.height); + mira220_update_image_pad_format(mira220, mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + printk(KERN_INFO "[MIRA220]: mira220_set_pad_format() use try_format.\n"); + framefmt = v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); + *framefmt = fmt->format; + } else if (mira220->mode != mode || + mira220->fmt.code != fmt->format.code) { + + printk(KERN_INFO "[MIRA220]: mira220_set_pad_format() use new mode.\n"); + printk(KERN_INFO "[MIRA220]: mira220->mode %p mode %p.\n", (void *)mira220->mode, (void *)mode); + printk(KERN_INFO "[MIRA220]: mira220->fmt.code 0x%x fmt->format.code 0x%x.\n", mira220->fmt.code, fmt->format.code); + + mira220->fmt = fmt->format; + mira220->mode = mode; + + // Update controls based on new mode (range and current value). + max_exposure = mira220_calculate_max_exposure_time( + mira220->mode->height, + mira220->mode->min_vblank); + default_exp = MIRA220_DEFAULT_EXPOSURE > max_exposure ? max_exposure : MIRA220_DEFAULT_EXPOSURE; + printk(KERN_INFO "[MIRA220]: mira220_set_pad_format() min_exp %d max_exp %d, default_exp %d\n", + MIRA220_EXPOSURE_MIN, max_exposure, default_exp); + __v4l2_ctrl_modify_range(mira220->exposure, + MIRA220_EXPOSURE_MIN, + max_exposure, 1, + default_exp); + + // Update pixel rate based on new mode. + __v4l2_ctrl_modify_range(mira220->pixel_rate, + mira220->mode->pixel_rate, + mira220->mode->pixel_rate, 1, + mira220->mode->pixel_rate); + printk(KERN_INFO "[MIRA220]: mira220_set_pad_format() update V4L2_CID_PIXEL_RATE to %u\n", mira220->mode->pixel_rate); + + // Update hblank based on new mode. + __v4l2_ctrl_modify_range(mira220->hblank, + mira220->mode->hblank, + mira220->mode->hblank, 1, + mira220->mode->hblank); + printk(KERN_INFO "[MIRA220]: mira220_set_pad_format() update V4L2_CID_HBLANK to %u\n", mira220->mode->hblank); + + printk(KERN_INFO "[MIRA220]: Mira220 VBLANK = %u.\n", + mira220->mode->min_vblank); + + __v4l2_ctrl_modify_range(mira220->vblank, + mira220->mode->min_vblank, + mira220->mode->max_vblank, + 1, + mira220->mode->min_vblank); + + // Set the current vblank value + printk(KERN_INFO "[MIRA220]: mira220_set_pad_format() mira220->mode->min_vblank, %d\n", + mira220->mode->min_vblank); + + __v4l2_ctrl_s_ctrl(mira220->vblank, mira220->mode->min_vblank); + } + } else { + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, sd_state, + fmt->pad); + *framefmt = fmt->format; + } else { + /* Only one embedded data mode is supported */ + mira220_update_metadata_pad_format(fmt); + } + } + + printk(KERN_INFO "[MIRA220]: mira220_set_pad_format() to unlock and return.\n"); + + mutex_unlock(&mira220->mutex); + + return 0; +} + +static int mira220_set_framefmt(struct mira220 *mira220) +{ + if (mira220->skip_reg_upload == 0) { + switch (mira220->fmt.code) { + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + printk(KERN_INFO "[MIRA220]: mira220_set_framefmt() write 8 bpp regs.\n"); + mira220_write(mira220, MIRA220_BIT_DEPTH_REG, MIRA220_BIT_DEPTH_8_BIT); + mira220_write(mira220, MIRA220_CSI_DATA_TYPE_REG, + MIRA220_CSI_DATA_TYPE_8_BIT); + return 0; + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + printk(KERN_INFO "[MIRA220]: mira220_set_framefmt() write 10 bpp regs.\n"); + mira220_write(mira220, MIRA220_BIT_DEPTH_REG,MIRA220_BIT_DEPTH_10_BIT); + mira220_write(mira220, MIRA220_CSI_DATA_TYPE_REG, + MIRA220_CSI_DATA_TYPE_10_BIT); + return 0; + case MEDIA_BUS_FMT_Y12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + printk(KERN_INFO "[MIRA220]: mira220_set_framefmt() write 12 bpp regs.\n"); + mira220_write(mira220, MIRA220_BIT_DEPTH_REG, MIRA220_BIT_DEPTH_12_BIT); + mira220_write(mira220, MIRA220_CSI_DATA_TYPE_REG, + MIRA220_CSI_DATA_TYPE_12_BIT); + return 0; + default: + printk(KERN_ERR "Unknown format requested %d", mira220->fmt.code); + } + } + + return -EINVAL; +} + +static const struct v4l2_rect * +__mira220_get_pad_crop(struct mira220 *mira220, struct v4l2_subdev_state *sd_state, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&mira220->sd, sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mira220->mode->crop; + } + + return NULL; +} + +static int mira220_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct mira220 *mira220 = to_mira220(sd); + + mutex_lock(&mira220->mutex); + sel->r = *__mira220_get_pad_crop(mira220, sd_state, sel->pad, + sel->which); + mutex_unlock(&mira220->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = MIRA220_NATIVE_WIDTH; + sel->r.height = MIRA220_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = MIRA220_PIXEL_ARRAY_TOP; + sel->r.left = MIRA220_PIXEL_ARRAY_LEFT; + sel->r.width = MIRA220_PIXEL_ARRAY_WIDTH; + sel->r.height = MIRA220_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + +static int mira220_start_streaming(struct mira220 *mira220) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + const struct mira220_reg_list *reg_list; + int ret; + + printk(KERN_INFO "[MIRA220]: Entering start streaming function.\n"); + + /* Follow examples of other camera driver, here use pm_runtime_resume_and_get */ + ret = pm_runtime_resume_and_get(&client->dev); + + if (ret < 0) { + //printk(KERN_INFO "[MIRA220]: get_sync failed, but continue.\n"); + pm_runtime_put_noidle(&client->dev); + return ret; + } + + /* Apply default values of current mode */ + if (mira220->skip_reg_upload == 0) { + /* Stop treaming before uploading register sequence */ + printk(KERN_INFO "[MIRA220]: Writing stop streaming regs.\n"); + ret = mira220_write_stop_streaming_regs(mira220); + if (ret) { + dev_err(&client->dev, "Could not write stream-on sequence"); + goto err_rpm_put; + } + + reg_list = &mira220->mode->reg_list; + printk(KERN_INFO "[MIRA220]: Write %d regs.\n", reg_list->num_of_regs); + ret = mira220_write_regs(mira220, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + goto err_rpm_put; + } + + ret = mira220_set_framefmt(mira220); + if (ret) { + dev_err(&client->dev, "%s failed to set frame format: %d\n", + __func__, ret); + goto err_rpm_put; + } + } else { + printk(KERN_INFO "[MIRA220]: Skip base register sequence upload, due to mira220->skip_reg_upload=%u.\n", mira220->skip_reg_upload); + } + + + printk(KERN_INFO "[MIRA220]: Entering v4l2 ctrl handler setup function.\n"); + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(mira220->sd.ctrl_handler); + printk(KERN_INFO "[MIRA220]: __v4l2_ctrl_handler_setup ret = %d.\n", ret); + if (ret) + goto err_rpm_put; + + + if (mira220->skip_reg_upload == 0 || + (mira220->skip_reg_upload == 1 && mira220->force_stream_ctrl == 1) ) { + printk(KERN_INFO "[MIRA220]: Writing start streaming regs.\n"); + ret = mira220_write_start_streaming_regs(mira220); + if (ret) { + dev_err(&client->dev, "Could not write stream-on sequence"); + goto err_rpm_put; + } + } else { + printk(KERN_INFO "[MIRA220]: Skip write_start_streaming_regs due to skip_reg_upload == %d and force_stream_ctrl == %d.\n", + mira220->skip_reg_upload, mira220->force_stream_ctrl); + } + + /* vflip and hflip cannot change during streaming */ + printk(KERN_INFO "[MIRA220]: Entering v4l2 ctrl grab vflip grab vflip.\n"); + __v4l2_ctrl_grab(mira220->vflip, true); + printk(KERN_INFO "[MIRA220]: Entering v4l2 ctrl grab vflip grab hflip.\n"); + __v4l2_ctrl_grab(mira220->hflip, true); + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); + return ret; +} + +static void mira220_stop_streaming(struct mira220 *mira220) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + int ret = 0; + + /* Unlock controls for vflip and hflip */ + __v4l2_ctrl_grab(mira220->vflip, false); + __v4l2_ctrl_grab(mira220->hflip, false); + + if (mira220->skip_reset == 0) { + if (mira220->skip_reg_upload == 0 || + (mira220->skip_reg_upload == 1 && mira220->force_stream_ctrl == 1) ) { + printk(KERN_INFO "[MIRA220]: Writing stop streaming regs.\n"); + ret = mira220_write_stop_streaming_regs(mira220); + if (ret) { + dev_err(&client->dev, "Could not write the stream-off sequence"); + } + } else { + printk(KERN_INFO "[MIRA220]: Skip write_stop_streaming_regs due to skip_reg_upload == %d and force_stream_ctrl == %d.\n", + mira220->skip_reg_upload, mira220->force_stream_ctrl); + } + } else { + printk(KERN_INFO "[MIRA220]: Skip write_stop_streaming_regs due to mira220->skip_reset == %d.\n", mira220->skip_reset); + } + + pm_runtime_put(&client->dev); +} + +static int mira220_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct mira220 *mira220 = to_mira220(sd); + int ret = 0; + + mutex_lock(&mira220->mutex); + if (mira220->streaming == enable) { + mutex_unlock(&mira220->mutex); + return 0; + } + + printk(KERN_INFO "[MIRA220]: Entering mira220_set_stream enable: %d.\n", enable); + + if (enable) { + /* + * Apply default & customized values + * and then start streaming. + */ + ret = mira220_start_streaming(mira220); + if (ret) + goto err_unlock; + } else { + mira220_stop_streaming(mira220); + } + + mira220->streaming = enable; + + mutex_unlock(&mira220->mutex); + + printk(KERN_INFO "[MIRA220]: Returning mira220_set_stream with ret: %d.\n", ret); + + return ret; + +err_unlock: + mutex_unlock(&mira220->mutex); + + return ret; +} + +static int __maybe_unused mira220_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira220 *mira220 = to_mira220(sd); + + printk(KERN_INFO "[MIRA220]: Entering suspend function.\n"); + + if (mira220->streaming) + mira220_stop_streaming(mira220); + + return 0; +} + +static int __maybe_unused mira220_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira220 *mira220 = to_mira220(sd); + int ret; + + printk(KERN_INFO "[MIRA220]: Entering resume function.\n"); + + if (mira220->streaming) { + ret = mira220_start_streaming(mira220); + if (ret) + goto error; + } + + return 0; + +error: + mira220_stop_streaming(mira220); + mira220->streaming = false; + + return ret; +} + +static int mira220_get_regulators(struct mira220 *mira220) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + unsigned int i; + + for (i = 0; i < MIRA220_NUM_SUPPLIES; i++) + mira220->supplies[i].supply = mira220_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, + MIRA220_NUM_SUPPLIES, + mira220->supplies); +} + +/* OTP power on */ +static int mira220_otp_power_on(struct mira220 *mira220) +{ + int ret; + + ret = mira220_write(mira220, 0x0080, 0x04); + + return 0; +} + +/* OTP power off */ +static int mira220_otp_power_off(struct mira220 *mira220) +{ + int ret; + + ret = mira220_write(mira220, 0x0080, 0x08); + + return 0; +} + +/* OTP power on */ +static int mira220_otp_read(struct mira220 *mira220, u8 addr, u8 offset, u8 *val) +{ + int ret; + + ret = mira220_write(mira220, 0x0086, addr); + ret = mira220_write(mira220, 0x0080, 0x02); + ret = mira220_read(mira220, 0x0082 + offset, val); + return 0; +} + + +/* Verify chip ID */ +static int mira220_identify_module(struct mira220 *mira220) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + int ret; + u8 val; + + mira220_otp_power_on(mira220); + + usleep_range(100, 110); + + ret = mira220_otp_read(mira220, 0x0d, 0, &val); + dev_err(&client->dev, "Read OTP add 0x0d with val %x\n", val); + + mira220_otp_power_off(mira220); + + return 0; +} + +static const struct v4l2_subdev_core_ops mira220_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops mira220_video_ops = { + .s_stream = mira220_set_stream, +}; + +static const struct v4l2_subdev_pad_ops mira220_pad_ops = { + .enum_mbus_code = mira220_enum_mbus_code, + .get_fmt = mira220_get_pad_format, + .set_fmt = mira220_set_pad_format, + .get_selection = mira220_get_selection, + .enum_frame_size = mira220_enum_frame_size, +}; + +static const struct v4l2_subdev_ops mira220_subdev_ops = { + .core = &mira220_core_ops, + .video = &mira220_video_ops, + .pad = &mira220_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mira220_internal_ops = { + .open = mira220_open, +}; + +/* Initialize control handlers */ +static int mira220_init_controls(struct mira220 *mira220) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd); + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_fwnode_device_properties props; + int ret; + struct v4l2_ctrl_config *mira220_reg_w; + struct v4l2_ctrl_config *mira220_reg_r; + + u32 max_exposure = 0; + + ctrl_hdlr = &mira220->ctrl_handler; + /* v4l2_ctrl_handler_init gives a hint/guess of the number of v4l2_ctrl_new */ + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16); + if (ret) + return ret; + + mutex_init(&mira220->mutex); + ctrl_hdlr->lock = &mira220->mutex; + + printk(KERN_INFO "[MIRA220]: %s V4L2_CID_PIXEL_RATE %X.\n", __func__, V4L2_CID_PIXEL_RATE); + + /* By default, PIXEL_RATE is read only */ + mira220->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_PIXEL_RATE, + mira220->mode->pixel_rate, + mira220->mode->pixel_rate, 1, + mira220->mode->pixel_rate); + + printk(KERN_INFO "[MIRA220]: %s V4L2_CID_VBLANK %X.\n", __func__, V4L2_CID_VBLANK); + + mira220->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_VBLANK, mira220->mode->min_vblank, + mira220->mode->max_vblank, 1, + mira220->mode->min_vblank); + + printk(KERN_INFO "[MIRA220]: %s V4L2_CID_HBLANK %X.\n", __func__, V4L2_CID_HBLANK); + + mira220->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_HBLANK, mira220->mode->hblank, + mira220->mode->hblank, 1, + mira220->mode->hblank); + + // Make the vblank control read only. This could be changed to allow changing framerate in + // runtime, but would require adapting other settings + // mira220->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + // Exposure is indicated in number of lines here + // Max is determined by vblank + vsize and Tglob. + max_exposure = mira220_calculate_max_exposure_time(mira220->mode->height, + mira220->mode->min_vblank); + + printk(KERN_INFO "[MIRA220]: %s V4L2_CID_EXPOSURE %X.\n", __func__, V4L2_CID_EXPOSURE); + + mira220->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_EXPOSURE, + MIRA220_EXPOSURE_MIN, max_exposure, + 1, + MIRA220_DEFAULT_EXPOSURE); + + printk(KERN_INFO "[MIRA220]: %s V4L2_CID_ANALOGUE_GAIN %X.\n", __func__, V4L2_CID_ANALOGUE_GAIN); + + mira220->gain = v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + MIRA220_ANALOG_GAIN_MIN, MIRA220_ANALOG_GAIN_MAX, + MIRA220_ANALOG_GAIN_STEP, MIRA220_ANALOG_GAIN_DEFAULT); + + printk(KERN_INFO "[MIRA220]: %s V4L2_CID_HFLIP %X.\n", __func__, V4L2_CID_HFLIP); + + mira220->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (mira220->hflip) + mira220->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + printk(KERN_INFO "[MIRA220]: %s V4L2_CID_VFLIP %X.\n", __func__, V4L2_CID_VFLIP); + + mira220->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (mira220->vflip) + mira220->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + printk(KERN_INFO "[MIRA220]: %s V4L2_CID_TEST_PATTERN %X.\n", __func__, V4L2_CID_TEST_PATTERN); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &mira220_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(mira220_test_pattern_menu) - 1, + 0, 0, mira220_test_pattern_menu); + /* + * Custom op + */ + mira220_reg_w = &custom_ctrl_config_list[0]; + printk(KERN_INFO "[MIRA220]: %s AMS_CAMERA_CID_MIRA_REG_W %X.\n", __func__, AMS_CAMERA_CID_MIRA_REG_W); + mira220->mira220_reg_w = v4l2_ctrl_new_custom(ctrl_hdlr, mira220_reg_w, NULL); + + mira220_reg_r = &custom_ctrl_config_list[1]; + printk(KERN_INFO "[MIRA220]: %s AMS_CAMERA_CID_MIRA_REG_R %X.\n", __func__, AMS_CAMERA_CID_MIRA_REG_R); + mira220->mira220_reg_r = v4l2_ctrl_new_custom(ctrl_hdlr, mira220_reg_r, NULL); + if (mira220->mira220_reg_r) + mira220->mira220_reg_r->flags |= (V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY); + + 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, &mira220_ctrl_ops, + &props); + if (ret) + goto error; + + mira220->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + mutex_destroy(&mira220->mutex); + + return ret; +} + +static void mira220_free_controls(struct mira220 *mira220) +{ + v4l2_ctrl_handler_free(mira220->sd.ctrl_handler); + mutex_destroy(&mira220->mutex); +} + +static int mira220_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; + + 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; + } + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) { + dev_err(dev, "only 2 data lanes are currently supported\n"); + goto error_out; + } + + /* Check the link frequency set in device tree */ + if (!ep_cfg.nr_of_link_frequencies) { + dev_err(dev, "link-frequency property not found in DT\n"); + goto error_out; + } + + if (ep_cfg.nr_of_link_frequencies != 1 || + ep_cfg.link_frequencies[0] != MIRA220_DEFAULT_LINK_FREQ) { + dev_err(dev, "Link frequency not supported: %lld\n", + ep_cfg.link_frequencies[0]); + goto error_out; + } + + + // TODO(jalv): Check device tree configuration and make sure it is supported by the driver + ret = 0; + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int mira220pmic_init_controls(struct i2c_client *client) +{ + int ret; + u8 val; + + ret = mira220pmic_write(client, 0x62, 0x00); + ret = mira220pmic_write(client, 0x61, 0x00); + + ret = mira220pmic_read(client, 0x61, &val); + dev_err(&client->dev, "Read 0x61 with val %x\n", val); + + + usleep_range(100, 110); + + ret = mira220pmic_write(client, 0x05, 0x00); + ret = mira220pmic_write(client, 0x0e, 0x00); + ret = mira220pmic_write(client, 0x11, 0x00); + ret = mira220pmic_write(client, 0x14, 0x00); + ret = mira220pmic_write(client, 0x17, 0x00); + ret = mira220pmic_write(client, 0x1a, 0x00); + ret = mira220pmic_write(client, 0x1c, 0x00); + ret = mira220pmic_write(client, 0x1d, 0x00); + ret = mira220pmic_write(client, 0x1e, 0x00); + ret = mira220pmic_write(client, 0x1f, 0x00); + + ret = mira220pmic_write(client, 0x24, 0x48); + ret = mira220pmic_write(client, 0x20, 0x00); + ret = mira220pmic_write(client, 0x21, 0x00); + ret = mira220pmic_write(client, 0x1a, 0x00); + ret = mira220pmic_write(client, 0x01, 0x00); + ret = mira220pmic_write(client, 0x08, 0x00); + ret = mira220pmic_write(client, 0x02, 0x00); + ret = mira220pmic_write(client, 0x0b, 0x00); + ret = mira220pmic_write(client, 0x14, 0x00); + ret = mira220pmic_write(client, 0x17, 0x00); + ret = mira220pmic_write(client, 0x1c, 0x00); + ret = mira220pmic_write(client, 0x1d, 0x00); + ret = mira220pmic_write(client, 0x1f, 0x00); + + usleep_range(50, 60); + + ret = mira220pmic_write(client, 0x62, 0x0d); + + usleep_range(50, 60); + usleep_range(50000, 50000+100); + + ret = mira220pmic_write(client, 0x27, 0xff); + ret = mira220pmic_write(client, 0x28, 0xff); + ret = mira220pmic_write(client, 0x29, 0xff); + ret = mira220pmic_write(client, 0x2a, 0xff); + ret = mira220pmic_write(client, 0x2b, 0xff); + + ret = mira220pmic_write(client, 0x41, 0x04); + usleep_range(50, 60); + + ret = mira220pmic_read(client, 0x20, &val); + dev_err(&client->dev, "Read 0x20 with val %x\n", val); + + // PCB V2.0 or above, enable LDO9=2.50V for VDD25 + ret = mira220pmic_write(client, 0x20, 0xb2); + // For PCB V1.0, VDD28 on 2.85V for older PCBs + // ret = mira220pmic_write(client, 0x20, 0xb9); + + ret = mira220pmic_read(client, 0x20, &val); + dev_err(&client->dev, "Read 0x20 with val %x\n", val); + + usleep_range(700, 710); + + ret = mira220pmic_write(client, 0x12, 0x16); + ret = mira220pmic_write(client, 0x10, 0x16); + ret = mira220pmic_write(client, 0x11, 0x96); + ret = mira220pmic_write(client, 0x1e, 0x96); + ret = mira220pmic_write(client, 0x21, 0x96); + usleep_range(50, 60); + + ret = mira220pmic_write(client, 0x00, 0x04); + ret = mira220pmic_write(client, 0x04, 0x34); + ret = mira220pmic_write(client, 0x06, 0xbf); + ret = mira220pmic_write(client, 0x05, 0xb4); + ret = mira220pmic_write(client, 0x03, 0x00); + ret = mira220pmic_write(client, 0x0d, 0x34); + ret = mira220pmic_write(client, 0x0f, 0xbf); + ret = mira220pmic_write(client, 0x0e, 0xb4); + usleep_range(50, 60); + + ret = mira220pmic_write(client, 0x42, 0x05); + usleep_range(50, 60); + + ret = mira220pmic_write(client, 0x45, 0x40); + ret = mira220pmic_write(client, 0x57, 0x02); + ret = mira220pmic_write(client, 0x5d, 0x10); + ret = mira220pmic_write(client, 0x61, 0x10); + + return 0; +} + + +static int mira220_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct mira220 *mira220; + int ret; + + printk(KERN_INFO "[MIRA220]: probing v4l2 sensor.\n"); + printk(KERN_INFO "[MIRA220]: Driver Version 0.0.\n"); + + dev_err(dev, "[MIRA220] name: %s.\n", client->name); + + mira220 = devm_kzalloc(&client->dev, sizeof(*mira220), GFP_KERNEL); + if (!mira220) + return -ENOMEM; + + v4l2_i2c_subdev_init(&mira220->sd, client, &mira220_subdev_ops); + + /* Check the hardware configuration in device tree */ + if (mira220_check_hwcfg(dev)) + return -EINVAL; + + /* Parse device tree to check if dtoverlay has param skip-reg-upload=1 */ + device_property_read_u32(dev, "skip-reg-upload", &mira220->skip_reg_upload); + printk(KERN_INFO "[MIRA220]: skip-reg-upload %d.\n", mira220->skip_reg_upload); + /* Set default TBD I2C device address to LED I2C Address*/ + mira220->tbd_client_i2c_addr = MIRA220LED_I2C_ADDR; + printk(KERN_INFO "[MIRA220]: User defined I2C device address defaults to LED driver I2C address 0x%X.\n", mira220->tbd_client_i2c_addr); + + + /* Get system clock (xclk) */ + mira220->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(mira220->xclk)) { + dev_err(dev, "failed to get xclk\n"); + return PTR_ERR(mira220->xclk); + } + + mira220->xclk_freq = clk_get_rate(mira220->xclk); + if (mira220->xclk_freq != MIRA220_SUPPORTED_XCLK_FREQ) { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + mira220->xclk_freq); + return -EINVAL; + } + + ret = mira220_get_regulators(mira220); + if (ret) { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + /* Request optional enable pin */ + // mira220->reset_gpio = devm_gpiod_get_optional(dev, "reset", + // GPIOD_OUT_HIGH); + + { + printk(KERN_INFO "[MIRA220]: Init PMIC.\n"); + mira220->pmic_client = i2c_new_dummy_device(client->adapter, + MIRA220PMIC_I2C_ADDR); + if (IS_ERR(mira220->pmic_client)) + return PTR_ERR(mira220->pmic_client); + mira220->uc_client = i2c_new_dummy_device(client->adapter, + MIRA220UC_I2C_ADDR); + if (IS_ERR(mira220->uc_client)) + return PTR_ERR(mira220->uc_client); + mira220->led_client = i2c_new_dummy_device(client->adapter, + MIRA220LED_I2C_ADDR); + if (IS_ERR(mira220->led_client)) + return PTR_ERR(mira220->led_client); + + mira220pmic_init_controls(mira220->pmic_client); + } + + dev_err(dev, "[MIRA220] Sleep for 1 second to let PMIC driver complete init.\n"); + usleep_range(1000000, 1000000+100); + + /* + * The sensor must be powered for mira220_identify_module() + * to be able to read the CHIP_ID register + */ + ret = mira220_power_on(dev); + if (ret) + return ret; + + printk(KERN_INFO "[MIRA220]: Entering identify function.\n"); + + ret = mira220_identify_module(mira220); + if (ret) + goto error_power_off; + + printk(KERN_INFO "[MIRA220]: Setting support function.\n"); + + /* Initialize default illumination trigger parameters */ + /* ILLUM_WIDTH (length) is in unit of rows. */ + mira220->illum_width = MIRA220_ILLUM_WIDTH_DEFAULT; + /* ILLUM_DELAY is in unit of rows. */ + mira220->illum_delay = MIRA220_ILLUM_DELAY_DEFAULT; + + /* Set default mode to max resolution */ + mira220->mode = &supported_modes[0]; + + printk(KERN_INFO "[MIRA220]: Entering init controls function.\n"); + + ret = mira220_init_controls(mira220); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + mira220->sd.internal_ops = &mira220_internal_ops; + mira220->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + mira220->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pads */ + mira220->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; + mira220->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; + + printk(KERN_INFO "[MIRA220]: Entering set default format function.\n"); + + /* Initialize default format */ + mira220_set_default_format(mira220); + + printk(KERN_INFO "[MIRA220]: Entering pads init function.\n"); + + ret = media_entity_pads_init(&mira220->sd.entity, NUM_PADS, mira220->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + printk(KERN_INFO "[MIRA220]: Entering subdev sensor common function.\n"); + + ret = v4l2_async_register_subdev_sensor(&mira220->sd); + if (ret < 0) { + dev_err(dev, "failed to register sensor sub-device: %d\n", ret); + goto error_media_entity; + } + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&mira220->sd.entity); + +error_handler_free: + mira220_free_controls(mira220); + +error_power_off: + mira220_power_off(dev); + + i2c_unregister_device(mira220->pmic_client); + i2c_unregister_device(mira220->uc_client); + i2c_unregister_device(mira220->led_client); + + return ret; +} + +static void mira220_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mira220 *mira220 = to_mira220(sd); + + i2c_unregister_device(mira220->pmic_client); + i2c_unregister_device(mira220->uc_client); + i2c_unregister_device(mira220->led_client); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + mira220_free_controls(mira220); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + mira220_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + +} + +static const struct dev_pm_ops mira220_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mira220_suspend, mira220_resume) + SET_RUNTIME_PM_OPS(mira220_power_off, mira220_power_on, NULL) +}; + +#endif // __MIRA220_INL__ + diff --git a/drivers/media/i2c/mira220color.c b/drivers/media/i2c/mira220color.c new file mode 100644 index 00000000000000..744a9cbc8afe53 --- /dev/null +++ b/drivers/media/i2c/mira220color.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for ams MIRA220 cameras. + * Copyright (C) 2022, ams-OSRAM + * + * Based on Sony IMX219 camera driver + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + */ + +#include "mira220.inl" + +static const struct of_device_id mira220_dt_ids[] = { + { .compatible = "ams,mira220color" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mira220_dt_ids); + +static const struct i2c_device_id mira220_ids[] = { + { "mira220color", 1 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mira220_ids); + +static struct i2c_driver mira220_i2c_driver = { + .driver = { + .name = "mira220color", + .of_match_table = mira220_dt_ids, + .pm = &mira220_pm_ops, + }, + .probe = mira220_probe, + .remove = mira220_remove, + .id_table = mira220_ids, +}; + +module_i2c_driver(mira220_i2c_driver); + +MODULE_AUTHOR("Zhenyu Ye "); +MODULE_DESCRIPTION("ams MIRA220 sensor driver"); +MODULE_LICENSE("GPL v2");