Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH 1/2] dt-bindings: pwm: clk-pwm: add optional GPIO and pinctrl properties
From: Xilin Wu @ 2026-04-13  8:45 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Nikita Travkin,
	linux-pwm, devicetree, linux-kernel, linux-arm-msm
In-Reply-To: <adyr_17yvFGkihU5@monoceros>

On 4/13/2026 4:41 PM, Uwe Kleine-König wrote:
> Hello,
> 
> On Mon, Apr 06, 2026 at 11:50:01PM +0800, Xilin Wu wrote:
>> The clk-pwm driver cannot produce constant output levels (0% or 100%
>> duty cycle, or disabled state) through the clock hardware alone - the
>> actual pin level when the clock is off is undefined and
>> hardware-dependent.
>>
>> Document optional gpios, pinctrl-names, pinctrl-0, and pinctrl-1
>> properties that allow the driver to switch the pin between clock
>> function mux (for normal PWM output) and GPIO mode (to drive a
>> deterministic constant level).
>>
>> Signed-off-by: Xilin Wu <sophon@radxa.com>
>> ---
>>   Documentation/devicetree/bindings/pwm/clk-pwm.yaml | 36 +++++++++++++++++++++-
>>   1 file changed, 35 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/pwm/clk-pwm.yaml b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
>> index ec1768291503..2a0e3e02d27b 100644
>> --- a/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
>> +++ b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
>> @@ -15,6 +15,11 @@ description: |
>>     It's often possible to control duty-cycle of such clocks which makes them
>>     suitable for generating PWM signal.
>>   
>> +  Optionally, a GPIO and pinctrl states can be provided. When a constant
>> +  output level is needed (0%, 100%, or disabled), the pin is switched to
> 
> A constant output isn't needed when disabled. The state is undefined
> then. A plain clk_disable() is fine then.
> 
> Best regards
> Uwe

Ack. I will change the binding description and the driver behavior in 
the next revision.

Thank you for the review.

-- 
Best regards,
Xilin Wu <sophon@radxa.com>


^ permalink raw reply

* Re: [PATCH v2 09/13] i3c: dw-i3c-master: Add a quirk to skip clock and reset
From: Alexandre Belloni @ 2026-04-13  8:45 UTC (permalink / raw)
  To: Akhil R
  Cc: frank.li, acpica-devel, conor+dt, devicetree, ebiggers, krzk+dt,
	lenb, linux-acpi, linux-hwmon, linux-i3c, linux-kernel, linux,
	miquel.raynal, p.zabel, rafael, robert.moore, robh, sakari.ailus,
	wsa+renesas
In-Reply-To: <20260410060712.30377-1-akhilrajeev@nvidia.com>

On 10/04/2026 11:37:11+0530, Akhil R wrote:
> On Thu, 9 Apr 2026 22:45:09 -0400, Frank Li wrote:
> > On Thu, Apr 09, 2026 at 04:27:39PM +0530, Akhil R wrote:
> >> Some ACPI-enumerated devices like Tegra410 do not have clock and reset
> >> resources exposed via the clk/reset frameworks. Add a match data for
> > 
> > why not export fix clock at ACPI?
> > 
> >> such devices to skip acquiring clock and reset controls during probe.
> >>
> >> Move match data parsing before clock/reset acquisition so the quirk is
> >> available early enough.  When the quirk is set, fall back to reading
> >> the clock rate from the "clock-frequency" device property instead.
> > 
> > "clock-frequency" is legacy proptery.
> 
> The document ACPI on ARMv8 Servers [1] suggests not to use clock or reset
> framework at all. I also could not find any proper way to export the clock
> to the kernel from the ACPI table. The same document suggests to use only
> _DSD properties, if to pass any data to the kernel.
> 
> I can rename the property to 'default-clock-rate' if that sounds good.
> Please let me know your suggestion.
> 

"clock-frequency" is fine for ACPI

> [1]: https://www.kernel.org/doc/html/v6.1/arm64/arm-acpi.html#programmable-power-control-resources
> 
> Best Regards,
> Akhil

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply

* Re: [PATCH v1] arm64: dts: qcom: lemans: Enable DISPLAY-PORT
From: Konrad Dybcio @ 2026-04-13  8:44 UTC (permalink / raw)
  To: Kumar Anurag, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <20260413043713.1659-1-kumar.singh@oss.qualcomm.com>

On 4/13/26 6:37 AM, Kumar Anurag wrote:
> Add dailinks for DISPLAY-PORT to enable audio functionality
> on edp0.

"DisplayPort"

The commit title is misleading.

[...]

> +		dp0-dai-link {
> +			link-name = "DisplayPort0 Playback";
> +
> +			cpu {
> +				sound-dai = <&q6apmbedai DISPLAY_PORT_RX_0>;
> +			};
> +
> +			codec {
> +				sound-dai = <&mdss0_dp0>;
> +			};

'co'dec < 'cp'u, please re-sort

Konrad

^ permalink raw reply

* Re: [PATCH] arm64: dts: qcom: eliza: Fix reserved memory addresses & sizes
From: Konrad Dybcio @ 2026-04-13  8:43 UTC (permalink / raw)
  To: Alexander Koskovich, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <20260412-eliza-reserved-memory-fix-v1-1-05cb3e33a9fe@pm.me>

On 4/12/26 5:38 PM, Alexander Koskovich wrote:
> Update cpusys_vm_mem from 256KiB to 4MiB, cpucp_mem from 2MiB to 1MiB
> and fix cpucp_scandump_mem node name to match actual reg address.
> 
> This matches the downstream memmap and kera-reserved-memory.dtsi.

These changes also match the latest memory map release
(for the record: LA.1.0_v4)

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: pwm: clk-pwm: add optional GPIO and pinctrl properties
From: Uwe Kleine-König @ 2026-04-13  8:41 UTC (permalink / raw)
  To: Xilin Wu
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Nikita Travkin,
	linux-pwm, devicetree, linux-kernel, linux-arm-msm
In-Reply-To: <20260406-clk-pwm-gpio-v1-1-40d2f3a20aff@radxa.com>

[-- Attachment #1: Type: text/plain, Size: 1469 bytes --]

Hello,

On Mon, Apr 06, 2026 at 11:50:01PM +0800, Xilin Wu wrote:
> The clk-pwm driver cannot produce constant output levels (0% or 100%
> duty cycle, or disabled state) through the clock hardware alone - the
> actual pin level when the clock is off is undefined and
> hardware-dependent.
> 
> Document optional gpios, pinctrl-names, pinctrl-0, and pinctrl-1
> properties that allow the driver to switch the pin between clock
> function mux (for normal PWM output) and GPIO mode (to drive a
> deterministic constant level).
> 
> Signed-off-by: Xilin Wu <sophon@radxa.com>
> ---
>  Documentation/devicetree/bindings/pwm/clk-pwm.yaml | 36 +++++++++++++++++++++-
>  1 file changed, 35 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/pwm/clk-pwm.yaml b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
> index ec1768291503..2a0e3e02d27b 100644
> --- a/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
> +++ b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
> @@ -15,6 +15,11 @@ description: |
>    It's often possible to control duty-cycle of such clocks which makes them
>    suitable for generating PWM signal.
>  
> +  Optionally, a GPIO and pinctrl states can be provided. When a constant
> +  output level is needed (0%, 100%, or disabled), the pin is switched to

A constant output isn't needed when disabled. The state is undefined
then. A plain clk_disable() is fine then.

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: Phandles
From: Kyle Bonnici @ 2026-04-13  8:40 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Herve Codina, devicetree-compiler@vger.kernel.org, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, devicetree@vger.kernel.org
In-Reply-To: <e3b4c7c4-64cd-494c-b2c5-fa71a7303038@kernel.org>


> Please wrap your email responses to mailing list style.

Hope I got this part right in this email!

> To repeat my question:
> What does '1' stand for?

‘1' was just an random value to show the issue. Please note that 
this issue has more context. This can all be found tin the Zephyr issue 
https://github.com/zephyrproject-rtos/zephyr/issues/107066 
And also on the discord link.

To summarize, In zephyr the dts is pickled to a header file on
compile. Zephyr build system uses the bindings to determine types.
Any property with no type will not be pickled. 

`/zephyr,user` node is a convenience node that does not need a binding
And types are inferred from values not property names.

The reported issues:
- Zephyr is inferring a type but the DTC compiler is inferring another
type and reporting w waring about it in the build output
- Zephyr does not infer types for properties with no bindings, hence 
If any of mention properties are used and node has no binding for
that property name, zephyr will ignore this, but the DTC reports a
warning that contradics Zephyr's implementation.


^ permalink raw reply

* Re: [PATCH v3 1/2] arm64: dts: qcom: sdm845-google: Add dual front IMX355 cameras
From: Konrad Dybcio @ 2026-04-13  8:40 UTC (permalink / raw)
  To: david, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: Petr Hodina, Richard Acayan, linux-arm-msm, devicetree,
	linux-kernel, phone-devel
In-Reply-To: <20260412-pixel3-camera-v3-1-e26b090a6110@ixit.cz>

On 4/12/26 6:35 PM, David Heidelberg via B4 Relay wrote:
> From: David Heidelberg <david@ixit.cz>
> 
> The Pixel 3 features two front-facing Sony IMX355 sensors with
> different focal lengths (standard and wide-angle).

Fancy. Some Sony phone had a dual-focal-length lens on the back,
which IIUC involved some pretty heavy engineering


> +	ports {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		port@1 {
> +			reg = <1>;
> +			camss_endpoint1: endpoint {

\n before child nodes, please


> @@ -459,6 +618,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 {

I believe this may fail dtbs check

> +		pins = "gpio8";
> +		function = "gpio";
> +
> +		bias-disable;
> +		drive-strength = <2>;

Please don't add \n between these properties and align them in common order
(i.e. pins/func/drive-strength/bias), matching the rest of the file

Konrad


^ permalink raw reply

* Re: [PATCH 2/2] pwm: pxa: Add optional bus clock
From: Uwe Kleine-König @ 2026-04-13  8:38 UTC (permalink / raw)
  To: Yixun Lan
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Duje Mihanović, linux-pwm, devicetree, linux-kernel,
	linux-riscv, spacemit
In-Reply-To: <20260409-03-k3-pwm-drv-v1-2-1307a06fba38@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 1447 bytes --]

Hello,

On Thu, Apr 09, 2026 at 12:45:12AM +0000, Yixun Lan wrote:
> Add one secondary optional bus clock for the PWM PXA driver, also keep it
> compatible with old single clock.
> 
> The SpacemiT K3 SoC require one bus clock for PWM controller, acquire

s/one/a/ ?

> and enable it during probe phase.
> 
> Signed-off-by: Yixun Lan <dlan@kernel.org>
> ---
>  drivers/pwm/pwm-pxa.c | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
> index 0f5bdb0e395e..2ace31405c2d 100644
> --- a/drivers/pwm/pwm-pxa.c
> +++ b/drivers/pwm/pwm-pxa.c
> @@ -53,6 +53,7 @@ struct pxa_pwm_chip {
>  	struct device	*dev;
>  
>  	struct clk	*clk;
> +	struct clk	*bus_clk;
>  	void __iomem	*mmio_base;
>  };
>  
> @@ -177,7 +178,12 @@ static int pwm_probe(struct platform_device *pdev)
>  		return PTR_ERR(chip);
>  	pc = to_pxa_pwm_chip(chip);
>  
> -	pc->clk = devm_clk_get(dev, NULL);
> +	pc->bus_clk = devm_clk_get_optional_enabled(dev, "bus");
> +	if (IS_ERR(pc->bus_clk))
> +		return dev_err_probe(dev, PTR_ERR(pc->bus_clk), "Failed to get bus clock\n");
> +
> +	/* Get named func clk if bus clock is valid */
> +	pc->clk = devm_clk_get(dev, pc->bus_clk ? "func" : NULL);

A local variable for bus_clk would be sufficient.

I'm not sure, but I think passing "func" unconditionally to
devm_clk_get() would also work fine.

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH v3 2/2] arm64: dts: qcom: sdm845-google: Enable PMI8998 camera flash LED
From: Konrad Dybcio @ 2026-04-13  8:36 UTC (permalink / raw)
  To: david, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: Petr Hodina, Richard Acayan, linux-arm-msm, devicetree,
	linux-kernel, phone-devel, Dmitry Baryshkov
In-Reply-To: <20260412-pixel3-camera-v3-2-e26b090a6110@ixit.cz>

On 4/12/26 6:35 PM, David Heidelberg via B4 Relay wrote:
> 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.
> 
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> 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 070023a9813ce..e9d9842cb8674 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>
>  
> @@ -596,6 +597,19 @@ &pmi8998_charger {
>  	status = "okay";
>  };
>  
> +&pmi8998_flash {
> +	status = "okay";
> +
> +	led-1 {

"-0"?

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* Re: [PATCH 3/3] arm64: dts: qcom: eliza-mtp: Enable USB and ADSP support
From: Konrad Dybcio @ 2026-04-13  8:33 UTC (permalink / raw)
  To: Alexander Koskovich, Abel Vesa
  Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <adyfWzVHsg3qo9cH@prism>

On 4/13/26 9:52 AM, Alexander Koskovich wrote:
> On Tue, Mar 31, 2026 at 01:37:24PM +0300, Abel Vesa wrote:
>> The Eliza MTP features a single USB Type-C port. Its USB 2.0 lines are
>> routed through an eUSB2 repeater provided by the PM7750BA PMIC.
>>
>> Describe the port and repeater, and enable the USB controller and PHYs.
>>
>> Also specify the ADSP firmware and enable the remoteproc.
>>
>> Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
>> ---
>>  arch/arm64/boot/dts/qcom/eliza-mtp.dts | 83 ++++++++++++++++++++++++++++++++++
>>  1 file changed, 83 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/qcom/eliza-mtp.dts b/arch/arm64/boot/dts/qcom/eliza-mtp.dts
>> index 90f629800cb0..c31f00e36eee 100644
>> --- a/arch/arm64/boot/dts/qcom/eliza-mtp.dts
>> +++ b/arch/arm64/boot/dts/qcom/eliza-mtp.dts
>> @@ -6,9 +6,12 @@
>>  /dts-v1/;
> ...
>>  
>> +&usb_hsphy {
>> +	vdd-supply = <&vreg_l2b>;
> 
> Shouldn't this be l7k? Looking at kera-usb.dtsi I see the vdd-supply for
> eusb2_phy0 as l7k. I don't see this being overriden by anything else
> downstream either.
> 
> Just bringing it up since I copied this part for another Eliza platform
> (USB 2 only) and USB failed until I fixed this supply.

Looking at some diagrams, L7K seems to be the right one indeed

Konrad

^ permalink raw reply

* Re: [PATCH 05/35] irqchip/qcom-pdc: Add PDC_VERSION() macro to describe version register fields
From: Konrad Dybcio @ 2026-04-13  8:29 UTC (permalink / raw)
  To: Mukesh Ojha, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio
  Cc: cros-qcom-dts-watchers, linux-arm-msm, linux-kernel, devicetree
In-Reply-To: <20260410184124.1068210-6-mukesh.ojha@oss.qualcomm.com>

On 4/10/26 8:40 PM, Mukesh Ojha wrote:
> The PDC hardware version register encodes major, minor and step fields
> in byte-sized fields at bits [23:16], [15:8] and [7:0] respectively.
> The existing PDC_VERSION_3_2 constant was a bare magic number (0x30200)
> with no indication of this encoding.
> 
> Add GENMASK-based field definitions for each sub-field and a
> PDC_VERSION(maj, min, step) constructor macro using FIELD_PREP, making
> the encoding self-documenting. Replace the magic constant with
> PDC_VERSION(3, 2, 0).
> 
> Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
> ---

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* Re: [PATCH 02/35] irqchip/qcom-pdc: Split __pdc_enable_intr() into per-version helpers
From: Konrad Dybcio @ 2026-04-13  8:28 UTC (permalink / raw)
  To: Mukesh Ojha, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio
  Cc: cros-qcom-dts-watchers, linux-arm-msm, linux-kernel, devicetree
In-Reply-To: <20260410184124.1068210-3-mukesh.ojha@oss.qualcomm.com>

On 4/10/26 8:40 PM, Mukesh Ojha wrote:
> The __pdc_enable_intr() function contains a version branch that selects
> between two distinct enable mechanisms: a bank-based IRQ_ENABLE_BANK
> register for HW < 3.2, and a per-pin enable bit in IRQ_i_CFG for
> HW >= 3.2. These two paths share no code and serve different hardware.
> 
> Split them into two focused static functions: pdc_enable_intr_bank()
> for HW < 3.2 and pdc_enable_intr_cfg() for HW >= 3.2. No functional
> change.
> 
> Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
> ---

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* Re: [PATCH 00/35] irqchip/qcom-pdc: Clean up register mapping and DT descriptions
From: Konrad Dybcio @ 2026-04-13  8:27 UTC (permalink / raw)
  To: Mukesh Ojha, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio
  Cc: cros-qcom-dts-watchers, linux-arm-msm, linux-kernel, devicetree
In-Reply-To: <20260410184124.1068210-1-mukesh.ojha@oss.qualcomm.com>

On 4/10/26 8:40 PM, 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.

[...]

>   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

I believe that was intended for this feature:

https://lore.kernel.org/linux-arm-msm/1568411962-1022-8-git-send-email-ilina@codeaurora.org/

Is that something that ever turned out useful?

Konrad

^ permalink raw reply

* Re: [PATCH 2/2] media: i2c: add os02g10 image sensor driver
From: Laurent Pinchart @ 2026-04-13  8:25 UTC (permalink / raw)
  To: Elgin Perumbilly
  Cc: sakari.ailus, tarang.raval, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
	Vladimir Zapolskiy, Xiaolei Wang, Walter Werner Schneider,
	Kate Hsuan, Sylvain Petinot, Mehdi Djait, Heimir Thor Sverrisson,
	linux-media, devicetree, linux-kernel
In-Reply-To: <20260411094723.129738-3-elgin.perumbilly@siliconsignals.io>

Hello Elgin,

On Sat, Apr 11, 2026 at 03:17:05PM +0530, Elgin Perumbilly wrote:
> 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},
> +};

Let's try to avoid this in new driver. The mode configuration should be
dynamic, and not hardcoded to a specific mode.

> +
> +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");

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* Re: [PATCH 06/35] irqchip/qcom-pdc: Use FIELD_GET() to extract bank index and bit position
From: Konrad Dybcio @ 2026-04-13  8:25 UTC (permalink / raw)
  To: Mukesh Ojha, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio
  Cc: cros-qcom-dts-watchers, linux-arm-msm, linux-kernel, devicetree
In-Reply-To: <20260410184124.1068210-7-mukesh.ojha@oss.qualcomm.com>

On 4/10/26 8:40 PM, Mukesh Ojha wrote:
> The IRQ_ENABLE_BANK register is a bank of 32-bit words where each bit
> represents one PDC pin. The bank index and bit position within the bank
> are encoded in the flat pin number as bits [31:5] and [4:0] respectively.
> 
> Replace the open-coded division and modulo with FIELD_GET() and GENMASK()
> to make the bit extraction self-documenting and consistent with the
> FIELD_PREP() style already used in the PDC_VERSION() macro.
> 
> Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
> ---

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* Re: [PATCH 01/35] dt-bindings: qcom,pdc: Tighten reg to single APSS DRV region
From: Konrad Dybcio @ 2026-04-13  8:23 UTC (permalink / raw)
  To: Dmitry Baryshkov, Mukesh Ojha
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, cros-qcom-dts-watchers,
	linux-arm-msm, linux-kernel, devicetree
In-Reply-To: <gd5ixcfablbyyyz2wdacrvg43jogwg425na6utsgfcterm276k@tdko64tn6gwh>

On 4/11/26 4:32 PM, Dmitry Baryshkov wrote:
> On Sat, Apr 11, 2026 at 12:10:38AM +0530, Mukesh Ojha wrote:
>> The PDC has multiple DRV regions, each sized 0x10000, where each region
>> serves a specific client in the system. Linux only needs access to the
> 
> Nit: there are other OS than Linux. Would you rather point out that
> other DRV regions are to be used by ... what?

TZ, HYP, HLOS, CPUCP..

I'm wondering if we can make use of the HYP one on e.g. Glymur, to
parallelize accesses (and whether that would bring any practical
benefit).

In the RPMH architecture, each "client" has their own (GPU, AOP, DISP,
etc.). Then, each one of those clients may have an associates RSC
(Resource State Coordinator) and/or anyOf BCM ("interconnect"), VRM
("regulator"), ARC ("RPMHPD") voting interfaces

Konrad

^ permalink raw reply

* Re: [PATCH 2/4] soc: amlogic: clk-measure: Add A1 and T7 support
From: Jian Hu @ 2026-04-13  8:21 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Neil Armstrong, Jerome Brunet, Kevin Hilman,
	Michael Turquette, Martin Blumenstingl, robh+dt, Rob Herring
  Cc: devicetree, linux-amlogic, linux-kernel, linux-arm-kernel
In-Reply-To: <9a4f69e7-838a-4992-af1d-46324e14eb48@kernel.org>


On 4/12/2026 5:55 PM, Krzysztof Kozlowski wrote:
> [ EXTERNAL EMAIL ]
>
> On 10/04/2026 12:03, Jian Hu wrote:
>> Add support for the A1 and T7 SoC family in amlogic clk measure.
>>
>> Signed-off-by: Jian Hu <jian.hu@amlogic.com>
>> ---
>>   drivers/soc/amlogic/meson-clk-measure.c | 272 ++++++++++++++++++++++++
>>   1 file changed, 272 insertions(+)
>>
>> diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c
>> index d862e30a244e..083524671b76 100644
>> --- a/drivers/soc/amlogic/meson-clk-measure.c
>> +++ b/drivers/soc/amlogic/meson-clk-measure.c
>> @@ -787,6 +787,258 @@ static const struct meson_msr_id clk_msr_s4[] = {
>>
>>   };
>>
>> +static struct meson_msr_id clk_msr_a1[] = {
> And existing code uses what sort of array? Seems you send us obsolete or
> downstream code.


Thanks for your review.


I have checked the previous Amlogic SoC's commits. Such as Amlogic AXG, 
G12A, C3, S4.

The clk_msr_xx entry is added after last SoC's array, sorted by 
submissin date rather than alphabetical order.

So I place A1 and T7 after S4 accordingly.


The A1 clock controller driver was already supported in 
https://lore.kernel.org/all/20230523135351.19133-7-ddrokosov@sberdevices.ru/

It is also present in the mainline kernel: 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/clk/meson/Kconfig#n113


This clock measure IP is used to measure the internal clock paths 
frequencies,  and A1 clock controller driver was supported.

Since the corresponding clock measure driver does not support A1 yet, So 
add A1 clk msr here.


>
> Best regards,
> Krzysztof

^ permalink raw reply

* Re: [PATCH] dt-bindings: iio: light: Add PixArt PAJ7620 gesture sensor
From: Krzysztof Kozlowski @ 2026-04-13  8:20 UTC (permalink / raw)
  To: Harpreet Saini
  Cc: jic23, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Neil Armstrong,
	Bjorn Andersson, Marek Vasut, Kael D'Alcamo, Lad Prabhakar,
	linux-iio, devicetree, linux-kernel
In-Reply-To: <20260413000308.7618-1-sainiharpreet29@yahoo.com>

On Sun, Apr 12, 2026 at 08:02:55PM -0400, Harpreet Saini wrote:
> Signed-off-by: Harpreet Saini <sainiharpreet29@yahoo.com>
> ---

Please run scripts/checkpatch.pl on the patches and fix reported
warnings. After that, run also 'scripts/checkpatch.pl --strict' on the
patches and (probably) fix more warnings. Some warnings can be ignored,
especially from --strict run, but the code here looks like it needs a
fix. Feel free to get in touch if the warning is not clear.

> This is part 1 of a 2-patch series proposing a driver for the PixArt
> PAJ7620 gesture sensor. This patch adds the Device Tree bindings and 
> registers the "pixart" vendor prefix. The driver patch will follow in 
> a succeeding branch.

No, we do not want bindings without users. The driver patch must follow
here. We also do not work with branches, but patches on mailing list.

Please read submitting patches documents - both, so also the one in DT
binding dir.

I finished the review here.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v7 0/2] i2c: Add Loongson-2K0300 I2C controller support
From: Andy Shevchenko @ 2026-04-13  8:17 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andi Shyti, Wolfram Sang, Andy Shevchenko,
	linux-i2c, Huacai Chen, Xuerui Wang, loongarch, devicetree
In-Reply-To: <CAMpQs4+QmyAObvCcJOFnfTuufG8=M0q5m+XMkaP6-SOp-8wq9w@mail.gmail.com>

On Mon, Apr 13, 2026 at 11:08 AM Binbin Zhou <zhoubb.aaron@gmail.com> wrote:
> On Mon, Apr 13, 2026 at 3:52 PM Andy Shevchenko
> <andy.shevchenko@gmail.com> wrote:
> > On Sat, Apr 11, 2026 at 12:58 PM Binbin Zhou <zhoubinbin@loongson.cn> wrote:

...

> > >  - parent_rate_MHz -> parent_rate_mhz to avoid CamelCase.
> >
> > Was it a special requirement from maintainers?
>
> I’m actually a bit confused, because when I submitted the v6 patchset,
> checkpatch didn’t issue this warning.
> But now, it produces the following output:
>
> scripts/checkpatch.pl --strict i2c-ls2k0300-v6/v6-0002*
> CHECK: Avoid CamelCase: <parent_rate_MHz>

It's a false positive of the checkpatch.

> #512: FILE: drivers/i2c/busses/i2c-ls2x-v2.c:437:
> +       priv->parent_rate_MHz = clk_get_rate(priv->clk);
>
> total: 0 errors, 0 warnings, 1 checks, 574 lines checked
>
> NOTE: For some of the reported defects, checkpatch may be able to
>       mechanically convert to the typical style using --fix or --fix-inplace
>
> > Note, the physical units are special. The m and M have quite a
> > different multiplier value. So, even if asked by somebody I think it's
> > still arguably should be kept as MHz.

See above why.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply

* Re: [PATCH v3 3/5] phy: qcom: qmp-pcie: Support multiple nocsr resets
From: Philipp Zabel @ 2026-04-13  8:10 UTC (permalink / raw)
  To: Qiang Yu, Vinod Koul, Neil Armstrong, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <20260412-glymur_gen5x8_phy_0413-v3-3-affcebc16b8b@oss.qualcomm.com>

On So, 2026-04-12 at 23:25 -0700, Qiang Yu wrote:
> Refactor nocsr reset handling to support multiple nocsr resets required
> for PHY configurations with bifurcated operation modes.
> 
> The Glymur SoC's 3rd PCIe instance supports 8-lane mode using two PHYs
> in bifurcation, where each PHY requires its own nocsr reset to be
> controlled simultaneously. The current implementation only supports a
> single nocsr reset per PHY configuration.
> 
> Add num_nocsr and nocsr_list fields to struct qmp_phy_cfg to represent the
> number and names of a group of nocsr reset names. Initialize these fields
> for all PHYs that have nocsr resets, allowing the driver to correctly
> acquire multiple nocsr resets during probe and control them as an array
> by using reset_control_bulk APIs.
> 
> The refactoring maintains backward compatibility for existing single
> nocsr reset configurations while enabling support for multi-PHY
> scenarios like Glymur's 8-lane bifurcation mode.
> 
> Additionally, introduces x1e80100_qmp_gen3x2_pciephy_cfg as a separate
> configuration from sm8550_qmp_gen3x2_pciephy_cfg since the x1e80100 Gen3x2
> PHY requires nocsr reset support while the sm8550 Gen3x2 PHY does not.
> 
> Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
> ---
>  drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 87 ++++++++++++++++++++++++++++----
>  1 file changed, 77 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> index 424c935e27a8766e1e26762bd3d7df527c1520e3..51db9eea41255bad0034bbcfbfdc36894c2bc95f 100644
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
[...]
> @@ -4998,14 +5054,25 @@ static int qmp_pcie_reset_init(struct qmp_pcie *qmp)
>  	for (i = 0; i < cfg->num_resets; i++)
>  		qmp->resets[i].id = cfg->reset_list[i];
>  
> -	ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets);
> +	ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets,
> +						    qmp->resets);

Unrelated and unnecessary change.

>  	if (ret)
>  		return dev_err_probe(dev, ret, "failed to get resets\n");
>  
> -	qmp->nocsr_reset = devm_reset_control_get_optional_exclusive(dev, "phy_nocsr");
> -	if (IS_ERR(qmp->nocsr_reset))
> -		return dev_err_probe(dev, PTR_ERR(qmp->nocsr_reset),
> -							"failed to get no-csr reset\n");
> +	if (!cfg->num_nocsr_resets)
> +		return 0;
> +	qmp->nocsr_reset = devm_kcalloc(dev, cfg->num_nocsr_resets,
> +				   sizeof(*qmp->nocsr_reset), GFP_KERNEL);
> +	if (!qmp->nocsr_reset)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < cfg->num_nocsr_resets; i++)
> +		qmp->nocsr_reset[i].id = cfg->nocsr_reset_list[i];
> +
> +	ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_nocsr_resets,
> +						    qmp->nocsr_reset);

Should this be devm_reset_control_bulk_get_optional_exclusive()?

regards
Philipp

^ permalink raw reply

* Re: [PATCH 01/35] dt-bindings: qcom,pdc: Tighten reg to single APSS DRV region
From: Krzysztof Kozlowski @ 2026-04-13  8:10 UTC (permalink / raw)
  To: Mukesh Ojha
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, cros-qcom-dts-watchers,
	linux-arm-msm, linux-kernel, devicetree
In-Reply-To: <20260410184124.1068210-2-mukesh.ojha@oss.qualcomm.com>

On Sat, Apr 11, 2026 at 12:10:38AM +0530, Mukesh Ojha wrote:
> The PDC has multiple DRV regions, each sized 0x10000, where each region

Here and in subject - add "example". You are not tightening anything
relevant. This is just an example, it can contain whatever "reg" value.
It's nice that value is real and correct, but now your subject creates
impression you are actually fixing something relevant, but in fact it
has zero impact.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v7 0/2] i2c: Add Loongson-2K0300 I2C controller support
From: Binbin Zhou @ 2026-04-13  8:08 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andi Shyti, Wolfram Sang, Andy Shevchenko,
	linux-i2c, Huacai Chen, Xuerui Wang, loongarch, devicetree
In-Reply-To: <CAHp75Ve59GPAFvKM6yOkPmr=kmHBjVL-Vz78X0WDikiWd+2arQ@mail.gmail.com>

Hi Andy:

On Mon, Apr 13, 2026 at 3:52 PM Andy Shevchenko
<andy.shevchenko@gmail.com> wrote:
>
> On Sat, Apr 11, 2026 at 12:58 PM Binbin Zhou <zhoubinbin@loongson.cn> wrote:
>
> > 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.
>
> ...
>
> >  - parent_rate_MHz -> parent_rate_mhz to avoid CamelCase.
>
> Was it a special requirement from maintainers?

I’m actually a bit confused, because when I submitted the v6 patchset,
checkpatch didn’t issue this warning.
But now, it produces the following output:

scripts/checkpatch.pl --strict i2c-ls2k0300-v6/v6-0002*
CHECK: Avoid CamelCase: <parent_rate_MHz>
#512: FILE: drivers/i2c/busses/i2c-ls2x-v2.c:437:
+       priv->parent_rate_MHz = clk_get_rate(priv->clk);

total: 0 errors, 0 warnings, 1 checks, 574 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace

>
> Note, the physical units are special. The m and M have quite a
> different multiplier value. So, even if asked by somebody I think it's
> still arguably should be kept as MHz.
>
> --
> With Best Regards,
> Andy Shevchenko

-- 
Thanks.
Binbin

^ permalink raw reply

* Re: [PATCH v8 0/9] riscv: spacemit: enable SD card support with UHS modes for OrangePi RV2
From: Krzysztof Kozlowski @ 2026-04-13  8:07 UTC (permalink / raw)
  To: Iker Pedrosa, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Adrian Hunter, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, Yixun Lan
  Cc: Troy Mitchell, Michael Opdenacker, Javier Martinez Canillas,
	linux-mmc, devicetree, linux-riscv, spacemit, linux-kernel,
	Anand Moon, Trevor Gamblin, Vincent Legoll
In-Reply-To: <20260413-orangepi-sd-card-uhs-v8-0-c21c40ec16d0@gmail.com>

On 13/04/2026 10:02, Iker Pedrosa wrote:
> This series enables complete SD card support for the Spacemit K1-based
> OrangePi RV2 board, including UHS (Ultra High Speed) modes for
> high-performance SD card operation.
> 
> Background
> 
> The Spacemit K1 SoC includes an SDHCI controller capable of supporting
> SD cards up to UHS-I speeds (SDR104 at 208MHz). However, mainline
> currently lacks basic SD controller configuration, SDHCI driver
> enhancements for voltage switching and tuning, and power management
> infrastructure.
> 
> Implementation
> 
> The series enables SD card support through coordinated layers:
> 
> - Hardware infrastructure (patches 1-2): Device tree bindings for voltage
> switching hardware and essential clock infrastructure.
> - SDHCI driver enhancements (patches 3-7): Regulator framework
> integration, pinctrl state switching for voltage domains, AIB register
> programming, and comprehensive SDR tuning support for reliable UHS
> operation.
> - SoC and board integration (patches 8-10): Complete K1 SoC controller
> definitions, PMIC power infrastructure, and OrangePi RV2 board enablement
> with full UHS support.
> 
> This transforms the OrangePi RV2 from having no SD card support to full
> UHS-I capability, enabling high-performance storage up to 208MHz.
> 
> Tested-by: Michael Opdenacker <michael.opdenacker@rootcommit.com>
> Signed-off-by: Iker Pedrosa <ikerpedrosam@gmail.com>
> ---
> Changes in v8:
> - Resending the series as v8. The v7 submission failed due to an SMTP
>   error during transit, which resulted in a broken thread on the mailing 
>   list.

Hm? Everything is here:
https://lore.kernel.org/all/20260413-orangepi-sd-card-uhs-v7-1-16650f49c022@gmail.com/

You can send individual patches to fix up threading, use --in-reply-to.

> - No functional changes from v7.
> - Link to v7: https://lore.kernel.org/r/20260413-orangepi-sd-card-uhs-v7-1-16650f49c022@gmail.com
> 

You already sent it on 13th of April. And now v8 the same day. Wait a
few days to allow people to actually review your code.

It's BTW merge window, so big series should slow down.

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v8 9/9] riscv: dts: spacemit: k1-musepi-pro: add SD card support with UHS modes
From: Iker Pedrosa @ 2026-04-13  8:02 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Adrian Hunter, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Yixun Lan, Yixun Lan
  Cc: Troy Mitchell, Michael Opdenacker, Javier Martinez Canillas,
	linux-mmc, devicetree, linux-riscv, spacemit, linux-kernel,
	Iker Pedrosa, Trevor Gamblin
In-Reply-To: <20260413-orangepi-sd-card-uhs-v8-0-c21c40ec16d0@gmail.com>

From: Trevor Gamblin <tgamblin@baylibre.com>

Update the Muse Pi Pro devicetree with SD card support to match what
was done for the OrangePi RV2 in [1]. More precisely:

- Enable sdhci0 controller with 4-bit bus width
- Configure card detect GPIO with inversion
- Connect vmmc-supply to buck4 for 3.3V card power
- Connect vqmmc-supply to aldo1 for 1.8V/3.3V I/O switching
- Add dual pinctrl states for voltage-dependent pin configuration
- Support UHS-I SDR25, SDR50, and SDR104 modes

[1] https://lore.kernel.org/linux-riscv/20260316-orangepi-sd-card-uhs-v3-0-aefd3b7832df@gmail.com/T/#

Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
Signed-off-by: Iker Pedrosa <ikerpedrosam@gmail.com>
---
 arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts | 66 ++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts
index 29e333b670cf..774a4640f065 100644
--- a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts
+++ b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts
@@ -18,6 +18,24 @@ aliases {
 		ethernet0 = &eth0;
 		serial0 = &uart0;
 	};
+	reg_dc_in: dc-in-5v {
+		compatible = "regulator-fixed";
+		regulator-name = "dc_in_5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
+
+	reg_vcc_4v: vcc-4v {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_4v";
+		regulator-min-microvolt = <4000000>;
+		regulator-max-microvolt = <4000000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
+
 
 	chosen {
 		stdout-path = "serial0";
@@ -77,3 +95,51 @@ &uart0 {
 	pinctrl-names = "default";
 	status = "okay";
 };
+
+&i2c8 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c8_cfg>;
+	status = "okay";
+
+	pmic@41 {
+		compatible = "spacemit,p1";
+		reg = <0x41>;
+		interrupts = <64>;
+		vin-supply = <&reg_vcc_4v>;
+
+		regulators {
+			buck4: buck4 {
+				regulator-min-microvolt = <500000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-ramp-delay = <5000>;
+				regulator-always-on;
+			};
+
+			aldo1: aldo1 {
+				regulator-min-microvolt = <500000>;
+				regulator-max-microvolt = <3400000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+		};
+	};
+};
+
+&sdhci0 {
+	pinctrl-names = "default", "uhs";
+	pinctrl-0 = <&mmc1_cfg>;
+	pinctrl-1 = <&mmc1_uhs_cfg>;
+	bus-width = <4>;
+	cd-gpios = <&gpio K1_GPIO(80) GPIO_ACTIVE_HIGH>;
+	cd-inverted;
+	no-mmc;
+	no-sdio;
+	disable-wp;
+	cap-sd-highspeed;
+	vmmc-supply = <&buck4>;
+	vqmmc-supply = <&aldo1>;
+	sd-uhs-sdr25;
+	sd-uhs-sdr50;
+	sd-uhs-sdr104;
+	status = "okay";
+};

-- 
2.53.0


^ permalink raw reply related

* [PATCH v8 8/9] riscv: dts: spacemit: k1-bananapi-f3: add SD card support with UHS modes
From: Iker Pedrosa @ 2026-04-13  8:02 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Adrian Hunter, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Yixun Lan, Yixun Lan
  Cc: Troy Mitchell, Michael Opdenacker, Javier Martinez Canillas,
	linux-mmc, devicetree, linux-riscv, spacemit, linux-kernel,
	Iker Pedrosa, Anand Moon
In-Reply-To: <20260413-orangepi-sd-card-uhs-v8-0-c21c40ec16d0@gmail.com>

Add complete SD card controller support with UHS high-speed modes.

- Enable sdhci0 controller with 4-bit bus width
- Configure card detect GPIO with inversion
- Connect vmmc-supply to buck4 for 3.3V card power
- Connect vqmmc-supply to aldo1 for 1.8V/3.3V I/O switching
- Add dual pinctrl states for voltage-dependent pin configuration
- Support UHS-I SDR25, SDR50, and SDR104 modes

This enables full SD card functionality including high-speed UHS modes
for improved performance.

Suggested-by: Anand Moon <linux.amoon@gmail.com>
Tested-by: Anand Moon <linux.amoon@gmail.com>
Signed-off-by: Iker Pedrosa <ikerpedrosam@gmail.com>
---
 arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts
index 5790d927b93d..a7d88564630f 100644
--- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts
+++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts
@@ -220,7 +220,7 @@ buck3_1v8: buck3 {
 				regulator-always-on;
 			};
 
-			buck4 {
+			buck4: buck4 {
 				regulator-min-microvolt = <500000>;
 				regulator-max-microvolt = <3300000>;
 				regulator-ramp-delay = <5000>;
@@ -241,7 +241,7 @@ buck6 {
 				regulator-always-on;
 			};
 
-			aldo1 {
+			aldo1: aldo1 {
 				regulator-min-microvolt = <500000>;
 				regulator-max-microvolt = <3400000>;
 				regulator-boot-on;
@@ -367,3 +367,23 @@ hub_3_0: hub@2 {
 		reset-gpios = <&gpio K1_GPIO(124) GPIO_ACTIVE_LOW>;
 	};
 };
+
+&sdhci0 {
+	pinctrl-names = "default", "uhs";
+	pinctrl-0 = <&mmc1_cfg>;
+	pinctrl-1 = <&mmc1_uhs_cfg>;
+	bus-width = <4>;
+	cd-gpios = <&gpio K1_GPIO(80) GPIO_ACTIVE_HIGH>;
+	cd-inverted;
+	broken-cd;
+	no-mmc;
+	no-sdio;
+	disable-wp;
+	cap-sd-highspeed;
+	vmmc-supply = <&buck4>;
+	vqmmc-supply = <&aldo1>;
+	sd-uhs-sdr25;
+	sd-uhs-sdr50;
+	sd-uhs-sdr104;
+	status = "okay";
+};

-- 
2.53.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox