* [PATCH v11 1/2] dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
From: Bryan O'Donoghue @ 2026-07-19 12:29 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel,
Bryan O'Donoghue, Krzysztof Kozlowski
In-Reply-To: <20260719-x1e-csi2-phy-v11-0-9d0a1ed0632d@linaro.org>
Add a base schema for the MIPI CSI2 PHYs on Qualcomm SoCs. This PHY
supports both DPHY and CPHY operation. A special mode of DPHY operation -
called variously split-mode or combo-mode also allows for two sensors to be
connected to one PHY.
The submitted binding here describes the DPHY modes of operation only. CPHY
is left to future work.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
.../bindings/phy/qcom,x1e80100-csi2-phy.yaml | 195 +++++++++++++++++++++
1 file changed, 195 insertions(+)
diff --git a/Documentation/devicetree/bindings/phy/qcom,x1e80100-csi2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,x1e80100-csi2-phy.yaml
new file mode 100644
index 0000000000000..880fe602945cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom,x1e80100-csi2-phy.yaml
@@ -0,0 +1,195 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/qcom,x1e80100-csi2-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm X1E80100 SoC CSI2 PHY
+
+maintainers:
+ - Bryan O'Donoghue <bod@kernel.org>
+
+description:
+ Qualcomm MIPI CSI2 C-PHY/D-PHY combination PHY. Connects MIPI CSI2 sensors
+ to Qualcomm's Camera CSI Decoder. The PHY supports both C-PHY and D-PHY
+ modes.
+
+properties:
+ compatible:
+ const: qcom,x1e80100-csi2-phy
+
+ reg:
+ maxItems: 1
+
+ "#phy-cells":
+ const: 1
+ description:
+ The single cell specifies the PHY operating mode.
+
+ clocks:
+ maxItems: 3
+
+ clock-names:
+ items:
+ - const: core
+ - const: timer
+ - const: ahb
+
+ interrupts:
+ maxItems: 1
+
+ operating-points-v2: true
+
+ opp-table:
+ type: object
+
+ power-domains:
+ items:
+ - description: Titan Top GDSC - Titan ISP Block, Global Distributed Switch Controller.
+ - description: MMCX voltage rail
+ - description: MXC or MXA voltage rail
+
+ power-domain-names:
+ items:
+ - const: top
+ - const: mmcx
+ - const: mx
+
+ vdda-0p9-supply:
+ description: Phandle to a 0.9V regulator supply to a PHY.
+
+ vdda-1p2-supply:
+ description: Phandle to 1.2V regulator supply to a PHY.
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ description:
+ Sensor input. Always present. A single sensor is described by a
+ single endpoint with one to four data lanes. DPHY split mode,
+ where two independent sensors share the same PHY, is described
+ by two endpoints; endpoint@0 with exactly two data-lanes and
+ endpoint@1 with exactly one data-lane.
+ unevaluatedProperties: false
+
+ patternProperties:
+ "^endpoint(@[0-9a-f]+)?$":
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+ properties:
+ data-lanes:
+ minItems: 1
+ maxItems: 4
+
+ required:
+ - data-lanes
+ - remote-endpoint
+
+ allOf:
+ - if:
+ required:
+ - endpoint@1
+ then:
+ properties:
+ endpoint@0:
+ properties:
+ data-lanes:
+ minItems: 2
+ maxItems: 2
+ endpoint@1:
+ properties:
+ data-lanes:
+ maxItems: 1
+ required:
+ - endpoint@0
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Output to the CAMSS CSID controller.
+
+ required:
+ - port@0
+ - port@1
+
+required:
+ - compatible
+ - reg
+ - "#phy-cells"
+ - clocks
+ - clock-names
+ - interrupts
+ - operating-points-v2
+ - power-domains
+ - power-domain-names
+ - vdda-0p9-supply
+ - vdda-1p2-supply
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/qcom,x1e80100-camcc.h>
+ #include <dt-bindings/clock/qcom,x1e80100-gcc.h>
+ #include <dt-bindings/power/qcom,rpmhpd.h>
+
+ phy@ace4000 {
+ compatible = "qcom,x1e80100-csi2-phy";
+ reg = <0x0ace4000 0x2000>;
+ #phy-cells = <1>;
+
+ clocks = <&camcc CAM_CC_CSIPHY0_CLK>,
+ <&camcc CAM_CC_CSI0PHYTIMER_CLK>,
+ <&camcc CAM_CC_CORE_AHB_CLK>;
+ clock-names = "core",
+ "timer",
+ "ahb";
+
+ interrupts = <GIC_SPI 477 IRQ_TYPE_EDGE_RISING>;
+
+ operating-points-v2 = <&csiphy_opp_table>;
+
+ power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>,
+ <&rpmhpd RPMHPD_MMCX>,
+ <&rpmhpd RPMHPD_MX>;
+ power-domain-names = "top",
+ "mmcx",
+ "mx";
+
+ vdda-0p9-supply = <&vreg_l2c_0p9>;
+ vdda-1p2-supply = <&vreg_l1c_1p2>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ csiphy0_in: endpoint {
+ data-lanes = <0 1 2 3>;
+ remote-endpoint = <&sensor_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ csiphy0_out: endpoint {
+ remote-endpoint = <&csid_in>;
+ };
+ };
+ };
+
+ csiphy_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-300000000 {
+ opp-hz = /bits/ 64 <300000000>;
+ required-opps = <&rpmhpd_opp_low_svs_d1>,
+ <&rpmhpd_opp_low_svs_d1>;
+ };
+ };
+ };
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v11 0/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: Bryan O'Donoghue @ 2026-07-19 12:29 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel,
Bryan O'Donoghue, Krzysztof Kozlowski
This driver is very critical to several SoCs being merged and CPHY being
added to Qualcomm SoCs.
It would be very beneficial therefore to get this merged in this cycle. A
lot of other enablement code depends on it.
Vinod, Kishon what do you need to take this for 7.2 ?
Changes in v11:
- Adds RB - Krzysztof
- Fixes div_u64 to div64_u64 - Sashiko
- Fixes pos * 2 reg offset - Sashiko
- Fixes devm_pm_runtime_enable. Missed in v10 - Frank
- Link to v10: https://patch.msgid.link/20260718-x1e-csi2-phy-v10-0-5720a7888953@linaro.org
Changes in v10:
- Rewords commit log of yaml schema to read more organically - Krzysztof
- title: Qualcomm X1E80100 SoC CSI2 PHY - Krzysztof
- opp-table: object - Krzysztof
- Example opp-table in PHY node - Krzysztof
- port@1 uses stock graph.yaml - Krzysztof
- Dropped redundant remote-endpoint declaration - Krzysztof
- vdda-0p9 - SoC pins are vdda-0p9 rail is l2c_0p8. Named as with pin - Vlad, Konrad
- Retaining #phy-cells = 1.
Rob gave a steer to have the consumer configure this via DT. Not the way
Vlad suggests but I'm sticking to Rob's guidance for now - Rob/Vlad.
- data-lanes - retained as per CAMSS existing.
Nihal suggested, I agreed and Loic added his assent too. Vlad disagrees
with this. I accept Nihal's suggestion though we should remain
consistent. - Nihal, Loic, Vlad.
- pd_data = {} - Sashiko, Wengmeng
- lanes_enable() failure unwinds - Sashiko
- power_off and error paths drop OPP rate before performance state - Sashiko
- probe returns -ENODEV when pd_list is NULL with ret == 0 - Sashiko
- Move to devm_clk_bulk_get_all(), timer clock taken from the bulk,
clk_names indirection removed from soc_cfg - Frank
- Drop static CSI_COMMON_CTRL5 (0x1014) table row - Nihal
- Link to v9: https://patch.msgid.link/20260708-x1e-csi2-phy-v9-0-0210b90c04cf@linaro.org
Changes in v9:
Key advantages of implementing this logic as drivers/phy:
a) Per-PHY power rails become declarable and enforceable on a per-PHY
basis.
b) With future adaptations of CAMSS routing the PHY output to something
other than the CSI Decoders becomes possible.
c) Standard framework contract. phy_configure(mipi_dphy opts) / power_on / of_xlate.
d) Introduction of CPHY in this driver will elaborate CPHY for Linux in
general as there is currently no mipi_cphy_opts structure.
- TITAN_TOP_GDSC retained. Further elaboration in below link. - Vlad
Link: https://lore.kernel.org/all/d5407ab1-1af7-4678-ae67-5cf30ce8fa4b@kernel.org/
- Consumer selects PHY mode - Rob Herring
Link: https://lore.kernel.org/linux-media/20250710230846.GA44483-robh@kernel.org/
- Incorporates two comments by Krzysztof I missed previously - krzk
- Changes 0p9 rail name to 0p8 - Konrad, Sashiko
- Uses port@0 and port@1 to denote input/output - Vladimir
- Label phy@ instead of csiphy@ in example - Sashiko
- Drops additional entries in example opp one is enough - bod
- return 0 on timer_period_ps < 6. - Sashiko
- Use power-domain names and a "scaled" bool to flag if
a power-domain needs to be scaled or not. Allowing the code to
attached to all PDs, GDSC and RPMPD alike but only scale the
RPMPD. - Sashiko
- Fixes error cleanup flagged by Sashiko.
- Checks args->args_count. Sashiko
- Performs checks on pd_list == NULL. Sashiko
- Makes opp-table required. Sashiko
- Uses const "core" and "timer" when getting clocks. - Loic
- Adds AHB clock - Vijay
- Revert v7 lane-enable change, BIT(pos * 2) is correct: data-lanes
are logical indices, CSI_COMMON_CTRL5 is a physical bitmap - Nihal
- Adds a comment to explain clock-lane - Nihal
- Link to v8: https://patch.msgid.link/20260523-x1e-csi2-phy-v8-0-a85668459521@linaro.org
Changes in v8:
- Fixes two dt verification splats I missed by passing the wrong yaml file
to my checking script :( - Rob's bot
- Fixes the polarity offset error - thanks Sashiko.
- CONFIG_PM implies CONFIG_GENERIC_PM_DOMAINS no change - Sashiko, Bryan
- Implemented suggested unwinding by Sashiko
- Leaving the flagged settle_cnt alone. Requires invalid DT settings and in
addition the result is just a long settle count. Not real bug - Sashiko
- Link to v7: https://patch.msgid.link/20260522-x1e-csi2-phy-v7-0-79cb1280fad6@linaro.org
Changes in v7:
- Made CONFIG_PM a dependency. Sashiko commented on the pd_list being NULL
and suggested I check the pointer but, we need to ramp the rails when
switching clock rate so we need CONFIG_PM full stop. - Sashiko.ai, Bryan
- Added unwind operation for performance state error path - Sashiko
- Made clock-lanes genuinely optional for the supported use-case. - Sashiko
- Fixed the enable of lanes. Thus far we have had forever it seems
val |= BIT(lane.pos * 2) which I've never looked at much because it
has always worked. But looking at how to switch on polarities I realised
the relevant register is a linear bitmask without gaps so the correct
method is `val |= BIT(lane.pos)`.
This needs an update in the legacy PHY too in another series - Bryan
- I opted not to do any of the "but if DT send junk into your driver" fixes
from Sashiko since TBH I think the code would be Spaghetti afterwards.
We trust DT and if DT is wrong we fix it, we don't try to graph its
relative (in)sanity.
- Fixed my example in the yaml. Sashiko
- Link to v6: https://patch.msgid.link/20260521-x1e-csi2-phy-v6-0-9d73d9bd7d20@linaro.org
Changes in v6:
- Taking feedback from lively debate added ports and
endpoints to the PHY - Neil, Vlad
- Detection of split mode by way of which ports are declared.
port@0 is always a sensor input.
port@1 is optional and if present implies split-mode
port@2 is always the output. - Dmitry, Neil, Vlad.
- Split mode is left as -ENOTSUPP unless/until someone with the appropriate
hardware can take on responsibility to drive to completion.
- Extending phy_config_opts dropped.
I think this is a worthwhile extension but this series no longer depends
on it so dropped. - Bryan
- MX/MXC.
Two OPP tables one for CSIPHY0/1/2 scaling MXC one for CSIPHY4 keeping
MXA at LOWSVS_D1 - to be implemented in DT not here. Taniya, Konrad, Bryan
- Changed MAINTAINERS from Supported to Maintained.
Hobby time for me right now. - Bryan
- Link to v5: https://lore.kernel.org/r/20260326-x1e-csi2-phy-v5-0-0c0fc7f5c01b@linaro.org
v5:
- Adds support to apply passed parameters for clock/data position/polarity - Neil
- Drops GEN1/GEN2 differentiation this can be reconstituted if GEN1 ever
gets supported in this driver - Dmitry
- Drops camnoc_axi, cpas_ahb - Konrad
- Renames csiphy->core csiphy_timer->timer - Konrad
- Renames rail from 0p8 to 0p9 schematics say VDD_A_CSI_n_0P9 - Konrad
- TITAN_TOP_GDSC dropped - Konrad
- Passes PHY_QCOM_CSI2_MODE_{DPHY|CPHY|SPLIT_DPHY} with the controller
selecting the mode. Only DPHY mode is supported but the method to pass
CPHY or split-mode DPHY configuration is there.
Since split-mode is a Qualcomm specific mode the PHY modes are defined in
our binding instead of adding a new type to include/linux/phy/phy.h - bod
- Depends-on: https://lore.kernel.org/r/20260325-dphy-params-extension-v1-0-c6df5599284a@linaro.org
- Link to v4: https://lore.kernel.org/r/20260315-x1e-csi2-phy-v4-0-90c09203888d@linaro.org
v4:
- MMCX, MCX and MX/MXA power-domains added - Dmitry, Vijay, Konrad
- power-domain-names added as required - bod
- opp-tables amended to capture RPMHPD deps - Dmitry, Vijay
- Switched to dev_pm_opp_set_rate, dev_pm_domain_attach_by_name etc
dropped inherited CAMSS code - Dmitry
- Amended parameters structure to specify power-domain name list - bod
- Removed dead defines - Dmitry
- Noted in CSIPHY commit log intention to rework patterns of
PHY lane configs into loops/defines/bit-fields later - Dmitry, bod
- Lowercase hex throughout - Dmitry
- The yaml and code in this driver doesn't care if the node is a
sibling or a sub-node of CAMSS confirmed to work both ways - Dmitry, bod
- Link to v3: https://lore.kernel.org/r/20260226-x1e-csi2-phy-v3-0-11e608759410@linaro.org
v3:
- Resending this to make clear this submission is additive to x1e/Hamoa
The existing bindings and code will continue to work
Bindings are added only, nothing is subtracted from existing ABI.
- Link to v2: https://lore.kernel.org/r/20260225-x1e-csi2-phy-v2-0-7756edb67ea9@linaro.org
v2:
In this updated version
- Added operating-point support
The csiphy clock sets the OPP prior to setting the rate
for csiphy and csiphy_timer - Konrad
- Combo mode
Combo mode in CAMSS yaml has been added. Right now
no code has been changed in the PHY driver to support it as
I don't have hardware to test. In principle though it can
be supported. - Vladimir
- CSIPHY init sequences
I left these as their "magic number formats". With my diminished
status as a non-qcom VPN person - I can no longer see what the bits
map to. Moreover this is the situation any non-VPN community member
will be in when submitting CSIPHY sequences derived from downstream.
I think it is perfectly reasonable to take public CSIPHY init sequences
as magic numbers. If someone with bit-level access wants to enumerate
the bits that's fine but, it shouldn't gate in the interim. - Konrad/bod
- Sensor endpoints
I've stuck to the format used by every other CSIPHY in upstream.
Sensor endpoints hit the CAMSS/CSID endpoint not a endpoint in the PHY.
Given the proposed changes to CAMSS though to support "combo mode" I
think this should achieve the same outcome - multiple sensors on the one
PHY without introducing endpoints into the PHY that no other CSIPHY in
upstream currently has.
- Bitmask of enabled lanes
Work needs to be done in the v4l2 layer to really support this.
I propose making a separate series dedicated to non-linear bit
interpretation after merging this so as to contain the scope of the
series to something more bite (byte haha) sized. - Konrad/bod
- Link to v1: https://lore.kernel.org/r/20250710-x1e-csi2-phy-v1-0-74acbb5b162b@linaro.org
v1:
This short series adds a CSI2 MIPI PHY driver, initially supporting D-PHY
mode. The core logic and init sequences come directly from CAMSS and are
working on at least five separate x1e devices.
The rationale to instantiate CSI2 PHYs as standalone devices instead of as
sub-nodes of CAMSS is as follows.
1. Precedence
CAMSS has a dedicated I2C bus called CCI Camera Control Interface.
We model this controller as its own separate device in devicetree.
This makes sense and CCI/I2C is a well defined bus type already modelled
in Linux.
MIPI CSI2 PHY devices similarly fit into a well defined separate
bus/device structure.
Contrast to another CAMSS component such as VFE, CSID or TPG these
components only interact with other CAMSS inputs/outputs unlike CSIPHY
which interacts with non-SoC components.
2. Hardware pinouts and rails
The CSI2 PHY has its own data/clock lanes out from the SoC and indeed
has its own incoming power-rails.
3. Other devicetree schemas
There are several examples throughout the kernel of CSI PHYs modeled as
standalone devices which one assumes follows the same reasoning as given
above.
I've been working on this on-and-off since the end of April:
Link: https://lore.kernel.org/linux-media/c5cf0155-f839-4db9-b865-d39b56bb1e0a@linaro.org
There is another proposal to have the PHYs be subdevices of CAMSS but, I
believe we should go with a "full fat" PHY to match best practices in
drivers/phy/qualcomm/*.
Using the standard PHY API and the parameter passing that goes with it
allows us to move away from custom interfaces in CAMSS and to conform more
clearly to established PHY paradigms such as the QMP combo PHY.
Looking at existing compat strings I settled on
"qcom,x1e80100-mipi-csi2-combo-phy" deliberately omitting reference to the
fact the PHY is built on a four nano-meter process node, which seems to
match recent submissions to QMP PHY.
My first pass at this driver included support for the old two phase
devices:
Link: https://git.codelinaro.org/bryan.odonoghue/kernel/-/commit/a504c28d109296c93470340cfe7281231f573bcb#b6e59ed7db94c9da22e492bb03fcda6a4300983c
I realised that the device tree schema changes required to support a
comprehensive conversion of all CAMSS to this driver would be an
almost certainly be unacceptable ABI break or at the very least an enormous
amount of work and verification so I instead aimed to support just one new
SoC in the submission.
I've retained the callback indirections give us scope to add in another type of
future PHY including potentially adding in the 2PH later on.
This driver is tested and working on x1e/Hamoa and has been tested as not
breaking sc8280xp/Makena and sm8250/Kona.
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
Bryan O'Donoghue (2):
dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
.../bindings/phy/qcom,x1e80100-csi2-phy.yaml | 195 +++++++++
MAINTAINERS | 10 +
drivers/phy/qualcomm/Kconfig | 14 +
drivers/phy/qualcomm/Makefile | 5 +
drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c | 386 ++++++++++++++++++
drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c | 446 +++++++++++++++++++++
drivers/phy/qualcomm/phy-qcom-mipi-csi2.h | 98 +++++
7 files changed, 1154 insertions(+)
---
base-commit: 8dac27bfa2f994ecb11f01a63641527d17d48fc1
change-id: 20250710-x1e-csi2-phy-f6434b651d3a
Best regards,
--
Bryan O'Donoghue <bryan.odonoghue@linaro.org>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v5 1/3] dt-bindings: phy: qcom: Add Glymur QMP PCIe multiple link-mode PHY
From: Krzysztof Kozlowski @ 2026-07-19 6:50 UTC (permalink / raw)
To: Qiang Yu
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Manivannan Sadhasivam, Philipp Zabel,
Bjorn Andersson, Konrad Dybcio, linux-arm-msm, linux-phy,
devicetree, linux-kernel
In-Reply-To: <20260717-glymur_linkmode_0717-v5-1-4f9e87a61463@oss.qualcomm.com>
On Fri, Jul 17, 2026 at 02:58:33AM -0700, Qiang Yu wrote:
> diff --git a/Documentation/devicetree/bindings/phy/qcom,glymur-qmp-pcie-multiphy.yaml b/Documentation/devicetree/bindings/phy/qcom,glymur-qmp-pcie-multiphy.yaml
> new file mode 100644
> index 000000000000..73550f77e9b6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/qcom,glymur-qmp-pcie-multiphy.yaml
Filename: qcom,glymur-qmp-gen5x8-pcie-phy.yaml
> @@ -0,0 +1,176 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/phy/qcom,glymur-qmp-pcie-multiphy.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm QMP PCIe multiple link-mode PHY controller (PCIe, Glymur)
> +
> +maintainers:
> + - Qiang Yu <qiang.yu@oss.qualcomm.com>
> + - Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> +
> +description: |
> + The Glymur SoC uses a single PCIe Gen5 PHY hardware block for the
> + PCIe3a/PCIe3b controllers. This block supports two link modes, selected
> + at runtime via a TCSR syscon register:
> +
> + 1. x8 - a single 8-lane PHY instance is exposed (PCIe3a only)
> + 2. x4+x4 - two independent 4-lane PHY instances are exposed (PCIe3a and
> + PCIe3b)
> +
> + The node always describes both PHY instances ("port a" and "port b"),
> + regardless of which link mode is active on the board.
> +
> +properties:
> + compatible:
> + enum:
> + - qcom,glymur-qmp-gen5x8-pcie-phy
> +
> + reg:
> + minItems: 2
Dop
> + maxItems: 2
> +
> + reg-names:
> + items:
> + - const: port_a
> + - const: port_b
> +
> + clocks:
> + minItems: 10
Drop
> + maxItems: 10
> +
> + clock-names:
> + items:
> + - const: aux
> + - const: cfg_ahb
> + - const: ref
> + - const: rchng
> + - const: pipe
> + - const: phy_b_aux
> + - const: cfg_ahb_b
> + - const: rchng_b
> + - const: pipe_b
> + - const: pipediv2_b
> +
> + power-domains:
> + minItems: 2
Drop
> + maxItems: 2
> +
> + power-domain-names:
> + items:
> + - const: phy_a_gdsc
port_a
> + - const: phy_b_gdsc
port_b
> +
> + resets:
> + minItems: 4
Drop
> + maxItems: 4
> +
> + reset-names:
> + items:
> + - const: phy_a
port_a
> + - const: phy_a_nocsr
port_a_nocsr
etc.
> + - const: phy_b
> + - const: phy_b_nocsr
> +
> + vdda-phy-supply: true
> +
> + vdda-pll-supply: true
> +
> + vdda-refgen0p9-supply: true
> +
> + vdda-refgen1p2-supply: true
> +
> + qcom,link-mode:
> + description:
> + Reference to a register in the TCSR syscon that reports the link
> + mode the PCIe PHY is currently configured for, either a single x8
> + link or two independent x4 links. The link mode is programmed by
> + firmware before Linux boots; this property is only used to read
> + the active link mode, specified as a phandle to the syscon and
> + the register offset.
> + $ref: /schemas/types.yaml#/definitions/phandle-array
> + items:
> + - items:
> + - description: phandle of TCSR syscon
> + - description: offset of link mode register
> +
> + "#clock-cells":
> + const: 1
> +
> + clock-output-names:
> + items:
> + - description: Name of the PHY A pipe clock output.
> + - description: Name of the PHY B pipe clock output.
> +
> + "#phy-cells":
> + const: 1
You should explain in description the meaning of cell value. Or provide
filename of header with constants.
> +
> +required:
> + - compatible
> + - reg
> + - reg-names
> + - clocks
> + - clock-names
> + - power-domains
> + - power-domain-names
> + - resets
> + - reset-names
> + - vdda-phy-supply
> + - vdda-pll-supply
> + - vdda-refgen0p9-supply
> + - vdda-refgen1p2-supply
> + - qcom,link-mode
> + - "#clock-cells"
> + - clock-output-names
> + - "#phy-cells"
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/qcom,glymur-gcc.h>
> + #include <dt-bindings/clock/qcom,glymur-tcsr.h>
> +
> + pcie3_phy: phy@f00000 {
Drop unused label.
The old 5x4 glymur compatible could be deprecated as well, but fine for
me without doing so.
Best regards,
Krzysztof
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v5 0/3] Add QMP PCIe multiple link-mode PHY support
From: Krzysztof Kozlowski @ 2026-07-19 6:43 UTC (permalink / raw)
To: Qiang Yu
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Manivannan Sadhasivam, Philipp Zabel,
Bjorn Andersson, Konrad Dybcio, linux-arm-msm, linux-phy,
devicetree, linux-kernel
In-Reply-To: <20260717-glymur_linkmode_0717-v5-0-4f9e87a61463@oss.qualcomm.com>
On Fri, Jul 17, 2026 at 02:58:32AM -0700, Qiang Yu wrote:
> Hi all,
>
> Some QMP PCIe PHY hardware blocks can be configured through a link-mode
> register into different link topologies, such as a single wide link, or
> multiple independent narrower links (e.g. x8 or x4+x4 mode on the Glymur
> PCIe3 PHY).
>
> Earlier revisions tried to extend the existing single-instance
> phy-qcom-qmp-pcie.c driver to cover this hardware. That added a large
> amount of conditional, multi-PHY logic to a driver whose data model
> assumes one PHY per node.
>
> This series adds a dedicated PHY provider driver,
> phy-qcom-qmp-pcie-multiphy.c:
>
> A single PHY provider node describes the shared hardware block. A new
> "qcom,link-mode" property points at the TCSR syscon register that reports
> the active topology; the driver reads it once at probe. #phy-cells = <1>
> lets consumers pass a logical PHY index to obtain their sub-PHY. Match
> data is indexed by link mode, and each link mode has its own array of
> per-PHY config tables, so one shared provider exposes a different set of
> logical PHYs depending on the active mode. The driver inherits the PHY
> settings and link mode already programmed by UEFI, so only the no-CSR
> reset is used, and no PHY setting tables or related structures are
> defined. Each sub-PHY owns its register regions and power domain, so in a
> bifurcated mode the links are brought up, and powered independently.
>
> The driver is implemented and validated on Glymur, and is intended to
> be extensible to other multi-mode QMP PCIe PHYs.
>
> This series depends on a prerequisite patch by Krzysztof Kozlowski:
> https://lore.kernel.org/r/20260420133616.88740-2-krzysztof.kozlowski@oss.qualcomm.com
There was a v2, still not applied I think:
https://lore.kernel.org/all/20260609141608.354186-2-krzysztof.kozlowski@oss.qualcomm.com/
Best regards,
Krzysztof
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v10 1/2] dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
From: Krzysztof Kozlowski @ 2026-07-19 6:28 UTC (permalink / raw)
To: Bryan O'Donoghue
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong,
Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel
In-Reply-To: <20260718-x1e-csi2-phy-v10-1-5720a7888953@linaro.org>
On Sat, Jul 18, 2026 at 03:38:17AM +0100, Bryan O'Donoghue wrote:
> Add a base schema for the MIPI CSI2 PHYs on Qualcomm SoCs. This PHY
> supports both DPHY and CPHY operation. A special mode of DPHY operation -
> called variously split-mode or combo-mode also allows for two sensors to be
> connected to one PHY.
>
> The submitted binding here describes the DPHY modes of operation only. CPHY
> is left to future work.
>
> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
> ---
> .../bindings/phy/qcom,x1e80100-csi2-phy.yaml | 195 +++++++++++++++++++++
> 1 file changed, 195 insertions(+)
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: qcom: m31-eusb2: Fix return value of init call
From: sashiko-bot @ 2026-07-18 9:36 UTC (permalink / raw)
To: Krishna Kurapati; +Cc: vkoul, neil.armstrong, olteanv, linux-phy
In-Reply-To: <20260718-m31-eusb2-fix-v1-1-8588a1b94d76@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Hardware initialization failures during register configuration in `m31eusb2_phy_init` are ignored, leading to a silent failure.
--
--- Patch [1]: [PATCH] phy: qcom: m31-eusb2: Fix return value of init call ---
commit 65f666fe30cbc54b37ee682f3064a24fcc24b16f
Author: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
phy: qcom: m31-eusb2: Fix return value of init call
The init call previously returned 0 even when repeater initialization or clock
enablement failed. This patch updates the failure path to return the actual
error value.
> diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
> index 0bec8657149c9..275df38256de3 100644
> --- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
> +++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
[ ... ]
> m31eusb2_phy_write_sequence(phy, data->setup_seq, data->setup_seq_nregs);
> m31eusb2_phy_write_readback(phy->base,
> USB_PHY_HS_PHY_CTRL_COMMON0, FSEL,
> FIELD_PREP(FSEL, data->fsel));
> m31eusb2_phy_write_sequence(phy, data->override_seq, data->override_seq_nregs);
> m31eusb2_phy_write_sequence(phy, data->reset_seq, data->reset_seq_nregs);
>
> return 0;
[Severity: High]
This is a pre-existing issue, but since this patch fixes error handling in
m31eusb2_phy_init(), should we also check the return values of these register
configuration functions?
If m31eusb2_phy_write_sequence() or m31eusb2_phy_write_readback() fails, the
error is currently ignored. The function successfully returns 0 without
executing the disable_repeater and disable_vreg cleanup paths. This could leave
the PHY hardware in an incorrectly initialized state.
>
> disable_repeater:
> phy_exit(phy->repeater);
> disable_vreg:
> regulator_bulk_disable(M31_EUSB_NUM_VREGS, phy->vregs);
>
> - return 0;
> + return ret;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260718-m31-eusb2-fix-v1-1-8588a1b94d76@oss.qualcomm.com?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH] phy: qcom: m31-eusb2: Fix return value of init call
From: Krishna Kurapati @ 2026-07-18 9:31 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Wesley Cheng, Melody Olvera,
Dmitry Baryshkov
Cc: linux-arm-msm, linux-phy, linux-kernel, Krishna Kurapati
The init call currently returns success irrespective of any failures
during repeater init or clock enablement. Return appropriate error value
in the init call failure path.
Fixes: 9c8504861cc4 ("phy: qcom: Add M31 based eUSB2 PHY driver")
Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
---
drivers/phy/qualcomm/phy-qcom-m31-eusb2.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
index 0bec8657149c..275df38256de 100644
--- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
+++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
@@ -221,7 +221,7 @@ static int m31eusb2_phy_init(struct phy *uphy)
disable_vreg:
regulator_bulk_disable(M31_EUSB_NUM_VREGS, phy->vregs);
- return 0;
+ return ret;
}
static int m31eusb2_phy_exit(struct phy *uphy)
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260718-m31-eusb2-fix-666cf8c5a72c
Best regards,
--
Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v2 1/2] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY TX probe
From: sashiko-bot @ 2026-07-18 9:11 UTC (permalink / raw)
To: Can Peng; +Cc: neil.armstrong, vkoul, olteanv, linux-phy
In-Reply-To: <20260718090054.444513-2-pengcan@kylinos.cn>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
Pre-existing issues:
- [High] Use-After-Free (UAF) of `dphy` when the PHY provider is unbound but a consumer is still active.
- [Medium] The runtime PM fix is incomplete as it misses the identical leak in the companion RX driver.
- [Medium] Resource leak of `txesc_clk` on the error path of `stf_dphy_init()`.
- [Medium] Incomplete hardware teardown and clock leak in `stf_dphy_exit()` upon reset failure.
--
--- Patch [1]: [PATCH v2 1/2] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY TX probe ---
commit a6c202e493e39186dc1b4cf599b709a5e76356a2
Author: Can Peng <pengcan@kylinos.cn>
Subject: phy: starfive: Fix runtime PM cleanup in JH7110 DPHY TX probe
This commit changes pm_runtime_enable() to devm_pm_runtime_enable() in the
JH7110 DPHY TX driver's probe function. This ensures that runtime PM is
properly disabled on probe failures or driver unbind.
> diff --git a/drivers/phy/starfive/phy-jh7110-dphy-tx.c b/drivers/phy/starfive/phy-jh7110-dphy-tx.c
> index c64d1c91b1307..181491a938079 100644
> --- a/drivers/phy/starfive/phy-jh7110-dphy-tx.c
> +++ b/drivers/phy/starfive/phy-jh7110-dphy-tx.c
> @@ -392,6 +392,7 @@ static int stf_dphy_probe(struct platform_device *pdev)
> {
> struct phy_provider *phy_provider;
> struct stf_dphy *dphy;
> + int ret;
>
> dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
[Severity: High]
This is a pre-existing issue, but is it safe to tie the lifetime of dphy to
the provider device using devm_kzalloc?
If the provider is unbound via sysfs, devres will automatically free dphy.
However, a consumer might still be bound due to a stateless device link, and
if it calls phy_power_off(), it triggers stf_dphy_power_off() which could
dereference the freed dphy->dev:
stf_dphy_power_off() {
...
struct stf_dphy *dphy = phy_get_drvdata(phy);
return pm_runtime_put_sync(dphy->dev);
}
> if (!dphy)
> return -ENOMEM;
[ ... ]
> - pm_runtime_enable(&pdev->dev);
> + ret = devm_pm_runtime_enable(&pdev->dev);
[Severity: Medium]
This isn't a bug introduced by this patch, but does the companion RX driver
in drivers/phy/starfive/phy-jh7110-dphy-rx.c also need this same fix?
It appears to have the exact same pattern where pm_runtime_enable() is not
managed by devres and is not reverted on a subsequent provider registration
error or driver unbind:
stf_dphy_probe() {
...
pm_runtime_enable(&pdev->dev);
phy_set_drvdata(dphy->phy, dphy);
phy_provider = devm_of_phy_provider_register(&pdev->dev,
of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
> + if (ret)
> + return ret;
>
> dphy->txesc_clk = devm_clk_get(&pdev->dev, "txesc");
[Severity: Medium]
This is a pre-existing issue, but does the error path in stf_dphy_init() leak
the txesc_clk clock prepare and enable counts?
If reset_control_deassert() fails, it returns without unwinding the previous
clk_prepare_enable():
stf_dphy_init() {
...
ret = reset_control_deassert(dphy->sys_rst);
if (ret) {
dev_err(dphy->dev, "Failed to deassert sys_rst\n");
return ret;
}
...
}
[Severity: Medium]
This is a pre-existing issue, but does stf_dphy_exit() abandon the remaining
hardware teardown sequence if reset_control_assert() fails?
The early return skips calling clk_disable_unprepare() and
stf_dphy_hw_reset():
stf_dphy_exit() {
...
ret = reset_control_assert(dphy->sys_rst);
if (ret) {
dev_err(dphy->dev, "Failed to assert sys_rst\n");
return ret;
}
clk_disable_unprepare(dphy->txesc_clk);
...
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260718090054.444513-1-pengcan@kylinos.cn?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 2/2] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY RX probe
From: sashiko-bot @ 2026-07-18 9:10 UTC (permalink / raw)
To: Can Peng; +Cc: olteanv, neil.armstrong, vkoul, linux-phy
In-Reply-To: <20260718090054.444513-3-pengcan@kylinos.cn>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] The phy_configure callback accesses hardware registers while the device is still in reset and without ensuring the PM domain is active.
- [High] The driver acquires and configures clocks but never enables them.
--
--- Patch [2]: [PATCH v2 2/2] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY RX probe ---
commit fe8a50ded4b6e0393fd6d6a27d22735266427a91
Author: Can Peng <pengcan@kylinos.cn>
phy: starfive: Fix runtime PM cleanup in JH7110 DPHY RX probe
stf_dphy_probe() enables runtime PM before registering the PHY provider.
If devm_of_phy_provider_register() fails, probe returns with runtime PM
still enabled. This commit uses devm_pm_runtime_enable() to ensure
runtime PM is disabled automatically on later probe failures or unbind.
> diff --git a/drivers/phy/starfive/phy-jh7110-dphy-rx.c b/drivers/phy/starfive/phy-jh7110-dphy-rx.c
> index 0b039e1f71c55..d06f21ad63325 100644
> --- a/drivers/phy/starfive/phy-jh7110-dphy-rx.c
> +++ b/drivers/phy/starfive/phy-jh7110-dphy-rx.c
> @@ -150,6 +150,7 @@ static int stf_dphy_probe(struct platform_device *pdev)
> {
> struct phy_provider *phy_provider;
> struct stf_dphy *dphy;
> + int ret;
>
> dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
> if (!dphy)
[ ... ]
> @@ -190,7 +191,9 @@ static int stf_dphy_probe(struct platform_device *pdev)
> return PTR_ERR(dphy->phy);
> }
>
> - pm_runtime_enable(&pdev->dev);
> + ret = devm_pm_runtime_enable(&pdev->dev);
> + if (ret)
> + return ret;
>
> phy_set_drvdata(dphy->phy, dphy);
> phy_provider = devm_of_phy_provider_register(&pdev->dev,
[Severity: High]
This isn't a bug introduced by this patch, but while reviewing the PM
changes, I noticed an issue in stf_dphy_configure().
The phy_configure callback accesses hardware registers directly:
stf_dphy_configure() {
...
writel(FIELD_PREP(STF_DPHY_ENABLE_CLK, 1) |
FIELD_PREP(STF_DPHY_ENABLE_CLK1, 1) |
...
}
However, the PM domain is resumed and the reset is deasserted later in
stf_dphy_power_on().
Because the RX driver lacks a phy_init() callback, if a consumer (like
a MIPI CSI-2/DSI controller) calls phy_configure() prior to phy_power_on(),
will this cause a synchronous external abort or silent write failure by
accessing registers of a device that is still held in reset and unpowered?
[Severity: High]
This is also a pre-existing issue, but I noticed a potential problem in
stf_dphy_power_on() regarding clock enablement.
The driver acquires cfg_clk, ref_clk, and tx_clk in probe() and sets their
rates in stf_dphy_power_on():
stf_dphy_power_on() {
...
clk_set_rate(dphy->cfg_clk, 99000000);
clk_set_rate(dphy->ref_clk, 49500000);
clk_set_rate(dphy->tx_clk, 19800000);
...
}
Are these clocks ever actually enabled? I do not see any corresponding
clk_prepare_enable() calls in the driver. Will the PHY hardware fail to
operate or hang on register access if the clocks are not implicitly kept on
by the bootloader or a PM domain?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260718090054.444513-1-pengcan@kylinos.cn?part=2
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v2 2/2] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY RX probe
From: Can Peng @ 2026-07-18 9:00 UTC (permalink / raw)
To: jack.zhu, changhuang.liang, keith.zhao, vkoul, neil.armstrong
Cc: linux-phy, linux-kernel, Can Peng, stable
In-Reply-To: <20260718090054.444513-1-pengcan@kylinos.cn>
stf_dphy_probe() enables runtime PM before registering the PHY provider.
If devm_of_phy_provider_register() fails, probe returns with runtime PM
still enabled.
The driver also has no remove callback, so runtime PM is left enabled
on driver unbind after a successful probe.
Use devm_pm_runtime_enable() so runtime PM is disabled automatically
on later probe failures and on driver unbind.
Fixes: f8aa660841bc ("phy: starfive: Add mipi dphy rx support")
Cc: stable@vger.kernel.org
Signed-off-by: Can Peng <pengcan@kylinos.cn>
---
drivers/phy/starfive/phy-jh7110-dphy-rx.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/starfive/phy-jh7110-dphy-rx.c b/drivers/phy/starfive/phy-jh7110-dphy-rx.c
index 0b039e1f71c5..d06f21ad6332 100644
--- a/drivers/phy/starfive/phy-jh7110-dphy-rx.c
+++ b/drivers/phy/starfive/phy-jh7110-dphy-rx.c
@@ -150,6 +150,7 @@ static int stf_dphy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct stf_dphy *dphy;
+ int ret;
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
if (!dphy)
@@ -190,7 +191,9 @@ static int stf_dphy_probe(struct platform_device *pdev)
return PTR_ERR(dphy->phy);
}
- pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
phy_set_drvdata(dphy->phy, dphy);
phy_provider = devm_of_phy_provider_register(&pdev->dev,
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 1/2] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY TX probe
From: Can Peng @ 2026-07-18 9:00 UTC (permalink / raw)
To: jack.zhu, changhuang.liang, keith.zhao, vkoul, neil.armstrong
Cc: linux-phy, linux-kernel, Can Peng, stable
In-Reply-To: <20260718090054.444513-1-pengcan@kylinos.cn>
stf_dphy_probe() enables runtime PM before getting the clock and
reset controls, creating the PHY and registering the PHY provider. If
any of those steps fails, probe returns with runtime PM still enabled.
The driver also has no remove callback, so runtime PM is left enabled
on driver unbind after a successful probe.
Use devm_pm_runtime_enable() so runtime PM is disabled automatically
on later probe failures and on driver unbind.
Fixes: d3ab79553308 ("phy: starfive: Add mipi dphy tx support")
Cc: stable@vger.kernel.org
Signed-off-by: Can Peng <pengcan@kylinos.cn>
---
drivers/phy/starfive/phy-jh7110-dphy-tx.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/starfive/phy-jh7110-dphy-tx.c b/drivers/phy/starfive/phy-jh7110-dphy-tx.c
index c64d1c91b130..181491a93807 100644
--- a/drivers/phy/starfive/phy-jh7110-dphy-tx.c
+++ b/drivers/phy/starfive/phy-jh7110-dphy-tx.c
@@ -392,6 +392,7 @@ static int stf_dphy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct stf_dphy *dphy;
+ int ret;
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
if (!dphy)
@@ -406,7 +407,9 @@ static int stf_dphy_probe(struct platform_device *pdev)
if (IS_ERR(dphy->topsys))
return PTR_ERR(dphy->topsys);
- pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
dphy->txesc_clk = devm_clk_get(&pdev->dev, "txesc");
if (IS_ERR(dphy->txesc_clk))
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v2 0/2] phy: starfive: Fix JH7110 DPHY runtime PM cleanup
From: Can Peng @ 2026-07-18 9:00 UTC (permalink / raw)
To: jack.zhu, changhuang.liang, keith.zhao, vkoul, neil.armstrong
Cc: linux-phy, linux-kernel, Can Peng
The JH7110 DPHY TX and RX drivers both enable runtime PM during probe
without arranging for it to be disabled on later probe failures or on
driver unbind.
Use devm_pm_runtime_enable() in both drivers so the runtime PM state is
cleaned up automatically when probe fails after runtime PM has been
enabled and when the driver is unbound.
Changes in v2:
- Turn the single TX fix into a two-patch series.
- Add the matching runtime PM cleanup fix for the RX driver.
Can Peng (2):
phy: starfive: Fix runtime PM cleanup in JH7110 DPHY TX probe
phy: starfive: Fix runtime PM cleanup in JH7110 DPHY RX probe
drivers/phy/starfive/phy-jh7110-dphy-rx.c | 5 ++++-
drivers/phy/starfive/phy-jh7110-dphy-tx.c | 5 ++++-
2 files changed, 8 insertions(+), 2 deletions(-)
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v7 01/13] dt-bindings: phy: Add zx297520v3 USB phy documentation
From: Stefan Dösinger @ 2026-07-18 7:50 UTC (permalink / raw)
To: Vinod Koul
Cc: Krzysztof Kozlowski, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Brian Masney,
Neil Armstrong, Russell King, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, linux-phy
In-Reply-To: <20260717-uncovered-feathered-groundhog-eab16e@quoll>
[-- Attachment #1.1: Type: text/plain, Size: 1097 bytes --]
Am Freitag, 17. Juli 2026, 09:32:18 Ostafrikanische Zeit schrieb Krzysztof
Kozlowski:
> On Fri, Jul 17, 2026 at 12:35:37AM +0300, Stefan Dösinger wrote:
> > + resets:
> > + items:
> > + - description: USB phy reset
> > + - description: HSIC phy reset
>
> Why do you reference here resets from the parent? That's a clear signal
> you have one device, so one device node regardless of Linux phy core
> behavior.
This is a follow-up question for the PHY maintainers, if they notice the E-
Mail thread: I'm trying to resolve an apparent conflict between DT
expectations and a check in the PHY core:
device_set_of_node_from_dev(dev, dev->parent);
...
devm_of_phy_provider_register(dev, xlate);
Passes the letter of the check in __of_phy_provider_register(), but I am not
sure about the spirit.
"dev" here is a MFD child node without a DT node in accordance to DT's
writing-bindings instructions ("DON’T create nodes just for the sake of
instantiating drivers"). The PHY core prohibits a child to register a phy
provider to its parent node. Why?
[-- Attachment #1.2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 870 bytes --]
[-- Attachment #2: Type: text/plain, Size: 112 bytes --]
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: freescale: fsl-samsung-hdmi: Fix runtime PM cleanup
From: sashiko-bot @ 2026-07-18 7:30 UTC (permalink / raw)
To: Can Peng; +Cc: olteanv, linux-phy, vkoul, neil.armstrong
In-Reply-To: <20260718071816.437047-1-pengcan@kylinos.cn>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] Double-disable of `apbclk` on driver unbind causing a clock enable count underflow.
- [Medium] Hardware registers are accessed in `clk_set_rate` while the device may be runtime suspended, causing a bus error.
- [Medium] Clock enable count leak when hardware configuration fails during runtime resume.
--
--- Patch [1]: [PATCH] phy: freescale: fsl-samsung-hdmi: Fix runtime PM cleanup ---
commit c64d89c2a04d8191edbe1e45c553f7627c043e56
Author: Can Peng <pengcan@kylinos.cn>
phy: freescale: fsl-samsung-hdmi: Fix runtime PM cleanup
fsl_samsung_hdmi_phy_probe() takes a runtime PM reference, marks the
device active and enables runtime PM before registering the PHY clock.
If phy_clk_register() fails, probe returns without dropping the runtime
PM reference or disabling runtime PM.
Fixes: 6ad082bee902 ("phy: freescale: add Samsung HDMI PHY")
Cc: stable@vger.kernel.org
> diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
> index d010fec15671d..e7af4f9c7b20b 100644
> --- a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
> +++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
> @@ -665,20 +665,26 @@ static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
> "failed to get ref clk\n");
>
> pm_runtime_get_noresume(phy->dev);
> - pm_runtime_set_active(phy->dev);
> - pm_runtime_enable(phy->dev);
> + ret = pm_runtime_set_active(phy->dev);
> + if (ret)
> + goto pm_put_noidle;
[Severity: High]
This isn't a bug introduced by this patch, but doesn't this driver double-disable
apbclk on unbind if the device is runtime suspended?
In fsl_samsung_hdmi_phy_probe(), apbclk is acquired via devm_clk_get_enabled(),
which registers a devres action to disable it on teardown:
phy->apbclk = devm_clk_get_enabled(phy->dev, "apb");
However, the driver explicitly disables this clock during runtime suspend.
When the driver is unbound, fsl_samsung_hdmi_phy_remove() completes without
calling pm_runtime_get_sync() to resume the device.
If the device was already in runtime suspend, the clock is disabled, and then
devres executes and disables it a second time, causing a clock enable count
underflow warning.
Could the remove callback be updated to ensure the device is resumed?
> +
> + ret = devm_pm_runtime_enable(phy->dev);
> + if (ret)
> + goto pm_put_noidle;
>
> ret = phy_clk_register(phy);
> if (ret) {
> dev_err(&pdev->dev, "register clk failed\n");
> - goto register_clk_failed;
> + goto pm_put_noidle;
> }
>
> pm_runtime_put(phy->dev);
>
> return 0;
>
> -register_clk_failed:
> +pm_put_noidle:
> + pm_runtime_put_noidle(phy->dev);
> return ret;
> }
[Severity: Medium]
This isn't a bug introduced by this patch, but can the clock framework trigger
a bus error by accessing hardware registers while the device is runtime suspended?
The clock framework calls fsl_samsung_hdmi_phy_clk_set_rate(), which calls
fsl_samsung_hdmi_phy_configure():
drivers/phy/freescale/phy-fsl-samsung-hdmi.c:fsl_samsung_hdmi_phy_configure() {
...
writeb(REG33_FIX_DA, phy->regs + PHY_REG(33));
...
}
The driver does not set the CLK_SET_RATE_GATE flag, so a consumer can set the
rate while the clock is unprepared.
Since there are no PM runtime calls in the clock operations, the device might
be runtime suspended (and apbclk disabled) when this happens, causing a
synchronous external abort when accessing PHY registers.
Should the clock operations ensure the device is resumed before accessing registers?
[Severity: Medium]
This is a pre-existing issue, but does fsl_samsung_hdmi_phy_resume() leak the
clock enable count if hardware configuration fails?
During fsl_samsung_hdmi_phy_resume(), clk_prepare_enable() is called on apbclk.
If the subsequent call to fsl_samsung_hdmi_phy_configure() fails, it returns
the error without disabling the clock:
drivers/phy/freescale/phy-fsl-samsung-hdmi.c:fsl_samsung_hdmi_phy_resume() {
...
ret = clk_prepare_enable(phy->apbclk);
...
ret = fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
return ret;
}
Should this error path unwind the clock enable?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260718071816.437047-1-pengcan@kylinos.cn?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY TX probe
From: sashiko-bot @ 2026-07-18 7:25 UTC (permalink / raw)
To: Can Peng; +Cc: vkoul, olteanv, neil.armstrong, linux-phy
In-Reply-To: <20260718071733.436927-1-pengcan@kylinos.cn>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] The patch fixes a runtime PM cleanup leak in the DPHY TX driver by replacing `pm_runtime_enable()` with `devm_pm_runtime_enable()`, but completely misses the exact same bug in the companion DPHY RX driver (`phy-jh7110-dphy-rx.c`).
- [Medium] Resource leaks in `stf_dphy_init()` and `stf_dphy_exit()` error paths where clocks are not correctly unwound or cleaned up.
--
--- Patch [1]: [PATCH] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY TX probe ---
commit d91037f438cbeda1f5f7dbdb182d938b6883ff9c
Author: Can Peng <pengcan@kylinos.cn>
phy: starfive: Fix runtime PM cleanup in JH7110 DPHY TX probe
stf_dphy_probe() enables runtime PM before getting the clock and
reset controls, creating the PHY and registering the PHY provider. If
any of those steps fails, probe returns with runtime PM still enabled.
Use devm_pm_runtime_enable() so runtime PM is disabled automatically
on later probe failures and on driver unbind.
> diff --git a/drivers/phy/starfive/phy-jh7110-dphy-tx.c b/drivers/phy/starfive/phy-jh7110-dphy-tx.c
> index c64d1c91b1307..181491a938079 100644
> --- a/drivers/phy/starfive/phy-jh7110-dphy-tx.c
> +++ b/drivers/phy/starfive/phy-jh7110-dphy-tx.c
> @@ -392,6 +392,7 @@ static int stf_dphy_probe(struct platform_device *pdev)
> {
> struct phy_provider *phy_provider;
> struct stf_dphy *dphy;
> + int ret;
>
> dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
> if (!dphy)
> @@ -406,7 +407,9 @@ static int stf_dphy_probe(struct platform_device *pdev)
> if (IS_ERR(dphy->topsys))
> return PTR_ERR(dphy->topsys);
>
> - pm_runtime_enable(&pdev->dev);
> + ret = devm_pm_runtime_enable(&pdev->dev);
> + if (ret)
> + return ret;
[Severity: High]
This isn't a bug introduced by this patch, but the companion DPHY RX driver
(drivers/phy/starfive/phy-jh7110-dphy-rx.c) has the exact same bug in
stf_dphy_probe():
pm_runtime_enable(&pdev->dev);
phy_set_drvdata(dphy->phy, dphy);
phy_provider = devm_of_phy_provider_register(&pdev->dev,
of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
If probe fails or the driver unbinds, pm_runtime_disable() is missing,
leading to unbalanced PM usage counts. Should a similar fix be applied there?
[Severity: Medium]
This is a pre-existing issue, but does stf_dphy_init() leak the prepared
clock if reset_control_deassert() fails?
stf_dphy_init() in drivers/phy/starfive/phy-jh7110-dphy-tx.c has:
ret = clk_prepare_enable(dphy->txesc_clk);
if (ret) {
dev_err(dphy->dev, "Failed to prepare/enable txesc_clk\n");
return ret;
}
ret = reset_control_deassert(dphy->sys_rst);
if (ret) {
dev_err(dphy->dev, "Failed to deassert sys_rst\n");
return ret;
}
If reset_control_deassert() fails, the function returns without calling
clk_disable_unprepare(dphy->txesc_clk).
Similarly, in stf_dphy_exit(), if reset_control_assert() fails, it returns
early and skips clk_disable_unprepare():
ret = reset_control_assert(dphy->sys_rst);
if (ret) {
dev_err(dphy->dev, "Failed to assert sys_rst\n");
return ret;
}
clk_disable_unprepare(dphy->txesc_clk);
Could these failure paths lock the clock in an enabled state?
>
> dphy->txesc_clk = devm_clk_get(&pdev->dev, "txesc");
> if (IS_ERR(dphy->txesc_clk))
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260718071733.436927-1-pengcan@kylinos.cn?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH] phy: freescale: fsl-samsung-hdmi: Fix runtime PM cleanup
From: Can Peng @ 2026-07-18 7:18 UTC (permalink / raw)
To: vkoul, neil.armstrong; +Cc: linux-phy, linux-kernel, Can Peng, stable
fsl_samsung_hdmi_phy_probe() takes a runtime PM reference, marks the
device active and enables runtime PM before registering the PHY clock.
If phy_clk_register() fails, probe returns without dropping the runtime
PM reference or disabling runtime PM.
The remove callback only unregisters the clock provider, so runtime PM
is also left enabled after a successful probe followed by driver unbind.
Check pm_runtime_set_active(), use devm_pm_runtime_enable() so runtime
PM is disabled automatically, and drop the initial noresume reference
on PM setup and clock registration failures.
Fixes: 6ad082bee902 ("phy: freescale: add Samsung HDMI PHY")
Cc: stable@vger.kernel.org
Signed-off-by: Can Peng <pengcan@kylinos.cn>
---
drivers/phy/freescale/phy-fsl-samsung-hdmi.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
index d010fec15671..e7af4f9c7b20 100644
--- a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
+++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
@@ -665,20 +665,26 @@ static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
"failed to get ref clk\n");
pm_runtime_get_noresume(phy->dev);
- pm_runtime_set_active(phy->dev);
- pm_runtime_enable(phy->dev);
+ ret = pm_runtime_set_active(phy->dev);
+ if (ret)
+ goto pm_put_noidle;
+
+ ret = devm_pm_runtime_enable(phy->dev);
+ if (ret)
+ goto pm_put_noidle;
ret = phy_clk_register(phy);
if (ret) {
dev_err(&pdev->dev, "register clk failed\n");
- goto register_clk_failed;
+ goto pm_put_noidle;
}
pm_runtime_put(phy->dev);
return 0;
-register_clk_failed:
+pm_put_noidle:
+ pm_runtime_put_noidle(phy->dev);
return ret;
}
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY TX probe
From: Can Peng @ 2026-07-18 7:17 UTC (permalink / raw)
To: keith.zhao, vkoul, neil.armstrong
Cc: linux-phy, linux-kernel, Can Peng, stable
stf_dphy_probe() enables runtime PM before getting the clock and
reset controls, creating the PHY and registering the PHY provider. If
any of those steps fails, probe returns with runtime PM still enabled.
The driver also has no remove callback, so runtime PM is left enabled
on driver unbind after a successful probe.
Use devm_pm_runtime_enable() so runtime PM is disabled automatically
on later probe failures and on driver unbind.
Fixes: d3ab79553308 ("phy: starfive: Add mipi dphy tx support")
Cc: stable@vger.kernel.org
Signed-off-by: Can Peng <pengcan@kylinos.cn>
---
drivers/phy/starfive/phy-jh7110-dphy-tx.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/starfive/phy-jh7110-dphy-tx.c b/drivers/phy/starfive/phy-jh7110-dphy-tx.c
index c64d1c91b130..181491a93807 100644
--- a/drivers/phy/starfive/phy-jh7110-dphy-tx.c
+++ b/drivers/phy/starfive/phy-jh7110-dphy-tx.c
@@ -392,6 +392,7 @@ static int stf_dphy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct stf_dphy *dphy;
+ int ret;
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
if (!dphy)
@@ -406,7 +407,9 @@ static int stf_dphy_probe(struct platform_device *pdev)
if (IS_ERR(dphy->topsys))
return PTR_ERR(dphy->topsys);
- pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
dphy->txesc_clk = devm_clk_get(&pdev->dev, "txesc");
if (IS_ERR(dphy->txesc_clk))
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v10 2/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: Bryan O'Donoghue @ 2026-07-18 3:33 UTC (permalink / raw)
To: sashiko-reviews
Cc: neil.armstrong, linux-phy, olteanv, devicetree, robh, conor+dt,
media-ci, vkoul
In-Reply-To: <20260718025109.EED601F000E9@smtp.kernel.org>
On 18/07/2026 03:51, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [High] Incorrect translation of logical to physical lanes causes register offset errors and bitwise truncation.
> - [High] Integer truncation in division leads to potential division-by-zero panic in settle count calculation.
> - [Low] Hardware version register read does not mask upper bits, potentially corrupting the version struct.
> --
>
> --- Patch [2]: [PATCH v10 2/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver ---
> commit 3a4ef513bbeefe5185b0f08d343f98883cbe66ca
> Author: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
>
> phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
>
> This commit adds a new MIPI CSI2 driver in DPHY mode initially. It supports
> devices like "qcom,x1e80100-csi2-phy" and imports CAMSS CSI PHY init sequences.
>
>> diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
>> index 0000000000000..5ac0d041b36b7
>> --- /dev/null
>> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
>
> [ ... ]
>
>> +static void phy_qcom_mipi_csi2_hw_version_read(struct mipi_csi2phy_device *csi2phy)
>> +{
>> + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
>> + u32 tmp;
>> +
>> + writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID, csi2phy->base +
>> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 6));
>> +
>> + tmp = readl_relaxed(csi2phy->base +
>> + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 12));
>> + csi2phy->hw_version = tmp;
>
> [Severity: Low]
> Is it possible for the hardware register to return garbage or reserved bits in
> the upper bytes here? Since this first read is assigned directly without
> masking (e.g., tmp & 0xFF), any upper garbage bits would remain in hw_version
> and wouldn't be cleared by the subsequent bitwise OR operations.
This is not credible.
>> +
>> + tmp = readl_relaxed(csi2phy->base +
>> + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 13));
>> + csi2phy->hw_version |= (tmp << 8) & 0xFF00;
>
> [ ... ]
>
>> +static u8 phy_qcom_mipi_csi2_settle_cnt_calc(s64 link_freq, u32 timer_clk_rate)
>> +{
>> + u32 t_hs_prepare_max_ps;
>> + u32 timer_period_ps;
>> + u32 t_hs_settle_ps;
>> + u8 settle_cnt;
>> + u32 ui_ps;
>> +
>> + if (link_freq <= 0)
>> + return 0;
>> +
>> + ui_ps = div_u64(PSEC_PER_SEC, link_freq);
>
> [Severity: High]
> Does this code silently truncate link_freq?
>
> The div_u64() macro expects a u32 divisor, but link_freq is an s64. This will
> implicitly truncate the 64-bit link_freq value to 32 bits.
>
> If link_freq happens to be an exact multiple of 2^32, the truncated divisor
> becomes exactly 0, which would cause a division-by-zero panic. Frequencies
> exceeding 4.29 GHz would also be silently corrupted.
>
> Could this be updated to use div64_u64() or similar to avoid truncation?
This is a pretty outlandish level of defensive coding. Nope, not doing it.
>
>> + ui_ps /= 2;
>> + t_hs_prepare_max_ps = 85000 + 6 * ui_ps;
>
> [ ... ]
>
>> +static int phy_qcom_mipi_csi2_lanes_enable(struct mipi_csi2phy_device *csi2phy,
>> + struct mipi_csi2phy_stream_cfg *cfg)
>> +{
>> + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
>> + struct mipi_csi2phy_lanes_cfg *lane_cfg = &cfg->lane_cfg;
>> + u8 settle_cnt;
>> + u8 val;
>> + int i;
>> +
>> + settle_cnt = phy_qcom_mipi_csi2_settle_cnt_calc(cfg->link_freq, csi2phy->timer_clk_rate);
>> + if (!settle_cnt)
>> + return -EINVAL;
>> +
>> + /*
>> + * CSI_COMMON_CTRL5 is a physical lane power-up bitmap:
>> + * - Bits [0,2,4,6] → D-PHY data lanes(LN0, LN2, LN4, LN6)
>> + * - Bits [1,3,5] → C-PHY trio lanes(LN1, LN3, LN5)
>> + * - Bit [7] → D-PHY clock lane(LNCK) dedicated clock enable
>> + */
>> + val = BIT(lane_cfg->clk.pos);
>> + for (i = 0; i < cfg->num_data_lanes; i++)
>> + val |= BIT(lane_cfg->data[i].pos * 2);
>
> [Severity: High]
> Can this bitmask overflow the u8 val?
>
> If the device tree uses physical indices (e.g., 4 or 6) for pos, pos * 2
> becomes 8 or 12. Since val is a u8, using BIT(8) or BIT(12) will overflow
> and silently truncate to 0, failing to enable the corresponding hardware lane.
>
Actually no - the current yaml has a range of data-lanes = <0 1 2 3> =>
3 * 2 == 6
>> +
>> + writel(val, csi2phy->base +
>> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
>> +
>> + /* Lane configuration for polarity @ CSIPHY-base + CTRL9 */
>> + for (i = 0; i < cfg->num_data_lanes; i++) {
>> + if (lane_cfg->data[i].pol) {
>> + u8 pos = lane_cfg->data[i].pos;
>> +
>> + writel(BIT(2), csi2phy->base + CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(pos));
>
> [Severity: High]
> Does this correctly map logical to physical lanes?
>
> The CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(pos) macro computes 0x200 * pos + 0x24.
> However, D-PHY data lane blocks are spaced at offsets 0, 0x400, 0x800, and
> 0xC00.
>
> If pos is a logical index like 1, this would access offset 0x200 (which is a
> C-PHY lane) instead of the intended 0x400.
True.
Baby don't be sad, cause 1/4 ain't bad.
>
>> + }
>> + }
>> +
>> + if (lane_cfg->clk.pol)
>> + writel(BIT(2), csi2phy->base + CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(lane_cfg->clk.pos));
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v10 2/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: sashiko-bot @ 2026-07-18 2:51 UTC (permalink / raw)
To: Bryan O'Donoghue
Cc: neil.armstrong, linux-phy, olteanv, devicetree, robh, conor+dt,
media-ci, vkoul
In-Reply-To: <20260718-x1e-csi2-phy-v10-2-5720a7888953@linaro.org>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Incorrect translation of logical to physical lanes causes register offset errors and bitwise truncation.
- [High] Integer truncation in division leads to potential division-by-zero panic in settle count calculation.
- [Low] Hardware version register read does not mask upper bits, potentially corrupting the version struct.
--
--- Patch [2]: [PATCH v10 2/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver ---
commit 3a4ef513bbeefe5185b0f08d343f98883cbe66ca
Author: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
This commit adds a new MIPI CSI2 driver in DPHY mode initially. It supports
devices like "qcom,x1e80100-csi2-phy" and imports CAMSS CSI PHY init sequences.
> diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
> index 0000000000000..5ac0d041b36b7
> --- /dev/null
> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
[ ... ]
> +static void phy_qcom_mipi_csi2_hw_version_read(struct mipi_csi2phy_device *csi2phy)
> +{
> + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
> + u32 tmp;
> +
> + writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 6));
> +
> + tmp = readl_relaxed(csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 12));
> + csi2phy->hw_version = tmp;
[Severity: Low]
Is it possible for the hardware register to return garbage or reserved bits in
the upper bytes here? Since this first read is assigned directly without
masking (e.g., tmp & 0xFF), any upper garbage bits would remain in hw_version
and wouldn't be cleared by the subsequent bitwise OR operations.
> +
> + tmp = readl_relaxed(csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 13));
> + csi2phy->hw_version |= (tmp << 8) & 0xFF00;
[ ... ]
> +static u8 phy_qcom_mipi_csi2_settle_cnt_calc(s64 link_freq, u32 timer_clk_rate)
> +{
> + u32 t_hs_prepare_max_ps;
> + u32 timer_period_ps;
> + u32 t_hs_settle_ps;
> + u8 settle_cnt;
> + u32 ui_ps;
> +
> + if (link_freq <= 0)
> + return 0;
> +
> + ui_ps = div_u64(PSEC_PER_SEC, link_freq);
[Severity: High]
Does this code silently truncate link_freq?
The div_u64() macro expects a u32 divisor, but link_freq is an s64. This will
implicitly truncate the 64-bit link_freq value to 32 bits.
If link_freq happens to be an exact multiple of 2^32, the truncated divisor
becomes exactly 0, which would cause a division-by-zero panic. Frequencies
exceeding 4.29 GHz would also be silently corrupted.
Could this be updated to use div64_u64() or similar to avoid truncation?
> + ui_ps /= 2;
> + t_hs_prepare_max_ps = 85000 + 6 * ui_ps;
[ ... ]
> +static int phy_qcom_mipi_csi2_lanes_enable(struct mipi_csi2phy_device *csi2phy,
> + struct mipi_csi2phy_stream_cfg *cfg)
> +{
> + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
> + struct mipi_csi2phy_lanes_cfg *lane_cfg = &cfg->lane_cfg;
> + u8 settle_cnt;
> + u8 val;
> + int i;
> +
> + settle_cnt = phy_qcom_mipi_csi2_settle_cnt_calc(cfg->link_freq, csi2phy->timer_clk_rate);
> + if (!settle_cnt)
> + return -EINVAL;
> +
> + /*
> + * CSI_COMMON_CTRL5 is a physical lane power-up bitmap:
> + * - Bits [0,2,4,6] → D-PHY data lanes(LN0, LN2, LN4, LN6)
> + * - Bits [1,3,5] → C-PHY trio lanes(LN1, LN3, LN5)
> + * - Bit [7] → D-PHY clock lane(LNCK) dedicated clock enable
> + */
> + val = BIT(lane_cfg->clk.pos);
> + for (i = 0; i < cfg->num_data_lanes; i++)
> + val |= BIT(lane_cfg->data[i].pos * 2);
[Severity: High]
Can this bitmask overflow the u8 val?
If the device tree uses physical indices (e.g., 4 or 6) for pos, pos * 2
becomes 8 or 12. Since val is a u8, using BIT(8) or BIT(12) will overflow
and silently truncate to 0, failing to enable the corresponding hardware lane.
> +
> + writel(val, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
> +
> + /* Lane configuration for polarity @ CSIPHY-base + CTRL9 */
> + for (i = 0; i < cfg->num_data_lanes; i++) {
> + if (lane_cfg->data[i].pol) {
> + u8 pos = lane_cfg->data[i].pos;
> +
> + writel(BIT(2), csi2phy->base + CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(pos));
[Severity: High]
Does this correctly map logical to physical lanes?
The CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(pos) macro computes 0x200 * pos + 0x24.
However, D-PHY data lane blocks are spaced at offsets 0, 0x400, 0x800, and
0xC00.
If pos is a logical index like 1, this would access offset 0x200 (which is a
C-PHY lane) instead of the intended 0x400.
> + }
> + }
> +
> + if (lane_cfg->clk.pol)
> + writel(BIT(2), csi2phy->base + CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(lane_cfg->clk.pos));
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260718-x1e-csi2-phy-v10-0-5720a7888953@linaro.org?part=2
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v10 2/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: Bryan O'Donoghue @ 2026-07-18 2:38 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel,
Bryan O'Donoghue
In-Reply-To: <20260718-x1e-csi2-phy-v10-0-5720a7888953@linaro.org>
Add a new MIPI CSI2 driver in DPHY mode initially. The entire set of
existing CAMSS CSI PHY init sequences are imported in order to save time
and effort in later patches.
The following devices are supported in this drop:
"qcom,x1e80100-csi2-phy"
In-line with other PHY drivers the process node is included in the name.
Data-lane and clock lane positioning and polarity selection via newly
amended struct phy_configure_opts_mipi_dphy{} is supported.
The Qualcomm 3PH class of PHYs can do both DPHY and CPHY mode. For now only
DPHY is supported.
In porting some of the logic over from camss-csiphy*.c to here its also
possible to rationalise some of the code.
In particular use of regulator_bulk and clk_bulk as well as dropping the
seemingly useless and unused interrupt handler.
The PHY sequences and a lot of the logic that goes with them are well
proven in CAMSS and mature so the main thing to watch out for here is how
to get the right sequencing of regulators, clocks and register-writes.
The register init sequence table is imported verbatim from the existing
CAMSS csiphy driver. A follow-up series will rework the table to extract
the repetitive per-lane pattern into a loop.
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
MAINTAINERS | 10 +
drivers/phy/qualcomm/Kconfig | 14 +
drivers/phy/qualcomm/Makefile | 5 +
drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c | 386 ++++++++++++++++++
drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c | 443 +++++++++++++++++++++
drivers/phy/qualcomm/phy-qcom-mipi-csi2.h | 98 +++++
6 files changed, 956 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a99..a203b41475ea4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22296,6 +22296,16 @@ S: Maintained
F: Documentation/devicetree/bindings/media/qcom,*-iris.yaml
F: drivers/media/platform/qcom/iris/
+QUALCOMM MIPI CSI2 PHY DRIVER
+M: Bryan O'Donoghue <bod@kernel.org>
+L: linux-phy@lists.infradead.org
+L: linux-media@vger.kernel.org
+L: linux-arm-msm@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/phy/qcom,*-csi2-phy.yaml
+F: drivers/phy/qualcomm/phy-qcom-mipi-csi2*.c
+F: drivers/phy/qualcomm/phy-qcom-mipi-csi2*.h
+
QUALCOMM NAND CONTROLLER DRIVER
M: Manivannan Sadhasivam <mani@kernel.org>
L: linux-mtd@lists.infradead.org
diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig
index 60a0ead127fa9..779a3511ba852 100644
--- a/drivers/phy/qualcomm/Kconfig
+++ b/drivers/phy/qualcomm/Kconfig
@@ -28,6 +28,20 @@ config PHY_QCOM_EDP
Enable this driver to support the Qualcomm eDP PHY found in various
Qualcomm chipsets.
+config PHY_QCOM_MIPI_CSI2
+ tristate "Qualcomm MIPI CSI2 PHY driver"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on OF
+ depends on PM
+ depends on COMMON_CLK
+ select GENERIC_PHY
+ select GENERIC_PHY_MIPI_DPHY
+ help
+ Enable this to support the MIPI CSI2 PHY driver found in various
+ Qualcomm chipsets. This PHY is used to connect MIPI CSI2
+ camera sensors to the CSI Decoder in the Qualcomm Camera Subsystem
+ CAMSS.
+
config PHY_QCOM_IPQ4019_USB
tristate "Qualcomm IPQ4019 USB PHY driver"
depends on OF && (ARCH_QCOM || COMPILE_TEST)
diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile
index b71a6a0bed3f1..382cb594b06b6 100644
--- a/drivers/phy/qualcomm/Makefile
+++ b/drivers/phy/qualcomm/Makefile
@@ -6,6 +6,11 @@ obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
obj-$(CONFIG_PHY_QCOM_M31_USB) += phy-qcom-m31.o
obj-$(CONFIG_PHY_QCOM_M31_EUSB) += phy-qcom-m31-eusb2.o
+
+phy-qcom-mipi-csi2-objs += phy-qcom-mipi-csi2-core.o \
+ phy-qcom-mipi-csi2-3ph-dphy.o
+obj-$(CONFIG_PHY_QCOM_MIPI_CSI2) += phy-qcom-mipi-csi2.o
+
obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o
obj-$(CONFIG_PHY_QCOM_QMP_COMBO) += phy-qcom-qmp-combo.o phy-qcom-qmp-usbc.o
diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
new file mode 100644
index 0000000000000..5ac0d041b36b7
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Qualcomm MSM Camera Subsystem - CSIPHY Module 3phase v1.0
+ *
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2016-2026 Linaro Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/time64.h>
+
+#include "phy-qcom-mipi-csi2.h"
+
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(offset, n) ((offset) + 0x4 * (n))
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL0_PHY_SW_RESET BIT(0)
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE BIT(7)
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0)
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1)
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL10_IRQ_CLEAR_CMD BIT(0)
+#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(offset, n) ((offset) + 0xb0 + 0x4 * (n))
+
+#define CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(n) ((0x200 * (n)) + 0x24)
+
+/*
+ * 3 phase CSI has 19 common status regs with only 0-10 being used
+ * and 11-18 being reserved.
+ */
+#define CSI_COMMON_STATUS_NUM 11
+/*
+ * There are a number of common control registers
+ * The offset to clear the CSIPHY IRQ status starts @ 22
+ * So to clear CSI_COMMON_STATUS0 this is CSI_COMMON_CONTROL22, STATUS1 is
+ * CONTROL23 and so on
+ */
+#define CSI_CTRL_STATUS_INDEX 22
+
+/*
+ * There are 43 COMMON_CTRL registers with regs after # 33 being reserved
+ */
+#define CSI_CTRL_MAX 33
+
+#define CSIPHY_DEFAULT_PARAMS 0
+#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2
+#define CSIPHY_SKEW_CAL 7
+
+/* 4nm 2PH v 2.1.2 2p5Gbps 4 lane DPHY mode */
+static const struct
+mipi_csi2phy_lane_regs lane_regs_x1e80100[] = {
+ /* Power up lanes 2ph mode */
+ {.reg_addr = 0x101c, .reg_data = 0x7a, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x1018, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+
+ {.reg_addr = 0x0094, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x00a0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0090, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0098, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0094, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0030, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0000, .reg_data = 0x8e, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0038, .reg_data = 0xfe, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x002c, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0034, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x001c, .reg_data = 0x0a, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0014, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x003c, .reg_data = 0xb8, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0004, .reg_data = 0x0c, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0020, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0008, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {.reg_addr = 0x0010, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0094, .reg_data = 0xd7, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x005c, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0060, .reg_data = 0xbd, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0064, .reg_data = 0x7f, .param_type = CSIPHY_SKEW_CAL},
+
+ {.reg_addr = 0x0e94, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0ea0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e90, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e98, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e94, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e30, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e28, .reg_data = 0x04, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e00, .reg_data = 0x80, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e0c, .reg_data = 0xff, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e38, .reg_data = 0x1f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e2c, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e34, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e1c, .reg_data = 0x0a, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e14, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e3c, .reg_data = 0xb8, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e04, .reg_data = 0x0c, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e20, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0e08, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {.reg_addr = 0x0e10, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
+
+ {.reg_addr = 0x0494, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x04a0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0490, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0498, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0494, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0430, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0400, .reg_data = 0x8e, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0438, .reg_data = 0xfe, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x042c, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0434, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x041c, .reg_data = 0x0a, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0414, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x043c, .reg_data = 0xb8, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0404, .reg_data = 0x0c, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0420, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0408, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {.reg_addr = 0x0410, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0494, .reg_data = 0xd7, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x045c, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0460, .reg_data = 0xbd, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0464, .reg_data = 0x7f, .param_type = CSIPHY_SKEW_CAL},
+
+ {.reg_addr = 0x0894, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x08a0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0890, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0898, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0894, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0830, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0800, .reg_data = 0x8e, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0838, .reg_data = 0xfe, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x082c, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0834, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x081c, .reg_data = 0x0a, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0814, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x083c, .reg_data = 0xb8, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0804, .reg_data = 0x0c, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0820, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0808, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {.reg_addr = 0x0810, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0894, .reg_data = 0xd7, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x085c, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0860, .reg_data = 0xbd, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0864, .reg_data = 0x7f, .param_type = CSIPHY_SKEW_CAL},
+
+ {.reg_addr = 0x0c94, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0ca0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c90, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c98, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c94, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c30, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c00, .reg_data = 0x8e, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c38, .reg_data = 0xfe, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c2c, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c34, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c1c, .reg_data = 0x0a, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c14, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c3c, .reg_data = 0xb8, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c04, .reg_data = 0x0c, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c20, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c08, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {.reg_addr = 0x0c10, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0c94, .reg_data = 0xd7, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0c5c, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0c60, .reg_data = 0xbd, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0c64, .reg_data = 0x7f, .param_type = CSIPHY_SKEW_CAL},
+};
+
+static inline const struct mipi_csi2phy_device_regs *
+csi2phy_dev_to_regs(struct mipi_csi2phy_device *csi2phy)
+{
+ return &csi2phy->soc_cfg->reg_info;
+}
+
+static void phy_qcom_mipi_csi2_hw_version_read(struct mipi_csi2phy_device *csi2phy)
+{
+ const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
+ u32 tmp;
+
+ writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 6));
+
+ tmp = readl_relaxed(csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 12));
+ csi2phy->hw_version = tmp;
+
+ tmp = readl_relaxed(csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 13));
+ csi2phy->hw_version |= (tmp << 8) & 0xFF00;
+
+ tmp = readl_relaxed(csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 14));
+ csi2phy->hw_version |= (tmp << 16) & 0xFF0000;
+
+ tmp = readl_relaxed(csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 15));
+ csi2phy->hw_version |= (tmp << 24) & 0xFF000000;
+
+ dev_dbg_once(csi2phy->dev, "CSIPHY 3PH HW Version = 0x%08x\n", csi2phy->hw_version);
+}
+
+/*
+ * phy_qcom_mipi_csi2_reset - Perform software reset on CSIPHY module
+ * @phy_qcom_mipi_csi2: CSIPHY device
+ */
+static void phy_qcom_mipi_csi2_reset(struct mipi_csi2phy_device *csi2phy)
+{
+ const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
+
+ writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL0_PHY_SW_RESET,
+ csi2phy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 0));
+ usleep_range(5000, 8000);
+ writel(0x0, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 0));
+}
+
+/*
+ * phy_qcom_mipi_csi2_settle_cnt_calc - Calculate settle count value
+ *
+ * Helper function to calculate settle count value. This is
+ * based on the CSI2 T_hs_settle parameter which in turn
+ * is calculated based on the CSI2 transmitter link frequency.
+ *
+ * Return settle count value or 0 if the CSI2 link frequency
+ * is not available
+ */
+static u8 phy_qcom_mipi_csi2_settle_cnt_calc(s64 link_freq, u32 timer_clk_rate)
+{
+ u32 t_hs_prepare_max_ps;
+ u32 timer_period_ps;
+ u32 t_hs_settle_ps;
+ u8 settle_cnt;
+ u32 ui_ps;
+
+ if (link_freq <= 0)
+ return 0;
+
+ ui_ps = div_u64(PSEC_PER_SEC, link_freq);
+ ui_ps /= 2;
+ t_hs_prepare_max_ps = 85000 + 6 * ui_ps;
+ t_hs_settle_ps = t_hs_prepare_max_ps;
+
+ timer_period_ps = div_u64(PSEC_PER_SEC, timer_clk_rate);
+
+ if ((t_hs_settle_ps / timer_period_ps) < 6)
+ return 0;
+
+ settle_cnt = t_hs_settle_ps / timer_period_ps - 6;
+
+ return settle_cnt;
+}
+
+static void
+phy_qcom_mipi_csi2_gen2_config_lanes(struct mipi_csi2phy_device *csi2phy,
+ u8 settle_cnt)
+{
+ const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
+ const struct mipi_csi2phy_lane_regs *r = regs->init_seq;
+ int i, array_size = regs->lane_array_size;
+ u32 val;
+
+ for (i = 0; i < array_size; i++, r++) {
+ switch (r->param_type) {
+ case CSIPHY_SETTLE_CNT_LOWER_BYTE:
+ val = settle_cnt & 0xff;
+ break;
+ case CSIPHY_SKEW_CAL:
+ /* TODO: support application of skew from dt flag */
+ continue;
+ default:
+ val = r->reg_data;
+ break;
+ }
+ writel(val, csi2phy->base + r->reg_addr);
+ if (r->delay_us)
+ udelay(r->delay_us);
+ }
+}
+
+static int phy_qcom_mipi_csi2_lanes_enable(struct mipi_csi2phy_device *csi2phy,
+ struct mipi_csi2phy_stream_cfg *cfg)
+{
+ const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
+ struct mipi_csi2phy_lanes_cfg *lane_cfg = &cfg->lane_cfg;
+ u8 settle_cnt;
+ u8 val;
+ int i;
+
+ settle_cnt = phy_qcom_mipi_csi2_settle_cnt_calc(cfg->link_freq, csi2phy->timer_clk_rate);
+ if (!settle_cnt)
+ return -EINVAL;
+
+ /*
+ * CSI_COMMON_CTRL5 is a physical lane power-up bitmap:
+ * - Bits [0,2,4,6] → D-PHY data lanes(LN0, LN2, LN4, LN6)
+ * - Bits [1,3,5] → C-PHY trio lanes(LN1, LN3, LN5)
+ * - Bit [7] → D-PHY clock lane(LNCK) dedicated clock enable
+ */
+ val = BIT(lane_cfg->clk.pos);
+ for (i = 0; i < cfg->num_data_lanes; i++)
+ val |= BIT(lane_cfg->data[i].pos * 2);
+
+ writel(val, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
+
+ /* Lane configuration for polarity @ CSIPHY-base + CTRL9 */
+ for (i = 0; i < cfg->num_data_lanes; i++) {
+ if (lane_cfg->data[i].pol) {
+ u8 pos = lane_cfg->data[i].pos;
+
+ writel(BIT(2), csi2phy->base + CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(pos));
+ }
+ }
+
+ if (lane_cfg->clk.pol)
+ writel(BIT(2), csi2phy->base + CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(lane_cfg->clk.pos));
+
+ val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B;
+ writel(val, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 6));
+
+ val = 0x02;
+ writel(val, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 7));
+
+ val = 0x00;
+ writel(val, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 0));
+
+ phy_qcom_mipi_csi2_gen2_config_lanes(csi2phy, settle_cnt);
+
+ /* IRQ_MASK registers - disable all interrupts */
+ for (i = CSI_COMMON_STATUS_NUM; i < CSI_CTRL_STATUS_INDEX; i++) {
+ writel(0, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, i));
+ }
+
+ return 0;
+}
+
+static void
+phy_qcom_mipi_csi2_lanes_disable(struct mipi_csi2phy_device *csi2phy,
+ struct mipi_csi2phy_stream_cfg *cfg)
+{
+ const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
+
+ writel(0, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
+
+ writel(0, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 6));
+}
+
+static const struct mipi_csi2phy_hw_ops phy_qcom_mipi_csi2_ops_3ph_1_0 = {
+ .hw_version_read = phy_qcom_mipi_csi2_hw_version_read,
+ .reset = phy_qcom_mipi_csi2_reset,
+ .lanes_enable = phy_qcom_mipi_csi2_lanes_enable,
+ .lanes_disable = phy_qcom_mipi_csi2_lanes_disable,
+};
+
+static const char * const x1e_clks[] = {
+ "core",
+ "timer",
+ "ahb"
+};
+
+static const char * const x1e_supplies[] = {
+ "vdda-0p9",
+ "vdda-1p2"
+};
+
+static struct mipi_csi2_genpd x1e_genpds[] = {
+ { .name = "top", .scaled = false },
+ { .name = "mmcx", .scaled = true },
+ { .name = "mx", .scaled = true },
+};
+
+const struct mipi_csi2phy_soc_cfg mipi_csi2_dphy_4nm_x1e = {
+ .ops = &phy_qcom_mipi_csi2_ops_3ph_1_0,
+ .reg_info = {
+ .init_seq = lane_regs_x1e80100,
+ .lane_array_size = ARRAY_SIZE(lane_regs_x1e80100),
+ .common_regs_offset = 0x1000,
+ },
+ .supply_names = (const char **)x1e_supplies,
+ .num_supplies = ARRAY_SIZE(x1e_supplies),
+ .clk_names = (const char **)x1e_clks,
+ .num_clk = ARRAY_SIZE(x1e_clks),
+ .genpds = x1e_genpds,
+ .num_genpds = ARRAY_SIZE(x1e_genpds),
+};
diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
new file mode 100644
index 0000000000000..8dc8c9f4f2cf0
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026, Linaro Ltd.
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_opp.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include "phy-qcom-mipi-csi2.h"
+
+static int
+phy_qcom_mipi_csi2_set_clock_rates(struct mipi_csi2phy_device *csi2phy,
+ s64 link_freq)
+{
+ struct device *dev = csi2phy->dev;
+ unsigned long opp_rate = link_freq / 4;
+ struct dev_pm_opp *opp;
+ long timer_rate;
+ int i, pstate;
+ int ret;
+
+ opp = dev_pm_opp_find_freq_ceil(dev, &opp_rate);
+ if (IS_ERR(opp)) {
+ dev_err(csi2phy->dev, "Couldn't find ceiling for %lld Hz\n",
+ link_freq);
+ return PTR_ERR(opp);
+ }
+
+ pstate = 0;
+ for (i = 0; i < csi2phy->pd_list->num_pds; i++) {
+ unsigned int perf;
+
+ if (!csi2phy->soc_cfg->genpds[i].scaled)
+ continue;
+
+ perf = dev_pm_opp_get_required_pstate(opp, pstate);
+ pstate += 1;
+
+ ret = dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], perf);
+ if (ret) {
+ dev_err(csi2phy->dev, "Couldn't set perf state %u\n",
+ perf);
+ dev_pm_opp_put(opp);
+ goto unset_pstate;
+ }
+ }
+ dev_pm_opp_put(opp);
+
+ ret = dev_pm_opp_set_rate(dev, opp_rate);
+ if (ret) {
+ dev_err(csi2phy->dev, "dev_pm_opp_set_rate() fail\n");
+ goto unset_opp_rate;
+ }
+
+ timer_rate = clk_round_rate(csi2phy->timer_clk, link_freq / 4);
+ if (timer_rate <= 0) {
+ ret = -ENODEV;
+ goto unset_opp_rate;
+ }
+
+ ret = clk_set_rate(csi2phy->timer_clk, timer_rate);
+ if (ret)
+ goto unset_opp_rate;
+
+ csi2phy->timer_clk_rate = timer_rate;
+
+ return 0;
+
+unset_opp_rate:
+ dev_pm_opp_set_rate(dev, 0);
+
+unset_pstate:
+ while (i--) {
+ if (!csi2phy->soc_cfg->genpds[i].scaled)
+ continue;
+
+ dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], 0);
+ }
+
+ return ret;
+}
+
+static int phy_qcom_mipi_csi2_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy);
+ struct phy_configure_opts_mipi_dphy *dphy_cfg = &opts->mipi_dphy;
+ struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg;
+ int ret;
+
+ ret = phy_mipi_dphy_config_validate(dphy_cfg);
+ if (ret)
+ return ret;
+
+ if (dphy_cfg->lanes < 1 || dphy_cfg->lanes > CSI2_MAX_DATA_LANES)
+ return -EINVAL;
+
+ stream_cfg->link_freq = dphy_cfg->hs_clk_rate;
+
+ return 0;
+}
+
+static int phy_qcom_mipi_csi2_power_on(struct phy *phy)
+{
+ struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy);
+ const struct mipi_csi2phy_hw_ops *ops = csi2phy->soc_cfg->ops;
+ int i, ret;
+
+ ret = regulator_bulk_enable(csi2phy->soc_cfg->num_supplies,
+ csi2phy->supplies);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(csi2phy->dev);
+ if (ret < 0)
+ goto disable_regulators;
+
+ ret = phy_qcom_mipi_csi2_set_clock_rates(csi2phy, csi2phy->stream_cfg.link_freq);
+ if (ret)
+ goto poweroff_phy;
+
+ ret = clk_bulk_prepare_enable(csi2phy->soc_cfg->num_clk,
+ csi2phy->clks);
+ if (ret) {
+ dev_err(csi2phy->dev, "failed to enable clocks, %d\n", ret);
+ goto unset_rate;
+ }
+
+ ops->reset(csi2phy);
+
+ ops->hw_version_read(csi2phy);
+
+ ret = ops->lanes_enable(csi2phy, &csi2phy->stream_cfg);
+ if (ret)
+ goto unset_clocks;
+
+ return 0;
+
+unset_clocks:
+ clk_bulk_disable_unprepare(csi2phy->soc_cfg->num_clk,
+ csi2phy->clks);
+
+unset_rate:
+ dev_pm_opp_set_rate(csi2phy->dev, 0);
+
+ for (i = 0; i < csi2phy->pd_list->num_pds; i++) {
+ if (!csi2phy->soc_cfg->genpds[i].scaled)
+ continue;
+
+ dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], 0);
+ }
+
+poweroff_phy:
+ pm_runtime_put_sync(csi2phy->dev);
+
+disable_regulators:
+ regulator_bulk_disable(csi2phy->soc_cfg->num_supplies,
+ csi2phy->supplies);
+
+ return ret;
+}
+
+static int phy_qcom_mipi_csi2_power_off(struct phy *phy)
+{
+ struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy);
+ const struct mipi_csi2phy_hw_ops *ops = csi2phy->soc_cfg->ops;
+ int i;
+
+ ops->lanes_disable(csi2phy, &csi2phy->stream_cfg);
+
+ clk_bulk_disable_unprepare(csi2phy->soc_cfg->num_clk,
+ csi2phy->clks);
+
+ dev_pm_opp_set_rate(csi2phy->dev, 0);
+
+ for (i = 0; i < csi2phy->pd_list->num_pds; i++) {
+ if (!csi2phy->soc_cfg->genpds[i].scaled)
+ continue;
+
+ dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], 0);
+ }
+
+ pm_runtime_put_sync(csi2phy->dev);
+
+ regulator_bulk_disable(csi2phy->soc_cfg->num_supplies,
+ csi2phy->supplies);
+
+ return 0;
+}
+
+static const struct phy_ops phy_qcom_mipi_csi2_ops = {
+ .configure = phy_qcom_mipi_csi2_configure,
+ .power_on = phy_qcom_mipi_csi2_power_on,
+ .power_off = phy_qcom_mipi_csi2_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct phy *qcom_csi2_phy_xlate(struct device *dev,
+ const struct of_phandle_args *args)
+{
+ struct mipi_csi2phy_device *csi2phy = dev_get_drvdata(dev);
+
+ if (args->args_count < 1 || args->args[0] != PHY_TYPE_DPHY) {
+ dev_err(csi2phy->dev, "invalid phy mode in DTB\n");
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+
+ csi2phy->phy_mode = args->args[0];
+
+ return csi2phy->phy;
+}
+
+static int phy_qcom_mipi_csi2_attach_pm_domains(struct mipi_csi2phy_device *csi2phy)
+{
+ struct dev_pm_domain_attach_data pd_data = { 0 };
+ const char **pd_names;
+ int i;
+
+ pd_names = devm_kzalloc(csi2phy->dev,
+ sizeof(char *) * csi2phy->soc_cfg->num_genpds,
+ GFP_KERNEL);
+ if (!pd_names)
+ return -ENOMEM;
+
+ for (i = 0; i < csi2phy->soc_cfg->num_genpds; i++)
+ pd_names[i] = csi2phy->soc_cfg->genpds[i].name;
+
+ pd_data.pd_names = pd_names;
+ pd_data.num_pd_names = csi2phy->soc_cfg->num_genpds;
+
+ return devm_pm_domain_attach_list(csi2phy->dev, &pd_data,
+ &csi2phy->pd_list);
+}
+
+static int phy_qcom_mipi_csi2_parse_routing(struct mipi_csi2phy_device *csi2phy)
+{
+ struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg;
+ u32 lane_polarities[CSI2_MAX_DATA_LANES + 1];
+ u32 data_lanes[CSI2_MAX_DATA_LANES];
+ struct device *dev = csi2phy->dev;
+ struct fwnode_handle *ep;
+ int num_polarities;
+ int num_data_lanes;
+ int i, ret;
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 1, 0);
+ if (ep) {
+ fwnode_handle_put(ep);
+ dev_err(dev, "DPHY split mode is not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
+ if (!ep) {
+ dev_err(dev, "Missing port@0\n");
+ return -ENODEV;
+ }
+
+ num_data_lanes = fwnode_property_count_u32(ep, "data-lanes");
+ if (num_data_lanes < 1 || num_data_lanes > CSI2_MAX_DATA_LANES) {
+ ret = -EINVAL;
+ dev_err(dev, "Invalid data-lanes count: %d\n", num_data_lanes);
+ goto out_put;
+ }
+ stream_cfg->num_data_lanes = num_data_lanes;
+
+ ret = fwnode_property_read_u32_array(ep, "data-lanes", data_lanes,
+ stream_cfg->num_data_lanes);
+ if (ret) {
+ dev_err(dev, "Failed to read data-lanes: %d\n", ret);
+ goto out_put;
+ }
+
+ /* lane-polarities: optional, up to num_data_lanes + 1 entries */
+ memset(lane_polarities, 0x00, sizeof(lane_polarities));
+ num_polarities = fwnode_property_count_u32(ep, "lane-polarities");
+ if (num_polarities > 0) {
+ if (num_polarities != stream_cfg->num_data_lanes + 1) {
+ ret = -EINVAL;
+ dev_err(dev, "clock+data-lane %d/polarities %d mismatch\n",
+ stream_cfg->num_data_lanes + 1, num_polarities);
+ goto out_put;
+ }
+
+ ret = fwnode_property_read_u32_array(ep, "lane-polarities", lane_polarities,
+ num_polarities);
+ if (ret) {
+ dev_err(dev, "Failed to read lane-polarities: %d\n", ret);
+ goto out_put;
+ }
+ }
+
+ csi2phy->stream_cfg.lane_cfg.clk.pos = CSI2_DEFAULT_CLK_LANE;
+ csi2phy->stream_cfg.lane_cfg.clk.pol = lane_polarities[0];
+
+ for (i = 0; i < csi2phy->stream_cfg.num_data_lanes; i++) {
+ if (data_lanes[i] > CSI2_MAX_LANE_NUM) {
+ dev_err(dev, "Invalid lane %d\n", data_lanes[i]);
+ ret = -EINVAL;
+ goto out_put;
+ }
+ csi2phy->stream_cfg.lane_cfg.data[i].pos = data_lanes[i];
+ csi2phy->stream_cfg.lane_cfg.data[i].pol = lane_polarities[i + 1];
+ }
+
+ ret = 0;
+
+out_put:
+ fwnode_handle_put(ep);
+
+ return ret;
+}
+
+static int phy_qcom_mipi_csi2_probe(struct platform_device *pdev)
+{
+ unsigned int i, num_clk, num_supplies;
+ struct mipi_csi2phy_device *csi2phy;
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct phy *generic_phy;
+ int ret;
+
+ csi2phy = devm_kzalloc(dev, sizeof(*csi2phy), GFP_KERNEL);
+ if (!csi2phy)
+ return -ENOMEM;
+
+ csi2phy->dev = dev;
+ dev_set_drvdata(dev, csi2phy);
+
+ csi2phy->soc_cfg = device_get_match_data(&pdev->dev);
+
+ if (!csi2phy->soc_cfg)
+ return -EINVAL;
+
+ num_clk = csi2phy->soc_cfg->num_clk;
+
+ ret = phy_qcom_mipi_csi2_parse_routing(csi2phy);
+ if (ret)
+ return ret;
+
+ ret = phy_qcom_mipi_csi2_attach_pm_domains(csi2phy);
+ if (ret < 0 || csi2phy->pd_list == NULL) {
+ if (ret == 0)
+ ret = -ENODEV;
+ return dev_err_probe(dev, ret, "Failed to attach power-domain list\n");
+ }
+ devm_pm_runtime_enable(dev);
+
+ ret = devm_clk_bulk_get_all(dev, &csi2phy->clks);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+ if (num_clk != ret) {
+ return dev_err_probe(dev, -ENODEV, "clock count %d expected %d\n",
+ ret, num_clk);
+ }
+
+ for (i = 0; i < num_clk; i++) {
+ if (!strcmp(csi2phy->clks[i].id, "timer")) {
+ csi2phy->timer_clk = csi2phy->clks[i].clk;
+ break;
+ }
+ }
+ if (!csi2phy->timer_clk)
+ return dev_err_probe(dev, -ENODEV, "no timer clock\n");
+
+ ret = devm_pm_opp_set_clkname(dev, "core");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set opp clkname\n");
+
+ ret = devm_pm_opp_of_add_table(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "invalid OPP table in device tree\n");
+
+ num_supplies = csi2phy->soc_cfg->num_supplies;
+ csi2phy->supplies = devm_kzalloc(dev, sizeof(*csi2phy->supplies) * num_supplies,
+ GFP_KERNEL);
+ if (!csi2phy->supplies)
+ return -ENOMEM;
+
+ for (i = 0; i < num_supplies; i++)
+ csi2phy->supplies[i].supply = csi2phy->soc_cfg->supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, num_supplies, csi2phy->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get regulator supplies\n");
+
+ csi2phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(csi2phy->base))
+ return PTR_ERR(csi2phy->base);
+
+ generic_phy = devm_phy_create(dev, NULL, &phy_qcom_mipi_csi2_ops);
+ if (IS_ERR(generic_phy)) {
+ ret = PTR_ERR(generic_phy);
+ return dev_err_probe(dev, ret, "failed to create phy\n");
+ }
+ csi2phy->phy = generic_phy;
+
+ phy_set_drvdata(generic_phy, csi2phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, qcom_csi2_phy_xlate);
+ if (!IS_ERR(phy_provider))
+ dev_dbg(dev, "Registered MIPI CSI2 PHY device\n");
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_qcom_mipi_csi2_of_match_table[] = {
+ { .compatible = "qcom,x1e80100-csi2-phy", .data = &mipi_csi2_dphy_4nm_x1e },
+ { }
+};
+MODULE_DEVICE_TABLE(of, phy_qcom_mipi_csi2_of_match_table);
+
+static struct platform_driver phy_qcom_mipi_csi2_driver = {
+ .probe = phy_qcom_mipi_csi2_probe,
+ .driver = {
+ .name = "qcom-mipi-csi2-phy",
+ .of_match_table = phy_qcom_mipi_csi2_of_match_table,
+ },
+};
+
+module_platform_driver(phy_qcom_mipi_csi2_driver);
+
+MODULE_DESCRIPTION("Qualcomm MIPI CSI2 PHY driver");
+MODULE_AUTHOR("Bryan O'Donoghue <bod@kernel.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
new file mode 100644
index 0000000000000..17b8eb505b7bd
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *
+ * Qualcomm MIPI CSI2 CPHY/DPHY driver
+ *
+ * Copyright (C) 2025 Linaro Ltd.
+ */
+#ifndef __PHY_QCOM_MIPI_CSI2_H__
+#define __PHY_QCOM_MIPI_CSI2_H__
+
+#include <linux/phy/phy.h>
+
+#define CSI2_MAX_DATA_LANES 4
+#define CSI2_DEFAULT_CLK_LANE 7
+#define CSI2_MAX_LANE_NUM 7
+
+struct mipi_csi2phy_lane {
+ u8 pos;
+ u8 pol;
+};
+
+struct mipi_csi2phy_lanes_cfg {
+ struct mipi_csi2phy_lane data[CSI2_MAX_DATA_LANES];
+ struct mipi_csi2phy_lane clk;
+};
+
+struct mipi_csi2phy_stream_cfg {
+ s64 link_freq;
+ u8 num_data_lanes;
+ struct mipi_csi2phy_lanes_cfg lane_cfg;
+};
+
+struct mipi_csi2phy_device;
+
+struct mipi_csi2phy_hw_ops {
+ void (*hw_version_read)(struct mipi_csi2phy_device *csi2phy_dev);
+ void (*reset)(struct mipi_csi2phy_device *csi2phy_dev);
+ int (*lanes_enable)(struct mipi_csi2phy_device *csi2phy_dev,
+ struct mipi_csi2phy_stream_cfg *cfg);
+ void (*lanes_disable)(struct mipi_csi2phy_device *csi2phy_dev,
+ struct mipi_csi2phy_stream_cfg *cfg);
+};
+
+struct mipi_csi2phy_lane_regs {
+ const s32 reg_addr;
+ const s32 reg_data;
+ const u32 delay_us;
+ const u32 param_type;
+};
+
+struct mipi_csi2phy_device_regs {
+ const struct mipi_csi2phy_lane_regs *init_seq;
+ const int lane_array_size;
+ const u32 common_regs_offset;
+};
+
+struct mipi_csi2_genpd {
+ const char *name;
+ bool scaled;
+};
+
+struct mipi_csi2phy_soc_cfg {
+ const struct mipi_csi2phy_hw_ops *ops;
+ const struct mipi_csi2phy_device_regs reg_info;
+
+ const char ** const supply_names;
+ const unsigned int num_supplies;
+
+ const char ** const clk_names;
+ const unsigned int num_clk;
+
+ const struct mipi_csi2_genpd *genpds;
+ const unsigned int num_genpds;
+};
+
+struct mipi_csi2phy_device {
+ struct device *dev;
+ u8 phy_mode;
+
+ struct phy *phy;
+ void __iomem *base;
+
+ struct clk_bulk_data *clks;
+ struct clk *timer_clk;
+ u32 timer_clk_rate;
+
+ struct regulator_bulk_data *supplies;
+ struct dev_pm_domain_list *pd_list;
+
+ const struct mipi_csi2phy_soc_cfg *soc_cfg;
+ struct mipi_csi2phy_stream_cfg stream_cfg;
+
+ u32 hw_version;
+};
+
+extern const struct mipi_csi2phy_soc_cfg mipi_csi2_dphy_4nm_x1e;
+
+#endif /* __PHY_QCOM_MIPI_CSI2_H__ */
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v10 0/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: Bryan O'Donoghue @ 2026-07-18 2:38 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel,
Bryan O'Donoghue
This driver is very critical to several SoCs being merged and CPHY being
added to Qualcomm SoCs.
It would be very beneficial therefore to get this merged in this cycle. A
lot of other enablement code depends on it.
Vinod, Kishon what do you need to take this for 7.2 ?
Changes in v10:
- Rewords commit log of yaml schema to read more organically - Krzysztof
- title: Qualcomm X1E80100 SoC CSI2 PHY - Krzysztof
- opp-table: object - Krzysztof
- Example opp-table in PHY node - Krzysztof
- port@1 uses stock graph.yaml - Krzysztof
- Dropped redundant remote-endpoint declaration - Krzysztof
- vdda-0p9 - SoC pins are vdda-0p9 rail is l2c_0p8. Named as with pin - Vlad, Konrad
- Retaining #phy-cells = 1.
Rob gave a steer to have the consumer configure this via DT. Not the way
Vlad suggests but I'm sticking to Rob's guidance for now - Rob/Vlad.
- data-lanes - retained as per CAMSS existing.
Nihal suggested, I agreed and Loic added his assent too. Vlad disagrees
with this. I accept Nihal's suggestion though we should remain
consistent. - Nihal, Loic, Vlad.
- pd_data = {} - Sashiko, Wengmeng
- lanes_enable() failure unwinds - Sashiko
- power_off and error paths drop OPP rate before performance state - Sashiko
- probe returns -ENODEV when pd_list is NULL with ret == 0 - Sashiko
- Move to devm_clk_bulk_get_all(), timer clock taken from the bulk,
clk_names indirection removed from soc_cfg - Frank
- Drop static CSI_COMMON_CTRL5 (0x1014) table row - Nihal
- Link to v9: https://patch.msgid.link/20260708-x1e-csi2-phy-v9-0-0210b90c04cf@linaro.org
Changes in v9:
Key advantages of implementing this logic as drivers/phy:
a) Per-PHY power rails become declarable and enforceable on a per-PHY
basis.
b) With future adaptations of CAMSS routing the PHY output to something
other than the CSI Decoders becomes possible.
c) Standard framework contract. phy_configure(mipi_dphy opts) / power_on / of_xlate.
d) Introduction of CPHY in this driver will elaborate CPHY for Linux in
general as there is currently no mipi_cphy_opts structure.
- TITAN_TOP_GDSC retained. Further elaboration in below link. - Vlad
Link: https://lore.kernel.org/all/d5407ab1-1af7-4678-ae67-5cf30ce8fa4b@kernel.org/
- Consumer selects PHY mode - Rob Herring
Link: https://lore.kernel.org/linux-media/20250710230846.GA44483-robh@kernel.org/
- Incorporates two comments by Krzysztof I missed previously - krzk
- Changes 0p9 rail name to 0p8 - Konrad, Sashiko
- Uses port@0 and port@1 to denote input/output - Vladimir
- Label phy@ instead of csiphy@ in example - Sashiko
- Drops additional entries in example opp one is enough - bod
- return 0 on timer_period_ps < 6. - Sashiko
- Use power-domain names and a "scaled" bool to flag if
a power-domain needs to be scaled or not. Allowing the code to
attached to all PDs, GDSC and RPMPD alike but only scale the
RPMPD. - Sashiko
- Fixes error cleanup flagged by Sashiko.
- Checks args->args_count. Sashiko
- Performs checks on pd_list == NULL. Sashiko
- Makes opp-table required. Sashiko
- Uses const "core" and "timer" when getting clocks. - Loic
- Adds AHB clock - Vijay
- Revert v7 lane-enable change, BIT(pos * 2) is correct: data-lanes
are logical indices, CSI_COMMON_CTRL5 is a physical bitmap - Nihal
- Adds a comment to explain clock-lane - Nihal
- Link to v8: https://patch.msgid.link/20260523-x1e-csi2-phy-v8-0-a85668459521@linaro.org
Changes in v8:
- Fixes two dt verification splats I missed by passing the wrong yaml file
to my checking script :( - Rob's bot
- Fixes the polarity offset error - thanks Sashiko.
- CONFIG_PM implies CONFIG_GENERIC_PM_DOMAINS no change - Sashiko, Bryan
- Implemented suggested unwinding by Sashiko
- Leaving the flagged settle_cnt alone. Requires invalid DT settings and in
addition the result is just a long settle count. Not real bug - Sashiko
- Link to v7: https://patch.msgid.link/20260522-x1e-csi2-phy-v7-0-79cb1280fad6@linaro.org
Changes in v7:
- Made CONFIG_PM a dependency. Sashiko commented on the pd_list being NULL
and suggested I check the pointer but, we need to ramp the rails when
switching clock rate so we need CONFIG_PM full stop. - Sashiko.ai, Bryan
- Added unwind operation for performance state error path - Sashiko
- Made clock-lanes genuinely optional for the supported use-case. - Sashiko
- Fixed the enable of lanes. Thus far we have had forever it seems
val |= BIT(lane.pos * 2) which I've never looked at much because it
has always worked. But looking at how to switch on polarities I realised
the relevant register is a linear bitmask without gaps so the correct
method is `val |= BIT(lane.pos)`.
This needs an update in the legacy PHY too in another series - Bryan
- I opted not to do any of the "but if DT send junk into your driver" fixes
from Sashiko since TBH I think the code would be Spaghetti afterwards.
We trust DT and if DT is wrong we fix it, we don't try to graph its
relative (in)sanity.
- Fixed my example in the yaml. Sashiko
- Link to v6: https://patch.msgid.link/20260521-x1e-csi2-phy-v6-0-9d73d9bd7d20@linaro.org
Changes in v6:
- Taking feedback from lively debate added ports and
endpoints to the PHY - Neil, Vlad
- Detection of split mode by way of which ports are declared.
port@0 is always a sensor input.
port@1 is optional and if present implies split-mode
port@2 is always the output. - Dmitry, Neil, Vlad.
- Split mode is left as -ENOTSUPP unless/until someone with the appropriate
hardware can take on responsibility to drive to completion.
- Extending phy_config_opts dropped.
I think this is a worthwhile extension but this series no longer depends
on it so dropped. - Bryan
- MX/MXC.
Two OPP tables one for CSIPHY0/1/2 scaling MXC one for CSIPHY4 keeping
MXA at LOWSVS_D1 - to be implemented in DT not here. Taniya, Konrad, Bryan
- Changed MAINTAINERS from Supported to Maintained.
Hobby time for me right now. - Bryan
- Link to v5: https://lore.kernel.org/r/20260326-x1e-csi2-phy-v5-0-0c0fc7f5c01b@linaro.org
v5:
- Adds support to apply passed parameters for clock/data position/polarity - Neil
- Drops GEN1/GEN2 differentiation this can be reconstituted if GEN1 ever
gets supported in this driver - Dmitry
- Drops camnoc_axi, cpas_ahb - Konrad
- Renames csiphy->core csiphy_timer->timer - Konrad
- Renames rail from 0p8 to 0p9 schematics say VDD_A_CSI_n_0P9 - Konrad
- TITAN_TOP_GDSC dropped - Konrad
- Passes PHY_QCOM_CSI2_MODE_{DPHY|CPHY|SPLIT_DPHY} with the controller
selecting the mode. Only DPHY mode is supported but the method to pass
CPHY or split-mode DPHY configuration is there.
Since split-mode is a Qualcomm specific mode the PHY modes are defined in
our binding instead of adding a new type to include/linux/phy/phy.h - bod
- Depends-on: https://lore.kernel.org/r/20260325-dphy-params-extension-v1-0-c6df5599284a@linaro.org
- Link to v4: https://lore.kernel.org/r/20260315-x1e-csi2-phy-v4-0-90c09203888d@linaro.org
v4:
- MMCX, MCX and MX/MXA power-domains added - Dmitry, Vijay, Konrad
- power-domain-names added as required - bod
- opp-tables amended to capture RPMHPD deps - Dmitry, Vijay
- Switched to dev_pm_opp_set_rate, dev_pm_domain_attach_by_name etc
dropped inherited CAMSS code - Dmitry
- Amended parameters structure to specify power-domain name list - bod
- Removed dead defines - Dmitry
- Noted in CSIPHY commit log intention to rework patterns of
PHY lane configs into loops/defines/bit-fields later - Dmitry, bod
- Lowercase hex throughout - Dmitry
- The yaml and code in this driver doesn't care if the node is a
sibling or a sub-node of CAMSS confirmed to work both ways - Dmitry, bod
- Link to v3: https://lore.kernel.org/r/20260226-x1e-csi2-phy-v3-0-11e608759410@linaro.org
v3:
- Resending this to make clear this submission is additive to x1e/Hamoa
The existing bindings and code will continue to work
Bindings are added only, nothing is subtracted from existing ABI.
- Link to v2: https://lore.kernel.org/r/20260225-x1e-csi2-phy-v2-0-7756edb67ea9@linaro.org
v2:
In this updated version
- Added operating-point support
The csiphy clock sets the OPP prior to setting the rate
for csiphy and csiphy_timer - Konrad
- Combo mode
Combo mode in CAMSS yaml has been added. Right now
no code has been changed in the PHY driver to support it as
I don't have hardware to test. In principle though it can
be supported. - Vladimir
- CSIPHY init sequences
I left these as their "magic number formats". With my diminished
status as a non-qcom VPN person - I can no longer see what the bits
map to. Moreover this is the situation any non-VPN community member
will be in when submitting CSIPHY sequences derived from downstream.
I think it is perfectly reasonable to take public CSIPHY init sequences
as magic numbers. If someone with bit-level access wants to enumerate
the bits that's fine but, it shouldn't gate in the interim. - Konrad/bod
- Sensor endpoints
I've stuck to the format used by every other CSIPHY in upstream.
Sensor endpoints hit the CAMSS/CSID endpoint not a endpoint in the PHY.
Given the proposed changes to CAMSS though to support "combo mode" I
think this should achieve the same outcome - multiple sensors on the one
PHY without introducing endpoints into the PHY that no other CSIPHY in
upstream currently has.
- Bitmask of enabled lanes
Work needs to be done in the v4l2 layer to really support this.
I propose making a separate series dedicated to non-linear bit
interpretation after merging this so as to contain the scope of the
series to something more bite (byte haha) sized. - Konrad/bod
- Link to v1: https://lore.kernel.org/r/20250710-x1e-csi2-phy-v1-0-74acbb5b162b@linaro.org
v1:
This short series adds a CSI2 MIPI PHY driver, initially supporting D-PHY
mode. The core logic and init sequences come directly from CAMSS and are
working on at least five separate x1e devices.
The rationale to instantiate CSI2 PHYs as standalone devices instead of as
sub-nodes of CAMSS is as follows.
1. Precedence
CAMSS has a dedicated I2C bus called CCI Camera Control Interface.
We model this controller as its own separate device in devicetree.
This makes sense and CCI/I2C is a well defined bus type already modelled
in Linux.
MIPI CSI2 PHY devices similarly fit into a well defined separate
bus/device structure.
Contrast to another CAMSS component such as VFE, CSID or TPG these
components only interact with other CAMSS inputs/outputs unlike CSIPHY
which interacts with non-SoC components.
2. Hardware pinouts and rails
The CSI2 PHY has its own data/clock lanes out from the SoC and indeed
has its own incoming power-rails.
3. Other devicetree schemas
There are several examples throughout the kernel of CSI PHYs modeled as
standalone devices which one assumes follows the same reasoning as given
above.
I've been working on this on-and-off since the end of April:
Link: https://lore.kernel.org/linux-media/c5cf0155-f839-4db9-b865-d39b56bb1e0a@linaro.org
There is another proposal to have the PHYs be subdevices of CAMSS but, I
believe we should go with a "full fat" PHY to match best practices in
drivers/phy/qualcomm/*.
Using the standard PHY API and the parameter passing that goes with it
allows us to move away from custom interfaces in CAMSS and to conform more
clearly to established PHY paradigms such as the QMP combo PHY.
Looking at existing compat strings I settled on
"qcom,x1e80100-mipi-csi2-combo-phy" deliberately omitting reference to the
fact the PHY is built on a four nano-meter process node, which seems to
match recent submissions to QMP PHY.
My first pass at this driver included support for the old two phase
devices:
Link: https://git.codelinaro.org/bryan.odonoghue/kernel/-/commit/a504c28d109296c93470340cfe7281231f573bcb#b6e59ed7db94c9da22e492bb03fcda6a4300983c
I realised that the device tree schema changes required to support a
comprehensive conversion of all CAMSS to this driver would be an
almost certainly be unacceptable ABI break or at the very least an enormous
amount of work and verification so I instead aimed to support just one new
SoC in the submission.
I've retained the callback indirections give us scope to add in another type of
future PHY including potentially adding in the 2PH later on.
This driver is tested and working on x1e/Hamoa and has been tested as not
breaking sc8280xp/Makena and sm8250/Kona.
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
Bryan O'Donoghue (2):
dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
.../bindings/phy/qcom,x1e80100-csi2-phy.yaml | 195 +++++++++
MAINTAINERS | 10 +
drivers/phy/qualcomm/Kconfig | 14 +
drivers/phy/qualcomm/Makefile | 5 +
drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c | 386 ++++++++++++++++++
drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c | 443 +++++++++++++++++++++
drivers/phy/qualcomm/phy-qcom-mipi-csi2.h | 98 +++++
7 files changed, 1151 insertions(+)
---
base-commit: 8dac27bfa2f994ecb11f01a63641527d17d48fc1
change-id: 20250710-x1e-csi2-phy-f6434b651d3a
Best regards,
--
Bryan O'Donoghue <bryan.odonoghue@linaro.org>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v10 1/2] dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
From: Bryan O'Donoghue @ 2026-07-18 2:38 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel,
Bryan O'Donoghue
In-Reply-To: <20260718-x1e-csi2-phy-v10-0-5720a7888953@linaro.org>
Add a base schema for the MIPI CSI2 PHYs on Qualcomm SoCs. This PHY
supports both DPHY and CPHY operation. A special mode of DPHY operation -
called variously split-mode or combo-mode also allows for two sensors to be
connected to one PHY.
The submitted binding here describes the DPHY modes of operation only. CPHY
is left to future work.
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
.../bindings/phy/qcom,x1e80100-csi2-phy.yaml | 195 +++++++++++++++++++++
1 file changed, 195 insertions(+)
diff --git a/Documentation/devicetree/bindings/phy/qcom,x1e80100-csi2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,x1e80100-csi2-phy.yaml
new file mode 100644
index 0000000000000..880fe602945cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom,x1e80100-csi2-phy.yaml
@@ -0,0 +1,195 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/qcom,x1e80100-csi2-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm X1E80100 SoC CSI2 PHY
+
+maintainers:
+ - Bryan O'Donoghue <bod@kernel.org>
+
+description:
+ Qualcomm MIPI CSI2 C-PHY/D-PHY combination PHY. Connects MIPI CSI2 sensors
+ to Qualcomm's Camera CSI Decoder. The PHY supports both C-PHY and D-PHY
+ modes.
+
+properties:
+ compatible:
+ const: qcom,x1e80100-csi2-phy
+
+ reg:
+ maxItems: 1
+
+ "#phy-cells":
+ const: 1
+ description:
+ The single cell specifies the PHY operating mode.
+
+ clocks:
+ maxItems: 3
+
+ clock-names:
+ items:
+ - const: core
+ - const: timer
+ - const: ahb
+
+ interrupts:
+ maxItems: 1
+
+ operating-points-v2: true
+
+ opp-table:
+ type: object
+
+ power-domains:
+ items:
+ - description: Titan Top GDSC - Titan ISP Block, Global Distributed Switch Controller.
+ - description: MMCX voltage rail
+ - description: MXC or MXA voltage rail
+
+ power-domain-names:
+ items:
+ - const: top
+ - const: mmcx
+ - const: mx
+
+ vdda-0p9-supply:
+ description: Phandle to a 0.9V regulator supply to a PHY.
+
+ vdda-1p2-supply:
+ description: Phandle to 1.2V regulator supply to a PHY.
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ description:
+ Sensor input. Always present. A single sensor is described by a
+ single endpoint with one to four data lanes. DPHY split mode,
+ where two independent sensors share the same PHY, is described
+ by two endpoints; endpoint@0 with exactly two data-lanes and
+ endpoint@1 with exactly one data-lane.
+ unevaluatedProperties: false
+
+ patternProperties:
+ "^endpoint(@[0-9a-f]+)?$":
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+ properties:
+ data-lanes:
+ minItems: 1
+ maxItems: 4
+
+ required:
+ - data-lanes
+ - remote-endpoint
+
+ allOf:
+ - if:
+ required:
+ - endpoint@1
+ then:
+ properties:
+ endpoint@0:
+ properties:
+ data-lanes:
+ minItems: 2
+ maxItems: 2
+ endpoint@1:
+ properties:
+ data-lanes:
+ maxItems: 1
+ required:
+ - endpoint@0
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Output to the CAMSS CSID controller.
+
+ required:
+ - port@0
+ - port@1
+
+required:
+ - compatible
+ - reg
+ - "#phy-cells"
+ - clocks
+ - clock-names
+ - interrupts
+ - operating-points-v2
+ - power-domains
+ - power-domain-names
+ - vdda-0p9-supply
+ - vdda-1p2-supply
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/qcom,x1e80100-camcc.h>
+ #include <dt-bindings/clock/qcom,x1e80100-gcc.h>
+ #include <dt-bindings/power/qcom,rpmhpd.h>
+
+ phy@ace4000 {
+ compatible = "qcom,x1e80100-csi2-phy";
+ reg = <0x0ace4000 0x2000>;
+ #phy-cells = <1>;
+
+ clocks = <&camcc CAM_CC_CSIPHY0_CLK>,
+ <&camcc CAM_CC_CSI0PHYTIMER_CLK>,
+ <&camcc CAM_CC_CORE_AHB_CLK>;
+ clock-names = "core",
+ "timer",
+ "ahb";
+
+ interrupts = <GIC_SPI 477 IRQ_TYPE_EDGE_RISING>;
+
+ operating-points-v2 = <&csiphy_opp_table>;
+
+ power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>,
+ <&rpmhpd RPMHPD_MMCX>,
+ <&rpmhpd RPMHPD_MX>;
+ power-domain-names = "top",
+ "mmcx",
+ "mx";
+
+ vdda-0p9-supply = <&vreg_l2c_0p9>;
+ vdda-1p2-supply = <&vreg_l1c_1p2>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ csiphy0_in: endpoint {
+ data-lanes = <0 1 2 3>;
+ remote-endpoint = <&sensor_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ csiphy0_out: endpoint {
+ remote-endpoint = <&csid_in>;
+ };
+ };
+ };
+
+ csiphy_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-300000000 {
+ opp-hz = /bits/ 64 <300000000>;
+ required-opps = <&rpmhpd_opp_low_svs_d1>,
+ <&rpmhpd_opp_low_svs_d1>;
+ };
+ };
+ };
--
2.54.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v4 0/4] phy: qcom: qmp-pcie: Add PCIe PHY support for Hawi
From: Matthew Leung @ 2026-07-18 1:04 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel,
Krzysztof Kozlowski
In-Reply-To: <20260707-hawi-phy-pcie-v4-0-8d9cc6324947@oss.qualcomm.com>
Gentle ping. All patches have Reviewed-by tags. Please let me know if anything is needed.
On Tue, Jul 07, 2026 at 08:32:45PM +0000, Matthew Leung wrote:
> This series adds QMP PCIe PHY support for the Qualcomm Hawi SoC. The Hawi
> platform features two PCIe PHY configurations: Gen3 x2 and Gen4 x1.
>
> The Gen3 x2 PHY uses v10 register definitions, while the Gen4 x1 PHY uses
> v10.60 register definitions.
>
> The series adds:
> - device tree bindings
> - v10 register offset headers
> - v10.60 register offset headers
> - driver support with PHY initialization tables for both configurations
>
> Overlap:
> The series has overlap with "phy: qcom: Introduce USB support for Hawi"
> by Ronak Raheja (see link [1]). Both patch series introduce a subset of
> v10 registers (this series for PCIe and Ronak's for USB). I have
> coordinated with Ronak regarding the overlap, and we can update the
> series to resolve any overlap based on the order of merging.
>
> Link: https://lore.kernel.org/all/20260508213234.4643-1-ronak.raheja@oss.qualcomm.com/ [1]
>
> Signed-off-by: Matthew Leung <matthew.leung@oss.qualcomm.com>
> ---
> Changes in v4:
> - Rebased onto v7.2-rc2
> - Link to v3: https://patch.msgid.link/20260618-hawi-phy-pcie-v3-0-3fa42ca45ea4@oss.qualcomm.com
>
> Changes in v3:
> - Squashed v10 register offsets into a single change
> - Squashed v10.60 register offsets into a single change
> - Removed USB mentions from header comments; offsets are PCIe-specific
> - Reused the tx offset for the v10.60 combined txrx module instead of
> introducing a separate txrx offset
> - Link to v2: https://patch.msgid.link/20260604-hawi-phy-pcie-v2-0-be908d3560db@oss.qualcomm.com
>
> Changes in v2:
> - Rebased onto v7.1-rc6
> - Patch 1: no change (Reviewed-by carried forward)
> - Patch 9: rename QPHY_PCIE_V10_60_PCS_PCS_TX_RX_CONFIG to
> QPHY_PCIE_V10_60_PCS_TX_RX_CONFIG to be consistent with the
> naming convention used in previous pcs-pcie headers
> - Patch 10: update usage of renamed macro
> - Link to v1: https://patch.msgid.link/20260508-hawi-phy-pcie-v1-0-237b894353fc@oss.qualcomm.com
>
> To: Vinod Koul <vkoul@kernel.org>
> To: Neil Armstrong <neil.armstrong@linaro.org>
> To: Rob Herring <robh@kernel.org>
> To: Krzysztof Kozlowski <krzk+dt@kernel.org>
> To: Conor Dooley <conor+dt@kernel.org>
> Cc: linux-arm-msm@vger.kernel.org
> Cc: linux-phy@lists.infradead.org
> Cc: devicetree@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
>
> ---
> Matthew Leung (4):
> dt-bindings: phy: qcom,sc8280xp-qmp-pcie-phy: Add Hawi compatibles
> phy: qcom-qmp: Add v10 register offsets
> phy: qcom-qmp: Add v10.60 register offsets
> phy: qcom: qmp-pcie: Add QMP PCIe PHY support for Hawi
>
> .../bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml | 6 +
> drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 375 +++++++++++++++++++++
> drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v10.h | 18 +
> .../phy/qualcomm/phy-qcom-qmp-pcs-pcie-v10_60.h | 26 ++
> drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h | 22 ++
> drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10_60.h | 23 ++
> .../phy/qualcomm/phy-qcom-qmp-qserdes-com-v10.h | 49 +++
> .../phy/qualcomm/phy-qcom-qmp-qserdes-com-v10_60.h | 55 +++
> .../phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h | 47 +++
> .../qualcomm/phy-qcom-qmp-qserdes-txrx-v10_60.h | 109 ++++++
> drivers/phy/qualcomm/phy-qcom-qmp.h | 10 +
> 11 files changed, 740 insertions(+)
> ---
> base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda
> change-id: 20260506-hawi-phy-pcie-283933b4113e
>
> Best regards,
> --
> Matthew Leung <matthew.leung@oss.qualcomm.com>
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v5 2/3] phy: qcom: qmp-pcie: Add QMP PCIe Multi-PHY driver
From: Bjorn Andersson @ 2026-07-17 22:45 UTC (permalink / raw)
To: Qiang Yu
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Manivannan Sadhasivam, Philipp Zabel, Konrad Dybcio,
linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <20260717-glymur_linkmode_0717-v5-2-4f9e87a61463@oss.qualcomm.com>
On Fri, Jul 17, 2026 at 02:58:34AM -0700, Qiang Yu wrote:
[..]
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c
> new file mode 100644
> index 000000000000..b79edf2b7d5f
> --- /dev/null
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c
> @@ -0,0 +1,770 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries.
No, there should not be a year in there.
> + */
> +
[..]
> +static int qmp_pcie_pd_power_on(struct qmp_pcie *qmp)
> +{
> + const struct qmp_phy_cfg *cfg = qmp->cfg;
> + int i, ret;
> +
> + for (i = 0; i < cfg->num_pds; i++) {
> + ret = pm_runtime_get_sync(qmp->pd_devs[i]);
In the error path you will put pd_devs[0..i-1], but
pm_runtime_get_sync() requires that you put pd_devs[i] as well.
Please use pm_runtime_resume_and_get() instead.
> + if (ret < 0) {
> + dev_err(qmp->dev, "failed to power on %s domain\n",
> + cfg->pd_names[i]);
> + goto err_power_off;
> + }
> + }
> +
> + return 0;
> +
> +err_power_off:
> + while (--i >= 0)
> + pm_runtime_put(qmp->pd_devs[i]);
> +
> + return ret;
> +}
Regards,
Bjorn
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v3 1/5] drm/bridge: Implement generic USB Type-C DP HPD bridge
From: Sebastian Reichel @ 2026-07-17 17:19 UTC (permalink / raw)
To: Chaoyi Chen
Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Sandy Huang, Heiko Stübner, Andy Yan, Vinod Koul,
Chaoyi Chen, Nicolas Frattaroli, linux-kernel, dri-devel,
linux-arm-kernel, linux-rockchip, linux-phy
In-Reply-To: <20260717072323.96-2-kernel@airkyi.com>
[-- Attachment #1.1: Type: text/plain, Size: 6133 bytes --]
Hello,
On Fri, Jul 17, 2026 at 03:23:19PM +0800, Chaoyi Chen wrote:
> From: Chaoyi Chen <chaoyi.chen@rock-chips.com>
>
> The HPD function of Type-C DP is implemented through
> drm_connector_oob_hotplug_event(). For embedded DP, it is required
> that the DRM connector fwnode corresponds to the Type-C port fwnode.
>
> To describe the relationship between the DP controller and the Type-C
> port device, we usually using drm_bridge to build a bridge chain.
>
> Now several USB-C controller drivers have already implemented the DP
> HPD bridge function provided by aux-hpd-bridge.c, it will build a DP
> HPD bridge on USB-C connector port device.
>
> But this requires the USB-C controller driver to manually register the
> HPD bridge. If the driver does not implement this feature, the bridge
> will not be create.
>
> So this patch implements a generic DP HPD bridge based on
> aux-hpd-bridge.c. It will monitor Type-C bus events, and when a
> Type-C port device containing the DP svid is registered, it will
> create an HPD bridge for it without the need for the USB-C controller
> driver to implement it.
>
> Signed-off-by: Chaoyi Chen <chaoyi.chen@rock-chips.com>
> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> Reviewed-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
> ---
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Tested-by: Sebastian Reichel <sebastian.reichel@collabora.com> # ArmSom Sige5
I gave this a test together with the RK3588/RK3576 USB-C DP AltMode
patches I'm working on. As the fusb302 does a manual registration
for the DRM bridge in its probe function, the bridge is registered
twice:
root@sige5 # cat /sys/kernel/debug/dri/bridges
...
bridge[1]: drm_aux_hpd_bridge_funcs
refcount: 4
type: [10] DP
OF: /soc/i2c@2ac50000/typec-portc@22/connector:usb-c-connector
ops: [0x4] hpd
bridge[2]: drm_aux_hpd_bridge_funcs
refcount: 2
type: [10] DP
OF: /soc/i2c@2ac50000/typec-portc@22/connector:usb-c-connector
ops: [0x4] hpd
...
Apparently the USB-C DP AltMode keeps working, so this just wastes
a few CPU cycles and some memory. So this can land and then we can
remove the manual code from the driver as a follow-up step. I also
gave that a try and things keep working. I won't send the fusb302
patch for now to ensure its not applied before this patch lands.
Greetings,
-- Sebastian
> (no changes since v3)
>
> Changes in v2:
> - Add copyright text.
> - Remove useless goto.
>
> ---
> drivers/gpu/drm/bridge/Kconfig | 10 ++++
> drivers/gpu/drm/bridge/Makefile | 1 +
> .../gpu/drm/bridge/aux-hpd-typec-dp-bridge.c | 54 +++++++++++++++++++
> 3 files changed, 65 insertions(+)
> create mode 100644 drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 4a57d49b4c6d..9739b2a19758 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -30,6 +30,16 @@ config DRM_AUX_HPD_BRIDGE
> Simple bridge that terminates the bridge chain and provides HPD
> support.
>
> +if DRM_AUX_HPD_BRIDGE
> +config DRM_AUX_HPD_TYPEC_BRIDGE
> + tristate
> + depends on TYPEC || !TYPEC
> + default TYPEC
> + help
> + Simple bridge that terminates the bridge chain and provides HPD
> + support. It build bridge on each USB-C connector device node.
> +endif
> +
> menu "Display Interface Bridges"
> depends on DRM && DRM_BRIDGE
>
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index 15cc821d85b7..d88a9e1ccc9a 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -1,6 +1,7 @@
> # SPDX-License-Identifier: GPL-2.0
> obj-$(CONFIG_DRM_AUX_BRIDGE) += aux-bridge.o
> obj-$(CONFIG_DRM_AUX_HPD_BRIDGE) += aux-hpd-bridge.o
> +obj-$(CONFIG_DRM_AUX_HPD_TYPEC_BRIDGE) += aux-hpd-typec-dp-bridge.o
> obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o
> obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
> obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
> diff --git a/drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c b/drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
> new file mode 100644
> index 000000000000..c09579ff7ab9
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
> @@ -0,0 +1,54 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2026 Rockchip Electronics Co., Ltd.
> + *
> + * Author: Chaoyi Chen <chaoyi.chen@rock-chips.com>
> + */
> +#include <linux/of.h>
> +#include <linux/usb/typec_altmode.h>
> +#include <linux/usb/typec_dp.h>
> +
> +#include <drm/bridge/aux-bridge.h>
> +
> +static int drm_typec_bus_event(struct notifier_block *nb,
> + unsigned long action, void *data)
> +{
> + struct device *dev = (struct device *)data;
> + struct typec_altmode *alt = to_typec_altmode(dev);
> +
> + if (action != BUS_NOTIFY_ADD_DEVICE)
> + return NOTIFY_OK;
> +
> + /*
> + * alt->dev.parent->parent : USB-C controller device
> + * alt->dev.parent : USB-C connector device
> + */
> + if (is_typec_port_altmode(&alt->dev) && alt->svid == USB_TYPEC_DP_SID)
> + drm_dp_hpd_bridge_register(alt->dev.parent->parent,
> + to_of_node(alt->dev.parent->fwnode));
> +
> + return NOTIFY_OK;
> +}
> +
> +static struct notifier_block drm_typec_event_nb = {
> + .notifier_call = drm_typec_bus_event,
> +};
> +
> +static void drm_aux_hpd_typec_dp_bridge_module_exit(void)
> +{
> + bus_unregister_notifier(&typec_bus, &drm_typec_event_nb);
> +}
> +
> +static int __init drm_aux_hpd_typec_dp_bridge_module_init(void)
> +{
> + bus_register_notifier(&typec_bus, &drm_typec_event_nb);
> +
> + return 0;
> +}
> +
> +module_init(drm_aux_hpd_typec_dp_bridge_module_init);
> +module_exit(drm_aux_hpd_typec_dp_bridge_module_exit);
> +
> +MODULE_AUTHOR("Chaoyi Chen <chaoyi.chen@rock-chips.com>");
> +MODULE_DESCRIPTION("DRM TYPEC DP HPD BRIDGE");
> +MODULE_LICENSE("GPL");
> --
> 2.53.0
>
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 112 bytes --]
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox