mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-18 10:56:14 +00:00
Merge tag 'drm-msm-next-2021-10-26' of https://gitlab.freedesktop.org/drm/msm into drm-next
* eDP support in DP sub-driver (for newer SoCs with native eDP output) * dpu irq handling cleanup * CRC support for making igt happy * Support for NO_CONNECTOR bridges * dsi: 14nm phy support for msm8953 * mdp5: support for msm8x53, sdm450, sdm632 * various smaller fixes and cleanups Signed-off-by: Dave Airlie <airlied@redhat.com> From: Rob Clark <robdclark@gmail.com> Link: https://patchwork.freedesktop.org/patch/msgid/CAF6AEGsH9EwcpqGNNRJeL99NvFFjHX3SUg+nTYu0dHG5U9+QuA@mail.gmail.com
This commit is contained in:
commit
de99e64798
@ -17,9 +17,16 @@ properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sc7180-dp
|
||||
- qcom,sc8180x-dp
|
||||
- qcom,sc8180x-edp
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
items:
|
||||
- description: ahb register block
|
||||
- description: aux register block
|
||||
- description: link register block
|
||||
- description: p0 register block
|
||||
- description: p1 register block
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
@ -100,7 +107,11 @@ examples:
|
||||
|
||||
displayport-controller@ae90000 {
|
||||
compatible = "qcom,sc7180-dp";
|
||||
reg = <0xae90000 0x1400>;
|
||||
reg = <0xae90000 0x200>,
|
||||
<0xae90200 0x200>,
|
||||
<0xae90400 0xc00>,
|
||||
<0xae91000 0x400>,
|
||||
<0xae91400 0x400>;
|
||||
interrupt-parent = <&mdss>;
|
||||
interrupts = <12>;
|
||||
clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>,
|
||||
|
232
Documentation/devicetree/bindings/display/msm/dpu-sc7280.yaml
Normal file
232
Documentation/devicetree/bindings/display/msm/dpu-sc7280.yaml
Normal file
@ -0,0 +1,232 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/msm/dpu-sc7280.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Display DPU dt properties for SC7280
|
||||
|
||||
maintainers:
|
||||
- Krishna Manikandan <mkrishn@codeaurora.org>
|
||||
|
||||
description: |
|
||||
Device tree bindings for MSM Mobile Display Subsystem (MDSS) that encapsulates
|
||||
sub-blocks like DPU display controller, DSI and DP interfaces etc. Device tree
|
||||
bindings of MDSS and DPU are mentioned for SC7280.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,sc7280-mdss
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
reg-names:
|
||||
const: mdss
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Display AHB clock from gcc
|
||||
- description: Display AHB clock from dispcc
|
||||
- description: Display core clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: iface
|
||||
- const: ahb
|
||||
- const: core
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
"#address-cells": true
|
||||
|
||||
"#size-cells": true
|
||||
|
||||
"#interrupt-cells":
|
||||
const: 1
|
||||
|
||||
iommus:
|
||||
items:
|
||||
- description: Phandle to apps_smmu node with SID mask for Hard-Fail port0
|
||||
|
||||
ranges: true
|
||||
|
||||
interconnects:
|
||||
items:
|
||||
- description: Interconnect path specifying the port ids for data bus
|
||||
|
||||
interconnect-names:
|
||||
const: mdp0-mem
|
||||
|
||||
patternProperties:
|
||||
"^display-controller@[0-9a-f]+$":
|
||||
type: object
|
||||
description: Node containing the properties of DPU.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,sc7280-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
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
operating-points-v2: true
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
description: |
|
||||
Contains the list of output ports from DPU device. These ports
|
||||
connect to interfaces that are external to the DPU hardware,
|
||||
such as DSI, DP etc. Each output port contains an endpoint that
|
||||
describes how it is connected to an external interface.
|
||||
|
||||
properties:
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: DPU_INTF1 (DSI)
|
||||
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: DPU_INTF5 (EDP)
|
||||
|
||||
required:
|
||||
- port@0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- clocks
|
||||
- interrupts
|
||||
- power-domains
|
||||
- operating-points-v2
|
||||
- ports
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- power-domains
|
||||
- clocks
|
||||
- interrupts
|
||||
- interrupt-controller
|
||||
- iommus
|
||||
- ranges
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,dispcc-sc7280.h>
|
||||
#include <dt-bindings/clock/qcom,gcc-sc7280.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interconnect/qcom,sc7280.h>
|
||||
#include <dt-bindings/power/qcom-rpmpd.h>
|
||||
|
||||
display-subsystem@ae00000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "qcom,sc7280-mdss";
|
||||
reg = <0xae00000 0x1000>;
|
||||
reg-names = "mdss";
|
||||
power-domains = <&dispcc DISP_CC_MDSS_CORE_GDSC>;
|
||||
clocks = <&gcc GCC_DISP_AHB_CLK>,
|
||||
<&dispcc DISP_CC_MDSS_AHB_CLK>,
|
||||
<&dispcc DISP_CC_MDSS_MDP_CLK>;
|
||||
clock-names = "iface",
|
||||
"ahb",
|
||||
"core";
|
||||
|
||||
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
|
||||
interconnects = <&mmss_noc MASTER_MDP0 &mc_virt SLAVE_EBI1>;
|
||||
interconnect-names = "mdp0-mem";
|
||||
|
||||
iommus = <&apps_smmu 0x900 0x402>;
|
||||
ranges;
|
||||
|
||||
display-controller@ae01000 {
|
||||
compatible = "qcom,sc7280-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";
|
||||
|
||||
interrupt-parent = <&mdss>;
|
||||
interrupts = <0>;
|
||||
power-domains = <&rpmhpd SC7280_CX>;
|
||||
operating-points-v2 = <&mdp_opp_table>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
dpu_intf1_out: endpoint {
|
||||
remote-endpoint = <&dsi0_in>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
dpu_intf5_out: endpoint {
|
||||
remote-endpoint = <&edp_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -17,6 +17,7 @@ properties:
|
||||
enum:
|
||||
- qcom,dsi-phy-14nm
|
||||
- qcom,dsi-phy-14nm-660
|
||||
- qcom,dsi-phy-14nm-8953
|
||||
|
||||
reg:
|
||||
items:
|
||||
|
@ -1,157 +0,0 @@
|
||||
Qualcomm adreno/snapdragon GPU
|
||||
|
||||
Required properties:
|
||||
- compatible: "qcom,adreno-XYZ.W", "qcom,adreno" or
|
||||
"amd,imageon-XYZ.W", "amd,imageon"
|
||||
for example: "qcom,adreno-306.0", "qcom,adreno"
|
||||
Note that you need to list the less specific "qcom,adreno" (since this
|
||||
is what the device is matched on), in addition to the more specific
|
||||
with the chip-id.
|
||||
If "amd,imageon" is used, there should be no top level msm device.
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- interrupts: The interrupt signal from the gpu.
|
||||
- clocks: device clocks (if applicable)
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names: the following clocks are required by a3xx, a4xx and a5xx
|
||||
cores:
|
||||
* "core"
|
||||
* "iface"
|
||||
* "mem_iface"
|
||||
For GMU attached devices the GPU clocks are not used and are not required. The
|
||||
following devices should not list clocks:
|
||||
- qcom,adreno-630.2
|
||||
- iommus: optional phandle to an adreno iommu instance
|
||||
- operating-points-v2: optional phandle to the OPP operating points
|
||||
- interconnects: optional phandle to an interconnect provider. See
|
||||
../interconnect/interconnect.txt for details. Some A3xx and all A4xx platforms
|
||||
will have two paths; all others will have one path.
|
||||
- interconnect-names: The names of the interconnect paths that correspond to the
|
||||
interconnects property. Values must be gfx-mem and ocmem.
|
||||
- qcom,gmu: For GMU attached devices a phandle to the GMU device that will
|
||||
control the power for the GPU. Applicable targets:
|
||||
- qcom,adreno-630.2
|
||||
- zap-shader: For a5xx and a6xx devices this node contains a memory-region that
|
||||
points to reserved memory to store the zap shader that can be used to help
|
||||
bring the GPU out of secure mode.
|
||||
- firmware-name: optional property of the 'zap-shader' node, listing the
|
||||
relative path of the device specific zap firmware.
|
||||
- sram: phandle to the On Chip Memory (OCMEM) that's present on some a3xx and
|
||||
a4xx Snapdragon SoCs. See
|
||||
Documentation/devicetree/bindings/sram/qcom,ocmem.yaml.
|
||||
|
||||
Optional properties:
|
||||
- #cooling-cells: The value must be 2. For details, please refer
|
||||
Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml.
|
||||
|
||||
Example 3xx/4xx:
|
||||
|
||||
/ {
|
||||
...
|
||||
|
||||
gpu: adreno@fdb00000 {
|
||||
compatible = "qcom,adreno-330.2",
|
||||
"qcom,adreno";
|
||||
reg = <0xfdb00000 0x10000>;
|
||||
reg-names = "kgsl_3d0_reg_memory";
|
||||
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "kgsl_3d0_irq";
|
||||
clock-names = "core",
|
||||
"iface",
|
||||
"mem_iface";
|
||||
clocks = <&mmcc OXILI_GFX3D_CLK>,
|
||||
<&mmcc OXILICX_AHB_CLK>,
|
||||
<&mmcc OXILICX_AXI_CLK>;
|
||||
sram = <&gpu_sram>;
|
||||
power-domains = <&mmcc OXILICX_GDSC>;
|
||||
operating-points-v2 = <&gpu_opp_table>;
|
||||
iommus = <&gpu_iommu 0>;
|
||||
#cooling-cells = <2>;
|
||||
};
|
||||
|
||||
gpu_sram: ocmem@fdd00000 {
|
||||
compatible = "qcom,msm8974-ocmem";
|
||||
|
||||
reg = <0xfdd00000 0x2000>,
|
||||
<0xfec00000 0x180000>;
|
||||
reg-names = "ctrl",
|
||||
"mem";
|
||||
|
||||
clocks = <&rpmcc RPM_SMD_OCMEMGX_CLK>,
|
||||
<&mmcc OCMEMCX_OCMEMNOC_CLK>;
|
||||
clock-names = "core",
|
||||
"iface";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
gpu_sram: gpu-sram@0 {
|
||||
reg = <0x0 0x100000>;
|
||||
ranges = <0 0 0xfec00000 0x100000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Example a6xx (with GMU):
|
||||
|
||||
/ {
|
||||
...
|
||||
|
||||
gpu@5000000 {
|
||||
compatible = "qcom,adreno-630.2", "qcom,adreno";
|
||||
#stream-id-cells = <16>;
|
||||
|
||||
reg = <0x5000000 0x40000>, <0x509e000 0x10>;
|
||||
reg-names = "kgsl_3d0_reg_memory", "cx_mem";
|
||||
|
||||
#cooling-cells = <2>;
|
||||
|
||||
/*
|
||||
* Look ma, no clocks! The GPU clocks and power are
|
||||
* controlled entirely by the GMU
|
||||
*/
|
||||
|
||||
interrupts = <GIC_SPI 300 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
iommus = <&adreno_smmu 0>;
|
||||
|
||||
operating-points-v2 = <&gpu_opp_table>;
|
||||
|
||||
interconnects = <&rsc_hlos MASTER_GFX3D &rsc_hlos SLAVE_EBI1>;
|
||||
interconnect-names = "gfx-mem";
|
||||
|
||||
gpu_opp_table: opp-table {
|
||||
compatible = "operating-points-v2";
|
||||
|
||||
opp-430000000 {
|
||||
opp-hz = /bits/ 64 <430000000>;
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_SVS_L1>;
|
||||
opp-peak-kBps = <5412000>;
|
||||
};
|
||||
|
||||
opp-355000000 {
|
||||
opp-hz = /bits/ 64 <355000000>;
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_SVS>;
|
||||
opp-peak-kBps = <3072000>;
|
||||
};
|
||||
|
||||
opp-267000000 {
|
||||
opp-hz = /bits/ 64 <267000000>;
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>;
|
||||
opp-peak-kBps = <3072000>;
|
||||
};
|
||||
|
||||
opp-180000000 {
|
||||
opp-hz = /bits/ 64 <180000000>;
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_MIN_SVS>;
|
||||
opp-peak-kBps = <1804000>;
|
||||
};
|
||||
};
|
||||
|
||||
qcom,gmu = <&gmu>;
|
||||
|
||||
zap-shader {
|
||||
memory-region = <&zap_shader_region>;
|
||||
firmware-name = "qcom/LENOVO/81JL/qcdxkmsuc850.mbn"
|
||||
};
|
||||
};
|
||||
};
|
288
Documentation/devicetree/bindings/display/msm/gpu.yaml
Normal file
288
Documentation/devicetree/bindings/display/msm/gpu.yaml
Normal file
@ -0,0 +1,288 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
|
||||
$id: "http://devicetree.org/schemas/display/msm/gpu.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Devicetree bindings for the Adreno or Snapdragon GPUs
|
||||
|
||||
maintainers:
|
||||
- Rob Clark <robdclark@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- description: |
|
||||
The driver is parsing the compat string for Adreno to
|
||||
figure out the gpu-id and patch level.
|
||||
items:
|
||||
- pattern: '^qcom,adreno-[3-6][0-9][0-9]\.[0-9]$'
|
||||
- const: qcom,adreno
|
||||
- description: |
|
||||
The driver is parsing the compat string for Imageon to
|
||||
figure out the gpu-id and patch level.
|
||||
items:
|
||||
- pattern: '^amd,imageon-200\.[0-1]$'
|
||||
- const: amd,imageon
|
||||
|
||||
clocks: true
|
||||
|
||||
clock-names: true
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
reg-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: kgsl_3d0_reg_memory
|
||||
- const: cx_mem
|
||||
- const: cx_dbgc
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-names:
|
||||
maxItems: 1
|
||||
|
||||
interconnects:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interconnect-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: gfx-mem
|
||||
- const: ocmem
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
sram:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
description: |
|
||||
phandles to one or more reserved on-chip SRAM regions.
|
||||
phandle to the On Chip Memory (OCMEM) that's present on some a3xx and
|
||||
a4xx Snapdragon SoCs. See
|
||||
Documentation/devicetree/bindings/sram/qcom,ocmem.yaml
|
||||
|
||||
operating-points-v2: true
|
||||
opp-table:
|
||||
type: object
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
zap-shader:
|
||||
type: object
|
||||
description: |
|
||||
For a5xx and a6xx devices this node contains a memory-region that
|
||||
points to reserved memory to store the zap shader that can be used to
|
||||
help bring the GPU out of secure mode.
|
||||
properties:
|
||||
memory-region:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
firmware-name:
|
||||
description: |
|
||||
Default name of the firmware to load to the remote processor.
|
||||
|
||||
"#cooling-cells":
|
||||
const: 2
|
||||
|
||||
nvmem-cell-names:
|
||||
maxItems: 1
|
||||
|
||||
nvmem-cells:
|
||||
description: efuse registers
|
||||
maxItems: 1
|
||||
|
||||
qcom,gmu:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: |
|
||||
For GMU attached devices a phandle to the GMU device that will
|
||||
control the power for the GPU.
|
||||
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
pattern: '^qcom,adreno-[3-5][0-9][0-9]\.[0-9]$'
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 7
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
anyOf:
|
||||
- const: core
|
||||
description: GPU Core clock
|
||||
- const: iface
|
||||
description: GPU Interface clock
|
||||
- const: mem
|
||||
description: GPU Memory clock
|
||||
- const: mem_iface
|
||||
description: GPU Memory Interface clock
|
||||
- const: alt_mem_iface
|
||||
description: GPU Alternative Memory Interface clock
|
||||
- const: gfx3d
|
||||
description: GPU 3D engine clock
|
||||
- const: rbbmtimer
|
||||
description: GPU RBBM Timer for Adreno 5xx series
|
||||
minItems: 2
|
||||
maxItems: 7
|
||||
|
||||
required:
|
||||
- clocks
|
||||
- clock-names
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
pattern: '^qcom,adreno-6[0-9][0-9]\.[0-9]$'
|
||||
|
||||
then: # Since Adreno 6xx series clocks should be defined in GMU
|
||||
properties:
|
||||
clocks: false
|
||||
clock-names: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
||||
// Example a3xx/4xx:
|
||||
|
||||
#include <dt-bindings/clock/qcom,mmcc-msm8974.h>
|
||||
#include <dt-bindings/clock/qcom,rpmcc.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
gpu: gpu@fdb00000 {
|
||||
compatible = "qcom,adreno-330.2", "qcom,adreno";
|
||||
|
||||
reg = <0xfdb00000 0x10000>;
|
||||
reg-names = "kgsl_3d0_reg_memory";
|
||||
|
||||
clock-names = "core", "iface", "mem_iface";
|
||||
clocks = <&mmcc OXILI_GFX3D_CLK>,
|
||||
<&mmcc OXILICX_AHB_CLK>,
|
||||
<&mmcc OXILICX_AXI_CLK>;
|
||||
|
||||
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "kgsl_3d0_irq";
|
||||
|
||||
sram = <&gpu_sram>;
|
||||
power-domains = <&mmcc OXILICX_GDSC>;
|
||||
operating-points-v2 = <&gpu_opp_table>;
|
||||
iommus = <&gpu_iommu 0>;
|
||||
#cooling-cells = <2>;
|
||||
};
|
||||
|
||||
ocmem@fdd00000 {
|
||||
compatible = "qcom,msm8974-ocmem";
|
||||
|
||||
reg = <0xfdd00000 0x2000>,
|
||||
<0xfec00000 0x180000>;
|
||||
reg-names = "ctrl", "mem";
|
||||
|
||||
clocks = <&rpmcc RPM_SMD_OCMEMGX_CLK>,
|
||||
<&mmcc OCMEMCX_OCMEMNOC_CLK>;
|
||||
clock-names = "core", "iface";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0xfec00000 0x100000>;
|
||||
|
||||
gpu_sram: gpu-sram@0 {
|
||||
reg = <0x0 0x100000>;
|
||||
};
|
||||
};
|
||||
- |
|
||||
|
||||
// Example a6xx (with GMU):
|
||||
|
||||
#include <dt-bindings/clock/qcom,gpucc-sdm845.h>
|
||||
#include <dt-bindings/clock/qcom,gcc-sdm845.h>
|
||||
#include <dt-bindings/power/qcom-rpmpd.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interconnect/qcom,sdm845.h>
|
||||
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
zap_shader_region: gpu@8f200000 {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0x0 0x90b00000 0x0 0xa00000>;
|
||||
no-map;
|
||||
};
|
||||
};
|
||||
|
||||
gpu@5000000 {
|
||||
compatible = "qcom,adreno-630.2", "qcom,adreno";
|
||||
|
||||
reg = <0x5000000 0x40000>, <0x509e000 0x10>;
|
||||
reg-names = "kgsl_3d0_reg_memory", "cx_mem";
|
||||
|
||||
#cooling-cells = <2>;
|
||||
|
||||
interrupts = <GIC_SPI 300 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
iommus = <&adreno_smmu 0>;
|
||||
|
||||
operating-points-v2 = <&gpu_opp_table>;
|
||||
|
||||
interconnects = <&rsc_hlos MASTER_GFX3D &rsc_hlos SLAVE_EBI1>;
|
||||
interconnect-names = "gfx-mem";
|
||||
|
||||
qcom,gmu = <&gmu>;
|
||||
|
||||
gpu_opp_table: opp-table {
|
||||
compatible = "operating-points-v2";
|
||||
|
||||
opp-430000000 {
|
||||
opp-hz = /bits/ 64 <430000000>;
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_SVS_L1>;
|
||||
opp-peak-kBps = <5412000>;
|
||||
};
|
||||
|
||||
opp-355000000 {
|
||||
opp-hz = /bits/ 64 <355000000>;
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_SVS>;
|
||||
opp-peak-kBps = <3072000>;
|
||||
};
|
||||
|
||||
opp-267000000 {
|
||||
opp-hz = /bits/ 64 <267000000>;
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>;
|
||||
opp-peak-kBps = <3072000>;
|
||||
};
|
||||
|
||||
opp-180000000 {
|
||||
opp-hz = /bits/ 64 <180000000>;
|
||||
opp-level = <RPMH_REGULATOR_LEVEL_MIN_SVS>;
|
||||
opp-peak-kBps = <1804000>;
|
||||
};
|
||||
};
|
||||
|
||||
zap-shader {
|
||||
memory-region = <&zap_shader_region>;
|
||||
firmware-name = "qcom/LENOVO/81JL/qcdxkmsuc850.mbn";
|
||||
};
|
||||
};
|
@ -1733,7 +1733,6 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
|
||||
|
||||
DBG("%s", dev_name(gpu->dev));
|
||||
|
||||
flush_workqueue(gpu->wq);
|
||||
destroy_workqueue(gpu->wq);
|
||||
|
||||
etnaviv_sched_fini(gpu);
|
||||
|
@ -14,6 +14,8 @@ config DRM_MSM
|
||||
select REGULATOR
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_PANEL
|
||||
select DRM_BRIDGE
|
||||
select DRM_PANEL_BRIDGE
|
||||
select DRM_SCHED
|
||||
select SHMEM
|
||||
select TMPFS
|
||||
|
@ -51,7 +51,6 @@ msm-y := \
|
||||
disp/mdp5/mdp5_mixer.o \
|
||||
disp/mdp5/mdp5_plane.o \
|
||||
disp/mdp5/mdp5_smp.o \
|
||||
disp/dpu1/dpu_core_irq.o \
|
||||
disp/dpu1/dpu_core_perf.o \
|
||||
disp/dpu1/dpu_crtc.o \
|
||||
disp/dpu1/dpu_encoder.o \
|
||||
|
@ -138,7 +138,7 @@ reset_set(void *data, u64 val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(reset_fops, NULL, reset_set, "%llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(reset_fops, NULL, reset_set, "%llx\n");
|
||||
|
||||
|
||||
void a5xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor)
|
||||
@ -154,6 +154,6 @@ void a5xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor)
|
||||
ARRAY_SIZE(a5xx_debugfs_list),
|
||||
minor->debugfs_root, minor);
|
||||
|
||||
debugfs_create_file("reset", S_IWUGO, minor->debugfs_root, dev,
|
||||
&reset_fops);
|
||||
debugfs_create_file_unsafe("reset", S_IWUGO, minor->debugfs_root, dev,
|
||||
&reset_fops);
|
||||
}
|
||||
|
@ -516,11 +516,11 @@ static void a6xx_gmu_rpmh_init(struct a6xx_gmu *gmu)
|
||||
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
|
||||
struct platform_device *pdev = to_platform_device(gmu->dev);
|
||||
void __iomem *pdcptr = a6xx_gmu_get_mmio(pdev, "gmu_pdc");
|
||||
void __iomem *seqptr;
|
||||
void __iomem *seqptr = NULL;
|
||||
uint32_t pdc_address_offset;
|
||||
bool pdc_in_aop = false;
|
||||
|
||||
if (!pdcptr)
|
||||
if (IS_ERR(pdcptr))
|
||||
goto err;
|
||||
|
||||
if (adreno_is_a650(adreno_gpu) || adreno_is_a660_family(adreno_gpu))
|
||||
@ -532,7 +532,7 @@ static void a6xx_gmu_rpmh_init(struct a6xx_gmu *gmu)
|
||||
|
||||
if (!pdc_in_aop) {
|
||||
seqptr = a6xx_gmu_get_mmio(pdev, "gmu_pdc_seq");
|
||||
if (!seqptr)
|
||||
if (IS_ERR(seqptr))
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -891,7 +891,7 @@ static void a6xx_gmu_set_initial_freq(struct msm_gpu *gpu, struct a6xx_gmu *gmu)
|
||||
unsigned long gpu_freq = gmu->gpu_freqs[gmu->current_perf_index];
|
||||
|
||||
gpu_opp = dev_pm_opp_find_freq_exact(&gpu->pdev->dev, gpu_freq, true);
|
||||
if (IS_ERR_OR_NULL(gpu_opp))
|
||||
if (IS_ERR(gpu_opp))
|
||||
return;
|
||||
|
||||
gmu->freq = 0; /* so a6xx_gmu_set_freq() doesn't exit early */
|
||||
@ -905,7 +905,7 @@ static void a6xx_gmu_set_initial_bw(struct msm_gpu *gpu, struct a6xx_gmu *gmu)
|
||||
unsigned long gpu_freq = gmu->gpu_freqs[gmu->current_perf_index];
|
||||
|
||||
gpu_opp = dev_pm_opp_find_freq_exact(&gpu->pdev->dev, gpu_freq, true);
|
||||
if (IS_ERR_OR_NULL(gpu_opp))
|
||||
if (IS_ERR(gpu_opp))
|
||||
return;
|
||||
|
||||
dev_pm_opp_set_opp(&gpu->pdev->dev, gpu_opp);
|
||||
|
@ -180,7 +180,7 @@ static int debugbus_read(struct msm_gpu *gpu, u32 block, u32 offset,
|
||||
msm_readl((ptr) + ((offset) << 2))
|
||||
|
||||
/* read a value from the CX debug bus */
|
||||
static int cx_debugbus_read(void *__iomem cxdbg, u32 block, u32 offset,
|
||||
static int cx_debugbus_read(void __iomem *cxdbg, u32 block, u32 offset,
|
||||
u32 *data)
|
||||
{
|
||||
u32 reg = A6XX_CX_DBGC_CFG_DBGBUS_SEL_A_PING_INDEX(offset) |
|
||||
|
@ -1,256 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#include "dpu_core_irq.h"
|
||||
#include "dpu_trace.h"
|
||||
|
||||
/**
|
||||
* dpu_core_irq_callback_handler - dispatch core interrupts
|
||||
* @arg: private data of callback handler
|
||||
* @irq_idx: interrupt index
|
||||
*/
|
||||
static void dpu_core_irq_callback_handler(void *arg, int irq_idx)
|
||||
{
|
||||
struct dpu_kms *dpu_kms = arg;
|
||||
struct dpu_irq *irq_obj = &dpu_kms->irq_obj;
|
||||
struct dpu_irq_callback *cb;
|
||||
|
||||
VERB("irq_idx=%d\n", irq_idx);
|
||||
|
||||
if (list_empty(&irq_obj->irq_cb_tbl[irq_idx]))
|
||||
DRM_ERROR("no registered cb, idx:%d\n", irq_idx);
|
||||
|
||||
atomic_inc(&irq_obj->irq_counts[irq_idx]);
|
||||
|
||||
/*
|
||||
* Perform registered function callback
|
||||
*/
|
||||
list_for_each_entry(cb, &irq_obj->irq_cb_tbl[irq_idx], list)
|
||||
if (cb->func)
|
||||
cb->func(cb->arg, irq_idx);
|
||||
}
|
||||
|
||||
u32 dpu_core_irq_read(struct dpu_kms *dpu_kms, int irq_idx, bool clear)
|
||||
{
|
||||
if (!dpu_kms->hw_intr ||
|
||||
!dpu_kms->hw_intr->ops.get_interrupt_status)
|
||||
return 0;
|
||||
|
||||
if (irq_idx < 0) {
|
||||
DPU_ERROR("[%pS] invalid irq_idx=%d\n",
|
||||
__builtin_return_address(0), irq_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dpu_kms->hw_intr->ops.get_interrupt_status(dpu_kms->hw_intr,
|
||||
irq_idx, clear);
|
||||
}
|
||||
|
||||
int dpu_core_irq_register_callback(struct dpu_kms *dpu_kms, int irq_idx,
|
||||
struct dpu_irq_callback *register_irq_cb)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
|
||||
if (!dpu_kms->irq_obj.irq_cb_tbl) {
|
||||
DPU_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!register_irq_cb || !register_irq_cb->func) {
|
||||
DPU_ERROR("invalid irq_cb:%d func:%d\n",
|
||||
register_irq_cb != NULL,
|
||||
register_irq_cb ?
|
||||
register_irq_cb->func != NULL : -1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (irq_idx < 0 || irq_idx >= dpu_kms->hw_intr->total_irqs) {
|
||||
DPU_ERROR("invalid IRQ index: [%d]\n", irq_idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
VERB("[%pS] irq_idx=%d\n", __builtin_return_address(0), irq_idx);
|
||||
|
||||
irq_flags = dpu_kms->hw_intr->ops.lock(dpu_kms->hw_intr);
|
||||
trace_dpu_core_irq_register_callback(irq_idx, register_irq_cb);
|
||||
list_del_init(®ister_irq_cb->list);
|
||||
list_add_tail(®ister_irq_cb->list,
|
||||
&dpu_kms->irq_obj.irq_cb_tbl[irq_idx]);
|
||||
if (list_is_first(®ister_irq_cb->list,
|
||||
&dpu_kms->irq_obj.irq_cb_tbl[irq_idx])) {
|
||||
int ret = dpu_kms->hw_intr->ops.enable_irq_locked(
|
||||
dpu_kms->hw_intr,
|
||||
irq_idx);
|
||||
if (ret)
|
||||
DPU_ERROR("Fail to enable IRQ for irq_idx:%d\n",
|
||||
irq_idx);
|
||||
}
|
||||
dpu_kms->hw_intr->ops.unlock(dpu_kms->hw_intr, irq_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dpu_core_irq_unregister_callback(struct dpu_kms *dpu_kms, int irq_idx,
|
||||
struct dpu_irq_callback *register_irq_cb)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
|
||||
if (!dpu_kms->irq_obj.irq_cb_tbl) {
|
||||
DPU_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!register_irq_cb || !register_irq_cb->func) {
|
||||
DPU_ERROR("invalid irq_cb:%d func:%d\n",
|
||||
register_irq_cb != NULL,
|
||||
register_irq_cb ?
|
||||
register_irq_cb->func != NULL : -1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (irq_idx < 0 || irq_idx >= dpu_kms->hw_intr->total_irqs) {
|
||||
DPU_ERROR("invalid IRQ index: [%d]\n", irq_idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
VERB("[%pS] irq_idx=%d\n", __builtin_return_address(0), irq_idx);
|
||||
|
||||
irq_flags = dpu_kms->hw_intr->ops.lock(dpu_kms->hw_intr);
|
||||
trace_dpu_core_irq_unregister_callback(irq_idx, register_irq_cb);
|
||||
list_del_init(®ister_irq_cb->list);
|
||||
/* empty callback list but interrupt is still enabled */
|
||||
if (list_empty(&dpu_kms->irq_obj.irq_cb_tbl[irq_idx])) {
|
||||
int ret = dpu_kms->hw_intr->ops.disable_irq_locked(
|
||||
dpu_kms->hw_intr,
|
||||
irq_idx);
|
||||
if (ret)
|
||||
DPU_ERROR("Fail to disable IRQ for irq_idx:%d\n",
|
||||
irq_idx);
|
||||
VERB("irq_idx=%d ret=%d\n", irq_idx, ret);
|
||||
}
|
||||
dpu_kms->hw_intr->ops.unlock(dpu_kms->hw_intr, irq_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpu_clear_all_irqs(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
if (!dpu_kms->hw_intr || !dpu_kms->hw_intr->ops.clear_all_irqs)
|
||||
return;
|
||||
|
||||
dpu_kms->hw_intr->ops.clear_all_irqs(dpu_kms->hw_intr);
|
||||
}
|
||||
|
||||
static void dpu_disable_all_irqs(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
if (!dpu_kms->hw_intr || !dpu_kms->hw_intr->ops.disable_all_irqs)
|
||||
return;
|
||||
|
||||
dpu_kms->hw_intr->ops.disable_all_irqs(dpu_kms->hw_intr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int dpu_debugfs_core_irq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct dpu_kms *dpu_kms = s->private;
|
||||
struct dpu_irq *irq_obj = &dpu_kms->irq_obj;
|
||||
struct dpu_irq_callback *cb;
|
||||
unsigned long irq_flags;
|
||||
int i, irq_count, cb_count;
|
||||
|
||||
if (WARN_ON(!irq_obj->irq_cb_tbl))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < irq_obj->total_irqs; i++) {
|
||||
irq_flags = dpu_kms->hw_intr->ops.lock(dpu_kms->hw_intr);
|
||||
cb_count = 0;
|
||||
irq_count = atomic_read(&irq_obj->irq_counts[i]);
|
||||
list_for_each_entry(cb, &irq_obj->irq_cb_tbl[i], list)
|
||||
cb_count++;
|
||||
dpu_kms->hw_intr->ops.unlock(dpu_kms->hw_intr, irq_flags);
|
||||
|
||||
if (irq_count || cb_count)
|
||||
seq_printf(s, "idx:%d irq:%d cb:%d\n",
|
||||
i, irq_count, cb_count);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(dpu_debugfs_core_irq);
|
||||
|
||||
void dpu_debugfs_core_irq_init(struct dpu_kms *dpu_kms,
|
||||
struct dentry *parent)
|
||||
{
|
||||
debugfs_create_file("core_irq", 0600, parent, dpu_kms,
|
||||
&dpu_debugfs_core_irq_fops);
|
||||
}
|
||||
#endif
|
||||
|
||||
void dpu_core_irq_preinstall(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
int i;
|
||||
|
||||
pm_runtime_get_sync(&dpu_kms->pdev->dev);
|
||||
dpu_clear_all_irqs(dpu_kms);
|
||||
dpu_disable_all_irqs(dpu_kms);
|
||||
pm_runtime_put_sync(&dpu_kms->pdev->dev);
|
||||
|
||||
/* Create irq callbacks for all possible irq_idx */
|
||||
dpu_kms->irq_obj.total_irqs = dpu_kms->hw_intr->total_irqs;
|
||||
dpu_kms->irq_obj.irq_cb_tbl = kcalloc(dpu_kms->irq_obj.total_irqs,
|
||||
sizeof(struct list_head), GFP_KERNEL);
|
||||
dpu_kms->irq_obj.irq_counts = kcalloc(dpu_kms->irq_obj.total_irqs,
|
||||
sizeof(atomic_t), GFP_KERNEL);
|
||||
for (i = 0; i < dpu_kms->irq_obj.total_irqs; i++) {
|
||||
INIT_LIST_HEAD(&dpu_kms->irq_obj.irq_cb_tbl[i]);
|
||||
atomic_set(&dpu_kms->irq_obj.irq_counts[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
void dpu_core_irq_uninstall(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
int i;
|
||||
|
||||
pm_runtime_get_sync(&dpu_kms->pdev->dev);
|
||||
for (i = 0; i < dpu_kms->irq_obj.total_irqs; i++)
|
||||
if (!list_empty(&dpu_kms->irq_obj.irq_cb_tbl[i]))
|
||||
DPU_ERROR("irq_idx=%d still enabled/registered\n", i);
|
||||
|
||||
dpu_clear_all_irqs(dpu_kms);
|
||||
dpu_disable_all_irqs(dpu_kms);
|
||||
pm_runtime_put_sync(&dpu_kms->pdev->dev);
|
||||
|
||||
kfree(dpu_kms->irq_obj.irq_cb_tbl);
|
||||
kfree(dpu_kms->irq_obj.irq_counts);
|
||||
dpu_kms->irq_obj.irq_cb_tbl = NULL;
|
||||
dpu_kms->irq_obj.irq_counts = NULL;
|
||||
dpu_kms->irq_obj.total_irqs = 0;
|
||||
}
|
||||
|
||||
irqreturn_t dpu_core_irq(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
/*
|
||||
* Dispatch to HW driver to handle interrupt lookup that is being
|
||||
* fired. When matching interrupt is located, HW driver will call to
|
||||
* dpu_core_irq_callback_handler with the irq_idx from the lookup table.
|
||||
* dpu_core_irq_callback_handler will perform the registered function
|
||||
* callback, and do the interrupt status clearing once the registered
|
||||
* callback is finished.
|
||||
* Function will also clear the interrupt status after reading.
|
||||
*/
|
||||
dpu_kms->hw_intr->ops.dispatch_irqs(
|
||||
dpu_kms->hw_intr,
|
||||
dpu_core_irq_callback_handler,
|
||||
dpu_kms);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2014-2018 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*/
|
||||
@ -70,17 +70,147 @@ static struct drm_encoder *get_encoder_from_crtc(struct drm_crtc *crtc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static u32 dpu_crtc_get_vblank_counter(struct drm_crtc *crtc)
|
||||
static enum dpu_crtc_crc_source dpu_crtc_parse_crc_source(const char *src_name)
|
||||
{
|
||||
struct drm_encoder *encoder;
|
||||
if (!src_name ||
|
||||
!strcmp(src_name, "none"))
|
||||
return DPU_CRTC_CRC_SOURCE_NONE;
|
||||
if (!strcmp(src_name, "auto") ||
|
||||
!strcmp(src_name, "lm"))
|
||||
return DPU_CRTC_CRC_SOURCE_LAYER_MIXER;
|
||||
|
||||
encoder = get_encoder_from_crtc(crtc);
|
||||
if (!encoder) {
|
||||
DRM_ERROR("no encoder found for crtc %d\n", crtc->index);
|
||||
return false;
|
||||
return DPU_CRTC_CRC_SOURCE_INVALID;
|
||||
}
|
||||
|
||||
static int dpu_crtc_verify_crc_source(struct drm_crtc *crtc,
|
||||
const char *src_name, size_t *values_cnt)
|
||||
{
|
||||
enum dpu_crtc_crc_source source = dpu_crtc_parse_crc_source(src_name);
|
||||
struct dpu_crtc_state *crtc_state = to_dpu_crtc_state(crtc->state);
|
||||
|
||||
if (source < 0) {
|
||||
DRM_DEBUG_DRIVER("Invalid source %s for CRTC%d\n", src_name, crtc->index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return dpu_encoder_get_frame_count(encoder);
|
||||
if (source == DPU_CRTC_CRC_SOURCE_LAYER_MIXER)
|
||||
*values_cnt = crtc_state->num_mixers;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dpu_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
|
||||
{
|
||||
enum dpu_crtc_crc_source source = dpu_crtc_parse_crc_source(src_name);
|
||||
enum dpu_crtc_crc_source current_source;
|
||||
struct dpu_crtc_state *crtc_state;
|
||||
struct drm_device *drm_dev = crtc->dev;
|
||||
struct dpu_crtc_mixer *m;
|
||||
|
||||
bool was_enabled;
|
||||
bool enable = false;
|
||||
int i, ret = 0;
|
||||
|
||||
if (source < 0) {
|
||||
DRM_DEBUG_DRIVER("Invalid CRC source %s for CRTC%d\n", src_name, crtc->index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = drm_modeset_lock(&crtc->mutex, NULL);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
enable = (source != DPU_CRTC_CRC_SOURCE_NONE);
|
||||
crtc_state = to_dpu_crtc_state(crtc->state);
|
||||
|
||||
spin_lock_irq(&drm_dev->event_lock);
|
||||
current_source = crtc_state->crc_source;
|
||||
spin_unlock_irq(&drm_dev->event_lock);
|
||||
|
||||
was_enabled = (current_source != DPU_CRTC_CRC_SOURCE_NONE);
|
||||
|
||||
if (!was_enabled && enable) {
|
||||
ret = drm_crtc_vblank_get(crtc);
|
||||
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
} else if (was_enabled && !enable) {
|
||||
drm_crtc_vblank_put(crtc);
|
||||
}
|
||||
|
||||
spin_lock_irq(&drm_dev->event_lock);
|
||||
crtc_state->crc_source = source;
|
||||
spin_unlock_irq(&drm_dev->event_lock);
|
||||
|
||||
crtc_state->crc_frame_skip_count = 0;
|
||||
|
||||
for (i = 0; i < crtc_state->num_mixers; ++i) {
|
||||
m = &crtc_state->mixers[i];
|
||||
|
||||
if (!m->hw_lm || !m->hw_lm->ops.setup_misr)
|
||||
continue;
|
||||
|
||||
/* Calculate MISR over 1 frame */
|
||||
m->hw_lm->ops.setup_misr(m->hw_lm, true, 1);
|
||||
}
|
||||
|
||||
|
||||
cleanup:
|
||||
drm_modeset_unlock(&crtc->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 dpu_crtc_get_vblank_counter(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_encoder *encoder = get_encoder_from_crtc(crtc);
|
||||
if (!encoder) {
|
||||
DRM_ERROR("no encoder found for crtc %d\n", crtc->index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dpu_encoder_get_vsync_count(encoder);
|
||||
}
|
||||
|
||||
|
||||
static int dpu_crtc_get_crc(struct drm_crtc *crtc)
|
||||
{
|
||||
struct dpu_crtc_state *crtc_state;
|
||||
struct dpu_crtc_mixer *m;
|
||||
u32 crcs[CRTC_DUAL_MIXERS];
|
||||
|
||||
int i = 0;
|
||||
int rc = 0;
|
||||
|
||||
crtc_state = to_dpu_crtc_state(crtc->state);
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(crcs) != ARRAY_SIZE(crtc_state->mixers));
|
||||
|
||||
/* Skip first 2 frames in case of "uncooked" CRCs */
|
||||
if (crtc_state->crc_frame_skip_count < 2) {
|
||||
crtc_state->crc_frame_skip_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < crtc_state->num_mixers; ++i) {
|
||||
|
||||
m = &crtc_state->mixers[i];
|
||||
|
||||
if (!m->hw_lm || !m->hw_lm->ops.collect_misr)
|
||||
continue;
|
||||
|
||||
rc = m->hw_lm->ops.collect_misr(m->hw_lm, &crcs[i]);
|
||||
|
||||
if (rc) {
|
||||
DRM_DEBUG_DRIVER("MISR read failed\n");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return drm_crtc_add_crc_entry(crtc, true,
|
||||
drm_crtc_accurate_vblank_count(crtc), crcs);
|
||||
}
|
||||
|
||||
static bool dpu_crtc_get_scanout_position(struct drm_crtc *crtc,
|
||||
@ -389,6 +519,9 @@ void dpu_crtc_vblank_callback(struct drm_crtc *crtc)
|
||||
dpu_crtc->vblank_cb_time = ktime_get();
|
||||
else
|
||||
dpu_crtc->vblank_cb_count++;
|
||||
|
||||
dpu_crtc_get_crc(crtc);
|
||||
|
||||
drm_crtc_handle_vblank(crtc);
|
||||
trace_dpu_crtc_vblank_cb(DRMID(crtc));
|
||||
}
|
||||
@ -1332,6 +1465,8 @@ static const struct drm_crtc_funcs dpu_crtc_funcs = {
|
||||
.atomic_destroy_state = dpu_crtc_destroy_state,
|
||||
.late_register = dpu_crtc_late_register,
|
||||
.early_unregister = dpu_crtc_early_unregister,
|
||||
.verify_crc_source = dpu_crtc_verify_crc_source,
|
||||
.set_crc_source = dpu_crtc_set_crc_source,
|
||||
.enable_vblank = msm_crtc_enable_vblank,
|
||||
.disable_vblank = msm_crtc_disable_vblank,
|
||||
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2015-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*/
|
||||
@ -69,6 +69,19 @@ struct dpu_crtc_smmu_state_data {
|
||||
uint32_t transition_error;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum dpu_crtc_crc_source: CRC source
|
||||
* @DPU_CRTC_CRC_SOURCE_NONE: no source set
|
||||
* @DPU_CRTC_CRC_SOURCE_LAYER_MIXER: CRC in layer mixer
|
||||
* @DPU_CRTC_CRC_SOURCE_INVALID: Invalid source
|
||||
*/
|
||||
enum dpu_crtc_crc_source {
|
||||
DPU_CRTC_CRC_SOURCE_NONE = 0,
|
||||
DPU_CRTC_CRC_SOURCE_LAYER_MIXER,
|
||||
DPU_CRTC_CRC_SOURCE_MAX,
|
||||
DPU_CRTC_CRC_SOURCE_INVALID = -1
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_crtc_mixer: stores the map for each virtual pipeline in the CRTC
|
||||
* @hw_lm: LM HW Driver context
|
||||
@ -139,6 +152,7 @@ struct dpu_crtc_frame_event {
|
||||
* @event_lock : Spinlock around event handling code
|
||||
* @phandle: Pointer to power handler
|
||||
* @cur_perf : current performance committed to clock/bandwidth driver
|
||||
* @crc_source : CRC source
|
||||
*/
|
||||
struct dpu_crtc {
|
||||
struct drm_crtc base;
|
||||
@ -210,6 +224,9 @@ struct dpu_crtc_state {
|
||||
|
||||
u32 num_ctls;
|
||||
struct dpu_hw_ctl *hw_ctls[CRTC_DUAL_MIXERS];
|
||||
|
||||
enum dpu_crtc_crc_source crc_source;
|
||||
int crc_frame_skip_count;
|
||||
};
|
||||
|
||||
#define to_dpu_crtc_state(x) \
|
||||
|
@ -168,6 +168,7 @@ enum dpu_enc_rc_states {
|
||||
* @vsync_event_work: worker to handle vsync event for autorefresh
|
||||
* @topology: topology of the display
|
||||
* @idle_timeout: idle timeout duration in milliseconds
|
||||
* @dp: msm_dp pointer, for DP encoders
|
||||
*/
|
||||
struct dpu_encoder_virt {
|
||||
struct drm_encoder base;
|
||||
@ -206,6 +207,8 @@ struct dpu_encoder_virt {
|
||||
struct msm_display_topology topology;
|
||||
|
||||
u32 idle_timeout;
|
||||
|
||||
struct msm_dp *dp;
|
||||
};
|
||||
|
||||
#define to_dpu_encoder_virt(x) container_of(x, struct dpu_encoder_virt, base)
|
||||
@ -395,19 +398,11 @@ int dpu_encoder_helper_unregister_irq(struct dpu_encoder_phys *phys_enc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dpu_encoder_get_frame_count(struct drm_encoder *drm_enc)
|
||||
int dpu_encoder_get_vsync_count(struct drm_encoder *drm_enc)
|
||||
{
|
||||
struct dpu_encoder_virt *dpu_enc;
|
||||
struct dpu_encoder_phys *phys;
|
||||
int framecount = 0;
|
||||
|
||||
dpu_enc = to_dpu_encoder_virt(drm_enc);
|
||||
phys = dpu_enc ? dpu_enc->cur_master : NULL;
|
||||
|
||||
if (phys && phys->ops.get_frame_count)
|
||||
framecount = phys->ops.get_frame_count(phys);
|
||||
|
||||
return framecount;
|
||||
struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
|
||||
struct dpu_encoder_phys *phys = dpu_enc ? dpu_enc->cur_master : NULL;
|
||||
return phys ? atomic_read(&phys->vsync_cnt) : 0;
|
||||
}
|
||||
|
||||
int dpu_encoder_get_linecount(struct drm_encoder *drm_enc)
|
||||
@ -1000,8 +995,8 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc,
|
||||
|
||||
trace_dpu_enc_mode_set(DRMID(drm_enc));
|
||||
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp)
|
||||
msm_dp_display_mode_set(priv->dp, drm_enc, mode, adj_mode);
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS)
|
||||
msm_dp_display_mode_set(dpu_enc->dp, drm_enc, mode, adj_mode);
|
||||
|
||||
list_for_each_entry(conn_iter, connector_list, head)
|
||||
if (conn_iter->encoder == drm_enc)
|
||||
@ -1182,9 +1177,8 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
|
||||
|
||||
_dpu_encoder_virt_enable_helper(drm_enc);
|
||||
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) {
|
||||
ret = msm_dp_display_enable(priv->dp,
|
||||
drm_enc);
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS) {
|
||||
ret = msm_dp_display_enable(dpu_enc->dp, drm_enc);
|
||||
if (ret) {
|
||||
DPU_ERROR_ENC(dpu_enc, "dp display enable failed: %d\n",
|
||||
ret);
|
||||
@ -1224,8 +1218,8 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
|
||||
/* wait for idle */
|
||||
dpu_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE);
|
||||
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) {
|
||||
if (msm_dp_display_pre_disable(priv->dp, drm_enc))
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS) {
|
||||
if (msm_dp_display_pre_disable(dpu_enc->dp, drm_enc))
|
||||
DPU_ERROR_ENC(dpu_enc, "dp display push idle failed\n");
|
||||
}
|
||||
|
||||
@ -1253,8 +1247,8 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
|
||||
|
||||
DPU_DEBUG_ENC(dpu_enc, "encoder disabled\n");
|
||||
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) {
|
||||
if (msm_dp_display_disable(priv->dp, drm_enc))
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS) {
|
||||
if (msm_dp_display_disable(dpu_enc->dp, drm_enc))
|
||||
DPU_ERROR_ENC(dpu_enc, "dp display disable failed\n");
|
||||
}
|
||||
|
||||
@ -2170,7 +2164,8 @@ int dpu_encoder_setup(struct drm_device *dev, struct drm_encoder *enc,
|
||||
timer_setup(&dpu_enc->vsync_event_timer,
|
||||
dpu_encoder_vsync_event_handler,
|
||||
0);
|
||||
|
||||
else if (disp_info->intf_type == DRM_MODE_ENCODER_TMDS)
|
||||
dpu_enc->dp = priv->dp[disp_info->h_tile_instance[0]];
|
||||
|
||||
INIT_DELAYED_WORK(&dpu_enc->delayed_off_work,
|
||||
dpu_encoder_off_work);
|
||||
|
@ -163,9 +163,9 @@ void dpu_encoder_set_idle_timeout(struct drm_encoder *drm_enc,
|
||||
int dpu_encoder_get_linecount(struct drm_encoder *drm_enc);
|
||||
|
||||
/**
|
||||
* dpu_encoder_get_frame_count - get interface frame count for the encoder.
|
||||
* dpu_encoder_get_vsync_count - get vsync count for the encoder.
|
||||
* @drm_enc: Pointer to previously created drm encoder structure
|
||||
*/
|
||||
int dpu_encoder_get_frame_count(struct drm_encoder *drm_enc);
|
||||
int dpu_encoder_get_vsync_count(struct drm_encoder *drm_enc);
|
||||
|
||||
#endif /* __DPU_ENCODER_H__ */
|
||||
|
@ -42,7 +42,7 @@
|
||||
|
||||
static bool dpu_encoder_phys_cmd_is_master(struct dpu_encoder_phys *phys_enc)
|
||||
{
|
||||
return (phys_enc->split_role != ENC_ROLE_SLAVE) ? true : false;
|
||||
return (phys_enc->split_role != ENC_ROLE_SLAVE);
|
||||
}
|
||||
|
||||
static bool dpu_encoder_phys_cmd_mode_fixup(
|
||||
|
@ -844,7 +844,7 @@ static const struct dpu_intf_cfg sdm845_intf[] = {
|
||||
};
|
||||
|
||||
static const struct dpu_intf_cfg sc7180_intf[] = {
|
||||
INTF_BLK("intf_0", INTF_0, 0x6A000, INTF_DP, 0, 24, INTF_SC7180_MASK, MDP_SSPP_TOP0_INTR, 24, 25),
|
||||
INTF_BLK("intf_0", INTF_0, 0x6A000, INTF_DP, MSM_DP_CONTROLLER_0, 24, INTF_SC7180_MASK, MDP_SSPP_TOP0_INTR, 24, 25),
|
||||
INTF_BLK("intf_1", INTF_1, 0x6A800, INTF_DSI, 0, 24, INTF_SC7180_MASK, MDP_SSPP_TOP0_INTR, 26, 27),
|
||||
};
|
||||
|
||||
@ -958,12 +958,6 @@ static const struct dpu_perf_cfg sdm845_perf_data = {
|
||||
.min_core_ib = 2400000,
|
||||
.min_llcc_ib = 800000,
|
||||
.min_dram_ib = 800000,
|
||||
.core_ib_ff = "6.0",
|
||||
.core_clk_ff = "1.0",
|
||||
.comp_ratio_rt =
|
||||
"NV12/5/1/1.23 AB24/5/1/1.23 XB24/5/1/1.23",
|
||||
.comp_ratio_nrt =
|
||||
"NV12/5/1/1.25 AB24/5/1/1.25 XB24/5/1/1.25",
|
||||
.undersized_prefill_lines = 2,
|
||||
.xtra_prefill_lines = 2,
|
||||
.dest_scale_prefill_lines = 3,
|
||||
|
@ -676,10 +676,6 @@ struct dpu_perf_cdp_cfg {
|
||||
* @min_core_ib minimum mnoc ib vote in kbps
|
||||
* @min_llcc_ib minimum llcc ib vote in kbps
|
||||
* @min_dram_ib minimum dram ib vote in kbps
|
||||
* @core_ib_ff core instantaneous bandwidth fudge factor
|
||||
* @core_clk_ff core clock fudge factor
|
||||
* @comp_ratio_rt string of 0 or more of <fourcc>/<ven>/<mod>/<comp ratio>
|
||||
* @comp_ratio_nrt string of 0 or more of <fourcc>/<ven>/<mod>/<comp ratio>
|
||||
* @undersized_prefill_lines undersized prefill in lines
|
||||
* @xtra_prefill_lines extra prefill latency in lines
|
||||
* @dest_scale_prefill_lines destination scaler latency in lines
|
||||
@ -702,10 +698,6 @@ struct dpu_perf_cfg {
|
||||
u32 min_core_ib;
|
||||
u32 min_llcc_ib;
|
||||
u32 min_dram_ib;
|
||||
const char *core_ib_ff;
|
||||
const char *core_clk_ff;
|
||||
const char *comp_ratio_rt;
|
||||
const char *comp_ratio_nrt;
|
||||
u32 undersized_prefill_lines;
|
||||
u32 xtra_prefill_lines;
|
||||
u32 dest_scale_prefill_lines;
|
||||
|
@ -3,12 +3,15 @@
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "dpu_core_irq.h"
|
||||
#include "dpu_kms.h"
|
||||
#include "dpu_hw_interrupts.h"
|
||||
#include "dpu_hw_util.h"
|
||||
#include "dpu_hw_mdss.h"
|
||||
#include "dpu_trace.h"
|
||||
|
||||
/**
|
||||
* Register offsets in MDSS register file for the interrupt registers
|
||||
@ -117,25 +120,33 @@ static const struct dpu_intr_reg dpu_intr_set[] = {
|
||||
#define DPU_IRQ_REG(irq_idx) (irq_idx / 32)
|
||||
#define DPU_IRQ_MASK(irq_idx) (BIT(irq_idx % 32))
|
||||
|
||||
static void dpu_hw_intr_clear_intr_status_nolock(struct dpu_hw_intr *intr,
|
||||
int irq_idx)
|
||||
/**
|
||||
* dpu_core_irq_callback_handler - dispatch core interrupts
|
||||
* @arg: private data of callback handler
|
||||
* @irq_idx: interrupt index
|
||||
*/
|
||||
static void dpu_core_irq_callback_handler(struct dpu_kms *dpu_kms, int irq_idx)
|
||||
{
|
||||
int reg_idx;
|
||||
struct dpu_irq_callback *cb;
|
||||
|
||||
if (!intr)
|
||||
return;
|
||||
VERB("irq_idx=%d\n", irq_idx);
|
||||
|
||||
reg_idx = DPU_IRQ_REG(irq_idx);
|
||||
DPU_REG_WRITE(&intr->hw, dpu_intr_set[reg_idx].clr_off, DPU_IRQ_MASK(irq_idx));
|
||||
if (list_empty(&dpu_kms->hw_intr->irq_cb_tbl[irq_idx]))
|
||||
DRM_ERROR("no registered cb, idx:%d\n", irq_idx);
|
||||
|
||||
/* ensure register writes go through */
|
||||
wmb();
|
||||
atomic_inc(&dpu_kms->hw_intr->irq_counts[irq_idx]);
|
||||
|
||||
/*
|
||||
* Perform registered function callback
|
||||
*/
|
||||
list_for_each_entry(cb, &dpu_kms->hw_intr->irq_cb_tbl[irq_idx], list)
|
||||
if (cb->func)
|
||||
cb->func(cb->arg, irq_idx);
|
||||
}
|
||||
|
||||
static void dpu_hw_intr_dispatch_irq(struct dpu_hw_intr *intr,
|
||||
void (*cbfunc)(void *, int),
|
||||
void *arg)
|
||||
irqreturn_t dpu_core_irq(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
struct dpu_hw_intr *intr = dpu_kms->hw_intr;
|
||||
int reg_idx;
|
||||
int irq_idx;
|
||||
u32 irq_status;
|
||||
@ -144,13 +155,8 @@ static void dpu_hw_intr_dispatch_irq(struct dpu_hw_intr *intr,
|
||||
unsigned long irq_flags;
|
||||
|
||||
if (!intr)
|
||||
return;
|
||||
return IRQ_NONE;
|
||||
|
||||
/*
|
||||
* The dispatcher will save the IRQ status before calling here.
|
||||
* Now need to go through each IRQ status and find matching
|
||||
* irq lookup index.
|
||||
*/
|
||||
spin_lock_irqsave(&intr->irq_lock, irq_flags);
|
||||
for (reg_idx = 0; reg_idx < ARRAY_SIZE(dpu_intr_set); reg_idx++) {
|
||||
if (!test_bit(reg_idx, &intr->irq_mask))
|
||||
@ -178,17 +184,8 @@ static void dpu_hw_intr_dispatch_irq(struct dpu_hw_intr *intr,
|
||||
*/
|
||||
while ((bit = ffs(irq_status)) != 0) {
|
||||
irq_idx = DPU_IRQ_IDX(reg_idx, bit - 1);
|
||||
/*
|
||||
* Once a match on irq mask, perform a callback
|
||||
* to the given cbfunc. cbfunc will take care
|
||||
* the interrupt status clearing. If cbfunc is
|
||||
* not provided, then the interrupt clearing
|
||||
* is here.
|
||||
*/
|
||||
if (cbfunc)
|
||||
cbfunc(arg, irq_idx);
|
||||
|
||||
dpu_hw_intr_clear_intr_status_nolock(intr, irq_idx);
|
||||
dpu_core_irq_callback_handler(dpu_kms, irq_idx);
|
||||
|
||||
/*
|
||||
* When callback finish, clear the irq_status
|
||||
@ -203,6 +200,8 @@ static void dpu_hw_intr_dispatch_irq(struct dpu_hw_intr *intr,
|
||||
wmb();
|
||||
|
||||
spin_unlock_irqrestore(&intr->irq_lock, irq_flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dpu_hw_intr_enable_irq_locked(struct dpu_hw_intr *intr, int irq_idx)
|
||||
@ -303,12 +302,13 @@ static int dpu_hw_intr_disable_irq_locked(struct dpu_hw_intr *intr, int irq_idx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dpu_hw_intr_clear_irqs(struct dpu_hw_intr *intr)
|
||||
static void dpu_clear_irqs(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
struct dpu_hw_intr *intr = dpu_kms->hw_intr;
|
||||
int i;
|
||||
|
||||
if (!intr)
|
||||
return -EINVAL;
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dpu_intr_set); i++) {
|
||||
if (test_bit(i, &intr->irq_mask))
|
||||
@ -318,16 +318,15 @@ static int dpu_hw_intr_clear_irqs(struct dpu_hw_intr *intr)
|
||||
|
||||
/* ensure register writes go through */
|
||||
wmb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dpu_hw_intr_disable_irqs(struct dpu_hw_intr *intr)
|
||||
static void dpu_disable_all_irqs(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
struct dpu_hw_intr *intr = dpu_kms->hw_intr;
|
||||
int i;
|
||||
|
||||
if (!intr)
|
||||
return -EINVAL;
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dpu_intr_set); i++) {
|
||||
if (test_bit(i, &intr->irq_mask))
|
||||
@ -337,13 +336,11 @@ static int dpu_hw_intr_disable_irqs(struct dpu_hw_intr *intr)
|
||||
|
||||
/* ensure register writes go through */
|
||||
wmb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 dpu_hw_intr_get_interrupt_status(struct dpu_hw_intr *intr,
|
||||
int irq_idx, bool clear)
|
||||
u32 dpu_core_irq_read(struct dpu_kms *dpu_kms, int irq_idx, bool clear)
|
||||
{
|
||||
struct dpu_hw_intr *intr = dpu_kms->hw_intr;
|
||||
int reg_idx;
|
||||
unsigned long irq_flags;
|
||||
u32 intr_status;
|
||||
@ -351,6 +348,12 @@ static u32 dpu_hw_intr_get_interrupt_status(struct dpu_hw_intr *intr,
|
||||
if (!intr)
|
||||
return 0;
|
||||
|
||||
if (irq_idx < 0) {
|
||||
DPU_ERROR("[%pS] invalid irq_idx=%d\n",
|
||||
__builtin_return_address(0), irq_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (irq_idx < 0 || irq_idx >= intr->total_irqs) {
|
||||
pr_err("invalid IRQ index: [%d]\n", irq_idx);
|
||||
return 0;
|
||||
@ -374,32 +377,6 @@ static u32 dpu_hw_intr_get_interrupt_status(struct dpu_hw_intr *intr,
|
||||
return intr_status;
|
||||
}
|
||||
|
||||
static unsigned long dpu_hw_intr_lock(struct dpu_hw_intr *intr)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
|
||||
spin_lock_irqsave(&intr->irq_lock, irq_flags);
|
||||
|
||||
return irq_flags;
|
||||
}
|
||||
|
||||
static void dpu_hw_intr_unlock(struct dpu_hw_intr *intr, unsigned long irq_flags)
|
||||
{
|
||||
spin_unlock_irqrestore(&intr->irq_lock, irq_flags);
|
||||
}
|
||||
|
||||
static void __setup_intr_ops(struct dpu_hw_intr_ops *ops)
|
||||
{
|
||||
ops->enable_irq_locked = dpu_hw_intr_enable_irq_locked;
|
||||
ops->disable_irq_locked = dpu_hw_intr_disable_irq_locked;
|
||||
ops->dispatch_irqs = dpu_hw_intr_dispatch_irq;
|
||||
ops->clear_all_irqs = dpu_hw_intr_clear_irqs;
|
||||
ops->disable_all_irqs = dpu_hw_intr_disable_irqs;
|
||||
ops->get_interrupt_status = dpu_hw_intr_get_interrupt_status;
|
||||
ops->lock = dpu_hw_intr_lock;
|
||||
ops->unlock = dpu_hw_intr_unlock;
|
||||
}
|
||||
|
||||
static void __intr_offset(struct dpu_mdss_cfg *m,
|
||||
void __iomem *addr, struct dpu_hw_blk_reg_map *hw)
|
||||
{
|
||||
@ -421,7 +398,6 @@ struct dpu_hw_intr *dpu_hw_intr_init(void __iomem *addr,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
__intr_offset(m, addr, &intr->hw);
|
||||
__setup_intr_ops(&intr->ops);
|
||||
|
||||
intr->total_irqs = ARRAY_SIZE(dpu_intr_set) * 32;
|
||||
|
||||
@ -443,7 +419,168 @@ void dpu_hw_intr_destroy(struct dpu_hw_intr *intr)
|
||||
{
|
||||
if (intr) {
|
||||
kfree(intr->cache_irq_mask);
|
||||
|
||||
kfree(intr->irq_cb_tbl);
|
||||
kfree(intr->irq_counts);
|
||||
|
||||
kfree(intr);
|
||||
}
|
||||
}
|
||||
|
||||
int dpu_core_irq_register_callback(struct dpu_kms *dpu_kms, int irq_idx,
|
||||
struct dpu_irq_callback *register_irq_cb)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
|
||||
if (!dpu_kms->hw_intr->irq_cb_tbl) {
|
||||
DPU_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!register_irq_cb || !register_irq_cb->func) {
|
||||
DPU_ERROR("invalid irq_cb:%d func:%d\n",
|
||||
register_irq_cb != NULL,
|
||||
register_irq_cb ?
|
||||
register_irq_cb->func != NULL : -1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (irq_idx < 0 || irq_idx >= dpu_kms->hw_intr->total_irqs) {
|
||||
DPU_ERROR("invalid IRQ index: [%d]\n", irq_idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
VERB("[%pS] irq_idx=%d\n", __builtin_return_address(0), irq_idx);
|
||||
|
||||
spin_lock_irqsave(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
trace_dpu_core_irq_register_callback(irq_idx, register_irq_cb);
|
||||
list_del_init(®ister_irq_cb->list);
|
||||
list_add_tail(®ister_irq_cb->list,
|
||||
&dpu_kms->hw_intr->irq_cb_tbl[irq_idx]);
|
||||
if (list_is_first(®ister_irq_cb->list,
|
||||
&dpu_kms->hw_intr->irq_cb_tbl[irq_idx])) {
|
||||
int ret = dpu_hw_intr_enable_irq_locked(
|
||||
dpu_kms->hw_intr,
|
||||
irq_idx);
|
||||
if (ret)
|
||||
DPU_ERROR("Fail to enable IRQ for irq_idx:%d\n",
|
||||
irq_idx);
|
||||
}
|
||||
spin_unlock_irqrestore(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dpu_core_irq_unregister_callback(struct dpu_kms *dpu_kms, int irq_idx,
|
||||
struct dpu_irq_callback *register_irq_cb)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
|
||||
if (!dpu_kms->hw_intr->irq_cb_tbl) {
|
||||
DPU_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!register_irq_cb || !register_irq_cb->func) {
|
||||
DPU_ERROR("invalid irq_cb:%d func:%d\n",
|
||||
register_irq_cb != NULL,
|
||||
register_irq_cb ?
|
||||
register_irq_cb->func != NULL : -1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (irq_idx < 0 || irq_idx >= dpu_kms->hw_intr->total_irqs) {
|
||||
DPU_ERROR("invalid IRQ index: [%d]\n", irq_idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
VERB("[%pS] irq_idx=%d\n", __builtin_return_address(0), irq_idx);
|
||||
|
||||
spin_lock_irqsave(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
trace_dpu_core_irq_unregister_callback(irq_idx, register_irq_cb);
|
||||
list_del_init(®ister_irq_cb->list);
|
||||
/* empty callback list but interrupt is still enabled */
|
||||
if (list_empty(&dpu_kms->hw_intr->irq_cb_tbl[irq_idx])) {
|
||||
int ret = dpu_hw_intr_disable_irq_locked(
|
||||
dpu_kms->hw_intr,
|
||||
irq_idx);
|
||||
if (ret)
|
||||
DPU_ERROR("Fail to disable IRQ for irq_idx:%d\n",
|
||||
irq_idx);
|
||||
VERB("irq_idx=%d ret=%d\n", irq_idx, ret);
|
||||
}
|
||||
spin_unlock_irqrestore(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int dpu_debugfs_core_irq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct dpu_kms *dpu_kms = s->private;
|
||||
struct dpu_irq_callback *cb;
|
||||
unsigned long irq_flags;
|
||||
int i, irq_count, cb_count;
|
||||
|
||||
if (WARN_ON(!dpu_kms->hw_intr->irq_cb_tbl))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < dpu_kms->hw_intr->total_irqs; i++) {
|
||||
spin_lock_irqsave(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
cb_count = 0;
|
||||
irq_count = atomic_read(&dpu_kms->hw_intr->irq_counts[i]);
|
||||
list_for_each_entry(cb, &dpu_kms->hw_intr->irq_cb_tbl[i], list)
|
||||
cb_count++;
|
||||
spin_unlock_irqrestore(&dpu_kms->hw_intr->irq_lock, irq_flags);
|
||||
|
||||
if (irq_count || cb_count)
|
||||
seq_printf(s, "idx:%d irq:%d cb:%d\n",
|
||||
i, irq_count, cb_count);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(dpu_debugfs_core_irq);
|
||||
|
||||
void dpu_debugfs_core_irq_init(struct dpu_kms *dpu_kms,
|
||||
struct dentry *parent)
|
||||
{
|
||||
debugfs_create_file("core_irq", 0600, parent, dpu_kms,
|
||||
&dpu_debugfs_core_irq_fops);
|
||||
}
|
||||
#endif
|
||||
|
||||
void dpu_core_irq_preinstall(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
int i;
|
||||
|
||||
pm_runtime_get_sync(&dpu_kms->pdev->dev);
|
||||
dpu_clear_irqs(dpu_kms);
|
||||
dpu_disable_all_irqs(dpu_kms);
|
||||
pm_runtime_put_sync(&dpu_kms->pdev->dev);
|
||||
|
||||
/* Create irq callbacks for all possible irq_idx */
|
||||
dpu_kms->hw_intr->irq_cb_tbl = kcalloc(dpu_kms->hw_intr->total_irqs,
|
||||
sizeof(struct list_head), GFP_KERNEL);
|
||||
dpu_kms->hw_intr->irq_counts = kcalloc(dpu_kms->hw_intr->total_irqs,
|
||||
sizeof(atomic_t), GFP_KERNEL);
|
||||
for (i = 0; i < dpu_kms->hw_intr->total_irqs; i++) {
|
||||
INIT_LIST_HEAD(&dpu_kms->hw_intr->irq_cb_tbl[i]);
|
||||
atomic_set(&dpu_kms->hw_intr->irq_counts[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
void dpu_core_irq_uninstall(struct dpu_kms *dpu_kms)
|
||||
{
|
||||
int i;
|
||||
|
||||
pm_runtime_get_sync(&dpu_kms->pdev->dev);
|
||||
for (i = 0; i < dpu_kms->hw_intr->total_irqs; i++)
|
||||
if (!list_empty(&dpu_kms->hw_intr->irq_cb_tbl[i]))
|
||||
DPU_ERROR("irq_idx=%d still enabled/registered\n", i);
|
||||
|
||||
dpu_clear_irqs(dpu_kms);
|
||||
dpu_disable_all_irqs(dpu_kms);
|
||||
pm_runtime_put_sync(&dpu_kms->pdev->dev);
|
||||
}
|
||||
|
@ -32,92 +32,6 @@ enum dpu_hw_intr_reg {
|
||||
|
||||
#define DPU_IRQ_IDX(reg_idx, offset) (reg_idx * 32 + offset)
|
||||
|
||||
struct dpu_hw_intr;
|
||||
|
||||
/**
|
||||
* Interrupt operations.
|
||||
*/
|
||||
struct dpu_hw_intr_ops {
|
||||
|
||||
/**
|
||||
* enable_irq - Enable IRQ based on lookup IRQ index
|
||||
* @intr: HW interrupt handle
|
||||
* @irq_idx: Lookup irq index return from irq_idx_lookup
|
||||
* @return: 0 for success, otherwise failure
|
||||
*/
|
||||
int (*enable_irq_locked)(
|
||||
struct dpu_hw_intr *intr,
|
||||
int irq_idx);
|
||||
|
||||
/**
|
||||
* disable_irq - Disable IRQ based on lookup IRQ index
|
||||
* @intr: HW interrupt handle
|
||||
* @irq_idx: Lookup irq index return from irq_idx_lookup
|
||||
* @return: 0 for success, otherwise failure
|
||||
*/
|
||||
int (*disable_irq_locked)(
|
||||
struct dpu_hw_intr *intr,
|
||||
int irq_idx);
|
||||
|
||||
/**
|
||||
* clear_all_irqs - Clears all the interrupts (i.e. acknowledges
|
||||
* any asserted IRQs). Useful during reset.
|
||||
* @intr: HW interrupt handle
|
||||
* @return: 0 for success, otherwise failure
|
||||
*/
|
||||
int (*clear_all_irqs)(
|
||||
struct dpu_hw_intr *intr);
|
||||
|
||||
/**
|
||||
* disable_all_irqs - Disables all the interrupts. Useful during reset.
|
||||
* @intr: HW interrupt handle
|
||||
* @return: 0 for success, otherwise failure
|
||||
*/
|
||||
int (*disable_all_irqs)(
|
||||
struct dpu_hw_intr *intr);
|
||||
|
||||
/**
|
||||
* dispatch_irqs - IRQ dispatcher will call the given callback
|
||||
* function when a matching interrupt status bit is
|
||||
* found in the irq mapping table.
|
||||
* @intr: HW interrupt handle
|
||||
* @cbfunc: Callback function pointer
|
||||
* @arg: Argument to pass back during callback
|
||||
*/
|
||||
void (*dispatch_irqs)(
|
||||
struct dpu_hw_intr *intr,
|
||||
void (*cbfunc)(void *arg, int irq_idx),
|
||||
void *arg);
|
||||
|
||||
/**
|
||||
* get_interrupt_status - Gets HW interrupt status, and clear if set,
|
||||
* based on given lookup IRQ index.
|
||||
* @intr: HW interrupt handle
|
||||
* @irq_idx: Lookup irq index return from irq_idx_lookup
|
||||
* @clear: True to clear irq after read
|
||||
*/
|
||||
u32 (*get_interrupt_status)(
|
||||
struct dpu_hw_intr *intr,
|
||||
int irq_idx,
|
||||
bool clear);
|
||||
|
||||
/**
|
||||
* lock - take the IRQ lock
|
||||
* @intr: HW interrupt handle
|
||||
* @return: irq_flags for the taken spinlock
|
||||
*/
|
||||
unsigned long (*lock)(
|
||||
struct dpu_hw_intr *intr);
|
||||
|
||||
/**
|
||||
* unlock - take the IRQ lock
|
||||
* @intr: HW interrupt handle
|
||||
* @irq_flags: the irq_flags returned from lock
|
||||
*/
|
||||
void (*unlock)(
|
||||
struct dpu_hw_intr *intr, unsigned long irq_flags);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_hw_intr: hw interrupts handling data structure
|
||||
* @hw: virtual address mapping
|
||||
@ -126,15 +40,19 @@ struct dpu_hw_intr_ops {
|
||||
* @save_irq_status: array of IRQ status reg storage created during init
|
||||
* @total_irqs: total number of irq_idx mapped in the hw_interrupts
|
||||
* @irq_lock: spinlock for accessing IRQ resources
|
||||
* @irq_cb_tbl: array of IRQ callbacks lists
|
||||
* @irq_counts: array of IRQ counts
|
||||
*/
|
||||
struct dpu_hw_intr {
|
||||
struct dpu_hw_blk_reg_map hw;
|
||||
struct dpu_hw_intr_ops ops;
|
||||
u32 *cache_irq_mask;
|
||||
u32 *save_irq_status;
|
||||
u32 total_irqs;
|
||||
spinlock_t irq_lock;
|
||||
unsigned long irq_mask;
|
||||
|
||||
struct list_head *irq_cb_tbl;
|
||||
atomic_t *irq_counts;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
|
||||
/*
|
||||
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "dpu_kms.h"
|
||||
@ -24,6 +25,15 @@
|
||||
#define LM_BLEND0_FG_ALPHA 0x04
|
||||
#define LM_BLEND0_BG_ALPHA 0x08
|
||||
|
||||
#define LM_MISR_CTRL 0x310
|
||||
#define LM_MISR_SIGNATURE 0x314
|
||||
#define LM_MISR_FRAME_COUNT_MASK 0xFF
|
||||
#define LM_MISR_CTRL_ENABLE BIT(8)
|
||||
#define LM_MISR_CTRL_STATUS BIT(9)
|
||||
#define LM_MISR_CTRL_STATUS_CLEAR BIT(10)
|
||||
#define LM_MISR_CTRL_FREE_RUN_MASK BIT(31)
|
||||
|
||||
|
||||
static const struct dpu_lm_cfg *_lm_offset(enum dpu_lm mixer,
|
||||
const struct dpu_mdss_cfg *m,
|
||||
void __iomem *addr,
|
||||
@ -96,6 +106,48 @@ static void dpu_hw_lm_setup_border_color(struct dpu_hw_mixer *ctx,
|
||||
}
|
||||
}
|
||||
|
||||
static void dpu_hw_lm_setup_misr(struct dpu_hw_mixer *ctx, bool enable, u32 frame_count)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &ctx->hw;
|
||||
u32 config = 0;
|
||||
|
||||
DPU_REG_WRITE(c, LM_MISR_CTRL, LM_MISR_CTRL_STATUS_CLEAR);
|
||||
|
||||
/* Clear old MISR value (in case it's read before a new value is calculated)*/
|
||||
wmb();
|
||||
|
||||
if (enable) {
|
||||
config = (frame_count & LM_MISR_FRAME_COUNT_MASK) |
|
||||
LM_MISR_CTRL_ENABLE | LM_MISR_CTRL_FREE_RUN_MASK;
|
||||
|
||||
DPU_REG_WRITE(c, LM_MISR_CTRL, config);
|
||||
} else {
|
||||
DPU_REG_WRITE(c, LM_MISR_CTRL, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int dpu_hw_lm_collect_misr(struct dpu_hw_mixer *ctx, u32 *misr_value)
|
||||
{
|
||||
struct dpu_hw_blk_reg_map *c = &ctx->hw;
|
||||
u32 ctrl = 0;
|
||||
|
||||
if (!misr_value)
|
||||
return -EINVAL;
|
||||
|
||||
ctrl = DPU_REG_READ(c, LM_MISR_CTRL);
|
||||
|
||||
if (!(ctrl & LM_MISR_CTRL_ENABLE))
|
||||
return -EINVAL;
|
||||
|
||||
if (!(ctrl & LM_MISR_CTRL_STATUS))
|
||||
return -EINVAL;
|
||||
|
||||
*misr_value = DPU_REG_READ(c, LM_MISR_SIGNATURE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dpu_hw_lm_setup_blend_config_sdm845(struct dpu_hw_mixer *ctx,
|
||||
u32 stage, u32 fg_alpha, u32 bg_alpha, u32 blend_op)
|
||||
{
|
||||
@ -158,6 +210,8 @@ static void _setup_mixer_ops(const struct dpu_mdss_cfg *m,
|
||||
ops->setup_blend_config = dpu_hw_lm_setup_blend_config;
|
||||
ops->setup_alpha_out = dpu_hw_lm_setup_color3;
|
||||
ops->setup_border_color = dpu_hw_lm_setup_border_color;
|
||||
ops->setup_misr = dpu_hw_lm_setup_misr;
|
||||
ops->collect_misr = dpu_hw_lm_collect_misr;
|
||||
}
|
||||
|
||||
struct dpu_hw_mixer *dpu_hw_lm_init(enum dpu_lm idx,
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
|
||||
/*
|
||||
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DPU_HW_LM_H
|
||||
@ -53,6 +54,16 @@ struct dpu_hw_lm_ops {
|
||||
void (*setup_border_color)(struct dpu_hw_mixer *ctx,
|
||||
struct dpu_mdss_color *color,
|
||||
u8 border_en);
|
||||
|
||||
/**
|
||||
* setup_misr: Enable/disable MISR
|
||||
*/
|
||||
void (*setup_misr)(struct dpu_hw_mixer *ctx, bool enable, u32 frame_count);
|
||||
|
||||
/**
|
||||
* collect_misr: Read MISR signature
|
||||
*/
|
||||
int (*collect_misr)(struct dpu_hw_mixer *ctx, u32 *misr_value);
|
||||
};
|
||||
|
||||
struct dpu_hw_mixer {
|
||||
|
@ -138,11 +138,13 @@ static int _sspp_subblk_offset(struct dpu_hw_pipe *ctx,
|
||||
u32 *idx)
|
||||
{
|
||||
int rc = 0;
|
||||
const struct dpu_sspp_sub_blks *sblk = ctx->cap->sblk;
|
||||
const struct dpu_sspp_sub_blks *sblk;
|
||||
|
||||
if (!ctx)
|
||||
if (!ctx || !ctx->cap || !ctx->cap->sblk)
|
||||
return -EINVAL;
|
||||
|
||||
sblk = ctx->cap->sblk;
|
||||
|
||||
switch (s_id) {
|
||||
case DPU_SSPP_SRC:
|
||||
*idx = sblk->src_blk.base;
|
||||
@ -419,7 +421,7 @@ static void _dpu_hw_sspp_setup_scaler3(struct dpu_hw_pipe *ctx,
|
||||
|
||||
(void)pe;
|
||||
if (_sspp_subblk_offset(ctx, DPU_SSPP_SCALER_QSEED3, &idx) || !sspp
|
||||
|| !scaler3_cfg || !ctx || !ctx->cap || !ctx->cap->sblk)
|
||||
|| !scaler3_cfg)
|
||||
return;
|
||||
|
||||
dpu_hw_setup_scaler3(&ctx->hw, scaler3_cfg, idx,
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
|
||||
/*
|
||||
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DPU_HW_UTIL_H
|
||||
|
@ -188,6 +188,7 @@ static int dpu_kms_debugfs_init(struct msm_kms *kms, struct drm_minor *minor)
|
||||
struct dentry *entry;
|
||||
struct drm_device *dev;
|
||||
struct msm_drm_private *priv;
|
||||
int i;
|
||||
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
@ -203,8 +204,10 @@ static int dpu_kms_debugfs_init(struct msm_kms *kms, struct drm_minor *minor)
|
||||
dpu_debugfs_vbif_init(dpu_kms, entry);
|
||||
dpu_debugfs_core_irq_init(dpu_kms, entry);
|
||||
|
||||
if (priv->dp)
|
||||
msm_dp_debugfs_init(priv->dp, minor);
|
||||
for (i = 0; i < ARRAY_SIZE(priv->dp); i++) {
|
||||
if (priv->dp[i])
|
||||
msm_dp_debugfs_init(priv->dp[i], minor);
|
||||
}
|
||||
|
||||
return dpu_core_perf_debugfs_init(dpu_kms, entry);
|
||||
}
|
||||
@ -544,35 +547,42 @@ static int _dpu_kms_initialize_displayport(struct drm_device *dev,
|
||||
{
|
||||
struct drm_encoder *encoder = NULL;
|
||||
struct msm_display_info info;
|
||||
int rc = 0;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
if (!priv->dp)
|
||||
return rc;
|
||||
for (i = 0; i < ARRAY_SIZE(priv->dp); i++) {
|
||||
if (!priv->dp[i])
|
||||
continue;
|
||||
|
||||
encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_TMDS);
|
||||
if (IS_ERR(encoder)) {
|
||||
DPU_ERROR("encoder init failed for dsi display\n");
|
||||
return PTR_ERR(encoder);
|
||||
encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_TMDS);
|
||||
if (IS_ERR(encoder)) {
|
||||
DPU_ERROR("encoder init failed for dsi display\n");
|
||||
return PTR_ERR(encoder);
|
||||
}
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
rc = msm_dp_modeset_init(priv->dp[i], dev, encoder);
|
||||
if (rc) {
|
||||
DPU_ERROR("modeset_init failed for DP, rc = %d\n", rc);
|
||||
drm_encoder_cleanup(encoder);
|
||||
return rc;
|
||||
}
|
||||
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
|
||||
info.num_of_h_tiles = 1;
|
||||
info.h_tile_instance[0] = i;
|
||||
info.capabilities = MSM_DISPLAY_CAP_VID_MODE;
|
||||
info.intf_type = encoder->encoder_type;
|
||||
rc = dpu_encoder_setup(dev, encoder, &info);
|
||||
if (rc) {
|
||||
DPU_ERROR("failed to setup DPU encoder %d: rc:%d\n",
|
||||
encoder->base.id, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
rc = msm_dp_modeset_init(priv->dp, dev, encoder);
|
||||
if (rc) {
|
||||
DPU_ERROR("modeset_init failed for DP, rc = %d\n", rc);
|
||||
drm_encoder_cleanup(encoder);
|
||||
return rc;
|
||||
}
|
||||
|
||||
priv->encoders[priv->num_encoders++] = encoder;
|
||||
|
||||
info.num_of_h_tiles = 1;
|
||||
info.capabilities = MSM_DISPLAY_CAP_VID_MODE;
|
||||
info.intf_type = encoder->encoder_type;
|
||||
rc = dpu_encoder_setup(dev, encoder, &info);
|
||||
if (rc)
|
||||
DPU_ERROR("failed to setup DPU encoder %d: rc:%d\n",
|
||||
encoder->base.id, rc);
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -792,6 +802,7 @@ static int dpu_irq_postinstall(struct msm_kms *kms)
|
||||
{
|
||||
struct msm_drm_private *priv;
|
||||
struct dpu_kms *dpu_kms = to_dpu_kms(kms);
|
||||
int i;
|
||||
|
||||
if (!dpu_kms || !dpu_kms->dev)
|
||||
return -EINVAL;
|
||||
@ -800,7 +811,8 @@ static int dpu_irq_postinstall(struct msm_kms *kms)
|
||||
if (!priv)
|
||||
return -EINVAL;
|
||||
|
||||
msm_dp_irq_postinstall(priv->dp);
|
||||
for (i = 0; i < ARRAY_SIZE(priv->dp); i++)
|
||||
msm_dp_irq_postinstall(priv->dp[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -908,6 +920,10 @@ static int _dpu_kms_mmu_init(struct dpu_kms *dpu_kms)
|
||||
return 0;
|
||||
|
||||
mmu = msm_iommu_new(dpu_kms->dev->dev, domain);
|
||||
if (IS_ERR(mmu)) {
|
||||
iommu_domain_free(domain);
|
||||
return PTR_ERR(mmu);
|
||||
}
|
||||
aspace = msm_gem_address_space_create(mmu, "dpu1",
|
||||
0x1000, 0x100000000 - 0x1000);
|
||||
|
||||
|
@ -78,18 +78,6 @@ struct dpu_irq_callback {
|
||||
void *arg;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dpu_irq: IRQ structure contains callback registration info
|
||||
* @total_irq: total number of irq_idx obtained from HW interrupts mapping
|
||||
* @irq_cb_tbl: array of IRQ callbacks setting
|
||||
* @debugfs_file: debugfs file for irq statistics
|
||||
*/
|
||||
struct dpu_irq {
|
||||
u32 total_irqs;
|
||||
struct list_head *irq_cb_tbl;
|
||||
atomic_t *irq_counts;
|
||||
};
|
||||
|
||||
struct dpu_kms {
|
||||
struct msm_kms base;
|
||||
struct drm_device *dev;
|
||||
@ -104,7 +92,6 @@ struct dpu_kms {
|
||||
struct regulator *venus;
|
||||
|
||||
struct dpu_hw_intr *hw_intr;
|
||||
struct dpu_irq irq_obj;
|
||||
|
||||
struct dpu_core_perf perf;
|
||||
|
||||
|
@ -1193,7 +1193,7 @@ static void dpu_plane_sspp_atomic_update(struct drm_plane *plane)
|
||||
if (DPU_FORMAT_IS_YUV(fmt))
|
||||
_dpu_plane_setup_csc(pdpu);
|
||||
else
|
||||
pdpu->csc_ptr = 0;
|
||||
pdpu->csc_ptr = NULL;
|
||||
}
|
||||
|
||||
_dpu_plane_set_qos_lut(plane, fb);
|
||||
@ -1330,7 +1330,7 @@ static void dpu_plane_reset(struct drm_plane *plane)
|
||||
/* remove previous state, if present */
|
||||
if (plane->state) {
|
||||
dpu_plane_destroy_state(plane, plane->state);
|
||||
plane->state = 0;
|
||||
plane->state = NULL;
|
||||
}
|
||||
|
||||
pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
|
||||
|
@ -173,12 +173,9 @@ int mdp4_disable(struct mdp4_kms *mdp4_kms)
|
||||
DBG("");
|
||||
|
||||
clk_disable_unprepare(mdp4_kms->clk);
|
||||
if (mdp4_kms->pclk)
|
||||
clk_disable_unprepare(mdp4_kms->pclk);
|
||||
if (mdp4_kms->lut_clk)
|
||||
clk_disable_unprepare(mdp4_kms->lut_clk);
|
||||
if (mdp4_kms->axi_clk)
|
||||
clk_disable_unprepare(mdp4_kms->axi_clk);
|
||||
clk_disable_unprepare(mdp4_kms->pclk);
|
||||
clk_disable_unprepare(mdp4_kms->lut_clk);
|
||||
clk_disable_unprepare(mdp4_kms->axi_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -188,12 +185,9 @@ int mdp4_enable(struct mdp4_kms *mdp4_kms)
|
||||
DBG("");
|
||||
|
||||
clk_prepare_enable(mdp4_kms->clk);
|
||||
if (mdp4_kms->pclk)
|
||||
clk_prepare_enable(mdp4_kms->pclk);
|
||||
if (mdp4_kms->lut_clk)
|
||||
clk_prepare_enable(mdp4_kms->lut_clk);
|
||||
if (mdp4_kms->axi_clk)
|
||||
clk_prepare_enable(mdp4_kms->axi_clk);
|
||||
clk_prepare_enable(mdp4_kms->pclk);
|
||||
clk_prepare_enable(mdp4_kms->lut_clk);
|
||||
clk_prepare_enable(mdp4_kms->axi_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -752,6 +752,94 @@ const struct mdp5_cfg_hw msm8x76_config = {
|
||||
.max_clk = 360000000,
|
||||
};
|
||||
|
||||
static const struct mdp5_cfg_hw msm8x53_config = {
|
||||
.name = "msm8x53",
|
||||
.mdp = {
|
||||
.count = 1,
|
||||
.caps = MDP_CAP_CDM |
|
||||
MDP_CAP_SRC_SPLIT,
|
||||
},
|
||||
.ctl = {
|
||||
.count = 3,
|
||||
.base = { 0x01000, 0x01200, 0x01400 },
|
||||
.flush_hw_mask = 0xffffffff,
|
||||
},
|
||||
.pipe_vig = {
|
||||
.count = 1,
|
||||
.base = { 0x04000 },
|
||||
.caps = MDP_PIPE_CAP_HFLIP |
|
||||
MDP_PIPE_CAP_VFLIP |
|
||||
MDP_PIPE_CAP_SCALE |
|
||||
MDP_PIPE_CAP_CSC |
|
||||
MDP_PIPE_CAP_DECIMATION |
|
||||
MDP_PIPE_CAP_SW_PIX_EXT |
|
||||
0,
|
||||
},
|
||||
.pipe_rgb = {
|
||||
.count = 2,
|
||||
.base = { 0x14000, 0x16000 },
|
||||
.caps = MDP_PIPE_CAP_HFLIP |
|
||||
MDP_PIPE_CAP_VFLIP |
|
||||
MDP_PIPE_CAP_DECIMATION |
|
||||
MDP_PIPE_CAP_SW_PIX_EXT |
|
||||
0,
|
||||
},
|
||||
.pipe_dma = {
|
||||
.count = 1,
|
||||
.base = { 0x24000 },
|
||||
.caps = MDP_PIPE_CAP_HFLIP |
|
||||
MDP_PIPE_CAP_VFLIP |
|
||||
MDP_PIPE_CAP_SW_PIX_EXT |
|
||||
0,
|
||||
},
|
||||
.pipe_cursor = {
|
||||
.count = 1,
|
||||
.base = { 0x34000 },
|
||||
.caps = MDP_PIPE_CAP_HFLIP |
|
||||
MDP_PIPE_CAP_VFLIP |
|
||||
MDP_PIPE_CAP_SW_PIX_EXT |
|
||||
MDP_PIPE_CAP_CURSOR |
|
||||
0,
|
||||
},
|
||||
|
||||
.lm = {
|
||||
.count = 3,
|
||||
.base = { 0x44000, 0x45000 },
|
||||
.instances = {
|
||||
{ .id = 0, .pp = 0, .dspp = 0,
|
||||
.caps = MDP_LM_CAP_DISPLAY |
|
||||
MDP_LM_CAP_PAIR },
|
||||
{ .id = 1, .pp = 1, .dspp = -1,
|
||||
.caps = MDP_LM_CAP_DISPLAY },
|
||||
},
|
||||
.nb_stages = 5,
|
||||
.max_width = 2048,
|
||||
.max_height = 0xFFFF,
|
||||
},
|
||||
.dspp = {
|
||||
.count = 1,
|
||||
.base = { 0x54000 },
|
||||
|
||||
},
|
||||
.pp = {
|
||||
.count = 2,
|
||||
.base = { 0x70000, 0x70800 },
|
||||
},
|
||||
.cdm = {
|
||||
.count = 1,
|
||||
.base = { 0x79200 },
|
||||
},
|
||||
.intf = {
|
||||
.base = { 0x6a000, 0x6a800, 0x6b000 },
|
||||
.connect = {
|
||||
[0] = INTF_DISABLED,
|
||||
[1] = INTF_DSI,
|
||||
[2] = INTF_DSI,
|
||||
},
|
||||
},
|
||||
.max_clk = 400000000,
|
||||
};
|
||||
|
||||
static const struct mdp5_cfg_hw msm8917_config = {
|
||||
.name = "msm8917",
|
||||
.mdp = {
|
||||
@ -1151,6 +1239,7 @@ static const struct mdp5_cfg_handler cfg_handlers_v1[] = {
|
||||
{ .revision = 7, .config = { .hw = &msm8x96_config } },
|
||||
{ .revision = 11, .config = { .hw = &msm8x76_config } },
|
||||
{ .revision = 15, .config = { .hw = &msm8917_config } },
|
||||
{ .revision = 16, .config = { .hw = &msm8x53_config } },
|
||||
};
|
||||
|
||||
static const struct mdp5_cfg_handler cfg_handlers_v3[] = {
|
||||
|
@ -295,15 +295,12 @@ static int mdp5_disable(struct mdp5_kms *mdp5_kms)
|
||||
mdp5_kms->enable_count--;
|
||||
WARN_ON(mdp5_kms->enable_count < 0);
|
||||
|
||||
if (mdp5_kms->tbu_rt_clk)
|
||||
clk_disable_unprepare(mdp5_kms->tbu_rt_clk);
|
||||
if (mdp5_kms->tbu_clk)
|
||||
clk_disable_unprepare(mdp5_kms->tbu_clk);
|
||||
clk_disable_unprepare(mdp5_kms->tbu_rt_clk);
|
||||
clk_disable_unprepare(mdp5_kms->tbu_clk);
|
||||
clk_disable_unprepare(mdp5_kms->ahb_clk);
|
||||
clk_disable_unprepare(mdp5_kms->axi_clk);
|
||||
clk_disable_unprepare(mdp5_kms->core_clk);
|
||||
if (mdp5_kms->lut_clk)
|
||||
clk_disable_unprepare(mdp5_kms->lut_clk);
|
||||
clk_disable_unprepare(mdp5_kms->lut_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -317,12 +314,9 @@ static int mdp5_enable(struct mdp5_kms *mdp5_kms)
|
||||
clk_prepare_enable(mdp5_kms->ahb_clk);
|
||||
clk_prepare_enable(mdp5_kms->axi_clk);
|
||||
clk_prepare_enable(mdp5_kms->core_clk);
|
||||
if (mdp5_kms->lut_clk)
|
||||
clk_prepare_enable(mdp5_kms->lut_clk);
|
||||
if (mdp5_kms->tbu_clk)
|
||||
clk_prepare_enable(mdp5_kms->tbu_clk);
|
||||
if (mdp5_kms->tbu_rt_clk)
|
||||
clk_prepare_enable(mdp5_kms->tbu_rt_clk);
|
||||
clk_prepare_enable(mdp5_kms->lut_clk);
|
||||
clk_prepare_enable(mdp5_kms->tbu_clk);
|
||||
clk_prepare_enable(mdp5_kms->tbu_rt_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -136,10 +136,8 @@ static int mdp5_mdss_enable(struct msm_mdss *mdss)
|
||||
DBG("");
|
||||
|
||||
clk_prepare_enable(mdp5_mdss->ahb_clk);
|
||||
if (mdp5_mdss->axi_clk)
|
||||
clk_prepare_enable(mdp5_mdss->axi_clk);
|
||||
if (mdp5_mdss->vsync_clk)
|
||||
clk_prepare_enable(mdp5_mdss->vsync_clk);
|
||||
clk_prepare_enable(mdp5_mdss->axi_clk);
|
||||
clk_prepare_enable(mdp5_mdss->vsync_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -149,10 +147,8 @@ static int mdp5_mdss_disable(struct msm_mdss *mdss)
|
||||
struct mdp5_mdss *mdp5_mdss = to_mdp5_mdss(mdss);
|
||||
DBG("");
|
||||
|
||||
if (mdp5_mdss->vsync_clk)
|
||||
clk_disable_unprepare(mdp5_mdss->vsync_clk);
|
||||
if (mdp5_mdss->axi_clk)
|
||||
clk_disable_unprepare(mdp5_mdss->axi_clk);
|
||||
clk_disable_unprepare(mdp5_mdss->vsync_clk);
|
||||
clk_disable_unprepare(mdp5_mdss->axi_clk);
|
||||
clk_disable_unprepare(mdp5_mdss->ahb_clk);
|
||||
|
||||
return 0;
|
||||
|
@ -126,8 +126,12 @@ void msm_disp_snapshot_capture_state(struct msm_disp_state *disp_state)
|
||||
priv = drm_dev->dev_private;
|
||||
kms = priv->kms;
|
||||
|
||||
if (priv->dp)
|
||||
msm_dp_snapshot(disp_state, priv->dp);
|
||||
for (i = 0; i < ARRAY_SIZE(priv->dp); i++) {
|
||||
if (!priv->dp[i])
|
||||
continue;
|
||||
|
||||
msm_dp_snapshot(disp_state, priv->dp[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->dsi); i++) {
|
||||
if (!priv->dsi[i])
|
||||
|
@ -24,15 +24,6 @@
|
||||
#define DP_INTERRUPT_STATUS_ACK_SHIFT 1
|
||||
#define DP_INTERRUPT_STATUS_MASK_SHIFT 2
|
||||
|
||||
#define MSM_DP_CONTROLLER_AHB_OFFSET 0x0000
|
||||
#define MSM_DP_CONTROLLER_AHB_SIZE 0x0200
|
||||
#define MSM_DP_CONTROLLER_AUX_OFFSET 0x0200
|
||||
#define MSM_DP_CONTROLLER_AUX_SIZE 0x0200
|
||||
#define MSM_DP_CONTROLLER_LINK_OFFSET 0x0400
|
||||
#define MSM_DP_CONTROLLER_LINK_SIZE 0x0C00
|
||||
#define MSM_DP_CONTROLLER_P0_OFFSET 0x1000
|
||||
#define MSM_DP_CONTROLLER_P0_SIZE 0x0400
|
||||
|
||||
#define DP_INTERRUPT_STATUS1 \
|
||||
(DP_INTR_AUX_I2C_DONE| \
|
||||
DP_INTR_WRONG_ADDR | DP_INTR_TIMEOUT | \
|
||||
@ -66,82 +57,77 @@ void dp_catalog_snapshot(struct dp_catalog *dp_catalog, struct msm_disp_state *d
|
||||
{
|
||||
struct dp_catalog_private *catalog = container_of(dp_catalog,
|
||||
struct dp_catalog_private, dp_catalog);
|
||||
struct dss_io_data *dss = &catalog->io->dp_controller;
|
||||
|
||||
msm_disp_snapshot_add_block(disp_state, catalog->io->dp_controller.len,
|
||||
catalog->io->dp_controller.base, "dp_ctrl");
|
||||
msm_disp_snapshot_add_block(disp_state, dss->ahb.len, dss->ahb.base, "dp_ahb");
|
||||
msm_disp_snapshot_add_block(disp_state, dss->aux.len, dss->aux.base, "dp_aux");
|
||||
msm_disp_snapshot_add_block(disp_state, dss->link.len, dss->link.base, "dp_link");
|
||||
msm_disp_snapshot_add_block(disp_state, dss->p0.len, dss->p0.base, "dp_p0");
|
||||
}
|
||||
|
||||
static inline u32 dp_read_aux(struct dp_catalog_private *catalog, u32 offset)
|
||||
{
|
||||
offset += MSM_DP_CONTROLLER_AUX_OFFSET;
|
||||
return readl_relaxed(catalog->io->dp_controller.base + offset);
|
||||
return readl_relaxed(catalog->io->dp_controller.aux.base + offset);
|
||||
}
|
||||
|
||||
static inline void dp_write_aux(struct dp_catalog_private *catalog,
|
||||
u32 offset, u32 data)
|
||||
{
|
||||
offset += MSM_DP_CONTROLLER_AUX_OFFSET;
|
||||
/*
|
||||
* To make sure aux reg writes happens before any other operation,
|
||||
* this function uses writel() instread of writel_relaxed()
|
||||
*/
|
||||
writel(data, catalog->io->dp_controller.base + offset);
|
||||
writel(data, catalog->io->dp_controller.aux.base + offset);
|
||||
}
|
||||
|
||||
static inline u32 dp_read_ahb(struct dp_catalog_private *catalog, u32 offset)
|
||||
{
|
||||
offset += MSM_DP_CONTROLLER_AHB_OFFSET;
|
||||
return readl_relaxed(catalog->io->dp_controller.base + offset);
|
||||
return readl_relaxed(catalog->io->dp_controller.ahb.base + offset);
|
||||
}
|
||||
|
||||
static inline void dp_write_ahb(struct dp_catalog_private *catalog,
|
||||
u32 offset, u32 data)
|
||||
{
|
||||
offset += MSM_DP_CONTROLLER_AHB_OFFSET;
|
||||
/*
|
||||
* To make sure phy reg writes happens before any other operation,
|
||||
* this function uses writel() instread of writel_relaxed()
|
||||
*/
|
||||
writel(data, catalog->io->dp_controller.base + offset);
|
||||
writel(data, catalog->io->dp_controller.ahb.base + offset);
|
||||
}
|
||||
|
||||
static inline void dp_write_p0(struct dp_catalog_private *catalog,
|
||||
u32 offset, u32 data)
|
||||
{
|
||||
offset += MSM_DP_CONTROLLER_P0_OFFSET;
|
||||
/*
|
||||
* To make sure interface reg writes happens before any other operation,
|
||||
* this function uses writel() instread of writel_relaxed()
|
||||
*/
|
||||
writel(data, catalog->io->dp_controller.base + offset);
|
||||
writel(data, catalog->io->dp_controller.p0.base + offset);
|
||||
}
|
||||
|
||||
static inline u32 dp_read_p0(struct dp_catalog_private *catalog,
|
||||
u32 offset)
|
||||
{
|
||||
offset += MSM_DP_CONTROLLER_P0_OFFSET;
|
||||
/*
|
||||
* To make sure interface reg writes happens before any other operation,
|
||||
* this function uses writel() instread of writel_relaxed()
|
||||
*/
|
||||
return readl_relaxed(catalog->io->dp_controller.base + offset);
|
||||
return readl_relaxed(catalog->io->dp_controller.p0.base + offset);
|
||||
}
|
||||
|
||||
static inline u32 dp_read_link(struct dp_catalog_private *catalog, u32 offset)
|
||||
{
|
||||
offset += MSM_DP_CONTROLLER_LINK_OFFSET;
|
||||
return readl_relaxed(catalog->io->dp_controller.base + offset);
|
||||
return readl_relaxed(catalog->io->dp_controller.link.base + offset);
|
||||
}
|
||||
|
||||
static inline void dp_write_link(struct dp_catalog_private *catalog,
|
||||
u32 offset, u32 data)
|
||||
{
|
||||
offset += MSM_DP_CONTROLLER_LINK_OFFSET;
|
||||
/*
|
||||
* To make sure link reg writes happens before any other operation,
|
||||
* this function uses writel() instread of writel_relaxed()
|
||||
*/
|
||||
writel(data, catalog->io->dp_controller.base + offset);
|
||||
writel(data, catalog->io->dp_controller.link.base + offset);
|
||||
}
|
||||
|
||||
/* aux related catalog functions */
|
||||
@ -276,29 +262,21 @@ static void dump_regs(void __iomem *base, int len)
|
||||
|
||||
void dp_catalog_dump_regs(struct dp_catalog *dp_catalog)
|
||||
{
|
||||
u32 offset, len;
|
||||
struct dp_catalog_private *catalog = container_of(dp_catalog,
|
||||
struct dp_catalog_private, dp_catalog);
|
||||
struct dss_io_data *io = &catalog->io->dp_controller;
|
||||
|
||||
pr_info("AHB regs\n");
|
||||
offset = MSM_DP_CONTROLLER_AHB_OFFSET;
|
||||
len = MSM_DP_CONTROLLER_AHB_SIZE;
|
||||
dump_regs(catalog->io->dp_controller.base + offset, len);
|
||||
dump_regs(io->ahb.base, io->ahb.len);
|
||||
|
||||
pr_info("AUXCLK regs\n");
|
||||
offset = MSM_DP_CONTROLLER_AUX_OFFSET;
|
||||
len = MSM_DP_CONTROLLER_AUX_SIZE;
|
||||
dump_regs(catalog->io->dp_controller.base + offset, len);
|
||||
dump_regs(io->aux.base, io->aux.len);
|
||||
|
||||
pr_info("LCLK regs\n");
|
||||
offset = MSM_DP_CONTROLLER_LINK_OFFSET;
|
||||
len = MSM_DP_CONTROLLER_LINK_SIZE;
|
||||
dump_regs(catalog->io->dp_controller.base + offset, len);
|
||||
dump_regs(io->link.base, io->link.len);
|
||||
|
||||
pr_info("P0CLK regs\n");
|
||||
offset = MSM_DP_CONTROLLER_P0_OFFSET;
|
||||
len = MSM_DP_CONTROLLER_P0_SIZE;
|
||||
dump_regs(catalog->io->dp_controller.base + offset, len);
|
||||
dump_regs(io->p0.base, io->p0.len);
|
||||
}
|
||||
|
||||
u32 dp_catalog_aux_get_irq(struct dp_catalog *dp_catalog)
|
||||
@ -493,8 +471,7 @@ int dp_catalog_ctrl_set_pattern(struct dp_catalog *dp_catalog,
|
||||
bit = BIT(pattern - 1) << DP_MAINLINK_READY_LINK_TRAINING_SHIFT;
|
||||
|
||||
/* Poll for mainlink ready status */
|
||||
ret = readx_poll_timeout(readl, catalog->io->dp_controller.base +
|
||||
MSM_DP_CONTROLLER_LINK_OFFSET +
|
||||
ret = readx_poll_timeout(readl, catalog->io->dp_controller.link.base +
|
||||
REG_DP_MAINLINK_READY,
|
||||
data, data & bit,
|
||||
POLLING_SLEEP_US, POLLING_TIMEOUT_US);
|
||||
@ -541,8 +518,7 @@ bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog)
|
||||
struct dp_catalog_private, dp_catalog);
|
||||
|
||||
/* Poll for mainlink ready status */
|
||||
ret = readl_poll_timeout(catalog->io->dp_controller.base +
|
||||
MSM_DP_CONTROLLER_LINK_OFFSET +
|
||||
ret = readl_poll_timeout(catalog->io->dp_controller.link.base +
|
||||
REG_DP_MAINLINK_READY,
|
||||
data, data & DP_MAINLINK_READY_FOR_VIDEO,
|
||||
POLLING_SLEEP_US, POLLING_TIMEOUT_US);
|
||||
|
@ -24,240 +24,108 @@ struct dp_debug_private {
|
||||
struct dp_usbpd *usbpd;
|
||||
struct dp_link *link;
|
||||
struct dp_panel *panel;
|
||||
struct drm_connector **connector;
|
||||
struct drm_connector *connector;
|
||||
struct device *dev;
|
||||
struct drm_device *drm_dev;
|
||||
|
||||
struct dp_debug dp_debug;
|
||||
};
|
||||
|
||||
static int dp_debug_check_buffer_overflow(int rc, int *max_size, int *len)
|
||||
static int dp_debug_show(struct seq_file *seq, void *p)
|
||||
{
|
||||
if (rc >= *max_size) {
|
||||
DRM_ERROR("buffer overflow\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
*len += rc;
|
||||
*max_size = SZ_4K - *len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t dp_debug_read_info(struct file *file, char __user *user_buff,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct dp_debug_private *debug = file->private_data;
|
||||
char *buf;
|
||||
u32 len = 0, rc = 0;
|
||||
struct dp_debug_private *debug = seq->private;
|
||||
u64 lclk = 0;
|
||||
u32 max_size = SZ_4K;
|
||||
u32 link_params_rate;
|
||||
struct drm_display_mode *drm_mode;
|
||||
const struct drm_display_mode *drm_mode;
|
||||
|
||||
if (!debug)
|
||||
return -ENODEV;
|
||||
|
||||
if (*ppos)
|
||||
return 0;
|
||||
|
||||
buf = kzalloc(SZ_4K, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
drm_mode = &debug->panel->dp_mode.drm_mode;
|
||||
|
||||
rc = snprintf(buf + len, max_size, "\tname = %s\n", DEBUG_NAME);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\tdp_panel\n\t\tmax_pclk_khz = %d\n",
|
||||
seq_printf(seq, "\tname = %s\n", DEBUG_NAME);
|
||||
seq_printf(seq, "\tdp_panel\n\t\tmax_pclk_khz = %d\n",
|
||||
debug->panel->max_pclk_khz);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\tdrm_dp_link\n\t\trate = %u\n",
|
||||
seq_printf(seq, "\tdrm_dp_link\n\t\trate = %u\n",
|
||||
debug->panel->link_info.rate);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tnum_lanes = %u\n",
|
||||
seq_printf(seq, "\t\tnum_lanes = %u\n",
|
||||
debug->panel->link_info.num_lanes);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tcapabilities = %lu\n",
|
||||
seq_printf(seq, "\t\tcapabilities = %lu\n",
|
||||
debug->panel->link_info.capabilities);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\tdp_panel_info:\n\t\tactive = %dx%d\n",
|
||||
seq_printf(seq, "\tdp_panel_info:\n\t\tactive = %dx%d\n",
|
||||
drm_mode->hdisplay,
|
||||
drm_mode->vdisplay);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tback_porch = %dx%d\n",
|
||||
seq_printf(seq, "\t\tback_porch = %dx%d\n",
|
||||
drm_mode->htotal - drm_mode->hsync_end,
|
||||
drm_mode->vtotal - drm_mode->vsync_end);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tfront_porch = %dx%d\n",
|
||||
seq_printf(seq, "\t\tfront_porch = %dx%d\n",
|
||||
drm_mode->hsync_start - drm_mode->hdisplay,
|
||||
drm_mode->vsync_start - drm_mode->vdisplay);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tsync_width = %dx%d\n",
|
||||
seq_printf(seq, "\t\tsync_width = %dx%d\n",
|
||||
drm_mode->hsync_end - drm_mode->hsync_start,
|
||||
drm_mode->vsync_end - drm_mode->vsync_start);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tactive_low = %dx%d\n",
|
||||
seq_printf(seq, "\t\tactive_low = %dx%d\n",
|
||||
debug->panel->dp_mode.h_active_low,
|
||||
debug->panel->dp_mode.v_active_low);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\th_skew = %d\n",
|
||||
seq_printf(seq, "\t\th_skew = %d\n",
|
||||
drm_mode->hskew);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\trefresh rate = %d\n",
|
||||
seq_printf(seq, "\t\trefresh rate = %d\n",
|
||||
drm_mode_vrefresh(drm_mode));
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tpixel clock khz = %d\n",
|
||||
seq_printf(seq, "\t\tpixel clock khz = %d\n",
|
||||
drm_mode->clock);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tbpp = %d\n",
|
||||
seq_printf(seq, "\t\tbpp = %d\n",
|
||||
debug->panel->dp_mode.bpp);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
/* Link Information */
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\tdp_link:\n\t\ttest_requested = %d\n",
|
||||
seq_printf(seq, "\tdp_link:\n\t\ttest_requested = %d\n",
|
||||
debug->link->sink_request);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tnum_lanes = %d\n",
|
||||
seq_printf(seq, "\t\tnum_lanes = %d\n",
|
||||
debug->link->link_params.num_lanes);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
link_params_rate = debug->link->link_params.rate;
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tbw_code = %d\n",
|
||||
seq_printf(seq, "\t\tbw_code = %d\n",
|
||||
drm_dp_link_rate_to_bw_code(link_params_rate));
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
lclk = debug->link->link_params.rate * 1000;
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tlclk = %lld\n", lclk);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tv_level = %d\n",
|
||||
seq_printf(seq, "\t\tlclk = %lld\n", lclk);
|
||||
seq_printf(seq, "\t\tv_level = %d\n",
|
||||
debug->link->phy_params.v_level);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
rc = snprintf(buf + len, max_size,
|
||||
"\t\tp_level = %d\n",
|
||||
seq_printf(seq, "\t\tp_level = %d\n",
|
||||
debug->link->phy_params.p_level);
|
||||
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
|
||||
goto error;
|
||||
|
||||
if (copy_to_user(user_buff, buf, len))
|
||||
goto error;
|
||||
|
||||
*ppos += len;
|
||||
|
||||
kfree(buf);
|
||||
return len;
|
||||
error:
|
||||
kfree(buf);
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(dp_debug);
|
||||
|
||||
static int dp_test_data_show(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_device *dev;
|
||||
struct dp_debug_private *debug;
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
const struct dp_debug_private *debug = m->private;
|
||||
const struct drm_connector *connector = debug->connector;
|
||||
u32 bpc;
|
||||
|
||||
debug = m->private;
|
||||
dev = debug->drm_dev;
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
drm_for_each_connector_iter(connector, &conn_iter) {
|
||||
|
||||
if (connector->connector_type !=
|
||||
DRM_MODE_CONNECTOR_DisplayPort)
|
||||
continue;
|
||||
|
||||
if (connector->status == connector_status_connected) {
|
||||
bpc = debug->link->test_video.test_bit_depth;
|
||||
seq_printf(m, "hdisplay: %d\n",
|
||||
debug->link->test_video.test_h_width);
|
||||
seq_printf(m, "vdisplay: %d\n",
|
||||
debug->link->test_video.test_v_height);
|
||||
seq_printf(m, "bpc: %u\n",
|
||||
dp_link_bit_depth_to_bpc(bpc));
|
||||
} else
|
||||
seq_puts(m, "0");
|
||||
if (connector->status == connector_status_connected) {
|
||||
bpc = debug->link->test_video.test_bit_depth;
|
||||
seq_printf(m, "hdisplay: %d\n",
|
||||
debug->link->test_video.test_h_width);
|
||||
seq_printf(m, "vdisplay: %d\n",
|
||||
debug->link->test_video.test_v_height);
|
||||
seq_printf(m, "bpc: %u\n",
|
||||
dp_link_bit_depth_to_bpc(bpc));
|
||||
} else {
|
||||
seq_puts(m, "0");
|
||||
}
|
||||
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(dp_test_data);
|
||||
|
||||
static int dp_test_type_show(struct seq_file *m, void *data)
|
||||
{
|
||||
struct dp_debug_private *debug = m->private;
|
||||
struct drm_device *dev = debug->drm_dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
const struct dp_debug_private *debug = m->private;
|
||||
const struct drm_connector *connector = debug->connector;
|
||||
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
drm_for_each_connector_iter(connector, &conn_iter) {
|
||||
|
||||
if (connector->connector_type !=
|
||||
DRM_MODE_CONNECTOR_DisplayPort)
|
||||
continue;
|
||||
|
||||
if (connector->status == connector_status_connected)
|
||||
seq_printf(m, "%02x", DP_TEST_LINK_VIDEO_PATTERN);
|
||||
else
|
||||
seq_puts(m, "0");
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
if (connector->status == connector_status_connected)
|
||||
seq_printf(m, "%02x", DP_TEST_LINK_VIDEO_PATTERN);
|
||||
else
|
||||
seq_puts(m, "0");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -269,14 +137,12 @@ static ssize_t dp_test_active_write(struct file *file,
|
||||
{
|
||||
char *input_buffer;
|
||||
int status = 0;
|
||||
struct dp_debug_private *debug;
|
||||
struct drm_device *dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
const struct dp_debug_private *debug;
|
||||
const struct drm_connector *connector;
|
||||
int val = 0;
|
||||
|
||||
debug = ((struct seq_file *)file->private_data)->private;
|
||||
dev = debug->drm_dev;
|
||||
connector = debug->connector;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
@ -287,30 +153,22 @@ static ssize_t dp_test_active_write(struct file *file,
|
||||
|
||||
DRM_DEBUG_DRIVER("Copied %d bytes from user\n", (unsigned int)len);
|
||||
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
drm_for_each_connector_iter(connector, &conn_iter) {
|
||||
if (connector->connector_type !=
|
||||
DRM_MODE_CONNECTOR_DisplayPort)
|
||||
continue;
|
||||
|
||||
if (connector->status == connector_status_connected) {
|
||||
status = kstrtoint(input_buffer, 10, &val);
|
||||
if (status < 0)
|
||||
break;
|
||||
DRM_DEBUG_DRIVER("Got %d for test active\n", val);
|
||||
/* To prevent erroneous activation of the compliance
|
||||
* testing code, only accept an actual value of 1 here
|
||||
*/
|
||||
if (val == 1)
|
||||
debug->panel->video_test = true;
|
||||
else
|
||||
debug->panel->video_test = false;
|
||||
if (connector->status == connector_status_connected) {
|
||||
status = kstrtoint(input_buffer, 10, &val);
|
||||
if (status < 0) {
|
||||
kfree(input_buffer);
|
||||
return status;
|
||||
}
|
||||
DRM_DEBUG_DRIVER("Got %d for test active\n", val);
|
||||
/* To prevent erroneous activation of the compliance
|
||||
* testing code, only accept an actual value of 1 here
|
||||
*/
|
||||
if (val == 1)
|
||||
debug->panel->video_test = true;
|
||||
else
|
||||
debug->panel->video_test = false;
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
kfree(input_buffer);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
*offp += len;
|
||||
return len;
|
||||
@ -319,25 +177,16 @@ static ssize_t dp_test_active_write(struct file *file,
|
||||
static int dp_test_active_show(struct seq_file *m, void *data)
|
||||
{
|
||||
struct dp_debug_private *debug = m->private;
|
||||
struct drm_device *dev = debug->drm_dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
struct drm_connector *connector = debug->connector;
|
||||
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
drm_for_each_connector_iter(connector, &conn_iter) {
|
||||
if (connector->connector_type !=
|
||||
DRM_MODE_CONNECTOR_DisplayPort)
|
||||
continue;
|
||||
|
||||
if (connector->status == connector_status_connected) {
|
||||
if (debug->panel->video_test)
|
||||
seq_puts(m, "1");
|
||||
else
|
||||
seq_puts(m, "0");
|
||||
} else
|
||||
if (connector->status == connector_status_connected) {
|
||||
if (debug->panel->video_test)
|
||||
seq_puts(m, "1");
|
||||
else
|
||||
seq_puts(m, "0");
|
||||
} else {
|
||||
seq_puts(m, "0");
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -349,11 +198,6 @@ static int dp_test_active_open(struct inode *inode,
|
||||
inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations dp_debug_fops = {
|
||||
.open = simple_open,
|
||||
.read = dp_debug_read_info,
|
||||
};
|
||||
|
||||
static const struct file_operations test_active_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = dp_test_active_open,
|
||||
@ -391,7 +235,7 @@ static int dp_debug_init(struct dp_debug *dp_debug, struct drm_minor *minor)
|
||||
|
||||
struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
|
||||
struct dp_usbpd *usbpd, struct dp_link *link,
|
||||
struct drm_connector **connector, struct drm_minor *minor)
|
||||
struct drm_connector *connector, struct drm_minor *minor)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_debug_private *debug;
|
||||
|
@ -43,7 +43,7 @@ struct dp_debug {
|
||||
*/
|
||||
struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
|
||||
struct dp_usbpd *usbpd, struct dp_link *link,
|
||||
struct drm_connector **connector,
|
||||
struct drm_connector *connector,
|
||||
struct drm_minor *minor);
|
||||
|
||||
/**
|
||||
@ -60,7 +60,7 @@ void dp_debug_put(struct dp_debug *dp_debug);
|
||||
static inline
|
||||
struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
|
||||
struct dp_usbpd *usbpd, struct dp_link *link,
|
||||
struct drm_connector **connector, struct drm_minor *minor)
|
||||
struct drm_connector *connector, struct drm_minor *minor)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/component.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_kms.h"
|
||||
@ -27,7 +28,6 @@
|
||||
#include "dp_audio.h"
|
||||
#include "dp_debug.h"
|
||||
|
||||
static struct msm_dp *g_dp_display;
|
||||
#define HPD_STRING_SIZE 30
|
||||
|
||||
enum {
|
||||
@ -79,6 +79,8 @@ struct dp_display_private {
|
||||
char *name;
|
||||
int irq;
|
||||
|
||||
unsigned int id;
|
||||
|
||||
/* state variables */
|
||||
bool core_initialized;
|
||||
bool hpd_irq_on;
|
||||
@ -116,11 +118,35 @@ struct dp_display_private {
|
||||
struct dp_audio *audio;
|
||||
};
|
||||
|
||||
struct msm_dp_desc {
|
||||
phys_addr_t io_start;
|
||||
unsigned int connector_type;
|
||||
};
|
||||
|
||||
struct msm_dp_config {
|
||||
const struct msm_dp_desc *descs;
|
||||
size_t num_descs;
|
||||
};
|
||||
|
||||
static const struct msm_dp_config sc7180_dp_cfg = {
|
||||
.descs = (const struct msm_dp_desc[]) {
|
||||
[MSM_DP_CONTROLLER_0] = { .io_start = 0x0ae90000, .connector_type = DRM_MODE_CONNECTOR_DisplayPort },
|
||||
},
|
||||
.num_descs = 1,
|
||||
};
|
||||
|
||||
static const struct of_device_id dp_dt_match[] = {
|
||||
{.compatible = "qcom,sc7180-dp"},
|
||||
{ .compatible = "qcom,sc7180-dp", .data = &sc7180_dp_cfg },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct dp_display_private *dev_get_dp_display_private(struct device *dev)
|
||||
{
|
||||
struct msm_dp *dp = dev_get_drvdata(dev);
|
||||
|
||||
return container_of(dp, struct dp_display_private, dp_display);
|
||||
}
|
||||
|
||||
static int dp_add_event(struct dp_display_private *dp_priv, u32 event,
|
||||
u32 data, u32 delay)
|
||||
{
|
||||
@ -197,25 +223,24 @@ static int dp_display_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
struct drm_device *drm;
|
||||
struct dp_display_private *dp = dev_get_dp_display_private(dev);
|
||||
struct msm_drm_private *priv;
|
||||
struct drm_device *drm;
|
||||
|
||||
drm = dev_get_drvdata(master);
|
||||
|
||||
dp = container_of(g_dp_display,
|
||||
struct dp_display_private, dp_display);
|
||||
|
||||
dp->dp_display.drm_dev = drm;
|
||||
priv = drm->dev_private;
|
||||
priv->dp = &(dp->dp_display);
|
||||
priv->dp[dp->id] = &dp->dp_display;
|
||||
|
||||
rc = dp->parser->parse(dp->parser);
|
||||
rc = dp->parser->parse(dp->parser, dp->dp_display.connector_type);
|
||||
if (rc) {
|
||||
DRM_ERROR("device tree parsing failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
dp->dp_display.panel_bridge = dp->parser->panel_bridge;
|
||||
|
||||
dp->aux->drm_dev = drm;
|
||||
rc = dp_aux_register(dp->aux);
|
||||
if (rc) {
|
||||
@ -240,16 +265,13 @@ end:
|
||||
static void dp_display_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
struct dp_display_private *dp = dev_get_dp_display_private(dev);
|
||||
struct drm_device *drm = dev_get_drvdata(master);
|
||||
struct msm_drm_private *priv = drm->dev_private;
|
||||
|
||||
dp = container_of(g_dp_display,
|
||||
struct dp_display_private, dp_display);
|
||||
|
||||
dp_power_client_deinit(dp->power);
|
||||
dp_aux_unregister(dp->aux);
|
||||
priv->dp = NULL;
|
||||
priv->dp[dp->id] = NULL;
|
||||
}
|
||||
|
||||
static const struct component_ops dp_display_comp_ops = {
|
||||
@ -379,38 +401,17 @@ static void dp_display_host_deinit(struct dp_display_private *dp)
|
||||
|
||||
static int dp_display_usbpd_configure_cb(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dev) {
|
||||
DRM_ERROR("invalid dev\n");
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dp = container_of(g_dp_display,
|
||||
struct dp_display_private, dp_display);
|
||||
struct dp_display_private *dp = dev_get_dp_display_private(dev);
|
||||
|
||||
dp_display_host_init(dp, false);
|
||||
|
||||
rc = dp_display_process_hpd_high(dp);
|
||||
end:
|
||||
return rc;
|
||||
return dp_display_process_hpd_high(dp);
|
||||
}
|
||||
|
||||
static int dp_display_usbpd_disconnect_cb(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dev) {
|
||||
DRM_ERROR("invalid dev\n");
|
||||
rc = -EINVAL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
dp = container_of(g_dp_display,
|
||||
struct dp_display_private, dp_display);
|
||||
struct dp_display_private *dp = dev_get_dp_display_private(dev);
|
||||
|
||||
dp_add_event(dp, EV_USER_NOTIFICATION, false, 0);
|
||||
|
||||
@ -472,15 +473,7 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
u32 sink_request;
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dev) {
|
||||
DRM_ERROR("invalid dev\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp = container_of(g_dp_display,
|
||||
struct dp_display_private, dp_display);
|
||||
struct dp_display_private *dp = dev_get_dp_display_private(dev);
|
||||
|
||||
/* check for any test request issued by sink */
|
||||
rc = dp_link_process_request(dp->link);
|
||||
@ -647,7 +640,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
|
||||
|
||||
DRM_DEBUG_DP("hpd_state=%d\n", state);
|
||||
/* signal the disconnect event early to ensure proper teardown */
|
||||
dp_display_handle_plugged_change(g_dp_display, false);
|
||||
dp_display_handle_plugged_change(&dp->dp_display, false);
|
||||
|
||||
/* enable HDP plug interrupt to prepare for next plugin */
|
||||
dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK, true);
|
||||
@ -834,7 +827,7 @@ static int dp_display_set_mode(struct msm_dp *dp_display,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_prepare(struct msm_dp *dp)
|
||||
static int dp_display_prepare(struct msm_dp *dp_display)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -842,9 +835,7 @@ static int dp_display_prepare(struct msm_dp *dp)
|
||||
static int dp_display_enable(struct dp_display_private *dp, u32 data)
|
||||
{
|
||||
int rc = 0;
|
||||
struct msm_dp *dp_display;
|
||||
|
||||
dp_display = g_dp_display;
|
||||
struct msm_dp *dp_display = &dp->dp_display;
|
||||
|
||||
DRM_DEBUG_DP("sink_count=%d\n", dp->link->sink_count);
|
||||
if (dp_display->power_on) {
|
||||
@ -880,9 +871,7 @@ static int dp_display_post_enable(struct msm_dp *dp_display)
|
||||
|
||||
static int dp_display_disable(struct dp_display_private *dp, u32 data)
|
||||
{
|
||||
struct msm_dp *dp_display;
|
||||
|
||||
dp_display = g_dp_display;
|
||||
struct msm_dp *dp_display = &dp->dp_display;
|
||||
|
||||
if (!dp_display->power_on)
|
||||
return 0;
|
||||
@ -912,7 +901,7 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_unprepare(struct msm_dp *dp)
|
||||
static int dp_display_unprepare(struct msm_dp *dp_display)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -1213,10 +1202,33 @@ int dp_display_request_irq(struct msm_dp *dp_display)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct msm_dp_desc *dp_display_get_desc(struct platform_device *pdev,
|
||||
unsigned int *id)
|
||||
{
|
||||
const struct msm_dp_config *cfg = of_device_get_match_data(&pdev->dev);
|
||||
struct resource *res;
|
||||
int i;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < cfg->num_descs; i++) {
|
||||
if (cfg->descs[i].io_start == res->start) {
|
||||
*id = i;
|
||||
return &cfg->descs[i];
|
||||
}
|
||||
}
|
||||
|
||||
dev_err(&pdev->dev, "unknown displayport instance\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int dp_display_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
const struct msm_dp_desc *desc;
|
||||
|
||||
if (!pdev || !pdev->dev.of_node) {
|
||||
DRM_ERROR("pdev not found\n");
|
||||
@ -1227,8 +1239,13 @@ static int dp_display_probe(struct platform_device *pdev)
|
||||
if (!dp)
|
||||
return -ENOMEM;
|
||||
|
||||
desc = dp_display_get_desc(pdev, &dp->id);
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
|
||||
dp->pdev = pdev;
|
||||
dp->name = "drm_dp";
|
||||
dp->dp_display.connector_type = desc->connector_type;
|
||||
|
||||
rc = dp_init_sub_modules(dp);
|
||||
if (rc) {
|
||||
@ -1237,14 +1254,13 @@ static int dp_display_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
mutex_init(&dp->event_mutex);
|
||||
g_dp_display = &dp->dp_display;
|
||||
|
||||
/* Store DP audio handle inside DP display */
|
||||
g_dp_display->dp_audio = dp->audio;
|
||||
dp->dp_display.dp_audio = dp->audio;
|
||||
|
||||
init_completion(&dp->audio_comp);
|
||||
|
||||
platform_set_drvdata(pdev, g_dp_display);
|
||||
platform_set_drvdata(pdev, &dp->dp_display);
|
||||
|
||||
rc = component_add(&pdev->dev, &dp_display_comp_ops);
|
||||
if (rc) {
|
||||
@ -1257,10 +1273,7 @@ static int dp_display_probe(struct platform_device *pdev)
|
||||
|
||||
static int dp_display_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
|
||||
dp = container_of(g_dp_display,
|
||||
struct dp_display_private, dp_display);
|
||||
struct dp_display_private *dp = dev_get_dp_display_private(&pdev->dev);
|
||||
|
||||
dp_display_deinit_sub_modules(dp);
|
||||
|
||||
@ -1315,7 +1328,7 @@ static int dp_pm_resume(struct device *dev)
|
||||
dp->dp_display.is_connected = true;
|
||||
} else {
|
||||
dp->dp_display.is_connected = false;
|
||||
dp_display_handle_plugged_change(g_dp_display, false);
|
||||
dp_display_handle_plugged_change(dp_display, false);
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("After, sink_count=%d is_connected=%d core_inited=%d power_on=%d\n",
|
||||
@ -1429,7 +1442,7 @@ void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor)
|
||||
dev = &dp->pdev->dev;
|
||||
|
||||
dp->debug = dp_debug_get(dev, dp->panel, dp->usbpd,
|
||||
dp->link, &dp->dp_display.connector,
|
||||
dp->link, dp->dp_display.connector,
|
||||
minor);
|
||||
if (IS_ERR(dp->debug)) {
|
||||
rc = PTR_ERR(dp->debug);
|
||||
|
@ -15,9 +15,11 @@ struct msm_dp {
|
||||
struct device *codec_dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_bridge *panel_bridge;
|
||||
bool is_connected;
|
||||
bool audio_enabled;
|
||||
bool power_on;
|
||||
unsigned int connector_type;
|
||||
|
||||
hdmi_codec_plugged_cb plugged_cb;
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
@ -147,7 +148,7 @@ struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
|
||||
|
||||
ret = drm_connector_init(dp_display->drm_dev, connector,
|
||||
&dp_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DisplayPort);
|
||||
dp_display->connector_type);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
@ -160,5 +161,15 @@ struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
|
||||
|
||||
drm_connector_attach_encoder(connector, dp_display->encoder);
|
||||
|
||||
if (dp_display->panel_bridge) {
|
||||
ret = drm_bridge_attach(dp_display->encoder,
|
||||
dp_display->panel_bridge, NULL,
|
||||
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to attach panel bridge: %d\n", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
}
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ u32 dp_panel_get_mode_bpp(struct dp_panel *dp_panel,
|
||||
u32 mode_edid_bpp, u32 mode_pclk_khz)
|
||||
{
|
||||
struct dp_panel_private *panel;
|
||||
u32 bpp = mode_edid_bpp;
|
||||
u32 bpp;
|
||||
|
||||
if (!dp_panel || !mode_edid_bpp || !mode_pclk_khz) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
|
@ -6,11 +6,22 @@
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
|
||||
#include "dp_parser.h"
|
||||
#include "dp_reg.h"
|
||||
|
||||
#define DP_DEFAULT_AHB_OFFSET 0x0000
|
||||
#define DP_DEFAULT_AHB_SIZE 0x0200
|
||||
#define DP_DEFAULT_AUX_OFFSET 0x0200
|
||||
#define DP_DEFAULT_AUX_SIZE 0x0200
|
||||
#define DP_DEFAULT_LINK_OFFSET 0x0400
|
||||
#define DP_DEFAULT_LINK_SIZE 0x0C00
|
||||
#define DP_DEFAULT_P0_OFFSET 0x1000
|
||||
#define DP_DEFAULT_P0_SIZE 0x0400
|
||||
|
||||
static const struct dp_regulator_cfg sdm845_dp_reg_cfg = {
|
||||
.num = 2,
|
||||
.regs = {
|
||||
@ -19,67 +30,73 @@ static const struct dp_regulator_cfg sdm845_dp_reg_cfg = {
|
||||
},
|
||||
};
|
||||
|
||||
static int msm_dss_ioremap(struct platform_device *pdev,
|
||||
struct dss_io_data *io_data)
|
||||
static void __iomem *dp_ioremap(struct platform_device *pdev, int idx, size_t *len)
|
||||
{
|
||||
struct resource *res = NULL;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
DRM_ERROR("%pS->%s: msm_dss_get_res failed\n",
|
||||
__builtin_return_address(0), __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
base = devm_platform_get_and_ioremap_resource(pdev, idx, &res);
|
||||
if (!IS_ERR(base))
|
||||
*len = resource_size(res);
|
||||
|
||||
io_data->len = (u32)resource_size(res);
|
||||
io_data->base = ioremap(res->start, io_data->len);
|
||||
if (!io_data->base) {
|
||||
DRM_ERROR("%pS->%s: ioremap failed\n",
|
||||
__builtin_return_address(0), __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_dss_iounmap(struct dss_io_data *io_data)
|
||||
{
|
||||
if (io_data->base) {
|
||||
iounmap(io_data->base);
|
||||
io_data->base = NULL;
|
||||
}
|
||||
io_data->len = 0;
|
||||
}
|
||||
|
||||
static void dp_parser_unmap_io_resources(struct dp_parser *parser)
|
||||
{
|
||||
struct dp_io *io = &parser->io;
|
||||
|
||||
msm_dss_iounmap(&io->dp_controller);
|
||||
return base;
|
||||
}
|
||||
|
||||
static int dp_parser_ctrl_res(struct dp_parser *parser)
|
||||
{
|
||||
int rc = 0;
|
||||
struct platform_device *pdev = parser->pdev;
|
||||
struct dp_io *io = &parser->io;
|
||||
struct dss_io_data *dss = &io->dp_controller;
|
||||
|
||||
rc = msm_dss_ioremap(pdev, &io->dp_controller);
|
||||
if (rc) {
|
||||
DRM_ERROR("unable to remap dp io resources, rc=%d\n", rc);
|
||||
goto err;
|
||||
dss->ahb.base = dp_ioremap(pdev, 0, &dss->ahb.len);
|
||||
if (IS_ERR(dss->ahb.base))
|
||||
return PTR_ERR(dss->ahb.base);
|
||||
|
||||
dss->aux.base = dp_ioremap(pdev, 1, &dss->aux.len);
|
||||
if (IS_ERR(dss->aux.base)) {
|
||||
/*
|
||||
* The initial binding had a single reg, but in order to
|
||||
* support variation in the sub-region sizes this was split.
|
||||
* dp_ioremap() will fail with -EINVAL here if only a single
|
||||
* reg is specified, so fill in the sub-region offsets and
|
||||
* lengths based on this single region.
|
||||
*/
|
||||
if (PTR_ERR(dss->aux.base) == -EINVAL) {
|
||||
if (dss->ahb.len < DP_DEFAULT_P0_OFFSET + DP_DEFAULT_P0_SIZE) {
|
||||
DRM_ERROR("legacy memory region not large enough\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dss->ahb.len = DP_DEFAULT_AHB_SIZE;
|
||||
dss->aux.base = dss->ahb.base + DP_DEFAULT_AUX_OFFSET;
|
||||
dss->aux.len = DP_DEFAULT_AUX_SIZE;
|
||||
dss->link.base = dss->ahb.base + DP_DEFAULT_LINK_OFFSET;
|
||||
dss->link.len = DP_DEFAULT_LINK_SIZE;
|
||||
dss->p0.base = dss->ahb.base + DP_DEFAULT_P0_OFFSET;
|
||||
dss->p0.len = DP_DEFAULT_P0_SIZE;
|
||||
} else {
|
||||
DRM_ERROR("unable to remap aux region: %pe\n", dss->aux.base);
|
||||
return PTR_ERR(dss->aux.base);
|
||||
}
|
||||
} else {
|
||||
dss->link.base = dp_ioremap(pdev, 2, &dss->link.len);
|
||||
if (IS_ERR(dss->link.base)) {
|
||||
DRM_ERROR("unable to remap link region: %pe\n", dss->link.base);
|
||||
return PTR_ERR(dss->link.base);
|
||||
}
|
||||
|
||||
dss->p0.base = dp_ioremap(pdev, 3, &dss->p0.len);
|
||||
if (IS_ERR(dss->p0.base)) {
|
||||
DRM_ERROR("unable to remap p0 region: %pe\n", dss->p0.base);
|
||||
return PTR_ERR(dss->p0.base);
|
||||
}
|
||||
}
|
||||
|
||||
io->phy = devm_phy_get(&pdev->dev, "dp");
|
||||
if (IS_ERR(io->phy)) {
|
||||
rc = PTR_ERR(io->phy);
|
||||
goto err;
|
||||
}
|
||||
if (IS_ERR(io->phy))
|
||||
return PTR_ERR(io->phy);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dp_parser_unmap_io_resources(parser);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_parser_misc(struct dp_parser *parser)
|
||||
@ -248,7 +265,28 @@ static int dp_parser_clock(struct dp_parser *parser)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_parser_parse(struct dp_parser *parser)
|
||||
static int dp_parser_find_panel(struct dp_parser *parser)
|
||||
{
|
||||
struct device *dev = &parser->pdev->dev;
|
||||
struct drm_panel *panel;
|
||||
int rc;
|
||||
|
||||
rc = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to acquire DRM panel: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
parser->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
|
||||
if (IS_ERR(parser->panel_bridge)) {
|
||||
DRM_ERROR("failed to create panel bridge\n");
|
||||
return PTR_ERR(parser->panel_bridge);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_parser_parse(struct dp_parser *parser, int connector_type)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
@ -269,6 +307,12 @@ static int dp_parser_parse(struct dp_parser *parser)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (connector_type == DRM_MODE_CONNECTOR_eDP) {
|
||||
rc = dp_parser_find_panel(parser);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Map the corresponding regulator information according to
|
||||
* version. Currently, since we only have one supported platform,
|
||||
* mapping the regulator directly.
|
||||
|
@ -25,11 +25,18 @@ enum dp_pm_type {
|
||||
DP_MAX_PM
|
||||
};
|
||||
|
||||
struct dss_io_data {
|
||||
u32 len;
|
||||
struct dss_io_region {
|
||||
size_t len;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
struct dss_io_data {
|
||||
struct dss_io_region ahb;
|
||||
struct dss_io_region aux;
|
||||
struct dss_io_region link;
|
||||
struct dss_io_region p0;
|
||||
};
|
||||
|
||||
static inline const char *dp_parser_pm_name(enum dp_pm_type module)
|
||||
{
|
||||
switch (module) {
|
||||
@ -116,8 +123,9 @@ struct dp_parser {
|
||||
struct dp_display_data disp_data;
|
||||
const struct dp_regulator_cfg *regulator_cfg;
|
||||
u32 max_dp_lanes;
|
||||
struct drm_bridge *panel_bridge;
|
||||
|
||||
int (*parse)(struct dp_parser *parser);
|
||||
int (*parse)(struct dp_parser *parser, int connector_type);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -107,6 +107,8 @@ void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host,
|
||||
u32 dma_base, u32 len);
|
||||
int msm_dsi_host_enable(struct mipi_dsi_host *host);
|
||||
int msm_dsi_host_disable(struct mipi_dsi_host *host);
|
||||
void msm_dsi_host_enable_irq(struct mipi_dsi_host *host);
|
||||
void msm_dsi_host_disable_irq(struct mipi_dsi_host *host);
|
||||
int msm_dsi_host_power_on(struct mipi_dsi_host *host,
|
||||
struct msm_dsi_phy_shared_timings *phy_shared_timings,
|
||||
bool is_bonded_dsi, struct msm_dsi_phy *phy);
|
||||
|
@ -106,7 +106,8 @@ struct msm_dsi_host {
|
||||
phys_addr_t ctrl_size;
|
||||
struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX];
|
||||
|
||||
struct clk *bus_clks[DSI_BUS_CLK_MAX];
|
||||
int num_bus_clks;
|
||||
struct clk_bulk_data bus_clks[DSI_BUS_CLK_MAX];
|
||||
|
||||
struct clk *byte_clk;
|
||||
struct clk *esc_clk;
|
||||
@ -115,16 +116,16 @@ struct msm_dsi_host {
|
||||
struct clk *pixel_clk_src;
|
||||
struct clk *byte_intf_clk;
|
||||
|
||||
u32 byte_clk_rate;
|
||||
u32 pixel_clk_rate;
|
||||
u32 esc_clk_rate;
|
||||
unsigned long byte_clk_rate;
|
||||
unsigned long pixel_clk_rate;
|
||||
unsigned long esc_clk_rate;
|
||||
|
||||
/* DSI v2 specific clocks */
|
||||
struct clk *src_clk;
|
||||
struct clk *esc_clk_src;
|
||||
struct clk *dsi_clk_src;
|
||||
|
||||
u32 src_clk_rate;
|
||||
unsigned long src_clk_rate;
|
||||
|
||||
struct gpio_desc *disp_en_gpio;
|
||||
struct gpio_desc *te_gpio;
|
||||
@ -374,15 +375,14 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host)
|
||||
int i, ret = 0;
|
||||
|
||||
/* get bus clocks */
|
||||
for (i = 0; i < cfg->num_bus_clks; i++) {
|
||||
msm_host->bus_clks[i] = msm_clk_get(pdev,
|
||||
cfg->bus_clk_names[i]);
|
||||
if (IS_ERR(msm_host->bus_clks[i])) {
|
||||
ret = PTR_ERR(msm_host->bus_clks[i]);
|
||||
pr_err("%s: Unable to get %s clock, ret = %d\n",
|
||||
__func__, cfg->bus_clk_names[i], ret);
|
||||
goto exit;
|
||||
}
|
||||
for (i = 0; i < cfg->num_bus_clks; i++)
|
||||
msm_host->bus_clks[i].id = cfg->bus_clk_names[i];
|
||||
msm_host->num_bus_clks = cfg->num_bus_clks;
|
||||
|
||||
ret = devm_clk_bulk_get(&pdev->dev, msm_host->num_bus_clks, msm_host->bus_clks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Unable to get clocks, ret = %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* get link and source clocks */
|
||||
@ -433,41 +433,6 @@ exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dsi_bus_clk_enable(struct msm_dsi_host *msm_host)
|
||||
{
|
||||
const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg;
|
||||
int i, ret;
|
||||
|
||||
DBG("id=%d", msm_host->id);
|
||||
|
||||
for (i = 0; i < cfg->num_bus_clks; i++) {
|
||||
ret = clk_prepare_enable(msm_host->bus_clks[i]);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to enable bus clock %d ret %d\n",
|
||||
__func__, i, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
while (--i >= 0)
|
||||
clk_disable_unprepare(msm_host->bus_clks[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dsi_bus_clk_disable(struct msm_dsi_host *msm_host)
|
||||
{
|
||||
const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg;
|
||||
int i;
|
||||
|
||||
DBG("");
|
||||
|
||||
for (i = cfg->num_bus_clks - 1; i >= 0; i--)
|
||||
clk_disable_unprepare(msm_host->bus_clks[i]);
|
||||
}
|
||||
|
||||
int msm_dsi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
@ -478,7 +443,7 @@ int msm_dsi_runtime_suspend(struct device *dev)
|
||||
if (!msm_host->cfg_hnd)
|
||||
return 0;
|
||||
|
||||
dsi_bus_clk_disable(msm_host);
|
||||
clk_bulk_disable_unprepare(msm_host->num_bus_clks, msm_host->bus_clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -493,15 +458,15 @@ int msm_dsi_runtime_resume(struct device *dev)
|
||||
if (!msm_host->cfg_hnd)
|
||||
return 0;
|
||||
|
||||
return dsi_bus_clk_enable(msm_host);
|
||||
return clk_bulk_prepare_enable(msm_host->num_bus_clks, msm_host->bus_clks);
|
||||
}
|
||||
|
||||
int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host)
|
||||
{
|
||||
u32 byte_intf_rate;
|
||||
unsigned long byte_intf_rate;
|
||||
int ret;
|
||||
|
||||
DBG("Set clk rates: pclk=%d, byteclk=%d",
|
||||
DBG("Set clk rates: pclk=%d, byteclk=%lu",
|
||||
msm_host->mode->clock, msm_host->byte_clk_rate);
|
||||
|
||||
ret = dev_pm_opp_set_rate(&msm_host->pdev->dev,
|
||||
@ -558,13 +523,11 @@ int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host)
|
||||
goto pixel_clk_err;
|
||||
}
|
||||
|
||||
if (msm_host->byte_intf_clk) {
|
||||
ret = clk_prepare_enable(msm_host->byte_intf_clk);
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to enable byte intf clk\n",
|
||||
__func__);
|
||||
goto byte_intf_clk_err;
|
||||
}
|
||||
ret = clk_prepare_enable(msm_host->byte_intf_clk);
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to enable byte intf clk\n",
|
||||
__func__);
|
||||
goto byte_intf_clk_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -583,7 +546,7 @@ int dsi_link_clk_set_rate_v2(struct msm_dsi_host *msm_host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DBG("Set clk rates: pclk=%d, byteclk=%d, esc_clk=%d, dsi_src_clk=%d",
|
||||
DBG("Set clk rates: pclk=%d, byteclk=%lu, esc_clk=%lu, dsi_src_clk=%lu",
|
||||
msm_host->mode->clock, msm_host->byte_clk_rate,
|
||||
msm_host->esc_clk_rate, msm_host->src_clk_rate);
|
||||
|
||||
@ -660,8 +623,7 @@ void dsi_link_clk_disable_6g(struct msm_dsi_host *msm_host)
|
||||
dev_pm_opp_set_rate(&msm_host->pdev->dev, 0);
|
||||
clk_disable_unprepare(msm_host->esc_clk);
|
||||
clk_disable_unprepare(msm_host->pixel_clk);
|
||||
if (msm_host->byte_intf_clk)
|
||||
clk_disable_unprepare(msm_host->byte_intf_clk);
|
||||
clk_disable_unprepare(msm_host->byte_intf_clk);
|
||||
clk_disable_unprepare(msm_host->byte_clk);
|
||||
}
|
||||
|
||||
@ -673,10 +635,10 @@ void dsi_link_clk_disable_v2(struct msm_dsi_host *msm_host)
|
||||
clk_disable_unprepare(msm_host->byte_clk);
|
||||
}
|
||||
|
||||
static u32 dsi_get_pclk_rate(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
|
||||
static unsigned long dsi_get_pclk_rate(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
|
||||
{
|
||||
struct drm_display_mode *mode = msm_host->mode;
|
||||
u32 pclk_rate;
|
||||
unsigned long pclk_rate;
|
||||
|
||||
pclk_rate = mode->clock * 1000;
|
||||
|
||||
@ -696,7 +658,7 @@ static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
|
||||
{
|
||||
u8 lanes = msm_host->lanes;
|
||||
u32 bpp = dsi_get_bpp(msm_host->format);
|
||||
u32 pclk_rate = dsi_get_pclk_rate(msm_host, is_bonded_dsi);
|
||||
unsigned long pclk_rate = dsi_get_pclk_rate(msm_host, is_bonded_dsi);
|
||||
u64 pclk_bpp = (u64)pclk_rate * bpp;
|
||||
|
||||
if (lanes == 0) {
|
||||
@ -713,7 +675,7 @@ static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
|
||||
msm_host->pixel_clk_rate = pclk_rate;
|
||||
msm_host->byte_clk_rate = pclk_bpp;
|
||||
|
||||
DBG("pclk=%d, bclk=%d", msm_host->pixel_clk_rate,
|
||||
DBG("pclk=%lu, bclk=%lu", msm_host->pixel_clk_rate,
|
||||
msm_host->byte_clk_rate);
|
||||
|
||||
}
|
||||
@ -772,7 +734,7 @@ int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
|
||||
|
||||
msm_host->esc_clk_rate = msm_host->byte_clk_rate / esc_div;
|
||||
|
||||
DBG("esc=%d, src=%d", msm_host->esc_clk_rate,
|
||||
DBG("esc=%lu, src=%lu", msm_host->esc_clk_rate,
|
||||
msm_host->src_clk_rate);
|
||||
|
||||
return 0;
|
||||
@ -1898,6 +1860,23 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
msm_host->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
if (msm_host->irq < 0) {
|
||||
ret = msm_host->irq;
|
||||
dev_err(&pdev->dev, "failed to get irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* do not autoenable, will be enabled later */
|
||||
ret = devm_request_irq(&pdev->dev, msm_host->irq, dsi_host_irq,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQF_NO_AUTOEN,
|
||||
"dsi_isr", msm_host);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request IRQ%u: %d\n",
|
||||
msm_host->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_completion(&msm_host->dma_comp);
|
||||
init_completion(&msm_host->video_comp);
|
||||
mutex_init(&msm_host->dev_mutex);
|
||||
@ -1925,7 +1904,6 @@ void msm_dsi_host_destroy(struct mipi_dsi_host *host)
|
||||
DBG("");
|
||||
dsi_tx_buf_free(msm_host);
|
||||
if (msm_host->workqueue) {
|
||||
flush_workqueue(msm_host->workqueue);
|
||||
destroy_workqueue(msm_host->workqueue);
|
||||
msm_host->workqueue = NULL;
|
||||
}
|
||||
@ -1941,25 +1919,8 @@ int msm_dsi_host_modeset_init(struct mipi_dsi_host *host,
|
||||
{
|
||||
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
|
||||
const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
|
||||
struct platform_device *pdev = msm_host->pdev;
|
||||
int ret;
|
||||
|
||||
msm_host->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
if (msm_host->irq < 0) {
|
||||
ret = msm_host->irq;
|
||||
DRM_DEV_ERROR(dev->dev, "failed to get irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, msm_host->irq,
|
||||
dsi_host_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
"dsi_isr", msm_host);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(&pdev->dev, "failed to request IRQ%u: %d\n",
|
||||
msm_host->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msm_host->dev = dev;
|
||||
ret = cfg_hnd->ops->tx_buf_alloc(msm_host, SZ_4K);
|
||||
if (ret) {
|
||||
@ -2315,6 +2276,20 @@ void msm_dsi_host_get_phy_clk_req(struct mipi_dsi_host *host,
|
||||
clk_req->escclk_rate = msm_host->esc_clk_rate;
|
||||
}
|
||||
|
||||
void msm_dsi_host_enable_irq(struct mipi_dsi_host *host)
|
||||
{
|
||||
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
|
||||
|
||||
enable_irq(msm_host->irq);
|
||||
}
|
||||
|
||||
void msm_dsi_host_disable_irq(struct mipi_dsi_host *host)
|
||||
{
|
||||
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
|
||||
|
||||
disable_irq(msm_host->irq);
|
||||
}
|
||||
|
||||
int msm_dsi_host_enable(struct mipi_dsi_host *host)
|
||||
{
|
||||
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
|
||||
|
@ -3,6 +3,8 @@
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "drm/drm_bridge_connector.h"
|
||||
|
||||
#include "msm_kms.h"
|
||||
#include "dsi.h"
|
||||
|
||||
@ -377,6 +379,14 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable before preparing the panel, disable after unpreparing, so
|
||||
* that the panel can communicate over the DSI link.
|
||||
*/
|
||||
msm_dsi_host_enable_irq(host);
|
||||
if (is_bonded_dsi && msm_dsi1)
|
||||
msm_dsi_host_enable_irq(msm_dsi1->host);
|
||||
|
||||
/* Always call panel functions once, because even for dual panels,
|
||||
* there is only one drm_panel instance.
|
||||
*/
|
||||
@ -411,6 +421,10 @@ host_en_fail:
|
||||
if (panel)
|
||||
drm_panel_unprepare(panel);
|
||||
panel_prep_fail:
|
||||
msm_dsi_host_disable_irq(host);
|
||||
if (is_bonded_dsi && msm_dsi1)
|
||||
msm_dsi_host_disable_irq(msm_dsi1->host);
|
||||
|
||||
if (is_bonded_dsi && msm_dsi1)
|
||||
msm_dsi_host_power_off(msm_dsi1->host);
|
||||
host1_on_fail:
|
||||
@ -523,6 +537,10 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge)
|
||||
id, ret);
|
||||
}
|
||||
|
||||
msm_dsi_host_disable_irq(host);
|
||||
if (is_bonded_dsi && msm_dsi1)
|
||||
msm_dsi_host_disable_irq(msm_dsi1->host);
|
||||
|
||||
/* Save PHY status if it is a clock source */
|
||||
msm_dsi_phy_pll_save_state(msm_dsi->phy);
|
||||
|
||||
@ -688,10 +706,10 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id)
|
||||
{
|
||||
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
|
||||
struct drm_device *dev = msm_dsi->dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_bridge *int_bridge, *ext_bridge;
|
||||
struct drm_connector *connector;
|
||||
struct list_head *connector_list;
|
||||
int ret;
|
||||
|
||||
int_bridge = msm_dsi->bridge;
|
||||
ext_bridge = msm_dsi->external_bridge =
|
||||
@ -699,22 +717,44 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id)
|
||||
|
||||
encoder = msm_dsi->encoder;
|
||||
|
||||
/* link the internal dsi bridge to the external bridge */
|
||||
drm_bridge_attach(encoder, ext_bridge, int_bridge, 0);
|
||||
|
||||
/*
|
||||
* we need the drm_connector created by the external bridge
|
||||
* driver (or someone else) to feed it to our driver's
|
||||
* priv->connector[] list, mainly for msm_fbdev_init()
|
||||
* Try first to create the bridge without it creating its own
|
||||
* connector.. currently some bridges support this, and others
|
||||
* do not (and some support both modes)
|
||||
*/
|
||||
connector_list = &dev->mode_config.connector_list;
|
||||
ret = drm_bridge_attach(encoder, ext_bridge, int_bridge,
|
||||
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (ret == -EINVAL) {
|
||||
struct drm_connector *connector;
|
||||
struct list_head *connector_list;
|
||||
|
||||
list_for_each_entry(connector, connector_list, head) {
|
||||
if (drm_connector_has_possible_encoder(connector, encoder))
|
||||
return connector;
|
||||
/* link the internal dsi bridge to the external bridge */
|
||||
drm_bridge_attach(encoder, ext_bridge, int_bridge, 0);
|
||||
|
||||
/*
|
||||
* we need the drm_connector created by the external bridge
|
||||
* driver (or someone else) to feed it to our driver's
|
||||
* priv->connector[] list, mainly for msm_fbdev_init()
|
||||
*/
|
||||
connector_list = &dev->mode_config.connector_list;
|
||||
|
||||
list_for_each_entry(connector, connector_list, head) {
|
||||
if (drm_connector_has_possible_encoder(connector, encoder))
|
||||
return connector;
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENODEV);
|
||||
connector = drm_bridge_connector_init(dev, encoder);
|
||||
if (IS_ERR(connector)) {
|
||||
DRM_ERROR("Unable to create bridge connector\n");
|
||||
return ERR_CAST(connector);
|
||||
}
|
||||
|
||||
drm_connector_attach_encoder(connector, encoder);
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
||||
void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge)
|
||||
|
@ -627,6 +627,8 @@ static const struct of_device_id dsi_phy_dt_match[] = {
|
||||
.data = &dsi_phy_14nm_cfgs },
|
||||
{ .compatible = "qcom,dsi-phy-14nm-660",
|
||||
.data = &dsi_phy_14nm_660_cfgs },
|
||||
{ .compatible = "qcom,dsi-phy-14nm-8953",
|
||||
.data = &dsi_phy_14nm_8953_cfgs },
|
||||
#endif
|
||||
#ifdef CONFIG_DRM_MSM_DSI_10NM_PHY
|
||||
{ .compatible = "qcom,dsi-phy-10nm",
|
||||
|
@ -48,6 +48,7 @@ extern const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs;
|
||||
extern const struct msm_dsi_phy_cfg dsi_phy_28nm_8960_cfgs;
|
||||
extern const struct msm_dsi_phy_cfg dsi_phy_14nm_cfgs;
|
||||
extern const struct msm_dsi_phy_cfg dsi_phy_14nm_660_cfgs;
|
||||
extern const struct msm_dsi_phy_cfg dsi_phy_14nm_8953_cfgs;
|
||||
extern const struct msm_dsi_phy_cfg dsi_phy_10nm_cfgs;
|
||||
extern const struct msm_dsi_phy_cfg dsi_phy_10nm_8998_cfgs;
|
||||
extern const struct msm_dsi_phy_cfg dsi_phy_7nm_cfgs;
|
||||
|
@ -213,9 +213,7 @@ static void pll_14nm_dec_frac_calc(struct dsi_pll_14nm *pll, struct dsi_pll_conf
|
||||
DBG("vco_clk_rate=%lld ref_clk_rate=%lld", vco_clk_rate, fref);
|
||||
|
||||
dec_start_multiple = div_u64(vco_clk_rate * multiplier, fref);
|
||||
div_u64_rem(dec_start_multiple, multiplier, &div_frac_start);
|
||||
|
||||
dec_start = div_u64(dec_start_multiple, multiplier);
|
||||
dec_start = div_u64_rem(dec_start_multiple, multiplier, &div_frac_start);
|
||||
|
||||
pconf->dec_start = (u32)dec_start;
|
||||
pconf->div_frac_start = div_frac_start;
|
||||
@ -1065,3 +1063,24 @@ const struct msm_dsi_phy_cfg dsi_phy_14nm_660_cfgs = {
|
||||
.io_start = { 0xc994400, 0xc996000 },
|
||||
.num_dsi_phy = 2,
|
||||
};
|
||||
|
||||
const struct msm_dsi_phy_cfg dsi_phy_14nm_8953_cfgs = {
|
||||
.has_phy_lane = true,
|
||||
.reg_cfg = {
|
||||
.num = 1,
|
||||
.regs = {
|
||||
{"vcca", 17000, 32},
|
||||
},
|
||||
},
|
||||
.ops = {
|
||||
.enable = dsi_14nm_phy_enable,
|
||||
.disable = dsi_14nm_phy_disable,
|
||||
.pll_init = dsi_pll_14nm_init,
|
||||
.save_pll_state = dsi_14nm_pll_save_state,
|
||||
.restore_pll_state = dsi_14nm_pll_restore_state,
|
||||
},
|
||||
.min_pll_rate = VCO_MIN_RATE,
|
||||
.max_pll_rate = VCO_MAX_RATE,
|
||||
.io_start = { 0x1a94400, 0x1a96400 },
|
||||
.num_dsi_phy = 2,
|
||||
};
|
||||
|
@ -114,9 +114,7 @@ static void dsi_pll_calc_dec_frac(struct dsi_pll_7nm *pll, struct dsi_pll_config
|
||||
|
||||
multiplier = 1 << FRAC_BITS;
|
||||
dec_multiple = div_u64(pll_freq * multiplier, divider);
|
||||
div_u64_rem(dec_multiple, multiplier, &frac);
|
||||
|
||||
dec = div_u64(dec_multiple, multiplier);
|
||||
dec = div_u64_rem(dec_multiple, multiplier, &frac);
|
||||
|
||||
if (!(pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V4_1))
|
||||
config->pll_clock_inverters = 0x28;
|
||||
|
@ -1190,7 +1190,6 @@ void msm_edp_ctrl_destroy(struct edp_ctrl *ctrl)
|
||||
return;
|
||||
|
||||
if (ctrl->workqueue) {
|
||||
flush_workqueue(ctrl->workqueue);
|
||||
destroy_workqueue(ctrl->workqueue);
|
||||
ctrl->workqueue = NULL;
|
||||
}
|
||||
@ -1243,8 +1242,6 @@ bool msm_edp_ctrl_panel_connected(struct edp_ctrl *ctrl)
|
||||
int msm_edp_ctrl_get_panel_info(struct edp_ctrl *ctrl,
|
||||
struct drm_connector *connector, struct edid **edid)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&ctrl->dev_mutex);
|
||||
|
||||
if (ctrl->edid) {
|
||||
@ -1279,7 +1276,7 @@ disable_ret:
|
||||
}
|
||||
unlock_ret:
|
||||
mutex_unlock(&ctrl->dev_mutex);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_edp_ctrl_timing_cfg(struct edp_ctrl *ctrl,
|
||||
|
@ -61,10 +61,8 @@ static void msm_hdmi_destroy(struct hdmi *hdmi)
|
||||
* at this point, hpd has been disabled,
|
||||
* after flush workq, it's safe to deinit hdcp
|
||||
*/
|
||||
if (hdmi->workq) {
|
||||
flush_workqueue(hdmi->workq);
|
||||
if (hdmi->workq)
|
||||
destroy_workqueue(hdmi->workq);
|
||||
}
|
||||
msm_hdmi_hdcp_destroy(hdmi);
|
||||
|
||||
if (hdmi->phy_dev) {
|
||||
@ -154,19 +152,13 @@ static struct hdmi *msm_hdmi_init(struct platform_device *pdev)
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
for (i = 0; i < config->hpd_reg_cnt; i++) {
|
||||
struct regulator *reg;
|
||||
for (i = 0; i < config->hpd_reg_cnt; i++)
|
||||
hdmi->hpd_regs[i].supply = config->hpd_reg_names[i];
|
||||
|
||||
reg = devm_regulator_get(&pdev->dev,
|
||||
config->hpd_reg_names[i]);
|
||||
if (IS_ERR(reg)) {
|
||||
ret = PTR_ERR(reg);
|
||||
DRM_DEV_ERROR(&pdev->dev, "failed to get hpd regulator: %s (%d)\n",
|
||||
config->hpd_reg_names[i], ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->hpd_regs[i] = reg;
|
||||
ret = devm_regulator_bulk_get(&pdev->dev, config->hpd_reg_cnt, hdmi->hpd_regs);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(&pdev->dev, "failed to get hpd regulator: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->pwr_regs = devm_kcalloc(&pdev->dev,
|
||||
@ -177,19 +169,11 @@ static struct hdmi *msm_hdmi_init(struct platform_device *pdev)
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
for (i = 0; i < config->pwr_reg_cnt; i++) {
|
||||
struct regulator *reg;
|
||||
|
||||
reg = devm_regulator_get(&pdev->dev,
|
||||
config->pwr_reg_names[i]);
|
||||
if (IS_ERR(reg)) {
|
||||
ret = PTR_ERR(reg);
|
||||
DRM_DEV_ERROR(&pdev->dev, "failed to get pwr regulator: %s (%d)\n",
|
||||
config->pwr_reg_names[i], ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->pwr_regs[i] = reg;
|
||||
ret = devm_regulator_bulk_get(&pdev->dev, config->pwr_reg_cnt, hdmi->pwr_regs);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(&pdev->dev, "failed to get pwr regulator: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdmi->hpd_clks = devm_kcalloc(&pdev->dev,
|
||||
|
@ -56,8 +56,8 @@ struct hdmi {
|
||||
void __iomem *qfprom_mmio;
|
||||
phys_addr_t mmio_phy_addr;
|
||||
|
||||
struct regulator **hpd_regs;
|
||||
struct regulator **pwr_regs;
|
||||
struct regulator_bulk_data *hpd_regs;
|
||||
struct regulator_bulk_data *pwr_regs;
|
||||
struct clk **hpd_clks;
|
||||
struct clk **pwr_clks;
|
||||
|
||||
@ -163,7 +163,7 @@ struct hdmi_phy {
|
||||
void __iomem *mmio;
|
||||
struct hdmi_phy_cfg *cfg;
|
||||
const struct hdmi_phy_funcs *funcs;
|
||||
struct regulator **regs;
|
||||
struct regulator_bulk_data *regs;
|
||||
struct clk **clks;
|
||||
};
|
||||
|
||||
|
@ -28,13 +28,9 @@ static void msm_hdmi_power_on(struct drm_bridge *bridge)
|
||||
|
||||
pm_runtime_get_sync(&hdmi->pdev->dev);
|
||||
|
||||
for (i = 0; i < config->pwr_reg_cnt; i++) {
|
||||
ret = regulator_enable(hdmi->pwr_regs[i]);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev->dev, "failed to enable pwr regulator: %s (%d)\n",
|
||||
config->pwr_reg_names[i], ret);
|
||||
}
|
||||
}
|
||||
ret = regulator_bulk_enable(config->pwr_reg_cnt, hdmi->pwr_regs);
|
||||
if (ret)
|
||||
DRM_DEV_ERROR(dev->dev, "failed to enable pwr regulator: %d\n", ret);
|
||||
|
||||
if (config->pwr_clk_cnt > 0) {
|
||||
DBG("pixclock: %lu", hdmi->pixclock);
|
||||
@ -70,13 +66,9 @@ static void power_off(struct drm_bridge *bridge)
|
||||
for (i = 0; i < config->pwr_clk_cnt; i++)
|
||||
clk_disable_unprepare(hdmi->pwr_clks[i]);
|
||||
|
||||
for (i = 0; i < config->pwr_reg_cnt; i++) {
|
||||
ret = regulator_disable(hdmi->pwr_regs[i]);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev->dev, "failed to disable pwr regulator: %s (%d)\n",
|
||||
config->pwr_reg_names[i], ret);
|
||||
}
|
||||
}
|
||||
ret = regulator_bulk_disable(config->pwr_reg_cnt, hdmi->pwr_regs);
|
||||
if (ret)
|
||||
DRM_DEV_ERROR(dev->dev, "failed to disable pwr regulator: %d\n", ret);
|
||||
|
||||
pm_runtime_put_autosuspend(&hdmi->pdev->dev);
|
||||
}
|
||||
|
@ -146,16 +146,13 @@ int msm_hdmi_hpd_enable(struct drm_connector *connector)
|
||||
const struct hdmi_platform_config *config = hdmi->config;
|
||||
struct device *dev = &hdmi->pdev->dev;
|
||||
uint32_t hpd_ctrl;
|
||||
int i, ret;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < config->hpd_reg_cnt; i++) {
|
||||
ret = regulator_enable(hdmi->hpd_regs[i]);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to enable hpd regulator: %s (%d)\n",
|
||||
config->hpd_reg_names[i], ret);
|
||||
goto fail;
|
||||
}
|
||||
ret = regulator_bulk_enable(config->hpd_reg_cnt, hdmi->hpd_regs);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to enable hpd regulators: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = pinctrl_pm_select_default_state(dev);
|
||||
@ -207,7 +204,7 @@ static void hdp_disable(struct hdmi_connector *hdmi_connector)
|
||||
struct hdmi *hdmi = hdmi_connector->hdmi;
|
||||
const struct hdmi_platform_config *config = hdmi->config;
|
||||
struct device *dev = &hdmi->pdev->dev;
|
||||
int i, ret = 0;
|
||||
int ret;
|
||||
|
||||
/* Disable HPD interrupt */
|
||||
hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);
|
||||
@ -225,12 +222,9 @@ static void hdp_disable(struct hdmi_connector *hdmi_connector)
|
||||
if (ret)
|
||||
dev_warn(dev, "pinctrl state chg failed: %d\n", ret);
|
||||
|
||||
for (i = 0; i < config->hpd_reg_cnt; i++) {
|
||||
ret = regulator_disable(hdmi->hpd_regs[i]);
|
||||
if (ret)
|
||||
dev_warn(dev, "failed to disable hpd regulator: %s (%d)\n",
|
||||
config->hpd_reg_names[i], ret);
|
||||
}
|
||||
ret = regulator_bulk_disable(config->hpd_reg_cnt, hdmi->hpd_regs);
|
||||
if (ret)
|
||||
dev_warn(dev, "failed to disable hpd regulator: %d\n", ret);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -23,22 +23,15 @@ static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy)
|
||||
if (!phy->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < cfg->num_regs; i++) {
|
||||
struct regulator *reg;
|
||||
for (i = 0; i < cfg->num_regs; i++)
|
||||
phy->regs[i].supply = cfg->reg_names[i];
|
||||
|
||||
reg = devm_regulator_get(dev, cfg->reg_names[i]);
|
||||
if (IS_ERR(reg)) {
|
||||
ret = PTR_ERR(reg);
|
||||
if (ret != -EPROBE_DEFER) {
|
||||
DRM_DEV_ERROR(dev,
|
||||
"failed to get phy regulator: %s (%d)\n",
|
||||
cfg->reg_names[i], ret);
|
||||
}
|
||||
ret = devm_regulator_bulk_get(dev, cfg->num_regs, phy->regs);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
DRM_DEV_ERROR(dev, "failed to get phy regulators: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy->regs[i] = reg;
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < cfg->num_clks; i++) {
|
||||
@ -66,11 +59,10 @@ int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy)
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
for (i = 0; i < cfg->num_regs; i++) {
|
||||
ret = regulator_enable(phy->regs[i]);
|
||||
if (ret)
|
||||
DRM_DEV_ERROR(dev, "failed to enable regulator: %s (%d)\n",
|
||||
cfg->reg_names[i], ret);
|
||||
ret = regulator_bulk_enable(cfg->num_regs, phy->regs);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "failed to enable regulators: (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < cfg->num_clks; i++) {
|
||||
@ -92,8 +84,7 @@ void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy)
|
||||
for (i = cfg->num_clks - 1; i >= 0; i--)
|
||||
clk_disable_unprepare(phy->clks[i]);
|
||||
|
||||
for (i = cfg->num_regs - 1; i >= 0; i--)
|
||||
regulator_disable(phy->regs[i]);
|
||||
regulator_bulk_disable(cfg->num_regs, phy->regs);
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
}
|
||||
|
@ -682,7 +682,7 @@ static int hdmi_8996_pll_is_enabled(struct clk_hw *hw)
|
||||
return pll_locked;
|
||||
}
|
||||
|
||||
static struct clk_ops hdmi_8996_pll_ops = {
|
||||
static const struct clk_ops hdmi_8996_pll_ops = {
|
||||
.set_rate = hdmi_8996_pll_set_clk_rate,
|
||||
.round_rate = hdmi_8996_pll_round_rate,
|
||||
.recalc_rate = hdmi_8996_pll_recalc_rate,
|
||||
@ -695,7 +695,7 @@ static const char * const hdmi_pll_parents[] = {
|
||||
"xo",
|
||||
};
|
||||
|
||||
static struct clk_init_data pll_init = {
|
||||
static const struct clk_init_data pll_init = {
|
||||
.name = "hdmipll",
|
||||
.ops = &hdmi_8996_pll_ops,
|
||||
.parent_names = hdmi_pll_parents,
|
||||
|
@ -116,20 +116,10 @@ out:
|
||||
trace_msm_atomic_async_commit_finish(crtc_mask);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart msm_atomic_pending_timer(struct hrtimer *t)
|
||||
{
|
||||
struct msm_pending_timer *timer = container_of(t,
|
||||
struct msm_pending_timer, timer);
|
||||
|
||||
kthread_queue_work(timer->worker, &timer->work);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static void msm_atomic_pending_work(struct kthread_work *work)
|
||||
{
|
||||
struct msm_pending_timer *timer = container_of(work,
|
||||
struct msm_pending_timer, work);
|
||||
struct msm_pending_timer, work.work);
|
||||
|
||||
msm_atomic_async_commit(timer->kms, timer->crtc_idx);
|
||||
}
|
||||
@ -139,8 +129,6 @@ int msm_atomic_init_pending_timer(struct msm_pending_timer *timer,
|
||||
{
|
||||
timer->kms = kms;
|
||||
timer->crtc_idx = crtc_idx;
|
||||
hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
timer->timer.function = msm_atomic_pending_timer;
|
||||
|
||||
timer->worker = kthread_create_worker(0, "atomic-worker-%d", crtc_idx);
|
||||
if (IS_ERR(timer->worker)) {
|
||||
@ -149,7 +137,10 @@ int msm_atomic_init_pending_timer(struct msm_pending_timer *timer,
|
||||
return ret;
|
||||
}
|
||||
sched_set_fifo(timer->worker->task);
|
||||
kthread_init_work(&timer->work, msm_atomic_pending_work);
|
||||
|
||||
msm_hrtimer_work_init(&timer->work, timer->worker,
|
||||
msm_atomic_pending_work,
|
||||
CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -258,7 +249,7 @@ void msm_atomic_commit_tail(struct drm_atomic_state *state)
|
||||
vsync_time = kms->funcs->vsync_time(kms, async_crtc);
|
||||
wakeup_time = ktime_sub(vsync_time, ms_to_ktime(1));
|
||||
|
||||
hrtimer_start(&timer->timer, wakeup_time,
|
||||
msm_hrtimer_queue_work(&timer->work, wakeup_time,
|
||||
HRTIMER_MODE_ABS);
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ static const struct drm_mode_config_helper_funcs mode_config_helper_funcs = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DRM_MSM_REGISTER_LOGGING
|
||||
static bool reglog = false;
|
||||
static bool reglog;
|
||||
MODULE_PARM_DESC(reglog, "Enable register read/write logging");
|
||||
module_param(reglog, bool, 0600);
|
||||
#else
|
||||
@ -75,7 +75,7 @@ static char *vram = "16m";
|
||||
MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU)");
|
||||
module_param(vram, charp, 0);
|
||||
|
||||
bool dumpstate = false;
|
||||
bool dumpstate;
|
||||
MODULE_PARM_DESC(dumpstate, "Dump KMS state on errors");
|
||||
module_param(dumpstate, bool, 0600);
|
||||
|
||||
@ -200,6 +200,35 @@ void msm_rmw(void __iomem *addr, u32 mask, u32 or)
|
||||
msm_writel(val | or, addr);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart msm_hrtimer_worktimer(struct hrtimer *t)
|
||||
{
|
||||
struct msm_hrtimer_work *work = container_of(t,
|
||||
struct msm_hrtimer_work, timer);
|
||||
|
||||
kthread_queue_work(work->worker, &work->work);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
void msm_hrtimer_queue_work(struct msm_hrtimer_work *work,
|
||||
ktime_t wakeup_time,
|
||||
enum hrtimer_mode mode)
|
||||
{
|
||||
hrtimer_start(&work->timer, wakeup_time, mode);
|
||||
}
|
||||
|
||||
void msm_hrtimer_work_init(struct msm_hrtimer_work *work,
|
||||
struct kthread_worker *worker,
|
||||
kthread_work_func_t fn,
|
||||
clockid_t clock_id,
|
||||
enum hrtimer_mode mode)
|
||||
{
|
||||
hrtimer_init(&work->timer, clock_id, mode);
|
||||
work->timer.function = msm_hrtimer_worktimer;
|
||||
work->worker = worker;
|
||||
kthread_init_work(&work->work, fn);
|
||||
}
|
||||
|
||||
static irqreturn_t msm_irq(int irq, void *arg)
|
||||
{
|
||||
struct drm_device *dev = arg;
|
||||
|
@ -60,6 +60,13 @@ enum msm_mdp_plane_property {
|
||||
PLANE_PROP_MAX_NUM
|
||||
};
|
||||
|
||||
enum msm_dp_controller {
|
||||
MSM_DP_CONTROLLER_0,
|
||||
MSM_DP_CONTROLLER_1,
|
||||
MSM_DP_CONTROLLER_2,
|
||||
MSM_DP_CONTROLLER_COUNT,
|
||||
};
|
||||
|
||||
#define MSM_GPU_MAX_RINGS 4
|
||||
#define MAX_H_TILES_PER_DISPLAY 2
|
||||
|
||||
@ -153,7 +160,7 @@ struct msm_drm_private {
|
||||
/* DSI is shared by mdp4 and mdp5 */
|
||||
struct msm_dsi *dsi[2];
|
||||
|
||||
struct msm_dp *dp;
|
||||
struct msm_dp *dp[MSM_DP_CONTROLLER_COUNT];
|
||||
|
||||
/* when we have more than one 'msm_gpu' these need to be an array: */
|
||||
struct msm_gpu *gpu;
|
||||
@ -480,6 +487,28 @@ void msm_writel(u32 data, void __iomem *addr);
|
||||
u32 msm_readl(const void __iomem *addr);
|
||||
void msm_rmw(void __iomem *addr, u32 mask, u32 or);
|
||||
|
||||
/**
|
||||
* struct msm_hrtimer_work - a helper to combine an hrtimer with kthread_work
|
||||
*
|
||||
* @timer: hrtimer to control when the kthread work is triggered
|
||||
* @work: the kthread work
|
||||
* @worker: the kthread worker the work will be scheduled on
|
||||
*/
|
||||
struct msm_hrtimer_work {
|
||||
struct hrtimer timer;
|
||||
struct kthread_work work;
|
||||
struct kthread_worker *worker;
|
||||
};
|
||||
|
||||
void msm_hrtimer_queue_work(struct msm_hrtimer_work *work,
|
||||
ktime_t wakeup_time,
|
||||
enum hrtimer_mode mode);
|
||||
void msm_hrtimer_work_init(struct msm_hrtimer_work *work,
|
||||
struct kthread_worker *worker,
|
||||
kthread_work_func_t fn,
|
||||
clockid_t clock_id,
|
||||
enum hrtimer_mode mode);
|
||||
|
||||
#define DBG(fmt, ...) DRM_DEBUG_DRIVER(fmt"\n", ##__VA_ARGS__)
|
||||
#define VERB(fmt, ...) if (0) DRM_DEBUG_DRIVER(fmt"\n", ##__VA_ARGS__)
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/dma-buf.h>
|
||||
@ -1132,6 +1133,7 @@ static int msm_gem_new_impl(struct drm_device *dev,
|
||||
msm_obj->flags = flags;
|
||||
msm_obj->madv = MSM_MADV_WILLNEED;
|
||||
|
||||
INIT_LIST_HEAD(&msm_obj->node);
|
||||
INIT_LIST_HEAD(&msm_obj->vmas);
|
||||
|
||||
*obj = &msm_obj->base;
|
||||
@ -1166,7 +1168,7 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, uint32_t size, uint32
|
||||
|
||||
ret = msm_gem_new_impl(dev, size, flags, &obj);
|
||||
if (ret)
|
||||
goto fail;
|
||||
return ERR_PTR(ret);
|
||||
|
||||
msm_obj = to_msm_bo(obj);
|
||||
|
||||
@ -1250,7 +1252,7 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
|
||||
|
||||
ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj);
|
||||
if (ret)
|
||||
goto fail;
|
||||
return ERR_PTR(ret);
|
||||
|
||||
drm_gem_private_object_init(dev, obj, size);
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_gem.h"
|
||||
#include "msm_gpu.h"
|
||||
|
@ -296,7 +296,7 @@ static void msm_gpu_crashstate_capture(struct msm_gpu *gpu,
|
||||
state->bos = kcalloc(nr,
|
||||
sizeof(struct msm_gpu_state_bo), GFP_KERNEL);
|
||||
|
||||
for (i = 0; i < submit->nr_bos; i++) {
|
||||
for (i = 0; state->bos && i < submit->nr_bos; i++) {
|
||||
if (should_dump(submit, i)) {
|
||||
msm_gpu_crashstate_get_bo(state, submit->bos[i].obj,
|
||||
submit->bos[i].iova, submit->bos[i].flags);
|
||||
|
@ -112,6 +112,13 @@ struct msm_gpu_devfreq {
|
||||
* it is inactive.
|
||||
*/
|
||||
unsigned long idle_freq;
|
||||
|
||||
/**
|
||||
* idle_work:
|
||||
*
|
||||
* Used to delay clamping to idle freq on active->idle transition.
|
||||
*/
|
||||
struct msm_hrtimer_work idle_work;
|
||||
};
|
||||
|
||||
struct msm_gpu {
|
||||
|
@ -88,8 +88,12 @@ static struct devfreq_dev_profile msm_devfreq_profile = {
|
||||
.get_cur_freq = msm_devfreq_get_cur_freq,
|
||||
};
|
||||
|
||||
static void msm_devfreq_idle_work(struct kthread_work *work);
|
||||
|
||||
void msm_devfreq_init(struct msm_gpu *gpu)
|
||||
{
|
||||
struct msm_gpu_devfreq *df = &gpu->devfreq;
|
||||
|
||||
/* We need target support to do devfreq */
|
||||
if (!gpu->funcs->gpu_busy)
|
||||
return;
|
||||
@ -105,25 +109,27 @@ void msm_devfreq_init(struct msm_gpu *gpu)
|
||||
msm_devfreq_profile.freq_table = NULL;
|
||||
msm_devfreq_profile.max_state = 0;
|
||||
|
||||
gpu->devfreq.devfreq = devm_devfreq_add_device(&gpu->pdev->dev,
|
||||
df->devfreq = devm_devfreq_add_device(&gpu->pdev->dev,
|
||||
&msm_devfreq_profile, DEVFREQ_GOV_SIMPLE_ONDEMAND,
|
||||
NULL);
|
||||
|
||||
if (IS_ERR(gpu->devfreq.devfreq)) {
|
||||
if (IS_ERR(df->devfreq)) {
|
||||
DRM_DEV_ERROR(&gpu->pdev->dev, "Couldn't initialize GPU devfreq\n");
|
||||
gpu->devfreq.devfreq = NULL;
|
||||
df->devfreq = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
devfreq_suspend_device(gpu->devfreq.devfreq);
|
||||
devfreq_suspend_device(df->devfreq);
|
||||
|
||||
gpu->cooling = of_devfreq_cooling_register(gpu->pdev->dev.of_node,
|
||||
gpu->devfreq.devfreq);
|
||||
gpu->cooling = of_devfreq_cooling_register(gpu->pdev->dev.of_node, df->devfreq);
|
||||
if (IS_ERR(gpu->cooling)) {
|
||||
DRM_DEV_ERROR(&gpu->pdev->dev,
|
||||
"Couldn't register GPU cooling device\n");
|
||||
gpu->cooling = NULL;
|
||||
}
|
||||
|
||||
msm_hrtimer_work_init(&df->idle_work, gpu->worker, msm_devfreq_idle_work,
|
||||
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
void msm_devfreq_cleanup(struct msm_gpu *gpu)
|
||||
@ -154,6 +160,11 @@ void msm_devfreq_active(struct msm_gpu *gpu)
|
||||
if (!df->devfreq)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Cancel any pending transition to idle frequency:
|
||||
*/
|
||||
hrtimer_cancel(&df->idle_work.timer);
|
||||
|
||||
/*
|
||||
* Hold devfreq lock to synchronize with get_dev_status()/
|
||||
* target() callbacks
|
||||
@ -184,9 +195,12 @@ void msm_devfreq_active(struct msm_gpu *gpu)
|
||||
mutex_unlock(&df->devfreq->lock);
|
||||
}
|
||||
|
||||
void msm_devfreq_idle(struct msm_gpu *gpu)
|
||||
|
||||
static void msm_devfreq_idle_work(struct kthread_work *work)
|
||||
{
|
||||
struct msm_gpu_devfreq *df = &gpu->devfreq;
|
||||
struct msm_gpu_devfreq *df = container_of(work,
|
||||
struct msm_gpu_devfreq, idle_work.work);
|
||||
struct msm_gpu *gpu = container_of(df, struct msm_gpu, devfreq);
|
||||
unsigned long idle_freq, target_freq = 0;
|
||||
|
||||
if (!df->devfreq)
|
||||
@ -208,3 +222,11 @@ void msm_devfreq_idle(struct msm_gpu *gpu)
|
||||
|
||||
mutex_unlock(&df->devfreq->lock);
|
||||
}
|
||||
|
||||
void msm_devfreq_idle(struct msm_gpu *gpu)
|
||||
{
|
||||
struct msm_gpu_devfreq *df = &gpu->devfreq;
|
||||
|
||||
msm_hrtimer_queue_work(&df->idle_work, ms_to_ktime(1),
|
||||
HRTIMER_MODE_ABS);
|
||||
}
|
||||
|
@ -136,8 +136,7 @@ struct msm_kms;
|
||||
* shortly before vblank to flush pending async updates.
|
||||
*/
|
||||
struct msm_pending_timer {
|
||||
struct hrtimer timer;
|
||||
struct kthread_work work;
|
||||
struct msm_hrtimer_work work;
|
||||
struct kthread_worker *worker;
|
||||
struct msm_kms *kms;
|
||||
unsigned crtc_idx;
|
||||
|
@ -101,6 +101,7 @@ get_sched_entity(struct msm_file_private *ctx, struct msm_ringbuffer *ring,
|
||||
|
||||
ret = drm_sched_entity_init(entity, sched_prio, &sched, 1, NULL);
|
||||
if (ret) {
|
||||
mutex_unlock(&entity_lock);
|
||||
kfree(entity);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
@ -186,10 +186,8 @@ static void tilcdc_fini(struct drm_device *dev)
|
||||
if (priv->mmio)
|
||||
iounmap(priv->mmio);
|
||||
|
||||
if (priv->wq) {
|
||||
flush_workqueue(priv->wq);
|
||||
if (priv->wq)
|
||||
destroy_workqueue(priv->wq);
|
||||
}
|
||||
|
||||
dev->dev_private = NULL;
|
||||
|
||||
|
@ -468,7 +468,6 @@ void ttm_mem_global_release(struct ttm_mem_global *glob)
|
||||
struct ttm_mem_zone *zone;
|
||||
unsigned int i;
|
||||
|
||||
flush_workqueue(glob->swap_queue);
|
||||
destroy_workqueue(glob->swap_queue);
|
||||
glob->swap_queue = NULL;
|
||||
for (i = 0; i < glob->num_zones; ++i) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user