From 9ca47b45ceeef2f6dad05d76d8f6ed11804f9b5f Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 31 Jul 2026 17:47:05 +0800 Subject: [PATCH 1/6] PENDING: dt-bindings: media: Add bindings for qcom,nord-camss Add bindings for the camera subsystem (CAMSS) on the Qualcomm Nord platform. The Nord platform provides: - 5 x CSIPHY (CSI Physical Layer) - 3 x TPG (Test Pattern Generator) - 3 x CSID (CSI Decoder) - 10 x CSID Lite - 3 x VFE (Video Front End), 12 RDI per VFE - 10 x VFE Lite, 8 RDI per VFE Lite Signed-off-by: Wenmeng Liu --- .../bindings/media/qcom,nord-camss.yaml | 393 ++++++++++++++++++ 1 file changed, 393 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..93dbb55e1a974 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,nord-camss.yaml @@ -0,0 +1,393 @@ +# 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: 29 + + 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: 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: 22 + + clock-names: + items: + - const: camnoc_rt_axi + - const: csid + - const: csid_csiphy_rx + - 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: 26 + + 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: 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 + + 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. + + 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: + - 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 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", + "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>, + <&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", + "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", + "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"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + endpoint { + data-lanes = <0 1 2 3>; + remote-endpoint = <&sensor_ep>; + }; + }; + }; + }; + }; From 1bd44b621a3c3d6a09fc2dc0c7e478bde5791e00 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 31 Jul 2026 17:47:32 +0800 Subject: [PATCH 2/6] PENDING: media: qcom: camss: Add nord compatible camss driver Add support for Nord in the camss driver. Add high level resource information along with the bus bandwidth votes. Module level detailed resource information will be enumerated in the following patches of the series. Signed-off-by: Wenmeng Liu --- drivers/media/platform/qcom/camss/camss.c | 22 ++++++++++++++++++++++ drivers/media/platform/qcom/camss/camss.h | 1 + 2 files changed, 23 insertions(+) diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index a00af0c718dcd..2037ed9568638 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -381,6 +381,20 @@ static const struct resources_icc icc_res_kaanapali[] = { }, }; +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 +6378,13 @@ 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", + .icc_res = icc_res_nord, + .icc_path_num = ARRAY_SIZE(icc_res_nord), +}; + static const struct camss_resources msm8916_resources = { .version = CAMSS_8x16, .legacy_phy = true, @@ -6678,6 +6699,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 507279e5009f5780114b6fc180cd54dace1108e6 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 31 Jul 2026 17:47:50 +0800 Subject: [PATCH 3/6] PENDING: media: qcom: camss: tpg: Add support for TPG v2.5.0 Nord uses TPG v2.5.0, which moves the stream enable bit from TPG_CTRL to a dedicated command register (TPG_CTRL_CMD). Branch tpg_stream_on() on the runtime hw_version to enable the generator accordingly; the reset bit is unchanged. 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 2037ed9568638..f04cef6698478 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -381,6 +381,45 @@ static const struct resources_icc icc_res_kaanapali[] = { }, }; +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", @@ -6381,8 +6420,10 @@ static const struct camss_resources kaanapali_resources = { static const struct camss_resources nord_resources = { .version = CAMSS_NORD, .pd_name = "top", + .tpg_res = tpg_res_nord, .icc_res = icc_res_nord, .icc_path_num = ARRAY_SIZE(icc_res_nord), + .tpg_num = ARRAY_SIZE(tpg_res_nord), }; static const struct camss_resources msm8916_resources = { From 04805b491021d210ea2df502049d41d2ccb6f7bc Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 31 Jul 2026 17:48:33 +0800 Subject: [PATCH 4/6] PENDING: media: qcom: camss: csid: Add support for CSID 900 Add a CSID driver for the "900" revision on the Nord SoC. It shares the CSI-2 RX helpers and the split RUP/AUP command model of CSID 980, but uses a single register map for both the full and lite instances, so add a separate file rather than a variant of CSID 980. Signed-off-by: Wenmeng Liu --- drivers/media/platform/qcom/camss/Makefile | 1 + .../platform/qcom/camss/camss-csid-900.c | 363 ++++++++++++++++++ .../media/platform/qcom/camss/camss-csid.h | 1 + drivers/media/platform/qcom/camss/camss.c | 200 ++++++++++ 4 files changed, 565 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..18aaa6e1bf9cf --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csid-900.c @@ -0,0 +1,363 @@ +// 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" + +#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) + +#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) + +#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) + +#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 + +#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 + +#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) +#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) + +#define CSID_RDI_BASE (csid_is_lite(csid) ? 0x600 : 0x1300) +#define CSID_RDI_CFG0(rdi) (CSID_RDI_BASE + 0x0 + 0x200 * (rdi)) +#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) + +#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) + +#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 + +#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); + + __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) +{ + 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; + + 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; + + 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); + + 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); + + __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); +} + +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; +} + +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)) { + 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); + + 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 f04cef6698478..2993ba7f258d8 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -420,6 +420,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", @@ -6420,9 +6618,11 @@ static const struct camss_resources kaanapali_resources = { static const struct camss_resources nord_resources = { .version = CAMSS_NORD, .pd_name = "top", + .csid_res = csid_res_nord, .tpg_res = tpg_res_nord, .icc_res = icc_res_nord, .icc_path_num = ARRAY_SIZE(icc_res_nord), + .csid_num = ARRAY_SIZE(csid_res_nord), .tpg_num = ARRAY_SIZE(tpg_res_nord), }; From 9558b2068fbdd88d23caf514e6b48e61998986f4 Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Fri, 31 Jul 2026 17:48:57 +0800 Subject: [PATCH 5/6] PENDING: media: qcom: camss: vfe: Add support for VFE 900 The Nord IFE (VFE) reuses the VFE ops of the 980/1080 IFE but places the bus write-master register block at a different base and drops the WM_CGC_OVERRIDE register. Add an IS_VFE_900() variant in camss-vfe-gen4.c to handle these differences; all other write-master register offsets are reused unchanged. 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 2993ba7f258d8..3025e742047bf 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -618,6 +618,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", @@ -6619,10 +6956,12 @@ static const struct camss_resources nord_resources = { .version = CAMSS_NORD, .pd_name = "top", .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), .csid_num = ARRAY_SIZE(csid_res_nord), + .vfe_num = ARRAY_SIZE(vfe_res_nord), .tpg_num = ARRAY_SIZE(tpg_res_nord), }; From af7fac47b2dfae46564947ba4d12581d5634f62d Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Mon, 20 Jul 2026 19:57:51 +0800 Subject: [PATCH 6/6] PENDING: 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. Signed-off-by: Wenmeng Liu --- arch/arm64/boot/dts/qcom/nord-embedded.dtsi | 187 ++++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi index ad2596806a6e2..b1c27d7c0f0df 100644 --- a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi +++ b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi @@ -1039,6 +1039,193 @@ #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 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", + "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>, + <&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", + "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", + "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"; + }; + camcc: clock-controller@9ce0000 { compatible = "qcom,nord-camcc"; reg = <0x0 0x09ce0000 0x0 0x20000>;