# --- T2-COPYRIGHT-BEGIN --- # t2/package/*/linux/up-next-qcom-x1e80100-rproc.patch.arm64 # Copyright (C) 2026 The T2 SDE Project # SPDX-License-Identifier: GPL-2.0 or patched project license # --- T2-COPYRIGHT-END --- diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index ef97faac7e47..889fba1330d8 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -106,6 +106,14 @@ properties: Phandle to the memory region reserved for the shared memory bridge to TZ. maxItems: 1 + qcom,shm-bridge-vmid: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + VMID to use as owner of shared memory bridge allocations for TZ. + QCOM_SCM_VMID_SELF_OWNER may be used to indicate that the allocations + should be self-owned rather than being assigned to a specific VMID. + Defaults to QCOM_SCM_VMID_HLOS if not specified. + qcom,sdi-enabled: description: Indicates that the SDI (Secure Debug Image) has been enabled by TZ diff --git a/Documentation/devicetree/bindings/media/qcom,sm8350-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sm8350-venus.yaml new file mode 100644 index 000000000000..352ad85ae50c --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sm8350-venus.yaml @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,sm8350-venus.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SM8350 Venus video encode and decode accelerators + +maintainers: + - Konrad Dybcio + +description: | + The Venus Iris2 IP is a video encode and decode accelerator present + on Qualcomm platforms + +allOf: + - $ref: qcom,venus-common.yaml# + +properties: + compatible: + enum: + - qcom,sc8280xp-venus + - qcom,sm8350-venus + + clocks: + maxItems: 3 + + clock-names: + items: + - const: iface + - const: core + - const: vcodec0_core + + resets: + maxItems: 1 + + reset-names: + items: + - const: core + + power-domains: + maxItems: 3 + + power-domain-names: + items: + - const: venus + - const: vcodec0 + - const: mx + + interconnects: + maxItems: 3 + + interconnect-names: + items: + - const: cpu-cfg + - const: video-mem + - const: video-llcc + + operating-points-v2: true + opp-table: + type: object + + iommus: + maxItems: 1 + +required: + - compatible + - power-domain-names + - iommus + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + + venus: video-codec@aa00000 { + compatible = "qcom,sm8350-venus"; + reg = <0x0aa00000 0x100000>; + interrupts = ; + + clocks = <&gcc GCC_VIDEO_AXI0_CLK>, + <&videocc VIDEO_CC_MVS0C_CLK>, + <&videocc VIDEO_CC_MVS0_CLK>; + clock-names = "iface", + "core", + "vcodec0_core"; + + resets = <&gcc GCC_VIDEO_AXI0_CLK_ARES>; + reset-names = "core"; + + power-domains = <&videocc MVS0C_GDSC>, + <&videocc MVS0_GDSC>, + <&rpmhpd SM8350_MX>; + power-domain-names = "venus", + "vcodec0", + "mx"; + + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_VENUS_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_VIDEO_P0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_VIDEO_P0 QCOM_ICC_TAG_ALWAYS + &gem_noc SLAVE_LLCC QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "cpu-cfg", + "video-mem", + "video-llcc"; + + operating-points-v2 = <&venus_opp_table>; + iommus = <&apps_smmu 0x2100 0x400>; + memory-region = <&pil_video_mem>; + + status = "disabled"; + }; diff --git a/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml index b075341caafc..c130733887e3 100644 --- a/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml @@ -17,20 +17,16 @@ properties: const: qcom,x1e80100-camss reg: - maxItems: 17 + maxItems: 13 reg-names: items: + - const: csid_wrapper - const: csid0 - const: csid1 - const: csid2 - const: csid_lite0 - const: csid_lite1 - - const: csid_wrapper - - const: csiphy0 - - const: csiphy1 - - const: csiphy2 - - const: csiphy4 - const: csitpg0 - const: csitpg1 - const: csitpg2 @@ -40,7 +36,7 @@ properties: - const: vfe_lite1 clocks: - maxItems: 29 + maxItems: 21 clock-names: items: @@ -55,14 +51,6 @@ properties: - const: cphy_rx_clk_src - const: csid - const: csid_csiphy_rx - - const: csiphy0 - - const: csiphy0_timer - - const: csiphy1 - - const: csiphy1_timer - - const: csiphy2 - - const: csiphy2_timer - - const: csiphy4 - - const: csiphy4_timer - const: gcc_axi_hf - const: gcc_axi_sf - const: vfe0 @@ -75,7 +63,7 @@ properties: - const: vfe_lite_csid interrupts: - maxItems: 13 + maxItems: 9 interrupt-names: items: @@ -84,15 +72,17 @@ properties: - const: csid2 - const: csid_lite0 - const: csid_lite1 - - const: csiphy0 - - const: csiphy1 - - const: csiphy2 - - const: csiphy4 - const: vfe0 - const: vfe1 - const: vfe_lite0 - const: vfe_lite1 + phys: + maxItems: 4 + + phy-names: + maxItems: 4 + interconnects: maxItems: 4 @@ -118,14 +108,6 @@ properties: - const: ife1 - const: top - vdd-csiphy-0p8-supply: - description: - Phandle to a 0.8V regulator supply to a PHY. - - vdd-csiphy-1p2-supply: - description: - Phandle to 1.8V regulator supply to a PHY. - ports: $ref: /schemas/graph.yaml#/properties/ports @@ -166,13 +148,13 @@ required: - clock-names - interrupts - interrupt-names + - phys + - phy-names - interconnects - interconnect-names - iommus - power-domains - power-domain-names - - vdd-csiphy-0p8-supply - - vdd-csiphy-1p2-supply - ports additionalProperties: false @@ -190,19 +172,15 @@ examples: #address-cells = <2>; #size-cells = <2>; - camss: isp@acb7000 { + camss: isp@acb6000 { compatible = "qcom,x1e80100-camss"; - reg = <0 0x0acb7000 0 0x2000>, + reg = <0 0x0acb6000 0 0x1000>, + <0 0x0acb7000 0 0x2000>, <0 0x0acb9000 0 0x2000>, <0 0x0acbb000 0 0x2000>, <0 0x0acc6000 0 0x1000>, <0 0x0acca000 0 0x1000>, - <0 0x0acb6000 0 0x1000>, - <0 0x0ace4000 0 0x1000>, - <0 0x0ace6000 0 0x1000>, - <0 0x0ace8000 0 0x1000>, - <0 0x0acec000 0 0x4000>, <0 0x0acf6000 0 0x1000>, <0 0x0acf7000 0 0x1000>, <0 0x0acf8000 0 0x1000>, @@ -211,16 +189,12 @@ examples: <0 0x0acc7000 0 0x2000>, <0 0x0accb000 0 0x2000>; - reg-names = "csid0", + reg-names = "csid_wrapper", + "csid0", "csid1", "csid2", "csid_lite0", "csid_lite1", - "csid_wrapper", - "csiphy0", - "csiphy1", - "csiphy2", - "csiphy4", "csitpg0", "csitpg1", "csitpg2", @@ -240,14 +214,6 @@ examples: <&camcc CAM_CC_CPHY_RX_CLK_SRC>, <&camcc CAM_CC_CSID_CLK>, <&camcc CAM_CC_CSID_CSIPHY_RX_CLK>, - <&camcc CAM_CC_CSIPHY0_CLK>, - <&camcc CAM_CC_CSI0PHYTIMER_CLK>, - <&camcc CAM_CC_CSIPHY1_CLK>, - <&camcc CAM_CC_CSI1PHYTIMER_CLK>, - <&camcc CAM_CC_CSIPHY2_CLK>, - <&camcc CAM_CC_CSI2PHYTIMER_CLK>, - <&camcc CAM_CC_CSIPHY4_CLK>, - <&camcc CAM_CC_CSI4PHYTIMER_CLK>, <&gcc GCC_CAMERA_HF_AXI_CLK>, <&gcc GCC_CAMERA_SF_AXI_CLK>, <&camcc CAM_CC_IFE_0_CLK>, @@ -270,14 +236,6 @@ examples: "cphy_rx_clk_src", "csid", "csid_csiphy_rx", - "csiphy0", - "csiphy0_timer", - "csiphy1", - "csiphy1_timer", - "csiphy2", - "csiphy2_timer", - "csiphy4", - "csiphy4_timer", "gcc_axi_hf", "gcc_axi_sf", "vfe0", @@ -294,10 +252,6 @@ examples: , , , - , - , - , - , , , , @@ -308,15 +262,16 @@ examples: "csid2", "csid_lite0", "csid_lite1", - "csiphy0", - "csiphy1", - "csiphy2", - "csiphy4", "vfe0", "vfe1", "vfe_lite0", "vfe_lite1"; + phys = <&csiphy0>, <&csiphy1>, + <&csiphy2>, <&csiphy4>; + phy-names = "csiphy0", "csiphy1", + "csiphy2", "csiphy4"; + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS @@ -348,9 +303,6 @@ examples: "ife1", "top"; - vdd-csiphy-0p8-supply = <&csiphy_0p8_supply>; - vdd-csiphy-1p2-supply = <&csiphy_1p2_supply>; - ports { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml index eb97181cbb95..bfc4d75f50ff 100644 --- a/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml @@ -37,12 +37,15 @@ properties: - description: PLL register block clocks: - maxItems: 2 + minItems: 2 + maxItems: 3 clock-names: + minItems: 2 items: - const: aux - const: cfg_ahb + - const: ref "#clock-cells": const: 1 @@ -64,6 +67,29 @@ required: - "#clock-cells" - "#phy-cells" +allOf: + - if: + properties: + compatible: + enum: + - qcom,x1e80100-dp-phy + then: + properties: + clocks: + minItems: 3 + maxItems: 3 + clock-names: + minItems: 3 + maxItems: 3 + else: + properties: + clocks: + minItems: 2 + maxItems: 2 + clock-names: + minItems: 2 + maxItems: 2 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml index c8bc512df08b..e0ec45b96bf5 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml @@ -78,10 +78,77 @@ properties: ports: $ref: /schemas/graph.yaml#/properties/ports + properties: port@0: - $ref: /schemas/graph.yaml#/properties/port + $ref: /schemas/graph.yaml#/$defs/port-base description: Output endpoint of the PHY + unevaluatedProperties: false + + properties: + endpoint: + $ref: /schemas/graph.yaml#/$defs/endpoint-base + unevaluatedProperties: false + + endpoint@0: + $ref: /schemas/graph.yaml#/$defs/endpoint-base + description: Display Port Output lanes of the PHY when used with static mapping, + The entry index is the DP lanes index, and the number is the PHY + signal in the order RX0, TX0, TX1, RX1. + unevaluatedProperties: false + + properties: + # Static lane mappings are mutually exclusive with typec-mux/orientation-mux + data-lanes: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 2 + maxItems: 4 + oneOf: + - items: # DisplayPort 1 lane, normal orientation + - const: 3 + - items: # DisplayPort 1 lane, flipped orientation + - const: 0 + - items: # DisplayPort 2 lanes, normal orientation + - const: 3 + - const: 2 + - items: # DisplayPort 2 lanes, flipped orientation + - const: 0 + - const: 1 + - items: # DisplayPort 4 lanes, normal orientation + - const: 3 + - const: 2 + - const: 1 + - const: 0 + - items: # DisplayPort 4 lanes, flipped orientation + - const: 0 + - const: 1 + - const: 2 + - const: 3 + required: + - data-lanes + + endpoint@1: + $ref: /schemas/graph.yaml#/$defs/endpoint-base + description: USB Output lanes of the PHY when used with static mapping. + The entry index is the USB3 lane in the order TX then RX, and the + number is the PHY signal in the order RX0, TX0, TX1, RX1. + unevaluatedProperties: false + + properties: + # Static lane mappings are mutually exclusive with typec-mux/orientation-mux + data-lanes: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 2 + oneOf: + - items: # USB3, normal orientation + - const: 1 + - const: 0 + - items: # USB3, flipped orientation + - const: 2 + - const: 3 + + required: + - data-lanes port@1: $ref: /schemas/graph.yaml#/properties/port diff --git a/Documentation/devicetree/bindings/phy/qcom,x1e80100-mipi-csi2-combo-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,x1e80100-mipi-csi2-combo-phy.yaml new file mode 100644 index 000000000000..e0976f012516 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,x1e80100-mipi-csi2-combo-phy.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/qcom,x1e80100-mipi-csi2-combo-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MIPI CSI2 Combo PHY + +maintainers: + - Bryan O'Donoghue + +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-mipi-csi2-combo-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + clocks: + maxItems: 4 + + clock-names: + items: + - const: camnoc_axi + - const: cpas_ahb + - const: csiphy + - const: csiphy_timer + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + + vdda-0p8-supply: + description: Phandle to a 0.8V regulator supply to a PHY. + + vdda-1p2-supply: + description: Phandle to 1.2V regulator supply to a PHY. + + phy-type: + description: D-PHY or C-PHY mode + enum: [ 10, 11 ] + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - "#phy-cells" + - clocks + - clock-names + - vdda-0p8-supply + - vdda-1p2-supply + - phy-type + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + + csiphy0: csiphy@ace4000 { + compatible = "qcom,x1e80100-mipi-csi2-combo-phy"; + reg = <0x0ace4000 0x2000>; + #phy-cells = <0>; + + clocks = <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CSIPHY0_CLK>, + <&camcc CAM_CC_CSI0PHYTIMER_CLK>; + clock-names = "camnoc_axi", + "cpas_ahb", + "csiphy", + "csiphy_timer"; + + interrupts = ; + + power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; + + vdda-0p8-supply = <&vreg_l2c_0p8>; + vdda-1p2-supply = <&vreg_l1c_1p2>; + + phy-type = ; + }; diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index b49a920af704..5048d1f94f57 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -61,6 +61,10 @@ properties: List of phandles pointing to auxiliary devices, such as amplifiers, to be added to the sound card. + channels-swapped: + $ref: /schemas/types.yaml#/definitions/flag + description: Marks the channels as left-right swapped. + model: $ref: /schemas/types.yaml#/definitions/string description: User visible long sound card name diff --git a/MAINTAINERS b/MAINTAINERS index 3da2c26a796b..1e60ee22e86a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21232,6 +21232,17 @@ 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 +L: linux-phy@lists.infradead.org +L: linux-media@vger.kernel.org +L: linux-arm-msm@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/phy/qcom,x1e80100-mipi-csi2-combo-phy.yaml +F: drivers/phy/qualcomm/phy-qcom-mipi-csi2*.c +F: drivers/phy/qualcomm/phy-qcom-mipi-csi2*.h +F: include/dt-bindings/phy/phy-qcom-mipi-csi2* + QUALCOMM NAND CONTROLLER DRIVER M: Manivannan Sadhasivam L: linux-mtd@lists.infradead.org diff --git a/arch/arm64/boot/dts/qcom/sc7180-el2.dtso b/arch/arm64/boot/dts/qcom/sc7180-el2.dtso index 49a98676ca4d..d69790445dfa 100644 --- a/arch/arm64/boot/dts/qcom/sc7180-el2.dtso +++ b/arch/arm64/boot/dts/qcom/sc7180-el2.dtso @@ -1,4 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause +#include /* * sc7180 specific modifications required to boot in EL2. @@ -14,6 +15,18 @@ zap-shader { }; }; +&remoteproc_adsp { + qcom,broken-reset; +}; + +&remoteproc_mpss { + /* Reset is implemented in TZ firmware, so no qcom,broken-reset here */ +}; + +&scm { + qcom,shm-bridge-vmid = ; +}; + /* Venus can be used in EL2 if booted similarly to ChromeOS devices. */ &venus { video-firmware { diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-el2.dtso b/arch/arm64/boot/dts/qcom/sc8280xp-el2.dtso index 25d1fa4bc205..71cf230d8bfa 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-el2.dtso +++ b/arch/arm64/boot/dts/qcom/sc8280xp-el2.dtso @@ -1,4 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause +#include /* * sc8280xp specific modifications required to boot in EL2. @@ -42,3 +43,29 @@ &pcie4 { &pcie_smmu { status = "okay"; }; + +&remoteproc_adsp { + qcom,broken-reset; +}; + +&remoteproc_slpi { + qcom,broken-reset; +}; + +&remoteproc_nsp0 { + qcom,broken-reset; +}; + +&remoteproc_nsp1 { + qcom,broken-reset; +}; + +&scm { + qcom,shm-bridge-vmid = ; +}; + +&venus { + video-firmware { + iommus = <&apps_smmu 0x2a02 0x400>; + }; +}; diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts b/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts index 637430719e6d..61d3e2ebbb29 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts +++ b/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts @@ -1480,6 +1480,11 @@ &vamacro { status = "okay"; }; +&venus { + firmware-name = "qcom/sc8280xp/LENOVO/21BX/qcvss8280.mbn"; + status = "okay"; +}; + &wsamacro { status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index 279e5e6beae2..7850ff8a35e3 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -691,6 +693,11 @@ reserved-region@85b00000 { no-map; }; + pil_video_mem: pil_video_region@86700000 { + reg = <0 0x86700000 0 0x500000>; + no-map; + }; + pil_adsp_mem: adsp-region@86c00000 { reg = <0 0x86c00000 0 0x2000000>; no-map; @@ -4166,6 +4173,81 @@ usb_1_dwc3_ss: endpoint { }; }; + venus: video-codec@aa00000 { + compatible = "qcom,sm8350-venus"; + reg = <0 0x0aa00000 0 0x100000>; + interrupts = ; + + clocks = <&gcc GCC_VIDEO_AXI0_CLK>, + <&videocc VIDEO_CC_MVS0C_CLK>, + <&videocc VIDEO_CC_MVS0_CLK>; + clock-names = "iface", + "core", + "vcodec0_core"; + power-domains = <&videocc MVS0C_GDSC>, + <&videocc MVS0_GDSC>, + <&rpmhpd SC8280XP_MX>; + power-domain-names = "venus", + "vcodec0", + "mx"; + + resets = <&gcc GCC_VIDEO_AXI0_CLK_ARES>; + reset-names = "core"; + + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_VENUS_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_VIDEO_P0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_VIDEO_P0 QCOM_ICC_TAG_ALWAYS + &gem_noc SLAVE_LLCC QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "cpu-cfg", + "video-mem", + "video-llcc"; + + operating-points-v2 = <&venus_opp_table>; + iommus = <&apps_smmu 0x2e00 0x400>; + memory-region = <&pil_video_mem>; + + status = "disabled"; + + venus_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-720000000 { + opp-hz = /bits/ 64 <720000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-1014000000 { + opp-hz = /bits/ 64 <1014000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-1098000000 { + opp-hz = /bits/ 64 <1098000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-1332000000 { + opp-hz = /bits/ 64 <1332000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + }; + }; + + videocc: clock-controller@abf0000 { + compatible = "qcom,sc8280xp-videocc"; + reg = <0 0x0abf0000 0 0x10000>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&rpmhcc RPMH_CXO_CLK_A>, + <&sleep_clk>; + power-domains = <&rpmhpd SC8280XP_MMCX>; + required-opps = <&rpmhpd_opp_low_svs>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + cci0: cci@ac4a000 { compatible = "qcom,sc8280xp-cci", "qcom,msm8996-cci"; reg = <0 0x0ac4a000 0 0x1000>; diff --git a/arch/arm64/boot/dts/qcom/x1-crd.dtsi b/arch/arm64/boot/dts/qcom/x1-crd.dtsi index 3c9455fede5c..ecc02dbf8a42 100644 --- a/arch/arm64/boot/dts/qcom/x1-crd.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-crd.dtsi @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,7 @@ / { aliases { serial0 = &uart21; + serial1 = &uart14; }; wcd938x: audio-codec { @@ -82,6 +84,13 @@ pmic-glink { <&tlmm 123 GPIO_ACTIVE_HIGH>, <&tlmm 125 GPIO_ACTIVE_HIGH>; + nvmem-cells = <&charge_limit_en>, + <&charge_limit_end>, + <&charge_limit_delta>; + nvmem-cell-names = "charge_limit_en", + "charge_limit_end", + "charge_limit_delta"; + /* Left-side rear port */ connector@0 { compatible = "usb-c-connector"; @@ -491,6 +500,48 @@ vph_pwr: regulator-vph-pwr { regulator-boot-on; }; + vreg_wcn_3p3: regulator-wcn-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 214 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&wcn_sw_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + /* + * TODO: These two regulators are actually part of the removable M.2 + * card and not the CRD mainboard. Need to describe this differently. + * Functionally it works correctly, because all we need to do is to + * turn on the actual 3.3V supply above. + */ + vreg_wcn_0p95: regulator-wcn-0p95 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_0P95"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <950000>; + + vin-supply = <&vreg_wcn_3p3>; + }; + + vreg_wcn_1p9: regulator-wcn-1p9 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_1P9"; + regulator-min-microvolt = <1900000>; + regulator-max-microvolt = <1900000>; + + vin-supply = <&vreg_wcn_3p3>; + }; + vreg_wwan: regulator-wwan { compatible = "regulator-fixed"; @@ -506,6 +557,65 @@ vreg_wwan: regulator-wwan { regulator-boot-on; }; + + wcn7850-pmu { + compatible = "qcom,wcn7850-pmu"; + + vdd-supply = <&vreg_wcn_0p95>; + vddio-supply = <&vreg_l15b_1p8>; + vddaon-supply = <&vreg_wcn_0p95>; + vdddig-supply = <&vreg_wcn_0p95>; + vddrfa1p2-supply = <&vreg_wcn_1p9>; + vddrfa1p8-supply = <&vreg_wcn_1p9>; + + wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>; + bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&wcn_wlan_bt_en>; + pinctrl-names = "default"; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p8: ldo7 { + regulator-name = "vreg_pmu_rfa_1p8"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; }; &apps_rsc { @@ -858,12 +968,103 @@ vreg_l3j_0p8: ldo3 { regulator-initial-mode = ; }; }; + + regulators-8 { + compatible = "qcom,pm8010-rpmh-regulators"; + qcom,pmic-id = "m"; + + vdd-l1-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-l4-supply = <&vreg_s4c_1p8>; + vdd-l7-supply = <&vreg_bob1>; + + vreg_l3m_1p8: ldo3 { + regulator-name = "vreg_l3m_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1808000>; + regulator-initial-mode = ; + }; + + vreg_l4m_1p8: ldo4 { + regulator-name = "vrer_l4m_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1808000>; + regulator-initial-mode = ; + }; + + vreg_l7m_2p9: ldo7 { + regulator-name = "vreg_l7m_2p9"; + regulator-min-microvolt = <2912000>; + regulator-max-microvolt = <2912000>; + regulator-initial-mode = ; + }; + }; }; &gpu { status = "okay"; }; +&camss { + status = "okay"; + + ports { + /* + * port0 => csiphy0 + * port1 => csiphy1 + * port2 => csiphy2 + * port3 => csiphy4 + */ + port@3 { + csiphy4_ep: endpoint@4 { + reg = <4>; + clock-lanes = <7>; + data-lanes = <0 1 2 3>; + remote-endpoint = <&ov08x40_ep>; + }; + }; + }; +}; + +&cci1 { + status = "okay"; +}; + +&cci1_i2c1 { + camera@36 { + compatible = "ovti,ov08x40"; + reg = <0x36>; + + reset-gpios = <&tlmm 237 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&cam_rgb_default>; + pinctrl-names = "default"; + + clocks = <&camcc CAM_CC_MCLK4_CLK>; + assigned-clocks = <&camcc CAM_CC_MCLK4_CLK>; + assigned-clock-rates = <19200000>; + + orientation = <0>; /* front facing */ + + avdd-supply = <&vreg_l7b_2p8>; + dovdd-supply = <&vreg_l3m_1p8>; + + port { + ov08x40_ep: endpoint { + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <400000000>; + remote-endpoint = <&csiphy4_ep>; + }; + }; + }; +}; + +&csiphy4 { + vdda-0p8-supply = <&vreg_l2c_0p8>; + vdda-1p2-supply = <&vreg_l1c_1p2>; + phy-type = ; + + status = "okay"; +}; + &i2c0 { clock-frequency = <400000>; @@ -1225,6 +1426,23 @@ &pcie4_phy { status = "okay"; }; +&pcie4_port0 { + wifi@0 { + compatible = "pci17cb,1107"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; +}; + &pcie5 { perst-gpios = <&tlmm 149 GPIO_ACTIVE_LOW>; wake-gpios = <&tlmm 151 GPIO_ACTIVE_LOW>; @@ -1478,6 +1696,22 @@ &tlmm { <44 4>, /* SPI (TPM) */ <238 1>; /* UFS Reset */ + cam_rgb_default: cam-rgb-default-state { + mclk-pins { + pins = "gpio100"; + function = "cam_aon"; + drive-strength = <16>; + bias-disable; + }; + + reset-n-pins { + pins = "gpio237"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + }; + edp_reg_en: edp-reg-en-state { pins = "gpio70"; function = "gpio"; @@ -1666,6 +1900,20 @@ wcd_default: wcd-reset-n-active-state { output-low; }; + wcn_sw_en: wcn-sw-en-state { + pins = "gpio214"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wcn_wlan_bt_en: wcn-wlan-bt-en-state { + pins = "gpio116", "gpio117"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + wwan_sw_en: wwan-sw-en-state { pins = "gpio221"; function = "gpio"; @@ -1674,6 +1922,23 @@ wwan_sw_en: wwan-sw-en-state { }; }; +&uart14 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn7850-bt"; + max-speed = <3200000>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + }; +}; + &uart21 { compatible = "qcom,geni-debug-uart"; status = "okay"; diff --git a/arch/arm64/boot/dts/qcom/x1-el2.dtso b/arch/arm64/boot/dts/qcom/x1-el2.dtso index 2d1c9151cf1b..1a821f1de13e 100644 --- a/arch/arm64/boot/dts/qcom/x1-el2.dtso +++ b/arch/arm64/boot/dts/qcom/x1-el2.dtso @@ -1,4 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause +#include /* * x1 specific modifications required to boot in EL2. @@ -13,8 +14,13 @@ &gpu_zap_shader { }; &iris { - /* TODO: Add video-firmware iommus to start IRIS from EL2 */ - status = "disabled"; + /* Use generic firmware in EL2 and enable it by default */ + status = "okay"; + firmware-name = "qcom/vpu/vpu30_p4.mbn"; + + video-firmware { + iommus = <&apps_smmu 0x1942 0x0>; + }; }; /* @@ -48,6 +54,14 @@ &pcie_smmu { status = "okay"; }; +&remoteproc_adsp { + qcom,broken-reset; +}; + +&remoteproc_cdsp { + qcom,broken-reset; +}; + /* * The "SBSA watchdog" is implemented in software in Gunyah * and can't be used when running in EL2. @@ -55,3 +69,7 @@ &pcie_smmu { &sbsa_watchdog { status = "disabled"; }; + +&scm { + qcom,shm-bridge-vmid = ; +}; diff --git a/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi b/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi index 654cbce9d6ec..6f5c84e528fe 100644 --- a/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi +++ b/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,10 @@ / { compatible = "lenovo,thinkpad-t14s", "qcom,x1e78100", "qcom,x1e80100"; chassis-type = "laptop"; + aliases { + serial1 = &uart14; + }; + wcd938x: audio-codec { compatible = "qcom,wcd9385-codec"; @@ -62,6 +67,45 @@ switch-lid { }; }; + hdmi-bridge { + compatible = "realtek,rtd2171"; + + pinctrl-0 = <&hdmi_hpd_default>; + pinctrl-names = "default"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + hdmi_bridge_dp_in: endpoint { + remote-endpoint = <&usb_1_ss2_qmpphy_out_dp>; + }; + }; + + port@1 { + reg = <1>; + + hdmi_bridge_tmds_out: endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + }; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con: endpoint { + remote-endpoint = <&hdmi_bridge_tmds_out>; + }; + }; + }; + pmic-glink { compatible = "qcom,x1e80100-pmic-glink", "qcom,sm8550-pmic-glink", @@ -321,6 +365,48 @@ vph_pwr: regulator-vph-pwr { regulator-boot-on; }; + vreg_wcn_3p3: regulator-wcn-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 214 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&wcn_sw_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + /* + * TODO: These two regulators are actually part of the removable M.2 + * card and not the CRD mainboard. Need to describe this differently. + * Functionally it works correctly, because all we need to do is to + * turn on the actual 3.3V supply above. + */ + vreg_wcn_0p95: regulator-wcn-0p95 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_0P95"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <950000>; + + vin-supply = <&vreg_wcn_3p3>; + }; + + vreg_wcn_1p9: regulator-wcn-1p9 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_1P9"; + regulator-min-microvolt = <1900000>; + regulator-max-microvolt = <1900000>; + + vin-supply = <&vreg_wcn_3p3>; + }; + vreg_wwan: regulator-wwan { compatible = "regulator-fixed"; @@ -351,6 +437,54 @@ sound { "VA DMIC1", "VA MIC BIAS1", "TX SWR_INPUT1", "ADC2_OUTPUT"; + displayport-0-dai-link { + link-name = "DisplayPort0 Playback"; + + codec { + sound-dai = <&mdss_dp0>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + displayport-1-dai-link { + link-name = "DisplayPort1 Playback"; + + codec { + sound-dai = <&mdss_dp1>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_1>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + displayport-2-dai-link { + link-name = "DisplayPort2 Playback"; + + codec { + sound-dai = <&mdss_dp2>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_2>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + wcd-playback-dai-link { link-name = "WCD Playback"; @@ -415,6 +549,65 @@ platform { }; }; }; + + wcn7850-pmu { + compatible = "qcom,wcn7850-pmu"; + + vdd-supply = <&vreg_wcn_0p95>; + vddio-supply = <&vreg_l15b_1p8>; + vddaon-supply = <&vreg_wcn_0p95>; + vdddig-supply = <&vreg_wcn_0p95>; + vddrfa1p2-supply = <&vreg_wcn_1p9>; + vddrfa1p8-supply = <&vreg_wcn_1p9>; + + wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>; + bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&wcn_wlan_bt_en>; + pinctrl-names = "default"; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p8: ldo7 { + regulator-name = "vreg_pmu_rfa_1p8"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; }; &apps_rsc { @@ -475,6 +668,13 @@ vreg_l6b_1p8: ldo6 { regulator-initial-mode = ; }; + vreg_l7b_2p8: ldo7 { + regulator-name = "vreg_l7b_2p8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-initial-mode = ; + }; + vreg_l8b_3p0: ldo8 { regulator-name = "vreg_l8b_3p0"; regulator-min-microvolt = <3072000>; @@ -718,6 +918,120 @@ vreg_l3j_0p8: ldo3 { regulator-initial-mode = ; }; }; + + regulators-8 { + compatible = "qcom,pm8010-rpmh-regulators"; + qcom,pmic-id = "m"; + + vdd-l1-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-l4-supply = <&vreg_s4c_1p8>; + vdd-l7-supply = <&vreg_bob1>; + + vreg_l1m_1p2: ldo1 { + regulator-name = "vreg_l1m_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1260000>; + regulator-initial-mode = ; + }; + + vreg_l2m_1p2: ldo2 { + regulator-name = "vreg_l2m_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1260000>; + regulator-initial-mode = ; + }; + + vreg_l3m_1p8: ldo3 { + regulator-name = "vreg_l3m_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1900000>; + regulator-initial-mode = ; + }; + + vreg_l4m_1p8: ldo4 { + regulator-name = "vreg_l4m_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1900000>; + regulator-initial-mode = ; + }; + + vreg_l5m_2p8: ldo5 { + regulator-name = "vreg_l5m_2p9"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l7m_2p8: ldo7 { + regulator-name = "vreg_l7m_2p9"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + }; + +}; + +&camss { + status = "okay"; + + ports { + /* + * port0 => csiphy0 + * port1 => csiphy1 + * port2 => csiphy2 + * port3 => csiphy4 + */ + port@3 { + csiphy4_ep: endpoint@4 { + reg = <4>; + clock-lanes = <7>; + data-lanes = <0 1>; + remote-endpoint = <&ov02c10_ep>; + }; + }; + }; +}; + +&cci1 { + status = "okay"; +}; + +&cci1_i2c1 { + camera@36 { + compatible = "ovti,ov02c10"; + reg = <0x36>; + + reset-gpios = <&tlmm 237 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&cam_rgb_default>; + + clocks = <&camcc CAM_CC_MCLK4_CLK>; + assigned-clocks = <&camcc CAM_CC_MCLK4_CLK>; + assigned-clock-rates = <19200000>; + + orientation = <0>; /* front facing */ + + avdd-supply = <&vreg_l7m_2p8>; + dvdd-supply = <&vreg_l2m_1p2>; + dovdd-supply = <&vreg_l4m_1p8>; + + port { + ov02c10_ep: endpoint { + data-lanes = <1 2>; + link-frequencies = /bits/ 64 <400000000>; + remote-endpoint = <&csiphy4_ep>; + }; + }; + }; +}; + +&csiphy4 { + vdda-0p8-supply = <&vreg_l2c_0p8>; + vdda-1p2-supply = <&vreg_l1c_1p2>; + phy-type = ; + + status = "okay"; }; &gpu { @@ -1013,6 +1327,8 @@ &mdss { }; &mdss_dp0 { + sound-name-prefix = "DisplayPort0"; + status = "okay"; }; @@ -1021,6 +1337,8 @@ &mdss_dp0_out { }; &mdss_dp1 { + sound-name-prefix = "DisplayPort1"; + status = "okay"; }; @@ -1028,6 +1346,16 @@ &mdss_dp1_out { link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; }; +&mdss_dp2 { + sound-name-prefix = "DisplayPort2"; + + status = "okay"; +}; + +&mdss_dp2_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + &mdss_dp3 { /delete-property/ #sound-dai-cells; @@ -1081,6 +1409,23 @@ &pcie4_phy { status = "okay"; }; +&pcie4_port0 { + wifi@0 { + compatible = "pci17cb,1107"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; +}; + &pcie5 { perst-gpios = <&tlmm 149 GPIO_ACTIVE_LOW>; wake-gpios = <&tlmm 151 GPIO_ACTIVE_LOW>; @@ -1287,6 +1632,22 @@ &tlmm { <72 2>, /* Secure EC I2C connection (?) */ <238 1>; /* UFS Reset */ + cam_rgb_default: cam-rgb-default-state { + mclk-pins { + pins = "gpio100"; + function = "cam_aon"; + drive-strength = <16>; + bias-disable; + }; + + reset-n-pins { + pins = "gpio237"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + }; + ec_int_n_default: ec-int-n-state { pins = "gpio66"; function = "gpio"; @@ -1317,6 +1678,12 @@ eusb6_reset_n: eusb6-reset-n-state { output-low; }; + hdmi_hpd_default: hdmi-hpd-default-state { + pins = "gpio126"; + function = "usb2_dp"; + bias-disable; + }; + tpad_default: tpad-default-state { pins = "gpio3"; function = "gpio"; @@ -1476,6 +1843,20 @@ wcd_default: wcd-reset-n-active-state { output-low; }; + wcn_sw_en: wcn-sw-en-state { + pins = "gpio214"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wcn_wlan_bt_en: wcn-wlan-bt-en-state { + pins = "gpio116", "gpio117"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + wwan_sw_en: wwan-sw-en-state { pins = "gpio221"; function = "gpio"; @@ -1484,6 +1865,23 @@ wwan_sw_en: wwan-sw-en-state { }; }; +&uart14 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn7850-bt"; + max-speed = <3200000>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + }; +}; + &usb_1_ss0_hsphy { vdd-supply = <&vreg_l3j_0p8>; vdda12-supply = <&vreg_l2j_1p2>; @@ -1548,6 +1946,34 @@ &usb_1_ss1_qmpphy_out { remote-endpoint = <&retimer_ss1_ss_in>; }; +&usb_1_ss2_qmpphy { + vdda-phy-supply = <&vreg_l2j_1p2>; + vdda-pll-supply = <&vreg_l2d_0p9>; + + /delete-property/ mode-switch; + /delete-property/ orientation-switch; + + status = "okay"; + + ports { + port@0 { + #address-cells = <1>; + #size-cells = <0>; + + /delete-node/ endpoint; + + usb_1_ss2_qmpphy_out_dp: endpoint@0 { + reg = <0>; + + data-lanes = <3 2 1 0>; + remote-endpoint = <&hdmi_bridge_dp_in>; + }; + + /* No USB3 lanes connected */ + }; + }; +}; + &usb_2 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts b/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts index 58f8caaa7258..cd71fe59369e 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "x1e80100.dtsi" @@ -21,7 +22,6 @@ / { chassis-type = "laptop"; aliases { - serial0 = &uart21; serial1 = &uart14; }; @@ -151,6 +151,67 @@ linux,cma { }; }; + sound { + compatible = "qcom,x1e80100-sndcard"; + model = "X1E80100-Dell-XPS-13-9345"; + audio-routing = "WooferLeft IN", "WSA WSA_SPK1 OUT", + "TweeterLeft IN", "WSA WSA_SPK2 OUT", + "WooferRight IN", "WSA2 WSA_SPK1 OUT", + "TweeterRight IN", "WSA2 WSA_SPK2 OUT", + "VA DMIC0", "vdd-micb", + "VA DMIC1", "vdd-micb"; + channels-swapped; + + wsa-dai-link { + link-name = "WSA Playback"; + + cpu { + sound-dai = <&q6apmbedai WSA_CODEC_DMA_RX_0>; + }; + + codec { + sound-dai = <&left_woofer>, <&left_tweeter>, + <&swr0 0>, <&lpass_wsamacro 0>, + <&right_woofer>, <&right_tweeter>, + <&swr3 0>, <&lpass_wsa2macro 0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + va-dai-link { + link-name = "VA Capture"; + + cpu { + sound-dai = <&q6apmbedai VA_CODEC_DMA_TX_0>; + }; + + codec { + sound-dai = <&lpass_vamacro 0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + + vreg_cam_1p8: regulator-cam-1p8 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_CAM_1P8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + gpio = <&tlmm 91 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&cam_ldo_en>; + pinctrl-names = "default"; + }; + vreg_edp_3p3: regulator-edp-3p3 { compatible = "regulator-fixed"; @@ -415,6 +476,13 @@ vreg_bob2: bob2 { regulator-initial-mode = ; }; + vreg_l1b_1p8: ldo1 { + regulator-name = "vreg_l1b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + vreg_l2b_3p0: ldo2 { regulator-name = "vreg_l2b_3p0"; regulator-min-microvolt = <3072000>; @@ -436,6 +504,13 @@ vreg_l6b_1p8: ldo6 { regulator-initial-mode = ; }; + vreg_l7b_2p8: ldo7 { + regulator-name = "vreg_l7b_2p8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-initial-mode = ; + }; + vreg_l8b_3p0: ldo8 { regulator-name = "vreg_l8b_3p0"; regulator-min-microvolt = <3072000>; @@ -682,6 +757,71 @@ zap-shader { }; }; +&camcc { + status = "okay"; +}; + +&camss { + status = "okay"; + + ports { + /* + * port0 => csiphy0 + * port1 => csiphy1 + * port2 => csiphy2 + * port3 => csiphy4 + */ + port@3 { + csiphy4_ep: endpoint@4 { + reg = <4>; + clock-lanes = <7>; + data-lanes = <0 1>; + remote-endpoint = <&ov02e10_ep>; + }; + }; + }; +}; + +&cci1 { + status = "okay"; +}; + +&cci1_i2c1 { + camera@36 { + compatible = "ovti,ov02c10"; + reg = <0x36>; + + reset-gpios = <&tlmm 237 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&cam_rgb_default>; + + clocks = <&camcc CAM_CC_MCLK4_CLK>; + assigned-clocks = <&camcc CAM_CC_MCLK4_CLK>; + assigned-clock-rates = <19200000>; + + orientation = <0>; /* front facing */ + + avdd-supply = <&vreg_l7b_2p8>; + dvdd-supply = <&vreg_l7b_2p8>; + dovdd-supply = <&vreg_cam_1p8>; + + port { + ov02e10_ep: endpoint { + data-lanes = <1 2>; + link-frequencies = /bits/ 64 <400000000>; + remote-endpoint = <&csiphy4_ep>; + }; + }; + }; +}; + +&csiphy4 { + vdda-0p8-supply = <&vreg_l2c_0p8>; + vdda-1p2-supply = <&vreg_l1c_1p2>; + + status = "okay"; +}; + &i2c0 { clock-frequency = <400000>; status = "okay"; @@ -880,6 +1020,32 @@ &iris { status = "okay"; }; +&lpass_tlmm { + spkr_01_sd_n_active: spkr-01-sd-n-active-state { + pins = "gpio12"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; + + spkr_23_sd_n_active: spkr-23-sd-n-active-state { + pins = "gpio13"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; +}; + +&lpass_vamacro { + pinctrl-0 = <&dmic01_default>, <&dmic23_default>; + pinctrl-names = "default"; + + vdd-micb-supply = <&vreg_l1b_1p8>; + qcom,dmic-sample-rate = <4800000>; +}; + &mdss { status = "okay"; }; @@ -1067,11 +1233,90 @@ &smb2360_1_eusb2_repeater { vdd3-supply = <&vreg_l14b_3p0>; }; +&swr0 { + status = "okay"; + + pinctrl-0 = <&wsa_swr_active>, <&spkr_01_sd_n_active>; + pinctrl-names = "default"; + + /* WSA8845, Left Woofer */ + left_woofer: speaker@0,0 { + compatible = "sdw20217020400"; + reg = <0 0>; + reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "WooferLeft"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l12b_1p2>; + qcom,port-mapping = <1 2 3 7 10 13>; + }; + + /* WSA8845, Left Tweeter */ + left_tweeter: speaker@0,1 { + compatible = "sdw20217020400"; + reg = <0 1>; + reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "TweeterLeft"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l12b_1p2>; + qcom,port-mapping = <4 5 6 7 11 13>; + }; +}; + +&swr3 { + status = "okay"; + + pinctrl-0 = <&wsa2_swr_active>, <&spkr_23_sd_n_active>; + pinctrl-names = "default"; + + /* WSA8845, Right Woofer */ + right_woofer: speaker@0,0 { + compatible = "sdw20217020400"; + reg = <0 0>; + reset-gpios = <&lpass_tlmm 13 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "WooferRight"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l12b_1p2>; + qcom,port-mapping = <1 2 3 7 10 13>; + }; + + /* WSA8845, Right Tweeter */ + right_tweeter: speaker@0,1 { + compatible = "sdw20217020400"; + reg = <0 1>; + reset-gpios = <&lpass_tlmm 13 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "TweeterRight"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l12b_1p2>; + qcom,port-mapping = <4 5 6 7 11 13>; + }; +}; + &tlmm { gpio-reserved-ranges = <44 4>, /* SPI11 (TPM) */ <76 4>, /* SPI19 (TZ Protected) */ <238 1>; /* UFS Reset */ + cam_rgb_default: cam-rgb-default-state { + mclk-pins { + /* cam_aon_mclk4 */ + pins = "gpio100"; + function = "cam_aon"; + drive-strength = <16>; + bias-disable; + }; + + reset-n-pins { + pins = "gpio237"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + }; + cam_indicator_en: cam-indicator-en-state { pins = "gpio110"; function = "gpio"; @@ -1079,6 +1324,13 @@ cam_indicator_en: cam-indicator-en-state { bias-disable; }; + cam_ldo_en: cam-ldo-en-state { + pins = "gpio91"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + edp_bl_en: edp-bl-en-state { pins = "gpio74"; function = "gpio"; @@ -1269,11 +1521,6 @@ bluetooth { }; }; -&uart21 { - compatible = "qcom,geni-debug-uart"; - status = "okay"; -}; - &usb_1_ss0_hsphy { vdd-supply = <&vreg_l3j_0p8>; vdda12-supply = <&vreg_l2j_1p2>; diff --git a/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts b/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts index e0642fe8343f..af7f64a4adfc 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts @@ -7,6 +7,7 @@ #include #include +#include #include #include "x1e80100.dtsi" @@ -795,6 +796,57 @@ vreg_l3j_0p8: ldo3 { regulator-initial-mode = ; }; }; + + regulators-8 { + compatible = "qcom,pm8010-rpmh-regulators"; + qcom,pmic-id = "m"; + + vdd-l1-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-l4-supply = <&vreg_s4c_1p8>; + vdd-l7-supply = <&vreg_bob1>; + + vreg_l1m_1p2: ldo1 { + regulator-name = "vreg_l1m_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1260000>; + regulator-initial-mode = ; + }; + + vreg_l2m_1p2: ldo2 { + regulator-name = "vreg_l2m_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1260000>; + regulator-initial-mode = ; + }; + + vreg_l3m_1p8: ldo3 { + regulator-name = "vreg_l3m_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1900000>; + regulator-initial-mode = ; + }; + + vreg_l4m_1p8: ldo4 { + regulator-name = "vreg_l4m_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1900000>; + regulator-initial-mode = ; + }; + + vreg_l5m_2p8: ldo5 { + regulator-name = "vreg_l5m_2p9"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l7m_2p8: ldo7 { + regulator-name = "vreg_l7m_2p9"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + }; }; &gpu { @@ -805,6 +857,68 @@ zap-shader { }; }; +&camss { + status = "okay"; + + ports { + /* + * port0 => csiphy0 + * port1 => csiphy1 + * port2 => csiphy2 + * port3 => csiphy4 + */ + port@3 { + csiphy4_ep: endpoint@4 { + reg = <4>; + clock-lanes = <7>; + data-lanes = <0 1>; + remote-endpoint = <&ov02c10_ep>; + }; + }; + }; +}; + +&cci1 { + status = "okay"; +}; + +&cci1_i2c1 { + camera@36 { + compatible = "ovti,ov02c10"; + reg = <0x36>; + + reset-gpios = <&tlmm 237 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&cam_rgb_default>; + + clocks = <&camcc CAM_CC_MCLK4_CLK>; + assigned-clocks = <&camcc CAM_CC_MCLK4_CLK>; + assigned-clock-rates = <19200000>; + + orientation = <0>; /* front facing */ + + avdd-supply = <&vreg_l7m_2p8>; + dvdd-supply = <&vreg_l2m_1p2>; + dovdd-supply = <&vreg_l4m_1p8>; + + port { + ov02c10_ep: endpoint { + data-lanes = <1 2>; + link-frequencies = /bits/ 64 <400000000>; + remote-endpoint = <&csiphy4_ep>; + }; + }; + }; +}; + +&csiphy4 { + vdda-0p8-supply = <&vreg_l2c_0p8>; + vdda-1p2-supply = <&vreg_l1c_1p2>; + phy-type = ; + + status = "okay"; +}; + &i2c0 { clock-frequency = <400000>; @@ -1350,6 +1464,22 @@ &tlmm { <44 4>, /* SPI (TPM) */ <238 1>; /* UFS Reset */ + cam_rgb_default: cam-rgb-default-state { + mclk-pins { + pins = "gpio100"; + function = "cam_aon"; + drive-strength = <16>; + bias-disable; + }; + + reset-n-pins { + pins = "gpio237"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + }; + edp_reg_en: edp-reg-en-state { pins = "gpio70"; function = "gpio"; diff --git a/arch/arm64/boot/dts/qcom/x1e80100-pmics.dtsi b/arch/arm64/boot/dts/qcom/x1e80100-pmics.dtsi index 621890ada153..6a31a0adf8be 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-pmics.dtsi +++ b/arch/arm64/boot/dts/qcom/x1e80100-pmics.dtsi @@ -240,6 +240,26 @@ reboot_reason: reboot-reason@48 { }; }; + pmk8550_sdam_15: nvram@7e00 { + compatible = "qcom,spmi-sdam"; + reg = <0x7e00>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x7e00 0x100>; + + charge_limit_en: charge-limit-en@73 { + reg = <0x73 0x1>; + }; + + charge_limit_end: charge-limit-end@75 { + reg = <0x75 0x1>; + }; + + charge_limit_delta: charge-limit-delta@76 { + reg = <0x76 0x1>; + }; + }; + pmk8550_gpios: gpio@8800 { compatible = "qcom,pmk8550-gpio", "qcom,spmi-gpio"; reg = <0xb800>; diff --git a/arch/arm64/boot/dts/qcom/x1e80100.dtsi b/arch/arm64/boot/dts/qcom/x1e80100.dtsi index 51576d9c935d..3cf2568def3f 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100.dtsi +++ b/arch/arm64/boot/dts/qcom/x1e80100.dtsi @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -5335,6 +5336,346 @@ videocc: clock-controller@aaf0000 { #power-domain-cells = <1>; }; + cci0: cci@ac15000 { + compatible = "qcom,x1e80100-cci", "qcom,msm8996-cci"; + reg = <0 0x0ac15000 0 0x1000>; + + interrupts = ; + + clocks = <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CCI_0_CLK>; + clock-names = "camnoc_axi", + "cpas_ahb", + "cci"; + + power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; + + pinctrl-0 = <&cci0_default>; + pinctrl-1 = <&cci0_sleep>; + pinctrl-names = "default", "sleep"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + cci0_i2c0: i2c-bus@0 { + reg = <0>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + + cci0_i2c1: i2c-bus@1 { + reg = <1>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + cci1: cci@ac16000 { + compatible = "qcom,x1e80100-cci", "qcom,msm8996-cci"; + reg = <0 0x0ac16000 0 0x1000>; + + interrupts = ; + + clocks = <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CCI_1_CLK>; + clock-names = "camnoc_axi", + "cpas_ahb", + "cci"; + + power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; + + pinctrl-0 = <&cci1_default>; + pinctrl-1 = <&cci1_sleep>; + pinctrl-names = "default", "sleep"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + + cci1_i2c0: i2c-bus@0 { + reg = <0>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + + cci1_i2c1: i2c-bus@1 { + reg = <1>; + clock-frequency = <1000000>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + camss: isp@acb6000 { + compatible = "qcom,x1e80100-camss"; + + reg = <0 0x0acb6000 0 0x1000>, + <0 0x0acb7000 0 0x2000>, + <0 0x0acb9000 0 0x2000>, + <0 0x0acbb000 0 0x2000>, + <0 0x0acc6000 0 0x1000>, + <0 0x0acca000 0 0x1000>, + <0 0x0acf6000 0 0x1000>, + <0 0x0acf7000 0 0x1000>, + <0 0x0acf8000 0 0x1000>, + <0 0x0ac62000 0 0x4000>, + <0 0x0ac71000 0 0x4000>, + <0 0x0acc7000 0 0x2000>, + <0 0x0accb000 0 0x2000>; + reg-names = "csid_wrapper", + "csid0", + "csid1", + "csid2", + "csid_lite0", + "csid_lite1", + "csitpg0", + "csitpg1", + "csitpg2", + "vfe0", + "vfe1", + "vfe_lite0", + "vfe_lite1"; + + clocks = <&camcc CAM_CC_CAMNOC_AXI_NRT_CLK>, + <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>, + <&camcc CAM_CC_CORE_AHB_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CPAS_FAST_AHB_CLK>, + <&camcc CAM_CC_CPAS_IFE_0_CLK>, + <&camcc CAM_CC_CPAS_IFE_1_CLK>, + <&camcc CAM_CC_CPAS_IFE_LITE_CLK>, + <&camcc CAM_CC_CPHY_RX_CLK_SRC>, + <&camcc CAM_CC_CSID_CLK>, + <&camcc CAM_CC_CSID_CSIPHY_RX_CLK>, + <&gcc GCC_CAMERA_HF_AXI_CLK>, + <&gcc GCC_CAMERA_SF_AXI_CLK>, + <&camcc CAM_CC_IFE_0_CLK>, + <&camcc CAM_CC_IFE_0_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_1_CLK>, + <&camcc CAM_CC_IFE_1_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_LITE_CLK>, + <&camcc CAM_CC_IFE_LITE_AHB_CLK>, + <&camcc CAM_CC_IFE_LITE_CPHY_RX_CLK>, + <&camcc CAM_CC_IFE_LITE_CSID_CLK>; + clock-names = "camnoc_nrt_axi", + "camnoc_rt_axi", + "core_ahb", + "cpas_ahb", + "cpas_fast_ahb", + "cpas_vfe0", + "cpas_vfe1", + "cpas_vfe_lite", + "cphy_rx_clk_src", + "csid", + "csid_csiphy_rx", + "gcc_axi_hf", + "gcc_axi_sf", + "vfe0", + "vfe0_fast_ahb", + "vfe1", + "vfe1_fast_ahb", + "vfe_lite", + "vfe_lite_ahb", + "vfe_lite_cphy_rx", + "vfe_lite_csid"; + + interrupts = , + , + , + , + , + , + , + , + ; + interrupt-names = "csid0", + "csid1", + "csid2", + "csid_lite0", + "csid_lite1", + "vfe0", + "vfe1", + "vfe_lite0", + "vfe_lite1"; + + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_ICP QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "ahb", + "hf_mnoc", + "sf_mnoc", + "sf_icp_mnoc"; + + iommus = <&apps_smmu 0x800 0x60>, + <&apps_smmu 0x860 0x60>, + <&apps_smmu 0x1800 0x60>, + <&apps_smmu 0x1860 0x60>, + <&apps_smmu 0x18e0 0x00>, + <&apps_smmu 0x1900 0x00>, + <&apps_smmu 0x1980 0x20>, + <&apps_smmu 0x19a0 0x20>; + + phys = <&csiphy0>, <&csiphy1>, + <&csiphy2>, <&csiphy4>; + phy-names = "csiphy0", "csiphy1", + "csiphy2", "csiphy4"; + + power-domains = <&camcc CAM_CC_IFE_0_GDSC>, + <&camcc CAM_CC_IFE_1_GDSC>, + <&camcc CAM_CC_TITAN_TOP_GDSC>; + power-domain-names = "ife0", + "ife1", + "top"; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + + port@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + port@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + }; + + csiphy0: csiphy@ace4000 { + compatible = "qcom,x1e80100-mipi-csi2-combo-phy"; + reg = <0 0x0ace4000 0 0x2000>; + + clocks = <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CSIPHY0_CLK>, + <&camcc CAM_CC_CSI0PHYTIMER_CLK>; + clock-names = "camnoc_axi", + "cpas_ahb", + "csiphy", + "csiphy_timer"; + + interrupts = ; + + power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; + + #phy-cells = <0>; + + status = "disabled"; + }; + + csiphy1: csiphy@ace6000 { + compatible = "qcom,x1e80100-mipi-csi2-combo-phy"; + reg = <0 0x0ace6000 0 0x2000>; + + clocks = <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CSIPHY1_CLK>, + <&camcc CAM_CC_CSI1PHYTIMER_CLK>; + clock-names = "camnoc_axi", + "cpas_ahb", + "csiphy", + "csiphy_timer"; + + interrupts = ; + + power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; + + #phy-cells = <0>; + + status = "disabled"; + }; + + csiphy2: csiphy@ace8000 { + compatible = "qcom,x1e80100-mipi-csi2-combo-phy"; + reg = <0 0x0ace8000 0 0x2000>; + + clocks = <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CSIPHY2_CLK>, + <&camcc CAM_CC_CSI2PHYTIMER_CLK>; + clock-names = "camnoc_axi", + "cpas_ahb", + "csiphy", + "csiphy_timer"; + + interrupts = ; + + power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; + + #phy-cells = <0>; + + status = "disabled"; + }; + + csiphy4: csiphy@acec000 { + compatible = "qcom,x1e80100-mipi-csi2-combo-phy"; + reg = <0 0x0acec000 0 0x2000>; + + clocks = <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CSIPHY4_CLK>, + <&camcc CAM_CC_CSI4PHYTIMER_CLK>; + clock-names = "camnoc_axi", + "cpas_ahb", + "csiphy", + "csiphy_timer"; + + interrupts = ; + + power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; + + #phy-cells = <0>; + + status = "disabled"; + }; + + camcc: clock-controller@ade0000 { + compatible = "qcom,x1e80100-camcc"; + reg = <0 0x0ade0000 0 0x20000>; + clocks = <&gcc GCC_CAMERA_AHB_CLK>, + <&bi_tcxo_div2>, + <&bi_tcxo_ao_div2>, + <&sleep_clk>; + power-domains = <&rpmhpd RPMHPD_MXC>, + <&rpmhpd RPMHPD_MMCX>; + required-opps = <&rpmhpd_opp_low_svs>, + <&rpmhpd_opp_low_svs>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + mdss: display-subsystem@ae00000 { compatible = "qcom,x1e80100-mdss"; reg = <0 0x0ae00000 0 0x1000>; @@ -5817,9 +6158,11 @@ mdss_dp2_phy: phy@aec2a00 { <0 0x0aec2000 0 0x1c8>; clocks = <&dispcc DISP_CC_MDSS_DPTX2_AUX_CLK>, - <&dispcc DISP_CC_MDSS_AHB_CLK>; + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&tcsr TCSR_EDP_CLKREF_EN>; clock-names = "aux", - "cfg_ahb"; + "cfg_ahb", + "ref"; power-domains = <&rpmhpd RPMHPD_MX>; @@ -5837,9 +6180,11 @@ mdss_dp3_phy: phy@aec5a00 { <0 0x0aec5000 0 0x1c8>; clocks = <&dispcc DISP_CC_MDSS_DPTX3_AUX_CLK>, - <&dispcc DISP_CC_MDSS_AHB_CLK>; + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&tcsr TCSR_EDP_CLKREF_EN>; clock-names = "aux", - "cfg_ahb"; + "cfg_ahb", + "ref"; power-domains = <&rpmhpd RPMHPD_MX>; @@ -5961,6 +6306,78 @@ tlmm: pinctrl@f100000 { gpio-ranges = <&tlmm 0 0 239>; wakeup-parent = <&pdc>; + cci0_default: cci0-default-state { + cci0_i2c0_default: cci0-i2c0-default-pins { + /* cci_i2c_sda0, cci_i2c_scl0 */ + pins = "gpio101", "gpio102"; + function = "cci_i2c"; + drive-strength = <2>; + bias-pull-up; + }; + + cci0_i2c1_default: cci0-i2c1-default-pins { + /* cci_i2c_sda1, cci_i2c_scl1 */ + pins = "gpio103", "gpio104"; + function = "cci_i2c"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + cci0_sleep: cci0-sleep-state { + cci0_i2c0_sleep: cci0-i2c0-sleep-pins { + /* cci_i2c_sda0, cci_i2c_scl0 */ + pins = "gpio101", "gpio102"; + function = "cci_i2c"; + drive-strength = <2>; + bias-pull-down; + }; + + cci0_i2c1_sleep: cci0-i2c1-sleep-pins { + /* cci_i2c_sda1, cci_i2c_scl1 */ + pins = "gpio103", "gpio104"; + function = "cci_i2c"; + drive-strength = <2>; + bias-pull-down; + }; + }; + + cci1_default: cci1-default-state { + cci1_i2c0_default: cci1-i2c0-default-pins { + /* cci_i2c_sda2, cci_i2c_scl2 */ + pins = "gpio105","gpio106"; + function = "cci_i2c"; + drive-strength = <2>; + bias-pull-up; + }; + + cci1_i2c1_default: cci1-i2c1-default-pins { + /* aon_cci_i2c_sda3, aon_cci_i2c_scl3 */ + pins = "gpio235","gpio236"; + function = "aon_cci"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + cci1_sleep: cci1-sleep-state { + cci1_i2c0_sleep: cci1-i2c0-sleep-pins { + /* cci_i2c_sda2, cci_i2c_scl2 */ + pins = "gpio105","gpio106"; + function = "cci_i2c"; + drive-strength = <2>; + bias-pull-down; + }; + + cci1_i2c1_sleep: cci1-i2c1-sleep-pins { + /* aon_cci_i2c_sda3, aon_cci_i2c_scl3 */ + pins = "gpio235","gpio236"; + function = "aon_cci"; + drive-strength = <2>; + bias-pull-down; + }; + }; + edp0_hpd_default: edp0-hpd-default-state { pins = "gpio119"; function = "edp0_hot"; diff --git a/arch/arm64/configs/qcom_laptops.config b/arch/arm64/configs/qcom_laptops.config new file mode 100644 index 000000000000..a2664bd01701 --- /dev/null +++ b/arch/arm64/configs/qcom_laptops.config @@ -0,0 +1,96 @@ +# Disable other archs to speed up builds and reduce size of kernel +# CONFIG_ARCH_ACTIONS is not set +# CONFIG_ARCH_AIROHA is not set +# CONFIG_ARCH_SUNXI is not set +# CONFIG_ARCH_ALPINE is not set +# CONFIG_ARCH_APPLE is not set +# CONFIG_ARCH_AXIADO is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_ARCH_BERLIN is not set +# CONFIG_ARCH_BLAIZE is not set +# CONFIG_ARCH_CIX is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_SPARX5 is not set +# CONFIG_ARCH_K3 is not set +# CONFIG_ARCH_LG1K is not set +# CONFIG_ARCH_HISI is not set +# CONFIG_ARCH_KEEMBAY is not set +# CONFIG_ARCH_MEDIATEK is not set +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_NXP is not set +# CONFIG_ARCH_LAYERSCAPE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_S32 is not set +# CONFIG_ARCH_MA35 is not set +# CONFIG_ARCH_NPCM is not set +# CONFIG_ARCH_REALTEK is not set +# CONFIG_ARCH_RENESAS is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_SEATTLE is not set +# CONFIG_ARCH_INTEL_SOCFPGA is not set +# CONFIG_ARCH_SOPHGO is not set +# CONFIG_ARCH_STM32 is not set +# CONFIG_ARCH_SYNQUACER is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_TESLA_FSD is not set +# CONFIG_ARCH_SPRD is not set +# CONFIG_ARCH_THUNDER is not set +# CONFIG_ARCH_THUNDER2 is not set +# CONFIG_ARCH_UNIPHIER is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_VISCONTI is not set +# CONFIG_ARCH_XGENE is not set +# CONFIG_ARCH_ZYNQMP is not set + +# Disable bigger drivers for non-existing hardware (e.g. other SoCs) +# CONFIG_DRM_NOUVEAU is not set +# CONFIG_DRM_ETNAVIV is not set +# CONFIG_DRM_HISI_HIBMC is not set +# CONFIG_DRM_HISI_KIRIN is not set +# CONFIG_DRM_LIMA is not set +# CONFIG_DRM_PANFROST is not set +# CONFIG_DRM_PANTHOR is not set +# CONFIG_DRM_TIDSS is not set +# CONFIG_DRM_POWERVR is not set + +# Needed to have SCM calls properly working in EL2 +# TODO: Is this stable enough to be enabled for all SoCs in defconfig? +CONFIG_QCOM_TZMEM_MODE_SHMBRIDGE=y + +# Full disk encryption +CONFIG_DM_CRYPT=m + +# libcamera softisp +CONFIG_DMABUF_HEAPS_CMA=y + +# For Ubuntu/snap +CONFIG_SQUASHFS_XATTR=y +CONFIG_SQUASHFS_LZ4=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +# For Ubuntu/localsearch +CONFIG_SECURITY_LANDLOCK=y + +# For network sharing, e.g. in Ubuntu connection sharing with test boards +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_NF_TABLES=m +CONFIG_NF_TABLES_INET=y +CONFIG_NFT_NAT=m +CONFIG_NFT_MASQ=m +CONFIG_NFT_CT=m +CONFIG_NFT_REJECT=m +CONFIG_NF_CONNTRACK=m + +# Network from mobile phone (so as a device) +CONFIG_USB_NET_RNDIS_HOST=m + +# Typical use-cases for desktop/laptop +CONFIG_EXFAT_FS=m +CONFIG_NTFS_FS=m +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_SOC_USB=m +CONFIG_SND_USB_AUDIO_MIDI_V2=y +CONFIG_FW_LOADER_COMPRESS=y +CONFIG_FW_LOADER_COMPRESS_XZ=y +CONFIG_FW_LOADER_COMPRESS_ZSTD=y diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 7aa2f9ad2935..9edf4f2b1be4 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -407,6 +407,13 @@ char *efi_convert_cmdline(efi_loaded_image_t *image) return (char *)cmdline_addr; } +#define EFI_EVENT_GROUP_BEFORE_EXIT_BOOT_SERVICES \ + EFI_GUID(0x8be0e274, 0x3970, 0x4b44, 0x80, 0xc5, 0x1a, 0xb9, 0x50, 0x2f, 0x3b, 0xfc) + +static void efi_before_ebs_notify(efi_event_t event, void *context) +{ +} + /** * efi_exit_boot_services() - Exit boot services * @handle: handle of the exiting image @@ -427,10 +434,27 @@ efi_status_t efi_exit_boot_services(void *handle, void *priv, { struct efi_boot_memmap *map; efi_status_t status; + efi_guid_t guid = EFI_EVENT_GROUP_BEFORE_EXIT_BOOT_SERVICES; + efi_event_t event; if (efi_disable_pci_dma) efi_pci_disable_bridge_busmaster(); + status = efi_bs_call(create_event_ex, EFI_EVT_NOTIFY_SIGNAL, + EFI_TPL_CALLBACK, efi_before_ebs_notify, NULL, + &guid, &event); + if (status == EFI_SUCCESS) { + status = efi_bs_call(signal_event, event); + if (status != EFI_SUCCESS) + efi_err("%s - signal event failed: %02lx\n", __func__, status); + + status = efi_bs_call(close_event, event); + if (status != EFI_SUCCESS) + efi_err("%s - close event failed: %02lx\n", __func__, status); + } else { + efi_err("%s - create event ex failed: %02lx\n", __func__, status); + } + status = efi_get_memory_map(&map, true); if (status != EFI_SUCCESS) return status; diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index f5ba032863a9..f81dbe8ee217 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -272,7 +272,7 @@ union efi_boot_services { efi_status_t (__efiapi *wait_for_event)(unsigned long, efi_event_t *, unsigned long *); - void *signal_event; + efi_status_t (__efiapi *signal_event)(efi_event_t); efi_status_t (__efiapi *close_event)(efi_event_t); void *check_event; void *install_protocol_interface; @@ -324,7 +324,7 @@ union efi_boot_services { void *calculate_crc32; void (__efiapi *copy_mem)(void *, const void *, unsigned long); void (__efiapi *set_mem)(void *, unsigned long, unsigned char); - void *create_event_ex; + efi_status_t (__efiapi *create_event_ex)(u32, int, void *, void *, void *, efi_event_t *); }; struct { efi_table_hdr_t hdr; diff --git a/drivers/firmware/qcom/qcom_tzmem.c b/drivers/firmware/qcom/qcom_tzmem.c index 9f232e53115e..2de11f591c6b 100644 --- a/drivers/firmware/qcom/qcom_tzmem.c +++ b/drivers/firmware/qcom/qcom_tzmem.c @@ -71,9 +71,12 @@ static void qcom_tzmem_cleanup_area(struct qcom_tzmem_area *area) #include #include +#define QCOM_SHM_BRIDGE_SELF_OWNER BIT(1) +#define QCOM_SHM_BRIDGE_SELF_OWNER_PERM_SHIFT 2 #define QCOM_SHM_BRIDGE_NUM_VM_SHIFT 9 static bool qcom_tzmem_using_shm_bridge; +static u32 qcom_tzmem_vmid = QCOM_SCM_VMID_HLOS; /* List of machines that are known to not support SHM bridge correctly. */ static const char *const qcom_tzmem_blacklist[] = { @@ -91,9 +94,15 @@ static int qcom_tzmem_init(void) const char *const *platform; int ret; - for (platform = qcom_tzmem_blacklist; *platform; platform++) { - if (of_machine_is_compatible(*platform)) - goto notsupp; + device_property_read_u32(qcom_tzmem_dev, "qcom,shm-bridge-vmid", + &qcom_tzmem_vmid); + + /* Skip blocklist if self owner is requested */ + if (qcom_tzmem_vmid != QCOM_SCM_VMID_SELF_OWNER) { + for (platform = qcom_tzmem_blacklist; *platform; platform++) { + if (of_machine_is_compatible(*platform)) + goto notsupp; + } } ret = qcom_scm_shm_bridge_enable(qcom_tzmem_dev); @@ -125,6 +134,7 @@ static int qcom_tzmem_init(void) int qcom_tzmem_shm_bridge_create(phys_addr_t paddr, size_t size, u64 *handle) { u64 pfn_and_ns_perm, ipfn_and_s_perm, size_and_flags; + u64 ns_vmids = qcom_tzmem_vmid; int ret; if (!qcom_tzmem_using_shm_bridge) @@ -134,9 +144,15 @@ int qcom_tzmem_shm_bridge_create(phys_addr_t paddr, size_t size, u64 *handle) ipfn_and_s_perm = paddr | QCOM_SCM_PERM_RW; size_and_flags = size | (1 << QCOM_SHM_BRIDGE_NUM_VM_SHIFT); + if (qcom_tzmem_vmid == QCOM_SCM_VMID_SELF_OWNER) { + size_and_flags = size | QCOM_SHM_BRIDGE_SELF_OWNER | + (QCOM_SCM_PERM_RW << QCOM_SHM_BRIDGE_SELF_OWNER_PERM_SHIFT); + ns_vmids = 0; + } + + ret = qcom_scm_shm_bridge_create(pfn_and_ns_perm, ipfn_and_s_perm, - size_and_flags, QCOM_SCM_VMID_HLOS, - handle); + size_and_flags, ns_vmids, handle); if (ret) { dev_err(qcom_tzmem_dev, "SHM Bridge failed: ret %d paddr 0x%pa, size %zu\n", diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index 4aaeae4fa03c..d1077f8c0d6e 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -4128,22 +4129,61 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf { int fxp, fxp_min, fxp_max, fxp_actual, f = 1; int ret; - u8 pn, pn_min, pn_max; + u8 pn, pn_min, pn_max, bit_count; if (!bl->aux_set) return 0; - ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, &pn); + ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, &bit_count); if (ret < 0) { drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap: %d\n", aux->name, ret); return -ENODEV; } - pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + bit_count &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + + ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min); + if (ret < 0) { + drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n", + aux->name, ret); + return -ENODEV; + } + pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + + ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max); + if (ret < 0) { + drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n", + aux->name, ret); + return -ENODEV; + } + pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + + if (unlikely(pn_min > pn_max)) { + drm_dbg_kms(aux->drm_dev, "%s: Invalid pwmgen bit count cap min/max returned: %d %d\n", + aux->name, pn_min, pn_max); + return -EINVAL; + } + + /* + * Per VESA eDP Spec v1.4b, section 3.3.10.2: + * If DP_EDP_PWMGEN_BIT_COUNT is less than DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, + * the sink must use the MIN value as the effective PWM bit count. + * Clamp the reported value to the [MIN, MAX] capability range to ensure + * correct brightness scaling on compliant eDP panels. + * Only enable this logic if the [MIN, MAX] range is valid in regard to Spec. + */ + pn = bit_count; + if (bit_count < pn_min) + pn = clamp(bit_count, pn_min, pn_max); + bl->max = (1 << pn) - 1; - if (!driver_pwm_freq_hz) + if (!driver_pwm_freq_hz) { + if (pn != bit_count) + goto bit_count_write_back; + return 0; + } /* * Set PWM Frequency divider to match desired frequency provided by the driver. @@ -4167,21 +4207,6 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf * - FxP is within 25% of desired value. * Note: 25% is arbitrary value and may need some tweak. */ - ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min); - if (ret < 0) { - drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n", - aux->name, ret); - return 0; - } - ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max); - if (ret < 0) { - drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n", - aux->name, ret); - return 0; - } - pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; - pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK; - /* Ensure frequency is within 25% of desired value */ fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4); fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4); @@ -4199,12 +4224,17 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf break; } +bit_count_write_back: ret = drm_dp_dpcd_write_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, pn); if (ret < 0) { drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n", aux->name, ret); return 0; } + + if (!driver_pwm_freq_hz) + return 0; + bl->pwmgen_bit_count = pn; bl->max = (1 << pn) - 1; diff --git a/drivers/gpu/drm/display/drm_hdmi_audio_helper.c b/drivers/gpu/drm/display/drm_hdmi_audio_helper.c index 7d78b02c1446..6ca1c7ad0632 100644 --- a/drivers/gpu/drm/display/drm_hdmi_audio_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_audio_helper.c @@ -130,6 +130,7 @@ EXPORT_SYMBOL(drm_connector_hdmi_audio_plugged_notify); static const struct hdmi_codec_ops drm_connector_hdmi_audio_ops = { .audio_startup = drm_connector_hdmi_audio_startup, + .hw_params = drm_connector_hdmi_audio_prepare, .prepare = drm_connector_hdmi_audio_prepare, .audio_shutdown = drm_connector_hdmi_audio_shutdown, .mute_stream = drm_connector_hdmi_audio_mute_stream, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 4b970a59deaf..2f8156051d9b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1545,6 +1545,9 @@ static enum drm_mode_status dpu_crtc_mode_valid(struct drm_crtc *crtc, adjusted_mode_clk = dpu_core_perf_adjusted_mode_clk(mode->clock, dpu_kms->perf.perf_cfg); + if (dpu_kms->catalog->caps->has_3d_merge) + adjusted_mode_clk /= 2; + /* * The given mode, adjusted for the perf clock factor, should not exceed * the max core clock rate diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 62435e3cd9f4..76816eb7490b 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1964,6 +1964,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a5d, &delay_200_500_e50, "NV116WHM-N45"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a6a, &delay_200_500_e80, "NV140WUM-N44"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ac5, &delay_200_500_e50, "NV116WHM-N4C"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a84, &delay_200_500_e50, "NV133WUM-T01"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ae8, &delay_200_500_e50_p2e80, "NV140WUM-N41"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b09, &delay_200_500_e50_po2e200, "NV140FHM-NZ"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b1e, &delay_200_500_e80, "NE140QDM-N6A"), diff --git a/drivers/media/platform/qcom/camss/Kconfig b/drivers/media/platform/qcom/camss/Kconfig index 4eda48cb1adf..1edc5e5a1829 100644 --- a/drivers/media/platform/qcom/camss/Kconfig +++ b/drivers/media/platform/qcom/camss/Kconfig @@ -7,3 +7,4 @@ config VIDEO_QCOM_CAMSS select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_SG select V4L2_FWNODE + select PHY_QCOM_MIPI_CSI2 diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 2de97f58f9ae..185a51aa73d1 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -131,10 +132,10 @@ static u8 csiphy_get_bpp(const struct csiphy_format_info *formats, } /* - * csiphy_set_clock_rates - Calculate and set clock rates on CSIPHY module + * csiphy_set_clock_rates_legacy - Calculate and set clock rates on CSIPHY module * @csiphy: CSIPHY device */ -static int csiphy_set_clock_rates(struct csiphy_device *csiphy) +static int csiphy_set_clock_rates_legacy(struct csiphy_device *csiphy) { struct device *dev = csiphy->camss->dev; s64 link_freq; @@ -200,7 +201,7 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) * * Return 0 on success or a negative error code otherwise */ -static int csiphy_set_power(struct v4l2_subdev *sd, int on) +static int csiphy_set_power_legacy(struct v4l2_subdev *sd, int on) { struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); struct device *dev = csiphy->camss->dev; @@ -219,7 +220,7 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) return ret; } - ret = csiphy_set_clock_rates(csiphy); + ret = csiphy_set_clock_rates_legacy(csiphy); if (ret < 0) { regulator_bulk_disable(csiphy->num_supplies, csiphy->supplies); @@ -254,7 +255,7 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) } /* - * csiphy_stream_on - Enable streaming on CSIPHY module + * csiphy_stream_on_legacy - Enable streaming on CSIPHY module * @csiphy: CSIPHY device * * Helper function to enable streaming on CSIPHY module. @@ -262,7 +263,7 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) * * Return 0 on success or a negative error code otherwise */ -static int csiphy_stream_on(struct csiphy_device *csiphy) +static int csiphy_stream_on_legacy(struct csiphy_device *csiphy) { struct csiphy_config *cfg = &csiphy->cfg; s64 link_freq; @@ -300,6 +301,63 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) return 0; } +/* + * eam_off - Disable streaming on CSIPHY module + * @csiphy: CSIPHY device + * + * Helper function to disable streaming on CSIPHY module + */ +static void csiphy_stream_off_legacy(struct csiphy_device *csiphy) +{ + csiphy->res->hw_ops->lanes_disable(csiphy, &csiphy->cfg); +} + +/* + * csiphy_stream_on - Enable streaming on CSIPHY module + * @csiphy: CSIPHY device + * + * Helper function to enable streaming on CSIPHY module. + * Main configuration of CSIPHY module is also done here. + * + * Return 0 on success or a negative error code otherwise + */ +static int csiphy_stream_on(struct csiphy_device *csiphy) +{ + u8 bpp = csiphy_get_bpp(csiphy->res->formats->formats, csiphy->res->formats->nformats, + csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); + u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; + struct phy_configure_opts_mipi_dphy *dphy_cfg; + union phy_configure_opts dphy_opts = { 0 }; + struct device *dev = csiphy->camss->dev; + s64 link_freq; + int ret; + + dphy_cfg = &dphy_opts.mipi_dphy; + + link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes); + + if (link_freq < 0) { + dev_err(dev, + "Cannot get CSI2 transmitter's link frequency\n"); + return -EINVAL; + } + + phy_mipi_dphy_get_default_config_for_hsclk(link_freq, num_lanes, dphy_cfg); + + phy_set_mode(csiphy->phy, PHY_MODE_MIPI_DPHY); + + ret = phy_configure(csiphy->phy, &dphy_opts); + if (ret) { + dev_err(dev, "failed to configure MIPI D-PHY\n"); + goto error; + } + + return phy_power_on(csiphy->phy); + +error: + return ret; +} + /* * csiphy_stream_off - Disable streaming on CSIPHY module * @csiphy: CSIPHY device @@ -308,9 +366,28 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) */ static void csiphy_stream_off(struct csiphy_device *csiphy) { - csiphy->res->hw_ops->lanes_disable(csiphy, &csiphy->cfg); + phy_power_off(csiphy->phy); } +/* + * csiphy_set_stream - Enable/disable streaming on CSIPHY module + * @sd: CSIPHY V4L2 subdevice + * @enable: Requested streaming state + * + * Return 0 on success or a negative error code otherwise + */ +static int csiphy_set_stream_legacy(struct v4l2_subdev *sd, int enable) +{ + struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); + int ret = 0; + + if (enable) + ret = csiphy_stream_on_legacy(csiphy); + else + csiphy_stream_off_legacy(csiphy); + + return ret; +} /* * csiphy_set_stream - Enable/disable streaming on CSIPHY module @@ -568,16 +645,16 @@ static bool csiphy_match_clock_name(const char *clock_name, const char *format, } /* - * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources + * msm_csiphy_subdev_init_legacy - Initialize CSIPHY device structure and resources * @csiphy: CSIPHY device * @res: CSIPHY module resources table * @id: CSIPHY module id * * Return 0 on success or a negative error code otherwise */ -int msm_csiphy_subdev_init(struct camss *camss, - struct csiphy_device *csiphy, - const struct camss_subdev_resources *res, u8 id) +int msm_csiphy_subdev_init_legacy(struct camss *camss, + struct csiphy_device *csiphy, + const struct camss_subdev_resources *res, u8 id) { struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); @@ -715,6 +792,43 @@ int msm_csiphy_subdev_init(struct camss *camss, return ret; } +/* + * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources + * @csiphy: CSIPHY device + * @res: CSIPHY module resources table + * @id: CSIPHY module id + * + * Return 0 on success or a negative error code otherwise + */ +int msm_csiphy_subdev_init(struct camss *camss, + struct csiphy_device *csiphy, + const struct camss_subdev_resources *res, u8 id) +{ + struct device *dev = camss->dev; + int ret; + + csiphy->camss = camss; + csiphy->id = id; + csiphy->cfg.combo_mode = 0; + csiphy->res = &res->csiphy; + + snprintf(csiphy->name, ARRAY_SIZE(csiphy->name), "csiphy%d", + csiphy->id); + + csiphy->phy = devm_phy_get(dev, csiphy->name); + + if (IS_ERR(csiphy->phy)) { + dev_err(dev, "failed to get phy %s %d\n", csiphy->name, ret); + return PTR_ERR(csiphy->phy); + } + + ret = phy_init(csiphy->phy); + if (ret) + dev_err(dev, "phy %s init fail %d\n", csiphy->name, ret); + + return ret; +} + /* * csiphy_link_setup - Setup CSIPHY connections * @entity: Pointer to media entity structure @@ -749,8 +863,12 @@ static int csiphy_link_setup(struct media_entity *entity, return 0; } -static const struct v4l2_subdev_core_ops csiphy_core_ops = { - .s_power = csiphy_set_power, +static const struct v4l2_subdev_core_ops csiphy_core_ops_legacy = { + .s_power = csiphy_set_power_legacy, +}; + +static const struct v4l2_subdev_video_ops csiphy_video_ops_legacy = { + .s_stream = csiphy_set_stream_legacy, }; static const struct v4l2_subdev_video_ops csiphy_video_ops = { @@ -764,8 +882,13 @@ static const struct v4l2_subdev_pad_ops csiphy_pad_ops = { .set_fmt = csiphy_set_format, }; +static const struct v4l2_subdev_ops csiphy_v4l2_ops_legacy = { + .core = &csiphy_core_ops_legacy, + .video = &csiphy_video_ops_legacy, + .pad = &csiphy_pad_ops, +}; + static const struct v4l2_subdev_ops csiphy_v4l2_ops = { - .core = &csiphy_core_ops, .video = &csiphy_video_ops, .pad = &csiphy_pad_ops, }; @@ -794,7 +917,11 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, struct device *dev = csiphy->camss->dev; int ret; - v4l2_subdev_init(sd, &csiphy_v4l2_ops); + if (IS_ERR(csiphy->phy)) + v4l2_subdev_init(sd, &csiphy_v4l2_ops_legacy); + else + v4l2_subdev_init(sd, &csiphy_v4l2_ops); + sd->internal_ops = &csiphy_v4l2_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 895f80003c44..48398c331fe1 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -94,6 +95,7 @@ struct csiphy_device_regs { struct csiphy_device { struct camss *camss; + struct phy *phy; u8 id; struct v4l2_subdev subdev; struct media_pad pads[MSM_CSIPHY_PADS_NUM]; @@ -101,6 +103,7 @@ struct csiphy_device { void __iomem *base_clk_mux; u32 irq; char irq_name[30]; + char name[16]; struct camss_clock *clock; bool *rate_set; int nclocks; @@ -115,6 +118,10 @@ struct csiphy_device { struct camss_subdev_resources; +int msm_csiphy_subdev_init_legacy(struct camss *camss, + struct csiphy_device *csiphy, + const struct camss_subdev_resources *res, u8 id); + int msm_csiphy_subdev_init(struct camss *camss, struct csiphy_device *csiphy, const struct camss_subdev_resources *res, u8 id); diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 2fbcd0e343aa..c31ef3d14d7b 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -3081,13 +3081,6 @@ static const struct resources_icc icc_res_sa8775p[] = { static const struct camss_subdev_resources csiphy_res_x1e80100[] = { /* CSIPHY0 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, - .clock = { "csiphy0", "csiphy0_timer" }, - .clock_rate = { { 300000000, 400000000, 480000000 }, - { 266666667, 400000000 } }, - .reg = { "csiphy0" }, - .interrupt = { "csiphy0" }, .csiphy = { .id = 0, .hw_ops = &csiphy_ops_3ph_1_0, @@ -3096,13 +3089,6 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY1 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, - .clock = { "csiphy1", "csiphy1_timer" }, - .clock_rate = { { 300000000, 400000000, 480000000 }, - { 266666667, 400000000 } }, - .reg = { "csiphy1" }, - .interrupt = { "csiphy1" }, .csiphy = { .id = 1, .hw_ops = &csiphy_ops_3ph_1_0, @@ -3111,13 +3097,6 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY2 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, - .clock = { "csiphy2", "csiphy2_timer" }, - .clock_rate = { { 300000000, 400000000, 480000000 }, - { 266666667, 400000000 } }, - .reg = { "csiphy2" }, - .interrupt = { "csiphy2" }, .csiphy = { .id = 2, .hw_ops = &csiphy_ops_3ph_1_0, @@ -3126,13 +3105,6 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY4 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, - .clock = { "csiphy4", "csiphy4_timer" }, - .clock_rate = { { 300000000, 400000000, 480000000 }, - { 266666667, 400000000 } }, - .reg = { "csiphy4" }, - .interrupt = { "csiphy4" }, .csiphy = { .id = 4, .hw_ops = &csiphy_ops_3ph_1_0, @@ -3440,6 +3412,14 @@ struct media_pad *camss_find_sensor_pad(struct media_entity *entity) while (1) { pad = &entity->pads[0]; + + /* + * Work around unresolved bug in camss (or v4l2) which can + * result in pad being NULL here. + */ + if (WARN_ON(!pad)) + return NULL; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) return NULL; @@ -3676,18 +3656,40 @@ static int camss_init_subdevices(struct camss *camss) { struct platform_device *pdev = to_platform_device(camss->dev); const struct camss_resources *res = camss->res; + struct device_node *phy_np; unsigned int i; int ret; for (i = 0; i < camss->res->csiphy_num; i++) { - ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], - &res->csiphy_res[i], - res->csiphy_res[i].csiphy.id); - if (ret < 0) { - dev_err(camss->dev, - "Failed to init csiphy%d sub-device: %d\n", - i, ret); - return ret; + phy_np = of_parse_phandle(pdev->dev.of_node, "phys", i); + if (phy_np && of_device_is_available(phy_np)) { + ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], + &res->csiphy_res[i], + res->csiphy_res[i].csiphy.id); + if (ret < 0) { + dev_err(camss->dev, + "Failed to init csiphy%d sub-device: %d\n", + i, ret); + return ret; + } + } + } + + if (!phy_np) { + if (!res->legacy_phy) + return -ENODEV; + + for (i = 0; i < camss->res->csiphy_num; i++) { + ret = msm_csiphy_subdev_init_legacy(camss, &camss->csiphy[i], + &res->csiphy_res[i], + res->csiphy_res[i].csiphy.id); + if (ret < 0) { + dev_err(camss->dev, + "Failed to init csiphy%d sub-device: %d\n", + i, ret); + return ret; + } + camss->csiphy[i].phy = ERR_PTR(-ENODEV); } } @@ -3764,6 +3766,9 @@ static int camss_link_entities(struct camss *camss) for (i = 0; i < camss->res->csiphy_num; i++) { for (j = 0; j < camss->res->csid_num; j++) { + if (!camss->csiphy[i].phy) + continue; + ret = media_create_pad_link(&camss->csiphy[i].subdev.entity, MSM_CSIPHY_PAD_SRC, &camss->csid[j].subdev.entity, @@ -3873,6 +3878,9 @@ static int camss_register_entities(struct camss *camss) int ret; for (i = 0; i < camss->res->csiphy_num; i++) { + if (!camss->csiphy[i].phy) + continue; + ret = msm_csiphy_register_entity(&camss->csiphy[i], &camss->v4l2_dev); if (ret < 0) { @@ -3928,8 +3936,10 @@ static int camss_register_entities(struct camss *camss) i = camss->res->csiphy_num; err_reg_csiphy: - for (i--; i >= 0; i--) - msm_csiphy_unregister_entity(&camss->csiphy[i]); + for (i--; i >= 0; i--) { + if (camss->csiphy[i].phy) + msm_csiphy_unregister_entity(&camss->csiphy[i]); + } return ret; } @@ -3944,8 +3954,10 @@ static void camss_unregister_entities(struct camss *camss) { unsigned int i; - for (i = 0; i < camss->res->csiphy_num; i++) - msm_csiphy_unregister_entity(&camss->csiphy[i]); + for (i = 0; i < camss->res->csiphy_num; i++) { + if (camss->csiphy[i].phy) + msm_csiphy_unregister_entity(&camss->csiphy[i]); + } for (i = 0; i < camss->res->csid_num; i++) msm_csid_unregister_entity(&camss->csid[i]); @@ -4293,6 +4305,7 @@ static void camss_remove(struct platform_device *pdev) static const struct camss_resources msm8916_resources = { .version = CAMSS_8x16, + .legacy_phy = true, .csiphy_res = csiphy_res_8x16, .csid_res = csid_res_8x16, .ispif_res = &ispif_res_8x16, @@ -4304,6 +4317,7 @@ static const struct camss_resources msm8916_resources = { static const struct camss_resources msm8953_resources = { .version = CAMSS_8x53, + .legacy_phy = true, .icc_res = icc_res_8x53, .icc_path_num = ARRAY_SIZE(icc_res_8x53), .csiphy_res = csiphy_res_8x96, @@ -4317,6 +4331,7 @@ static const struct camss_resources msm8953_resources = { static const struct camss_resources msm8996_resources = { .version = CAMSS_8x96, + .legacy_phy = true, .csiphy_res = csiphy_res_8x96, .csid_res = csid_res_8x96, .ispif_res = &ispif_res_8x96, @@ -4368,6 +4383,7 @@ static const struct camss_resources sa8775p_resources = { static const struct camss_resources sdm660_resources = { .version = CAMSS_660, + .legacy_phy = true, .csiphy_res = csiphy_res_660, .csid_res = csid_res_660, .ispif_res = &ispif_res_660, @@ -4379,6 +4395,7 @@ static const struct camss_resources sdm660_resources = { static const struct camss_resources sdm670_resources = { .version = CAMSS_845, + .legacy_phy = true, .csiphy_res = csiphy_res_670, .csid_res = csid_res_670, .vfe_res = vfe_res_670, @@ -4390,6 +4407,7 @@ static const struct camss_resources sdm670_resources = { static const struct camss_resources sdm845_resources = { .version = CAMSS_845, .pd_name = "top", + .legacy_phy = true, .csiphy_res = csiphy_res_845, .csid_res = csid_res_845, .vfe_res = vfe_res_845, @@ -4401,6 +4419,7 @@ static const struct camss_resources sdm845_resources = { static const struct camss_resources sm8250_resources = { .version = CAMSS_8250, .pd_name = "top", + .legacy_phy = true, .csiphy_res = csiphy_res_8250, .csid_res = csid_res_8250, .vfe_res = vfe_res_8250, @@ -4414,6 +4433,7 @@ static const struct camss_resources sm8250_resources = { static const struct camss_resources sc8280xp_resources = { .version = CAMSS_8280XP, .pd_name = "top", + .legacy_phy = true, .csiphy_res = csiphy_res_sc8280xp, .csid_res = csid_res_sc8280xp, .ispif_res = NULL, @@ -4428,6 +4448,7 @@ static const struct camss_resources sc8280xp_resources = { static const struct camss_resources sc7280_resources = { .version = CAMSS_7280, .pd_name = "top", + .legacy_phy = true, .csiphy_res = csiphy_res_7280, .csid_res = csid_res_7280, .vfe_res = vfe_res_7280, @@ -4441,6 +4462,7 @@ static const struct camss_resources sc7280_resources = { static const struct camss_resources sm8550_resources = { .version = CAMSS_8550, .pd_name = "top", + .legacy_phy = true, .csiphy_res = csiphy_res_8550, .csid_res = csid_res_8550, .vfe_res = vfe_res_8550, diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index a70fbc78ccc3..3ca10f79f0f2 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -100,6 +100,7 @@ enum icc_count { struct camss_resources { enum camss_version version; const char *pd_name; + const bool legacy_phy; const struct camss_subdev_resources *csiphy_res; const struct camss_subdev_resources *csid_res; const struct camss_subdev_resources *ispif_res; diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h index fb194c967ad4..81c7b3bd6dac 100644 --- a/drivers/media/platform/qcom/iris/iris_core.h +++ b/drivers/media/platform/qcom/iris/iris_core.h @@ -117,6 +117,12 @@ struct iris_core { /* encoder and decoder have overlapping caps, so two different arrays are required */ struct platform_inst_fw_cap inst_fw_caps_dec[INST_FW_CAP_MAX]; struct platform_inst_fw_cap inst_fw_caps_enc[INST_FW_CAP_MAX]; + bool use_tz; + struct video_firmware { + struct platform_device *pdev; + struct iommu_domain *iommu_domain; + size_t mapped_mem_size; + } fw; }; int iris_core_init(struct iris_core *core); diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index 9ab499fad946..f458d34fa922 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -5,7 +5,9 @@ #include #include +#include #include +#include #include #include @@ -14,6 +16,45 @@ #define MAX_FIRMWARE_NAME_SIZE 128 +#define WRAPPER_TZ_BASE_OFFS 0x000C0000 + +#define WRAPPER_TZ_XTSS_SW_RESET (WRAPPER_TZ_BASE_OFFS + 0x1000) +#define WRAPPER_XTSS_SW_RESET_BIT BIT(0) + +#define WRAPPER_CPA_START_ADDR (WRAPPER_TZ_BASE_OFFS + 0x1020) +#define WRAPPER_CPA_END_ADDR (WRAPPER_TZ_BASE_OFFS + 0x1024) +#define WRAPPER_FW_START_ADDR (WRAPPER_TZ_BASE_OFFS + 0x1028) +#define WRAPPER_FW_END_ADDR (WRAPPER_TZ_BASE_OFFS + 0x102C) +#define WRAPPER_NONPIX_START_ADDR (WRAPPER_TZ_BASE_OFFS + 0x1030) +#define WRAPPER_NONPIX_END_ADDR (WRAPPER_TZ_BASE_OFFS + 0x1034) + +#define IRIS_FW_START_ADDR 0x0 + +static void iris_reset_cpu_no_tz(struct iris_core *core) +{ + u32 fw_size = core->fw.mapped_mem_size; + + writel(0, core->reg_base + WRAPPER_FW_START_ADDR); + writel(fw_size, core->reg_base + WRAPPER_FW_END_ADDR); + writel(0, core->reg_base + WRAPPER_CPA_START_ADDR); + writel(fw_size, core->reg_base + WRAPPER_CPA_END_ADDR); + writel(fw_size, core->reg_base + WRAPPER_NONPIX_START_ADDR); + writel(fw_size, core->reg_base + WRAPPER_NONPIX_END_ADDR); + + /* Bring XTSS out of reset */ + writel(0, core->reg_base + WRAPPER_TZ_XTSS_SW_RESET); +} + +static int iris_set_hw_state_no_tz(struct iris_core *core, bool resume) +{ + if (resume) + iris_reset_cpu_no_tz(core); + else + writel(WRAPPER_XTSS_SW_RESET_BIT, core->reg_base + WRAPPER_TZ_XTSS_SW_RESET); + + return 0; +} + static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) { u32 pas_id = core->iris_platform_data->pas_id; @@ -58,10 +99,29 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) goto err_release_fw; } - ret = qcom_mdt_load(dev, firmware, fw_name, - pas_id, mem_virt, mem_phys, res_size, NULL); + if (core->use_tz) + ret = qcom_mdt_load(dev, firmware, fw_name, + pas_id, mem_virt, mem_phys, res_size, NULL); + else + ret = qcom_mdt_load_no_init(dev, firmware, fw_name, + mem_virt, mem_phys, res_size, NULL); + /* Unmap memory on our side before we give firmware access to it */ memunmap(mem_virt); + if (ret) + goto err_release_fw; + + if (core->fw.iommu_domain) { + ret = iommu_map(core->fw.iommu_domain, IRIS_FW_START_ADDR, mem_phys, res_size, + IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV, GFP_KERNEL); + if (ret) { + dev_err(core->dev, "failed to map firmware in IOMMU: %d\n", ret); + goto err_release_fw; + } + + core->fw.mapped_mem_size = res_size; + } + err_release_fw: release_firmware(firmware); @@ -85,20 +145,24 @@ int iris_fw_load(struct iris_core *core) return -ENOMEM; } - ret = qcom_scm_pas_auth_and_reset(core->iris_platform_data->pas_id); - if (ret) { - dev_err(core->dev, "auth and reset failed: %d\n", ret); - return ret; - } + if (core->use_tz) { + ret = qcom_scm_pas_auth_and_reset(core->iris_platform_data->pas_id); + if (ret) { + dev_err(core->dev, "auth and reset failed: %d\n", ret); + return ret; + } - ret = qcom_scm_mem_protect_video_var(cp_config->cp_start, - cp_config->cp_size, - cp_config->cp_nonpixel_start, - cp_config->cp_nonpixel_size); - if (ret) { - dev_err(core->dev, "protect memory failed\n"); - qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); - return ret; + ret = qcom_scm_mem_protect_video_var(cp_config->cp_start, + cp_config->cp_size, + cp_config->cp_nonpixel_start, + cp_config->cp_nonpixel_size); + if (ret) { + dev_err(core->dev, "protect memory failed\n"); + qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); + return ret; + } + } else { + iris_reset_cpu_no_tz(core); } return ret; @@ -106,10 +170,98 @@ int iris_fw_load(struct iris_core *core) int iris_fw_unload(struct iris_core *core) { - return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); + int ret; + + if (core->use_tz) + ret = qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); + else + ret = iris_set_hw_state_no_tz(core, false); + + if (core->fw.mapped_mem_size) { + size_t unmapped = iommu_unmap(core->fw.iommu_domain, IRIS_FW_START_ADDR, + core->fw.mapped_mem_size); + + if (unmapped != core->fw.mapped_mem_size) + dev_err(core->dev, "failed to unmap firmware in IOMMU\n"); + + core->fw.mapped_mem_size = 0; + } + + return ret; } int iris_set_hw_state(struct iris_core *core, bool resume) { - return qcom_scm_set_remote_state(resume, 0); + if (core->use_tz) + return qcom_scm_set_remote_state(resume, 0); + else + return iris_set_hw_state_no_tz(core, resume); +} + +int iris_fw_init(struct iris_core *core) +{ + struct iommu_domain *iommu_domain; + struct platform_device_info info; + struct platform_device *pdev; + struct device_node *np; + int ret; + + np = of_get_child_by_name(core->dev->of_node, "video-firmware"); + if (!np) { + core->use_tz = true; + return 0; + } + + memset(&info, 0, sizeof(info)); + info.parent = core->dev; + info.fwnode = &np->fwnode; + info.name = np->name; + info.dma_mask = DMA_BIT_MASK(32); + + pdev = platform_device_register_full(&info); + if (IS_ERR(pdev)) { + of_node_put(np); + return PTR_ERR(pdev); + } + + ret = of_dma_configure(&pdev->dev, np, true); + of_node_put(np); + if (ret) { + dev_err(&pdev->dev, "failed to configure dma: %d\n", ret); + goto err_unregister; + } + + iommu_domain = iommu_paging_domain_alloc(&pdev->dev); + if (IS_ERR(iommu_domain)) { + dev_err(&pdev->dev, "failed to allocate iommu domain: %pe\n", iommu_domain); + ret = PTR_ERR(iommu_domain); + goto err_unregister; + } + + ret = iommu_attach_device(iommu_domain, &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "could not attach device to IOMMU: %d\n", ret); + goto err_iommu_free; + } + + core->fw.pdev = pdev; + core->fw.iommu_domain = iommu_domain; + + return 0; + +err_iommu_free: + iommu_domain_free(iommu_domain); +err_unregister: + platform_device_unregister(pdev); + return ret; +} + +void iris_fw_deinit(struct iris_core *core) +{ + if (!core->fw.iommu_domain) + return; + + iommu_detach_device(core->fw.iommu_domain, &core->fw.pdev->dev); + iommu_domain_free(core->fw.iommu_domain); + platform_device_unregister(core->fw.pdev); } diff --git a/drivers/media/platform/qcom/iris/iris_firmware.h b/drivers/media/platform/qcom/iris/iris_firmware.h index e833ecd34887..1a077a587c80 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.h +++ b/drivers/media/platform/qcom/iris/iris_firmware.h @@ -12,4 +12,7 @@ int iris_fw_load(struct iris_core *core); int iris_fw_unload(struct iris_core *core); int iris_set_hw_state(struct iris_core *core, bool resume); +int iris_fw_init(struct iris_core *core); +void iris_fw_deinit(struct iris_core *core); + #endif diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 00e99be16e08..19784d03b698 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -13,6 +13,7 @@ #include "iris_core.h" #include "iris_ctrls.h" +#include "iris_firmware.h" #include "iris_vidc.h" static int iris_init_icc(struct iris_core *core) @@ -203,6 +204,8 @@ static void iris_remove(struct platform_device *pdev) v4l2_device_unregister(&core->v4l2_dev); + iris_fw_deinit(core); + mutex_destroy(&core->lock); } @@ -263,6 +266,10 @@ static int iris_probe(struct platform_device *pdev) if (ret) return ret; + ret = iris_fw_init(core); + if (ret) + return ret; + iris_session_init_caps(core); ret = v4l2_device_register(dev, &core->v4l2_dev); diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index abf959b8f3a6..d36210460ab7 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -398,6 +398,15 @@ static int venus_probe(struct platform_device *pdev) if (IS_ERR(core->cpucfg_path)) return PTR_ERR(core->cpucfg_path); + core->llcc_path = devm_of_icc_get(dev, "video-llcc"); + if (IS_ERR(core->llcc_path)) { + /* LLCC path is optional */ + if (PTR_ERR(core->llcc_path) == -ENODATA) + core->llcc_path = NULL; + else + return PTR_ERR(core->llcc_path); + } + core->irq = platform_get_irq(pdev, 0); if (core->irq < 0) return core->irq; @@ -594,12 +603,18 @@ static __maybe_unused int venus_runtime_suspend(struct device *dev) if (ret) goto err_cpucfg_path; + ret = icc_set_bw(core->llcc_path, 0, 0); + if (ret) + goto err_llcc_path; + ret = icc_set_bw(core->video_path, 0, 0); if (ret) goto err_video_path; return ret; +err_llcc_path: + icc_set_bw(core->video_path, kbps_to_icc(20000), 0); err_video_path: icc_set_bw(core->cpucfg_path, kbps_to_icc(1000), 0); err_cpucfg_path: @@ -639,6 +654,10 @@ static __maybe_unused int venus_runtime_resume(struct device *dev) if (ret) return ret; + ret = icc_set_bw(core->llcc_path, kbps_to_icc(20000), 0); + if (ret) + return ret; + ret = icc_set_bw(core->cpucfg_path, kbps_to_icc(1000), 0); if (ret) return ret; @@ -1006,6 +1025,46 @@ static const struct venus_resources sm8250_res = { .enc_nodename = "video-encoder", }; +static const struct reg_val sm8350_reg_preset[] = { + { 0xb0088, 0, 0x11 }, +}; + +static const struct venus_resources sm8350_res = { + .freq_tbl = sm8250_freq_table, + .freq_tbl_size = ARRAY_SIZE(sm8250_freq_table), + .reg_tbl = sm8350_reg_preset, + .reg_tbl_size = ARRAY_SIZE(sm8350_reg_preset), + .bw_tbl_enc = sm8250_bw_table_enc, + .bw_tbl_enc_size = ARRAY_SIZE(sm8250_bw_table_enc), + .bw_tbl_dec = sm8250_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec), + .clks = { "core", "iface" }, + .clks_num = 2, + .resets = { "core" }, + .resets_num = 1, + .vcodec0_clks = { "vcodec0_core" }, + .vcodec_clks_num = 1, + .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, + .vcodec_pmdomains_num = 2, + .opp_pmdomain = (const char *[]) { "mx", NULL }, + .vcodec_num = 1, + .max_load = 7833600, /* 7680x4320@60fps */ + .hfi_version = HFI_VERSION_6XX, + .vpu_version = VPU_VERSION_IRIS2, + .num_vpp_pipes = 4, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = GENMASK(31, 29) - 1, + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x1000000, + .cp_nonpixel_size = 0x24800000, + .fwname = "qcom/vpu-2.0/venus.mbn", + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", +}; + static const struct freq_tbl sc7280_freq_table[] = { { 0, 460000000 }, { 0, 424000000 }, @@ -1119,17 +1178,64 @@ static const struct venus_resources qcm2290_res = { .min_fw = &min_fw, }; +static const struct freq_tbl sc8280xp_freq_table[] = { + { 0, 239999999 }, + { 0, 338000000 }, + { 0, 366000000 }, + { 0, 444000000 }, + { 0, 533000000 }, + { 0, 560000000 }, +}; + +static const struct venus_resources sc8280xp_res = { + .freq_tbl = sc8280xp_freq_table, + .freq_tbl_size = ARRAY_SIZE(sc8280xp_freq_table), + .reg_tbl = sm8350_reg_preset, + .reg_tbl_size = ARRAY_SIZE(sm8350_reg_preset), + .bw_tbl_enc = sm8250_bw_table_enc, + .bw_tbl_enc_size = ARRAY_SIZE(sm8250_bw_table_enc), + .bw_tbl_dec = sm8250_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec), + .clks = { "core", "iface" }, + .clks_num = 2, + .resets = { "core" }, + .resets_num = 1, + .vcodec0_clks = { "vcodec0_core" }, + .vcodec_clks_num = 1, + .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" }, + .vcodec_pmdomains_num = 2, + .opp_pmdomain = (const char *[]) { "mx", NULL }, + .vcodec_num = 1, + .max_load = 7833600, /* 7680x4320@60fps */ + .hfi_version = HFI_VERSION_6XX, + .vpu_version = VPU_VERSION_IRIS2, + .num_vpp_pipes = 4, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = GENMASK(31, 29) - 1, + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x1000000, + .cp_nonpixel_size = 0x24800000, + .fwname = "qcom/vpu-2.0/venus.mbn", + .dec_nodename = "video-decoder", + .enc_nodename = "video-encoder", +}; + static const struct of_device_id venus_dt_match[] = { - { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, - { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, - { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, }, - { .compatible = "qcom,qcm2290-venus", .data = &qcm2290_res, }, - { .compatible = "qcom,sc7180-venus", .data = &sc7180_res, }, - { .compatible = "qcom,sc7280-venus", .data = &sc7280_res, }, - { .compatible = "qcom,sdm660-venus", .data = &sdm660_res, }, - { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, - { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, - { .compatible = "qcom,sm8250-venus", .data = &sm8250_res, }, + { .compatible = "qcom,msm8916-venus", .data = &msm8916_res }, + { .compatible = "qcom,msm8996-venus", .data = &msm8996_res }, + { .compatible = "qcom,msm8998-venus", .data = &msm8998_res }, + { .compatible = "qcom,qcm2290-venus", .data = &qcm2290_res }, + { .compatible = "qcom,sc7180-venus", .data = &sc7180_res }, + { .compatible = "qcom,sc7280-venus", .data = &sc7280_res }, + { .compatible = "qcom,sc8280xp-venus", .data = &sc8280xp_res }, + { .compatible = "qcom,sdm660-venus", .data = &sdm660_res }, + { .compatible = "qcom,sdm845-venus", .data = &sdm845_res }, + { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2 }, + { .compatible = "qcom,sm8250-venus", .data = &sm8250_res }, + { .compatible = "qcom,sm8350-venus", .data = &sm8350_res }, { } }; MODULE_DEVICE_TABLE(of, venus_dt_match); diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 7506f5d0f609..bd89a5d58baf 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -40,6 +40,7 @@ struct freq_tbl { struct reg_val { u32 reg; u32 value; + u32 mask; }; struct bw_tbl { @@ -72,6 +73,7 @@ struct venus_resources { unsigned int bw_tbl_enc_size; const struct bw_tbl *bw_tbl_dec; unsigned int bw_tbl_dec_size; + bool has_llcc_path; const struct reg_val *reg_tbl; unsigned int reg_tbl_size; const struct hfi_ubwc_config *ubwc_conf; @@ -144,6 +146,7 @@ struct venus_format { * @vcodec1_clks: an array of vcodec1 struct clk pointers * @video_path: an interconnect handle to video to/from memory path * @cpucfg_path: an interconnect handle to cpu configuration path + * @llcc_path: an interconnect handle to video to/from llcc path * @pmdomains: a pointer to a list of pmdomains * @opp_pmdomain: an OPP power-domain * @resets: an array of reset signals @@ -198,6 +201,7 @@ struct venus_core { struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct icc_path *video_path; struct icc_path *cpucfg_path; + struct icc_path *llcc_path; struct dev_pm_domain_list *pmdomains; struct dev_pm_domain_list *opp_pmdomain; struct reset_control *resets[VIDC_RESETS_NUM_MAX]; diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index d3da35f67fd5..be38ebf5ff81 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -369,10 +369,19 @@ static void venus_set_registers(struct venus_hfi_device *hdev) const struct venus_resources *res = hdev->core->res; const struct reg_val *tbl = res->reg_tbl; unsigned int count = res->reg_tbl_size; - unsigned int i; + unsigned int i, val; + + for (i = 0; i < count; i++) { + val = tbl[i].value; - for (i = 0; i < count; i++) - writel(tbl[i].value, hdev->core->base + tbl[i].reg); + /* In some cases, we only want to update certain bits */ + if (tbl[i].mask) { + val = readl(hdev->core->base + tbl[i].reg); + val = (val & ~tbl[i].mask) | (tbl[i].value & tbl[i].mask); + } + + writel(val, hdev->core->base + tbl[i].reg); + } } static void venus_soft_int(struct venus_hfi_device *hdev) diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index f0269524ac70..bf9eddf74ee5 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -243,6 +243,9 @@ static int load_scale_bw(struct venus_core *core) dev_dbg(core->dev, VDBGL "total: avg_bw: %u, peak_bw: %u\n", total_avg, total_peak); + if (core->res->has_llcc_path) + icc_set_bw(core->llcc_path, total_avg, total_peak); + return icc_set_bw(core->video_path, total_avg, total_peak); } diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index 34654f710d8a..ef685123b66b 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include /* @@ -336,4 +338,16 @@ static inline const char *ath_bus_type_to_string(enum ath_bus_type bustype) return ath_bus_type_strings[bustype]; } +static inline int ath_pci_aspm_state(u16 lnkctl) +{ + int state = 0; + + if (lnkctl & PCI_EXP_LNKCTL_ASPM_L0S) + state |= PCIE_LINK_STATE_L0S; + if (lnkctl & PCI_EXP_LNKCTL_ASPM_L1) + state |= PCIE_LINK_STATE_L1; + + return state; +} + #endif /* ATH_H */ diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 97b49bf4ad80..4f74928f4761 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1966,9 +1966,7 @@ static int ath10k_pci_hif_start(struct ath10k *ar) ath10k_pci_irq_enable(ar); ath10k_pci_rx_post(ar); - pcie_capability_clear_and_set_word(ar_pci->pdev, PCI_EXP_LNKCTL, - PCI_EXP_LNKCTL_ASPMC, - ar_pci->link_ctl & PCI_EXP_LNKCTL_ASPMC); + pci_enable_link_state(ar_pci->pdev, ath_pci_aspm_state(ar_pci->link_ctl)); return 0; } @@ -2825,8 +2823,7 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar, pcie_capability_read_word(ar_pci->pdev, PCI_EXP_LNKCTL, &ar_pci->link_ctl); - pcie_capability_clear_word(ar_pci->pdev, PCI_EXP_LNKCTL, - PCI_EXP_LNKCTL_ASPMC); + pci_disable_link_state(ar_pci->pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); /* * Bring the target up cleanly. diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index d8655badd96d..61c223000898 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -19,6 +19,8 @@ #include "pcic.h" #include "qmi.h" +#include "../ath.h" + #define ATH11K_PCI_BAR_NUM 0 #define ATH11K_PCI_DMA_MASK 36 #define ATH11K_PCI_COHERENT_DMA_MASK 32 @@ -597,8 +599,7 @@ static void ath11k_pci_aspm_disable(struct ath11k_pci *ab_pci) u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L1)); /* disable L0s and L1 */ - pcie_capability_clear_word(ab_pci->pdev, PCI_EXP_LNKCTL, - PCI_EXP_LNKCTL_ASPMC); + pci_disable_link_state(ab_pci->pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); set_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags); } @@ -606,10 +607,7 @@ static void ath11k_pci_aspm_disable(struct ath11k_pci *ab_pci) static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci) { if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags)) - pcie_capability_clear_and_set_word(ab_pci->pdev, PCI_EXP_LNKCTL, - PCI_EXP_LNKCTL_ASPMC, - ab_pci->link_ctl & - PCI_EXP_LNKCTL_ASPMC); + pci_enable_link_state(ab_pci->pdev, ath_pci_aspm_state(ab_pci->link_ctl)); } #ifdef CONFIG_DEV_COREDUMP diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index c729d5526c75..9aa12bfba233 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -16,6 +16,8 @@ #include "mhi.h" #include "debug.h" +#include "../ath.h" + #define ATH12K_PCI_BAR_NUM 0 #define ATH12K_PCI_DMA_MASK 36 @@ -928,8 +930,7 @@ static void ath12k_pci_aspm_disable(struct ath12k_pci *ab_pci) u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L1)); /* disable L0s and L1 */ - pcie_capability_clear_word(ab_pci->pdev, PCI_EXP_LNKCTL, - PCI_EXP_LNKCTL_ASPMC); + pci_disable_link_state(ab_pci->pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); set_bit(ATH12K_PCI_ASPM_RESTORE, &ab_pci->flags); } @@ -958,10 +959,7 @@ static void ath12k_pci_aspm_restore(struct ath12k_pci *ab_pci) { if (ab_pci->ab->hw_params->supports_aspm && test_and_clear_bit(ATH12K_PCI_ASPM_RESTORE, &ab_pci->flags)) - pcie_capability_clear_and_set_word(ab_pci->pdev, PCI_EXP_LNKCTL, - PCI_EXP_LNKCTL_ASPMC, - ab_pci->link_ctl & - PCI_EXP_LNKCTL_ASPMC); + pci_enable_link_state(ab_pci->pdev, ath_pci_aspm_state(ab_pci->link_ctl)); } static void ath12k_pci_cancel_workqueue(struct ath12k_base *ab) diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index b4b62b9ccc45..e56583c2f428 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -778,11 +778,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata) pci_info(pdev, "VMD: Default LTR value set by driver\n"); out_state_change: - /* - * Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per - * PCIe r6.0, sec 5.5.4. - */ - pci_set_power_state_locked(pdev, PCI_D0); pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL); return 0; } diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 79b965158473..e92288f4ee46 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1433,6 +1433,19 @@ static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool locked return 0; } +/** + * pci_disable_link_state_locked - Disable device's link state + * @pdev: PCI device + * @state: ASPM link state to disable + * + * Disable device's link state, so the link will never enter specific states. + * Note that if the BIOS didn't grant ASPM control to the OS, this does nothing + * because we can't touch the LNKCTL register. + * + * Context: Caller holds pci_bus_sem read lock. + * + * Return: 0 on success, a negative errno otherwise. + */ int pci_disable_link_state_locked(struct pci_dev *pdev, int state) { lockdep_assert_held_read(&pci_bus_sem); @@ -1442,13 +1455,15 @@ int pci_disable_link_state_locked(struct pci_dev *pdev, int state) EXPORT_SYMBOL(pci_disable_link_state_locked); /** - * pci_disable_link_state - Disable device's link state, so the link will - * never enter specific states. Note that if the BIOS didn't grant ASPM - * control to the OS, this does nothing because we can't touch the LNKCTL - * register. Returns 0 or a negative errno. - * + * pci_disable_link_state - Disable device's link state * @pdev: PCI device * @state: ASPM link state to disable + * + * Disable device's link state, so the link will never enter specific states. + * Note that if the BIOS didn't grant ASPM control to the OS, this does nothing + * because we can't touch the LNKCTL register. + * + * Return: 0 on success, a negative errno otherwise. */ int pci_disable_link_state(struct pci_dev *pdev, int state) { @@ -1477,6 +1492,7 @@ static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); link->aspm_default = pci_calc_aspm_enable_mask(state); + link->aspm_disable &= ~state; pcie_config_aspm_link(link, policy_to_aspm_state(link)); link->clkpm_default = (state & PCIE_LINK_STATE_CLKPM) ? 1 : 0; @@ -1489,43 +1505,59 @@ static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked) } /** - * pci_enable_link_state - Clear and set the default device link state so that - * the link may be allowed to enter the specified states. Note that if the - * BIOS didn't grant ASPM control to the OS, this does nothing because we can't - * touch the LNKCTL register. Also note that this does not enable states - * disabled by pci_disable_link_state(). Return 0 or a negative errno. - * - * Note: Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per - * PCIe r6.0, sec 5.5.4. - * + * pci_enable_link_state - Enable device's link state * @pdev: PCI device * @state: Mask of ASPM link states to enable + * + * Enable device's link state, so the link will enter the specified states. + * Note that if the BIOS didn't grant ASPM control to the OS, this does + * nothing because we can't touch the LNKCTL register. + * + * Note: The device will be transitioned to D0 state if the PCI-PM L1 Substates + * are getting enabled. + * + * Return: 0 on success, a negative errno otherwise. */ int pci_enable_link_state(struct pci_dev *pdev, int state) { + /* + * Ensure the device is in D0 before enabling PCI-PM L1 PM Substates, per + * PCIe r6.0, sec 5.5.4. + */ + if (FIELD_GET(PCIE_LINK_STATE_L1_SS_PCIPM, state)) + pci_set_power_state(pdev, PCI_D0); + return __pci_enable_link_state(pdev, state, false); } EXPORT_SYMBOL(pci_enable_link_state); /** - * pci_enable_link_state_locked - Clear and set the default device link state - * so that the link may be allowed to enter the specified states. Note that if - * the BIOS didn't grant ASPM control to the OS, this does nothing because we - * can't touch the LNKCTL register. Also note that this does not enable states - * disabled by pci_disable_link_state(). Return 0 or a negative errno. - * - * Note: Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per - * PCIe r6.0, sec 5.5.4. - * + * pci_enable_link_state_locked - Enable device's link state * @pdev: PCI device * @state: Mask of ASPM link states to enable * + * Enable device's link state, so the link will enter the specified states. + * Note that if the BIOS didn't grant ASPM control to the OS, this does + * nothing because we can't touch the LNKCTL register. + * + * Note: The device will be transitioned to D0 state if the PCI-PM L1 Substates + * are getting enabled. + * * Context: Caller holds pci_bus_sem read lock. + * + * Return: 0 on success, a negative errno otherwise. */ int pci_enable_link_state_locked(struct pci_dev *pdev, int state) { lockdep_assert_held_read(&pci_bus_sem); + /* + * Ensure the device is in D0 before enabling PCI-PM L1 PM Substates, per + * PCIe r6.0, sec 5.5.4. + */ + if (FIELD_GET(PCIE_LINK_STATE_L1_SS_PCIPM, state)) + pci_set_power_state(pdev, PCI_D0); + return __pci_enable_link_state(pdev, state, true); } EXPORT_SYMBOL(pci_enable_link_state_locked); diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index 60a0ead127fa..24263f8f30a9 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -28,6 +28,17 @@ 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 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. + 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 b71a6a0bed3f..b21a4f9086f6 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -6,6 +6,12 @@ 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-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c index f1b51018683d..ca9bb9d70e29 100644 --- a/drivers/phy/qualcomm/phy-qcom-edp.c +++ b/drivers/phy/qualcomm/phy-qcom-edp.c @@ -103,7 +103,9 @@ struct qcom_edp { struct phy_configure_opts_dp dp_opts; - struct clk_bulk_data clks[2]; + struct clk_bulk_data *clks; + int num_clks; + struct regulator_bulk_data supplies[2]; bool is_edp; @@ -218,7 +220,7 @@ static int qcom_edp_phy_init(struct phy *phy) if (ret) return ret; - ret = clk_bulk_prepare_enable(ARRAY_SIZE(edp->clks), edp->clks); + ret = clk_bulk_prepare_enable(edp->num_clks, edp->clks); if (ret) goto out_disable_supplies; @@ -885,7 +887,7 @@ static int qcom_edp_phy_exit(struct phy *phy) { struct qcom_edp *edp = phy_get_drvdata(phy); - clk_bulk_disable_unprepare(ARRAY_SIZE(edp->clks), edp->clks); + clk_bulk_disable_unprepare(edp->num_clks, edp->clks); regulator_bulk_disable(ARRAY_SIZE(edp->supplies), edp->supplies); return 0; @@ -1092,11 +1094,9 @@ static int qcom_edp_phy_probe(struct platform_device *pdev) if (IS_ERR(edp->pll)) return PTR_ERR(edp->pll); - edp->clks[0].id = "aux"; - edp->clks[1].id = "cfg_ahb"; - ret = devm_clk_bulk_get(dev, ARRAY_SIZE(edp->clks), edp->clks); - if (ret) - return ret; + edp->num_clks = devm_clk_bulk_get_all(dev, &edp->clks); + if (edp->num_clks < 0) + return dev_err_probe(dev, edp->num_clks, "failed to parse clocks\n"); edp->supplies[0].supply = "vdda-phy"; edp->supplies[1].supply = "vdda-pll"; 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 000000000000..1a99efee88cc --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-phy_qcom_mipi_csi2-3ph-1-0.c + * + * Qualcomm MSM Camera Subsystem - CSIPHY Module 3phase v1.0 + * + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2016-2025 Linaro Ltd. + */ +#define DEBUG +#include +#include +#include + +#include "phy-qcom-mipi-csi2.h" + +#define CSIPHY_3PH_LNn_CFG1(n) (0x000 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG (BIT(7) | BIT(6)) +#define CSIPHY_3PH_LNn_CFG2(n) (0x004 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG2_LP_REC_EN_INT BIT(3) +#define CSIPHY_3PH_LNn_CFG3(n) (0x008 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG4(n) (0x00c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS 0xa4 +#define CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS_660 0xa5 +#define CSIPHY_3PH_LNn_CFG5(n) (0x010 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG5_T_HS_DTERM 0x02 +#define CSIPHY_3PH_LNn_CFG5_HS_REC_EQ_FQ_INT 0x50 +#define CSIPHY_3PH_LNn_TEST_IMP(n) (0x01c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_TEST_IMP_HS_TERM_IMP 0xa +#define CSIPHY_3PH_LNn_MISC1(n) (0x028 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_MISC1_IS_CLKLANE BIT(2) +#define CSIPHY_3PH_LNn_CFG6(n) (0x02c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG6_SWI_FORCE_INIT_EXIT BIT(0) +#define CSIPHY_3PH_LNn_CFG7(n) (0x030 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG7_SWI_T_INIT 0x2 +#define CSIPHY_3PH_LNn_CFG8(n) (0x034 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG8_SWI_SKIP_WAKEUP BIT(0) +#define CSIPHY_3PH_LNn_CFG8_SKEW_FILTER_ENABLE BIT(1) +#define CSIPHY_3PH_LNn_CFG9(n) (0x038 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG9_SWI_T_WAKEUP 0x1 +#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15(n) (0x03c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL 0xb8 + +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(offset, n) ((offset) + 0x4 * (n)) +#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_STATUSn(offset, n) ((offset) + 0xb0 + 0x4 * (n)) + +#define CSIPHY_DEFAULT_PARAMS 0 +#define CSIPHY_LANE_ENABLE 1 +#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2 +#define CSIPHY_SETTLE_CNT_HIGHER_BYTE 3 +#define CSIPHY_DNP_PARAMS 4 +#define CSIPHY_2PH_REGS 5 +#define CSIPHY_3PH_REGS 6 +#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 */ + {0x1014, 0xD5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x101C, 0x7A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x1018, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + + {0x0094, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x00A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0090, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0098, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0094, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0094, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x005C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0060, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0064, 0x7F, 0x00, CSIPHY_SKEW_CAL}, + + {0x0E94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0EA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0E30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E28, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E00, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E0C, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E38, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0E08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0E10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + + {0x0494, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x04A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0490, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0498, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0494, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0400, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0494, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x045C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0460, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0464, 0x7F, 0x00, CSIPHY_SKEW_CAL}, + + {0x0894, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x08A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0890, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0898, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0894, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0830, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0800, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0838, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x082C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0834, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x081C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0814, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x083C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0804, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0820, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0808, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0810, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0894, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x085C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0860, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0864, 0x7F, 0x00, CSIPHY_SKEW_CAL}, + + {0x0C94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0CA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS}, + {0x0C30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C00, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C38, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x0C10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0C94, 0xD7, 0x00, CSIPHY_SKEW_CAL}, + {0x0C5C, 0x00, 0x00, CSIPHY_SKEW_CAL}, + {0x0C60, 0xBD, 0x00, CSIPHY_SKEW_CAL}, + {0x0C64, 0x7F, 0x00, CSIPHY_SKEW_CAL}, +}; + +static inline const struct mipi_csi2phy_device_regs * +csi2phy_dev_to_regs(const 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 hw_version; + + writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6)); + + hw_version = readl_relaxed(csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 12)); + hw_version |= readl_relaxed(csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 13)) << 8; + hw_version |= readl_relaxed(csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 14)) << 16; + hw_version |= readl_relaxed(csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 15)) << 24; + + csi2phy->hw_version = hw_version; + + dev_dbg(csi2phy->dev, "CSIPHY 3PH HW Version = 0x%08x\n", 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_relaxed(0x1, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0)); + usleep_range(5000, 8000); + writel_relaxed(0x0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0)); +} + +static irqreturn_t phy_qcom_mipi_csi2_isr(int irq, void *dev) +{ + const struct mipi_csi2phy_device *csi2phy = dev; + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy); + int i; + + for (i = 0; i < 11; i++) { + int c = i + 22; + u8 val = readl_relaxed(csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, i)); + + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, c)); + } + + writel_relaxed(0x1, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 10)); + writel_relaxed(0x0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 10)); + + for (i = 22; i < 33; i++) { + writel_relaxed(0x0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, i)); + } + + return IRQ_HANDLED; +} + +/* + * 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 ui; /* ps */ + u32 timer_period; /* ps */ + u32 t_hs_prepare_max; /* ps */ + u32 t_hs_settle; /* ps */ + u8 settle_cnt; + + if (link_freq <= 0) + return 0; + + ui = div_u64(1000000000000LL, link_freq); + ui /= 2; + t_hs_prepare_max = 85000 + 6 * ui; + t_hs_settle = t_hs_prepare_max; + + timer_period = div_u64(1000000000000LL, timer_clk_rate); + settle_cnt = t_hs_settle / timer_period - 6; + + return settle_cnt; +} + +static void phy_qcom_mipi_csi2_gen1_config_lanes(struct mipi_csi2phy_device *csi2phy, + struct mipi_csi2phy_stream_cfg *cfg, + u8 settle_cnt) +{ + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy); + struct mipi_csi2phy_lanes_cfg *lane_cfg = &cfg->lane_cfg; + int i, l = 0; + u8 val; + + for (i = 0; i <= cfg->num_data_lanes; i++) { + if (i == cfg->num_data_lanes) + l = 7; + else + l = lane_cfg->data[i].pos * 2; + + val = CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG; + val |= 0x17; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG1(l)); + + val = CSIPHY_3PH_LNn_CFG2_LP_REC_EN_INT; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG2(l)); + + val = settle_cnt; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG3(l)); + + val = CSIPHY_3PH_LNn_CFG5_T_HS_DTERM | + CSIPHY_3PH_LNn_CFG5_HS_REC_EQ_FQ_INT; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG5(l)); + + val = CSIPHY_3PH_LNn_CFG6_SWI_FORCE_INIT_EXIT; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG6(l)); + + val = CSIPHY_3PH_LNn_CFG7_SWI_T_INIT; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG7(l)); + + val = CSIPHY_3PH_LNn_CFG8_SWI_SKIP_WAKEUP | + CSIPHY_3PH_LNn_CFG8_SKEW_FILTER_ENABLE; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG8(l)); + + val = CSIPHY_3PH_LNn_CFG9_SWI_T_WAKEUP; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG9(l)); + + val = CSIPHY_3PH_LNn_TEST_IMP_HS_TERM_IMP; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_TEST_IMP(l)); + + val = CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL; + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_LNn_CSI_LANE_CTRL15(l)); + } + + val = CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG1(l)); + + if (regs->generation == GEN1_660) + val = CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS_660; + else + val = CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_CFG4(l)); + + val = CSIPHY_3PH_LNn_MISC1_IS_CLKLANE; + writel_relaxed(val, csi2phy->base + CSIPHY_3PH_LNn_MISC1(l)); +} + +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->mipi_csi2phy_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; + case CSIPHY_DNP_PARAMS: + continue; + default: + val = r->reg_data; + break; + } + writel_relaxed(val, csi2phy->base + r->reg_addr); + if (r->delay_us) + udelay(r->delay_us); + } +} + +static bool phy_qcom_mipi_csi2_is_gen2(struct mipi_csi2phy_device *csi2phy) +{ + const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy); + + return regs->generation == GEN2; +} + +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); + + val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE; + for (i = 0; i < cfg->num_data_lanes; i++) + val |= BIT(lane_cfg->data[i].pos * 2); + + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 5)); + + val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B; + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6)); + + val = 0x02; + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 7)); + + val = 0x00; + writel_relaxed(val, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0)); + + if (phy_qcom_mipi_csi2_is_gen2(csi2phy)) + phy_qcom_mipi_csi2_gen2_config_lanes(csi2phy, settle_cnt); + else + phy_qcom_mipi_csi2_gen1_config_lanes(csi2phy, cfg, settle_cnt); + + /* IRQ_MASK registers - disable all interrupts */ + for (i = 11; i < 22; i++) { + writel_relaxed(0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(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_relaxed(0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 5)); + + writel_relaxed(0, csi2phy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6)); +} + +static int phy_qcom_mipi_csi2_init(struct mipi_csi2phy_device *csi2phy) +{ + return 0; +} + +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, + .isr = phy_qcom_mipi_csi2_isr, + .init = phy_qcom_mipi_csi2_init, +}; + +const struct mipi_csi2phy_clk_freq zero = { 0 }; + +const struct mipi_csi2phy_clk_freq dphy_4nm_x1e_csiphy = { + .freq = { + 300000000, 400000000, 480000000 + }, + .num_freq = 3, +}; + +const struct mipi_csi2phy_clk_freq dphy_4nm_x1e_csiphy_timer = { + .freq = { + 266666667, 400000000 + }, + .num_freq = 2, +}; + +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), + .offset = 0x1000, + .generation = GEN2, + }, + .supply_names = (const char *[]){ + "vdda-0p8", + "vdda-1p2" + }, + .num_supplies = 2, + .clk_names = (const char *[]) { + "camnoc_axi", + "cpas_ahb", + "csiphy", + "csiphy_timer" + }, + .num_clk = 4, + .clk_freq = { + zero, + zero, + dphy_4nm_x1e_csiphy, + dphy_4nm_x1e_csiphy_timer, + }, +}; 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 000000000000..1def2d1258d9 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025, Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-qcom-mipi-csi2.h" + +#define CAMSS_CLOCK_MARGIN_NUMERATOR 105 +#define CAMSS_CLOCK_MARGIN_DENOMINATOR 100 + +static inline void phy_qcom_mipi_csi2_add_clock_margin(u64 *rate) +{ + *rate *= CAMSS_CLOCK_MARGIN_NUMERATOR; + *rate = div_u64(*rate, CAMSS_CLOCK_MARGIN_DENOMINATOR); +} + +static int +phy_qcom_mipi_csi2_set_clock_rates(struct mipi_csi2phy_device *csi2phy, + s64 link_freq) +{ + const struct mipi_csi2phy_soc_cfg *soc_cfg = csi2phy->soc_cfg; + struct device *dev = csi2phy->dev; + int i, j; + int ret; + + for (i = 0; i < soc_cfg->num_clk; i++) { + const struct mipi_csi2phy_clk_freq *clk_freq = &soc_cfg->clk_freq[i]; + const char *clk_name = soc_cfg->clk_names[i]; + struct clk *clk = csi2phy->clks[i].clk; + u64 min_rate = link_freq / 4; + long round_rate; + + phy_qcom_mipi_csi2_add_clock_margin(&min_rate); + + /* This clock should be enabled only not set */ + if (!clk_freq->num_freq) + continue; + + for (j = 0; j < clk_freq->num_freq; j++) + if (min_rate < clk_freq->freq[j]) + break; + + if (j == clk_freq->num_freq) { + dev_err(dev, + "Pixel clock %llu is too high for %s\n", + min_rate, clk_name); + return -EINVAL; + } + + /* if sensor pixel clock is not available + * set highest possible CSIPHY clock rate + */ + if (min_rate == 0) + j = clk_freq->num_freq - 1; + + round_rate = clk_round_rate(clk, clk_freq->freq[j]); + if (round_rate < 0) { + dev_err(dev, "clk round rate failed: %ld\n", + round_rate); + return -EINVAL; + } + + csi2phy->timer_clk_rate = round_rate; + + dev_dbg(dev, "set clk %s %lu Hz\n", + clk_name, round_rate); + + ret = clk_set_rate(clk, csi2phy->timer_clk_rate); + if (ret < 0) { + dev_err(dev, "clk set rate failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +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 = &opts->mipi_dphy; + struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg; + int ret; + int i; + + ret = phy_mipi_dphy_config_validate(dphy_cfg_opts); + if (ret) + return ret; + + if (dphy_cfg_opts->lanes < 1 || dphy_cfg_opts->lanes > CSI2_MAX_DATA_LANES) + return -EINVAL; + + stream_cfg->combo_mode = 0; + stream_cfg->link_freq = dphy_cfg_opts->hs_clk_rate; + stream_cfg->num_data_lanes = dphy_cfg_opts->lanes; + + /* + * phy_configure_opts_mipi_dphy.lanes starts from zero to + * the maximum number of enabled lanes. + * + * TODO: add support for bitmask of enabled lanes and polarities + * of those lanes to the phy_configure_opts_mipi_dphy struct. + * For now take the polarities as zero and the position as fixed + * this is fine as no current upstream implementation maps otherwise. + */ + for (i = 0; i < stream_cfg->num_data_lanes; i++) { + stream_cfg->lane_cfg.data[i].pol = 0; + stream_cfg->lane_cfg.data[i].pos = i; + } + + stream_cfg->lane_cfg.clk.pol = 0; + stream_cfg->lane_cfg.clk.pos = 7; + + 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; + struct device *dev = &phy->dev; + int ret; + + ret = regulator_bulk_enable(csi2phy->soc_cfg->num_supplies, + csi2phy->supplies); + if (ret) + return ret; + + 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(dev, "failed to enable clocks, %d\n", ret); + goto poweroff_phy; + } + + ops->hw_version_read(csi2phy); + + return ops->lanes_enable(csi2phy, &csi2phy->stream_cfg); + +poweroff_phy: + 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); + + clk_bulk_disable_unprepare(csi2phy->soc_cfg->num_clk, + csi2phy->clks); + 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 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; + csi2phy->soc_cfg = device_get_match_data(&pdev->dev); + + if (!csi2phy->soc_cfg) + return -EINVAL; + + num_clk = csi2phy->soc_cfg->num_clk; + csi2phy->clks = devm_kzalloc(dev, sizeof(*csi2phy->clks) * num_clk, GFP_KERNEL); + if (!csi2phy->clks) + return -ENOMEM; + + for (i = 0; i < num_clk; i++) + csi2phy->clks[i].id = csi2phy->soc_cfg->clk_names[i]; + + ret = devm_clk_bulk_get(dev, num_clk, csi2phy->clks); + if (ret) { + dev_err(dev, "Failed to get clocks %d\n", ret); + return ret; + } + + ret = clk_bulk_prepare_enable(num_clk, csi2phy->clks); + if (ret) { + dev_err(dev, "apq8016 clk_enable failed\n"); + return ret; + } + + 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); + dev_err(dev, "failed to create phy, %d\n", ret); + return ret; + } + csi2phy->phy = generic_phy; + + phy_set_drvdata(generic_phy, csi2phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (!IS_ERR(phy_provider)) + dev_dbg(dev, "Registered MIPI CSI2 PHY device\n"); + else + pm_runtime_disable(dev); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id phy_qcom_mipi_csi2_of_match_table[] = { + { .compatible = "qcom,x1e80100-mipi-csi2-combo-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_DESCRIPTION("Bryan O'Donoghue "); +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 000000000000..adcb371c9bf6 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h @@ -0,0 +1,101 @@ +/* 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 + +#define CSI2_MAX_DATA_LANES 4 + +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 { + u8 combo_mode; + 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); + irqreturn_t (*isr)(int irq, void *dev); + int (*init)(struct mipi_csi2phy_device *csi2phy_dev); +}; + +struct mipi_csi2phy_lane_regs { + const s32 reg_addr; + const s32 reg_data; + const u32 delay_us; + const u32 mipi_csi2phy_param_type; +}; + +struct mipi_csi2phy_device_regs { + const struct mipi_csi2phy_lane_regs *init_seq; + const int lane_array_size; + const u32 offset; + enum { + GEN1 = 0, + GEN1_660, + GEN1_670, + GEN2, + } generation; +}; + +#define MAX_CSI2PHY_CLKS 8 +struct mipi_csi2phy_clk_freq { + u32 num_freq; + u32 freq[MAX_CSI2PHY_CLKS]; +}; + +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_csi2phy_clk_freq clk_freq[]; +}; + +struct mipi_csi2phy_device { + struct device *dev; + + struct phy *phy; + void __iomem *base; + + struct clk_bulk_data *clks; + struct regulator_bulk_data *supplies; + u32 timer_clk_rate; + + 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__ */ diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index 7b5af30f1d02..da1823fa5daf 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1744,6 +1745,26 @@ static const u8 qmp_dp_v6_pre_emphasis_hbr_rbr[4][4] = { { 0x22, 0xff, 0xff, 0xff } }; +struct qmp_combo_lane_mapping { + unsigned int lanes_count; + enum typec_orientation orientation; + u32 lanes[4]; +}; + +static const struct qmp_combo_lane_mapping usb3_data_lanes[] = { + { 2, TYPEC_ORIENTATION_NORMAL, { 1, 0 }}, + { 2, TYPEC_ORIENTATION_REVERSE, { 2, 3 }}, +}; + +static const struct qmp_combo_lane_mapping dp_data_lanes[] = { + { 1, TYPEC_ORIENTATION_NORMAL, { 3 }}, + { 1, TYPEC_ORIENTATION_REVERSE, { 0 }}, + { 2, TYPEC_ORIENTATION_NORMAL, { 3, 2 }}, + { 2, TYPEC_ORIENTATION_REVERSE, { 0, 1 }}, + { 4, TYPEC_ORIENTATION_NORMAL, { 3, 2, 1, 0 }}, + { 4, TYPEC_ORIENTATION_REVERSE, { 0, 1, 2, 3 }}, +}; + struct qmp_combo; struct qmp_combo_offsets { @@ -4117,6 +4138,87 @@ static struct phy *qmp_combo_phy_xlate(struct device *dev, const struct of_phand return ERR_PTR(-EINVAL); } +static void qmp_combo_find_lanes_orientation(const struct qmp_combo_lane_mapping *mapping, + unsigned int mapping_count, + u32 *lanes, unsigned int lanes_count, + enum typec_orientation *orientation) +{ + int i; + + for (i = 0; i < mapping_count; i++) { + if (mapping[i].lanes_count != lanes_count) + continue; + if (!memcmp(mapping[i].lanes, lanes, sizeof(u32) * lanes_count)) { + *orientation = mapping[i].orientation; + return; + } + } +} + +static int qmp_combo_get_dt_lanes_mapping(struct device *dev, unsigned int endpoint, + u32 *data_lanes, unsigned int max, + unsigned int *count) +{ + struct device_node *ep; + int ret; + + ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, endpoint); + if (!ep) + return -EINVAL; + + ret = of_property_count_u32_elems(ep, "data-lanes"); + if (ret < 0) + goto err_node_put; + + *count = ret; + + ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, + max_t(unsigned int, *count, max)); + +err_node_put: + of_node_put(ep); + + return ret; +} + +static int qmp_combo_get_dt_dp_orientation(struct device *dev, + enum typec_orientation *orientation) +{ + unsigned int count; + u32 data_lanes[4]; + int ret; + + /* DP is described on the first endpoint of the first port */ + ret = qmp_combo_get_dt_lanes_mapping(dev, 0, data_lanes, 4, &count); + if (ret < 0) + return ret == -EINVAL ? 0 : ret; + + /* Search for a match and only update orientation if found */ + qmp_combo_find_lanes_orientation(dp_data_lanes, ARRAY_SIZE(dp_data_lanes), + data_lanes, count, orientation); + + return 0; +} + +static int qmp_combo_get_dt_usb3_orientation(struct device *dev, + enum typec_orientation *orientation) +{ + unsigned int count; + u32 data_lanes[2]; + int ret; + + /* USB3 is described on the second endpoint of the first port */ + ret = qmp_combo_get_dt_lanes_mapping(dev, 1, data_lanes, 2, &count); + if (ret < 0) + return ret == -EINVAL ? 0 : ret; + + /* Search for a match and only update orientation if found */ + qmp_combo_find_lanes_orientation(usb3_data_lanes, ARRAY_SIZE(usb3_data_lanes), + data_lanes, count, orientation); + + return 0; +} + static int qmp_combo_probe(struct platform_device *pdev) { struct qmp_combo *qmp; @@ -4167,9 +4269,41 @@ static int qmp_combo_probe(struct platform_device *pdev) if (ret) goto err_node_put; - ret = qmp_combo_typec_register(qmp); - if (ret) - goto err_node_put; + qmp->qmpphy_mode = QMPPHY_MODE_USB3DP; + + if (of_property_present(dev->of_node, "mode-switch") || + of_property_present(dev->of_node, "orientation-switch")) { + ret = qmp_combo_typec_register(qmp); + if (ret) + goto err_node_put; + } else { + enum typec_orientation dp_orientation = TYPEC_ORIENTATION_NONE; + enum typec_orientation usb3_orientation = TYPEC_ORIENTATION_NONE; + + ret = qmp_combo_get_dt_dp_orientation(dev, &dp_orientation); + if (ret) + goto err_node_put; + + ret = qmp_combo_get_dt_usb3_orientation(dev, &usb3_orientation); + if (ret) + goto err_node_put; + + if (dp_orientation == TYPEC_ORIENTATION_NONE && + usb3_orientation != TYPEC_ORIENTATION_NONE) { + qmp->qmpphy_mode = QMPPHY_MODE_USB3_ONLY; + qmp->orientation = usb3_orientation; + } else if (usb3_orientation == TYPEC_ORIENTATION_NONE && + dp_orientation != TYPEC_ORIENTATION_NONE) { + qmp->qmpphy_mode = QMPPHY_MODE_DP_ONLY; + qmp->orientation = dp_orientation; + } else if (dp_orientation != TYPEC_ORIENTATION_NONE && + dp_orientation == usb3_orientation) { + qmp->qmpphy_mode = QMPPHY_MODE_USB3DP; + qmp->orientation = dp_orientation; + } else { + dev_warn(dev, "unable to determine orientation & mode from data-lanes"); + } + } ret = drm_aux_bridge_register(dev); if (ret) @@ -4189,11 +4323,6 @@ static int qmp_combo_probe(struct platform_device *pdev) if (ret) goto err_node_put; - /* - * The hw default is USB3_ONLY, but USB3+DP mode lets us more easily - * check both sub-blocks' init tables for blunders at probe time. - */ - qmp->qmpphy_mode = QMPPHY_MODE_USB3DP; qmp->usb_phy = devm_phy_create(dev, usb_np, &qmp_combo_usb_phy_ops); if (IS_ERR(qmp->usb_phy)) { diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index 6e78a01755c7..9ef174728d64 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -1198,7 +1198,7 @@ static int imx_dsp_rproc_probe(struct platform_device *pdev) } init_completion(&priv->pm_comp); - rproc->auto_boot = false; + rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED; ret = rproc_add(rproc); if (ret) { dev_err(dev, "rproc_add failed\n"); diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index bb25221a4a89..23520cec886e 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -1097,8 +1097,12 @@ static int imx_rproc_probe(struct platform_device *pdev) if (ret) goto err_put_scu; - if (rproc->state != RPROC_DETACHED) - rproc->auto_boot = of_property_read_bool(np, "fsl,auto-boot"); + if (rproc->state != RPROC_DETACHED) { + if (of_property_read_bool(np, "fsl,auto-boot")) + rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START; + else + rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED; + } if (dcfg->flags & IMX_RPROC_NEED_SYSTEM_OFF) { /* diff --git a/drivers/remoteproc/ingenic_rproc.c b/drivers/remoteproc/ingenic_rproc.c index 1b78d8ddeacf..351b95297137 100644 --- a/drivers/remoteproc/ingenic_rproc.c +++ b/drivers/remoteproc/ingenic_rproc.c @@ -177,7 +177,10 @@ static int ingenic_rproc_probe(struct platform_device *pdev) if (!rproc) return -ENOMEM; - rproc->auto_boot = auto_boot; + if (auto_boot) + rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START; + else + rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED; vpu = rproc->priv; vpu->dev = &pdev->dev; diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c index 5e3eb7b86a0e..2ac2d2d5d090 100644 --- a/drivers/remoteproc/pru_rproc.c +++ b/drivers/remoteproc/pru_rproc.c @@ -1031,7 +1031,7 @@ static int pru_rproc_probe(struct platform_device *pdev) * remote-processor as part of its state machine either through the * remoteproc sysfs interface or through the equivalent kernel API. */ - rproc->auto_boot = false; + rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED; pru = rproc->priv; pru->dev = dev; diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c index 58d5b85e58cd..ed1b578a831d 100644 --- a/drivers/remoteproc/qcom_q6v5.c +++ b/drivers/remoteproc/qcom_q6v5.c @@ -36,6 +36,25 @@ static int q6v5_load_state_toggle(struct qcom_q6v5 *q6v5, bool enable) return ret; } +static int q6v5_init(struct qcom_q6v5 *q6v5, bool handover_issued) +{ + int ret; + + /* Always send correct load state in case it was not done before */ + ret = q6v5_load_state_toggle(q6v5, true); + if (ret) + return ret; + + reinit_completion(&q6v5->start_done); + reinit_completion(&q6v5->stop_done); + + q6v5->running = true; + q6v5->handover_issued = handover_issued; + + enable_irq(q6v5->handover_irq); + return 0; +} + /** * qcom_q6v5_prepare() - reinitialize the qcom_q6v5 context before start * @q6v5: reference to qcom_q6v5 context to be reinitialized @@ -52,23 +71,34 @@ int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5) return ret; } - ret = q6v5_load_state_toggle(q6v5, true); + ret = q6v5_init(q6v5, false); if (ret) { icc_set_bw(q6v5->path, 0, 0); return ret; } - reinit_completion(&q6v5->start_done); - reinit_completion(&q6v5->stop_done); + return 0; +} +EXPORT_SYMBOL_GPL(qcom_q6v5_prepare); - q6v5->running = true; - q6v5->handover_issued = false; +/** + * qcom_q6v5_attach() - attach to already running qcom_q6v5 remoteproc + * @q6v5: reference to qcom_q6v5 context to be attached + * + * Return: 0 on success, negative errno on failure + */ +int qcom_q6v5_attach(struct qcom_q6v5 *q6v5) +{ + int ret; - enable_irq(q6v5->handover_irq); + ret = q6v5_init(q6v5, true); + if (ret) + return ret; + complete(&q6v5->start_done); return 0; } -EXPORT_SYMBOL_GPL(qcom_q6v5_prepare); +EXPORT_SYMBOL_GPL(qcom_q6v5_attach); /** * qcom_q6v5_unprepare() - unprepare the qcom_q6v5 context after stop @@ -201,8 +231,8 @@ int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon) q6v5->running = false; - /* Don't perform SMP2P dance if remote isn't running */ - if (q6v5->rproc->state != RPROC_RUNNING || qcom_sysmon_shutdown_acked(sysmon)) + /* Don't perform SMP2P dance if remote crashed or already stopped */ + if (q6v5->rproc->state == RPROC_CRASHED || qcom_sysmon_shutdown_acked(sysmon)) return 0; qcom_smem_state_update_bits(q6v5->state, @@ -234,6 +264,35 @@ unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5) } EXPORT_SYMBOL_GPL(qcom_q6v5_panic); +/** + * qcom_q6v5_read_smp2p_state() - check the initial state during boot using SMP2P + * @q6v5: reference to qcom_q6v5 context + * + * Read the current state of the SMP2P interrupts and attempt to determine if + * the remoteproc is already running. If yes, the remoteproc state is set to + * RPROC_DETACHED. Drivers can use this to add support for attaching to + * remoteprocs during boot. + * + * There is a small chance for false-positives, so drivers should combine this + * with device-specific status information if possible. + */ +void qcom_q6v5_read_smp2p_state(struct qcom_q6v5 *q6v5) +{ + bool handover = false; + bool ready = false; + bool fatal = false; + bool stop = false; + + irq_get_irqchip_state(q6v5->handover_irq, IRQCHIP_STATE_LINE_LEVEL, &handover); + irq_get_irqchip_state(q6v5->ready_irq, IRQCHIP_STATE_LINE_LEVEL, &ready); + irq_get_irqchip_state(q6v5->fatal_irq, IRQCHIP_STATE_LINE_LEVEL, &fatal); + irq_get_irqchip_state(q6v5->stop_irq, IRQCHIP_STATE_LINE_LEVEL, &stop); + + if (ready && handover && !stop && !fatal) + q6v5->rproc->state = RPROC_DETACHED; +} +EXPORT_SYMBOL_GPL(qcom_q6v5_read_smp2p_state); + /** * qcom_q6v5_init() - initializer of the q6v5 common struct * @q6v5: handle to be initialized diff --git a/drivers/remoteproc/qcom_q6v5.h b/drivers/remoteproc/qcom_q6v5.h index 5a859c41896e..f034bb628605 100644 --- a/drivers/remoteproc/qcom_q6v5.h +++ b/drivers/remoteproc/qcom_q6v5.h @@ -47,7 +47,10 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, void (*handover)(struct qcom_q6v5 *q6v5)); void qcom_q6v5_deinit(struct qcom_q6v5 *q6v5); +void qcom_q6v5_read_smp2p_state(struct qcom_q6v5 *q6v5); + int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5); +int qcom_q6v5_attach(struct qcom_q6v5 *q6v5); int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5); int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon); int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout); diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index e98b7e03162c..18e8d03e0bea 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -678,7 +678,10 @@ static int adsp_probe(struct platform_device *pdev) return -ENOMEM; } - rproc->auto_boot = desc->auto_boot; + if (desc->auto_boot) + rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START; + else + rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED; rproc->has_iommu = desc->has_iommu; rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 3087d895b87f..47553acdadd2 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -2079,7 +2079,7 @@ static int q6v5_probe(struct platform_device *pdev) return -ENOMEM; } - rproc->auto_boot = false; + rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED; rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); qproc = rproc->priv; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 158bcd6cc85c..c80af96543e4 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -226,10 +226,13 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) /* Store firmware handle to be used in qcom_pas_start() */ pas->firmware = fw; - if (pas->lite_pas_id) - qcom_scm_pas_shutdown(pas->lite_pas_id); - if (pas->lite_dtb_pas_id) - qcom_scm_pas_shutdown(pas->lite_dtb_pas_id); + /* + * We don't support loading the "lite" firmware, so we don't need to + * keep trying to shut it down. If it was running, it should have + * already been stopped by adsp_stop(). + */ + pas->lite_pas_id = 0; + pas->lite_dtb_pas_id = 0; if (pas->dtb_pas_id) { ret = request_firmware(&pas->dtb_firmware, pas->dtb_firmware_name, pas->dev); @@ -379,6 +382,28 @@ static void qcom_pas_handover(struct qcom_q6v5 *q6v5) qcom_pas_pds_disable(pas, pas->proxy_pds, pas->proxy_pd_count); } +static int qcom_pas_shutdown(int pas_id, int lite_pas_id) +{ + int ret, lite_ret = -ENODEV; + + /* + * We don't know if the boot firmware started the "full" or "lite" + * firmware, so we don't know if we need to shutdown the lite_pas_id or + * the normal pas_id. Unfortunately, the return codes of the SCM calls + * are also not helpful to figure that out. Since shutting down a + * stopped remoteproc is a no-op, we just shutdown both and if one of + * the calls succeeds, we assume it's okay. + */ + if (lite_pas_id) + lite_ret = qcom_scm_pas_shutdown(lite_pas_id); + + ret = qcom_scm_pas_shutdown(pas_id); + if (ret && lite_ret) + return ret; + + return 0; +} + static int qcom_pas_stop(struct rproc *rproc) { struct qcom_pas *pas = rproc->priv; @@ -389,7 +414,7 @@ static int qcom_pas_stop(struct rproc *rproc) if (ret == -ETIMEDOUT) dev_err(pas->dev, "timed out on wait\n"); - ret = qcom_scm_pas_shutdown(pas->pas_id); + ret = qcom_pas_shutdown(pas->pas_id, pas->lite_pas_id); if (ret && pas->decrypt_shutdown) ret = qcom_pas_shutdown_poll_decrypt(pas); @@ -397,14 +422,16 @@ static int qcom_pas_stop(struct rproc *rproc) dev_err(pas->dev, "failed to shutdown: %d\n", ret); if (pas->dtb_pas_id) { - ret = qcom_scm_pas_shutdown(pas->dtb_pas_id); + ret = qcom_pas_shutdown(pas->dtb_pas_id, pas->lite_dtb_pas_id); if (ret) dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret); } - handover = qcom_q6v5_unprepare(&pas->q6v5); - if (handover) - qcom_pas_handover(&pas->q6v5); + if (rproc->state != RPROC_DETACHED) { + handover = qcom_q6v5_unprepare(&pas->q6v5); + if (handover) + qcom_pas_handover(&pas->q6v5); + } if (pas->smem_host_id) ret = qcom_smem_bust_hwspin_lock_by_host(pas->smem_host_id); @@ -412,6 +439,13 @@ static int qcom_pas_stop(struct rproc *rproc) return ret; } +static int qcom_pas_attach(struct rproc *rproc) +{ + struct qcom_pas *pas = rproc->priv; + + return qcom_q6v5_attach(&pas->q6v5); +} + static void *qcom_pas_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) { struct qcom_pas *pas = rproc->priv; @@ -438,6 +472,7 @@ static const struct rproc_ops qcom_pas_ops = { .unprepare = qcom_pas_unprepare, .start = qcom_pas_start, .stop = qcom_pas_stop, + .attach = qcom_pas_attach, .da_to_va = qcom_pas_da_to_va, .parse_fw = qcom_register_dump_segments, .load = qcom_pas_load, @@ -448,6 +483,7 @@ static const struct rproc_ops qcom_pas_minidump_ops = { .unprepare = qcom_pas_unprepare, .start = qcom_pas_start, .stop = qcom_pas_stop, + .attach = qcom_pas_attach, .da_to_va = qcom_pas_da_to_va, .parse_fw = qcom_register_dump_segments, .load = qcom_pas_load, @@ -455,6 +491,13 @@ static const struct rproc_ops qcom_pas_minidump_ops = { .coredump = qcom_pas_minidump, }; +static const struct rproc_ops qcom_pas_ops_no_reset = { + .attach = qcom_pas_attach, + .da_to_va = qcom_pas_da_to_va, + .stop = qcom_pas_stop, + .panic = qcom_pas_panic, +}; + static int qcom_pas_init_clock(struct qcom_pas *pas) { pas->xo = devm_clk_get(pas->dev, "xo"); @@ -709,6 +752,9 @@ static int qcom_pas_probe(struct platform_device *pdev) if (desc->minidump_id) ops = &qcom_pas_minidump_ops; + if (device_property_read_bool(&pdev->dev, "qcom,broken-reset")) + ops = &qcom_pas_ops_no_reset; + rproc = devm_rproc_alloc(&pdev->dev, desc->sysmon_name, ops, fw_name, sizeof(*pas)); if (!rproc) { @@ -716,7 +762,14 @@ static int qcom_pas_probe(struct platform_device *pdev) return -ENOMEM; } - rproc->auto_boot = desc->auto_boot; + if (desc->auto_boot) { + if (ops->start) + rproc->auto_boot = RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE; + else + rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START; + } else { + rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED; + } rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); pas = rproc->priv; @@ -769,6 +822,21 @@ static int qcom_pas_probe(struct platform_device *pdev) if (ret) goto detach_proxy_pds; + /* + * Unfortunately, the PAS interface does not provide a reliable way to + * check if a certain pas_id is currently running. Reading the smp2p + * state is the best we can do to check if the remoteproc is already + * running during boot. + */ + qcom_q6v5_read_smp2p_state(&pas->q6v5); + + if (rproc->state == RPROC_OFFLINE && !ops->start) { + dev_err(&pdev->dev, "reset broken and remoteproc not running during boot, exiting\n"); + /* Release all resources, but return 0 so we don't block sync_state() */ + ret = 0; + goto deinit_q6v5; + } + qcom_add_glink_subdev(rproc, &pas->glink_subdev, desc->ssr_name); qcom_add_smd_subdev(rproc, &pas->smd_subdev); qcom_add_pdm_subdev(rproc, &pas->pdm_subdev); @@ -792,6 +860,7 @@ static int qcom_pas_probe(struct platform_device *pdev) qcom_remove_pdm_subdev(rproc, &pas->pdm_subdev); qcom_remove_smd_subdev(rproc, &pas->smd_subdev); qcom_remove_glink_subdev(rproc, &pas->glink_subdev); +deinit_q6v5: qcom_q6v5_deinit(&pas->q6v5); detach_proxy_pds: qcom_pas_pds_detach(pas, pas->proxy_pds, pas->proxy_pd_count); @@ -799,6 +868,7 @@ static int qcom_pas_probe(struct platform_device *pdev) qcom_pas_unassign_memory_region(pas); free_rproc: device_init_wakeup(pas->dev, false); + pas->rproc = NULL; return ret; } @@ -807,6 +877,9 @@ static void qcom_pas_remove(struct platform_device *pdev) { struct qcom_pas *pas = platform_get_drvdata(pdev); + if (!pas->rproc) + return; + rproc_del(pas->rproc); qcom_q6v5_deinit(&pas->q6v5); diff --git a/drivers/remoteproc/rcar_rproc.c b/drivers/remoteproc/rcar_rproc.c index 921d853594f4..3ee6d754d605 100644 --- a/drivers/remoteproc/rcar_rproc.c +++ b/drivers/remoteproc/rcar_rproc.c @@ -182,7 +182,7 @@ static int rcar_rproc_probe(struct platform_device *pdev) dev_set_drvdata(dev, rproc); /* Manually start the rproc */ - rproc->auto_boot = false; + rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED; ret = devm_rproc_add(dev, rproc); if (ret) { diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 825672100528..f88a53d2b73d 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -1688,7 +1688,8 @@ static int rproc_trigger_auto_boot(struct rproc *rproc) * for a firmware image to be loaded, we can simply initiate the process * of attaching to it immediately. */ - if (rproc->state == RPROC_DETACHED) + if (rproc->state == RPROC_DETACHED && + rproc->auto_boot != RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE) return rproc_boot(rproc); /* @@ -1713,8 +1714,9 @@ static int rproc_stop(struct rproc *rproc, bool crashed) if (!rproc->ops->stop) return -EINVAL; - /* Stop any subdevices for the remote processor */ - rproc_stop_subdevices(rproc, crashed); + /* Stop any subdevices for the remote processor if it was attached */ + if (rproc->state != RPROC_DETACHED) + rproc_stop_subdevices(rproc, crashed); /* the installed resource table is no longer accessible */ ret = rproc_reset_rsc_table_on_stop(rproc); @@ -1731,7 +1733,8 @@ static int rproc_stop(struct rproc *rproc, bool crashed) return ret; } - rproc_unprepare_subdevices(rproc); + if (rproc->state != RPROC_DETACHED) + rproc_unprepare_subdevices(rproc); rproc->state = RPROC_OFFLINE; @@ -1908,9 +1911,9 @@ static void rproc_crash_handler_work(struct work_struct *work) */ int rproc_boot(struct rproc *rproc) { - const struct firmware *firmware_p; + const struct firmware *firmware_p = NULL; struct device *dev; - int ret; + int ret, fw_ret = 1; if (!rproc) { pr_err("invalid rproc handle\n"); @@ -1937,6 +1940,19 @@ int rproc_boot(struct rproc *rproc) goto unlock_mutex; } + /* Check early if we have firmware avilable if needed */ + if (rproc->auto_boot == RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE && + rproc->state == RPROC_DETACHED) { + fw_ret = request_firmware(&firmware_p, rproc->firmware, dev); + if (fw_ret == 0) { + dev_info(dev, "restarting %s with new firmware\n", rproc->name); + + ret = rproc_stop(rproc, false); + if (ret) + goto downref_rproc; + } + } + if (rproc->state == RPROC_DETACHED) { dev_info(dev, "attaching to %s\n", rproc->name); @@ -1944,19 +1960,20 @@ int rproc_boot(struct rproc *rproc) } else { dev_info(dev, "powering up %s\n", rproc->name); - /* load firmware */ - ret = request_firmware(&firmware_p, rproc->firmware, dev); - if (ret < 0) { - dev_err(dev, "request_firmware failed: %d\n", ret); + /* load firmware (if not already happened above) */ + if (fw_ret == 1) + fw_ret = request_firmware(&firmware_p, rproc->firmware, dev); + if (fw_ret < 0) { + dev_err(dev, "request_firmware failed: %d\n", fw_ret); + ret = fw_ret; goto downref_rproc; } ret = rproc_fw_boot(rproc, firmware_p); - - release_firmware(firmware_p); } downref_rproc: + release_firmware(firmware_p); if (ret) atomic_dec(&rproc->power); unlock_mutex: @@ -2264,6 +2281,10 @@ static int rproc_validate(struct rproc *rproc) return -EINVAL; } + if (rproc->auto_boot == RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE && + (!rproc->ops->stop || !rproc->ops->start || !rproc->ops->attach)) + return -EINVAL; + return 0; } @@ -2475,7 +2496,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, return NULL; rproc->priv = &rproc[1]; - rproc->auto_boot = true; + rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START; rproc->elf_class = ELFCLASSNONE; rproc->elf_machine = EM_NONE; diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 431648607d53..6384f2fbd62e 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -704,7 +704,8 @@ static int stm32_rproc_get_syscon(struct device_node *np, const char *prop, } static int stm32_rproc_parse_dt(struct platform_device *pdev, - struct stm32_rproc *ddata, bool *auto_boot) + struct stm32_rproc *ddata, + enum rproc_auto_boot *auto_boot) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; @@ -785,7 +786,10 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev, if (err) dev_info(dev, "failed to get pdds\n"); - *auto_boot = of_property_read_bool(np, "st,auto-boot"); + if (of_property_read_bool(np, "st,auto-boot")) + *auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START; + else + *auto_boot = RPROC_AUTO_BOOT_DISABLED; /* * See if we can check the M4 status, i.e if it was started diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c index 2d5bfbefcacc..8815648295c8 100644 --- a/drivers/remoteproc/wkup_m3_rproc.c +++ b/drivers/remoteproc/wkup_m3_rproc.c @@ -170,7 +170,7 @@ static int wkup_m3_rproc_probe(struct platform_device *pdev) if (!rproc) return -ENOMEM; - rproc->auto_boot = false; + rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED; rproc->sysfs_read_only = true; wkupm3 = rproc->priv; diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 0b7b173d0d26..cc6fad782cd5 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -940,7 +940,7 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) r5_rproc->recovery_disabled = true; r5_rproc->has_iommu = false; - r5_rproc->auto_boot = false; + r5_rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED; r5_core = r5_rproc->priv; r5_core->dev = cdev; r5_core->np = dev_of_node(cdev); diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 5d661681a9b6..223e63c76e1b 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -95,6 +95,14 @@ EXPORT_SYMBOL(rpmsg_release_channel); * equals to the src address of their rpmsg channel), the driver's handler * is invoked to process it. * + * Note that the endpoint for simple rpmsg drivers is created before calling + * probe() and closed after calling remove(), so special care must be taken + * to handle calls to the rx callback before/in parallel of probe() and + * after/in parallel of remove(). If more control over the endpoint creation + * is required to avoid race conditions, drivers can omit the callback and + * explicitly call rpmsg_dev_open_ept() in probe() and rpmsg_destroy_ept() in + * remove(), together with locks as needed. + * * That said, more complicated drivers might need to allocate * additional rpmsg addresses, and bind them to different rx callbacks. * To accomplish that, those drivers need to call this function. @@ -463,6 +471,32 @@ static int rpmsg_uevent(const struct device *dev, struct kobj_uevent_env *env) rpdev->id.name); } +struct rpmsg_endpoint *rpmsg_dev_open_ept(struct rpmsg_device *rpdev, + rpmsg_rx_cb_t cb, void *priv) +{ + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct rpmsg_channel_info chinfo = { + .src = rpdev->src, + .dst = RPMSG_ADDR_ANY, + }; + struct rpmsg_endpoint *ept; + + strscpy(chinfo.name, rpdev->id.name, sizeof(chinfo.name)); + + ept = rpmsg_create_ept(rpdev, cb, priv, chinfo); + if (!ept) { + dev_err(&rpdev->dev, "failed to create endpoint\n"); + return NULL; + } + + rpdev->ept = ept; + rpdev->src = ept->addr; + + ept->flow_cb = rpdrv->flowcontrol; + return ept; +} +EXPORT_SYMBOL(rpmsg_dev_open_ept); + /* * when an rpmsg driver is probed with a channel, we seamlessly create * it an endpoint, binding its rx callback to a unique local rpmsg @@ -475,7 +509,6 @@ static int rpmsg_dev_probe(struct device *dev) { struct rpmsg_device *rpdev = to_rpmsg_device(dev); struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); - struct rpmsg_channel_info chinfo = {}; struct rpmsg_endpoint *ept = NULL; int err; @@ -485,21 +518,11 @@ static int rpmsg_dev_probe(struct device *dev) goto out; if (rpdrv->callback) { - strscpy(chinfo.name, rpdev->id.name, sizeof(chinfo.name)); - chinfo.src = rpdev->src; - chinfo.dst = RPMSG_ADDR_ANY; - - ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo); + ept = rpmsg_dev_open_ept(rpdev, rpdrv->callback, NULL); if (!ept) { - dev_err(dev, "failed to create endpoint\n"); err = -ENOMEM; goto out; } - - rpdev->ept = ept; - rpdev->src = ept->addr; - - ept->flow_cb = rpdrv->flowcontrol; } err = rpdrv->probe(rpdev); @@ -508,7 +531,7 @@ static int rpmsg_dev_probe(struct device *dev) goto destroy_ept; } - if (ept && rpdev->ops->announce_create) { + if (rpdev->ept && rpdev->ops->announce_create) { err = rpdev->ops->announce_create(rpdev); if (err) { dev_err(dev, "failed to announce creation\n"); @@ -533,13 +556,13 @@ static void rpmsg_dev_remove(struct device *dev) struct rpmsg_device *rpdev = to_rpmsg_device(dev); struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); - if (rpdev->ops->announce_destroy) + if (rpdev->ept && rpdev->ops->announce_destroy) rpdev->ops->announce_destroy(rpdev); if (rpdrv->remove) rpdrv->remove(rpdev); - if (rpdev->ept) + if (rpdrv->callback && rpdev->ept) rpmsg_destroy_ept(rpdev->ept); } diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c index c0a4be5df926..cf23c3c11024 100644 --- a/drivers/soc/qcom/pmic_glink.c +++ b/drivers/soc/qcom/pmic_glink.c @@ -342,8 +342,18 @@ static int pmic_glink_probe(struct platform_device *pdev) __pmic_glink = pg; mutex_unlock(&__pmic_glink_lock); + ret = register_rpmsg_driver(&pmic_glink_rpmsg_driver); + if (ret) { + ret = dev_err_probe(&pdev->dev, ret, "failed to register rpmsg driver\n"); + goto out_release_pmic_glink; + } + return 0; +out_release_pmic_glink: + mutex_lock(&__pmic_glink_lock); + __pmic_glink = NULL; + mutex_unlock(&__pmic_glink_lock); out_release_aux_devices: if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_BATT)) pmic_glink_del_aux_device(pg, &pg->ps_aux); @@ -372,6 +382,8 @@ static void pmic_glink_remove(struct platform_device *pdev) if (pg->client_mask & BIT(PMIC_GLINK_CLIENT_UCSI)) pmic_glink_del_aux_device(pg, &pg->ucsi_aux); + unregister_rpmsg_driver(&pmic_glink_rpmsg_driver); + guard(mutex)(&__pmic_glink_lock); __pmic_glink = NULL; } @@ -394,31 +406,7 @@ static struct platform_driver pmic_glink_driver = { .of_match_table = pmic_glink_of_match, }, }; - -static int pmic_glink_init(void) -{ - int ret; - - ret = platform_driver_register(&pmic_glink_driver); - if (ret < 0) - return ret; - - ret = register_rpmsg_driver(&pmic_glink_rpmsg_driver); - if (ret < 0) { - platform_driver_unregister(&pmic_glink_driver); - return ret; - } - - return 0; -} -module_init(pmic_glink_init); - -static void pmic_glink_exit(void) -{ - unregister_rpmsg_driver(&pmic_glink_rpmsg_driver); - platform_driver_unregister(&pmic_glink_driver); -} -module_exit(pmic_glink_exit); +module_platform_driver(pmic_glink_driver); MODULE_DESCRIPTION("Qualcomm PMIC GLINK driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index 7f11acd33323..22e4c1af745c 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -303,7 +303,7 @@ static void pmic_glink_altmode_sc8180xp_notify(struct pmic_glink_altmode *altmod alt_port->mode = mode; alt_port->hpd_state = hpd_state; alt_port->hpd_irq = hpd_irq; - schedule_work(&alt_port->work); + queue_work(system_freezable_wq, &alt_port->work); } #define SC8280XP_DPAM_MASK 0x3f @@ -346,7 +346,7 @@ static void pmic_glink_altmode_sc8280xp_notify(struct pmic_glink_altmode *altmod alt_port->mode = mode; alt_port->hpd_state = hpd_state; alt_port->hpd_irq = hpd_irq; - schedule_work(&alt_port->work); + queue_work(system_freezable_wq, &alt_port->work); } static void pmic_glink_altmode_callback(const void *data, size_t len, void *priv) diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index cb515c2340c1..983bf72c5c0d 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -73,7 +73,7 @@ struct smp2p_smem_item { u32 flags; struct { - u8 name[SMP2P_MAX_ENTRY_NAME]; + char name[SMP2P_MAX_ENTRY_NAME]; u32 value; } entries[SMP2P_MAX_ENTRY]; } __packed; @@ -202,8 +202,6 @@ static void qcom_smp2p_do_ssr_ack(struct qcom_smp2p *smp2p) if (smp2p->ssr_ack) val |= BIT(SMP2P_FLAGS_RESTART_ACK_BIT); out->flags = val; - - qcom_smp2p_kick(smp2p); } static void qcom_smp2p_negotiate(struct qcom_smp2p *smp2p) @@ -214,8 +212,10 @@ static void qcom_smp2p_negotiate(struct qcom_smp2p *smp2p) if (in->version == out->version) { out->features &= in->features; - if (out->features & SMP2P_FEATURE_SSR_ACK) + if (out->features & SMP2P_FEATURE_SSR_ACK) { smp2p->ssr_ack_enabled = true; + smp2p->ssr_ack = !!(out->flags & BIT(SMP2P_FLAGS_RESTART_ACK_BIT)); + } smp2p->negotiation_done = true; trace_smp2p_negotiate(smp2p->dev, out->features); @@ -228,7 +228,6 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p) struct smp2p_entry *entry; int irq_pin; u32 status; - char buf[SMP2P_MAX_ENTRY_NAME]; u32 val; int i; @@ -237,8 +236,7 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p) /* Match newly created entries */ for (i = smp2p->valid_entries; i < in->valid_entries; i++) { list_for_each_entry(entry, &smp2p->inbound, node) { - memcpy(buf, in->entries[i].name, sizeof(buf)); - if (!strcmp(buf, entry->name)) { + if (!strncmp(in->entries[i].name, entry->name, SMP2P_MAX_ENTRY_NAME)) { entry->value = &in->entries[i].value; break; } @@ -276,6 +274,24 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p) } } +static bool qcom_smp2p_scan(struct qcom_smp2p *smp2p) +{ + bool ack_restart = false; + + if (!smp2p->negotiation_done) + qcom_smp2p_negotiate(smp2p); + + if (smp2p->negotiation_done) { + ack_restart = qcom_smp2p_check_ssr(smp2p); + qcom_smp2p_notify_in(smp2p); + + if (ack_restart) + qcom_smp2p_do_ssr_ack(smp2p); + } + + return ack_restart; +} + /** * qcom_smp2p_intr() - interrupt handler for incoming notifications * @irq: unused @@ -292,7 +308,6 @@ static irqreturn_t qcom_smp2p_intr(int irq, void *data) struct qcom_smp2p *smp2p = data; unsigned int smem_id = smp2p->smem_items[SMP2P_INBOUND]; unsigned int pid = smp2p->remote_pid; - bool ack_restart; size_t size; in = smp2p->in; @@ -309,16 +324,8 @@ static irqreturn_t qcom_smp2p_intr(int irq, void *data) smp2p->in = in; } - if (!smp2p->negotiation_done) - qcom_smp2p_negotiate(smp2p); - - if (smp2p->negotiation_done) { - ack_restart = qcom_smp2p_check_ssr(smp2p); - qcom_smp2p_notify_in(smp2p); - - if (ack_restart) - qcom_smp2p_do_ssr_ack(smp2p); - } + if (qcom_smp2p_scan(smp2p)) + qcom_smp2p_kick(smp2p); out: return IRQ_HANDLED; @@ -368,12 +375,33 @@ static void smp2p_irq_print_chip(struct irq_data *irqd, struct seq_file *p) seq_printf(p, "%8s", dev_name(entry->smp2p->dev)); } +static int smp2p_get_irqchip_state(struct irq_data *irqd, + enum irqchip_irq_state which, bool *state) +{ + struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd); + irq_hw_number_t irq = irqd_to_hwirq(irqd); + u32 val; + + if (which != IRQCHIP_STATE_LINE_LEVEL) + return -EINVAL; + + if (entry->value) { + val = readl(entry->value); + *state = !!(val & BIT(irq)); + } else { + *state = 0; + } + + return 0; +} + static struct irq_chip smp2p_irq_chip = { .name = "smp2p", .irq_mask = smp2p_mask_irq, .irq_unmask = smp2p_unmask_irq, .irq_set_type = smp2p_set_irq_type, .irq_print_chip = smp2p_irq_print_chip, + .irq_get_irqchip_state = smp2p_get_irqchip_state, }; static int smp2p_irq_map(struct irq_domain *d, @@ -439,16 +467,24 @@ static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p, struct device_node *node) { struct smp2p_smem_item *out = smp2p->out; - char buf[SMP2P_MAX_ENTRY_NAME] = {}; + int i; - /* Allocate an entry from the smem item */ - strscpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME); - memcpy(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME); + /* Check if we have an entry already (e.g. allocated by boot firmware) */ + for (i = 0; i < out->valid_entries; i++) + if (!strncmp(out->entries[i].name, entry->name, SMP2P_MAX_ENTRY_NAME)) + break; - /* Make the logical entry reference the physical value */ - entry->value = &out->entries[out->valid_entries].value; + if (i == out->valid_entries) { + /* Allocate an entry from the smem item */ + if (i == out->total_entries) + return -ENOMEM; + + strscpy(out->entries[i].name, entry->name, SMP2P_MAX_ENTRY_NAME); + out->valid_entries++; + } - out->valid_entries++; + /* Make the logical entry reference the physical value */ + entry->value = &out->entries[i].value; entry->state = qcom_smem_state_register(node, &smp2p_state_ops, entry); if (IS_ERR(entry->state)) { @@ -477,6 +513,29 @@ static int qcom_smp2p_alloc_outbound_item(struct qcom_smp2p *smp2p) return PTR_ERR(out); } + smp2p->out = out; + + if (ret == -EEXIST && smp2p->in) { + if (out->magic == SMP2P_MAGIC && + out->version == 1 && + out->local_pid == smp2p->local_pid && + out->remote_pid == smp2p->remote_pid && + out->total_entries >= SMP2P_MAX_ENTRY && + out->valid_entries <= out->total_entries) { + /* + * Reuse existing smem item, but adjust features to + * what we support. This will be updated later when we + * negotiate with the features of the remote side. + */ + out->features = SMP2P_ALL_FEATURES; + return 0; + } else { + dev_warn(smp2p->dev, "Unexpected local smp2p item allocated by firmware, resetting. " + "(magic: %#x, version: %d, local_pid: %d, remote_pid: %d, total_entries: %d, valid_entries: %d)\n", + out->magic, out->version, out->local_pid, out->remote_pid, out->total_entries, out->valid_entries); + } + } + memset(out, 0, sizeof(*out)); out->magic = SMP2P_MAGIC; out->local_pid = smp2p->local_pid; @@ -494,8 +553,6 @@ static int qcom_smp2p_alloc_outbound_item(struct qcom_smp2p *smp2p) qcom_smp2p_kick(smp2p); - smp2p->out = out; - return 0; } @@ -535,6 +592,7 @@ static int smp2p_parse_ipc(struct qcom_smp2p *smp2p) static int qcom_smp2p_probe(struct platform_device *pdev) { + struct smp2p_smem_item *in; struct smp2p_entry *entry; struct qcom_smp2p *smp2p; const char *key; @@ -585,6 +643,10 @@ static int qcom_smp2p_probe(struct platform_device *pdev) return ret; } + in = qcom_smem_get(smp2p->remote_pid, smp2p->smem_items[SMP2P_INBOUND], NULL); + if (!IS_ERR(in)) + smp2p->in = in; + ret = qcom_smp2p_alloc_outbound_item(smp2p); if (ret < 0) goto release_mbox; @@ -618,8 +680,9 @@ static int qcom_smp2p_probe(struct platform_device *pdev) } } - /* Kick the outgoing edge after allocating entries */ - qcom_smp2p_kick(smp2p); + if (smp2p->in) + /* Ignore return status since we kick unconditionally below */ + qcom_smp2p_scan(smp2p); ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, qcom_smp2p_intr, @@ -630,6 +693,9 @@ static int qcom_smp2p_probe(struct platform_device *pdev) goto unwind_interfaces; } + /* Kick the outgoing edge after allocating entries */ + qcom_smp2p_kick(smp2p); + /* * Treat smp2p interrupt as wakeup source, but keep it disabled * by default. User space can decide enabling it depending on its diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 3f568f790f39..b0ebab0ae51f 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1327,7 +1327,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num) } if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) - schedule_work(&con->work); + queue_work(system_freezable_wq, &con->work); } EXPORT_SYMBOL_GPL(ucsi_connector_change); diff --git a/include/dt-bindings/firmware/qcom,scm.h b/include/dt-bindings/firmware/qcom,scm.h index 6de8b08e1e79..1e0a477f0db9 100644 --- a/include/dt-bindings/firmware/qcom,scm.h +++ b/include/dt-bindings/firmware/qcom,scm.h @@ -36,4 +36,7 @@ #define QCOM_SCM_VMID_TVM 0x2D #define QCOM_SCM_VMID_OEMVM 0x31 +/* Special value intended for qcom,shm-bridge-vmid */ +#define QCOM_SCM_VMID_SELF_OWNER 0xffffffff + #endif diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index b4795698d8c2..c40b18d120ab 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -503,6 +503,27 @@ enum rproc_features { RPROC_MAX_FEATURES, }; +/** + * enum rproc_auto_boot - auto boot strategy for remoteproc during initial boot + * + * @RPROC_AUTO_BOOT_DISABLED: The remoteproc will be left offline (or detached). + * @RPROC_AUTO_BOOT_ATTACH_OR_START: The remoteproc will be attached (if it is + * already running). Otherwise, it will be + * started with new loaded firmware. + * @RPROC_FEAT_REBOOT_IF_FW_AVAILABLE: The remoteproc will be restarted if + * requesting new firmware succeeds. If + * the firmware is missing and the + * remoteproc is already running, it will + * be attached instead. A remoteproc + * implementing this must handle stop() + * being called in detached state. + */ +enum rproc_auto_boot { + RPROC_AUTO_BOOT_DISABLED, + RPROC_AUTO_BOOT_ATTACH_OR_START, + RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE, +}; + /** * struct rproc - represents a physical remote processor device * @node: list node of this rproc object @@ -577,7 +598,7 @@ struct rproc { struct resource_table *cached_table; size_t table_sz; bool has_iommu; - bool auto_boot; + enum rproc_auto_boot auto_boot; bool sysfs_read_only; struct list_head dump_segments; int nb_vdev; diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index fb7ab9165645..8cf3fa83fa95 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -181,6 +181,8 @@ void rpmsg_destroy_ept(struct rpmsg_endpoint *); struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *, rpmsg_rx_cb_t cb, void *priv, struct rpmsg_channel_info chinfo); +struct rpmsg_endpoint *rpmsg_dev_open_ept(struct rpmsg_device *rpdev, + rpmsg_rx_cb_t cb, void *priv); int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len); int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); @@ -249,6 +251,16 @@ static inline struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev return NULL; } +static inline struct rpmsg_endpoint *rpmsg_dev_open_ept(struct rpmsg_device *rpdev, + rpmsg_rx_cb_t cb, + void *priv) +{ + /* This shouldn't be possible */ + WARN_ON(1); + + return NULL; +} + static inline int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len) { /* This shouldn't be possible */ diff --git a/net/qrtr/mhi.c b/net/qrtr/mhi.c index 69f53625a049..5b7268868bbd 100644 --- a/net/qrtr/mhi.c +++ b/net/qrtr/mhi.c @@ -15,6 +15,7 @@ struct qrtr_mhi_dev { struct qrtr_endpoint ep; struct mhi_device *mhi_dev; struct device *dev; + struct completion prepared; }; /* From MHI to QRTR */ @@ -53,6 +54,10 @@ static int qcom_mhi_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb) if (skb->sk) sock_hold(skb->sk); + rc = wait_for_completion_interruptible(&qdev->prepared); + if (rc) + goto free_skb; + rc = skb_linearize(skb); if (rc) goto free_skb; @@ -85,6 +90,7 @@ static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev, qdev->mhi_dev = mhi_dev; qdev->dev = &mhi_dev->dev; qdev->ep.xmit = qcom_mhi_qrtr_send; + init_completion(&qdev->prepared); dev_set_drvdata(&mhi_dev->dev, qdev); rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); @@ -97,6 +103,7 @@ static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev, qrtr_endpoint_unregister(&qdev->ep); return rc; } + complete_all(&qdev->prepared); dev_dbg(qdev->dev, "Qualcomm MHI QRTR driver probed\n"); diff --git a/net/qrtr/smd.c b/net/qrtr/smd.c index c91bf030fbc7..bd70dcfe499a 100644 --- a/net/qrtr/smd.c +++ b/net/qrtr/smd.c @@ -5,6 +5,7 @@ */ #include +#include #include #include @@ -14,18 +15,18 @@ struct qrtr_smd_dev { struct qrtr_endpoint ep; struct rpmsg_endpoint *channel; struct device *dev; + + /* Protect against channel open/close while sending */ + struct mutex send_lock; }; /* from smd to qrtr */ static int qcom_smd_qrtr_callback(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 addr) { - struct qrtr_smd_dev *qdev = dev_get_drvdata(&rpdev->dev); + struct qrtr_smd_dev *qdev = priv; int rc; - if (!qdev) - return -EAGAIN; - rc = qrtr_endpoint_post(&qdev->ep, data, len); if (rc == -EINVAL) { dev_err(qdev->dev, "invalid ipcrouter packet\n"); @@ -46,7 +47,12 @@ static int qcom_smd_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb) if (rc) goto out; - rc = rpmsg_send(qdev->channel, skb->data, skb->len); + scoped_guard(mutex, &qdev->send_lock) { + if (qdev->channel) + rc = rpmsg_send(qdev->channel, skb->data, skb->len); + else + rc = -ENODEV; + } out: if (rc) @@ -65,14 +71,26 @@ static int qcom_smd_qrtr_probe(struct rpmsg_device *rpdev) if (!qdev) return -ENOMEM; - qdev->channel = rpdev->ept; qdev->dev = &rpdev->dev; qdev->ep.xmit = qcom_smd_qrtr_send; + rc = devm_mutex_init(&rpdev->dev, &qdev->send_lock); + if (rc) + return rc; + + /* Block sending until we have fully opened the channel */ + guard(mutex)(&qdev->send_lock); + rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); if (rc) return rc; + qdev->channel = rpmsg_dev_open_ept(rpdev, qcom_smd_qrtr_callback, qdev); + if (!qdev->channel) { + qrtr_endpoint_unregister(&qdev->ep); + return -EREMOTEIO; + } + dev_set_drvdata(&rpdev->dev, qdev); dev_dbg(&rpdev->dev, "Qualcomm SMD QRTR driver probed\n"); @@ -83,10 +101,16 @@ static int qcom_smd_qrtr_probe(struct rpmsg_device *rpdev) static void qcom_smd_qrtr_remove(struct rpmsg_device *rpdev) { struct qrtr_smd_dev *qdev = dev_get_drvdata(&rpdev->dev); + struct rpmsg_endpoint *ept; - qrtr_endpoint_unregister(&qdev->ep); + /* We are about to close the channel, so stop all sending now */ + scoped_guard(mutex, &qdev->send_lock) { + ept = qdev->channel; + qdev->channel = NULL; + } - dev_set_drvdata(&rpdev->dev, NULL); + rpmsg_destroy_ept(ept); + qrtr_endpoint_unregister(&qdev->ep); } static const struct rpmsg_device_id qcom_smd_qrtr_smd_match[] = { @@ -97,7 +121,6 @@ static const struct rpmsg_device_id qcom_smd_qrtr_smd_match[] = { static struct rpmsg_driver qcom_smd_qrtr_driver = { .probe = qcom_smd_qrtr_probe, .remove = qcom_smd_qrtr_remove, - .callback = qcom_smd_qrtr_callback, .id_table = qcom_smd_qrtr_smd_match, .drv = { .name = "qcom_smd_qrtr", diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c index 444f2162889f..7bd621757f5c 100644 --- a/sound/soc/qcom/x1e80100.c +++ b/sound/soc/qcom/x1e80100.c @@ -22,6 +22,7 @@ struct x1e80100_snd_data { struct snd_soc_jack jack; struct snd_soc_jack dp_jack[8]; bool jack_setup; + bool lr_swapped; }; static int x1e80100_snd_init(struct snd_soc_pcm_runtime *rtd) @@ -95,26 +96,44 @@ static int x1e80100_snd_hw_params(struct snd_pcm_substream *substream, return qcom_snd_sdw_hw_params(substream, params, &data->sruntime[cpu_dai->id]); } -static int x1e80100_snd_hw_map_channels(unsigned int *ch_map, int num) +static int x1e80100_snd_hw_map_channels(unsigned int *ch_map, int num, + struct x1e80100_snd_data *data) { switch (num) { case 1: ch_map[0] = PCM_CHANNEL_FC; break; case 2: - ch_map[0] = PCM_CHANNEL_FL; - ch_map[1] = PCM_CHANNEL_FR; + if (data->lr_swapped) { + ch_map[0] = PCM_CHANNEL_FR; + ch_map[1] = PCM_CHANNEL_FL; + } else { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + } break; case 3: - ch_map[0] = PCM_CHANNEL_FL; - ch_map[1] = PCM_CHANNEL_FR; + if (data->lr_swapped) { + ch_map[0] = PCM_CHANNEL_FR; + ch_map[1] = PCM_CHANNEL_FL; + } else { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + } ch_map[2] = PCM_CHANNEL_FC; break; case 4: - ch_map[0] = PCM_CHANNEL_FL; - ch_map[1] = PCM_CHANNEL_LB; - ch_map[2] = PCM_CHANNEL_FR; - ch_map[3] = PCM_CHANNEL_RB; + if (data->lr_swapped) { + ch_map[0] = PCM_CHANNEL_FR; + ch_map[1] = PCM_CHANNEL_RB; + ch_map[2] = PCM_CHANNEL_FL; + ch_map[3] = PCM_CHANNEL_LB; + } else { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_LB; + ch_map[2] = PCM_CHANNEL_FR; + ch_map[3] = PCM_CHANNEL_RB; + } break; default: return -EINVAL; @@ -136,7 +155,7 @@ static int x1e80100_snd_prepare(struct snd_pcm_substream *substream) switch (cpu_dai->id) { case WSA_CODEC_DMA_RX_0: case WSA_CODEC_DMA_RX_1: - ret = x1e80100_snd_hw_map_channels(rx_slot, channels); + ret = x1e80100_snd_hw_map_channels(rx_slot, channels, data); if (ret) return ret; @@ -211,6 +230,10 @@ static int x1e80100_platform_probe(struct platform_device *pdev) return ret; card->driver_name = of_device_get_match_data(dev); + + if (of_property_present(dev->of_node, "channels-swapped")) + data->lr_swapped = true; + x1e80100_add_be_ops(card); return devm_snd_soc_register_card(dev, card);