From 7dd201ad7e8e9111e90edddd3d09ba7c23f40212 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 31 Jul 2026 17:47:05 +0800 Subject: [PATCH 1/6] dt-bindings: media: Add bindings for qcom,nord-camss Add the device tree bindings for the camera subsystem (CAMSS) found on the Qualcomm Nord SoC. Nord's CAMSS is a gen4 Titan ISP built from 5 CSIPHY (v2.4.0), 3 CSID plus 10 CSID lite, and 3 IFE (VFE full) plus 10 IFE lite blocks. The CSID full/lite blocks use the "900" register layout and the IFE blocks reuse the gen4 VFE programming model. Each CSIPHY has its own pair of 0.8V/1.2V supplies as the rails can differ per sensor. Signed-off-by: Wenmeng Liu --- .../bindings/media/qcom,nord-camss.yaml | 577 ++++++++++++++++++ 1 file changed, 577 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,nord-camss.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,nord-camss.yaml b/Documentation/devicetree/bindings/media/qcom,nord-camss.yaml new file mode 100644 index 0000000000000..68735b19283b3 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,nord-camss.yaml @@ -0,0 +1,577 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,nord-camss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Nord Camera Subsystem (CAMSS) + +maintainers: + - Wenmeng Liu + +description: + The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms. + +properties: + compatible: + const: qcom,nord-camss + + reg: + maxItems: 34 + + reg-names: + items: + - const: csid0 + - const: csid1 + - const: csid2 + - const: csid_lite0 + - const: csid_lite1 + - const: csid_lite2 + - const: csid_lite3 + - const: csid_lite4 + - const: csid_lite5 + - const: csid_lite6 + - const: csid_lite7 + - const: csid_lite8 + - const: csid_lite9 + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: csiphy3 + - const: csiphy4 + - const: csitpg0 + - const: csitpg1 + - const: csitpg2 + - const: vfe0 + - const: vfe1 + - const: vfe2 + - const: vfe_lite0 + - const: vfe_lite1 + - const: vfe_lite2 + - const: vfe_lite3 + - const: vfe_lite4 + - const: vfe_lite5 + - const: vfe_lite6 + - const: vfe_lite7 + - const: vfe_lite8 + - const: vfe_lite9 + + clocks: + maxItems: 52 + + clock-names: + items: + - const: camnoc_nrt_axi + - const: camnoc_rt_axi + - const: core_ahb + - const: csid + - const: csid_csiphy_rx + - const: csiphy0 + - const: csiphy0_timer + - const: csiphy1 + - const: csiphy1_timer + - const: csiphy2 + - const: csiphy2_timer + - const: csiphy3 + - const: csiphy3_timer + - const: csiphy4 + - const: csiphy4_timer + - const: gcc_axi_hf + - const: top_ahb + - const: top_fast_ahb + - const: top_ife0 + - const: top_ife1 + - const: top_ife2 + - const: top_ife_lite + - const: vfe0 + - const: vfe0_fast_ahb + - const: vfe0_pcp + - const: vfe0_pcp_fast_ahb + - const: vfe0_tmc + - const: vfe0_tmc_fast_ahb + - const: vfe0_scalar + - const: vfe0_scalar_fast_ahb + - const: vfe1 + - const: vfe1_fast_ahb + - const: vfe1_pcp + - const: vfe1_pcp_fast_ahb + - const: vfe1_tmc + - const: vfe1_tmc_fast_ahb + - const: vfe1_scalar + - const: vfe1_scalar_fast_ahb + - const: vfe2 + - const: vfe2_fast_ahb + - const: vfe2_pcp + - const: vfe2_pcp_fast_ahb + - const: vfe2_tmc + - const: vfe2_tmc_fast_ahb + - const: vfe2_scalar + - const: vfe2_scalar_fast_ahb + - const: vfe_lite + - const: vfe_lite_ahb + - const: vfe_lite_cphy_rx + - const: vfe_lite_csid + - const: tpg_csiphy_rx + - const: qdss_debug_xo + + interrupts: + maxItems: 34 + + interrupt-names: + items: + - const: csid0 + - const: csid1 + - const: csid2 + - const: csid_lite0 + - const: csid_lite1 + - const: csid_lite2 + - const: csid_lite3 + - const: csid_lite4 + - const: csid_lite5 + - const: csid_lite6 + - const: csid_lite7 + - const: csid_lite8 + - const: csid_lite9 + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: csiphy3 + - const: csiphy4 + - const: csitpg0 + - const: csitpg1 + - const: csitpg2 + - const: vfe0 + - const: vfe1 + - const: vfe2 + - const: vfe_lite0 + - const: vfe_lite1 + - const: vfe_lite2 + - const: vfe_lite3 + - const: vfe_lite4 + - const: vfe_lite5 + - const: vfe_lite6 + - const: vfe_lite7 + - const: vfe_lite8 + - const: vfe_lite9 + + interconnects: + maxItems: 2 + + interconnect-names: + items: + - const: ahb + - const: hf_mnoc + + iommus: + maxItems: 2 + + power-domains: + items: + - description: IFE0 GDSC - Image Front End, Global Distributed Switch Controller. + - description: IFE1 GDSC - Image Front End, Global Distributed Switch Controller. + - description: IFE2 GDSC - Image Front End, Global Distributed Switch Controller. + - description: Titan Top GDSC - Titan ISP Block, Global Distributed Switch Controller. + + power-domain-names: + items: + - const: ife0 + - const: ife1 + - const: ife2 + - const: top + + vdd-csiphy0-0p8-supply: + description: + Phandle to a 0.8V regulator supply to CSIPHY0 core block. + + vdd-csiphy0-1p2-supply: + description: + Phandle to a 1.2V regulator supply to CSIPHY0 pll block. + + vdd-csiphy1-0p8-supply: + description: + Phandle to a 0.8V regulator supply to CSIPHY1 core block. + + vdd-csiphy1-1p2-supply: + description: + Phandle to a 1.2V regulator supply to CSIPHY1 pll block. + + vdd-csiphy2-0p8-supply: + description: + Phandle to a 0.8V regulator supply to CSIPHY2 core block. + + vdd-csiphy2-1p2-supply: + description: + Phandle to a 1.2V regulator supply to CSIPHY2 pll block. + + vdd-csiphy3-0p8-supply: + description: + Phandle to a 0.8V regulator supply to CSIPHY3 core block. + + vdd-csiphy3-1p2-supply: + description: + Phandle to a 1.2V regulator supply to CSIPHY3 pll block. + + vdd-csiphy4-0p8-supply: + description: + Phandle to a 0.8V regulator supply to CSIPHY4 core block. + + vdd-csiphy4-1p2-supply: + description: + Phandle to a 1.2V regulator supply to CSIPHY4 pll block. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + description: + CSI input ports. + + patternProperties: + "^port@[0-4]$": + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + + description: + Input port for receiving CSI data from a CSIPHY. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + + required: + - data-lanes + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - interrupt-names + - interconnects + - interconnect-names + - iommus + - power-domains + - power-domain-names + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + isp@9b32000 { + compatible = "qcom,nord-camss"; + + reg = <0 0x09b32000 0 0x2000>, + <0 0x09b38800 0 0x2000>, + <0 0x09b3f000 0 0x2000>, + <0 0x09c32000 0 0x1000>, + <0 0x09c37000 0 0x1000>, + <0 0x09c3c000 0 0x1000>, + <0 0x09c41000 0 0x1000>, + <0 0x09c46000 0 0x1000>, + <0 0x09c4b000 0 0x1000>, + <0 0x09c50000 0 0x1000>, + <0 0x09c55000 0 0x1000>, + <0 0x09c5a000 0 0x1000>, + <0 0x09c5f000 0 0x1000>, + <0 0x09c7b000 0 0x2000>, + <0 0x09c7d000 0 0x2000>, + <0 0x09c7f000 0 0x2000>, + <0 0x09c81000 0 0x2000>, + <0 0x09c83000 0 0x2000>, + <0 0x09a92000 0 0x400>, + <0 0x09a93000 0 0x400>, + <0 0x09a94000 0 0x400>, + <0 0x09ab1000 0 0x4000>, + <0 0x09ad1000 0 0x4000>, + <0 0x09af1000 0 0x4000>, + <0 0x09c34000 0 0x2000>, + <0 0x09c39000 0 0x2000>, + <0 0x09c3e000 0 0x2000>, + <0 0x09c43000 0 0x2000>, + <0 0x09c48000 0 0x2000>, + <0 0x09c4d000 0 0x2000>, + <0 0x09c52000 0 0x2000>, + <0 0x09c57000 0 0x2000>, + <0 0x09c5c000 0 0x2000>, + <0 0x09c61000 0 0x2000>; + + reg-names = "csid0", + "csid1", + "csid2", + "csid_lite0", + "csid_lite1", + "csid_lite2", + "csid_lite3", + "csid_lite4", + "csid_lite5", + "csid_lite6", + "csid_lite7", + "csid_lite8", + "csid_lite9", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "csiphy4", + "csitpg0", + "csitpg1", + "csitpg2", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite0", + "vfe_lite1", + "vfe_lite2", + "vfe_lite3", + "vfe_lite4", + "vfe_lite5", + "vfe_lite6", + "vfe_lite7", + "vfe_lite8", + "vfe_lite9"; + + clocks = <&camcc CAM_CC_CAMNOC_NRT_AXI_CLK>, + <&camcc CAM_CC_CAMNOC_RT_AXI_CLK>, + <&camcc CAM_CC_CORE_AHB_CLK>, + <&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_CSIPHY3_CLK>, + <&camcc CAM_CC_CSI3PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY4_CLK>, + <&camcc CAM_CC_CSI4PHYTIMER_CLK>, + <&nwgcc NW_GCC_CAMERA_HF_AXI_CLK>, + <&camcc CAM_CC_TOP_AHB_CLK>, + <&camcc CAM_CC_TOP_FAST_AHB_CLK>, + <&camcc CAM_CC_TOP_IFE_0_CLK>, + <&camcc CAM_CC_TOP_IFE_1_CLK>, + <&camcc CAM_CC_TOP_IFE_2_CLK>, + <&camcc CAM_CC_TOP_IFE_LITE_CLK>, + <&camcc CAM_CC_IFE_0_MAIN_CLK>, + <&camcc CAM_CC_IFE_0_MAIN_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_0_PCP_CLK>, + <&camcc CAM_CC_IFE_0_PCP_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_0_TMC_CLK>, + <&camcc CAM_CC_IFE_0_TMC_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_0_SCALAR_CLK>, + <&camcc CAM_CC_IFE_0_SCALAR_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_1_MAIN_CLK>, + <&camcc CAM_CC_IFE_1_MAIN_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_1_PCP_CLK>, + <&camcc CAM_CC_IFE_1_PCP_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_1_TMC_CLK>, + <&camcc CAM_CC_IFE_1_TMC_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_1_SCALAR_CLK>, + <&camcc CAM_CC_IFE_1_SCALAR_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_2_MAIN_CLK>, + <&camcc CAM_CC_IFE_2_MAIN_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_2_PCP_CLK>, + <&camcc CAM_CC_IFE_2_PCP_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_2_TMC_CLK>, + <&camcc CAM_CC_IFE_2_TMC_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_2_SCALAR_CLK>, + <&camcc CAM_CC_IFE_2_SCALAR_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>, + <&camcc CAM_CC_TPG_CSIPHY_RX_CLK>, + <&camcc CAM_CC_QDSS_DEBUG_XO_CLK>; + + clock-names = "camnoc_nrt_axi", + "camnoc_rt_axi", + "core_ahb", + "csid", + "csid_csiphy_rx", + "csiphy0", + "csiphy0_timer", + "csiphy1", + "csiphy1_timer", + "csiphy2", + "csiphy2_timer", + "csiphy3", + "csiphy3_timer", + "csiphy4", + "csiphy4_timer", + "gcc_axi_hf", + "top_ahb", + "top_fast_ahb", + "top_ife0", + "top_ife1", + "top_ife2", + "top_ife_lite", + "vfe0", + "vfe0_fast_ahb", + "vfe0_pcp", + "vfe0_pcp_fast_ahb", + "vfe0_tmc", + "vfe0_tmc_fast_ahb", + "vfe0_scalar", + "vfe0_scalar_fast_ahb", + "vfe1", + "vfe1_fast_ahb", + "vfe1_pcp", + "vfe1_pcp_fast_ahb", + "vfe1_tmc", + "vfe1_tmc_fast_ahb", + "vfe1_scalar", + "vfe1_scalar_fast_ahb", + "vfe2", + "vfe2_fast_ahb", + "vfe2_pcp", + "vfe2_pcp_fast_ahb", + "vfe2_tmc", + "vfe2_tmc_fast_ahb", + "vfe2_scalar", + "vfe2_scalar_fast_ahb", + "vfe_lite", + "vfe_lite_ahb", + "vfe_lite_cphy_rx", + "vfe_lite_csid", + "tpg_csiphy_rx", + "qdss_debug_xo"; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + + interrupt-names = "csid0", + "csid1", + "csid2", + "csid_lite0", + "csid_lite1", + "csid_lite2", + "csid_lite3", + "csid_lite4", + "csid_lite5", + "csid_lite6", + "csid_lite7", + "csid_lite8", + "csid_lite9", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "csiphy4", + "csitpg0", + "csitpg1", + "csitpg2", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite0", + "vfe_lite1", + "vfe_lite2", + "vfe_lite3", + "vfe_lite4", + "vfe_lite5", + "vfe_lite6", + "vfe_lite7", + "vfe_lite8", + "vfe_lite9"; + + 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>; + + interconnect-names = "ahb", + "hf_mnoc"; + + iommus = <&apps_smmu_2 0x1401 0x20>, + <&apps_smmu_2 0x1460 0x00>; + + power-domains = <&camcc CAM_CC_IFE_0_GDSC>, + <&camcc CAM_CC_IFE_1_GDSC>, + <&camcc CAM_CC_IFE_2_GDSC>, + <&camcc CAM_CC_TITAN_TOP_GDSC>; + + power-domain-names = "ife0", + "ife1", + "ife2", + "top"; + + vdd-csiphy0-0p8-supply = <&vreg_l1>; + vdd-csiphy0-1p2-supply = <&vreg_l2>; + vdd-csiphy1-0p8-supply = <&vreg_l1>; + vdd-csiphy1-1p2-supply = <&vreg_l2>; + vdd-csiphy2-0p8-supply = <&vreg_l1>; + vdd-csiphy2-1p2-supply = <&vreg_l2>; + vdd-csiphy3-0p8-supply = <&vreg_l1>; + vdd-csiphy3-1p2-supply = <&vreg_l2>; + vdd-csiphy4-0p8-supply = <&vreg_l1>; + vdd-csiphy4-1p2-supply = <&vreg_l2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + endpoint { + data-lanes = <0 1 2 3>; + remote-endpoint = <&sensor_ep>; + }; + }; + }; + }; + }; From 935c85440f1d72a91a70600162fcf720112fcbc9 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 31 Jul 2026 17:47:32 +0800 Subject: [PATCH 2/6] media: qcom: camss: Add nord compatible camss driver Add the CAMSS_NORD version and the "qcom,nord-camss" compatible for the Qualcomm Nord camera subsystem. Nord is a gen4 Titan ISP modelled on kaanapali. This commit wires up the platform skeleton and the CSIPHY block: - 5x CSIPHY (v2.4.0), reusing csiphy_ops_3ph_1_0 Nord's CSIPHY is v2.4.0, already supported in-tree, so only the shared gen2/init switch-cases in camss-csiphy-3ph-1-0.c gain a CAMSS_NORD entry and no new CSIPHY register code is needed. The CSID, VFE and TPG resource tables are added in the following per-module commits, each filling in the corresponding nord_resources fields. Unlike its kaanapali/sm8750 siblings, Nord has no CPAS clocks; it uses the CORE_AHB/TOP_FAST_AHB interface clocks and the split IFE MAIN/PCP/SCALAR/TMC plus per-IFE TOP_IFE clock tree. Signed-off-by: Wenmeng Liu --- .../qcom/camss/camss-csiphy-3ph-1-0.c | 2 + drivers/media/platform/qcom/camss/camss.c | 134 ++++++++++++++++++ drivers/media/platform/qcom/camss/camss.h | 1 + 3 files changed, 137 insertions(+) diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c index 75c0aef3725e2..620b6e18fdfd8 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -1261,6 +1261,7 @@ static bool csiphy_is_gen2(u32 version) case CAMSS_8775P: case CAMSS_GLYMUR: case CAMSS_KAANAPALI: + case CAMSS_NORD: case CAMSS_X1E80100: case CAMSS_X1P42100: ret = true; @@ -1386,6 +1387,7 @@ static int csiphy_init(struct csiphy_device *csiphy) break; case CAMSS_8750: case CAMSS_KAANAPALI: + case CAMSS_NORD: /* CSPHY v2.4.0 is backward compatible with v2.3.0 settings */ regs->lane_regs = &lane_regs_2_4_0[0]; regs->lane_array_size = ARRAY_SIZE(lane_regs_2_4_0); diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index a00af0c718dcd..6b0525dd2f578 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -381,6 +381,129 @@ static const struct resources_icc icc_res_kaanapali[] = { }, }; +static const struct camss_subdev_resources csiphy_res_nord[] = { + /* CSIPHY0 */ + { + .regulators = { + { .supply = "vdd-csiphy0-0p8", .init_load_uA = 151020 }, + { .supply = "vdd-csiphy0-1p2", .init_load_uA = 14660 } + }, + .clock = { "csiphy0", "csiphy0_timer", + "core_ahb", "top_ahb", "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" }, + .csiphy = { + .id = 0, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY1 */ + { + .regulators = { + { .supply = "vdd-csiphy1-0p8", .init_load_uA = 151020 }, + { .supply = "vdd-csiphy1-1p2", .init_load_uA = 14660 } + }, + .clock = { "csiphy1", "csiphy1_timer", + "core_ahb", "top_ahb", "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" }, + .csiphy = { + .id = 1, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY2 */ + { + .regulators = { + { .supply = "vdd-csiphy2-0p8", .init_load_uA = 151020 }, + { .supply = "vdd-csiphy2-1p2", .init_load_uA = 14660 } + }, + .clock = { "csiphy2", "csiphy2_timer", + "core_ahb", "top_ahb", "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" }, + .csiphy = { + .id = 2, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY3 */ + { + .regulators = { + { .supply = "vdd-csiphy3-0p8", .init_load_uA = 151020 }, + { .supply = "vdd-csiphy3-1p2", .init_load_uA = 14660 } + }, + .clock = { "csiphy3", "csiphy3_timer", + "core_ahb", "top_ahb", "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csiphy3" }, + .interrupt = { "csiphy3" }, + .csiphy = { + .id = 3, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY4 */ + { + .regulators = { + { .supply = "vdd-csiphy4-0p8", .init_load_uA = 151020 }, + { .supply = "vdd-csiphy4-1p2", .init_load_uA = 14660 } + }, + .clock = { "csiphy4", "csiphy4_timer", + "core_ahb", "top_ahb", "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csiphy4" }, + .interrupt = { "csiphy4" }, + .csiphy = { + .id = 4, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, +}; + +static const struct resources_icc icc_res_nord[] = { + { + .name = "ahb", + .icc_bw_tbl.avg = 150000, + .icc_bw_tbl.peak = 300000, + }, + /* Based on 4096 x 3072 30 FPS 2496 Mbps mode */ + { + .name = "hf_mnoc", + .icc_bw_tbl.avg = 471860, + .icc_bw_tbl.peak = 925857, + }, +}; + + static const struct camss_subdev_resources csiphy_res_8x16[] = { /* CSIPHY0 */ { @@ -6364,6 +6487,16 @@ static const struct camss_resources kaanapali_resources = { .vfe_num = ARRAY_SIZE(vfe_res_kaanapali), }; +static const struct camss_resources nord_resources = { + .version = CAMSS_NORD, + .pd_name = "top", + .legacy_phy = true, + .csiphy_res = csiphy_res_nord, + .icc_res = icc_res_nord, + .icc_path_num = ARRAY_SIZE(icc_res_nord), + .csiphy_num = ARRAY_SIZE(csiphy_res_nord), +}; + static const struct camss_resources msm8916_resources = { .version = CAMSS_8x16, .legacy_phy = true, @@ -6678,6 +6811,7 @@ static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,msm8939-camss", .data = &msm8939_resources }, { .compatible = "qcom,msm8953-camss", .data = &msm8953_resources }, { .compatible = "qcom,msm8996-camss", .data = &msm8996_resources }, + { .compatible = "qcom,nord-camss", .data = &nord_resources }, { .compatible = "qcom,qcm2290-camss", .data = &qcm2290_resources }, { .compatible = "qcom,qcs8300-camss", .data = &qcs8300_resources }, { .compatible = "qcom,sa8775p-camss", .data = &sa8775p_resources }, diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 91438769419e5..28dfb07245b5b 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -100,6 +100,7 @@ enum camss_version { CAMSS_8775P, CAMSS_GLYMUR, CAMSS_KAANAPALI, + CAMSS_NORD, CAMSS_X1E80100, CAMSS_X1P42100, }; From ef12b057856f43f22cc8e6f77a6a534b071c0ec5 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 31 Jul 2026 17:47:50 +0800 Subject: [PATCH 3/6] media: qcom: camss: tpg: Add support for TPG v2.5.0 Nord's test pattern generator is TPG v2.5.0, which relocates the stream enable and reset out of TPG_CTRL/TPG_CLEAR into a dedicated command register (TPG_CTRL_CMD) at the same 0x1F4 offset. Branch tpg_stream_on() on the runtime hw_version so --- .../platform/qcom/camss/camss-tpg-gen1.c | 33 +++++++++++++-- drivers/media/platform/qcom/camss/camss.c | 41 +++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-tpg-gen1.c b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c index d29de5f93c18e..b188dffeaf3d5 100644 --- a/drivers/media/platform/qcom/camss/camss-tpg-gen1.c +++ b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c @@ -22,6 +22,7 @@ #define TPG_HW_VER_2_0_0 TPG_HW_VER(2, 0, 0) #define TPG_HW_VER_2_1_0 TPG_HW_VER(2, 1, 0) +#define TPG_HW_VER_2_5_0 TPG_HW_VER(2, 5, 0) #define TPG_HW_STATUS 0x4 @@ -35,6 +36,14 @@ #define TPG_CLEAR 0x1F4 +/* + * v2.5.0 moves the stream enable and reset out of TPG_CTRL/TPG_CLEAR into a + * dedicated command register at the same 0x1F4 offset. + */ +#define TPG_CTRL_CMD 0x1F4 +# define TPG_CTRL_CMD_HW_RESET BIT(0) +# define TPG_CTRL_CMD_TEST_EN BIT(4) + /* TPG VC-based registers */ #define TPG_VC_n_GAIN_CFG(n) (0x60 + (n) * 0x60) @@ -86,8 +95,15 @@ */ #define TPG_USER_SPECIFIED_PAYLOAD_DEFAULT 0xBE #define TPG_LFSR_SEED_DEFAULT 0x12345678 +/* + * Preserve the CFA pattern size (SIZE_X/SIZE_Y, "n means n+1") at its reset + * value of 3 (a 4x4 pattern). Writing only ROTATE_PERIOD zeroes these fields, + * degenerating the pattern to 1x1 and producing no valid pixel output. + */ #define TPG_COLOR_BARS_CFG_STANDARD \ - FIELD_PREP(TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD, 0xA) + (FIELD_PREP(TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD, 0xA) | \ + FIELD_PREP(TPG_VC_n_COLOR_BARS_CFG_SIZE_X, 3) | \ + FIELD_PREP(TPG_VC_n_COLOR_BARS_CFG_SIZE_Y, 3)) static const char * const testgen_payload_modes[] = { [TPG_PAYLOAD_MODE_DISABLED] = "Disabled", @@ -164,16 +180,27 @@ static int tpg_stream_on(struct tpg_device *tpg) } /* Global TPG control */ - val = FIELD_PREP(TPG_CTRL_TEST_EN, 1) | - FIELD_PREP(TPG_CTRL_NUM_ACTIVE_LANES, lane_cnt - 1) | + val = FIELD_PREP(TPG_CTRL_NUM_ACTIVE_LANES, lane_cnt - 1) | FIELD_PREP(TPG_CTRL_NUM_ACTIVE_VC, last_vc); + if (tpg->hw_version < TPG_HW_VER_2_5_0) + val |= FIELD_PREP(TPG_CTRL_TEST_EN, 1); writel(val, tpg->base + TPG_CTRL); + /* v2.5.0 enables the generator through the command register */ + if (tpg->hw_version >= TPG_HW_VER_2_5_0) + writel(TPG_CTRL_CMD_TEST_EN, tpg->base + TPG_CTRL_CMD); + return 0; } static int tpg_reset(struct tpg_device *tpg) { + /* + * The reset/clear bit is bit 0 of the register at 0x1F4 on every + * revision (TPG_CLEAR on hw_version. + */ writel(0, tpg->base + TPG_CTRL); writel(1, tpg->base + TPG_CLEAR); diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 6b0525dd2f578..771d237fb9051 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -489,6 +489,45 @@ static const struct camss_subdev_resources csiphy_res_nord[] = { }, }; +static const struct camss_subdev_resources tpg_res_nord[] = { + /* TPG0 */ + { + .clock = { "top_ahb", "tpg_csiphy_rx" }, + .clock_rate = { { 0 }, + { 400000000, 480000000 } }, + .reg = { "csitpg0" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG1 */ + { + .clock = { "top_ahb", "tpg_csiphy_rx" }, + .clock_rate = { { 0 }, + { 400000000, 480000000 } }, + .reg = { "csitpg1" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, + /* TPG2 */ + { + .clock = { "top_ahb", "tpg_csiphy_rx" }, + .clock_rate = { { 0 }, + { 400000000, 480000000 } }, + .reg = { "csitpg2" }, + .tpg = { + .lane_cnt = 4, + .formats = &tpg_formats_gen1, + .hw_ops = &tpg_ops_gen1 + } + }, +}; + static const struct resources_icc icc_res_nord[] = { { .name = "ahb", @@ -6492,9 +6531,11 @@ static const struct camss_resources nord_resources = { .pd_name = "top", .legacy_phy = true, .csiphy_res = csiphy_res_nord, + .tpg_res = tpg_res_nord, .icc_res = icc_res_nord, .icc_path_num = ARRAY_SIZE(icc_res_nord), .csiphy_num = ARRAY_SIZE(csiphy_res_nord), + .tpg_num = ARRAY_SIZE(tpg_res_nord), }; static const struct camss_resources msm8916_resources = { From 3f049e281b2af51e2fee7b83023503dfe8810def Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 31 Jul 2026 17:48:33 +0800 Subject: [PATCH 4/6] media: qcom: camss: csid: Add support for CSID 900 Add a CSID hardware driver for the "900" revision found on the Nord SoC. CSID 900 shares the gen4 CSI-2 RX helpers and the split RUP/AUP command model of CSID 980 (an explicit SET is required after programming the AUP/RUP registers), but uses a single uniform register map for both CSID full and CSID lite instances - only the RDI region base differs (full at 0x1300, lite at 0x600, 0x200 stride). This differs from CSID 980, which forks the lite layout, so a dedicated file rather than an IS_CSID_900 variant keeps the offsets readable. Nord has no test pattern generator wired to the CSID, so the testgen path is a no-op as on the other gen4 platforms. Wire the nord CSID resource table (csid_res_nord) into nord_resources. Signed-off-by: Wenmeng Liu --- drivers/media/platform/qcom/camss/Makefile | 1 + .../platform/qcom/camss/camss-csid-900.c | 465 ++++++++++++++++++ .../media/platform/qcom/camss/camss-csid.h | 1 + drivers/media/platform/qcom/camss/camss.c | 206 ++++++++ 4 files changed, 673 insertions(+) create mode 100644 drivers/media/platform/qcom/camss/camss-csid-900.c diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index c19d3183882a9..c057a7e64d82b 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -8,6 +8,7 @@ qcom-camss-objs += \ camss-csid-4-7.o \ camss-csid-340.o \ camss-csid-680.o \ + camss-csid-900.o \ camss-csid-980.o \ camss-csid-gen2.o \ camss-csid-gen3.o \ diff --git a/drivers/media/platform/qcom/camss/camss-csid-900.c b/drivers/media/platform/qcom/camss/camss-csid-900.c new file mode 100644 index 0000000000000..b50488216389a --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csid-900.c @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-csid-900.c + * + * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module 900 + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#include +#include +#include +#include +#include +#include +#include "camss.h" +#include "camss-csid.h" +#include "camss-csid-gen3.h" + +/* Reset and Command Registers */ +#define CSID_RST_CFG 0xC +#define RST_MODE BIT(0) +#define RST_LOCATION BIT(4) + +#define CSID_RST_CMD 0x10 +#define SELECT_HW_RST BIT(0) +#define SELECT_IRQ_RST BIT(2) +#define CSID_IRQ_CMD 0x14 +#define IRQ_CMD_CLEAR BIT(0) + +/* Register Update Commands, RUP/AUP */ +#define CSID_RUP_CMD 0x18 +#define CSID_AUP_CMD 0x1C +#define CSID_RUP_AUP_RDI(rdi) (BIT(8) << (rdi)) +#define CSID_RUP_AUP_CMD 0x20 +#define RUP_SET BIT(0) +#define MUP BIT(4) + +/* Top level interrupt registers */ +#define CSID_TOP_IRQ_STATUS 0x84 +#define CSID_TOP_IRQ_MASK 0x88 +#define CSID_TOP_IRQ_CLEAR 0x8C +#define CSID_TOP_IRQ_SET 0x90 +#define INFO_RST_DONE BIT(0) +#define CSI2_RX_IRQ_STATUS BIT(2) +#define BUF_DONE_IRQ_STATUS BIT(3) + +/* Buffer done interrupt registers */ +#define CSID_BUF_DONE_IRQ_STATUS 0xA4 +#define BUF_DONE_IRQ_STATUS_RDI_OFFSET 16 +#define CSID_BUF_DONE_IRQ_MASK 0xA8 +#define CSID_BUF_DONE_IRQ_CLEAR 0xAC +#define CSID_BUF_DONE_IRQ_SET 0xB0 + +/* CSI2 RX interrupt registers */ +#define CSID_CSI2_RX_IRQ_STATUS 0xB4 +#define CSID_CSI2_RX_IRQ_MASK 0xB8 +#define CSID_CSI2_RX_IRQ_CLEAR 0xBC +#define CSID_CSI2_RX_IRQ_SET 0xC0 + +/* CSI2 RX Configuration */ +#define CSID_CSI2_RX_CFG0 0x400 +#define CSI2_RX_CFG0_NUM_ACTIVE_LANES 0 +#define CSI2_RX_CFG0_DL0_INPUT_SEL 4 +#define CSI2_RX_CFG0_PHY_NUM_SEL 20 +#define CSI2_RX_CFG0_TPG_MUX_EN BIT(27) +/* TPG_NUM_SEL: which TPG drives the data, programmed as n == n (TPG0 -> 0) */ +#define CSI2_RX_CFG0_TPG_MUX_SEL GENMASK(29, 28) +#define CSID_CSI2_RX_CFG1 0x404 +#define CSI2_RX_CFG1_ECC_CORRECTION_EN BIT(0) +#define CSI2_RX_CFG1_VC_MODE BIT(2) + +#define MSM_CSID_MAX_SRC_STREAMS_900 (csid_is_lite(csid) ? 4 : 5) + +/* + * v900 uses a uniform register map for both CSID full and CSID lite (unlike + * v980 where the lite RDI region and IRQ block are laid out differently). Only + * the RDI region base differs: full at 0x1300, lite at 0x600, stride 0x200. + */ +#define CSID_RDI_BASE (csid_is_lite(csid) ? 0x600 : 0x1300) +#define CSID_RDI_CFG0(rdi) (CSID_RDI_BASE + 0x0 + 0x200 * (rdi)) +/* + * Silicon (nordschleife_2.0) names CFG0 bit5 RETIME_DIS = "Disable SOF/EOF + * strobe retiming" (reset 0). Set here to match the 980, which sets the same + * bit (as RETIME_BS). + */ +#define RDI_CFG0_RETIME_DIS BIT(5) +#define RDI_CFG0_TIMESTAMP_EN BIT(6) +#define RDI_CFG0_TIMESTAMP_STB_SEL BIT(8) +#define RDI_CFG0_DECODE_FORMAT 12 +#define RDI_CFG0_DT 16 +#define RDI_CFG0_VC 22 +#define RDI_CFG0_DT_ID 27 +#define RDI_CFG0_EN BIT(31) + +/* RDI Control and Configuration */ +#define CSID_RDI_CTRL(rdi) (CSID_RDI_BASE + 0x4 + 0x200 * (rdi)) +#define RDI_CTRL_START_CMD BIT(0) + +#define CSID_RDI_CFG1(rdi) (CSID_RDI_BASE + 0x10 + 0x200 * (rdi)) +#define RDI_CFG1_DROP_H_EN BIT(5) +#define RDI_CFG1_DROP_V_EN BIT(6) +#define RDI_CFG1_CROP_H_EN BIT(7) +#define RDI_CFG1_CROP_V_EN BIT(8) +#define RDI_CFG1_PACKING_FORMAT_MIPI BIT(15) + +/* RDI Pixel Store Configuration (full only) */ +#define CSID_RDI_PIX_STORE_CFG0(rdi) (CSID_RDI_BASE + 0x14 + 0x200 * (rdi)) +#define RDI_PIX_STORE_CFG0_EN BIT(0) +#define RDI_PIX_STORE_CFG0_MIN_HBI 1 + +/* RDI IRQ Status in wrapper */ +#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) (0x114 + 0x10 * (rdi)) +#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) (0x11C + 0x10 * (rdi)) +#define INFO_RUP_DONE BIT(23) + +/* + * Per-path domain_id (secure sub-block). The 5-bit domain_id CSID passes to the + * IFE becomes the write master's AUSER[4:0], which the NOC translates to the + * SMMU SID: a value of 0 yields an unmapped SID, a non-zero value selects the + * HLOS IFE SID that the device tree maps. The register lives in a secure + * sub-block one 4KB page below each CSID's mmio base, outside the CSID mmio + * window, so it is mapped separately. Physical bases per full CSID (lite has + * no such block). + */ +static const u32 csid_secure_ctrl_base[] = { 0x09b31000, 0x09b37800, 0x09b3e000 }; +#define CSID_SECURE_CTRL_SIZE 0x10 +#define CSID_PATH_DOMAIN_ID_CFG1 0x4 +#define PATH_DOMAIN_ID_RDI(rdi) (0x1U << ((rdi) * 8)) + +static void __csid_aup_rup_trigger(struct csid_device *csid) +{ + /* trigger SET in combined register */ + writel(RUP_SET, csid->base + CSID_RUP_AUP_CMD); +} + +static void __csid_aup_update(struct csid_device *csid, int port_id) +{ + csid->aup_update |= CSID_RUP_AUP_RDI(port_id); + writel(csid->aup_update, csid->base + CSID_AUP_CMD); + + /* + * v900 splits AUP and RUP commands, which requires an additional SET + * operation to make the register modification take effect. + */ + __csid_aup_rup_trigger(csid); +} + +static void __csid_rup_update(struct csid_device *csid, int port_id) +{ + csid->rup_update |= CSID_RUP_AUP_RDI(port_id); + writel(csid->rup_update, csid->base + CSID_RUP_CMD); + + __csid_aup_rup_trigger(csid); +} + +static void __csid_aup_rup_clear(struct csid_device *csid, int port_id) +{ + /* Hardware clears the registers upon consuming the settings */ + csid->aup_update &= ~CSID_RUP_AUP_RDI(port_id); + csid->rup_update &= ~CSID_RUP_AUP_RDI(port_id); +} + +static void __csid_configure_rx(struct csid_device *csid, + struct csid_phy_config *phy) +{ + struct camss *camss = csid->camss; + int val; + + val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; + val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; + + if (camss->tpg && csid->tpg_linked && + camss->tpg[phy->csiphy_id].testgen.mode != TPG_PAYLOAD_MODE_DISABLED) { + val |= FIELD_PREP(CSI2_RX_CFG0_TPG_MUX_SEL, phy->csiphy_id + 1); + val |= CSI2_RX_CFG0_TPG_MUX_EN; + } else { + val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) + << CSI2_RX_CFG0_PHY_NUM_SEL; + } + writel(val, csid->base + CSID_CSI2_RX_CFG0); + + val = CSI2_RX_CFG1_ECC_CORRECTION_EN; + writel(val, csid->base + CSID_CSI2_RX_CFG1); +} + +static void __csid_configure_rx_vc(struct csid_device *csid, int vc) +{ + int val; + + if (vc > 3) { + val = readl(csid->base + CSID_CSI2_RX_CFG1); + val |= CSI2_RX_CFG1_VC_MODE; + writel(val, csid->base + CSID_CSI2_RX_CFG1); + } +} + +static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) +{ + int val = 0; + u32 rdi_ctrl_offset = CSID_RDI_CTRL(rdi); + + if (enable) + val = RDI_CTRL_START_CMD; + + writel(val, csid->base + rdi_ctrl_offset); +} + +static void __csid_configure_rdi_pix_store(struct csid_device *csid, u8 rdi) +{ + u32 val; + + /* + * Configure pixel store to allow absorption of hblanking or idle time. + * This helps with horizontal crop and prevents line buffer conflicts. + * Reset state is 0x8 which has MIN_HBI=4, we keep the default MIN_HBI + * and just enable the pixel store functionality. + */ + val = (4 << RDI_PIX_STORE_CFG0_MIN_HBI) | RDI_PIX_STORE_CFG0_EN; + writel(val, csid->base + CSID_RDI_PIX_STORE_CFG0(rdi)); +} + +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) +{ + u32 val; + u8 lane_cnt = csid->phy.lane_cnt; + + /* Source pads matching RDI channels on hardware. + * E.g. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. + */ + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; + const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, + input_format->code); + + if (!lane_cnt) + lane_cnt = 4; + + /* + * DT_ID is a two bit bitfield that is concatenated with + * the four least significant bits of the five bit VC + * bitfield to generate an internal CID value. + * + * CSID_RDI_CFG0(vc) + * DT_ID : 28:27 + * VC : 26:22 + * DT : 21:16 + * + * CID : VC 3:0 << 2 | DT_ID 1:0 + */ + u8 dt_id = vc & 0x03; + u32 rdi_cfg0_offset = CSID_RDI_CFG0(port); + u32 rdi_cfg1_offset = CSID_RDI_CFG1(port); + u32 rdi_ctrl_offset = CSID_RDI_CTRL(port); + + val = RDI_CFG0_TIMESTAMP_EN; + val |= RDI_CFG0_TIMESTAMP_STB_SEL; + /* bit5 set to match the 980 */ + val |= RDI_CFG0_RETIME_DIS; + + /* note: for non-RDI path, this should be format->decode_format */ + val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; + val |= vc << RDI_CFG0_VC; + val |= format->data_type << RDI_CFG0_DT; + val |= dt_id << RDI_CFG0_DT_ID; + writel(val, csid->base + rdi_cfg0_offset); + + val = RDI_CFG1_PACKING_FORMAT_MIPI; + writel(val, csid->base + rdi_cfg1_offset); + + /* Pixel store is only present on CSID full */ + if (!csid_is_lite(csid)) + __csid_configure_rdi_pix_store(csid, port); + + val = 0; + writel(val, csid->base + rdi_ctrl_offset); + + val = readl(csid->base + rdi_cfg0_offset); + + if (enable) + val |= RDI_CFG0_EN; + + writel(val, csid->base + rdi_cfg0_offset); +} + +/* Program a non-zero per-path domain_id (see CSID_PATH_DOMAIN_ID_CFG1). */ +static void __csid_configure_domain_id(struct csid_device *csid) +{ + void __iomem *sec; + u32 val; + int i; + + /* Only full CSIDs (0..2) have the secure domain_id block */ + if (csid_is_lite(csid) || csid->id >= ARRAY_SIZE(csid_secure_ctrl_base)) + return; + + sec = ioremap(csid_secure_ctrl_base[csid->id], CSID_SECURE_CTRL_SIZE); + if (!sec) + return; + + /* CFG1: one 8-bit domain_id field per RDI (RDI0..3) */ + val = readl(sec + CSID_PATH_DOMAIN_ID_CFG1); + for (i = 0; i < 4; i++) + if (csid->phy.en_vc & BIT(i)) + val |= PATH_DOMAIN_ID_RDI(i); + writel(val, sec + CSID_PATH_DOMAIN_ID_CFG1); + + iounmap(sec); +} + +static void csid_configure_stream(struct csid_device *csid, u8 enable) +{ + u8 i, k; + + __csid_configure_rx(csid, &csid->phy); + + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS_900; i++) { + if (csid->phy.en_vc & BIT(i)) { + __csid_configure_rdi_stream(csid, enable, i, 0); + __csid_configure_rx_vc(csid, 0); + + for (k = 0; k < CAMSS_INIT_BUF_COUNT; k++) + __csid_aup_update(csid, i); + + /* + * Latch the configuration into the active bank with a + * single register update after the address updates, then + * start. + */ + __csid_rup_update(csid, i); + + /* Program domain_id before START (see __csid_configure_domain_id) */ + if (enable) + __csid_configure_domain_id(csid); + + __csid_ctrl_rdi(csid, enable, i); + } + } +} + +static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val) +{ + return 0; +} + +static void csid_subdev_reg_update(struct csid_device *csid, int port_id, + bool clear) +{ + if (clear) + __csid_aup_rup_clear(csid, port_id); + else + __csid_aup_update(csid, port_id); +} + +/** + * csid_isr - CSID module interrupt service routine + * @irq: Interrupt line + * @dev: CSID device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t csid_isr(int irq, void *dev) +{ + struct csid_device *csid = dev; + u32 val, buf_done_val; + u8 reset_done; + int i; + + val = readl(csid->base + CSID_TOP_IRQ_STATUS); + writel(val, csid->base + CSID_TOP_IRQ_CLEAR); + + reset_done = val & INFO_RST_DONE; + + buf_done_val = readl(csid->base + CSID_BUF_DONE_IRQ_STATUS); + writel(buf_done_val, csid->base + CSID_BUF_DONE_IRQ_CLEAR); + + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS_900; i++) { + if (csid->phy.en_vc & BIT(i)) { + val = readl(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i)); + writel(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i)); + + if (val & INFO_RUP_DONE) + csid_subdev_reg_update(csid, i, true); + + if (buf_done_val & BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i)) + camss_buf_done(csid->camss, csid->id, i); + } + } + + val = IRQ_CMD_CLEAR; + writel(val, csid->base + CSID_IRQ_CMD); + + if (reset_done) + complete(&csid->reset_complete); + + return IRQ_HANDLED; +} + +/** + * csid_reset - Trigger reset on CSID module and wait to complete + * @csid: CSID device + * + * Return 0 on success or a negative error code otherwise + */ +static int csid_reset(struct csid_device *csid) +{ + unsigned long time; + u32 val; + int i; + + reinit_completion(&csid->reset_complete); + + val = INFO_RST_DONE | BUF_DONE_IRQ_STATUS; + writel(val, csid->base + CSID_TOP_IRQ_CLEAR); + writel(val, csid->base + CSID_TOP_IRQ_MASK); + + val = 0; + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS_900; i++) { + if (csid->phy.en_vc & BIT(i)) { + /* + * Only need to clear buf done IRQ status here, + * RUP done IRQ status will be cleared once isr + * strobe generated by CSID_RST_CMD + */ + val |= BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i); + } + } + writel(val, csid->base + CSID_BUF_DONE_IRQ_CLEAR); + writel(val, csid->base + CSID_BUF_DONE_IRQ_MASK); + + /* Clear all IRQ status with CLEAR bits set */ + val = IRQ_CMD_CLEAR; + writel(val, csid->base + CSID_IRQ_CMD); + + val = RST_LOCATION | RST_MODE; + writel(val, csid->base + CSID_RST_CFG); + + val = SELECT_HW_RST | SELECT_IRQ_RST; + writel(val, csid->base + CSID_RST_CMD); + + time = wait_for_completion_timeout(&csid->reset_complete, + msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); + + if (!time) { + dev_err(csid->camss->dev, "CSID reset timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void csid_subdev_init(struct csid_device *csid) +{ + csid->testgen.nmodes = CSID_PAYLOAD_MODE_DISABLED; +} + +const struct csid_hw_ops csid_ops_900 = { + .configure_stream = csid_configure_stream, + .configure_testgen_pattern = csid_configure_testgen_pattern, + .hw_version = csid_hw_version, + .isr = csid_isr, + .reset = csid_reset, + .src_pad_code = csid_src_pad_code, + .subdev_init = csid_subdev_init, + .reg_update = csid_subdev_reg_update, +}; diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index dd9c0868e9c73..6b04ff77bf459 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -224,6 +224,7 @@ extern const struct csid_hw_ops csid_ops_4_1; extern const struct csid_hw_ops csid_ops_4_7; extern const struct csid_hw_ops csid_ops_340; extern const struct csid_hw_ops csid_ops_680; +extern const struct csid_hw_ops csid_ops_900; extern const struct csid_hw_ops csid_ops_980; extern const struct csid_hw_ops csid_ops_gen2; extern const struct csid_hw_ops csid_ops_gen3; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 771d237fb9051..bf2e55af8a8c0 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -528,6 +528,210 @@ static const struct camss_subdev_resources tpg_res_nord[] = { }, }; +static const struct camss_subdev_resources csid_res_nord[] = { + /* CSID0 */ + { + .clock = { "csid", "csid_csiphy_rx", "top_ahb", + "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .is_lite = false, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID1 */ + { + .clock = { "csid", "csid_csiphy_rx", "top_ahb", + "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .is_lite = false, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID2 */ + { + .clock = { "csid", "csid_csiphy_rx", "top_ahb", + "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" }, + .csid = { + .is_lite = false, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE0 */ + { + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "top_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite0" }, + .interrupt = { "csid_lite0" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE1 */ + { + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "top_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite1" }, + .interrupt = { "csid_lite1" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE2 */ + { + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "top_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite2" }, + .interrupt = { "csid_lite2" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE3 */ + { + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "top_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite3" }, + .interrupt = { "csid_lite3" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE4 */ + { + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "top_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite4" }, + .interrupt = { "csid_lite4" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE5 */ + { + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "top_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite5" }, + .interrupt = { "csid_lite5" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE6 */ + { + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "top_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite6" }, + .interrupt = { "csid_lite6" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE7 */ + { + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "top_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite7" }, + .interrupt = { "csid_lite7" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE8 */ + { + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "top_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite8" }, + .interrupt = { "csid_lite8" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + }, + /* CSID_LITE9 */ + { + .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "top_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 0 } }, + .reg = { "csid_lite9" }, + .interrupt = { "csid_lite9" }, + .csid = { + .is_lite = true, + .parent_dev_ops = &vfe_parent_dev_ops, + .hw_ops = &csid_ops_900, + .formats = &csid_formats_gen2 + } + } +}; + static const struct resources_icc icc_res_nord[] = { { .name = "ahb", @@ -6531,10 +6735,12 @@ static const struct camss_resources nord_resources = { .pd_name = "top", .legacy_phy = true, .csiphy_res = csiphy_res_nord, + .csid_res = csid_res_nord, .tpg_res = tpg_res_nord, .icc_res = icc_res_nord, .icc_path_num = ARRAY_SIZE(icc_res_nord), .csiphy_num = ARRAY_SIZE(csiphy_res_nord), + .csid_num = ARRAY_SIZE(csid_res_nord), .tpg_num = ARRAY_SIZE(tpg_res_nord), }; From 3747121576e50f1a1c49afd06dc00a175deb82ac Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 31 Jul 2026 17:48:57 +0800 Subject: [PATCH 5/6] media: qcom: camss: vfe: Add support for VFE 900 The Nord IFE (VFE) reuses the gen4 VFE ops but places the bus write master register block at a different base and drops one register, so add an IS_VFE_900() variant inside camss-vfe-gen4.c. Differences from the existing gen4 (980/1080) layouts: - BUS_REG_BASE is 0xA00 for VFE full and 0x700 for VFE lite. - There is no WM_CGC_OVERRIDE register, so the clock-gating override write is skipped. - TEST_BUS_CTRL is relocated to the head of the bus write master block (BUS_REG_BASE - 0x1BC) instead of BUS_REG_BASE + 0x128. - ADDR_IMAGE takes a full byte address instead of the 256-byte-unit (addr >> 8) encoding, and the full-IFE RDI write-master clients start at index 28 (0x1C) instead of 0x17. All other write-master register offsets match gen4 and are reused unchanged. Add CAMSS_NORD to the shared VFE switch-cases and wire the nord VFE resource table (vfe_res_nord) into nord_resources, completing the platform. Signed-off-by: Wenmeng Liu --- .../platform/qcom/camss/camss-vfe-gen4.c | 49 ++- drivers/media/platform/qcom/camss/camss-vfe.c | 2 + drivers/media/platform/qcom/camss/camss.c | 388 ++++++++++++++++++ 3 files changed, 431 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen4.c b/drivers/media/platform/qcom/camss/camss-vfe-gen4.c index 46d8e61b9bac0..cea2ad7f61795 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-gen4.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-gen4.c @@ -14,16 +14,24 @@ #include "camss-vfe.h" #define IS_VFE_980(vfe) ((vfe)->camss->res->version == CAMSS_8750) +#define IS_VFE_900(vfe) ((vfe)->camss->res->version == CAMSS_NORD) +#define BUS_REG_BASE_900 (vfe_is_lite(vfe) ? 0x700 : 0xA00) #define BUS_REG_BASE_980 (vfe_is_lite(vfe) ? 0x200 : 0x800) #define BUS_REG_BASE_1080 (vfe_is_lite(vfe) ? 0x800 : 0x1000) #define BUS_REG_BASE \ - (IS_VFE_980(vfe) ? BUS_REG_BASE_980 : BUS_REG_BASE_1080) + (IS_VFE_900(vfe) ? BUS_REG_BASE_900 : \ + IS_VFE_980(vfe) ? BUS_REG_BASE_980 : BUS_REG_BASE_1080) #define VFE_BUS_WM_CGC_OVERRIDE (BUS_REG_BASE + 0x08) #define WM_CGC_OVERRIDE_ALL (0x7FFFFFF) -#define VFE_BUS_WM_TEST_BUS_CTRL (BUS_REG_BASE + 0x128) +/* + * v900 relocates TEST_BUS_CTRL to the head of the BUS_WR sub-block, while + * v980/v1080 keep it at BUS_REG_BASE + 0x128. + */ +#define VFE_BUS_WM_TEST_BUS_CTRL \ + (IS_VFE_900(vfe) ? (BUS_REG_BASE - 0x1BC) : (BUS_REG_BASE + 0x128)) #define VFE_BUS_WM_CFG(n) (BUS_REG_BASE + 0x500 + (n) * 0x100) #define WM_CFG_EN BIT(0) @@ -87,8 +95,26 @@ * RDI3 3 * GAMMA 4 * STATES_BE 5 + * + * v900 full IFE write master client map, RDI clients start at 28 after 28 + * image/stats clients (0-27): + * + * MAIN C0/C1/C2/UV 0-3 + * PIXEL_RAW 4 + * W_IR 5 + * AI_1 C0/C1/C2/UV 6-9 + * AI_2 C0/C1/C2/UV 10-13 + * HV_DS16 / HV_DS4 14-15 + * STATS BG_IR..BLTM 16-27 + * RDI0 28 + * RDI1 29 + * RDI2 30 + * ... RDI11 39 + * + * v900 IFE Lite matches the generic map above (RDI0 at client 0). */ -#define RDI_WM(n) ((vfe_is_lite(vfe) ? 0x0 : 0x17) + (n)) +#define RDI_WM(n) \ + ((vfe_is_lite(vfe) ? 0x0 : (IS_VFE_900(vfe) ? 0x1C : 0x17)) + (n)) static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) { @@ -97,8 +123,10 @@ static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) wm = RDI_WM(wm); - /* no clock gating at bus input */ - writel(WM_CGC_OVERRIDE_ALL, vfe->base + VFE_BUS_WM_CGC_OVERRIDE); + /* no clock gating at bus input; v900 has no such register */ + if (!IS_VFE_900(vfe)) + writel(WM_CGC_OVERRIDE_ALL, + vfe->base + VFE_BUS_WM_CGC_OVERRIDE); writel(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL); @@ -135,9 +163,14 @@ static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr, struct vfe_line *line) { wm = RDI_WM(wm); - writel(addr >> 8, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm)); - - dev_dbg(vfe->camss->dev, "wm:%d, image buf addr:0x%x\n", wm, addr); + /* + * v900's ADDR_IMAGE register takes a full byte address, unlike + * v980/v1080 which use a 256-byte-unit (addr >> 8) encoding. + */ + if (IS_VFE_900(vfe)) + writel(addr, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm)); + else + writel(addr >> 8, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm)); } static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index c12814085b077..ee5063ff14888 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -357,6 +357,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, case CAMSS_8775P: case CAMSS_GLYMUR: case CAMSS_KAANAPALI: + case CAMSS_NORD: case CAMSS_X1E80100: case CAMSS_X1P42100: switch (sink_code) { @@ -2024,6 +2025,7 @@ static int vfe_bpl_align_rdi(struct vfe_device *vfe) case CAMSS_8775P: case CAMSS_GLYMUR: case CAMSS_KAANAPALI: + case CAMSS_NORD: case CAMSS_X1E80100: case CAMSS_X1P42100: ret = 16; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index bf2e55af8a8c0..eaf0c1d62d2c1 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -732,6 +732,392 @@ static const struct camss_subdev_resources csid_res_nord[] = { } }; +static const struct camss_subdev_resources vfe_res_nord[] = { + /* VFE0 - IFE Full */ + { + .clock = { "gcc_axi_hf", "vfe0_fast_ahb", "vfe0", + "vfe0_pcp", "vfe0_pcp_fast_ahb", + "vfe0_tmc", "vfe0_tmc_fast_ahb", + "vfe0_scalar", "vfe0_scalar_fast_ahb", + "top_ahb", "top_fast_ahb", + "top_ife0", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 3, + .is_lite = false, + .reg_update_after_csid_config = true, + .has_pd = true, + .pd_name = "ife0", + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE1 - IFE Full */ + { + .clock = { "gcc_axi_hf", "vfe1_fast_ahb", "vfe1", + "vfe1_pcp", "vfe1_pcp_fast_ahb", + "vfe1_tmc", "vfe1_tmc_fast_ahb", + "vfe1_scalar", "vfe1_scalar_fast_ahb", + "top_ahb", "top_fast_ahb", + "top_ife1", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" }, + .vfe = { + .line_num = 3, + .is_lite = false, + .reg_update_after_csid_config = true, + .has_pd = true, + .pd_name = "ife1", + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE2 - IFE Full */ + { + .clock = { "gcc_axi_hf", "vfe2_fast_ahb", "vfe2", + "vfe2_pcp", "vfe2_pcp_fast_ahb", + "vfe2_tmc", "vfe2_tmc_fast_ahb", + "vfe2_scalar", "vfe2_scalar_fast_ahb", + "top_ahb", "top_fast_ahb", + "top_ife2", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe2" }, + .interrupt = { "vfe2" }, + .vfe = { + .line_num = 3, + .is_lite = false, + .reg_update_after_csid_config = true, + .has_pd = true, + .pd_name = "ife2", + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE0 - IFE Lite */ + { + .clock = { "gcc_axi_hf", "vfe_lite_ahb", "vfe_lite", + "top_ahb", "top_fast_ahb", + "top_ife_lite", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite0" }, + .interrupt = { "vfe_lite0" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .reg_update_after_csid_config = true, + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE1 - IFE Lite */ + { + .clock = { "gcc_axi_hf", "vfe_lite_ahb", "vfe_lite", + "top_ahb", "top_fast_ahb", + "top_ife_lite", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite1" }, + .interrupt = { "vfe_lite1" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .reg_update_after_csid_config = true, + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE2 - IFE Lite */ + { + .clock = { "gcc_axi_hf", "vfe_lite_ahb", "vfe_lite", + "top_ahb", "top_fast_ahb", + "top_ife_lite", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite2" }, + .interrupt = { "vfe_lite2" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .reg_update_after_csid_config = true, + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE3 - IFE Lite */ + { + .clock = { "gcc_axi_hf", "vfe_lite_ahb", "vfe_lite", + "top_ahb", "top_fast_ahb", + "top_ife_lite", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite3" }, + .interrupt = { "vfe_lite3" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .reg_update_after_csid_config = true, + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE4 - IFE Lite */ + { + .clock = { "gcc_axi_hf", "vfe_lite_ahb", "vfe_lite", + "top_ahb", "top_fast_ahb", + "top_ife_lite", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite4" }, + .interrupt = { "vfe_lite4" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .reg_update_after_csid_config = true, + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE5 - IFE Lite */ + { + .clock = { "gcc_axi_hf", "vfe_lite_ahb", "vfe_lite", + "top_ahb", "top_fast_ahb", + "top_ife_lite", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite5" }, + .interrupt = { "vfe_lite5" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .reg_update_after_csid_config = true, + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE6 - IFE Lite */ + { + .clock = { "gcc_axi_hf", "vfe_lite_ahb", "vfe_lite", + "top_ahb", "top_fast_ahb", + "top_ife_lite", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite6" }, + .interrupt = { "vfe_lite6" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .reg_update_after_csid_config = true, + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE7 - IFE Lite */ + { + .clock = { "gcc_axi_hf", "vfe_lite_ahb", "vfe_lite", + "top_ahb", "top_fast_ahb", + "top_ife_lite", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite7" }, + .interrupt = { "vfe_lite7" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .reg_update_after_csid_config = true, + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE8 - IFE Lite */ + { + .clock = { "gcc_axi_hf", "vfe_lite_ahb", "vfe_lite", + "top_ahb", "top_fast_ahb", + "top_ife_lite", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite8" }, + .interrupt = { "vfe_lite8" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .reg_update_after_csid_config = true, + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE_LITE9 - IFE Lite */ + { + .clock = { "gcc_axi_hf", "vfe_lite_ahb", "vfe_lite", + "top_ahb", "top_fast_ahb", + "top_ife_lite", "camnoc_rt_axi", "camnoc_nrt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 0 }, + { 0 } }, + .reg = { "vfe_lite9" }, + .interrupt = { "vfe_lite9" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .reg_update_after_csid_config = true, + .hw_ops = &vfe_ops_gen4, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + } +}; + static const struct resources_icc icc_res_nord[] = { { .name = "ahb", @@ -6736,11 +7122,13 @@ static const struct camss_resources nord_resources = { .legacy_phy = true, .csiphy_res = csiphy_res_nord, .csid_res = csid_res_nord, + .vfe_res = vfe_res_nord, .tpg_res = tpg_res_nord, .icc_res = icc_res_nord, .icc_path_num = ARRAY_SIZE(icc_res_nord), .csiphy_num = ARRAY_SIZE(csiphy_res_nord), .csid_num = ARRAY_SIZE(csid_res_nord), + .vfe_num = ARRAY_SIZE(vfe_res_nord), .tpg_num = ARRAY_SIZE(tpg_res_nord), }; From d4df0773a9ffc52621ec318e2bae53d8bbdc6449 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Mon, 20 Jul 2026 19:57:51 +0800 Subject: [PATCH 6/6] arm64: dts: qcom: nord: Add camss node Add the camera subsystem node for the Nord SoC, describing its 5 CSIPHY, 3 CSID + 10 CSID lite and 3 IFE + 10 IFE lite blocks, their clocks, interrupts, interconnects, SMMU stream IDs and power domains. The node is left disabled; it is enabled per board once the CSI input ports and CSIPHY supplies are wired to the on-board sensors. Signed-off-by: Wenmeng Liu --- arch/arm64/boot/dts/qcom/nord-embedded.dtsi | 278 ++++++++++++++++++++ 1 file changed, 278 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi index 7ce6d21a2e730..67abe95e365ec 100644 --- a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi +++ b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi @@ -792,6 +792,284 @@ #power-domain-cells = <1>; }; + camss: isp@9b32000 { + compatible = "qcom,nord-camss"; + + reg = <0 0x09b32000 0 0x2000>, + <0 0x09b38800 0 0x2000>, + <0 0x09b3f000 0 0x2000>, + <0 0x09c32000 0 0x1000>, + <0 0x09c37000 0 0x1000>, + <0 0x09c3c000 0 0x1000>, + <0 0x09c41000 0 0x1000>, + <0 0x09c46000 0 0x1000>, + <0 0x09c4b000 0 0x1000>, + <0 0x09c50000 0 0x1000>, + <0 0x09c55000 0 0x1000>, + <0 0x09c5a000 0 0x1000>, + <0 0x09c5f000 0 0x1000>, + <0 0x09c7b000 0 0x2000>, + <0 0x09c7d000 0 0x2000>, + <0 0x09c7f000 0 0x2000>, + <0 0x09c81000 0 0x2000>, + <0 0x09c83000 0 0x2000>, + <0 0x09a92000 0 0x400>, + <0 0x09a93000 0 0x400>, + <0 0x09a94000 0 0x400>, + <0 0x09ab1000 0 0x4000>, + <0 0x09ad1000 0 0x4000>, + <0 0x09af1000 0 0x4000>, + <0 0x09c34000 0 0x2000>, + <0 0x09c39000 0 0x2000>, + <0 0x09c3e000 0 0x2000>, + <0 0x09c43000 0 0x2000>, + <0 0x09c48000 0 0x2000>, + <0 0x09c4d000 0 0x2000>, + <0 0x09c52000 0 0x2000>, + <0 0x09c57000 0 0x2000>, + <0 0x09c5c000 0 0x2000>, + <0 0x09c61000 0 0x2000>; + + reg-names = "csid0", + "csid1", + "csid2", + "csid_lite0", + "csid_lite1", + "csid_lite2", + "csid_lite3", + "csid_lite4", + "csid_lite5", + "csid_lite6", + "csid_lite7", + "csid_lite8", + "csid_lite9", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "csiphy4", + "csitpg0", + "csitpg1", + "csitpg2", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite0", + "vfe_lite1", + "vfe_lite2", + "vfe_lite3", + "vfe_lite4", + "vfe_lite5", + "vfe_lite6", + "vfe_lite7", + "vfe_lite8", + "vfe_lite9"; + + clocks = <&camcc CAM_CC_CAMNOC_NRT_AXI_CLK>, + <&camcc CAM_CC_CAMNOC_RT_AXI_CLK>, + <&camcc CAM_CC_CORE_AHB_CLK>, + <&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_CSIPHY3_CLK>, + <&camcc CAM_CC_CSI3PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY4_CLK>, + <&camcc CAM_CC_CSI4PHYTIMER_CLK>, + <&nwgcc NW_GCC_CAMERA_HF_AXI_CLK>, + <&camcc CAM_CC_TOP_AHB_CLK>, + <&camcc CAM_CC_TOP_FAST_AHB_CLK>, + <&camcc CAM_CC_TOP_IFE_0_CLK>, + <&camcc CAM_CC_TOP_IFE_1_CLK>, + <&camcc CAM_CC_TOP_IFE_2_CLK>, + <&camcc CAM_CC_TOP_IFE_LITE_CLK>, + <&camcc CAM_CC_IFE_0_MAIN_CLK>, + <&camcc CAM_CC_IFE_0_MAIN_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_0_PCP_CLK>, + <&camcc CAM_CC_IFE_0_PCP_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_0_TMC_CLK>, + <&camcc CAM_CC_IFE_0_TMC_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_0_SCALAR_CLK>, + <&camcc CAM_CC_IFE_0_SCALAR_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_1_MAIN_CLK>, + <&camcc CAM_CC_IFE_1_MAIN_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_1_PCP_CLK>, + <&camcc CAM_CC_IFE_1_PCP_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_1_TMC_CLK>, + <&camcc CAM_CC_IFE_1_TMC_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_1_SCALAR_CLK>, + <&camcc CAM_CC_IFE_1_SCALAR_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_2_MAIN_CLK>, + <&camcc CAM_CC_IFE_2_MAIN_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_2_PCP_CLK>, + <&camcc CAM_CC_IFE_2_PCP_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_2_TMC_CLK>, + <&camcc CAM_CC_IFE_2_TMC_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_2_SCALAR_CLK>, + <&camcc CAM_CC_IFE_2_SCALAR_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>, + <&camcc CAM_CC_TPG_CSIPHY_RX_CLK>, + <&camcc CAM_CC_QDSS_DEBUG_XO_CLK>; + + clock-names = "camnoc_nrt_axi", + "camnoc_rt_axi", + "core_ahb", + "csid", + "csid_csiphy_rx", + "csiphy0", + "csiphy0_timer", + "csiphy1", + "csiphy1_timer", + "csiphy2", + "csiphy2_timer", + "csiphy3", + "csiphy3_timer", + "csiphy4", + "csiphy4_timer", + "gcc_axi_hf", + "top_ahb", + "top_fast_ahb", + "top_ife0", + "top_ife1", + "top_ife2", + "top_ife_lite", + "vfe0", + "vfe0_fast_ahb", + "vfe0_pcp", + "vfe0_pcp_fast_ahb", + "vfe0_tmc", + "vfe0_tmc_fast_ahb", + "vfe0_scalar", + "vfe0_scalar_fast_ahb", + "vfe1", + "vfe1_fast_ahb", + "vfe1_pcp", + "vfe1_pcp_fast_ahb", + "vfe1_tmc", + "vfe1_tmc_fast_ahb", + "vfe1_scalar", + "vfe1_scalar_fast_ahb", + "vfe2", + "vfe2_fast_ahb", + "vfe2_pcp", + "vfe2_pcp_fast_ahb", + "vfe2_tmc", + "vfe2_tmc_fast_ahb", + "vfe2_scalar", + "vfe2_scalar_fast_ahb", + "vfe_lite", + "vfe_lite_ahb", + "vfe_lite_cphy_rx", + "vfe_lite_csid", + "tpg_csiphy_rx", + "qdss_debug_xo"; + + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + + interrupt-names = "csid0", + "csid1", + "csid2", + "csid_lite0", + "csid_lite1", + "csid_lite2", + "csid_lite3", + "csid_lite4", + "csid_lite5", + "csid_lite6", + "csid_lite7", + "csid_lite8", + "csid_lite9", + "csiphy0", + "csiphy1", + "csiphy2", + "csiphy3", + "csiphy4", + "csitpg0", + "csitpg1", + "csitpg2", + "vfe0", + "vfe1", + "vfe2", + "vfe_lite0", + "vfe_lite1", + "vfe_lite2", + "vfe_lite3", + "vfe_lite4", + "vfe_lite5", + "vfe_lite6", + "vfe_lite7", + "vfe_lite8", + "vfe_lite9"; + + interconnects = <&hscnoc 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>; + + interconnect-names = "ahb", + "hf_mnoc"; + + iommus = <&apps_smmu_2 0x1401 0x20>, + <&apps_smmu_2 0x1460 0x00>; + + power-domains = <&camcc CAM_CC_IFE_0_GDSC>, + <&camcc CAM_CC_IFE_1_GDSC>, + <&camcc CAM_CC_IFE_2_GDSC>, + <&camcc CAM_CC_TITAN_TOP_GDSC>; + + power-domain-names = "ife0", + "ife1", + "ife2", + "top"; + + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + }; + }; + camcc: clock-controller@9ce0000 { compatible = "qcom,nord-camcc"; reg = <0x0 0x09ce0000 0x0 0x20000>;