* [PATCH v2 0/2] Add initial dual front camera and rear flash support for Pixel 3 / 3 XL
From: David Heidelberg via B4 Relay @ 2026-04-11 10:12 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Petr Hodina, Richard Acayan, linux-arm-msm, devicetree,
linux-kernel, phone-devel, David Heidelberg
Describe the dual front-facing IMX355 sensors (standard and wide)
and enable the PMI8998 flash LED with hardware-accurate limits.
This brings up the basic camera topology and flash support in DT.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
Changes in v2:
- leds.h include escaped the initial submission. Fixed.
- Link to v1: https://lore.kernel.org/r/20260411-pixel3-camera-v1-0-2757606515b6@ixit.cz
---
David Heidelberg (2):
arm64: dts: qcom: sdm845-google: Add dual front IMX355 cameras
arm64: dts: qcom: sdm845-google: Enable PMI8998 camera flash LED
arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi | 201 ++++++++++++++++++++-
1 file changed, 200 insertions(+), 1 deletion(-)
---
base-commit: db7efce4ae23ad5e42f5f55428f529ff62b86fab
change-id: 20260315-pixel3-camera-a9989bf589ee
Best regards,
--
David Heidelberg <david@ixit.cz>
^ permalink raw reply
* [PATCH v2 2/2] arm64: dts: qcom: sdm845-google: Enable PMI8998 camera flash LED
From: David Heidelberg via B4 Relay @ 2026-04-11 10:12 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Petr Hodina, Richard Acayan, linux-arm-msm, devicetree,
linux-kernel, phone-devel, David Heidelberg
In-Reply-To: <20260411-pixel3-camera-v2-0-41b889abb14c@ixit.cz>
From: David Heidelberg <david@ixit.cz>
Enable the PMI8998 flash LED block and describe the white flash LED
used for the rear camera.
Configure the LED in flash mode with hardware limits matching the
original device configuration, including maximum current and timeout.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
index 084058a597c10..44b6d61697caf 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
@@ -6,6 +6,7 @@
#include <dt-bindings/dma/qcom-gpi.h>
#include <dt-bindings/input/linux-event-codes.h>
#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/leds/common.h>
#include <dt-bindings/media/video-interfaces.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
@@ -590,6 +591,19 @@ &pmi8998_charger {
status = "okay";
};
+&pmi8998_flash {
+ status = "okay";
+
+ led-1 {
+ function = LED_FUNCTION_FLASH;
+ color = <LED_COLOR_ID_WHITE>;
+ led-sources = <2>;
+ led-max-microamp = <500000>;
+ flash-max-microamp = <750000>;
+ flash-max-timeout-us = <1280000>;
+ };
+};
+
&qupv3_id_0 {
status = "okay";
};
--
2.53.0
^ permalink raw reply related
* [PATCH v2 1/2] arm64: dts: qcom: sdm845-google: Add dual front IMX355 cameras
From: David Heidelberg via B4 Relay @ 2026-04-11 10:12 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Petr Hodina, Richard Acayan, linux-arm-msm, devicetree,
linux-kernel, phone-devel, David Heidelberg
In-Reply-To: <20260411-pixel3-camera-v2-0-41b889abb14c@ixit.cz>
From: David Heidelberg <david@ixit.cz>
The Pixel 3 features two front-facing Sony IMX355 sensors with
different focal lengths (standard and wide-angle).
Both sensors are connected via CSIPHY1 and controlled over CCI I2C1,
using MCLK2 as the clock source. Describe the camera nodes and
associated resources in the device tree.
This enables support for the dual front camera configuration.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi | 187 ++++++++++++++++++++-
1 file changed, 186 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
index 6930066857768..084058a597c10 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
@@ -6,6 +6,7 @@
#include <dt-bindings/dma/qcom-gpi.h>
#include <dt-bindings/input/linux-event-codes.h>
#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/media/video-interfaces.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
#include "sdm845.dtsi"
@@ -132,6 +133,38 @@ vreg_s4a_1p8: regulator-vreg-s4a-1p8 {
vin-supply = <&vph_pwr>;
};
+ camera_front_avdd: front-cam-avdd-regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "front_cam_avdd";
+
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+
+ gpios = <&tlmm 8 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+
+ pinctrl-0 = <&cam_front_avdd_default_pin>;
+ pinctrl-names = "default";
+
+ vin-supply = <&vreg_bob>;
+ };
+
+ camera_front_aux_avdd: front-cam-aux-avdd-regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "front_cam_aux_avdd";
+
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+
+ gpios = <&tlmm 14 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+
+ pinctrl-0 = <&cam_front_aux_avdd_default_pin>;
+ pinctrl-names = "default";
+
+ vin-supply = <&vreg_bob>;
+ };
+
wcn3990-pmu {
compatible = "qcom,wcn3990-pmu";
@@ -214,6 +247,9 @@ vreg_s7a_1p025: smps7 {
regulator-max-microvolt = <1028000>;
};
+ vdda_mipi_csi0_0p9:
+ vdda_mipi_csi1_0p9:
+ vdda_mipi_csi2_0p9:
vdda_mipi_dsi0_pll:
vreg_l1a_0p875: ldo1 {
regulator-min-microvolt = <880000>;
@@ -288,6 +324,13 @@ vreg_l21a_2p95: ldo21 {
regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
};
+ vreg_l22a_3p3: ldo22 {
+ regulator-min-microvolt = <2864000>;
+ regulator-max-microvolt = <2864000>;
+
+ regulator-boot-on;
+ };
+
vreg_l24a_3p075: ldo24 {
regulator-min-microvolt = <3088000>;
regulator-max-microvolt = <3088000>;
@@ -319,6 +362,12 @@ vreg_l28a_3p0: ldo28 {
*/
regulator-always-on;
};
+
+ cam_vio_1p8:
+ vreg_lvs1_1p8: lvs1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
};
regulators-1 {
@@ -351,6 +400,45 @@ vreg_s3c_0p6: smps3 {
};
};
+&camss {
+ vdda-phy-supply = <&vreg_l1a_0p875>;
+ vdda-pll-supply = <&vreg_l26a_1p2>;
+
+ vdda-csi0-supply = <&vdda_mipi_csi0_0p9>;
+ vdda-csi1-supply = <&vdda_mipi_csi1_0p9>;
+ vdda-csi2-supply = <&vdda_mipi_csi2_0p9>;
+
+ status = "okay";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@1 {
+ reg = <1>;
+ camss_endpoint1: endpoint {
+ bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
+ data-lanes = <0 1 2 3>;
+ remote-endpoint = <&cam_aux_front_endpoint>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ camss_endpoint2: endpoint {
+ bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
+ data-lanes = <0 1 2 3>;
+ remote-endpoint = <&cam_front_endpoint>;
+ };
+ };
+ };
+};
+
+&cci0_sleep {
+ /* bus has external pull-up, don't pull down */
+ bias-disable;
+};
+
&cci {
status = "okay";
};
@@ -358,7 +446,72 @@ &cci {
&cci_i2c1 {
/* actuator @0c */
- /* front camera, imx355 @1a */
+ front_cam: camera@10 {
+ compatible = "sony,imx355";
+ reg = <0x10>;
+
+ clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ assigned-clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ /*
+ * The sensor can accept a 24 MHz clock, but 19.2 MHz has
+ * better driver compatibility.
+ */
+ assigned-clock-rates = <19200000>;
+
+ reset-gpios = <&tlmm 21 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&camera_front_avdd>;
+ dvdd-supply = <&vreg_s3a_1p35>;
+ dovdd-supply = <&cam_vio_1p8>;
+
+ /* MCLK2 pin (gpio15) is claimed by the aux sensor */
+ pinctrl-0 = <&cam_front_reset_default_pin>;
+ pinctrl-names = "default";
+
+ rotation = <270>;
+ orientation = <0>;
+
+ port {
+ cam_front_endpoint: endpoint {
+ data-lanes = <1 2 3 4>;
+ link-frequencies = /bits/ 64 <360000000>;
+ remote-endpoint = <&camss_endpoint2>;
+ };
+ };
+ };
+
+ front_aux_cam: camera@1a {
+ compatible = "sony,imx355";
+ reg = <0x1a>;
+
+ clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ assigned-clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ /*
+ * The sensor can accept a 24 MHz clock, but 19.2 MHz has
+ * better driver compatibility.
+ */
+ assigned-clock-rates = <19200000>;
+
+ reset-gpios = <&tlmm 9 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&camera_front_aux_avdd>;
+ dvdd-supply = <&vreg_s3a_1p35>;
+ dovdd-supply = <&cam_vio_1p8>;
+
+ pinctrl-0 = <&cam_mclk2_default &cam_front_aux_reset_default_pin>;
+ pinctrl-names = "default";
+
+ rotation = <270>;
+ orientation = <0>;
+
+ port {
+ cam_aux_front_endpoint: endpoint {
+ data-lanes = <1 2 3 4>;
+ link-frequencies = /bits/ 64 <360000000>;
+ remote-endpoint = <&camss_endpoint1>;
+ };
+ };
+ };
/* eeprom @50, at24 driver says 8K */
};
@@ -459,6 +612,38 @@ &tlmm {
gpio-reserved-ranges = < 0 4>, /* SPI (Intel MNH Pixel Visual Core) */
<81 4>; /* SPI (most likely Fingerprint Cards FPC1075) */
+ cam_front_avdd_default_pin: cam-avdd-default-pins {
+ pins = "gpio8";
+ function = "gpio";
+
+ bias-disable;
+ drive-strength = <2>;
+ };
+
+ cam_front_aux_reset_default_pin: cam-front-aux-reset-default-pins {
+ pins = "gpio9";
+ function = "gpio";
+
+ bias-disable;
+ drive-strength = <2>;
+ };
+
+ cam_front_aux_avdd_default_pin: cam-avdd-aux-default-pins {
+ pins = "gpio14";
+ function = "gpio";
+
+ bias-disable;
+ drive-strength = <2>;
+ };
+
+ cam_front_reset_default_pin: cam-front-reset-default-pins {
+ pins = "gpio21";
+ function = "gpio";
+
+ bias-disable;
+ drive-strength = <2>;
+ };
+
touchscreen_reset: ts-reset-state {
pins = "gpio99";
function = "gpio";
--
2.53.0
^ permalink raw reply related
* [PATCH 1/2] arm64: dts: qcom: sdm845-google: Add dual front IMX355 cameras
From: David Heidelberg via B4 Relay @ 2026-04-11 10:09 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Petr Hodina, Richard Acayan, linux-arm-msm, devicetree,
linux-kernel, phone-devel, David Heidelberg
In-Reply-To: <20260411-pixel3-camera-v1-0-2757606515b6@ixit.cz>
From: David Heidelberg <david@ixit.cz>
The Pixel 3 features two front-facing Sony IMX355 sensors with
different focal lengths (standard and wide-angle).
Both sensors are connected via CSIPHY1 and controlled over CCI I2C1,
using MCLK2 as the clock source. Describe the camera nodes and
associated resources in the device tree.
This enables support for the dual front camera configuration.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi | 187 ++++++++++++++++++++-
1 file changed, 186 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
index 6930066857768..084058a597c10 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
@@ -6,6 +6,7 @@
#include <dt-bindings/dma/qcom-gpi.h>
#include <dt-bindings/input/linux-event-codes.h>
#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/media/video-interfaces.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
#include "sdm845.dtsi"
@@ -132,6 +133,38 @@ vreg_s4a_1p8: regulator-vreg-s4a-1p8 {
vin-supply = <&vph_pwr>;
};
+ camera_front_avdd: front-cam-avdd-regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "front_cam_avdd";
+
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+
+ gpios = <&tlmm 8 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+
+ pinctrl-0 = <&cam_front_avdd_default_pin>;
+ pinctrl-names = "default";
+
+ vin-supply = <&vreg_bob>;
+ };
+
+ camera_front_aux_avdd: front-cam-aux-avdd-regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "front_cam_aux_avdd";
+
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+
+ gpios = <&tlmm 14 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+
+ pinctrl-0 = <&cam_front_aux_avdd_default_pin>;
+ pinctrl-names = "default";
+
+ vin-supply = <&vreg_bob>;
+ };
+
wcn3990-pmu {
compatible = "qcom,wcn3990-pmu";
@@ -214,6 +247,9 @@ vreg_s7a_1p025: smps7 {
regulator-max-microvolt = <1028000>;
};
+ vdda_mipi_csi0_0p9:
+ vdda_mipi_csi1_0p9:
+ vdda_mipi_csi2_0p9:
vdda_mipi_dsi0_pll:
vreg_l1a_0p875: ldo1 {
regulator-min-microvolt = <880000>;
@@ -288,6 +324,13 @@ vreg_l21a_2p95: ldo21 {
regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
};
+ vreg_l22a_3p3: ldo22 {
+ regulator-min-microvolt = <2864000>;
+ regulator-max-microvolt = <2864000>;
+
+ regulator-boot-on;
+ };
+
vreg_l24a_3p075: ldo24 {
regulator-min-microvolt = <3088000>;
regulator-max-microvolt = <3088000>;
@@ -319,6 +362,12 @@ vreg_l28a_3p0: ldo28 {
*/
regulator-always-on;
};
+
+ cam_vio_1p8:
+ vreg_lvs1_1p8: lvs1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
};
regulators-1 {
@@ -351,6 +400,45 @@ vreg_s3c_0p6: smps3 {
};
};
+&camss {
+ vdda-phy-supply = <&vreg_l1a_0p875>;
+ vdda-pll-supply = <&vreg_l26a_1p2>;
+
+ vdda-csi0-supply = <&vdda_mipi_csi0_0p9>;
+ vdda-csi1-supply = <&vdda_mipi_csi1_0p9>;
+ vdda-csi2-supply = <&vdda_mipi_csi2_0p9>;
+
+ status = "okay";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@1 {
+ reg = <1>;
+ camss_endpoint1: endpoint {
+ bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
+ data-lanes = <0 1 2 3>;
+ remote-endpoint = <&cam_aux_front_endpoint>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ camss_endpoint2: endpoint {
+ bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
+ data-lanes = <0 1 2 3>;
+ remote-endpoint = <&cam_front_endpoint>;
+ };
+ };
+ };
+};
+
+&cci0_sleep {
+ /* bus has external pull-up, don't pull down */
+ bias-disable;
+};
+
&cci {
status = "okay";
};
@@ -358,7 +446,72 @@ &cci {
&cci_i2c1 {
/* actuator @0c */
- /* front camera, imx355 @1a */
+ front_cam: camera@10 {
+ compatible = "sony,imx355";
+ reg = <0x10>;
+
+ clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ assigned-clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ /*
+ * The sensor can accept a 24 MHz clock, but 19.2 MHz has
+ * better driver compatibility.
+ */
+ assigned-clock-rates = <19200000>;
+
+ reset-gpios = <&tlmm 21 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&camera_front_avdd>;
+ dvdd-supply = <&vreg_s3a_1p35>;
+ dovdd-supply = <&cam_vio_1p8>;
+
+ /* MCLK2 pin (gpio15) is claimed by the aux sensor */
+ pinctrl-0 = <&cam_front_reset_default_pin>;
+ pinctrl-names = "default";
+
+ rotation = <270>;
+ orientation = <0>;
+
+ port {
+ cam_front_endpoint: endpoint {
+ data-lanes = <1 2 3 4>;
+ link-frequencies = /bits/ 64 <360000000>;
+ remote-endpoint = <&camss_endpoint2>;
+ };
+ };
+ };
+
+ front_aux_cam: camera@1a {
+ compatible = "sony,imx355";
+ reg = <0x1a>;
+
+ clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ assigned-clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ /*
+ * The sensor can accept a 24 MHz clock, but 19.2 MHz has
+ * better driver compatibility.
+ */
+ assigned-clock-rates = <19200000>;
+
+ reset-gpios = <&tlmm 9 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&camera_front_aux_avdd>;
+ dvdd-supply = <&vreg_s3a_1p35>;
+ dovdd-supply = <&cam_vio_1p8>;
+
+ pinctrl-0 = <&cam_mclk2_default &cam_front_aux_reset_default_pin>;
+ pinctrl-names = "default";
+
+ rotation = <270>;
+ orientation = <0>;
+
+ port {
+ cam_aux_front_endpoint: endpoint {
+ data-lanes = <1 2 3 4>;
+ link-frequencies = /bits/ 64 <360000000>;
+ remote-endpoint = <&camss_endpoint1>;
+ };
+ };
+ };
/* eeprom @50, at24 driver says 8K */
};
@@ -459,6 +612,38 @@ &tlmm {
gpio-reserved-ranges = < 0 4>, /* SPI (Intel MNH Pixel Visual Core) */
<81 4>; /* SPI (most likely Fingerprint Cards FPC1075) */
+ cam_front_avdd_default_pin: cam-avdd-default-pins {
+ pins = "gpio8";
+ function = "gpio";
+
+ bias-disable;
+ drive-strength = <2>;
+ };
+
+ cam_front_aux_reset_default_pin: cam-front-aux-reset-default-pins {
+ pins = "gpio9";
+ function = "gpio";
+
+ bias-disable;
+ drive-strength = <2>;
+ };
+
+ cam_front_aux_avdd_default_pin: cam-avdd-aux-default-pins {
+ pins = "gpio14";
+ function = "gpio";
+
+ bias-disable;
+ drive-strength = <2>;
+ };
+
+ cam_front_reset_default_pin: cam-front-reset-default-pins {
+ pins = "gpio21";
+ function = "gpio";
+
+ bias-disable;
+ drive-strength = <2>;
+ };
+
touchscreen_reset: ts-reset-state {
pins = "gpio99";
function = "gpio";
--
2.53.0
^ permalink raw reply related
* [PATCH 0/2] Add initial dual front camera and rear flash support for Pixel 3 / 3 XL
From: David Heidelberg via B4 Relay @ 2026-04-11 10:09 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Petr Hodina, Richard Acayan, linux-arm-msm, devicetree,
linux-kernel, phone-devel, David Heidelberg
Describe the dual front-facing IMX355 sensors (standard and wide)
and enable the PMI8998 flash LED with hardware-accurate limits.
This brings up the basic camera topology and flash support in DT.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
David Heidelberg (2):
arm64: dts: qcom: sdm845-google: Add dual front IMX355 cameras
arm64: dts: qcom: sdm845-google: Enable PMI8998 camera flash LED
arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi | 200 ++++++++++++++++++++-
1 file changed, 199 insertions(+), 1 deletion(-)
---
base-commit: db7efce4ae23ad5e42f5f55428f529ff62b86fab
change-id: 20260315-pixel3-camera-a9989bf589ee
Best regards,
--
David Heidelberg <david@ixit.cz>
^ permalink raw reply
* [PATCH 2/2] arm64: dts: qcom: sdm845-google: Enable PMI8998 camera flash LED
From: David Heidelberg via B4 Relay @ 2026-04-11 10:09 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Petr Hodina, Richard Acayan, linux-arm-msm, devicetree,
linux-kernel, phone-devel, David Heidelberg
In-Reply-To: <20260411-pixel3-camera-v1-0-2757606515b6@ixit.cz>
From: David Heidelberg <david@ixit.cz>
Enable the PMI8998 flash LED block and describe the white flash LED
used for the rear camera.
Configure the LED in flash mode with hardware limits matching the
original device configuration, including maximum current and timeout.
Signed-off-by: David Heidelberg <david@ixit.cz>
---
arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
index 084058a597c10..3aa164c34f43f 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-google-common.dtsi
@@ -590,6 +590,19 @@ &pmi8998_charger {
status = "okay";
};
+&pmi8998_flash {
+ status = "okay";
+
+ led-1 {
+ function = LED_FUNCTION_FLASH;
+ color = <LED_COLOR_ID_WHITE>;
+ led-sources = <2>;
+ led-max-microamp = <500000>;
+ flash-max-microamp = <750000>;
+ flash-max-timeout-us = <1280000>;
+ };
+};
+
&qupv3_id_0 {
status = "okay";
};
--
2.53.0
^ permalink raw reply related
* [PATCH v7 2/2] i2c: ls2x-v2: Add driver for Loongson-2K0300 I2C controller
From: Binbin Zhou @ 2026-04-11 9:58 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Andi Shyti, Wolfram Sang, Andy Shevchenko,
linux-i2c
Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, Binbin Zhou,
Andy Shevchenko
In-Reply-To: <cover.1775900045.git.zhoubinbin@loongson.cn>
This I2C module is integrated into the Loongson-2K0300 SoCs.
It provides multi-master functionality and controls all I2C bus-specific
timing, protocols, arbitration, and timing. It supports both standard
and fast modes.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Reviewed-by: Huacai Chen <chenhuacai@loongson.cn>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
MAINTAINERS | 1 +
drivers/i2c/busses/Kconfig | 11 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-ls2x-v2.c | 544 +++++++++++++++++++++++++++++++
4 files changed, 557 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-ls2x-v2.c
diff --git a/MAINTAINERS b/MAINTAINERS
index c3fe46d7c4bc..b35d1891abbb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14965,6 +14965,7 @@ M: Binbin Zhou <zhoubinbin@loongson.cn>
L: linux-i2c@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
+F: drivers/i2c/busses/i2c-ls2x-v2.c
F: drivers/i2c/busses/i2c-ls2x.c
LOONGSON PWM DRIVER
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 8c935f867a37..ea3e7e92465d 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -850,6 +850,17 @@ config I2C_LS2X
This driver can also be built as a module. If so, the module
will be called i2c-ls2x.
+config I2C_LS2X_V2
+ tristate "Loongson-2 Fast Speed I2C adapter"
+ depends on LOONGARCH || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ If you say yes to this option, support will be included for the
+ I2C interface on the Loongson-2K0300 SoCs.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-ls2x-v2.
+
config I2C_MLXBF
tristate "Mellanox BlueField I2C controller"
depends on (MELLANOX_PLATFORM && ARM64) || COMPILE_TEST
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 547123ab351f..3755c54b3d82 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_I2C_KEBA) += i2c-keba.o
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o
obj-$(CONFIG_I2C_LS2X) += i2c-ls2x.o
+obj-$(CONFIG_I2C_LS2X_V2) += i2c-ls2x-v2.o
obj-$(CONFIG_I2C_MESON) += i2c-meson.o
obj-$(CONFIG_I2C_MICROCHIP_CORE) += i2c-microchip-corei2c.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
diff --git a/drivers/i2c/busses/i2c-ls2x-v2.c b/drivers/i2c/busses/i2c-ls2x-v2.c
new file mode 100644
index 000000000000..2909a721d632
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ls2x-v2.c
@@ -0,0 +1,544 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Loongson-2K0300 I2C controller driver
+ *
+ * Copyright (C) 2025-2026 Loongson Technology Corporation Limited
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+/* Loongson-2 fast I2C offset registers */
+#define LOONGSON2_I2C_CR1 0x00 /* I2C control 1 register */
+#define LOONGSON2_I2C_CR2 0x04 /* I2C control 2 register */
+#define LOONGSON2_I2C_OAR 0x08 /* I2C slave address register */
+#define LOONGSON2_I2C_DR 0x10 /* I2C data register */
+#define LOONGSON2_I2C_SR1 0x14 /* I2C status 1 register */
+#define LOONGSON2_I2C_SR2 0x18 /* I2C status 2 register */
+#define LOONGSON2_I2C_CCR 0x1c /* I2C clock control register */
+#define LOONGSON2_I2C_TRISE 0x20 /* I2C trise register */
+#define LOONGSON2_I2C_FLTR 0x24
+
+/* Bitfields of I2C control 1 register */
+#define LOONGSON2_I2C_CR1_PE BIT(0) /* Peripheral enable */
+#define LOONGSON2_I2C_CR1_START BIT(8) /* Start generation */
+#define LOONGSON2_I2C_CR1_STOP BIT(9) /* Stop generation */
+#define LOONGSON2_I2C_CR1_ACK BIT(10) /* Acknowledge enable */
+#define LOONGSON2_I2C_CR1_POS BIT(11) /* Acknowledge/PEC Position (for data reception) */
+
+#define LOONGSON2_I2C_CR1_OP_MASK (LOONGSON2_I2C_CR1_START | LOONGSON2_I2C_CR1_STOP)
+
+/* Bitfields of I2C control 2 register */
+#define LOONGSON2_I2C_CR2_FREQ GENMASK(5, 0) /* APB Clock Frequency in MHz */
+#define LOONGSON2_I2C_CR2_ITERREN BIT(8) /* Fault-Class Interrupt Enable */
+#define LOONGSON2_I2C_CR2_ITEVTEN BIT(9) /* Event-Based Interrupt Enable */
+#define LOONGSON2_I2C_CR2_ITBUFEN BIT(10) /* Cache-Class Interrupt Enable */
+
+#define LOONGSON2_I2C_CR2_INT_MASK \
+ (LOONGSON2_I2C_CR2_ITBUFEN | LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN)
+
+/* Bitfields of I2C status 1 register */
+#define LOONGSON2_I2C_SR1_SB BIT(0) /* Start bit (Master mode) */
+#define LOONGSON2_I2C_SR1_ADDR BIT(1) /* Address sent (master mode) */
+#define LOONGSON2_I2C_SR1_BTF BIT(2) /* Byte transfer finished */
+#define LOONGSON2_I2C_SR1_RXNE BIT(6) /* Data register not empty (receivers) */
+#define LOONGSON2_I2C_SR1_TXE BIT(7) /* Data register empty (transmitters) */
+#define LOONGSON2_I2C_SR1_BERR BIT(8) /* Bus error */
+#define LOONGSON2_I2C_SR1_ARLO BIT(9) /* Arbitration lost (master mode) */
+#define LOONGSON2_I2C_SR1_AF BIT(10) /* Acknowledge failure */
+
+#define LOONGSON2_I2C_SR1_ITEVTEN_MASK \
+ (LOONGSON2_I2C_SR1_BTF | LOONGSON2_I2C_SR1_ADDR | LOONGSON2_I2C_SR1_SB)
+#define LOONGSON2_I2C_SR1_ITBUFEN_MASK (LOONGSON2_I2C_SR1_TXE | LOONGSON2_I2C_SR1_RXNE)
+#define LOONGSON2_I2C_SR1_ITERREN_MASK \
+ (LOONGSON2_I2C_SR1_AF | LOONGSON2_I2C_SR1_ARLO | LOONGSON2_I2C_SR1_BERR)
+
+/* Bitfields of I2C status 2 register */
+#define LOONGSON2_I2C_SR2_MSL BIT(0) /* Master/slave */
+#define LOONGSON2_I2C_SR2_BUSY BIT(1) /* Bus busy */
+#define LOONGSON2_I2C_SR2_TRA BIT(2) /* Transmitter/receiver */
+#define LOONGSON2_I2C_SR2_GENCALL BIT(4) /* General call address (Slave mode) */
+
+/* Bitfields of I2C clock control register */
+#define LOONGSON2_I2C_CCR_CCR GENMASK(11, 0)
+#define LOONGSON2_I2C_CCR_DUTY BIT(14)
+#define LOONGSON2_I2C_CCR_FS BIT(15)
+
+/* Bitfields of I2C trise register */
+#define LOONGSON2_I2C_TRISE_SCL GENMASK(5, 0)
+
+#define LOONGSON2_I2C_FREE_SLEEP_US 10
+#define LOONGSON2_I2C_FREE_TIMEOUT_US (2 * USEC_PER_MSEC)
+
+/**
+ * struct loongson2_i2c_msg - client specific data
+ * @buf: data buffer
+ * @count: number of bytes to be transferred
+ * @result: result of the transfer
+ * @addr: 8-bit slave addr, including r/w bit
+ * @stop: last I2C msg to be sent, i.e. STOP to be generated
+ */
+struct loongson2_i2c_msg {
+ u8 *buf;
+ u32 count;
+ int result;
+ u8 addr;
+ bool stop;
+};
+
+/**
+ * struct loongson2_i2c_priv - private data of the controller
+ * @adapter: I2C adapter for this controller
+ * @complete: completion of I2C message
+ * @clk: hw i2c clock
+ * @regmap: regmap of the I2C device
+ * @parent_rate_mhz: I2C clock parent rate
+ * @msg: I2C transfer information
+ */
+struct loongson2_i2c_priv {
+ struct i2c_adapter adapter;
+ struct completion complete;
+ struct clk *clk;
+ struct regmap *regmap;
+ unsigned long parent_rate_mhz;
+ struct loongson2_i2c_msg msg;
+};
+
+static void loongson2_i2c_disable_irq(struct loongson2_i2c_priv *priv)
+{
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_INT_MASK, 0);
+}
+
+static void loongson2_i2c_read_msg(struct loongson2_i2c_priv *priv)
+{
+ struct loongson2_i2c_msg *msg = &priv->msg;
+ u32 rbuf;
+
+ regmap_read(priv->regmap, LOONGSON2_I2C_DR, &rbuf);
+ *msg->buf++ = rbuf;
+ msg->count--;
+}
+
+static void loongson2_i2c_write_msg(struct loongson2_i2c_priv *priv, u8 byte)
+{
+ regmap_write(priv->regmap, LOONGSON2_I2C_DR, byte);
+}
+
+static void loongson2_i2c_terminate_xfer(struct loongson2_i2c_priv *priv)
+{
+ struct loongson2_i2c_msg *msg = &priv->msg;
+
+ loongson2_i2c_disable_irq(priv);
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK,
+ msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START);
+ complete(&priv->complete);
+}
+
+static void loongson2_i2c_handle_write(struct loongson2_i2c_priv *priv)
+{
+ struct loongson2_i2c_msg *msg = &priv->msg;
+
+ if (msg->count) {
+ loongson2_i2c_write_msg(priv, *msg->buf++);
+ if (!--msg->count)
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2,
+ LOONGSON2_I2C_CR2_ITBUFEN, 0);
+ } else {
+ loongson2_i2c_terminate_xfer(priv);
+ }
+}
+
+static void loongson2_i2c_handle_rx_addr(struct loongson2_i2c_priv *priv)
+{
+ struct loongson2_i2c_msg *msg = &priv->msg;
+
+ switch (msg->count) {
+ case 0:
+ loongson2_i2c_terminate_xfer(priv);
+ break;
+ case 1:
+ /* Enable NACK and reset POS (Acknowledge position) */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1,
+ LOONGSON2_I2C_CR1_ACK | LOONGSON2_I2C_CR1_POS, 0);
+ /* Set STOP or RepSTART */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK,
+ msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START);
+ break;
+ case 2:
+ /* Enable NACK */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, 0);
+ /* Set POS (NACK position) */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_POS,
+ LOONGSON2_I2C_CR1_POS);
+ break;
+
+ default:
+ /* Enable ACK */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK,
+ LOONGSON2_I2C_CR1_ACK);
+ /* Reset POS (ACK position) */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_POS, 0);
+ break;
+ }
+}
+
+static void loongson2_i2c_isr_error(u32 status, void *data)
+{
+ struct loongson2_i2c_priv *priv = data;
+ struct loongson2_i2c_msg *msg = &priv->msg;
+
+ /* Arbitration lost */
+ if (status & LOONGSON2_I2C_SR1_ARLO) {
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_ARLO, 0);
+ msg->result = -EAGAIN;
+ goto out;
+ }
+
+ /*
+ * Acknowledge failure:
+ * In master transmitter mode a Stop must be generated by software.
+ */
+ if (status & LOONGSON2_I2C_SR1_AF) {
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_STOP,
+ LOONGSON2_I2C_CR1_STOP);
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_AF, 0);
+ msg->result = -EIO;
+ goto out;
+ }
+
+ /* Bus error */
+ if (status & LOONGSON2_I2C_SR1_BERR) {
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_SR1, LOONGSON2_I2C_SR1_BERR, 0);
+ msg->result = -EIO;
+ goto out;
+ }
+
+out:
+ loongson2_i2c_disable_irq(priv);
+ complete(&priv->complete);
+}
+
+static void loongson2_i2c_handle_read(struct loongson2_i2c_priv *priv)
+{
+ struct loongson2_i2c_msg *msg = &priv->msg;
+
+ switch (msg->count) {
+ case 1:
+ loongson2_i2c_disable_irq(priv);
+ loongson2_i2c_read_msg(priv);
+ complete(&priv->complete);
+ break;
+ case 2:
+ case 3:
+ /*
+ * For 2-byte/3-byte reception and for N-byte reception with N > 3, we have to
+ * wait for byte transferred finished event before reading data.
+ * Just disable buffer interrupt in order to avoid another system preemption due
+ * to RX not empty event.
+ */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_ITBUFEN, 0);
+ break;
+ default:
+ /*
+ * For N byte reception with N > 3 we directly read data register
+ * until N-2 data.
+ */
+ loongson2_i2c_read_msg(priv);
+ break;
+ }
+}
+
+static void loongson2_i2c_handle_rx_done(struct loongson2_i2c_priv *priv)
+{
+ struct loongson2_i2c_msg *msg = &priv->msg;
+
+ switch (msg->count) {
+ case 2:
+ /*
+ * The STOP/START bit has to be set before reading the last two bytes.
+ * After that, we could read the last two bytes.
+ */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_OP_MASK,
+ msg->stop ? LOONGSON2_I2C_CR1_STOP : LOONGSON2_I2C_CR1_START);
+
+ for (unsigned int i = msg->count; i > 0; i--)
+ loongson2_i2c_read_msg(priv);
+
+ loongson2_i2c_disable_irq(priv);
+
+ complete(&priv->complete);
+ break;
+ case 3:
+ /*
+ * In order to generate the NACK after the last received data byte, enable NACK
+ * before reading N-2 data.
+ */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_ACK, 0);
+ loongson2_i2c_read_msg(priv);
+ break;
+ default:
+ loongson2_i2c_read_msg(priv);
+ break;
+ }
+}
+
+static irqreturn_t loongson2_i2c_isr_event(int irq, void *data)
+{
+ struct loongson2_i2c_priv *priv = data;
+ struct device *dev = regmap_get_device(priv->regmap);
+ struct loongson2_i2c_msg *msg = &priv->msg;
+ u32 status, ien, event, cr2, possible_status;
+
+ regmap_read(priv->regmap, LOONGSON2_I2C_SR1, &status);
+ if (status & LOONGSON2_I2C_SR1_ITERREN_MASK) {
+ loongson2_i2c_isr_error(status, data);
+ return IRQ_NONE;
+ }
+
+ regmap_read(priv->regmap, LOONGSON2_I2C_CR2, &cr2);
+ ien = cr2 & LOONGSON2_I2C_CR2_INT_MASK;
+
+ /* Update possible_status if buffer interrupt is enabled */
+ possible_status = LOONGSON2_I2C_SR1_ITEVTEN_MASK;
+ if (ien & LOONGSON2_I2C_CR2_ITBUFEN)
+ possible_status |= LOONGSON2_I2C_SR1_ITBUFEN_MASK;
+
+ event = status & possible_status;
+ if (!event) {
+ dev_dbg(dev, "spurious evt IRQ (status=0x%08x, ien=0x%08x)\n", status, ien);
+ return IRQ_NONE;
+ }
+
+ /* Start condition generated */
+ if (event & LOONGSON2_I2C_SR1_SB)
+ loongson2_i2c_write_msg(priv, msg->addr);
+
+ /* I2C Address sent */
+ if (event & LOONGSON2_I2C_SR1_ADDR) {
+ if (msg->addr & I2C_M_RD)
+ loongson2_i2c_handle_rx_addr(priv);
+ /* Clear ADDR flag */
+ regmap_read(priv->regmap, LOONGSON2_I2C_SR2, &status);
+ /* Enable buffer interrupts for RX/TX not empty events */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_ITBUFEN,
+ LOONGSON2_I2C_CR2_ITBUFEN);
+ }
+
+ /* TX empty */
+ if ((event & LOONGSON2_I2C_SR1_TXE) && !(msg->addr & I2C_M_RD))
+ loongson2_i2c_handle_write(priv);
+
+ /* RX not empty */
+ if ((event & LOONGSON2_I2C_SR1_RXNE) && (msg->addr & I2C_M_RD))
+ loongson2_i2c_handle_read(priv);
+
+ /*
+ * The BTF (Byte Transfer finished) event occurs when:
+ * - in reception: a new byte is received in the shift register
+ * but the previous byte has not been read yet from data register
+ * - in transmission: a new byte should be sent but the data register
+ * has not been written yet
+ */
+ if (event & LOONGSON2_I2C_SR1_BTF) {
+ if (msg->addr & I2C_M_RD)
+ loongson2_i2c_handle_rx_done(priv);
+ else
+ loongson2_i2c_handle_write(priv);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int loongson2_i2c_xfer_msg(struct loongson2_i2c_priv *priv, struct i2c_msg *msg,
+ bool is_stop)
+{
+ struct loongson2_i2c_msg *l_msg = &priv->msg;
+ unsigned long timeout;
+
+ l_msg->addr = i2c_8bit_addr_from_msg(msg);
+ l_msg->buf = msg->buf;
+ l_msg->count = msg->len;
+ l_msg->stop = is_stop;
+ l_msg->result = 0;
+
+ reinit_completion(&priv->complete);
+
+ /* Enable events and errors interrupts */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2,
+ LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN,
+ LOONGSON2_I2C_CR2_ITEVTEN | LOONGSON2_I2C_CR2_ITERREN);
+
+ timeout = wait_for_completion_timeout(&priv->complete, priv->adapter.timeout);
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ return l_msg->result;
+}
+
+static int loongson2_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+ struct loongson2_i2c_priv *priv = i2c_get_adapdata(i2c_adap);
+ struct device *dev = regmap_get_device(priv->regmap);
+ unsigned int status;
+ int ret;
+
+ /* Wait I2C bus free */
+ ret = regmap_read_poll_timeout(priv->regmap, LOONGSON2_I2C_SR2, status,
+ !(status & LOONGSON2_I2C_SR2_BUSY),
+ LOONGSON2_I2C_FREE_SLEEP_US,
+ LOONGSON2_I2C_FREE_TIMEOUT_US);
+ if (ret) {
+ dev_dbg(dev, "The I2C bus is busy now.\n");
+ return ret;
+ }
+
+ /* Start generation */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_START,
+ LOONGSON2_I2C_CR1_START);
+
+ for (unsigned int i = 0; i < num; i++) {
+ ret = loongson2_i2c_xfer_msg(priv, &msgs[i], i == num - 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ return num;
+}
+
+static u32 loongson2_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm loongson2_i2c_algo = {
+ .xfer = loongson2_i2c_xfer,
+ .functionality = loongson2_i2c_func,
+};
+
+static int loongson2_i2c_adjust_bus_speed(struct loongson2_i2c_priv *priv)
+{
+ struct device *dev = regmap_get_device(priv->regmap);
+ struct i2c_timings i2c_t;
+ u32 val, freq_MHz, ccr;
+
+ i2c_parse_fw_timings(dev, &i2c_t, true);
+ priv->parent_rate_mhz = clk_get_rate(priv->clk);
+
+ if (i2c_t.bus_freq_hz == I2C_MAX_STANDARD_MODE_FREQ) {
+ /* Select Standard mode */
+ ccr = 0;
+ val = DIV_ROUND_UP(priv->parent_rate_mhz, i2c_t.bus_freq_hz * 2);
+ } else if (i2c_t.bus_freq_hz == I2C_MAX_FAST_MODE_FREQ) {
+ /* Select Fast mode */
+ ccr = LOONGSON2_I2C_CCR_FS;
+ val = DIV_ROUND_UP(priv->parent_rate_mhz, i2c_t.bus_freq_hz * 3);
+ } else {
+ return dev_err_probe(dev, -EINVAL, "Unsupported speed (%uHz)\n", i2c_t.bus_freq_hz);
+ }
+
+ FIELD_MODIFY(LOONGSON2_I2C_CCR_CCR, &ccr, val);
+ regmap_write(priv->regmap, LOONGSON2_I2C_CCR, ccr);
+
+ freq_MHz = DIV_ROUND_UP(priv->parent_rate_mhz, HZ_PER_MHZ);
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR2, LOONGSON2_I2C_CR2_FREQ,
+ FIELD_GET(LOONGSON2_I2C_CR2_FREQ, freq_MHz));
+
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_TRISE, LOONGSON2_I2C_TRISE_SCL,
+ LOONGSON2_I2C_TRISE_SCL);
+
+ /* Enable I2C */
+ regmap_update_bits(priv->regmap, LOONGSON2_I2C_CR1, LOONGSON2_I2C_CR1_PE,
+ LOONGSON2_I2C_CR1_PE);
+
+ return 0;
+}
+
+static const struct regmap_config loongson2_i2c_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = LOONGSON2_I2C_TRISE,
+};
+
+static int loongson2_i2c_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct loongson2_i2c_priv *priv;
+ struct i2c_adapter *adap;
+ void __iomem *base;
+ int irq, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->regmap = devm_regmap_init_mmio(dev, base, &loongson2_i2c_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap), "Failed to init regmap.\n");
+
+ priv->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to enable clock.\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ adap = &priv->adapter;
+ adap->retries = 5;
+ adap->nr = pdev->id;
+ adap->dev.parent = dev;
+ adap->owner = THIS_MODULE;
+ adap->algo = &loongson2_i2c_algo;
+ adap->timeout = 2 * HZ;
+ device_set_node(&adap->dev, dev_fwnode(dev));
+ i2c_set_adapdata(adap, priv);
+ strscpy(adap->name, pdev->name);
+ init_completion(&priv->complete);
+ platform_set_drvdata(pdev, priv);
+
+ ret = loongson2_i2c_adjust_bus_speed(priv);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(dev, irq, loongson2_i2c_isr_event, IRQF_SHARED, pdev->name, priv);
+ if (ret)
+ return ret;
+
+ return devm_i2c_add_adapter(dev, adap);
+}
+
+static const struct of_device_id loongson2_i2c_id_table[] = {
+ { .compatible = "loongson,ls2k0300-i2c" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, loongson2_i2c_id_table);
+
+static struct platform_driver loongson2_i2c_driver = {
+ .driver = {
+ .name = "loongson2-i2c-v2",
+ .of_match_table = loongson2_i2c_id_table,
+ },
+ .probe = loongson2_i2c_probe,
+};
+module_platform_driver(loongson2_i2c_driver);
+
+MODULE_DESCRIPTION("Loongson-2K0300 I2C bus driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
--
2.52.0
^ permalink raw reply related
* [PATCH v7 1/2] dt-bindings: i2c: loongson,ls2x: Add ls2k0300-i2c compatible
From: Binbin Zhou @ 2026-04-11 9:58 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Andi Shyti, Wolfram Sang, Andy Shevchenko,
linux-i2c
Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, Binbin Zhou,
Conor Dooley
In-Reply-To: <cover.1775900045.git.zhoubinbin@loongson.cn>
Add "loongson,ls2k0300-i2c" dedicated compatible for representing I2C of
Loongson-2K0300 chip, because its HW integration is quiet different from
others.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Huacai Chen <chenhuacai@loongson.cn>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml b/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
index 67882ec6e06a..ee09c6d9c5f0 100644
--- a/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
@@ -16,6 +16,7 @@ properties:
compatible:
enum:
- loongson,ls2k-i2c
+ - loongson,ls2k0300-i2c
- loongson,ls7a-i2c
reg:
@@ -24,6 +25,9 @@ properties:
interrupts:
maxItems: 1
+ clocks:
+ maxItems: 1
+
required:
- compatible
- reg
--
2.52.0
^ permalink raw reply related
* [PATCH v7 0/2] i2c: Add Loongson-2K0300 I2C controller support
From: Binbin Zhou @ 2026-04-11 9:58 UTC (permalink / raw)
To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Andi Shyti, Wolfram Sang, Andy Shevchenko,
linux-i2c
Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, Binbin Zhou
Hi all:
This patch set describes the I2C controller integrated the
Loongson-2K0300 chip.
It has a significantly different design from the previous I2C
controller(i2c-ls2x), such as support for master-slave transfer mode,
and DMA transfers (implementation in progress), etc. Therefore, we try
to name it i2c-ls2x-v2.
Thanks.
======
V7:
- Add Reviewed-by tag from Huacai & Andy, thanks.
Patch (2/2):
- Add the REGMAP_MMIO Kconfig dependency;
- parent_rate_MHz -> parent_rate_mhz to avoid CamelCase.
Link to V6:
https://lore.kernel.org/all/cover.1773991081.git.zhoubinbin@loongson.cn/
V6:
- Rebase on linux-i2c/i2c-next;
Patch (2/2):
- Update comment;
- Use regmap_get_device();
Link to V5:
https://lore.kernel.org/all/cover.1773142933.git.zhoubinbin@loongson.cn/
V5:
- Rebase on linux-i2c/i2c-next;
Patch (2/2):
- Add time.h header file;
- Add the `MHz` suffix to `parent_rate` and `freq`;
- Define an iterator within the for loop, with its type being
`unsigned int`;
- Use dev_err_probe() in loongson2_i2c_adjust_bus_speed();
- i2c_adapter_set_node()->device_set_node().
Link to V4:
https://lore.kernel.org/all/cover.1772714348.git.zhoubinbin@loongson.cn/
V4:
- Rebase on linux-i2c/i2c-next;
Patch (2/2):
- The parent_rate parameter type should be `unsigned long`;
- Drop fallthrough and add missing break;
- device_set_node()->i2c_adapter_set_node();
- Use i2c_parse_fw_timings();
- Use i2c_t.bus_freq_hz instead of priv->speed;
- Sperate loongson2_i2c_handle_read() into loongson2_i2c_handle_read()
and loongson2_i2c_handle_rx_done().
Link to V3:
https://lore.kernel.org/all/cover.1772001073.git.zhoubinbin@loongson.cn/
V3:
- Rebase on linux-i2c/i2c-next;
Patch (2/2):
- Reorder header file follow IWYU principle;
- Better indentation and coding style;
- Use generic macro definitions;
- Amend *all* struct data types;
- Correct unreasonable variable type definitions;
- Refact loongson2_i2c_isr_error();
- of_property_read_u32()->device_property_read_u32();
- Remove meaningless blank lines and output.
Link to V2:
https://lore.kernel.org/all/cover.1769476820.git.zhoubinbin@loongson.cn/
V2:
Patch (1/2):
- Add Acked-by tag from Conor, thanks.
Patch (2/2):
- Reorder the definitions of read() and write();
- Adjust the calculation method for bus speed.
Link to V1:
https://lore.kernel.org/all/cover.1763018288.git.zhoubinbin@loongson.cn/
Binbin Zhou (2):
dt-bindings: i2c: loongson,ls2x: Add ls2k0300-i2c compatible
i2c: ls2x-v2: Add driver for Loongson-2K0300 I2C controller
.../bindings/i2c/loongson,ls2x-i2c.yaml | 4 +
MAINTAINERS | 1 +
drivers/i2c/busses/Kconfig | 11 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-ls2x-v2.c | 544 ++++++++++++++++++
5 files changed, 561 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-ls2x-v2.c
base-commit: 8fc326e15895c9f0403e6243dd4ad468b10aab3d
--
2.52.0
^ permalink raw reply
* [PATCH 2/2] media: i2c: add os02g10 image sensor driver
From: Elgin Perumbilly @ 2026-04-11 9:47 UTC (permalink / raw)
To: sakari.ailus, tarang.raval
Cc: Elgin Perumbilly, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
Vladimir Zapolskiy, Laurent Pinchart, Xiaolei Wang,
Walter Werner Schneider, Kate Hsuan, Sylvain Petinot, Mehdi Djait,
Heimir Thor Sverrisson, linux-media, devicetree, linux-kernel
In-Reply-To: <20260411094723.129738-1-elgin.perumbilly@siliconsignals.io>
Add a v4l2 subdevice driver for the Omnivision os02g10 sensor.
The Omnivision os02g10 is a CMOS image sensor with an active array size of
1920 x 1080.
The following features are supported:
- Manual exposure an gain control support
- vblank/hblank control support
- vflip/hflip control support
- Test pattern control support
- Supported resolution: 1920 x 1080 @ 30fps (SBGGR10)
Signed-off-by: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
---
MAINTAINERS | 1 +
drivers/media/i2c/Kconfig | 10 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/os02g10.c | 1010 +++++++++++++++++++++++++++++++++++
4 files changed, 1022 insertions(+)
create mode 100644 drivers/media/i2c/os02g10.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 8a0a55073c30..693e71b51926 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19449,6 +19449,7 @@ M: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
+F: drivers/media/i2c/os02g10.c
OMNIVISION OS05B10 SENSOR DRIVER
M: Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 5eb1e0e0a87a..dd6e9562acf6 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -372,6 +372,16 @@ config VIDEO_OG0VE1B
To compile this driver as a module, choose M here: the
module will be called og0ve1b.
+config VIDEO_OS02G10
+ tristate "OmniVision OS02G10 sensor support"
+ select V4L2_CCI_I2C
+ help
+ This is a Video4Linux2 sensor driver for Omnivision
+ OS02G10 camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called os02g10.
+
config VIDEO_OS05B10
tristate "OmniVision OS05B10 sensor support"
select V4L2_CCI_I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index a3a6396df3c4..a7554d2eb140 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o
obj-$(CONFIG_VIDEO_OG0VE1B) += og0ve1b.o
+obj-$(CONFIG_VIDEO_OS02G10) += os02g10.o
obj-$(CONFIG_VIDEO_OS05B10) += os05b10.o
obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o
obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o
diff --git a/drivers/media/i2c/os02g10.c b/drivers/media/i2c/os02g10.c
new file mode 100644
index 000000000000..f2972a0509db
--- /dev/null
+++ b/drivers/media/i2c/os02g10.c
@@ -0,0 +1,1010 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * V4L2 Support for the OS02G10
+ *
+ * Copyright (C) 2026 Silicon Signals Pvt. Ltd.
+ *
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/units.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/regmap.h>
+
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mediabus.h>
+
+#define OS02G10_XCLK_FREQ (24 * HZ_PER_MHZ)
+
+/* Add page number in CCI private bits [31:28] of the register address */
+#define OS02G10_PAGE_REG8(p, x) (((p) << CCI_REG_PRIVATE_SHIFT) | CCI_REG8(x))
+#define OS02G10_PAGE_REG16(p, x) (((p) << CCI_REG_PRIVATE_SHIFT) | CCI_REG16(x))
+#define OS02G10_PAGE_REG24(p, x) (((p) << CCI_REG_PRIVATE_SHIFT) | CCI_REG24(x))
+
+#define OS02G10_REG_PAGE_SELECT CCI_REG8(0xfd)
+
+/* Page 0 */
+#define OS02G10_REG_CHIPID OS02G10_PAGE_REG24(0x00, 0x02)
+#define OS02G10_CHIPID 0x560247
+
+#define OS02G10_REG_PLL_DIV_CTRL OS02G10_PAGE_REG8(0x00, 0x30)
+#define OS02G10_REG_PLL_DCTL_BIAS_CTRL OS02G10_PAGE_REG8(0x00, 0x35)
+#define OS02G10_REG_GATE_EN_CTRL OS02G10_PAGE_REG8(0x00, 0x38)
+#define OS02G10_REG_DPLL_NC OS02G10_PAGE_REG8(0x00, 0x41)
+#define OS02G10_REG_MP_PHASE_CTRL OS02G10_PAGE_REG8(0x00, 0x44)
+
+/* Page 1 */
+#define OS02G10_REG_STREAM_CTRL OS02G10_PAGE_REG8(0x01, 0xb1)
+#define OS02G10_STREAM_CTRL_ON 0x03
+#define OS02G10_STREAM_CTRL_OFF 0x00
+
+#define OS02G10_REG_FRAME_SYNC OS02G10_PAGE_REG8(0x01, 0x01)
+
+#define OS02G10_REG_FRAME_LENGTH OS02G10_PAGE_REG16(0x01, 0x0e)
+#define OS02G10_REG_FRAME_EXP_SEPERATE_EN OS02G10_PAGE_REG8(0x01, 0x0d)
+#define OS02G10_FRAME_EXP_SEPERATE_EN 0x10
+#define OS02G10_FRAME_LENGTH_MAX 0xffff
+#define OS02G10_REG_HBLANK OS02G10_PAGE_REG16(0x01, 0x09)
+
+#define OS02G10_REG_TEST_PATTERN OS02G10_PAGE_REG8(0x01, 0x0d)
+#define OS02G10_TEST_PATTERN_ENABLE 0x01
+
+#define OS02G10_REG_ULP_PWD_DUMMY_CTRL OS02G10_PAGE_REG8(0x01, 0x3c)
+#define OS02G10_REG_DC_LEVEL_LIMIT_EN OS02G10_PAGE_REG8(0x01, 0x46)
+#define OS02G10_REG_DC_LEVEL_LIMIT_L OS02G10_PAGE_REG8(0x01, 0x47)
+#define OS02G10_REG_BLC_DATA_LIMIT_L OS02G10_PAGE_REG8(0x01, 0x48)
+#define OS02G10_REG_DC_BLC_LIMIT_H OS02G10_PAGE_REG8(0x01, 0x49)
+
+#define OS02G10_REG_HS_LP_CTRL OS02G10_PAGE_REG8(0x01, 0x92)
+#define OS02G10_REG_HS_LEVEL OS02G10_PAGE_REG8(0x01, 0x9d)
+#define OS02G10_REG_HS_DRV OS02G10_PAGE_REG8(0x01, 0x9e)
+
+#define OS02G10_REG_GB_SUBOFFSET OS02G10_PAGE_REG8(0x01, 0xf0)
+#define OS02G10_REG_BLUE_SUBOFFSET OS02G10_PAGE_REG8(0x01, 0xf1)
+#define OS02G10_REG_RED_SUBOFFSET OS02G10_PAGE_REG8(0x01, 0xf2)
+#define OS02G10_REG_GR_SUBOFFSET OS02G10_PAGE_REG8(0x01, 0xf3)
+
+#define OS02G10_REG_ABL_TRIGGER OS02G10_PAGE_REG8(0x01, 0xfa)
+#define OS02G10_REG_ABL OS02G10_PAGE_REG8(0x01, 0xfb)
+
+#define OS02G10_REG_H_SIZE_MIPI OS02G10_PAGE_REG16(0x01, 0x8e)
+#define OS02G10_REG_V_SIZE_MIPI OS02G10_PAGE_REG16(0x01, 0x90)
+#define OS02G10_REG_MIPI_TX_SPEED_CTRL OS02G10_PAGE_REG8(0x01, 0xa1)
+
+#define OS02G10_REG_LONG_EXPOSURE OS02G10_PAGE_REG16(0x01, 0x03)
+#define OS02G10_EXPOSURE_MIN 4
+#define OS02G10_EXPOSURE_STEP 1
+#define OS02G10_EXPOSURE_MARGIN 9
+
+#define OS02G10_REG_ANALOG_GAIN OS02G10_PAGE_REG8(0x01, 0x24)
+#define OS02G10_ANALOG_GAIN_MIN 0x10
+#define OS02G10_ANALOG_GAIN_MAX 0xf8
+#define OS02G10_ANALOG_GAIN_STEP 1
+#define OS02G10_ANALOG_GAIN_DEFAULT 0x10
+
+#define OS02G10_REG_DIGITAL_GAIN_H OS02G10_PAGE_REG8(0x01, 0x37)
+#define OS02G10_REG_DIGITAL_GAIN_L OS02G10_PAGE_REG8(0x01, 0x39)
+#define OS02G10_DIGITAL_GAIN_MIN 0x40
+#define OS02G10_DIGITAL_GAIN_MAX 0x800
+#define OS02G10_DIGITAL_GAIN_STEP 64
+#define OS02G10_DIGITAL_GAIN_DEFAULT 0x40
+
+#define OS02G10_REG_FLIP_MIRROR OS02G10_PAGE_REG8(0x01, 0x3f)
+#define OS02G10_FLIP BIT(1)
+#define OS02G10_MIRROR BIT(0)
+
+#define OS02G10_REG_SIF_CTRL OS02G10_PAGE_REG8(0x02, 0x5e)
+#define OS02G10_ORIENTATION_BAYER_FIX 0x32
+
+/* Page 2 */
+#define OS02G10_REG_V_START OS02G10_PAGE_REG16(0x02, 0xa0)
+#define OS02G10_REG_V_SIZE OS02G10_PAGE_REG16(0x02, 0xa2)
+#define OS02G10_REG_H_START OS02G10_PAGE_REG16(0x02, 0xa4)
+#define OS02G10_REG_H_SIZE OS02G10_PAGE_REG16(0x02, 0xa6)
+
+#define OS02G10_LINK_FREQ_720MHZ (720 * HZ_PER_MHZ)
+
+/* OS02G10 native and active pixel array size */
+static const struct v4l2_rect os02g10_native_area = {
+ .top = 0,
+ .left = 0,
+ .width = 1928,
+ .height = 1088,
+};
+
+static const struct v4l2_rect os02g10_active_area = {
+ .top = 4,
+ .left = 4,
+ .width = 1920,
+ .height = 1080,
+};
+
+static const char * const os02g10_supply_name[] = {
+ "avdd", /* Analog power */
+ "dovdd", /* Digital I/O power */
+ "dvdd", /* Digital core power */
+};
+
+struct os02g10 {
+ struct device *dev;
+ struct regmap *cci;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct clk *xclk;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(os02g10_supply_name)];
+
+ /* V4L2 Controls */
+ struct v4l2_ctrl_handler handler;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *hflip;
+
+ u32 link_freq_index;
+
+ u8 current_page;
+ struct mutex page_lock;
+};
+
+struct os02g10_mode {
+ u32 width;
+ u32 height;
+ u32 vts_def;
+ u32 hts_def;
+ u32 exp_def;
+};
+
+static const struct cci_reg_sequence os02g10_common_regs[] = {
+ { OS02G10_REG_PLL_DIV_CTRL, 0x0a},
+ { OS02G10_REG_PLL_DCTL_BIAS_CTRL, 0x04},
+ { OS02G10_REG_GATE_EN_CTRL, 0x11},
+ { OS02G10_REG_DPLL_NC, 0x06},
+ { OS02G10_REG_MP_PHASE_CTRL, 0x20},
+ { OS02G10_PAGE_REG8(0x01, 0x19), 0x50},
+ { OS02G10_PAGE_REG8(0x01, 0x1a), 0x0c},
+ { OS02G10_PAGE_REG8(0x01, 0x1b), 0x0d},
+ { OS02G10_PAGE_REG8(0x01, 0x1c), 0x00},
+ { OS02G10_PAGE_REG8(0x01, 0x1d), 0x75},
+ { OS02G10_PAGE_REG8(0x01, 0x1e), 0x52},
+ { OS02G10_PAGE_REG8(0x01, 0x22), 0x14},
+ { OS02G10_PAGE_REG8(0x01, 0x25), 0x44},
+ { OS02G10_PAGE_REG8(0x01, 0x26), 0x0f},
+ { OS02G10_REG_ULP_PWD_DUMMY_CTRL, 0xca},
+ { OS02G10_PAGE_REG8(0x01, 0x3d), 0x4a},
+ { OS02G10_PAGE_REG8(0x01, 0x40), 0x0f},
+ { OS02G10_PAGE_REG8(0x01, 0x43), 0x38},
+ { OS02G10_REG_DC_LEVEL_LIMIT_EN, 0x01},
+ { OS02G10_REG_DC_LEVEL_LIMIT_L, 0x00},
+ { OS02G10_REG_DC_BLC_LIMIT_H, 0x32},
+ { OS02G10_PAGE_REG8(0x01, 0x50), 0x01},
+ { OS02G10_PAGE_REG8(0x01, 0x51), 0x28},
+ { OS02G10_PAGE_REG8(0x01, 0x52), 0x20},
+ { OS02G10_PAGE_REG8(0x01, 0x53), 0x03},
+ { OS02G10_PAGE_REG8(0x01, 0x57), 0x16},
+ { OS02G10_PAGE_REG8(0x01, 0x59), 0x01},
+ { OS02G10_PAGE_REG8(0x01, 0x5a), 0x01},
+ { OS02G10_PAGE_REG8(0x01, 0x5d), 0x04},
+ { OS02G10_PAGE_REG8(0x01, 0x6a), 0x04},
+ { OS02G10_PAGE_REG8(0x01, 0x6b), 0x03},
+ { OS02G10_PAGE_REG8(0x01, 0x6e), 0x28},
+ { OS02G10_PAGE_REG8(0x01, 0x71), 0xc2},
+ { OS02G10_PAGE_REG8(0x01, 0x72), 0x04},
+ { OS02G10_PAGE_REG8(0x01, 0x73), 0x38},
+ { OS02G10_PAGE_REG8(0x01, 0x74), 0x04},
+ { OS02G10_PAGE_REG8(0x01, 0x79), 0x00},
+ { OS02G10_PAGE_REG8(0x01, 0x7a), 0xb2},
+ { OS02G10_PAGE_REG8(0x01, 0x7b), 0x10},
+ { OS02G10_REG_H_SIZE_MIPI, 0x780},
+ { OS02G10_REG_V_SIZE_MIPI, 0x438},
+ { OS02G10_REG_HS_LP_CTRL, 0x02},
+ { OS02G10_REG_HS_LEVEL, 0x03},
+ { OS02G10_REG_HS_DRV, 0x55},
+ { OS02G10_PAGE_REG8(0x01, 0xb8), 0x70},
+ { OS02G10_PAGE_REG8(0x01, 0xb9), 0x70},
+ { OS02G10_PAGE_REG8(0x01, 0xba), 0x70},
+ { OS02G10_PAGE_REG8(0x01, 0xbb), 0x70},
+ { OS02G10_PAGE_REG8(0x01, 0xbc), 0x00},
+ { OS02G10_PAGE_REG8(0x01, 0xc4), 0x6d},
+ { OS02G10_PAGE_REG8(0x01, 0xc5), 0x6d},
+ { OS02G10_PAGE_REG8(0x01, 0xc6), 0x6d},
+ { OS02G10_PAGE_REG8(0x01, 0xc7), 0x6d},
+ { OS02G10_PAGE_REG8(0x01, 0xcc), 0x11},
+ { OS02G10_PAGE_REG8(0x01, 0xcd), 0xe0},
+ { OS02G10_PAGE_REG8(0x01, 0xd0), 0x1b},
+ { OS02G10_PAGE_REG8(0x01, 0xd2), 0x76},
+ { OS02G10_PAGE_REG8(0x01, 0xd3), 0x68},
+ { OS02G10_PAGE_REG8(0x01, 0xd4), 0x68},
+ { OS02G10_PAGE_REG8(0x01, 0xd5), 0x73},
+ { OS02G10_PAGE_REG8(0x01, 0xd6), 0x73},
+ { OS02G10_PAGE_REG8(0x01, 0xe8), 0x55},
+ { OS02G10_REG_GB_SUBOFFSET, 0x40},
+ { OS02G10_REG_BLUE_SUBOFFSET, 0x40},
+ { OS02G10_REG_RED_SUBOFFSET, 0x40},
+ { OS02G10_REG_GR_SUBOFFSET, 0x40},
+ { OS02G10_REG_ABL_TRIGGER, 0x1c},
+ { OS02G10_REG_ABL, 0x33},
+ { OS02G10_PAGE_REG8(0x01, 0xfc), 0x80},
+ { OS02G10_PAGE_REG8(0x01, 0xfe), 0x80},
+ { OS02G10_PAGE_REG8(0x03, 0x03), 0x67},
+ { OS02G10_PAGE_REG8(0x03, 0x00), 0x59},
+ { OS02G10_PAGE_REG8(0x03, 0x04), 0x11},
+ { OS02G10_PAGE_REG8(0x03, 0x05), 0x04},
+ { OS02G10_PAGE_REG8(0x03, 0x06), 0x0c},
+ { OS02G10_PAGE_REG8(0x03, 0x07), 0x08},
+ { OS02G10_PAGE_REG8(0x03, 0x08), 0x08},
+ { OS02G10_PAGE_REG8(0x03, 0x09), 0x4f},
+ { OS02G10_PAGE_REG8(0x03, 0x0b), 0x08},
+ { OS02G10_PAGE_REG8(0x03, 0x0d), 0x26},
+ { OS02G10_PAGE_REG8(0x03, 0x0f), 0x00},
+ { OS02G10_PAGE_REG8(0x02, 0x34), 0xfe},
+ { OS02G10_REG_V_START, 0x0006},
+ { OS02G10_REG_V_SIZE, 0x0438},
+ { OS02G10_REG_H_START, 0x0002},
+ { OS02G10_REG_H_SIZE, 0x0780},
+ { OS02G10_REG_MIPI_TX_SPEED_CTRL, 0x05},
+};
+
+static const struct os02g10_mode supported_modes[] = {
+ {
+ .width = 1920,
+ .height = 1080,
+ .vts_def = 1246,
+ .hts_def = 1082,
+ .exp_def = 1100,
+ },
+};
+
+static const s64 link_freq_menu_items[] = {
+ OS02G10_LINK_FREQ_720MHZ,
+};
+
+static const char * const os02g10_test_pattern_menu[] = {
+ "Disabled",
+ "Colorbar",
+};
+
+static int os02g10_page_access(struct os02g10 *os02g10, u32 reg, int *err)
+{
+ u8 page = (reg & CCI_REG_PRIVATE_MASK) >> CCI_REG_PRIVATE_SHIFT;
+ int ret = 0;
+
+ if (err && *err)
+ return *err;
+
+ guard(mutex)(&os02g10->page_lock);
+
+ /* Perform page access before read/write */
+ if (os02g10->current_page == page)
+ return ret;
+
+ ret = cci_write(os02g10->cci, OS02G10_REG_PAGE_SELECT, page, err);
+ if (!ret)
+ os02g10->current_page = page;
+
+ return ret;
+}
+
+static int os02g10_read(struct os02g10 *os02g10, u32 reg, u64 *val, int *err)
+{
+ u32 addr = reg & ~CCI_REG_PRIVATE_MASK;
+ int ret;
+
+ ret = os02g10_page_access(os02g10, reg, err);
+ if (ret)
+ return ret;
+
+ return cci_read(os02g10->cci, addr, val, err);
+}
+
+static int os02g10_write(struct os02g10 *os02g10, u32 reg, u64 val, int *err)
+{
+ u32 addr = reg & ~CCI_REG_PRIVATE_MASK;
+ int ret;
+
+ ret = os02g10_page_access(os02g10, reg, err);
+ if (ret)
+ return ret;
+
+ return cci_write(os02g10->cci, addr, val, err);
+}
+
+static int os02g10_update_bits(struct os02g10 *os02g10, u32 reg, u64 mask,
+ u64 val, int *err)
+{
+ u32 addr = reg & ~CCI_REG_PRIVATE_MASK;
+ int ret;
+
+ ret = os02g10_page_access(os02g10, reg, err);
+ if (ret)
+ return ret;
+
+ return cci_update_bits(os02g10->cci, addr, mask, val, err);
+}
+
+static int os02g10_multi_reg_write(struct os02g10 *os02g10,
+ const struct cci_reg_sequence *regs,
+ unsigned int num_regs, int *err)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < num_regs; i++) {
+ ret = os02g10_write(os02g10, regs[i].reg, regs[i].val, err);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline struct os02g10 *to_os02g10(struct v4l2_subdev *_sd)
+{
+ return container_of_const(_sd, struct os02g10, sd);
+}
+
+/* Get bayer order based on flip setting. */
+static u32 os02g10_get_format_code(struct os02g10 *os02g10)
+{
+ static const u32 codes[2][2] = {
+ { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, },
+ };
+
+ u32 code = codes[os02g10->vflip->val][os02g10->hflip->val];
+
+ return code;
+}
+
+static int os02g10_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct os02g10 *os02g10 = container_of_const(ctrl->handler,
+ struct os02g10, handler);
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ state = v4l2_subdev_get_locked_active_state(&os02g10->sd);
+ fmt = v4l2_subdev_state_get_format(state, 0);
+
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ /* Honour the VBLANK limits when setting exposure */
+ s64 max = fmt->height + ctrl->val - OS02G10_EXPOSURE_MARGIN;
+
+ ret = __v4l2_ctrl_modify_range(os02g10->exposure,
+ os02g10->exposure->minimum, max,
+ os02g10->exposure->step,
+ os02g10->exposure->default_value);
+ if (ret)
+ return ret;
+ }
+
+ if (pm_runtime_get_if_in_use(os02g10->dev) == 0)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ os02g10_write(os02g10, OS02G10_REG_LONG_EXPOSURE, ctrl->val, &ret);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ os02g10_write(os02g10, OS02G10_REG_ANALOG_GAIN, ctrl->val, &ret);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ os02g10_write(os02g10, OS02G10_REG_DIGITAL_GAIN_L,
+ (ctrl->val & 0xff), &ret);
+ os02g10_write(os02g10, OS02G10_REG_DIGITAL_GAIN_H,
+ ((ctrl->val >> 8) & 0x7), &ret);
+ break;
+ case V4L2_CID_VBLANK:
+ u64 vts = ctrl->val + fmt->height;
+
+ os02g10_update_bits(os02g10, OS02G10_REG_FRAME_EXP_SEPERATE_EN,
+ OS02G10_FRAME_EXP_SEPERATE_EN,
+ OS02G10_FRAME_EXP_SEPERATE_EN, &ret);
+ os02g10_write(os02g10, OS02G10_REG_FRAME_LENGTH, vts, &ret);
+ break;
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ os02g10_write(os02g10, OS02G10_REG_FLIP_MIRROR,
+ os02g10->hflip->val | os02g10->vflip->val << 1,
+ &ret);
+ os02g10_write(os02g10, OS02G10_REG_SIF_CTRL,
+ OS02G10_ORIENTATION_BAYER_FIX, &ret);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ os02g10_update_bits(os02g10,
+ OS02G10_REG_TEST_PATTERN,
+ OS02G10_TEST_PATTERN_ENABLE,
+ ctrl->val ? OS02G10_TEST_PATTERN_ENABLE : 0,
+ &ret);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ os02g10_write(os02g10, OS02G10_REG_FRAME_SYNC, 0x01, &ret);
+
+ pm_runtime_put(os02g10->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops os02g10_ctrl_ops = {
+ .s_ctrl = os02g10_set_ctrl,
+};
+
+static int os02g10_init_controls(struct os02g10 *os02g10)
+{
+ const struct os02g10_mode *mode = &supported_modes[0];
+ u64 vblank_def, hblank_def, exp_max, pixel_rate;
+ struct v4l2_fwnode_device_properties props;
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ int ret;
+
+ ctrl_hdlr = &os02g10->handler;
+ v4l2_ctrl_handler_init(ctrl_hdlr, 12);
+
+ /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
+ pixel_rate = div_u64(OS02G10_LINK_FREQ_720MHZ * 2 * 2, 10);
+ v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops, V4L2_CID_PIXEL_RATE, 0,
+ pixel_rate, 1, pixel_rate);
+
+ os02g10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ os02g10->link_freq_index,
+ 0, link_freq_menu_items);
+ if (os02g10->link_freq)
+ os02g10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ hblank_def = mode->hts_def - mode->width;
+ os02g10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_HBLANK, hblank_def, hblank_def,
+ 1, hblank_def);
+ if (os02g10->hblank)
+ os02g10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vblank_def = mode->vts_def - mode->height;
+ os02g10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_VBLANK, vblank_def,
+ OS02G10_FRAME_LENGTH_MAX - mode->height,
+ 1, vblank_def);
+
+ exp_max = mode->vts_def - OS02G10_EXPOSURE_MARGIN;
+ os02g10->exposure =
+ v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ OS02G10_EXPOSURE_MIN, exp_max,
+ OS02G10_EXPOSURE_STEP, mode->exp_def);
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, OS02G10_ANALOG_GAIN_MIN,
+ OS02G10_ANALOG_GAIN_MAX, OS02G10_ANALOG_GAIN_STEP,
+ OS02G10_ANALOG_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_DIGITAL_GAIN, OS02G10_DIGITAL_GAIN_MIN,
+ OS02G10_DIGITAL_GAIN_MAX, OS02G10_DIGITAL_GAIN_STEP,
+ OS02G10_DIGITAL_GAIN_DEFAULT);
+
+ os02g10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (os02g10->hflip)
+ os02g10->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ os02g10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ if (os02g10->vflip)
+ os02g10->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(os02g10_test_pattern_menu) - 1,
+ 0, 0, os02g10_test_pattern_menu);
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ dev_err(os02g10->dev, "control init failed (%d)\n", ret);
+ goto err_handler_free;
+ }
+
+ ret = v4l2_fwnode_device_parse(os02g10->dev, &props);
+ if (ret)
+ goto err_handler_free;
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr,
+ &os02g10_ctrl_ops, &props);
+ if (ret)
+ goto err_handler_free;
+
+ os02g10->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+err_handler_free:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
+}
+
+static int os02g10_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct os02g10 *os02g10 = to_os02g10(sd);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(os02g10->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = os02g10_multi_reg_write(os02g10, os02g10_common_regs,
+ ARRAY_SIZE(os02g10_common_regs), NULL);
+ if (ret) {
+ dev_err(os02g10->dev, "failed to write common registers\n");
+ goto err_rpm_put;
+ }
+
+ /* Apply customized values from user */
+ ret = __v4l2_ctrl_handler_setup(os02g10->sd.ctrl_handler);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = os02g10_write(os02g10, OS02G10_REG_STREAM_CTRL,
+ OS02G10_STREAM_CTRL_ON, NULL);
+ if (ret)
+ goto err_rpm_put;
+
+ /* vflip and hflip cannot change during streaming */
+ __v4l2_ctrl_grab(os02g10->vflip, true);
+ __v4l2_ctrl_grab(os02g10->hflip, true);
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put(os02g10->dev);
+ return ret;
+}
+
+static int os02g10_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct os02g10 *os02g10 = to_os02g10(sd);
+ int ret;
+
+ ret = os02g10_write(os02g10, OS02G10_REG_STREAM_CTRL,
+ OS02G10_STREAM_CTRL_OFF, NULL);
+ if (ret)
+ dev_err(os02g10->dev, "%s failed to set stream\n", __func__);
+
+ __v4l2_ctrl_grab(os02g10->vflip, false);
+ __v4l2_ctrl_grab(os02g10->hflip, false);
+
+ pm_runtime_put(os02g10->dev);
+
+ return ret;
+}
+
+static int os02g10_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ sel->r = os02g10_native_area;
+ return 0;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r = os02g10_active_area;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int os02g10_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct os02g10 *os02g10 = to_os02g10(sd);
+
+ if (code->index)
+ return -EINVAL;
+
+ code->code = os02g10_get_format_code(os02g10);
+
+ return 0;
+}
+
+static int os02g10_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct os02g10 *os02g10 = to_os02g10(sd);
+
+ if (fse->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ if (fse->code != os02g10_get_format_code(os02g10))
+ return -EINVAL;
+
+ fse->min_width = supported_modes[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = supported_modes[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int os02g10_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct os02g10 *os02g10 = to_os02g10(sd);
+ struct v4l2_mbus_framefmt *format;
+ const struct os02g10_mode *mode;
+ int ret;
+
+ format = v4l2_subdev_state_get_format(sd_state, 0);
+
+ mode = v4l2_find_nearest_size(supported_modes,
+ ARRAY_SIZE(supported_modes),
+ width, height,
+ fmt->format.width, fmt->format.height);
+
+ fmt->format.code = os02g10_get_format_code(os02g10);
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.field = V4L2_FIELD_NONE;
+ fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+ fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
+
+ *format = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ u32 vblank_def = mode->vts_def - mode->height;
+
+ ret = __v4l2_ctrl_modify_range(os02g10->vblank, vblank_def,
+ OS02G10_FRAME_LENGTH_MAX -
+ mode->height, 1, vblank_def);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int os02g10_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct os02g10 *os02g10 = to_os02g10(sd);
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .format = {
+ .code = os02g10_get_format_code(os02g10),
+ .width = supported_modes[0].width,
+ .height = supported_modes[0].height,
+ },
+ };
+
+ os02g10_set_pad_format(sd, state, &fmt);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops os02g10_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops os02g10_pad_ops = {
+ .enum_mbus_code = os02g10_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = os02g10_set_pad_format,
+ .get_selection = os02g10_get_selection,
+ .enum_frame_size = os02g10_enum_frame_size,
+ .enable_streams = os02g10_enable_streams,
+ .disable_streams = os02g10_disable_streams,
+};
+
+static const struct v4l2_subdev_ops os02g10_subdev_ops = {
+ .video = &os02g10_video_ops,
+ .pad = &os02g10_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops os02g10_internal_ops = {
+ .init_state = os02g10_init_state,
+};
+
+static int os02g10_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct os02g10 *os02g10 = to_os02g10(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(os02g10_supply_name),
+ os02g10->supplies);
+ if (ret) {
+ dev_err(os02g10->dev, "failed to enable regulators\n");
+ return ret;
+ }
+
+ /* T4: delay from DOVDD stable to MCLK on */
+ fsleep(5 * USEC_PER_MSEC);
+
+ ret = clk_prepare_enable(os02g10->xclk);
+ if (ret) {
+ dev_err(os02g10->dev, "failed to enable clock\n");
+ goto err_regulator_off;
+ }
+
+ /* T3: delay from DVDD stable to sensor power up stable */
+ fsleep(5 * USEC_PER_MSEC);
+
+ gpiod_set_value_cansleep(os02g10->reset_gpio, 0);
+
+ /* T5: delay from sensor power up stable to SCCB initialization */
+ fsleep(5 * USEC_PER_MSEC);
+
+ return 0;
+
+err_regulator_off:
+ regulator_bulk_disable(ARRAY_SIZE(os02g10_supply_name), os02g10->supplies);
+ return ret;
+}
+
+static int os02g10_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct os02g10 *os02g10 = to_os02g10(sd);
+
+ clk_disable_unprepare(os02g10->xclk);
+ gpiod_set_value_cansleep(os02g10->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(os02g10_supply_name), os02g10->supplies);
+
+ return 0;
+}
+
+static int os02g10_identify_module(struct os02g10 *os02g10)
+{
+ u64 chip_id;
+ int ret;
+
+ ret = os02g10_read(os02g10, OS02G10_REG_CHIPID, &chip_id, NULL);
+ if (ret)
+ return dev_err_probe(os02g10->dev, ret,
+ "failed to read chip id %x\n",
+ OS02G10_CHIPID);
+
+ if (chip_id != OS02G10_CHIPID)
+ return dev_err_probe(os02g10->dev, -EIO,
+ "chip id mismatch: %x!=%llx\n",
+ OS02G10_CHIPID, chip_id);
+
+ return 0;
+}
+
+static int os02g10_parse_endpoint(struct os02g10 *os02g10)
+{
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ unsigned long link_freq_bitmap;
+ struct fwnode_handle *ep;
+ int ret;
+
+ ep = fwnode_graph_get_next_endpoint(dev_fwnode(os02g10->dev), NULL);
+ if (!ep)
+ return dev_err_probe(os02g10->dev, -ENXIO,
+ "Failed to get next endpoint\n");
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) {
+ ret = dev_err_probe(os02g10->dev, -EINVAL,
+ "only 2 data lanes are supported\n");
+ goto error_out;
+ }
+
+ ret = v4l2_link_freq_to_bitmap(os02g10->dev, bus_cfg.link_frequencies,
+ bus_cfg.nr_of_link_frequencies,
+ link_freq_menu_items,
+ ARRAY_SIZE(link_freq_menu_items),
+ &link_freq_bitmap);
+ if (ret) {
+ ret = dev_err_probe(os02g10->dev, -EINVAL,
+ "only 720MHz frequency is available\n");
+ goto error_out;
+ }
+
+ os02g10->link_freq_index = __ffs(link_freq_bitmap);
+
+error_out:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+};
+
+static int os02g10_probe(struct i2c_client *client)
+{
+ struct os02g10 *os02g10;
+ unsigned int xclk_freq;
+ int ret;
+
+ os02g10 = devm_kzalloc(&client->dev, sizeof(*os02g10), GFP_KERNEL);
+ if (!os02g10)
+ return -ENOMEM;
+
+ os02g10->dev = &client->dev;
+
+ v4l2_i2c_subdev_init(&os02g10->sd, client, &os02g10_subdev_ops);
+ os02g10->sd.internal_ops = &os02g10_internal_ops;
+
+ os02g10->cci = devm_cci_regmap_init_i2c(client, 8);
+ if (IS_ERR(os02g10->cci))
+ return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->cci),
+ "failed to initialize CCI\n");
+
+ /* Set Current page to 0 */
+ os02g10->current_page = 0;
+
+ ret = devm_mutex_init(os02g10->dev, &os02g10->page_lock);
+ if (ret)
+ return dev_err_probe(os02g10->dev, ret,
+ "Failed to initialize lock\n");
+
+ /* Get system clock (xvclk) */
+ os02g10->xclk = devm_v4l2_sensor_clk_get(os02g10->dev, NULL);
+ if (IS_ERR(os02g10->xclk))
+ return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->xclk),
+ "failed to get xclk\n");
+
+ xclk_freq = clk_get_rate(os02g10->xclk);
+ if (xclk_freq != OS02G10_XCLK_FREQ)
+ return dev_err_probe(os02g10->dev, -EINVAL,
+ "xclk frequency not supported: %u Hz\n",
+ xclk_freq);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(os02g10_supply_name); i++)
+ os02g10->supplies[i].supply = os02g10_supply_name[i];
+
+ ret = devm_regulator_bulk_get(os02g10->dev,
+ ARRAY_SIZE(os02g10_supply_name),
+ os02g10->supplies);
+ if (ret)
+ return dev_err_probe(os02g10->dev, ret,
+ "failed to get regulators\n");
+
+ ret = os02g10_parse_endpoint(os02g10);
+ if (ret)
+ return dev_err_probe(os02g10->dev, ret,
+ "failed to parse endpoint configuration\n");
+
+ os02g10->reset_gpio = devm_gpiod_get_optional(os02g10->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(os02g10->reset_gpio))
+ return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->reset_gpio),
+ "failed to get reset GPIO\n");
+
+ ret = os02g10_power_on(os02g10->dev);
+ if (ret)
+ return ret;
+
+ ret = os02g10_identify_module(os02g10);
+ if (ret)
+ goto error_power_off;
+
+ ret = os02g10_init_controls(os02g10);
+ if (ret)
+ goto error_power_off;
+
+ /* Initialize subdev */
+ os02g10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ os02g10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ os02g10->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&os02g10->sd.entity, 1, &os02g10->pad);
+ if (ret) {
+ dev_err_probe(os02g10->dev, ret, "failed to init entity pads\n");
+ goto error_handler_free;
+ }
+
+ os02g10->sd.state_lock = os02g10->handler.lock;
+ ret = v4l2_subdev_init_finalize(&os02g10->sd);
+ if (ret) {
+ dev_err_probe(os02g10->dev, ret, "subdev init error\n");
+ goto error_media_entity;
+ }
+
+ pm_runtime_set_active(os02g10->dev);
+ pm_runtime_enable(os02g10->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&os02g10->sd);
+ if (ret) {
+ dev_err_probe(os02g10->dev, ret,
+ "failed to register os02g10 sub-device\n");
+ goto error_subdev_cleanup;
+ }
+
+ pm_runtime_idle(os02g10->dev);
+ return 0;
+
+error_subdev_cleanup:
+ v4l2_subdev_cleanup(&os02g10->sd);
+ pm_runtime_disable(os02g10->dev);
+ pm_runtime_set_suspended(os02g10->dev);
+
+error_media_entity:
+ media_entity_cleanup(&os02g10->sd.entity);
+
+error_handler_free:
+ v4l2_ctrl_handler_free(os02g10->sd.ctrl_handler);
+
+error_power_off:
+ os02g10_power_off(os02g10->dev);
+
+ return ret;
+}
+
+static void os02g10_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct os02g10 *os02g10 = to_os02g10(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_subdev_cleanup(&os02g10->sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(os02g10->sd.ctrl_handler);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev)) {
+ os02g10_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ }
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(os02g10_pm_ops,
+ os02g10_power_off, os02g10_power_on, NULL);
+
+static const struct of_device_id os02g10_id[] = {
+ { .compatible = "ovti,os02g10" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, os02g10_id);
+
+static struct i2c_driver os02g10_driver = {
+ .driver = {
+ .name = "os02g10",
+ .pm = pm_ptr(&os02g10_pm_ops),
+ .of_match_table = os02g10_id,
+ },
+ .probe = os02g10_probe,
+ .remove = os02g10_remove,
+};
+module_i2c_driver(os02g10_driver);
+
+MODULE_DESCRIPTION("OS02G10 Camera Sensor Driver");
+MODULE_AUTHOR("Tarang Raval <tarang.raval@siliconsignals.io>");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: media: i2c: Add os02g10 sensor
From: Elgin Perumbilly @ 2026-04-11 9:47 UTC (permalink / raw)
To: sakari.ailus, tarang.raval
Cc: Elgin Perumbilly, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
Vladimir Zapolskiy, Mehdi Djait, Xiaolei Wang, Laurent Pinchart,
Walter Werner Schneider, Kate Hsuan, Sylvain Petinot,
Bryan O'Donoghue, Svyatoslav Ryhel, Hardevsinh Palaniya,
linux-media, devicetree, linux-kernel
In-Reply-To: <20260411094723.129738-1-elgin.perumbilly@siliconsignals.io>
Add bindings for Omnivision OS02G10 sensor.
Signed-off-by: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
---
.../bindings/media/i2c/ovti,os02g10.yaml | 96 +++++++++++++++++++
MAINTAINERS | 7 ++
2 files changed, 103 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
new file mode 100644
index 000000000000..06e2b7dc4bdd
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/ovti,os05b10.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: OmniVision OS02G10 Image Sensor
+
+maintainers:
+ - Tarang Raval <tarang.raval@siliconsignals.io>
+
+description:
+ The OmniVision OS02G10 is a 2MP (1920x1080) color CMOS image sensor controlled
+ through an I2C-compatible SCCB bus. it outputs RAW10 format.
+
+properties:
+ compatible:
+ const: ovti,os02g10
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: XCLK clock
+
+ avdd-supply:
+ description: Analog Domain Power Supply (2.8v)
+
+ dovdd-supply:
+ description: I/O Domain Power Supply (1.8v)
+
+ dvdd-supply:
+ description: Digital core Power Supply (1.5v)
+
+ reset-gpios:
+ maxItems: 1
+ description: Reset Pin GPIO Control (active low)
+
+ port:
+ description: MIPI CSI-2 transmitter port
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ additionalProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ - items:
+ - const: 1
+ - const: 2
+ required:
+ - data-lanes
+ - link-frequencies
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - avdd-supply
+ - dovdd-supply
+ - dvdd-supply
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ camera-sensor@3c {
+ compatible = "ovti,os02g10";
+ reg = <0x3c>;
+ clocks = <&os02g10_clk>;
+ reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&os02g10_avdd_2v8>;
+ dvdd-supply = <&os02g10_dvdd_1v2>;
+ dovdd-supply = <&os2gb10_dovdd_1v8>;
+
+ port {
+ cam_out: endpoint {
+ remote-endpoint = <&mipi_in_cam>;
+ data-lanes = <1 2>;
+ link-frequencies = /bits/ 64 <720000000>;
+ };
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 900fc00b73e6..8a0a55073c30 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19443,6 +19443,13 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/ovti,og0ve1b.yaml
F: drivers/media/i2c/og0ve1b.c
+OMNIVISION OS02G10 SENSOR DRIVER
+M: Tarang Raval <tarang.raval@siliconsignals.io>
+M: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
+
OMNIVISION OS05B10 SENSOR DRIVER
M: Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
M: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
--
2.34.1
^ permalink raw reply related
* [PATCH 0/2] media: i2c: Add os02g10 camera sensor driver
From: Elgin Perumbilly @ 2026-04-11 9:47 UTC (permalink / raw)
To: sakari.ailus, tarang.raval
Cc: Elgin Perumbilly, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
Vladimir Zapolskiy, Mehdi Djait, Laurent Pinchart, Xiaolei Wang,
Walter Werner Schneider, Kate Hsuan, Sylvain Petinot,
Bryan O'Donoghue, Svyatoslav Ryhel, Hardevsinh Palaniya,
linux-media, devicetree, linux-kernel
The following features are supported:
- Manual exposure an gain control support.
- vblank/hblank control support.
- vflip/hflip control support
- Test pattern control support.
- Supported resolution: 1920 x 1080 @ 30fps (SBGGR10).
The driver is tested on mainline branch v7.0-rc2 on IMX8MP Debix Model a.
debix@imx8mp-debix:~$ v4l2-compliance -d /dev/v4l-subdev3
v4l2-compliance 1.31.0-5387, 64 bits, 64-bit time_t
v4l2-compliance SHA: 5508bc4301ac 2025-08-25 08:14:22
Compliance test for device /dev/v4l-subdev3:
Driver Info:
Driver version : 7.0.0
Capabilities : 0x00000000
Client Capabilities: 0x0000000000000002
interval-uses-which
Required ioctls:
test VIDIOC_SUDBEV_QUERYCAP: OK
test invalid ioctls: OK
Allow for multiple opens:
test second /dev/v4l-subdev3 open: OK
test VIDIOC_SUBDEV_QUERYCAP: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_LOG_STATUS: OK (Not Supported)
Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 0 Audio Inputs: 0 Tuners: 0
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0
Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)
Control ioctls:
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
test VIDIOC_QUERYCTRL: OK
test VIDIOC_G/S_CTRL: OK
test VIDIOC_G/S/TRY_EXT_CTRLS: OK
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 13 Private Controls: 0
Format ioctls:
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
test VIDIOC_G/S_PARM: OK (Not Supported)
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK (Not Supported)
test VIDIOC_TRY_FMT: OK (Not Supported)
test VIDIOC_S_FMT: OK (Not Supported)
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)
Codec ioctls:
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
Buffer ioctls:
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
test CREATE_BUFS maximum buffers: OK
test VIDIOC_REMOVE_BUFS: OK
test VIDIOC_EXPBUF: OK (Not Supported)
test Requests: OK (Not Supported)
test blocking wait: OK (Not Supported)
Total for device /dev/v4l-subdev3: 46, Succeeded: 46, Failed: 0, Warnings: 0
Elgin Perumbilly (2):
dt-bindings: media: i2c: Add os02g10 sensor
media: i2c: add os02g10 image sensor driver
.../bindings/media/i2c/ovti,os02g10.yaml | 96 ++
MAINTAINERS | 8 +
drivers/media/i2c/Kconfig | 10 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/os02g10.c | 1010 +++++++++++++++++
5 files changed, 1125 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
create mode 100644 drivers/media/i2c/os02g10.c
--
2.34.1
^ permalink raw reply
* Re: [PATCH v3 1/4] ARM: dts: qcom: msm8960: expressatt: Sort node references and includes
From: David Heidelberg @ 2026-04-11 8:33 UTC (permalink / raw)
To: Krzysztof Kozlowski, Rudraksha Gupta, Bjorn Andersson,
Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <f51ddaf0-a857-4250-ad9e-e50982e8c108@kernel.org>
On 11/04/2026 09:38, Krzysztof Kozlowski wrote:
> On 11/04/2026 06:01, Rudraksha Gupta wrote:
>> On 4/7/26 14:46, David Heidelberg wrote:
>>> On 07/04/2026 23:04, Krzysztof Kozlowski wrote:
>>>> On 07/04/2026 22:39, Rudraksha Gupta wrote:
>>>>> On 4/7/26 12:59, Krzysztof Kozlowski wrote:
>>>>>> On 01/04/2026 22:32, Rudraksha Gupta via B4 Relay wrote:
>>>>>>> From: Rudraksha Gupta <guptarud@gmail.com>
>>>>>>>
>>>>>>> Reorganize the DTS file for consistency with other msm8960 board
>>>>>>> files.
>>>>>>>
>>>>>>> Assisted-by: Claude:claude-opus-4.6
>>>>>>> Signed-off-by: Rudraksha Gupta <guptarud@gmail.com>
>>>>>>> ---
>>>>>>> .../dts/qcom/qcom-msm8960-samsung-expressatt.dts | 408
>>>>>>> +++++++++++----------
>>>>>>> 1 file changed, 207 insertions(+), 201 deletions(-)
>>>>>>>
>>>>>> Sorry, but no. We are not taking Claude as one determining coding
>>>>>> style.
>>>>>> Are we going to do the work again the moment we come with proper tool?
>>>>>
>>>>> There is no tool currently to auto format DTS, and doesn't seem to be
>>>>> coming for a while:
>>>>>
>>>>> https://www.youtube.com/watch?v=cvoIbTL_ZQA
>>>>>
>>>>>
>>>>> Claude didn't determine the coding style. I did based on sony-huashan,
>>>>> which is already upstream:
>>>>>
>>>>> https://github.com/torvalds/linux/blob/master/arch/arm/boot/dts/qcom/qcom-msm8960-sony-huashan.dts
>>>>>
>>>>>
>>>>>
>>>>> I just used Claude to do the manual work for me. In v2, I made sure the
>>>>> diff before and after the change was nill. v3 included additional
>>>>> changes requested by Konrad and some comments that I remembered during
>>>>> prior attempts mainlining patch series for this device.
>>>>
>>>> IMO, it is just too risky to let Claude reorganize the nodes, but I
>>>> assume reviewers of your code did run dtx_diff.
>>>
>>> I think it depends on the prompt. Since I’m performing many of the
>>> same tasks repeatedly across multiple sdm845 devices, asking an LLM to
>>> do node-by-node reorganization can be reasonably reviewable (at least
>>> when reviewing incremental progress, not just the final diff).
>>>
>>> I would prefer to do more of the sorting myself, but I find it quite
>>> tedious. The diff tool struggles when similar or identical lines
>>> appear in different nodes, which often results in a messy final diff
>>> (I noticed this in Sajattack’s sdm845 LG patchset).
>>>
>>> This leads me to an idea:
>>>
>>> For these sorting cleanups, perhaps we could introduce a “squash mode”?
>>>
>>> Contributors could submit commits per node, making the reorganization
>>> clearly visible (and ensuring nothing is accidentally lost), and then
>>> the maintainer could squash them into a single commit to avoid
>>> cluttering the git log.
>>>
>>> What do you think?
>>
>> Easiest solution would be to get Claude to make a DTS auto formatter. I
>> estimate it would likely take a couple iterations to get a functional
>> prototype and max a week to get it into a mergable state, if the style
>> is agreed beforehand. Simply provide DTS'es that follow the pattern you
>> like to Claude, then tell Claude that you want to make a Python script
>
> Yeah, and who wants to review the Claude code?
If it's good quality, I would do (depends on the language ofc).
>
> I already have a formatter to review in the pipeline...
Nice!
>
>> to auto format DTS files and make functions for each different common
>> style pattern identified in the DTS'es. I assume it would give a good
>> enough base to work off of. The most painful part will be determining
>> what the correct style for all DTS'es as I'm sure others will have
>> opinions on that.
>>
>
> The biggest problem is that no way maintainers will run untrusted 3rd
> party code, co-contributed by random people with Claude.
Generally, LLMs can be forced/instructed to write human-reviewable code, the
main issue is people usually don't ask for human-readable code.
David
>
>
> Best regards,
> Krzysztof
--
David Heidelberg
^ permalink raw reply
* Re: [PATCH v3 1/4] ARM: dts: qcom: msm8960: expressatt: Sort node references and includes
From: Krzysztof Kozlowski @ 2026-04-11 7:38 UTC (permalink / raw)
To: Rudraksha Gupta, David Heidelberg, Bjorn Andersson, Konrad Dybcio,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <a0eb87b5-7bb8-4aae-83a1-982d8b4c06f8@gmail.com>
On 11/04/2026 06:01, Rudraksha Gupta wrote:
> On 4/7/26 14:46, David Heidelberg wrote:
>> On 07/04/2026 23:04, Krzysztof Kozlowski wrote:
>>> On 07/04/2026 22:39, Rudraksha Gupta wrote:
>>>> On 4/7/26 12:59, Krzysztof Kozlowski wrote:
>>>>> On 01/04/2026 22:32, Rudraksha Gupta via B4 Relay wrote:
>>>>>> From: Rudraksha Gupta <guptarud@gmail.com>
>>>>>>
>>>>>> Reorganize the DTS file for consistency with other msm8960 board
>>>>>> files.
>>>>>>
>>>>>> Assisted-by: Claude:claude-opus-4.6
>>>>>> Signed-off-by: Rudraksha Gupta <guptarud@gmail.com>
>>>>>> ---
>>>>>> .../dts/qcom/qcom-msm8960-samsung-expressatt.dts | 408
>>>>>> +++++++++++----------
>>>>>> 1 file changed, 207 insertions(+), 201 deletions(-)
>>>>>>
>>>>> Sorry, but no. We are not taking Claude as one determining coding
>>>>> style.
>>>>> Are we going to do the work again the moment we come with proper tool?
>>>>
>>>> There is no tool currently to auto format DTS, and doesn't seem to be
>>>> coming for a while:
>>>>
>>>> https://www.youtube.com/watch?v=cvoIbTL_ZQA
>>>>
>>>>
>>>> Claude didn't determine the coding style. I did based on sony-huashan,
>>>> which is already upstream:
>>>>
>>>> https://github.com/torvalds/linux/blob/master/arch/arm/boot/dts/qcom/qcom-msm8960-sony-huashan.dts
>>>>
>>>>
>>>>
>>>> I just used Claude to do the manual work for me. In v2, I made sure the
>>>> diff before and after the change was nill. v3 included additional
>>>> changes requested by Konrad and some comments that I remembered during
>>>> prior attempts mainlining patch series for this device.
>>>
>>> IMO, it is just too risky to let Claude reorganize the nodes, but I
>>> assume reviewers of your code did run dtx_diff.
>>
>> I think it depends on the prompt. Since I’m performing many of the
>> same tasks repeatedly across multiple sdm845 devices, asking an LLM to
>> do node-by-node reorganization can be reasonably reviewable (at least
>> when reviewing incremental progress, not just the final diff).
>>
>> I would prefer to do more of the sorting myself, but I find it quite
>> tedious. The diff tool struggles when similar or identical lines
>> appear in different nodes, which often results in a messy final diff
>> (I noticed this in Sajattack’s sdm845 LG patchset).
>>
>> This leads me to an idea:
>>
>> For these sorting cleanups, perhaps we could introduce a “squash mode”?
>>
>> Contributors could submit commits per node, making the reorganization
>> clearly visible (and ensuring nothing is accidentally lost), and then
>> the maintainer could squash them into a single commit to avoid
>> cluttering the git log.
>>
>> What do you think?
>
> Easiest solution would be to get Claude to make a DTS auto formatter. I
> estimate it would likely take a couple iterations to get a functional
> prototype and max a week to get it into a mergable state, if the style
> is agreed beforehand. Simply provide DTS'es that follow the pattern you
> like to Claude, then tell Claude that you want to make a Python script
Yeah, and who wants to review the Claude code?
I already have a formatter to review in the pipeline...
> to auto format DTS files and make functions for each different common
> style pattern identified in the DTS'es. I assume it would give a good
> enough base to work off of. The most painful part will be determining
> what the correct style for all DTS'es as I'm sure others will have
> opinions on that.
>
The biggest problem is that no way maintainers will run untrusted 3rd
party code, co-contributed by random people with Claude.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v3 1/4] ARM: dts: qcom: msm8960: expressatt: Sort node references and includes
From: Krzysztof Kozlowski @ 2026-04-11 7:36 UTC (permalink / raw)
To: David Heidelberg, Rudraksha Gupta, Bjorn Andersson, Konrad Dybcio,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <eb1eebd2-75c0-4ffe-95e7-9f5d5d02edd1@ixit.cz>
On 07/04/2026 23:46, David Heidelberg wrote:
> On 07/04/2026 23:04, Krzysztof Kozlowski wrote:
>> On 07/04/2026 22:39, Rudraksha Gupta wrote:
>>>
>>> On 4/7/26 12:59, Krzysztof Kozlowski wrote:
>>>> On 01/04/2026 22:32, Rudraksha Gupta via B4 Relay wrote:
>>>>> From: Rudraksha Gupta <guptarud@gmail.com>
>>>>>
>>>>> Reorganize the DTS file for consistency with other msm8960 board files.
>>>>>
>>>>> Assisted-by: Claude:claude-opus-4.6
>>>>> Signed-off-by: Rudraksha Gupta <guptarud@gmail.com>
>>>>> ---
>>>>> .../dts/qcom/qcom-msm8960-samsung-expressatt.dts | 408 +++++++++++----------
>>>>> 1 file changed, 207 insertions(+), 201 deletions(-)
>>>>>
>>>> Sorry, but no. We are not taking Claude as one determining coding style.
>>>> Are we going to do the work again the moment we come with proper tool?
>>>
>>> There is no tool currently to auto format DTS, and doesn't seem to be
>>> coming for a while:
>>>
>>> https://www.youtube.com/watch?v=cvoIbTL_ZQA
>>>
>>>
>>> Claude didn't determine the coding style. I did based on sony-huashan,
>>> which is already upstream:
>>>
>>> https://github.com/torvalds/linux/blob/master/arch/arm/boot/dts/qcom/qcom-msm8960-sony-huashan.dts
>>>
>>>
>>> I just used Claude to do the manual work for me. In v2, I made sure the
>>> diff before and after the change was nill. v3 included additional
>>> changes requested by Konrad and some comments that I remembered during
>>> prior attempts mainlining patch series for this device.
>>
>> IMO, it is just too risky to let Claude reorganize the nodes, but I
>> assume reviewers of your code did run dtx_diff.
>
> I think it depends on the prompt. Since I’m performing many of the same tasks
> repeatedly across multiple sdm845 devices, asking an LLM to do node-by-node
> reorganization can be reasonably reviewable (at least when reviewing incremental
> progress, not just the final diff).
>
> I would prefer to do more of the sorting myself, but I find it quite tedious.
> The diff tool struggles when similar or identical lines appear in different
> nodes, which often results in a messy final diff (I noticed this in Sajattack’s
> sdm845 LG patchset).
>
> This leads me to an idea:
>
> For these sorting cleanups, perhaps we could introduce a “squash mode”?
>
> Contributors could submit commits per node, making the reorganization clearly
> visible (and ensuring nothing is accidentally lost), and then the maintainer
> could squash them into a single commit to avoid cluttering the git log.
>
> What do you think?
It would help review on the lists, although the actual solution is to
use deterministic tools. That's why usage of Claude is wrong. It
requires you and us to thoroughly review it. If you did not thoroughly
review that patch, you would be sending microslop.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v2 13/13] arm64: defconfig: Enable I3C and SPD5118 hwmon
From: Guenter Roeck @ 2026-04-11 7:20 UTC (permalink / raw)
To: Akhil R, krzk
Cc: Frank.Li, acpica-devel, alexandre.belloni, conor+dt, devicetree,
ebiggers, krzk+dt, lenb, linux-acpi, linux-hwmon, linux-i3c,
linux-kernel, miquel.raynal, p.zabel, rafael, robh, sakari.ailus,
wsa+renesas
In-Reply-To: <20260411053433.49655-1-akhilrajeev@nvidia.com>
On 4/10/26 22:34, Akhil R wrote:
[ ... ]
>>>> And it
>>>> should bring me clear rule what I can or cannot remove from defconfig,
>>>> if in 2 years I come and start pruning it from symbols.
>
> I am still a little confused on what information would likely accept (and
> keep) these configs in the defconfig. Would updating the commit message
> as below work?
>
> "These configs enable the support for SPD5118 within the
> Small-Outline-Compression-Attached Memory Modules (SOCAMM) LPDDR5X found
> in the NVIDIA Vera CPUs. The Vera CPU uses ACPI and is part of platforms
> such as Vera Rubin."
>
It is quite interesting that we argue about SPD5118 which is mandatory in
DDR5 systems. At the same time, CONFIG_IGB_HWMON, CONFIG_SENSORS_MACSMC_HWMON,
CONFIG_SENSORS_RASPBERRYPI_HWMON, and CONFIG_RTC_DRV_DS3232_HWMON _are_
enabled in arm64:defconfig. CONFIG_IGB_HWMON is even built-in.
It is kind of difficult to understand why those are more important than
the temperature sensor on DDR5 modules (or the temperature sensor on DDR4
modules, for that matter).
I don't know what the policy for defconfig is, but just based on that it does
seem to lack consistency.
A separate question is if it is time to enable I3C in default configurations.
I'd think so - more and more chip vendors support it, and presumably they would
not invest in it if there was no demand, but that is just my personal opinion.
Guenter
^ permalink raw reply
* Re: [PATCH 00/35] irqchip/qcom-pdc: Clean up register mapping and DT descriptions
From: Mukesh Ojha @ 2026-04-11 6:55 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Konrad Dybcio, cros-qcom-dts-watchers, linux-arm-msm,
linux-kernel, devicetree
In-Reply-To: <adm1uXe6QRes8DiX@baldur>
On Fri, Apr 10, 2026 at 09:48:25PM -0500, Bjorn Andersson wrote:
> On Sat, Apr 11, 2026 at 12:10:37AM +0530, Mukesh Ojha wrote:
> > The Qualcomm PDC (Power Domain Controller) hardware exposes multiple DRV
> > (Driver) regions, each 0x10000 bytes in size, where each region serves a
> > specific client in the system . Linux only needs access to the APSS DRV
> > region.
> >
> > Despite this, the driver was mapping up to 0x30000 bytes (three DRV
> > regions) via a QCOM_PDC_SIZE clamp introduced as a workaround for old
> > sm8150 DTs that described a too-small register window. Correspondingly,
> > most platform DTS files described the PDC reg as 0x30000 in size, and
> > several also carried a second, entirely unused reg entry pointing at an
> > unrelated register region that the driver never maps.
> >
> > This series cleans all of that up in three logical steps:
> >
> > 1. (patches 2-6):
> >
>
> These patches are for the IRQ subsystem/maintainer.
It's bad on my part that I didn't expect this to be merged in the first
iteration itself, but yes, I could have put PDC register sizing and
related device tree binding and driver changes together with the device
tree as one set. Like in the current patch series, it would be patches
1, 3, and all "Fix PDC reg size..." patches or may be the removing the
2nd register space change as well in one set and pdc driver clean up as
separate ?
>
> > Split __pdc_enable_intr() into two focused per-version helpers
> > to separate the HW < 3.2 bank-based path from the HW >= 3.2 per-pin
> > path. Replace the pdc_version global with a function pointer assigned
> > once at probe time, moving the version check out of the hot path.
> > Tighten the ioremap clamp from QCOM_PDC_SIZE (0x30000) to PDC_DRV_SIZE
> > (0x10000) now that the DT fixes below make the workaround unnecessary.
> > Also add a PDC_VERSION() constructor macro and use FIELD_GET() for bank
> > index extraction to make the bit encoding self-documenting.
> >
> > 2. (patches 1, 7-28):
> >
>
> And these patches are for the Qualcomm SoC/DT tree.
Yes, As I said, I could have done better..
>
> > All 28 platform DTS files that described the PDC reg window as 0x30000
> > are corrected to 0x10000, reflecting the single APSS DRV region that
> > Linux actually maps.
> >
> > 3. (patches 29-35):
> >
>
> Same with these.
>
>
> I don't see any dependencies between the IRQ and DT patches, can they be
> merged independently? Why did you send them together?
For better context, register sizing changes can be sent together
including binding, driver, and DT's as well.
>
> Regards,
> Bjorn
>
> > Seven platform DTS files (kaanapali, lemans, milos, monaco, sc8280xp,
> > sdx75, talos) carried a second reg entry pointing at an unrelated
> > hardware block. The driver only ever calls of_address_to_resource(node,
> > 0, ...) so this second entry was never mapped or accessed. Remove it.
> >
> > The net result is that every PDC node in the tree now describes exactly
> > one register region of exactly 0x10000 bytes — the APSS DRV region that
> > the driver actually uses — and the driver's ioremap clamp matches that
> > reality.
> >
> > Mukesh Ojha (35):
> > dt-bindings: qcom,pdc: Tighten reg to single APSS DRV region
> > irqchip/qcom-pdc: Split __pdc_enable_intr() into per-version helpers
> > irqchip/qcom-pdc: Tighten ioremap clamp to single DRV region size
> > irqchip/qcom-pdc: Replace pdc_version global with a function pointer
> > irqchip/qcom-pdc: Add PDC_VERSION() macro to describe version register
> > fields
> > irqchip/qcom-pdc: Use FIELD_GET() to extract bank index and bit
> > position
> > arm64: dts: qcom: sdm845: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sdm670: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sc7180: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sc7280: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sc8180x: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sm8150: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sc8280xp: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sm8250: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sm8350: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sm8450: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sm8550: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sm8650: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sm4450: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: x1e80100: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sm6350: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sar2130p: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: qcs615: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: qcs8300: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sa8775p: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: sdx75: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: milos: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: qdu1000: Fix PDC reg size to single APSS DRV region
> > arm64: dts: qcom: kaanapali: Drop unused second PDC reg entry
> > arm64: dts: qcom: lemans: Drop unused second PDC reg entry
> > arm64: dts: qcom: milos: Drop unused second PDC reg entry
> > arm64: dts: qcom: monaco: Drop unused second PDC reg entry
> > arm64: dts: qcom: sc8280xp: Drop unused second PDC reg entry
> > arm64: dts: qcom: sdx75: Drop unused second PDC reg entry
> > arm64: dts: qcom: talos: Drop unused second PDC reg entry
> >
> > .../interrupt-controller/qcom,pdc.yaml | 2 +-
> > arch/arm64/boot/dts/qcom/hamoa.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/kaanapali.dtsi | 3 +-
> > arch/arm64/boot/dts/qcom/kodiak.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/lemans.dtsi | 3 +-
> > arch/arm64/boot/dts/qcom/milos.dtsi | 3 +-
> > arch/arm64/boot/dts/qcom/monaco.dtsi | 3 +-
> > arch/arm64/boot/dts/qcom/qdu1000.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sar2130p.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sc7180.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sc8180x.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sdm670.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sdm845.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sdx75.dtsi | 3 +-
> > arch/arm64/boot/dts/qcom/sm4450.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sm6350.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sm8150.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sm8250.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sm8350.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sm8450.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sm8550.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/sm8650.dtsi | 2 +-
> > arch/arm64/boot/dts/qcom/talos.dtsi | 3 +-
> > drivers/irqchip/qcom-pdc.c | 56 +++++++++++--------
> > 25 files changed, 57 insertions(+), 53 deletions(-)
> >
> > --
> > 2.53.0
> >
--
-Mukesh Ojha
^ permalink raw reply
* Re: [PATCH 04/35] irqchip/qcom-pdc: Replace pdc_version global with a function pointer
From: Mukesh Ojha @ 2026-04-11 6:23 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Konrad Dybcio, cros-qcom-dts-watchers, linux-arm-msm,
linux-kernel, devicetree
In-Reply-To: <adm0X2ybeG5McXVv@baldur>
On Fri, Apr 10, 2026 at 09:43:10PM -0500, Bjorn Andersson wrote:
> On Sat, Apr 11, 2026 at 12:10:41AM +0530, Mukesh Ojha wrote:
> > Now that the two enable paths are separate functions, replace the
> > pdc_version global with a __pdc_enable_intr function pointer. The
> > pointer is assigned once at probe time based on the version register,
> > moving the version comparison out of the interrupt enable/disable hot
> > path entirely.
>
> That's what the patch does, but why?
I thought, it was odd to compare against the version every time during
enable/disable instead of clearing the path to take at probe time itself.
however, I don't have data to prove how hot this path is ?
> >
> > Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
> > ---
> > drivers/irqchip/qcom-pdc.c | 13 +++----------
> > 1 file changed, 3 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c
> > index 21e2b4b884ee..734576cdce0c 100644
> > --- a/drivers/irqchip/qcom-pdc.c
> > +++ b/drivers/irqchip/qcom-pdc.c
> > @@ -51,7 +51,7 @@ static void __iomem *pdc_base;
> > static void __iomem *pdc_prev_base;
> > static struct pdc_pin_region *pdc_region;
> > static int pdc_region_cnt;
> > -static unsigned int pdc_version;
> > +static void (*__pdc_enable_intr)(int pin_out, bool on);
> > static bool pdc_x1e_quirk;
> >
> > static void pdc_base_reg_write(void __iomem *base, int reg, u32 i, u32 val)
> > @@ -123,14 +123,6 @@ static void pdc_enable_intr_cfg(int pin_out, bool on)
> > pdc_reg_write(IRQ_i_CFG, pin_out, enable);
> > }
> >
> > -static void __pdc_enable_intr(int pin_out, bool on)
> > -{
> > - if (pdc_version < PDC_VERSION_3_2)
> > - pdc_enable_intr_bank(pin_out, on);
> > - else
> > - pdc_enable_intr_cfg(pin_out, on);
>
> This style is comfortable to read.
Agree, code readingwise, this looks easier..
>
> > -}
> > -
> > static void pdc_enable_intr(struct irq_data *d, bool on)
> > {
> > unsigned long flags;
> > @@ -400,7 +392,8 @@ static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *pare
> > goto fail;
> > }
> >
> > - pdc_version = pdc_reg_read(PDC_VERSION_REG, 0);
> > + __pdc_enable_intr = (pdc_reg_read(PDC_VERSION_REG, 0) < PDC_VERSION_3_2) ?
> > + pdc_enable_intr_bank : pdc_enable_intr_cfg;
>
> This style is a mess.
>
> Regards,
> Bjorn
>
> >
> > parent_domain = irq_find_host(parent);
> > if (!parent_domain) {
> > --
> > 2.53.0
> >
--
-Mukesh Ojha
^ permalink raw reply
* Re: [PATCH v5 4/5] remoteproc: qcom: pas: Add late attach support for subsystems
From: Jie Gan @ 2026-04-11 6:04 UTC (permalink / raw)
To: Jingyi Wang, Bjorn Andersson, Mathieu Poirier, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Manivannan Sadhasivam,
Luca Weiss, Bartosz Golaszewski, Konrad Dybcio
Cc: aiqun.yu, tingwei.zhang, trilok.soni, yijie.yang, linux-arm-msm,
linux-remoteproc, devicetree, linux-kernel,
Gokul Krishna Krishnakumar
In-Reply-To: <20260409-knp-soccp-v5-4-805a492124da@oss.qualcomm.com>
On 4/9/2026 4:52 PM, Jingyi Wang wrote:
> Subsystems can be brought out of reset by entities such as bootloaders.
> As the irq enablement could be later than subsystem bring up, the state
> of subsystem should be checked by reading SMP2P bits and performing ping
> test.
>
> A new qcom_pas_attach() function is introduced. if a crash state is
> detected for the subsystem, rproc_report_crash() is called. If the
> subsystem is ready and the ping is successful, it will be marked as
> "attached". If ready irq is not received, it could be the early boot
> feature is not supported by other entities. In this case, the state will
> be marked as RPROC_OFFLINE so that the PAS driver can load the firmware
> and start the remoteproc.
>
> Co-developed-by: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
> Signed-off-by: Gokul Krishna Krishnakumar <gokul.krishnakumar@oss.qualcomm.com>
> Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
> ---
> drivers/remoteproc/qcom_q6v5.c | 69 ++++++++++++++++++++++++++++++++
> drivers/remoteproc/qcom_q6v5.h | 6 +++
> drivers/remoteproc/qcom_q6v5_pas.c | 80 ++++++++++++++++++++++++++++++++++++--
> 3 files changed, 152 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c
> index 58d5b85e58cd..52247c17c38a 100644
> --- a/drivers/remoteproc/qcom_q6v5.c
> +++ b/drivers/remoteproc/qcom_q6v5.c
> @@ -20,6 +20,7 @@
>
> #define Q6V5_LOAD_STATE_MSG_LEN 64
> #define Q6V5_PANIC_DELAY_MS 200
> +#define Q6V5_PING_TIMEOUT_MS 500
>
> static int q6v5_load_state_toggle(struct qcom_q6v5 *q6v5, bool enable)
> {
> @@ -234,6 +235,74 @@ unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5)
> }
> EXPORT_SYMBOL_GPL(qcom_q6v5_panic);
>
> +static irqreturn_t q6v5_pong_interrupt(int irq, void *data)
> +{
> + struct qcom_q6v5 *q6v5 = data;
> +
> + complete(&q6v5->ping_done);
> +
> + return IRQ_HANDLED;
> +}
> +
> +int qcom_q6v5_ping_subsystem(struct qcom_q6v5 *q6v5)
> +{
> + int ret;
> + int ping_failed = 0;
> +
> + reinit_completion(&q6v5->ping_done);
> +
> + /* Set master kernel Ping bit */
> + ret = qcom_smem_state_update_bits(q6v5->ping_state,
> + BIT(q6v5->ping_bit), BIT(q6v5->ping_bit));
> + if (ret) {
> + dev_err(q6v5->dev, "Failed to update ping bits\n");
> + return ret;
> + }
> +
> + ret = wait_for_completion_timeout(&q6v5->ping_done, msecs_to_jiffies(Q6V5_PING_TIMEOUT_MS));
> + if (!ret) {
> + ping_failed = -ETIMEDOUT;
> + dev_err(q6v5->dev, "Failed to get back pong\n");
> + }
> +
> + /* Clear ping bit master kernel */
> + ret = qcom_smem_state_update_bits(q6v5->ping_state, BIT(q6v5->ping_bit), 0);
> + if (ret) {
> + dev_err(q6v5->dev, "Failed to clear master kernel bits\n");
> + return ret;
> + }
> +
> + return ping_failed;
> +}
> +EXPORT_SYMBOL_GPL(qcom_q6v5_ping_subsystem);
> +
> +int qcom_q6v5_ping_subsystem_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev)
> +{
> + int ret = -ENODEV;
> +
> + q6v5->ping_state = devm_qcom_smem_state_get(&pdev->dev, "ping", &q6v5->ping_bit);
> + if (IS_ERR(q6v5->ping_state)) {
> + dev_err(&pdev->dev, "Failed to acquire smem state %ld\n",
> + PTR_ERR(q6v5->ping_state));
> + return PTR_ERR(q6v5->ping_state);
> + }
> +
> + init_completion(&q6v5->ping_done);
> +
> + q6v5->pong_irq = platform_get_irq_byname(pdev, "pong");
> + if (q6v5->pong_irq < 0)
> + return q6v5->pong_irq;
> +
> + ret = devm_request_threaded_irq(&pdev->dev, q6v5->pong_irq, NULL,
> + q6v5_pong_interrupt, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> + "q6v5 pong", q6v5);
> + if (ret)
> + dev_err(&pdev->dev, "Failed to acquire pong IRQ\n");
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(qcom_q6v5_ping_subsystem_init);
> +
> /**
> * qcom_q6v5_init() - initializer of the q6v5 common struct
> * @q6v5: handle to be initialized
> diff --git a/drivers/remoteproc/qcom_q6v5.h b/drivers/remoteproc/qcom_q6v5.h
> index 5a859c41896e..5025ffc4dbe8 100644
> --- a/drivers/remoteproc/qcom_q6v5.h
> +++ b/drivers/remoteproc/qcom_q6v5.h
> @@ -17,22 +17,26 @@ struct qcom_q6v5 {
> struct rproc *rproc;
>
> struct qcom_smem_state *state;
> + struct qcom_smem_state *ping_state;
> struct qmp *qmp;
>
> struct icc_path *path;
>
> unsigned stop_bit;
> + unsigned int ping_bit;
>
> int wdog_irq;
> int fatal_irq;
> int ready_irq;
> int handover_irq;
> int stop_irq;
> + int pong_irq;
>
> bool handover_issued;
>
> struct completion start_done;
> struct completion stop_done;
> + struct completion ping_done;
>
> int crash_reason;
>
> @@ -52,5 +56,7 @@ int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5);
> int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon);
> int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout);
> unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5);
> +int qcom_q6v5_ping_subsystem(struct qcom_q6v5 *q6v5);
> +int qcom_q6v5_ping_subsystem_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev);
>
> #endif
> diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
> index da27d1d3c9da..34b54cf832d0 100644
> --- a/drivers/remoteproc/qcom_q6v5_pas.c
> +++ b/drivers/remoteproc/qcom_q6v5_pas.c
> @@ -60,6 +60,7 @@ struct qcom_pas_data {
> int region_assign_count;
> bool region_assign_shared;
> int region_assign_vmid;
> + bool early_boot;
> };
>
> struct qcom_pas {
> @@ -423,9 +424,15 @@ static int qcom_pas_stop(struct rproc *rproc)
>
> qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
>
> - handover = qcom_q6v5_unprepare(&pas->q6v5);
> - if (handover)
> - qcom_pas_handover(&pas->q6v5);
> + /*
> + * qcom_q6v5_prepare is not called in qcom_pas_attach, skip unprepare to
> + * avoid mismatch.
> + */
> + if (pas->rproc->state != RPROC_ATTACHED) {
> + handover = qcom_q6v5_unprepare(&pas->q6v5);
> + if (handover)
> + qcom_pas_handover(&pas->q6v5);
> + }
>
> if (pas->smem_host_id)
> ret = qcom_smem_bust_hwspin_lock_by_host(pas->smem_host_id);
> @@ -510,6 +517,63 @@ static unsigned long qcom_pas_panic(struct rproc *rproc)
> return qcom_q6v5_panic(&pas->q6v5);
> }
>
> +static int qcom_pas_attach(struct rproc *rproc)
> +{
> + int ret;
> + struct qcom_pas *pas = rproc->priv;
> + bool ready_state;
> + bool crash_state;
> +
> + pas->q6v5.running = true;
> + ret = irq_get_irqchip_state(pas->q6v5.fatal_irq,
> + IRQCHIP_STATE_LINE_LEVEL, &crash_state);
> +
> + if (ret)
> + goto disable_running;
> +
> + if (crash_state) {
> + dev_err(pas->dev, "Subsystem has crashed before driver probe\n");
> + rproc_report_crash(rproc, RPROC_FATAL_ERROR);
> + ret = -EINVAL;
> + goto disable_running;
> + }
> +
> + ret = irq_get_irqchip_state(pas->q6v5.ready_irq,
> + IRQCHIP_STATE_LINE_LEVEL, &ready_state);
> +
> + if (ret)
> + goto disable_running;
> +
> + if (unlikely(!ready_state)) {
> + /*
> + * The bootloader may not support early boot, mark the state as
> + * RPROC_OFFLINE so that the PAS driver can load the firmware and
> + * start the remoteproc.
> + */
> + dev_err(pas->dev, "Failed to get subsystem ready interrupt\n");
> + pas->rproc->state = RPROC_OFFLINE;
> + ret = -EINVAL;
> + goto disable_running;
> + }
> +
> + ret = qcom_q6v5_ping_subsystem(&pas->q6v5);
> +
> + if (ret) {
> + dev_err(pas->dev, "Failed to ping subsystem, assuming device crashed\n");
> + rproc_report_crash(rproc, RPROC_FATAL_ERROR);
> + goto disable_running;
> + }
> +
> + pas->q6v5.handover_issued = true;
> +
> + return 0;
> +
> +disable_running:
> + pas->q6v5.running = false;
> +
> + return ret;
> +}
> +
> static const struct rproc_ops qcom_pas_ops = {
> .unprepare = qcom_pas_unprepare,
> .start = qcom_pas_start,
> @@ -518,6 +582,7 @@ static const struct rproc_ops qcom_pas_ops = {
> .parse_fw = qcom_pas_parse_firmware,
> .load = qcom_pas_load,
> .panic = qcom_pas_panic,
> + .attach = qcom_pas_attach,
Possible issue in the future here. The kaanapali_soccp_resource does not
set minidump_id, so this is not triggered today, but it is a latent bug
for any future device that sets both early_boot and minidump_id.
qcom_pas_attach is added to qcom_pas_ops but not to
qcom_pas_minidump_ops. When a device with minidump_id set uses the
minidump ops table, the .attach pointer is NULL. rproc_attach_device()
checks if (rproc->ops->attach) before calling it, so the attach callback
is silently skipped. For a device with early_boot = true and minidump_id
!= 0, the state is set to RPROC_DETACHED in probe, but the attach logic
(crash check, ready check, ping) is never executed, leaving the
subsystem in an inconsistent state.
Thanks,
Jie
> };
>
> static const struct rproc_ops qcom_pas_minidump_ops = {
> @@ -855,6 +920,15 @@ static int qcom_pas_probe(struct platform_device *pdev)
>
> pas->pas_ctx->use_tzmem = rproc->has_iommu;
> pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu;
> +
> + if (desc->early_boot) {
> + ret = qcom_q6v5_ping_subsystem_init(&pas->q6v5, pdev);
> + if (ret)
> + dev_warn(&pdev->dev, "Falling back to firmware load\n");
> + else
> + pas->rproc->state = RPROC_DETACHED;
> + }
> +
> ret = rproc_add(rproc);
> if (ret)
> goto remove_ssr_sysmon;
>
^ permalink raw reply
* Re: [PATCH v2 02/13] ACPICA: Read LVR from the I2C resource descriptor
From: Akhil R @ 2026-04-11 5:41 UTC (permalink / raw)
To: rafael
Cc: acpica-devel, akhilrajeev, alexandre.belloni, conor+dt,
devicetree, ebiggers, frank.li, krzk+dt, lenb, linux-acpi,
linux-hwmon, linux-i3c, linux-kernel, linux, miquel.raynal,
p.zabel, robh, sakari.ailus, wsa+renesas
In-Reply-To: <CAJZ5v0jW9=VtjyjeoEDTm9hrGKP_BYUeuKCxcKoV-VsvM5otiA@mail.gmail.com>
On Fri, 10 Apr 2026 12:59:06 +0200, Rafael J. Wysocki wrote:
> On Fri, Apr 10, 2026 at 6:45 AM Akhil R <akhilrajeev@nvidia.com> wrote:
>>
>> On Thu, 9 Apr 2026 22:04:05 -0400, Frank Li wrote:
>>> On Thu, Apr 09, 2026 at 04:27:32PM +0530, Akhil R wrote:
>>>> ACPI 6.3 specifies byte 8 of I2C Serial Bus Connection descriptor to be
>>>> used for Legacy Virtual Register (LVR) data as specified in the MIPI
>>>> I3C Specification for an I2C device connected to an I3C Host Controller.
>>>> LVR will be read by I3C host controller drivers and it provides details
>>>> about the specific speed and 50ns spike filter capabilities of I2C
>>>> devices.
>>>>
>>>> Update the rsconvert_info to include this field. For I2C devices on an
>>>> I2C bus, this field is Reserved and unused.
>>>>
>>>> This commit is the result of squashing the following:
>>>> ACPICA commit 70082dc8fc847673ac7f4bbb1541776730f0b63e
>>>> ACPICA commit e62e74baf7e08cf059ec82049aeccd565b24d661
>>>> ACPICA commit c404118235108012cad396c834b5aabe2dd1b51a
>>>> ACPICA commit 7650d4a889ea7907060bfce89f4f780ce83e7b28
>>>> ACPICA commit 014fa9f2dbcc6b1bd42a4a4a6f6705d9cf7d460b
>>>
>>> These commit number is not existed at linus official tree. Please remove it.
>>
>> These are commits from ACPI-CA github. The files in the acpica folder is
>> a mirror of that repo. I suppose the commits in this folder are expected
>> to be structured like this. The process is also described here -
>> https://docs.kernel.org/driver-api/acpi/linuxized-acpica.html
>
> While the above is correct overall, it would also be sufficient to use
> Link: tags pointing to those commits.
Ack. I will drop the commit IDs in the next version.
Best Regards,
Akhil
^ permalink raw reply
* Re: [PATCH v2 13/13] arm64: defconfig: Enable I3C and SPD5118 hwmon
From: Akhil R @ 2026-04-11 5:34 UTC (permalink / raw)
To: krzk
Cc: Frank.Li, acpica-devel, akhilrajeev, alexandre.belloni, conor+dt,
devicetree, ebiggers, krzk+dt, lenb, linux-acpi, linux-hwmon,
linux-i3c, linux-kernel, linux, miquel.raynal, p.zabel, rafael,
robh, sakari.ailus, wsa+renesas
In-Reply-To: <5c751739-5044-4d23-9648-8d46dd0945d1@kernel.org>
On Fri, 10 Apr 2026 11:57:11 +0200, Krzysztof Kozlowski wrote:
> On 10/04/2026 10:37, Akhil R wrote:
>> On Fri, 10 Apr 2026 09:18:48 +0200, Krzysztof Kozlowski wrote:
>>> On 10/04/2026 08:57, Guenter Roeck wrote:
>>>> On 4/9/26 23:39, Krzysztof Kozlowski wrote:
>>>>> On 09/04/2026 12:57, Akhil R wrote:
>>>>>> Add I3C subsystem support, DesignWare I3C master controller, and
>>>>>> SPD5118 hwmon sensor as modules to the defconfig and therefore
>>>>>> enable the support for SPD5118 sensor on SOCAMM found in NVIDIA
>>>>>> Vera platforms.
>>>>>
>>>>> git grep for "Vera" gave me zero results. Are you sure this is an
>>>>> upstream platform? Please point the DTS using this.
>>>>>
>>>>
>>>> I think this is an ACPI based system, or at least that is what Google search
>>>> tells me.
>>>
>>> Thanks. Following Google Vera is either a "CPU" or entire architecture
>>> (at least that's how they call it), so it does not have SPD5118 sensor.
>>
>> SOCAMM is a Memory Module. SPD5118, as it's Kconfig mentions, is a sensor
>> found within such memory modules. I didn't quite get why would you state
>> that the SOCAMM present in Vera architecture (or CPU) does not have
>> SPD5118 in it.
>
> I said that CPU or entire architecture does not have it.
>
> Commit is pretty vague in helping me to figure out the things I asked
> for in last email.
>
>
>>
>> Pasting the below from the Vera Rubin product page [1] -
>> "NVIDIA Vera CPUs add enhanced serviceability with small-outline
>> compression-attached memory modules (SOCAMM) LPDDR5X and in-system tests
>> for the CPU cores."
>>
>> [1]: https://www.nvidia.com/en-us/data-center/technologies/rubin/
>
> So this is for Vera Rubin? For what is this exactly?
SOCAMM is with the Vera CPU. Any Vera based platform would have this module.
Vera Rubin is one such platform.
SPD5118 is within the SOCAMM.
>
>>
>>>
>>>
>>> "Nvidia vera socamm" gives me something about "rubin". It's not me who
>>> should be guessing all this.
>>>
>>> "nvidia vera socamm SPD5118" gives me even less, so justification is flaky.
>>>
>>> To remind, this commit msg should convince why generic kernel for
>>> developers affecting all possible platforms - not end users, because
>>> they always use distro kernels - should enable these configs. And it
>>> should bring me clear rule what I can or cannot remove from defconfig,
>>> if in 2 years I come and start pruning it from symbols.
>>
>> I found little details on what we should be adding in the defconfig. It
>
> Then maybe we should not be adding it to defconfig?
>
> We usually do not make changes which we do not know why we are making
> them. IOW, every commit must be useful for the community and this is
> achieved by either explicit or implicit answer why doing this.
>
> And I gave in the past clear guidelines - this is config for the
> upstream kernel developers to use the upstream hardware and/or for
> distros to understand what is needed to support that upstream hardware
> (although last part in theory, because many distros do variantion of
> allmodconfig, so they don't really care about our defconfig).
>
>> would help if you could share some guidance. Do you mean to say that the
>> defconfig should include only the configs which are necessary in
>> development platforms and not in end-products?
>
> No, the type of product does not matter because upstream supports every
> type of product. Upstream does not say "oh, you run end-product, so we
> don't care about you". But defconfig is not used by endusers and has
> zero meaning to them.
>
> It seems to missed or ignored one more reason I wrote:
>
>>> And it
>>> should bring me clear rule what I can or cannot remove from defconfig,
>>> if in 2 years I come and start pruning it from symbols.
I am still a little confused on what information would likely accept (and
keep) these configs in the defconfig. Would updating the commit message
as below work?
"These configs enable the support for SPD5118 within the
Small-Outline-Compression-Attached Memory Modules (SOCAMM) LPDDR5X found
in the NVIDIA Vera CPUs. The Vera CPU uses ACPI and is part of platforms
such as Vera Rubin."
Regards,
Akhil
^ permalink raw reply
* Re: [PATCH v3 1/4] ARM: dts: qcom: msm8960: expressatt: Sort node references and includes
From: Rudraksha Gupta @ 2026-04-11 4:01 UTC (permalink / raw)
To: David Heidelberg, Krzysztof Kozlowski, Bjorn Andersson,
Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <eb1eebd2-75c0-4ffe-95e7-9f5d5d02edd1@ixit.cz>
On 4/7/26 14:46, David Heidelberg wrote:
> On 07/04/2026 23:04, Krzysztof Kozlowski wrote:
>> On 07/04/2026 22:39, Rudraksha Gupta wrote:
>>> On 4/7/26 12:59, Krzysztof Kozlowski wrote:
>>>> On 01/04/2026 22:32, Rudraksha Gupta via B4 Relay wrote:
>>>>> From: Rudraksha Gupta <guptarud@gmail.com>
>>>>>
>>>>> Reorganize the DTS file for consistency with other msm8960 board
>>>>> files.
>>>>>
>>>>> Assisted-by: Claude:claude-opus-4.6
>>>>> Signed-off-by: Rudraksha Gupta <guptarud@gmail.com>
>>>>> ---
>>>>> .../dts/qcom/qcom-msm8960-samsung-expressatt.dts | 408
>>>>> +++++++++++----------
>>>>> 1 file changed, 207 insertions(+), 201 deletions(-)
>>>>>
>>>> Sorry, but no. We are not taking Claude as one determining coding
>>>> style.
>>>> Are we going to do the work again the moment we come with proper tool?
>>>
>>> There is no tool currently to auto format DTS, and doesn't seem to be
>>> coming for a while:
>>>
>>> https://www.youtube.com/watch?v=cvoIbTL_ZQA
>>>
>>>
>>> Claude didn't determine the coding style. I did based on sony-huashan,
>>> which is already upstream:
>>>
>>> https://github.com/torvalds/linux/blob/master/arch/arm/boot/dts/qcom/qcom-msm8960-sony-huashan.dts
>>>
>>>
>>>
>>> I just used Claude to do the manual work for me. In v2, I made sure the
>>> diff before and after the change was nill. v3 included additional
>>> changes requested by Konrad and some comments that I remembered during
>>> prior attempts mainlining patch series for this device.
>>
>> IMO, it is just too risky to let Claude reorganize the nodes, but I
>> assume reviewers of your code did run dtx_diff.
>
> I think it depends on the prompt. Since I’m performing many of the
> same tasks repeatedly across multiple sdm845 devices, asking an LLM to
> do node-by-node reorganization can be reasonably reviewable (at least
> when reviewing incremental progress, not just the final diff).
>
> I would prefer to do more of the sorting myself, but I find it quite
> tedious. The diff tool struggles when similar or identical lines
> appear in different nodes, which often results in a messy final diff
> (I noticed this in Sajattack’s sdm845 LG patchset).
>
> This leads me to an idea:
>
> For these sorting cleanups, perhaps we could introduce a “squash mode”?
>
> Contributors could submit commits per node, making the reorganization
> clearly visible (and ensuring nothing is accidentally lost), and then
> the maintainer could squash them into a single commit to avoid
> cluttering the git log.
>
> What do you think?
Easiest solution would be to get Claude to make a DTS auto formatter. I
estimate it would likely take a couple iterations to get a functional
prototype and max a week to get it into a mergable state, if the style
is agreed beforehand. Simply provide DTS'es that follow the pattern you
like to Claude, then tell Claude that you want to make a Python script
to auto format DTS files and make functions for each different common
style pattern identified in the DTS'es. I assume it would give a good
enough base to work off of. The most painful part will be determining
what the correct style for all DTS'es as I'm sure others will have
opinions on that.
^ permalink raw reply
* Re: [PATCH v5 4/5] remoteproc: qcom: pas: Add late attach support for subsystems
From: Bjorn Andersson @ 2026-04-11 3:04 UTC (permalink / raw)
To: Jingyi Wang
Cc: Mathieu Poirier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Manivannan Sadhasivam, Luca Weiss, Bartosz Golaszewski,
Konrad Dybcio, aiqun.yu, tingwei.zhang, trilok.soni, yijie.yang,
linux-arm-msm, linux-remoteproc, devicetree, linux-kernel,
Gokul Krishna Krishnakumar
In-Reply-To: <20260409-knp-soccp-v5-4-805a492124da@oss.qualcomm.com>
On Thu, Apr 09, 2026 at 01:52:27AM -0700, Jingyi Wang wrote:
[..]
> diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
> index da27d1d3c9da..34b54cf832d0 100644
> --- a/drivers/remoteproc/qcom_q6v5_pas.c
> +++ b/drivers/remoteproc/qcom_q6v5_pas.c
> @@ -60,6 +60,7 @@ struct qcom_pas_data {
> int region_assign_count;
> bool region_assign_shared;
> int region_assign_vmid;
> + bool early_boot;
> };
>
> struct qcom_pas {
> @@ -423,9 +424,15 @@ static int qcom_pas_stop(struct rproc *rproc)
>
> qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
>
> - handover = qcom_q6v5_unprepare(&pas->q6v5);
> - if (handover)
> - qcom_pas_handover(&pas->q6v5);
> + /*
> + * qcom_q6v5_prepare is not called in qcom_pas_attach, skip unprepare to
> + * avoid mismatch.
Can you confirm that no load_state should be sent to AOSS for SoCCP?
(I.e. from the skipped qcom_q6v5_prepare())
Regards,
Bjorn
^ permalink raw reply
* Re: [PATCH v5 5/5] remoteproc: qcom_q6v5_pas: Add SoCCP node on Kaanapali
From: Bjorn Andersson @ 2026-04-11 3:01 UTC (permalink / raw)
To: Jingyi Wang
Cc: Mathieu Poirier, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Manivannan Sadhasivam, Luca Weiss, Bartosz Golaszewski,
Konrad Dybcio, aiqun.yu, tingwei.zhang, trilok.soni, yijie.yang,
linux-arm-msm, linux-remoteproc, devicetree, linux-kernel,
Dmitry Baryshkov
In-Reply-To: <20260409-knp-soccp-v5-5-805a492124da@oss.qualcomm.com>
On Thu, Apr 09, 2026 at 01:52:28AM -0700, Jingyi Wang wrote:
> The SoC Control Processor (SoCCP) is small RISC-V MCU that controls
> USB Type-C, battery charging and various other functions on Qualcomm SoCs.
> It provides a solution for control-plane processing, reducing per-subsystem
> microcontroller reinvention. Add support for SoCCP PAS loader on Kaanapali
> platform.
>
Very nice commit message.
(Patch looks good as well)
Thank you,
Bjorn
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
> ---
> drivers/remoteproc/qcom_q6v5_pas.c | 18 ++++++++++++++++++
> 1 file changed, 18 insertions(+)
>
> diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
> index 34b54cf832d0..1c81f22438cc 100644
> --- a/drivers/remoteproc/qcom_q6v5_pas.c
> +++ b/drivers/remoteproc/qcom_q6v5_pas.c
> @@ -1604,8 +1604,26 @@ static const struct qcom_pas_data sm8750_mpss_resource = {
> .region_assign_vmid = QCOM_SCM_VMID_MSS_MSA,
> };
>
> +static const struct qcom_pas_data kaanapali_soccp_resource = {
> + .crash_reason_smem = 656,
> + .firmware_name = "soccp.mbn",
> + .dtb_firmware_name = "soccp_dtb.mbn",
> + .pas_id = 51,
> + .dtb_pas_id = 0x41,
> + .proxy_pd_names = (char*[]){
> + "cx",
> + "mx",
> + NULL
> + },
> + .ssr_name = "soccp",
> + .sysmon_name = "soccp",
> + .auto_boot = true,
> + .early_boot = true,
> +};
> +
> static const struct of_device_id qcom_pas_of_match[] = {
> { .compatible = "qcom,eliza-adsp-pas", .data = &sm8550_adsp_resource },
> + { .compatible = "qcom,kaanapali-soccp-pas", .data = &kaanapali_soccp_resource },
> { .compatible = "qcom,milos-adsp-pas", .data = &sm8550_adsp_resource },
> { .compatible = "qcom,milos-cdsp-pas", .data = &milos_cdsp_resource },
> { .compatible = "qcom,milos-mpss-pas", .data = &sm8450_mpss_resource },
>
> --
> 2.34.1
>
^ permalink raw reply
* RE: [PATCH v2 2/3] remoteproc: imx_rproc: Pass bootaddr to SM CPU/LMM reset vector
From: Peng Fan @ 2026-04-11 3:00 UTC (permalink / raw)
To: Mathieu Poirier, Peng Fan (OSS)
Cc: Bjorn Andersson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Daniel Baluta, linux-remoteproc@vger.kernel.org,
devicetree@vger.kernel.org, imx@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
In-Reply-To: <adkcugNgyrkHtUML@p14s>
> Subject: Re: [PATCH v2 2/3] remoteproc: imx_rproc: Pass bootaddr to
> SM CPU/LMM reset vector
>
> On Thu, Apr 09, 2026 at 08:30:54AM +0800, Peng Fan wrote:
> > On Wed, Apr 08, 2026 at 09:46:32AM -0600, Mathieu Poirier wrote:
> > >On Wed, Apr 08, 2026 at 01:30:16AM +0000, Peng Fan wrote:
> > >> > Subject: Re: [PATCH v2 2/3] remoteproc: imx_rproc: Pass
> bootaddr
> > >> > to SM CPU/LMM reset vector
> > >> >
> > >> [...]
> > >> >
> > >> > >
> > >> > > Aligning the ELF entry point with the hardware reset base on
> > >> > Cortex‑M
> > >> > > systems is possible, but it comes with several risks.
> > >> >
> > >> > I'm not asking to align the ELF entry point with the hardware
> reset base.
> > >> > All I want is to have the correct start address embedded in the
> > >> > ELF file to avoid having to use a mask.
> > >>
> > >> I see, per my understanding:
> > >> FreeRTOS typically exposes __isr_vector, which corresponds to the
> > >> hardware reset / vector table base.
> > >> Zephyr (Cortex‑M) exposes _vector_table, which serves the same
> purpose.
> > >> I am not certain about other RTOSes, but the pattern seems
> consistent:
> > >> the vector table base is already available as a named ELF symbol.
> > >>
> > >> Given that, if the preferred approach is to parse the ELF and
> > >> explicitly retrieve the hardware reset base, I can update the
> implementation accordingly.
> > >> If you prefer to parse the elf file to get the hardware reset base,
> > >> I could update to use them.
> > >>
> > >> Options1: Something as below:
> > >> 1. Include rproc_elf_find_symbol in remoteproc_elf_loader.c 2.
> Use
> > >> below in imx_rproc.c ret = rproc_elf_find_symbol(rproc, fw,
> > >> "__isr_vector", &vector_base); if (ret)
> > >> ret = rproc_elf_find_symbol(rproc, fw, "__vector_table",
> > >> &vector_base);
> > >>
> > >> if (!ret)
> > >> rproc->bootaddr = vector_base
> > >> else
> > >> dev_info(dev, "no __isr_vector or __vector_table\n")
> > >
> > >No
> >
> > If your concern is about rproc->bootaddr, I could introduce
> > imx_rproc->vector_base for i.MX. Please help detail a bit.
> >
> > >
> > >>
> > >> This makes the hardware reset base explicit, avoids masking
> e_entry.
> > >>
> > >> Option 2: User‑provided reset symbol via sysfs As an alternative,
> > >> we could expose a sysfs attribute, e.g. reset_symbol, allowing
> > >> users to specify the symbol name to be used as the reset base:
> > >>
> > >> echo __isr_vector >
> /sys/class/remoteproc/remoteprocX/reset_symbol
> > >>
> > >
> > >Definitely not.
> > >
> > >The definition of e_entry in the specification is clear, i.e "the
> > >address of the entry point from where the process starts executing".
> > >If masking is required because the tool that puts the image together
> > >gets the wrong address, then it should be fixed.
> >
> > The hardware reset base is the address from which the hardware
> fetches
> > the initial stack pointer and program counter values and loads them
> > into the SP and PC registers. In contrast, bootaddr (i.e. e_entry)
> > represents the address at which the CPU starts executing code (the
> PC
> > value after reset). As you pointed out earlier, this distinction is clear.
> >
> > In our case, we need to obtain the hardware reset base and pass that
> > value to the system firmware. However, e_entry should not be set to
> > the hardware reset base. Doing so would introduce the issues I
> > described in [1]. This means we should not modify the Zephyr or
> > FreeRTOS build outputs to make e_entry equal to the hardware reset
> base.
>
>
> As I said earlier, I am _not_ suggesting to make e_entry equal to the
> hardware reset base.
Let me try to restate my understanding more precisely and please
correct me if I am still missing the point.
From your comment:
"
If masking is required because the tool that puts the image together gets the
wrong address, then it should be fixed.
"
I understand this as saying that masking e_entry is not acceptable, because
e_entry already has a clear and correct meaning: it is the execution entry
address, and the kernel should not reinterpret or “fix up” that value.
At the same time, we still need to provide the hardware reset vector base
to the system firmware, and that value is distinct from e_entry.
On i.MX94/5 platforms the reset base is software‑programmable, but that
information is not represented by e_entry, nor is there currently a
separate place in the remoteproc framework to convey a reset‑vector
base independent of the execution entry point.
Given these constraints, I see limited options on the kernel side.
One conservative approach would be to rely on a fixed, platform‑defined
reset base for the affected SoCs. And update RTOS linking script to put
the vector to the location of fixed hardware reset base.
Thanks,
Peng
>
> We are going in circles here.
>
> >
> > Given these constraints, the feasible solutions I can see are either:
> > - option 1 (explicitly retrieving the hardware reset base), or
> > - continuing to use masking.
> >
> > Please suggest.
> >
> > [1]
> >
> https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2F
> lore
> > .kernel.org%2Fall%2Facs2PAZq2k3zjmDW%40shlinux89%2F&data=0
> 5%7C02%7Cpen
> >
> g.fan%40nxp.com%7C8a5ce35d492b4adb2d3b08de97192cbb%7C686
> ea1d3bc2b4c6fa
> >
> 92cd99c5c301635%7C0%7C0%7C639114331565834960%7CUnknow
> n%7CTWFpbGZsb3d8e
> >
> yJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsI
> kFOIjoiTWF
> >
> pbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=Pnkirz3BMEuLsJU9
> MHQNon84HIyMX
> > 08x1wCK04dS7VU%3D&reserved=0
> >
> > Thanks,
> > Peng
> >
> > >
> > >> The remoteproc core would then resolve that symbol from the ELF
> and
> > >> set rproc->bootaddr accordingly.
> > >> This provides maximum flexibility but does introduce a new
> > >> user‑visible ABI, so I see it more as an opt‑in or fallback
> mechanism.
> > >>
> > >> Please let me know which approach you prefer, and I will update
> > >> this series accordingly in v3..
> > >>
> > >> Thanks,
> > >> Peng.
> > >>
> > >>
> > >> >
> > >> > > 1, Semantic mismatch (ELF vs. hardware behavior) 2,
> Debuggers
> > >> > > may attempt to set breakpoints or start execution at the entry
> > >> > > symbol
> > >> > >
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox