From ab7e4d7cf6f8efd37a675a85eb0aef3d6a4954ba Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 7 May 2026 16:02:58 +0300 Subject: [PATCH 01/67] soc: qcom: ubwc: define UBWC 3.1 Follow the comment for the macrotile_mode and introduce separate revision for UBWC 3.0 + 8-channel macrotiling mode. It is not used by the database (since the drivers are not yet changed to handle it yet). Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260507-ubwc-rework-v4-2-c19593d20c1d@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- include/linux/soc/qcom/ubwc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index f5d0e2341261..319caed88775 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -50,6 +50,7 @@ struct qcom_ubwc_cfg_data { #define UBWC_1_0 0x10000000 #define UBWC_2_0 0x20000000 #define UBWC_3_0 0x30000000 +#define UBWC_3_1 0x30010000 /* UBWC 3.0 + Macrotile mode */ #define UBWC_4_0 0x40000000 #define UBWC_4_3 0x40030000 #define UBWC_5_0 0x50000000 From b5f7365c44d8fd58ef0224bf4111805c4fea0a1e Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 7 May 2026 16:02:59 +0300 Subject: [PATCH 02/67] soc: qcom: ubwc: define helper for MDSS and Adreno drivers Define special helper returning version setting for MDSS and A8xx drivers. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260507-ubwc-rework-v4-3-c19593d20c1d@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- include/linux/soc/qcom/ubwc.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 319caed88775..8355ffe40f88 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -100,4 +100,20 @@ static inline u32 qcom_ubwc_swizzle(const struct qcom_ubwc_cfg_data *cfg) return cfg->ubwc_swizzle; } +static inline u32 qcom_ubwc_version_tag(const struct qcom_ubwc_cfg_data *cfg) +{ + if (cfg->ubwc_enc_version >= UBWC_6_0) + return 5; + if (cfg->ubwc_enc_version >= UBWC_5_0) + return 4; + if (cfg->ubwc_enc_version >= UBWC_4_3) + return 3; + if (cfg->ubwc_enc_version >= UBWC_4_0) + return 2; + if (cfg->ubwc_enc_version >= UBWC_3_0) + return 1; + + return 0; +} + #endif /* __QCOM_UBWC_H__ */ From c5e3f2a3abcb925f0364df09abfd759ff0590454 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 7 May 2026 16:03:00 +0300 Subject: [PATCH 03/67] soc: qcom: ubwc: add helper controlling AMSBC enablement Adreno and MDSS drivers need to know whether to enable AMSBC. Add separate helper, describing that feature. Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260507-ubwc-rework-v4-4-c19593d20c1d@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- include/linux/soc/qcom/ubwc.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 8355ffe40f88..83d2c2a7116c 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -116,4 +116,9 @@ static inline u32 qcom_ubwc_version_tag(const struct qcom_ubwc_cfg_data *cfg) return 0; } +static inline bool qcom_ubwc_enable_amsbc(const struct qcom_ubwc_cfg_data *cfg) +{ + return cfg->ubwc_enc_version >= UBWC_3_0; +} + #endif /* __QCOM_UBWC_H__ */ From 8db813dc086d9257af4f20dd4dfa873021f1a504 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 22 May 2026 02:22:07 +0300 Subject: [PATCH 04/67] drm/ci: disable mr-label-maker-test The MR labelling is not used for DRM CI, however the job got enabled as a part of the CI pipeline and now prevents it from being executed. Disable the mr-label-maker-test job implicitly. Link: https://gitlab.freedesktop.org/drm/msm/-/pipelines/1672049 Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/ci/gitlab-ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/ci/gitlab-ci.yml b/drivers/gpu/drm/ci/gitlab-ci.yml index 56088c5393cd..c1087731d1a2 100644 --- a/drivers/gpu/drm/ci/gitlab-ci.yml +++ b/drivers/gpu/drm/ci/gitlab-ci.yml @@ -386,6 +386,10 @@ linkcheck-docs: rules: - when: never +mr-label-maker-test: + rules: + - when: never + test-docs: rules: - when: never From 41abd39cd1396aa1ad9c2bece6443baed3a01d18 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 09:14:43 +0200 Subject: [PATCH 05/67] dt-bindings: display: msm-dsi-phy-7nm: document the Milos DSI PHY Document the DSI PHY on the Milos Platform. Acked-by: Rob Herring (Arm) Signed-off-by: Luca Weiss Patchwork: https://patchwork.freedesktop.org/patch/722309/ Link: https://lore.kernel.org/r/20260501-milos-mdss-v3-1-58bfc58c0e13@fairphone.com Signed-off-by: Dmitry Baryshkov --- Documentation/devicetree/bindings/phy/qcom,dsi-phy-7nm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,dsi-phy-7nm.yaml b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-7nm.yaml index 966c70d746aa..f397ba3fa84a 100644 --- a/Documentation/devicetree/bindings/phy/qcom,dsi-phy-7nm.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,dsi-phy-7nm.yaml @@ -20,6 +20,7 @@ properties: - qcom,dsi-phy-7nm - qcom,dsi-phy-7nm-8150 - qcom,kaanapali-dsi-phy-3nm + - qcom,milos-dsi-phy-4nm - qcom,sa8775p-dsi-phy-5nm - qcom,sar2130p-dsi-phy-5nm - qcom,sc7280-dsi-phy-7nm From e0e560c56b6c488c30a445632bb2053a85af62f5 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 09:14:44 +0200 Subject: [PATCH 06/67] dt-bindings: display: msm-dsi-controller-main: document the Milos DSI Controller Document the DSI Controller on the Milos Platform. Acked-by: Rob Herring (Arm) Signed-off-by: Luca Weiss Patchwork: https://patchwork.freedesktop.org/patch/722310/ Link: https://lore.kernel.org/r/20260501-milos-mdss-v3-2-58bfc58c0e13@fairphone.com Signed-off-by: Dmitry Baryshkov --- .../devicetree/bindings/display/msm/dsi-controller-main.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml index a24fcb914418..dbc0613e427e 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml +++ b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml @@ -16,6 +16,7 @@ properties: - enum: - qcom,apq8064-dsi-ctrl - qcom,kaanapali-dsi-ctrl + - qcom,milos-dsi-ctrl - qcom,msm8226-dsi-ctrl - qcom,msm8916-dsi-ctrl - qcom,msm8953-dsi-ctrl @@ -339,6 +340,7 @@ allOf: compatible: contains: enum: + - qcom,milos-dsi-ctrl - qcom,msm8998-dsi-ctrl - qcom,sa8775p-dsi-ctrl - qcom,sar2130p-dsi-ctrl From d55d6176f3b5f458a26d095cbe3b75a2c6836383 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 09:14:45 +0200 Subject: [PATCH 07/67] dt-bindings: display: msm: document the Milos DPU Document the DPU Display Controller on the Milos Platform. Acked-by: Rob Herring (Arm) Signed-off-by: Luca Weiss Patchwork: https://patchwork.freedesktop.org/patch/722313/ Link: https://lore.kernel.org/r/20260501-milos-mdss-v3-3-58bfc58c0e13@fairphone.com Signed-off-by: Dmitry Baryshkov --- .../devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml b/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml index 134321b50897..aa0cf0ec5b93 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml @@ -18,6 +18,7 @@ properties: - qcom,eliza-dpu - qcom,glymur-dpu - qcom,kaanapali-dpu + - qcom,milos-dpu - qcom,sa8775p-dpu - qcom,sm8650-dpu - qcom,sm8750-dpu From 1bfae9f58318bb8cf921d806df8c5a420021ec41 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 09:14:46 +0200 Subject: [PATCH 08/67] dt-bindings: display: msm: document the Milos Mobile Display Subsystem Document the Mobile Display Subsystem (MDSS) on the Milos SoC. Signed-off-by: Luca Weiss Reviewed-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/722315/ Link: https://lore.kernel.org/r/20260501-milos-mdss-v3-4-58bfc58c0e13@fairphone.com Signed-off-by: Dmitry Baryshkov --- .../bindings/display/msm/qcom,milos-mdss.yaml | 286 ++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/msm/qcom,milos-mdss.yaml diff --git a/Documentation/devicetree/bindings/display/msm/qcom,milos-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,milos-mdss.yaml new file mode 100644 index 000000000000..7010ffa0ae35 --- /dev/null +++ b/Documentation/devicetree/bindings/display/msm/qcom,milos-mdss.yaml @@ -0,0 +1,286 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/msm/qcom,milos-mdss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Milos Display MDSS + +maintainers: + - Luca Weiss + +description: + Milos MSM Mobile Display Subsystem(MDSS), which encapsulates sub-blocks like + DPU display controller, DSI and DP interfaces etc. + +$ref: /schemas/display/msm/mdss-common.yaml# + +properties: + compatible: + const: qcom,milos-mdss + + clocks: + items: + - description: Display AHB + - description: Display hf AXI + - description: Display core + + iommus: + maxItems: 1 + + interconnects: + items: + - description: Interconnect path from mdp0 port to the data bus + - description: Interconnect path from CPU to the reg bus + + interconnect-names: + items: + - const: mdp0-mem + - const: cpu-cfg + +patternProperties: + "^display-controller@[0-9a-f]+$": + type: object + additionalProperties: true + properties: + compatible: + const: qcom,milos-dpu + + "^displayport-controller@[0-9a-f]+$": + type: object + additionalProperties: true + properties: + compatible: + const: qcom,milos-dp + + "^dsi@[0-9a-f]+$": + type: object + additionalProperties: true + properties: + compatible: + contains: + const: qcom,milos-dsi-ctrl + + "^phy@[0-9a-f]+$": + type: object + additionalProperties: true + properties: + compatible: + const: qcom,milos-dsi-phy-4nm + +required: + - compatible + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + #include + #include + + display-subsystem@ae00000 { + compatible = "qcom,milos-mdss"; + reg = <0x0ae00000 0x1000>; + reg-names = "mdss"; + + interrupts = ; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>, + <&dispcc DISP_CC_MDSS_MDP_CLK>; + + resets = <&dispcc DISP_CC_MDSS_CORE_BCR>; + + interconnects = <&mmss_noc MASTER_MDP QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &cnoc_main SLAVE_DISPLAY_CFG QCOM_ICC_TAG_ACTIVE_ONLY>; + interconnect-names = "mdp0-mem", + "cpu-cfg"; + + power-domains = <&dispcc DISP_CC_MDSS_CORE_GDSC>; + + iommus = <&apps_smmu 0x1c00 0x2>; + + interrupt-controller; + #interrupt-cells = <1>; + + #address-cells = <1>; + #size-cells = <1>; + ranges; + + display-controller@ae01000 { + compatible = "qcom,milos-dpu"; + reg = <0x0ae01000 0x8f000>, + <0x0aeb0000 0x3000>; + reg-names = "mdp", + "vbif"; + + interrupts-extended = <&mdss 0>; + + clocks = <&gcc GCC_DISP_HF_AXI_CLK>, + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&dispcc DISP_CC_MDSS_MDP_LUT_CLK>, + <&dispcc DISP_CC_MDSS_MDP_CLK>, + <&dispcc DISP_CC_MDSS_VSYNC_CLK>; + clock-names = "nrt_bus", + "iface", + "lut", + "core", + "vsync"; + + assigned-clocks = <&dispcc DISP_CC_MDSS_VSYNC_CLK>; + assigned-clock-rates = <19200000>; + + operating-points-v2 = <&mdp_opp_table>; + + power-domains = <&rpmhpd RPMHPD_CX>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dpu_intf1_out: endpoint { + remote-endpoint = <&mdss_dsi0_in>; + }; + }; + }; + + mdp_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-342000000 { + opp-hz = /bits/ 64 <342000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-402000000 { + opp-hz = /bits/ 64 <402000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-535000000 { + opp-hz = /bits/ 64 <535000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + + opp-600000000 { + opp-hz = /bits/ 64 <600000000>; + required-opps = <&rpmhpd_opp_nom_l1>; + }; + + opp-630000000 { + opp-hz = /bits/ 64 <630000000>; + required-opps = <&rpmhpd_opp_turbo>; + }; + }; + }; + + dsi@ae94000 { + compatible = "qcom,milos-dsi-ctrl", "qcom,mdss-dsi-ctrl"; + reg = <0x0ae94000 0x1000>; + reg-names = "dsi_ctrl"; + + interrupts-extended = <&mdss 4>; + + clocks = <&dispcc DISP_CC_MDSS_BYTE0_CLK>, + <&dispcc DISP_CC_MDSS_BYTE0_INTF_CLK>, + <&dispcc DISP_CC_MDSS_PCLK0_CLK>, + <&dispcc DISP_CC_MDSS_ESC0_CLK>, + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus"; + + assigned-clocks = <&dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>, + <&dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>; + assigned-clock-parents = <&mdss_dsi0_phy DSI_BYTE_PLL_CLK>, + <&mdss_dsi0_phy DSI_PIXEL_PLL_CLK>; + + operating-points-v2 = <&mdss_dsi_opp_table>; + + power-domains = <&rpmhpd RPMHPD_CX>; + + phys = <&mdss_dsi0_phy>; + phy-names = "dsi"; + + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mdss_dsi0_in: endpoint { + remote-endpoint = <&dpu_intf1_out>; + }; + }; + + port@1 { + reg = <1>; + + mdss_dsi0_out: endpoint { + }; + }; + }; + + mdss_dsi_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-187500000 { + opp-hz = /bits/ 64 <187500000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-358000000 { + opp-hz = /bits/ 64 <358000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + }; + }; + + mdss_dsi0_phy: phy@ae95000 { + compatible = "qcom,milos-dsi-phy-4nm"; + reg = <0x0ae95000 0x200>, + <0x0ae95200 0x300>, + <0x0ae95500 0x400>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", + "ref"; + + #clock-cells = <1>; + #phy-cells = <0>; + }; + }; +... From 11afc9eb680df36823a3d958f44f1d18f5370edf Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 09:14:48 +0200 Subject: [PATCH 09/67] drm/msm/dsi: add support for DSI-PHY on Milos Add DSI PHY support for the Milos platform. Reviewed-by: Dmitry Baryshkov Signed-off-by: Luca Weiss Patchwork: https://patchwork.freedesktop.org/patch/722319/ Link: https://lore.kernel.org/r/20260501-milos-mdss-v3-6-58bfc58c0e13@fairphone.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/phy/dsi_phy.c | 2 ++ drivers/gpu/drm/msm/dsi/phy/dsi_phy.h | 1 + drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c | 23 +++++++++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c index c59375aaae19..1fb3899b88bf 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c @@ -571,6 +571,8 @@ static const struct of_device_id dsi_phy_dt_match[] = { .data = &dsi_phy_5nm_8350_cfgs }, { .compatible = "qcom,sm8450-dsi-phy-5nm", .data = &dsi_phy_5nm_8450_cfgs }, + { .compatible = "qcom,milos-dsi-phy-4nm", + .data = &dsi_phy_4nm_milos_cfgs }, { .compatible = "qcom,sm8550-dsi-phy-4nm", .data = &dsi_phy_4nm_8550_cfgs }, { .compatible = "qcom,sm8650-dsi-phy-4nm", diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h index c01784ca38ed..21a59d66e8dc 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h @@ -61,6 +61,7 @@ extern const struct msm_dsi_phy_cfg dsi_phy_5nm_8350_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_5nm_8450_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_5nm_8775p_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_5nm_sar2130p_cfgs; +extern const struct msm_dsi_phy_cfg dsi_phy_4nm_milos_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_4nm_8550_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_4nm_8650_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_3nm_8750_cfgs; diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c index 8f4b03713f25..984a66085dfb 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c @@ -1436,6 +1436,29 @@ const struct msm_dsi_phy_cfg dsi_phy_5nm_sar2130p_cfgs = { .quirks = DSI_PHY_7NM_QUIRK_V5_2, }; +const struct msm_dsi_phy_cfg dsi_phy_4nm_milos_cfgs = { + .has_phy_lane = true, + .regulator_data = dsi_phy_7nm_98000uA_regulators, + .num_regulators = ARRAY_SIZE(dsi_phy_7nm_98000uA_regulators), + .ops = { + .enable = dsi_7nm_phy_enable, + .disable = dsi_7nm_phy_disable, + .pll_init = dsi_pll_7nm_init, + .save_pll_state = dsi_7nm_pll_save_state, + .restore_pll_state = dsi_7nm_pll_restore_state, + .set_continuous_clock = dsi_7nm_set_continuous_clock, + }, + .min_pll_rate = 600000000UL, +#ifdef CONFIG_64BIT + .max_pll_rate = 5000000000UL, +#else + .max_pll_rate = ULONG_MAX, +#endif + .io_start = { 0xae95000 }, + .num_dsi_phy = 1, + .quirks = DSI_PHY_7NM_QUIRK_V5_2, +}; + const struct msm_dsi_phy_cfg dsi_phy_4nm_8550_cfgs = { .has_phy_lane = true, .regulator_data = dsi_phy_7nm_98400uA_regulators, From 0203d3cc77b1584a7a601b413df3e027f85c049c Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 09:14:49 +0200 Subject: [PATCH 10/67] drm/msm: mdss: Add Milos support Add support for MDSS on Milos. Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Luca Weiss Patchwork: https://patchwork.freedesktop.org/patch/722320/ Link: https://lore.kernel.org/r/20260501-milos-mdss-v3-7-58bfc58c0e13@fairphone.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_mdss.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index 90c3fa0681a0..754ceef38717 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -550,6 +550,10 @@ static void mdss_remove(struct platform_device *pdev) msm_mdss_destroy(mdss); } +static const struct msm_mdss_data data_14k = { + .reg_bus_bw = 14000, +}; + static const struct msm_mdss_data data_57k = { .reg_bus_bw = 57000, }; @@ -571,6 +575,7 @@ static const struct of_device_id mdss_dt_match[] = { { .compatible = "qcom,eliza-mdss", .data = &data_57k }, { .compatible = "qcom,glymur-mdss", .data = &data_57k }, { .compatible = "qcom,kaanapali-mdss", .data = &data_57k }, + { .compatible = "qcom,milos-mdss", .data = &data_14k }, { .compatible = "qcom,msm8998-mdss", .data = &data_76k8 }, { .compatible = "qcom,qcm2290-mdss", .data = &data_76k8 }, { .compatible = "qcom,qcs8300-mdss", .data = &data_74k }, From ccb0472a54a5d2152ce2e494d8a8cdc53a54d8f5 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 09:14:50 +0200 Subject: [PATCH 11/67] drm/msm/dpu: Add Milos support Add definitions for the display hardware used on the Qualcomm Milos platform. Signed-off-by: Luca Weiss Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/722323/ Link: https://lore.kernel.org/r/20260501-milos-mdss-v3-8-58bfc58c0e13@fairphone.com Signed-off-by: Dmitry Baryshkov --- .../msm/disp/dpu1/catalog/dpu_10_2_milos.h | 279 ++++++++++++++++++ .../gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 29 ++ .../gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h | 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 1 + 4 files changed, 310 insertions(+) create mode 100644 drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_2_milos.h diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_2_milos.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_2_milos.h new file mode 100644 index 000000000000..1aa8aea4e352 --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_2_milos.h @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2026, Luca Weiss + */ + +#ifndef _DPU_10_2_MILOS_H +#define _DPU_10_2_MILOS_H + +static const struct dpu_caps milos_dpu_caps = { + .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, + .max_mixer_blendstages = 0x7, + .has_src_split = true, + .has_dim_layer = true, + .has_idle_pc = true, + .has_3d_merge = true, + .max_linewidth = 8192, + .pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE, +}; + +static const struct dpu_mdp_cfg milos_mdp = { + .name = "top_0", + .base = 0, .len = 0x494, + .clk_ctrls = { + [DPU_CLK_CTRL_REG_DMA] = { .reg_off = 0x2bc, .bit_off = 20 }, + }, +}; + +static const struct dpu_ctl_cfg milos_ctl[] = { + { + .name = "ctl_0", .id = CTL_0, + .base = 0x15000, .len = 0x1000, + .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 9), + }, { + .name = "ctl_1", .id = CTL_1, + .base = 0x16000, .len = 0x1000, + .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 10), + }, { + .name = "ctl_2", .id = CTL_2, + .base = 0x17000, .len = 0x1000, + .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 11), + }, { + .name = "ctl_3", .id = CTL_3, + .base = 0x18000, .len = 0x1000, + .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 12), + }, +}; + +static const struct dpu_sspp_cfg milos_sspp[] = { + { + .name = "sspp_0", .id = SSPP_VIG0, + .base = 0x4000, .len = 0x344, + .features = VIG_SDM845_MASK_SDMA, + .sblk = &dpu_vig_sblk_qseed3_3_3, + .xin_id = 0, + .type = SSPP_TYPE_VIG, + }, { + .name = "sspp_8", .id = SSPP_DMA0, + .base = 0x24000, .len = 0x344, + .features = DMA_SDM845_MASK_SDMA, + .sblk = &dpu_dma_sblk, + .xin_id = 1, + .type = SSPP_TYPE_DMA, + }, { + .name = "sspp_9", .id = SSPP_DMA1, + .base = 0x26000, .len = 0x344, + .features = DMA_SDM845_MASK_SDMA, + .sblk = &dpu_dma_sblk, + .xin_id = 5, + .type = SSPP_TYPE_DMA, + }, { + .name = "sspp_10", .id = SSPP_DMA2, + .base = 0x28000, .len = 0x344, + .features = DMA_SDM845_MASK_SDMA, + .sblk = &dpu_dma_sblk, + .xin_id = 9, + .type = SSPP_TYPE_DMA, + }, +}; + +static const struct dpu_lm_cfg milos_lm[] = { + { + .name = "lm_0", .id = LM_0, + .base = 0x44000, .len = 0x400, + .features = MIXER_MSM8998_MASK, + .sblk = &sdm845_lm_sblk, + .pingpong = PINGPONG_0, + .dspp = DSPP_0, + }, { + .name = "lm_2", .id = LM_2, + .base = 0x46000, .len = 0x400, + .features = MIXER_MSM8998_MASK, + .sblk = &sdm845_lm_sblk, + .lm_pair = LM_3, + .pingpong = PINGPONG_2, + }, { + .name = "lm_3", .id = LM_3, + .base = 0x47000, .len = 0x400, + .features = MIXER_MSM8998_MASK, + .sblk = &sdm845_lm_sblk, + .lm_pair = LM_2, + .pingpong = PINGPONG_3, + }, +}; + +static const struct dpu_dspp_cfg milos_dspp[] = { + { + .name = "dspp_0", .id = DSPP_0, + .base = 0x54000, .len = 0x1800, + .sblk = &sdm845_dspp_sblk, + }, +}; + +static const struct dpu_pingpong_cfg milos_pp[] = { + { + .name = "pingpong_0", .id = PINGPONG_0, + .base = 0x69000, .len = 0, + .sblk = &sc7280_pp_sblk, + .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8), + }, { + .name = "pingpong_2", .id = PINGPONG_2, + .base = 0x6b000, .len = 0, + .sblk = &sc7280_pp_sblk, + .merge_3d = MERGE_3D_1, + .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 10), + }, { + .name = "pingpong_3", .id = PINGPONG_3, + .base = 0x6c000, .len = 0, + .sblk = &sc7280_pp_sblk, + .merge_3d = MERGE_3D_1, + .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 11), + }, { + .name = "pingpong_cwb_0", .id = PINGPONG_CWB_0, + .base = 0x66000, .len = 0, + .sblk = &sc7280_pp_sblk, + }, +}; + +static const struct dpu_merge_3d_cfg milos_merge_3d[] = { + { + .name = "merge_3d_1", .id = MERGE_3D_1, + .base = 0x4f000, .len = 0x8, + }, +}; + +/* + * NOTE: Each display compression engine (DCE) contains dual hard + * slice DSC encoders so both share same base address but with + * its own different sub block address. + */ +static const struct dpu_dsc_cfg milos_dsc[] = { + { + .name = "dce_0_0", .id = DSC_0, + .base = 0x80000, .len = 0x6, + .features = BIT(DPU_DSC_NATIVE_42x_EN), + .sblk = &milos_dsc_sblk_0, + }, { + .name = "dce_0_1", .id = DSC_1, + .base = 0x80000, .len = 0x6, + .features = BIT(DPU_DSC_NATIVE_42x_EN), + .sblk = &milos_dsc_sblk_1, + }, +}; + +static const struct dpu_wb_cfg milos_wb[] = { + { + .name = "wb_2", .id = WB_2, + .base = 0x65000, .len = 0x2c8, + .features = WB_SDM845_MASK, + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), + .xin_id = 6, + .maxlinewidth = 4096, + .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), + }, +}; + +static const struct dpu_cwb_cfg milos_cwb[] = { + { + .name = "cwb_0", .id = CWB_0, + .base = 0x66200, .len = 0x8, + }, +}; + +static const struct dpu_intf_cfg milos_intf[] = { + { + .name = "intf_0", .id = INTF_0, + .base = 0x34000, .len = 0x300, + .type = INTF_DP, + .controller_id = MSM_DP_CONTROLLER_0, + .prog_fetch_lines_worst_case = 24, + .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 24), + .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 25), + }, { + .name = "intf_1", .id = INTF_1, + .base = 0x35000, .len = 0x300, + .type = INTF_DSI, + .controller_id = MSM_DSI_CONTROLLER_0, + .prog_fetch_lines_worst_case = 24, + .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 26), + .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 27), + .intr_tear_rd_ptr = DPU_IRQ_IDX(MDP_INTF1_TEAR_INTR, 2), + }, { + .name = "intf_3", .id = INTF_3, + .base = 0x37000, .len = 0x300, + .type = INTF_DP, + .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ + .prog_fetch_lines_worst_case = 24, + .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30), + .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31), + }, +}; + +static const struct dpu_perf_cfg milos_perf_data = { + .max_bw_low = 7100000, + .max_bw_high = 9800000, + .min_core_ib = 2500000, + .min_llcc_ib = 0, + .min_dram_ib = 1600000, + .min_prefill_lines = 40, + /* FIXME: lut tables */ + .danger_lut_tbl = {0x3ffff, 0x3ffff, 0x0}, + .safe_lut_tbl = {0xff00, 0xfff0, 0x0fff}, + .qos_lut_tbl = { + {.nentry = ARRAY_SIZE(sc7180_qos_linear), + .entries = sc7180_qos_linear + }, + {.nentry = ARRAY_SIZE(sc7180_qos_macrotile), + .entries = sc7180_qos_macrotile + }, + {.nentry = ARRAY_SIZE(sc7180_qos_nrt), + .entries = sc7180_qos_nrt + }, + /* TODO: macrotile-qseed is different from macrotile */ + }, + .cdp_cfg = { + {.rd_enable = 1, .wr_enable = 1}, + {.rd_enable = 1, .wr_enable = 0} + }, + .clk_inefficiency_factor = 105, + .bw_inefficiency_factor = 120, +}; + +static const struct dpu_mdss_version milos_mdss_ver = { + .core_major_ver = 10, + .core_minor_ver = 2, +}; + +const struct dpu_mdss_cfg dpu_milos_cfg = { + .mdss_ver = &milos_mdss_ver, + .caps = &milos_dpu_caps, + .mdp = &milos_mdp, + .cdm = &dpu_cdm_5_x, + .ctl_count = ARRAY_SIZE(milos_ctl), + .ctl = milos_ctl, + .sspp_count = ARRAY_SIZE(milos_sspp), + .sspp = milos_sspp, + .mixer_count = ARRAY_SIZE(milos_lm), + .mixer = milos_lm, + .dspp_count = ARRAY_SIZE(milos_dspp), + .dspp = milos_dspp, + .pingpong_count = ARRAY_SIZE(milos_pp), + .pingpong = milos_pp, + .dsc_count = ARRAY_SIZE(milos_dsc), + .dsc = milos_dsc, + .merge_3d_count = ARRAY_SIZE(milos_merge_3d), + .merge_3d = milos_merge_3d, + .wb_count = ARRAY_SIZE(milos_wb), + .wb = milos_wb, + .cwb_count = ARRAY_SIZE(milos_cwb), + .cwb = milos_cwb, + .intf_count = ARRAY_SIZE(milos_intf), + .intf = milos_intf, + .vbif = &milos_vbif, + .perf = &milos_perf_data, +}; + +#endif diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index bb4fd5fa4b22..2e10add84fd7 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -454,6 +454,16 @@ static const struct dpu_dsc_sub_blks dsc_sblk_1 = { .ctl = {.name = "ctl", .base = 0xF80, .len = 0x10}, }; +static const struct dpu_dsc_sub_blks milos_dsc_sblk_0 = { + .enc = {.name = "enc", .base = 0x100, .len = 0x100}, + .ctl = {.name = "ctl", .base = 0xF00, .len = 0x80}, +}; + +static const struct dpu_dsc_sub_blks milos_dsc_sblk_1 = { + .enc = {.name = "enc", .base = 0x200, .len = 0x100}, + .ctl = {.name = "ctl", .base = 0xF80, .len = 0x80}, +}; + static const struct dpu_dsc_sub_blks sm8750_dsc_sblk_0 = { .enc = {.name = "enc", .base = 0x100, .len = 0x100}, .ctl = {.name = "ctl", .base = 0xF00, .len = 0x24}, @@ -513,6 +523,23 @@ static const struct dpu_vbif_dynamic_ot_cfg msm8998_ot_rdwr_cfg[] = { }, }; +static const struct dpu_vbif_cfg milos_vbif = { + .len = 0x1074, + .features = BIT(DPU_VBIF_QOS_REMAP), + .xin_halt_timeout = 0x4000, + .qos_rp_remap_size = 0x40, + .qos_rt_tbl = { + .npriority_lvl = ARRAY_SIZE(sdm845_rt_pri_lvl), + .priority_lvl = sdm845_rt_pri_lvl, + }, + .qos_nrt_tbl = { + .npriority_lvl = ARRAY_SIZE(sdm845_nrt_pri_lvl), + .priority_lvl = sdm845_nrt_pri_lvl, + }, + .memtype_count = 16, + .memtype = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, +}; + static const struct dpu_vbif_cfg msm8996_vbif = { .len = 0x1040, .default_ot_rd_limit = 32, @@ -754,6 +781,8 @@ static const struct dpu_qos_lut_entry sc7180_qos_nrt[] = { #include "catalog/dpu_9_2_x1e80100.h" #include "catalog/dpu_10_0_sm8650.h" +#include "catalog/dpu_10_2_milos.h" + #include "catalog/dpu_12_0_sm8750.h" #include "catalog/dpu_12_2_glymur.h" #include "catalog/dpu_12_4_eliza.h" diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h index ba04ac24d5a9..f45faf87333e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h @@ -766,6 +766,7 @@ struct dpu_mdss_cfg { extern const struct dpu_mdss_cfg dpu_eliza_cfg; extern const struct dpu_mdss_cfg dpu_glymur_cfg; extern const struct dpu_mdss_cfg dpu_kaanapali_cfg; +extern const struct dpu_mdss_cfg dpu_milos_cfg; extern const struct dpu_mdss_cfg dpu_msm8917_cfg; extern const struct dpu_mdss_cfg dpu_msm8937_cfg; extern const struct dpu_mdss_cfg dpu_msm8953_cfg; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 35f7af4743d7..7c37bd51f934 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -1483,6 +1483,7 @@ static const struct of_device_id dpu_dt_match[] = { { .compatible = "qcom,eliza-dpu", .data = &dpu_eliza_cfg, }, { .compatible = "qcom,glymur-dpu", .data = &dpu_glymur_cfg, }, { .compatible = "qcom,kaanapali-dpu", .data = &dpu_kaanapali_cfg, }, + { .compatible = "qcom,milos-dpu", .data = &dpu_milos_cfg, }, { .compatible = "qcom,msm8917-mdp5", .data = &dpu_msm8917_cfg, }, { .compatible = "qcom,msm8937-mdp5", .data = &dpu_msm8937_cfg, }, { .compatible = "qcom,msm8953-mdp5", .data = &dpu_msm8953_cfg, }, From 78de481c75c34623f450abf0f4604344f9396593 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 7 May 2026 16:02:57 +0300 Subject: [PATCH 12/67] drm/msm/mdss: correct UBWC programming sequences The UBWC registers in the MDSS region are not dependent on the UBWC version (it is an invalid assumption we inherited from the vendor SDE driver). Instead they are dependent only on the MDSS core revision. Rework UBWC programming to follow MDSS revision and to use required (aka encoder) UBWC version instead of the ubwc_dec_version. Fixes: d68db6069a8e ("drm/msm/mdss: convert UBWC setup to use match data") Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/723384/ Link: https://lore.kernel.org/r/20260507-ubwc-rework-v4-1-c19593d20c1d@oss.qualcomm.com --- drivers/gpu/drm/msm/msm_mdss.c | 113 ++++++++++++--------------------- 1 file changed, 40 insertions(+), 73 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index 754ceef38717..19ab0ff80049 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -166,22 +166,19 @@ static int _msm_mdss_irq_domain_add(struct msm_mdss *msm_mdss) return 0; } -static void msm_mdss_setup_ubwc_dec_20(struct msm_mdss *msm_mdss) +static void msm_mdss_4x_setup_ubwc(struct msm_mdss *msm_mdss) { const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; - u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle) | + u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle & 0x1) | MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); - if (data->ubwc_bank_spread) - value |= MDSS_UBWC_STATIC_UBWC_BANK_SPREAD; - if (data->ubwc_enc_version == UBWC_1_0) value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(1); writel_relaxed(value, msm_mdss->mmio + REG_MDSS_UBWC_STATIC); } -static void msm_mdss_setup_ubwc_dec_30(struct msm_mdss *msm_mdss) +static void msm_mdss_5x_setup_ubwc(struct msm_mdss *msm_mdss) { const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle & 0x1) | @@ -199,11 +196,12 @@ static void msm_mdss_setup_ubwc_dec_30(struct msm_mdss *msm_mdss) writel_relaxed(value, msm_mdss->mmio + REG_MDSS_UBWC_STATIC); } -static void msm_mdss_setup_ubwc_dec_40(struct msm_mdss *msm_mdss) +static void msm_mdss_6x_setup_ubwc(struct msm_mdss *msm_mdss) { const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle) | MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); + u32 ver, prediction_mode; if (data->ubwc_bank_spread) value |= MDSS_UBWC_STATIC_UBWC_BANK_SPREAD; @@ -211,45 +209,42 @@ static void msm_mdss_setup_ubwc_dec_40(struct msm_mdss *msm_mdss) if (data->macrotile_mode) value |= MDSS_UBWC_STATIC_MACROTILE_MODE; - writel_relaxed(value, msm_mdss->mmio + REG_MDSS_UBWC_STATIC); - - if (data->ubwc_enc_version == UBWC_3_0) { - writel_relaxed(1, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); - writel_relaxed(0, msm_mdss->mmio + REG_MDSS_UBWC_PREDICTION_MODE); - } else { - if (data->ubwc_dec_version == UBWC_4_3) - writel_relaxed(3, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); - else - writel_relaxed(2, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); - writel_relaxed(1, msm_mdss->mmio + REG_MDSS_UBWC_PREDICTION_MODE); - } -} - -static void msm_mdss_setup_ubwc_dec_50(struct msm_mdss *msm_mdss) -{ - const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; - u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle) | - MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); - - if (data->ubwc_bank_spread) - value |= MDSS_UBWC_STATIC_UBWC_BANK_SPREAD; - - if (data->macrotile_mode) - value |= MDSS_UBWC_STATIC_MACROTILE_MODE; + if (data->ubwc_enc_version == UBWC_1_0) + value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(1); writel_relaxed(value, msm_mdss->mmio + REG_MDSS_UBWC_STATIC); - if (data->ubwc_dec_version == UBWC_6_0) - writel_relaxed(5, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); + if (data->ubwc_enc_version < UBWC_4_0) + prediction_mode = 0; else - writel_relaxed(4, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); + prediction_mode = 1; - writel_relaxed(1, msm_mdss->mmio + REG_MDSS_UBWC_PREDICTION_MODE); + if (data->ubwc_enc_version >= UBWC_6_0) + ver = 5; + else if (data->ubwc_enc_version >= UBWC_5_0) + ver = 4; + else if (data->ubwc_enc_version >= UBWC_4_3) + ver = 3; + else if (data->ubwc_enc_version >= UBWC_4_0) + ver = 2; + else if (data->ubwc_enc_version >= UBWC_3_0) + ver = 1; + else /* UBWC 1.0 and 2.0 */ + ver = 0; + + writel_relaxed(ver, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); + writel_relaxed(prediction_mode, msm_mdss->mmio + REG_MDSS_UBWC_PREDICTION_MODE); } +#define MDSS_HW_VER(major, minor, step) \ + ((((major) & 0xf) << 28) | \ + (((minor) & 0xfff) << 16) | \ + ((step) & 0xffff)) + static int msm_mdss_enable(struct msm_mdss *msm_mdss) { int ret, i; + u32 hw_rev; /* * Several components have AXI clocks that can only be turned on if @@ -283,43 +278,15 @@ static int msm_mdss_enable(struct msm_mdss *msm_mdss) if (msm_mdss->is_mdp5 || !msm_mdss->mdss_data) return 0; - /* - * ubwc config is part of the "mdss" region which is not accessible - * from the rest of the driver. hardcode known configurations here - * - * Decoder version can be read from the UBWC_DEC_HW_VERSION reg, - * UBWC_n and the rest of params comes from hw data. - */ - switch (msm_mdss->mdss_data->ubwc_dec_version) { - case 0: /* no UBWC */ - case UBWC_1_0: - /* do nothing */ - break; - case UBWC_2_0: - msm_mdss_setup_ubwc_dec_20(msm_mdss); - break; - case UBWC_3_0: - msm_mdss_setup_ubwc_dec_30(msm_mdss); - break; - case UBWC_4_0: - case UBWC_4_3: - msm_mdss_setup_ubwc_dec_40(msm_mdss); - break; - case UBWC_5_0: - msm_mdss_setup_ubwc_dec_50(msm_mdss); - break; - case UBWC_6_0: - msm_mdss_setup_ubwc_dec_50(msm_mdss); - break; - default: - dev_err(msm_mdss->dev, "Unsupported UBWC decoder version %x\n", - msm_mdss->mdss_data->ubwc_dec_version); - dev_err(msm_mdss->dev, "HW_REV: 0x%x\n", - readl_relaxed(msm_mdss->mmio + REG_MDSS_HW_VERSION)); - dev_err(msm_mdss->dev, "UBWC_DEC_HW_VERSION: 0x%x\n", - readl_relaxed(msm_mdss->mmio + REG_MDSS_UBWC_DEC_HW_VERSION)); - break; - } + hw_rev = readl_relaxed(msm_mdss->mmio + REG_MDSS_HW_VERSION); + + if (hw_rev >= MDSS_HW_VER(6, 0, 0)) + msm_mdss_6x_setup_ubwc(msm_mdss); + else if (hw_rev >= MDSS_HW_VER(5, 0, 0)) + msm_mdss_5x_setup_ubwc(msm_mdss); + else if (hw_rev >= MDSS_HW_VER(4, 0, 0)) + msm_mdss_4x_setup_ubwc(msm_mdss); + /* else UBWC 1.0 or none, no params to program */ return ret; } From d158886cba08688f2d8d0dbb7025a4440e6a8ffd Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 20 May 2026 17:51:11 +0300 Subject: [PATCH 13/67] drm/msm/adreno: Trust the SSoT UBWC config Now that the highest_bank_bit value is retrieved from the running system and the global config has been part of the tree for a couple of releases, there is no reason to keep any hardcoded values inside the GPU driver. Get rid of them. Signed-off-by: Konrad Dybcio Reviewed-by: Rob Clark Reviewed-by: Dmitry Baryshkov Reviewed-by: Akhil P Oommen Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726488/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-4-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 11 +--- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 82 +------------------------ drivers/gpu/drm/msm/adreno/adreno_gpu.h | 5 -- 3 files changed, 6 insertions(+), 92 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 79acae11154a..699fc0f551ed 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -1725,7 +1725,6 @@ static struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) struct msm_drm_private *priv = dev->dev_private; struct platform_device *pdev = priv->gpu_pdev; struct adreno_platform_config *config = pdev->dev.platform_data; - const struct qcom_ubwc_cfg_data *common_cfg; struct a5xx_gpu *a5xx_gpu = NULL; struct adreno_gpu *adreno_gpu; struct msm_gpu *gpu; @@ -1769,13 +1768,9 @@ static struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) a5xx_preempt_init(gpu); /* Inherit the common config and make some necessary fixups */ - common_cfg = qcom_ubwc_config_get_data(); - if (IS_ERR(common_cfg)) - return ERR_CAST(common_cfg); - - /* Copy the data into the internal struct to drop the const qualifier (temporarily) */ - adreno_gpu->_ubwc_config = *common_cfg; - adreno_gpu->ubwc_config = &adreno_gpu->_ubwc_config; + adreno_gpu->ubwc_config = qcom_ubwc_config_get_data(); + if (IS_ERR(adreno_gpu->ubwc_config)) + return ERR_CAST(adreno_gpu->ubwc_config); adreno_gpu->uche_trap_base = 0x0001ffffffff0000ull; diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 7a3e3c2f5cf3..90e814c64c6e 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -733,82 +733,6 @@ static void a6xx_set_cp_protect(struct msm_gpu *gpu) gpu_write(gpu, REG_A6XX_CP_PROTECT(protect->count_max - 1), protect->regs[i]); } -static int a6xx_calc_ubwc_config(struct adreno_gpu *gpu) -{ - const struct qcom_ubwc_cfg_data *common_cfg; - struct qcom_ubwc_cfg_data *cfg = &gpu->_ubwc_config; - - /* Inherit the common config and make some necessary fixups */ - common_cfg = qcom_ubwc_config_get_data(); - if (IS_ERR(common_cfg)) - return PTR_ERR(common_cfg); - - /* Copy the data into the internal struct to drop the const qualifier (temporarily) */ - *cfg = *common_cfg; - - /* Use common config as is for A8x */ - if (!adreno_is_a8xx(gpu)) { - cfg->ubwc_swizzle = 0x6; - cfg->highest_bank_bit = 15; - } - - if (adreno_is_a610(gpu)) { - cfg->highest_bank_bit = 13; - cfg->ubwc_swizzle = 0x7; - } - - if (adreno_is_a612(gpu)) - cfg->highest_bank_bit = 14; - - if (adreno_is_a618(gpu)) - cfg->highest_bank_bit = 14; - - if (adreno_is_a619(gpu)) - /* TODO: Should be 14 but causes corruption at e.g. 1920x1200 on DP */ - cfg->highest_bank_bit = 13; - - if (adreno_is_a619_holi(gpu)) - cfg->highest_bank_bit = 13; - - if (adreno_is_a621(gpu)) - cfg->highest_bank_bit = 13; - - if (adreno_is_a623(gpu)) - cfg->highest_bank_bit = 16; - - if (adreno_is_a650(gpu) || - adreno_is_a660(gpu) || - adreno_is_a690(gpu) || - adreno_is_a730(gpu) || - adreno_is_a740_family(gpu)) { - /* TODO: get ddr type from bootloader and use 15 for LPDDR4 */ - cfg->highest_bank_bit = 16; - } - - if (adreno_is_a663(gpu)) { - cfg->highest_bank_bit = 13; - cfg->ubwc_swizzle = 0x4; - } - - if (adreno_is_7c3(gpu)) - cfg->highest_bank_bit = 14; - - if (adreno_is_a702(gpu)) - cfg->highest_bank_bit = 14; - - if (cfg->highest_bank_bit != common_cfg->highest_bank_bit) - DRM_WARN_ONCE("Inconclusive highest_bank_bit value: %u (GPU) vs %u (UBWC_CFG)\n", - cfg->highest_bank_bit, common_cfg->highest_bank_bit); - - if (cfg->ubwc_swizzle != common_cfg->ubwc_swizzle) - DRM_WARN_ONCE("Inconclusive ubwc_swizzle value: %u (GPU) vs %u (UBWC_CFG)\n", - cfg->ubwc_swizzle, common_cfg->ubwc_swizzle); - - gpu->ubwc_config = &gpu->_ubwc_config; - - return 0; -} - static void a6xx_set_ubwc_config(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); @@ -2700,10 +2624,10 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) msm_mmu_set_fault_handler(to_msm_vm(gpu->vm)->mmu, gpu, adreno_gpu->funcs->mmu_fault_handler); - ret = a6xx_calc_ubwc_config(adreno_gpu); - if (ret) { + adreno_gpu->ubwc_config = qcom_ubwc_config_get_data(); + if (IS_ERR(adreno_gpu->ubwc_config)) { a6xx_destroy(&(a6xx_gpu->base.base)); - return ERR_PTR(ret); + return ERR_CAST(adreno_gpu->ubwc_config); } /* Set up the preemption specific bits and pieces for each ringbuffer */ diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index ec643b84646b..324319762383 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -236,12 +236,7 @@ struct adreno_gpu { /* firmware: */ const struct firmware *fw[ADRENO_FW_MAX]; - /* - * The migration to the central UBWC config db is still in flight - keep - * a copy containing some local fixups until that's done. - */ const struct qcom_ubwc_cfg_data *ubwc_config; - struct qcom_ubwc_cfg_data _ubwc_config; /* * Register offsets are different between some GPUs. From 95776c9f016b6ead57909694fedee3e6919f1bde Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:12 +0300 Subject: [PATCH 14/67] drm/msm/adreno: use qcom_ubwc_version_tag() helper Use new helper defined to program UBWC version to the hardware. Reviewed-by: Konrad Dybcio Reviewed-by: Akhil P Oommen Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726485/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-5-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index ccfccc45133f..1923f904d37d 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -282,35 +282,33 @@ static void a8xx_set_ubwc_config(struct msm_gpu *gpu) bool rgb565_predicator = false, amsbc = false; bool ubwc_mode = qcom_ubwc_get_ubwc_mode(cfg); u32 ubwc_version = cfg->ubwc_enc_version; - u32 hbb, hbb_hi, hbb_lo, mode = 1; + u32 hbb, hbb_hi, hbb_lo, mode; u8 uavflagprd_inv = 2; switch (ubwc_version) { case UBWC_6_0: yuvnotcomptofc = true; - mode = 5; break; case UBWC_5_0: amsbc = true; rgb565_predicator = true; - mode = 4; break; case UBWC_4_0: amsbc = true; rgb565_predicator = true; fp16compoptdis = true; rgba8888_lossless = true; - mode = 2; break; case UBWC_3_0: amsbc = true; - mode = 1; break; default: dev_err(&gpu->pdev->dev, "Unknown UBWC version: 0x%x\n", ubwc_version); break; } + mode = qcom_ubwc_version_tag(cfg); + /* * We subtract 13 from the highest bank bit (13 is the minimum value * allowed by hw) and write the lowest two bits of the remaining value From 4faca4742c20f001b08bf384bedd85f0fe03aae9 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:13 +0300 Subject: [PATCH 15/67] drm/msm/mdss: use qcom_ubwc_version_tag() helper Use new helper defined to program UBWC version to the hardware. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726487/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-6-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/msm_mdss.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index 19ab0ff80049..06c3b68f5e23 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -201,7 +201,7 @@ static void msm_mdss_6x_setup_ubwc(struct msm_mdss *msm_mdss) const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle) | MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); - u32 ver, prediction_mode; + u32 prediction_mode; if (data->ubwc_bank_spread) value |= MDSS_UBWC_STATIC_UBWC_BANK_SPREAD; @@ -219,20 +219,8 @@ static void msm_mdss_6x_setup_ubwc(struct msm_mdss *msm_mdss) else prediction_mode = 1; - if (data->ubwc_enc_version >= UBWC_6_0) - ver = 5; - else if (data->ubwc_enc_version >= UBWC_5_0) - ver = 4; - else if (data->ubwc_enc_version >= UBWC_4_3) - ver = 3; - else if (data->ubwc_enc_version >= UBWC_4_0) - ver = 2; - else if (data->ubwc_enc_version >= UBWC_3_0) - ver = 1; - else /* UBWC 1.0 and 2.0 */ - ver = 0; - - writel_relaxed(ver, msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); + writel_relaxed(qcom_ubwc_version_tag(data), + msm_mdss->mmio + REG_MDSS_UBWC_CTRL_2); writel_relaxed(prediction_mode, msm_mdss->mmio + REG_MDSS_UBWC_PREDICTION_MODE); } From bfa4d53b7283bf69cb1188d90396e883dc9ecc8e Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:14 +0300 Subject: [PATCH 16/67] drm/msm/adreno: use new helper to set min_acc length Use freshly defined helper instead of hardcoding the checks in the driver. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726495/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-7-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 90e814c64c6e..45c966610511 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -748,7 +748,7 @@ static void a6xx_set_ubwc_config(struct msm_gpu *gpu) u32 level2_swizzling_dis = !(cfg->ubwc_swizzle & UBWC_SWIZZLE_ENABLE_LVL2); bool ubwc_mode = qcom_ubwc_get_ubwc_mode(cfg); bool amsbc = cfg->ubwc_enc_version >= UBWC_3_0; - bool min_acc_len_64b = false; + bool min_acc_len_64b; u8 uavflagprd_inv = 0; u32 hbb_hi = hbb >> 2; u32 hbb_lo = hbb & 3; @@ -756,8 +756,7 @@ static void a6xx_set_ubwc_config(struct msm_gpu *gpu) if (adreno_is_a650_family(adreno_gpu) || adreno_is_a7xx(adreno_gpu)) uavflagprd_inv = 2; - if (adreno_is_a610(adreno_gpu) || adreno_is_a702(adreno_gpu)) - min_acc_len_64b = true; + min_acc_len_64b = qcom_ubwc_min_acc_length_64b(cfg); gpu_write(gpu, REG_A6XX_RB_NC_MODE_CNTL, level2_swizzling_dis << 12 | From 1a8996b45201dc07ac7ef33557d54c93f86e4364 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:15 +0300 Subject: [PATCH 17/67] drm/msm/mdss: use new helper to set min_acc length Use freshly defined helper instead of hardcoding the checks in the driver. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726500/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-8-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/msm_mdss.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index 06c3b68f5e23..e8231a5720aa 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -172,8 +172,7 @@ static void msm_mdss_4x_setup_ubwc(struct msm_mdss *msm_mdss) u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle & 0x1) | MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); - if (data->ubwc_enc_version == UBWC_1_0) - value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(1); + value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(qcom_ubwc_min_acc_length_64b(data)); writel_relaxed(value, msm_mdss->mmio + REG_MDSS_UBWC_STATIC); } @@ -190,8 +189,7 @@ static void msm_mdss_5x_setup_ubwc(struct msm_mdss *msm_mdss) if (data->ubwc_enc_version == UBWC_3_0) value |= MDSS_UBWC_STATIC_UBWC_AMSBC; - if (data->ubwc_enc_version == UBWC_1_0) - value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(1); + value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(qcom_ubwc_min_acc_length_64b(data)); writel_relaxed(value, msm_mdss->mmio + REG_MDSS_UBWC_STATIC); } @@ -209,8 +207,7 @@ static void msm_mdss_6x_setup_ubwc(struct msm_mdss *msm_mdss) if (data->macrotile_mode) value |= MDSS_UBWC_STATIC_MACROTILE_MODE; - if (data->ubwc_enc_version == UBWC_1_0) - value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(1); + value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(qcom_ubwc_min_acc_length_64b(data)); writel_relaxed(value, msm_mdss->mmio + REG_MDSS_UBWC_STATIC); From 9bd4451e5334c2405b7c83e359ae61ac064679ef Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:16 +0300 Subject: [PATCH 18/67] drm/msm/adreno: use new helper to set macrotile_mode Use freshly defined helper instead of using the raw value from the database. Reviewed-by: Konrad Dybcio Reviewed-by: Akhil P Oommen Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726493/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-9-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 2 +- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 45c966610511..43818d1907ab 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -791,7 +791,7 @@ static void a6xx_set_ubwc_config(struct msm_gpu *gpu) min_acc_len_64b << 23 | hbb_lo << 21); gpu_write(gpu, REG_A6XX_RBBM_NC_MODE_CNTL, - cfg->macrotile_mode); + qcom_ubwc_macrotile_mode(cfg)); } static void a7xx_patch_pwrup_reglist(struct msm_gpu *gpu) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 03f96a1154e1..78d7ac3fd8c7 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -439,7 +439,7 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, case MSM_PARAM_MACROTILE_MODE: if (!adreno_gpu->ubwc_config) return UERR(ENOENT, drm, "no UBWC on this platform"); - *value = adreno_gpu->ubwc_config->macrotile_mode; + *value = qcom_ubwc_macrotile_mode(adreno_gpu->ubwc_config); return 0; case MSM_PARAM_UCHE_TRAP_BASE: *value = adreno_gpu->uche_trap_base; From 6be890e4e208f1168b26748ead15ee94651f343e Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:17 +0300 Subject: [PATCH 19/67] drm/msm/mdss: use new helper to set macrotile_mode Use freshly defined helper instead of using the raw value from the database. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726497/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-10-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/msm_mdss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index e8231a5720aa..d3ed1a0ed09a 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -183,7 +183,7 @@ static void msm_mdss_5x_setup_ubwc(struct msm_mdss *msm_mdss) u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle & 0x1) | MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); - if (data->macrotile_mode) + if (qcom_ubwc_macrotile_mode(data)) value |= MDSS_UBWC_STATIC_MACROTILE_MODE; if (data->ubwc_enc_version == UBWC_3_0) @@ -204,7 +204,7 @@ static void msm_mdss_6x_setup_ubwc(struct msm_mdss *msm_mdss) if (data->ubwc_bank_spread) value |= MDSS_UBWC_STATIC_UBWC_BANK_SPREAD; - if (data->macrotile_mode) + if (qcom_ubwc_macrotile_mode(data)) value |= MDSS_UBWC_STATIC_MACROTILE_MODE; value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(qcom_ubwc_min_acc_length_64b(data)); From 618cabd0fd7d26e895f9599af1e7de4603de62ed Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:18 +0300 Subject: [PATCH 20/67] drm/msm/mdss: use new helper to set UBWC bank spreading Use freshly defined helper instead of hardcoding the checks in the driver. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726501/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-11-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/msm_mdss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index d3ed1a0ed09a..7c5b76681be1 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -201,7 +201,7 @@ static void msm_mdss_6x_setup_ubwc(struct msm_mdss *msm_mdss) MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); u32 prediction_mode; - if (data->ubwc_bank_spread) + if (qcom_ubwc_bank_spread(data)) value |= MDSS_UBWC_STATIC_UBWC_BANK_SPREAD; if (qcom_ubwc_macrotile_mode(data)) From d2fb2372768a54abb63bf5a61efb5a6a03a15ad5 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:19 +0300 Subject: [PATCH 21/67] drm/msm/adreno: use new helper to set ubwc_swizzle Use freshly defined helper instead of using the raw value from the database. Reviewed-by: Konrad Dybcio Reviewed-by: Akhil P Oommen Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726498/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-12-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 2 +- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 4 ++-- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 43818d1907ab..e7a0d315e022 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -745,7 +745,7 @@ static void a6xx_set_ubwc_config(struct msm_gpu *gpu) BUG_ON(cfg->highest_bank_bit < 13); u32 hbb = cfg->highest_bank_bit - 13; bool rgb565_predicator = cfg->ubwc_enc_version >= UBWC_4_0; - u32 level2_swizzling_dis = !(cfg->ubwc_swizzle & UBWC_SWIZZLE_ENABLE_LVL2); + u32 level2_swizzling_dis = !(qcom_ubwc_swizzle(cfg) & UBWC_SWIZZLE_ENABLE_LVL2); bool ubwc_mode = qcom_ubwc_get_ubwc_mode(cfg); bool amsbc = cfg->ubwc_enc_version >= UBWC_3_0; bool min_acc_len_64b; diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 1923f904d37d..53def136e0fc 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -275,8 +275,8 @@ static void a8xx_set_ubwc_config(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); const struct qcom_ubwc_cfg_data *cfg = adreno_gpu->ubwc_config; - u32 level2_swizzling_dis = !(cfg->ubwc_swizzle & UBWC_SWIZZLE_ENABLE_LVL2); - u32 level3_swizzling_dis = !(cfg->ubwc_swizzle & UBWC_SWIZZLE_ENABLE_LVL3); + u32 level2_swizzling_dis = !(qcom_ubwc_swizzle(cfg) & UBWC_SWIZZLE_ENABLE_LVL2); + u32 level3_swizzling_dis = !(qcom_ubwc_swizzle(cfg) & UBWC_SWIZZLE_ENABLE_LVL3); bool rgba8888_lossless = false, fp16compoptdis = false; bool yuvnotcomptofc = false, min_acc_len_64b = false; bool rgb565_predicator = false, amsbc = false; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 78d7ac3fd8c7..6a0877e5374c 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -434,7 +434,7 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, case MSM_PARAM_UBWC_SWIZZLE: if (!adreno_gpu->ubwc_config) return UERR(ENOENT, drm, "no UBWC on this platform"); - *value = adreno_gpu->ubwc_config->ubwc_swizzle; + *value = qcom_ubwc_swizzle(adreno_gpu->ubwc_config); return 0; case MSM_PARAM_MACROTILE_MODE: if (!adreno_gpu->ubwc_config) From d7c0878bbda8f7e0f392ce3e0b7dccf4733b461b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:20 +0300 Subject: [PATCH 22/67] drm/msm/dpu: use new helper to set ubwc_swizzle Use freshly defined helper instead of using the raw value from the database. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726496/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-13-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c index d3da70009234..6089a58074ac 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c @@ -318,13 +318,14 @@ void dpu_hw_setup_format_impl(struct dpu_sw_pipe *pipe, const struct msm_format if (ctx->ubwc->ubwc_enc_version == UBWC_1_0) { fast_clear = fmt->alpha_enable ? BIT(31) : 0; - ctrl_val = fast_clear | (ctx->ubwc->ubwc_swizzle & 0x1) | + ctrl_val = fast_clear | + (qcom_ubwc_swizzle(ctx->ubwc) & UBWC_SWIZZLE_ENABLE_LVL1) | BIT(8) | (hbb << 4); } else if (ctx->ubwc->ubwc_enc_version == UBWC_2_0) { fast_clear = fmt->alpha_enable ? BIT(31) : 0; - ctrl_val = fast_clear | ctx->ubwc->ubwc_swizzle | (hbb << 4); + ctrl_val = fast_clear | qcom_ubwc_swizzle(ctx->ubwc) | (hbb << 4); } else if (ctx->ubwc->ubwc_enc_version == UBWC_3_0) { - ctrl_val = BIT(30) | (ctx->ubwc->ubwc_swizzle) | (hbb << 4); + ctrl_val = BIT(30) | qcom_ubwc_swizzle(ctx->ubwc) | (hbb << 4); } else if (ctx->ubwc->ubwc_enc_version == UBWC_4_0) { ctrl_val = MSM_FORMAT_IS_YUV(fmt) ? 0 : BIT(30); } else if (ctx->ubwc->ubwc_enc_version <= UBWC_6_0) { From 86ec9084a27370306fa07753bb8938a327cf00fa Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:21 +0300 Subject: [PATCH 23/67] drm/msm/mdss: use new helper to set ubwc_swizzle Use freshly defined helper instead of using the raw value from the database. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726516/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-14-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/msm_mdss.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index 7c5b76681be1..3c5023c05802 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -169,7 +169,8 @@ static int _msm_mdss_irq_domain_add(struct msm_mdss *msm_mdss) static void msm_mdss_4x_setup_ubwc(struct msm_mdss *msm_mdss) { const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; - u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle & 0x1) | + u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(qcom_ubwc_swizzle(data) & + UBWC_SWIZZLE_ENABLE_LVL1) | MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(qcom_ubwc_min_acc_length_64b(data)); @@ -180,7 +181,8 @@ static void msm_mdss_4x_setup_ubwc(struct msm_mdss *msm_mdss) static void msm_mdss_5x_setup_ubwc(struct msm_mdss *msm_mdss) { const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; - u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle & 0x1) | + u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(qcom_ubwc_swizzle(data) & + UBWC_SWIZZLE_ENABLE_LVL1) | MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); if (qcom_ubwc_macrotile_mode(data)) @@ -197,7 +199,7 @@ static void msm_mdss_5x_setup_ubwc(struct msm_mdss *msm_mdss) static void msm_mdss_6x_setup_ubwc(struct msm_mdss *msm_mdss) { const struct qcom_ubwc_cfg_data *data = msm_mdss->mdss_data; - u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(data->ubwc_swizzle) | + u32 value = MDSS_UBWC_STATIC_UBWC_SWIZZLE(qcom_ubwc_swizzle(data)) | MDSS_UBWC_STATIC_HIGHEST_BANK_BIT(data->highest_bank_bit - 13); u32 prediction_mode; From ddbcc8750043039a8f1a6f6cbef705cf1851f81a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:22 +0300 Subject: [PATCH 24/67] drm/msm/adreno: write reserved UBWC-related bits On the latest A8xx Adreno chips several of the bits in the UBWC-related registers are now hardwired to 1. Currently the driver doesn't write them because there is no side-effect. In the preparation for the refactoring in the next patch, write '1' to those bits anyway. Reviewed-by: Akhil P Oommen Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726504/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-15-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 53def136e0fc..7a6223ddd8cf 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -288,6 +288,8 @@ static void a8xx_set_ubwc_config(struct msm_gpu *gpu) switch (ubwc_version) { case UBWC_6_0: yuvnotcomptofc = true; + amsbc = true; + rgb565_predicator = true; break; case UBWC_5_0: amsbc = true; From ca65f7f7545ea5277c3b78842b40b9fd81d40378 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:23 +0300 Subject: [PATCH 25/67] drm/msm/adreno: set fp16compoptdis for UBWC 3.0 formats The fp16compoptdis bit should be set if the system targets UBWC 3.0 format in addition to UBWC 4.0. Reviewed-by: Akhil P Oommen Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726507/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-16-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 7a6223ddd8cf..867c7d05f670 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -303,6 +303,7 @@ static void a8xx_set_ubwc_config(struct msm_gpu *gpu) break; case UBWC_3_0: amsbc = true; + fp16compoptdis = true; break; default: dev_err(&gpu->pdev->dev, "Unknown UBWC version: 0x%x\n", ubwc_version); From 82595de3ef937af073e1b06905752646493f6932 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:24 +0300 Subject: [PATCH 26/67] drm/msm/adreno: use new helper to set amsbc Use freshly defined helper instead of checking the UBWC version directly. Reviewed-by: Konrad Dybcio Reviewed-by: Akhil P Oommen Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726523/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-17-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 2 +- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index e7a0d315e022..1c5ad3e83cc2 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -747,7 +747,7 @@ static void a6xx_set_ubwc_config(struct msm_gpu *gpu) bool rgb565_predicator = cfg->ubwc_enc_version >= UBWC_4_0; u32 level2_swizzling_dis = !(qcom_ubwc_swizzle(cfg) & UBWC_SWIZZLE_ENABLE_LVL2); bool ubwc_mode = qcom_ubwc_get_ubwc_mode(cfg); - bool amsbc = cfg->ubwc_enc_version >= UBWC_3_0; + bool amsbc = qcom_ubwc_enable_amsbc(cfg); bool min_acc_len_64b; u8 uavflagprd_inv = 0; u32 hbb_hi = hbb >> 2; diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 867c7d05f670..5c73a9ebb22b 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -279,7 +279,8 @@ static void a8xx_set_ubwc_config(struct msm_gpu *gpu) u32 level3_swizzling_dis = !(qcom_ubwc_swizzle(cfg) & UBWC_SWIZZLE_ENABLE_LVL3); bool rgba8888_lossless = false, fp16compoptdis = false; bool yuvnotcomptofc = false, min_acc_len_64b = false; - bool rgb565_predicator = false, amsbc = false; + bool rgb565_predicator = false; + bool amsbc = qcom_ubwc_enable_amsbc(cfg); bool ubwc_mode = qcom_ubwc_get_ubwc_mode(cfg); u32 ubwc_version = cfg->ubwc_enc_version; u32 hbb, hbb_hi, hbb_lo, mode; @@ -288,21 +289,17 @@ static void a8xx_set_ubwc_config(struct msm_gpu *gpu) switch (ubwc_version) { case UBWC_6_0: yuvnotcomptofc = true; - amsbc = true; rgb565_predicator = true; break; case UBWC_5_0: - amsbc = true; rgb565_predicator = true; break; case UBWC_4_0: - amsbc = true; rgb565_predicator = true; fp16compoptdis = true; rgba8888_lossless = true; break; case UBWC_3_0: - amsbc = true; fp16compoptdis = true; break; default: From 579f66114027467246e6c480ec028a22dcf25034 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:25 +0300 Subject: [PATCH 27/67] drm/msm/adreno: use version ranges in A8xx UBWC code In order to simplify handling of UBWC minor revisions (like 3.1 or 4.3) use version ranges instead of a case switch. Reviewed-by: Akhil P Oommen Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726506/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-18-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 36 +++++++++++++-------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 5c73a9ebb22b..29d559fe4683 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -286,26 +286,24 @@ static void a8xx_set_ubwc_config(struct msm_gpu *gpu) u32 hbb, hbb_hi, hbb_lo, mode; u8 uavflagprd_inv = 2; - switch (ubwc_version) { - case UBWC_6_0: - yuvnotcomptofc = true; - rgb565_predicator = true; - break; - case UBWC_5_0: - rgb565_predicator = true; - break; - case UBWC_4_0: - rgb565_predicator = true; - fp16compoptdis = true; - rgba8888_lossless = true; - break; - case UBWC_3_0: - fp16compoptdis = true; - break; - default: + if (ubwc_version > UBWC_6_0) dev_err(&gpu->pdev->dev, "Unknown UBWC version: 0x%x\n", ubwc_version); - break; - } + + if (ubwc_version == UBWC_6_0) + yuvnotcomptofc = true; + + if (ubwc_version < UBWC_5_0 && + ubwc_version >= UBWC_4_0) + rgba8888_lossless = true; + + if (ubwc_version < UBWC_4_3) + fp16compoptdis = true; + + if (cfg->ubwc_enc_version >= UBWC_4_0) + rgb565_predicator = true; + + if (ubwc_version < UBWC_3_0) + dev_err(&gpu->pdev->dev, "Unsupported UBWC version: 0x%x\n", ubwc_version); mode = qcom_ubwc_version_tag(cfg); From 76bcf0194934a858238eaadc5b5a91eafa4d5829 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:26 +0300 Subject: [PATCH 28/67] drm/msm/mdss: use new helper to set amsbc Use freshly defined helper instead of checking the UBWC version directly. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726525/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-19-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/msm_mdss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index 3c5023c05802..9087c4b290db 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -188,7 +188,7 @@ static void msm_mdss_5x_setup_ubwc(struct msm_mdss *msm_mdss) if (qcom_ubwc_macrotile_mode(data)) value |= MDSS_UBWC_STATIC_MACROTILE_MODE; - if (data->ubwc_enc_version == UBWC_3_0) + if (qcom_ubwc_enable_amsbc(data)) value |= MDSS_UBWC_STATIC_UBWC_AMSBC; value |= MDSS_UBWC_STATIC_UBWC_MIN_ACC_LEN(qcom_ubwc_min_acc_length_64b(data)); From 62342315b65a40a34942b85d07eee499a8283cd3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:27 +0300 Subject: [PATCH 29/67] drm/msm/dpu: drop ubwc_dec_version Stop using ubwc_dec_version (the version of the UBWC block in the display subsystem) for detecting the enablement of the UBWC. Use only ubwc_enc_version, the version of the UBWC which we are setting up for. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726515/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-20-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index 3c315d5805b8..5f42058bedbb 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -1781,8 +1781,7 @@ static bool dpu_plane_format_mod_supported(struct drm_plane *plane, uint32_t format, uint64_t modifier) { struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); - bool has_no_ubwc = (dpu_kms->mdss->ubwc_enc_version == 0) && - (dpu_kms->mdss->ubwc_dec_version == 0); + bool has_no_ubwc = (dpu_kms->mdss->ubwc_enc_version == 0); if (modifier == DRM_FORMAT_MOD_LINEAR) return true; From 25d06d0a4760024f01aa046b434c59ec745295e7 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:28 +0300 Subject: [PATCH 30/67] drm/msm/dpu: invert the order of UBWC checks Unlike other drivers, the DPU driver checks for exact UBWC version, making it hard to add minor versions if necessary. Invert the order of UBWC checks, letting the DPU driver handle new minors transparently. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/726511/ Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-21-72f2749bc807@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c | 36 ++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c index 6089a58074ac..b5e50fc1916f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c @@ -310,35 +310,35 @@ void dpu_hw_setup_format_impl(struct dpu_sw_pipe *pipe, const struct msm_format if (fmt->fetch_mode != MDP_FETCH_LINEAR) { u32 hbb = ctx->ubwc->highest_bank_bit - 13; - u32 ctrl_val; + u32 ctrl_val = 0; if (MSM_FORMAT_IS_UBWC(fmt)) opmode |= MDSS_MDP_OP_BWC_EN; src_format |= (fmt->fetch_mode & 3) << 30; /*FRAME_FORMAT */ - if (ctx->ubwc->ubwc_enc_version == UBWC_1_0) { + if (ctx->ubwc->ubwc_enc_version > UBWC_6_0) { + DRM_WARN_ONCE("Unsupported UBWC version %x\n", ctx->ubwc->ubwc_enc_version); + } else if (ctx->ubwc->ubwc_enc_version >= UBWC_5_0) { + if (!MSM_FORMAT_IS_YUV(fmt)) { + ctrl_val = BIT(30); + if (!MSM_FORMAT_IS_DX(fmt)) /* and not FP16, but it's unsupported */ + ctrl_val |= BIT(31); + } + /* SDE also sets bits for lossy formats, but we don't support them yet */ + } else if (ctx->ubwc->ubwc_enc_version >= UBWC_4_0) { + ctrl_val = MSM_FORMAT_IS_YUV(fmt) ? 0 : BIT(30); + } else if (ctx->ubwc->ubwc_enc_version >= UBWC_3_0) { + ctrl_val = BIT(30) | qcom_ubwc_swizzle(ctx->ubwc) | (hbb << 4); + } else if (ctx->ubwc->ubwc_enc_version >= UBWC_2_0) { + fast_clear = fmt->alpha_enable ? BIT(31) : 0; + ctrl_val = fast_clear | qcom_ubwc_swizzle(ctx->ubwc) | (hbb << 4); + } else if (ctx->ubwc->ubwc_enc_version >= UBWC_1_0) { fast_clear = fmt->alpha_enable ? BIT(31) : 0; ctrl_val = fast_clear | (qcom_ubwc_swizzle(ctx->ubwc) & UBWC_SWIZZLE_ENABLE_LVL1) | BIT(8) | (hbb << 4); - } else if (ctx->ubwc->ubwc_enc_version == UBWC_2_0) { - fast_clear = fmt->alpha_enable ? BIT(31) : 0; - ctrl_val = fast_clear | qcom_ubwc_swizzle(ctx->ubwc) | (hbb << 4); - } else if (ctx->ubwc->ubwc_enc_version == UBWC_3_0) { - ctrl_val = BIT(30) | qcom_ubwc_swizzle(ctx->ubwc) | (hbb << 4); - } else if (ctx->ubwc->ubwc_enc_version == UBWC_4_0) { - ctrl_val = MSM_FORMAT_IS_YUV(fmt) ? 0 : BIT(30); - } else if (ctx->ubwc->ubwc_enc_version <= UBWC_6_0) { - if (MSM_FORMAT_IS_YUV(fmt)) - ctrl_val = 0; - else if (MSM_FORMAT_IS_DX(fmt)) /* or FP16, but it's unsupported */ - ctrl_val = BIT(30); - else - ctrl_val = BIT(30) | BIT(31); - /* SDE also sets bits for lossy formats, but we don't support them yet */ } else { DRM_WARN_ONCE("Unsupported UBWC version %x\n", ctx->ubwc->ubwc_enc_version); - ctrl_val = 0; } DPU_REG_WRITE(c, ubwc_ctrl_off, ctrl_val); From 2e6c2e81d81251623c458a60e2a57447dcbc988e Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Sun, 24 May 2026 13:33:29 +0300 Subject: [PATCH 31/67] drm/msm/dp: fix HPD state status bit shift value The HPD state status is the 3 most significant bits, not 4 bits of the HPD_INT_STATUS register. Fix the bit shift macro so that the correct bits are returned in msm_dp_aux_is_link_connected(). Fixes: 19e52bcb27c2 ("drm/msm/dp: return correct connection status after suspend") Signed-off-by: Jessica Zhang Reviewed-by: Konrad Dybcio Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/727611/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-1-cf3ab488dd7b@oss.qualcomm.com --- drivers/gpu/drm/msm/dp/dp_reg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h index 7c44d4e2cf13..3689642b7fc0 100644 --- a/drivers/gpu/drm/msm/dp/dp_reg.h +++ b/drivers/gpu/drm/msm/dp/dp_reg.h @@ -68,8 +68,8 @@ #define DP_DP_IRQ_HPD_INT_ACK (0x00000002) #define DP_DP_HPD_REPLUG_INT_ACK (0x00000004) #define DP_DP_HPD_UNPLUG_INT_ACK (0x00000008) -#define DP_DP_HPD_STATE_STATUS_BITS_MASK (0x0000000F) -#define DP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1C) +#define DP_DP_HPD_STATE_STATUS_BITS_MASK (0x00000007) +#define DP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1D) #define REG_DP_DP_HPD_INT_MASK (0x0000000C) #define DP_DP_HPD_PLUG_INT_MASK (0x00000001) From 3fbfdc3b1d48cc115a86953e5df0c76cd2efc42b Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Sun, 24 May 2026 13:33:30 +0300 Subject: [PATCH 32/67] drm/msm/dp: Fix the ISR_* enum values The ISR_HPD_* enum should represent values that can be read from the REG_DP_DP_HPD_INT_STATUS register. Swap ISR_HPD_IO_GLITCH_COUNT and ISR_HPD_REPLUG_COUNT to map them correctly to register values. While we are at it, correct the spelling for ISR_HPD_REPLUG_COUNT. Fixes: 8ede2ecc3e5e ("drm/msm/dp: Add DP compliance tests on Snapdragon Chipsets") Signed-off-by: Jessica Zhang Reviewed-by: Konrad Dybcio Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/727602/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-2-cf3ab488dd7b@oss.qualcomm.com --- drivers/gpu/drm/msm/dp/dp_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index d2124d625485..cf2415635c42 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -38,9 +38,9 @@ enum { ISR_DISCONNECTED, ISR_CONNECT_PENDING, ISR_CONNECTED, - ISR_HPD_REPLUG_COUNT, + ISR_HPD_IO_GLITCH_COUNT, ISR_IRQ_HPD_PULSE_COUNT, - ISR_HPD_LO_GLITH_COUNT, + ISR_HPD_REPLUG_COUNT, }; /* event thread connection state */ From 0f2293dfb5fe525cd79c2ff2a59e4bec387baba5 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Sun, 24 May 2026 13:33:31 +0300 Subject: [PATCH 33/67] drm/msm/dp: Read DPCD and sink count in bridge detect() Instead of relying on the link_ready flag to specify if DP is connected, read the DPCD bits and get the sink count to accurately detect if DP is connected. Signed-off-by: Jessica Zhang Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/727603/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-3-cf3ab488dd7b@oss.qualcomm.com --- drivers/gpu/drm/msm/dp/dp_display.c | 63 +++++++++++++++++++++++++++++ drivers/gpu/drm/msm/dp/dp_drm.c | 20 --------- drivers/gpu/drm/msm/dp/dp_drm.h | 2 + 3 files changed, 65 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index cf2415635c42..77f1ae5d7210 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -1152,6 +1152,69 @@ static int msm_dp_hpd_event_thread_start(struct msm_dp_display_private *msm_dp_p return 0; } +/** + * msm_dp_bridge_detect - callback to determine if connector is connected + * + * @bridge: Pointer to drm bridge structure + * @connector: Pointer to drm connector structure + * + * Returns: where there is a display connected to the DPTX (returning + * disconnected for branch devices without DP Sinks being connected). + */ +enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(bridge); + struct msm_dp *dp = msm_dp_bridge->msm_dp_display; + int status = connector_status_disconnected; + struct msm_dp_display_private *priv; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + struct drm_dp_desc desc; + int ret; + + dp = to_dp_bridge(bridge)->msm_dp_display; + + priv = container_of(dp, struct msm_dp_display_private, msm_dp_display); + + if (!dp->link_ready) + return status; + + msm_dp_aux_enable_xfers(priv->aux, true); + + ret = pm_runtime_resume_and_get(&dp->pdev->dev); + if (ret) { + DRM_ERROR("failed to pm_runtime_resume\n"); + msm_dp_aux_enable_xfers(priv->aux, false); + return status; + } + + ret = msm_dp_aux_is_link_connected(priv->aux); + if (dp->internal_hpd && !ret) + goto end; + + ret = drm_dp_read_dpcd_caps(priv->aux, dpcd); + if (ret) + goto end; + + ret = drm_dp_read_desc(priv->aux, &desc, drm_dp_is_branch(dpcd)); + if (ret) + goto end; + + status = connector_status_connected; + if (drm_dp_read_sink_count_cap(connector, dpcd, &desc)) { + int sink_count = drm_dp_read_sink_count(priv->aux); + + drm_dbg_dp(dp->drm_dev, "sink_count = %d\n", sink_count); + + if (sink_count <= 0) + status = connector_status_disconnected; + } + +end: + pm_runtime_put_sync(&dp->pdev->dev); + return status; +} + static irqreturn_t msm_dp_display_irq_handler(int irq, void *dev_id) { struct msm_dp_display_private *dp = dev_id; diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index fd6443d2b6ce..e4622c85fb66 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -15,26 +15,6 @@ #include "dp_audio.h" #include "dp_drm.h" -/** - * msm_dp_bridge_detect - callback to determine if connector is connected - * @bridge: Pointer to drm bridge structure - * @connector: Pointer to drm connector structure - * Returns: Bridge's 'is connected' status - */ -static enum drm_connector_status -msm_dp_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector) -{ - struct msm_dp *dp; - - dp = to_dp_bridge(bridge)->msm_dp_display; - - drm_dbg_dp(dp->drm_dev, "link_ready = %s\n", - str_true_false(dp->link_ready)); - - return (dp->link_ready) ? connector_status_connected : - connector_status_disconnected; -} - static int msm_dp_bridge_atomic_check(struct drm_bridge *bridge, struct drm_bridge_state *bridge_state, struct drm_crtc_state *crtc_state, diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h index 9eb3431dd93a..6c0426803d78 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.h +++ b/drivers/gpu/drm/msm/dp/dp_drm.h @@ -25,6 +25,8 @@ int msm_dp_bridge_init(struct msm_dp *msm_dp_display, struct drm_device *dev, struct drm_encoder *encoder, bool yuv_supported); +enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge, + struct drm_connector *connector); void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, struct drm_atomic_state *state); void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge, From 6fe5a253d7ea94e6926f61f360c785a1d8c2d9f7 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Sun, 24 May 2026 13:33:32 +0300 Subject: [PATCH 34/67] drm/msm/dp: Move link training to atomic_enable() Currently, the DP link training is being done during HPD. Move link training to atomic_enable() in accordance with the atomic_enable() documentation. Link disabling is already done in atomic_post_disable() (as part of the dp_ctrl_off_link_stream() helper). Signed-off-by: Jessica Zhang Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Acked-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/727606/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-4-cf3ab488dd7b@oss.qualcomm.com --- drivers/gpu/drm/msm/dp/dp_display.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 77f1ae5d7210..1745735b3eb2 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -437,11 +437,6 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) msm_dp_link_psm_config(dp->link, &dp->panel->link_info, false); msm_dp_link_reset_phy_params_vx_px(dp->link); - rc = msm_dp_ctrl_on_link(dp->ctrl); - if (rc) { - DRM_ERROR("failed to complete DP link training\n"); - goto end; - } msm_dp_add_event(dp, EV_USER_NOTIFICATION, true, 0); @@ -1699,6 +1694,13 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, force_link_train = true; } + rc = msm_dp_ctrl_on_link(msm_dp_display->ctrl); + if (rc) { + DRM_ERROR("Failed link training (rc=%d)\n", rc); + // TODO: schedule drm_connector_set_link_status_property() + return; + } + msm_dp_display_enable(msm_dp_display, force_link_train); rc = msm_dp_display_post_enable(dp); From 168dacddc3f41ba593e2a297ab1b93bf0d8dbe9f Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Sun, 24 May 2026 13:33:33 +0300 Subject: [PATCH 35/67] drm/msm/dp: Drop EV_USER_NOTIFICATION Currently, we queue an event for signalling HPD connect/disconnect. This can mean a delay in plug/unplug handling and notifying DRM core when a hotplug happens. Drop EV_USER_NOTIFICATION and signal the IRQ event as part of hotplug handling. Signed-off-by: Jessica Zhang Reviewed-by: Bjorn Andersson Acked-by: Konrad Dybcio Reviewed-by: Konrad Dybcio Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/727607/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-5-cf3ab488dd7b@oss.qualcomm.com --- drivers/gpu/drm/msm/dp/dp_display.c | 28 ++++++++-------------------- drivers/gpu/drm/msm/dp/dp_display.h | 1 + drivers/gpu/drm/msm/dp/dp_drm.c | 2 ++ 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 1745735b3eb2..8bf8c4099bbc 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -58,7 +58,6 @@ enum { EV_HPD_PLUG_INT, EV_IRQ_HPD_INT, EV_HPD_UNPLUG_INT, - EV_USER_NOTIFICATION, }; #define EVENT_TIMEOUT (HZ/10) /* 100ms */ @@ -344,17 +343,6 @@ static const struct component_ops msm_dp_display_comp_ops = { .unbind = msm_dp_display_unbind, }; -static void msm_dp_display_send_hpd_event(struct msm_dp *msm_dp_display) -{ - struct msm_dp_display_private *dp; - struct drm_connector *connector; - - dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); - - connector = dp->msm_dp_display.connector; - drm_helper_hpd_irq_event(connector->dev); -} - static int msm_dp_display_send_hpd_notification(struct msm_dp_display_private *dp, bool hpd) { @@ -378,7 +366,11 @@ static int msm_dp_display_send_hpd_notification(struct msm_dp_display_private *d drm_dbg_dp(dp->drm_dev, "type=%d hpd=%d\n", dp->msm_dp_display.connector_type, hpd); - msm_dp_display_send_hpd_event(&dp->msm_dp_display); + + drm_bridge_hpd_notify(dp->msm_dp_display.bridge, + hpd ? + connector_status_connected : + connector_status_disconnected); return 0; } @@ -438,7 +430,7 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) msm_dp_link_reset_phy_params_vx_px(dp->link); - msm_dp_add_event(dp, EV_USER_NOTIFICATION, true, 0); + msm_dp_display_send_hpd_notification(dp, true); end: return rc; @@ -507,7 +499,7 @@ static int msm_dp_display_notify_disconnect(struct device *dev) { struct msm_dp_display_private *dp = dev_get_dp_display_private(dev); - msm_dp_add_event(dp, EV_USER_NOTIFICATION, false, 0); + msm_dp_display_send_hpd_notification(dp, false); return 0; } @@ -528,7 +520,7 @@ static int msm_dp_display_handle_port_status_changed(struct msm_dp_display_priva drm_dbg_dp(dp->drm_dev, "sink count is zero, nothing to do\n"); if (dp->hpd_state != ST_DISCONNECTED) { dp->hpd_state = ST_DISCONNECT_PENDING; - msm_dp_add_event(dp, EV_USER_NOTIFICATION, false, 0); + msm_dp_display_send_hpd_notification(dp, false); } } else { if (dp->hpd_state == ST_DISCONNECTED) { @@ -1122,10 +1114,6 @@ static int hpd_event_thread(void *data) case EV_IRQ_HPD_INT: msm_dp_irq_hpd_handle(msm_dp_priv, todo->data); break; - case EV_USER_NOTIFICATION: - msm_dp_display_send_hpd_notification(msm_dp_priv, - todo->data); - break; default: break; } diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h index cc6e2cab36e9..60094061c102 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.h +++ b/drivers/gpu/drm/msm/dp/dp_display.h @@ -16,6 +16,7 @@ struct msm_dp { struct platform_device *pdev; struct drm_connector *connector; struct drm_bridge *next_bridge; + struct drm_bridge *bridge; bool link_ready; bool audio_enabled; bool power_on; diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index e4622c85fb66..f935093c4df4 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -340,6 +340,8 @@ int msm_dp_bridge_init(struct msm_dp *msm_dp_display, struct drm_device *dev, } } + msm_dp_display->bridge = bridge; + return 0; } From c5d65a2b48da88564e91b5b1f83605267fd1da70 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 24 May 2026 13:33:34 +0300 Subject: [PATCH 36/67] drm/msm/dp: drop event data With EV_USER_NOTIFICATION gone event's data is no longer useful. Drop it, removing also the argument from event handlers. Reviewed-by: Bjorn Andersson Reviewed-by: Konrad Dybcio Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/727610/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-6-cf3ab488dd7b@oss.qualcomm.com --- drivers/gpu/drm/msm/dp/dp_display.c | 39 +++++++++++++---------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 8bf8c4099bbc..588fd9dddfc6 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -69,7 +69,6 @@ enum { struct msm_dp_event { u32 event_id; - u32 data; u32 delay; }; @@ -219,7 +218,7 @@ static struct msm_dp_display_private *dev_get_dp_display_private(struct device * } static int msm_dp_add_event(struct msm_dp_display_private *msm_dp_priv, u32 event, - u32 data, u32 delay) + u32 delay) { unsigned long flag; struct msm_dp_event *todo; @@ -237,7 +236,6 @@ static int msm_dp_add_event(struct msm_dp_display_private *msm_dp_priv, u32 even todo = &msm_dp_priv->event_list[msm_dp_priv->event_pndx++]; msm_dp_priv->event_pndx %= DP_EVENT_Q_MAX; todo->event_id = event; - todo->data = data; todo->delay = delay; wake_up(&msm_dp_priv->event_q); spin_unlock_irqrestore(&msm_dp_priv->event_lock, flag); @@ -577,7 +575,7 @@ static int msm_dp_display_usbpd_attention_cb(struct device *dev) return rc; } -static int msm_dp_hpd_plug_handle(struct msm_dp_display_private *dp, u32 data) +static int msm_dp_hpd_plug_handle(struct msm_dp_display_private *dp) { u32 state; int ret; @@ -603,7 +601,7 @@ static int msm_dp_hpd_plug_handle(struct msm_dp_display_private *dp, u32 data) if (state == ST_DISCONNECT_PENDING) { /* wait until ST_DISCONNECTED */ - msm_dp_add_event(dp, EV_HPD_PLUG_INT, 0, 1); /* delay = 1 */ + msm_dp_add_event(dp, EV_HPD_PLUG_INT, 1); mutex_unlock(&dp->event_mutex); return 0; } @@ -645,7 +643,7 @@ static void msm_dp_display_handle_plugged_change(struct msm_dp *msm_dp_display, plugged); } -static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp, u32 data) +static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp) { u32 state; struct platform_device *pdev = dp->msm_dp_display.pdev; @@ -707,7 +705,7 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp, u32 data) return 0; } -static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp, u32 data) +static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp) { u32 state; @@ -725,7 +723,7 @@ static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp, u32 data) if (state == ST_MAINLINK_READY || state == ST_DISCONNECT_PENDING) { /* wait until ST_CONNECTED */ - msm_dp_add_event(dp, EV_IRQ_HPD_INT, 0, 1); /* delay = 1 */ + msm_dp_add_event(dp, EV_IRQ_HPD_INT, 1); mutex_unlock(&dp->event_mutex); return 0; } @@ -1080,7 +1078,6 @@ static int hpd_event_thread(void *data) todo_next = &msm_dp_priv->event_list[msm_dp_priv->event_pndx++]; msm_dp_priv->event_pndx %= DP_EVENT_Q_MAX; todo_next->event_id = todo->event_id; - todo_next->data = todo->data; todo_next->delay = todo->delay - 1; /* clean up older event */ @@ -1106,13 +1103,13 @@ static int hpd_event_thread(void *data) switch (todo->event_id) { case EV_HPD_PLUG_INT: - msm_dp_hpd_plug_handle(msm_dp_priv, todo->data); + msm_dp_hpd_plug_handle(msm_dp_priv); break; case EV_HPD_UNPLUG_INT: - msm_dp_hpd_unplug_handle(msm_dp_priv, todo->data); + msm_dp_hpd_unplug_handle(msm_dp_priv); break; case EV_IRQ_HPD_INT: - msm_dp_irq_hpd_handle(msm_dp_priv, todo->data); + msm_dp_irq_hpd_handle(msm_dp_priv); break; default: break; @@ -1216,19 +1213,19 @@ static irqreturn_t msm_dp_display_irq_handler(int irq, void *dev_id) dp->msm_dp_display.connector_type, hpd_isr_status); /* hpd related interrupts */ if (hpd_isr_status & DP_DP_HPD_PLUG_INT_MASK) - msm_dp_add_event(dp, EV_HPD_PLUG_INT, 0, 0); + msm_dp_add_event(dp, EV_HPD_PLUG_INT, 0); if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) { - msm_dp_add_event(dp, EV_IRQ_HPD_INT, 0, 0); + msm_dp_add_event(dp, EV_IRQ_HPD_INT, 0); } if (hpd_isr_status & DP_DP_HPD_REPLUG_INT_MASK) { - msm_dp_add_event(dp, EV_HPD_UNPLUG_INT, 0, 0); - msm_dp_add_event(dp, EV_HPD_PLUG_INT, 0, 3); + msm_dp_add_event(dp, EV_HPD_UNPLUG_INT, 0); + msm_dp_add_event(dp, EV_HPD_PLUG_INT, 3); } if (hpd_isr_status & DP_DP_HPD_UNPLUG_INT_MASK) - msm_dp_add_event(dp, EV_HPD_UNPLUG_INT, 0, 0); + msm_dp_add_event(dp, EV_HPD_UNPLUG_INT, 0); ret = IRQ_HANDLED; } @@ -1653,7 +1650,7 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, } if (dp->is_edp) - msm_dp_hpd_plug_handle(msm_dp_display, 0); + msm_dp_hpd_plug_handle(msm_dp_display); mutex_lock(&msm_dp_display->event_mutex); if (pm_runtime_resume_and_get(&dp->pdev->dev)) { @@ -1727,7 +1724,7 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); if (dp->is_edp) - msm_dp_hpd_unplug_handle(msm_dp_display, 0); + msm_dp_hpd_unplug_handle(msm_dp_display); mutex_lock(&msm_dp_display->event_mutex); @@ -1849,7 +1846,7 @@ void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge, return; if (!msm_dp_display->link_ready && status == connector_status_connected) - msm_dp_add_event(dp, EV_HPD_PLUG_INT, 0, 0); + msm_dp_add_event(dp, EV_HPD_PLUG_INT, 0); else if (msm_dp_display->link_ready && status == connector_status_disconnected) - msm_dp_add_event(dp, EV_HPD_UNPLUG_INT, 0, 0); + msm_dp_add_event(dp, EV_HPD_UNPLUG_INT, 0); } From 5043357e75ba7cc6285174216fd448912dc65ab7 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Sun, 24 May 2026 13:33:35 +0300 Subject: [PATCH 37/67] drm/msm/dp: rework HPD handling Handling of the HPD events in the MSM DP driver is plagued with lots of problems. It tries to work aside of the main DRM framework, handling the HPD signals on its own. There are two separate paths, one for the HPD signals coming from the DP HPD pin and another path for signals coming from outside (e.g. from the Type-C AltMode). It lies about the connected state, returning the link established state instead. It is not easy to understand or modify it. Having a separate event machine doesn't add extra clarity. Drop the whole event machine. When the DP receives a HPD event, send it to the DRM core. Then handle the events in the hpd_notify callback, unifying paths for HPD signals. Signed-off-by: Jessica Zhang Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Co-developed-by: Dmitry Baryshkov Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/727616/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-7-cf3ab488dd7b@oss.qualcomm.com --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 16 - drivers/gpu/drm/msm/dp/dp_ctrl.h | 1 - drivers/gpu/drm/msm/dp/dp_display.c | 601 +++++++--------------------- drivers/gpu/drm/msm/dp/dp_display.h | 1 - 4 files changed, 134 insertions(+), 485 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index cba8a71a2561..86ef8c89ad44 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -2583,22 +2583,6 @@ void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl) phy_init(phy); } -void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl) -{ - struct msm_dp_ctrl_private *ctrl; - struct phy *phy; - - ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); - phy = ctrl->phy; - - msm_dp_ctrl_mainlink_disable(ctrl); - - dev_pm_opp_set_rate(ctrl->dev, 0); - msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl); - - phy_power_off(phy); -} - void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl) { struct msm_dp_ctrl_private *ctrl; diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h index 124b9b21bb7f..f68bee62713f 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h @@ -19,7 +19,6 @@ struct phy; int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl); int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train); void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl); -void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl); irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl); diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 588fd9dddfc6..9292c475e4e7 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -43,35 +43,6 @@ enum { ISR_HPD_REPLUG_COUNT, }; -/* event thread connection state */ -enum { - ST_DISCONNECTED, - ST_MAINLINK_READY, - ST_CONNECTED, - ST_DISCONNECT_PENDING, - ST_DISPLAY_OFF, -}; - -enum { - EV_NO_EVENT, - /* hpd events */ - EV_HPD_PLUG_INT, - EV_IRQ_HPD_INT, - EV_HPD_UNPLUG_INT, -}; - -#define EVENT_TIMEOUT (HZ/10) /* 100ms */ -#define DP_EVENT_Q_MAX 8 - -#define DP_TIMEOUT_NONE 0 - -#define WAIT_FOR_RESUME_TIMEOUT_JIFFIES (HZ / 2) - -struct msm_dp_event { - u32 event_id; - u32 delay; -}; - struct msm_dp_display_private { int irq; @@ -95,15 +66,9 @@ struct msm_dp_display_private { /* wait for audio signaling */ struct completion audio_comp; - /* event related only access by event thread */ - struct mutex event_mutex; - wait_queue_head_t event_q; - u32 hpd_state; - u32 event_pndx; - u32 event_gndx; - struct task_struct *ev_tsk; - struct msm_dp_event event_list[DP_EVENT_Q_MAX]; - spinlock_t event_lock; + /* HPD IRQ handling */ + spinlock_t irq_thread_lock; + u32 hpd_isr_status; bool wide_bus_supported; @@ -217,59 +182,6 @@ static struct msm_dp_display_private *dev_get_dp_display_private(struct device * return container_of(dp, struct msm_dp_display_private, msm_dp_display); } -static int msm_dp_add_event(struct msm_dp_display_private *msm_dp_priv, u32 event, - u32 delay) -{ - unsigned long flag; - struct msm_dp_event *todo; - int pndx; - - spin_lock_irqsave(&msm_dp_priv->event_lock, flag); - pndx = msm_dp_priv->event_pndx + 1; - pndx %= DP_EVENT_Q_MAX; - if (pndx == msm_dp_priv->event_gndx) { - pr_err("event_q is full: pndx=%d gndx=%d\n", - msm_dp_priv->event_pndx, msm_dp_priv->event_gndx); - spin_unlock_irqrestore(&msm_dp_priv->event_lock, flag); - return -EPERM; - } - todo = &msm_dp_priv->event_list[msm_dp_priv->event_pndx++]; - msm_dp_priv->event_pndx %= DP_EVENT_Q_MAX; - todo->event_id = event; - todo->delay = delay; - wake_up(&msm_dp_priv->event_q); - spin_unlock_irqrestore(&msm_dp_priv->event_lock, flag); - - return 0; -} - -static int msm_dp_del_event(struct msm_dp_display_private *msm_dp_priv, u32 event) -{ - unsigned long flag; - struct msm_dp_event *todo; - u32 gndx; - - spin_lock_irqsave(&msm_dp_priv->event_lock, flag); - if (msm_dp_priv->event_pndx == msm_dp_priv->event_gndx) { - spin_unlock_irqrestore(&msm_dp_priv->event_lock, flag); - return -ENOENT; - } - - gndx = msm_dp_priv->event_gndx; - while (msm_dp_priv->event_pndx != gndx) { - todo = &msm_dp_priv->event_list[gndx]; - if (todo->event_id == event) { - todo->event_id = EV_NO_EVENT; /* deleted */ - todo->delay = 0; - } - gndx++; - gndx %= DP_EVENT_Q_MAX; - } - spin_unlock_irqrestore(&msm_dp_priv->event_lock, flag); - - return 0; -} - void msm_dp_display_signal_audio_start(struct msm_dp *msm_dp_display) { struct msm_dp_display_private *dp; @@ -288,8 +200,6 @@ void msm_dp_display_signal_audio_complete(struct msm_dp *msm_dp_display) complete_all(&dp->audio_comp); } -static int msm_dp_hpd_event_thread_start(struct msm_dp_display_private *msm_dp_priv); - static int msm_dp_display_bind(struct device *dev, struct device *master, void *data) { @@ -309,12 +219,6 @@ static int msm_dp_display_bind(struct device *dev, struct device *master, goto end; } - rc = msm_dp_hpd_event_thread_start(dp); - if (rc) { - DRM_ERROR("Event thread create failed\n"); - goto end; - } - return 0; end: return rc; @@ -326,8 +230,6 @@ static void msm_dp_display_unbind(struct device *dev, struct device *master, struct msm_dp_display_private *dp = dev_get_dp_display_private(dev); struct msm_drm_private *priv = dev_get_drvdata(master); - kthread_stop(dp->ev_tsk); - of_dp_aux_depopulate_bus(dp->aux); msm_dp_aux_unregister(dp->aux); @@ -341,38 +243,6 @@ static const struct component_ops msm_dp_display_comp_ops = { .unbind = msm_dp_display_unbind, }; -static int msm_dp_display_send_hpd_notification(struct msm_dp_display_private *dp, - bool hpd) -{ - if ((hpd && dp->msm_dp_display.link_ready) || - (!hpd && !dp->msm_dp_display.link_ready)) { - drm_dbg_dp(dp->drm_dev, "HPD already %s\n", str_on_off(hpd)); - return 0; - } - - /* reset video pattern flag on disconnect */ - if (!hpd) { - dp->panel->video_test = false; - if (!dp->msm_dp_display.is_edp) - drm_dp_set_subconnector_property(dp->msm_dp_display.connector, - connector_status_disconnected, - dp->panel->dpcd, - dp->panel->downstream_ports); - } - - dp->msm_dp_display.link_ready = hpd; - - drm_dbg_dp(dp->drm_dev, "type=%d hpd=%d\n", - dp->msm_dp_display.connector_type, hpd); - - drm_bridge_hpd_notify(dp->msm_dp_display.bridge, - hpd ? - connector_status_connected : - connector_status_disconnected); - - return 0; -} - static int msm_dp_display_lttpr_init(struct msm_dp_display_private *dp, u8 *dpcd) { int rc, lttpr_count; @@ -415,6 +285,8 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) dp->panel->dpcd, dp->panel->downstream_ports); + dp->msm_dp_display.link_ready = true; + dp->msm_dp_display.psr_supported = dp->panel->psr_cap.version && psr_enabled; dp->audio_supported = info->has_audio; @@ -428,8 +300,6 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) msm_dp_link_reset_phy_params_vx_px(dp->link); - msm_dp_display_send_hpd_notification(dp, true); - end: return rc; } @@ -484,24 +354,6 @@ static void msm_dp_display_host_deinit(struct msm_dp_display_private *dp) dp->core_initialized = false; } -static int msm_dp_display_usbpd_configure_cb(struct device *dev) -{ - struct msm_dp_display_private *dp = dev_get_dp_display_private(dev); - - msm_dp_display_host_phy_init(dp); - - return msm_dp_display_process_hpd_high(dp); -} - -static int msm_dp_display_notify_disconnect(struct device *dev) -{ - struct msm_dp_display_private *dp = dev_get_dp_display_private(dev); - - msm_dp_display_send_hpd_notification(dp, false); - - return 0; -} - static void msm_dp_display_handle_video_request(struct msm_dp_display_private *dp) { if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) { @@ -510,34 +362,12 @@ static void msm_dp_display_handle_video_request(struct msm_dp_display_private *d } } -static int msm_dp_display_handle_port_status_changed(struct msm_dp_display_private *dp) -{ - int rc = 0; - - if (drm_dp_is_branch(dp->panel->dpcd) && dp->link->sink_count == 0) { - drm_dbg_dp(dp->drm_dev, "sink count is zero, nothing to do\n"); - if (dp->hpd_state != ST_DISCONNECTED) { - dp->hpd_state = ST_DISCONNECT_PENDING; - msm_dp_display_send_hpd_notification(dp, false); - } - } else { - if (dp->hpd_state == ST_DISCONNECTED) { - dp->hpd_state = ST_MAINLINK_READY; - rc = msm_dp_display_process_hpd_high(dp); - if (rc) - dp->hpd_state = ST_DISCONNECTED; - } - } - - return rc; -} - static int msm_dp_display_handle_irq_hpd(struct msm_dp_display_private *dp) { u32 sink_request = dp->link->sink_request; drm_dbg_dp(dp->drm_dev, "%d\n", sink_request); - if (dp->hpd_state == ST_DISCONNECTED) { + if (!dp->msm_dp_display.link_ready) { if (sink_request & DP_LINK_STATUS_UPDATED) { drm_dbg_dp(dp->drm_dev, "Disconnected sink_request: %d\n", sink_request); @@ -554,76 +384,36 @@ static int msm_dp_display_handle_irq_hpd(struct msm_dp_display_private *dp) return 0; } -static int msm_dp_display_usbpd_attention_cb(struct device *dev) -{ - int rc = 0; - u32 sink_request; - struct msm_dp_display_private *dp = dev_get_dp_display_private(dev); - - /* check for any test request issued by sink */ - rc = msm_dp_link_process_request(dp->link); - if (!rc) { - sink_request = dp->link->sink_request; - drm_dbg_dp(dp->drm_dev, "hpd_state=%d sink_request=%d\n", - dp->hpd_state, sink_request); - if (sink_request & DS_PORT_STATUS_CHANGED) - rc = msm_dp_display_handle_port_status_changed(dp); - else - rc = msm_dp_display_handle_irq_hpd(dp); - } - - return rc; -} - static int msm_dp_hpd_plug_handle(struct msm_dp_display_private *dp) { - u32 state; int ret; struct platform_device *pdev = dp->msm_dp_display.pdev; - msm_dp_aux_enable_xfers(dp->aux, true); + drm_dbg_dp(dp->drm_dev, "Before, type=%d\n", + dp->msm_dp_display.connector_type); - mutex_lock(&dp->event_mutex); - - state = dp->hpd_state; - drm_dbg_dp(dp->drm_dev, "Before, type=%d hpd_state=%d\n", - dp->msm_dp_display.connector_type, state); - - if (state == ST_DISPLAY_OFF) { - mutex_unlock(&dp->event_mutex); + if (dp->msm_dp_display.link_ready) return 0; - } - - if (state == ST_MAINLINK_READY || state == ST_CONNECTED) { - mutex_unlock(&dp->event_mutex); - return 0; - } - - if (state == ST_DISCONNECT_PENDING) { - /* wait until ST_DISCONNECTED */ - msm_dp_add_event(dp, EV_HPD_PLUG_INT, 1); - mutex_unlock(&dp->event_mutex); - return 0; - } ret = pm_runtime_resume_and_get(&pdev->dev); if (ret) { DRM_ERROR("failed to pm_runtime_resume\n"); - mutex_unlock(&dp->event_mutex); return ret; } - ret = msm_dp_display_usbpd_configure_cb(&pdev->dev); + msm_dp_aux_enable_xfers(dp->aux, true); + + msm_dp_display_host_phy_init(dp); + + ret = msm_dp_display_process_hpd_high(dp); if (ret) { /* link train failed */ - dp->hpd_state = ST_DISCONNECTED; + dp->msm_dp_display.link_ready = false; + msm_dp_aux_enable_xfers(dp->aux, false); pm_runtime_put_sync(&pdev->dev); - } else { - dp->hpd_state = ST_MAINLINK_READY; } - drm_dbg_dp(dp->drm_dev, "After, type=%d hpd_state=%d\n", - dp->msm_dp_display.connector_type, state); - mutex_unlock(&dp->event_mutex); + drm_dbg_dp(dp->drm_dev, "After, type=%d\n", + dp->msm_dp_display.connector_type); /* uevent will complete connection part */ return 0; @@ -645,97 +435,69 @@ static void msm_dp_display_handle_plugged_change(struct msm_dp *msm_dp_display, static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp) { - u32 state; struct platform_device *pdev = dp->msm_dp_display.pdev; + dp->panel->video_test = false; + msm_dp_aux_enable_xfers(dp->aux, false); - mutex_lock(&dp->event_mutex); + drm_dbg_dp(dp->drm_dev, "Before, type=%d\n", + dp->msm_dp_display.connector_type); - state = dp->hpd_state; - - drm_dbg_dp(dp->drm_dev, "Before, type=%d hpd_state=%d\n", - dp->msm_dp_display.connector_type, state); - - /* unplugged, no more irq_hpd handle */ - msm_dp_del_event(dp, EV_IRQ_HPD_INT); - - if (state == ST_DISCONNECTED) { - /* triggered by irq_hdp with sink_count = 0 */ - if (dp->link->sink_count == 0) { - msm_dp_display_host_phy_exit(dp); - } - msm_dp_display_notify_disconnect(&dp->msm_dp_display.pdev->dev); - mutex_unlock(&dp->event_mutex); + if (!dp->msm_dp_display.link_ready) return 0; - } else if (state == ST_DISCONNECT_PENDING) { - mutex_unlock(&dp->event_mutex); - return 0; - } else if (state == ST_MAINLINK_READY) { - msm_dp_ctrl_off_link(dp->ctrl); + + /* triggered by irq_hdp with sink_count = 0 */ + if (dp->link->sink_count == 0) msm_dp_display_host_phy_exit(dp); - dp->hpd_state = ST_DISCONNECTED; - msm_dp_display_notify_disconnect(&dp->msm_dp_display.pdev->dev); - pm_runtime_put_sync(&pdev->dev); - mutex_unlock(&dp->event_mutex); - return 0; - } /* * We don't need separate work for disconnect as * connect/attention interrupts are disabled */ - msm_dp_display_notify_disconnect(&dp->msm_dp_display.pdev->dev); + if (!dp->msm_dp_display.is_edp) + drm_dp_set_subconnector_property(dp->msm_dp_display.connector, + connector_status_disconnected, + dp->panel->dpcd, + dp->panel->downstream_ports); - if (state == ST_DISPLAY_OFF) { - dp->hpd_state = ST_DISCONNECTED; - } else { - dp->hpd_state = ST_DISCONNECT_PENDING; - } + dp->msm_dp_display.link_ready = false; /* signal the disconnect event early to ensure proper teardown */ msm_dp_display_handle_plugged_change(&dp->msm_dp_display, false); - drm_dbg_dp(dp->drm_dev, "After, type=%d hpd_state=%d\n", - dp->msm_dp_display.connector_type, state); + drm_dbg_dp(dp->drm_dev, "After, type=%d\n", + dp->msm_dp_display.connector_type); /* uevent will complete disconnection part */ pm_runtime_put_sync(&pdev->dev); - mutex_unlock(&dp->event_mutex); return 0; } static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp) { - u32 state; - - mutex_lock(&dp->event_mutex); + u32 sink_request; + int rc = 0; /* irq_hpd can happen at either connected or disconnected state */ - state = dp->hpd_state; - drm_dbg_dp(dp->drm_dev, "Before, type=%d hpd_state=%d\n", - dp->msm_dp_display.connector_type, state); + drm_dbg_dp(dp->drm_dev, "Before, type=%d\n", + dp->msm_dp_display.connector_type); - if (state == ST_DISPLAY_OFF) { - mutex_unlock(&dp->event_mutex); - return 0; + /* check for any test request issued by sink */ + rc = msm_dp_link_process_request(dp->link); + if (!rc) { + sink_request = dp->link->sink_request; + drm_dbg_dp(dp->drm_dev, "sink_request=%d\n", sink_request); + if (sink_request & DS_PORT_STATUS_CHANGED) + rc = msm_dp_display_process_hpd_high(dp); + else + rc = msm_dp_display_handle_irq_hpd(dp); } - if (state == ST_MAINLINK_READY || state == ST_DISCONNECT_PENDING) { - /* wait until ST_CONNECTED */ - msm_dp_add_event(dp, EV_IRQ_HPD_INT, 1); - mutex_unlock(&dp->event_mutex); - return 0; - } + drm_dbg_dp(dp->drm_dev, "After, type=%d\n", + dp->msm_dp_display.connector_type); - msm_dp_display_usbpd_attention_cb(&dp->msm_dp_display.pdev->dev); - - drm_dbg_dp(dp->drm_dev, "After, type=%d hpd_state=%d\n", - dp->msm_dp_display.connector_type, state); - - mutex_unlock(&dp->event_mutex); - - return 0; + return rc; } static void msm_dp_display_deinit_sub_modules(struct msm_dp_display_private *dp) @@ -1011,12 +773,8 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp) * power_on status before dumping DP registers to avoid crash due * to unclocked access */ - mutex_lock(&msm_dp_display->event_mutex); - - if (!dp->power_on) { - mutex_unlock(&msm_dp_display->event_mutex); + if (!dp->power_on) return; - } msm_disp_snapshot_add_block(disp_state, msm_dp_display->ahb_len, msm_dp_display->ahb_base, "dp_ahb"); @@ -1026,8 +784,6 @@ void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp) msm_dp_display->link_base, "dp_link"); msm_disp_snapshot_add_block(disp_state, msm_dp_display->p0_len, msm_dp_display->p0_base, "dp_p0"); - - mutex_unlock(&msm_dp_display->event_mutex); } void msm_dp_display_set_psr(struct msm_dp *msm_dp_display, bool enter) @@ -1043,95 +799,6 @@ void msm_dp_display_set_psr(struct msm_dp *msm_dp_display, bool enter) msm_dp_ctrl_set_psr(dp->ctrl, enter); } -static int hpd_event_thread(void *data) -{ - struct msm_dp_display_private *msm_dp_priv; - unsigned long flag; - struct msm_dp_event *todo; - int timeout_mode = 0; - - msm_dp_priv = (struct msm_dp_display_private *)data; - - while (1) { - if (timeout_mode) { - wait_event_timeout(msm_dp_priv->event_q, - (msm_dp_priv->event_pndx == msm_dp_priv->event_gndx) || - kthread_should_stop(), EVENT_TIMEOUT); - } else { - wait_event_interruptible(msm_dp_priv->event_q, - (msm_dp_priv->event_pndx != msm_dp_priv->event_gndx) || - kthread_should_stop()); - } - - if (kthread_should_stop()) - break; - - spin_lock_irqsave(&msm_dp_priv->event_lock, flag); - todo = &msm_dp_priv->event_list[msm_dp_priv->event_gndx]; - if (todo->delay) { - struct msm_dp_event *todo_next; - - msm_dp_priv->event_gndx++; - msm_dp_priv->event_gndx %= DP_EVENT_Q_MAX; - - /* re enter delay event into q */ - todo_next = &msm_dp_priv->event_list[msm_dp_priv->event_pndx++]; - msm_dp_priv->event_pndx %= DP_EVENT_Q_MAX; - todo_next->event_id = todo->event_id; - todo_next->delay = todo->delay - 1; - - /* clean up older event */ - todo->event_id = EV_NO_EVENT; - todo->delay = 0; - - /* switch to timeout mode */ - timeout_mode = 1; - spin_unlock_irqrestore(&msm_dp_priv->event_lock, flag); - continue; - } - - /* timeout with no events in q */ - if (msm_dp_priv->event_pndx == msm_dp_priv->event_gndx) { - spin_unlock_irqrestore(&msm_dp_priv->event_lock, flag); - continue; - } - - msm_dp_priv->event_gndx++; - msm_dp_priv->event_gndx %= DP_EVENT_Q_MAX; - timeout_mode = 0; - spin_unlock_irqrestore(&msm_dp_priv->event_lock, flag); - - switch (todo->event_id) { - case EV_HPD_PLUG_INT: - msm_dp_hpd_plug_handle(msm_dp_priv); - break; - case EV_HPD_UNPLUG_INT: - msm_dp_hpd_unplug_handle(msm_dp_priv); - break; - case EV_IRQ_HPD_INT: - msm_dp_irq_hpd_handle(msm_dp_priv); - break; - default: - break; - } - } - - return 0; -} - -static int msm_dp_hpd_event_thread_start(struct msm_dp_display_private *msm_dp_priv) -{ - /* set event q to empty */ - msm_dp_priv->event_gndx = 0; - msm_dp_priv->event_pndx = 0; - - msm_dp_priv->ev_tsk = kthread_run(hpd_event_thread, msm_dp_priv, "dp_hpd_handler"); - if (IS_ERR(msm_dp_priv->ev_tsk)) - return PTR_ERR(msm_dp_priv->ev_tsk); - - return 0; -} - /** * msm_dp_bridge_detect - callback to determine if connector is connected * @@ -1159,26 +826,31 @@ enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge, if (!dp->link_ready) return status; - msm_dp_aux_enable_xfers(priv->aux, true); - ret = pm_runtime_resume_and_get(&dp->pdev->dev); if (ret) { DRM_ERROR("failed to pm_runtime_resume\n"); - msm_dp_aux_enable_xfers(priv->aux, false); return status; } + msm_dp_aux_enable_xfers(priv->aux, true); + ret = msm_dp_aux_is_link_connected(priv->aux); - if (dp->internal_hpd && !ret) + if (!ret) { + DRM_DEBUG_DP("aux not connected\n"); goto end; + } ret = drm_dp_read_dpcd_caps(priv->aux, dpcd); - if (ret) + if (ret) { + DRM_DEBUG_DP("failed to read caps\n"); goto end; + } ret = drm_dp_read_desc(priv->aux, &desc, drm_dp_is_branch(dpcd)); - if (ret) + if (ret) { + DRM_DEBUG_DP("failed to read desc\n"); goto end; + } status = connector_status_connected; if (drm_dp_read_sink_count_cap(connector, dpcd, &desc)) { @@ -1198,36 +870,20 @@ enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge, static irqreturn_t msm_dp_display_irq_handler(int irq, void *dev_id) { struct msm_dp_display_private *dp = dev_id; - irqreturn_t ret = IRQ_NONE; u32 hpd_isr_status; - - if (!dp) { - DRM_ERROR("invalid data\n"); - return IRQ_NONE; - } + unsigned long flags; + irqreturn_t ret = IRQ_HANDLED; hpd_isr_status = msm_dp_aux_get_hpd_intr_status(dp->aux); if (hpd_isr_status & 0x0F) { drm_dbg_dp(dp->drm_dev, "type=%d isr=0x%x\n", dp->msm_dp_display.connector_type, hpd_isr_status); - /* hpd related interrupts */ - if (hpd_isr_status & DP_DP_HPD_PLUG_INT_MASK) - msm_dp_add_event(dp, EV_HPD_PLUG_INT, 0); - if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) { - msm_dp_add_event(dp, EV_IRQ_HPD_INT, 0); - } - - if (hpd_isr_status & DP_DP_HPD_REPLUG_INT_MASK) { - msm_dp_add_event(dp, EV_HPD_UNPLUG_INT, 0); - msm_dp_add_event(dp, EV_HPD_PLUG_INT, 3); - } - - if (hpd_isr_status & DP_DP_HPD_UNPLUG_INT_MASK) - msm_dp_add_event(dp, EV_HPD_UNPLUG_INT, 0); - - ret = IRQ_HANDLED; + spin_lock_irqsave(&dp->irq_thread_lock, flags); + dp->hpd_isr_status |= hpd_isr_status; + ret = IRQ_WAKE_THREAD; + spin_unlock_irqrestore(&dp->irq_thread_lock, flags); } /* DP controller isr */ @@ -1236,6 +892,36 @@ static irqreturn_t msm_dp_display_irq_handler(int irq, void *dev_id) return ret; } +static irqreturn_t msm_dp_display_irq_thread(int irq, void *dev_id) +{ + struct msm_dp_display_private *dp = dev_id; + irqreturn_t ret = IRQ_NONE; + unsigned long flags; + u32 hpd_isr_status; + + spin_lock_irqsave(&dp->irq_thread_lock, flags); + hpd_isr_status = dp->hpd_isr_status; + dp->hpd_isr_status = 0; + spin_unlock_irqrestore(&dp->irq_thread_lock, flags); + + if (hpd_isr_status & DP_DP_HPD_UNPLUG_INT_MASK) + drm_bridge_hpd_notify(dp->msm_dp_display.bridge, + connector_status_disconnected); + + if (hpd_isr_status & DP_DP_HPD_PLUG_INT_MASK) + drm_bridge_hpd_notify(dp->msm_dp_display.bridge, + connector_status_connected); + + /* Send HPD as connected and distinguish it in the notifier */ + if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) + drm_bridge_hpd_notify(dp->msm_dp_display.bridge, + connector_status_connected); + + ret = IRQ_HANDLED; + + return ret; +} + static int msm_dp_display_request_irq(struct msm_dp_display_private *dp) { int rc = 0; @@ -1247,9 +933,13 @@ static int msm_dp_display_request_irq(struct msm_dp_display_private *dp) return dp->irq; } - rc = devm_request_irq(&pdev->dev, dp->irq, msm_dp_display_irq_handler, - IRQF_TRIGGER_HIGH|IRQF_NO_AUTOEN, - "dp_display_isr", dp); + spin_lock_init(&dp->irq_thread_lock); + irq_set_status_flags(dp->irq, IRQ_NOAUTOEN); + rc = devm_request_threaded_irq(&pdev->dev, dp->irq, + msm_dp_display_irq_handler, + msm_dp_display_irq_thread, + IRQ_TYPE_LEVEL_HIGH, + "dp_display_isr", dp); if (rc < 0) { DRM_ERROR("failed to request IRQ%u: %d\n", @@ -1429,6 +1119,7 @@ static int msm_dp_display_probe(struct platform_device *pdev) dp->wide_bus_supported = desc->wide_bus_supported; dp->msm_dp_display.is_edp = (dp->msm_dp_display.connector_type == DRM_MODE_CONNECTOR_eDP); + dp->hpd_isr_status = 0; rc = msm_dp_display_get_io(dp); if (rc) @@ -1440,11 +1131,6 @@ static int msm_dp_display_probe(struct platform_device *pdev) return -EPROBE_DEFER; } - /* setup event q */ - mutex_init(&dp->event_mutex); - init_waitqueue_head(&dp->event_q); - spin_lock_init(&dp->event_lock); - /* Store DP audio handle inside DP display */ dp->msm_dp_display.msm_dp_audio = dp->audio; @@ -1640,7 +1326,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, struct msm_dp *dp = msm_dp_bridge->msm_dp_display; int rc = 0; struct msm_dp_display_private *msm_dp_display; - u32 hpd_state; bool force_link_train = false; msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); @@ -1652,29 +1337,21 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, if (dp->is_edp) msm_dp_hpd_plug_handle(msm_dp_display); - mutex_lock(&msm_dp_display->event_mutex); if (pm_runtime_resume_and_get(&dp->pdev->dev)) { DRM_ERROR("failed to pm_runtime_resume\n"); - mutex_unlock(&msm_dp_display->event_mutex); return; } - hpd_state = msm_dp_display->hpd_state; - if (hpd_state != ST_DISPLAY_OFF && hpd_state != ST_MAINLINK_READY) { - mutex_unlock(&msm_dp_display->event_mutex); + if (msm_dp_display->link->sink_count == 0) return; - } rc = msm_dp_display_set_mode(dp, &msm_dp_display->msm_dp_mode); if (rc) { DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc); - mutex_unlock(&msm_dp_display->event_mutex); return; } - hpd_state = msm_dp_display->hpd_state; - - if (hpd_state == ST_DISPLAY_OFF) { + if (dp->link_ready && !dp->power_on) { msm_dp_display_host_phy_init(msm_dp_display); force_link_train = true; } @@ -1694,11 +1371,7 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, msm_dp_display_disable(msm_dp_display); } - /* completed connection */ - msm_dp_display->hpd_state = ST_CONNECTED; - drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type); - mutex_unlock(&msm_dp_display->event_mutex); } void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge, @@ -1718,7 +1391,6 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, { struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge); struct msm_dp *dp = msm_dp_bridge->msm_dp_display; - u32 hpd_state; struct msm_dp_display_private *msm_dp_display; msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); @@ -1726,27 +1398,14 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, if (dp->is_edp) msm_dp_hpd_unplug_handle(msm_dp_display); - mutex_lock(&msm_dp_display->event_mutex); - - hpd_state = msm_dp_display->hpd_state; - if (hpd_state != ST_DISCONNECT_PENDING && hpd_state != ST_CONNECTED) - drm_dbg_dp(dp->drm_dev, "type=%d wrong hpd_state=%d\n", - dp->connector_type, hpd_state); + if (!dp->link_ready) + drm_dbg_dp(dp->drm_dev, "type=%d is disconnected\n", dp->connector_type); msm_dp_display_disable(msm_dp_display); - hpd_state = msm_dp_display->hpd_state; - if (hpd_state == ST_DISCONNECT_PENDING) { - /* completed disconnection */ - msm_dp_display->hpd_state = ST_DISCONNECTED; - } else { - msm_dp_display->hpd_state = ST_DISPLAY_OFF; - } - drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type); pm_runtime_put_sync(&dp->pdev->dev); - mutex_unlock(&msm_dp_display->event_mutex); } void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge, @@ -1802,18 +1461,13 @@ void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge) * step-4: DP PHY is initialized at plugin handler before link training * */ - mutex_lock(&dp->event_mutex); if (pm_runtime_resume_and_get(&msm_dp_display->pdev->dev)) { DRM_ERROR("failed to resume power\n"); - mutex_unlock(&dp->event_mutex); return; } msm_dp_aux_hpd_enable(dp->aux); msm_dp_aux_hpd_intr_enable(dp->aux); - - msm_dp_display->internal_hpd = true; - mutex_unlock(&dp->event_mutex); } void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge) @@ -1822,15 +1476,10 @@ void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge) struct msm_dp *msm_dp_display = msm_dp_bridge->msm_dp_display; struct msm_dp_display_private *dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); - mutex_lock(&dp->event_mutex); - msm_dp_aux_hpd_intr_disable(dp->aux); msm_dp_aux_hpd_disable(dp->aux); - msm_dp_display->internal_hpd = false; - pm_runtime_put_sync(&msm_dp_display->pdev->dev); - mutex_unlock(&dp->event_mutex); } void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge, @@ -1840,13 +1489,31 @@ void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge, struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(bridge); struct msm_dp *msm_dp_display = msm_dp_bridge->msm_dp_display; struct msm_dp_display_private *dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); + u32 hpd_link_status = 0; - /* Without next_bridge interrupts are handled by the DP core directly */ - if (msm_dp_display->internal_hpd) + if (pm_runtime_resume_and_get(&msm_dp_display->pdev->dev)) { + DRM_ERROR("failed to pm_runtime_resume\n"); return; + } - if (!msm_dp_display->link_ready && status == connector_status_connected) - msm_dp_add_event(dp, EV_HPD_PLUG_INT, 0); - else if (msm_dp_display->link_ready && status == connector_status_disconnected) - msm_dp_add_event(dp, EV_HPD_UNPLUG_INT, 0); + hpd_link_status = msm_dp_aux_is_link_connected(dp->aux); + + drm_dbg_dp(dp->drm_dev, "type=%d link hpd_link_status=0x%x, link_ready=%d, status=%d\n", + msm_dp_display->connector_type, hpd_link_status, + msm_dp_display->link_ready, status); + + if (status == connector_status_connected) { + if (hpd_link_status == ISR_HPD_REPLUG_COUNT) { + msm_dp_hpd_unplug_handle(dp); + msm_dp_hpd_plug_handle(dp); + } else if (hpd_link_status == ISR_IRQ_HPD_PULSE_COUNT) { + msm_dp_irq_hpd_handle(dp); + } else { + msm_dp_hpd_plug_handle(dp); + } + } else { + msm_dp_hpd_unplug_handle(dp); + } + + pm_runtime_put_sync(&msm_dp_display->pdev->dev); } diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h index 60094061c102..d2d3d61eb0b0 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.h +++ b/drivers/gpu/drm/msm/dp/dp_display.h @@ -22,7 +22,6 @@ struct msm_dp { bool power_on; unsigned int connector_type; bool is_edp; - bool internal_hpd; struct msm_dp_audio *msm_dp_audio; bool psr_supported; From c2aed45fda708607ba0bcc15aa6cc3dd0ac390ec Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Sun, 24 May 2026 13:33:36 +0300 Subject: [PATCH 38/67] drm/msm/dp: Add sink_count to debug logs Add sink count to the debug logs for [un]plug and HPD IRQ handling. Signed-off-by: Jessica Zhang [DB: dropped link_ready handling] Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/727620/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-8-cf3ab488dd7b@oss.qualcomm.com --- drivers/gpu/drm/msm/dp/dp_display.c | 30 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 9292c475e4e7..bf31bc3f8377 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -389,8 +389,9 @@ static int msm_dp_hpd_plug_handle(struct msm_dp_display_private *dp) int ret; struct platform_device *pdev = dp->msm_dp_display.pdev; - drm_dbg_dp(dp->drm_dev, "Before, type=%d\n", - dp->msm_dp_display.connector_type); + drm_dbg_dp(dp->drm_dev, "Before, type=%d sink_count=%d\n", + dp->msm_dp_display.connector_type, + dp->link->sink_count); if (dp->msm_dp_display.link_ready) return 0; @@ -412,8 +413,9 @@ static int msm_dp_hpd_plug_handle(struct msm_dp_display_private *dp) pm_runtime_put_sync(&pdev->dev); } - drm_dbg_dp(dp->drm_dev, "After, type=%d\n", - dp->msm_dp_display.connector_type); + drm_dbg_dp(dp->drm_dev, "After, type=%d sink_count=%d\n", + dp->msm_dp_display.connector_type, + dp->link->sink_count); /* uevent will complete connection part */ return 0; @@ -441,8 +443,9 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp) msm_dp_aux_enable_xfers(dp->aux, false); - drm_dbg_dp(dp->drm_dev, "Before, type=%d\n", - dp->msm_dp_display.connector_type); + drm_dbg_dp(dp->drm_dev, "Before, type=%d sink_count=%d\n", + dp->msm_dp_display.connector_type, + dp->link->sink_count); if (!dp->msm_dp_display.link_ready) return 0; @@ -466,8 +469,9 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp) /* signal the disconnect event early to ensure proper teardown */ msm_dp_display_handle_plugged_change(&dp->msm_dp_display, false); - drm_dbg_dp(dp->drm_dev, "After, type=%d\n", - dp->msm_dp_display.connector_type); + drm_dbg_dp(dp->drm_dev, "After, type=%d, sink_count=%d\n", + dp->msm_dp_display.connector_type, + dp->link->sink_count); /* uevent will complete disconnection part */ pm_runtime_put_sync(&pdev->dev); @@ -480,8 +484,9 @@ static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp) int rc = 0; /* irq_hpd can happen at either connected or disconnected state */ - drm_dbg_dp(dp->drm_dev, "Before, type=%d\n", - dp->msm_dp_display.connector_type); + drm_dbg_dp(dp->drm_dev, "Before, type=%d, sink_count=%d\n", + dp->msm_dp_display.connector_type, + dp->link->sink_count); /* check for any test request issued by sink */ rc = msm_dp_link_process_request(dp->link); @@ -494,8 +499,9 @@ static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp) rc = msm_dp_display_handle_irq_hpd(dp); } - drm_dbg_dp(dp->drm_dev, "After, type=%d\n", - dp->msm_dp_display.connector_type); + drm_dbg_dp(dp->drm_dev, "After, type=%d, sink_count=%d\n", + dp->msm_dp_display.connector_type, + dp->link->sink_count); return rc; } From 3ea2d1c3d15476d85978a3e40df5cdd6fa944fe6 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 24 May 2026 13:33:37 +0300 Subject: [PATCH 39/67] drm/msm/dp: turn link_ready into plugged Tracking when the DP link is ready isn't that useful from the driver point of view. It doesn't provide a direct information if the device should be suspended, etc. Replace it with the 'plugged' boolean, which is set when the driver knows that there is DPRX plugged. Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/727614/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-9-cf3ab488dd7b@oss.qualcomm.com --- drivers/gpu/drm/msm/dp/dp_display.c | 93 +++++++++++++++++------------ drivers/gpu/drm/msm/dp/dp_display.h | 1 - drivers/gpu/drm/msm/dp/dp_drm.c | 41 ++----------- 3 files changed, 60 insertions(+), 75 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index bf31bc3f8377..8aaf925ee72d 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -53,6 +53,9 @@ struct msm_dp_display_private { bool phy_initialized; bool audio_supported; + struct mutex plugged_lock; + bool plugged; + struct drm_device *drm_dev; struct drm_dp_aux *aux; @@ -285,8 +288,6 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) dp->panel->dpcd, dp->panel->downstream_ports); - dp->msm_dp_display.link_ready = true; - dp->msm_dp_display.psr_supported = dp->panel->psr_cap.version && psr_enabled; dp->audio_supported = info->has_audio; @@ -304,7 +305,16 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) return rc; } -static void msm_dp_display_host_phy_init(struct msm_dp_display_private *dp) +/** + * msm_dp_display_host_phy_init() - start up DP PHY + * @dp: main display data structure + * + * Prepare DP PHY for the AUX transactions to succeed. + * + * Returns: true if this call has initliazed the PHY and false if the PHY has + * already been setup beforehand. + */ +static bool msm_dp_display_host_phy_init(struct msm_dp_display_private *dp) { drm_dbg_dp(dp->drm_dev, "type=%d core_init=%d phy_init=%d\n", dp->msm_dp_display.connector_type, dp->core_initialized, @@ -313,7 +323,10 @@ static void msm_dp_display_host_phy_init(struct msm_dp_display_private *dp) if (!dp->phy_initialized) { msm_dp_ctrl_phy_init(dp->ctrl); dp->phy_initialized = true; + return true; } + + return false; } static void msm_dp_display_host_phy_exit(struct msm_dp_display_private *dp) @@ -367,14 +380,6 @@ static int msm_dp_display_handle_irq_hpd(struct msm_dp_display_private *dp) u32 sink_request = dp->link->sink_request; drm_dbg_dp(dp->drm_dev, "%d\n", sink_request); - if (!dp->msm_dp_display.link_ready) { - if (sink_request & DP_LINK_STATUS_UPDATED) { - drm_dbg_dp(dp->drm_dev, "Disconnected sink_request: %d\n", - sink_request); - DRM_ERROR("Disconnected, no DP_LINK_STATUS_UPDATED\n"); - return -EINVAL; - } - } msm_dp_ctrl_handle_sink_request(dp->ctrl); @@ -393,8 +398,7 @@ static int msm_dp_hpd_plug_handle(struct msm_dp_display_private *dp) dp->msm_dp_display.connector_type, dp->link->sink_count); - if (dp->msm_dp_display.link_ready) - return 0; + guard(mutex)(&dp->plugged_lock); ret = pm_runtime_resume_and_get(&pdev->dev); if (ret) { @@ -407,18 +411,14 @@ static int msm_dp_hpd_plug_handle(struct msm_dp_display_private *dp) msm_dp_display_host_phy_init(dp); ret = msm_dp_display_process_hpd_high(dp); - if (ret) { /* link train failed */ - dp->msm_dp_display.link_ready = false; - msm_dp_aux_enable_xfers(dp->aux, false); - pm_runtime_put_sync(&pdev->dev); - } drm_dbg_dp(dp->drm_dev, "After, type=%d sink_count=%d\n", dp->msm_dp_display.connector_type, dp->link->sink_count); - /* uevent will complete connection part */ - return 0; + dp->plugged = true; + + return ret; }; static void msm_dp_display_handle_plugged_change(struct msm_dp *msm_dp_display, @@ -447,7 +447,8 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp) dp->msm_dp_display.connector_type, dp->link->sink_count); - if (!dp->msm_dp_display.link_ready) + guard(mutex)(&dp->plugged_lock); + if (!dp->plugged) return 0; /* triggered by irq_hdp with sink_count = 0 */ @@ -464,8 +465,6 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp) dp->panel->dpcd, dp->panel->downstream_ports); - dp->msm_dp_display.link_ready = false; - /* signal the disconnect event early to ensure proper teardown */ msm_dp_display_handle_plugged_change(&dp->msm_dp_display, false); @@ -473,8 +472,11 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp) dp->msm_dp_display.connector_type, dp->link->sink_count); - /* uevent will complete disconnection part */ - pm_runtime_put_sync(&pdev->dev); + if (dp->plugged) { + pm_runtime_put_sync(&pdev->dev); + dp->plugged = false; + } + return 0; } @@ -823,42 +825,49 @@ enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge, struct msm_dp_display_private *priv; u8 dpcd[DP_RECEIVER_CAP_SIZE]; struct drm_dp_desc desc; + bool phy_deinit; int ret; dp = to_dp_bridge(bridge)->msm_dp_display; priv = container_of(dp, struct msm_dp_display_private, msm_dp_display); - if (!dp->link_ready) - return status; - + guard(mutex)(&priv->plugged_lock); ret = pm_runtime_resume_and_get(&dp->pdev->dev); if (ret) { DRM_ERROR("failed to pm_runtime_resume\n"); return status; } + phy_deinit = msm_dp_display_host_phy_init(priv); + msm_dp_aux_enable_xfers(priv->aux, true); ret = msm_dp_aux_is_link_connected(priv->aux); - if (!ret) { + DRM_DEBUG_DP("aux link status: %x\n", ret); + if (!priv->plugged && !ret) { DRM_DEBUG_DP("aux not connected\n"); + priv->plugged = false; goto end; } ret = drm_dp_read_dpcd_caps(priv->aux, dpcd); if (ret) { DRM_DEBUG_DP("failed to read caps\n"); + priv->plugged = false; goto end; } ret = drm_dp_read_desc(priv->aux, &desc, drm_dp_is_branch(dpcd)); if (ret) { DRM_DEBUG_DP("failed to read desc\n"); + priv->plugged = false; goto end; } status = connector_status_connected; + priv->plugged = true; + if (drm_dp_read_sink_count_cap(connector, dpcd, &desc)) { int sink_count = drm_dp_read_sink_count(priv->aux); @@ -869,7 +878,19 @@ enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge, } end: - pm_runtime_put_sync(&dp->pdev->dev); + /* + * If we detected the DPRX, leave the controller on so that it doesn't + * lose the state. + */ + if (!priv->plugged) { + if (phy_deinit) { + msm_dp_aux_enable_xfers(priv->aux, false); + msm_dp_display_host_phy_exit(priv); + } + + pm_runtime_put_sync(&dp->pdev->dev); + } + return status; } @@ -1127,6 +1148,8 @@ static int msm_dp_display_probe(struct platform_device *pdev) (dp->msm_dp_display.connector_type == DRM_MODE_CONNECTOR_eDP); dp->hpd_isr_status = 0; + mutex_init(&dp->plugged_lock); + rc = msm_dp_display_get_io(dp); if (rc) return rc; @@ -1357,7 +1380,7 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, return; } - if (dp->link_ready && !dp->power_on) { + if (!dp->power_on) { msm_dp_display_host_phy_init(msm_dp_display); force_link_train = true; } @@ -1404,9 +1427,6 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, if (dp->is_edp) msm_dp_hpd_unplug_handle(msm_dp_display); - if (!dp->link_ready) - drm_dbg_dp(dp->drm_dev, "type=%d is disconnected\n", dp->connector_type); - msm_dp_display_disable(msm_dp_display); drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type); @@ -1504,9 +1524,8 @@ void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge, hpd_link_status = msm_dp_aux_is_link_connected(dp->aux); - drm_dbg_dp(dp->drm_dev, "type=%d link hpd_link_status=0x%x, link_ready=%d, status=%d\n", - msm_dp_display->connector_type, hpd_link_status, - msm_dp_display->link_ready, status); + drm_dbg_dp(dp->drm_dev, "type=%d link hpd_link_status=0x%x, status=%d\n", + msm_dp_display->connector_type, hpd_link_status, status); if (status == connector_status_connected) { if (hpd_link_status == ISR_HPD_REPLUG_COUNT) { diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h index d2d3d61eb0b0..0b65e16c790d 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.h +++ b/drivers/gpu/drm/msm/dp/dp_display.h @@ -17,7 +17,6 @@ struct msm_dp { struct drm_connector *connector; struct drm_bridge *next_bridge; struct drm_bridge *bridge; - bool link_ready; bool audio_enabled; bool power_on; unsigned int connector_type; diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index f935093c4df4..8dc0dabd275c 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -15,36 +15,6 @@ #include "dp_audio.h" #include "dp_drm.h" -static int msm_dp_bridge_atomic_check(struct drm_bridge *bridge, - struct drm_bridge_state *bridge_state, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) -{ - struct msm_dp *dp; - - dp = to_dp_bridge(bridge)->msm_dp_display; - - drm_dbg_dp(dp->drm_dev, "link_ready = %s\n", - str_true_false(dp->link_ready)); - - /* - * There is no protection in the DRM framework to check if the display - * pipeline has been already disabled before trying to disable it again. - * Hence if the sink is unplugged, the pipeline gets disabled, but the - * crtc->active is still true. Any attempt to set the mode or manually - * disable this encoder will result in the crash. - * - * TODO: add support for telling the DRM subsystem that the pipeline is - * disabled by the hardware and thus all access to it should be forbidden. - * After that this piece of code can be removed. - */ - if (bridge->ops & DRM_BRIDGE_OP_HPD) - return (dp->link_ready) ? 0 : -ENOTCONN; - - return 0; -} - - /** * msm_dp_bridge_get_modes - callback to add drm modes via drm_mode_probed_add() * @bridge: Poiner to drm bridge @@ -62,12 +32,10 @@ static int msm_dp_bridge_get_modes(struct drm_bridge *bridge, struct drm_connect dp = to_dp_bridge(bridge)->msm_dp_display; /* pluggable case assumes EDID is read when HPD */ - if (dp->link_ready) { - rc = msm_dp_display_get_modes(dp); - if (rc <= 0) { - DRM_ERROR("failed to get DP sink modes, rc=%d\n", rc); - return rc; - } + rc = msm_dp_display_get_modes(dp); + if (rc <= 0) { + DRM_ERROR("failed to get DP sink modes, rc=%d\n", rc); + return rc; } else { drm_dbg_dp(connector->dev, "No sink connected\n"); } @@ -92,7 +60,6 @@ static const struct drm_bridge_funcs msm_dp_bridge_ops = { .mode_valid = msm_dp_bridge_mode_valid, .get_modes = msm_dp_bridge_get_modes, .detect = msm_dp_bridge_detect, - .atomic_check = msm_dp_bridge_atomic_check, .hpd_enable = msm_dp_bridge_hpd_enable, .hpd_disable = msm_dp_bridge_hpd_disable, .hpd_notify = msm_dp_bridge_hpd_notify, From 24acb35682eacc751417613d37aba6f93b68378d Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 24 May 2026 13:33:38 +0300 Subject: [PATCH 40/67] drm/msm/dp: clear EDID on display unplug Currently the driver only updates the EDID when it detects a connected monitor, which results in the connector still listing outdated modes even after the display is unplugged. Set connector's EDID to NULL on unplug to clear the list of modes. Tested-by: Val Packett # x1e80100-dell-latitude-7455 Tested-by: Yongxing Mou # Hamoa IOT EVK, QCS8300 Ride Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/727619/ Link: https://lore.kernel.org/r/20260524-hpd-refactor-v6-10-cf3ab488dd7b@oss.qualcomm.com --- drivers/gpu/drm/msm/dp/dp_display.c | 4 ++++ drivers/gpu/drm/msm/dp/dp_panel.c | 8 ++++++++ drivers/gpu/drm/msm/dp/dp_panel.h | 2 ++ 3 files changed, 14 insertions(+) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 8aaf925ee72d..133c84f33056 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -451,6 +451,10 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp) if (!dp->plugged) return 0; + /* Don't forget modes for eDP */ + if (!dp->msm_dp_display.is_edp) + msm_dp_panel_unplugged(dp->panel, dp->msm_dp_display.connector); + /* triggered by irq_hdp with sink_count = 0 */ if (dp->link->sink_count == 0) msm_dp_display_host_phy_exit(dp); diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index 891211b23202..6bb021820d7c 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -293,6 +293,14 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, return rc; } +void msm_dp_panel_unplugged(struct msm_dp_panel *msm_dp_panel, + struct drm_connector *connector) +{ + drm_edid_connector_update(connector, NULL); + drm_edid_free(msm_dp_panel->drm_edid); + msm_dp_panel->drm_edid = NULL; +} + u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, u32 mode_edid_bpp, u32 mode_pclk_khz) { diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h index 177c1328fd99..9173e90a5053 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.h +++ b/drivers/gpu/drm/msm/dp/dp_panel.h @@ -49,6 +49,8 @@ int msm_dp_panel_deinit(struct msm_dp_panel *msm_dp_panel); int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en); int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, struct drm_connector *connector); +void msm_dp_panel_unplugged(struct msm_dp_panel *msm_dp_panel, + struct drm_connector *connector); u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, u32 mode_max_bpp, u32 mode_pclk_khz); int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel, From 8de061ca149c94871e41d91a5b2e442ecc26a572 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 18 May 2026 15:17:14 -0700 Subject: [PATCH 41/67] drm/msm: Restore second parameter name in purge() and evict() After commit 3392291fc509 ("drm/msm: Fix shrinker deadlock"), all supported versions of clang warn (or error with CONFIG_WERROR=y): drivers/gpu/drm/msm/msm_gem_shrinker.c:105:58: error: omitting the parameter name in a function definition is a C23 extension [-Werror,-Wc23-extensions] 105 | purge(struct drm_gem_object *obj, struct ww_acquire_ctx *) | ^ drivers/gpu/drm/msm/msm_gem_shrinker.c:117:58: error: omitting the parameter name in a function definition is a C23 extension [-Werror,-Wc23-extensions] 117 | evict(struct drm_gem_object *obj, struct ww_acquire_ctx *) | ^ 2 errors generated. With older but supported versions of GCC, this is an unconditional hard error: drivers/gpu/drm/msm/msm_gem_shrinker.c: In function 'purge': drivers/gpu/drm/msm/msm_gem_shrinker.c:105:35: error: parameter name omitted purge(struct drm_gem_object *obj, struct ww_acquire_ctx *) ^~~~~~~~~~~~~~~~~~~~~~~ drivers/gpu/drm/msm/msm_gem_shrinker.c: In function 'evict': drivers/gpu/drm/msm/msm_gem_shrinker.c:117:35: error: parameter name omitted evict(struct drm_gem_object *obj, struct ww_acquire_ctx *) ^~~~~~~~~~~~~~~~~~~~~~~ Restore the parameter name to clear up the warnings, renaming it "unused" to make it clear it is only needed to satisfy the prototype of drm_gem_lru_scan(). Cc: stable@vger.kernel.org Fixes: 3392291fc509 ("drm/msm: Fix shrinker deadlock") Signed-off-by: Nathan Chancellor Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/725924/ Link: https://lore.kernel.org/r/20260518-drm-msm-fix-c23-extensions-v1-1-0833559418c7@kernel.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_gem_shrinker.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c index 6e39e4e578bb..8f118b5185a1 100644 --- a/drivers/gpu/drm/msm/msm_gem_shrinker.c +++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c @@ -102,7 +102,7 @@ with_vm_locks(void (*fn)(struct drm_gem_object *obj), } static bool -purge(struct drm_gem_object *obj, struct ww_acquire_ctx *) +purge(struct drm_gem_object *obj, struct ww_acquire_ctx *unused) { if (!is_purgeable(to_msm_bo(obj))) return false; @@ -114,7 +114,7 @@ purge(struct drm_gem_object *obj, struct ww_acquire_ctx *) } static bool -evict(struct drm_gem_object *obj, struct ww_acquire_ctx *) +evict(struct drm_gem_object *obj, struct ww_acquire_ctx *unused) { if (is_unevictable(to_msm_bo(obj))) return false; From e7c45d9838b7487c1846a0202c649336b2f5bf87 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Mon, 27 Apr 2026 12:08:59 +0530 Subject: [PATCH 42/67] drm/msm/a8xx: Make a8xx_recover IFPC safe Similar to a6xx_recover(), check the GX power domain status before accessing mmio in GX domain a8xx_recover(). Fixes: 288a93200892 ("drm/msm/adreno: Introduce A8x GPU Support") Signed-off-by: Akhil P Oommen Reviewed-by: Konrad Dybcio Signed-off-by: Taniya Das Patchwork: https://patchwork.freedesktop.org/patch/720977/ Message-ID: <20260427-gfx-clk-fixes-v2-5-797e54b3d464@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 29d559fe4683..86f2b1af9e2a 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -882,17 +882,22 @@ void a8xx_recover(struct msm_gpu *gpu) adreno_dump_info(gpu); - if (hang_debug) - a8xx_dump(gpu); - /* * To handle recovery specific sequences during the rpm suspend we are * about to trigger */ a6xx_gpu->hung = true; - /* Halt SQE first */ - gpu_write(gpu, REG_A8XX_CP_SQE_CNTL, 3); + if (adreno_gpu->funcs->gx_is_on(adreno_gpu)) { + /* + * Sometimes crashstate capture is skipped, so SQE should be + * halted here again + */ + gpu_write(gpu, REG_A8XX_CP_SQE_CNTL, 3); + + if (hang_debug) + a8xx_dump(gpu); + } pm_runtime_dont_use_autosuspend(&gpu->pdev->dev); From fb495a57d66e791144ad1da898914b97a87853d6 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Mon, 27 Apr 2026 12:09:00 +0530 Subject: [PATCH 43/67] drm/msm/a6xx: Limit GXPD votes to recovery in A8x In A8x GPUs, the GX GDSC is moved to a separate block called GXCLKCTL which is under the GX power domain. Due to the way the support for this block is implemented in its driver, pm_runtime votes result in a vote on GX/GMxC/MxC rails from the APPS RSC. This is against the Adreno architecture which require GMU to be the sole voter of these collapsible rails on behalf of GPU, except during the GPU/GMU recovery. To align with this architectural requirement and to realize the power benefits of the IFPC feature, remove the GXPD votes during gmu resume and suspend. And during the recovery sequence, enable/disable the GXPD along with the 'synced_poweroff' genpd hint to force collapse this GDSC. Signed-off-by: Akhil P Oommen Signed-off-by: Taniya Das Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/720979/ Message-ID: <20260427-gfx-clk-fixes-v2-6-797e54b3d464@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 64 ++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index 1b44b9e21ad8..b7166a883b01 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -1250,6 +1250,56 @@ static int a6xx_gmu_secure_init(struct a6xx_gpu *a6xx_gpu) return 0; } +static int a6xx_gmu_gxpd_get(struct a6xx_gmu *gmu) +{ + struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); + struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; + + if (IS_ERR_OR_NULL(gmu->gxpd)) + return 0; + + /* + * On A8xx HW, GX GDSC is moved to a new clk controller block under GX + * power domain. The clock driver for this new block keeps the GX rail + * voted when gxpd is voted. So, use the gxpd only during gpu recovery. + */ + if (adreno_gpu->info->family >= ADRENO_8XX_GEN1) + return 0; + + /* + * On A6x/A7x, "enable" the GX power domain which won't actually do + * anything but it will make sure that the refcounting is correct in + * case we need to bring down the GX after a GMU failure + */ + return pm_runtime_get_sync(gmu->gxpd); +} + +static int a6xx_gmu_gxpd_put(struct a6xx_gmu *gmu) +{ + struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); + struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; + + if (IS_ERR_OR_NULL(gmu->gxpd)) + return 0; + + if (adreno_gpu->info->family < ADRENO_8XX_GEN1) + return pm_runtime_put_sync(gmu->gxpd); + + /* + * On A8x, GX GDSC collapse should be triggered only when it is stuck ON + */ + if (adreno_gpu->funcs->gx_is_on(adreno_gpu)) { + pm_runtime_get_sync(gmu->gxpd); + /* + * Hint to gfxclkctl driver to do a hw collapse during the next + * RPM PUT. This is a special behavior in the gfxclkctl driver + */ + dev_pm_genpd_synced_poweroff(gmu->gxpd); + pm_runtime_put_sync(gmu->gxpd); + } + + return 0; +} int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) { @@ -1266,13 +1316,7 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) /* Turn on the resources */ pm_runtime_get_sync(gmu->dev); - /* - * "enable" the GX power domain which won't actually do anything but it - * will make sure that the refcounting is correct in case we need to - * bring down the GX after a GMU failure - */ - if (!IS_ERR_OR_NULL(gmu->gxpd)) - pm_runtime_get_sync(gmu->gxpd); + a6xx_gmu_gxpd_get(gmu); /* Use a known rate to bring up the GMU */ clk_set_rate(gmu->core_clk, 200000000); @@ -1339,7 +1383,8 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) disable_clk: clk_bulk_disable_unprepare(gmu->nr_clocks, gmu->clocks); rpm_put: - pm_runtime_put(gmu->gxpd); + a6xx_gmu_gxpd_put(gmu); + pm_runtime_put(gmu->dev); return ret; @@ -1455,8 +1500,7 @@ int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu) * domain. Usually the GMU does this but only if the shutdown sequence * was successful */ - if (!IS_ERR_OR_NULL(gmu->gxpd)) - pm_runtime_put_sync(gmu->gxpd); + a6xx_gmu_gxpd_put(gmu); clk_bulk_disable_unprepare(gmu->nr_clocks, gmu->clocks); From 586a34dafc08c4fa68d1b4673de24cd84d09df05 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 22 May 2026 15:41:57 +0530 Subject: [PATCH 44/67] drm/msm/a8xx: Fix RSCC offset In A8xx, the RSCC block is part of GPU's register space. Update the virtual base address of rscc to point to the correct address. Fixes: 50e8a557d8d3 ("drm/msm/a8xx: Add support for A8x GMU") Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/727117/ Message-ID: <20260522-glymur-gpu-dt-v5-1-562c406b210c@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index b7166a883b01..616198a836a4 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -2401,7 +2401,12 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node) goto err_mmio; } } else if (adreno_is_a8xx(adreno_gpu)) { - gmu->rscc = gmu->mmio + 0x19000; + /* + * On a8xx , RSCC lives at GPU base + 0x50000, which falls + * inside the GPU's kgsl_3d0_reg_memory range rather than the + * GMU's. + */ + gmu->rscc = gpu->mmio + 0x50000; } else { gmu->rscc = gmu->mmio + 0x23000; } From f67a9e77277654a85ad3900e8cde7e73a9c9bf15 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 22 May 2026 15:41:58 +0530 Subject: [PATCH 45/67] dt-bindings: display/msm: gpu: Document Adreno X2-185 Adreno X2-185 GPU found in Glymur chipsets belongs to the A8x family. It features a new slice architecture with 4 slices, significantly higher bandwidth throughput compared to mobile counterparts, raytracing support, and the highest GPU Fmax seen so far on an Adreno GPU (1850 Mhz), among other improvements. Update the dt bindings documentation to describe this GPU. Signed-off-by: Akhil P Oommen Reviewed-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/727119/ Message-ID: <20260522-glymur-gpu-dt-v5-2-562c406b210c@oss.qualcomm.com> Signed-off-by: Rob Clark --- .../devicetree/bindings/display/msm/gpu.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/display/msm/gpu.yaml b/Documentation/devicetree/bindings/display/msm/gpu.yaml index 04b2328903ca..77caacd0fb3f 100644 --- a/Documentation/devicetree/bindings/display/msm/gpu.yaml +++ b/Documentation/devicetree/bindings/display/msm/gpu.yaml @@ -411,6 +411,21 @@ allOf: - clocks - clock-names + - if: + properties: + compatible: + contains: + const: qcom,adreno-44070001 + then: + properties: + reg: + minItems: 2 + maxItems: 2 + + reg-names: + minItems: 2 + maxItems: 2 + - if: properties: compatible: @@ -434,6 +449,7 @@ allOf: - qcom,adreno-43050a01 - qcom,adreno-43050c01 - qcom,adreno-43051401 + - qcom,adreno-44070001 then: # Starting with A6xx, the clocks are usually defined in the GMU node properties: From 6477bd5ef0f696484fc12ae7a3902e038df1f71d Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:35 -0700 Subject: [PATCH 46/67] drm/msm: Remove obsolete perf infrastructure Outside of a3xx, this was never really used. And it low-key gets in the way of the new perfcntr support (or at least it is confusing to have two things called "perf"). So lets remove it. This drops the "perf" debugfs file. But these days, nvtop is a better option. (Plus perfetto for newer gens.) Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728200/ Message-ID: <20260526145137.160554-2-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/Makefile | 1 - drivers/gpu/drm/msm/adreno/a2xx_gpu.c | 7 - drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 16 -- drivers/gpu/drm/msm/adreno/a4xx_gpu.c | 3 - drivers/gpu/drm/msm/msm_debugfs.c | 6 - drivers/gpu/drm/msm/msm_drv.c | 1 - drivers/gpu/drm/msm/msm_drv.h | 5 - drivers/gpu/drm/msm/msm_gpu.c | 107 ------------ drivers/gpu/drm/msm/msm_gpu.h | 31 ---- drivers/gpu/drm/msm/msm_perf.c | 235 -------------------------- 10 files changed, 412 deletions(-) delete mode 100644 drivers/gpu/drm/msm/msm_perf.c diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index ba45e99be05b..ce00cfb0a875 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -122,7 +122,6 @@ msm-y += \ msm_gpu_devfreq.o \ msm_io_utils.o \ msm_iommu.o \ - msm_perf.o \ msm_rd.o \ msm_ringbuffer.o \ msm_submitqueue.o \ diff --git a/drivers/gpu/drm/msm/adreno/a2xx_gpu.c b/drivers/gpu/drm/msm/adreno/a2xx_gpu.c index d5a5fa9e2cf8..df4cded9143f 100644 --- a/drivers/gpu/drm/msm/adreno/a2xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a2xx_gpu.c @@ -489,10 +489,6 @@ static u32 a2xx_get_rptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring) return ring->memptrs->rptr; } -static const struct msm_gpu_perfcntr perfcntrs[] = { -/* TODO */ -}; - static struct msm_gpu *a2xx_gpu_init(struct drm_device *dev) { struct a2xx_gpu *a2xx_gpu = NULL; @@ -518,9 +514,6 @@ static struct msm_gpu *a2xx_gpu_init(struct drm_device *dev) adreno_gpu = &a2xx_gpu->base; gpu = &adreno_gpu->base; - gpu->perfcntrs = perfcntrs; - gpu->num_perfcntrs = ARRAY_SIZE(perfcntrs); - ret = adreno_gpu_init(dev, pdev, adreno_gpu, config->info->funcs, 1); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 018183e0ac3f..c17e9777beae 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -266,12 +266,6 @@ static int a3xx_hw_init(struct msm_gpu *gpu) /* Turn on performance counters: */ gpu_write(gpu, REG_A3XX_RBBM_PERFCTR_CTL, 0x01); - /* Enable the perfcntrs that we use.. */ - for (i = 0; i < gpu->num_perfcntrs; i++) { - const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i]; - gpu_write(gpu, perfcntr->select_reg, perfcntr->select_val); - } - gpu_write(gpu, REG_A3XX_RBBM_INT_0_MASK, A3XX_INT0_MASK); ret = adreno_hw_init(gpu); @@ -508,13 +502,6 @@ static u32 a3xx_get_rptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring) return ring->memptrs->rptr; } -static const struct msm_gpu_perfcntr perfcntrs[] = { - { REG_A3XX_SP_PERFCOUNTER6_SELECT, REG_A3XX_RBBM_PERFCTR_SP_6_LO, - SP_ALU_ACTIVE_CYCLES, "ALUACTIVE" }, - { REG_A3XX_SP_PERFCOUNTER7_SELECT, REG_A3XX_RBBM_PERFCTR_SP_7_LO, - SP_FS_FULL_ALU_INSTRUCTIONS, "ALUFULL" }, -}; - static struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) { struct a3xx_gpu *a3xx_gpu = NULL; @@ -542,9 +529,6 @@ static struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) adreno_gpu = &a3xx_gpu->base; gpu = &adreno_gpu->base; - gpu->perfcntrs = perfcntrs; - gpu->num_perfcntrs = ARRAY_SIZE(perfcntrs); - adreno_gpu->registers = a3xx_registers; ret = adreno_gpu_init(dev, pdev, adreno_gpu, config->info->funcs, 1); diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c index e6ab731f8e9a..6392126f48f2 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c @@ -652,9 +652,6 @@ static struct msm_gpu *a4xx_gpu_init(struct drm_device *dev) adreno_gpu = &a4xx_gpu->base; gpu = &adreno_gpu->base; - gpu->perfcntrs = NULL; - gpu->num_perfcntrs = 0; - ret = adreno_gpu_init(dev, pdev, adreno_gpu, config->info->funcs, 1); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/msm_debugfs.c b/drivers/gpu/drm/msm/msm_debugfs.c index 1059a9b29d6a..f12701e286ec 100644 --- a/drivers/gpu/drm/msm/msm_debugfs.c +++ b/drivers/gpu/drm/msm/msm_debugfs.c @@ -344,12 +344,6 @@ static int late_init_minor(struct drm_minor *minor) return ret; } - ret = msm_perf_debugfs_init(minor); - if (ret) { - DRM_DEV_ERROR(dev->dev, "could not install perf debugfs\n"); - return ret; - } - return 0; } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 195f40e331e5..66322eab7890 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -87,7 +87,6 @@ static int msm_drm_uninit(struct device *dev, const struct component_ops *gpu_op msm_gem_shrinker_cleanup(ddev); - msm_perf_debugfs_cleanup(priv); msm_rd_debugfs_cleanup(priv); if (priv->kms) diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 6d847d593f1a..e53e4f220bed 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -45,7 +45,6 @@ struct msm_gpu; struct msm_mmu; struct msm_mdss; struct msm_rd_state; -struct msm_perf_state; struct msm_gem_submit; struct msm_fence_context; struct msm_disp_state; @@ -89,7 +88,6 @@ struct msm_drm_private { struct msm_rd_state *rd; /* debugfs to dump all submits */ struct msm_rd_state *hangrd; /* debugfs to dump hanging submits */ - struct msm_perf_state *perf; /** * total_mem: Total/global amount of memory backing GEM objects. @@ -442,8 +440,6 @@ void msm_rd_debugfs_cleanup(struct msm_drm_private *priv); __printf(3, 4) void msm_rd_dump_submit(struct msm_rd_state *rd, struct msm_gem_submit *submit, const char *fmt, ...); -int msm_perf_debugfs_init(struct drm_minor *minor); -void msm_perf_debugfs_cleanup(struct msm_drm_private *priv); #else static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; } __printf(3, 4) @@ -451,7 +447,6 @@ static inline void msm_rd_dump_submit(struct msm_rd_state *rd, struct msm_gem_submit *submit, const char *fmt, ...) {} static inline void msm_rd_debugfs_cleanup(struct msm_drm_private *priv) {} -static inline void msm_perf_debugfs_cleanup(struct msm_drm_private *priv) {} #endif struct clk *msm_clk_get(struct platform_device *pdev, const char *name); diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 3f3925b11eea..08940bae3b6e 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -681,104 +681,6 @@ static void hangcheck_handler(struct timer_list *t) msm_gpu_retire(gpu); } -/* - * Performance Counters: - */ - -/* called under perf_lock */ -static int update_hw_cntrs(struct msm_gpu *gpu, uint32_t ncntrs, uint32_t *cntrs) -{ - uint32_t current_cntrs[ARRAY_SIZE(gpu->last_cntrs)]; - int i, n = min(ncntrs, gpu->num_perfcntrs); - - /* read current values: */ - for (i = 0; i < gpu->num_perfcntrs; i++) - current_cntrs[i] = gpu_read(gpu, gpu->perfcntrs[i].sample_reg); - - /* update cntrs: */ - for (i = 0; i < n; i++) - cntrs[i] = current_cntrs[i] - gpu->last_cntrs[i]; - - /* save current values: */ - for (i = 0; i < gpu->num_perfcntrs; i++) - gpu->last_cntrs[i] = current_cntrs[i]; - - return n; -} - -static void update_sw_cntrs(struct msm_gpu *gpu) -{ - ktime_t time; - uint32_t elapsed; - unsigned long flags; - - spin_lock_irqsave(&gpu->perf_lock, flags); - if (!gpu->perfcntr_active) - goto out; - - time = ktime_get(); - elapsed = ktime_to_us(ktime_sub(time, gpu->last_sample.time)); - - gpu->totaltime += elapsed; - if (gpu->last_sample.active) - gpu->activetime += elapsed; - - gpu->last_sample.active = msm_gpu_active(gpu); - gpu->last_sample.time = time; - -out: - spin_unlock_irqrestore(&gpu->perf_lock, flags); -} - -void msm_gpu_perfcntr_start(struct msm_gpu *gpu) -{ - unsigned long flags; - - pm_runtime_get_sync(&gpu->pdev->dev); - - spin_lock_irqsave(&gpu->perf_lock, flags); - /* we could dynamically enable/disable perfcntr registers too.. */ - gpu->last_sample.active = msm_gpu_active(gpu); - gpu->last_sample.time = ktime_get(); - gpu->activetime = gpu->totaltime = 0; - gpu->perfcntr_active = true; - update_hw_cntrs(gpu, 0, NULL); - spin_unlock_irqrestore(&gpu->perf_lock, flags); -} - -void msm_gpu_perfcntr_stop(struct msm_gpu *gpu) -{ - gpu->perfcntr_active = false; - pm_runtime_put_sync(&gpu->pdev->dev); -} - -/* returns -errno or # of cntrs sampled */ -int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime, - uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&gpu->perf_lock, flags); - - if (!gpu->perfcntr_active) { - ret = -EINVAL; - goto out; - } - - *activetime = gpu->activetime; - *totaltime = gpu->totaltime; - - gpu->activetime = gpu->totaltime = 0; - - ret = update_hw_cntrs(gpu, ncntrs, cntrs); - -out: - spin_unlock_irqrestore(&gpu->perf_lock, flags); - - return ret; -} - /* * Cmdstream submission/retirement: */ @@ -881,7 +783,6 @@ void msm_gpu_retire(struct msm_gpu *gpu) msm_update_fence(gpu->rb[i]->fctx, gpu->rb[i]->memptrs->fence); kthread_queue_work(gpu->worker, &gpu->retire_work); - update_sw_cntrs(gpu); } /* add bo's to gpu's ring, and kick gpu: */ @@ -898,8 +799,6 @@ void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) submit->seqno = submit->hw_fence->seqno; - update_sw_cntrs(gpu); - /* * ring->submits holds a ref to the submit, to deal with the case * that a submit completes before msm_ioctl_gem_submit() returns. @@ -991,9 +890,6 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, void *memptrs; uint64_t memptrs_iova; - if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs))) - gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs); - gpu->dev = drm; gpu->funcs = funcs; gpu->name = name; @@ -1025,9 +921,6 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, timer_setup(&gpu->hangcheck_timer, hangcheck_handler, 0); - spin_lock_init(&gpu->perf_lock); - - /* Map registers: */ gpu->mmio = msm_ioremap(pdev, config->ioname); if (IS_ERR(gpu->mmio)) { diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 666cf499b7ec..4b16b8069617 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -22,7 +22,6 @@ struct msm_gem_submit; struct msm_gem_vm_log_entry; -struct msm_gpu_perfcntr; struct msm_gpu_state; struct msm_context; @@ -168,18 +167,6 @@ struct msm_gpu { struct adreno_smmu_priv adreno_smmu; - /* performance counters (hw & sw): */ - spinlock_t perf_lock; - bool perfcntr_active; - struct { - bool active; - ktime_t time; - } last_sample; - uint32_t totaltime, activetime; /* sw counters */ - uint32_t last_cntrs[5]; /* hw counters */ - const struct msm_gpu_perfcntr *perfcntrs; - uint32_t num_perfcntrs; - struct msm_ringbuffer *rb[MSM_GPU_MAX_RINGS]; int nr_rings; @@ -320,19 +307,6 @@ static inline bool msm_gpu_active(struct msm_gpu *gpu) return false; } -/* Perf-Counters: - * The select_reg and select_val are just there for the benefit of the child - * class that actually enables the perf counter.. but msm_gpu base class - * will handle sampling/displaying the counters. - */ - -struct msm_gpu_perfcntr { - uint32_t select_reg; - uint32_t sample_reg; - uint32_t select_val; - const char *name; -}; - /* * The number of priority levels provided by drm gpu scheduler. The * DRM_SCHED_PRIORITY_KERNEL priority level is treated specially in some @@ -689,11 +663,6 @@ void msm_devfreq_idle(struct msm_gpu *gpu); int msm_gpu_hw_init(struct msm_gpu *gpu); -void msm_gpu_perfcntr_start(struct msm_gpu *gpu); -void msm_gpu_perfcntr_stop(struct msm_gpu *gpu); -int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime, - uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs); - void msm_gpu_retire(struct msm_gpu *gpu); void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit); diff --git a/drivers/gpu/drm/msm/msm_perf.c b/drivers/gpu/drm/msm/msm_perf.c deleted file mode 100644 index 7768bde6745f..000000000000 --- a/drivers/gpu/drm/msm/msm_perf.c +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2013 Red Hat - * Author: Rob Clark - */ - -/* For profiling, userspace can: - * - * tail -f /sys/kernel/debug/dri//gpu - * - * This will enable performance counters/profiling to track the busy time - * and any gpu specific performance counters that are supported. - */ - -#ifdef CONFIG_DEBUG_FS - -#include -#include - -#include - -#include "msm_drv.h" -#include "msm_gpu.h" - -struct msm_perf_state { - struct drm_device *dev; - - bool open; - int cnt; - struct mutex read_lock; - - char buf[256]; - int buftot, bufpos; - - unsigned long next_jiffies; -}; - -#define SAMPLE_TIME (HZ/4) - -/* wait for next sample time: */ -static int wait_sample(struct msm_perf_state *perf) -{ - unsigned long start_jiffies = jiffies; - - if (time_after(perf->next_jiffies, start_jiffies)) { - unsigned long remaining_jiffies = - perf->next_jiffies - start_jiffies; - int ret = schedule_timeout_interruptible(remaining_jiffies); - if (ret > 0) { - /* interrupted */ - return -ERESTARTSYS; - } - } - perf->next_jiffies += SAMPLE_TIME; - return 0; -} - -static int refill_buf(struct msm_perf_state *perf) -{ - struct msm_drm_private *priv = perf->dev->dev_private; - struct msm_gpu *gpu = priv->gpu; - char *ptr = perf->buf; - int rem = sizeof(perf->buf); - int i, n; - - if ((perf->cnt++ % 32) == 0) { - /* Header line: */ - n = scnprintf(ptr, rem, "%%BUSY"); - ptr += n; - rem -= n; - - for (i = 0; i < gpu->num_perfcntrs; i++) { - const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i]; - n = scnprintf(ptr, rem, "\t%s", perfcntr->name); - ptr += n; - rem -= n; - } - } else { - /* Sample line: */ - uint32_t activetime = 0, totaltime = 0; - uint32_t cntrs[5]; - uint32_t val; - int ret; - - /* sleep until next sample time: */ - ret = wait_sample(perf); - if (ret) - return ret; - - ret = msm_gpu_perfcntr_sample(gpu, &activetime, &totaltime, - ARRAY_SIZE(cntrs), cntrs); - if (ret < 0) - return ret; - - val = totaltime ? 1000 * activetime / totaltime : 0; - n = scnprintf(ptr, rem, "%3d.%d%%", val / 10, val % 10); - ptr += n; - rem -= n; - - for (i = 0; i < ret; i++) { - /* cycle counters (I think).. convert to MHz.. */ - val = cntrs[i] / 10000; - n = scnprintf(ptr, rem, "\t%5d.%02d", - val / 100, val % 100); - ptr += n; - rem -= n; - } - } - - n = scnprintf(ptr, rem, "\n"); - ptr += n; - rem -= n; - - perf->bufpos = 0; - perf->buftot = ptr - perf->buf; - - return 0; -} - -static ssize_t perf_read(struct file *file, char __user *buf, - size_t sz, loff_t *ppos) -{ - struct msm_perf_state *perf = file->private_data; - int n = 0, ret = 0; - - mutex_lock(&perf->read_lock); - - if (perf->bufpos >= perf->buftot) { - ret = refill_buf(perf); - if (ret) - goto out; - } - - n = min((int)sz, perf->buftot - perf->bufpos); - if (copy_to_user(buf, &perf->buf[perf->bufpos], n)) { - ret = -EFAULT; - goto out; - } - - perf->bufpos += n; - *ppos += n; - -out: - mutex_unlock(&perf->read_lock); - if (ret) - return ret; - return n; -} - -static int perf_open(struct inode *inode, struct file *file) -{ - struct msm_perf_state *perf = inode->i_private; - struct drm_device *dev = perf->dev; - struct msm_drm_private *priv = dev->dev_private; - struct msm_gpu *gpu = priv->gpu; - int ret = 0; - - if (!gpu) - return -ENODEV; - - mutex_lock(&gpu->lock); - - if (perf->open) { - ret = -EBUSY; - goto out; - } - - file->private_data = perf; - perf->open = true; - perf->cnt = 0; - perf->buftot = 0; - perf->bufpos = 0; - msm_gpu_perfcntr_start(gpu); - perf->next_jiffies = jiffies + SAMPLE_TIME; - -out: - mutex_unlock(&gpu->lock); - return ret; -} - -static int perf_release(struct inode *inode, struct file *file) -{ - struct msm_perf_state *perf = inode->i_private; - struct msm_drm_private *priv = perf->dev->dev_private; - msm_gpu_perfcntr_stop(priv->gpu); - perf->open = false; - return 0; -} - - -static const struct file_operations perf_debugfs_fops = { - .owner = THIS_MODULE, - .open = perf_open, - .read = perf_read, - .release = perf_release, -}; - -int msm_perf_debugfs_init(struct drm_minor *minor) -{ - struct msm_drm_private *priv = minor->dev->dev_private; - struct msm_perf_state *perf; - - /* only create on first minor: */ - if (priv->perf) - return 0; - - perf = kzalloc_obj(*perf); - if (!perf) - return -ENOMEM; - - perf->dev = minor->dev; - - mutex_init(&perf->read_lock); - priv->perf = perf; - - debugfs_create_file("perf", S_IFREG | S_IRUGO, minor->debugfs_root, - perf, &perf_debugfs_fops); - return 0; -} - -void msm_perf_debugfs_cleanup(struct msm_drm_private *priv) -{ - struct msm_perf_state *perf = priv->perf; - - if (!perf) - return; - - priv->perf = NULL; - - mutex_destroy(&perf->read_lock); - - kfree(perf); -} - -#endif From e8c4d60f5372a7b5d4f74cc71685cd185c3e8de0 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:36 -0700 Subject: [PATCH 47/67] drm/msm: Allow CAP_PERFMON for setting SYSPROF Use perfmon_capable() which checks both CAP_SYS_ADMIN and CAP_PERFMON. This matches what i915 and xe do, and seems more appropriate. Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728198/ Message-ID: <20260526145137.160554-3-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 6a0877e5374c..69918975f711 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -500,7 +500,7 @@ int adreno_set_param(struct msm_gpu *gpu, struct msm_context *ctx, return 0; } case MSM_PARAM_SYSPROF: - if (!capable(CAP_SYS_ADMIN)) + if (!perfmon_capable()) return UERR(EPERM, drm, "invalid permissions"); return msm_context_set_sysprof(ctx, gpu, value); case MSM_PARAM_EN_VM_BIND: From 9f860dbf0bb691604d7da612148c390ae9bdaa26 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:37 -0700 Subject: [PATCH 48/67] drm/msm/adreno: Sync registers from mesa Most of the churn is just reworking the usage attribute on the mesa side. Sync from mesa commit ff41a00fab89 ("freedreno/registers: Correct register name") Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728202/ Message-ID: <20260526145137.160554-4-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 16 +- drivers/gpu/drm/msm/registers/adreno/a3xx.xml | 8 +- drivers/gpu/drm/msm/registers/adreno/a5xx.xml | 139 +- drivers/gpu/drm/msm/registers/adreno/a6xx.xml | 1286 ++++++----- .../msm/registers/adreno/a6xx_descriptors.xml | 71 +- .../drm/msm/registers/adreno/a6xx_enums.xml | 3 + .../msm/registers/adreno/a8xx_descriptors.xml | 96 +- .../msm/registers/adreno/a8xx_perfcntrs.xml | 1929 +++++++++++++++++ .../msm/registers/adreno/adreno_common.xml | 42 + .../drm/msm/registers/adreno/adreno_pm4.xml | 50 +- 10 files changed, 2838 insertions(+), 802 deletions(-) create mode 100644 drivers/gpu/drm/msm/registers/adreno/a8xx_perfcntrs.xml diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 699fc0f551ed..2c0bbac43c52 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -752,17 +752,13 @@ static int a5xx_hw_init(struct msm_gpu *gpu) gpu_write(gpu, REG_A5XX_UCHE_CACHE_WAYS, 0x02); /* Disable L2 bypass in the UCHE */ - gpu_write(gpu, REG_A5XX_UCHE_TRAP_BASE_LO, lower_32_bits(adreno_gpu->uche_trap_base)); - gpu_write(gpu, REG_A5XX_UCHE_TRAP_BASE_HI, upper_32_bits(adreno_gpu->uche_trap_base)); - gpu_write(gpu, REG_A5XX_UCHE_WRITE_THRU_BASE_LO, lower_32_bits(adreno_gpu->uche_trap_base)); - gpu_write(gpu, REG_A5XX_UCHE_WRITE_THRU_BASE_HI, upper_32_bits(adreno_gpu->uche_trap_base)); + gpu_write64(gpu, REG_A5XX_UCHE_TRAP_BASE, adreno_gpu->uche_trap_base); + gpu_write64(gpu, REG_A5XX_UCHE_WRITE_THRU_BASE, adreno_gpu->uche_trap_base); /* Set the GMEM VA range (0 to gpu->gmem) */ - gpu_write(gpu, REG_A5XX_UCHE_GMEM_RANGE_MIN_LO, 0x00100000); - gpu_write(gpu, REG_A5XX_UCHE_GMEM_RANGE_MIN_HI, 0x00000000); - gpu_write(gpu, REG_A5XX_UCHE_GMEM_RANGE_MAX_LO, + gpu_write64(gpu, REG_A5XX_UCHE_GMEM_RANGE_MIN, 0x00100000); + gpu_write64(gpu, REG_A5XX_UCHE_GMEM_RANGE_MAX, 0x00100000 + adreno_gpu->info->gmem - 1); - gpu_write(gpu, REG_A5XX_UCHE_GMEM_RANGE_MAX_HI, 0x00000000); if (adreno_is_a505(adreno_gpu) || adreno_is_a506(adreno_gpu) || adreno_is_a508(adreno_gpu) || adreno_is_a510(adreno_gpu)) { @@ -1217,9 +1213,7 @@ static void a5xx_rbbm_err_irq(struct msm_gpu *gpu, u32 status) static void a5xx_uche_err_irq(struct msm_gpu *gpu) { - uint64_t addr = (uint64_t) gpu_read(gpu, REG_A5XX_UCHE_TRAP_LOG_HI); - - addr |= gpu_read(gpu, REG_A5XX_UCHE_TRAP_LOG_LO); + uint64_t addr = gpu_read64(gpu, REG_A5XX_UCHE_TRAP_LOG); dev_err_ratelimited(gpu->dev->dev, "UCHE | Out of bounds access | addr=0x%llX\n", addr); diff --git a/drivers/gpu/drm/msm/registers/adreno/a3xx.xml b/drivers/gpu/drm/msm/registers/adreno/a3xx.xml index 6717abc0a897..09c9606fc3e1 100644 --- a/drivers/gpu/drm/msm/registers/adreno/a3xx.xml +++ b/drivers/gpu/drm/msm/registers/adreno/a3xx.xml @@ -1330,11 +1330,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> - - + @@ -1420,7 +1416,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> - + diff --git a/drivers/gpu/drm/msm/registers/adreno/a5xx.xml b/drivers/gpu/drm/msm/registers/adreno/a5xx.xml index bd8df5945166..4af76b3750f7 100644 --- a/drivers/gpu/drm/msm/registers/adreno/a5xx.xml +++ b/drivers/gpu/drm/msm/registers/adreno/a5xx.xml @@ -1418,8 +1418,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> - - + @@ -1498,12 +1497,10 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> - - + - - + @@ -1555,20 +1552,14 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> - - - - - - - - + + + + - - - - + + @@ -1583,8 +1574,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> - - + @@ -1923,8 +1913,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> invalidates the LRZ buffer? (Or just the covered positions? --> - - + @@ -1933,8 +1922,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> is also divided by 8 (ie. covers 8x8 pixels) - - + @@ -2035,8 +2023,7 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set --> - - + @@ -2089,8 +2076,7 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set - - + stride of depth/stencil buffer @@ -2119,8 +2105,7 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set - - + @@ -2163,8 +2148,7 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set also for gmem->mem preserving tiling --> - - + @@ -2235,25 +2219,22 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set - - + - + - - + - - + @@ -2357,13 +2338,11 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set - - + - - + @@ -2423,8 +2402,7 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set - - + @@ -2475,7 +2453,7 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set - + @@ -2516,8 +2494,7 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set - - + @@ -2538,8 +2515,7 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set - - + @@ -2577,8 +2553,7 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set - - + @@ -2587,22 +2562,19 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set - - + - - + - - + @@ -2615,8 +2587,7 @@ bit 7 for RECTLIST (clear) when z32s8 (used for clear of depth32? not set - - + diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml index 2309870f5031..3349c01646e1 100644 --- a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml +++ b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml @@ -10,19 +10,24 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> + @@ -1321,7 +1326,7 @@ by a particular renderpass/blit. - + @@ -1331,13 +1336,13 @@ by a particular renderpass/blit. - + - + - + Configures the mapping between VSC_PIPE buffer and @@ -1370,7 +1375,7 @@ by a particular renderpass/blit. - + Seems to be a bitmap of which tiles mapped to the VSC pipe contain geometry. @@ -1381,7 +1386,7 @@ by a particular renderpass/blit. - + Has the size of data written to corresponding VSC_PRIM_STRM buffer. @@ -1389,7 +1394,7 @@ by a particular renderpass/blit. - + Has the size of data written to corresponding VSC pipe, ie. same thing that is written out to VSC_SIZE_BASE @@ -1397,7 +1402,7 @@ by a particular renderpass/blit. - + @@ -1428,29 +1433,22 @@ by a particular renderpass/blit. - - + + - - - - + + + + - - - - - - - - - - - + + + + @@ -1465,16 +1463,16 @@ by a particular renderpass/blit. - - + + - - + + @@ -1499,33 +1497,48 @@ by a particular renderpass/blit. - + + + - + - + - + - + + + + + + + + + + + + + + - + @@ -1534,7 +1547,7 @@ by a particular renderpass/blit. - + @@ -1543,16 +1556,16 @@ by a particular renderpass/blit. - + - + - + @@ -1572,17 +1585,18 @@ by a particular renderpass/blit. TODO: what about gen2 (a640)? --> - - - - - - - - + + + + + + + + + @@ -1590,49 +1604,49 @@ by a particular renderpass/blit. - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -1640,8 +1654,8 @@ by a particular renderpass/blit. - - + + @@ -1667,19 +1681,19 @@ by a particular renderpass/blit. - - - + + + - - - + + + - + @@ -1738,10 +1752,10 @@ by a particular renderpass/blit. - + - + @@ -1778,7 +1792,7 @@ by a particular renderpass/blit. - + @@ -1795,7 +1809,7 @@ by a particular renderpass/blit. - + @@ -1808,16 +1822,16 @@ by a particular renderpass/blit. - - + + - - + + @@ -1835,15 +1849,15 @@ by a particular renderpass/blit. - - - + + + - - - - - + + + + + @@ -1860,30 +1874,53 @@ by a particular renderpass/blit. - + - + - + - + - - + + + + + + + + + + + + + + + - - + + + + + + + + + + + + @@ -1893,45 +1930,59 @@ by a particular renderpass/blit. - + + + + + + + + + - - + + + + + - - + + + + + - - + + - - + + - - + + @@ -1966,24 +2017,23 @@ by a particular renderpass/blit. - + - - + + - + The total size of the LRZ image array (not including fast clear buffer), used as a stride for double buffering used with concurrent binning. - - - - + + + @@ -1995,28 +2045,28 @@ by a particular renderpass/blit. - - + + - - + + - - + + - + - - + + - + - - + + - + @@ -2082,8 +2132,8 @@ by a particular renderpass/blit. - - + + LUT used to convert quality buffer values to HW shading rate values. An array of 4-bit values. @@ -2128,32 +2178,32 @@ by a particular renderpass/blit. - + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - + + + + + + + + + @@ -2180,10 +2230,10 @@ by a particular renderpass/blit. --> - - + + - + @@ -2197,7 +2247,7 @@ by a particular renderpass/blit. - + @@ -2206,26 +2256,26 @@ by a particular renderpass/blit. - + - + - - - - - + + + + + - + @@ -2236,7 +2286,7 @@ by a particular renderpass/blit. - + @@ -2248,16 +2298,16 @@ by a particular renderpass/blit. - + - + - + @@ -2277,7 +2327,7 @@ by a particular renderpass/blit. - + @@ -2289,11 +2339,11 @@ by a particular renderpass/blit. - + - + @@ -2316,7 +2366,7 @@ by a particular renderpass/blit. - + @@ -2330,12 +2380,12 @@ by a particular renderpass/blit. - + - + @@ -2383,16 +2433,16 @@ by a particular renderpass/blit. - - - - + + + + - + @@ -2401,12 +2451,12 @@ by a particular renderpass/blit. - + - + - + @@ -2422,23 +2472,23 @@ by a particular renderpass/blit. - + - + - - - - + + + + - - + + - + - + - + @@ -2498,41 +2548,41 @@ by a particular renderpass/blit. - - + + - + - + - + - - + + - + - - - + + + - - + + - + - + @@ -2541,23 +2591,23 @@ by a particular renderpass/blit. - - + + - - + + - + - - - - + + + + @@ -2567,7 +2617,7 @@ by a particular renderpass/blit. - + @@ -2595,7 +2645,7 @@ by a particular renderpass/blit. - + @@ -2637,17 +2687,17 @@ by a particular renderpass/blit. - + - + - + @@ -2656,16 +2706,25 @@ by a particular renderpass/blit. + + + + + + + + + - - + + - + @@ -2683,7 +2742,8 @@ by a particular renderpass/blit. - + + @@ -2693,13 +2753,13 @@ by a particular renderpass/blit. These show up in a6xx gen3+ but so far haven't found an example of blob writing non-zero: --> - - - - + + + + - - + + @@ -2731,28 +2791,28 @@ by a particular renderpass/blit. - - - + + + - - - + + + - - + + - - + + - - - - + + + + - + @@ -2821,7 +2881,7 @@ by a particular renderpass/blit. - + @@ -2847,7 +2907,7 @@ by a particular renderpass/blit. - + @@ -2860,17 +2920,17 @@ by a particular renderpass/blit. - - - + + + - - - + + + - - - + + + @@ -2878,17 +2938,17 @@ by a particular renderpass/blit. - - - + + + - - - + + + - - - + + + @@ -2897,19 +2957,19 @@ by a particular renderpass/blit. - - - - + + + + - + - - + + @@ -2949,14 +3009,14 @@ by a particular renderpass/blit. - - - - - - - - + + + + + + + + @@ -2973,20 +3033,20 @@ by a particular renderpass/blit. - + Packed array of a6xx_varying_interp_mode - + Packed array of a6xx_varying_ps_repl_mode - + Packed array of a6xx_varying_interp_mode - + Packed array of a6xx_varying_ps_repl_mode @@ -2995,12 +3055,12 @@ by a particular renderpass/blit. - + - + @@ -3034,8 +3094,8 @@ by a particular renderpass/blit. - - + + @@ -3047,8 +3107,8 @@ by a particular renderpass/blit. - - + + @@ -3097,13 +3157,13 @@ by a particular renderpass/blit. - - - + + + - - - + + + @@ -3124,8 +3184,8 @@ by a particular renderpass/blit. - - + + - - + + @@ -3253,7 +3313,7 @@ by a particular renderpass/blit. - + @@ -3287,9 +3347,12 @@ by a particular renderpass/blit. - - - + + + + + + - - + + @@ -3324,31 +3387,31 @@ by a particular renderpass/blit. - - - - + + + + - - - - + + + + - - + + - + size in vec4s of per-primitive storage for gs. TODO: not actually in VPC - - + + - - + + @@ -3426,18 +3489,18 @@ by a particular renderpass/blit. - + - + - + This is the ID of the current patch within the @@ -3450,20 +3513,20 @@ by a particular renderpass/blit. - + - - + + - + - + - + @@ -3474,7 +3537,7 @@ by a particular renderpass/blit. - + @@ -3482,14 +3545,14 @@ by a particular renderpass/blit. - - - + + + - + @@ -3502,14 +3565,14 @@ by a particular renderpass/blit. - + - + @@ -3549,10 +3612,12 @@ by a particular renderpass/blit. --> - - - + + + + + @@ -3580,7 +3645,7 @@ by a particular renderpass/blit. - + - - + + @@ -3623,7 +3688,7 @@ by a particular renderpass/blit. an extra varying after, but with a lower OUTLOC position. If present, psize is last, preceded by position. --> - + @@ -3712,20 +3777,20 @@ by a particular renderpass/blit. - - - - - - - - - + + + + + + + + + - + @@ -3735,32 +3800,32 @@ by a particular renderpass/blit. the maximum size of local storage should be: 64 (wavesize) * 64 (SP_HS_CNTL_1) * 4 = 16k --> - - + + - - - - - - - - - + + + + + + + + + - + - - + + @@ -3768,7 +3833,7 @@ by a particular renderpass/blit. - + @@ -3778,24 +3843,24 @@ by a particular renderpass/blit. - - - - - - - - - + + + + + + + + + - + - + Normally the size of the output of the last stage in dwords. It should be programmed as follows: @@ -3809,14 +3874,14 @@ by a particular renderpass/blit. doesn't matter in practice. - + - + - + @@ -3825,7 +3890,7 @@ by a particular renderpass/blit. - + @@ -3835,15 +3900,15 @@ by a particular renderpass/blit. - - - - - - - - - + + + + + + + + + @@ -3859,7 +3924,7 @@ by a particular renderpass/blit. - + @@ -3878,16 +3943,15 @@ by a particular renderpass/blit. and so one pixel's value is always unused. - - - - - - + + + + + @@ -3897,12 +3961,12 @@ by a particular renderpass/blit. - - + + - + @@ -3913,7 +3977,7 @@ by a particular renderpass/blit. - + @@ -3923,17 +3987,17 @@ by a particular renderpass/blit. - + - + - + per MRT @@ -3941,7 +4005,7 @@ by a particular renderpass/blit. - + @@ -3950,7 +4014,7 @@ by a particular renderpass/blit. - + @@ -3967,7 +4031,7 @@ by a particular renderpass/blit. - + @@ -3981,7 +4045,7 @@ by a particular renderpass/blit. - + @@ -3993,21 +4057,21 @@ by a particular renderpass/blit. - + - + - + - + @@ -4025,7 +4089,7 @@ by a particular renderpass/blit. - + If 0 - all 32k of shared storage is enabled, otherwise @@ -4045,29 +4109,29 @@ by a particular renderpass/blit. - - - - - - - - - - - - + + + + + + + + + + + + - + - + @@ -4104,10 +4168,10 @@ by a particular renderpass/blit. - - - - + + + + @@ -4118,13 +4182,13 @@ by a particular renderpass/blit. - + - + @@ -4134,9 +4198,9 @@ by a particular renderpass/blit. - - - + + + @@ -4192,7 +4256,7 @@ by a particular renderpass/blit. - + - + - + - - + + - + - + - + @@ -4259,8 +4323,8 @@ by a particular renderpass/blit. - - + + @@ -4268,7 +4332,14 @@ by a particular renderpass/blit. - + + + When this bit is enabled, new waves may be unlocked once + all invocations have signaled they don't need local + memory anymore using (eolm)nop. + + + @@ -4333,7 +4404,7 @@ by a particular renderpass/blit. - + - - - - + + + + - + + - - + @@ -4387,6 +4458,11 @@ by a particular renderpass/blit. + + + + + @@ -4395,24 +4471,26 @@ by a particular renderpass/blit. badly named or the functionality moved in a6xx. But downstream kernel calls this "a6xx_sp_ps_tp_2d_cluster" --> - - + + - - + + - - + + - - + + + + - - - + + + - - - + + + - - + + - - + + - - - - - + + + + + - - - - - - + + + + + + - + + + - - + + + - - + + + - + + + - + - - - - - - + + + + + + - + - + - - - - + + + + - + - + - + - + - + - + - + - + - - - + + + - + - + - + - + - + - + - + - - - + + + @@ -4690,7 +4774,7 @@ by a particular renderpass/blit. - + @@ -4711,7 +4795,7 @@ by a particular renderpass/blit. - + @@ -4723,7 +4807,7 @@ by a particular renderpass/blit. - + @@ -4731,7 +4815,7 @@ by a particular renderpass/blit. - + @@ -4750,7 +4834,7 @@ by a particular renderpass/blit. - + This register clears pending loads queued up by CP_LOAD_STATE6. Each bit resets a particular kind(s) of @@ -4790,7 +4874,7 @@ by a particular renderpass/blit. - + This register clears pending loads queued up by CP_LOAD_STATE6. Each bit resets a particular kind(s) of @@ -4813,7 +4897,7 @@ by a particular renderpass/blit. - + This register clears pending loads queued up by CP_LOAD_STATE6. Each bit resets a particular kind(s) of @@ -4829,16 +4913,16 @@ by a particular renderpass/blit. - - + + - - + + - - + + - + Shared constants are intended to be used for Vulkan push constants. When enabled, 8 vec4's are reserved in the FS diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx_descriptors.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx_descriptors.xml index 56cfaff614a4..08bc37f29a6f 100644 --- a/drivers/gpu/drm/msm/registers/adreno/a6xx_descriptors.xml +++ b/drivers/gpu/drm/msm/registers/adreno/a6xx_descriptors.xml @@ -45,19 +45,21 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> - + Texture constant dwords - - - - - + + + + + + - - + + + probably for D3D structured UAVs, normally set to 1 - - + + - + Pitch in bytes (so actually stride) - + @@ -94,15 +97,15 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> layer size at the point that it stops being reduced moving to higher (smaller) mipmap levels --> - - + + - - + + - + - + + + + + - + + - + + + + - + + - + - - + + + + + diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx_enums.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx_enums.xml index 81538831dc19..b44946f36fae 100644 --- a/drivers/gpu/drm/msm/registers/adreno/a6xx_enums.xml +++ b/drivers/gpu/drm/msm/registers/adreno/a6xx_enums.xml @@ -328,6 +328,9 @@ to upconvert to 32b float internally? + + + diff --git a/drivers/gpu/drm/msm/registers/adreno/a8xx_descriptors.xml b/drivers/gpu/drm/msm/registers/adreno/a8xx_descriptors.xml index edcbdb3b6921..d119d021446c 100644 --- a/drivers/gpu/drm/msm/registers/adreno/a8xx_descriptors.xml +++ b/drivers/gpu/drm/msm/registers/adreno/a8xx_descriptors.xml @@ -39,76 +39,92 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> - + Texture memobj dwords - + + for type TEX_BUFFER + - + + - + + for type TEX_BUFFER, probably for D3D structured UAVs, normally set to 1 + - - - + + + + - - - - + + + + - - - - + + + + - + - + - + - - - - + + + + + + + + + + - - - + + + - - - - - - + + + + + + - - + + + + + + - + - - + + - + - + - - + + diff --git a/drivers/gpu/drm/msm/registers/adreno/a8xx_perfcntrs.xml b/drivers/gpu/drm/msm/registers/adreno/a8xx_perfcntrs.xml new file mode 100644 index 000000000000..a5bb44f76956 --- /dev/null +++ b/drivers/gpu/drm/msm/registers/adreno/a8xx_perfcntrs.xml @@ -0,0 +1,1929 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/gpu/drm/msm/registers/adreno/adreno_common.xml b/drivers/gpu/drm/msm/registers/adreno/adreno_common.xml index 79d204f1e400..195cee078357 100644 --- a/drivers/gpu/drm/msm/registers/adreno/adreno_common.xml +++ b/drivers/gpu/drm/msm/registers/adreno/adreno_common.xml @@ -14,6 +14,27 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> + + + + TEX_MEMOBJ descriptor types. These are used + to mark fields that only apply to certain + descriptor types, and potentially overlap + with fields in other types. + + + + + + + Additional descriptor types not part of + TEX_MEMOBJ. These are described by their + own toplevel domain. + + + + + @@ -409,4 +430,25 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> + + + + + + + + + + + + + + + + diff --git a/drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml b/drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml index 51e9c94f5e37..f185b541aa70 100644 --- a/drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml +++ b/drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml @@ -152,6 +152,8 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> + + @@ -1095,7 +1097,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords) - + @@ -1275,8 +1277,15 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords) - + + @@ -1469,6 +1478,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords) + @@ -1476,7 +1486,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords) - + @@ -2055,28 +2065,20 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords) - Executes the following DWORDs of commands if the dword at ADDR0 - is not equal to 0 and the dword at ADDR1 is less than REF - (signed comparison). + Executes the following DWORDs of commands if the dword + at BOOL_ADDR is not equal to 0 and the the timestamp + value ACTIVE_TIMESTAMP is ahead of the value fetched + from TIMESTAMP_ADDR. + + The timestamp comparision is an unsigned compare with + wraparound, ie: + + (ACTIVE_TIMESTAMP - *TIMESTAMP_ADDR) < 0x80000000 - - - - - - - - - - - - - - - - - - + + + + From 510fad8aa449901044f9110d2d9c29aac30ea54b Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:38 -0700 Subject: [PATCH 49/67] drm/msm/registers: Sync gen_header.py from mesa Update gen_header.py to bring in support for generating perfcntr tables. Sync from mesa commit 96c5179c02d1 ("freedreno/registers: Skip deprecated warns for kernel") Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728203/ Message-ID: <20260526145137.160554-5-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/registers/gen_header.py | 1857 +++++++++++-------- 1 file changed, 1035 insertions(+), 822 deletions(-) diff --git a/drivers/gpu/drm/msm/registers/gen_header.py b/drivers/gpu/drm/msm/registers/gen_header.py index 2acad951f1e2..d3b56a9d84fb 100644 --- a/drivers/gpu/drm/msm/registers/gen_header.py +++ b/drivers/gpu/drm/msm/registers/gen_header.py @@ -11,997 +11,1210 @@ import collections import argparse import time import datetime +import json + class Error(Exception): - def __init__(self, message): - self.message = message + def __init__(self, message): + self.message = message + class Enum(object): - def __init__(self, name): - self.name = name - self.values = [] + def __init__(self, name): + self.name = name + self.values = [] - def has_name(self, name): - for (n, value) in self.values: - if n == name: - return True - return False + def has_name(self, name): + for (n, value) in self.values: + if n == name: + return True + return False - def names(self): - return [n for (n, value) in self.values] + def names(self): + return [n for (n, value) in self.values] - def dump(self, is_deprecated): - use_hex = False - for (name, value) in self.values: - if value > 0x1000: - use_hex = True + def value(self, name): + for (n, v) in self.values: + if n == name: + return v - print("enum %s {" % self.name) - for (name, value) in self.values: - if use_hex: - print("\t%s = 0x%08x," % (name, value)) - else: - print("\t%s = %d," % (name, value)) - print("};\n") + def dump(self, has_variants): + use_hex = False + for (name, value) in self.values: + if value > 0x1000: + use_hex = True + + print("enum %s {" % self.name) + for (name, value) in self.values: + if use_hex: + print("\t%s = 0x%08x," % (name, value)) + else: + print("\t%s = %d," % (name, value)) + print("};\n") + + def dump_pack_struct(self, has_variants): + pass - def dump_pack_struct(self, is_deprecated): - pass class Field(object): - def __init__(self, name, low, high, shr, type, parser): - self.name = name - self.low = low - self.high = high - self.shr = shr - self.type = type + def __init__(self, name, low, high, shr, type, parser): + self.name = name + self.low = low + self.high = high + self.shr = shr + self.type = type - builtin_types = [ None, "a3xx_regid", "boolean", "uint", "hex", "int", "fixed", "ufixed", "float", "address", "waddress" ] + builtin_types = [None, "a3xx_regid", "boolean", "uint", "hex", + "int", "fixed", "ufixed", "float", "address", "waddress"] - maxpos = parser.current_bitsize - 1 + maxpos = parser.current_bitsize - 1 - if low < 0 or low > maxpos: - raise parser.error("low attribute out of range: %d" % low) - if high < 0 or high > maxpos: - raise parser.error("high attribute out of range: %d" % high) - if high < low: - raise parser.error("low is greater than high: low=%d, high=%d" % (low, high)) - if self.type == "boolean" and not low == high: - raise parser.error("booleans should be 1 bit fields") - elif self.type == "float" and not (high - low == 31 or high - low == 15): - raise parser.error("floats should be 16 or 32 bit fields") - elif self.type not in builtin_types and self.type not in parser.enums: - raise parser.error("unknown type '%s'" % self.type) + if low < 0 or low > maxpos: + raise parser.error("low attribute out of range: %d" % low) + if high < 0 or high > maxpos: + raise parser.error("high attribute out of range: %d" % high) + if high < low: + raise parser.error( + "low is greater than high: low=%d, high=%d" % (low, high)) + if self.type == "boolean" and not low == high: + raise parser.error("booleans should be 1 bit fields") + elif self.type == "float" and not (high - low == 31 or high - low == 15): + raise parser.error("floats should be 16 or 32 bit fields") + elif self.type not in builtin_types and self.type not in parser.enums: + raise parser.error("unknown type '%s'" % self.type) - def ctype(self, var_name): - if self.type is None: - type = "uint32_t" - val = var_name - elif self.type == "boolean": - type = "bool" - val = var_name - elif self.type == "uint" or self.type == "hex" or self.type == "a3xx_regid": - type = "uint32_t" - val = var_name - elif self.type == "int": - type = "int32_t" - val = var_name - elif self.type == "fixed": - type = "float" - val = "((int32_t)(%s * %d.0))" % (var_name, 1 << self.radix) - elif self.type == "ufixed": - type = "float" - val = "((uint32_t)(%s * %d.0))" % (var_name, 1 << self.radix) - elif self.type == "float" and self.high - self.low == 31: - type = "float" - val = "fui(%s)" % var_name - elif self.type == "float" and self.high - self.low == 15: - type = "float" - val = "_mesa_float_to_half(%s)" % var_name - elif self.type in [ "address", "waddress" ]: - type = "uint64_t" - val = var_name - else: - type = "enum %s" % self.type - val = var_name + def ctype(self, var_name): + if self.type is None: + type = "uint32_t" + val = var_name + elif self.type == "boolean": + type = "bool" + val = var_name + elif self.type == "uint" or self.type == "hex" or self.type == "a3xx_regid": + type = "uint32_t" + val = var_name + elif self.type == "int": + type = "int32_t" + val = var_name + elif self.type == "fixed": + type = "float" + val = "(uint32_t)((int32_t)(%s * %d.0))" % (var_name, 1 << self.radix) + elif self.type == "ufixed": + type = "float" + val = "((uint32_t)(%s * %d.0))" % (var_name, 1 << self.radix) + elif self.type == "float" and self.high - self.low == 31: + type = "float" + val = "fui(%s)" % var_name + elif self.type == "float" and self.high - self.low == 15: + type = "float" + val = "_mesa_float_to_half(%s)" % var_name + elif self.type in ["address", "waddress"]: + type = "uint64_t" + val = var_name + else: + type = "enum %s" % self.type + val = var_name - if self.shr > 0: - val = "(%s >> %d)" % (val, self.shr) + if self.shr > 0: + val = "(%s >> %d)" % (val, self.shr) + + return (type, val) - return (type, val) def tab_to(name, value): - tab_count = (68 - (len(name) & ~7)) // 8 - if tab_count <= 0: - tab_count = 1 - print(name + ('\t' * tab_count) + value) + tab_count = (68 - (len(name) & ~7)) // 8 + if tab_count <= 0: + tab_count = 1 + print(name + ('\t' * tab_count) + value) + +def define_macro(name, value, has_variants): + if has_variants: + value = "__FD_DEPRECATED " + value + tab_to(name, value) def mask(low, high): - return ((0xffffffffffffffff >> (64 - (high + 1 - low))) << low) + return ((0xffffffffffffffff >> (64 - (high + 1 - low))) << low) + def field_name(reg, f): - if f.name: - name = f.name.lower() - else: - # We hit this path when a reg is defined with no bitset fields, ie. - # - name = reg.name.lower() + if f.name: + name = f.name.lower() + else: + # We hit this path when a reg is defined with no bitset fields, ie. + # + name = reg.name.lower() - if (name in [ "double", "float", "int" ]) or not (name[0].isalpha()): - name = "_" + name + if (name in ["double", "float", "int"]) or not (name[0].isalpha()): + name = "_" + name - return name + return name # indices - array of (ctype, stride, __offsets_NAME) + + def indices_varlist(indices): - return ", ".join(["i%d" % i for i in range(len(indices))]) + return ", ".join(["i%d" % i for i in range(len(indices))]) + def indices_prototype(indices): - return ", ".join(["%s i%d" % (ctype, idx) - for (idx, (ctype, stride, offset)) in enumerate(indices)]) + return ", ".join(["%s i%d" % (ctype, idx) + for (idx, (ctype, stride, offset)) in enumerate(indices)]) + def indices_strides(indices): - return " + ".join(["0x%x*i%d" % (stride, idx) - if stride else - "%s(i%d)" % (offset, idx) - for (idx, (ctype, stride, offset)) in enumerate(indices)]) + return " + ".join(["0x%x*i%d" % (stride, idx) + if stride else + "%s(i%d)" % (offset, idx) + for (idx, (ctype, stride, offset)) in enumerate(indices)]) + def is_number(str): - try: - int(str) - return True - except ValueError: - return False + try: + int(str) + return True + except ValueError: + return False + def sanitize_variant(variant): - if variant and "-" in variant: - return variant[:variant.index("-")] - return variant + if variant and "-" in variant: + return variant[:variant.index("-")] + return variant + class Bitset(object): - def __init__(self, name, template): - self.name = name - self.inline = False - self.reg = None - if template: - self.fields = template.fields[:] - else: - self.fields = [] + def __init__(self, name, template): + self.name = name + self.inline = False + self.reg = None + if template: + self.fields = template.fields[:] + else: + self.fields = [] - # Get address field if there is one in the bitset, else return None: - def get_address_field(self): - for f in self.fields: - if f.type in [ "address", "waddress" ]: - return f - return None + # Get address field if there is one in the bitset, else return None: + def get_address_field(self): + for f in self.fields: + if f.type in ["address", "waddress"]: + return f + return None - def dump_regpair_builder(self, reg): - print("#ifndef NDEBUG") - known_mask = 0 - for f in self.fields: - known_mask |= mask(f.low, f.high) - if f.type in [ "boolean", "address", "waddress" ]: - continue - type, val = f.ctype("fields.%s" % field_name(reg, f)) - print(" assert((%-40s & 0x%08x) == 0);" % (val, 0xffffffff ^ mask(0 , f.high - f.low))) - print(" assert((%-40s & 0x%08x) == 0);" % ("fields.unknown", known_mask)) - print("#endif\n") + def dump_regpair_builder(self, reg): + print("#ifndef NDEBUG") + known_mask = 0 + for f in self.fields: + known_mask |= mask(f.low, f.high) + if f.type in ["boolean", "address", "waddress"]: + continue + type, val = f.ctype("fields.%s" % field_name(reg, f)) + print(" assert((%-40s & 0x%08x) == 0);" % + (val, 0xffffffff ^ mask(0, f.high - f.low))) + print(" assert((%-40s & 0x%08x) == 0);" % + ("fields.unknown", known_mask)) + print("#endif\n") - print(" return (struct fd_reg_pair) {") - print(" .reg = (uint32_t)%s," % reg.reg_offset()) - print(" .value =") - cast = "(uint64_t)" if reg.bit_size == 64 else "" - for f in self.fields: - if f.type in [ "address", "waddress" ]: - continue - else: - type, val = f.ctype("fields.%s" % field_name(reg, f)) - print(" (%s%-40s << %2d) |" % (cast, val, f.low)) - value_name = "dword" - if reg.bit_size == 64: - value_name = "qword" - print(" fields.unknown | fields.%s," % (value_name,)) + print(" return (struct fd_reg_pair) {") + print(" .reg = (uint32_t)%s," % reg.reg_offset()) + print(" .value =") + cast = "(uint64_t)" if reg.bit_size == 64 else "" + for f in self.fields: + if f.type in ["address", "waddress"]: + continue + else: + type, val = f.ctype("fields.%s" % field_name(reg, f)) + print(" (%s%-40s << %2d) |" % (cast, val, f.low)) + value_name = "dword" + if reg.bit_size == 64: + value_name = "qword" + print(" fields.unknown | fields.%s," % (value_name,)) - address = self.get_address_field() - if address: - print(" .bo = fields.bo,") - print(" .is_address = true,") - if f.type == "waddress": - print(" .bo_write = true,") - print(" .bo_offset = fields.bo_offset,") - print(" .bo_shift = %d," % address.shr) - print(" .bo_low = %d," % address.low) + address = self.get_address_field() + if address: + print("#ifndef TU_CS_H") + print(" .bo = fields.bo,") + print(" .is_address = true,") + print(" .bo_offset = fields.bo_offset,") + print(" .bo_shift = %d," % address.shr) + print(" .bo_low = %d," % address.low) + print("#else") + print(" .is_address = true,") + print("#endif") - print(" };") + print(" };") - def dump_pack_struct(self, is_deprecated, reg=None): - if not reg: - return + def dump_pack_struct(self, has_variants, reg=None): + if not reg: + return - prefix = reg.full_name + prefix = reg.full_name - print("struct %s {" % prefix) - for f in self.fields: - if f.type in [ "address", "waddress" ]: - tab_to(" __bo_type", "bo;") - tab_to(" uint32_t", "bo_offset;") - continue - name = field_name(reg, f) + constexpr_mark = " CONSTEXPR" - type, val = f.ctype("var") + print("struct %s {" % prefix) + for f in self.fields: + if f.type in ["address", "waddress"]: + print("#ifndef TU_CS_H") + tab_to(" __bo_type", "bo;") + tab_to(" uint32_t", "bo_offset;") + print("#endif\n") + continue + name = field_name(reg, f) - tab_to(" %s" % type, "%s;" % name) - if reg.bit_size == 64: - tab_to(" uint64_t", "unknown;") - tab_to(" uint64_t", "qword;") - else: - tab_to(" uint32_t", "unknown;") - tab_to(" uint32_t", "dword;") - print("};\n") + type, val = f.ctype("var") - depcrstr = "" - if is_deprecated: - depcrstr = " FD_DEPRECATED" - if reg.array: - print("static inline%s struct fd_reg_pair\npack_%s(uint32_t __i, struct %s fields)\n{" % - (depcrstr, prefix, prefix)) - else: - print("static inline%s struct fd_reg_pair\npack_%s(struct %s fields)\n{" % - (depcrstr, prefix, prefix)) + tab_to(" %s" % type, "%s;" % name) - self.dump_regpair_builder(reg) + if f.type == "float": + # Requires using `fui()` or `_mesa_float_to_half()` + constexpr_mark = "" + if reg.bit_size == 64: + tab_to(" uint64_t", "qword;") + tab_to(" uint64_t", "unknown;") + else: + tab_to(" uint32_t", "dword;") + tab_to(" uint32_t", "unknown;") + print("};\n") - print("\n}\n") + if not has_variants: + print("static%s inline struct fd_reg_pair" % constexpr_mark) + if reg.array: + print("pack_%s(uint32_t __i, struct %s fields)\n{" % (prefix, prefix)) + else: + print("pack_%s(struct %s fields)\n{" % (prefix, prefix)) - if self.get_address_field(): - skip = ", { .reg = 0 }" - else: - skip = "" + self.dump_regpair_builder(reg) - if reg.array: - print("#define %s(__i, ...) pack_%s(__i, __struct_cast(%s) { __VA_ARGS__ })%s\n" % - (prefix, prefix, prefix, skip)) - else: - print("#define %s(...) pack_%s(__struct_cast(%s) { __VA_ARGS__ })%s\n" % - (prefix, prefix, prefix, skip)) + print("\n}\n") + if self.get_address_field(): + skip = ", { .reg = 0 }" + else: + skip = "" - def dump(self, is_deprecated, prefix=None, reg=None): - if prefix is None: - prefix = self.name - reg64 = reg and self.reg and self.reg.bit_size == 64 - if reg64: - print("static inline uint32_t %s_LO(uint32_t val)\n{" % prefix) - print("\treturn val;\n}") - print("static inline uint32_t %s_HI(uint32_t val)\n{" % prefix) - print("\treturn val;\n}") - for f in self.fields: - if f.name: - name = prefix + "_" + f.name - else: - name = prefix + if reg.array: + print("#define %s(__i, ...) pack_%s(__i, __struct_cast(%s) { __VA_ARGS__ })%s\n" % + (prefix, prefix, prefix, skip)) + else: + print("#define %s(...) pack_%s(__struct_cast(%s) { __VA_ARGS__ })%s\n" % + (prefix, prefix, prefix, skip)) - if not f.name and f.low == 0 and f.shr == 0 and f.type not in ["float", "fixed", "ufixed"]: - pass - elif f.type == "boolean" or (f.type is None and f.low == f.high): - tab_to("#define %s" % name, "0x%08x" % (1 << f.low)) - else: - typespec = "ull" if reg64 else "u" - tab_to("#define %s__MASK" % name, "0x%08x%s" % (mask(f.low, f.high), typespec)) - tab_to("#define %s__SHIFT" % name, "%d" % f.low) - type, val = f.ctype("val") - ret_type = "uint64_t" if reg64 else "uint32_t" - cast = "(uint64_t)" if reg64 else "" + def dump(self, has_variants, prefix=None, reg=None): + if prefix is None: + prefix = self.name + suffix = "" + if self.reg and self.reg.bit_size == 64: + print( + "static CONSTEXPR inline uint32_t %s_LO(uint32_t val)\n{" % prefix) + print("\treturn val;\n}") + print( + "static CONSTEXPR inline uint32_t %s_HI(uint32_t val)\n{" % prefix) + print("\treturn val;\n}") + suffix = "ull" + + for f in self.fields: + if f.name: + name = prefix + "_" + f.name + else: + name = prefix + + if not f.name and f.low == 0 and f.shr == 0 and f.type not in ["float", "fixed", "ufixed"]: + pass + elif f.type == "boolean" or (f.type is None and f.low == f.high): + tab_to("#define %s" % name, "0x%08x%s" % ((1 << f.low), suffix)) + else: + tab_to("#define %s__MASK" % + name, "0x%08x%s" % (mask(f.low, f.high), suffix)) + tab_to("#define %s__SHIFT" % name, "%d" % f.low) + type, val = f.ctype("val") + ret_type = "uint64_t" if reg and reg.bit_size == 64 else "uint32_t" + cast = "(uint64_t)" if reg and reg.bit_size == 64 else "" + + constexpr_mark = "" if type == "float" else " CONSTEXPR" + print("static%s inline %s %s(%s val)\n{" % ( + constexpr_mark, ret_type, name, type)) + if f.shr > 0: + print("\tassert(!(val & 0x%x));" % mask(0, f.shr - 1)) + print("\treturn (%s(%s) << %s__SHIFT) & %s__MASK;\n}" % + (cast, val, name, name)) + print() - print("static inline %s %s(%s val)\n{" % (ret_type, name, type)) - if f.shr > 0: - print("\tassert(!(val & 0x%x));" % mask(0, f.shr - 1)) - print("\treturn (%s(%s) << %s__SHIFT) & %s__MASK;\n}" % (cast, val, name, name)) - print() class Array(object): - def __init__(self, attrs, domain, variant, parent, index_type): - if "name" in attrs: - self.local_name = attrs["name"] - else: - self.local_name = "" - self.domain = domain - self.variant = variant - self.parent = parent - self.children = [] - if self.parent: - self.name = self.parent.name + "_" + self.local_name - else: - self.name = self.local_name - if "offsets" in attrs: - self.offsets = map(lambda i: "0x%08x" % int(i, 0), attrs["offsets"].split(",")) - self.fixed_offsets = True - elif "doffsets" in attrs: - self.offsets = map(lambda s: "(%s)" % s , attrs["doffsets"].split(",")) - self.fixed_offsets = True - else: - self.offset = int(attrs["offset"], 0) - self.stride = int(attrs["stride"], 0) - self.fixed_offsets = False - if "index" in attrs: - self.index_type = index_type - else: - self.index_type = None - self.length = int(attrs["length"], 0) - if "usage" in attrs: - self.usages = attrs["usage"].split(',') - else: - self.usages = None + def __init__(self, attrs, domain, variant, parent, index_type): + if "name" in attrs: + self.local_name = attrs["name"] + else: + self.local_name = "" + self.domain = domain + self.variant = variant + self.parent = parent + self.children = [] + if self.parent: + self.name = self.parent.name + "_" + self.local_name + else: + self.name = self.local_name + if "offsets" in attrs: + self.offsets = map(lambda i: "0x%08x" % + int(i, 0), attrs["offsets"].split(",")) + self.fixed_offsets = True + elif "doffsets" in attrs: + self.offsets = map(lambda s: "(%s)" % + s, attrs["doffsets"].split(",")) + self.fixed_offsets = True + else: + self.offset = int(attrs["offset"], 0) + self.stride = int(attrs["stride"], 0) + self.fixed_offsets = False + if "index" in attrs: + self.index_type = index_type + else: + self.index_type = None + self.length = int(attrs["length"], 0) + if "usage" in attrs: + self.usages = attrs["usage"].split(',') + else: + self.usages = None - def index_ctype(self): - if not self.index_type: - return "uint32_t" - else: - return "enum %s" % self.index_type.name + def index_ctype(self): + if not self.index_type: + return "uint32_t" + else: + return "enum %s" % self.index_type.name - # Generate array of (ctype, stride, __offsets_NAME) - def indices(self): - if self.parent: - indices = self.parent.indices() - else: - indices = [] - if self.length != 1: - if self.fixed_offsets: - indices.append((self.index_ctype(), None, "__offset_%s" % self.local_name)) - else: - indices.append((self.index_ctype(), self.stride, None)) - return indices + # Generate array of (ctype, stride, __offsets_NAME) + def indices(self): + if self.parent: + indices = self.parent.indices() + else: + indices = [] + if self.length != 1: + if self.fixed_offsets: + indices.append((self.index_ctype(), None, + "__offset_%s" % self.local_name)) + else: + indices.append((self.index_ctype(), self.stride, None)) + return indices - def total_offset(self): - offset = 0 - if not self.fixed_offsets: - offset += self.offset - if self.parent: - offset += self.parent.total_offset() - return offset + def total_offset(self): + offset = 0 + if not self.fixed_offsets: + offset += self.offset + if self.parent: + offset += self.parent.total_offset() + return offset - def dump(self, is_deprecated): - depcrstr = "" - if is_deprecated: - depcrstr = " FD_DEPRECATED" - proto = indices_varlist(self.indices()) - strides = indices_strides(self.indices()) - array_offset = self.total_offset() - if self.fixed_offsets: - print("static inline%s uint32_t __offset_%s(%s idx)" % (depcrstr, self.local_name, self.index_ctype())) - print("{\n\tswitch (idx) {") - if self.index_type: - for val, offset in zip(self.index_type.names(), self.offsets): - print("\t\tcase %s: return %s;" % (val, offset)) - else: - for idx, offset in enumerate(self.offsets): - print("\t\tcase %d: return %s;" % (idx, offset)) - print("\t\tdefault: return INVALID_IDX(idx);") - print("\t}\n}") - if proto == '': - tab_to("#define REG_%s_%s" % (self.domain, self.name), "0x%08x\n" % array_offset) - else: - tab_to("#define REG_%s_%s(%s)" % (self.domain, self.name, proto), "(0x%08x + %s )\n" % (array_offset, strides)) + def dump(self, has_variants): + proto = indices_varlist(self.indices()) + strides = indices_strides(self.indices()) + array_offset = self.total_offset() + if self.fixed_offsets and not has_variants: + print("static CONSTEXPR inline uint32_t __offset_%s(%s idx)" % + (self.local_name, self.index_ctype())) + print("{\n\tswitch (idx) {") + if self.index_type: + for val, offset in zip(self.index_type.names(), self.offsets): + print("\t\tcase %s: return %s;" % (val, offset)) + else: + for idx, offset in enumerate(self.offsets): + print("\t\tcase %d: return %s;" % (idx, offset)) + print("\t\tdefault: return INVALID_IDX(idx);") + print("\t}\n}") + if proto == '': + define_macro("#define REG_%s_%s" % + (self.domain, self.name), "0x%08x\n" % array_offset, + has_variants) + else: + define_macro("#define REG_%s_%s(%s)" % (self.domain, self.name, + proto), "(0x%08x + %s )\n" % (array_offset, strides), + has_variants) - def dump_pack_struct(self, is_deprecated): - pass + def dump_pack_struct(self, has_variants): + pass + + def dump_regpair_builder(self): + pass - def dump_regpair_builder(self): - pass class Reg(object): - def __init__(self, attrs, domain, array, bit_size): - self.name = attrs["name"] - self.domain = domain - self.array = array - self.offset = int(attrs["offset"], 0) - self.type = None - self.bit_size = bit_size - if array: - self.name = array.name + "_" + self.name - array.children.append(self) - self.full_name = self.domain + "_" + self.name - if "stride" in attrs: - self.stride = int(attrs["stride"], 0) - self.length = int(attrs["length"], 0) - else: - self.stride = None - self.length = None + def __init__(self, attrs, domain, array, bit_size): + self.name = attrs["name"] + self.domain = domain + self.array = array + self.offset = int(attrs["offset"], 0) + self.type = None + self.bit_size = bit_size + if array: + self.name = array.name + "_" + self.name + array.children.append(self) + self.full_name = self.domain + "_" + self.name + if "stride" in attrs: + self.stride = int(attrs["stride"], 0) + self.length = int(attrs["length"], 0) + else: + self.stride = None + self.length = None - # Generate array of (ctype, stride, __offsets_NAME) - def indices(self): - if self.array: - indices = self.array.indices() - else: - indices = [] - if self.stride: - indices.append(("uint32_t", self.stride, None)) - return indices + # Generate array of (ctype, stride, __offsets_NAME) + def indices(self): + if self.array: + indices = self.array.indices() + else: + indices = [] + if self.stride: + indices.append(("uint32_t", self.stride, None)) + return indices - def total_offset(self): - if self.array: - return self.array.total_offset() + self.offset - else: - return self.offset + def total_offset(self): + if self.array: + return self.array.total_offset() + self.offset + else: + return self.offset - def reg_offset(self): - if self.array: - offset = self.array.offset + self.offset - return "(0x%08x + 0x%x*__i)" % (offset, self.array.stride) - return "0x%08x" % self.offset + def reg_offset(self): + if self.array: + offset = self.array.offset + self.offset + return "(0x%08x + 0x%x*__i)" % (offset, self.array.stride) + return "0x%08x" % self.offset - def dump(self, is_deprecated): - depcrstr = "" - if is_deprecated: - depcrstr = " FD_DEPRECATED " - proto = indices_prototype(self.indices()) - strides = indices_strides(self.indices()) - offset = self.total_offset() - if proto == '': - tab_to("#define REG_%s" % self.full_name, "0x%08x" % offset) - else: - print("static inline%s uint32_t REG_%s(%s) { return 0x%08x + %s; }" % (depcrstr, self.full_name, proto, offset, strides)) + def dump(self, has_variants): + proto = indices_prototype(self.indices()) + strides = indices_strides(self.indices()) + offset = self.total_offset() + if proto == '': + define_macro("#define REG_%s" % self.full_name, "0x%08x" % offset, has_variants) + elif not has_variants: + depcrstr = "" + if has_variants: + depcrstr = " __FD_DEPRECATED " + print("static CONSTEXPR inline%s uint32_t REG_%s(%s) { return 0x%08x + %s; }" % ( + depcrstr, self.full_name, proto, offset, strides)) - if self.bitset.inline: - self.bitset.dump(is_deprecated, self.full_name, self) - print("") + if self.bitset.inline: + self.bitset.dump(has_variants, self.full_name, self) + print("") - def dump_pack_struct(self, is_deprecated): - if self.bitset.inline: - self.bitset.dump_pack_struct(is_deprecated, self) + def dump_pack_struct(self, has_variants): + if self.bitset.inline: + self.bitset.dump_pack_struct(has_variants, self) - def dump_regpair_builder(self): - self.bitset.dump_regpair_builder(self) + def dump_regpair_builder(self): + self.bitset.dump_regpair_builder(self) - def dump_py(self): - print("\tREG_%s = 0x%08x" % (self.full_name, self.offset)) + def dump_py(self): + offset = self.offset + if self.array: + offset += self.array.offset + print("\tREG_%s = 0x%08x" % (self.full_name, offset)) class Parser(object): - def __init__(self): - self.current_array = None - self.current_domain = None - self.current_prefix = None - self.current_prefix_type = None - self.current_stripe = None - self.current_bitset = None - self.current_bitsize = 32 - # The varset attribute on the domain specifies the enum which - # specifies all possible hw variants: - self.current_varset = None - # Regs that have multiple variants.. we only generated the C++ - # template based struct-packers for these - self.variant_regs = {} - # Information in which contexts regs are used, to be used in - # debug options - self.usage_regs = collections.defaultdict(list) - self.bitsets = {} - self.enums = {} - self.variants = set() - self.file = [] - self.xml_files = [] + def __init__(self): + self.current_array = None + self.current_domain = None + self.current_prefix = None + self.current_prefix_type = None + self.current_stripe = None + self.current_bitset = None + self.current_bitsize = 32 + # The varset attribute on the domain specifies the enum which + # specifies all possible hw variants: + self.current_varset = None + # Regs that have multiple variants.. we only generated the C++ + # template based struct-packers for these + self.variant_regs = {} + # Information in which contexts regs are used, to be used in + # debug options + self.usage_regs = collections.defaultdict(list) + self.bitsets = {} + self.enums = {} + self.variants = set() + self.file = [] + self.xml_files = [] - def error(self, message): - parser, filename = self.stack[-1] - return Error("%s:%d:%d: %s" % (filename, parser.CurrentLineNumber, parser.CurrentColumnNumber, message)) + def error(self, message): + parser, filename = self.stack[-1] + return Error("%s:%d:%d: %s" % (filename, parser.CurrentLineNumber, parser.CurrentColumnNumber, message)) - def prefix(self, variant=None): - if self.current_prefix_type == "variant" and variant: - return sanitize_variant(variant) - elif self.current_stripe: - return self.current_stripe + "_" + self.current_domain - elif self.current_prefix: - return self.current_prefix + "_" + self.current_domain - else: - return self.current_domain + def prefix(self, variant=None): + if self.current_prefix_type == "variant" and variant: + return sanitize_variant(variant) + elif self.current_stripe: + return self.current_stripe + "_" + self.current_domain + elif self.current_prefix: + return self.current_prefix + "_" + self.current_domain + else: + return self.current_domain - def parse_field(self, name, attrs): - try: - if "pos" in attrs: - high = low = int(attrs["pos"], 0) - elif "high" in attrs and "low" in attrs: - high = int(attrs["high"], 0) - low = int(attrs["low"], 0) - else: - low = 0 - high = self.current_bitsize - 1 + def parse_field(self, name, attrs): + try: + if "pos" in attrs: + high = low = int(attrs["pos"], 0) + elif "high" in attrs and "low" in attrs: + high = int(attrs["high"], 0) + low = int(attrs["low"], 0) + else: + low = 0 + high = self.current_bitsize - 1 - if "type" in attrs: - type = attrs["type"] - else: - type = None + if "type" in attrs: + type = attrs["type"] + else: + type = None - if "shr" in attrs: - shr = int(attrs["shr"], 0) - else: - shr = 0 + if "shr" in attrs: + shr = int(attrs["shr"], 0) + else: + shr = 0 - b = Field(name, low, high, shr, type, self) + b = Field(name, low, high, shr, type, self) - if type == "fixed" or type == "ufixed": - b.radix = int(attrs["radix"], 0) + if type == "fixed" or type == "ufixed": + b.radix = int(attrs["radix"], 0) - self.current_bitset.fields.append(b) - except ValueError as e: - raise self.error(e) + self.current_bitset.fields.append(b) + except ValueError as e: + raise self.error(e) - def parse_varset(self, attrs): - # Inherit the varset from the enclosing domain if not overriden: - varset = self.current_varset - if "varset" in attrs: - varset = self.enums[attrs["varset"]] - return varset + def parse_varset(self, attrs): + # Inherit the varset from the enclosing domain if not overriden: + varset = self.current_varset + if "varset" in attrs: + varset = self.enums[attrs["varset"]] + return varset - def parse_variants(self, attrs): - if "variants" not in attrs: - return None + def parse_variants(self, attrs): + if "variants" not in attrs: + return None - variant = attrs["variants"].split(",")[0] - varset = self.parse_varset(attrs) + variant = attrs["variants"].split(",")[0] + varset = self.parse_varset(attrs) - if "-" in variant: - # if we have a range, validate that both the start and end - # of the range are valid enums: - start = variant[:variant.index("-")] - end = variant[variant.index("-") + 1:] - assert varset.has_name(start) - if end != "": - assert varset.has_name(end) - else: - assert varset.has_name(variant) + if "-" in variant: + # if we have a range, validate that both the start and end + # of the range are valid enums: + start = variant[:variant.index("-")] + end = variant[variant.index("-") + 1:] + assert varset.has_name(start) + if end != "": + assert varset.has_name(end) + else: + assert varset.has_name(variant) - return variant + return variant - def add_all_variants(self, reg, attrs, parent_variant): - # TODO this should really handle *all* variants, including dealing - # with open ended ranges (ie. "A2XX,A4XX-") (we have the varset - # enum now to make that possible) - variant = self.parse_variants(attrs) - if not variant: - variant = parent_variant + def add_all_variants(self, reg, attrs, parent_variant): + # TODO this should really handle *all* variants, including dealing + # with open ended ranges (ie. "A2XX,A4XX-") (we have the varset + # enum now to make that possible) + variant = self.parse_variants(attrs) + if not variant: + variant = parent_variant - if reg.name not in self.variant_regs: - self.variant_regs[reg.name] = {} - else: - # All variants must be same size: - v = next(iter(self.variant_regs[reg.name])) - assert self.variant_regs[reg.name][v].bit_size == reg.bit_size + if reg.name not in self.variant_regs: + self.variant_regs[reg.name] = {} + else: + # All variants must be same size: + v = next(iter(self.variant_regs[reg.name])) + assert self.variant_regs[reg.name][v].bit_size == reg.bit_size - self.variant_regs[reg.name][variant] = reg + self.variant_regs[reg.name][variant] = reg - def add_all_usages(self, reg, usages): - if not usages: - return + def add_all_usages(self, reg, usages): + if not usages: + return - for usage in usages: - self.usage_regs[usage].append(reg) + for usage in usages: + self.usage_regs[usage].append(reg) - self.variants.add(reg.domain) + self.variants.add(reg.domain) - def do_validate(self, schemafile): - if not self.validate: - return + def do_validate(self, schemafile): + if not self.validate: + return - try: - from lxml import etree + try: + from lxml import etree - parser, filename = self.stack[-1] - dirname = os.path.dirname(filename) + parser, filename = self.stack[-1] + dirname = os.path.dirname(filename) - # we expect this to look like schema.xsd.. I think - # technically it is supposed to be just a URL, but that doesn't - # quite match up to what we do.. Just skip over everything up to - # and including the first whitespace character: - schemafile = schemafile[schemafile.rindex(" ")+1:] + # we expect this to look like schema.xsd.. I think + # technically it is supposed to be just a URL, but that doesn't + # quite match up to what we do.. Just skip over everything up to + # and including the first whitespace character: + schemafile = schemafile[schemafile.rindex(" ")+1:] - # this is a bit cheezy, but the xml file to validate could be - # in a child director, ie. we don't really know where the schema - # file is, the way the rnn C code does. So if it doesn't exist - # just look one level up - if not os.path.exists(dirname + "/" + schemafile): - schemafile = "../" + schemafile + # this is a bit cheezy, but the xml file to validate could be + # in a child director, ie. we don't really know where the schema + # file is, the way the rnn C code does. So if it doesn't exist + # just look one level up + if not os.path.exists(dirname + "/" + schemafile): + schemafile = "../" + schemafile - if not os.path.exists(dirname + "/" + schemafile): - raise self.error("Cannot find schema for: " + filename) + if not os.path.exists(dirname + "/" + schemafile): + raise self.error("Cannot find schema for: " + filename) - xmlschema_doc = etree.parse(dirname + "/" + schemafile) - xmlschema = etree.XMLSchema(xmlschema_doc) + xmlschema_doc = etree.parse(dirname + "/" + schemafile) + xmlschema = etree.XMLSchema(xmlschema_doc) - xml_doc = etree.parse(filename) - if not xmlschema.validate(xml_doc): - error_str = str(xmlschema.error_log.filter_from_errors()[0]) - raise self.error("Schema validation failed for: " + filename + "\n" + error_str) - except ImportError as e: - print("lxml not found, skipping validation", file=sys.stderr) + xml_doc = etree.parse(filename) + if not xmlschema.validate(xml_doc): + error_str = str(xmlschema.error_log.filter_from_errors()[0]) + raise self.error( + "Schema validation failed for: " + filename + "\n" + error_str) + except ImportError as e: + print("lxml not found, skipping validation", file=sys.stderr) - def do_parse(self, filename): - filepath = os.path.abspath(filename) - if filepath in self.xml_files: - return - self.xml_files.append(filepath) - file = open(filename, "rb") - parser = xml.parsers.expat.ParserCreate() - self.stack.append((parser, filename)) - parser.StartElementHandler = self.start_element - parser.EndElementHandler = self.end_element - parser.CharacterDataHandler = self.character_data - parser.buffer_text = True - parser.ParseFile(file) - self.stack.pop() - file.close() + def do_parse(self, filename): + filepath = os.path.abspath(filename) + if filepath in self.xml_files: + return + self.xml_files.append(filepath) + file = open(filename, "rb") + parser = xml.parsers.expat.ParserCreate() + self.stack.append((parser, filename)) + parser.StartElementHandler = self.start_element + parser.EndElementHandler = self.end_element + parser.CharacterDataHandler = self.character_data + parser.buffer_text = True + parser.ParseFile(file) + self.stack.pop() + file.close() - def parse(self, rnn_path, filename, validate): - self.path = rnn_path - self.stack = [] - self.validate = validate - self.do_parse(filename) + def parse(self, rnn_path, filename, validate): + self.path = rnn_path + self.stack = [] + self.validate = validate + self.do_parse(filename) - def parse_reg(self, attrs, bit_size): - self.current_bitsize = bit_size - if "type" in attrs and attrs["type"] in self.bitsets: - bitset = self.bitsets[attrs["type"]] - if bitset.inline: - self.current_bitset = Bitset(attrs["name"], bitset) - self.current_bitset.inline = True - else: - self.current_bitset = bitset - else: - self.current_bitset = Bitset(attrs["name"], None) - self.current_bitset.inline = True - if "type" in attrs: - self.parse_field(None, attrs) + def parse_reg(self, attrs, bit_size): + self.current_bitsize = bit_size + if "type" in attrs and attrs["type"] in self.bitsets: + bitset = self.bitsets[attrs["type"]] + if bitset.inline: + self.current_bitset = Bitset(attrs["name"], bitset) + self.current_bitset.inline = True + else: + self.current_bitset = bitset + else: + self.current_bitset = Bitset(attrs["name"], None) + self.current_bitset.inline = True + if "type" in attrs: + self.parse_field(None, attrs) - variant = self.parse_variants(attrs) - if not variant and self.current_array: - variant = self.current_array.variant + variant = self.parse_variants(attrs) + if not variant and self.current_array: + variant = self.current_array.variant - self.current_reg = Reg(attrs, self.prefix(variant), self.current_array, bit_size) - self.current_reg.bitset = self.current_bitset - self.current_bitset.reg = self.current_reg + self.current_reg = Reg(attrs, self.prefix( + variant), self.current_array, bit_size) + self.current_reg.bitset = self.current_bitset + self.current_bitset.reg = self.current_reg - if len(self.stack) == 1: - self.file.append(self.current_reg) + if len(self.stack) == 1: + self.file.append(self.current_reg) - if variant is not None: - self.add_all_variants(self.current_reg, attrs, variant) + if variant is not None: + self.add_all_variants(self.current_reg, attrs, variant) - usages = None - if "usage" in attrs: - usages = attrs["usage"].split(',') - elif self.current_array: - usages = self.current_array.usages + usages = None + if "usage" in attrs: + usages = attrs["usage"].split(',') + elif self.current_array: + usages = self.current_array.usages - self.add_all_usages(self.current_reg, usages) + self.add_all_usages(self.current_reg, usages) - def start_element(self, name, attrs): - self.cdata = "" - if name == "import": - filename = attrs["file"] - self.do_parse(os.path.join(self.path, filename)) - elif name == "domain": - self.current_domain = attrs["name"] - if "prefix" in attrs: - self.current_prefix = sanitize_variant(self.parse_variants(attrs)) - self.current_prefix_type = attrs["prefix"] - else: - self.current_prefix = None - self.current_prefix_type = None - if "varset" in attrs: - self.current_varset = self.enums[attrs["varset"]] - elif name == "stripe": - self.current_stripe = sanitize_variant(self.parse_variants(attrs)) - elif name == "enum": - self.current_enum_value = 0 - self.current_enum = Enum(attrs["name"]) - self.enums[attrs["name"]] = self.current_enum - if len(self.stack) == 1: - self.file.append(self.current_enum) - elif name == "value": - if "value" in attrs: - value = int(attrs["value"], 0) - else: - value = self.current_enum_value - self.current_enum.values.append((attrs["name"], value)) - elif name == "reg32": - self.parse_reg(attrs, 32) - elif name == "reg64": - self.parse_reg(attrs, 64) - elif name == "array": - self.current_bitsize = 32 - variant = self.parse_variants(attrs) - index_type = self.enums[attrs["index"]] if "index" in attrs else None - self.current_array = Array(attrs, self.prefix(variant), variant, self.current_array, index_type) - if len(self.stack) == 1: - self.file.append(self.current_array) - elif name == "bitset": - self.current_bitset = Bitset(attrs["name"], None) - if "inline" in attrs and attrs["inline"] == "yes": - self.current_bitset.inline = True - self.bitsets[self.current_bitset.name] = self.current_bitset - if len(self.stack) == 1 and not self.current_bitset.inline: - self.file.append(self.current_bitset) - elif name == "bitfield" and self.current_bitset: - self.parse_field(attrs["name"], attrs) - elif name == "database": - self.do_validate(attrs["xsi:schemaLocation"]) + def start_element(self, name, attrs): + self.cdata = "" + if name == "import": + filename = attrs["file"] + self.do_parse(os.path.join(self.path, filename)) + elif name == "domain": + self.current_domain = attrs["name"] + if "prefix" in attrs: + self.current_prefix = sanitize_variant( + self.parse_variants(attrs)) + self.current_prefix_type = attrs["prefix"] + else: + self.current_prefix = None + self.current_prefix_type = None + if "varset" in attrs: + self.current_varset = self.enums[attrs["varset"]] + elif name == "stripe": + self.current_stripe = sanitize_variant(self.parse_variants(attrs)) + elif name == "enum": + self.current_enum_value = 0 + self.current_enum = Enum(attrs["name"]) + self.enums[attrs["name"]] = self.current_enum + if len(self.stack) == 1: + self.file.append(self.current_enum) + elif name == "value": + if "value" in attrs: + value = int(attrs["value"], 0) + else: + value = self.current_enum_value + self.current_enum.values.append((attrs["name"], value)) + elif name == "reg32": + self.parse_reg(attrs, 32) + elif name == "reg64": + self.parse_reg(attrs, 64) + elif name == "array": + self.current_bitsize = 32 + variant = self.parse_variants(attrs) + index_type = self.enums[attrs["index"] + ] if "index" in attrs else None + self.current_array = Array(attrs, self.prefix( + variant), variant, self.current_array, index_type) + if len(self.stack) == 1: + self.file.append(self.current_array) + elif name == "bitset": + self.current_bitset = Bitset(attrs["name"], None) + if "inline" in attrs and attrs["inline"] == "yes": + self.current_bitset.inline = True + self.bitsets[self.current_bitset.name] = self.current_bitset + if len(self.stack) == 1 and not self.current_bitset.inline: + self.file.append(self.current_bitset) + elif name == "bitfield" and self.current_bitset: + self.parse_field(attrs["name"], attrs) + elif name == "database": + self.do_validate(attrs["xsi:schemaLocation"]) - def end_element(self, name): - if name == "domain": - self.current_domain = None - self.current_prefix = None - self.current_prefix_type = None - elif name == "stripe": - self.current_stripe = None - elif name == "bitset": - self.current_bitset = None - elif name == "reg32": - self.current_reg = None - elif name == "array": - # if the array has no Reg children, push an implicit reg32: - if len(self.current_array.children) == 0: - attrs = { - "name": "REG", - "offset": "0", - } - self.parse_reg(attrs, 32) - self.current_array = self.current_array.parent - elif name == "enum": - self.current_enum = None + def end_element(self, name): + if name == "domain": + self.current_domain = None + self.current_prefix = None + self.current_prefix_type = None + elif name == "stripe": + self.current_stripe = None + elif name == "bitset": + self.current_bitset = None + elif name == "reg32": + self.current_reg = None + elif name == "array": + # if the array has no Reg children, push an implicit reg32: + if len(self.current_array.children) == 0: + attrs = { + "name": "REG", + "offset": "0", + } + self.parse_reg(attrs, 32) + self.current_array = self.current_array.parent + elif name == "enum": + self.current_enum = None - def character_data(self, data): - self.cdata += data + def character_data(self, data): + self.cdata += data - def dump_reg_usages(self): - d = collections.defaultdict(list) - for usage, regs in self.usage_regs.items(): - for reg in regs: - variants = self.variant_regs.get(reg.name) - if variants: - for variant, vreg in variants.items(): - if reg == vreg: - d[(usage, sanitize_variant(variant))].append(reg) - else: - for variant in self.variants: - d[(usage, sanitize_variant(variant))].append(reg) + def dump_reg_usages(self): + d = collections.defaultdict(list) + for usage, regs in self.usage_regs.items(): + for reg in regs: + variants = self.variant_regs.get(reg.name) + if variants: + for variant, vreg in variants.items(): + if reg == vreg: + d[(usage, sanitize_variant(variant))].append(reg) + else: + for variant in self.variants: + d[(usage, sanitize_variant(variant))].append(reg) - print("#ifdef __cplusplus") + print("#ifdef __cplusplus") - for usage, regs in self.usage_regs.items(): - print("template constexpr inline uint16_t %s_REGS[] = {};" % (usage.upper())) + for usage, regs in self.usage_regs.items(): + print("template constexpr inline uint16_t %s_REGS[] = {};" % ( + usage.upper())) - for (usage, variant), regs in d.items(): - offsets = [] + for (usage, variant), regs in d.items(): + offsets = [] - for reg in regs: - if reg.array: - for i in range(reg.array.length): - offsets.append(reg.array.offset + reg.offset + i * reg.array.stride) - if reg.bit_size == 64: - offsets.append(offsets[-1] + 1) - else: - offsets.append(reg.offset) - if reg.bit_size == 64: - offsets.append(offsets[-1] + 1) + for reg in regs: + if reg.array: + for i in range(reg.array.length): + offsets.append(reg.array.offset + + reg.offset + i * reg.array.stride) + if reg.bit_size == 64: + offsets.append(offsets[-1] + 1) + else: + offsets.append(reg.offset) + if reg.bit_size == 64: + offsets.append(offsets[-1] + 1) - offsets.sort() + offsets.sort() - print("template<> constexpr inline uint16_t %s_REGS<%s>[] = {" % (usage.upper(), variant)) - for offset in offsets: - print("\t%s," % hex(offset)) - print("};") + print("template<> constexpr inline uint16_t %s_REGS<%s>[] = {" % ( + usage.upper(), variant)) + for offset in offsets: + print("\t%s," % hex(offset)) + print("};") - print("#endif") + print("#endif") - def has_variants(self, reg): - return reg.name in self.variant_regs and not is_number(reg.name) and not is_number(reg.name[1:]) + def has_variants(self, reg): + return reg.name in self.variant_regs and not is_number(reg.name) and not is_number(reg.name[1:]) - def dump(self): - enums = [] - bitsets = [] - regs = [] - for e in self.file: - if isinstance(e, Enum): - enums.append(e) - elif isinstance(e, Bitset): - bitsets.append(e) - else: - regs.append(e) + def dump(self): + enums = [] + bitsets = [] + regs = [] + for e in self.file: + if isinstance(e, Enum): + enums.append(e) + elif isinstance(e, Bitset): + bitsets.append(e) + else: + regs.append(e) - for e in enums + bitsets + regs: - e.dump(self.has_variants(e)) + for e in enums + bitsets + regs: + e.dump(self.has_variants(e)) - self.dump_reg_usages() + self.dump_reg_usages() + def dump_regs_py(self): + regs = [] + for e in self.file: + if isinstance(e, Reg): + regs.append(e) - def dump_regs_py(self): - regs = [] - for e in self.file: - if isinstance(e, Reg): - regs.append(e) + for e in regs: + e.dump_py() - for e in regs: - e.dump_py() + def dump_reg_variants(self, regname, variants): + if is_number(regname) or is_number(regname[1:]): + return + print("#ifdef __cplusplus") + print("struct __%s {" % regname) + # TODO be more clever.. we should probably figure out which + # fields have the same type in all variants (in which they + # appear) and stuff everything else in a variant specific + # sub-structure. + seen_fields = [] + bit_size = 32 + array = False + address = None + constexpr_mark = " CONSTEXPR" + for variant in variants.keys(): + print(" /* %s fields: */" % variant) + reg = variants[variant] + bit_size = reg.bit_size + array = reg.array + for f in reg.bitset.fields: + fld_name = field_name(reg, f) + if fld_name in seen_fields: + continue + seen_fields.append(fld_name) + name = fld_name.lower() + if f.type in ["address", "waddress"]: + if address: + continue + address = f + print("#ifndef TU_CS_H") + tab_to(" __bo_type", "bo;") + tab_to(" uint32_t", "bo_offset;") + print("#endif") + continue + type, val = f.ctype("var") + tab_to(" %s" % type, "%s;" % name) + if f.type == "float": + constexpr_mark = "" + print(" /* fallback fields: */") + if bit_size == 64: + tab_to(" uint64_t", "unknown;") + tab_to(" uint64_t", "qword;") + else: + tab_to(" uint32_t", "unknown;") + tab_to(" uint32_t", "dword;") + print("};") + # TODO don't hardcode the varset enum name + varenum = "chip" + print("template <%s %s>" % (varenum, varenum.upper())) + print("static%s inline struct fd_reg_pair" % (constexpr_mark)) + xtra = "" + xtravar = "" + if array: + xtra = "int __i, " + xtravar = "__i, " + print("__%s(%sstruct __%s fields) {" % (regname, xtra, regname)) + for variant in variants.keys(): + if "-" in variant: + start = variant[:variant.index("-")] + end = variant[variant.index("-") + 1:] + if end != "": + print(" if ((%s >= %s) && (%s <= %s)) {" % ( + varenum.upper(), start, varenum.upper(), end)) + else: + print(" if (%s >= %s) {" % (varenum.upper(), start)) + else: + print(" if (%s == %s) {" % (varenum.upper(), variant)) + reg = variants[variant] + reg.dump_regpair_builder() + print(" } else") + print(" assert(!\"invalid variant\");") + print(" return (struct fd_reg_pair){};") + print("}") + if bit_size == 64: + skip = ", { .reg = 0 }" + else: + skip = "" - def dump_reg_variants(self, regname, variants): - if is_number(regname) or is_number(regname[1:]): - return - print("#ifdef __cplusplus") - print("struct __%s {" % regname) - # TODO be more clever.. we should probably figure out which - # fields have the same type in all variants (in which they - # appear) and stuff everything else in a variant specific - # sub-structure. - seen_fields = [] - bit_size = 32 - array = False - address = None - for variant in variants.keys(): - print(" /* %s fields: */" % variant) - reg = variants[variant] - bit_size = reg.bit_size - array = reg.array - for f in reg.bitset.fields: - fld_name = field_name(reg, f) - if fld_name in seen_fields: - continue - seen_fields.append(fld_name) - name = fld_name.lower() - if f.type in [ "address", "waddress" ]: - if address: - continue - address = f - tab_to(" __bo_type", "bo;") - tab_to(" uint32_t", "bo_offset;") - continue - type, val = f.ctype("var") - tab_to(" %s" %type, "%s;" %name) - print(" /* fallback fields: */") - if bit_size == 64: - tab_to(" uint64_t", "unknown;") - tab_to(" uint64_t", "qword;") - else: - tab_to(" uint32_t", "unknown;") - tab_to(" uint32_t", "dword;") - print("};") - # TODO don't hardcode the varset enum name - varenum = "chip" - print("template <%s %s>" % (varenum, varenum.upper())) - print("static inline struct fd_reg_pair") - xtra = "" - xtravar = "" - if array: - xtra = "int __i, " - xtravar = "__i, " - print("__%s(%sstruct __%s fields) {" % (regname, xtra, regname)) - for variant in variants.keys(): - if "-" in variant: - start = variant[:variant.index("-")] - end = variant[variant.index("-") + 1:] - if end != "": - print(" if ((%s >= %s) && (%s <= %s)) {" % (varenum.upper(), start, varenum.upper(), end)) - else: - print(" if (%s >= %s) {" % (varenum.upper(), start)) - else: - print(" if (%s == %s) {" % (varenum.upper(), variant)) - reg = variants[variant] - reg.dump_regpair_builder() - print(" } else") - print(" assert(!\"invalid variant\");") - print(" return (struct fd_reg_pair){};") - print("}") + print("#define %s(VARIANT, %s...) __%s(%s{__VA_ARGS__})%s" % ( + regname, xtravar, regname, xtravar, skip)) + print("#endif /* __cplusplus */") - if bit_size == 64: - skip = ", { .reg = 0 }" - else: - skip = "" + def dump_structs(self): + for e in self.file: + e.dump_pack_struct(self.has_variants(e)) - print("#define %s(VARIANT, %s...) __%s(%s{__VA_ARGS__})%s" % (regname, xtravar, regname, xtravar, skip)) - print("#endif /* __cplusplus */") - - def dump_structs(self): - for e in self.file: - e.dump_pack_struct(self.has_variants(e)) - - for regname in self.variant_regs: - self.dump_reg_variants(regname, self.variant_regs[regname]) + for regname in self.variant_regs: + self.dump_reg_variants(regname, self.variant_regs[regname]) def dump_c(args, guard, func): - p = Parser() + p = Parser() - try: - p.parse(args.rnn, args.xml, args.validate) - except Error as e: - print(e, file=sys.stderr) - exit(1) + try: + p.parse(args.rnn, args.xml, args.validate) + except Error as e: + print(e, file=sys.stderr) + exit(1) - print("#ifndef %s\n#define %s\n" % (guard, guard)) + print("#ifndef %s\n#define %s\n" % (guard, guard)) - print("/* Autogenerated file, DO NOT EDIT manually! */") + print("/* Autogenerated file, DO NOT EDIT manually! */") - print() - print("#ifdef __KERNEL__") - print("#include ") - print("#define assert(x) BUG_ON(!(x))") - print("#else") - print("#include ") - print("#endif") - print() + print() + print("#ifdef __KERNEL__") + print("#include ") + print("#define assert(x) BUG_ON(!(x))") + print("#else") + print("#include ") + print("#endif") + print() - print("#ifdef __cplusplus") - print("#define __struct_cast(X)") - print("#else") - print("#define __struct_cast(X) (struct X)") - print("#endif") - print() + print("#ifdef __cplusplus") + print("#define __struct_cast(X)") + print("#define CONSTEXPR constexpr") + print("#else") + print("#define __struct_cast(X) (struct X)") + print("#define CONSTEXPR") + print("#endif") + print() - print("#ifndef FD_NO_DEPRECATED_PACK") - print("#define FD_DEPRECATED __attribute__((deprecated))") - print("#else") - print("#define FD_DEPRECATED") - print("#endif") - print() + # TODO figure out what to do about fd_reg_stomp_allowed() + # vs gcc.. for now only enable the warnings with clang: + print("#if defined(__clang__) && !defined(FD_NO_DEPRECATED_PACK) && !defined(__KERNEL__)") + print("#define __FD_DEPRECATED _Pragma (\"GCC warning \\\"Deprecated reg builder\\\"\")") + print("#else") + print("#define __FD_DEPRECATED") + print("#endif") + print() - func(p) + func(p) - print() - print("#undef FD_DEPRECATED") - print() - - print("#endif /* %s */" % guard) + print("#endif /* %s */" % guard) def dump_c_defines(args): - guard = str.replace(os.path.basename(args.xml), '.', '_').upper() - dump_c(args, guard, lambda p: p.dump()) + guard = str.replace(os.path.basename(args.xml), '.', '_').upper() + dump_c(args, guard, lambda p: p.dump()) def dump_c_pack_structs(args): - guard = str.replace(os.path.basename(args.xml), '.', '_').upper() + '_STRUCTS' - dump_c(args, guard, lambda p: p.dump_structs()) + guard = str.replace(os.path.basename(args.xml), + '.', '_').upper() + '_STRUCTS' + dump_c(args, guard, lambda p: p.dump_structs()) +def dump_perfcntrs(args): + p = Parser() + + try: + p.parse(args.rnn, args.xml, args.validate) + except Error as e: + print(e, file=sys.stderr) + exit(1) + + perfcntrs = json.load(open(args.json, "r", encoding="utf-8")) + + chip_type = p.enums['chip'] + chip = perfcntrs['chip'] + if not chip_type.has_name(chip): + raise Error("Invalid chip: " + chip) + + groups = perfcntrs['groups'] + + guard = "__" + chip + "_PERFCNTRS_" + print("#ifndef %s\n#define %s\n" % (guard, guard)) + print("/* Autogenerated file, DO NOT EDIT manually! */") + print() + print("#ifdef __KERNEL__") + print("#include \"msm_perfcntr.h\"") + print("#endif") + print() + + def has_variant(variant): + if variant is None: + return True + if "-" in variant: + start = chip_type.value(variant[:variant.index("-")]) + end = chip_type.value(variant[variant.index("-") + 1:]) + chipn = chip_type.value(chip) + + return (start is None or chipn >= start) and (end is None or chipn <= end) + return chip == variant + + # Split out arrays and regs for later access: + arrays = {} + regs = {} + for e in p.file: + if isinstance(e, Array) and has_variant(e.variant): + arrays[e.local_name] = e + if isinstance(e, Reg): + regs[e.name] = e + + # For variant regs, overwrite 'regs' entries with correct variant: + for regname in p.variant_regs: + for (variant, reg) in p.variant_regs[regname].items(): + if has_variant(variant): + regs[regname] = reg + break + + for group in groups: + name = group['name'] + name_low = name.lower() + num = group['num'] + countable_type_name = group['countable_type'] + + if not countable_type_name in p.enums: + raise Error("Invalid type: " + countable_type_name) + + countable_type = p.enums[countable_type_name] + + print("#ifndef __KERNEL__") + print("static const struct fd_perfcntr_countable " + name_low + "_countables[] = {") + for (name, value) in countable_type.values: + # if the countable is prefixed with the chip, strip that: + # (note: avoid py3.9 dependency for kernel) + if name.startswith(chip + "_"): + name = name[len(chip)+1:] + print(" { \"" + name + "\", " + str(value) + " },") + print("};") + print("#endif") + + print("static const struct fd_perfcntr_counter " + name_low + "_counters[] = {") + for i in range(0, num): + if "reserved" in group and i in group["reserved"]: + continue + def get_reg(name): + # if reg has {} pattern, expand that first: + name = name.format(i) + + if name in arrays: + arr = arrays[name] + return arr.offset + (i * arr.stride) + + if not name in regs: + raise Error("Invalid reg: " + name) + + reg = regs[name] + return reg.offset + + def get_counter(): + # if the counter is just a single "counter" value + # should be specified in the json, but for legacy separate + # hi/lo pairs "counter_lo" and "counter_hi" should + # be specified + if "counter" in group: + counter = get_reg(group["counter"]) + return [counter, counter+1] + counter_lo = get_reg(group["counter_lo"]) + counter_hi = get_reg(group["counter_hi"]) + return [counter_lo, counter_hi] + + (counter_lo, counter_hi) = get_counter() + select = get_reg(group['select']) + + select_offset = 0 + if "select_offset" in group: + select_offset = int(group["select_offset"]) + select = select + select_offset + + slice_select_str = "" + if "slice_select" in group: + slice_select = group["slice_select"] + for reg in slice_select: + val = get_reg(reg) + select_offset + slice_select_str += "0x%04x, " % val + + # TODO add support for things that need enable/clear regs + + print(" { 0x%04x, {%s}, 0x%04x, 0x%04x }," % (select, slice_select_str, counter_lo, counter_hi)) + print("};") + + print() + + print("const struct fd_perfcntr_group " + chip.lower() + "_perfcntr_groups[] = {") + for group in groups: + name = group['name'] + name_low = name.lower() + pipe = 'NONE' + if 'pipe' in group: + pipe = group['pipe'] + + print(" GROUP(\"%s\", PIPE_%s, %s_counters, %s_countables)," % (name, pipe, name_low, name_low)) + + print("};") + print("const unsigned " + chip.lower() + "_num_perfcntr_groups = ARRAY_SIZE(" + chip.lower() + "_perfcntr_groups);") + + print() + print("#endif /* %s */" % guard) + def dump_py_defines(args): - p = Parser() + p = Parser() - try: - p.parse(args.rnn, args.xml, args.validate) - except Error as e: - print(e, file=sys.stderr) - exit(1) + try: + p.parse(args.rnn, args.xml, args.validate) + except Error as e: + print(e, file=sys.stderr) + exit(1) - file_name = os.path.splitext(os.path.basename(args.xml))[0] + file_name = os.path.splitext(os.path.basename(args.xml))[0] - print("from enum import IntEnum") - print("class %sRegs(IntEnum):" % file_name.upper()) + print("from enum import IntEnum") + print("class %sRegs(IntEnum):" % file_name.upper()) - os.path.basename(args.xml) + os.path.basename(args.xml) - p.dump_regs_py() + p.dump_regs_py() def main(): - parser = argparse.ArgumentParser() - parser.add_argument('--rnn', type=str, required=True) - parser.add_argument('--xml', type=str, required=True) - parser.add_argument('--validate', default=False, action='store_true') - parser.add_argument('--no-validate', dest='validate', action='store_false') + parser = argparse.ArgumentParser() + parser.add_argument('--rnn', type=str, required=True) + parser.add_argument('--xml', type=str, required=True) + parser.add_argument('--validate', default=False, action='store_true') + parser.add_argument('--no-validate', dest='validate', action='store_false') - subparsers = parser.add_subparsers() - subparsers.required = True + subparsers = parser.add_subparsers() + subparsers.required = True - parser_c_defines = subparsers.add_parser('c-defines') - parser_c_defines.set_defaults(func=dump_c_defines) + parser_c_defines = subparsers.add_parser('c-defines') + parser_c_defines.set_defaults(func=dump_c_defines) - parser_c_pack_structs = subparsers.add_parser('c-pack-structs') - parser_c_pack_structs.set_defaults(func=dump_c_pack_structs) + parser_c_pack_structs = subparsers.add_parser('c-pack-structs') + parser_c_pack_structs.set_defaults(func=dump_c_pack_structs) - parser_py_defines = subparsers.add_parser('py-defines') - parser_py_defines.set_defaults(func=dump_py_defines) + parser_perfcntrs = subparsers.add_parser('perfcntrs') + parser_perfcntrs.add_argument('--json', type=str, required=True) + parser_perfcntrs.set_defaults(func=dump_perfcntrs) - args = parser.parse_args() - args.func(args) + parser_py_defines = subparsers.add_parser('py-defines') + parser_py_defines.set_defaults(func=dump_py_defines) + + args = parser.parse_args() + args.func(args) if __name__ == '__main__': - main() + main() From 5442bafd7de590aba13ae4fe6ae3b8a8b9106d86 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:39 -0700 Subject: [PATCH 50/67] drm/msm/registers: Add perfcntr json Pull in perfcntr json and wire up generation of perfcntr tables. Sync from mesa commit a573e25b6dcd ("freedreno/registers: Gen8 perfcntr fixes") Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728204/ Message-ID: <20260526145137.160554-6-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/Makefile | 27 +- drivers/gpu/drm/msm/msm_perfcntr.h | 48 ++++ .../msm/registers/adreno/a2xx_perfcntrs.json | 109 ++++++++ .../msm/registers/adreno/a5xx_perfcntrs.json | 128 ++++++++++ .../msm/registers/adreno/a6xx_perfcntrs.json | 112 ++++++++ .../msm/registers/adreno/a7xx_perfcntrs.json | 228 +++++++++++++++++ .../msm/registers/adreno/a8xx_perfcntrs.json | 241 ++++++++++++++++++ 7 files changed, 891 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/msm/msm_perfcntr.h create mode 100644 drivers/gpu/drm/msm/registers/adreno/a2xx_perfcntrs.json create mode 100644 drivers/gpu/drm/msm/registers/adreno/a5xx_perfcntrs.json create mode 100644 drivers/gpu/drm/msm/registers/adreno/a6xx_perfcntrs.json create mode 100644 drivers/gpu/drm/msm/registers/adreno/a7xx_perfcntrs.json create mode 100644 drivers/gpu/drm/msm/registers/adreno/a8xx_perfcntrs.json diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index ce00cfb0a875..363c984a62e6 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -176,6 +176,11 @@ quiet_cmd_headergen = GENHDR $@ cmd_headergen = mkdir -p $(obj)/generated && $(PYTHON3) $(src)/registers/gen_header.py \ $(headergen-opts) --rnn $(src)/registers --xml $< c-defines > $@ +# TODO how to do this for a2xx/a5xx which have different .xml arg? +quiet_cmd_headergen_json = GENHDRJSN $@ + cmd_headergen_json = mkdir -p $(obj)/generated && $(PYTHON3) $(src)/registers/gen_header.py \ + $(headergen-opts) --rnn $(src)/registers --xml $(filter %.xml,$^) perfcntrs --json $< > $@ + $(obj)/generated/%.xml.h: $(src)/registers/adreno/%.xml \ $(src)/registers/adreno/adreno_common.xml \ $(src)/registers/adreno/adreno_pm4.xml \ @@ -192,6 +197,24 @@ $(obj)/generated/%.xml.h: $(src)/registers/display/%.xml \ FORCE $(call if_changed,headergen) +ADRENO_PERFCNTRS = + +define adreno_perfcntrs +ADRENO_PERFCNTRS += generated/$(1)_perfcntrs.json.c +$$(obj)/generated/$(1)_perfcntrs.json.c: $$(src)/registers/adreno/$(1)_perfcntrs.json \ + $$(src)/registers/adreno/$(2).xml \ + FORCE + $$(call if_changed,headergen_json) +endef + +$(eval $(call adreno_perfcntrs,a2xx,a2xx)) +$(eval $(call adreno_perfcntrs,a5xx,a5xx)) +$(eval $(call adreno_perfcntrs,a6xx,a6xx)) +$(eval $(call adreno_perfcntrs,a7xx,a6xx)) +$(eval $(call adreno_perfcntrs,a8xx,a6xx)) + +adreno-y += $(ADRENO_PERFCNTRS:.c=.o) + ADRENO_HEADERS = \ generated/a2xx.xml.h \ generated/a3xx.xml.h \ @@ -223,7 +246,7 @@ DISPLAY_HEADERS = \ generated/mdss.xml.h \ generated/sfpb.xml.h -$(addprefix $(obj)/,$(adreno-y)): $(addprefix $(obj)/,$(ADRENO_HEADERS)) +$(addprefix $(obj)/,$(msm-y)): $(addprefix $(obj)/,$(ADRENO_HEADERS)) $(addprefix $(obj)/,$(msm-display-y)): $(addprefix $(obj)/,$(DISPLAY_HEADERS)) -targets += $(ADRENO_HEADERS) $(DISPLAY_HEADERS) +targets += $(ADRENO_HEADERS) $(DISPLAY_HEADERS) $(ADRENO_PERFCNTRS) diff --git a/drivers/gpu/drm/msm/msm_perfcntr.h b/drivers/gpu/drm/msm/msm_perfcntr.h new file mode 100644 index 000000000000..75300bd69e80 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_perfcntr.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __MSM_PERFCNTR_H__ +#define __MSM_PERFCNTR_H__ + +#include "linux/array_size.h" + +#include "adreno_common.xml.h" + +/* + * This is a subset of the tables used by mesa. We don't need to + * enumerate the countables on the kernel side. + */ + +/* Describes a single counter: */ +struct msm_perfcntr_counter { + /* offset of the SELect register to choose what to count: */ + unsigned select_reg; + /* additional SEL regs to enable slice counters (gen8+) */ + unsigned slice_select_regs[2]; + /* offset of the lo/hi 32b to read current counter value: */ + unsigned counter_reg_lo; + unsigned counter_reg_hi; + /* TODO some counters have enable/clear registers */ +}; + +/* Describes an entire counter group: */ +struct msm_perfcntr_group { + const char *name; + enum adreno_pipe pipe; + unsigned num_counters; + const struct msm_perfcntr_counter *counters; +}; + +#define GROUP(_name, _pipe, _counters, _countables) { \ + .name = _name, \ + .pipe = _pipe, \ + .num_counters = ARRAY_SIZE(_counters), \ + .counters = _counters, \ + } + +#define fd_perfcntr_counter msm_perfcntr_counter +#define fd_perfcntr_group msm_perfcntr_group + +#endif /* __MSM_PERFCNTR_H__ */ diff --git a/drivers/gpu/drm/msm/registers/adreno/a2xx_perfcntrs.json b/drivers/gpu/drm/msm/registers/adreno/a2xx_perfcntrs.json new file mode 100644 index 000000000000..8095345ffd8e --- /dev/null +++ b/drivers/gpu/drm/msm/registers/adreno/a2xx_perfcntrs.json @@ -0,0 +1,109 @@ +{ + "chip": "A2XX", + "groups": [ + { + "name": "CP", + "num": 1, + "select": "CP_PERFCOUNTER_SELECT", + "counter_lo": "CP_PERFCOUNTER_LO", + "counter_hi": "CP_PERFCOUNTER_HI", + "countable_type": "a2xx_cp_perfcount_sel" + }, + { + "name": "PA_SU", + "num": 4, + "select": "PA_SU_PERFCOUNTER{}_SELECT", + "counter_lo": "PA_SU_PERFCOUNTER{}_LOW", + "counter_hi": "PA_SU_PERFCOUNTER{}_HI", + "countable_type": "a2xx_su_perfcnt_select" + }, + { + "name": "PA_SC", + "num": 1, + "select": "PA_SC_PERFCOUNTER{}_SELECT", + "counter_lo": "PA_SC_PERFCOUNTER{}_LOW", + "counter_hi": "PA_SC_PERFCOUNTER{}_HI", + "countable_type": "a2xx_sc_perfcnt_select" + }, + { + "name": "VGT", + "num": 4, + "select": "VGT_PERFCOUNTER{}_SELECT", + "counter_lo": "VGT_PERFCOUNTER{}_LOW", + "counter_hi": "VGT_PERFCOUNTER{}_HI", + "countable_type": "a2xx_vgt_perfcount_select" + }, + { + "name": "TCR", + "num": 2, + "select": "TCR_PERFCOUNTER{}_SELECT", + "counter_lo": "TCR_PERFCOUNTER{}_LOW", + "counter_hi": "TCR_PERFCOUNTER{}_HI", + "countable_type": "a2xx_tcr_perfcount_select" + }, + { + "name": "TP0", + "num": 2, + "select": "TP0_PERFCOUNTER{}_SELECT", + "counter_lo": "TP0_PERFCOUNTER{}_LOW", + "counter_hi": "TP0_PERFCOUNTER{}_HI", + "countable_type": "a2xx_tp_perfcount_select" + }, + { + "name": "TCM", + "num": 2, + "select": "TCM_PERFCOUNTER{}_SELECT", + "counter_lo": "TCM_PERFCOUNTER{}_LOW", + "counter_hi": "TCM_PERFCOUNTER{}_HI", + "countable_type": "a2xx_tcm_perfcount_select" + }, + { + "name": "TCF", + "num": 12, + "select": "TCF_PERFCOUNTER{}_SELECT", + "counter_lo": "TCF_PERFCOUNTER{}_LOW", + "counter_hi": "TCF_PERFCOUNTER{}_HI", + "countable_type": "a2xx_tcf_perfcount_select" + }, + { + "name": "SQ", + "num": 4, + "select": "SQ_PERFCOUNTER{}_SELECT", + "counter_lo": "SQ_PERFCOUNTER{}_LOW", + "counter_hi": "SQ_PERFCOUNTER{}_HI", + "countable_type": "a2xx_sq_perfcnt_select" + }, + { + "name": "SX", + "num": 1, + "select": "SX_PERFCOUNTER{}_SELECT", + "counter_lo": "SX_PERFCOUNTER{}_LOW", + "counter_hi": "SX_PERFCOUNTER{}_HI", + "countable_type": "a2xx_sx_perfcnt_select" + }, + { + "name": "MH", + "num": 2, + "select": "MH_PERFCOUNTER{}_SELECT", + "counter_lo": "MH_PERFCOUNTER{}_LOW", + "counter_hi": "MH_PERFCOUNTER{}_HI", + "countable_type": "a2xx_mh_perfcnt_select" + }, + { + "name": "RBBM", + "num": 2, + "select": "RBBM_PERFCOUNTER{}_SELECT", + "counter_lo": "RBBM_PERFCOUNTER{}_LO", + "counter_hi": "RBBM_PERFCOUNTER{}_HI", + "countable_type": "a2xx_rbbm_perfcount1_sel" + }, + { + "name": "RB", + "num": 4, + "select": "RB_PERFCOUNTER{}_SELECT", + "counter_lo": "RB_PERFCOUNTER{}_LOW", + "counter_hi": "RB_PERFCOUNTER{}_HI", + "countable_type": "a2xx_rb_perfcnt_select" + } + ] +} diff --git a/drivers/gpu/drm/msm/registers/adreno/a5xx_perfcntrs.json b/drivers/gpu/drm/msm/registers/adreno/a5xx_perfcntrs.json new file mode 100644 index 000000000000..d95503543f94 --- /dev/null +++ b/drivers/gpu/drm/msm/registers/adreno/a5xx_perfcntrs.json @@ -0,0 +1,128 @@ +{ + "chip": "A5XX", + "groups": [ + { + "name": "CP", + "num": 8, + "reserved": [ 0 ], + "select": "CP_PERFCTR_CP_SEL_{}", + "counter_lo": "RBBM_PERFCTR_CP_{}_LO", + "counter_hi": "RBBM_PERFCTR_CP_{}_HI", + "countable_type": "a5xx_cp_perfcounter_select" + }, + { + "name": "CCU", + "num": 4, + "select": "RB_PERFCTR_CCU_SEL_{}", + "counter_lo": "RBBM_PERFCTR_CCU_{}_LO", + "counter_hi": "RBBM_PERFCTR_CCU_{}_HI", + "countable_type": "a5xx_ccu_perfcounter_select" + }, + { + "name": "TSE", + "num": 4, + "select": "GRAS_PERFCTR_TSE_SEL_{}", + "counter_lo": "RBBM_PERFCTR_TSE_{}_LO", + "counter_hi": "RBBM_PERFCTR_TSE_{}_HI", + "countable_type": "a5xx_tse_perfcounter_select" + }, + { + "name": "RAS", + "num": 4, + "select": "GRAS_PERFCTR_RAS_SEL_{}", + "counter_lo": "RBBM_PERFCTR_RAS_{}_LO", + "counter_hi": "RBBM_PERFCTR_RAS_{}_HI", + "countable_type": "a5xx_ras_perfcounter_select" + }, + { + "name": "LRZ", + "num": 4, + "select": "GRAS_PERFCTR_LRZ_SEL_{}", + "counter_lo": "RBBM_PERFCTR_LRZ_{}_LO", + "counter_hi": "RBBM_PERFCTR_LRZ_{}_HI", + "countable_type": "a5xx_lrz_perfcounter_select" + }, + { + "name": "HLSQ", + "num": 8, + "select": "HLSQ_PERFCTR_HLSQ_SEL_{}", + "counter_lo": "RBBM_PERFCTR_HLSQ_{}_LO", + "counter_hi": "RBBM_PERFCTR_HLSQ_{}_HI", + "countable_type": "a5xx_hlsq_perfcounter_select" + }, + { + "name": "PC", + "num": 8, + "select": "PC_PERFCTR_PC_SEL_{}", + "counter_lo": "RBBM_PERFCTR_PC_{}_LO", + "counter_hi": "RBBM_PERFCTR_PC_{}_HI", + "countable_type": "a5xx_pc_perfcounter_select" + }, + { + "name": "RB", + "num": 8, + "select": "RB_PERFCTR_RB_SEL_{}", + "counter_lo": "RBBM_PERFCTR_RB_{}_LO", + "counter_hi": "RBBM_PERFCTR_RB_{}_HI", + "countable_type": "a5xx_rb_perfcounter_select" + }, + { + "name": "RBBM", + "num": 4, + "reserved": [ 0 ], + "select": "RBBM_PERFCTR_RBBM_SEL_{}", + "counter_lo": "RBBM_PERFCTR_RBBM_{}_LO", + "counter_hi": "RBBM_PERFCTR_RBBM_{}_HI", + "countable_type": "a5xx_rbbm_perfcounter_select" + }, + { + "name": "SP", + "num": 12, + "reserved": [ 0 ], + "select": "SP_PERFCTR_SP_SEL_{}", + "counter_lo": "RBBM_PERFCTR_SP_{}_LO", + "counter_hi": "RBBM_PERFCTR_SP_{}_HI", + "countable_type": "a5xx_sp_perfcounter_select" + }, + { + "name": "TP", + "num": 8, + "select": "TPL1_PERFCTR_TP_SEL_{}", + "counter_lo": "RBBM_PERFCTR_TP_{}_LO", + "counter_hi": "RBBM_PERFCTR_TP_{}_HI", + "countable_type": "a5xx_tp_perfcounter_select" + }, + { + "name": "UCHE", + "num": 8, + "select": "UCHE_PERFCTR_UCHE_SEL_{}", + "counter_lo": "RBBM_PERFCTR_UCHE_{}_LO", + "counter_hi": "RBBM_PERFCTR_UCHE_{}_HI", + "countable_type": "a5xx_uche_perfcounter_select" + }, + { + "name": "VFD", + "num": 8, + "select": "VFD_PERFCTR_VFD_SEL_{}", + "counter_lo": "RBBM_PERFCTR_VFD_{}_LO", + "counter_hi": "RBBM_PERFCTR_VFD_{}_HI", + "countable_type": "a5xx_vfd_perfcounter_select" + }, + { + "name": "VPC", + "num": 4, + "select": "VPC_PERFCTR_VPC_SEL_{}", + "counter_lo": "RBBM_PERFCTR_VPC_{}_LO", + "counter_hi": "RBBM_PERFCTR_VPC_{}_HI", + "countable_type": "a5xx_vpc_perfcounter_select" + }, + { + "name": "VSC", + "num": 2, + "select": "VSC_PERFCTR_VSC_SEL_{}", + "counter_lo": "RBBM_PERFCTR_VSC_{}_LO", + "counter_hi": "RBBM_PERFCTR_VSC_{}_HI", + "countable_type": "a5xx_vsc_perfcounter_select" + } + ] +} diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx_perfcntrs.json b/drivers/gpu/drm/msm/registers/adreno/a6xx_perfcntrs.json new file mode 100644 index 000000000000..ec303e0b9f28 --- /dev/null +++ b/drivers/gpu/drm/msm/registers/adreno/a6xx_perfcntrs.json @@ -0,0 +1,112 @@ +{ + "chip": "A6XX", + "groups": [ + { + "name": "CP", + "num": 14, + "reserved": [ 0 ], + "select": "CP_PERFCTR_CP_SEL", + "counter": "RBBM_PERFCTR_CP", + "countable_type": "a6xx_cp_perfcounter_select" + }, + { + "name": "CCU", + "num": 5, + "select": "RB_PERFCTR_CCU_SEL", + "counter": "RBBM_PERFCTR_CCU", + "countable_type": "a6xx_ccu_perfcounter_select" + }, + { + "name": "TSE", + "num": 4, + "select": "GRAS_PERFCTR_TSE_SEL", + "counter": "RBBM_PERFCTR_TSE", + "countable_type": "a6xx_tse_perfcounter_select" + }, + { + "name": "RAS", + "num": 4, + "select": "GRAS_PERFCTR_RAS_SEL", + "counter": "RBBM_PERFCTR_RAS", + "countable_type": "a6xx_ras_perfcounter_select" + }, + { + "name": "LRZ", + "num": 4, + "select": "GRAS_PERFCTR_LRZ_SEL", + "counter": "RBBM_PERFCTR_LRZ", + "countable_type": "a6xx_lrz_perfcounter_select" + }, + { + "name": "CMP", + "num": 4, + "select": "RB_PERFCTR_CMP_SEL", + "counter": "RBBM_PERFCTR_CMP", + "countable_type": "a6xx_cmp_perfcounter_select" + }, + { + "name": "HLSQ", + "num": 6, + "select": "HLSQ_PERFCTR_HLSQ_SEL", + "counter": "RBBM_PERFCTR_HLSQ", + "countable_type": "a6xx_hlsq_perfcounter_select" + }, + { + "name": "PC", + "num": 8, + "select": "PC_PERFCTR_PC_SEL", + "counter": "RBBM_PERFCTR_PC", + "countable_type": "a6xx_pc_perfcounter_select" + }, + { + "name": "RB", + "num": 8, + "select": "RB_PERFCTR_RB_SEL", + "counter": "RBBM_PERFCTR_RB", + "countable_type": "a6xx_rb_perfcounter_select" + }, + { + "name": "SP", + "num": 24, + "reserved": [ 0 ], + "select": "SP_PERFCTR_SP_SEL", + "counter": "RBBM_PERFCTR_SP", + "countable_type": "a6xx_sp_perfcounter_select" + }, + { + "name": "TP", + "num": 12, + "select": "TPL1_PERFCTR_TP_SEL", + "counter": "RBBM_PERFCTR_TP", + "countable_type": "a6xx_tp_perfcounter_select" + }, + { + "name": "UCHE", + "num": 12, + "select": "UCHE_PERFCTR_UCHE_SEL", + "counter": "RBBM_PERFCTR_UCHE", + "countable_type": "a6xx_uche_perfcounter_select" + }, + { + "name": "VFD", + "num": 8, + "select": "VFD_PERFCTR_VFD_SEL", + "counter": "RBBM_PERFCTR_VFD", + "countable_type": "a6xx_vfd_perfcounter_select" + }, + { + "name": "VPC", + "num": 6, + "select": "VPC_PERFCTR_VPC_SEL", + "counter": "RBBM_PERFCTR_VPC", + "countable_type": "a6xx_vpc_perfcounter_select" + }, + { + "name": "VSC", + "num": 2, + "select": "VSC_PERFCTR_VSC_SEL", + "counter": "RBBM_PERFCTR_VSC", + "countable_type": "a6xx_vsc_perfcounter_select" + } + ] +} diff --git a/drivers/gpu/drm/msm/registers/adreno/a7xx_perfcntrs.json b/drivers/gpu/drm/msm/registers/adreno/a7xx_perfcntrs.json new file mode 100644 index 000000000000..e60aab1862ec --- /dev/null +++ b/drivers/gpu/drm/msm/registers/adreno/a7xx_perfcntrs.json @@ -0,0 +1,228 @@ +{ + "chip": "A7XX", + "groups": [ + { + "name": "CP", + "num": 14, + "reserved": [ 0 ], + "select": "CP_PERFCTR_CP_SEL", + "counter": "RBBM_PERFCTR_CP", + "countable_type": "a7xx_cp_perfcounter_select" + }, + { + "name": "RBBM", + "num": 4, + "select": "RBBM_PERFCTR_RBBM_SEL", + "counter": "RBBM_PERFCTR_RBBM", + "countable_type": "a7xx_rbbm_perfcounter_select" + }, + { + "name": "PC", + "pipe": "BR", + "num": 8, + "select": "PC_PERFCTR_PC_SEL", + "counter": "RBBM_PERFCTR_PC", + "countable_type": "a7xx_pc_perfcounter_select" + }, + { + "name": "VFD", + "pipe": "BR", + "num": 8, + "select": "VFD_PERFCTR_VFD_SEL", + "counter": "RBBM_PERFCTR_VFD", + "countable_type": "a7xx_vfd_perfcounter_select" + }, + { + "name": "HLSQ", + "pipe": "BR", + "num": 6, + "select": "SP_PERFCTR_HLSQ_SEL", + "counter": "RBBM_PERFCTR_HLSQ", + "countable_type": "a7xx_hlsq_perfcounter_select" + }, + { + "name": "VPC", + "pipe": "BR", + "num": 6, + "select": "VPC_PERFCTR_VPC_SEL", + "counter": "RBBM_PERFCTR_VPC", + "countable_type": "a7xx_vpc_perfcounter_select" + }, + { + "name": "TSE", + "pipe": "BR", + "num": 4, + "select": "GRAS_PERFCTR_TSE_SEL", + "counter": "RBBM_PERFCTR_TSE", + "countable_type": "a7xx_tse_perfcounter_select" + }, + { + "name": "RAS", + "pipe": "BR", + "num": 4, + "select": "GRAS_PERFCTR_RAS_SEL", + "counter": "RBBM_PERFCTR_RAS", + "countable_type": "a7xx_ras_perfcounter_select" + }, + { + "name": "UCHE", + "num": 12, + "select": "UCHE_PERFCTR_UCHE_SEL", + "counter": "RBBM_PERFCTR_UCHE", + "countable_type": "a7xx_uche_perfcounter_select" + }, + { + "name": "TP", + "pipe": "BR", + "num": 12, + "select": "TPL1_PERFCTR_TP_SEL", + "counter": "RBBM_PERFCTR_TP", + "countable_type": "a7xx_tp_perfcounter_select" + }, + { + "name": "SP", + "pipe": "BR", + "num": 24, + "select": "SP_PERFCTR_SP_SEL", + "counter": "RBBM_PERFCTR_SP", + "countable_type": "a7xx_sp_perfcounter_select" + }, + { + "name": "RB", + "num": 8, + "select": "RB_PERFCTR_RB_SEL", + "counter": "RBBM_PERFCTR_RB", + "countable_type": "a7xx_rb_perfcounter_select" + }, + { + "name": "VSC", + "num": 2, + "select": "VSC_PERFCTR_VSC_SEL", + "counter": "RBBM_PERFCTR_VSC", + "countable_type": "a7xx_vsc_perfcounter_select" + }, + { + "name": "CCU", + "num": 5, + "select": "RB_PERFCTR_CCU_SEL", + "counter": "RBBM_PERFCTR_CCU", + "countable_type": "a7xx_ccu_perfcounter_select" + }, + { + "name": "LRZ", + "pipe": "BR", + "num": 4, + "select": "GRAS_PERFCTR_LRZ_SEL", + "counter": "RBBM_PERFCTR_LRZ", + "countable_type": "a7xx_lrz_perfcounter_select" + }, + { + "name": "CMP", + "num": 4, + "select": "RB_PERFCTR_CMP_SEL", + "counter": "RBBM_PERFCTR_CMP", + "countable_type": "a7xx_cmp_perfcounter_select" + }, + { + "name": "UFC", + "pipe": "BR", + "num": 4, + "select": "RB_PERFCTR_UFC_SEL", + "counter": "RBBM_PERFCTR_UFC", + "countable_type": "a7xx_ufc_perfcounter_select" + }, + { + "name": "BV_CP", + "num": 7, + "select": "CP_BV_PERFCTR_CP_SEL", + "counter": "RBBM_PERFCTR2_CP", + "countable_type": "a7xx_cp_perfcounter_select" + }, + { + "name": "BV_PC", + "pipe": "BV", + "num": 8, + "select_offset": 8, + "select": "PC_PERFCTR_PC_SEL", + "counter": "RBBM_PERFCTR_BV_PC", + "countable_type": "a7xx_pc_perfcounter_select" + }, + { + "name": "BV_VFD", + "pipe": "BV", + "num": 8, + "select_offset": 8, + "select": "VFD_PERFCTR_VFD_SEL", + "counter": "RBBM_PERFCTR_BV_VFD", + "countable_type": "a7xx_vfd_perfcounter_select" + }, + { + "name": "BV_VPC", + "pipe": "BV", + "num": 6, + "select_offset": 6, + "select": "VPC_PERFCTR_VPC_SEL", + "counter": "RBBM_PERFCTR_BV_VPC", + "countable_type": "a7xx_vpc_perfcounter_select" + }, + { + "name": "BV_TP", + "pipe": "BV", + "num": 6, + "select_offset": 12, + "select": "TPL1_PERFCTR_TP_SEL", + "counter": "RBBM_PERFCTR2_TP", + "countable_type": "a7xx_tp_perfcounter_select" + }, + { + "name": "BV_SP", + "pipe": "BV", + "num": 12, + "select_offset": 24, + "select": "SP_PERFCTR_SP_SEL", + "counter": "RBBM_PERFCTR2_SP", + "countable_type": "a7xx_sp_perfcounter_select" + }, + { + "name": "BV_UFC", + "pipe": "BV", + "num": 2, + "select_offset": 4, + "select": "RB_PERFCTR_UFC_SEL", + "counter": "RBBM_PERFCTR2_UFC", + "countable_type": "a7xx_ufc_perfcounter_select" + }, + { + "name": "BV_TSE", + "pipe": "BV", + "num": 4, + "select": "GRAS_PERFCTR_TSE_SEL", + "counter": "RBBM_PERFCTR_BV_TSE", + "countable_type": "a7xx_tse_perfcounter_select" + }, + { + "name": "BV_RAS", + "pipe": "BV", + "num": 4, + "select": "GRAS_PERFCTR_RAS_SEL", + "counter": "RBBM_PERFCTR_BV_RAS", + "countable_type": "a7xx_ras_perfcounter_select" + }, + { + "name": "BV_LRZ", + "pipe": "BV", + "num": 4, + "select": "GRAS_PERFCTR_LRZ_SEL", + "counter": "RBBM_PERFCTR_BV_LRZ", + "countable_type": "a7xx_lrz_perfcounter_select" + }, + { + "name": "BV_HLSQ", + "pipe": "BV", + "num": 6, + "select": "SP_PERFCTR_HLSQ_SEL", + "counter": "RBBM_PERFCTR2_HLSQ", + "countable_type": "a7xx_hlsq_perfcounter_select" + } + ] +} diff --git a/drivers/gpu/drm/msm/registers/adreno/a8xx_perfcntrs.json b/drivers/gpu/drm/msm/registers/adreno/a8xx_perfcntrs.json new file mode 100644 index 000000000000..b20d0db088e3 --- /dev/null +++ b/drivers/gpu/drm/msm/registers/adreno/a8xx_perfcntrs.json @@ -0,0 +1,241 @@ +{ + "chip": "A8XX", + "groups": [ + { + "name": "CP", + "num": 14, + "reserved": [ 0 ], + "select": "CP_PERFCTR_CP_SEL", + "counter": "RBBM_PERFCTR_CP", + "countable_type": "a8xx_cp_perfcounter_select" + }, + { + "name": "RBBM", + "num": 4, + "select": "RBBM_PERFCTR_RBBM_SEL", + "slice_select": [ "RBBM_SLICE_PERFCTR_RBBM_SEL" ], + "counter": "RBBM_PERFCTR_RBBM", + "countable_type": "a8xx_rbbm_perfcounter_select" + }, + { + "name": "PC", + "pipe": "BR", + "num": 8, + "select": "PC_PERFCTR_PC_SEL", + "slice_select": [ "PC_SLICE_PERFCTR_PC_SEL" ], + "counter": "RBBM_PERFCTR_PC", + "countable_type": "a8xx_pc_perfcounter_select" + }, + { + "name": "VFD", + "pipe": "BR", + "num": 8, + "select": "VFD_PERFCTR_VFD_SEL", + "counter": "RBBM_PERFCTR_VFD", + "countable_type": "a8xx_vfd_perfcounter_select" + }, + { + "name": "HLSQ", + "pipe": "BR", + "num": 6, + "select": "SP_PERFCTR_HLSQ_SEL", + "slice_select": [ "SP_PERFCTR_HLSQ_SEL_2" ], + "counter": "RBBM_PERFCTR_HLSQ", + "countable_type": "a8xx_hlsq_perfcounter_select" + }, + { + "name": "VPC", + "pipe": "BR", + "num": 6, + "select": "VPC_PERFCTR_VPC_SEL", + "slice_select": [ "VPC_PERFCTR_VPC_SEL_1", "VPC_PERFCTR_VPC_SEL_2" ], + "counter": "RBBM_PERFCTR_VPC", + "countable_type": "a8xx_vpc_perfcounter_select" + }, + { + "name": "TSE", + "pipe": "BR", + "num": 4, + "select": "GRAS_PERFCTR_TSE_SEL", + "slice_select": [ "GRAS_PERFCTR_TSEFE_SEL" ], + "counter": "RBBM_PERFCTR_TSE", + "countable_type": "a8xx_tse_perfcounter_select" + }, + { + "name": "RAS", + "pipe": "BR", + "num": 4, + "select": "GRAS_PERFCTR_RAS_SEL", + "counter": "RBBM_PERFCTR_RAS", + "countable_type": "a8xx_ras_perfcounter_select" + }, + { + "name": "UCHE", + "num": 24, + "select": "UCHE_PERFCTR_UCHE_SEL", + "counter": "RBBM_PERFCTR_UCHE", + "countable_type": "a8xx_uche_perfcounter_select" + }, + { + "name": "TP", + "pipe": "BR", + "num": 12, + "select": "TPL1_PERFCTR_TP_SEL", + "counter": "RBBM_PERFCTR_TP", + "countable_type": "a8xx_tp_perfcounter_select" + }, + { + "name": "SP", + "pipe": "BR", + "num": 24, + "select": "SP_PERFCTR_SP_SEL", + "counter": "RBBM_PERFCTR_SP", + "countable_type": "a8xx_sp_perfcounter_select" + }, + { + "name": "RB", + "pipe": "BR", + "num": 8, + "select": "RB_PERFCTR_RB_SEL", + "counter": "RBBM_PERFCTR_RB", + "countable_type": "a8xx_rb_perfcounter_select" + }, + { + "name": "VSC", + "num": 2, + "select": "VSC_PERFCTR_VSC_SEL", + "counter": "RBBM_PERFCTR_VSC", + "countable_type": "a8xx_vsc_perfcounter_select" + }, + { + "name": "CCU", + "pipe": "BR", + "num": 5, + "select": "RB_PERFCTR_CCU_SEL", + "counter": "RBBM_PERFCTR_CCU", + "countable_type": "a8xx_ccu_perfcounter_select" + }, + { + "name": "LRZ", + "pipe": "BR", + "num": 4, + "select": "GRAS_PERFCTR_LRZ_SEL", + "counter": "RBBM_PERFCTR_LRZ", + "countable_type": "a8xx_lrz_perfcounter_select" + }, + { + "name": "CMP", + "pipe": "BR", + "num": 4, + "select": "RB_PERFCTR_CMP_SEL", + "counter": "RBBM_PERFCTR_CMP", + "countable_type": "a8xx_cmp_perfcounter_select" + }, + { + "name": "UFC", + "pipe": "BR", + "num": 4, + "select": "RB_PERFCTR_UFC_SEL", + "counter": "RBBM_PERFCTR_UFC", + "countable_type": "a8xx_ufc_perfcounter_select" + }, + { + "name": "BV_CP", + "num": 7, + "select_offset": 14, + "select": "CP_PERFCTR_CP_SEL", + "counter": "RBBM_PERFCTR2_CP", + "countable_type": "a8xx_cp_perfcounter_select" + }, + { + "name": "BV_PC", + "pipe": "BV", + "num": 8, + "select_offset": 8, + "select": "PC_PERFCTR_PC_SEL", + "slice_select": [ "PC_SLICE_PERFCTR_PC_SEL" ], + "counter": "RBBM_PERFCTR_BV_PC", + "countable_type": "a8xx_pc_perfcounter_select" + }, + { + "name": "BV_VFD", + "pipe": "BV", + "num": 8, + "select_offset": 8, + "select": "VFD_PERFCTR_VFD_SEL", + "counter": "RBBM_PERFCTR_BV_VFD", + "countable_type": "a8xx_vfd_perfcounter_select" + }, + { + "name": "BV_VPC", + "pipe": "BV", + "num": 6, + "select_offset": 6, + "select": "VPC_PERFCTR_VPC_SEL", + "slice_select": [ "VPC_PERFCTR_VPC_SEL_1", "VPC_PERFCTR_VPC_SEL_2" ], + "counter": "RBBM_PERFCTR_BV_VPC", + "countable_type": "a8xx_vpc_perfcounter_select" + }, + { + "name": "BV_TP", + "pipe": "BV", + "num": 8, + "select_offset": 12, + "select": "TPL1_PERFCTR_TP_SEL", + "counter": "RBBM_PERFCTR2_TP", + "countable_type": "a8xx_tp_perfcounter_select" + }, + { + "name": "BV_SP", + "pipe": "BV", + "num": 12, + "select_offset": 24, + "select": "SP_PERFCTR_SP_SEL", + "counter": "RBBM_PERFCTR2_SP", + "countable_type": "a8xx_sp_perfcounter_select" + }, + { + "name": "BV_UFC", + "pipe": "BV", + "num": 2, + "select_offset": 4, + "select": "RB_PERFCTR_UFC_SEL", + "counter": "RBBM_PERFCTR2_UFC", + "countable_type": "a8xx_ufc_perfcounter_select" + }, + { + "name": "BV_TSE", + "pipe": "BV", + "num": 4, + "select": "GRAS_PERFCTR_TSE_SEL", + "slice_select": [ "GRAS_PERFCTR_TSEFE_SEL" ], + "counter": "RBBM_PERFCTR_BV_TSE", + "countable_type": "a8xx_tse_perfcounter_select" + }, + { + "name": "BV_RAS", + "pipe": "BV", + "num": 4, + "select": "GRAS_PERFCTR_RAS_SEL", + "counter": "RBBM_PERFCTR_BV_RAS", + "countable_type": "a8xx_ras_perfcounter_select" + }, + { + "name": "BV_LRZ", + "pipe": "BV", + "num": 4, + "select": "GRAS_PERFCTR_LRZ_SEL", + "counter": "RBBM_PERFCTR_BV_LRZ", + "countable_type": "a8xx_lrz_perfcounter_select" + }, + { + "name": "BV_HLSQ", + "pipe": "BV", + "num": 6, + "select": "SP_PERFCTR_HLSQ_SEL", + "slice_select": [ "SP_PERFCTR_HLSQ_SEL_2" ], + "counter": "RBBM_PERFCTR2_HLSQ", + "countable_type": "a8xx_hlsq_perfcounter_select" + } + ] +} From c897d550f0b4efdb19e71622dc9ec0c5d1b4df70 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:40 -0700 Subject: [PATCH 51/67] drm/msm: Add a6xx+ perfcntr tables Wire up the generated perfcntr tables for a6xx+. The PERFCNTR_CONFIG ioctl will use this information to assign counters. Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728213/ Message-ID: <20260526145137.160554-7-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 15 +++++++++++++++ drivers/gpu/drm/msm/msm_gpu.h | 4 ++++ drivers/gpu/drm/msm/msm_perfcntr.h | 9 +++++++++ 3 files changed, 28 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 1c5ad3e83cc2..e9424d20277c 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -5,6 +5,7 @@ #include "msm_gem.h" #include "msm_mmu.h" #include "msm_gpu_trace.h" +#include "msm_perfcntr.h" #include "a6xx_gpu.h" #include "a6xx_gmu.xml.h" @@ -2559,6 +2560,20 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) adreno_gpu = &a6xx_gpu->base; gpu = &adreno_gpu->base; + if ((ADRENO_6XX_GEN1 <= config->info->family) && + (config->info->family <= ADRENO_6XX_GEN4)) { + gpu->perfcntr_groups = a6xx_perfcntr_groups; + gpu->num_perfcntr_groups = a6xx_num_perfcntr_groups; + } else if ((ADRENO_7XX_GEN1 <= config->info->family) && + (config->info->family <= ADRENO_7XX_GEN3)) { + gpu->perfcntr_groups = a7xx_perfcntr_groups; + gpu->num_perfcntr_groups = a7xx_num_perfcntr_groups; + } else if ((ADRENO_8XX_GEN1 <= config->info->family) && + (config->info->family <= ADRENO_8XX_GEN2)) { + gpu->perfcntr_groups = a8xx_perfcntr_groups; + gpu->num_perfcntr_groups = a8xx_num_perfcntr_groups; + } + mutex_init(&a6xx_gpu->gmu.lock); spin_lock_init(&a6xx_gpu->aperture_lock); diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 4b16b8069617..9b91d79dc277 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -24,6 +24,7 @@ struct msm_gem_submit; struct msm_gem_vm_log_entry; struct msm_gpu_state; struct msm_context; +struct msm_perfcntr_group; struct msm_gpu_config { const char *ioname; @@ -262,6 +263,9 @@ struct msm_gpu { bool allow_relocs; struct thermal_cooling_device *cooling; + + const struct msm_perfcntr_group *perfcntr_groups; + unsigned num_perfcntr_groups; }; static inline struct msm_gpu *dev_to_gpu(struct device *dev) diff --git a/drivers/gpu/drm/msm/msm_perfcntr.h b/drivers/gpu/drm/msm/msm_perfcntr.h index 75300bd69e80..8b0d627b2779 100644 --- a/drivers/gpu/drm/msm/msm_perfcntr.h +++ b/drivers/gpu/drm/msm/msm_perfcntr.h @@ -35,6 +35,15 @@ struct msm_perfcntr_group { const struct msm_perfcntr_counter *counters; }; +extern const struct msm_perfcntr_group a6xx_perfcntr_groups[]; +extern const unsigned a6xx_num_perfcntr_groups; + +extern const struct msm_perfcntr_group a7xx_perfcntr_groups[]; +extern const unsigned a7xx_num_perfcntr_groups; + +extern const struct msm_perfcntr_group a8xx_perfcntr_groups[]; +extern const unsigned a8xx_num_perfcntr_groups; + #define GROUP(_name, _pipe, _counters, _countables) { \ .name = _name, \ .pipe = _pipe, \ From 4250923945d05311f41411e8e9d3cffc72a9c523 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:41 -0700 Subject: [PATCH 52/67] drm/msm: Add sysprof accessors Currently the sysprof param serves two functions, (a) disabling perfcntr clearing on context switch/preemption, and (b) disabling IFPC. In the future, with kernel side global perfcntr collection/stream, the decision about disabling IFPC will change. To prepare for this, split out two helpers/accessors for the two different cases. For now, they are the same thing, but this will change. Signed-off-by: Rob Clark Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728214/ Message-ID: <20260526145137.160554-8-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 8 +++----- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 5 +++-- drivers/gpu/drm/msm/adreno/a6xx_preempt.c | 2 +- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 2 +- drivers/gpu/drm/msm/adreno/a8xx_preempt.c | 2 +- drivers/gpu/drm/msm/msm_gpu.h | 18 ++++++++++++++++++ 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index 616198a836a4..d26f8733672d 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -2080,10 +2080,10 @@ static int a6xx_gmu_get_irq(struct a6xx_gmu *gmu, struct platform_device *pdev, void a6xx_gmu_sysprof_setup(struct msm_gpu *gpu) { + bool sysprof = msm_gpu_sysprof_no_ifpc(gpu); struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); struct a6xx_gmu *gmu = &a6xx_gpu->gmu; - unsigned int sysprof_active; /* Nothing to do if GPU is suspended. We will handle this during GMU resume */ if (!pm_runtime_get_if_active(&gpu->pdev->dev)) @@ -2091,15 +2091,13 @@ void a6xx_gmu_sysprof_setup(struct msm_gpu *gpu) mutex_lock(&gmu->lock); - sysprof_active = refcount_read(&gpu->sysprof_active); - /* * 'Perfcounter select' register values are lost during IFPC collapse. To avoid that, * use the currently unused perfcounter oob vote to block IFPC when sysprof is active */ - if ((sysprof_active > 1) && !test_and_set_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status)) + if (sysprof && !test_and_set_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status)) a6xx_gmu_set_oob(gmu, GMU_OOB_PERFCOUNTER_SET); - else if ((sysprof_active == 1) && test_and_clear_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status)) + else if (!sysprof && test_and_clear_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status)) a6xx_gmu_clear_oob(gmu, GMU_OOB_PERFCOUNTER_SET); mutex_unlock(&gmu->lock); diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index e9424d20277c..d24601cb162d 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -203,7 +203,7 @@ static void get_stats_counter(struct msm_ringbuffer *ring, u32 counter, static void a6xx_set_pagetable(struct a6xx_gpu *a6xx_gpu, struct msm_ringbuffer *ring, struct msm_gem_submit *submit) { - bool sysprof = refcount_read(&a6xx_gpu->base.base.sysprof_active) > 1; + bool sysprof = msm_gpu_sysprof_no_perfcntr_zap(&a6xx_gpu->base.base); struct msm_context *ctx = submit->queue->ctx; struct drm_gpuvm *vm = msm_context_vm(submit->dev, ctx); struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; @@ -1531,7 +1531,7 @@ static int hw_init(struct msm_gpu *gpu) a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_BOOT_SLUMBER); } - if (!ret && (refcount_read(&gpu->sysprof_active) > 1)) { + if (!ret && msm_gpu_sysprof_no_ifpc(gpu)) { ret = a6xx_gmu_set_oob(gmu, GMU_OOB_PERFCOUNTER_SET); if (!ret) set_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status); @@ -2776,6 +2776,7 @@ const struct adreno_gpu_funcs a8xx_gpu_funcs = { .create_private_vm = a6xx_create_private_vm, .get_rptr = a6xx_get_rptr, .progress = a8xx_progress, + .sysprof_setup = a6xx_gmu_sysprof_setup, }, .init = a6xx_gpu_init, .get_timestamp = a8xx_gmu_get_timestamp, diff --git a/drivers/gpu/drm/msm/adreno/a6xx_preempt.c b/drivers/gpu/drm/msm/adreno/a6xx_preempt.c index df4cbf42e9a4..1e599d4ddea1 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_preempt.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_preempt.c @@ -261,7 +261,7 @@ void a6xx_preempt_trigger(struct msm_gpu *gpu) mod_timer(&a6xx_gpu->preempt_timer, jiffies + msecs_to_jiffies(10000)); /* Enable or disable postamble as needed */ - sysprof = refcount_read(&a6xx_gpu->base.base.sysprof_active) > 1; + sysprof = msm_gpu_sysprof_no_perfcntr_zap(gpu); if (!sysprof && !a6xx_gpu->postamble_enabled) preempt_prepare_postamble(a6xx_gpu); diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 86f2b1af9e2a..9cf307cf9fda 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -845,7 +845,7 @@ static int hw_init(struct msm_gpu *gpu) */ a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_GPU_SET); - if (!ret && (refcount_read(&gpu->sysprof_active) > 1)) { + if (!ret && msm_gpu_sysprof_no_perfcntr_zap(gpu)) { ret = a6xx_gmu_set_oob(gmu, GMU_OOB_PERFCOUNTER_SET); if (!ret) set_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status); diff --git a/drivers/gpu/drm/msm/adreno/a8xx_preempt.c b/drivers/gpu/drm/msm/adreno/a8xx_preempt.c index 3d8c33ba722e..6cb53a071801 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_preempt.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_preempt.c @@ -242,7 +242,7 @@ void a8xx_preempt_trigger(struct msm_gpu *gpu) mod_timer(&a6xx_gpu->preempt_timer, jiffies + msecs_to_jiffies(10000)); /* Enable or disable postamble as needed */ - sysprof = refcount_read(&a6xx_gpu->base.base.sysprof_active) > 1; + sysprof = msm_gpu_sysprof_no_perfcntr_zap(gpu); if (!sysprof && !a6xx_gpu->postamble_enabled) preempt_prepare_postamble(a6xx_gpu); diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 9b91d79dc277..bdafdf20f3f7 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -311,6 +311,24 @@ static inline bool msm_gpu_active(struct msm_gpu *gpu) return false; } +static inline bool +msm_gpu_sysprof_no_perfcntr_zap(struct msm_gpu *gpu) +{ + return refcount_read(&gpu->sysprof_active) > 1; +} + +static inline bool +msm_gpu_sysprof_no_ifpc(struct msm_gpu *gpu) +{ + /* + * For now, this is the same condition as disabling perfcntr clears + * on context switch. But once kernel perfcntr IFPC support is in + * place, we will only need to disable IFPC for legacy userspace + * setting SYSPROF param. + */ + return msm_gpu_sysprof_no_perfcntr_zap(gpu); +} + /* * The number of priority levels provided by drm gpu scheduler. The * DRM_SCHED_PRIORITY_KERNEL priority level is treated specially in some From 90f2b0461b0cf41b1553d9ec51fba0d6bb1695b2 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:42 -0700 Subject: [PATCH 53/67] drm/msm/a6xx: Add yield & flush helper It's a common pattern, needing to insert a yield packet before flushing the rb. And we'll need this once again for configuring perfcntr SEL regs. So add a helper. Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728208/ Message-ID: <20260526145137.160554-9-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 55 +++++++++++++-------------- drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 1 + drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 10 +---- 3 files changed, 28 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index d24601cb162d..e1ada0ebbe1a 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -189,6 +189,30 @@ void a6xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring) spin_unlock_irqrestore(&ring->preempt_lock, flags); } +void +a6xx_flush_yield(struct msm_gpu *gpu, struct msm_ringbuffer *ring) +{ + /* If preemption is enabled */ + if (gpu->nr_rings > 1) { + /* Yield the floor on command completion */ + OUT_PKT7(ring, CP_CONTEXT_SWITCH_YIELD, 4); + + /* + * If dword[2:1] are non zero, they specify an address for + * the CP to write the value of dword[3] to on preemption + * complete. Write 0 to skip the write + */ + OUT_RING(ring, 0x00); + OUT_RING(ring, 0x00); + /* Data value - not used if the address above is 0 */ + OUT_RING(ring, 0x01); + /* generate interrupt on preemption completion */ + OUT_RING(ring, 0x00); + } + + a6xx_flush(gpu, ring); +} + static void get_stats_counter(struct msm_ringbuffer *ring, u32 counter, u64 iova) { @@ -597,28 +621,9 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) OUT_PKT7(ring, CP_SET_MARKER, 1); OUT_RING(ring, 0x100); /* IFPC enable */ - /* If preemption is enabled */ - if (gpu->nr_rings > 1) { - /* Yield the floor on command completion */ - OUT_PKT7(ring, CP_CONTEXT_SWITCH_YIELD, 4); - - /* - * If dword[2:1] are non zero, they specify an address for - * the CP to write the value of dword[3] to on preemption - * complete. Write 0 to skip the write - */ - OUT_RING(ring, 0x00); - OUT_RING(ring, 0x00); - /* Data value - not used if the address above is 0 */ - OUT_RING(ring, 0x01); - /* generate interrupt on preemption completion */ - OUT_RING(ring, 0x00); - } - - trace_msm_gpu_submit_flush(submit, adreno_gpu->funcs->get_timestamp(gpu)); - a6xx_flush(gpu, ring); + a6xx_flush_yield(gpu, ring); /* Check to see if we need to start preemption */ if (adreno_is_a8xx(adreno_gpu)) @@ -881,15 +886,7 @@ static int a7xx_preempt_start(struct msm_gpu *gpu) a6xx_emit_set_pseudo_reg(ring, a6xx_gpu, NULL); - /* Yield the floor on command completion */ - OUT_PKT7(ring, CP_CONTEXT_SWITCH_YIELD, 4); - OUT_RING(ring, 0x00); - OUT_RING(ring, 0x00); - OUT_RING(ring, 0x00); - /* Generate interrupt on preemption completion */ - OUT_RING(ring, 0x00); - - a6xx_flush(gpu, ring); + a6xx_flush_yield(gpu, ring); return a6xx_idle(gpu, ring) ? 0 : -EINVAL; } diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h index eb431e5e00b1..99c3e55f5ca8 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h @@ -317,6 +317,7 @@ void a6xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu, bool gx_ void a6xx_gpu_sw_reset(struct msm_gpu *gpu, bool assert); int a6xx_fenced_write(struct a6xx_gpu *gpu, u32 offset, u64 value, u32 mask, bool is_64b); void a6xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring); +void a6xx_flush_yield(struct msm_gpu *gpu, struct msm_ringbuffer *ring); int a6xx_zap_shader_init(struct msm_gpu *gpu); void a8xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu, bool gx_off); diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 9cf307cf9fda..f29cd6e1fde0 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -484,15 +484,7 @@ static int a8xx_preempt_start(struct msm_gpu *gpu) a6xx_emit_set_pseudo_reg(ring, a6xx_gpu, NULL); - /* Yield the floor on command completion */ - OUT_PKT7(ring, CP_CONTEXT_SWITCH_YIELD, 4); - OUT_RING(ring, 0x00); - OUT_RING(ring, 0x00); - OUT_RING(ring, 0x00); - /* Generate interrupt on preemption completion */ - OUT_RING(ring, 0x00); - - a6xx_flush(gpu, ring); + a6xx_flush_yield(gpu, ring); return a8xx_idle(gpu, ring) ? 0 : -EINVAL; } From 8766dbc37d7caac76785fd3dc1d8a8f87355bf35 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:43 -0700 Subject: [PATCH 54/67] drm/msm: Add per-context perfcntr state The upcoming PERFCNTR_CONFIG ioctl will allow for both global counter collection, and per-context counter reservation for local (ie. within a single GEM_SUBMIT ioctl) counter collection. Any number of contexts can reserve the same counters, but we will need to ensure that counters reserved for local counter collection do not conflict with counters used for global counter collection. So add tracking for per-context local counter reservations. Signed-off-by: Rob Clark Reviewed-by: Dmitry Baryshkov Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728211/ Message-ID: <20260526145137.160554-10-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gpu.h | 5 +++++ drivers/gpu/drm/msm/msm_perfcntr.h | 21 +++++++++++++++++++++ drivers/gpu/drm/msm/msm_submitqueue.c | 1 + 3 files changed, 27 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index bdafdf20f3f7..677cd7d509d7 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -434,6 +434,11 @@ struct msm_context { * this context. */ atomic64_t ctx_mem; + + /** + * @perfcntrs: Per-context reserved perfcntrs state + */ + struct msm_perfcntr_context_state *perfctx; }; struct drm_gpuvm *msm_context_vm(struct drm_device *dev, struct msm_context *ctx); diff --git a/drivers/gpu/drm/msm/msm_perfcntr.h b/drivers/gpu/drm/msm/msm_perfcntr.h index 8b0d627b2779..d73f1ad9039d 100644 --- a/drivers/gpu/drm/msm/msm_perfcntr.h +++ b/drivers/gpu/drm/msm/msm_perfcntr.h @@ -35,6 +35,27 @@ struct msm_perfcntr_group { const struct msm_perfcntr_counter *counters; }; +/** + * struct msm_perfcntr_context_state - per-msm_context counter state + * + * A given counter can either be unused, reserved for global counter + * collection exclusively, or reserved for local per-context counter + * collection inclusively. Multiple contexts can reserve the same + * counter, since SEL reg programming and counter begin/end sampling + * happen locally (within a single GEM_SUBMIT ioctl). + */ +struct msm_perfcntr_context_state { + /** @dummy: Some compilers dislike structs with only a flex array */ + unsigned dummy; + + /** + * @reserved_counters: + * + * The number of reserved counters indexed by perfcntr group. + */ + unsigned reserved_counters[]; +}; + extern const struct msm_perfcntr_group a6xx_perfcntr_groups[]; extern const unsigned a6xx_num_perfcntr_groups; diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c index 2598d674a99d..a58fe41602c6 100644 --- a/drivers/gpu/drm/msm/msm_submitqueue.c +++ b/drivers/gpu/drm/msm/msm_submitqueue.c @@ -66,6 +66,7 @@ void __msm_context_destroy(struct kref *kref) drm_gpuvm_put(ctx->vm); kfree(ctx->comm); kfree(ctx->cmdline); + kfree(ctx->perfctx); kfree(ctx); } From 02195633635c42db9ca29f3c9fa3a758bc1a2cee Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:44 -0700 Subject: [PATCH 55/67] drm/msm: Add basic perfcntr infrastructure Add the basic infrastructure for tracking assigned perfcntrs. Signed-off-by: Rob Clark Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728212/ Message-ID: <20260526145137.160554-11-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/adreno/adreno_device.c | 8 +- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 5 +- drivers/gpu/drm/msm/msm_drv.h | 6 + drivers/gpu/drm/msm/msm_gpu.c | 12 ++ drivers/gpu/drm/msm/msm_gpu.h | 57 ++++++++- drivers/gpu/drm/msm/msm_perfcntr.c | 133 +++++++++++++++++++++ drivers/gpu/drm/msm/msm_perfcntr.h | 23 ++++ 8 files changed, 239 insertions(+), 6 deletions(-) create mode 100644 drivers/gpu/drm/msm/msm_perfcntr.c diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 363c984a62e6..d0c3a4c6703b 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -122,6 +122,7 @@ msm-y += \ msm_gpu_devfreq.o \ msm_io_utils.o \ msm_iommu.o \ + msm_perfcntr.o \ msm_rd.o \ msm_ringbuffer.o \ msm_submitqueue.o \ diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c index fc38331ce640..7f20320ef66a 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_device.c +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -307,8 +307,10 @@ MODULE_DEVICE_TABLE(of, dt_match); static int adreno_runtime_resume(struct device *dev) { struct msm_gpu *gpu = dev_to_gpu(dev); - - return gpu->funcs->pm_resume(gpu); + int ret = gpu->funcs->pm_resume(gpu); + if (!ret) + ret = msm_perfcntr_resume(gpu); + return ret; } static int adreno_runtime_suspend(struct device *dev) @@ -322,6 +324,8 @@ static int adreno_runtime_suspend(struct device *dev) */ WARN_ON_ONCE(gpu->active_submits); + msm_perfcntr_suspend(gpu); + return gpu->funcs->pm_suspend(gpu); } diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 69918975f711..c62c45bb0ddb 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -708,11 +708,10 @@ void adreno_recover(struct msm_gpu *gpu) struct drm_device *dev = gpu->dev; int ret; - // XXX pm-runtime?? we *need* the device to be off after this - // so maybe continuing to call ->pm_suspend/resume() is better? - + msm_perfcntr_suspend(gpu); gpu->funcs->pm_suspend(gpu); gpu->funcs->pm_resume(gpu); + msm_perfcntr_resume(gpu); ret = msm_gpu_hw_init(gpu); if (ret) { diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index e53e4f220bed..f00b2e7aeb91 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -235,6 +235,12 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, int msm_ioctl_vm_bind(struct drm_device *dev, void *data, struct drm_file *file); +int msm_perfcntr_resume(struct msm_gpu *gpu); +void msm_perfcntr_suspend(struct msm_gpu *gpu); + +struct msm_perfcntr_state * msm_perfcntr_init(struct msm_gpu *gpu); +void msm_perfcntr_cleanup(struct msm_gpu *gpu); + #ifdef CONFIG_DEBUG_FS unsigned long msm_gem_shrinker_shrink(struct drm_device *dev, unsigned long nr_to_scan); #endif diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 08940bae3b6e..18ed00e5f143 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -1010,6 +1010,17 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, refcount_set(&gpu->sysprof_active, 1); + mutex_init(&gpu->perfcntr_lock); + + if (gpu->num_perfcntr_groups > 0) { + gpu->perfcntrs = msm_perfcntr_init(gpu); + if (IS_ERR(gpu->perfcntrs)) { + ret = PTR_ERR(gpu->perfcntrs); + gpu->perfcntrs = NULL; + goto fail; + } + } + return 0; fail: @@ -1048,6 +1059,7 @@ void msm_gpu_cleanup(struct msm_gpu *gpu) } msm_devfreq_cleanup(gpu); + msm_perfcntr_cleanup(gpu); platform_set_drvdata(gpu->pdev, NULL); } diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 677cd7d509d7..ac124d562037 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -25,6 +25,7 @@ struct msm_gem_vm_log_entry; struct msm_gpu_state; struct msm_context; struct msm_perfcntr_group; +struct msm_perfcntr_stream; struct msm_gpu_config { const char *ioname; @@ -93,6 +94,13 @@ struct msm_gpu_funcs { */ bool (*progress)(struct msm_gpu *gpu, struct msm_ringbuffer *ring); void (*sysprof_setup)(struct msm_gpu *gpu); + + /* Configure perfcntr SELect regs: */ + void (*perfcntr_configure)(struct msm_gpu *gpu, struct msm_ringbuffer *ring, + const struct msm_perfcntr_stream *stream); + + /* Flush perfcntrs before reading (optional): */ + void (*perfcntr_flush)(struct msm_gpu *gpu); }; /* Additional state for iommu faults: */ @@ -266,6 +274,11 @@ struct msm_gpu { const struct msm_perfcntr_group *perfcntr_groups; unsigned num_perfcntr_groups; + + struct msm_perfcntr_state *perfcntrs; + + /** @perfcntr_lock: protects perfcntr related state */ + struct mutex perfcntr_lock; }; static inline struct msm_gpu *dev_to_gpu(struct device *dev) @@ -311,10 +324,52 @@ static inline bool msm_gpu_active(struct msm_gpu *gpu) return false; } +/** + * struct msm_perfcntr_group_state - Tracking for the currently allocated counter state + */ +struct msm_perfcntr_group_state { + /** + * @allocated_counters: + * + * allocated counters for global counter collection. The + * corresponding counters are allocated from highest to + * lowest, to minimize chance of conflict with old userspace + * allocating from lowest to highest. + */ + unsigned allocated_counters; + + /** + * @countables: + * + * The corresponding SELect reg values for the allocated counters + */ + uint32_t countables[]; +}; + +/** + * struct msm_perfcntr_state - overall global perfcntr state + */ +struct msm_perfcntr_state { + /** @stream: current global counter stream if active */ + struct msm_perfcntr_stream *stream; + + /** + * @groups: Global perfcntr stream group state. + * + * Conceptually this is part of msm_perfcntr_stream state, but is + * statically pre-allocated when the gpu is initialized to simplify + * error path cleanup in PERFCNTR_CONFIG ioctl. (__free(kfree) + * doesn't really help with variable length arrays of allocated + * pointers.) + */ + struct msm_perfcntr_group_state *groups[]; +}; + static inline bool msm_gpu_sysprof_no_perfcntr_zap(struct msm_gpu *gpu) { - return refcount_read(&gpu->sysprof_active) > 1; + return (refcount_read(&gpu->sysprof_active) > 1) || + (gpu->perfcntrs && READ_ONCE(gpu->perfcntrs->stream)); } static inline bool diff --git a/drivers/gpu/drm/msm/msm_perfcntr.c b/drivers/gpu/drm/msm/msm_perfcntr.c new file mode 100644 index 000000000000..aeea60cd002e --- /dev/null +++ b/drivers/gpu/drm/msm/msm_perfcntr.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include "msm_drv.h" +#include "msm_gpu.h" +#include "msm_perfcntr.h" + +static int +msm_perfcntr_resume_locked(struct msm_perfcntr_stream *stream) +{ + return 0; +} + +int +msm_perfcntr_resume(struct msm_gpu *gpu) +{ + if (!gpu->perfcntrs) + return 0; + guard(mutex)(&gpu->perfcntr_lock); + return msm_perfcntr_resume_locked(gpu->perfcntrs->stream); +} + +static void +msm_perfcntr_suspend_locked(struct msm_perfcntr_stream *stream) +{ +} + +void +msm_perfcntr_suspend(struct msm_gpu *gpu) +{ + if (!gpu->perfcntrs) + return; + guard(mutex)(&gpu->perfcntr_lock); + msm_perfcntr_suspend_locked(gpu->perfcntrs->stream); +} + +/** + * msm_perfcntr_group_idx - map idx of perfcntr group to group_idx + * @stream: The global perfcntr stream + * @n: The requested group_idx + * + * The PERFCNTR_CONFIG ioctl requested N counters/countables per perfcntr + * group, but the order of groups is not required to match the order they + * are defined in the perfcntr tables (which is not stable/UABI, only the + * group names are UABI). + * + * But the order samples are returned in the stream should match the + * order they are requested in the PERFCNTR_CONFIG ioctl. This helper + * handles the order remapping. + * + * Returns an index into gpu->perfcntr_groups[] and perfcntrs->groups[]. + */ +uint32_t +msm_perfcntr_group_idx(const struct msm_perfcntr_stream *stream, uint32_t n) +{ + WARN_ON_ONCE(n >= stream->nr_groups); + return stream->group_idx[n]; +} + +/** + * msm_perfcntr_counter_base - get idx of the first counter in group + * @stream: The global perfcntr stream + * @group_idx: the index of the counter group + * + * For global counter collection, counters are allocated from the end + * (last counter) while UMD allocates them from the start (0..N-1). + * Since UMD always allocated them from the start this also minimizes + * the chance of conflict when using old UMD which predates + * PERFCNTR_CONFIG ioctl. + * + * Returns the index of first counter to use. An index into + * msm_perfcntr_group::counters[]. + */ +uint32_t +msm_perfcntr_counter_base(const struct msm_perfcntr_stream *stream, uint32_t group_idx) +{ + struct msm_gpu *gpu = stream->gpu; + struct msm_perfcntr_state *perfcntrs = gpu->perfcntrs; + unsigned num_counters = gpu->perfcntr_groups[group_idx].num_counters; + unsigned allocated_counters = perfcntrs->groups[group_idx]->allocated_counters; + + return num_counters - allocated_counters; +} + +static void +__msm_perfcntr_cleanup(struct msm_gpu *gpu, struct msm_perfcntr_state *perfcntrs) +{ + struct device *dev = &gpu->pdev->dev; + + for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++) + devm_kfree(dev, perfcntrs->groups[i]); + + devm_kfree(dev, perfcntrs); +} + +void +msm_perfcntr_cleanup(struct msm_gpu *gpu) +{ + if (!gpu->perfcntrs) + return; + + __msm_perfcntr_cleanup(gpu, gpu->perfcntrs); + gpu->perfcntrs = NULL; +} + +struct msm_perfcntr_state * +msm_perfcntr_init(struct msm_gpu *gpu) +{ + struct msm_perfcntr_state *perfcntrs; + struct device *dev = &gpu->pdev->dev; + size_t sz; + + sz = struct_size(perfcntrs, groups, gpu->num_perfcntr_groups); + perfcntrs = devm_kzalloc(dev, sz, GFP_KERNEL); + if (!perfcntrs) + return ERR_PTR(-ENOMEM); + + for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++) { + const struct msm_perfcntr_group *group = + &gpu->perfcntr_groups[i]; + + sz = struct_size(perfcntrs->groups[i], countables, group->num_counters); + perfcntrs->groups[i] = devm_kzalloc(dev, sz, GFP_KERNEL); + if (!perfcntrs->groups[i]) { + __msm_perfcntr_cleanup(gpu, perfcntrs); + return ERR_PTR(-ENOMEM); + } + } + + return perfcntrs; +} diff --git a/drivers/gpu/drm/msm/msm_perfcntr.h b/drivers/gpu/drm/msm/msm_perfcntr.h index d73f1ad9039d..b5a00ab7362b 100644 --- a/drivers/gpu/drm/msm/msm_perfcntr.h +++ b/drivers/gpu/drm/msm/msm_perfcntr.h @@ -35,6 +35,29 @@ struct msm_perfcntr_group { const struct msm_perfcntr_counter *counters; }; +/** + * struct msm_perfcntr_stream - state for a single open stream fd + */ +struct msm_perfcntr_stream { + /** @gpu: Back-link to the GPU */ + struct msm_gpu *gpu; + + /** @nr_groups: # of counter groups with enabled counters */ + uint32_t nr_groups; + + /** + * @group_idx: array of nr_groups + * + * Maps the order of groups in PERFCNTR_CONFIG ioctl to group idx, + * so that results in the results stream can be ordered to match + * the ioctl call that setup the stream + */ + uint32_t *group_idx; +}; + +uint32_t msm_perfcntr_group_idx(const struct msm_perfcntr_stream *stream, uint32_t n); +uint32_t msm_perfcntr_counter_base(const struct msm_perfcntr_stream *stream, uint32_t group_idx); + /** * struct msm_perfcntr_context_state - per-msm_context counter state * From 5eedc8c2d0b52a44cecbd12641524b3616070c0c Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:45 -0700 Subject: [PATCH 56/67] drm/msm/a6xx+: Add support to configure perfcntrs Add support to configure counter SELect regs. In some cases the reg writes need to happen while the GPU is idle. And for a7xx+, in some cases SEL regs need to be configured from BV or BR aperture. The easiest way to deal with this is to configure from the RB. Signed-off-by: Rob Clark Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728215/ Message-ID: <20260526145137.160554-12-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 69 +++++++++++++++++++++++++++ drivers/gpu/drm/msm/msm_perfcntr.h | 3 ++ drivers/gpu/drm/msm/msm_ringbuffer.h | 2 + 3 files changed, 74 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index e1ada0ebbe1a..e47fa2cdc314 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -2458,6 +2458,71 @@ static bool a6xx_progress(struct msm_gpu *gpu, struct msm_ringbuffer *ring) return progress; } +static void +a6xx_perfcntr_configure(struct msm_gpu *gpu, struct msm_ringbuffer *ring, + const struct msm_perfcntr_stream *stream) +{ + enum adreno_pipe pipe = PIPE_NONE; + + for (unsigned i = 0; i < stream->nr_groups; i++) { + unsigned group_idx = msm_perfcntr_group_idx(stream, i); + unsigned base = msm_perfcntr_counter_base(stream, group_idx); + + const struct msm_perfcntr_group *group = + &gpu->perfcntr_groups[group_idx]; + + struct msm_perfcntr_group_state *group_state = + gpu->perfcntrs->groups[group_idx]; + + if (group->pipe != pipe) { + pipe = group->pipe; + + OUT_PKT7(ring, CP_THREAD_CONTROL, 1); + + if (pipe == PIPE_BR) { + OUT_RING(ring, CP_SET_THREAD_BR); + } else if (pipe == PIPE_BV) { + OUT_RING(ring, CP_SET_THREAD_BV); + } else { + OUT_RING(ring, CP_SET_THREAD_BOTH); + } + } + + const struct msm_perfcntr_counter *counter = &group->counters[base]; + unsigned nr = group_state->allocated_counters; + OUT_PKT4(ring, counter->select_reg, nr); + for (unsigned c = 0; c < nr; c++) + OUT_RING(ring, group_state->countables[c]); + + for (unsigned s = 0; s < ARRAY_SIZE(counter->slice_select_regs); s++) { + if (!counter->slice_select_regs[s]) + break; + + OUT_PKT4(ring, counter->slice_select_regs[s], nr); + for (unsigned c = 0; c < nr; c++) + OUT_RING(ring, group_state->countables[c]); + } + } + + if (pipe != PIPE_NONE) { + OUT_PKT7(ring, CP_THREAD_CONTROL, 1); + OUT_RING(ring, CP_SET_THREAD_BOTH); + } + + OUT_PKT7(ring, CP_MEM_WRITE, 3); + OUT_RING(ring, lower_32_bits(rbmemptr(ring, perfcntr_fence))); + OUT_RING(ring, upper_32_bits(rbmemptr(ring, perfcntr_fence))); + OUT_RING(ring, stream->sel_fence); + + a6xx_flush_yield(gpu, ring); + + /* Check to see if we need to start preemption */ + if (adreno_is_a8xx(to_adreno_gpu(gpu))) + a8xx_preempt_trigger(gpu); + else + a6xx_preempt_trigger(gpu); +} + static u32 fuse_to_supp_hw(const struct adreno_info *info, u32 fuse) { if (!info->speedbins) @@ -2675,6 +2740,7 @@ const struct adreno_gpu_funcs a6xx_gpu_funcs = { .get_rptr = a6xx_get_rptr, .progress = a6xx_progress, .sysprof_setup = a6xx_gmu_sysprof_setup, + .perfcntr_configure = a6xx_perfcntr_configure, }, .init = a6xx_gpu_init, .get_timestamp = a6xx_gmu_get_timestamp, @@ -2708,6 +2774,7 @@ const struct adreno_gpu_funcs a6xx_gmuwrapper_funcs = { .create_private_vm = a6xx_create_private_vm, .get_rptr = a6xx_get_rptr, .progress = a6xx_progress, + .perfcntr_configure = a6xx_perfcntr_configure, }, .init = a6xx_gpu_init, .get_timestamp = a6xx_get_timestamp, @@ -2744,6 +2811,7 @@ const struct adreno_gpu_funcs a7xx_gpu_funcs = { .get_rptr = a6xx_get_rptr, .progress = a6xx_progress, .sysprof_setup = a6xx_gmu_sysprof_setup, + .perfcntr_configure = a6xx_perfcntr_configure, }, .init = a6xx_gpu_init, .get_timestamp = a6xx_gmu_get_timestamp, @@ -2774,6 +2842,7 @@ const struct adreno_gpu_funcs a8xx_gpu_funcs = { .get_rptr = a6xx_get_rptr, .progress = a8xx_progress, .sysprof_setup = a6xx_gmu_sysprof_setup, + .perfcntr_configure = a6xx_perfcntr_configure, }, .init = a6xx_gpu_init, .get_timestamp = a8xx_gmu_get_timestamp, diff --git a/drivers/gpu/drm/msm/msm_perfcntr.h b/drivers/gpu/drm/msm/msm_perfcntr.h index b5a00ab7362b..2ceedec5296b 100644 --- a/drivers/gpu/drm/msm/msm_perfcntr.h +++ b/drivers/gpu/drm/msm/msm_perfcntr.h @@ -45,6 +45,9 @@ struct msm_perfcntr_stream { /** @nr_groups: # of counter groups with enabled counters */ uint32_t nr_groups; + /** @sel_fence: Fence for SEL reg programming */ + uint32_t sel_fence; + /** * @group_idx: array of nr_groups * diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h index d1e49f701c81..28ca8c9f7463 100644 --- a/drivers/gpu/drm/msm/msm_ringbuffer.h +++ b/drivers/gpu/drm/msm/msm_ringbuffer.h @@ -37,6 +37,8 @@ struct msm_rbmemptrs { volatile struct msm_gpu_submit_stats stats[MSM_GPU_SUBMIT_STATS_COUNT]; volatile u64 ttbr0; volatile u32 context_idr; + + volatile u32 perfcntr_fence; }; struct msm_cp_state { From 5ef26a2a4b2e55b07984e17baec179b849b52bcc Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:46 -0700 Subject: [PATCH 57/67] drm/msm/a8xx: Add perfcntr flush sequence With the slice architecture, we need to flush the slice and unslice counters to perf RAM before reading counters. Signed-off-by: Rob Clark Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728216/ Message-ID: <20260526145137.160554-13-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 1 + drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 1 + drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index e47fa2cdc314..2178b4960979 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -2843,6 +2843,7 @@ const struct adreno_gpu_funcs a8xx_gpu_funcs = { .progress = a8xx_progress, .sysprof_setup = a6xx_gmu_sysprof_setup, .perfcntr_configure = a6xx_perfcntr_configure, + .perfcntr_flush = a8xx_perfcntr_flush, }, .init = a6xx_gpu_init, .get_timestamp = a8xx_gmu_get_timestamp, diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h index 99c3e55f5ca8..3491a24a9320 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h @@ -334,5 +334,6 @@ void a8xx_preempt_hw_init(struct msm_gpu *gpu); void a8xx_preempt_trigger(struct msm_gpu *gpu); void a8xx_preempt_irq(struct msm_gpu *gpu); bool a8xx_progress(struct msm_gpu *gpu, struct msm_ringbuffer *ring); +void a8xx_perfcntr_flush(struct msm_gpu *gpu); void a8xx_recover(struct msm_gpu *gpu); #endif /* __A6XX_GPU_H__ */ diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index f29cd6e1fde0..3adf25030548 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -1346,3 +1346,23 @@ bool a8xx_progress(struct msm_gpu *gpu, struct msm_ringbuffer *ring) { return true; } + +void a8xx_perfcntr_flush(struct msm_gpu *gpu) +{ + u32 val; + + /* + * Flush delta counters (both perf counters and pipe stats) present in + * RBBM_S and RBBM_US to perf RAM logic to get the latest data. + */ + gpu_write(gpu, REG_A8XX_RBBM_PERFCTR_FLUSH_HOST_CMD, BIT(0)); + gpu_write(gpu, REG_A8XX_RBBM_SLICE_PERFCTR_FLUSH_HOST_CMD, BIT(0)); + + /* Ensure all writes are posted before polling status register */ + wmb(); + + if (gpu_poll_timeout(gpu, REG_A8XX_RBBM_PERFCTR_FLUSH_HOST_STATUS, val, + val & BIT(0), 100, 100 * 1000)) { + dev_err(&gpu->pdev->dev, "Perfcounter flush timed out: status=0x%08x\n", val); + } +} From a7b378c949373b6bb4c5a89ffc53085736959dcf Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:47 -0700 Subject: [PATCH 58/67] drm/msm: Add PERFCNTR_CONFIG ioctl Add new UABI and implementation of PERFCNTR_CONFIG ioctl. A bit more work is required to configure the pwrup_reglist for the GMU to restore SELect regs on exit of IFPC, before we can stop disabling IFPC while global counter collection. This will follow in a later commit, but will be transparent to userspace. Signed-off-by: Rob Clark Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728217/ Message-ID: <20260526145137.160554-14-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_drv.c | 1 + drivers/gpu/drm/msm/msm_drv.h | 2 + drivers/gpu/drm/msm/msm_gpu.h | 3 + drivers/gpu/drm/msm/msm_perfcntr.c | 529 +++++++++++++++++++++++++++++ drivers/gpu/drm/msm/msm_perfcntr.h | 51 +++ include/uapi/drm/msm_drm.h | 48 +++ 6 files changed, 634 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 66322eab7890..79f78e7e16e9 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -801,6 +801,7 @@ static const struct drm_ioctl_desc msm_ioctls[] = { DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_CLOSE, msm_ioctl_submitqueue_close, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_QUERY, msm_ioctl_submitqueue_query, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_VM_BIND, msm_ioctl_vm_bind, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_PERFCNTR_CONFIG, msm_ioctl_perfcntr_config, DRM_RENDER_ALLOW), }; static void msm_show_fdinfo(struct drm_printer *p, struct drm_file *file) diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index f00b2e7aeb91..204e140ac8e9 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -237,6 +237,8 @@ int msm_ioctl_vm_bind(struct drm_device *dev, void *data, int msm_perfcntr_resume(struct msm_gpu *gpu); void msm_perfcntr_suspend(struct msm_gpu *gpu); +int msm_ioctl_perfcntr_config(struct drm_device *dev, void *data, + struct drm_file *file); struct msm_perfcntr_state * msm_perfcntr_init(struct msm_gpu *gpu); void msm_perfcntr_cleanup(struct msm_gpu *gpu); diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index ac124d562037..c9c9a3b75b68 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -353,6 +353,9 @@ struct msm_perfcntr_state { /** @stream: current global counter stream if active */ struct msm_perfcntr_stream *stream; + /** @sel_seqno: counter for sel_fence */ + uint32_t sel_seqno; + /** * @groups: Global perfcntr stream group state. * diff --git a/drivers/gpu/drm/msm/msm_perfcntr.c b/drivers/gpu/drm/msm/msm_perfcntr.c index aeea60cd002e..0a6f8039f610 100644 --- a/drivers/gpu/drm/msm/msm_perfcntr.c +++ b/drivers/gpu/drm/msm/msm_perfcntr.c @@ -3,13 +3,44 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ +#include "drm/drm_file.h" +#include "drm/msm_drm.h" + +#include "linux/anon_inodes.h" +#include "linux/gfp_types.h" +#include "linux/poll.h" +#include "linux/slab.h" + #include "msm_drv.h" #include "msm_gpu.h" #include "msm_perfcntr.h" +#include "adreno/adreno_gpu.h" + +/* space used: */ +#define fifo_count(stream) \ + (CIRC_CNT((stream)->fifo.head, (stream)->fifo.tail, (stream)->fifo_size)) +#define fifo_count_to_end(stream) \ + (CIRC_CNT_TO_END(smp_load_acquire(&(stream)->fifo.head), (stream)->fifo.tail, (stream)->fifo_size)) +/* space available: */ +#define fifo_space(stream) \ + (CIRC_SPACE((stream)->fifo.head, (stream)->fifo.tail, (stream)->fifo_size)) + static int msm_perfcntr_resume_locked(struct msm_perfcntr_stream *stream) { + if (!stream) + return 0; + + /* Reprogram SEL regs on highest priority rb: */ + struct msm_ringbuffer *ring = stream->gpu->rb[0]; + + queue_work(ring->sched.submit_wq, &stream->sel_work); + + hrtimer_start(&stream->sample_timer, + ns_to_ktime(stream->sample_period_ns), + HRTIMER_MODE_REL_PINNED); + return 0; } @@ -25,6 +56,22 @@ msm_perfcntr_resume(struct msm_gpu *gpu) static void msm_perfcntr_suspend_locked(struct msm_perfcntr_stream *stream) { + if (!stream) + return; + + hrtimer_cancel(&stream->sample_timer); + kthread_cancel_work_sync(&stream->sample_work); + + /* + * We can't use cancel_work_sync() here, since sel_work acquires + * gpu->lock which (a) in suspend path can already be held, or + * (b) in release path would invert the order of gpu->lock and + * gpu->perfcntr_lock. Either would cause deadlock. + */ + cancel_work(&stream->sel_work); + + stream->sel_fence = ++stream->gpu->perfcntrs->sel_seqno; + stream->seqno = 0; } void @@ -36,6 +83,488 @@ msm_perfcntr_suspend(struct msm_gpu *gpu) msm_perfcntr_suspend_locked(gpu->perfcntrs->stream); } +static int +msm_perfcntrs_stream_release(struct inode *inode, struct file *file) +{ + struct msm_perfcntr_stream *stream = file->private_data; + struct msm_gpu *gpu = stream->gpu; + + scoped_guard (mutex, &gpu->perfcntr_lock) { + struct msm_perfcntr_state *perfcntrs = gpu->perfcntrs; + + msm_perfcntr_suspend_locked(stream); + perfcntrs->stream = NULL; + + /* release previously allocated counters: */ + for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++) + perfcntrs->groups[i]->allocated_counters = 0; + } + + /* + * In the suspend path we use async cancel_work(), to avoid blocking + * on sel_work, which acquires gpu->lock (which could deadlock since + * other paths acquire gpu->lock before perfcntr_lock) or already + * hold gpu->lock. + * + * But since we are freeing the stream, after dropping perfcntr_lock + * we need to block until sel_work is done: + */ + cancel_work_sync(&stream->sel_work); + + kfree(stream->group_idx); + kfree(stream->fifo.buf); + kfree(stream); + + return 0; +} + +static __poll_t +msm_perfcntrs_stream_poll(struct file *file, poll_table *wait) +{ + struct msm_perfcntr_stream *stream = file->private_data; + __poll_t events = 0; + + poll_wait(file, &stream->poll_wq, wait); + + /* Are there samples to read? */ + if (fifo_count(stream) > 0) + events |= EPOLLIN; + + return events; +} + +static ssize_t +msm_perfcntrs_stream_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct msm_perfcntr_stream *stream = file->private_data; + int ret; + + if (!(file->f_flags & O_NONBLOCK)) { + ret = wait_event_interruptible(stream->poll_wq, + fifo_count(stream) > 0); + if (ret) + return ret; + } + + guard(mutex)(&stream->read_lock); + + struct circ_buf *fifo = &stream->fifo; + const char *fptr = &fifo->buf[fifo->tail]; + + count = min_t(size_t, count, fifo_count_to_end(stream)); + if (!count) + return -EAGAIN; + if (copy_to_user(buf, fptr, count)) + return -EFAULT; + + smp_store_release(&fifo->tail, (fifo->tail + count) & (stream->fifo_size - 1)); + *ppos += count; + + return count; +} + +static const struct file_operations stream_fops = { + .owner = THIS_MODULE, + .release = msm_perfcntrs_stream_release, + .poll = msm_perfcntrs_stream_poll, + .read = msm_perfcntrs_stream_read, +}; + +static void +sel_worker(struct work_struct *w) +{ + struct msm_perfcntr_stream *stream = + container_of(w, typeof(*stream), sel_work); + struct msm_gpu *gpu = stream->gpu; + /* Reprogram SEL regs on highest priority rb: */ + struct msm_ringbuffer *ring = stream->gpu->rb[0]; + + /* + * If in the process of resuming, wait for that. Otherwise sel_worker + * which is enqueued in the resume path can be scheduled before the + * resume completes. + */ + pm_runtime_barrier(&gpu->pdev->dev); + + /* + * sel_work could end up scheduled before suspend, but running + * after. See msm_perfcntr_suspend_locked() + * + * So if we end up running sel_work after the GPU is already + * suspended, just bail. It will be scheduled again after + * the GPU is resumed. + */ + if (!pm_runtime_get_if_active(&gpu->pdev->dev)) + return; + + scoped_guard (mutex, &gpu->lock) { + guard(mutex)(&gpu->perfcntr_lock); + + if (stream == gpu->perfcntrs->stream) { + msm_gpu_hw_init(gpu); + gpu->funcs->perfcntr_configure(gpu, ring, stream); + } + } + + pm_runtime_put_autosuspend(&gpu->pdev->dev); +} + +static void +sample_write(struct msm_perfcntr_stream *stream, int *head, const void *buf, size_t sz) +{ + /* + * FIFO size is power-of-two, and guaranteed to have enough space to + * fit what we are writing. So we should not hit the wrap-around + * point writing things that are power-of-two sized + */ + WARN_ON(CIRC_SPACE_TO_END(*head, stream->fifo.tail, stream->fifo_size) < sz); + + memcpy(&stream->fifo.buf[*head], buf, sz); + + /* Advance head, wrapping around if necessary: */ + *head = (*head + sz) & (stream->fifo_size - 1); +} + +static void +sample_write_u32(struct msm_perfcntr_stream *stream, int *head, uint32_t val) +{ + sample_write(stream, head, &val, sizeof(val)); +} + +static void +sample_write_u64(struct msm_perfcntr_stream *stream, int *head, uint64_t val) +{ + sample_write(stream, head, &val, sizeof(val)); +} + +static void +sample_worker(struct kthread_work *work) +{ + struct msm_perfcntr_stream *stream = + container_of(work, typeof(*stream), sample_work); + struct msm_gpu *gpu = stream->gpu; + struct msm_rbmemptrs *memptrs = gpu->rb[0]->memptrs; + + if (memptrs->perfcntr_fence != stream->sel_fence) + return; + + /* + * Ensure we have enough space to capture a sample period's + * worth of data: + */ + if (stream->period_size > fifo_space(stream)) { + stream->seqno = 0; + return; + } + + if (gpu->funcs->perfcntr_flush) + gpu->funcs->perfcntr_flush(gpu); + + /* Keep local copy of head to avoid updating fifo until the end: */ + int head = stream->fifo.head; + + /* + * We expect the GPU to be powered at this point, as the timer + * and kthread work are canceled/flushed in the suspend path: + */ + sample_write_u64(stream, &head, + to_adreno_gpu(gpu)->funcs->get_timestamp(gpu)); + sample_write_u32(stream, &head, stream->seqno++); + sample_write_u32(stream, &head, 0); + + for (unsigned i = 0; i < stream->nr_groups; i++) { + unsigned group_idx = msm_perfcntr_group_idx(stream, i); + unsigned base = msm_perfcntr_counter_base(stream, group_idx); + + const struct msm_perfcntr_group *group = + &gpu->perfcntr_groups[group_idx]; + + struct msm_perfcntr_group_state *group_state = + gpu->perfcntrs->groups[group_idx]; + + unsigned nr = group_state->allocated_counters; + for (unsigned j = 0; j < nr; j++) { + const struct msm_perfcntr_counter *counter = + &group->counters[j + base]; + uint64_t val = gpu_read64(gpu, counter->counter_reg_lo); + sample_write_u64(stream, &head, val); + } + } + + smp_store_release(&stream->fifo.head, head); + wake_up_all(&stream->poll_wq); +} + +static enum hrtimer_restart +sample_timer(struct hrtimer *hrtimer) +{ + struct msm_perfcntr_stream *stream = + container_of(hrtimer, typeof(*stream), sample_timer); + + kthread_queue_work(stream->gpu->worker, &stream->sample_work); + + hrtimer_forward_now(hrtimer, ns_to_ktime(stream->sample_period_ns)); + + return HRTIMER_RESTART; +} + +static int +get_group_idx(struct msm_gpu *gpu, const char *name, size_t len) +{ + for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++) { + const struct msm_perfcntr_group *group = + &gpu->perfcntr_groups[i]; + if (!strncmp(group->name, name, len)) + return i; + } + + return -1; +} + +static int +get_available_counters(struct msm_gpu *gpu, int group_idx, uint32_t flags) +{ + struct msm_perfcntr_state *perfcntrs = gpu->perfcntrs; + + /* + * For local counter reservation, anything that is not used by + * global perfcntr stream is available: + */ + if (!(flags & MSM_PERFCNTR_STREAM)) { + return gpu->perfcntr_groups[group_idx].num_counters - + perfcntrs->groups[group_idx]->allocated_counters; + } + + /* + * For global counter collection, anything that is not reserved by + * one or more contexts is available: + */ + guard(mutex)(&gpu->dev->filelist_mutex); + + unsigned reserved_counters = 0; + struct drm_file *file; + + list_for_each_entry (file, &gpu->dev->filelist, lhead) { + struct msm_context *ctx = file->driver_priv; + + if (!ctx || !ctx->perfctx) + continue; + + unsigned n = ctx->perfctx->reserved_counters[group_idx]; + reserved_counters = max(reserved_counters, n); + } + + return gpu->perfcntr_groups[group_idx].num_counters - reserved_counters; +} + +int +msm_ioctl_perfcntr_config(struct drm_device *dev, void *data, struct drm_file *file) +{ + struct msm_drm_private *priv = dev->dev_private; + const struct drm_msm_perfcntr_config *args = data; + struct msm_context *ctx = file->driver_priv; + struct msm_gpu *gpu = priv->gpu; + int stream_fd = 0; + + if (!gpu || !gpu->num_perfcntr_groups) + return -ENXIO; + + struct msm_perfcntr_state *perfcntrs = gpu->perfcntrs; + + /* + * Validate args that don't require locks/power first: + */ + + if (args->flags & ~MSM_PERFCNTR_FLAGS) + return UERR(EINVAL, dev, "invalid flags"); + + if (args->nr_groups && !args->group_stride) + return UERR(EINVAL, dev, "invalid group_stride"); + + if (args->nr_groups > gpu->num_perfcntr_groups) + return UERR(EINVAL, dev, "too many groups"); + + if (args->nr_groups && !args->groups) + return UERR(EINVAL, dev, "no groups"); + + if (args->flags & MSM_PERFCNTR_STREAM) { + if (!perfmon_capable()) + return UERR(EPERM, dev, "invalid permissions"); + if (!args->nr_groups) + return UERR(EINVAL, dev, "invalid nr_groups"); + if (!args->period) + return UERR(EINVAL, dev, "invalid sampling period"); + if (args->bufsz_shift > const_ilog2(SZ_128M)) + return UERR(EINVAL, dev, "buffer size too big (>128M)"); + } else { + if (args->period) + return UERR(EINVAL, dev, "sampling period not allowed"); + if (args->bufsz_shift) + return UERR(EINVAL, dev, "sample buf size not allowed"); + } + + /* + * To avoid iterating over the groups multiple times, allocate and setup + * both a ctx and global stream object. Only one of the two will be + * kept in the end. + */ + + struct msm_perfcntr_context_state *perfctx __free(kfree) = kzalloc( + struct_size(perfctx, reserved_counters, gpu->num_perfcntr_groups), + GFP_KERNEL); + if (!perfctx) + return -ENOMEM; + + struct msm_perfcntr_stream *stream __free(kfree) = kzalloc_obj(*stream); + if (!stream) + return -ENOMEM; + + uint8_t *nr_counters __free(kfree) = kzalloc_objs(uint8_t, gpu->num_perfcntr_groups); + if (!nr_counters) + return -ENOMEM; + + uint32_t *group_idx __free(kfree) = kzalloc_objs(uint32_t, args->nr_groups); + if (!group_idx) + return -ENOMEM; + + stream->gpu = gpu; + stream->sample_period_ns = args->period; + stream->nr_groups = args->nr_groups; + stream->fifo_size = 1ull << args->bufsz_shift; + + mutex_init(&stream->read_lock); + + guard(mutex)(&gpu->perfcntr_lock); + + if (args->flags & MSM_PERFCNTR_STREAM) { + if (perfcntrs->stream) + return UERR(EBUSY, dev, "perfcntr stream already open"); + } + + size_t bufsz = 16; /* header size includes seqno and 64b timestamp: */ + int ret = 0; + + for (unsigned i = 0; i < args->nr_groups; i++) { + struct drm_msm_perfcntr_group g = {0}; + size_t sz = min_t(size_t, args->group_stride, sizeof(g)); + void __user *userptr = + u64_to_user_ptr(args->groups + (i * args->group_stride)); + + if (copy_from_user(&g, userptr, sz)) + return -EFAULT; + + if (g.pad) + return UERR(EINVAL, dev, "groups[%d]: invalid pad", i); + + int idx = get_group_idx(gpu, g.group_name, sizeof(g.group_name)); + + if (idx < 0) + return UERR(EINVAL, dev, "groups[%d]: unknown group", i); + + if (nr_counters[idx]) + return UERR(EINVAL, dev, "groups[%d]: duplicate group", i); + + if (g.nr_countables > gpu->perfcntr_groups[idx].num_counters) + return UERR(EINVAL, dev, "groups[%d]: too many counters", i); + + if (args->flags & MSM_PERFCNTR_STREAM) { + if (g.nr_countables && !g.countables) + return UERR(EINVAL, dev, "groups[%d]: no countables", i); + } else { + if (g.countables) + return UERR(EINVAL, dev, "groups[%d]: countables should be NULL", i); + } + + int avail_counters = get_available_counters(gpu, idx, args->flags); + if (g.nr_countables > avail_counters) { + /* + * Defer error return until we process all groups, in + * case there are other E2BIG groups: + */ + ret = UERR(E2BIG, dev, "groups[%d]: too few counters available", i); + + if (args->flags & MSM_PERFCNTR_UPDATE) { + /* Let userspace know how many counters are actually avail: */ + g.nr_countables = avail_counters; + if (copy_to_user(userptr, &g, sz)) + return -EFAULT; + } + } + + group_idx[i] = idx; + perfctx->reserved_counters[idx] = g.nr_countables; + + /* +1 to catch duplicate zero sized groups: */ + nr_counters[idx] = g.nr_countables + 1; + + if (args->flags & MSM_PERFCNTR_STREAM) { + size_t sz = sizeof(uint32_t) * g.nr_countables; + void __user *userptr = u64_to_user_ptr(g.countables); + + if (copy_from_user(perfcntrs->groups[idx]->countables, userptr, sz)) + return -EFAULT; + + /* Samples are 64b per countable: */ + bufsz += 2 * sz; + } + } + + if (ret) + return ret; + + if (args->flags & MSM_PERFCNTR_STREAM) { + /* + * Validate requested buffer size is large enough for at least + * a single sample period. + * + * Note the circ_buf implementation needs to be 1 byte larger + * than max it can hold (see CIRC_SPACE()). + */ + if (stream->fifo_size <= bufsz) + return UERR(EINVAL, dev, "required buffer size: %zu", bufsz); + + /* There aren't enough counters to hit this limit: */ + WARN_ON(bufsz > SZ_128M); + + stream->period_size = bufsz; + + void *buf __free(kfree) = kmalloc(stream->fifo_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + FD_PREPARE(fdf, O_CLOEXEC, + anon_inode_getfile("[msm_perfcntrs]", &stream_fops, stream, 0)); + if (fdf.err) + return fdf.err; + + INIT_WORK(&stream->sel_work, sel_worker); + kthread_init_work(&stream->sample_work, sample_worker); + init_waitqueue_head(&stream->poll_wq); + hrtimer_setup(&stream->sample_timer, sample_timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + + stream->sel_fence = ++perfcntrs->sel_seqno; + stream->group_idx = no_free_ptr(group_idx); + stream->fifo.buf = no_free_ptr(buf); + + /* commit the allocated counters, subtracting off original +1: */ + for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++) + perfcntrs->groups[i]->allocated_counters = nr_counters[i] - 1; + + perfcntrs->stream = no_free_ptr(stream); + + msm_perfcntr_resume_locked(perfcntrs->stream); + + stream_fd = fd_publish(fdf); + } else { + kfree(ctx->perfctx); + ctx->perfctx = no_free_ptr(perfctx); + } + + return stream_fd; +} + /** * msm_perfcntr_group_idx - map idx of perfcntr group to group_idx * @stream: The global perfcntr stream diff --git a/drivers/gpu/drm/msm/msm_perfcntr.h b/drivers/gpu/drm/msm/msm_perfcntr.h index 2ceedec5296b..0feeb81c531f 100644 --- a/drivers/gpu/drm/msm/msm_perfcntr.h +++ b/drivers/gpu/drm/msm/msm_perfcntr.h @@ -7,6 +7,11 @@ #define __MSM_PERFCNTR_H__ #include "linux/array_size.h" +#include "linux/circ_buf.h" +#include "linux/hrtimer.h" +#include "linux/kthread.h" +#include "linux/wait.h" +#include "linux/workqueue.h" #include "adreno_common.xml.h" @@ -42,12 +47,49 @@ struct msm_perfcntr_stream { /** @gpu: Back-link to the GPU */ struct msm_gpu *gpu; + /** @sample_timer: Timer to sample counters */ + struct hrtimer sample_timer; + + /** @poll_wq: Wait queue for waiting for OA data to be available */ + wait_queue_head_t poll_wq; + + /** @sample_period_ns: Sampling period */ + uint64_t sample_period_ns; + /** @nr_groups: # of counter groups with enabled counters */ uint32_t nr_groups; + /** @seqno: counter for collected samples */ + uint32_t seqno; + /** @sel_fence: Fence for SEL reg programming */ uint32_t sel_fence; + /** + * @sel_work: Worker for SEL reg programming + * + * Initial SEL reg programming (as opposed to restoring the SEL + * regs on runpm resume) must run on the same ordered wq as is + * used by drm_sched, to serialize it with GEM_SUBMITs written + * into the same ringbuffer. + */ + struct work_struct sel_work; + + /** + * @sample_work: Worker for collecting samples + */ + struct kthread_work sample_work; + + /** + * @read_lock: + * + * Fifo access is synchronied on the producer side by virtue + * of there being a single timer collecting samples and writing + * into the fifo. It is protected on the consumer side by + * @read_lock. + */ + struct mutex read_lock; + /** * @group_idx: array of nr_groups * @@ -56,6 +98,15 @@ struct msm_perfcntr_stream { * the ioctl call that setup the stream */ uint32_t *group_idx; + + /** @fifo: circular buffer for samples */ + struct circ_buf fifo; + + /** @fifo_size: circular buffer size */ + size_t fifo_size; + + /** @period_size: size of data for single sampling period */ + size_t period_size; }; uint32_t msm_perfcntr_group_idx(const struct msm_perfcntr_stream *stream, uint32_t n); diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index b99098792371..7f2e594be4eb 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -491,6 +491,52 @@ struct drm_msm_submitqueue_query { __u32 pad; }; +#define MSM_PERFCNTR_STREAM 0x00000001 +#define MSM_PERFCNTR_UPDATE 0x00000002 +#define MSM_PERFCNTR_FLAGS ( \ + MSM_PERFCNTR_STREAM | \ + MSM_PERFCNTR_UPDATE | \ + 0) + +struct drm_msm_perfcntr_group { + char group_name[16]; + __u32 nr_countables; + __u32 pad; /* mbz */ + __u64 countables; /* pointer to an array of nr_countables u32 */ +}; + +/* + * Note, for MSM_PERFCNTR_STREAM, the ioctl returns an fd to read recorded + * counters. This only works because the ioctl is DRM_IOW(), if we returned + * a out param in the ioctl struct the copy_to_user() (in drm_ioctl()) + * could fault, causing us to leak the fd. + * + * If the ioctl returns with error E2BIG, that means more counters/countables + * are requested than are currently available. If MSM_PERFCNTR_UPDATE flag + * is set, drm_msm_perfcntr_group::nr_countables will be updated to return + * the actual # of counters available. + * + * The data read from the has the following format for each sampling period: + * + * uint64_t timestamp; // CP_ALWAYS_ON_COUNTER captured at sample time + * uint32_t seqno; // increments by 1 each period, reset to 0 on discontinuity + * uint32_t mbz; // pad out counters to 64b + * struct { + * uint64_t counter[nr_countables]; + * } groups[nr_groups]; + * + * The ordering of groups and counters matches the order in PERFCNTR_CONFIG + * ioctl. + */ +struct drm_msm_perfcntr_config { + __u32 flags; /* bitmask of MSM_PERFCNTR_x */ + __u32 nr_groups; /* # of entries in groups array */ + __u64 groups; /* pointer to array of drm_msm_perfcntr_group */ + __u64 period; /* sampling period in ns */ + __u32 bufsz_shift; /* sample buffer size in bytes is 1< Date: Tue, 26 May 2026 07:50:48 -0700 Subject: [PATCH 59/67] drm/msm/a6xx: Increase pwrup_reglist size To make room for appending SEL reg programming. Without increasing the size, we would overflow the pwrup_reglist at ~190 counters on gen8. Or possibly fewer, considering that some gen8 counter groups also have separate slice vs unslice SELectors. Signed-off-by: Rob Clark Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728228/ Message-ID: <20260526145137.160554-15-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 2 +- drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 2178b4960979..8613d21cecb5 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -1106,7 +1106,7 @@ static int a6xx_ucode_load(struct msm_gpu *gpu) msm_gem_object_set_name(a6xx_gpu->shadow_bo, "shadow"); } - a6xx_gpu->pwrup_reglist_ptr = msm_gem_kernel_new(gpu->dev, PAGE_SIZE, + a6xx_gpu->pwrup_reglist_ptr = msm_gem_kernel_new(gpu->dev, PWRUP_REGLIST_SIZE, MSM_BO_WC | MSM_BO_MAP_PRIV, gpu->vm, &a6xx_gpu->pwrup_reglist_bo, &a6xx_gpu->pwrup_reglist_iova); diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h index 3491a24a9320..d3f0b40787db 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h @@ -96,6 +96,7 @@ struct a6xx_gpu { uint32_t *shadow; struct drm_gem_object *pwrup_reglist_bo; +#define PWRUP_REGLIST_SIZE (2 * PAGE_SIZE) void *pwrup_reglist_ptr; uint64_t pwrup_reglist_iova; bool pwrup_reglist_emitted; From 45d4a295b307b67c8a05d5d31a31385d0893553c Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:49 -0700 Subject: [PATCH 60/67] drm/msm/a6xx: Append SEL regs to dyn pwrup reglist This is needed so that SEL reg values are restored on exit from IFPC. Signed-off-by: Rob Clark Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728218/ Message-ID: <20260526145137.160554-16-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 84 +++++++++++++++++++++++++-- drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 11 +++- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 1 + 3 files changed, 89 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 8613d21cecb5..4bb3be3ff9d0 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -869,6 +869,7 @@ static void a7xx_patch_pwrup_reglist(struct msm_gpu *gpu) A7XX_CP_APERTURE_CNTL_HOST_PIPE(PIPE_NONE)); } lock->dynamic_list_len = dyn_pwrup_reglist_count; + a6xx_gpu->dynamic_sel_reglist_offset = dyn_pwrup_reglist_count; } static int a7xx_preempt_start(struct msm_gpu *gpu) @@ -2458,11 +2459,60 @@ static bool a6xx_progress(struct msm_gpu *gpu, struct msm_ringbuffer *ring) return progress; } +static void +perfcntr_select(struct msm_ringbuffer *ring, enum adreno_pipe pipe, + uint32_t regidx, uint32_t *countables, uint32_t nr, + uint32_t **reglist) +{ + OUT_PKT4(ring, regidx, nr); + for (unsigned i = 0; i < nr; i++) + OUT_RING(ring, countables[i]); + + if (!*reglist) + return; + + for (unsigned i = 0; i < nr; i++) { + /* + * Bitfield is in same position on a7xx, but only 2 bits.. + * which is sufficient for NONE/BR/BV: + */ + *(*reglist)++ = A8XX_CP_APERTURE_CNTL_HOST_PIPEID(pipe); + *(*reglist)++ = regidx + i; + *(*reglist)++ = countables[i]; + } +} + static void a6xx_perfcntr_configure(struct msm_gpu *gpu, struct msm_ringbuffer *ring, const struct msm_perfcntr_stream *stream) { + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); enum adreno_pipe pipe = PIPE_NONE; + uint32_t *reglist = NULL; + uint32_t *reglist_sel_start; + + if (to_adreno_gpu(gpu)->info->family >= ADRENO_7XX_GEN1) { + WARN_ON(!a6xx_gpu->pwrup_reglist_emitted); + + struct cpu_gpu_lock *lock = a6xx_gpu->pwrup_reglist_ptr; + int off = (2 * lock->ifpc_list_len) + + (2 * lock->preemption_list_len) + + (3 * a6xx_gpu->dynamic_sel_reglist_offset); + + reglist = (uint32_t *)&lock->regs[0]; + reglist += off; + reglist_sel_start = reglist; + + /* Clear any previously configured SEL reg entries: */ + lock->dynamic_list_len = a6xx_gpu->dynamic_sel_reglist_offset; + + /* + * Ensure CP sees the dynamic_list_len update before we + * start modifying the SEL entries: + */ + dma_wmb(); + } for (unsigned i = 0; i < stream->nr_groups; i++) { unsigned group_idx = msm_perfcntr_group_idx(stream, i); @@ -2490,17 +2540,15 @@ a6xx_perfcntr_configure(struct msm_gpu *gpu, struct msm_ringbuffer *ring, const struct msm_perfcntr_counter *counter = &group->counters[base]; unsigned nr = group_state->allocated_counters; - OUT_PKT4(ring, counter->select_reg, nr); - for (unsigned c = 0; c < nr; c++) - OUT_RING(ring, group_state->countables[c]); + perfcntr_select(ring, pipe, counter->select_reg, + group_state->countables, nr, ®list); for (unsigned s = 0; s < ARRAY_SIZE(counter->slice_select_regs); s++) { if (!counter->slice_select_regs[s]) break; - OUT_PKT4(ring, counter->slice_select_regs[s], nr); - for (unsigned c = 0; c < nr; c++) - OUT_RING(ring, group_state->countables[c]); + perfcntr_select(ring, pipe, counter->slice_select_regs[s], + group_state->countables, nr, ®list); } } @@ -2514,6 +2562,30 @@ a6xx_perfcntr_configure(struct msm_gpu *gpu, struct msm_ringbuffer *ring, OUT_RING(ring, upper_32_bits(rbmemptr(ring, perfcntr_fence))); OUT_RING(ring, stream->sel_fence); + /* + * Update the pwrup reglist size before flushing. Kgsl does a shared- + * memory spinlock dance with SQE to avoid racing with IFPC exit. But + * we can skip that since the ringbuffer programming will be executed + * by SQE after dynamic reglist size is updated. So even if we lose + * the race, the register programming in the rb will overwrite/correct + * the SEL regs restored by SQE on IFPC exit, before sampling begins. + */ + if (reglist) { + struct cpu_gpu_lock *lock = a6xx_gpu->pwrup_reglist_ptr; + unsigned nr_regs = (reglist - reglist_sel_start) / 3; + + /* + * Ensure CP sees updates to the pwrup_reglist before it + * sees the new (increased) length: + */ + dma_wmb(); + + /* Update dynamic reglist len to include new SEL reg programming: */ + lock->dynamic_list_len = a6xx_gpu->dynamic_sel_reglist_offset + nr_regs; + + WARN_ON_ONCE(reglist > (uint32_t *)((uint8_t *)lock + PWRUP_REGLIST_SIZE)); + } + a6xx_flush_yield(gpu, ring); /* Check to see if we need to start preemption */ diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h index d3f0b40787db..b72fb58bf223 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h @@ -21,17 +21,19 @@ struct cpu_gpu_lock { uint32_t cpu_req; uint32_t turn; union { + /* a6xx: */ struct { uint16_t list_length; uint16_t list_offset; }; + /* a7xx+: */ struct { uint8_t ifpc_list_len; uint8_t preemption_list_len; uint16_t dynamic_list_len; }; }; - uint64_t regs[62]; + uint64_t regs[]; }; /** @@ -101,6 +103,13 @@ struct a6xx_gpu { uint64_t pwrup_reglist_iova; bool pwrup_reglist_emitted; + /* + * Offset of start of SEL regs appended to pwrup_reglist. This + * is equal to lock->dynamic_list_len if no SEL regs are appended + * to the end of the dynamic reglist. + */ + uint16_t dynamic_sel_reglist_offset; + bool has_whereami; void __iomem *llc_mmio; diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 3adf25030548..abe100d9aeaa 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -464,6 +464,7 @@ static void a8xx_patch_pwrup_reglist(struct msm_gpu *gpu) } lock->dynamic_list_len = dyn_pwrup_reglist_count; + a6xx_gpu->dynamic_sel_reglist_offset = dyn_pwrup_reglist_count; done: a8xx_aperture_clear(gpu); From 632ad5510114eb3866bc519bd216860d48ccf77c Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 26 May 2026 07:50:50 -0700 Subject: [PATCH 61/67] drm/msm/a6xx: Allow IFPC with perfcntr stream Now that the dynamic pwrup reglist has SEL reg values to restore appended, so that SEL regs are restored on IFPC exit, we can stop completely disabling IFPC while global counter sampling is active. To accomplish this, we re-use sysprof_setup() with a force_on param to inhibit IFPC specifically while the counter regs are being read, while leaving IFPC enabled the rest of the time. Signed-off-by: Rob Clark Reviewed-by: Anna Maniscalco Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/728219/ Message-ID: <20260526145137.160554-17-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 4 ++-- drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 2 +- drivers/gpu/drm/msm/msm_gpu.h | 10 ++-------- drivers/gpu/drm/msm/msm_perfcntr.c | 8 ++++++++ drivers/gpu/drm/msm/msm_submitqueue.c | 2 +- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index d26f8733672d..47b5702e1b40 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -2078,9 +2078,9 @@ static int a6xx_gmu_get_irq(struct a6xx_gmu *gmu, struct platform_device *pdev, return irq; } -void a6xx_gmu_sysprof_setup(struct msm_gpu *gpu) +void a6xx_gmu_sysprof_setup(struct msm_gpu *gpu, bool force_on) { - bool sysprof = msm_gpu_sysprof_no_ifpc(gpu); + bool sysprof = msm_gpu_sysprof_no_ifpc(gpu) || force_on; struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); struct a6xx_gmu *gmu = &a6xx_gpu->gmu; diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h index b72fb58bf223..f60a0801a62d 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h @@ -281,7 +281,7 @@ void a6xx_gmu_clear_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state); int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node); int a6xx_gmu_wrapper_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node); void a6xx_gmu_remove(struct a6xx_gpu *a6xx_gpu); -void a6xx_gmu_sysprof_setup(struct msm_gpu *gpu); +void a6xx_gmu_sysprof_setup(struct msm_gpu *gpu, bool force_on); void a6xx_preempt_init(struct msm_gpu *gpu); void a6xx_preempt_hw_init(struct msm_gpu *gpu); diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index c9c9a3b75b68..6c83b8cbbb90 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -93,7 +93,7 @@ struct msm_gpu_funcs { * for cmdstream that is buffered in this FIFO upstream of the CP fw. */ bool (*progress)(struct msm_gpu *gpu, struct msm_ringbuffer *ring); - void (*sysprof_setup)(struct msm_gpu *gpu); + void (*sysprof_setup)(struct msm_gpu *gpu, bool force_on); /* Configure perfcntr SELect regs: */ void (*perfcntr_configure)(struct msm_gpu *gpu, struct msm_ringbuffer *ring, @@ -378,13 +378,7 @@ msm_gpu_sysprof_no_perfcntr_zap(struct msm_gpu *gpu) static inline bool msm_gpu_sysprof_no_ifpc(struct msm_gpu *gpu) { - /* - * For now, this is the same condition as disabling perfcntr clears - * on context switch. But once kernel perfcntr IFPC support is in - * place, we will only need to disable IFPC for legacy userspace - * setting SYSPROF param. - */ - return msm_gpu_sysprof_no_perfcntr_zap(gpu); + return refcount_read(&gpu->sysprof_active) > 1; } /* diff --git a/drivers/gpu/drm/msm/msm_perfcntr.c b/drivers/gpu/drm/msm/msm_perfcntr.c index 0a6f8039f610..ce65b1160955 100644 --- a/drivers/gpu/drm/msm/msm_perfcntr.c +++ b/drivers/gpu/drm/msm/msm_perfcntr.c @@ -258,6 +258,10 @@ sample_worker(struct kthread_work *work) return; } + /* Inhibit IFPC while accessing registers: */ + if (gpu->funcs->sysprof_setup) + gpu->funcs->sysprof_setup(gpu, true); + if (gpu->funcs->perfcntr_flush) gpu->funcs->perfcntr_flush(gpu); @@ -292,6 +296,10 @@ sample_worker(struct kthread_work *work) } } + /* Re-enable IFPC: */ + if (gpu->funcs->sysprof_setup) + gpu->funcs->sysprof_setup(gpu, false); + smp_store_release(&stream->fifo.head, head); wake_up_all(&stream->poll_wq); } diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c index a58fe41602c6..1a5a77b28016 100644 --- a/drivers/gpu/drm/msm/msm_submitqueue.c +++ b/drivers/gpu/drm/msm/msm_submitqueue.c @@ -42,7 +42,7 @@ int msm_context_set_sysprof(struct msm_context *ctx, struct msm_gpu *gpu, int sy /* Some gpu families require additional setup for sysprof */ if (gpu->funcs->sysprof_setup) - gpu->funcs->sysprof_setup(gpu); + gpu->funcs->sysprof_setup(gpu, false); ctx->sysprof = sysprof; From 2baf19c14ea342ad6f943148f5e9605340928008 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Thu, 28 May 2026 09:48:30 +0000 Subject: [PATCH 62/67] dt-bindings: display/msm/gmu: Document Adreno 810 GMU Document Adreno 810 GMU in the dt-binding specification. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Akhil P Oommen Signed-off-by: Alexander Koskovich Patchwork: https://patchwork.freedesktop.org/patch/728802/ Message-ID: <20260528-adreno-810-v7-1-7fe7fdd97fc2@pm.me> Signed-off-by: Rob Clark --- .../devicetree/bindings/display/msm/gmu.yaml | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Documentation/devicetree/bindings/display/msm/gmu.yaml b/Documentation/devicetree/bindings/display/msm/gmu.yaml index 93e5e6e19754..8578c2f8122e 100644 --- a/Documentation/devicetree/bindings/display/msm/gmu.yaml +++ b/Documentation/devicetree/bindings/display/msm/gmu.yaml @@ -300,6 +300,36 @@ allOf: required: - qcom,qmp + - if: + properties: + compatible: + contains: + const: qcom,adreno-gmu-810.0 + then: + properties: + reg: + items: + - description: Core GMU registers + reg-names: + items: + - const: gmu + clocks: + items: + - description: GPU AHB clock + - description: GMU clock + - description: GPU CX clock + - description: GPU AXI clock + - description: GPU MEMNOC clock + - description: GMU HUB clock + clock-names: + items: + - const: ahb + - const: gmu + - const: cxo + - const: axi + - const: memnoc + - const: hub + - if: properties: compatible: From b780f888d9fc2303b666d30f5078c069e496fc71 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Thu, 28 May 2026 09:48:39 +0000 Subject: [PATCH 63/67] dt-bindings: display/msm/gpu: Document Adreno 810 GPU Document the GPU compatible string used for the Adreno 810. Reviewed-by: Akhil P Oommen Signed-off-by: Alexander Koskovich Patchwork: https://patchwork.freedesktop.org/patch/728804/ Message-ID: <20260528-adreno-810-v7-2-7fe7fdd97fc2@pm.me> Signed-off-by: Rob Clark --- Documentation/devicetree/bindings/display/msm/gpu.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/display/msm/gpu.yaml b/Documentation/devicetree/bindings/display/msm/gpu.yaml index 77caacd0fb3f..a40899e5ea58 100644 --- a/Documentation/devicetree/bindings/display/msm/gpu.yaml +++ b/Documentation/devicetree/bindings/display/msm/gpu.yaml @@ -415,7 +415,9 @@ allOf: properties: compatible: contains: - const: qcom,adreno-44070001 + enum: + - qcom,adreno-44010000 + - qcom,adreno-44070001 then: properties: reg: @@ -449,6 +451,7 @@ allOf: - qcom,adreno-43050a01 - qcom,adreno-43050c01 - qcom,adreno-43051401 + - qcom,adreno-44010000 - qcom,adreno-44070001 then: # Starting with A6xx, the clocks are usually defined in the GMU node From d8bf2eddd6520c5806334a22d817f58df8c8c018 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Thu, 28 May 2026 09:48:47 +0000 Subject: [PATCH 64/67] drm/msm/adreno: rename llc_mmio to cx_misc_mmio This region is used for more than just LLCC, it also provides access to software fuse values (raytracing, etc). Rename relevant symbols from _llc to _cx_misc for use in a follow up change that decouples this from LLCC. Reviewed-by: Akhil P Oommen Reviewed-by: Konrad Dybcio Signed-off-by: Alexander Koskovich Patchwork: https://patchwork.freedesktop.org/patch/728806/ Message-ID: <20260528-adreno-810-v7-3-7fe7fdd97fc2@pm.me> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 8 ++++---- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 16 ++++++++-------- drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 14 +++++++------- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index 47b5702e1b40..2e5d7b53a0c3 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -947,7 +947,7 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state) /* Turn on TCM (Tightly Coupled Memory) retention */ if (adreno_is_a7xx(adreno_gpu)) - a6xx_llc_write(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL, 1); + a6xx_cx_misc_write(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL, 1); else if (!adreno_is_a8xx(adreno_gpu)) gmu_write(gmu, REG_A6XX_GMU_GENERAL_7, 1); @@ -1215,7 +1215,7 @@ static int a6xx_gmu_secure_init(struct a6xx_gpu *a6xx_gpu) if (!qcom_scm_is_available()) { dev_warn_once(gpu->dev->dev, "SCM is not available, poking fuse register\n"); - a6xx_llc_write(a6xx_gpu, REG_A7XX_CX_MISC_SW_FUSE_VALUE, + a6xx_cx_misc_write(a6xx_gpu, REG_A7XX_CX_MISC_SW_FUSE_VALUE, A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING | A7XX_CX_MISC_SW_FUSE_VALUE_FASTBLEND | A7XX_CX_MISC_SW_FUSE_VALUE_LPAC); @@ -1236,7 +1236,7 @@ static int a6xx_gmu_secure_init(struct a6xx_gpu *a6xx_gpu) * firmware, find out whether that's the case. The scm call * above sets the fuse register. */ - fuse_val = a6xx_llc_read(a6xx_gpu, + fuse_val = a6xx_cx_misc_read(a6xx_gpu, REG_A7XX_CX_MISC_SW_FUSE_VALUE); adreno_gpu->has_ray_tracing = !!(fuse_val & A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING); @@ -1343,7 +1343,7 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) /* Check to see if we are doing a cold or warm boot */ if (adreno_is_a7xx(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) { - status = a6xx_llc_read(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL) == 1 ? + status = a6xx_cx_misc_read(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL) == 1 ? GMU_WARM_BOOT : GMU_COLD_BOOT; } else if (gmu->legacy) { status = gmu_read(gmu, REG_A6XX_GMU_GENERAL_7) == 1 ? diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 4bb3be3ff9d0..a65dc653ca73 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -1961,7 +1961,7 @@ static void a6xx_llc_activate(struct a6xx_gpu *a6xx_gpu) struct msm_gpu *gpu = &adreno_gpu->base; u32 cntl1_regval = 0; - if (IS_ERR(a6xx_gpu->llc_mmio)) + if (IS_ERR(a6xx_gpu->cx_misc_mmio)) return; if (!llcc_slice_activate(a6xx_gpu->llc_slice)) { @@ -2000,14 +2000,14 @@ static void a6xx_llc_activate(struct a6xx_gpu *a6xx_gpu) * pagetables */ if (!a6xx_gpu->have_mmu500) { - a6xx_llc_write(a6xx_gpu, + a6xx_cx_misc_write(a6xx_gpu, REG_A6XX_CX_MISC_SYSTEM_CACHE_CNTL_1, cntl1_regval); /* * Program cacheability overrides to not allocate cache * lines on a write miss */ - a6xx_llc_rmw(a6xx_gpu, + a6xx_cx_misc_rmw(a6xx_gpu, REG_A6XX_CX_MISC_SYSTEM_CACHE_CNTL_0, 0xF, 0x03); return; } @@ -2020,7 +2020,7 @@ static void a7xx_llc_activate(struct a6xx_gpu *a6xx_gpu) struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; struct msm_gpu *gpu = &adreno_gpu->base; - if (IS_ERR(a6xx_gpu->llc_mmio)) + if (IS_ERR(a6xx_gpu->cx_misc_mmio)) return; if (!llcc_slice_activate(a6xx_gpu->llc_slice)) { @@ -2073,15 +2073,15 @@ static void a6xx_llc_slices_init(struct platform_device *pdev, of_node_put(phandle); if (is_a7xx || !a6xx_gpu->have_mmu500) - a6xx_gpu->llc_mmio = msm_ioremap(pdev, "cx_mem"); + a6xx_gpu->cx_misc_mmio = msm_ioremap(pdev, "cx_mem"); else - a6xx_gpu->llc_mmio = NULL; + a6xx_gpu->cx_misc_mmio = NULL; a6xx_gpu->llc_slice = llcc_slice_getd(LLCC_GPU); a6xx_gpu->htw_llc_slice = llcc_slice_getd(LLCC_GPUHTW); if (IS_ERR_OR_NULL(a6xx_gpu->llc_slice) && IS_ERR_OR_NULL(a6xx_gpu->htw_llc_slice)) - a6xx_gpu->llc_mmio = ERR_PTR(-EINVAL); + a6xx_gpu->cx_misc_mmio = ERR_PTR(-EINVAL); } #define GBIF_CLIENT_HALT_MASK BIT(0) @@ -2618,7 +2618,7 @@ static int a6xx_read_speedbin(struct device *dev, struct a6xx_gpu *a6xx_gpu, return ret; if (info->quirks & ADRENO_QUIRK_SOFTFUSE) { - *speedbin = a6xx_llc_read(a6xx_gpu, REG_A8XX_CX_MISC_SW_FUSE_FREQ_LIMIT_STATUS); + *speedbin = a6xx_cx_misc_read(a6xx_gpu, REG_A8XX_CX_MISC_SW_FUSE_FREQ_LIMIT_STATUS); *speedbin = A8XX_CX_MISC_SW_FUSE_FREQ_LIMIT_STATUS_FINALFREQLIMIT(*speedbin); return 0; } diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h index f60a0801a62d..b50c57f427b4 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h @@ -112,7 +112,7 @@ struct a6xx_gpu { bool has_whereami; - void __iomem *llc_mmio; + void __iomem *cx_misc_mmio; void *llc_slice; void *htw_llc_slice; bool have_mmu500; @@ -250,19 +250,19 @@ static inline bool a6xx_has_gbif(struct adreno_gpu *gpu) return true; } -static inline void a6xx_llc_rmw(struct a6xx_gpu *a6xx_gpu, u32 reg, u32 mask, u32 or) +static inline void a6xx_cx_misc_rmw(struct a6xx_gpu *a6xx_gpu, u32 reg, u32 mask, u32 or) { - return msm_rmw(a6xx_gpu->llc_mmio + (reg << 2), mask, or); + return msm_rmw(a6xx_gpu->cx_misc_mmio + (reg << 2), mask, or); } -static inline u32 a6xx_llc_read(struct a6xx_gpu *a6xx_gpu, u32 reg) +static inline u32 a6xx_cx_misc_read(struct a6xx_gpu *a6xx_gpu, u32 reg) { - return readl(a6xx_gpu->llc_mmio + (reg << 2)); + return readl(a6xx_gpu->cx_misc_mmio + (reg << 2)); } -static inline void a6xx_llc_write(struct a6xx_gpu *a6xx_gpu, u32 reg, u32 value) +static inline void a6xx_cx_misc_write(struct a6xx_gpu *a6xx_gpu, u32 reg, u32 value) { - writel(value, a6xx_gpu->llc_mmio + (reg << 2)); + writel(value, a6xx_gpu->cx_misc_mmio + (reg << 2)); } #define shadowptr(_a6xx_gpu, _ring) ((_a6xx_gpu)->shadow_iova + \ diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index abe100d9aeaa..c3492e2705ce 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -104,7 +104,7 @@ void a8xx_gpu_get_slice_info(struct msm_gpu *gpu) return; } - slice_mask &= a6xx_llc_read(a6xx_gpu, + slice_mask &= a6xx_cx_misc_read(a6xx_gpu, REG_A8XX_CX_MISC_SLICE_ENABLE_FINAL); a6xx_gpu->slice_mask = slice_mask; From b150b562ec0607f068ed75eddfc31553976a874f Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Thu, 28 May 2026 09:48:57 +0000 Subject: [PATCH 65/67] drm/msm/adreno: set cx_misc_mmio regardless of if platform has LLCC Platforms without a LLCC (e.g. milos) still need to be able to read and write to the cx_mem region. Previously if LLCC slices were unavailable the cx_misc_mmio mapping was overwritten with ERR_PTR, causing a crash when the GMU later accessed cx_mem. Move the cx_misc_mmio mapping out of a6xx_llc_slices_init() into a6xx_gpu_init() so that cx_mem mapping is independent of LLCC. Reviewed-by: Konrad Dybcio Reviewed-by: Akhil P Oommen Signed-off-by: Alexander Koskovich Patchwork: https://patchwork.freedesktop.org/patch/728808/ Message-ID: <20260528-adreno-810-v7-4-7fe7fdd97fc2@pm.me> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 38 ++++++++++++--------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index a65dc653ca73..8b3bb2fd433b 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -1961,7 +1961,7 @@ static void a6xx_llc_activate(struct a6xx_gpu *a6xx_gpu) struct msm_gpu *gpu = &adreno_gpu->base; u32 cntl1_regval = 0; - if (IS_ERR(a6xx_gpu->cx_misc_mmio)) + if (IS_ERR_OR_NULL(a6xx_gpu->llc_slice) && IS_ERR_OR_NULL(a6xx_gpu->htw_llc_slice)) return; if (!llcc_slice_activate(a6xx_gpu->llc_slice)) { @@ -2020,7 +2020,7 @@ static void a7xx_llc_activate(struct a6xx_gpu *a6xx_gpu) struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; struct msm_gpu *gpu = &adreno_gpu->base; - if (IS_ERR(a6xx_gpu->cx_misc_mmio)) + if (IS_ERR_OR_NULL(a6xx_gpu->llc_slice) && IS_ERR_OR_NULL(a6xx_gpu->htw_llc_slice)) return; if (!llcc_slice_activate(a6xx_gpu->llc_slice)) { @@ -2057,31 +2057,12 @@ static void a6xx_llc_slices_destroy(struct a6xx_gpu *a6xx_gpu) static void a6xx_llc_slices_init(struct platform_device *pdev, struct a6xx_gpu *a6xx_gpu, bool is_a7xx) { - struct device_node *phandle; - /* No LLCC on non-RPMh (and by extension, non-GMU) SoCs */ if (adreno_has_gmu_wrapper(&a6xx_gpu->base)) return; - /* - * There is a different programming path for A6xx targets with an - * mmu500 attached, so detect if that is the case - */ - phandle = of_parse_phandle(pdev->dev.of_node, "iommus", 0); - a6xx_gpu->have_mmu500 = (phandle && - of_device_is_compatible(phandle, "arm,mmu-500")); - of_node_put(phandle); - - if (is_a7xx || !a6xx_gpu->have_mmu500) - a6xx_gpu->cx_misc_mmio = msm_ioremap(pdev, "cx_mem"); - else - a6xx_gpu->cx_misc_mmio = NULL; - a6xx_gpu->llc_slice = llcc_slice_getd(LLCC_GPU); a6xx_gpu->htw_llc_slice = llcc_slice_getd(LLCC_GPUHTW); - - if (IS_ERR_OR_NULL(a6xx_gpu->llc_slice) && IS_ERR_OR_NULL(a6xx_gpu->htw_llc_slice)) - a6xx_gpu->cx_misc_mmio = ERR_PTR(-EINVAL); } #define GBIF_CLIENT_HALT_MASK BIT(0) @@ -2679,6 +2660,7 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) struct platform_device *pdev = priv->gpu_pdev; struct adreno_platform_config *config = pdev->dev.platform_data; const struct adreno_info *info = config->info; + struct device_node *phandle; struct a6xx_gpu *a6xx_gpu; struct adreno_gpu *adreno_gpu; struct msm_gpu *gpu; @@ -2729,6 +2711,20 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) a6xx_llc_slices_init(pdev, a6xx_gpu, is_a7xx); + /* + * There is a different programming path for A6xx targets with an + * mmu500 attached, so detect if that is the case + */ + phandle = of_parse_phandle(pdev->dev.of_node, "iommus", 0); + a6xx_gpu->have_mmu500 = (phandle && + of_device_is_compatible(phandle, "arm,mmu-500")); + of_node_put(phandle); + + if (is_a7xx || !a6xx_gpu->have_mmu500) + a6xx_gpu->cx_misc_mmio = msm_ioremap(pdev, "cx_mem"); + else + a6xx_gpu->cx_misc_mmio = NULL; + ret = a6xx_set_supported_hw(&pdev->dev, a6xx_gpu, info); if (ret) { a6xx_llc_slices_destroy(a6xx_gpu); From c2c083bbe07bd65f376d1ce7e136838427f494b1 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Thu, 28 May 2026 09:49:04 +0000 Subject: [PATCH 66/67] drm/msm/a8xx: use pipe protect slot 15 for last-span-unbound feature A8XX GPUs have two sets of protect registers: 64 global slots and 16 pipe specific slots. The last-span-unbound feature is only available on pipe protect registers, and should always target pipe slot 15. This matches the downstream driver which hardcodes pipe slot 15 for all A8XX GPUs (GRAPHICS.LA.15.0.r1) and resolves protect errors on A810. Reviewed-by: Konrad Dybcio Reviewed-by: Akhil P Oommen Signed-off-by: Alexander Koskovich Patchwork: https://patchwork.freedesktop.org/patch/728810/ Message-ID: <20260528-adreno-810-v7-5-7fe7fdd97fc2@pm.me> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index c3492e2705ce..9e44fd1ae634 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -265,8 +265,8 @@ static void a8xx_set_cp_protect(struct msm_gpu *gpu) * Last span feature is only supported on PIPE specific register. * So update those here */ - a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_CP_PROTECT_PIPE(protect->count_max), final_cfg); - a8xx_write_pipe(gpu, PIPE_BV, REG_A8XX_CP_PROTECT_PIPE(protect->count_max), final_cfg); + a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_CP_PROTECT_PIPE(15), final_cfg); + a8xx_write_pipe(gpu, PIPE_BV, REG_A8XX_CP_PROTECT_PIPE(15), final_cfg); a8xx_aperture_clear(gpu); } From 9a967125427e03c7ebc24d7ad26e9307e8403d4e Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Thu, 28 May 2026 09:49:16 +0000 Subject: [PATCH 67/67] drm/msm/adreno: add Adreno 810 GPU support Add catalog entry and register configuration for the Adreno 810 found in Qualcomm SM7635 (Milos) based devices. Reviewed-by: Konrad Dybcio Reviewed-by: Akhil P Oommen Signed-off-by: Alexander Koskovich Patchwork: https://patchwork.freedesktop.org/patch/728812/ Message-ID: <20260528-adreno-810-v7-6-7fe7fdd97fc2@pm.me> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 298 ++++++++++++++++++++++ drivers/gpu/drm/msm/adreno/adreno_gpu.h | 5 + 2 files changed, 303 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index 550ff3a9b82e..3e6f409d13a2 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c @@ -1799,6 +1799,261 @@ static const struct adreno_reglist_pipe x285_dyn_pwrup_reglist_regs[] = { }; DECLARE_ADRENO_REGLIST_PIPE_LIST(x285_dyn_pwrup_reglist); +static const struct adreno_reglist_pipe a810_nonctxt_regs[] = { + { REG_A8XX_CP_SMMU_STREAM_ID_LPAC, 0x00000101, BIT(PIPE_NONE) }, + { REG_A8XX_GRAS_DBG_ECO_CNTL, 0x00f80800, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A6XX_PC_AUTO_VERTEX_STRIDE, 0x00000001, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_VIS_STREAM_CNTL, 0x10010000, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CONTEXT_SWITCH_STABILIZE_CNTL_1, 0x00000002, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_1, 0x00000003, BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_1, 0x00000023, BIT(PIPE_BV) }, /* Avoid partial waves at VFD */ + { REG_A8XX_PC_CHICKEN_BITS_2, 0x00000200, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_3, 0x00500000, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_4, 0x00500050, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A7XX_RB_CCU_CNTL, 0x00000068, BIT(PIPE_BR) }, + /* Partially enable perf clear, Disable DINT to c/z be data forwarding */ + { REG_A7XX_RB_CCU_DBG_ECO_CNTL, 0x00002200, BIT(PIPE_BR) }, + { REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0x00000007, BIT(PIPE_BR) }, + { REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0x00004000, BIT(PIPE_BR) }, + { REG_A8XX_RBBM_NC_MODE_CNTL, 0x00000001, BIT(PIPE_NONE) }, + { REG_A8XX_RBBM_SLICE_NC_MODE_CNTL, 0x00000001, BIT(PIPE_NONE) }, + { REG_A8XX_RBBM_WAIT_IDLE_CLOCKS_CNTL, 0x00000030, BIT(PIPE_NONE) }, + { REG_A8XX_RBBM_WAIT_IDLE_CLOCKS_CNTL2, 0x00000030, BIT(PIPE_NONE) }, + { REG_A8XX_UCHE_GBIF_GX_CONFIG, 0x010240e0, BIT(PIPE_NONE) }, + { REG_A8XX_RBBM_GBIF_CLIENT_QOS_CNTL, 0x22122212, BIT(PIPE_NONE) }, + { REG_A8XX_RBBM_CGC_P2S_CNTL, 0x00000040, BIT(PIPE_NONE) }, + /* + * BIT(22): Disable PS out of order retire + * BIT(23): Enable half wave mode and MM instruction src&dst is half precision + */ + { REG_A7XX_SP_CHICKEN_BITS_2, BIT(22) | BIT(23), BIT(PIPE_NONE) }, + { REG_A7XX_SP_CHICKEN_BITS_3, 0x00300000, BIT(PIPE_NONE) }, + { REG_A6XX_SP_PERFCTR_SHADER_MASK, 0x0000003f, BIT(PIPE_NONE) }, + { REG_A7XX_SP_HLSQ_TIMEOUT_THRESHOLD_DP, 0x00000080, BIT(PIPE_NONE) }, + { REG_A6XX_TPL1_DBG_ECO_CNTL, 0x10100000, BIT(PIPE_NONE) }, + { REG_A6XX_TPL1_DBG_ECO_CNTL1, 0x04000724, BIT(PIPE_NONE) }, + { REG_A6XX_UCHE_MODE_CNTL, 0x00020000, BIT(PIPE_NONE) }, + { REG_A8XX_UCHE_CCHE_MODE_CNTL, 0x00001000, BIT(PIPE_NONE) }, + { REG_A8XX_UCHE_CCHE_CACHE_WAYS, 0x00000800, BIT(PIPE_NONE) }, + /* Disable write slow pointer in data phase queue */ + { REG_A8XX_UCHE_HW_DBG_CNTL, BIT(8), BIT(PIPE_NONE) }, + { REG_A8XX_UCHE_VARB_IDLE_TIMEOUT, 0x00000020, BIT(PIPE_NONE) }, + { REG_A7XX_VFD_DBG_ECO_CNTL, 0x00008000, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_BV_THRESHOLD, 0x00500050, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_BR_THRESHOLD, 0x00600060, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_BUSY_REQ_CNT, 0x00200020, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_LP_REQ_CNT, 0x00100020, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VPC_FLATSHADE_MODE_CNTL, 0x00000001, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_RB_GC_GMEM_PROTECT, 0x00900000, BIT(PIPE_BR) }, + { }, +}; + +static const u32 a810_protect_regs[] = { + A6XX_PROTECT_RDONLY(0x00000, 0x03a3), + A6XX_PROTECT_RDONLY(0x003b4, 0x008b), + A6XX_PROTECT_NORDWR(0x00440, 0x001f), + A6XX_PROTECT_RDONLY(0x00580, 0x005f), + A6XX_PROTECT_NORDWR(0x005e0, 0x011f), + A6XX_PROTECT_RDONLY(0x0074a, 0x0005), + A6XX_PROTECT_RDONLY(0x00759, 0x0026), + A6XX_PROTECT_RDONLY(0x00789, 0x0000), + A6XX_PROTECT_RDONLY(0x0078c, 0x0013), + A6XX_PROTECT_NORDWR(0x00800, 0x0029), + A6XX_PROTECT_NORDWR(0x00837, 0x00af), + A6XX_PROTECT_RDONLY(0x008e7, 0x00c9), + A6XX_PROTECT_NORDWR(0x008ec, 0x00c3), + A6XX_PROTECT_NORDWR(0x009b1, 0x0250), + A6XX_PROTECT_RDONLY(0x00ce0, 0x0001), + A6XX_PROTECT_RDONLY(0x00df0, 0x0000), + A6XX_PROTECT_NORDWR(0x00df1, 0x0000), + A6XX_PROTECT_NORDWR(0x00e01, 0x0000), + A6XX_PROTECT_NORDWR(0x00e03, 0x1fff), + A6XX_PROTECT_NORDWR(0x03c00, 0x00c5), + A6XX_PROTECT_RDONLY(0x03cc6, 0x1fff), + A6XX_PROTECT_NORDWR(0x08600, 0x01ff), + A6XX_PROTECT_NORDWR(0x08e00, 0x00ff), + A6XX_PROTECT_RDONLY(0x08f00, 0x0000), + A6XX_PROTECT_NORDWR(0x08f01, 0x01be), + A6XX_PROTECT_NORDWR(0x09600, 0x01ff), + A6XX_PROTECT_RDONLY(0x0981a, 0x02e5), + A6XX_PROTECT_NORDWR(0x09e00, 0x01ff), + A6XX_PROTECT_NORDWR(0x0a600, 0x01ff), + A6XX_PROTECT_NORDWR(0x0ae00, 0x0006), + A6XX_PROTECT_NORDWR(0x0ae08, 0x0006), + A6XX_PROTECT_NORDWR(0x0ae10, 0x036f), + A6XX_PROTECT_NORDWR(0x0b600, 0x1fff), + A6XX_PROTECT_NORDWR(0x0dc00, 0x1fff), + A6XX_PROTECT_RDONLY(0x0fc00, 0x1fff), + A6XX_PROTECT_NORDWR(0x18400, 0x003f), + A6XX_PROTECT_RDONLY(0x18440, 0x013f), + A6XX_PROTECT_NORDWR(0x18580, 0x1fff), + A6XX_PROTECT_NORDWR(0x1b400, 0x1fff), + A6XX_PROTECT_NORDWR(0x1f400, 0x0477), + A6XX_PROTECT_RDONLY(0x1f878, 0x0787), + A6XX_PROTECT_NORDWR(0x1f930, 0x0329), + A6XX_PROTECT_NORDWR(0x20000, 0x1fff), + A6XX_PROTECT_NORDWR(0x27800, 0x007f), + A6XX_PROTECT_RDONLY(0x27880, 0x0381), + A6XX_PROTECT_NORDWR(0x27882, 0x0001), + A6XX_PROTECT_NORDWR(0x27c02, 0x0000), +}; +DECLARE_ADRENO_PROTECT(a810_protect, 64); + +static const uint32_t a810_pwrup_reglist_regs[] = { + REG_A6XX_UCHE_MODE_CNTL, + REG_A8XX_UCHE_VARB_IDLE_TIMEOUT, + REG_A8XX_UCHE_GBIF_GX_CONFIG, + REG_A8XX_UCHE_CACHE_WAYS, + REG_A8XX_UCHE_CCHE_MODE_CNTL, + REG_A8XX_UCHE_CCHE_CACHE_WAYS, + REG_A8XX_UCHE_CCHE_GC_GMEM_RANGE_MIN, + REG_A8XX_UCHE_CCHE_GC_GMEM_RANGE_MIN + 1, + REG_A8XX_UCHE_CCHE_TRAP_BASE, + REG_A8XX_UCHE_CCHE_TRAP_BASE + 1, + REG_A8XX_UCHE_CCHE_WRITE_THRU_BASE, + REG_A8XX_UCHE_CCHE_WRITE_THRU_BASE + 1, + REG_A8XX_UCHE_HW_DBG_CNTL, + REG_A8XX_UCHE_WRITE_THRU_BASE, + REG_A8XX_UCHE_WRITE_THRU_BASE + 1, + REG_A8XX_UCHE_TRAP_BASE, + REG_A8XX_UCHE_TRAP_BASE + 1, + REG_A8XX_UCHE_CLIENT_PF, + REG_A8XX_RB_CMP_NC_MODE_CNTL, + REG_A7XX_SP_HLSQ_TIMEOUT_THRESHOLD_DP, + REG_A8XX_SP_HLSQ_GC_GMEM_RANGE_MIN, + REG_A8XX_SP_HLSQ_GC_GMEM_RANGE_MIN + 1, + REG_A7XX_SP_READ_SEL, + REG_A6XX_TPL1_NC_MODE_CNTL, + REG_A6XX_TPL1_DBG_ECO_CNTL, + REG_A6XX_TPL1_DBG_ECO_CNTL1, + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(1), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(2), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(3), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(4), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(5), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(6), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(7), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(8), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(9), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(10), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(11), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(12), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(13), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(14), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(15), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(16), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(17), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(18), + REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(19), +}; +DECLARE_ADRENO_REGLIST_LIST(a810_pwrup_reglist); + +static const u32 a810_ifpc_reglist_regs[] = { + REG_A8XX_RBBM_NC_MODE_CNTL, + REG_A8XX_RBBM_PERFCTR_CNTL, + REG_A8XX_RBBM_SLICE_INTERFACE_HANG_INT_CNTL, + REG_A8XX_RBBM_SLICE_NC_MODE_CNTL, + REG_A6XX_SP_NC_MODE_CNTL, + REG_A7XX_SP_CHICKEN_BITS_2, + REG_A7XX_SP_CHICKEN_BITS_3, + REG_A6XX_SP_PERFCTR_SHADER_MASK, + REG_A8XX_CP_PROTECT_GLOBAL(0), + REG_A8XX_CP_PROTECT_GLOBAL(1), + REG_A8XX_CP_PROTECT_GLOBAL(2), + REG_A8XX_CP_PROTECT_GLOBAL(3), + REG_A8XX_CP_PROTECT_GLOBAL(4), + REG_A8XX_CP_PROTECT_GLOBAL(5), + REG_A8XX_CP_PROTECT_GLOBAL(6), + REG_A8XX_CP_PROTECT_GLOBAL(7), + REG_A8XX_CP_PROTECT_GLOBAL(8), + REG_A8XX_CP_PROTECT_GLOBAL(9), + REG_A8XX_CP_PROTECT_GLOBAL(10), + REG_A8XX_CP_PROTECT_GLOBAL(11), + REG_A8XX_CP_PROTECT_GLOBAL(12), + REG_A8XX_CP_PROTECT_GLOBAL(13), + REG_A8XX_CP_PROTECT_GLOBAL(14), + REG_A8XX_CP_PROTECT_GLOBAL(15), + REG_A8XX_CP_PROTECT_GLOBAL(16), + REG_A8XX_CP_PROTECT_GLOBAL(17), + REG_A8XX_CP_PROTECT_GLOBAL(18), + REG_A8XX_CP_PROTECT_GLOBAL(19), + REG_A8XX_CP_PROTECT_GLOBAL(20), + REG_A8XX_CP_PROTECT_GLOBAL(21), + REG_A8XX_CP_PROTECT_GLOBAL(22), + REG_A8XX_CP_PROTECT_GLOBAL(23), + REG_A8XX_CP_PROTECT_GLOBAL(24), + REG_A8XX_CP_PROTECT_GLOBAL(25), + REG_A8XX_CP_PROTECT_GLOBAL(26), + REG_A8XX_CP_PROTECT_GLOBAL(27), + REG_A8XX_CP_PROTECT_GLOBAL(28), + REG_A8XX_CP_PROTECT_GLOBAL(29), + REG_A8XX_CP_PROTECT_GLOBAL(30), + REG_A8XX_CP_PROTECT_GLOBAL(31), + REG_A8XX_CP_PROTECT_GLOBAL(32), + REG_A8XX_CP_PROTECT_GLOBAL(33), + REG_A8XX_CP_PROTECT_GLOBAL(34), + REG_A8XX_CP_PROTECT_GLOBAL(35), + REG_A8XX_CP_PROTECT_GLOBAL(36), + REG_A8XX_CP_PROTECT_GLOBAL(37), + REG_A8XX_CP_PROTECT_GLOBAL(38), + REG_A8XX_CP_PROTECT_GLOBAL(39), + REG_A8XX_CP_PROTECT_GLOBAL(40), + REG_A8XX_CP_PROTECT_GLOBAL(41), + REG_A8XX_CP_PROTECT_GLOBAL(42), + REG_A8XX_CP_PROTECT_GLOBAL(43), + REG_A8XX_CP_PROTECT_GLOBAL(44), + REG_A8XX_CP_PROTECT_GLOBAL(45), + REG_A8XX_CP_PROTECT_GLOBAL(46), + REG_A8XX_CP_PROTECT_GLOBAL(47), + REG_A8XX_CP_PROTECT_GLOBAL(48), + REG_A8XX_CP_PROTECT_GLOBAL(49), + REG_A8XX_CP_PROTECT_GLOBAL(50), + REG_A8XX_CP_PROTECT_GLOBAL(51), + REG_A8XX_CP_PROTECT_GLOBAL(52), + REG_A8XX_CP_PROTECT_GLOBAL(53), + REG_A8XX_CP_PROTECT_GLOBAL(54), + REG_A8XX_CP_PROTECT_GLOBAL(55), + REG_A8XX_CP_PROTECT_GLOBAL(56), + REG_A8XX_CP_PROTECT_GLOBAL(57), + REG_A8XX_CP_PROTECT_GLOBAL(58), + REG_A8XX_CP_PROTECT_GLOBAL(59), + REG_A8XX_CP_PROTECT_GLOBAL(60), + REG_A8XX_CP_PROTECT_GLOBAL(61), + REG_A8XX_CP_PROTECT_GLOBAL(62), + REG_A8XX_CP_PROTECT_GLOBAL(63), +}; +DECLARE_ADRENO_REGLIST_LIST(a810_ifpc_reglist); + +static const struct adreno_reglist_pipe a810_dyn_pwrup_reglist_regs[] = { + { REG_A8XX_CP_PROTECT_CNTL_PIPE, 0, BIT(PIPE_BR) | BIT(PIPE_BV) }, + { REG_A8XX_CP_PROTECT_PIPE(15), 0, BIT(PIPE_BR) | BIT(PIPE_BV) }, + { REG_A8XX_GRAS_TSEFE_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_GRAS_NC_MODE_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_GRAS_DBG_ECO_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A7XX_RB_CCU_CNTL, 0, BIT(PIPE_BR) }, + { REG_A7XX_RB_CCU_DBG_ECO_CNTL, 0, BIT(PIPE_BR) }, + { REG_A8XX_RB_CCU_NC_MODE_CNTL, 0, BIT(PIPE_BR) }, + { REG_A8XX_RB_CMP_NC_MODE_CNTL, 0, BIT(PIPE_BR) }, + { REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0, BIT(PIPE_BR) }, + { REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0, BIT(PIPE_BR) }, + { REG_A8XX_RB_GC_GMEM_PROTECT, 0, BIT(PIPE_BR) }, + { REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE_ENABLE, 0, BIT(PIPE_BR) }, + { REG_A8XX_VPC_FLATSHADE_MODE_CNTL, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_1, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_2, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_3, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_PC_CHICKEN_BITS_4, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A6XX_PC_AUTO_VERTEX_STRIDE, 0, BIT(PIPE_BR) | BIT(PIPE_BV) }, + { REG_A8XX_PC_VIS_STREAM_CNTL, 0, BIT(PIPE_BR) | BIT(PIPE_BV) }, + { REG_A8XX_PC_CONTEXT_SWITCH_STABILIZE_CNTL_1, 0, BIT(PIPE_BR) | BIT(PIPE_BV) }, + { REG_A8XX_VFD_CB_BV_THRESHOLD, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_BR_THRESHOLD, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_BUSY_REQ_CNT, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A8XX_VFD_CB_LP_REQ_CNT, 0, BIT(PIPE_BV) | BIT(PIPE_BR) }, + { REG_A7XX_VFD_DBG_ECO_CNTL, 0, BIT(PIPE_BR) | BIT(PIPE_BV) }, +}; +DECLARE_ADRENO_REGLIST_PIPE_LIST(a810_dyn_pwrup_reglist); + static const struct adreno_reglist_pipe a840_nonctxt_regs[] = { { REG_A8XX_CP_SMMU_STREAM_ID_LPAC, 0x00000101, BIT(PIPE_NONE) }, { REG_A8XX_GRAS_DBG_ECO_CNTL, 0x00000800, BIT(PIPE_BV) | BIT(PIPE_BR) }, @@ -2193,6 +2448,48 @@ static const struct adreno_info a8xx_gpus[] = { { 252, 2 }, { 221, 3 }, ), + }, { + .chip_ids = ADRENO_CHIP_IDS(0x44010000), + .family = ADRENO_8XX_GEN1, + .fw = { + [ADRENO_FW_SQE] = "gen80300_sqe.fw", + [ADRENO_FW_GMU] = "gen80300_gmu.bin", + }, + .gmem = SZ_512K + SZ_64K, + .inactive_period = DRM_MSM_INACTIVE_PERIOD, + .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT | + ADRENO_QUIRK_HAS_HW_APRIV | + ADRENO_QUIRK_PREEMPTION | + ADRENO_QUIRK_IFPC, + .funcs = &a8xx_gpu_funcs, + .zapfw = "gen80300_zap.mbn", + .a6xx = &(const struct a6xx_info) { + .protect = &a810_protect, + .nonctxt_reglist = a810_nonctxt_regs, + .pwrup_reglist = &a810_pwrup_reglist, + .dyn_pwrup_reglist = &a810_dyn_pwrup_reglist, + .ifpc_reglist = &a810_ifpc_reglist, + .gbif_cx = a840_gbif, + .max_slices = 1, + .gmu_chipid = 0x8030000, + .bcms = (const struct a6xx_bcm[]) { + { .name = "SH0", .buswidth = 16 }, + { .name = "MC0", .buswidth = 4 }, + { + .name = "ACV", + .fixed = true, + .perfmode = BIT(2), + .perfmode_bw = 10687500, + }, + { /* sentinel */ }, + }, + }, + .preempt_record_size = 4558 * SZ_1K, + .speedbins = ADRENO_SPEEDBINS( + { 0, 0 }, + { 242, 1 }, + { 221, 2 }, + ), } }; @@ -2205,4 +2502,5 @@ static inline __always_unused void __build_asserts(void) BUILD_BUG_ON(a660_protect.count > a660_protect.count_max); BUILD_BUG_ON(a690_protect.count > a690_protect.count_max); BUILD_BUG_ON(a730_protect.count > a730_protect.count_max); + BUILD_BUG_ON(a810_protect.count > a810_protect.count_max); } diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 324319762383..1f201322cb6e 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -587,6 +587,11 @@ static inline int adreno_is_a8xx(struct adreno_gpu *gpu) return gpu->info->family >= ADRENO_8XX_GEN1; } +static inline int adreno_is_a810(struct adreno_gpu *gpu) +{ + return gpu->info->chip_ids[0] == 0x44010000; +} + static inline int adreno_is_x285(struct adreno_gpu *gpu) { return gpu->info->chip_ids[0] == 0x44070001;