Merge tag 'drm-msm-next-2024-10-28' of https://gitlab.freedesktop.org/drm/msm into drm-next

Updates for v6.13

Core:
- Switch to aperture_remove_all_conflicting_devices()
- Simplify msm_disp_state_dump_regs()

DPU:
- Add SA8775P support
- Add (disabled by default) MSM8917, MSM8937, MSM8953 and MSM8996
  support
- Enable support for larger framebuffers (required for X.Org working
  with several outputs)
- Dropped LM_3, LM_4 (MSM8998, SDM845)
- Fixed DSPP_3 routing on SDM845

DP:
- Add SA8775P support

HDMI:
- Mark two arrays as const in MSM8998 HDMI PHY driver

GPU:
- a7xx preemption support
- Adreno A663 support
- Typos fixes, etc
- Fix excessive stack usage in a6xx GMU

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Rob Clark <robdclark@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/CAF6AEGt7k8zDHsg2Uzx9apzyQMut8XdLXMQSRNn7WArdPUV5Qw@mail.gmail.com
This commit is contained in:
Dave Airlie 2024-11-05 07:21:46 +10:00
commit ffd99396c6
64 changed files with 3196 additions and 1018 deletions

View File

@ -17,6 +17,7 @@ properties:
compatible:
oneOf:
- enum:
- qcom,sa8775p-dp
- qcom,sc7180-dp
- qcom,sc7280-dp
- qcom,sc7280-edp

View File

@ -125,6 +125,7 @@ allOf:
enum:
- qcom,adreno-gmu-635.0
- qcom,adreno-gmu-660.1
- qcom,adreno-gmu-663.0
then:
properties:
reg:

View File

@ -0,0 +1,241 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/msm/qcom,sa8775p-mdss.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies, Inc. SA87755P Display MDSS
maintainers:
- Mahadevan <quic_mahap@quicinc.com>
description:
SA8775P MSM Mobile Display Subsystem(MDSS), which encapsulates sub-blocks like
DPU display controller, DP interfaces and EDP etc.
$ref: /schemas/display/msm/mdss-common.yaml#
properties:
compatible:
const: qcom,sa8775p-mdss
clocks:
items:
- description: Display AHB
- description: Display hf AXI
- description: Display core
iommus:
maxItems: 1
interconnects:
maxItems: 3
interconnect-names:
maxItems: 3
patternProperties:
"^display-controller@[0-9a-f]+$":
type: object
additionalProperties: true
properties:
compatible:
const: qcom,sa8775p-dpu
"^displayport-controller@[0-9a-f]+$":
type: object
additionalProperties: true
properties:
compatible:
items:
- const: qcom,sa8775p-dp
required:
- compatible
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interconnect/qcom,icc.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/qcom,sa8775p-gcc.h>
#include <dt-bindings/interconnect/qcom,sa8775p-rpmh.h>
#include <dt-bindings/power/qcom,rpmhpd.h>
#include <dt-bindings/power/qcom-rpmpd.h>
display-subsystem@ae00000 {
compatible = "qcom,sa8775p-mdss";
reg = <0x0ae00000 0x1000>;
reg-names = "mdss";
interconnects = <&mmss_noc MASTER_MDP0 &mc_virt SLAVE_EBI1>,
<&mmss_noc MASTER_MDP1 &mc_virt SLAVE_EBI1>,
<&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_DISPLAY_CFG>;
interconnect-names = "mdp0-mem",
"mdp1-mem",
"cpu-cfg";
resets = <&dispcc_core_bcr>;
power-domains = <&dispcc_gdsc>;
clocks = <&dispcc_ahb_clk>,
<&gcc GCC_DISP_HF_AXI_CLK>,
<&dispcc_mdp_clk>;
interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
interrupt-controller;
#interrupt-cells = <1>;
iommus = <&apps_smmu 0x1000 0x402>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
display-controller@ae01000 {
compatible = "qcom,sa8775p-dpu";
reg = <0x0ae01000 0x8f000>,
<0x0aeb0000 0x2008>;
reg-names = "mdp", "vbif";
clocks = <&gcc GCC_DISP_HF_AXI_CLK>,
<&dispcc_ahb_clk>,
<&dispcc_mdp_lut_clk>,
<&dispcc_mdp_clk>,
<&dispcc_mdp_vsync_clk>;
clock-names = "nrt_bus",
"iface",
"lut",
"core",
"vsync";
assigned-clocks = <&dispcc_mdp_vsync_clk>;
assigned-clock-rates = <19200000>;
operating-points-v2 = <&mdss0_mdp_opp_table>;
power-domains = <&rpmhpd RPMHPD_MMCX>;
interrupt-parent = <&mdss0>;
interrupts = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
dpu_intf0_out: endpoint {
remote-endpoint = <&mdss0_dp0_in>;
};
};
};
mdss0_mdp_opp_table: opp-table {
compatible = "operating-points-v2";
opp-375000000 {
opp-hz = /bits/ 64 <375000000>;
required-opps = <&rpmhpd_opp_svs_l1>;
};
opp-500000000 {
opp-hz = /bits/ 64 <500000000>;
required-opps = <&rpmhpd_opp_nom>;
};
opp-575000000 {
opp-hz = /bits/ 64 <575000000>;
required-opps = <&rpmhpd_opp_turbo>;
};
opp-650000000 {
opp-hz = /bits/ 64 <650000000>;
required-opps = <&rpmhpd_opp_turbo_l1>;
};
};
};
displayport-controller@af54000 {
compatible = "qcom,sa8775p-dp";
pinctrl-0 = <&dp_hot_plug_det>;
pinctrl-names = "default";
reg = <0xaf54000 0x104>,
<0xaf54200 0x0c0>,
<0xaf55000 0x770>,
<0xaf56000 0x09c>;
interrupt-parent = <&mdss0>;
interrupts = <12>;
clocks = <&dispcc_mdss_ahb_clk>,
<&dispcc_dptx0_aux_clk>,
<&dispcc_dptx0_link_clk>,
<&dispcc_dptx0_link_intf_clk>,
<&dispcc_dptx0_pixel0_clk>;
clock-names = "core_iface",
"core_aux",
"ctrl_link",
"ctrl_link_iface",
"stream_pixel";
assigned-clocks = <&dispcc_mdss_dptx0_link_clk_src>,
<&dispcc_mdss_dptx0_pixel0_clk_src>;
assigned-clock-parents = <&mdss0_edp_phy 0>, <&mdss0_edp_phy 1>;
phys = <&mdss0_edp_phy>;
phy-names = "dp";
operating-points-v2 = <&dp_opp_table>;
power-domains = <&rpmhpd SA8775P_MMCX>;
#sound-dai-cells = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
mdss0_dp0_in: endpoint {
remote-endpoint = <&dpu_intf0_out>;
};
};
port@1 {
reg = <1>;
mdss0_dp_out: endpoint { };
};
};
dp_opp_table: opp-table {
compatible = "operating-points-v2";
opp-160000000 {
opp-hz = /bits/ 64 <160000000>;
required-opps = <&rpmhpd_opp_low_svs>;
};
opp-270000000 {
opp-hz = /bits/ 64 <270000000>;
required-opps = <&rpmhpd_opp_svs>;
};
opp-540000000 {
opp-hz = /bits/ 64 <540000000>;
required-opps = <&rpmhpd_opp_svs_l1>;
};
opp-810000000 {
opp-hz = /bits/ 64 <810000000>;
required-opps = <&rpmhpd_opp_nom>;
};
};
};
};
...

View File

@ -7,13 +7,21 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Display DPU on SC7280
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- Neil Armstrong <neil.armstrong@linaro.org>
- Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
- Krishna Manikandan <quic_mkrishn@quicinc.com>
$ref: /schemas/display/msm/dpu-common.yaml#
properties:
compatible:
const: qcom,sc7280-dpu
enum:
- qcom,sc7280-dpu
- qcom,sc8280xp-dpu
- qcom,sm8350-dpu
- qcom,sm8450-dpu
- qcom,sm8550-dpu
reg:
items:

View File

@ -1,122 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/msm/qcom,sc8280xp-dpu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SC8280XP Display Processing Unit
maintainers:
- Bjorn Andersson <andersson@kernel.org>
description:
Device tree bindings for SC8280XP Display Processing Unit.
$ref: /schemas/display/msm/dpu-common.yaml#
properties:
compatible:
const: qcom,sc8280xp-dpu
reg:
items:
- description: Address offset and size for mdp register set
- description: Address offset and size for vbif register set
reg-names:
items:
- const: mdp
- const: vbif
clocks:
items:
- description: Display hf axi clock
- description: Display sf axi clock
- description: Display ahb clock
- description: Display lut clock
- description: Display core clock
- description: Display vsync clock
clock-names:
items:
- const: bus
- const: nrt_bus
- const: iface
- const: lut
- const: core
- const: vsync
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,dispcc-sc8280xp.h>
#include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interconnect/qcom,sc8280xp.h>
#include <dt-bindings/power/qcom-rpmpd.h>
display-controller@ae01000 {
compatible = "qcom,sc8280xp-dpu";
reg = <0x0ae01000 0x8f000>,
<0x0aeb0000 0x2008>;
reg-names = "mdp", "vbif";
clocks = <&gcc GCC_DISP_HF_AXI_CLK>,
<&gcc GCC_DISP_SF_AXI_CLK>,
<&dispcc0 DISP_CC_MDSS_AHB_CLK>,
<&dispcc0 DISP_CC_MDSS_MDP_LUT_CLK>,
<&dispcc0 DISP_CC_MDSS_MDP_CLK>,
<&dispcc0 DISP_CC_MDSS_VSYNC_CLK>;
clock-names = "bus",
"nrt_bus",
"iface",
"lut",
"core",
"vsync";
assigned-clocks = <&dispcc0 DISP_CC_MDSS_MDP_CLK>,
<&dispcc0 DISP_CC_MDSS_VSYNC_CLK>;
assigned-clock-rates = <460000000>,
<19200000>;
operating-points-v2 = <&mdp_opp_table>;
power-domains = <&rpmhpd SC8280XP_MMCX>;
interrupt-parent = <&mdss0>;
interrupts = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
endpoint {
remote-endpoint = <&mdss0_dp0_in>;
};
};
port@4 {
reg = <4>;
endpoint {
remote-endpoint = <&mdss0_dp1_in>;
};
};
port@5 {
reg = <5>;
endpoint {
remote-endpoint = <&mdss0_dp3_in>;
};
};
port@6 {
reg = <6>;
endpoint {
remote-endpoint = <&mdss0_dp2_in>;
};
};
};
};
...

View File

@ -13,7 +13,9 @@ $ref: /schemas/display/msm/dpu-common.yaml#
properties:
compatible:
const: qcom,sm8150-dpu
enum:
- qcom,sm8150-dpu
- qcom,sm8250-dpu
reg:
items:

View File

@ -1,99 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/msm/qcom,sm8250-dpu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SM8250 Display DPU
maintainers:
- Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
$ref: /schemas/display/msm/dpu-common.yaml#
properties:
compatible:
const: qcom,sm8250-dpu
reg:
items:
- description: Address offset and size for mdp register set
- description: Address offset and size for vbif register set
reg-names:
items:
- const: mdp
- const: vbif
clocks:
items:
- description: Display ahb clock
- description: Display hf axi clock
- description: Display core clock
- description: Display vsync clock
clock-names:
items:
- const: iface
- const: bus
- const: core
- const: vsync
required:
- compatible
- reg
- reg-names
- clocks
- clock-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,dispcc-sm8250.h>
#include <dt-bindings/clock/qcom,gcc-sm8250.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interconnect/qcom,sm8250.h>
#include <dt-bindings/power/qcom,rpmhpd.h>
display-controller@ae01000 {
compatible = "qcom,sm8250-dpu";
reg = <0x0ae01000 0x8f000>,
<0x0aeb0000 0x2008>;
reg-names = "mdp", "vbif";
clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>,
<&gcc GCC_DISP_HF_AXI_CLK>,
<&dispcc DISP_CC_MDSS_MDP_CLK>,
<&dispcc DISP_CC_MDSS_VSYNC_CLK>;
clock-names = "iface", "bus", "core", "vsync";
assigned-clocks = <&dispcc DISP_CC_MDSS_VSYNC_CLK>;
assigned-clock-rates = <19200000>;
operating-points-v2 = <&mdp_opp_table>;
power-domains = <&rpmhpd RPMHPD_MMCX>;
interrupt-parent = <&mdss>;
interrupts = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
endpoint {
remote-endpoint = <&dsi0_in>;
};
};
port@1 {
reg = <1>;
endpoint {
remote-endpoint = <&dsi1_in>;
};
};
};
};
...

View File

@ -1,120 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/msm/qcom,sm8350-dpu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SM8350 Display DPU
maintainers:
- Robert Foss <robert.foss@linaro.org>
$ref: /schemas/display/msm/dpu-common.yaml#
properties:
compatible:
const: qcom,sm8350-dpu
reg:
items:
- description: Address offset and size for mdp register set
- description: Address offset and size for vbif register set
reg-names:
items:
- const: mdp
- const: vbif
clocks:
items:
- description: Display hf axi clock
- description: Display sf axi clock
- description: Display ahb clock
- description: Display lut clock
- description: Display core clock
- description: Display vsync clock
clock-names:
items:
- const: bus
- const: nrt_bus
- const: iface
- const: lut
- const: core
- const: vsync
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,dispcc-sm8350.h>
#include <dt-bindings/clock/qcom,gcc-sm8350.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interconnect/qcom,sm8350.h>
#include <dt-bindings/power/qcom,rpmhpd.h>
display-controller@ae01000 {
compatible = "qcom,sm8350-dpu";
reg = <0x0ae01000 0x8f000>,
<0x0aeb0000 0x2008>;
reg-names = "mdp", "vbif";
clocks = <&gcc GCC_DISP_HF_AXI_CLK>,
<&gcc GCC_DISP_SF_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 = "bus",
"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_MMCX>;
interrupt-parent = <&mdss>;
interrupts = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
dpu_intf1_out: endpoint {
remote-endpoint = <&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-300000000 {
opp-hz = /bits/ 64 <300000000>;
required-opps = <&rpmhpd_opp_svs>;
};
opp-345000000 {
opp-hz = /bits/ 64 <345000000>;
required-opps = <&rpmhpd_opp_svs_l1>;
};
opp-460000000 {
opp-hz = /bits/ 64 <460000000>;
required-opps = <&rpmhpd_opp_nom>;
};
};
};
...

View File

@ -1,139 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/msm/qcom,sm8450-dpu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SM8450 Display DPU
maintainers:
- Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
$ref: /schemas/display/msm/dpu-common.yaml#
properties:
compatible:
const: qcom,sm8450-dpu
reg:
items:
- description: Address offset and size for mdp register set
- description: Address offset and size for vbif register set
reg-names:
items:
- const: mdp
- const: vbif
clocks:
items:
- description: Display hf axi
- description: Display sf axi
- description: Display ahb
- description: Display lut
- description: Display core
- description: Display vsync
clock-names:
items:
- const: bus
- const: nrt_bus
- const: iface
- const: lut
- const: core
- const: vsync
required:
- compatible
- reg
- reg-names
- clocks
- clock-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,sm8450-dispcc.h>
#include <dt-bindings/clock/qcom,gcc-sm8450.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interconnect/qcom,sm8450.h>
#include <dt-bindings/power/qcom,rpmhpd.h>
display-controller@ae01000 {
compatible = "qcom,sm8450-dpu";
reg = <0x0ae01000 0x8f000>,
<0x0aeb0000 0x2008>;
reg-names = "mdp", "vbif";
clocks = <&gcc GCC_DISP_HF_AXI_CLK>,
<&gcc GCC_DISP_SF_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 = "bus",
"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_MMCX>;
interrupt-parent = <&mdss>;
interrupts = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
dpu_intf1_out: endpoint {
remote-endpoint = <&dsi0_in>;
};
};
port@1 {
reg = <1>;
dpu_intf2_out: endpoint {
remote-endpoint = <&dsi1_in>;
};
};
};
mdp_opp_table: opp-table {
compatible = "operating-points-v2";
opp-172000000{
opp-hz = /bits/ 64 <172000000>;
required-opps = <&rpmhpd_opp_low_svs_d1>;
};
opp-200000000 {
opp-hz = /bits/ 64 <200000000>;
required-opps = <&rpmhpd_opp_low_svs>;
};
opp-325000000 {
opp-hz = /bits/ 64 <325000000>;
required-opps = <&rpmhpd_opp_svs>;
};
opp-375000000 {
opp-hz = /bits/ 64 <375000000>;
required-opps = <&rpmhpd_opp_svs_l1>;
};
opp-500000000 {
opp-hz = /bits/ 64 <500000000>;
required-opps = <&rpmhpd_opp_nom>;
};
};
};
...

View File

@ -1,133 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/msm/qcom,sm8550-dpu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SM8550 Display DPU
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
$ref: /schemas/display/msm/dpu-common.yaml#
properties:
compatible:
const: qcom,sm8550-dpu
reg:
items:
- description: Address offset and size for mdp register set
- description: Address offset and size for vbif register set
reg-names:
items:
- const: mdp
- const: vbif
clocks:
items:
- description: Display AHB
- description: Display hf axi
- description: Display MDSS ahb
- description: Display lut
- description: Display core
- description: Display vsync
clock-names:
items:
- const: bus
- const: nrt_bus
- const: iface
- const: lut
- const: core
- const: vsync
required:
- compatible
- reg
- reg-names
- clocks
- clock-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,sm8550-dispcc.h>
#include <dt-bindings/clock/qcom,sm8550-gcc.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/power/qcom,rpmhpd.h>
display-controller@ae01000 {
compatible = "qcom,sm8550-dpu";
reg = <0x0ae01000 0x8f000>,
<0x0aeb0000 0x2008>;
reg-names = "mdp", "vbif";
clocks = <&gcc GCC_DISP_AHB_CLK>,
<&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 = "bus",
"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_MMCX>;
interrupt-parent = <&mdss>;
interrupts = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
dpu_intf1_out: endpoint {
remote-endpoint = <&dsi0_in>;
};
};
port@1 {
reg = <1>;
dpu_intf2_out: endpoint {
remote-endpoint = <&dsi1_in>;
};
};
};
mdp_opp_table: opp-table {
compatible = "operating-points-v2";
opp-200000000 {
opp-hz = /bits/ 64 <200000000>;
required-opps = <&rpmhpd_opp_low_svs>;
};
opp-325000000 {
opp-hz = /bits/ 64 <325000000>;
required-opps = <&rpmhpd_opp_svs>;
};
opp-375000000 {
opp-hz = /bits/ 64 <375000000>;
required-opps = <&rpmhpd_opp_svs_l1>;
};
opp-514000000 {
opp-hz = /bits/ 64 <514000000>;
required-opps = <&rpmhpd_opp_nom>;
};
};
};
...

View File

@ -14,6 +14,7 @@ $ref: /schemas/display/msm/dpu-common.yaml#
properties:
compatible:
enum:
- qcom,sa8775p-dpu
- qcom,sm8650-dpu
- qcom,x1e80100-dpu

View File

@ -0,0 +1,99 @@
.. SPDX-License-Identifier: GPL-2.0
:orphan:
==============
MSM Preemption
==============
Preemption allows Adreno GPUs to switch to a higher priority ring when work is
pushed to it, reducing latency for high priority submissions.
When preemption is enabled 4 rings are initialized, corresponding to different
priority levels. Having multiple rings is purely a software concept as the GPU
only has registers to keep track of one graphics ring.
The kernel is able to switch which ring is currently being processed by
requesting preemption. When certain conditions are met, depending on the
priority level, the GPU will save its current state in a series of buffers,
then restores state from a similar set of buffers specified by the kernel. It
then resumes execution and fires an IRQ to let the kernel know the context
switch has completed.
This mechanism can be used by the kernel to switch between rings. Whenever a
submission occurs the kernel finds the highest priority ring which isn't empty
and preempts to it if said ring is not the one being currently executed. This is
also done whenever a submission completes to make sure execution resumes on a
lower priority ring when a higher priority ring is done.
Preemption levels
-----------------
Preemption can only occur at certain boundaries. The exact conditions can be
configured by changing the preemption level, this allows to compromise between
latency (ie. the time that passes between when the kernel requests preemption
and when the SQE begins saving state) and overhead (the amount of state that
needs to be saved).
The GPU offers 3 levels:
Level 0
Preemption only occurs at the submission level. This requires the least amount
of state to be saved as the execution of userspace submitted IBs is never
interrupted, however it offers very little benefit compared to not enabling
preemption of any kind.
Level 1
Preemption occurs at either bin level, if using GMEM rendering, or draw level
in the sysmem rendering case.
Level 2
Preemption occurs at draw level.
Level 1 is the mode that is used by the msm driver.
Additionally the GPU allows to specify a `skip_save_restore` option. This
disables the saving and restoring of all registers except those relating to the
operation of the SQE itself, reducing overhead. Saving and restoring is only
skipped when using GMEM with Level 1 preemption. When enabling this userspace is
expected to set the state that isn't preserved whenever preemption occurs which
is done by specifying preamble and postambles. Those are IBs that are executed
before and after preemption.
Preemption buffers
------------------
A series of buffers are necessary to store the state of rings while they are not
being executed. There are different kinds of preemption records and most of
those require one buffer per ring. This is because preemption never occurs
between submissions on the same ring, which always run in sequence when the ring
is active. This means that only one context per ring is effectively active.
SMMU_INFO
This buffer contains info about the current SMMU configuration such as the
ttbr0 register. The SQE firmware isn't actually able to save this record.
As a result SMMU info must be saved manually from the CP to a buffer and the
SMMU record updated with info from said buffer before triggering
preemption.
NON_SECURE
This is the main preemption record where most state is saved. It is mostly
opaque to the kernel except for the first few words that must be initialized
by the kernel.
SECURE
This saves state related to the GPU's secure mode.
NON_PRIV
The intended purpose of this record is unknown. The SQE firmware actually
ignores it and therefore msm doesn't handle it.
COUNTER
This record is used to save and restore performance counters.
Handling the permissions of those buffers is critical for security. All but the
NON_PRIV records need to be inaccessible from userspace, so they must be mapped
in the kernel address space with the MSM_BO_MAP_PRIV flag.
For example, making the NON_SECURE record accessible from userspace would allow
any process to manipulate a saved ring's RPTR which can be used to skip the
execution of some packets in a ring and execute user commands with higher
privileges.

View File

@ -23,6 +23,7 @@ adreno-y := \
adreno/a6xx_gpu.o \
adreno/a6xx_gmu.o \
adreno/a6xx_hfi.o \
adreno/a6xx_preempt.o \
adreno-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o \

View File

@ -22,7 +22,7 @@ static void a2xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
break;
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
/* ignore if there has not been a ctx switch: */
if (gpu->cur_ctx_seqno == submit->queue->ctx->seqno)
if (ring->cur_ctx_seqno == submit->queue->ctx->seqno)
break;
fallthrough;
case MSM_SUBMIT_CMD_BUF:

View File

@ -40,7 +40,7 @@ static void a3xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
break;
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
/* ignore if there has not been a ctx switch: */
if (gpu->cur_ctx_seqno == submit->queue->ctx->seqno)
if (ring->cur_ctx_seqno == submit->queue->ctx->seqno)
break;
fallthrough;
case MSM_SUBMIT_CMD_BUF:

View File

@ -34,7 +34,7 @@ static void a4xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
break;
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
/* ignore if there has not been a ctx switch: */
if (gpu->cur_ctx_seqno == submit->queue->ctx->seqno)
if (ring->cur_ctx_seqno == submit->queue->ctx->seqno)
break;
fallthrough;
case MSM_SUBMIT_CMD_BUF:

View File

@ -77,7 +77,7 @@ static void a5xx_submit_in_rb(struct msm_gpu *gpu, struct msm_gem_submit *submit
case MSM_SUBMIT_CMD_IB_TARGET_BUF:
break;
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
if (gpu->cur_ctx_seqno == submit->queue->ctx->seqno)
if (ring->cur_ctx_seqno == submit->queue->ctx->seqno)
break;
fallthrough;
case MSM_SUBMIT_CMD_BUF:
@ -132,7 +132,7 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
unsigned int i, ibs = 0;
if (IS_ENABLED(CONFIG_DRM_MSM_GPU_SUDO) && submit->in_rb) {
gpu->cur_ctx_seqno = 0;
ring->cur_ctx_seqno = 0;
a5xx_submit_in_rb(gpu, submit);
return;
}
@ -171,7 +171,7 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
case MSM_SUBMIT_CMD_IB_TARGET_BUF:
break;
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
if (gpu->cur_ctx_seqno == submit->queue->ctx->seqno)
if (ring->cur_ctx_seqno == submit->queue->ctx->seqno)
break;
fallthrough;
case MSM_SUBMIT_CMD_BUF:

View File

@ -307,7 +307,7 @@ int a5xx_power_init(struct msm_gpu *gpu)
else if (adreno_is_a540(adreno_gpu))
a540_lm_setup(gpu);
/* Set up SP/TP power collpase */
/* Set up SP/TP power collapse */
a5xx_pc_init(gpu);
/* Start the GPMU */

View File

@ -972,6 +972,25 @@ static const struct adreno_info a6xx_gpus[] = {
.prim_fifo_threshold = 0x00300200,
},
.address_space_size = SZ_16G,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x06060300),
.family = ADRENO_6XX_GEN4,
.fw = {
[ADRENO_FW_SQE] = "a660_sqe.fw",
[ADRENO_FW_GMU] = "a663_gmu.bin",
},
.gmem = SZ_1M + SZ_512K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
.init = a6xx_gpu_init,
.a6xx = &(const struct a6xx_info) {
.hwcg = a690_hwcg,
.protect = &a660_protect,
.gmu_cgc_mode = 0x00020200,
.prim_fifo_threshold = 0x00300200,
},
.address_space_size = SZ_16G,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x06030500),
.family = ADRENO_6XX_GEN4,
@ -1281,6 +1300,28 @@ static const u32 a730_protect_regs[] = {
};
DECLARE_ADRENO_PROTECT(a730_protect, 48);
static const uint32_t a7xx_pwrup_reglist_regs[] = {
REG_A6XX_UCHE_TRAP_BASE,
REG_A6XX_UCHE_TRAP_BASE + 1,
REG_A6XX_UCHE_WRITE_THRU_BASE,
REG_A6XX_UCHE_WRITE_THRU_BASE + 1,
REG_A6XX_UCHE_GMEM_RANGE_MIN,
REG_A6XX_UCHE_GMEM_RANGE_MIN + 1,
REG_A6XX_UCHE_GMEM_RANGE_MAX,
REG_A6XX_UCHE_GMEM_RANGE_MAX + 1,
REG_A6XX_UCHE_CACHE_WAYS,
REG_A6XX_UCHE_MODE_CNTL,
REG_A6XX_RB_NC_MODE_CNTL,
REG_A6XX_RB_CMP_DBG_ECO_CNTL,
REG_A7XX_GRAS_NC_MODE_CNTL,
REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE,
REG_A6XX_UCHE_GBIF_GX_CONFIG,
REG_A6XX_UCHE_CLIENT_PF,
REG_A6XX_TPL1_DBG_ECO_CNTL1,
};
DECLARE_ADRENO_REGLIST_LIST(a7xx_pwrup_reglist);
static const struct adreno_info a7xx_gpus[] = {
{
.chip_ids = ADRENO_CHIP_IDS(0x07000200),
@ -1315,15 +1356,18 @@ static const struct adreno_info a7xx_gpus[] = {
.gmem = SZ_2M,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
ADRENO_QUIRK_HAS_HW_APRIV |
ADRENO_QUIRK_PREEMPTION,
.init = a6xx_gpu_init,
.zapfw = "a730_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a730_hwcg,
.protect = &a730_protect,
.pwrup_reglist = &a7xx_pwrup_reglist,
.gmu_cgc_mode = 0x00020000,
},
.address_space_size = SZ_16G,
.preempt_record_size = 2860 * SZ_1K,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x43050a01), /* "C510v2" */
.family = ADRENO_7XX_GEN2,
@ -1334,16 +1378,19 @@ static const struct adreno_info a7xx_gpus[] = {
.gmem = 3 * SZ_1M,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
ADRENO_QUIRK_HAS_HW_APRIV |
ADRENO_QUIRK_PREEMPTION,
.init = a6xx_gpu_init,
.zapfw = "a740_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a740_hwcg,
.protect = &a730_protect,
.pwrup_reglist = &a7xx_pwrup_reglist,
.gmu_chipid = 0x7020100,
.gmu_cgc_mode = 0x00020202,
},
.address_space_size = SZ_16G,
.preempt_record_size = 4192 * SZ_1K,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x43050c01), /* "C512v2" */
.family = ADRENO_7XX_GEN2,
@ -1354,15 +1401,18 @@ static const struct adreno_info a7xx_gpus[] = {
.gmem = 3 * SZ_1M,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
ADRENO_QUIRK_HAS_HW_APRIV |
ADRENO_QUIRK_PREEMPTION,
.init = a6xx_gpu_init,
.a6xx = &(const struct a6xx_info) {
.hwcg = a740_hwcg,
.protect = &a730_protect,
.pwrup_reglist = &a7xx_pwrup_reglist,
.gmu_chipid = 0x7050001,
.gmu_cgc_mode = 0x00020202,
},
.address_space_size = SZ_256G,
.preempt_record_size = 4192 * SZ_1K,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x43051401), /* "C520v2" */
.family = ADRENO_7XX_GEN3,
@ -1373,15 +1423,18 @@ static const struct adreno_info a7xx_gpus[] = {
.gmem = 3 * SZ_1M,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
ADRENO_QUIRK_HAS_HW_APRIV |
ADRENO_QUIRK_PREEMPTION,
.init = a6xx_gpu_init,
.zapfw = "gen70900_zap.mbn",
.a6xx = &(const struct a6xx_info) {
.protect = &a730_protect,
.pwrup_reglist = &a7xx_pwrup_reglist,
.gmu_chipid = 0x7090100,
.gmu_cgc_mode = 0x00020202,
},
.address_space_size = SZ_16G,
.preempt_record_size = 3572 * SZ_1K,
}
};
DECLARE_ADRENO_GPULIST(a7xx);

View File

@ -1522,15 +1522,13 @@ static int a6xx_gmu_get_irq(struct a6xx_gmu *gmu, struct platform_device *pdev,
irq = platform_get_irq_byname(pdev, name);
ret = request_irq(irq, handler, IRQF_TRIGGER_HIGH, name, gmu);
ret = request_irq(irq, handler, IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, name, gmu);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "Unable to get interrupt %s %d\n",
name, ret);
return ret;
}
disable_irq(irq);
return irq;
}

View File

@ -99,6 +99,7 @@ struct a6xx_gmu {
struct completion pd_gate;
struct qmp *qmp;
struct a6xx_hfi_msg_bw_table *bw_table;
};
static inline u32 gmu_read(struct a6xx_gmu *gmu, u32 offset)

View File

@ -68,6 +68,8 @@ static void update_shadow_rptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
static void a6xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
uint32_t wptr;
unsigned long flags;
@ -81,12 +83,17 @@ static void a6xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
/* Make sure to wrap wptr if we need to */
wptr = get_wptr(ring);
/* Update HW if this is the current ring and we are not in preempt*/
if (!a6xx_in_preempt(a6xx_gpu)) {
if (a6xx_gpu->cur_ring == ring)
gpu_write(gpu, REG_A6XX_CP_RB_WPTR, wptr);
else
ring->restore_wptr = true;
} else {
ring->restore_wptr = true;
}
spin_unlock_irqrestore(&ring->preempt_lock, flags);
/* Make sure everything is posted before making a decision */
mb();
gpu_write(gpu, REG_A6XX_CP_RB_WPTR, wptr);
}
static void get_stats_counter(struct msm_ringbuffer *ring, u32 counter,
@ -110,7 +117,7 @@ static void a6xx_set_pagetable(struct a6xx_gpu *a6xx_gpu,
u32 asid;
u64 memptr = rbmemptr(ring, ttbr0);
if (ctx->seqno == a6xx_gpu->base.base.cur_ctx_seqno)
if (ctx->seqno == ring->cur_ctx_seqno)
return;
if (msm_iommu_pagetable_params(ctx->aspace->mmu, &ttbr, &asid))
@ -148,12 +155,14 @@ static void a6xx_set_pagetable(struct a6xx_gpu *a6xx_gpu,
/*
* Write the new TTBR0 to the memstore. This is good for debugging.
* Needed for preemption
*/
OUT_PKT7(ring, CP_MEM_WRITE, 4);
OUT_PKT7(ring, CP_MEM_WRITE, 5);
OUT_RING(ring, CP_MEM_WRITE_0_ADDR_LO(lower_32_bits(memptr)));
OUT_RING(ring, CP_MEM_WRITE_1_ADDR_HI(upper_32_bits(memptr)));
OUT_RING(ring, lower_32_bits(ttbr));
OUT_RING(ring, (asid << 16) | upper_32_bits(ttbr));
OUT_RING(ring, upper_32_bits(ttbr));
OUT_RING(ring, ctx->seqno);
/*
* Sync both threads after switching pagetables and enable BR only
@ -229,7 +238,7 @@ static void a6xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
case MSM_SUBMIT_CMD_IB_TARGET_BUF:
break;
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
if (gpu->cur_ctx_seqno == submit->queue->ctx->seqno)
if (ring->cur_ctx_seqno == submit->queue->ctx->seqno)
break;
fallthrough;
case MSM_SUBMIT_CMD_BUF:
@ -278,6 +287,46 @@ static void a6xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
a6xx_flush(gpu, ring);
}
static void a6xx_emit_set_pseudo_reg(struct msm_ringbuffer *ring,
struct a6xx_gpu *a6xx_gpu, struct msm_gpu_submitqueue *queue)
{
u64 preempt_postamble;
OUT_PKT7(ring, CP_SET_PSEUDO_REG, 12);
OUT_RING(ring, SMMU_INFO);
/* don't save SMMU, we write the record from the kernel instead */
OUT_RING(ring, 0);
OUT_RING(ring, 0);
/* privileged and non secure buffer save */
OUT_RING(ring, NON_SECURE_SAVE_ADDR);
OUT_RING(ring, lower_32_bits(
a6xx_gpu->preempt_iova[ring->id]));
OUT_RING(ring, upper_32_bits(
a6xx_gpu->preempt_iova[ring->id]));
/* user context buffer save, seems to be unnused by fw */
OUT_RING(ring, NON_PRIV_SAVE_ADDR);
OUT_RING(ring, 0);
OUT_RING(ring, 0);
OUT_RING(ring, COUNTER);
/* seems OK to set to 0 to disable it */
OUT_RING(ring, 0);
OUT_RING(ring, 0);
/* Emit postamble to clear perfcounters */
preempt_postamble = a6xx_gpu->preempt_postamble_iova;
OUT_PKT7(ring, CP_SET_AMBLE, 3);
OUT_RING(ring, lower_32_bits(preempt_postamble));
OUT_RING(ring, upper_32_bits(preempt_postamble));
OUT_RING(ring, CP_SET_AMBLE_2_DWORDS(
a6xx_gpu->preempt_postamble_len) |
CP_SET_AMBLE_2_TYPE(KMD_AMBLE_TYPE));
}
static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
{
unsigned int index = submit->seqno % MSM_GPU_SUBMIT_STATS_COUNT;
@ -295,6 +344,13 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
a6xx_set_pagetable(a6xx_gpu, ring, submit);
/*
* If preemption is enabled, then set the pseudo register for the save
* sequence
*/
if (gpu->nr_rings > 1)
a6xx_emit_set_pseudo_reg(ring, a6xx_gpu, submit->queue);
get_stats_counter(ring, REG_A7XX_RBBM_PERFCTR_CP(0),
rbmemptr_stats(ring, index, cpcycles_start));
get_stats_counter(ring, REG_A6XX_CP_ALWAYS_ON_COUNTER,
@ -306,8 +362,10 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
OUT_PKT7(ring, CP_SET_MARKER, 1);
OUT_RING(ring, 0x101); /* IFPC disable */
OUT_PKT7(ring, CP_SET_MARKER, 1);
OUT_RING(ring, 0x00d); /* IB1LIST start */
if (submit->queue->flags & MSM_SUBMITQUEUE_ALLOW_PREEMPT) {
OUT_PKT7(ring, CP_SET_MARKER, 1);
OUT_RING(ring, 0x00d); /* IB1LIST start */
}
/* Submit the commands */
for (i = 0; i < submit->nr_cmds; i++) {
@ -315,7 +373,7 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
case MSM_SUBMIT_CMD_IB_TARGET_BUF:
break;
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
if (gpu->cur_ctx_seqno == submit->queue->ctx->seqno)
if (ring->cur_ctx_seqno == submit->queue->ctx->seqno)
break;
fallthrough;
case MSM_SUBMIT_CMD_BUF:
@ -338,8 +396,10 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
update_shadow_rptr(gpu, ring);
}
OUT_PKT7(ring, CP_SET_MARKER, 1);
OUT_RING(ring, 0x00e); /* IB1LIST end */
if (submit->queue->flags & MSM_SUBMITQUEUE_ALLOW_PREEMPT) {
OUT_PKT7(ring, CP_SET_MARKER, 1);
OUT_RING(ring, 0x00e); /* IB1LIST end */
}
get_stats_counter(ring, REG_A7XX_RBBM_PERFCTR_CP(0),
rbmemptr_stats(ring, index, cpcycles_end));
@ -386,6 +446,8 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
OUT_RING(ring, upper_32_bits(rbmemptr(ring, bv_fence)));
OUT_RING(ring, submit->seqno);
a6xx_gpu->last_seqno[ring->id] = submit->seqno;
/* write the ringbuffer timestamp */
OUT_PKT7(ring, CP_EVENT_WRITE, 4);
OUT_RING(ring, CACHE_CLEAN | CP_EVENT_WRITE_0_IRQ | BIT(27));
@ -399,10 +461,32 @@ 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,
gpu_read64(gpu, REG_A6XX_CP_ALWAYS_ON_COUNTER));
a6xx_flush(gpu, ring);
/* Check to see if we need to start preemption */
a6xx_preempt_trigger(gpu);
}
static void a6xx_set_hwcg(struct msm_gpu *gpu, bool state)
@ -551,6 +635,15 @@ static void a6xx_calc_ubwc_config(struct adreno_gpu *gpu)
gpu->ubwc_config.macrotile_mode = 1;
}
if (adreno_is_a663(gpu)) {
gpu->ubwc_config.highest_bank_bit = 13;
gpu->ubwc_config.amsbc = 1;
gpu->ubwc_config.rgb565_predicator = 1;
gpu->ubwc_config.uavflagprd_inv = 2;
gpu->ubwc_config.macrotile_mode = 1;
gpu->ubwc_config.ubwc_swizzle = 0x4;
}
if (adreno_is_7c3(gpu)) {
gpu->ubwc_config.highest_bank_bit = 14;
gpu->ubwc_config.amsbc = 1;
@ -609,6 +702,77 @@ static void a6xx_set_ubwc_config(struct msm_gpu *gpu)
adreno_gpu->ubwc_config.macrotile_mode);
}
static void a7xx_patch_pwrup_reglist(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
const struct adreno_reglist_list *reglist;
void *ptr = a6xx_gpu->pwrup_reglist_ptr;
struct cpu_gpu_lock *lock = ptr;
u32 *dest = (u32 *)&lock->regs[0];
int i;
reglist = adreno_gpu->info->a6xx->pwrup_reglist;
lock->gpu_req = lock->cpu_req = lock->turn = 0;
lock->ifpc_list_len = 0;
lock->preemption_list_len = reglist->count;
/*
* For each entry in each of the lists, write the offset and the current
* register value into the GPU buffer
*/
for (i = 0; i < reglist->count; i++) {
*dest++ = reglist->regs[i];
*dest++ = gpu_read(gpu, reglist->regs[i]);
}
/*
* The overall register list is composed of
* 1. Static IFPC-only registers
* 2. Static IFPC + preemption registers
* 3. Dynamic IFPC + preemption registers (ex: perfcounter selects)
*
* The first two lists are static. Size of these lists are stored as
* number of pairs in ifpc_list_len and preemption_list_len
* respectively. With concurrent binning, Some of the perfcounter
* registers being virtualized, CP needs to know the pipe id to program
* the aperture inorder to restore the same. Thus, third list is a
* dynamic list with triplets as
* (<aperture, shifted 12 bits> <address> <data>), and the length is
* stored as number for triplets in dynamic_list_len.
*/
lock->dynamic_list_len = 0;
}
static int a7xx_preempt_start(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
struct msm_ringbuffer *ring = gpu->rb[0];
if (gpu->nr_rings <= 1)
return 0;
/* Turn CP protection off */
OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1);
OUT_RING(ring, 0);
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);
return a6xx_idle(gpu, ring) ? 0 : -EINVAL;
}
static int a6xx_cp_init(struct msm_gpu *gpu)
{
struct msm_ringbuffer *ring = gpu->rb[0];
@ -640,6 +804,8 @@ static int a6xx_cp_init(struct msm_gpu *gpu)
static int a7xx_cp_init(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
struct msm_ringbuffer *ring = gpu->rb[0];
u32 mask;
@ -677,11 +843,11 @@ static int a7xx_cp_init(struct msm_gpu *gpu)
/* *Don't* send a power up reg list for concurrent binning (TODO) */
/* Lo address */
OUT_RING(ring, 0x00000000);
OUT_RING(ring, lower_32_bits(a6xx_gpu->pwrup_reglist_iova));
/* Hi address */
OUT_RING(ring, 0x00000000);
OUT_RING(ring, upper_32_bits(a6xx_gpu->pwrup_reglist_iova));
/* BIT(31) set => read the regs from the list */
OUT_RING(ring, 0x00000000);
OUT_RING(ring, BIT(31));
a6xx_flush(gpu, ring);
return a6xx_idle(gpu, ring) ? 0 : -EINVAL;
@ -805,6 +971,16 @@ 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,
MSM_BO_WC | MSM_BO_MAP_PRIV,
gpu->aspace, &a6xx_gpu->pwrup_reglist_bo,
&a6xx_gpu->pwrup_reglist_iova);
if (IS_ERR(a6xx_gpu->pwrup_reglist_ptr))
return PTR_ERR(a6xx_gpu->pwrup_reglist_ptr);
msm_gem_object_set_name(a6xx_gpu->pwrup_reglist_bo, "pwrup_reglist");
return 0;
}
@ -864,6 +1040,7 @@ static int hw_init(struct msm_gpu *gpu)
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
u64 gmem_range_min;
unsigned int i;
int ret;
if (!adreno_has_gmu_wrapper(adreno_gpu)) {
@ -1072,7 +1249,7 @@ static int hw_init(struct msm_gpu *gpu)
if (adreno_is_a690(adreno_gpu))
gpu_write(gpu, REG_A6XX_UCHE_CMDQ_CONFIG, 0x90);
/* Set dualQ + disable afull for A660 GPU */
else if (adreno_is_a660(adreno_gpu))
else if (adreno_is_a660(adreno_gpu) || adreno_is_a663(adreno_gpu))
gpu_write(gpu, REG_A6XX_UCHE_CMDQ_CONFIG, 0x66906);
else if (adreno_is_a7xx(adreno_gpu))
gpu_write(gpu, REG_A6XX_UCHE_CMDQ_CONFIG,
@ -1134,22 +1311,32 @@ static int hw_init(struct msm_gpu *gpu)
if (a6xx_gpu->shadow_bo) {
gpu_write64(gpu, REG_A6XX_CP_RB_RPTR_ADDR,
shadowptr(a6xx_gpu, gpu->rb[0]));
for (unsigned int i = 0; i < gpu->nr_rings; i++)
a6xx_gpu->shadow[i] = 0;
}
/* ..which means "always" on A7xx, also for BV shadow */
if (adreno_is_a7xx(adreno_gpu)) {
gpu_write64(gpu, REG_A7XX_CP_BV_RB_RPTR_ADDR,
rbmemptr(gpu->rb[0], bv_fence));
rbmemptr(gpu->rb[0], bv_rptr));
}
a6xx_preempt_hw_init(gpu);
/* Always come up on rb 0 */
a6xx_gpu->cur_ring = gpu->rb[0];
gpu->cur_ctx_seqno = 0;
for (i = 0; i < gpu->nr_rings; i++)
gpu->rb[i]->cur_ctx_seqno = 0;
/* Enable the SQE_to start the CP engine */
gpu_write(gpu, REG_A6XX_CP_SQE_CNTL, 1);
if (adreno_is_a7xx(adreno_gpu) && !a6xx_gpu->pwrup_reglist_emitted) {
a7xx_patch_pwrup_reglist(gpu);
a6xx_gpu->pwrup_reglist_emitted = true;
}
ret = adreno_is_a7xx(adreno_gpu) ? a7xx_cp_init(gpu) : a6xx_cp_init(gpu);
if (ret)
goto out;
@ -1187,6 +1374,10 @@ static int hw_init(struct msm_gpu *gpu)
out:
if (adreno_has_gmu_wrapper(adreno_gpu))
return ret;
/* Last step - yield the ringbuffer */
a7xx_preempt_start(gpu);
/*
* Tell the GMU that we are done touching the GPU and it can start power
* management
@ -1564,8 +1755,13 @@ static irqreturn_t a6xx_irq(struct msm_gpu *gpu)
if (status & A6XX_RBBM_INT_0_MASK_SWFUSEVIOLATION)
a7xx_sw_fuse_violation_irq(gpu);
if (status & A6XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS)
if (status & A6XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS) {
msm_gpu_retire(gpu);
a6xx_preempt_trigger(gpu);
}
if (status & A6XX_RBBM_INT_0_MASK_CP_SW)
a6xx_preempt_irq(gpu);
return IRQ_HANDLED;
}
@ -2259,6 +2455,7 @@ struct msm_gpu *a6xx_gpu_init(struct drm_device *dev)
struct a6xx_gpu *a6xx_gpu;
struct adreno_gpu *adreno_gpu;
struct msm_gpu *gpu;
extern int enable_preemption;
bool is_a7xx;
int ret;
@ -2297,7 +2494,10 @@ struct msm_gpu *a6xx_gpu_init(struct drm_device *dev)
return ERR_PTR(ret);
}
if (is_a7xx)
if ((enable_preemption == 1) || (enable_preemption == -1 &&
(config->info->quirks & ADRENO_QUIRK_PREEMPTION)))
ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs_a7xx, 4);
else if (is_a7xx)
ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs_a7xx, 1);
else if (adreno_has_gmu_wrapper(adreno_gpu))
ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs_gmuwrapper, 1);
@ -2338,6 +2538,8 @@ struct msm_gpu *a6xx_gpu_init(struct drm_device *dev)
a6xx_fault_handler);
a6xx_calc_ubwc_config(adreno_gpu);
/* Set up the preemption specific bits and pieces for each ringbuffer */
a6xx_preempt_init(gpu);
return gpu;
}

View File

@ -12,15 +12,35 @@
extern bool hang_debug;
struct cpu_gpu_lock {
uint32_t gpu_req;
uint32_t cpu_req;
uint32_t turn;
union {
struct {
uint16_t list_length;
uint16_t list_offset;
};
struct {
uint8_t ifpc_list_len;
uint8_t preemption_list_len;
uint16_t dynamic_list_len;
};
};
uint64_t regs[62];
};
/**
* struct a6xx_info - a6xx specific information from device table
*
* @hwcg: hw clock gating register sequence
* @protect: CP_PROTECT settings
* @pwrup_reglist pwrup reglist for preemption
*/
struct a6xx_info {
const struct adreno_reglist *hwcg;
const struct adreno_protect *protect;
const struct adreno_reglist_list *pwrup_reglist;
u32 gmu_chipid;
u32 gmu_cgc_mode;
u32 prim_fifo_threshold;
@ -33,6 +53,29 @@ struct a6xx_gpu {
uint64_t sqe_iova;
struct msm_ringbuffer *cur_ring;
struct msm_ringbuffer *next_ring;
struct drm_gem_object *preempt_bo[MSM_GPU_MAX_RINGS];
void *preempt[MSM_GPU_MAX_RINGS];
uint64_t preempt_iova[MSM_GPU_MAX_RINGS];
struct drm_gem_object *preempt_smmu_bo[MSM_GPU_MAX_RINGS];
void *preempt_smmu[MSM_GPU_MAX_RINGS];
uint64_t preempt_smmu_iova[MSM_GPU_MAX_RINGS];
uint32_t last_seqno[MSM_GPU_MAX_RINGS];
atomic_t preempt_state;
spinlock_t eval_lock;
struct timer_list preempt_timer;
unsigned int preempt_level;
bool uses_gmem;
bool skip_save_restore;
struct drm_gem_object *preempt_postamble_bo;
void *preempt_postamble_ptr;
uint64_t preempt_postamble_iova;
uint64_t preempt_postamble_len;
bool postamble_enabled;
struct a6xx_gmu gmu;
@ -40,6 +83,11 @@ struct a6xx_gpu {
uint64_t shadow_iova;
uint32_t *shadow;
struct drm_gem_object *pwrup_reglist_bo;
void *pwrup_reglist_ptr;
uint64_t pwrup_reglist_iova;
bool pwrup_reglist_emitted;
bool has_whereami;
void __iomem *llc_mmio;
@ -51,6 +99,100 @@ struct a6xx_gpu {
#define to_a6xx_gpu(x) container_of(x, struct a6xx_gpu, base)
/*
* In order to do lockless preemption we use a simple state machine to progress
* through the process.
*
* PREEMPT_NONE - no preemption in progress. Next state START.
* PREEMPT_START - The trigger is evaluating if preemption is possible. Next
* states: TRIGGERED, NONE
* PREEMPT_FINISH - An intermediate state before moving back to NONE. Next
* state: NONE.
* PREEMPT_TRIGGERED: A preemption has been executed on the hardware. Next
* states: FAULTED, PENDING
* PREEMPT_FAULTED: A preemption timed out (never completed). This will trigger
* recovery. Next state: N/A
* PREEMPT_PENDING: Preemption complete interrupt fired - the callback is
* checking the success of the operation. Next state: FAULTED, NONE.
*/
enum a6xx_preempt_state {
PREEMPT_NONE = 0,
PREEMPT_START,
PREEMPT_FINISH,
PREEMPT_TRIGGERED,
PREEMPT_FAULTED,
PREEMPT_PENDING,
};
/*
* struct a6xx_preempt_record is a shared buffer between the microcode and the
* CPU to store the state for preemption. The record itself is much larger
* (2112k) but most of that is used by the CP for storage.
*
* There is a preemption record assigned per ringbuffer. When the CPU triggers a
* preemption, it fills out the record with the useful information (wptr, ring
* base, etc) and the microcode uses that information to set up the CP following
* the preemption. When a ring is switched out, the CP will save the ringbuffer
* state back to the record. In this way, once the records are properly set up
* the CPU can quickly switch back and forth between ringbuffers by only
* updating a few registers (often only the wptr).
*
* These are the CPU aware registers in the record:
* @magic: Must always be 0xAE399D6EUL
* @info: Type of the record - written 0 by the CPU, updated by the CP
* @errno: preemption error record
* @data: Data field in YIELD and SET_MARKER packets, Written and used by CP
* @cntl: Value of RB_CNTL written by CPU, save/restored by CP
* @rptr: Value of RB_RPTR written by CPU, save/restored by CP
* @wptr: Value of RB_WPTR written by CPU, save/restored by CP
* @_pad: Reserved/padding
* @rptr_addr: Value of RB_RPTR_ADDR_LO|HI written by CPU, save/restored by CP
* @rbase: Value of RB_BASE written by CPU, save/restored by CP
* @counter: GPU address of the storage area for the preemption counters
* @bv_rptr_addr: Value of BV_RB_RPTR_ADDR_LO|HI written by CPU, save/restored by CP
*/
struct a6xx_preempt_record {
u32 magic;
u32 info;
u32 errno;
u32 data;
u32 cntl;
u32 rptr;
u32 wptr;
u32 _pad;
u64 rptr_addr;
u64 rbase;
u64 counter;
u64 bv_rptr_addr;
};
#define A6XX_PREEMPT_RECORD_MAGIC 0xAE399D6EUL
#define PREEMPT_SMMU_INFO_SIZE 4096
#define PREEMPT_RECORD_SIZE(adreno_gpu) \
((adreno_gpu->info->preempt_record_size) == 0 ? \
4192 * SZ_1K : (adreno_gpu->info->preempt_record_size))
/*
* The preemption counter block is a storage area for the value of the
* preemption counters that are saved immediately before context switch. We
* append it on to the end of the allocation for the preemption record.
*/
#define A6XX_PREEMPT_COUNTER_SIZE (16 * 4)
struct a7xx_cp_smmu_info {
u32 magic;
u32 _pad4;
u64 ttbr0;
u32 asid;
u32 context_idr;
u32 context_bank;
};
#define GEN7_CP_SMMU_INFO_MAGIC 0x241350d5UL
/*
* Given a register and a count, return a value to program into
* REG_CP_PROTECT_REG(n) - this will block both reads and writes for
@ -108,6 +250,34 @@ 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_preempt_init(struct msm_gpu *gpu);
void a6xx_preempt_hw_init(struct msm_gpu *gpu);
void a6xx_preempt_trigger(struct msm_gpu *gpu);
void a6xx_preempt_irq(struct msm_gpu *gpu);
void a6xx_preempt_fini(struct msm_gpu *gpu);
int a6xx_preempt_submitqueue_setup(struct msm_gpu *gpu,
struct msm_gpu_submitqueue *queue);
void a6xx_preempt_submitqueue_close(struct msm_gpu *gpu,
struct msm_gpu_submitqueue *queue);
/* Return true if we are in a preempt state */
static inline bool a6xx_in_preempt(struct a6xx_gpu *a6xx_gpu)
{
/*
* Make sure the read to preempt_state is ordered with respect to reads
* of other variables before ...
*/
smp_rmb();
int preempt_state = atomic_read(&a6xx_gpu->preempt_state);
/* ... and after. */
smp_rmb();
return !(preempt_state == PREEMPT_NONE ||
preempt_state == PREEMPT_FINISH);
}
void a6xx_gmu_set_freq(struct msm_gpu *gpu, struct dev_pm_opp *opp,
bool suspended);
unsigned long a6xx_gmu_get_freq(struct msm_gpu *gpu);

View File

@ -478,6 +478,37 @@ static void a660_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
msg->cnoc_cmds_data[1][0] = 0x60000001;
}
static void a663_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
{
/*
* Send a single "off" entry just to get things running
* TODO: bus scaling
*/
msg->bw_level_num = 1;
msg->ddr_cmds_num = 3;
msg->ddr_wait_bitmask = 0x07;
msg->ddr_cmds_addrs[0] = 0x50004;
msg->ddr_cmds_addrs[1] = 0x50000;
msg->ddr_cmds_addrs[2] = 0x500b4;
msg->ddr_cmds_data[0][0] = 0x40000000;
msg->ddr_cmds_data[0][1] = 0x40000000;
msg->ddr_cmds_data[0][2] = 0x40000000;
/*
* These are the CX (CNOC) votes - these are used by the GMU but the
* votes are known and fixed for the target
*/
msg->cnoc_cmds_num = 1;
msg->cnoc_wait_bitmask = 0x01;
msg->cnoc_cmds_addrs[0] = 0x50058;
msg->cnoc_cmds_data[0][0] = 0x40000000;
msg->cnoc_cmds_data[1][0] = 0x60000001;
}
static void adreno_7c3_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
{
/*
@ -630,32 +661,44 @@ static void a6xx_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
static int a6xx_hfi_send_bw_table(struct a6xx_gmu *gmu)
{
struct a6xx_hfi_msg_bw_table msg = { 0 };
struct a6xx_hfi_msg_bw_table *msg;
struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
if (adreno_is_a618(adreno_gpu))
a618_build_bw_table(&msg);
else if (adreno_is_a619(adreno_gpu))
a619_build_bw_table(&msg);
else if (adreno_is_a640_family(adreno_gpu))
a640_build_bw_table(&msg);
else if (adreno_is_a650(adreno_gpu))
a650_build_bw_table(&msg);
else if (adreno_is_7c3(adreno_gpu))
adreno_7c3_build_bw_table(&msg);
else if (adreno_is_a660(adreno_gpu))
a660_build_bw_table(&msg);
else if (adreno_is_a690(adreno_gpu))
a690_build_bw_table(&msg);
else if (adreno_is_a730(adreno_gpu))
a730_build_bw_table(&msg);
else if (adreno_is_a740_family(adreno_gpu))
a740_build_bw_table(&msg);
else
a6xx_build_bw_table(&msg);
if (gmu->bw_table)
goto send;
return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_BW_TABLE, &msg, sizeof(msg),
msg = devm_kzalloc(gmu->dev, sizeof(*msg), GFP_KERNEL);
if (!msg)
return -ENOMEM;
if (adreno_is_a618(adreno_gpu))
a618_build_bw_table(msg);
else if (adreno_is_a619(adreno_gpu))
a619_build_bw_table(msg);
else if (adreno_is_a640_family(adreno_gpu))
a640_build_bw_table(msg);
else if (adreno_is_a650(adreno_gpu))
a650_build_bw_table(msg);
else if (adreno_is_7c3(adreno_gpu))
adreno_7c3_build_bw_table(msg);
else if (adreno_is_a660(adreno_gpu))
a660_build_bw_table(msg);
else if (adreno_is_a663(adreno_gpu))
a663_build_bw_table(msg);
else if (adreno_is_a690(adreno_gpu))
a690_build_bw_table(msg);
else if (adreno_is_a730(adreno_gpu))
a730_build_bw_table(msg);
else if (adreno_is_a740_family(adreno_gpu))
a740_build_bw_table(msg);
else
a6xx_build_bw_table(msg);
gmu->bw_table = msg;
send:
return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_BW_TABLE, gmu->bw_table, sizeof(*(gmu->bw_table)),
NULL, 0);
}

View File

@ -0,0 +1,456 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
/* Copyright (c) 2023 Collabora, Ltd. */
/* Copyright (c) 2024 Valve Corporation */
#include "msm_gem.h"
#include "a6xx_gpu.h"
#include "a6xx_gmu.xml.h"
#include "msm_mmu.h"
#include "msm_gpu_trace.h"
/*
* Try to transition the preemption state from old to new. Return
* true on success or false if the original state wasn't 'old'
*/
static inline bool try_preempt_state(struct a6xx_gpu *a6xx_gpu,
enum a6xx_preempt_state old, enum a6xx_preempt_state new)
{
enum a6xx_preempt_state cur = atomic_cmpxchg(&a6xx_gpu->preempt_state,
old, new);
return (cur == old);
}
/*
* Force the preemption state to the specified state. This is used in cases
* where the current state is known and won't change
*/
static inline void set_preempt_state(struct a6xx_gpu *gpu,
enum a6xx_preempt_state new)
{
/*
* preempt_state may be read by other cores trying to trigger a
* preemption or in the interrupt handler so barriers are needed
* before...
*/
smp_mb__before_atomic();
atomic_set(&gpu->preempt_state, new);
/* ... and after*/
smp_mb__after_atomic();
}
/* Write the most recent wptr for the given ring into the hardware */
static inline void update_wptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
{
unsigned long flags;
uint32_t wptr;
spin_lock_irqsave(&ring->preempt_lock, flags);
if (ring->restore_wptr) {
wptr = get_wptr(ring);
gpu_write(gpu, REG_A6XX_CP_RB_WPTR, wptr);
ring->restore_wptr = false;
}
spin_unlock_irqrestore(&ring->preempt_lock, flags);
}
/* Return the highest priority ringbuffer with something in it */
static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
unsigned long flags;
int i;
for (i = 0; i < gpu->nr_rings; i++) {
bool empty;
struct msm_ringbuffer *ring = gpu->rb[i];
spin_lock_irqsave(&ring->preempt_lock, flags);
empty = (get_wptr(ring) == gpu->funcs->get_rptr(gpu, ring));
if (!empty && ring == a6xx_gpu->cur_ring)
empty = ring->memptrs->fence == a6xx_gpu->last_seqno[i];
spin_unlock_irqrestore(&ring->preempt_lock, flags);
if (!empty)
return ring;
}
return NULL;
}
static void a6xx_preempt_timer(struct timer_list *t)
{
struct a6xx_gpu *a6xx_gpu = from_timer(a6xx_gpu, t, preempt_timer);
struct msm_gpu *gpu = &a6xx_gpu->base.base;
struct drm_device *dev = gpu->dev;
if (!try_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED, PREEMPT_FAULTED))
return;
dev_err(dev->dev, "%s: preemption timed out\n", gpu->name);
kthread_queue_work(gpu->worker, &gpu->recover_work);
}
static void preempt_prepare_postamble(struct a6xx_gpu *a6xx_gpu)
{
u32 *postamble = a6xx_gpu->preempt_postamble_ptr;
u32 count = 0;
postamble[count++] = PKT7(CP_REG_RMW, 3);
postamble[count++] = REG_A6XX_RBBM_PERFCTR_SRAM_INIT_CMD;
postamble[count++] = 0;
postamble[count++] = 1;
postamble[count++] = PKT7(CP_WAIT_REG_MEM, 6);
postamble[count++] = CP_WAIT_REG_MEM_0_FUNCTION(WRITE_EQ);
postamble[count++] = CP_WAIT_REG_MEM_1_POLL_ADDR_LO(
REG_A6XX_RBBM_PERFCTR_SRAM_INIT_STATUS);
postamble[count++] = CP_WAIT_REG_MEM_2_POLL_ADDR_HI(0);
postamble[count++] = CP_WAIT_REG_MEM_3_REF(0x1);
postamble[count++] = CP_WAIT_REG_MEM_4_MASK(0x1);
postamble[count++] = CP_WAIT_REG_MEM_5_DELAY_LOOP_CYCLES(0);
a6xx_gpu->preempt_postamble_len = count;
a6xx_gpu->postamble_enabled = true;
}
static void preempt_disable_postamble(struct a6xx_gpu *a6xx_gpu)
{
u32 *postamble = a6xx_gpu->preempt_postamble_ptr;
/*
* Disable the postamble by replacing the first packet header with a NOP
* that covers the whole buffer.
*/
*postamble = PKT7(CP_NOP, (a6xx_gpu->preempt_postamble_len - 1));
a6xx_gpu->postamble_enabled = false;
}
void a6xx_preempt_irq(struct msm_gpu *gpu)
{
uint32_t status;
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
struct drm_device *dev = gpu->dev;
if (!try_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED, PREEMPT_PENDING))
return;
/* Delete the preemption watchdog timer */
del_timer(&a6xx_gpu->preempt_timer);
/*
* The hardware should be setting the stop bit of CP_CONTEXT_SWITCH_CNTL
* to zero before firing the interrupt, but there is a non zero chance
* of a hardware condition or a software race that could set it again
* before we have a chance to finish. If that happens, log and go for
* recovery
*/
status = gpu_read(gpu, REG_A6XX_CP_CONTEXT_SWITCH_CNTL);
if (unlikely(status & A6XX_CP_CONTEXT_SWITCH_CNTL_STOP)) {
DRM_DEV_ERROR(&gpu->pdev->dev,
"!!!!!!!!!!!!!!!! preemption faulted !!!!!!!!!!!!!! irq\n");
set_preempt_state(a6xx_gpu, PREEMPT_FAULTED);
dev_err(dev->dev, "%s: Preemption failed to complete\n",
gpu->name);
kthread_queue_work(gpu->worker, &gpu->recover_work);
return;
}
a6xx_gpu->cur_ring = a6xx_gpu->next_ring;
a6xx_gpu->next_ring = NULL;
set_preempt_state(a6xx_gpu, PREEMPT_FINISH);
update_wptr(gpu, a6xx_gpu->cur_ring);
set_preempt_state(a6xx_gpu, PREEMPT_NONE);
trace_msm_gpu_preemption_irq(a6xx_gpu->cur_ring->id);
/*
* Retrigger preemption to avoid a deadlock that might occur when preemption
* is skipped due to it being already in flight when requested.
*/
a6xx_preempt_trigger(gpu);
}
void a6xx_preempt_hw_init(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
int i;
/* No preemption if we only have one ring */
if (gpu->nr_rings == 1)
return;
for (i = 0; i < gpu->nr_rings; i++) {
struct a6xx_preempt_record *record_ptr = a6xx_gpu->preempt[i];
record_ptr->wptr = 0;
record_ptr->rptr = 0;
record_ptr->rptr_addr = shadowptr(a6xx_gpu, gpu->rb[i]);
record_ptr->info = 0;
record_ptr->data = 0;
record_ptr->rbase = gpu->rb[i]->iova;
}
/* Write a 0 to signal that we aren't switching pagetables */
gpu_write64(gpu, REG_A6XX_CP_CONTEXT_SWITCH_SMMU_INFO, 0);
/* Enable the GMEM save/restore feature for preemption */
gpu_write(gpu, REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE, 0x1);
/* Reset the preemption state */
set_preempt_state(a6xx_gpu, PREEMPT_NONE);
spin_lock_init(&a6xx_gpu->eval_lock);
/* Always come up on rb 0 */
a6xx_gpu->cur_ring = gpu->rb[0];
}
void a6xx_preempt_trigger(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
unsigned long flags;
struct msm_ringbuffer *ring;
unsigned int cntl;
bool sysprof;
if (gpu->nr_rings == 1)
return;
/*
* Lock to make sure another thread attempting preemption doesn't skip it
* while we are still evaluating the next ring. This makes sure the other
* thread does start preemption if we abort it and avoids a soft lock.
*/
spin_lock_irqsave(&a6xx_gpu->eval_lock, flags);
/*
* Try to start preemption by moving from NONE to START. If
* unsuccessful, a preemption is already in flight
*/
if (!try_preempt_state(a6xx_gpu, PREEMPT_NONE, PREEMPT_START)) {
spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags);
return;
}
cntl = A6XX_CP_CONTEXT_SWITCH_CNTL_LEVEL(a6xx_gpu->preempt_level);
if (a6xx_gpu->skip_save_restore)
cntl |= A6XX_CP_CONTEXT_SWITCH_CNTL_SKIP_SAVE_RESTORE;
if (a6xx_gpu->uses_gmem)
cntl |= A6XX_CP_CONTEXT_SWITCH_CNTL_USES_GMEM;
cntl |= A6XX_CP_CONTEXT_SWITCH_CNTL_STOP;
/* Get the next ring to preempt to */
ring = get_next_ring(gpu);
/*
* If no ring is populated or the highest priority ring is the current
* one do nothing except to update the wptr to the latest and greatest
*/
if (!ring || (a6xx_gpu->cur_ring == ring)) {
set_preempt_state(a6xx_gpu, PREEMPT_FINISH);
update_wptr(gpu, a6xx_gpu->cur_ring);
set_preempt_state(a6xx_gpu, PREEMPT_NONE);
spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags);
return;
}
spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags);
spin_lock_irqsave(&ring->preempt_lock, flags);
struct a7xx_cp_smmu_info *smmu_info_ptr =
a6xx_gpu->preempt_smmu[ring->id];
struct a6xx_preempt_record *record_ptr = a6xx_gpu->preempt[ring->id];
u64 ttbr0 = ring->memptrs->ttbr0;
u32 context_idr = ring->memptrs->context_idr;
smmu_info_ptr->ttbr0 = ttbr0;
smmu_info_ptr->context_idr = context_idr;
record_ptr->wptr = get_wptr(ring);
/*
* The GPU will write the wptr we set above when we preempt. Reset
* restore_wptr to make sure that we don't write WPTR to the same
* thing twice. It's still possible subsequent submissions will update
* wptr again, in which case they will set the flag to true. This has
* to be protected by the lock for setting the flag and updating wptr
* to be atomic.
*/
ring->restore_wptr = false;
trace_msm_gpu_preemption_trigger(a6xx_gpu->cur_ring->id, ring->id);
spin_unlock_irqrestore(&ring->preempt_lock, flags);
gpu_write64(gpu,
REG_A6XX_CP_CONTEXT_SWITCH_SMMU_INFO,
a6xx_gpu->preempt_smmu_iova[ring->id]);
gpu_write64(gpu,
REG_A6XX_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR,
a6xx_gpu->preempt_iova[ring->id]);
a6xx_gpu->next_ring = ring;
/* Start a timer to catch a stuck preemption */
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;
if (!sysprof && !a6xx_gpu->postamble_enabled)
preempt_prepare_postamble(a6xx_gpu);
if (sysprof && a6xx_gpu->postamble_enabled)
preempt_disable_postamble(a6xx_gpu);
/* Set the preemption state to triggered */
set_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED);
/* Trigger the preemption */
gpu_write(gpu, REG_A6XX_CP_CONTEXT_SWITCH_CNTL, cntl);
}
static int preempt_init_ring(struct a6xx_gpu *a6xx_gpu,
struct msm_ringbuffer *ring)
{
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct msm_gpu *gpu = &adreno_gpu->base;
struct drm_gem_object *bo = NULL;
phys_addr_t ttbr;
u64 iova = 0;
void *ptr;
int asid;
ptr = msm_gem_kernel_new(gpu->dev,
PREEMPT_RECORD_SIZE(adreno_gpu),
MSM_BO_WC | MSM_BO_MAP_PRIV, gpu->aspace, &bo, &iova);
if (IS_ERR(ptr))
return PTR_ERR(ptr);
memset(ptr, 0, PREEMPT_RECORD_SIZE(adreno_gpu));
msm_gem_object_set_name(bo, "preempt_record ring%d", ring->id);
a6xx_gpu->preempt_bo[ring->id] = bo;
a6xx_gpu->preempt_iova[ring->id] = iova;
a6xx_gpu->preempt[ring->id] = ptr;
struct a6xx_preempt_record *record_ptr = ptr;
ptr = msm_gem_kernel_new(gpu->dev,
PREEMPT_SMMU_INFO_SIZE,
MSM_BO_WC | MSM_BO_MAP_PRIV | MSM_BO_GPU_READONLY,
gpu->aspace, &bo, &iova);
if (IS_ERR(ptr))
return PTR_ERR(ptr);
memset(ptr, 0, PREEMPT_SMMU_INFO_SIZE);
msm_gem_object_set_name(bo, "preempt_smmu_info ring%d", ring->id);
a6xx_gpu->preempt_smmu_bo[ring->id] = bo;
a6xx_gpu->preempt_smmu_iova[ring->id] = iova;
a6xx_gpu->preempt_smmu[ring->id] = ptr;
struct a7xx_cp_smmu_info *smmu_info_ptr = ptr;
msm_iommu_pagetable_params(gpu->aspace->mmu, &ttbr, &asid);
smmu_info_ptr->magic = GEN7_CP_SMMU_INFO_MAGIC;
smmu_info_ptr->ttbr0 = ttbr;
smmu_info_ptr->asid = 0xdecafbad;
smmu_info_ptr->context_idr = 0;
/* Set up the defaults on the preemption record */
record_ptr->magic = A6XX_PREEMPT_RECORD_MAGIC;
record_ptr->info = 0;
record_ptr->data = 0;
record_ptr->rptr = 0;
record_ptr->wptr = 0;
record_ptr->cntl = MSM_GPU_RB_CNTL_DEFAULT;
record_ptr->rbase = ring->iova;
record_ptr->counter = 0;
record_ptr->bv_rptr_addr = rbmemptr(ring, bv_rptr);
return 0;
}
void a6xx_preempt_fini(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
int i;
for (i = 0; i < gpu->nr_rings; i++)
msm_gem_kernel_put(a6xx_gpu->preempt_bo[i], gpu->aspace);
}
void a6xx_preempt_init(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
int i;
/* No preemption if we only have one ring */
if (gpu->nr_rings <= 1)
return;
for (i = 0; i < gpu->nr_rings; i++) {
if (preempt_init_ring(a6xx_gpu, gpu->rb[i]))
goto fail;
}
/* TODO: make this configurable? */
a6xx_gpu->preempt_level = 1;
a6xx_gpu->uses_gmem = 1;
a6xx_gpu->skip_save_restore = 1;
a6xx_gpu->preempt_postamble_ptr = msm_gem_kernel_new(gpu->dev,
PAGE_SIZE,
MSM_BO_WC | MSM_BO_MAP_PRIV | MSM_BO_GPU_READONLY,
gpu->aspace, &a6xx_gpu->preempt_postamble_bo,
&a6xx_gpu->preempt_postamble_iova);
preempt_prepare_postamble(a6xx_gpu);
if (IS_ERR(a6xx_gpu->preempt_postamble_ptr))
goto fail;
timer_setup(&a6xx_gpu->preempt_timer, a6xx_preempt_timer, 0);
return;
fail:
/*
* On any failure our adventure is over. Clean up and
* set nr_rings to 1 to force preemption off
*/
a6xx_preempt_fini(gpu);
gpu->nr_rings = 1;
DRM_DEV_ERROR(&gpu->pdev->dev,
"preemption init failed, disabling preemption\n");
return;
}

View File

@ -20,6 +20,10 @@ bool allow_vram_carveout = false;
MODULE_PARM_DESC(allow_vram_carveout, "Allow using VRAM Carveout, in place of IOMMU");
module_param_named(allow_vram_carveout, allow_vram_carveout, bool, 0600);
int enable_preemption = -1;
MODULE_PARM_DESC(enable_preemption, "Enable preemption (A7xx only) (1=on , 0=disable, -1=auto (default))");
module_param(enable_preemption, int, 0600);
extern const struct adreno_gpulist a2xx_gpulist;
extern const struct adreno_gpulist a3xx_gpulist;
extern const struct adreno_gpulist a4xx_gpulist;

View File

@ -533,7 +533,7 @@ int adreno_load_fw(struct adreno_gpu *adreno_gpu)
if (!adreno_gpu->info->fw[i])
continue;
/* Skip loading GMU firwmare with GMU Wrapper */
/* Skip loading GMU firmware with GMU Wrapper */
if (adreno_has_gmu_wrapper(adreno_gpu) && i == ADRENO_FW_GMU)
continue;

View File

@ -56,6 +56,7 @@ enum adreno_family {
#define ADRENO_QUIRK_LMLOADKILL_DISABLE BIT(2)
#define ADRENO_QUIRK_HAS_HW_APRIV BIT(3)
#define ADRENO_QUIRK_HAS_CACHED_COHERENT BIT(4)
#define ADRENO_QUIRK_PREEMPTION BIT(5)
/* Helper for formating the chip_id in the way that userspace tools like
* crashdec expect.
@ -111,6 +112,7 @@ struct adreno_info {
* {SHRT_MAX, 0} sentinal.
*/
struct adreno_speedbin *speedbins;
u64 preempt_record_size;
};
#define ADRENO_CHIP_IDS(tbl...) (uint32_t[]) { tbl, 0 }
@ -156,6 +158,19 @@ static const struct adreno_protect name = { \
.count_max = __count_max, \
};
struct adreno_reglist_list {
/** @reg: List of register **/
const u32 *regs;
/** @count: Number of registers in the list **/
u32 count;
};
#define DECLARE_ADRENO_REGLIST_LIST(name) \
static const struct adreno_reglist_list name = { \
.regs = name ## _regs, \
.count = ARRAY_SIZE(name ## _regs), \
};
struct adreno_gpu {
struct msm_gpu base;
const struct adreno_info *info;
@ -455,6 +470,11 @@ static inline int adreno_is_a680(const struct adreno_gpu *gpu)
return adreno_is_revn(gpu, 680);
}
static inline int adreno_is_a663(const struct adreno_gpu *gpu)
{
return gpu->info->chip_ids[0] == 0x06060300;
}
static inline int adreno_is_a690(const struct adreno_gpu *gpu)
{
return gpu->info->chip_ids[0] == 0x06090000;
@ -656,12 +676,15 @@ OUT_PKT4(struct msm_ringbuffer *ring, uint16_t regindx, uint16_t cnt)
OUT_RING(ring, PKT4(regindx, cnt));
}
#define PKT7(opcode, cnt) \
(CP_TYPE7_PKT | (cnt << 0) | (PM4_PARITY(cnt) << 15) | \
((opcode & 0x7F) << 16) | (PM4_PARITY(opcode) << 23))
static inline void
OUT_PKT7(struct msm_ringbuffer *ring, uint8_t opcode, uint16_t cnt)
{
adreno_wait_ring(ring, cnt + 1);
OUT_RING(ring, CP_TYPE7_PKT | (cnt << 0) | (PM4_PARITY(cnt) << 15) |
((opcode & 0x7F) << 16) | (PM4_PARITY(opcode) << 23));
OUT_RING(ring, PKT7(opcode, cnt));
}
struct msm_gpu *a2xx_gpu_init(struct drm_device *dev);

View File

@ -0,0 +1,210 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023, Linaro Limited
*/
#ifndef _DPU_1_14_MSM8937_H
#define _DPU_1_14_MSM8937_H
static const struct dpu_caps msm8937_dpu_caps = {
.max_mixer_width = DEFAULT_DPU_LINE_WIDTH,
.max_mixer_blendstages = 0x4,
.max_linewidth = DEFAULT_DPU_LINE_WIDTH,
.pixel_ram_size = 40 * 1024,
.max_hdeci_exp = MAX_HORZ_DECIMATION,
.max_vdeci_exp = MAX_VERT_DECIMATION,
};
static const struct dpu_mdp_cfg msm8937_mdp[] = {
{
.name = "top_0",
.base = 0x0, .len = 0x454,
.features = BIT(DPU_MDP_VSYNC_SEL),
.clk_ctrls = {
[DPU_CLK_CTRL_VIG0] = { .reg_off = 0x2ac, .bit_off = 0 },
[DPU_CLK_CTRL_RGB0] = { .reg_off = 0x2ac, .bit_off = 4 },
[DPU_CLK_CTRL_RGB1] = { .reg_off = 0x2b4, .bit_off = 4 },
[DPU_CLK_CTRL_DMA0] = { .reg_off = 0x2ac, .bit_off = 8 },
[DPU_CLK_CTRL_CURSOR0] = { .reg_off = 0x3a8, .bit_off = 16 },
},
},
};
static const struct dpu_ctl_cfg msm8937_ctl[] = {
{
.name = "ctl_0", .id = CTL_0,
.base = 0x1000, .len = 0x64,
}, {
.name = "ctl_1", .id = CTL_1,
.base = 0x1200, .len = 0x64,
}, {
.name = "ctl_2", .id = CTL_2,
.base = 0x1400, .len = 0x64,
},
};
static const struct dpu_sspp_cfg msm8937_sspp[] = {
{
.name = "sspp_0", .id = SSPP_VIG0,
.base = 0x4000, .len = 0x150,
.features = VIG_MSM8953_MASK,
.sblk = &dpu_vig_sblk_qseed2,
.xin_id = 0,
.type = SSPP_TYPE_VIG,
.clk_ctrl = DPU_CLK_CTRL_VIG0,
}, {
.name = "sspp_4", .id = SSPP_RGB0,
.base = 0x14000, .len = 0x150,
.features = RGB_MSM8953_MASK,
.sblk = &dpu_rgb_sblk,
.xin_id = 1,
.type = SSPP_TYPE_RGB,
.clk_ctrl = DPU_CLK_CTRL_RGB0,
}, {
.name = "sspp_5", .id = SSPP_RGB1,
.base = 0x16000, .len = 0x150,
.features = RGB_MSM8953_MASK,
.sblk = &dpu_rgb_sblk,
.xin_id = 5,
.type = SSPP_TYPE_RGB,
.clk_ctrl = DPU_CLK_CTRL_RGB1,
}, {
.name = "sspp_8", .id = SSPP_DMA0,
.base = 0x24000, .len = 0x150,
.features = DMA_MSM8953_MASK | BIT(DPU_SSPP_CURSOR),
.sblk = &dpu_dma_sblk,
.xin_id = 2,
.type = SSPP_TYPE_DMA,
.clk_ctrl = DPU_CLK_CTRL_DMA0,
},
};
static const struct dpu_lm_cfg msm8937_lm[] = {
{
.name = "lm_0", .id = LM_0,
.base = 0x44000, .len = 0x320,
.sblk = &msm8998_lm_sblk,
.lm_pair = LM_1,
.pingpong = PINGPONG_0,
.dspp = DSPP_0,
}, {
.name = "lm_1", .id = LM_1,
.base = 0x45000, .len = 0x320,
.sblk = &msm8998_lm_sblk,
.lm_pair = LM_0,
.pingpong = PINGPONG_1,
},
};
static const struct dpu_pingpong_cfg msm8937_pp[] = {
{
.name = "pingpong_0", .id = PINGPONG_0,
.base = 0x70000, .len = 0xd4,
.features = PINGPONG_MSM8996_MASK,
.sblk = &msm8996_pp_sblk,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8),
.intr_rdptr = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12),
}, {
.name = "pingpong_1", .id = PINGPONG_1,
.base = 0x70800, .len = 0xd4,
.features = PINGPONG_MSM8996_MASK,
.sblk = &msm8996_pp_sblk,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 9),
.intr_rdptr = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 13),
},
};
static const struct dpu_dspp_cfg msm8937_dspp[] = {
{
.name = "dspp_0", .id = DSPP_0,
.base = 0x54000, .len = 0x1800,
.features = DSPP_SC7180_MASK,
.sblk = &msm8998_dspp_sblk,
},
};
static const struct dpu_intf_cfg msm8937_intf[] = {
{
.name = "intf_1", .id = INTF_1,
.base = 0x6a800, .len = 0x268,
.type = INTF_DSI,
.controller_id = MSM_DSI_CONTROLLER_0,
.prog_fetch_lines_worst_case = 14,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 26),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 27),
.intr_tear_rd_ptr = -1,
}, {
.name = "intf_2", .id = INTF_2,
.base = 0x6b000, .len = 0x268,
.type = INTF_DSI,
.controller_id = MSM_DSI_CONTROLLER_1,
.prog_fetch_lines_worst_case = 14,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 28),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 29),
.intr_tear_rd_ptr = -1,
},
};
static const struct dpu_perf_cfg msm8937_perf_data = {
.max_bw_low = 3100000,
.max_bw_high = 3100000,
.min_core_ib = 2400000,
.min_llcc_ib = 0, /* No LLCC on this SoC */
.min_dram_ib = 800000,
.undersized_prefill_lines = 2,
.xtra_prefill_lines = 2,
.dest_scale_prefill_lines = 3,
.macrotile_prefill_lines = 4,
.yuv_nv12_prefill_lines = 8,
.linear_prefill_lines = 1,
.downscaling_prefill_lines = 1,
.amortizable_threshold = 25,
.min_prefill_lines = 14,
.danger_lut_tbl = {0xf, 0xffff, 0x0},
.safe_lut_tbl = {0xfffc, 0xff00, 0xffff},
.qos_lut_tbl = {
{.nentry = ARRAY_SIZE(msm8998_qos_linear),
.entries = msm8998_qos_linear
},
{.nentry = ARRAY_SIZE(msm8998_qos_macrotile),
.entries = msm8998_qos_macrotile
},
{.nentry = ARRAY_SIZE(msm8998_qos_nrt),
.entries = msm8998_qos_nrt
},
},
.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 msm8937_mdss_ver = {
.core_major_ver = 1,
.core_minor_ver = 14,
};
const struct dpu_mdss_cfg dpu_msm8937_cfg = {
.mdss_ver = &msm8937_mdss_ver,
.caps = &msm8937_dpu_caps,
.mdp = msm8937_mdp,
.ctl_count = ARRAY_SIZE(msm8937_ctl),
.ctl = msm8937_ctl,
.sspp_count = ARRAY_SIZE(msm8937_sspp),
.sspp = msm8937_sspp,
.mixer_count = ARRAY_SIZE(msm8937_lm),
.mixer = msm8937_lm,
.dspp_count = ARRAY_SIZE(msm8937_dspp),
.dspp = msm8937_dspp,
.pingpong_count = ARRAY_SIZE(msm8937_pp),
.pingpong = msm8937_pp,
.intf_count = ARRAY_SIZE(msm8937_intf),
.intf = msm8937_intf,
.vbif_count = ARRAY_SIZE(msm8996_vbif),
.vbif = msm8996_vbif,
.perf = &msm8937_perf_data,
};
#endif

View File

@ -0,0 +1,187 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023, Linaro Limited
*/
#ifndef _DPU_1_14_MSM8917_H
#define _DPU_1_14_MSM8917_H
static const struct dpu_caps msm8917_dpu_caps = {
.max_mixer_width = DEFAULT_DPU_LINE_WIDTH,
.max_mixer_blendstages = 0x4,
.max_linewidth = DEFAULT_DPU_LINE_WIDTH,
.pixel_ram_size = 16 * 1024,
.max_hdeci_exp = MAX_HORZ_DECIMATION,
.max_vdeci_exp = MAX_VERT_DECIMATION,
};
static const struct dpu_mdp_cfg msm8917_mdp[] = {
{
.name = "top_0",
.base = 0x0, .len = 0x454,
.features = BIT(DPU_MDP_VSYNC_SEL),
.clk_ctrls = {
[DPU_CLK_CTRL_VIG0] = { .reg_off = 0x2ac, .bit_off = 0 },
[DPU_CLK_CTRL_RGB0] = { .reg_off = 0x2ac, .bit_off = 4 },
[DPU_CLK_CTRL_RGB1] = { .reg_off = 0x2b4, .bit_off = 4 },
[DPU_CLK_CTRL_DMA0] = { .reg_off = 0x2ac, .bit_off = 8 },
[DPU_CLK_CTRL_CURSOR0] = { .reg_off = 0x3a8, .bit_off = 16 },
},
},
};
static const struct dpu_ctl_cfg msm8917_ctl[] = {
{
.name = "ctl_0", .id = CTL_0,
.base = 0x1000, .len = 0x64,
}, {
.name = "ctl_1", .id = CTL_1,
.base = 0x1200, .len = 0x64,
}, {
.name = "ctl_2", .id = CTL_2,
.base = 0x1400, .len = 0x64,
},
};
static const struct dpu_sspp_cfg msm8917_sspp[] = {
{
.name = "sspp_0", .id = SSPP_VIG0,
.base = 0x4000, .len = 0x150,
.features = VIG_MSM8953_MASK,
.sblk = &dpu_vig_sblk_qseed2,
.xin_id = 0,
.type = SSPP_TYPE_VIG,
.clk_ctrl = DPU_CLK_CTRL_VIG0,
}, {
.name = "sspp_4", .id = SSPP_RGB0,
.base = 0x14000, .len = 0x150,
.features = RGB_MSM8953_MASK,
.sblk = &dpu_rgb_sblk,
.xin_id = 1,
.type = SSPP_TYPE_RGB,
.clk_ctrl = DPU_CLK_CTRL_RGB0,
}, {
.name = "sspp_5", .id = SSPP_RGB1,
.base = 0x16000, .len = 0x150,
.features = RGB_MSM8953_MASK,
.sblk = &dpu_rgb_sblk,
.xin_id = 5,
.type = SSPP_TYPE_RGB,
.clk_ctrl = DPU_CLK_CTRL_RGB1,
}, {
.name = "sspp_8", .id = SSPP_DMA0,
.base = 0x24000, .len = 0x150,
.features = DMA_MSM8953_MASK | BIT(DPU_SSPP_CURSOR),
.sblk = &dpu_dma_sblk,
.xin_id = 2,
.type = SSPP_TYPE_DMA,
.clk_ctrl = DPU_CLK_CTRL_DMA0,
},
};
static const struct dpu_lm_cfg msm8917_lm[] = {
{
.name = "lm_0", .id = LM_0,
.base = 0x44000, .len = 0x320,
.sblk = &msm8998_lm_sblk,
.pingpong = PINGPONG_0,
.dspp = DSPP_0,
},
};
static const struct dpu_pingpong_cfg msm8917_pp[] = {
{
.name = "pingpong_0", .id = PINGPONG_0,
.base = 0x70000, .len = 0xd4,
.features = PINGPONG_MSM8996_MASK,
.sblk = &msm8996_pp_sblk,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8),
.intr_rdptr = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12),
},
};
static const struct dpu_dspp_cfg msm8917_dspp[] = {
{
.name = "dspp_0", .id = DSPP_0,
.base = 0x54000, .len = 0x1800,
.features = DSPP_SC7180_MASK,
.sblk = &msm8998_dspp_sblk,
},
};
static const struct dpu_intf_cfg msm8917_intf[] = {
{
.name = "intf_1", .id = INTF_1,
.base = 0x6a800, .len = 0x268,
.type = INTF_DSI,
.controller_id = MSM_DSI_CONTROLLER_0,
.prog_fetch_lines_worst_case = 14,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 26),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 27),
.intr_tear_rd_ptr = -1,
},
};
static const struct dpu_perf_cfg msm8917_perf_data = {
.max_bw_low = 1800000,
.max_bw_high = 1800000,
.min_core_ib = 2400000,
.min_llcc_ib = 0, /* No LLCC on this SoC */
.min_dram_ib = 800000,
.undersized_prefill_lines = 2,
.xtra_prefill_lines = 2,
.dest_scale_prefill_lines = 3,
.macrotile_prefill_lines = 4,
.yuv_nv12_prefill_lines = 8,
.linear_prefill_lines = 1,
.downscaling_prefill_lines = 1,
.amortizable_threshold = 25,
.min_prefill_lines = 21,
.danger_lut_tbl = {0xf, 0xffff, 0x0},
.safe_lut_tbl = {0xfffc, 0xff00, 0xffff},
.qos_lut_tbl = {
{.nentry = ARRAY_SIZE(msm8998_qos_linear),
.entries = msm8998_qos_linear
},
{.nentry = ARRAY_SIZE(msm8998_qos_macrotile),
.entries = msm8998_qos_macrotile
},
{.nentry = ARRAY_SIZE(msm8998_qos_nrt),
.entries = msm8998_qos_nrt
},
},
.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 msm8917_mdss_ver = {
.core_major_ver = 1,
.core_minor_ver = 15,
};
const struct dpu_mdss_cfg dpu_msm8917_cfg = {
.mdss_ver = &msm8917_mdss_ver,
.caps = &msm8917_dpu_caps,
.mdp = msm8917_mdp,
.ctl_count = ARRAY_SIZE(msm8917_ctl),
.ctl = msm8917_ctl,
.sspp_count = ARRAY_SIZE(msm8917_sspp),
.sspp = msm8917_sspp,
.mixer_count = ARRAY_SIZE(msm8917_lm),
.mixer = msm8917_lm,
.dspp_count = ARRAY_SIZE(msm8917_dspp),
.dspp = msm8917_dspp,
.pingpong_count = ARRAY_SIZE(msm8917_pp),
.pingpong = msm8917_pp,
.intf_count = ARRAY_SIZE(msm8917_intf),
.intf = msm8917_intf,
.vbif_count = ARRAY_SIZE(msm8996_vbif),
.vbif = msm8996_vbif,
.perf = &msm8917_perf_data,
};
#endif

View File

@ -0,0 +1,218 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023, Linaro Limited
*/
#ifndef _DPU_1_16_MSM8953_H
#define _DPU_1_16_MSM8953_H
static const struct dpu_caps msm8953_dpu_caps = {
.max_mixer_width = DEFAULT_DPU_LINE_WIDTH,
.max_mixer_blendstages = 0x4,
.max_linewidth = DEFAULT_DPU_LINE_WIDTH,
.pixel_ram_size = 40 * 1024,
.max_hdeci_exp = MAX_HORZ_DECIMATION,
.max_vdeci_exp = MAX_VERT_DECIMATION,
};
static const struct dpu_mdp_cfg msm8953_mdp[] = {
{
.name = "top_0",
.base = 0x0, .len = 0x454,
.features = BIT(DPU_MDP_VSYNC_SEL),
.clk_ctrls = {
[DPU_CLK_CTRL_VIG0] = { .reg_off = 0x2ac, .bit_off = 0 },
[DPU_CLK_CTRL_RGB0] = { .reg_off = 0x2ac, .bit_off = 4 },
[DPU_CLK_CTRL_RGB1] = { .reg_off = 0x2b4, .bit_off = 4 },
[DPU_CLK_CTRL_DMA0] = { .reg_off = 0x2ac, .bit_off = 8 },
[DPU_CLK_CTRL_CURSOR0] = { .reg_off = 0x3a8, .bit_off = 16 },
},
},
};
static const struct dpu_ctl_cfg msm8953_ctl[] = {
{
.name = "ctl_0", .id = CTL_0,
.base = 0x1000, .len = 0x64,
}, {
.name = "ctl_1", .id = CTL_1,
.base = 0x1200, .len = 0x64,
}, {
.name = "ctl_2", .id = CTL_2,
.base = 0x1400, .len = 0x64,
},
};
static const struct dpu_sspp_cfg msm8953_sspp[] = {
{
.name = "sspp_0", .id = SSPP_VIG0,
.base = 0x4000, .len = 0x150,
.features = VIG_MSM8953_MASK,
.sblk = &dpu_vig_sblk_qseed2,
.xin_id = 0,
.type = SSPP_TYPE_VIG,
.clk_ctrl = DPU_CLK_CTRL_VIG0,
}, {
.name = "sspp_4", .id = SSPP_RGB0,
.base = 0x14000, .len = 0x150,
.features = RGB_MSM8953_MASK,
.sblk = &dpu_rgb_sblk,
.xin_id = 1,
.type = SSPP_TYPE_RGB,
.clk_ctrl = DPU_CLK_CTRL_RGB0,
}, {
.name = "sspp_5", .id = SSPP_RGB1,
.base = 0x16000, .len = 0x150,
.features = RGB_MSM8953_MASK,
.sblk = &dpu_rgb_sblk,
.xin_id = 5,
.type = SSPP_TYPE_RGB,
.clk_ctrl = DPU_CLK_CTRL_RGB1,
}, {
.name = "sspp_8", .id = SSPP_DMA0,
.base = 0x24000, .len = 0x150,
.features = DMA_MSM8953_MASK | BIT(DPU_SSPP_CURSOR),
.sblk = &dpu_dma_sblk,
.xin_id = 2,
.type = SSPP_TYPE_DMA,
.clk_ctrl = DPU_CLK_CTRL_DMA0,
},
};
static const struct dpu_lm_cfg msm8953_lm[] = {
{
.name = "lm_0", .id = LM_0,
.base = 0x44000, .len = 0x320,
.sblk = &msm8998_lm_sblk,
.lm_pair = LM_1,
.pingpong = PINGPONG_0,
.dspp = DSPP_0,
}, {
.name = "lm_1", .id = LM_1,
.base = 0x45000, .len = 0x320,
.sblk = &msm8998_lm_sblk,
.lm_pair = LM_0,
.pingpong = PINGPONG_1,
},
};
static const struct dpu_pingpong_cfg msm8953_pp[] = {
{
.name = "pingpong_0", .id = PINGPONG_0,
.base = 0x70000, .len = 0xd4,
.features = PINGPONG_MSM8996_MASK,
.sblk = &msm8996_pp_sblk,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8),
.intr_rdptr = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12),
}, {
.name = "pingpong_1", .id = PINGPONG_1,
.base = 0x70800, .len = 0xd4,
.features = PINGPONG_MSM8996_MASK,
.sblk = &msm8996_pp_sblk,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 9),
.intr_rdptr = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 13),
},
};
static const struct dpu_dspp_cfg msm8953_dspp[] = {
{
.name = "dspp_0", .id = DSPP_0,
.base = 0x54000, .len = 0x1800,
.features = DSPP_SC7180_MASK,
.sblk = &msm8998_dspp_sblk,
},
};
static const struct dpu_intf_cfg msm8953_intf[] = {
{
.name = "intf_0", .id = INTF_0,
.base = 0x6a000, .len = 0x268,
.type = INTF_NONE,
.prog_fetch_lines_worst_case = 14,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 24),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 25),
.intr_tear_rd_ptr = -1,
}, {
.name = "intf_1", .id = INTF_1,
.base = 0x6a800, .len = 0x268,
.type = INTF_DSI,
.controller_id = MSM_DSI_CONTROLLER_0,
.prog_fetch_lines_worst_case = 14,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 26),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 27),
.intr_tear_rd_ptr = -1,
}, {
.name = "intf_2", .id = INTF_2,
.base = 0x6b000, .len = 0x268,
.type = INTF_DSI,
.controller_id = MSM_DSI_CONTROLLER_1,
.prog_fetch_lines_worst_case = 14,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 28),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 29),
.intr_tear_rd_ptr = -1,
},
};
static const struct dpu_perf_cfg msm8953_perf_data = {
.max_bw_low = 3400000,
.max_bw_high = 3400000,
.min_core_ib = 2400000,
.min_llcc_ib = 0, /* No LLCC on this SoC */
.min_dram_ib = 800000,
.undersized_prefill_lines = 2,
.xtra_prefill_lines = 2,
.dest_scale_prefill_lines = 3,
.macrotile_prefill_lines = 4,
.yuv_nv12_prefill_lines = 8,
.linear_prefill_lines = 1,
.downscaling_prefill_lines = 1,
.amortizable_threshold = 25,
.min_prefill_lines = 14,
.danger_lut_tbl = {0xf, 0xffff, 0x0},
.safe_lut_tbl = {0xfffc, 0xff00, 0xffff},
.qos_lut_tbl = {
{.nentry = ARRAY_SIZE(msm8998_qos_linear),
.entries = msm8998_qos_linear
},
{.nentry = ARRAY_SIZE(msm8998_qos_macrotile),
.entries = msm8998_qos_macrotile
},
{.nentry = ARRAY_SIZE(msm8998_qos_nrt),
.entries = msm8998_qos_nrt
},
},
.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 msm8953_mdss_ver = {
.core_major_ver = 1,
.core_minor_ver = 16,
};
const struct dpu_mdss_cfg dpu_msm8953_cfg = {
.mdss_ver = &msm8953_mdss_ver,
.caps = &msm8953_dpu_caps,
.mdp = msm8953_mdp,
.ctl_count = ARRAY_SIZE(msm8953_ctl),
.ctl = msm8953_ctl,
.sspp_count = ARRAY_SIZE(msm8953_sspp),
.sspp = msm8953_sspp,
.mixer_count = ARRAY_SIZE(msm8953_lm),
.mixer = msm8953_lm,
.dspp_count = ARRAY_SIZE(msm8953_dspp),
.dspp = msm8953_dspp,
.pingpong_count = ARRAY_SIZE(msm8953_pp),
.pingpong = msm8953_pp,
.intf_count = ARRAY_SIZE(msm8953_intf),
.intf = msm8953_intf,
.vbif_count = ARRAY_SIZE(msm8996_vbif),
.vbif = msm8996_vbif,
.perf = &msm8953_perf_data,
};
#endif

View File

@ -0,0 +1,338 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023, Linaro Limited
* Copyright (c) 2022. Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved.
*/
#ifndef _DPU_1_7_MSM8996_H
#define _DPU_1_7_MSM8996_H
static const struct dpu_caps msm8996_dpu_caps = {
.max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH,
.max_mixer_blendstages = 0x7,
.has_src_split = true,
.max_linewidth = DEFAULT_DPU_OUTPUT_LINE_WIDTH,
.pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE,
.max_hdeci_exp = MAX_HORZ_DECIMATION,
.max_vdeci_exp = MAX_VERT_DECIMATION,
};
static const struct dpu_mdp_cfg msm8996_mdp[] = {
{
.name = "top_0",
.base = 0x0, .len = 0x454,
.features = BIT(DPU_MDP_VSYNC_SEL),
.clk_ctrls = {
[DPU_CLK_CTRL_VIG0] = { .reg_off = 0x2ac, .bit_off = 0 },
[DPU_CLK_CTRL_VIG1] = { .reg_off = 0x2b4, .bit_off = 0 },
[DPU_CLK_CTRL_VIG2] = { .reg_off = 0x2bc, .bit_off = 0 },
[DPU_CLK_CTRL_VIG3] = { .reg_off = 0x2c4, .bit_off = 0 },
[DPU_CLK_CTRL_RGB0] = { .reg_off = 0x2ac, .bit_off = 4 },
[DPU_CLK_CTRL_RGB1] = { .reg_off = 0x2b4, .bit_off = 4 },
[DPU_CLK_CTRL_RGB2] = { .reg_off = 0x2bc, .bit_off = 4 },
[DPU_CLK_CTRL_RGB3] = { .reg_off = 0x2c4, .bit_off = 4 },
[DPU_CLK_CTRL_DMA0] = { .reg_off = 0x2ac, .bit_off = 8 },
[DPU_CLK_CTRL_DMA1] = { .reg_off = 0x2b4, .bit_off = 8 },
[DPU_CLK_CTRL_CURSOR0] = { .reg_off = 0x3a8, .bit_off = 16 },
[DPU_CLK_CTRL_CURSOR1] = { .reg_off = 0x3b0, .bit_off = 16 },
},
},
};
static const struct dpu_ctl_cfg msm8996_ctl[] = {
{
.name = "ctl_0", .id = CTL_0,
.base = 0x1000, .len = 0x64,
}, {
.name = "ctl_1", .id = CTL_1,
.base = 0x1200, .len = 0x64,
}, {
.name = "ctl_2", .id = CTL_2,
.base = 0x1400, .len = 0x64,
}, {
.name = "ctl_3", .id = CTL_3,
.base = 0x1600, .len = 0x64,
}, {
.name = "ctl_4", .id = CTL_4,
.base = 0x1800, .len = 0x64,
},
};
static const struct dpu_sspp_cfg msm8996_sspp[] = {
{
.name = "sspp_0", .id = SSPP_VIG0,
.base = 0x4000, .len = 0x150,
.features = VIG_MSM8996_MASK,
.sblk = &dpu_vig_sblk_qseed2,
.xin_id = 0,
.type = SSPP_TYPE_VIG,
.clk_ctrl = DPU_CLK_CTRL_VIG0,
}, {
.name = "sspp_1", .id = SSPP_VIG1,
.base = 0x6000, .len = 0x150,
.features = VIG_MSM8996_MASK,
.sblk = &dpu_vig_sblk_qseed2,
.xin_id = 4,
.type = SSPP_TYPE_VIG,
.clk_ctrl = DPU_CLK_CTRL_VIG1,
}, {
.name = "sspp_2", .id = SSPP_VIG2,
.base = 0x8000, .len = 0x150,
.features = VIG_MSM8996_MASK,
.sblk = &dpu_vig_sblk_qseed2,
.xin_id = 8,
.type = SSPP_TYPE_VIG,
.clk_ctrl = DPU_CLK_CTRL_VIG2,
}, {
.name = "sspp_3", .id = SSPP_VIG3,
.base = 0xa000, .len = 0x150,
.features = VIG_MSM8996_MASK,
.sblk = &dpu_vig_sblk_qseed2,
.xin_id = 12,
.type = SSPP_TYPE_VIG,
.clk_ctrl = DPU_CLK_CTRL_VIG3,
}, {
.name = "sspp_4", .id = SSPP_RGB0,
.base = 0x14000, .len = 0x150,
.features = RGB_MSM8996_MASK,
.sblk = &dpu_rgb_sblk,
.xin_id = 1,
.type = SSPP_TYPE_RGB,
.clk_ctrl = DPU_CLK_CTRL_RGB0,
}, {
.name = "sspp_5", .id = SSPP_RGB1,
.base = 0x16000, .len = 0x150,
.features = RGB_MSM8996_MASK,
.sblk = &dpu_rgb_sblk,
.xin_id = 5,
.type = SSPP_TYPE_RGB,
.clk_ctrl = DPU_CLK_CTRL_RGB1,
}, {
.name = "sspp_6", .id = SSPP_RGB2,
.base = 0x18000, .len = 0x150,
.features = RGB_MSM8996_MASK,
.sblk = &dpu_rgb_sblk,
.xin_id = 9,
.type = SSPP_TYPE_RGB,
.clk_ctrl = DPU_CLK_CTRL_RGB2,
}, {
.name = "sspp_7", .id = SSPP_RGB3,
.base = 0x1a000, .len = 0x150,
.features = RGB_MSM8996_MASK,
.sblk = &dpu_rgb_sblk,
.xin_id = 13,
.type = SSPP_TYPE_RGB,
.clk_ctrl = DPU_CLK_CTRL_RGB3,
}, {
.name = "sspp_8", .id = SSPP_DMA0,
.base = 0x24000, .len = 0x150,
.features = DMA_MSM8996_MASK,
.sblk = &dpu_dma_sblk,
.xin_id = 2,
.type = SSPP_TYPE_DMA,
.clk_ctrl = DPU_CLK_CTRL_DMA0,
}, {
.name = "sspp_9", .id = SSPP_DMA1,
.base = 0x26000, .len = 0x150,
.features = DMA_MSM8996_MASK,
.sblk = &dpu_dma_sblk,
.xin_id = 10,
.type = SSPP_TYPE_DMA,
.clk_ctrl = DPU_CLK_CTRL_DMA1,
},
};
static const struct dpu_lm_cfg msm8996_lm[] = {
{
.name = "lm_0", .id = LM_0,
.base = 0x44000, .len = 0x320,
.features = MIXER_MSM8998_MASK,
.sblk = &msm8998_lm_sblk,
.lm_pair = LM_1,
.pingpong = PINGPONG_0,
.dspp = DSPP_0,
}, {
.name = "lm_1", .id = LM_1,
.base = 0x45000, .len = 0x320,
.features = MIXER_MSM8998_MASK,
.sblk = &msm8998_lm_sblk,
.lm_pair = LM_0,
.pingpong = PINGPONG_1,
.dspp = DSPP_1,
}, {
.name = "lm_2", .id = LM_2,
.base = 0x46000, .len = 0x320,
.features = MIXER_MSM8998_MASK,
.sblk = &msm8998_lm_sblk,
.lm_pair = LM_5,
.pingpong = PINGPONG_2,
}, {
.name = "lm_5", .id = LM_5,
.base = 0x49000, .len = 0x320,
.features = MIXER_MSM8998_MASK,
.sblk = &msm8998_lm_sblk,
.lm_pair = LM_2,
.pingpong = PINGPONG_3,
},
};
static const struct dpu_pingpong_cfg msm8996_pp[] = {
{
.name = "pingpong_0", .id = PINGPONG_0,
.base = 0x70000, .len = 0xd4,
.features = PINGPONG_MSM8996_TE2_MASK,
.sblk = &msm8996_pp_sblk_te,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8),
.intr_rdptr = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12),
}, {
.name = "pingpong_1", .id = PINGPONG_1,
.base = 0x70800, .len = 0xd4,
.features = PINGPONG_MSM8996_TE2_MASK,
.sblk = &msm8996_pp_sblk_te,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 9),
.intr_rdptr = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 13),
}, {
.name = "pingpong_2", .id = PINGPONG_2,
.base = 0x71000, .len = 0xd4,
.features = PINGPONG_MSM8996_MASK,
.sblk = &msm8996_pp_sblk,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 10),
.intr_rdptr = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 14),
}, {
.name = "pingpong_3", .id = PINGPONG_3,
.base = 0x71800, .len = 0xd4,
.features = PINGPONG_MSM8996_MASK,
.sblk = &msm8996_pp_sblk,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 11),
.intr_rdptr = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 15),
},
};
static const struct dpu_dsc_cfg msm8996_dsc[] = {
{
.name = "dsc_0", .id = DSC_0,
.base = 0x80000, .len = 0x140,
}, {
.name = "dsc_1", .id = DSC_1,
.base = 0x80400, .len = 0x140,
},
};
static const struct dpu_dspp_cfg msm8996_dspp[] = {
{
.name = "dspp_0", .id = DSPP_0,
.base = 0x54000, .len = 0x1800,
.features = DSPP_SC7180_MASK,
.sblk = &msm8998_dspp_sblk,
}, {
.name = "dspp_1", .id = DSPP_1,
.base = 0x56000, .len = 0x1800,
.features = DSPP_SC7180_MASK,
.sblk = &msm8998_dspp_sblk,
},
};
static const struct dpu_intf_cfg msm8996_intf[] = {
{
.name = "intf_0", .id = INTF_0,
.base = 0x6a000, .len = 0x268,
.type = INTF_NONE,
.prog_fetch_lines_worst_case = 25,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 24),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 25),
.intr_tear_rd_ptr = -1,
}, {
.name = "intf_1", .id = INTF_1,
.base = 0x6a800, .len = 0x268,
.type = INTF_DSI,
.controller_id = MSM_DSI_CONTROLLER_0,
.prog_fetch_lines_worst_case = 25,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 26),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 27),
.intr_tear_rd_ptr = -1,
}, {
.name = "intf_2", .id = INTF_2,
.base = 0x6b000, .len = 0x268,
.type = INTF_DSI,
.controller_id = MSM_DSI_CONTROLLER_1,
.prog_fetch_lines_worst_case = 25,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 28),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 29),
.intr_tear_rd_ptr = -1,
}, {
.name = "intf_3", .id = INTF_3,
.base = 0x6b800, .len = 0x268,
.type = INTF_HDMI,
.prog_fetch_lines_worst_case = 25,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31),
.intr_tear_rd_ptr = -1,
},
};
static const struct dpu_perf_cfg msm8996_perf_data = {
.max_bw_low = 9600000,
.max_bw_high = 9600000,
.min_core_ib = 2400000,
.min_llcc_ib = 0, /* No LLCC on this SoC */
.min_dram_ib = 800000,
.undersized_prefill_lines = 2,
.xtra_prefill_lines = 2,
.dest_scale_prefill_lines = 3,
.macrotile_prefill_lines = 4,
.yuv_nv12_prefill_lines = 8,
.linear_prefill_lines = 1,
.downscaling_prefill_lines = 1,
.amortizable_threshold = 25,
.min_prefill_lines = 21,
.danger_lut_tbl = {0xf, 0xffff, 0x0},
.safe_lut_tbl = {0xfffc, 0xff00, 0xffff},
.qos_lut_tbl = {
{.nentry = ARRAY_SIZE(msm8998_qos_linear),
.entries = msm8998_qos_linear
},
{.nentry = ARRAY_SIZE(msm8998_qos_macrotile),
.entries = msm8998_qos_macrotile
},
{.nentry = ARRAY_SIZE(msm8998_qos_nrt),
.entries = msm8998_qos_nrt
},
},
.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 msm8996_mdss_ver = {
.core_major_ver = 1,
.core_minor_ver = 7,
};
const struct dpu_mdss_cfg dpu_msm8996_cfg = {
.mdss_ver = &msm8996_mdss_ver,
.caps = &msm8996_dpu_caps,
.mdp = msm8996_mdp,
.ctl_count = ARRAY_SIZE(msm8996_ctl),
.ctl = msm8996_ctl,
.sspp_count = ARRAY_SIZE(msm8996_sspp),
.sspp = msm8996_sspp,
.mixer_count = ARRAY_SIZE(msm8996_lm),
.mixer = msm8996_lm,
.dspp_count = ARRAY_SIZE(msm8996_dspp),
.dspp = msm8996_dspp,
.pingpong_count = ARRAY_SIZE(msm8996_pp),
.pingpong = msm8996_pp,
.dsc_count = ARRAY_SIZE(msm8996_dsc),
.dsc = msm8996_dsc,
.intf_count = ARRAY_SIZE(msm8996_intf),
.intf = msm8996_intf,
.vbif_count = ARRAY_SIZE(msm8996_vbif),
.vbif = msm8996_vbif,
.perf = &msm8996_perf_data,
};
#endif

View File

@ -156,18 +156,6 @@ static const struct dpu_lm_cfg msm8998_lm[] = {
.sblk = &msm8998_lm_sblk,
.lm_pair = LM_5,
.pingpong = PINGPONG_2,
}, {
.name = "lm_3", .id = LM_3,
.base = 0x47000, .len = 0x320,
.features = MIXER_MSM8998_MASK,
.sblk = &msm8998_lm_sblk,
.pingpong = PINGPONG_NONE,
}, {
.name = "lm_4", .id = LM_4,
.base = 0x48000, .len = 0x320,
.features = MIXER_MSM8998_MASK,
.sblk = &msm8998_lm_sblk,
.pingpong = PINGPONG_NONE,
}, {
.name = "lm_5", .id = LM_5,
.base = 0x49000, .len = 0x320,

View File

@ -155,19 +155,6 @@ static const struct dpu_lm_cfg sdm845_lm[] = {
.lm_pair = LM_5,
.pingpong = PINGPONG_2,
.dspp = DSPP_2,
}, {
.name = "lm_3", .id = LM_3,
.base = 0x0, .len = 0x320,
.features = MIXER_SDM845_MASK,
.sblk = &sdm845_lm_sblk,
.pingpong = PINGPONG_NONE,
.dspp = DSPP_3,
}, {
.name = "lm_4", .id = LM_4,
.base = 0x0, .len = 0x320,
.features = MIXER_SDM845_MASK,
.sblk = &sdm845_lm_sblk,
.pingpong = PINGPONG_NONE,
}, {
.name = "lm_5", .id = LM_5,
.base = 0x49000, .len = 0x320,
@ -175,6 +162,7 @@ static const struct dpu_lm_cfg sdm845_lm[] = {
.sblk = &sdm845_lm_sblk,
.lm_pair = LM_2,
.pingpong = PINGPONG_3,
.dspp = DSPP_3,
},
};

View File

@ -0,0 +1,485 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _DPU_8_4_SA8775P_H
#define _DPU_8_4_SA8775P_H
static const struct dpu_caps sa8775p_dpu_caps = {
.max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH,
.max_mixer_blendstages = 0xb,
.has_src_split = true,
.has_dim_layer = true,
.has_idle_pc = true,
.has_3d_merge = true,
.max_linewidth = 5120,
.pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE,
};
static const struct dpu_mdp_cfg sa8775p_mdp = {
.name = "top_0",
.base = 0x0, .len = 0x494,
.features = BIT(DPU_MDP_PERIPH_0_REMOVED),
.clk_ctrls = {
[DPU_CLK_CTRL_VIG0] = { .reg_off = 0x2ac, .bit_off = 0 },
[DPU_CLK_CTRL_VIG1] = { .reg_off = 0x2b4, .bit_off = 0 },
[DPU_CLK_CTRL_VIG2] = { .reg_off = 0x2bc, .bit_off = 0 },
[DPU_CLK_CTRL_VIG3] = { .reg_off = 0x2c4, .bit_off = 0 },
[DPU_CLK_CTRL_DMA0] = { .reg_off = 0x2ac, .bit_off = 8 },
[DPU_CLK_CTRL_DMA1] = { .reg_off = 0x2b4, .bit_off = 8 },
[DPU_CLK_CTRL_DMA2] = { .reg_off = 0x2bc, .bit_off = 8 },
[DPU_CLK_CTRL_DMA3] = { .reg_off = 0x2c4, .bit_off = 8 },
[DPU_CLK_CTRL_WB2] = { .reg_off = 0x2bc, .bit_off = 16 },
[DPU_CLK_CTRL_REG_DMA] = { .reg_off = 0x2bc, .bit_off = 20 },
},
};
/* FIXME: get rid of DPU_CTL_SPLIT_DISPLAY in favour of proper ACTIVE_CTL support */
static const struct dpu_ctl_cfg sa8775p_ctl[] = {
{
.name = "ctl_0", .id = CTL_0,
.base = 0x15000, .len = 0x204,
.features = BIT(DPU_CTL_SPLIT_DISPLAY) | CTL_SC7280_MASK,
.intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 9),
}, {
.name = "ctl_1", .id = CTL_1,
.base = 0x16000, .len = 0x204,
.features = BIT(DPU_CTL_SPLIT_DISPLAY) | CTL_SC7280_MASK,
.intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 10),
}, {
.name = "ctl_2", .id = CTL_2,
.base = 0x17000, .len = 0x204,
.features = CTL_SC7280_MASK,
.intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 11),
}, {
.name = "ctl_3", .id = CTL_3,
.base = 0x18000, .len = 0x204,
.features = CTL_SC7280_MASK,
.intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 12),
}, {
.name = "ctl_4", .id = CTL_4,
.base = 0x19000, .len = 0x204,
.features = CTL_SC7280_MASK,
.intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 13),
}, {
.name = "ctl_5", .id = CTL_5,
.base = 0x1a000, .len = 0x204,
.features = CTL_SC7280_MASK,
.intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 23),
},
};
static const struct dpu_sspp_cfg sa8775p_sspp[] = {
{
.name = "sspp_0", .id = SSPP_VIG0,
.base = 0x4000, .len = 0x32c,
.features = VIG_SDM845_MASK_SDMA,
.sblk = &dpu_vig_sblk_qseed3_3_1,
.xin_id = 0,
.type = SSPP_TYPE_VIG,
.clk_ctrl = DPU_CLK_CTRL_VIG0,
}, {
.name = "sspp_1", .id = SSPP_VIG1,
.base = 0x6000, .len = 0x32c,
.features = VIG_SDM845_MASK_SDMA,
.sblk = &dpu_vig_sblk_qseed3_3_1,
.xin_id = 4,
.type = SSPP_TYPE_VIG,
.clk_ctrl = DPU_CLK_CTRL_VIG1,
}, {
.name = "sspp_2", .id = SSPP_VIG2,
.base = 0x8000, .len = 0x32c,
.features = VIG_SDM845_MASK_SDMA,
.sblk = &dpu_vig_sblk_qseed3_3_1,
.xin_id = 8,
.type = SSPP_TYPE_VIG,
.clk_ctrl = DPU_CLK_CTRL_VIG2,
}, {
.name = "sspp_3", .id = SSPP_VIG3,
.base = 0xa000, .len = 0x32c,
.features = VIG_SDM845_MASK_SDMA,
.sblk = &dpu_vig_sblk_qseed3_3_1,
.xin_id = 12,
.type = SSPP_TYPE_VIG,
.clk_ctrl = DPU_CLK_CTRL_VIG3,
}, {
.name = "sspp_8", .id = SSPP_DMA0,
.base = 0x24000, .len = 0x32c,
.features = DMA_SDM845_MASK_SDMA,
.sblk = &dpu_dma_sblk,
.xin_id = 1,
.type = SSPP_TYPE_DMA,
.clk_ctrl = DPU_CLK_CTRL_DMA0,
}, {
.name = "sspp_9", .id = SSPP_DMA1,
.base = 0x26000, .len = 0x32c,
.features = DMA_SDM845_MASK_SDMA,
.sblk = &dpu_dma_sblk,
.xin_id = 5,
.type = SSPP_TYPE_DMA,
.clk_ctrl = DPU_CLK_CTRL_DMA1,
}, {
.name = "sspp_10", .id = SSPP_DMA2,
.base = 0x28000, .len = 0x32c,
.features = DMA_CURSOR_SDM845_MASK_SDMA,
.sblk = &dpu_dma_sblk,
.xin_id = 9,
.type = SSPP_TYPE_DMA,
.clk_ctrl = DPU_CLK_CTRL_DMA2,
}, {
.name = "sspp_11", .id = SSPP_DMA3,
.base = 0x2a000, .len = 0x32c,
.features = DMA_CURSOR_SDM845_MASK_SDMA,
.sblk = &dpu_dma_sblk,
.xin_id = 13,
.type = SSPP_TYPE_DMA,
.clk_ctrl = DPU_CLK_CTRL_DMA3,
},
};
static const struct dpu_lm_cfg sa8775p_lm[] = {
{
.name = "lm_0", .id = LM_0,
.base = 0x44000, .len = 0x400,
.features = MIXER_SDM845_MASK,
.sblk = &sdm845_lm_sblk,
.lm_pair = LM_1,
.pingpong = PINGPONG_0,
.dspp = DSPP_0,
}, {
.name = "lm_1", .id = LM_1,
.base = 0x45000, .len = 0x400,
.features = MIXER_SDM845_MASK,
.sblk = &sdm845_lm_sblk,
.lm_pair = LM_0,
.pingpong = PINGPONG_1,
.dspp = DSPP_1,
}, {
.name = "lm_2", .id = LM_2,
.base = 0x46000, .len = 0x400,
.features = MIXER_SDM845_MASK,
.sblk = &sdm845_lm_sblk,
.lm_pair = LM_3,
.pingpong = PINGPONG_2,
.dspp = DSPP_2,
}, {
.name = "lm_3", .id = LM_3,
.base = 0x47000, .len = 0x400,
.features = MIXER_SDM845_MASK,
.sblk = &sdm845_lm_sblk,
.lm_pair = LM_2,
.pingpong = PINGPONG_3,
.dspp = DSPP_3,
}, {
.name = "lm_4", .id = LM_4,
.base = 0x48000, .len = 0x400,
.features = MIXER_SDM845_MASK,
.sblk = &sdm845_lm_sblk,
.lm_pair = LM_5,
.pingpong = PINGPONG_4,
}, {
.name = "lm_5", .id = LM_5,
.base = 0x49000, .len = 0x400,
.features = MIXER_SDM845_MASK,
.sblk = &sdm845_lm_sblk,
.lm_pair = LM_4,
.pingpong = PINGPONG_5,
},
};
static const struct dpu_dspp_cfg sa8775p_dspp[] = {
{
.name = "dspp_0", .id = DSPP_0,
.base = 0x54000, .len = 0x1800,
.features = DSPP_SC7180_MASK,
.sblk = &sdm845_dspp_sblk,
}, {
.name = "dspp_1", .id = DSPP_1,
.base = 0x56000, .len = 0x1800,
.features = DSPP_SC7180_MASK,
.sblk = &sdm845_dspp_sblk,
}, {
.name = "dspp_2", .id = DSPP_2,
.base = 0x58000, .len = 0x1800,
.features = DSPP_SC7180_MASK,
.sblk = &sdm845_dspp_sblk,
}, {
.name = "dspp_3", .id = DSPP_3,
.base = 0x5a000, .len = 0x1800,
.features = DSPP_SC7180_MASK,
.sblk = &sdm845_dspp_sblk,
},
};
static const struct dpu_pingpong_cfg sa8775p_pp[] = {
{
.name = "pingpong_0", .id = PINGPONG_0,
.base = 0x69000, .len = 0,
.features = BIT(DPU_PINGPONG_DITHER),
.sblk = &sc7280_pp_sblk,
.merge_3d = MERGE_3D_0,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8),
}, {
.name = "pingpong_1", .id = PINGPONG_1,
.base = 0x6a000, .len = 0,
.features = BIT(DPU_PINGPONG_DITHER),
.sblk = &sc7280_pp_sblk,
.merge_3d = MERGE_3D_0,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 9),
}, {
.name = "pingpong_2", .id = PINGPONG_2,
.base = 0x6b000, .len = 0,
.features = BIT(DPU_PINGPONG_DITHER),
.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,
.features = BIT(DPU_PINGPONG_DITHER),
.sblk = &sc7280_pp_sblk,
.merge_3d = MERGE_3D_1,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 11),
}, {
.name = "pingpong_4", .id = PINGPONG_4,
.base = 0x6d000, .len = 0,
.features = BIT(DPU_PINGPONG_DITHER),
.sblk = &sc7280_pp_sblk,
.merge_3d = MERGE_3D_2,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 30),
}, {
.name = "pingpong_5", .id = PINGPONG_5,
.base = 0x6e000, .len = 0,
.features = BIT(DPU_PINGPONG_DITHER),
.sblk = &sc7280_pp_sblk,
.merge_3d = MERGE_3D_2,
.intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 31),
}, {
.name = "pingpong_6", .id = PINGPONG_6,
.base = 0x65800, .len = 0,
.features = BIT(DPU_PINGPONG_DITHER),
.sblk = &sc7280_pp_sblk,
.merge_3d = MERGE_3D_3,
}, {
.name = "pingpong_7", .id = PINGPONG_7,
.base = 0x65c00, .len = 0,
.features = BIT(DPU_PINGPONG_DITHER),
.sblk = &sc7280_pp_sblk,
.merge_3d = MERGE_3D_3,
},
};
static const struct dpu_merge_3d_cfg sa8775p_merge_3d[] = {
{
.name = "merge_3d_0", .id = MERGE_3D_0,
.base = 0x4e000, .len = 0x8,
}, {
.name = "merge_3d_1", .id = MERGE_3D_1,
.base = 0x4f000, .len = 0x8,
}, {
.name = "merge_3d_2", .id = MERGE_3D_2,
.base = 0x50000, .len = 0x8,
}, {
.name = "merge_3d_3", .id = MERGE_3D_3,
.base = 0x65f00, .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 sa8775p_dsc[] = {
{
.name = "dce_0_0", .id = DSC_0,
.base = 0x80000, .len = 0x4,
.features = BIT(DPU_DSC_HW_REV_1_2),
.sblk = &dsc_sblk_0,
}, {
.name = "dce_0_1", .id = DSC_1,
.base = 0x80000, .len = 0x4,
.features = BIT(DPU_DSC_HW_REV_1_2),
.sblk = &dsc_sblk_1,
}, {
.name = "dce_1_0", .id = DSC_2,
.base = 0x81000, .len = 0x4,
.features = BIT(DPU_DSC_HW_REV_1_2) | BIT(DPU_DSC_NATIVE_42x_EN),
.sblk = &dsc_sblk_0,
}, {
.name = "dce_1_1", .id = DSC_3,
.base = 0x81000, .len = 0x4,
.features = BIT(DPU_DSC_HW_REV_1_2) | BIT(DPU_DSC_NATIVE_42x_EN),
.sblk = &dsc_sblk_1,
}, {
.name = "dce_2_0", .id = DSC_4,
.base = 0x82000, .len = 0x4,
.features = BIT(DPU_DSC_HW_REV_1_2),
.sblk = &dsc_sblk_0,
}, {
.name = "dce_2_1", .id = DSC_5,
.base = 0x82000, .len = 0x4,
.features = BIT(DPU_DSC_HW_REV_1_2),
.sblk = &dsc_sblk_1,
},
};
static const struct dpu_wb_cfg sa8775p_wb[] = {
{
.name = "wb_2", .id = WB_2,
.base = 0x65000, .len = 0x2c8,
.features = WB_SM8250_MASK,
.format_list = wb2_formats_rgb_yuv,
.num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv),
.clk_ctrl = DPU_CLK_CTRL_WB2,
.xin_id = 6,
.vbif_idx = VBIF_RT,
.maxlinewidth = 4096,
.intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4),
},
};
/* TODO: INTF 3, 6, 7 and 8 are used for MST, marked as INTF_NONE for now */
static const struct dpu_intf_cfg sa8775p_intf[] = {
{
.name = "intf_0", .id = INTF_0,
.base = 0x34000, .len = 0x280,
.features = INTF_SC7280_MASK,
.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,
.features = INTF_SC7280_MASK,
.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_2", .id = INTF_2,
.base = 0x36000, .len = 0x300,
.features = INTF_SC7280_MASK,
.type = INTF_DSI,
.controller_id = MSM_DSI_CONTROLLER_1,
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 28),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 29),
.intr_tear_rd_ptr = DPU_IRQ_IDX(MDP_INTF2_TEAR_INTR, 2),
}, {
.name = "intf_3", .id = INTF_3,
.base = 0x37000, .len = 0x280,
.features = INTF_SC7280_MASK,
.type = INTF_NONE,
.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),
}, {
.name = "intf_4", .id = INTF_4,
.base = 0x38000, .len = 0x280,
.features = INTF_SC7280_MASK,
.type = INTF_DP,
.controller_id = MSM_DP_CONTROLLER_1,
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 20),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 21),
}, {
.name = "intf_6", .id = INTF_6,
.base = 0x3A000, .len = 0x280,
.features = INTF_SC7280_MASK,
.type = INTF_NONE,
.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, 17),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 16),
}, {
.name = "intf_7", .id = INTF_7,
.base = 0x3b000, .len = 0x280,
.features = INTF_SC7280_MASK,
.type = INTF_NONE,
.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, 18),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 19),
}, {
.name = "intf_8", .id = INTF_8,
.base = 0x3c000, .len = 0x280,
.features = INTF_SC7280_MASK,
.type = INTF_NONE,
.controller_id = MSM_DP_CONTROLLER_1, /* pair with intf_4 for DP MST */
.prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 13),
},
};
static const struct dpu_perf_cfg sa8775p_perf_data = {
.max_bw_low = 13600000,
.max_bw_high = 18200000,
.min_core_ib = 2500000,
.min_llcc_ib = 0,
.min_dram_ib = 800000,
.min_prefill_lines = 35,
/* FIXME: lut tables */
.danger_lut_tbl = {0x3ffff, 0x3ffff, 0x0},
.safe_lut_tbl = {0xfff0, 0xfff0, 0x1},
.qos_lut_tbl = {
{.nentry = ARRAY_SIZE(sm6350_qos_linear_macrotile),
.entries = sm6350_qos_linear_macrotile
},
{.nentry = ARRAY_SIZE(sm6350_qos_linear_macrotile),
.entries = sm6350_qos_linear_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 sa8775p_mdss_ver = {
.core_major_ver = 8,
.core_minor_ver = 4,
};
const struct dpu_mdss_cfg dpu_sa8775p_cfg = {
.mdss_ver = &sa8775p_mdss_ver,
.caps = &sa8775p_dpu_caps,
.mdp = &sa8775p_mdp,
.cdm = &sc7280_cdm,
.ctl_count = ARRAY_SIZE(sa8775p_ctl),
.ctl = sa8775p_ctl,
.sspp_count = ARRAY_SIZE(sa8775p_sspp),
.sspp = sa8775p_sspp,
.mixer_count = ARRAY_SIZE(sa8775p_lm),
.mixer = sa8775p_lm,
.dspp_count = ARRAY_SIZE(sa8775p_dspp),
.dspp = sa8775p_dspp,
.pingpong_count = ARRAY_SIZE(sa8775p_pp),
.pingpong = sa8775p_pp,
.dsc_count = ARRAY_SIZE(sa8775p_dsc),
.dsc = sa8775p_dsc,
.merge_3d_count = ARRAY_SIZE(sa8775p_merge_3d),
.merge_3d = sa8775p_merge_3d,
.wb_count = ARRAY_SIZE(sa8775p_wb),
.wb = sa8775p_wb,
.intf_count = ARRAY_SIZE(sa8775p_intf),
.intf = sa8775p_intf,
.vbif_count = ARRAY_SIZE(sdm845_vbif),
.vbif = sdm845_vbif,
.perf = &sa8775p_perf_data,
};
#endif

View File

@ -1230,6 +1230,19 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc,
return 0;
}
static enum drm_mode_status dpu_crtc_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{
struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc);
/*
* max crtc width is equal to the max mixer width * 2 and max height is 4K
*/
return drm_mode_validate_size(mode,
2 * dpu_kms->catalog->caps->max_mixer_width,
4096);
}
int dpu_crtc_vblank(struct drm_crtc *crtc, bool en)
{
struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc);
@ -1445,6 +1458,7 @@ static const struct drm_crtc_helper_funcs dpu_crtc_helper_funcs = {
.atomic_check = dpu_crtc_atomic_check,
.atomic_begin = dpu_crtc_atomic_begin,
.atomic_flush = dpu_crtc_atomic_flush,
.mode_valid = dpu_crtc_mode_valid,
.get_scanout_position = dpu_crtc_get_scanout_position,
};

View File

@ -166,10 +166,10 @@ static void dpu_encoder_phys_wb_set_qos(struct dpu_encoder_phys *phys_enc)
/**
* dpu_encoder_phys_wb_setup_fb - setup output framebuffer
* @phys_enc: Pointer to physical encoder
* @fb: Pointer to output framebuffer
* @format: Format of the framebuffer
*/
static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys *phys_enc,
struct drm_framebuffer *fb)
const struct msm_format *format)
{
struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
struct dpu_hw_wb *hw_wb;
@ -193,12 +193,12 @@ static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys *phys_enc,
hw_wb->ops.setup_roi(hw_wb, wb_cfg);
if (hw_wb->ops.setup_outformat)
hw_wb->ops.setup_outformat(hw_wb, wb_cfg);
hw_wb->ops.setup_outformat(hw_wb, wb_cfg, format);
if (hw_wb->ops.setup_cdp) {
const struct dpu_perf_cfg *perf = phys_enc->dpu_kms->catalog->perf;
hw_wb->ops.setup_cdp(hw_wb, wb_cfg->dest.format,
hw_wb->ops.setup_cdp(hw_wb, format,
perf->cdp_cfg[DPU_PERF_CDP_USAGE_NRT].wr_enable);
}
@ -321,15 +321,10 @@ static void dpu_encoder_phys_wb_setup(
{
struct dpu_hw_wb *hw_wb = phys_enc->hw_wb;
struct drm_display_mode mode = phys_enc->cached_mode;
struct drm_framebuffer *fb = NULL;
struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc);
struct drm_writeback_job *wb_job;
const struct msm_format *format;
const struct msm_format *dpu_fmt;
wb_job = wb_enc->wb_job;
format = msm_framebuffer_format(wb_enc->wb_job->fb);
dpu_fmt = mdp_get_format(&phys_enc->dpu_kms->base, format->pixel_format, wb_job->fb->modifier);
DPU_DEBUG("[mode_set:%d, \"%s\",%d,%d]\n",
hw_wb->idx - WB_0, mode.name,
@ -341,9 +336,9 @@ static void dpu_encoder_phys_wb_setup(
dpu_encoder_phys_wb_set_qos(phys_enc);
dpu_encoder_phys_wb_setup_fb(phys_enc, fb);
dpu_encoder_phys_wb_setup_fb(phys_enc, format);
dpu_encoder_helper_phys_setup_cdm(phys_enc, dpu_fmt, CDM_CDWN_OUTPUT_WB);
dpu_encoder_helper_phys_setup_cdm(phys_enc, format, CDM_CDWN_OUTPUT_WB);
dpu_encoder_phys_wb_setup_ctl(phys_enc);
}
@ -587,26 +582,20 @@ static void dpu_encoder_phys_wb_prepare_wb_job(struct dpu_encoder_phys *phys_enc
format = msm_framebuffer_format(job->fb);
wb_cfg->dest.format = mdp_get_format(&phys_enc->dpu_kms->base,
format->pixel_format, job->fb->modifier);
if (!wb_cfg->dest.format) {
/* this error should be detected during atomic_check */
DPU_ERROR("failed to get format %p4cc\n", &format->pixel_format);
ret = dpu_format_populate_plane_sizes(job->fb, &wb_cfg->dest);
if (ret) {
DPU_DEBUG("failed to populate plane sizes%d\n", ret);
return;
}
ret = dpu_format_populate_layout(aspace, job->fb, &wb_cfg->dest);
if (ret) {
DPU_DEBUG("failed to populate layout %d\n", ret);
return;
}
dpu_format_populate_addrs(aspace, job->fb, &wb_cfg->dest);
wb_cfg->dest.width = job->fb->width;
wb_cfg->dest.height = job->fb->height;
wb_cfg->dest.num_planes = wb_cfg->dest.format->num_planes;
wb_cfg->dest.num_planes = format->num_planes;
if ((wb_cfg->dest.format->fetch_type == MDP_PLANE_PLANAR) &&
(wb_cfg->dest.format->element[0] == C1_B_Cb))
if ((format->fetch_type == MDP_PLANE_PLANAR) &&
(format->element[0] == C1_B_Cb))
swap(wb_cfg->dest.plane_addr[1], wb_cfg->dest.plane_addr[2]);
DPU_DEBUG("[fb_offset:%8.8x,%8.8x,%8.8x,%8.8x]\n",

View File

@ -13,9 +13,6 @@
#define DPU_UBWC_PLANE_SIZE_ALIGNMENT 4096
#define DPU_MAX_IMG_WIDTH 0x3FFF
#define DPU_MAX_IMG_HEIGHT 0x3FFF
/*
* struct dpu_media_color_map - maps drm format to media format
* @format: DRM base pixel format
@ -93,10 +90,9 @@ static int _dpu_format_get_media_color_ubwc(const struct msm_format *fmt)
return color_fmt;
}
static int _dpu_format_get_plane_sizes_ubwc(
static int _dpu_format_populate_plane_sizes_ubwc(
const struct msm_format *fmt,
const uint32_t width,
const uint32_t height,
struct drm_framebuffer *fb,
struct dpu_hw_fmt_layout *layout)
{
int i;
@ -104,9 +100,8 @@ static int _dpu_format_get_plane_sizes_ubwc(
bool meta = MSM_FORMAT_IS_UBWC(fmt);
memset(layout, 0, sizeof(struct dpu_hw_fmt_layout));
layout->format = fmt;
layout->width = width;
layout->height = height;
layout->width = fb->width;
layout->height = fb->height;
layout->num_planes = fmt->num_planes;
color = _dpu_format_get_media_color_ubwc(fmt);
@ -116,19 +111,19 @@ static int _dpu_format_get_plane_sizes_ubwc(
return -EINVAL;
}
if (MSM_FORMAT_IS_YUV(layout->format)) {
if (MSM_FORMAT_IS_YUV(fmt)) {
uint32_t y_sclines, uv_sclines;
uint32_t y_meta_scanlines = 0;
uint32_t uv_meta_scanlines = 0;
layout->num_planes = 2;
layout->plane_pitch[0] = VENUS_Y_STRIDE(color, width);
y_sclines = VENUS_Y_SCANLINES(color, height);
layout->plane_pitch[0] = VENUS_Y_STRIDE(color, fb->width);
y_sclines = VENUS_Y_SCANLINES(color, fb->height);
layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] *
y_sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT);
layout->plane_pitch[1] = VENUS_UV_STRIDE(color, width);
uv_sclines = VENUS_UV_SCANLINES(color, height);
layout->plane_pitch[1] = VENUS_UV_STRIDE(color, fb->width);
uv_sclines = VENUS_UV_SCANLINES(color, fb->height);
layout->plane_size[1] = MSM_MEDIA_ALIGN(layout->plane_pitch[1] *
uv_sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT);
@ -136,13 +131,13 @@ static int _dpu_format_get_plane_sizes_ubwc(
goto done;
layout->num_planes += 2;
layout->plane_pitch[2] = VENUS_Y_META_STRIDE(color, width);
y_meta_scanlines = VENUS_Y_META_SCANLINES(color, height);
layout->plane_pitch[2] = VENUS_Y_META_STRIDE(color, fb->width);
y_meta_scanlines = VENUS_Y_META_SCANLINES(color, fb->height);
layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] *
y_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT);
layout->plane_pitch[3] = VENUS_UV_META_STRIDE(color, width);
uv_meta_scanlines = VENUS_UV_META_SCANLINES(color, height);
layout->plane_pitch[3] = VENUS_UV_META_STRIDE(color, fb->width);
uv_meta_scanlines = VENUS_UV_META_SCANLINES(color, fb->height);
layout->plane_size[3] = MSM_MEDIA_ALIGN(layout->plane_pitch[3] *
uv_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT);
@ -151,16 +146,16 @@ static int _dpu_format_get_plane_sizes_ubwc(
layout->num_planes = 1;
layout->plane_pitch[0] = VENUS_RGB_STRIDE(color, width);
rgb_scanlines = VENUS_RGB_SCANLINES(color, height);
layout->plane_pitch[0] = VENUS_RGB_STRIDE(color, fb->width);
rgb_scanlines = VENUS_RGB_SCANLINES(color, fb->height);
layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] *
rgb_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT);
if (!meta)
goto done;
layout->num_planes += 2;
layout->plane_pitch[2] = VENUS_RGB_META_STRIDE(color, width);
rgb_meta_scanlines = VENUS_RGB_META_SCANLINES(color, height);
layout->plane_pitch[2] = VENUS_RGB_META_STRIDE(color, fb->width);
rgb_meta_scanlines = VENUS_RGB_META_SCANLINES(color, fb->height);
layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] *
rgb_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT);
}
@ -172,26 +167,23 @@ static int _dpu_format_get_plane_sizes_ubwc(
return 0;
}
static int _dpu_format_get_plane_sizes_linear(
static int _dpu_format_populate_plane_sizes_linear(
const struct msm_format *fmt,
const uint32_t width,
const uint32_t height,
struct dpu_hw_fmt_layout *layout,
const uint32_t *pitches)
struct drm_framebuffer *fb,
struct dpu_hw_fmt_layout *layout)
{
int i;
memset(layout, 0, sizeof(struct dpu_hw_fmt_layout));
layout->format = fmt;
layout->width = width;
layout->height = height;
layout->width = fb->width;
layout->height = fb->height;
layout->num_planes = fmt->num_planes;
/* Due to memset above, only need to set planes of interest */
if (fmt->fetch_type == MDP_PLANE_INTERLEAVED) {
layout->num_planes = 1;
layout->plane_size[0] = width * height * layout->format->bpp;
layout->plane_pitch[0] = width * layout->format->bpp;
layout->plane_size[0] = fb->width * fb->height * fmt->bpp;
layout->plane_pitch[0] = fb->width * fmt->bpp;
} else {
uint32_t v_subsample, h_subsample;
uint32_t chroma_samp;
@ -201,7 +193,7 @@ static int _dpu_format_get_plane_sizes_linear(
_dpu_get_v_h_subsample_rate(chroma_samp, &v_subsample,
&h_subsample);
if (width % h_subsample || height % v_subsample) {
if (fb->width % h_subsample || fb->height % v_subsample) {
DRM_ERROR("mismatch in subsample vs dimensions\n");
return -EINVAL;
}
@ -209,11 +201,11 @@ static int _dpu_format_get_plane_sizes_linear(
if ((fmt->pixel_format == DRM_FORMAT_NV12) &&
(MSM_FORMAT_IS_DX(fmt)))
bpp = 2;
layout->plane_pitch[0] = width * bpp;
layout->plane_pitch[0] = fb->width * bpp;
layout->plane_pitch[1] = layout->plane_pitch[0] / h_subsample;
layout->plane_size[0] = layout->plane_pitch[0] * height;
layout->plane_size[0] = layout->plane_pitch[0] * fb->height;
layout->plane_size[1] = layout->plane_pitch[1] *
(height / v_subsample);
(fb->height / v_subsample);
if (fmt->fetch_type == MDP_PLANE_PSEUDO_PLANAR) {
layout->num_planes = 2;
@ -234,8 +226,13 @@ static int _dpu_format_get_plane_sizes_linear(
* all the components based on ubwc specifications.
*/
for (i = 0; i < layout->num_planes && i < DPU_MAX_PLANES; ++i) {
if (pitches && layout->plane_pitch[i] < pitches[i])
layout->plane_pitch[i] = pitches[i];
if (layout->plane_pitch[i] <= fb->pitches[i]) {
layout->plane_pitch[i] = fb->pitches[i];
} else {
DRM_DEBUG("plane %u expected pitch %u, fb %u\n",
i, layout->plane_pitch[i], fb->pitches[i]);
return -EINVAL;
}
}
for (i = 0; i < DPU_MAX_PLANES; i++)
@ -244,53 +241,54 @@ static int _dpu_format_get_plane_sizes_linear(
return 0;
}
static int dpu_format_get_plane_sizes(
const struct msm_format *fmt,
const uint32_t w,
const uint32_t h,
struct dpu_hw_fmt_layout *layout,
const uint32_t *pitches)
/*
* dpu_format_populate_addrs - populate non-address part of the layout based on
* fb, and format found in the fb
* @fb: framebuffer pointer
* @layout: format layout structure to populate
*
* Return: error code on failure or 0 if new addresses were populated
*/
int dpu_format_populate_plane_sizes(
struct drm_framebuffer *fb,
struct dpu_hw_fmt_layout *layout)
{
if (!layout || !fmt) {
const struct msm_format *fmt;
if (!layout || !fb) {
DRM_ERROR("invalid pointer\n");
return -EINVAL;
}
if ((w > DPU_MAX_IMG_WIDTH) || (h > DPU_MAX_IMG_HEIGHT)) {
if (fb->width > DPU_MAX_IMG_WIDTH ||
fb->height > DPU_MAX_IMG_HEIGHT) {
DRM_ERROR("image dimensions outside max range\n");
return -ERANGE;
}
if (MSM_FORMAT_IS_UBWC(fmt) || MSM_FORMAT_IS_TILE(fmt))
return _dpu_format_get_plane_sizes_ubwc(fmt, w, h, layout);
fmt = msm_framebuffer_format(fb);
return _dpu_format_get_plane_sizes_linear(fmt, w, h, layout, pitches);
if (MSM_FORMAT_IS_UBWC(fmt) || MSM_FORMAT_IS_TILE(fmt))
return _dpu_format_populate_plane_sizes_ubwc(fmt, fb, layout);
return _dpu_format_populate_plane_sizes_linear(fmt, fb, layout);
}
static int _dpu_format_populate_addrs_ubwc(
struct msm_gem_address_space *aspace,
struct drm_framebuffer *fb,
struct dpu_hw_fmt_layout *layout)
static void _dpu_format_populate_addrs_ubwc(struct msm_gem_address_space *aspace,
struct drm_framebuffer *fb,
struct dpu_hw_fmt_layout *layout)
{
const struct msm_format *fmt;
uint32_t base_addr = 0;
bool meta;
if (!fb || !layout) {
DRM_ERROR("invalid pointers\n");
return -EINVAL;
}
base_addr = msm_framebuffer_iova(fb, aspace, 0);
if (aspace)
base_addr = msm_framebuffer_iova(fb, aspace, 0);
if (!base_addr) {
DRM_ERROR("failed to retrieve base addr\n");
return -EFAULT;
}
meta = MSM_FORMAT_IS_UBWC(layout->format);
fmt = msm_framebuffer_format(fb);
meta = MSM_FORMAT_IS_UBWC(fmt);
/* Per-format logic for verifying active planes */
if (MSM_FORMAT_IS_YUV(layout->format)) {
if (MSM_FORMAT_IS_YUV(fmt)) {
/************************************************/
/* UBWC ** */
/* buffer ** DPU PLANE */
@ -319,7 +317,7 @@ static int _dpu_format_populate_addrs_ubwc(
+ layout->plane_size[2] + layout->plane_size[3];
if (!meta)
return 0;
return;
/* configure Y metadata plane */
layout->plane_addr[2] = base_addr;
@ -350,119 +348,36 @@ static int _dpu_format_populate_addrs_ubwc(
layout->plane_addr[1] = 0;
if (!meta)
return 0;
return;
layout->plane_addr[2] = base_addr;
layout->plane_addr[3] = 0;
}
return 0;
}
static int _dpu_format_populate_addrs_linear(
struct msm_gem_address_space *aspace,
struct drm_framebuffer *fb,
struct dpu_hw_fmt_layout *layout)
static void _dpu_format_populate_addrs_linear(struct msm_gem_address_space *aspace,
struct drm_framebuffer *fb,
struct dpu_hw_fmt_layout *layout)
{
unsigned int i;
/* Can now check the pitches given vs pitches expected */
for (i = 0; i < layout->num_planes; ++i) {
if (layout->plane_pitch[i] > fb->pitches[i]) {
DRM_ERROR("plane %u expected pitch %u, fb %u\n",
i, layout->plane_pitch[i], fb->pitches[i]);
return -EINVAL;
}
}
/* Populate addresses for simple formats here */
for (i = 0; i < layout->num_planes; ++i) {
if (aspace)
layout->plane_addr[i] =
msm_framebuffer_iova(fb, aspace, i);
if (!layout->plane_addr[i]) {
DRM_ERROR("failed to retrieve base addr\n");
return -EFAULT;
}
}
return 0;
for (i = 0; i < layout->num_planes; ++i)
layout->plane_addr[i] = msm_framebuffer_iova(fb, aspace, i);
}
int dpu_format_populate_layout(
struct msm_gem_address_space *aspace,
struct drm_framebuffer *fb,
struct dpu_hw_fmt_layout *layout)
void dpu_format_populate_addrs(struct msm_gem_address_space *aspace,
struct drm_framebuffer *fb,
struct dpu_hw_fmt_layout *layout)
{
int ret;
const struct msm_format *fmt;
if (!fb || !layout) {
DRM_ERROR("invalid arguments\n");
return -EINVAL;
}
if ((fb->width > DPU_MAX_IMG_WIDTH) ||
(fb->height > DPU_MAX_IMG_HEIGHT)) {
DRM_ERROR("image dimensions outside max range\n");
return -ERANGE;
}
layout->format = msm_framebuffer_format(fb);
/* Populate the plane sizes etc via get_format */
ret = dpu_format_get_plane_sizes(layout->format, fb->width, fb->height,
layout, fb->pitches);
if (ret)
return ret;
fmt = msm_framebuffer_format(fb);
/* Populate the addresses given the fb */
if (MSM_FORMAT_IS_UBWC(layout->format) ||
MSM_FORMAT_IS_TILE(layout->format))
ret = _dpu_format_populate_addrs_ubwc(aspace, fb, layout);
if (MSM_FORMAT_IS_UBWC(fmt) ||
MSM_FORMAT_IS_TILE(fmt))
_dpu_format_populate_addrs_ubwc(aspace, fb, layout);
else
ret = _dpu_format_populate_addrs_linear(aspace, fb, layout);
return ret;
}
int dpu_format_check_modified_format(
const struct msm_kms *kms,
const struct msm_format *fmt,
const struct drm_mode_fb_cmd2 *cmd,
struct drm_gem_object **bos)
{
const struct drm_format_info *info;
struct dpu_hw_fmt_layout layout;
uint32_t bos_total_size = 0;
int ret, i;
if (!fmt || !cmd || !bos) {
DRM_ERROR("invalid arguments\n");
return -EINVAL;
}
info = drm_format_info(fmt->pixel_format);
if (!info)
return -EINVAL;
ret = dpu_format_get_plane_sizes(fmt, cmd->width, cmd->height,
&layout, cmd->pitches);
if (ret)
return ret;
for (i = 0; i < info->num_planes; i++) {
if (!bos[i]) {
DRM_ERROR("invalid handle for plane %d\n", i);
return -EINVAL;
}
if ((i == 0) || (bos[i] != bos[0]))
bos_total_size += bos[i]->size;
}
if (bos_total_size < layout.total_size) {
DRM_ERROR("buffers total size too small %u expected %u\n",
bos_total_size, layout.total_size);
return -EINVAL;
}
return 0;
_dpu_format_populate_addrs_linear(aspace, fb, layout);
}

View File

@ -32,34 +32,18 @@ static inline bool dpu_find_format(u32 format, const u32 *supported_formats,
}
/**
* dpu_format_check_modified_format - validate format and buffers for
* dpu non-standard, i.e. modified format
* @kms: kms driver
* @msm_fmt: pointer to the msm_fmt base pointer of an msm_format
* @cmd: fb_cmd2 structure user request
* @bos: gem buffer object list
*
* Return: error code on failure, 0 on success
*/
int dpu_format_check_modified_format(
const struct msm_kms *kms,
const struct msm_format *msm_fmt,
const struct drm_mode_fb_cmd2 *cmd,
struct drm_gem_object **bos);
/**
* dpu_format_populate_layout - populate the given format layout based on
* dpu_format_populate_addrs - populate buffer addresses based on
* mmu, fb, and format found in the fb
* @aspace: address space pointer
* @fb: framebuffer pointer
* @fmtl: format layout structure to populate
*
* Return: error code on failure, -EAGAIN if success but the addresses
* are the same as before or 0 if new addresses were populated
*/
int dpu_format_populate_layout(
struct msm_gem_address_space *aspace,
void dpu_format_populate_addrs(struct msm_gem_address_space *aspace,
struct drm_framebuffer *fb,
struct dpu_hw_fmt_layout *layout);
int dpu_format_populate_plane_sizes(
struct drm_framebuffer *fb,
struct dpu_hw_fmt_layout *fmtl);
struct dpu_hw_fmt_layout *layout);
#endif /*_DPU_FORMATS_H */

View File

@ -21,6 +21,16 @@
(VIG_BASE_MASK | \
BIT(DPU_SSPP_CSC_10BIT))
#define VIG_MSM8953_MASK \
(BIT(DPU_SSPP_QOS) |\
BIT(DPU_SSPP_SCALER_QSEED2) |\
BIT(DPU_SSPP_CSC))
#define VIG_MSM8996_MASK \
(BIT(DPU_SSPP_QOS) | BIT(DPU_SSPP_CDP) |\
BIT(DPU_SSPP_TS_PREFILL) | BIT(DPU_SSPP_SCALER_QSEED2) |\
BIT(DPU_SSPP_CSC))
#define VIG_MSM8998_MASK \
(VIG_MASK | BIT(DPU_SSPP_SCALER_QSEED3_COMPATIBLE))
@ -32,6 +42,12 @@
#define VIG_QCM2290_MASK (VIG_BASE_MASK | BIT(DPU_SSPP_QOS_8LVL))
#define DMA_MSM8953_MASK \
(BIT(DPU_SSPP_QOS))
#define DMA_MSM8996_MASK \
(BIT(DPU_SSPP_QOS) | BIT(DPU_SSPP_TS_PREFILL) | BIT(DPU_SSPP_CDP))
#define DMA_MSM8998_MASK \
(BIT(DPU_SSPP_QOS) |\
BIT(DPU_SSPP_TS_PREFILL) | BIT(DPU_SSPP_TS_PREFILL_REC1) |\
@ -57,9 +73,19 @@
#define DMA_CURSOR_SDM845_MASK_SDMA \
(DMA_CURSOR_SDM845_MASK | BIT(DPU_SSPP_SMART_DMA_V2))
#define DMA_CURSOR_MSM8996_MASK \
(DMA_MSM8996_MASK | BIT(DPU_SSPP_CURSOR))
#define DMA_CURSOR_MSM8998_MASK \
(DMA_MSM8998_MASK | BIT(DPU_SSPP_CURSOR))
#define RGB_MSM8953_MASK \
(BIT(DPU_SSPP_QOS))
#define RGB_MSM8996_MASK \
(BIT(DPU_SSPP_QOS) | BIT(DPU_SSPP_CDP) |\
BIT(DPU_SSPP_TS_PREFILL) | BIT(DPU_SSPP_SCALER_RGB))
#define MIXER_MSM8998_MASK \
(BIT(DPU_MIXER_SOURCESPLIT))
@ -69,6 +95,12 @@
#define MIXER_QCM2290_MASK \
(BIT(DPU_DIM_LAYER) | BIT(DPU_MIXER_COMBINED_ALPHA))
#define PINGPONG_MSM8996_MASK \
(BIT(DPU_PINGPONG_DSC))
#define PINGPONG_MSM8996_TE2_MASK \
(PINGPONG_MSM8996_MASK | BIT(DPU_PINGPONG_TE2))
#define PINGPONG_SDM845_MASK \
(BIT(DPU_PINGPONG_DITHER) | BIT(DPU_PINGPONG_DSC))
@ -316,6 +348,35 @@ static const u32 wb2_formats_rgb_yuv[] = {
.virt_num_formats = ARRAY_SIZE(plane_formats), \
}
/* qseed2 is not supported, so disabled scaling */
#define _VIG_SBLK_QSEED2() \
{ \
.maxdwnscale = SSPP_UNITY_SCALE, \
.maxupscale = SSPP_UNITY_SCALE, \
.scaler_blk = {.name = "scaler", \
/* no version for qseed2 */ \
.base = 0x200, .len = 0xa0,}, \
.csc_blk = {.name = "csc", \
.base = 0x320, .len = 0x100,}, \
.format_list = plane_formats_yuv, \
.num_formats = ARRAY_SIZE(plane_formats_yuv), \
.virt_format_list = plane_formats, \
.virt_num_formats = ARRAY_SIZE(plane_formats), \
.rotation_cfg = NULL, \
}
#define _RGB_SBLK() \
{ \
.maxdwnscale = SSPP_UNITY_SCALE, \
.maxupscale = SSPP_UNITY_SCALE, \
.scaler_blk = {.name = "scaler", \
.base = 0x200, .len = 0x28,}, \
.format_list = plane_formats, \
.num_formats = ARRAY_SIZE(plane_formats), \
.virt_format_list = plane_formats, \
.virt_num_formats = ARRAY_SIZE(plane_formats), \
}
#define _DMA_SBLK() \
{ \
.maxdwnscale = SSPP_UNITY_SCALE, \
@ -332,6 +393,9 @@ static const struct dpu_rotation_cfg dpu_rot_sc7280_cfg_v2 = {
.rot_format_list = rotation_v2_formats,
};
static const struct dpu_sspp_sub_blks dpu_vig_sblk_qseed2 =
_VIG_SBLK_QSEED2();
static const struct dpu_sspp_sub_blks dpu_vig_sblk_noscale =
_VIG_SBLK_NOSCALE();
@ -363,6 +427,8 @@ static const struct dpu_sspp_sub_blks dpu_vig_sblk_qseed3_3_2 =
static const struct dpu_sspp_sub_blks dpu_vig_sblk_qseed3_3_3 =
_VIG_SBLK(SSPP_SCALER_VER(3, 3));
static const struct dpu_sspp_sub_blks dpu_rgb_sblk = _RGB_SBLK();
static const struct dpu_sspp_sub_blks dpu_dma_sblk = _DMA_SBLK();
/*************************************************************
@ -427,6 +493,15 @@ static const struct dpu_dspp_sub_blks sdm845_dspp_sblk = {
/*************************************************************
* PINGPONG sub blocks config
*************************************************************/
static const struct dpu_pingpong_sub_blks msm8996_pp_sblk_te = {
.te2 = {.name = "te2", .base = 0x2000, .len = 0x0,
.version = 0x1},
};
static const struct dpu_pingpong_sub_blks msm8996_pp_sblk = {
/* No dither block */
};
static const struct dpu_pingpong_sub_blks sdm845_pp_sblk_te = {
.te2 = {.name = "te2", .base = 0x2000, .len = 0x0,
.version = 0x1},
@ -492,6 +567,34 @@ static const struct dpu_vbif_dynamic_ot_cfg msm8998_ot_rdwr_cfg[] = {
},
};
static const struct dpu_vbif_cfg msm8996_vbif[] = {
{
.name = "vbif_rt", .id = VBIF_RT,
.base = 0, .len = 0x1040,
.default_ot_rd_limit = 32,
.default_ot_wr_limit = 16,
.features = BIT(DPU_VBIF_QOS_REMAP) | BIT(DPU_VBIF_QOS_OTLIM),
.xin_halt_timeout = 0x4000,
.qos_rp_remap_size = 0x20,
.dynamic_ot_rd_tbl = {
.count = ARRAY_SIZE(msm8998_ot_rdwr_cfg),
.cfg = msm8998_ot_rdwr_cfg,
},
.dynamic_ot_wr_tbl = {
.count = ARRAY_SIZE(msm8998_ot_rdwr_cfg),
.cfg = msm8998_ot_rdwr_cfg,
},
.qos_rt_tbl = {
.npriority_lvl = ARRAY_SIZE(msm8998_rt_pri_lvl),
.priority_lvl = msm8998_rt_pri_lvl,
},
.qos_nrt_tbl = {
.npriority_lvl = ARRAY_SIZE(msm8998_nrt_pri_lvl),
.priority_lvl = msm8998_nrt_pri_lvl,
},
},
};
static const struct dpu_vbif_cfg msm8998_vbif[] = {
{
.name = "vbif_rt", .id = VBIF_RT,
@ -675,6 +778,11 @@ static const struct dpu_qos_lut_entry sc7180_qos_nrt[] = {
* Hardware catalog
*************************************************************/
#include "catalog/dpu_1_7_msm8996.h"
#include "catalog/dpu_1_14_msm8937.h"
#include "catalog/dpu_1_15_msm8917.h"
#include "catalog/dpu_1_16_msm8953.h"
#include "catalog/dpu_3_0_msm8998.h"
#include "catalog/dpu_3_2_sdm660.h"
#include "catalog/dpu_3_3_sdm630.h"
@ -699,6 +807,7 @@ static const struct dpu_qos_lut_entry sc7180_qos_nrt[] = {
#include "catalog/dpu_8_0_sc8280xp.h"
#include "catalog/dpu_8_1_sm8450.h"
#include "catalog/dpu_8_4_sa8775p.h"
#include "catalog/dpu_9_0_sm8550.h"

View File

@ -21,8 +21,8 @@
#define DPU_HW_BLK_NAME_LEN 16
#define MAX_IMG_WIDTH 0x3fff
#define MAX_IMG_HEIGHT 0x3fff
#define DPU_MAX_IMG_WIDTH 0x3fff
#define DPU_MAX_IMG_HEIGHT 0x3fff
#define CRTC_DUAL_MIXERS 2
@ -831,6 +831,10 @@ struct dpu_mdss_cfg {
const struct dpu_format_extended *vig_formats;
};
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;
extern const struct dpu_mdss_cfg dpu_msm8996_cfg;
extern const struct dpu_mdss_cfg dpu_msm8998_cfg;
extern const struct dpu_mdss_cfg dpu_sdm630_cfg;
extern const struct dpu_mdss_cfg dpu_sdm660_cfg;
@ -850,6 +854,7 @@ extern const struct dpu_mdss_cfg dpu_sm8350_cfg;
extern const struct dpu_mdss_cfg dpu_sc7280_cfg;
extern const struct dpu_mdss_cfg dpu_sc8280xp_cfg;
extern const struct dpu_mdss_cfg dpu_sm8450_cfg;
extern const struct dpu_mdss_cfg dpu_sa8775p_cfg;
extern const struct dpu_mdss_cfg dpu_sm8550_cfg;
extern const struct dpu_mdss_cfg dpu_sm8650_cfg;
extern const struct dpu_mdss_cfg dpu_x1e80100_cfg;

View File

@ -293,7 +293,6 @@ enum dpu_3d_blend_mode {
/**
* struct dpu_hw_fmt_layout - format information of the source pixel data
* @format: pixel format parameters
* @num_planes: number of planes (including meta data planes)
* @width: image width
* @height: image height
@ -303,7 +302,6 @@ enum dpu_3d_blend_mode {
* @plane_pitch: pitch of each plane
*/
struct dpu_hw_fmt_layout {
const struct msm_format *format;
uint32_t num_planes;
uint32_t width;
uint32_t height;

View File

@ -12,6 +12,8 @@
struct dpu_hw_sspp;
#define DPU_SSPP_MAX_PITCH_SIZE 0xffff
/**
* Flags
*/

View File

@ -64,10 +64,10 @@ static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx,
}
static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx,
struct dpu_hw_wb_cfg *data)
struct dpu_hw_wb_cfg *data,
const struct msm_format *fmt)
{
struct dpu_hw_blk_reg_map *c = &ctx->hw;
const struct msm_format *fmt = data->dest.format;
u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
u32 write_config = 0;
u32 opmode = 0;

View File

@ -37,7 +37,8 @@ struct dpu_hw_wb_ops {
struct dpu_hw_wb_cfg *wb);
void (*setup_outformat)(struct dpu_hw_wb *ctx,
struct dpu_hw_wb_cfg *wb);
struct dpu_hw_wb_cfg *wb,
const struct msm_format *fmt);
void (*setup_roi)(struct dpu_hw_wb *ctx,
struct dpu_hw_wb_cfg *wb);

View File

@ -1025,7 +1025,6 @@ static const struct msm_kms_funcs kms_funcs = {
.complete_commit = dpu_kms_complete_commit,
.enable_vblank = dpu_kms_enable_vblank,
.disable_vblank = dpu_kms_disable_vblank,
.check_modified_format = dpu_format_check_modified_format,
.destroy = dpu_kms_destroy,
.snapshot = dpu_kms_mdp_snapshot,
#ifdef CONFIG_DEBUG_FS
@ -1202,13 +1201,8 @@ static int dpu_kms_hw_init(struct msm_kms *kms)
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
/*
* max crtc width is equal to the max mixer width * 2 and max height is
* is 4K
*/
dev->mode_config.max_width =
dpu_kms->catalog->caps->max_mixer_width * 2;
dev->mode_config.max_height = 4096;
dev->mode_config.max_width = DPU_MAX_IMG_WIDTH;
dev->mode_config.max_height = DPU_MAX_IMG_HEIGHT;
dev->max_vblank_count = 0xffffffff;
/* Disable vblank irqs aggressively for power-saving */
@ -1445,8 +1439,13 @@ static const struct dev_pm_ops dpu_pm_ops = {
};
static const struct of_device_id dpu_dt_match[] = {
{ .compatible = "qcom,msm8917-mdp5", .data = &dpu_msm8917_cfg, },
{ .compatible = "qcom,msm8937-mdp5", .data = &dpu_msm8937_cfg, },
{ .compatible = "qcom,msm8953-mdp5", .data = &dpu_msm8953_cfg, },
{ .compatible = "qcom,msm8996-mdp5", .data = &dpu_msm8996_cfg, },
{ .compatible = "qcom,msm8998-dpu", .data = &dpu_msm8998_cfg, },
{ .compatible = "qcom,qcm2290-dpu", .data = &dpu_qcm2290_cfg, },
{ .compatible = "qcom,sa8775p-dpu", .data = &dpu_sa8775p_cfg, },
{ .compatible = "qcom,sdm630-mdp5", .data = &dpu_sdm630_cfg, },
{ .compatible = "qcom,sdm660-mdp5", .data = &dpu_sdm660_cfg, },
{ .compatible = "qcom,sdm670-dpu", .data = &dpu_sdm670_cfg, },

View File

@ -648,7 +648,6 @@ static int dpu_plane_prepare_fb(struct drm_plane *plane,
struct drm_framebuffer *fb = new_state->fb;
struct dpu_plane *pdpu = to_dpu_plane(plane);
struct dpu_plane_state *pstate = to_dpu_plane_state(new_state);
struct dpu_hw_fmt_layout layout;
struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base);
int ret;
@ -676,17 +675,6 @@ static int dpu_plane_prepare_fb(struct drm_plane *plane,
}
}
/* validate framebuffer layout before commit */
ret = dpu_format_populate_layout(pstate->aspace,
new_state->fb, &layout);
if (ret) {
DPU_ERROR_PLANE(pdpu, "failed to get format layout, %d\n", ret);
if (pstate->aspace)
msm_framebuffer_cleanup(new_state->fb, pstate->aspace,
pstate->needs_dirtyfb);
return ret;
}
return 0;
}
@ -794,7 +782,7 @@ static int dpu_plane_atomic_check(struct drm_plane *plane,
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
int ret = 0, min_scale;
int i, ret = 0, min_scale;
struct dpu_plane *pdpu = to_dpu_plane(plane);
struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base);
u64 max_mdp_clk_rate = kms->perf.max_core_clk_rate;
@ -855,13 +843,23 @@ static int dpu_plane_atomic_check(struct drm_plane *plane,
fb_rect.y2 = new_plane_state->fb->height;
/* Ensure fb size is supported */
if (drm_rect_width(&fb_rect) > MAX_IMG_WIDTH ||
drm_rect_height(&fb_rect) > MAX_IMG_HEIGHT) {
if (drm_rect_width(&fb_rect) > DPU_MAX_IMG_WIDTH ||
drm_rect_height(&fb_rect) > DPU_MAX_IMG_HEIGHT) {
DPU_DEBUG_PLANE(pdpu, "invalid framebuffer " DRM_RECT_FMT "\n",
DRM_RECT_ARG(&fb_rect));
return -E2BIG;
}
ret = dpu_format_populate_plane_sizes(new_plane_state->fb, &pstate->layout);
if (ret) {
DPU_ERROR_PLANE(pdpu, "failed to get format plane sizes, %d\n", ret);
return ret;
}
for (i = 0; i < pstate->layout.num_planes; i++)
if (pstate->layout.plane_pitch[i] > DPU_SSPP_MAX_PITCH_SIZE)
return -E2BIG;
fmt = msm_framebuffer_format(new_plane_state->fb);
max_linewidth = pdpu->catalog->caps->max_linewidth;
@ -1101,7 +1099,8 @@ static void dpu_plane_sspp_update_pipe(struct drm_plane *plane,
_dpu_plane_set_qos_remap(plane, pipe);
}
static void dpu_plane_sspp_atomic_update(struct drm_plane *plane)
static void dpu_plane_sspp_atomic_update(struct drm_plane *plane,
struct drm_plane_state *new_state)
{
struct dpu_plane *pdpu = to_dpu_plane(plane);
struct drm_plane_state *state = plane->state;
@ -1115,17 +1114,6 @@ static void dpu_plane_sspp_atomic_update(struct drm_plane *plane)
msm_framebuffer_format(fb);
struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg;
struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->r_pipe_cfg;
struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base);
struct msm_gem_address_space *aspace = kms->base.aspace;
struct dpu_hw_fmt_layout layout;
bool layout_valid = false;
int ret;
ret = dpu_format_populate_layout(aspace, fb, &layout);
if (ret)
DPU_ERROR_PLANE(pdpu, "failed to get format layout, %d\n", ret);
else
layout_valid = true;
pstate->pending = true;
@ -1133,6 +1121,8 @@ static void dpu_plane_sspp_atomic_update(struct drm_plane *plane)
pstate->needs_qos_remap |= (is_rt_pipe != pdpu->is_rt_pipe);
pdpu->is_rt_pipe = is_rt_pipe;
dpu_format_populate_addrs(pstate->aspace, new_state->fb, &pstate->layout);
DPU_DEBUG_PLANE(pdpu, "FB[%u] " DRM_RECT_FP_FMT "->crtc%u " DRM_RECT_FMT
", %p4cc ubwc %d\n", fb->base.id, DRM_RECT_FP_ARG(&state->src),
crtc->base.id, DRM_RECT_ARG(&state->dst),
@ -1140,12 +1130,12 @@ static void dpu_plane_sspp_atomic_update(struct drm_plane *plane)
dpu_plane_sspp_update_pipe(plane, pipe, pipe_cfg, fmt,
drm_mode_vrefresh(&crtc->mode),
layout_valid ? &layout : NULL);
&pstate->layout);
if (r_pipe->sspp) {
dpu_plane_sspp_update_pipe(plane, r_pipe, r_pipe_cfg, fmt,
drm_mode_vrefresh(&crtc->mode),
layout_valid ? &layout : NULL);
&pstate->layout);
}
if (pstate->needs_qos_remap)
@ -1197,7 +1187,7 @@ static void dpu_plane_atomic_update(struct drm_plane *plane,
if (!new_state->visible) {
_dpu_plane_atomic_disable(plane);
} else {
dpu_plane_sspp_atomic_update(plane);
dpu_plane_sspp_atomic_update(plane, new_state);
}
}

View File

@ -31,6 +31,7 @@
* @plane_clk: calculated clk per plane
* @needs_dirtyfb: whether attached CRTC needs pixel data explicitly flushed
* @rotation: simplified drm rotation hint
* @layout: framebuffer memory layout
*/
struct dpu_plane_state {
struct drm_plane_state base;
@ -48,6 +49,8 @@ struct dpu_plane_state {
bool needs_dirtyfb;
unsigned int rotation;
struct dpu_hw_fmt_layout layout;
};
#define to_dpu_plane_state(x) \

View File

@ -25,24 +25,21 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b
addr = base_addr;
end_addr = base_addr + aligned_len;
if (!(*reg))
*reg = kvzalloc(len_padded, GFP_KERNEL);
if (*reg)
dump_addr = *reg;
*reg = kvzalloc(len_padded, GFP_KERNEL);
if (!*reg)
return;
dump_addr = *reg;
for (i = 0; i < num_rows; i++) {
x0 = (addr < end_addr) ? readl_relaxed(addr + 0x0) : 0;
x4 = (addr + 0x4 < end_addr) ? readl_relaxed(addr + 0x4) : 0;
x8 = (addr + 0x8 < end_addr) ? readl_relaxed(addr + 0x8) : 0;
xc = (addr + 0xc < end_addr) ? readl_relaxed(addr + 0xc) : 0;
if (dump_addr) {
dump_addr[i * 4] = x0;
dump_addr[i * 4 + 1] = x4;
dump_addr[i * 4 + 2] = x8;
dump_addr[i * 4 + 3] = xc;
}
dump_addr[i * 4] = x0;
dump_addr[i * 4 + 1] = x4;
dump_addr[i * 4 + 2] = x8;
dump_addr[i * 4 + 3] = xc;
addr += REG_DUMP_ALIGN;
}

View File

@ -118,6 +118,14 @@ struct msm_dp_desc {
bool wide_bus_supported;
};
static const struct msm_dp_desc sa8775p_dp_descs[] = {
{ .io_start = 0x0af54000, .id = MSM_DP_CONTROLLER_0, .wide_bus_supported = true },
{ .io_start = 0x0af5c000, .id = MSM_DP_CONTROLLER_1, .wide_bus_supported = true },
{ .io_start = 0x22154000, .id = MSM_DP_CONTROLLER_2, .wide_bus_supported = true },
{ .io_start = 0x2215c000, .id = MSM_DP_CONTROLLER_3, .wide_bus_supported = true },
{}
};
static const struct msm_dp_desc sc7180_dp_descs[] = {
{ .io_start = 0x0ae90000, .id = MSM_DP_CONTROLLER_0, .wide_bus_supported = true },
{}
@ -162,6 +170,7 @@ static const struct msm_dp_desc x1e80100_dp_descs[] = {
};
static const struct of_device_id dp_dt_match[] = {
{ .compatible = "qcom,sa8775p-dp", .data = &sa8775p_dp_descs },
{ .compatible = "qcom,sc7180-dp", .data = &sc7180_dp_descs },
{ .compatible = "qcom,sc7280-dp", .data = &sc7280_dp_descs },
{ .compatible = "qcom,sc7280-edp", .data = &sc7280_dp_descs },

View File

@ -157,9 +157,8 @@ static inline u32 pll_get_pll_cmp(u64 fdata, unsigned long ref_clk)
#define HDMI_MHZ_TO_HZ ((u64)1000000)
static int pll_get_post_div(struct hdmi_8998_post_divider *pd, u64 bclk)
{
u32 const ratio_list[] = {1, 2, 3, 4, 5, 6,
9, 10, 12, 15, 25};
u32 const band_list[] = {0, 1, 2, 3};
static const u32 ratio_list[] = {1, 2, 3, 4, 5, 6, 9, 10, 12, 15, 25};
static const u32 band_list[] = {0, 1, 2, 3};
u32 const sz_ratio = ARRAY_SIZE(ratio_list);
u32 const sz_band = ARRAY_SIZE(band_list);
u32 const cmp_cnt = 1024;
@ -270,7 +269,7 @@ static int pll_get_post_div(struct hdmi_8998_post_divider *pd, u64 bclk)
case 25:
found_hsclk_divsel = 14;
break;
};
}
pd->vco_freq = found_vco_freq;
pd->tx_band_sel = found_tx_band_sel;

View File

@ -985,6 +985,10 @@ module_param(prefer_mdp5, bool, 0444);
/* list all platforms supported by both mdp5 and dpu drivers */
static const char *const msm_mdp5_dpu_migration[] = {
"qcom,msm8917-mdp5",
"qcom,msm8937-mdp5",
"qcom,msm8953-mdp5",
"qcom,msm8996-mdp5",
"qcom,sdm630-mdp5",
"qcom,sdm660-mdp5",
NULL,

View File

@ -783,7 +783,7 @@ void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
mutex_unlock(&gpu->active_lock);
gpu->funcs->submit(gpu, submit);
gpu->cur_ctx_seqno = submit->queue->ctx->seqno;
submit->ring->cur_ctx_seqno = submit->queue->ctx->seqno;
pm_runtime_put(&gpu->pdev->dev);
hangcheck_timer_reset(gpu);

View File

@ -193,17 +193,6 @@ struct msm_gpu {
*/
refcount_t sysprof_active;
/**
* cur_ctx_seqno:
*
* The ctx->seqno value of the last context to submit rendering,
* and the one with current pgtables installed (for generations
* that support per-context pgtables). Tracked by seqno rather
* than pointer value to avoid dangling pointers, and cases where
* a ctx can be freed and a new one created with the same address.
*/
int cur_ctx_seqno;
/**
* lock:
*

View File

@ -140,6 +140,7 @@ void msm_devfreq_init(struct msm_gpu *gpu)
{
struct msm_gpu_devfreq *df = &gpu->devfreq;
struct msm_drm_private *priv = gpu->dev->dev_private;
int ret;
/* We need target support to do devfreq */
if (!gpu->funcs->gpu_busy)
@ -156,8 +157,12 @@ void msm_devfreq_init(struct msm_gpu *gpu)
mutex_init(&df->lock);
dev_pm_qos_add_request(&gpu->pdev->dev, &df->boost_freq,
DEV_PM_QOS_MIN_FREQUENCY, 0);
ret = dev_pm_qos_add_request(&gpu->pdev->dev, &df->boost_freq,
DEV_PM_QOS_MIN_FREQUENCY, 0);
if (ret < 0) {
DRM_DEV_ERROR(&gpu->pdev->dev, "Couldn't initialize QoS\n");
return;
}
msm_devfreq_profile.initial_freq = gpu->fast_rate;

View File

@ -177,6 +177,34 @@ TRACE_EVENT(msm_gpu_resume,
TP_printk("%u", __entry->dummy)
);
TRACE_EVENT(msm_gpu_preemption_trigger,
TP_PROTO(int ring_id_from, int ring_id_to),
TP_ARGS(ring_id_from, ring_id_to),
TP_STRUCT__entry(
__field(int, ring_id_from)
__field(int, ring_id_to)
),
TP_fast_assign(
__entry->ring_id_from = ring_id_from;
__entry->ring_id_to = ring_id_to;
),
TP_printk("preempting %u -> %u",
__entry->ring_id_from,
__entry->ring_id_to)
);
TRACE_EVENT(msm_gpu_preemption_irq,
TP_PROTO(u32 ring_id),
TP_ARGS(ring_id),
TP_STRUCT__entry(
__field(u32, ring_id)
),
TP_fast_assign(
__entry->ring_id = ring_id;
),
TP_printk("preempted to %u", __entry->ring_id)
);
#endif
#undef TRACE_INCLUDE_PATH

View File

@ -92,12 +92,6 @@ struct msm_kms_funcs {
* Format handling:
*/
/* do format checking on format modified through fb_cmd2 modifiers */
int (*check_modified_format)(const struct msm_kms *kms,
const struct msm_format *msm_fmt,
const struct drm_mode_fb_cmd2 *cmd,
struct drm_gem_object **bos);
/* misc: */
long (*round_pixclk)(struct msm_kms *kms, unsigned long rate,
struct drm_encoder *encoder);

View File

@ -573,6 +573,16 @@ static const struct msm_mdss_data qcm2290_data = {
.reg_bus_bw = 76800,
};
static const struct msm_mdss_data sa8775p_data = {
.ubwc_enc_version = UBWC_4_0,
.ubwc_dec_version = UBWC_4_0,
.ubwc_swizzle = 4,
.ubwc_static = 1,
.highest_bank_bit = 0,
.macrotile_mode = 1,
.reg_bus_bw = 74000,
};
static const struct msm_mdss_data sc7180_data = {
.ubwc_enc_version = UBWC_2_0,
.ubwc_dec_version = UBWC_2_0,
@ -710,6 +720,7 @@ static const struct of_device_id mdss_dt_match[] = {
{ .compatible = "qcom,mdss" },
{ .compatible = "qcom,msm8998-mdss", .data = &msm8998_data },
{ .compatible = "qcom,qcm2290-mdss", .data = &qcm2290_data },
{ .compatible = "qcom,sa8775p-mdss", .data = &sa8775p_data },
{ .compatible = "qcom,sdm670-mdss", .data = &sdm670_data },
{ .compatible = "qcom,sdm845-mdss", .data = &sdm845_data },
{ .compatible = "qcom,sc7180-mdss", .data = &sc7180_data },

View File

@ -64,7 +64,7 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id,
char name[32];
int ret;
/* We assume everwhere that MSM_GPU_RINGBUFFER_SZ is a power of 2 */
/* We assume everywhere that MSM_GPU_RINGBUFFER_SZ is a power of 2 */
BUILD_BUG_ON(!is_power_of_2(MSM_GPU_RINGBUFFER_SZ));
ring = kzalloc(sizeof(*ring), GFP_KERNEL);

View File

@ -31,10 +31,12 @@ struct msm_rbmemptrs {
volatile uint32_t rptr;
volatile uint32_t fence;
/* Introduced on A7xx */
volatile uint32_t bv_rptr;
volatile uint32_t bv_fence;
volatile struct msm_gpu_submit_stats stats[MSM_GPU_SUBMIT_STATS_COUNT];
volatile u64 ttbr0;
volatile u32 context_idr;
};
struct msm_cp_state {
@ -99,6 +101,22 @@ struct msm_ringbuffer {
* preemption. Can be aquired from irq context.
*/
spinlock_t preempt_lock;
/*
* Whether we skipped writing wptr and it needs to be updated in the
* future when the ring becomes current.
*/
bool restore_wptr;
/**
* cur_ctx_seqno:
*
* The ctx->seqno value of the last context to submit to this ring
* Tracked by seqno rather than pointer value to avoid dangling
* pointers, and cases where a ctx can be freed and a new one created
* with the same address.
*/
int cur_ctx_seqno;
};
struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id,

View File

@ -161,6 +161,8 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
struct msm_drm_private *priv = drm->dev_private;
struct msm_gpu_submitqueue *queue;
enum drm_sched_priority sched_prio;
extern int enable_preemption;
bool preemption_supported;
unsigned ring_nr;
int ret;
@ -170,6 +172,11 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
if (!priv->gpu)
return -ENODEV;
preemption_supported = priv->gpu->nr_rings == 1 && enable_preemption != 0;
if (flags & MSM_SUBMITQUEUE_ALLOW_PREEMPT && preemption_supported)
return -EINVAL;
ret = msm_gpu_convert_priority(priv->gpu, prio, &ring_nr, &sched_prio);
if (ret)
return ret;

View File

@ -2358,7 +2358,12 @@ to upconvert to 32b float internally?
<reg32 offset="0x0" name="REG" type="a6x_cp_protect"/>
</array>
<reg32 offset="0x08A0" name="CP_CONTEXT_SWITCH_CNTL"/>
<reg32 offset="0x08A0" name="CP_CONTEXT_SWITCH_CNTL">
<bitfield name="STOP" pos="0" type="boolean"/>
<bitfield name="LEVEL" low="6" high="7"/>
<bitfield name="USES_GMEM" pos="8" type="boolean"/>
<bitfield name="SKIP_SAVE_RESTORE" pos="9" type="boolean"/>
</reg32>
<reg64 offset="0x08A1" name="CP_CONTEXT_SWITCH_SMMU_INFO"/>
<reg64 offset="0x08A3" name="CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR"/>
<reg64 offset="0x08A5" name="CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR"/>

View File

@ -581,8 +581,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
and forcibly switch to the indicated context.
</doc>
<value name="CP_CONTEXT_SWITCH" value="0x54" variants="A6XX"/>
<!-- Note, kgsl calls this CP_SET_AMBLE: -->
<value name="CP_SET_CTXSWITCH_IB" value="0x55" variants="A6XX-"/>
<value name="CP_SET_AMBLE" value="0x55" variants="A6XX-"/>
<!--
Seems to always have the payload:
@ -2013,42 +2012,38 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32>
</domain>
<domain name="CP_SET_CTXSWITCH_IB" width="32">
<domain name="CP_SET_AMBLE" width="32">
<doc>
Used by the userspace driver to set various IB's which are
executed during context save/restore for handling
state that isn't restored by the
context switch routine itself.
</doc>
<enum name="ctxswitch_ib">
<value name="RESTORE_IB" value="0">
Used by the userspace and kernel drivers to set various IB's
which are executed during context save/restore for handling
state that isn't restored by the context switch routine itself.
</doc>
<enum name="amble_type">
<value name="PREAMBLE_AMBLE_TYPE" value="0">
<doc>Executed unconditionally when switching back to the context.</doc>
</value>
<value name="YIELD_RESTORE_IB" value="1">
<value name="BIN_PREAMBLE_AMBLE_TYPE" value="1">
<doc>
Executed when switching back after switching
away during execution of
a CP_SET_MARKER packet with RM6_YIELD as the
payload *and* the normal save routine was
bypassed for a shorter one. I think this is
connected to the "skipsaverestore" bit set by
the kernel when preempting.
a CP_SET_MARKER packet with RM6_BIN_RENDER_END as the
payload *and* skipsaverestore is set. This is
expected to restore static register values not
saved when skipsaverestore is set.
</doc>
</value>
<value name="SAVE_IB" value="2">
<value name="POSTAMBLE_AMBLE_TYPE" value="2">
<doc>
Executed when switching away from the context,
except for context switches initiated via
CP_YIELD.
</doc>
</value>
<value name="RB_SAVE_IB" value="3">
<value name="KMD_AMBLE_TYPE" value="3">
<doc>
This can only be set by the RB (i.e. the kernel)
and executes with protected mode off, but
is otherwise similar to SAVE_IB.
Note, kgsl calls this CP_KMD_AMBLE_TYPE
is otherwise similar to POSTAMBLE_AMBLE_TYPE.
</doc>
</value>
</enum>
@ -2060,7 +2055,7 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32>
<reg32 offset="2" name="2">
<bitfield name="DWORDS" low="0" high="19" type="uint"/>
<bitfield name="TYPE" low="20" high="21" type="ctxswitch_ib"/>
<bitfield name="TYPE" low="20" high="21" type="amble_type"/>
</reg32>
</domain>

View File

@ -347,7 +347,10 @@ struct drm_msm_gem_madvise {
* backwards compatibility as a "default" submitqueue
*/
#define MSM_SUBMITQUEUE_FLAGS (0)
#define MSM_SUBMITQUEUE_ALLOW_PREEMPT 0x00000001
#define MSM_SUBMITQUEUE_FLAGS ( \
MSM_SUBMITQUEUE_ALLOW_PREEMPT | \
0)
/*
* The submitqueue priority should be between 0 and MSM_PARAM_PRIORITIES-1,