Merge commit 'fbdb94a0c7bddcf16d5e01588e5d0aed65ae43f2'

* commit 'fbdb94a0c7bddcf16d5e01588e5d0aed65ae43f2': (40 commits)
  hwspinlock: rockchip: Add maximum user count config
  dt-bindings: hwlock: rockchip: Add the maximum user count property
  mailbox: rockchip: add get properties helper
  mailbox: rockchip: add version and lock register support
  arm64: dts: rockchip: rv1126b-evb: Add the regulator-settling-time-up-us configuration
  power: supply: sc89601_charger: fix the abnormal display of the charging icon
  arm64: dts: rockchip: add Tablet Board devicetree for RK3576S-RK809
  arm64: dts: rockchip: rv1126b: Fix compatible for can node
  arm64: dts: rockchip: rk3576: Fix compatible for can node
  ARM: dts: rockchip: rk3506: Fix compatible for can node
  arm64: configs: rockchip_linux_defconfig enable CONFIG_CAN_RK3576
  net: can: rockchip: rename rk3576_canfd.c to rk3576_can.c
  drm/rockchip: dw-dp: support more feature active protocol converter adapters
  media: rockchip: vicap force update buffer addr when init stream
  media: rockchip: vicap change stop dma to fs for dvp
  media: rockchip: vicap fixes error of stop stream with dvp
  media: rockchip: vicap fixes error info of fps with toisp mode
  iio: imu: add inv icm42607 support
  arm64: dts: rockchip: rv1126b-evb2: Add the rk96x_wake_aov_irq driver's dts.
  spi: rockchip-sfc: Wait for thunder boot DMA status change before rockchip_sfc_get_gpio_descs
  ...

Change-Id: I21a5ce10a4a9f5c23e607ac2b2ee51c80659aa88
This commit is contained in:
Tao Huang
2025-10-11 18:54:37 +08:00
93 changed files with 8692 additions and 772 deletions

View File

@@ -13,7 +13,9 @@ Required properties :
- rockchip,hwlock-num-locks number of hwlocks provided by this device.
Optional properties :
- rockchip,hwlock-user-id : Set hwlock user id (4 bit, default is 0x01).
- rockchip,hwlock-user-id : Set hwlock user id (default is 0x01).
- rockchip,hwlock-max-user : Set the maximum hwlock user count supported
by the IP (default is 0x0F).
Please look at the generic hwlock binding for usage information for consumers,
"Documentation/devicetree/bindings/hwlock/hwlock.txt"

View File

@@ -40,7 +40,7 @@
};
can0: can@ff320000 {
compatible = "rockchip,rk3506-canfd", "rockchip,rk3576-canfd";
compatible = "rockchip,rk3506-can", "rockchip,rk3576-can";
reg = <0xff320000 0x1000>;
interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru CLK_CAN0>, <&cru HCLK_CAN0>;
@@ -53,7 +53,7 @@
};
can1: can@ff330000 {
compatible = "rockchip,rk3506-canfd", "rockchip,rk3576-canfd";
compatible = "rockchip,rk3506-can", "rockchip,rk3576-can";
reg = <0xff330000 0x1000>;
interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru CLK_CAN1>, <&cru HCLK_CAN1>;

View File

@@ -304,6 +304,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-vehicle-evb-v21.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-vehicle-evb-v21-mcu.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576s-evb1-v10.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576s-evb1-v10-linux.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576s-rk809-tablet-v10.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576s-tablet-v10.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588-evb1-lp4-v10-dsi-dsc-MV2100UZ1.dtb

View File

@@ -67,12 +67,12 @@
interrupt-parent = <&gpio0>;
interrupts = <6 IRQ_TYPE_LEVEL_LOW>;
pinctrl-names = "default", "pmic-power-off";
pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, <&rk806_dvs2_null>, <&rk806_dvs3_null>;
pinctrl-1 = <&rk806_dvs2_pwrdn>;
pinctrl-names = "default";
pinctrl-0 = <&pmic_pins>;
//for rk3576 have ultra sleep circuit design
pwrctrl3_output = <0>;
shutown_by_pwrctrln = <2>;
/* 2800mv-3500mv */
low_voltage_threshold = <3000>;

View File

@@ -282,62 +282,34 @@
0322 0024
//Init Default
0326 00E4
//HSYNC_WIDTH_L
0385 0038
//VSYNC_WIDTH_L
0386 0008
//HSYNC_WIDTH_L HSYNC=32
0385 0020
//VSYNC_WIDTH_L VSYNC=2
0386 0002
//HSYNC_WIDTH_H/VSYNC_WIDTH_H
0387 0000
//VFP_L
//VFP_L VFP=200
03A5 00C8
//VBP_H
03A7 0000
//VFP_H/VBP_L
03A6 0020
//VRES_L
//VBP_L/VFP_H VBP=8
03A6 0080
//VRES_L VRES=0X02D0=720
03A8 00D0
//VRES_H
03A9 0002
//HFP_L
//HFP_L HFP=56
03AA 0038
//HBP_H
03AC 0002
//HFP_H/HBP_L
03AB 0000
//HRES_L
03AC 0003
//HBP_L/HFP_H(4bit) HBP=56
03AB 0080
//HRES_L HRES=0X0780=1920
03AD 0080
//HRES_H
03AE 0007
//Disable FIFO/DESKEW_EN
03A4 00C0
//HSYNC_WIDTH_L
0395 0038
//VSYNC_WIDTH_L
0396 0008
//HSYNC_WIDTH_H/VSYNC_WIDTH_H
0397 0000
//VFP_L
03B1 00C8
//VBP_H
03B3 0000
//VFP_H/VBP_L
03B2 0020
//VRES_L
03B4 00D0
//VRES_H
03B5 0002
//HFP_L
03B6 0038
//HBP_H
03B8 0002
//HFP_H/HBP_L
03B7 0000
//HRES_L
03B9 0080
//HRES_H
03BA 0007
//Disable FIFO/DESKEW_EN
03B0 00C0
03A4 00C1
//Turn on video pipe
0002 0033
//Enable splitter mode reset one shot

View File

@@ -561,6 +561,10 @@
status = "okay";
};
&usbdp_phy_u3 {
status = "disabled";
};
&usb_drd1_dwc3 {
snps,dis_u2_susphy_quirk;
snps,usb2-lpm-disable;

View File

@@ -574,6 +574,10 @@
status = "okay";
};
&usbdp_phy_u3 {
status = "disabled";
};
&usb_drd1_dwc3 {
snps,dis_u2_susphy_quirk;
snps,usb2-lpm-disable;

View File

@@ -569,6 +569,10 @@
status = "okay";
};
&usbdp_phy_u3 {
status = "disabled";
};
&usb_drd1_dwc3 {
snps,dis_u2_susphy_quirk;
snps,usb2-lpm-disable;

View File

@@ -5547,7 +5547,7 @@
};
can0: can@2ac00000 {
compatible = "rockchip,rk3576-canfd";
compatible = "rockchip,rk3576-can";
reg = <0x0 0x2ac00000 0x0 0x1000>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru CLK_CAN0>, <&cru HCLK_CAN0>;
@@ -5558,7 +5558,7 @@
};
can1: can@2ac10000 {
compatible = "rockchip,rk3576-canfd";
compatible = "rockchip,rk3576-can";
reg = <0x0 0x2ac10000 0x0 0x1000>;
interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru CLK_CAN1>, <&cru HCLK_CAN1>;

View File

@@ -0,0 +1,434 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2024 Rockchip Electronics Co., Ltd.
*
*/
/ {
flash_rgb13h: flash-rgb13h {
status = "okay";
compatible = "led,rgb13h";
label = "gpio-flash";
pinctrl-names = "default";
pinctrl-0 = <&flash_led_gpios>;
led-max-microamp = <20000>;
flash-max-microamp = <20000>;
flash-max-timeout-us = <1000000>;
enable-gpio = <&gpio3 RK_PD0 GPIO_ACTIVE_HIGH>;
//mode-gpio = <&gpio2 RK_PC3 GPIO_ACTIVE_HIGH>; //EN
rockchip,camera-module-index = <0>;
rockchip,camera-module-facing = "back";
};
vcc_mipipwr: vcc-mipipwr-regulator {
compatible = "regulator-fixed";
gpio = <&gpio3 RK_PC6 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&mipicam_pwr>;
regulator-name = "vcc_mipipwr";
enable-active-high;
regulator-boot-on;
};
};
&csi2_dcphy0 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi_in_gc05a2: endpoint@1 {
reg = <1>;
remote-endpoint = <&gc05a2_out0>;
data-lanes = <1 2 3 4>;
};
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
csidcphy0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi0_csi2_input>;
};
};
};
};
&csi2_dphy0 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi_in_gc02m1: endpoint@1 {
reg = <1>;
remote-endpoint = <&gc02m1_out>;
data-lanes = <1>;
};
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
csidphy0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi1_csi2_input>;
};
};
};
};
&csi2_dphy3 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi_in_ov13850: endpoint@1 {
reg = <1>;
remote-endpoint = <&ov13850_out>;
data-lanes = <1 2 3 4>;
};
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
csidphy3_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi3_csi2_input>;
};
};
};
};
&i2c4 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c4m3_xfer>;
gc02m1: gc02m1@37 {
status = "okay";
compatible = "galaxycore,gc02m1";
reg = <0x37>;
clocks = <&cru CLK_MIPI_CAMERAOUT_M1>;
clock-names = "xvclk";
pinctrl-names = "default";
pinctrl-0 = <&cam_clk1m0_clk1>;
pwdn-gpios = <&gpio3 RK_PD3 GPIO_ACTIVE_LOW>;
reset-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>;
avdd-supply = <&vcc_mipipwr>;
//dovdd-supply = <&vcc_1v8_cam>;
//dvdd-supply = <&vcc1v2_dvp>;
dvdd-supply = <&vcc_1v2_cam>;
rockchip,camera-module-index = <2>;
rockchip,camera-module-facing = "back";
rockchip,camera-module-name = "KYT-8789-V10";
rockchip,camera-module-lens-name = "default";
port {
gc02m1_out: endpoint {
remote-endpoint = <&mipi_in_gc02m1>;
data-lanes = <1>;
};
};
};
};
&i2c5 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c5m3_xfer>;
dw9714: dw9714@c {
compatible = "dongwoon,dw9714";
status = "okay";
reg = <0x0c>;
rockchip,camera-module-index = <0>;
rockchip,vcm-start-current = <5>;
rockchip,vcm-rated-current = <65>;
rockchip,vcm-step-mode = <5>;
rockchip,camera-module-facing = "back";
avdd-supply = <&vcc_mipipwr>;
xsd-gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_HIGH>;
};
gc05a2: gc05a2@37 {
compatible = "galaxycore,gc05a2";
status = "okay";
reg = <0x37>;
clocks = <&cru CLK_MIPI_CAMERAOUT_M0>;
clock-names = "xvclk";
pinctrl-names = "default";
pinctrl-0 = <&cam_clk0m0_clk0>;
pwdn-gpios = <&gpio3 RK_PC7 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>;
avdd-supply = <&vcc_mipipwr>;
//dovdd-supply = <&vcc_1v8_cam>;
dvdd-supply = <&vcc_1v2_cam>;
rockchip,camera-module-index = <1>;
rockchip,camera-module-facing = "front";
rockchip,camera-module-name = "KYT-11210-V2";
rockchip,camera-module-lens-name = "default";
port {
gc05a2_out0: endpoint {
remote-endpoint = <&mipi_in_gc05a2>;
data-lanes = <1 2>;
};
};
};
ov13850: ov13850@10 {
status = "okay";
compatible = "ovti,ov13850";
reg = <0x10>;
clocks = <&cru CLK_MIPI_CAMERAOUT_M2>;
clock-names = "xvclk";
pinctrl-names = "default";
pinctrl-0 = <&cam_clk2m0_clk2>;
pwdn-gpios = <&gpio3 RK_PD2 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio3 RK_PA7 GPIO_ACTIVE_HIGH>;
dvdd-supply = <&vcc_1v2_cam>;
avdd-supply = <&vcc_mipipwr>;
//dovdd-supply = <&vcc_1v8_cam>;
rockchip,camera-module-index = <0>;
rockchip,camera-module-facing = "back";
rockchip,camera-module-name = "G1-T-B-s5k";
rockchip,camera-module-lens-name = "XA-0806B";
flash-leds = <&flash_rgb13h>;
lens-focus = <&dw9714>;
port {
ov13850_out: endpoint {
remote-endpoint = <&mipi_in_ov13850>;
data-lanes = <1 2 3 4>;
};
};
};
};
&mipi0_csi2 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi0_csi2_input: endpoint@1 {
reg = <1>;
remote-endpoint = <&csidcphy0_out>;
};
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
mipi0_csi2_output: endpoint@0 {
reg = <0>;
remote-endpoint = <&cif_mipi_in0>;
};
};
};
};
&mipi3_csi2 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi3_csi2_input: endpoint@1 {
reg = <1>;
remote-endpoint = <&csidphy3_out>;
};
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
mipi3_csi2_output: endpoint@0 {
reg = <0>;
remote-endpoint = <&cif_mipi3_in0>;
};
};
};
};
&mipi1_csi2 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi1_csi2_input: endpoint@1 {
reg = <1>;
remote-endpoint = <&csidphy0_out>;
};
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
mipi1_csi2_output: endpoint@0 {
reg = <0>;
remote-endpoint = <&cif_mipi1_in0>;
};
};
};
};
&pinctrl {
cam {
mipicam_pwr: mipicam-pwr {
rockchip,pins =
/* camera power en */
<3 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>;
};
flash_led_gpios: flash-led {
rockchip,pins =
/* flash led enable */
<3 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
};
&rkcif {
status = "okay";
};
&rkcif_mipi_lvds {
status = "okay";
port {
cif_mipi_in0: endpoint {
remote-endpoint = <&mipi0_csi2_output>;
};
};
};
&rkcif_mipi_lvds_sditf {
status = "okay";
port {
mipi_lvds_sditf: endpoint {
remote-endpoint = <&isp_vir0_in0>;
};
};
};
&rkcif_mipi_lvds1 {
status = "okay";
port {
cif_mipi1_in0: endpoint {
remote-endpoint = <&mipi1_csi2_output>;
};
};
};
&rkcif_mipi_lvds1_sditf {
status = "okay";
port {
mipi_lvds1_sditf: endpoint {
remote-endpoint = <&isp_vir0_in2>;
};
};
};
&rkcif_mipi_lvds3 {
status = "okay";
port {
cif_mipi3_in0: endpoint {
remote-endpoint = <&mipi3_csi2_output>;
};
};
};
&rkcif_mipi_lvds3_sditf {
status = "okay";
port {
mipi_lvds3_sditf: endpoint {
remote-endpoint = <&isp_vir0_in1>;
};
};
};
&rkcif_mmu {
status = "okay";
};
&rkisp {
status = "okay";
};
&rkisp_mmu {
status = "okay";
};
&rkisp_vir0 {
status = "okay";
port {
#address-cells = <1>;
#size-cells = <0>;
isp_vir0_in0: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi_lvds_sditf>;
};
isp_vir0_in1: endpoint@1 {
reg = <1>;
remote-endpoint = <&mipi_lvds3_sditf>;
};
isp_vir0_in2: endpoint@2 {
reg = <2>;
remote-endpoint = <&mipi_lvds1_sditf>;
};
};
};

File diff suppressed because it is too large Load Diff

View File

@@ -562,6 +562,14 @@
phy-supply = <&vcc5v0_host_usb30>;
};
&usbdp_phy0_u3 {
status = "disabled";
};
&usbdp_phy1_u3 {
status = "disabled";
};
&vdd_log_s0 {
regulator-state-mem {
regulator-on-in-suspend;
@@ -591,4 +599,3 @@
&vcc5v0_host {
status = "disabled";
};

View File

@@ -859,6 +859,14 @@
phy-supply = <&vcc5v0_host_usb30>;
};
&usbdp_phy0_u3 {
status = "disabled";
};
&usbdp_phy1_u3 {
status = "disabled";
};
&vdd_log_s0 {
regulator-state-mem {
regulator-on-in-suspend;

View File

@@ -499,8 +499,8 @@
03A5 00C8
//VBP_H
03A7 0000
//VFP_H/VBP_L VBP=8
03A6 0008
//VBP_L/VFP_H VBP=8
03A6 0080
//VRES_L VRES=0X02D0=720
03A8 00D0
//VRES_H
@@ -509,14 +509,14 @@
03AA 0038
//HBP_H
03AC 0003
//HFP_H/HBP_L(4bit) HBP=56
03AB 0008
//HBP_L/HFP_H(4bit) HBP=56
03AB 0080
//HRES_L HRES=0X0780=1920
03AD 0080
//HRES_H
03AE 0007
//Disable FIFO/DESKEW_EN
03A4 00C0
03A4 00C1
//HSYNC_WIDTH_L HSYNC=40
0395 0028
//VSYNC_WIDTH_L VSYNC=20
@@ -527,8 +527,8 @@
03B1 000F
//VBP_H
03B3 0000
//VFP_H/VBP_L VBP=10
03B2 000A
//VBP_L/VFP_H VBP=10
03B2 00A0
//VRES_L VRES=0X0438=1080
03B4 0038
//VRES_H
@@ -537,14 +537,14 @@
03B6 008C
//HBP_H
03B8 0006
//HFP_H/HBP_L HBP=100
03B7 0004
//HBP_L/HFP_H HBP=100
03B7 0040
//HRES_L HRES=0X0780=1920
03B9 0080
//HRES_H
03BA 0007
//Disable FIFO/DESKEW_EN
03B0 00C0
03B0 00C1
//Turn on video pipe
0002 0033
//Enable splitter mode reset one shot
@@ -740,7 +740,7 @@
panel-size= <346 194>;
panel-timing {
clock-frequency = <115200000>;
clock-frequency = <115000000>;
hactive = <1920>;
vactive = <720>;
hfront-porch = <56>;
@@ -1422,7 +1422,7 @@
panel-size= <346 194>;
panel-timing {
clock-frequency = <230400000>; //4128*930@60
clock-frequency = <230000000>; //3840*720@60
hactive = <3840>;
vactive = <720>;
hfront-porch = <112>;
@@ -1550,7 +1550,7 @@
panel-size= <346 194>;
panel-timing {
clock-frequency = <115200000>;
clock-frequency = <115000000>;
hactive = <1920>;
vactive = <720>;
hfront-porch = <56>;
@@ -2101,7 +2101,7 @@
panel-size= <346 194>;
panel-timing {
clock-frequency = <230400000>; //4128*930@60
clock-frequency = <230000000>; //3840*720@60
hactive = <3840>;
vactive = <720>;
hfront-porch = <112>;
@@ -2230,7 +2230,7 @@
panel-size= <346 194>;
panel-timing {
clock-frequency = <115200000>;
clock-frequency = <115000000>;
hactive = <1920>;
vactive = <720>;
hfront-porch = <56>;
@@ -2413,8 +2413,7 @@
};
&vop {
assigned-clocks = <&cru PLL_V0PLL>;
assigned-clock-rates = <2304000000>;
status = "okay";
};
&vp0 {

View File

@@ -499,8 +499,8 @@
03A5 00C8
//VBP_H
03A7 0000
//VFP_H/VBP_L VBP=8
03A6 0008
//VBP_L/VFP_H VBP=8
03A6 0080
//VRES_L VRES=0X02D0=720
03A8 00D0
//VRES_H
@@ -509,42 +509,42 @@
03AA 0038
//HBP_H
03AC 0003
//HFP_H/HBP_L(4bit) HBP=56
03AB 0008
//HBP_L/HFP_H(4bit) HBP=56
03AB 0080
//HRES_L HRES=0X0780=1920
03AD 0080
//HRES_H
03AE 0007
//Disable FIFO/DESKEW_EN
03A4 00C0
//HSYNC_WIDTH_L HSYNC=40
0395 0028
//VSYNC_WIDTH_L VSYNC=20
0396 0014
03A4 00C1
//HSYNC_WIDTH_L HSYNC=32
0395 0020
//VSYNC_WIDTH_L VSYNC=2
0396 0002
//HSYNC_WIDTH_H/VSYNC_WIDTH_H
0397 0000
//VFP_L VFP=15
03B1 000F
//VFP_L VFP=200
03B1 00C8
//VBP_H
03B3 0000
//VFP_H/VBP_L VBP=10
03B2 000A
//VRES_L VRES=0X0438=1080
03B4 0038
//VBP_L/VFP_H VBP=8
03B2 0080
//VRES_L VRES=0X02D0=720
03B4 00D0
//VRES_H
03B5 0004
//HFP_L HFP=140
03B6 008C
03B5 0002
//HFP_L HFP=56
03B6 0038
//HBP_H
03B8 0006
//HFP_H/HBP_L HBP=100
03B7 0004
03B8 0003
//HBP_L/HFP_H HBP=56
03B7 0080
//HRES_L HRES=0X0780=1920
03B9 0080
//HRES_H
03BA 0007
//Disable FIFO/DESKEW_EN
03B0 00C0
03B0 00C1
//Turn on video pipe
0002 0033
//Enable splitter mode reset one shot
@@ -2103,8 +2103,7 @@
};
&vop {
assigned-clocks = <&cru PLL_V0PLL>;
assigned-clock-rates = <1150000000>;
status = "okay";
};
//dp
&vp0 {

View File

@@ -380,7 +380,7 @@
native-mode = <&dsi1_timing0>;
dsi1_timing0: timing0 {
clock-frequency = <280000000>;
hactive = <1140>;
hactive = <1440>;
vactive = <3120>;
hfront-porch = <16>;
hsync-len = <8>;

View File

@@ -1071,7 +1071,7 @@
native-mode = <&dsi1_timing0>;
dsi1_timing0: timing0 {
clock-frequency = <280000000>;
hactive = <1140>;
hactive = <1440>;
vactive = <3120>;
hfront-porch = <16>;
hsync-len = <8>;

View File

@@ -100,6 +100,7 @@
regulator-init-microvolt = <950000>;
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1100000>;
regulator-settling-time-up-us = <250>;
regulator-always-on;
regulator-boot-on;
enable-gpios = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>;

View File

@@ -179,6 +179,7 @@
regulator-init-microvolt = <905000>;
regulator-min-microvolt = <810000>;
regulator-max-microvolt = <1006000>;
regulator-settling-time-up-us = <250>;
regulator-always-on;
regulator-boot-on;
pwm-supply = <&vccsys_stb>;
@@ -192,6 +193,7 @@
regulator-init-microvolt = <950000>;
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <1150000>;
regulator-settling-time-up-us = <250>;
regulator-always-on;
regulator-boot-on;
pwm-supply = <&vccsys_stb>;
@@ -205,6 +207,7 @@
regulator-init-microvolt = <950000>;
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <1100000>;
regulator-settling-time-up-us = <250>;
regulator-always-on;
regulator-boot-on;
pwm-supply = <&vccsys_stb>;
@@ -230,6 +233,13 @@
WIFI,host_wake_irq = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>;
status = "okay";
};
rk96x_wake_aov_irq: rk96x-wake-aov-irq {
compatible = "rockchip,rk96x-wake-aov-irq";
rk96x-wake-irq-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>;
wakeup-source;
status = "okay";
};
};
&acdcdig_dsm {

View File

@@ -166,6 +166,7 @@
regulator-init-microvolt = <905000>;
regulator-min-microvolt = <810000>;
regulator-max-microvolt = <1006000>;
regulator-settling-time-up-us = <250>;
regulator-always-on;
regulator-boot-on;
pwm-supply = <&vccsys_stb>;
@@ -179,6 +180,7 @@
regulator-init-microvolt = <950000>;
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1100000>;
regulator-settling-time-up-us = <250>;
regulator-always-on;
regulator-boot-on;
pwm-supply = <&vccsys_stb>;
@@ -192,6 +194,7 @@
regulator-init-microvolt = <950000>;
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1100000>;
regulator-settling-time-up-us = <250>;
regulator-always-on;
regulator-boot-on;
pwm-supply = <&vccsys_stb>;

View File

@@ -103,6 +103,7 @@
regulator-init-microvolt = <950000>;
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1100000>;
regulator-settling-time-up-us = <250>;
regulator-always-on;
regulator-boot-on;
enable-gpios = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>;

View File

@@ -454,18 +454,20 @@
cpu_opp_j_od_1416000000: opp-j-od-1416000000 {
opp-supported-hw = <0x04 0xffff>;
opp-hz = /bits/ 64 <1416000000>;
opp-microvolt = <925000 925000 1100000>;
opp-microvolt-L0 = <9750000 9750000 1100000>;
opp-microvolt-L1 = <950000 950000 1100000>;
opp-microvolt = <950000 950000 1100000>;
opp-microvolt-L0 = <1025000 1025000 1100000>;
opp-microvolt-L1 = <1000000 1000000 1100000>;
opp-microvolt-L2 = <975000 975000 1100000>;
clock-latency-ns = <40000>;
status = "disabled";
};
cpu_opp_j_od_1512000000: opp-j-od-1512000000 {
opp-supported-hw = <0x04 0xffff>;
opp-hz = /bits/ 64 <1512000000>;
opp-microvolt = <950000 950000 1100000>;
opp-microvolt-L0 = <1000000 1000000 1100000>;
opp-microvolt-L1 = <975000 975000 1100000>;
opp-microvolt = <975000 975000 1100000>;
opp-microvolt-L0 = <1050000 1050000 1100000>;
opp-microvolt-L1 = <1025000 1025000 1100000>;
opp-microvolt-L2 = <1000000 1000000 1100000>;
clock-latency-ns = <40000>;
status = "disabled";
};
@@ -3256,7 +3258,7 @@
};
can0: can@21d40000 {
compatible = "rockchip,rv1126b-canfd";
compatible = "rockchip,rv1126b-can";
reg = <0x21d40000 0x1000>;
interrupts = <GIC_SPI 204 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru CLK_CAN0>, <&cru HCLK_CAN0>;
@@ -3269,7 +3271,7 @@
};
can1: can@21d50000 {
compatible = "rockchip,rv1126b-canfd";
compatible = "rockchip,rv1126b-can";
reg = <0x21d50000 0x1000>;
interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru CLK_CAN1>, <&cru HCLK_CAN1>;

View File

@@ -106,6 +106,7 @@ CONFIG_VEHICLE_GPIO_MCU_EXPANDER=y
# CONFIG_VIDEO_GC4C33 is not set
# CONFIG_VIDEO_GC8034 is not set
# CONFIG_VIDEO_IMX415 is not set
CONFIG_VIDEO_MAX96756=y
CONFIG_VIDEO_MAXIM_SERDES=y
# CONFIG_VIDEO_OV02B10 is not set
# CONFIG_VIDEO_OV13850 is not set

View File

@@ -220,7 +220,7 @@ CONFIG_MOTORCOMM_PHY=y
CONFIG_ROCKCHIP_FEPHY=y
CONFIG_ROCKCHIP_PHY=y
CONFIG_RK630_PHY=y
CONFIG_CANFD_RK3576=y
CONFIG_CAN_RK3576=y
CONFIG_USB_RTL8150=y
CONFIG_USB_RTL8152=y
CONFIG_WL_ROCKCHIP=y

View File

@@ -173,14 +173,14 @@ static struct pvtpll_table rv1126b_core_pvtpll_table[] = {
static struct pvtpll_table rv1126bj_core_pvtpll_table[] = {
/* rate_hz, ring_sel, length */
ROCKCHIP_PVTPLL_VOLT_SEL(1900000000, 0, 30, 7),
ROCKCHIP_PVTPLL_VOLT_SEL(1850000000, 0, 30, 7),
ROCKCHIP_PVTPLL_VOLT_SEL(1800000000, 0, 30, 7),
ROCKCHIP_PVTPLL_VOLT_SEL(1750000000, 0, 30, 6),
ROCKCHIP_PVTPLL_VOLT_SEL(1700000000, 0, 30, 6),
ROCKCHIP_PVTPLL_VOLT_SEL(1608000000, 0, 30, 5),
ROCKCHIP_PVTPLL_VOLT_SEL(1512000000, 0, 30, 5),
ROCKCHIP_PVTPLL_VOLT_SEL(1416000000, 0, 34, 5),
ROCKCHIP_PVTPLL_VOLT_SEL(1900000000, 0, 32, 7),
ROCKCHIP_PVTPLL_VOLT_SEL(1850000000, 0, 32, 7),
ROCKCHIP_PVTPLL_VOLT_SEL(1800000000, 0, 32, 7),
ROCKCHIP_PVTPLL_VOLT_SEL(1750000000, 0, 32, 6),
ROCKCHIP_PVTPLL_VOLT_SEL(1700000000, 0, 32, 6),
ROCKCHIP_PVTPLL_VOLT_SEL(1608000000, 0, 32, 5),
ROCKCHIP_PVTPLL_VOLT_SEL(1512000000, 0, 34, 5),
ROCKCHIP_PVTPLL_VOLT_SEL(1416000000, 0, 38, 5),
ROCKCHIP_PVTPLL_VOLT_SEL(1296000000, 0, 38, 5),
ROCKCHIP_PVTPLL_VOLT_SEL(1200000000, 0, 38, 3),
ROCKCHIP_PVTPLL_VOLT_SEL(1008000000, 0, 56, 1),

View File

@@ -322,6 +322,7 @@ struct drm_dp_link_train {
struct dw_dp_link {
u8 dpcd[DP_RECEIVER_CAP_SIZE];
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
unsigned char revision;
unsigned int max_rate;
unsigned int rate;
@@ -439,6 +440,14 @@ struct dw_dp_mst_enc {
bool active;
};
struct dw_dp_dfp {
int min_tmds_clock;
int max_tmds_clock;
int max_dotclock;
u8 max_bpc;
bool ycbcr_444_to_420;
};
struct dw_dp {
const struct dw_dp_chip_data *chip_data;
struct device *dev;
@@ -473,12 +482,14 @@ struct dw_dp {
struct dw_dp_video video;
struct dw_dp_audio *audio;
struct dw_dp_compliance compliance;
struct dw_dp_dfp dfp;
DECLARE_BITMAP(sdp_reg_bank, SDP_REG_BANK_SIZE);
bool split_mode;
bool dual_connector_split;
bool left_display;
bool branch_ycbcr_444_to_422;
struct dw_dp *left;
struct dw_dp *right;
@@ -1661,6 +1672,35 @@ static int dw_dp_update_hdr_property(struct drm_connector *connector)
return ret;
}
static void dw_dp_update_dfp(struct dw_dp *dp, struct edid *edid)
{
struct dw_dp_link *link = &dp->link;
struct dw_dp_dfp *dfp = &dp->dfp;
bool ycbcr_420_passthrough, ycbcr_444_to_420;
memset(&dp->dfp, 0, sizeof(dp->dfp));
dfp->max_bpc = drm_dp_downstream_max_bpc(link->dpcd, link->downstream_ports, edid);
dfp->max_dotclock = drm_dp_downstream_max_dotclock(link->dpcd, link->downstream_ports);
dfp->min_tmds_clock = drm_dp_downstream_min_tmds_clock(link->dpcd, link->downstream_ports,
edid);
dfp->max_tmds_clock = drm_dp_downstream_max_tmds_clock(link->dpcd, link->downstream_ports,
edid);
ycbcr_420_passthrough = drm_dp_downstream_420_passthrough(link->dpcd,
link->downstream_ports);
ycbcr_444_to_420 = drm_dp_downstream_444_to_420_conversion(link->dpcd,
link->downstream_ports);
/* Prefer 4:2:0 passthrough over 4:4:4->4:2:0 conversion */
dfp->ycbcr_444_to_420 = ycbcr_444_to_420 && !ycbcr_420_passthrough;
dw_dp_dbg(dp,
"dfp max bpc:%d, max dot:%d, min tmds:%d, max tmds:%d, ycbcr 444 to 420:%d\n",
dfp->max_bpc, dfp->max_dotclock, dfp->min_tmds_clock, dfp->max_tmds_clock,
dfp->ycbcr_444_to_420);
}
static int dw_dp_connector_get_modes(struct drm_connector *connector)
{
struct dw_dp *dp = connector_to_dp(connector);
@@ -1689,6 +1729,7 @@ static int dw_dp_connector_get_modes(struct drm_connector *connector)
drm_connector_update_edid_property(connector, edid);
num_modes = drm_add_edid_modes(connector, edid);
dw_dp_update_hdr_property(connector);
dw_dp_update_dfp(dp, edid);
kfree(edid);
}
}
@@ -1922,6 +1963,9 @@ static int dw_dp_link_probe(struct dw_dp *dp)
}
link->sink_support_mst = drm_dp_read_mst_cap(&dp->aux, dp->link.dpcd);
ret = drm_dp_read_downstream_info(&dp->aux, link->dpcd, link->downstream_ports);
if (ret)
return ret;
ret = drm_dp_dpcd_readb(&dp->aux, DP_DPRX_FEATURE_ENUMERATION_LIST,
&dpcd);
@@ -3016,6 +3060,9 @@ static int dw_dp_video_enable(struct dw_dp *dp, struct dw_dp_video *video, int s
FIELD_PREP(HBLANK_INTERVAL_EN, 1) |
FIELD_PREP(HBLANK_INTERVAL, hblank_interval));
if (dp->branch_ycbcr_444_to_422)
drm_dp_dpcd_writeb(&dp->aux, DP_PROTOCOL_CONVERTER_CONTROL_1,
DP_CONVERSION_TO_YCBCR420_ENABLE);
/* Video stream enable */
regmap_update_bits(dp->regmap, DPTX_VSAMPLE_CTRL_N(stream_id), VIDEO_STREAM_ENABLE,
FIELD_PREP(VIDEO_STREAM_ENABLE, 1));
@@ -3419,6 +3466,39 @@ static ssize_t dw_dp_sim_aux_transfer(struct drm_dp_aux *aux,
return dw_dp_aux_transfer(aux, msg);
}
static int dw_dp_hdmi_tmds_clock(int clock, int bpc, bool ycbcr420_output)
{
if (ycbcr420_output)
clock /= 2;
return DIV_ROUND_CLOSEST(clock * bpc, 8);
}
static enum drm_mode_status
dw_dp_tmds_clock_valid(struct dw_dp *dp, int bpc,
const struct drm_display_mode *mode,
const struct drm_display_info *info)
{
int tmds_clock, min_tmds_clock, max_tmds_clock;
bool ycbcr_420_output;
if (dp->dfp.max_dotclock && mode->clock > dp->dfp.max_dotclock)
return MODE_CLOCK_HIGH;
ycbcr_420_output = drm_mode_is_420_only(info, mode);
tmds_clock = dw_dp_hdmi_tmds_clock(mode->clock, bpc, ycbcr_420_output);
min_tmds_clock = dp->dfp.min_tmds_clock;
max_tmds_clock = min(dp->dfp.max_tmds_clock, info->max_tmds_clock);
if (min_tmds_clock && tmds_clock < min_tmds_clock)
return MODE_CLOCK_LOW;
if (max_tmds_clock && tmds_clock > max_tmds_clock)
return MODE_CLOCK_HIGH;
return MODE_OK;
}
static enum drm_mode_status
dw_dp_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_info *info,
@@ -3446,6 +3526,7 @@ dw_dp_bridge_mode_valid(struct drm_bridge *bridge,
min_bpp = 24;
if (!link->vsc_sdp_extension_for_colorimetry_supported &&
!dp->dfp.ycbcr_444_to_420 &&
drm_mode_is_420_only(info, &m))
return MODE_NO_420;
@@ -3455,7 +3536,8 @@ dw_dp_bridge_mode_valid(struct drm_bridge *bridge,
if (m.flags & DRM_MODE_FLAG_DBLCLK)
return MODE_H_ILLEGAL;
return MODE_OK;
/* Assume 8bpc for the HDMI/DVI TMDS clock check */
return dw_dp_tmds_clock_valid(dp, 8, mode, info);
}
static void _dw_dp_loader_protect(struct dw_dp *dp, bool on)
@@ -4799,6 +4881,9 @@ out:
}
}
if (status == connector_status_disconnected)
memset(&dp->dfp, 0, sizeof(dp->dfp));
return status;
}
@@ -4852,6 +4937,7 @@ static u32 *dw_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
unsigned int i, j = 0;
dp->eotf_type = dw_dp_get_eotf(conn_state);
dp->branch_ycbcr_444_to_422 = false;
if (dp->split_mode || dp->dual_connector_split)
drm_mode_convert_to_origin_mode(&mode);
@@ -4895,9 +4981,16 @@ static u32 *dw_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
fmt->color_format == DRM_COLOR_FORMAT_YCBCR420)
continue;
if (drm_mode_is_420_only(di, &mode) &&
fmt->color_format != DRM_COLOR_FORMAT_YCBCR420)
continue;
if (drm_mode_is_420_only(di, &mode)) {
if (dp->dfp.ycbcr_444_to_420) {
dp->branch_ycbcr_444_to_422 = true;
if (fmt->color_format != DRM_COLOR_FORMAT_YCBCR444)
continue;
} else {
if (fmt->color_format != DRM_COLOR_FORMAT_YCBCR420)
continue;
}
}
if (!dw_dp_bandwidth_ok(dp, &mode, fmt->bpp, link->lanes, link->max_rate))
continue;
@@ -4905,6 +4998,12 @@ static u32 *dw_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
if (dw_dp_is_hdr_eotf(dp->eotf_type) && fmt->bpc < 8)
continue;
if (dp->dfp.max_bpc && fmt->bpc > dp->dfp.max_bpc)
continue;
if (dp->dfp.max_tmds_clock && fmt->bpc > 8)
continue;
output_fmts[j++] = fmt->bus_format;
}

View File

@@ -241,6 +241,11 @@ static const uint64_t format_modifiers_afbc[] = {
AFBC_FORMAT_MOD_SPARSE |
AFBC_FORMAT_MOD_SPLIT),
/* SPLIT mandates SPARSE */
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
AFBC_FORMAT_MOD_SPARSE |
AFBC_FORMAT_MOD_SPLIT),
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID,
};

View File

@@ -14,6 +14,8 @@
struct rockchip_hwspinlock {
void __iomem *io_base;
u32 user_id;
u32 user_id_mask;
struct hwspinlock_device bank;
};
@@ -22,35 +24,35 @@ struct rockchip_hwspinlock {
/* Hardware spinlock register offsets */
#define HWSPINLOCK_OFFSET(x) (0x4 * (x))
#define HWSPINLOCK_ID_MASK 0x0F
#define HWLOCK_DEFAULT_USER 0x01
static u32 hwlock_user_id;
#define HWLOCK_DEFAULT_USER 0x01
#define HWLOCK_DEFAULT_USER_ID_MASK 0x0F
static int rockchip_hwspinlock_trylock(struct hwspinlock *lock)
{
struct rockchip_hwspinlock *hwlock = dev_get_drvdata(lock->bank->dev);
void __iomem *lock_addr = lock->priv;
writel(hwlock_user_id, lock_addr);
writel(hwlock->user_id, lock_addr);
/*
* Get only first 4 bits and compare to HWSPINLOCK_OWNER_ID,
* if equal, we attempt to acquire the lock, otherwise,
* someone else has it.
*/
return (hwlock_user_id == (readl(lock_addr) & HWSPINLOCK_ID_MASK));
return (hwlock->user_id == (readl(lock_addr) & hwlock->user_id_mask));
}
static void rockchip_hwspinlock_unlock(struct hwspinlock *lock)
{
struct rockchip_hwspinlock *hwlock = dev_get_drvdata(lock->bank->dev);
void __iomem *lock_addr = lock->priv;
u32 lock_owner = readl(lock_addr) & HWSPINLOCK_ID_MASK;
u32 lock_owner = readl(lock_addr) & hwlock->user_id_mask;
if (lock_owner != hwlock_user_id) {
if (lock_owner != hwlock->user_id) {
dev_warn(lock->bank->dev,
"WARNING: against user %u release a lock held by %u\n",
hwlock_user_id, lock_owner);
hwlock->user_id, lock_owner);
return;
}
@@ -85,11 +87,18 @@ static int rockchip_hwspinlock_probe(struct platform_device *pdev)
if (IS_ERR(hwspin->io_base))
return PTR_ERR(hwspin->io_base);
ret = device_property_read_u32(&pdev->dev, "rockchip,hwlock-max-user",
&hwspin->user_id_mask);
if (ret || !hwspin->user_id_mask)
hwspin->user_id_mask = HWLOCK_DEFAULT_USER_ID_MASK;
ret = device_property_read_u32(&pdev->dev, "rockchip,hwlock-user-id",
&hwlock_user_id);
if (ret || !hwlock_user_id || hwlock_user_id > HWSPINLOCK_ID_MASK)
hwlock_user_id = HWLOCK_DEFAULT_USER;
dev_info(&pdev->dev, "hwlock user id %u, locks %u\n", hwlock_user_id, num_locks);
&hwspin->user_id);
if (ret || !hwspin->user_id || hwspin->user_id > hwspin->user_id_mask)
hwspin->user_id = HWLOCK_DEFAULT_USER;
dev_info(&pdev->dev, "hwlock user id %u, locks %u, maximum user %u\n",
hwspin->user_id, num_locks, hwspin->user_id_mask);
for (idx = 0; idx < num_locks; idx++) {
hwlock = &hwspin->bank.lock[idx];

View File

@@ -94,6 +94,7 @@ config KMX61
be called kmx61.
source "drivers/iio/imu/inv_icm42600/Kconfig"
source "drivers/iio/imu/inv_icm42607/Kconfig"
source "drivers/iio/imu/inv_icm42670/Kconfig"
source "drivers/iio/imu/inv_mpu6050/Kconfig"
source "drivers/iio/imu/st_lsm6dsr/Kconfig"

View File

@@ -22,6 +22,7 @@ obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o
obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o
obj-y += inv_icm42600/
obj-y += inv_icm42607/
obj-y += inv_icm42670/
obj-y += inv_mpu6050/

View File

@@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0
config IIO_INV_ICM42607
tristate "INV ICM42607 driver for 6-axis IMU MEMS sensors"
depends on I2C
select IIO_BUFFER
select IIO_KFIFO_BUF
select IIO_INV_ICM42607_I2C if (I2C)
help
Say yes here to build support for INV icm42607 sensor.
To compile this driver as a module, choose M here: the module
will be called inv_icm42607.
config IIO_INV_ICM42607_I2C
tristate
depends on IIO_INV_ICM42607
select REGMAP_I2C

View File

@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
inv-icm42607-y := invimu_core.o invimu_iio.o icm42607.o
obj-$(CONFIG_IIO_INV_ICM42607) += inv-icm42607.o
inv-icm42607-i2c-y := invimu_i2c.o
obj-$(CONFIG_IIO_INV_ICM42607_I2C) += inv-icm42607-i2c.o

View File

@@ -0,0 +1,416 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2025 Rockchip Electronics Co., Ltd.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#include "icm42607.h"
static struct imu_reg_value_map icm_acce_only_regs_cfg_map[] = {
{REG_PWR_MGMT_0, 0x03}, /* Accelerometer only mode */
{REG_ACCEL_CONFIG0, 0x9},/* 100Hz, 16g range */
};
static struct imu_reg_value_map icm_acce_gyro_regs_cfg_map[] = {
{REG_PWR_MGMT_0, 0x0f}, /* Both accelerometer and gyroscope on */
{REG_ACCEL_CONFIG0, 0x9},/* 100Hz, 16g range */
{REG_GYRO_CONFIG0, 0x9}, /* 100Hz, 2000dps range */
};
static struct imu_reg_value_map icm_powerdown_regs_cfg_map[] = {
{REG_PWR_MGMT_0, 0x00},
};
static int icm42607_regs_cfg_write(struct imu_ctrb *ctrb,
struct imu_reg_value_map *maparrays, int regcnt)
{
int i, ret;
if (ctrb == NULL)
return -1;
if (regcnt <= 0) {
dev_err(ctrb->dev, "reg map len err\n");
return -1;
}
for (i = 0; i < regcnt; i++) {
ret = regmap_write(ctrb->regmap, maparrays[i].reg, maparrays[i].value);
if (ret) {
dev_err(ctrb->dev, "regmap write err\n");
return -1;
}
}
return 0;
}
static int icm42607_read_id(void *ctrbp)
{
int ret;
unsigned int val = 0;
struct imu_ctrb *ctrb = (struct imu_ctrb *)ctrbp;
if (ctrb == NULL)
return -1;
ret = regmap_read(ctrb->regmap, REG_WHO_AM_I, &val);
if (ret) {
dev_err(ctrb->dev, "regmap_read err\n");
return 0;
}
dev_info(ctrb->dev, "ID = 0x%02x\n", val);
return (int)val;
}
static int icm42607_mode_set(void *ctrbp, int mode)
{
int ret = 0;
struct imu_ctrb *ctrb = (struct imu_ctrb *)ctrbp;
if (ctrb == NULL)
return -1;
switch (mode) {
case IMU_POWER_MODE_INIT:
break;
case IMU_POWER_MODE_DOWN:
ret = icm42607_regs_cfg_write(ctrb, icm_powerdown_regs_cfg_map,
sizeof(icm_powerdown_regs_cfg_map) / sizeof(struct imu_reg_value_map));
break;
case IMU_POWER_ACCE_ONLY:
ret = icm42607_regs_cfg_write(ctrb, icm_acce_only_regs_cfg_map,
sizeof(icm_acce_only_regs_cfg_map) / sizeof(struct imu_reg_value_map));
break;
case IMU_POWER_ACCE_GYRO:
ret = icm42607_regs_cfg_write(ctrb, icm_acce_gyro_regs_cfg_map,
sizeof(icm_acce_gyro_regs_cfg_map) / sizeof(struct imu_reg_value_map));
break;
default:
ret = -1;
}
dev_info(ctrb->dev, "set power:%d success\n", mode);
return ret;
}
static int icm42607_mreg_write(struct imu_ctrb *ctrb, u16 addr, u8 data)
{
int ret;
u8 bank = addr >> 8;
u8 reg = addr & 0xFF;
ret = regmap_write(ctrb->regmap, REG_BLK_SEL_W, bank);
usleep_range(10, 11);
ret |= regmap_write(ctrb->regmap, REG_MADDR_W, reg);
usleep_range(10, 11);
ret |= regmap_write(ctrb->regmap, REG_M_W, data);
usleep_range(10, 11);
ret |= regmap_write(ctrb->regmap, REG_BLK_SEL_W, 0);
return ret;
}
static int icm42607_mreg_read(struct imu_ctrb *ctrb, u16 addr, u8 *data)
{
int ret;
u8 bank = addr >> 8;
u8 reg = addr & 0xFF;
unsigned int tmp;
ret = regmap_write(ctrb->regmap, REG_BLK_SEL_R, bank);
usleep_range(10, 11);
ret |= regmap_write(ctrb->regmap, REG_MADDR_R, reg);
usleep_range(10, 11);
ret |= regmap_read(ctrb->regmap, REG_M_R, &tmp);
usleep_range(10, 11);
ret |= regmap_write(ctrb->regmap, REG_BLK_SEL_R, 0);
if (!ret)
*data = (u8)tmp;
return ret;
}
static int icm42607_otp_reload(struct imu_ctrb *ctrb)
{
int ret;
u8 rb;
ret = regmap_write(ctrb->regmap, REG_PWR_MGMT_0, BIT_IDLE);
if (ret)
return ret;
usleep_range(20, 21);
/* OTP_COPY_MODE = 2'b01 */
ret = icm42607_mreg_read(ctrb, REG_OTP_CONFIG_MREG_TOP1, &rb);
if (ret)
return ret;
rb &= ~OTP_COPY_MODE_MASK;
rb |= BIT_OTP_COPY_NORMAL;
ret = icm42607_mreg_write(ctrb, REG_OTP_CONFIG_MREG_TOP1, rb);
if (ret)
return ret;
/* OTP_PWR_DOWN = 0 */
ret = icm42607_mreg_read(ctrb, REG_OTP_CTRL7_MREG_OTP, &rb);
if (ret)
return ret;
rb &= ~BIT_OTP_PWR_DOWN;
ret = icm42607_mreg_write(ctrb, REG_OTP_CTRL7_MREG_OTP, rb);
if (ret)
return ret;
usleep_range(300, 400);
/* OTP_RELOAD = 1 */
ret = icm42607_mreg_read(ctrb, REG_OTP_CTRL7_MREG_OTP, &rb);
if (ret)
return ret;
rb |= BIT_OTP_RELOAD;
ret = icm42607_mreg_write(ctrb, REG_OTP_CTRL7_MREG_OTP, rb);
if (ret)
return ret;
usleep_range(280, 380);
return 0;
}
static int icm42607_set_default_register(struct imu_ctrb *ctrb)
{
int s = 0;
s |= regmap_write(ctrb->regmap, REG_GYRO_CONFIG0, 0x69);
s |= regmap_write(ctrb->regmap, REG_ACCEL_CONFIG0, 0x69);
s |= regmap_write(ctrb->regmap, REG_APEX_CONFIG0, 0x08);
s |= regmap_write(ctrb->regmap, REG_APEX_CONFIG1, 0x02);
s |= regmap_write(ctrb->regmap, REG_WOM_CONFIG, 0x00);
s |= regmap_write(ctrb->regmap, REG_FIFO_CONFIG1, 0x01);
s |= regmap_write(ctrb->regmap, REG_FIFO_CONFIG2, 0x00);
s |= regmap_write(ctrb->regmap, REG_FIFO_CONFIG3, 0x00);
s |= icm42607_mreg_write(ctrb, REG_FIFO_CONFIG5_MREG_TOP1, 0x20);
s |= icm42607_mreg_write(ctrb, REG_ST_CONFIG_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_INT_SOURCE7_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_INT_SOURCE8_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_INT_SOURCE9_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_INT_SOURCE10_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_APEX_CONFIG2_MREG_TOP1, 0xA2);
s |= icm42607_mreg_write(ctrb, REG_APEX_CONFIG3_MREG_TOP1, 0x85);
s |= icm42607_mreg_write(ctrb, REG_APEX_CONFIG4_MREG_TOP1, 0x51);
s |= icm42607_mreg_write(ctrb, REG_APEX_CONFIG5_MREG_TOP1, 0x80);
s |= icm42607_mreg_write(ctrb, REG_APEX_CONFIG9_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_APEX_CONFIG10_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_APEX_CONFIG11_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_ACCEL_WOM_X_THR_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_ACCEL_WOM_Y_THR_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_ACCEL_WOM_Z_THR_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_GOS_USER0_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_GOS_USER1_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_GOS_USER2_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_GOS_USER3_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_GOS_USER4_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_GOS_USER5_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_GOS_USER6_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_GOS_USER7_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_GOS_USER8_MREG_TOP1, 0x00);
s |= icm42607_mreg_write(ctrb, REG_APEX_CONFIG12_MREG_TOP1, 0x00);
return s ? -EIO : 0;
}
static int icm42607_chip_init(void *ctrbp)
{
struct imu_ctrb *ctrb = (struct imu_ctrb *)ctrbp;
int ret;
unsigned int val;
if (!ctrb)
return -EINVAL;
ret = icm42607_otp_reload(ctrb);
if (ret) {
dev_err(ctrb->dev, "OTP reload fail(%d)\n", ret);
return ret;
}
ret = icm42607_set_default_register(ctrb);
if (ret) {
dev_err(ctrb->dev, "set default reg fail(%d)\n", ret);
return ret;
}
val = BIT_SENSOR_DATA_ENDIAN | BIT_FIFO_COUNT_ENDIAN;
ret = regmap_write(ctrb->regmap, REG_INTF_CONFIG0, val);
if (ret)
return ret;
val = BIT_CLK_SEL_PLL | BIT_I3C_SDR_EN | BIT_I3C_DDR_EN;
ret = regmap_write(ctrb->regmap, REG_INTF_CONFIG1, val);
if (ret)
return ret;
return icm42607_mode_set(ctrb, IMU_POWER_MODE_DOWN);
}
static int icm42607_read_acce_gyro_raw(void *ctrbp, void *rawdatap, uint8_t reg)
{
int ret = 0;
uint8_t rawarrays[6] = { 0 };
struct imu_ctrb *ctrb = (struct imu_ctrb *)ctrbp;
struct imu_3axis_data *rawdata = (struct imu_3axis_data *)rawdatap;
if (ctrb == NULL || rawdata == NULL)
return -1;
ret = regmap_bulk_read(ctrb->regmap, reg, rawarrays, sizeof(rawarrays));
if (!ret) {
rawdata->raw[0] = ((uint16_t)rawarrays[0]) << 8 | rawarrays[1];
rawdata->raw[1] = ((uint16_t)rawarrays[2]) << 8 | rawarrays[3];
rawdata->raw[2] = ((uint16_t)rawarrays[4]) << 8 | rawarrays[5];
} else {
dev_err(ctrb->dev, "regmap_bulk_read err:%d\n", ret);
}
return ret;
}
static int icm42607_read_acce_raw(void *ctrbp, void *rawdatap)
{
return icm42607_read_acce_gyro_raw(ctrbp, rawdatap, REG_ACCEL_DATA_X0_UI);
}
static int icm42607_read_gyro_raw(void *ctrbp, void *rawdatap)
{
return icm42607_read_acce_gyro_raw(ctrbp, rawdatap, REG_GYRO_DATA_X0_UI);
}
static int icm42607_read_asix_one(void *ctrbp, int addr, int *datap)
{
int ret;
struct imu_3axis_data accedata = {0}, gyrodata = {0};
ret = icm42607_read_acce_raw(ctrbp, &accedata);
ret = ret || icm42607_read_gyro_raw(ctrbp, &gyrodata);
switch (addr) {
case 0:
(*datap) = accedata.raw[0];
break;
case 1:
(*datap) = accedata.raw[1];
break;
case 2:
(*datap) = accedata.raw[2];
break;
case 3:
(*datap) = gyrodata.raw[0];
break;
case 4:
(*datap) = gyrodata.raw[1];
break;
case 5:
(*datap) = gyrodata.raw[2];
break;
}
return ret;
}
static int icm42607_set_accel_offset(void *ctrbp, int offset, int axis)
{
struct imu_ctrb *ctrb = (struct imu_ctrb *)ctrbp;
int ret = 0;
u8 reg_l, reg_h;
u16 val;
offset = clamp(offset, -2048, 2047);
val = (u16)(offset & 0x0FFF);
switch (axis) {
case 0: /* X */
reg_l = REG_GOS_USER5_MREG_TOP1;
reg_h = REG_GOS_USER4_MREG_TOP1;
break;
case 1: /* Y */
reg_l = REG_GOS_USER6_MREG_TOP1;
reg_h = REG_GOS_USER7_MREG_TOP1;
break;
case 2: /* Z */
reg_l = REG_GOS_USER8_MREG_TOP1;
reg_h = REG_GOS_USER7_MREG_TOP1;
break;
default:
return -EINVAL;
}
ret |= icm42607_mreg_write(ctrb, reg_l, val & 0xFF);
ret |= icm42607_mreg_write(ctrb, reg_h, (val >> 8) & 0x0F);
return ret ? -EIO : 0;
}
static int icm42607_set_gyro_offset(void *ctrbp, int offset, int axis)
{
struct imu_ctrb *ctrb = (struct imu_ctrb *)ctrbp;
int ret = 0;
u8 reg_l, reg_h;
u16 val;
offset = clamp(offset, -2048, 2047);
val = (u16)(offset & 0x0FFF);
switch (axis) {
case 0: /* X */
reg_l = REG_GOS_USER0_MREG_TOP1;
reg_h = REG_GOS_USER1_MREG_TOP1;
break;
case 1: /* Y */
reg_l = REG_GOS_USER2_MREG_TOP1;
reg_h = REG_GOS_USER1_MREG_TOP1;
break;
case 2: /* Z */
reg_l = REG_GOS_USER3_MREG_TOP1;
reg_h = REG_GOS_USER4_MREG_TOP1;
break;
default:
return -EINVAL;
}
ret |= icm42607_mreg_write(ctrb, reg_l, val & 0xFF);
ret |= icm42607_mreg_write(ctrb, reg_h, (val >> 8) & 0x0F);
return ret ? -EIO : 0;
}
static const struct imu_info icm42607_info = {
.name = "icm42607",
.id = ICM42607_CHIP_ID,
.read_id = icm42607_read_id,
.chip_init = icm42607_chip_init,
.mode_set = icm42607_mode_set,
.read_acce_raw = icm42607_read_acce_raw,
.read_gyro_raw = icm42607_read_gyro_raw,
.read_asix_one = icm42607_read_asix_one,
.set_accel_offset = icm42607_set_accel_offset,
.set_gyro_offset = icm42607_set_gyro_offset,
};
struct imu_info *icm42607_chip_probe(struct imu_ctrb *ctrb)
{
int chip_id = -1;
chip_id = icm42607_read_id(ctrb);
if (chip_id == icm42607_info.id) {
dev_info(ctrb->dev, "probe sensor: %s, id = 0x%02X\n",
icm42607_info.name, icm42607_info.id);
return (struct imu_info *)&icm42607_info;
}
return NULL;
}

View File

@@ -0,0 +1,435 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2025 Rockchip Electronics Co., Ltd.
*/
#ifndef __ICM42607_H__
#define __ICM42607_H__
#include "imu.h"
/* Registers and associated bit definitions */
/* Bank 0 */
#define REG_MISC_1 0x00
#define REG_CHIP_CONFIG_REG 0x01
#define REG_SIGNAL_PATH_RESET 0x02
#define REG_DRIVE_CONFIG_REG1 0x03
#define REG_DRIVE_CONFIG_REG2 0x04
#define REG_DRIVE_CONFIG_REG3 0x05
#define REG_INT_CONFIG_REG 0x06
#define REG_ODRGRID0 0x07
#define REG_ODRGRID1 0x08
#define REG_TEMP_DATA0_UI 0x09
#define REG_TEMP_DATA1_UI 0x0a
#define REG_ACCEL_DATA_X0_UI 0x0b
#define REG_ACCEL_DATA_X1_UI 0x0c
#define REG_ACCEL_DATA_Y0_UI 0x0d
#define REG_ACCEL_DATA_Y1_UI 0x0e
#define REG_ACCEL_DATA_Z0_UI 0x0f
#define REG_ACCEL_DATA_Z1_UI 0x10
#define REG_GYRO_DATA_X0_UI 0x11
#define REG_GYRO_DATA_X1_UI 0x12
#define REG_GYRO_DATA_Y0_UI 0x13
#define REG_GYRO_DATA_Y1_UI 0x14
#define REG_GYRO_DATA_Z0_UI 0x15
#define REG_GYRO_DATA_Z1_UI 0x16
#define REG_TMST_FSYNC1 0x17
#define REG_TMST_FSYNC2 0x18
#define REG_ODR_LP_STATUS 0x19
#define REG_PWR_MGMT_0 0x1f
#define REG_GYRO_CONFIG0 0x20
#define REG_ACCEL_CONFIG0 0x21
#define REG_TEMP_CONFIG0 0x22
#define REG_GYRO_CONFIG1 0x23
#define REG_ACCEL_CONFIG1 0x24
#define REG_APEX_CONFIG0 0x25
#define REG_APEX_CONFIG1 0x26
#define REG_WOM_CONFIG 0x27
#define REG_FIFO_CONFIG1 0x28
#define REG_FIFO_CONFIG2 0x29
#define REG_FIFO_CONFIG3 0x2a
#define REG_INT_SOURCE0 0x2b
#define REG_INT_SOURCE1 0x2c
#define REG_INT_SOURCE3 0x2d
#define REG_INT_SOURCE4 0x2e
#define REG_FIFO_LOST_PKT0 0x2f
#define REG_FIFO_LOST_PKT1 0x30
#define REG_APEX_DATA0 0x31
#define REG_APEX_DATA1 0x32
#define REG_APEX_DATA2 0x33
#define REG_APEX_DATA3 0x34
#define REG_INTF_CONFIG0 0x35
#define REG_INTF_CONFIG1 0x36
#define REG_INT_STATUS_DRDY 0x39
#define REG_INT_STATUS 0x3a
#define REG_INT_STATUS2 0x3b
#define REG_INT_STATUS3 0x3c
#define REG_FIFO_BYTE_COUNT1 0x3d
#define REG_FIFO_BYTE_COUNT2 0x3e
#define REG_FIFO_DATA_REG 0x3f
#define REG_S4S_GYRO_TPH1 0x40
#define REG_S4S_GYRO_TPH2 0x41
#define REG_S4S_ACCEL_TPH1 0x42
#define REG_S4S_ACCEL_TPH2 0x43
#define REG_S4S_RR 0x44
#define REG_GYR_BIAS_CFG1 0x46
#define REG_WHO_AM_I 0x75
#define REG_S4S_ST 0x76
#define REG_S4S_ST_CLONE 0x77
#define REG_S4S_DT 0x78
#define REG_BLK_SEL_W 0x79
#define REG_MADDR_W 0x7a
#define REG_M_W 0x7b
#define REG_BLK_SEL_R 0x7c
#define REG_MADDR_R 0x7d
#define REG_M_R 0x7e
/* MREG_TOP1 */
#define REG_TMST_CONFIG1_MREG_TOP1 0x00
#define REG_FIFO_CONFIG5_MREG_TOP1 0x01
#define REG_FIFO_CONFIG6_MREG_TOP1 0x02
#define REG_FSYNC_CONFIG_MREG_TOP1 0x03
#define REG_INT_CONFIG0_MREG_TOP1 0x04
#define REG_INT_CONFIG1_MREG_TOP1 0x05
#define REG_AFSR_CONFIG0_MREG_TOP1 0x07
#define REG_AFSR_CONFIG1_MREG_TOP1 0x08
#define REG_TBC_RCOSC_MREG_TOP1 0x0d
#define REG_TBC_PLL_MREG_TOP1 0x0e
#define REG_ST_CONFIG_MREG_TOP1 0x13
#define REG_SELFTEST_MREG_TOP1 0x14
#define REG_PADS_CONFIG3_MREG_TOP1 0x17
#define REG_TEMP_CONFIG1_MREG_TOP1 0x1c
#define REG_TEMP_CONFIG3_MREG_TOP1 0x1e
#define REG_S4S_CONFIG1_MREG_TOP1 0x1f
#define REG_S4S_CONFIG2_MREG_TOP1 0x20
#define REG_S4S_FREQ_RATIO1_MREG_TOP1 0x21
#define REG_S4S_FREQ_RATIO2_MREG_TOP1 0x22
#define REG_INTF_CONFIG6_MREG_TOP1 0x23
#define REG_INTF_CONFIG10_MREG_TOP1 0x25
#define REG_INTF_CONFIG7_MREG_TOP1 0x28
#define REG_OTP_CONFIG_MREG_TOP1 0x2b
#define REG_INT_SOURCE6_MREG_TOP1 0x2f
#define REG_INT_SOURCE7_MREG_TOP1 0x30
#define REG_INT_SOURCE8_MREG_TOP1 0x31
#define REG_INT_SOURCE9_MREG_TOP1 0x32
#define REG_INT_SOURCE10_MREG_TOP1 0x33
#define REG_GYRO_PWR_CFG0_MREG_TOP1 0x38
#define REG_ACCEL_CP_CFG0_MREG_TOP1 0x39
#define REG_APEX_CONFIG2_MREG_TOP1 0x44
#define REG_APEX_CONFIG3_MREG_TOP1 0x45
#define REG_APEX_CONFIG4_MREG_TOP1 0x46
#define REG_APEX_CONFIG5_MREG_TOP1 0x47
#define REG_APEX_CONFIG9_MREG_TOP1 0x48
#define REG_APEX_CONFIG10_MREG_TOP1 0x49
#define REG_APEX_CONFIG11_MREG_TOP1 0x4a
#define REG_APEX_CONFIG12_MREG_TOP1 0x67
#define REG_ACCEL_WOM_X_THR_MREG_TOP1 0x4b
#define REG_ACCEL_WOM_Y_THR_MREG_TOP1 0x4c
#define REG_ACCEL_WOM_Z_THR_MREG_TOP1 0x4d
#define REG_GOS_USER0_MREG_TOP1 0x4e
#define REG_GOS_USER1_MREG_TOP1 0x4f
#define REG_GOS_USER2_MREG_TOP1 0x50
#define REG_GOS_USER3_MREG_TOP1 0x51
#define REG_GOS_USER4_MREG_TOP1 0x52
#define REG_GOS_USER5_MREG_TOP1 0x53
#define REG_GOS_USER6_MREG_TOP1 0x54
#define REG_GOS_USER7_MREG_TOP1 0x55
#define REG_GOS_USER8_MREG_TOP1 0x56
#define REG_ST_STATUS1_MREG_TOP1 0x63
#define REG_ST_STATUS2_MREG_TOP1 0x64
/* MMEM_TOP */
#define REG_XG_ST_DATA_MMEM_TOP 0x5000
#define REG_YG_ST_DATA_MMEM_TOP 0x5001
#define REG_ZG_ST_DATA_MMEM_TOP 0x5002
#define REG_XA_ST_DATA_MMEM_TOP 0x5003
#define REG_YA_ST_DATA_MMEM_TOP 0x5004
#define REG_ZA_ST_DATA_MMEM_TOP 0x5005
/* MREG_OTP */
#define REG_OTP_CTRL7_MREG_OTP 0x2806
/* Bank0 REG_GYRO_CONFIG0/REG_ACCEL_CONFIG0 */
#define SHIFT_GYRO_FS_SEL 5
#define SHIFT_ACCEL_FS_SEL 5
#define SHIFT_ODR_CONF 0
#define BIT_GYRO_FSR 0x60
#define BIT_GYRO_ODR 0x0F
#define BIT_ACCEL_FSR 0x60
#define ACCEL_FS_SEL 3 //(-2G, +2G)
#define GYRO_FS_SEL 3 //00~11:2000dps\1000dps\500dps\250dps
#define BIT_ACCEL_ODR 0x0F
#define BIT_SENSOR_ODR_800HZ 0x06
#define BIT_SENSOR_ODR_400HZ 0x07
#define BIT_SENSOR_ODR_200HZ 0x08
#define BIT_SENSOR_ODR_100HZ 0x09
#define BIT_SENSOR_ODR_50HZ 0x0A
#define BIT_SENSOR_ODR_25HZ 0x0B
#define BIT_SENSOR_ODR_12HZ 0x0C
#define BIT_SENSOR_ODR_6HZ 0x0D
#define BIT_SENSOR_ODR_3HZ 0x0E
/* Bank0 REG_GYRO_CONFIG1 */
#define BIT_GYR_UI_FLT_BW_BYPASS 0x00
#define BIT_GYR_UI_FLT_BW_180HZ 0x01
#define BIT_GYR_UI_FLT_BW_121HZ 0x02
#define BIT_GYR_UI_FLT_BW_73HZ 0x03
#define BIT_GYR_UI_FLT_BW_53HZ 0x04
#define BIT_GYR_UI_FLT_BW_34HZ 0x05
#define BIT_GYR_UI_FLT_BW_25HZ 0x06
#define BIT_GYR_UI_FLT_BW_16HZ 0x07
#define BIT_GYR_UI_AVG_IND_2X 0x00
#define BIT_GYR_UI_AVG_IND_4X 0x10
#define BIT_GYR_UI_AVG_IND_8X 0x20
#define BIT_GYR_UI_AVG_IND_16X 0x30
#define BIT_GYR_UI_AVG_IND_32X 0x40
#define BIT_GYR_UI_AVG_IND_64X 0x50
/* Bank0 REG_ACCEL_CONFIG1 */
#define BIT_ACC_FILT_BW_IND_BYPASS 0x00
#define BIT_ACC_FILT_BW_IND_180HZ 0x01
#define BIT_ACC_FILT_BW_IND_121HZ 0x02
#define BIT_ACC_FILT_BW_IND_73HZ 0x03
#define BIT_ACC_FILT_BW_IND_53HZ 0x04
#define BIT_ACC_FILT_BW_IND_34HZ 0x05
#define BIT_ACC_FILT_BW_IND_25HZ 0x06
#define BIT_ACC_FILT_BW_IND_16HZ 0x07
#define BIT_ACC_UI_AVG_IND_2X 0x00
#define BIT_ACC_UI_AVG_IND_4X 0x10
#define BIT_ACC_UI_AVG_IND_8X 0x20
#define BIT_ACC_UI_AVG_IND_16X 0x30
#define BIT_ACC_UI_AVG_IND_32X 0x40
#define BIT_ACC_UI_AVG_IND_64X 0x50
/* Bank0 REG_INT_CONFIG_REG */
#define SHIFT_INT1_MODE 0x02
#define SHIFT_INT1_DRIVE_CIRCUIT 0x01
#define SHIFT_INT1_POLARITY 0x00
/* Bank0 REG_PWR_MGMT_0 */
#define BIT_ACCEL_MODE_OFF 0x00
#define BIT_ACCEL_MODE_LPM 0x02
#define BIT_ACCEL_MODE_LNM 0x03
#define BIT_ACCEL_MODE_MASK 0x03
#define BIT_GYRO_MODE_OFF 0x00
#define BIT_GYRO_MODE_STBY 0x04
#define BIT_GYRO_MODE_LPM 0x08
#define BIT_GYRO_MODE_LNM 0x0c
#define BIT_GYRO_MODE_MASK 0x0c
#define BIT_IDLE 0x10
#define BIT_ACCEL_LP_CLK_SEL 0x80
/* Bank0 REG_SIGNAL_PATH_RESET */
#define BIT_FIFO_FLUSH 0x04
#define BIT_SOFT_RESET_CHIP_CONFIG 0x10
/* Bank0 REG_INTF_CONFIG0 */
#define BIT_SIFS_CFG_I2C_ONLY 0x02
#define BIT_SIFS_CFG_SPI_ONLY 0x03
#define BIT_SENSOR_DATA_ENDIAN 0x10
#define BIT_FIFO_COUNT_ENDIAN 0x20
#define BIT_FIFO_COUNT_FORMAT 0x40
#define BIT_FIFO_SREG_INVALID_IND_DIS 0x80
/* Bank0 REG_INTF_CONFIG1 */
#define BIT_CLK_SEL_RC 0x00
#define BIT_CLK_SEL_PLL 0x01
#define BIT_CLK_SEL_DIS 0x03
#define BIT_I3C_DDR_EN 0x04
#define BIT_I3C_SDR_EN 0x08
#define BIT_GYRO_AFSR_MODE_LFS 0x00
#define BIT_GYRO_AFSR_MODE_HFS 0x20
#define BIT_GYRO_AFSR_MODE_DYN 0x40
/* Bank0 REG_FIFO_CONFIG1 */
#define BIT_FIFO_MODE_NO_BYPASS 0x00
#define BIT_FIFO_MODE_BYPASS 0x01
#define BIT_FIFO_MODE_STREAM 0x00
#define BIT_FIFO_MODE_STOPFULL 0x02
/* Bank 0 REG_INT_SOURCE0 */
#define BIT_INT_AGC_RDY_INT1_EN 0x01
#define BIT_INT_FIFO_FULL_INT1_EN 0x02
#define BIT_INT_FIFO_THS_INT1_EN 0x04
#define BIT_INT_DRDY_INT_EN 0x08
#define BIT_INT_RESET_DONE_INT1_EN 0x10
#define BIT_INT_PLL_RDY_INT1_EN 0x20
#define BIT_INT_FSYNC_INT1_EN 0x40
#define BIT_INT_ST_DONE_INT1_EN 0x80
/* Bank 0 REG_INT_SOURCE1 */
#define BIT_INT_WOM_X_INT1_EN 0x01
#define BIT_INT_WOM_Y_INT1_EN 0x02
#define BIT_INT_WOM_Z_INT1_EN 0x04
#define BIT_INT_WOM_XYZ_INT1_EN (BIT_INT_WOM_X_INT1_EN | \
BIT_INT_WOM_Y_INT1_EN | BIT_INT_WOM_Z_INT1_EN)
#define BIT_INT_SMD_INT1_EN 0x08
#define BIT_INT_I3C_PROTCL_ERR_INT1_EN 0x40
/* Bank0 REG_INT_STATUS_DRDY */
#define BIT_INT_STATUS_DRDY 0x01
/* Bank0 REG_INT_STATUS */
#define BIT_INT_STATUS_AGC_RDY 0x01
#define BIT_INT_STATUS_FIFO_FULL 0x02
#define BIT_INT_STATUS_FIFO_THS 0x04
#define BIT_INT_STATUS_RESET_DONE 0x10
#define BIT_INT_STATUS_PLL_RDY 0x20
#define BIT_INT_STATUS_FSYNC 0x40
#define BIT_INT_STATUS_ST_DONE 0x80
/* Bank0 REG_INT_STATUS2 */
#define BIT_INT_STATUS_WOM_Z 0x01
#define BIT_INT_STATUS_WOM_Y 0x02
#define BIT_INT_STATUS_WOM_X 0x04
#define BIT_INT_STATUS_WOM_XYZ (BIT_INT_STATUS_WOM_X | \
BIT_INT_STATUS_WOM_Y | BIT_INT_STATUS_WOM_Z)
#define BIT_INT_STATUS_SMD 0x08
/* Bank 0 REG_INT_STATUS3 */
#define BIT_INT_STATUS_LOWG_DET 0x02
#define BIT_INT_STATUS_FF_DET 0x04
#define BIT_INT_STATUS_TILT_DET 0x08
#define BIT_INT_STATUS_STEP_CNT_OVFL 0x10
#define BIT_INT_STATUS_STEP_DET 0x20
/* Bank0 REG_WOM_CONFIG */
#define BIT_WOM_EN_OFF 0x00
#define BIT_WOM_EN_ON 0x01
#define BIT_WOM_MODE_INITIAL 0x00
#define BIT_WOM_MODE_PREV 0x02
#define BIT_WOM_INT_MODE_OR 0x00
#define BIT_WOM_INT_MODE_AND 0x04
#define BIT_WOM_INT_DUR_LEGACY 0x00
#define BIT_WOM_INT_DUR_2ND 0x08
#define BIT_WOM_INT_DUR_3RD 0x10
#define BIT_WOM_INT_DUR_4TH 0x18
/* Bank0 REG_APEX_CONFIG0 */
#define BIT_DMP_SRAM_RESET_APEX 0x01
#define BIT_DMP_INIT_EN 0x04
#define BIT_DMP_POWER_SAVE_EN 0x08
/* Bank0 REG_APEX_CONFIG1 */
#define BIT_DMP_ODR_25HZ 0x00
#define BIT_DMP_ODR_50HZ 0x02
#define BIT_DMP_ODR_100HZ 0x03
#define BIT_DMP_PEDO_EN 0x08
#define BIT_DMP_TILT_EN 0x10
#define BIT_DMP_FF_EN 0x20
#define BIT_DMP_SMD_EN 0x40
/* REG_OTP_CONFIG_MREG_TOP1 */
#define BIT_OTP_COPY_NORMAL 0x04
#define BIT_OTP_COPY_ST_DATA 0x0C
#define OTP_COPY_MODE_MASK 0x0C
/* REG_INT_SOURCE6_MREG_TOP1 */
#define BIT_INT_TLT_DET_INT1_EN 0x08
#define BIT_INT_STEP_CNT_OVFL_INT1_EN 0x10
#define BIT_INT_STEP_DET_INT1_EN 0x20
#define BIT_INT_LOWG_INT1_EN 0x40
#define BIT_INT_FF_INT1_EN 0x80
/* REG_TMST_CONFIG1_MREG_TOP1 */
#define BIT_TMST_EN 0x01
#define BIT_TMST_FSYNC_EN 0x02
#define BIT_TMST_DELTA_EN 0x04
#define BIT_TMST_RESOL 0x08
#define BIT_TMST_ON_SREG_EN 0x10
#define BIT_ODR_EN_WITHOUT_SENSOR 0x40
/* REG_FIFO_CONFIG5_MREG_TOP1 */
#define BIT_FIFO_ACCEL_EN 0x01
#define BIT_FIFO_GYRO_EN 0x02
#define BIT_FIFO_TMST_FSYNC_EN 0x04
#define BIT_FIFO_HIRES_EN 0x08
#define BIT_RESUME_PARTIAL_RD 0x10
#define BIT_WM_GT_TH 0x20
/* REG_SELFTEST_MREG_TOP1 */
#define BIT_EN_AX_ST 0x01
#define BIT_EN_AY_ST 0x02
#define BIT_EN_AZ_ST 0x04
#define BIT_EN_GX_ST 0x08
#define BIT_EN_GY_ST 0x10
#define BIT_EN_GZ_ST 0x20
#define BIT_ACCEL_ST_EN 0x40
#define BIT_GYRO_ST_EN 0x80
/* REG_ST_CONFIG_MREG_TOP1 */
#define BIT_PD_ACCEL_CP45_ST_REG 0x80
#define SHIFT_GYRO_ST_LIM 0
#define SHIFT_ACCEL_ST_LIM 3
#define SHIFT_ST_NUM_SAMPLE 6
/* REG_ST_STATUS1_MREG_TOP1 */
#define BIT_DMP_AX_ST_PASS 0x02
#define BIT_DMP_AY_ST_PASS 0x04
#define BIT_DMP_AZ_ST_PASS 0x08
#define BIT_DMP_ACCEL_ST_DONE 0x10
#define BIT_DMP_ACCEL_ST_PASS 0x20
/* REG_ST_STATUS2_MREG_TOP1 */
#define BIT_DMP_GX_ST_PASS 0x02
#define BIT_DMP_GY_ST_PASS 0x04
#define BIT_DMP_GZ_ST_PASS 0x08
#define BIT_DMP_GYRO_ST_DONE 0x10
#define BIT_DMP_GYRO_ST_PASS 0x20
#define BIT_DMP_ST_INCOMPLETE 0x40
/* REG_OTP_CTRL7_MREG_OTP */
#define BIT_OTP_RELOAD 0x08
#define BIT_OTP_PWR_DOWN 0x02
/* fifo data packet header */
#define BIT_FIFO_HEAD_MSG 0x80
#define BIT_FIFO_HEAD_ACCEL 0x40
#define BIT_FIFO_HEAD_GYRO 0x20
#define BIT_FIFO_HEAD_20 0x10
#define BIT_FIFO_HEAD_TMSP_ODR 0x08
#define BIT_FIFO_HEAD_TMSP_NO_ODR 0x04
#define BIT_FIFO_HEAD_TMSP_FSYNC 0x0C
#define BIT_FIFO_HEAD_ODR_ACCEL 0x02
#define BIT_FIFO_HEAD_ODR_GYRO 0x01
/* data definitions */
#define FIFO_PACKET_BYTE_SINGLE 8
#define FIFO_PACKET_BYTE_6X 16
#define FIFO_PACKET_BYTE_HIRES 20
#define FIFO_COUNT_BYTE 2
/* sensor startup time */
#define INV_ICM43600_GYRO_START_TIME 100
#define INV_ICM43600_ACCEL_START_TIME 100
/* sensor stop time */
#define INV_ICM43600_GYRO_STOP_TIME 20
/* M-reg access wait tile */
#define INV_ICM42607_MCLK_WAIT_US 20
#define INV_ICM42607_BLK_SEL_WAIT_US 10
#define INV_ICM42607_MADDR_WAIT_US 10
#define INV_ICM42607_M_RW_WAIT_US 10
/* temperature sensor */
#define TEMP_SCALE 100 /* scale by 100 */
#define TEMP_LSB_PER_DEG 2 /* 2LSB=1degC */
#define TEMP_OFFSET 25 /* 25 degC */
/*
* INT configurations
* Polarity: 0 -> Active Low, 1 -> Active High
* Drive circuit: 0 -> Open Drain, 1 -> Push-Pull
* Mode: 0 -> Pulse, 1 -> Latch
*/
#define INT_POLARITY 1
#define INT_DRIVE_CIRCUIT 1
#define INT_MODE 0
#define ICM42607_CHIP_ID 0x61
struct imu_info *icm42607_chip_probe(struct imu_ctrb *ctrb);
#endif

View File

@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2025 Rockchip Electronics Co., Ltd.
*/
#ifndef __IMU_H__
#define __IMU_H__
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/version.h>
#include <linux/platform_device.h>
#include <linux/atomic.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/of_platform.h>
enum imu_power_mode {
IMU_POWER_MODE_INIT = 0,
IMU_POWER_MODE_DOWN = 1,
IMU_POWER_ACCE_ONLY = 2,
IMU_POWER_ACCE_GYRO = 3,
};
enum imu_sensor_id {
IMU_SENSOR_ID_ACCE = 0,
IMU_SENSOR_ID_GYRO,
IMU_SENSOR_ID_MAX
};
enum imu_position_id {
IMU_P_D_0 = 0,
IMU_P_D_90 = 1,
IMU_P_D_180 = 2,
IMU_P_D_270 = 3,
IMU_D_MAX
};
struct imu_3axis_data {
int16_t raw[3];
s64 ts;
};
struct imu_info {
char name[30];
int id;
int (*read_id)(void *ctrbp);
int (*mode_set)(void *ctrbp, int mode);
int (*chip_init)(void *ctrbp);
int (*read_acce_raw)(void *ctrbp, void *rawdata);
int (*read_gyro_raw)(void *ctrbp, void *rawdata);
int (*read_asix_one)(void *ctrbp, int addr, int *datap);
int (*set_accel_offset)(void *ctrbp, int offset, int axis);
int (*set_gyro_offset)(void *ctrbp, int offset, int axis);
};
struct imu_ctrb {
struct device *dev;
struct regmap *regmap;
struct iio_dev *iio_devs[IMU_SENSOR_ID_MAX];
struct mutex power_lock;
struct delayed_work pollingwork;
struct imu_info *chipinfo;
int irq;
int mode;
int debugon;
int position;
bool irq_enable;
};
struct imu_sensor {
char name[32];
enum imu_sensor_id id;
struct imu_ctrb *ctrb;
unsigned int odr;
int calibrated;
int32_t offset_x;
int32_t offset_y;
int32_t offset_z;
uint32_t readcnt;
struct imu_3axis_data rawdata;
};
struct imu_reg_value_map {
uint8_t reg;
uint8_t value;
};
#define IMU_POLLING_TIME_MS (10)
#endif

View File

@@ -0,0 +1,270 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2025 Rockchip Electronics Co., Ltd.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/pm_runtime.h>
#include "icm42607.h"
#include "invimu_iio.h"
#include "invimu_core.h"
#include "imu.h"
static ssize_t invimu_debug_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int val = -1;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct imu_sensor *sensor = iio_priv(indio_dev);
struct imu_ctrb *ctrb = sensor->ctrb;
if (sscanf(buf, "%d\n", &val) != 1) {
dev_err(ctrb->dev, "debugon para err\n");
return -1;
}
ctrb->debugon = val;
dev_info(ctrb->dev, "debugon set %d\n", val);
return size;
}
static IIO_DEVICE_ATTR(in_imu_debug, 0200, NULL, invimu_debug_store, 0);
static struct attribute *invimu_accel_attributes[] = {
&iio_dev_attr_in_imu_debug.dev_attr.attr,
NULL,
};
static const struct attribute_group invimu_accel_attribute_group = {
.attrs = invimu_accel_attributes,
};
static const struct iio_info invimu_acc_info = {
.attrs = &invimu_accel_attribute_group,
.read_raw = invimu_read_raw,
.read_avail = invimu_read_avail,
.write_raw = invimu_write_raw,
.write_raw_get_fmt = invimu_write_raw_get_fmt,
};
static struct attribute *invimu_anglvel_attributes[] = {
NULL,
};
static const struct attribute_group invimu_anglvel_attribute_group = {
.attrs = invimu_anglvel_attributes,
};
static const struct iio_info invimu_gryo_info = {
.attrs = &invimu_anglvel_attribute_group,
.read_raw = invimu_read_raw,
.read_avail = invimu_read_avail,
.write_raw = invimu_write_raw,
.write_raw_get_fmt = invimu_write_raw_get_fmt,
};
static void invimu_axis_transposition(struct imu_3axis_data *rawdata, int position)
{
struct imu_3axis_data tempdata;
memcpy(&tempdata, rawdata, sizeof(struct imu_3axis_data));
if (position == IMU_P_D_90) {
rawdata->raw[0] = tempdata.raw[1];
rawdata->raw[1] = 0 - tempdata.raw[0];
rawdata->raw[2] = tempdata.raw[2];
} else if (position == IMU_P_D_270) {
rawdata->raw[0] = tempdata.raw[1];
rawdata->raw[1] = tempdata.raw[0];
rawdata->raw[2] = tempdata.raw[2];
} else if (position == IMU_P_D_180) {
rawdata->raw[0] = tempdata.raw[0];
rawdata->raw[1] = -tempdata.raw[1];
rawdata->raw[2] = tempdata.raw[2];
}
}
static void invimu_axis_print(struct imu_ctrb *ctrb, struct imu_sensor *sensor)
{
int print_on = 0;
if (ctrb == NULL || sensor == NULL)
return;
print_on = (sensor->readcnt != 0) && ((sensor->readcnt % 6000) == 0);
print_on = (print_on) || (ctrb->debugon);
if (print_on) {
if (sensor->id == IMU_SENSOR_ID_ACCE) {
dev_info(ctrb->dev, "acce read cnt=%d, raw=%d,%d,%d\n", sensor->readcnt,
sensor->rawdata.raw[0],
sensor->rawdata.raw[1],
sensor->rawdata.raw[2]);
dev_info(ctrb->dev, "acce calib offset=%d,%d,%d\n",
sensor->offset_x, sensor->offset_y, sensor->offset_z);
}
if (sensor->id == IMU_SENSOR_ID_GYRO) {
dev_info(ctrb->dev, "gyro read cnt=%d, raw=%d,%d,%d\n", sensor->readcnt,
sensor->rawdata.raw[0],
sensor->rawdata.raw[1],
sensor->rawdata.raw[2]);
dev_info(ctrb->dev, "gyro calib offset=%d,%d,%d\n",
sensor->offset_x, sensor->offset_y, sensor->offset_z);
}
}
}
static int invimu_data_report(struct imu_ctrb *ctrb)
{
int i, ret = 0;
struct iio_dev *indio_dev;
struct imu_sensor *sensor;
struct imu_3axis_data rawdata;
if (ctrb == NULL)
return -1;
for (i = 0; i < IMU_SENSOR_ID_MAX; i++) {
indio_dev = ctrb->iio_devs[i];
sensor = iio_priv(indio_dev);
if (iio_buffer_enabled(indio_dev)) {
memset(&rawdata, 0, sizeof(struct imu_3axis_data));
sensor->readcnt++;
switch (sensor->id) {
case IMU_SENSOR_ID_ACCE:
ret = ctrb->chipinfo->read_acce_raw(ctrb, &rawdata);
break;
case IMU_SENSOR_ID_GYRO:
ret = ctrb->chipinfo->read_gyro_raw(ctrb, &rawdata);
break;
default:
continue;
}
invimu_axis_transposition(&rawdata, ctrb->position);
memcpy(&sensor->rawdata, &rawdata, sizeof(struct imu_3axis_data));
iio_push_to_buffers_with_timestamp(indio_dev,
&rawdata, ktime_get_boottime_ns());
invimu_axis_print(ctrb, sensor);
//invimu_calibrator_process(ctrb, sensor);
}
}
return ret;
}
static void invimu_work_handler(struct work_struct *work)
{
struct imu_ctrb *ctrb;
ctrb = (struct imu_ctrb *)container_of(work, struct imu_ctrb, pollingwork.work);
invimu_data_report(ctrb);
schedule_delayed_work(&ctrb->pollingwork, msecs_to_jiffies(IMU_POLLING_TIME_MS));
}
int invimu_chip_init(struct imu_ctrb *ctrb, bool use_spi)
{
if (ctrb == NULL)
return -1;
return ctrb->chipinfo->chip_init(ctrb);
}
EXPORT_SYMBOL_GPL(invimu_chip_init);
static int invimu_parse_dt_parameters(struct device *dev, struct imu_ctrb *ctrb)
{
int position = 0;
struct device_node *np = dev->of_node;
ctrb->position = 0;
if (np != NULL) {
if (of_property_read_s32(np, "position", &position) == 0) {
if (position < IMU_P_D_0 || position >= IMU_D_MAX)
goto HWCIMU_PARSE_DT_ERR;
else
ctrb->position = position;
} else {
goto HWCIMU_PARSE_DT_ERR;
}
} else {
goto HWCIMU_PARSE_DT_ERR;
}
dev_info(ctrb->dev, "imu position sets %d\n", ctrb->position);
return 0;
HWCIMU_PARSE_DT_ERR:
dev_err(ctrb->dev, "imu position sets default %d\n", ctrb->position);
return -ENODEV;
}
int invimu_core_probe(struct device *dev, struct regmap *regmap, int irq, bool use_spi)
{
int i, ret = 0;
struct imu_ctrb *ctrb;
struct imu_info *info = NULL;
ctrb = devm_kzalloc(dev, sizeof(*ctrb), GFP_KERNEL);
if (!ctrb)
return -ENOMEM;
dev_set_drvdata(dev, (void *)ctrb);
mutex_init(&ctrb->power_lock);
ctrb->dev = dev;
ctrb->regmap = regmap;
ctrb->irq = irq;
ctrb->debugon = 0;
dev_info(ctrb->dev, "probe start\n");
info = icm42607_chip_probe(ctrb);
if (info == NULL) {
dev_err(ctrb->dev, "no chip probed!\n");
return -ENODEV;
}
ctrb->chipinfo = info;
ret = invimu_chip_init(ctrb, use_spi);
if (ret) {
dev_err(ctrb->dev, "chip err\n");
return ret;
}
for (i = 0; i < IMU_SENSOR_ID_MAX; i++) {
ctrb->iio_devs[i] = invimu_alloc_iiodev(ctrb,
&invimu_acc_info, &invimu_gryo_info, i, ctrb->chipinfo->name);
if (!ctrb->iio_devs[i]) {
dev_err(ctrb->dev, "iio alloc err\n");
return -ENOMEM;
}
ret = devm_iio_device_register(ctrb->dev, ctrb->iio_devs[i]);
if (ret) {
dev_err(ctrb->dev, "iio register err\n");
return ret;
}
}
invimu_parse_dt_parameters(dev, ctrb);
INIT_DELAYED_WORK(&ctrb->pollingwork, invimu_work_handler);
dev_info(ctrb->dev, "probe end\n");
return 0;
}
EXPORT_SYMBOL_GPL(invimu_core_probe);
MODULE_DESCRIPTION("inv imu driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2025 Rockchip Electronics Co., Ltd.
*/
#ifndef __INVIMU_CORE_H
#define __INVIMU_CORE_H
int invimu_chip_init(struct imu_ctrb *ctrb, bool use_spi);
int invimu_core_probe(struct device *dev, struct regmap *regmap, int irq, bool use_spi);
#endif

View File

@@ -0,0 +1,109 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2025 Rockchip Electronics Co., Ltd.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/sysfs.h>
#include <linux/pm_runtime.h>
#include "imu.h"
#include "invimu_core.h"
static bool invimu_writeable_reg(struct device *dev, unsigned int reg);
static bool invimu_volatile_reg(struct device *dev, unsigned int reg);
const struct regmap_config invimu_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0xff,
.cache_type = REGCACHE_RBTREE,
.writeable_reg = invimu_writeable_reg,
.volatile_reg = invimu_volatile_reg,
};
static bool invimu_writeable_reg(struct device *dev, unsigned int reg)
{
return true;
}
static bool invimu_volatile_reg(struct device *dev, unsigned int reg)
{
return true;
}
static int invimu_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct regmap *regmap;
regmap = devm_regmap_init_i2c(client, &invimu_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Failed to register i2c regmap: %p\n", regmap);
return PTR_ERR(regmap);
}
return invimu_core_probe(&client->dev, regmap, client->irq, false);
}
static int invimu_suspend(struct device *dev)
{
dev_info(dev, "inv_imu suspend\n");
return 0;
}
static int invimu_resume(struct device *dev)
{
int ret;
struct imu_ctrb *ctrb = dev_get_drvdata(dev);
ret = invimu_chip_init(ctrb, false);
dev_info(dev, "inv_imu resume:%d\n", ret);
return ret;
}
static const struct dev_pm_ops invimu_pm_ops = {
.suspend = invimu_suspend,
.resume = invimu_resume,
};
#ifdef CONFIG_OF
static const struct of_device_id invimu_of_match[] = {
{ .compatible = "inv,icm42607"},
{ },
};
MODULE_DEVICE_TABLE(of, invimu_of_match);
#endif
static struct i2c_driver invimu_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "invimu_i2c",
.pm = &invimu_pm_ops,
.of_match_table = of_match_ptr(invimu_of_match),
},
.probe = invimu_i2c_probe,
};
static int32_t __init invimu_driver_init(void)
{
return i2c_add_driver(&invimu_i2c_driver);
}
static void __exit invimu_driver_exit(void)
{
i2c_del_driver(&invimu_i2c_driver);
}
late_initcall(invimu_driver_init);
module_exit(invimu_driver_exit);
MODULE_DESCRIPTION("INV ICM42607 I2C driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,295 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2025 Rockchip Electronics Co., Ltd.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/pm_runtime.h>
#include "icm42607.h"
#include "imu.h"
#include "invimu_iio.h"
#define INVIMU_CHANNEL(_type, _address, _channel2, _scan_index) \
{ \
.type = _type, \
.address = _address, \
.modified = 1, \
.channel2 = _channel2, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = _scan_index, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
}
static const int icm42607_avail_acc_sample_freqs[] = {100};
static const int icm42607_avail_gyro_sample_freqs[] = {100};
int invimu_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{
struct imu_sensor *sensor = iio_priv(indio_dev);
struct imu_ctrb *ctrb = sensor->ctrb;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ctrb->chipinfo->read_asix_one(ctrb, chan->address, val);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (sensor->id == IMU_SENSOR_ID_ACCE) {
*val = 980665ULL;
*val2 = 100000ULL * 2048;/* scale = 9.8 / 2048 */
} else if (sensor->id == IMU_SENSOR_ID_GYRO) {
*val = 314159ULL;
*val2 = 1800000ULL * 143;/* scale = pi / (180 * 14.3) */
} else {
return -EINVAL;
}
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_OFFSET:
if (chan->channel2 == IIO_MOD_X)
*val = sensor->offset_x;
else if (chan->channel2 == IIO_MOD_Y)
*val = sensor->offset_y;
else if (chan->channel2 == IIO_MOD_Z)
*val = sensor->offset_z;
else
return -EINVAL;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = sensor->odr;
return IIO_VAL_INT;
default:
return -EINVAL;
}
return IIO_VAL_INT;
}
EXPORT_SYMBOL_GPL(invimu_read_raw);
int invimu_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, const int **vals, int *type, int *length, long mask)
{
struct imu_sensor *sensor = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
*type = IIO_VAL_INT;
switch (sensor->id) {
case IMU_SENSOR_ID_ACCE:
*vals = icm42607_avail_acc_sample_freqs;
*length = ARRAY_SIZE(icm42607_avail_acc_sample_freqs);
return IIO_AVAIL_LIST;
case IMU_SENSOR_ID_GYRO:
*vals = icm42607_avail_gyro_sample_freqs;
*length = ARRAY_SIZE(icm42607_avail_gyro_sample_freqs);
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
EXPORT_SYMBOL_GPL(invimu_read_avail);
int invimu_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
struct imu_sensor *sensor = iio_priv(indio_dev);
struct imu_ctrb *ctrb = sensor->ctrb;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
break;
case IIO_CHAN_INFO_SAMP_FREQ:
sensor->odr = val;
break;
case IIO_CHAN_INFO_OFFSET:
switch (chan->channel2) {
case IIO_MOD_X:
sensor->offset_x = val;
break;
case IIO_MOD_Y:
sensor->offset_y = val;
break;
case IIO_MOD_Z:
sensor->offset_z = val;
break;
default:
return -EINVAL;
}
if (sensor->id == IMU_SENSOR_ID_ACCE)
return ctrb->chipinfo->set_accel_offset(ctrb, val, chan->channel2);
else if (sensor->id == IMU_SENSOR_ID_GYRO)
return ctrb->chipinfo->set_gyro_offset(ctrb, val, chan->channel2);
else
return -EINVAL;
default:
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_GPL(invimu_write_raw);
int invimu_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
EXPORT_SYMBOL_GPL(invimu_write_raw_get_fmt);
static const struct iio_chan_spec invimu_acc_channels[] = {
INVIMU_CHANNEL(IIO_ACCEL, 0, IIO_MOD_X, 0),
INVIMU_CHANNEL(IIO_ACCEL, 1, IIO_MOD_Y, 1),
INVIMU_CHANNEL(IIO_ACCEL, 2, IIO_MOD_Z, 2),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const struct iio_chan_spec invimu_gyro_channels[] = {
INVIMU_CHANNEL(IIO_ANGL_VEL, 3, IIO_MOD_X, 0),
INVIMU_CHANNEL(IIO_ANGL_VEL, 4, IIO_MOD_Y, 1),
INVIMU_CHANNEL(IIO_ANGL_VEL, 5, IIO_MOD_Z, 2),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static int invimu_buffer_preenable(struct iio_dev *indio_dev)
{
int mode, ret = 0;
struct imu_sensor *sensor = iio_priv(indio_dev);
struct imu_ctrb *ctrb = sensor->ctrb;
mutex_lock(&ctrb->power_lock);
switch (sensor->id) {
case IMU_SENSOR_ID_ACCE:
mode = iio_buffer_enabled(ctrb->iio_devs[IMU_SENSOR_ID_GYRO]) ?
IMU_POWER_ACCE_GYRO : IMU_POWER_ACCE_ONLY;
break;
case IMU_SENSOR_ID_GYRO:
mode = IMU_POWER_ACCE_GYRO;
break;
default:
mode = IMU_POWER_MODE_DOWN;
break;
}
ret = ctrb->chipinfo->mode_set(ctrb, mode);
if (ret == 0 && (mode == IMU_POWER_ACCE_ONLY || mode == IMU_POWER_ACCE_GYRO))
schedule_delayed_work(&ctrb->pollingwork, msecs_to_jiffies(IMU_POLLING_TIME_MS));
mutex_unlock(&ctrb->power_lock);
return ret;
}
static int invimu_buffer_postdisable(struct iio_dev *indio_dev)
{
int ret = 0, mode = 0;
struct imu_sensor *sensor = iio_priv(indio_dev);
struct imu_ctrb *ctrb = sensor->ctrb;
mutex_lock(&ctrb->power_lock);
switch (sensor->id) {
case IMU_SENSOR_ID_ACCE:
mode = iio_buffer_enabled(ctrb->iio_devs[IMU_SENSOR_ID_GYRO]) ?
IMU_POWER_ACCE_GYRO : IMU_POWER_MODE_DOWN;
break;
case IMU_SENSOR_ID_GYRO:
mode = iio_buffer_enabled(ctrb->iio_devs[IMU_SENSOR_ID_ACCE]) ?
IMU_POWER_ACCE_ONLY : IMU_POWER_MODE_DOWN;
break;
default:
mode = IMU_POWER_MODE_DOWN;
break;
}
ret = ctrb->chipinfo->mode_set(ctrb, mode);
if (ret == 0 && mode == IMU_POWER_MODE_DOWN)
cancel_delayed_work(&ctrb->pollingwork);
mutex_unlock(&ctrb->power_lock);
return ret;
}
static const struct iio_buffer_setup_ops invimu_buffer_ops = {
.preenable = invimu_buffer_preenable,
.postdisable = invimu_buffer_postdisable,
};
struct iio_dev *invimu_alloc_iiodev(struct imu_ctrb *ctrb,
const struct iio_info *acce_iio_info, const struct iio_info *gyro_iio_info,
enum imu_sensor_id id, char *name)
{
struct imu_sensor *sensor;
struct iio_dev *indio_dev;
indio_dev = devm_iio_device_alloc(ctrb->dev, sizeof(*sensor));
if (!indio_dev)
return NULL;
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
devm_iio_kfifo_buffer_setup(ctrb->dev, indio_dev, &invimu_buffer_ops);
sensor = iio_priv(indio_dev);
sensor->id = id;
sensor->ctrb = ctrb;
switch (id) {
case IMU_SENSOR_ID_ACCE:
sensor->odr = icm42607_avail_acc_sample_freqs[0];
indio_dev->info = acce_iio_info;
indio_dev->channels = invimu_acc_channels;
indio_dev->num_channels = ARRAY_SIZE(invimu_acc_channels);
scnprintf(sensor->name, sizeof(sensor->name), "%s_accel", name);
break;
case IMU_SENSOR_ID_GYRO:
sensor->odr = icm42607_avail_gyro_sample_freqs[0];
indio_dev->info = gyro_iio_info;
indio_dev->channels = invimu_gyro_channels;
indio_dev->num_channels = ARRAY_SIZE(invimu_gyro_channels);
scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro", name);
break;
default:
return NULL;
}
indio_dev->name = sensor->name;
return indio_dev;
}
EXPORT_SYMBOL_GPL(invimu_alloc_iiodev);

View File

@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2025 Rockchip Electronics Co., Ltd.
*/
#ifndef __INVIMU_IIO_H
#define __INVIMU_IIO_H
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
int invimu_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2, long mask);
int invimu_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, const int **vals, int *type, int *length, long mask);
int invimu_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask);
int invimu_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, long mask);
struct iio_dev *invimu_alloc_iiodev(struct imu_ctrb *ctrb,
const struct iio_info *acce_iio_info, const struct iio_info *gyro_iio_info,
enum imu_sensor_id id, char *name);
#endif

View File

@@ -32,6 +32,8 @@ config CROS_EC_MKBP_PROXIMITY
To compile this driver as a module, choose M here: the
module will be called cros_ec_mkbp_proximity.
source "drivers/iio/proximity/nds03/Kconfig"
config ISL29501
tristate "Intersil ISL29501 Time Of Flight sensor"
depends on I2C

View File

@@ -6,6 +6,7 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AS3935) += as3935.o
obj-$(CONFIG_CROS_EC_MKBP_PROXIMITY) += cros_ec_mkbp_proximity.o
obj-$(CONFIG_DTOF_NDS03) += nds03/
obj-$(CONFIG_ISL29501) += isl29501.o
obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
obj-$(CONFIG_MB1232) += mb1232.o

View File

@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
config DTOF_NDS03
tristate "NDS03 I2C DEVICE"
depends on I2C && SYSFS
help
Using I2C

View File

@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Makefile for the nds03 drivers.
#
obj-$(CONFIG_DTOF_NDS03) += nds03.o
nds03-y += nds03_module.o nds03_platform.o nds03_module_i2c.o nds03_iio.o \
nds03_dev.o nds03_data.o \
nds03_comm.o nds03_calib.o

View File

@@ -0,0 +1,104 @@
/*!
@file nds03.h
@brief
@author lull
@date 2025-06
@copyright Copyright (c) 2025 Shenzhen Nephotonics Semiconductor Technology Co., Ltd.
@license BSD 3-Clause License
This file is part of the Nephotonics sensor SDK.
It is licensed under the BSD 3-Clause License.
A copy of the license can be found in the project root directory, in the file named LICENSE.
*/
#ifndef __NDS03_H
#define __NDS03_H
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/version.h>
#include <linux/platform_device.h>
#include <linux/atomic.h>
#include <linux/interrupt.h>
/* nds03 sdk */
#include "nds03_comm.h"
#include "nds03_dev.h"
#include "nds03_data.h"
#include "nds03_calib.h"
#include "nds03_def.h"
#define TOF_NDS03_DRV_NAME "tof_nds03"
#define DRIVER_VERSION "1.0.4"
#define TOF_NDS03_MAJOR 255
#define MAX_POS_BITS 32
/*! use debug */
extern int nds03_enable_debug;
#define nds03_dbgmsg(str, ...) \
do { \
if (nds03_enable_debug > 0) \
printk("%s: " str, __FUNCTION__, ##__VA_ARGS__); \
} while (0)
#define nds03_info(str, ...) \
pr_info("%s: " str , __FUNCTION__, ##__VA_ARGS__)
#define nds03_errmsg(str, ...) \
pr_err("%s: " str, __FUNCTION__, ##__VA_ARGS__)
#define nds03_warnmsg(str, ...) \
pr_warn("%s: " str,__FUNCTION__, ##__VA_ARGS__)
struct nds03_context {
/*!< multiple device id 0 based*/
int id;
/*!< misc device name */
char name[64];
struct i2c_client * client;
/*!< nds03 device info */
NDS03_Dev_t g_nds03_device;
/*!< main dev mutex/lock */
struct mutex work_mutex;
// /*!< work for pseudo irq polling check */
struct delayed_work dwork;
struct work_struct irq_work;
// /*!< input device used for sending event */
struct input_dev *idev;
/*!< intr gpio number */
int irq;
bool remove_flag;
/// /* user control configuration parameter */
/*!< measure mode irq or poll*/
atomic_t meas_mode;
/*!< rescheduled time use in poll mode */
atomic_t poll_delay_ms;
/*!< use ctrl measure state */
atomic_t is_meas;
/*!< calibtion result of the deivce */
int calib_result;
/*!< open input file descriptor count*/
int fd_open_count;
struct iio_dev *indio_dev;
struct nds03_iio_dev *iio;
};
int nds03_common_probe(struct nds03_context * ctx);
int nds03_common_remove(struct nds03_context * ctx);
int nds03_interrupt_config(NDS03_Dev_t *pNxDevice, uint8_t is_open);
int nds03_sensor_init(struct nds03_context *ctx);
#endif

View File

@@ -0,0 +1,275 @@
/**
* @file nds03_calib.c
* @author tongsheng.tang
* @brief NDS03 Calibration functions
* @version 2.x.x
* @date 2025-06
*
* @copyright Copyright (c) 2025, Nephotonics Information Technology (Hefei) Co., Ltd.
*
* @license BSD 3-Clause License
* This file is part of the NDS03 SDK and is licensed under the BSD 3-Clause License.
* You may obtain a copy of the license in the project root directory,
* in the file named LICENSE.
*/
#include "nds03_comm.h"
#include "nds03_dev.h"
#include "nds03_data.h"
#include "nds03_calib.h"
/**
* @brief NDS03 Get Offset Calib Depth MM
* offset标定距离
* @param pNxDevice
* @param calib_depth_mm
* @return NDS03_Error
*/
NDS03_Error NDS03_GetOffsetCalibDepthMM(NDS03_Dev_t *pNxDevice, uint16_t *calib_depth_mm)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, NDS03_REG_OFFSET_MM, calib_depth_mm));
return ret;
}
/**
* @brief NDS03 Set Offset Calib Depth MM
* offset标定距离
* @param pNxDevice
* @param calib_depth_mm
* @return NDS03_Error
*/
NDS03_Error NDS03_SetOffsetCalibDepthMM(NDS03_Dev_t *pNxDevice, uint16_t calib_depth_mm)
{
NDS03_Error ret = NDS03_ERROR_NONE;
if (calib_depth_mm == 0)
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, NDS03_REG_OFFSET_MM, &calib_depth_mm));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteHalfWord(pNxDevice, NDS03_REG_OFFSET_MM, calib_depth_mm));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_DISABLE));
return ret;
}
/**
* @brief NDS03 Offset Calibration Check
* Offset
* @param pNxDevice
* @param calib_depth_mm
* @return NDS03_Error
*/
static NDS03_Error NDS03_OffsetCalibrationCheck(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint16_t ref_histo_max, depth[2];
uint8_t depth_flag;
CHECK_RET(NDS03_GetSingleRangingData(pNxDevice));
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, NDS03_REG_REF_HISTO_MAX, &ref_histo_max));
if (ret == NDS03_ERROR_NONE && ref_histo_max < NDS03_OFFSET_REF_MAX_COUNT_TH) {
ret = NDS03_ERROR_VCSEL_ERROR;
NX_PRINTF("ref_histo_max:%d\r\n", ref_histo_max);
return ret;
}
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_DEPTH_FLAG, &depth_flag));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DEPTH_FLAG, 1));
CHECK_RET(NDS03_GetSingleRangingData(pNxDevice));
depth[0] = pNxDevice->ranging_data[0].depth;
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DEPTH_FLAG, 2));
CHECK_RET(NDS03_GetSingleRangingData(pNxDevice));
depth[1] = pNxDevice->ranging_data[0].depth;
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DEPTH_FLAG, depth_flag));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_DISABLE));
if (ret == NDS03_ERROR_NONE && ((depth[0] + NDS03_OFFSET_DEPTH_ERROR_TH < depth[1]) ||
(depth[0] > depth[1] + NDS03_OFFSET_DEPTH_ERROR_TH) ||
depth[0] == NDS03_DEPTH_INVALID_VALUE || depth[1] == NDS03_DEPTH_INVALID_VALUE)) {
ret = NDS03_ERROR_OFFSET_ERROR;
NX_PRINTF("depth[0]:%d depth[1]:%d\r\n", depth[0], depth[1]);
}
return ret;
}
/**
* @brief ToF Offset
* @details 使500mm
*
* @param pNxDevice
* @return int8_t
* @retval 0:
* @retval !0: Offset标定失败
*/
NDS03_Error NDS03_OffsetCalibration(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint16_t ambient_bg;
NX_PRINTF("%s Start!\r\n", __func__);
CHECK_RET(NDS03_GetSingleRangingData(pNxDevice));
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, NDS03_REG_AMBIENT, &ambient_bg));
if (ambient_bg > NDS03_AMBIENT_TH) {
ret = NDS03_ERROR_AMBIENT_HIGH;
NX_PRINTF("ambient_bg:%d\r\n", ambient_bg);
return ret;
}
// 打开使能
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_REQ, NDS03_CMD_OFFSET_CALIB));
CHECK_RET(NDS03_WaitforCmdVal(pNxDevice, NDS03_CMD_OFFSET_CALIB, 10000));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_VAL, NDS03_CMD_ENA_DISABLE));
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_CALIB_STATE, (uint8_t*)&ret));
CHECK_RET(NDS03_OffsetCalibrationCheck(pNxDevice));
NX_PRINTF("%s End!\r\n", __func__);
return ret;
}
/**
* @brief ToF Offset
* @details
*
* @param pNxDevice
* @param calib_depth_mm
* @return int8_t
* @retval 0:
* @retval !0: Offset标定失败
*/
NDS03_Error NDS03_OffsetCalibrationAtDepth(NDS03_Dev_t *pNxDevice, uint16_t calib_depth_mm)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint16_t ambient_bg;
NX_PRINTF("%s Start!\r\n", __func__);
CHECK_RET(NDS03_GetSingleRangingData(pNxDevice));
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, NDS03_REG_AMBIENT, &ambient_bg));
if (ambient_bg > NDS03_AMBIENT_TH){
ret = NDS03_ERROR_AMBIENT_HIGH;
NX_PRINTF("ambient:%d \r\n", ambient_bg);
return ret;
}
CHECK_RET(NDS03_SetOffsetCalibDepthMM(pNxDevice, calib_depth_mm));
// 打开使能
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_REQ, NDS03_CMD_OFFSET_CALIB));
CHECK_RET(NDS03_WaitforCmdVal(pNxDevice, NDS03_CMD_OFFSET_CALIB, 10000));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_VAL, NDS03_CMD_ENA_DISABLE));
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_CALIB_STATE, (uint8_t*)&ret));
CHECK_RET(NDS03_OffsetCalibrationCheck(pNxDevice));
NX_PRINTF("%s End!\r\n", __func__);
return ret;
}
/**
* @brief NDS03 Read Xtalk Data
* NDS03串扰数据
* @param pNxDevice
* @param rbuf
* @param size
* @return NDS03_Error
*/
static NDS03_Error NDS03_ReadXtalkData(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint32_t rsize, _size;
uint8_t one_size;
uint16_t addr;
uint16_t rbuf[240];
uint8_t *rbuf_ptr = (uint8_t *)rbuf;
uint32_t size = 240 * 2;
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_CACHE_SIZE, &one_size));
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, 0xEC, &addr));
_size = (uint32_t)one_size;
rsize = 0;
while ((rsize < size) && (ret == NDS03_ERROR_NONE)) {
CHECK_RET(NDS03_WriteHalfWord(pNxDevice, NDS03_REG_CACHE_ADDR, addr));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_ENA, 0x05));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_REQ, 0xC0));
CHECK_RET(NDS03_WaitforDataVal(pNxDevice, 0xC0, 200));
CHECK_RET(NDS03_ReadNBytes(pNxDevice, NDS03_REG_CACHE_DATA, rbuf_ptr,
((size-rsize)>_size) ? _size:(size-rsize)));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_VAL, NDS03_DATA_VAL_IDLE));
rbuf_ptr += _size;
addr += _size;
rsize += _size;
}
for (int i = 80; i < 240; i++) {
if (rbuf[i] > 10000)
return -14;
}
return ret;
}
/**
* @brief NDS03 Xtalk Calibration
* NDS03串扰/
* @param pNxDevice
* @return int8_t
* @retval 0:
* @retval !0: xtalk标定失败
*/
NDS03_Error NDS03_XtalkCalibration(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE, calib_state = NDS03_ERROR_NONE;
uint16_t ambient_bg,ref_histo_max;
uint8_t cnt = 2;
int8_t check_xtalk_state = 0;
NX_PRINTF("%s Start!\r\n", __func__);
do {
CHECK_RET(NDS03_GetSingleRangingData(pNxDevice));
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, NDS03_REG_AMBIENT, &ambient_bg));
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, NDS03_REG_REF_HISTO_MAX, &ref_histo_max));
if (ambient_bg > NDS03_AMBIENT_TH) {
ret = NDS03_ERROR_AMBIENT_HIGH;
NX_PRINTF("ambient:%d\r\n", ambient_bg);
return ret;
}
if (ret == NDS03_ERROR_NONE && ref_histo_max < NDS03_OFFSET_REF_MAX_COUNT_TH) {
ret = NDS03_ERROR_VCSEL_ERROR;
NX_PRINTF("ref_histo_max:%d\r\n", ref_histo_max);
return ret;
}
// 打开使能
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_REQ, NDS03_CMD_XTALK_CALIB));
CHECK_RET(NDS03_WaitforCmdVal(pNxDevice, NDS03_CMD_XTALK_CALIB, 5000));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_VAL, NDS03_CMD_ENA_DISABLE));
CHECK_RET(NDS03_GetFirmwareVersion(pNxDevice));
if (pNxDevice->chip_info.fw_version == 0x10203) {
CHECK_RET(NDS03_GetSingleRangingData(pNxDevice));
check_xtalk_state = NDS03_ReadXtalkData(pNxDevice);
}
} while (cnt-- && check_xtalk_state != 0);
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_CALIB_STATE, (uint8_t*)&calib_state));
ret = check_xtalk_state ? check_xtalk_state : (ret | calib_state) &
(NDS03_CALIB_ERROR_XTALK_OVERFLOW | NDS03_CALIB_ERROR_XTALK_EXCESSIVE);
NX_PRINTF("%s End!\r\n", __func__);
return ret;
}
/**
* @brief NDS03 Get Xtalk Value
*
* @param pNxDevice
* @return NDS03_Error
*/
NDS03_Error NDS03_GetXTalkValue(NDS03_Dev_t *pNxDevice, uint16_t* xtalk_value)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, NDS03_REG_XTALK, xtalk_value));
return ret;
}

View File

@@ -0,0 +1,42 @@
/**
* @file nds03_calib.h
* @author tongsheng.tang
* @brief NDS03 Calibration functions
* @version 2.x.x
* @date 2025-06
*
* @copyright Copyright (c) 2025, Nephotonics Information Technology (Hefei) Co., Ltd.
*
* @license BSD 3-Clause License
* This file is part of the NDS03 SDK and is licensed under the BSD 3-Clause License.
* You may obtain a copy of the license in the project root directory, in the file named LICENSE.
*
*/
#ifndef __NDS03_CALIB_H__
#define __NDS03_CALIB_H__
#include "nds03_def.h"
/** @defgroup NDS03_Calibration_Group NDS03 Calibration Functions
* @brief NDS03 Calibration Functions
* @{
*/
/** 获取offset标定距离 */
NDS03_Error NDS03_GetOffsetCalibDepthMM(NDS03_Dev_t *pNxDevice, uint16_t *calib_depth_mm);
/** 设置offset标定距离 */
NDS03_Error NDS03_SetOffsetCalibDepthMM(NDS03_Dev_t *pNxDevice, uint16_t calib_depth_mm);
/** Offset标定函数(无标定距离设置) */
NDS03_Error NDS03_OffsetCalibration(NDS03_Dev_t *pNxDevice);
/** Offset标定函数(有标定距离设置) */
NDS03_Error NDS03_OffsetCalibrationAtDepth(NDS03_Dev_t *pNxDevice, uint16_t calib_depth_mm);
/** XTalk标定 */
NDS03_Error NDS03_XtalkCalibration(NDS03_Dev_t *pNxDevice);
/** 获取标定串扰值 */
NDS03_Error NDS03_GetXTalkValue(NDS03_Dev_t *pNxDevice, uint16_t* xtalk_value);
/** @} NDS03_Calibration_Group */
#endif

View File

@@ -0,0 +1,385 @@
/**
* @file nds03_comm.c
* @author tongsheng.tang
* @brief NDS03 communication functions
* @version 2.x.x
* @date 2025-06
*
* @copyright Copyright (c) 2025, Nephotonics Information Technology (Hefei) Co., Ltd.
*
* @license BSD 3-Clause License
* This file is part of the NDS03 SDK and is licensed under the BSD 3-Clause License.
* You may obtain a copy of the license in the project root directory,
* in the file named LICENSE.
*/
#include "nds03_stdint.h"
#if NDS03_PLATFORM != PLATFORM_LINUX_DRIVER
#include <stdint.h>
#include <stdlib.h>
#else
#include <linux/types.h>
#endif
#include "nds03_dev.h"
#include "nds03_comm.h"
#include "nds03_def.h"
#include "nds03_platform.h"
/**
* @brief NDS03 Delay 1ms
* @param ms
* @return void
*/
NDS03_Error NDS03_Delay1ms(NDS03_Dev_t *pNxDevice, uint32_t ms)
{
return nds03_delay_1ms(&pNxDevice->platform, ms);
}
/**
* @brief NDS03 Delay 10us
* @param us
* @return void
*/
NDS03_Error NDS03_Delay10us(NDS03_Dev_t *pNxDevice, uint32_t us)
{
return nds03_delay_10us(&pNxDevice->platform, us);
}
/**
* @brief NDS03 Get System Clk Ms
* @param pNxDevice
* @param time_ms ms
* @return void
*/
NDS03_Error NDS03_GetSystemClkMs(NDS03_Dev_t *pNxDevice,int32_t *time_ms)
{
NDS03_Error ret = NDS03_ERROR_NONE;
ret = nds03_get_system_clk_ms(&pNxDevice->platform, time_ms);
return ret;
}
/**
* @brief NDS03 Set XShut Pin Level
* xshut引脚的电平
* @param pNxDevice
* @param level xshut引脚电平01
* @return void
*/
NDS03_Error NDS03_SetXShutPinLevel(NDS03_Dev_t *pNxDevice, int8_t level)
{
return nds03_set_xshut_pin_level(&pNxDevice->platform, level);
}
/**
* @brief i2c时钟频率
*
* @param pNxDevice
* @param freq
* @return
*/
NDS03_Error NDS03_GetI2cFreq(NDS03_Dev_t *pNxDevice, uint32_t *freq)
{
return nds03_i2c_get_clock_frequency(&pNxDevice->platform, freq);
}
/**
* @brief i2c时钟频率
*
* @param pNxDevice
* @param freq
* @return
*/
NDS03_Error NDS03_SetI2cFreq(NDS03_Dev_t *pNxDevice, uint32_t freq)
{
return nds03_i2c_set_clock_frequency(&pNxDevice->platform, freq);
}
/**
* @brief 2
*
* @param buf
* @param buf_num
*/
static void NDS03_HalfWordDataFmtChange(uint16_t *buf, uint16_t buf_num)
{
uint16_t tmp;
uint8_t *pu8buf = (uint8_t*)buf;
uint16_t i;
for (i = 0; i < buf_num; i++) {
tmp = buf[i];
*pu8buf++ = (tmp >> 0);
*pu8buf++ = (tmp >> 8);
}
}
/**
* @brief 14
*
* @param buf
* @param buf_num
*/
static void NDS03_WordDataFmtChange(uint32_t *buf, uint16_t buf_num)
{
uint32_t tmp;
uint8_t *pu8buf = (uint8_t*)buf;
uint16_t i;
for (i = 0; i < buf_num; i++) {
tmp = buf[i];
*pu8buf++ = (tmp >> 0);
*pu8buf++ = (tmp >> 8);
*pu8buf++ = (tmp >> 16);
*pu8buf++ = (tmp >> 24);
}
}
/**
* @brief Write n Words to NDS03
* NDS03的寄存器写N个字
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param wdata:
* @param len:
* @return NDS03_Error
*/
NDS03_Error NDS03_WriteNBytes(NDS03_Dev_t *pNxDevice, uint8_t addr, void *wdata, uint16_t size)
{
NDS03_Error ret = NDS03_ERROR_NONE;
ret = nds03_i2c_write_nbytes(&pNxDevice->platform, addr, (uint8_t*)wdata, size);
return ret;
}
/**
* @brief Read n Words from NDS03
* NDS03的寄存器读N个字
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param rdata:
* @param len:
* @return NDS03_Error
*/
NDS03_Error NDS03_ReadNBytes(NDS03_Dev_t *pNxDevice, uint8_t addr, void *rdata, uint16_t size)
{
NDS03_Error ret = NDS03_ERROR_NONE;
ret = nds03_i2c_read_nbytes(&pNxDevice->platform, addr, (uint8_t*)rdata, size);
return ret;
}
/**
* @brief Write 1 Byte to NDS03
* NDS03的寄存器写1个字节
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param wdata:
* @return NDS03_Error
*/
NDS03_Error NDS03_WriteByte(NDS03_Dev_t *pNxDevice, uint8_t addr, uint8_t wdata)
{
NDS03_Error ret = NDS03_ERROR_NONE;
ret = nds03_i2c_write_nbytes(&pNxDevice->platform, addr, &wdata, 1);
return ret;
}
/**
* @brief Read 1 Byte from NDS03
* NDS03的寄存器读1个字节
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param rdata:
* @return NDS03_Error
*/
NDS03_Error NDS03_ReadByte(NDS03_Dev_t *pNxDevice, uint8_t addr, uint8_t *rdata)
{
NDS03_Error ret = NDS03_ERROR_NONE;
ret = nds03_i2c_read_nbytes(&pNxDevice->platform, addr, rdata, 1);
return ret;
}
/**
* @brief Write N bytes By Half-Word to NDS03
* NDS03的寄存器写N个字节,使
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param wdata:
* @param size:
* size必须是2的倍数
* @return NDS03_Error
*/
NDS03_Error NDS03_WriteNBytesByHalfWord(NDS03_Dev_t *pNxDevice,
uint8_t addr, uint16_t *wdata, uint16_t size)
{
NDS03_Error ret = NDS03_ERROR_NONE;
if ((size & 0x01) == 0) {
NDS03_HalfWordDataFmtChange(wdata, size / 2);
ret = nds03_i2c_write_nbytes(&pNxDevice->platform, addr, (uint8_t*)wdata, size);
NDS03_HalfWordDataFmtChange(wdata, size / 2);
}
return ret;
}
/**
* @brief Read N Bytes By Half-World from NDS03
* NDS03的寄存器读N个字节,使
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param rdata:
* @param size:
* size必须是2的倍数
* @return NDS03_Error
*/
NDS03_Error NDS03_ReadNBytesByHalfWord(NDS03_Dev_t *pNxDevice, uint8_t addr, uint16_t *rdata, uint16_t size)
{
NDS03_Error ret = NDS03_ERROR_NONE;
if ((size & 0x01) == 0 && rdata != NULL) {
ret = nds03_i2c_read_nbytes(&pNxDevice->platform, addr, (uint8_t*)rdata, size);
NDS03_HalfWordDataFmtChange(rdata, size / 2);
}
return ret;
}
/**
* @brief Write 2 Byte to NDS03
* NDS03的寄存器写2个字节
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param wdata:
* @return NDS03_Error
*/
NDS03_Error NDS03_WriteHalfWord(NDS03_Dev_t *pNxDevice, uint8_t addr, uint16_t wdata)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint8_t tmp[2];
tmp[0] = (wdata >> 0) & 0xFF;
tmp[1] = (wdata >> 8) & 0xFF;
ret = nds03_i2c_write_nbytes(&pNxDevice->platform, addr, tmp, sizeof(tmp));
return ret;
}
/**
* @brief Read 2 Byte from NDS03
* NDS03的寄存器读2个字节
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param rdata:
* @return NDS03_Error
*/
NDS03_Error NDS03_ReadHalfWord(NDS03_Dev_t *pNxDevice, uint8_t addr, uint16_t *rdata)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint8_t tmp[2];
if (rdata != NULL) {
ret = nds03_i2c_read_nbytes(&pNxDevice->platform, addr, tmp, sizeof(tmp));
*rdata = (uint16_t)tmp[0] | ((uint16_t)tmp[1] << 8);
}
return ret;
}
/**
* @brief Write N Bytes By Word to NDS03
* NDS03的寄存器写N个字节,使
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param wdata:
* @param size:
* size必须是4的倍数
* @return NDS03_Error
*/
NDS03_Error NDS03_WriteNBytesByWord(NDS03_Dev_t *pNxDevice, uint8_t addr,
uint32_t *wdata, uint16_t size)
{
NDS03_Error ret = NDS03_ERROR_NONE;
if ((size & 0x03) == 0) {
NDS03_WordDataFmtChange(wdata, size / 4);
ret = nds03_i2c_write_nbytes(&pNxDevice->platform, addr, (uint8_t*)wdata, size);
NDS03_WordDataFmtChange(wdata, size / 4);
}
return ret;
}
/**
* @brief Read N Bytes By Word from NDS03
* NDS03的寄存器读N个字节,使
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param rdata:
* @param size:
* size必须是4的倍数
* @return NDS03_Error
*/
NDS03_Error NDS03_ReadNBytesByWord(NDS03_Dev_t *pNxDevice, uint8_t addr, uint32_t *rdata, uint16_t size)
{
NDS03_Error ret = NDS03_ERROR_NONE;
if ((size & 0x03) == 0 && rdata != NULL) {
ret = nds03_i2c_read_nbytes(&pNxDevice->platform, addr, (uint8_t*)rdata, size);
NDS03_WordDataFmtChange(rdata, size / 4);
}
return ret;
}
/**
* @brief Write 4 Byte to NDS03
* NDS03的寄存器写2个字节
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param wdata:
* @return NDS03_Error
*/
NDS03_Error NDS03_WriteWord(NDS03_Dev_t *pNxDevice, uint8_t addr, uint32_t wdata)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint8_t tmp[4];
tmp[0] = (wdata >> 0) & 0xFF;
tmp[1] = (wdata >> 8) & 0xFF;
tmp[2] = (wdata >> 16) & 0xFF;
tmp[3] = (wdata >> 24) & 0xFF;
ret = nds03_i2c_write_nbytes(&pNxDevice->platform, addr, tmp, sizeof(tmp));
return ret;
}
/**
* @brief Read 4 Byte from NDS03
* NDS03的寄存器读2个字节
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param addr:
* @param rdata:
* @return NDS03_Error
*/
NDS03_Error NDS03_ReadWord(NDS03_Dev_t *pNxDevice, uint8_t addr, uint32_t *rdata)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint8_t tmp[4];
if (rdata != NULL) {
ret = nds03_i2c_read_nbytes(&pNxDevice->platform, addr, tmp, sizeof(tmp));
*rdata = (uint32_t)tmp[0] | ((uint32_t)tmp[1] << 8) |
((uint32_t)tmp[2] << 16) | ((uint32_t)tmp[3] << 24);
}
return ret;
}

View File

@@ -0,0 +1,71 @@
/**
* @file nds03_comm.h
* @author tongsheng.tang
* @brief NDS03 communication functions
* @version 2.x.x
* @date 2025-06
*
* @copyright Copyright (c) 2025, Nephotonics Information Technology (Hefei) Co., Ltd.
*
* @license BSD 3-Clause License
* This file is part of the NDS03 SDK and is licensed under the BSD 3-Clause License.
* You may obtain a copy of the license in the project root directory,
* in the file named LICENSE.
*/
#ifndef __NDS03_COMM_H__
#define __NDS03_COMM_H__
#include "nds03_def.h"
/** @defgroup NDS03_Communication_Group NDS03 Communication Functions
* @brief NDS03 Communication Functions
* @{
*/
/** 延时时间ms */
NDS03_Error NDS03_Delay1ms(NDS03_Dev_t *pNxDevice, uint32_t ms);
/** 延时时间10us */
NDS03_Error NDS03_Delay10us(NDS03_Dev_t *pNxDevice, uint32_t us);
/** 设置xshut引脚的电平 */
NDS03_Error NDS03_SetXShutPinLevel(NDS03_Dev_t *pNxDevice, int8_t level);
/** NDS03设置i2c频率 */
NDS03_Error NDS03_SetI2cFreq(NDS03_Dev_t *pNxDevice, uint32_t freq);
/** NDS03获取i2c频率 */
NDS03_Error NDS03_GetI2cFreq(NDS03_Dev_t *pNxDevice, uint32_t *freq);
/** 对NDS03寄存器写N个字节 */
NDS03_Error NDS03_WriteNBytes(NDS03_Dev_t *pNxDevice, uint8_t addr, void *wdata, uint16_t size);
/** 对NDS03寄存器读N个字节 */
NDS03_Error NDS03_ReadNBytes(NDS03_Dev_t *pNxDevice, uint8_t addr, void *rdata, uint16_t size);
/** 对NDS03寄存器写1个字节 */
NDS03_Error NDS03_WriteByte(NDS03_Dev_t *pNxDevice, uint8_t addr, uint8_t wdata);
/** 对NDS03寄存器写1个字节 */
NDS03_Error NDS03_ReadByte(NDS03_Dev_t *pNxDevice, uint8_t addr, uint8_t *rdata);
/** 对NDS03寄存器写N个字节使用半字方式 */
NDS03_Error NDS03_WriteNBytesByHalfWord(NDS03_Dev_t *pNxDevice,
uint8_t addr, uint16_t *wdata, uint16_t size);
/** 对NDS03寄存器读N个字节使用半字方式 */
NDS03_Error NDS03_ReadNBytesByHalfWord(NDS03_Dev_t *pNxDevice,
uint8_t addr, uint16_t *rdata, uint16_t size);
/** 对NDS03寄存器写1个字 */
NDS03_Error NDS03_WriteHalfWord(NDS03_Dev_t *pNxDevice, uint8_t addr, uint16_t wdata);
/** 对NDS03寄存器读1个字 */
NDS03_Error NDS03_ReadHalfWord(NDS03_Dev_t *pNxDevice, uint8_t addr, uint16_t *rdata);
/** 对NDS03寄存器写N个字节,使用字方式 */
NDS03_Error NDS03_WriteNBytesByWord(NDS03_Dev_t *pNxDevice,
uint8_t addr, uint32_t *wdata, uint16_t size);
/** 对NDS03寄存器读N个字节,使用字方式 */
NDS03_Error NDS03_ReadNBytesByWord(NDS03_Dev_t *pNxDevice,
uint8_t addr, uint32_t *rdata, uint16_t size);
/** 对NDS03寄存器写1个字 */
NDS03_Error NDS03_WriteWord(NDS03_Dev_t *pNxDevice, uint8_t addr, uint32_t wdata);
/** 对NDS03寄存器读1个字 */
NDS03_Error NDS03_ReadWord(NDS03_Dev_t *pNxDevice, uint8_t addr, uint32_t *rdata);
/** 获取系统时钟时间ms */
NDS03_Error NDS03_GetSystemClkMs(NDS03_Dev_t *pNxDevice,int32_t *time_ms);
/** @} NDS03_Communication_Group */
#endif

View File

@@ -0,0 +1,238 @@
/**
* @file nds03_data.c
* @author tongsheng.tang
* @brief NDS03 get depth data functions
* @version 2.x.x
* @date 2025-06
*
* @copyright Copyright (c) 2025, Nephotonics Information Technology (Hefei) Co., Ltd.
*
* @license BSD 3-Clause License
* This file is part of the NDS03 SDK and is licensed under the BSD 3-Clause License.
* You may obtain a copy of the license in the project root directory,
* in the file named LICENSE.
*/
#include "nds03_def.h"
#include "nds03_comm.h"
#include "nds03_dev.h"
#include "nds03_data.h"
/**
* @brief NDS03 Get Ranging Data Ready
* NDS03测距是否完成
* @param pNxDevice
* @return int8_t
* @retval
* - 0
* 1:
* - ::NDS03_ERROR_I2C:IIC通讯错误/
*/
NDS03_Error NDS03_GetRangingDataReady(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint8_t buf_valid_flag = 0x00;
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_DAT_VAL, &buf_valid_flag));
if (buf_valid_flag == NDS03_DEPTH_DATA_FLAG)
return 1;
return ret;
}
/**
* @brief NDS03 Start Single Measurement
*
* @param pNxDevice
* @return int8_t
*/
NDS03_Error NDS03_StartSingleMeasurement(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
/* 清除标志位 */
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_VAL, NDS03_DATA_VAL_IDLE));
/* 发送触发信号 */
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_REQ, NDS03_DEPTH_DATA_FLAG));
return ret;
}
/**
* @brief NDS03 Start Continuous Measurement
*
* @param pNxDevice
* @return int8_t
*/
NDS03_Error NDS03_StartContinuousMeasurement(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
if (pNxDevice->config.continuous_flag == 0) {
/* 清除标志位 */
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_VAL, NDS03_DATA_VAL_IDLE));
/* 发送触发信号 */
CHECK_RET(NDS03_WriteByte(pNxDevice,
NDS03_REG_DAT_REQ, NDS03_DEPTH_CONTINUOUS_FLAG));
pNxDevice->config.continuous_flag = 1;
}
return ret;
}
/**
* @brief NDS03 Stop Continuous Measurement
*
* @param pNxDevice
* @return int8_t
*/
NDS03_Error NDS03_StopContinuousMeasurement(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
if (pNxDevice->config.continuous_flag != 0) {
/* 清除标志位 */
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_REQ, NDS03_DATA_REQ_IDLE));
/* 等待测量完成 */
CHECK_RET(NDS03_WaitforDataVal(pNxDevice, NDS03_DEPTH_CONTINUOUS_FLAG, 200));
/* 清除标志位 */
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_VAL, NDS03_DATA_VAL_IDLE));
pNxDevice->config.continuous_flag = 0;
}
return ret;
}
/**
* @brief NDS03 Clear Data Valid Flag
* NDS03测量数据的有效位
* NDS03数据已读取
* @param pNxDevice
* @return NDS03_Error
*/
NDS03_Error NDS03_ClearDataValidFlag(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_VAL, NDS03_DATA_VAL_IDLE));
return ret;
}
/**
* @brief NDS03 Read Ranging Data
* NDS03寄存器获取测量数据
* @param pNxDevice
* @return int32_t
*/
NDS03_Error NDS03_ReadRangingData(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
#if NDS03_TARGET_MAX_NUM == 1
CHECK_RET(NDS03_ReadNBytesByHalfWord(pNxDevice,
NDS03_REG_DEPTH, (uint16_t*)&pNxDevice->ranging_data[0],
sizeof(pNxDevice->ranging_data[0])));
#endif
#if NDS03_TARGET_MAX_NUM == 2
CHECK_RET(NDS03_ReadNBytesByHalfWord(pNxDevice,
NDS03_REG_DEPTH, (uint16_t*)&pNxDevice->ranging_data[0],
2 * sizeof(pNxDevice->ranging_data[0])));
#endif
#if NDS03_TARGET_MAX_NUM == 3
CHECK_RET(NDS03_ReadNBytesByHalfWord(pNxDevice,
NDS03_REG_DEPTH, (uint16_t*)&pNxDevice->ranging_data[0],
3 * sizeof(pNxDevice->ranging_data[0])));
#endif
#if NDS03_TARGET_MAX_NUM >= 4
CHECK_RET(NDS03_ReadNBytesByHalfWord(pNxDevice,
NDS03_REG_DEPTH, (uint16_t*)&pNxDevice->ranging_data[0],
4 * sizeof(pNxDevice->ranging_data[0])));
#endif
return ret;
}
/**
* @brief NDS03 Get Continuous Ranging Data
* NDS03中获取一次深度数据
* NDS03_StartContinuousMeasurement函数搭配
* @param pNxDevice
* @param pData
* @return int32_t
*/
NDS03_Error NDS03_GetContinuousRangingData(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint8_t data_cnt;
uint32_t retry_cnt = 20000;
/* 等待测量完成 */
do {
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_DATA_CNT, &data_cnt));
CHECK_RET(NDS03_Delay10us(pNxDevice, 10));
} while (data_cnt == pNxDevice->data_cnt && --retry_cnt);
pNxDevice->data_cnt = data_cnt;
if (retry_cnt != 0) {
/* 读取测量数据 */
CHECK_RET(NDS03_ReadRangingData(pNxDevice));
/* 清除数据有效标志位 */
CHECK_RET(NDS03_ClearDataValidFlag(pNxDevice));
} else {
ret = NDS03_ERROR_TIMEOUT;
}
return ret;
}
/**
* @brief NDS03 Get Single Ranging Data
* NDS03中获取一次深度数据
*
* @param pNxDevice
* @return int32_t
*/
NDS03_Error NDS03_GetSingleRangingData(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
int32_t timeout_ms = pNxDevice->config.range_frame_time_us / 1000;
/* 清除标志位 */
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_VAL, NDS03_DATA_VAL_IDLE));
/* 发送触发信号 */
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_REQ, NDS03_DEPTH_DATA_FLAG));
/* 等待测量完成 */
timeout_ms = (timeout_ms < 200) ? 200 : timeout_ms;
CHECK_RET(NDS03_WaitforDataVal(pNxDevice, NDS03_DEPTH_DATA_FLAG, timeout_ms));
/* 读取测量数据 */
CHECK_RET(NDS03_ReadRangingData(pNxDevice));
/* 清除数据有效标志位 */
CHECK_RET(NDS03_ClearDataValidFlag(pNxDevice));
return ret;
}
/**
* @brief NDS03 Get Interrupt Ranging Data
* NDS03中获取一次中断深度数据
*
* @param pNxDevice
* @return int32_t
*/
NDS03_Error NDS03_GetInterruptRangingData(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
/* 读取测量数据 */
CHECK_RET(NDS03_ReadRangingData(pNxDevice));
/* 清除数据有效标志位 */
CHECK_RET(NDS03_ClearDataValidFlag(pNxDevice));
return ret;
}

View File

@@ -0,0 +1,46 @@
/**
* @file nds03_data.h
* @author tongsheng.tang
* @brief NDS03 communication and data handling functions
* @version 2.x.x
* @date 2025-06
*
* @copyright Copyright (c) 2025, Nephotonics Information Technology (Hefei) Co., Ltd.
*
* @license BSD 3-Clause License
* This file is part of the NDS03 SDK and is licensed under the BSD 3-Clause License.
* You may obtain a copy of the license in the project root directory,
* in the file named LICENSE.
*
*/
#ifndef __NDS03_DATA_H__
#define __NDS03_DATA_H__
#include "nds03_def.h"
/** @defgroup NDS03_Data_Group NDS03 Data Functions
* @brief NDS03 Data Functions
* @{
*/
/** 检测NDS03测距是否完成 */
NDS03_Error NDS03_GetRangingDataReady(NDS03_Dev_t *pNxDevice);
/** 发送开始单次测量信号 */
NDS03_Error NDS03_StartSingleMeasurement(NDS03_Dev_t *pNxDevice);
/** 发送开始连续测量信号 */
NDS03_Error NDS03_StartContinuousMeasurement(NDS03_Dev_t *pNxDevice);
/** 发送结束连续测量信号 */
NDS03_Error NDS03_StopContinuousMeasurement(NDS03_Dev_t *pNxDevice);
/** 清除数据有效位 */
NDS03_Error NDS03_ClearDataValidFlag(NDS03_Dev_t *pNxDevice);
/** 读取深度和幅度值 */
NDS03_Error NDS03_ReadRangingData(NDS03_Dev_t *pNxDevice);
/** 获取连续测量深度和幅度值 */
NDS03_Error NDS03_GetContinuousRangingData(NDS03_Dev_t *pNxDevice);
/** 获取一次测量深度和幅度值 */
NDS03_Error NDS03_GetSingleRangingData(NDS03_Dev_t *pNxDevice);
/** 获取一次中断数据 */
NDS03_Error NDS03_GetInterruptRangingData(NDS03_Dev_t *pNxDevice);
/** @} NDS03_Data_Group */
#endif // __NDS03_DATA_H__

View File

@@ -0,0 +1,308 @@
/**
* @file nds03_def.h
* @author tongsheng.tang
* @brief NDS03's Macro definition and data structure
* @version 2.x.x
* @date 2025-06
*
* @copyright Copyright (c) 2025, Nephotonics Information Technology (Hefei) Co., Ltd.
*
* @license BSD 3-Clause License
* This file is part of the NDS03 SDK and is licensed under the BSD 3-Clause License.
* You may obtain a copy of the license in the project root directory,
* in the file named LICENSE.
*
*/
#ifndef __NDS03_DEF_H__
#define __NDS03_DEF_H__
#include "nds03_stdint.h"
#if NDS03_PLATFORM == PLATFORM_LINUX_DRIVER
#include <linux/kernel.h>
#else
#include <stdio.h>
#endif
#include "nds03_platform.h"
#ifndef DEBUG_INFO
#define DEBUG_INFO 0 /** 调试信息打印开关 */
#endif
#if NDS03_PLATFORM == PLATFORM_NOT_C51
#include <stdio.h>
#define NX_PRINTF(fmt, ...) do { if (DEBUG_INFO) printf(fmt, ##__VA_ARGS__); } while(0)
#elif NDS03_PLATFORM == PLATFORM_LINUX_DRIVER
#include <linux/kernel.h>
#define NX_PRINTF(fmt, args...) do { if (DEBUG_INFO) printk(fmt, ##args); } while(0)
#elif NDS03_PLATFORM == PLATFORM_C51
#define NX_PRINTF(fmt, ...)
#else
#define NX_PRINTF(fmt, ...)
#endif
/** @defgroup NDS03_Global_Define_Group NDS03 Defines
* @brief NDS03 Defines
* @{
*/
/** @defgroup NDS03_Reg_Group NDS03 Register Defines
* @brief NDS03 Register Defines
* @{
*/
#define NDS03_REG_DEV_ADDR (0x1E) /** 模组的设备地址 */
#define NDS03_REG_STATE (0x20) /** 模组运行状态寄存器 */
#define NDS03_REG_DATA_CNT (0x21) /** 模组测距次数计数值 */
#define NDS03_REG_FW_VER (0x24) /** 模组固件版本号寄存器 */
#define NDS03_REG_RANGE_STATE (0x28) /** 测距状态码 */
#define NDS03_REG_CALIB_STATE (0x29) /** 标定状态码 */
#define NDS03_REG_THERM (0x2A) /** 温度值 */
#define NDS03_REG_AMBIENT (0x2C) /** 环境光值 */
#define NDS03_REG_DEPTH (0x30) /** 深度值 */
#define NDS03_REG_CONFI (0x32) /** 置信度 */
#define NDS03_REG_COUNT (0x34) /** 计数值 */
#define NDS03_REG_CRATE (0x36) /** 计数率 */
#define NDS03_REG_DEPTH2 (0x38) /** 深度值 */
#define NDS03_REG_CONFI2 (0x3A) /** 置信度 */
#define NDS03_REG_COUNT2 (0x3C) /** 计数值 */
#define NDS03_REG_CRATE2 (0x3E) /** 计数率 */
#define NDS03_REG_DEPTH3 (0x40) /** 深度值 */
#define NDS03_REG_CONFI3 (0x42) /** 置信度 */
#define NDS03_REG_COUNT3 (0x44) /** 计数值 */
#define NDS03_REG_CRATE3 (0x46) /** 计数率 */
#define NDS03_REG_DEPTH4 (0x48) /** 深度值 */
#define NDS03_REG_CONFI4 (0x4A) /** 置信度 */
#define NDS03_REG_COUNT4 (0x4C) /** 计数值 */
#define NDS03_REG_CRATE4 (0x4E) /** 计数率 */
#define NDS03_REG_DAT_REQ (0x50) /** 获取深度请求寄存器 */
#define NDS03_REG_DAT_VAL (0x51) /** 获取数据命令有效寄存器 */
#define NDS03_REG_CMD_REQ (0x52) /** 命令请求寄存器 */
#define NDS03_REG_CMD_VAL (0x53) /** 命令完成寄存器 */
#define NDS03_REG_CMD_ENA (0x54) /** 命令使能寄存器 */
#define NDS03_REG_CFG_ENA (0x55) /** 配置使能该值为0xA5才能访问其他寄存器 */
#define NDS03_REG_REF_HISTO_MAX (0x56) /** 参考直方图最大值 */
#define NDS03_REG_DEPTH_FLAG (0x64) /** 测距结果标志位 */
#define NDS03_REG_SLEEP_TIME (0x66) /** 睡眠时间(ms) */
#define NDS03_REG_PULSE_NUM (0x6C) /** 发光次数 */
#define NDS03_REG_DEPTH_TH_L (0x70) /** 深度低阈值 */
#define NDS03_REG_DEPTH_TH_H (0x72) /** 深度高阈值 */
#define NDS03_REG_GPIO1_FUNC (0x74) /** 中断功能/方式 */
#define NDS03_REG_SLEEP_MODE (0x75) /** 睡眠方式 */
#define NDS03_REG_CONFI_TH (0x76) /** 置信度阈值 */
#define NDS03_REG_TARGET_NUM (0x77) /** 目标个数 */
#define NDS03_REG_INV_TIME (0x7C) /** 测量间隔时间 */
#define NDS03_REG_OFFSET_MM (0x90) /** offset 标定位置 */
#define NDS03_REG_XTALK_TH (0xB0) /** 串扰阈值 */
#define NDS03_REG_XTALK (0xB2) /** 串扰值 */
#define NDS03_REG_DATA_SIZE (0x5C) /** 数据大小 */
#define NDS03_REG_CACHE_SIZE (0x5D) /** 缓存大小 */
#define NDS03_REG_CACHE_ADDR (0x5E) /** 缓存地址 */
#define NDS03_REG_CACHE_DATA (0xC0) /** 缓存数据 */
/** @} NDS03_Reg_Group */
/** @defgroup NDS03_State_Group NDS03 Data Request Index
* @brief NDS03 State (NDS03_REG_STATE)
* @{
*/
#define NDS03_STATE_IDLE 0x00 /** 空闲状态 */
#define NDS03_STATE_SOFT_READY 0xA5 /** 软件就绪状态 */
#define NDS03_STATE_GOT_DEPTH 0xA6 /** 已获取深度状态 */
/** @} NDS03_State_Group */
/** @defgroup NDS03_Data_Val_Req_Idx_Group NDS03 Data Request Index
* @brief NDS03 Data Request Mask (NDS03_REG_DAT_VAL_REQ)
* @{
*/
/** DATA REQ */
#define NDS03_DATA_REQ_IDLE 0x00 /** 无效数据命令标志位 */
#define NDS03_DEPTH_DATA_FLAG 0x10 /** 获取深度数据标志 */
#define NDS03_DEPTH_CONTINUOUS_FLAG 0x13 /** 连续获取深度数据标志 */
#define NDS03_USER_DATA_FLAG 0x50 /** 读用户数据标志位 */
#define NDS03_GET_MODEL_FLAG 0x51 /** 获取模组型号标志位 */
#define NDS03_CMD_READ_HGM_DATA 0xC0 /** 读直方图数据标志位 */
#define NDS03_CMD_READ_HGM_DATA_ENA 0x05 /** 读直方图数据使能标志位 */
/** DATA VAL */
#define NDS03_DATA_VAL_IDLE 0x00 /** 无效数据标志位 */
/** CMD REQ */
#define NDS03_CMD_OFFSET_CALIB 0x20 /** offset标定命令 */
#define NDS03_CMD_XTALK_CALIB 0x24 /** xtalk标定 */
#define NDS03_CMD_WRITE_USER_DATA 0x50 /** 写用户数据区域标志位 */
/** CMD VAL */
#define NDS03_CMD_VAL_IDLE 0x00 /** 无效命令标志位 */
/** CMD ENA */
#define NDS03_CMD_WRITE_USER_DATA_ENA 0xAA /** 用户数据使能标志位 */
#define NDS03_CMD_ENA_ENABLE 0xA5 /** 配置开启标志位 */
#define NDS03_CMD_ENA_DISABLE 0x00 /** 配置关闭标志位 */
/** @} NDS03_Data_Val_Req_Idx_Group */
/** @defgroup NDS03_Sleep_Group NDS03 Sleep Group
* @brief NDS03 Sleep (NDS03_REG_SLEEP_MODE)
* @{
*/
#define NDS03_MANUAL_SLEEP_TIME_OUT_WEAK_UP 0xA5 /* 手动进入软件睡眠标志位,超时唤醒 */
#define NDS03_MANUAL_SLEEP_MANUAL_WEAK_UP 0xA4 /* 手动进入软件睡眠标志位,手动唤醒 */
// #define NDS03_AUTO_SLEEP_MANUAL_WEAK_UP 0xA3 /* 测量结束自动进入软件睡眠标志位,手动唤醒 */
// #define NDS03_AUTO_SLEEP_TIME_OUT_WEAK_UP 0xA2 /* 测量结束自动进入软件睡眠标志位,超时唤醒 */
/** @} NDS03_Sleep_Group */
/** @enum NDS03_Status_e
* @brief NDS03状态宏
*/
typedef int8_t NDS03_Error;
/** @defgroup NDS03_Error_Group NDS03 Error Group
* @brief NDS03 Error Group (NDS03_REG_ERROR_FLAG)
* @{
*/
#define NDS03_ERROR_NONE 0 /** 成功 */
#define NDS03_ERROR_API -1 /** api接口注册失败 */
#define NDS03_ERROR_TIMEOUT -2 /** 超时错误 */
#define NDS03_ERROR_I2C -3 /** IIC通讯错误 */
#define NDS03_ERROR_BOOT -4 /** 模组启动错误 */
#define NDS03_ERROR_CALIB -5 /** 标定失败错误 */
#define NDS03_ERROR_INIT -6 /** 初始化失败 */
#define NDS03_ERROR_RANGING -7 /** 测距出错 */
#define NDS03_ERROR_UPGRADE_VER -8 /** 升级版本不对 */
#define NDS03_ERROR_UPGRADE -9 /** 升级失败 */
#define NDS03_ERROR_AMBIENT_HIGH -10 /** 标定时环境光强过大 */
#define NDS03_ERROR_NO_NDS03 -11 /** 不是NDS03模组 */
#define NDS03_ERROR_VCSEL_ERROR -12 /** VCSEL不亮 */
#define NDS03_ERROR_OFFSET_ERROR -13 /** OFFSET 标定失败 */
/** @} NDS03_Error_Group */
/** @defgroup NDS03_Calib_State_Group NDS03 Calib State Group
* @brief NDS03 Calib State Group (NDS03_REG_CALIB_STATE)
* @{
*/
#define NDS03_CALIB_ERROR_NONE 0x00 /** 成功 */
#define NDS03_CALIB_ERROR_XTALK_OVERFLOW 0x02 /** 串扰溢出 */
#define NDS03_CALIB_ERROR_XTALK_EXCESSIVE 0x10 /** 串扰过大 */
#define NDS03_CALIB_ERROR_OFFSET 0x20 /** offset标定后测距误差过大 */
/** @} NDS03_Calib_State_Group */
/** @defgroup NDS03_GPIO1_Func_Group NDS03 GPIO1 Functions Define
* @brief NDS03 GPIO1 Functions Define Group (NDS03_REG_GPIO1_FUNC)
* @{
*/
/** REG_GPIO1_SETTING Mask */
#define NDS03_GPIO1_FUNCTIONALITY_MASK 0x07 /** GPIO1功能配置掩码 */
#define NDS03_GPIO1_POLARITY_MASK 0x08 /** 极性配置掩码 */
/** GPIO1 Functionality */
typedef uint8_t NDS03_Gpio1Func_t;
#define NDS03_GPIO1_FUNCTIONALITY_OFF ((NDS03_Gpio1Func_t)0x00) /** 无触发中断 */
#define NDS03_GPIO1_THRESHOLD_LOW ((NDS03_Gpio1Func_t)0x01) /** 低深度触发中断 (value < NDS03_REG_DEPTH_TH_L) */
#define NDS03_GPIO1_THRESHOLD_HIGH ((NDS03_Gpio1Func_t)0x02) /** 高深度触发中断 (value > NDS03_REG_DEPTH_TH_H) */
#define NDS03_GPIO1_THRESHOLD_DOMAIN_OUT ((NDS03_Gpio1Func_t)0x03) /** 低深度或高深度触发中断 (value < NDS03_REG_DEPTH_TH_L 或 value > NDS03_REG_DEPTH_TH_H) */
#define NDS03_GPIO1_NEW_MEASURE_READY ((NDS03_Gpio1Func_t)0x04) /** 新深度数据就绪中断 */
/** GPIO1 polarity */
typedef uint8_t NDS03_Gpio1Polar_t;
#define NDS03_GPIO1_POLARITY_LOW ((NDS03_Gpio1Polar_t)0x00) /** 负极性, 低电平有效 */
#define NDS03_GPIO1_POLARITY_HIGH ((NDS03_Gpio1Polar_t)0x01) /** 正极性, 高电平有效 */
/** @} NDS03_GPIO1_Func_Group */
#define NDS03_TARGET_MAX_NUM 4
#define NDS03_AMBIENT_TH 1000 /** 标定时环境光强阈值 */
#define NDS03_OFFSET_DEPTH_ERROR_TH 10 /** OFFSET 标定深度误差阈值 */
#define NDS03_OFFSET_REF_MAX_COUNT_TH 500 /** OFFSET 标定深度误差阈值 */
#define NDS03_DEPTH_INVALID_VALUE 65300 /** 深度无效值 */
/** @enum NDS03_Status_e
* @brief NDS03状态宏
*/
typedef enum{
NDS03_DISABLE = 0, ///< 关闭状态
NDS03_ENABLE = 1 ///< 使能状态
} NDS03_Status_e;
/**
* @struct NDS03_RangingData_t
*
* @brief NDS03测量结果结构体 \n
* NDS03的深度
*/
typedef struct{
uint16_t depth; ///< 测量距离
uint16_t confi; ///< 测量置信度
uint16_t count; ///< 计数值
uint16_t crate; ///< 计数率
} NDS03_RangingData_t;
/**
* @struct NDS03_ChipInfo_t
*
* @brief NDS03模组生产信息\n
*/
typedef struct {
uint32_t fw_version; ///< NDS03固件版本
} NDS03_ChipInfo_t;
/**
* @struct NDS03_DevConfig_t
*
* @brief NDS03模组配置数据\n
*/
typedef struct {
uint32_t range_frame_time_us; //< 模组取图帧间隔时间配置
uint8_t continuous_flag; ///< 连续模式标志位
uint8_t target_num; ///< 目标个数
} NDS03_DevConfig_t;
/**
* @struct NDS03_Dev_t
*
* @brief \n
*/
typedef struct {
uint8_t dev_pwr_state; ///< 设备的当前状态, 就绪模式或者休眠模式
NDS03_DevConfig_t config; ///< 模组配置信息
NDS03_ChipInfo_t chip_info; ///< 模组设备信息
uint8_t data_cnt; ///< 数据获取次数
NDS03_RangingData_t ranging_data[NDS03_TARGET_MAX_NUM]; ///< 测距数据结果
NDS03_Platform_t platform;
} NDS03_Dev_t;
#define NDS03_DEFAULT_SLAVE_ADDR 0x5C
#if NDS03_PLATFORM == PLATFORM_C51
#define CHECK_RET(func) \
ret = func; \
if(ret != NDS03_ERROR_NONE) \
return ret;
#else
#define CHECK_RET(func) do { \
ret = func; \
if(ret != NDS03_ERROR_NONE) { \
NX_PRINTF("%s I2c Error, ret: %d\r\n", #func, ret); \
return ret; \
} \
} while (0)
#endif
#endif

View File

@@ -0,0 +1,743 @@
/**
* @file nds03_dev.c
* @author tongsheng.tang
* @brief NDS03 device setting functions
* @version 2.x.x
* @date 2025-06
*
* @copyright Copyright (c) 2025, Nephotonics Information Technology (Hefei) Co., Ltd.
*
* @license BSD 3-Clause License
* This file is part of the NDS03 SDK and is licensed under the BSD 3-Clause License.
* You may obtain a copy of the license in the project root directory,
* in the file named LICENSE.
*
*/
#include "nds03_dev.h"
#include "nds03_def.h"
#include "nds03_comm.h"
/** SDK主版本 */
static uint8_t sdk_version_major = 2;
/** SDK次版本 */
static uint8_t sdk_version_minor = 0;
/** SDK小版本 */
static uint8_t sdk_version_patch = 3;
/**
* @brief NDS03 Get SDK Version
* SDK的软件版本号
* @return uint32_t
* @retval
*/
uint32_t NDS03_GetSdkVersion(void)
{
return ((uint32_t)sdk_version_major << 16) +
((uint32_t)sdk_version_minor << 8) + (uint32_t)sdk_version_patch;
}
/**
* @brief NDS03 Get Firmware Version
* NDS03模组固件版本号
* @param pNxDevice
* @return NDS03_Error
*/
NDS03_Error NDS03_GetFirmwareVersion(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_ReadWord(pNxDevice, NDS03_REG_FW_VER, &pNxDevice->chip_info.fw_version));
return ret;
}
/**
* @brief NDS03 Get Therm
* NDS03的温度
* @param pNxDevice
* @param therm 0.1
* @return NDS03_Error
*/
NDS03_Error NDS03_GetTherm(NDS03_Dev_t *pNxDevice, int16_t* therm)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, NDS03_REG_THERM, (uint16_t*)therm));
return ret;
}
/**
* @brief NDS03 Set Pulse Num
*
* @param pNxDevice
* @param pulse_num
* @return NDS03_Error
*/
NDS03_Error NDS03_SetPulseNum(NDS03_Dev_t *pNxDevice, uint32_t pulse_num)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteWord(pNxDevice, NDS03_REG_PULSE_NUM, pulse_num));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_DISABLE));
return ret;
}
/**
* @brief NDS03 Get Pulse Num
*
* @param pNxDevice
* @param pulse_num
* @return NDS03_Error
*/
NDS03_Error NDS03_GetPulseNum(NDS03_Dev_t *pNxDevice, uint32_t *pulse_num)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_ReadWord(pNxDevice, NDS03_REG_PULSE_NUM, pulse_num));
return ret;
}
/**
* @brief NDS03 Set Frame Time
* (us)
* @param pNxDevice
* @param inv_time
* @return NDS03_Error
*/
NDS03_Error NDS03_SetFrameTime(NDS03_Dev_t *pNxDevice, uint32_t frame_time_us)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteWord(pNxDevice, NDS03_REG_INV_TIME, frame_time_us));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_DISABLE));
CHECK_RET(NDS03_ReadWord(pNxDevice, NDS03_REG_INV_TIME,
&pNxDevice->config.range_frame_time_us));
return ret;
}
/**
* @brief NDS03 Get Frame Time
* (us)
* @param pNxDevice
* @param inv_time
* @return NDS03_Error
*/
NDS03_Error NDS03_GetFrameTime(NDS03_Dev_t *pNxDevice, uint32_t *frame_time_us)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_ReadWord(pNxDevice, NDS03_REG_INV_TIME,
&pNxDevice->config.range_frame_time_us));
*frame_time_us = pNxDevice->config.range_frame_time_us;
return ret;
}
/**
* @brief NDS03 Set Confidence threshold
*
* @param pNxDevice
* @param confi_th
* @return NDS03_Error
*/
NDS03_Error NDS03_SetConfiTh(NDS03_Dev_t *pNxDevice, uint8_t confi_th)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CONFI_TH, confi_th));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_DISABLE));
return ret;
}
/**
* @brief NDS03 Get Confidence threshold
*
* @param pNxDevice
* @param confi_th
* @return NDS03_Error
*/
NDS03_Error NDS03_GetConfiTh(NDS03_Dev_t *pNxDevice, uint8_t *confi_th)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_CONFI_TH, confi_th));
return ret;
}
/**
* @brief NDS03 Set Target Num
*
* @param pNxDevice
* @param num
* @return NDS03_Error
*/
NDS03_Error NDS03_SetTargetNum(NDS03_Dev_t *pNxDevice, uint8_t num)
{
NDS03_Error ret = NDS03_ERROR_NONE;
num = (num > NDS03_TARGET_MAX_NUM) ? NDS03_TARGET_MAX_NUM : num;
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_TARGET_NUM, num));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_DISABLE));
pNxDevice->config.target_num = num;
return ret;
}
/**
* @brief NDS03 Get Target Num
*
* @param pNxDevice
* @param num
* @return NDS03_Error
*/
NDS03_Error NDS03_GetTargetNum(NDS03_Dev_t *pNxDevice, uint8_t *num)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_TARGET_NUM, num));
pNxDevice->config.target_num = *num;
return ret;
}
/**
* @brief NDS03 Set Gpio1 Configuration
*
* @param pNxDevice: NDS03模组设备信息结构体
* @param functionality:
* NDS03_GPIO1_FUNCTIONALITY_OFF:
* NDS03_GPIO1_THRESHOLD_LOWGPIO输出有效电平
* NDS03_GPIO1_THRESHOLD_HIGHGPIO输出有效电平
* NDS03_GPIO1_THRESHOLD_DOMAIN_OUTGPIO输出有效电平
* NDS03_GPIO1_NEW_MEASURE_READY:GPIO输出有效电平
* @param polarity: INT引脚有效电平
* NDS03_GPIO1_POLARITY_LOW
* NDS03_GPIO1_POLARITY_HIGH
* @return NDS03_Error
*/
NDS03_Error NDS03_SetGpio1Config(NDS03_Dev_t *pNxDevice,
NDS03_Gpio1Func_t functionality, NDS03_Gpio1Polar_t polarity)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint8_t rbuf;
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_GPIO1_FUNC, &rbuf));
/** 设置中断功能 */
rbuf = rbuf & (~NDS03_GPIO1_FUNCTIONALITY_MASK);
rbuf = rbuf | (functionality & NDS03_GPIO1_FUNCTIONALITY_MASK);
/** 设置中断引脚极性 */
if (polarity == NDS03_GPIO1_POLARITY_HIGH)
rbuf = rbuf | NDS03_GPIO1_POLARITY_MASK;
else
rbuf = rbuf & (~NDS03_GPIO1_POLARITY_MASK);
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_GPIO1_FUNC, rbuf));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_DISABLE));
return ret;
}
/**
* @brief NDS03 Get Gpio1 Config
*
* @param pNxDevice: NDS03模组设备信息结构体指针
* @param functionality:
* @param polarity:
* @return NDS03_Error
*/
NDS03_Error NDS03_GetGpio1Config(NDS03_Dev_t *pNxDevice,
NDS03_Gpio1Func_t *functionality, NDS03_Gpio1Polar_t *polarity)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint8_t rbuf;
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_GPIO1_FUNC, &rbuf));
// Polarity
*polarity = ((rbuf & NDS03_GPIO1_POLARITY_MASK) == NDS03_GPIO1_POLARITY_MASK) ?
NDS03_GPIO1_POLARITY_HIGH : NDS03_GPIO1_POLARITY_LOW;
// Functionality
*functionality = rbuf & NDS03_GPIO1_FUNCTIONALITY_MASK;
return ret;
}
/**
* @brief NDS03 Set Depth Threshold
*
* GPIO功能为
* NDS03_GPIO1_THRESHOLD_LOW
* NDS03_GPIO1_THRESHOLD_HIGH
* NDS03_GPIO1_THRESHOLD_DOMAIN_OUT
* @param pNxDevice: NDS03模组设备信息结构体
* @param depth_low: / mm
* @param depth_high: / mm
* @return NDS03_Error
*/
NDS03_Error NDS03_SetDepthThreshold(NDS03_Dev_t *pNxDevice,
uint16_t depth_low, uint16_t depth_high)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteHalfWord(pNxDevice, NDS03_REG_DEPTH_TH_L, depth_low));
CHECK_RET(NDS03_WriteHalfWord(pNxDevice, NDS03_REG_DEPTH_TH_H, depth_high));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_DISABLE));
return ret;
}
/**
* @brief NDS03 Get Depth Threshold
*
* @param pNxDevice: NDS03模组设备信息结构体
* @param depth_low: / mm
* @param depth_high: / mm
* @return NDS03_Error
*/
NDS03_Error NDS03_GetDepthThreshold(NDS03_Dev_t *pNxDevice,
uint16_t *depth_low, uint16_t *depth_high)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, NDS03_REG_DEPTH_TH_L, depth_low));
CHECK_RET(NDS03_ReadHalfWord(pNxDevice, NDS03_REG_DEPTH_TH_H, depth_high));
return ret;
}
/**
* @brief NDS03 Waitfor Data Val
*
* @param pNxDevice
* @param flag
* @return NDS03_Error
*/
NDS03_Error NDS03_WaitforDataVal(NDS03_Dev_t *pNxDevice, uint8_t flag, int32_t timeout_ms)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint8_t dat_valid_flag = 0x00;
int32_t retry_cnt;
retry_cnt = timeout_ms * 2;
do {
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_DAT_VAL, &dat_valid_flag));
if (dat_valid_flag == flag)
break;
NDS03_Delay10us(pNxDevice, 50);
} while (--retry_cnt);
if (retry_cnt == 0) {
NX_PRINTF("data_valid_flag: %d\r\n", dat_valid_flag);
ret = NDS03_ERROR_TIMEOUT;
}
return ret;
}
/**
* @brief NDS03 Waitfor Cmd Val
*
* @param pNxDevice
* @param cmd
* @param timeout_ms
* @return NDS03_Error
*/
NDS03_Error NDS03_WaitforCmdVal(NDS03_Dev_t *pNxDevice, uint8_t cmd, int32_t timeout_ms)
{
NDS03_Error ret = NDS03_ERROR_NONE;
int32_t retry_cnt;
uint8_t val;
retry_cnt = timeout_ms * 2;
do {
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_CMD_VAL, &val));
if (val == cmd)
break;
NDS03_Delay10us(pNxDevice, 50);
} while (--retry_cnt);
if (retry_cnt == 0) {
NX_PRINTF("Timeout!!, val: 0x%02x\r\n", val);
ret = NDS03_ERROR_TIMEOUT;
}
return ret;
}
/**
* @brief NDS03 Read Hgm Data
* NDS03直方图数据
* @param pNxDevice
* @param rbuf
* @param size
* @return NDS03_Error
*/
NDS03_Error NDS03_ReadHgmData(NDS03_Dev_t *pNxDevice, uint8_t *rbuf, uint32_t size)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint32_t rsize, _size;
uint8_t one_size;
uint16_t addr = 0xe000;
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_CACHE_SIZE, &one_size));
_size = (uint32_t)one_size;
rsize = 0;
while ((rsize < size) && (ret == NDS03_ERROR_NONE)) {
CHECK_RET(NDS03_WriteHalfWord(pNxDevice, NDS03_REG_CACHE_ADDR, addr));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_ENA, 0x05));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_REQ, 0xC0));
CHECK_RET(NDS03_WaitforDataVal(pNxDevice, 0xC0, 200));
CHECK_RET(NDS03_ReadNBytes(pNxDevice,
NDS03_REG_CACHE_DATA, rbuf,
((size - rsize) > _size) ? _size : (size - rsize)));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_VAL, NDS03_DATA_VAL_IDLE));
rbuf += _size;
addr += _size;
rsize += _size;
}
return ret;
}
/**
* @brief NDS03 Read User Data
* NDS03中用户数据区域0xFFF032
* 160xFFF0~0xFFFF160x0000~0x000F
* @param pNxDevice
* @param addr 0x0000~0xFFFF
* @param rbuf
* @param size
* @return NDS03_Error
*/
NDS03_Error NDS03_ReadUserData(NDS03_Dev_t *pNxDevice,
uint16_t addr, uint8_t *rbuf, uint32_t size)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint32_t rsize, _size;
uint8_t one_size;
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_CACHE_SIZE, &one_size));
_size = (uint32_t)one_size;
rsize = 0;
while ((rsize < size) && (ret == NDS03_ERROR_NONE)) {
CHECK_RET(NDS03_WriteHalfWord(pNxDevice, NDS03_REG_CACHE_ADDR, addr));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_REQ, NDS03_USER_DATA_FLAG));
CHECK_RET(NDS03_WaitforDataVal(pNxDevice, NDS03_USER_DATA_FLAG, 200));
CHECK_RET(NDS03_ReadNBytes(pNxDevice, NDS03_REG_CACHE_DATA,
rbuf, ((size - rsize) > _size) ? _size : (size - rsize)));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_VAL, NDS03_DATA_VAL_IDLE));
rbuf += _size;
addr += _size;
rsize += _size;
}
return ret;
}
/**
* @brief NDS03 Read User Data
* NDS03中用户数据区域, 0xFFF032
* 160xFFF0~0xFFFF160x0000~0x000F
* @param pNxDevice
* @param addr 0x0000~0xFFFF
* @param wbuf
* @param size
* @return NDS03_Error
*/
NDS03_Error NDS03_WriteUserData(NDS03_Dev_t *pNxDevice,
uint16_t addr, uint8_t *wbuf, uint32_t size)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint32_t rsize, _size;
uint8_t one_size;
// NX_PRINTF("%s Start\r\n", __func__);
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_CACHE_SIZE, &one_size));
_size = (uint32_t)one_size;
rsize = 0;
while ((rsize < size) && (ret == NDS03_ERROR_NONE)) {
CHECK_RET(NDS03_WriteNBytes(pNxDevice, NDS03_REG_CACHE_DATA,
wbuf, ((size - rsize) > _size) ? _size : (size - rsize)));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DATA_SIZE,
((size - rsize) > _size) ? _size : (size - rsize)));
CHECK_RET(NDS03_WriteHalfWord(pNxDevice, NDS03_REG_CACHE_ADDR, addr));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_ENA,
NDS03_CMD_WRITE_USER_DATA_ENA));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_REQ,
NDS03_CMD_WRITE_USER_DATA));
CHECK_RET(NDS03_WaitforCmdVal(pNxDevice, NDS03_CMD_WRITE_USER_DATA, 500));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CMD_VAL, NDS03_CMD_VAL_IDLE));
wbuf += _size;
addr += _size;
rsize += _size;
}
// NX_PRINTF("%s End\r\n", __func__);
return ret;
}
/**
* @brief NDS03软件休眠
*
* @param pNxDevice
* @param sleep_time_ms
* @return NDS03_Error
*/
NDS03_Error NDS03_SoftSleepWithAutoWakeup(NDS03_Dev_t *pNxDevice, uint16_t sleep_time_ms)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteHalfWord(pNxDevice, NDS03_REG_SLEEP_TIME, sleep_time_ms));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_SLEEP_MODE,
NDS03_MANUAL_SLEEP_TIME_OUT_WEAK_UP));
return ret;
}
/**
* @brief NDS03软件休眠
*
* @param pNxDevice
* @return NDS03_Error
*/
NDS03_Error NDS03_SoftSleepWithManualWakeup(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteByte(pNxDevice,
NDS03_REG_SLEEP_MODE, NDS03_MANUAL_SLEEP_MANUAL_WEAK_UP));
return ret;
}
/**
* @brief NDS03 Sleep
* NDS03进入休眠
* @param pNxDevice
* @return NDS03_Error
*/
NDS03_Error NDS03_Sleep(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_SetXShutPinLevel(pNxDevice, 0));
return ret;
}
/**
* @brief NDS03 Wakeup
* NDS03从软件睡眠中唤醒
* @param pNxDevice
* @return NDS03_Error
*/
NDS03_Error NDS03_SoftWakeup(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint32_t freq;
CHECK_RET(NDS03_GetI2cFreq(pNxDevice,&freq));
CHECK_RET(NDS03_SetI2cFreq(pNxDevice,1000));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_SLEEP_MODE, 0));
CHECK_RET(NDS03_SetI2cFreq(pNxDevice,freq));
return ret;
}
/**
* @brief NDS03 Software Sleep
*
* @param pNxDevice
* @param sleep_time_ms
* @return NDS03_Error
* @deprecated 使NDS03_SoftSleepWithAutoWakeup()
*/
static __maybe_unused NDS03_Error NDS03_SoftSleep(NDS03_Dev_t *pNxDevice, uint16_t sleep_time_ms)
{
NDS03_Error ret = NDS03_ERROR_NONE;
ret |= NDS03_WriteByte(pNxDevice, NDS03_REG_CFG_ENA, NDS03_CMD_ENA_ENABLE);
ret |= NDS03_WriteHalfWord(pNxDevice, NDS03_REG_SLEEP_TIME, sleep_time_ms);
ret |= NDS03_WriteByte(pNxDevice, NDS03_REG_SLEEP_MODE,
NDS03_MANUAL_SLEEP_TIME_OUT_WEAK_UP);
return ret;
}
/**
* @brief NDS03 Wakeup
* NDS03从睡眠中唤醒
* @param pNxDevice
* @return NDS03_Error
*/
NDS03_Error NDS03_Wakeup(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_SetXShutPinLevel(pNxDevice, 1));
return ret;
}
/**
* @brief NDS03
*
* @param pNxDevice
* @return NDS03_Error
* @arg 0: NDS03
* @arg NDS03_ERROR_NO_NDS03: NDS03
* @arg others: I2C通信异常
* I2C通信异常NDS03
*/
NDS03_Error NDS03_IsNDS03(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint8_t buf[4] = {0};
NDS03_SetXShutPinLevel(pNxDevice, 1);
CHECK_RET(NDS03_Delay1ms(pNxDevice, 10));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_REQ, NDS03_GET_MODEL_FLAG));
CHECK_RET(NDS03_WaitforDataVal(pNxDevice, NDS03_GET_MODEL_FLAG, 200));
CHECK_RET(NDS03_ReadNBytes(pNxDevice, NDS03_REG_CACHE_DATA, buf, sizeof(buf)));
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DAT_VAL, NDS03_DATA_VAL_IDLE));
if ((buf[0] != 0x31) || (buf[1] != 0x53) || (buf[2] != 0x30) || (buf[3] != 0x33))
ret = NDS03_ERROR_NO_NDS03;
return ret;
}
/**
* @brief NDS03 Set Device Address
*
* @param pNxDevice
* @param dev_addr
* @return NDS03_Error
*/
NDS03_Error NDS03_SetDevAddr(NDS03_Dev_t *pNxDevice, uint8_t dev_addr)
{
NDS03_Error ret = NDS03_ERROR_NONE;
CHECK_RET(NDS03_WriteByte(pNxDevice, NDS03_REG_DEV_ADDR, dev_addr));
pNxDevice->platform.i2c_dev_addr = dev_addr;
return ret;
}
/**
* @brief NDS03 InitDevice
*
* @param pNxDevice
* @return NDS03_Error
*/
NDS03_Error NDS03_InitDevice(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
// NX_PRINTF("%s Start!\r\n", __func__);
CHECK_RET(NDS03_GetFirmwareVersion(pNxDevice));
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_DATA_CNT, &pNxDevice->data_cnt));
CHECK_RET(NDS03_GetFrameTime(pNxDevice, &pNxDevice->config.range_frame_time_us));
pNxDevice->config.continuous_flag = 0;
// NX_PRINTF("%s End!\r\n", __func__);
return ret;
}
/**
* @brief NDS03 Wait for Device Boot Up
* NDS03模组启动
* @param pNxDevice
* @return NDS03_ERROR_NONE:
* NDS03_ERROR_BOOT:--i2c地址与读写函数是否错误
* NDS03_ERROR_FW:--FAE联系SDK不兼容
*/
NDS03_Error NDS03_WaitDeviceBootUp(NDS03_Dev_t *pNxDevice)
{
NDS03_Error ret = NDS03_ERROR_NONE;
int32_t try_times = 200;
uint8_t slave_addr = pNxDevice->platform.i2c_dev_addr;
NDS03_SetXShutPinLevel(pNxDevice, 0);
CHECK_RET(NDS03_Delay10us(pNxDevice, 20));
NDS03_SetXShutPinLevel(pNxDevice, 1);
CHECK_RET(NDS03_Delay1ms(pNxDevice, 2));
ret = NDS03_SetXShutPinLevel(pNxDevice, 1);
pNxDevice->platform.i2c_dev_addr = (ret == NDS03_ERROR_NONE) ?
NDS03_DEFAULT_SLAVE_ADDR : pNxDevice->platform.i2c_dev_addr;
pNxDevice->data_cnt = 0;
pNxDevice->config.continuous_flag = 0;
do {
CHECK_RET(NDS03_Delay10us(pNxDevice, 10));
CHECK_RET(NDS03_ReadByte(pNxDevice, NDS03_REG_STATE, &pNxDevice->dev_pwr_state));
} while ((pNxDevice->dev_pwr_state != NDS03_STATE_SOFT_READY) &&
(pNxDevice->dev_pwr_state != NDS03_STATE_GOT_DEPTH) && --try_times);
if (0 == try_times) {
NX_PRINTF("state: 0x%02x\r\n", pNxDevice->dev_pwr_state);
NX_PRINTF("NDS03 boot error\r\n");
return NDS03_ERROR_BOOT;
}
if (slave_addr != pNxDevice->platform.i2c_dev_addr)
CHECK_RET(NDS03_SetDevAddr(pNxDevice, slave_addr));
return ret;
}
/**
* @brief NDS03 Dirty Warning
*
* @param pNxDevice
* @param flag 10
* @param time_th ms
* @return NDS03_ERROR_NONE:
*/
NDS03_Error NDS03_DirtyWarning(NDS03_Dev_t *pNxDevice,uint8_t *flag,uint32_t time_th)
{
NDS03_Error ret = NDS03_ERROR_NONE;
uint16_t depth_low_th = 30;
static int32_t depth_low_count = 0, depth_low_time_sta = 0, depth_low_time_end = 0;
if (time_th == 0)
time_th = 10 * 1000;
if (pNxDevice->ranging_data[0].depth < depth_low_th) {
depth_low_count ++;
if (depth_low_count == 1)
NDS03_GetSystemClkMs(pNxDevice, &depth_low_time_sta);
NDS03_GetSystemClkMs(pNxDevice, &depth_low_time_end);
if (depth_low_time_end < depth_low_time_sta) {
depth_low_time_sta = 0;
depth_low_time_end = 0;
}
} else {
depth_low_count = 0;
depth_low_time_sta = 0;
depth_low_time_end = 0;
}
if (depth_low_count > 10 && pNxDevice->ranging_data[1].depth != NDS03_DEPTH_INVALID_VALUE)
*flag = 1;
if ((depth_low_time_end - depth_low_time_sta) > time_th)
*flag = 1;
return ret;
}

View File

@@ -0,0 +1,94 @@
/**
* @file nds03_dev.h
* @author tongsheng.tang
* @brief NDS03 device setting functions
* @version 2.x.x
* @date 2025-06
*
* @copyright Copyright (c) 2025, Nephotonics Information Technology (Hefei) Co., Ltd.
*
* @license BSD 3-Clause License
* This file is part of the NDS03 SDK and is licensed under the BSD 3-Clause License.
* You may obtain a copy of the license in the project root directory, in the file named LICENSE.
*
*/
#ifndef __NDS03_DEV_H__
#define __NDS03_DEV_H__
#include "nds03_def.h"
/** @defgroup NDS03_Dev_Group NDS03 Device Functions
* @brief NDS03 Device Functions
* @{
*/
/** 获取SDK版本号 */
uint32_t NDS03_GetSdkVersion(void);
/** 获取NDS03模组固件版本号 */
NDS03_Error NDS03_GetFirmwareVersion(NDS03_Dev_t *pNxDevice);
/** 获取温度 */
NDS03_Error NDS03_GetTherm(NDS03_Dev_t *pNxDevice, int16_t* therm);
/** 设置发光次数 */
NDS03_Error NDS03_SetPulseNum(NDS03_Dev_t *pNxDevice, uint32_t pulse_num);
/** 获取发光次数 */
NDS03_Error NDS03_GetPulseNum(NDS03_Dev_t *pNxDevice, uint32_t *pulse_num);
/** 设置测量间隔时间 */
NDS03_Error NDS03_SetFrameTime(NDS03_Dev_t *pNxDevice, uint32_t frame_time_us);
/** 获取测量间隔时间 */
NDS03_Error NDS03_GetFrameTime(NDS03_Dev_t *pNxDevice, uint32_t *frame_time_us);
/** 设置置信度阈值 */
NDS03_Error NDS03_SetConfiTh(NDS03_Dev_t *pNxDevice, uint8_t confi_th);
/** 获取置信度阈值 */
NDS03_Error NDS03_GetConfiTh(NDS03_Dev_t *pNxDevice, uint8_t *confi_th);
/** 设置目标个数 */
NDS03_Error NDS03_SetTargetNum(NDS03_Dev_t *pNxDevice, uint8_t num);
/** 获取目标个数 */
NDS03_Error NDS03_GetTargetNum(NDS03_Dev_t *pNxDevice, uint8_t *num);
/** 设置中断引脚功能 */
NDS03_Error NDS03_SetGpio1Config(NDS03_Dev_t *pNxDevice,
NDS03_Gpio1Func_t functionality, NDS03_Gpio1Polar_t polarity);
/** 获取中断引脚功能 */
NDS03_Error NDS03_GetGpio1Config(NDS03_Dev_t *pNxDevice,
NDS03_Gpio1Func_t *functionality, NDS03_Gpio1Polar_t *polarity);
/** 设置深度阈值 */
NDS03_Error NDS03_SetDepthThreshold(NDS03_Dev_t *pNxDevice,
uint16_t depth_low, uint16_t depth_high);
/** 获取深度阈值 */
NDS03_Error NDS03_GetDepthThreshold(NDS03_Dev_t *pNxDevice,
uint16_t *depth_low, uint16_t *depth_high);
/** 等待获取数据结束 */
NDS03_Error NDS03_WaitforDataVal(NDS03_Dev_t *pNxDevice, uint8_t flag, int32_t timeout_ms);
/** 等待命令结束 */
NDS03_Error NDS03_WaitforCmdVal(NDS03_Dev_t *pNxDevice, uint8_t cmd, int32_t timeout_ms);
/** 从NDS03中读取直方图数据 */
NDS03_Error NDS03_ReadHgmData(NDS03_Dev_t *pNxDevice,uint8_t *rbuf, uint32_t size);
/** 从NDS03中读取用户数据 */
NDS03_Error NDS03_ReadUserData(NDS03_Dev_t *pNxDevice, uint16_t addr, uint8_t *rbuf, uint32_t size);
/** 从NDS03中写入用户数据 */
NDS03_Error NDS03_WriteUserData(NDS03_Dev_t *pNxDevice,
uint16_t addr, uint8_t *wbuf, uint32_t size);
/** NDS03进入软件睡眠,手动唤醒 */
NDS03_Error NDS03_SoftSleepWithManualWakeup(NDS03_Dev_t *pNxDevice);
/** NDS03进入软件睡眠,自动唤醒 */
NDS03_Error NDS03_SoftSleepWithAutoWakeup(NDS03_Dev_t *pNxDevice, uint16_t sleep_time_ms);
/** NDS03从软件睡眠中唤醒 */
NDS03_Error NDS03_SoftWakeup(NDS03_Dev_t *pNxDevice);
/** NDS03进入睡眠 */
NDS03_Error NDS03_Sleep(NDS03_Dev_t *pNxDevice);
/** NDS03从睡眠中唤醒 */
NDS03_Error NDS03_Wakeup(NDS03_Dev_t *pNxDevice);
/** 判断是否为NDS03设备,返回值为0表示是NDS03设备 */
NDS03_Error NDS03_IsNDS03(NDS03_Dev_t *pNxDevice);
/** 设置模组设备地址 */
NDS03_Error NDS03_SetDevAddr(NDS03_Dev_t *pNxDevice, uint8_t dev_addr);
/** 初始化设备 */
NDS03_Error NDS03_InitDevice(NDS03_Dev_t *pNxDevice);
/** 等待设备启动 */
NDS03_Error NDS03_WaitDeviceBootUp(NDS03_Dev_t *pNxDevice);
/** 脏污预警 */
NDS03_Error NDS03_DirtyWarning(NDS03_Dev_t *pNxDevice,uint8_t *flag,uint32_t time_th);
/** @} NDS03_Dev_Group */
#endif

View File

@@ -0,0 +1,235 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/ktime.h>
#include "nds03_iio.h"
#include "nds03.h"
#define NDS03_DISTANCE_CHANNEL { \
.type = IIO_DISTANCE, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_ENABLE), \
.info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),\
.scan_index = NDS03_IIO_CHAN_DISTANCE, \
.scan_type = { \
.sign = 'u', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
}
static const int nds03_scales[] = { 1 };
static const struct iio_chan_spec nds03_channels[] = {
NDS03_DISTANCE_CHANNEL,
IIO_CHAN_SOFT_TIMESTAMP(NDS03_IIO_CHAN_TIMESTAMP),
};
static int nds03_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
*type = IIO_VAL_INT;
*vals = nds03_scales;
*length = ARRAY_SIZE(nds03_scales);
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
/* === read_raw === */
static int nds03_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct nds03_iio_dev *iio = iio_device_get_drvdata(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->scan_index == NDS03_IIO_CHAN_DISTANCE) {
*val = iio->ctx->g_nds03_device.ranging_data[0].depth;
return IIO_VAL_INT;
}
break;
case IIO_CHAN_INFO_SCALE:
*val = nds03_scales[0];
return IIO_VAL_INT;
case IIO_CHAN_INFO_ENABLE:
*val = iio->enabled;
return IIO_VAL_INT;
default:
return -EINVAL;
}
return -EINVAL;
}
static int nds03_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_RAW:
return -EINVAL;
case IIO_CHAN_INFO_SCALE:
/* NOTE: Default to 1 */
return 0;
case IIO_CHAN_INFO_ENABLE:
return -EINVAL;
default:
return -EINVAL;
}
}
static const struct iio_info nds03_iio_info = {
.read_raw = nds03_read_raw,
.write_raw = nds03_write_raw,
.read_avail= nds03_read_avail,
};
/* === buffer setup === */
static void nds03_enable_work(struct work_struct *work)
{
struct nds03_iio_dev *iio = container_of(work, struct nds03_iio_dev, enable_work);
struct nds03_context *ctx = iio->ctx;
NDS03_Dev_t *dev = &ctx->g_nds03_device;
int ret;
mutex_lock(&ctx->work_mutex);
if (iio->enabled)
goto out;
schedule_delayed_work(&ctx->dwork,
msecs_to_jiffies(atomic_read(&ctx->poll_delay_ms)));
atomic_set(&ctx->is_meas, 1);
ret = nds03_sensor_init(ctx);
if(ret != 0) {
dev_err(&ctx->client->dev, "nds03 sensor init failed %d\n", ret);
goto out;
}
ret = NDS03_StartContinuousMeasurement(dev);
if (ret) {
dev_err(&ctx->client->dev, "StartContinuous fail %d\n", ret);
goto out;
}
iio->enabled = true;
dev_info(&ctx->client->dev, "NDS03 sensor started\n");
goto out;
out:
mutex_unlock(&ctx->work_mutex);
}
static void nds03_disable_work(struct work_struct *work)
{
struct nds03_iio_dev *iio = container_of(work, struct nds03_iio_dev, disable_work);
struct nds03_context *ctx = iio->ctx;
NDS03_Dev_t *dev = &ctx->g_nds03_device;
mutex_lock(&ctx->work_mutex);
if (!iio->enabled)
goto out;
atomic_set(&ctx->is_meas, 0);
NDS03_StopContinuousMeasurement(dev);
iio->enabled = false;
dev_info(&ctx->client->dev, "NDS03 sensor stopped\n");
out:
mutex_unlock(&ctx->work_mutex);
}
static int nds03_buffer_preenable(struct iio_dev *indio_dev)
{
struct nds03_iio_dev *iio = iio_device_get_drvdata(indio_dev);
schedule_work(&iio->enable_work);
return 0;
}
static int nds03_buffer_predisable(struct iio_dev *indio_dev)
{
struct nds03_iio_dev *iio = iio_device_get_drvdata(indio_dev);
schedule_work(&iio->disable_work);
return 0;
}
static const struct iio_buffer_setup_ops nds03_buffer_setup_ops = {
.preenable = nds03_buffer_preenable,
.predisable = nds03_buffer_predisable,
};
int nds03_iio_init(struct nds03_context *ctx)
{
struct device *dev = &ctx->client->dev;
struct nds03_iio_dev *iio;
int ret;
iio = devm_kzalloc(dev, sizeof(*iio), GFP_KERNEL);
if (!iio) {
nds03_errmsg("devm_kzalloc error\n");
return -ENOMEM;
}
iio->indio_dev = devm_iio_device_alloc(dev, 0);
if (!iio->indio_dev) {
nds03_errmsg("devm_iio_device_alloc error\n");
return -ENOMEM;
}
iio->ctx = ctx;
iio->enabled = false;
INIT_WORK(&iio->enable_work, nds03_enable_work);
INIT_WORK(&iio->disable_work, nds03_disable_work);
iio->indio_dev->name = "nds03";
iio->indio_dev->channels = nds03_channels;
iio->indio_dev->num_channels = ARRAY_SIZE(nds03_channels);
iio->indio_dev->info = &nds03_iio_info;
iio->indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
iio_device_set_drvdata(iio->indio_dev, iio);
/* kfifo buffer + setup_ops */
ret = devm_iio_kfifo_buffer_setup(dev, iio->indio_dev, &nds03_buffer_setup_ops);
if (ret)
return ret;
ret = devm_iio_device_register(dev, iio->indio_dev);
if (ret)
return ret;
ctx->iio = iio;
return 0;
}
EXPORT_SYMBOL_GPL(nds03_iio_init);
void nds03_iio_remove(struct nds03_context *ctx)
{
return;
}
EXPORT_SYMBOL_GPL(nds03_iio_remove);
void nds03_iio_push_data(struct nds03_context *ctx, uint16_t distance_mm)
{
struct nds03_iio_dev *iio = ctx->iio;
struct nds03_scan scan;
if (!iio || !iio->enabled)
return;
scan.distance = distance_mm;
if (iio_buffer_enabled(iio->indio_dev))
iio_push_to_buffers_with_timestamp(iio->indio_dev, &scan,
ktime_get_boottime_ns());
}
EXPORT_SYMBOL_GPL(nds03_iio_push_data);

View File

@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __NDS03_IIO_H
#define __NDS03_IIO_H
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
struct nds03_iio_dev {
struct iio_dev *indio_dev;
struct nds03_context *ctx;
bool enabled;
struct work_struct enable_work;
struct work_struct disable_work;
};
enum nds03_iio_chan {
NDS03_IIO_CHAN_DISTANCE,
NDS03_IIO_CHAN_TIMESTAMP,
};
struct nds03_scan {
__le16 distance;
s64 ts;
};
int nds03_iio_init(struct nds03_context *ctx);
void nds03_iio_remove(struct nds03_context *ctx);
void nds03_iio_push_data(struct nds03_context *ctx, uint16_t distance_mm);
#endif

View File

@@ -0,0 +1,674 @@
/*!
@file nds03_module.c
@brief
@author lull
@date 2025-06
@copyright Copyright (c) 2025 Shenzhen Nephotonics Semiconductor Technology Co., Ltd.
@license BSD 3-Clause License
This file is part of the Nephotonics sensor SDK.
It is licensed under the BSD 3-Clause License.
A copy of the license can be found in the project root directory, in the file named LICENSE.
*/
#include "nds03.h"
#include "nds03_iio.h"
/* Set default value to 1 to allow to see module insertion debug messages */
int nds03_enable_debug = 0;
static inline NDS03_Platform_t *to_nds03_platform(struct nds03_context *ctx)
{
return &ctx->g_nds03_device.platform;
}
static inline NDS03_Dev_t * to_nds03_dev(struct nds03_context *ctx)
{
return &ctx->g_nds03_device;
}
int nds03_interrupt_config(NDS03_Dev_t *pNxDevice, uint8_t is_open)
{
int8_t retval = 0;
if (is_open) {
retval |= NDS03_SetGpio1Config(pNxDevice,
NDS03_GPIO1_NEW_MEASURE_READY, NDS03_GPIO1_POLARITY_LOW);
/* 连续模式开启测量 */
retval |= NDS03_StartContinuousMeasurement(pNxDevice);
} else {
/* 连续模式关闭测量 */
retval |= NDS03_StopContinuousMeasurement(pNxDevice);
/* 关闭测距中断 */
retval |= NDS03_SetGpio1Config(pNxDevice,
NDS03_GPIO1_FUNCTIONALITY_OFF, NDS03_GPIO1_POLARITY_LOW);
}
return retval;
}
EXPORT_SYMBOL_GPL(nds03_interrupt_config);
int nds03_sensor_init(struct nds03_context *ctx)
{
NDS03_Dev_t *pNxDevice = to_nds03_dev(ctx);
NDS03_Platform_t *pdev = to_nds03_platform(ctx);
/* 函数指针结构体 */
if (NDS03_ERROR_NONE != nds03_platform_init(pdev, ctx->client)) {
nds03_errmsg("nds03_platform init error\n");
return -1;
}
/* 循环等待设备启动, 若模组或者IIC读写函数有问题则会报错 */
if (NDS03_ERROR_NONE != NDS03_WaitDeviceBootUp(pNxDevice)) {
nds03_errmsg("NDS03_WaitDeviceBootUp error\r\n");
return -1;
}
/** 判断是否为NDS03设备 */
if (NDS03_ERROR_NONE != NDS03_IsNDS03(pNxDevice)) {
nds03_errmsg("The device is not NDS03, please change the device!\n");
return -2;
}
/* 初始化模组设备 */
if (NDS03_ERROR_NONE != NDS03_InitDevice(pNxDevice)) {
nds03_errmsg("NDS03_InitDevice error!!\r\n");
return -3;
}
if (atomic_read(&ctx->meas_mode) == 0)
nds03_interrupt_config(pNxDevice, 1);
else
nds03_interrupt_config(pNxDevice, 0);
return 0;
}
EXPORT_SYMBOL_GPL(nds03_sensor_init);
static void printf_ranging_data(NDS03_Dev_t *pNxDevice)
{
nds03_info("ranging data start:\r\n");
nds03_info("dist start:\r\n");
nds03_info(
"%d %d %d %d\r\n",
pNxDevice->ranging_data[0].depth, pNxDevice->ranging_data[1].depth,
pNxDevice->ranging_data[2].depth, pNxDevice->ranging_data[3].depth);
nds03_info("dist end\r\n");
nds03_info("confi start:\r\n");
nds03_info(
"%d %d %d %d\r\n",
pNxDevice->ranging_data[0].confi, pNxDevice->ranging_data[1].confi,
pNxDevice->ranging_data[2].confi, pNxDevice->ranging_data[3].confi);
nds03_info("confi end\r\n");
nds03_info("count start:\r\n");
nds03_info(
"%d %d %d %d\r\n",
pNxDevice->ranging_data[0].count, pNxDevice->ranging_data[1].count,
pNxDevice->ranging_data[2].count, pNxDevice->ranging_data[3].count);
nds03_info("count end\r\n");
nds03_info("crate start:\r\n");
nds03_info(
"%d %d %d %d\r\n",
pNxDevice->ranging_data[0].crate, pNxDevice->ranging_data[1].crate,
pNxDevice->ranging_data[2].crate, pNxDevice->ranging_data[3].crate);
nds03_info("crate end\r\n");
nds03_info("ranging data end\r\n");
}
static int32_t nds03_make_measure(struct nds03_context *ctx)
{
int32_t ret = 0;
NDS03_Dev_t *pNxDevice = &ctx->g_nds03_device;
mutex_lock(&ctx->work_mutex);
/* 获取测量数据 */
if (atomic_read(&ctx->meas_mode))
ret = NDS03_GetSingleRangingData(pNxDevice);
else
ret = NDS03_GetInterruptRangingData(pNxDevice);
if (ret >= 0 && nds03_enable_debug)
printf_ranging_data(pNxDevice);
mutex_unlock(&ctx->work_mutex);
return ret;
}
static int __ctrl_tof_start(struct nds03_context * ctx)
{
schedule_delayed_work(&ctx->dwork,
msecs_to_jiffies(atomic_read(&ctx->poll_delay_ms)));
atomic_set(&ctx->is_meas, 1);
return 0;
}
static int __ctrl_tof_stop(struct nds03_context * ctx)
{
atomic_set(&ctx->is_meas, 0);
return 0;
}
static int __ctrl_tof_reset(struct nds03_context * ctx)
{
int ret = 0;
__ctrl_tof_stop(ctx);
mutex_lock(&ctx->work_mutex);
ret = nds03_sensor_init(ctx);
if (ret != 0)
nds03_errmsg("nds03 sensor init failed\n");
mutex_unlock(&ctx->work_mutex);
return ret;
}
static int32_t tof_offset_calib(struct nds03_context *ctx, uint16_t calib_dist)
{
int32_t i, cnt = 20;
int32_t depth_sum, depth_aver;
NDS03_Dev_t *pNxDevice = to_nds03_dev(ctx);
/* 将设备处于500mm处 */
NDS03_StopContinuousMeasurement(pNxDevice);
msleep(100);
/* 串扰标定 */
ctx->calib_result = NDS03_XtalkCalibration(pNxDevice);
if (NDS03_ERROR_NONE != ctx->calib_result) {
nds03_info("Xtalk calib error: %d\n", ctx->calib_result);
return -1;
}
/* Offset标定 */
ctx->calib_result = NDS03_OffsetCalibrationAtDepth(pNxDevice, calib_dist);
if(NDS03_ERROR_NONE != ctx->calib_result) {
nds03_info("Offset calib error: %d\n", ctx->calib_result);
return -1;
}
/* 获取平均值 */
NDS03_GetSingleRangingData(pNxDevice);
NDS03_GetSingleRangingData(pNxDevice);
depth_sum = 0;
for (i = 0; i < cnt; i++) {
if (NDS03_ERROR_NONE != NDS03_GetSingleRangingData(pNxDevice)) {
nds03_info("NDS03_GetSingleRangingData error!!\r\n");
return -1;
}
depth_sum = depth_sum + (int32_t)(uint32_t)pNxDevice->ranging_data[0].depth;
}
depth_aver = depth_sum / cnt;
if (depth_aver < (calib_dist - 20) || depth_aver > (calib_dist + 20)) {
ctx->calib_result = NDS03_ERROR_RANGING;
nds03_info("NDS03 calibration fail!!\r\n");
return -1;
}
nds03_info("NDS03 calibration success\r\n");
return 0;
}
static void report_meas_event(struct nds03_context * ctx)
{
int retval = 0;
retval = nds03_make_measure(ctx);
if (retval < 0)
return;
uint16_t distance = ctx->g_nds03_device.ranging_data[0].depth;
nds03_iio_push_data(ctx, distance);
}
static irqreturn_t tof_irq_handler_i2c(int vec, void *info)
{
struct nds03_context *ctx = (struct nds03_context *)info;
bool is_meas = atomic_read(&ctx->is_meas);
if (ctx->irq == vec && is_meas)
schedule_work(&ctx->irq_work);
return IRQ_HANDLED;
}
static void nds03_work_handler(struct work_struct *work)
{
struct nds03_context *ctx = container_of(work, struct nds03_context, dwork.work);
if (atomic_read(&ctx->meas_mode) && atomic_read(&ctx->is_meas)) {
nds03_make_measure(ctx);
schedule_delayed_work(&ctx->dwork,
msecs_to_jiffies(atomic_read(&ctx->poll_delay_ms)));
report_meas_event(ctx);
}
}
static void nds03_measure_irq_work(struct work_struct *work)
{
struct nds03_context *ctx = container_of(work, struct nds03_context, irq_work);
report_meas_event(ctx);
}
static ssize_t nds03_show_is_meas(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nds03_context *ctx = dev_get_drvdata(dev);
return snprintf(buf, 10, "%d\n", atomic_read(&ctx->is_meas));
}
static ssize_t nds03_store_is_meas(struct device *dev,
struct device_attribute *attr, const char *buf,size_t count)
{
struct nds03_context *ctx = dev_get_drvdata(dev);
int ret = count;
switch (buf[0]) {
case '0':
__ctrl_tof_stop(ctx);
break;
case '1':
__ctrl_tof_start(ctx);
break;
default:
nds03_warnmsg("Invalid value\n");
ret = -EINVAL;
break;
}
return ret ;
}
static DEVICE_ATTR(is_meas, 0660, nds03_show_is_meas, nds03_store_is_meas);
static ssize_t nds03_show_poll_delay_ms(struct device *dev,
struct device_attribute *attr, char *buf)
{
uint32_t poll_ms;
struct nds03_context *ctx = dev_get_drvdata(dev);
poll_ms = atomic_read(&ctx->poll_delay_ms);
return snprintf(buf, 10, "%d\n", poll_ms);
}
static ssize_t nds03_store_poll_delay_ms(struct device *dev,
struct device_attribute *attr, const char *buf,size_t count)
{
struct nds03_context *ctx = dev_get_drvdata(dev);
int ret;
unsigned long delay_ms;
ret = kstrtoul(buf, 10, &delay_ms);
if(ret < 0) {
nds03_warnmsg("Invalid input ctx\n");
goto store_err;
}
atomic_set(&ctx->poll_delay_ms, delay_ms);
nds03_dbgmsg("Poll delay %lu ms\n", delay_ms);
store_err:
return ret < 0 ? -EINVAL : count;
}
static DEVICE_ATTR(meas_delay_ms, 0660, nds03_show_poll_delay_ms, nds03_store_poll_delay_ms);
static ssize_t nds03_show_meas_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nds03_context *ctx = dev_get_drvdata(dev);
return snprintf(buf, 10, "%d\n",atomic_read(&ctx->meas_mode));
}
static ssize_t nds03_store_meas_mode(struct device *dev,
struct device_attribute *attr, const char *buf,size_t count)
{
struct nds03_context *ctx = dev_get_drvdata(dev);
NDS03_Dev_t *pNxDevice = to_nds03_dev(ctx);
int ret = count;
mutex_lock(&ctx->work_mutex);
switch (buf[0]) {
case '0':
if(ctx->irq < 0){
nds03_warnmsg("No support Interrupt\n");
ret = -EINVAL;
} else {
atomic_set(&ctx->meas_mode, 0);
nds03_interrupt_config(pNxDevice, 1);
nds03_dbgmsg("Enter Interrupt Mode\n");
}
break;
case '1':
atomic_set(&ctx->meas_mode, 1);
nds03_interrupt_config(pNxDevice, 0);
nds03_dbgmsg("Enter Poll Mode\n");
break;
default:
nds03_warnmsg("Invalid value\n");
ret = -EINVAL;
break;
}
mutex_unlock(&ctx->work_mutex);
return ret ;
}
static DEVICE_ATTR(meas_mode, 0660, nds03_show_meas_mode, nds03_store_meas_mode);
static ssize_t nds03_store_tof_reset(struct device *dev,
struct device_attribute *attr, const char *buf,size_t count)
{
struct nds03_context *ctx = dev_get_drvdata(dev);
int ret = 0;
if(buf[0] == '1')
ret = __ctrl_tof_reset(ctx);
return ret < 0 ? ret : count;
}
static DEVICE_ATTR(tof_reset, 0660, NULL, nds03_store_tof_reset);
static ssize_t nds03_show_tof_calib(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nds03_context *ctx = dev_get_drvdata(dev);
return snprintf(buf, 10, "%d\n",ctx->calib_result);
}
static ssize_t nds03_store_tof_calib(struct device *dev,
struct device_attribute *attr, const char *buf,size_t count)
{
struct nds03_context *ctx = dev_get_drvdata(dev);
int ret;
unsigned long calib_dis;
mutex_lock(&ctx->work_mutex);
ret = kstrtoul(buf, 10, &calib_dis);
if(ret < 0) {
nds03_warnmsg("Invalid input ctx\n");
ret = -EPERM;
goto store_err;
}
nds03_info("offset calib distance: %ld mm\n", calib_dis);
ret = tof_offset_calib(ctx, calib_dis);
if (nds03_sensor_init(ctx) != 0)
nds03_errmsg("nds03 sensor init failed\n");
store_err:
mutex_unlock(&ctx->work_mutex);
return ret < 0 ? ret : count;
}
static DEVICE_ATTR(tof_calib, 0660, nds03_show_tof_calib, nds03_store_tof_calib);
static ssize_t nds03_show_enable_debug(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, 10, "%d\n", nds03_enable_debug);
}
static ssize_t nds03_store_enable_debug(struct device *dev,
struct device_attribute *attr, const char *buf,size_t count)
{
struct nds03_context *ctx = dev_get_drvdata(dev);
int32_t ret = 0;
mutex_lock(&ctx->work_mutex);
switch (buf[0]) {
case '0':
nds03_enable_debug = 0;
nds03_info("close nds03 debug\n");
break;
case '1':
nds03_enable_debug = 1;
nds03_info("open nds03 debug\n");
break;
default:
nds03_warnmsg("Invalid value\n");
ret = -EINVAL;
break;
}
mutex_unlock(&ctx->work_mutex);
return ret < 0 ? ret : count;
}
static DEVICE_ATTR(enable_debug, 0660, nds03_show_enable_debug, nds03_store_enable_debug);
static ssize_t nds03_store_tof_pulsenum(struct device *dev,
struct device_attribute *attr, const char *buf,size_t count)
{
int ret;
unsigned long pulsenum;
struct nds03_context *ctx = dev_get_drvdata(dev);
NDS03_Dev_t *pdev = to_nds03_dev(ctx);
ret = kstrtoul(buf, 10, &pulsenum);
if (ret < 0) {
nds03_errmsg("Invalid input ctx\n");
return -EPERM;
}
mutex_lock(&ctx->work_mutex);
ret = NDS03_SetPulseNum(pdev, pulsenum);
mutex_unlock(&ctx->work_mutex);
nds03_info("set tof pulsenum: %ld, ret: %d\n", pulsenum, ret);
return ret < 0 ? ret : count;
}
static DEVICE_ATTR(tof_pulsenum, 0660, NULL, nds03_store_tof_pulsenum);
static ssize_t nds03_show_tof_xtalk(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret, count;
uint16_t xtalk_value;
struct nds03_context *ctx = dev_get_drvdata(dev);
NDS03_Dev_t *pdev = to_nds03_dev(ctx);
mutex_lock(&ctx->work_mutex);
ret = NDS03_GetXTalkValue(pdev, &xtalk_value);
if (ret < 0)
nds03_errmsg("get tof xtalk_value error\n");
mutex_unlock(&ctx->work_mutex);
count = snprintf(buf, 10, "%d\n",xtalk_value);
return ret < 0 ? ret : count;
}
static DEVICE_ATTR(tof_xtalk, 0660, nds03_show_tof_xtalk, NULL);
static ssize_t nds03_store_xtalk_calibration(struct device *dev,
struct device_attribute *attr, const char *buf,size_t count)
{
int32_t ret = 0;
struct nds03_context *ctx = dev_get_drvdata(dev);
NDS03_Dev_t *pdev = to_nds03_dev(ctx);
if (sysfs_streq(buf, "1") || sysfs_streq(buf, "on") ||
sysfs_streq(buf, "start")) {
mutex_lock(&ctx->work_mutex);
NDS03_StopContinuousMeasurement(pdev);
ret = NDS03_XtalkCalibration(pdev);
if (nds03_sensor_init(ctx) != 0)
nds03_errmsg("nds03 sensor init failed\n");
mutex_unlock(&ctx->work_mutex);
if(ret < 0) {
nds03_errmsg("NDS03_XtalkCalibration error, ret = %d\n", ret);
return -EIO;
}
return count;
} else {
return -EINVAL;
}
}
static DEVICE_ATTR(xtalk_calibration, 0660, NULL, nds03_store_xtalk_calibration);
static ssize_t nds03_store_offset_calibration(struct device *dev,
struct device_attribute *attr, const char *buf,size_t count)
{
int ret;
struct nds03_context *ctx = dev_get_drvdata(dev);
NDS03_Dev_t *pdev = to_nds03_dev(ctx);
int32_t calib_depth_mm_tmp = 0;
ret = kstrtoint(buf, 10, &calib_depth_mm_tmp);
if (ret)
return -EINVAL;
mutex_lock(&ctx->work_mutex);
NDS03_StopContinuousMeasurement(pdev);
ret = NDS03_OffsetCalibrationAtDepth(pdev, calib_depth_mm_tmp);
if (nds03_sensor_init(ctx) != 0)
nds03_errmsg("nds03 sensor init failed\n");
mutex_unlock(&ctx->work_mutex);
if (ret < 0) {
nds03_errmsg("NDS03_OffsetCalibrationAtDepth error, ret = %d\n", ret);
return -EIO;
}
return count;
}
static DEVICE_ATTR(offset_calibration, 0660, NULL, nds03_store_offset_calibration);
static struct attribute *nds03_attributes[] = {
&dev_attr_is_meas.attr,
&dev_attr_meas_delay_ms.attr,
&dev_attr_meas_mode.attr,
&dev_attr_enable_debug.attr,
&dev_attr_tof_reset.attr,
&dev_attr_tof_calib.attr,
&dev_attr_tof_pulsenum.attr,
&dev_attr_tof_xtalk.attr,
&dev_attr_xtalk_calibration.attr,
&dev_attr_offset_calibration.attr,
NULL
};
static const struct attribute_group nds03_sysfs_groups = {
.attrs = nds03_attributes,
};
static void nds03_parse_device_tree(struct nds03_context *ctx)
{
int32_t ret;
NDS03_Platform_t *pdev = to_nds03_platform(ctx);
/* Initialize xshut gpio */
pdev->xshut_gpio = devm_gpiod_get_optional(&ctx->client->dev, "xshut", GPIOD_OUT_HIGH);
if (IS_ERR(pdev->xshut_gpio)) {
ret = PTR_ERR(pdev->xshut_gpio);
nds03_warnmsg( "no xshut pin available, error = %d\n", ret);
return;
} else {
nds03_dbgmsg("get xshut pin success\n");
}
/* Initialize irq gpio*/
atomic_set(&ctx->meas_mode, 1); //Default to polling mode
ctx->irq = -1;
pdev->intr_gpio = devm_gpiod_get_optional(&ctx->client->dev, "intr", GPIOD_IN);
if (IS_ERR(pdev->intr_gpio)) {
ret = PTR_ERR(pdev->intr_gpio);
nds03_warnmsg( "no intr pin available, error = %d\n", ret);
return;
}
nds03_dbgmsg("get intr pin success\n");
ctx->irq = ctx->client->irq;
if (ctx->irq) {
unsigned long default_trigger = irqd_get_trigger_type(irq_get_irq_data(ctx->irq));
ret = devm_request_threaded_irq(&ctx->client->dev,
ctx->irq, NULL,
tof_irq_handler_i2c,
default_trigger|IRQF_ONESHOT,
"nds03_interrupt",
(void *)ctx);
if (ret) {
nds03_errmsg("fail to req threaded irq rc=%d\n", ret);
} else {
nds03_info(
"request irq success, irq mode use, irq num: %d, type: %lu",
ctx->irq, default_trigger);
atomic_set(&ctx->meas_mode, 0); //Configure in interrupt mode
}
} else {
void *poll_interval_dt = NULL;
uint32_t delay_ms;
nds03_info("no irq number specified, polling mode is used\n");
poll_interval_dt = (void *)of_get_property(ctx->client->dev.of_node,
"nds03_poll_interval",
NULL);
delay_ms = poll_interval_dt ? be32_to_cpup(poll_interval_dt) : 0;
nds03_dbgmsg("poll delay ms:%d\n", delay_ms);
atomic_set(&ctx->poll_delay_ms, delay_ms);
}
}
int nds03_common_probe(struct nds03_context * ctx)
{
int ret = 0;
u32 i2c_freq = 0;
// struct input_dev *input;
/* Initialize mutex */
mutex_init(&ctx->work_mutex);
/* init work handler */
INIT_DELAYED_WORK(&ctx->dwork, nds03_work_handler);
INIT_WORK(&ctx->irq_work, nds03_measure_irq_work);
atomic_set(&ctx->is_meas, 0);
/* Parse the device tree for NDS03 sensor configuration. */
nds03_parse_device_tree(ctx);
of_property_read_u32(ctx->client->adapter->dev.of_node, "clock-frequency", &i2c_freq);
pr_info("I2C bus number is %d, bus speed: %u Hz\n", ctx->client->adapter->nr, i2c_freq);
ret = nds03_iio_init(ctx);
if (ret) {
nds03_errmsg("IIO init failed: %d\n", ret);
goto err_iio;
}
ctx->fd_open_count = 0;
nds03_dbgmsg("create sysfs group successfully\n");
/* Initialize nds03 sensor */
ret = nds03_sensor_init(ctx);
if (ret != 0 ) {
nds03_errmsg("Failed to init nds03 sensor error:%d\n", ret);
goto err_iio;
}
nds03_dbgmsg("init nds03 sensor successfully\n");
ret = sysfs_create_group(&ctx->client->dev.kobj, &nds03_sysfs_groups);
if (ret) {
nds03_errmsg("Failed to create sysfs group error:%d\n", ret);
goto exit_err;
}
ctx->remove_flag = false;
nds03_dbgmsg("register chardev successfully\n");
nds03_info("nds03 module registered successfully\n");
nds03_info( "NDS03 Driver version: %s \n", DRIVER_VERSION);
return 0;
err_iio:
nds03_iio_remove(ctx);
exit_err:
return ret;
}
EXPORT_SYMBOL_GPL(nds03_common_probe);
int nds03_common_remove(struct nds03_context * ctx)
{
nds03_dbgmsg("Enter %s \n", __FUNCTION__);
cancel_delayed_work(&ctx->dwork);
ctx->remove_flag = true;
sysfs_remove_group(&ctx->client->dev.kobj, &nds03_sysfs_groups);
nds03_iio_remove(ctx);
return 0;
}
EXPORT_SYMBOL_GPL(nds03_common_remove);

View File

@@ -0,0 +1,99 @@
/*!
@file nds03_module_i2c.c
@brief
@author lull
@date 2025-06
@copyright Copyright (c) 2025 Shenzhen Nephotonics Semiconductor Technology Co., Ltd.
@license BSD 3-Clause License
This file is part of the Nephotonics sensor SDK.
It is licensed under the BSD 3-Clause License.
A copy of the license can be found in the project root directory, in the file named LICENSE.
*/
#include "nds03.h"
#include "nds03_platform.h"
struct nds03_context *nds03_context_obj = NULL;
// int nds03_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
static int nds03_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
struct device *dev = &client->dev;
struct nds03_context * ctx = NULL;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if(IS_ERR(ctx)) {
nds03_errmsg("nds03_data memory allocation failed\n");
ret = -ENOMEM;
goto module_reg_err;
}
nds03_context_obj = ctx;
ctx->client = client;
ret = nds03_common_probe(ctx);
if(ret != 0) {
nds03_errmsg("Failed to register nds03 module: %d\n", ret);
goto module_reg_err;
}
i2c_set_clientdata(client, ctx);
return 0;
module_reg_err:
return ret;
}
static void nds03_i2c_remove(struct i2c_client *client)
{
struct nds03_context * ctx = i2c_get_clientdata(client);
nds03_common_remove(ctx);
// return 0;
}
static const struct i2c_device_id tof_nds03_id[] = {
{TOF_NDS03_DRV_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tof_nds03_id);
static const struct of_device_id tof_nds03_dt_match[] = {
{ .compatible = "nx,"TOF_NDS03_DRV_NAME, },
{ }
};
MODULE_DEVICE_TABLE(of, tof_nds03_dt_match);
static struct i2c_driver tof_nds03_driver = {
.driver = {
.name = TOF_NDS03_DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = tof_nds03_dt_match,
},
.probe = nds03_i2c_probe,
.remove = nds03_i2c_remove,
.id_table = tof_nds03_id,
};
static int __init tof_nds03_init_i2c(void)
{
int ret = 0;
nds03_dbgmsg("Enter\n");
ret = i2c_add_driver(&tof_nds03_driver);
if (ret)
nds03_errmsg("%d erro ret:%d\n", __LINE__, ret);
return ret;
}
static void __exit tof_nds03_exit_i2c(void )
{
nds03_dbgmsg("Exit\n");
i2c_del_driver(&tof_nds03_driver);
}
module_init(tof_nds03_init_i2c);
module_exit(tof_nds03_exit_i2c);
MODULE_DESCRIPTION("Time-of-Flight sensor driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);

View File

@@ -0,0 +1,117 @@
/*!
@file nds03_platform.c
@brief
@author lull
@date 2025-06
@copyright Copyright (c) 2025 Shenzhen Nephotonics Semiconductor Technology Co., Ltd.
@license BSD 3-Clause License
This file is part of the Nephotonics sensor SDK.
It is licensed under the BSD 3-Clause License.
A copy of the license can be found in the project root directory, in the file named LICENSE.
*/
#include "nds03_platform.h"
int8_t nds03_platform_init(NDS03_Platform_t *pdev, void * client)
{
pdev->client = (struct i2c_client *)client;
pdev->i2c_dev_addr = 0X5C;
return 0;
}
int8_t nds03_platform_uninit(NDS03_Platform_t *pdev, void * param)
{
return 0;
}
int8_t nds03_i2c_read_nbytes(NDS03_Platform_t *pDev, uint8_t i2c_raddr, uint8_t *i2c_rdata, uint16_t len)
{
int8_t ret;
struct i2c_msg i2c_message[2];
uint8_t i2c_buf[256];
i2c_buf[0] = i2c_raddr;
i2c_message[0].addr = pDev->i2c_dev_addr;
i2c_message[0].buf = i2c_buf;
i2c_message[0].len = 1;
i2c_message[0].flags = 0 ; //| I2C_M_STOP;//I2C_M_STOP;
i2c_message[1].addr = pDev->i2c_dev_addr;
i2c_message[1].buf = i2c_rdata;
i2c_message[1].len = len;
i2c_message[1].flags = I2C_M_RD;
ret = i2c_transfer(pDev->client->adapter, i2c_message, 2);
if (ret != 2) {
pr_err("NDS03: i2c read error, i2c_raddr: 0x%x, reg: 0x%x, ret: %d", pDev->i2c_dev_addr, i2c_raddr, ret);
return -EIO;
}
return 0;
}
int8_t nds03_i2c_write_nbytes(NDS03_Platform_t *pDev, uint8_t i2c_waddr, uint8_t *i2c_wdata, uint16_t len)
{
int32_t ret;
struct i2c_msg i2c_message;
//For user implement
uint8_t i2c_buf[256];
memcpy(&i2c_buf[1], i2c_wdata, len);
i2c_buf[0] = i2c_waddr;
i2c_message.addr = pDev->i2c_dev_addr;
i2c_message.buf = i2c_buf;
i2c_message.len = len + 1;
i2c_message.flags = 0;
ret = i2c_transfer(pDev->client->adapter, &i2c_message, 1);
if (ret != 1) {
pr_err("NDS03: i2c write error, i2c_waddr: 0x%x, reg: 0x%x, ret: %d", pDev->i2c_dev_addr, i2c_waddr, ret);
return -EIO;
}
return 0;
}
int8_t nds03_delay_10us(NDS03_Platform_t *pDev, uint32_t wait_10us)
{
wait_10us *= 10;
if (wait_10us < 10)
udelay(wait_10us);
else if (wait_10us < 20000)
usleep_range(wait_10us, wait_10us + 1);
else
msleep(wait_10us / 1000);
return 0;
}
int8_t nds03_delay_1ms(NDS03_Platform_t *pDev, uint32_t wait_ms)
{
nds03_delay_10us(pDev, wait_ms * 100);
return 0;
}
int8_t nds03_set_xshut_pin_level(NDS03_Platform_t *pDev, int8_t level)
{
if (pDev->xshut_gpio == NULL) {
pr_err("xshut_gpiod is not init , not setting xshut\n");
return -1;
}
gpiod_set_value(pDev->xshut_gpio, level);
// pr_info("xshut set to %d\n", level);
return 0;
}
int8_t nds03_i2c_get_clock_frequency(NDS03_Platform_t *pDev, uint32_t *clock_frequency)
{
return 0;
}
int8_t nds03_i2c_set_clock_frequency(NDS03_Platform_t *pDev, uint32_t clock_frequency)
{
return 0;
}
int8_t nds03_get_system_clk_ms(NDS03_Platform_t *pDev, int32_t *time_ms)
{
return 0;
}

View File

@@ -0,0 +1,137 @@
/*!
@file nds03_platform.h
@brief
@author lull
@date 2025-06
@copyright Copyright (c) 2025 Shenzhen Nephotonics Semiconductor Technology Co., Ltd.
@license BSD 3-Clause License
This file is part of the Nephotonics sensor SDK.
It is licensed under the BSD 3-Clause License.
A copy of the license can be found in the project root directory, in the file named LICENSE.
*/
#ifndef __NDS03_PLATFORM__H__
#define __NDS03_PLATFORM__H__
#include "nds03_stdint.h"
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/gpio.h>
/**
* @struct NDS03_Platform_t
*
* @brief NDS03平台相关定义 \n
* i2c地址等i2c地址
*/
typedef struct{
//文件描述符
int fd;
/** 用户不可更改以下变量 @{ */
uint8_t i2c_dev_addr; // i2c设备地址
/** @} */
/** 用户可根据需要添加变量 */
struct i2c_client *client;
////// /* nds03 device io */
struct gpio_desc *intr_gpio;
/*!< xsdn reset (low active) gpio number to device */
struct gpio_desc *xshut_gpio;
} NDS03_Platform_t;
/**
* @brief NDS03平台初始化
*
* @param pDev
* @param void*
* @return int8_t
* @retval 0:, :
*/
int8_t nds03_platform_init(NDS03_Platform_t *pdev, void *);
/**
* @brief NDS03平台释放
*
* @param pDev
* @param void*
* @return int8_t
* @retval 0:, :
*/
int8_t nds03_platform_uninit(NDS03_Platform_t *pdev, void *);
/**
* @brief I2C读一个字节
*
* @param pDev
* @param i2c_raddr
* @param i2c_rdata
* @return int8_t
* @retval 0:, :
*/
int8_t nds03_i2c_read_nbytes(NDS03_Platform_t *pDev, uint8_t i2c_raddr, uint8_t *i2c_rdata, uint16_t len);
/**
* @brief I2C写一个字节
*
* @param pDev
* @param i2c_waddr
* @param i2c_wdata
* @return int8_t
* @retval 0:, :
*/
int8_t nds03_i2c_write_nbytes(NDS03_Platform_t *pDev, uint8_t i2c_waddr, uint8_t *i2c_wdata, uint16_t len);
/**
* @brief wait_ms毫秒
*
* @param pDev
* @param wait_ms
* @return int8_t
* @retval 0:, :
*/
int8_t nds03_delay_1ms(NDS03_Platform_t *pDev, uint32_t wait_ms);
/**
* @brief 10*wait_10us微秒
*
* @param pDev
* @param wait_10us
* @return int8_t
* @retval 0:, :
*/
int8_t nds03_delay_10us(NDS03_Platform_t *pDev, uint32_t wait_10us);
/**
* @brief nds03 xshut引脚电平
*
* @param pDev
* @param level 01
* @return int8_t
* @retval 0:, :
*/
int8_t nds03_set_xshut_pin_level(NDS03_Platform_t *pDev, int8_t level);
/*!
@brief NDS03获取当前i2c时钟频率
@param pDev
@param clock_frequency
@return int8_t
*/
int8_t nds03_i2c_get_clock_frequency(NDS03_Platform_t *pDev, uint32_t *clock_frequency);
/*!
@brief NDS03设置当前i2c时钟频率
@param pDev
@param clock_frequency
@return int8_t
*/
int8_t nds03_i2c_set_clock_frequency(NDS03_Platform_t *pDev, uint32_t clock_frequency);
/*!
@brief ms
@param pDev
@param time_ms
@return int8_t
*/
int8_t nds03_get_system_clk_ms(NDS03_Platform_t *pDev, int32_t *time_ms);
#endif

View File

@@ -0,0 +1,95 @@
/**
* @file nds03_stdint.h
* @author tongsheng.tang
* @brief NDS03 platform-specific integer type definitions
* @version 2.x.x
* @date 2025-06
*
* @copyright Copyright (c) 2025, Nephotonics Information Technology (Hefei) Co., Ltd.
*
* @license BSD 3-Clause License
* This file is part of the NDS03 SDK and is licensed under the BSD 3-Clause License.
* You may obtain a copy of the license in the project root directory, in the file named LICENSE.
*
*/
#ifndef __NDS03_STDINT_H__
#define __NDS03_STDINT_H__
#define PLATFORM_C51 0
#define PLATFORM_NOT_C51 1
#define PLATFORM_LINUX_DRIVER 2
#ifndef NDS03_PLATFORM
#define NDS03_PLATFORM PLATFORM_LINUX_DRIVER
#endif
#if NDS03_PLATFORM == PLATFORM_NOT_C51
#include <stdint.h>
#elif NDS03_PLATFORM == PLATFORM_C51
/* exact-width signed integer types */
typedef signed char int8_t;
typedef signed int int16_t;
typedef signed long int32_t;
/* exact-width unsigned integer types */
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef unsigned long uint32_t;
#define __func__ __FILE__
/* minimum values of exact-width signed integer types */
#define INT32_MIN (~0x7fffffff) /* -2147483648 is unsigned */
/* maximum values of exact-width signed integer types */
#define INT32_MAX 2147483647
/* maximum values of exact-width unsigned integer types */
#define UINT32_MAX 4294967295u
/* 7.18.2.2 */
/* minimum values of minimum-width signed integer types */
#define INT_LEAST32_MIN (~0x7fffffff)
/* maximum values of minimum-width signed integer types */
#define INT_LEAST32_MAX 2147483647
/* maximum values of minimum-width unsigned integer types */
#define UINT_LEAST32_MAX 4294967295u
/* 7.18.2.3 */
/* minimum values of fastest minimum-width signed integer types */
#define INT_FAST32_MIN (~0x7fffffff)
/* maximum values of fastest minimum-width signed integer types */
#define INT_FAST32_MAX 2147483647
/* maximum values of fastest minimum-width unsigned integer types */
#define UINT_FAST32_MAX 4294967295u
#elif NDS03_PLATFORM == PLATFORM_LINUX_DRIVER
/* exact-width signed integer types */
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
/* exact-width unsigned integer types */
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
#define __func__ __FILE__
/* minimum values of exact-width signed integer types */
#define INT32_MIN (~0x7fffffff) /* -2147483648 is unsigned */
/* maximum values of exact-width signed integer types */
#define INT32_MAX 2147483647
/* maximum values of exact-width unsigned integer types */
#define UINT32_MAX 4294967295u
#endif
#endif /** __NDS03_STDINT_H__ */

View File

@@ -12,6 +12,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/string_helpers.h>
#include <soc/rockchip/rockchip-mailbox.h>
#define MAILBOX_A2B_INTEN 0x00
@@ -28,11 +29,16 @@
#define MAILBOX_V2_A2B_STATUS MAILBOX_A2B_STATUS
#define MAILBOX_V2_A2B_CMD 0x08
#define MAILBOX_V2_A2B_DAT 0x0c
#define MAILBOX_V2_A2B_LOCK 0x20
#define MAILBOX_V2_B2A_INTEN 0x10
#define MAILBOX_V2_B2A_STATUS 0x14
#define MAILBOX_V2_B2A_CMD 0x18
#define MAILBOX_V2_B2A_DAT 0x1c
#define MAILBOX_V2_B2A_LOCK 0x30
#define MAILBOX_V2_VERSION 0x40
#define MAILBOX_V2_VERSION_MASK GENMASK(31, 16)
#define MAILBOX_V2_TRIGGER_SHIFT 8
#define MAILBOX_V2_TRIGGER_MASK BIT(8)
@@ -45,6 +51,11 @@
#define MAILBOX_V2_STATUS_RX_DONE BIT(1)
#define MAILBOX_V2_STATUS_MASK GENMASK(1, 0)
#define MAILBOX_VERSION_1_0_0 0x100U
#define MAILBOX_VERSION_2_0_0 0x200U
#define MAILBOX_VERSION_2_1_0 0x210U
#define MAILBOX_VERSION_2_2_0 0x220U
#define MAILBOX_POLLING_MS 5 /* default polling interval 5ms */
#define BIT_WRITEABLE_SHIFT 16
@@ -53,14 +64,17 @@ struct rockchip_mbox_reg {
u32 tx_sts;
u32 tx_cmd;
u32 tx_dat;
u32 tx_lock;
u32 rx_int;
u32 rx_sts;
u32 rx_cmd;
u32 rx_dat;
u32 rx_lock;
};
struct rockchip_mbox_data {
int num_chans;
u32 version;
struct rockchip_mbox_reg reg_a2b;
struct rockchip_mbox_reg reg_b2a;
const struct mbox_chan_ops *ops;
@@ -78,6 +92,7 @@ struct rockchip_mbox {
void __iomem *mbox_base;
spinlock_t cfg_lock; /* Serialise access to the register */
unsigned char trigger_method; /* 0 = write cmd, 1 = write cmd first, then write data */
u32 version;
struct rockchip_mbox_msg *msg;
const struct rockchip_mbox_reg *reg;
@@ -181,17 +196,27 @@ static irqreturn_t rockchip_mbox_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
static bool rockchip_mbox_v2_is_busy(struct rockchip_mbox *mb)
{
u32 status;
if (mb->version < MAILBOX_VERSION_2_2_0)
status = readl_relaxed(mb->mbox_base + mb->reg->tx_sts) & MAILBOX_V2_STATUS_TX_DONE;
else
status = readl_relaxed(mb->mbox_base + mb->reg->tx_lock);
return !!status;
}
static int rockchip_mbox_v2_send_data(struct mbox_chan *chan, void *data)
{
struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev);
struct rockchip_mbox_msg *msg = data;
u32 status;
if (!msg)
return -EINVAL;
status = readl_relaxed(mb->mbox_base + mb->reg->tx_sts);
if (status & MAILBOX_V2_STATUS_TX_DONE) {
if (rockchip_mbox_v2_is_busy(mb)) {
dev_err(mb->mbox.dev, "The mailbox is busy\n");
return -EBUSY;
}
@@ -325,20 +350,26 @@ EXPORT_SYMBOL_GPL(rockchip_mbox_read_msg);
static const struct rockchip_mbox_data rk3368_drv_data = {
.num_chans = 4,
.version = MAILBOX_VERSION_1_0_0,
.ops = &rockchip_mbox_chan_ops,
.irq_func = rockchip_mbox_irq,
};
static const struct rockchip_mbox_data rk3576_drv_data = {
.num_chans = 1,
.version = MAILBOX_VERSION_2_0_0,
.reg_a2b = { MAILBOX_V2_A2B_INTEN, MAILBOX_V2_A2B_STATUS,
MAILBOX_V2_A2B_CMD, MAILBOX_V2_A2B_DAT,
MAILBOX_V2_A2B_LOCK,
MAILBOX_V2_B2A_INTEN, MAILBOX_V2_B2A_STATUS,
MAILBOX_V2_B2A_CMD, MAILBOX_V2_B2A_DAT },
MAILBOX_V2_B2A_CMD, MAILBOX_V2_B2A_DAT,
MAILBOX_V2_B2A_LOCK },
.reg_b2a = { MAILBOX_V2_B2A_INTEN, MAILBOX_V2_B2A_STATUS,
MAILBOX_V2_B2A_CMD, MAILBOX_V2_B2A_DAT,
MAILBOX_V2_B2A_LOCK,
MAILBOX_V2_A2B_INTEN, MAILBOX_V2_A2B_STATUS,
MAILBOX_V2_A2B_CMD, MAILBOX_V2_A2B_DAT },
MAILBOX_V2_A2B_CMD, MAILBOX_V2_A2B_DAT,
MAILBOX_V2_A2B_LOCK },
.ops = &rockchip_mbox_v2_chan_ops,
.irq_func = rockchip_mbox_v2_irq,
};
@@ -350,6 +381,69 @@ static const struct of_device_id rockchip_mbox_of_match[] = {
};
MODULE_DEVICE_TABLE(of, rockchip_mbox_of_match);
static void rockchip_mbox_get_properties(struct rockchip_mbox *mb,
const struct rockchip_mbox_data *drv_data)
{
struct device *dev = mb->mbox.dev;
u32 txpoll_period;
int ret;
if (device_property_present(dev, "rockchip,tx-direction-b2a"))
mb->reg = &drv_data->reg_b2a;
else
mb->reg = &drv_data->reg_a2b;
/*
* rockchip,txdone-ack: the mailbox client uses its own ACK to check
* TX_DONE, and call mbox_client_txdone() API to schedule tx_tick.
* rockchip,txdone-irq: the feature only support from RK3506, the ISR
* function call mbox_chan_txdone() API to schedule tx_tick.
* txdone_poll is default for all the platform, it cooperates with
* "rockchip,txpoll-period-ms" or "rockchip,txpoll-period-us"
* periodically call last_tx_done() to check TX_DONE by the hrtimer
* in mailbox framework.
*/
if (device_property_present(dev, "rockchip,txdone-ack")) {
mb->mbox.txdone_irq = false;
mb->mbox.txdone_poll = false;
} else if (device_property_present(dev, "rockchip,txdone-irq")) {
mb->mbox.txdone_irq = true;
} else {
mb->mbox.txdone_poll = true;
if (IS_REACHABLE(CONFIG_MAILBOX_POLL_PERIOD_US)) {
ret = device_property_read_u32(dev,
"rockchip,txpoll-period-us",
&txpoll_period);
if (!ret) {
mb->mbox.txpoll_period = txpoll_period;
} else {
ret = device_property_read_u32(dev,
"rockchip,txpoll-period-ms",
&txpoll_period);
mb->mbox.txpoll_period = !ret ? txpoll_period : MAILBOX_POLLING_MS;
mb->mbox.txpoll_period *= 1000U; /* Convert to us */
}
} else {
ret = device_property_read_u32(dev,
"rockchip,txpoll-period-ms",
&txpoll_period);
mb->mbox.txpoll_period = !ret ? txpoll_period : MAILBOX_POLLING_MS;
}
}
if (device_property_present(dev, "rockchip,enable-cmd-trigger"))
mb->trigger_method = 0;
else
mb->trigger_method = 1;
if (drv_data->version != MAILBOX_VERSION_1_0_0)
mb->version = FIELD_GET(MAILBOX_V2_VERSION_MASK,
readl_relaxed(mb->mbox_base + MAILBOX_V2_VERSION));
if (!mb->version)
mb->version = drv_data->version;
}
static int rockchip_mbox_probe(struct platform_device *pdev)
{
struct rockchip_mbox *mb;
@@ -357,7 +451,6 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
const struct rockchip_mbox_data *drv_data;
struct resource *res;
int ret, irq, i;
u32 txpoll_period;
if (!pdev->dev.of_node)
return -ENODEV;
@@ -391,54 +484,6 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
mb->mbox.ops = drv_data->ops;
spin_lock_init(&mb->cfg_lock);
if (device_property_present(&pdev->dev, "rockchip,tx-direction-b2a"))
mb->reg = &drv_data->reg_b2a;
else
mb->reg = &drv_data->reg_a2b;
/*
* rockchip,txdone-ack: the mailbox client uses its own ACK to check
* TX_DONE, and call mbox_client_txdone() API to schedule tx_tick.
* rockchip,txdone-irq: the feature only support from RK3506, the ISR
* function call mbox_chan_txdone() API to schedule tx_tick.
* txdone_poll is default for all the platform, it cooperates with
* "rockchip,txpoll-period-ms" or "rockchip,txpoll-period-us"
* periodically call last_tx_done() to check TX_DONE by the hrtimer
* in mailbox framework.
*/
if (device_property_present(&pdev->dev, "rockchip,txdone-ack")) {
mb->mbox.txdone_irq = false;
mb->mbox.txdone_poll = false;
} else if (device_property_present(&pdev->dev, "rockchip,txdone-irq")) {
mb->mbox.txdone_irq = true;
} else {
mb->mbox.txdone_poll = true;
if (IS_REACHABLE(CONFIG_MAILBOX_POLL_PERIOD_US)) {
ret = device_property_read_u32(&pdev->dev,
"rockchip,txpoll-period-us",
&txpoll_period);
if (!ret) {
mb->mbox.txpoll_period = txpoll_period;
} else {
ret = device_property_read_u32(&pdev->dev,
"rockchip,txpoll-period-ms",
&txpoll_period);
mb->mbox.txpoll_period = !ret ? txpoll_period : MAILBOX_POLLING_MS;
mb->mbox.txpoll_period *= 1000U; /* Convert to us */
}
} else {
ret = device_property_read_u32(&pdev->dev,
"rockchip,txpoll-period-ms",
&txpoll_period);
mb->mbox.txpoll_period = !ret ? txpoll_period : MAILBOX_POLLING_MS;
}
}
if (device_property_present(&pdev->dev, "rockchip,enable-cmd-trigger"))
mb->trigger_method = 0;
else
mb->trigger_method = 1;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
@@ -447,6 +492,8 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
if (IS_ERR(mb->mbox_base))
return PTR_ERR(mb->mbox_base);
rockchip_mbox_get_properties(mb, drv_data);
mb->pclk = devm_clk_get(&pdev->dev, "pclk_mailbox");
if (IS_ERR(mb->pclk)) {
ret = PTR_ERR(mb->pclk);
@@ -503,6 +550,13 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
enable_irq_wake(mb->chans[i].irq);
}
dev_info(&pdev->dev, "version: 0x%04x, tx_dir: %s, tx_done: %s, poll_period: %u, fast_mode: %s\n",
mb->version,
mb->reg == &drv_data->reg_b2a ? "B2A" : "A2B",
mb->mbox.txdone_irq ? "irq" : mb->mbox.txdone_poll ? "poll" : "ack",
mb->mbox.txpoll_period,
str_yes_no(!mb->trigger_method));
return 0;
disable_clk:

View File

@@ -21,6 +21,7 @@
#include <linux/of_graph.h>
#include <linux/rk-camera-module.h>
#include <linux/rk_hdmirx_class.h>
#include <linux/rk_hdmirx_config.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/v4l2-dv-timings.h>
@@ -265,7 +266,7 @@ enum av_mute_state {
};
enum {
AUDIO_OFF = 0x00,
AUDIO_OFF_MODE = 0x00,
AUDIO_I2S = 0x01,
AUDIO_SPDIF = 0x02,
};
@@ -2659,7 +2660,7 @@ static void it6616_hdmi_tx_audio_output_enable(struct it6616 *it6616, u8 output_
it6616_hdmi_chgbank(hdmi, 1);
switch (output_interface) {
case AUDIO_OFF:
case AUDIO_OFF_MODE:
dev_info(dev, "audio off");
it6616_hdmi_write(hdmi, 0xC7, 0x7F); // SPDIF/I2S tri-state on
break;
@@ -2685,7 +2686,7 @@ static void it6616_hdmi_audio_mute_clear(struct it6616 *it6616)
static void it6616_hdmi_rx_audio_process(struct it6616 *it6616)
{
it6616_hdmi_tx_audio_output_enable(it6616, AUDIO_OFF);
it6616_hdmi_tx_audio_output_enable(it6616, AUDIO_OFF_MODE);
it6616_hdmi_rx_reset_audio_logic(it6616);
it6616_hdmi_tx_audio_setup(it6616);
it6616_hdmi_audio_mute_clear(it6616);
@@ -3052,6 +3053,7 @@ static int it6616_initial(struct it6616 *it6616)
/* get device id */
if (it6616_get_chip_id(it6616)) {
dev_err(dev, "can not find it6616");
mutex_unlock(&it6616->confctl_mutex);
return -ENODEV;
}
@@ -3277,7 +3279,7 @@ static ssize_t mipi_reg_store(struct device *dev,
if (ret) {
dev_info(dev, "addr= %2.2X\n", addr);
dev_info(dev, "val = %2.2X\n", val);
if (((addr <= 0xFF) && (addr >= 0x00)) && ((val <= 0xFF) && (val >= 0x00)))
if (addr <= 0xFF && val <= 0xFF)
regmap_write(mipi, addr, val);
} else {
dev_info(dev, "it6616_fwrite_mipi_reg , error[%s]\n", buf);
@@ -3409,7 +3411,7 @@ static int it6616_get_detected_timings(struct v4l2_subdev *sd,
bt->il_vsync = bt->vsync + 1;
}
v4l2_dbg(1, debug, sd, "act:%dx%d, total:%dx%d, pixclk:%d, fps:%d\n",
v4l2_dbg(1, debug, sd, "act:%dx%d, total:%dx%d, pixclk:%llu, fps:%d\n",
bt->width, bt->height, htotal, vtotal, bt->pixelclock, fps);
v4l2_dbg(1, debug, sd, "hfp:%d, hs:%d, hbp:%d, vfp:%d, vs:%d, vbp:%d\n",
bt->hfrontporch, bt->hsync, bt->hbackporch,
@@ -3871,6 +3873,9 @@ static long it6616_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
case RKMODULE_GET_HDMI_MODE:
*(int *)arg = RKMODULE_HDMIIN_MODE;
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
*(int *)arg = !it6616->nosignal;
break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -3932,6 +3937,20 @@ static long it6616_compat_ioctl32(struct v4l2_subdev *sd,
return ret;
}
ret = it6616_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = it6616_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
@@ -4257,8 +4276,10 @@ static int it6616_probe(struct i2c_client *client,
it6616->edid_i2c = i2c_new_dummy_device(client->adapter,
I2C_ADR_EDID >> 1);
if (!it6616->edid_i2c)
if (!it6616->edid_i2c) {
err = -EIO;
goto unregister_mipi_i2c;
}
it6616->hdmi_regmap = devm_regmap_init_i2c(client,
&it6616_hdmi_regmap_config);

View File

@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/rk-camera-module.h>
#include <linux/rk_hdmirx_config.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/v4l2-dv-timings.h>
@@ -973,6 +974,9 @@ static long lt6911c_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
dev_dbg(&lt6911c->i2c_client->dev,
"sensor get dphy param\n");
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
*(int *)arg = !lt6911c->nosignal;
break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -1051,6 +1055,20 @@ static long lt6911c_compat_ioctl32(struct v4l2_subdev *sd,
}
kfree(dphy_param);
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = lt6911c_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
default:
ret = -ENOIOCTLCMD;
break;

View File

@@ -21,6 +21,7 @@
#include <linux/of_graph.h>
#include <linux/rk-camera-module.h>
#include <linux/rk_hdmirx_class.h>
#include <linux/rk_hdmirx_config.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/v4l2-dv-timings.h>
@@ -1028,6 +1029,9 @@ static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
capture_info->mode = 0;
}
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
*(int *)arg = !lt6911uxc->nosignal;
break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -1122,6 +1126,20 @@ static long lt6911uxc_compat_ioctl32(struct v4l2_subdev *sd,
}
kfree(capture_info);
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = lt6911uxc_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
default:
ret = -ENOIOCTLCMD;
break;

View File

@@ -28,6 +28,7 @@
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/rk-camera-module.h>
#include <linux/rk_hdmirx_config.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/v4l2-dv-timings.h>
@@ -41,7 +42,6 @@
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <linux/rk_hdmirx_config.h>
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x06)
@@ -1532,6 +1532,9 @@ static long lt6911uxe_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
capture_info->multi_dev = lt6911uxe->multi_dev_info;
}
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
*(int *)arg = !lt6911uxe->nosignal;
break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -1647,6 +1650,20 @@ static long lt6911uxe_compat_ioctl32(struct v4l2_subdev *sd,
}
kfree(capture_info);
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = lt6911uxe_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
default:
ret = -ENOIOCTLCMD;
break;

View File

@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/rk-camera-module.h>
#include <linux/rk_hdmirx_config.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/v4l2-dv-timings.h>
@@ -900,6 +901,9 @@ static long lt7911d_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
case RKMODULE_GET_HDMI_MODE:
*(int *)arg = RKMODULE_HDMIIN_MODE;
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
*(int *)arg = !lt7911d->nosignal;
break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -940,6 +944,20 @@ static long lt7911d_compat_ioctl32(struct v4l2_subdev *sd,
return ret;
}
ret = lt7911d_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = lt7911d_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));

View File

@@ -31,6 +31,7 @@
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/rk-camera-module.h>
#include <linux/rk_hdmirx_config.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/v4l2-dv-timings.h>
@@ -44,7 +45,6 @@
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <linux/rk_hdmirx_config.h>
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x07)
@@ -1193,6 +1193,9 @@ static long lt7911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
dev_dbg(&lt7911uxc->i2c_client->dev,
"sensor get dphy param\n");
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
*(int *)arg = !lt7911uxc->nosignal;
break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -1292,6 +1295,20 @@ static long lt7911uxc_compat_ioctl32(struct v4l2_subdev *sd,
}
kfree(dphy_param);
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = lt7911uxc_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
default:
ret = -ENOIOCTLCMD;
break;

View File

@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/rk-camera-module.h>
#include <linux/rk_hdmirx_config.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/v4l2-dv-timings.h>
@@ -33,7 +34,6 @@
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <linux/rk_hdmirx_config.h>
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00)
@@ -1181,6 +1181,9 @@ static long lt8668sx_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
dev_dbg(&lt8668sx->i2c_client->dev,
"sensor get dphy param\n");
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
*(int *)arg = !lt8668sx->nosignal;
break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -1280,6 +1283,20 @@ static long lt8668sx_compat_ioctl32(struct v4l2_subdev *sd,
}
kfree(dphy_param);
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = lt8668sx_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
default:
ret = -ENOIOCTLCMD;
break;

View File

@@ -78,6 +78,8 @@ static const struct regmap_range rk628_hdmirx_readable_ranges[] = {
regmap_reg_range(HDMI_RX_PDEC_ACR_CTS, HDMI_RX_PDEC_ACR_N),
regmap_reg_range(HDMI_RX_PDEC_AVI_HB, HDMI_RX_PDEC_AVI_PB),
regmap_reg_range(HDMI_RX_PDEC_AIF_CTRL, HDMI_RX_PDEC_AIF_PB0),
regmap_reg_range(HDMI_RX_PDEC_GMD_HB0, HDMI_RX_PDEC_GMD_PB0),
regmap_reg_range(HDMI_RX_PDEC_DRM_HB, HDMI_RX_PDEC_DRM_PAYLOAD6),
regmap_reg_range(HDMI_RX_HDMI20_CONTROL, HDMI_RX_CHLOCK_CONFIG),
regmap_reg_range(HDMI_RX_SCDC_REGS0, HDMI_RX_SCDC_REGS2),
regmap_reg_range(HDMI_RX_SCDC_WRDATA0, HDMI_RX_SCDC_WRDATA0),

View File

@@ -314,6 +314,8 @@ struct rk628 {
struct gpio_desc *hdmirx_det_gpio;
bool last_mipi_status;
bool is_suspend;
bool is_10bit;
bool enable_csi1;
};
#define rk628_dbg(rk628, format, ...) \

View File

@@ -115,4 +115,18 @@
#define CSI_SKIP_FRAME_NORMAL 1
enum csi_pixfmt {
CSI_RAW8 = 0,
CSI_RAW10,
CSI_PIXEL10,
CSI_PIXEL128,
CSI_RAW12,
CSI_PIXEL12,
};
enum csi_datatype {
YUV422_8BIT = 0x1e,
YUV422_10BIT = 0x1f,
};
#endif

View File

@@ -133,6 +133,7 @@ struct rk628_csi {
bool is_streaming;
bool csi_ints_en;
bool dual_mipi_use;
bool hdr_support;
enum user_color_range user_color_range;
};
@@ -151,8 +152,10 @@ struct rk628_edid {
};
static const s64 link_freq_menu_items[] = {
RK628_CSI_LINK_FREQ_LOW,
RK628_CSI_LINK_FREQ_HIGH,
RK628_CSI_LINK_FREQ_350M,
RK628_CSI_LINK_FREQ_450M,
RK628_CSI_LINK_FREQ_650M,
RK628_CSI_LINK_FREQ_750M,
RK628_CSI_LINK_FREQ_925M,
};
@@ -240,6 +243,41 @@ static u8 rk628f_edid_init_data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23,
};
static u8 rk628f_hdr_edid_init_data[] = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
0x24, 0xD0, 0x8F, 0x62, 0x01, 0x00, 0x00, 0x00,
0x2D, 0x21, 0x01, 0x03, 0x80, 0x78, 0x44, 0x78,
0x0A, 0xCF, 0x74, 0xA3, 0x57, 0x4C, 0xB0, 0x23,
0x09, 0x48, 0x4C, 0x21, 0x08, 0x00, 0x61, 0x40,
0x01, 0x01, 0x81, 0x00, 0x95, 0x00, 0xA9, 0xC0,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0xE8,
0x00, 0x30, 0xF2, 0x70, 0x5A, 0x80, 0xB0, 0x58,
0x8A, 0x00, 0xC4, 0x8E, 0x21, 0x00, 0x00, 0x1E,
0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40,
0x58, 0x2C, 0x45, 0x00, 0xB9, 0xA8, 0x42, 0x00,
0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x49,
0x46, 0x50, 0x20, 0x44, 0x69, 0x73, 0x70, 0x6C,
0x61, 0x79, 0x0A, 0x20, 0x00, 0x00, 0x00, 0xFD,
0x00, 0x3B, 0x46, 0x1F, 0x8C, 0x3C, 0x00, 0x0A,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xA8,
0x02, 0x03, 0x40, 0xF2, 0x4D, 0x01, 0x03, 0x12,
0x13, 0x84, 0x22, 0x1F, 0x90, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01,
0x00, 0x00, 0x6D, 0x03, 0x0C, 0x00, 0x10, 0x00,
0x18, 0x44, 0x20, 0x00, 0x60, 0x03, 0x02, 0x01,
0x67, 0xD8, 0x5D, 0xC4, 0x01, 0x78, 0x80, 0x00,
0xE3, 0x05, 0xE3, 0x01, 0xE4, 0x0F, 0x00, 0x18,
0x00, 0xE2, 0x00, 0xCB, 0xE3, 0x06, 0x0D, 0x01,
0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40,
0x58, 0x2C, 0x45, 0x00, 0xB9, 0xA8, 0x42, 0x00,
0x00, 0x1E, 0x08, 0xE8, 0x00, 0x30, 0xF2, 0x70,
0x5A, 0x80, 0xB0, 0x58, 0x8A, 0x00, 0xC4, 0x8E,
0x21, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
};
static struct rk628_edid edid_data[] = {
{
@@ -250,6 +288,10 @@ static struct rk628_edid edid_data[] = {
.version = 2,
.data = rk628f_edid_init_data,
},
{
.version = 3,
.data = rk628f_hdr_edid_init_data,
},
};
static const unsigned int rk628_csi_extcon_cable[] = {
@@ -444,11 +486,19 @@ static int rk628_csi_get_detected_timings(struct v4l2_subdev *sd,
struct rk628_csi *csi = to_csi(sd);
struct v4l2_bt_timings *bt = &timings->bt;
int ret;
u32 val, eotf;
csi->rk628->is_10bit = false;
ret = rk628_hdmirx_get_timings(csi->rk628, timings);
if (ret)
return ret;
rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_DRM_HB, &val);
rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_DRM_PAYLOAD0, &val);
eotf = (val >> 8) & 0xff;
if (eotf == 0x02 && csi->rk628->color_format == BUS_FMT_YUV422)
csi->rk628->is_10bit = true;
v4l2_dbg(1, debug, sd, "hfp:%d, hs:%d, hbp:%d, vfp:%d, vs:%d, vbp:%d, interlace:%d\n",
bt->hfrontporch, bt->hsync, bt->hbackporch, bt->vfrontporch, bt->vsync,
bt->vbackporch, bt->interlaced);
@@ -466,6 +516,13 @@ static int rk628_csi_get_detected_timings(struct v4l2_subdev *sd,
csi->rk628->dual_mipi = false;
}
if (csi->plat_data->tx_mode == CSI_MODE) {
if (csi->rk628->is_10bit)
csi->mbus_fmt_code = MEDIA_BUS_FMT_YUYV10_2X10;
else
csi->mbus_fmt_code = MEDIA_BUS_FMT_UYVY8_2X8;
}
return ret;
}
@@ -729,12 +786,12 @@ static void rk628_csi_soft_reset(struct v4l2_subdev *sd)
struct rk628_csi *csi = to_csi(sd);
rk628_i2c_write(csi->rk628, CSITX_SYS_CTRL0_IMD, 0x1);
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
rk628_i2c_write(csi->rk628, CSITX1_SYS_CTRL0_IMD, 0x1);
rk628_mipi_txdata_reset(sd);
rk628_i2c_write(csi->rk628, CSITX_SYS_CTRL0_IMD, 0x0);
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
rk628_i2c_write(csi->rk628, CSITX1_SYS_CTRL0_IMD, 0x0);
}
@@ -757,13 +814,14 @@ static void enable_csitx(struct v4l2_subdev *sd)
csi->rk628->dual_mipi ? GRF_DPHY_CH1_EN(1) : 0);
rk628_i2c_update_bits(csi->rk628, GRF_POST_PROC_CON, SW_SPLIT_EN,
csi->rk628->dual_mipi ? SW_SPLIT_EN : 0);
csi->rk628->enable_csi1 = csi->rk628->dual_mipi;
rk628_csi_set_csi(sd);
rk628_csi_soft_reset(sd);
usleep_range(5000, 5500);
//disabled csi state ints
rk628_i2c_write(csi->rk628, CSITX_INTR_EN_IMD, 0x0fff0000);
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
rk628_i2c_write(csi->rk628, CSITX1_INTR_EN_IMD, 0x0fff0000);
rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN,
@@ -772,7 +830,7 @@ static void enable_csitx(struct v4l2_subdev *sd)
DPHY_EN(1) |
CSITX_EN(1));
rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
if (csi->rk628->version >= RK628F_VERSION) {
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1) {
rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN,
DPHY_EN_MASK |
CSITX_EN_MASK,
@@ -787,7 +845,7 @@ static void enable_csitx(struct v4l2_subdev *sd)
BYPASS_SELECT_MASK, BYPASS_SELECT(0));
rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
if (csi->rk628->version >= RK628F_VERSION) {
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1) {
rk628_i2c_write(csi->rk628, CSITX1_ERR_INTR_CLR_IMD, 0xffffffff);
rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE_IMD);
}
@@ -900,7 +958,7 @@ static void rk628_csi_disable_stream(struct v4l2_subdev *sd)
csi->continues_clk ? CONT_MODE_CLK_CLR(1) : CONT_MODE_CLK_CLR(0));
rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
if (csi->rk628->version >= RK628F_VERSION) {
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1) {
rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN,
DPHY_EN_MASK | CSITX_EN_MASK,
DPHY_EN(0) | CSITX_EN(0));
@@ -1020,21 +1078,37 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd)
struct rk628_csi *csi = to_csi(sd);
u8 video_fmt;
u8 lanes = csi->csi_lanes_in_use;
u8 lane_num;
u32 wc_usrdef, val;
u8 lane_num, yc_swap;
u32 wc_usrdef, val, data_type, pixfmt;
int avi_rdy;
lane_num = lanes - 1;
csi->rk628->dphy_lane_en = (1 << (lanes + 1)) - 1;
wc_usrdef = csi->timings.bt.width * 2;
if (csi->rk628->dual_mipi)
if (csi->rk628->dual_mipi && !csi->rk628->is_10bit)
wc_usrdef = csi->timings.bt.width;
else if (csi->rk628->dual_mipi && csi->rk628->is_10bit)
wc_usrdef = div_u64(csi->timings.bt.width * 10, 8);
else if (!csi->rk628->dual_mipi && csi->rk628->is_10bit)
wc_usrdef = div_u64(csi->timings.bt.width * 2 * 10, 8);
else
wc_usrdef = csi->timings.bt.width * 2;
if (csi->rk628->is_10bit) {
pixfmt = CSI_RAW10;
data_type = YUV422_10BIT;
yc_swap = 1;
} else {
pixfmt = CSI_RAW8;
data_type = YUV422_8BIT;
yc_swap = 0;
}
v4l2_info(sd, "%s mipi mode, word count user define: %d\n",
csi->rk628->dual_mipi ? "dual" : "single", wc_usrdef);
rk628_csi_disable_stream(sd);
usleep_range(5000, 5500);
rk628_csi0_cru_reset(sd);
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
rk628_csi1_cru_reset(sd);
rk628_post_process_setup(sd);
@@ -1069,6 +1143,7 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd)
BYPASS_SELECT(1));
} else {
rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN,
VOP_YU_SWAP_MASK |
VOP_UV_SWAP_MASK |
VOP_YUV422_EN_MASK |
VOP_YUV422_MODE_MASK |
@@ -1076,6 +1151,7 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd)
LANE_NUM_MASK |
DPHY_EN_MASK |
CSITX_EN_MASK,
VOP_YU_SWAP(yc_swap) |
VOP_UV_SWAP(0) |
VOP_YUV422_EN(1) |
VOP_YUV422_MODE(2) |
@@ -1109,8 +1185,8 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd)
rk628_i2c_write(csi->rk628, CSITX_VOP_PATH_CTRL,
VOP_WC_USERDEFINE(wc_usrdef) |
VOP_DT_USERDEFINE(YUV422_8BIT) |
VOP_PIXEL_FORMAT(0) |
VOP_DT_USERDEFINE(data_type) |
VOP_PIXEL_FORMAT(pixfmt) |
VOP_WC_USERDEFINE_EN(1) |
VOP_DT_USERDEFINE_EN(1) |
VOP_PATH_EN(1));
@@ -1123,8 +1199,9 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd)
rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
v4l2_dbg(1, debug, sd, "%s csi config done\n", __func__);
if (csi->rk628->version >= RK628F_VERSION) {
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1) {
rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN,
VOP_YU_SWAP_MASK |
VOP_UV_SWAP_MASK |
VOP_YUV422_EN_MASK |
VOP_YUV422_MODE_MASK |
@@ -1132,6 +1209,7 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd)
LANE_NUM_MASK |
DPHY_EN_MASK |
CSITX_EN_MASK,
VOP_YU_SWAP(yc_swap) |
VOP_UV_SWAP(0) |
VOP_YUV422_EN(1) |
VOP_YUV422_MODE(2) |
@@ -1163,8 +1241,8 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd)
rk628_i2c_write(csi->rk628, CSITX1_VOP_PATH_CTRL,
VOP_WC_USERDEFINE(wc_usrdef) |
VOP_DT_USERDEFINE(YUV422_8BIT) |
VOP_PIXEL_FORMAT(0) |
VOP_DT_USERDEFINE(data_type) |
VOP_PIXEL_FORMAT(pixfmt) |
VOP_WC_USERDEFINE_EN(1) |
VOP_DT_USERDEFINE_EN(1) |
VOP_PATH_EN(1));
@@ -1473,6 +1551,10 @@ static void rk628_csi_initial(struct v4l2_subdev *sd)
def_edid.edid = edid_init_data;
csi->edid_version = 1;
}
if (csi->hdr_support && csi->rk628->version >= RK628F_VERSION) {
def_edid.edid = rk628f_hdr_edid_init_data;
csi->edid_version = 3;
}
rk628_csi_s_edid(sd, &def_edid);
}
@@ -1568,7 +1650,7 @@ static void rk628_csi_enable_csi_interrupts(struct v4l2_subdev *sd, bool en)
rk628_csi_clear_csi_interrupts(sd);
//disabled csi state ints
rk628_i2c_write(csi->rk628, CSITX_INTR_EN_IMD, 0x0fff0000);
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
rk628_i2c_write(csi->rk628, CSITX1_INTR_EN_IMD, 0x0fff0000);
//enable csi error ints
@@ -1578,7 +1660,8 @@ static void rk628_csi_enable_csi_interrupts(struct v4l2_subdev *sd, bool en)
GRF_INTR0_EN, CSI_INT_EN_MASK | CSI_INT_WRITE_EN_MASK,
CSI_INT_EN(3) | CSI_INT_WRITE_EN(3));
rk628_i2c_write(csi->rk628, CSITX_ERR_INTR_EN_IMD, 0x0fff0fff);
rk628_i2c_write(csi->rk628, CSITX1_ERR_INTR_EN_IMD, 0x0fff0fff);
if (csi->rk628->enable_csi1)
rk628_i2c_write(csi->rk628, CSITX1_ERR_INTR_EN_IMD, 0x0fff0fff);
} else {
rk628_i2c_update_bits(csi->rk628,
GRF_INTR0_EN, CSI_INT_EN_MASK | CSI_INT_WRITE_EN_MASK,
@@ -1592,7 +1675,8 @@ static void rk628_csi_enable_csi_interrupts(struct v4l2_subdev *sd, bool en)
GRF_INTR0_EN, CSI_INT_EN_MASK | CSI_INT_WRITE_EN_MASK,
CSI_INT_EN(0) | CSI_INT_WRITE_EN(3));
rk628_i2c_write(csi->rk628, CSITX_ERR_INTR_EN_IMD, 0x0fff0000);
rk628_i2c_write(csi->rk628, CSITX1_ERR_INTR_EN_IMD, 0x0fff0000);
if (csi->rk628->enable_csi1)
rk628_i2c_write(csi->rk628, CSITX1_ERR_INTR_EN_IMD, 0x0fff0000);
} else {
rk628_i2c_update_bits(csi->rk628,
GRF_INTR0_EN, CSI_INT_EN_MASK | CSI_INT_WRITE_EN_MASK,
@@ -1638,7 +1722,7 @@ static void rk628_csi_clear_csi_interrupts(struct v4l2_subdev *sd)
//clr int status
rk628_i2c_write(csi->rk628, CSITX_ERR_INTR_CLR_IMD, 0xffffffff);
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
rk628_i2c_write(csi->rk628, CSITX1_ERR_INTR_CLR_IMD, 0xffffffff);
if (csi->rk628->version >= RK628F_VERSION)
@@ -1670,11 +1754,11 @@ static void rk628_csi_error_process(struct v4l2_subdev *sd)
rk628_hdmirx_vid_enable(sd, false);
rk628_csi_enable_csi_interrupts(sd, false);
rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN, CSITX_EN_MASK, CSITX_EN(0));
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN,
CSITX_EN_MASK, CSITX_EN(0));
rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE_IMD);
usleep_range(5000, 5500);
@@ -1687,7 +1771,7 @@ static void rk628_csi_error_process(struct v4l2_subdev *sd)
rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN, CSITX_EN_MASK, CSITX_EN(1));
rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
if (csi->rk628->version >= RK628F_VERSION) {
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1) {
rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN,
CSITX_EN_MASK, CSITX_EN(1));
rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE_IMD);
@@ -1695,7 +1779,7 @@ static void rk628_csi_error_process(struct v4l2_subdev *sd)
for (i = 0; i < 3; i++) {
rk628_i2c_read(csi->rk628, CSITX_CSITX_EN, &val);
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
rk628_i2c_read(csi->rk628, CSITX1_CSITX_EN, &val_csi1);
v4l2_dbg(1, debug, sd, "%s, csi0_status: 0x%x, csi1_status: 0x%x, i=%d\n",
__func__, val, val_csi1, i);
@@ -1707,7 +1791,7 @@ static void rk628_csi_error_process(struct v4l2_subdev *sd)
rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN,
CSITX_EN_MASK, CSITX_EN(1));
rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
if (csi->rk628->version >= RK628F_VERSION) {
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1) {
rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN,
CSITX_EN_MASK, CSITX_EN(1));
rk628_i2c_write(csi->rk628,
@@ -1862,7 +1946,7 @@ static int rk628_csi_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
v4l2_dbg(1, debug, sd, "%s: int0 status: 0x%x\n", __func__, int0_status);
rk628_i2c_read(csi->rk628, CSITX_ERR_INTR_RAW_STATUS_IMD, &csi0_raw_ints);
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
rk628_i2c_read(csi->rk628, CSITX1_ERR_INTR_RAW_STATUS_IMD, &csi1_raw_ints);
rk628_csi_clear_csi_interrupts(sd);
@@ -2141,7 +2225,7 @@ static int rk628_csi_enum_frame_interval(struct v4l2_subdev *sd,
static u32 rk628_csi_get_lane_rate_mbps(struct rk628_csi *csi)
{
u32 lane_rate;
u32 max_lane_rate = 1300;
u32 max_lane_rate = 1800;
u8 bpp, lanes;
u64 pixelclock = csi->timings.bt.pixelclock;
@@ -2152,23 +2236,48 @@ static u32 rk628_csi_get_lane_rate_mbps(struct rk628_csi *csi)
lane_rate = div_u64(lane_rate, lanes);
if (csi->rk628->dual_mipi)
lane_rate /= 2;
if (csi->rk628->is_10bit)
lane_rate = div_u64(lane_rate * 5, 4);
if (lane_rate > 1300)
if (lane_rate > 1500)
lane_rate = max_lane_rate;
else if (lane_rate > 700 && lane_rate <= 1300)
else if (lane_rate > 1300 && lane_rate <= 1500)
lane_rate = 1500;
else if (lane_rate > 900 && lane_rate <= 1300)
lane_rate = 1300;
else if (lane_rate > 700 && lane_rate <= 900)
lane_rate = 900;
else
lane_rate = 700;
return lane_rate;
}
static int rk628_find_best_link_freq(u32 rate)
{
u32 dist;
int cur_best_fit = 0;
u32 cur_best_fit_dist = -1;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
dist = abs(div_u64(link_freq_menu_items[i] * 2, 1000000) - rate);
if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
cur_best_fit_dist = dist;
cur_best_fit = i;
}
}
return cur_best_fit;
}
static int rk628_csi_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
struct rk628_csi *csi = to_csi(sd);
u32 rate;
int index;
if (!tx_5v_power_present(sd) || csi->nosignal) {
v4l2_info(sd, "%s hdmirx no signal\n", __func__);
@@ -2195,13 +2304,8 @@ static int rk628_csi_get_fmt(struct v4l2_subdev *sd,
v4l2_dbg(1, debug, sd, "%s mipi bitrate:%u mbps\n", __func__, rate);
if (rate > 1300)
__v4l2_ctrl_s_ctrl(csi->link_freq, 2);
else if (rate <= 1300 && rate > 700)
__v4l2_ctrl_s_ctrl(csi->link_freq, 1);
else
__v4l2_ctrl_s_ctrl(csi->link_freq, 0);
index = rk628_find_best_link_freq(rate);
__v4l2_ctrl_s_ctrl(csi->link_freq, index);
__v4l2_ctrl_s_ctrl_int64(csi->pixel_rate, RK628_CSI_PIXEL_RATE_HIGH);
mutex_unlock(&csi->confctl_mutex);
@@ -2256,11 +2360,15 @@ static int rk628_csi_set_fmt(struct v4l2_subdev *sd,
switch (code) {
case MEDIA_BUS_FMT_UYVY8_2X8:
if (csi->plat_data->bus_fmt == MEDIA_BUS_FMT_UYVY8_2X8)
if (csi->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_2X8)
break;
return -EINVAL;
case MEDIA_BUS_FMT_RGB888_1X24:
if (csi->plat_data->bus_fmt == MEDIA_BUS_FMT_RGB888_1X24)
if (csi->mbus_fmt_code == MEDIA_BUS_FMT_RGB888_1X24)
break;
return -EINVAL;
case MEDIA_BUS_FMT_YUYV10_2X10:
if (csi->mbus_fmt_code == MEDIA_BUS_FMT_YUYV10_2X10)
break;
return -EINVAL;
default:
@@ -2447,7 +2555,8 @@ static void rk628_csi_reset_streaming(struct v4l2_subdev *sd, int on)
CONT_MODE_CLK_CLR_MASK | CONT_MODE_CLK_SET_MASK |
NON_CONTINUOUS_MODE_MASK, CONT_MODE_CLK_CLR(0) |
CONT_MODE_CLK_SET(1) | NON_CONTINUOUS_MODE(0));
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION &&
csi->rk628->enable_csi1)
rk628_i2c_update_bits(csi->rk628, CSITX1_SYS_CTRL3_IMD,
CONT_MODE_CLK_CLR_MASK | CONT_MODE_CLK_SET_MASK |
NON_CONTINUOUS_MODE_MASK, CONT_MODE_CLK_CLR(0) |
@@ -2457,7 +2566,8 @@ static void rk628_csi_reset_streaming(struct v4l2_subdev *sd, int on)
CONT_MODE_CLK_CLR_MASK | CONT_MODE_CLK_SET_MASK |
NON_CONTINUOUS_MODE_MASK, CONT_MODE_CLK_CLR(0) |
CONT_MODE_CLK_SET(0) | NON_CONTINUOUS_MODE(1));
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION &&
csi->rk628->enable_csi1)
rk628_i2c_update_bits(csi->rk628, CSITX1_SYS_CTRL3_IMD,
CONT_MODE_CLK_CLR_MASK | CONT_MODE_CLK_SET_MASK |
NON_CONTINUOUS_MODE_MASK, CONT_MODE_CLK_CLR(0) |
@@ -2467,7 +2577,7 @@ static void rk628_csi_reset_streaming(struct v4l2_subdev *sd, int on)
rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN,
DPHY_EN_MASK | CSITX_EN_MASK, DPHY_EN(1) | CSITX_EN(1));
rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
if (csi->rk628->version >= RK628F_VERSION) {
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1) {
rk628_i2c_update_bits(csi->rk628, CSITX1_CSITX_EN,
DPHY_EN_MASK | CSITX_EN_MASK, DPHY_EN(1) | CSITX_EN(1));
rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE_IMD);
@@ -2521,6 +2631,8 @@ static long rk628_csi_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
struct rkmodule_capture_info *capture_info;
u32 stream = 0;
int edid_version, i;
struct hdr_metadata_infoframe hdmi_metadata;
u8 input_color_space;
switch (cmd) {
case RKMODULE_GET_MODULE_INFO:
@@ -2619,6 +2731,18 @@ static long rk628_csi_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
}
v4l2_info(sd, "the edid version is not supported: %d\n", edid_version);
return -EINVAL;
case RK_HDMIRX_CMD_GET_HDR_METADATA:
rk628_hdmirx_get_hdr_matedata(csi->rk628, &hdmi_metadata);
memcpy(arg, &hdmi_metadata, sizeof(hdmi_metadata));
break;
case RK_HDMIRX_CMD_GET_OUTPUT_COLOR_RANGE:
*(int *)arg = HDMIRX_FULL_RANGE;
break;
case RK_HDMIRX_CMD_GET_OUTPUT_COLOR_SPACE:
input_color_space = rk628_csc_color_space_convert(csi->rk628->color_space,
csi->rk628->color_format);
*(int *)arg = rk628_get_output_color_space(csi->rk628, input_color_space);
break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -2637,7 +2761,7 @@ static int mipi_dphy_power_on(struct rk628_csi *csi)
csi->lane_mbps = rk628_csi_get_lane_rate_mbps(csi);
bus_width = csi->lane_mbps << 8;
bus_width |= COMBTXPHY_MODULEA_EN;
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
bus_width |= COMBTXPHY_MODULEB_EN;
v4l2_info(sd, "%s mipi bitrate:%llu mbps\n", __func__, csi->lane_mbps);
@@ -2648,7 +2772,7 @@ static int mipi_dphy_power_on(struct rk628_csi *csi)
rk628_txphy_set_mode(csi->rk628, PHY_MODE_VIDEO_MIPI);
rk628_mipi_dphy_init_hsfreqrange(csi->rk628, csi->lane_mbps, 0);
if (csi->rk628->version >= RK628F_VERSION)
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1)
rk628_mipi_dphy_init_hsfreqrange(csi->rk628, csi->lane_mbps, 1);
rk628_txphy_power_on(csi->rk628);
@@ -2660,7 +2784,7 @@ static int mipi_dphy_power_on(struct rk628_csi *csi)
0, 1000);
if (ret < 0)
dev_err(csi->rk628->dev, "csi0 phy is not locked\n");
if (csi->rk628->version >= RK628F_VERSION) {
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1) {
ret = regmap_read_poll_timeout(csi->rk628->regmap[RK628_DEV_CSI1],
CSITX1_CSITX_STATUS1,
val, val & DPHY_PLL_LOCK,
@@ -2677,7 +2801,7 @@ static int mipi_dphy_power_on(struct rk628_csi *csi)
if (ret < 0)
dev_err(csi->rk628->dev, "csi0 lane module is not in stop state, val: 0x%x\n", val);
if (csi->rk628->version >= RK628F_VERSION) {
if (csi->rk628->version >= RK628F_VERSION && csi->rk628->enable_csi1) {
ret = regmap_read_poll_timeout(csi->rk628->regmap[RK628_DEV_CSI1],
CSITX1_CSITX_STATUS1,
val, (val & mask) == mask,
@@ -3223,6 +3347,9 @@ static int rk628_csi_probe_of(struct rk628_csi *csi)
if (of_property_read_bool(dev->of_node, "cec-enable"))
csi->cec_enable = true;
if (of_property_read_bool(dev->of_node, "hdr-support"))
csi->hdr_support = true;
if (of_property_read_bool(dev->of_node, "i2s-enable-default"))
i2s_enable_default = true;

View File

@@ -1572,6 +1572,38 @@ u32 rk628_hdmirx_get_tmdsclk_cnt(struct rk628 *rk628)
}
EXPORT_SYMBOL(rk628_hdmirx_get_tmdsclk_cnt);
int rk628_hdmirx_get_hdr_matedata(struct rk628 *rk628,
struct hdr_metadata_infoframe *hdmi_metadata)
{
u32 val;
int i;
rk628_i2c_read(rk628, HDMI_RX_PDEC_DRM_HB, &val);
if (!val)
return -EINVAL;
rk628_i2c_read(rk628, HDMI_RX_PDEC_DRM_PAYLOAD0, &val);
hdmi_metadata->eotf = (val >> 8) & 0xff;
hdmi_metadata->metadata_type = (val >> 16) & 0xff;
for (i = 0; i < 3; i++) {
rk628_i2c_read(rk628, HDMI_RX_PDEC_DRM_PAYLOAD1 + i * 4, &val);
hdmi_metadata->display_primaries[i].x = val & 0xffff;
hdmi_metadata->display_primaries[i].y = (val >> 16) & 0xffff;
}
rk628_i2c_read(rk628, HDMI_RX_PDEC_DRM_PAYLOAD4, &val);
hdmi_metadata->white_point.x = val & 0xffff;
hdmi_metadata->white_point.y = (val >> 16) & 0xffff;
rk628_i2c_read(rk628, HDMI_RX_PDEC_DRM_PAYLOAD5, &val);
hdmi_metadata->max_display_mastering_luminance = val & 0xffff;
hdmi_metadata->min_display_mastering_luminance = (val >> 16) & 0xffff;
rk628_i2c_read(rk628, HDMI_RX_PDEC_DRM_PAYLOAD6, &val);
hdmi_metadata->max_cll = val & 0xffff;
hdmi_metadata->max_fall = (val >> 16) & 0xffff;
return 0;
}
EXPORT_SYMBOL(rk628_hdmirx_get_hdr_matedata);
struct rk628_timings {
int vic;
int hactive;
@@ -1721,6 +1753,7 @@ static int rk628_hdmirx_read_timing(struct rk628 *rk628,
hfp = hfp * 2 * 8 / 10;
hbp = hbp * 2 * 8 / 10;
hs = hs * 2 * 8 / 10;
rk628->is_10bit = true;
} else {
htotal *= 2;
hact *= 2;
@@ -1898,9 +1931,9 @@ u8 rk628_hdmirx_get_range(struct rk628 *rk628)
if (dvi)
color_range = HDMIRX_FULL_RANGE;
if (color_range == HDMIRX_DEFAULT_RANGE)
vic ?
(color_range = HDMIRX_FULL_RANGE) :
(color_range = HDMIRX_LIMIT_RANGE);
((vic >= 2) && (vic <= 127)) ?
(color_range = HDMIRX_LIMIT_RANGE) :
(color_range = HDMIRX_FULL_RANGE);
}
return color_range;
@@ -2205,6 +2238,7 @@ static int rk628_hdmirx_status_show(struct seq_file *s, void *v)
bool plugin;
u32 val, htot, vtot, fps, format;
u8 fmt, range, space;
struct hdr_metadata_infoframe hdmi_metadata;
plugin = rk628_hdmirx_tx_5v_power_detect(rk628->hdmirx_det_gpio);
seq_printf(s, "status: %s\n", plugin ? "plugin" : "plugout");
@@ -2265,6 +2299,41 @@ static int rk628_hdmirx_status_show(struct seq_file *s, void *v)
else
seq_puts(s, "Unknown\n");
seq_puts(s, "EOTF: ");
if (!rk628_hdmirx_get_hdr_matedata(rk628, &hdmi_metadata)) {
switch (hdmi_metadata.eotf & 0x7) {
case HDMI_EOTF_TRADITIONAL_GAMMA_SDR:
seq_puts(s, "SDR");
break;
case HDMI_EOTF_TRADITIONAL_GAMMA_HDR:
seq_puts(s, "HDR");
break;
case HDMI_EOTF_SMPTE_ST2084:
seq_puts(s, "ST2084");
break;
case HDMI_EOTF_BT_2100_HLG:
seq_puts(s, "HLG");
break;
default:
seq_puts(s, "Not Defined\n");
return 0;
}
seq_printf(s, "\nx0: %d", hdmi_metadata.display_primaries[0].x);
seq_printf(s, "\t\t\t\ty0: %d\n", hdmi_metadata.display_primaries[0].y);
seq_printf(s, "x1: %d", hdmi_metadata.display_primaries[1].x);
seq_printf(s, "\t\t\t\ty1: %d\n", hdmi_metadata.display_primaries[1].y);
seq_printf(s, "x2: %d", hdmi_metadata.display_primaries[2].x);
seq_printf(s, "\t\t\t\ty2: %d\n", hdmi_metadata.display_primaries[2].y);
seq_printf(s, "white x: %d", hdmi_metadata.white_point.x);
seq_printf(s, "\t\t\twhite y: %d\n", hdmi_metadata.white_point.y);
seq_printf(s, "max lum: %d", hdmi_metadata.max_display_mastering_luminance);
seq_printf(s, "\t\t\tmin lum: %d\n", hdmi_metadata.min_display_mastering_luminance);
seq_printf(s, "max cll: %d", hdmi_metadata.max_cll);
seq_printf(s, "\t\t\tmax fall: %d\n", hdmi_metadata.max_fall);
} else {
seq_puts(s, "Off\n");
}
return 0;
}

View File

@@ -8,6 +8,8 @@
#ifndef __RK628_HDMIRX_H
#define __RK628_HDMIRX_H
#include <drm/drm_mode.h>
#include <linux/hdmi.h>
#include <linux/gpio/consumer.h>
#include <media/cec.h>
#include <media/cec-notifier.h>
@@ -280,6 +282,16 @@
#define HDMI_RX_PDEC_AIF_CTRL (HDMI_RX_BASE + 0x03c0)
#define FC_LFE_EXCHG(x) UPDATE(x, 18, 18)
#define HDMI_RX_PDEC_AIF_PB0 (HDMI_RX_BASE + 0x03c8)
#define HDMI_RX_PDEC_GMD_HB0 (HDMI_RX_BASE + 0x03d0)
#define HDMI_RX_PDEC_GMD_PB0 (HDMI_RX_BASE + 0x03d4)
#define HDMI_RX_PDEC_DRM_HB (HDMI_RX_BASE + 0x04c0)
#define HDMI_RX_PDEC_DRM_PAYLOAD0 (HDMI_RX_BASE + 0x04c4)
#define HDMI_RX_PDEC_DRM_PAYLOAD1 (HDMI_RX_BASE + 0x04c8)
#define HDMI_RX_PDEC_DRM_PAYLOAD2 (HDMI_RX_BASE + 0x04cc)
#define HDMI_RX_PDEC_DRM_PAYLOAD3 (HDMI_RX_BASE + 0x04d0)
#define HDMI_RX_PDEC_DRM_PAYLOAD4 (HDMI_RX_BASE + 0x04d4)
#define HDMI_RX_PDEC_DRM_PAYLOAD5 (HDMI_RX_BASE + 0x04d8)
#define HDMI_RX_PDEC_DRM_PAYLOAD6 (HDMI_RX_BASE + 0x04dc)
#define HDMI_RX_HDMI20_CONTROL (HDMI_RX_BASE + 0x0800)
#define PVO1UNMUTE(x) UPDATE(x, 29, 29)
@@ -448,6 +460,10 @@
#define RK628_CSI_LINK_FREQ_LOW 350000000
#define RK628_CSI_LINK_FREQ_HIGH 650000000
#define RK628_CSI_LINK_FREQ_350M 350000000
#define RK628_CSI_LINK_FREQ_450M 450000000
#define RK628_CSI_LINK_FREQ_650M 650000000
#define RK628_CSI_LINK_FREQ_750M 750000000
#define RK628_CSI_LINK_FREQ_925M 925000000
#define RK628_CSI_PIXEL_RATE_LOW 400000000
#define RK628_CSI_PIXEL_RATE_HIGH 600000000
@@ -459,7 +475,6 @@
#define CSITX_ERR_RETRY_TIMES 3
#define USE_4_LANES 4
#define YUV422_8BIT 0x1e
#define SCDC_CED_ERR_CNT 0xfff
@@ -545,6 +560,8 @@ int rk628_hdmirx_get_timings(struct rk628 *rk628,
u8 rk628_hdmirx_get_range(struct rk628 *rk628);
u8 rk628_hdmirx_get_color_space(struct rk628 *rk628);
int rk628_hdmirx_get_hdcp_enc_status(struct rk628 *rk628);
int rk628_hdmirx_get_hdr_matedata(struct rk628 *rk628,
struct hdr_metadata_infoframe *hdmi_metadata);
void rk628_hdmirx_controller_reset(struct rk628 *rk628);
bool rk628_hdmirx_scdc_ced_err(struct rk628 *rk628);
bool rk628_hdmirx_is_locked(struct rk628 *rk628);

View File

@@ -1603,7 +1603,7 @@ static int rockchip_calc_post_csc(struct rk628 *rk628, struct post_csc_coef *csc
return ret;
}
static u8 rk628_csc_color_space_convert(u8 in_color_space, u8 format)
u8 rk628_csc_color_space_convert(u8 in_color_space, u8 format)
{
switch (in_color_space) {
case HDMIRX_XVYCC601:
@@ -1628,8 +1628,9 @@ static u8 rk628_csc_color_space_convert(u8 in_color_space, u8 format)
return OPTM_CS_E_UNKNOWN;
}
}
EXPORT_SYMBOL(rk628_csc_color_space_convert);
static u8 rk628_get_output_color_space(struct rk628 *rk628, u8 input_color_space)
u8 rk628_get_output_color_space(struct rk628 *rk628, u8 input_color_space)
{
switch (input_color_space) {
case OPTM_CS_E_XV_YCC_601:
@@ -1646,6 +1647,7 @@ static u8 rk628_get_output_color_space(struct rk628 *rk628, u8 input_color_space
return OPTM_CS_E_XV_YCC_709;
}
}
EXPORT_SYMBOL(rk628_get_output_color_space);
static void rk628_post_process_csc(struct rk628 *rk628,
bool is_input_full_range, bool is_output_full_range)

View File

@@ -7,6 +7,8 @@
#ifndef POST_PROCESS_H
#define POST_PROCESS_H
u8 rk628_csc_color_space_convert(u8 in_color_space, u8 format);
u8 rk628_get_output_color_space(struct rk628 *rk628, u8 input_color_space);
void rk628_post_process_csc_en(struct rk628 *rk628, bool input_full_range, bool output_full_range);
void rk628_post_process_csc_dis(struct rk628 *rk628);
void rk628_post_process_pattern_node(struct rk628 *rk628);

View File

@@ -1479,7 +1479,6 @@ static int __sc850sl_stop_stream(struct sc850sl *sc850sl)
sc850sl->has_init_exp = false;
if (sc850sl->is_thunderboot) {
sc850sl->is_first_streamoff = true;
pm_runtime_put(&sc850sl->client->dev);
}
return sc850sl_write_reg(sc850sl->client, SC850SL_REG_CTRL_MODE,
SC850SL_REG_VALUE_08BIT, SC850SL_MODE_SW_STANDBY);

View File

@@ -43,6 +43,7 @@
#include <linux/version.h>
#include <linux/compat.h>
#include <linux/rk-camera-module.h>
#include <linux/rk_hdmirx_config.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
@@ -1946,6 +1947,9 @@ static long tc35874x_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
case RKMODULE_GET_HDMI_MODE:
*(int *)arg = RKMODULE_HDMIIN_MODE;
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
*(int *)arg = !no_signal(sd);
break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -2001,6 +2005,20 @@ static long tc35874x_compat_ioctl32(struct v4l2_subdev *sd,
return ret;
}
ret = tc35874x_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = tc35874x_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));

View File

@@ -5041,10 +5041,25 @@ static int rkcif_csi_channel_set_v1(struct rkcif_stream *stream,
channel->crop_st_y << 16 |
(channel->crop_st_x + capture_info->multi_dev.pixel_offset));
if (!(capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE &&
index < capture_info->multi_dev.dev_num - 1)) {
if (mode == RKCIF_STREAM_MODE_CAPTURE)
rkcif_assign_new_buffer_pingpong(stream,
RKCIF_YUV_ADDR_STATE_INIT,
channel->id);
else if (mode == RKCIF_STREAM_MODE_TOISP ||
mode == RKCIF_STREAM_MODE_TOISP_RDBK)
rkcif_assign_new_buffer_pingpong_toisp(stream,
RKCIF_YUV_ADDR_STATE_INIT,
channel->id);
}
val = channel->virtual_width;
if (dev->chip_id >= CHIP_RV1103B_CIF && dev->sditf[0] &&
dev->sditf[0]->hdr_wrap_line)
val |= dev->sditf[0]->hdr_wrap_line << 20;
if (dev->chip_id >= CHIP_RK3562_CIF)
val |= BIT(31);
rkcif_write_register(dev, get_reg_index_of_frm0_y_vlw(channel->id), val);
if (stream->lack_buf_cnt == 2)
@@ -5172,18 +5187,7 @@ static int rkcif_csi_channel_set_v1(struct rkcif_stream *stream,
} else {
atomic_inc(&stream->cifdev->id_use_cnt);
}
if (!(capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE &&
index < capture_info->multi_dev.dev_num - 1)) {
if (mode == RKCIF_STREAM_MODE_CAPTURE)
rkcif_assign_new_buffer_pingpong(stream,
RKCIF_YUV_ADDR_STATE_INIT,
channel->id);
else if (mode == RKCIF_STREAM_MODE_TOISP ||
mode == RKCIF_STREAM_MODE_TOISP_RDBK)
rkcif_assign_new_buffer_pingpong_toisp(stream,
RKCIF_YUV_ADDR_STATE_INIT,
channel->id);
}
dev->intr_mask = rkcif_read_register(dev, CIF_REG_MIPI_LVDS_INTEN);
return 0;
}
@@ -5306,10 +5310,24 @@ static int rkcif_csi_channel_set_rv1126b(struct rkcif_stream *stream,
channel->crop_st_y << 16 |
(channel->crop_st_x + capture_info->multi_dev.pixel_offset));
if (!(capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE &&
index < capture_info->multi_dev.dev_num - 1)) {
if (mode == RKCIF_STREAM_MODE_CAPTURE)
rkcif_assign_new_buffer_pingpong(stream,
RKCIF_YUV_ADDR_STATE_INIT,
channel->id);
else if (mode == RKCIF_STREAM_MODE_TOISP ||
mode == RKCIF_STREAM_MODE_TOISP_RDBK)
rkcif_assign_new_buffer_pingpong_toisp(stream,
RKCIF_YUV_ADDR_STATE_INIT,
channel->id);
}
val = channel->virtual_width;
if (dev->sditf[0] &&
dev->sditf[0]->hdr_wrap_line)
val |= dev->sditf[0]->hdr_wrap_line << 20;
val |= BIT(31);
rkcif_write_register(dev, get_reg_index_of_frm0_y_vlw(channel->id), val);
if (stream->lack_buf_cnt == 2)
@@ -5387,18 +5405,7 @@ static int rkcif_csi_channel_set_rv1126b(struct rkcif_stream *stream,
} else {
atomic_inc(&stream->cifdev->id_use_cnt);
}
if (!(capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE &&
index < capture_info->multi_dev.dev_num - 1)) {
if (mode == RKCIF_STREAM_MODE_CAPTURE)
rkcif_assign_new_buffer_pingpong(stream,
RKCIF_YUV_ADDR_STATE_INIT,
channel->id);
else if (mode == RKCIF_STREAM_MODE_TOISP ||
mode == RKCIF_STREAM_MODE_TOISP_RDBK)
rkcif_assign_new_buffer_pingpong_toisp(stream,
RKCIF_YUV_ADDR_STATE_INIT,
channel->id);
}
dev->intr_mask = rkcif_read_register(dev, CIF_REG_MIPI_LVDS_INTEN);
return 0;
}
@@ -5640,7 +5647,12 @@ static void rkcif_stream_stop(struct rkcif_stream *stream)
}
} else {
if (atomic_read(&cif_dev->pipe.stream_cnt) == 1) {
if (stream->cifdev->chip_id >= CHIP_RV1126B_CIF) {
val = rkcif_read_register(cif_dev, get_dvp_reg_index_of_id_ctrl0(stream->id));
val &= ~(CSI_ENABLE_CAPTURE | CSI_DMA_ENABLE_RK3576);
rkcif_write_register(cif_dev, get_dvp_reg_index_of_id_ctrl0(stream->id), val);
}
if (atomic_read(&stream->cifdev->id_use_cnt) == 0) {
val = rkcif_read_register(cif_dev, CIF_REG_DVP_CTRL);
rkcif_write_register(cif_dev, CIF_REG_DVP_CTRL,
val & (~ENABLE_CAPTURE));
@@ -7897,6 +7909,7 @@ static int rkcif_stream_start(struct rkcif_stream *stream, unsigned int mode)
if (dma_state)
return 0;
atomic_inc(&stream->cifdev->id_use_cnt);
mbus_flags = mbus->bus.parallel.flags;
if ((mbus_flags & CIF_DVP_PCLK_DUAL_EDGE) == CIF_DVP_PCLK_DUAL_EDGE) {
bt1120_edge_mode = (dev->chip_id < CHIP_RK3588_CIF ?
@@ -8264,6 +8277,7 @@ static int rkcif_stream_start_rv1126b(struct rkcif_stream *stream, unsigned int
if (dma_state)
return 0;
atomic_inc(&stream->cifdev->id_use_cnt);
mbus_flags = mbus->bus.parallel.flags;
if ((mbus_flags & CIF_DVP_PCLK_DUAL_EDGE) == CIF_DVP_PCLK_DUAL_EDGE) {
bt1120_edge_mode = BT1120_CLOCK_DOUBLE_EDGES_RV1126B;
@@ -8306,7 +8320,6 @@ static int rkcif_stream_start_rv1126b(struct rkcif_stream *stream, unsigned int
}
}
val = stream->pixm.plane_fmt[0].bytesperline;
if (stream->crop_enable) {
dev->channels[stream->id].crop_en = 1;
dev->channels[stream->id].crop_st_x = stream->crop[CROP_SRC_ACT].left;
@@ -8321,10 +8334,6 @@ static int rkcif_stream_start_rv1126b(struct rkcif_stream *stream, unsigned int
dev->channels[stream->id].crop_en = 0;
}
if (dev->chip_id > CHIP_RK3562_CIF && stream->sw_dbg_en)
val = (val + 23) / 24 * 24;
rkcif_write_register(dev, get_dvp_reg_index_of_vlw(stream->id), val);
rkcif_write_register(dev, CIF_REG_DVP_SET_SIZE_ID0 + stream->id,
dev->channels[stream->id].width |
(dev->channels[stream->id].height << 16));
@@ -8351,6 +8360,11 @@ static int rkcif_stream_start_rv1126b(struct rkcif_stream *stream, unsigned int
stream->id);
}
val = stream->pixm.plane_fmt[0].bytesperline;
if (dev->chip_id > CHIP_RK3562_CIF && stream->sw_dbg_en)
val = (val + 23) / 24 * 24;
rkcif_write_register(dev, get_dvp_reg_index_of_vlw(stream->id), val | BIT(31));
dev->workmode = RKCIF_WORKMODE_PINGPONG;
href_pol = (mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) ?
HSY_HIGH_ACTIVE : HSY_LOW_ACTIVE;
@@ -12122,7 +12136,6 @@ static void rkcif_line_wake_up_rdbk(struct rkcif_stream *stream, int mipi_id)
active_buf->dbufs.sequence = stream->sequence;
active_buf->dbufs.timestamp = stream->readout.fs_timestamp;
active_buf->fe_timestamp = rkcif_time_get_ns(stream->cifdev);
stream->last_frame_idx = stream->frame_idx;
if (stream->cifdev->hdr.hdr_mode == NO_HDR) {
rkcif_s_rx_buffer(stream, &active_buf->dbufs);
if (stream->cifdev->is_support_tools && stream->tools_vdev)
@@ -12139,9 +12152,12 @@ static void rkcif_deal_readout_time(struct rkcif_stream *stream)
struct rkcif_device *cif_dev = stream->cifdev;
struct rkcif_stream *detect_stream = &cif_dev->stream[0];
unsigned long flags;
u64 cur_time = 0;
spin_lock_irqsave(&stream->fps_lock, flags);
stream->readout.fe_timestamp = rkcif_time_get_ns(cif_dev);
cur_time = rkcif_time_get_ns(cif_dev);
stream->readout.rate_time = cur_time - stream->readout.fe_timestamp;
stream->readout.fe_timestamp = cur_time;
if (cif_dev->inf_id == RKCIF_DVP) {
spin_unlock_irqrestore(&stream->fps_lock, flags);
@@ -12241,7 +12257,8 @@ static void rkcif_update_stream_interlace(struct rkcif_device *cif_dev,
}
}
}
rkcif_deal_readout_time(stream);
if (!cif_dev->sditf[0] || cif_dev->sditf[0]->mode.rdbk_mode >= RKISP_VICAP_RDBK_AIQ)
rkcif_deal_readout_time(stream);
stream->last_fe_interlaced_phase = fe_interlaced_phase;
}
@@ -12273,7 +12290,8 @@ static void rkcif_update_stream(struct rkcif_device *cif_dev,
spin_unlock_irqrestore(&stream->fps_lock, flags);
}
rkcif_deal_readout_time(stream);
if (!cif_dev->sditf[0] || cif_dev->sditf[0]->mode.rdbk_mode >= RKISP_VICAP_RDBK_AIQ)
rkcif_deal_readout_time(stream);
if (!stream->is_line_wake_up) {
ret = rkcif_assign_new_buffer_pingpong(stream,
@@ -12281,7 +12299,6 @@ static void rkcif_update_stream(struct rkcif_device *cif_dev,
mipi_id);
if (ret && cif_dev->chip_id < CHIP_RK3588_CIF)
return;
stream->last_frame_idx = stream->frame_idx;
} else {
ret = rkcif_update_new_buffer_wake_up_mode(stream);
if (ret && cif_dev->chip_id < CHIP_RK3588_CIF)
@@ -12318,7 +12335,7 @@ static void rkcif_update_stream_toisp(struct rkcif_device *cif_dev,
stream->fps_stats.frm1_timestamp = rkcif_time_get_ns(cif_dev);
spin_unlock(&stream->fps_lock);
if (cif_dev->inf_id == RKCIF_MIPI_LVDS)
if (!cif_dev->sditf[0] || cif_dev->sditf[0]->mode.rdbk_mode >= RKISP_VICAP_RDBK_AIQ)
rkcif_deal_readout_time(stream);
if (!stream->is_line_wake_up)
@@ -12355,7 +12372,7 @@ static void rkcif_update_stream_rockit(struct rkcif_device *cif_dev,
spin_unlock_irqrestore(&stream->fps_lock, flags);
}
if (cif_dev->inf_id == RKCIF_MIPI_LVDS)
if (!cif_dev->sditf[0] || cif_dev->sditf[0]->mode.rdbk_mode >= RKISP_VICAP_RDBK_AIQ)
rkcif_deal_readout_time(stream);
rkcif_assign_new_buffer_pingpong_rockit(stream,
@@ -13347,7 +13364,7 @@ void rkcif_enable_dma_capture(struct rkcif_stream *stream, bool is_only_enable)
else if (cif_dev->chip_id < CHIP_RK3576_CIF)
rkcif_write_register_or(cif_dev, CIF_REG_DVP_VIR_LINE_WIDTH, BIT(28) << stream->id);
else
rkcif_write_register_or(cif_dev, CIF_REG_DVP_VIR_LINE_WIDTH, BIT(31));
rkcif_write_register_or(cif_dev, get_dvp_reg_index_of_vlw(stream->id), BIT(31));
}
if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY ||
mbus_cfg->type == V4L2_MBUS_CSI2_CPHY) {
@@ -13442,16 +13459,22 @@ static int rkcif_stop_dma_capture(struct rkcif_stream *stream)
}
rkcif_write_register(cif_dev, get_reg_index_of_lvds_id_ctrl0(stream->id), val);
} else {
val = rkcif_read_register(cif_dev, CIF_REG_DVP_CTRL);
if (cif_dev->chip_id == CHIP_RK3588_CIF)
val &= ~DVP_DMA_EN;
else if (cif_dev->chip_id == CHIP_RV1106_CIF)
val &= ~(DVP_SW_DMA_EN(stream->id));
if (stream->is_stop_capture) {
val &= ~ENABLE_CAPTURE;
stream->is_stop_capture = false;
if (stream->cifdev->chip_id >= CHIP_RV1126B_CIF) {
val = rkcif_read_register(cif_dev, get_dvp_reg_index_of_id_ctrl0(stream->id));
val &= ~(CSI_ENABLE_CAPTURE | CSI_DMA_ENABLE_RK3576);
rkcif_write_register(cif_dev, get_dvp_reg_index_of_id_ctrl0(stream->id), val);
} else {
val = rkcif_read_register(cif_dev, CIF_REG_DVP_CTRL);
if (cif_dev->chip_id == CHIP_RK3588_CIF)
val &= ~DVP_DMA_EN;
else if (cif_dev->chip_id == CHIP_RV1106_CIF)
val &= ~(DVP_SW_DMA_EN(stream->id));
if (stream->is_stop_capture) {
val &= ~ENABLE_CAPTURE;
stream->is_stop_capture = false;
}
rkcif_write_register(cif_dev, CIF_REG_DVP_CTRL, val);
}
rkcif_write_register(cif_dev, CIF_REG_DVP_CTRL, val);
}
stream->to_stop_dma = 0;
v4l2_dbg(4, rkcif_debug, &cif_dev->v4l2_dev,
@@ -13913,7 +13936,6 @@ static void rkcif_toisp_check_stop_status(struct sditf_priv *priv,
int src_id = 0;
int i = 0;
u32 val = 0;
u64 cur_time = 0;
int on = 0;
unsigned long flags;
struct v4l2_subdev *sd = NULL;
@@ -13946,12 +13968,6 @@ static void rkcif_toisp_check_stop_status(struct sditf_priv *priv,
stream->stopping = false;
wake_up(&stream->wq_stopped);
}
if (!(stream->cur_stream_mode & RKCIF_STREAM_MODE_CAPTURE)) {
cur_time = rkcif_time_get_ns(stream->cifdev);
stream->readout.total_time = cur_time - stream->readout.fe_timestamp;
stream->readout.readout_time = cur_time - stream->readout.fs_timestamp;
stream->readout.fe_timestamp = cur_time;
}
spin_lock_irqsave(&stream->cifdev->stream_spinlock, flags);
if (stream->is_wait_stop_complete) {
@@ -14026,6 +14042,9 @@ static void rkcif_toisp_check_stop_status(struct sditf_priv *priv,
if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE_UNITE ||
priv->mode.rdbk_mode == RKISP_VICAP_ONLINE_MULTI)
priv->is_toisp_off = true;
rkcif_deal_readout_time(stream);
switch (ch) {
case RKCIF_TOISP_CH0:
if (priv->cif_dev->chip_id < CHIP_RK3576_CIF)
@@ -14250,6 +14269,7 @@ static void rkcif_deal_sof(struct rkcif_device *cif_dev)
detect_stream->fs_cnt_in_single_frame++;
if ((!cif_dev->sditf[0] ||
cif_dev->sditf[0]->mode.rdbk_mode >= RKISP_VICAP_RDBK_AIQ) &&
cif_dev->inf_id == RKCIF_MIPI_LVDS &&
detect_stream->fs_cnt_in_single_frame > 1 &&
cif_dev->chip_id < CHIP_RK3588_CIF)
return;
@@ -15496,6 +15516,7 @@ void rkcif_irq_pingpong_v1(struct rkcif_device *cif_dev)
stream->dma_en);
rkcif_update_stream_rockit(cif_dev, stream, mipi_id);
}
stream->last_frame_idx = stream->frame_idx;
spin_lock_irqsave(&stream->cifdev->stream_spinlock, flags);
if (stream->is_single_cap && !stream->cur_skip_frame) {
stream->is_single_cap = false;
@@ -15751,7 +15772,7 @@ void rkcif_irq_pingpong_v1(struct rkcif_device *cif_dev)
cif_dev->sditf[0]->mode.rdbk_mode >= RKISP_VICAP_RDBK_AIQ)
stream->buf_wake_up_cnt++;
if (stream->stopping) {
if (stream->stopping && (!stream->dma_en)) {
rkcif_stream_stop(stream);
stream->stopping = false;
wake_up(&stream->wq_stopped);
@@ -15816,6 +15837,23 @@ void rkcif_irq_pingpong_v1(struct rkcif_device *cif_dev)
spin_unlock_irqrestore(&stream->fps_lock, flags);
}
stream->is_in_vblank = false;
spin_lock_irqsave(&stream->vbq_lock, flags);
if (stream->stopping && stream->dma_en) {
if (stream->dma_en & RKCIF_DMAEN_BY_VICAP)
stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP;
else if (stream->dma_en & RKCIF_DMAEN_BY_ISP)
stream->to_stop_dma = RKCIF_DMAEN_BY_ISP;
stream->is_stop_capture = true;
}
if (stream->to_stop_dma) {
ret = rkcif_stop_dma_capture(stream);
if (!ret) {
stream->is_finish_stop_dma = true;
if (stream->is_wait_stop_complete)
stream->is_pause_stream = true;
}
}
spin_unlock_irqrestore(&stream->vbq_lock, flags);
}
}

View File

@@ -364,6 +364,7 @@ struct rkcif_fps_stats {
* @readout_time: one frame of readout time
* @early_time: early time of buf send to user
* @total_time: totaltime of readout time in hdr
* @rate_time: single frame interval
*/
struct rkcif_readout_stats {
u64 fs_timestamp;
@@ -372,6 +373,7 @@ struct rkcif_readout_stats {
u64 readout_time;
u64 early_time;
u64 total_time;
u64 rate_time;
};
/* struct rkcif_irq_stats - take notes on irq number

View File

@@ -604,7 +604,7 @@ static void rkcif_show_format(struct rkcif_device *dev, struct seq_file *f)
timestamp1 = stream->fps_stats.frm1_timestamp;
spin_unlock_irqrestore(&stream->fps_lock, flags);
if (dev->sditf[0] && dev->sditf[0]->mode.rdbk_mode < RKISP_VICAP_RDBK_AIQ)
fps = dev->stream[0].readout.total_time;
fps = dev->stream[0].readout.rate_time;
else
fps = timestamp0 > timestamp1 ?
timestamp0 - timestamp1 : timestamp1 - timestamp0;
@@ -638,8 +638,9 @@ static void rkcif_show_format(struct rkcif_device *dev, struct seq_file *f)
}
time_val = div_u64_rem(fps, 1000, &remainder);
seq_printf(f, "\trate:%u.%u ms\n", time_val, remainder);
fps = div_u64(1000000, fps);
seq_printf(f, "\tfps:%llu\n", fps);
fps = div_u64(1000000000, fps);
time_val = div_u64_rem(fps, 1000, &remainder);
seq_printf(f, "\tfps:%u.%u \n", time_val, remainder);
seq_puts(f, "\tirq statistics:\n");
seq_printf(f, "\t\t\ttotal:%llu\n",
dev->irq_stats.frm_end_cnt[0] +

View File

@@ -789,6 +789,18 @@ static int rk806_parse_dt(struct rk806 *rk806)
if (device_property_read_bool(dev, "vdc-wakeup-enable"))
pdata->vdc_wakeup_enable = 1;
ret = device_property_read_u32(dev,
"shutown_by_pwrctrln",
&pdata->shutown_by_pwrctrln);
if (ret < 0) {
dev_info(dev, "shutown_by_pwrctrln missing!\n");
pdata->shutown_by_pwrctrln = 1;
}
if ((pdata->shutown_by_pwrctrln < 1) || (pdata->shutown_by_pwrctrln > 3)) {
dev_err(dev, "shutown_by_pwrctrln out [1 3]!\n");
pdata->shutown_by_pwrctrln = 1;
}
pdata->shutdown_sequence = devm_kzalloc(dev,
RK806_ID_END * sizeof(int),
GFP_KERNEL);

View File

@@ -26,11 +26,11 @@ config CAN_RK3562
To compile this driver as a module, choose M here: the module will
be called rk3562_can.
config CANFD_RK3576
tristate "RK3576 CANFD controller"
config CAN_RK3576
tristate "RK3576 CAN controller"
depends on ARCH_ROCKCHIP
help
Say Y here if you want to use CANFD controller found on RK3576 SoCs.
Say Y here if you want to use CAN controller found on RK3576 SoCs.
To compile this driver as a module, choose M here: the module will
be called rk3576_canfd.
be called rk3576_can.

View File

@@ -1,9 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the rockchip can and canfd controller driver.
#
obj-$(CONFIG_CAN_ROCKCHIP) += rockchip_can.o
obj-$(CONFIG_CANFD_ROCKCHIP) += rockchip_canfd.o
obj-$(CONFIG_CAN_RK3562) += rk3562_can.o
obj-$(CONFIG_CANFD_RK3576) += rk3576_canfd.o
obj-$(CONFIG_CAN_RK3576) += rk3576_can.o

View File

@@ -59,6 +59,7 @@ struct rockchip_p3phy_priv {
struct clk_bulk_data *clks;
int num_clks;
bool is_bifurcation;
bool is_initialized;
};
struct rockchip_p3phy_ops {
@@ -215,6 +216,9 @@ static int rockchip_p3phy_init(struct phy *phy)
return ret;
}
if (priv->is_initialized)
return 0;
reset_control_assert(priv->p30phy);
udelay(1);
@@ -233,6 +237,7 @@ static int rockchip_p3phy_exit(struct phy *phy)
clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
reset_control_assert(priv->p30phy);
priv->is_initialized = false;
return 0;
}
@@ -283,33 +288,38 @@ static int rockchip_p3phy_probe(struct platform_device *pdev)
if (IS_ERR(priv->pipe_grf))
dev_info(dev, "failed to find rockchip,pipe_grf regmap\n");
/* Configuring grf with clk enabled. */
ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
if (ret) {
pr_err("failed to enable PCIe bulk clks %d\n", ret);
return ret;
}
priv->is_initialized = device_property_read_bool(dev, "rockchip,skip-init");
ret = device_property_read_u32(dev, "rockchip,pcie30-phymode", &val);
if (!ret) {
priv->pcie30_phymode = val;
if (priv->pcie30_phymode > 4)
priv->pcie30_phymode = PHY_MODE_PCIE_AGGREGATION;
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0,
(0x7<<16) | priv->pcie30_phymode);
} else {
priv->pcie30_phymode = PHY_MODE_PCIE_AGGREGATION;
}
/* Set pcie1ln_sel in PHP_GRF_PCIESEL_CON */
if (!IS_ERR(priv->pipe_grf)) {
reg = priv->pcie30_phymode & 3;
if (reg)
regmap_write(priv->pipe_grf, PHP_GRF_PCIESEL_CON,
(reg << 16) | reg);
};
if (!priv->is_initialized) {
ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
if (ret) {
pr_err("failed to enable PCIe bulk clks %d\n", ret);
return ret;
}
clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
if (priv->pcie30_phymode != PHY_MODE_PCIE_AGGREGATION)
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0,
(0x7 << 16) | priv->pcie30_phymode);
/* Set pcie1ln_sel in PHP_GRF_PCIESEL_CON */
if (!IS_ERR(priv->pipe_grf)) {
reg = priv->pcie30_phymode & 3;
if (reg)
regmap_write(priv->pipe_grf, PHP_GRF_PCIESEL_CON,
(reg << 16) | reg);
};
clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
}
priv->phy = devm_phy_create(dev, NULL, &rockchip_p3phy_ops);
if (IS_ERR(priv->phy)) {

View File

@@ -106,7 +106,7 @@ static const struct regmap_access_table sc89601_writeable_regs = {
static const struct regmap_range sc89601_volatile_reg_ranges[] = {
regmap_reg_range(0x00, 0x00),
regmap_reg_range(0x02, 0x02),
regmap_reg_range(0x09, 0x09),
regmap_reg_range(0x08, 0x09),
regmap_reg_range(0x0b, 0x0b),
regmap_reg_range(0x0c, 0x0c),
regmap_reg_range(0x0d, 0x14),

View File

@@ -1252,9 +1252,19 @@ static void rk806_regulator_shutdown(struct platform_device *pdev)
if (system_state == SYSTEM_POWER_OFF) {
rk806_shutdown_requence_config(rk806);
rk806_field_write(rk806, PWRCTRL1_FUN, PWRCTRL_NULL_FUN);
rk806_field_write(rk806, PWRCTRL1_POL, POL_HIGH);
rk806_field_write(rk806, PWRCTRL1_FUN, PWRCTRL_POWOFF_FUN);
if (rk806->pdata->shutown_by_pwrctrln == 2) {
rk806_field_write(rk806, PWRCTRL2_FUN, PWRCTRL_NULL_FUN);
rk806_field_write(rk806, PWRCTRL2_POL, POL_HIGH);
rk806_field_write(rk806, PWRCTRL2_FUN, PWRCTRL_POWOFF_FUN);
} else if (rk806->pdata->shutown_by_pwrctrln == 3) {
rk806_field_write(rk806, PWRCTRL3_FUN, PWRCTRL_NULL_FUN);
rk806_field_write(rk806, PWRCTRL3_POL, POL_HIGH);
rk806_field_write(rk806, PWRCTRL3_FUN, PWRCTRL_POWOFF_FUN);
} else {
rk806_field_write(rk806, PWRCTRL1_FUN, PWRCTRL_NULL_FUN);
rk806_field_write(rk806, PWRCTRL1_POL, POL_HIGH);
rk806_field_write(rk806, PWRCTRL1_FUN, PWRCTRL_POWOFF_FUN);
}
}
}

View File

@@ -619,7 +619,7 @@ static int rk630_rtc_probe(struct platform_device *pdev)
return ret;
}
return rtc_register_device(rk630_rtc->rtc);
return devm_rtc_register_device(rk630_rtc->rtc);
}
static struct platform_driver rk630_rtc_driver = {

View File

@@ -1010,12 +1010,6 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
if (sfc->max_dll_cells > SFC_DLL_CTRL0_DLL_MAX_VER5)
sfc->max_dll_cells = SFC_DLL_CTRL0_DLL_MAX_VER5;
ret = rockchip_sfc_get_gpio_descs(master, sfc);
if (ret) {
dev_err(&pdev->dev, "Failed to get gpio_descs\n");
return ret;
}
ret = clk_prepare_enable(sfc->hclk);
if (ret) {
dev_err(&pdev->dev, "Failed to enable ahb clk\n");
@@ -1063,6 +1057,12 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
dev_err(dev, "Wait for SFC idle timeout!\n");
}
ret = rockchip_sfc_get_gpio_descs(master, sfc);
if (ret) {
dev_err(&pdev->dev, "Failed to get gpio_descs\n");
goto err_irq;
}
ret = rockchip_sfc_init(sfc);
if (ret)
goto err_irq;

View File

@@ -505,6 +505,7 @@ struct rk806_platform_data {
int hotdie_temperture_threshold;
int vdc_wakeup_enable;
int shutown_by_pwrctrln;
int *shutdown_sequence;
int *vb_shutdown_sequence;
int *dvs_control_suspend;

View File

@@ -7,6 +7,7 @@
#ifndef _UAPI_RK_HDMIRX_CONFIG_H
#define _UAPI_RK_HDMIRX_CONFIG_H
#include <drm/drm_mode.h>
#include <linux/types.h>
#include <linux/v4l2-controls.h>
@@ -119,6 +120,15 @@ enum user_color_range {
#define RK_HDMIRX_CMD_SET_EDID_VERSION \
_IOW('V', BASE_VIDIOC_PRIVATE + 17, int)
#define RK_HDMIRX_CMD_GET_HDR_METADATA \
_IOR('V', BASE_VIDIOC_PRIVATE + 18, struct hdr_metadata_infoframe)
#define RK_HDMIRX_CMD_GET_OUTPUT_COLOR_RANGE \
_IOR('V', BASE_VIDIOC_PRIVATE + 19, int)
#define RK_HDMIRX_CMD_GET_OUTPUT_COLOR_SPACE \
_IOR('V', BASE_VIDIOC_PRIVATE + 20, int)
/* Private v4l2 event */
#define RK_HDMIRX_V4L2_EVENT_SIGNAL_LOST \
(V4L2_EVENT_PRIVATE_START + 1)