Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH 3/7] remoteproc: qcom_q6v5_mss: Use mss as regulator for MSM8953
From: Konrad Dybcio @ 2026-04-16 13:32 UTC (permalink / raw)
  To: Barnabás Czémán, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Ulf Hansson, Mathieu Poirier,
	Konrad Dybcio, Stephan Gerhold
  Cc: linux-arm-msm, devicetree, linux-kernel, linux-pm,
	linux-remoteproc
In-Reply-To: <20260327-sdm632-rpmpd-v1-3-6098dc997d66@mainlining.org>

On 3/27/26 9:11 PM, Barnabás Czémán wrote:
> MSM8953 MSS is using mss-supply as regulator what is usually pm8953_s1.
> 
> Signed-off-by: Barnabás Czémán <barnabas.czeman@mainlining.org>
> ---
>  drivers/remoteproc/qcom_q6v5_mss.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c
> index 4e9eb5bd11fa..86edd826ede8 100644
> --- a/drivers/remoteproc/qcom_q6v5_mss.c
> +++ b/drivers/remoteproc/qcom_q6v5_mss.c
> @@ -2705,6 +2705,14 @@ static const struct rproc_hexagon_res msm8953_mss = {
>  		},
>  		{}
>  	},
> +	.active_supply = (struct qcom_mss_reg_res[]) {
> +		{
> +			.supply = "mss",
> +			.uV = 1050000,
> +			.uA = 100000,

I don't know if it's a typo, but msm8953-regulator.dtsi on msm-3.18
suggests one more zero

Konrad

^ permalink raw reply

* Re: [PATCH 6/7] remoteproc: qcom_q6v5_mss: Add SDM632 MSS
From: Konrad Dybcio @ 2026-04-16 13:32 UTC (permalink / raw)
  To: Barnabás Czémán, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Ulf Hansson, Mathieu Poirier,
	Konrad Dybcio, Stephan Gerhold
  Cc: linux-arm-msm, devicetree, linux-kernel, linux-pm,
	linux-remoteproc
In-Reply-To: <20260327-sdm632-rpmpd-v1-6-6098dc997d66@mainlining.org>

On 3/27/26 9:11 PM, Barnabás Czémán wrote:
> Add support for SDM632 mss, it is very similar to MSM8953 mss only
> difference SDM632 is using mss-supply as pm domain.
> 
> Signed-off-by: Barnabás Czémán <barnabas.czeman@mainlining.org>
> ---

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

Konrad

^ permalink raw reply

* Re: [PATCH 4/7] arm64: dts: qcom: msm8953: fix modem pm domains
From: Konrad Dybcio @ 2026-04-16 13:33 UTC (permalink / raw)
  To: Dmitry Baryshkov, Barnabás Czémán
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Ulf Hansson, Mathieu Poirier, Konrad Dybcio, Stephan Gerhold,
	linux-arm-msm, devicetree, linux-kernel, linux-pm,
	linux-remoteproc
In-Reply-To: <i6lymgal5c62ud3aas3qvzv6fjnjrzuncguwechraahmflr45l@5t5qli3k7npu>

On 3/27/26 9:35 PM, Dmitry Baryshkov wrote:
> On Fri, Mar 27, 2026 at 09:11:46PM +0100, Barnabás Czémán wrote:
>> MSM8953 MSS is using mss-supply as regulator.
> 
> "On MSM8953 MSS regulators is controlled using the voltages rather than
> performance levels. Correct DT definition and model the MSS as a
> regulator rather than a power domain".
> 
> Also please squash with the changes actually making use of the
> regulator.


+1

Konrad

^ permalink raw reply

* [PATCH] dt-bindings: iio: dac: mcp47feb02: Fix binding issues
From: Ariana Lazar @ 2026-04-16 13:33 UTC (permalink / raw)
  To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Jonathan Cameron, Conor Dooley, linux-iio, devicetree,
	linux-kernel, Conor Dooley, Ariana Lazar

Change maxItems value from 8 to 1 for the channel number reg property.
Change example reg value from 0 to 0x60.
Fix a few typos in property descriptions.
Sort the part numbers in the enum list lexicographically.

Fixes: 4ba12d304175 ("dt-bindings: iio: dac: adding support for Microchip MCP47FEB02")
Reported-by: Conor Dooley <conor@kernel.org>
Closes: https://lore.kernel.org/all/20260403-speed-childless-1360de358229@spud/
Reported-by: David Lechner <dlechner@baylibre.com>
Closes: https://lore.kernel.org/all/dd0dbadb-604b-4f12-8674-268b7db096fd@baylibre.com/
Signed-off-by: Ariana Lazar <ariana.lazar@microchip.com>
---
 .../bindings/iio/dac/microchip,mcp47feb02.yaml     | 57 +++++++++++-----------
 1 file changed, 28 insertions(+), 29 deletions(-)

diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
index d2466aa6bda2106a8b695347a0edf38462294d03..88a1495f2967a3d821ada7e7e9a7fbb466401040 100644
--- a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
+++ b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
@@ -61,29 +61,29 @@ properties:
   compatible:
     enum:
       - microchip,mcp47feb01
-      - microchip,mcp47feb11
-      - microchip,mcp47feb21
       - microchip,mcp47feb02
+      - microchip,mcp47feb04
+      - microchip,mcp47feb08
+      - microchip,mcp47feb11
       - microchip,mcp47feb12
+      - microchip,mcp47feb14
+      - microchip,mcp47feb18
+      - microchip,mcp47feb21
       - microchip,mcp47feb22
+      - microchip,mcp47feb24
+      - microchip,mcp47feb28
       - microchip,mcp47fvb01
-      - microchip,mcp47fvb11
-      - microchip,mcp47fvb21
       - microchip,mcp47fvb02
-      - microchip,mcp47fvb12
-      - microchip,mcp47fvb22
       - microchip,mcp47fvb04
-      - microchip,mcp47fvb14
-      - microchip,mcp47fvb24
       - microchip,mcp47fvb08
+      - microchip,mcp47fvb11
+      - microchip,mcp47fvb12
+      - microchip,mcp47fvb14
       - microchip,mcp47fvb18
+      - microchip,mcp47fvb21
+      - microchip,mcp47fvb22
+      - microchip,mcp47fvb24
       - microchip,mcp47fvb28
-      - microchip,mcp47feb04
-      - microchip,mcp47feb14
-      - microchip,mcp47feb24
-      - microchip,mcp47feb08
-      - microchip,mcp47feb18
-      - microchip,mcp47feb28
 
   reg:
     maxItems: 1
@@ -111,13 +111,13 @@ properties:
         - for single-channel device: Vout0;
         - for dual-channel device: Vout0, Vout1;
         - for quad-channel device: Vout0, Vout2;
-        - for octal-channel device: Vout0, Vout2, Vout6, Vout8;
+        - for octal-channel device: Vout0, Vout2, Vout4, Vout6;
 
   vref1-supply:
     description: |
       Vref1 pin may be used as a voltage reference when this supply is specified.
       The internal reference will be taken into account for voltage reference
-      beside VDD if this supply does not exist.
+      besides VDD if this supply does not exist.
 
       This supply will be voltage reference for the following outputs:
         - for quad-channel device: Vout1, Vout3;
@@ -141,7 +141,7 @@ properties:
     description:
       Enable buffering of the external Vref/Vref0 pin in cases where the
       external reference voltage does not have sufficient current capability in
-      order not to drop it’s voltage when connected to the internal resistor
+      order not to drop its voltage when connected to the internal resistor
       ladder circuit.
 
   microchip,vref1-buffered:
@@ -149,7 +149,7 @@ properties:
     description:
       Enable buffering of the external Vref1 pin in cases where the external
       reference voltage does not have sufficient current capability in order not
-      to drop it’s voltage when connected to the internal resistor ladder
+      to drop its voltage when connected to the internal resistor ladder
       circuit.
 
 patternProperties:
@@ -161,8 +161,7 @@ patternProperties:
     properties:
       reg:
         description: The channel number.
-        minItems: 1
-        maxItems: 8
+        maxItems: 1
 
       label:
         description: Unique name to identify which channel this is.
@@ -227,12 +226,12 @@ allOf:
         compatible:
           contains:
             enum:
-              - microchip,mcp47fvb04
-              - microchip,mcp47fvb14
-              - microchip,mcp47fvb24
               - microchip,mcp47feb04
               - microchip,mcp47feb14
               - microchip,mcp47feb24
+              - microchip,mcp47fvb04
+              - microchip,mcp47fvb14
+              - microchip,mcp47fvb24
     then:
       patternProperties:
         "^channel@[0-3]$":
@@ -245,12 +244,12 @@ allOf:
         compatible:
           contains:
             enum:
-              - microchip,mcp47fvb08
-              - microchip,mcp47fvb18
-              - microchip,mcp47fvb28
               - microchip,mcp47feb08
               - microchip,mcp47feb18
               - microchip,mcp47feb28
+              - microchip,mcp47fvb08
+              - microchip,mcp47fvb18
+              - microchip,mcp47fvb28
     then:
       patternProperties:
         "^channel@[0-7]$":
@@ -280,9 +279,9 @@ examples:
 
         #address-cells = <1>;
         #size-cells = <0>;
-        dac@0 {
+        dac@60 {
           compatible = "microchip,mcp47feb02";
-          reg = <0>;
+          reg = <0x60>;
           vdd-supply = <&vdac_vdd>;
           vref-supply = <&vref_reg>;
 
@@ -297,6 +296,6 @@ examples:
             reg = <0x1>;
             label = "Adjustable_voltage_ch1";
           };
-      };
+        };
     };
 ...

---
base-commit: d2a4ec19d2a2e54c23b5180e939994d3da4a6b91
change-id: 20260416-mcp47feb02-fix5-26994c5b428c

Best regards,
-- 
Ariana Lazar <ariana.lazar@microchip.com>


^ permalink raw reply related

* Re: [PATCH v7 0/8] Add support for handling PCIe M.2 Key E connectors in devicetree
From: Herve Codina @ 2026-04-16 13:34 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Chen-Yu Tsai, Andy Shevchenko, Manivannan Sadhasivam, Rob Herring,
	Greg Kroah-Hartman, Jiri Slaby, Nathan Chancellor, Nicolas Schier,
	Hans de Goede, Ilpo Järvinen, Mark Pearson, Derek J. Clark,
	Krzysztof Kozlowski, Conor Dooley, Marcel Holtmann,
	Luiz Augusto von Dentz, Bartosz Golaszewski, Bartosz Golaszewski,
	linux-serial, linux-kernel, linux-kbuild, platform-driver-x86,
	linux-pci, devicetree, linux-arm-msm, linux-bluetooth, linux-pm,
	Stephan Gerhold, Dmitry Baryshkov, linux-acpi, Hans de Goede,
	Bartosz Golaszewski, Luca Ceresoli
In-Reply-To: <4yockfx5rjcvfh2n2excrgsknnhi72rv2w7wf7onks2ryt33sm@w7zkcxuc6vem>

Hi Manivannan,

On Thu, 16 Apr 2026 14:25:39 +0530
Manivannan Sadhasivam <mani@kernel.org> wrote:

> On Wed, Apr 15, 2026 at 04:56:51PM +0200, Herve Codina wrote:
> > Hi Chen, all,
> > 
> > ...
> >    
> > > 
> > > I'm not arguing for a even more generic "M.2" connector. The "key" is
> > > already described in the compatible. I'm saying we should have some way
> > > of describing the individual interfaces (PCIe, SDIO, USB, UART, I2S, I2C)
> > > on the connector so further nodes or properties can be attached to them,
> > > either with overlays or dynamically within the kernel. Right now the
> > > are only described as individual ports, but we can't actually tie a
> > > device to a OF graph port.
> > > 
> > > But maybe I'm overthinking the representation part. AFAICT for Qualcomm's
> > > UART-based BT bit part, Mani just had the driver create a device node
> > > under the UART (by traversing the OF graph to find the UART). If that's
> > > the desired way then the connector binding should mention it. And that
> > > works for me. But I think it's messier and also we're missing an
> > > opportunity to make the M.2 connector a standardized attachment point
> > > for overlays.
> > > 
> > > Mani, could you also chime in a bit on what you envisioned?
> > > 
> > > (Added Luca from Bootlin to CC, as I think there are parallels to the
> > >  "Hotplug of Non-discoverable Hardware" work)
> > >  
> > 
> > Related to "Hotplug of Non-discoverable Hardware",
> > 
> > I would add entries for busses in the connector without using an OF graph.
> >   
> 
> I don't think this is a correct representation. It is non-standard to describe
> the device nodes in some other connectors. While it may work with your series in
> the future, not something I would bet-on at this point.
> 
> Using OF graph to link the connector nodes look like the cleaner solution to me.

In your DT binding, there is the i2c-parent property at connector level.

How it is used or planned to be used in order to handle devices available on the
board connected to the M2 connector ?

> 
> > For I2C and later SPI, this was is done.
> > 
> > You already have an i2c-parent property but no node where an i2c device
> > can be added.
> > 
> > The last discussion related to hotplug, connectors and DT led to the RFC
> > series [1].
> > 
> > It is a huge series. The last patch give a real example of representation:
> >   https://lore.kernel.org/all/20260112142009.1006236-78-herve.codina@bootlin.com/
> > 
> > In your case I would see some thing like:
> > 
> >     connector {
> >         compatible = "pcie-m2-e-connector";
> >         vpcie3v3-supply = <&vreg_wcn_3p3>;
> >         vpcie1v8-supply = <&vreg_l15b_1p8>;
> > 
> > 	/*
> > 	 * If those GPIOs have to be used by components available in
> > 	 * the connected board, a Nexus node should be used.
> >          */
> >         w-disable1-gpios = <&tlmm 115 GPIO_ACTIVE_LOW>;
> >         w-disable2-gpios = <&tlmm 116 GPIO_ACTIVE_LOW>;
> >         viocfg-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>;
> >         uart-wake-gpios = <&tlmm 118 GPIO_ACTIVE_LOW>;
> >         sdio-wake-gpios = <&tlmm 119 GPIO_ACTIVE_LOW>;
> >         sdio-reset-gpios = <&tlmm 120 GPIO_ACTIVE_LOW>;
> > 
> > 	conn-i2c {
> > 		i2c-parent = <&i2c0>;
> > 
> > 		/*
> >  		 * Here i2c devices available on the board
> > 		 * connected to the connector can be described.
> > 		 */
> > 	};
> > 
> > 	/* Same kind to description for other busses */
> > 	conn-pcie {
> > 		pci-parent = <&xxxxx>;
> > 
> > 		/*
> > 		 * The PCIe bus has abilities to discover devices.
> > 		 * Not sure this node is needed.
> > 		 *
> > 		 * If a PCI device need a DT description to describe
> > 		 * stuffs behind the device, what has been done for LAN966x
> > 		 * could be re-used [2] and [3]
> > 		 */  
> 
> I don't think anyone would connect something like LAN966x to the M.2 connector.
> M.2 cards have a defined purpose, like NVMe, WLAN etc... If anyone wants to
> connect another SoC like LAN966x, they would use non-M.2 connectors.

Hum, maybe yes, maybe not. I don't know what kind of hardware could be available
on a M2 connector.

One think sure is that a PCIe bus is available on the connector and so, potentially
all PCI devices could be wired on a M2 form factor.

LAN966x in PCI version is just a PCI device.

Anyway, Is it allowed to have sub-nodes in a port node ?

I mean, can we describe devices connected to a bus described by the port node ?

For USB and PCI it is probably not needed for common use cases but having a DT
description for devices on PCI and/or USB exists and is supported in the current
kernel

On SDIO, wireless devices can be described:
  Documentation/devicetree/bindings/net/wireless/

and so, sub-nodes will be needed.

As those devices need an address (reg property), #address-cells and
#size-cells will be needed.

This will end in a mix of sub-nodes describing devices and a sub-node
describing the endpoint connection.

#address-cells and #size-cells will conflict at some points.

Is a reg value for endpoints has the same characteristics as a reg value
for a SDIO device ?

How a BT devices is described under the UART available in the M2
connector ?

Best regards,
Hervé

^ permalink raw reply

* Re: [PATCH 7/7] arm64: dts: qcom: sdm632: Correct power domains
From: Konrad Dybcio @ 2026-04-16 13:34 UTC (permalink / raw)
  To: Barnabás Czémán, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Ulf Hansson, Mathieu Poirier,
	Konrad Dybcio, Stephan Gerhold
  Cc: linux-arm-msm, devicetree, linux-kernel, linux-pm,
	linux-remoteproc
In-Reply-To: <20260327-sdm632-rpmpd-v1-7-6098dc997d66@mainlining.org>

On 3/27/26 9:11 PM, Barnabás Czémán wrote:
> SDM632 is using different pm domains from MSM8953 override them
> where it is needed.
> 
> Signed-off-by: Barnabás Czémán <barnabas.czeman@mainlining.org>
> ---
>  arch/arm64/boot/dts/qcom/sdm632.dtsi | 31 +++++++++++++++++++++++++++++++
>  1 file changed, 31 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/sdm632.dtsi b/arch/arm64/boot/dts/qcom/sdm632.dtsi
> index 40d86d91b67f..b1dbcffd51b6 100644
> --- a/arch/arm64/boot/dts/qcom/sdm632.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sdm632.dtsi
> @@ -87,3 +87,34 @@ opp-725000000 {
>  		required-opps = <&rpmpd_opp_turbo>;
>  	};
>  };
> +
> +&lpass {
> +	power-domains = <&rpmpd SDM632_VDDCX>;
> +	power-domain-names = "cx";
> +};
> +
> +&mpss {
> +	compatible = "qcom,sdm632-mss-pil";
> +	power-domains = <&rpmpd SDM632_VDDCX>,
> +			<&rpmpd SDM632_VDDMX>,
> +			<&rpmpd SDM632_VDDMD>;
> +	power-domain-names = "cx", "mx", "mss";

nit: one a line would be perfect

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

Konrad

^ permalink raw reply

* Re: [PATCH 2/2] arm64: dts: qcom: glymur: Add crypto engine
From: Konrad Dybcio @ 2026-04-16 13:40 UTC (permalink / raw)
  To: Harshal Dev, Thara Gopinath, Herbert Xu, David S. Miller,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Dmitry Baryshkov
  Cc: Neeraj Soni, Kuldeep Singh, Abel Vesa, linux-arm-msm,
	linux-crypto, devicetree, linux-kernel
In-Reply-To: <20260416-glymur_crypto_enablement-v1-2-75e768c1417c@oss.qualcomm.com>

On 4/16/26 3:07 PM, Harshal Dev wrote:
> On Glymur, there is a crypto engine IP block similar to the ones found on
> SM8x50 platforms.
> 
> Describe the crypto engine and its BAM.
> 
> Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
> ---
>  arch/arm64/boot/dts/qcom/glymur.dtsi | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi
> index f23cf81ddb77..e8c796f2c572 100644
> --- a/arch/arm64/boot/dts/qcom/glymur.dtsi
> +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi
> @@ -3675,6 +3675,32 @@ pcie3b_phy: phy@f10000 {
>  			status = "disabled";
>  		};
>  
> +		cryptobam: dma-controller@1dc4000 {
> +			compatible = "qcom,bam-v1.7.4", "qcom,bam-v1.7.0";
> +			reg = <0x0 0x01dc4000 0x0 0x28000>;
> +			interrupts = <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>;
> +			#dma-cells = <1>;
> +			iommus = <&apps_smmu 0x480 0x0>,
> +				 <&apps_smmu 0x481 0x0>;

It seems like these aren't the right SIDs on this platform.. Have you
tested this patch on hw?

Konrad

^ permalink raw reply

* [PATCH v6 0/4] Introduce Allwinner H616 PWM controller
From: Richard Genoud @ 2026-04-16 13:40 UTC (permalink / raw)
  To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Philipp Zabel
  Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
	bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, Richard Genoud

Allwinner H616 PWM controller is quite different from the A10 one.

It can drive 6 PWM channels, and like for the A10, each channel has a
bypass that permits to output a clock, bypassing the PWM logic, when
enabled.

But, the channels are paired 2 by 2, sharing a first set of
MUX/prescaler/gate.
Then, for each channel, there's another prescaler (that will be bypassed
if the bypass is enabled for this channel).

It looks like that:
            _____      ______      ________
OSC24M --->|     |    |      |    |        |
APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> PWM_clock_src_xy
           |_____|    |______|    |________|
                          ________
                         |        |
                      +->| /div_k |---> PWM_clock_x
                      |  |________|
                      |    ______
                      |   |      |
                      +-->| Gate |----> PWM_bypass_clock_x
                      |   |______|
PWM_clock_src_xy -----+   ________
                      |  |        |
                      +->| /div_k |---> PWM_clock_y
                      |  |________|
                      |    ______
                      |   |      |
                      +-->| Gate |----> PWM_bypass_clock_y
                          |______|

Where xy can be 0/1, 2/3, 4/5

PWM_clock_x/y serve for the PWM purpose.
PWM_bypass_clock_x/y serve for the clock-provider purpose.
The common clock framework has been used to manage those clocks.

This PWM driver serves as a clock-provider for PWM_bypass_clocks.
This is needed for example by the embedded AC300 PHY which clock comes
from PMW5 pin (PB12).

Usually, to get a clock from a PWM driver, we use the pwm-clock driver
so that the PWM driver doesn't need to be a clk-provider itself.
While this works in most cases, here it just doesn't.
That's because the pwm-clock request a period from the PWM driver,
without any clue that it actually wants a clock at a specific frequency,
and not a PWM signal with duty cycle capability.
So, the PWM driver doesn't know if it can use the bypass or not, it
doesn't even have the real accurate frequency information (23809524 Hz
instead of 24MHz) because PWM drivers only deal with periods.

With pwm-clock, we loose a precious information along the way (that we
actually want a clock and not a PWM signal).
That's ok with simple PWM drivers that don't have multiple input clocks,
but in this case, without this information, we can't know for sure which
clock to use.
And here, for instance, if we ask for a 24MHz clock, pwm-clock will
requests 42ns (assigned-clocks doesn't help for that matter). The logic
is to select the highest clock (100MHz) with no prescaler and a duty
cycle value of 2/4 => we have 25MHz instead of 24MHz.
And that's a perfectly fine choice for a PMW, because we still can
change the duty cycle in the range [0-4]/4.
But obviously for a clock, we don't care about the duty cycle, but more
about the clock accuracy.

And actually, this PWM is really a PWM AND a real clock when the bypass
is set.

This series is based onto v7.0

NB: checkpatch is not happy with patch 2, but it's a false positive.
It doesn't detect that PWM_XY_SRC_MUX/GATE/DIV are structures, but as
it's more readable like that, I prefer keeping it that way.

Changes since v5:
- remove trailing junk after commit message in patch 4
- remove Tested-by when it doesn't make sense.
(sorry for the noise)

Changes since v4:
- Fix a bug on bypass for channels greater than 1
- add colons to clarify 2 debug messages
- switch from H616 to sun8i prefix (in code, filename, module name)
- fix consistency issues in macro parameters
- rename some macros with a confusing naming
- rebase on v7.0

Changes since v3:
- gather Acked-by/Tested-by
- fix cast from pointer to integer of different size (kernel test robot
  with arc platform)
- add devm_action for clk_hw_unregister_composite as suggested by Philipp
- remove now unused pwm_remove as suggested by Philipp

Changes since v2:
- use U32_MAX instead of defining UINT32_MAX
- add a comment on U32_MAX usage in clk_round_rate()
- change clk_table_div_m (use macros)
- fix formatting (double space, superfluous comma, extra line feed)
- fix the parent clock order
- simplify code by using scoped_guard()
- add missing const in to_h616_pwm_chip() and rename to
h616_pwm_from_chip()
- add/remove missing/superfluous error messages
- rename cnt->period_ticks, duty_cnt->duty_ticks
- fix PWM_PERIOD_MAX
- add .remove() callback
- fix DIV_ROUND_CLOSEST_ULL->DIV_ROUND_UP_ULL
- add H616_ prefix
- protect _reg in macros
- switch to waveforms instead of apply/get_state
- shrink struct h616_pwm_channel
- rebase on v6.19-rc4

Changes since v1:
- rebase onto v6.19-rc1
- add missing headers
- remove MODULE_ALIAS (suggested by Krzysztof)
- use sun4i-pwm binding instead of creating a new one (suggested by Krzysztof)
- retrieve the parent clocks from the devicetree
- switch num_parents to unsigned int

Richard Genoud (4):
  dt-bindings: pwm: allwinner: add h616 pwm compatible
  pwm: sun8i: Add H616 PWM support
  arm64: dts: allwinner: h616: add PWM controller
  MAINTAINERS: Add entry on Allwinner sun8i/H616 PWM driver

 .../bindings/pwm/allwinner,sun4i-a10-pwm.yaml |  19 +-
 MAINTAINERS                                   |   5 +
 .../arm64/boot/dts/allwinner/sun50i-h616.dtsi |  47 +
 drivers/pwm/Kconfig                           |  12 +
 drivers/pwm/Makefile                          |   1 +
 drivers/pwm/pwm-sun8i.c                       | 938 ++++++++++++++++++
 6 files changed, 1021 insertions(+), 1 deletion(-)
 create mode 100644 drivers/pwm/pwm-sun8i.c


base-commit: 028ef9c96e96197026887c0f092424679298aae8

^ permalink raw reply

* [PATCH v6 1/4] dt-bindings: pwm: allwinner: add h616 pwm compatible
From: Richard Genoud @ 2026-04-16 13:40 UTC (permalink / raw)
  To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Philipp Zabel
  Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
	bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, Richard Genoud, Conor Dooley
In-Reply-To: <20260416134037.3160537-1-richard.genoud@bootlin.com>

Allwinner H616 PWM block is quite different from the A10 or H6, but at
the end, it uses the same clocks as the H6; so the sun4i pwm binding can
be used.
It has 6 channels than can generate PWM waveforms.
If the bypass is enabled (one bypass per channel) the output is no more
a PWM waveform, but a clock that can (and is) used as input for other
devices, like the AC300 PHY.

Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
---
 .../bindings/pwm/allwinner,sun4i-a10-pwm.yaml | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml
index 1197858e431f..4f58110ec98f 100644
--- a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml
+++ b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml
@@ -14,6 +14,9 @@ properties:
   "#pwm-cells":
     const: 3
 
+  "#clock-cells":
+    const: 1
+
   compatible:
     oneOf:
       - const: allwinner,sun4i-a10-pwm
@@ -36,6 +39,7 @@ properties:
           - const: allwinner,sun50i-h5-pwm
           - const: allwinner,sun5i-a13-pwm
       - const: allwinner,sun50i-h6-pwm
+      - const: allwinner,sun50i-h616-pwm
 
   reg:
     maxItems: 1
@@ -62,7 +66,9 @@ allOf:
       properties:
         compatible:
           contains:
-            const: allwinner,sun50i-h6-pwm
+            enum:
+              - allwinner,sun50i-h6-pwm
+              - allwinner,sun50i-h616-pwm
 
     then:
       properties:
@@ -83,6 +89,17 @@ allOf:
         clocks:
           maxItems: 1
 
+  - if:
+      not:
+        properties:
+          compatible:
+            contains:
+              const: allwinner,sun50i-h616-pwm
+
+    then:
+      properties:
+        "#clock-cells": false
+
 required:
   - compatible
   - reg

^ permalink raw reply related

* [PATCH v6 2/4] pwm: sun8i: Add H616 PWM support
From: Richard Genoud @ 2026-04-16 13:40 UTC (permalink / raw)
  To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Philipp Zabel
  Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
	bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, Richard Genoud
In-Reply-To: <20260416134037.3160537-1-richard.genoud@bootlin.com>

Add driver for Allwinner H616 PWM controller, supporting up to 6
channels.
Those channels output can be either a PWM signal output or a clock
output, thanks to the bypass.

The channels are paired (0/1, 2/3 and 4/5) and each pair has a
prescaler/mux/gate.
Moreover, each channel has its own prescaler and bypass.

The clock provider part of this driver is needed not only because the
H616 PWM controller provides also clocks when bypass is enabled, but
really because pwm-clock isn't fit to handle all cases here.
pwm-clock would work if the 100MHz clock is requested, but if a lower
clock is requested (like 24MHz), it will request a 42ns period to the
PWM driver which will happily serve, with the 100MHz clock as input a
25MHz frequency and a duty cycle adjustable in the range [0-4]/4,
because that is a sane thing to do for a PWM.
The information missing is that a real clock is resquested, not a PWM.

Tested-by: John Stultz <jstultz@google.com>
Tested-by: Joao Schim <joao@schimsalabim.eu>
Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
---
 drivers/pwm/Kconfig     |  12 +
 drivers/pwm/Makefile    |   1 +
 drivers/pwm/pwm-sun8i.c | 938 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 951 insertions(+)
 create mode 100644 drivers/pwm/pwm-sun8i.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 6f3147518376..c4fd682860d6 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -736,6 +736,18 @@ config PWM_SUN4I
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-sun4i.
 
+config PWM_SUN8I
+	tristate "Allwinner sun8i/sun50i PWM support"
+	depends on ARCH_SUNXI || COMPILE_TEST
+	depends on HAS_IOMEM && COMMON_CLK
+	help
+	  Generic PWM framework driver for Allwinner H616 SoCs.
+	  It supports generic PWM, but can also provides a plain clock.
+	  The AC300 PHY integrated in H616 SoC needs such a clock.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-sun8i.
+
 config PWM_SUNPLUS
 	tristate "Sunplus PWM support"
 	depends on ARCH_SUNPLUS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 0dc0d2b69025..ba2e0ec7fc17 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_PWM_STM32)		+= pwm-stm32.o
 obj-$(CONFIG_PWM_STM32_LP)	+= pwm-stm32-lp.o
 obj-$(CONFIG_PWM_STMPE)		+= pwm-stmpe.o
 obj-$(CONFIG_PWM_SUN4I)		+= pwm-sun4i.o
+obj-$(CONFIG_PWM_SUN8I)		+= pwm-sun8i.o
 obj-$(CONFIG_PWM_SUNPLUS)	+= pwm-sunplus.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
 obj-$(CONFIG_PWM_TH1520)	+= pwm_th1520.o
diff --git a/drivers/pwm/pwm-sun8i.c b/drivers/pwm/pwm-sun8i.c
new file mode 100644
index 000000000000..8f1023e3a2e5
--- /dev/null
+++ b/drivers/pwm/pwm-sun8i.c
@@ -0,0 +1,938 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Allwinner sun8i Pulse Width Modulation Controller
+ *
+ * (C) Copyright 2025 Richard Genoud, Bootlin <richard.genoud@bootlin.com>
+ *
+ * Based on drivers/pwm/pwm-sun4i.c with Copyright:
+ *
+ * Copyright (C) 2014 Alexandre Belloni <alexandre.belloni@bootlin.com>
+ *
+ * Limitations:
+ * - As the channels are paired (0/1, 2/3, 4/5), they share the same clock
+ *   source and prescaler(div_m), but they also have their own prescaler(div_k)
+ *   and bypass.
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/limits.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+
+/* PWM IRQ Enable Register */
+#define SUN8I_PWM_PIER			0x0
+
+/* PWM IRQ Status Register */
+#define SUN8I_PWM_PISR			0x4
+
+/* PWM Capture IRQ Enable Register */
+#define SUN8I_PWM_CIER			0x10
+
+/* PWM Capture IRQ Status Register */
+#define SUN8I_PWM_CISR			0x14
+
+/* PWMCC Pairs Clock Configuration Registers */
+#define SUN8I_PWM_PCCR(pair)		(0x20 + ((pair) * 0x4))
+#define SUN8I_PWM_PCCR_SRC_SHIFT	7
+#define SUN8I_PWM_PCCR_SRC_MASK		1
+#define SUN8I_PWM_PCCR_GATE_BIT		4
+#define SUN8I_PWM_PCCR_BYPASS_BIT(chan)	((chan) % 2 + 5)
+#define SUN8I_PWM_PCCR_DIV_M_SHIFT	0
+
+/* PWMCC Pairs Dead Zone Control Registers */
+#define SUN8I_PWM_PDZCR(pair)		(0x30 + ((pair) * 0x4))
+
+/* PWM Enable Register */
+#define SUN8I_PWM_PER			0x40
+#define SUN8I_PWM_ENABLE(chan)		BIT(chan)
+
+/* PWM Capture Enable Register */
+#define SUN8I_PWM_CER			0x44
+
+/* PWM Control Register */
+#define SUN8I_PWM_PCR(chan)		(0x60 + (chan) * 0x20)
+#define SUN8I_PWM_PCR_PRESCAL_K_SHIFT	0
+#define SUN8I_PWM_PCR_PRESCAL_K_WIDTH	8
+#define SUN8I_PWM_PCR_ACTIVE_STATE	BIT(8)
+
+/* PWM Period Register */
+#define SUN8I_PWM_PPR(chan)		(0x64 + (chan) * 0x20)
+#define SUN8I_PWM_PPR_PERIOD_MASK	GENMASK(31, 16)
+#define SUN8I_PWM_PPR_DUTY_MASK		GENMASK(15, 0)
+#define SUN8I_PWM_PPR_PERIOD_VALUE(reg)	(FIELD_GET(SUN8I_PWM_PPR_PERIOD_MASK, reg) + 1)
+#define SUN8I_PWM_PPR_DUTY_VALUE(reg)	FIELD_GET(SUN8I_PWM_PPR_DUTY_MASK, reg)
+#define SUN8I_PWM_PPR_PERIOD(prd)	FIELD_PREP(SUN8I_PWM_PPR_PERIOD_MASK, (prd) - 1)
+#define SUN8I_PWM_DUTY(dty)		FIELD_PREP(SUN8I_PWM_PPR_DUTY_MASK, dty)
+#define SUN8I_PWM_PPR_PERIOD_MAX	(FIELD_MAX(SUN8I_PWM_PPR_PERIOD_MASK) + 1)
+
+/* PWM Count Register */
+#define SUN8I_PWM_PCNTR(chan)		(0x68 + (chan) * 0x20)
+
+/* PWM Capture Control Register */
+#define SUN8I_PWM_CCR(chan)		(0x6c + (chan) * 0x20)
+
+/* PWM Capture Rise Lock Register */
+#define SUN8I_PWM_CRLR(chan)		(0x70 + (chan) * 0x20)
+
+/* PWM Capture Fall Lock Register */
+#define SUN8I_PWM_CFLR(chan)		(0x74 + (chan) * 0x20)
+
+#define SUN8I_PWM_PAIR_IDX(chan)	((chan) >> 1)
+
+/*
+ * Block diagram of the PWM clock controller:
+ *
+ *             _____      ______      ________
+ * OSC24M --->|     |    |      |    |        |
+ * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> SUN8I_PWM_clock_src_xy
+ *            |_____|    |______|    |________|
+ *                               ________
+ *                              |        |
+ *                           +->| /div_k |---> SUN8I_PWM_clock_x
+ *                           |  |________|
+ *                           |    ______
+ *                           |   |      |
+ *                           +-->| Gate |----> SUN8I_PWM_bypass_clock_x
+ *                           |   |______|
+ * SUN8I_PWM_clock_src_xy ---+   ________
+ *                           |  |        |
+ *                           +->| /div_k |---> SUN8I_PWM_clock_y
+ *                           |  |________|
+ *                           |    ______
+ *                           |   |      |
+ *                           +-->| Gate |----> SUN8I_PWM_bypass_clock_y
+ *                               |______|
+ *
+ * NB: when the bypass is set, all the PWM logic is bypassed.
+ * So, the duty cycle and polarity can't be modified (we just have a clock).
+ * The bypass in PWM mode is used to achieve a 1/2 relative duty cycle with the
+ * fastest clock.
+ *
+ * SUN8I_PWM_clock_x/y serve for the PWM purpose.
+ * SUN8I_PWM_bypass_clock_x/y serve for the clock-provider purpose.
+ *
+ */
+
+/*
+ * Table used for /div_m (diviser before obtaining SUN8I_PWM_clock_src_xy)
+ * It's actually CLK_DIVIDER_POWER_OF_TWO, but limited to /256
+ */
+#define CLK_TABLE_DIV_M_ENTRY(i) { \
+	.val = (i), .div = 1 << (i) \
+}
+
+static const struct clk_div_table clk_table_div_m[] = {
+	CLK_TABLE_DIV_M_ENTRY(0),
+	CLK_TABLE_DIV_M_ENTRY(1),
+	CLK_TABLE_DIV_M_ENTRY(2),
+	CLK_TABLE_DIV_M_ENTRY(3),
+	CLK_TABLE_DIV_M_ENTRY(4),
+	CLK_TABLE_DIV_M_ENTRY(5),
+	CLK_TABLE_DIV_M_ENTRY(6),
+	CLK_TABLE_DIV_M_ENTRY(7),
+	CLK_TABLE_DIV_M_ENTRY(8),
+	{ /* sentinel */ }
+};
+
+#define SUN8I_PWM_XY_SRC_GATE(_pair, _reg)		\
+struct clk_gate gate_xy_##_pair = {			\
+	.reg = (void *)(_reg),				\
+	.bit_idx = SUN8I_PWM_PCCR_GATE_BIT,		\
+	.hw.init = &(struct clk_init_data){		\
+		.ops = &clk_gate_ops,			\
+	}						\
+}
+
+#define SUN8I_PWM_XY_SRC_MUX(_pair, _reg)		\
+struct clk_mux mux_xy_##_pair = {			\
+	.reg = (void *)(_reg),				\
+	.shift = SUN8I_PWM_PCCR_SRC_SHIFT,		\
+	.mask = SUN8I_PWM_PCCR_SRC_MASK,		\
+	.flags = CLK_MUX_ROUND_CLOSEST,			\
+	.hw.init = &(struct clk_init_data){		\
+		.ops = &clk_mux_ops,			\
+	}						\
+}
+
+#define SUN8I_PWM_XY_SRC_DIV(_pair, _reg)		\
+struct clk_divider rate_xy_##_pair = {			\
+	.reg = (void *)(_reg),				\
+	.shift = SUN8I_PWM_PCCR_DIV_M_SHIFT,		\
+	.table = clk_table_div_m,			\
+	.hw.init = &(struct clk_init_data){		\
+		.ops = &clk_divider_ops,		\
+	}						\
+}
+
+#define SUN8I_PWM_X_DIV(_idx, _reg)			\
+struct clk_divider rate_x_##_idx = {			\
+	.reg = (void *)(_reg),				\
+	.shift = SUN8I_PWM_PCR_PRESCAL_K_SHIFT,		\
+	.width = SUN8I_PWM_PCR_PRESCAL_K_WIDTH,		\
+	.hw.init = &(struct clk_init_data){		\
+		.ops = &clk_divider_ops,		\
+	}						\
+}
+
+#define SUN8I_PWM_X_BYPASS_GATE(_idx)			\
+struct clk_gate gate_x_bypass_##_idx = {		\
+	.reg = (void *)SUN8I_PWM_PER,			\
+	.bit_idx = _idx,				\
+	.hw.init = &(struct clk_init_data){		\
+		.ops = &clk_gate_ops,			\
+	}						\
+}
+
+#define SUN8I_PWM_XY_CLK_SRC(_pair, _reg)			\
+	static SUN8I_PWM_XY_SRC_MUX(_pair, _reg);		\
+	static SUN8I_PWM_XY_SRC_GATE(_pair, _reg);		\
+	static SUN8I_PWM_XY_SRC_DIV(_pair, _reg)
+
+#define SUN8I_PWM_X_CLK(_idx)					\
+	static SUN8I_PWM_X_DIV(_idx, SUN8I_PWM_PCR(_idx))
+
+#define SUN8I_PWM_X_BYPASS_CLK(_idx)				\
+	SUN8I_PWM_X_BYPASS_GATE(_idx)
+
+#define REF_CLK_XY_SRC(_pair)						\
+	{								\
+		.name = "pwm-clk-src" #_pair,				\
+		.mux_hw = &mux_xy_##_pair.hw,				\
+		.gate_hw = &gate_xy_##_pair.hw,				\
+		.rate_hw = &rate_xy_##_pair.hw,				\
+	}
+
+#define REF_CLK_X(_idx, _pair)						\
+	{								\
+		.name = "pwm-clk" #_idx,				\
+		.parent_names = (const char *[]){ "pwm-clk-src" #_pair }, \
+		.num_parents = 1,					\
+		.rate_hw = &rate_x_##_idx.hw,				\
+		.flags = CLK_SET_RATE_PARENT,				\
+	}
+
+#define REF_CLK_BYPASS(_idx, _pair)					\
+	{								\
+		.name = "pwm-clk-bypass" #_idx,				\
+		.parent_names = (const char *[]){ "pwm-clk-src" #_pair }, \
+		.num_parents = 1,					\
+		.gate_hw = &gate_x_bypass_##_idx.hw,			\
+		.flags = CLK_SET_RATE_PARENT,				\
+	}
+
+/*
+ * SUN8I_PWM_clock_src_xy generation:
+ *             _____      ______      ________
+ * OSC24M --->|     |    |      |    |        |
+ * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> SUN8I_PWM_clock_src_xy
+ *            |_____|    |______|    |________|
+ */
+SUN8I_PWM_XY_CLK_SRC(01, SUN8I_PWM_PCCR(0));
+SUN8I_PWM_XY_CLK_SRC(23, SUN8I_PWM_PCCR(1));
+SUN8I_PWM_XY_CLK_SRC(45, SUN8I_PWM_PCCR(2));
+
+/*
+ * SUN8I_PWM_clock_x_div generation:
+ *                            ________
+ *                           |        | SUN8I_PWM_clock_x/y
+ * SUN8I_PWM_clock_src_xy -->| /div_k |--------------->
+ *                           |________|
+ */
+SUN8I_PWM_X_CLK(0);
+SUN8I_PWM_X_CLK(1);
+SUN8I_PWM_X_CLK(2);
+SUN8I_PWM_X_CLK(3);
+SUN8I_PWM_X_CLK(4);
+SUN8I_PWM_X_CLK(5);
+
+/*
+ * SUN8I_PWM_bypass_clock_xy generation:
+ *                             ______
+ *                            |      |
+ * SUN8I_PWM_clock_src_xy --->| Gate |-------> SUN8I_PWM_bypass_clock_x
+ *                            |______|
+ *
+ * The gate is actually SUN8I_PWM_PER register.
+ */
+SUN8I_PWM_X_BYPASS_CLK(0);
+SUN8I_PWM_X_BYPASS_CLK(1);
+SUN8I_PWM_X_BYPASS_CLK(2);
+SUN8I_PWM_X_BYPASS_CLK(3);
+SUN8I_PWM_X_BYPASS_CLK(4);
+SUN8I_PWM_X_BYPASS_CLK(5);
+
+struct clk_pwm_data {
+	const char *name;
+	const char **parent_names;
+	unsigned int num_parents;
+	struct clk_hw *mux_hw;
+	struct clk_hw *rate_hw;
+	struct clk_hw *gate_hw;
+	unsigned long flags;
+};
+
+/* Indexes of REF_CLK_BYPASS and REF_CLK_XY_SRC in the array */
+#define CLK_BYPASS_IDX(sun8i_chip, chan) ((sun8i_chip)->data->npwm + (chan))
+#define CLK_XY_SRC_IDX(sun8i_chip, chan) \
+	((sun8i_chip)->data->npwm * 2 + SUN8I_PWM_PAIR_IDX(chan))
+static struct clk_pwm_data pwmcc_data[] = {
+	REF_CLK_X(0, 01),
+	REF_CLK_X(1, 01),
+	REF_CLK_X(2, 23),
+	REF_CLK_X(3, 23),
+	REF_CLK_X(4, 45),
+	REF_CLK_X(5, 45),
+	REF_CLK_BYPASS(0, 01),
+	REF_CLK_BYPASS(1, 01),
+	REF_CLK_BYPASS(2, 23),
+	REF_CLK_BYPASS(3, 23),
+	REF_CLK_BYPASS(4, 45),
+	REF_CLK_BYPASS(5, 45),
+	REF_CLK_XY_SRC(01),
+	REF_CLK_XY_SRC(23),
+	REF_CLK_XY_SRC(45),
+	{ /* sentinel */ }
+};
+
+enum sun8i_pwm_mode {
+	SUN8I_PWM_MODE_NONE,
+	SUN8I_PWM_MODE_PWM,
+	SUN8I_PWM_MODE_CLK,
+};
+
+struct sun8i_pwm_data {
+	unsigned int npwm;
+};
+
+struct sun8i_pwm_channel {
+	struct clk *pwm_clk;
+	enum sun8i_pwm_mode mode;
+};
+
+struct clk_pwm_pdata {
+	struct clk_hw_onecell_data *hw_data;
+	spinlock_t lock;
+	void __iomem *reg;
+};
+
+struct sun8i_pwm_chip {
+	struct clk_pwm_pdata *clk_pdata;
+	struct sun8i_pwm_channel *channels;
+	struct clk *bus_clk;
+	struct reset_control *rst;
+	void __iomem *base;
+	const struct sun8i_pwm_data *data;
+};
+
+struct sun8i_pwm_waveform {
+	u8 enabled:1;
+	u8 active_state:1;
+	u8 bypass_en:1;
+	u16 duty_ticks;
+	u32 period_ticks;
+	unsigned long clk_rate;
+};
+
+static inline struct sun8i_pwm_chip *sun8i_pwm_from_chip(const struct pwm_chip *chip)
+{
+	return pwmchip_get_drvdata(chip);
+}
+
+static inline u32 sun8i_pwm_readl(struct sun8i_pwm_chip *sun8i_chip,
+				  unsigned long offset)
+{
+	return readl(sun8i_chip->base + offset);
+}
+
+static inline void sun8i_pwm_writel(struct sun8i_pwm_chip *sun8i_chip,
+				    u32 val, unsigned long offset)
+{
+	writel(val, sun8i_chip->base + offset);
+}
+
+static void sun8i_pwm_set_bypass(struct sun8i_pwm_chip *sun8i_chip,
+				 unsigned int idx, bool en_bypass)
+{
+	unsigned long flags, reg_offset;
+	u32 val;
+
+	spin_lock_irqsave(&sun8i_chip->clk_pdata->lock, flags);
+
+	reg_offset = SUN8I_PWM_PCCR(SUN8I_PWM_PAIR_IDX(idx));
+	val = sun8i_pwm_readl(sun8i_chip, reg_offset);
+	if (en_bypass)
+		val |= BIT(SUN8I_PWM_PCCR_BYPASS_BIT(idx));
+	else
+		val &= ~BIT(SUN8I_PWM_PCCR_BYPASS_BIT(idx));
+
+	sun8i_pwm_writel(sun8i_chip, val, reg_offset);
+
+	spin_unlock_irqrestore(&sun8i_chip->clk_pdata->lock, flags);
+}
+
+static int sun8i_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip);
+	struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm];
+
+	scoped_guard(spinlock_irqsave, &sun8i_chip->clk_pdata->lock) {
+		if (chan->mode == SUN8I_PWM_MODE_CLK)
+			return -EBUSY;
+		chan->mode = SUN8I_PWM_MODE_PWM;
+	}
+
+	return clk_prepare_enable(chan->pwm_clk);
+}
+
+static void sun8i_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip);
+	struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm];
+
+	clk_disable_unprepare(chan->pwm_clk);
+	chan->mode = SUN8I_PWM_MODE_NONE;
+}
+
+static int sun8i_pwm_read_waveform(struct pwm_chip *chip,
+				   struct pwm_device *pwm,
+				   void *_wfhw)
+{
+	struct sun8i_pwm_waveform *wfhw = _wfhw;
+	struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip);
+	struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm];
+	u32 val;
+
+	wfhw->clk_rate = clk_get_rate(chan->pwm_clk);
+
+	val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER);
+	wfhw->enabled = !!(SUN8I_PWM_ENABLE(pwm->hwpwm) & val);
+
+	val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(SUN8I_PWM_PAIR_IDX(pwm->hwpwm)));
+	wfhw->bypass_en = !!(val & BIT(SUN8I_PWM_PCCR_BYPASS_BIT(pwm->hwpwm)));
+
+	val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCR(pwm->hwpwm));
+	wfhw->active_state = !!(val & SUN8I_PWM_PCR_ACTIVE_STATE);
+
+	val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PPR(pwm->hwpwm));
+	wfhw->duty_ticks = SUN8I_PWM_PPR_DUTY_VALUE(val);
+	wfhw->period_ticks = SUN8I_PWM_PPR_PERIOD_VALUE(val);
+
+	dev_dbg(pwmchip_parent(chip),
+		"pwm%u: %s, bypass: %s, polarity: %s, clk_rate=%lu period_ticks=%u duty_ticks=%u\n",
+		pwm->hwpwm,
+		wfhw->enabled ? "enabled" : "disabled",
+		wfhw->bypass_en ? "enabled" : "disabled",
+		wfhw->active_state ? "normal" : "inversed",
+		wfhw->clk_rate, wfhw->period_ticks, wfhw->duty_ticks);
+
+	return 0;
+}
+
+static int sun8i_pwm_round_waveform_fromhw(struct pwm_chip *chip,
+					   struct pwm_device *pwm,
+					   const void *_wfhw,
+					   struct pwm_waveform *wf)
+{
+	const struct sun8i_pwm_waveform *wfhw = _wfhw;
+	u64 tmp, resolution;
+
+	dev_dbg(pwmchip_parent(chip),
+		"pwm%u: %s, bypass: %s, polarity: %s, clk_rate=%lu period_ticks=%u duty_ticks=%u\n",
+		pwm->hwpwm,
+		wfhw->enabled ? "enabled" : "disabled",
+		wfhw->bypass_en ? "enabled" : "disabled",
+		wfhw->active_state ? "normal" : "inversed",
+		wfhw->clk_rate, wfhw->period_ticks, wfhw->duty_ticks);
+
+	wf->duty_offset_ns = 0;
+
+	if (!wfhw->enabled || !wfhw->clk_rate) {
+		wf->period_length_ns = 0;
+		wf->duty_length_ns = 0;
+		return 0;
+	}
+
+	if (wfhw->bypass_en) {
+		wf->period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC,
+							wfhw->clk_rate);
+		wf->duty_length_ns = DIV_ROUND_UP_ULL(wf->period_length_ns, 2);
+		return 0;
+	}
+
+	tmp = NSEC_PER_SEC * (u64)wfhw->period_ticks;
+	wf->period_length_ns = DIV_ROUND_UP_ULL(tmp, wfhw->clk_rate);
+
+	tmp = NSEC_PER_SEC * (u64)wfhw->duty_ticks;
+	wf->duty_length_ns = DIV_ROUND_UP_ULL(tmp, wfhw->clk_rate);
+	if (!wfhw->active_state) {
+		/*
+		 * For inverted polarity, we have to fix cases where
+		 * computed duty_length_ns > requested duty_length_ns
+		 * For that, we subtract the actual resolution of the PWM
+		 * registers
+		 */
+		wf->duty_offset_ns = wf->duty_length_ns;
+		wf->duty_length_ns = wf->period_length_ns - wf->duty_length_ns;
+
+		resolution = DIV_ROUND_UP_ULL(NSEC_PER_SEC, wfhw->clk_rate);
+
+		if (wf->duty_offset_ns >= resolution)
+			wf->duty_offset_ns -= resolution;
+	}
+
+	dev_dbg(pwmchip_parent(chip),
+		"pwm%u period_length_ns=%llu duty_length_ns=%llu duty_offset_ns=%llu\n",
+		pwm->hwpwm, wf->period_length_ns, wf->duty_length_ns,
+		wf->duty_offset_ns);
+
+	return 0;
+}
+
+static int sun8i_pwm_write_waveform(struct pwm_chip *chip,
+				    struct pwm_device *pwm, const void *_wfhw)
+{
+	const struct sun8i_pwm_waveform *wfhw = _wfhw;
+	struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip);
+	struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm];
+	unsigned long flags;
+	u32 val;
+	int ret;
+
+	ret = clk_set_rate(chan->pwm_clk, wfhw->clk_rate);
+	if (ret)
+		return ret;
+
+	sun8i_pwm_set_bypass(sun8i_chip, pwm->hwpwm, wfhw->bypass_en);
+
+	val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCR(pwm->hwpwm));
+	if (wfhw->active_state)
+		val |= SUN8I_PWM_PCR_ACTIVE_STATE;
+	else
+		val &= ~SUN8I_PWM_PCR_ACTIVE_STATE;
+	sun8i_pwm_writel(sun8i_chip, val, SUN8I_PWM_PCR(pwm->hwpwm));
+
+	val = SUN8I_PWM_DUTY(wfhw->duty_ticks);
+	val |= SUN8I_PWM_PPR_PERIOD(wfhw->period_ticks);
+	sun8i_pwm_writel(sun8i_chip, val, SUN8I_PWM_PPR(pwm->hwpwm));
+
+	spin_lock_irqsave(&sun8i_chip->clk_pdata->lock, flags);
+
+	val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER);
+	if (wfhw->enabled)
+		val |= SUN8I_PWM_ENABLE(pwm->hwpwm);
+	else
+		val &= ~SUN8I_PWM_ENABLE(pwm->hwpwm);
+	sun8i_pwm_writel(sun8i_chip, val, SUN8I_PWM_PER);
+
+	spin_unlock_irqrestore(&sun8i_chip->clk_pdata->lock, flags);
+
+	return 0;
+}
+
+static int sun8i_pwm_round_waveform_tohw(struct pwm_chip *chip,
+					 struct pwm_device *pwm,
+					 const struct pwm_waveform *wf,
+					 void *_wfhw)
+{
+	struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip);
+	struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm];
+	struct sun8i_pwm_waveform *wfhw = _wfhw;
+	unsigned long max_rate;
+	long calc_rate;
+	u64 period_ratio, double_duty_ratio, freq, duty_cycle;
+
+	dev_dbg(pwmchip_parent(chip),
+		"pwm%u period_length_ns=%llu duty_length_ns=%llu duty_offset_ns=%llu\n",
+		pwm->hwpwm, wf->period_length_ns, wf->duty_length_ns,
+		wf->duty_offset_ns);
+
+	if (wf->period_length_ns == 0) {
+		wfhw->enabled = 0;
+		return 0;
+	}
+
+	wfhw->enabled = 1;
+
+	duty_cycle = wf->duty_length_ns;
+	if (wf->duty_length_ns + wf->duty_offset_ns < wf->period_length_ns)
+		wfhw->active_state = 1;
+	else
+		wfhw->active_state = 0;
+
+	dev_dbg(pwmchip_parent(chip), "polarity: %s\n",
+		wfhw->active_state ? "normal" : "inversed");
+
+	/*
+	 * Lowest possible period case:
+	 * Without bypass, the lowest possible period is when:
+	 * duty cycle = 1 and period cycle = 2 (0x10001 in period register)
+	 * E.g. if the input clock is 100MHz, we have a lowest period of 20ns.
+	 * Now, with the bypass, the period register is ignored and we directly
+	 * have the 100MHz clock as PWM output, that can act as a 10ns period
+	 * with 5ns duty.
+	 * So, to detect this lowest period case, just get the maximum possible
+	 * rate from chan->pwm_clk and compare it with requested period and
+	 * duty_cycle.
+	 *
+	 * But, to get the maximum possible rate, we have to use U32_MAX instead
+	 * of (unsigned long)-1.
+	 * This is because clk_round_rate() uses ultimately DIV_ROUND_UP_ULL()
+	 * that in turn do_div(n,base). And base is uint32_t divisor.
+	 */
+	max_rate = clk_round_rate(chan->pwm_clk, U32_MAX);
+
+	dev_dbg(pwmchip_parent(chip), "max_rate: %ld Hz\n", max_rate);
+
+	period_ratio = mul_u64_u64_div_u64(wf->period_length_ns,
+					   max_rate, NSEC_PER_SEC);
+	double_duty_ratio = mul_u64_u64_div_u64(duty_cycle, (u64)max_rate * 2,
+						NSEC_PER_SEC);
+	if (period_ratio == 1) {
+		if (double_duty_ratio == 0)
+			/* requested period and duty are too small */
+			return -EINVAL;
+		/*
+		 * If the requested period is to small to be generated by the
+		 * PWM, but matches the highest clock with a
+		 * duty_cycle >= period*2, just bypass the PWM logic
+		 */
+		freq = div64_u64(NSEC_PER_SEC, wf->period_length_ns);
+		wfhw->bypass_en = true;
+	} else {
+		wfhw->bypass_en = false;
+		freq = div64_u64(NSEC_PER_SEC * (u64)SUN8I_PWM_PPR_PERIOD_MAX,
+				 wf->period_length_ns);
+		/*
+		 * Same remark as above, this is to prevent a value to big for
+		 * clk_round_rate() to handle
+		 */
+		if (freq > U32_MAX)
+			freq = U32_MAX;
+	}
+
+	dev_dbg(pwmchip_parent(chip), "bypass: %s\n",
+		wfhw->bypass_en ? "enabled" : "disabled");
+
+	calc_rate = clk_round_rate(chan->pwm_clk, freq);
+	if (calc_rate <= 0)
+		return calc_rate ? calc_rate : -EINVAL;
+
+	dev_dbg(pwmchip_parent(chip), "calc_rate: %ld Hz\n", calc_rate);
+
+	wfhw->period_ticks = mul_u64_u64_div_u64(calc_rate,
+						 wf->period_length_ns,
+						 NSEC_PER_SEC);
+	if (wfhw->period_ticks > SUN8I_PWM_PPR_PERIOD_MAX)
+		wfhw->period_ticks = SUN8I_PWM_PPR_PERIOD_MAX;
+
+	/* min value in period register is 1 */
+	if (wfhw->period_ticks == 0)
+		return -EINVAL;
+
+	wfhw->duty_ticks = mul_u64_u64_div_u64(calc_rate, duty_cycle,
+					       NSEC_PER_SEC);
+
+	if (wfhw->duty_ticks > wfhw->period_ticks)
+		wfhw->duty_ticks = wfhw->period_ticks;
+
+	if (!wfhw->active_state)
+		wfhw->duty_ticks = wfhw->period_ticks - wfhw->duty_ticks;
+
+	dev_dbg(pwmchip_parent(chip),
+		"pwm%u period_ticks=%u duty_cycle=%llu duty_ticks=%u\n",
+		pwm->hwpwm, wfhw->period_ticks, duty_cycle, wfhw->duty_ticks);
+
+	wfhw->clk_rate = calc_rate;
+
+	return 0;
+}
+
+static const struct pwm_ops sun8i_pwm_ops = {
+	.request = sun8i_pwm_request,
+	.free = sun8i_pwm_free,
+	.sizeof_wfhw = sizeof(struct sun8i_pwm_waveform),
+	.round_waveform_tohw = sun8i_pwm_round_waveform_tohw,
+	.round_waveform_fromhw = sun8i_pwm_round_waveform_fromhw,
+	.read_waveform = sun8i_pwm_read_waveform,
+	.write_waveform = sun8i_pwm_write_waveform,
+};
+
+static struct clk_hw *sun8i_pwm_of_clk_get(struct of_phandle_args *clkspec,
+					   void *data)
+{
+	struct sun8i_pwm_chip *sun8i_chip = data;
+	struct clk_hw_onecell_data *hw_data = sun8i_chip->clk_pdata->hw_data;
+	unsigned int idx = clkspec->args[0];
+	struct sun8i_pwm_channel *chan;
+	struct clk_hw *ret_clk = NULL;
+	unsigned long flags;
+
+	if (idx >= sun8i_chip->data->npwm)
+		return ERR_PTR(-EINVAL);
+
+	chan = &sun8i_chip->channels[idx];
+
+	spin_lock_irqsave(&sun8i_chip->clk_pdata->lock, flags);
+
+	if (chan->mode == SUN8I_PWM_MODE_PWM) {
+		ret_clk = ERR_PTR(-EBUSY);
+	} else {
+		chan->mode = SUN8I_PWM_MODE_CLK;
+		ret_clk = hw_data->hws[CLK_BYPASS_IDX(sun8i_chip, idx)];
+	}
+	spin_unlock_irqrestore(&sun8i_chip->clk_pdata->lock, flags);
+
+	if (IS_ERR(ret_clk))
+		goto out;
+
+	sun8i_pwm_set_bypass(sun8i_chip, idx, true);
+out:
+	return ret_clk;
+}
+
+static int sun8i_add_composite_clk(struct clk_pwm_data *data,
+				   void __iomem *reg, spinlock_t *lock,
+				   struct device *dev, struct clk_hw **hw)
+{
+	const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *rate_ops = NULL;
+	struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *rate_hw = NULL;
+	struct device_node *node = dev->of_node;
+
+	if (data->mux_hw) {
+		struct clk_mux *mux;
+
+		mux_hw = data->mux_hw;
+		mux = to_clk_mux(mux_hw);
+		mux->lock = lock;
+		mux_ops = mux_hw->init->ops;
+		mux->reg = (uintptr_t)mux->reg + reg;
+	}
+
+	if (data->gate_hw) {
+		struct clk_gate *gate;
+
+		gate_hw = data->gate_hw;
+		gate = to_clk_gate(gate_hw);
+		gate->lock = lock;
+		gate_ops = gate_hw->init->ops;
+		gate->reg = (uintptr_t)gate->reg + reg;
+	}
+
+	if (data->rate_hw) {
+		struct clk_divider *rate;
+
+		rate_hw = data->rate_hw;
+		rate = to_clk_divider(rate_hw);
+		rate_ops = rate_hw->init->ops;
+		rate->lock = lock;
+		rate->reg = (uintptr_t)rate->reg + reg;
+
+		if (rate->table) {
+			const struct clk_div_table *clkt;
+			int table_size = 0;
+
+			for (clkt = rate->table; clkt->div; clkt++)
+				table_size++;
+			rate->width = order_base_2(table_size);
+		}
+	}
+
+	/*
+	 * Retrieve the parent clock names from DTS for pwm-clk-srcxy
+	 */
+	if (!data->parent_names) {
+		data->num_parents = of_clk_get_parent_count(node);
+		if (data->num_parents == 0)
+			return -ENOENT;
+
+		data->parent_names = devm_kzalloc(dev,
+						  sizeof(*data->parent_names),
+						  GFP_KERNEL);
+		for (unsigned int i = 0; i < data->num_parents; i++)
+			data->parent_names[i] = of_clk_get_parent_name(node, i);
+	}
+
+	*hw = clk_hw_register_composite(dev, data->name, data->parent_names,
+					data->num_parents, mux_hw,
+					mux_ops, rate_hw, rate_ops,
+					gate_hw, gate_ops, data->flags);
+
+	return PTR_ERR_OR_ZERO(*hw);
+}
+
+static int sun8i_pwm_init_clocks(struct platform_device *pdev,
+				 struct sun8i_pwm_chip *sun8i_chip)
+{
+	struct clk_pwm_pdata *pdata;
+	struct device *dev = &pdev->dev;
+	int num_clocks = 0;
+	int ret;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return dev_err_probe(dev, -ENOMEM,
+				     "Failed to allocate clk_pwm_pdata\n");
+
+	while (pwmcc_data[num_clocks].name)
+		num_clocks++;
+
+	pdata->hw_data = devm_kzalloc(dev, struct_size(pdata->hw_data, hws, num_clocks),
+				      GFP_KERNEL);
+	if (!pdata->hw_data)
+		return dev_err_probe(dev, -ENOMEM,
+				     "Failed to allocate hw clocks\n");
+
+	pdata->hw_data->num = num_clocks;
+	pdata->reg = sun8i_chip->base;
+
+	spin_lock_init(&pdata->lock);
+
+	for (int i = 0; i < num_clocks; i++) {
+		struct clk_hw **hw = &pdata->hw_data->hws[i];
+
+		ret = sun8i_add_composite_clk(&pwmcc_data[i], pdata->reg,
+					      &pdata->lock, dev, hw);
+		if (ret) {
+			dev_err_probe(dev, ret,
+				      "Failed to register hw clock %s\n",
+				      pwmcc_data[i].name);
+			for (i--; i >= 0; i--)
+				clk_hw_unregister_composite(pdata->hw_data->hws[i]);
+			return ret;
+		}
+	}
+
+	sun8i_chip->clk_pdata = pdata;
+
+	return 0;
+}
+
+static void sun8i_pwm_unregister_clk(void *data)
+{
+	struct clk_hw_onecell_data *hw_data = data;
+
+	for (unsigned int i = 0; i < hw_data->num; i++)
+		clk_hw_unregister_composite(hw_data->hws[i]);
+}
+
+static int sun8i_pwm_probe(struct platform_device *pdev)
+{
+	const struct sun8i_pwm_data *data;
+	struct device *dev = &pdev->dev;
+	struct sun8i_pwm_chip *sun8i_chip;
+	struct pwm_chip *chip;
+	int ret;
+
+	data = of_device_get_match_data(dev);
+	if (!data)
+		return dev_err_probe(dev, -ENODEV,
+				     "Missing specific data structure\n");
+
+	chip = devm_pwmchip_alloc(dev, data->npwm, sizeof(*sun8i_chip));
+	if (IS_ERR(chip))
+		return dev_err_probe(dev, PTR_ERR(chip),
+				     "Failed to allocate pwmchip\n");
+
+	sun8i_chip = sun8i_pwm_from_chip(chip);
+	sun8i_chip->data = data;
+	sun8i_chip->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(sun8i_chip->base))
+		return dev_err_probe(dev, PTR_ERR(sun8i_chip->base),
+				     "Failed to get PWM base address\n");
+
+	sun8i_chip->bus_clk = devm_clk_get_enabled(dev, "bus");
+	if (IS_ERR(sun8i_chip->bus_clk))
+		return dev_err_probe(dev, PTR_ERR(sun8i_chip->bus_clk),
+				     "Failed to get bus clock\n");
+
+	sun8i_chip->channels = devm_kmalloc_array(dev, data->npwm,
+						  sizeof(*(sun8i_chip->channels)),
+						  GFP_KERNEL);
+	if (!sun8i_chip->channels)
+		return dev_err_probe(dev, -ENOMEM,
+				     "Failed to allocate %d channels array\n",
+				     data->npwm);
+
+	chip->ops = &sun8i_pwm_ops;
+
+	ret = sun8i_pwm_init_clocks(pdev, sun8i_chip);
+	if (ret)
+		return ret;
+
+	for (unsigned int i = 0; i < data->npwm; i++) {
+		struct sun8i_pwm_channel *chan = &sun8i_chip->channels[i];
+		struct clk_hw **hw = &sun8i_chip->clk_pdata->hw_data->hws[i];
+
+		chan->pwm_clk = devm_clk_hw_get_clk(dev, *hw, NULL);
+		if (IS_ERR(chan->pwm_clk)) {
+			ret = dev_err_probe(dev, PTR_ERR(chan->pwm_clk),
+					    "Failed to register PWM clock %d\n", i);
+			return ret;
+		}
+		chan->mode = SUN8I_PWM_MODE_NONE;
+	}
+
+	ret = devm_of_clk_add_hw_provider(dev, sun8i_pwm_of_clk_get, sun8i_chip);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to add HW clock provider\n");
+
+	ret = devm_add_action_or_reset(dev, sun8i_pwm_unregister_clk,
+				       sun8i_chip->clk_pdata->hw_data);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to add devm action\n");
+
+	/* Deassert reset */
+	sun8i_chip->rst = devm_reset_control_get_shared_deasserted(dev, NULL);
+	if (IS_ERR(sun8i_chip->rst))
+		return dev_err_probe(dev, PTR_ERR(sun8i_chip->rst),
+				     "Failed to get reset control\n");
+
+	ret = devm_pwmchip_add(dev, chip);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Failed to add PWM chip\n");
+
+	platform_set_drvdata(pdev, chip);
+
+	return 0;
+}
+
+static const struct sun8i_pwm_data sun50i_h616_pwm_data = {
+	.npwm = 6,
+};
+
+static const struct of_device_id sun8i_pwm_dt_ids[] = {
+	{
+		.compatible = "allwinner,sun50i-h616-pwm",
+		.data = &sun50i_h616_pwm_data,
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, sun8i_pwm_dt_ids);
+
+static struct platform_driver sun8i_pwm_driver = {
+	.driver = {
+		.name = "sun8i-pwm",
+		.of_match_table = sun8i_pwm_dt_ids,
+	},
+	.probe = sun8i_pwm_probe,
+};
+module_platform_driver(sun8i_pwm_driver);
+
+MODULE_AUTHOR("Richard Genoud <richard.genoud@bootlin.com>");
+MODULE_DESCRIPTION("Allwinner sun8i PWM driver");
+MODULE_LICENSE("GPL");

^ permalink raw reply related

* [PATCH v6 3/4] arm64: dts: allwinner: h616: add PWM controller
From: Richard Genoud @ 2026-04-16 13:40 UTC (permalink / raw)
  To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Philipp Zabel
  Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
	bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, Richard Genoud
In-Reply-To: <20260416134037.3160537-1-richard.genoud@bootlin.com>

The H616 has a PWM controller that can provide PWM signals, but also
plain clocks.

Add the PWM controller node and pins in the device tree.

Tested-by: John Stultz <jstultz@google.com>
Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
---
 .../arm64/boot/dts/allwinner/sun50i-h616.dtsi | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
index 8d1110c14bad..1c7628a6e4bb 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
@@ -236,6 +236,17 @@ watchdog: watchdog@30090a0 {
 			clocks = <&osc24M>;
 		};
 
+		pwm: pwm@300a000 {
+			compatible = "allwinner,sun50i-h616-pwm";
+			reg = <0x0300a000 0x400>;
+			clocks = <&osc24M>, <&ccu CLK_BUS_PWM>;
+			clock-names = "mod", "bus";
+			resets = <&ccu RST_BUS_PWM>;
+			#pwm-cells = <3>;
+			#clock-cells = <1>;
+			status = "disabled";
+		};
+
 		pio: pinctrl@300b000 {
 			compatible = "allwinner,sun50i-h616-pinctrl";
 			reg = <0x0300b000 0x400>;
@@ -340,6 +351,42 @@ nand_rb1_pin: nand-rb1-pin {
 				bias-pull-up;
 			};
 
+			/omit-if-no-ref/
+			pwm0_pin: pwm0-pin {
+				pins = "PD28";
+				function = "pwm0";
+			};
+
+			/omit-if-no-ref/
+			pwm1_pin: pwm1-pin {
+				pins = "PG19";
+				function = "pwm1";
+			};
+
+			/omit-if-no-ref/
+			pwm2_pin: pwm2-pin {
+				pins = "PH2";
+				function = "pwm2";
+			};
+
+			/omit-if-no-ref/
+			pwm3_pin: pwm3-pin {
+				pins = "PH0";
+				function = "pwm3";
+			};
+
+			/omit-if-no-ref/
+			pwm4_pin: pwm4-pin {
+				pins = "PI14";
+				function = "pwm4";
+			};
+
+			/omit-if-no-ref/
+			pwm5_pin: pwm5-pin {
+				pins = "PA12";
+				function = "pwm5";
+			};
+
 			/omit-if-no-ref/
 			spi0_pins: spi0-pins {
 				pins = "PC0", "PC2", "PC4";

^ permalink raw reply related

* [PATCH v6 4/4] MAINTAINERS: Add entry on Allwinner sun8i/H616 PWM driver
From: Richard Genoud @ 2026-04-16 13:40 UTC (permalink / raw)
  To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Philipp Zabel
  Cc: Paul Kocialkowski, Thomas Petazzoni, John Stultz, Joao Schim,
	bigunclemax, linux-pwm, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, Richard Genoud
In-Reply-To: <20260416134037.3160537-1-richard.genoud@bootlin.com>

Add myself as maintainer of Allwinner SUN8I PWM driver.

Signed-off-by: Richard Genoud <richard.genoud@bootlin.com>
---
 MAINTAINERS | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e12fe1f..256ab8be19f1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -912,6 +912,11 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml
 F:	drivers/hwspinlock/sun6i_hwspinlock.c
 
+ALLWINNER SUN8I PWM DRIVER
+M:	Richard Genoud <richard.genoud@bootlin.com>
+S:	Maintained
+F:	drivers/pwm/pwm-sun8i.c
+
 ALLWINNER THERMAL DRIVER
 M:	Vasily Khoruzhick <anarsoul@gmail.com>
 M:	Yangtao Li <tiny.windzz@gmail.com>

^ permalink raw reply related

* [PATCH RFC 00/10] media: qcom: venus: add MSM8939 support
From: Erikas Bitovtas @ 2026-04-16 13:43 UTC (permalink / raw)
  To: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, André Apitzsch, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
	~postmarketos/upstreaming, phone-devel, Erikas Bitovtas

This patch series adds support for Venus on MSM8939. It is mostly
similar to MSM8916 Venus, except it needs two additional cores to be
powered on before it can start decoding.

This patch series is marked as an RFC. Before submitting a non-RFC
series, I would like to have some details clarified regarding how Venus
works in order to improve and eventually upstream support for MSM8939.

1. In downstream, particularly in LA.BR.1.2.9.1_rb1.5, the buses
   for vcodec0 cores have only decoding bits enabled, as depicted
   by qcom,bus-configs property of qcom,msm-bus-clients children
   in qcom,vidc node. Do I understand correctly that these cores
   are only needed for decoding, and not for encoding?
2. Currently in device tree there is a video-decoder subnode for Venus
   node, however, for SDM845-v2 (and newer) chipsets, Venus does not use
   subnodes. Does this mean it should be dropped for MSM8939 as well?
3. MSM8939 supports HEVC decoding, however, as the patchset is written
   now, it does not work. It can be enabled, however, it will result in
   breakage of Venus for faulty MSM8916 firmwares, because the code
   disabling HEVC for HFI v1 needs to be removed, and as per commit
   c50cc6dc6c48 ("media: venus: hfi_parser: Ignore HEVC encoding for V1"),
   this would break support for some MSM8916 devices. What could be the
   best way to work around this?
4. To attach vcodec0 power domain list to dev_{dec,enc}, I had to move
   vdec_get and venc_get later in the probe. Should this be avoided, and
   is there a better way to attach vcodec power domains?

There may be some other issues with this patchset - this is WIP code, so
feedback is very appreciated. Thank you!

Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
---
André Apitzsch (4):
      media: dt-bindings: venus: Add qcom,msm8939 schema
      media: venus: core: Add msm8939 resource struct
      arm64: dts: qcom: msm8939: Add venus node
      arm64: dts: qcom: msm8939-longcheer-l9100: Enable venus node

Erikas Bitovtas (6):
      media: venus: add pmdomains to the struct based on the purpose of cores
      arm64: dts: qcom: msm8939-asus-z00t: add Venus
      clk: qcom: gcc-msm8939: mark Venus core GDSCs as hardware controlled
      media: venus: move getting vdec and venc for later
      media: qcom: venus: Move HFI v3 venc and vdec methods to HFI v1
      media: venus: add power domain enable logic for Venus cores

 .../bindings/media/qcom,msm8939-venus.yaml         | 104 ++++++++++++
 arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts     |   8 +
 .../boot/dts/qcom/msm8939-longcheer-l9100.dts      |   8 +
 arch/arm64/boot/dts/qcom/msm8939.dtsi              |  24 +++
 drivers/clk/qcom/gcc-msm8939.c                     |   4 +
 drivers/media/platform/qcom/venus/core.c           |  39 +++++
 drivers/media/platform/qcom/venus/core.h           |   8 +
 drivers/media/platform/qcom/venus/pm_helpers.c     | 187 ++++++++++++++++++---
 drivers/media/platform/qcom/venus/vdec.c           |  12 +-
 drivers/media/platform/qcom/venus/venc.c           |  12 +-
 10 files changed, 374 insertions(+), 32 deletions(-)
---
base-commit: 936c21068d7ade00325e40d82bfd2f3f29d9f659
change-id: 20260416-msm8939-venus-rfc-c025c4c74fae

Best regards,
--  
Erikas Bitovtas <xerikasxx@gmail.com>


^ permalink raw reply

* [PATCH RFC 01/10] media: dt-bindings: venus: Add qcom,msm8939 schema
From: Erikas Bitovtas @ 2026-04-16 13:43 UTC (permalink / raw)
  To: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, André Apitzsch, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
	~postmarketos/upstreaming, phone-devel, Erikas Bitovtas
In-Reply-To: <20260416-msm8939-venus-rfc-v1-0-a09fcf2c23df@gmail.com>

From: André Apitzsch <git@apitzsch.eu>

Add a schema description for the Venus video decoder/encoder IP in
MSM8939.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
---
 .../bindings/media/qcom,msm8939-venus.yaml         | 104 +++++++++++++++++++++
 1 file changed, 104 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml b/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml
new file mode 100644
index 000000000000..730473cfcce4
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml
@@ -0,0 +1,104 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/qcom,msm8939-venus.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm MSM8939 Venus video encode and decode accelerators
+
+maintainers:
+  - André Apitzsch <git@apitzsch.eu>
+  - Erikas Bitovtas <xerikasxx@gmail.com>
+
+description: |
+  The Venus IP is a video encode and decode accelerator present
+  on Qualcomm platforms
+
+allOf:
+  - $ref: qcom,venus-common.yaml#
+
+properties:
+  compatible:
+    const: qcom,msm8939-venus
+
+  power-domains:
+    maxItems: 1
+
+  clocks:
+    maxItems: 3
+
+  clock-names:
+    items:
+      - const: core
+      - const: iface
+      - const: bus
+
+  iommus:
+    maxItems: 1
+
+  video-decoder:
+    type: object
+
+    properties:
+      compatible:
+        const: venus-decoder
+
+      clocks:
+        maxItems: 2
+
+      clock-names:
+        items:
+          - const: core0
+          - const: core1
+
+      power-domains:
+        maxItems: 2
+
+      power-domain-names:
+        items:
+          - const: core0
+          - const: core1
+
+    required:
+      - compatible
+      - clocks
+      - clock-names
+      - power-domains
+      - power-domain-names
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - iommus
+  - video-decoder
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/qcom,gcc-msm8939.h>
+
+    video-codec@1d00000 {
+        compatible = "qcom,msm8939-venus";
+        reg = <0x01d00000 0xff000>;
+        interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>,
+                 <&gcc GCC_VENUS0_AHB_CLK>,
+                 <&gcc GCC_VENUS0_AXI_CLK>;
+        clock-names = "core", "iface", "bus";
+        power-domains = <&gcc VENUS_GDSC>;
+        iommus = <&apps_iommu 5>;
+        memory-region = <&venus_mem>;
+
+        video-decoder {
+          compatible = "venus-decoder";
+          clocks = <&gcc GCC_VENUS0_CORE0_VCODEC0_CLK>,
+                   <&gcc GCC_VENUS0_CORE1_VCODEC0_CLK>;
+          clock-names = "core0", "core1";
+          power-domains = <&gcc VENUS_CORE0_GDSC>,
+                          <&gcc VENUS_CORE1_GDSC>;
+          power-domain-names = "vcodec_core0", "vcodec_core1";
+        };
+    };

-- 
2.53.0


^ permalink raw reply related

* [PATCH RFC 02/10] media: venus: add pmdomains to the struct based on the purpose of cores
From: Erikas Bitovtas @ 2026-04-16 13:43 UTC (permalink / raw)
  To: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, André Apitzsch, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
	~postmarketos/upstreaming, phone-devel, Erikas Bitovtas
In-Reply-To: <20260416-msm8939-venus-rfc-v1-0-a09fcf2c23df@gmail.com>

Add vcodec0_pmdomains and vcodec1_pmdomains to indicate power domains
for Venus cores.

Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
---
 drivers/media/platform/qcom/venus/core.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index 7506f5d0f609..e56ce740d1eb 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -80,6 +80,10 @@ struct venus_resources {
 	const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX];
 	const char * const vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX];
 	unsigned int vcodec_clks_num;
+	const char **vcodec0_pmdomains;
+	unsigned int vcodec0_pmdomains_num;
+	const char **vcodec1_pmdomains;
+	unsigned int vcodec1_pmdomains_num;
 	const char **vcodec_pmdomains;
 	unsigned int vcodec_pmdomains_num;
 	const char **opp_pmdomain;
@@ -144,6 +148,8 @@ struct venus_format {
  * @vcodec1_clks: an array of vcodec1 struct clk pointers
  * @video_path: an interconnect handle to video to/from memory path
  * @cpucfg_path: an interconnect handle to cpu configuration path
+ * @vcodec0_pmdomans: a pointer to a list of pmdomains for vcodec0 cores
+ * @vcodec1_pmdomans: a pointer to a list of pmdomains for vcodec1 cores
  * @pmdomains:	a pointer to a list of pmdomains
  * @opp_pmdomain: an OPP power-domain
  * @resets: an array of reset signals
@@ -198,6 +204,8 @@ struct venus_core {
 	struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX];
 	struct icc_path *video_path;
 	struct icc_path *cpucfg_path;
+	struct dev_pm_domain_list *vcodec0_pmdomains;
+	struct dev_pm_domain_list *vcodec1_pmdomains;
 	struct dev_pm_domain_list *pmdomains;
 	struct dev_pm_domain_list *opp_pmdomain;
 	struct reset_control *resets[VIDC_RESETS_NUM_MAX];

-- 
2.53.0


^ permalink raw reply related

* [PATCH RFC 03/10] media: venus: core: Add msm8939 resource struct
From: Erikas Bitovtas @ 2026-04-16 13:43 UTC (permalink / raw)
  To: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, André Apitzsch, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
	~postmarketos/upstreaming, phone-devel, Erikas Bitovtas
In-Reply-To: <20260416-msm8939-venus-rfc-v1-0-a09fcf2c23df@gmail.com>

From: André Apitzsch <git@apitzsch.eu>

Add msm8939 configuration data and related compatible.
Cores on MSM8939 Venus are used for decoding, not encoding. Move them to
vcodec0 so they can be enabled accordingly.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
---
 drivers/media/platform/qcom/venus/core.c | 39 ++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index 7e639760c41d..b67cf1628f1c 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -688,6 +688,44 @@ static const struct venus_resources msm8916_res = {
 	.enc_nodename = "video-encoder",
 };
 
+static const struct freq_tbl msm8939_freq_table[] = {
+	{ 489600, 266670000 },	/* 1080p @ 60 */
+	{ 244800, 133330000 },	/* 1080p @ 30 */
+	{ 244800, 200000000 },	/* 1080p @ 30 */
+	{ 220800, 133330000 },	/* 720p @ 60 */
+	{ 108000, 133330000 },	/* 720p @ 30 */
+	{ 108000, 200000000 },	/* 720p @ 30 */
+	{ 72000, 133330000 },	/* VGA @ 60 */
+	{ 36000, 133330000 },	/* VGA @ 30 */
+};
+
+static const struct reg_val msm8939_reg_preset[] = {
+	{ 0xe0020, 0x0aaaaaaa },
+	{ 0xe0024, 0x0aaaaaaa },
+	{ 0x80124, 0x00000003 },
+};
+
+static const struct venus_resources msm8939_res = {
+	.freq_tbl = msm8939_freq_table,
+	.freq_tbl_size = ARRAY_SIZE(msm8939_freq_table),
+	.reg_tbl = msm8939_reg_preset,
+	.reg_tbl_size = ARRAY_SIZE(msm8939_reg_preset),
+	.clks = { "core", "iface", "bus", },
+	.clks_num = 3,
+	.vcodec0_clks = { "core0", "core1" },
+	.vcodec_clks_num = 2,
+	.vcodec0_pmdomains = (const char *[]) { "core0", "core1" },
+	.vcodec0_pmdomains_num = 2,
+	.max_load = 489600, /* 1080p@30 + 1080p@30 */
+	.hfi_version = HFI_VERSION_1XX,
+	.vmem_id = VIDC_RESOURCE_NONE,
+	.vmem_size = 0,
+	.vmem_addr = 0,
+	.dma_mask = 0xddc00000 - 1,
+	.fwname = "qcom/venus-1.8/venus.mbn",
+	.enc_nodename = "video-encoder",
+};
+
 static const struct freq_tbl msm8996_freq_table[] = {
 	{ 1944000, 520000000 },	/* 4k UHD @ 60 (decode only) */
 	{  972000, 520000000 },	/* 4k UHD @ 30 */
@@ -1121,6 +1159,7 @@ static const struct venus_resources qcm2290_res = {
 
 static const struct of_device_id venus_dt_match[] = {
 	{ .compatible = "qcom,msm8916-venus", .data = &msm8916_res, },
+	{ .compatible = "qcom,msm8939-venus", .data = &msm8939_res, },
 	{ .compatible = "qcom,msm8996-venus", .data = &msm8996_res, },
 	{ .compatible = "qcom,msm8998-venus", .data = &msm8998_res, },
 	{ .compatible = "qcom,qcm2290-venus", .data = &qcm2290_res, },

-- 
2.53.0


^ permalink raw reply related

* [PATCH RFC 04/10] arm64: dts: qcom: msm8939: Add venus node
From: Erikas Bitovtas @ 2026-04-16 13:43 UTC (permalink / raw)
  To: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, André Apitzsch, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
	~postmarketos/upstreaming, phone-devel, Erikas Bitovtas
In-Reply-To: <20260416-msm8939-venus-rfc-v1-0-a09fcf2c23df@gmail.com>

From: André Apitzsch <git@apitzsch.eu>

Add DT entries for the msm8939 venus encoder/decoder.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
---
 arch/arm64/boot/dts/qcom/msm8939.dtsi | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/msm8939.dtsi b/arch/arm64/boot/dts/qcom/msm8939.dtsi
index d4d7b0c9206c..b7cfd289d31c 100644
--- a/arch/arm64/boot/dts/qcom/msm8939.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8939.dtsi
@@ -1627,6 +1627,30 @@ opp-19200000 {
 			};
 		};
 
+		venus: video-codec@1d00000 {
+			compatible = "qcom,msm8939-venus";
+			reg = <0x01d00000 0xff000>;
+			interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>,
+				 <&gcc GCC_VENUS0_AHB_CLK>,
+				 <&gcc GCC_VENUS0_AXI_CLK>;
+			clock-names = "core", "iface", "bus";
+			power-domains = <&gcc VENUS_GDSC>;
+			iommus = <&apps_iommu 5>;
+			memory-region = <&venus_mem>;
+			status = "disabled";
+
+			video-decoder {
+				compatible = "venus-decoder";
+				clocks = <&gcc GCC_VENUS0_CORE0_VCODEC0_CLK>,
+					 <&gcc GCC_VENUS0_CORE1_VCODEC0_CLK>;
+				clock-names = "core0", "core1";
+				power-domains = <&gcc VENUS_CORE0_GDSC>,
+						<&gcc VENUS_CORE1_GDSC>;
+				power-domain-names = "core0", "core1";
+			};
+		};
+
 		apps_iommu: iommu@1ef0000 {
 			compatible = "qcom,msm8916-iommu", "qcom,msm-iommu-v1";
 			reg = <0x01ef0000 0x3000>;

-- 
2.53.0


^ permalink raw reply related

* [PATCH RFC 05/10] arm64: dts: qcom: msm8939-longcheer-l9100: Enable venus node
From: Erikas Bitovtas @ 2026-04-16 13:43 UTC (permalink / raw)
  To: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, André Apitzsch, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
	~postmarketos/upstreaming, phone-devel, Erikas Bitovtas
In-Reply-To: <20260416-msm8939-venus-rfc-v1-0-a09fcf2c23df@gmail.com>

From: André Apitzsch <git@apitzsch.eu>

Enable the venus node so that the video encoder/decoder will start
working.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
---
 arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts b/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts
index 13422a19c26a..48514c3df718 100644
--- a/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts
+++ b/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts
@@ -314,6 +314,14 @@ &usb_hs_phy {
 	extcon = <&usb_id>;
 };
 
+&venus {
+	status = "okay";
+};
+
+&venus_mem {
+	status = "okay";
+};
+
 &wcnss {
 	status = "okay";
 };

-- 
2.53.0


^ permalink raw reply related

* [PATCH RFC 06/10] arm64: dts: qcom: msm8939-asus-z00t: add Venus
From: Erikas Bitovtas @ 2026-04-16 13:43 UTC (permalink / raw)
  To: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, André Apitzsch, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
	~postmarketos/upstreaming, phone-devel, Erikas Bitovtas
In-Reply-To: <20260416-msm8939-venus-rfc-v1-0-a09fcf2c23df@gmail.com>

Enable Venus video encoder/decoder for Asus ZenFone 2 Laser/Selfie.

Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
---
 arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts b/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts
index 90e966242720..231a3e9c1929 100644
--- a/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts
+++ b/arch/arm64/boot/dts/qcom/msm8939-asus-z00t.dts
@@ -267,6 +267,14 @@ &usb_hs_phy {
 	extcon = <&usb_id>;
 };
 
+&venus {
+	status = "okay";
+};
+
+&venus_mem {
+	status = "okay";
+};
+
 &wcnss {
 	status = "okay";
 };

-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH v11 2/4] crypto: spacc - Add SPAcc ahash support
From: Pavitrakumar Managutte @ 2026-04-16 13:44 UTC (permalink / raw)
  To: Herbert Xu
  Cc: linux-crypto, linux-kernel, devicetree, robh, conor+dt,
	Ruud.Derwig, manjunath.hadli, adityak, navami.telsang, bhoomikak
In-Reply-To: <ad4iJhEM-ZwgadBh@gondor.apana.org.au>

Hi Herbert,
  I have pushed the V12 patchset as per your inputs. Dropped SM3 for
now, added other code improvements.

Warm regards,
PK


On Tue, Apr 14, 2026 at 4:47 PM Herbert Xu <herbert@gondor.apana.org.au> wrote:
>
> On Tue, Apr 14, 2026 at 03:58:16PM +0530, Pavitrakumar Managutte wrote:
> > Hi Herbert,
> >    If the above snip looks good, I can push that and some more code
> > clean-ups/improvements as part of V12 patchset. Do let me know.
> >
> > Below are the code fixes and improvements
> > 1. Multi-device safety handling - All packed up inside priv
> > 2. Minor code polishes
> > 3. memzero_explicit inside setkey, spacc_compute_xcbc_key etc.
> > 4. Algo registration clean-ups
>
> I would prefer if you left out sm3 for now.  If it really mattered
> someone would move it to lib/crypto.
>
> Thanks,
> --
> Email: Herbert Xu <herbert@gondor.apana.org.au>
> Home Page: http://gondor.apana.org.au/~herbert/
> PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
>

^ permalink raw reply

* [PATCH RFC 07/10] clk: qcom: gcc-msm8939: mark Venus core GDSCs as hardware controlled
From: Erikas Bitovtas @ 2026-04-16 13:43 UTC (permalink / raw)
  To: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, André Apitzsch, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
	~postmarketos/upstreaming, phone-devel, Erikas Bitovtas
In-Reply-To: <20260416-msm8939-venus-rfc-v1-0-a09fcf2c23df@gmail.com>

Since in downstream kernel VENUS_CORE0_GDSC and VENUS_CORE1_GDSC have a
device tree property "qcom,supports-hw-trigger", add a HW_CTRL flag
to these GDSCs to indicate that they are hardware controlled.

Because they can be switched off at any moment, also skip voting for
it so it can be enabled later.

Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
---
 drivers/clk/qcom/gcc-msm8939.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/clk/qcom/gcc-msm8939.c b/drivers/clk/qcom/gcc-msm8939.c
index 45193b3d714b..420997b00ae0 100644
--- a/drivers/clk/qcom/gcc-msm8939.c
+++ b/drivers/clk/qcom/gcc-msm8939.c
@@ -3664,6 +3664,7 @@ static struct clk_branch gcc_venus0_vcodec0_clk = {
 
 static struct clk_branch gcc_venus0_core0_vcodec0_clk = {
 	.halt_reg = 0x4c02c,
+	.halt_check = BRANCH_HALT_SKIP,
 	.clkr = {
 		.enable_reg = 0x4c02c,
 		.enable_mask = BIT(0),
@@ -3681,6 +3682,7 @@ static struct clk_branch gcc_venus0_core0_vcodec0_clk = {
 
 static struct clk_branch gcc_venus0_core1_vcodec0_clk = {
 	.halt_reg = 0x4c034,
+	.halt_check = BRANCH_HALT_SKIP,
 	.clkr = {
 		.enable_reg = 0x4c034,
 		.enable_mask = BIT(0),
@@ -3753,6 +3755,7 @@ static struct gdsc venus_core0_gdsc = {
 	.pd = {
 		.name = "venus_core0",
 	},
+	.flags = HW_CTRL,
 	.pwrsts = PWRSTS_OFF_ON,
 };
 
@@ -3761,6 +3764,7 @@ static struct gdsc venus_core1_gdsc = {
 	.pd = {
 		.name = "venus_core1",
 	},
+	.flags = HW_CTRL,
 	.pwrsts = PWRSTS_OFF_ON,
 };
 

-- 
2.53.0


^ permalink raw reply related

* [PATCH RFC 08/10] media: venus: move getting vdec and venc for later
From: Erikas Bitovtas @ 2026-04-16 13:43 UTC (permalink / raw)
  To: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, André Apitzsch, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
	~postmarketos/upstreaming, phone-devel, Erikas Bitovtas
In-Reply-To: <20260416-msm8939-venus-rfc-v1-0-a09fcf2c23df@gmail.com>

Call vdec_get and venc_get later in the probe, after core->dev_{dec,enc}
get assigned. This is needed so dev_dec and dev_enc are initialized
when we are calling vdec_get and venc_get, so we can attach core power
domain lists to their devices.

Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
---
 drivers/media/platform/qcom/venus/vdec.c | 12 ++++++------
 drivers/media/platform/qcom/venus/venc.c | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index daa8f56610c7..a4aacf5e535b 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -1792,12 +1792,6 @@ static int vdec_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, core);
 
-	if (core->pm_ops->vdec_get) {
-		ret = core->pm_ops->vdec_get(dev);
-		if (ret)
-			return ret;
-	}
-
 	vdev = video_device_alloc();
 	if (!vdev)
 		return -ENOMEM;
@@ -1817,6 +1811,12 @@ static int vdec_probe(struct platform_device *pdev)
 	core->vdev_dec = vdev;
 	core->dev_dec = dev;
 
+	if (core->pm_ops->vdec_get) {
+		ret = core->pm_ops->vdec_get(dev);
+		if (ret)
+			return ret;
+	}
+
 	video_set_drvdata(vdev, core);
 	pm_runtime_set_autosuspend_delay(dev, 2000);
 	pm_runtime_use_autosuspend(dev);
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index bf53267cb68d..5f6844d3d9d0 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -1568,12 +1568,6 @@ static int venc_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, core);
 
-	if (core->pm_ops->venc_get) {
-		ret = core->pm_ops->venc_get(dev);
-		if (ret)
-			return ret;
-	}
-
 	vdev = video_device_alloc();
 	if (!vdev)
 		return -ENOMEM;
@@ -1593,6 +1587,12 @@ static int venc_probe(struct platform_device *pdev)
 	core->vdev_enc = vdev;
 	core->dev_enc = dev;
 
+	if (core->pm_ops->venc_get) {
+		ret = core->pm_ops->venc_get(dev);
+		if (ret)
+			return ret;
+	}
+
 	video_set_drvdata(vdev, core);
 	pm_runtime_set_autosuspend_delay(dev, 2000);
 	pm_runtime_use_autosuspend(dev);

-- 
2.53.0


^ permalink raw reply related

* [PATCH RFC 09/10] media: qcom: venus: Move HFI v3 venc and vdec methods to HFI v1
From: Erikas Bitovtas @ 2026-04-16 13:43 UTC (permalink / raw)
  To: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, André Apitzsch, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
	~postmarketos/upstreaming, phone-devel, Erikas Bitovtas
In-Reply-To: <20260416-msm8939-venus-rfc-v1-0-a09fcf2c23df@gmail.com>

Some Qualcomm SoCs which come with HFI v1 like MSM8909 and MSM8939 also
have dedicated cores for Venus, like in HFI v3. Move methods from HFI v3
to HFI v1 so they can be reused.

Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
---
 drivers/media/platform/qcom/venus/pm_helpers.c | 66 +++++++++++++++++++-------
 1 file changed, 48 insertions(+), 18 deletions(-)

diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
index f0269524ac70..cc21a4762188 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -328,10 +328,56 @@ static int core_power_v1(struct venus_core *core, int on)
 	return ret;
 }
 
+static int vdec_get_v1(struct device *dev)
+{
+	struct venus_core *core = dev_get_drvdata(dev);
+
+	return vcodec_clks_get(core, dev, core->vcodec0_clks,
+			       core->res->vcodec0_clks);
+}
+
+static int vdec_power_v1(struct device *dev, int on)
+{
+	struct venus_core *core = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (on == POWER_ON)
+		ret = vcodec_clks_enable(core, core->vcodec0_clks);
+	else
+		vcodec_clks_disable(core, core->vcodec0_clks);
+
+	return ret;
+}
+
+static int venc_get_v1(struct device *dev)
+{
+	struct venus_core *core = dev_get_drvdata(dev);
+
+	return vcodec_clks_get(core, dev, core->vcodec1_clks,
+			       core->res->vcodec1_clks);
+}
+
+static int venc_power_v1(struct device *dev, int on)
+{
+	struct venus_core *core = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (on == POWER_ON)
+		ret = vcodec_clks_enable(core, core->vcodec1_clks);
+	else
+		vcodec_clks_disable(core, core->vcodec1_clks);
+
+	return ret;
+}
+
 static const struct venus_pm_ops pm_ops_v1 = {
 	.core_get = core_get_v1,
 	.core_put = core_put_v1,
 	.core_power = core_power_v1,
+	.vdec_get = vdec_get_v1,
+	.vdec_power = vdec_power_v1,
+	.venc_get = venc_get_v1,
+	.venc_power = venc_power_v1,
 	.load_scale = load_scale_v1,
 };
 
@@ -351,14 +397,6 @@ vcodec_control_v3(struct venus_core *core, u32 session_type, bool enable)
 		writel(1, ctrl);
 }
 
-static int vdec_get_v3(struct device *dev)
-{
-	struct venus_core *core = dev_get_drvdata(dev);
-
-	return vcodec_clks_get(core, dev, core->vcodec0_clks,
-			       core->res->vcodec0_clks);
-}
-
 static int vdec_power_v3(struct device *dev, int on)
 {
 	struct venus_core *core = dev_get_drvdata(dev);
@@ -376,14 +414,6 @@ static int vdec_power_v3(struct device *dev, int on)
 	return ret;
 }
 
-static int venc_get_v3(struct device *dev)
-{
-	struct venus_core *core = dev_get_drvdata(dev);
-
-	return vcodec_clks_get(core, dev, core->vcodec1_clks,
-			       core->res->vcodec1_clks);
-}
-
 static int venc_power_v3(struct device *dev, int on)
 {
 	struct venus_core *core = dev_get_drvdata(dev);
@@ -405,9 +435,9 @@ static const struct venus_pm_ops pm_ops_v3 = {
 	.core_get = core_get_v1,
 	.core_put = core_put_v1,
 	.core_power = core_power_v1,
-	.vdec_get = vdec_get_v3,
+	.vdec_get = vdec_get_v1,
 	.vdec_power = vdec_power_v3,
-	.venc_get = venc_get_v3,
+	.venc_get = venc_get_v1,
 	.venc_power = venc_power_v3,
 	.load_scale = load_scale_v1,
 };

-- 
2.53.0


^ permalink raw reply related

* [PATCH RFC 10/10] media: venus: add power domain enable logic for Venus cores
From: Erikas Bitovtas @ 2026-04-16 13:43 UTC (permalink / raw)
  To: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, André Apitzsch, Bjorn Andersson, Konrad Dybcio,
	Michael Turquette, Stephen Boyd
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, linux-clk,
	~postmarketos/upstreaming, phone-devel, Erikas Bitovtas
In-Reply-To: <20260416-msm8939-venus-rfc-v1-0-a09fcf2c23df@gmail.com>

Attach power domains for vdec and venc cores and power them up if a vdec
or venc session is started.

Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
---
 drivers/media/platform/qcom/venus/pm_helpers.c | 121 ++++++++++++++++++++++++-
 1 file changed, 119 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
index cc21a4762188..713812421c44 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -246,6 +246,94 @@ static int load_scale_bw(struct venus_core *core)
 	return icc_set_bw(core->video_path, total_avg, total_peak);
 }
 
+static int vcodec_domains_get_v1(struct venus_core *core, struct device *dev,
+				 struct dev_pm_domain_list **pmdomains,
+				 const char **pmdomain_names,
+				 unsigned int pmdomains_num)
+{
+	int ret;
+	struct dev_pm_domain_attach_data vcodec_data = {
+		.pd_names = pmdomain_names,
+		.num_pd_names = pmdomains_num,
+		.pd_flags = PD_FLAG_NO_DEV_LINK,
+	};
+
+	if (!pmdomain_names)
+		return 0;
+
+	ret = devm_pm_domain_attach_list(dev, &vcodec_data, pmdomains);
+	return ret;
+}
+
+static int vdec_domains_attach(struct venus_core *core)
+{
+	struct device *dev = core->dev_dec;
+	const struct venus_resources *res = core->res;
+
+	return vcodec_domains_get_v1(core, dev, &core->vcodec0_pmdomains,
+				     res->vcodec0_pmdomains,
+				     res->vcodec0_pmdomains_num);
+}
+
+static int venc_domains_attach(struct venus_core *core)
+{
+	struct device *dev = core->dev_enc;
+	const struct venus_resources *res = core->res;
+
+	return vcodec_domains_get_v1(core, dev, &core->vcodec1_pmdomains,
+				     res->vcodec1_pmdomains,
+				     res->vcodec1_pmdomains_num);
+}
+
+static int vcodec_domains_enable_v1(struct venus_core *core,
+				    struct dev_pm_domain_list *pmdomains,
+				    unsigned int pmdomains_num)
+{
+	int i, ret;
+
+	if (!pmdomains)
+		return 0;
+
+	for (i = 0; i < pmdomains_num; i++) {
+		ret = pm_runtime_resume_and_get(pmdomains->pd_devs[i]);
+		if (ret)
+			goto err;
+	}
+
+	return ret;
+
+err:
+	while (i--)
+		pm_runtime_put_sync(pmdomains->pd_devs[i]);
+
+	return ret;
+}
+
+static int vdec_domains_get(struct venus_core *core)
+{
+	const struct venus_resources *res = core->res;
+
+	return vcodec_domains_enable_v1(core, core->vcodec0_pmdomains,
+					res->vcodec0_pmdomains_num);
+}
+
+static int venc_domains_get(struct venus_core *core)
+{
+	const struct venus_resources *res = core->res;
+
+	return vcodec_domains_enable_v1(core, core->vcodec1_pmdomains,
+					res->vcodec1_pmdomains_num);
+}
+
+static void vcodec_domains_put_v1(struct dev_pm_domain_list *pmdomains,
+				  unsigned int pmdomains_num)
+{
+	unsigned int i = pmdomains_num;
+
+	while (i--)
+		pm_runtime_put_sync(pmdomains->pd_devs[i]);
+}
+
 static int load_scale_v1(struct venus_inst *inst)
 {
 	struct venus_core *core = inst->core;
@@ -331,6 +419,11 @@ static int core_power_v1(struct venus_core *core, int on)
 static int vdec_get_v1(struct device *dev)
 {
 	struct venus_core *core = dev_get_drvdata(dev);
+	int ret;
+
+	ret = vdec_domains_attach(core);
+	if (ret < 0)
+		return ret;
 
 	return vcodec_clks_get(core, dev, core->vcodec0_clks,
 			       core->res->vcodec0_clks);
@@ -339,8 +432,18 @@ static int vdec_get_v1(struct device *dev)
 static int vdec_power_v1(struct device *dev, int on)
 {
 	struct venus_core *core = dev_get_drvdata(dev);
+	const struct venus_resources *res = core->res;
 	int ret = 0;
 
+	if (on == POWER_ON)
+		ret = vdec_domains_get(core);
+	else
+		vcodec_domains_put_v1(core->vcodec0_pmdomains,
+				      res->vcodec0_pmdomains_num);
+
+	if (ret)
+		return ret;
+
 	if (on == POWER_ON)
 		ret = vcodec_clks_enable(core, core->vcodec0_clks);
 	else
@@ -352,6 +455,11 @@ static int vdec_power_v1(struct device *dev, int on)
 static int venc_get_v1(struct device *dev)
 {
 	struct venus_core *core = dev_get_drvdata(dev);
+	int ret;
+
+	ret = venc_domains_attach(core);
+	if (ret < 0)
+		return ret;
 
 	return vcodec_clks_get(core, dev, core->vcodec1_clks,
 			       core->res->vcodec1_clks);
@@ -360,8 +468,17 @@ static int venc_get_v1(struct device *dev)
 static int venc_power_v1(struct device *dev, int on)
 {
 	struct venus_core *core = dev_get_drvdata(dev);
+	const struct venus_resources *res = core->res;
 	int ret = 0;
 
+	if (on == POWER_ON)
+		ret = venc_domains_get(core);
+	else
+		vcodec_domains_put_v1(core->vcodec1_pmdomains,
+				      res->vcodec1_pmdomains_num);
+	if (ret)
+		return ret;
+
 	if (on == POWER_ON)
 		ret = vcodec_clks_enable(core, core->vcodec1_clks);
 	else
@@ -905,7 +1022,7 @@ static int venc_power_v4(struct device *dev, int on)
 	return ret;
 }
 
-static int vcodec_domains_get(struct venus_core *core)
+static int vcodec_domains_get_v4(struct venus_core *core)
 {
 	int ret;
 	struct device *dev = core->dev;
@@ -1023,7 +1140,7 @@ static int core_get_v4(struct venus_core *core)
 	if (ret)
 		return ret;
 
-	ret = vcodec_domains_get(core);
+	ret = vcodec_domains_get_v4(core);
 	if (ret)
 		return ret;
 

-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH v2 2/3] pwm: rp1: Add RP1 PWM controller driver
From: Uwe Kleine-König @ 2026-04-16 13:48 UTC (permalink / raw)
  To: Andrea della Porta
  Cc: linux-pwm, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Florian Fainelli, Broadcom internal kernel review list,
	devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
	Naushir Patuck, Stanimir Varbanov, mbrugger
In-Reply-To: <aeC6U7D6TfWm8JPx@apocalypse>

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

Hello Andrea,

one thing I forgot to ask: Is there a public reference manual covering
the hardware. If yes, please add a link at the top of the driver.

On Thu, Apr 16, 2026 at 12:30:43PM +0200, Andrea della Porta wrote:
> On 19:31 Fri 10 Apr     , Uwe Kleine-König wrote:
> > I assume there is a glitch if I update two channels and the old
> > configuration of the first channel ends while I'm in the middle of
> > configuring the second?
> 
> The configuration registers are per-channel but the update flag is global.
> I don't have details of the hw insights, my best guess is that anything that
> you set in the registers before updating the flag will take effect, so there
> should be no glitches.

Would be great if you could test that. (Something along the lines of:
configure a very short period and wait a bit to be sure the short
configuration is active. Configure something with a long period and wait
shortly to be sure that the long period started, then change the duty,
toggle the update bit and modify a 2nd channel without toggling update
again. Then check the output of the 2nd channel after the first
channel's period ended.

> > > +	if (ticks > U32_MAX)
> > > +		ticks = U32_MAX;
> > > +	wfhw->period_ticks = ticks;
> > 
> > What happens if wf->period_length_ns > 0 but ticks == 0?
> 
> I've added a check, returning 1 to signal teh round-up, and a minimum tick of 1
> in this case.

Sounds good. Are you able to verify that there is no +1 missing in the
calculation, e.g. using 1 as register value really gives you a period of
1 tick and not 2?

> > > +	if (wf->duty_offset_ns + wf->duty_length_ns >= wf->period_length_ns) {
> > 
> > The maybe surprising effect here is that in the two cases
> > 
> > 	wf->duty_offset_ns == wf->period_length_ns and wf->duty_length_ns == 0
> > 
> > and
> > 	
> > 	wf->duty_length_ns == wf->period_length_ns and wf->duty_offset_ns == 0
> > 
> > you're configuring inverted polarity. I doesn't matter technically
> > because the result is the same, but for consumers still using pwm_state
> > this is irritating. That's why pwm-stm32 uses inverted polarity only if
> > also wf->duty_length_ns and wf->duty_offset_ns are non-zero.

Please align to the pwm-stm32 algorithm (as of
https://patch.msgid.link/c5e7767cee821b5f6e00f95bd14a5e13015646fb.1776264104.git.u.kleine-koenig@baylibre.com)
here to decide when to select inverted polarity.

> > > +	}
> > > +
> > > +	return 0;
> > > +
> > > +err_disable_clk:
> > > +	clk_disable_unprepare(rp1->clk);
> > > +
> > > +	return ret;
> > > +}
> > 
> > On remove you miss to balance the call to clk_prepare_enable() (if no
> > failed call to clk_prepare_enable() in rp1_pwm_resume() happend).
> 
> Since this driver now exports a syscon, it's only builtin (=Y) so
> it cannot be unloaded.
> I've also avoided the .remove callback via .suppress_bind_attrs.

Oh no, please work cleanly here and make the driver unbindable. This
yields better code quality and also helps during development and
debugging.

Best regards
Uwe

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

^ permalink raw reply


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