From 0dbf06ad997224417b90a8268898d6e20a40b9da 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 | 507 ++++++++++++++++++ 1 file changed, 507 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..f9c5067e00153 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,nord-camss.yaml @@ -0,0 +1,507 @@ +# 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: 32 + + clock-names: + items: + - const: camnoc_rt_axi + - 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: vfe1 + - const: vfe1_fast_ahb + - const: vfe2 + - const: vfe2_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: 31 + + 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: 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 + + 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_RT_AXI_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_1_MAIN_CLK>, + <&camcc CAM_CC_IFE_1_MAIN_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_2_MAIN_CLK>, + <&camcc CAM_CC_IFE_2_MAIN_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_rt_axi", + "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", + "vfe1", + "vfe1_fast_ahb", + "vfe2", + "vfe2_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", + "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"; + + 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 afc15d98ba778e0d0b8dc89f5b436aedbe43911b 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 Nord camera subsystem. Nord is a gen4 Titan ISP based on Kaanapali. Nord has 5 CSIPHY (v2.4.0), which is already supported in-tree, so only a CAMSS_NORD entry in the shared switch-cases in camss-csiphy-3ph-1-0.c is required and no new CSIPHY register code is added. Unlike Kaanapali and SM8750, 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. The CSID, VFE and TPG resource tables are added in the following patches. Signed-off-by: Wenmeng Liu --- .../qcom/camss/camss-csiphy-3ph-1-0.c | 2 + drivers/media/platform/qcom/camss/camss.c | 128 ++++++++++++++++++ drivers/media/platform/qcom/camss/camss.h | 1 + 3 files changed, 131 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..476ae8f70fe7d 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -381,6 +381,123 @@ 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", + "top_ahb", "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000 }, + { 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", + "top_ahb", "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000 }, + { 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", + "top_ahb", "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000 }, + { 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", + "top_ahb", "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000 }, + { 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", + "top_ahb", "top_fast_ahb" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000 }, + { 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 +6481,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 +6805,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 3181fe19aa3fa7728ec8671108ed16e2d05cf4f3 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 uses TPG v2.5.0, which moves 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: revisions before v2.5.0 use the TPG_CTRL.TEST_EN bit, while v2.5.0 enables the generator through TPG_CTRL_CMD.TEST_EN. The reset bit stays at bit 0 of the 0x1F4 register on every revision, so tpg_reset() needs no version check; it also runs before the hardware version is read. Preserve the CFA pattern size (SIZE_X/SIZE_Y) at its reset value in TPG_COLOR_BARS_CFG_STANDARD. Writing only ROTATE_PERIOD zeroed those fields and reduced the pattern to 1x1, producing no valid output. Add the Nord TPG resource table (tpg_res_nord) to nord_resources. Signed-off-by: Wenmeng Liu --- .../platform/qcom/camss/camss-tpg-gen1.c | 27 ++++++++++-- drivers/media/platform/qcom/camss/camss.c | 41 +++++++++++++++++++ 2 files changed, 65 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..be9c22c4b1d13 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,11 +180,16 @@ 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; } diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 476ae8f70fe7d..b5277f7d7525b 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -484,6 +484,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", @@ -6486,9 +6525,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 cc9fc983baef3a9d2758ae34b086eda83ace17c4 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 driver for the "900" revision on the Nord SoC. CSID 900 shares the gen4 CSI-2 RX helpers and the split RUP/AUP command model of CSID 980, where an explicit SET is required after programming the AUP/RUP registers. Unlike CSID 980 it uses a single register map for both the full and lite instances; only the RDI region base differs (full at 0x1300, lite at 0x600, 0x200 stride). CSID 980 forks the lite layout, so add a separate file rather than an IS_CSID_900() variant. Nord has no test pattern generator wired to the CSID, so the testgen path is a no-op, as on the other gen4 platforms. Add the Nord CSID resource table (csid_res_nord) to nord_resources. Signed-off-by: Wenmeng Liu --- drivers/media/platform/qcom/camss/Makefile | 1 + .../platform/qcom/camss/camss-csid-900.c | 420 ++++++++++++++++++ .../media/platform/qcom/camss/camss-csid.h | 1 + drivers/media/platform/qcom/camss/camss.c | 200 +++++++++ 4 files changed, 622 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..c99890701685b --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csid-900.c @@ -0,0 +1,420 @@ +// 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) + +static void __csid_aup_rup_trigger(struct csid_device *csid) +{ + 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; + 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); +} + +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); + + __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 b5277f7d7525b..b84bb19b8b83c 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -523,6 +523,204 @@ 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" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 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" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 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" }, + .clock_rate = { { 400000000, 480000000 }, + { 400000000, 480000000 }, + { 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", @@ -6525,10 +6723,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 32f45c441fcc29e14a6aef367d7cd25ec47eb700 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. Add an IS_VFE_900() variant in camss-vfe-gen4.c to handle the differences from the existing gen4 (980/1080) layout: - 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 moves 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 the Nord VFE resource table (vfe_res_nord) to nord_resources. Signed-off-by: Wenmeng Liu --- .../platform/qcom/camss/camss-vfe-gen4.c | 47 ++- drivers/media/platform/qcom/camss/camss-vfe.c | 2 + drivers/media/platform/qcom/camss/camss.c | 339 ++++++++++++++++++ 3 files changed, 382 insertions(+), 6 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..3b700340d9fd6 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,7 +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)); + /* + * 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)); dev_dbg(vfe->camss->dev, "wm:%d, image buf addr:0x%x\n", wm, addr); } 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 b84bb19b8b83c..47205d7657ae4 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -721,6 +721,343 @@ 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", + "top_ahb", "top_fast_ahb", + "top_ife0", "camnoc_rt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "top_ahb", "top_fast_ahb", + "top_ife1", "camnoc_rt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "top_ahb", "top_fast_ahb", + "top_ife2", "camnoc_rt_axi", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 360280000, 480000000, 630000000, 716000000, + 833000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", + "qdss_debug_xo" }, + .clock_rate = { { 0 }, + { 0 }, + { 266666667, 400000000, 480000000 }, + { 0 }, + { 0 }, + { 0 }, + { 200000000, 300000000, 400000000, 480000000 }, + { 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", @@ -6724,11 +7061,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 13be9e128254c2234e23145e7eb17e68d881777d 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 with its 5 CSIPHY, 3 CSID plus 10 CSID lite and 3 IFE plus 10 IFE lite blocks, along with their clocks, interrupts, interconnects, SMMU stream IDs and power domains. The node is enabled at the SoC level with the CSIPHY input ports left as empty stubs. Each board fills in the port endpoints and CSIPHY supplies for its on-board sensors. Signed-off-by: Wenmeng Liu --- arch/arm64/boot/dts/qcom/nord-embedded.dtsi | 252 ++++++++++++++++++++ 1 file changed, 252 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi index ad2596806a6e2..d41958be114fa 100644 --- a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi +++ b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi @@ -1039,6 +1039,258 @@ #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_RT_AXI_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_1_MAIN_CLK>, + <&camcc CAM_CC_IFE_1_MAIN_FAST_AHB_CLK>, + <&camcc CAM_CC_IFE_2_MAIN_CLK>, + <&camcc CAM_CC_IFE_2_MAIN_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_rt_axi", + "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", + "vfe1", + "vfe1_fast_ahb", + "vfe2", + "vfe2_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", + "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>; + + port@0 { + reg = <0>; + }; + + port@1 { + reg = <1>; + }; + + port@2 { + reg = <2>; + }; + + port@3 { + reg = <3>; + }; + + port@4 { + reg = <4>; + }; + }; + }; + camcc: clock-controller@9ce0000 { compatible = "qcom,nord-camcc"; reg = <0x0 0x09ce0000 0x0 0x20000>;