summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/display/bridge/cdns,mhdp8546.yaml169
-rw-r--r--Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml3
-rw-r--r--Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt4
-rw-r--r--Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/renesas,du.txt6
-rw-r--r--drivers/acpi/acpi_lpss.c22
-rw-r--r--drivers/dma-buf/heaps/heap-helpers.c13
-rw-r--r--drivers/dma-buf/udmabuf.c7
-rw-r--r--drivers/gpu/drm/armada/armada_gem.c24
-rw-r--r--drivers/gpu/drm/bridge/Kconfig2
-rw-r--r--drivers/gpu/drm/bridge/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/cadence/Kconfig24
-rw-r--r--drivers/gpu/drm/bridge/cadence/Makefile4
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c2532
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h400
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.c78
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.h19
-rw-r--r--drivers/gpu/drm/bridge/lvds-codec.c29
-rw-r--r--drivers/gpu/drm/drm_cache.c2
-rw-r--r--drivers/gpu/drm/drm_dp_helper.c394
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c150
-rw-r--r--drivers/gpu/drm/drm_edid.c28
-rw-r--r--drivers/gpu/drm/drm_gem_cma_helper.c23
-rw-r--r--drivers/gpu/drm/drm_gem_shmem_helper.c14
-rw-r--r--drivers/gpu/drm/drm_prime.c91
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem.c12
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_mmu.c15
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dma.c27
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dsi.c7
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_g2d.c10
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.c23
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c7
-rw-r--r--drivers/gpu/drm/i915/Makefile1
-rw-r--r--drivers/gpu/drm/i915/display/icl_dsi.c1
-rw-r--r--drivers/gpu/drm/i915/display/intel_atomic.c39
-rw-r--r--drivers/gpu/drm/i915/display/intel_atomic.h4
-rw-r--r--drivers/gpu/drm/i915/display/intel_audio.c5
-rw-r--r--drivers/gpu/drm/i915/display/intel_bios.c10
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_crt.c3
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.c190
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.c124
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.h6
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_debugfs.c36
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_types.h48
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c1078
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.h12
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_hdcp.c703
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_mst.c23
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpll_mgr.c35
-rw-r--r--drivers/gpu/drm/i915/display/intel_dvo.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbdev.c3
-rw-r--r--drivers/gpu/drm/i915/display/intel_gmbus.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp.c208
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.c117
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_hotplug.c28
-rw-r--r--drivers/gpu/drm/i915/display/intel_lvds.c8
-rw-r--r--drivers/gpu/drm/i915/display/intel_panel.c106
-rw-r--r--drivers/gpu/drm/i915/display/intel_panel.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr.c4
-rw-r--r--drivers/gpu/drm/i915/display/intel_sdvo.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_sprite.c9
-rw-r--r--drivers/gpu/drm/i915/display/intel_tv.c3
-rw-r--r--drivers/gpu/drm/i915/display/intel_vbt_defs.h10
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dsi.c1
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dsi_pll.c2
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c11
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/mock_dmabuf.c7
-rw-r--r--drivers/gpu/drm/i915/gt/intel_workarounds.c35
-rw-r--r--drivers/gpu/drm/i915/gvt/cmd_parser.c6
-rw-r--r--drivers/gpu/drm/i915/gvt/gvt.h44
-rw-r--r--drivers/gpu/drm/i915/gvt/handlers.c32
-rw-r--r--drivers/gpu/drm/i915/gvt/mmio.c3
-rw-r--r--drivers/gpu/drm/i915/gvt/mmio_context.c2
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c155
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h66
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c227
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h38
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c46
-rw-r--r--drivers/gpu/drm/i915/intel_device_info.c8
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c2
-rw-r--r--drivers/gpu/drm/i915/intel_sideband.c16
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.c3
-rw-r--r--drivers/gpu/drm/i915/selftests/mock_gem_device.c12
-rw-r--r--drivers/gpu/drm/lima/lima_gem.c11
-rw-r--r--drivers/gpu/drm/lima/lima_vm.c5
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_gem.c37
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c13
-rw-r--r--drivers/gpu/drm/msm/msm_gpummu.c15
-rw-r--r--drivers/gpu/drm/msm/msm_iommu.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c4
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem.c14
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem.c4
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_mmu.c7
-rw-r--r--drivers/gpu/drm/rcar-du/Kconfig5
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.c37
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.c54
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.h1
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_vsp.c17
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_lvds.c2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_gem.c42
-rw-r--r--drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c17
-rw-r--r--drivers/gpu/drm/tegra/drm.h2
-rw-r--r--drivers/gpu/drm/tegra/gem.c27
-rw-r--r--drivers/gpu/drm/tegra/output.c24
-rw-r--r--drivers/gpu/drm/tegra/plane.c15
-rw-r--r--drivers/gpu/drm/tegra/rgb.c102
-rw-r--r--drivers/gpu/drm/tegra/sor.c7
-rw-r--r--drivers/gpu/drm/v3d/v3d_mmu.c13
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_object.c36
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_vq.c12
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c17
-rw-r--r--drivers/gpu/drm/xen/xen_drm_front_gem.c2
-rw-r--r--drivers/gpu/host1x/job.c22
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.c8
-rw-r--r--drivers/phy/cadence/phy-cadence-torrent.c4
-rw-r--r--drivers/pwm/pwm-crc.c128
-rw-r--r--drivers/pwm/pwm-lpss-platform.c1
-rw-r--r--drivers/pwm/pwm-lpss.c85
-rw-r--r--drivers/pwm/pwm-lpss.h3
-rw-r--r--drivers/xen/gntdev-dmabuf.c13
-rw-r--r--include/drm/drm_dp_helper.h66
-rw-r--r--include/drm/drm_dp_mst_helper.h44
-rw-r--r--include/drm/drm_edid.h4
-rw-r--r--include/drm/drm_prime.h2
-rw-r--r--include/drm/i915_pciids.h14
-rw-r--r--include/linux/phy/phy.h2
-rw-r--r--samples/vfio-mdev/mbochs.c3
133 files changed, 6560 insertions, 2082 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/cdns,mhdp8546.yaml b/Documentation/devicetree/bindings/display/bridge/cdns,mhdp8546.yaml
new file mode 100644
index 000000000000..74d675fc6e7b
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/cdns,mhdp8546.yaml
@@ -0,0 +1,169 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/display/bridge/cdns,mhdp8546.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Cadence MHDP8546 bridge
+
+maintainers:
+ - Swapnil Jakhade <sjakhade@cadence.com>
+ - Yuti Amonkar <yamonkar@cadence.com>
+
+properties:
+ compatible:
+ enum:
+ - cdns,mhdp8546
+ - ti,j721e-mhdp8546
+
+ reg:
+ minItems: 1
+ maxItems: 2
+ items:
+ - description:
+ Register block of mhdptx apb registers up to PHY mapped area (AUX_CONFIG_P).
+ The AUX and PMA registers are not part of this range, they are instead
+ included in the associated PHY.
+ - description:
+ Register block for DSS_EDP0_INTG_CFG_VP registers in case of TI J7 SoCs.
+
+ reg-names:
+ minItems: 1
+ maxItems: 2
+ items:
+ - const: mhdptx
+ - const: j721e-intg
+
+ clocks:
+ maxItems: 1
+ description:
+ DP bridge clock, used by the IP to know how to translate a number of
+ clock cycles into a time (which is used to comply with DP standard timings
+ and delays).
+
+ phys:
+ maxItems: 1
+ description:
+ phandle to the DisplayPort PHY.
+
+ phy-names:
+ items:
+ - const: dpphy
+
+ power-domains:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ ports:
+ type: object
+ description:
+ Ports as described in Documentation/devicetree/bindings/graph.txt.
+
+ properties:
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ port@0:
+ type: object
+ description:
+ First input port representing the DP bridge input.
+
+ port@1:
+ type: object
+ description:
+ Second input port representing the DP bridge input.
+
+ port@2:
+ type: object
+ description:
+ Third input port representing the DP bridge input.
+
+ port@3:
+ type: object
+ description:
+ Fourth input port representing the DP bridge input.
+
+ port@4:
+ type: object
+ description:
+ Output port representing the DP bridge output.
+
+ required:
+ - port@0
+ - port@4
+ - '#address-cells'
+ - '#size-cells'
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ti,j721e-mhdp8546
+ then:
+ properties:
+ reg:
+ minItems: 2
+ reg-names:
+ minItems: 2
+ else:
+ properties:
+ reg:
+ maxItems: 1
+ reg-names:
+ maxItems: 1
+
+required:
+ - compatible
+ - clocks
+ - reg
+ - reg-names
+ - phys
+ - phy-names
+ - interrupts
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ bus {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ mhdp: dp-bridge@f0fb000000 {
+ compatible = "cdns,mhdp8546";
+ reg = <0xf0 0xfb000000 0x0 0x1000000>;
+ reg-names = "mhdptx";
+ clocks = <&mhdp_clock>;
+ phys = <&dp_phy>;
+ phy-names = "dpphy";
+ interrupts = <GIC_SPI 614 IRQ_TYPE_LEVEL_HIGH>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ dp_bridge_input: endpoint {
+ remote-endpoint = <&xxx_dpi_output>;
+ };
+ };
+
+ port@4 {
+ reg = <4>;
+ dp_bridge_output: endpoint {
+ remote-endpoint = <&xxx_dp_connector_input>;
+ };
+ };
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml b/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml
index 68951d56ebba..ff3ae25f8b23 100644
--- a/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml
@@ -79,6 +79,9 @@ properties:
The GPIO used to control the power down line of this device.
maxItems: 1
+ power-supply:
+ maxItems: 1
+
required:
- compatible
- ports
diff --git a/Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt b/Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt
index 819f3e31013c..3f6072651182 100644
--- a/Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt
+++ b/Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt
@@ -14,8 +14,10 @@ Required properties:
- compatible : Shall contain one or more of
- "renesas,r8a774a1-hdmi" for R8A774A1 (RZ/G2M) compatible HDMI TX
- "renesas,r8a774b1-hdmi" for R8A774B1 (RZ/G2N) compatible HDMI TX
+ - "renesas,r8a774e1-hdmi" for R8A774E1 (RZ/G2H) compatible HDMI TX
- "renesas,r8a7795-hdmi" for R8A7795 (R-Car H3) compatible HDMI TX
- "renesas,r8a7796-hdmi" for R8A7796 (R-Car M3-W) compatible HDMI TX
+ - "renesas,r8a77961-hdmi" for R8A77961 (R-Car M3-W+) compatible HDMI TX
- "renesas,r8a77965-hdmi" for R8A77965 (R-Car M3-N) compatible HDMI TX
- "renesas,rcar-gen3-hdmi" for the generic R-Car Gen3 and RZ/G2 compatible
HDMI TX
@@ -42,7 +44,7 @@ Optional properties:
Example:
hdmi0: hdmi@fead0000 {
- compatible = "renesas,r8a7795-dw-hdmi";
+ compatible = "renesas,r8a7795-hdmi", "renesas,rcar-gen3-hdmi";
reg = <0 0xfead0000 0 0x10000>;
interrupts = <0 389 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_CORE R8A7795_CLK_S0D4>, <&cpg CPG_MOD 729>;
diff --git a/Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml b/Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml
index baaf2a2a6fed..e5b163951b91 100644
--- a/Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml
@@ -16,11 +16,13 @@ description: |
properties:
compatible:
enum:
+ - renesas,r8a7742-lvds # for RZ/G1H compatible LVDS encoders
- renesas,r8a7743-lvds # for RZ/G1M compatible LVDS encoders
- renesas,r8a7744-lvds # for RZ/G1N compatible LVDS encoders
- renesas,r8a774a1-lvds # for RZ/G2M compatible LVDS encoders
- renesas,r8a774b1-lvds # for RZ/G2N compatible LVDS encoders
- renesas,r8a774c0-lvds # for RZ/G2E compatible LVDS encoders
+ - renesas,r8a774e1-lvds # for RZ/G2H compatible LVDS encoders
- renesas,r8a7790-lvds # for R-Car H2 compatible LVDS encoders
- renesas,r8a7791-lvds # for R-Car M2-W compatible LVDS encoders
- renesas,r8a7793-lvds # for R-Car M2-N compatible LVDS encoders
diff --git a/Documentation/devicetree/bindings/display/renesas,du.txt b/Documentation/devicetree/bindings/display/renesas,du.txt
index 51cd4d162770..7d65c24fcda8 100644
--- a/Documentation/devicetree/bindings/display/renesas,du.txt
+++ b/Documentation/devicetree/bindings/display/renesas,du.txt
@@ -3,6 +3,7 @@
Required Properties:
- compatible: must be one of the following.
+ - "renesas,du-r8a7742" for R8A7742 (RZ/G1H) compatible DU
- "renesas,du-r8a7743" for R8A7743 (RZ/G1M) compatible DU
- "renesas,du-r8a7744" for R8A7744 (RZ/G1N) compatible DU
- "renesas,du-r8a7745" for R8A7745 (RZ/G1E) compatible DU
@@ -10,6 +11,7 @@ Required Properties:
- "renesas,du-r8a774a1" for R8A774A1 (RZ/G2M) compatible DU
- "renesas,du-r8a774b1" for R8A774B1 (RZ/G2N) compatible DU
- "renesas,du-r8a774c0" for R8A774C0 (RZ/G2E) compatible DU
+ - "renesas,du-r8a774e1" for R8A774E1 (RZ/G2H) compatible DU
- "renesas,du-r8a7779" for R8A7779 (R-Car H1) compatible DU
- "renesas,du-r8a7790" for R8A7790 (R-Car H2) compatible DU
- "renesas,du-r8a7791" for R8A7791 (R-Car M2-W) compatible DU
@@ -18,6 +20,7 @@ Required Properties:
- "renesas,du-r8a7794" for R8A7794 (R-Car E2) compatible DU
- "renesas,du-r8a7795" for R8A7795 (R-Car H3) compatible DU
- "renesas,du-r8a7796" for R8A7796 (R-Car M3-W) compatible DU
+ - "renesas,du-r8a77961" for R8A77961 (R-Car M3-W+) compatible DU
- "renesas,du-r8a77965" for R8A77965 (R-Car M3-N) compatible DU
- "renesas,du-r8a77970" for R8A77970 (R-Car V3M) compatible DU
- "renesas,du-r8a77980" for R8A77980 (R-Car V3H) compatible DU
@@ -68,6 +71,7 @@ corresponding to each DU output.
Port0 Port1 Port2 Port3
-----------------------------------------------------------------------------
+ R8A7742 (RZ/G1H) DPAD 0 LVDS 0 LVDS 1 -
R8A7743 (RZ/G1M) DPAD 0 LVDS 0 - -
R8A7744 (RZ/G1N) DPAD 0 LVDS 0 - -
R8A7745 (RZ/G1E) DPAD 0 DPAD 1 - -
@@ -75,6 +79,7 @@ corresponding to each DU output.
R8A774A1 (RZ/G2M) DPAD 0 HDMI 0 LVDS 0 -
R8A774B1 (RZ/G2N) DPAD 0 HDMI 0 LVDS 0 -
R8A774C0 (RZ/G2E) DPAD 0 LVDS 0 LVDS 1 -
+ R8A774E1 (RZ/G2H) DPAD 0 HDMI 0 LVDS 0 -
R8A7779 (R-Car H1) DPAD 0 DPAD 1 - -
R8A7790 (R-Car H2) DPAD 0 LVDS 0 LVDS 1 -
R8A7791 (R-Car M2-W) DPAD 0 LVDS 0 - -
@@ -83,6 +88,7 @@ corresponding to each DU output.
R8A7794 (R-Car E2) DPAD 0 DPAD 1 - -
R8A7795 (R-Car H3) DPAD 0 HDMI 0 HDMI 1 LVDS 0
R8A7796 (R-Car M3-W) DPAD 0 HDMI 0 LVDS 0 -
+ R8A77961 (R-Car M3-W+) DPAD 0 HDMI 0 LVDS 0 -
R8A77965 (R-Car M3-N) DPAD 0 HDMI 0 LVDS 0 -
R8A77970 (R-Car V3M) DPAD 0 LVDS 0 - -
R8A77980 (R-Car V3H) DPAD 0 LVDS 0 - -
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index 5e2bfbcf526f..a8d7d83ac761 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -67,7 +67,15 @@ ACPI_MODULE_NAME("acpi_lpss");
#define LPSS_CLK_DIVIDER BIT(2)
#define LPSS_LTR BIT(3)
#define LPSS_SAVE_CTX BIT(4)
-#define LPSS_NO_D3_DELAY BIT(5)
+/*
+ * For some devices the DSDT AML code for another device turns off the device
+ * before our suspend handler runs, causing us to read/save all 1-s (0xffffffff)
+ * as ctx register values.
+ * Luckily these devices always use the same ctx register values, so we can
+ * work around this by saving the ctx registers once on activation.
+ */
+#define LPSS_SAVE_CTX_ONCE BIT(5)
+#define LPSS_NO_D3_DELAY BIT(6)
struct lpss_private_data;
@@ -254,9 +262,10 @@ static const struct lpss_device_desc byt_pwm_dev_desc = {
};
static const struct lpss_device_desc bsw_pwm_dev_desc = {
- .flags = LPSS_SAVE_CTX | LPSS_NO_D3_DELAY,
+ .flags = LPSS_SAVE_CTX_ONCE | LPSS_NO_D3_DELAY,
.prv_offset = 0x800,
.setup = bsw_pwm_setup,
+ .resume_from_noirq = true,
};
static const struct lpss_device_desc byt_uart_dev_desc = {
@@ -884,9 +893,14 @@ static int acpi_lpss_activate(struct device *dev)
* we have to deassert reset line to be sure that ->probe() will
* recognize the device.
*/
- if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
+ if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE))
lpss_deassert_reset(pdata);
+#ifdef CONFIG_PM
+ if (pdata->dev_desc->flags & LPSS_SAVE_CTX_ONCE)
+ acpi_lpss_save_ctx(dev, pdata);
+#endif
+
return 0;
}
@@ -1030,7 +1044,7 @@ static int acpi_lpss_resume(struct device *dev)
acpi_lpss_d3_to_d0_delay(pdata);
- if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
+ if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE))
acpi_lpss_restore_ctx(dev, pdata);
return 0;
diff --git a/drivers/dma-buf/heaps/heap-helpers.c b/drivers/dma-buf/heaps/heap-helpers.c
index 9f964ca3f59c..d0696cf937af 100644
--- a/drivers/dma-buf/heaps/heap-helpers.c
+++ b/drivers/dma-buf/heaps/heap-helpers.c
@@ -140,13 +140,12 @@ struct sg_table *dma_heap_map_dma_buf(struct dma_buf_attachment *attachment,
enum dma_data_direction direction)
{
struct dma_heaps_attachment *a = attachment->priv;
- struct sg_table *table;
-
- table = &a->table;
+ struct sg_table *table = &a->table;
+ int ret;
- if (!dma_map_sg(attachment->dev, table->sgl, table->nents,
- direction))
- table = ERR_PTR(-ENOMEM);
+ ret = dma_map_sgtable(attachment->dev, table, direction, 0);
+ if (ret)
+ table = ERR_PTR(ret);
return table;
}
@@ -154,7 +153,7 @@ static void dma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
struct sg_table *table,
enum dma_data_direction direction)
{
- dma_unmap_sg(attachment->dev, table->sgl, table->nents, direction);
+ dma_unmap_sgtable(attachment->dev, table, direction, 0);
}
static vm_fault_t dma_heap_vm_fault(struct vm_fault *vmf)
diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
index acb26c627d27..89e293bd9252 100644
--- a/drivers/dma-buf/udmabuf.c
+++ b/drivers/dma-buf/udmabuf.c
@@ -63,10 +63,9 @@ static struct sg_table *get_sg_table(struct device *dev, struct dma_buf *buf,
GFP_KERNEL);
if (ret < 0)
goto err;
- if (!dma_map_sg(dev, sg->sgl, sg->nents, direction)) {
- ret = -EINVAL;
+ ret = dma_map_sgtable(dev, sg, direction, 0);
+ if (ret < 0)
goto err;
- }
return sg;
err:
@@ -78,7 +77,7 @@ err:
static void put_sg_table(struct device *dev, struct sg_table *sg,
enum dma_data_direction direction)
{
- dma_unmap_sg(dev, sg->sgl, sg->nents, direction);
+ dma_unmap_sgtable(dev, sg, direction, 0);
sg_free_table(sg);
kfree(sg);
}
diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
index 8005614d2e6b..a63008ce284d 100644
--- a/drivers/gpu/drm/armada/armada_gem.c
+++ b/drivers/gpu/drm/armada/armada_gem.c
@@ -379,7 +379,7 @@ armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
struct armada_gem_object *dobj = drm_to_armada_gem(obj);
struct scatterlist *sg;
struct sg_table *sgt;
- int i, num;
+ int i;
sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt)
@@ -395,22 +395,18 @@ armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
mapping = dobj->obj.filp->f_mapping;
- for_each_sg(sgt->sgl, sg, count, i) {
+ for_each_sgtable_sg(sgt, sg, i) {
struct page *page;
page = shmem_read_mapping_page(mapping, i);
- if (IS_ERR(page)) {
- num = i;
+ if (IS_ERR(page))
goto release;
- }
sg_set_page(sg, page, PAGE_SIZE, 0);
}
- if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0) {
- num = sgt->nents;
+ if (dma_map_sgtable(attach->dev, sgt, dir, 0))
goto release;
- }
} else if (dobj->page) {
/* Single contiguous page */
if (sg_alloc_table(sgt, 1, GFP_KERNEL))
@@ -418,7 +414,7 @@ armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
sg_set_page(sgt->sgl, dobj->page, dobj->obj.size, 0);
- if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
+ if (dma_map_sgtable(attach->dev, sgt, dir, 0))
goto free_table;
} else if (dobj->linear) {
/* Single contiguous physical region - no struct page */
@@ -432,8 +428,9 @@ armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
return sgt;
release:
- for_each_sg(sgt->sgl, sg, num, i)
- put_page(sg_page(sg));
+ for_each_sgtable_sg(sgt, sg, i)
+ if (sg_page(sg))
+ put_page(sg_page(sg));
free_table:
sg_free_table(sgt);
free_sgt:
@@ -449,11 +446,12 @@ static void armada_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach,
int i;
if (!dobj->linear)
- dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
+ dma_unmap_sgtable(attach->dev, sgt, dir, 0);
if (dobj->obj.filp) {
struct scatterlist *sg;
- for_each_sg(sgt->sgl, sg, sgt->nents, i)
+
+ for_each_sgtable_sg(sgt, sg, i)
put_page(sg_page(sg));
}
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 3e11af4e9f63..ef91646441b1 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -241,6 +241,8 @@ source "drivers/gpu/drm/bridge/analogix/Kconfig"
source "drivers/gpu/drm/bridge/adv7511/Kconfig"
+source "drivers/gpu/drm/bridge/cadence/Kconfig"
+
source "drivers/gpu/drm/bridge/synopsys/Kconfig"
endmenu
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index c589a6a7cbe1..2b3aff104e46 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -25,4 +25,5 @@ obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o
obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi.o
obj-y += analogix/
+obj-y += cadence/
obj-y += synopsys/
diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig
new file mode 100644
index 000000000000..511d67b16d14
--- /dev/null
+++ b/drivers/gpu/drm/bridge/cadence/Kconfig
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config DRM_CDNS_MHDP8546
+ tristate "Cadence DPI/DP bridge"
+ select DRM_KMS_HELPER
+ select DRM_PANEL_BRIDGE
+ depends on OF
+ help
+ Support Cadence DPI to DP bridge. This is an internal
+ bridge and is meant to be directly embedded in a SoC.
+ It takes a DPI stream as input and outputs it encoded
+ in DP format.
+
+if DRM_CDNS_MHDP8546
+
+config DRM_CDNS_MHDP8546_J721E
+ depends on ARCH_K3_J721E_SOC || COMPILE_TEST
+ bool "J721E Cadence DPI/DP wrapper support"
+ default y
+ help
+ Support J721E Cadence DPI/DP wrapper. This is a wrapper
+ which adds support for J721E related platform ops. It
+ initializes the J721E Display Port and sets up the
+ clock and data muxes.
+endif
diff --git a/drivers/gpu/drm/bridge/cadence/Makefile b/drivers/gpu/drm/bridge/cadence/Makefile
new file mode 100644
index 000000000000..8f647991b374
--- /dev/null
+++ b/drivers/gpu/drm/bridge/cadence/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_DRM_CDNS_MHDP8546) += cdns-mhdp8546.o
+cdns-mhdp8546-y := cdns-mhdp8546-core.o
+cdns-mhdp8546-$(CONFIG_DRM_CDNS_MHDP8546_J721E) += cdns-mhdp8546-j721e.o
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
new file mode 100644
index 000000000000..621ebdbff8a3
--- /dev/null
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
@@ -0,0 +1,2532 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence MHDP8546 DP bridge driver.
+ *
+ * Copyright (C) 2020 Cadence Design Systems, Inc.
+ *
+ * Authors: Quentin Schulz <quentin.schulz@free-electrons.com>
+ * Swapnil Jakhade <sjakhade@cadence.com>
+ * Yuti Amonkar <yamonkar@cadence.com>
+ * Tomi Valkeinen <tomi.valkeinen@ti.com>
+ * Jyri Sarha <jsarha@ti.com>
+ *
+ * TODO:
+ * - Implement optimized mailbox communication using mailbox interrupts
+ * - Add support for power management
+ * - Add support for features like audio, MST and fast link training
+ * - Implement request_fw_cancel to handle HW_STATE
+ * - Fix asynchronous loading of firmware implementation
+ * - Add DRM helper function for cdns_mhdp_lower_link_rate
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-dp.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include <asm/unaligned.h>
+
+#include "cdns-mhdp8546-core.h"
+
+#include "cdns-mhdp8546-j721e.h"
+
+static int cdns_mhdp_mailbox_read(struct cdns_mhdp_device *mhdp)
+{
+ int ret, empty;
+
+ WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex));
+
+ ret = readx_poll_timeout(readl, mhdp->regs + CDNS_MAILBOX_EMPTY,
+ empty, !empty, MAILBOX_RETRY_US,
+ MAILBOX_TIMEOUT_US);
+ if (ret < 0)
+ return ret;
+
+ return readl(mhdp->regs + CDNS_MAILBOX_RX_DATA) & 0xff;
+}
+
+static int cdns_mhdp_mailbox_write(struct cdns_mhdp_device *mhdp, u8 val)
+{
+ int ret, full;
+
+ WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex));
+
+ ret = readx_poll_timeout(readl, mhdp->regs + CDNS_MAILBOX_FULL,
+ full, !full, MAILBOX_RETRY_US,
+ MAILBOX_TIMEOUT_US);
+ if (ret < 0)
+ return ret;
+
+ writel(val, mhdp->regs + CDNS_MAILBOX_TX_DATA);
+
+ return 0;
+}
+
+static int cdns_mhdp_mailbox_recv_header(struct cdns_mhdp_device *mhdp,
+ u8 module_id, u8 opcode,
+ u16 req_size)
+{
+ u32 mbox_size, i;
+ u8 header[4];
+ int ret;
+
+ /* read the header of the message */
+ for (i = 0; i < sizeof(header); i++) {
+ ret = cdns_mhdp_mailbox_read(mhdp);
+ if (ret < 0)
+ return ret;
+
+ header[i] = ret;
+ }
+
+ mbox_size = get_unaligned_be16(header + 2);
+
+ if (opcode != header[0] || module_id != header[1] ||
+ req_size != mbox_size) {
+ /*
+ * If the message in mailbox is not what we want, we need to
+ * clear the mailbox by reading its contents.
+ */
+ for (i = 0; i < mbox_size; i++)
+ if (cdns_mhdp_mailbox_read(mhdp) < 0)
+ break;
+
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cdns_mhdp_mailbox_recv_data(struct cdns_mhdp_device *mhdp,
+ u8 *buff, u16 buff_size)
+{
+ u32 i;
+ int ret;
+
+ for (i = 0; i < buff_size; i++) {
+ ret = cdns_mhdp_mailbox_read(mhdp);
+ if (ret < 0)
+ return ret;
+
+ buff[i] = ret;
+ }
+
+ return 0;
+}
+
+static int cdns_mhdp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 module_id,
+ u8 opcode, u16 size, u8 *message)
+{
+ u8 header[4];
+ int ret, i;
+
+ header[0] = opcode;
+ header[1] = module_id;
+ put_unaligned_be16(size, header + 2);
+
+ for (i = 0; i < sizeof(header); i++) {
+ ret = cdns_mhdp_mailbox_write(mhdp, header[i]);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < size; i++) {
+ ret = cdns_mhdp_mailbox_write(mhdp, message[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static
+int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr, u32 *value)
+{
+ u8 msg[4], resp[8];
+ int ret;
+
+ put_unaligned_be32(addr, msg);
+
+ mutex_lock(&mhdp->mbox_mutex);
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL,
+ GENERAL_REGISTER_READ,
+ sizeof(msg), msg);
+ if (ret)
+ goto out;
+
+ ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_GENERAL,
+ GENERAL_REGISTER_READ,
+ sizeof(resp));
+ if (ret)
+ goto out;
+
+ ret = cdns_mhdp_mailbox_recv_data(mhdp, resp, sizeof(resp));
+ if (ret)
+ goto out;
+
+ /* Returned address value should be the same as requested */
+ if (memcmp(msg, resp, sizeof(msg))) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ *value = get_unaligned_be32(resp + 4);
+
+out:
+ mutex_unlock(&mhdp->mbox_mutex);
+ if (ret) {
+ dev_err(mhdp->dev, "Failed to read register\n");
+ *value = 0;
+ }
+
+ return ret;
+}
+
+static
+int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u16 addr, u32 val)
+{
+ u8 msg[6];
+ int ret;
+
+ put_unaligned_be16(addr, msg);
+ put_unaligned_be32(val, msg + 2);
+
+ mutex_lock(&mhdp->mbox_mutex);
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_WRITE_REGISTER, sizeof(msg), msg);
+
+ mutex_unlock(&mhdp->mbox_mutex);
+
+ return ret;
+}
+
+static
+int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr,
+ u8 start_bit, u8 bits_no, u32 val)
+{
+ u8 field[8];
+ int ret;
+
+ put_unaligned_be16(addr, field);
+ field[2] = start_bit;
+ field[3] = bits_no;
+ put_unaligned_be32(val, field + 4);
+
+ mutex_lock(&mhdp->mbox_mutex);
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_WRITE_FIELD, sizeof(field), field);
+
+ mutex_unlock(&mhdp->mbox_mutex);
+
+ return ret;
+}
+
+static
+int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp,
+ u32 addr, u8 *data, u16 len)
+{
+ u8 msg[5], reg[5];
+ int ret;
+
+ put_unaligned_be16(len, msg);
+ put_unaligned_be24(addr, msg + 2);
+
+ mutex_lock(&mhdp->mbox_mutex);
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_READ_DPCD, sizeof(msg), msg);
+ if (ret)
+ goto out;
+
+ ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_READ_DPCD,
+ sizeof(reg) + len);
+ if (ret)
+ goto out;
+
+ ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg));
+ if (ret)
+ goto out;
+
+ ret = cdns_mhdp_mailbox_recv_data(mhdp, data, len);
+
+out:
+ mutex_unlock(&mhdp->mbox_mutex);
+
+ return ret;
+}
+
+static
+int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value)
+{
+ u8 msg[6], reg[5];
+ int ret;
+
+ put_unaligned_be16(1, msg);
+ put_unaligned_be24(addr, msg + 2);
+ msg[5] = value;
+
+ mutex_lock(&mhdp->mbox_mutex);
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_WRITE_DPCD, sizeof(msg), msg);
+ if (ret)
+ goto out;
+
+ ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_WRITE_DPCD, sizeof(reg));
+ if (ret)
+ goto out;
+
+ ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg));
+ if (ret)
+ goto out;
+
+ if (addr != get_unaligned_be24(reg + 2))
+ ret = -EINVAL;
+
+out:
+ mutex_unlock(&mhdp->mbox_mutex);
+
+ if (ret)
+ dev_err(mhdp->dev, "dpcd write failed: %d\n", ret);
+ return ret;
+}
+
+static
+int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable)
+{
+ u8 msg[5];
+ int ret, i;
+
+ msg[0] = GENERAL_MAIN_CONTROL;
+ msg[1] = MB_MODULE_ID_GENERAL;
+ msg[2] = 0;
+ msg[3] = 1;
+ msg[4] = enable ? FW_ACTIVE : FW_STANDBY;
+
+ mutex_lock(&mhdp->mbox_mutex);
+
+ for (i = 0; i < sizeof(msg); i++) {
+ ret = cdns_mhdp_mailbox_write(mhdp, msg[i]);
+ if (ret)
+ goto out;
+ }
+
+ /* read the firmware state */
+ ret = cdns_mhdp_mailbox_recv_data(mhdp, msg, sizeof(msg));
+ if (ret)
+ goto out;
+
+ ret = 0;
+
+out:
+ mutex_unlock(&mhdp->mbox_mutex);
+
+ if (ret < 0)
+ dev_err(mhdp->dev, "set firmware active failed\n");
+ return ret;
+}
+
+static
+int cdns_mhdp_get_hpd_status(struct cdns_mhdp_device *mhdp)
+{
+ u8 status;
+ int ret;
+
+ mutex_lock(&mhdp->mbox_mutex);
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_HPD_STATE, 0, NULL);
+ if (ret)
+ goto err_get_hpd;
+
+ ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_HPD_STATE,
+ sizeof(status));
+ if (ret)
+ goto err_get_hpd;
+
+ ret = cdns_mhdp_mailbox_recv_data(mhdp, &status, sizeof(status));
+ if (ret)
+ goto err_get_hpd;
+
+ mutex_unlock(&mhdp->mbox_mutex);
+
+ dev_dbg(mhdp->dev, "%s: HPD %splugged\n", __func__,
+ status ? "" : "un");
+
+ return status;
+
+err_get_hpd:
+ mutex_unlock(&mhdp->mbox_mutex);
+
+ return ret;
+}
+
+static
+int cdns_mhdp_get_edid_block(void *data, u8 *edid,
+ unsigned int block, size_t length)
+{
+ struct cdns_mhdp_device *mhdp = data;
+ u8 msg[2], reg[2], i;
+ int ret;
+
+ mutex_lock(&mhdp->mbox_mutex);
+
+ for (i = 0; i < 4; i++) {
+ msg[0] = block / 2;
+ msg[1] = block % 2;
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_GET_EDID, sizeof(msg), msg);
+ if (ret)
+ continue;
+
+ ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_GET_EDID,
+ sizeof(reg) + length);
+ if (ret)
+ continue;
+
+ ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg));
+ if (ret)
+ continue;
+
+ ret = cdns_mhdp_mailbox_recv_data(mhdp, edid, length);
+ if (ret)
+ continue;
+
+ if (reg[0] == length && reg[1] == block / 2)
+ break;
+ }
+
+ mutex_unlock(&mhdp->mbox_mutex);
+
+ if (ret)
+ dev_err(mhdp->dev, "get block[%d] edid failed: %d\n",
+ block, ret);
+
+ return ret;
+}
+
+static
+int cdns_mhdp_read_hpd_event(struct cdns_mhdp_device *mhdp)
+{
+ u8 event = 0;
+ int ret;
+
+ mutex_lock(&mhdp->mbox_mutex);
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_READ_EVENT, 0, NULL);
+ if (ret)
+ goto out;
+
+ ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_READ_EVENT, sizeof(event));
+ if (ret < 0)
+ goto out;
+
+ ret = cdns_mhdp_mailbox_recv_data(mhdp, &event, sizeof(event));
+out:
+ mutex_unlock(&mhdp->mbox_mutex);
+
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(mhdp->dev, "%s: %s%s%s%s\n", __func__,
+ (event & DPTX_READ_EVENT_HPD_TO_HIGH) ? "TO_HIGH " : "",
+ (event & DPTX_READ_EVENT_HPD_TO_LOW) ? "TO_LOW " : "",
+ (event & DPTX_READ_EVENT_HPD_PULSE) ? "PULSE " : "",
+ (event & DPTX_READ_EVENT_HPD_STATE) ? "HPD_STATE " : "");
+
+ return event;
+}
+
+static
+int cdns_mhdp_adjust_lt(struct cdns_mhdp_device *mhdp, unsigned int nlanes,
+ unsigned int udelay, const u8 *lanes_data,
+ u8 link_status[DP_LINK_STATUS_SIZE])
+{
+ u8 payload[7];
+ u8 hdr[5]; /* For DPCD read response header */
+ u32 addr;
+ int ret;
+
+ if (nlanes != 4 && nlanes != 2 && nlanes != 1) {
+ dev_err(mhdp->dev, "invalid number of lanes: %u\n", nlanes);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ payload[0] = nlanes;
+ put_unaligned_be16(udelay, payload + 1);
+ memcpy(payload + 3, lanes_data, nlanes);
+
+ mutex_lock(&mhdp->mbox_mutex);
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_ADJUST_LT,
+ sizeof(payload), payload);
+ if (ret)
+ goto out;
+
+ /* Yes, read the DPCD read command response */
+ ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_READ_DPCD,
+ sizeof(hdr) + DP_LINK_STATUS_SIZE);
+ if (ret)
+ goto out;
+
+ ret = cdns_mhdp_mailbox_recv_data(mhdp, hdr, sizeof(hdr));
+ if (ret)
+ goto out;
+
+ addr = get_unaligned_be24(hdr + 2);
+ if (addr != DP_LANE0_1_STATUS)
+ goto out;
+
+ ret = cdns_mhdp_mailbox_recv_data(mhdp, link_status,
+ DP_LINK_STATUS_SIZE);
+
+out:
+ mutex_unlock(&mhdp->mbox_mutex);
+
+ if (ret)
+ dev_err(mhdp->dev, "Failed to adjust Link Training.\n");
+
+ return ret;
+}
+
+/**
+ * cdns_mhdp_link_power_up() - power up a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static
+int cdns_mhdp_link_power_up(struct drm_dp_aux *aux, struct cdns_mhdp_link *link)
+{
+ u8 value;
+ int err;
+
+ /* DP_SET_POWER register is only available on DPCD v1.1 and later */
+ if (link->revision < 0x11)
+ return 0;
+
+ err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
+ if (err < 0)
+ return err;
+
+ value &= ~DP_SET_POWER_MASK;
+ value |= DP_SET_POWER_D0;
+
+ err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
+ if (err < 0)
+ return err;
+
+ /*
+ * According to the DP 1.1 specification, a "Sink Device must exit the
+ * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
+ * Control Field" (register 0x600).
+ */
+ usleep_range(1000, 2000);
+
+ return 0;
+}
+
+/**
+ * cdns_mhdp_link_power_down() - power down a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static
+int cdns_mhdp_link_power_down(struct drm_dp_aux *aux,
+ struct cdns_mhdp_link *link)
+{
+ u8 value;
+ int err;
+
+ /* DP_SET_POWER register is only available on DPCD v1.1 and later */
+ if (link->revision < 0x11)
+ return 0;
+
+ err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
+ if (err < 0)
+ return err;
+
+ value &= ~DP_SET_POWER_MASK;
+ value |= DP_SET_POWER_D3;
+
+ err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/**
+ * cdns_mhdp_link_configure() - configure a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static
+int cdns_mhdp_link_configure(struct drm_dp_aux *aux,
+ struct cdns_mhdp_link *link)
+{
+ u8 values[2];
+ int err;
+
+ values[0] = drm_dp_link_rate_to_bw_code(link->rate);
+ values[1] = link->num_lanes;
+
+ if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
+ values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+ err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static unsigned int cdns_mhdp_max_link_rate(struct cdns_mhdp_device *mhdp)
+{
+ return min(mhdp->host.link_rate, mhdp->sink.link_rate);
+}
+
+static u8 cdns_mhdp_max_num_lanes(struct cdns_mhdp_device *mhdp)
+{
+ return min(mhdp->sink.lanes_cnt, mhdp->host.lanes_cnt);
+}
+
+static u8 cdns_mhdp_eq_training_pattern_supported(struct cdns_mhdp_device *mhdp)
+{
+ return fls(mhdp->host.pattern_supp & mhdp->sink.pattern_supp);
+}
+
+static bool cdns_mhdp_get_ssc_supported(struct cdns_mhdp_device *mhdp)
+{
+ /* Check if SSC is supported by both sides */
+ return mhdp->host.ssc && mhdp->sink.ssc;
+}
+
+static enum drm_connector_status cdns_mhdp_detect(struct cdns_mhdp_device *mhdp)
+{
+ dev_dbg(mhdp->dev, "%s: %d\n", __func__, mhdp->plugged);
+
+ if (mhdp->plugged)
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+}
+
+static int cdns_mhdp_check_fw_version(struct cdns_mhdp_device *mhdp)
+{
+ u32 major_num, minor_num, revision;
+ u32 fw_ver, lib_ver;
+
+ fw_ver = (readl(mhdp->regs + CDNS_VER_H) << 8)
+ | readl(mhdp->regs + CDNS_VER_L);
+
+ lib_ver = (readl(mhdp->regs + CDNS_LIB_H_ADDR) << 8)
+ | readl(mhdp->regs + CDNS_LIB_L_ADDR);
+
+ if (lib_ver < 33984) {
+ /*
+ * Older FW versions with major number 1, used to store FW
+ * version information by storing repository revision number
+ * in registers. This is for identifying these FW versions.
+ */
+ major_num = 1;
+ minor_num = 2;
+ if (fw_ver == 26098) {
+ revision = 15;
+ } else if (lib_ver == 0 && fw_ver == 0) {
+ revision = 17;
+ } else {
+ dev_err(mhdp->dev, "Unsupported FW version: fw_ver = %u, lib_ver = %u\n",
+ fw_ver, lib_ver);
+ return -ENODEV;
+ }
+ } else {
+ /* To identify newer FW versions with major number 2 onwards. */
+ major_num = fw_ver / 10000;
+ minor_num = (fw_ver / 100) % 100;
+ revision = (fw_ver % 10000) % 100;
+ }
+
+ dev_dbg(mhdp->dev, "FW version: v%u.%u.%u\n", major_num, minor_num,
+ revision);
+ return 0;
+}
+
+static int cdns_mhdp_fw_activate(const struct firmware *fw,
+ struct cdns_mhdp_device *mhdp)
+{
+ unsigned int reg;
+ int ret;
+
+ /* Release uCPU reset and stall it. */
+ writel(CDNS_CPU_STALL, mhdp->regs + CDNS_APB_CTRL);
+
+ memcpy_toio(mhdp->regs + CDNS_MHDP_IMEM, fw->data, fw->size);
+
+ /* Leave debug mode, release stall */
+ writel(0, mhdp->regs + CDNS_APB_CTRL);
+
+ /*
+ * Wait for the KEEP_ALIVE "message" on the first 8 bits.
+ * Updated each sched "tick" (~2ms)
+ */
+ ret = readl_poll_timeout(mhdp->regs + CDNS_KEEP_ALIVE, reg,
+ reg & CDNS_KEEP_ALIVE_MASK, 500,
+ CDNS_KEEP_ALIVE_TIMEOUT);
+ if (ret) {
+ dev_err(mhdp->dev,
+ "device didn't give any life sign: reg %d\n", reg);
+ return ret;
+ }
+
+ ret = cdns_mhdp_check_fw_version(mhdp);
+ if (ret)
+ return ret;
+
+ /* Init events to 0 as it's not cleared by FW at boot but on read */
+ readl(mhdp->regs + CDNS_SW_EVENT0);
+ readl(mhdp->regs + CDNS_SW_EVENT1);
+ readl(mhdp->regs + CDNS_SW_EVENT2);
+ readl(mhdp->regs + CDNS_SW_EVENT3);
+
+ /* Activate uCPU */
+ ret = cdns_mhdp_set_firmware_active(mhdp, true);
+ if (ret)
+ return ret;
+
+ spin_lock(&mhdp->start_lock);
+
+ mhdp->hw_state = MHDP_HW_READY;
+
+ /*
+ * Here we must keep the lock while enabling the interrupts
+ * since it would otherwise be possible that interrupt enable
+ * code is executed after the bridge is detached. The similar
+ * situation is not possible in attach()/detach() callbacks
+ * since the hw_state changes from MHDP_HW_READY to
+ * MHDP_HW_STOPPED happens only due to driver removal when
+ * bridge should already be detached.
+ */
+ if (mhdp->bridge_attached)
+ writel(~CDNS_APB_INT_MASK_SW_EVENT_INT,
+ mhdp->regs + CDNS_APB_INT_MASK);
+
+ spin_unlock(&mhdp->start_lock);
+
+ wake_up(&mhdp->fw_load_wq);
+ dev_dbg(mhdp->dev, "DP FW activated\n");
+
+ return 0;
+}
+
+static void cdns_mhdp_fw_cb(const struct firmware *fw, void *context)
+{
+ struct cdns_mhdp_device *mhdp = context;
+ bool bridge_attached;
+ int ret;
+
+ dev_dbg(mhdp->dev, "firmware callback\n");
+
+ if (!fw || !fw->data) {
+ dev_err(mhdp->dev, "%s: No firmware.\n", __func__);
+ return;
+ }
+
+ ret = cdns_mhdp_fw_activate(fw, mhdp);
+
+ release_firmware(fw);
+
+ if (ret)
+ return;
+
+ /*
+ * XXX how to make sure the bridge is still attached when
+ * calling drm_kms_helper_hotplug_event() after releasing
+ * the lock? We should not hold the spin lock when
+ * calling drm_kms_helper_hotplug_event() since it may
+ * cause a dead lock. FB-dev console calls detect from the
+ * same thread just down the call stack started here.
+ */
+ spin_lock(&mhdp->start_lock);
+ bridge_attached = mhdp->bridge_attached;
+ spin_unlock(&mhdp->start_lock);
+ if (bridge_attached) {
+ if (mhdp->connector.dev)
+ drm_kms_helper_hotplug_event(mhdp->bridge.dev);
+ else
+ drm_bridge_hpd_notify(&mhdp->bridge, cdns_mhdp_detect(mhdp));
+ }
+}
+
+static int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp)
+{
+ int ret;
+
+ ret = request_firmware_nowait(THIS_MODULE, true, FW_NAME, mhdp->dev,
+ GFP_KERNEL, mhdp, cdns_mhdp_fw_cb);
+ if (ret) {
+ dev_err(mhdp->dev, "failed to load firmware (%s), ret: %d\n",
+ FW_NAME, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static ssize_t cdns_mhdp_transfer(struct drm_dp_aux *aux,
+ struct drm_dp_aux_msg *msg)
+{
+ struct cdns_mhdp_device *mhdp = dev_get_drvdata(aux->dev);
+ int ret;
+
+ if (msg->request != DP_AUX_NATIVE_WRITE &&
+ msg->request != DP_AUX_NATIVE_READ)
+ return -EOPNOTSUPP;
+
+ if (msg->request == DP_AUX_NATIVE_WRITE) {
+ const u8 *buf = msg->buffer;
+ unsigned int i;
+
+ for (i = 0; i < msg->size; ++i) {
+ ret = cdns_mhdp_dpcd_write(mhdp,
+ msg->address + i, buf[i]);
+ if (!ret)
+ continue;
+
+ dev_err(mhdp->dev,
+ "Failed to write DPCD addr %u\n",
+ msg->address + i);
+
+ return ret;
+ }
+ } else {
+ ret = cdns_mhdp_dpcd_read(mhdp, msg->address,
+ msg->buffer, msg->size);
+ if (ret) {
+ dev_err(mhdp->dev,
+ "Failed to read DPCD addr %u\n",
+ msg->address);
+
+ return ret;
+ }
+ }
+
+ return msg->size;
+}
+
+static int cdns_mhdp_link_training_init(struct cdns_mhdp_device *mhdp)
+{
+ union phy_configure_opts phy_cfg;
+ u32 reg32;
+ int ret;
+
+ drm_dp_dpcd_writeb(&mhdp->aux, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
+
+ /* Reset PHY configuration */
+ reg32 = CDNS_PHY_COMMON_CONFIG | CDNS_PHY_TRAINING_TYPE(1);
+ if (!mhdp->host.scrambler)
+ reg32 |= CDNS_PHY_SCRAMBLER_BYPASS;
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32);
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_ENHNCD,
+ mhdp->sink.enhanced & mhdp->host.enhanced);
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_LANE_EN,
+ CDNS_DP_LANE_EN_LANES(mhdp->link.num_lanes));
+
+ cdns_mhdp_link_configure(&mhdp->aux, &mhdp->link);
+ phy_cfg.dp.link_rate = mhdp->link.rate / 100;
+ phy_cfg.dp.lanes = mhdp->link.num_lanes;
+
+ memset(phy_cfg.dp.voltage, 0, sizeof(phy_cfg.dp.voltage));
+ memset(phy_cfg.dp.pre, 0, sizeof(phy_cfg.dp.pre));
+
+ phy_cfg.dp.ssc = cdns_mhdp_get_ssc_supported(mhdp);
+ phy_cfg.dp.set_lanes = true;
+ phy_cfg.dp.set_rate = true;
+ phy_cfg.dp.set_voltages = true;
+ ret = phy_configure(mhdp->phy, &phy_cfg);
+ if (ret) {
+ dev_err(mhdp->dev, "%s: phy_configure() failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG,
+ CDNS_PHY_COMMON_CONFIG |
+ CDNS_PHY_TRAINING_EN |
+ CDNS_PHY_TRAINING_TYPE(1) |
+ CDNS_PHY_SCRAMBLER_BYPASS);
+
+ drm_dp_dpcd_writeb(&mhdp->aux, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_1 | DP_LINK_SCRAMBLING_DISABLE);
+
+ return 0;
+}
+
+static void cdns_mhdp_get_adjust_train(struct cdns_mhdp_device *mhdp,
+ u8 link_status[DP_LINK_STATUS_SIZE],
+ u8 lanes_data[CDNS_DP_MAX_NUM_LANES],
+ union phy_configure_opts *phy_cfg)
+{
+ u8 adjust, max_pre_emph, max_volt_swing;
+ u8 set_volt, set_pre;
+ unsigned int i;
+
+ max_pre_emph = CDNS_PRE_EMPHASIS(mhdp->host.pre_emphasis)
+ << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+ max_volt_swing = CDNS_VOLT_SWING(mhdp->host.volt_swing);
+
+ for (i = 0; i < mhdp->link.num_lanes; i++) {
+ /* Check if Voltage swing and pre-emphasis are within limits */
+ adjust = drm_dp_get_adjust_request_voltage(link_status, i);
+ set_volt = min(adjust, max_volt_swing);
+
+ adjust = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
+ set_pre = min(adjust, max_pre_emph)
+ >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+ /*
+ * Voltage swing level and pre-emphasis level combination is
+ * not allowed: leaving pre-emphasis as-is, and adjusting
+ * voltage swing.
+ */
+ if (set_volt + set_pre > 3)
+ set_volt = 3 - set_pre;
+
+ phy_cfg->dp.voltage[i] = set_volt;
+ lanes_data[i] = set_volt;
+
+ if (set_volt == max_volt_swing)
+ lanes_data[i] |= DP_TRAIN_MAX_SWING_REACHED;
+
+ phy_cfg->dp.pre[i] = set_pre;
+ lanes_data[i] |= (set_pre << DP_TRAIN_PRE_EMPHASIS_SHIFT);
+
+ if (set_pre == (max_pre_emph >> DP_TRAIN_PRE_EMPHASIS_SHIFT))
+ lanes_data[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+ }
+}
+
+static
+void cdns_mhdp_set_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE],
+ unsigned int lane, u8 volt)
+{
+ unsigned int s = ((lane & 1) ?
+ DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
+ DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
+ unsigned int idx = DP_ADJUST_REQUEST_LANE0_1 - DP_LANE0_1_STATUS + (lane >> 1);
+
+ link_status[idx] &= ~(DP_ADJUST_VOLTAGE_SWING_LANE0_MASK << s);
+ link_status[idx] |= volt << s;
+}
+
+static
+void cdns_mhdp_set_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE],
+ unsigned int lane, u8 pre_emphasis)
+{
+ unsigned int s = ((lane & 1) ?
+ DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
+ DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
+ unsigned int idx = DP_ADJUST_REQUEST_LANE0_1 - DP_LANE0_1_STATUS + (lane >> 1);
+
+ link_status[idx] &= ~(DP_ADJUST_PRE_EMPHASIS_LANE0_MASK << s);
+ link_status[idx] |= pre_emphasis << s;
+}
+
+static void cdns_mhdp_adjust_requested_eq(struct cdns_mhdp_device *mhdp,
+ u8 link_status[DP_LINK_STATUS_SIZE])
+{
+ u8 max_pre = CDNS_PRE_EMPHASIS(mhdp->host.pre_emphasis);
+ u8 max_volt = CDNS_VOLT_SWING(mhdp->host.volt_swing);
+ unsigned int i;
+ u8 volt, pre;
+
+ for (i = 0; i < mhdp->link.num_lanes; i++) {
+ volt = drm_dp_get_adjust_request_voltage(link_status, i);
+ pre = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
+ if (volt + pre > 3)
+ cdns_mhdp_set_adjust_request_voltage(link_status, i,
+ 3 - pre);
+ if (mhdp->host.volt_swing & CDNS_FORCE_VOLT_SWING)
+ cdns_mhdp_set_adjust_request_voltage(link_status, i,
+ max_volt);
+ if (mhdp->host.pre_emphasis & CDNS_FORCE_PRE_EMPHASIS)
+ cdns_mhdp_set_adjust_request_pre_emphasis(link_status,
+ i, max_pre);
+ }
+}
+
+static void cdns_mhdp_print_lt_status(const char *prefix,
+ struct cdns_mhdp_device *mhdp,
+ union phy_configure_opts *phy_cfg)
+{
+ char vs[8] = "0/0/0/0";
+ char pe[8] = "0/0/0/0";
+ unsigned int i;
+
+ for (i = 0; i < mhdp->link.num_lanes; i++) {
+ vs[i * 2] = '0' + phy_cfg->dp.voltage[i];
+ pe[i * 2] = '0' + phy_cfg->dp.pre[i];
+ }
+
+ vs[i * 2 - 1] = '\0';
+ pe[i * 2 - 1] = '\0';
+
+ dev_dbg(mhdp->dev, "%s, %u lanes, %u Mbps, vs %s, pe %s\n",
+ prefix,
+ mhdp->link.num_lanes, mhdp->link.rate / 100,
+ vs, pe);
+}
+
+static bool cdns_mhdp_link_training_channel_eq(struct cdns_mhdp_device *mhdp,
+ u8 eq_tps,
+ unsigned int training_interval)
+{
+ u8 lanes_data[CDNS_DP_MAX_NUM_LANES], fail_counter_short = 0;
+ u8 link_status[DP_LINK_STATUS_SIZE];
+ union phy_configure_opts phy_cfg;
+ u32 reg32;
+ int ret;
+ bool r;
+
+ dev_dbg(mhdp->dev, "Starting EQ phase\n");
+
+ /* Enable link training TPS[eq_tps] in PHY */
+ reg32 = CDNS_PHY_COMMON_CONFIG | CDNS_PHY_TRAINING_EN |
+ CDNS_PHY_TRAINING_TYPE(eq_tps);
+ if (eq_tps != 4)
+ reg32 |= CDNS_PHY_SCRAMBLER_BYPASS;
+ cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32);
+
+ drm_dp_dpcd_writeb(&mhdp->aux, DP_TRAINING_PATTERN_SET,
+ (eq_tps != 4) ? eq_tps | DP_LINK_SCRAMBLING_DISABLE :
+ CDNS_DP_TRAINING_PATTERN_4);
+
+ drm_dp_dpcd_read_link_status(&mhdp->aux, link_status);
+
+ do {
+ cdns_mhdp_get_adjust_train(mhdp, link_status, lanes_data,
+ &phy_cfg);
+ phy_cfg.dp.lanes = mhdp->link.num_lanes;
+ phy_cfg.dp.ssc = cdns_mhdp_get_ssc_supported(mhdp);
+ phy_cfg.dp.set_lanes = false;
+ phy_cfg.dp.set_rate = false;
+ phy_cfg.dp.set_voltages = true;
+ ret = phy_configure(mhdp->phy, &phy_cfg);
+ if (ret) {
+ dev_err(mhdp->dev, "%s: phy_configure() failed: %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ cdns_mhdp_adjust_lt(mhdp, mhdp->link.num_lanes,
+ training_interval, lanes_data, link_status);
+
+ r = drm_dp_clock_recovery_ok(link_status, mhdp->link.num_lanes);
+ if (!r)
+ goto err;
+
+ if (drm_dp_channel_eq_ok(link_status, mhdp->link.num_lanes)) {
+ cdns_mhdp_print_lt_status("EQ phase ok", mhdp,
+ &phy_cfg);
+ return true;
+ }
+
+ fail_counter_short++;
+
+ cdns_mhdp_adjust_requested_eq(mhdp, link_status);
+ } while (fail_counter_short < 5);
+
+err:
+ cdns_mhdp_print_lt_status("EQ phase failed", mhdp, &phy_cfg);
+
+ return false;
+}
+
+static void cdns_mhdp_adjust_requested_cr(struct cdns_mhdp_device *mhdp,
+ u8 link_status[DP_LINK_STATUS_SIZE],
+ u8 *req_volt, u8 *req_pre)
+{
+ const u8 max_volt = CDNS_VOLT_SWING(mhdp->host.volt_swing);
+ const u8 max_pre = CDNS_PRE_EMPHASIS(mhdp->host.pre_emphasis);
+ unsigned int i;
+
+ for (i = 0; i < mhdp->link.num_lanes; i++) {
+ u8 val;
+
+ val = mhdp->host.volt_swing & CDNS_FORCE_VOLT_SWING ?
+ max_volt : req_volt[i];
+ cdns_mhdp_set_adjust_request_voltage(link_status, i, val);
+
+ val = mhdp->host.pre_emphasis & CDNS_FORCE_PRE_EMPHASIS ?
+ max_pre : req_pre[i];
+ cdns_mhdp_set_adjust_request_pre_emphasis(link_status, i, val);
+ }
+}
+
+static
+void cdns_mhdp_validate_cr(struct cdns_mhdp_device *mhdp, bool *cr_done,
+ bool *same_before_adjust, bool *max_swing_reached,
+ u8 before_cr[CDNS_DP_MAX_NUM_LANES],
+ u8 after_cr[DP_LINK_STATUS_SIZE], u8 *req_volt,
+ u8 *req_pre)
+{
+ const u8 max_volt = CDNS_VOLT_SWING(mhdp->host.volt_swing);
+ const u8 max_pre = CDNS_PRE_EMPHASIS(mhdp->host.pre_emphasis);
+ bool same_pre, same_volt;
+ unsigned int i;
+ u8 adjust;
+
+ *same_before_adjust = false;
+ *max_swing_reached = false;
+ *cr_done = drm_dp_clock_recovery_ok(after_cr, mhdp->link.num_lanes);
+
+ for (i = 0; i < mhdp->link.num_lanes; i++) {
+ adjust = drm_dp_get_adjust_request_voltage(after_cr, i);
+ req_volt[i] = min(adjust, max_volt);
+
+ adjust = drm_dp_get_adjust_request_pre_emphasis(after_cr, i) >>
+ DP_TRAIN_PRE_EMPHASIS_SHIFT;
+ req_pre[i] = min(adjust, max_pre);
+
+ same_pre = (before_cr[i] & DP_TRAIN_PRE_EMPHASIS_MASK) ==
+ req_pre[i] << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+ same_volt = (before_cr[i] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
+ req_volt[i];
+ if (same_pre && same_volt)
+ *same_before_adjust = true;
+
+ /* 3.1.5.2 in DP Standard v1.4. Table 3-1 */
+ if (!*cr_done && req_volt[i] + req_pre[i] >= 3) {
+ *max_swing_reached = true;
+ return;
+ }
+ }
+}
+
+static bool cdns_mhdp_link_training_cr(struct cdns_mhdp_device *mhdp)
+{
+ u8 lanes_data[CDNS_DP_MAX_NUM_LANES],
+ fail_counter_short = 0, fail_counter_cr_long = 0;
+ u8 link_status[DP_LINK_STATUS_SIZE];
+ bool cr_done;
+ union phy_configure_opts phy_cfg;
+ int ret;
+
+ dev_dbg(mhdp->dev, "Starting CR phase\n");
+
+ ret = cdns_mhdp_link_training_init(mhdp);
+ if (ret)
+ goto err;
+
+ drm_dp_dpcd_read_link_status(&mhdp->aux, link_status);
+
+ do {
+ u8 requested_adjust_volt_swing[CDNS_DP_MAX_NUM_LANES] = {};
+ u8 requested_adjust_pre_emphasis[CDNS_DP_MAX_NUM_LANES] = {};
+ bool same_before_adjust, max_swing_reached;
+
+ cdns_mhdp_get_adjust_train(mhdp, link_status, lanes_data,
+ &phy_cfg);
+ phy_cfg.dp.lanes = mhdp->link.num_lanes;
+ phy_cfg.dp.ssc = cdns_mhdp_get_ssc_supported(mhdp);
+ phy_cfg.dp.set_lanes = false;
+ phy_cfg.dp.set_rate = false;
+ phy_cfg.dp.set_voltages = true;
+ ret = phy_configure(mhdp->phy, &phy_cfg);
+ if (ret) {
+ dev_err(mhdp->dev, "%s: phy_configure() failed: %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ cdns_mhdp_adjust_lt(mhdp, mhdp->link.num_lanes, 100,
+ lanes_data, link_status);
+
+ cdns_mhdp_validate_cr(mhdp, &cr_done, &same_before_adjust,
+ &max_swing_reached, lanes_data,
+ link_status,
+ requested_adjust_volt_swing,
+ requested_adjust_pre_emphasis);
+
+ if (max_swing_reached) {
+ dev_err(mhdp->dev, "CR: max swing reached\n");
+ goto err;
+ }
+
+ if (cr_done) {
+ cdns_mhdp_print_lt_status("CR phase ok", mhdp,
+ &phy_cfg);
+ return true;
+ }
+
+ /* Not all CR_DONE bits set */
+ fail_counter_cr_long++;
+
+ if (same_before_adjust) {
+ fail_counter_short++;
+ continue;
+ }
+
+ fail_counter_short = 0;
+ /*
+ * Voltage swing/pre-emphasis adjust requested
+ * during CR phase
+ */
+ cdns_mhdp_adjust_requested_cr(mhdp, link_status,
+ requested_adjust_volt_swing,
+ requested_adjust_pre_emphasis);
+ } while (fail_counter_short < 5 && fail_counter_cr_long < 10);
+
+err:
+ cdns_mhdp_print_lt_status("CR phase failed", mhdp, &phy_cfg);
+
+ return false;
+}
+
+static void cdns_mhdp_lower_link_rate(struct cdns_mhdp_link *link)
+{
+ switch (drm_dp_link_rate_to_bw_code(link->rate)) {
+ case DP_LINK_BW_2_7:
+ link->rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_1_62);
+ break;
+ case DP_LINK_BW_5_4:
+ link->rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_2_7);
+ break;
+ case DP_LINK_BW_8_1:
+ link->rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_5_4);
+ break;
+ }
+}
+
+static int cdns_mhdp_link_training(struct cdns_mhdp_device *mhdp,
+ unsigned int training_interval)
+{
+ u32 reg32;
+ const u8 eq_tps = cdns_mhdp_eq_training_pattern_supported(mhdp);
+ int ret;
+
+ while (1) {
+ if (!cdns_mhdp_link_training_cr(mhdp)) {
+ if (drm_dp_link_rate_to_bw_code(mhdp->link.rate) !=
+ DP_LINK_BW_1_62) {
+ dev_dbg(mhdp->dev,
+ "Reducing link rate during CR phase\n");
+ cdns_mhdp_lower_link_rate(&mhdp->link);
+
+ continue;
+ } else if (mhdp->link.num_lanes > 1) {
+ dev_dbg(mhdp->dev,
+ "Reducing lanes number during CR phase\n");
+ mhdp->link.num_lanes >>= 1;
+ mhdp->link.rate = cdns_mhdp_max_link_rate(mhdp);
+
+ continue;
+ }
+
+ dev_err(mhdp->dev,
+ "Link training failed during CR phase\n");
+ goto err;
+ }
+
+ if (cdns_mhdp_link_training_channel_eq(mhdp, eq_tps,
+ training_interval))
+ break;
+
+ if (mhdp->link.num_lanes > 1) {
+ dev_dbg(mhdp->dev,
+ "Reducing lanes number during EQ phase\n");
+ mhdp->link.num_lanes >>= 1;
+
+ continue;
+ } else if (drm_dp_link_rate_to_bw_code(mhdp->link.rate) !=
+ DP_LINK_BW_1_62) {
+ dev_dbg(mhdp->dev,
+ "Reducing link rate during EQ phase\n");
+ cdns_mhdp_lower_link_rate(&mhdp->link);
+ mhdp->link.num_lanes = cdns_mhdp_max_num_lanes(mhdp);
+
+ continue;
+ }
+
+ dev_err(mhdp->dev, "Link training failed during EQ phase\n");
+ goto err;
+ }
+
+ dev_dbg(mhdp->dev, "Link training ok. Lanes: %u, Rate %u Mbps\n",
+ mhdp->link.num_lanes, mhdp->link.rate / 100);
+
+ drm_dp_dpcd_writeb(&mhdp->aux, DP_TRAINING_PATTERN_SET,
+ mhdp->host.scrambler ? 0 :
+ DP_LINK_SCRAMBLING_DISABLE);
+
+ ret = cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, &reg32);
+ if (ret < 0) {
+ dev_err(mhdp->dev,
+ "Failed to read CDNS_DP_FRAMER_GLOBAL_CONFIG %d\n",
+ ret);
+ return ret;
+ }
+ reg32 &= ~GENMASK(1, 0);
+ reg32 |= CDNS_DP_NUM_LANES(mhdp->link.num_lanes);
+ reg32 |= CDNS_DP_WR_FAILING_EDGE_VSYNC;
+ reg32 |= CDNS_DP_FRAMER_EN;
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, reg32);
+
+ /* Reset PHY config */
+ reg32 = CDNS_PHY_COMMON_CONFIG | CDNS_PHY_TRAINING_TYPE(1);
+ if (!mhdp->host.scrambler)
+ reg32 |= CDNS_PHY_SCRAMBLER_BYPASS;
+ cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32);
+
+ return 0;
+err:
+ /* Reset PHY config */
+ reg32 = CDNS_PHY_COMMON_CONFIG | CDNS_PHY_TRAINING_TYPE(1);
+ if (!mhdp->host.scrambler)
+ reg32 |= CDNS_PHY_SCRAMBLER_BYPASS;
+ cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32);
+
+ drm_dp_dpcd_writeb(&mhdp->aux, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
+
+ return -EIO;
+}
+
+static u32 cdns_mhdp_get_training_interval_us(struct cdns_mhdp_device *mhdp,
+ u32 interval)
+{
+ if (interval == 0)
+ return 400;
+ if (interval < 5)
+ return 4000 << (interval - 1);
+ dev_err(mhdp->dev,
+ "wrong training interval returned by DPCD: %d\n", interval);
+ return 0;
+}
+
+static void cdns_mhdp_fill_host_caps(struct cdns_mhdp_device *mhdp)
+{
+ unsigned int link_rate;
+
+ /* Get source capabilities based on PHY attributes */
+
+ mhdp->host.lanes_cnt = mhdp->phy->attrs.bus_width;
+ if (!mhdp->host.lanes_cnt)
+ mhdp->host.lanes_cnt = 4;
+
+ link_rate = mhdp->phy->attrs.max_link_rate;
+ if (!link_rate)
+ link_rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_8_1);
+ else
+ /* PHY uses Mb/s, DRM uses tens of kb/s. */
+ link_rate *= 100;
+
+ mhdp->host.link_rate = link_rate;
+ mhdp->host.volt_swing = CDNS_VOLT_SWING(3);
+ mhdp->host.pre_emphasis = CDNS_PRE_EMPHASIS(3);
+ mhdp->host.pattern_supp = CDNS_SUPPORT_TPS(1) |
+ CDNS_SUPPORT_TPS(2) | CDNS_SUPPORT_TPS(3) |
+ CDNS_SUPPORT_TPS(4);
+ mhdp->host.lane_mapping = CDNS_LANE_MAPPING_NORMAL;
+ mhdp->host.fast_link = false;
+ mhdp->host.enhanced = true;
+ mhdp->host.scrambler = true;
+ mhdp->host.ssc = false;
+}
+
+static void cdns_mhdp_fill_sink_caps(struct cdns_mhdp_device *mhdp,
+ u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+ mhdp->sink.link_rate = mhdp->link.rate;
+ mhdp->sink.lanes_cnt = mhdp->link.num_lanes;
+ mhdp->sink.enhanced = !!(mhdp->link.capabilities &
+ DP_LINK_CAP_ENHANCED_FRAMING);
+
+ /* Set SSC support */
+ mhdp->sink.ssc = !!(dpcd[DP_MAX_DOWNSPREAD] &
+ DP_MAX_DOWNSPREAD_0_5);
+
+ /* Set TPS support */
+ mhdp->sink.pattern_supp = CDNS_SUPPORT_TPS(1) | CDNS_SUPPORT_TPS(2);
+ if (drm_dp_tps3_supported(dpcd))
+ mhdp->sink.pattern_supp |= CDNS_SUPPORT_TPS(3);
+ if (drm_dp_tps4_supported(dpcd))
+ mhdp->sink.pattern_supp |= CDNS_SUPPORT_TPS(4);
+
+ /* Set fast link support */
+ mhdp->sink.fast_link = !!(dpcd[DP_MAX_DOWNSPREAD] &
+ DP_NO_AUX_HANDSHAKE_LINK_TRAINING);
+}
+
+static int cdns_mhdp_link_up(struct cdns_mhdp_device *mhdp)
+{
+ u8 dpcd[DP_RECEIVER_CAP_SIZE], amp[2];
+ u32 resp, interval, interval_us;
+ u8 ext_cap_chk = 0;
+ unsigned int addr;
+ int err;
+
+ WARN_ON(!mutex_is_locked(&mhdp->link_mutex));
+
+ drm_dp_dpcd_readb(&mhdp->aux, DP_TRAINING_AUX_RD_INTERVAL,
+ &ext_cap_chk);
+
+ if (ext_cap_chk & DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT)
+ addr = DP_DP13_DPCD_REV;
+ else
+ addr = DP_DPCD_REV;
+
+ err = drm_dp_dpcd_read(&mhdp->aux, addr, dpcd, DP_RECEIVER_CAP_SIZE);
+ if (err < 0) {
+ dev_err(mhdp->dev, "Failed to read receiver capabilities\n");
+ return err;
+ }
+
+ mhdp->link.revision = dpcd[0];
+ mhdp->link.rate = drm_dp_bw_code_to_link_rate(dpcd[1]);
+ mhdp->link.num_lanes = dpcd[2] & DP_MAX_LANE_COUNT_MASK;
+
+ if (dpcd[2] & DP_ENHANCED_FRAME_CAP)
+ mhdp->link.capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
+
+ dev_dbg(mhdp->dev, "Set sink device power state via DPCD\n");
+ cdns_mhdp_link_power_up(&mhdp->aux, &mhdp->link);
+
+ cdns_mhdp_fill_sink_caps(mhdp, dpcd);
+
+ mhdp->link.rate = cdns_mhdp_max_link_rate(mhdp);
+ mhdp->link.num_lanes = cdns_mhdp_max_num_lanes(mhdp);
+
+ /* Disable framer for link training */
+ err = cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, &resp);
+ if (err < 0) {
+ dev_err(mhdp->dev,
+ "Failed to read CDNS_DP_FRAMER_GLOBAL_CONFIG %d\n",
+ err);
+ return err;
+ }
+
+ resp &= ~CDNS_DP_FRAMER_EN;
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, resp);
+
+ /* Spread AMP if required, enable 8b/10b coding */
+ amp[0] = cdns_mhdp_get_ssc_supported(mhdp) ? DP_SPREAD_AMP_0_5 : 0;
+ amp[1] = DP_SET_ANSI_8B10B;
+ drm_dp_dpcd_write(&mhdp->aux, DP_DOWNSPREAD_CTRL, amp, 2);
+
+ if (mhdp->host.fast_link & mhdp->sink.fast_link) {
+ dev_err(mhdp->dev, "fastlink not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & DP_TRAINING_AUX_RD_MASK;
+ interval_us = cdns_mhdp_get_training_interval_us(mhdp, interval);
+ if (!interval_us ||
+ cdns_mhdp_link_training(mhdp, interval_us)) {
+ dev_err(mhdp->dev, "Link training failed. Exiting.\n");
+ return -EIO;
+ }
+
+ mhdp->link_up = true;
+
+ return 0;
+}
+
+static void cdns_mhdp_link_down(struct cdns_mhdp_device *mhdp)
+{
+ WARN_ON(!mutex_is_locked(&mhdp->link_mutex));
+
+ if (mhdp->plugged)
+ cdns_mhdp_link_power_down(&mhdp->aux, &mhdp->link);
+
+ mhdp->link_up = false;
+}
+
+static struct edid *cdns_mhdp_get_edid(struct cdns_mhdp_device *mhdp,
+ struct drm_connector *connector)
+{
+ if (!mhdp->plugged)
+ return NULL;
+
+ return drm_do_get_edid(connector, cdns_mhdp_get_edid_block, mhdp);
+}
+
+static int cdns_mhdp_get_modes(struct drm_connector *connector)
+{
+ struct cdns_mhdp_device *mhdp = connector_to_mhdp(connector);
+ struct edid *edid;
+ int num_modes;
+
+ if (!mhdp->plugged)
+ return 0;
+
+ edid = cdns_mhdp_get_edid(mhdp, connector);
+ if (!edid) {
+ dev_err(mhdp->dev, "Failed to read EDID\n");
+ return 0;
+ }
+
+ drm_connector_update_edid_property(connector, edid);
+ num_modes = drm_add_edid_modes(connector, edid);
+ kfree(edid);
+
+ /*
+ * HACK: Warn about unsupported display formats until we deal
+ * with them correctly.
+ */
+ if (connector->display_info.color_formats &&
+ !(connector->display_info.color_formats &
+ mhdp->display_fmt.color_format))
+ dev_warn(mhdp->dev,
+ "%s: No supported color_format found (0x%08x)\n",
+ __func__, connector->display_info.color_formats);
+
+ if (connector->display_info.bpc &&
+ connector->display_info.bpc < mhdp->display_fmt.bpc)
+ dev_warn(mhdp->dev, "%s: Display bpc only %d < %d\n",
+ __func__, connector->display_info.bpc,
+ mhdp->display_fmt.bpc);
+
+ return num_modes;
+}
+
+static int cdns_mhdp_connector_detect(struct drm_connector *conn,
+ struct drm_modeset_acquire_ctx *ctx,
+ bool force)
+{
+ struct cdns_mhdp_device *mhdp = connector_to_mhdp(conn);
+
+ return cdns_mhdp_detect(mhdp);
+}
+
+static u32 cdns_mhdp_get_bpp(struct cdns_mhdp_display_fmt *fmt)
+{
+ u32 bpp;
+
+ if (fmt->y_only)
+ return fmt->bpc;
+
+ switch (fmt->color_format) {
+ case DRM_COLOR_FORMAT_RGB444:
+ case DRM_COLOR_FORMAT_YCRCB444:
+ bpp = fmt->bpc * 3;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB422:
+ bpp = fmt->bpc * 2;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB420:
+ bpp = fmt->bpc * 3 / 2;
+ break;
+ default:
+ bpp = fmt->bpc * 3;
+ WARN_ON(1);
+ }
+ return bpp;
+}
+
+static
+bool cdns_mhdp_bandwidth_ok(struct cdns_mhdp_device *mhdp,
+ const struct drm_display_mode *mode,
+ unsigned int lanes, unsigned int rate)
+{
+ u32 max_bw, req_bw, bpp;
+
+ /*
+ * mode->clock is expressed in kHz. Multiplying by bpp and dividing by 8
+ * we get the number of kB/s. DisplayPort applies a 8b-10b encoding, the
+ * value thus equals the bandwidth in 10kb/s units, which matches the
+ * units of the rate parameter.
+ */
+
+ bpp = cdns_mhdp_get_bpp(&mhdp->display_fmt);
+ req_bw = mode->clock * bpp / 8;
+ max_bw = lanes * rate;
+ if (req_bw > max_bw) {
+ dev_dbg(mhdp->dev,
+ "Unsupported Mode: %s, Req BW: %u, Available Max BW:%u\n",
+ mode->name, req_bw, max_bw);
+
+ return false;
+ }
+
+ return true;
+}
+
+static
+enum drm_mode_status cdns_mhdp_mode_valid(struct drm_connector *conn,
+ struct drm_display_mode *mode)
+{
+ struct cdns_mhdp_device *mhdp = connector_to_mhdp(conn);
+
+ mutex_lock(&mhdp->link_mutex);
+
+ if (!cdns_mhdp_bandwidth_ok(mhdp, mode, mhdp->link.num_lanes,
+ mhdp->link.rate)) {
+ mutex_unlock(&mhdp->link_mutex);
+ return MODE_CLOCK_HIGH;
+ }
+
+ mutex_unlock(&mhdp->link_mutex);
+ return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs cdns_mhdp_conn_helper_funcs = {
+ .detect_ctx = cdns_mhdp_connector_detect,
+ .get_modes = cdns_mhdp_get_modes,
+ .mode_valid = cdns_mhdp_mode_valid,
+};
+
+static const struct drm_connector_funcs cdns_mhdp_conn_funcs = {
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .reset = drm_atomic_helper_connector_reset,
+ .destroy = drm_connector_cleanup,
+};
+
+static int cdns_mhdp_connector_init(struct cdns_mhdp_device *mhdp)
+{
+ u32 bus_format = MEDIA_BUS_FMT_RGB121212_1X36;
+ struct drm_connector *conn = &mhdp->connector;
+ struct drm_bridge *bridge = &mhdp->bridge;
+ int ret;
+
+ if (!bridge->encoder) {
+ dev_err(mhdp->dev, "Parent encoder object not found");
+ return -ENODEV;
+ }
+
+ conn->polled = DRM_CONNECTOR_POLL_HPD;
+
+ ret = drm_connector_init(bridge->dev, conn, &cdns_mhdp_conn_funcs,
+ DRM_MODE_CONNECTOR_DisplayPort);
+ if (ret) {
+ dev_err(mhdp->dev, "Failed to initialize connector with drm\n");
+ return ret;
+ }
+
+ drm_connector_helper_add(conn, &cdns_mhdp_conn_helper_funcs);
+
+ ret = drm_display_info_set_bus_formats(&conn->display_info,
+ &bus_format, 1);
+ if (ret)
+ return ret;
+
+ ret = drm_connector_attach_encoder(conn, bridge->encoder);
+ if (ret) {
+ dev_err(mhdp->dev, "Failed to attach connector to encoder\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cdns_mhdp_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge);
+ bool hw_ready;
+ int ret;
+
+ dev_dbg(mhdp->dev, "%s\n", __func__);
+
+ if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+ ret = cdns_mhdp_connector_init(mhdp);
+ if (ret)
+ return ret;
+ }
+
+ spin_lock(&mhdp->start_lock);
+
+ mhdp->bridge_attached = true;
+ hw_ready = mhdp->hw_state == MHDP_HW_READY;
+
+ spin_unlock(&mhdp->start_lock);
+
+ /* Enable SW event interrupts */
+ if (hw_ready)
+ writel(~CDNS_APB_INT_MASK_SW_EVENT_INT,
+ mhdp->regs + CDNS_APB_INT_MASK);
+
+ return 0;
+}
+
+static void cdns_mhdp_configure_video(struct cdns_mhdp_device *mhdp,
+ const struct drm_display_mode *mode)
+{
+ unsigned int dp_framer_sp = 0, msa_horizontal_1,
+ msa_vertical_1, bnd_hsync2vsync, hsync2vsync_pol_ctrl,
+ misc0 = 0, misc1 = 0, pxl_repr,
+ front_porch, back_porch, msa_h0, msa_v0, hsync, vsync,
+ dp_vertical_1;
+ u8 stream_id = mhdp->stream_id;
+ u32 bpp, bpc, pxlfmt, framer;
+ int ret;
+
+ pxlfmt = mhdp->display_fmt.color_format;
+ bpc = mhdp->display_fmt.bpc;
+
+ /*
+ * If YCBCR supported and stream not SD, use ITU709
+ * Need to handle ITU version with YCBCR420 when supported
+ */
+ if ((pxlfmt == DRM_COLOR_FORMAT_YCRCB444 ||
+ pxlfmt == DRM_COLOR_FORMAT_YCRCB422) && mode->crtc_vdisplay >= 720)
+ misc0 = DP_YCBCR_COEFFICIENTS_ITU709;
+
+ bpp = cdns_mhdp_get_bpp(&mhdp->display_fmt);
+
+ switch (pxlfmt) {
+ case DRM_COLOR_FORMAT_RGB444:
+ pxl_repr = CDNS_DP_FRAMER_RGB << CDNS_DP_FRAMER_PXL_FORMAT;
+ misc0 |= DP_COLOR_FORMAT_RGB;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB444:
+ pxl_repr = CDNS_DP_FRAMER_YCBCR444 << CDNS_DP_FRAMER_PXL_FORMAT;
+ misc0 |= DP_COLOR_FORMAT_YCbCr444 | DP_TEST_DYNAMIC_RANGE_CEA;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB422:
+ pxl_repr = CDNS_DP_FRAMER_YCBCR422 << CDNS_DP_FRAMER_PXL_FORMAT;
+ misc0 |= DP_COLOR_FORMAT_YCbCr422 | DP_TEST_DYNAMIC_RANGE_CEA;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB420:
+ pxl_repr = CDNS_DP_FRAMER_YCBCR420 << CDNS_DP_FRAMER_PXL_FORMAT;
+ break;
+ default:
+ pxl_repr = CDNS_DP_FRAMER_Y_ONLY << CDNS_DP_FRAMER_PXL_FORMAT;
+ }
+
+ switch (bpc) {
+ case 6:
+ misc0 |= DP_TEST_BIT_DEPTH_6;
+ pxl_repr |= CDNS_DP_FRAMER_6_BPC;
+ break;
+ case 8:
+ misc0 |= DP_TEST_BIT_DEPTH_8;
+ pxl_repr |= CDNS_DP_FRAMER_8_BPC;
+ break;
+ case 10:
+ misc0 |= DP_TEST_BIT_DEPTH_10;
+ pxl_repr |= CDNS_DP_FRAMER_10_BPC;
+ break;
+ case 12:
+ misc0 |= DP_TEST_BIT_DEPTH_12;
+ pxl_repr |= CDNS_DP_FRAMER_12_BPC;
+ break;
+ case 16:
+ misc0 |= DP_TEST_BIT_DEPTH_16;
+ pxl_repr |= CDNS_DP_FRAMER_16_BPC;
+ break;
+ }
+
+ bnd_hsync2vsync = CDNS_IP_BYPASS_V_INTERFACE;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ bnd_hsync2vsync |= CDNS_IP_DET_INTERLACE_FORMAT;
+
+ cdns_mhdp_reg_write(mhdp, CDNS_BND_HSYNC2VSYNC(stream_id),
+ bnd_hsync2vsync);
+
+ hsync2vsync_pol_ctrl = 0;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ hsync2vsync_pol_ctrl |= CDNS_H2V_HSYNC_POL_ACTIVE_LOW;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ hsync2vsync_pol_ctrl |= CDNS_H2V_VSYNC_POL_ACTIVE_LOW;
+ cdns_mhdp_reg_write(mhdp, CDNS_HSYNC2VSYNC_POL_CTRL(stream_id),
+ hsync2vsync_pol_ctrl);
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_PXL_REPR(stream_id), pxl_repr);
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ dp_framer_sp |= CDNS_DP_FRAMER_INTERLACE;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ dp_framer_sp |= CDNS_DP_FRAMER_HSYNC_POL_LOW;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ dp_framer_sp |= CDNS_DP_FRAMER_VSYNC_POL_LOW;
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_SP(stream_id), dp_framer_sp);
+
+ front_porch = mode->crtc_hsync_start - mode->crtc_hdisplay;
+ back_porch = mode->crtc_htotal - mode->crtc_hsync_end;
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_FRONT_BACK_PORCH(stream_id),
+ CDNS_DP_FRONT_PORCH(front_porch) |
+ CDNS_DP_BACK_PORCH(back_porch));
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_BYTE_COUNT(stream_id),
+ mode->crtc_hdisplay * bpp / 8);
+
+ msa_h0 = mode->crtc_htotal - mode->crtc_hsync_start;
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_HORIZONTAL_0(stream_id),
+ CDNS_DP_MSAH0_H_TOTAL(mode->crtc_htotal) |
+ CDNS_DP_MSAH0_HSYNC_START(msa_h0));
+
+ hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;
+ msa_horizontal_1 = CDNS_DP_MSAH1_HSYNC_WIDTH(hsync) |
+ CDNS_DP_MSAH1_HDISP_WIDTH(mode->crtc_hdisplay);
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ msa_horizontal_1 |= CDNS_DP_MSAH1_HSYNC_POL_LOW;
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_HORIZONTAL_1(stream_id),
+ msa_horizontal_1);
+
+ msa_v0 = mode->crtc_vtotal - mode->crtc_vsync_start;
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_VERTICAL_0(stream_id),
+ CDNS_DP_MSAV0_V_TOTAL(mode->crtc_vtotal) |
+ CDNS_DP_MSAV0_VSYNC_START(msa_v0));
+
+ vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;
+ msa_vertical_1 = CDNS_DP_MSAV1_VSYNC_WIDTH(vsync) |
+ CDNS_DP_MSAV1_VDISP_WIDTH(mode->crtc_vdisplay);
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ msa_vertical_1 |= CDNS_DP_MSAV1_VSYNC_POL_LOW;
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_VERTICAL_1(stream_id),
+ msa_vertical_1);
+
+ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
+ mode->crtc_vtotal % 2 == 0)
+ misc1 = DP_TEST_INTERLACED;
+ if (mhdp->display_fmt.y_only)
+ misc1 |= CDNS_DP_TEST_COLOR_FORMAT_RAW_Y_ONLY;
+ /* Use VSC SDP for Y420 */
+ if (pxlfmt == DRM_COLOR_FORMAT_YCRCB420)
+ misc1 = CDNS_DP_TEST_VSC_SDP;
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_MISC(stream_id),
+ misc0 | (misc1 << 8));
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_HORIZONTAL(stream_id),
+ CDNS_DP_H_HSYNC_WIDTH(hsync) |
+ CDNS_DP_H_H_TOTAL(mode->crtc_hdisplay));
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_VERTICAL_0(stream_id),
+ CDNS_DP_V0_VHEIGHT(mode->crtc_vdisplay) |
+ CDNS_DP_V0_VSTART(msa_v0));
+
+ dp_vertical_1 = CDNS_DP_V1_VTOTAL(mode->crtc_vtotal);
+ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
+ mode->crtc_vtotal % 2 == 0)
+ dp_vertical_1 |= CDNS_DP_V1_VTOTAL_EVEN;
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_VERTICAL_1(stream_id), dp_vertical_1);
+
+ cdns_mhdp_reg_write_bit(mhdp, CDNS_DP_VB_ID(stream_id), 2, 1,
+ (mode->flags & DRM_MODE_FLAG_INTERLACE) ?
+ CDNS_DP_VB_ID_INTERLACED : 0);
+
+ ret = cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, &framer);
+ if (ret < 0) {
+ dev_err(mhdp->dev,
+ "Failed to read CDNS_DP_FRAMER_GLOBAL_CONFIG %d\n",
+ ret);
+ return;
+ }
+ framer |= CDNS_DP_FRAMER_EN;
+ framer &= ~CDNS_DP_NO_VIDEO_MODE;
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, framer);
+}
+
+static void cdns_mhdp_sst_enable(struct cdns_mhdp_device *mhdp,
+ const struct drm_display_mode *mode)
+{
+ u32 rate, vs, required_bandwidth, available_bandwidth;
+ s32 line_thresh1, line_thresh2, line_thresh = 0;
+ int pxlclock = mode->crtc_clock;
+ u32 tu_size = 64;
+ u32 bpp;
+
+ /* Get rate in MSymbols per second per lane */
+ rate = mhdp->link.rate / 1000;
+
+ bpp = cdns_mhdp_get_bpp(&mhdp->display_fmt);
+
+ required_bandwidth = pxlclock * bpp / 8;
+ available_bandwidth = mhdp->link.num_lanes * rate;
+
+ vs = tu_size * required_bandwidth / available_bandwidth;
+ vs /= 1000;
+
+ if (vs == tu_size)
+ vs = tu_size - 1;
+
+ line_thresh1 = ((vs + 1) << 5) * 8 / bpp;
+ line_thresh2 = (pxlclock << 5) / 1000 / rate * (vs + 1) - (1 << 5);
+ line_thresh = line_thresh1 - line_thresh2 / (s32)mhdp->link.num_lanes;
+ line_thresh = (line_thresh >> 5) + 2;
+
+ mhdp->stream_id = 0;
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_TU,
+ CDNS_DP_FRAMER_TU_VS(vs) |
+ CDNS_DP_FRAMER_TU_SIZE(tu_size) |
+ CDNS_DP_FRAMER_TU_CNT_RST_EN);
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_LINE_THRESH(0),
+ line_thresh & GENMASK(5, 0));
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_STREAM_CONFIG_2(0),
+ CDNS_DP_SC2_TU_VS_DIFF((tu_size - vs > 3) ?
+ 0 : tu_size - vs));
+
+ cdns_mhdp_configure_video(mhdp, mode);
+}
+
+static void cdns_mhdp_atomic_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state)
+{
+ struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge);
+ struct drm_atomic_state *state = bridge_state->base.state;
+ struct cdns_mhdp_bridge_state *mhdp_state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector *connector;
+ struct drm_connector_state *conn_state;
+ struct drm_bridge_state *new_state;
+ const struct drm_display_mode *mode;
+ u32 resp;
+ int ret;
+
+ dev_dbg(mhdp->dev, "bridge enable\n");
+
+ mutex_lock(&mhdp->link_mutex);
+
+ if (mhdp->plugged && !mhdp->link_up) {
+ ret = cdns_mhdp_link_up(mhdp);
+ if (ret < 0)
+ goto out;
+ }
+
+ if (mhdp->info && mhdp->info->ops && mhdp->info->ops->enable)
+ mhdp->info->ops->enable(mhdp);
+
+ /* Enable VIF clock for stream 0 */
+ ret = cdns_mhdp_reg_read(mhdp, CDNS_DPTX_CAR, &resp);
+ if (ret < 0) {
+ dev_err(mhdp->dev, "Failed to read CDNS_DPTX_CAR %d\n", ret);
+ goto out;
+ }
+
+ cdns_mhdp_reg_write(mhdp, CDNS_DPTX_CAR,
+ resp | CDNS_VIF_CLK_EN | CDNS_VIF_CLK_RSTN);
+
+ connector = drm_atomic_get_new_connector_for_encoder(state,
+ bridge->encoder);
+ if (WARN_ON(!connector))
+ goto out;
+
+ conn_state = drm_atomic_get_new_connector_state(state, connector);
+ if (WARN_ON(!conn_state))
+ goto out;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+ if (WARN_ON(!crtc_state))
+ goto out;
+
+ mode = &crtc_state->adjusted_mode;
+
+ new_state = drm_atomic_get_new_bridge_state(state, bridge);
+ if (WARN_ON(!new_state))
+ goto out;
+
+ if (!cdns_mhdp_bandwidth_ok(mhdp, mode, mhdp->link.num_lanes,
+ mhdp->link.rate)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cdns_mhdp_sst_enable(mhdp, mode);
+
+ mhdp_state = to_cdns_mhdp_bridge_state(new_state);
+
+ mhdp_state->current_mode = drm_mode_duplicate(bridge->dev, mode);
+ drm_mode_set_name(mhdp_state->current_mode);
+
+ dev_dbg(mhdp->dev, "%s: Enabling mode %s\n", __func__, mode->name);
+
+ mhdp->bridge_enabled = true;
+
+out:
+ mutex_unlock(&mhdp->link_mutex);
+ if (ret < 0)
+ schedule_work(&mhdp->modeset_retry_work);
+}
+
+static void cdns_mhdp_atomic_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state)
+{
+ struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge);
+ u32 resp;
+
+ dev_dbg(mhdp->dev, "%s\n", __func__);
+
+ mutex_lock(&mhdp->link_mutex);
+
+ mhdp->bridge_enabled = false;
+ cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, &resp);
+ resp &= ~CDNS_DP_FRAMER_EN;
+ resp |= CDNS_DP_NO_VIDEO_MODE;
+ cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, resp);
+
+ cdns_mhdp_link_down(mhdp);
+
+ /* Disable VIF clock for stream 0 */
+ cdns_mhdp_reg_read(mhdp, CDNS_DPTX_CAR, &resp);
+ cdns_mhdp_reg_write(mhdp, CDNS_DPTX_CAR,
+ resp & ~(CDNS_VIF_CLK_EN | CDNS_VIF_CLK_RSTN));
+
+ if (mhdp->info && mhdp->info->ops && mhdp->info->ops->disable)
+ mhdp->info->ops->disable(mhdp);
+
+ mutex_unlock(&mhdp->link_mutex);
+}
+
+static void cdns_mhdp_detach(struct drm_bridge *bridge)
+{
+ struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge);
+
+ dev_dbg(mhdp->dev, "%s\n", __func__);
+
+ spin_lock(&mhdp->start_lock);
+
+ mhdp->bridge_attached = false;
+
+ spin_unlock(&mhdp->start_lock);
+
+ writel(~0, mhdp->regs + CDNS_APB_INT_MASK);
+}
+
+static struct drm_bridge_state *
+cdns_mhdp_bridge_atomic_duplicate_state(struct drm_bridge *bridge)
+{
+ struct cdns_mhdp_bridge_state *state;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ __drm_atomic_helper_bridge_duplicate_state(bridge, &state->base);
+
+ return &state->base;
+}
+
+static void
+cdns_mhdp_bridge_atomic_destroy_state(struct drm_bridge *bridge,
+ struct drm_bridge_state *state)
+{
+ struct cdns_mhdp_bridge_state *cdns_mhdp_state;
+
+ cdns_mhdp_state = to_cdns_mhdp_bridge_state(state);
+
+ if (cdns_mhdp_state->current_mode) {
+ drm_mode_destroy(bridge->dev, cdns_mhdp_state->current_mode);
+ cdns_mhdp_state->current_mode = NULL;
+ }
+
+ kfree(cdns_mhdp_state);
+}
+
+static struct drm_bridge_state *
+cdns_mhdp_bridge_atomic_reset(struct drm_bridge *bridge)
+{
+ struct cdns_mhdp_bridge_state *cdns_mhdp_state;
+
+ cdns_mhdp_state = kzalloc(sizeof(*cdns_mhdp_state), GFP_KERNEL);
+ if (!cdns_mhdp_state)
+ return NULL;
+
+ __drm_atomic_helper_bridge_reset(bridge, &cdns_mhdp_state->base);
+
+ return &cdns_mhdp_state->base;
+}
+
+static int cdns_mhdp_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge);
+ const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+
+ mutex_lock(&mhdp->link_mutex);
+
+ if (!cdns_mhdp_bandwidth_ok(mhdp, mode, mhdp->link.num_lanes,
+ mhdp->link.rate)) {
+ dev_err(mhdp->dev, "%s: Not enough BW for %s (%u lanes at %u Mbps)\n",
+ __func__, mode->name, mhdp->link.num_lanes,
+ mhdp->link.rate / 100);
+ mutex_unlock(&mhdp->link_mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&mhdp->link_mutex);
+ return 0;
+}
+
+static enum drm_connector_status cdns_mhdp_bridge_detect(struct drm_bridge *bridge)
+{
+ struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge);
+
+ return cdns_mhdp_detect(mhdp);
+}
+
+static struct edid *cdns_mhdp_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge);
+
+ return cdns_mhdp_get_edid(mhdp, connector);
+}
+
+static void cdns_mhdp_bridge_hpd_enable(struct drm_bridge *bridge)
+{
+ struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge);
+
+ /* Enable SW event interrupts */
+ if (mhdp->bridge_attached)
+ writel(~CDNS_APB_INT_MASK_SW_EVENT_INT,
+ mhdp->regs + CDNS_APB_INT_MASK);
+}
+
+static void cdns_mhdp_bridge_hpd_disable(struct drm_bridge *bridge)
+{
+ struct cdns_mhdp_device *mhdp = bridge_to_mhdp(bridge);
+
+ writel(CDNS_APB_INT_MASK_SW_EVENT_INT, mhdp->regs + CDNS_APB_INT_MASK);
+}
+
+static const struct drm_bridge_funcs cdns_mhdp_bridge_funcs = {
+ .atomic_enable = cdns_mhdp_atomic_enable,
+ .atomic_disable = cdns_mhdp_atomic_disable,
+ .atomic_check = cdns_mhdp_atomic_check,
+ .attach = cdns_mhdp_attach,
+ .detach = cdns_mhdp_detach,
+ .atomic_duplicate_state = cdns_mhdp_bridge_atomic_duplicate_state,
+ .atomic_destroy_state = cdns_mhdp_bridge_atomic_destroy_state,
+ .atomic_reset = cdns_mhdp_bridge_atomic_reset,
+ .detect = cdns_mhdp_bridge_detect,
+ .get_edid = cdns_mhdp_bridge_get_edid,
+ .hpd_enable = cdns_mhdp_bridge_hpd_enable,
+ .hpd_disable = cdns_mhdp_bridge_hpd_disable,
+};
+
+static bool cdns_mhdp_detect_hpd(struct cdns_mhdp_device *mhdp, bool *hpd_pulse)
+{
+ int hpd_event, hpd_status;
+
+ *hpd_pulse = false;
+
+ hpd_event = cdns_mhdp_read_hpd_event(mhdp);
+
+ /* Getting event bits failed, bail out */
+ if (hpd_event < 0) {
+ dev_warn(mhdp->dev, "%s: read event failed: %d\n",
+ __func__, hpd_event);
+ return false;
+ }
+
+ hpd_status = cdns_mhdp_get_hpd_status(mhdp);
+ if (hpd_status < 0) {
+ dev_warn(mhdp->dev, "%s: get hpd status failed: %d\n",
+ __func__, hpd_status);
+ return false;
+ }
+
+ if (hpd_event & DPTX_READ_EVENT_HPD_PULSE)
+ *hpd_pulse = true;
+
+ return !!hpd_status;
+}
+
+static int cdns_mhdp_update_link_status(struct cdns_mhdp_device *mhdp)
+{
+ struct cdns_mhdp_bridge_state *cdns_bridge_state;
+ struct drm_display_mode *current_mode;
+ bool old_plugged = mhdp->plugged;
+ struct drm_bridge_state *state;
+ u8 status[DP_LINK_STATUS_SIZE];
+ bool hpd_pulse;
+ int ret = 0;
+
+ mutex_lock(&mhdp->link_mutex);
+
+ mhdp->plugged = cdns_mhdp_detect_hpd(mhdp, &hpd_pulse);
+
+ if (!mhdp->plugged) {
+ cdns_mhdp_link_down(mhdp);
+ mhdp->link.rate = mhdp->host.link_rate;
+ mhdp->link.num_lanes = mhdp->host.lanes_cnt;
+ goto out;
+ }
+
+ /*
+ * If we get a HPD pulse event and we were and still are connected,
+ * check the link status. If link status is ok, there's nothing to do
+ * as we don't handle DP interrupts. If link status is bad, continue
+ * with full link setup.
+ */
+ if (hpd_pulse && old_plugged == mhdp->plugged) {
+ ret = drm_dp_dpcd_read_link_status(&mhdp->aux, status);
+
+ /*
+ * If everything looks fine, just return, as we don't handle
+ * DP IRQs.
+ */
+ if (ret > 0 &&
+ drm_dp_channel_eq_ok(status, mhdp->link.num_lanes) &&
+ drm_dp_clock_recovery_ok(status, mhdp->link.num_lanes))
+ goto out;
+
+ /* If link is bad, mark link as down so that we do a new LT */
+ mhdp->link_up = false;
+ }
+
+ if (!mhdp->link_up) {
+ ret = cdns_mhdp_link_up(mhdp);
+ if (ret < 0)
+ goto out;
+ }
+
+ if (mhdp->bridge_enabled) {
+ state = drm_priv_to_bridge_state(mhdp->bridge.base.state);
+ if (!state) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cdns_bridge_state = to_cdns_mhdp_bridge_state(state);
+ if (!cdns_bridge_state) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ current_mode = cdns_bridge_state->current_mode;
+ if (!current_mode) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!cdns_mhdp_bandwidth_ok(mhdp, current_mode, mhdp->link.num_lanes,
+ mhdp->link.rate)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ dev_dbg(mhdp->dev, "%s: Enabling mode %s\n", __func__,
+ current_mode->name);
+
+ cdns_mhdp_sst_enable(mhdp, current_mode);
+ }
+out:
+ mutex_unlock(&mhdp->link_mutex);
+ return ret;
+}
+
+static void cdns_mhdp_modeset_retry_fn(struct work_struct *work)
+{
+ struct cdns_mhdp_device *mhdp;
+ struct drm_connector *conn;
+
+ mhdp = container_of(work, typeof(*mhdp), modeset_retry_work);
+
+ conn = &mhdp->connector;
+
+ /* Grab the locks before changing connector property */
+ mutex_lock(&conn->dev->mode_config.mutex);
+
+ /*
+ * Set connector link status to BAD and send a Uevent to notify
+ * userspace to do a modeset.
+ */
+ drm_connector_set_link_status_property(conn, DRM_MODE_LINK_STATUS_BAD);
+ mutex_unlock(&conn->dev->mode_config.mutex);
+
+ /* Send Hotplug uevent so userspace can reprobe */
+ drm_kms_helper_hotplug_event(mhdp->bridge.dev);
+}
+
+static irqreturn_t cdns_mhdp_irq_handler(int irq, void *data)
+{
+ struct cdns_mhdp_device *mhdp = data;
+ u32 apb_stat, sw_ev0;
+ bool bridge_attached;
+ int ret;
+
+ apb_stat = readl(mhdp->regs + CDNS_APB_INT_STATUS);
+ if (!(apb_stat & CDNS_APB_INT_MASK_SW_EVENT_INT))
+ return IRQ_NONE;
+
+ sw_ev0 = readl(mhdp->regs + CDNS_SW_EVENT0);
+
+ /*
+ * Calling drm_kms_helper_hotplug_event() when not attached
+ * to drm device causes an oops because the drm_bridge->dev
+ * is NULL. See cdns_mhdp_fw_cb() comments for details about the
+ * problems related drm_kms_helper_hotplug_event() call.
+ */
+ spin_lock(&mhdp->start_lock);
+ bridge_attached = mhdp->bridge_attached;
+ spin_unlock(&mhdp->start_lock);
+
+ if (bridge_attached && (sw_ev0 & CDNS_DPTX_HPD)) {
+ ret = cdns_mhdp_update_link_status(mhdp);
+ if (mhdp->connector.dev) {
+ if (ret < 0)
+ schedule_work(&mhdp->modeset_retry_work);
+ else
+ drm_kms_helper_hotplug_event(mhdp->bridge.dev);
+ } else {
+ drm_bridge_hpd_notify(&mhdp->bridge, cdns_mhdp_detect(mhdp));
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int cdns_mhdp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cdns_mhdp_device *mhdp;
+ unsigned long rate;
+ struct clk *clk;
+ int ret;
+ int irq;
+
+ mhdp = devm_kzalloc(dev, sizeof(*mhdp), GFP_KERNEL);
+ if (!mhdp)
+ return -ENOMEM;
+
+ clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "couldn't get clk: %ld\n", PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+
+ mhdp->clk = clk;
+ mhdp->dev = dev;
+ mutex_init(&mhdp->mbox_mutex);
+ mutex_init(&mhdp->link_mutex);
+ spin_lock_init(&mhdp->start_lock);
+
+ drm_dp_aux_init(&mhdp->aux);
+ mhdp->aux.dev = dev;
+ mhdp->aux.transfer = cdns_mhdp_transfer;
+
+ mhdp->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mhdp->regs)) {
+ dev_err(dev, "Failed to get memory resource\n");
+ return PTR_ERR(mhdp->regs);
+ }
+
+ mhdp->phy = devm_of_phy_get_by_index(dev, pdev->dev.of_node, 0);
+ if (IS_ERR(mhdp->phy)) {
+ dev_err(dev, "no PHY configured\n");
+ return PTR_ERR(mhdp->phy);
+ }
+
+ platform_set_drvdata(pdev, mhdp);
+
+ mhdp->info = of_device_get_match_data(dev);
+
+ clk_prepare_enable(clk);
+
+ pm_runtime_enable(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "pm_runtime_get_sync failed\n");
+ pm_runtime_disable(dev);
+ goto clk_disable;
+ }
+
+ if (mhdp->info && mhdp->info->ops && mhdp->info->ops->init) {
+ ret = mhdp->info->ops->init(mhdp);
+ if (ret != 0) {
+ dev_err(dev, "MHDP platform initialization failed: %d\n",
+ ret);
+ goto runtime_put;
+ }
+ }
+
+ rate = clk_get_rate(clk);
+ writel(rate % 1000000, mhdp->regs + CDNS_SW_CLK_L);
+ writel(rate / 1000000, mhdp->regs + CDNS_SW_CLK_H);
+
+ dev_dbg(dev, "func clk rate %lu Hz\n", rate);
+
+ writel(~0, mhdp->regs + CDNS_APB_INT_MASK);
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_threaded_irq(mhdp->dev, irq, NULL,
+ cdns_mhdp_irq_handler, IRQF_ONESHOT,
+ "mhdp8546", mhdp);
+ if (ret) {
+ dev_err(dev, "cannot install IRQ %d\n", irq);
+ ret = -EIO;
+ goto plat_fini;
+ }
+
+ cdns_mhdp_fill_host_caps(mhdp);
+
+ /* Initialize link rate and num of lanes to host values */
+ mhdp->link.rate = mhdp->host.link_rate;
+ mhdp->link.num_lanes = mhdp->host.lanes_cnt;
+
+ /* The only currently supported format */
+ mhdp->display_fmt.y_only = false;
+ mhdp->display_fmt.color_format = DRM_COLOR_FORMAT_RGB444;
+ mhdp->display_fmt.bpc = 8;
+
+ mhdp->bridge.of_node = pdev->dev.of_node;
+ mhdp->bridge.funcs = &cdns_mhdp_bridge_funcs;
+ mhdp->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
+ DRM_BRIDGE_OP_HPD;
+ mhdp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
+ if (mhdp->info)
+ mhdp->bridge.timings = mhdp->info->timings;
+
+ ret = phy_init(mhdp->phy);
+ if (ret) {
+ dev_err(mhdp->dev, "Failed to initialize PHY: %d\n", ret);
+ goto plat_fini;
+ }
+
+ /* Initialize the work for modeset in case of link train failure */
+ INIT_WORK(&mhdp->modeset_retry_work, cdns_mhdp_modeset_retry_fn);
+
+ init_waitqueue_head(&mhdp->fw_load_wq);
+
+ ret = cdns_mhdp_load_firmware(mhdp);
+ if (ret)
+ goto phy_exit;
+
+ drm_bridge_add(&mhdp->bridge);
+
+ return 0;
+
+phy_exit:
+ phy_exit(mhdp->phy);
+plat_fini:
+ if (mhdp->info && mhdp->info->ops && mhdp->info->ops->exit)
+ mhdp->info->ops->exit(mhdp);
+runtime_put:
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+clk_disable:
+ clk_disable_unprepare(mhdp->clk);
+
+ return ret;
+}
+
+static int cdns_mhdp_remove(struct platform_device *pdev)
+{
+ struct cdns_mhdp_device *mhdp = dev_get_drvdata(&pdev->dev);
+ unsigned long timeout = msecs_to_jiffies(100);
+ bool stop_fw = false;
+ int ret;
+
+ drm_bridge_remove(&mhdp->bridge);
+
+ ret = wait_event_timeout(mhdp->fw_load_wq,
+ mhdp->hw_state == MHDP_HW_READY,
+ timeout);
+ if (ret == 0)
+ dev_err(mhdp->dev, "%s: Timeout waiting for fw loading\n",
+ __func__);
+ else
+ stop_fw = true;
+
+ spin_lock(&mhdp->start_lock);
+ mhdp->hw_state = MHDP_HW_STOPPED;
+ spin_unlock(&mhdp->start_lock);
+
+ if (stop_fw)
+ ret = cdns_mhdp_set_firmware_active(mhdp, false);
+
+ phy_exit(mhdp->phy);
+
+ if (mhdp->info && mhdp->info->ops && mhdp->info->ops->exit)
+ mhdp->info->ops->exit(mhdp);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ cancel_work_sync(&mhdp->modeset_retry_work);
+ flush_scheduled_work();
+
+ clk_disable_unprepare(mhdp->clk);
+
+ return ret;
+}
+
+static const struct of_device_id mhdp_ids[] = {
+ { .compatible = "cdns,mhdp8546", },
+#ifdef CONFIG_DRM_CDNS_MHDP8546_J721E
+ { .compatible = "ti,j721e-mhdp8546",
+ .data = &(const struct cdns_mhdp_platform_info) {
+ .timings = &mhdp_ti_j721e_bridge_timings,
+ .ops = &mhdp_ti_j721e_ops,
+ },
+ },
+#endif
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mhdp_ids);
+
+static struct platform_driver mhdp_driver = {
+ .driver = {
+ .name = "cdns-mhdp8546",
+ .of_match_table = of_match_ptr(mhdp_ids),
+ },
+ .probe = cdns_mhdp_probe,
+ .remove = cdns_mhdp_remove,
+};
+module_platform_driver(mhdp_driver);
+
+MODULE_FIRMWARE(FW_NAME);
+
+MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
+MODULE_AUTHOR("Swapnil Jakhade <sjakhade@cadence.com>");
+MODULE_AUTHOR("Yuti Amonkar <yamonkar@cadence.com>");
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
+MODULE_DESCRIPTION("Cadence MHDP8546 DP bridge driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:cdns-mhdp8546");
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h
new file mode 100644
index 000000000000..5897a85e3159
--- /dev/null
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h
@@ -0,0 +1,400 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence MHDP8546 DP bridge driver.
+ *
+ * Copyright (C) 2020 Cadence Design Systems, Inc.
+ *
+ * Author: Quentin Schulz <quentin.schulz@free-electrons.com>
+ * Swapnil Jakhade <sjakhade@cadence.com>
+ */
+
+#ifndef CDNS_MHDP8546_CORE_H
+#define CDNS_MHDP8546_CORE_H
+
+#include <linux/bits.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_dp_helper.h>
+
+struct clk;
+struct device;
+struct phy;
+
+/* Register offsets */
+#define CDNS_APB_CTRL 0x00000
+#define CDNS_CPU_STALL BIT(3)
+
+#define CDNS_MAILBOX_FULL 0x00008
+#define CDNS_MAILBOX_EMPTY 0x0000c
+#define CDNS_MAILBOX_TX_DATA 0x00010
+#define CDNS_MAILBOX_RX_DATA 0x00014
+#define CDNS_KEEP_ALIVE 0x00018
+#define CDNS_KEEP_ALIVE_MASK GENMASK(7, 0)
+
+#define CDNS_VER_L 0x0001C
+#define CDNS_VER_H 0x00020
+#define CDNS_LIB_L_ADDR 0x00024
+#define CDNS_LIB_H_ADDR 0x00028
+
+#define CDNS_MB_INT_MASK 0x00034
+#define CDNS_MB_INT_STATUS 0x00038
+
+#define CDNS_SW_CLK_L 0x0003c
+#define CDNS_SW_CLK_H 0x00040
+
+#define CDNS_SW_EVENT0 0x00044
+#define CDNS_DPTX_HPD BIT(0)
+
+#define CDNS_SW_EVENT1 0x00048
+#define CDNS_SW_EVENT2 0x0004c
+#define CDNS_SW_EVENT3 0x00050
+
+#define CDNS_APB_INT_MASK 0x0006C
+#define CDNS_APB_INT_MASK_MAILBOX_INT BIT(0)
+#define CDNS_APB_INT_MASK_SW_EVENT_INT BIT(1)
+
+#define CDNS_APB_INT_STATUS 0x00070
+
+#define CDNS_DPTX_CAR 0x00904
+#define CDNS_VIF_CLK_EN BIT(0)
+#define CDNS_VIF_CLK_RSTN BIT(1)
+
+#define CDNS_SOURCE_VIDEO_IF(s) (0x00b00 + ((s) * 0x20))
+#define CDNS_BND_HSYNC2VSYNC(s) (CDNS_SOURCE_VIDEO_IF(s) + \
+ 0x00)
+#define CDNS_IP_DTCT_WIN GENMASK(11, 0)
+#define CDNS_IP_DET_INTERLACE_FORMAT BIT(12)
+#define CDNS_IP_BYPASS_V_INTERFACE BIT(13)
+
+#define CDNS_HSYNC2VSYNC_POL_CTRL(s) (CDNS_SOURCE_VIDEO_IF(s) + \
+ 0x10)
+#define CDNS_H2V_HSYNC_POL_ACTIVE_LOW BIT(1)
+#define CDNS_H2V_VSYNC_POL_ACTIVE_LOW BIT(2)
+
+#define CDNS_DPTX_PHY_CONFIG 0x02000
+#define CDNS_PHY_TRAINING_EN BIT(0)
+#define CDNS_PHY_TRAINING_TYPE(x) (((x) & GENMASK(3, 0)) << 1)
+#define CDNS_PHY_SCRAMBLER_BYPASS BIT(5)
+#define CDNS_PHY_ENCODER_BYPASS BIT(6)
+#define CDNS_PHY_SKEW_BYPASS BIT(7)
+#define CDNS_PHY_TRAINING_AUTO BIT(8)
+#define CDNS_PHY_LANE0_SKEW(x) (((x) & GENMASK(2, 0)) << 9)
+#define CDNS_PHY_LANE1_SKEW(x) (((x) & GENMASK(2, 0)) << 12)
+#define CDNS_PHY_LANE2_SKEW(x) (((x) & GENMASK(2, 0)) << 15)
+#define CDNS_PHY_LANE3_SKEW(x) (((x) & GENMASK(2, 0)) << 18)
+#define CDNS_PHY_COMMON_CONFIG (CDNS_PHY_LANE1_SKEW(1) | \
+ CDNS_PHY_LANE2_SKEW(2) | \
+ CDNS_PHY_LANE3_SKEW(3))
+#define CDNS_PHY_10BIT_EN BIT(21)
+
+#define CDNS_DP_FRAMER_GLOBAL_CONFIG 0x02200
+#define CDNS_DP_NUM_LANES(x) ((x) - 1)
+#define CDNS_DP_MST_EN BIT(2)
+#define CDNS_DP_FRAMER_EN BIT(3)
+#define CDNS_DP_RATE_GOVERNOR_EN BIT(4)
+#define CDNS_DP_NO_VIDEO_MODE BIT(5)
+#define CDNS_DP_DISABLE_PHY_RST BIT(6)
+#define CDNS_DP_WR_FAILING_EDGE_VSYNC BIT(7)
+
+#define CDNS_DP_FRAMER_TU 0x02208
+#define CDNS_DP_FRAMER_TU_SIZE(x) (((x) & GENMASK(6, 0)) << 8)
+#define CDNS_DP_FRAMER_TU_VS(x) ((x) & GENMASK(5, 0))
+#define CDNS_DP_FRAMER_TU_CNT_RST_EN BIT(15)
+
+#define CDNS_DP_MTPH_CONTROL 0x02264
+#define CDNS_DP_MTPH_ECF_EN BIT(0)
+#define CDNS_DP_MTPH_ACT_EN BIT(1)
+#define CDNS_DP_MTPH_LVP_EN BIT(2)
+
+#define CDNS_DP_MTPH_STATUS 0x0226C
+#define CDNS_DP_MTPH_ACT_STATUS BIT(0)
+
+#define CDNS_DP_LANE_EN 0x02300
+#define CDNS_DP_LANE_EN_LANES(x) GENMASK((x) - 1, 0)
+
+#define CDNS_DP_ENHNCD 0x02304
+
+#define CDNS_DPTX_STREAM(s) (0x03000 + (s) * 0x80)
+#define CDNS_DP_MSA_HORIZONTAL_0(s) (CDNS_DPTX_STREAM(s) + 0x00)
+#define CDNS_DP_MSAH0_H_TOTAL(x) (x)
+#define CDNS_DP_MSAH0_HSYNC_START(x) ((x) << 16)
+
+#define CDNS_DP_MSA_HORIZONTAL_1(s) (CDNS_DPTX_STREAM(s) + 0x04)
+#define CDNS_DP_MSAH1_HSYNC_WIDTH(x) (x)
+#define CDNS_DP_MSAH1_HSYNC_POL_LOW BIT(15)
+#define CDNS_DP_MSAH1_HDISP_WIDTH(x) ((x) << 16)
+
+#define CDNS_DP_MSA_VERTICAL_0(s) (CDNS_DPTX_STREAM(s) + 0x08)
+#define CDNS_DP_MSAV0_V_TOTAL(x) (x)
+#define CDNS_DP_MSAV0_VSYNC_START(x) ((x) << 16)
+
+#define CDNS_DP_MSA_VERTICAL_1(s) (CDNS_DPTX_STREAM(s) + 0x0c)
+#define CDNS_DP_MSAV1_VSYNC_WIDTH(x) (x)
+#define CDNS_DP_MSAV1_VSYNC_POL_LOW BIT(15)
+#define CDNS_DP_MSAV1_VDISP_WIDTH(x) ((x) << 16)
+
+#define CDNS_DP_MSA_MISC(s) (CDNS_DPTX_STREAM(s) + 0x10)
+#define CDNS_DP_STREAM_CONFIG(s) (CDNS_DPTX_STREAM(s) + 0x14)
+#define CDNS_DP_STREAM_CONFIG_2(s) (CDNS_DPTX_STREAM(s) + 0x2c)
+#define CDNS_DP_SC2_TU_VS_DIFF(x) ((x) << 8)
+
+#define CDNS_DP_HORIZONTAL(s) (CDNS_DPTX_STREAM(s) + 0x30)
+#define CDNS_DP_H_HSYNC_WIDTH(x) (x)
+#define CDNS_DP_H_H_TOTAL(x) ((x) << 16)
+
+#define CDNS_DP_VERTICAL_0(s) (CDNS_DPTX_STREAM(s) + 0x34)
+#define CDNS_DP_V0_VHEIGHT(x) (x)
+#define CDNS_DP_V0_VSTART(x) ((x) << 16)
+
+#define CDNS_DP_VERTICAL_1(s) (CDNS_DPTX_STREAM(s) + 0x38)
+#define CDNS_DP_V1_VTOTAL(x) (x)
+#define CDNS_DP_V1_VTOTAL_EVEN BIT(16)
+
+#define CDNS_DP_MST_SLOT_ALLOCATE(s) (CDNS_DPTX_STREAM(s) + 0x44)
+#define CDNS_DP_S_ALLOC_START_SLOT(x) (x)
+#define CDNS_DP_S_ALLOC_END_SLOT(x) ((x) << 8)
+
+#define CDNS_DP_RATE_GOVERNING(s) (CDNS_DPTX_STREAM(s) + 0x48)
+#define CDNS_DP_RG_TARG_AV_SLOTS_Y(x) (x)
+#define CDNS_DP_RG_TARG_AV_SLOTS_X(x) ((x) << 4)
+#define CDNS_DP_RG_ENABLE BIT(10)
+
+#define CDNS_DP_FRAMER_PXL_REPR(s) (CDNS_DPTX_STREAM(s) + 0x4c)
+#define CDNS_DP_FRAMER_6_BPC BIT(0)
+#define CDNS_DP_FRAMER_8_BPC BIT(1)
+#define CDNS_DP_FRAMER_10_BPC BIT(2)
+#define CDNS_DP_FRAMER_12_BPC BIT(3)
+#define CDNS_DP_FRAMER_16_BPC BIT(4)
+#define CDNS_DP_FRAMER_PXL_FORMAT 0x8
+#define CDNS_DP_FRAMER_RGB BIT(0)
+#define CDNS_DP_FRAMER_YCBCR444 BIT(1)
+#define CDNS_DP_FRAMER_YCBCR422 BIT(2)
+#define CDNS_DP_FRAMER_YCBCR420 BIT(3)
+#define CDNS_DP_FRAMER_Y_ONLY BIT(4)
+
+#define CDNS_DP_FRAMER_SP(s) (CDNS_DPTX_STREAM(s) + 0x50)
+#define CDNS_DP_FRAMER_VSYNC_POL_LOW BIT(0)
+#define CDNS_DP_FRAMER_HSYNC_POL_LOW BIT(1)
+#define CDNS_DP_FRAMER_INTERLACE BIT(2)
+
+#define CDNS_DP_LINE_THRESH(s) (CDNS_DPTX_STREAM(s) + 0x64)
+#define CDNS_DP_ACTIVE_LINE_THRESH(x) (x)
+
+#define CDNS_DP_VB_ID(s) (CDNS_DPTX_STREAM(s) + 0x68)
+#define CDNS_DP_VB_ID_INTERLACED BIT(2)
+#define CDNS_DP_VB_ID_COMPRESSED BIT(6)
+
+#define CDNS_DP_FRONT_BACK_PORCH(s) (CDNS_DPTX_STREAM(s) + 0x78)
+#define CDNS_DP_BACK_PORCH(x) (x)
+#define CDNS_DP_FRONT_PORCH(x) ((x) << 16)
+
+#define CDNS_DP_BYTE_COUNT(s) (CDNS_DPTX_STREAM(s) + 0x7c)
+#define CDNS_DP_BYTE_COUNT_BYTES_IN_CHUNK_SHIFT 16
+
+/* mailbox */
+#define MAILBOX_RETRY_US 1000
+#define MAILBOX_TIMEOUT_US 2000000
+
+#define MB_OPCODE_ID 0
+#define MB_MODULE_ID 1
+#define MB_SIZE_MSB_ID 2
+#define MB_SIZE_LSB_ID 3
+#define MB_DATA_ID 4
+
+#define MB_MODULE_ID_DP_TX 0x01
+#define MB_MODULE_ID_HDCP_TX 0x07
+#define MB_MODULE_ID_HDCP_RX 0x08
+#define MB_MODULE_ID_HDCP_GENERAL 0x09
+#define MB_MODULE_ID_GENERAL 0x0a
+
+/* firmware and opcodes */
+#define FW_NAME "cadence/mhdp8546.bin"
+#define CDNS_MHDP_IMEM 0x10000
+
+#define GENERAL_MAIN_CONTROL 0x01
+#define GENERAL_TEST_ECHO 0x02
+#define GENERAL_BUS_SETTINGS 0x03
+#define GENERAL_TEST_ACCESS 0x04
+#define GENERAL_REGISTER_READ 0x07
+
+#define DPTX_SET_POWER_MNG 0x00
+#define DPTX_GET_EDID 0x02
+#define DPTX_READ_DPCD 0x03
+#define DPTX_WRITE_DPCD 0x04
+#define DPTX_ENABLE_EVENT 0x05
+#define DPTX_WRITE_REGISTER 0x06
+#define DPTX_READ_REGISTER 0x07
+#define DPTX_WRITE_FIELD 0x08
+#define DPTX_READ_EVENT 0x0a
+#define DPTX_GET_LAST_AUX_STAUS 0x0e
+#define DPTX_HPD_STATE 0x11
+#define DPTX_ADJUST_LT 0x12
+
+#define FW_STANDBY 0
+#define FW_ACTIVE 1
+
+/* HPD */
+#define DPTX_READ_EVENT_HPD_TO_HIGH BIT(0)
+#define DPTX_READ_EVENT_HPD_TO_LOW BIT(1)
+#define DPTX_READ_EVENT_HPD_PULSE BIT(2)
+#define DPTX_READ_EVENT_HPD_STATE BIT(3)
+
+/* general */
+#define CDNS_DP_TRAINING_PATTERN_4 0x7
+
+#define CDNS_KEEP_ALIVE_TIMEOUT 2000
+
+#define CDNS_VOLT_SWING(x) ((x) & GENMASK(1, 0))
+#define CDNS_FORCE_VOLT_SWING BIT(2)
+
+#define CDNS_PRE_EMPHASIS(x) ((x) & GENMASK(1, 0))
+#define CDNS_FORCE_PRE_EMPHASIS BIT(2)
+
+#define CDNS_SUPPORT_TPS(x) BIT((x) - 1)
+
+#define CDNS_FAST_LINK_TRAINING BIT(0)
+
+#define CDNS_LANE_MAPPING_TYPE_C_LANE_0(x) ((x) & GENMASK(1, 0))
+#define CDNS_LANE_MAPPING_TYPE_C_LANE_1(x) ((x) & GENMASK(3, 2))
+#define CDNS_LANE_MAPPING_TYPE_C_LANE_2(x) ((x) & GENMASK(5, 4))
+#define CDNS_LANE_MAPPING_TYPE_C_LANE_3(x) ((x) & GENMASK(7, 6))
+#define CDNS_LANE_MAPPING_NORMAL 0xe4
+#define CDNS_LANE_MAPPING_FLIPPED 0x1b
+
+#define CDNS_DP_MAX_NUM_LANES 4
+#define CDNS_DP_TEST_VSC_SDP BIT(6) /* 1.3+ */
+#define CDNS_DP_TEST_COLOR_FORMAT_RAW_Y_ONLY BIT(7)
+
+#define CDNS_MHDP_MAX_STREAMS 4
+
+#define DP_LINK_CAP_ENHANCED_FRAMING BIT(0)
+
+struct cdns_mhdp_link {
+ unsigned char revision;
+ unsigned int rate;
+ unsigned int num_lanes;
+ unsigned long capabilities;
+};
+
+struct cdns_mhdp_host {
+ unsigned int link_rate;
+ u8 lanes_cnt;
+ u8 volt_swing;
+ u8 pre_emphasis;
+ u8 pattern_supp;
+ u8 lane_mapping;
+ bool fast_link;
+ bool enhanced;
+ bool scrambler;
+ bool ssc;
+};
+
+struct cdns_mhdp_sink {
+ unsigned int link_rate;
+ u8 lanes_cnt;
+ u8 pattern_supp;
+ bool fast_link;
+ bool enhanced;
+ bool ssc;
+};
+
+struct cdns_mhdp_display_fmt {
+ u32 color_format;
+ u32 bpc;
+ bool y_only;
+};
+
+/*
+ * These enums present MHDP hw initialization state
+ * Legal state transitions are:
+ * MHDP_HW_READY <-> MHDP_HW_STOPPED
+ */
+enum mhdp_hw_state {
+ MHDP_HW_READY = 1, /* HW ready, FW active */
+ MHDP_HW_STOPPED /* Driver removal FW to be stopped */
+};
+
+struct cdns_mhdp_device;
+
+struct mhdp_platform_ops {
+ int (*init)(struct cdns_mhdp_device *mhdp);
+ void (*exit)(struct cdns_mhdp_device *mhdp);
+ void (*enable)(struct cdns_mhdp_device *mhdp);
+ void (*disable)(struct cdns_mhdp_device *mhdp);
+};
+
+struct cdns_mhdp_bridge_state {
+ struct drm_bridge_state base;
+ struct drm_display_mode *current_mode;
+};
+
+struct cdns_mhdp_platform_info {
+ const struct drm_bridge_timings *timings;
+ const struct mhdp_platform_ops *ops;
+};
+
+#define to_cdns_mhdp_bridge_state(s) \
+ container_of(s, struct cdns_mhdp_bridge_state, base)
+
+struct cdns_mhdp_device {
+ void __iomem *regs;
+ void __iomem *j721e_regs;
+
+ struct device *dev;
+ struct clk *clk;
+ struct phy *phy;
+
+ const struct cdns_mhdp_platform_info *info;
+
+ /* This is to protect mailbox communications with the firmware */
+ struct mutex mbox_mutex;
+
+ /*
+ * "link_mutex" protects the access to all the link parameters
+ * including the link training process. Link training will be
+ * invoked both from threaded interrupt handler and from atomic
+ * callbacks when link_up is not set. So this mutex protects
+ * flags such as link_up, bridge_enabled, link.num_lanes,
+ * link.rate etc.
+ */
+ struct mutex link_mutex;
+
+ struct drm_connector connector;
+ struct drm_bridge bridge;
+
+ struct cdns_mhdp_link link;
+ struct drm_dp_aux aux;
+
+ struct cdns_mhdp_host host;
+ struct cdns_mhdp_sink sink;
+ struct cdns_mhdp_display_fmt display_fmt;
+ u8 stream_id;
+
+ bool link_up;
+ bool plugged;
+
+ /*
+ * "start_lock" protects the access to bridge_attached and
+ * hw_state data members that control the delayed firmware
+ * loading and attaching the bridge. They are accessed from
+ * both the DRM core and cdns_mhdp_fw_cb(). In most cases just
+ * protecting the data members is enough, but the irq mask
+ * setting needs to be protected when enabling the FW.
+ */
+ spinlock_t start_lock;
+ bool bridge_attached;
+ bool bridge_enabled;
+ enum mhdp_hw_state hw_state;
+ wait_queue_head_t fw_load_wq;
+
+ /* Work struct to schedule a uevent on link train failure */
+ struct work_struct modeset_retry_work;
+};
+
+#define connector_to_mhdp(x) container_of(x, struct cdns_mhdp_device, connector)
+#define bridge_to_mhdp(x) container_of(x, struct cdns_mhdp_device, bridge)
+
+#endif
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.c
new file mode 100644
index 000000000000..dfe1b59514f7
--- /dev/null
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI j721e Cadence MHDP8546 DP wrapper
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Jyri Sarha <jsarha@ti.com>
+ */
+
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+#include "cdns-mhdp8546-j721e.h"
+
+#define REVISION 0x00
+#define DPTX_IPCFG 0x04
+#define ECC_MEM_CFG 0x08
+#define DPTX_DSC_CFG 0x0c
+#define DPTX_SRC_CFG 0x10
+#define DPTX_VIF_SECURE_MODE_CFG 0x14
+#define DPTX_VIF_CONN_STATUS 0x18
+#define PHY_CLK_STATUS 0x1c
+
+#define DPTX_SRC_AIF_EN BIT(16)
+#define DPTX_SRC_VIF_3_IN30B BIT(11)
+#define DPTX_SRC_VIF_2_IN30B BIT(10)
+#define DPTX_SRC_VIF_1_IN30B BIT(9)
+#define DPTX_SRC_VIF_0_IN30B BIT(8)
+#define DPTX_SRC_VIF_3_SEL_DPI5 BIT(7)
+#define DPTX_SRC_VIF_3_SEL_DPI3 0
+#define DPTX_SRC_VIF_2_SEL_DPI4 BIT(6)
+#define DPTX_SRC_VIF_2_SEL_DPI2 0
+#define DPTX_SRC_VIF_1_SEL_DPI3 BIT(5)
+#define DPTX_SRC_VIF_1_SEL_DPI1 0
+#define DPTX_SRC_VIF_0_SEL_DPI2 BIT(4)
+#define DPTX_SRC_VIF_0_SEL_DPI0 0
+#define DPTX_SRC_VIF_3_EN BIT(3)
+#define DPTX_SRC_VIF_2_EN BIT(2)
+#define DPTX_SRC_VIF_1_EN BIT(1)
+#define DPTX_SRC_VIF_0_EN BIT(0)
+
+/* TODO turn DPTX_IPCFG fw_mem_clk_en at pm_runtime_suspend. */
+
+static int cdns_mhdp_j721e_init(struct cdns_mhdp_device *mhdp)
+{
+ struct platform_device *pdev = to_platform_device(mhdp->dev);
+
+ mhdp->j721e_regs = devm_platform_ioremap_resource(pdev, 1);
+ return PTR_ERR_OR_ZERO(mhdp->j721e_regs);
+}
+
+static void cdns_mhdp_j721e_enable(struct cdns_mhdp_device *mhdp)
+{
+ /*
+ * Enable VIF_0 and select DPI2 as its input. DSS0 DPI0 is connected
+ * to eDP DPI2. This is the only supported SST configuration on
+ * J721E.
+ */
+ writel(DPTX_SRC_VIF_0_EN | DPTX_SRC_VIF_0_SEL_DPI2,
+ mhdp->j721e_regs + DPTX_SRC_CFG);
+}
+
+static void cdns_mhdp_j721e_disable(struct cdns_mhdp_device *mhdp)
+{
+ /* Put everything to defaults */
+ writel(0, mhdp->j721e_regs + DPTX_DSC_CFG);
+}
+
+const struct mhdp_platform_ops mhdp_ti_j721e_ops = {
+ .init = cdns_mhdp_j721e_init,
+ .enable = cdns_mhdp_j721e_enable,
+ .disable = cdns_mhdp_j721e_disable,
+};
+
+const struct drm_bridge_timings mhdp_ti_j721e_bridge_timings = {
+ .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE |
+ DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE |
+ DRM_BUS_FLAG_DE_HIGH,
+};
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.h
new file mode 100644
index 000000000000..97d20d115a24
--- /dev/null
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-j721e.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * TI j721e Cadence MHDP8546 DP wrapper
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Jyri Sarha <jsarha@ti.com>
+ */
+
+#ifndef CDNS_MHDP8546_J721E_H
+#define CDNS_MHDP8546_J721E_H
+
+#include "cdns-mhdp8546-core.h"
+
+struct mhdp_platform_ops;
+
+extern const struct mhdp_platform_ops mhdp_ti_j721e_ops;
+extern const struct drm_bridge_timings mhdp_ti_j721e_bridge_timings;
+
+#endif /* !CDNS_MHDP8546_J721E_H */
diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c
index f19d9f7a5db2..f52ccffc1bd1 100644
--- a/drivers/gpu/drm/bridge/lvds-codec.c
+++ b/drivers/gpu/drm/bridge/lvds-codec.c
@@ -10,13 +10,16 @@
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <drm/drm_bridge.h>
#include <drm/drm_panel.h>
struct lvds_codec {
+ struct device *dev;
struct drm_bridge bridge;
struct drm_bridge *panel_bridge;
+ struct regulator *vcc;
struct gpio_desc *powerdown_gpio;
u32 connector_type;
};
@@ -38,6 +41,14 @@ static int lvds_codec_attach(struct drm_bridge *bridge,
static void lvds_codec_enable(struct drm_bridge *bridge)
{
struct lvds_codec *lvds_codec = to_lvds_codec(bridge);
+ int ret;
+
+ ret = regulator_enable(lvds_codec->vcc);
+ if (ret) {
+ dev_err(lvds_codec->dev,
+ "Failed to enable regulator \"vcc\": %d\n", ret);
+ return;
+ }
if (lvds_codec->powerdown_gpio)
gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 0);
@@ -46,9 +57,15 @@ static void lvds_codec_enable(struct drm_bridge *bridge)
static void lvds_codec_disable(struct drm_bridge *bridge)
{
struct lvds_codec *lvds_codec = to_lvds_codec(bridge);
+ int ret;
if (lvds_codec->powerdown_gpio)
gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 1);
+
+ ret = regulator_disable(lvds_codec->vcc);
+ if (ret)
+ dev_err(lvds_codec->dev,
+ "Failed to disable regulator \"vcc\": %d\n", ret);
}
static const struct drm_bridge_funcs funcs = {
@@ -63,12 +80,24 @@ static int lvds_codec_probe(struct platform_device *pdev)
struct device_node *panel_node;
struct drm_panel *panel;
struct lvds_codec *lvds_codec;
+ int ret;
lvds_codec = devm_kzalloc(dev, sizeof(*lvds_codec), GFP_KERNEL);
if (!lvds_codec)
return -ENOMEM;
+ lvds_codec->dev = &pdev->dev;
lvds_codec->connector_type = (uintptr_t)of_device_get_match_data(dev);
+
+ lvds_codec->vcc = devm_regulator_get(lvds_codec->dev, "power");
+ if (IS_ERR(lvds_codec->vcc)) {
+ ret = PTR_ERR(lvds_codec->vcc);
+ if (ret != -EPROBE_DEFER)
+ dev_err(lvds_codec->dev,
+ "Unable to get \"vcc\" supply: %d\n", ret);
+ return ret;
+ }
+
lvds_codec->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown",
GPIOD_OUT_HIGH);
if (IS_ERR(lvds_codec->powerdown_gpio))
diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c
index 03e01b000f7a..0fe3c496002a 100644
--- a/drivers/gpu/drm/drm_cache.c
+++ b/drivers/gpu/drm/drm_cache.c
@@ -127,7 +127,7 @@ drm_clflush_sg(struct sg_table *st)
struct sg_page_iter sg_iter;
mb(); /*CLFLUSH is ordered only by using memory barriers*/
- for_each_sg_page(st->sgl, &sg_iter, st->nents, 0)
+ for_each_sgtable_page(st, &sg_iter, 0)
drm_clflush_page(sg_page_iter_page(&sg_iter));
mb(); /*Make sure that all cache line entry is flushed*/
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 1e7c638873c8..90807a6b415c 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -363,6 +363,66 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
}
EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
+static bool is_edid_digital_input_dp(const struct edid *edid)
+{
+ return edid && edid->revision >= 4 &&
+ edid->input & DRM_EDID_INPUT_DIGITAL &&
+ (edid->input & DRM_EDID_DIGITAL_TYPE_MASK) == DRM_EDID_DIGITAL_TYPE_DP;
+}
+
+/**
+ * drm_dp_downstream_is_type() - is the downstream facing port of certain type?
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ *
+ * Caveat: Only works with DPCD 1.1+ port caps.
+ *
+ * Returns: whether the downstream facing port matches the type.
+ */
+bool drm_dp_downstream_is_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4], u8 type)
+{
+ return drm_dp_is_branch(dpcd) &&
+ dpcd[DP_DPCD_REV] >= 0x11 &&
+ (port_cap[0] & DP_DS_PORT_TYPE_MASK) == type;
+}
+EXPORT_SYMBOL(drm_dp_downstream_is_type);
+
+/**
+ * drm_dp_downstream_is_tmds() - is the downstream facing port TMDS?
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ * @edid: EDID
+ *
+ * Returns: whether the downstream facing port is TMDS (HDMI/DVI).
+ */
+bool drm_dp_downstream_is_tmds(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4],
+ const struct edid *edid)
+{
+ if (dpcd[DP_DPCD_REV] < 0x11) {
+ switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) {
+ case DP_DWN_STRM_PORT_TYPE_TMDS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) {
+ case DP_DS_PORT_TYPE_DP_DUALMODE:
+ if (is_edid_digital_input_dp(edid))
+ return false;
+ fallthrough;
+ case DP_DS_PORT_TYPE_DVI:
+ case DP_DS_PORT_TYPE_HDMI:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL(drm_dp_downstream_is_tmds);
+
/**
* drm_dp_send_real_edid_checksum() - send back real edid checksum value
* @aux: DisplayPort AUX channel
@@ -545,79 +605,191 @@ int drm_dp_read_downstream_info(struct drm_dp_aux *aux,
ret = drm_dp_dpcd_read(aux, DP_DOWNSTREAM_PORT_0, downstream_ports, len);
if (ret < 0)
return ret;
+ if (ret != len)
+ return -EIO;
+
+ DRM_DEBUG_KMS("%s: DPCD DFP: %*ph\n",
+ aux->name, len, downstream_ports);
- return ret == len ? 0 : -EIO;
+ return 0;
}
EXPORT_SYMBOL(drm_dp_read_downstream_info);
/**
- * drm_dp_downstream_max_clock() - extract branch device max
- * pixel rate for legacy VGA
- * converter or max TMDS clock
- * rate for others
+ * drm_dp_downstream_max_dotclock() - extract downstream facing port max dot clock
* @dpcd: DisplayPort configuration data
* @port_cap: port capabilities
*
- * See also:
- * drm_dp_read_downstream_info()
- * drm_dp_downstream_max_bpc()
- *
- * Returns: Max clock in kHz on success or 0 if max clock not defined
+ * Returns: Downstream facing port max dot clock in kHz on success,
+ * or 0 if max clock not defined
*/
-int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
- const u8 port_cap[4])
+int drm_dp_downstream_max_dotclock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4])
{
- int type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
- bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
- DP_DETAILED_CAP_INFO_AVAILABLE;
+ if (!drm_dp_is_branch(dpcd))
+ return 0;
- if (!detailed_cap_info)
+ if (dpcd[DP_DPCD_REV] < 0x11)
return 0;
- switch (type) {
+ switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) {
case DP_DS_PORT_TYPE_VGA:
- return port_cap[1] * 8 * 1000;
- case DP_DS_PORT_TYPE_DVI:
- case DP_DS_PORT_TYPE_HDMI:
+ if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0)
+ return 0;
+ return port_cap[1] * 8000;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(drm_dp_downstream_max_dotclock);
+
+/**
+ * drm_dp_downstream_max_tmds_clock() - extract downstream facing port max TMDS clock
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ * @edid: EDID
+ *
+ * Returns: HDMI/DVI downstream facing port max TMDS clock in kHz on success,
+ * or 0 if max TMDS clock not defined
+ */
+int drm_dp_downstream_max_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4],
+ const struct edid *edid)
+{
+ if (!drm_dp_is_branch(dpcd))
+ return 0;
+
+ if (dpcd[DP_DPCD_REV] < 0x11) {
+ switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) {
+ case DP_DWN_STRM_PORT_TYPE_TMDS:
+ return 165000;
+ default:
+ return 0;
+ }
+ }
+
+ switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) {
case DP_DS_PORT_TYPE_DP_DUALMODE:
+ if (is_edid_digital_input_dp(edid))
+ return 0;
+ /*
+ * It's left up to the driver to check the
+ * DP dual mode adapter's max TMDS clock.
+ *
+ * Unfortunatley it looks like branch devices
+ * may not fordward that the DP dual mode i2c
+ * access so we just usually get i2c nak :(
+ */
+ fallthrough;
+ case DP_DS_PORT_TYPE_HDMI:
+ /*
+ * We should perhaps assume 165 MHz when detailed cap
+ * info is not available. But looks like many typical
+ * branch devices fall into that category and so we'd
+ * probably end up with users complaining that they can't
+ * get high resolution modes with their favorite dongle.
+ *
+ * So let's limit to 300 MHz instead since DPCD 1.4
+ * HDMI 2.0 DFPs are required to have the detailed cap
+ * info. So it's more likely we're dealing with a HDMI 1.4
+ * compatible* device here.
+ */
+ if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0)
+ return 300000;
+ return port_cap[1] * 2500;
+ case DP_DS_PORT_TYPE_DVI:
+ if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0)
+ return 165000;
+ /* FIXME what to do about DVI dual link? */
return port_cap[1] * 2500;
default:
return 0;
}
}
-EXPORT_SYMBOL(drm_dp_downstream_max_clock);
+EXPORT_SYMBOL(drm_dp_downstream_max_tmds_clock);
/**
- * drm_dp_downstream_max_bpc() - extract branch device max
- * bits per component
+ * drm_dp_downstream_min_tmds_clock() - extract downstream facing port min TMDS clock
* @dpcd: DisplayPort configuration data
* @port_cap: port capabilities
+ * @edid: EDID
*
- * See also:
- * drm_dp_read_downstream_info()
- * drm_dp_downstream_max_clock()
+ * Returns: HDMI/DVI downstream facing port min TMDS clock in kHz on success,
+ * or 0 if max TMDS clock not defined
+ */
+int drm_dp_downstream_min_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4],
+ const struct edid *edid)
+{
+ if (!drm_dp_is_branch(dpcd))
+ return 0;
+
+ if (dpcd[DP_DPCD_REV] < 0x11) {
+ switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) {
+ case DP_DWN_STRM_PORT_TYPE_TMDS:
+ return 25000;
+ default:
+ return 0;
+ }
+ }
+
+ switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) {
+ case DP_DS_PORT_TYPE_DP_DUALMODE:
+ if (is_edid_digital_input_dp(edid))
+ return 0;
+ fallthrough;
+ case DP_DS_PORT_TYPE_DVI:
+ case DP_DS_PORT_TYPE_HDMI:
+ /*
+ * Unclear whether the protocol converter could
+ * utilize pixel replication. Assume it won't.
+ */
+ return 25000;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(drm_dp_downstream_min_tmds_clock);
+
+/**
+ * drm_dp_downstream_max_bpc() - extract downstream facing port max
+ * bits per component
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: downstream facing port capabilities
+ * @edid: EDID
*
* Returns: Max bpc on success or 0 if max bpc not defined
*/
int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
- const u8 port_cap[4])
+ const u8 port_cap[4],
+ const struct edid *edid)
{
- int type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
- bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
- DP_DETAILED_CAP_INFO_AVAILABLE;
- int bpc;
-
- if (!detailed_cap_info)
+ if (!drm_dp_is_branch(dpcd))
return 0;
- switch (type) {
- case DP_DS_PORT_TYPE_VGA:
- case DP_DS_PORT_TYPE_DVI:
- case DP_DS_PORT_TYPE_HDMI:
+ if (dpcd[DP_DPCD_REV] < 0x11) {
+ switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) {
+ case DP_DWN_STRM_PORT_TYPE_DP:
+ return 0;
+ default:
+ return 8;
+ }
+ }
+
+ switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) {
+ case DP_DS_PORT_TYPE_DP:
+ return 0;
case DP_DS_PORT_TYPE_DP_DUALMODE:
- bpc = port_cap[2] & DP_DS_MAX_BPC_MASK;
+ if (is_edid_digital_input_dp(edid))
+ return 0;
+ fallthrough;
+ case DP_DS_PORT_TYPE_HDMI:
+ case DP_DS_PORT_TYPE_DVI:
+ case DP_DS_PORT_TYPE_VGA:
+ if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0)
+ return 8;
- switch (bpc) {
+ switch (port_cap[2] & DP_DS_MAX_BPC_MASK) {
case DP_DS_8BPC:
return 8;
case DP_DS_10BPC:
@@ -626,15 +798,131 @@ int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
return 12;
case DP_DS_16BPC:
return 16;
+ default:
+ return 8;
}
- fallthrough;
+ break;
default:
- return 0;
+ return 8;
}
}
EXPORT_SYMBOL(drm_dp_downstream_max_bpc);
/**
+ * drm_dp_downstream_420_passthrough() - determine downstream facing port
+ * YCbCr 4:2:0 pass-through capability
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: downstream facing port capabilities
+ *
+ * Returns: whether the downstream facing port can pass through YCbCr 4:2:0
+ */
+bool drm_dp_downstream_420_passthrough(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4])
+{
+ if (!drm_dp_is_branch(dpcd))
+ return false;
+
+ if (dpcd[DP_DPCD_REV] < 0x13)
+ return false;
+
+ switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) {
+ case DP_DS_PORT_TYPE_DP:
+ return true;
+ case DP_DS_PORT_TYPE_HDMI:
+ if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0)
+ return false;
+
+ return port_cap[3] & DP_DS_HDMI_YCBCR420_PASS_THROUGH;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL(drm_dp_downstream_420_passthrough);
+
+/**
+ * drm_dp_downstream_444_to_420_conversion() - determine downstream facing port
+ * YCbCr 4:4:4->4:2:0 conversion capability
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: downstream facing port capabilities
+ *
+ * Returns: whether the downstream facing port can convert YCbCr 4:4:4 to 4:2:0
+ */
+bool drm_dp_downstream_444_to_420_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4])
+{
+ if (!drm_dp_is_branch(dpcd))
+ return false;
+
+ if (dpcd[DP_DPCD_REV] < 0x13)
+ return false;
+
+ switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) {
+ case DP_DS_PORT_TYPE_HDMI:
+ if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0)
+ return false;
+
+ return port_cap[3] & DP_DS_HDMI_YCBCR444_TO_420_CONV;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL(drm_dp_downstream_444_to_420_conversion);
+
+/**
+ * drm_dp_downstream_mode() - return a mode for downstream facing port
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ *
+ * Provides a suitable mode for downstream facing ports without EDID.
+ *
+ * Returns: A new drm_display_mode on success or NULL on failure
+ */
+struct drm_display_mode *
+drm_dp_downstream_mode(struct drm_device *dev,
+ const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4])
+
+{
+ u8 vic;
+
+ if (!drm_dp_is_branch(dpcd))
+ return NULL;
+
+ if (dpcd[DP_DPCD_REV] < 0x11)
+ return NULL;
+
+ switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) {
+ case DP_DS_PORT_TYPE_NON_EDID:
+ switch (port_cap[0] & DP_DS_NON_EDID_MASK) {
+ case DP_DS_NON_EDID_720x480i_60:
+ vic = 6;
+ break;
+ case DP_DS_NON_EDID_720x480i_50:
+ vic = 21;
+ break;
+ case DP_DS_NON_EDID_1920x1080i_60:
+ vic = 5;
+ break;
+ case DP_DS_NON_EDID_1920x1080i_50:
+ vic = 20;
+ break;
+ case DP_DS_NON_EDID_1280x720_60:
+ vic = 4;
+ break;
+ case DP_DS_NON_EDID_1280x720_50:
+ vic = 19;
+ break;
+ default:
+ return NULL;
+ }
+ return drm_display_mode_from_cea_vic(dev, vic);
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(drm_dp_downstream_mode);
+
+/**
* drm_dp_downstream_id() - identify branch device
* @aux: DisplayPort AUX channel
* @id: DisplayPort branch device id
@@ -652,12 +940,15 @@ EXPORT_SYMBOL(drm_dp_downstream_id);
* @m: pointer for debugfs file
* @dpcd: DisplayPort configuration data
* @port_cap: port capabilities
+ * @edid: EDID
* @aux: DisplayPort AUX channel
*
*/
void drm_dp_downstream_debug(struct seq_file *m,
const u8 dpcd[DP_RECEIVER_CAP_SIZE],
- const u8 port_cap[4], struct drm_dp_aux *aux)
+ const u8 port_cap[4],
+ const struct edid *edid,
+ struct drm_dp_aux *aux)
{
bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
DP_DETAILED_CAP_INFO_AVAILABLE;
@@ -715,16 +1006,19 @@ void drm_dp_downstream_debug(struct seq_file *m,
seq_printf(m, "\t\tSW: %d.%d\n", rev[0], rev[1]);
if (detailed_cap_info) {
- clk = drm_dp_downstream_max_clock(dpcd, port_cap);
+ clk = drm_dp_downstream_max_dotclock(dpcd, port_cap);
+ if (clk > 0)
+ seq_printf(m, "\t\tMax dot clock: %d kHz\n", clk);
- if (clk > 0) {
- if (type == DP_DS_PORT_TYPE_VGA)
- seq_printf(m, "\t\tMax dot clock: %d kHz\n", clk);
- else
- seq_printf(m, "\t\tMax TMDS clock: %d kHz\n", clk);
- }
+ clk = drm_dp_downstream_max_tmds_clock(dpcd, port_cap, edid);
+ if (clk > 0)
+ seq_printf(m, "\t\tMax TMDS clock: %d kHz\n", clk);
+
+ clk = drm_dp_downstream_min_tmds_clock(dpcd, port_cap, edid);
+ if (clk > 0)
+ seq_printf(m, "\t\tMin TMDS clock: %d kHz\n", clk);
- bpc = drm_dp_downstream_max_bpc(dpcd, port_cap);
+ bpc = drm_dp_downstream_max_bpc(dpcd, port_cap, edid);
if (bpc > 0)
seq_printf(m, "\t\tMax bpc: %d\n", bpc);
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 17dbed0a9800..54864400015d 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -20,11 +20,13 @@
* OF THIS SOFTWARE.
*/
+#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/random.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/iopoll.h>
@@ -423,6 +425,22 @@ drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
idx += req->u.i2c_write.num_bytes;
break;
+ case DP_QUERY_STREAM_ENC_STATUS: {
+ const struct drm_dp_query_stream_enc_status *msg;
+
+ msg = &req->u.enc_status;
+ buf[idx] = msg->stream_id;
+ idx++;
+ memcpy(&buf[idx], msg->client_id, sizeof(msg->client_id));
+ idx += sizeof(msg->client_id);
+ buf[idx] = 0;
+ buf[idx] |= FIELD_PREP(GENMASK(1, 0), msg->stream_event);
+ buf[idx] |= msg->valid_stream_event ? BIT(2) : 0;
+ buf[idx] |= FIELD_PREP(GENMASK(4, 3), msg->stream_behavior);
+ buf[idx] |= msg->valid_stream_behavior ? BIT(5) : 0;
+ idx++;
+ }
+ break;
}
raw->cur_len = idx;
}
@@ -551,6 +569,20 @@ drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
return -ENOMEM;
}
break;
+ case DP_QUERY_STREAM_ENC_STATUS:
+ req->u.enc_status.stream_id = buf[idx++];
+ for (i = 0; i < sizeof(req->u.enc_status.client_id); i++)
+ req->u.enc_status.client_id[i] = buf[idx++];
+
+ req->u.enc_status.stream_event = FIELD_GET(GENMASK(1, 0),
+ buf[idx]);
+ req->u.enc_status.valid_stream_event = FIELD_GET(BIT(2),
+ buf[idx]);
+ req->u.enc_status.stream_behavior = FIELD_GET(GENMASK(4, 3),
+ buf[idx]);
+ req->u.enc_status.valid_stream_behavior = FIELD_GET(BIT(5),
+ buf[idx]);
+ break;
}
return 0;
@@ -629,6 +661,16 @@ drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req
req->u.i2c_write.num_bytes, req->u.i2c_write.num_bytes,
req->u.i2c_write.bytes);
break;
+ case DP_QUERY_STREAM_ENC_STATUS:
+ P("stream_id=%u client_id=%*ph stream_event=%x "
+ "valid_event=%d stream_behavior=%x valid_behavior=%d",
+ req->u.enc_status.stream_id,
+ (int)ARRAY_SIZE(req->u.enc_status.client_id),
+ req->u.enc_status.client_id, req->u.enc_status.stream_event,
+ req->u.enc_status.valid_stream_event,
+ req->u.enc_status.stream_behavior,
+ req->u.enc_status.valid_stream_behavior);
+ break;
default:
P("???\n");
break;
@@ -936,6 +978,42 @@ static bool drm_dp_sideband_parse_power_updown_phy_ack(struct drm_dp_sideband_ms
return true;
}
+static bool
+drm_dp_sideband_parse_query_stream_enc_status(
+ struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+ struct drm_dp_query_stream_enc_status_ack_reply *reply;
+
+ reply = &repmsg->u.enc_status;
+
+ reply->stream_id = raw->msg[3];
+
+ reply->reply_signed = raw->msg[2] & BIT(0);
+
+ /*
+ * NOTE: It's my impression from reading the spec that the below parsing
+ * is correct. However I noticed while testing with an HDCP 1.4 display
+ * through an HDCP 2.2 hub that only bit 3 was set. In that case, I
+ * would expect both bits to be set. So keep the parsing following the
+ * spec, but beware reality might not match the spec (at least for some
+ * configurations).
+ */
+ reply->hdcp_1x_device_present = raw->msg[2] & BIT(4);
+ reply->hdcp_2x_device_present = raw->msg[2] & BIT(3);
+
+ reply->query_capable_device_present = raw->msg[2] & BIT(5);
+ reply->legacy_device_present = raw->msg[2] & BIT(6);
+ reply->unauthorizable_device_present = raw->msg[2] & BIT(7);
+
+ reply->auth_completed = !!(raw->msg[1] & BIT(3));
+ reply->encryption_enabled = !!(raw->msg[1] & BIT(4));
+ reply->repeater_present = !!(raw->msg[1] & BIT(5));
+ reply->state = (raw->msg[1] & GENMASK(7, 6)) >> 6;
+
+ return true;
+}
+
static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
struct drm_dp_sideband_msg_reply_body *msg)
{
@@ -970,6 +1048,8 @@ static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
return drm_dp_sideband_parse_power_updown_phy_ack(raw, msg);
case DP_CLEAR_PAYLOAD_ID_TABLE:
return true; /* since there's nothing to parse */
+ case DP_QUERY_STREAM_ENC_STATUS:
+ return drm_dp_sideband_parse_query_stream_enc_status(raw, msg);
default:
DRM_ERROR("Got unknown reply 0x%02x (%s)\n", msg->req_type,
drm_dp_mst_req_type_str(msg->req_type));
@@ -1121,6 +1201,25 @@ static void build_power_updown_phy(struct drm_dp_sideband_msg_tx *msg,
msg->path_msg = true;
}
+static int
+build_query_stream_enc_status(struct drm_dp_sideband_msg_tx *msg, u8 stream_id,
+ u8 *q_id)
+{
+ struct drm_dp_sideband_msg_req_body req;
+
+ req.req_type = DP_QUERY_STREAM_ENC_STATUS;
+ req.u.enc_status.stream_id = stream_id;
+ memcpy(req.u.enc_status.client_id, q_id,
+ sizeof(req.u.enc_status.client_id));
+ req.u.enc_status.stream_event = 0;
+ req.u.enc_status.valid_stream_event = false;
+ req.u.enc_status.stream_behavior = 0;
+ req.u.enc_status.valid_stream_behavior = false;
+
+ drm_dp_encode_sideband_req(&req, msg);
+ return 0;
+}
+
static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_vcpi *vcpi)
{
@@ -3153,6 +3252,57 @@ int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
}
EXPORT_SYMBOL(drm_dp_send_power_updown_phy);
+int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port,
+ struct drm_dp_query_stream_enc_status_ack_reply *status)
+{
+ struct drm_dp_sideband_msg_tx *txmsg;
+ u8 nonce[7];
+ int len, ret;
+
+ txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+ if (!txmsg)
+ return -ENOMEM;
+
+ port = drm_dp_mst_topology_get_port_validated(mgr, port);
+ if (!port) {
+ ret = -EINVAL;
+ goto out_get_port;
+ }
+
+ get_random_bytes(nonce, sizeof(nonce));
+
+ /*
+ * "Source device targets the QUERY_STREAM_ENCRYPTION_STATUS message
+ * transaction at the MST Branch device directly connected to the
+ * Source"
+ */
+ txmsg->dst = mgr->mst_primary;
+
+ len = build_query_stream_enc_status(txmsg, port->vcpi.vcpi, nonce);
+
+ drm_dp_queue_down_tx(mgr, txmsg);
+
+ ret = drm_dp_mst_wait_tx_reply(mgr->mst_primary, txmsg);
+ if (ret < 0) {
+ goto out;
+ } else if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
+ drm_dbg_kms(mgr->dev, "query encryption status nak received\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ ret = 0;
+ memcpy(status, &txmsg->reply.u.enc_status, sizeof(*status));
+
+out:
+ drm_dp_mst_topology_put_port(port);
+out_get_port:
+ kfree(txmsg);
+ return ret;
+}
+EXPORT_SYMBOL(drm_dp_send_query_stream_enc_status);
+
static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
int id,
struct drm_dp_payload *payload)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 6840f0530a38..a82f37d44258 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -3738,6 +3738,34 @@ drm_add_cmdb_modes(struct drm_connector *connector, u8 svd)
bitmap_set(hdmi->y420_cmdb_modes, vic, 1);
}
+/**
+ * drm_display_mode_from_cea_vic() - return a mode for CEA VIC
+ * @dev: DRM device
+ * @vic: CEA VIC of the mode
+ *
+ * Creates a new mode matching the specified CEA VIC.
+ *
+ * Returns: A new drm_display_mode on success or NULL on failure
+ */
+struct drm_display_mode *
+drm_display_mode_from_cea_vic(struct drm_device *dev,
+ u8 video_code)
+{
+ const struct drm_display_mode *cea_mode;
+ struct drm_display_mode *newmode;
+
+ cea_mode = cea_mode_for_vic(video_code);
+ if (!cea_mode)
+ return NULL;
+
+ newmode = drm_mode_duplicate(dev, cea_mode);
+ if (!newmode)
+ return NULL;
+
+ return newmode;
+}
+EXPORT_SYMBOL(drm_display_mode_from_cea_vic);
+
static int
do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len)
{
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
index 822edeadbab3..59b9ca207b42 100644
--- a/drivers/gpu/drm/drm_gem_cma_helper.c
+++ b/drivers/gpu/drm/drm_gem_cma_helper.c
@@ -471,26 +471,9 @@ drm_gem_cma_prime_import_sg_table(struct drm_device *dev,
{
struct drm_gem_cma_object *cma_obj;
- if (sgt->nents != 1) {
- /* check if the entries in the sg_table are contiguous */
- dma_addr_t next_addr = sg_dma_address(sgt->sgl);
- struct scatterlist *s;
- unsigned int i;
-
- for_each_sg(sgt->sgl, s, sgt->nents, i) {
- /*
- * sg_dma_address(s) is only valid for entries
- * that have sg_dma_len(s) != 0
- */
- if (!sg_dma_len(s))
- continue;
-
- if (sg_dma_address(s) != next_addr)
- return ERR_PTR(-EINVAL);
-
- next_addr = sg_dma_address(s) + sg_dma_len(s);
- }
- }
+ /* check if the entries in the sg_table are contiguous */
+ if (drm_prime_get_contiguous_size(sgt) < attach->dmabuf->size)
+ return ERR_PTR(-EINVAL);
/* Create a CMA GEM buffer. */
cma_obj = __drm_gem_cma_create(dev, attach->dmabuf->size);
diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 4b7cfbac4daa..47d8211221f2 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -126,8 +126,8 @@ void drm_gem_shmem_free_object(struct drm_gem_object *obj)
drm_prime_gem_destroy(obj, shmem->sgt);
} else {
if (shmem->sgt) {
- dma_unmap_sg(obj->dev->dev, shmem->sgt->sgl,
- shmem->sgt->nents, DMA_BIDIRECTIONAL);
+ dma_unmap_sgtable(obj->dev->dev, shmem->sgt,
+ DMA_BIDIRECTIONAL, 0);
sg_free_table(shmem->sgt);
kfree(shmem->sgt);
}
@@ -424,8 +424,7 @@ void drm_gem_shmem_purge_locked(struct drm_gem_object *obj)
WARN_ON(!drm_gem_shmem_is_purgeable(shmem));
- dma_unmap_sg(obj->dev->dev, shmem->sgt->sgl,
- shmem->sgt->nents, DMA_BIDIRECTIONAL);
+ dma_unmap_sgtable(obj->dev->dev, shmem->sgt, DMA_BIDIRECTIONAL, 0);
sg_free_table(shmem->sgt);
kfree(shmem->sgt);
shmem->sgt = NULL;
@@ -697,12 +696,17 @@ struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_object *obj)
goto err_put_pages;
}
/* Map the pages for use by the h/w. */
- dma_map_sg(obj->dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
+ ret = dma_map_sgtable(obj->dev->dev, sgt, DMA_BIDIRECTIONAL, 0);
+ if (ret)
+ goto err_free_sgt;
shmem->sgt = sgt;
return sgt;
+err_free_sgt:
+ sg_free_table(sgt);
+ kfree(sgt);
err_put_pages:
drm_gem_shmem_put_pages(shmem);
return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index 1693aa7c14b5..b8c7f068a5a4 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -617,6 +617,7 @@ struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach,
{
struct drm_gem_object *obj = attach->dmabuf->priv;
struct sg_table *sgt;
+ int ret;
if (WARN_ON(dir == DMA_NONE))
return ERR_PTR(-EINVAL);
@@ -626,11 +627,12 @@ struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach,
else
sgt = obj->dev->driver->gem_prime_get_sg_table(obj);
- if (!dma_map_sg_attrs(attach->dev, sgt->sgl, sgt->nents, dir,
- DMA_ATTR_SKIP_CPU_SYNC)) {
+ ret = dma_map_sgtable(attach->dev, sgt, dir,
+ DMA_ATTR_SKIP_CPU_SYNC);
+ if (ret) {
sg_free_table(sgt);
kfree(sgt);
- sgt = ERR_PTR(-ENOMEM);
+ sgt = ERR_PTR(ret);
}
return sgt;
@@ -652,8 +654,7 @@ void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
if (!sgt)
return;
- dma_unmap_sg_attrs(attach->dev, sgt->sgl, sgt->nents, dir,
- DMA_ATTR_SKIP_CPU_SYNC);
+ dma_unmap_sgtable(attach->dev, sgt, dir, DMA_ATTR_SKIP_CPU_SYNC);
sg_free_table(sgt);
kfree(sgt);
}
@@ -826,6 +827,37 @@ out:
EXPORT_SYMBOL(drm_prime_pages_to_sg);
/**
+ * drm_prime_get_contiguous_size - returns the contiguous size of the buffer
+ * @sgt: sg_table describing the buffer to check
+ *
+ * This helper calculates the contiguous size in the DMA address space
+ * of the the buffer described by the provided sg_table.
+ *
+ * This is useful for implementing
+ * &drm_gem_object_funcs.gem_prime_import_sg_table.
+ */
+unsigned long drm_prime_get_contiguous_size(struct sg_table *sgt)
+{
+ dma_addr_t expected = sg_dma_address(sgt->sgl);
+ struct scatterlist *sg;
+ unsigned long size = 0;
+ int i;
+
+ for_each_sgtable_dma_sg(sgt, sg, i) {
+ unsigned int len = sg_dma_len(sg);
+
+ if (!len)
+ break;
+ if (sg_dma_address(sg) != expected)
+ break;
+ expected += len;
+ size += len;
+ }
+ return size;
+}
+EXPORT_SYMBOL(drm_prime_get_contiguous_size);
+
+/**
* drm_gem_prime_export - helper library implementation of the export callback
* @obj: GEM object to export
* @flags: flags like DRM_CLOEXEC and DRM_RDWR
@@ -959,45 +991,26 @@ EXPORT_SYMBOL(drm_gem_prime_import);
int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages,
dma_addr_t *addrs, int max_entries)
{
- unsigned count;
- struct scatterlist *sg;
- struct page *page;
- u32 page_len, page_index;
- dma_addr_t addr;
- u32 dma_len, dma_index;
-
- /*
- * Scatterlist elements contains both pages and DMA addresses, but
- * one shoud not assume 1:1 relation between them. The sg->length is
- * the size of the physical memory chunk described by the sg->page,
- * while sg_dma_len(sg) is the size of the DMA (IO virtual) chunk
- * described by the sg_dma_address(sg).
- */
- page_index = 0;
- dma_index = 0;
- for_each_sg(sgt->sgl, sg, sgt->nents, count) {
- page_len = sg->length;
- page = sg_page(sg);
- dma_len = sg_dma_len(sg);
- addr = sg_dma_address(sg);
-
- while (pages && page_len > 0) {
- if (WARN_ON(page_index >= max_entries))
+ struct sg_dma_page_iter dma_iter;
+ struct sg_page_iter page_iter;
+ struct page **p = pages;
+ dma_addr_t *a = addrs;
+
+ if (pages) {
+ for_each_sgtable_page(sgt, &page_iter, 0) {
+ if (WARN_ON(p - pages >= max_entries))
return -1;
- pages[page_index] = page;
- page++;
- page_len -= PAGE_SIZE;
- page_index++;
+ *p++ = sg_page_iter_page(&page_iter);
}
- while (addrs && dma_len > 0) {
- if (WARN_ON(dma_index >= max_entries))
+ }
+ if (addrs) {
+ for_each_sgtable_dma_page(sgt, &dma_iter, 0) {
+ if (WARN_ON(a - addrs >= max_entries))
return -1;
- addrs[dma_index] = addr;
- addr += PAGE_SIZE;
- dma_len -= PAGE_SIZE;
- dma_index++;
+ *a++ = sg_page_iter_dma_address(&dma_iter);
}
}
+
return 0;
}
EXPORT_SYMBOL(drm_prime_sg_to_page_addr_arrays);
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
index f06e19e7be04..eaf1949bc2e4 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
@@ -27,7 +27,7 @@ static void etnaviv_gem_scatter_map(struct etnaviv_gem_object *etnaviv_obj)
* because display controller, GPU, etc. are not coherent.
*/
if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK)
- dma_map_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
+ dma_map_sgtable(dev->dev, sgt, DMA_BIDIRECTIONAL, 0);
}
static void etnaviv_gem_scatterlist_unmap(struct etnaviv_gem_object *etnaviv_obj)
@@ -51,7 +51,7 @@ static void etnaviv_gem_scatterlist_unmap(struct etnaviv_gem_object *etnaviv_obj
* discard those writes.
*/
if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK)
- dma_unmap_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
+ dma_unmap_sgtable(dev->dev, sgt, DMA_BIDIRECTIONAL, 0);
}
/* called with etnaviv_obj->lock held */
@@ -404,9 +404,8 @@ int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op,
}
if (etnaviv_obj->flags & ETNA_BO_CACHED) {
- dma_sync_sg_for_cpu(dev->dev, etnaviv_obj->sgt->sgl,
- etnaviv_obj->sgt->nents,
- etnaviv_op_to_dma_dir(op));
+ dma_sync_sgtable_for_cpu(dev->dev, etnaviv_obj->sgt,
+ etnaviv_op_to_dma_dir(op));
etnaviv_obj->last_cpu_prep_op = op;
}
@@ -421,8 +420,7 @@ int etnaviv_gem_cpu_fini(struct drm_gem_object *obj)
if (etnaviv_obj->flags & ETNA_BO_CACHED) {
/* fini without a prep is almost certainly a userspace error */
WARN_ON(etnaviv_obj->last_cpu_prep_op == 0);
- dma_sync_sg_for_device(dev->dev, etnaviv_obj->sgt->sgl,
- etnaviv_obj->sgt->nents,
+ dma_sync_sgtable_for_device(dev->dev, etnaviv_obj->sgt,
etnaviv_op_to_dma_dir(etnaviv_obj->last_cpu_prep_op));
etnaviv_obj->last_cpu_prep_op = 0;
}
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
index 3607d348c298..15d9fa3879e5 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
@@ -73,13 +73,13 @@ static int etnaviv_iommu_map(struct etnaviv_iommu_context *context, u32 iova,
struct sg_table *sgt, unsigned len, int prot)
{ struct scatterlist *sg;
unsigned int da = iova;
- unsigned int i, j;
+ unsigned int i;
int ret;
if (!context || !sgt)
return -EINVAL;
- for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ for_each_sgtable_dma_sg(sgt, sg, i) {
u32 pa = sg_dma_address(sg) - sg->offset;
size_t bytes = sg_dma_len(sg) + sg->offset;
@@ -95,14 +95,7 @@ static int etnaviv_iommu_map(struct etnaviv_iommu_context *context, u32 iova,
return 0;
fail:
- da = iova;
-
- for_each_sg(sgt->sgl, sg, i, j) {
- size_t bytes = sg_dma_len(sg) + sg->offset;
-
- etnaviv_context_unmap(context, da, bytes);
- da += bytes;
- }
+ etnaviv_context_unmap(context, iova, da - iova);
return ret;
}
@@ -113,7 +106,7 @@ static void etnaviv_iommu_unmap(struct etnaviv_iommu_context *context, u32 iova,
unsigned int da = iova;
int i;
- for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ for_each_sgtable_dma_sg(sgt, sg, i) {
size_t bytes = sg_dma_len(sg) + sg->offset;
etnaviv_context_unmap(context, da, bytes);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dma.c b/drivers/gpu/drm/exynos/exynos_drm_dma.c
index 58b89ec11b0e..5887f7f52f96 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dma.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dma.c
@@ -31,23 +31,6 @@
#define EXYNOS_DEV_ADDR_START 0x20000000
#define EXYNOS_DEV_ADDR_SIZE 0x40000000
-static inline int configure_dma_max_seg_size(struct device *dev)
-{
- if (!dev->dma_parms)
- dev->dma_parms = kzalloc(sizeof(*dev->dma_parms), GFP_KERNEL);
- if (!dev->dma_parms)
- return -ENOMEM;
-
- dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
- return 0;
-}
-
-static inline void clear_dma_max_seg_size(struct device *dev)
-{
- kfree(dev->dma_parms);
- dev->dma_parms = NULL;
-}
-
/*
* drm_iommu_attach_device- attach device to iommu mapping
*
@@ -69,10 +52,7 @@ static int drm_iommu_attach_device(struct drm_device *drm_dev,
return -EINVAL;
}
- ret = configure_dma_max_seg_size(subdrv_dev);
- if (ret)
- return ret;
-
+ dma_set_max_seg_size(subdrv_dev, DMA_BIT_MASK(32));
if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) {
/*
* Keep the original DMA mapping of the sub-device and
@@ -89,9 +69,6 @@ static int drm_iommu_attach_device(struct drm_device *drm_dev,
ret = iommu_attach_device(priv->mapping, subdrv_dev);
}
- if (ret)
- clear_dma_max_seg_size(subdrv_dev);
-
return ret;
}
@@ -114,8 +91,6 @@ static void drm_iommu_detach_device(struct drm_device *drm_dev,
arm_iommu_attach_device(subdrv_dev, *dma_priv);
} else if (IS_ENABLED(CONFIG_IOMMU_DMA))
iommu_detach_device(priv->mapping, subdrv_dev);
-
- clear_dma_max_seg_size(subdrv_dev);
}
int exynos_drm_register_dma(struct drm_device *drm, struct device *dev,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 1a1a2853a842..5b9666fc7af1 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1760,11 +1760,8 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dsi->supplies[1].supply = "vddio";
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies),
dsi->supplies);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_info(dev, "failed to get regulators: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get regulators\n");
dsi->clks = devm_kcalloc(dev,
dsi->driver_data->num_clks, sizeof(*dsi->clks),
diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index 03be31427181..967a5cdc120e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -395,8 +395,8 @@ static void g2d_userptr_put_dma_addr(struct g2d_data *g2d,
return;
out:
- dma_unmap_sg(to_dma_dev(g2d->drm_dev), g2d_userptr->sgt->sgl,
- g2d_userptr->sgt->nents, DMA_BIDIRECTIONAL);
+ dma_unmap_sgtable(to_dma_dev(g2d->drm_dev), g2d_userptr->sgt,
+ DMA_BIDIRECTIONAL, 0);
pages = frame_vector_pages(g2d_userptr->vec);
if (!IS_ERR(pages)) {
@@ -511,10 +511,10 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct g2d_data *g2d,
g2d_userptr->sgt = sgt;
- if (!dma_map_sg(to_dma_dev(g2d->drm_dev), sgt->sgl, sgt->nents,
- DMA_BIDIRECTIONAL)) {
+ ret = dma_map_sgtable(to_dma_dev(g2d->drm_dev), sgt,
+ DMA_BIDIRECTIONAL, 0);
+ if (ret) {
DRM_DEV_ERROR(g2d->dev, "failed to map sgt with dma region.\n");
- ret = -ENOMEM;
goto err_sg_free_table;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index efa476858db5..1716a023bca0 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -431,27 +431,10 @@ exynos_drm_gem_prime_import_sg_table(struct drm_device *dev,
{
struct exynos_drm_gem *exynos_gem;
- if (sgt->nents < 1)
+ /* check if the entries in the sg_table are contiguous */
+ if (drm_prime_get_contiguous_size(sgt) < attach->dmabuf->size) {
+ DRM_ERROR("buffer chunks must be mapped contiguously");
return ERR_PTR(-EINVAL);
-
- /*
- * Check if the provided buffer has been mapped as contiguous
- * into DMA address space.
- */
- if (sgt->nents > 1) {
- dma_addr_t next_addr = sg_dma_address(sgt->sgl);
- struct scatterlist *s;
- unsigned int i;
-
- for_each_sg(sgt->sgl, s, sgt->nents, i) {
- if (!sg_dma_len(s))
- break;
- if (sg_dma_address(s) != next_addr) {
- DRM_ERROR("buffer chunks must be mapped contiguously");
- return ERR_PTR(-EINVAL);
- }
- next_addr = sg_dma_address(s) + sg_dma_len(s);
- }
}
exynos_gem = exynos_drm_gem_init(dev, attach->dmabuf->size);
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index c5ba32fca5f3..dc01c188c0e0 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -1797,11 +1797,8 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
hdata->regul_bulk[i].supply = supply[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- DRM_DEV_ERROR(dev, "failed to get regulators\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get regulators\n");
hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en");
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index bda4c0e408f8..e5574e506a5c 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -234,6 +234,7 @@ i915-y += \
display/intel_ddi.o \
display/intel_dp.o \
display/intel_dp_aux_backlight.o \
+ display/intel_dp_hdcp.o \
display/intel_dp_link_training.o \
display/intel_dp_mst.o \
display/intel_dsi.o \
diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c
index f4053dd6bde9..520715b7d5b5 100644
--- a/drivers/gpu/drm/i915/display/icl_dsi.c
+++ b/drivers/gpu/drm/i915/display/icl_dsi.c
@@ -1646,6 +1646,7 @@ static const struct drm_encoder_funcs gen11_dsi_encoder_funcs = {
};
static const struct drm_connector_funcs gen11_dsi_connector_funcs = {
+ .detect = intel_panel_detect,
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
.destroy = intel_connector_destroy,
diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c
index 630f49b7aa01..86be032bcf96 100644
--- a/drivers/gpu/drm/i915/display/intel_atomic.c
+++ b/drivers/gpu/drm/i915/display/intel_atomic.c
@@ -527,8 +527,6 @@ void intel_atomic_state_clear(struct drm_atomic_state *s)
intel_atomic_clear_global_state(state);
state->dpll_set = state->modeset = false;
- state->global_state_changed = false;
- state->active_pipes = 0;
}
struct intel_crtc_state *
@@ -542,40 +540,3 @@ intel_atomic_get_crtc_state(struct drm_atomic_state *state,
return to_intel_crtc_state(crtc_state);
}
-
-int _intel_atomic_lock_global_state(struct intel_atomic_state *state)
-{
- struct drm_i915_private *dev_priv = to_i915(state->base.dev);
- struct intel_crtc *crtc;
-
- state->global_state_changed = true;
-
- for_each_intel_crtc(&dev_priv->drm, crtc) {
- int ret;
-
- ret = drm_modeset_lock(&crtc->base.mutex,
- state->base.acquire_ctx);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-int _intel_atomic_serialize_global_state(struct intel_atomic_state *state)
-{
- struct drm_i915_private *dev_priv = to_i915(state->base.dev);
- struct intel_crtc *crtc;
-
- state->global_state_changed = true;
-
- for_each_intel_crtc(&dev_priv->drm, crtc) {
- struct intel_crtc_state *crtc_state;
-
- crtc_state = intel_atomic_get_crtc_state(&state->base, crtc);
- if (IS_ERR(crtc_state))
- return PTR_ERR(crtc_state);
- }
-
- return 0;
-}
diff --git a/drivers/gpu/drm/i915/display/intel_atomic.h b/drivers/gpu/drm/i915/display/intel_atomic.h
index 11146292b06f..285de07011dc 100644
--- a/drivers/gpu/drm/i915/display/intel_atomic.h
+++ b/drivers/gpu/drm/i915/display/intel_atomic.h
@@ -56,8 +56,4 @@ int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv,
struct intel_crtc *intel_crtc,
struct intel_crtc_state *crtc_state);
-int _intel_atomic_lock_global_state(struct intel_atomic_state *state);
-
-int _intel_atomic_serialize_global_state(struct intel_atomic_state *state);
-
#endif /* __INTEL_ATOMIC_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_audio.c b/drivers/gpu/drm/i915/display/intel_audio.c
index ad4aa66fd676..f7de55707746 100644
--- a/drivers/gpu/drm/i915/display/intel_audio.c
+++ b/drivers/gpu/drm/i915/display/intel_audio.c
@@ -958,13 +958,8 @@ static int glk_force_audio_cdclk_commit(struct intel_atomic_state *state,
if (IS_ERR(cdclk_state))
return PTR_ERR(cdclk_state);
- cdclk_state->force_min_cdclk_changed = true;
cdclk_state->force_min_cdclk = enable ? 2 * 96000 : 0;
- ret = intel_atomic_lock_global_state(&cdclk_state->base);
- if (ret)
- return ret;
-
return drm_atomic_commit(&state->base);
}
diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
index a0a41ec5c341..4716484af62d 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.c
+++ b/drivers/gpu/drm/i915/display/intel_bios.c
@@ -1656,6 +1656,8 @@ static enum port dvo_port_to_port(struct drm_i915_private *dev_priv,
[PORT_E] = { DVO_PORT_HDMIE, DVO_PORT_DPE, DVO_PORT_CRT },
[PORT_F] = { DVO_PORT_HDMIF, DVO_PORT_DPF, -1 },
[PORT_G] = { DVO_PORT_HDMIG, DVO_PORT_DPG, -1 },
+ [PORT_H] = { DVO_PORT_HDMIH, DVO_PORT_DPH, -1 },
+ [PORT_I] = { DVO_PORT_HDMII, DVO_PORT_DPI, -1 },
};
/*
* Bspec lists the ports as A, B, C, D - however internally in our
@@ -2133,7 +2135,7 @@ void intel_bios_init(struct drm_i915_private *dev_priv)
INIT_LIST_HEAD(&dev_priv->vbt.display_devices);
- if (!HAS_DISPLAY(dev_priv) || !INTEL_DISPLAY_ENABLED(dev_priv)) {
+ if (!HAS_DISPLAY(dev_priv)) {
drm_dbg_kms(&dev_priv->drm,
"Skipping VBT init due to disabled display.\n");
return;
@@ -2650,6 +2652,12 @@ enum aux_ch intel_bios_port_aux_ch(struct drm_i915_private *dev_priv,
case DP_AUX_G:
aux_ch = AUX_CH_G;
break;
+ case DP_AUX_H:
+ aux_ch = AUX_CH_H;
+ break;
+ case DP_AUX_I:
+ aux_ch = AUX_CH_I;
+ break;
default:
MISSING_CASE(info->alternate_aux_channel);
aux_ch = AUX_CH_A;
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c
index 577c4441f32d..cb93f6cf6d37 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
@@ -2426,7 +2426,6 @@ static struct intel_global_state *intel_cdclk_duplicate_state(struct intel_globa
if (!cdclk_state)
return NULL;
- cdclk_state->force_min_cdclk_changed = false;
cdclk_state->pipe = INVALID_PIPE;
return &cdclk_state->base;
@@ -2501,6 +2500,7 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state)
if (ret)
return ret;
} else if (old_cdclk_state->active_pipes != new_cdclk_state->active_pipes ||
+ old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk ||
intel_cdclk_changed(&old_cdclk_state->logical,
&new_cdclk_state->logical)) {
ret = intel_atomic_lock_global_state(&new_cdclk_state->base);
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.h b/drivers/gpu/drm/i915/display/intel_cdclk.h
index 6b31fde4be16..b34eb00fb327 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.h
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.h
@@ -49,7 +49,6 @@ struct intel_cdclk_state {
/* forced minimum cdclk for glk+ audio w/a */
int force_min_cdclk;
- bool force_min_cdclk_changed;
/* bitmask of active pipes */
u8 active_pipes;
diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c
index 5b4510ce5693..4934edd51cb0 100644
--- a/drivers/gpu/drm/i915/display/intel_crt.c
+++ b/drivers/gpu/drm/i915/display/intel_crt.c
@@ -833,6 +833,9 @@ intel_crt_detect(struct drm_connector *connector,
connector->base.id, connector->name,
force);
+ if (!INTEL_DISPLAY_ENABLED(dev_priv))
+ return connector_status_disconnected;
+
if (dev_priv->params.load_detect_test) {
wakeref = intel_display_power_get(dev_priv,
intel_encoder->power_domain);
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index 19ac6b2a664c..4d06178cd76c 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -572,13 +572,13 @@ static const struct cnl_ddi_buf_trans ehl_combo_phy_ddi_translations_dp[] = {
/* NT mV Trans mV db */
{ 0xA, 0x33, 0x3F, 0x00, 0x00 }, /* 350 350 0.0 */
{ 0xA, 0x47, 0x36, 0x00, 0x09 }, /* 350 500 3.1 */
- { 0xC, 0x64, 0x30, 0x00, 0x0F }, /* 350 700 6.0 */
- { 0x6, 0x7F, 0x2C, 0x00, 0x13 }, /* 350 900 8.2 */
+ { 0xC, 0x64, 0x34, 0x00, 0x0B }, /* 350 700 6.0 */
+ { 0x6, 0x7F, 0x30, 0x00, 0x0F }, /* 350 900 8.2 */
{ 0xA, 0x46, 0x3F, 0x00, 0x00 }, /* 500 500 0.0 */
- { 0xC, 0x64, 0x36, 0x00, 0x09 }, /* 500 700 2.9 */
- { 0x6, 0x7F, 0x30, 0x00, 0x0F }, /* 500 900 5.1 */
+ { 0xC, 0x64, 0x38, 0x00, 0x07 }, /* 500 700 2.9 */
+ { 0x6, 0x7F, 0x32, 0x00, 0x0D }, /* 500 900 5.1 */
{ 0xC, 0x61, 0x3F, 0x00, 0x00 }, /* 650 700 0.6 */
- { 0x6, 0x7F, 0x37, 0x00, 0x08 }, /* 600 900 3.5 */
+ { 0x6, 0x7F, 0x38, 0x00, 0x07 }, /* 600 900 3.5 */
{ 0x6, 0x7F, 0x3F, 0x00, 0x00 }, /* 900 900 0.0 */
};
@@ -1074,12 +1074,28 @@ static const struct cnl_ddi_buf_trans *
ehl_get_combo_buf_trans(struct intel_encoder *encoder, int type, int rate,
int *n_entries)
{
- if (type != INTEL_OUTPUT_HDMI && type != INTEL_OUTPUT_EDP) {
+ struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+
+ switch (type) {
+ case INTEL_OUTPUT_HDMI:
+ *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_hdmi);
+ return icl_combo_phy_ddi_translations_hdmi;
+ case INTEL_OUTPUT_EDP:
+ if (dev_priv->vbt.edp.low_vswing) {
+ if (rate > 540000) {
+ *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_edp_hbr3);
+ return icl_combo_phy_ddi_translations_edp_hbr3;
+ } else {
+ *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_edp_hbr2);
+ return icl_combo_phy_ddi_translations_edp_hbr2;
+ }
+ }
+ /* fall through */
+ default:
+ /* All combo DP and eDP ports that do not support low_vswing */
*n_entries = ARRAY_SIZE(ehl_combo_phy_ddi_translations_dp);
return ehl_combo_phy_ddi_translations_dp;
}
-
- return icl_get_combo_buf_trans(encoder, type, rate, n_entries);
}
static const struct cnl_ddi_buf_trans *
@@ -1088,30 +1104,44 @@ tgl_get_combo_buf_trans(struct intel_encoder *encoder, int type, int rate,
{
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- if (type == INTEL_OUTPUT_EDP && dev_priv->vbt.edp.hobl) {
- struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
-
- if (!intel_dp->hobl_failed && rate <= 540000) {
- /* Same table applies to TGL, RKL and DG1 */
- *n_entries = ARRAY_SIZE(tgl_combo_phy_ddi_translations_edp_hbr2_hobl);
- return tgl_combo_phy_ddi_translations_edp_hbr2_hobl;
+ switch (type) {
+ case INTEL_OUTPUT_HDMI:
+ *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_hdmi);
+ return icl_combo_phy_ddi_translations_hdmi;
+ case INTEL_OUTPUT_EDP:
+ if (dev_priv->vbt.edp.hobl) {
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+ if (!intel_dp->hobl_failed && rate <= 540000) {
+ /* Same table applies to TGL, RKL and DG1 */
+ *n_entries = ARRAY_SIZE(tgl_combo_phy_ddi_translations_edp_hbr2_hobl);
+ return tgl_combo_phy_ddi_translations_edp_hbr2_hobl;
+ }
}
- }
- if (type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_EDP) {
- return icl_get_combo_buf_trans(encoder, type, rate, n_entries);
- } else if (rate > 270000) {
- if (IS_TGL_U(dev_priv) || IS_TGL_Y(dev_priv)) {
- *n_entries = ARRAY_SIZE(tgl_uy_combo_phy_ddi_translations_dp_hbr2);
- return tgl_uy_combo_phy_ddi_translations_dp_hbr2;
+ if (rate > 540000) {
+ *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_edp_hbr3);
+ return icl_combo_phy_ddi_translations_edp_hbr3;
+ } else if (dev_priv->vbt.edp.low_vswing) {
+ *n_entries = ARRAY_SIZE(icl_combo_phy_ddi_translations_edp_hbr2);
+ return icl_combo_phy_ddi_translations_edp_hbr2;
+ }
+ /* fall through */
+ default:
+ /* All combo DP and eDP ports that do not support low_vswing */
+ if (rate > 270000) {
+ if (IS_TGL_U(dev_priv) || IS_TGL_Y(dev_priv)) {
+ *n_entries = ARRAY_SIZE(tgl_uy_combo_phy_ddi_translations_dp_hbr2);
+ return tgl_uy_combo_phy_ddi_translations_dp_hbr2;
+ }
+
+ *n_entries = ARRAY_SIZE(tgl_combo_phy_ddi_translations_dp_hbr2);
+ return tgl_combo_phy_ddi_translations_dp_hbr2;
}
- *n_entries = ARRAY_SIZE(tgl_combo_phy_ddi_translations_dp_hbr2);
- return tgl_combo_phy_ddi_translations_dp_hbr2;
+ *n_entries = ARRAY_SIZE(tgl_combo_phy_ddi_translations_dp_hbr);
+ return tgl_combo_phy_ddi_translations_dp_hbr;
}
-
- *n_entries = ARRAY_SIZE(tgl_combo_phy_ddi_translations_dp_hbr);
- return tgl_combo_phy_ddi_translations_dp_hbr;
}
static const struct tgl_dkl_phy_ddi_buf_trans *
@@ -1791,6 +1821,8 @@ void intel_ddi_disable_transcoder_func(const struct intel_crtc_state *crtc_state
ctl = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder));
+ drm_WARN_ON(crtc->base.dev, ctl & TRANS_DDI_HDCP_SIGNALLING);
+
ctl &= ~TRANS_DDI_FUNC_ENABLE;
if (IS_GEN_RANGE(dev_priv, 8, 10))
@@ -1818,12 +1850,12 @@ void intel_ddi_disable_transcoder_func(const struct intel_crtc_state *crtc_state
}
int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder,
+ enum transcoder cpu_transcoder,
bool enable)
{
struct drm_device *dev = intel_encoder->base.dev;
struct drm_i915_private *dev_priv = to_i915(dev);
intel_wakeref_t wakeref;
- enum pipe pipe = 0;
int ret = 0;
u32 tmp;
@@ -1832,19 +1864,12 @@ int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder,
if (drm_WARN_ON(dev, !wakeref))
return -ENXIO;
- if (drm_WARN_ON(dev,
- !intel_encoder->get_hw_state(intel_encoder, &pipe))) {
- ret = -EIO;
- goto out;
- }
-
- tmp = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(pipe));
+ tmp = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder));
if (enable)
tmp |= TRANS_DDI_HDCP_SIGNALLING;
else
tmp &= ~TRANS_DDI_HDCP_SIGNALLING;
- intel_de_write(dev_priv, TRANS_DDI_FUNC_CTL(pipe), tmp);
-out:
+ intel_de_write(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder), tmp);
intel_display_power_put(dev_priv, intel_encoder->power_domain, wakeref);
return ret;
}
@@ -3445,6 +3470,7 @@ static void hsw_ddi_pre_enable_dp(struct intel_atomic_state *state,
intel_ddi_init_dp_buf_reg(encoder);
if (!is_mst)
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+ intel_dp_configure_protocol_converter(intel_dp);
intel_dp_sink_set_decompression_state(intel_dp, crtc_state,
true);
intel_dp_sink_set_fec_ready(intel_dp, crtc_state);
@@ -3556,19 +3582,17 @@ static void intel_ddi_pre_enable(struct intel_atomic_state *state,
intel_ddi_pre_enable_hdmi(state, encoder, crtc_state,
conn_state);
} else {
- struct intel_lspcon *lspcon =
- enc_to_intel_lspcon(encoder);
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
intel_ddi_pre_enable_dp(state, encoder, crtc_state,
conn_state);
- if (lspcon->active) {
- struct intel_digital_port *dig_port =
- enc_to_dig_port(encoder);
+ /* FIXME precompute everything properly */
+ /* FIXME how do we turn infoframes off again? */
+ if (dig_port->lspcon.active && dig_port->dp.has_hdmi_sink)
dig_port->set_infoframes(encoder,
crtc_state->has_infoframe,
crtc_state, conn_state);
- }
}
}
@@ -4012,18 +4036,19 @@ static void intel_ddi_update_pipe_dp(struct intel_atomic_state *state,
intel_psr_update(intel_dp, crtc_state, conn_state);
intel_dp_set_infoframes(encoder, true, crtc_state, conn_state);
- intel_edp_drrs_enable(intel_dp, crtc_state);
+ intel_edp_drrs_update(intel_dp, crtc_state);
intel_panel_update_backlight(state, encoder, crtc_state, conn_state);
}
-static void intel_ddi_update_pipe(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *crtc_state,
- const struct drm_connector_state *conn_state)
+void intel_ddi_update_pipe(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state,
+ const struct drm_connector_state *conn_state)
{
- if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
+ if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) &&
+ !intel_encoder_is_mst(encoder))
intel_ddi_update_pipe_dp(state, encoder, crtc_state,
conn_state);
@@ -4949,6 +4974,57 @@ static bool hti_uses_phy(struct drm_i915_private *i915, enum phy phy)
i915->hti_state & HDPORT_PHY_USED_HDMI(phy));
}
+static enum hpd_pin tgl_hpd_pin(struct drm_i915_private *dev_priv,
+ enum port port)
+{
+ if (port >= PORT_D)
+ return HPD_PORT_TC1 + port - PORT_D;
+ else
+ return HPD_PORT_A + port - PORT_A;
+}
+
+static enum hpd_pin rkl_hpd_pin(struct drm_i915_private *dev_priv,
+ enum port port)
+{
+ if (HAS_PCH_TGP(dev_priv))
+ return tgl_hpd_pin(dev_priv, port);
+
+ if (port >= PORT_D)
+ return HPD_PORT_C + port - PORT_D;
+ else
+ return HPD_PORT_A + port - PORT_A;
+}
+
+static enum hpd_pin icl_hpd_pin(struct drm_i915_private *dev_priv,
+ enum port port)
+{
+ if (port >= PORT_C)
+ return HPD_PORT_TC1 + port - PORT_C;
+ else
+ return HPD_PORT_A + port - PORT_A;
+}
+
+static enum hpd_pin ehl_hpd_pin(struct drm_i915_private *dev_priv,
+ enum port port)
+{
+ if (port == PORT_D)
+ return HPD_PORT_A;
+
+ if (HAS_PCH_MCC(dev_priv))
+ return icl_hpd_pin(dev_priv, port);
+
+ return HPD_PORT_A + port - PORT_A;
+}
+
+static enum hpd_pin cnl_hpd_pin(struct drm_i915_private *dev_priv,
+ enum port port)
+{
+ if (port == PORT_F)
+ return HPD_PORT_E;
+
+ return HPD_PORT_A + port - PORT_A;
+}
+
void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
{
struct intel_digital_port *dig_port;
@@ -5001,6 +5077,9 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
drm_encoder_init(&dev_priv->drm, &encoder->base, &intel_ddi_funcs,
DRM_MODE_ENCODER_TMDS, "DDI %c", port_name(port));
+ mutex_init(&dig_port->hdcp_mutex);
+ dig_port->num_hdcp_streams = 0;
+
encoder->hotplug = intel_ddi_hotplug;
encoder->compute_output_type = intel_ddi_compute_output_type;
encoder->compute_config = intel_ddi_compute_config;
@@ -5022,6 +5101,19 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
encoder->cloneable = 0;
encoder->pipe_mask = ~0;
+ if (IS_ROCKETLAKE(dev_priv))
+ encoder->hpd_pin = rkl_hpd_pin(dev_priv, port);
+ else if (INTEL_GEN(dev_priv) >= 12)
+ encoder->hpd_pin = tgl_hpd_pin(dev_priv, port);
+ else if (IS_ELKHARTLAKE(dev_priv))
+ encoder->hpd_pin = ehl_hpd_pin(dev_priv, port);
+ else if (IS_GEN(dev_priv, 11))
+ encoder->hpd_pin = icl_hpd_pin(dev_priv, port);
+ else if (IS_GEN(dev_priv, 10))
+ encoder->hpd_pin = cnl_hpd_pin(dev_priv, port);
+ else
+ encoder->hpd_pin = intel_hpd_pin_default(dev_priv, port);
+
if (INTEL_GEN(dev_priv) >= 11)
dig_port->saved_port_bits =
intel_de_read(dev_priv, DDI_BUF_CTL(port))
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.h b/drivers/gpu/drm/i915/display/intel_ddi.h
index 077e9dbbe367..f5fb62fc9400 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.h
+++ b/drivers/gpu/drm/i915/display/intel_ddi.h
@@ -16,6 +16,7 @@ struct intel_crtc_state;
struct intel_dp;
struct intel_dpll_hw_state;
struct intel_encoder;
+enum transcoder;
void intel_ddi_fdi_post_disable(struct intel_atomic_state *state,
struct intel_encoder *intel_encoder,
@@ -43,6 +44,7 @@ void intel_ddi_compute_min_voltage_level(struct drm_i915_private *dev_priv,
u32 bxt_signal_levels(struct intel_dp *intel_dp);
u32 ddi_signal_levels(struct intel_dp *intel_dp);
int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder,
+ enum transcoder cpu_transcoder,
bool enable);
void icl_sanitize_encoder_pll_mapping(struct intel_encoder *encoder);
diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index ddb588f863f8..efc1d0f33fd7 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -67,6 +67,7 @@
#include "intel_bw.h"
#include "intel_cdclk.h"
#include "intel_color.h"
+#include "intel_csr.h"
#include "intel_display_types.h"
#include "intel_dp_link_training.h"
#include "intel_fbc.h"
@@ -7331,6 +7332,10 @@ enum intel_display_power_domain intel_port_to_power_domain(enum port port)
return POWER_DOMAIN_PORT_DDI_F_LANES;
case PORT_G:
return POWER_DOMAIN_PORT_DDI_G_LANES;
+ case PORT_H:
+ return POWER_DOMAIN_PORT_DDI_H_LANES;
+ case PORT_I:
+ return POWER_DOMAIN_PORT_DDI_I_LANES;
default:
MISSING_CASE(port);
return POWER_DOMAIN_PORT_OTHER;
@@ -7356,6 +7361,10 @@ intel_aux_power_domain(struct intel_digital_port *dig_port)
return POWER_DOMAIN_AUX_F_TBT;
case AUX_CH_G:
return POWER_DOMAIN_AUX_G_TBT;
+ case AUX_CH_H:
+ return POWER_DOMAIN_AUX_H_TBT;
+ case AUX_CH_I:
+ return POWER_DOMAIN_AUX_I_TBT;
default:
MISSING_CASE(dig_port->aux_ch);
return POWER_DOMAIN_AUX_C_TBT;
@@ -7387,6 +7396,10 @@ intel_legacy_aux_to_power_domain(enum aux_ch aux_ch)
return POWER_DOMAIN_AUX_F;
case AUX_CH_G:
return POWER_DOMAIN_AUX_G;
+ case AUX_CH_H:
+ return POWER_DOMAIN_AUX_H;
+ case AUX_CH_I:
+ return POWER_DOMAIN_AUX_I;
default:
MISSING_CASE(aux_ch);
return POWER_DOMAIN_AUX_A;
@@ -14636,16 +14649,8 @@ u8 intel_calc_active_pipes(struct intel_atomic_state *state,
static int intel_modeset_checks(struct intel_atomic_state *state)
{
struct drm_i915_private *dev_priv = to_i915(state->base.dev);
- int ret;
state->modeset = true;
- state->active_pipes = intel_calc_active_pipes(state, dev_priv->active_pipes);
-
- if (state->active_pipes != dev_priv->active_pipes) {
- ret = _intel_atomic_lock_global_state(state);
- if (ret)
- return ret;
- }
if (IS_HASWELL(dev_priv))
return hsw_mode_set_planes_workaround(state);
@@ -14789,7 +14794,8 @@ static int intel_atomic_check_cdclk(struct intel_atomic_state *state,
bool *need_cdclk_calc)
{
struct drm_i915_private *dev_priv = to_i915(state->base.dev);
- struct intel_cdclk_state *new_cdclk_state;
+ const struct intel_cdclk_state *old_cdclk_state;
+ const struct intel_cdclk_state *new_cdclk_state;
struct intel_plane_state *plane_state;
struct intel_bw_state *new_bw_state;
struct intel_plane *plane;
@@ -14808,9 +14814,11 @@ static int intel_atomic_check_cdclk(struct intel_atomic_state *state,
return ret;
}
+ old_cdclk_state = intel_atomic_get_old_cdclk_state(state);
new_cdclk_state = intel_atomic_get_new_cdclk_state(state);
- if (new_cdclk_state && new_cdclk_state->force_min_cdclk_changed)
+ if (new_cdclk_state &&
+ old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk)
*need_cdclk_calc = true;
ret = dev_priv->display.bw_calc_min_cdclk(state);
@@ -15757,14 +15765,6 @@ static void intel_atomic_track_fbs(struct intel_atomic_state *state)
plane->frontbuffer_bit);
}
-static void assert_global_state_locked(struct drm_i915_private *dev_priv)
-{
- struct intel_crtc *crtc;
-
- for_each_intel_crtc(&dev_priv->drm, crtc)
- drm_modeset_lock_assert_held(&crtc->base.mutex);
-}
-
static int intel_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *_state,
bool nonblock)
@@ -15840,12 +15840,6 @@ static int intel_atomic_commit(struct drm_device *dev,
intel_shared_dpll_swap_state(state);
intel_atomic_track_fbs(state);
- if (state->global_state_changed) {
- assert_global_state_locked(dev_priv);
-
- dev_priv->active_pipes = state->active_pipes;
- }
-
drm_atomic_state_get(&state->base);
INIT_WORK(&state->base.commit_work, intel_atomic_commit_work);
@@ -16892,7 +16886,7 @@ static void intel_setup_outputs(struct drm_i915_private *dev_priv)
intel_pps_init(dev_priv);
- if (!HAS_DISPLAY(dev_priv) || !INTEL_DISPLAY_ENABLED(dev_priv))
+ if (!HAS_DISPLAY(dev_priv))
return;
if (IS_ROCKETLAKE(dev_priv)) {
@@ -17878,6 +17872,27 @@ int intel_modeset_init_noirq(struct drm_i915_private *i915)
{
int ret;
+ if (i915_inject_probe_failure(i915))
+ return -ENODEV;
+
+ if (HAS_DISPLAY(i915)) {
+ ret = drm_vblank_init(&i915->drm,
+ INTEL_NUM_PIPES(i915));
+ if (ret)
+ return ret;
+ }
+
+ intel_bios_init(i915);
+
+ ret = intel_vga_register(i915);
+ if (ret)
+ goto cleanup_bios;
+
+ /* FIXME: completely on the wrong abstraction layer */
+ intel_power_domains_init_hw(i915, false);
+
+ intel_csr_ucode_init(i915);
+
i915->modeset_wq = alloc_ordered_workqueue("i915_modeset", 0);
i915->flip_wq = alloc_workqueue("i915_flip", WQ_HIGHPRI |
WQ_UNBOUND, WQ_UNBOUND_MAX_ACTIVE);
@@ -17886,15 +17901,15 @@ int intel_modeset_init_noirq(struct drm_i915_private *i915)
ret = intel_cdclk_init(i915);
if (ret)
- return ret;
+ goto cleanup_vga_client_pw_domain_csr;
ret = intel_dbuf_init(i915);
if (ret)
- return ret;
+ goto cleanup_vga_client_pw_domain_csr;
ret = intel_bw_init(i915);
if (ret)
- return ret;
+ goto cleanup_vga_client_pw_domain_csr;
init_llist_head(&i915->atomic_helper.free_list);
INIT_WORK(&i915->atomic_helper.free_work,
@@ -17905,10 +17920,19 @@ int intel_modeset_init_noirq(struct drm_i915_private *i915)
intel_fbc_init(i915);
return 0;
+
+cleanup_vga_client_pw_domain_csr:
+ intel_csr_ucode_fini(i915);
+ intel_power_domains_driver_remove(i915);
+ intel_vga_unregister(i915);
+cleanup_bios:
+ intel_bios_driver_remove(i915);
+
+ return ret;
}
-/* part #2: call after irq install */
-int intel_modeset_init(struct drm_i915_private *i915)
+/* part #2: call after irq install, but before gem init */
+int intel_modeset_init_nogem(struct drm_i915_private *i915)
{
struct drm_device *dev = &i915->drm;
enum pipe pipe;
@@ -17925,7 +17949,7 @@ int intel_modeset_init(struct drm_i915_private *i915)
INTEL_NUM_PIPES(i915),
INTEL_NUM_PIPES(i915) > 1 ? "s" : "");
- if (HAS_DISPLAY(i915) && INTEL_DISPLAY_ENABLED(i915)) {
+ if (HAS_DISPLAY(i915)) {
for_each_pipe(i915, pipe) {
ret = intel_crtc_init(i915, pipe);
if (ret) {
@@ -18007,6 +18031,30 @@ int intel_modeset_init(struct drm_i915_private *i915)
return 0;
}
+/* part #3: call after gem init */
+int intel_modeset_init(struct drm_i915_private *i915)
+{
+ int ret;
+
+ intel_overlay_setup(i915);
+
+ if (!HAS_DISPLAY(i915))
+ return 0;
+
+ ret = intel_fbdev_init(&i915->drm);
+ if (ret)
+ return ret;
+
+ /* Only enable hotplug handling once the fbdev is fully set up. */
+ intel_hpd_init(i915);
+
+ intel_init_ipc(i915);
+
+ intel_psr_set_force_mode_changed(i915->psr.dp);
+
+ return 0;
+}
+
void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe)
{
struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
@@ -18891,6 +18939,18 @@ void intel_modeset_driver_remove_noirq(struct drm_i915_private *i915)
intel_fbc_cleanup_cfb(i915);
}
+/* part #3: call after gem init */
+void intel_modeset_driver_remove_nogem(struct drm_i915_private *i915)
+{
+ intel_csr_ucode_fini(i915);
+
+ intel_power_domains_driver_remove(i915);
+
+ intel_vga_unregister(i915);
+
+ intel_bios_driver_remove(i915);
+}
+
#if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
struct intel_display_error_state {
@@ -18951,7 +19011,7 @@ intel_display_capture_error_state(struct drm_i915_private *dev_priv)
BUILD_BUG_ON(ARRAY_SIZE(transcoders) != ARRAY_SIZE(error->transcoder));
- if (!HAS_DISPLAY(dev_priv) || !INTEL_DISPLAY_ENABLED(dev_priv))
+ if (!HAS_DISPLAY(dev_priv))
return NULL;
error = kzalloc(sizeof(*error), GFP_ATOMIC);
diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
index e890c8fb779b..d10b7c8cde3f 100644
--- a/drivers/gpu/drm/i915/display/intel_display.h
+++ b/drivers/gpu/drm/i915/display/intel_display.h
@@ -272,8 +272,6 @@ enum dpio_phy {
DPIO_PHY2,
};
-#define I915_NUM_PHYS_VLV 2
-
enum aux_ch {
AUX_CH_A,
AUX_CH_B,
@@ -282,6 +280,8 @@ enum aux_ch {
AUX_CH_E, /* ICL+ */
AUX_CH_F,
AUX_CH_G,
+ AUX_CH_H,
+ AUX_CH_I,
};
#define aux_ch_name(a) ((a) + 'A')
@@ -629,9 +629,11 @@ intel_format_info_is_yuv_semiplanar(const struct drm_format_info *info,
/* modesetting */
void intel_modeset_init_hw(struct drm_i915_private *i915);
int intel_modeset_init_noirq(struct drm_i915_private *i915);
+int intel_modeset_init_nogem(struct drm_i915_private *i915);
int intel_modeset_init(struct drm_i915_private *i915);
void intel_modeset_driver_remove(struct drm_i915_private *i915);
void intel_modeset_driver_remove_noirq(struct drm_i915_private *i915);
+void intel_modeset_driver_remove_nogem(struct drm_i915_private *i915);
void intel_display_resume(struct drm_device *dev);
void intel_init_pch_refclk(struct drm_i915_private *dev_priv);
diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
index f549381048b3..0bf31f9a8af5 100644
--- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c
+++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
@@ -601,6 +601,11 @@ static void intel_hdcp_info(struct seq_file *m,
{
bool hdcp_cap, hdcp2_cap;
+ if (!intel_connector->hdcp.shim) {
+ seq_puts(m, "No Connector Support");
+ goto out;
+ }
+
hdcp_cap = intel_hdcp_capable(intel_connector);
hdcp2_cap = intel_hdcp2_capable(intel_connector);
@@ -612,6 +617,7 @@ static void intel_hdcp_info(struct seq_file *m,
if (!hdcp_cap && !hdcp2_cap)
seq_puts(m, "None");
+out:
seq_puts(m, "\n");
}
@@ -620,6 +626,7 @@ static void intel_dp_info(struct seq_file *m,
{
struct intel_encoder *intel_encoder = intel_attached_encoder(intel_connector);
struct intel_dp *intel_dp = enc_to_intel_dp(intel_encoder);
+ const struct drm_property_blob *edid = intel_connector->base.edid_blob_ptr;
seq_printf(m, "\tDPCD rev: %x\n", intel_dp->dpcd[DP_DPCD_REV]);
seq_printf(m, "\taudio support: %s\n", yesno(intel_dp->has_audio));
@@ -627,11 +634,7 @@ static void intel_dp_info(struct seq_file *m,
intel_panel_info(m, &intel_connector->panel);
drm_dp_downstream_debug(m, intel_dp->dpcd, intel_dp->downstream_ports,
- &intel_dp->aux);
- if (intel_connector->hdcp.shim) {
- seq_puts(m, "\tHDCP version: ");
- intel_hdcp_info(m, intel_connector);
- }
+ edid ? edid->data : NULL, &intel_dp->aux);
}
static void intel_dp_mst_info(struct seq_file *m,
@@ -649,10 +652,6 @@ static void intel_hdmi_info(struct seq_file *m,
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(intel_encoder);
seq_printf(m, "\taudio support: %s\n", yesno(intel_hdmi->has_audio));
- if (intel_connector->hdcp.shim) {
- seq_puts(m, "\tHDCP version: ");
- intel_hdcp_info(m, intel_connector);
- }
}
static void intel_lvds_info(struct seq_file *m,
@@ -708,6 +707,9 @@ static void intel_connector_info(struct seq_file *m,
break;
}
+ seq_puts(m, "\tHDCP version: ");
+ intel_hdcp_info(m, intel_connector);
+
seq_printf(m, "\tmodes:\n");
list_for_each_entry(mode, &connector->modes, head)
intel_seq_print_mode(m, 2, mode);
@@ -1069,10 +1071,18 @@ static void drrs_status_per_crtc(struct seq_file *m,
drm_connector_list_iter_begin(dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter) {
+ bool supported = false;
+
if (connector->state->crtc != &intel_crtc->base)
continue;
seq_printf(m, "%s:\n", connector->name);
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP &&
+ drrs->type == SEAMLESS_DRRS_SUPPORT)
+ supported = true;
+
+ seq_printf(m, "\tDRRS Supported: %s\n", yesno(supported));
}
drm_connector_list_iter_end(&conn_iter);
@@ -1083,7 +1093,7 @@ static void drrs_status_per_crtc(struct seq_file *m,
mutex_lock(&drrs->mutex);
/* DRRS Supported */
- seq_puts(m, "\tDRRS Supported: Yes\n");
+ seq_puts(m, "\tDRRS Enabled: Yes\n");
/* disable_drrs() will make drrs->dp NULL */
if (!drrs->dp) {
@@ -1118,7 +1128,7 @@ static void drrs_status_per_crtc(struct seq_file *m,
mutex_unlock(&drrs->mutex);
} else {
/* DRRS not supported. Print the VBT parameter*/
- seq_puts(m, "\tDRRS Supported : No");
+ seq_puts(m, "\tDRRS Enabled : No");
}
seq_puts(m, "\n");
}
@@ -2029,10 +2039,6 @@ static int i915_hdcp_sink_capability_show(struct seq_file *m, void *data)
if (connector->status != connector_status_connected)
return -ENODEV;
- /* HDCP is supported by connector */
- if (!intel_connector->hdcp.shim)
- return -EINVAL;
-
seq_printf(m, "%s:%d HDCP version: ", connector->name,
connector->base.id);
intel_hdcp_info(m, intel_connector);
diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c
index 7946c6af4b1e..7277e58b01f1 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power.c
@@ -5263,7 +5263,7 @@ static void tgl_bw_buddy_init(struct drm_i915_private *dev_priv)
unsigned long abox_mask = INTEL_INFO(dev_priv)->abox_mask;
int config, i;
- if (IS_TGL_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_B0))
+ if (IS_TGL_DISP_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_B0))
/* Wa_1409767108: tgl */
table = wa_1409767108_buddy_page_masks;
else
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index 9349b15afff6..3d4bf9b6a0a2 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -28,6 +28,7 @@
#include <linux/async.h>
#include <linux/i2c.h>
+#include <linux/pwm.h>
#include <linux/sched/clock.h>
#include <drm/drm_atomic.h>
@@ -223,6 +224,7 @@ struct intel_panel {
bool util_pin_active_low; /* bxt+ */
u8 controller; /* bxt+ only */
struct pwm_device *pwm;
+ struct pwm_state pwm_state;
/* DPCD backlight */
u8 pwmgen_bit_count;
@@ -314,10 +316,12 @@ struct intel_hdcp_shim {
/* Enables HDCP signalling on the port */
int (*toggle_signalling)(struct intel_digital_port *dig_port,
+ enum transcoder cpu_transcoder,
bool enable);
/* Ensures the link is still protected */
- bool (*check_link)(struct intel_digital_port *dig_port);
+ bool (*check_link)(struct intel_digital_port *dig_port,
+ struct intel_connector *connector);
/* Detects panel's hdcp capability. This is optional for HDMI. */
int (*hdcp_capable)(struct intel_digital_port *dig_port,
@@ -479,8 +483,6 @@ struct intel_atomic_state {
bool dpll_set, modeset;
- u8 active_pipes;
-
struct intel_shared_dpll_state shared_dpll[I915_NUM_PLLS];
/*
@@ -491,11 +493,6 @@ struct intel_atomic_state {
bool rps_interactive;
- /*
- * active_pipes
- */
- bool global_state_changed;
-
struct i915_sw_fence commit_ready;
struct llist_node freed;
@@ -1275,6 +1272,7 @@ struct intel_dp {
u8 sink_count;
bool link_mst;
bool link_trained;
+ bool has_hdmi_sink;
bool has_audio;
bool reset_link_params;
u8 dpcd[DP_RECEIVER_CAP_SIZE];
@@ -1376,6 +1374,14 @@ struct intel_dp {
/* Displayport compliance testing */
struct intel_dp_compliance compliance;
+ /* Downstream facing port caps */
+ struct {
+ int min_tmds_clock, max_tmds_clock;
+ int max_dotclock;
+ u8 max_bpc;
+ bool ycbcr_444_to_420;
+ } dfp;
+
/* Display stream compression testing */
bool force_dsc_en;
@@ -1415,6 +1421,11 @@ struct intel_digital_port {
enum phy_fia tc_phy_fia;
u8 tc_phy_fia_idx;
+ /* protects num_hdcp_streams reference count */
+ struct mutex hdcp_mutex;
+ /* the number of pipes using HDCP signalling out of this port */
+ unsigned int num_hdcp_streams;
+
void (*write_infoframe)(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
unsigned int type,
@@ -1525,6 +1536,18 @@ static inline bool intel_encoder_is_dig_port(struct intel_encoder *encoder)
}
}
+static inline bool intel_encoder_is_mst(struct intel_encoder *encoder)
+{
+ return encoder->type == INTEL_OUTPUT_DP_MST;
+}
+
+static inline struct intel_dp_mst_encoder *
+enc_to_mst(struct intel_encoder *encoder)
+{
+ return container_of(&encoder->base, struct intel_dp_mst_encoder,
+ base.base);
+}
+
static inline struct intel_digital_port *
enc_to_dig_port(struct intel_encoder *encoder)
{
@@ -1533,6 +1556,8 @@ enc_to_dig_port(struct intel_encoder *encoder)
if (intel_encoder_is_dig_port(intel_encoder))
return container_of(&encoder->base, struct intel_digital_port,
base.base);
+ else if (intel_encoder_is_mst(intel_encoder))
+ return enc_to_mst(encoder)->primary;
else
return NULL;
}
@@ -1543,13 +1568,6 @@ intel_attached_dig_port(struct intel_connector *connector)
return enc_to_dig_port(intel_attached_encoder(connector));
}
-static inline struct intel_dp_mst_encoder *
-enc_to_mst(struct intel_encoder *encoder)
-{
- return container_of(&encoder->base, struct intel_dp_mst_encoder,
- base.base);
-}
-
static inline struct intel_dp *enc_to_intel_dp(struct intel_encoder *encoder)
{
return &enc_to_dig_port(encoder)->dp;
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 284b15f84592..bf1e9cf1c0f3 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -38,7 +38,6 @@
#include <drm/drm_crtc.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_edid.h>
-#include <drm/drm_hdcp.h>
#include <drm/drm_probe_helper.h>
#include "i915_debugfs.h"
@@ -248,29 +247,6 @@ intel_dp_max_data_rate(int max_link_clock, int max_lanes)
return max_link_clock * max_lanes;
}
-static int
-intel_dp_downstream_max_dotclock(struct intel_dp *intel_dp)
-{
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct intel_encoder *encoder = &dig_port->base;
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- int max_dotclk = dev_priv->max_dotclk_freq;
- int ds_max_dotclk;
-
- int type = intel_dp->downstream_ports[0] & DP_DS_PORT_TYPE_MASK;
-
- if (type != DP_DS_PORT_TYPE_VGA)
- return max_dotclk;
-
- ds_max_dotclk = drm_dp_downstream_max_clock(intel_dp->dpcd,
- intel_dp->downstream_ports);
-
- if (ds_max_dotclk != 0)
- max_dotclk = min(max_dotclk, ds_max_dotclk);
-
- return max_dotclk;
-}
-
static int cnl_max_source_rate(struct intel_dp *intel_dp)
{
struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
@@ -636,6 +612,34 @@ static bool intel_dp_hdisplay_bad(struct drm_i915_private *dev_priv,
}
static enum drm_mode_status
+intel_dp_mode_valid_downstream(struct intel_connector *connector,
+ const struct drm_display_mode *mode,
+ int target_clock)
+{
+ struct intel_dp *intel_dp = intel_attached_dp(connector);
+ const struct drm_display_info *info = &connector->base.display_info;
+ int tmds_clock;
+
+ if (intel_dp->dfp.max_dotclock &&
+ target_clock > intel_dp->dfp.max_dotclock)
+ return MODE_CLOCK_HIGH;
+
+ /* Assume 8bpc for the DP++/HDMI/DVI TMDS clock check */
+ tmds_clock = target_clock;
+ if (drm_mode_is_420_only(info, mode))
+ tmds_clock /= 2;
+
+ if (intel_dp->dfp.min_tmds_clock &&
+ tmds_clock < intel_dp->dfp.min_tmds_clock)
+ return MODE_CLOCK_LOW;
+ if (intel_dp->dfp.max_tmds_clock &&
+ tmds_clock > intel_dp->dfp.max_tmds_clock)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static enum drm_mode_status
intel_dp_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
@@ -645,15 +649,14 @@ intel_dp_mode_valid(struct drm_connector *connector,
struct drm_i915_private *dev_priv = to_i915(connector->dev);
int target_clock = mode->clock;
int max_rate, mode_rate, max_lanes, max_link_clock;
- int max_dotclk;
+ int max_dotclk = dev_priv->max_dotclk_freq;
u16 dsc_max_output_bpp = 0;
u8 dsc_slice_count = 0;
+ enum drm_mode_status status;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
- max_dotclk = intel_dp_downstream_max_dotclock(intel_dp);
-
if (intel_dp_is_edp(intel_dp) && fixed_mode) {
if (mode->hdisplay > fixed_mode->hdisplay)
return MODE_PANEL;
@@ -709,6 +712,11 @@ intel_dp_mode_valid(struct drm_connector *connector,
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
return MODE_H_ILLEGAL;
+ status = intel_dp_mode_valid_downstream(intel_connector,
+ mode, target_clock);
+ if (status != MODE_OK)
+ return status;
+
return intel_mode_valid_max_plane_size(dev_priv, mode);
}
@@ -1563,6 +1571,20 @@ intel_dp_aux_header(u8 txbuf[HEADER_SIZE],
txbuf[3] = msg->size - 1;
}
+static u32 intel_dp_aux_xfer_flags(const struct drm_dp_aux_msg *msg)
+{
+ /*
+ * If we're trying to send the HDCP Aksv, we need to set a the Aksv
+ * select bit to inform the hardware to send the Aksv after our header
+ * since we can't access that data from software.
+ */
+ if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_WRITE &&
+ msg->address == DP_AUX_HDCP_AKSV)
+ return DP_AUX_CH_CTL_AUX_AKSV_SELECT;
+
+ return 0;
+}
+
static ssize_t
intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
{
@@ -1570,6 +1592,7 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
u8 txbuf[20], rxbuf[20];
size_t txsize, rxsize;
+ u32 flags = intel_dp_aux_xfer_flags(msg);
int ret;
intel_dp_aux_header(txbuf, msg);
@@ -1590,7 +1613,7 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size);
ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
- rxbuf, rxsize, 0);
+ rxbuf, rxsize, flags);
if (ret > 0) {
msg->reply = rxbuf[0] >> 4;
@@ -1613,7 +1636,7 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
return -E2BIG;
ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
- rxbuf, rxsize, 0);
+ rxbuf, rxsize, flags);
if (ret > 0) {
msg->reply = rxbuf[0] >> 4;
/*
@@ -1954,19 +1977,72 @@ static bool intel_dp_supports_dsc(struct intel_dp *intel_dp,
drm_dp_sink_supports_dsc(intel_dp->dsc_dpcd);
}
-static int intel_dp_compute_bpp(struct intel_dp *intel_dp,
- struct intel_crtc_state *pipe_config)
+static bool intel_dp_hdmi_ycbcr420(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state)
+{
+ return crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420 ||
+ (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444 &&
+ intel_dp->dfp.ycbcr_444_to_420);
+}
+
+static int intel_dp_hdmi_tmds_clock(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state, int bpc)
+{
+ int clock = crtc_state->hw.adjusted_mode.crtc_clock * bpc / 8;
+
+ if (intel_dp_hdmi_ycbcr420(intel_dp, crtc_state))
+ clock /= 2;
+
+ return clock;
+}
+
+static bool intel_dp_hdmi_tmds_clock_valid(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state, int bpc)
+{
+ int tmds_clock = intel_dp_hdmi_tmds_clock(intel_dp, crtc_state, bpc);
+
+ if (intel_dp->dfp.min_tmds_clock &&
+ tmds_clock < intel_dp->dfp.min_tmds_clock)
+ return false;
+
+ if (intel_dp->dfp.max_tmds_clock &&
+ tmds_clock > intel_dp->dfp.max_tmds_clock)
+ return false;
+
+ return true;
+}
+
+static bool intel_dp_hdmi_deep_color_possible(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state,
+ int bpc)
+{
+
+ return intel_hdmi_deep_color_possible(crtc_state, bpc,
+ intel_dp->has_hdmi_sink,
+ intel_dp_hdmi_ycbcr420(intel_dp, crtc_state)) &&
+ intel_dp_hdmi_tmds_clock_valid(intel_dp, crtc_state, bpc);
+}
+
+static int intel_dp_max_bpp(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state)
{
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
struct intel_connector *intel_connector = intel_dp->attached_connector;
int bpp, bpc;
- bpp = pipe_config->pipe_bpp;
- bpc = drm_dp_downstream_max_bpc(intel_dp->dpcd, intel_dp->downstream_ports);
+ bpc = crtc_state->pipe_bpp / 3;
- if (bpc > 0)
- bpp = min(bpp, 3*bpc);
+ if (intel_dp->dfp.max_bpc)
+ bpc = min_t(int, bpc, intel_dp->dfp.max_bpc);
+ if (intel_dp->dfp.min_tmds_clock) {
+ for (; bpc >= 10; bpc -= 2) {
+ if (intel_dp_hdmi_deep_color_possible(intel_dp, crtc_state, bpc))
+ break;
+ }
+ }
+
+ bpp = bpc * 3;
if (intel_dp_is_edp(intel_dp)) {
/* Get bpp from vbt only for panels that dont have bpp in edid */
if (intel_connector->base.display_info.bpc == 0 &&
@@ -2288,7 +2364,7 @@ intel_dp_compute_link_config(struct intel_encoder *encoder,
limits.max_lane_count = intel_dp_max_lane_count(intel_dp);
limits.min_bpp = intel_dp_min_bpp(pipe_config);
- limits.max_bpp = intel_dp_compute_bpp(intel_dp, pipe_config);
+ limits.max_bpp = intel_dp_max_bpp(intel_dp, pipe_config);
if (intel_dp_is_edp(intel_dp)) {
/*
@@ -2363,10 +2439,16 @@ intel_dp_ycbcr420_config(struct intel_dp *intel_dp,
const struct drm_display_mode *adjusted_mode =
&crtc_state->hw.adjusted_mode;
- if (!drm_mode_is_420_only(info, adjusted_mode) ||
- !intel_dp_get_colorimetry_status(intel_dp) ||
- !connector->ycbcr_420_allowed)
+ if (!connector->ycbcr_420_allowed)
+ return 0;
+
+ if (!drm_mode_is_420_only(info, adjusted_mode))
+ return 0;
+
+ if (intel_dp->dfp.ycbcr_444_to_420) {
+ crtc_state->output_format = INTEL_OUTPUT_FORMAT_YCBCR444;
return 0;
+ }
crtc_state->output_format = INTEL_OUTPUT_FORMAT_YCBCR420;
@@ -2575,6 +2657,34 @@ intel_dp_compute_hdr_metadata_infoframe_sdp(struct intel_dp *intel_dp,
intel_hdmi_infoframe_enable(HDMI_PACKET_TYPE_GAMUT_METADATA);
}
+static void
+intel_dp_drrs_compute_config(struct intel_dp *intel_dp,
+ struct intel_crtc_state *pipe_config,
+ int output_bpp, bool constant_n)
+{
+ struct intel_connector *intel_connector = intel_dp->attached_connector;
+ struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+
+ /*
+ * DRRS and PSR can't be enable together, so giving preference to PSR
+ * as it allows more power-savings by complete shutting down display,
+ * so to guarantee this, intel_dp_drrs_compute_config() must be called
+ * after intel_psr_compute_config().
+ */
+ if (pipe_config->has_psr)
+ return;
+
+ if (!intel_connector->panel.downclock_mode ||
+ dev_priv->drrs.type != SEAMLESS_DRRS_SUPPORT)
+ return;
+
+ pipe_config->has_drrs = true;
+ intel_link_compute_m_n(output_bpp, pipe_config->lane_count,
+ intel_connector->panel.downclock_mode->clock,
+ pipe_config->port_clock, &pipe_config->dp_m2_n2,
+ constant_n, pipe_config->fec_enable);
+}
+
int
intel_dp_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
@@ -2605,7 +2715,6 @@ intel_dp_compute_config(struct intel_encoder *encoder,
if (ret)
return ret;
- pipe_config->has_drrs = false;
if (!intel_dp_port_has_audio(dev_priv, port))
pipe_config->has_audio = false;
else if (intel_conn_state->force_audio == HDMI_AUDIO_AUTO)
@@ -2657,21 +2766,12 @@ intel_dp_compute_config(struct intel_encoder *encoder,
&pipe_config->dp_m_n,
constant_n, pipe_config->fec_enable);
- if (intel_connector->panel.downclock_mode != NULL &&
- dev_priv->drrs.type == SEAMLESS_DRRS_SUPPORT) {
- pipe_config->has_drrs = true;
- intel_link_compute_m_n(output_bpp,
- pipe_config->lane_count,
- intel_connector->panel.downclock_mode->clock,
- pipe_config->port_clock,
- &pipe_config->dp_m2_n2,
- constant_n, pipe_config->fec_enable);
- }
-
if (!HAS_DDI(dev_priv))
intel_dp_set_clock(encoder, pipe_config);
intel_psr_compute_config(intel_dp, pipe_config);
+ intel_dp_drrs_compute_config(intel_dp, pipe_config, output_bpp,
+ constant_n);
intel_dp_compute_vsc_sdp(intel_dp, pipe_config, conn_state);
intel_dp_compute_hdr_metadata_infoframe_sdp(intel_dp, pipe_config, conn_state);
@@ -3752,6 +3852,43 @@ static void intel_dp_enable_port(struct intel_dp *intel_dp,
intel_de_posting_read(dev_priv, intel_dp->output_reg);
}
+void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp)
+{
+ struct drm_i915_private *i915 = dp_to_i915(intel_dp);
+ u8 tmp;
+
+ if (intel_dp->dpcd[DP_DPCD_REV] < 0x13)
+ return;
+
+ if (!drm_dp_is_branch(intel_dp->dpcd))
+ return;
+
+ tmp = intel_dp->has_hdmi_sink ?
+ DP_HDMI_DVI_OUTPUT_CONFIG : 0;
+
+ if (drm_dp_dpcd_writeb(&intel_dp->aux,
+ DP_PROTOCOL_CONVERTER_CONTROL_0, tmp) != 1)
+ drm_dbg_kms(&i915->drm, "Failed to set protocol converter HDMI mode to %s\n",
+ enableddisabled(intel_dp->has_hdmi_sink));
+
+ tmp = intel_dp->dfp.ycbcr_444_to_420 ?
+ DP_CONVERSION_TO_YCBCR420_ENABLE : 0;
+
+ if (drm_dp_dpcd_writeb(&intel_dp->aux,
+ DP_PROTOCOL_CONVERTER_CONTROL_1, tmp) != 1)
+ drm_dbg_kms(&i915->drm,
+ "Failed to set protocol converter YCbCr 4:2:0 conversion mode to %s\n",
+ enableddisabled(intel_dp->dfp.ycbcr_444_to_420));
+
+ tmp = 0;
+
+ if (drm_dp_dpcd_writeb(&intel_dp->aux,
+ DP_PROTOCOL_CONVERTER_CONTROL_2, tmp) <= 0)
+ drm_dbg_kms(&i915->drm,
+ "Failed to set protocol converter YCbCr 4:2:2 conversion mode to %s\n",
+ enableddisabled(false));
+}
+
static void intel_enable_dp(struct intel_atomic_state *state,
struct intel_encoder *encoder,
const struct intel_crtc_state *pipe_config,
@@ -3789,6 +3926,7 @@ static void intel_enable_dp(struct intel_atomic_state *state,
}
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+ intel_dp_configure_protocol_converter(intel_dp);
intel_dp_start_link_train(intel_dp);
intel_dp_stop_link_train(intel_dp);
@@ -6028,16 +6166,103 @@ intel_dp_get_edid(struct intel_dp *intel_dp)
}
static void
+intel_dp_update_dfp(struct intel_dp *intel_dp,
+ const struct edid *edid)
+{
+ struct drm_i915_private *i915 = dp_to_i915(intel_dp);
+ struct intel_connector *connector = intel_dp->attached_connector;
+
+ intel_dp->dfp.max_bpc =
+ drm_dp_downstream_max_bpc(intel_dp->dpcd,
+ intel_dp->downstream_ports, edid);
+
+ intel_dp->dfp.max_dotclock =
+ drm_dp_downstream_max_dotclock(intel_dp->dpcd,
+ intel_dp->downstream_ports);
+
+ intel_dp->dfp.min_tmds_clock =
+ drm_dp_downstream_min_tmds_clock(intel_dp->dpcd,
+ intel_dp->downstream_ports,
+ edid);
+ intel_dp->dfp.max_tmds_clock =
+ drm_dp_downstream_max_tmds_clock(intel_dp->dpcd,
+ intel_dp->downstream_ports,
+ edid);
+
+ drm_dbg_kms(&i915->drm,
+ "[CONNECTOR:%d:%s] DFP max bpc %d, max dotclock %d, TMDS clock %d-%d\n",
+ connector->base.base.id, connector->base.name,
+ intel_dp->dfp.max_bpc,
+ intel_dp->dfp.max_dotclock,
+ intel_dp->dfp.min_tmds_clock,
+ intel_dp->dfp.max_tmds_clock);
+}
+
+static void
+intel_dp_update_420(struct intel_dp *intel_dp)
+{
+ struct drm_i915_private *i915 = dp_to_i915(intel_dp);
+ struct intel_connector *connector = intel_dp->attached_connector;
+ bool is_branch, ycbcr_420_passthrough, ycbcr_444_to_420;
+
+ /* No YCbCr output support on gmch platforms */
+ if (HAS_GMCH(i915))
+ return;
+
+ /*
+ * ILK doesn't seem capable of DP YCbCr output. The
+ * displayed image is severly corrupted. SNB+ is fine.
+ */
+ if (IS_GEN(i915, 5))
+ return;
+
+ is_branch = drm_dp_is_branch(intel_dp->dpcd);
+ ycbcr_420_passthrough =
+ drm_dp_downstream_420_passthrough(intel_dp->dpcd,
+ intel_dp->downstream_ports);
+ ycbcr_444_to_420 =
+ drm_dp_downstream_444_to_420_conversion(intel_dp->dpcd,
+ intel_dp->downstream_ports);
+
+ if (INTEL_GEN(i915) >= 11) {
+ /* Prefer 4:2:0 passthrough over 4:4:4->4:2:0 conversion */
+ intel_dp->dfp.ycbcr_444_to_420 =
+ ycbcr_444_to_420 && !ycbcr_420_passthrough;
+
+ connector->base.ycbcr_420_allowed =
+ !is_branch || ycbcr_444_to_420 || ycbcr_420_passthrough;
+ } else {
+ /* 4:4:4->4:2:0 conversion is the only way */
+ intel_dp->dfp.ycbcr_444_to_420 = ycbcr_444_to_420;
+
+ connector->base.ycbcr_420_allowed = ycbcr_444_to_420;
+ }
+
+ drm_dbg_kms(&i915->drm,
+ "[CONNECTOR:%d:%s] YCbCr 4:2:0 allowed? %s, YCbCr 4:4:4->4:2:0 conversion? %s\n",
+ connector->base.base.id, connector->base.name,
+ yesno(connector->base.ycbcr_420_allowed),
+ yesno(intel_dp->dfp.ycbcr_444_to_420));
+}
+
+static void
intel_dp_set_edid(struct intel_dp *intel_dp)
{
- struct intel_connector *intel_connector = intel_dp->attached_connector;
+ struct intel_connector *connector = intel_dp->attached_connector;
struct edid *edid;
intel_dp_unset_edid(intel_dp);
edid = intel_dp_get_edid(intel_dp);
- intel_connector->detect_edid = edid;
+ connector->detect_edid = edid;
+
+ intel_dp_update_dfp(intel_dp, edid);
+ intel_dp_update_420(intel_dp);
+
+ if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) {
+ intel_dp->has_hdmi_sink = drm_detect_hdmi_monitor(edid);
+ intel_dp->has_audio = drm_detect_monitor_audio(edid);
+ }
- intel_dp->has_audio = drm_detect_monitor_audio(edid);
drm_dp_cec_set_edid(&intel_dp->aux, edid);
intel_dp->edid_quirks = drm_dp_get_edid_quirks(edid);
}
@@ -6045,14 +6270,23 @@ intel_dp_set_edid(struct intel_dp *intel_dp)
static void
intel_dp_unset_edid(struct intel_dp *intel_dp)
{
- struct intel_connector *intel_connector = intel_dp->attached_connector;
+ struct intel_connector *connector = intel_dp->attached_connector;
drm_dp_cec_unset_edid(&intel_dp->aux);
- kfree(intel_connector->detect_edid);
- intel_connector->detect_edid = NULL;
+ kfree(connector->detect_edid);
+ connector->detect_edid = NULL;
+ intel_dp->has_hdmi_sink = false;
intel_dp->has_audio = false;
intel_dp->edid_quirks = 0;
+
+ intel_dp->dfp.max_bpc = 0;
+ intel_dp->dfp.max_dotclock = 0;
+ intel_dp->dfp.min_tmds_clock = 0;
+ intel_dp->dfp.max_tmds_clock = 0;
+
+ intel_dp->dfp.ycbcr_444_to_420 = false;
+ connector->base.ycbcr_420_allowed = false;
}
static int
@@ -6071,6 +6305,9 @@ intel_dp_detect(struct drm_connector *connector,
drm_WARN_ON(&dev_priv->drm,
!drm_modeset_is_locked(&dev_priv->drm.mode_config.connection_mutex));
+ if (!INTEL_DISPLAY_ENABLED(dev_priv))
+ return connector_status_disconnected;
+
/* Can't disconnect eDP */
if (intel_dp_is_edp(intel_dp))
status = edp_detect(intel_dp);
@@ -6211,7 +6448,7 @@ static int intel_dp_get_modes(struct drm_connector *connector)
}
/* if eDP has no EDID, fall back to fixed mode */
- if (intel_dp_is_edp(intel_attached_dp(to_intel_connector(connector))) &&
+ if (intel_dp_is_edp(intel_attached_dp(intel_connector)) &&
intel_connector->panel.fixed_mode) {
struct drm_display_mode *mode;
@@ -6223,6 +6460,19 @@ static int intel_dp_get_modes(struct drm_connector *connector)
}
}
+ if (!edid) {
+ struct intel_dp *intel_dp = intel_attached_dp(intel_connector);
+ struct drm_display_mode *mode;
+
+ mode = drm_dp_downstream_mode(connector->dev,
+ intel_dp->dpcd,
+ intel_dp->downstream_ports);
+ if (mode) {
+ drm_mode_probed_add(connector, mode);
+ return 1;
+ }
+ }
+
return 0;
}
@@ -6308,628 +6558,6 @@ void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
edp_panel_vdd_off_sync(intel_dp);
}
-static void intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp, int timeout)
-{
- long ret;
-
-#define C (hdcp->cp_irq_count_cached != atomic_read(&hdcp->cp_irq_count))
- ret = wait_event_interruptible_timeout(hdcp->cp_irq_queue, C,
- msecs_to_jiffies(timeout));
-
- if (!ret)
- DRM_DEBUG_KMS("Timedout at waiting for CP_IRQ\n");
-}
-
-static
-int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *dig_port,
- u8 *an)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- struct intel_dp *intel_dp = enc_to_intel_dp(to_intel_encoder(&dig_port->base.base));
- static const struct drm_dp_aux_msg msg = {
- .request = DP_AUX_NATIVE_WRITE,
- .address = DP_AUX_HDCP_AKSV,
- .size = DRM_HDCP_KSV_LEN,
- };
- u8 txbuf[HEADER_SIZE + DRM_HDCP_KSV_LEN] = {}, rxbuf[2], reply = 0;
- ssize_t dpcd_ret;
- int ret;
-
- /* Output An first, that's easy */
- dpcd_ret = drm_dp_dpcd_write(&dig_port->dp.aux, DP_AUX_HDCP_AN,
- an, DRM_HDCP_AN_LEN);
- if (dpcd_ret != DRM_HDCP_AN_LEN) {
- drm_dbg_kms(&i915->drm,
- "Failed to write An over DP/AUX (%zd)\n",
- dpcd_ret);
- return dpcd_ret >= 0 ? -EIO : dpcd_ret;
- }
-
- /*
- * Since Aksv is Oh-So-Secret, we can't access it in software. So in
- * order to get it on the wire, we need to create the AUX header as if
- * we were writing the data, and then tickle the hardware to output the
- * data once the header is sent out.
- */
- intel_dp_aux_header(txbuf, &msg);
-
- ret = intel_dp_aux_xfer(intel_dp, txbuf, HEADER_SIZE + msg.size,
- rxbuf, sizeof(rxbuf),
- DP_AUX_CH_CTL_AUX_AKSV_SELECT);
- if (ret < 0) {
- drm_dbg_kms(&i915->drm,
- "Write Aksv over DP/AUX failed (%d)\n", ret);
- return ret;
- } else if (ret == 0) {
- drm_dbg_kms(&i915->drm, "Aksv write over DP/AUX was empty\n");
- return -EIO;
- }
-
- reply = (rxbuf[0] >> 4) & DP_AUX_NATIVE_REPLY_MASK;
- if (reply != DP_AUX_NATIVE_REPLY_ACK) {
- drm_dbg_kms(&i915->drm,
- "Aksv write: no DP_AUX_NATIVE_REPLY_ACK %x\n",
- reply);
- return -EIO;
- }
- return 0;
-}
-
-static int intel_dp_hdcp_read_bksv(struct intel_digital_port *dig_port,
- u8 *bksv)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- ssize_t ret;
-
- ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BKSV, bksv,
- DRM_HDCP_KSV_LEN);
- if (ret != DRM_HDCP_KSV_LEN) {
- drm_dbg_kms(&i915->drm,
- "Read Bksv from DP/AUX failed (%zd)\n", ret);
- return ret >= 0 ? -EIO : ret;
- }
- return 0;
-}
-
-static int intel_dp_hdcp_read_bstatus(struct intel_digital_port *dig_port,
- u8 *bstatus)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- ssize_t ret;
-
- /*
- * For some reason the HDMI and DP HDCP specs call this register
- * definition by different names. In the HDMI spec, it's called BSTATUS,
- * but in DP it's called BINFO.
- */
- ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BINFO,
- bstatus, DRM_HDCP_BSTATUS_LEN);
- if (ret != DRM_HDCP_BSTATUS_LEN) {
- drm_dbg_kms(&i915->drm,
- "Read bstatus from DP/AUX failed (%zd)\n", ret);
- return ret >= 0 ? -EIO : ret;
- }
- return 0;
-}
-
-static
-int intel_dp_hdcp_read_bcaps(struct intel_digital_port *dig_port,
- u8 *bcaps)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- ssize_t ret;
-
- ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BCAPS,
- bcaps, 1);
- if (ret != 1) {
- drm_dbg_kms(&i915->drm,
- "Read bcaps from DP/AUX failed (%zd)\n", ret);
- return ret >= 0 ? -EIO : ret;
- }
-
- return 0;
-}
-
-static
-int intel_dp_hdcp_repeater_present(struct intel_digital_port *dig_port,
- bool *repeater_present)
-{
- ssize_t ret;
- u8 bcaps;
-
- ret = intel_dp_hdcp_read_bcaps(dig_port, &bcaps);
- if (ret)
- return ret;
-
- *repeater_present = bcaps & DP_BCAPS_REPEATER_PRESENT;
- return 0;
-}
-
-static
-int intel_dp_hdcp_read_ri_prime(struct intel_digital_port *dig_port,
- u8 *ri_prime)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- ssize_t ret;
-
- ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_RI_PRIME,
- ri_prime, DRM_HDCP_RI_LEN);
- if (ret != DRM_HDCP_RI_LEN) {
- drm_dbg_kms(&i915->drm, "Read Ri' from DP/AUX failed (%zd)\n",
- ret);
- return ret >= 0 ? -EIO : ret;
- }
- return 0;
-}
-
-static
-int intel_dp_hdcp_read_ksv_ready(struct intel_digital_port *dig_port,
- bool *ksv_ready)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- ssize_t ret;
- u8 bstatus;
-
- ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BSTATUS,
- &bstatus, 1);
- if (ret != 1) {
- drm_dbg_kms(&i915->drm,
- "Read bstatus from DP/AUX failed (%zd)\n", ret);
- return ret >= 0 ? -EIO : ret;
- }
- *ksv_ready = bstatus & DP_BSTATUS_READY;
- return 0;
-}
-
-static
-int intel_dp_hdcp_read_ksv_fifo(struct intel_digital_port *dig_port,
- int num_downstream, u8 *ksv_fifo)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- ssize_t ret;
- int i;
-
- /* KSV list is read via 15 byte window (3 entries @ 5 bytes each) */
- for (i = 0; i < num_downstream; i += 3) {
- size_t len = min(num_downstream - i, 3) * DRM_HDCP_KSV_LEN;
- ret = drm_dp_dpcd_read(&dig_port->dp.aux,
- DP_AUX_HDCP_KSV_FIFO,
- ksv_fifo + i * DRM_HDCP_KSV_LEN,
- len);
- if (ret != len) {
- drm_dbg_kms(&i915->drm,
- "Read ksv[%d] from DP/AUX failed (%zd)\n",
- i, ret);
- return ret >= 0 ? -EIO : ret;
- }
- }
- return 0;
-}
-
-static
-int intel_dp_hdcp_read_v_prime_part(struct intel_digital_port *dig_port,
- int i, u32 *part)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- ssize_t ret;
-
- if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
- return -EINVAL;
-
- ret = drm_dp_dpcd_read(&dig_port->dp.aux,
- DP_AUX_HDCP_V_PRIME(i), part,
- DRM_HDCP_V_PRIME_PART_LEN);
- if (ret != DRM_HDCP_V_PRIME_PART_LEN) {
- drm_dbg_kms(&i915->drm,
- "Read v'[%d] from DP/AUX failed (%zd)\n", i, ret);
- return ret >= 0 ? -EIO : ret;
- }
- return 0;
-}
-
-static
-int intel_dp_hdcp_toggle_signalling(struct intel_digital_port *dig_port,
- bool enable)
-{
- /* Not used for single stream DisplayPort setups */
- return 0;
-}
-
-static
-bool intel_dp_hdcp_check_link(struct intel_digital_port *dig_port)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- ssize_t ret;
- u8 bstatus;
-
- ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BSTATUS,
- &bstatus, 1);
- if (ret != 1) {
- drm_dbg_kms(&i915->drm,
- "Read bstatus from DP/AUX failed (%zd)\n", ret);
- return false;
- }
-
- return !(bstatus & (DP_BSTATUS_LINK_FAILURE | DP_BSTATUS_REAUTH_REQ));
-}
-
-static
-int intel_dp_hdcp_capable(struct intel_digital_port *dig_port,
- bool *hdcp_capable)
-{
- ssize_t ret;
- u8 bcaps;
-
- ret = intel_dp_hdcp_read_bcaps(dig_port, &bcaps);
- if (ret)
- return ret;
-
- *hdcp_capable = bcaps & DP_BCAPS_HDCP_CAPABLE;
- return 0;
-}
-
-struct hdcp2_dp_errata_stream_type {
- u8 msg_id;
- u8 stream_type;
-} __packed;
-
-struct hdcp2_dp_msg_data {
- u8 msg_id;
- u32 offset;
- bool msg_detectable;
- u32 timeout;
- u32 timeout2; /* Added for non_paired situation */
-};
-
-static const struct hdcp2_dp_msg_data hdcp2_dp_msg_data[] = {
- { HDCP_2_2_AKE_INIT, DP_HDCP_2_2_AKE_INIT_OFFSET, false, 0, 0 },
- { HDCP_2_2_AKE_SEND_CERT, DP_HDCP_2_2_AKE_SEND_CERT_OFFSET,
- false, HDCP_2_2_CERT_TIMEOUT_MS, 0 },
- { HDCP_2_2_AKE_NO_STORED_KM, DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET,
- false, 0, 0 },
- { HDCP_2_2_AKE_STORED_KM, DP_HDCP_2_2_AKE_STORED_KM_OFFSET,
- false, 0, 0 },
- { HDCP_2_2_AKE_SEND_HPRIME, DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET,
- true, HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS,
- HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS },
- { HDCP_2_2_AKE_SEND_PAIRING_INFO,
- DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET, true,
- HDCP_2_2_PAIRING_TIMEOUT_MS, 0 },
- { HDCP_2_2_LC_INIT, DP_HDCP_2_2_LC_INIT_OFFSET, false, 0, 0 },
- { HDCP_2_2_LC_SEND_LPRIME, DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET,
- false, HDCP_2_2_DP_LPRIME_TIMEOUT_MS, 0 },
- { HDCP_2_2_SKE_SEND_EKS, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, false,
- 0, 0 },
- { HDCP_2_2_REP_SEND_RECVID_LIST,
- DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, true,
- HDCP_2_2_RECVID_LIST_TIMEOUT_MS, 0 },
- { HDCP_2_2_REP_SEND_ACK, DP_HDCP_2_2_REP_SEND_ACK_OFFSET, false,
- 0, 0 },
- { HDCP_2_2_REP_STREAM_MANAGE,
- DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET, false,
- 0, 0 },
- { HDCP_2_2_REP_STREAM_READY, DP_HDCP_2_2_REP_STREAM_READY_OFFSET,
- false, HDCP_2_2_STREAM_READY_TIMEOUT_MS, 0 },
-/* local define to shovel this through the write_2_2 interface */
-#define HDCP_2_2_ERRATA_DP_STREAM_TYPE 50
- { HDCP_2_2_ERRATA_DP_STREAM_TYPE,
- DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET, false,
- 0, 0 },
-};
-
-static int
-intel_dp_hdcp2_read_rx_status(struct intel_digital_port *dig_port,
- u8 *rx_status)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- ssize_t ret;
-
- ret = drm_dp_dpcd_read(&dig_port->dp.aux,
- DP_HDCP_2_2_REG_RXSTATUS_OFFSET, rx_status,
- HDCP_2_2_DP_RXSTATUS_LEN);
- if (ret != HDCP_2_2_DP_RXSTATUS_LEN) {
- drm_dbg_kms(&i915->drm,
- "Read bstatus from DP/AUX failed (%zd)\n", ret);
- return ret >= 0 ? -EIO : ret;
- }
-
- return 0;
-}
-
-static
-int hdcp2_detect_msg_availability(struct intel_digital_port *dig_port,
- u8 msg_id, bool *msg_ready)
-{
- u8 rx_status;
- int ret;
-
- *msg_ready = false;
- ret = intel_dp_hdcp2_read_rx_status(dig_port, &rx_status);
- if (ret < 0)
- return ret;
-
- switch (msg_id) {
- case HDCP_2_2_AKE_SEND_HPRIME:
- if (HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status))
- *msg_ready = true;
- break;
- case HDCP_2_2_AKE_SEND_PAIRING_INFO:
- if (HDCP_2_2_DP_RXSTATUS_PAIRING(rx_status))
- *msg_ready = true;
- break;
- case HDCP_2_2_REP_SEND_RECVID_LIST:
- if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
- *msg_ready = true;
- break;
- default:
- DRM_ERROR("Unidentified msg_id: %d\n", msg_id);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static ssize_t
-intel_dp_hdcp2_wait_for_msg(struct intel_digital_port *dig_port,
- const struct hdcp2_dp_msg_data *hdcp2_msg_data)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- struct intel_dp *dp = &dig_port->dp;
- struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
- u8 msg_id = hdcp2_msg_data->msg_id;
- int ret, timeout;
- bool msg_ready = false;
-
- if (msg_id == HDCP_2_2_AKE_SEND_HPRIME && !hdcp->is_paired)
- timeout = hdcp2_msg_data->timeout2;
- else
- timeout = hdcp2_msg_data->timeout;
-
- /*
- * There is no way to detect the CERT, LPRIME and STREAM_READY
- * availability. So Wait for timeout and read the msg.
- */
- if (!hdcp2_msg_data->msg_detectable) {
- mdelay(timeout);
- ret = 0;
- } else {
- /*
- * As we want to check the msg availability at timeout, Ignoring
- * the timeout at wait for CP_IRQ.
- */
- intel_dp_hdcp_wait_for_cp_irq(hdcp, timeout);
- ret = hdcp2_detect_msg_availability(dig_port,
- msg_id, &msg_ready);
- if (!msg_ready)
- ret = -ETIMEDOUT;
- }
-
- if (ret)
- drm_dbg_kms(&i915->drm,
- "msg_id %d, ret %d, timeout(mSec): %d\n",
- hdcp2_msg_data->msg_id, ret, timeout);
-
- return ret;
-}
-
-static const struct hdcp2_dp_msg_data *get_hdcp2_dp_msg_data(u8 msg_id)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hdcp2_dp_msg_data); i++)
- if (hdcp2_dp_msg_data[i].msg_id == msg_id)
- return &hdcp2_dp_msg_data[i];
-
- return NULL;
-}
-
-static
-int intel_dp_hdcp2_write_msg(struct intel_digital_port *dig_port,
- void *buf, size_t size)
-{
- struct intel_dp *dp = &dig_port->dp;
- struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
- unsigned int offset;
- u8 *byte = buf;
- ssize_t ret, bytes_to_write, len;
- const struct hdcp2_dp_msg_data *hdcp2_msg_data;
-
- hdcp2_msg_data = get_hdcp2_dp_msg_data(*byte);
- if (!hdcp2_msg_data)
- return -EINVAL;
-
- offset = hdcp2_msg_data->offset;
-
- /* No msg_id in DP HDCP2.2 msgs */
- bytes_to_write = size - 1;
- byte++;
-
- hdcp->cp_irq_count_cached = atomic_read(&hdcp->cp_irq_count);
-
- while (bytes_to_write) {
- len = bytes_to_write > DP_AUX_MAX_PAYLOAD_BYTES ?
- DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_write;
-
- ret = drm_dp_dpcd_write(&dig_port->dp.aux,
- offset, (void *)byte, len);
- if (ret < 0)
- return ret;
-
- bytes_to_write -= ret;
- byte += ret;
- offset += ret;
- }
-
- return size;
-}
-
-static
-ssize_t get_receiver_id_list_size(struct intel_digital_port *dig_port)
-{
- u8 rx_info[HDCP_2_2_RXINFO_LEN];
- u32 dev_cnt;
- ssize_t ret;
-
- ret = drm_dp_dpcd_read(&dig_port->dp.aux,
- DP_HDCP_2_2_REG_RXINFO_OFFSET,
- (void *)rx_info, HDCP_2_2_RXINFO_LEN);
- if (ret != HDCP_2_2_RXINFO_LEN)
- return ret >= 0 ? -EIO : ret;
-
- dev_cnt = (HDCP_2_2_DEV_COUNT_HI(rx_info[0]) << 4 |
- HDCP_2_2_DEV_COUNT_LO(rx_info[1]));
-
- if (dev_cnt > HDCP_2_2_MAX_DEVICE_COUNT)
- dev_cnt = HDCP_2_2_MAX_DEVICE_COUNT;
-
- ret = sizeof(struct hdcp2_rep_send_receiverid_list) -
- HDCP_2_2_RECEIVER_IDS_MAX_LEN +
- (dev_cnt * HDCP_2_2_RECEIVER_ID_LEN);
-
- return ret;
-}
-
-static
-int intel_dp_hdcp2_read_msg(struct intel_digital_port *dig_port,
- u8 msg_id, void *buf, size_t size)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- unsigned int offset;
- u8 *byte = buf;
- ssize_t ret, bytes_to_recv, len;
- const struct hdcp2_dp_msg_data *hdcp2_msg_data;
-
- hdcp2_msg_data = get_hdcp2_dp_msg_data(msg_id);
- if (!hdcp2_msg_data)
- return -EINVAL;
- offset = hdcp2_msg_data->offset;
-
- ret = intel_dp_hdcp2_wait_for_msg(dig_port, hdcp2_msg_data);
- if (ret < 0)
- return ret;
-
- if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST) {
- ret = get_receiver_id_list_size(dig_port);
- if (ret < 0)
- return ret;
-
- size = ret;
- }
- bytes_to_recv = size - 1;
-
- /* DP adaptation msgs has no msg_id */
- byte++;
-
- while (bytes_to_recv) {
- len = bytes_to_recv > DP_AUX_MAX_PAYLOAD_BYTES ?
- DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_recv;
-
- ret = drm_dp_dpcd_read(&dig_port->dp.aux, offset,
- (void *)byte, len);
- if (ret < 0) {
- drm_dbg_kms(&i915->drm, "msg_id %d, ret %zd\n",
- msg_id, ret);
- return ret;
- }
-
- bytes_to_recv -= ret;
- byte += ret;
- offset += ret;
- }
- byte = buf;
- *byte = msg_id;
-
- return size;
-}
-
-static
-int intel_dp_hdcp2_config_stream_type(struct intel_digital_port *dig_port,
- bool is_repeater, u8 content_type)
-{
- int ret;
- struct hdcp2_dp_errata_stream_type stream_type_msg;
-
- if (is_repeater)
- return 0;
-
- /*
- * Errata for DP: As Stream type is used for encryption, Receiver
- * should be communicated with stream type for the decryption of the
- * content.
- * Repeater will be communicated with stream type as a part of it's
- * auth later in time.
- */
- stream_type_msg.msg_id = HDCP_2_2_ERRATA_DP_STREAM_TYPE;
- stream_type_msg.stream_type = content_type;
-
- ret = intel_dp_hdcp2_write_msg(dig_port, &stream_type_msg,
- sizeof(stream_type_msg));
-
- return ret < 0 ? ret : 0;
-
-}
-
-static
-int intel_dp_hdcp2_check_link(struct intel_digital_port *dig_port)
-{
- u8 rx_status;
- int ret;
-
- ret = intel_dp_hdcp2_read_rx_status(dig_port, &rx_status);
- if (ret)
- return ret;
-
- if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status))
- ret = HDCP_REAUTH_REQUEST;
- else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status))
- ret = HDCP_LINK_INTEGRITY_FAILURE;
- else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
- ret = HDCP_TOPOLOGY_CHANGE;
-
- return ret;
-}
-
-static
-int intel_dp_hdcp2_capable(struct intel_digital_port *dig_port,
- bool *capable)
-{
- u8 rx_caps[3];
- int ret;
-
- *capable = false;
- ret = drm_dp_dpcd_read(&dig_port->dp.aux,
- DP_HDCP_2_2_REG_RX_CAPS_OFFSET,
- rx_caps, HDCP_2_2_RXCAPS_LEN);
- if (ret != HDCP_2_2_RXCAPS_LEN)
- return ret >= 0 ? -EIO : ret;
-
- if (rx_caps[0] == HDCP_2_2_RX_CAPS_VERSION_VAL &&
- HDCP_2_2_DP_HDCP_CAPABLE(rx_caps[2]))
- *capable = true;
-
- return 0;
-}
-
-static const struct intel_hdcp_shim intel_dp_hdcp_shim = {
- .write_an_aksv = intel_dp_hdcp_write_an_aksv,
- .read_bksv = intel_dp_hdcp_read_bksv,
- .read_bstatus = intel_dp_hdcp_read_bstatus,
- .repeater_present = intel_dp_hdcp_repeater_present,
- .read_ri_prime = intel_dp_hdcp_read_ri_prime,
- .read_ksv_ready = intel_dp_hdcp_read_ksv_ready,
- .read_ksv_fifo = intel_dp_hdcp_read_ksv_fifo,
- .read_v_prime_part = intel_dp_hdcp_read_v_prime_part,
- .toggle_signalling = intel_dp_hdcp_toggle_signalling,
- .check_link = intel_dp_hdcp_check_link,
- .hdcp_capable = intel_dp_hdcp_capable,
- .write_2_2_msg = intel_dp_hdcp2_write_msg,
- .read_2_2_msg = intel_dp_hdcp2_read_msg,
- .config_stream_type = intel_dp_hdcp2_config_stream_type,
- .check_2_2_link = intel_dp_hdcp2_check_link,
- .hdcp_2_2_capable = intel_dp_hdcp2_capable,
- .protocol = HDCP_PROTOCOL_DP,
-};
-
static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
{
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
@@ -7640,6 +7268,15 @@ static void intel_dp_set_drrs_state(struct drm_i915_private *dev_priv,
refresh_rate);
}
+static void
+intel_edp_drrs_enable_locked(struct intel_dp *intel_dp)
+{
+ struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+
+ dev_priv->drrs.busy_frontbuffer_bits = 0;
+ dev_priv->drrs.dp = intel_dp;
+}
+
/**
* intel_edp_drrs_enable - init drrs struct if supported
* @intel_dp: DP struct
@@ -7652,31 +7289,40 @@ void intel_edp_drrs_enable(struct intel_dp *intel_dp,
{
struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
- if (!crtc_state->has_drrs) {
- drm_dbg_kms(&dev_priv->drm, "Panel doesn't support DRRS\n");
+ if (!crtc_state->has_drrs)
return;
- }
- if (dev_priv->psr.enabled) {
- drm_dbg_kms(&dev_priv->drm,
- "PSR enabled. Not enabling DRRS.\n");
- return;
- }
+ drm_dbg_kms(&dev_priv->drm, "Enabling DRRS\n");
mutex_lock(&dev_priv->drrs.mutex);
+
if (dev_priv->drrs.dp) {
- drm_dbg_kms(&dev_priv->drm, "DRRS already enabled\n");
+ drm_warn(&dev_priv->drm, "DRRS already enabled\n");
goto unlock;
}
- dev_priv->drrs.busy_frontbuffer_bits = 0;
-
- dev_priv->drrs.dp = intel_dp;
+ intel_edp_drrs_enable_locked(intel_dp);
unlock:
mutex_unlock(&dev_priv->drrs.mutex);
}
+static void
+intel_edp_drrs_disable_locked(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+
+ if (dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR) {
+ int refresh;
+
+ refresh = drm_mode_vrefresh(intel_dp->attached_connector->panel.fixed_mode);
+ intel_dp_set_drrs_state(dev_priv, crtc_state, refresh);
+ }
+
+ dev_priv->drrs.dp = NULL;
+}
+
/**
* intel_edp_drrs_disable - Disable DRRS
* @intel_dp: DP struct
@@ -7697,16 +7343,45 @@ void intel_edp_drrs_disable(struct intel_dp *intel_dp,
return;
}
- if (dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR)
- intel_dp_set_drrs_state(dev_priv, old_crtc_state,
- drm_mode_vrefresh(intel_dp->attached_connector->panel.fixed_mode));
-
- dev_priv->drrs.dp = NULL;
+ intel_edp_drrs_disable_locked(intel_dp, old_crtc_state);
mutex_unlock(&dev_priv->drrs.mutex);
cancel_delayed_work_sync(&dev_priv->drrs.work);
}
+/**
+ * intel_edp_drrs_update - Update DRRS state
+ * @intel_dp: Intel DP
+ * @crtc_state: new CRTC state
+ *
+ * This function will update DRRS states, disabling or enabling DRRS when
+ * executing fastsets. For full modeset, intel_edp_drrs_disable() and
+ * intel_edp_drrs_enable() should be called instead.
+ */
+void
+intel_edp_drrs_update(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct drm_i915_private *dev_priv = dp_to_i915(intel_dp);
+
+ if (dev_priv->drrs.type != SEAMLESS_DRRS_SUPPORT)
+ return;
+
+ mutex_lock(&dev_priv->drrs.mutex);
+
+ /* New state matches current one? */
+ if (crtc_state->has_drrs == !!dev_priv->drrs.dp)
+ goto unlock;
+
+ if (crtc_state->has_drrs)
+ intel_edp_drrs_enable_locked(intel_dp);
+ else
+ intel_edp_drrs_disable_locked(intel_dp, crtc_state);
+
+unlock:
+ mutex_unlock(&dev_priv->drrs.mutex);
+}
+
static void intel_edp_drrs_downclock_work(struct work_struct *work)
{
struct drm_i915_private *dev_priv =
@@ -8138,10 +7813,6 @@ intel_dp_init_connector(struct intel_digital_port *dig_port,
connector->interlace_allowed = true;
connector->doublescan_allowed = 0;
- if (INTEL_GEN(dev_priv) >= 11)
- connector->ycbcr_420_allowed = true;
-
- intel_encoder->hpd_pin = intel_hpd_pin_default(dev_priv, port);
intel_connector->polled = DRM_CONNECTOR_POLL_HPD;
intel_dp_aux_init(intel_dp);
@@ -8166,7 +7837,7 @@ intel_dp_init_connector(struct intel_digital_port *dig_port,
intel_dp_add_properties(intel_dp, connector);
if (is_hdcp_supported(dev_priv, port) && !intel_dp_is_edp(intel_dp)) {
- int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim);
+ int ret = intel_dp_init_hdcp(dig_port, intel_connector);
if (ret)
drm_dbg_kms(&dev_priv->drm,
"HDCP init failed, skipping.\n");
@@ -8210,6 +7881,8 @@ bool intel_dp_init(struct drm_i915_private *dev_priv,
intel_encoder = &dig_port->base;
encoder = &intel_encoder->base;
+ mutex_init(&dig_port->hdcp_mutex);
+
if (drm_encoder_init(&dev_priv->drm, &intel_encoder->base,
&intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS,
"DP %c", port_name(port)))
@@ -8284,6 +7957,7 @@ bool intel_dp_init(struct drm_i915_private *dev_priv,
}
intel_encoder->cloneable = 0;
intel_encoder->port = port;
+ intel_encoder->hpd_pin = intel_hpd_pin_default(dev_priv, port);
dig_port->hpd_pulse = intel_dp_hpd_pulse;
diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h
index 0a3af3410d52..08a1c0aa8b94 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.h
+++ b/drivers/gpu/drm/i915/display/intel_dp.h
@@ -17,6 +17,7 @@ struct drm_encoder;
struct drm_i915_private;
struct drm_modeset_acquire_ctx;
struct drm_dp_vsc_sdp;
+struct intel_atomic_state;
struct intel_connector;
struct intel_crtc_state;
struct intel_digital_port;
@@ -50,6 +51,7 @@ int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
int intel_dp_retrain_link(struct intel_encoder *encoder,
struct drm_modeset_acquire_ctx *ctx);
void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode);
+void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp);
void intel_dp_sink_set_decompression_state(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state,
bool enable);
@@ -81,6 +83,8 @@ void intel_edp_drrs_enable(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state);
void intel_edp_drrs_disable(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state);
+void intel_edp_drrs_update(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state);
void intel_edp_drrs_invalidate(struct drm_i915_private *dev_priv,
unsigned int frontbuffer_bits);
void intel_edp_drrs_flush(struct drm_i915_private *dev_priv,
@@ -127,4 +131,12 @@ static inline unsigned int intel_dp_unused_lane_mask(int lane_count)
u32 intel_dp_mode_to_fec_clock(u32 mode_clock);
+void intel_ddi_update_pipe(struct intel_atomic_state *state,
+ struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state,
+ const struct drm_connector_state *conn_state);
+
+int intel_dp_init_hdcp(struct intel_digital_port *dig_port,
+ struct intel_connector *intel_connector);
+
#endif /* __INTEL_DP_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c
new file mode 100644
index 000000000000..03424d20e9f7
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c
@@ -0,0 +1,703 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2020 Google, Inc.
+ *
+ * Authors:
+ * Sean Paul <seanpaul@chromium.org>
+ */
+
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_dp_mst_helper.h>
+#include <drm/drm_hdcp.h>
+#include <drm/drm_print.h>
+
+#include "intel_display_types.h"
+#include "intel_ddi.h"
+#include "intel_dp.h"
+#include "intel_hdcp.h"
+
+static void intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp, int timeout)
+{
+ long ret;
+
+#define C (hdcp->cp_irq_count_cached != atomic_read(&hdcp->cp_irq_count))
+ ret = wait_event_interruptible_timeout(hdcp->cp_irq_queue, C,
+ msecs_to_jiffies(timeout));
+
+ if (!ret)
+ DRM_DEBUG_KMS("Timedout at waiting for CP_IRQ\n");
+}
+
+static
+int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *dig_port,
+ u8 *an)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ u8 aksv[DRM_HDCP_KSV_LEN] = {};
+ ssize_t dpcd_ret;
+
+ /* Output An first, that's easy */
+ dpcd_ret = drm_dp_dpcd_write(&dig_port->dp.aux, DP_AUX_HDCP_AN,
+ an, DRM_HDCP_AN_LEN);
+ if (dpcd_ret != DRM_HDCP_AN_LEN) {
+ drm_dbg_kms(&i915->drm,
+ "Failed to write An over DP/AUX (%zd)\n",
+ dpcd_ret);
+ return dpcd_ret >= 0 ? -EIO : dpcd_ret;
+ }
+
+ /*
+ * Since Aksv is Oh-So-Secret, we can't access it in software. So we
+ * send an empty buffer of the correct length through the DP helpers. On
+ * the other side, in the transfer hook, we'll generate a flag based on
+ * the destination address which will tickle the hardware to output the
+ * Aksv on our behalf after the header is sent.
+ */
+ dpcd_ret = drm_dp_dpcd_write(&dig_port->dp.aux, DP_AUX_HDCP_AKSV,
+ aksv, DRM_HDCP_KSV_LEN);
+ if (dpcd_ret != DRM_HDCP_KSV_LEN) {
+ drm_dbg_kms(&i915->drm,
+ "Failed to write Aksv over DP/AUX (%zd)\n",
+ dpcd_ret);
+ return dpcd_ret >= 0 ? -EIO : dpcd_ret;
+ }
+ return 0;
+}
+
+static int intel_dp_hdcp_read_bksv(struct intel_digital_port *dig_port,
+ u8 *bksv)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BKSV, bksv,
+ DRM_HDCP_KSV_LEN);
+ if (ret != DRM_HDCP_KSV_LEN) {
+ drm_dbg_kms(&i915->drm,
+ "Read Bksv from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ return 0;
+}
+
+static int intel_dp_hdcp_read_bstatus(struct intel_digital_port *dig_port,
+ u8 *bstatus)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ ssize_t ret;
+
+ /*
+ * For some reason the HDMI and DP HDCP specs call this register
+ * definition by different names. In the HDMI spec, it's called BSTATUS,
+ * but in DP it's called BINFO.
+ */
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BINFO,
+ bstatus, DRM_HDCP_BSTATUS_LEN);
+ if (ret != DRM_HDCP_BSTATUS_LEN) {
+ drm_dbg_kms(&i915->drm,
+ "Read bstatus from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ return 0;
+}
+
+static
+int intel_dp_hdcp_read_bcaps(struct intel_digital_port *dig_port,
+ u8 *bcaps)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BCAPS,
+ bcaps, 1);
+ if (ret != 1) {
+ drm_dbg_kms(&i915->drm,
+ "Read bcaps from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+
+ return 0;
+}
+
+static
+int intel_dp_hdcp_repeater_present(struct intel_digital_port *dig_port,
+ bool *repeater_present)
+{
+ ssize_t ret;
+ u8 bcaps;
+
+ ret = intel_dp_hdcp_read_bcaps(dig_port, &bcaps);
+ if (ret)
+ return ret;
+
+ *repeater_present = bcaps & DP_BCAPS_REPEATER_PRESENT;
+ return 0;
+}
+
+static
+int intel_dp_hdcp_read_ri_prime(struct intel_digital_port *dig_port,
+ u8 *ri_prime)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_RI_PRIME,
+ ri_prime, DRM_HDCP_RI_LEN);
+ if (ret != DRM_HDCP_RI_LEN) {
+ drm_dbg_kms(&i915->drm, "Read Ri' from DP/AUX failed (%zd)\n",
+ ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ return 0;
+}
+
+static
+int intel_dp_hdcp_read_ksv_ready(struct intel_digital_port *dig_port,
+ bool *ksv_ready)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ ssize_t ret;
+ u8 bstatus;
+
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BSTATUS,
+ &bstatus, 1);
+ if (ret != 1) {
+ drm_dbg_kms(&i915->drm,
+ "Read bstatus from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ *ksv_ready = bstatus & DP_BSTATUS_READY;
+ return 0;
+}
+
+static
+int intel_dp_hdcp_read_ksv_fifo(struct intel_digital_port *dig_port,
+ int num_downstream, u8 *ksv_fifo)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ ssize_t ret;
+ int i;
+
+ /* KSV list is read via 15 byte window (3 entries @ 5 bytes each) */
+ for (i = 0; i < num_downstream; i += 3) {
+ size_t len = min(num_downstream - i, 3) * DRM_HDCP_KSV_LEN;
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux,
+ DP_AUX_HDCP_KSV_FIFO,
+ ksv_fifo + i * DRM_HDCP_KSV_LEN,
+ len);
+ if (ret != len) {
+ drm_dbg_kms(&i915->drm,
+ "Read ksv[%d] from DP/AUX failed (%zd)\n",
+ i, ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ }
+ return 0;
+}
+
+static
+int intel_dp_hdcp_read_v_prime_part(struct intel_digital_port *dig_port,
+ int i, u32 *part)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ ssize_t ret;
+
+ if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
+ return -EINVAL;
+
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux,
+ DP_AUX_HDCP_V_PRIME(i), part,
+ DRM_HDCP_V_PRIME_PART_LEN);
+ if (ret != DRM_HDCP_V_PRIME_PART_LEN) {
+ drm_dbg_kms(&i915->drm,
+ "Read v'[%d] from DP/AUX failed (%zd)\n", i, ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ return 0;
+}
+
+static
+int intel_dp_hdcp_toggle_signalling(struct intel_digital_port *dig_port,
+ enum transcoder cpu_transcoder,
+ bool enable)
+{
+ /* Not used for single stream DisplayPort setups */
+ return 0;
+}
+
+static
+bool intel_dp_hdcp_check_link(struct intel_digital_port *dig_port,
+ struct intel_connector *connector)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ ssize_t ret;
+ u8 bstatus;
+
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BSTATUS,
+ &bstatus, 1);
+ if (ret != 1) {
+ drm_dbg_kms(&i915->drm,
+ "Read bstatus from DP/AUX failed (%zd)\n", ret);
+ return false;
+ }
+
+ return !(bstatus & (DP_BSTATUS_LINK_FAILURE | DP_BSTATUS_REAUTH_REQ));
+}
+
+static
+int intel_dp_hdcp_capable(struct intel_digital_port *dig_port,
+ bool *hdcp_capable)
+{
+ ssize_t ret;
+ u8 bcaps;
+
+ ret = intel_dp_hdcp_read_bcaps(dig_port, &bcaps);
+ if (ret)
+ return ret;
+
+ *hdcp_capable = bcaps & DP_BCAPS_HDCP_CAPABLE;
+ return 0;
+}
+
+struct hdcp2_dp_errata_stream_type {
+ u8 msg_id;
+ u8 stream_type;
+} __packed;
+
+struct hdcp2_dp_msg_data {
+ u8 msg_id;
+ u32 offset;
+ bool msg_detectable;
+ u32 timeout;
+ u32 timeout2; /* Added for non_paired situation */
+};
+
+static const struct hdcp2_dp_msg_data hdcp2_dp_msg_data[] = {
+ { HDCP_2_2_AKE_INIT, DP_HDCP_2_2_AKE_INIT_OFFSET, false, 0, 0 },
+ { HDCP_2_2_AKE_SEND_CERT, DP_HDCP_2_2_AKE_SEND_CERT_OFFSET,
+ false, HDCP_2_2_CERT_TIMEOUT_MS, 0 },
+ { HDCP_2_2_AKE_NO_STORED_KM, DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET,
+ false, 0, 0 },
+ { HDCP_2_2_AKE_STORED_KM, DP_HDCP_2_2_AKE_STORED_KM_OFFSET,
+ false, 0, 0 },
+ { HDCP_2_2_AKE_SEND_HPRIME, DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET,
+ true, HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS,
+ HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS },
+ { HDCP_2_2_AKE_SEND_PAIRING_INFO,
+ DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET, true,
+ HDCP_2_2_PAIRING_TIMEOUT_MS, 0 },
+ { HDCP_2_2_LC_INIT, DP_HDCP_2_2_LC_INIT_OFFSET, false, 0, 0 },
+ { HDCP_2_2_LC_SEND_LPRIME, DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET,
+ false, HDCP_2_2_DP_LPRIME_TIMEOUT_MS, 0 },
+ { HDCP_2_2_SKE_SEND_EKS, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, false,
+ 0, 0 },
+ { HDCP_2_2_REP_SEND_RECVID_LIST,
+ DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, true,
+ HDCP_2_2_RECVID_LIST_TIMEOUT_MS, 0 },
+ { HDCP_2_2_REP_SEND_ACK, DP_HDCP_2_2_REP_SEND_ACK_OFFSET, false,
+ 0, 0 },
+ { HDCP_2_2_REP_STREAM_MANAGE,
+ DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET, false,
+ 0, 0 },
+ { HDCP_2_2_REP_STREAM_READY, DP_HDCP_2_2_REP_STREAM_READY_OFFSET,
+ false, HDCP_2_2_STREAM_READY_TIMEOUT_MS, 0 },
+/* local define to shovel this through the write_2_2 interface */
+#define HDCP_2_2_ERRATA_DP_STREAM_TYPE 50
+ { HDCP_2_2_ERRATA_DP_STREAM_TYPE,
+ DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET, false,
+ 0, 0 },
+};
+
+static int
+intel_dp_hdcp2_read_rx_status(struct intel_digital_port *dig_port,
+ u8 *rx_status)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RXSTATUS_OFFSET, rx_status,
+ HDCP_2_2_DP_RXSTATUS_LEN);
+ if (ret != HDCP_2_2_DP_RXSTATUS_LEN) {
+ drm_dbg_kms(&i915->drm,
+ "Read bstatus from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+
+ return 0;
+}
+
+static
+int hdcp2_detect_msg_availability(struct intel_digital_port *dig_port,
+ u8 msg_id, bool *msg_ready)
+{
+ u8 rx_status;
+ int ret;
+
+ *msg_ready = false;
+ ret = intel_dp_hdcp2_read_rx_status(dig_port, &rx_status);
+ if (ret < 0)
+ return ret;
+
+ switch (msg_id) {
+ case HDCP_2_2_AKE_SEND_HPRIME:
+ if (HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status))
+ *msg_ready = true;
+ break;
+ case HDCP_2_2_AKE_SEND_PAIRING_INFO:
+ if (HDCP_2_2_DP_RXSTATUS_PAIRING(rx_status))
+ *msg_ready = true;
+ break;
+ case HDCP_2_2_REP_SEND_RECVID_LIST:
+ if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
+ *msg_ready = true;
+ break;
+ default:
+ DRM_ERROR("Unidentified msg_id: %d\n", msg_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ssize_t
+intel_dp_hdcp2_wait_for_msg(struct intel_digital_port *dig_port,
+ const struct hdcp2_dp_msg_data *hdcp2_msg_data)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct intel_dp *dp = &dig_port->dp;
+ struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
+ u8 msg_id = hdcp2_msg_data->msg_id;
+ int ret, timeout;
+ bool msg_ready = false;
+
+ if (msg_id == HDCP_2_2_AKE_SEND_HPRIME && !hdcp->is_paired)
+ timeout = hdcp2_msg_data->timeout2;
+ else
+ timeout = hdcp2_msg_data->timeout;
+
+ /*
+ * There is no way to detect the CERT, LPRIME and STREAM_READY
+ * availability. So Wait for timeout and read the msg.
+ */
+ if (!hdcp2_msg_data->msg_detectable) {
+ mdelay(timeout);
+ ret = 0;
+ } else {
+ /*
+ * As we want to check the msg availability at timeout, Ignoring
+ * the timeout at wait for CP_IRQ.
+ */
+ intel_dp_hdcp_wait_for_cp_irq(hdcp, timeout);
+ ret = hdcp2_detect_msg_availability(dig_port,
+ msg_id, &msg_ready);
+ if (!msg_ready)
+ ret = -ETIMEDOUT;
+ }
+
+ if (ret)
+ drm_dbg_kms(&i915->drm,
+ "msg_id %d, ret %d, timeout(mSec): %d\n",
+ hdcp2_msg_data->msg_id, ret, timeout);
+
+ return ret;
+}
+
+static const struct hdcp2_dp_msg_data *get_hdcp2_dp_msg_data(u8 msg_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdcp2_dp_msg_data); i++)
+ if (hdcp2_dp_msg_data[i].msg_id == msg_id)
+ return &hdcp2_dp_msg_data[i];
+
+ return NULL;
+}
+
+static
+int intel_dp_hdcp2_write_msg(struct intel_digital_port *dig_port,
+ void *buf, size_t size)
+{
+ struct intel_dp *dp = &dig_port->dp;
+ struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
+ unsigned int offset;
+ u8 *byte = buf;
+ ssize_t ret, bytes_to_write, len;
+ const struct hdcp2_dp_msg_data *hdcp2_msg_data;
+
+ hdcp2_msg_data = get_hdcp2_dp_msg_data(*byte);
+ if (!hdcp2_msg_data)
+ return -EINVAL;
+
+ offset = hdcp2_msg_data->offset;
+
+ /* No msg_id in DP HDCP2.2 msgs */
+ bytes_to_write = size - 1;
+ byte++;
+
+ hdcp->cp_irq_count_cached = atomic_read(&hdcp->cp_irq_count);
+
+ while (bytes_to_write) {
+ len = bytes_to_write > DP_AUX_MAX_PAYLOAD_BYTES ?
+ DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_write;
+
+ ret = drm_dp_dpcd_write(&dig_port->dp.aux,
+ offset, (void *)byte, len);
+ if (ret < 0)
+ return ret;
+
+ bytes_to_write -= ret;
+ byte += ret;
+ offset += ret;
+ }
+
+ return size;
+}
+
+static
+ssize_t get_receiver_id_list_size(struct intel_digital_port *dig_port)
+{
+ u8 rx_info[HDCP_2_2_RXINFO_LEN];
+ u32 dev_cnt;
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RXINFO_OFFSET,
+ (void *)rx_info, HDCP_2_2_RXINFO_LEN);
+ if (ret != HDCP_2_2_RXINFO_LEN)
+ return ret >= 0 ? -EIO : ret;
+
+ dev_cnt = (HDCP_2_2_DEV_COUNT_HI(rx_info[0]) << 4 |
+ HDCP_2_2_DEV_COUNT_LO(rx_info[1]));
+
+ if (dev_cnt > HDCP_2_2_MAX_DEVICE_COUNT)
+ dev_cnt = HDCP_2_2_MAX_DEVICE_COUNT;
+
+ ret = sizeof(struct hdcp2_rep_send_receiverid_list) -
+ HDCP_2_2_RECEIVER_IDS_MAX_LEN +
+ (dev_cnt * HDCP_2_2_RECEIVER_ID_LEN);
+
+ return ret;
+}
+
+static
+int intel_dp_hdcp2_read_msg(struct intel_digital_port *dig_port,
+ u8 msg_id, void *buf, size_t size)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ unsigned int offset;
+ u8 *byte = buf;
+ ssize_t ret, bytes_to_recv, len;
+ const struct hdcp2_dp_msg_data *hdcp2_msg_data;
+
+ hdcp2_msg_data = get_hdcp2_dp_msg_data(msg_id);
+ if (!hdcp2_msg_data)
+ return -EINVAL;
+ offset = hdcp2_msg_data->offset;
+
+ ret = intel_dp_hdcp2_wait_for_msg(dig_port, hdcp2_msg_data);
+ if (ret < 0)
+ return ret;
+
+ if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST) {
+ ret = get_receiver_id_list_size(dig_port);
+ if (ret < 0)
+ return ret;
+
+ size = ret;
+ }
+ bytes_to_recv = size - 1;
+
+ /* DP adaptation msgs has no msg_id */
+ byte++;
+
+ while (bytes_to_recv) {
+ len = bytes_to_recv > DP_AUX_MAX_PAYLOAD_BYTES ?
+ DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_recv;
+
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux, offset,
+ (void *)byte, len);
+ if (ret < 0) {
+ drm_dbg_kms(&i915->drm, "msg_id %d, ret %zd\n",
+ msg_id, ret);
+ return ret;
+ }
+
+ bytes_to_recv -= ret;
+ byte += ret;
+ offset += ret;
+ }
+ byte = buf;
+ *byte = msg_id;
+
+ return size;
+}
+
+static
+int intel_dp_hdcp2_config_stream_type(struct intel_digital_port *dig_port,
+ bool is_repeater, u8 content_type)
+{
+ int ret;
+ struct hdcp2_dp_errata_stream_type stream_type_msg;
+
+ if (is_repeater)
+ return 0;
+
+ /*
+ * Errata for DP: As Stream type is used for encryption, Receiver
+ * should be communicated with stream type for the decryption of the
+ * content.
+ * Repeater will be communicated with stream type as a part of it's
+ * auth later in time.
+ */
+ stream_type_msg.msg_id = HDCP_2_2_ERRATA_DP_STREAM_TYPE;
+ stream_type_msg.stream_type = content_type;
+
+ ret = intel_dp_hdcp2_write_msg(dig_port, &stream_type_msg,
+ sizeof(stream_type_msg));
+
+ return ret < 0 ? ret : 0;
+
+}
+
+static
+int intel_dp_hdcp2_check_link(struct intel_digital_port *dig_port)
+{
+ u8 rx_status;
+ int ret;
+
+ ret = intel_dp_hdcp2_read_rx_status(dig_port, &rx_status);
+ if (ret)
+ return ret;
+
+ if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status))
+ ret = HDCP_REAUTH_REQUEST;
+ else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status))
+ ret = HDCP_LINK_INTEGRITY_FAILURE;
+ else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
+ ret = HDCP_TOPOLOGY_CHANGE;
+
+ return ret;
+}
+
+static
+int intel_dp_hdcp2_capable(struct intel_digital_port *dig_port,
+ bool *capable)
+{
+ u8 rx_caps[3];
+ int ret;
+
+ *capable = false;
+ ret = drm_dp_dpcd_read(&dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RX_CAPS_OFFSET,
+ rx_caps, HDCP_2_2_RXCAPS_LEN);
+ if (ret != HDCP_2_2_RXCAPS_LEN)
+ return ret >= 0 ? -EIO : ret;
+
+ if (rx_caps[0] == HDCP_2_2_RX_CAPS_VERSION_VAL &&
+ HDCP_2_2_DP_HDCP_CAPABLE(rx_caps[2]))
+ *capable = true;
+
+ return 0;
+}
+
+static const struct intel_hdcp_shim intel_dp_hdcp_shim = {
+ .write_an_aksv = intel_dp_hdcp_write_an_aksv,
+ .read_bksv = intel_dp_hdcp_read_bksv,
+ .read_bstatus = intel_dp_hdcp_read_bstatus,
+ .repeater_present = intel_dp_hdcp_repeater_present,
+ .read_ri_prime = intel_dp_hdcp_read_ri_prime,
+ .read_ksv_ready = intel_dp_hdcp_read_ksv_ready,
+ .read_ksv_fifo = intel_dp_hdcp_read_ksv_fifo,
+ .read_v_prime_part = intel_dp_hdcp_read_v_prime_part,
+ .toggle_signalling = intel_dp_hdcp_toggle_signalling,
+ .check_link = intel_dp_hdcp_check_link,
+ .hdcp_capable = intel_dp_hdcp_capable,
+ .write_2_2_msg = intel_dp_hdcp2_write_msg,
+ .read_2_2_msg = intel_dp_hdcp2_read_msg,
+ .config_stream_type = intel_dp_hdcp2_config_stream_type,
+ .check_2_2_link = intel_dp_hdcp2_check_link,
+ .hdcp_2_2_capable = intel_dp_hdcp2_capable,
+ .protocol = HDCP_PROTOCOL_DP,
+};
+
+static int
+intel_dp_mst_hdcp_toggle_signalling(struct intel_digital_port *dig_port,
+ enum transcoder cpu_transcoder,
+ bool enable)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ int ret;
+
+ if (!enable)
+ usleep_range(6, 60); /* Bspec says >= 6us */
+
+ ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base,
+ cpu_transcoder, enable);
+ if (ret)
+ drm_dbg_kms(&i915->drm, "%s HDCP signalling failed (%d)\n",
+ enable ? "Enable" : "Disable", ret);
+ return ret;
+}
+
+static
+bool intel_dp_mst_hdcp_check_link(struct intel_digital_port *dig_port,
+ struct intel_connector *connector)
+{
+ struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+ struct intel_dp *intel_dp = &dig_port->dp;
+ struct drm_dp_query_stream_enc_status_ack_reply reply;
+ int ret;
+
+ if (!intel_dp_hdcp_check_link(dig_port, connector))
+ return false;
+
+ ret = drm_dp_send_query_stream_enc_status(&intel_dp->mst_mgr,
+ connector->port, &reply);
+ if (ret) {
+ drm_dbg_kms(&i915->drm,
+ "[CONNECTOR:%d:%s] failed QSES ret=%d\n",
+ connector->base.base.id, connector->base.name, ret);
+ return false;
+ }
+
+ return reply.auth_completed && reply.encryption_enabled;
+}
+
+static const struct intel_hdcp_shim intel_dp_mst_hdcp_shim = {
+ .write_an_aksv = intel_dp_hdcp_write_an_aksv,
+ .read_bksv = intel_dp_hdcp_read_bksv,
+ .read_bstatus = intel_dp_hdcp_read_bstatus,
+ .repeater_present = intel_dp_hdcp_repeater_present,
+ .read_ri_prime = intel_dp_hdcp_read_ri_prime,
+ .read_ksv_ready = intel_dp_hdcp_read_ksv_ready,
+ .read_ksv_fifo = intel_dp_hdcp_read_ksv_fifo,
+ .read_v_prime_part = intel_dp_hdcp_read_v_prime_part,
+ .toggle_signalling = intel_dp_mst_hdcp_toggle_signalling,
+ .check_link = intel_dp_mst_hdcp_check_link,
+ .hdcp_capable = intel_dp_hdcp_capable,
+
+ .protocol = HDCP_PROTOCOL_DP,
+};
+
+int intel_dp_init_hdcp(struct intel_digital_port *dig_port,
+ struct intel_connector *intel_connector)
+{
+ struct drm_device *dev = intel_connector->base.dev;
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ struct intel_encoder *intel_encoder = &dig_port->base;
+ enum port port = intel_encoder->port;
+ struct intel_dp *intel_dp = &dig_port->dp;
+
+ if (!is_hdcp_supported(dev_priv, port))
+ return 0;
+
+ if (intel_connector->mst_port)
+ return intel_hdcp_init(intel_connector, port,
+ &intel_dp_mst_hdcp_shim);
+ else if (!intel_dp_is_edp(intel_dp))
+ return intel_hdcp_init(intel_connector, port,
+ &intel_dp_hdcp_shim);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index a2d91a499700..64d885539e94 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -37,6 +37,7 @@
#include "intel_dp.h"
#include "intel_dp_mst.h"
#include "intel_dpio_phy.h"
+#include "intel_hdcp.h"
static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
@@ -352,6 +353,8 @@ static void intel_mst_disable_dp(struct intel_atomic_state *state,
drm_dbg_kms(&i915->drm, "active links %d\n",
intel_dp->active_mst_links);
+ intel_hdcp_disable(intel_mst->connector);
+
drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, connector->port);
ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
@@ -556,6 +559,13 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state,
if (pipe_config->has_audio)
intel_audio_codec_enable(encoder, pipe_config, conn_state);
+
+ /* Enable hdcp if it's desired */
+ if (conn_state->content_protection ==
+ DRM_MODE_CONTENT_PROTECTION_DESIRED)
+ intel_hdcp_enable(to_intel_connector(conn_state->connector),
+ pipe_config->cpu_transcoder,
+ (u8)conn_state->hdcp_content_type);
}
static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
@@ -709,9 +719,13 @@ static int
intel_dp_mst_detect(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx, bool force)
{
+ struct drm_i915_private *i915 = to_i915(connector->dev);
struct intel_connector *intel_connector = to_intel_connector(connector);
struct intel_dp *intel_dp = intel_connector->mst_port;
+ if (!INTEL_DISPLAY_ENABLED(i915))
+ return connector_status_disconnected;
+
if (drm_connector_is_unregistered(connector))
return connector_status_disconnected;
@@ -799,6 +813,14 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo
intel_attach_force_audio_property(connector);
intel_attach_broadcast_rgb_property(connector);
+
+ /* TODO: Figure out how to make HDCP work on GEN12+ */
+ if (INTEL_GEN(dev_priv) < 12) {
+ ret = intel_dp_init_hdcp(dig_port, intel_connector);
+ if (ret)
+ DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
+ }
+
/*
* Reuse the prop from the SST connector because we're
* not allowed to create new props after device registration.
@@ -865,6 +887,7 @@ intel_dp_create_fake_mst_encoder(struct intel_digital_port *dig_port, enum pipe
intel_encoder->compute_config_late = intel_dp_mst_compute_config_late;
intel_encoder->disable = intel_mst_disable_dp;
intel_encoder->post_disable = intel_mst_post_disable_dp;
+ intel_encoder->update_pipe = intel_ddi_update_pipe;
intel_encoder->pre_pll_enable = intel_mst_pre_pll_enable_dp;
intel_encoder->pre_enable = intel_mst_pre_enable_dp;
intel_encoder->enable = intel_mst_enable_dp;
diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
index c9013f8f766f..e08684e34078 100644
--- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
+++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
@@ -147,6 +147,18 @@ void assert_shared_dpll(struct drm_i915_private *dev_priv,
pll->info->name, onoff(state), onoff(cur_state));
}
+static i915_reg_t
+intel_combo_pll_enable_reg(struct drm_i915_private *i915,
+ struct intel_shared_dpll *pll)
+{
+
+ if (IS_ELKHARTLAKE(i915) && (pll->info->id == DPLL_ID_EHL_DPLL4))
+ return MG_PLL_ENABLE(0);
+
+ return CNL_DPLL_ENABLE(pll->info->id);
+
+
+}
/**
* intel_prepare_shared_dpll - call a dpll's prepare hook
* @crtc_state: CRTC, and its state, which has a shared dpll
@@ -3842,12 +3854,7 @@ static bool combo_pll_get_hw_state(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll,
struct intel_dpll_hw_state *hw_state)
{
- i915_reg_t enable_reg = CNL_DPLL_ENABLE(pll->info->id);
-
- if (IS_ELKHARTLAKE(dev_priv) &&
- pll->info->id == DPLL_ID_EHL_DPLL4) {
- enable_reg = MG_PLL_ENABLE(0);
- }
+ i915_reg_t enable_reg = intel_combo_pll_enable_reg(dev_priv, pll);
return icl_pll_get_hw_state(dev_priv, pll, hw_state, enable_reg);
}
@@ -4045,11 +4052,10 @@ static void icl_pll_enable(struct drm_i915_private *dev_priv,
static void combo_pll_enable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
- i915_reg_t enable_reg = CNL_DPLL_ENABLE(pll->info->id);
+ i915_reg_t enable_reg = intel_combo_pll_enable_reg(dev_priv, pll);
if (IS_ELKHARTLAKE(dev_priv) &&
pll->info->id == DPLL_ID_EHL_DPLL4) {
- enable_reg = MG_PLL_ENABLE(0);
/*
* We need to disable DC states when this DPLL is enabled.
@@ -4157,19 +4163,14 @@ static void icl_pll_disable(struct drm_i915_private *dev_priv,
static void combo_pll_disable(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll)
{
- i915_reg_t enable_reg = CNL_DPLL_ENABLE(pll->info->id);
+ i915_reg_t enable_reg = intel_combo_pll_enable_reg(dev_priv, pll);
- if (IS_ELKHARTLAKE(dev_priv) &&
- pll->info->id == DPLL_ID_EHL_DPLL4) {
- enable_reg = MG_PLL_ENABLE(0);
- icl_pll_disable(dev_priv, pll, enable_reg);
+ icl_pll_disable(dev_priv, pll, enable_reg);
+ if (IS_ELKHARTLAKE(dev_priv) &&
+ pll->info->id == DPLL_ID_EHL_DPLL4)
intel_display_power_put(dev_priv, POWER_DOMAIN_DPLL_DC_OFF,
pll->wakeref);
- return;
- }
-
- icl_pll_disable(dev_priv, pll, enable_reg);
}
static void tbt_pll_disable(struct drm_i915_private *dev_priv,
diff --git a/drivers/gpu/drm/i915/display/intel_dvo.c b/drivers/gpu/drm/i915/display/intel_dvo.c
index 307ed8ae9a19..237dbb1ba0ee 100644
--- a/drivers/gpu/drm/i915/display/intel_dvo.c
+++ b/drivers/gpu/drm/i915/display/intel_dvo.c
@@ -313,9 +313,15 @@ static void intel_dvo_pre_enable(struct intel_atomic_state *state,
static enum drm_connector_status
intel_dvo_detect(struct drm_connector *connector, bool force)
{
+ struct drm_i915_private *i915 = to_i915(connector->dev);
struct intel_dvo *intel_dvo = intel_attached_dvo(to_intel_connector(connector));
+
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id, connector->name);
+
+ if (!INTEL_DISPLAY_ENABLED(i915))
+ return connector_status_disconnected;
+
return intel_dvo->dev.dev_ops->detect(&intel_dvo->dev);
}
diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c
index bd39eb6a21b8..842c04e63214 100644
--- a/drivers/gpu/drm/i915/display/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/display/intel_fbdev.c
@@ -451,8 +451,7 @@ int intel_fbdev_init(struct drm_device *dev)
struct intel_fbdev *ifbdev;
int ret;
- if (drm_WARN_ON(dev, !HAS_DISPLAY(dev_priv) ||
- !INTEL_DISPLAY_ENABLED(dev_priv)))
+ if (drm_WARN_ON(dev, !HAS_DISPLAY(dev_priv)))
return -ENODEV;
ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL);
diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c
index a8d119b6b45c..e6b8d6dfb598 100644
--- a/drivers/gpu/drm/i915/display/intel_gmbus.c
+++ b/drivers/gpu/drm/i915/display/intel_gmbus.c
@@ -834,7 +834,7 @@ int intel_gmbus_setup(struct drm_i915_private *dev_priv)
unsigned int pin;
int ret;
- if (!HAS_DISPLAY(dev_priv) || !INTEL_DISPLAY_ENABLED(dev_priv))
+ if (!HAS_DISPLAY(dev_priv))
return 0;
if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c
index 1a0d49af2a08..5492076d1ae0 100644
--- a/drivers/gpu/drm/i915/display/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/display/intel_hdcp.c
@@ -148,9 +148,8 @@ static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *dig_port,
static bool hdcp_key_loadable(struct drm_i915_private *dev_priv)
{
- struct i915_power_domains *power_domains = &dev_priv->power_domains;
- struct i915_power_well *power_well;
enum i915_power_well_id id;
+ intel_wakeref_t wakeref;
bool enabled = false;
/*
@@ -162,17 +161,9 @@ static bool hdcp_key_loadable(struct drm_i915_private *dev_priv)
else
id = SKL_DISP_PW_1;
- mutex_lock(&power_domains->lock);
-
/* PG1 (power well #1) needs to be enabled */
- for_each_power_well(dev_priv, power_well) {
- if (power_well->desc->id == id) {
- enabled = power_well->desc->ops->is_enabled(dev_priv,
- power_well);
- break;
- }
- }
- mutex_unlock(&power_domains->lock);
+ with_intel_runtime_pm(&dev_priv->runtime_pm, wakeref)
+ enabled = intel_display_power_well_is_enabled(dev_priv, id);
/*
* Another req for hdcp key loadability is enabled state of pll for
@@ -713,7 +704,7 @@ static int intel_hdcp_auth(struct intel_connector *connector)
intel_de_write(dev_priv, HDCP_REP_CTL,
intel_hdcp_get_repeater_ctl(dev_priv, cpu_transcoder, port));
- ret = shim->toggle_signalling(dig_port, true);
+ ret = shim->toggle_signalling(dig_port, cpu_transcoder, true);
if (ret)
return ret;
@@ -801,6 +792,19 @@ static int _intel_hdcp_disable(struct intel_connector *connector)
drm_dbg_kms(&dev_priv->drm, "[%s:%d] HDCP is being disabled...\n",
connector->base.name, connector->base.base.id);
+ /*
+ * If there are other connectors on this port using HDCP, don't disable
+ * it. Instead, toggle the HDCP signalling off on that particular
+ * connector/pipe and exit.
+ */
+ if (dig_port->num_hdcp_streams > 0) {
+ ret = hdcp->shim->toggle_signalling(dig_port,
+ cpu_transcoder, false);
+ if (ret)
+ DRM_ERROR("Failed to disable HDCP signalling\n");
+ return ret;
+ }
+
hdcp->hdcp_encrypted = false;
intel_de_write(dev_priv, HDCP_CONF(dev_priv, cpu_transcoder, port), 0);
if (intel_de_wait_for_clear(dev_priv,
@@ -816,7 +820,7 @@ static int _intel_hdcp_disable(struct intel_connector *connector)
intel_de_write(dev_priv, HDCP_REP_CTL,
intel_de_read(dev_priv, HDCP_REP_CTL) & ~repeater_ctl);
- ret = hdcp->shim->toggle_signalling(dig_port, false);
+ ret = hdcp->shim->toggle_signalling(dig_port, cpu_transcoder, false);
if (ret) {
drm_err(&dev_priv->drm, "Failed to disable HDCP signalling\n");
return ret;
@@ -876,6 +880,34 @@ static struct intel_connector *intel_hdcp_to_connector(struct intel_hdcp *hdcp)
return container_of(hdcp, struct intel_connector, hdcp);
}
+static void intel_hdcp_update_value(struct intel_connector *connector,
+ u64 value, bool update_property)
+{
+ struct drm_device *dev = connector->base.dev;
+ struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+
+ drm_WARN_ON(connector->base.dev, !mutex_is_locked(&hdcp->mutex));
+
+ if (hdcp->value == value)
+ return;
+
+ drm_WARN_ON(dev, !mutex_is_locked(&dig_port->hdcp_mutex));
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+ if (!drm_WARN_ON(dev, dig_port->num_hdcp_streams == 0))
+ dig_port->num_hdcp_streams--;
+ } else if (value == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+ dig_port->num_hdcp_streams++;
+ }
+
+ hdcp->value = value;
+ if (update_property) {
+ drm_connector_get(&connector->base);
+ schedule_work(&hdcp->prop_work);
+ }
+}
+
/* Implements Part 3 of the HDCP authorization procedure */
static int intel_hdcp_check_link(struct intel_connector *connector)
{
@@ -887,6 +919,8 @@ static int intel_hdcp_check_link(struct intel_connector *connector)
int ret = 0;
mutex_lock(&hdcp->mutex);
+ mutex_lock(&dig_port->hdcp_mutex);
+
cpu_transcoder = hdcp->cpu_transcoder;
/* Check_link valid only when HDCP1.4 is enabled */
@@ -903,15 +937,16 @@ static int intel_hdcp_check_link(struct intel_connector *connector)
connector->base.name, connector->base.base.id,
intel_de_read(dev_priv, HDCP_STATUS(dev_priv, cpu_transcoder, port)));
ret = -ENXIO;
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_DESIRED,
+ true);
goto out;
}
- if (hdcp->shim->check_link(dig_port)) {
+ if (hdcp->shim->check_link(dig_port, connector)) {
if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_ENABLED, true);
}
goto out;
}
@@ -923,20 +958,23 @@ static int intel_hdcp_check_link(struct intel_connector *connector)
ret = _intel_hdcp_disable(connector);
if (ret) {
drm_err(&dev_priv->drm, "Failed to disable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_DESIRED,
+ true);
goto out;
}
ret = _intel_hdcp_enable(connector);
if (ret) {
drm_err(&dev_priv->drm, "Failed to enable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_DESIRED,
+ true);
goto out;
}
out:
+ mutex_unlock(&dig_port->hdcp_mutex);
mutex_unlock(&hdcp->mutex);
return ret;
}
@@ -962,6 +1000,8 @@ static void intel_hdcp_prop_work(struct work_struct *work)
mutex_unlock(&hdcp->mutex);
drm_modeset_unlock(&dev_priv->drm.mode_config.connection_mutex);
+
+ drm_connector_put(&connector->base);
}
bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
@@ -1600,7 +1640,8 @@ static int hdcp2_enable_encryption(struct intel_connector *connector)
intel_de_read(dev_priv, HDCP2_STATUS(dev_priv, cpu_transcoder, port)) &
LINK_ENCRYPTION_STATUS);
if (hdcp->shim->toggle_signalling) {
- ret = hdcp->shim->toggle_signalling(dig_port, true);
+ ret = hdcp->shim->toggle_signalling(dig_port, cpu_transcoder,
+ true);
if (ret) {
drm_err(&dev_priv->drm,
"Failed to enable HDCP signalling. %d\n",
@@ -1650,7 +1691,8 @@ static int hdcp2_disable_encryption(struct intel_connector *connector)
drm_dbg_kms(&dev_priv->drm, "Disable Encryption Timedout");
if (hdcp->shim->toggle_signalling) {
- ret = hdcp->shim->toggle_signalling(dig_port, false);
+ ret = hdcp->shim->toggle_signalling(dig_port, cpu_transcoder,
+ false);
if (ret) {
drm_err(&dev_priv->drm,
"Failed to disable HDCP signalling. %d\n",
@@ -1766,16 +1808,18 @@ static int intel_hdcp2_check_link(struct intel_connector *connector)
"HDCP2.2 link stopped the encryption, %x\n",
intel_de_read(dev_priv, HDCP2_STATUS(dev_priv, cpu_transcoder, port)));
ret = -ENXIO;
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_DESIRED,
+ true);
goto out;
}
ret = hdcp->shim->check_2_2_link(dig_port);
if (ret == HDCP_LINK_PROTECTED) {
if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_ENABLED,
+ true);
}
goto out;
}
@@ -1788,8 +1832,9 @@ static int intel_hdcp2_check_link(struct intel_connector *connector)
"HDCP2.2 Downstream topology change\n");
ret = hdcp2_authenticate_repeater_topology(connector);
if (!ret) {
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_ENABLED,
+ true);
goto out;
}
drm_dbg_kms(&dev_priv->drm,
@@ -1807,8 +1852,8 @@ static int intel_hdcp2_check_link(struct intel_connector *connector)
drm_err(&dev_priv->drm,
"[%s:%d] Failed to disable hdcp2.2 (%d)\n",
connector->base.name, connector->base.base.id, ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_DESIRED, true);
goto out;
}
@@ -1818,8 +1863,9 @@ static int intel_hdcp2_check_link(struct intel_connector *connector)
"[%s:%d] Failed to enable hdcp2.2 (%d)\n",
connector->base.name, connector->base.base.id,
ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_DESIRED,
+ true);
goto out;
}
@@ -1835,6 +1881,9 @@ static void intel_hdcp_check_work(struct work_struct *work)
check_work);
struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
+ if (drm_connector_is_unregistered(&connector->base))
+ return;
+
if (!intel_hdcp2_check_link(connector))
schedule_delayed_work(&hdcp->check_work,
DRM_HDCP2_CHECK_PERIOD_MS);
@@ -1896,6 +1945,7 @@ static enum mei_fw_tc intel_get_mei_fw_tc(enum transcoder cpu_transcoder)
}
static int initialize_hdcp_port_data(struct intel_connector *connector,
+ enum port port,
const struct intel_hdcp_shim *shim)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
@@ -1903,8 +1953,7 @@ static int initialize_hdcp_port_data(struct intel_connector *connector,
struct hdcp_port_data *data = &hdcp->port_data;
if (INTEL_GEN(dev_priv) < 12)
- data->fw_ddi =
- intel_get_mei_fw_ddi_index(intel_attached_encoder(connector)->port);
+ data->fw_ddi = intel_get_mei_fw_ddi_index(port);
else
/*
* As per ME FW API expectation, for GEN 12+, fw_ddi is filled
@@ -1974,14 +2023,14 @@ void intel_hdcp_component_init(struct drm_i915_private *dev_priv)
}
}
-static void intel_hdcp2_init(struct intel_connector *connector,
+static void intel_hdcp2_init(struct intel_connector *connector, enum port port,
const struct intel_hdcp_shim *shim)
{
struct drm_i915_private *i915 = to_i915(connector->base.dev);
struct intel_hdcp *hdcp = &connector->hdcp;
int ret;
- ret = initialize_hdcp_port_data(connector, shim);
+ ret = initialize_hdcp_port_data(connector, port, shim);
if (ret) {
drm_dbg_kms(&i915->drm, "Mei hdcp data init failed\n");
return;
@@ -1991,6 +2040,7 @@ static void intel_hdcp2_init(struct intel_connector *connector,
}
int intel_hdcp_init(struct intel_connector *connector,
+ enum port port,
const struct intel_hdcp_shim *shim)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
@@ -2000,8 +2050,8 @@ int intel_hdcp_init(struct intel_connector *connector,
if (!shim)
return -EINVAL;
- if (is_hdcp2_supported(dev_priv))
- intel_hdcp2_init(connector, shim);
+ if (is_hdcp2_supported(dev_priv) && !connector->mst_port)
+ intel_hdcp2_init(connector, port, shim);
ret =
drm_connector_attach_content_protection_property(&connector->base,
@@ -2025,6 +2075,7 @@ int intel_hdcp_enable(struct intel_connector *connector,
enum transcoder cpu_transcoder, u8 content_type)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct intel_hdcp *hdcp = &connector->hdcp;
unsigned long check_link_interval = DRM_HDCP_CHECK_PERIOD_MS;
int ret = -EINVAL;
@@ -2033,14 +2084,14 @@ int intel_hdcp_enable(struct intel_connector *connector,
return -ENOENT;
mutex_lock(&hdcp->mutex);
+ mutex_lock(&dig_port->hdcp_mutex);
drm_WARN_ON(&dev_priv->drm,
hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED);
hdcp->content_type = content_type;
+ hdcp->cpu_transcoder = cpu_transcoder;
- if (INTEL_GEN(dev_priv) >= 12) {
- hdcp->cpu_transcoder = cpu_transcoder;
+ if (INTEL_GEN(dev_priv) >= 12)
hdcp->port_data.fw_tc = intel_get_mei_fw_tc(cpu_transcoder);
- }
/*
* Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
@@ -2063,16 +2114,19 @@ int intel_hdcp_enable(struct intel_connector *connector,
if (!ret) {
schedule_delayed_work(&hdcp->check_work, check_link_interval);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_ENABLED,
+ true);
}
+ mutex_unlock(&dig_port->hdcp_mutex);
mutex_unlock(&hdcp->mutex);
return ret;
}
int intel_hdcp_disable(struct intel_connector *connector)
{
+ struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
struct intel_hdcp *hdcp = &connector->hdcp;
int ret = 0;
@@ -2080,15 +2134,20 @@ int intel_hdcp_disable(struct intel_connector *connector)
return -ENOENT;
mutex_lock(&hdcp->mutex);
+ mutex_lock(&dig_port->hdcp_mutex);
- if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
- if (hdcp->hdcp2_encrypted)
- ret = _intel_hdcp2_disable(connector);
- else if (hdcp->hdcp_encrypted)
- ret = _intel_hdcp_disable(connector);
- }
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_UNDESIRED, false);
+ if (hdcp->hdcp2_encrypted)
+ ret = _intel_hdcp2_disable(connector);
+ else if (hdcp->hdcp_encrypted)
+ ret = _intel_hdcp_disable(connector);
+out:
+ mutex_unlock(&dig_port->hdcp_mutex);
mutex_unlock(&hdcp->mutex);
cancel_delayed_work_sync(&hdcp->check_work);
return ret;
@@ -2102,11 +2161,15 @@ void intel_hdcp_update_pipe(struct intel_atomic_state *state,
struct intel_connector *connector =
to_intel_connector(conn_state->connector);
struct intel_hdcp *hdcp = &connector->hdcp;
- bool content_protection_type_changed =
+ bool content_protection_type_changed, desired_and_not_enabled = false;
+
+ if (!connector->hdcp.shim)
+ return;
+
+ content_protection_type_changed =
(conn_state->hdcp_content_type != hdcp->content_type &&
conn_state->content_protection !=
DRM_MODE_CONTENT_PROTECTION_UNDESIRED);
- bool desired_and_not_enabled = false;
/*
* During the HDCP encryption session if Type change is requested,
@@ -2159,12 +2222,39 @@ void intel_hdcp_component_fini(struct drm_i915_private *dev_priv)
void intel_hdcp_cleanup(struct intel_connector *connector)
{
- if (!connector->hdcp.shim)
+ struct intel_hdcp *hdcp = &connector->hdcp;
+
+ if (!hdcp->shim)
return;
- mutex_lock(&connector->hdcp.mutex);
- kfree(connector->hdcp.port_data.streams);
- mutex_unlock(&connector->hdcp.mutex);
+ /*
+ * If the connector is registered, it's possible userspace could kick
+ * off another HDCP enable, which would re-spawn the workers.
+ */
+ drm_WARN_ON(connector->base.dev,
+ connector->base.registration_state == DRM_CONNECTOR_REGISTERED);
+
+ /*
+ * Now that the connector is not registered, check_work won't be run,
+ * but cancel any outstanding instances of it
+ */
+ cancel_delayed_work_sync(&hdcp->check_work);
+
+ /*
+ * We don't cancel prop_work in the same way as check_work since it
+ * requires connection_mutex which could be held while calling this
+ * function. Instead, we rely on the connector references grabbed before
+ * scheduling prop_work to ensure the connector is alive when prop_work
+ * is run. So if we're in the destroy path (which is where this
+ * function should be called), we're "guaranteed" that prop_work is not
+ * active (tl;dr This Should Never Happen).
+ */
+ drm_WARN_ON(connector->base.dev, work_pending(&hdcp->prop_work));
+
+ mutex_lock(&hdcp->mutex);
+ kfree(hdcp->port_data.streams);
+ hdcp->shim = NULL;
+ mutex_unlock(&hdcp->mutex);
}
void intel_hdcp_atomic_check(struct drm_connector *connector,
diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.h b/drivers/gpu/drm/i915/display/intel_hdcp.h
index 86bbaec120cc..1bbf5b67ed0a 100644
--- a/drivers/gpu/drm/i915/display/intel_hdcp.h
+++ b/drivers/gpu/drm/i915/display/intel_hdcp.h
@@ -22,7 +22,7 @@ enum transcoder;
void intel_hdcp_atomic_check(struct drm_connector *connector,
struct drm_connector_state *old_state,
struct drm_connector_state *new_state);
-int intel_hdcp_init(struct intel_connector *connector,
+int intel_hdcp_init(struct intel_connector *connector, enum port port,
const struct intel_hdcp_shim *hdcp_shim);
int intel_hdcp_enable(struct intel_connector *connector,
enum transcoder cpu_transcoder, u8 content_type);
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c
index de2ce5632b94..3f2008d845c2 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
@@ -1477,7 +1477,8 @@ int intel_hdmi_hdcp_read_v_prime_part(struct intel_digital_port *dig_port,
return ret;
}
-static int kbl_repositioning_enc_en_signal(struct intel_connector *connector)
+static int kbl_repositioning_enc_en_signal(struct intel_connector *connector,
+ enum transcoder cpu_transcoder)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
@@ -1494,13 +1495,15 @@ static int kbl_repositioning_enc_en_signal(struct intel_connector *connector)
usleep_range(25, 50);
}
- ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, false);
+ ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, cpu_transcoder,
+ false);
if (ret) {
drm_err(&dev_priv->drm,
"Disable HDCP signalling failed (%d)\n", ret);
return ret;
}
- ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, true);
+ ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, cpu_transcoder,
+ true);
if (ret) {
drm_err(&dev_priv->drm,
"Enable HDCP signalling failed (%d)\n", ret);
@@ -1512,6 +1515,7 @@ static int kbl_repositioning_enc_en_signal(struct intel_connector *connector)
static
int intel_hdmi_hdcp_toggle_signalling(struct intel_digital_port *dig_port,
+ enum transcoder cpu_transcoder,
bool enable)
{
struct intel_hdmi *hdmi = &dig_port->hdmi;
@@ -1522,7 +1526,8 @@ int intel_hdmi_hdcp_toggle_signalling(struct intel_digital_port *dig_port,
if (!enable)
usleep_range(6, 60); /* Bspec says >= 6us */
- ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, enable);
+ ret = intel_ddi_toggle_hdcp_signalling(&dig_port->base, cpu_transcoder,
+ enable);
if (ret) {
drm_err(&dev_priv->drm, "%s HDCP signalling failed (%d)\n",
enable ? "Enable" : "Disable", ret);
@@ -1534,17 +1539,17 @@ int intel_hdmi_hdcp_toggle_signalling(struct intel_digital_port *dig_port,
* opportunity and enc_en signalling in KABYLAKE.
*/
if (IS_KABYLAKE(dev_priv) && enable)
- return kbl_repositioning_enc_en_signal(connector);
+ return kbl_repositioning_enc_en_signal(connector,
+ cpu_transcoder);
return 0;
}
static
-bool intel_hdmi_hdcp_check_link_once(struct intel_digital_port *dig_port)
+bool intel_hdmi_hdcp_check_link_once(struct intel_digital_port *dig_port,
+ struct intel_connector *connector)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- struct intel_connector *connector =
- dig_port->hdmi.attached_connector;
enum port port = dig_port->base.port;
enum transcoder cpu_transcoder = connector->hdcp.cpu_transcoder;
int ret;
@@ -1572,13 +1577,14 @@ bool intel_hdmi_hdcp_check_link_once(struct intel_digital_port *dig_port)
}
static
-bool intel_hdmi_hdcp_check_link(struct intel_digital_port *dig_port)
+bool intel_hdmi_hdcp_check_link(struct intel_digital_port *dig_port,
+ struct intel_connector *connector)
{
struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
int retry;
for (retry = 0; retry < 3; retry++)
- if (intel_hdmi_hdcp_check_link_once(dig_port))
+ if (intel_hdmi_hdcp_check_link_once(dig_port, connector))
return true;
drm_err(&i915->drm, "Link check failed\n");
@@ -2271,35 +2277,18 @@ intel_hdmi_mode_valid(struct drm_connector *connector,
return intel_mode_valid_max_plane_size(dev_priv, mode);
}
-static bool hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
- int bpc)
+bool intel_hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
+ int bpc, bool has_hdmi_sink, bool ycbcr420_output)
{
- struct drm_i915_private *dev_priv =
- to_i915(crtc_state->uapi.crtc->dev);
struct drm_atomic_state *state = crtc_state->uapi.state;
struct drm_connector_state *connector_state;
struct drm_connector *connector;
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
int i;
- if (HAS_GMCH(dev_priv))
- return false;
-
- if (bpc == 10 && INTEL_GEN(dev_priv) < 11)
- return false;
-
if (crtc_state->pipe_bpp < bpc * 3)
return false;
- if (!crtc_state->has_hdmi_sink)
- return false;
-
- /*
- * HDMI deep color affects the clocks, so it's only possible
- * when not cloning with other encoder types.
- */
- if (crtc_state->output_types != 1 << INTEL_OUTPUT_HDMI)
+ if (!has_hdmi_sink)
return false;
for_each_new_connector_in_state(state, connector, connector_state, i) {
@@ -2308,7 +2297,7 @@ static bool hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
if (connector_state->crtc != crtc_state->uapi.crtc)
continue;
- if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420) {
+ if (ycbcr420_output) {
const struct drm_hdmi_info *hdmi = &info->hdmi;
if (bpc == 12 && !(hdmi->y420_dc_modes &
@@ -2327,6 +2316,30 @@ static bool hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
}
}
+ return true;
+}
+
+static bool hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
+ int bpc)
+{
+ struct drm_i915_private *dev_priv =
+ to_i915(crtc_state->uapi.crtc->dev);
+ const struct drm_display_mode *adjusted_mode =
+ &crtc_state->hw.adjusted_mode;
+
+ if (HAS_GMCH(dev_priv))
+ return false;
+
+ if (bpc == 10 && INTEL_GEN(dev_priv) < 11)
+ return false;
+
+ /*
+ * HDMI deep color affects the clocks, so it's only possible
+ * when not cloning with other encoder types.
+ */
+ if (crtc_state->output_types != BIT(INTEL_OUTPUT_HDMI))
+ return false;
+
/* Display Wa_1405510057:icl,ehl */
if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420 &&
bpc == 10 && IS_GEN(dev_priv, 11) &&
@@ -2334,7 +2347,10 @@ static bool hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state,
adjusted_mode->crtc_hblank_start) % 8 == 2)
return false;
- return true;
+ return intel_hdmi_deep_color_possible(crtc_state, bpc,
+ crtc_state->has_hdmi_sink,
+ crtc_state->output_format ==
+ INTEL_OUTPUT_FORMAT_YCBCR420);
}
static int
@@ -2459,6 +2475,23 @@ bool intel_hdmi_limited_color_range(const struct intel_crtc_state *crtc_state,
}
}
+static bool intel_hdmi_has_audio(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state,
+ const struct drm_connector_state *conn_state)
+{
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+ const struct intel_digital_connector_state *intel_conn_state =
+ to_intel_digital_connector_state(conn_state);
+
+ if (!crtc_state->has_hdmi_sink)
+ return false;
+
+ if (intel_conn_state->force_audio == HDMI_AUDIO_AUTO)
+ return intel_hdmi->has_audio;
+ else
+ return intel_conn_state->force_audio == HDMI_AUDIO_ON;
+}
+
int intel_hdmi_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state)
@@ -2468,8 +2501,6 @@ int intel_hdmi_compute_config(struct intel_encoder *encoder,
struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
struct drm_connector *connector = conn_state->connector;
struct drm_scdc *scdc = &connector->display_info.hdmi.scdc;
- struct intel_digital_connector_state *intel_conn_state =
- to_intel_digital_connector_state(conn_state);
int ret;
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
@@ -2495,13 +2526,8 @@ int intel_hdmi_compute_config(struct intel_encoder *encoder,
if (HAS_PCH_SPLIT(dev_priv) && !HAS_DDI(dev_priv))
pipe_config->has_pch_encoder = true;
- if (pipe_config->has_hdmi_sink) {
- if (intel_conn_state->force_audio == HDMI_AUDIO_AUTO)
- pipe_config->has_audio = intel_hdmi->has_audio;
- else
- pipe_config->has_audio =
- intel_conn_state->force_audio == HDMI_AUDIO_ON;
- }
+ pipe_config->has_audio =
+ intel_hdmi_has_audio(encoder, pipe_config, conn_state);
ret = intel_hdmi_compute_clock(encoder, pipe_config);
if (ret)
@@ -2667,6 +2693,9 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
drm_dbg_kms(&dev_priv->drm, "[CONNECTOR:%d:%s]\n",
connector->base.id, connector->name);
+ if (!INTEL_DISPLAY_ENABLED(dev_priv))
+ return connector_status_disconnected;
+
wakeref = intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
if (INTEL_GEN(dev_priv) >= 11 &&
@@ -3250,7 +3279,6 @@ void intel_hdmi_init_connector(struct intel_digital_port *dig_port,
if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
connector->ycbcr_420_allowed = true;
- intel_encoder->hpd_pin = intel_hpd_pin_default(dev_priv, port);
intel_connector->polled = DRM_CONNECTOR_POLL_HPD;
if (HAS_DDI(dev_priv))
@@ -3264,7 +3292,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *dig_port,
intel_hdmi->attached_connector = intel_connector;
if (is_hdcp_supported(dev_priv, port)) {
- int ret = intel_hdcp_init(intel_connector,
+ int ret = intel_hdcp_init(intel_connector, port,
&intel_hdmi_hdcp_shim);
if (ret)
drm_dbg_kms(&dev_priv->drm,
@@ -3335,6 +3363,8 @@ void intel_hdmi_init(struct drm_i915_private *dev_priv,
intel_encoder = &dig_port->base;
+ mutex_init(&dig_port->hdcp_mutex);
+
drm_encoder_init(&dev_priv->drm, &intel_encoder->base,
&intel_hdmi_enc_funcs, DRM_MODE_ENCODER_TMDS,
"HDMI %c", port_name(port));
@@ -3382,6 +3412,7 @@ void intel_hdmi_init(struct drm_i915_private *dev_priv,
intel_encoder->pipe_mask = ~0;
}
intel_encoder->cloneable = 1 << INTEL_OUTPUT_ANALOG;
+ intel_encoder->hpd_pin = intel_hpd_pin_default(dev_priv, port);
/*
* BSpec is unclear about HDMI+HDMI cloning on g4x, but it seems
* to work on real hardware. And since g4x can send infoframes to
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h
index 5b348dcab77a..15eb0ccde76e 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.h
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.h
@@ -48,5 +48,7 @@ void intel_read_infoframe(struct intel_encoder *encoder,
union hdmi_infoframe *frame);
bool intel_hdmi_limited_color_range(const struct intel_crtc_state *crtc_state,
const struct drm_connector_state *conn_state);
+bool intel_hdmi_deep_color_possible(const struct intel_crtc_state *crtc_state, int bpc,
+ bool has_hdmi_sink, bool ycbcr420_output);
#endif /* __INTEL_HDMI_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.c b/drivers/gpu/drm/i915/display/intel_hotplug.c
index 3f1d7b804a66..5c58c1ed6493 100644
--- a/drivers/gpu/drm/i915/display/intel_hotplug.c
+++ b/drivers/gpu/drm/i915/display/intel_hotplug.c
@@ -81,33 +81,12 @@
*
* It is only valid and used by digital port encoder.
*
- * Return pin that is associatade with @port and HDP_NONE if no pin is
- * hard associated with that @port.
+ * Return pin that is associatade with @port.
*/
enum hpd_pin intel_hpd_pin_default(struct drm_i915_private *dev_priv,
enum port port)
{
- enum phy phy = intel_port_to_phy(dev_priv, port);
-
- /*
- * RKL + TGP PCH is a special case; we effectively choose the hpd_pin
- * based on the DDI rather than the PHY (i.e., the last two outputs
- * shold be HPD_PORT_{D,E} rather than {C,D}. Note that this differs
- * from the behavior of both TGL+TGP and RKL+CMP.
- */
- if (IS_ROCKETLAKE(dev_priv) && HAS_PCH_TGP(dev_priv))
- return HPD_PORT_A + port - PORT_A;
-
- switch (phy) {
- case PHY_F:
- return IS_CNL_WITH_PORT_F(dev_priv) ? HPD_PORT_E : HPD_PORT_F;
- case PHY_A ... PHY_E:
- case PHY_G ... PHY_I:
- return HPD_PORT_A + phy - PHY_A;
- default:
- MISSING_CASE(phy);
- return HPD_NONE;
- }
+ return HPD_PORT_A + port - PORT_A;
}
#define HPD_STORM_DETECT_PERIOD 1000
@@ -503,7 +482,6 @@ void intel_hpd_irq_handler(struct drm_i915_private *dev_priv,
* only the one of them (DP) will have ->hpd_pulse().
*/
for_each_intel_encoder(&dev_priv->drm, encoder) {
- bool has_hpd_pulse = intel_encoder_has_hpd_pulse(encoder);
enum port port = encoder->port;
bool long_hpd;
@@ -511,7 +489,7 @@ void intel_hpd_irq_handler(struct drm_i915_private *dev_priv,
if (!(BIT(pin) & pin_mask))
continue;
- if (!has_hpd_pulse)
+ if (!intel_encoder_has_hpd_pulse(encoder))
continue;
long_hpd = long_mask & BIT(pin);
diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c
index 1888611244db..e65c2de522c3 100644
--- a/drivers/gpu/drm/i915/display/intel_lvds.c
+++ b/drivers/gpu/drm/i915/display/intel_lvds.c
@@ -456,12 +456,6 @@ static int intel_lvds_compute_config(struct intel_encoder *intel_encoder,
return 0;
}
-static enum drm_connector_status
-intel_lvds_detect(struct drm_connector *connector, bool force)
-{
- return connector_status_connected;
-}
-
/*
* Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
*/
@@ -490,7 +484,7 @@ static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs
};
static const struct drm_connector_funcs intel_lvds_connector_funcs = {
- .detect = intel_lvds_detect,
+ .detect = intel_panel_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_get_property = intel_digital_connector_atomic_get_property,
.atomic_set_property = intel_digital_connector_atomic_set_property,
diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c
index 4072d7062efd..9f23bac0d792 100644
--- a/drivers/gpu/drm/i915/display/intel_panel.c
+++ b/drivers/gpu/drm/i915/display/intel_panel.c
@@ -40,8 +40,6 @@
#include "intel_dsi_dcs_backlight.h"
#include "intel_panel.h"
-#define CRC_PMIC_PWM_PERIOD_NS 21333
-
void
intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode,
struct drm_display_mode *adjusted_mode)
@@ -594,10 +592,10 @@ static u32 bxt_get_backlight(struct intel_connector *connector)
static u32 pwm_get_backlight(struct intel_connector *connector)
{
struct intel_panel *panel = &connector->panel;
- int duty_ns;
+ struct pwm_state state;
- duty_ns = pwm_get_duty_cycle(panel->backlight.pwm);
- return DIV_ROUND_UP(duty_ns * 100, CRC_PMIC_PWM_PERIOD_NS);
+ pwm_get_state(panel->backlight.pwm, &state);
+ return pwm_get_relative_duty_cycle(&state, 100);
}
static void lpt_set_backlight(const struct drm_connector_state *conn_state, u32 level)
@@ -671,9 +669,9 @@ static void bxt_set_backlight(const struct drm_connector_state *conn_state, u32
static void pwm_set_backlight(const struct drm_connector_state *conn_state, u32 level)
{
struct intel_panel *panel = &to_intel_connector(conn_state->connector)->panel;
- int duty_ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100);
- pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS);
+ pwm_set_relative_duty_cycle(&panel->backlight.pwm_state, level, 100);
+ pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state);
}
static void
@@ -842,10 +840,8 @@ static void pwm_disable_backlight(const struct drm_connector_state *old_conn_sta
struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
struct intel_panel *panel = &connector->panel;
- /* Disable the backlight */
- intel_panel_actually_set_backlight(old_conn_state, 0);
- usleep_range(2000, 3000);
- pwm_disable(panel->backlight.pwm);
+ panel->backlight.pwm_state.enabled = false;
+ pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state);
}
void intel_panel_disable_backlight(const struct drm_connector_state *old_conn_state)
@@ -1177,9 +1173,12 @@ static void pwm_enable_backlight(const struct intel_crtc_state *crtc_state,
{
struct intel_connector *connector = to_intel_connector(conn_state->connector);
struct intel_panel *panel = &connector->panel;
+ int level = panel->backlight.level;
- pwm_enable(panel->backlight.pwm);
- intel_panel_actually_set_backlight(conn_state, panel->backlight.level);
+ level = intel_panel_compute_brightness(connector, level);
+ pwm_set_relative_duty_cycle(&panel->backlight.pwm_state, level, 100);
+ panel->backlight.pwm_state.enabled = true;
+ pwm_apply_state(panel->backlight.pwm, &panel->backlight.pwm_state);
}
static void __intel_panel_enable_backlight(const struct intel_crtc_state *crtc_state,
@@ -1543,18 +1542,9 @@ static u32 vlv_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
return DIV_ROUND_CLOSEST(clock, pwm_freq_hz * mul);
}
-static u32 get_backlight_max_vbt(struct intel_connector *connector)
+static u16 get_vbt_pwm_freq(struct drm_i915_private *dev_priv)
{
- struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct intel_panel *panel = &connector->panel;
u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz;
- u32 pwm;
-
- if (!panel->backlight.hz_to_pwm) {
- drm_dbg_kms(&dev_priv->drm,
- "backlight frequency conversion not supported\n");
- return 0;
- }
if (pwm_freq_hz) {
drm_dbg_kms(&dev_priv->drm,
@@ -1567,6 +1557,22 @@ static u32 get_backlight_max_vbt(struct intel_connector *connector)
pwm_freq_hz);
}
+ return pwm_freq_hz;
+}
+
+static u32 get_backlight_max_vbt(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_panel *panel = &connector->panel;
+ u16 pwm_freq_hz = get_vbt_pwm_freq(dev_priv);
+ u32 pwm;
+
+ if (!panel->backlight.hz_to_pwm) {
+ drm_dbg_kms(&dev_priv->drm,
+ "backlight frequency conversion not supported\n");
+ return 0;
+ }
+
pwm = panel->backlight.hz_to_pwm(connector, pwm_freq_hz);
if (!pwm) {
drm_dbg_kms(&dev_priv->drm,
@@ -1891,8 +1897,7 @@ static int pwm_setup_backlight(struct intel_connector *connector,
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_panel *panel = &connector->panel;
const char *desc;
- u32 level, ns;
- int retval;
+ u32 level;
/* Get the right PWM chip for DSI backlight according to VBT */
if (dev_priv->vbt.dsi.config->pwm_blc == PPS_BLC_PMIC) {
@@ -1910,30 +1915,28 @@ static int pwm_setup_backlight(struct intel_connector *connector,
return -ENODEV;
}
- /*
- * FIXME: pwm_apply_args() should be removed when switching to
- * the atomic PWM API.
- */
- pwm_apply_args(panel->backlight.pwm);
-
- panel->backlight.min = 0; /* 0% */
panel->backlight.max = 100; /* 100% */
- level = intel_panel_compute_brightness(connector, 100);
- ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100);
+ panel->backlight.min = get_backlight_min_vbt(connector);
- retval = pwm_config(panel->backlight.pwm, ns, CRC_PMIC_PWM_PERIOD_NS);
- if (retval < 0) {
- drm_err(&dev_priv->drm, "Failed to configure the pwm chip\n");
- pwm_put(panel->backlight.pwm);
- panel->backlight.pwm = NULL;
- return retval;
- }
+ if (pwm_is_enabled(panel->backlight.pwm)) {
+ /* PWM is already enabled, use existing settings */
+ pwm_get_state(panel->backlight.pwm, &panel->backlight.pwm_state);
+
+ level = pwm_get_relative_duty_cycle(&panel->backlight.pwm_state,
+ 100);
+ level = intel_panel_compute_brightness(connector, level);
+ panel->backlight.level = clamp(level, panel->backlight.min,
+ panel->backlight.max);
+ panel->backlight.enabled = true;
- level = DIV_ROUND_UP_ULL(pwm_get_duty_cycle(panel->backlight.pwm) * 100,
- CRC_PMIC_PWM_PERIOD_NS);
- panel->backlight.level =
- intel_panel_compute_brightness(connector, level);
- panel->backlight.enabled = panel->backlight.level != 0;
+ drm_dbg_kms(&dev_priv->drm, "PWM already enabled at freq %ld, VBT freq %d, level %d\n",
+ NSEC_PER_SEC / (unsigned long)panel->backlight.pwm_state.period,
+ get_vbt_pwm_freq(dev_priv), level);
+ } else {
+ /* Set period from VBT frequency, leave other settings at 0. */
+ panel->backlight.pwm_state.period =
+ NSEC_PER_SEC / get_vbt_pwm_freq(dev_priv);
+ }
drm_info(&dev_priv->drm, "Using %s PWM for LCD backlight control\n",
desc);
@@ -2092,6 +2095,17 @@ intel_panel_init_backlight_funcs(struct intel_panel *panel)
}
}
+enum drm_connector_status
+intel_panel_detect(struct drm_connector *connector, bool force)
+{
+ struct drm_i915_private *i915 = to_i915(connector->dev);
+
+ if (!INTEL_DISPLAY_ENABLED(i915))
+ return connector_status_disconnected;
+
+ return connector_status_connected;
+}
+
int intel_panel_init(struct intel_panel *panel,
struct drm_display_mode *fixed_mode,
struct drm_display_mode *downclock_mode)
diff --git a/drivers/gpu/drm/i915/display/intel_panel.h b/drivers/gpu/drm/i915/display/intel_panel.h
index 968b95281cb4..5b813fe90557 100644
--- a/drivers/gpu/drm/i915/display/intel_panel.h
+++ b/drivers/gpu/drm/i915/display/intel_panel.h
@@ -23,6 +23,8 @@ int intel_panel_init(struct intel_panel *panel,
struct drm_display_mode *fixed_mode,
struct drm_display_mode *downclock_mode);
void intel_panel_fini(struct intel_panel *panel);
+enum drm_connector_status
+intel_panel_detect(struct drm_connector *connector, bool force);
void intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode,
struct drm_display_mode *adjusted_mode);
int intel_pch_panel_fitting(struct intel_crtc_state *crtc_state,
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
index 2b004ee9619c..8a9d0bdde1bf 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -555,7 +555,7 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp)
if (dev_priv->psr.psr2_sel_fetch_enabled) {
/* WA 1408330847 */
- if (IS_TGL_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_A0) ||
+ if (IS_TGL_DISP_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_A0) ||
IS_RKL_REVID(dev_priv, RKL_REVID_A0, RKL_REVID_A0))
intel_de_rmw(dev_priv, CHICKEN_PAR1_1,
DIS_RAM_BYPASS_PSR2_MAN_TRACK,
@@ -1109,7 +1109,7 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp)
/* WA 1408330847 */
if (dev_priv->psr.psr2_sel_fetch_enabled &&
- (IS_TGL_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_A0) ||
+ (IS_TGL_DISP_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_A0) ||
IS_RKL_REVID(dev_priv, RKL_REVID_A0, RKL_REVID_A0)))
intel_de_rmw(dev_priv, CHICKEN_PAR1_1,
DIS_RAM_BYPASS_PSR2_MAN_TRACK, 0);
diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c
index 5e9fb349c829..4eaa4aa86ecd 100644
--- a/drivers/gpu/drm/i915/display/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/display/intel_sdvo.c
@@ -2084,14 +2084,18 @@ intel_sdvo_connector_matches_edid(struct intel_sdvo_connector *sdvo,
static enum drm_connector_status
intel_sdvo_detect(struct drm_connector *connector, bool force)
{
- u16 response;
+ struct drm_i915_private *i915 = to_i915(connector->dev);
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(to_intel_connector(connector));
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
enum drm_connector_status ret;
+ u16 response;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id, connector->name);
+ if (!INTEL_DISPLAY_ENABLED(i915))
+ return connector_status_disconnected;
+
if (!intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_ATTACHED_DISPLAYS,
&response, 2))
diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c
index 6b72223981be..63040cb0d4e1 100644
--- a/drivers/gpu/drm/i915/display/intel_sprite.c
+++ b/drivers/gpu/drm/i915/display/intel_sprite.c
@@ -1626,8 +1626,7 @@ static int g4x_sprite_min_cdclk(const struct intel_crtc_state *crtc_state,
hscale = drm_rect_calc_hscale(&plane_state->uapi.src,
&plane_state->uapi.dst,
0, INT_MAX);
- if (hscale < 0x10000)
- return pixel_rate;
+ hscale = max(hscale, 0x10000u);
/* Decimation steps at 2x,4x,8x,16x */
decimate = ilog2(hscale >> 16);
@@ -1640,8 +1639,8 @@ static int g4x_sprite_min_cdclk(const struct intel_crtc_state *crtc_state,
limit -= decimate;
/* -10% for RGB */
- if (fb->format->cpp[0] >= 4)
- limit--; /* -10% for RGB */
+ if (!fb->format->is_yuv)
+ limit--;
/*
* We should also do -10% if sprite scaling is enabled
@@ -2845,7 +2844,7 @@ static bool gen12_plane_supports_mc_ccs(struct drm_i915_private *dev_priv,
{
/* Wa_14010477008:tgl[a0..c0],rkl[all] */
if (IS_ROCKETLAKE(dev_priv) ||
- IS_TGL_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_C0))
+ IS_TGL_DISP_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_C0))
return false;
return plane_id < PLANE_SPRITE4;
diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c
index 777032d9697b..7a7b99b015a5 100644
--- a/drivers/gpu/drm/i915/display/intel_tv.c
+++ b/drivers/gpu/drm/i915/display/intel_tv.c
@@ -1706,6 +1706,9 @@ intel_tv_detect(struct drm_connector *connector,
drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] force=%d\n",
connector->base.id, connector->name, force);
+ if (!INTEL_DISPLAY_ENABLED(i915))
+ return connector_status_disconnected;
+
if (force) {
struct intel_load_detect_pipe tmp;
int ret;
diff --git a/drivers/gpu/drm/i915/display/intel_vbt_defs.h b/drivers/gpu/drm/i915/display/intel_vbt_defs.h
index 6faabd4f6d49..54bcc6a6947c 100644
--- a/drivers/gpu/drm/i915/display/intel_vbt_defs.h
+++ b/drivers/gpu/drm/i915/display/intel_vbt_defs.h
@@ -293,8 +293,12 @@ struct bdb_general_features {
#define DVO_PORT_HDMIE 12 /* 193 */
#define DVO_PORT_DPF 13 /* N/A */
#define DVO_PORT_HDMIF 14 /* N/A */
-#define DVO_PORT_DPG 15
-#define DVO_PORT_HDMIG 16
+#define DVO_PORT_DPG 15 /* 217 */
+#define DVO_PORT_HDMIG 16 /* 217 */
+#define DVO_PORT_DPH 17 /* 217 */
+#define DVO_PORT_HDMIH 18 /* 217 */
+#define DVO_PORT_DPI 19 /* 217 */
+#define DVO_PORT_HDMII 20 /* 217 */
#define DVO_PORT_MIPIA 21 /* 171 */
#define DVO_PORT_MIPIB 22 /* 171 */
#define DVO_PORT_MIPIC 23 /* 171 */
@@ -330,6 +334,8 @@ enum vbt_gmbus_ddi {
#define DP_AUX_E 0x50
#define DP_AUX_F 0x60
#define DP_AUX_G 0x70
+#define DP_AUX_H 0x80
+#define DP_AUX_I 0x90
#define VBT_DP_MAX_LINK_RATE_HBR3 0
#define VBT_DP_MAX_LINK_RATE_HBR2 1
diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c
index 052e0b31a2da..5e5522923b1e 100644
--- a/drivers/gpu/drm/i915/display/vlv_dsi.c
+++ b/drivers/gpu/drm/i915/display/vlv_dsi.c
@@ -1585,6 +1585,7 @@ static const struct drm_connector_helper_funcs intel_dsi_connector_helper_funcs
};
static const struct drm_connector_funcs intel_dsi_connector_funcs = {
+ .detect = intel_panel_detect,
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
.destroy = intel_connector_destroy,
diff --git a/drivers/gpu/drm/i915/display/vlv_dsi_pll.c b/drivers/gpu/drm/i915/display/vlv_dsi_pll.c
index d0a514301575..4070b00c3690 100644
--- a/drivers/gpu/drm/i915/display/vlv_dsi_pll.c
+++ b/drivers/gpu/drm/i915/display/vlv_dsi_pll.c
@@ -483,7 +483,7 @@ int bxt_dsi_pll_compute(struct intel_encoder *encoder,
if (dsi_ratio < dsi_ratio_min || dsi_ratio > dsi_ratio_max) {
drm_err(&dev_priv->drm,
- "Cant get a suitable ratio from DSI PLL ratios\n");
+ "Can't get a suitable ratio from DSI PLL ratios\n");
return -ECHRNG;
} else
drm_dbg_kms(&dev_priv->drm, "DSI PLL calculation is Done!!\n");
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c
index 27fddc22a7c6..8dd295dbe241 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c
@@ -48,12 +48,9 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme
src = sg_next(src);
}
- if (!dma_map_sg_attrs(attachment->dev,
- st->sgl, st->nents, dir,
- DMA_ATTR_SKIP_CPU_SYNC)) {
- ret = -ENOMEM;
+ ret = dma_map_sgtable(attachment->dev, st, dir, DMA_ATTR_SKIP_CPU_SYNC);
+ if (ret)
goto err_free_sg;
- }
return st;
@@ -73,9 +70,7 @@ static void i915_gem_unmap_dma_buf(struct dma_buf_attachment *attachment,
{
struct drm_i915_gem_object *obj = dma_buf_to_obj(attachment->dmabuf);
- dma_unmap_sg_attrs(attachment->dev,
- sg->sgl, sg->nents, dir,
- DMA_ATTR_SKIP_CPU_SYNC);
+ dma_unmap_sgtable(attachment->dev, sg, dir, DMA_ATTR_SKIP_CPU_SYNC);
sg_free_table(sg);
kfree(sg);
diff --git a/drivers/gpu/drm/i915/gem/selftests/mock_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/mock_dmabuf.c
index debaf7b18ab5..be30b27e2926 100644
--- a/drivers/gpu/drm/i915/gem/selftests/mock_dmabuf.c
+++ b/drivers/gpu/drm/i915/gem/selftests/mock_dmabuf.c
@@ -28,10 +28,9 @@ static struct sg_table *mock_map_dma_buf(struct dma_buf_attachment *attachment,
sg = sg_next(sg);
}
- if (!dma_map_sg(attachment->dev, st->sgl, st->nents, dir)) {
- err = -ENOMEM;
+ err = dma_map_sgtable(attachment->dev, st, dir, 0);
+ if (err)
goto err_st;
- }
return st;
@@ -46,7 +45,7 @@ static void mock_unmap_dma_buf(struct dma_buf_attachment *attachment,
struct sg_table *st,
enum dma_data_direction dir)
{
- dma_unmap_sg(attachment->dev, st->sgl, st->nents, dir);
+ dma_unmap_sgtable(attachment->dev, st, dir, 0);
sg_free_table(st);
kfree(st);
}
diff --git a/drivers/gpu/drm/i915/gt/intel_workarounds.c b/drivers/gpu/drm/i915/gt/intel_workarounds.c
index a3f72b75c61e..6c580d0d9ea8 100644
--- a/drivers/gpu/drm/i915/gt/intel_workarounds.c
+++ b/drivers/gpu/drm/i915/gt/intel_workarounds.c
@@ -70,6 +70,19 @@ const struct i915_rev_steppings kbl_revids[] = {
[7] = { .gt_stepping = KBL_REVID_G0, .disp_stepping = KBL_REVID_C0 },
};
+const struct i915_rev_steppings tgl_uy_revids[] = {
+ [0] = { .gt_stepping = TGL_REVID_A0, .disp_stepping = TGL_REVID_A0 },
+ [1] = { .gt_stepping = TGL_REVID_B0, .disp_stepping = TGL_REVID_C0 },
+ [2] = { .gt_stepping = TGL_REVID_B1, .disp_stepping = TGL_REVID_C0 },
+ [3] = { .gt_stepping = TGL_REVID_C0, .disp_stepping = TGL_REVID_D0 },
+};
+
+/* Same GT stepping between tgl_uy_revids and tgl_revids don't mean the same HW */
+const struct i915_rev_steppings tgl_revids[] = {
+ [0] = { .gt_stepping = TGL_REVID_A0, .disp_stepping = TGL_REVID_B0 },
+ [1] = { .gt_stepping = TGL_REVID_B0, .disp_stepping = TGL_REVID_D0 },
+};
+
static void wa_init_start(struct i915_wa_list *wal, const char *name, const char *engine_name)
{
wal->name = name;
@@ -1219,13 +1232,13 @@ tgl_gt_workarounds_init(struct drm_i915_private *i915, struct i915_wa_list *wal)
gen12_gt_workarounds_init(i915, wal);
/* Wa_1409420604:tgl */
- if (IS_TGL_REVID(i915, TGL_REVID_A0, TGL_REVID_A0))
+ if (IS_TGL_UY_GT_REVID(i915, TGL_REVID_A0, TGL_REVID_A0))
wa_write_or(wal,
SUBSLICE_UNIT_LEVEL_CLKGATE2,
CPSSUNIT_CLKGATE_DIS);
/* Wa_1607087056:tgl also know as BUG:1409180338 */
- if (IS_TGL_REVID(i915, TGL_REVID_A0, TGL_REVID_A0))
+ if (IS_TGL_UY_GT_REVID(i915, TGL_REVID_A0, TGL_REVID_A0))
wa_write_or(wal,
SLICE_UNIT_LEVEL_CLKGATE,
L3_CLKGATE_DIS | L3_CR2X_CLKGATE_DIS);
@@ -1660,7 +1673,7 @@ rcs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal)
{
struct drm_i915_private *i915 = engine->i915;
- if (IS_TGL_REVID(i915, TGL_REVID_A0, TGL_REVID_A0)) {
+ if (IS_TGL_UY_GT_REVID(i915, TGL_REVID_A0, TGL_REVID_A0)) {
/*
* Wa_1607138336:tgl
* Wa_1607063988:tgl
@@ -1700,7 +1713,7 @@ rcs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal)
* Wa_1407928979:tgl A*
* Wa_18011464164:tgl B0+
* Wa_22010931296:tgl B0+
- * Wa_14010919138:rkl
+ * Wa_14010919138:rkl,tgl
*/
wa_write_or(wal, GEN7_FF_THREAD_MODE,
GEN12_FF_TESSELATION_DOP_GATE_DISABLE);
@@ -1716,15 +1729,23 @@ rcs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal)
GEN6_RC_SLEEP_PSMI_CONTROL,
GEN12_WAIT_FOR_EVENT_POWER_DOWN_DISABLE |
GEN8_RC_SEMA_IDLE_MSG_DISABLE);
- }
- if (IS_TIGERLAKE(i915)) {
- /* Wa_1606700617:tgl */
+ /*
+ * Wa_1606700617:tgl
+ * Wa_22010271021:tgl,rkl
+ */
wa_masked_en(wal,
GEN9_CS_DEBUG_MODE1,
FF_DOP_CLOCK_GATE_DISABLE);
}
+ if (IS_GEN(i915, 12)) {
+ /* Wa_1406941453:gen12 */
+ wa_masked_en(wal,
+ GEN10_SAMPLER_MODE,
+ ENABLE_SMALLPL);
+ }
+
if (IS_GEN(i915, 11)) {
/* This is not an Wa. Enable for better image quality */
wa_masked_en(wal,
diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c
index d0a599b51bfe..16b582cb97ed 100644
--- a/drivers/gpu/drm/i915/gvt/cmd_parser.c
+++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c
@@ -936,7 +936,7 @@ static int cmd_reg_handler(struct parser_exec_state *s,
return -EFAULT;
}
- if (!intel_gvt_mmio_is_cmd_access(gvt, offset)) {
+ if (!intel_gvt_mmio_is_cmd_accessible(gvt, offset)) {
gvt_vgpu_err("%s access to non-render register (%x)\n",
cmd, offset);
return -EBADRQC;
@@ -976,7 +976,7 @@ static int cmd_reg_handler(struct parser_exec_state *s,
* inhibit context will restore with correct values
*/
if (IS_GEN(s->engine->i915, 9) &&
- intel_gvt_mmio_is_in_ctx(gvt, offset) &&
+ intel_gvt_mmio_is_sr_in_ctx(gvt, offset) &&
!strncmp(cmd, "lri", 3)) {
intel_gvt_hypervisor_read_gpa(s->vgpu,
s->workload->ring_context_gpa + 12, &ctx_sr_ctl, 4);
@@ -992,8 +992,6 @@ static int cmd_reg_handler(struct parser_exec_state *s,
}
}
- /* TODO: Update the global mask if this MMIO is a masked-MMIO */
- intel_gvt_mmio_set_cmd_accessed(gvt, offset);
return 0;
}
diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h
index ff7f2515a6fe..9831361f181e 100644
--- a/drivers/gpu/drm/i915/gvt/gvt.h
+++ b/drivers/gpu/drm/i915/gvt/gvt.h
@@ -256,11 +256,11 @@ struct intel_gvt_mmio {
/* This reg has been accessed by a VM */
#define F_ACCESSED (1 << 4)
/* This reg has been accessed through GPU commands */
-#define F_CMD_ACCESSED (1 << 5)
-/* This reg could be accessed by unaligned address */
#define F_UNALIGN (1 << 6)
-/* This reg is saved/restored in context */
-#define F_IN_CTX (1 << 7)
+/* This reg is in GVT's mmio save-restor list and in hardware
+ * logical context image
+ */
+#define F_SR_IN_CTX (1 << 7)
struct gvt_mmio_block *mmio_block;
unsigned int num_mmio_block;
@@ -597,39 +597,42 @@ static inline void intel_gvt_mmio_set_accessed(
}
/**
- * intel_gvt_mmio_is_cmd_accessed - mark a MMIO could be accessed by command
+ * intel_gvt_mmio_is_cmd_accessible - if a MMIO could be accessed by command
* @gvt: a GVT device
* @offset: register offset
*
+ * Returns:
+ * True if an MMIO is able to be accessed by GPU commands
*/
-static inline bool intel_gvt_mmio_is_cmd_access(
+static inline bool intel_gvt_mmio_is_cmd_accessible(
struct intel_gvt *gvt, unsigned int offset)
{
return gvt->mmio.mmio_attribute[offset >> 2] & F_CMD_ACCESS;
}
/**
- * intel_gvt_mmio_is_unalign - mark a MMIO could be accessed unaligned
+ * intel_gvt_mmio_set_cmd_accessible -
+ * mark a MMIO could be accessible by command
* @gvt: a GVT device
* @offset: register offset
*
*/
-static inline bool intel_gvt_mmio_is_unalign(
+static inline void intel_gvt_mmio_set_cmd_accessible(
struct intel_gvt *gvt, unsigned int offset)
{
- return gvt->mmio.mmio_attribute[offset >> 2] & F_UNALIGN;
+ gvt->mmio.mmio_attribute[offset >> 2] |= F_CMD_ACCESS;
}
/**
- * intel_gvt_mmio_set_cmd_accessed - mark a MMIO has been accessed by command
+ * intel_gvt_mmio_is_unalign - mark a MMIO could be accessed unaligned
* @gvt: a GVT device
* @offset: register offset
*
*/
-static inline void intel_gvt_mmio_set_cmd_accessed(
+static inline bool intel_gvt_mmio_is_unalign(
struct intel_gvt *gvt, unsigned int offset)
{
- gvt->mmio.mmio_attribute[offset >> 2] |= F_CMD_ACCESSED;
+ return gvt->mmio.mmio_attribute[offset >> 2] & F_UNALIGN;
}
/**
@@ -648,30 +651,33 @@ static inline bool intel_gvt_mmio_has_mode_mask(
}
/**
- * intel_gvt_mmio_is_in_ctx - check if a MMIO has in-ctx mask
+ * intel_gvt_mmio_is_sr_in_ctx -
+ * check if an MMIO has F_SR_IN_CTX mask
* @gvt: a GVT device
* @offset: register offset
*
* Returns:
- * True if a MMIO has a in-context mask, false if it isn't.
+ * True if an MMIO has an F_SR_IN_CTX mask, false if it isn't.
*
*/
-static inline bool intel_gvt_mmio_is_in_ctx(
+static inline bool intel_gvt_mmio_is_sr_in_ctx(
struct intel_gvt *gvt, unsigned int offset)
{
- return gvt->mmio.mmio_attribute[offset >> 2] & F_IN_CTX;
+ return gvt->mmio.mmio_attribute[offset >> 2] & F_SR_IN_CTX;
}
/**
- * intel_gvt_mmio_set_in_ctx - mask a MMIO in logical context
+ * intel_gvt_mmio_set_sr_in_ctx -
+ * mask an MMIO in GVT's mmio save-restore list and also
+ * in hardware logical context image
* @gvt: a GVT device
* @offset: register offset
*
*/
-static inline void intel_gvt_mmio_set_in_ctx(
+static inline void intel_gvt_mmio_set_sr_in_ctx(
struct intel_gvt *gvt, unsigned int offset)
{
- gvt->mmio.mmio_attribute[offset >> 2] |= F_IN_CTX;
+ gvt->mmio.mmio_attribute[offset >> 2] |= F_SR_IN_CTX;
}
void intel_gvt_debugfs_add_vgpu(struct intel_vgpu *vgpu);
diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c
index 05f3bc98d242..3be37e6fe33d 100644
--- a/drivers/gpu/drm/i915/gvt/handlers.c
+++ b/drivers/gpu/drm/i915/gvt/handlers.c
@@ -1892,7 +1892,7 @@ static int init_generic_mmio_info(struct intel_gvt *gvt)
struct drm_i915_private *dev_priv = gvt->gt->i915;
int ret;
- MMIO_RING_DFH(RING_IMR, D_ALL, F_CMD_ACCESS, NULL,
+ MMIO_RING_DFH(RING_IMR, D_ALL, 0, NULL,
intel_vgpu_reg_imr_handler);
MMIO_DFH(SDEIMR, D_ALL, 0, NULL, intel_vgpu_reg_imr_handler);
@@ -1900,7 +1900,8 @@ static int init_generic_mmio_info(struct intel_gvt *gvt)
MMIO_DFH(SDEIIR, D_ALL, 0, NULL, intel_vgpu_reg_iir_handler);
MMIO_D(SDEISR, D_ALL);
- MMIO_RING_DFH(RING_HWSTAM, D_ALL, F_CMD_ACCESS, NULL, NULL);
+ MMIO_RING_DFH(RING_HWSTAM, D_ALL, 0, NULL, NULL);
+
MMIO_DH(GEN8_GAMW_ECO_DEV_RW_IA, D_BDW_PLUS, NULL,
gamw_echo_dev_rw_ia_write);
@@ -1927,11 +1928,11 @@ static int init_generic_mmio_info(struct intel_gvt *gvt)
MMIO_GM_RDR(_MMIO(0x12198), D_ALL, NULL, NULL);
MMIO_D(GEN7_CXT_SIZE, D_ALL);
- MMIO_RING_DFH(RING_TAIL, D_ALL, F_CMD_ACCESS, NULL, NULL);
- MMIO_RING_DFH(RING_HEAD, D_ALL, F_CMD_ACCESS, NULL, NULL);
- MMIO_RING_DFH(RING_CTL, D_ALL, F_CMD_ACCESS, NULL, NULL);
- MMIO_RING_DFH(RING_ACTHD, D_ALL, F_CMD_ACCESS, mmio_read_from_hw, NULL);
- MMIO_RING_GM_RDR(RING_START, D_ALL, NULL, NULL);
+ MMIO_RING_DFH(RING_TAIL, D_ALL, 0, NULL, NULL);
+ MMIO_RING_DFH(RING_HEAD, D_ALL, 0, NULL, NULL);
+ MMIO_RING_DFH(RING_CTL, D_ALL, 0, NULL, NULL);
+ MMIO_RING_DFH(RING_ACTHD, D_ALL, 0, mmio_read_from_hw, NULL);
+ MMIO_RING_GM(RING_START, D_ALL, NULL, NULL);
/* RING MODE */
#define RING_REG(base) _MMIO((base) + 0x29c)
@@ -2686,7 +2687,7 @@ static int init_generic_mmio_info(struct intel_gvt *gvt)
MMIO_DFH(_MMIO(0x4094), D_BDW_PLUS, F_CMD_ACCESS, NULL, NULL);
MMIO_DFH(ARB_MODE, D_ALL, F_MODE_MASK | F_CMD_ACCESS, NULL, NULL);
- MMIO_RING_GM_RDR(RING_BBADDR, D_ALL, NULL, NULL);
+ MMIO_RING_GM(RING_BBADDR, D_ALL, NULL, NULL);
MMIO_DFH(_MMIO(0x2220), D_ALL, F_CMD_ACCESS, NULL, NULL);
MMIO_DFH(_MMIO(0x12220), D_ALL, F_CMD_ACCESS, NULL, NULL);
MMIO_DFH(_MMIO(0x22220), D_ALL, F_CMD_ACCESS, NULL, NULL);
@@ -2771,7 +2772,7 @@ static int init_bdw_mmio_info(struct intel_gvt *gvt)
MMIO_DH(GEN8_MASTER_IRQ, D_BDW_PLUS, NULL,
intel_vgpu_reg_master_irq_handler);
- MMIO_RING_DFH(RING_ACTHD_UDW, D_BDW_PLUS, F_CMD_ACCESS,
+ MMIO_RING_DFH(RING_ACTHD_UDW, D_BDW_PLUS, 0,
mmio_read_from_hw, NULL);
#define RING_REG(base) _MMIO((base) + 0xd0)
@@ -2785,7 +2786,7 @@ static int init_bdw_mmio_info(struct intel_gvt *gvt)
#undef RING_REG
#define RING_REG(base) _MMIO((base) + 0x234)
- MMIO_RING_F(RING_REG, 8, F_RO | F_CMD_ACCESS, 0, ~0, D_BDW_PLUS,
+ MMIO_RING_F(RING_REG, 8, F_RO, 0, ~0, D_BDW_PLUS,
NULL, NULL);
#undef RING_REG
@@ -2820,7 +2821,7 @@ static int init_bdw_mmio_info(struct intel_gvt *gvt)
MMIO_RING_F(RING_REG, 32, F_CMD_ACCESS, 0, 0, D_BDW_PLUS, NULL, NULL);
#undef RING_REG
- MMIO_RING_GM_RDR(RING_HWS_PGA, D_BDW_PLUS, NULL, hws_pga_write);
+ MMIO_RING_GM(RING_HWS_PGA, D_BDW_PLUS, NULL, hws_pga_write);
MMIO_DFH(HDC_CHICKEN0, D_BDW_PLUS, F_MODE_MASK | F_CMD_ACCESS, NULL, NULL);
@@ -2921,7 +2922,7 @@ static int init_skl_mmio_info(struct intel_gvt *gvt)
MMIO_D(GEN9_MEDIA_PG_IDLE_HYSTERESIS, D_SKL_PLUS);
MMIO_D(GEN9_RENDER_PG_IDLE_HYSTERESIS, D_SKL_PLUS);
MMIO_DFH(GEN9_GAMT_ECO_REG_RW_IA, D_SKL_PLUS, F_CMD_ACCESS, NULL, NULL);
- MMIO_DH(MMCD_MISC_CTRL, D_SKL_PLUS, NULL, NULL);
+ MMIO_DFH(MMCD_MISC_CTRL, D_SKL_PLUS, F_CMD_ACCESS, NULL, NULL);
MMIO_DH(CHICKEN_PAR1_1, D_SKL_PLUS, NULL, NULL);
MMIO_D(DC_STATE_EN, D_SKL_PLUS);
MMIO_D(DC_STATE_DEBUG, D_SKL_PLUS);
@@ -3137,7 +3138,7 @@ static int init_skl_mmio_info(struct intel_gvt *gvt)
MMIO_DFH(GEN9_WM_CHICKEN3, D_SKL_PLUS, F_MODE_MASK | F_CMD_ACCESS,
NULL, NULL);
- MMIO_D(GAMT_CHKN_BIT_REG, D_KBL | D_CFL);
+ MMIO_DFH(GAMT_CHKN_BIT_REG, D_KBL | D_CFL, F_CMD_ACCESS, NULL, NULL);
MMIO_D(GEN9_CTX_PREEMPT_REG, D_SKL_PLUS);
return 0;
@@ -3357,7 +3358,10 @@ void intel_gvt_clean_mmio_info(struct intel_gvt *gvt)
gvt->mmio.mmio_attribute = NULL;
}
-/* Special MMIO blocks. */
+/* Special MMIO blocks. registers in MMIO block ranges should not be command
+ * accessible (should have no F_CMD_ACCESS flag).
+ * otherwise, need to update cmd_reg_handler in cmd_parser.c
+ */
static struct gvt_mmio_block mmio_blocks[] = {
{D_SKL_PLUS, _MMIO(CSR_MMIO_START_RANGE), 0x3000, NULL, NULL},
{D_ALL, _MMIO(MCHBAR_MIRROR_BASE_SNB), 0x40000, NULL, NULL},
diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c
index 291993615af9..b6811f6a230d 100644
--- a/drivers/gpu/drm/i915/gvt/mmio.c
+++ b/drivers/gpu/drm/i915/gvt/mmio.c
@@ -251,6 +251,9 @@ void intel_vgpu_reset_mmio(struct intel_vgpu *vgpu, bool dmlr)
/* set the bit 0:2(Core C-State ) to C0 */
vgpu_vreg_t(vgpu, GEN6_GT_CORE_STATUS) = 0;
+ /* uc reset hw expect GS_MIA_IN_RESET */
+ vgpu_vreg_t(vgpu, GUC_STATUS) |= GS_MIA_IN_RESET;
+
if (IS_BROXTON(vgpu->gvt->gt->i915)) {
vgpu_vreg_t(vgpu, BXT_P_CR_GT_DISP_PWRON) &=
~(BIT(0) | BIT(1));
diff --git a/drivers/gpu/drm/i915/gvt/mmio_context.c b/drivers/gpu/drm/i915/gvt/mmio_context.c
index 86a60bdf0818..afe574d6b3b5 100644
--- a/drivers/gpu/drm/i915/gvt/mmio_context.c
+++ b/drivers/gpu/drm/i915/gvt/mmio_context.c
@@ -595,7 +595,7 @@ void intel_gvt_init_engine_mmio_context(struct intel_gvt *gvt)
i915_mmio_reg_valid(mmio->reg); mmio++) {
if (mmio->in_context) {
gvt->engine_mmio_list.ctx_mmio_count[mmio->id]++;
- intel_gvt_mmio_set_in_ctx(gvt, mmio->reg.reg);
+ intel_gvt_mmio_set_sr_in_ctx(gvt, mmio->reg.reg);
}
}
}
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 00292a849c34..acc32066cec3 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -58,7 +58,6 @@
#include "display/intel_hotplug.h"
#include "display/intel_overlay.h"
#include "display/intel_pipe_crc.h"
-#include "display/intel_psr.h"
#include "display/intel_sprite.h"
#include "display/intel_vga.h"
@@ -216,125 +215,6 @@ intel_teardown_mchbar(struct drm_i915_private *dev_priv)
release_resource(&dev_priv->mch_res);
}
-/* part #1: call before irq install */
-static int i915_driver_modeset_probe_noirq(struct drm_i915_private *i915)
-{
- int ret;
-
- if (i915_inject_probe_failure(i915))
- return -ENODEV;
-
- if (HAS_DISPLAY(i915) && INTEL_DISPLAY_ENABLED(i915)) {
- ret = drm_vblank_init(&i915->drm,
- INTEL_NUM_PIPES(i915));
- if (ret)
- return ret;
- }
-
- intel_bios_init(i915);
-
- ret = intel_vga_register(i915);
- if (ret)
- goto cleanup_bios;
-
- intel_power_domains_init_hw(i915, false);
-
- intel_csr_ucode_init(i915);
-
- ret = intel_modeset_init_noirq(i915);
- if (ret)
- goto cleanup_vga_client_pw_domain_csr;
-
- return 0;
-
-cleanup_vga_client_pw_domain_csr:
- intel_csr_ucode_fini(i915);
- intel_power_domains_driver_remove(i915);
- intel_vga_unregister(i915);
-cleanup_bios:
- intel_bios_driver_remove(i915);
- return ret;
-}
-
-/* part #2: call after irq install */
-static int i915_driver_modeset_probe(struct drm_i915_private *i915)
-{
- int ret;
-
- /* Important: The output setup functions called by modeset_init need
- * working irqs for e.g. gmbus and dp aux transfers. */
- ret = intel_modeset_init(i915);
- if (ret)
- goto out;
-
- ret = i915_gem_init(i915);
- if (ret)
- goto cleanup_modeset;
-
- intel_overlay_setup(i915);
-
- if (!HAS_DISPLAY(i915) || !INTEL_DISPLAY_ENABLED(i915))
- return 0;
-
- ret = intel_fbdev_init(&i915->drm);
- if (ret)
- goto cleanup_gem;
-
- /* Only enable hotplug handling once the fbdev is fully set up. */
- intel_hpd_init(i915);
-
- intel_init_ipc(i915);
-
- intel_psr_set_force_mode_changed(i915->psr.dp);
-
- return 0;
-
-cleanup_gem:
- i915_gem_suspend(i915);
- i915_gem_driver_remove(i915);
- i915_gem_driver_release(i915);
-cleanup_modeset:
- /* FIXME */
- intel_modeset_driver_remove(i915);
- intel_irq_uninstall(i915);
- intel_modeset_driver_remove_noirq(i915);
-out:
- return ret;
-}
-
-/* part #1: call before irq uninstall */
-static void i915_driver_modeset_remove(struct drm_i915_private *i915)
-{
- intel_modeset_driver_remove(i915);
-}
-
-/* part #2: call after irq uninstall */
-static void i915_driver_modeset_remove_noirq(struct drm_i915_private *i915)
-{
- intel_csr_ucode_fini(i915);
-
- intel_power_domains_driver_remove(i915);
-
- intel_vga_unregister(i915);
-
- intel_bios_driver_remove(i915);
-}
-
-static void intel_init_dpio(struct drm_i915_private *dev_priv)
-{
- /*
- * IOSF_PORT_DPIO is used for VLV x2 PHY (DP/HDMI B and C),
- * CHV x1 PHY (DP/HDMI D)
- * IOSF_PORT_DPIO_2 is used for CHV x2 PHY (DP/HDMI B and C)
- */
- if (IS_CHERRYVIEW(dev_priv)) {
- DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO_2;
- DPIO_PHY_IOSF_PORT(DPIO_PHY1) = IOSF_PORT_DPIO;
- } else if (IS_VALLEYVIEW(dev_priv)) {
- DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO;
- }
-}
-
static int i915_workqueues_init(struct drm_i915_private *dev_priv)
{
/*
@@ -463,7 +343,6 @@ static int i915_driver_early_probe(struct drm_i915_private *dev_priv)
intel_detect_pch(dev_priv);
intel_pm_setup(dev_priv);
- intel_init_dpio(dev_priv);
ret = intel_power_domains_init(dev_priv);
if (ret < 0)
goto err_gem;
@@ -798,7 +677,7 @@ static void i915_driver_register(struct drm_i915_private *dev_priv)
drm_err(&dev_priv->drm,
"Failed to register driver for userspace access!\n");
- if (HAS_DISPLAY(dev_priv) && INTEL_DISPLAY_ENABLED(dev_priv)) {
+ if (HAS_DISPLAY(dev_priv)) {
/* Must be done after probing outputs */
intel_opregion_register(dev_priv);
acpi_video_register();
@@ -821,7 +700,7 @@ static void i915_driver_register(struct drm_i915_private *dev_priv)
* We need to coordinate the hotplugs with the asynchronous fbdev
* configuration, for which we use the fbdev->async_cookie.
*/
- if (HAS_DISPLAY(dev_priv) && INTEL_DISPLAY_ENABLED(dev_priv))
+ if (HAS_DISPLAY(dev_priv))
drm_kms_helper_poll_init(dev);
intel_power_domains_enable(dev_priv);
@@ -988,7 +867,7 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret < 0)
goto out_cleanup_mmio;
- ret = i915_driver_modeset_probe_noirq(i915);
+ ret = intel_modeset_init_noirq(i915);
if (ret < 0)
goto out_cleanup_hw;
@@ -996,10 +875,18 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
goto out_cleanup_modeset;
- ret = i915_driver_modeset_probe(i915);
- if (ret < 0)
+ ret = intel_modeset_init_nogem(i915);
+ if (ret)
goto out_cleanup_irq;
+ ret = i915_gem_init(i915);
+ if (ret)
+ goto out_cleanup_modeset2;
+
+ ret = intel_modeset_init(i915);
+ if (ret)
+ goto out_cleanup_gem;
+
i915_driver_register(i915);
enable_rpm_wakeref_asserts(&i915->runtime_pm);
@@ -1010,10 +897,20 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
+out_cleanup_gem:
+ i915_gem_suspend(i915);
+ i915_gem_driver_remove(i915);
+ i915_gem_driver_release(i915);
+out_cleanup_modeset2:
+ /* FIXME clean up the error path */
+ intel_modeset_driver_remove(i915);
+ intel_irq_uninstall(i915);
+ intel_modeset_driver_remove_noirq(i915);
+ goto out_cleanup_modeset;
out_cleanup_irq:
intel_irq_uninstall(i915);
out_cleanup_modeset:
- i915_driver_modeset_remove_noirq(i915);
+ intel_modeset_driver_remove_nogem(i915);
out_cleanup_hw:
i915_driver_hw_remove(i915);
intel_memory_regions_driver_release(i915);
@@ -1045,7 +942,7 @@ void i915_driver_remove(struct drm_i915_private *i915)
intel_gvt_driver_remove(i915);
- i915_driver_modeset_remove(i915);
+ intel_modeset_driver_remove(i915);
intel_irq_uninstall(i915);
@@ -1054,7 +951,7 @@ void i915_driver_remove(struct drm_i915_private *i915)
i915_reset_error_state(i915);
i915_gem_driver_remove(i915);
- i915_driver_modeset_remove_noirq(i915);
+ intel_modeset_driver_remove_nogem(i915);
i915_driver_hw_remove(i915);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index ab17084af0ff..72a9449b674e 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -108,18 +108,11 @@
#define DRIVER_NAME "i915"
#define DRIVER_DESC "Intel Graphics"
-#define DRIVER_DATE "20200824"
-#define DRIVER_TIMESTAMP 1598293597
+#define DRIVER_DATE "20200917"
+#define DRIVER_TIMESTAMP 1600375437
struct drm_i915_gem_object;
-/*
- * The code assumes that the hpd_pins below have consecutive values and
- * starting with HPD_PORT_A, the HPD pin associated with any port can be
- * retrieved by adding the corresponding port (or phy) enum value to
- * HPD_PORT_A in most cases. For example:
- * HPD_PORT_C = HPD_PORT_A + PHY_C - PHY_A
- */
enum hpd_pin {
HPD_NONE = 0,
HPD_TV = HPD_NONE, /* TV is known to be unreliable */
@@ -131,10 +124,12 @@ enum hpd_pin {
HPD_PORT_C,
HPD_PORT_D,
HPD_PORT_E,
- HPD_PORT_F,
- HPD_PORT_G,
- HPD_PORT_H,
- HPD_PORT_I,
+ HPD_PORT_TC1,
+ HPD_PORT_TC2,
+ HPD_PORT_TC3,
+ HPD_PORT_TC4,
+ HPD_PORT_TC5,
+ HPD_PORT_TC6,
HPD_NUM_PINS
};
@@ -537,13 +532,9 @@ struct intel_gmbus {
struct i915_suspend_saved_registers {
u32 saveDSPARB;
- u32 saveFBC_CONTROL;
- u32 saveCACHE_MODE_0;
- u32 saveMI_ARB_STATE;
u32 saveSWF0[16];
u32 saveSWF1[16];
u32 saveSWF3[3];
- u32 savePCH_PORT_HOTPLUG;
u16 saveGCDGMBUS;
};
@@ -1020,8 +1011,6 @@ struct drm_i915_private {
*/
u8 active_pipes;
- int dpio_phy_iosf_port[I915_NUM_PHYS_VLV];
-
struct i915_wa_list gt_wa_list;
struct i915_frontbuffer_tracking fb_tracking;
@@ -1572,12 +1561,41 @@ extern const struct i915_rev_steppings kbl_revids[];
#define IS_EHL_REVID(p, since, until) \
(IS_ELKHARTLAKE(p) && IS_REVID(p, since, until))
-#define TGL_REVID_A0 0x0
-#define TGL_REVID_B0 0x1
-#define TGL_REVID_C0 0x2
+enum {
+ TGL_REVID_A0,
+ TGL_REVID_B0,
+ TGL_REVID_B1,
+ TGL_REVID_C0,
+ TGL_REVID_D0,
+};
+
+extern const struct i915_rev_steppings tgl_uy_revids[];
+extern const struct i915_rev_steppings tgl_revids[];
+
+static inline const struct i915_rev_steppings *
+tgl_revids_get(struct drm_i915_private *dev_priv)
+{
+ if (IS_TGL_U(dev_priv) || IS_TGL_Y(dev_priv))
+ return tgl_uy_revids;
+ else
+ return tgl_revids;
+}
-#define IS_TGL_REVID(p, since, until) \
- (IS_TIGERLAKE(p) && IS_REVID(p, since, until))
+#define IS_TGL_DISP_REVID(p, since, until) \
+ (IS_TIGERLAKE(p) && \
+ tgl_revids_get(p)->disp_stepping >= (since) && \
+ tgl_revids_get(p)->disp_stepping <= (until))
+
+#define IS_TGL_UY_GT_REVID(p, since, until) \
+ ((IS_TGL_U(p) || IS_TGL_Y(p)) && \
+ tgl_uy_revids->gt_stepping >= (since) && \
+ tgl_uy_revids->gt_stepping <= (until))
+
+#define IS_TGL_GT_REVID(p, since, until) \
+ (IS_TIGERLAKE(p) && \
+ !(IS_TGL_U(p) || IS_TGL_Y(p)) && \
+ tgl_revids->gt_stepping >= (since) && \
+ tgl_revids->gt_stepping <= (until))
#define RKL_REVID_A0 0x0
#define RKL_REVID_B0 0x1
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index f113fe44572b..759f523c6a6b 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -132,40 +132,24 @@ static const u32 hpd_bxt[HPD_NUM_PINS] = {
};
static const u32 hpd_gen11[HPD_NUM_PINS] = {
- [HPD_PORT_C] = GEN11_TC1_HOTPLUG | GEN11_TBT1_HOTPLUG,
- [HPD_PORT_D] = GEN11_TC2_HOTPLUG | GEN11_TBT2_HOTPLUG,
- [HPD_PORT_E] = GEN11_TC3_HOTPLUG | GEN11_TBT3_HOTPLUG,
- [HPD_PORT_F] = GEN11_TC4_HOTPLUG | GEN11_TBT4_HOTPLUG,
-};
-
-static const u32 hpd_gen12[HPD_NUM_PINS] = {
- [HPD_PORT_D] = GEN11_TC1_HOTPLUG | GEN11_TBT1_HOTPLUG,
- [HPD_PORT_E] = GEN11_TC2_HOTPLUG | GEN11_TBT2_HOTPLUG,
- [HPD_PORT_F] = GEN11_TC3_HOTPLUG | GEN11_TBT3_HOTPLUG,
- [HPD_PORT_G] = GEN11_TC4_HOTPLUG | GEN11_TBT4_HOTPLUG,
- [HPD_PORT_H] = GEN12_TC5_HOTPLUG | GEN12_TBT5_HOTPLUG,
- [HPD_PORT_I] = GEN12_TC6_HOTPLUG | GEN12_TBT6_HOTPLUG,
+ [HPD_PORT_TC1] = GEN11_TC_HOTPLUG(PORT_TC1) | GEN11_TBT_HOTPLUG(PORT_TC1),
+ [HPD_PORT_TC2] = GEN11_TC_HOTPLUG(PORT_TC2) | GEN11_TBT_HOTPLUG(PORT_TC2),
+ [HPD_PORT_TC3] = GEN11_TC_HOTPLUG(PORT_TC3) | GEN11_TBT_HOTPLUG(PORT_TC3),
+ [HPD_PORT_TC4] = GEN11_TC_HOTPLUG(PORT_TC4) | GEN11_TBT_HOTPLUG(PORT_TC4),
+ [HPD_PORT_TC5] = GEN11_TC_HOTPLUG(PORT_TC5) | GEN11_TBT_HOTPLUG(PORT_TC5),
+ [HPD_PORT_TC6] = GEN11_TC_HOTPLUG(PORT_TC6) | GEN11_TBT_HOTPLUG(PORT_TC6),
};
static const u32 hpd_icp[HPD_NUM_PINS] = {
[HPD_PORT_A] = SDE_DDI_HOTPLUG_ICP(PORT_A),
[HPD_PORT_B] = SDE_DDI_HOTPLUG_ICP(PORT_B),
- [HPD_PORT_C] = SDE_TC_HOTPLUG_ICP(PORT_TC1),
- [HPD_PORT_D] = SDE_TC_HOTPLUG_ICP(PORT_TC2),
- [HPD_PORT_E] = SDE_TC_HOTPLUG_ICP(PORT_TC3),
- [HPD_PORT_F] = SDE_TC_HOTPLUG_ICP(PORT_TC4),
-};
-
-static const u32 hpd_tgp[HPD_NUM_PINS] = {
- [HPD_PORT_A] = SDE_DDI_HOTPLUG_ICP(PORT_A),
- [HPD_PORT_B] = SDE_DDI_HOTPLUG_ICP(PORT_B),
[HPD_PORT_C] = SDE_DDI_HOTPLUG_ICP(PORT_C),
- [HPD_PORT_D] = SDE_TC_HOTPLUG_ICP(PORT_TC1),
- [HPD_PORT_E] = SDE_TC_HOTPLUG_ICP(PORT_TC2),
- [HPD_PORT_F] = SDE_TC_HOTPLUG_ICP(PORT_TC3),
- [HPD_PORT_G] = SDE_TC_HOTPLUG_ICP(PORT_TC4),
- [HPD_PORT_H] = SDE_TC_HOTPLUG_ICP(PORT_TC5),
- [HPD_PORT_I] = SDE_TC_HOTPLUG_ICP(PORT_TC6),
+ [HPD_PORT_TC1] = SDE_TC_HOTPLUG_ICP(PORT_TC1),
+ [HPD_PORT_TC2] = SDE_TC_HOTPLUG_ICP(PORT_TC2),
+ [HPD_PORT_TC3] = SDE_TC_HOTPLUG_ICP(PORT_TC3),
+ [HPD_PORT_TC4] = SDE_TC_HOTPLUG_ICP(PORT_TC4),
+ [HPD_PORT_TC5] = SDE_TC_HOTPLUG_ICP(PORT_TC5),
+ [HPD_PORT_TC6] = SDE_TC_HOTPLUG_ICP(PORT_TC6),
};
static void intel_hpd_init_pins(struct drm_i915_private *dev_priv)
@@ -181,9 +165,7 @@ static void intel_hpd_init_pins(struct drm_i915_private *dev_priv)
return;
}
- if (INTEL_GEN(dev_priv) >= 12)
- hpd->hpd = hpd_gen12;
- else if (INTEL_GEN(dev_priv) >= 11)
+ if (INTEL_GEN(dev_priv) >= 11)
hpd->hpd = hpd_gen11;
else if (IS_GEN9_LP(dev_priv))
hpd->hpd = hpd_bxt;
@@ -197,9 +179,8 @@ static void intel_hpd_init_pins(struct drm_i915_private *dev_priv)
if (!HAS_PCH_SPLIT(dev_priv) || HAS_PCH_NOP(dev_priv))
return;
- if (HAS_PCH_TGP(dev_priv) || HAS_PCH_JSP(dev_priv))
- hpd->pch_hpd = hpd_tgp;
- else if (HAS_PCH_ICP(dev_priv) || HAS_PCH_MCC(dev_priv))
+ if (HAS_PCH_TGP(dev_priv) || HAS_PCH_JSP(dev_priv) ||
+ HAS_PCH_ICP(dev_priv) || HAS_PCH_MCC(dev_priv))
hpd->pch_hpd = hpd_icp;
else if (HAS_PCH_CNP(dev_priv) || HAS_PCH_SPT(dev_priv))
hpd->pch_hpd = hpd_spt;
@@ -1049,33 +1030,17 @@ out:
static bool gen11_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
{
switch (pin) {
- case HPD_PORT_C:
- return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC1);
- case HPD_PORT_D:
- return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC2);
- case HPD_PORT_E:
- return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC3);
- case HPD_PORT_F:
- return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC4);
- default:
- return false;
- }
-}
-
-static bool gen12_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
-{
- switch (pin) {
- case HPD_PORT_D:
+ case HPD_PORT_TC1:
return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC1);
- case HPD_PORT_E:
+ case HPD_PORT_TC2:
return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC2);
- case HPD_PORT_F:
+ case HPD_PORT_TC3:
return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC3);
- case HPD_PORT_G:
+ case HPD_PORT_TC4:
return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC4);
- case HPD_PORT_H:
+ case HPD_PORT_TC5:
return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC5);
- case HPD_PORT_I:
+ case HPD_PORT_TC6:
return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC6);
default:
return false;
@@ -1113,33 +1078,17 @@ static bool icp_ddi_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
static bool icp_tc_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
{
switch (pin) {
- case HPD_PORT_C:
- return val & ICP_TC_HPD_LONG_DETECT(PORT_TC1);
- case HPD_PORT_D:
- return val & ICP_TC_HPD_LONG_DETECT(PORT_TC2);
- case HPD_PORT_E:
- return val & ICP_TC_HPD_LONG_DETECT(PORT_TC3);
- case HPD_PORT_F:
- return val & ICP_TC_HPD_LONG_DETECT(PORT_TC4);
- default:
- return false;
- }
-}
-
-static bool tgp_tc_port_hotplug_long_detect(enum hpd_pin pin, u32 val)
-{
- switch (pin) {
- case HPD_PORT_D:
+ case HPD_PORT_TC1:
return val & ICP_TC_HPD_LONG_DETECT(PORT_TC1);
- case HPD_PORT_E:
+ case HPD_PORT_TC2:
return val & ICP_TC_HPD_LONG_DETECT(PORT_TC2);
- case HPD_PORT_F:
+ case HPD_PORT_TC3:
return val & ICP_TC_HPD_LONG_DETECT(PORT_TC3);
- case HPD_PORT_G:
+ case HPD_PORT_TC4:
return val & ICP_TC_HPD_LONG_DETECT(PORT_TC4);
- case HPD_PORT_H:
+ case HPD_PORT_TC5:
return val & ICP_TC_HPD_LONG_DETECT(PORT_TC5);
- case HPD_PORT_I:
+ case HPD_PORT_TC6:
return val & ICP_TC_HPD_LONG_DETECT(PORT_TC6);
default:
return false;
@@ -1893,19 +1842,16 @@ static void icp_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir)
{
u32 ddi_hotplug_trigger, tc_hotplug_trigger;
u32 pin_mask = 0, long_mask = 0;
- bool (*tc_port_hotplug_long_detect)(enum hpd_pin pin, u32 val);
if (HAS_PCH_TGP(dev_priv)) {
ddi_hotplug_trigger = pch_iir & SDE_DDI_MASK_TGP;
tc_hotplug_trigger = pch_iir & SDE_TC_MASK_TGP;
- tc_port_hotplug_long_detect = tgp_tc_port_hotplug_long_detect;
} else if (HAS_PCH_JSP(dev_priv)) {
ddi_hotplug_trigger = pch_iir & SDE_DDI_MASK_TGP;
tc_hotplug_trigger = 0;
} else if (HAS_PCH_MCC(dev_priv)) {
ddi_hotplug_trigger = pch_iir & SDE_DDI_MASK_ICP;
tc_hotplug_trigger = pch_iir & SDE_TC_HOTPLUG_ICP(PORT_TC1);
- tc_port_hotplug_long_detect = icp_tc_port_hotplug_long_detect;
} else {
drm_WARN(&dev_priv->drm, !HAS_PCH_ICP(dev_priv),
"Unrecognized PCH type 0x%x\n",
@@ -1913,7 +1859,6 @@ static void icp_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir)
ddi_hotplug_trigger = pch_iir & SDE_DDI_MASK_ICP;
tc_hotplug_trigger = pch_iir & SDE_TC_MASK_ICP;
- tc_port_hotplug_long_detect = icp_tc_port_hotplug_long_detect;
}
if (ddi_hotplug_trigger) {
@@ -1937,7 +1882,7 @@ static void icp_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir)
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask,
tc_hotplug_trigger, dig_hotplug_reg,
dev_priv->hotplug.pch_hpd,
- tc_port_hotplug_long_detect);
+ icp_tc_port_hotplug_long_detect);
}
if (pin_mask)
@@ -2185,12 +2130,6 @@ static void gen11_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 iir)
u32 pin_mask = 0, long_mask = 0;
u32 trigger_tc = iir & GEN11_DE_TC_HOTPLUG_MASK;
u32 trigger_tbt = iir & GEN11_DE_TBT_HOTPLUG_MASK;
- long_pulse_detect_func long_pulse_detect;
-
- if (INTEL_GEN(dev_priv) >= 12)
- long_pulse_detect = gen12_port_hotplug_long_detect;
- else
- long_pulse_detect = gen11_port_hotplug_long_detect;
if (trigger_tc) {
u32 dig_hotplug_reg;
@@ -2201,7 +2140,7 @@ static void gen11_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 iir)
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask,
trigger_tc, dig_hotplug_reg,
dev_priv->hotplug.hpd,
- long_pulse_detect);
+ gen11_port_hotplug_long_detect);
}
if (trigger_tbt) {
@@ -2213,7 +2152,7 @@ static void gen11_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 iir)
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask,
trigger_tbt, dig_hotplug_reg,
dev_priv->hotplug.hpd,
- long_pulse_detect);
+ gen11_port_hotplug_long_detect);
}
if (pin_mask)
@@ -3048,6 +2987,18 @@ static u32 intel_hpd_enabled_irqs(struct drm_i915_private *dev_priv,
return enabled_irqs;
}
+static u32 intel_hpd_hotplug_irqs(struct drm_i915_private *dev_priv,
+ const u32 hpd[HPD_NUM_PINS])
+{
+ struct intel_encoder *encoder;
+ u32 hotplug_irqs = 0;
+
+ for_each_intel_encoder(&dev_priv->drm, encoder)
+ hotplug_irqs |= hpd[encoder->hpd_pin];
+
+ return hotplug_irqs;
+}
+
static void ibx_hpd_detection_setup(struct drm_i915_private *dev_priv)
{
u32 hotplug;
@@ -3077,50 +3028,50 @@ static void ibx_hpd_irq_setup(struct drm_i915_private *dev_priv)
{
u32 hotplug_irqs, enabled_irqs;
- if (HAS_PCH_IBX(dev_priv))
- hotplug_irqs = SDE_HOTPLUG_MASK;
- else
- hotplug_irqs = SDE_HOTPLUG_MASK_CPT;
-
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.pch_hpd);
+ hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.pch_hpd);
ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs);
ibx_hpd_detection_setup(dev_priv);
}
-static void icp_hpd_detection_setup(struct drm_i915_private *dev_priv,
- u32 ddi_hotplug_enable_mask,
- u32 tc_hotplug_enable_mask)
+static void icp_ddi_hpd_detection_setup(struct drm_i915_private *dev_priv,
+ u32 enable_mask)
{
u32 hotplug;
hotplug = I915_READ(SHOTPLUG_CTL_DDI);
- hotplug |= ddi_hotplug_enable_mask;
+ hotplug |= enable_mask;
I915_WRITE(SHOTPLUG_CTL_DDI, hotplug);
+}
- if (tc_hotplug_enable_mask) {
- hotplug = I915_READ(SHOTPLUG_CTL_TC);
- hotplug |= tc_hotplug_enable_mask;
- I915_WRITE(SHOTPLUG_CTL_TC, hotplug);
- }
+static void icp_tc_hpd_detection_setup(struct drm_i915_private *dev_priv,
+ u32 enable_mask)
+{
+ u32 hotplug;
+
+ hotplug = I915_READ(SHOTPLUG_CTL_TC);
+ hotplug |= enable_mask;
+ I915_WRITE(SHOTPLUG_CTL_TC, hotplug);
}
static void icp_hpd_irq_setup(struct drm_i915_private *dev_priv,
- u32 sde_ddi_mask, u32 sde_tc_mask,
u32 ddi_enable_mask, u32 tc_enable_mask)
{
u32 hotplug_irqs, enabled_irqs;
- hotplug_irqs = sde_ddi_mask | sde_tc_mask;
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.pch_hpd);
+ hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.pch_hpd);
if (INTEL_PCH_TYPE(dev_priv) <= PCH_TGP)
I915_WRITE(SHPD_FILTER_CNT, SHPD_FILTER_CNT_500_ADJ);
ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs);
- icp_hpd_detection_setup(dev_priv, ddi_enable_mask, tc_enable_mask);
+ icp_ddi_hpd_detection_setup(dev_priv, ddi_enable_mask);
+ if (tc_enable_mask)
+ icp_tc_hpd_detection_setup(dev_priv, tc_enable_mask);
}
/*
@@ -3130,7 +3081,6 @@ static void icp_hpd_irq_setup(struct drm_i915_private *dev_priv,
static void mcc_hpd_irq_setup(struct drm_i915_private *dev_priv)
{
icp_hpd_irq_setup(dev_priv,
- SDE_DDI_MASK_ICP, SDE_TC_HOTPLUG_ICP(PORT_TC1),
ICP_DDI_HPD_ENABLE_MASK, ICP_TC_HPD_ENABLE(PORT_TC1));
}
@@ -3142,7 +3092,6 @@ static void mcc_hpd_irq_setup(struct drm_i915_private *dev_priv)
static void jsp_hpd_irq_setup(struct drm_i915_private *dev_priv)
{
icp_hpd_irq_setup(dev_priv,
- SDE_DDI_MASK_TGP, 0,
TGP_DDI_HPD_ENABLE_MASK, 0);
}
@@ -3154,14 +3103,18 @@ static void gen11_hpd_detection_setup(struct drm_i915_private *dev_priv)
hotplug |= GEN11_HOTPLUG_CTL_ENABLE(PORT_TC1) |
GEN11_HOTPLUG_CTL_ENABLE(PORT_TC2) |
GEN11_HOTPLUG_CTL_ENABLE(PORT_TC3) |
- GEN11_HOTPLUG_CTL_ENABLE(PORT_TC4);
+ GEN11_HOTPLUG_CTL_ENABLE(PORT_TC4) |
+ GEN11_HOTPLUG_CTL_ENABLE(PORT_TC5) |
+ GEN11_HOTPLUG_CTL_ENABLE(PORT_TC6);
I915_WRITE(GEN11_TC_HOTPLUG_CTL, hotplug);
hotplug = I915_READ(GEN11_TBT_HOTPLUG_CTL);
hotplug |= GEN11_HOTPLUG_CTL_ENABLE(PORT_TC1) |
GEN11_HOTPLUG_CTL_ENABLE(PORT_TC2) |
GEN11_HOTPLUG_CTL_ENABLE(PORT_TC3) |
- GEN11_HOTPLUG_CTL_ENABLE(PORT_TC4);
+ GEN11_HOTPLUG_CTL_ENABLE(PORT_TC4) |
+ GEN11_HOTPLUG_CTL_ENABLE(PORT_TC5) |
+ GEN11_HOTPLUG_CTL_ENABLE(PORT_TC6);
I915_WRITE(GEN11_TBT_HOTPLUG_CTL, hotplug);
}
@@ -3171,7 +3124,7 @@ static void gen11_hpd_irq_setup(struct drm_i915_private *dev_priv)
u32 val;
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.hpd);
- hotplug_irqs = GEN11_DE_TC_HOTPLUG_MASK | GEN11_DE_TBT_HOTPLUG_MASK;
+ hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.hpd);
val = I915_READ(GEN11_DE_HPD_IMR);
val &= ~hotplug_irqs;
@@ -3182,10 +3135,10 @@ static void gen11_hpd_irq_setup(struct drm_i915_private *dev_priv)
gen11_hpd_detection_setup(dev_priv);
if (INTEL_PCH_TYPE(dev_priv) >= PCH_TGP)
- icp_hpd_irq_setup(dev_priv, SDE_DDI_MASK_TGP, SDE_TC_MASK_TGP,
+ icp_hpd_irq_setup(dev_priv,
TGP_DDI_HPD_ENABLE_MASK, TGP_TC_HPD_ENABLE_MASK);
else if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP)
- icp_hpd_irq_setup(dev_priv, SDE_DDI_MASK_ICP, SDE_TC_MASK_ICP,
+ icp_hpd_irq_setup(dev_priv,
ICP_DDI_HPD_ENABLE_MASK, ICP_TC_HPD_ENABLE_MASK);
}
@@ -3221,8 +3174,8 @@ static void spt_hpd_irq_setup(struct drm_i915_private *dev_priv)
if (INTEL_PCH_TYPE(dev_priv) >= PCH_CNP)
I915_WRITE(SHPD_FILTER_CNT, SHPD_FILTER_CNT_500_ADJ);
- hotplug_irqs = SDE_HOTPLUG_MASK_SPT;
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.pch_hpd);
+ hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.pch_hpd);
ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs);
@@ -3249,22 +3202,13 @@ static void ilk_hpd_irq_setup(struct drm_i915_private *dev_priv)
{
u32 hotplug_irqs, enabled_irqs;
- if (INTEL_GEN(dev_priv) >= 8) {
- hotplug_irqs = GEN8_PORT_DP_A_HOTPLUG;
- enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.hpd);
+ enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.hpd);
+ hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.hpd);
+ if (INTEL_GEN(dev_priv) >= 8)
bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs);
- } else if (INTEL_GEN(dev_priv) >= 7) {
- hotplug_irqs = DE_DP_A_HOTPLUG_IVB;
- enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.hpd);
-
- ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs);
- } else {
- hotplug_irqs = DE_DP_A_HOTPLUG;
- enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.hpd);
-
+ else
ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs);
- }
ilk_hpd_detection_setup(dev_priv);
@@ -3313,7 +3257,7 @@ static void bxt_hpd_irq_setup(struct drm_i915_private *dev_priv)
u32 hotplug_irqs, enabled_irqs;
enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.hpd);
- hotplug_irqs = BXT_DE_PORT_HOTPLUG_MASK;
+ hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.hpd);
bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs);
@@ -3534,17 +3478,18 @@ static void icp_irq_postinstall(struct drm_i915_private *dev_priv)
gen3_assert_iir_is_zero(&dev_priv->uncore, SDEIIR);
I915_WRITE(SDEIMR, ~mask);
- if (HAS_PCH_TGP(dev_priv))
- icp_hpd_detection_setup(dev_priv, TGP_DDI_HPD_ENABLE_MASK,
- TGP_TC_HPD_ENABLE_MASK);
- else if (HAS_PCH_JSP(dev_priv))
- icp_hpd_detection_setup(dev_priv, TGP_DDI_HPD_ENABLE_MASK, 0);
- else if (HAS_PCH_MCC(dev_priv))
- icp_hpd_detection_setup(dev_priv, ICP_DDI_HPD_ENABLE_MASK,
- ICP_TC_HPD_ENABLE(PORT_TC1));
- else
- icp_hpd_detection_setup(dev_priv, ICP_DDI_HPD_ENABLE_MASK,
- ICP_TC_HPD_ENABLE_MASK);
+ if (HAS_PCH_TGP(dev_priv)) {
+ icp_ddi_hpd_detection_setup(dev_priv, TGP_DDI_HPD_ENABLE_MASK);
+ icp_tc_hpd_detection_setup(dev_priv, TGP_TC_HPD_ENABLE_MASK);
+ } else if (HAS_PCH_JSP(dev_priv)) {
+ icp_ddi_hpd_detection_setup(dev_priv, TGP_DDI_HPD_ENABLE_MASK);
+ } else if (HAS_PCH_MCC(dev_priv)) {
+ icp_ddi_hpd_detection_setup(dev_priv, ICP_DDI_HPD_ENABLE_MASK);
+ icp_tc_hpd_detection_setup(dev_priv, ICP_TC_HPD_ENABLE(PORT_TC1));
+ } else {
+ icp_ddi_hpd_detection_setup(dev_priv, ICP_DDI_HPD_ENABLE_MASK);
+ icp_tc_hpd_detection_setup(dev_priv, ICP_TC_HPD_ENABLE_MASK);
+ }
}
static void gen11_irq_postinstall(struct drm_i915_private *dev_priv)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index ac691927e29d..d805d4da6181 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1382,7 +1382,6 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define DPIO_CMNRST (1 << 0)
#define DPIO_PHY(pipe) ((pipe) >> 1)
-#define DPIO_PHY_IOSF_PORT(phy) (dev_priv->dpio_phy_iosf_port[phy])
/*
* Per pipe/PLL DPIO regs
@@ -7760,32 +7759,20 @@ enum {
#define GEN11_DE_HPD_IMR _MMIO(0x44474)
#define GEN11_DE_HPD_IIR _MMIO(0x44478)
#define GEN11_DE_HPD_IER _MMIO(0x4447c)
-#define GEN12_TC6_HOTPLUG (1 << 21)
-#define GEN12_TC5_HOTPLUG (1 << 20)
-#define GEN11_TC4_HOTPLUG (1 << 19)
-#define GEN11_TC3_HOTPLUG (1 << 18)
-#define GEN11_TC2_HOTPLUG (1 << 17)
-#define GEN11_TC1_HOTPLUG (1 << 16)
#define GEN11_TC_HOTPLUG(tc_port) (1 << ((tc_port) + 16))
-#define GEN11_DE_TC_HOTPLUG_MASK (GEN12_TC6_HOTPLUG | \
- GEN12_TC5_HOTPLUG | \
- GEN11_TC4_HOTPLUG | \
- GEN11_TC3_HOTPLUG | \
- GEN11_TC2_HOTPLUG | \
- GEN11_TC1_HOTPLUG)
-#define GEN12_TBT6_HOTPLUG (1 << 5)
-#define GEN12_TBT5_HOTPLUG (1 << 4)
-#define GEN11_TBT4_HOTPLUG (1 << 3)
-#define GEN11_TBT3_HOTPLUG (1 << 2)
-#define GEN11_TBT2_HOTPLUG (1 << 1)
-#define GEN11_TBT1_HOTPLUG (1 << 0)
+#define GEN11_DE_TC_HOTPLUG_MASK (GEN11_TC_HOTPLUG(PORT_TC6) | \
+ GEN11_TC_HOTPLUG(PORT_TC5) | \
+ GEN11_TC_HOTPLUG(PORT_TC4) | \
+ GEN11_TC_HOTPLUG(PORT_TC3) | \
+ GEN11_TC_HOTPLUG(PORT_TC2) | \
+ GEN11_TC_HOTPLUG(PORT_TC1))
#define GEN11_TBT_HOTPLUG(tc_port) (1 << (tc_port))
-#define GEN11_DE_TBT_HOTPLUG_MASK (GEN12_TBT6_HOTPLUG | \
- GEN12_TBT5_HOTPLUG | \
- GEN11_TBT4_HOTPLUG | \
- GEN11_TBT3_HOTPLUG | \
- GEN11_TBT2_HOTPLUG | \
- GEN11_TBT1_HOTPLUG)
+#define GEN11_DE_TBT_HOTPLUG_MASK (GEN11_TBT_HOTPLUG(PORT_TC6) | \
+ GEN11_TBT_HOTPLUG(PORT_TC5) | \
+ GEN11_TBT_HOTPLUG(PORT_TC4) | \
+ GEN11_TBT_HOTPLUG(PORT_TC3) | \
+ GEN11_TBT_HOTPLUG(PORT_TC2) | \
+ GEN11_TBT_HOTPLUG(PORT_TC1))
#define GEN11_TBT_HOTPLUG_CTL _MMIO(0x44030)
#define GEN11_TC_HOTPLUG_CTL _MMIO(0x44038)
@@ -9315,6 +9302,7 @@ enum {
#define GEN11_LSN_UNSLCVC_GAFS_HALF_SF_MAXALLOC (1 << 7)
#define GEN10_SAMPLER_MODE _MMIO(0xE18C)
+#define ENABLE_SMALLPL REG_BIT(15)
#define GEN11_SAMPLER_ENABLE_HEADLESS_MSG REG_BIT(5)
/* IVYBRIDGE DPF */
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index ed2be3489f8e..7b64e7137270 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -34,17 +34,25 @@
static void i915_save_display(struct drm_i915_private *dev_priv)
{
+ struct pci_dev *pdev = dev_priv->drm.pdev;
+
/* Display arbitration control */
if (INTEL_GEN(dev_priv) <= 4)
dev_priv->regfile.saveDSPARB = I915_READ(DSPARB);
- /* save FBC interval */
- if (HAS_FBC(dev_priv) && INTEL_GEN(dev_priv) <= 4 && !IS_G4X(dev_priv))
- dev_priv->regfile.saveFBC_CONTROL = I915_READ(FBC_CONTROL);
+ if (IS_GEN(dev_priv, 4))
+ pci_read_config_word(pdev, GCDGMBUS,
+ &dev_priv->regfile.saveGCDGMBUS);
}
static void i915_restore_display(struct drm_i915_private *dev_priv)
{
+ struct pci_dev *pdev = dev_priv->drm.pdev;
+
+ if (IS_GEN(dev_priv, 4))
+ pci_write_config_word(pdev, GCDGMBUS,
+ dev_priv->regfile.saveGCDGMBUS);
+
/* Display arbitration */
if (INTEL_GEN(dev_priv) <= 4)
I915_WRITE(DSPARB, dev_priv->regfile.saveDSPARB);
@@ -52,31 +60,17 @@ static void i915_restore_display(struct drm_i915_private *dev_priv)
/* only restore FBC info on the platform that supports FBC*/
intel_fbc_global_disable(dev_priv);
- /* restore FBC interval */
- if (HAS_FBC(dev_priv) && INTEL_GEN(dev_priv) <= 4 && !IS_G4X(dev_priv))
- I915_WRITE(FBC_CONTROL, dev_priv->regfile.saveFBC_CONTROL);
-
intel_vga_redisable(dev_priv);
+
+ intel_gmbus_reset(dev_priv);
}
int i915_save_state(struct drm_i915_private *dev_priv)
{
- struct pci_dev *pdev = dev_priv->drm.pdev;
int i;
i915_save_display(dev_priv);
- if (IS_GEN(dev_priv, 4))
- pci_read_config_word(pdev, GCDGMBUS,
- &dev_priv->regfile.saveGCDGMBUS);
-
- /* Cache mode state */
- if (INTEL_GEN(dev_priv) < 7)
- dev_priv->regfile.saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
-
- /* Memory Arbitration state */
- dev_priv->regfile.saveMI_ARB_STATE = I915_READ(MI_ARB_STATE);
-
/* Scratch space */
if (IS_GEN(dev_priv, 2) && IS_MOBILE(dev_priv)) {
for (i = 0; i < 7; i++) {
@@ -102,22 +96,10 @@ int i915_save_state(struct drm_i915_private *dev_priv)
int i915_restore_state(struct drm_i915_private *dev_priv)
{
- struct pci_dev *pdev = dev_priv->drm.pdev;
int i;
- if (IS_GEN(dev_priv, 4))
- pci_write_config_word(pdev, GCDGMBUS,
- dev_priv->regfile.saveGCDGMBUS);
i915_restore_display(dev_priv);
- /* Cache mode state */
- if (INTEL_GEN(dev_priv) < 7)
- I915_WRITE(CACHE_MODE_0, dev_priv->regfile.saveCACHE_MODE_0 |
- 0xffff0000);
-
- /* Memory arbitration state */
- I915_WRITE(MI_ARB_STATE, dev_priv->regfile.saveMI_ARB_STATE | 0xffff0000);
-
/* Scratch space */
if (IS_GEN(dev_priv, 2) && IS_MOBILE(dev_priv)) {
for (i = 0; i < 7; i++) {
@@ -138,7 +120,5 @@ int i915_restore_state(struct drm_i915_private *dev_priv)
I915_WRITE(SWF3(i), dev_priv->regfile.saveSWF3[i]);
}
- intel_gmbus_reset(dev_priv);
-
return 0;
}
diff --git a/drivers/gpu/drm/i915/intel_device_info.c b/drivers/gpu/drm/i915/intel_device_info.c
index e2aa5bc3a6e0..adc836f15fde 100644
--- a/drivers/gpu/drm/i915/intel_device_info.c
+++ b/drivers/gpu/drm/i915/intel_device_info.c
@@ -516,6 +516,14 @@ void intel_device_info_runtime_init(struct drm_i915_private *dev_priv)
S32_MAX),
USEC_PER_SEC));
}
+
+ if (!HAS_DISPLAY(dev_priv)) {
+ dev_priv->drm.driver_features &= ~(DRIVER_MODESET |
+ DRIVER_ATOMIC);
+ memset(&info->display, 0, sizeof(info->display));
+ memset(runtime->num_sprites, 0, sizeof(runtime->num_sprites));
+ memset(runtime->num_scalers, 0, sizeof(runtime->num_scalers));
+ }
}
void intel_driver_caps_print(const struct intel_driver_caps *caps,
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index b4bd19266b8c..34e0d22d456b 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -7136,7 +7136,7 @@ static void tgl_init_clock_gating(struct drm_i915_private *dev_priv)
I915_READ(POWERGATE_ENABLE) | vd_pg_enable);
/* Wa_1409825376:tgl (pre-prod)*/
- if (IS_TGL_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_A0))
+ if (IS_TGL_DISP_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_B1))
I915_WRITE(GEN9_CLKGATE_DIS_3, I915_READ(GEN9_CLKGATE_DIS_3) |
TGL_VRH_GATING_DIS);
diff --git a/drivers/gpu/drm/i915/intel_sideband.c b/drivers/gpu/drm/i915/intel_sideband.c
index 916ccd1c0e96..5b3279262123 100644
--- a/drivers/gpu/drm/i915/intel_sideband.c
+++ b/drivers/gpu/drm/i915/intel_sideband.c
@@ -231,9 +231,21 @@ void vlv_ccu_write(struct drm_i915_private *i915, u32 reg, u32 val)
SB_CRWRDA_NP, reg, &val);
}
+static u32 vlv_dpio_phy_iosf_port(struct drm_i915_private *i915, enum dpio_phy phy)
+{
+ /*
+ * IOSF_PORT_DPIO: VLV x2 PHY (DP/HDMI B and C), CHV x1 PHY (DP/HDMI D)
+ * IOSF_PORT_DPIO_2: CHV x2 PHY (DP/HDMI B and C)
+ */
+ if (IS_CHERRYVIEW(i915))
+ return phy == DPIO_PHY0 ? IOSF_PORT_DPIO_2 : IOSF_PORT_DPIO;
+ else
+ return IOSF_PORT_DPIO;
+}
+
u32 vlv_dpio_read(struct drm_i915_private *i915, enum pipe pipe, int reg)
{
- int port = i915->dpio_phy_iosf_port[DPIO_PHY(pipe)];
+ u32 port = vlv_dpio_phy_iosf_port(i915, DPIO_PHY(pipe));
u32 val = 0;
vlv_sideband_rw(i915, DPIO_DEVFN, port, SB_MRD_NP, reg, &val);
@@ -252,7 +264,7 @@ u32 vlv_dpio_read(struct drm_i915_private *i915, enum pipe pipe, int reg)
void vlv_dpio_write(struct drm_i915_private *i915,
enum pipe pipe, int reg, u32 val)
{
- int port = i915->dpio_phy_iosf_port[DPIO_PHY(pipe)];
+ u32 port = vlv_dpio_phy_iosf_port(i915, DPIO_PHY(pipe));
vlv_sideband_rw(i915, DPIO_DEVFN, port, SB_MWR_NP, reg, &val);
}
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index 8d5a933e6af6..263ffcb832b7 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -1993,13 +1993,14 @@ int __intel_wait_for_register_fw(struct intel_uncore *uncore,
unsigned int slow_timeout_ms,
u32 *out_value)
{
- u32 reg_value;
+ u32 reg_value = 0;
#define done (((reg_value = intel_uncore_read_fw(uncore, reg)) & mask) == value)
int ret;
/* Catch any overuse of this function */
might_sleep_if(slow_timeout_ms);
GEM_BUG_ON(fast_timeout_us > 20000);
+ GEM_BUG_ON(!fast_timeout_us && !slow_timeout_ms);
ret = -ETIMEDOUT;
if (fast_timeout_us && fast_timeout_us <= 20000)
diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
index f127e633f7ca..397c313a8b69 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
@@ -118,11 +118,11 @@ static struct dev_pm_domain pm_domain = {
struct drm_i915_private *mock_gem_device(void)
{
- struct drm_i915_private *i915;
- struct pci_dev *pdev;
#if IS_ENABLED(CONFIG_IOMMU_API) && defined(CONFIG_INTEL_IOMMU)
- struct dev_iommu iommu;
+ static struct dev_iommu fake_iommu = { .priv = (void *)-1 };
#endif
+ struct drm_i915_private *i915;
+ struct pci_dev *pdev;
int err;
pdev = kzalloc(sizeof(*pdev), GFP_KERNEL);
@@ -141,10 +141,8 @@ struct drm_i915_private *mock_gem_device(void)
dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
#if IS_ENABLED(CONFIG_IOMMU_API) && defined(CONFIG_INTEL_IOMMU)
- /* HACK HACK HACK to disable iommu for the fake device; force identity mapping */
- memset(&iommu, 0, sizeof(iommu));
- iommu.priv = (void *)-1;
- pdev->dev.iommu = &iommu;
+ /* HACK to disable iommu for the fake device; force identity mapping */
+ pdev->dev.iommu = &fake_iommu;
#endif
pci_set_drvdata(pdev, i915);
diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c
index 155f2b4b4030..11223fe348df 100644
--- a/drivers/gpu/drm/lima/lima_gem.c
+++ b/drivers/gpu/drm/lima/lima_gem.c
@@ -69,8 +69,7 @@ int lima_heap_alloc(struct lima_bo *bo, struct lima_vm *vm)
return ret;
if (bo->base.sgt) {
- dma_unmap_sg(dev, bo->base.sgt->sgl,
- bo->base.sgt->nents, DMA_BIDIRECTIONAL);
+ dma_unmap_sgtable(dev, bo->base.sgt, DMA_BIDIRECTIONAL, 0);
sg_free_table(bo->base.sgt);
} else {
bo->base.sgt = kmalloc(sizeof(*bo->base.sgt), GFP_KERNEL);
@@ -80,7 +79,13 @@ int lima_heap_alloc(struct lima_bo *bo, struct lima_vm *vm)
}
}
- dma_map_sg(dev, sgt.sgl, sgt.nents, DMA_BIDIRECTIONAL);
+ ret = dma_map_sgtable(dev, &sgt, DMA_BIDIRECTIONAL, 0);
+ if (ret) {
+ sg_free_table(&sgt);
+ kfree(bo->base.sgt);
+ bo->base.sgt = NULL;
+ return ret;
+ }
*bo->base.sgt = sgt;
diff --git a/drivers/gpu/drm/lima/lima_vm.c b/drivers/gpu/drm/lima/lima_vm.c
index 5b92fb82674a..2b2739adc7f5 100644
--- a/drivers/gpu/drm/lima/lima_vm.c
+++ b/drivers/gpu/drm/lima/lima_vm.c
@@ -124,7 +124,7 @@ int lima_vm_bo_add(struct lima_vm *vm, struct lima_bo *bo, bool create)
if (err)
goto err_out1;
- for_each_sg_dma_page(bo->base.sgt->sgl, &sg_iter, bo->base.sgt->nents, 0) {
+ for_each_sgtable_dma_page(bo->base.sgt, &sg_iter, 0) {
err = lima_vm_map_page(vm, sg_page_iter_dma_address(&sg_iter),
bo_va->node.start + offset);
if (err)
@@ -298,8 +298,7 @@ int lima_vm_map_bo(struct lima_vm *vm, struct lima_bo *bo, int pageoff)
mutex_lock(&vm->lock);
base = bo_va->node.start + (pageoff << PAGE_SHIFT);
- for_each_sg_dma_page(bo->base.sgt->sgl, &sg_iter,
- bo->base.sgt->nents, pageoff) {
+ for_each_sgtable_dma_page(bo->base.sgt, &sg_iter, pageoff) {
err = lima_vm_map_page(vm, sg_page_iter_dma_address(&sg_iter),
base + offset);
if (err)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.c b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
index 6190cc3b7b0d..0583e557ad37 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_gem.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
@@ -212,46 +212,28 @@ struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach, struct sg_table *sg)
{
struct mtk_drm_gem_obj *mtk_gem;
- int ret;
- struct scatterlist *s;
- unsigned int i;
- dma_addr_t expected;
- mtk_gem = mtk_drm_gem_init(dev, attach->dmabuf->size);
+ /* check if the entries in the sg_table are contiguous */
+ if (drm_prime_get_contiguous_size(sg) < attach->dmabuf->size) {
+ DRM_ERROR("sg_table is not contiguous");
+ return ERR_PTR(-EINVAL);
+ }
+ mtk_gem = mtk_drm_gem_init(dev, attach->dmabuf->size);
if (IS_ERR(mtk_gem))
return ERR_CAST(mtk_gem);
- expected = sg_dma_address(sg->sgl);
- for_each_sg(sg->sgl, s, sg->nents, i) {
- if (!sg_dma_len(s))
- break;
-
- if (sg_dma_address(s) != expected) {
- DRM_ERROR("sg_table is not contiguous");
- ret = -EINVAL;
- goto err_gem_free;
- }
- expected = sg_dma_address(s) + sg_dma_len(s);
- }
-
mtk_gem->dma_addr = sg_dma_address(sg->sgl);
mtk_gem->sg = sg;
return &mtk_gem->base;
-
-err_gem_free:
- kfree(mtk_gem);
- return ERR_PTR(ret);
}
void *mtk_drm_gem_prime_vmap(struct drm_gem_object *obj)
{
struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
struct sg_table *sgt;
- struct sg_page_iter iter;
unsigned int npages;
- unsigned int i = 0;
if (mtk_gem->kvaddr)
return mtk_gem->kvaddr;
@@ -265,11 +247,8 @@ void *mtk_drm_gem_prime_vmap(struct drm_gem_object *obj)
if (!mtk_gem->pages)
goto out;
- for_each_sg_page(sgt->sgl, &iter, sgt->orig_nents, 0) {
- mtk_gem->pages[i++] = sg_page_iter_page(&iter);
- if (i > npages)
- break;
- }
+ drm_prime_sg_to_page_addr_arrays(sgt, mtk_gem->pages, NULL, npages);
+
mtk_gem->kvaddr = vmap(mtk_gem->pages, npages, VM_MAP,
pgprot_writecombine(PAGE_KERNEL));
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index b2f49152b4d4..8c7ae812b813 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -53,11 +53,10 @@ static void sync_for_device(struct msm_gem_object *msm_obj)
struct device *dev = msm_obj->base.dev->dev;
if (get_dma_ops(dev) && IS_ENABLED(CONFIG_ARM64)) {
- dma_sync_sg_for_device(dev, msm_obj->sgt->sgl,
- msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
+ dma_sync_sgtable_for_device(dev, msm_obj->sgt,
+ DMA_BIDIRECTIONAL);
} else {
- dma_map_sg(dev, msm_obj->sgt->sgl,
- msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
+ dma_map_sgtable(dev, msm_obj->sgt, DMA_BIDIRECTIONAL, 0);
}
}
@@ -66,11 +65,9 @@ static void sync_for_cpu(struct msm_gem_object *msm_obj)
struct device *dev = msm_obj->base.dev->dev;
if (get_dma_ops(dev) && IS_ENABLED(CONFIG_ARM64)) {
- dma_sync_sg_for_cpu(dev, msm_obj->sgt->sgl,
- msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
+ dma_sync_sgtable_for_cpu(dev, msm_obj->sgt, DMA_BIDIRECTIONAL);
} else {
- dma_unmap_sg(dev, msm_obj->sgt->sgl,
- msm_obj->sgt->nents, DMA_BIDIRECTIONAL);
+ dma_unmap_sgtable(dev, msm_obj->sgt, DMA_BIDIRECTIONAL, 0);
}
}
diff --git a/drivers/gpu/drm/msm/msm_gpummu.c b/drivers/gpu/drm/msm/msm_gpummu.c
index 310a31b05faa..53a7348476a1 100644
--- a/drivers/gpu/drm/msm/msm_gpummu.c
+++ b/drivers/gpu/drm/msm/msm_gpummu.c
@@ -30,21 +30,20 @@ static int msm_gpummu_map(struct msm_mmu *mmu, uint64_t iova,
{
struct msm_gpummu *gpummu = to_msm_gpummu(mmu);
unsigned idx = (iova - GPUMMU_VA_START) / GPUMMU_PAGE_SIZE;
- struct scatterlist *sg;
+ struct sg_dma_page_iter dma_iter;
unsigned prot_bits = 0;
- unsigned i, j;
if (prot & IOMMU_WRITE)
prot_bits |= 1;
if (prot & IOMMU_READ)
prot_bits |= 2;
- for_each_sg(sgt->sgl, sg, sgt->nents, i) {
- dma_addr_t addr = sg->dma_address;
- for (j = 0; j < sg->length / GPUMMU_PAGE_SIZE; j++, idx++) {
- gpummu->table[idx] = addr | prot_bits;
- addr += GPUMMU_PAGE_SIZE;
- }
+ for_each_sgtable_dma_page(sgt, &dma_iter, 0) {
+ dma_addr_t addr = sg_page_iter_dma_address(&dma_iter);
+ int i;
+
+ for (i = 0; i < PAGE_SIZE; i += GPUMMU_PAGE_SIZE)
+ gpummu->table[idx++] = (addr + i) | prot_bits;
}
/* we can improve by deferring flush for multiple map() */
diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c
index 3a381a9674c9..6c31e65834c6 100644
--- a/drivers/gpu/drm/msm/msm_iommu.c
+++ b/drivers/gpu/drm/msm/msm_iommu.c
@@ -36,7 +36,7 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova,
struct msm_iommu *iommu = to_msm_iommu(mmu);
size_t ret;
- ret = iommu_map_sg(iommu->domain, iova, sgt->sgl, sgt->nents, prot);
+ ret = iommu_map_sgtable(iommu->domain, iova, sgt, prot);
WARN_ON(!ret);
return (ret == len) ? 0 : -EINVAL;
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 810bf6956568..7b640e05bd4c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -239,8 +239,8 @@ nv50_dp_mode_valid(struct drm_connector *connector,
return MODE_NO_INTERLACE;
max_clock = outp->dp.link_nr * outp->dp.link_bw;
- ds_clock = drm_dp_downstream_max_clock(outp->dp.dpcd,
- outp->dp.downstream_ports);
+ ds_clock = drm_dp_downstream_max_dotclock(outp->dp.dpcd,
+ outp->dp.downstream_ports);
if (ds_clock)
max_clock = min(max_clock, ds_clock);
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index d0d12d5dd76c..f67f223c6479 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -1297,10 +1297,9 @@ struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size,
omap_obj->dma_addr = sg_dma_address(sgt->sgl);
} else {
/* Create pages list from sgt */
- struct sg_page_iter iter;
struct page **pages;
unsigned int npages;
- unsigned int i = 0;
+ unsigned int ret;
npages = DIV_ROUND_UP(size, PAGE_SIZE);
pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL);
@@ -1311,14 +1310,9 @@ struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size,
}
omap_obj->pages = pages;
-
- for_each_sg_page(sgt->sgl, &iter, sgt->orig_nents, 0) {
- pages[i++] = sg_page_iter_page(&iter);
- if (i > npages)
- break;
- }
-
- if (WARN_ON(i != npages)) {
+ ret = drm_prime_sg_to_page_addr_arrays(sgt, pages, NULL,
+ npages);
+ if (ret) {
omap_gem_free_object(obj);
obj = ERR_PTR(-ENOMEM);
goto done;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 33355dd302f1..1a6cea0e0bd7 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -41,8 +41,8 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
for (i = 0; i < n_sgt; i++) {
if (bo->sgts[i].sgl) {
- dma_unmap_sg(pfdev->dev, bo->sgts[i].sgl,
- bo->sgts[i].nents, DMA_BIDIRECTIONAL);
+ dma_unmap_sgtable(pfdev->dev, &bo->sgts[i],
+ DMA_BIDIRECTIONAL, 0);
sg_free_table(&bo->sgts[i]);
}
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index e8f7b11352d2..776448c527ea 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -253,7 +253,7 @@ static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu,
struct io_pgtable_ops *ops = mmu->pgtbl_ops;
u64 start_iova = iova;
- for_each_sg(sgt->sgl, sgl, sgt->nents, count) {
+ for_each_sgtable_dma_sg(sgt, sgl, count) {
unsigned long paddr = sg_dma_address(sgl);
size_t len = sg_dma_len(sgl);
@@ -517,10 +517,9 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
if (ret)
goto err_pages;
- if (!dma_map_sg(pfdev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL)) {
- ret = -EINVAL;
+ ret = dma_map_sgtable(pfdev->dev, sgt, DMA_BIDIRECTIONAL, 0);
+ if (ret)
goto err_map;
- }
mmu_map_sg(pfdev, bomapping->mmu, addr,
IOMMU_WRITE | IOMMU_READ | IOMMU_NOEXEC, sgt);
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index f65d1489dc50..b47e74421e34 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -22,11 +22,11 @@ config DRM_RCAR_CMM
Enable support for R-Car Color Management Module (CMM).
config DRM_RCAR_DW_HDMI
- tristate "R-Car DU Gen3 HDMI Encoder Support"
+ tristate "R-Car Gen3 and RZ/G2 DU HDMI Encoder Support"
depends on DRM && OF
select DRM_DW_HDMI
help
- Enable support for R-Car Gen3 internal HDMI encoder.
+ Enable support for R-Car Gen3 or RZ/G2 internal HDMI encoder.
config DRM_RCAR_LVDS
tristate "R-Car DU LVDS Encoder Support"
@@ -49,3 +49,4 @@ config DRM_RCAR_VSP
config DRM_RCAR_WRITEBACK
bool
default y if ARM64
+ depends on DRM_RCAR_DU
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index f53b0ec71085..447be991fa25 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -186,6 +186,35 @@ static const struct rcar_du_device_info rcar_du_r8a774c0_info = {
.lvds_clk_mask = BIT(1) | BIT(0),
};
+static const struct rcar_du_device_info rcar_du_r8a774e1_info = {
+ .gen = 3,
+ .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
+ | RCAR_DU_FEATURE_VSP1_SOURCE
+ | RCAR_DU_FEATURE_INTERLACED
+ | RCAR_DU_FEATURE_TVM_SYNC,
+ .channels_mask = BIT(3) | BIT(1) | BIT(0),
+ .routes = {
+ /*
+ * R8A774E1 has one RGB output, one LVDS output and one HDMI
+ * output.
+ */
+ [RCAR_DU_OUTPUT_DPAD0] = {
+ .possible_crtcs = BIT(2),
+ .port = 0,
+ },
+ [RCAR_DU_OUTPUT_HDMI0] = {
+ .possible_crtcs = BIT(1),
+ .port = 1,
+ },
+ [RCAR_DU_OUTPUT_LVDS0] = {
+ .possible_crtcs = BIT(0),
+ .port = 2,
+ },
+ },
+ .num_lvds = 1,
+ .dpll_mask = BIT(1),
+};
+
static const struct rcar_du_device_info rcar_du_r8a7779_info = {
.gen = 1,
.features = RCAR_DU_FEATURE_INTERLACED
@@ -216,8 +245,9 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = {
.channels_mask = BIT(2) | BIT(1) | BIT(0),
.routes = {
/*
- * R8A7790 has one RGB output, two LVDS outputs and one
- * (currently unsupported) TCON output.
+ * R8A7742 and R8A7790 each have one RGB output and two LVDS
+ * outputs. Additionally R8A7790 supports one TCON output
+ * (currently unsupported by the driver).
*/
[RCAR_DU_OUTPUT_DPAD0] = {
.possible_crtcs = BIT(2) | BIT(1) | BIT(0),
@@ -443,6 +473,7 @@ static const struct rcar_du_device_info rcar_du_r8a7799x_info = {
};
static const struct of_device_id rcar_du_of_table[] = {
+ { .compatible = "renesas,du-r8a7742", .data = &rcar_du_r8a7790_info },
{ .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info },
{ .compatible = "renesas,du-r8a7744", .data = &rzg1_du_r8a7743_info },
{ .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info },
@@ -450,6 +481,7 @@ static const struct of_device_id rcar_du_of_table[] = {
{ .compatible = "renesas,du-r8a774a1", .data = &rcar_du_r8a774a1_info },
{ .compatible = "renesas,du-r8a774b1", .data = &rcar_du_r8a774b1_info },
{ .compatible = "renesas,du-r8a774c0", .data = &rcar_du_r8a774c0_info },
+ { .compatible = "renesas,du-r8a774e1", .data = &rcar_du_r8a774e1_info },
{ .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
{ .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
{ .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info },
@@ -458,6 +490,7 @@ static const struct of_device_id rcar_du_of_table[] = {
{ .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info },
{ .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info },
{ .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info },
+ { .compatible = "renesas,du-r8a77961", .data = &rcar_du_r8a7796_info },
{ .compatible = "renesas,du-r8a77965", .data = &rcar_du_r8a77965_info },
{ .compatible = "renesas,du-r8a77970", .data = &rcar_du_r8a77970_info },
{ .compatible = "renesas,du-r8a77980", .data = &rcar_du_r8a77970_info },
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index 482329102f19..72dda446355f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -40,6 +40,7 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
.v4l2 = V4L2_PIX_FMT_RGB565,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
.pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
.edf = PnDDCR4_EDF_NONE,
}, {
@@ -47,6 +48,7 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
.v4l2 = V4L2_PIX_FMT_ARGB555,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
.edf = PnDDCR4_EDF_NONE,
}, {
@@ -61,6 +63,7 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
.v4l2 = V4L2_PIX_FMT_XBGR32,
.bpp = 32,
.planes = 1,
+ .hsub = 1,
.pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
.edf = PnDDCR4_EDF_RGB888,
}, {
@@ -68,6 +71,7 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
.v4l2 = V4L2_PIX_FMT_ABGR32,
.bpp = 32,
.planes = 1,
+ .hsub = 1,
.pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP,
.edf = PnDDCR4_EDF_ARGB8888,
}, {
@@ -75,6 +79,7 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
.v4l2 = V4L2_PIX_FMT_UYVY,
.bpp = 16,
.planes = 1,
+ .hsub = 2,
.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
.edf = PnDDCR4_EDF_NONE,
}, {
@@ -82,6 +87,7 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
.v4l2 = V4L2_PIX_FMT_YUYV,
.bpp = 16,
.planes = 1,
+ .hsub = 2,
.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
.edf = PnDDCR4_EDF_NONE,
}, {
@@ -89,6 +95,7 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
.v4l2 = V4L2_PIX_FMT_NV12M,
.bpp = 12,
.planes = 2,
+ .hsub = 2,
.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
.edf = PnDDCR4_EDF_NONE,
}, {
@@ -96,6 +103,7 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
.v4l2 = V4L2_PIX_FMT_NV21M,
.bpp = 12,
.planes = 2,
+ .hsub = 2,
.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
.edf = PnDDCR4_EDF_NONE,
}, {
@@ -103,6 +111,7 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
.v4l2 = V4L2_PIX_FMT_NV16M,
.bpp = 16,
.planes = 2,
+ .hsub = 2,
.pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
.edf = PnDDCR4_EDF_NONE,
},
@@ -115,156 +124,187 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
.v4l2 = V4L2_PIX_FMT_RGB332,
.bpp = 8,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_ARGB4444,
.v4l2 = V4L2_PIX_FMT_ARGB444,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_XRGB4444,
.v4l2 = V4L2_PIX_FMT_XRGB444,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_RGBA4444,
.v4l2 = V4L2_PIX_FMT_RGBA444,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_RGBX4444,
.v4l2 = V4L2_PIX_FMT_RGBX444,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_ABGR4444,
.v4l2 = V4L2_PIX_FMT_ABGR444,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_XBGR4444,
.v4l2 = V4L2_PIX_FMT_XBGR444,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_BGRA4444,
.v4l2 = V4L2_PIX_FMT_BGRA444,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_BGRX4444,
.v4l2 = V4L2_PIX_FMT_BGRX444,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_RGBA5551,
.v4l2 = V4L2_PIX_FMT_RGBA555,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_RGBX5551,
.v4l2 = V4L2_PIX_FMT_RGBX555,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_ABGR1555,
.v4l2 = V4L2_PIX_FMT_ABGR555,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_XBGR1555,
.v4l2 = V4L2_PIX_FMT_XBGR555,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_BGRA5551,
.v4l2 = V4L2_PIX_FMT_BGRA555,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_BGRX5551,
.v4l2 = V4L2_PIX_FMT_BGRX555,
.bpp = 16,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_BGR888,
.v4l2 = V4L2_PIX_FMT_RGB24,
.bpp = 24,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_RGB888,
.v4l2 = V4L2_PIX_FMT_BGR24,
.bpp = 24,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_RGBA8888,
.v4l2 = V4L2_PIX_FMT_BGRA32,
.bpp = 32,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_RGBX8888,
.v4l2 = V4L2_PIX_FMT_BGRX32,
.bpp = 32,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_ABGR8888,
.v4l2 = V4L2_PIX_FMT_RGBA32,
.bpp = 32,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_XBGR8888,
.v4l2 = V4L2_PIX_FMT_RGBX32,
.bpp = 32,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_BGRA8888,
.v4l2 = V4L2_PIX_FMT_ARGB32,
.bpp = 32,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_BGRX8888,
.v4l2 = V4L2_PIX_FMT_XRGB32,
.bpp = 32,
.planes = 1,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_YVYU,
.v4l2 = V4L2_PIX_FMT_YVYU,
.bpp = 16,
.planes = 1,
+ .hsub = 2,
}, {
.fourcc = DRM_FORMAT_NV61,
.v4l2 = V4L2_PIX_FMT_NV61M,
.bpp = 16,
.planes = 2,
+ .hsub = 2,
}, {
.fourcc = DRM_FORMAT_YUV420,
.v4l2 = V4L2_PIX_FMT_YUV420M,
.bpp = 12,
.planes = 3,
+ .hsub = 2,
}, {
.fourcc = DRM_FORMAT_YVU420,
.v4l2 = V4L2_PIX_FMT_YVU420M,
.bpp = 12,
.planes = 3,
+ .hsub = 2,
}, {
.fourcc = DRM_FORMAT_YUV422,
.v4l2 = V4L2_PIX_FMT_YUV422M,
.bpp = 16,
.planes = 3,
+ .hsub = 2,
}, {
.fourcc = DRM_FORMAT_YVU422,
.v4l2 = V4L2_PIX_FMT_YVU422M,
.bpp = 16,
.planes = 3,
+ .hsub = 2,
}, {
.fourcc = DRM_FORMAT_YUV444,
.v4l2 = V4L2_PIX_FMT_YUV444M,
.bpp = 24,
.planes = 3,
+ .hsub = 1,
}, {
.fourcc = DRM_FORMAT_YVU444,
.v4l2 = V4L2_PIX_FMT_YVU444M,
.bpp = 24,
.planes = 3,
+ .hsub = 1,
},
};
@@ -311,6 +351,7 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
{
struct rcar_du_device *rcdu = dev->dev_private;
const struct rcar_du_format_info *format;
+ unsigned int chroma_pitch;
unsigned int max_pitch;
unsigned int align;
unsigned int i;
@@ -353,10 +394,19 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
return ERR_PTR(-EINVAL);
}
+ /*
+ * Calculate the chroma plane(s) pitch using the horizontal subsampling
+ * factor. For semi-planar formats, the U and V planes are combined, the
+ * pitch must thus be doubled.
+ */
+ chroma_pitch = mode_cmd->pitches[0] / format->hsub;
+ if (format->planes == 2)
+ chroma_pitch *= 2;
+
for (i = 1; i < format->planes; ++i) {
- if (mode_cmd->pitches[i] != mode_cmd->pitches[0]) {
+ if (mode_cmd->pitches[i] != chroma_pitch) {
dev_dbg(dev->dev,
- "luma and chroma pitches do not match\n");
+ "luma and chroma pitches are not compatible\n");
return ERR_PTR(-EINVAL);
}
}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
index 0346504d8c59..8f5fff176754 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
@@ -22,6 +22,7 @@ struct rcar_du_format_info {
u32 v4l2;
unsigned int bpp;
unsigned int planes;
+ unsigned int hsub;
unsigned int pnmr;
unsigned int edf;
};
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
index f1a81c9b184d..f6a69aa116e6 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
@@ -13,6 +13,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_managed.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_vblank.h>
@@ -197,9 +198,8 @@ int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
goto fail;
ret = vsp1_du_map_sg(vsp->vsp, sgt);
- if (!ret) {
+ if (ret) {
sg_free_table(sgt);
- ret = -ENOMEM;
goto fail;
}
}
@@ -279,7 +279,7 @@ static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
if (plane->state->visible)
rcar_du_vsp_plane_setup(rplane);
- else
+ else if (old_state->crtc)
vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
rplane->index, NULL);
}
@@ -341,6 +341,13 @@ static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
.atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
};
+static void rcar_du_vsp_cleanup(struct drm_device *dev, void *res)
+{
+ struct rcar_du_vsp *vsp = res;
+
+ put_device(vsp->vsp);
+}
+
int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
unsigned int crtcs)
{
@@ -357,6 +364,10 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
vsp->vsp = &pdev->dev;
+ ret = drmm_add_action(rcdu->ddev, rcar_du_vsp_cleanup, vsp);
+ if (ret < 0)
+ return ret;
+
ret = vsp1_du_init(vsp->vsp);
if (ret < 0)
return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c
index bced729a96fe..70dbbe44bb23 100644
--- a/drivers/gpu/drm/rcar-du/rcar_lvds.c
+++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c
@@ -978,11 +978,13 @@ static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = {
};
static const struct of_device_id rcar_lvds_of_table[] = {
+ { .compatible = "renesas,r8a7742-lvds", .data = &rcar_lvds_gen2_info },
{ .compatible = "renesas,r8a7743-lvds", .data = &rcar_lvds_gen2_info },
{ .compatible = "renesas,r8a7744-lvds", .data = &rcar_lvds_gen2_info },
{ .compatible = "renesas,r8a774a1-lvds", .data = &rcar_lvds_gen3_info },
{ .compatible = "renesas,r8a774b1-lvds", .data = &rcar_lvds_gen3_info },
{ .compatible = "renesas,r8a774c0-lvds", .data = &rcar_lvds_r8a77990_info },
+ { .compatible = "renesas,r8a774e1-lvds", .data = &rcar_lvds_gen3_info },
{ .compatible = "renesas,r8a7790-lvds", .data = &rcar_lvds_gen2_info },
{ .compatible = "renesas,r8a7791-lvds", .data = &rcar_lvds_gen2_info },
{ .compatible = "renesas,r8a7793-lvds", .data = &rcar_lvds_gen2_info },
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index b9275ba7c5a5..cb50f2ba2e46 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -36,8 +36,8 @@ static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj)
rk_obj->dma_addr = rk_obj->mm.start;
- ret = iommu_map_sg(private->domain, rk_obj->dma_addr, rk_obj->sgt->sgl,
- rk_obj->sgt->nents, prot);
+ ret = iommu_map_sgtable(private->domain, rk_obj->dma_addr, rk_obj->sgt,
+ prot);
if (ret < rk_obj->base.size) {
DRM_ERROR("failed to map buffer: size=%zd request_size=%zd\n",
ret, rk_obj->base.size);
@@ -98,11 +98,10 @@ static int rockchip_gem_get_pages(struct rockchip_gem_object *rk_obj)
* TODO: Replace this by drm_clflush_sg() once it can be implemented
* without relying on symbols that are not exported.
*/
- for_each_sg(rk_obj->sgt->sgl, s, rk_obj->sgt->nents, i)
+ for_each_sgtable_sg(rk_obj->sgt, s, i)
sg_dma_address(s) = sg_phys(s);
- dma_sync_sg_for_device(drm->dev, rk_obj->sgt->sgl, rk_obj->sgt->nents,
- DMA_TO_DEVICE);
+ dma_sync_sgtable_for_device(drm->dev, rk_obj->sgt, DMA_TO_DEVICE);
return 0;
@@ -350,8 +349,8 @@ void rockchip_gem_free_object(struct drm_gem_object *obj)
if (private->domain) {
rockchip_gem_iommu_unmap(rk_obj);
} else {
- dma_unmap_sg(drm->dev, rk_obj->sgt->sgl,
- rk_obj->sgt->nents, DMA_BIDIRECTIONAL);
+ dma_unmap_sgtable(drm->dev, rk_obj->sgt,
+ DMA_BIDIRECTIONAL, 0);
}
drm_prime_gem_destroy(obj, rk_obj->sgt);
} else {
@@ -460,23 +459,6 @@ struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
return sgt;
}
-static unsigned long rockchip_sg_get_contiguous_size(struct sg_table *sgt,
- int count)
-{
- struct scatterlist *s;
- dma_addr_t expected = sg_dma_address(sgt->sgl);
- unsigned int i;
- unsigned long size = 0;
-
- for_each_sg(sgt->sgl, s, count, i) {
- if (sg_dma_address(s) != expected)
- break;
- expected = sg_dma_address(s) + sg_dma_len(s);
- size += sg_dma_len(s);
- }
- return size;
-}
-
static int
rockchip_gem_iommu_map_sg(struct drm_device *drm,
struct dma_buf_attachment *attach,
@@ -493,15 +475,13 @@ rockchip_gem_dma_map_sg(struct drm_device *drm,
struct sg_table *sg,
struct rockchip_gem_object *rk_obj)
{
- int count = dma_map_sg(drm->dev, sg->sgl, sg->nents,
- DMA_BIDIRECTIONAL);
- if (!count)
- return -EINVAL;
+ int err = dma_map_sgtable(drm->dev, sg, DMA_BIDIRECTIONAL, 0);
+ if (err)
+ return err;
- if (rockchip_sg_get_contiguous_size(sg, count) < attach->dmabuf->size) {
+ if (drm_prime_get_contiguous_size(sg) < attach->dmabuf->size) {
DRM_ERROR("failed to map sg_table to contiguous linear address.\n");
- dma_unmap_sg(drm->dev, sg->sgl, sg->nents,
- DMA_BIDIRECTIONAL);
+ dma_unmap_sgtable(drm->dev, sg, DMA_BIDIRECTIONAL, 0);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
index bd990d178765..1d696ec001cf 100644
--- a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
+++ b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
@@ -5,6 +5,8 @@
#define PREFIX_STR "[drm_dp_mst_helper]"
+#include <linux/random.h>
+
#include <drm/drm_dp_mst_helper.h>
#include <drm/drm_print.h>
@@ -237,6 +239,21 @@ int igt_dp_mst_sideband_msg_req_decode(void *unused)
in.u.i2c_write.bytes = data;
DO_TEST();
+ in.req_type = DP_QUERY_STREAM_ENC_STATUS;
+ in.u.enc_status.stream_id = 1;
+ DO_TEST();
+ get_random_bytes(in.u.enc_status.client_id,
+ sizeof(in.u.enc_status.client_id));
+ DO_TEST();
+ in.u.enc_status.stream_event = 3;
+ DO_TEST();
+ in.u.enc_status.valid_stream_event = 0;
+ DO_TEST();
+ in.u.enc_status.stream_behavior = 3;
+ DO_TEST();
+ in.u.enc_status.valid_stream_behavior = 1;
+ DO_TEST();
+
#undef DO_TEST
return 0;
}
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index b25443255be6..f38de08e0c95 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -12,6 +12,7 @@
#include <linux/gpio/consumer.h>
#include <drm/drm_atomic.h>
+#include <drm/drm_bridge.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_fb_helper.h>
@@ -116,6 +117,7 @@ struct tegra_output {
struct device_node *of_node;
struct device *dev;
+ struct drm_bridge *bridge;
struct drm_panel *panel;
struct i2c_adapter *ddc;
const struct edid *edid;
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c
index 723df142a981..01d94befab11 100644
--- a/drivers/gpu/drm/tegra/gem.c
+++ b/drivers/gpu/drm/tegra/gem.c
@@ -98,8 +98,8 @@ static struct sg_table *tegra_bo_pin(struct device *dev, struct host1x_bo *bo,
* the SG table needs to be copied to avoid overwriting any
* other potential users of the original SG table.
*/
- err = sg_alloc_table_from_sg(sgt, obj->sgt->sgl, obj->sgt->nents,
- GFP_KERNEL);
+ err = sg_alloc_table_from_sg(sgt, obj->sgt->sgl,
+ obj->sgt->orig_nents, GFP_KERNEL);
if (err < 0)
goto free;
} else {
@@ -196,8 +196,7 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
bo->iova = bo->mm->start;
- bo->size = iommu_map_sg(tegra->domain, bo->iova, bo->sgt->sgl,
- bo->sgt->nents, prot);
+ bo->size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot);
if (!bo->size) {
dev_err(tegra->drm->dev, "failed to map buffer\n");
err = -ENOMEM;
@@ -264,8 +263,7 @@ free:
static void tegra_bo_free(struct drm_device *drm, struct tegra_bo *bo)
{
if (bo->pages) {
- dma_unmap_sg(drm->dev, bo->sgt->sgl, bo->sgt->nents,
- DMA_FROM_DEVICE);
+ dma_unmap_sgtable(drm->dev, bo->sgt, DMA_FROM_DEVICE, 0);
drm_gem_put_pages(&bo->gem, bo->pages, true, true);
sg_free_table(bo->sgt);
kfree(bo->sgt);
@@ -290,12 +288,9 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo)
goto put_pages;
}
- err = dma_map_sg(drm->dev, bo->sgt->sgl, bo->sgt->nents,
- DMA_FROM_DEVICE);
- if (err == 0) {
- err = -EFAULT;
+ err = dma_map_sgtable(drm->dev, bo->sgt, DMA_FROM_DEVICE, 0);
+ if (err)
goto free_sgt;
- }
return 0;
@@ -571,7 +566,7 @@ tegra_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
goto free;
}
- if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
+ if (dma_map_sgtable(attach->dev, sgt, dir, 0))
goto free;
return sgt;
@@ -590,7 +585,7 @@ static void tegra_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach,
struct tegra_bo *bo = to_tegra_bo(gem);
if (bo->pages)
- dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
+ dma_unmap_sgtable(attach->dev, sgt, dir, 0);
sg_free_table(sgt);
kfree(sgt);
@@ -609,8 +604,7 @@ static int tegra_gem_prime_begin_cpu_access(struct dma_buf *buf,
struct drm_device *drm = gem->dev;
if (bo->pages)
- dma_sync_sg_for_cpu(drm->dev, bo->sgt->sgl, bo->sgt->nents,
- DMA_FROM_DEVICE);
+ dma_sync_sgtable_for_cpu(drm->dev, bo->sgt, DMA_FROM_DEVICE);
return 0;
}
@@ -623,8 +617,7 @@ static int tegra_gem_prime_end_cpu_access(struct dma_buf *buf,
struct drm_device *drm = gem->dev;
if (bo->pages)
- dma_sync_sg_for_device(drm->dev, bo->sgt->sgl, bo->sgt->nents,
- DMA_TO_DEVICE);
+ dma_sync_sgtable_for_device(drm->dev, bo->sgt, DMA_TO_DEVICE);
return 0;
}
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index a3adb9e4debf..5a4fd0dbf4cf 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -5,6 +5,7 @@
*/
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_simple_kms_helper.h>
@@ -99,27 +100,38 @@ int tegra_output_probe(struct tegra_output *output)
if (!output->of_node)
output->of_node = output->dev->of_node;
+ err = drm_of_find_panel_or_bridge(output->of_node, -1, -1,
+ &output->panel, &output->bridge);
+ if (err && err != -ENODEV)
+ return err;
+
panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
if (panel) {
+ /*
+ * Don't mix nvidia,panel phandle with the graph in a
+ * device-tree.
+ */
+ WARN_ON(output->panel || output->bridge);
+
output->panel = of_drm_find_panel(panel);
+ of_node_put(panel);
+
if (IS_ERR(output->panel))
return PTR_ERR(output->panel);
-
- of_node_put(panel);
}
output->edid = of_get_property(output->of_node, "nvidia,edid", &size);
ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
if (ddc) {
- output->ddc = of_find_i2c_adapter_by_node(ddc);
+ output->ddc = of_get_i2c_adapter_by_node(ddc);
+ of_node_put(ddc);
+
if (!output->ddc) {
err = -EPROBE_DEFER;
of_node_put(ddc);
return err;
}
-
- of_node_put(ddc);
}
output->hpd_gpio = devm_gpiod_get_from_of_node(output->dev,
@@ -173,7 +185,7 @@ void tegra_output_remove(struct tegra_output *output)
free_irq(output->hpd_irq, output);
if (output->ddc)
- put_device(&output->ddc->dev);
+ i2c_put_adapter(output->ddc);
}
int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c
index 4cd0461cc508..539d14935728 100644
--- a/drivers/gpu/drm/tegra/plane.c
+++ b/drivers/gpu/drm/tegra/plane.c
@@ -131,12 +131,9 @@ static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
}
if (sgt) {
- err = dma_map_sg(dc->dev, sgt->sgl, sgt->nents,
- DMA_TO_DEVICE);
- if (err == 0) {
- err = -ENOMEM;
+ err = dma_map_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
+ if (err)
goto unpin;
- }
/*
* The display controller needs contiguous memory, so
@@ -144,7 +141,7 @@ static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
* map its SG table to a single contiguous chunk of
* I/O virtual memory.
*/
- if (err > 1) {
+ if (sgt->nents > 1) {
err = -EINVAL;
goto unpin;
}
@@ -166,8 +163,7 @@ unpin:
struct sg_table *sgt = state->sgt[i];
if (sgt)
- dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents,
- DMA_TO_DEVICE);
+ dma_unmap_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
host1x_bo_unpin(dc->dev, &bo->base, sgt);
state->iova[i] = DMA_MAPPING_ERROR;
@@ -186,8 +182,7 @@ static void tegra_dc_unpin(struct tegra_dc *dc, struct tegra_plane_state *state)
struct sg_table *sgt = state->sgt[i];
if (sgt)
- dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents,
- DMA_TO_DEVICE);
+ dma_unmap_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
host1x_bo_unpin(dc->dev, &bo->base, sgt);
state->iova[i] = DMA_MAPPING_ERROR;
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 0562a7eb793f..4142a56ca764 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -7,7 +7,7 @@
#include <linux/clk.h>
#include <drm/drm_atomic_helper.h>
-#include <drm/drm_panel.h>
+#include <drm/drm_bridge_connector.h>
#include <drm/drm_simple_kms_helper.h>
#include "drm.h"
@@ -85,45 +85,13 @@ static void tegra_dc_write_regs(struct tegra_dc *dc,
tegra_dc_writel(dc, table[i].value, table[i].offset);
}
-static const struct drm_connector_funcs tegra_rgb_connector_funcs = {
- .reset = drm_atomic_helper_connector_reset,
- .detect = tegra_output_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = tegra_output_connector_destroy,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static enum drm_mode_status
-tegra_rgb_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- /*
- * FIXME: For now, always assume that the mode is okay. There are
- * unresolved issues with clk_round_rate(), which doesn't always
- * reliably report whether a frequency can be set or not.
- */
- return MODE_OK;
-}
-
-static const struct drm_connector_helper_funcs tegra_rgb_connector_helper_funcs = {
- .get_modes = tegra_output_connector_get_modes,
- .mode_valid = tegra_rgb_connector_mode_valid,
-};
-
static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
{
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_rgb *rgb = to_rgb(output);
- if (output->panel)
- drm_panel_disable(output->panel);
-
tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));
tegra_dc_commit(rgb->dc);
-
- if (output->panel)
- drm_panel_unprepare(output->panel);
}
static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
@@ -132,9 +100,6 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
struct tegra_rgb *rgb = to_rgb(output);
u32 value;
- if (output->panel)
- drm_panel_prepare(output->panel);
-
tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable));
value = DE_SELECT_ACTIVE | DE_CONTROL_NORMAL;
@@ -156,9 +121,6 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
tegra_dc_commit(rgb->dc);
-
- if (output->panel)
- drm_panel_enable(output->panel);
}
static int
@@ -267,24 +229,68 @@ int tegra_dc_rgb_remove(struct tegra_dc *dc)
int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
{
struct tegra_output *output = dc->rgb;
+ struct drm_connector *connector;
int err;
if (!dc->rgb)
return -ENODEV;
- drm_connector_init(drm, &output->connector, &tegra_rgb_connector_funcs,
- DRM_MODE_CONNECTOR_LVDS);
- drm_connector_helper_add(&output->connector,
- &tegra_rgb_connector_helper_funcs);
- output->connector.dpms = DRM_MODE_DPMS_OFF;
-
drm_simple_encoder_init(drm, &output->encoder, DRM_MODE_ENCODER_LVDS);
drm_encoder_helper_add(&output->encoder,
&tegra_rgb_encoder_helper_funcs);
- drm_connector_attach_encoder(&output->connector,
- &output->encoder);
- drm_connector_register(&output->connector);
+ /*
+ * Wrap directly-connected panel into DRM bridge in order to let
+ * DRM core to handle panel for us.
+ */
+ if (output->panel) {
+ output->bridge = devm_drm_panel_bridge_add(output->dev,
+ output->panel);
+ if (IS_ERR(output->bridge)) {
+ dev_err(output->dev,
+ "failed to wrap panel into bridge: %pe\n",
+ output->bridge);
+ return PTR_ERR(output->bridge);
+ }
+
+ output->panel = NULL;
+ }
+
+ /*
+ * Tegra devices that have LVDS panel utilize LVDS encoder bridge
+ * for converting up to 28 LCD LVTTL lanes into 5/4 LVDS lanes that
+ * go to display panel's receiver.
+ *
+ * Encoder usually have a power-down control which needs to be enabled
+ * in order to transmit data to the panel. Historically devices that
+ * use an older device-tree version didn't model the bridge, assuming
+ * that encoder is turned ON by default, while today's DRM allows us
+ * to model LVDS encoder properly.
+ *
+ * Newer device-trees utilize LVDS encoder bridge, which provides
+ * us with a connector and handles the display panel.
+ *
+ * For older device-trees we wrapped panel into the panel-bridge.
+ */
+ if (output->bridge) {
+ err = drm_bridge_attach(&output->encoder, output->bridge,
+ NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (err) {
+ dev_err(output->dev, "failed to attach bridge: %d\n",
+ err);
+ return err;
+ }
+
+ connector = drm_bridge_connector_init(drm, &output->encoder);
+ if (IS_ERR(connector)) {
+ dev_err(output->dev,
+ "failed to initialize bridge connector: %pe\n",
+ connector);
+ return PTR_ERR(connector);
+ }
+
+ drm_connector_attach_encoder(connector, &output->encoder);
+ }
err = tegra_output_init(drm, output);
if (err < 0) {
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 45b5258c77a2..e88a17c2937f 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -3728,7 +3728,12 @@ static int tegra_sor_probe(struct platform_device *pdev)
if (!sor->aux)
return -EPROBE_DEFER;
- sor->output.ddc = &sor->aux->ddc;
+ if (get_device(&sor->aux->ddc.dev)) {
+ if (try_module_get(sor->aux->ddc.owner))
+ sor->output.ddc = &sor->aux->ddc;
+ else
+ put_device(&sor->aux->ddc.dev);
+ }
}
if (!sor->aux) {
diff --git a/drivers/gpu/drm/v3d/v3d_mmu.c b/drivers/gpu/drm/v3d/v3d_mmu.c
index 3b81ea28c0bb..5a453532901f 100644
--- a/drivers/gpu/drm/v3d/v3d_mmu.c
+++ b/drivers/gpu/drm/v3d/v3d_mmu.c
@@ -90,18 +90,17 @@ void v3d_mmu_insert_ptes(struct v3d_bo *bo)
struct v3d_dev *v3d = to_v3d_dev(shmem_obj->base.dev);
u32 page = bo->node.start;
u32 page_prot = V3D_PTE_WRITEABLE | V3D_PTE_VALID;
- unsigned int count;
- struct scatterlist *sgl;
+ struct sg_dma_page_iter dma_iter;
- for_each_sg(shmem_obj->sgt->sgl, sgl, shmem_obj->sgt->nents, count) {
- u32 page_address = sg_dma_address(sgl) >> V3D_MMU_PAGE_SHIFT;
+ for_each_sgtable_dma_page(shmem_obj->sgt, &dma_iter, 0) {
+ dma_addr_t dma_addr = sg_page_iter_dma_address(&dma_iter);
+ u32 page_address = dma_addr >> V3D_MMU_PAGE_SHIFT;
u32 pte = page_prot | page_address;
u32 i;
- BUG_ON(page_address + (sg_dma_len(sgl) >> V3D_MMU_PAGE_SHIFT) >=
+ BUG_ON(page_address + (PAGE_SIZE >> V3D_MMU_PAGE_SHIFT) >=
BIT(24));
-
- for (i = 0; i < sg_dma_len(sgl) >> V3D_MMU_PAGE_SHIFT; i++)
+ for (i = 0; i < PAGE_SIZE >> V3D_MMU_PAGE_SHIFT; i++)
v3d->pt[page++] = pte + i;
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index 842f8b61aa89..00d6b95e259d 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -72,9 +72,8 @@ void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo)
if (shmem->pages) {
if (shmem->mapped) {
- dma_unmap_sg(vgdev->vdev->dev.parent,
- shmem->pages->sgl, shmem->mapped,
- DMA_TO_DEVICE);
+ dma_unmap_sgtable(vgdev->vdev->dev.parent,
+ shmem->pages, DMA_TO_DEVICE, 0);
shmem->mapped = 0;
}
@@ -164,13 +163,13 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
}
if (use_dma_api) {
- shmem->mapped = dma_map_sg(vgdev->vdev->dev.parent,
- shmem->pages->sgl,
- shmem->pages->nents,
- DMA_TO_DEVICE);
- *nents = shmem->mapped;
+ ret = dma_map_sgtable(vgdev->vdev->dev.parent,
+ shmem->pages, DMA_TO_DEVICE, 0);
+ if (ret)
+ return ret;
+ *nents = shmem->mapped = shmem->pages->nents;
} else {
- *nents = shmem->pages->nents;
+ *nents = shmem->pages->orig_nents;
}
*ents = kmalloc_array(*nents, sizeof(struct virtio_gpu_mem_entry),
@@ -180,13 +179,20 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
return -ENOMEM;
}
- for_each_sg(shmem->pages->sgl, sg, *nents, si) {
- (*ents)[si].addr = cpu_to_le64(use_dma_api
- ? sg_dma_address(sg)
- : sg_phys(sg));
- (*ents)[si].length = cpu_to_le32(sg->length);
- (*ents)[si].padding = 0;
+ if (use_dma_api) {
+ for_each_sgtable_dma_sg(shmem->pages, sg, si) {
+ (*ents)[si].addr = cpu_to_le64(sg_dma_address(sg));
+ (*ents)[si].length = cpu_to_le32(sg_dma_len(sg));
+ (*ents)[si].padding = 0;
+ }
+ } else {
+ for_each_sgtable_sg(shmem->pages, sg, si) {
+ (*ents)[si].addr = cpu_to_le64(sg_phys(sg));
+ (*ents)[si].length = cpu_to_le32(sg->length);
+ (*ents)[si].padding = 0;
+ }
}
+
return 0;
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index c93c2db35aaf..651d1b0e8e8d 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -302,7 +302,7 @@ static struct sg_table *vmalloc_to_sgt(char *data, uint32_t size, int *sg_ents)
return NULL;
}
- for_each_sg(sgt->sgl, sg, *sg_ents, i) {
+ for_each_sgtable_sg(sgt, sg, i) {
pg = vmalloc_to_page(data);
if (!pg) {
sg_free_table(sgt);
@@ -603,9 +603,8 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo);
if (use_dma_api)
- dma_sync_sg_for_device(vgdev->vdev->dev.parent,
- shmem->pages->sgl, shmem->pages->nents,
- DMA_TO_DEVICE);
+ dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
+ shmem->pages, DMA_TO_DEVICE);
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
@@ -1019,9 +1018,8 @@ void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo);
if (use_dma_api)
- dma_sync_sg_for_device(vgdev->vdev->dev.parent,
- shmem->pages->sgl, shmem->pages->nents,
- DMA_TO_DEVICE);
+ dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
+ shmem->pages, DMA_TO_DEVICE);
cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
memset(cmd_p, 0, sizeof(*cmd_p));
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c
index c7f10b2c93d2..13c31e2d7254 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c
@@ -362,8 +362,7 @@ static void vmw_ttm_unmap_from_dma(struct vmw_ttm_tt *vmw_tt)
{
struct device *dev = vmw_tt->dev_priv->dev->dev;
- dma_unmap_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.nents,
- DMA_BIDIRECTIONAL);
+ dma_unmap_sgtable(dev, &vmw_tt->sgt, DMA_BIDIRECTIONAL, 0);
vmw_tt->sgt.nents = vmw_tt->sgt.orig_nents;
}
@@ -383,16 +382,8 @@ static void vmw_ttm_unmap_from_dma(struct vmw_ttm_tt *vmw_tt)
static int vmw_ttm_map_for_dma(struct vmw_ttm_tt *vmw_tt)
{
struct device *dev = vmw_tt->dev_priv->dev->dev;
- int ret;
-
- ret = dma_map_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.orig_nents,
- DMA_BIDIRECTIONAL);
- if (unlikely(ret == 0))
- return -ENOMEM;
- vmw_tt->sgt.nents = ret;
-
- return 0;
+ return dma_map_sgtable(dev, &vmw_tt->sgt, DMA_BIDIRECTIONAL, 0);
}
/**
@@ -449,10 +440,10 @@ static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt)
if (unlikely(ret != 0))
goto out_sg_alloc_fail;
- if (vsgt->num_pages > vmw_tt->sgt.nents) {
+ if (vsgt->num_pages > vmw_tt->sgt.orig_nents) {
uint64_t over_alloc =
sgl_size * (vsgt->num_pages -
- vmw_tt->sgt.nents);
+ vmw_tt->sgt.orig_nents);
ttm_mem_global_free(glob, over_alloc);
vmw_tt->sg_alloc_size -= over_alloc;
diff --git a/drivers/gpu/drm/xen/xen_drm_front_gem.c b/drivers/gpu/drm/xen/xen_drm_front_gem.c
index 534daf37c97e..a487384d5cb7 100644
--- a/drivers/gpu/drm/xen/xen_drm_front_gem.c
+++ b/drivers/gpu/drm/xen/xen_drm_front_gem.c
@@ -217,7 +217,7 @@ xen_drm_front_gem_import_sg_table(struct drm_device *dev,
return ERR_PTR(ret);
DRM_DEBUG("Imported buffer of size %zu with nents %u\n",
- size, sgt->nents);
+ size, sgt->orig_nents);
return &xen_obj->base;
}
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c
index 89b6c14b7392..82d0a60ba3f7 100644
--- a/drivers/gpu/host1x/job.c
+++ b/drivers/gpu/host1x/job.c
@@ -170,11 +170,9 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto unpin;
}
- err = dma_map_sg(dev, sgt->sgl, sgt->nents, dir);
- if (!err) {
- err = -ENOMEM;
+ err = dma_map_sgtable(dev, sgt, dir, 0);
+ if (err)
goto unpin;
- }
job->unpins[job->num_unpins].dev = dev;
job->unpins[job->num_unpins].dir = dir;
@@ -228,7 +226,7 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
}
if (host->domain) {
- for_each_sg(sgt->sgl, sg, sgt->nents, j)
+ for_each_sgtable_sg(sgt, sg, j)
gather_size += sg->length;
gather_size = iova_align(&host->iova, gather_size);
@@ -240,9 +238,9 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto put;
}
- err = iommu_map_sg(host->domain,
+ err = iommu_map_sgtable(host->domain,
iova_dma_addr(&host->iova, alloc),
- sgt->sgl, sgt->nents, IOMMU_READ);
+ sgt, IOMMU_READ);
if (err == 0) {
__free_iova(&host->iova, alloc);
err = -EINVAL;
@@ -252,12 +250,9 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
job->unpins[job->num_unpins].size = gather_size;
phys_addr = iova_dma_addr(&host->iova, alloc);
} else if (sgt) {
- err = dma_map_sg(host->dev, sgt->sgl, sgt->nents,
- DMA_TO_DEVICE);
- if (!err) {
- err = -ENOMEM;
+ err = dma_map_sgtable(host->dev, sgt, DMA_TO_DEVICE, 0);
+ if (err)
goto put;
- }
job->unpins[job->num_unpins].dir = DMA_TO_DEVICE;
job->unpins[job->num_unpins].dev = host->dev;
@@ -660,8 +655,7 @@ void host1x_job_unpin(struct host1x_job *job)
}
if (unpin->dev && sgt)
- dma_unmap_sg(unpin->dev, sgt->sgl, sgt->nents,
- unpin->dir);
+ dma_unmap_sgtable(unpin->dev, sgt, unpin->dir, 0);
host1x_bo_unpin(dev, unpin->bo, sgt);
host1x_bo_put(unpin->bo);
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index a4a45d68a6ef..86d5e3f4b1ff 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -912,8 +912,8 @@ int vsp1_du_map_sg(struct device *dev, struct sg_table *sgt)
* skip cache sync. This will need to be revisited when support for
* non-coherent buffers will be added to the DU driver.
*/
- return dma_map_sg_attrs(vsp1->bus_master, sgt->sgl, sgt->nents,
- DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
+ return dma_map_sgtable(vsp1->bus_master, sgt, DMA_TO_DEVICE,
+ DMA_ATTR_SKIP_CPU_SYNC);
}
EXPORT_SYMBOL_GPL(vsp1_du_map_sg);
@@ -921,8 +921,8 @@ void vsp1_du_unmap_sg(struct device *dev, struct sg_table *sgt)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
- dma_unmap_sg_attrs(vsp1->bus_master, sgt->sgl, sgt->nents,
- DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
+ dma_unmap_sgtable(vsp1->bus_master, sgt, DMA_TO_DEVICE,
+ DMA_ATTR_SKIP_CPU_SYNC);
}
EXPORT_SYMBOL_GPL(vsp1_du_unmap_sg);
diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c
index 7116127358ee..116aca36f7dd 100644
--- a/drivers/phy/cadence/phy-cadence-torrent.c
+++ b/drivers/phy/cadence/phy-cadence-torrent.c
@@ -1852,6 +1852,10 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
cdns_phy->phys[node].num_lanes,
cdns_phy->max_bit_rate / 1000,
cdns_phy->max_bit_rate % 1000);
+
+ gphy->attrs.bus_width = cdns_phy->phys[node].num_lanes;
+ gphy->attrs.max_link_rate = cdns_phy->max_bit_rate;
+ gphy->attrs.mode = PHY_MODE_DP;
} else {
dev_err(dev, "Driver supports only PHY_TYPE_DP\n");
ret = -ENOTSUPP;
diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c
index 272eeb071147..ecfdfac0c2d9 100644
--- a/drivers/pwm/pwm-crc.c
+++ b/drivers/pwm/pwm-crc.c
@@ -21,8 +21,8 @@
#define PWM_MAX_LEVEL 0xFF
-#define PWM_BASE_CLK 6000000 /* 6 MHz */
-#define PWM_MAX_PERIOD_NS 21333 /* 46.875KHz */
+#define PWM_BASE_CLK_MHZ 6 /* 6 MHz */
+#define PWM_MAX_PERIOD_NS 5461334 /* 183 Hz */
/**
* struct crystalcove_pwm - Crystal Cove PWM controller
@@ -39,59 +39,121 @@ static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *pc)
return container_of(pc, struct crystalcove_pwm, chip);
}
-static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm)
+static int crc_pwm_calc_clk_div(int period_ns)
{
- struct crystalcove_pwm *crc_pwm = to_crc_pwm(c);
+ int clk_div;
- regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1);
+ clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_USEC);
+ /* clk_div 1 - 128, maps to register values 0-127 */
+ if (clk_div > 0)
+ clk_div--;
- return 0;
-}
-
-static void crc_pwm_disable(struct pwm_chip *c, struct pwm_device *pwm)
-{
- struct crystalcove_pwm *crc_pwm = to_crc_pwm(c);
-
- regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0);
+ return clk_div;
}
-static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm,
- int duty_ns, int period_ns)
+static int crc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
{
- struct crystalcove_pwm *crc_pwm = to_crc_pwm(c);
+ struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip);
struct device *dev = crc_pwm->chip.dev;
- int level;
+ int err;
- if (period_ns > PWM_MAX_PERIOD_NS) {
+ if (state->period > PWM_MAX_PERIOD_NS) {
dev_err(dev, "un-supported period_ns\n");
return -EINVAL;
}
- if (pwm_get_period(pwm) != period_ns) {
- int clk_div;
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EOPNOTSUPP;
- /* changing the clk divisor, need to disable fisrt */
- crc_pwm_disable(c, pwm);
- clk_div = PWM_BASE_CLK * period_ns / NSEC_PER_SEC;
+ if (pwm_is_enabled(pwm) && !state->enabled) {
+ err = regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0);
+ if (err) {
+ dev_err(dev, "Error writing BACKLIGHT_EN %d\n", err);
+ return err;
+ }
+ }
- regmap_write(crc_pwm->regmap, PWM0_CLK_DIV,
- clk_div | PWM_OUTPUT_ENABLE);
+ if (pwm_get_duty_cycle(pwm) != state->duty_cycle ||
+ pwm_get_period(pwm) != state->period) {
+ u64 level = state->duty_cycle * PWM_MAX_LEVEL;
+
+ do_div(level, state->period);
+
+ err = regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level);
+ if (err) {
+ dev_err(dev, "Error writing PWM0_DUTY_CYCLE %d\n", err);
+ return err;
+ }
+ }
+
+ if (pwm_is_enabled(pwm) && state->enabled &&
+ pwm_get_period(pwm) != state->period) {
+ /* changing the clk divisor, clear PWM_OUTPUT_ENABLE first */
+ err = regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0);
+ if (err) {
+ dev_err(dev, "Error writing PWM0_CLK_DIV %d\n", err);
+ return err;
+ }
+ }
- /* enable back */
- crc_pwm_enable(c, pwm);
+ if (pwm_get_period(pwm) != state->period ||
+ pwm_is_enabled(pwm) != state->enabled) {
+ int clk_div = crc_pwm_calc_clk_div(state->period);
+ int pwm_output_enable = state->enabled ? PWM_OUTPUT_ENABLE : 0;
+
+ err = regmap_write(crc_pwm->regmap, PWM0_CLK_DIV,
+ clk_div | pwm_output_enable);
+ if (err) {
+ dev_err(dev, "Error writing PWM0_CLK_DIV %d\n", err);
+ return err;
+ }
}
- /* change the pwm duty cycle */
- level = duty_ns * PWM_MAX_LEVEL / period_ns;
- regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level);
+ if (!pwm_is_enabled(pwm) && state->enabled) {
+ err = regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1);
+ if (err) {
+ dev_err(dev, "Error writing BACKLIGHT_EN %d\n", err);
+ return err;
+ }
+ }
return 0;
}
+static void crc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip);
+ struct device *dev = crc_pwm->chip.dev;
+ unsigned int clk_div, clk_div_reg, duty_cycle_reg;
+ int error;
+
+ error = regmap_read(crc_pwm->regmap, PWM0_CLK_DIV, &clk_div_reg);
+ if (error) {
+ dev_err(dev, "Error reading PWM0_CLK_DIV %d\n", error);
+ return;
+ }
+
+ error = regmap_read(crc_pwm->regmap, PWM0_DUTY_CYCLE, &duty_cycle_reg);
+ if (error) {
+ dev_err(dev, "Error reading PWM0_DUTY_CYCLE %d\n", error);
+ return;
+ }
+
+ clk_div = (clk_div_reg & ~PWM_OUTPUT_ENABLE) + 1;
+
+ state->period =
+ DIV_ROUND_UP(clk_div * NSEC_PER_USEC * 256, PWM_BASE_CLK_MHZ);
+ state->duty_cycle =
+ DIV_ROUND_UP_ULL(duty_cycle_reg * state->period, PWM_MAX_LEVEL);
+ state->polarity = PWM_POLARITY_NORMAL;
+ state->enabled = !!(clk_div_reg & PWM_OUTPUT_ENABLE);
+}
+
static const struct pwm_ops crc_pwm_ops = {
- .config = crc_pwm_config,
- .enable = crc_pwm_enable,
- .disable = crc_pwm_disable,
+ .apply = crc_pwm_apply,
+ .get_state = crc_pwm_get_state,
};
static int crystalcove_pwm_probe(struct platform_device *pdev)
diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c
index 48f34d20aecd..c6502cf7a7af 100644
--- a/drivers/pwm/pwm-lpss-platform.c
+++ b/drivers/pwm/pwm-lpss-platform.c
@@ -89,7 +89,6 @@ static int pwm_lpss_prepare(struct device *dev)
static const struct dev_pm_ops pwm_lpss_platform_pm_ops = {
.prepare = pwm_lpss_prepare,
- SET_SYSTEM_SLEEP_PM_OPS(pwm_lpss_suspend, pwm_lpss_resume)
};
static const struct acpi_device_id pwm_lpss_acpi_match[] = {
diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c
index 9d965ffe66d1..3444c56b4bed 100644
--- a/drivers/pwm/pwm-lpss.c
+++ b/drivers/pwm/pwm-lpss.c
@@ -85,7 +85,7 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm,
unsigned long long on_time_div;
unsigned long c = lpwm->info->clk_rate, base_unit_range;
unsigned long long base_unit, freq = NSEC_PER_SEC;
- u32 orig_ctrl, ctrl;
+ u32 ctrl;
do_div(freq, period_ns);
@@ -93,26 +93,25 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm,
* The equation is:
* base_unit = round(base_unit_range * freq / c)
*/
- base_unit_range = BIT(lpwm->info->base_unit_bits) - 1;
+ base_unit_range = BIT(lpwm->info->base_unit_bits);
freq *= base_unit_range;
base_unit = DIV_ROUND_CLOSEST_ULL(freq, c);
+ /* base_unit must not be 0 and we also want to avoid overflowing it */
+ base_unit = clamp_val(base_unit, 1, base_unit_range - 1);
on_time_div = 255ULL * duty_ns;
do_div(on_time_div, period_ns);
on_time_div = 255ULL - on_time_div;
- orig_ctrl = ctrl = pwm_lpss_read(pwm);
+ ctrl = pwm_lpss_read(pwm);
ctrl &= ~PWM_ON_TIME_DIV_MASK;
- ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT);
- base_unit &= base_unit_range;
+ ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT);
ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT;
ctrl |= on_time_div;
- if (orig_ctrl != ctrl) {
- pwm_lpss_write(pwm, ctrl);
- pwm_lpss_write(pwm, ctrl | PWM_SW_UPDATE);
- }
+ pwm_lpss_write(pwm, ctrl);
+ pwm_lpss_write(pwm, ctrl | PWM_SW_UPDATE);
}
static inline void pwm_lpss_cond_enable(struct pwm_device *pwm, bool cond)
@@ -121,41 +120,47 @@ static inline void pwm_lpss_cond_enable(struct pwm_device *pwm, bool cond)
pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE);
}
+static int pwm_lpss_prepare_enable(struct pwm_lpss_chip *lpwm,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int ret;
+
+ ret = pwm_lpss_is_updating(pwm);
+ if (ret)
+ return ret;
+
+ pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
+ pwm_lpss_cond_enable(pwm, lpwm->info->bypass == false);
+ ret = pwm_lpss_wait_for_update(pwm);
+ if (ret)
+ return ret;
+
+ pwm_lpss_cond_enable(pwm, lpwm->info->bypass == true);
+ return 0;
+}
+
static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
struct pwm_lpss_chip *lpwm = to_lpwm(chip);
- int ret;
+ int ret = 0;
if (state->enabled) {
if (!pwm_is_enabled(pwm)) {
pm_runtime_get_sync(chip->dev);
- ret = pwm_lpss_is_updating(pwm);
- if (ret) {
- pm_runtime_put(chip->dev);
- return ret;
- }
- pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
- pwm_lpss_cond_enable(pwm, lpwm->info->bypass == false);
- ret = pwm_lpss_wait_for_update(pwm);
- if (ret) {
+ ret = pwm_lpss_prepare_enable(lpwm, pwm, state);
+ if (ret)
pm_runtime_put(chip->dev);
- return ret;
- }
- pwm_lpss_cond_enable(pwm, lpwm->info->bypass == true);
} else {
- ret = pwm_lpss_is_updating(pwm);
- if (ret)
- return ret;
- pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
- return pwm_lpss_wait_for_update(pwm);
+ ret = pwm_lpss_prepare_enable(lpwm, pwm, state);
}
} else if (pwm_is_enabled(pwm)) {
pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE);
pm_runtime_put(chip->dev);
}
- return 0;
+ return ret;
}
static void pwm_lpss_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -255,30 +260,6 @@ int pwm_lpss_remove(struct pwm_lpss_chip *lpwm)
}
EXPORT_SYMBOL_GPL(pwm_lpss_remove);
-int pwm_lpss_suspend(struct device *dev)
-{
- struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev);
- int i;
-
- for (i = 0; i < lpwm->info->npwm; i++)
- lpwm->saved_ctrl[i] = readl(lpwm->regs + i * PWM_SIZE + PWM);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pwm_lpss_suspend);
-
-int pwm_lpss_resume(struct device *dev)
-{
- struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev);
- int i;
-
- for (i = 0; i < lpwm->info->npwm; i++)
- writel(lpwm->saved_ctrl[i], lpwm->regs + i * PWM_SIZE + PWM);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pwm_lpss_resume);
-
MODULE_DESCRIPTION("PWM driver for Intel LPSS");
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-lpss.h b/drivers/pwm/pwm-lpss.h
index 7909fa12fca2..70db7e389d66 100644
--- a/drivers/pwm/pwm-lpss.h
+++ b/drivers/pwm/pwm-lpss.h
@@ -19,7 +19,6 @@ struct pwm_lpss_chip {
struct pwm_chip chip;
void __iomem *regs;
const struct pwm_lpss_boardinfo *info;
- u32 saved_ctrl[MAX_PWMS];
};
struct pwm_lpss_boardinfo {
@@ -37,7 +36,5 @@ struct pwm_lpss_boardinfo {
struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
const struct pwm_lpss_boardinfo *info);
int pwm_lpss_remove(struct pwm_lpss_chip *lpwm);
-int pwm_lpss_suspend(struct device *dev);
-int pwm_lpss_resume(struct device *dev);
#endif /* __PWM_LPSS_H */
diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c
index b1b6eebafd5d..4c13cbc99896 100644
--- a/drivers/xen/gntdev-dmabuf.c
+++ b/drivers/xen/gntdev-dmabuf.c
@@ -247,10 +247,9 @@ static void dmabuf_exp_ops_detach(struct dma_buf *dma_buf,
if (sgt) {
if (gntdev_dmabuf_attach->dir != DMA_NONE)
- dma_unmap_sg_attrs(attach->dev, sgt->sgl,
- sgt->nents,
- gntdev_dmabuf_attach->dir,
- DMA_ATTR_SKIP_CPU_SYNC);
+ dma_unmap_sgtable(attach->dev, sgt,
+ gntdev_dmabuf_attach->dir,
+ DMA_ATTR_SKIP_CPU_SYNC);
sg_free_table(sgt);
}
@@ -288,8 +287,8 @@ dmabuf_exp_ops_map_dma_buf(struct dma_buf_attachment *attach,
sgt = dmabuf_pages_to_sgt(gntdev_dmabuf->pages,
gntdev_dmabuf->nr_pages);
if (!IS_ERR(sgt)) {
- if (!dma_map_sg_attrs(attach->dev, sgt->sgl, sgt->nents, dir,
- DMA_ATTR_SKIP_CPU_SYNC)) {
+ if (dma_map_sgtable(attach->dev, sgt, dir,
+ DMA_ATTR_SKIP_CPU_SYNC)) {
sg_free_table(sgt);
kfree(sgt);
sgt = ERR_PTR(-ENOMEM);
@@ -633,7 +632,7 @@ dmabuf_imp_to_refs(struct gntdev_dmabuf_priv *priv, struct device *dev,
/* Now convert sgt to array of pages and check for page validity. */
i = 0;
- for_each_sg_page(sgt->sgl, &sg_iter, sgt->nents, 0) {
+ for_each_sgtable_page(sgt, &sg_iter, 0) {
struct page *page = sg_page_iter_page(&sg_iter);
/*
* Check if page is valid: this can happen if we are given
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 85513eeb2196..c9f2851904d0 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -28,6 +28,8 @@
#include <linux/types.h>
#include <drm/drm_connector.h>
+struct drm_device;
+
/*
* Unless otherwise noted, all values are from the DP 1.1a spec. Note that
* DP and DPCD versions are independent. Differences from 1.0 are not noted,
@@ -385,13 +387,30 @@
# define DP_DS_PORT_TYPE_DP_DUALMODE 5
# define DP_DS_PORT_TYPE_WIRELESS 6
# define DP_DS_PORT_HPD (1 << 3)
+# define DP_DS_NON_EDID_MASK (0xf << 4)
+# define DP_DS_NON_EDID_720x480i_60 (1 << 4)
+# define DP_DS_NON_EDID_720x480i_50 (2 << 4)
+# define DP_DS_NON_EDID_1920x1080i_60 (3 << 4)
+# define DP_DS_NON_EDID_1920x1080i_50 (4 << 4)
+# define DP_DS_NON_EDID_1280x720_60 (5 << 4)
+# define DP_DS_NON_EDID_1280x720_50 (7 << 4)
/* offset 1 for VGA is maximum megapixels per second / 8 */
-/* offset 2 */
+/* offset 1 for DVI/HDMI is maximum TMDS clock in Mbps / 2.5 */
+/* offset 2 for VGA/DVI/HDMI */
# define DP_DS_MAX_BPC_MASK (3 << 0)
# define DP_DS_8BPC 0
# define DP_DS_10BPC 1
# define DP_DS_12BPC 2
# define DP_DS_16BPC 3
+/* offset 3 for DVI */
+# define DP_DS_DVI_DUAL_LINK (1 << 1)
+# define DP_DS_DVI_HIGH_COLOR_DEPTH (1 << 2)
+/* offset 3 for HDMI */
+# define DP_DS_HDMI_FRAME_SEQ_TO_FRAME_PACK (1 << 0)
+# define DP_DS_HDMI_YCBCR422_PASS_THROUGH (1 << 1)
+# define DP_DS_HDMI_YCBCR420_PASS_THROUGH (1 << 2)
+# define DP_DS_HDMI_YCBCR444_TO_422_CONV (1 << 3)
+# define DP_DS_HDMI_YCBCR444_TO_420_CONV (1 << 4)
#define DP_MAX_DOWNSTREAM_PORTS 0x10
@@ -984,6 +1003,16 @@
#define DP_CEC_TX_MESSAGE_BUFFER 0x3020
#define DP_CEC_MESSAGE_BUFFER_LENGTH 0x10
+#define DP_PROTOCOL_CONVERTER_CONTROL_0 0x3050 /* DP 1.3 */
+# define DP_HDMI_DVI_OUTPUT_CONFIG (1 << 0) /* DP 1.3 */
+#define DP_PROTOCOL_CONVERTER_CONTROL_1 0x3051 /* DP 1.3 */
+# define DP_CONVERSION_TO_YCBCR420_ENABLE (1 << 0) /* DP 1.3 */
+# define DP_HDMI_EDID_PROCESSING_DISABLE (1 << 1) /* DP 1.4 */
+# define DP_HDMI_AUTONOMOUS_SCRAMBLING_DISABLE (1 << 2) /* DP 1.4 */
+# define DP_HDMI_FORCE_SCRAMBLING (1 << 3) /* DP 1.4 */
+#define DP_PROTOCOL_CONVERTER_CONTROL_2 0x3052 /* DP 1.3 */
+# define DP_CONVERSION_TO_YCBCR422_ENABLE (1 << 0) /* DP 1.3 */
+
#define DP_AUX_HDCP_BKSV 0x68000
#define DP_AUX_HDCP_RI_PRIME 0x68005
#define DP_AUX_HDCP_AKSV 0x68007
@@ -1109,6 +1138,9 @@
#define DP_POWER_DOWN_PHY 0x25
#define DP_SINK_EVENT_NOTIFY 0x30
#define DP_QUERY_STREAM_ENC_STATUS 0x38
+#define DP_QUERY_STREAM_ENC_STATUS_STATE_NO_EXIST 0
+#define DP_QUERY_STREAM_ENC_STATUS_STATE_INACTIVE 1
+#define DP_QUERY_STREAM_ENC_STATUS_STATE_ACTIVE 2
/* DP 1.2 MST sideband reply types */
#define DP_SIDEBAND_REPLY_ACK 0x00
@@ -1619,13 +1651,35 @@ bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux,
int drm_dp_read_downstream_info(struct drm_dp_aux *aux,
const u8 dpcd[DP_RECEIVER_CAP_SIZE],
u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]);
-int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
- const u8 port_cap[4]);
+bool drm_dp_downstream_is_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4], u8 type);
+bool drm_dp_downstream_is_tmds(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4],
+ const struct edid *edid);
+int drm_dp_downstream_max_dotclock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4]);
+int drm_dp_downstream_max_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4],
+ const struct edid *edid);
+int drm_dp_downstream_min_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4],
+ const struct edid *edid);
int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
- const u8 port_cap[4]);
+ const u8 port_cap[4],
+ const struct edid *edid);
+bool drm_dp_downstream_420_passthrough(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4]);
+bool drm_dp_downstream_444_to_420_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4]);
+struct drm_display_mode *drm_dp_downstream_mode(struct drm_device *dev,
+ const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4]);
int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]);
-void drm_dp_downstream_debug(struct seq_file *m, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
- const u8 port_cap[4], struct drm_dp_aux *aux);
+void drm_dp_downstream_debug(struct seq_file *m,
+ const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+ const u8 port_cap[4],
+ const struct edid *edid,
+ struct drm_dp_aux *aux);
enum drm_mode_subconnector
drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
const u8 port_cap[4]);
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index 6ae5860d8644..f5e92fe9151c 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -313,6 +313,34 @@ struct drm_dp_remote_i2c_write_ack_reply {
u8 port_number;
};
+struct drm_dp_query_stream_enc_status_ack_reply {
+ /* Bit[23:16]- Stream Id */
+ u8 stream_id;
+
+ /* Bit[15]- Signed */
+ bool reply_signed;
+
+ /* Bit[10:8]- Stream Output Sink Type */
+ bool unauthorizable_device_present;
+ bool legacy_device_present;
+ bool query_capable_device_present;
+
+ /* Bit[12:11]- Stream Output CP Type */
+ bool hdcp_1x_device_present;
+ bool hdcp_2x_device_present;
+
+ /* Bit[4]- Stream Authentication */
+ bool auth_completed;
+
+ /* Bit[3]- Stream Encryption */
+ bool encryption_enabled;
+
+ /* Bit[2]- Stream Repeater Function Present */
+ bool repeater_present;
+
+ /* Bit[1:0]- Stream State */
+ u8 state;
+};
#define DRM_DP_MAX_SDP_STREAMS 16
struct drm_dp_allocate_payload {
@@ -374,6 +402,15 @@ struct drm_dp_remote_i2c_write {
u8 *bytes;
};
+struct drm_dp_query_stream_enc_status {
+ u8 stream_id;
+ u8 client_id[7]; /* 56-bit nonce */
+ u8 stream_event;
+ bool valid_stream_event;
+ u8 stream_behavior;
+ u8 valid_stream_behavior;
+};
+
/* this covers ENUM_RESOURCES, POWER_DOWN_PHY, POWER_UP_PHY */
struct drm_dp_port_number_req {
u8 port_number;
@@ -422,6 +459,8 @@ struct drm_dp_sideband_msg_req_body {
struct drm_dp_remote_i2c_read i2c_read;
struct drm_dp_remote_i2c_write i2c_write;
+
+ struct drm_dp_query_stream_enc_status enc_status;
} u;
};
@@ -444,6 +483,8 @@ struct drm_dp_sideband_msg_reply_body {
struct drm_dp_remote_i2c_read_ack_reply remote_i2c_read_ack;
struct drm_dp_remote_i2c_read_nak_reply remote_i2c_read_nack;
struct drm_dp_remote_i2c_write_ack_reply remote_i2c_write_ack;
+
+ struct drm_dp_query_stream_enc_status_ack_reply enc_status;
} u;
};
@@ -807,6 +848,9 @@ drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
struct drm_dp_mst_port *port);
int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port, bool power_up);
+int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port,
+ struct drm_dp_query_stream_enc_status_ack_reply *status);
int __must_check drm_dp_mst_atomic_check(struct drm_atomic_state *state);
void drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port);
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index cfa4f5af49af..b27a0e2169c8 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -517,4 +517,8 @@ void drm_edid_get_monitor_name(struct edid *edid, char *name,
struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
int hsize, int vsize, int fresh,
bool rb);
+struct drm_display_mode *
+drm_display_mode_from_cea_vic(struct drm_device *dev,
+ u8 video_code);
+
#endif /* __DRM_EDID_H__ */
diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h
index 9af7422b44cf..47ef11614627 100644
--- a/include/drm/drm_prime.h
+++ b/include/drm/drm_prime.h
@@ -92,6 +92,8 @@ struct sg_table *drm_prime_pages_to_sg(struct page **pages, unsigned int nr_page
struct dma_buf *drm_gem_prime_export(struct drm_gem_object *obj,
int flags);
+unsigned long drm_prime_get_contiguous_size(struct sg_table *sgt);
+
/* helper functions for importing */
struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev,
struct dma_buf *dma_buf,
diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h
index 8e7ae30ebcbb..7eeecb07c9a1 100644
--- a/include/drm/i915_pciids.h
+++ b/include/drm/i915_pciids.h
@@ -594,19 +594,25 @@
INTEL_VGA_DEVICE(0x4E51, info)
/* TGL */
-#define INTEL_TGL_12_IDS(info) \
+#define INTEL_TGL_12_GT1_IDS(info) \
+ INTEL_VGA_DEVICE(0x9A60, info), \
+ INTEL_VGA_DEVICE(0x9A68, info), \
+ INTEL_VGA_DEVICE(0x9A70, info)
+
+#define INTEL_TGL_12_GT2_IDS(info) \
INTEL_VGA_DEVICE(0x9A40, info), \
INTEL_VGA_DEVICE(0x9A49, info), \
INTEL_VGA_DEVICE(0x9A59, info), \
- INTEL_VGA_DEVICE(0x9A60, info), \
- INTEL_VGA_DEVICE(0x9A68, info), \
- INTEL_VGA_DEVICE(0x9A70, info), \
INTEL_VGA_DEVICE(0x9A78, info), \
INTEL_VGA_DEVICE(0x9AC0, info), \
INTEL_VGA_DEVICE(0x9AC9, info), \
INTEL_VGA_DEVICE(0x9AD9, info), \
INTEL_VGA_DEVICE(0x9AF8, info)
+#define INTEL_TGL_12_IDS(info) \
+ INTEL_TGL_12_GT1_IDS(info), \
+ INTEL_TGL_12_GT2_IDS(info)
+
/* RKL */
#define INTEL_RKL_IDS(info) \
INTEL_VGA_DEVICE(0x4C80, info), \
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index bcee8eba62b3..e435bdb0bab3 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -115,10 +115,12 @@ struct phy_ops {
/**
* struct phy_attrs - represents phy attributes
* @bus_width: Data path width implemented by PHY
+ * @max_link_rate: Maximum link rate supported by PHY (in Mbps)
* @mode: PHY mode
*/
struct phy_attrs {
u32 bus_width;
+ u32 max_link_rate;
enum phy_mode mode;
};
diff --git a/samples/vfio-mdev/mbochs.c b/samples/vfio-mdev/mbochs.c
index 3cc5e5921682..e03068917273 100644
--- a/samples/vfio-mdev/mbochs.c
+++ b/samples/vfio-mdev/mbochs.c
@@ -846,7 +846,7 @@ static struct sg_table *mbochs_map_dmabuf(struct dma_buf_attachment *at,
if (sg_alloc_table_from_pages(sg, dmabuf->pages, dmabuf->pagecount,
0, dmabuf->mode.size, GFP_KERNEL) < 0)
goto err2;
- if (!dma_map_sg(at->dev, sg->sgl, sg->nents, direction))
+ if (dma_map_sgtable(at->dev, sg, direction, 0))
goto err3;
return sg;
@@ -868,6 +868,7 @@ static void mbochs_unmap_dmabuf(struct dma_buf_attachment *at,
dev_dbg(dev, "%s: %d\n", __func__, dmabuf->id);
+ dma_unmap_sgtable(at->dev, sg, direction, 0);
sg_free_table(sg);
kfree(sg);
}