Merge tag 'drm-misc-next-2022-01-27' of git://anongit.freedesktop.org/drm/drm-misc...
authorDave Airlie <airlied@redhat.com>
Mon, 31 Jan 2022 06:33:54 +0000 (16:33 +1000)
committerDave Airlie <airlied@redhat.com>
Tue, 1 Feb 2022 09:02:41 +0000 (19:02 +1000)
[airlied: add two missing Kconfig]

drm-misc-next for v5.18:

UAPI Changes:
- Fix invalid IN_FORMATS blob when plane->format_mod_supported is NULL.

Cross-subsystem Changes:
- Assorted dt bindings updates.
- Fix vga16fb vga checking on x86.
- Fix extra semicolon in rwsem.h's _down_write_nest_lock.
- Assorted small fixes to agp and fbdev drivers.
- Fix oops in creating a udmabuf with 0 pages.
- Hot-unplug firmware fb devices on forced removal
- Reqquest memory region in simplefb and simpledrm, and don't make the ioresource as busy.

Core Changes:
- Mock a drm_plane in drm-plane-helper selftest.
- Assorted bug fixes to device logging, dbi.
- Use DP helper for sink count in mst.
- Assorted documentation fixes.
- Assorted small fixes.
- Move DP headers to drm/dp, and add a drm dp helper module.
- Move the buddy allocator from i915 to common drm.
- Add simple pci and platform module init macros to remove a lot of boilerplate from some drivers.
- Support microsoft extension for HMDs and specialized monitors.
- Improve edid parser's deep color handling.
- Add type 7 timing support to edid parser.
- Add a weak backpointer to the ttm_bo from ttm_resource
- Add 3 eDP panels.

Driver Changes:
- Add support for HDMI and JZ4780 to ingenic.
- Add support for higher DP/eDP bitrates to nouveau.
- Assorted driver fixes to tilcdc, vmwgfx, sn65dsi83, meson, stm, panfrost, v3d, gma500, vc4, virtio, mgag200, ast, radeon, amdgpu, nouveau, various bridge drivers.
- Convert and revert exynos dsi support to bridge driver.
- Add vcc supply regulator support for sn65dsi83.
- More conversion of bridge/chipone-icn6211 to atomic.
- Remove conflicting fb's from stm, and add support for new hw version.
- Add device link in parade-ps8640 to fix suspend/resume.
- Update Boe-tv110c9m init sequence.
- Add wide screen support to AST2600.
- Fix omapdrm implicit dma_buf fencing.
- Add support for multiple overlay planes to vkms.
- Convert bridge/anx7625 to atomic, add HDCP support,
  add eld support for audio, and fix HPD.
- Add driver for ChromeOS privacy screen.
- Handover display from firmware to vc4 more gracefully, and support nomodeset.
- Add flexible and ycbcr pixel formats to stm/ltdc.
- Convert exynos mipi dsi to atomic.
- Add initial dual core group GPUs support to panfrost.
- No longer add exclusive fence in amdgpu as shared fence.
- Add CSC and full range supoprt to vc4.
- Shutdown the display on system shutdown and unbind.
- Add Multi-Inno Technology MI0700S4T-6 simple panel.

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/456a23c6-7324-7543-0c45-751f30ef83f7@linux.intel.com
244 files changed:
Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml
Documentation/devicetree/bindings/display/bridge/ti,sn65dsi83.yaml
Documentation/devicetree/bindings/display/panel/panel-simple.yaml
Documentation/devicetree/bindings/display/panel/sony,acx424akp.yaml
Documentation/gpu/drm-internals.rst
Documentation/gpu/drm-kms-helpers.rst
Documentation/gpu/drm-kms.rst
Documentation/gpu/drm-mm.rst
Documentation/gpu/todo.rst
Documentation/gpu/vkms.rst
drivers/char/agp/ati-agp.c
drivers/char/agp/backend.c
drivers/char/agp/frontend.c
drivers/char/agp/nvidia-agp.c
drivers/char/agp/sworks-agp.c
drivers/char/agp/via-agp.c
drivers/dma-buf/dma-resv.c
drivers/dma-buf/st-dma-resv.c
drivers/dma-buf/udmabuf.c
drivers/firmware/sysfb_simplefb.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
drivers/gpu/drm/amd/amdgpu/atombios_dp.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
drivers/gpu/drm/amd/display/dc/core/dc_link_dpcd.c
drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
drivers/gpu/drm/amd/display/dc/os_types.h
drivers/gpu/drm/amd/display/include/dpcd_defs.h
drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h
drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
drivers/gpu/drm/arm/display/komeda/komeda_drv.c
drivers/gpu/drm/arm/hdlcd_drv.c
drivers/gpu/drm/arm/malidp_drv.c
drivers/gpu/drm/ast/ast_drv.c
drivers/gpu/drm/ast/ast_main.c
drivers/gpu/drm/ast/ast_mode.c
drivers/gpu/drm/bridge/Kconfig
drivers/gpu/drm/bridge/adv7511/adv7511.h
drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
drivers/gpu/drm/bridge/adv7511/adv7533.c
drivers/gpu/drm/bridge/analogix/Kconfig
drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c
drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
drivers/gpu/drm/bridge/analogix/anx7625.c
drivers/gpu/drm/bridge/analogix/anx7625.h
drivers/gpu/drm/bridge/cadence/Kconfig
drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h
drivers/gpu/drm/bridge/chipone-icn6211.c
drivers/gpu/drm/bridge/ite-it66121.c
drivers/gpu/drm/bridge/lontium-lt9611.c
drivers/gpu/drm/bridge/lontium-lt9611uxc.c
drivers/gpu/drm/bridge/nwl-dsi.c
drivers/gpu/drm/bridge/parade-ps8640.c
drivers/gpu/drm/bridge/sii902x.c
drivers/gpu/drm/bridge/sil-sii8620.c
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
drivers/gpu/drm/bridge/tc358767.c
drivers/gpu/drm/bridge/tc358775.c
drivers/gpu/drm/bridge/ti-sn65dsi83.c
drivers/gpu/drm/bridge/ti-sn65dsi86.c
drivers/gpu/drm/dp/Makefile [new file with mode: 0644]
drivers/gpu/drm/dp/drm_dp.c [new file with mode: 0644]
drivers/gpu/drm/dp/drm_dp_aux_bus.c [new file with mode: 0644]
drivers/gpu/drm/dp/drm_dp_aux_dev.c [new file with mode: 0644]
drivers/gpu/drm/dp/drm_dp_cec.c [new file with mode: 0644]
drivers/gpu/drm/dp/drm_dp_dual_mode_helper.c [new file with mode: 0644]
drivers/gpu/drm/dp/drm_dp_helper_internal.h [new file with mode: 0644]
drivers/gpu/drm/dp/drm_dp_helper_mod.c [new file with mode: 0644]
drivers/gpu/drm/dp/drm_dp_mst_topology.c [new file with mode: 0644]
drivers/gpu/drm/dp/drm_dp_mst_topology_internal.h [new file with mode: 0644]
drivers/gpu/drm/drm_buddy.c [new file with mode: 0644]
drivers/gpu/drm/drm_color_mgmt.c
drivers/gpu/drm/drm_crtc_helper_internal.h
drivers/gpu/drm/drm_dp_aux_bus.c [deleted file]
drivers/gpu/drm/drm_dp_aux_dev.c [deleted file]
drivers/gpu/drm/drm_dp_cec.c [deleted file]
drivers/gpu/drm/drm_dp_dual_mode_helper.c [deleted file]
drivers/gpu/drm/drm_dp_helper.c [deleted file]
drivers/gpu/drm/drm_dp_mst_topology.c [deleted file]
drivers/gpu/drm/drm_dp_mst_topology_internal.h [deleted file]
drivers/gpu/drm/drm_dsc.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_kms_helper_common.c
drivers/gpu/drm/drm_plane.c
drivers/gpu/drm/drm_privacy_screen.c
drivers/gpu/drm/drm_privacy_screen_x86.c
drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
drivers/gpu/drm/exynos/Kconfig
drivers/gpu/drm/exynos/exynos_drm_dsi.c
drivers/gpu/drm/gma500/cdv_intel_dp.c
drivers/gpu/drm/gma500/gma_display.c
drivers/gpu/drm/gma500/intel_bios.c
drivers/gpu/drm/gma500/mmu.c
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
drivers/gpu/drm/i915/Kconfig
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/display/intel_bios.c
drivers/gpu/drm/i915/display/intel_display.c
drivers/gpu/drm/i915/display/intel_display_types.h
drivers/gpu/drm/i915/display/intel_dp.c
drivers/gpu/drm/i915/display/intel_dp_hdcp.c
drivers/gpu/drm/i915/display/intel_dp_link_training.h
drivers/gpu/drm/i915/display/intel_hdmi.c
drivers/gpu/drm/i915/display/intel_lspcon.c
drivers/gpu/drm/i915/i915_buddy.c [deleted file]
drivers/gpu/drm/i915/i915_buddy.h [deleted file]
drivers/gpu/drm/i915/i915_module.c
drivers/gpu/drm/i915/i915_scatterlist.c
drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
drivers/gpu/drm/i915/i915_ttm_buddy_manager.h
drivers/gpu/drm/i915/selftests/i915_buddy.c [deleted file]
drivers/gpu/drm/i915/selftests/i915_mock_selftests.h
drivers/gpu/drm/i915/selftests/intel_memory_region.c
drivers/gpu/drm/imx/dcss/dcss-drv.c
drivers/gpu/drm/ingenic/ingenic-drm-drv.c
drivers/gpu/drm/ingenic/ingenic-drm.h
drivers/gpu/drm/meson/meson_drv.c
drivers/gpu/drm/meson/meson_osd_afbcd.c
drivers/gpu/drm/meson/meson_osd_afbcd.h
drivers/gpu/drm/mgag200/mgag200_mode.c
drivers/gpu/drm/msm/Kconfig
drivers/gpu/drm/msm/dp/dp_audio.c
drivers/gpu/drm/msm/dp/dp_aux.h
drivers/gpu/drm/msm/dp/dp_catalog.c
drivers/gpu/drm/msm/dp/dp_ctrl.c
drivers/gpu/drm/msm/edp/edp.h [new file with mode: 0644]
drivers/gpu/drm/msm/edp/edp_ctrl.c [new file with mode: 0644]
drivers/gpu/drm/mxsfb/mxsfb_drv.c
drivers/gpu/drm/nouveau/Kconfig
drivers/gpu/drm/nouveau/dispnv50/disp.c
drivers/gpu/drm/nouveau/nouveau_connector.h
drivers/gpu/drm/nouveau/nouveau_dp.c
drivers/gpu/drm/nouveau/nouveau_encoder.h
drivers/gpu/drm/nouveau/nouveau_mem.c
drivers/gpu/drm/nouveau/nouveau_mem.h
drivers/gpu/drm/nouveau/nouveau_ttm.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorga102.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c
drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c
drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c
drivers/gpu/drm/panel/panel-edp.c
drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
drivers/gpu/drm/panel/panel-simple.c
drivers/gpu/drm/panfrost/panfrost_features.h
drivers/gpu/drm/panfrost/panfrost_gpu.c
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_dp_mst.c
drivers/gpu/drm/radeon/radeon_mode.h
drivers/gpu/drm/radeon/radeon_ttm.c
drivers/gpu/drm/rockchip/Kconfig
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
drivers/gpu/drm/rockchip/cdn-dp-core.c
drivers/gpu/drm/rockchip/cdn-dp-core.h
drivers/gpu/drm/rockchip/rockchip_lvds.c
drivers/gpu/drm/rockchip/rockchip_rgb.c
drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c
drivers/gpu/drm/selftests/test-drm_plane_helper.c
drivers/gpu/drm/stm/drv.c
drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
drivers/gpu/drm/stm/ltdc.c
drivers/gpu/drm/stm/ltdc.h
drivers/gpu/drm/tegra/Kconfig
drivers/gpu/drm/tegra/dp.c
drivers/gpu/drm/tegra/dpaux.c
drivers/gpu/drm/tegra/sor.c
drivers/gpu/drm/tilcdc/tilcdc_drv.c
drivers/gpu/drm/tiny/bochs.c
drivers/gpu/drm/tiny/cirrus.c
drivers/gpu/drm/tiny/simpledrm.c
drivers/gpu/drm/ttm/ttm_bo_util.c
drivers/gpu/drm/ttm/ttm_range_manager.c
drivers/gpu/drm/ttm/ttm_resource.c
drivers/gpu/drm/ttm/ttm_sys_manager.c
drivers/gpu/drm/v3d/v3d_drv.c
drivers/gpu/drm/vc4/vc4_bo.c
drivers/gpu/drm/vc4/vc4_drv.c
drivers/gpu/drm/vc4/vc4_hdmi.c
drivers/gpu/drm/vc4/vc4_hdmi.h
drivers/gpu/drm/vc4/vc4_regs.h
drivers/gpu/drm/virtio/virtgpu_gem.c
drivers/gpu/drm/vkms/vkms_drv.h
drivers/gpu/drm/vkms/vkms_output.c
drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
drivers/gpu/drm/vmwgfx/vmwgfx_system_manager.c
drivers/gpu/drm/xlnx/Kconfig
drivers/gpu/drm/xlnx/zynqmp_dp.c
drivers/platform/chrome/Kconfig
drivers/platform/chrome/Makefile
drivers/platform/chrome/chromeos_privacy_screen.c [new file with mode: 0644]
drivers/platform/x86/thinkpad_acpi.c
drivers/video/fbdev/asiliantfb.c
drivers/video/fbdev/core/fbmem.c
drivers/video/fbdev/s3c-fb.c
drivers/video/fbdev/simplefb.c
drivers/video/fbdev/vga16fb.c
include/drm/bridge/dw_mipi_dsi.h
include/drm/dp/drm_dp_aux_bus.h [new file with mode: 0644]
include/drm/dp/drm_dp_dual_mode_helper.h [new file with mode: 0644]
include/drm/dp/drm_dp_helper.h [new file with mode: 0644]
include/drm/dp/drm_dp_mst_helper.h [new file with mode: 0644]
include/drm/drm_buddy.h [new file with mode: 0644]
include/drm/drm_connector.h
include/drm/drm_crtc.h
include/drm/drm_dp_aux_bus.h [deleted file]
include/drm/drm_dp_dual_mode_helper.h [deleted file]
include/drm/drm_dp_helper.h [deleted file]
include/drm/drm_dp_mst_helper.h [deleted file]
include/drm/drm_dsc.h
include/drm/drm_edid.h
include/drm/drm_mipi_dbi.h
include/drm/drm_modeset_lock.h
include/drm/drm_module.h [new file with mode: 0644]
include/drm/drm_plane.h
include/drm/drm_privacy_screen_driver.h
include/drm/ttm/ttm_resource.h
include/linux/dma-buf-map.h
include/linux/dma-resv.h
include/linux/fb.h
include/linux/rwsem.h
include/soc/bcm2835/raspberrypi-firmware.h
include/uapi/drm/panfrost_drm.h

index 5079c1c..56b4c55 100644 (file)
@@ -39,6 +39,7 @@ properties:
           - const: lvds-encoder # Generic LVDS encoder compatible fallback
       - items:
           - enum:
+              - ti,ds90cf364a # For the DS90CF364A FPD-Link LVDS Receiver
               - ti,ds90cf384a # For the DS90CF384A FPD-Link LVDS Receiver
           - const: lvds-decoder # Generic LVDS decoders compatible fallback
       - enum:
index b446d0f..48a97bb 100644 (file)
@@ -32,6 +32,9 @@ properties:
     maxItems: 1
     description: GPIO specifier for bridge_en pin (active high).
 
+  vcc-supply:
+    description: A 1.8V power supply (see regulator/regulator.yaml).
+
   ports:
     $ref: /schemas/graph.yaml#/properties/ports
 
@@ -91,7 +94,6 @@ properties:
 required:
   - compatible
   - reg
-  - enable-gpios
   - ports
 
 allOf:
@@ -133,6 +135,7 @@ examples:
             reg = <0x2d>;
 
             enable-gpios = <&gpio2 1 GPIO_ACTIVE_HIGH>;
+            vcc-supply = <&reg_sn65dsi83_1v8>;
 
             ports {
                 #address-cells = <1>;
index 62f5f05..9cf5588 100644 (file)
@@ -222,6 +222,8 @@ properties:
       - logictechno,lttd800480070-l6wh-rt
         # Mitsubishi "AA070MC01 7.0" WVGA TFT LCD panel
       - mitsubishi,aa070mc01-ca1
+        # Multi-Inno Technology Co.,Ltd MI0700S4T-6 7" 800x480 TFT Resistive Touch Module
+      - multi-inno,mi0700s4t-6
         # Multi-Inno Technology Co.,Ltd MI1010AIT-1CP 10.1" 1280x800 LVDS IPS Cap Touch Mod.
       - multi-inno,mi1010ait-1cp
         # NEC LCD Technologies, Ltd. 12.1" WXGA (1280x800) LVDS TFT LCD panel
index 78d0600..059cc6d 100644 (file)
@@ -4,7 +4,12 @@
 $id: http://devicetree.org/schemas/display/panel/sony,acx424akp.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: Sony ACX424AKP 4" 480x864 AMOLED panel
+title: Sony ACX424AKP/ACX424AKM 4" 480x864/480x854 AMOLED panel
+
+description: The Sony ACX424AKP and ACX424AKM are panels built around
+  the Novatek NT35560 display controller. The only difference is that
+  the AKM is configured to use 10 pixels less in the Y axis than the
+  AKP.
 
 maintainers:
   - Linus Walleij <linus.walleij@linaro.org>
@@ -14,7 +19,9 @@ allOf:
 
 properties:
   compatible:
-    const: sony,acx424akp
+    enum:
+      - sony,acx424akp
+      - sony,acx424akm
   reg: true
   reset-gpios: true
   vddi-supply:
index 607f78f..38afed2 100644 (file)
@@ -75,6 +75,12 @@ update it, its value is mostly useless. The DRM core prints it to the
 kernel log at initialization time and passes it to userspace through the
 DRM_IOCTL_VERSION ioctl.
 
+Module Initialization
+---------------------
+
+.. kernel-doc:: include/drm/drm_module.h
+   :doc: overview
+
 Managing Ownership of the Framebuffer Aperture
 ----------------------------------------------
 
index 5bb55ec..c3ce91e 100644 (file)
@@ -232,34 +232,34 @@ HDCP Helper Functions Reference
 Display Port Helper Functions Reference
 =======================================
 
-.. kernel-doc:: drivers/gpu/drm/drm_dp_helper.c
+.. kernel-doc:: drivers/gpu/drm/dp/drm_dp.c
    :doc: dp helpers
 
-.. kernel-doc:: include/drm/drm_dp_helper.h
+.. kernel-doc:: include/drm/dp/drm_dp_helper.h
    :internal:
 
-.. kernel-doc:: drivers/gpu/drm/drm_dp_helper.c
+.. kernel-doc:: drivers/gpu/drm/dp/drm_dp.c
    :export:
 
 Display Port CEC Helper Functions Reference
 ===========================================
 
-.. kernel-doc:: drivers/gpu/drm/drm_dp_cec.c
+.. kernel-doc:: drivers/gpu/drm/dp/drm_dp_cec.c
    :doc: dp cec helpers
 
-.. kernel-doc:: drivers/gpu/drm/drm_dp_cec.c
+.. kernel-doc:: drivers/gpu/drm/dp/drm_dp_cec.c
    :export:
 
 Display Port Dual Mode Adaptor Helper Functions Reference
 =========================================================
 
-.. kernel-doc:: drivers/gpu/drm/drm_dp_dual_mode_helper.c
+.. kernel-doc:: drivers/gpu/drm/dp/drm_dp_dual_mode_helper.c
    :doc: dp dual mode helpers
 
-.. kernel-doc:: include/drm/drm_dp_dual_mode_helper.h
+.. kernel-doc:: include/drm/dp/drm_dp_dual_mode_helper.h
    :internal:
 
-.. kernel-doc:: drivers/gpu/drm/drm_dp_dual_mode_helper.c
+.. kernel-doc:: drivers/gpu/drm/dp/drm_dp_dual_mode_helper.c
    :export:
 
 Display Port MST Helpers
@@ -268,19 +268,19 @@ Display Port MST Helpers
 Overview
 --------
 
-.. kernel-doc:: drivers/gpu/drm/drm_dp_mst_topology.c
+.. kernel-doc:: drivers/gpu/drm/dp/drm_dp_mst_topology.c
    :doc: dp mst helper
 
-.. kernel-doc:: drivers/gpu/drm/drm_dp_mst_topology.c
+.. kernel-doc:: drivers/gpu/drm/dp/drm_dp_mst_topology.c
    :doc: Branch device and port refcounting
 
 Functions Reference
 -------------------
 
-.. kernel-doc:: include/drm/drm_dp_mst_helper.h
+.. kernel-doc:: include/drm/dp/drm_dp_mst_helper.h
    :internal:
 
-.. kernel-doc:: drivers/gpu/drm/drm_dp_mst_topology.c
+.. kernel-doc:: drivers/gpu/drm/dp/drm_dp_mst_topology.c
    :export:
 
 Topology Lifetime Internals
@@ -289,7 +289,7 @@ Topology Lifetime Internals
 These functions aren't exported to drivers, but are documented here to help make
 the MST topology helpers easier to understand
 
-.. kernel-doc:: drivers/gpu/drm/drm_dp_mst_topology.c
+.. kernel-doc:: drivers/gpu/drm/dp/drm_dp_mst_topology.c
    :functions: drm_dp_mst_topology_try_get_mstb drm_dp_mst_topology_get_mstb
                drm_dp_mst_topology_put_mstb
                drm_dp_mst_topology_try_get_port drm_dp_mst_topology_get_port
index d14bf1c..6f9c064 100644 (file)
@@ -423,12 +423,12 @@ Connector Functions Reference
 Writeback Connectors
 --------------------
 
-.. kernel-doc:: include/drm/drm_writeback.h
-  :internal:
-
 .. kernel-doc:: drivers/gpu/drm/drm_writeback.c
   :doc: overview
 
+.. kernel-doc:: include/drm/drm_writeback.h
+  :internal:
+
 .. kernel-doc:: drivers/gpu/drm/drm_writeback.c
   :export:
 
index e053808..198bcc1 100644 (file)
@@ -8,7 +8,7 @@ the very dynamic nature of many of that data, managing graphics memory
 efficiently is thus crucial for the graphics stack and plays a central
 role in the DRM infrastructure.
 
-The DRM core includes two memory managers, namely Translation Table Maps
+The DRM core includes two memory managers, namely Translation Table Manager
 (TTM) and Graphics Execution Manager (GEM). TTM was the first DRM memory
 manager to be developed and tried to be a one-size-fits-them all
 solution. It provides a single userspace API to accommodate the need of
index da138dd..1b2372e 100644 (file)
@@ -467,6 +467,21 @@ Contact: Thomas Zimmermann <tzimmermann@suse.de>
 
 Level: Intermediate
 
+Request memory regions in all drivers
+-------------------------------------
+
+Go through all drivers and add code to request the memory regions that the
+driver uses. This requires adding calls to request_mem_region(),
+pci_request_region() or similar functions. Use helpers for managed cleanup
+where possible.
+
+Drivers are pretty bad at doing this and there used to be conflicts among
+DRM and fbdev drivers. Still, it's the correct thing to do.
+
+Contact: Thomas Zimmermann <tzimmermann@suse.de>
+
+Level: Starter
+
 
 Core refactorings
 =================
index 941f0e7..9c873c3 100644 (file)
@@ -124,8 +124,6 @@ Add Plane Features
 
 There's lots of plane features we could add support for:
 
-- Multiple overlay planes. [Good to get started]
-
 - Clearing primary plane: clear primary plane before plane composition (at the
   start) for correctness of pixel blend ops. It also guarantees alpha channel
   is cleared in the target buffer for stable crc. [Good to get started]
index 20bf5f7..6f55304 100644 (file)
@@ -55,7 +55,7 @@ static struct _ati_generic_private {
 
 static int ati_create_page_map(struct ati_page_map *page_map)
 {
-       int i, err = 0;
+       int i, err;
 
        page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL);
        if (page_map->real == NULL)
@@ -63,6 +63,10 @@ static int ati_create_page_map(struct ati_page_map *page_map)
 
        set_memory_uc((unsigned long)page_map->real, 1);
        err = map_page_into_agp(virt_to_page(page_map->real));
+       if (err) {
+               free_page((unsigned long)page_map->real);
+               return err;
+       }
        page_map->remapped = page_map->real;
 
        for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) {
@@ -303,7 +307,7 @@ static int ati_insert_memory(struct agp_memory * mem,
        for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
                addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
                cur_gatt = GET_GATT(addr);
-               writel(agp_bridge->driver->mask_memory(agp_bridge,      
+               writel(agp_bridge->driver->mask_memory(agp_bridge,
                                                       page_to_phys(mem->pages[i]),
                                                       mem->type),
                       cur_gatt+GET_GATT_OFF(addr));
index 004a3ce..0e19c60 100644 (file)
@@ -62,6 +62,7 @@ EXPORT_SYMBOL(agp_find_bridge);
 
 /**
  *     agp_backend_acquire  -  attempt to acquire an agp backend.
+ *     @pdev: the PCI device
  *
  */
 struct agp_bridge_data *agp_backend_acquire(struct pci_dev *pdev)
@@ -83,6 +84,7 @@ EXPORT_SYMBOL(agp_backend_acquire);
 
 /**
  *     agp_backend_release  -  release the lock on the agp backend.
+ *     @bridge: the AGP backend to release
  *
  *     The caller must insure that the graphics aperture translation table
  *     is read for use by another entity.
index 00ff5fc..321118a 100644 (file)
@@ -39,7 +39,9 @@
 #include <linux/fs.h>
 #include <linux/sched.h>
 #include <linux/uaccess.h>
+
 #include "agp.h"
+#include "compat_ioctl.h"
 
 struct agp_front_data agp_fe;
 
@@ -1017,7 +1019,7 @@ static long agp_ioctl(struct file *file,
        case AGPIOC_UNBIND:
                ret_val = agpioc_unbind_wrap(curr_priv, (void __user *) arg);
                break;
-              
+
        case AGPIOC_CHIPSET_FLUSH:
                break;
        }
index f78e756..826dbd0 100644 (file)
@@ -261,7 +261,8 @@ static int nvidia_remove_memory(struct agp_memory *mem, off_t pg_start, int type
 static void nvidia_tlbflush(struct agp_memory *mem)
 {
        unsigned long end;
-       u32 wbc_reg, temp;
+       u32 wbc_reg;
+       u32 __maybe_unused temp;
        int i;
 
        /* flush chipset */
index f875970..b91da59 100644 (file)
@@ -262,13 +262,10 @@ static void serverworks_tlbflush(struct agp_memory *temp)
 
 static int serverworks_configure(void)
 {
-       struct aper_size_info_lvl2 *current_size;
        u32 temp;
        u8 enable_reg;
        u16 cap_reg;
 
-       current_size = A_SIZE_LVL2(agp_bridge->current_size);
-
        /* Get the memory mapped registers */
        pci_read_config_dword(agp_bridge->dev, serverworks_private.mm_addr_ofs, &temp);
        temp = (temp & PCI_BASE_ADDRESS_MEM_MASK);
@@ -350,7 +347,7 @@ static int serverworks_insert_memory(struct agp_memory *mem,
        for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
                addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr;
                cur_gatt = SVRWRKS_GET_GATT(addr);
-               writel(agp_bridge->driver->mask_memory(agp_bridge, 
+               writel(agp_bridge->driver->mask_memory(agp_bridge,
                                page_to_phys(mem->pages[i]), mem->type),
                       cur_gatt+GET_GATT_OFF(addr));
        }
index a460ae3..b2f484f 100644 (file)
@@ -128,9 +128,6 @@ static int via_fetch_size_agp3(void)
 static int via_configure_agp3(void)
 {
        u32 temp;
-       struct aper_size_info_16 *current_size;
-
-       current_size = A_SIZE_16(agp_bridge->current_size);
 
        /* address to map to */
        agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev,
index 4deea75..6dd9a40 100644 (file)
@@ -542,57 +542,45 @@ EXPORT_SYMBOL(dma_resv_copy_fences);
  * dma_resv_get_fences - Get an object's shared and exclusive
  * fences without update side lock held
  * @obj: the reservation object
- * @fence_excl: the returned exclusive fence (or NULL)
- * @shared_count: the number of shared fences returned
- * @shared: the array of shared fence ptrs returned (array is krealloc'd to
- * the required size, and must be freed by caller)
+ * @write: true if we should return all fences
+ * @num_fences: the number of fences returned
+ * @fences: the array of fence ptrs returned (array is krealloc'd to the
+ * required size, and must be freed by caller)
  *
- * Retrieve all fences from the reservation object. If the pointer for the
- * exclusive fence is not specified the fence is put into the array of the
- * shared fences as well. Returns either zero or -ENOMEM.
+ * Retrieve all fences from the reservation object.
+ * Returns either zero or -ENOMEM.
  */
-int dma_resv_get_fences(struct dma_resv *obj, struct dma_fence **fence_excl,
-                       unsigned int *shared_count, struct dma_fence ***shared)
+int dma_resv_get_fences(struct dma_resv *obj, bool write,
+                       unsigned int *num_fences, struct dma_fence ***fences)
 {
        struct dma_resv_iter cursor;
        struct dma_fence *fence;
 
-       *shared_count = 0;
-       *shared = NULL;
+       *num_fences = 0;
+       *fences = NULL;
 
-       if (fence_excl)
-               *fence_excl = NULL;
-
-       dma_resv_iter_begin(&cursor, obj, true);
+       dma_resv_iter_begin(&cursor, obj, write);
        dma_resv_for_each_fence_unlocked(&cursor, fence) {
 
                if (dma_resv_iter_is_restarted(&cursor)) {
                        unsigned int count;
 
-                       while (*shared_count)
-                               dma_fence_put((*shared)[--(*shared_count)]);
-
-                       if (fence_excl)
-                               dma_fence_put(*fence_excl);
+                       while (*num_fences)
+                               dma_fence_put((*fences)[--(*num_fences)]);
 
-                       count = cursor.shared_count;
-                       count += fence_excl ? 0 : 1;
+                       count = cursor.shared_count + 1;
 
                        /* Eventually re-allocate the array */
-                       *shared = krealloc_array(*shared, count,
+                       *fences = krealloc_array(*fences, count,
                                                 sizeof(void *),
                                                 GFP_KERNEL);
-                       if (count && !*shared) {
+                       if (count && !*fences) {
                                dma_resv_iter_end(&cursor);
                                return -ENOMEM;
                        }
                }
 
-               dma_fence_get(fence);
-               if (dma_resv_iter_is_exclusive(&cursor) && fence_excl)
-                       *fence_excl = fence;
-               else
-                       (*shared)[(*shared_count)++] = fence;
+               (*fences)[(*num_fences)++] = dma_fence_get(fence);
        }
        dma_resv_iter_end(&cursor);
 
index bc32b3e..cbe999c 100644 (file)
@@ -275,7 +275,7 @@ static int test_shared_for_each_unlocked(void *arg)
 
 static int test_get_fences(void *arg, bool shared)
 {
-       struct dma_fence *f, *excl = NULL, **fences = NULL;
+       struct dma_fence *f, **fences = NULL;
        struct dma_resv resv;
        int r, i;
 
@@ -304,35 +304,19 @@ static int test_get_fences(void *arg, bool shared)
        }
        dma_resv_unlock(&resv);
 
-       r = dma_resv_get_fences(&resv, &excl, &i, &fences);
+       r = dma_resv_get_fences(&resv, shared, &i, &fences);
        if (r) {
                pr_err("get_fences failed\n");
                goto err_free;
        }
 
-       if (shared) {
-               if (excl != NULL) {
-                       pr_err("get_fences returned unexpected excl fence\n");
-                       goto err_free;
-               }
-               if (i != 1 || fences[0] != f) {
-                       pr_err("get_fences returned unexpected shared fence\n");
-                       goto err_free;
-               }
-       } else {
-               if (excl != f) {
-                       pr_err("get_fences returned unexpected excl fence\n");
-                       goto err_free;
-               }
-               if (i != 0) {
-                       pr_err("get_fences returned unexpected shared fence\n");
-                       goto err_free;
-               }
+       if (i != 1 || fences[0] != f) {
+               pr_err("get_fences returned unexpected fence\n");
+               goto err_free;
        }
 
        dma_fence_signal(f);
 err_free:
-       dma_fence_put(excl);
        while (i--)
                dma_fence_put(fences[i]);
        kfree(fences);
index c57a609..e733068 100644 (file)
@@ -190,6 +190,10 @@ static long udmabuf_create(struct miscdevice *device,
                if (ubuf->pagecount > pglimit)
                        goto err;
        }
+
+       if (!ubuf->pagecount)
+               goto err;
+
        ubuf->pages = kmalloc_array(ubuf->pagecount, sizeof(*ubuf->pages),
                                    GFP_KERNEL);
        if (!ubuf->pages) {
index 303a491..76c4abc 100644 (file)
@@ -99,7 +99,7 @@ __init int sysfb_create_simplefb(const struct screen_info *si,
 
        /* setup IORESOURCE_MEM as framebuffer memory */
        memset(&res, 0, sizeof(res));
-       res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+       res.flags = IORESOURCE_MEM;
        res.name = simplefb_resname;
        res.start = base;
        res.end = res.start + length - 1;
index b1f22e4..dfdd3ec 100644 (file)
@@ -68,6 +68,7 @@ config DRM_DEBUG_SELFTEST
        depends on DRM
        depends on DEBUG_KERNEL
        select PRIME_NUMBERS
+       select DRM_DP_HELPER
        select DRM_LIB_RANDOM
        select DRM_KMS_HELPER
        select DRM_EXPORT_FOR_TESTS if m
@@ -80,6 +81,12 @@ config DRM_DEBUG_SELFTEST
 
          If in doubt, say "N".
 
+config DRM_DP_HELPER
+       tristate
+       depends on DRM
+       help
+         DRM helpers for DisplayPort.
+
 config DRM_KMS_HELPER
        tristate
        depends on DRM
@@ -198,6 +205,12 @@ config DRM_TTM
          GPU memory types. Will be enabled automatically if a device driver
          uses it.
 
+config DRM_BUDDY
+       tristate
+       depends on DRM
+       help
+         A page based buddy allocator
+
 config DRM_VRAM_HELPER
        tristate
        depends on DRM
@@ -236,6 +249,7 @@ config DRM_RADEON
        depends on DRM && PCI && MMU
        depends on AGP || !AGP
        select FW_LOADER
+       select DRM_DP_HELPER
         select DRM_KMS_HELPER
         select DRM_TTM
        select DRM_TTM_HELPER
@@ -256,6 +270,7 @@ config DRM_AMDGPU
        tristate "AMD GPU"
        depends on DRM && PCI && MMU
        select FW_LOADER
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select DRM_SCHED
        select DRM_TTM
index 301a44d..8675c2a 100644 (file)
@@ -31,8 +31,6 @@ drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o
 drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 drm-$(CONFIG_DRM_PRIVACY_SCREEN) += drm_privacy_screen.o drm_privacy_screen_x86.o
 
-obj-$(CONFIG_DRM_DP_AUX_BUS) += drm_dp_aux_bus.o
-
 obj-$(CONFIG_DRM_NOMODESET) += drm_nomodeset.o
 
 drm_cma_helper-y := drm_gem_cma_helper.o
@@ -42,27 +40,26 @@ obj-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_cma_helper.o
 drm_shmem_helper-y := drm_gem_shmem_helper.o
 obj-$(CONFIG_DRM_GEM_SHMEM_HELPER) += drm_shmem_helper.o
 
+obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o
+
 drm_vram_helper-y := drm_gem_vram_helper.o
 obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o
 
 drm_ttm_helper-y := drm_gem_ttm_helper.o
 obj-$(CONFIG_DRM_TTM_HELPER) += drm_ttm_helper.o
 
-drm_kms_helper-y := drm_bridge_connector.o drm_crtc_helper.o drm_dp_helper.o \
+drm_kms_helper-y := drm_bridge_connector.o drm_crtc_helper.o \
                drm_dsc.o drm_encoder_slave.o drm_flip_work.o drm_hdcp.o \
                drm_probe_helper.o \
-               drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
-               drm_kms_helper_common.o drm_dp_dual_mode_helper.o \
+               drm_plane_helper.o drm_atomic_helper.o \
+               drm_kms_helper_common.o \
                drm_simple_kms_helper.o drm_modeset_helper.o \
                drm_scdc_helper.o drm_gem_atomic_helper.o \
                drm_gem_framebuffer_helper.o \
                drm_atomic_state_helper.o drm_damage_helper.o \
                drm_format_helper.o drm_self_refresh_helper.o drm_rect.o
-
 drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
 drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
-drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
-drm_kms_helper-$(CONFIG_DRM_DP_CEC) += drm_dp_cec.o
 
 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
 obj-$(CONFIG_DRM_DEBUG_SELFTEST) += selftests/
@@ -72,6 +69,7 @@ obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o
 obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o
 obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o
 obj-y                  += arm/
+obj-y                  += dp/
 obj-$(CONFIG_DRM_TTM)  += ttm/
 obj-$(CONFIG_DRM_SCHED)        += scheduler/
 obj-$(CONFIG_DRM_TDFX) += tdfx/
index c16a270..fa20261 100644 (file)
@@ -26,7 +26,7 @@
 
 #include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/amdgpu_drm.h>
 #include "amdgpu.h"
@@ -175,7 +175,7 @@ int amdgpu_connector_get_monitor_bpc(struct drm_connector *connector)
 
                        /* Check if bpc is within clock limit. Try to degrade gracefully otherwise */
                        if ((bpc == 12) && (mode_clock * 3/2 > max_tmds_clock)) {
-                               if ((connector->display_info.edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) &&
+                               if ((connector->display_info.edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30) &&
                                    (mode_clock * 5/4 <= max_tmds_clock))
                                        bpc = 10;
                                else
index 06d0750..e8440d3 100644 (file)
@@ -1274,14 +1274,11 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
                /*
                 * Work around dma_resv shortcommings by wrapping up the
                 * submission in a dma_fence_chain and add it as exclusive
-                * fence, but first add the submission as shared fence to make
-                * sure that shared fences never signal before the exclusive
-                * one.
+                * fence.
                 */
                dma_fence_chain_init(chain, dma_resv_excl_fence(resv),
                                     dma_fence_get(p->fence), 1);
 
-               dma_resv_add_shared_fence(resv, p->fence);
                rcu_assign_pointer(resv->fence_excl, &chain->base);
                e->chain = NULL;
        }
index 82011e7..3f21a13 100644 (file)
@@ -200,8 +200,10 @@ int amdgpu_display_crtc_page_flip_target(struct drm_crtc *crtc,
                goto unpin;
        }
 
-       r = dma_resv_get_fences(new_abo->tbo.base.resv, NULL,
-                               &work->shared_count, &work->shared);
+       /* TODO: Unify this with other drivers */
+       r = dma_resv_get_fences(new_abo->tbo.base.resv, true,
+                               &work->shared_count,
+                               &work->shared);
        if (unlikely(r != 0)) {
                DRM_ERROR("failed to get fences for buffer\n");
                goto unpin;
index c0d8f40..9b12cab 100644 (file)
@@ -226,12 +226,6 @@ static void amdgpu_gem_object_close(struct drm_gem_object *obj,
        if (!amdgpu_vm_ready(vm))
                goto out_unlock;
 
-       fence = dma_resv_excl_fence(bo->tbo.base.resv);
-       if (fence) {
-               amdgpu_bo_fence(bo, fence, true);
-               fence = NULL;
-       }
-
        r = amdgpu_vm_clear_freed(adev, vm, &fence);
        if (r || !fence)
                goto out_unlock;
index 72022df..e0c7fbe 100644 (file)
@@ -167,6 +167,7 @@ static int amdgpu_gtt_mgr_new(struct ttm_resource_manager *man,
        return 0;
 
 err_free:
+       ttm_resource_fini(man, &node->base.base);
        kfree(node);
 
 err_out:
@@ -198,6 +199,7 @@ static void amdgpu_gtt_mgr_del(struct ttm_resource_manager *man,
        if (!(res->placement & TTM_PL_FLAG_TEMPORARY))
                atomic64_sub(res->num_pages, &mgr->used);
 
+       ttm_resource_fini(man, res);
        kfree(node);
 }
 
@@ -286,7 +288,8 @@ int amdgpu_gtt_mgr_init(struct amdgpu_device *adev, uint64_t gtt_size)
        man->use_tt = true;
        man->func = &amdgpu_gtt_mgr_func;
 
-       ttm_resource_manager_init(man, gtt_size >> PAGE_SHIFT);
+       ttm_resource_manager_init(man, &adev->mman.bdev,
+                                 gtt_size >> PAGE_SHIFT);
 
        start = AMDGPU_GTT_MAX_TRANSFER_SIZE * AMDGPU_GTT_NUM_TRANSFER_WINDOWS;
        size = (adev->gmc.gart_size >> PAGE_SHIFT) - start;
index b7fb72b..be48487 100644 (file)
@@ -112,7 +112,7 @@ void amdgpu_pasid_free_delayed(struct dma_resv *resv,
        unsigned count;
        int r;
 
-       r = dma_resv_get_fences(resv, NULL, &count, &fences);
+       r = dma_resv_get_fences(resv, true, &count, &fences);
        if (r)
                goto fallback;
 
index 6043bf6..902235f 100644 (file)
@@ -33,7 +33,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_fixed.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
@@ -44,7 +44,7 @@
 #include <linux/hrtimer.h>
 #include "amdgpu_irq.h"
 
-#include <drm/drm_dp_mst_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
 #include "modules/inc/mod_freesync.h"
 #include "amdgpu_dm_irq_params.h"
 
index 786afe4..0d85c20 100644 (file)
@@ -95,6 +95,7 @@ static void amdgpu_preempt_mgr_del(struct ttm_resource_manager *man,
        struct amdgpu_preempt_mgr *mgr = to_preempt_mgr(man);
 
        atomic64_sub(res->num_pages, &mgr->used);
+       ttm_resource_fini(man, res);
        kfree(res);
 }
 
@@ -152,7 +153,7 @@ int amdgpu_preempt_mgr_init(struct amdgpu_device *adev)
        man->use_tt = true;
        man->func = &amdgpu_preempt_mgr_func;
 
-       ttm_resource_manager_init(man, (1 << 30));
+       ttm_resource_manager_init(man, &adev->mman.bdev, (1 << 30));
 
        atomic64_set(&mgr->used, 0);
 
index 5c3f240..d178fbe 100644 (file)
@@ -2087,7 +2087,7 @@ static int amdgpu_mm_vram_table_show(struct seq_file *m, void *unused)
                                                            TTM_PL_VRAM);
        struct drm_printer p = drm_seq_file_printer(m);
 
-       man->func->debug(man, &p);
+       ttm_resource_manager_debug(man, &p);
        return 0;
 }
 
@@ -2105,7 +2105,7 @@ static int amdgpu_mm_tt_table_show(struct seq_file *m, void *unused)
                                                            TTM_PL_TT);
        struct drm_printer p = drm_seq_file_printer(m);
 
-       man->func->debug(man, &p);
+       ttm_resource_manager_debug(man, &p);
        return 0;
 }
 
@@ -2116,7 +2116,7 @@ static int amdgpu_mm_gds_table_show(struct seq_file *m, void *unused)
                                                            AMDGPU_PL_GDS);
        struct drm_printer p = drm_seq_file_printer(m);
 
-       man->func->debug(man, &p);
+       ttm_resource_manager_debug(man, &p);
        return 0;
 }
 
@@ -2127,7 +2127,7 @@ static int amdgpu_mm_gws_table_show(struct seq_file *m, void *unused)
                                                            AMDGPU_PL_GWS);
        struct drm_printer p = drm_seq_file_printer(m);
 
-       man->func->debug(man, &p);
+       ttm_resource_manager_debug(man, &p);
        return 0;
 }
 
@@ -2138,7 +2138,7 @@ static int amdgpu_mm_oa_table_show(struct seq_file *m, void *unused)
                                                            AMDGPU_PL_OA);
        struct drm_printer p = drm_seq_file_printer(m);
 
-       man->func->debug(man, &p);
+       ttm_resource_manager_debug(man, &p);
        return 0;
 }
 
index 7a2b487..7442095 100644 (file)
@@ -472,6 +472,7 @@ error_free:
        while (i--)
                drm_mm_remove_node(&node->mm_nodes[i]);
        spin_unlock(&mgr->lock);
+       ttm_resource_fini(man, &node->base);
        kvfree(node);
 
 error_sub:
@@ -511,6 +512,7 @@ static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man,
        atomic64_sub(usage, &mgr->usage);
        atomic64_sub(vis_usage, &mgr->vis_usage);
 
+       ttm_resource_fini(man, res);
        kvfree(node);
 }
 
@@ -689,7 +691,8 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
        struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
        struct ttm_resource_manager *man = &mgr->manager;
 
-       ttm_resource_manager_init(man, adev->gmc.real_vram_size >> PAGE_SHIFT);
+       ttm_resource_manager_init(man, &adev->mman.bdev,
+                                 adev->gmc.real_vram_size >> PAGE_SHIFT);
 
        man->func = &amdgpu_vram_mgr_func;
 
index f327bec..49a2f59 100644 (file)
@@ -34,7 +34,7 @@
 #include "atombios_dp.h"
 #include "amdgpu_connectors.h"
 #include "amdgpu_atombios.h"
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 /* move these to drm_dp_helper.c/h */
 #define DP_LINK_CONFIGURATION_SIZE 9
index 7f9773f..526076e 100644 (file)
@@ -76,7 +76,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_uapi.h>
 #include <drm/drm_atomic_helper.h>
-#include <drm/drm_dp_mst_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_edid.h>
@@ -5856,7 +5856,7 @@ static void fill_stream_properties_from_drm_display_mode(
        else if (drm_mode_is_420_also(info, mode_in)
                        && aconnector->force_yuv420_output)
                timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
-       else if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB444)
+       else if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCBCR444)
                        && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
                timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
        else
index b9a69b0..bee806a 100644 (file)
@@ -29,7 +29,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
-#include <drm/drm_dp_mst_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
 #include <drm/drm_plane.h>
 
 /*
index cc34a35..35c944a 100644 (file)
@@ -25,8 +25,8 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
-#include <drm/drm_dp_mst_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include "dm_services.h"
 #include "amdgpu.h"
 #include "amdgpu_dm.h"
index 7f25c11..48a1876 100644 (file)
@@ -27,7 +27,7 @@
 #include <dc_link.h>
 #include <inc/link_hwss.h>
 #include <inc/link_dpcd.h>
-#include "drm/drm_dp_helper.h"
+#include <drm/dp/drm_dp_helper.h>
 #include <dc_dp_types.h>
 #include "dm_helpers.h"
 
index 9c74564..efc2339 100644 (file)
@@ -25,7 +25,7 @@
 #include <drm/drm_dsc.h>
 #include "dc_hw_types.h"
 #include "dsc.h"
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include "dc.h"
 #include "rc_calc.h"
 #include "fixed31_32.h"
index 5df1d80..17d0507 100644 (file)
@@ -36,7 +36,7 @@
 #include <asm/byteorder.h>
 
 #include <drm/drm_print.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 #include "cgs_common.h"
 
index ffd0df1..270260e 100644 (file)
@@ -26,7 +26,7 @@
 #ifndef __DAL_DPCD_DEFS_H__
 #define __DAL_DPCD_DEFS_H__
 
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #ifndef DP_SINK_HW_REVISION_START // can remove this once the define gets into linux drm_dp_helper.h
 #define DP_SINK_HW_REVISION_START 0x409
 #endif
index 399fbca..8502263 100644 (file)
@@ -30,7 +30,7 @@
 #include "hdcp_log.h"
 
 #include <drm/drm_hdcp.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 enum mod_hdcp_trans_input_result {
        UNKNOWN = 0,
index 8a02ade..42510fd 100644 (file)
@@ -1078,11 +1078,11 @@ static void d71_improc_update(struct komeda_component *c,
        mask |= IPS_CTRL_YUV | IPS_CTRL_CHD422 | IPS_CTRL_CHD420;
 
        /* config color format */
-       if (st->color_format == DRM_COLOR_FORMAT_YCRCB420)
+       if (st->color_format == DRM_COLOR_FORMAT_YCBCR420)
                ctrl |= IPS_CTRL_YUV | IPS_CTRL_CHD422 | IPS_CTRL_CHD420;
-       else if (st->color_format == DRM_COLOR_FORMAT_YCRCB422)
+       else if (st->color_format == DRM_COLOR_FORMAT_YCBCR422)
                ctrl |= IPS_CTRL_YUV | IPS_CTRL_CHD422;
-       else if (st->color_format == DRM_COLOR_FORMAT_YCRCB444)
+       else if (st->color_format == DRM_COLOR_FORMAT_YCBCR444)
                ctrl |= IPS_CTRL_YUV;
 
        malidp_write32_mask(reg, BLK_CONTROL, mask, ctrl);
@@ -1144,11 +1144,11 @@ static int d71_improc_init(struct d71_dev *d71,
        improc = to_improc(c);
        improc->supported_color_depths = BIT(8) | BIT(10);
        improc->supported_color_formats = DRM_COLOR_FORMAT_RGB444 |
-                                         DRM_COLOR_FORMAT_YCRCB444 |
-                                         DRM_COLOR_FORMAT_YCRCB422;
+                                         DRM_COLOR_FORMAT_YCBCR444 |
+                                         DRM_COLOR_FORMAT_YCBCR422;
        value = malidp_read32(reg, BLK_INFO);
        if (value & IPS_INFO_CHD420)
-               improc->supported_color_formats |= DRM_COLOR_FORMAT_YCRCB420;
+               improc->supported_color_formats |= DRM_COLOR_FORMAT_YCBCR420;
 
        improc->supports_csc = true;
        improc->supports_gamma = true;
index e793393..51e51ff 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/platform_device.h>
 #include <linux/component.h>
 #include <linux/pm_runtime.h>
+#include <drm/drm_module.h>
 #include <drm/drm_of.h>
 #include "komeda_dev.h"
 #include "komeda_kms.h"
@@ -198,7 +199,7 @@ static struct platform_driver komeda_platform_driver = {
        },
 };
 
-module_platform_driver(komeda_platform_driver);
+drm_module_platform_driver(komeda_platform_driver);
 
 MODULE_AUTHOR("James.Qian.Wang <james.qian.wang@arm.com>");
 MODULE_DESCRIPTION("Komeda KMS driver");
index 479c242..e89ae0e 100644 (file)
@@ -30,6 +30,7 @@
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_modeset_helper.h>
+#include <drm/drm_module.h>
 #include <drm/drm_of.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_vblank.h>
@@ -434,7 +435,7 @@ static struct platform_driver hdlcd_platform_driver = {
        },
 };
 
-module_platform_driver(hdlcd_platform_driver);
+drm_module_platform_driver(hdlcd_platform_driver);
 
 MODULE_AUTHOR("Liviu Dudau");
 MODULE_DESCRIPTION("ARM HDLCD DRM driver");
index 78d15b0..d5aef21 100644 (file)
@@ -25,6 +25,7 @@
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_modeset_helper.h>
+#include <drm/drm_module.h>
 #include <drm/drm_of.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_vblank.h>
@@ -1008,7 +1009,7 @@ static struct platform_driver malidp_platform_driver = {
        },
 };
 
-module_platform_driver(malidp_platform_driver);
+drm_module_platform_driver(malidp_platform_driver);
 
 MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>");
 MODULE_DESCRIPTION("ARM Mali DP DRM driver");
index 6d8613f..7465c4f 100644 (file)
@@ -34,6 +34,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_gem_vram_helper.h>
+#include <drm/drm_module.h>
 #include <drm/drm_probe_helper.h>
 
 #include "ast_drv.h"
@@ -230,22 +231,7 @@ static struct pci_driver ast_pci_driver = {
        .driver.pm = &ast_pm_ops,
 };
 
-static int __init ast_init(void)
-{
-       if (drm_firmware_drivers_only() && ast_modeset == -1)
-               return -EINVAL;
-
-       if (ast_modeset == 0)
-               return -EINVAL;
-       return pci_register_driver(&ast_pci_driver);
-}
-static void __exit ast_exit(void)
-{
-       pci_unregister_driver(&ast_pci_driver);
-}
-
-module_init(ast_init);
-module_exit(ast_exit);
+drm_module_pci_driver_if_modeset(ast_pci_driver, ast_modeset);
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
index 79a3618..22e9e2d 100644 (file)
@@ -209,6 +209,8 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post)
                        if (ast->chip == AST2500 &&
                            scu_rev == 0x100)           /* ast2510 */
                                ast->support_wide_screen = true;
+                       if (ast->chip == AST2600)               /* ast2600 */
+                               ast->support_wide_screen = true;
                }
                break;
        }
index 956c898..ab52efb 100644 (file)
@@ -471,7 +471,10 @@ static void ast_set_color_reg(struct ast_private *ast,
 static void ast_set_crtthd_reg(struct ast_private *ast)
 {
        /* Set Threshold */
-       if (ast->chip == AST2300 || ast->chip == AST2400 ||
+       if (ast->chip == AST2600) {
+               ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0xe0);
+               ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0xa0);
+       } else if (ast->chip == AST2300 || ast->chip == AST2400 ||
            ast->chip == AST2500) {
                ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x78);
                ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x60);
index 61db5a6..fcd93f1 100644 (file)
@@ -30,6 +30,7 @@ config DRM_CDNS_DSI
 config DRM_CHIPONE_ICN6211
        tristate "Chipone ICN6211 MIPI-DSI/RGB Converter bridge"
        depends on OF
+       depends on DRM_KMS_HELPER
        select DRM_MIPI_DSI
        select DRM_PANEL_BRIDGE
        help
@@ -183,6 +184,7 @@ config DRM_PARADE_PS8640
        tristate "Parade PS8640 MIPI DSI to eDP Converter"
        depends on OF
        select DRM_DP_AUX_BUS
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select DRM_MIPI_DSI
        select DRM_PANEL
@@ -253,6 +255,7 @@ config DRM_TOSHIBA_TC358764
 config DRM_TOSHIBA_TC358767
        tristate "Toshiba TC358767 eDP bridge"
        depends on OF
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select REGMAP_I2C
        select DRM_PANEL
@@ -272,6 +275,7 @@ config DRM_TOSHIBA_TC358768
 config DRM_TOSHIBA_TC358775
        tristate "Toshiba TC358775 DSI/LVDS bridge"
        depends on OF
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select REGMAP_I2C
        select DRM_PANEL
@@ -299,6 +303,7 @@ config DRM_TI_SN65DSI83
 config DRM_TI_SN65DSI86
        tristate "TI SN65DSI86 DSI to eDP bridge"
        depends on OF
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select REGMAP_I2C
        select DRM_PANEL
index 592ecfc..6a88289 100644 (file)
 #define ADV7511_PACKET_ENABLE_SPARE2           BIT(1)
 #define ADV7511_PACKET_ENABLE_SPARE1           BIT(0)
 
+#define ADV7535_REG_POWER2_HPD_OVERRIDE                BIT(6)
 #define ADV7511_REG_POWER2_HPD_SRC_MASK                0xc0
 #define ADV7511_REG_POWER2_HPD_SRC_BOTH                0x00
 #define ADV7511_REG_POWER2_HPD_SRC_HPD         0x40
index f8e5da1..005bf18 100644 (file)
@@ -223,7 +223,7 @@ static void adv7511_set_config_csc(struct adv7511 *adv7511,
                config.csc_coefficents = adv7511_csc_ycbcr_to_rgb;
 
                if ((connector->display_info.color_formats &
-                    DRM_COLOR_FORMAT_YCRCB422) &&
+                    DRM_COLOR_FORMAT_YCBCR422) &&
                    config.hdmi_mode) {
                        config.csc_enable = false;
                        config.avi_infoframe.colorspace =
@@ -351,11 +351,17 @@ static void __adv7511_power_on(struct adv7511 *adv7511)
         * from standby or are enabled. When the HPD goes low the adv7511 is
         * reset and the outputs are disabled which might cause the monitor to
         * go to standby again. To avoid this we ignore the HPD pin for the
-        * first few seconds after enabling the output.
+        * first few seconds after enabling the output. On the other hand
+        * adv7535 require to enable HPD Override bit for proper HPD.
         */
-       regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
-                          ADV7511_REG_POWER2_HPD_SRC_MASK,
-                          ADV7511_REG_POWER2_HPD_SRC_NONE);
+       if (adv7511->type == ADV7535)
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
+                                  ADV7535_REG_POWER2_HPD_OVERRIDE,
+                                  ADV7535_REG_POWER2_HPD_OVERRIDE);
+       else
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
+                                  ADV7511_REG_POWER2_HPD_SRC_MASK,
+                                  ADV7511_REG_POWER2_HPD_SRC_NONE);
 }
 
 static void adv7511_power_on(struct adv7511 *adv7511)
@@ -375,6 +381,10 @@ static void adv7511_power_on(struct adv7511 *adv7511)
 static void __adv7511_power_off(struct adv7511 *adv7511)
 {
        /* TODO: setup additional power down modes */
+       if (adv7511->type == ADV7535)
+               regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
+                                  ADV7535_REG_POWER2_HPD_OVERRIDE, 0);
+
        regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
                           ADV7511_POWER_POWER_DOWN,
                           ADV7511_POWER_POWER_DOWN);
@@ -672,9 +682,14 @@ adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector)
                        status = connector_status_disconnected;
        } else {
                /* Renable HPD sensing */
-               regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
-                                  ADV7511_REG_POWER2_HPD_SRC_MASK,
-                                  ADV7511_REG_POWER2_HPD_SRC_BOTH);
+               if (adv7511->type == ADV7535)
+                       regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
+                                          ADV7535_REG_POWER2_HPD_OVERRIDE,
+                                          ADV7535_REG_POWER2_HPD_OVERRIDE);
+               else
+                       regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
+                                          ADV7511_REG_POWER2_HPD_SRC_MASK,
+                                          ADV7511_REG_POWER2_HPD_SRC_BOTH);
        }
 
        adv7511->status = status;
index eb7579d..ef62708 100644 (file)
@@ -29,7 +29,7 @@ static void adv7511_dsi_config_timing_gen(struct adv7511 *adv)
        struct mipi_dsi_device *dsi = adv->dsi;
        struct drm_display_mode *mode = &adv->curr_mode;
        unsigned int hsw, hfp, hbp, vsw, vfp, vbp;
-       u8 clock_div_by_lanes[] = { 6, 4, 3 };  /* 2, 3, 4 lanes */
+       static const u8 clock_div_by_lanes[] = { 6, 4, 3 };     /* 2, 3, 4 lanes */
 
        hsw = mode->hsync_end - mode->hsync_start;
        hfp = mode->hsync_start - mode->hdisplay;
index 2ef6eb2..319ba0d 100644 (file)
@@ -3,6 +3,7 @@ config DRM_ANALOGIX_ANX6345
        tristate "Analogix ANX6345 bridge"
        depends on OF
        select DRM_ANALOGIX_DP
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select REGMAP_I2C
        help
@@ -14,6 +15,7 @@ config DRM_ANALOGIX_ANX6345
 config DRM_ANALOGIX_ANX78XX
        tristate "Analogix ANX78XX bridge"
        select DRM_ANALOGIX_DP
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select REGMAP_I2C
        help
index e33cd07..94e56a2 100644 (file)
@@ -22,7 +22,7 @@
 #include <drm/drm_bridge.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
index 5e6a0ed..2768b41 100644 (file)
@@ -21,7 +21,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_crtc.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
index fe40bab..e829716 100644 (file)
@@ -8,7 +8,7 @@
 #include <linux/regmap.h>
 
 #include <drm/drm.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_print.h>
 
 #include "analogix-i2c-dptx.h"
index b7d2e44..eb590fb 100644 (file)
@@ -1537,9 +1537,9 @@ static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
                video->color_depth = COLOR_8;
                break;
        }
-       if (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+       if (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
                video->color_space = COLOR_YCBCR444;
-       else if (display_info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+       else if (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
                video->color_space = COLOR_YCBCR422;
        else
                video->color_space = COLOR_RGB;
index c051502..3266520 100644 (file)
@@ -10,7 +10,7 @@
 #define _ANALOGIX_DP_CORE_H
 
 #include <drm/drm_crtc.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 #define DP_TIMEOUT_LOOP_COUNT 100
 #define MAX_CR_LOOP 5
index 2346dbc..76662fc 100644 (file)
@@ -24,8 +24,9 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_crtc_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_hdcp.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
@@ -213,6 +214,65 @@ static int wait_aux_op_finish(struct anx7625_data *ctx)
        return 0;
 }
 
+static int anx7625_aux_dpcd_read(struct anx7625_data *ctx,
+                                u32 address, u8 len, u8 *buf)
+{
+       struct device *dev = &ctx->client->dev;
+       int ret;
+       u8 addrh, addrm, addrl;
+       u8 cmd;
+
+       if (len > MAX_DPCD_BUFFER_SIZE) {
+               dev_err(dev, "exceed aux buffer len.\n");
+               return -EINVAL;
+       }
+
+       addrl = address & 0xFF;
+       addrm = (address >> 8) & 0xFF;
+       addrh = (address >> 16) & 0xFF;
+
+       cmd = DPCD_CMD(len, DPCD_READ);
+       cmd = ((len - 1) << 4) | 0x09;
+
+       /* Set command and length */
+       ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                               AP_AUX_COMMAND, cmd);
+
+       /* Set aux access address */
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                AP_AUX_ADDR_7_0, addrl);
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                AP_AUX_ADDR_15_8, addrm);
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                AP_AUX_ADDR_19_16, addrh);
+
+       /* Enable aux access */
+       ret |= anx7625_write_or(ctx, ctx->i2c.rx_p0_client,
+                               AP_AUX_CTRL_STATUS, AP_AUX_CTRL_OP_EN);
+
+       if (ret < 0) {
+               dev_err(dev, "cannot access aux related register.\n");
+               return -EIO;
+       }
+
+       usleep_range(2000, 2100);
+
+       ret = wait_aux_op_finish(ctx);
+       if (ret) {
+               dev_err(dev, "aux IO error: wait aux op finish.\n");
+               return ret;
+       }
+
+       ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client,
+                                    AP_AUX_BUFF_START, len, buf);
+       if (ret < 0) {
+               dev_err(dev, "read dpcd register failed\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
 static int anx7625_video_mute_control(struct anx7625_data *ctx,
                                      u8 status)
 {
@@ -669,6 +729,165 @@ static int anx7625_dpi_config(struct anx7625_data *ctx)
        return ret;
 }
 
+static int anx7625_read_flash_status(struct anx7625_data *ctx)
+{
+       return anx7625_reg_read(ctx, ctx->i2c.rx_p0_client, R_RAM_CTRL);
+}
+
+static int anx7625_hdcp_key_probe(struct anx7625_data *ctx)
+{
+       int ret, val;
+       struct device *dev = &ctx->client->dev;
+       u8 ident[FLASH_BUF_LEN];
+
+       ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                               FLASH_ADDR_HIGH, 0x91);
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                FLASH_ADDR_LOW, 0xA0);
+       if (ret < 0) {
+               dev_err(dev, "IO error : set key flash address.\n");
+               return ret;
+       }
+
+       ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                               FLASH_LEN_HIGH, (FLASH_BUF_LEN - 1) >> 8);
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                FLASH_LEN_LOW, (FLASH_BUF_LEN - 1) & 0xFF);
+       if (ret < 0) {
+               dev_err(dev, "IO error : set key flash len.\n");
+               return ret;
+       }
+
+       ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                               R_FLASH_RW_CTRL, FLASH_READ);
+       ret |= readx_poll_timeout(anx7625_read_flash_status,
+                                 ctx, val,
+                                 ((val & FLASH_DONE) || (val < 0)),
+                                 2000,
+                                 2000 * 150);
+       if (ret) {
+               dev_err(dev, "flash read access fail!\n");
+               return -EIO;
+       }
+
+       ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client,
+                                    FLASH_BUF_BASE_ADDR,
+                                    FLASH_BUF_LEN, ident);
+       if (ret < 0) {
+               dev_err(dev, "read flash data fail!\n");
+               return -EIO;
+       }
+
+       if (ident[29] == 0xFF && ident[30] == 0xFF && ident[31] == 0xFF)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int anx7625_hdcp_key_load(struct anx7625_data *ctx)
+{
+       int ret;
+       struct device *dev = &ctx->client->dev;
+
+       /* Select HDCP 1.4 KEY */
+       ret = anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                               R_BOOT_RETRY, 0x12);
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                FLASH_ADDR_HIGH, HDCP14KEY_START_ADDR >> 8);
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                FLASH_ADDR_LOW, HDCP14KEY_START_ADDR & 0xFF);
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                R_RAM_LEN_H, HDCP14KEY_SIZE >> 12);
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                R_RAM_LEN_L, HDCP14KEY_SIZE >> 4);
+
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                R_RAM_ADDR_H, 0);
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                R_RAM_ADDR_L, 0);
+       /* Enable HDCP 1.4 KEY load */
+       ret |= anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
+                                R_RAM_CTRL, DECRYPT_EN | LOAD_START);
+       dev_dbg(dev, "load HDCP 1.4 key done\n");
+       return ret;
+}
+
+static int anx7625_hdcp_disable(struct anx7625_data *ctx)
+{
+       int ret;
+       struct device *dev = &ctx->client->dev;
+
+       dev_dbg(dev, "disable HDCP 1.4\n");
+
+       /* Disable HDCP */
+       ret = anx7625_write_and(ctx, ctx->i2c.rx_p1_client, 0xee, 0x9f);
+       /* Try auth flag */
+       ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xec, 0x10);
+       /* Interrupt for DRM */
+       ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xff, 0x01);
+       if (ret < 0)
+               dev_err(dev, "fail to disable HDCP\n");
+
+       return anx7625_write_and(ctx, ctx->i2c.tx_p0_client,
+                                TX_HDCP_CTRL0, ~HARD_AUTH_EN & 0xFF);
+}
+
+static int anx7625_hdcp_enable(struct anx7625_data *ctx)
+{
+       u8 bcap;
+       int ret;
+       struct device *dev = &ctx->client->dev;
+
+       ret = anx7625_hdcp_key_probe(ctx);
+       if (ret) {
+               dev_dbg(dev, "no key found, not to do hdcp\n");
+               return ret;
+       }
+
+       /* Read downstream capability */
+       anx7625_aux_dpcd_read(ctx, 0x68028, 1, &bcap);
+       if (!(bcap & 0x01)) {
+               pr_warn("downstream not support HDCP 1.4, cap(%x).\n", bcap);
+               return 0;
+       }
+
+       dev_dbg(dev, "enable HDCP 1.4\n");
+
+       /* First clear HDCP state */
+       ret = anx7625_reg_write(ctx, ctx->i2c.tx_p0_client,
+                               TX_HDCP_CTRL0,
+                               KSVLIST_VLD | BKSV_SRM_PASS | RE_AUTHEN);
+       usleep_range(1000, 1100);
+       /* Second clear HDCP state */
+       ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p0_client,
+                                TX_HDCP_CTRL0,
+                                KSVLIST_VLD | BKSV_SRM_PASS | RE_AUTHEN);
+
+       /* Set time for waiting KSVR */
+       ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p0_client,
+                                SP_TX_WAIT_KSVR_TIME, 0xc8);
+       /* Set time for waiting R0 */
+       ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p0_client,
+                                SP_TX_WAIT_R0_TIME, 0xb0);
+       ret |= anx7625_hdcp_key_load(ctx);
+       if (ret) {
+               pr_warn("prepare HDCP key failed.\n");
+               return ret;
+       }
+
+       ret = anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xee, 0x20);
+
+       /* Try auth flag */
+       ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xec, 0x10);
+       /* Interrupt for DRM */
+       ret |= anx7625_write_or(ctx, ctx->i2c.rx_p1_client, 0xff, 0x01);
+       if (ret < 0)
+               dev_err(dev, "fail to enable HDCP\n");
+
+       return anx7625_write_or(ctx, ctx->i2c.tx_p0_client,
+                               TX_HDCP_CTRL0, HARD_AUTH_EN);
+}
+
 static void anx7625_dp_start(struct anx7625_data *ctx)
 {
        int ret;
@@ -679,6 +898,9 @@ static void anx7625_dp_start(struct anx7625_data *ctx)
                return;
        }
 
+       /* Disable HDCP */
+       anx7625_write_and(ctx, ctx->i2c.rx_p1_client, 0xee, 0x9f);
+
        if (ctx->pdata.is_dpi)
                ret = anx7625_dpi_config(ctx);
        else
@@ -686,6 +908,10 @@ static void anx7625_dp_start(struct anx7625_data *ctx)
 
        if (ret < 0)
                DRM_DEV_ERROR(dev, "MIPI phy setup error.\n");
+
+       ctx->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+
+       ctx->dp_en = 1;
 }
 
 static void anx7625_dp_stop(struct anx7625_data *ctx)
@@ -705,6 +931,10 @@ static void anx7625_dp_stop(struct anx7625_data *ctx)
        ret |= anx7625_video_mute_control(ctx, 1);
        if (ret < 0)
                DRM_DEV_ERROR(dev, "IO error : mute video fail\n");
+
+       ctx->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+
+       ctx->dp_en = 0;
 }
 
 static int sp_tx_rst_aux(struct anx7625_data *ctx)
@@ -1098,9 +1328,18 @@ static void anx7625_init_gpio(struct anx7625_data *platform)
        /* Gpio for chip power enable */
        platform->pdata.gpio_p_on =
                devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+       if (IS_ERR_OR_NULL(platform->pdata.gpio_p_on)) {
+               DRM_DEV_DEBUG_DRIVER(dev, "no enable gpio found\n");
+               platform->pdata.gpio_p_on = NULL;
+       }
+
        /* Gpio for chip reset */
        platform->pdata.gpio_reset =
                devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR_OR_NULL(platform->pdata.gpio_reset)) {
+               DRM_DEV_DEBUG_DRIVER(dev, "no reset gpio found\n");
+               platform->pdata.gpio_reset = NULL;
+       }
 
        if (platform->pdata.gpio_p_on && platform->pdata.gpio_reset) {
                platform->pdata.low_power_mode = 1;
@@ -1601,9 +1840,27 @@ static int anx7625_audio_hook_plugged_cb(struct device *dev, void *data,
        return 0;
 }
 
+static int anx7625_audio_get_eld(struct device *dev, void *data,
+                                u8 *buf, size_t len)
+{
+       struct anx7625_data *ctx = dev_get_drvdata(dev);
+
+       if (!ctx->connector) {
+               dev_err(dev, "connector not initial\n");
+               return -EINVAL;
+       }
+
+       dev_dbg(dev, "audio copy eld\n");
+       memcpy(buf, ctx->connector->eld,
+              min(sizeof(ctx->connector->eld), len));
+
+       return 0;
+}
+
 static const struct hdmi_codec_ops anx7625_codec_ops = {
        .hw_params      = anx7625_audio_hw_params,
        .audio_shutdown = anx7625_audio_shutdown,
+       .get_eld        = anx7625_audio_get_eld,
        .get_dai_id     = anx7625_hdmi_i2s_get_dai_id,
        .hook_plugged_cb = anx7625_audio_hook_plugged_cb,
 };
@@ -1660,7 +1917,7 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx)
        host = of_find_mipi_dsi_host_by_node(ctx->pdata.mipi_host_node);
        if (!host) {
                DRM_DEV_ERROR(dev, "fail to find dsi host.\n");
-               return -EINVAL;
+               return -EPROBE_DEFER;
        }
 
        dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
@@ -1688,6 +1945,83 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx)
        return 0;
 }
 
+static void hdcp_check_work_func(struct work_struct *work)
+{
+       u8 status;
+       struct delayed_work *dwork;
+       struct anx7625_data *ctx;
+       struct device *dev;
+       struct drm_device *drm_dev;
+
+       dwork = to_delayed_work(work);
+       ctx = container_of(dwork, struct anx7625_data, hdcp_work);
+       dev = &ctx->client->dev;
+
+       if (!ctx->connector) {
+               dev_err(dev, "HDCP connector is null!");
+               return;
+       }
+
+       drm_dev = ctx->connector->dev;
+       drm_modeset_lock(&drm_dev->mode_config.connection_mutex, NULL);
+       mutex_lock(&ctx->hdcp_wq_lock);
+
+       status = anx7625_reg_read(ctx, ctx->i2c.tx_p0_client, 0);
+       dev_dbg(dev, "sink HDCP status check: %.02x\n", status);
+       if (status & BIT(1)) {
+               ctx->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+               drm_hdcp_update_content_protection(ctx->connector,
+                                                  ctx->hdcp_cp);
+               dev_dbg(dev, "update CP to ENABLE\n");
+       }
+
+       mutex_unlock(&ctx->hdcp_wq_lock);
+       drm_modeset_unlock(&drm_dev->mode_config.connection_mutex);
+}
+
+static int anx7625_connector_atomic_check(struct anx7625_data *ctx,
+                                         struct drm_connector_state *state)
+{
+       struct device *dev = &ctx->client->dev;
+       int cp;
+
+       dev_dbg(dev, "hdcp state check\n");
+       cp = state->content_protection;
+
+       if (cp == ctx->hdcp_cp)
+               return 0;
+
+       if (cp == DRM_MODE_CONTENT_PROTECTION_DESIRED) {
+               if (ctx->dp_en) {
+                       dev_dbg(dev, "enable HDCP\n");
+                       anx7625_hdcp_enable(ctx);
+
+                       queue_delayed_work(ctx->hdcp_workqueue,
+                                          &ctx->hdcp_work,
+                                          msecs_to_jiffies(2000));
+               }
+       }
+
+       if (cp == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+               if (ctx->hdcp_cp != DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+                       dev_err(dev, "current CP is not ENABLED\n");
+                       return -EINVAL;
+               }
+               anx7625_hdcp_disable(ctx);
+               ctx->hdcp_cp = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+               drm_hdcp_update_content_protection(ctx->connector,
+                                                  ctx->hdcp_cp);
+               dev_dbg(dev, "update CP to UNDESIRE\n");
+       }
+
+       if (cp == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+               dev_err(dev, "Userspace illegal set to PROTECTION ENABLE\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int anx7625_bridge_attach(struct drm_bridge *bridge,
                                 enum drm_bridge_attach_flags flags)
 {
@@ -1902,25 +2236,57 @@ static bool anx7625_bridge_mode_fixup(struct drm_bridge *bridge,
        return true;
 }
 
-static void anx7625_bridge_enable(struct drm_bridge *bridge)
+static int anx7625_bridge_atomic_check(struct drm_bridge *bridge,
+                                      struct drm_bridge_state *bridge_state,
+                                      struct drm_crtc_state *crtc_state,
+                                      struct drm_connector_state *conn_state)
+{
+       struct anx7625_data *ctx = bridge_to_anx7625(bridge);
+       struct device *dev = &ctx->client->dev;
+
+       dev_dbg(dev, "drm bridge atomic check\n");
+
+       anx7625_bridge_mode_fixup(bridge, &crtc_state->mode,
+                                 &crtc_state->adjusted_mode);
+
+       return anx7625_connector_atomic_check(ctx, conn_state);
+}
+
+static void anx7625_bridge_atomic_enable(struct drm_bridge *bridge,
+                                        struct drm_bridge_state *state)
 {
        struct anx7625_data *ctx = bridge_to_anx7625(bridge);
        struct device *dev = &ctx->client->dev;
+       struct drm_connector *connector;
 
-       DRM_DEV_DEBUG_DRIVER(dev, "drm enable\n");
+       dev_dbg(dev, "drm atomic enable\n");
+
+       if (!bridge->encoder) {
+               dev_err(dev, "Parent encoder object not found");
+               return;
+       }
+
+       connector = drm_atomic_get_new_connector_for_encoder(state->base.state,
+                                                            bridge->encoder);
+       if (!connector)
+               return;
+
+       ctx->connector = connector;
 
        pm_runtime_get_sync(dev);
 
        anx7625_dp_start(ctx);
 }
 
-static void anx7625_bridge_disable(struct drm_bridge *bridge)
+static void anx7625_bridge_atomic_disable(struct drm_bridge *bridge,
+                                         struct drm_bridge_state *old)
 {
        struct anx7625_data *ctx = bridge_to_anx7625(bridge);
        struct device *dev = &ctx->client->dev;
 
-       DRM_DEV_DEBUG_DRIVER(dev, "drm disable\n");
+       dev_dbg(dev, "drm atomic disable\n");
 
+       ctx->connector = NULL;
        anx7625_dp_stop(ctx);
 
        pm_runtime_put_sync(dev);
@@ -1950,11 +2316,14 @@ static struct edid *anx7625_bridge_get_edid(struct drm_bridge *bridge,
 
 static const struct drm_bridge_funcs anx7625_bridge_funcs = {
        .attach = anx7625_bridge_attach,
-       .disable = anx7625_bridge_disable,
        .mode_valid = anx7625_bridge_mode_valid,
        .mode_set = anx7625_bridge_mode_set,
-       .mode_fixup = anx7625_bridge_mode_fixup,
-       .enable = anx7625_bridge_enable,
+       .atomic_check = anx7625_bridge_atomic_check,
+       .atomic_enable = anx7625_bridge_atomic_enable,
+       .atomic_disable = anx7625_bridge_atomic_disable,
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset = drm_atomic_helper_bridge_reset,
        .detect = anx7625_bridge_detect,
        .get_edid = anx7625_bridge_get_edid,
 };
@@ -1962,40 +2331,54 @@ static const struct drm_bridge_funcs anx7625_bridge_funcs = {
 static int anx7625_register_i2c_dummy_clients(struct anx7625_data *ctx,
                                              struct i2c_client *client)
 {
+       int err = 0;
+
        ctx->i2c.tx_p0_client = i2c_new_dummy_device(client->adapter,
                                                     TX_P0_ADDR >> 1);
-       if (!ctx->i2c.tx_p0_client)
-               return -ENOMEM;
+       if (IS_ERR(ctx->i2c.tx_p0_client))
+               return PTR_ERR(ctx->i2c.tx_p0_client);
 
        ctx->i2c.tx_p1_client = i2c_new_dummy_device(client->adapter,
                                                     TX_P1_ADDR >> 1);
-       if (!ctx->i2c.tx_p1_client)
+       if (IS_ERR(ctx->i2c.tx_p1_client)) {
+               err = PTR_ERR(ctx->i2c.tx_p1_client);
                goto free_tx_p0;
+       }
 
        ctx->i2c.tx_p2_client = i2c_new_dummy_device(client->adapter,
                                                     TX_P2_ADDR >> 1);
-       if (!ctx->i2c.tx_p2_client)
+       if (IS_ERR(ctx->i2c.tx_p2_client)) {
+               err = PTR_ERR(ctx->i2c.tx_p2_client);
                goto free_tx_p1;
+       }
 
        ctx->i2c.rx_p0_client = i2c_new_dummy_device(client->adapter,
                                                     RX_P0_ADDR >> 1);
-       if (!ctx->i2c.rx_p0_client)
+       if (IS_ERR(ctx->i2c.rx_p0_client)) {
+               err = PTR_ERR(ctx->i2c.rx_p0_client);
                goto free_tx_p2;
+       }
 
        ctx->i2c.rx_p1_client = i2c_new_dummy_device(client->adapter,
                                                     RX_P1_ADDR >> 1);
-       if (!ctx->i2c.rx_p1_client)
+       if (IS_ERR(ctx->i2c.rx_p1_client)) {
+               err = PTR_ERR(ctx->i2c.rx_p1_client);
                goto free_rx_p0;
+       }
 
        ctx->i2c.rx_p2_client = i2c_new_dummy_device(client->adapter,
                                                     RX_P2_ADDR >> 1);
-       if (!ctx->i2c.rx_p2_client)
+       if (IS_ERR(ctx->i2c.rx_p2_client)) {
+               err = PTR_ERR(ctx->i2c.rx_p2_client);
                goto free_rx_p1;
+       }
 
        ctx->i2c.tcpc_client = i2c_new_dummy_device(client->adapter,
                                                    TCPC_INTERFACE_ADDR >> 1);
-       if (!ctx->i2c.tcpc_client)
+       if (IS_ERR(ctx->i2c.tcpc_client)) {
+               err = PTR_ERR(ctx->i2c.tcpc_client);
                goto free_rx_p2;
+       }
 
        return 0;
 
@@ -2012,7 +2395,7 @@ free_tx_p1:
 free_tx_p0:
        i2c_unregister_device(ctx->i2c.tx_p0_client);
 
-       return -ENOMEM;
+       return err;
 }
 
 static void anx7625_unregister_i2c_dummy_clients(struct anx7625_data *ctx)
@@ -2134,6 +2517,15 @@ static int anx7625_i2c_probe(struct i2c_client *client,
        anx7625_init_gpio(platform);
 
        mutex_init(&platform->lock);
+       mutex_init(&platform->hdcp_wq_lock);
+
+       INIT_DELAYED_WORK(&platform->hdcp_work, hdcp_check_work_func);
+       platform->hdcp_workqueue = create_workqueue("hdcp workqueue");
+       if (!platform->hdcp_workqueue) {
+               dev_err(dev, "fail to create work queue\n");
+               ret = -ENOMEM;
+               goto free_platform;
+       }
 
        platform->pdata.intp_irq = client->irq;
        if (platform->pdata.intp_irq) {
@@ -2143,7 +2535,7 @@ static int anx7625_i2c_probe(struct i2c_client *client,
                if (!platform->workqueue) {
                        DRM_DEV_ERROR(dev, "fail to create work queue\n");
                        ret = -ENOMEM;
-                       goto free_platform;
+                       goto free_hdcp_wq;
                }
 
                ret = devm_request_threaded_irq(dev, platform->pdata.intp_irq,
@@ -2213,6 +2605,10 @@ free_wq:
        if (platform->workqueue)
                destroy_workqueue(platform->workqueue);
 
+free_hdcp_wq:
+       if (platform->hdcp_workqueue)
+               destroy_workqueue(platform->hdcp_workqueue);
+
 free_platform:
        kfree(platform);
 
@@ -2228,6 +2624,12 @@ static int anx7625_i2c_remove(struct i2c_client *client)
        if (platform->pdata.intp_irq)
                destroy_workqueue(platform->workqueue);
 
+       if (platform->hdcp_workqueue) {
+               cancel_delayed_work(&platform->hdcp_work);
+               flush_workqueue(platform->workqueue);
+               destroy_workqueue(platform->workqueue);
+       }
+
        if (!platform->pdata.low_power_mode)
                pm_runtime_put_sync_suspend(&client->dev);
 
index 3d79b6f..56165f5 100644 (file)
 
 /***************************************************************/
 /* Register definition of device address 0x70 */
-#define  I2C_ADDR_70_DPTX              0x70
-
-#define SP_TX_LINK_BW_SET_REG 0xA0
-#define SP_TX_LANE_COUNT_SET_REG 0xA1
+#define TX_HDCP_CTRL0                  0x01
+#define STORE_AN                       BIT(7)
+#define RX_REPEATER                    BIT(6)
+#define RE_AUTHEN                      BIT(5)
+#define SW_AUTH_OK                     BIT(4)
+#define HARD_AUTH_EN                   BIT(3)
+#define ENC_EN                         BIT(2)
+#define BKSV_SRM_PASS                  BIT(1)
+#define KSVLIST_VLD                    BIT(0)
+
+#define SP_TX_WAIT_R0_TIME             0x40
+#define SP_TX_WAIT_KSVR_TIME           0x42
+#define SP_TX_SYS_CTRL1_REG            0x80
+#define HDCP2TX_FW_EN                  BIT(4)
+
+#define SP_TX_LINK_BW_SET_REG          0xA0
+#define SP_TX_LANE_COUNT_SET_REG       0xA1
 
 #define M_VID_0 0xC0
 #define M_VID_1 0xC1
 #define N_VID_1 0xC4
 #define N_VID_2 0xC5
 
+#define KEY_START_ADDR                 0x9000
+#define KEY_RESERVED                   416
+
+#define HDCP14KEY_START_ADDR           (KEY_START_ADDR + KEY_RESERVED)
+#define HDCP14KEY_SIZE                 624
+
 /***************************************************************/
 /* Register definition of device address 0x72 */
 #define AUX_RST        0x04
 
 #define  I2C_ADDR_7E_FLASH_CONTROLLER  0x7E
 
+#define R_BOOT_RETRY           0x00
+#define R_RAM_ADDR_H           0x01
+#define R_RAM_ADDR_L           0x02
+#define R_RAM_LEN_H            0x03
+#define R_RAM_LEN_L            0x04
 #define FLASH_LOAD_STA          0x05
 #define FLASH_LOAD_STA_CHK     BIT(7)
 
+#define R_RAM_CTRL              0x05
+/* bit positions */
+#define FLASH_DONE              BIT(7)
+#define BOOT_LOAD_DONE          BIT(6)
+#define CRC_OK                  BIT(5)
+#define LOAD_DONE               BIT(4)
+#define O_RW_DONE               BIT(3)
+#define FUSE_BUSY               BIT(2)
+#define DECRYPT_EN              BIT(1)
+#define LOAD_START              BIT(0)
+
+#define FLASH_ADDR_HIGH         0x0F
+#define FLASH_ADDR_LOW          0x10
+#define FLASH_LEN_HIGH          0x31
+#define FLASH_LEN_LOW           0x32
+#define R_FLASH_RW_CTRL         0x33
+/* bit positions */
+#define READ_DELAY_SELECT       BIT(7)
+#define GENERAL_INSTRUCTION_EN  BIT(6)
+#define FLASH_ERASE_EN          BIT(5)
+#define RDID_READ_EN            BIT(4)
+#define REMS_READ_EN            BIT(3)
+#define WRITE_STATUS_EN         BIT(2)
+#define FLASH_READ              BIT(1)
+#define FLASH_WRITE             BIT(0)
+
+#define FLASH_BUF_BASE_ADDR     0x60
+#define FLASH_BUF_LEN           0x20
+
 #define  XTAL_FRQ_SEL    0x3F
 /* bit field positions */
 #define  XTAL_FRQ_SEL_POS    5
 #define AP_AUX_CTRL_ADDRONLY 0x20
 
 #define AP_AUX_BUFF_START 0x15
-#define PIXEL_CLOCK_L 0x25
-#define PIXEL_CLOCK_H 0x26
+#define PIXEL_CLOCK_L  0x25
+#define PIXEL_CLOCK_H  0x26
+
+#define AP_AUX_COMMAND 0x27  /* com+len */
+#define LENGTH_SHIFT   4
+#define DPCD_READ      0x09
+#define DPCD_WRITE     0x08
+#define DPCD_CMD(len, cmd)     ((((len) - 1) << LENGTH_SHIFT) | (cmd))
 
-#define AP_AUX_COMMAND 0x27  /* com+len */
 /* Bit 0&1: 3D video structure */
 /* 0x01: frame packing,  0x02:Line alternative, 0x03:Side-by-side(full) */
 #define AP_AV_STATUS 0x28
@@ -392,21 +450,29 @@ struct anx7625_data {
        struct platform_device *audio_pdev;
        int hpd_status;
        int hpd_high_cnt;
+       int dp_en;
+       int hdcp_cp;
        /* Lock for work queue */
        struct mutex lock;
        struct i2c_client *client;
        struct anx7625_i2c_client i2c;
        struct i2c_client *last_client;
+       struct timer_list hdcp_timer;
        struct s_edid_data slimport_edid_p;
        struct device *codec_dev;
        hdmi_codec_plugged_cb plugged_cb;
        struct work_struct work;
        struct workqueue_struct *workqueue;
+       struct delayed_work hdcp_work;
+       struct workqueue_struct *hdcp_workqueue;
+       /* Lock for hdcp work queue */
+       struct mutex hdcp_wq_lock;
        char edid_block;
        struct display_timing dt;
        u8 display_timing_valid;
        struct drm_bridge bridge;
        u8 bridge_attached;
+       struct drm_connector *connector;
        struct mipi_dsi_device *dsi;
 };
 
index ef8c230..de697ba 100644 (file)
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config DRM_CDNS_MHDP8546
        tristate "Cadence DPI/DP bridge"
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select DRM_PANEL_BRIDGE
        depends on OF
index 5530fbf..ac18e15 100644 (file)
@@ -41,7 +41,7 @@
 #include <drm/drm_bridge.h>
 #include <drm/drm_connector.h>
 #include <drm/drm_crtc_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_hdcp.h>
 #include <drm/drm_modeset_helper_vtables.h>
 #include <drm/drm_print.h>
@@ -1553,13 +1553,13 @@ static u32 cdns_mhdp_get_bpp(struct cdns_mhdp_display_fmt *fmt)
 
        switch (fmt->color_format) {
        case DRM_COLOR_FORMAT_RGB444:
-       case DRM_COLOR_FORMAT_YCRCB444:
+       case DRM_COLOR_FORMAT_YCBCR444:
                bpp = fmt->bpc * 3;
                break;
-       case DRM_COLOR_FORMAT_YCRCB422:
+       case DRM_COLOR_FORMAT_YCBCR422:
                bpp = fmt->bpc * 2;
                break;
-       case DRM_COLOR_FORMAT_YCRCB420:
+       case DRM_COLOR_FORMAT_YCBCR420:
                bpp = fmt->bpc * 3 / 2;
                break;
        default:
@@ -1767,8 +1767,8 @@ static void cdns_mhdp_configure_video(struct cdns_mhdp_device *mhdp,
         * 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)
+       if ((pxlfmt == DRM_COLOR_FORMAT_YCBCR444 ||
+            pxlfmt == DRM_COLOR_FORMAT_YCBCR422) && mode->crtc_vdisplay >= 720)
                misc0 = DP_YCBCR_COEFFICIENTS_ITU709;
 
        bpp = cdns_mhdp_get_bpp(&mhdp->display_fmt);
@@ -1778,15 +1778,15 @@ static void cdns_mhdp_configure_video(struct cdns_mhdp_device *mhdp,
                pxl_repr = CDNS_DP_FRAMER_RGB << CDNS_DP_FRAMER_PXL_FORMAT;
                misc0 |= DP_COLOR_FORMAT_RGB;
                break;
-       case DRM_COLOR_FORMAT_YCRCB444:
+       case DRM_COLOR_FORMAT_YCBCR444:
                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:
+       case DRM_COLOR_FORMAT_YCBCR422:
                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:
+       case DRM_COLOR_FORMAT_YCBCR420:
                pxl_repr = CDNS_DP_FRAMER_YCBCR420 << CDNS_DP_FRAMER_PXL_FORMAT;
                break;
        default:
@@ -1882,7 +1882,7 @@ static void cdns_mhdp_configure_video(struct cdns_mhdp_device *mhdp,
        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)
+       if (pxlfmt == DRM_COLOR_FORMAT_YCBCR420)
                misc1 = CDNS_DP_TEST_VSC_SDP;
 
        cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_MISC(stream_id),
index c74439d..fc77f98 100644 (file)
@@ -17,7 +17,7 @@
 
 #include <drm/drm_bridge.h>
 #include <drm/drm_connector.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 struct clk;
 struct device;
index a6151db..e8f36dc 100644 (file)
@@ -4,6 +4,7 @@
  * Author: Jagan Teki <jagan@amarulasolutions.com>
  */
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_of.h>
 #include <drm/drm_print.h>
 #include <drm/drm_mipi_dsi.h>
@@ -30,6 +31,7 @@
 struct chipone {
        struct device *dev;
        struct drm_bridge bridge;
+       struct drm_display_mode mode;
        struct drm_bridge *panel_bridge;
        struct gpio_desc *enable_gpio;
        struct regulator *vdd1;
@@ -42,11 +44,6 @@ static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge)
        return container_of(bridge, struct chipone, bridge);
 }
 
-static struct drm_display_mode *bridge_to_mode(struct drm_bridge *bridge)
-{
-       return &bridge->encoder->crtc->state->adjusted_mode;
-}
-
 static inline int chipone_dsi_write(struct chipone *icn,  const void *seq,
                                    size_t len)
 {
@@ -61,10 +58,11 @@ static inline int chipone_dsi_write(struct chipone *icn,  const void *seq,
                chipone_dsi_write(icn, d, ARRAY_SIZE(d));       \
        }
 
-static void chipone_enable(struct drm_bridge *bridge)
+static void chipone_atomic_enable(struct drm_bridge *bridge,
+                                 struct drm_bridge_state *old_bridge_state)
 {
        struct chipone *icn = bridge_to_chipone(bridge);
-       struct drm_display_mode *mode = bridge_to_mode(bridge);
+       struct drm_display_mode *mode = &icn->mode;
 
        ICN6211_DSI(icn, 0x7a, 0xc1);
 
@@ -114,7 +112,8 @@ static void chipone_enable(struct drm_bridge *bridge)
        usleep_range(10000, 11000);
 }
 
-static void chipone_pre_enable(struct drm_bridge *bridge)
+static void chipone_atomic_pre_enable(struct drm_bridge *bridge,
+                                     struct drm_bridge_state *old_bridge_state)
 {
        struct chipone *icn = bridge_to_chipone(bridge);
        int ret;
@@ -145,7 +144,8 @@ static void chipone_pre_enable(struct drm_bridge *bridge)
        usleep_range(10000, 11000);
 }
 
-static void chipone_post_disable(struct drm_bridge *bridge)
+static void chipone_atomic_post_disable(struct drm_bridge *bridge,
+                                       struct drm_bridge_state *old_bridge_state)
 {
        struct chipone *icn = bridge_to_chipone(bridge);
 
@@ -161,6 +161,15 @@ static void chipone_post_disable(struct drm_bridge *bridge)
        gpiod_set_value(icn->enable_gpio, 0);
 }
 
+static void chipone_mode_set(struct drm_bridge *bridge,
+                            const struct drm_display_mode *mode,
+                            const struct drm_display_mode *adjusted_mode)
+{
+       struct chipone *icn = bridge_to_chipone(bridge);
+
+       drm_mode_copy(&icn->mode, adjusted_mode);
+}
+
 static int chipone_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags)
 {
        struct chipone *icn = bridge_to_chipone(bridge);
@@ -169,10 +178,14 @@ static int chipone_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flag
 }
 
 static const struct drm_bridge_funcs chipone_bridge_funcs = {
-       .attach = chipone_attach,
-       .post_disable = chipone_post_disable,
-       .pre_enable = chipone_pre_enable,
-       .enable = chipone_enable,
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state   = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset           = drm_atomic_helper_bridge_reset,
+       .atomic_pre_enable      = chipone_atomic_pre_enable,
+       .atomic_enable          = chipone_atomic_enable,
+       .atomic_post_disable    = chipone_atomic_post_disable,
+       .mode_set               = chipone_mode_set,
+       .attach                 = chipone_attach,
 };
 
 static int chipone_parse_dt(struct chipone *icn)
index 06b59b4..69288cf 100644 (file)
@@ -936,9 +936,6 @@ static int it66121_probe(struct i2c_client *client,
                return -EPROBE_DEFER;
        }
 
-       if (!ctx->next_bridge)
-               return -EPROBE_DEFER;
-
        i2c_set_clientdata(client, ctx);
        mutex_init(&ctx->lock);
 
index dafb1b4..feb128a 100644 (file)
@@ -1090,7 +1090,7 @@ static int lt9611_probe(struct i2c_client *client,
        if (!lt9611)
                return -ENOMEM;
 
-       lt9611->dev = &client->dev;
+       lt9611->dev = dev;
        lt9611->client = client;
        lt9611->sleep = false;
 
@@ -1100,7 +1100,7 @@ static int lt9611_probe(struct i2c_client *client,
                return PTR_ERR(lt9611->regmap);
        }
 
-       ret = lt9611_parse_dt(&client->dev, lt9611);
+       ret = lt9611_parse_dt(dev, lt9611);
        if (ret) {
                dev_err(dev, "failed to parse device tree\n");
                return ret;
index 33f9716..3d62e6b 100644 (file)
@@ -860,7 +860,7 @@ static int lt9611uxc_probe(struct i2c_client *client,
        if (!lt9611uxc)
                return -ENOMEM;
 
-       lt9611uxc->dev = &client->dev;
+       lt9611uxc->dev = dev;
        lt9611uxc->client = client;
        mutex_init(&lt9611uxc->ocm_lock);
 
@@ -870,7 +870,7 @@ static int lt9611uxc_probe(struct i2c_client *client,
                return PTR_ERR(lt9611uxc->regmap);
        }
 
-       ret = lt9611uxc_parse_dt(&client->dev, lt9611uxc);
+       ret = lt9611uxc_parse_dt(dev, lt9611uxc);
        if (ret) {
                dev_err(dev, "failed to parse device tree\n");
                return ret;
index a7389a0..9282e61 100644 (file)
@@ -65,7 +65,6 @@ struct nwl_dsi_transfer {
 struct nwl_dsi {
        struct drm_bridge bridge;
        struct mipi_dsi_host dsi_host;
-       struct drm_bridge *panel_bridge;
        struct device *dev;
        struct phy *phy;
        union phy_configure_opts phy_cfg;
@@ -924,13 +923,11 @@ static int nwl_dsi_bridge_attach(struct drm_bridge *bridge,
                if (IS_ERR(panel_bridge))
                        return PTR_ERR(panel_bridge);
        }
-       dsi->panel_bridge = panel_bridge;
 
-       if (!dsi->panel_bridge)
+       if (!panel_bridge)
                return -EPROBE_DEFER;
 
-       return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge,
-                                flags);
+       return drm_bridge_attach(bridge->encoder, panel_bridge, bridge, flags);
 }
 
 static void nwl_dsi_bridge_detach(struct drm_bridge *bridge)
@@ -1206,6 +1203,7 @@ static int nwl_dsi_probe(struct platform_device *pdev)
 
        ret = nwl_dsi_select_input(dsi);
        if (ret < 0) {
+               pm_runtime_disable(dev);
                mipi_dsi_host_unregister(&dsi->dsi_host);
                return ret;
        }
index 818704b..3f17337 100644 (file)
@@ -14,8 +14,8 @@
 #include <linux/regulator/consumer.h>
 
 #include <drm/drm_bridge.h>
-#include <drm/drm_dp_aux_bus.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_aux_bus.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
@@ -102,6 +102,7 @@ struct ps8640 {
        struct regulator_bulk_data supplies[2];
        struct gpio_desc *gpio_reset;
        struct gpio_desc *gpio_powerdown;
+       struct device_link *link;
        bool pre_enabled;
 };
 
@@ -456,14 +457,36 @@ static int ps8640_bridge_attach(struct drm_bridge *bridge,
                return ret;
        }
 
+       ps_bridge->link = device_link_add(bridge->dev->dev, dev, DL_FLAG_STATELESS);
+       if (!ps_bridge->link) {
+               dev_err(dev, "failed to create device link");
+               ret = -EINVAL;
+               goto err_devlink;
+       }
+
        /* Attach the panel-bridge to the dsi bridge */
-       return drm_bridge_attach(bridge->encoder, ps_bridge->panel_bridge,
-                                &ps_bridge->bridge, flags);
+       ret = drm_bridge_attach(bridge->encoder, ps_bridge->panel_bridge,
+                               &ps_bridge->bridge, flags);
+       if (ret)
+               goto err_bridge_attach;
+
+       return 0;
+
+err_bridge_attach:
+       device_link_del(ps_bridge->link);
+err_devlink:
+       drm_dp_aux_unregister(&ps_bridge->aux);
+
+       return ret;
 }
 
 static void ps8640_bridge_detach(struct drm_bridge *bridge)
 {
-       drm_dp_aux_unregister(&bridge_to_ps8640(bridge)->aux);
+       struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+
+       drm_dp_aux_unregister(&ps_bridge->aux);
+       if (ps_bridge->link)
+               device_link_del(ps_bridge->link);
 }
 
 static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge,
index 89558e5..65549fb 100644 (file)
@@ -166,10 +166,12 @@ struct sii902x {
        struct i2c_client *i2c;
        struct regmap *regmap;
        struct drm_bridge bridge;
+       struct drm_bridge *next_bridge;
        struct drm_connector connector;
        struct gpio_desc *reset_gpio;
        struct i2c_mux_core *i2cmux;
        struct regulator_bulk_data supplies[2];
+       bool sink_is_hdmi;
        /*
         * Mutex protects audio and video functions from interfering
         * each other, by keeping their i2c command sequences atomic.
@@ -245,10 +247,8 @@ static void sii902x_reset(struct sii902x *sii902x)
        gpiod_set_value(sii902x->reset_gpio, 0);
 }
 
-static enum drm_connector_status
-sii902x_connector_detect(struct drm_connector *connector, bool force)
+static enum drm_connector_status sii902x_detect(struct sii902x *sii902x)
 {
-       struct sii902x *sii902x = connector_to_sii902x(connector);
        unsigned int status;
 
        mutex_lock(&sii902x->mutex);
@@ -261,6 +261,14 @@ sii902x_connector_detect(struct drm_connector *connector, bool force)
               connector_status_connected : connector_status_disconnected;
 }
 
+static enum drm_connector_status
+sii902x_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct sii902x *sii902x = connector_to_sii902x(connector);
+
+       return sii902x_detect(sii902x);
+}
+
 static const struct drm_connector_funcs sii902x_connector_funcs = {
        .detect = sii902x_connector_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
@@ -270,42 +278,40 @@ static const struct drm_connector_funcs sii902x_connector_funcs = {
        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
-static int sii902x_get_modes(struct drm_connector *connector)
+static struct edid *sii902x_get_edid(struct sii902x *sii902x,
+                                    struct drm_connector *connector)
 {
-       struct sii902x *sii902x = connector_to_sii902x(connector);
-       u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
-       u8 output_mode = SII902X_SYS_CTRL_OUTPUT_DVI;
        struct edid *edid;
-       int num = 0, ret;
 
        mutex_lock(&sii902x->mutex);
 
        edid = drm_get_edid(connector, sii902x->i2cmux->adapter[0]);
-       drm_connector_update_edid_property(connector, edid);
        if (edid) {
                if (drm_detect_hdmi_monitor(edid))
-                       output_mode = SII902X_SYS_CTRL_OUTPUT_HDMI;
-
-               num = drm_add_edid_modes(connector, edid);
-               kfree(edid);
+                       sii902x->sink_is_hdmi = true;
+               else
+                       sii902x->sink_is_hdmi = false;
        }
 
-       ret = drm_display_info_set_bus_formats(&connector->display_info,
-                                              &bus_format, 1);
-       if (ret)
-               goto error_out;
+       mutex_unlock(&sii902x->mutex);
 
-       ret = regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
-                                SII902X_SYS_CTRL_OUTPUT_MODE, output_mode);
-       if (ret)
-               goto error_out;
+       return edid;
+}
 
-       ret = num;
+static int sii902x_get_modes(struct drm_connector *connector)
+{
+       struct sii902x *sii902x = connector_to_sii902x(connector);
+       struct edid *edid;
+       int num = 0;
 
-error_out:
-       mutex_unlock(&sii902x->mutex);
+       edid = sii902x_get_edid(sii902x, connector);
+       drm_connector_update_edid_property(connector, edid);
+       if (edid) {
+               num = drm_add_edid_modes(connector, edid);
+               kfree(edid);
+       }
 
-       return ret;
+       return num;
 }
 
 static enum drm_mode_status sii902x_mode_valid(struct drm_connector *connector,
@@ -354,12 +360,16 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
                                    const struct drm_display_mode *adj)
 {
        struct sii902x *sii902x = bridge_to_sii902x(bridge);
+       u8 output_mode = SII902X_SYS_CTRL_OUTPUT_DVI;
        struct regmap *regmap = sii902x->regmap;
        u8 buf[HDMI_INFOFRAME_SIZE(AVI)];
        struct hdmi_avi_infoframe frame;
        u16 pixel_clock_10kHz = adj->clock / 10;
        int ret;
 
+       if (sii902x->sink_is_hdmi)
+               output_mode = SII902X_SYS_CTRL_OUTPUT_HDMI;
+
        buf[0] = pixel_clock_10kHz & 0xff;
        buf[1] = pixel_clock_10kHz >> 8;
        buf[2] = drm_mode_vrefresh(adj);
@@ -375,6 +385,11 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
 
        mutex_lock(&sii902x->mutex);
 
+       ret = regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
+                                SII902X_SYS_CTRL_OUTPUT_MODE, output_mode);
+       if (ret)
+               goto out;
+
        ret = regmap_bulk_write(regmap, SII902X_TPI_VIDEO_DATA, buf, 10);
        if (ret)
                goto out;
@@ -405,13 +420,13 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge,
                                 enum drm_bridge_attach_flags flags)
 {
        struct sii902x *sii902x = bridge_to_sii902x(bridge);
+       u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
        struct drm_device *drm = bridge->dev;
        int ret;
 
-       if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
-               DRM_ERROR("Fix bridge driver to make connector optional!");
-               return -EINVAL;
-       }
+       if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+               return drm_bridge_attach(bridge->encoder, sii902x->next_bridge,
+                                        bridge, flags);
 
        drm_connector_helper_add(&sii902x->connector,
                                 &sii902x_connector_helper_funcs);
@@ -433,16 +448,38 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge,
        else
                sii902x->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
 
+       ret = drm_display_info_set_bus_formats(&sii902x->connector.display_info,
+                                              &bus_format, 1);
+       if (ret)
+               return ret;
+
        drm_connector_attach_encoder(&sii902x->connector, bridge->encoder);
 
        return 0;
 }
 
+static enum drm_connector_status sii902x_bridge_detect(struct drm_bridge *bridge)
+{
+       struct sii902x *sii902x = bridge_to_sii902x(bridge);
+
+       return sii902x_detect(sii902x);
+}
+
+static struct edid *sii902x_bridge_get_edid(struct drm_bridge *bridge,
+                                           struct drm_connector *connector)
+{
+       struct sii902x *sii902x = bridge_to_sii902x(bridge);
+
+       return sii902x_get_edid(sii902x, connector);
+}
+
 static const struct drm_bridge_funcs sii902x_bridge_funcs = {
        .attach = sii902x_bridge_attach,
        .mode_set = sii902x_bridge_mode_set,
        .disable = sii902x_bridge_disable,
        .enable = sii902x_bridge_enable,
+       .detect = sii902x_bridge_detect,
+       .get_edid = sii902x_bridge_get_edid,
 };
 
 static int sii902x_mute(struct sii902x *sii902x, bool mute)
@@ -829,8 +866,12 @@ static irqreturn_t sii902x_interrupt(int irq, void *data)
 
        mutex_unlock(&sii902x->mutex);
 
-       if ((status & SII902X_HOTPLUG_EVENT) && sii902x->bridge.dev)
+       if ((status & SII902X_HOTPLUG_EVENT) && sii902x->bridge.dev) {
                drm_helper_hpd_irq_event(sii902x->bridge.dev);
+               drm_bridge_hpd_notify(&sii902x->bridge, (status & SII902X_PLUGGED_STATUS)
+                                                               ? connector_status_connected
+                                                               : connector_status_disconnected);
+       }
 
        return IRQ_HANDLED;
 }
@@ -1001,6 +1042,11 @@ static int sii902x_init(struct sii902x *sii902x)
        sii902x->bridge.funcs = &sii902x_bridge_funcs;
        sii902x->bridge.of_node = dev->of_node;
        sii902x->bridge.timings = &default_sii902x_timings;
+       sii902x->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
+
+       if (sii902x->i2c->irq > 0)
+               sii902x->bridge.ops |= DRM_BRIDGE_OP_HPD;
+
        drm_bridge_add(&sii902x->bridge);
 
        sii902x_audio_codec_init(sii902x, dev);
@@ -1022,6 +1068,7 @@ static int sii902x_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
        struct device *dev = &client->dev;
+       struct device_node *endpoint;
        struct sii902x *sii902x;
        int ret;
 
@@ -1049,6 +1096,28 @@ static int sii902x_probe(struct i2c_client *client,
                return PTR_ERR(sii902x->reset_gpio);
        }
 
+       endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
+       if (endpoint) {
+               struct device_node *remote = of_graph_get_remote_port_parent(endpoint);
+
+               of_node_put(endpoint);
+               if (!remote) {
+                       dev_err(dev, "Endpoint in port@1 unconnected\n");
+                       return -ENODEV;
+               }
+
+               if (!of_device_is_available(remote)) {
+                       dev_err(dev, "port@1 remote device is disabled\n");
+                       of_node_put(remote);
+                       return -ENODEV;
+               }
+
+               sii902x->next_bridge = of_drm_find_bridge(remote);
+               of_node_put(remote);
+               if (!sii902x->next_bridge)
+                       return -EPROBE_DEFER;
+       }
+
        mutex_init(&sii902x->mutex);
 
        sii902x->supplies[0].supply = "iovcc";
index 843265d..ec7745c 100644 (file)
@@ -2120,7 +2120,7 @@ static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
        if (ret) {
                dev_err(ctx->dev, "Failed to register RC device\n");
                ctx->error = ret;
-               rc_free_device(ctx->rc_dev);
+               rc_free_device(rc_dev);
                return;
        }
        ctx->rc_dev = rc_dev;
index 54d8fda..b0d8110 100644 (file)
@@ -2540,7 +2540,7 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
        struct drm_display_mode *mode = &crtc_state->mode;
        u8 max_bpc = conn_state->max_requested_bpc;
        bool is_hdmi2_sink = info->hdmi.scdc.supported ||
-                            (info->color_formats & DRM_COLOR_FORMAT_YCRCB420);
+                            (info->color_formats & DRM_COLOR_FORMAT_YCBCR420);
        u32 *output_fmts;
        unsigned int i = 0;
 
@@ -2594,36 +2594,36 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
         */
 
        if (max_bpc >= 16 && info->bpc == 16) {
-               if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+               if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
                        output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
 
                output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
        }
 
        if (max_bpc >= 12 && info->bpc >= 12) {
-               if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+               if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
                        output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
 
-               if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+               if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
                        output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
 
                output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
        }
 
        if (max_bpc >= 10 && info->bpc >= 10) {
-               if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+               if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
                        output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
 
-               if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+               if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
                        output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
 
                output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
        }
 
-       if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+       if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
                output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
 
-       if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+       if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
                output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
 
        /* Default 8bit RGB fallback */
index e44e18a..11d20b8 100644 (file)
@@ -871,7 +871,8 @@ static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
        dsi_write(dsi, DSI_INT_MSK1, 0);
 }
 
-static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
+static void dw_mipi_dsi_bridge_post_atomic_disable(struct drm_bridge *bridge,
+                                                  struct drm_bridge_state *old_bridge_state)
 {
        struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
        const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
@@ -978,7 +979,8 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
                dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode);
 }
 
-static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
+static void dw_mipi_dsi_bridge_atomic_enable(struct drm_bridge *bridge,
+                                            struct drm_bridge_state *old_bridge_state)
 {
        struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
 
@@ -998,7 +1000,10 @@ dw_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge,
        enum drm_mode_status mode_status = MODE_OK;
 
        if (pdata->mode_valid)
-               mode_status = pdata->mode_valid(pdata->priv_data, mode);
+               mode_status = pdata->mode_valid(pdata->priv_data, mode,
+                                               dsi->mode_flags,
+                                               dw_mipi_dsi_get_lanes(dsi),
+                                               dsi->format);
 
        return mode_status;
 }
@@ -1032,11 +1037,14 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge,
 }
 
 static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
-       .mode_set     = dw_mipi_dsi_bridge_mode_set,
-       .enable       = dw_mipi_dsi_bridge_enable,
-       .post_disable = dw_mipi_dsi_bridge_post_disable,
-       .mode_valid   = dw_mipi_dsi_bridge_mode_valid,
-       .attach       = dw_mipi_dsi_bridge_attach,
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state   = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset           = drm_atomic_helper_bridge_reset,
+       .atomic_enable          = dw_mipi_dsi_bridge_atomic_enable,
+       .atomic_post_disable    = dw_mipi_dsi_bridge_post_atomic_disable,
+       .mode_set               = dw_mipi_dsi_bridge_mode_set,
+       .mode_valid             = dw_mipi_dsi_bridge_mode_valid,
+       .attach                 = dw_mipi_dsi_bridge_attach,
 };
 
 #ifdef CONFIG_DEBUG_FS
@@ -1199,6 +1207,7 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
        ret = mipi_dsi_host_register(&dsi->dsi_host);
        if (ret) {
                dev_err(dev, "Failed to register MIPI host: %d\n", ret);
+               pm_runtime_disable(dev);
                dw_mipi_dsi_debugfs_remove(dsi);
                return ERR_PTR(ret);
        }
index 23a6f90..c23e0ab 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
index 2c76331..695af3b 100644 (file)
@@ -22,7 +22,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_crtc_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
@@ -241,7 +241,7 @@ static inline u32 TC358775_LVCFG_PCLKDIV(uint32_t val)
 }
 
 #define TC358775_LVCFG_LVDLINK__MASK                         0x00000002
-#define TC358775_LVCFG_LVDLINK__SHIFT                        0
+#define TC358775_LVCFG_LVDLINK__SHIFT                        1
 static inline u32 TC358775_LVCFG_LVDLINK(uint32_t val)
 {
        return ((val) << TC358775_LVCFG_LVDLINK__SHIFT) &
index 945f08d..19daadd 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/of_device.h>
 #include <linux/of_graph.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
@@ -143,6 +144,7 @@ struct sn65dsi83 {
        struct mipi_dsi_device          *dsi;
        struct drm_bridge               *panel_bridge;
        struct gpio_desc                *enable_gpio;
+       struct regulator                *vcc;
        int                             dsi_lanes;
        bool                            lvds_dual_link;
        bool                            lvds_dual_link_even_odd_swap;
@@ -337,6 +339,12 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
        u16 val;
        int ret;
 
+       ret = regulator_enable(ctx->vcc);
+       if (ret) {
+               dev_err(ctx->dev, "Failed to enable vcc: %d\n", ret);
+               return;
+       }
+
        /* Deassert reset */
        gpiod_set_value(ctx->enable_gpio, 1);
        usleep_range(1000, 1100);
@@ -486,11 +494,16 @@ static void sn65dsi83_atomic_disable(struct drm_bridge *bridge,
                                     struct drm_bridge_state *old_bridge_state)
 {
        struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
+       int ret;
 
        /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */
        gpiod_set_value(ctx->enable_gpio, 0);
        usleep_range(10000, 11000);
 
+       ret = regulator_disable(ctx->vcc);
+       if (ret)
+               dev_err(ctx->dev, "Failed to disable vcc: %d\n", ret);
+
        regcache_mark_dirty(ctx->regmap);
 }
 
@@ -560,10 +573,14 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)
        ctx->host_node = of_graph_get_remote_port_parent(endpoint);
        of_node_put(endpoint);
 
-       if (ctx->dsi_lanes < 0 || ctx->dsi_lanes > 4)
-               return -EINVAL;
-       if (!ctx->host_node)
-               return -ENODEV;
+       if (ctx->dsi_lanes < 0 || ctx->dsi_lanes > 4) {
+               ret = -EINVAL;
+               goto err_put_node;
+       }
+       if (!ctx->host_node) {
+               ret = -ENODEV;
+               goto err_put_node;
+       }
 
        ctx->lvds_dual_link = false;
        ctx->lvds_dual_link_even_odd_swap = false;
@@ -590,16 +607,27 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)
 
        ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, &panel_bridge);
        if (ret < 0)
-               return ret;
+               goto err_put_node;
        if (panel) {
                panel_bridge = devm_drm_panel_bridge_add(dev, panel);
-               if (IS_ERR(panel_bridge))
-                       return PTR_ERR(panel_bridge);
+               if (IS_ERR(panel_bridge)) {
+                       ret = PTR_ERR(panel_bridge);
+                       goto err_put_node;
+               }
        }
 
        ctx->panel_bridge = panel_bridge;
 
+       ctx->vcc = devm_regulator_get(dev, "vcc");
+       if (IS_ERR(ctx->vcc))
+               return dev_err_probe(dev, PTR_ERR(ctx->vcc),
+                                    "Failed to get supply 'vcc'\n");
+
        return 0;
+
+err_put_node:
+       of_node_put(ctx->host_node);
+       return ret;
 }
 
 static int sn65dsi83_host_attach(struct sn65dsi83 *ctx)
@@ -662,7 +690,8 @@ static int sn65dsi83_probe(struct i2c_client *client,
        }
 
        /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */
-       ctx->enable_gpio = devm_gpiod_get(ctx->dev, "enable", GPIOD_OUT_LOW);
+       ctx->enable_gpio = devm_gpiod_get_optional(ctx->dev, "enable",
+                                                  GPIOD_OUT_LOW);
        if (IS_ERR(ctx->enable_gpio))
                return PTR_ERR(ctx->enable_gpio);
 
@@ -673,8 +702,10 @@ static int sn65dsi83_probe(struct i2c_client *client,
                return ret;
 
        ctx->regmap = devm_regmap_init_i2c(client, &sn65dsi83_regmap_config);
-       if (IS_ERR(ctx->regmap))
-               return PTR_ERR(ctx->regmap);
+       if (IS_ERR(ctx->regmap)) {
+               ret = PTR_ERR(ctx->regmap);
+               goto err_put_node;
+       }
 
        dev_set_drvdata(dev, ctx);
        i2c_set_clientdata(client, ctx);
@@ -691,6 +722,8 @@ static int sn65dsi83_probe(struct i2c_client *client,
 
 err_remove_bridge:
        drm_bridge_remove(&ctx->bridge);
+err_put_node:
+       of_node_put(ctx->host_node);
        return ret;
 }
 
index dab8f76..ba136a1 100644 (file)
@@ -26,8 +26,8 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
-#include <drm/drm_dp_aux_bus.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_aux_bus.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
diff --git a/drivers/gpu/drm/dp/Makefile b/drivers/gpu/drm/dp/Makefile
new file mode 100644 (file)
index 0000000..75faffc
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: MIT
+
+obj-$(CONFIG_DRM_DP_AUX_BUS) += drm_dp_aux_bus.o
+
+drm_dp_helper-y := drm_dp.o drm_dp_dual_mode_helper.o drm_dp_helper_mod.o drm_dp_mst_topology.o
+drm_dp_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o
+drm_dp_helper-$(CONFIG_DRM_DP_CEC) += drm_dp_cec.o
+
+obj-$(CONFIG_DRM_DP_HELPER) += drm_dp_helper.o
diff --git a/drivers/gpu/drm/dp/drm_dp.c b/drivers/gpu/drm/dp/drm_dp.c
new file mode 100644 (file)
index 0000000..6d43325
--- /dev/null
@@ -0,0 +1,3744 @@
+/*
+ * Copyright © 2009 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+
+#include <drm/dp/drm_dp_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+#include <drm/dp/drm_dp_mst_helper.h>
+#include <drm/drm_panel.h>
+
+#include "drm_dp_helper_internal.h"
+
+struct dp_aux_backlight {
+       struct backlight_device *base;
+       struct drm_dp_aux *aux;
+       struct drm_edp_backlight_info info;
+       bool enabled;
+};
+
+/**
+ * DOC: dp helpers
+ *
+ * These functions contain some common logic and helpers at various abstraction
+ * levels to deal with Display Port sink devices and related things like DP aux
+ * channel transfers, EDID reading over DP aux channels, decoding certain DPCD
+ * blocks, ...
+ */
+
+/* Helpers for DP link training */
+static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
+{
+       return link_status[r - DP_LANE0_1_STATUS];
+}
+
+static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
+                            int lane)
+{
+       int i = DP_LANE0_1_STATUS + (lane >> 1);
+       int s = (lane & 1) * 4;
+       u8 l = dp_link_status(link_status, i);
+
+       return (l >> s) & 0xf;
+}
+
+bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
+                         int lane_count)
+{
+       u8 lane_align;
+       u8 lane_status;
+       int lane;
+
+       lane_align = dp_link_status(link_status,
+                                   DP_LANE_ALIGN_STATUS_UPDATED);
+       if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
+               return false;
+       for (lane = 0; lane < lane_count; lane++) {
+               lane_status = dp_get_lane_status(link_status, lane);
+               if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
+                       return false;
+       }
+       return true;
+}
+EXPORT_SYMBOL(drm_dp_channel_eq_ok);
+
+bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
+                             int lane_count)
+{
+       int lane;
+       u8 lane_status;
+
+       for (lane = 0; lane < lane_count; lane++) {
+               lane_status = dp_get_lane_status(link_status, lane);
+               if ((lane_status & DP_LANE_CR_DONE) == 0)
+                       return false;
+       }
+       return true;
+}
+EXPORT_SYMBOL(drm_dp_clock_recovery_ok);
+
+u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                    int lane)
+{
+       int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+       int s = ((lane & 1) ?
+                DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
+                DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
+       u8 l = dp_link_status(link_status, i);
+
+       return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+EXPORT_SYMBOL(drm_dp_get_adjust_request_voltage);
+
+u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                         int lane)
+{
+       int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+       int s = ((lane & 1) ?
+                DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
+                DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
+       u8 l = dp_link_status(link_status, i);
+
+       return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis);
+
+/* DP 2.0 128b/132b */
+u8 drm_dp_get_adjust_tx_ffe_preset(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                  int lane)
+{
+       int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+       int s = ((lane & 1) ?
+                DP_ADJUST_TX_FFE_PRESET_LANE1_SHIFT :
+                DP_ADJUST_TX_FFE_PRESET_LANE0_SHIFT);
+       u8 l = dp_link_status(link_status, i);
+
+       return (l >> s) & 0xf;
+}
+EXPORT_SYMBOL(drm_dp_get_adjust_tx_ffe_preset);
+
+u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                        unsigned int lane)
+{
+       unsigned int offset = DP_ADJUST_REQUEST_POST_CURSOR2;
+       u8 value = dp_link_status(link_status, offset);
+
+       return (value >> (lane << 1)) & 0x3;
+}
+EXPORT_SYMBOL(drm_dp_get_adjust_request_post_cursor);
+
+static int __8b10b_clock_recovery_delay_us(const struct drm_dp_aux *aux, u8 rd_interval)
+{
+       if (rd_interval > 4)
+               drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n",
+                           aux->name, rd_interval);
+
+       if (rd_interval == 0)
+               return 100;
+
+       return rd_interval * 4 * USEC_PER_MSEC;
+}
+
+static int __8b10b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval)
+{
+       if (rd_interval > 4)
+               drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n",
+                           aux->name, rd_interval);
+
+       if (rd_interval == 0)
+               return 400;
+
+       return rd_interval * 4 * USEC_PER_MSEC;
+}
+
+static int __128b132b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval)
+{
+       switch (rd_interval) {
+       default:
+               drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x\n",
+                           aux->name, rd_interval);
+               fallthrough;
+       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US:
+               return 400;
+       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS:
+               return 4000;
+       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS:
+               return 8000;
+       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS:
+               return 12000;
+       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS:
+               return 16000;
+       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS:
+               return 32000;
+       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS:
+               return 64000;
+       }
+}
+
+/*
+ * The link training delays are different for:
+ *
+ *  - Clock recovery vs. channel equalization
+ *  - DPRX vs. LTTPR
+ *  - 128b/132b vs. 8b/10b
+ *  - DPCD rev 1.3 vs. later
+ *
+ * Get the correct delay in us, reading DPCD if necessary.
+ */
+static int __read_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                       enum drm_dp_phy dp_phy, bool uhbr, bool cr)
+{
+       int (*parse)(const struct drm_dp_aux *aux, u8 rd_interval);
+       unsigned int offset;
+       u8 rd_interval, mask;
+
+       if (dp_phy == DP_PHY_DPRX) {
+               if (uhbr) {
+                       if (cr)
+                               return 100;
+
+                       offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL;
+                       mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK;
+                       parse = __128b132b_channel_eq_delay_us;
+               } else {
+                       if (cr && dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
+                               return 100;
+
+                       offset = DP_TRAINING_AUX_RD_INTERVAL;
+                       mask = DP_TRAINING_AUX_RD_MASK;
+                       if (cr)
+                               parse = __8b10b_clock_recovery_delay_us;
+                       else
+                               parse = __8b10b_channel_eq_delay_us;
+               }
+       } else {
+               if (uhbr) {
+                       offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy);
+                       mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK;
+                       parse = __128b132b_channel_eq_delay_us;
+               } else {
+                       if (cr)
+                               return 100;
+
+                       offset = DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy);
+                       mask = DP_TRAINING_AUX_RD_MASK;
+                       parse = __8b10b_channel_eq_delay_us;
+               }
+       }
+
+       if (offset < DP_RECEIVER_CAP_SIZE) {
+               rd_interval = dpcd[offset];
+       } else {
+               if (drm_dp_dpcd_readb(aux, offset, &rd_interval) != 1) {
+                       drm_dbg_kms(aux->drm_dev, "%s: failed rd interval read\n",
+                                   aux->name);
+                       /* arbitrary default delay */
+                       return 400;
+               }
+       }
+
+       return parse(aux, rd_interval & mask);
+}
+
+int drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                                    enum drm_dp_phy dp_phy, bool uhbr)
+{
+       return __read_delay(aux, dpcd, dp_phy, uhbr, true);
+}
+EXPORT_SYMBOL(drm_dp_read_clock_recovery_delay);
+
+int drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                                enum drm_dp_phy dp_phy, bool uhbr)
+{
+       return __read_delay(aux, dpcd, dp_phy, uhbr, false);
+}
+EXPORT_SYMBOL(drm_dp_read_channel_eq_delay);
+
+void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux,
+                                           const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       u8 rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
+               DP_TRAINING_AUX_RD_MASK;
+       int delay_us;
+
+       if (dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
+               delay_us = 100;
+       else
+               delay_us = __8b10b_clock_recovery_delay_us(aux, rd_interval);
+
+       usleep_range(delay_us, delay_us * 2);
+}
+EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);
+
+static void __drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
+                                                u8 rd_interval)
+{
+       int delay_us = __8b10b_channel_eq_delay_us(aux, rd_interval);
+
+       usleep_range(delay_us, delay_us * 2);
+}
+
+void drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
+                                       const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       __drm_dp_link_train_channel_eq_delay(aux,
+                                            dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
+                                            DP_TRAINING_AUX_RD_MASK);
+}
+EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);
+
+void drm_dp_lttpr_link_train_clock_recovery_delay(void)
+{
+       usleep_range(100, 200);
+}
+EXPORT_SYMBOL(drm_dp_lttpr_link_train_clock_recovery_delay);
+
+static u8 dp_lttpr_phy_cap(const u8 phy_cap[DP_LTTPR_PHY_CAP_SIZE], int r)
+{
+       return phy_cap[r - DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1];
+}
+
+void drm_dp_lttpr_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
+                                             const u8 phy_cap[DP_LTTPR_PHY_CAP_SIZE])
+{
+       u8 interval = dp_lttpr_phy_cap(phy_cap,
+                                      DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1) &
+                     DP_TRAINING_AUX_RD_MASK;
+
+       __drm_dp_link_train_channel_eq_delay(aux, interval);
+}
+EXPORT_SYMBOL(drm_dp_lttpr_link_train_channel_eq_delay);
+
+u8 drm_dp_link_rate_to_bw_code(int link_rate)
+{
+       switch (link_rate) {
+       case 1000000:
+               return DP_LINK_BW_10;
+       case 1350000:
+               return DP_LINK_BW_13_5;
+       case 2000000:
+               return DP_LINK_BW_20;
+       default:
+               /* Spec says link_bw = link_rate / 0.27Gbps */
+               return link_rate / 27000;
+       }
+}
+EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code);
+
+int drm_dp_bw_code_to_link_rate(u8 link_bw)
+{
+       switch (link_bw) {
+       case DP_LINK_BW_10:
+               return 1000000;
+       case DP_LINK_BW_13_5:
+               return 1350000;
+       case DP_LINK_BW_20:
+               return 2000000;
+       default:
+               /* Spec says link_rate = link_bw * 0.27Gbps */
+               return link_bw * 27000;
+       }
+}
+EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate);
+
+#define AUX_RETRY_INTERVAL 500 /* us */
+
+static inline void
+drm_dp_dump_access(const struct drm_dp_aux *aux,
+                  u8 request, uint offset, void *buffer, int ret)
+{
+       const char *arrow = request == DP_AUX_NATIVE_READ ? "->" : "<-";
+
+       if (ret > 0)
+               drm_dbg_dp(aux->drm_dev, "%s: 0x%05x AUX %s (ret=%3d) %*ph\n",
+                          aux->name, offset, arrow, ret, min(ret, 20), buffer);
+       else
+               drm_dbg_dp(aux->drm_dev, "%s: 0x%05x AUX %s (ret=%3d)\n",
+                          aux->name, offset, arrow, ret);
+}
+
+/**
+ * DOC: dp helpers
+ *
+ * The DisplayPort AUX channel is an abstraction to allow generic, driver-
+ * independent access to AUX functionality. Drivers can take advantage of
+ * this by filling in the fields of the drm_dp_aux structure.
+ *
+ * Transactions are described using a hardware-independent drm_dp_aux_msg
+ * structure, which is passed into a driver's .transfer() implementation.
+ * Both native and I2C-over-AUX transactions are supported.
+ */
+
+static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
+                             unsigned int offset, void *buffer, size_t size)
+{
+       struct drm_dp_aux_msg msg;
+       unsigned int retry, native_reply;
+       int err = 0, ret = 0;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.address = offset;
+       msg.request = request;
+       msg.buffer = buffer;
+       msg.size = size;
+
+       mutex_lock(&aux->hw_mutex);
+
+       /*
+        * The specification doesn't give any recommendation on how often to
+        * retry native transactions. We used to retry 7 times like for
+        * aux i2c transactions but real world devices this wasn't
+        * sufficient, bump to 32 which makes Dell 4k monitors happier.
+        */
+       for (retry = 0; retry < 32; retry++) {
+               if (ret != 0 && ret != -ETIMEDOUT) {
+                       usleep_range(AUX_RETRY_INTERVAL,
+                                    AUX_RETRY_INTERVAL + 100);
+               }
+
+               ret = aux->transfer(aux, &msg);
+               if (ret >= 0) {
+                       native_reply = msg.reply & DP_AUX_NATIVE_REPLY_MASK;
+                       if (native_reply == DP_AUX_NATIVE_REPLY_ACK) {
+                               if (ret == size)
+                                       goto unlock;
+
+                               ret = -EPROTO;
+                       } else
+                               ret = -EIO;
+               }
+
+               /*
+                * We want the error we return to be the error we received on
+                * the first transaction, since we may get a different error the
+                * next time we retry
+                */
+               if (!err)
+                       err = ret;
+       }
+
+       drm_dbg_kms(aux->drm_dev, "%s: Too many retries, giving up. First error: %d\n",
+                   aux->name, err);
+       ret = err;
+
+unlock:
+       mutex_unlock(&aux->hw_mutex);
+       return ret;
+}
+
+/**
+ * drm_dp_dpcd_read() - read a series of bytes from the DPCD
+ * @aux: DisplayPort AUX channel (SST or MST)
+ * @offset: address of the (first) register to read
+ * @buffer: buffer to store the register values
+ * @size: number of bytes in @buffer
+ *
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -EIO is returned if the request was NAKed by the sink or
+ * if the retry count was exceeded. If not all bytes were transferred, this
+ * function returns -EPROTO. Errors from the underlying AUX channel transfer
+ * function, with the exception of -EBUSY (which causes the transaction to
+ * be retried), are propagated to the caller.
+ */
+ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
+                        void *buffer, size_t size)
+{
+       int ret;
+
+       /*
+        * HP ZR24w corrupts the first DPCD access after entering power save
+        * mode. Eg. on a read, the entire buffer will be filled with the same
+        * byte. Do a throw away read to avoid corrupting anything we care
+        * about. Afterwards things will work correctly until the monitor
+        * gets woken up and subsequently re-enters power save mode.
+        *
+        * The user pressing any button on the monitor is enough to wake it
+        * up, so there is no particularly good place to do the workaround.
+        * We just have to do it before any DPCD access and hope that the
+        * monitor doesn't power down exactly after the throw away read.
+        */
+       if (!aux->is_remote) {
+               ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, DP_DPCD_REV,
+                                        buffer, 1);
+               if (ret != 1)
+                       goto out;
+       }
+
+       if (aux->is_remote)
+               ret = drm_dp_mst_dpcd_read(aux, offset, buffer, size);
+       else
+               ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset,
+                                        buffer, size);
+
+out:
+       drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, buffer, ret);
+       return ret;
+}
+EXPORT_SYMBOL(drm_dp_dpcd_read);
+
+/**
+ * drm_dp_dpcd_write() - write a series of bytes to the DPCD
+ * @aux: DisplayPort AUX channel (SST or MST)
+ * @offset: address of the (first) register to write
+ * @buffer: buffer containing the values to write
+ * @size: number of bytes in @buffer
+ *
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -EIO is returned if the request was NAKed by the sink or
+ * if the retry count was exceeded. If not all bytes were transferred, this
+ * function returns -EPROTO. Errors from the underlying AUX channel transfer
+ * function, with the exception of -EBUSY (which causes the transaction to
+ * be retried), are propagated to the caller.
+ */
+ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
+                         void *buffer, size_t size)
+{
+       int ret;
+
+       if (aux->is_remote)
+               ret = drm_dp_mst_dpcd_write(aux, offset, buffer, size);
+       else
+               ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset,
+                                        buffer, size);
+
+       drm_dp_dump_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, ret);
+       return ret;
+}
+EXPORT_SYMBOL(drm_dp_dpcd_write);
+
+/**
+ * drm_dp_dpcd_read_link_status() - read DPCD link status (bytes 0x202-0x207)
+ * @aux: DisplayPort AUX channel
+ * @status: buffer to store the link status in (must be at least 6 bytes)
+ *
+ * Returns the number of bytes transferred on success or a negative error
+ * code on failure.
+ */
+int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
+                                u8 status[DP_LINK_STATUS_SIZE])
+{
+       return drm_dp_dpcd_read(aux, DP_LANE0_1_STATUS, status,
+                               DP_LINK_STATUS_SIZE);
+}
+EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
+
+/**
+ * drm_dp_dpcd_read_phy_link_status - get the link status information for a DP PHY
+ * @aux: DisplayPort AUX channel
+ * @dp_phy: the DP PHY to get the link status for
+ * @link_status: buffer to return the status in
+ *
+ * Fetch the AUX DPCD registers for the DPRX or an LTTPR PHY link status. The
+ * layout of the returned @link_status matches the DPCD register layout of the
+ * DPRX PHY link status.
+ *
+ * Returns 0 if the information was read successfully or a negative error code
+ * on failure.
+ */
+int drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux,
+                                    enum drm_dp_phy dp_phy,
+                                    u8 link_status[DP_LINK_STATUS_SIZE])
+{
+       int ret;
+
+       if (dp_phy == DP_PHY_DPRX) {
+               ret = drm_dp_dpcd_read(aux,
+                                      DP_LANE0_1_STATUS,
+                                      link_status,
+                                      DP_LINK_STATUS_SIZE);
+
+               if (ret < 0)
+                       return ret;
+
+               WARN_ON(ret != DP_LINK_STATUS_SIZE);
+
+               return 0;
+       }
+
+       ret = drm_dp_dpcd_read(aux,
+                              DP_LANE0_1_STATUS_PHY_REPEATER(dp_phy),
+                              link_status,
+                              DP_LINK_STATUS_SIZE - 1);
+
+       if (ret < 0)
+               return ret;
+
+       WARN_ON(ret != DP_LINK_STATUS_SIZE - 1);
+
+       /* Convert the LTTPR to the sink PHY link status layout */
+       memmove(&link_status[DP_SINK_STATUS - DP_LANE0_1_STATUS + 1],
+               &link_status[DP_SINK_STATUS - DP_LANE0_1_STATUS],
+               DP_LINK_STATUS_SIZE - (DP_SINK_STATUS - DP_LANE0_1_STATUS) - 1);
+       link_status[DP_SINK_STATUS - DP_LANE0_1_STATUS] = 0;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_dpcd_read_phy_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
+ * @type: port type to be checked. Can be:
+ *       %DP_DS_PORT_TYPE_DP, %DP_DS_PORT_TYPE_VGA, %DP_DS_PORT_TYPE_DVI,
+ *       %DP_DS_PORT_TYPE_HDMI, %DP_DS_PORT_TYPE_NON_EDID,
+ *       %DP_DS_PORT_TYPE_DP_DUALMODE or %DP_DS_PORT_TYPE_WIRELESS.
+ *
+ * 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
+ * @real_edid_checksum: real edid checksum for the last block
+ *
+ * Returns:
+ * True on success
+ */
+bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux,
+                                   u8 real_edid_checksum)
+{
+       u8 link_edid_read = 0, auto_test_req = 0, test_resp = 0;
+
+       if (drm_dp_dpcd_read(aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
+                            &auto_test_req, 1) < 1) {
+               drm_err(aux->drm_dev, "%s: DPCD failed read at register 0x%x\n",
+                       aux->name, DP_DEVICE_SERVICE_IRQ_VECTOR);
+               return false;
+       }
+       auto_test_req &= DP_AUTOMATED_TEST_REQUEST;
+
+       if (drm_dp_dpcd_read(aux, DP_TEST_REQUEST, &link_edid_read, 1) < 1) {
+               drm_err(aux->drm_dev, "%s: DPCD failed read at register 0x%x\n",
+                       aux->name, DP_TEST_REQUEST);
+               return false;
+       }
+       link_edid_read &= DP_TEST_LINK_EDID_READ;
+
+       if (!auto_test_req || !link_edid_read) {
+               drm_dbg_kms(aux->drm_dev, "%s: Source DUT does not support TEST_EDID_READ\n",
+                           aux->name);
+               return false;
+       }
+
+       if (drm_dp_dpcd_write(aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
+                             &auto_test_req, 1) < 1) {
+               drm_err(aux->drm_dev, "%s: DPCD failed write at register 0x%x\n",
+                       aux->name, DP_DEVICE_SERVICE_IRQ_VECTOR);
+               return false;
+       }
+
+       /* send back checksum for the last edid extension block data */
+       if (drm_dp_dpcd_write(aux, DP_TEST_EDID_CHECKSUM,
+                             &real_edid_checksum, 1) < 1) {
+               drm_err(aux->drm_dev, "%s: DPCD failed write at register 0x%x\n",
+                       aux->name, DP_TEST_EDID_CHECKSUM);
+               return false;
+       }
+
+       test_resp |= DP_TEST_EDID_CHECKSUM_WRITE;
+       if (drm_dp_dpcd_write(aux, DP_TEST_RESPONSE, &test_resp, 1) < 1) {
+               drm_err(aux->drm_dev, "%s: DPCD failed write at register 0x%x\n",
+                       aux->name, DP_TEST_RESPONSE);
+               return false;
+       }
+
+       return true;
+}
+EXPORT_SYMBOL(drm_dp_send_real_edid_checksum);
+
+static u8 drm_dp_downstream_port_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       u8 port_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_PORT_COUNT_MASK;
+
+       if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE && port_count > 4)
+               port_count = 4;
+
+       return port_count;
+}
+
+static int drm_dp_read_extended_dpcd_caps(struct drm_dp_aux *aux,
+                                         u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       u8 dpcd_ext[DP_RECEIVER_CAP_SIZE];
+       int ret;
+
+       /*
+        * Prior to DP1.3 the bit represented by
+        * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved.
+        * If it is set DP_DPCD_REV at 0000h could be at a value less than
+        * the true capability of the panel. The only way to check is to
+        * then compare 0000h and 2200h.
+        */
+       if (!(dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
+             DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT))
+               return 0;
+
+       ret = drm_dp_dpcd_read(aux, DP_DP13_DPCD_REV, &dpcd_ext,
+                              sizeof(dpcd_ext));
+       if (ret < 0)
+               return ret;
+       if (ret != sizeof(dpcd_ext))
+               return -EIO;
+
+       if (dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) {
+               drm_dbg_kms(aux->drm_dev,
+                           "%s: Extended DPCD rev less than base DPCD rev (%d > %d)\n",
+                           aux->name, dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]);
+               return 0;
+       }
+
+       if (!memcmp(dpcd, dpcd_ext, sizeof(dpcd_ext)))
+               return 0;
+
+       drm_dbg_kms(aux->drm_dev, "%s: Base DPCD: %*ph\n", aux->name, DP_RECEIVER_CAP_SIZE, dpcd);
+
+       memcpy(dpcd, dpcd_ext, sizeof(dpcd_ext));
+
+       return 0;
+}
+
+/**
+ * drm_dp_read_dpcd_caps() - read DPCD caps and extended DPCD caps if
+ * available
+ * @aux: DisplayPort AUX channel
+ * @dpcd: Buffer to store the resulting DPCD in
+ *
+ * Attempts to read the base DPCD caps for @aux. Additionally, this function
+ * checks for and reads the extended DPRX caps (%DP_DP13_DPCD_REV) if
+ * present.
+ *
+ * Returns: %0 if the DPCD was read successfully, negative error code
+ * otherwise.
+ */
+int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux,
+                         u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       int ret;
+
+       ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE);
+       if (ret < 0)
+               return ret;
+       if (ret != DP_RECEIVER_CAP_SIZE || dpcd[DP_DPCD_REV] == 0)
+               return -EIO;
+
+       ret = drm_dp_read_extended_dpcd_caps(aux, dpcd);
+       if (ret < 0)
+               return ret;
+
+       drm_dbg_kms(aux->drm_dev, "%s: DPCD: %*ph\n", aux->name, DP_RECEIVER_CAP_SIZE, dpcd);
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_dp_read_dpcd_caps);
+
+/**
+ * drm_dp_read_downstream_info() - read DPCD downstream port info if available
+ * @aux: DisplayPort AUX channel
+ * @dpcd: A cached copy of the port's DPCD
+ * @downstream_ports: buffer to store the downstream port info in
+ *
+ * See also:
+ * drm_dp_downstream_max_clock()
+ * drm_dp_downstream_max_bpc()
+ *
+ * Returns: 0 if either the downstream port info was read successfully or
+ * there was no downstream info to read, or a negative error code otherwise.
+ */
+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 ret;
+       u8 len;
+
+       memset(downstream_ports, 0, DP_MAX_DOWNSTREAM_PORTS);
+
+       /* No downstream info to read */
+       if (!drm_dp_is_branch(dpcd) || dpcd[DP_DPCD_REV] == DP_DPCD_REV_10)
+               return 0;
+
+       /* Some branches advertise having 0 downstream ports, despite also advertising they have a
+        * downstream port present. The DP spec isn't clear on if this is allowed or not, but since
+        * some branches do it we need to handle it regardless.
+        */
+       len = drm_dp_downstream_port_count(dpcd);
+       if (!len)
+               return 0;
+
+       if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE)
+               len *= 4;
+
+       ret = drm_dp_dpcd_read(aux, DP_DOWNSTREAM_PORT_0, downstream_ports, len);
+       if (ret < 0)
+               return ret;
+       if (ret != len)
+               return -EIO;
+
+       drm_dbg_kms(aux->drm_dev, "%s: DPCD DFP: %*ph\n", aux->name, len, downstream_ports);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_read_downstream_info);
+
+/**
+ * drm_dp_downstream_max_dotclock() - extract downstream facing port max dot clock
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ *
+ * Returns: Downstream facing port max dot clock in kHz on success,
+ * or 0 if max clock not defined
+ */
+int drm_dp_downstream_max_dotclock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                                  const u8 port_cap[4])
+{
+       if (!drm_dp_is_branch(dpcd))
+               return 0;
+
+       if (dpcd[DP_DPCD_REV] < 0x11)
+               return 0;
+
+       switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) {
+       case DP_DS_PORT_TYPE_VGA:
+               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.
+                *
+                * Unfortunately 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_tmds_clock);
+
+/**
+ * drm_dp_downstream_min_tmds_clock() - extract downstream facing port min TMDS clock
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ * @edid: EDID
+ *
+ * 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 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_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:
+               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 (port_cap[2] & DP_DS_MAX_BPC_MASK) {
+               case DP_DS_8BPC:
+                       return 8;
+               case DP_DS_10BPC:
+                       return 10;
+               case DP_DS_12BPC:
+                       return 12;
+               case DP_DS_16BPC:
+                       return 16;
+               default:
+                       return 8;
+               }
+               break;
+       default:
+               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_rgb_to_ycbcr_conversion() - determine downstream facing port
+ *                                               RGB->YCbCr conversion capability
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: downstream facing port capabilities
+ * @color_spc: Colorspace for which conversion cap is sought
+ *
+ * Returns: whether the downstream facing port can convert RGB->YCbCr for a given
+ * colorspace.
+ */
+bool drm_dp_downstream_rgb_to_ycbcr_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                                              const u8 port_cap[4],
+                                              u8 color_spc)
+{
+       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] & color_spc;
+       default:
+               return false;
+       }
+}
+EXPORT_SYMBOL(drm_dp_downstream_rgb_to_ycbcr_conversion);
+
+/**
+ * drm_dp_downstream_mode() - return a mode for downstream facing port
+ * @dev: DRM device
+ * @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
+ *
+ * Returns branch device id on success or NULL on failure
+ */
+int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6])
+{
+       return drm_dp_dpcd_read(aux, DP_BRANCH_ID, id, 6);
+}
+EXPORT_SYMBOL(drm_dp_downstream_id);
+
+/**
+ * drm_dp_downstream_debug() - debug DP branch devices
+ * @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],
+                            const struct edid *edid,
+                            struct drm_dp_aux *aux)
+{
+       bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+                                DP_DETAILED_CAP_INFO_AVAILABLE;
+       int clk;
+       int bpc;
+       char id[7];
+       int len;
+       uint8_t rev[2];
+       int type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
+       bool branch_device = drm_dp_is_branch(dpcd);
+
+       seq_printf(m, "\tDP branch device present: %s\n",
+                  branch_device ? "yes" : "no");
+
+       if (!branch_device)
+               return;
+
+       switch (type) {
+       case DP_DS_PORT_TYPE_DP:
+               seq_puts(m, "\t\tType: DisplayPort\n");
+               break;
+       case DP_DS_PORT_TYPE_VGA:
+               seq_puts(m, "\t\tType: VGA\n");
+               break;
+       case DP_DS_PORT_TYPE_DVI:
+               seq_puts(m, "\t\tType: DVI\n");
+               break;
+       case DP_DS_PORT_TYPE_HDMI:
+               seq_puts(m, "\t\tType: HDMI\n");
+               break;
+       case DP_DS_PORT_TYPE_NON_EDID:
+               seq_puts(m, "\t\tType: others without EDID support\n");
+               break;
+       case DP_DS_PORT_TYPE_DP_DUALMODE:
+               seq_puts(m, "\t\tType: DP++\n");
+               break;
+       case DP_DS_PORT_TYPE_WIRELESS:
+               seq_puts(m, "\t\tType: Wireless\n");
+               break;
+       default:
+               seq_puts(m, "\t\tType: N/A\n");
+       }
+
+       memset(id, 0, sizeof(id));
+       drm_dp_downstream_id(aux, id);
+       seq_printf(m, "\t\tID: %s\n", id);
+
+       len = drm_dp_dpcd_read(aux, DP_BRANCH_HW_REV, &rev[0], 1);
+       if (len > 0)
+               seq_printf(m, "\t\tHW: %d.%d\n",
+                          (rev[0] & 0xf0) >> 4, rev[0] & 0xf);
+
+       len = drm_dp_dpcd_read(aux, DP_BRANCH_SW_REV, rev, 2);
+       if (len > 0)
+               seq_printf(m, "\t\tSW: %d.%d\n", rev[0], rev[1]);
+
+       if (detailed_cap_info) {
+               clk = drm_dp_downstream_max_dotclock(dpcd, port_cap);
+               if (clk > 0)
+                       seq_printf(m, "\t\tMax dot 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, edid);
+
+               if (bpc > 0)
+                       seq_printf(m, "\t\tMax bpc: %d\n", bpc);
+       }
+}
+EXPORT_SYMBOL(drm_dp_downstream_debug);
+
+/**
+ * drm_dp_subconnector_type() - get DP branch device type
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ */
+enum drm_mode_subconnector
+drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                        const u8 port_cap[4])
+{
+       int type;
+       if (!drm_dp_is_branch(dpcd))
+               return DRM_MODE_SUBCONNECTOR_Native;
+       /* DP 1.0 approach */
+       if (dpcd[DP_DPCD_REV] == DP_DPCD_REV_10) {
+               type = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+                      DP_DWN_STRM_PORT_TYPE_MASK;
+
+               switch (type) {
+               case DP_DWN_STRM_PORT_TYPE_TMDS:
+                       /* Can be HDMI or DVI-D, DVI-D is a safer option */
+                       return DRM_MODE_SUBCONNECTOR_DVID;
+               case DP_DWN_STRM_PORT_TYPE_ANALOG:
+                       /* Can be VGA or DVI-A, VGA is more popular */
+                       return DRM_MODE_SUBCONNECTOR_VGA;
+               case DP_DWN_STRM_PORT_TYPE_DP:
+                       return DRM_MODE_SUBCONNECTOR_DisplayPort;
+               case DP_DWN_STRM_PORT_TYPE_OTHER:
+               default:
+                       return DRM_MODE_SUBCONNECTOR_Unknown;
+               }
+       }
+       type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
+
+       switch (type) {
+       case DP_DS_PORT_TYPE_DP:
+       case DP_DS_PORT_TYPE_DP_DUALMODE:
+               return DRM_MODE_SUBCONNECTOR_DisplayPort;
+       case DP_DS_PORT_TYPE_VGA:
+               return DRM_MODE_SUBCONNECTOR_VGA;
+       case DP_DS_PORT_TYPE_DVI:
+               return DRM_MODE_SUBCONNECTOR_DVID;
+       case DP_DS_PORT_TYPE_HDMI:
+               return DRM_MODE_SUBCONNECTOR_HDMIA;
+       case DP_DS_PORT_TYPE_WIRELESS:
+               return DRM_MODE_SUBCONNECTOR_Wireless;
+       case DP_DS_PORT_TYPE_NON_EDID:
+       default:
+               return DRM_MODE_SUBCONNECTOR_Unknown;
+       }
+}
+EXPORT_SYMBOL(drm_dp_subconnector_type);
+
+/**
+ * drm_dp_set_subconnector_property - set subconnector for DP connector
+ * @connector: connector to set property on
+ * @status: connector status
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ *
+ * Called by a driver on every detect event.
+ */
+void drm_dp_set_subconnector_property(struct drm_connector *connector,
+                                     enum drm_connector_status status,
+                                     const u8 *dpcd,
+                                     const u8 port_cap[4])
+{
+       enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+
+       if (status == connector_status_connected)
+               subconnector = drm_dp_subconnector_type(dpcd, port_cap);
+       drm_object_property_set_value(&connector->base,
+                       connector->dev->mode_config.dp_subconnector_property,
+                       subconnector);
+}
+EXPORT_SYMBOL(drm_dp_set_subconnector_property);
+
+/**
+ * drm_dp_read_sink_count_cap() - Check whether a given connector has a valid sink
+ * count
+ * @connector: The DRM connector to check
+ * @dpcd: A cached copy of the connector's DPCD RX capabilities
+ * @desc: A cached copy of the connector's DP descriptor
+ *
+ * See also: drm_dp_read_sink_count()
+ *
+ * Returns: %True if the (e)DP connector has a valid sink count that should
+ * be probed, %false otherwise.
+ */
+bool drm_dp_read_sink_count_cap(struct drm_connector *connector,
+                               const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                               const struct drm_dp_desc *desc)
+{
+       /* Some eDP panels don't set a valid value for the sink count */
+       return connector->connector_type != DRM_MODE_CONNECTOR_eDP &&
+               dpcd[DP_DPCD_REV] >= DP_DPCD_REV_11 &&
+               dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT &&
+               !drm_dp_has_quirk(desc, DP_DPCD_QUIRK_NO_SINK_COUNT);
+}
+EXPORT_SYMBOL(drm_dp_read_sink_count_cap);
+
+/**
+ * drm_dp_read_sink_count() - Retrieve the sink count for a given sink
+ * @aux: The DP AUX channel to use
+ *
+ * See also: drm_dp_read_sink_count_cap()
+ *
+ * Returns: The current sink count reported by @aux, or a negative error code
+ * otherwise.
+ */
+int drm_dp_read_sink_count(struct drm_dp_aux *aux)
+{
+       u8 count;
+       int ret;
+
+       ret = drm_dp_dpcd_readb(aux, DP_SINK_COUNT, &count);
+       if (ret < 0)
+               return ret;
+       if (ret != 1)
+               return -EIO;
+
+       return DP_GET_SINK_COUNT(count);
+}
+EXPORT_SYMBOL(drm_dp_read_sink_count);
+
+/*
+ * I2C-over-AUX implementation
+ */
+
+static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+              I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+              I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+              I2C_FUNC_10BIT_ADDR;
+}
+
+static void drm_dp_i2c_msg_write_status_update(struct drm_dp_aux_msg *msg)
+{
+       /*
+        * In case of i2c defer or short i2c ack reply to a write,
+        * we need to switch to WRITE_STATUS_UPDATE to drain the
+        * rest of the message
+        */
+       if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE) {
+               msg->request &= DP_AUX_I2C_MOT;
+               msg->request |= DP_AUX_I2C_WRITE_STATUS_UPDATE;
+       }
+}
+
+#define AUX_PRECHARGE_LEN 10 /* 10 to 16 */
+#define AUX_SYNC_LEN (16 + 4) /* preamble + AUX_SYNC_END */
+#define AUX_STOP_LEN 4
+#define AUX_CMD_LEN 4
+#define AUX_ADDRESS_LEN 20
+#define AUX_REPLY_PAD_LEN 4
+#define AUX_LENGTH_LEN 8
+
+/*
+ * Calculate the duration of the AUX request/reply in usec. Gives the
+ * "best" case estimate, ie. successful while as short as possible.
+ */
+static int drm_dp_aux_req_duration(const struct drm_dp_aux_msg *msg)
+{
+       int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN +
+               AUX_CMD_LEN + AUX_ADDRESS_LEN + AUX_LENGTH_LEN;
+
+       if ((msg->request & DP_AUX_I2C_READ) == 0)
+               len += msg->size * 8;
+
+       return len;
+}
+
+static int drm_dp_aux_reply_duration(const struct drm_dp_aux_msg *msg)
+{
+       int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN +
+               AUX_CMD_LEN + AUX_REPLY_PAD_LEN;
+
+       /*
+        * For read we expect what was asked. For writes there will
+        * be 0 or 1 data bytes. Assume 0 for the "best" case.
+        */
+       if (msg->request & DP_AUX_I2C_READ)
+               len += msg->size * 8;
+
+       return len;
+}
+
+#define I2C_START_LEN 1
+#define I2C_STOP_LEN 1
+#define I2C_ADDR_LEN 9 /* ADDRESS + R/W + ACK/NACK */
+#define I2C_DATA_LEN 9 /* DATA + ACK/NACK */
+
+/*
+ * Calculate the length of the i2c transfer in usec, assuming
+ * the i2c bus speed is as specified. Gives the the "worst"
+ * case estimate, ie. successful while as long as possible.
+ * Doesn't account the the "MOT" bit, and instead assumes each
+ * message includes a START, ADDRESS and STOP. Neither does it
+ * account for additional random variables such as clock stretching.
+ */
+static int drm_dp_i2c_msg_duration(const struct drm_dp_aux_msg *msg,
+                                  int i2c_speed_khz)
+{
+       /* AUX bitrate is 1MHz, i2c bitrate as specified */
+       return DIV_ROUND_UP((I2C_START_LEN + I2C_ADDR_LEN +
+                            msg->size * I2C_DATA_LEN +
+                            I2C_STOP_LEN) * 1000, i2c_speed_khz);
+}
+
+/*
+ * Determine how many retries should be attempted to successfully transfer
+ * the specified message, based on the estimated durations of the
+ * i2c and AUX transfers.
+ */
+static int drm_dp_i2c_retry_count(const struct drm_dp_aux_msg *msg,
+                             int i2c_speed_khz)
+{
+       int aux_time_us = drm_dp_aux_req_duration(msg) +
+               drm_dp_aux_reply_duration(msg);
+       int i2c_time_us = drm_dp_i2c_msg_duration(msg, i2c_speed_khz);
+
+       return DIV_ROUND_UP(i2c_time_us, aux_time_us + AUX_RETRY_INTERVAL);
+}
+
+/*
+ * FIXME currently assumes 10 kHz as some real world devices seem
+ * to require it. We should query/set the speed via DPCD if supported.
+ */
+static int dp_aux_i2c_speed_khz __read_mostly = 10;
+module_param_unsafe(dp_aux_i2c_speed_khz, int, 0644);
+MODULE_PARM_DESC(dp_aux_i2c_speed_khz,
+                "Assumed speed of the i2c bus in kHz, (1-400, default 10)");
+
+/*
+ * Transfer a single I2C-over-AUX message and handle various error conditions,
+ * retrying the transaction as appropriate.  It is assumed that the
+ * &drm_dp_aux.transfer function does not modify anything in the msg other than the
+ * reply field.
+ *
+ * Returns bytes transferred on success, or a negative error code on failure.
+ */
+static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
+{
+       unsigned int retry, defer_i2c;
+       int ret;
+       /*
+        * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device
+        * is required to retry at least seven times upon receiving AUX_DEFER
+        * before giving up the AUX transaction.
+        *
+        * We also try to account for the i2c bus speed.
+        */
+       int max_retries = max(7, drm_dp_i2c_retry_count(msg, dp_aux_i2c_speed_khz));
+
+       for (retry = 0, defer_i2c = 0; retry < (max_retries + defer_i2c); retry++) {
+               ret = aux->transfer(aux, msg);
+               if (ret < 0) {
+                       if (ret == -EBUSY)
+                               continue;
+
+                       /*
+                        * While timeouts can be errors, they're usually normal
+                        * behavior (for instance, when a driver tries to
+                        * communicate with a non-existent DisplayPort device).
+                        * Avoid spamming the kernel log with timeout errors.
+                        */
+                       if (ret == -ETIMEDOUT)
+                               drm_dbg_kms_ratelimited(aux->drm_dev, "%s: transaction timed out\n",
+                                                       aux->name);
+                       else
+                               drm_dbg_kms(aux->drm_dev, "%s: transaction failed: %d\n",
+                                           aux->name, ret);
+                       return ret;
+               }
+
+
+               switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) {
+               case DP_AUX_NATIVE_REPLY_ACK:
+                       /*
+                        * For I2C-over-AUX transactions this isn't enough, we
+                        * need to check for the I2C ACK reply.
+                        */
+                       break;
+
+               case DP_AUX_NATIVE_REPLY_NACK:
+                       drm_dbg_kms(aux->drm_dev, "%s: native nack (result=%d, size=%zu)\n",
+                                   aux->name, ret, msg->size);
+                       return -EREMOTEIO;
+
+               case DP_AUX_NATIVE_REPLY_DEFER:
+                       drm_dbg_kms(aux->drm_dev, "%s: native defer\n", aux->name);
+                       /*
+                        * We could check for I2C bit rate capabilities and if
+                        * available adjust this interval. We could also be
+                        * more careful with DP-to-legacy adapters where a
+                        * long legacy cable may force very low I2C bit rates.
+                        *
+                        * For now just defer for long enough to hopefully be
+                        * safe for all use-cases.
+                        */
+                       usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
+                       continue;
+
+               default:
+                       drm_err(aux->drm_dev, "%s: invalid native reply %#04x\n",
+                               aux->name, msg->reply);
+                       return -EREMOTEIO;
+               }
+
+               switch (msg->reply & DP_AUX_I2C_REPLY_MASK) {
+               case DP_AUX_I2C_REPLY_ACK:
+                       /*
+                        * Both native ACK and I2C ACK replies received. We
+                        * can assume the transfer was successful.
+                        */
+                       if (ret != msg->size)
+                               drm_dp_i2c_msg_write_status_update(msg);
+                       return ret;
+
+               case DP_AUX_I2C_REPLY_NACK:
+                       drm_dbg_kms(aux->drm_dev, "%s: I2C nack (result=%d, size=%zu)\n",
+                                   aux->name, ret, msg->size);
+                       aux->i2c_nack_count++;
+                       return -EREMOTEIO;
+
+               case DP_AUX_I2C_REPLY_DEFER:
+                       drm_dbg_kms(aux->drm_dev, "%s: I2C defer\n", aux->name);
+                       /* DP Compliance Test 4.2.2.5 Requirement:
+                        * Must have at least 7 retries for I2C defers on the
+                        * transaction to pass this test
+                        */
+                       aux->i2c_defer_count++;
+                       if (defer_i2c < 7)
+                               defer_i2c++;
+                       usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
+                       drm_dp_i2c_msg_write_status_update(msg);
+
+                       continue;
+
+               default:
+                       drm_err(aux->drm_dev, "%s: invalid I2C reply %#04x\n",
+                               aux->name, msg->reply);
+                       return -EREMOTEIO;
+               }
+       }
+
+       drm_dbg_kms(aux->drm_dev, "%s: Too many retries, giving up\n", aux->name);
+       return -EREMOTEIO;
+}
+
+static void drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg *msg,
+                                      const struct i2c_msg *i2c_msg)
+{
+       msg->request = (i2c_msg->flags & I2C_M_RD) ?
+               DP_AUX_I2C_READ : DP_AUX_I2C_WRITE;
+       if (!(i2c_msg->flags & I2C_M_STOP))
+               msg->request |= DP_AUX_I2C_MOT;
+}
+
+/*
+ * Keep retrying drm_dp_i2c_do_msg until all data has been transferred.
+ *
+ * Returns an error code on failure, or a recommended transfer size on success.
+ */
+static int drm_dp_i2c_drain_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *orig_msg)
+{
+       int err, ret = orig_msg->size;
+       struct drm_dp_aux_msg msg = *orig_msg;
+
+       while (msg.size > 0) {
+               err = drm_dp_i2c_do_msg(aux, &msg);
+               if (err <= 0)
+                       return err == 0 ? -EPROTO : err;
+
+               if (err < msg.size && err < ret) {
+                       drm_dbg_kms(aux->drm_dev,
+                                   "%s: Partial I2C reply: requested %zu bytes got %d bytes\n",
+                                   aux->name, msg.size, err);
+                       ret = err;
+               }
+
+               msg.size -= err;
+               msg.buffer += err;
+       }
+
+       return ret;
+}
+
+/*
+ * Bizlink designed DP->DVI-D Dual Link adapters require the I2C over AUX
+ * packets to be as large as possible. If not, the I2C transactions never
+ * succeed. Hence the default is maximum.
+ */
+static int dp_aux_i2c_transfer_size __read_mostly = DP_AUX_MAX_PAYLOAD_BYTES;
+module_param_unsafe(dp_aux_i2c_transfer_size, int, 0644);
+MODULE_PARM_DESC(dp_aux_i2c_transfer_size,
+                "Number of bytes to transfer in a single I2C over DP AUX CH message, (1-16, default 16)");
+
+static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
+                          int num)
+{
+       struct drm_dp_aux *aux = adapter->algo_data;
+       unsigned int i, j;
+       unsigned transfer_size;
+       struct drm_dp_aux_msg msg;
+       int err = 0;
+
+       dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, DP_AUX_MAX_PAYLOAD_BYTES);
+
+       memset(&msg, 0, sizeof(msg));
+
+       for (i = 0; i < num; i++) {
+               msg.address = msgs[i].addr;
+               drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
+               /* Send a bare address packet to start the transaction.
+                * Zero sized messages specify an address only (bare
+                * address) transaction.
+                */
+               msg.buffer = NULL;
+               msg.size = 0;
+               err = drm_dp_i2c_do_msg(aux, &msg);
+
+               /*
+                * Reset msg.request in case in case it got
+                * changed into a WRITE_STATUS_UPDATE.
+                */
+               drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
+
+               if (err < 0)
+                       break;
+               /* We want each transaction to be as large as possible, but
+                * we'll go to smaller sizes if the hardware gives us a
+                * short reply.
+                */
+               transfer_size = dp_aux_i2c_transfer_size;
+               for (j = 0; j < msgs[i].len; j += msg.size) {
+                       msg.buffer = msgs[i].buf + j;
+                       msg.size = min(transfer_size, msgs[i].len - j);
+
+                       err = drm_dp_i2c_drain_msg(aux, &msg);
+
+                       /*
+                        * Reset msg.request in case in case it got
+                        * changed into a WRITE_STATUS_UPDATE.
+                        */
+                       drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
+
+                       if (err < 0)
+                               break;
+                       transfer_size = err;
+               }
+               if (err < 0)
+                       break;
+       }
+       if (err >= 0)
+               err = num;
+       /* Send a bare address packet to close out the transaction.
+        * Zero sized messages specify an address only (bare
+        * address) transaction.
+        */
+       msg.request &= ~DP_AUX_I2C_MOT;
+       msg.buffer = NULL;
+       msg.size = 0;
+       (void)drm_dp_i2c_do_msg(aux, &msg);
+
+       return err;
+}
+
+static const struct i2c_algorithm drm_dp_i2c_algo = {
+       .functionality = drm_dp_i2c_functionality,
+       .master_xfer = drm_dp_i2c_xfer,
+};
+
+static struct drm_dp_aux *i2c_to_aux(struct i2c_adapter *i2c)
+{
+       return container_of(i2c, struct drm_dp_aux, ddc);
+}
+
+static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+       mutex_lock(&i2c_to_aux(i2c)->hw_mutex);
+}
+
+static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+       return mutex_trylock(&i2c_to_aux(i2c)->hw_mutex);
+}
+
+static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+       mutex_unlock(&i2c_to_aux(i2c)->hw_mutex);
+}
+
+static const struct i2c_lock_operations drm_dp_i2c_lock_ops = {
+       .lock_bus = lock_bus,
+       .trylock_bus = trylock_bus,
+       .unlock_bus = unlock_bus,
+};
+
+static int drm_dp_aux_get_crc(struct drm_dp_aux *aux, u8 *crc)
+{
+       u8 buf, count;
+       int ret;
+
+       ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf);
+       if (ret < 0)
+               return ret;
+
+       WARN_ON(!(buf & DP_TEST_SINK_START));
+
+       ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK_MISC, &buf);
+       if (ret < 0)
+               return ret;
+
+       count = buf & DP_TEST_COUNT_MASK;
+       if (count == aux->crc_count)
+               return -EAGAIN; /* No CRC yet */
+
+       aux->crc_count = count;
+
+       /*
+        * At DP_TEST_CRC_R_CR, there's 6 bytes containing CRC data, 2 bytes
+        * per component (RGB or CrYCb).
+        */
+       ret = drm_dp_dpcd_read(aux, DP_TEST_CRC_R_CR, crc, 6);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static void drm_dp_aux_crc_work(struct work_struct *work)
+{
+       struct drm_dp_aux *aux = container_of(work, struct drm_dp_aux,
+                                             crc_work);
+       struct drm_crtc *crtc;
+       u8 crc_bytes[6];
+       uint32_t crcs[3];
+       int ret;
+
+       if (WARN_ON(!aux->crtc))
+               return;
+
+       crtc = aux->crtc;
+       while (crtc->crc.opened) {
+               drm_crtc_wait_one_vblank(crtc);
+               if (!crtc->crc.opened)
+                       break;
+
+               ret = drm_dp_aux_get_crc(aux, crc_bytes);
+               if (ret == -EAGAIN) {
+                       usleep_range(1000, 2000);
+                       ret = drm_dp_aux_get_crc(aux, crc_bytes);
+               }
+
+               if (ret == -EAGAIN) {
+                       drm_dbg_kms(aux->drm_dev, "%s: Get CRC failed after retrying: %d\n",
+                                   aux->name, ret);
+                       continue;
+               } else if (ret) {
+                       drm_dbg_kms(aux->drm_dev, "%s: Failed to get a CRC: %d\n", aux->name, ret);
+                       continue;
+               }
+
+               crcs[0] = crc_bytes[0] | crc_bytes[1] << 8;
+               crcs[1] = crc_bytes[2] | crc_bytes[3] << 8;
+               crcs[2] = crc_bytes[4] | crc_bytes[5] << 8;
+               drm_crtc_add_crc_entry(crtc, false, 0, crcs);
+       }
+}
+
+/**
+ * drm_dp_remote_aux_init() - minimally initialise a remote aux channel
+ * @aux: DisplayPort AUX channel
+ *
+ * Used for remote aux channel in general. Merely initialize the crc work
+ * struct.
+ */
+void drm_dp_remote_aux_init(struct drm_dp_aux *aux)
+{
+       INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work);
+}
+EXPORT_SYMBOL(drm_dp_remote_aux_init);
+
+/**
+ * drm_dp_aux_init() - minimally initialise an aux channel
+ * @aux: DisplayPort AUX channel
+ *
+ * If you need to use the drm_dp_aux's i2c adapter prior to registering it with
+ * the outside world, call drm_dp_aux_init() first. For drivers which are
+ * grandparents to their AUX adapters (e.g. the AUX adapter is parented by a
+ * &drm_connector), you must still call drm_dp_aux_register() once the connector
+ * has been registered to allow userspace access to the auxiliary DP channel.
+ * Likewise, for such drivers you should also assign &drm_dp_aux.drm_dev as
+ * early as possible so that the &drm_device that corresponds to the AUX adapter
+ * may be mentioned in debugging output from the DRM DP helpers.
+ *
+ * For devices which use a separate platform device for their AUX adapters, this
+ * may be called as early as required by the driver.
+ *
+ */
+void drm_dp_aux_init(struct drm_dp_aux *aux)
+{
+       mutex_init(&aux->hw_mutex);
+       mutex_init(&aux->cec.lock);
+       INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work);
+
+       aux->ddc.algo = &drm_dp_i2c_algo;
+       aux->ddc.algo_data = aux;
+       aux->ddc.retries = 3;
+
+       aux->ddc.lock_ops = &drm_dp_i2c_lock_ops;
+}
+EXPORT_SYMBOL(drm_dp_aux_init);
+
+/**
+ * drm_dp_aux_register() - initialise and register aux channel
+ * @aux: DisplayPort AUX channel
+ *
+ * Automatically calls drm_dp_aux_init() if this hasn't been done yet. This
+ * should only be called once the parent of @aux, &drm_dp_aux.dev, is
+ * initialized. For devices which are grandparents of their AUX channels,
+ * &drm_dp_aux.dev will typically be the &drm_connector &device which
+ * corresponds to @aux. For these devices, it's advised to call
+ * drm_dp_aux_register() in &drm_connector_funcs.late_register, and likewise to
+ * call drm_dp_aux_unregister() in &drm_connector_funcs.early_unregister.
+ * Functions which don't follow this will likely Oops when
+ * %CONFIG_DRM_DP_AUX_CHARDEV is enabled.
+ *
+ * For devices where the AUX channel is a device that exists independently of
+ * the &drm_device that uses it, such as SoCs and bridge devices, it is
+ * recommended to call drm_dp_aux_register() after a &drm_device has been
+ * assigned to &drm_dp_aux.drm_dev, and likewise to call
+ * drm_dp_aux_unregister() once the &drm_device should no longer be associated
+ * with the AUX channel (e.g. on bridge detach).
+ *
+ * Drivers which need to use the aux channel before either of the two points
+ * mentioned above need to call drm_dp_aux_init() in order to use the AUX
+ * channel before registration.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_aux_register(struct drm_dp_aux *aux)
+{
+       int ret;
+
+       WARN_ON_ONCE(!aux->drm_dev);
+
+       if (!aux->ddc.algo)
+               drm_dp_aux_init(aux);
+
+       aux->ddc.class = I2C_CLASS_DDC;
+       aux->ddc.owner = THIS_MODULE;
+       aux->ddc.dev.parent = aux->dev;
+
+       strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev),
+               sizeof(aux->ddc.name));
+
+       ret = drm_dp_aux_register_devnode(aux);
+       if (ret)
+               return ret;
+
+       ret = i2c_add_adapter(&aux->ddc);
+       if (ret) {
+               drm_dp_aux_unregister_devnode(aux);
+               return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_aux_register);
+
+/**
+ * drm_dp_aux_unregister() - unregister an AUX adapter
+ * @aux: DisplayPort AUX channel
+ */
+void drm_dp_aux_unregister(struct drm_dp_aux *aux)
+{
+       drm_dp_aux_unregister_devnode(aux);
+       i2c_del_adapter(&aux->ddc);
+}
+EXPORT_SYMBOL(drm_dp_aux_unregister);
+
+#define PSR_SETUP_TIME(x) [DP_PSR_SETUP_TIME_ ## x >> DP_PSR_SETUP_TIME_SHIFT] = (x)
+
+/**
+ * drm_dp_psr_setup_time() - PSR setup in time usec
+ * @psr_cap: PSR capabilities from DPCD
+ *
+ * Returns:
+ * PSR setup time for the panel in microseconds,  negative
+ * error code on failure.
+ */
+int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE])
+{
+       static const u16 psr_setup_time_us[] = {
+               PSR_SETUP_TIME(330),
+               PSR_SETUP_TIME(275),
+               PSR_SETUP_TIME(220),
+               PSR_SETUP_TIME(165),
+               PSR_SETUP_TIME(110),
+               PSR_SETUP_TIME(55),
+               PSR_SETUP_TIME(0),
+       };
+       int i;
+
+       i = (psr_cap[1] & DP_PSR_SETUP_TIME_MASK) >> DP_PSR_SETUP_TIME_SHIFT;
+       if (i >= ARRAY_SIZE(psr_setup_time_us))
+               return -EINVAL;
+
+       return psr_setup_time_us[i];
+}
+EXPORT_SYMBOL(drm_dp_psr_setup_time);
+
+#undef PSR_SETUP_TIME
+
+/**
+ * drm_dp_start_crc() - start capture of frame CRCs
+ * @aux: DisplayPort AUX channel
+ * @crtc: CRTC displaying the frames whose CRCs are to be captured
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc)
+{
+       u8 buf;
+       int ret;
+
+       ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf);
+       if (ret < 0)
+               return ret;
+
+       ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf | DP_TEST_SINK_START);
+       if (ret < 0)
+               return ret;
+
+       aux->crc_count = 0;
+       aux->crtc = crtc;
+       schedule_work(&aux->crc_work);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_start_crc);
+
+/**
+ * drm_dp_stop_crc() - stop capture of frame CRCs
+ * @aux: DisplayPort AUX channel
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_stop_crc(struct drm_dp_aux *aux)
+{
+       u8 buf;
+       int ret;
+
+       ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf);
+       if (ret < 0)
+               return ret;
+
+       ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf & ~DP_TEST_SINK_START);
+       if (ret < 0)
+               return ret;
+
+       flush_work(&aux->crc_work);
+       aux->crtc = NULL;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_stop_crc);
+
+struct dpcd_quirk {
+       u8 oui[3];
+       u8 device_id[6];
+       bool is_branch;
+       u32 quirks;
+};
+
+#define OUI(first, second, third) { (first), (second), (third) }
+#define DEVICE_ID(first, second, third, fourth, fifth, sixth) \
+       { (first), (second), (third), (fourth), (fifth), (sixth) }
+
+#define DEVICE_ID_ANY  DEVICE_ID(0, 0, 0, 0, 0, 0)
+
+static const struct dpcd_quirk dpcd_quirk_list[] = {
+       /* Analogix 7737 needs reduced M and N at HBR2 link rates */
+       { OUI(0x00, 0x22, 0xb9), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_CONSTANT_N) },
+       /* LG LP140WF6-SPM1 eDP panel */
+       { OUI(0x00, 0x22, 0xb9), DEVICE_ID('s', 'i', 'v', 'a', 'r', 'T'), false, BIT(DP_DPCD_QUIRK_CONSTANT_N) },
+       /* Apple panels need some additional handling to support PSR */
+       { OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) },
+       /* CH7511 seems to leave SINK_COUNT zeroed */
+       { OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) },
+       /* Synaptics DP1.4 MST hubs can support DSC without virtual DPCD */
+       { OUI(0x90, 0xCC, 0x24), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) },
+       /* Apple MacBookPro 2017 15 inch eDP Retina panel reports too low DP_MAX_LINK_RATE */
+       { OUI(0x00, 0x10, 0xfa), DEVICE_ID(101, 68, 21, 101, 98, 97), false, BIT(DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS) },
+};
+
+#undef OUI
+
+/*
+ * Get a bit mask of DPCD quirks for the sink/branch device identified by
+ * ident. The quirk data is shared but it's up to the drivers to act on the
+ * data.
+ *
+ * For now, only the OUI (first three bytes) is used, but this may be extended
+ * to device identification string and hardware/firmware revisions later.
+ */
+static u32
+drm_dp_get_quirks(const struct drm_dp_dpcd_ident *ident, bool is_branch)
+{
+       const struct dpcd_quirk *quirk;
+       u32 quirks = 0;
+       int i;
+       u8 any_device[] = DEVICE_ID_ANY;
+
+       for (i = 0; i < ARRAY_SIZE(dpcd_quirk_list); i++) {
+               quirk = &dpcd_quirk_list[i];
+
+               if (quirk->is_branch != is_branch)
+                       continue;
+
+               if (memcmp(quirk->oui, ident->oui, sizeof(ident->oui)) != 0)
+                       continue;
+
+               if (memcmp(quirk->device_id, any_device, sizeof(any_device)) != 0 &&
+                   memcmp(quirk->device_id, ident->device_id, sizeof(ident->device_id)) != 0)
+                       continue;
+
+               quirks |= quirk->quirks;
+       }
+
+       return quirks;
+}
+
+#undef DEVICE_ID_ANY
+#undef DEVICE_ID
+
+/**
+ * drm_dp_read_desc - read sink/branch descriptor from DPCD
+ * @aux: DisplayPort AUX channel
+ * @desc: Device descriptor to fill from DPCD
+ * @is_branch: true for branch devices, false for sink devices
+ *
+ * Read DPCD 0x400 (sink) or 0x500 (branch) into @desc. Also debug log the
+ * identification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc,
+                    bool is_branch)
+{
+       struct drm_dp_dpcd_ident *ident = &desc->ident;
+       unsigned int offset = is_branch ? DP_BRANCH_OUI : DP_SINK_OUI;
+       int ret, dev_id_len;
+
+       ret = drm_dp_dpcd_read(aux, offset, ident, sizeof(*ident));
+       if (ret < 0)
+               return ret;
+
+       desc->quirks = drm_dp_get_quirks(ident, is_branch);
+
+       dev_id_len = strnlen(ident->device_id, sizeof(ident->device_id));
+
+       drm_dbg_kms(aux->drm_dev,
+                   "%s: DP %s: OUI %*phD dev-ID %*pE HW-rev %d.%d SW-rev %d.%d quirks 0x%04x\n",
+                   aux->name, is_branch ? "branch" : "sink",
+                   (int)sizeof(ident->oui), ident->oui, dev_id_len,
+                   ident->device_id, ident->hw_rev >> 4, ident->hw_rev & 0xf,
+                   ident->sw_major_rev, ident->sw_minor_rev, desc->quirks);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_read_desc);
+
+/**
+ * drm_dp_dsc_sink_max_slice_count() - Get the max slice count
+ * supported by the DSC sink.
+ * @dsc_dpcd: DSC capabilities from DPCD
+ * @is_edp: true if its eDP, false for DP
+ *
+ * Read the slice capabilities DPCD register from DSC sink to get
+ * the maximum slice count supported. This is used to populate
+ * the DSC parameters in the &struct drm_dsc_config by the driver.
+ * Driver creates an infoframe using these parameters to populate
+ * &struct drm_dsc_pps_infoframe. These are sent to the sink using DSC
+ * infoframe using the helper function drm_dsc_pps_infoframe_pack()
+ *
+ * Returns:
+ * Maximum slice count supported by DSC sink or 0 its invalid
+ */
+u8 drm_dp_dsc_sink_max_slice_count(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE],
+                                  bool is_edp)
+{
+       u8 slice_cap1 = dsc_dpcd[DP_DSC_SLICE_CAP_1 - DP_DSC_SUPPORT];
+
+       if (is_edp) {
+               /* For eDP, register DSC_SLICE_CAPABILITIES_1 gives slice count */
+               if (slice_cap1 & DP_DSC_4_PER_DP_DSC_SINK)
+                       return 4;
+               if (slice_cap1 & DP_DSC_2_PER_DP_DSC_SINK)
+                       return 2;
+               if (slice_cap1 & DP_DSC_1_PER_DP_DSC_SINK)
+                       return 1;
+       } else {
+               /* For DP, use values from DSC_SLICE_CAP_1 and DSC_SLICE_CAP2 */
+               u8 slice_cap2 = dsc_dpcd[DP_DSC_SLICE_CAP_2 - DP_DSC_SUPPORT];
+
+               if (slice_cap2 & DP_DSC_24_PER_DP_DSC_SINK)
+                       return 24;
+               if (slice_cap2 & DP_DSC_20_PER_DP_DSC_SINK)
+                       return 20;
+               if (slice_cap2 & DP_DSC_16_PER_DP_DSC_SINK)
+                       return 16;
+               if (slice_cap1 & DP_DSC_12_PER_DP_DSC_SINK)
+                       return 12;
+               if (slice_cap1 & DP_DSC_10_PER_DP_DSC_SINK)
+                       return 10;
+               if (slice_cap1 & DP_DSC_8_PER_DP_DSC_SINK)
+                       return 8;
+               if (slice_cap1 & DP_DSC_6_PER_DP_DSC_SINK)
+                       return 6;
+               if (slice_cap1 & DP_DSC_4_PER_DP_DSC_SINK)
+                       return 4;
+               if (slice_cap1 & DP_DSC_2_PER_DP_DSC_SINK)
+                       return 2;
+               if (slice_cap1 & DP_DSC_1_PER_DP_DSC_SINK)
+                       return 1;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_dsc_sink_max_slice_count);
+
+/**
+ * drm_dp_dsc_sink_line_buf_depth() - Get the line buffer depth in bits
+ * @dsc_dpcd: DSC capabilities from DPCD
+ *
+ * Read the DSC DPCD register to parse the line buffer depth in bits which is
+ * number of bits of precision within the decoder line buffer supported by
+ * the DSC sink. This is used to populate the DSC parameters in the
+ * &struct drm_dsc_config by the driver.
+ * Driver creates an infoframe using these parameters to populate
+ * &struct drm_dsc_pps_infoframe. These are sent to the sink using DSC
+ * infoframe using the helper function drm_dsc_pps_infoframe_pack()
+ *
+ * Returns:
+ * Line buffer depth supported by DSC panel or 0 its invalid
+ */
+u8 drm_dp_dsc_sink_line_buf_depth(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE])
+{
+       u8 line_buf_depth = dsc_dpcd[DP_DSC_LINE_BUF_BIT_DEPTH - DP_DSC_SUPPORT];
+
+       switch (line_buf_depth & DP_DSC_LINE_BUF_BIT_DEPTH_MASK) {
+       case DP_DSC_LINE_BUF_BIT_DEPTH_9:
+               return 9;
+       case DP_DSC_LINE_BUF_BIT_DEPTH_10:
+               return 10;
+       case DP_DSC_LINE_BUF_BIT_DEPTH_11:
+               return 11;
+       case DP_DSC_LINE_BUF_BIT_DEPTH_12:
+               return 12;
+       case DP_DSC_LINE_BUF_BIT_DEPTH_13:
+               return 13;
+       case DP_DSC_LINE_BUF_BIT_DEPTH_14:
+               return 14;
+       case DP_DSC_LINE_BUF_BIT_DEPTH_15:
+               return 15;
+       case DP_DSC_LINE_BUF_BIT_DEPTH_16:
+               return 16;
+       case DP_DSC_LINE_BUF_BIT_DEPTH_8:
+               return 8;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_dsc_sink_line_buf_depth);
+
+/**
+ * drm_dp_dsc_sink_supported_input_bpcs() - Get all the input bits per component
+ * values supported by the DSC sink.
+ * @dsc_dpcd: DSC capabilities from DPCD
+ * @dsc_bpc: An array to be filled by this helper with supported
+ *           input bpcs.
+ *
+ * Read the DSC DPCD from the sink device to parse the supported bits per
+ * component values. This is used to populate the DSC parameters
+ * in the &struct drm_dsc_config by the driver.
+ * Driver creates an infoframe using these parameters to populate
+ * &struct drm_dsc_pps_infoframe. These are sent to the sink using DSC
+ * infoframe using the helper function drm_dsc_pps_infoframe_pack()
+ *
+ * Returns:
+ * Number of input BPC values parsed from the DPCD
+ */
+int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE],
+                                        u8 dsc_bpc[3])
+{
+       int num_bpc = 0;
+       u8 color_depth = dsc_dpcd[DP_DSC_DEC_COLOR_DEPTH_CAP - DP_DSC_SUPPORT];
+
+       if (color_depth & DP_DSC_12_BPC)
+               dsc_bpc[num_bpc++] = 12;
+       if (color_depth & DP_DSC_10_BPC)
+               dsc_bpc[num_bpc++] = 10;
+       if (color_depth & DP_DSC_8_BPC)
+               dsc_bpc[num_bpc++] = 8;
+
+       return num_bpc;
+}
+EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs);
+
+/**
+ * drm_dp_read_lttpr_common_caps - read the LTTPR common capabilities
+ * @aux: DisplayPort AUX channel
+ * @caps: buffer to return the capability info in
+ *
+ * Read capabilities common to all LTTPRs.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,
+                                 u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
+{
+       int ret;
+
+       ret = drm_dp_dpcd_read(aux,
+                              DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV,
+                              caps, DP_LTTPR_COMMON_CAP_SIZE);
+       if (ret < 0)
+               return ret;
+
+       WARN_ON(ret != DP_LTTPR_COMMON_CAP_SIZE);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_read_lttpr_common_caps);
+
+/**
+ * drm_dp_read_lttpr_phy_caps - read the capabilities for a given LTTPR PHY
+ * @aux: DisplayPort AUX channel
+ * @dp_phy: LTTPR PHY to read the capabilities for
+ * @caps: buffer to return the capability info in
+ *
+ * Read the capabilities for the given LTTPR PHY.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,
+                              enum drm_dp_phy dp_phy,
+                              u8 caps[DP_LTTPR_PHY_CAP_SIZE])
+{
+       int ret;
+
+       ret = drm_dp_dpcd_read(aux,
+                              DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy),
+                              caps, DP_LTTPR_PHY_CAP_SIZE);
+       if (ret < 0)
+               return ret;
+
+       WARN_ON(ret != DP_LTTPR_PHY_CAP_SIZE);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_read_lttpr_phy_caps);
+
+static u8 dp_lttpr_common_cap(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE], int r)
+{
+       return caps[r - DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV];
+}
+
+/**
+ * drm_dp_lttpr_count - get the number of detected LTTPRs
+ * @caps: LTTPR common capabilities
+ *
+ * Get the number of detected LTTPRs from the LTTPR common capabilities info.
+ *
+ * Returns:
+ *   -ERANGE if more than supported number (8) of LTTPRs are detected
+ *   -EINVAL if the DP_PHY_REPEATER_CNT register contains an invalid value
+ *   otherwise the number of detected LTTPRs
+ */
+int drm_dp_lttpr_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
+{
+       u8 count = dp_lttpr_common_cap(caps, DP_PHY_REPEATER_CNT);
+
+       switch (hweight8(count)) {
+       case 0:
+               return 0;
+       case 1:
+               return 8 - ilog2(count);
+       case 8:
+               return -ERANGE;
+       default:
+               return -EINVAL;
+       }
+}
+EXPORT_SYMBOL(drm_dp_lttpr_count);
+
+/**
+ * drm_dp_lttpr_max_link_rate - get the maximum link rate supported by all LTTPRs
+ * @caps: LTTPR common capabilities
+ *
+ * Returns the maximum link rate supported by all detected LTTPRs.
+ */
+int drm_dp_lttpr_max_link_rate(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
+{
+       u8 rate = dp_lttpr_common_cap(caps, DP_MAX_LINK_RATE_PHY_REPEATER);
+
+       return drm_dp_bw_code_to_link_rate(rate);
+}
+EXPORT_SYMBOL(drm_dp_lttpr_max_link_rate);
+
+/**
+ * drm_dp_lttpr_max_lane_count - get the maximum lane count supported by all LTTPRs
+ * @caps: LTTPR common capabilities
+ *
+ * Returns the maximum lane count supported by all detected LTTPRs.
+ */
+int drm_dp_lttpr_max_lane_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
+{
+       u8 max_lanes = dp_lttpr_common_cap(caps, DP_MAX_LANE_COUNT_PHY_REPEATER);
+
+       return max_lanes & DP_MAX_LANE_COUNT_MASK;
+}
+EXPORT_SYMBOL(drm_dp_lttpr_max_lane_count);
+
+/**
+ * drm_dp_lttpr_voltage_swing_level_3_supported - check for LTTPR vswing3 support
+ * @caps: LTTPR PHY capabilities
+ *
+ * Returns true if the @caps for an LTTPR TX PHY indicate support for
+ * voltage swing level 3.
+ */
+bool
+drm_dp_lttpr_voltage_swing_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE])
+{
+       u8 txcap = dp_lttpr_phy_cap(caps, DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1);
+
+       return txcap & DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED;
+}
+EXPORT_SYMBOL(drm_dp_lttpr_voltage_swing_level_3_supported);
+
+/**
+ * drm_dp_lttpr_pre_emphasis_level_3_supported - check for LTTPR preemph3 support
+ * @caps: LTTPR PHY capabilities
+ *
+ * Returns true if the @caps for an LTTPR TX PHY indicate support for
+ * pre-emphasis level 3.
+ */
+bool
+drm_dp_lttpr_pre_emphasis_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE])
+{
+       u8 txcap = dp_lttpr_phy_cap(caps, DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1);
+
+       return txcap & DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED;
+}
+EXPORT_SYMBOL(drm_dp_lttpr_pre_emphasis_level_3_supported);
+
+/**
+ * drm_dp_get_phy_test_pattern() - get the requested pattern from the sink.
+ * @aux: DisplayPort AUX channel
+ * @data: DP phy compliance test parameters.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_get_phy_test_pattern(struct drm_dp_aux *aux,
+                               struct drm_dp_phy_test_params *data)
+{
+       int err;
+       u8 rate, lanes;
+
+       err = drm_dp_dpcd_readb(aux, DP_TEST_LINK_RATE, &rate);
+       if (err < 0)
+               return err;
+       data->link_rate = drm_dp_bw_code_to_link_rate(rate);
+
+       err = drm_dp_dpcd_readb(aux, DP_TEST_LANE_COUNT, &lanes);
+       if (err < 0)
+               return err;
+       data->num_lanes = lanes & DP_MAX_LANE_COUNT_MASK;
+
+       if (lanes & DP_ENHANCED_FRAME_CAP)
+               data->enhanced_frame_cap = true;
+
+       err = drm_dp_dpcd_readb(aux, DP_PHY_TEST_PATTERN, &data->phy_pattern);
+       if (err < 0)
+               return err;
+
+       switch (data->phy_pattern) {
+       case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
+               err = drm_dp_dpcd_read(aux, DP_TEST_80BIT_CUSTOM_PATTERN_7_0,
+                                      &data->custom80, sizeof(data->custom80));
+               if (err < 0)
+                       return err;
+
+               break;
+       case DP_PHY_TEST_PATTERN_CP2520:
+               err = drm_dp_dpcd_read(aux, DP_TEST_HBR2_SCRAMBLER_RESET,
+                                      &data->hbr2_reset,
+                                      sizeof(data->hbr2_reset));
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_get_phy_test_pattern);
+
+/**
+ * drm_dp_set_phy_test_pattern() - set the pattern to the sink.
+ * @aux: DisplayPort AUX channel
+ * @data: DP phy compliance test parameters.
+ * @dp_rev: DP revision to use for compliance testing
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_set_phy_test_pattern(struct drm_dp_aux *aux,
+                               struct drm_dp_phy_test_params *data, u8 dp_rev)
+{
+       int err, i;
+       u8 link_config[2];
+       u8 test_pattern;
+
+       link_config[0] = drm_dp_link_rate_to_bw_code(data->link_rate);
+       link_config[1] = data->num_lanes;
+       if (data->enhanced_frame_cap)
+               link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+       err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, link_config, 2);
+       if (err < 0)
+               return err;
+
+       test_pattern = data->phy_pattern;
+       if (dp_rev < 0x12) {
+               test_pattern = (test_pattern << 2) &
+                              DP_LINK_QUAL_PATTERN_11_MASK;
+               err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET,
+                                        test_pattern);
+               if (err < 0)
+                       return err;
+       } else {
+               for (i = 0; i < data->num_lanes; i++) {
+                       err = drm_dp_dpcd_writeb(aux,
+                                                DP_LINK_QUAL_LANE0_SET + i,
+                                                test_pattern);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_set_phy_test_pattern);
+
+static const char *dp_pixelformat_get_name(enum dp_pixelformat pixelformat)
+{
+       if (pixelformat < 0 || pixelformat > DP_PIXELFORMAT_RESERVED)
+               return "Invalid";
+
+       switch (pixelformat) {
+       case DP_PIXELFORMAT_RGB:
+               return "RGB";
+       case DP_PIXELFORMAT_YUV444:
+               return "YUV444";
+       case DP_PIXELFORMAT_YUV422:
+               return "YUV422";
+       case DP_PIXELFORMAT_YUV420:
+               return "YUV420";
+       case DP_PIXELFORMAT_Y_ONLY:
+               return "Y_ONLY";
+       case DP_PIXELFORMAT_RAW:
+               return "RAW";
+       default:
+               return "Reserved";
+       }
+}
+
+static const char *dp_colorimetry_get_name(enum dp_pixelformat pixelformat,
+                                          enum dp_colorimetry colorimetry)
+{
+       if (pixelformat < 0 || pixelformat > DP_PIXELFORMAT_RESERVED)
+               return "Invalid";
+
+       switch (colorimetry) {
+       case DP_COLORIMETRY_DEFAULT:
+               switch (pixelformat) {
+               case DP_PIXELFORMAT_RGB:
+                       return "sRGB";
+               case DP_PIXELFORMAT_YUV444:
+               case DP_PIXELFORMAT_YUV422:
+               case DP_PIXELFORMAT_YUV420:
+                       return "BT.601";
+               case DP_PIXELFORMAT_Y_ONLY:
+                       return "DICOM PS3.14";
+               case DP_PIXELFORMAT_RAW:
+                       return "Custom Color Profile";
+               default:
+                       return "Reserved";
+               }
+       case DP_COLORIMETRY_RGB_WIDE_FIXED: /* and DP_COLORIMETRY_BT709_YCC */
+               switch (pixelformat) {
+               case DP_PIXELFORMAT_RGB:
+                       return "Wide Fixed";
+               case DP_PIXELFORMAT_YUV444:
+               case DP_PIXELFORMAT_YUV422:
+               case DP_PIXELFORMAT_YUV420:
+                       return "BT.709";
+               default:
+                       return "Reserved";
+               }
+       case DP_COLORIMETRY_RGB_WIDE_FLOAT: /* and DP_COLORIMETRY_XVYCC_601 */
+               switch (pixelformat) {
+               case DP_PIXELFORMAT_RGB:
+                       return "Wide Float";
+               case DP_PIXELFORMAT_YUV444:
+               case DP_PIXELFORMAT_YUV422:
+               case DP_PIXELFORMAT_YUV420:
+                       return "xvYCC 601";
+               default:
+                       return "Reserved";
+               }
+       case DP_COLORIMETRY_OPRGB: /* and DP_COLORIMETRY_XVYCC_709 */
+               switch (pixelformat) {
+               case DP_PIXELFORMAT_RGB:
+                       return "OpRGB";
+               case DP_PIXELFORMAT_YUV444:
+               case DP_PIXELFORMAT_YUV422:
+               case DP_PIXELFORMAT_YUV420:
+                       return "xvYCC 709";
+               default:
+                       return "Reserved";
+               }
+       case DP_COLORIMETRY_DCI_P3_RGB: /* and DP_COLORIMETRY_SYCC_601 */
+               switch (pixelformat) {
+               case DP_PIXELFORMAT_RGB:
+                       return "DCI-P3";
+               case DP_PIXELFORMAT_YUV444:
+               case DP_PIXELFORMAT_YUV422:
+               case DP_PIXELFORMAT_YUV420:
+                       return "sYCC 601";
+               default:
+                       return "Reserved";
+               }
+       case DP_COLORIMETRY_RGB_CUSTOM: /* and DP_COLORIMETRY_OPYCC_601 */
+               switch (pixelformat) {
+               case DP_PIXELFORMAT_RGB:
+                       return "Custom Profile";
+               case DP_PIXELFORMAT_YUV444:
+               case DP_PIXELFORMAT_YUV422:
+               case DP_PIXELFORMAT_YUV420:
+                       return "OpYCC 601";
+               default:
+                       return "Reserved";
+               }
+       case DP_COLORIMETRY_BT2020_RGB: /* and DP_COLORIMETRY_BT2020_CYCC */
+               switch (pixelformat) {
+               case DP_PIXELFORMAT_RGB:
+                       return "BT.2020 RGB";
+               case DP_PIXELFORMAT_YUV444:
+               case DP_PIXELFORMAT_YUV422:
+               case DP_PIXELFORMAT_YUV420:
+                       return "BT.2020 CYCC";
+               default:
+                       return "Reserved";
+               }
+       case DP_COLORIMETRY_BT2020_YCC:
+               switch (pixelformat) {
+               case DP_PIXELFORMAT_YUV444:
+               case DP_PIXELFORMAT_YUV422:
+               case DP_PIXELFORMAT_YUV420:
+                       return "BT.2020 YCC";
+               default:
+                       return "Reserved";
+               }
+       default:
+               return "Invalid";
+       }
+}
+
+static const char *dp_dynamic_range_get_name(enum dp_dynamic_range dynamic_range)
+{
+       switch (dynamic_range) {
+       case DP_DYNAMIC_RANGE_VESA:
+               return "VESA range";
+       case DP_DYNAMIC_RANGE_CTA:
+               return "CTA range";
+       default:
+               return "Invalid";
+       }
+}
+
+static const char *dp_content_type_get_name(enum dp_content_type content_type)
+{
+       switch (content_type) {
+       case DP_CONTENT_TYPE_NOT_DEFINED:
+               return "Not defined";
+       case DP_CONTENT_TYPE_GRAPHICS:
+               return "Graphics";
+       case DP_CONTENT_TYPE_PHOTO:
+               return "Photo";
+       case DP_CONTENT_TYPE_VIDEO:
+               return "Video";
+       case DP_CONTENT_TYPE_GAME:
+               return "Game";
+       default:
+               return "Reserved";
+       }
+}
+
+void drm_dp_vsc_sdp_log(const char *level, struct device *dev,
+                       const struct drm_dp_vsc_sdp *vsc)
+{
+#define DP_SDP_LOG(fmt, ...) dev_printk(level, dev, fmt, ##__VA_ARGS__)
+       DP_SDP_LOG("DP SDP: %s, revision %u, length %u\n", "VSC",
+                  vsc->revision, vsc->length);
+       DP_SDP_LOG("    pixelformat: %s\n",
+                  dp_pixelformat_get_name(vsc->pixelformat));
+       DP_SDP_LOG("    colorimetry: %s\n",
+                  dp_colorimetry_get_name(vsc->pixelformat, vsc->colorimetry));
+       DP_SDP_LOG("    bpc: %u\n", vsc->bpc);
+       DP_SDP_LOG("    dynamic range: %s\n",
+                  dp_dynamic_range_get_name(vsc->dynamic_range));
+       DP_SDP_LOG("    content type: %s\n",
+                  dp_content_type_get_name(vsc->content_type));
+#undef DP_SDP_LOG
+}
+EXPORT_SYMBOL(drm_dp_vsc_sdp_log);
+
+/**
+ * drm_dp_get_pcon_max_frl_bw() - maximum frl supported by PCON
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ *
+ * Returns maximum frl bandwidth supported by PCON in GBPS,
+ * returns 0 if not supported.
+ */
+int drm_dp_get_pcon_max_frl_bw(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                              const u8 port_cap[4])
+{
+       int bw;
+       u8 buf;
+
+       buf = port_cap[2];
+       bw = buf & DP_PCON_MAX_FRL_BW;
+
+       switch (bw) {
+       case DP_PCON_MAX_9GBPS:
+               return 9;
+       case DP_PCON_MAX_18GBPS:
+               return 18;
+       case DP_PCON_MAX_24GBPS:
+               return 24;
+       case DP_PCON_MAX_32GBPS:
+               return 32;
+       case DP_PCON_MAX_40GBPS:
+               return 40;
+       case DP_PCON_MAX_48GBPS:
+               return 48;
+       case DP_PCON_MAX_0GBPS:
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_get_pcon_max_frl_bw);
+
+/**
+ * drm_dp_pcon_frl_prepare() - Prepare PCON for FRL.
+ * @aux: DisplayPort AUX channel
+ * @enable_frl_ready_hpd: Configure DP_PCON_ENABLE_HPD_READY.
+ *
+ * Returns 0 if success, else returns negative error code.
+ */
+int drm_dp_pcon_frl_prepare(struct drm_dp_aux *aux, bool enable_frl_ready_hpd)
+{
+       int ret;
+       u8 buf = DP_PCON_ENABLE_SOURCE_CTL_MODE |
+                DP_PCON_ENABLE_LINK_FRL_MODE;
+
+       if (enable_frl_ready_hpd)
+               buf |= DP_PCON_ENABLE_HPD_READY;
+
+       ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, buf);
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_dp_pcon_frl_prepare);
+
+/**
+ * drm_dp_pcon_is_frl_ready() - Is PCON ready for FRL
+ * @aux: DisplayPort AUX channel
+ *
+ * Returns true if success, else returns false.
+ */
+bool drm_dp_pcon_is_frl_ready(struct drm_dp_aux *aux)
+{
+       int ret;
+       u8 buf;
+
+       ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_TX_LINK_STATUS, &buf);
+       if (ret < 0)
+               return false;
+
+       if (buf & DP_PCON_FRL_READY)
+               return true;
+
+       return false;
+}
+EXPORT_SYMBOL(drm_dp_pcon_is_frl_ready);
+
+/**
+ * drm_dp_pcon_frl_configure_1() - Set HDMI LINK Configuration-Step1
+ * @aux: DisplayPort AUX channel
+ * @max_frl_gbps: maximum frl bw to be configured between PCON and HDMI sink
+ * @frl_mode: FRL Training mode, it can be either Concurrent or Sequential.
+ * In Concurrent Mode, the FRL link bring up can be done along with
+ * DP Link training. In Sequential mode, the FRL link bring up is done prior to
+ * the DP Link training.
+ *
+ * Returns 0 if success, else returns negative error code.
+ */
+
+int drm_dp_pcon_frl_configure_1(struct drm_dp_aux *aux, int max_frl_gbps,
+                               u8 frl_mode)
+{
+       int ret;
+       u8 buf;
+
+       ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_LINK_CONFIG_1, &buf);
+       if (ret < 0)
+               return ret;
+
+       if (frl_mode == DP_PCON_ENABLE_CONCURRENT_LINK)
+               buf |= DP_PCON_ENABLE_CONCURRENT_LINK;
+       else
+               buf &= ~DP_PCON_ENABLE_CONCURRENT_LINK;
+
+       switch (max_frl_gbps) {
+       case 9:
+               buf |=  DP_PCON_ENABLE_MAX_BW_9GBPS;
+               break;
+       case 18:
+               buf |=  DP_PCON_ENABLE_MAX_BW_18GBPS;
+               break;
+       case 24:
+               buf |=  DP_PCON_ENABLE_MAX_BW_24GBPS;
+               break;
+       case 32:
+               buf |=  DP_PCON_ENABLE_MAX_BW_32GBPS;
+               break;
+       case 40:
+               buf |=  DP_PCON_ENABLE_MAX_BW_40GBPS;
+               break;
+       case 48:
+               buf |=  DP_PCON_ENABLE_MAX_BW_48GBPS;
+               break;
+       case 0:
+               buf |=  DP_PCON_ENABLE_MAX_BW_0GBPS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, buf);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_pcon_frl_configure_1);
+
+/**
+ * drm_dp_pcon_frl_configure_2() - Set HDMI Link configuration Step-2
+ * @aux: DisplayPort AUX channel
+ * @max_frl_mask : Max FRL BW to be tried by the PCON with HDMI Sink
+ * @frl_type : FRL training type, can be Extended, or Normal.
+ * In Normal FRL training, the PCON tries each frl bw from the max_frl_mask
+ * starting from min, and stops when link training is successful. In Extended
+ * FRL training, all frl bw selected in the mask are trained by the PCON.
+ *
+ * Returns 0 if success, else returns negative error code.
+ */
+int drm_dp_pcon_frl_configure_2(struct drm_dp_aux *aux, int max_frl_mask,
+                               u8 frl_type)
+{
+       int ret;
+       u8 buf = max_frl_mask;
+
+       if (frl_type == DP_PCON_FRL_LINK_TRAIN_EXTENDED)
+               buf |= DP_PCON_FRL_LINK_TRAIN_EXTENDED;
+       else
+               buf &= ~DP_PCON_FRL_LINK_TRAIN_EXTENDED;
+
+       ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_2, buf);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_pcon_frl_configure_2);
+
+/**
+ * drm_dp_pcon_reset_frl_config() - Re-Set HDMI Link configuration.
+ * @aux: DisplayPort AUX channel
+ *
+ * Returns 0 if success, else returns negative error code.
+ */
+int drm_dp_pcon_reset_frl_config(struct drm_dp_aux *aux)
+{
+       int ret;
+
+       ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, 0x0);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_pcon_reset_frl_config);
+
+/**
+ * drm_dp_pcon_frl_enable() - Enable HDMI link through FRL
+ * @aux: DisplayPort AUX channel
+ *
+ * Returns 0 if success, else returns negative error code.
+ */
+int drm_dp_pcon_frl_enable(struct drm_dp_aux *aux)
+{
+       int ret;
+       u8 buf = 0;
+
+       ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_LINK_CONFIG_1, &buf);
+       if (ret < 0)
+               return ret;
+       if (!(buf & DP_PCON_ENABLE_SOURCE_CTL_MODE)) {
+               drm_dbg_kms(aux->drm_dev, "%s: PCON in Autonomous mode, can't enable FRL\n",
+                           aux->name);
+               return -EINVAL;
+       }
+       buf |= DP_PCON_ENABLE_HDMI_LINK;
+       ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, buf);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_pcon_frl_enable);
+
+/**
+ * drm_dp_pcon_hdmi_link_active() - check if the PCON HDMI LINK status is active.
+ * @aux: DisplayPort AUX channel
+ *
+ * Returns true if link is active else returns false.
+ */
+bool drm_dp_pcon_hdmi_link_active(struct drm_dp_aux *aux)
+{
+       u8 buf;
+       int ret;
+
+       ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_TX_LINK_STATUS, &buf);
+       if (ret < 0)
+               return false;
+
+       return buf & DP_PCON_HDMI_TX_LINK_ACTIVE;
+}
+EXPORT_SYMBOL(drm_dp_pcon_hdmi_link_active);
+
+/**
+ * drm_dp_pcon_hdmi_link_mode() - get the PCON HDMI LINK MODE
+ * @aux: DisplayPort AUX channel
+ * @frl_trained_mask: pointer to store bitmask of the trained bw configuration.
+ * Valid only if the MODE returned is FRL. For Normal Link training mode
+ * only 1 of the bits will be set, but in case of Extended mode, more than
+ * one bits can be set.
+ *
+ * Returns the link mode : TMDS or FRL on success, else returns negative error
+ * code.
+ */
+int drm_dp_pcon_hdmi_link_mode(struct drm_dp_aux *aux, u8 *frl_trained_mask)
+{
+       u8 buf;
+       int mode;
+       int ret;
+
+       ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_POST_FRL_STATUS, &buf);
+       if (ret < 0)
+               return ret;
+
+       mode = buf & DP_PCON_HDMI_LINK_MODE;
+
+       if (frl_trained_mask && DP_PCON_HDMI_MODE_FRL == mode)
+               *frl_trained_mask = (buf & DP_PCON_HDMI_FRL_TRAINED_BW) >> 1;
+
+       return mode;
+}
+EXPORT_SYMBOL(drm_dp_pcon_hdmi_link_mode);
+
+/**
+ * drm_dp_pcon_hdmi_frl_link_error_count() - print the error count per lane
+ * during link failure between PCON and HDMI sink
+ * @aux: DisplayPort AUX channel
+ * @connector: DRM connector
+ * code.
+ **/
+
+void drm_dp_pcon_hdmi_frl_link_error_count(struct drm_dp_aux *aux,
+                                          struct drm_connector *connector)
+{
+       u8 buf, error_count;
+       int i, num_error;
+       struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
+
+       for (i = 0; i < hdmi->max_lanes; i++) {
+               if (drm_dp_dpcd_readb(aux, DP_PCON_HDMI_ERROR_STATUS_LN0 + i, &buf) < 0)
+                       return;
+
+               error_count = buf & DP_PCON_HDMI_ERROR_COUNT_MASK;
+               switch (error_count) {
+               case DP_PCON_HDMI_ERROR_COUNT_HUNDRED_PLUS:
+                       num_error = 100;
+                       break;
+               case DP_PCON_HDMI_ERROR_COUNT_TEN_PLUS:
+                       num_error = 10;
+                       break;
+               case DP_PCON_HDMI_ERROR_COUNT_THREE_PLUS:
+                       num_error = 3;
+                       break;
+               default:
+                       num_error = 0;
+               }
+
+               drm_err(aux->drm_dev, "%s: More than %d errors since the last read for lane %d",
+                       aux->name, num_error, i);
+       }
+}
+EXPORT_SYMBOL(drm_dp_pcon_hdmi_frl_link_error_count);
+
+/*
+ * drm_dp_pcon_enc_is_dsc_1_2 - Does PCON Encoder supports DSC 1.2
+ * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder
+ *
+ * Returns true is PCON encoder is DSC 1.2 else returns false.
+ */
+bool drm_dp_pcon_enc_is_dsc_1_2(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE])
+{
+       u8 buf;
+       u8 major_v, minor_v;
+
+       buf = pcon_dsc_dpcd[DP_PCON_DSC_VERSION - DP_PCON_DSC_ENCODER];
+       major_v = (buf & DP_PCON_DSC_MAJOR_MASK) >> DP_PCON_DSC_MAJOR_SHIFT;
+       minor_v = (buf & DP_PCON_DSC_MINOR_MASK) >> DP_PCON_DSC_MINOR_SHIFT;
+
+       if (major_v == 1 && minor_v == 2)
+               return true;
+
+       return false;
+}
+EXPORT_SYMBOL(drm_dp_pcon_enc_is_dsc_1_2);
+
+/*
+ * drm_dp_pcon_dsc_max_slices - Get max slices supported by PCON DSC Encoder
+ * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder
+ *
+ * Returns maximum no. of slices supported by the PCON DSC Encoder.
+ */
+int drm_dp_pcon_dsc_max_slices(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE])
+{
+       u8 slice_cap1, slice_cap2;
+
+       slice_cap1 = pcon_dsc_dpcd[DP_PCON_DSC_SLICE_CAP_1 - DP_PCON_DSC_ENCODER];
+       slice_cap2 = pcon_dsc_dpcd[DP_PCON_DSC_SLICE_CAP_2 - DP_PCON_DSC_ENCODER];
+
+       if (slice_cap2 & DP_PCON_DSC_24_PER_DSC_ENC)
+               return 24;
+       if (slice_cap2 & DP_PCON_DSC_20_PER_DSC_ENC)
+               return 20;
+       if (slice_cap2 & DP_PCON_DSC_16_PER_DSC_ENC)
+               return 16;
+       if (slice_cap1 & DP_PCON_DSC_12_PER_DSC_ENC)
+               return 12;
+       if (slice_cap1 & DP_PCON_DSC_10_PER_DSC_ENC)
+               return 10;
+       if (slice_cap1 & DP_PCON_DSC_8_PER_DSC_ENC)
+               return 8;
+       if (slice_cap1 & DP_PCON_DSC_6_PER_DSC_ENC)
+               return 6;
+       if (slice_cap1 & DP_PCON_DSC_4_PER_DSC_ENC)
+               return 4;
+       if (slice_cap1 & DP_PCON_DSC_2_PER_DSC_ENC)
+               return 2;
+       if (slice_cap1 & DP_PCON_DSC_1_PER_DSC_ENC)
+               return 1;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_pcon_dsc_max_slices);
+
+/*
+ * drm_dp_pcon_dsc_max_slice_width() - Get max slice width for Pcon DSC encoder
+ * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder
+ *
+ * Returns maximum width of the slices in pixel width i.e. no. of pixels x 320.
+ */
+int drm_dp_pcon_dsc_max_slice_width(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE])
+{
+       u8 buf;
+
+       buf = pcon_dsc_dpcd[DP_PCON_DSC_MAX_SLICE_WIDTH - DP_PCON_DSC_ENCODER];
+
+       return buf * DP_DSC_SLICE_WIDTH_MULTIPLIER;
+}
+EXPORT_SYMBOL(drm_dp_pcon_dsc_max_slice_width);
+
+/*
+ * drm_dp_pcon_dsc_bpp_incr() - Get bits per pixel increment for PCON DSC encoder
+ * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder
+ *
+ * Returns the bpp precision supported by the PCON encoder.
+ */
+int drm_dp_pcon_dsc_bpp_incr(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE])
+{
+       u8 buf;
+
+       buf = pcon_dsc_dpcd[DP_PCON_DSC_BPP_INCR - DP_PCON_DSC_ENCODER];
+
+       switch (buf & DP_PCON_DSC_BPP_INCR_MASK) {
+       case DP_PCON_DSC_ONE_16TH_BPP:
+               return 16;
+       case DP_PCON_DSC_ONE_8TH_BPP:
+               return 8;
+       case DP_PCON_DSC_ONE_4TH_BPP:
+               return 4;
+       case DP_PCON_DSC_ONE_HALF_BPP:
+               return 2;
+       case DP_PCON_DSC_ONE_BPP:
+               return 1;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_pcon_dsc_bpp_incr);
+
+static
+int drm_dp_pcon_configure_dsc_enc(struct drm_dp_aux *aux, u8 pps_buf_config)
+{
+       u8 buf;
+       int ret;
+
+       ret = drm_dp_dpcd_readb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, &buf);
+       if (ret < 0)
+               return ret;
+
+       buf |= DP_PCON_ENABLE_DSC_ENCODER;
+
+       if (pps_buf_config <= DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER) {
+               buf &= ~DP_PCON_ENCODER_PPS_OVERRIDE_MASK;
+               buf |= pps_buf_config << 2;
+       }
+
+       ret = drm_dp_dpcd_writeb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, buf);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/**
+ * drm_dp_pcon_pps_default() - Let PCON fill the default pps parameters
+ * for DSC1.2 between PCON & HDMI2.1 sink
+ * @aux: DisplayPort AUX channel
+ *
+ * Returns 0 on success, else returns negative error code.
+ */
+int drm_dp_pcon_pps_default(struct drm_dp_aux *aux)
+{
+       int ret;
+
+       ret = drm_dp_pcon_configure_dsc_enc(aux, DP_PCON_ENC_PPS_OVERRIDE_DISABLED);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_pcon_pps_default);
+
+/**
+ * drm_dp_pcon_pps_override_buf() - Configure PPS encoder override buffer for
+ * HDMI sink
+ * @aux: DisplayPort AUX channel
+ * @pps_buf: 128 bytes to be written into PPS buffer for HDMI sink by PCON.
+ *
+ * Returns 0 on success, else returns negative error code.
+ */
+int drm_dp_pcon_pps_override_buf(struct drm_dp_aux *aux, u8 pps_buf[128])
+{
+       int ret;
+
+       ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVERRIDE_BASE, &pps_buf, 128);
+       if (ret < 0)
+               return ret;
+
+       ret = drm_dp_pcon_configure_dsc_enc(aux, DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_pcon_pps_override_buf);
+
+/*
+ * drm_dp_pcon_pps_override_param() - Write PPS parameters to DSC encoder
+ * override registers
+ * @aux: DisplayPort AUX channel
+ * @pps_param: 3 Parameters (2 Bytes each) : Slice Width, Slice Height,
+ * bits_per_pixel.
+ *
+ * Returns 0 on success, else returns negative error code.
+ */
+int drm_dp_pcon_pps_override_param(struct drm_dp_aux *aux, u8 pps_param[6])
+{
+       int ret;
+
+       ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVRD_SLICE_HEIGHT, &pps_param[0], 2);
+       if (ret < 0)
+               return ret;
+       ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVRD_SLICE_WIDTH, &pps_param[2], 2);
+       if (ret < 0)
+               return ret;
+       ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVRD_BPP, &pps_param[4], 2);
+       if (ret < 0)
+               return ret;
+
+       ret = drm_dp_pcon_configure_dsc_enc(aux, DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_pcon_pps_override_param);
+
+/*
+ * drm_dp_pcon_convert_rgb_to_ycbcr() - Configure the PCon to convert RGB to Ycbcr
+ * @aux: displayPort AUX channel
+ * @color_spc: Color-space/s for which conversion is to be enabled, 0 for disable.
+ *
+ * Returns 0 on success, else returns negative error code.
+ */
+int drm_dp_pcon_convert_rgb_to_ycbcr(struct drm_dp_aux *aux, u8 color_spc)
+{
+       int ret;
+       u8 buf;
+
+       ret = drm_dp_dpcd_readb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, &buf);
+       if (ret < 0)
+               return ret;
+
+       if (color_spc & DP_CONVERSION_RGB_YCBCR_MASK)
+               buf |= (color_spc & DP_CONVERSION_RGB_YCBCR_MASK);
+       else
+               buf &= ~DP_CONVERSION_RGB_YCBCR_MASK;
+
+       ret = drm_dp_dpcd_writeb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, buf);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_pcon_convert_rgb_to_ycbcr);
+
+/**
+ * drm_edp_backlight_set_level() - Set the backlight level of an eDP panel via AUX
+ * @aux: The DP AUX channel to use
+ * @bl: Backlight capability info from drm_edp_backlight_init()
+ * @level: The brightness level to set
+ *
+ * Sets the brightness level of an eDP panel's backlight. Note that the panel's backlight must
+ * already have been enabled by the driver by calling drm_edp_backlight_enable().
+ *
+ * Returns: %0 on success, negative error code on failure
+ */
+int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
+                               u16 level)
+{
+       int ret;
+       u8 buf[2] = { 0 };
+
+       /* The panel uses the PWM for controlling brightness levels */
+       if (!bl->aux_set)
+               return 0;
+
+       if (bl->lsb_reg_used) {
+               buf[0] = (level & 0xff00) >> 8;
+               buf[1] = (level & 0x00ff);
+       } else {
+               buf[0] = level;
+       }
+
+       ret = drm_dp_dpcd_write(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, buf, sizeof(buf));
+       if (ret != sizeof(buf)) {
+               drm_err(aux->drm_dev,
+                       "%s: Failed to write aux backlight level: %d\n",
+                       aux->name, ret);
+               return ret < 0 ? ret : -EIO;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_edp_backlight_set_level);
+
+static int
+drm_edp_backlight_set_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
+                            bool enable)
+{
+       int ret;
+       u8 buf;
+
+       /* This panel uses the EDP_BL_PWR GPIO for enablement */
+       if (!bl->aux_enable)
+               return 0;
+
+       ret = drm_dp_dpcd_readb(aux, DP_EDP_DISPLAY_CONTROL_REGISTER, &buf);
+       if (ret != 1) {
+               drm_err(aux->drm_dev, "%s: Failed to read eDP display control register: %d\n",
+                       aux->name, ret);
+               return ret < 0 ? ret : -EIO;
+       }
+       if (enable)
+               buf |= DP_EDP_BACKLIGHT_ENABLE;
+       else
+               buf &= ~DP_EDP_BACKLIGHT_ENABLE;
+
+       ret = drm_dp_dpcd_writeb(aux, DP_EDP_DISPLAY_CONTROL_REGISTER, buf);
+       if (ret != 1) {
+               drm_err(aux->drm_dev, "%s: Failed to write eDP display control register: %d\n",
+                       aux->name, ret);
+               return ret < 0 ? ret : -EIO;
+       }
+
+       return 0;
+}
+
+/**
+ * drm_edp_backlight_enable() - Enable an eDP panel's backlight using DPCD
+ * @aux: The DP AUX channel to use
+ * @bl: Backlight capability info from drm_edp_backlight_init()
+ * @level: The initial backlight level to set via AUX, if there is one
+ *
+ * This function handles enabling DPCD backlight controls on a panel over DPCD, while additionally
+ * restoring any important backlight state such as the given backlight level, the brightness byte
+ * count, backlight frequency, etc.
+ *
+ * Note that certain panels do not support being enabled or disabled via DPCD, but instead require
+ * that the driver handle enabling/disabling the panel through implementation-specific means using
+ * the EDP_BL_PWR GPIO. For such panels, &drm_edp_backlight_info.aux_enable will be set to %false,
+ * this function becomes a no-op, and the driver is expected to handle powering the panel on using
+ * the EDP_BL_PWR GPIO.
+ *
+ * Returns: %0 on success, negative error code on failure.
+ */
+int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
+                            const u16 level)
+{
+       int ret;
+       u8 dpcd_buf;
+
+       if (bl->aux_set)
+               dpcd_buf = DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
+       else
+               dpcd_buf = DP_EDP_BACKLIGHT_CONTROL_MODE_PWM;
+
+       if (bl->pwmgen_bit_count) {
+               ret = drm_dp_dpcd_writeb(aux, DP_EDP_PWMGEN_BIT_COUNT, bl->pwmgen_bit_count);
+               if (ret != 1)
+                       drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n",
+                                   aux->name, ret);
+       }
+
+       if (bl->pwm_freq_pre_divider) {
+               ret = drm_dp_dpcd_writeb(aux, DP_EDP_BACKLIGHT_FREQ_SET, bl->pwm_freq_pre_divider);
+               if (ret != 1)
+                       drm_dbg_kms(aux->drm_dev,
+                                   "%s: Failed to write aux backlight frequency: %d\n",
+                                   aux->name, ret);
+               else
+                       dpcd_buf |= DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE;
+       }
+
+       ret = drm_dp_dpcd_writeb(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, dpcd_buf);
+       if (ret != 1) {
+               drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux backlight mode: %d\n",
+                           aux->name, ret);
+               return ret < 0 ? ret : -EIO;
+       }
+
+       ret = drm_edp_backlight_set_level(aux, bl, level);
+       if (ret < 0)
+               return ret;
+       ret = drm_edp_backlight_set_enable(aux, bl, true);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_edp_backlight_enable);
+
+/**
+ * drm_edp_backlight_disable() - Disable an eDP backlight using DPCD, if supported
+ * @aux: The DP AUX channel to use
+ * @bl: Backlight capability info from drm_edp_backlight_init()
+ *
+ * This function handles disabling DPCD backlight controls on a panel over AUX.
+ *
+ * Note that certain panels do not support being enabled or disabled via DPCD, but instead require
+ * that the driver handle enabling/disabling the panel through implementation-specific means using
+ * the EDP_BL_PWR GPIO. For such panels, &drm_edp_backlight_info.aux_enable will be set to %false,
+ * this function becomes a no-op, and the driver is expected to handle powering the panel off using
+ * the EDP_BL_PWR GPIO.
+ *
+ * Returns: %0 on success or no-op, negative error code on failure.
+ */
+int drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl)
+{
+       int ret;
+
+       ret = drm_edp_backlight_set_enable(aux, bl, false);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_edp_backlight_disable);
+
+static inline int
+drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
+                           u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE])
+{
+       int fxp, fxp_min, fxp_max, fxp_actual, f = 1;
+       int ret;
+       u8 pn, pn_min, pn_max;
+
+       if (!bl->aux_set)
+               return 0;
+
+       ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT, &pn);
+       if (ret != 1) {
+               drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap: %d\n",
+                           aux->name, ret);
+               return -ENODEV;
+       }
+
+       pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
+       bl->max = (1 << pn) - 1;
+       if (!driver_pwm_freq_hz)
+               return 0;
+
+       /*
+        * Set PWM Frequency divider to match desired frequency provided by the driver.
+        * The PWM Frequency is calculated as 27Mhz / (F x P).
+        * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of the
+        *             EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h)
+        * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the
+        *             EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h)
+        */
+
+       /* Find desired value of (F x P)
+        * Note that, if F x P is out of supported range, the maximum value or minimum value will
+        * applied automatically. So no need to check that.
+        */
+       fxp = DIV_ROUND_CLOSEST(1000 * DP_EDP_BACKLIGHT_FREQ_BASE_KHZ, driver_pwm_freq_hz);
+
+       /* Use highest possible value of Pn for more granularity of brightness adjustment while
+        * satisfying the conditions below.
+        * - Pn is in the range of Pn_min and Pn_max
+        * - F is in the range of 1 and 255
+        * - FxP is within 25% of desired value.
+        *   Note: 25% is arbitrary value and may need some tweak.
+        */
+       ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min);
+       if (ret != 1) {
+               drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n",
+                           aux->name, ret);
+               return 0;
+       }
+       ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max);
+       if (ret != 1) {
+               drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n",
+                           aux->name, ret);
+               return 0;
+       }
+       pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
+       pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
+
+       /* Ensure frequency is within 25% of desired value */
+       fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
+       fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
+       if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) {
+               drm_dbg_kms(aux->drm_dev,
+                           "%s: Driver defined backlight frequency (%d) out of range\n",
+                           aux->name, driver_pwm_freq_hz);
+               return 0;
+       }
+
+       for (pn = pn_max; pn >= pn_min; pn--) {
+               f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
+               fxp_actual = f << pn;
+               if (fxp_min <= fxp_actual && fxp_actual <= fxp_max)
+                       break;
+       }
+
+       ret = drm_dp_dpcd_writeb(aux, DP_EDP_PWMGEN_BIT_COUNT, pn);
+       if (ret != 1) {
+               drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n",
+                           aux->name, ret);
+               return 0;
+       }
+       bl->pwmgen_bit_count = pn;
+       bl->max = (1 << pn) - 1;
+
+       if (edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP) {
+               bl->pwm_freq_pre_divider = f;
+               drm_dbg_kms(aux->drm_dev, "%s: Using backlight frequency from driver (%dHz)\n",
+                           aux->name, driver_pwm_freq_hz);
+       }
+
+       return 0;
+}
+
+static inline int
+drm_edp_backlight_probe_state(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
+                             u8 *current_mode)
+{
+       int ret;
+       u8 buf[2];
+       u8 mode_reg;
+
+       ret = drm_dp_dpcd_readb(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &mode_reg);
+       if (ret != 1) {
+               drm_dbg_kms(aux->drm_dev, "%s: Failed to read backlight mode: %d\n",
+                           aux->name, ret);
+               return ret < 0 ? ret : -EIO;
+       }
+
+       *current_mode = (mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK);
+       if (!bl->aux_set)
+               return 0;
+
+       if (*current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
+               int size = 1 + bl->lsb_reg_used;
+
+               ret = drm_dp_dpcd_read(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, buf, size);
+               if (ret != size) {
+                       drm_dbg_kms(aux->drm_dev, "%s: Failed to read backlight level: %d\n",
+                                   aux->name, ret);
+                       return ret < 0 ? ret : -EIO;
+               }
+
+               if (bl->lsb_reg_used)
+                       return (buf[0] << 8) | buf[1];
+               else
+                       return buf[0];
+       }
+
+       /*
+        * If we're not in DPCD control mode yet, the programmed brightness value is meaningless and
+        * the driver should assume max brightness
+        */
+       return bl->max;
+}
+
+/**
+ * drm_edp_backlight_init() - Probe a display panel's TCON using the standard VESA eDP backlight
+ * interface.
+ * @aux: The DP aux device to use for probing
+ * @bl: The &drm_edp_backlight_info struct to fill out with information on the backlight
+ * @driver_pwm_freq_hz: Optional PWM frequency from the driver in hz
+ * @edp_dpcd: A cached copy of the eDP DPCD
+ * @current_level: Where to store the probed brightness level, if any
+ * @current_mode: Where to store the currently set backlight control mode
+ *
+ * Initializes a &drm_edp_backlight_info struct by probing @aux for it's backlight capabilities,
+ * along with also probing the current and maximum supported brightness levels.
+ *
+ * If @driver_pwm_freq_hz is non-zero, this will be used as the backlight frequency. Otherwise, the
+ * default frequency from the panel is used.
+ *
+ * Returns: %0 on success, negative error code on failure.
+ */
+int
+drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
+                      u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE],
+                      u16 *current_level, u8 *current_mode)
+{
+       int ret;
+
+       if (edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP)
+               bl->aux_enable = true;
+       if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)
+               bl->aux_set = true;
+       if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
+               bl->lsb_reg_used = true;
+
+       /* Sanity check caps */
+       if (!bl->aux_set && !(edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP)) {
+               drm_dbg_kms(aux->drm_dev,
+                           "%s: Panel supports neither AUX or PWM brightness control? Aborting\n",
+                           aux->name);
+               return -EINVAL;
+       }
+
+       ret = drm_edp_backlight_probe_max(aux, bl, driver_pwm_freq_hz, edp_dpcd);
+       if (ret < 0)
+               return ret;
+
+       ret = drm_edp_backlight_probe_state(aux, bl, current_mode);
+       if (ret < 0)
+               return ret;
+       *current_level = ret;
+
+       drm_dbg_kms(aux->drm_dev,
+                   "%s: Found backlight: aux_set=%d aux_enable=%d mode=%d\n",
+                   aux->name, bl->aux_set, bl->aux_enable, *current_mode);
+       if (bl->aux_set) {
+               drm_dbg_kms(aux->drm_dev,
+                           "%s: Backlight caps: level=%d/%d pwm_freq_pre_divider=%d lsb_reg_used=%d\n",
+                           aux->name, *current_level, bl->max, bl->pwm_freq_pre_divider,
+                           bl->lsb_reg_used);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_edp_backlight_init);
+
+#if IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
+       (IS_MODULE(CONFIG_DRM_KMS_HELPER) && IS_MODULE(CONFIG_BACKLIGHT_CLASS_DEVICE))
+
+static int dp_aux_backlight_update_status(struct backlight_device *bd)
+{
+       struct dp_aux_backlight *bl = bl_get_data(bd);
+       u16 brightness = backlight_get_brightness(bd);
+       int ret = 0;
+
+       if (!backlight_is_blank(bd)) {
+               if (!bl->enabled) {
+                       drm_edp_backlight_enable(bl->aux, &bl->info, brightness);
+                       bl->enabled = true;
+                       return 0;
+               }
+               ret = drm_edp_backlight_set_level(bl->aux, &bl->info, brightness);
+       } else {
+               if (bl->enabled) {
+                       drm_edp_backlight_disable(bl->aux, &bl->info);
+                       bl->enabled = false;
+               }
+       }
+
+       return ret;
+}
+
+static const struct backlight_ops dp_aux_bl_ops = {
+       .update_status = dp_aux_backlight_update_status,
+};
+
+/**
+ * drm_panel_dp_aux_backlight - create and use DP AUX backlight
+ * @panel: DRM panel
+ * @aux: The DP AUX channel to use
+ *
+ * Use this function to create and handle backlight if your panel
+ * supports backlight control over DP AUX channel using DPCD
+ * registers as per VESA's standard backlight control interface.
+ *
+ * When the panel is enabled backlight will be enabled after a
+ * successful call to &drm_panel_funcs.enable()
+ *
+ * When the panel is disabled backlight will be disabled before the
+ * call to &drm_panel_funcs.disable().
+ *
+ * A typical implementation for a panel driver supporting backlight
+ * control over DP AUX will call this function at probe time.
+ * Backlight will then be handled transparently without requiring
+ * any intervention from the driver.
+ *
+ * drm_panel_dp_aux_backlight() must be called after the call to drm_panel_init().
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_panel_dp_aux_backlight(struct drm_panel *panel, struct drm_dp_aux *aux)
+{
+       struct dp_aux_backlight *bl;
+       struct backlight_properties props = { 0 };
+       u16 current_level;
+       u8 current_mode;
+       u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];
+       int ret;
+
+       if (!panel || !panel->dev || !aux)
+               return -EINVAL;
+
+       ret = drm_dp_dpcd_read(aux, DP_EDP_DPCD_REV, edp_dpcd,
+                              EDP_DISPLAY_CTL_CAP_SIZE);
+       if (ret < 0)
+               return ret;
+
+       if (!drm_edp_backlight_supported(edp_dpcd)) {
+               DRM_DEV_INFO(panel->dev, "DP AUX backlight is not supported\n");
+               return 0;
+       }
+
+       bl = devm_kzalloc(panel->dev, sizeof(*bl), GFP_KERNEL);
+       if (!bl)
+               return -ENOMEM;
+
+       bl->aux = aux;
+
+       ret = drm_edp_backlight_init(aux, &bl->info, 0, edp_dpcd,
+                                    &current_level, &current_mode);
+       if (ret < 0)
+               return ret;
+
+       props.type = BACKLIGHT_RAW;
+       props.brightness = current_level;
+       props.max_brightness = bl->info.max;
+
+       bl->base = devm_backlight_device_register(panel->dev, "dp_aux_backlight",
+                                                 panel->dev, bl,
+                                                 &dp_aux_bl_ops, &props);
+       if (IS_ERR(bl->base))
+               return PTR_ERR(bl->base);
+
+       backlight_disable(bl->base);
+
+       panel->backlight = bl->base;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_panel_dp_aux_backlight);
+
+#endif
diff --git a/drivers/gpu/drm/dp/drm_dp_aux_bus.c b/drivers/gpu/drm/dp/drm_dp_aux_bus.c
new file mode 100644 (file)
index 0000000..415afce
--- /dev/null
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * The DP AUX bus is used for devices that are connected over a DisplayPort
+ * AUX bus. The devices on the far side of the bus are referred to as
+ * endpoints in this code.
+ *
+ * Commonly there is only one device connected to the DP AUX bus: a panel.
+ * Though historically panels (even DP panels) have been modeled as simple
+ * platform devices, putting them under the DP AUX bus allows the panel driver
+ * to perform transactions on that bus.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/dp/drm_dp_aux_bus.h>
+#include <drm/dp/drm_dp_helper.h>
+
+/**
+ * dp_aux_ep_match() - The match function for the dp_aux_bus.
+ * @dev: The device to match.
+ * @drv: The driver to try to match against.
+ *
+ * At the moment, we just match on device tree.
+ *
+ * Return: True if this driver matches this device; false otherwise.
+ */
+static int dp_aux_ep_match(struct device *dev, struct device_driver *drv)
+{
+       return !!of_match_device(drv->of_match_table, dev);
+}
+
+/**
+ * dp_aux_ep_probe() - The probe function for the dp_aux_bus.
+ * @dev: The device to probe.
+ *
+ * Calls through to the endpoint driver probe.
+ *
+ * Return: 0 if no error or negative error code.
+ */
+static int dp_aux_ep_probe(struct device *dev)
+{
+       struct dp_aux_ep_driver *aux_ep_drv = to_dp_aux_ep_drv(dev->driver);
+       struct dp_aux_ep_device *aux_ep = to_dp_aux_ep_dev(dev);
+       int ret;
+
+       ret = dev_pm_domain_attach(dev, true);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to attach to PM Domain\n");
+
+       ret = aux_ep_drv->probe(aux_ep);
+       if (ret)
+               dev_pm_domain_detach(dev, true);
+
+       return ret;
+}
+
+/**
+ * dp_aux_ep_remove() - The remove function for the dp_aux_bus.
+ * @dev: The device to remove.
+ *
+ * Calls through to the endpoint driver remove.
+ *
+ */
+static void dp_aux_ep_remove(struct device *dev)
+{
+       struct dp_aux_ep_driver *aux_ep_drv = to_dp_aux_ep_drv(dev->driver);
+       struct dp_aux_ep_device *aux_ep = to_dp_aux_ep_dev(dev);
+
+       if (aux_ep_drv->remove)
+               aux_ep_drv->remove(aux_ep);
+       dev_pm_domain_detach(dev, true);
+}
+
+/**
+ * dp_aux_ep_shutdown() - The shutdown function for the dp_aux_bus.
+ * @dev: The device to shutdown.
+ *
+ * Calls through to the endpoint driver shutdown.
+ */
+static void dp_aux_ep_shutdown(struct device *dev)
+{
+       struct dp_aux_ep_driver *aux_ep_drv;
+
+       if (!dev->driver)
+               return;
+
+       aux_ep_drv = to_dp_aux_ep_drv(dev->driver);
+       if (aux_ep_drv->shutdown)
+               aux_ep_drv->shutdown(to_dp_aux_ep_dev(dev));
+}
+
+static struct bus_type dp_aux_bus_type = {
+       .name           = "dp-aux",
+       .match          = dp_aux_ep_match,
+       .probe          = dp_aux_ep_probe,
+       .remove         = dp_aux_ep_remove,
+       .shutdown       = dp_aux_ep_shutdown,
+};
+
+static ssize_t modalias_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       return of_device_modalias(dev, buf, PAGE_SIZE);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *dp_aux_ep_dev_attrs[] = {
+       &dev_attr_modalias.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(dp_aux_ep_dev);
+
+/**
+ * dp_aux_ep_dev_release() - Free memory for the dp_aux_ep device
+ * @dev: The device to free.
+ *
+ * Return: 0 if no error or negative error code.
+ */
+static void dp_aux_ep_dev_release(struct device *dev)
+{
+       kfree(to_dp_aux_ep_dev(dev));
+}
+
+static struct device_type dp_aux_device_type_type = {
+       .groups         = dp_aux_ep_dev_groups,
+       .uevent         = of_device_uevent_modalias,
+       .release        = dp_aux_ep_dev_release,
+};
+
+/**
+ * of_dp_aux_ep_destroy() - Destroy an DP AUX endpoint device
+ * @dev: The device to destroy.
+ * @data: Not used
+ *
+ * This is just used as a callback by of_dp_aux_depopulate_ep_devices() and
+ * is called for _all_ of the child devices of the device providing the AUX bus.
+ * We'll only act on those that are of type "dp_aux_bus_type".
+ *
+ * This function is effectively an inverse of what's in the loop
+ * in of_dp_aux_populate_ep_devices().
+ *
+ * Return: 0 if no error or negative error code.
+ */
+static int of_dp_aux_ep_destroy(struct device *dev, void *data)
+{
+       struct device_node *np = dev->of_node;
+
+       if (dev->bus != &dp_aux_bus_type)
+               return 0;
+
+       if (!of_node_check_flag(np, OF_POPULATED))
+               return 0;
+
+       of_node_clear_flag(np, OF_POPULATED);
+       of_node_put(np);
+
+       device_unregister(dev);
+
+       return 0;
+}
+
+/**
+ * of_dp_aux_depopulate_ep_devices() - Undo of_dp_aux_populate_ep_devices
+ * @aux: The AUX channel whose devices we want to depopulate
+ *
+ * This will destroy all devices that were created
+ * by of_dp_aux_populate_ep_devices().
+ */
+void of_dp_aux_depopulate_ep_devices(struct drm_dp_aux *aux)
+{
+       device_for_each_child_reverse(aux->dev, NULL, of_dp_aux_ep_destroy);
+}
+EXPORT_SYMBOL_GPL(of_dp_aux_depopulate_ep_devices);
+
+/**
+ * of_dp_aux_populate_ep_devices() - Populate the endpoint devices on the DP AUX
+ * @aux: The AUX channel whose devices we want to populate. It is required that
+ *       drm_dp_aux_init() has already been called for this AUX channel.
+ *
+ * This will populate all the devices under the "aux-bus" node of the device
+ * providing the AUX channel (AKA aux->dev).
+ *
+ * When this function finishes, it is _possible_ (but not guaranteed) that
+ * our sub-devices will have finished probing. It should be noted that if our
+ * sub-devices return -EPROBE_DEFER that we will not return any error codes
+ * ourselves but our sub-devices will _not_ have actually probed successfully
+ * yet. There may be other cases (maybe added in the future?) where sub-devices
+ * won't have been probed yet when this function returns, so it's best not to
+ * rely on that.
+ *
+ * If this function succeeds you should later make sure you call
+ * of_dp_aux_depopulate_ep_devices() to undo it, or just use the devm version
+ * of this function.
+ *
+ * Return: 0 if no error or negative error code.
+ */
+int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
+{
+       struct device_node *bus, *np;
+       struct dp_aux_ep_device *aux_ep;
+       int ret;
+
+       /* drm_dp_aux_init() should have been called already; warn if not */
+       WARN_ON_ONCE(!aux->ddc.algo);
+
+       if (!aux->dev->of_node)
+               return 0;
+
+       bus = of_get_child_by_name(aux->dev->of_node, "aux-bus");
+       if (!bus)
+               return 0;
+
+       for_each_available_child_of_node(bus, np) {
+               if (of_node_test_and_set_flag(np, OF_POPULATED))
+                       continue;
+
+               aux_ep = kzalloc(sizeof(*aux_ep), GFP_KERNEL);
+               if (!aux_ep)
+                       continue;
+               aux_ep->aux = aux;
+
+               aux_ep->dev.parent = aux->dev;
+               aux_ep->dev.bus = &dp_aux_bus_type;
+               aux_ep->dev.type = &dp_aux_device_type_type;
+               aux_ep->dev.of_node = of_node_get(np);
+               dev_set_name(&aux_ep->dev, "aux-%s", dev_name(aux->dev));
+
+               ret = device_register(&aux_ep->dev);
+               if (ret) {
+                       dev_err(aux->dev, "Failed to create AUX EP for %pOF: %d\n", np, ret);
+                       of_node_clear_flag(np, OF_POPULATED);
+                       of_node_put(np);
+
+                       /*
+                        * As per docs of device_register(), call this instead
+                        * of kfree() directly for error cases.
+                        */
+                       put_device(&aux_ep->dev);
+
+                       /*
+                        * Following in the footsteps of of_i2c_register_devices(),
+                        * we won't fail the whole function here--we'll just
+                        * continue registering any other devices we find.
+                        */
+               }
+       }
+
+       of_node_put(bus);
+
+       return 0;
+}
+
+static void of_dp_aux_depopulate_ep_devices_void(void *data)
+{
+       of_dp_aux_depopulate_ep_devices(data);
+}
+
+/**
+ * devm_of_dp_aux_populate_ep_devices() - devm wrapper for of_dp_aux_populate_ep_devices()
+ * @aux: The AUX channel whose devices we want to populate
+ *
+ * Handles freeing w/ devm on the device "aux->dev".
+ *
+ * Return: 0 if no error or negative error code.
+ */
+int devm_of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
+{
+       int ret;
+
+       ret = of_dp_aux_populate_ep_devices(aux);
+       if (ret)
+               return ret;
+
+       return devm_add_action_or_reset(aux->dev,
+                                       of_dp_aux_depopulate_ep_devices_void,
+                                       aux);
+}
+EXPORT_SYMBOL_GPL(devm_of_dp_aux_populate_ep_devices);
+
+int __dp_aux_dp_driver_register(struct dp_aux_ep_driver *drv, struct module *owner)
+{
+       drv->driver.owner = owner;
+       drv->driver.bus = &dp_aux_bus_type;
+
+       return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(__dp_aux_dp_driver_register);
+
+void dp_aux_dp_driver_unregister(struct dp_aux_ep_driver *drv)
+{
+       driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(dp_aux_dp_driver_unregister);
+
+static int __init dp_aux_bus_init(void)
+{
+       int ret;
+
+       ret = bus_register(&dp_aux_bus_type);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void __exit dp_aux_bus_exit(void)
+{
+       bus_unregister(&dp_aux_bus_type);
+}
+
+subsys_initcall(dp_aux_bus_init);
+module_exit(dp_aux_bus_exit);
+
+MODULE_AUTHOR("Douglas Anderson <dianders@chromium.org>");
+MODULE_DESCRIPTION("DRM DisplayPort AUX bus");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/dp/drm_dp_aux_dev.c b/drivers/gpu/drm/dp/drm_dp_aux_dev.c
new file mode 100644 (file)
index 0000000..53ad4e7
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rafael Antognolli <rafael.antognolli@intel.com>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/dp/drm_dp_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
+#include <drm/drm_print.h>
+
+#include "drm_dp_helper_internal.h"
+
+struct drm_dp_aux_dev {
+       unsigned index;
+       struct drm_dp_aux *aux;
+       struct device *dev;
+       struct kref refcount;
+       atomic_t usecount;
+};
+
+#define DRM_AUX_MINORS 256
+#define AUX_MAX_OFFSET (1 << 20)
+static DEFINE_IDR(aux_idr);
+static DEFINE_MUTEX(aux_idr_mutex);
+static struct class *drm_dp_aux_dev_class;
+static int drm_dev_major = -1;
+
+static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_minor(unsigned index)
+{
+       struct drm_dp_aux_dev *aux_dev = NULL;
+
+       mutex_lock(&aux_idr_mutex);
+       aux_dev = idr_find(&aux_idr, index);
+       if (aux_dev && !kref_get_unless_zero(&aux_dev->refcount))
+               aux_dev = NULL;
+       mutex_unlock(&aux_idr_mutex);
+
+       return aux_dev;
+}
+
+static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux)
+{
+       struct drm_dp_aux_dev *aux_dev;
+       int index;
+
+       aux_dev = kzalloc(sizeof(*aux_dev), GFP_KERNEL);
+       if (!aux_dev)
+               return ERR_PTR(-ENOMEM);
+       aux_dev->aux = aux;
+       atomic_set(&aux_dev->usecount, 1);
+       kref_init(&aux_dev->refcount);
+
+       mutex_lock(&aux_idr_mutex);
+       index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL);
+       mutex_unlock(&aux_idr_mutex);
+       if (index < 0) {
+               kfree(aux_dev);
+               return ERR_PTR(index);
+       }
+       aux_dev->index = index;
+
+       return aux_dev;
+}
+
+static void release_drm_dp_aux_dev(struct kref *ref)
+{
+       struct drm_dp_aux_dev *aux_dev =
+               container_of(ref, struct drm_dp_aux_dev, refcount);
+
+       kfree(aux_dev);
+}
+
+static ssize_t name_show(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+       ssize_t res;
+       struct drm_dp_aux_dev *aux_dev =
+               drm_dp_aux_dev_get_by_minor(MINOR(dev->devt));
+
+       if (!aux_dev)
+               return -ENODEV;
+
+       res = sprintf(buf, "%s\n", aux_dev->aux->name);
+       kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
+
+       return res;
+}
+static DEVICE_ATTR_RO(name);
+
+static struct attribute *drm_dp_aux_attrs[] = {
+       &dev_attr_name.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(drm_dp_aux);
+
+static int auxdev_open(struct inode *inode, struct file *file)
+{
+       unsigned int minor = iminor(inode);
+       struct drm_dp_aux_dev *aux_dev;
+
+       aux_dev = drm_dp_aux_dev_get_by_minor(minor);
+       if (!aux_dev)
+               return -ENODEV;
+
+       file->private_data = aux_dev;
+       return 0;
+}
+
+static loff_t auxdev_llseek(struct file *file, loff_t offset, int whence)
+{
+       return fixed_size_llseek(file, offset, whence, AUX_MAX_OFFSET);
+}
+
+static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+       struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data;
+       loff_t pos = iocb->ki_pos;
+       ssize_t res = 0;
+
+       if (!atomic_inc_not_zero(&aux_dev->usecount))
+               return -ENODEV;
+
+       iov_iter_truncate(to, AUX_MAX_OFFSET - pos);
+
+       while (iov_iter_count(to)) {
+               uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES];
+               ssize_t todo = min(iov_iter_count(to), sizeof(buf));
+
+               if (signal_pending(current)) {
+                       res = -ERESTARTSYS;
+                       break;
+               }
+
+               res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
+
+               if (res <= 0)
+                       break;
+
+               if (copy_to_iter(buf, res, to) != res) {
+                       res = -EFAULT;
+                       break;
+               }
+
+               pos += res;
+       }
+
+       if (pos != iocb->ki_pos)
+               res = pos - iocb->ki_pos;
+       iocb->ki_pos = pos;
+
+       if (atomic_dec_and_test(&aux_dev->usecount))
+               wake_up_var(&aux_dev->usecount);
+
+       return res;
+}
+
+static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+       struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data;
+       loff_t pos = iocb->ki_pos;
+       ssize_t res = 0;
+
+       if (!atomic_inc_not_zero(&aux_dev->usecount))
+               return -ENODEV;
+
+       iov_iter_truncate(from, AUX_MAX_OFFSET - pos);
+
+       while (iov_iter_count(from)) {
+               uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES];
+               ssize_t todo = min(iov_iter_count(from), sizeof(buf));
+
+               if (signal_pending(current)) {
+                       res = -ERESTARTSYS;
+                       break;
+               }
+
+               if (!copy_from_iter_full(buf, todo, from)) {
+                       res = -EFAULT;
+                       break;
+               }
+
+               res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo);
+
+               if (res <= 0)
+                       break;
+
+               pos += res;
+       }
+
+       if (pos != iocb->ki_pos)
+               res = pos - iocb->ki_pos;
+       iocb->ki_pos = pos;
+
+       if (atomic_dec_and_test(&aux_dev->usecount))
+               wake_up_var(&aux_dev->usecount);
+
+       return res;
+}
+
+static int auxdev_release(struct inode *inode, struct file *file)
+{
+       struct drm_dp_aux_dev *aux_dev = file->private_data;
+
+       kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
+       return 0;
+}
+
+static const struct file_operations auxdev_fops = {
+       .owner          = THIS_MODULE,
+       .llseek         = auxdev_llseek,
+       .read_iter      = auxdev_read_iter,
+       .write_iter     = auxdev_write_iter,
+       .open           = auxdev_open,
+       .release        = auxdev_release,
+};
+
+#define to_auxdev(d) container_of(d, struct drm_dp_aux_dev, aux)
+
+static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_aux(struct drm_dp_aux *aux)
+{
+       struct drm_dp_aux_dev *iter, *aux_dev = NULL;
+       int id;
+
+       /* don't increase kref count here because this function should only be
+        * used by drm_dp_aux_unregister_devnode. Thus, it will always have at
+        * least one reference - the one that drm_dp_aux_register_devnode
+        * created
+        */
+       mutex_lock(&aux_idr_mutex);
+       idr_for_each_entry(&aux_idr, iter, id) {
+               if (iter->aux == aux) {
+                       aux_dev = iter;
+                       break;
+               }
+       }
+       mutex_unlock(&aux_idr_mutex);
+       return aux_dev;
+}
+
+void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
+{
+       struct drm_dp_aux_dev *aux_dev;
+       unsigned int minor;
+
+       aux_dev = drm_dp_aux_dev_get_by_aux(aux);
+       if (!aux_dev) /* attach must have failed */
+               return;
+
+       /*
+        * As some AUX adapters may exist as platform devices which outlive their respective DRM
+        * devices, we clear drm_dev to ensure that we never accidentally reference a stale pointer
+        */
+       aux->drm_dev = NULL;
+
+       mutex_lock(&aux_idr_mutex);
+       idr_remove(&aux_idr, aux_dev->index);
+       mutex_unlock(&aux_idr_mutex);
+
+       atomic_dec(&aux_dev->usecount);
+       wait_var_event(&aux_dev->usecount, !atomic_read(&aux_dev->usecount));
+
+       minor = aux_dev->index;
+       if (aux_dev->dev)
+               device_destroy(drm_dp_aux_dev_class,
+                              MKDEV(drm_dev_major, minor));
+
+       DRM_DEBUG("drm_dp_aux_dev: aux [%s] unregistering\n", aux->name);
+       kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
+}
+
+int drm_dp_aux_register_devnode(struct drm_dp_aux *aux)
+{
+       struct drm_dp_aux_dev *aux_dev;
+       int res;
+
+       aux_dev = alloc_drm_dp_aux_dev(aux);
+       if (IS_ERR(aux_dev))
+               return PTR_ERR(aux_dev);
+
+       aux_dev->dev = device_create(drm_dp_aux_dev_class, aux->dev,
+                                    MKDEV(drm_dev_major, aux_dev->index), NULL,
+                                    "drm_dp_aux%d", aux_dev->index);
+       if (IS_ERR(aux_dev->dev)) {
+               res = PTR_ERR(aux_dev->dev);
+               aux_dev->dev = NULL;
+               goto error;
+       }
+
+       DRM_DEBUG("drm_dp_aux_dev: aux [%s] registered as minor %d\n",
+                 aux->name, aux_dev->index);
+       return 0;
+error:
+       drm_dp_aux_unregister_devnode(aux);
+       return res;
+}
+
+int drm_dp_aux_dev_init(void)
+{
+       int res;
+
+       drm_dp_aux_dev_class = class_create(THIS_MODULE, "drm_dp_aux_dev");
+       if (IS_ERR(drm_dp_aux_dev_class)) {
+               return PTR_ERR(drm_dp_aux_dev_class);
+       }
+       drm_dp_aux_dev_class->dev_groups = drm_dp_aux_groups;
+
+       res = register_chrdev(0, "aux", &auxdev_fops);
+       if (res < 0)
+               goto out;
+       drm_dev_major = res;
+
+       return 0;
+out:
+       class_destroy(drm_dp_aux_dev_class);
+       return res;
+}
+
+void drm_dp_aux_dev_exit(void)
+{
+       unregister_chrdev(drm_dev_major, "aux");
+       class_destroy(drm_dp_aux_dev_class);
+}
diff --git a/drivers/gpu/drm/dp/drm_dp_cec.c b/drivers/gpu/drm/dp/drm_dp_cec.c
new file mode 100644 (file)
index 0000000..f9e9273
--- /dev/null
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DisplayPort CEC-Tunneling-over-AUX support
+ *
+ * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <media/cec.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_device.h>
+#include <drm/dp/drm_dp_helper.h>
+
+/*
+ * Unfortunately it turns out that we have a chicken-and-egg situation
+ * here. Quite a few active (mini-)DP-to-HDMI or USB-C-to-HDMI adapters
+ * have a converter chip that supports CEC-Tunneling-over-AUX (usually the
+ * Parade PS176), but they do not wire up the CEC pin, thus making CEC
+ * useless. Note that MegaChips 2900-based adapters appear to have good
+ * support for CEC tunneling. Those adapters that I have tested using
+ * this chipset all have the CEC line connected.
+ *
+ * Sadly there is no way for this driver to know this. What happens is
+ * that a /dev/cecX device is created that is isolated and unable to see
+ * any of the other CEC devices. Quite literally the CEC wire is cut
+ * (or in this case, never connected in the first place).
+ *
+ * The reason so few adapters support this is that this tunneling protocol
+ * was never supported by any OS. So there was no easy way of testing it,
+ * and no incentive to correctly wire up the CEC pin.
+ *
+ * Hopefully by creating this driver it will be easier for vendors to
+ * finally fix their adapters and test the CEC functionality.
+ *
+ * I keep a list of known working adapters here:
+ *
+ * https://hverkuil.home.xs4all.nl/cec-status.txt
+ *
+ * Please mail me (hverkuil@xs4all.nl) if you find an adapter that works
+ * and is not yet listed there.
+ *
+ * Note that the current implementation does not support CEC over an MST hub.
+ * As far as I can see there is no mechanism defined in the DisplayPort
+ * standard to transport CEC interrupts over an MST device. It might be
+ * possible to do this through polling, but I have not been able to get that
+ * to work.
+ */
+
+/**
+ * DOC: dp cec helpers
+ *
+ * These functions take care of supporting the CEC-Tunneling-over-AUX
+ * feature of DisplayPort-to-HDMI adapters.
+ */
+
+/*
+ * When the EDID is unset because the HPD went low, then the CEC DPCD registers
+ * typically can no longer be read (true for a DP-to-HDMI adapter since it is
+ * powered by the HPD). However, some displays toggle the HPD off and on for a
+ * short period for one reason or another, and that would cause the CEC adapter
+ * to be removed and added again, even though nothing else changed.
+ *
+ * This module parameter sets a delay in seconds before the CEC adapter is
+ * actually unregistered. Only if the HPD does not return within that time will
+ * the CEC adapter be unregistered.
+ *
+ * If it is set to a value >= NEVER_UNREG_DELAY, then the CEC adapter will never
+ * be unregistered for as long as the connector remains registered.
+ *
+ * If it is set to 0, then the CEC adapter will be unregistered immediately as
+ * soon as the HPD disappears.
+ *
+ * The default is one second to prevent short HPD glitches from unregistering
+ * the CEC adapter.
+ *
+ * Note that for integrated HDMI branch devices that support CEC the DPCD
+ * registers remain available even if the HPD goes low since it is not powered
+ * by the HPD. In that case the CEC adapter will never be unregistered during
+ * the life time of the connector. At least, this is the theory since I do not
+ * have hardware with an integrated HDMI branch device that supports CEC.
+ */
+#define NEVER_UNREG_DELAY 1000
+static unsigned int drm_dp_cec_unregister_delay = 1;
+module_param(drm_dp_cec_unregister_delay, uint, 0600);
+MODULE_PARM_DESC(drm_dp_cec_unregister_delay,
+                "CEC unregister delay in seconds, 0: no delay, >= 1000: never unregister");
+
+static int drm_dp_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+       struct drm_dp_aux *aux = cec_get_drvdata(adap);
+       u32 val = enable ? DP_CEC_TUNNELING_ENABLE : 0;
+       ssize_t err = 0;
+
+       err = drm_dp_dpcd_writeb(aux, DP_CEC_TUNNELING_CONTROL, val);
+       return (enable && err < 0) ? err : 0;
+}
+
+static int drm_dp_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+       struct drm_dp_aux *aux = cec_get_drvdata(adap);
+       /* Bit 15 (logical address 15) should always be set */
+       u16 la_mask = 1 << CEC_LOG_ADDR_BROADCAST;
+       u8 mask[2];
+       ssize_t err;
+
+       if (addr != CEC_LOG_ADDR_INVALID)
+               la_mask |= adap->log_addrs.log_addr_mask | (1 << addr);
+       mask[0] = la_mask & 0xff;
+       mask[1] = la_mask >> 8;
+       err = drm_dp_dpcd_write(aux, DP_CEC_LOGICAL_ADDRESS_MASK, mask, 2);
+       return (addr != CEC_LOG_ADDR_INVALID && err < 0) ? err : 0;
+}
+
+static int drm_dp_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+                                   u32 signal_free_time, struct cec_msg *msg)
+{
+       struct drm_dp_aux *aux = cec_get_drvdata(adap);
+       unsigned int retries = min(5, attempts - 1);
+       ssize_t err;
+
+       err = drm_dp_dpcd_write(aux, DP_CEC_TX_MESSAGE_BUFFER,
+                               msg->msg, msg->len);
+       if (err < 0)
+               return err;
+
+       err = drm_dp_dpcd_writeb(aux, DP_CEC_TX_MESSAGE_INFO,
+                                (msg->len - 1) | (retries << 4) |
+                                DP_CEC_TX_MESSAGE_SEND);
+       return err < 0 ? err : 0;
+}
+
+static int drm_dp_cec_adap_monitor_all_enable(struct cec_adapter *adap,
+                                             bool enable)
+{
+       struct drm_dp_aux *aux = cec_get_drvdata(adap);
+       ssize_t err;
+       u8 val;
+
+       if (!(adap->capabilities & CEC_CAP_MONITOR_ALL))
+               return 0;
+
+       err = drm_dp_dpcd_readb(aux, DP_CEC_TUNNELING_CONTROL, &val);
+       if (err >= 0) {
+               if (enable)
+                       val |= DP_CEC_SNOOPING_ENABLE;
+               else
+                       val &= ~DP_CEC_SNOOPING_ENABLE;
+               err = drm_dp_dpcd_writeb(aux, DP_CEC_TUNNELING_CONTROL, val);
+       }
+       return (enable && err < 0) ? err : 0;
+}
+
+static void drm_dp_cec_adap_status(struct cec_adapter *adap,
+                                  struct seq_file *file)
+{
+       struct drm_dp_aux *aux = cec_get_drvdata(adap);
+       struct drm_dp_desc desc;
+       struct drm_dp_dpcd_ident *id = &desc.ident;
+
+       if (drm_dp_read_desc(aux, &desc, true))
+               return;
+       seq_printf(file, "OUI: %*phD\n",
+                  (int)sizeof(id->oui), id->oui);
+       seq_printf(file, "ID: %*pE\n",
+                  (int)strnlen(id->device_id, sizeof(id->device_id)),
+                  id->device_id);
+       seq_printf(file, "HW Rev: %d.%d\n", id->hw_rev >> 4, id->hw_rev & 0xf);
+       /*
+        * Show this both in decimal and hex: at least one vendor
+        * always reports this in hex.
+        */
+       seq_printf(file, "FW/SW Rev: %d.%d (0x%02x.0x%02x)\n",
+                  id->sw_major_rev, id->sw_minor_rev,
+                  id->sw_major_rev, id->sw_minor_rev);
+}
+
+static const struct cec_adap_ops drm_dp_cec_adap_ops = {
+       .adap_enable = drm_dp_cec_adap_enable,
+       .adap_log_addr = drm_dp_cec_adap_log_addr,
+       .adap_transmit = drm_dp_cec_adap_transmit,
+       .adap_monitor_all_enable = drm_dp_cec_adap_monitor_all_enable,
+       .adap_status = drm_dp_cec_adap_status,
+};
+
+static int drm_dp_cec_received(struct drm_dp_aux *aux)
+{
+       struct cec_adapter *adap = aux->cec.adap;
+       struct cec_msg msg;
+       u8 rx_msg_info;
+       ssize_t err;
+
+       err = drm_dp_dpcd_readb(aux, DP_CEC_RX_MESSAGE_INFO, &rx_msg_info);
+       if (err < 0)
+               return err;
+
+       if (!(rx_msg_info & DP_CEC_RX_MESSAGE_ENDED))
+               return 0;
+
+       msg.len = (rx_msg_info & DP_CEC_RX_MESSAGE_LEN_MASK) + 1;
+       err = drm_dp_dpcd_read(aux, DP_CEC_RX_MESSAGE_BUFFER, msg.msg, msg.len);
+       if (err < 0)
+               return err;
+
+       cec_received_msg(adap, &msg);
+       return 0;
+}
+
+static void drm_dp_cec_handle_irq(struct drm_dp_aux *aux)
+{
+       struct cec_adapter *adap = aux->cec.adap;
+       u8 flags;
+
+       if (drm_dp_dpcd_readb(aux, DP_CEC_TUNNELING_IRQ_FLAGS, &flags) < 0)
+               return;
+
+       if (flags & DP_CEC_RX_MESSAGE_INFO_VALID)
+               drm_dp_cec_received(aux);
+
+       if (flags & DP_CEC_TX_MESSAGE_SENT)
+               cec_transmit_attempt_done(adap, CEC_TX_STATUS_OK);
+       else if (flags & DP_CEC_TX_LINE_ERROR)
+               cec_transmit_attempt_done(adap, CEC_TX_STATUS_ERROR |
+                                               CEC_TX_STATUS_MAX_RETRIES);
+       else if (flags &
+                (DP_CEC_TX_ADDRESS_NACK_ERROR | DP_CEC_TX_DATA_NACK_ERROR))
+               cec_transmit_attempt_done(adap, CEC_TX_STATUS_NACK |
+                                               CEC_TX_STATUS_MAX_RETRIES);
+       drm_dp_dpcd_writeb(aux, DP_CEC_TUNNELING_IRQ_FLAGS, flags);
+}
+
+/**
+ * drm_dp_cec_irq() - handle CEC interrupt, if any
+ * @aux: DisplayPort AUX channel
+ *
+ * Should be called when handling an IRQ_HPD request. If CEC-tunneling-over-AUX
+ * is present, then it will check for a CEC_IRQ and handle it accordingly.
+ */
+void drm_dp_cec_irq(struct drm_dp_aux *aux)
+{
+       u8 cec_irq;
+       int ret;
+
+       /* No transfer function was set, so not a DP connector */
+       if (!aux->transfer)
+               return;
+
+       mutex_lock(&aux->cec.lock);
+       if (!aux->cec.adap)
+               goto unlock;
+
+       ret = drm_dp_dpcd_readb(aux, DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1,
+                               &cec_irq);
+       if (ret < 0 || !(cec_irq & DP_CEC_IRQ))
+               goto unlock;
+
+       drm_dp_cec_handle_irq(aux);
+       drm_dp_dpcd_writeb(aux, DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1, DP_CEC_IRQ);
+unlock:
+       mutex_unlock(&aux->cec.lock);
+}
+EXPORT_SYMBOL(drm_dp_cec_irq);
+
+static bool drm_dp_cec_cap(struct drm_dp_aux *aux, u8 *cec_cap)
+{
+       u8 cap = 0;
+
+       if (drm_dp_dpcd_readb(aux, DP_CEC_TUNNELING_CAPABILITY, &cap) != 1 ||
+           !(cap & DP_CEC_TUNNELING_CAPABLE))
+               return false;
+       if (cec_cap)
+               *cec_cap = cap;
+       return true;
+}
+
+/*
+ * Called if the HPD was low for more than drm_dp_cec_unregister_delay
+ * seconds. This unregisters the CEC adapter.
+ */
+static void drm_dp_cec_unregister_work(struct work_struct *work)
+{
+       struct drm_dp_aux *aux = container_of(work, struct drm_dp_aux,
+                                             cec.unregister_work.work);
+
+       mutex_lock(&aux->cec.lock);
+       cec_unregister_adapter(aux->cec.adap);
+       aux->cec.adap = NULL;
+       mutex_unlock(&aux->cec.lock);
+}
+
+/*
+ * A new EDID is set. If there is no CEC adapter, then create one. If
+ * there was a CEC adapter, then check if the CEC adapter properties
+ * were unchanged and just update the CEC physical address. Otherwise
+ * unregister the old CEC adapter and create a new one.
+ */
+void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const struct edid *edid)
+{
+       struct drm_connector *connector = aux->cec.connector;
+       u32 cec_caps = CEC_CAP_DEFAULTS | CEC_CAP_NEEDS_HPD |
+                      CEC_CAP_CONNECTOR_INFO;
+       struct cec_connector_info conn_info;
+       unsigned int num_las = 1;
+       u8 cap;
+
+       /* No transfer function was set, so not a DP connector */
+       if (!aux->transfer)
+               return;
+
+#ifndef CONFIG_MEDIA_CEC_RC
+       /*
+        * CEC_CAP_RC is part of CEC_CAP_DEFAULTS, but it is stripped by
+        * cec_allocate_adapter() if CONFIG_MEDIA_CEC_RC is undefined.
+        *
+        * Do this here as well to ensure the tests against cec_caps are
+        * correct.
+        */
+       cec_caps &= ~CEC_CAP_RC;
+#endif
+       cancel_delayed_work_sync(&aux->cec.unregister_work);
+
+       mutex_lock(&aux->cec.lock);
+       if (!drm_dp_cec_cap(aux, &cap)) {
+               /* CEC is not supported, unregister any existing adapter */
+               cec_unregister_adapter(aux->cec.adap);
+               aux->cec.adap = NULL;
+               goto unlock;
+       }
+
+       if (cap & DP_CEC_SNOOPING_CAPABLE)
+               cec_caps |= CEC_CAP_MONITOR_ALL;
+       if (cap & DP_CEC_MULTIPLE_LA_CAPABLE)
+               num_las = CEC_MAX_LOG_ADDRS;
+
+       if (aux->cec.adap) {
+               if (aux->cec.adap->capabilities == cec_caps &&
+                   aux->cec.adap->available_log_addrs == num_las) {
+                       /* Unchanged, so just set the phys addr */
+                       cec_s_phys_addr_from_edid(aux->cec.adap, edid);
+                       goto unlock;
+               }
+               /*
+                * The capabilities changed, so unregister the old
+                * adapter first.
+                */
+               cec_unregister_adapter(aux->cec.adap);
+       }
+
+       /* Create a new adapter */
+       aux->cec.adap = cec_allocate_adapter(&drm_dp_cec_adap_ops,
+                                            aux, connector->name, cec_caps,
+                                            num_las);
+       if (IS_ERR(aux->cec.adap)) {
+               aux->cec.adap = NULL;
+               goto unlock;
+       }
+
+       cec_fill_conn_info_from_drm(&conn_info, connector);
+       cec_s_conn_info(aux->cec.adap, &conn_info);
+
+       if (cec_register_adapter(aux->cec.adap, connector->dev->dev)) {
+               cec_delete_adapter(aux->cec.adap);
+               aux->cec.adap = NULL;
+       } else {
+               /*
+                * Update the phys addr for the new CEC adapter. When called
+                * from drm_dp_cec_register_connector() edid == NULL, so in
+                * that case the phys addr is just invalidated.
+                */
+               cec_s_phys_addr_from_edid(aux->cec.adap, edid);
+       }
+unlock:
+       mutex_unlock(&aux->cec.lock);
+}
+EXPORT_SYMBOL(drm_dp_cec_set_edid);
+
+/*
+ * The EDID disappeared (likely because of the HPD going down).
+ */
+void drm_dp_cec_unset_edid(struct drm_dp_aux *aux)
+{
+       /* No transfer function was set, so not a DP connector */
+       if (!aux->transfer)
+               return;
+
+       cancel_delayed_work_sync(&aux->cec.unregister_work);
+
+       mutex_lock(&aux->cec.lock);
+       if (!aux->cec.adap)
+               goto unlock;
+
+       cec_phys_addr_invalidate(aux->cec.adap);
+       /*
+        * We're done if we want to keep the CEC device
+        * (drm_dp_cec_unregister_delay is >= NEVER_UNREG_DELAY) or if the
+        * DPCD still indicates the CEC capability (expected for an integrated
+        * HDMI branch device).
+        */
+       if (drm_dp_cec_unregister_delay < NEVER_UNREG_DELAY &&
+           !drm_dp_cec_cap(aux, NULL)) {
+               /*
+                * Unregister the CEC adapter after drm_dp_cec_unregister_delay
+                * seconds. This to debounce short HPD off-and-on cycles from
+                * displays.
+                */
+               schedule_delayed_work(&aux->cec.unregister_work,
+                                     drm_dp_cec_unregister_delay * HZ);
+       }
+unlock:
+       mutex_unlock(&aux->cec.lock);
+}
+EXPORT_SYMBOL(drm_dp_cec_unset_edid);
+
+/**
+ * drm_dp_cec_register_connector() - register a new connector
+ * @aux: DisplayPort AUX channel
+ * @connector: drm connector
+ *
+ * A new connector was registered with associated CEC adapter name and
+ * CEC adapter parent device. After registering the name and parent
+ * drm_dp_cec_set_edid() is called to check if the connector supports
+ * CEC and to register a CEC adapter if that is the case.
+ */
+void drm_dp_cec_register_connector(struct drm_dp_aux *aux,
+                                  struct drm_connector *connector)
+{
+       WARN_ON(aux->cec.adap);
+       if (WARN_ON(!aux->transfer))
+               return;
+       aux->cec.connector = connector;
+       INIT_DELAYED_WORK(&aux->cec.unregister_work,
+                         drm_dp_cec_unregister_work);
+}
+EXPORT_SYMBOL(drm_dp_cec_register_connector);
+
+/**
+ * drm_dp_cec_unregister_connector() - unregister the CEC adapter, if any
+ * @aux: DisplayPort AUX channel
+ */
+void drm_dp_cec_unregister_connector(struct drm_dp_aux *aux)
+{
+       if (!aux->cec.adap)
+               return;
+       cancel_delayed_work_sync(&aux->cec.unregister_work);
+       cec_unregister_adapter(aux->cec.adap);
+       aux->cec.adap = NULL;
+}
+EXPORT_SYMBOL(drm_dp_cec_unregister_connector);
diff --git a/drivers/gpu/drm/dp/drm_dp_dual_mode_helper.c b/drivers/gpu/drm/dp/drm_dp_dual_mode_helper.c
new file mode 100644 (file)
index 0000000..2049cb0
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <drm/drm_device.h>
+#include <drm/dp/drm_dp_dual_mode_helper.h>
+#include <drm/drm_print.h>
+
+/**
+ * DOC: dp dual mode helpers
+ *
+ * Helper functions to deal with DP dual mode (aka. DP++) adaptors.
+ *
+ * Type 1:
+ * Adaptor registers (if any) and the sink DDC bus may be accessed via I2C.
+ *
+ * Type 2:
+ * Adaptor registers and sink DDC bus can be accessed either via I2C or
+ * I2C-over-AUX. Source devices may choose to implement either of these
+ * access methods.
+ */
+
+#define DP_DUAL_MODE_SLAVE_ADDRESS 0x40
+
+/**
+ * drm_dp_dual_mode_read - Read from the DP dual mode adaptor register(s)
+ * @adapter: I2C adapter for the DDC bus
+ * @offset: register offset
+ * @buffer: buffer for return data
+ * @size: sizo of the buffer
+ *
+ * Reads @size bytes from the DP dual mode adaptor registers
+ * starting at @offset.
+ *
+ * Returns:
+ * 0 on success, negative error code on failure
+ */
+ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter,
+                             u8 offset, void *buffer, size_t size)
+{
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = &offset,
+               },
+               {
+                       .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
+                       .flags = I2C_M_RD,
+                       .len = size,
+                       .buf = buffer,
+               },
+       };
+       int ret;
+
+       ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
+       if (ret < 0)
+               return ret;
+       if (ret != ARRAY_SIZE(msgs))
+               return -EPROTO;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_dual_mode_read);
+
+/**
+ * drm_dp_dual_mode_write - Write to the DP dual mode adaptor register(s)
+ * @adapter: I2C adapter for the DDC bus
+ * @offset: register offset
+ * @buffer: buffer for write data
+ * @size: sizo of the buffer
+ *
+ * Writes @size bytes to the DP dual mode adaptor registers
+ * starting at @offset.
+ *
+ * Returns:
+ * 0 on success, negative error code on failure
+ */
+ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter,
+                              u8 offset, const void *buffer, size_t size)
+{
+       struct i2c_msg msg = {
+               .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
+               .flags = 0,
+               .len = 1 + size,
+               .buf = NULL,
+       };
+       void *data;
+       int ret;
+
+       data = kmalloc(msg.len, GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       msg.buf = data;
+
+       memcpy(data, &offset, 1);
+       memcpy(data + 1, buffer, size);
+
+       ret = i2c_transfer(adapter, &msg, 1);
+
+       kfree(data);
+
+       if (ret < 0)
+               return ret;
+       if (ret != 1)
+               return -EPROTO;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_dual_mode_write);
+
+static bool is_hdmi_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN])
+{
+       static const char dp_dual_mode_hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] =
+               "DP-HDMI ADAPTOR\x04";
+
+       return memcmp(hdmi_id, dp_dual_mode_hdmi_id,
+                     sizeof(dp_dual_mode_hdmi_id)) == 0;
+}
+
+static bool is_type1_adaptor(uint8_t adaptor_id)
+{
+       return adaptor_id == 0 || adaptor_id == 0xff;
+}
+
+static bool is_type2_adaptor(uint8_t adaptor_id)
+{
+       return adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
+                             DP_DUAL_MODE_REV_TYPE2);
+}
+
+static bool is_lspcon_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN],
+                             const uint8_t adaptor_id)
+{
+       return is_hdmi_adaptor(hdmi_id) &&
+               (adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
+                DP_DUAL_MODE_TYPE_HAS_DPCD));
+}
+
+/**
+ * drm_dp_dual_mode_detect - Identify the DP dual mode adaptor
+ * @dev: &drm_device to use
+ * @adapter: I2C adapter for the DDC bus
+ *
+ * Attempt to identify the type of the DP dual mode adaptor used.
+ *
+ * Note that when the answer is @DRM_DP_DUAL_MODE_UNKNOWN it's not
+ * certain whether we're dealing with a native HDMI port or
+ * a type 1 DVI dual mode adaptor. The driver will have to use
+ * some other hardware/driver specific mechanism to make that
+ * distinction.
+ *
+ * Returns:
+ * The type of the DP dual mode adaptor used
+ */
+enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
+                                                  struct i2c_adapter *adapter)
+{
+       char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] = {};
+       uint8_t adaptor_id = 0x00;
+       ssize_t ret;
+
+       /*
+        * Let's see if the adaptor is there the by reading the
+        * HDMI ID registers.
+        *
+        * Note that type 1 DVI adaptors are not required to implemnt
+        * any registers, and that presents a problem for detection.
+        * If the i2c transfer is nacked, we may or may not be dealing
+        * with a type 1 DVI adaptor. Some other mechanism of detecting
+        * the presence of the adaptor is required. One way would be
+        * to check the state of the CONFIG1 pin, Another method would
+        * simply require the driver to know whether the port is a DP++
+        * port or a native HDMI port. Both of these methods are entirely
+        * hardware/driver specific so we can't deal with them here.
+        */
+       ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_HDMI_ID,
+                                   hdmi_id, sizeof(hdmi_id));
+       drm_dbg_kms(dev, "DP dual mode HDMI ID: %*pE (err %zd)\n",
+                   ret ? 0 : (int)sizeof(hdmi_id), hdmi_id, ret);
+       if (ret)
+               return DRM_DP_DUAL_MODE_UNKNOWN;
+
+       /*
+        * Sigh. Some (maybe all?) type 1 adaptors are broken and ack
+        * the offset but ignore it, and instead they just always return
+        * data from the start of the HDMI ID buffer. So for a broken
+        * type 1 HDMI adaptor a single byte read will always give us
+        * 0x44, and for a type 1 DVI adaptor it should give 0x00
+        * (assuming it implements any registers). Fortunately neither
+        * of those values will match the type 2 signature of the
+        * DP_DUAL_MODE_ADAPTOR_ID register so we can proceed with
+        * the type 2 adaptor detection safely even in the presence
+        * of broken type 1 adaptors.
+        */
+       ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_ADAPTOR_ID,
+                                   &adaptor_id, sizeof(adaptor_id));
+       drm_dbg_kms(dev, "DP dual mode adaptor ID: %02x (err %zd)\n", adaptor_id, ret);
+       if (ret == 0) {
+               if (is_lspcon_adaptor(hdmi_id, adaptor_id))
+                       return DRM_DP_DUAL_MODE_LSPCON;
+               if (is_type2_adaptor(adaptor_id)) {
+                       if (is_hdmi_adaptor(hdmi_id))
+                               return DRM_DP_DUAL_MODE_TYPE2_HDMI;
+                       else
+                               return DRM_DP_DUAL_MODE_TYPE2_DVI;
+               }
+               /*
+                * If neither a proper type 1 ID nor a broken type 1 adaptor
+                * as described above, assume type 1, but let the user know
+                * that we may have misdetected the type.
+                */
+               if (!is_type1_adaptor(adaptor_id) && adaptor_id != hdmi_id[0])
+                       drm_err(dev, "Unexpected DP dual mode adaptor ID %02x\n", adaptor_id);
+
+       }
+
+       if (is_hdmi_adaptor(hdmi_id))
+               return DRM_DP_DUAL_MODE_TYPE1_HDMI;
+       else
+               return DRM_DP_DUAL_MODE_TYPE1_DVI;
+}
+EXPORT_SYMBOL(drm_dp_dual_mode_detect);
+
+/**
+ * drm_dp_dual_mode_max_tmds_clock - Max TMDS clock for DP dual mode adaptor
+ * @dev: &drm_device to use
+ * @type: DP dual mode adaptor type
+ * @adapter: I2C adapter for the DDC bus
+ *
+ * Determine the max TMDS clock the adaptor supports based on the
+ * type of the dual mode adaptor and the DP_DUAL_MODE_MAX_TMDS_CLOCK
+ * register (on type2 adaptors). As some type 1 adaptors have
+ * problems with registers (see comments in drm_dp_dual_mode_detect())
+ * we don't read the register on those, instead we simply assume
+ * a 165 MHz limit based on the specification.
+ *
+ * Returns:
+ * Maximum supported TMDS clock rate for the DP dual mode adaptor in kHz.
+ */
+int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
+                                   struct i2c_adapter *adapter)
+{
+       uint8_t max_tmds_clock;
+       ssize_t ret;
+
+       /* native HDMI so no limit */
+       if (type == DRM_DP_DUAL_MODE_NONE)
+               return 0;
+
+       /*
+        * Type 1 adaptors are limited to 165MHz
+        * Type 2 adaptors can tells us their limit
+        */
+       if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
+               return 165000;
+
+       ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_MAX_TMDS_CLOCK,
+                                   &max_tmds_clock, sizeof(max_tmds_clock));
+       if (ret || max_tmds_clock == 0x00 || max_tmds_clock == 0xff) {
+               drm_dbg_kms(dev, "Failed to query max TMDS clock\n");
+               return 165000;
+       }
+
+       return max_tmds_clock * 5000 / 2;
+}
+EXPORT_SYMBOL(drm_dp_dual_mode_max_tmds_clock);
+
+/**
+ * drm_dp_dual_mode_get_tmds_output - Get the state of the TMDS output buffers in the DP dual mode adaptor
+ * @dev: &drm_device to use
+ * @type: DP dual mode adaptor type
+ * @adapter: I2C adapter for the DDC bus
+ * @enabled: current state of the TMDS output buffers
+ *
+ * Get the state of the TMDS output buffers in the adaptor. For
+ * type2 adaptors this is queried from the DP_DUAL_MODE_TMDS_OEN
+ * register. As some type 1 adaptors have problems with registers
+ * (see comments in drm_dp_dual_mode_detect()) we don't read the
+ * register on those, instead we simply assume that the buffers
+ * are always enabled.
+ *
+ * Returns:
+ * 0 on success, negative error code on failure
+ */
+int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev,
+                                    enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter,
+                                    bool *enabled)
+{
+       uint8_t tmds_oen;
+       ssize_t ret;
+
+       if (type < DRM_DP_DUAL_MODE_TYPE2_DVI) {
+               *enabled = true;
+               return 0;
+       }
+
+       ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
+                                   &tmds_oen, sizeof(tmds_oen));
+       if (ret) {
+               drm_dbg_kms(dev, "Failed to query state of TMDS output buffers\n");
+               return ret;
+       }
+
+       *enabled = !(tmds_oen & DP_DUAL_MODE_TMDS_DISABLE);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_dual_mode_get_tmds_output);
+
+/**
+ * drm_dp_dual_mode_set_tmds_output - Enable/disable TMDS output buffers in the DP dual mode adaptor
+ * @dev: &drm_device to use
+ * @type: DP dual mode adaptor type
+ * @adapter: I2C adapter for the DDC bus
+ * @enable: enable (as opposed to disable) the TMDS output buffers
+ *
+ * Set the state of the TMDS output buffers in the adaptor. For
+ * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register. As
+ * some type 1 adaptors have problems with registers (see comments
+ * in drm_dp_dual_mode_detect()) we avoid touching the register,
+ * making this function a no-op on type 1 adaptors.
+ *
+ * Returns:
+ * 0 on success, negative error code on failure
+ */
+int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
+                                    struct i2c_adapter *adapter, bool enable)
+{
+       uint8_t tmds_oen = enable ? 0 : DP_DUAL_MODE_TMDS_DISABLE;
+       ssize_t ret;
+       int retry;
+
+       if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
+               return 0;
+
+       /*
+        * LSPCON adapters in low-power state may ignore the first write, so
+        * read back and verify the written value a few times.
+        */
+       for (retry = 0; retry < 3; retry++) {
+               uint8_t tmp;
+
+               ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
+                                            &tmds_oen, sizeof(tmds_oen));
+               if (ret) {
+                       drm_dbg_kms(dev, "Failed to %s TMDS output buffers (%d attempts)\n",
+                                   enable ? "enable" : "disable", retry + 1);
+                       return ret;
+               }
+
+               ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
+                                           &tmp, sizeof(tmp));
+               if (ret) {
+                       drm_dbg_kms(dev,
+                                   "I2C read failed during TMDS output buffer %s (%d attempts)\n",
+                                   enable ? "enabling" : "disabling", retry + 1);
+                       return ret;
+               }
+
+               if (tmp == tmds_oen)
+                       return 0;
+       }
+
+       drm_dbg_kms(dev, "I2C write value mismatch during TMDS output buffer %s\n",
+                   enable ? "enabling" : "disabling");
+
+       return -EIO;
+}
+EXPORT_SYMBOL(drm_dp_dual_mode_set_tmds_output);
+
+/**
+ * drm_dp_get_dual_mode_type_name - Get the name of the DP dual mode adaptor type as a string
+ * @type: DP dual mode adaptor type
+ *
+ * Returns:
+ * String representation of the DP dual mode adaptor type
+ */
+const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type)
+{
+       switch (type) {
+       case DRM_DP_DUAL_MODE_NONE:
+               return "none";
+       case DRM_DP_DUAL_MODE_TYPE1_DVI:
+               return "type 1 DVI";
+       case DRM_DP_DUAL_MODE_TYPE1_HDMI:
+               return "type 1 HDMI";
+       case DRM_DP_DUAL_MODE_TYPE2_DVI:
+               return "type 2 DVI";
+       case DRM_DP_DUAL_MODE_TYPE2_HDMI:
+               return "type 2 HDMI";
+       case DRM_DP_DUAL_MODE_LSPCON:
+               return "lspcon";
+       default:
+               WARN_ON(type != DRM_DP_DUAL_MODE_UNKNOWN);
+               return "unknown";
+       }
+}
+EXPORT_SYMBOL(drm_dp_get_dual_mode_type_name);
+
+/**
+ * drm_lspcon_get_mode: Get LSPCON's current mode of operation by
+ * reading offset (0x80, 0x41)
+ * @dev: &drm_device to use
+ * @adapter: I2C-over-aux adapter
+ * @mode: current lspcon mode of operation output variable
+ *
+ * Returns:
+ * 0 on success, sets the current_mode value to appropriate mode
+ * -error on failure
+ */
+int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
+                       enum drm_lspcon_mode *mode)
+{
+       u8 data;
+       int ret = 0;
+       int retry;
+
+       if (!mode) {
+               drm_err(dev, "NULL input\n");
+               return -EINVAL;
+       }
+
+       /* Read Status: i2c over aux */
+       for (retry = 0; retry < 6; retry++) {
+               if (retry)
+                       usleep_range(500, 1000);
+
+               ret = drm_dp_dual_mode_read(adapter,
+                                           DP_DUAL_MODE_LSPCON_CURRENT_MODE,
+                                           &data, sizeof(data));
+               if (!ret)
+                       break;
+       }
+
+       if (ret < 0) {
+               drm_dbg_kms(dev, "LSPCON read(0x80, 0x41) failed\n");
+               return -EFAULT;
+       }
+
+       if (data & DP_DUAL_MODE_LSPCON_MODE_PCON)
+               *mode = DRM_LSPCON_MODE_PCON;
+       else
+               *mode = DRM_LSPCON_MODE_LS;
+       return 0;
+}
+EXPORT_SYMBOL(drm_lspcon_get_mode);
+
+/**
+ * drm_lspcon_set_mode: Change LSPCON's mode of operation by
+ * writing offset (0x80, 0x40)
+ * @dev: &drm_device to use
+ * @adapter: I2C-over-aux adapter
+ * @mode: required mode of operation
+ *
+ * Returns:
+ * 0 on success, -error on failure/timeout
+ */
+int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
+                       enum drm_lspcon_mode mode)
+{
+       u8 data = 0;
+       int ret;
+       int time_out = 200;
+       enum drm_lspcon_mode current_mode;
+
+       if (mode == DRM_LSPCON_MODE_PCON)
+               data = DP_DUAL_MODE_LSPCON_MODE_PCON;
+
+       /* Change mode */
+       ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_LSPCON_MODE_CHANGE,
+                                    &data, sizeof(data));
+       if (ret < 0) {
+               drm_err(dev, "LSPCON mode change failed\n");
+               return ret;
+       }
+
+       /*
+        * Confirm mode change by reading the status bit.
+        * Sometimes, it takes a while to change the mode,
+        * so wait and retry until time out or done.
+        */
+       do {
+               ret = drm_lspcon_get_mode(dev, adapter, &current_mode);
+               if (ret) {
+                       drm_err(dev, "can't confirm LSPCON mode change\n");
+                       return ret;
+               } else {
+                       if (current_mode != mode) {
+                               msleep(10);
+                               time_out -= 10;
+                       } else {
+                               drm_dbg_kms(dev, "LSPCON mode changed to %s\n",
+                                           mode == DRM_LSPCON_MODE_LS ? "LS" : "PCON");
+                               return 0;
+                       }
+               }
+       } while (time_out);
+
+       drm_err(dev, "LSPCON mode change timed out\n");
+       return -ETIMEDOUT;
+}
+EXPORT_SYMBOL(drm_lspcon_set_mode);
diff --git a/drivers/gpu/drm/dp/drm_dp_helper_internal.h b/drivers/gpu/drm/dp/drm_dp_helper_internal.h
new file mode 100644 (file)
index 0000000..8917fc3
--- /dev/null
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef DRM_DP_HELPER_INTERNAL_H
+#define DRM_DP_HELPER_INTERNAL_H
+
+struct drm_dp_aux;
+
+#ifdef CONFIG_DRM_DP_AUX_CHARDEV
+int drm_dp_aux_dev_init(void);
+void drm_dp_aux_dev_exit(void);
+int drm_dp_aux_register_devnode(struct drm_dp_aux *aux);
+void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux);
+#else
+static inline int drm_dp_aux_dev_init(void)
+{
+       return 0;
+}
+
+static inline void drm_dp_aux_dev_exit(void)
+{
+}
+
+static inline int drm_dp_aux_register_devnode(struct drm_dp_aux *aux)
+{
+       return 0;
+}
+
+static inline void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
+{
+}
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/dp/drm_dp_helper_mod.c b/drivers/gpu/drm/dp/drm_dp_helper_mod.c
new file mode 100644 (file)
index 0000000..db753de
--- /dev/null
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MIT
+
+#include <linux/module.h>
+
+#include "drm_dp_helper_internal.h"
+
+MODULE_DESCRIPTION("DRM DisplayPort helper");
+MODULE_LICENSE("GPL and additional rights");
+
+static int __init drm_dp_helper_module_init(void)
+{
+       return drm_dp_aux_dev_init();
+}
+
+static void __exit drm_dp_helper_module_exit(void)
+{
+       /* Call exit functions from specific dp helpers here */
+       drm_dp_aux_dev_exit();
+}
+
+module_init(drm_dp_helper_module_init);
+module_exit(drm_dp_helper_module_exit);
diff --git a/drivers/gpu/drm/dp/drm_dp_mst_topology.c b/drivers/gpu/drm/dp/drm_dp_mst_topology.c
new file mode 100644 (file)
index 0000000..11300b5
--- /dev/null
@@ -0,0 +1,5978 @@
+/*
+ * Copyright © 2014 Red Hat
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * 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>
+
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+#include <linux/stacktrace.h>
+#include <linux/sort.h>
+#include <linux/timekeeping.h>
+#include <linux/math64.h>
+#endif
+
+#include <drm/dp/drm_dp_mst_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "drm_dp_helper_internal.h"
+#include "drm_dp_mst_topology_internal.h"
+
+/**
+ * DOC: dp mst helper
+ *
+ * These functions contain parts of the DisplayPort 1.2a MultiStream Transport
+ * protocol. The helpers contain a topology manager and bandwidth manager.
+ * The helpers encapsulate the sending and received of sideband msgs.
+ */
+struct drm_dp_pending_up_req {
+       struct drm_dp_sideband_msg_hdr hdr;
+       struct drm_dp_sideband_msg_req_body msg;
+       struct list_head next;
+};
+
+static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
+                                 char *buf);
+
+static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port);
+
+static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
+                                    int id,
+                                    struct drm_dp_payload *payload);
+
+static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
+                                struct drm_dp_mst_port *port,
+                                int offset, int size, u8 *bytes);
+static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
+                                 struct drm_dp_mst_port *port,
+                                 int offset, int size, u8 *bytes);
+
+static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+                                   struct drm_dp_mst_branch *mstb);
+
+static void
+drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr,
+                                  struct drm_dp_mst_branch *mstb);
+
+static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
+                                          struct drm_dp_mst_branch *mstb,
+                                          struct drm_dp_mst_port *port);
+static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
+                                u8 *guid);
+
+static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port);
+static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port);
+static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr);
+
+static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port,
+                                                struct drm_dp_mst_branch *branch);
+
+#define DBG_PREFIX "[dp_mst]"
+
+#define DP_STR(x) [DP_ ## x] = #x
+
+static const char *drm_dp_mst_req_type_str(u8 req_type)
+{
+       static const char * const req_type_str[] = {
+               DP_STR(GET_MSG_TRANSACTION_VERSION),
+               DP_STR(LINK_ADDRESS),
+               DP_STR(CONNECTION_STATUS_NOTIFY),
+               DP_STR(ENUM_PATH_RESOURCES),
+               DP_STR(ALLOCATE_PAYLOAD),
+               DP_STR(QUERY_PAYLOAD),
+               DP_STR(RESOURCE_STATUS_NOTIFY),
+               DP_STR(CLEAR_PAYLOAD_ID_TABLE),
+               DP_STR(REMOTE_DPCD_READ),
+               DP_STR(REMOTE_DPCD_WRITE),
+               DP_STR(REMOTE_I2C_READ),
+               DP_STR(REMOTE_I2C_WRITE),
+               DP_STR(POWER_UP_PHY),
+               DP_STR(POWER_DOWN_PHY),
+               DP_STR(SINK_EVENT_NOTIFY),
+               DP_STR(QUERY_STREAM_ENC_STATUS),
+       };
+
+       if (req_type >= ARRAY_SIZE(req_type_str) ||
+           !req_type_str[req_type])
+               return "unknown";
+
+       return req_type_str[req_type];
+}
+
+#undef DP_STR
+#define DP_STR(x) [DP_NAK_ ## x] = #x
+
+static const char *drm_dp_mst_nak_reason_str(u8 nak_reason)
+{
+       static const char * const nak_reason_str[] = {
+               DP_STR(WRITE_FAILURE),
+               DP_STR(INVALID_READ),
+               DP_STR(CRC_FAILURE),
+               DP_STR(BAD_PARAM),
+               DP_STR(DEFER),
+               DP_STR(LINK_FAILURE),
+               DP_STR(NO_RESOURCES),
+               DP_STR(DPCD_FAIL),
+               DP_STR(I2C_NAK),
+               DP_STR(ALLOCATE_FAIL),
+       };
+
+       if (nak_reason >= ARRAY_SIZE(nak_reason_str) ||
+           !nak_reason_str[nak_reason])
+               return "unknown";
+
+       return nak_reason_str[nak_reason];
+}
+
+#undef DP_STR
+#define DP_STR(x) [DRM_DP_SIDEBAND_TX_ ## x] = #x
+
+static const char *drm_dp_mst_sideband_tx_state_str(int state)
+{
+       static const char * const sideband_reason_str[] = {
+               DP_STR(QUEUED),
+               DP_STR(START_SEND),
+               DP_STR(SENT),
+               DP_STR(RX),
+               DP_STR(TIMEOUT),
+       };
+
+       if (state >= ARRAY_SIZE(sideband_reason_str) ||
+           !sideband_reason_str[state])
+               return "unknown";
+
+       return sideband_reason_str[state];
+}
+
+static int
+drm_dp_mst_rad_to_str(const u8 rad[8], u8 lct, char *out, size_t len)
+{
+       int i;
+       u8 unpacked_rad[16];
+
+       for (i = 0; i < lct; i++) {
+               if (i % 2)
+                       unpacked_rad[i] = rad[i / 2] >> 4;
+               else
+                       unpacked_rad[i] = rad[i / 2] & BIT_MASK(4);
+       }
+
+       /* TODO: Eventually add something to printk so we can format the rad
+        * like this: 1.2.3
+        */
+       return snprintf(out, len, "%*phC", lct, unpacked_rad);
+}
+
+/* sideband msg handling */
+static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles)
+{
+       u8 bitmask = 0x80;
+       u8 bitshift = 7;
+       u8 array_index = 0;
+       int number_of_bits = num_nibbles * 4;
+       u8 remainder = 0;
+
+       while (number_of_bits != 0) {
+               number_of_bits--;
+               remainder <<= 1;
+               remainder |= (data[array_index] & bitmask) >> bitshift;
+               bitmask >>= 1;
+               bitshift--;
+               if (bitmask == 0) {
+                       bitmask = 0x80;
+                       bitshift = 7;
+                       array_index++;
+               }
+               if ((remainder & 0x10) == 0x10)
+                       remainder ^= 0x13;
+       }
+
+       number_of_bits = 4;
+       while (number_of_bits != 0) {
+               number_of_bits--;
+               remainder <<= 1;
+               if ((remainder & 0x10) != 0)
+                       remainder ^= 0x13;
+       }
+
+       return remainder;
+}
+
+static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes)
+{
+       u8 bitmask = 0x80;
+       u8 bitshift = 7;
+       u8 array_index = 0;
+       int number_of_bits = number_of_bytes * 8;
+       u16 remainder = 0;
+
+       while (number_of_bits != 0) {
+               number_of_bits--;
+               remainder <<= 1;
+               remainder |= (data[array_index] & bitmask) >> bitshift;
+               bitmask >>= 1;
+               bitshift--;
+               if (bitmask == 0) {
+                       bitmask = 0x80;
+                       bitshift = 7;
+                       array_index++;
+               }
+               if ((remainder & 0x100) == 0x100)
+                       remainder ^= 0xd5;
+       }
+
+       number_of_bits = 8;
+       while (number_of_bits != 0) {
+               number_of_bits--;
+               remainder <<= 1;
+               if ((remainder & 0x100) != 0)
+                       remainder ^= 0xd5;
+       }
+
+       return remainder & 0xff;
+}
+static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr)
+{
+       u8 size = 3;
+
+       size += (hdr->lct / 2);
+       return size;
+}
+
+static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
+                                          u8 *buf, int *len)
+{
+       int idx = 0;
+       int i;
+       u8 crc4;
+
+       buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf);
+       for (i = 0; i < (hdr->lct / 2); i++)
+               buf[idx++] = hdr->rad[i];
+       buf[idx++] = (hdr->broadcast << 7) | (hdr->path_msg << 6) |
+               (hdr->msg_len & 0x3f);
+       buf[idx++] = (hdr->somt << 7) | (hdr->eomt << 6) | (hdr->seqno << 4);
+
+       crc4 = drm_dp_msg_header_crc4(buf, (idx * 2) - 1);
+       buf[idx - 1] |= (crc4 & 0xf);
+
+       *len = idx;
+}
+
+static bool drm_dp_decode_sideband_msg_hdr(const struct drm_dp_mst_topology_mgr *mgr,
+                                          struct drm_dp_sideband_msg_hdr *hdr,
+                                          u8 *buf, int buflen, u8 *hdrlen)
+{
+       u8 crc4;
+       u8 len;
+       int i;
+       u8 idx;
+
+       if (buf[0] == 0)
+               return false;
+       len = 3;
+       len += ((buf[0] & 0xf0) >> 4) / 2;
+       if (len > buflen)
+               return false;
+       crc4 = drm_dp_msg_header_crc4(buf, (len * 2) - 1);
+
+       if ((crc4 & 0xf) != (buf[len - 1] & 0xf)) {
+               drm_dbg_kms(mgr->dev, "crc4 mismatch 0x%x 0x%x\n", crc4, buf[len - 1]);
+               return false;
+       }
+
+       hdr->lct = (buf[0] & 0xf0) >> 4;
+       hdr->lcr = (buf[0] & 0xf);
+       idx = 1;
+       for (i = 0; i < (hdr->lct / 2); i++)
+               hdr->rad[i] = buf[idx++];
+       hdr->broadcast = (buf[idx] >> 7) & 0x1;
+       hdr->path_msg = (buf[idx] >> 6) & 0x1;
+       hdr->msg_len = buf[idx] & 0x3f;
+       idx++;
+       hdr->somt = (buf[idx] >> 7) & 0x1;
+       hdr->eomt = (buf[idx] >> 6) & 0x1;
+       hdr->seqno = (buf[idx] >> 4) & 0x1;
+       idx++;
+       *hdrlen = idx;
+       return true;
+}
+
+void
+drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
+                          struct drm_dp_sideband_msg_tx *raw)
+{
+       int idx = 0;
+       int i;
+       u8 *buf = raw->msg;
+
+       buf[idx++] = req->req_type & 0x7f;
+
+       switch (req->req_type) {
+       case DP_ENUM_PATH_RESOURCES:
+       case DP_POWER_DOWN_PHY:
+       case DP_POWER_UP_PHY:
+               buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
+               idx++;
+               break;
+       case DP_ALLOCATE_PAYLOAD:
+               buf[idx] = (req->u.allocate_payload.port_number & 0xf) << 4 |
+                       (req->u.allocate_payload.number_sdp_streams & 0xf);
+               idx++;
+               buf[idx] = (req->u.allocate_payload.vcpi & 0x7f);
+               idx++;
+               buf[idx] = (req->u.allocate_payload.pbn >> 8);
+               idx++;
+               buf[idx] = (req->u.allocate_payload.pbn & 0xff);
+               idx++;
+               for (i = 0; i < req->u.allocate_payload.number_sdp_streams / 2; i++) {
+                       buf[idx] = ((req->u.allocate_payload.sdp_stream_sink[i * 2] & 0xf) << 4) |
+                               (req->u.allocate_payload.sdp_stream_sink[i * 2 + 1] & 0xf);
+                       idx++;
+               }
+               if (req->u.allocate_payload.number_sdp_streams & 1) {
+                       i = req->u.allocate_payload.number_sdp_streams - 1;
+                       buf[idx] = (req->u.allocate_payload.sdp_stream_sink[i] & 0xf) << 4;
+                       idx++;
+               }
+               break;
+       case DP_QUERY_PAYLOAD:
+               buf[idx] = (req->u.query_payload.port_number & 0xf) << 4;
+               idx++;
+               buf[idx] = (req->u.query_payload.vcpi & 0x7f);
+               idx++;
+               break;
+       case DP_REMOTE_DPCD_READ:
+               buf[idx] = (req->u.dpcd_read.port_number & 0xf) << 4;
+               buf[idx] |= ((req->u.dpcd_read.dpcd_address & 0xf0000) >> 16) & 0xf;
+               idx++;
+               buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff00) >> 8;
+               idx++;
+               buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff);
+               idx++;
+               buf[idx] = (req->u.dpcd_read.num_bytes);
+               idx++;
+               break;
+
+       case DP_REMOTE_DPCD_WRITE:
+               buf[idx] = (req->u.dpcd_write.port_number & 0xf) << 4;
+               buf[idx] |= ((req->u.dpcd_write.dpcd_address & 0xf0000) >> 16) & 0xf;
+               idx++;
+               buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff00) >> 8;
+               idx++;
+               buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff);
+               idx++;
+               buf[idx] = (req->u.dpcd_write.num_bytes);
+               idx++;
+               memcpy(&buf[idx], req->u.dpcd_write.bytes, req->u.dpcd_write.num_bytes);
+               idx += req->u.dpcd_write.num_bytes;
+               break;
+       case DP_REMOTE_I2C_READ:
+               buf[idx] = (req->u.i2c_read.port_number & 0xf) << 4;
+               buf[idx] |= (req->u.i2c_read.num_transactions & 0x3);
+               idx++;
+               for (i = 0; i < (req->u.i2c_read.num_transactions & 0x3); i++) {
+                       buf[idx] = req->u.i2c_read.transactions[i].i2c_dev_id & 0x7f;
+                       idx++;
+                       buf[idx] = req->u.i2c_read.transactions[i].num_bytes;
+                       idx++;
+                       memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes);
+                       idx += req->u.i2c_read.transactions[i].num_bytes;
+
+                       buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 4;
+                       buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf);
+                       idx++;
+               }
+               buf[idx] = (req->u.i2c_read.read_i2c_device_id) & 0x7f;
+               idx++;
+               buf[idx] = (req->u.i2c_read.num_bytes_read);
+               idx++;
+               break;
+
+       case DP_REMOTE_I2C_WRITE:
+               buf[idx] = (req->u.i2c_write.port_number & 0xf) << 4;
+               idx++;
+               buf[idx] = (req->u.i2c_write.write_i2c_device_id) & 0x7f;
+               idx++;
+               buf[idx] = (req->u.i2c_write.num_bytes);
+               idx++;
+               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;
+}
+EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_encode_sideband_req);
+
+/* Decode a sideband request we've encoded, mainly used for debugging */
+int
+drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
+                          struct drm_dp_sideband_msg_req_body *req)
+{
+       const u8 *buf = raw->msg;
+       int i, idx = 0;
+
+       req->req_type = buf[idx++] & 0x7f;
+       switch (req->req_type) {
+       case DP_ENUM_PATH_RESOURCES:
+       case DP_POWER_DOWN_PHY:
+       case DP_POWER_UP_PHY:
+               req->u.port_num.port_number = (buf[idx] >> 4) & 0xf;
+               break;
+       case DP_ALLOCATE_PAYLOAD:
+               {
+                       struct drm_dp_allocate_payload *a =
+                               &req->u.allocate_payload;
+
+                       a->number_sdp_streams = buf[idx] & 0xf;
+                       a->port_number = (buf[idx] >> 4) & 0xf;
+
+                       WARN_ON(buf[++idx] & 0x80);
+                       a->vcpi = buf[idx] & 0x7f;
+
+                       a->pbn = buf[++idx] << 8;
+                       a->pbn |= buf[++idx];
+
+                       idx++;
+                       for (i = 0; i < a->number_sdp_streams; i++) {
+                               a->sdp_stream_sink[i] =
+                                       (buf[idx + (i / 2)] >> ((i % 2) ? 0 : 4)) & 0xf;
+                       }
+               }
+               break;
+       case DP_QUERY_PAYLOAD:
+               req->u.query_payload.port_number = (buf[idx] >> 4) & 0xf;
+               WARN_ON(buf[++idx] & 0x80);
+               req->u.query_payload.vcpi = buf[idx] & 0x7f;
+               break;
+       case DP_REMOTE_DPCD_READ:
+               {
+                       struct drm_dp_remote_dpcd_read *r = &req->u.dpcd_read;
+
+                       r->port_number = (buf[idx] >> 4) & 0xf;
+
+                       r->dpcd_address = (buf[idx] << 16) & 0xf0000;
+                       r->dpcd_address |= (buf[++idx] << 8) & 0xff00;
+                       r->dpcd_address |= buf[++idx] & 0xff;
+
+                       r->num_bytes = buf[++idx];
+               }
+               break;
+       case DP_REMOTE_DPCD_WRITE:
+               {
+                       struct drm_dp_remote_dpcd_write *w =
+                               &req->u.dpcd_write;
+
+                       w->port_number = (buf[idx] >> 4) & 0xf;
+
+                       w->dpcd_address = (buf[idx] << 16) & 0xf0000;
+                       w->dpcd_address |= (buf[++idx] << 8) & 0xff00;
+                       w->dpcd_address |= buf[++idx] & 0xff;
+
+                       w->num_bytes = buf[++idx];
+
+                       w->bytes = kmemdup(&buf[++idx], w->num_bytes,
+                                          GFP_KERNEL);
+                       if (!w->bytes)
+                               return -ENOMEM;
+               }
+               break;
+       case DP_REMOTE_I2C_READ:
+               {
+                       struct drm_dp_remote_i2c_read *r = &req->u.i2c_read;
+                       struct drm_dp_remote_i2c_read_tx *tx;
+                       bool failed = false;
+
+                       r->num_transactions = buf[idx] & 0x3;
+                       r->port_number = (buf[idx] >> 4) & 0xf;
+                       for (i = 0; i < r->num_transactions; i++) {
+                               tx = &r->transactions[i];
+
+                               tx->i2c_dev_id = buf[++idx] & 0x7f;
+                               tx->num_bytes = buf[++idx];
+                               tx->bytes = kmemdup(&buf[++idx],
+                                                   tx->num_bytes,
+                                                   GFP_KERNEL);
+                               if (!tx->bytes) {
+                                       failed = true;
+                                       break;
+                               }
+                               idx += tx->num_bytes;
+                               tx->no_stop_bit = (buf[idx] >> 5) & 0x1;
+                               tx->i2c_transaction_delay = buf[idx] & 0xf;
+                       }
+
+                       if (failed) {
+                               for (i = 0; i < r->num_transactions; i++) {
+                                       tx = &r->transactions[i];
+                                       kfree(tx->bytes);
+                               }
+                               return -ENOMEM;
+                       }
+
+                       r->read_i2c_device_id = buf[++idx] & 0x7f;
+                       r->num_bytes_read = buf[++idx];
+               }
+               break;
+       case DP_REMOTE_I2C_WRITE:
+               {
+                       struct drm_dp_remote_i2c_write *w = &req->u.i2c_write;
+
+                       w->port_number = (buf[idx] >> 4) & 0xf;
+                       w->write_i2c_device_id = buf[++idx] & 0x7f;
+                       w->num_bytes = buf[++idx];
+                       w->bytes = kmemdup(&buf[++idx], w->num_bytes,
+                                          GFP_KERNEL);
+                       if (!w->bytes)
+                               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;
+}
+EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_decode_sideband_req);
+
+void
+drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
+                                 int indent, struct drm_printer *printer)
+{
+       int i;
+
+#define P(f, ...) drm_printf_indent(printer, indent, f, ##__VA_ARGS__)
+       if (req->req_type == DP_LINK_ADDRESS) {
+               /* No contents to print */
+               P("type=%s\n", drm_dp_mst_req_type_str(req->req_type));
+               return;
+       }
+
+       P("type=%s contents:\n", drm_dp_mst_req_type_str(req->req_type));
+       indent++;
+
+       switch (req->req_type) {
+       case DP_ENUM_PATH_RESOURCES:
+       case DP_POWER_DOWN_PHY:
+       case DP_POWER_UP_PHY:
+               P("port=%d\n", req->u.port_num.port_number);
+               break;
+       case DP_ALLOCATE_PAYLOAD:
+               P("port=%d vcpi=%d pbn=%d sdp_streams=%d %*ph\n",
+                 req->u.allocate_payload.port_number,
+                 req->u.allocate_payload.vcpi, req->u.allocate_payload.pbn,
+                 req->u.allocate_payload.number_sdp_streams,
+                 req->u.allocate_payload.number_sdp_streams,
+                 req->u.allocate_payload.sdp_stream_sink);
+               break;
+       case DP_QUERY_PAYLOAD:
+               P("port=%d vcpi=%d\n",
+                 req->u.query_payload.port_number,
+                 req->u.query_payload.vcpi);
+               break;
+       case DP_REMOTE_DPCD_READ:
+               P("port=%d dpcd_addr=%05x len=%d\n",
+                 req->u.dpcd_read.port_number, req->u.dpcd_read.dpcd_address,
+                 req->u.dpcd_read.num_bytes);
+               break;
+       case DP_REMOTE_DPCD_WRITE:
+               P("port=%d addr=%05x len=%d: %*ph\n",
+                 req->u.dpcd_write.port_number,
+                 req->u.dpcd_write.dpcd_address,
+                 req->u.dpcd_write.num_bytes, req->u.dpcd_write.num_bytes,
+                 req->u.dpcd_write.bytes);
+               break;
+       case DP_REMOTE_I2C_READ:
+               P("port=%d num_tx=%d id=%d size=%d:\n",
+                 req->u.i2c_read.port_number,
+                 req->u.i2c_read.num_transactions,
+                 req->u.i2c_read.read_i2c_device_id,
+                 req->u.i2c_read.num_bytes_read);
+
+               indent++;
+               for (i = 0; i < req->u.i2c_read.num_transactions; i++) {
+                       const struct drm_dp_remote_i2c_read_tx *rtx =
+                               &req->u.i2c_read.transactions[i];
+
+                       P("%d: id=%03d size=%03d no_stop_bit=%d tx_delay=%03d: %*ph\n",
+                         i, rtx->i2c_dev_id, rtx->num_bytes,
+                         rtx->no_stop_bit, rtx->i2c_transaction_delay,
+                         rtx->num_bytes, rtx->bytes);
+               }
+               break;
+       case DP_REMOTE_I2C_WRITE:
+               P("port=%d id=%d size=%d: %*ph\n",
+                 req->u.i2c_write.port_number,
+                 req->u.i2c_write.write_i2c_device_id,
+                 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;
+       }
+#undef P
+}
+EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_dump_sideband_msg_req_body);
+
+static inline void
+drm_dp_mst_dump_sideband_msg_tx(struct drm_printer *p,
+                               const struct drm_dp_sideband_msg_tx *txmsg)
+{
+       struct drm_dp_sideband_msg_req_body req;
+       char buf[64];
+       int ret;
+       int i;
+
+       drm_dp_mst_rad_to_str(txmsg->dst->rad, txmsg->dst->lct, buf,
+                             sizeof(buf));
+       drm_printf(p, "txmsg cur_offset=%x cur_len=%x seqno=%x state=%s path_msg=%d dst=%s\n",
+                  txmsg->cur_offset, txmsg->cur_len, txmsg->seqno,
+                  drm_dp_mst_sideband_tx_state_str(txmsg->state),
+                  txmsg->path_msg, buf);
+
+       ret = drm_dp_decode_sideband_req(txmsg, &req);
+       if (ret) {
+               drm_printf(p, "<failed to decode sideband req: %d>\n", ret);
+               return;
+       }
+       drm_dp_dump_sideband_msg_req_body(&req, 1, p);
+
+       switch (req.req_type) {
+       case DP_REMOTE_DPCD_WRITE:
+               kfree(req.u.dpcd_write.bytes);
+               break;
+       case DP_REMOTE_I2C_READ:
+               for (i = 0; i < req.u.i2c_read.num_transactions; i++)
+                       kfree(req.u.i2c_read.transactions[i].bytes);
+               break;
+       case DP_REMOTE_I2C_WRITE:
+               kfree(req.u.i2c_write.bytes);
+               break;
+       }
+}
+
+static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len)
+{
+       u8 crc4;
+
+       crc4 = drm_dp_msg_data_crc4(msg, len);
+       msg[len] = crc4;
+}
+
+static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body *rep,
+                                        struct drm_dp_sideband_msg_tx *raw)
+{
+       int idx = 0;
+       u8 *buf = raw->msg;
+
+       buf[idx++] = (rep->reply_type & 0x1) << 7 | (rep->req_type & 0x7f);
+
+       raw->cur_len = idx;
+}
+
+static int drm_dp_sideband_msg_set_header(struct drm_dp_sideband_msg_rx *msg,
+                                         struct drm_dp_sideband_msg_hdr *hdr,
+                                         u8 hdrlen)
+{
+       /*
+        * ignore out-of-order messages or messages that are part of a
+        * failed transaction
+        */
+       if (!hdr->somt && !msg->have_somt)
+               return false;
+
+       /* get length contained in this portion */
+       msg->curchunk_idx = 0;
+       msg->curchunk_len = hdr->msg_len;
+       msg->curchunk_hdrlen = hdrlen;
+
+       /* we have already gotten an somt - don't bother parsing */
+       if (hdr->somt && msg->have_somt)
+               return false;
+
+       if (hdr->somt) {
+               memcpy(&msg->initial_hdr, hdr,
+                      sizeof(struct drm_dp_sideband_msg_hdr));
+               msg->have_somt = true;
+       }
+       if (hdr->eomt)
+               msg->have_eomt = true;
+
+       return true;
+}
+
+/* this adds a chunk of msg to the builder to get the final msg */
+static bool drm_dp_sideband_append_payload(struct drm_dp_sideband_msg_rx *msg,
+                                          u8 *replybuf, u8 replybuflen)
+{
+       u8 crc4;
+
+       memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen);
+       msg->curchunk_idx += replybuflen;
+
+       if (msg->curchunk_idx >= msg->curchunk_len) {
+               /* do CRC */
+               crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1);
+               if (crc4 != msg->chunk[msg->curchunk_len - 1])
+                       print_hex_dump(KERN_DEBUG, "wrong crc",
+                                      DUMP_PREFIX_NONE, 16, 1,
+                                      msg->chunk,  msg->curchunk_len, false);
+               /* copy chunk into bigger msg */
+               memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1);
+               msg->curlen += msg->curchunk_len - 1;
+       }
+       return true;
+}
+
+static bool drm_dp_sideband_parse_link_address(const struct drm_dp_mst_topology_mgr *mgr,
+                                              struct drm_dp_sideband_msg_rx *raw,
+                                              struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+       int i;
+
+       memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16);
+       idx += 16;
+       repmsg->u.link_addr.nports = raw->msg[idx] & 0xf;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       for (i = 0; i < repmsg->u.link_addr.nports; i++) {
+               if (raw->msg[idx] & 0x80)
+                       repmsg->u.link_addr.ports[i].input_port = 1;
+
+               repmsg->u.link_addr.ports[i].peer_device_type = (raw->msg[idx] >> 4) & 0x7;
+               repmsg->u.link_addr.ports[i].port_number = (raw->msg[idx] & 0xf);
+
+               idx++;
+               if (idx > raw->curlen)
+                       goto fail_len;
+               repmsg->u.link_addr.ports[i].mcs = (raw->msg[idx] >> 7) & 0x1;
+               repmsg->u.link_addr.ports[i].ddps = (raw->msg[idx] >> 6) & 0x1;
+               if (repmsg->u.link_addr.ports[i].input_port == 0)
+                       repmsg->u.link_addr.ports[i].legacy_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
+               idx++;
+               if (idx > raw->curlen)
+                       goto fail_len;
+               if (repmsg->u.link_addr.ports[i].input_port == 0) {
+                       repmsg->u.link_addr.ports[i].dpcd_revision = (raw->msg[idx]);
+                       idx++;
+                       if (idx > raw->curlen)
+                               goto fail_len;
+                       memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16);
+                       idx += 16;
+                       if (idx > raw->curlen)
+                               goto fail_len;
+                       repmsg->u.link_addr.ports[i].num_sdp_streams = (raw->msg[idx] >> 4) & 0xf;
+                       repmsg->u.link_addr.ports[i].num_sdp_stream_sinks = (raw->msg[idx] & 0xf);
+                       idx++;
+
+               }
+               if (idx > raw->curlen)
+                       goto fail_len;
+       }
+
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx *raw,
+                                                  struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+
+       repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx];
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+
+       memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx *raw,
+                                                     struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+
+       repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx *raw,
+                                                     struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+
+       repmsg->u.remote_i2c_read_ack.port_number = (raw->msg[idx] & 0xf);
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx];
+       idx++;
+       /* TODO check */
+       memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("remote i2c reply parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx *raw,
+                                                         struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+
+       repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
+       repmsg->u.path_resources.fec_capable = raw->msg[idx] & 0x1;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+       idx += 2;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+       idx += 2;
+       if (idx > raw->curlen)
+               goto fail_len;
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx *raw,
+                                                         struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+
+       repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.allocate_payload.vcpi = raw->msg[idx];
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+       idx += 2;
+       if (idx > raw->curlen)
+               goto fail_len;
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx *raw,
+                                                   struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+
+       repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+       repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
+       idx += 2;
+       if (idx > raw->curlen)
+               goto fail_len;
+       return true;
+fail_len:
+       DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_power_updown_phy_ack(struct drm_dp_sideband_msg_rx *raw,
+                                                      struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+       int idx = 1;
+
+       repmsg->u.port_number.port_number = (raw->msg[idx] >> 4) & 0xf;
+       idx++;
+       if (idx > raw->curlen) {
+               DRM_DEBUG_KMS("power up/down phy parse length fail %d %d\n",
+                             idx, raw->curlen);
+               return false;
+       }
+       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(const struct drm_dp_mst_topology_mgr *mgr,
+                                       struct drm_dp_sideband_msg_rx *raw,
+                                       struct drm_dp_sideband_msg_reply_body *msg)
+{
+       memset(msg, 0, sizeof(*msg));
+       msg->reply_type = (raw->msg[0] & 0x80) >> 7;
+       msg->req_type = (raw->msg[0] & 0x7f);
+
+       if (msg->reply_type == DP_SIDEBAND_REPLY_NAK) {
+               memcpy(msg->u.nak.guid, &raw->msg[1], 16);
+               msg->u.nak.reason = raw->msg[17];
+               msg->u.nak.nak_data = raw->msg[18];
+               return false;
+       }
+
+       switch (msg->req_type) {
+       case DP_LINK_ADDRESS:
+               return drm_dp_sideband_parse_link_address(mgr, raw, msg);
+       case DP_QUERY_PAYLOAD:
+               return drm_dp_sideband_parse_query_payload_ack(raw, msg);
+       case DP_REMOTE_DPCD_READ:
+               return drm_dp_sideband_parse_remote_dpcd_read(raw, msg);
+       case DP_REMOTE_DPCD_WRITE:
+               return drm_dp_sideband_parse_remote_dpcd_write(raw, msg);
+       case DP_REMOTE_I2C_READ:
+               return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg);
+       case DP_REMOTE_I2C_WRITE:
+               return true; /* since there's nothing to parse */
+       case DP_ENUM_PATH_RESOURCES:
+               return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg);
+       case DP_ALLOCATE_PAYLOAD:
+               return drm_dp_sideband_parse_allocate_payload_ack(raw, msg);
+       case DP_POWER_DOWN_PHY:
+       case DP_POWER_UP_PHY:
+               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_err(mgr->dev, "Got unknown reply 0x%02x (%s)\n",
+                       msg->req_type, drm_dp_mst_req_type_str(msg->req_type));
+               return false;
+       }
+}
+
+static bool
+drm_dp_sideband_parse_connection_status_notify(const struct drm_dp_mst_topology_mgr *mgr,
+                                              struct drm_dp_sideband_msg_rx *raw,
+                                              struct drm_dp_sideband_msg_req_body *msg)
+{
+       int idx = 1;
+
+       msg->u.conn_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+
+       memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16);
+       idx += 16;
+       if (idx > raw->curlen)
+               goto fail_len;
+
+       msg->u.conn_stat.legacy_device_plug_status = (raw->msg[idx] >> 6) & 0x1;
+       msg->u.conn_stat.displayport_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
+       msg->u.conn_stat.message_capability_status = (raw->msg[idx] >> 4) & 0x1;
+       msg->u.conn_stat.input_port = (raw->msg[idx] >> 3) & 0x1;
+       msg->u.conn_stat.peer_device_type = (raw->msg[idx] & 0x7);
+       idx++;
+       return true;
+fail_len:
+       drm_dbg_kms(mgr->dev, "connection status reply parse length fail %d %d\n",
+                   idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_resource_status_notify(const struct drm_dp_mst_topology_mgr *mgr,
+                                                        struct drm_dp_sideband_msg_rx *raw,
+                                                        struct drm_dp_sideband_msg_req_body *msg)
+{
+       int idx = 1;
+
+       msg->u.resource_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
+       idx++;
+       if (idx > raw->curlen)
+               goto fail_len;
+
+       memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16);
+       idx += 16;
+       if (idx > raw->curlen)
+               goto fail_len;
+
+       msg->u.resource_stat.available_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
+       idx++;
+       return true;
+fail_len:
+       drm_dbg_kms(mgr->dev, "resource status reply parse length fail %d %d\n", idx, raw->curlen);
+       return false;
+}
+
+static bool drm_dp_sideband_parse_req(const struct drm_dp_mst_topology_mgr *mgr,
+                                     struct drm_dp_sideband_msg_rx *raw,
+                                     struct drm_dp_sideband_msg_req_body *msg)
+{
+       memset(msg, 0, sizeof(*msg));
+       msg->req_type = (raw->msg[0] & 0x7f);
+
+       switch (msg->req_type) {
+       case DP_CONNECTION_STATUS_NOTIFY:
+               return drm_dp_sideband_parse_connection_status_notify(mgr, raw, msg);
+       case DP_RESOURCE_STATUS_NOTIFY:
+               return drm_dp_sideband_parse_resource_status_notify(mgr, raw, msg);
+       default:
+               drm_err(mgr->dev, "Got unknown request 0x%02x (%s)\n",
+                       msg->req_type, drm_dp_mst_req_type_str(msg->req_type));
+               return false;
+       }
+}
+
+static void build_dpcd_write(struct drm_dp_sideband_msg_tx *msg,
+                            u8 port_num, u32 offset, u8 num_bytes, u8 *bytes)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       req.req_type = DP_REMOTE_DPCD_WRITE;
+       req.u.dpcd_write.port_number = port_num;
+       req.u.dpcd_write.dpcd_address = offset;
+       req.u.dpcd_write.num_bytes = num_bytes;
+       req.u.dpcd_write.bytes = bytes;
+       drm_dp_encode_sideband_req(&req, msg);
+}
+
+static void build_link_address(struct drm_dp_sideband_msg_tx *msg)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       req.req_type = DP_LINK_ADDRESS;
+       drm_dp_encode_sideband_req(&req, msg);
+}
+
+static void build_clear_payload_id_table(struct drm_dp_sideband_msg_tx *msg)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       req.req_type = DP_CLEAR_PAYLOAD_ID_TABLE;
+       drm_dp_encode_sideband_req(&req, msg);
+       msg->path_msg = true;
+}
+
+static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg,
+                                    int port_num)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       req.req_type = DP_ENUM_PATH_RESOURCES;
+       req.u.port_num.port_number = port_num;
+       drm_dp_encode_sideband_req(&req, msg);
+       msg->path_msg = true;
+       return 0;
+}
+
+static void build_allocate_payload(struct drm_dp_sideband_msg_tx *msg,
+                                  int port_num,
+                                  u8 vcpi, uint16_t pbn,
+                                  u8 number_sdp_streams,
+                                  u8 *sdp_stream_sink)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       memset(&req, 0, sizeof(req));
+       req.req_type = DP_ALLOCATE_PAYLOAD;
+       req.u.allocate_payload.port_number = port_num;
+       req.u.allocate_payload.vcpi = vcpi;
+       req.u.allocate_payload.pbn = pbn;
+       req.u.allocate_payload.number_sdp_streams = number_sdp_streams;
+       memcpy(req.u.allocate_payload.sdp_stream_sink, sdp_stream_sink,
+                  number_sdp_streams);
+       drm_dp_encode_sideband_req(&req, msg);
+       msg->path_msg = true;
+}
+
+static void build_power_updown_phy(struct drm_dp_sideband_msg_tx *msg,
+                                  int port_num, bool power_up)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       if (power_up)
+               req.req_type = DP_POWER_UP_PHY;
+       else
+               req.req_type = DP_POWER_DOWN_PHY;
+
+       req.u.port_num.port_number = port_num;
+       drm_dp_encode_sideband_req(&req, 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)
+{
+       int ret, vcpi_ret;
+
+       mutex_lock(&mgr->payload_lock);
+       ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1);
+       if (ret > mgr->max_payloads) {
+               ret = -EINVAL;
+               drm_dbg_kms(mgr->dev, "out of payload ids %d\n", ret);
+               goto out_unlock;
+       }
+
+       vcpi_ret = find_first_zero_bit(&mgr->vcpi_mask, mgr->max_payloads + 1);
+       if (vcpi_ret > mgr->max_payloads) {
+               ret = -EINVAL;
+               drm_dbg_kms(mgr->dev, "out of vcpi ids %d\n", ret);
+               goto out_unlock;
+       }
+
+       set_bit(ret, &mgr->payload_mask);
+       set_bit(vcpi_ret, &mgr->vcpi_mask);
+       vcpi->vcpi = vcpi_ret + 1;
+       mgr->proposed_vcpis[ret - 1] = vcpi;
+out_unlock:
+       mutex_unlock(&mgr->payload_lock);
+       return ret;
+}
+
+static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
+                                     int vcpi)
+{
+       int i;
+
+       if (vcpi == 0)
+               return;
+
+       mutex_lock(&mgr->payload_lock);
+       drm_dbg_kms(mgr->dev, "putting payload %d\n", vcpi);
+       clear_bit(vcpi - 1, &mgr->vcpi_mask);
+
+       for (i = 0; i < mgr->max_payloads; i++) {
+               if (mgr->proposed_vcpis[i] &&
+                   mgr->proposed_vcpis[i]->vcpi == vcpi) {
+                       mgr->proposed_vcpis[i] = NULL;
+                       clear_bit(i + 1, &mgr->payload_mask);
+               }
+       }
+       mutex_unlock(&mgr->payload_lock);
+}
+
+static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
+                             struct drm_dp_sideband_msg_tx *txmsg)
+{
+       unsigned int state;
+
+       /*
+        * All updates to txmsg->state are protected by mgr->qlock, and the two
+        * cases we check here are terminal states. For those the barriers
+        * provided by the wake_up/wait_event pair are enough.
+        */
+       state = READ_ONCE(txmsg->state);
+       return (state == DRM_DP_SIDEBAND_TX_RX ||
+               state == DRM_DP_SIDEBAND_TX_TIMEOUT);
+}
+
+static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
+                                   struct drm_dp_sideband_msg_tx *txmsg)
+{
+       struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+       unsigned long wait_timeout = msecs_to_jiffies(4000);
+       unsigned long wait_expires = jiffies + wait_timeout;
+       int ret;
+
+       for (;;) {
+               /*
+                * If the driver provides a way for this, change to
+                * poll-waiting for the MST reply interrupt if we didn't receive
+                * it for 50 msec. This would cater for cases where the HPD
+                * pulse signal got lost somewhere, even though the sink raised
+                * the corresponding MST interrupt correctly. One example is the
+                * Club 3D CAC-1557 TypeC -> DP adapter which for some reason
+                * filters out short pulses with a duration less than ~540 usec.
+                *
+                * The poll period is 50 msec to avoid missing an interrupt
+                * after the sink has cleared it (after a 110msec timeout
+                * since it raised the interrupt).
+                */
+               ret = wait_event_timeout(mgr->tx_waitq,
+                                        check_txmsg_state(mgr, txmsg),
+                                        mgr->cbs->poll_hpd_irq ?
+                                               msecs_to_jiffies(50) :
+                                               wait_timeout);
+
+               if (ret || !mgr->cbs->poll_hpd_irq ||
+                   time_after(jiffies, wait_expires))
+                       break;
+
+               mgr->cbs->poll_hpd_irq(mgr);
+       }
+
+       mutex_lock(&mgr->qlock);
+       if (ret > 0) {
+               if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
+                       ret = -EIO;
+                       goto out;
+               }
+       } else {
+               drm_dbg_kms(mgr->dev, "timedout msg send %p %d %d\n",
+                           txmsg, txmsg->state, txmsg->seqno);
+
+               /* dump some state */
+               ret = -EIO;
+
+               /* remove from q */
+               if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED ||
+                   txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND ||
+                   txmsg->state == DRM_DP_SIDEBAND_TX_SENT)
+                       list_del(&txmsg->next);
+       }
+out:
+       if (unlikely(ret == -EIO) && drm_debug_enabled(DRM_UT_DP)) {
+               struct drm_printer p = drm_debug_printer(DBG_PREFIX);
+
+               drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
+       }
+       mutex_unlock(&mgr->qlock);
+
+       drm_dp_mst_kick_tx(mgr);
+       return ret;
+}
+
+static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad)
+{
+       struct drm_dp_mst_branch *mstb;
+
+       mstb = kzalloc(sizeof(*mstb), GFP_KERNEL);
+       if (!mstb)
+               return NULL;
+
+       mstb->lct = lct;
+       if (lct > 1)
+               memcpy(mstb->rad, rad, lct / 2);
+       INIT_LIST_HEAD(&mstb->ports);
+       kref_init(&mstb->topology_kref);
+       kref_init(&mstb->malloc_kref);
+       return mstb;
+}
+
+static void drm_dp_free_mst_branch_device(struct kref *kref)
+{
+       struct drm_dp_mst_branch *mstb =
+               container_of(kref, struct drm_dp_mst_branch, malloc_kref);
+
+       if (mstb->port_parent)
+               drm_dp_mst_put_port_malloc(mstb->port_parent);
+
+       kfree(mstb);
+}
+
+/**
+ * DOC: Branch device and port refcounting
+ *
+ * Topology refcount overview
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The refcounting schemes for &struct drm_dp_mst_branch and &struct
+ * drm_dp_mst_port are somewhat unusual. Both ports and branch devices have
+ * two different kinds of refcounts: topology refcounts, and malloc refcounts.
+ *
+ * Topology refcounts are not exposed to drivers, and are handled internally
+ * by the DP MST helpers. The helpers use them in order to prevent the
+ * in-memory topology state from being changed in the middle of critical
+ * operations like changing the internal state of payload allocations. This
+ * means each branch and port will be considered to be connected to the rest
+ * of the topology until its topology refcount reaches zero. Additionally,
+ * for ports this means that their associated &struct drm_connector will stay
+ * registered with userspace until the port's refcount reaches 0.
+ *
+ * Malloc refcount overview
+ * ~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Malloc references are used to keep a &struct drm_dp_mst_port or &struct
+ * drm_dp_mst_branch allocated even after all of its topology references have
+ * been dropped, so that the driver or MST helpers can safely access each
+ * branch's last known state before it was disconnected from the topology.
+ * When the malloc refcount of a port or branch reaches 0, the memory
+ * allocation containing the &struct drm_dp_mst_branch or &struct
+ * drm_dp_mst_port respectively will be freed.
+ *
+ * For &struct drm_dp_mst_branch, malloc refcounts are not currently exposed
+ * to drivers. As of writing this documentation, there are no drivers that
+ * have a usecase for accessing &struct drm_dp_mst_branch outside of the MST
+ * helpers. Exposing this API to drivers in a race-free manner would take more
+ * tweaking of the refcounting scheme, however patches are welcome provided
+ * there is a legitimate driver usecase for this.
+ *
+ * Refcount relationships in a topology
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Let's take a look at why the relationship between topology and malloc
+ * refcounts is designed the way it is.
+ *
+ * .. kernel-figure:: dp-mst/topology-figure-1.dot
+ *
+ *    An example of topology and malloc refs in a DP MST topology with two
+ *    active payloads. Topology refcount increments are indicated by solid
+ *    lines, and malloc refcount increments are indicated by dashed lines.
+ *    Each starts from the branch which incremented the refcount, and ends at
+ *    the branch to which the refcount belongs to, i.e. the arrow points the
+ *    same way as the C pointers used to reference a structure.
+ *
+ * As you can see in the above figure, every branch increments the topology
+ * refcount of its children, and increments the malloc refcount of its
+ * parent. Additionally, every payload increments the malloc refcount of its
+ * assigned port by 1.
+ *
+ * So, what would happen if MSTB #3 from the above figure was unplugged from
+ * the system, but the driver hadn't yet removed payload #2 from port #3? The
+ * topology would start to look like the figure below.
+ *
+ * .. kernel-figure:: dp-mst/topology-figure-2.dot
+ *
+ *    Ports and branch devices which have been released from memory are
+ *    colored grey, and references which have been removed are colored red.
+ *
+ * Whenever a port or branch device's topology refcount reaches zero, it will
+ * decrement the topology refcounts of all its children, the malloc refcount
+ * of its parent, and finally its own malloc refcount. For MSTB #4 and port
+ * #4, this means they both have been disconnected from the topology and freed
+ * from memory. But, because payload #2 is still holding a reference to port
+ * #3, port #3 is removed from the topology but its &struct drm_dp_mst_port
+ * is still accessible from memory. This also means port #3 has not yet
+ * decremented the malloc refcount of MSTB #3, so its &struct
+ * drm_dp_mst_branch will also stay allocated in memory until port #3's
+ * malloc refcount reaches 0.
+ *
+ * This relationship is necessary because in order to release payload #2, we
+ * need to be able to figure out the last relative of port #3 that's still
+ * connected to the topology. In this case, we would travel up the topology as
+ * shown below.
+ *
+ * .. kernel-figure:: dp-mst/topology-figure-3.dot
+ *
+ * And finally, remove payload #2 by communicating with port #2 through
+ * sideband transactions.
+ */
+
+/**
+ * drm_dp_mst_get_mstb_malloc() - Increment the malloc refcount of a branch
+ * device
+ * @mstb: The &struct drm_dp_mst_branch to increment the malloc refcount of
+ *
+ * Increments &drm_dp_mst_branch.malloc_kref. When
+ * &drm_dp_mst_branch.malloc_kref reaches 0, the memory allocation for @mstb
+ * will be released and @mstb may no longer be used.
+ *
+ * See also: drm_dp_mst_put_mstb_malloc()
+ */
+static void
+drm_dp_mst_get_mstb_malloc(struct drm_dp_mst_branch *mstb)
+{
+       kref_get(&mstb->malloc_kref);
+       drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref));
+}
+
+/**
+ * drm_dp_mst_put_mstb_malloc() - Decrement the malloc refcount of a branch
+ * device
+ * @mstb: The &struct drm_dp_mst_branch to decrement the malloc refcount of
+ *
+ * Decrements &drm_dp_mst_branch.malloc_kref. When
+ * &drm_dp_mst_branch.malloc_kref reaches 0, the memory allocation for @mstb
+ * will be released and @mstb may no longer be used.
+ *
+ * See also: drm_dp_mst_get_mstb_malloc()
+ */
+static void
+drm_dp_mst_put_mstb_malloc(struct drm_dp_mst_branch *mstb)
+{
+       drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref) - 1);
+       kref_put(&mstb->malloc_kref, drm_dp_free_mst_branch_device);
+}
+
+static void drm_dp_free_mst_port(struct kref *kref)
+{
+       struct drm_dp_mst_port *port =
+               container_of(kref, struct drm_dp_mst_port, malloc_kref);
+
+       drm_dp_mst_put_mstb_malloc(port->parent);
+       kfree(port);
+}
+
+/**
+ * drm_dp_mst_get_port_malloc() - Increment the malloc refcount of an MST port
+ * @port: The &struct drm_dp_mst_port to increment the malloc refcount of
+ *
+ * Increments &drm_dp_mst_port.malloc_kref. When &drm_dp_mst_port.malloc_kref
+ * reaches 0, the memory allocation for @port will be released and @port may
+ * no longer be used.
+ *
+ * Because @port could potentially be freed at any time by the DP MST helpers
+ * if &drm_dp_mst_port.malloc_kref reaches 0, including during a call to this
+ * function, drivers that which to make use of &struct drm_dp_mst_port should
+ * ensure that they grab at least one main malloc reference to their MST ports
+ * in &drm_dp_mst_topology_cbs.add_connector. This callback is called before
+ * there is any chance for &drm_dp_mst_port.malloc_kref to reach 0.
+ *
+ * See also: drm_dp_mst_put_port_malloc()
+ */
+void
+drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port)
+{
+       kref_get(&port->malloc_kref);
+       drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->malloc_kref));
+}
+EXPORT_SYMBOL(drm_dp_mst_get_port_malloc);
+
+/**
+ * drm_dp_mst_put_port_malloc() - Decrement the malloc refcount of an MST port
+ * @port: The &struct drm_dp_mst_port to decrement the malloc refcount of
+ *
+ * Decrements &drm_dp_mst_port.malloc_kref. When &drm_dp_mst_port.malloc_kref
+ * reaches 0, the memory allocation for @port will be released and @port may
+ * no longer be used.
+ *
+ * See also: drm_dp_mst_get_port_malloc()
+ */
+void
+drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port)
+{
+       drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->malloc_kref) - 1);
+       kref_put(&port->malloc_kref, drm_dp_free_mst_port);
+}
+EXPORT_SYMBOL(drm_dp_mst_put_port_malloc);
+
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+
+#define STACK_DEPTH 8
+
+static noinline void
+__topology_ref_save(struct drm_dp_mst_topology_mgr *mgr,
+                   struct drm_dp_mst_topology_ref_history *history,
+                   enum drm_dp_mst_topology_ref_type type)
+{
+       struct drm_dp_mst_topology_ref_entry *entry = NULL;
+       depot_stack_handle_t backtrace;
+       ulong stack_entries[STACK_DEPTH];
+       uint n;
+       int i;
+
+       n = stack_trace_save(stack_entries, ARRAY_SIZE(stack_entries), 1);
+       backtrace = stack_depot_save(stack_entries, n, GFP_KERNEL);
+       if (!backtrace)
+               return;
+
+       /* Try to find an existing entry for this backtrace */
+       for (i = 0; i < history->len; i++) {
+               if (history->entries[i].backtrace == backtrace) {
+                       entry = &history->entries[i];
+                       break;
+               }
+       }
+
+       /* Otherwise add one */
+       if (!entry) {
+               struct drm_dp_mst_topology_ref_entry *new;
+               int new_len = history->len + 1;
+
+               new = krealloc(history->entries, sizeof(*new) * new_len,
+                              GFP_KERNEL);
+               if (!new)
+                       return;
+
+               entry = &new[history->len];
+               history->len = new_len;
+               history->entries = new;
+
+               entry->backtrace = backtrace;
+               entry->type = type;
+               entry->count = 0;
+       }
+       entry->count++;
+       entry->ts_nsec = ktime_get_ns();
+}
+
+static int
+topology_ref_history_cmp(const void *a, const void *b)
+{
+       const struct drm_dp_mst_topology_ref_entry *entry_a = a, *entry_b = b;
+
+       if (entry_a->ts_nsec > entry_b->ts_nsec)
+               return 1;
+       else if (entry_a->ts_nsec < entry_b->ts_nsec)
+               return -1;
+       else
+               return 0;
+}
+
+static inline const char *
+topology_ref_type_to_str(enum drm_dp_mst_topology_ref_type type)
+{
+       if (type == DRM_DP_MST_TOPOLOGY_REF_GET)
+               return "get";
+       else
+               return "put";
+}
+
+static void
+__dump_topology_ref_history(struct drm_dp_mst_topology_ref_history *history,
+                           void *ptr, const char *type_str)
+{
+       struct drm_printer p = drm_debug_printer(DBG_PREFIX);
+       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       int i;
+
+       if (!buf)
+               return;
+
+       if (!history->len)
+               goto out;
+
+       /* First, sort the list so that it goes from oldest to newest
+        * reference entry
+        */
+       sort(history->entries, history->len, sizeof(*history->entries),
+            topology_ref_history_cmp, NULL);
+
+       drm_printf(&p, "%s (%p) topology count reached 0, dumping history:\n",
+                  type_str, ptr);
+
+       for (i = 0; i < history->len; i++) {
+               const struct drm_dp_mst_topology_ref_entry *entry =
+                       &history->entries[i];
+               u64 ts_nsec = entry->ts_nsec;
+               u32 rem_nsec = do_div(ts_nsec, 1000000000);
+
+               stack_depot_snprint(entry->backtrace, buf, PAGE_SIZE, 4);
+
+               drm_printf(&p, "  %d %ss (last at %5llu.%06u):\n%s",
+                          entry->count,
+                          topology_ref_type_to_str(entry->type),
+                          ts_nsec, rem_nsec / 1000, buf);
+       }
+
+       /* Now free the history, since this is the only time we expose it */
+       kfree(history->entries);
+out:
+       kfree(buf);
+}
+
+static __always_inline void
+drm_dp_mst_dump_mstb_topology_history(struct drm_dp_mst_branch *mstb)
+{
+       __dump_topology_ref_history(&mstb->topology_ref_history, mstb,
+                                   "MSTB");
+}
+
+static __always_inline void
+drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port)
+{
+       __dump_topology_ref_history(&port->topology_ref_history, port,
+                                   "Port");
+}
+
+static __always_inline void
+save_mstb_topology_ref(struct drm_dp_mst_branch *mstb,
+                      enum drm_dp_mst_topology_ref_type type)
+{
+       __topology_ref_save(mstb->mgr, &mstb->topology_ref_history, type);
+}
+
+static __always_inline void
+save_port_topology_ref(struct drm_dp_mst_port *port,
+                      enum drm_dp_mst_topology_ref_type type)
+{
+       __topology_ref_save(port->mgr, &port->topology_ref_history, type);
+}
+
+static inline void
+topology_ref_history_lock(struct drm_dp_mst_topology_mgr *mgr)
+{
+       mutex_lock(&mgr->topology_ref_history_lock);
+}
+
+static inline void
+topology_ref_history_unlock(struct drm_dp_mst_topology_mgr *mgr)
+{
+       mutex_unlock(&mgr->topology_ref_history_lock);
+}
+#else
+static inline void
+topology_ref_history_lock(struct drm_dp_mst_topology_mgr *mgr) {}
+static inline void
+topology_ref_history_unlock(struct drm_dp_mst_topology_mgr *mgr) {}
+static inline void
+drm_dp_mst_dump_mstb_topology_history(struct drm_dp_mst_branch *mstb) {}
+static inline void
+drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port) {}
+#define save_mstb_topology_ref(mstb, type)
+#define save_port_topology_ref(port, type)
+#endif
+
+static void drm_dp_destroy_mst_branch_device(struct kref *kref)
+{
+       struct drm_dp_mst_branch *mstb =
+               container_of(kref, struct drm_dp_mst_branch, topology_kref);
+       struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+
+       drm_dp_mst_dump_mstb_topology_history(mstb);
+
+       INIT_LIST_HEAD(&mstb->destroy_next);
+
+       /*
+        * This can get called under mgr->mutex, so we need to perform the
+        * actual destruction of the mstb in another worker
+        */
+       mutex_lock(&mgr->delayed_destroy_lock);
+       list_add(&mstb->destroy_next, &mgr->destroy_branch_device_list);
+       mutex_unlock(&mgr->delayed_destroy_lock);
+       queue_work(mgr->delayed_destroy_wq, &mgr->delayed_destroy_work);
+}
+
+/**
+ * drm_dp_mst_topology_try_get_mstb() - Increment the topology refcount of a
+ * branch device unless it's zero
+ * @mstb: &struct drm_dp_mst_branch to increment the topology refcount of
+ *
+ * Attempts to grab a topology reference to @mstb, if it hasn't yet been
+ * removed from the topology (e.g. &drm_dp_mst_branch.topology_kref has
+ * reached 0). Holding a topology reference implies that a malloc reference
+ * will be held to @mstb as long as the user holds the topology reference.
+ *
+ * Care should be taken to ensure that the user has at least one malloc
+ * reference to @mstb. If you already have a topology reference to @mstb, you
+ * should use drm_dp_mst_topology_get_mstb() instead.
+ *
+ * See also:
+ * drm_dp_mst_topology_get_mstb()
+ * drm_dp_mst_topology_put_mstb()
+ *
+ * Returns:
+ * * 1: A topology reference was grabbed successfully
+ * * 0: @port is no longer in the topology, no reference was grabbed
+ */
+static int __must_check
+drm_dp_mst_topology_try_get_mstb(struct drm_dp_mst_branch *mstb)
+{
+       int ret;
+
+       topology_ref_history_lock(mstb->mgr);
+       ret = kref_get_unless_zero(&mstb->topology_kref);
+       if (ret) {
+               drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref));
+               save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_GET);
+       }
+
+       topology_ref_history_unlock(mstb->mgr);
+
+       return ret;
+}
+
+/**
+ * drm_dp_mst_topology_get_mstb() - Increment the topology refcount of a
+ * branch device
+ * @mstb: The &struct drm_dp_mst_branch to increment the topology refcount of
+ *
+ * Increments &drm_dp_mst_branch.topology_refcount without checking whether or
+ * not it's already reached 0. This is only valid to use in scenarios where
+ * you are already guaranteed to have at least one active topology reference
+ * to @mstb. Otherwise, drm_dp_mst_topology_try_get_mstb() must be used.
+ *
+ * See also:
+ * drm_dp_mst_topology_try_get_mstb()
+ * drm_dp_mst_topology_put_mstb()
+ */
+static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb)
+{
+       topology_ref_history_lock(mstb->mgr);
+
+       save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_GET);
+       WARN_ON(kref_read(&mstb->topology_kref) == 0);
+       kref_get(&mstb->topology_kref);
+       drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref));
+
+       topology_ref_history_unlock(mstb->mgr);
+}
+
+/**
+ * drm_dp_mst_topology_put_mstb() - release a topology reference to a branch
+ * device
+ * @mstb: The &struct drm_dp_mst_branch to release the topology reference from
+ *
+ * Releases a topology reference from @mstb by decrementing
+ * &drm_dp_mst_branch.topology_kref.
+ *
+ * See also:
+ * drm_dp_mst_topology_try_get_mstb()
+ * drm_dp_mst_topology_get_mstb()
+ */
+static void
+drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb)
+{
+       topology_ref_history_lock(mstb->mgr);
+
+       drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref) - 1);
+       save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_PUT);
+
+       topology_ref_history_unlock(mstb->mgr);
+       kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
+}
+
+static void drm_dp_destroy_port(struct kref *kref)
+{
+       struct drm_dp_mst_port *port =
+               container_of(kref, struct drm_dp_mst_port, topology_kref);
+       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+
+       drm_dp_mst_dump_port_topology_history(port);
+
+       /* There's nothing that needs locking to destroy an input port yet */
+       if (port->input) {
+               drm_dp_mst_put_port_malloc(port);
+               return;
+       }
+
+       kfree(port->cached_edid);
+
+       /*
+        * we can't destroy the connector here, as we might be holding the
+        * mode_config.mutex from an EDID retrieval
+        */
+       mutex_lock(&mgr->delayed_destroy_lock);
+       list_add(&port->next, &mgr->destroy_port_list);
+       mutex_unlock(&mgr->delayed_destroy_lock);
+       queue_work(mgr->delayed_destroy_wq, &mgr->delayed_destroy_work);
+}
+
+/**
+ * drm_dp_mst_topology_try_get_port() - Increment the topology refcount of a
+ * port unless it's zero
+ * @port: &struct drm_dp_mst_port to increment the topology refcount of
+ *
+ * Attempts to grab a topology reference to @port, if it hasn't yet been
+ * removed from the topology (e.g. &drm_dp_mst_port.topology_kref has reached
+ * 0). Holding a topology reference implies that a malloc reference will be
+ * held to @port as long as the user holds the topology reference.
+ *
+ * Care should be taken to ensure that the user has at least one malloc
+ * reference to @port. If you already have a topology reference to @port, you
+ * should use drm_dp_mst_topology_get_port() instead.
+ *
+ * See also:
+ * drm_dp_mst_topology_get_port()
+ * drm_dp_mst_topology_put_port()
+ *
+ * Returns:
+ * * 1: A topology reference was grabbed successfully
+ * * 0: @port is no longer in the topology, no reference was grabbed
+ */
+static int __must_check
+drm_dp_mst_topology_try_get_port(struct drm_dp_mst_port *port)
+{
+       int ret;
+
+       topology_ref_history_lock(port->mgr);
+       ret = kref_get_unless_zero(&port->topology_kref);
+       if (ret) {
+               drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->topology_kref));
+               save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_GET);
+       }
+
+       topology_ref_history_unlock(port->mgr);
+       return ret;
+}
+
+/**
+ * drm_dp_mst_topology_get_port() - Increment the topology refcount of a port
+ * @port: The &struct drm_dp_mst_port to increment the topology refcount of
+ *
+ * Increments &drm_dp_mst_port.topology_refcount without checking whether or
+ * not it's already reached 0. This is only valid to use in scenarios where
+ * you are already guaranteed to have at least one active topology reference
+ * to @port. Otherwise, drm_dp_mst_topology_try_get_port() must be used.
+ *
+ * See also:
+ * drm_dp_mst_topology_try_get_port()
+ * drm_dp_mst_topology_put_port()
+ */
+static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port)
+{
+       topology_ref_history_lock(port->mgr);
+
+       WARN_ON(kref_read(&port->topology_kref) == 0);
+       kref_get(&port->topology_kref);
+       drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->topology_kref));
+       save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_GET);
+
+       topology_ref_history_unlock(port->mgr);
+}
+
+/**
+ * drm_dp_mst_topology_put_port() - release a topology reference to a port
+ * @port: The &struct drm_dp_mst_port to release the topology reference from
+ *
+ * Releases a topology reference from @port by decrementing
+ * &drm_dp_mst_port.topology_kref.
+ *
+ * See also:
+ * drm_dp_mst_topology_try_get_port()
+ * drm_dp_mst_topology_get_port()
+ */
+static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port)
+{
+       topology_ref_history_lock(port->mgr);
+
+       drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->topology_kref) - 1);
+       save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_PUT);
+
+       topology_ref_history_unlock(port->mgr);
+       kref_put(&port->topology_kref, drm_dp_destroy_port);
+}
+
+static struct drm_dp_mst_branch *
+drm_dp_mst_topology_get_mstb_validated_locked(struct drm_dp_mst_branch *mstb,
+                                             struct drm_dp_mst_branch *to_find)
+{
+       struct drm_dp_mst_port *port;
+       struct drm_dp_mst_branch *rmstb;
+
+       if (to_find == mstb)
+               return mstb;
+
+       list_for_each_entry(port, &mstb->ports, next) {
+               if (port->mstb) {
+                       rmstb = drm_dp_mst_topology_get_mstb_validated_locked(
+                           port->mstb, to_find);
+                       if (rmstb)
+                               return rmstb;
+               }
+       }
+       return NULL;
+}
+
+static struct drm_dp_mst_branch *
+drm_dp_mst_topology_get_mstb_validated(struct drm_dp_mst_topology_mgr *mgr,
+                                      struct drm_dp_mst_branch *mstb)
+{
+       struct drm_dp_mst_branch *rmstb = NULL;
+
+       mutex_lock(&mgr->lock);
+       if (mgr->mst_primary) {
+               rmstb = drm_dp_mst_topology_get_mstb_validated_locked(
+                   mgr->mst_primary, mstb);
+
+               if (rmstb && !drm_dp_mst_topology_try_get_mstb(rmstb))
+                       rmstb = NULL;
+       }
+       mutex_unlock(&mgr->lock);
+       return rmstb;
+}
+
+static struct drm_dp_mst_port *
+drm_dp_mst_topology_get_port_validated_locked(struct drm_dp_mst_branch *mstb,
+                                             struct drm_dp_mst_port *to_find)
+{
+       struct drm_dp_mst_port *port, *mport;
+
+       list_for_each_entry(port, &mstb->ports, next) {
+               if (port == to_find)
+                       return port;
+
+               if (port->mstb) {
+                       mport = drm_dp_mst_topology_get_port_validated_locked(
+                           port->mstb, to_find);
+                       if (mport)
+                               return mport;
+               }
+       }
+       return NULL;
+}
+
+static struct drm_dp_mst_port *
+drm_dp_mst_topology_get_port_validated(struct drm_dp_mst_topology_mgr *mgr,
+                                      struct drm_dp_mst_port *port)
+{
+       struct drm_dp_mst_port *rport = NULL;
+
+       mutex_lock(&mgr->lock);
+       if (mgr->mst_primary) {
+               rport = drm_dp_mst_topology_get_port_validated_locked(
+                   mgr->mst_primary, port);
+
+               if (rport && !drm_dp_mst_topology_try_get_port(rport))
+                       rport = NULL;
+       }
+       mutex_unlock(&mgr->lock);
+       return rport;
+}
+
+static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num)
+{
+       struct drm_dp_mst_port *port;
+       int ret;
+
+       list_for_each_entry(port, &mstb->ports, next) {
+               if (port->port_num == port_num) {
+                       ret = drm_dp_mst_topology_try_get_port(port);
+                       return ret ? port : NULL;
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * calculate a new RAD for this MST branch device
+ * if parent has an LCT of 2 then it has 1 nibble of RAD,
+ * if parent has an LCT of 3 then it has 2 nibbles of RAD,
+ */
+static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
+                                u8 *rad)
+{
+       int parent_lct = port->parent->lct;
+       int shift = 4;
+       int idx = (parent_lct - 1) / 2;
+
+       if (parent_lct > 1) {
+               memcpy(rad, port->parent->rad, idx + 1);
+               shift = (parent_lct % 2) ? 4 : 0;
+       } else
+               rad[0] = 0;
+
+       rad[idx] |= port->port_num << shift;
+       return parent_lct + 1;
+}
+
+static bool drm_dp_mst_is_end_device(u8 pdt, bool mcs)
+{
+       switch (pdt) {
+       case DP_PEER_DEVICE_DP_LEGACY_CONV:
+       case DP_PEER_DEVICE_SST_SINK:
+               return true;
+       case DP_PEER_DEVICE_MST_BRANCHING:
+               /* For sst branch device */
+               if (!mcs)
+                       return true;
+
+               return false;
+       }
+       return true;
+}
+
+static int
+drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt,
+                   bool new_mcs)
+{
+       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+       struct drm_dp_mst_branch *mstb;
+       u8 rad[8], lct;
+       int ret = 0;
+
+       if (port->pdt == new_pdt && port->mcs == new_mcs)
+               return 0;
+
+       /* Teardown the old pdt, if there is one */
+       if (port->pdt != DP_PEER_DEVICE_NONE) {
+               if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
+                       /*
+                        * If the new PDT would also have an i2c bus,
+                        * don't bother with reregistering it
+                        */
+                       if (new_pdt != DP_PEER_DEVICE_NONE &&
+                           drm_dp_mst_is_end_device(new_pdt, new_mcs)) {
+                               port->pdt = new_pdt;
+                               port->mcs = new_mcs;
+                               return 0;
+                       }
+
+                       /* remove i2c over sideband */
+                       drm_dp_mst_unregister_i2c_bus(port);
+               } else {
+                       mutex_lock(&mgr->lock);
+                       drm_dp_mst_topology_put_mstb(port->mstb);
+                       port->mstb = NULL;
+                       mutex_unlock(&mgr->lock);
+               }
+       }
+
+       port->pdt = new_pdt;
+       port->mcs = new_mcs;
+
+       if (port->pdt != DP_PEER_DEVICE_NONE) {
+               if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
+                       /* add i2c over sideband */
+                       ret = drm_dp_mst_register_i2c_bus(port);
+               } else {
+                       lct = drm_dp_calculate_rad(port, rad);
+                       mstb = drm_dp_add_mst_branch_device(lct, rad);
+                       if (!mstb) {
+                               ret = -ENOMEM;
+                               drm_err(mgr->dev, "Failed to create MSTB for port %p", port);
+                               goto out;
+                       }
+
+                       mutex_lock(&mgr->lock);
+                       port->mstb = mstb;
+                       mstb->mgr = port->mgr;
+                       mstb->port_parent = port;
+
+                       /*
+                        * Make sure this port's memory allocation stays
+                        * around until its child MSTB releases it
+                        */
+                       drm_dp_mst_get_port_malloc(port);
+                       mutex_unlock(&mgr->lock);
+
+                       /* And make sure we send a link address for this */
+                       ret = 1;
+               }
+       }
+
+out:
+       if (ret < 0)
+               port->pdt = DP_PEER_DEVICE_NONE;
+       return ret;
+}
+
+/**
+ * drm_dp_mst_dpcd_read() - read a series of bytes from the DPCD via sideband
+ * @aux: Fake sideband AUX CH
+ * @offset: address of the (first) register to read
+ * @buffer: buffer to store the register values
+ * @size: number of bytes in @buffer
+ *
+ * Performs the same functionality for remote devices via
+ * sideband messaging as drm_dp_dpcd_read() does for local
+ * devices via actual AUX CH.
+ *
+ * Return: Number of bytes read, or negative error code on failure.
+ */
+ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
+                            unsigned int offset, void *buffer, size_t size)
+{
+       struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port,
+                                                   aux);
+
+       return drm_dp_send_dpcd_read(port->mgr, port,
+                                    offset, size, buffer);
+}
+
+/**
+ * drm_dp_mst_dpcd_write() - write a series of bytes to the DPCD via sideband
+ * @aux: Fake sideband AUX CH
+ * @offset: address of the (first) register to write
+ * @buffer: buffer containing the values to write
+ * @size: number of bytes in @buffer
+ *
+ * Performs the same functionality for remote devices via
+ * sideband messaging as drm_dp_dpcd_write() does for local
+ * devices via actual AUX CH.
+ *
+ * Return: number of bytes written on success, negative error code on failure.
+ */
+ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux,
+                             unsigned int offset, void *buffer, size_t size)
+{
+       struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port,
+                                                   aux);
+
+       return drm_dp_send_dpcd_write(port->mgr, port,
+                                     offset, size, buffer);
+}
+
+static int drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, u8 *guid)
+{
+       int ret = 0;
+
+       memcpy(mstb->guid, guid, 16);
+
+       if (!drm_dp_validate_guid(mstb->mgr, mstb->guid)) {
+               if (mstb->port_parent) {
+                       ret = drm_dp_send_dpcd_write(mstb->mgr,
+                                                    mstb->port_parent,
+                                                    DP_GUID, 16, mstb->guid);
+               } else {
+                       ret = drm_dp_dpcd_write(mstb->mgr->aux,
+                                               DP_GUID, mstb->guid, 16);
+               }
+       }
+
+       if (ret < 16 && ret > 0)
+               return -EPROTO;
+
+       return ret == 16 ? 0 : ret;
+}
+
+static void build_mst_prop_path(const struct drm_dp_mst_branch *mstb,
+                               int pnum,
+                               char *proppath,
+                               size_t proppath_size)
+{
+       int i;
+       char temp[8];
+
+       snprintf(proppath, proppath_size, "mst:%d", mstb->mgr->conn_base_id);
+       for (i = 0; i < (mstb->lct - 1); i++) {
+               int shift = (i % 2) ? 0 : 4;
+               int port_num = (mstb->rad[i / 2] >> shift) & 0xf;
+
+               snprintf(temp, sizeof(temp), "-%d", port_num);
+               strlcat(proppath, temp, proppath_size);
+       }
+       snprintf(temp, sizeof(temp), "-%d", pnum);
+       strlcat(proppath, temp, proppath_size);
+}
+
+/**
+ * drm_dp_mst_connector_late_register() - Late MST connector registration
+ * @connector: The MST connector
+ * @port: The MST port for this connector
+ *
+ * Helper to register the remote aux device for this MST port. Drivers should
+ * call this from their mst connector's late_register hook to enable MST aux
+ * devices.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int drm_dp_mst_connector_late_register(struct drm_connector *connector,
+                                      struct drm_dp_mst_port *port)
+{
+       drm_dbg_kms(port->mgr->dev, "registering %s remote bus for %s\n",
+                   port->aux.name, connector->kdev->kobj.name);
+
+       port->aux.dev = connector->kdev;
+       return drm_dp_aux_register_devnode(&port->aux);
+}
+EXPORT_SYMBOL(drm_dp_mst_connector_late_register);
+
+/**
+ * drm_dp_mst_connector_early_unregister() - Early MST connector unregistration
+ * @connector: The MST connector
+ * @port: The MST port for this connector
+ *
+ * Helper to unregister the remote aux device for this MST port, registered by
+ * drm_dp_mst_connector_late_register(). Drivers should call this from their mst
+ * connector's early_unregister hook.
+ */
+void drm_dp_mst_connector_early_unregister(struct drm_connector *connector,
+                                          struct drm_dp_mst_port *port)
+{
+       drm_dbg_kms(port->mgr->dev, "unregistering %s remote bus for %s\n",
+                   port->aux.name, connector->kdev->kobj.name);
+       drm_dp_aux_unregister_devnode(&port->aux);
+}
+EXPORT_SYMBOL(drm_dp_mst_connector_early_unregister);
+
+static void
+drm_dp_mst_port_add_connector(struct drm_dp_mst_branch *mstb,
+                             struct drm_dp_mst_port *port)
+{
+       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+       char proppath[255];
+       int ret;
+
+       build_mst_prop_path(mstb, port->port_num, proppath, sizeof(proppath));
+       port->connector = mgr->cbs->add_connector(mgr, port, proppath);
+       if (!port->connector) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       if (port->pdt != DP_PEER_DEVICE_NONE &&
+           drm_dp_mst_is_end_device(port->pdt, port->mcs) &&
+           port->port_num >= DP_MST_LOGICAL_PORT_0)
+               port->cached_edid = drm_get_edid(port->connector,
+                                                &port->aux.ddc);
+
+       drm_connector_register(port->connector);
+       return;
+
+error:
+       drm_err(mgr->dev, "Failed to create connector for port %p: %d\n", port, ret);
+}
+
+/*
+ * Drop a topology reference, and unlink the port from the in-memory topology
+ * layout
+ */
+static void
+drm_dp_mst_topology_unlink_port(struct drm_dp_mst_topology_mgr *mgr,
+                               struct drm_dp_mst_port *port)
+{
+       mutex_lock(&mgr->lock);
+       port->parent->num_ports--;
+       list_del(&port->next);
+       mutex_unlock(&mgr->lock);
+       drm_dp_mst_topology_put_port(port);
+}
+
+static struct drm_dp_mst_port *
+drm_dp_mst_add_port(struct drm_device *dev,
+                   struct drm_dp_mst_topology_mgr *mgr,
+                   struct drm_dp_mst_branch *mstb, u8 port_number)
+{
+       struct drm_dp_mst_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
+
+       if (!port)
+               return NULL;
+
+       kref_init(&port->topology_kref);
+       kref_init(&port->malloc_kref);
+       port->parent = mstb;
+       port->port_num = port_number;
+       port->mgr = mgr;
+       port->aux.name = "DPMST";
+       port->aux.dev = dev->dev;
+       port->aux.is_remote = true;
+
+       /* initialize the MST downstream port's AUX crc work queue */
+       port->aux.drm_dev = dev;
+       drm_dp_remote_aux_init(&port->aux);
+
+       /*
+        * Make sure the memory allocation for our parent branch stays
+        * around until our own memory allocation is released
+        */
+       drm_dp_mst_get_mstb_malloc(mstb);
+
+       return port;
+}
+
+static int
+drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
+                                   struct drm_device *dev,
+                                   struct drm_dp_link_addr_reply_port *port_msg)
+{
+       struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+       struct drm_dp_mst_port *port;
+       int old_ddps = 0, ret;
+       u8 new_pdt = DP_PEER_DEVICE_NONE;
+       bool new_mcs = 0;
+       bool created = false, send_link_addr = false, changed = false;
+
+       port = drm_dp_get_port(mstb, port_msg->port_number);
+       if (!port) {
+               port = drm_dp_mst_add_port(dev, mgr, mstb,
+                                          port_msg->port_number);
+               if (!port)
+                       return -ENOMEM;
+               created = true;
+               changed = true;
+       } else if (!port->input && port_msg->input_port && port->connector) {
+               /* Since port->connector can't be changed here, we create a
+                * new port if input_port changes from 0 to 1
+                */
+               drm_dp_mst_topology_unlink_port(mgr, port);
+               drm_dp_mst_topology_put_port(port);
+               port = drm_dp_mst_add_port(dev, mgr, mstb,
+                                          port_msg->port_number);
+               if (!port)
+                       return -ENOMEM;
+               changed = true;
+               created = true;
+       } else if (port->input && !port_msg->input_port) {
+               changed = true;
+       } else if (port->connector) {
+               /* We're updating a port that's exposed to userspace, so do it
+                * under lock
+                */
+               drm_modeset_lock(&mgr->base.lock, NULL);
+
+               old_ddps = port->ddps;
+               changed = port->ddps != port_msg->ddps ||
+                       (port->ddps &&
+                        (port->ldps != port_msg->legacy_device_plug_status ||
+                         port->dpcd_rev != port_msg->dpcd_revision ||
+                         port->mcs != port_msg->mcs ||
+                         port->pdt != port_msg->peer_device_type ||
+                         port->num_sdp_stream_sinks !=
+                         port_msg->num_sdp_stream_sinks));
+       }
+
+       port->input = port_msg->input_port;
+       if (!port->input)
+               new_pdt = port_msg->peer_device_type;
+       new_mcs = port_msg->mcs;
+       port->ddps = port_msg->ddps;
+       port->ldps = port_msg->legacy_device_plug_status;
+       port->dpcd_rev = port_msg->dpcd_revision;
+       port->num_sdp_streams = port_msg->num_sdp_streams;
+       port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks;
+
+       /* manage mstb port lists with mgr lock - take a reference
+          for this list */
+       if (created) {
+               mutex_lock(&mgr->lock);
+               drm_dp_mst_topology_get_port(port);
+               list_add(&port->next, &mstb->ports);
+               mstb->num_ports++;
+               mutex_unlock(&mgr->lock);
+       }
+
+       /*
+        * Reprobe PBN caps on both hotplug, and when re-probing the link
+        * for our parent mstb
+        */
+       if (old_ddps != port->ddps || !created) {
+               if (port->ddps && !port->input) {
+                       ret = drm_dp_send_enum_path_resources(mgr, mstb,
+                                                             port);
+                       if (ret == 1)
+                               changed = true;
+               } else {
+                       port->full_pbn = 0;
+               }
+       }
+
+       ret = drm_dp_port_set_pdt(port, new_pdt, new_mcs);
+       if (ret == 1) {
+               send_link_addr = true;
+       } else if (ret < 0) {
+               drm_err(dev, "Failed to change PDT on port %p: %d\n", port, ret);
+               goto fail;
+       }
+
+       /*
+        * If this port wasn't just created, then we're reprobing because
+        * we're coming out of suspend. In this case, always resend the link
+        * address if there's an MSTB on this port
+        */
+       if (!created && port->pdt == DP_PEER_DEVICE_MST_BRANCHING &&
+           port->mcs)
+               send_link_addr = true;
+
+       if (port->connector)
+               drm_modeset_unlock(&mgr->base.lock);
+       else if (!port->input)
+               drm_dp_mst_port_add_connector(mstb, port);
+
+       if (send_link_addr && port->mstb) {
+               ret = drm_dp_send_link_address(mgr, port->mstb);
+               if (ret == 1) /* MSTB below us changed */
+                       changed = true;
+               else if (ret < 0)
+                       goto fail_put;
+       }
+
+       /* put reference to this port */
+       drm_dp_mst_topology_put_port(port);
+       return changed;
+
+fail:
+       drm_dp_mst_topology_unlink_port(mgr, port);
+       if (port->connector)
+               drm_modeset_unlock(&mgr->base.lock);
+fail_put:
+       drm_dp_mst_topology_put_port(port);
+       return ret;
+}
+
+static void
+drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
+                           struct drm_dp_connection_status_notify *conn_stat)
+{
+       struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+       struct drm_dp_mst_port *port;
+       int old_ddps, ret;
+       u8 new_pdt;
+       bool new_mcs;
+       bool dowork = false, create_connector = false;
+
+       port = drm_dp_get_port(mstb, conn_stat->port_number);
+       if (!port)
+               return;
+
+       if (port->connector) {
+               if (!port->input && conn_stat->input_port) {
+                       /*
+                        * We can't remove a connector from an already exposed
+                        * port, so just throw the port out and make sure we
+                        * reprobe the link address of it's parent MSTB
+                        */
+                       drm_dp_mst_topology_unlink_port(mgr, port);
+                       mstb->link_address_sent = false;
+                       dowork = true;
+                       goto out;
+               }
+
+               /* Locking is only needed if the port's exposed to userspace */
+               drm_modeset_lock(&mgr->base.lock, NULL);
+       } else if (port->input && !conn_stat->input_port) {
+               create_connector = true;
+               /* Reprobe link address so we get num_sdp_streams */
+               mstb->link_address_sent = false;
+               dowork = true;
+       }
+
+       old_ddps = port->ddps;
+       port->input = conn_stat->input_port;
+       port->ldps = conn_stat->legacy_device_plug_status;
+       port->ddps = conn_stat->displayport_device_plug_status;
+
+       if (old_ddps != port->ddps) {
+               if (port->ddps && !port->input)
+                       drm_dp_send_enum_path_resources(mgr, mstb, port);
+               else
+                       port->full_pbn = 0;
+       }
+
+       new_pdt = port->input ? DP_PEER_DEVICE_NONE : conn_stat->peer_device_type;
+       new_mcs = conn_stat->message_capability_status;
+       ret = drm_dp_port_set_pdt(port, new_pdt, new_mcs);
+       if (ret == 1) {
+               dowork = true;
+       } else if (ret < 0) {
+               drm_err(mgr->dev, "Failed to change PDT for port %p: %d\n", port, ret);
+               dowork = false;
+       }
+
+       if (port->connector)
+               drm_modeset_unlock(&mgr->base.lock);
+       else if (create_connector)
+               drm_dp_mst_port_add_connector(mstb, port);
+
+out:
+       drm_dp_mst_topology_put_port(port);
+       if (dowork)
+               queue_work(system_long_wq, &mstb->mgr->work);
+}
+
+static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr,
+                                                              u8 lct, u8 *rad)
+{
+       struct drm_dp_mst_branch *mstb;
+       struct drm_dp_mst_port *port;
+       int i, ret;
+       /* find the port by iterating down */
+
+       mutex_lock(&mgr->lock);
+       mstb = mgr->mst_primary;
+
+       if (!mstb)
+               goto out;
+
+       for (i = 0; i < lct - 1; i++) {
+               int shift = (i % 2) ? 0 : 4;
+               int port_num = (rad[i / 2] >> shift) & 0xf;
+
+               list_for_each_entry(port, &mstb->ports, next) {
+                       if (port->port_num == port_num) {
+                               mstb = port->mstb;
+                               if (!mstb) {
+                                       drm_err(mgr->dev,
+                                               "failed to lookup MSTB with lct %d, rad %02x\n",
+                                               lct, rad[0]);
+                                       goto out;
+                               }
+
+                               break;
+                       }
+               }
+       }
+       ret = drm_dp_mst_topology_try_get_mstb(mstb);
+       if (!ret)
+               mstb = NULL;
+out:
+       mutex_unlock(&mgr->lock);
+       return mstb;
+}
+
+static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
+       struct drm_dp_mst_branch *mstb,
+       const uint8_t *guid)
+{
+       struct drm_dp_mst_branch *found_mstb;
+       struct drm_dp_mst_port *port;
+
+       if (memcmp(mstb->guid, guid, 16) == 0)
+               return mstb;
+
+
+       list_for_each_entry(port, &mstb->ports, next) {
+               if (!port->mstb)
+                       continue;
+
+               found_mstb = get_mst_branch_device_by_guid_helper(port->mstb, guid);
+
+               if (found_mstb)
+                       return found_mstb;
+       }
+
+       return NULL;
+}
+
+static struct drm_dp_mst_branch *
+drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr *mgr,
+                                    const uint8_t *guid)
+{
+       struct drm_dp_mst_branch *mstb;
+       int ret;
+
+       /* find the port by iterating down */
+       mutex_lock(&mgr->lock);
+
+       mstb = get_mst_branch_device_by_guid_helper(mgr->mst_primary, guid);
+       if (mstb) {
+               ret = drm_dp_mst_topology_try_get_mstb(mstb);
+               if (!ret)
+                       mstb = NULL;
+       }
+
+       mutex_unlock(&mgr->lock);
+       return mstb;
+}
+
+static int drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+                                              struct drm_dp_mst_branch *mstb)
+{
+       struct drm_dp_mst_port *port;
+       int ret;
+       bool changed = false;
+
+       if (!mstb->link_address_sent) {
+               ret = drm_dp_send_link_address(mgr, mstb);
+               if (ret == 1)
+                       changed = true;
+               else if (ret < 0)
+                       return ret;
+       }
+
+       list_for_each_entry(port, &mstb->ports, next) {
+               struct drm_dp_mst_branch *mstb_child = NULL;
+
+               if (port->input || !port->ddps)
+                       continue;
+
+               if (port->mstb)
+                       mstb_child = drm_dp_mst_topology_get_mstb_validated(
+                           mgr, port->mstb);
+
+               if (mstb_child) {
+                       ret = drm_dp_check_and_send_link_address(mgr,
+                                                                mstb_child);
+                       drm_dp_mst_topology_put_mstb(mstb_child);
+                       if (ret == 1)
+                               changed = true;
+                       else if (ret < 0)
+                               return ret;
+               }
+       }
+
+       return changed;
+}
+
+static void drm_dp_mst_link_probe_work(struct work_struct *work)
+{
+       struct drm_dp_mst_topology_mgr *mgr =
+               container_of(work, struct drm_dp_mst_topology_mgr, work);
+       struct drm_device *dev = mgr->dev;
+       struct drm_dp_mst_branch *mstb;
+       int ret;
+       bool clear_payload_id_table;
+
+       mutex_lock(&mgr->probe_lock);
+
+       mutex_lock(&mgr->lock);
+       clear_payload_id_table = !mgr->payload_id_table_cleared;
+       mgr->payload_id_table_cleared = true;
+
+       mstb = mgr->mst_primary;
+       if (mstb) {
+               ret = drm_dp_mst_topology_try_get_mstb(mstb);
+               if (!ret)
+                       mstb = NULL;
+       }
+       mutex_unlock(&mgr->lock);
+       if (!mstb) {
+               mutex_unlock(&mgr->probe_lock);
+               return;
+       }
+
+       /*
+        * Certain branch devices seem to incorrectly report an available_pbn
+        * of 0 on downstream sinks, even after clearing the
+        * DP_PAYLOAD_ALLOCATE_* registers in
+        * drm_dp_mst_topology_mgr_set_mst(). Namely, the CableMatters USB-C
+        * 2x DP hub. Sending a CLEAR_PAYLOAD_ID_TABLE message seems to make
+        * things work again.
+        */
+       if (clear_payload_id_table) {
+               drm_dbg_kms(dev, "Clearing payload ID table\n");
+               drm_dp_send_clear_payload_id_table(mgr, mstb);
+       }
+
+       ret = drm_dp_check_and_send_link_address(mgr, mstb);
+       drm_dp_mst_topology_put_mstb(mstb);
+
+       mutex_unlock(&mgr->probe_lock);
+       if (ret > 0)
+               drm_kms_helper_hotplug_event(dev);
+}
+
+static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
+                                u8 *guid)
+{
+       u64 salt;
+
+       if (memchr_inv(guid, 0, 16))
+               return true;
+
+       salt = get_jiffies_64();
+
+       memcpy(&guid[0], &salt, sizeof(u64));
+       memcpy(&guid[8], &salt, sizeof(u64));
+
+       return false;
+}
+
+static void build_dpcd_read(struct drm_dp_sideband_msg_tx *msg,
+                           u8 port_num, u32 offset, u8 num_bytes)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       req.req_type = DP_REMOTE_DPCD_READ;
+       req.u.dpcd_read.port_number = port_num;
+       req.u.dpcd_read.dpcd_address = offset;
+       req.u.dpcd_read.num_bytes = num_bytes;
+       drm_dp_encode_sideband_req(&req, msg);
+}
+
+static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr,
+                                   bool up, u8 *msg, int len)
+{
+       int ret;
+       int regbase = up ? DP_SIDEBAND_MSG_UP_REP_BASE : DP_SIDEBAND_MSG_DOWN_REQ_BASE;
+       int tosend, total, offset;
+       int retries = 0;
+
+retry:
+       total = len;
+       offset = 0;
+       do {
+               tosend = min3(mgr->max_dpcd_transaction_bytes, 16, total);
+
+               ret = drm_dp_dpcd_write(mgr->aux, regbase + offset,
+                                       &msg[offset],
+                                       tosend);
+               if (ret != tosend) {
+                       if (ret == -EIO && retries < 5) {
+                               retries++;
+                               goto retry;
+                       }
+                       drm_dbg_kms(mgr->dev, "failed to dpcd write %d %d\n", tosend, ret);
+
+                       return -EIO;
+               }
+               offset += tosend;
+               total -= tosend;
+       } while (total > 0);
+       return 0;
+}
+
+static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr,
+                                 struct drm_dp_sideband_msg_tx *txmsg)
+{
+       struct drm_dp_mst_branch *mstb = txmsg->dst;
+       u8 req_type;
+
+       req_type = txmsg->msg[0] & 0x7f;
+       if (req_type == DP_CONNECTION_STATUS_NOTIFY ||
+               req_type == DP_RESOURCE_STATUS_NOTIFY ||
+               req_type == DP_CLEAR_PAYLOAD_ID_TABLE)
+               hdr->broadcast = 1;
+       else
+               hdr->broadcast = 0;
+       hdr->path_msg = txmsg->path_msg;
+       if (hdr->broadcast) {
+               hdr->lct = 1;
+               hdr->lcr = 6;
+       } else {
+               hdr->lct = mstb->lct;
+               hdr->lcr = mstb->lct - 1;
+       }
+
+       memcpy(hdr->rad, mstb->rad, hdr->lct / 2);
+
+       return 0;
+}
+/*
+ * process a single block of the next message in the sideband queue
+ */
+static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,
+                                  struct drm_dp_sideband_msg_tx *txmsg,
+                                  bool up)
+{
+       u8 chunk[48];
+       struct drm_dp_sideband_msg_hdr hdr;
+       int len, space, idx, tosend;
+       int ret;
+
+       if (txmsg->state == DRM_DP_SIDEBAND_TX_SENT)
+               return 0;
+
+       memset(&hdr, 0, sizeof(struct drm_dp_sideband_msg_hdr));
+
+       if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED)
+               txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND;
+
+       /* make hdr from dst mst */
+       ret = set_hdr_from_dst_qlock(&hdr, txmsg);
+       if (ret < 0)
+               return ret;
+
+       /* amount left to send in this message */
+       len = txmsg->cur_len - txmsg->cur_offset;
+
+       /* 48 - sideband msg size - 1 byte for data CRC, x header bytes */
+       space = 48 - 1 - drm_dp_calc_sb_hdr_size(&hdr);
+
+       tosend = min(len, space);
+       if (len == txmsg->cur_len)
+               hdr.somt = 1;
+       if (space >= len)
+               hdr.eomt = 1;
+
+
+       hdr.msg_len = tosend + 1;
+       drm_dp_encode_sideband_msg_hdr(&hdr, chunk, &idx);
+       memcpy(&chunk[idx], &txmsg->msg[txmsg->cur_offset], tosend);
+       /* add crc at end */
+       drm_dp_crc_sideband_chunk_req(&chunk[idx], tosend);
+       idx += tosend + 1;
+
+       ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx);
+       if (ret) {
+               if (drm_debug_enabled(DRM_UT_DP)) {
+                       struct drm_printer p = drm_debug_printer(DBG_PREFIX);
+
+                       drm_printf(&p, "sideband msg failed to send\n");
+                       drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
+               }
+               return ret;
+       }
+
+       txmsg->cur_offset += tosend;
+       if (txmsg->cur_offset == txmsg->cur_len) {
+               txmsg->state = DRM_DP_SIDEBAND_TX_SENT;
+               return 1;
+       }
+       return 0;
+}
+
+static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+       int ret;
+
+       WARN_ON(!mutex_is_locked(&mgr->qlock));
+
+       /* construct a chunk from the first msg in the tx_msg queue */
+       if (list_empty(&mgr->tx_msg_downq))
+               return;
+
+       txmsg = list_first_entry(&mgr->tx_msg_downq,
+                                struct drm_dp_sideband_msg_tx, next);
+       ret = process_single_tx_qlock(mgr, txmsg, false);
+       if (ret < 0) {
+               drm_dbg_kms(mgr->dev, "failed to send msg in q %d\n", ret);
+               list_del(&txmsg->next);
+               txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+               wake_up_all(&mgr->tx_waitq);
+       }
+}
+
+static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
+                                struct drm_dp_sideband_msg_tx *txmsg)
+{
+       mutex_lock(&mgr->qlock);
+       list_add_tail(&txmsg->next, &mgr->tx_msg_downq);
+
+       if (drm_debug_enabled(DRM_UT_DP)) {
+               struct drm_printer p = drm_debug_printer(DBG_PREFIX);
+
+               drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
+       }
+
+       if (list_is_singular(&mgr->tx_msg_downq))
+               process_single_down_tx_qlock(mgr);
+       mutex_unlock(&mgr->qlock);
+}
+
+static void
+drm_dp_dump_link_address(const struct drm_dp_mst_topology_mgr *mgr,
+                        struct drm_dp_link_address_ack_reply *reply)
+{
+       struct drm_dp_link_addr_reply_port *port_reply;
+       int i;
+
+       for (i = 0; i < reply->nports; i++) {
+               port_reply = &reply->ports[i];
+               drm_dbg_kms(mgr->dev,
+                           "port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n",
+                           i,
+                           port_reply->input_port,
+                           port_reply->peer_device_type,
+                           port_reply->port_number,
+                           port_reply->dpcd_revision,
+                           port_reply->mcs,
+                           port_reply->ddps,
+                           port_reply->legacy_device_plug_status,
+                           port_reply->num_sdp_streams,
+                           port_reply->num_sdp_stream_sinks);
+       }
+}
+
+static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+                                    struct drm_dp_mst_branch *mstb)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+       struct drm_dp_link_address_ack_reply *reply;
+       struct drm_dp_mst_port *port, *tmp;
+       int i, ret, port_mask = 0;
+       bool changed = false;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg)
+               return -ENOMEM;
+
+       txmsg->dst = mstb;
+       build_link_address(txmsg);
+
+       mstb->link_address_sent = true;
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       /* FIXME: Actually do some real error handling here */
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret <= 0) {
+               drm_err(mgr->dev, "Sending link address failed with %d\n", ret);
+               goto out;
+       }
+       if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
+               drm_err(mgr->dev, "link address NAK received\n");
+               ret = -EIO;
+               goto out;
+       }
+
+       reply = &txmsg->reply.u.link_addr;
+       drm_dbg_kms(mgr->dev, "link address reply: %d\n", reply->nports);
+       drm_dp_dump_link_address(mgr, reply);
+
+       ret = drm_dp_check_mstb_guid(mstb, reply->guid);
+       if (ret) {
+               char buf[64];
+
+               drm_dp_mst_rad_to_str(mstb->rad, mstb->lct, buf, sizeof(buf));
+               drm_err(mgr->dev, "GUID check on %s failed: %d\n", buf, ret);
+               goto out;
+       }
+
+       for (i = 0; i < reply->nports; i++) {
+               port_mask |= BIT(reply->ports[i].port_number);
+               ret = drm_dp_mst_handle_link_address_port(mstb, mgr->dev,
+                                                         &reply->ports[i]);
+               if (ret == 1)
+                       changed = true;
+               else if (ret < 0)
+                       goto out;
+       }
+
+       /* Prune any ports that are currently a part of mstb in our in-memory
+        * topology, but were not seen in this link address. Usually this
+        * means that they were removed while the topology was out of sync,
+        * e.g. during suspend/resume
+        */
+       mutex_lock(&mgr->lock);
+       list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
+               if (port_mask & BIT(port->port_num))
+                       continue;
+
+               drm_dbg_kms(mgr->dev, "port %d was not in link address, removing\n",
+                           port->port_num);
+               list_del(&port->next);
+               drm_dp_mst_topology_put_port(port);
+               changed = true;
+       }
+       mutex_unlock(&mgr->lock);
+
+out:
+       if (ret <= 0)
+               mstb->link_address_sent = false;
+       kfree(txmsg);
+       return ret < 0 ? ret : changed;
+}
+
+static void
+drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr,
+                                  struct drm_dp_mst_branch *mstb)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+       int ret;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg)
+               return;
+
+       txmsg->dst = mstb;
+       build_clear_payload_id_table(txmsg);
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret > 0 && txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
+               drm_dbg_kms(mgr->dev, "clear payload table id nak received\n");
+
+       kfree(txmsg);
+}
+
+static int
+drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
+                               struct drm_dp_mst_branch *mstb,
+                               struct drm_dp_mst_port *port)
+{
+       struct drm_dp_enum_path_resources_ack_reply *path_res;
+       struct drm_dp_sideband_msg_tx *txmsg;
+       int ret;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg)
+               return -ENOMEM;
+
+       txmsg->dst = mstb;
+       build_enum_path_resources(txmsg, port->port_num);
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret > 0) {
+               ret = 0;
+               path_res = &txmsg->reply.u.path_resources;
+
+               if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
+                       drm_dbg_kms(mgr->dev, "enum path resources nak received\n");
+               } else {
+                       if (port->port_num != path_res->port_number)
+                               DRM_ERROR("got incorrect port in response\n");
+
+                       drm_dbg_kms(mgr->dev, "enum path resources %d: %d %d\n",
+                                   path_res->port_number,
+                                   path_res->full_payload_bw_number,
+                                   path_res->avail_payload_bw_number);
+
+                       /*
+                        * If something changed, make sure we send a
+                        * hotplug
+                        */
+                       if (port->full_pbn != path_res->full_payload_bw_number ||
+                           port->fec_capable != path_res->fec_capable)
+                               ret = 1;
+
+                       port->full_pbn = path_res->full_payload_bw_number;
+                       port->fec_capable = path_res->fec_capable;
+               }
+       }
+
+       kfree(txmsg);
+       return ret;
+}
+
+static struct drm_dp_mst_port *drm_dp_get_last_connected_port_to_mstb(struct drm_dp_mst_branch *mstb)
+{
+       if (!mstb->port_parent)
+               return NULL;
+
+       if (mstb->port_parent->mstb != mstb)
+               return mstb->port_parent;
+
+       return drm_dp_get_last_connected_port_to_mstb(mstb->port_parent->parent);
+}
+
+/*
+ * Searches upwards in the topology starting from mstb to try to find the
+ * closest available parent of mstb that's still connected to the rest of the
+ * topology. This can be used in order to perform operations like releasing
+ * payloads, where the branch device which owned the payload may no longer be
+ * around and thus would require that the payload on the last living relative
+ * be freed instead.
+ */
+static struct drm_dp_mst_branch *
+drm_dp_get_last_connected_port_and_mstb(struct drm_dp_mst_topology_mgr *mgr,
+                                       struct drm_dp_mst_branch *mstb,
+                                       int *port_num)
+{
+       struct drm_dp_mst_branch *rmstb = NULL;
+       struct drm_dp_mst_port *found_port;
+
+       mutex_lock(&mgr->lock);
+       if (!mgr->mst_primary)
+               goto out;
+
+       do {
+               found_port = drm_dp_get_last_connected_port_to_mstb(mstb);
+               if (!found_port)
+                       break;
+
+               if (drm_dp_mst_topology_try_get_mstb(found_port->parent)) {
+                       rmstb = found_port->parent;
+                       *port_num = found_port->port_num;
+               } else {
+                       /* Search again, starting from this parent */
+                       mstb = found_port->parent;
+               }
+       } while (!rmstb);
+out:
+       mutex_unlock(&mgr->lock);
+       return rmstb;
+}
+
+static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
+                                  struct drm_dp_mst_port *port,
+                                  int id,
+                                  int pbn)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+       struct drm_dp_mst_branch *mstb;
+       int ret, port_num;
+       u8 sinks[DRM_DP_MAX_SDP_STREAMS];
+       int i;
+
+       port_num = port->port_num;
+       mstb = drm_dp_mst_topology_get_mstb_validated(mgr, port->parent);
+       if (!mstb) {
+               mstb = drm_dp_get_last_connected_port_and_mstb(mgr,
+                                                              port->parent,
+                                                              &port_num);
+
+               if (!mstb)
+                       return -EINVAL;
+       }
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg) {
+               ret = -ENOMEM;
+               goto fail_put;
+       }
+
+       for (i = 0; i < port->num_sdp_streams; i++)
+               sinks[i] = i;
+
+       txmsg->dst = mstb;
+       build_allocate_payload(txmsg, port_num,
+                              id,
+                              pbn, port->num_sdp_streams, sinks);
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       /*
+        * FIXME: there is a small chance that between getting the last
+        * connected mstb and sending the payload message, the last connected
+        * mstb could also be removed from the topology. In the future, this
+        * needs to be fixed by restarting the
+        * drm_dp_get_last_connected_port_and_mstb() search in the event of a
+        * timeout if the topology is still connected to the system.
+        */
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret > 0) {
+               if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
+                       ret = -EINVAL;
+               else
+                       ret = 0;
+       }
+       kfree(txmsg);
+fail_put:
+       drm_dp_mst_topology_put_mstb(mstb);
+       return ret;
+}
+
+int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
+                                struct drm_dp_mst_port *port, bool power_up)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+       int ret;
+
+       port = drm_dp_mst_topology_get_port_validated(mgr, port);
+       if (!port)
+               return -EINVAL;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg) {
+               drm_dp_mst_topology_put_port(port);
+               return -ENOMEM;
+       }
+
+       txmsg->dst = port->parent;
+       build_power_updown_phy(txmsg, port->port_num, power_up);
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(port->parent, txmsg);
+       if (ret > 0) {
+               if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
+                       ret = -EINVAL;
+               else
+                       ret = 0;
+       }
+       kfree(txmsg);
+       drm_dp_mst_topology_put_port(port);
+
+       return ret;
+}
+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 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;
+
+       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)
+{
+       int ret;
+
+       ret = drm_dp_dpcd_write_payload(mgr, id, payload);
+       if (ret < 0) {
+               payload->payload_state = 0;
+               return ret;
+       }
+       payload->payload_state = DP_PAYLOAD_LOCAL;
+       return 0;
+}
+
+static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
+                                      struct drm_dp_mst_port *port,
+                                      int id,
+                                      struct drm_dp_payload *payload)
+{
+       int ret;
+
+       ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn);
+       if (ret < 0)
+               return ret;
+       payload->payload_state = DP_PAYLOAD_REMOTE;
+       return ret;
+}
+
+static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
+                                       struct drm_dp_mst_port *port,
+                                       int id,
+                                       struct drm_dp_payload *payload)
+{
+       drm_dbg_kms(mgr->dev, "\n");
+       /* it's okay for these to fail */
+       if (port) {
+               drm_dp_payload_send_msg(mgr, port, id, 0);
+       }
+
+       drm_dp_dpcd_write_payload(mgr, id, payload);
+       payload->payload_state = DP_PAYLOAD_DELETE_LOCAL;
+       return 0;
+}
+
+static int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
+                                       int id,
+                                       struct drm_dp_payload *payload)
+{
+       payload->payload_state = 0;
+       return 0;
+}
+
+/**
+ * drm_dp_update_payload_part1() - Execute payload update part 1
+ * @mgr: manager to use.
+ * @start_slot: this is the cur slot
+ *
+ * NOTE: start_slot is a temporary workaround for non-atomic drivers,
+ * this will be removed when non-atomic mst helpers are moved out of the helper
+ *
+ * This iterates over all proposed virtual channels, and tries to
+ * allocate space in the link for them. For 0->slots transitions,
+ * this step just writes the VCPI to the MST device. For slots->0
+ * transitions, this writes the updated VCPIs and removes the
+ * remote VC payloads.
+ *
+ * after calling this the driver should generate ACT and payload
+ * packets.
+ */
+int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr, int start_slot)
+{
+       struct drm_dp_payload req_payload;
+       struct drm_dp_mst_port *port;
+       int i, j;
+       int cur_slots = start_slot;
+       bool skip;
+
+       mutex_lock(&mgr->payload_lock);
+       for (i = 0; i < mgr->max_payloads; i++) {
+               struct drm_dp_vcpi *vcpi = mgr->proposed_vcpis[i];
+               struct drm_dp_payload *payload = &mgr->payloads[i];
+               bool put_port = false;
+
+               /* solve the current payloads - compare to the hw ones
+                  - update the hw view */
+               req_payload.start_slot = cur_slots;
+               if (vcpi) {
+                       port = container_of(vcpi, struct drm_dp_mst_port,
+                                           vcpi);
+
+                       mutex_lock(&mgr->lock);
+                       skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary);
+                       mutex_unlock(&mgr->lock);
+
+                       if (skip) {
+                               drm_dbg_kms(mgr->dev,
+                                           "Virtual channel %d is not in current topology\n",
+                                           i);
+                               continue;
+                       }
+                       /* Validated ports don't matter if we're releasing
+                        * VCPI
+                        */
+                       if (vcpi->num_slots) {
+                               port = drm_dp_mst_topology_get_port_validated(
+                                   mgr, port);
+                               if (!port) {
+                                       if (vcpi->num_slots == payload->num_slots) {
+                                               cur_slots += vcpi->num_slots;
+                                               payload->start_slot = req_payload.start_slot;
+                                               continue;
+                                       } else {
+                                               drm_dbg_kms(mgr->dev,
+                                                           "Fail:set payload to invalid sink");
+                                               mutex_unlock(&mgr->payload_lock);
+                                               return -EINVAL;
+                                       }
+                               }
+                               put_port = true;
+                       }
+
+                       req_payload.num_slots = vcpi->num_slots;
+                       req_payload.vcpi = vcpi->vcpi;
+               } else {
+                       port = NULL;
+                       req_payload.num_slots = 0;
+               }
+
+               payload->start_slot = req_payload.start_slot;
+               /* work out what is required to happen with this payload */
+               if (payload->num_slots != req_payload.num_slots) {
+
+                       /* need to push an update for this payload */
+                       if (req_payload.num_slots) {
+                               drm_dp_create_payload_step1(mgr, vcpi->vcpi,
+                                                           &req_payload);
+                               payload->num_slots = req_payload.num_slots;
+                               payload->vcpi = req_payload.vcpi;
+
+                       } else if (payload->num_slots) {
+                               payload->num_slots = 0;
+                               drm_dp_destroy_payload_step1(mgr, port,
+                                                            payload->vcpi,
+                                                            payload);
+                               req_payload.payload_state =
+                                       payload->payload_state;
+                               payload->start_slot = 0;
+                       }
+                       payload->payload_state = req_payload.payload_state;
+               }
+               cur_slots += req_payload.num_slots;
+
+               if (put_port)
+                       drm_dp_mst_topology_put_port(port);
+       }
+
+       for (i = 0; i < mgr->max_payloads; /* do nothing */) {
+               if (mgr->payloads[i].payload_state != DP_PAYLOAD_DELETE_LOCAL) {
+                       i++;
+                       continue;
+               }
+
+               drm_dbg_kms(mgr->dev, "removing payload %d\n", i);
+               for (j = i; j < mgr->max_payloads - 1; j++) {
+                       mgr->payloads[j] = mgr->payloads[j + 1];
+                       mgr->proposed_vcpis[j] = mgr->proposed_vcpis[j + 1];
+
+                       if (mgr->proposed_vcpis[j] &&
+                           mgr->proposed_vcpis[j]->num_slots) {
+                               set_bit(j + 1, &mgr->payload_mask);
+                       } else {
+                               clear_bit(j + 1, &mgr->payload_mask);
+                       }
+               }
+
+               memset(&mgr->payloads[mgr->max_payloads - 1], 0,
+                      sizeof(struct drm_dp_payload));
+               mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL;
+               clear_bit(mgr->max_payloads, &mgr->payload_mask);
+       }
+       mutex_unlock(&mgr->payload_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_update_payload_part1);
+
+/**
+ * drm_dp_update_payload_part2() - Execute payload update part 2
+ * @mgr: manager to use.
+ *
+ * This iterates over all proposed virtual channels, and tries to
+ * allocate space in the link for them. For 0->slots transitions,
+ * this step writes the remote VC payload commands. For slots->0
+ * this just resets some internal state.
+ */
+int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
+{
+       struct drm_dp_mst_port *port;
+       int i;
+       int ret = 0;
+       bool skip;
+
+       mutex_lock(&mgr->payload_lock);
+       for (i = 0; i < mgr->max_payloads; i++) {
+
+               if (!mgr->proposed_vcpis[i])
+                       continue;
+
+               port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+
+               mutex_lock(&mgr->lock);
+               skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary);
+               mutex_unlock(&mgr->lock);
+
+               if (skip)
+                       continue;
+
+               drm_dbg_kms(mgr->dev, "payload %d %d\n", i, mgr->payloads[i].payload_state);
+               if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) {
+                       ret = drm_dp_create_payload_step2(mgr, port, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]);
+               } else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) {
+                       ret = drm_dp_destroy_payload_step2(mgr, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]);
+               }
+               if (ret) {
+                       mutex_unlock(&mgr->payload_lock);
+                       return ret;
+               }
+       }
+       mutex_unlock(&mgr->payload_lock);
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_update_payload_part2);
+
+static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
+                                struct drm_dp_mst_port *port,
+                                int offset, int size, u8 *bytes)
+{
+       int ret = 0;
+       struct drm_dp_sideband_msg_tx *txmsg;
+       struct drm_dp_mst_branch *mstb;
+
+       mstb = drm_dp_mst_topology_get_mstb_validated(mgr, port->parent);
+       if (!mstb)
+               return -EINVAL;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg) {
+               ret = -ENOMEM;
+               goto fail_put;
+       }
+
+       build_dpcd_read(txmsg, port->port_num, offset, size);
+       txmsg->dst = port->parent;
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret < 0)
+               goto fail_free;
+
+       /* DPCD read should never be NACKed */
+       if (txmsg->reply.reply_type == 1) {
+               drm_err(mgr->dev, "mstb %p port %d: DPCD read on addr 0x%x for %d bytes NAKed\n",
+                       mstb, port->port_num, offset, size);
+               ret = -EIO;
+               goto fail_free;
+       }
+
+       if (txmsg->reply.u.remote_dpcd_read_ack.num_bytes != size) {
+               ret = -EPROTO;
+               goto fail_free;
+       }
+
+       ret = min_t(size_t, txmsg->reply.u.remote_dpcd_read_ack.num_bytes,
+                   size);
+       memcpy(bytes, txmsg->reply.u.remote_dpcd_read_ack.bytes, ret);
+
+fail_free:
+       kfree(txmsg);
+fail_put:
+       drm_dp_mst_topology_put_mstb(mstb);
+
+       return ret;
+}
+
+static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
+                                 struct drm_dp_mst_port *port,
+                                 int offset, int size, u8 *bytes)
+{
+       int ret;
+       struct drm_dp_sideband_msg_tx *txmsg;
+       struct drm_dp_mst_branch *mstb;
+
+       mstb = drm_dp_mst_topology_get_mstb_validated(mgr, port->parent);
+       if (!mstb)
+               return -EINVAL;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg) {
+               ret = -ENOMEM;
+               goto fail_put;
+       }
+
+       build_dpcd_write(txmsg, port->port_num, offset, size, bytes);
+       txmsg->dst = mstb;
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret > 0) {
+               if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
+                       ret = -EIO;
+               else
+                       ret = size;
+       }
+
+       kfree(txmsg);
+fail_put:
+       drm_dp_mst_topology_put_mstb(mstb);
+       return ret;
+}
+
+static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type)
+{
+       struct drm_dp_sideband_msg_reply_body reply;
+
+       reply.reply_type = DP_SIDEBAND_REPLY_ACK;
+       reply.req_type = req_type;
+       drm_dp_encode_sideband_reply(&reply, msg);
+       return 0;
+}
+
+static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr,
+                                   struct drm_dp_mst_branch *mstb,
+                                   int req_type, bool broadcast)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg)
+               return -ENOMEM;
+
+       txmsg->dst = mstb;
+       drm_dp_encode_up_ack_reply(txmsg, req_type);
+
+       mutex_lock(&mgr->qlock);
+       /* construct a chunk from the first msg in the tx_msg queue */
+       process_single_tx_qlock(mgr, txmsg, true);
+       mutex_unlock(&mgr->qlock);
+
+       kfree(txmsg);
+       return 0;
+}
+
+/**
+ * drm_dp_get_vc_payload_bw - get the VC payload BW for an MST link
+ * @mgr: The &drm_dp_mst_topology_mgr to use
+ * @link_rate: link rate in 10kbits/s units
+ * @link_lane_count: lane count
+ *
+ * Calculate the total bandwidth of a MultiStream Transport link. The returned
+ * value is in units of PBNs/(timeslots/1 MTP). This value can be used to
+ * convert the number of PBNs required for a given stream to the number of
+ * timeslots this stream requires in each MTP.
+ */
+int drm_dp_get_vc_payload_bw(const struct drm_dp_mst_topology_mgr *mgr,
+                            int link_rate, int link_lane_count)
+{
+       if (link_rate == 0 || link_lane_count == 0)
+               drm_dbg_kms(mgr->dev, "invalid link rate/lane count: (%d / %d)\n",
+                           link_rate, link_lane_count);
+
+       /* See DP v2.0 2.6.4.2, VCPayload_Bandwidth_for_OneTimeSlotPer_MTP_Allocation */
+       return link_rate * link_lane_count / 54000;
+}
+EXPORT_SYMBOL(drm_dp_get_vc_payload_bw);
+
+/**
+ * drm_dp_read_mst_cap() - check whether or not a sink supports MST
+ * @aux: The DP AUX channel to use
+ * @dpcd: A cached copy of the DPCD capabilities for this sink
+ *
+ * Returns: %True if the sink supports MST, %false otherwise
+ */
+bool drm_dp_read_mst_cap(struct drm_dp_aux *aux,
+                        const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       u8 mstm_cap;
+
+       if (dpcd[DP_DPCD_REV] < DP_DPCD_REV_12)
+               return false;
+
+       if (drm_dp_dpcd_readb(aux, DP_MSTM_CAP, &mstm_cap) != 1)
+               return false;
+
+       return mstm_cap & DP_MST_CAP;
+}
+EXPORT_SYMBOL(drm_dp_read_mst_cap);
+
+/**
+ * drm_dp_mst_topology_mgr_set_mst() - Set the MST state for a topology manager
+ * @mgr: manager to set state for
+ * @mst_state: true to enable MST on this connector - false to disable.
+ *
+ * This is called by the driver when it detects an MST capable device plugged
+ * into a DP MST capable port, or when a DP MST capable device is unplugged.
+ */
+int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state)
+{
+       int ret = 0;
+       struct drm_dp_mst_branch *mstb = NULL;
+
+       mutex_lock(&mgr->payload_lock);
+       mutex_lock(&mgr->lock);
+       if (mst_state == mgr->mst_state)
+               goto out_unlock;
+
+       mgr->mst_state = mst_state;
+       /* set the device into MST mode */
+       if (mst_state) {
+               struct drm_dp_payload reset_pay;
+               int lane_count;
+               int link_rate;
+
+               WARN_ON(mgr->mst_primary);
+
+               /* get dpcd info */
+               ret = drm_dp_read_dpcd_caps(mgr->aux, mgr->dpcd);
+               if (ret < 0) {
+                       drm_dbg_kms(mgr->dev, "%s: failed to read DPCD, ret %d\n",
+                                   mgr->aux->name, ret);
+                       goto out_unlock;
+               }
+
+               lane_count = min_t(int, mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK, mgr->max_lane_count);
+               link_rate = min_t(int, drm_dp_bw_code_to_link_rate(mgr->dpcd[1]), mgr->max_link_rate);
+               mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr,
+                                                       link_rate,
+                                                       lane_count);
+               if (mgr->pbn_div == 0) {
+                       ret = -EINVAL;
+                       goto out_unlock;
+               }
+
+               /* add initial branch device at LCT 1 */
+               mstb = drm_dp_add_mst_branch_device(1, NULL);
+               if (mstb == NULL) {
+                       ret = -ENOMEM;
+                       goto out_unlock;
+               }
+               mstb->mgr = mgr;
+
+               /* give this the main reference */
+               mgr->mst_primary = mstb;
+               drm_dp_mst_topology_get_mstb(mgr->mst_primary);
+
+               ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+                                        DP_MST_EN |
+                                        DP_UP_REQ_EN |
+                                        DP_UPSTREAM_IS_SRC);
+               if (ret < 0)
+                       goto out_unlock;
+
+               reset_pay.start_slot = 0;
+               reset_pay.num_slots = 0x3f;
+               drm_dp_dpcd_write_payload(mgr, 0, &reset_pay);
+
+               queue_work(system_long_wq, &mgr->work);
+
+               ret = 0;
+       } else {
+               /* disable MST on the device */
+               mstb = mgr->mst_primary;
+               mgr->mst_primary = NULL;
+               /* this can fail if the device is gone */
+               drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0);
+               ret = 0;
+               memset(mgr->payloads, 0,
+                      mgr->max_payloads * sizeof(mgr->payloads[0]));
+               memset(mgr->proposed_vcpis, 0,
+                      mgr->max_payloads * sizeof(mgr->proposed_vcpis[0]));
+               mgr->payload_mask = 0;
+               set_bit(0, &mgr->payload_mask);
+               mgr->vcpi_mask = 0;
+               mgr->payload_id_table_cleared = false;
+       }
+
+out_unlock:
+       mutex_unlock(&mgr->lock);
+       mutex_unlock(&mgr->payload_lock);
+       if (mstb)
+               drm_dp_mst_topology_put_mstb(mstb);
+       return ret;
+
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst);
+
+static void
+drm_dp_mst_topology_mgr_invalidate_mstb(struct drm_dp_mst_branch *mstb)
+{
+       struct drm_dp_mst_port *port;
+
+       /* The link address will need to be re-sent on resume */
+       mstb->link_address_sent = false;
+
+       list_for_each_entry(port, &mstb->ports, next)
+               if (port->mstb)
+                       drm_dp_mst_topology_mgr_invalidate_mstb(port->mstb);
+}
+
+/**
+ * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager
+ * @mgr: manager to suspend
+ *
+ * This function tells the MST device that we can't handle UP messages
+ * anymore. This should stop it from sending any since we are suspended.
+ */
+void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
+{
+       mutex_lock(&mgr->lock);
+       drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+                          DP_MST_EN | DP_UPSTREAM_IS_SRC);
+       mutex_unlock(&mgr->lock);
+       flush_work(&mgr->up_req_work);
+       flush_work(&mgr->work);
+       flush_work(&mgr->delayed_destroy_work);
+
+       mutex_lock(&mgr->lock);
+       if (mgr->mst_state && mgr->mst_primary)
+               drm_dp_mst_topology_mgr_invalidate_mstb(mgr->mst_primary);
+       mutex_unlock(&mgr->lock);
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
+
+/**
+ * drm_dp_mst_topology_mgr_resume() - resume the MST manager
+ * @mgr: manager to resume
+ * @sync: whether or not to perform topology reprobing synchronously
+ *
+ * This will fetch DPCD and see if the device is still there,
+ * if it is, it will rewrite the MSTM control bits, and return.
+ *
+ * If the device fails this returns -1, and the driver should do
+ * a full MST reprobe, in case we were undocked.
+ *
+ * During system resume (where it is assumed that the driver will be calling
+ * drm_atomic_helper_resume()) this function should be called beforehand with
+ * @sync set to true. In contexts like runtime resume where the driver is not
+ * expected to be calling drm_atomic_helper_resume(), this function should be
+ * called with @sync set to false in order to avoid deadlocking.
+ *
+ * Returns: -1 if the MST topology was removed while we were suspended, 0
+ * otherwise.
+ */
+int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
+                                  bool sync)
+{
+       int ret;
+       u8 guid[16];
+
+       mutex_lock(&mgr->lock);
+       if (!mgr->mst_primary)
+               goto out_fail;
+
+       ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd,
+                              DP_RECEIVER_CAP_SIZE);
+       if (ret != DP_RECEIVER_CAP_SIZE) {
+               drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
+               goto out_fail;
+       }
+
+       ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+                                DP_MST_EN |
+                                DP_UP_REQ_EN |
+                                DP_UPSTREAM_IS_SRC);
+       if (ret < 0) {
+               drm_dbg_kms(mgr->dev, "mst write failed - undocked during suspend?\n");
+               goto out_fail;
+       }
+
+       /* Some hubs forget their guids after they resume */
+       ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
+       if (ret != 16) {
+               drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
+               goto out_fail;
+       }
+
+       ret = drm_dp_check_mstb_guid(mgr->mst_primary, guid);
+       if (ret) {
+               drm_dbg_kms(mgr->dev, "check mstb failed - undocked during suspend?\n");
+               goto out_fail;
+       }
+
+       /*
+        * For the final step of resuming the topology, we need to bring the
+        * state of our in-memory topology back into sync with reality. So,
+        * restart the probing process as if we're probing a new hub
+        */
+       queue_work(system_long_wq, &mgr->work);
+       mutex_unlock(&mgr->lock);
+
+       if (sync) {
+               drm_dbg_kms(mgr->dev,
+                           "Waiting for link probe work to finish re-syncing topology...\n");
+               flush_work(&mgr->work);
+       }
+
+       return 0;
+
+out_fail:
+       mutex_unlock(&mgr->lock);
+       return -1;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume);
+
+static bool
+drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up,
+                     struct drm_dp_mst_branch **mstb)
+{
+       int len;
+       u8 replyblock[32];
+       int replylen, curreply;
+       int ret;
+       u8 hdrlen;
+       struct drm_dp_sideband_msg_hdr hdr;
+       struct drm_dp_sideband_msg_rx *msg =
+               up ? &mgr->up_req_recv : &mgr->down_rep_recv;
+       int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE :
+                          DP_SIDEBAND_MSG_DOWN_REP_BASE;
+
+       if (!up)
+               *mstb = NULL;
+
+       len = min(mgr->max_dpcd_transaction_bytes, 16);
+       ret = drm_dp_dpcd_read(mgr->aux, basereg, replyblock, len);
+       if (ret != len) {
+               drm_dbg_kms(mgr->dev, "failed to read DPCD down rep %d %d\n", len, ret);
+               return false;
+       }
+
+       ret = drm_dp_decode_sideband_msg_hdr(mgr, &hdr, replyblock, len, &hdrlen);
+       if (ret == false) {
+               print_hex_dump(KERN_DEBUG, "failed hdr", DUMP_PREFIX_NONE, 16,
+                              1, replyblock, len, false);
+               drm_dbg_kms(mgr->dev, "ERROR: failed header\n");
+               return false;
+       }
+
+       if (!up) {
+               /* Caller is responsible for giving back this reference */
+               *mstb = drm_dp_get_mst_branch_device(mgr, hdr.lct, hdr.rad);
+               if (!*mstb) {
+                       drm_dbg_kms(mgr->dev, "Got MST reply from unknown device %d\n", hdr.lct);
+                       return false;
+               }
+       }
+
+       if (!drm_dp_sideband_msg_set_header(msg, &hdr, hdrlen)) {
+               drm_dbg_kms(mgr->dev, "sideband msg set header failed %d\n", replyblock[0]);
+               return false;
+       }
+
+       replylen = min(msg->curchunk_len, (u8)(len - hdrlen));
+       ret = drm_dp_sideband_append_payload(msg, replyblock + hdrlen, replylen);
+       if (!ret) {
+               drm_dbg_kms(mgr->dev, "sideband msg build failed %d\n", replyblock[0]);
+               return false;
+       }
+
+       replylen = msg->curchunk_len + msg->curchunk_hdrlen - len;
+       curreply = len;
+       while (replylen > 0) {
+               len = min3(replylen, mgr->max_dpcd_transaction_bytes, 16);
+               ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply,
+                                   replyblock, len);
+               if (ret != len) {
+                       drm_dbg_kms(mgr->dev, "failed to read a chunk (len %d, ret %d)\n",
+                                   len, ret);
+                       return false;
+               }
+
+               ret = drm_dp_sideband_append_payload(msg, replyblock, len);
+               if (!ret) {
+                       drm_dbg_kms(mgr->dev, "failed to build sideband msg\n");
+                       return false;
+               }
+
+               curreply += len;
+               replylen -= len;
+       }
+       return true;
+}
+
+static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+       struct drm_dp_mst_branch *mstb = NULL;
+       struct drm_dp_sideband_msg_rx *msg = &mgr->down_rep_recv;
+
+       if (!drm_dp_get_one_sb_msg(mgr, false, &mstb))
+               goto out;
+
+       /* Multi-packet message transmission, don't clear the reply */
+       if (!msg->have_eomt)
+               goto out;
+
+       /* find the message */
+       mutex_lock(&mgr->qlock);
+       txmsg = list_first_entry_or_null(&mgr->tx_msg_downq,
+                                        struct drm_dp_sideband_msg_tx, next);
+       mutex_unlock(&mgr->qlock);
+
+       /* Were we actually expecting a response, and from this mstb? */
+       if (!txmsg || txmsg->dst != mstb) {
+               struct drm_dp_sideband_msg_hdr *hdr;
+
+               hdr = &msg->initial_hdr;
+               drm_dbg_kms(mgr->dev, "Got MST reply with no msg %p %d %d %02x %02x\n",
+                           mstb, hdr->seqno, hdr->lct, hdr->rad[0], msg->msg[0]);
+               goto out_clear_reply;
+       }
+
+       drm_dp_sideband_parse_reply(mgr, msg, &txmsg->reply);
+
+       if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
+               drm_dbg_kms(mgr->dev,
+                           "Got NAK reply: req 0x%02x (%s), reason 0x%02x (%s), nak data 0x%02x\n",
+                           txmsg->reply.req_type,
+                           drm_dp_mst_req_type_str(txmsg->reply.req_type),
+                           txmsg->reply.u.nak.reason,
+                           drm_dp_mst_nak_reason_str(txmsg->reply.u.nak.reason),
+                           txmsg->reply.u.nak.nak_data);
+       }
+
+       memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx));
+       drm_dp_mst_topology_put_mstb(mstb);
+
+       mutex_lock(&mgr->qlock);
+       txmsg->state = DRM_DP_SIDEBAND_TX_RX;
+       list_del(&txmsg->next);
+       mutex_unlock(&mgr->qlock);
+
+       wake_up_all(&mgr->tx_waitq);
+
+       return 0;
+
+out_clear_reply:
+       memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx));
+out:
+       if (mstb)
+               drm_dp_mst_topology_put_mstb(mstb);
+
+       return 0;
+}
+
+static inline bool
+drm_dp_mst_process_up_req(struct drm_dp_mst_topology_mgr *mgr,
+                         struct drm_dp_pending_up_req *up_req)
+{
+       struct drm_dp_mst_branch *mstb = NULL;
+       struct drm_dp_sideband_msg_req_body *msg = &up_req->msg;
+       struct drm_dp_sideband_msg_hdr *hdr = &up_req->hdr;
+       bool hotplug = false;
+
+       if (hdr->broadcast) {
+               const u8 *guid = NULL;
+
+               if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY)
+                       guid = msg->u.conn_stat.guid;
+               else if (msg->req_type == DP_RESOURCE_STATUS_NOTIFY)
+                       guid = msg->u.resource_stat.guid;
+
+               if (guid)
+                       mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid);
+       } else {
+               mstb = drm_dp_get_mst_branch_device(mgr, hdr->lct, hdr->rad);
+       }
+
+       if (!mstb) {
+               drm_dbg_kms(mgr->dev, "Got MST reply from unknown device %d\n", hdr->lct);
+               return false;
+       }
+
+       /* TODO: Add missing handler for DP_RESOURCE_STATUS_NOTIFY events */
+       if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY) {
+               drm_dp_mst_handle_conn_stat(mstb, &msg->u.conn_stat);
+               hotplug = true;
+       }
+
+       drm_dp_mst_topology_put_mstb(mstb);
+       return hotplug;
+}
+
+static void drm_dp_mst_up_req_work(struct work_struct *work)
+{
+       struct drm_dp_mst_topology_mgr *mgr =
+               container_of(work, struct drm_dp_mst_topology_mgr,
+                            up_req_work);
+       struct drm_dp_pending_up_req *up_req;
+       bool send_hotplug = false;
+
+       mutex_lock(&mgr->probe_lock);
+       while (true) {
+               mutex_lock(&mgr->up_req_lock);
+               up_req = list_first_entry_or_null(&mgr->up_req_list,
+                                                 struct drm_dp_pending_up_req,
+                                                 next);
+               if (up_req)
+                       list_del(&up_req->next);
+               mutex_unlock(&mgr->up_req_lock);
+
+               if (!up_req)
+                       break;
+
+               send_hotplug |= drm_dp_mst_process_up_req(mgr, up_req);
+               kfree(up_req);
+       }
+       mutex_unlock(&mgr->probe_lock);
+
+       if (send_hotplug)
+               drm_kms_helper_hotplug_event(mgr->dev);
+}
+
+static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
+{
+       struct drm_dp_pending_up_req *up_req;
+
+       if (!drm_dp_get_one_sb_msg(mgr, true, NULL))
+               goto out;
+
+       if (!mgr->up_req_recv.have_eomt)
+               return 0;
+
+       up_req = kzalloc(sizeof(*up_req), GFP_KERNEL);
+       if (!up_req)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&up_req->next);
+
+       drm_dp_sideband_parse_req(mgr, &mgr->up_req_recv, &up_req->msg);
+
+       if (up_req->msg.req_type != DP_CONNECTION_STATUS_NOTIFY &&
+           up_req->msg.req_type != DP_RESOURCE_STATUS_NOTIFY) {
+               drm_dbg_kms(mgr->dev, "Received unknown up req type, ignoring: %x\n",
+                           up_req->msg.req_type);
+               kfree(up_req);
+               goto out;
+       }
+
+       drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, up_req->msg.req_type,
+                                false);
+
+       if (up_req->msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
+               const struct drm_dp_connection_status_notify *conn_stat =
+                       &up_req->msg.u.conn_stat;
+
+               drm_dbg_kms(mgr->dev, "Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n",
+                           conn_stat->port_number,
+                           conn_stat->legacy_device_plug_status,
+                           conn_stat->displayport_device_plug_status,
+                           conn_stat->message_capability_status,
+                           conn_stat->input_port,
+                           conn_stat->peer_device_type);
+       } else if (up_req->msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
+               const struct drm_dp_resource_status_notify *res_stat =
+                       &up_req->msg.u.resource_stat;
+
+               drm_dbg_kms(mgr->dev, "Got RSN: pn: %d avail_pbn %d\n",
+                           res_stat->port_number,
+                           res_stat->available_pbn);
+       }
+
+       up_req->hdr = mgr->up_req_recv.initial_hdr;
+       mutex_lock(&mgr->up_req_lock);
+       list_add_tail(&up_req->next, &mgr->up_req_list);
+       mutex_unlock(&mgr->up_req_lock);
+       queue_work(system_long_wq, &mgr->up_req_work);
+
+out:
+       memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+       return 0;
+}
+
+/**
+ * drm_dp_mst_hpd_irq() - MST hotplug IRQ notify
+ * @mgr: manager to notify irq for.
+ * @esi: 4 bytes from SINK_COUNT_ESI
+ * @handled: whether the hpd interrupt was consumed or not
+ *
+ * This should be called from the driver when it detects a short IRQ,
+ * along with the value of the DEVICE_SERVICE_IRQ_VECTOR_ESI0. The
+ * topology manager will process the sideband messages received as a result
+ * of this.
+ */
+int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled)
+{
+       int ret = 0;
+       int sc;
+       *handled = false;
+       sc = DP_GET_SINK_COUNT(esi[0]);
+
+       if (sc != mgr->sink_count) {
+               mgr->sink_count = sc;
+               *handled = true;
+       }
+
+       if (esi[1] & DP_DOWN_REP_MSG_RDY) {
+               ret = drm_dp_mst_handle_down_rep(mgr);
+               *handled = true;
+       }
+
+       if (esi[1] & DP_UP_REQ_MSG_RDY) {
+               ret |= drm_dp_mst_handle_up_req(mgr);
+               *handled = true;
+       }
+
+       drm_dp_mst_kick_tx(mgr);
+       return ret;
+}
+EXPORT_SYMBOL(drm_dp_mst_hpd_irq);
+
+/**
+ * drm_dp_mst_detect_port() - get connection status for an MST port
+ * @connector: DRM connector for this port
+ * @ctx: The acquisition context to use for grabbing locks
+ * @mgr: manager for this port
+ * @port: pointer to a port
+ *
+ * This returns the current connection state for a port.
+ */
+int
+drm_dp_mst_detect_port(struct drm_connector *connector,
+                      struct drm_modeset_acquire_ctx *ctx,
+                      struct drm_dp_mst_topology_mgr *mgr,
+                      struct drm_dp_mst_port *port)
+{
+       int ret;
+
+       /* we need to search for the port in the mgr in case it's gone */
+       port = drm_dp_mst_topology_get_port_validated(mgr, port);
+       if (!port)
+               return connector_status_disconnected;
+
+       ret = drm_modeset_lock(&mgr->base.lock, ctx);
+       if (ret)
+               goto out;
+
+       ret = connector_status_disconnected;
+
+       if (!port->ddps)
+               goto out;
+
+       switch (port->pdt) {
+       case DP_PEER_DEVICE_NONE:
+               break;
+       case DP_PEER_DEVICE_MST_BRANCHING:
+               if (!port->mcs)
+                       ret = connector_status_connected;
+               break;
+
+       case DP_PEER_DEVICE_SST_SINK:
+               ret = connector_status_connected;
+               /* for logical ports - cache the EDID */
+               if (port->port_num >= DP_MST_LOGICAL_PORT_0 && !port->cached_edid)
+                       port->cached_edid = drm_get_edid(connector, &port->aux.ddc);
+               break;
+       case DP_PEER_DEVICE_DP_LEGACY_CONV:
+               if (port->ldps)
+                       ret = connector_status_connected;
+               break;
+       }
+out:
+       drm_dp_mst_topology_put_port(port);
+       return ret;
+}
+EXPORT_SYMBOL(drm_dp_mst_detect_port);
+
+/**
+ * drm_dp_mst_get_edid() - get EDID for an MST port
+ * @connector: toplevel connector to get EDID for
+ * @mgr: manager for this port
+ * @port: unverified pointer to a port.
+ *
+ * This returns an EDID for the port connected to a connector,
+ * It validates the pointer still exists so the caller doesn't require a
+ * reference.
+ */
+struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+       struct edid *edid = NULL;
+
+       /* we need to search for the port in the mgr in case it's gone */
+       port = drm_dp_mst_topology_get_port_validated(mgr, port);
+       if (!port)
+               return NULL;
+
+       if (port->cached_edid)
+               edid = drm_edid_duplicate(port->cached_edid);
+       else {
+               edid = drm_get_edid(connector, &port->aux.ddc);
+       }
+       port->has_audio = drm_detect_monitor_audio(edid);
+       drm_dp_mst_topology_put_port(port);
+       return edid;
+}
+EXPORT_SYMBOL(drm_dp_mst_get_edid);
+
+/**
+ * drm_dp_find_vcpi_slots() - Find VCPI slots for this PBN value
+ * @mgr: manager to use
+ * @pbn: payload bandwidth to convert into slots.
+ *
+ * Calculate the number of VCPI slots that will be required for the given PBN
+ * value. This function is deprecated, and should not be used in atomic
+ * drivers.
+ *
+ * RETURNS:
+ * The total slots required for this port, or error.
+ */
+int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr,
+                          int pbn)
+{
+       int num_slots;
+
+       num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+       /* max. time slots - one slot for MTP header */
+       if (num_slots > 63)
+               return -ENOSPC;
+       return num_slots;
+}
+EXPORT_SYMBOL(drm_dp_find_vcpi_slots);
+
+static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+                           struct drm_dp_vcpi *vcpi, int pbn, int slots)
+{
+       int ret;
+
+       vcpi->pbn = pbn;
+       vcpi->aligned_pbn = slots * mgr->pbn_div;
+       vcpi->num_slots = slots;
+
+       ret = drm_dp_mst_assign_payload_id(mgr, vcpi);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+/**
+ * drm_dp_atomic_find_vcpi_slots() - Find and add VCPI slots to the state
+ * @state: global atomic state
+ * @mgr: MST topology manager for the port
+ * @port: port to find vcpi slots for
+ * @pbn: bandwidth required for the mode in PBN
+ * @pbn_div: divider for DSC mode that takes FEC into account
+ *
+ * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
+ * may have had. Any atomic drivers which support MST must call this function
+ * in their &drm_encoder_helper_funcs.atomic_check() callback to change the
+ * current VCPI allocation for the new state, but only when
+ * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set
+ * to ensure compatibility with userspace applications that still use the
+ * legacy modesetting UAPI.
+ *
+ * Allocations set by this function are not checked against the bandwidth
+ * restraints of @mgr until the driver calls drm_dp_mst_atomic_check().
+ *
+ * Additionally, it is OK to call this function multiple times on the same
+ * @port as needed. It is not OK however, to call this function and
+ * drm_dp_atomic_release_vcpi_slots() in the same atomic check phase.
+ *
+ * See also:
+ * drm_dp_atomic_release_vcpi_slots()
+ * drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * Total slots in the atomic state assigned for this port, or a negative error
+ * code if the port no longer exists
+ */
+int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
+                                 struct drm_dp_mst_topology_mgr *mgr,
+                                 struct drm_dp_mst_port *port, int pbn,
+                                 int pbn_div)
+{
+       struct drm_dp_mst_topology_state *topology_state;
+       struct drm_dp_vcpi_allocation *pos, *vcpi = NULL;
+       int prev_slots, prev_bw, req_slots;
+
+       topology_state = drm_atomic_get_mst_topology_state(state, mgr);
+       if (IS_ERR(topology_state))
+               return PTR_ERR(topology_state);
+
+       /* Find the current allocation for this port, if any */
+       list_for_each_entry(pos, &topology_state->vcpis, next) {
+               if (pos->port == port) {
+                       vcpi = pos;
+                       prev_slots = vcpi->vcpi;
+                       prev_bw = vcpi->pbn;
+
+                       /*
+                        * This should never happen, unless the driver tries
+                        * releasing and allocating the same VCPI allocation,
+                        * which is an error
+                        */
+                       if (WARN_ON(!prev_slots)) {
+                               drm_err(mgr->dev,
+                                       "cannot allocate and release VCPI on [MST PORT:%p] in the same state\n",
+                                       port);
+                               return -EINVAL;
+                       }
+
+                       break;
+               }
+       }
+       if (!vcpi) {
+               prev_slots = 0;
+               prev_bw = 0;
+       }
+
+       if (pbn_div <= 0)
+               pbn_div = mgr->pbn_div;
+
+       req_slots = DIV_ROUND_UP(pbn, pbn_div);
+
+       drm_dbg_atomic(mgr->dev, "[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n",
+                      port->connector->base.id, port->connector->name,
+                      port, prev_slots, req_slots);
+       drm_dbg_atomic(mgr->dev, "[CONNECTOR:%d:%s] [MST PORT:%p] PBN %d -> %d\n",
+                      port->connector->base.id, port->connector->name,
+                      port, prev_bw, pbn);
+
+       /* Add the new allocation to the state */
+       if (!vcpi) {
+               vcpi = kzalloc(sizeof(*vcpi), GFP_KERNEL);
+               if (!vcpi)
+                       return -ENOMEM;
+
+               drm_dp_mst_get_port_malloc(port);
+               vcpi->port = port;
+               list_add(&vcpi->next, &topology_state->vcpis);
+       }
+       vcpi->vcpi = req_slots;
+       vcpi->pbn = pbn;
+
+       return req_slots;
+}
+EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);
+
+/**
+ * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots
+ * @state: global atomic state
+ * @mgr: MST topology manager for the port
+ * @port: The port to release the VCPI slots from
+ *
+ * Releases any VCPI slots that have been allocated to a port in the atomic
+ * state. Any atomic drivers which support MST must call this function in
+ * their &drm_connector_helper_funcs.atomic_check() callback when the
+ * connector will no longer have VCPI allocated (e.g. because its CRTC was
+ * removed) when it had VCPI allocated in the previous atomic state.
+ *
+ * It is OK to call this even if @port has been removed from the system.
+ * Additionally, it is OK to call this function multiple times on the same
+ * @port as needed. It is not OK however, to call this function and
+ * drm_dp_atomic_find_vcpi_slots() on the same @port in a single atomic check
+ * phase.
+ *
+ * See also:
+ * drm_dp_atomic_find_vcpi_slots()
+ * drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * 0 if all slots for this port were added back to
+ * &drm_dp_mst_topology_state.avail_slots or negative error code
+ */
+int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
+                                    struct drm_dp_mst_topology_mgr *mgr,
+                                    struct drm_dp_mst_port *port)
+{
+       struct drm_dp_mst_topology_state *topology_state;
+       struct drm_dp_vcpi_allocation *pos;
+       bool found = false;
+
+       topology_state = drm_atomic_get_mst_topology_state(state, mgr);
+       if (IS_ERR(topology_state))
+               return PTR_ERR(topology_state);
+
+       list_for_each_entry(pos, &topology_state->vcpis, next) {
+               if (pos->port == port) {
+                       found = true;
+                       break;
+               }
+       }
+       if (WARN_ON(!found)) {
+               drm_err(mgr->dev, "no VCPI for [MST PORT:%p] found in mst state %p\n",
+                       port, &topology_state->base);
+               return -EINVAL;
+       }
+
+       drm_dbg_atomic(mgr->dev, "[MST PORT:%p] VCPI %d -> 0\n", port, pos->vcpi);
+       if (pos->vcpi) {
+               drm_dp_mst_put_port_malloc(port);
+               pos->vcpi = 0;
+               pos->pbn = 0;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
+
+/**
+ * drm_dp_mst_update_slots() - updates the slot info depending on the DP ecoding format
+ * @mst_state: mst_state to update
+ * @link_encoding_cap: the ecoding format on the link
+ */
+void drm_dp_mst_update_slots(struct drm_dp_mst_topology_state *mst_state, uint8_t link_encoding_cap)
+{
+       if (link_encoding_cap == DP_CAP_ANSI_128B132B) {
+               mst_state->total_avail_slots = 64;
+               mst_state->start_slot = 0;
+       } else {
+               mst_state->total_avail_slots = 63;
+               mst_state->start_slot = 1;
+       }
+
+       DRM_DEBUG_KMS("%s encoding format on mst_state 0x%p\n",
+                     (link_encoding_cap == DP_CAP_ANSI_128B132B) ? "128b/132b":"8b/10b",
+                     mst_state);
+}
+EXPORT_SYMBOL(drm_dp_mst_update_slots);
+
+/**
+ * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
+ * @mgr: manager for this port
+ * @port: port to allocate a virtual channel for.
+ * @pbn: payload bandwidth number to request
+ * @slots: returned number of slots for this PBN.
+ */
+bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+                             struct drm_dp_mst_port *port, int pbn, int slots)
+{
+       int ret;
+
+       if (slots < 0)
+               return false;
+
+       port = drm_dp_mst_topology_get_port_validated(mgr, port);
+       if (!port)
+               return false;
+
+       if (port->vcpi.vcpi > 0) {
+               drm_dbg_kms(mgr->dev,
+                           "payload: vcpi %d already allocated for pbn %d - requested pbn %d\n",
+                           port->vcpi.vcpi, port->vcpi.pbn, pbn);
+               if (pbn == port->vcpi.pbn) {
+                       drm_dp_mst_topology_put_port(port);
+                       return true;
+               }
+       }
+
+       ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn, slots);
+       if (ret) {
+               drm_dbg_kms(mgr->dev, "failed to init vcpi slots=%d ret=%d\n",
+                           DIV_ROUND_UP(pbn, mgr->pbn_div), ret);
+               drm_dp_mst_topology_put_port(port);
+               goto out;
+       }
+       drm_dbg_kms(mgr->dev, "initing vcpi for pbn=%d slots=%d\n", pbn, port->vcpi.num_slots);
+
+       /* Keep port allocated until its payload has been removed */
+       drm_dp_mst_get_port_malloc(port);
+       drm_dp_mst_topology_put_port(port);
+       return true;
+out:
+       return false;
+}
+EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi);
+
+int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+       int slots = 0;
+
+       port = drm_dp_mst_topology_get_port_validated(mgr, port);
+       if (!port)
+               return slots;
+
+       slots = port->vcpi.num_slots;
+       drm_dp_mst_topology_put_port(port);
+       return slots;
+}
+EXPORT_SYMBOL(drm_dp_mst_get_vcpi_slots);
+
+/**
+ * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI
+ * @mgr: manager for this port
+ * @port: unverified pointer to a port.
+ *
+ * This just resets the number of slots for the ports VCPI for later programming.
+ */
+void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+       /*
+        * A port with VCPI will remain allocated until its VCPI is
+        * released, no verified ref needed
+        */
+
+       port->vcpi.num_slots = 0;
+}
+EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots);
+
+/**
+ * drm_dp_mst_deallocate_vcpi() - deallocate a VCPI
+ * @mgr: manager for this port
+ * @port: port to deallocate vcpi for
+ *
+ * This can be called unconditionally, regardless of whether
+ * drm_dp_mst_allocate_vcpi() succeeded or not.
+ */
+void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+                               struct drm_dp_mst_port *port)
+{
+       bool skip;
+
+       if (!port->vcpi.vcpi)
+               return;
+
+       mutex_lock(&mgr->lock);
+       skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary);
+       mutex_unlock(&mgr->lock);
+
+       if (skip)
+               return;
+
+       drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
+       port->vcpi.num_slots = 0;
+       port->vcpi.pbn = 0;
+       port->vcpi.aligned_pbn = 0;
+       port->vcpi.vcpi = 0;
+       drm_dp_mst_put_port_malloc(port);
+}
+EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi);
+
+static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
+                                    int id, struct drm_dp_payload *payload)
+{
+       u8 payload_alloc[3], status;
+       int ret;
+       int retries = 0;
+
+       drm_dp_dpcd_writeb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS,
+                          DP_PAYLOAD_TABLE_UPDATED);
+
+       payload_alloc[0] = id;
+       payload_alloc[1] = payload->start_slot;
+       payload_alloc[2] = payload->num_slots;
+
+       ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3);
+       if (ret != 3) {
+               drm_dbg_kms(mgr->dev, "failed to write payload allocation %d\n", ret);
+               goto fail;
+       }
+
+retry:
+       ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
+       if (ret < 0) {
+               drm_dbg_kms(mgr->dev, "failed to read payload table status %d\n", ret);
+               goto fail;
+       }
+
+       if (!(status & DP_PAYLOAD_TABLE_UPDATED)) {
+               retries++;
+               if (retries < 20) {
+                       usleep_range(10000, 20000);
+                       goto retry;
+               }
+               drm_dbg_kms(mgr->dev, "status not set after read payload table status %d\n",
+                           status);
+               ret = -EINVAL;
+               goto fail;
+       }
+       ret = 0;
+fail:
+       return ret;
+}
+
+static int do_get_act_status(struct drm_dp_aux *aux)
+{
+       int ret;
+       u8 status;
+
+       ret = drm_dp_dpcd_readb(aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
+       if (ret < 0)
+               return ret;
+
+       return status;
+}
+
+/**
+ * drm_dp_check_act_status() - Polls for ACT handled status.
+ * @mgr: manager to use
+ *
+ * Tries waiting for the MST hub to finish updating it's payload table by
+ * polling for the ACT handled bit for up to 3 seconds (yes-some hubs really
+ * take that long).
+ *
+ * Returns:
+ * 0 if the ACT was handled in time, negative error code on failure.
+ */
+int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr)
+{
+       /*
+        * There doesn't seem to be any recommended retry count or timeout in
+        * the MST specification. Since some hubs have been observed to take
+        * over 1 second to update their payload allocations under certain
+        * conditions, we use a rather large timeout value.
+        */
+       const int timeout_ms = 3000;
+       int ret, status;
+
+       ret = readx_poll_timeout(do_get_act_status, mgr->aux, status,
+                                status & DP_PAYLOAD_ACT_HANDLED || status < 0,
+                                200, timeout_ms * USEC_PER_MSEC);
+       if (ret < 0 && status >= 0) {
+               drm_err(mgr->dev, "Failed to get ACT after %dms, last status: %02x\n",
+                       timeout_ms, status);
+               return -EINVAL;
+       } else if (status < 0) {
+               /*
+                * Failure here isn't unexpected - the hub may have
+                * just been unplugged
+                */
+               drm_dbg_kms(mgr->dev, "Failed to read payload table status: %d\n", status);
+               return status;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_check_act_status);
+
+/**
+ * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode.
+ * @clock: dot clock for the mode
+ * @bpp: bpp for the mode.
+ * @dsc: DSC mode. If true, bpp has units of 1/16 of a bit per pixel
+ *
+ * This uses the formula in the spec to calculate the PBN value for a mode.
+ */
+int drm_dp_calc_pbn_mode(int clock, int bpp, bool dsc)
+{
+       /*
+        * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006
+        * The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on
+        * common multiplier to render an integer PBN for all link rate/lane
+        * counts combinations
+        * calculate
+        * peak_kbps *= (1006/1000)
+        * peak_kbps *= (64/54)
+        * peak_kbps *= 8    convert to bytes
+        *
+        * If the bpp is in units of 1/16, further divide by 16. Put this
+        * factor in the numerator rather than the denominator to avoid
+        * integer overflow
+        */
+
+       if (dsc)
+               return DIV_ROUND_UP_ULL(mul_u32_u32(clock * (bpp / 16), 64 * 1006),
+                                       8 * 54 * 1000 * 1000);
+
+       return DIV_ROUND_UP_ULL(mul_u32_u32(clock * bpp, 64 * 1006),
+                               8 * 54 * 1000 * 1000);
+}
+EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
+
+/* we want to kick the TX after we've ack the up/down IRQs. */
+static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr)
+{
+       queue_work(system_long_wq, &mgr->tx_work);
+}
+
+/*
+ * Helper function for parsing DP device types into convenient strings
+ * for use with dp_mst_topology
+ */
+static const char *pdt_to_string(u8 pdt)
+{
+       switch (pdt) {
+       case DP_PEER_DEVICE_NONE:
+               return "NONE";
+       case DP_PEER_DEVICE_SOURCE_OR_SST:
+               return "SOURCE OR SST";
+       case DP_PEER_DEVICE_MST_BRANCHING:
+               return "MST BRANCHING";
+       case DP_PEER_DEVICE_SST_SINK:
+               return "SST SINK";
+       case DP_PEER_DEVICE_DP_LEGACY_CONV:
+               return "DP LEGACY CONV";
+       default:
+               return "ERR";
+       }
+}
+
+static void drm_dp_mst_dump_mstb(struct seq_file *m,
+                                struct drm_dp_mst_branch *mstb)
+{
+       struct drm_dp_mst_port *port;
+       int tabs = mstb->lct;
+       char prefix[10];
+       int i;
+
+       for (i = 0; i < tabs; i++)
+               prefix[i] = '\t';
+       prefix[i] = '\0';
+
+       seq_printf(m, "%smstb - [%p]: num_ports: %d\n", prefix, mstb, mstb->num_ports);
+       list_for_each_entry(port, &mstb->ports, next) {
+               seq_printf(m, "%sport %d - [%p] (%s - %s): ddps: %d, ldps: %d, sdp: %d/%d, fec: %s, conn: %p\n",
+                          prefix,
+                          port->port_num,
+                          port,
+                          port->input ? "input" : "output",
+                          pdt_to_string(port->pdt),
+                          port->ddps,
+                          port->ldps,
+                          port->num_sdp_streams,
+                          port->num_sdp_stream_sinks,
+                          port->fec_capable ? "true" : "false",
+                          port->connector);
+               if (port->mstb)
+                       drm_dp_mst_dump_mstb(m, port->mstb);
+       }
+}
+
+#define DP_PAYLOAD_TABLE_SIZE          64
+
+static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
+                                 char *buf)
+{
+       int i;
+
+       for (i = 0; i < DP_PAYLOAD_TABLE_SIZE; i += 16) {
+               if (drm_dp_dpcd_read(mgr->aux,
+                                    DP_PAYLOAD_TABLE_UPDATE_STATUS + i,
+                                    &buf[i], 16) != 16)
+                       return false;
+       }
+       return true;
+}
+
+static void fetch_monitor_name(struct drm_dp_mst_topology_mgr *mgr,
+                              struct drm_dp_mst_port *port, char *name,
+                              int namelen)
+{
+       struct edid *mst_edid;
+
+       mst_edid = drm_dp_mst_get_edid(port->connector, mgr, port);
+       drm_edid_get_monitor_name(mst_edid, name, namelen);
+}
+
+/**
+ * drm_dp_mst_dump_topology(): dump topology to seq file.
+ * @m: seq_file to dump output to
+ * @mgr: manager to dump current topology for.
+ *
+ * helper to dump MST topology to a seq file for debugfs.
+ */
+void drm_dp_mst_dump_topology(struct seq_file *m,
+                             struct drm_dp_mst_topology_mgr *mgr)
+{
+       int i;
+       struct drm_dp_mst_port *port;
+
+       mutex_lock(&mgr->lock);
+       if (mgr->mst_primary)
+               drm_dp_mst_dump_mstb(m, mgr->mst_primary);
+
+       /* dump VCPIs */
+       mutex_unlock(&mgr->lock);
+
+       mutex_lock(&mgr->payload_lock);
+       seq_printf(m, "\n*** VCPI Info ***\n");
+       seq_printf(m, "payload_mask: %lx, vcpi_mask: %lx, max_payloads: %d\n", mgr->payload_mask, mgr->vcpi_mask, mgr->max_payloads);
+
+       seq_printf(m, "\n|   idx   |  port # |  vcp_id | # slots |     sink name     |\n");
+       for (i = 0; i < mgr->max_payloads; i++) {
+               if (mgr->proposed_vcpis[i]) {
+                       char name[14];
+
+                       port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+                       fetch_monitor_name(mgr, port, name, sizeof(name));
+                       seq_printf(m, "%10d%10d%10d%10d%20s\n",
+                                  i,
+                                  port->port_num,
+                                  port->vcpi.vcpi,
+                                  port->vcpi.num_slots,
+                                  (*name != 0) ? name : "Unknown");
+               } else
+                       seq_printf(m, "%6d - Unused\n", i);
+       }
+       seq_printf(m, "\n*** Payload Info ***\n");
+       seq_printf(m, "|   idx   |  state  |  start slot  | # slots |\n");
+       for (i = 0; i < mgr->max_payloads; i++) {
+               seq_printf(m, "%10d%10d%15d%10d\n",
+                          i,
+                          mgr->payloads[i].payload_state,
+                          mgr->payloads[i].start_slot,
+                          mgr->payloads[i].num_slots);
+       }
+       mutex_unlock(&mgr->payload_lock);
+
+       seq_printf(m, "\n*** DPCD Info ***\n");
+       mutex_lock(&mgr->lock);
+       if (mgr->mst_primary) {
+               u8 buf[DP_PAYLOAD_TABLE_SIZE];
+               int ret;
+
+               ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE);
+               if (ret) {
+                       seq_printf(m, "dpcd read failed\n");
+                       goto out;
+               }
+               seq_printf(m, "dpcd: %*ph\n", DP_RECEIVER_CAP_SIZE, buf);
+
+               ret = drm_dp_dpcd_read(mgr->aux, DP_FAUX_CAP, buf, 2);
+               if (ret) {
+                       seq_printf(m, "faux/mst read failed\n");
+                       goto out;
+               }
+               seq_printf(m, "faux/mst: %*ph\n", 2, buf);
+
+               ret = drm_dp_dpcd_read(mgr->aux, DP_MSTM_CTRL, buf, 1);
+               if (ret) {
+                       seq_printf(m, "mst ctrl read failed\n");
+                       goto out;
+               }
+               seq_printf(m, "mst ctrl: %*ph\n", 1, buf);
+
+               /* dump the standard OUI branch header */
+               ret = drm_dp_dpcd_read(mgr->aux, DP_BRANCH_OUI, buf, DP_BRANCH_OUI_HEADER_SIZE);
+               if (ret) {
+                       seq_printf(m, "branch oui read failed\n");
+                       goto out;
+               }
+               seq_printf(m, "branch oui: %*phN devid: ", 3, buf);
+
+               for (i = 0x3; i < 0x8 && buf[i]; i++)
+                       seq_printf(m, "%c", buf[i]);
+               seq_printf(m, " revision: hw: %x.%x sw: %x.%x\n",
+                          buf[0x9] >> 4, buf[0x9] & 0xf, buf[0xa], buf[0xb]);
+               if (dump_dp_payload_table(mgr, buf))
+                       seq_printf(m, "payload table: %*ph\n", DP_PAYLOAD_TABLE_SIZE, buf);
+       }
+
+out:
+       mutex_unlock(&mgr->lock);
+
+}
+EXPORT_SYMBOL(drm_dp_mst_dump_topology);
+
+static void drm_dp_tx_work(struct work_struct *work)
+{
+       struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work);
+
+       mutex_lock(&mgr->qlock);
+       if (!list_empty(&mgr->tx_msg_downq))
+               process_single_down_tx_qlock(mgr);
+       mutex_unlock(&mgr->qlock);
+}
+
+static inline void
+drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
+{
+       drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE, port->mcs);
+
+       if (port->connector) {
+               drm_connector_unregister(port->connector);
+               drm_connector_put(port->connector);
+       }
+
+       drm_dp_mst_put_port_malloc(port);
+}
+
+static inline void
+drm_dp_delayed_destroy_mstb(struct drm_dp_mst_branch *mstb)
+{
+       struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+       struct drm_dp_mst_port *port, *port_tmp;
+       struct drm_dp_sideband_msg_tx *txmsg, *txmsg_tmp;
+       bool wake_tx = false;
+
+       mutex_lock(&mgr->lock);
+       list_for_each_entry_safe(port, port_tmp, &mstb->ports, next) {
+               list_del(&port->next);
+               drm_dp_mst_topology_put_port(port);
+       }
+       mutex_unlock(&mgr->lock);
+
+       /* drop any tx slot msg */
+       mutex_lock(&mstb->mgr->qlock);
+       list_for_each_entry_safe(txmsg, txmsg_tmp, &mgr->tx_msg_downq, next) {
+               if (txmsg->dst != mstb)
+                       continue;
+
+               txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+               list_del(&txmsg->next);
+               wake_tx = true;
+       }
+       mutex_unlock(&mstb->mgr->qlock);
+
+       if (wake_tx)
+               wake_up_all(&mstb->mgr->tx_waitq);
+
+       drm_dp_mst_put_mstb_malloc(mstb);
+}
+
+static void drm_dp_delayed_destroy_work(struct work_struct *work)
+{
+       struct drm_dp_mst_topology_mgr *mgr =
+               container_of(work, struct drm_dp_mst_topology_mgr,
+                            delayed_destroy_work);
+       bool send_hotplug = false, go_again;
+
+       /*
+        * Not a regular list traverse as we have to drop the destroy
+        * connector lock before destroying the mstb/port, to avoid AB->BA
+        * ordering between this lock and the config mutex.
+        */
+       do {
+               go_again = false;
+
+               for (;;) {
+                       struct drm_dp_mst_branch *mstb;
+
+                       mutex_lock(&mgr->delayed_destroy_lock);
+                       mstb = list_first_entry_or_null(&mgr->destroy_branch_device_list,
+                                                       struct drm_dp_mst_branch,
+                                                       destroy_next);
+                       if (mstb)
+                               list_del(&mstb->destroy_next);
+                       mutex_unlock(&mgr->delayed_destroy_lock);
+
+                       if (!mstb)
+                               break;
+
+                       drm_dp_delayed_destroy_mstb(mstb);
+                       go_again = true;
+               }
+
+               for (;;) {
+                       struct drm_dp_mst_port *port;
+
+                       mutex_lock(&mgr->delayed_destroy_lock);
+                       port = list_first_entry_or_null(&mgr->destroy_port_list,
+                                                       struct drm_dp_mst_port,
+                                                       next);
+                       if (port)
+                               list_del(&port->next);
+                       mutex_unlock(&mgr->delayed_destroy_lock);
+
+                       if (!port)
+                               break;
+
+                       drm_dp_delayed_destroy_port(port);
+                       send_hotplug = true;
+                       go_again = true;
+               }
+       } while (go_again);
+
+       if (send_hotplug)
+               drm_kms_helper_hotplug_event(mgr->dev);
+}
+
+static struct drm_private_state *
+drm_dp_mst_duplicate_state(struct drm_private_obj *obj)
+{
+       struct drm_dp_mst_topology_state *state, *old_state =
+               to_dp_mst_topology_state(obj->state);
+       struct drm_dp_vcpi_allocation *pos, *vcpi;
+
+       state = kmemdup(old_state, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return NULL;
+
+       __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
+
+       INIT_LIST_HEAD(&state->vcpis);
+
+       list_for_each_entry(pos, &old_state->vcpis, next) {
+               /* Prune leftover freed VCPI allocations */
+               if (!pos->vcpi)
+                       continue;
+
+               vcpi = kmemdup(pos, sizeof(*vcpi), GFP_KERNEL);
+               if (!vcpi)
+                       goto fail;
+
+               drm_dp_mst_get_port_malloc(vcpi->port);
+               list_add(&vcpi->next, &state->vcpis);
+       }
+
+       return &state->base;
+
+fail:
+       list_for_each_entry_safe(pos, vcpi, &state->vcpis, next) {
+               drm_dp_mst_put_port_malloc(pos->port);
+               kfree(pos);
+       }
+       kfree(state);
+
+       return NULL;
+}
+
+static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,
+                                    struct drm_private_state *state)
+{
+       struct drm_dp_mst_topology_state *mst_state =
+               to_dp_mst_topology_state(state);
+       struct drm_dp_vcpi_allocation *pos, *tmp;
+
+       list_for_each_entry_safe(pos, tmp, &mst_state->vcpis, next) {
+               /* We only keep references to ports with non-zero VCPIs */
+               if (pos->vcpi)
+                       drm_dp_mst_put_port_malloc(pos->port);
+               kfree(pos);
+       }
+
+       kfree(mst_state);
+}
+
+static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port,
+                                                struct drm_dp_mst_branch *branch)
+{
+       while (port->parent) {
+               if (port->parent == branch)
+                       return true;
+
+               if (port->parent->port_parent)
+                       port = port->parent->port_parent;
+               else
+                       break;
+       }
+       return false;
+}
+
+static int
+drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port,
+                                     struct drm_dp_mst_topology_state *state);
+
+static int
+drm_dp_mst_atomic_check_mstb_bw_limit(struct drm_dp_mst_branch *mstb,
+                                     struct drm_dp_mst_topology_state *state)
+{
+       struct drm_dp_vcpi_allocation *vcpi;
+       struct drm_dp_mst_port *port;
+       int pbn_used = 0, ret;
+       bool found = false;
+
+       /* Check that we have at least one port in our state that's downstream
+        * of this branch, otherwise we can skip this branch
+        */
+       list_for_each_entry(vcpi, &state->vcpis, next) {
+               if (!vcpi->pbn ||
+                   !drm_dp_mst_port_downstream_of_branch(vcpi->port, mstb))
+                       continue;
+
+               found = true;
+               break;
+       }
+       if (!found)
+               return 0;
+
+       if (mstb->port_parent)
+               drm_dbg_atomic(mstb->mgr->dev,
+                              "[MSTB:%p] [MST PORT:%p] Checking bandwidth limits on [MSTB:%p]\n",
+                              mstb->port_parent->parent, mstb->port_parent, mstb);
+       else
+               drm_dbg_atomic(mstb->mgr->dev, "[MSTB:%p] Checking bandwidth limits\n", mstb);
+
+       list_for_each_entry(port, &mstb->ports, next) {
+               ret = drm_dp_mst_atomic_check_port_bw_limit(port, state);
+               if (ret < 0)
+                       return ret;
+
+               pbn_used += ret;
+       }
+
+       return pbn_used;
+}
+
+static int
+drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port,
+                                     struct drm_dp_mst_topology_state *state)
+{
+       struct drm_dp_vcpi_allocation *vcpi;
+       int pbn_used = 0;
+
+       if (port->pdt == DP_PEER_DEVICE_NONE)
+               return 0;
+
+       if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
+               bool found = false;
+
+               list_for_each_entry(vcpi, &state->vcpis, next) {
+                       if (vcpi->port != port)
+                               continue;
+                       if (!vcpi->pbn)
+                               return 0;
+
+                       found = true;
+                       break;
+               }
+               if (!found)
+                       return 0;
+
+               /*
+                * This could happen if the sink deasserted its HPD line, but
+                * the branch device still reports it as attached (PDT != NONE).
+                */
+               if (!port->full_pbn) {
+                       drm_dbg_atomic(port->mgr->dev,
+                                      "[MSTB:%p] [MST PORT:%p] no BW available for the port\n",
+                                      port->parent, port);
+                       return -EINVAL;
+               }
+
+               pbn_used = vcpi->pbn;
+       } else {
+               pbn_used = drm_dp_mst_atomic_check_mstb_bw_limit(port->mstb,
+                                                                state);
+               if (pbn_used <= 0)
+                       return pbn_used;
+       }
+
+       if (pbn_used > port->full_pbn) {
+               drm_dbg_atomic(port->mgr->dev,
+                              "[MSTB:%p] [MST PORT:%p] required PBN of %d exceeds port limit of %d\n",
+                              port->parent, port, pbn_used, port->full_pbn);
+               return -ENOSPC;
+       }
+
+       drm_dbg_atomic(port->mgr->dev, "[MSTB:%p] [MST PORT:%p] uses %d out of %d PBN\n",
+                      port->parent, port, pbn_used, port->full_pbn);
+
+       return pbn_used;
+}
+
+static inline int
+drm_dp_mst_atomic_check_vcpi_alloc_limit(struct drm_dp_mst_topology_mgr *mgr,
+                                        struct drm_dp_mst_topology_state *mst_state)
+{
+       struct drm_dp_vcpi_allocation *vcpi;
+       int avail_slots = mst_state->total_avail_slots, payload_count = 0;
+
+       list_for_each_entry(vcpi, &mst_state->vcpis, next) {
+               /* Releasing VCPI is always OK-even if the port is gone */
+               if (!vcpi->vcpi) {
+                       drm_dbg_atomic(mgr->dev, "[MST PORT:%p] releases all VCPI slots\n",
+                                      vcpi->port);
+                       continue;
+               }
+
+               drm_dbg_atomic(mgr->dev, "[MST PORT:%p] requires %d vcpi slots\n",
+                              vcpi->port, vcpi->vcpi);
+
+               avail_slots -= vcpi->vcpi;
+               if (avail_slots < 0) {
+                       drm_dbg_atomic(mgr->dev,
+                                      "[MST PORT:%p] not enough VCPI slots in mst state %p (avail=%d)\n",
+                                      vcpi->port, mst_state, avail_slots + vcpi->vcpi);
+                       return -ENOSPC;
+               }
+
+               if (++payload_count > mgr->max_payloads) {
+                       drm_dbg_atomic(mgr->dev,
+                                      "[MST MGR:%p] state %p has too many payloads (max=%d)\n",
+                                      mgr, mst_state, mgr->max_payloads);
+                       return -EINVAL;
+               }
+       }
+       drm_dbg_atomic(mgr->dev, "[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n",
+                      mgr, mst_state, avail_slots, mst_state->total_avail_slots - avail_slots);
+
+       return 0;
+}
+
+/**
+ * drm_dp_mst_add_affected_dsc_crtcs
+ * @state: Pointer to the new struct drm_dp_mst_topology_state
+ * @mgr: MST topology manager
+ *
+ * Whenever there is a change in mst topology
+ * DSC configuration would have to be recalculated
+ * therefore we need to trigger modeset on all affected
+ * CRTCs in that topology
+ *
+ * See also:
+ * drm_dp_mst_atomic_enable_dsc()
+ */
+int drm_dp_mst_add_affected_dsc_crtcs(struct drm_atomic_state *state, struct drm_dp_mst_topology_mgr *mgr)
+{
+       struct drm_dp_mst_topology_state *mst_state;
+       struct drm_dp_vcpi_allocation *pos;
+       struct drm_connector *connector;
+       struct drm_connector_state *conn_state;
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *crtc_state;
+
+       mst_state = drm_atomic_get_mst_topology_state(state, mgr);
+
+       if (IS_ERR(mst_state))
+               return -EINVAL;
+
+       list_for_each_entry(pos, &mst_state->vcpis, next) {
+
+               connector = pos->port->connector;
+
+               if (!connector)
+                       return -EINVAL;
+
+               conn_state = drm_atomic_get_connector_state(state, connector);
+
+               if (IS_ERR(conn_state))
+                       return PTR_ERR(conn_state);
+
+               crtc = conn_state->crtc;
+
+               if (!crtc)
+                       continue;
+
+               if (!drm_dp_mst_dsc_aux_for_port(pos->port))
+                       continue;
+
+               crtc_state = drm_atomic_get_crtc_state(mst_state->base.state, crtc);
+
+               if (IS_ERR(crtc_state))
+                       return PTR_ERR(crtc_state);
+
+               drm_dbg_atomic(mgr->dev, "[MST MGR:%p] Setting mode_changed flag on CRTC %p\n",
+                              mgr, crtc);
+
+               crtc_state->mode_changed = true;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_mst_add_affected_dsc_crtcs);
+
+/**
+ * drm_dp_mst_atomic_enable_dsc - Set DSC Enable Flag to On/Off
+ * @state: Pointer to the new drm_atomic_state
+ * @port: Pointer to the affected MST Port
+ * @pbn: Newly recalculated bw required for link with DSC enabled
+ * @pbn_div: Divider to calculate correct number of pbn per slot
+ * @enable: Boolean flag to enable or disable DSC on the port
+ *
+ * This function enables DSC on the given Port
+ * by recalculating its vcpi from pbn provided
+ * and sets dsc_enable flag to keep track of which
+ * ports have DSC enabled
+ *
+ */
+int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state,
+                                struct drm_dp_mst_port *port,
+                                int pbn, int pbn_div,
+                                bool enable)
+{
+       struct drm_dp_mst_topology_state *mst_state;
+       struct drm_dp_vcpi_allocation *pos;
+       bool found = false;
+       int vcpi = 0;
+
+       mst_state = drm_atomic_get_mst_topology_state(state, port->mgr);
+
+       if (IS_ERR(mst_state))
+               return PTR_ERR(mst_state);
+
+       list_for_each_entry(pos, &mst_state->vcpis, next) {
+               if (pos->port == port) {
+                       found = true;
+                       break;
+               }
+       }
+
+       if (!found) {
+               drm_dbg_atomic(state->dev,
+                              "[MST PORT:%p] Couldn't find VCPI allocation in mst state %p\n",
+                              port, mst_state);
+               return -EINVAL;
+       }
+
+       if (pos->dsc_enabled == enable) {
+               drm_dbg_atomic(state->dev,
+                              "[MST PORT:%p] DSC flag is already set to %d, returning %d VCPI slots\n",
+                              port, enable, pos->vcpi);
+               vcpi = pos->vcpi;
+       }
+
+       if (enable) {
+               vcpi = drm_dp_atomic_find_vcpi_slots(state, port->mgr, port, pbn, pbn_div);
+               drm_dbg_atomic(state->dev,
+                              "[MST PORT:%p] Enabling DSC flag, reallocating %d VCPI slots on the port\n",
+                              port, vcpi);
+               if (vcpi < 0)
+                       return -EINVAL;
+       }
+
+       pos->dsc_enabled = enable;
+
+       return vcpi;
+}
+EXPORT_SYMBOL(drm_dp_mst_atomic_enable_dsc);
+/**
+ * drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
+ * atomic update is valid
+ * @state: Pointer to the new &struct drm_dp_mst_topology_state
+ *
+ * Checks the given topology state for an atomic update to ensure that it's
+ * valid. This includes checking whether there's enough bandwidth to support
+ * the new VCPI allocations in the atomic update.
+ *
+ * Any atomic drivers supporting DP MST must make sure to call this after
+ * checking the rest of their state in their
+ * &drm_mode_config_funcs.atomic_check() callback.
+ *
+ * See also:
+ * drm_dp_atomic_find_vcpi_slots()
+ * drm_dp_atomic_release_vcpi_slots()
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */
+int drm_dp_mst_atomic_check(struct drm_atomic_state *state)
+{
+       struct drm_dp_mst_topology_mgr *mgr;
+       struct drm_dp_mst_topology_state *mst_state;
+       int i, ret = 0;
+
+       for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
+               if (!mgr->mst_state)
+                       continue;
+
+               ret = drm_dp_mst_atomic_check_vcpi_alloc_limit(mgr, mst_state);
+               if (ret)
+                       break;
+
+               mutex_lock(&mgr->lock);
+               ret = drm_dp_mst_atomic_check_mstb_bw_limit(mgr->mst_primary,
+                                                           mst_state);
+               mutex_unlock(&mgr->lock);
+               if (ret < 0)
+                       break;
+               else
+                       ret = 0;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_dp_mst_atomic_check);
+
+const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs = {
+       .atomic_duplicate_state = drm_dp_mst_duplicate_state,
+       .atomic_destroy_state = drm_dp_mst_destroy_state,
+};
+EXPORT_SYMBOL(drm_dp_mst_topology_state_funcs);
+
+/**
+ * drm_atomic_get_mst_topology_state: get MST topology state
+ *
+ * @state: global atomic state
+ * @mgr: MST topology manager, also the private object in this case
+ *
+ * This function wraps drm_atomic_get_priv_obj_state() passing in the MST atomic
+ * state vtable so that the private object state returned is that of a MST
+ * topology object. Also, drm_atomic_get_private_obj_state() expects the caller
+ * to care of the locking, so warn if don't hold the connection_mutex.
+ *
+ * RETURNS:
+ *
+ * The MST topology state or error pointer.
+ */
+struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
+                                                                   struct drm_dp_mst_topology_mgr *mgr)
+{
+       return to_dp_mst_topology_state(drm_atomic_get_private_obj_state(state, &mgr->base));
+}
+EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
+
+/**
+ * drm_dp_mst_topology_mgr_init - initialise a topology manager
+ * @mgr: manager struct to initialise
+ * @dev: device providing this structure - for i2c addition.
+ * @aux: DP helper aux channel to talk to this device
+ * @max_dpcd_transaction_bytes: hw specific DPCD transaction limit
+ * @max_payloads: maximum number of payloads this GPU can source
+ * @max_lane_count: maximum number of lanes this GPU supports
+ * @max_link_rate: maximum link rate per lane this GPU supports in kHz
+ * @conn_base_id: the connector object ID the MST device is connected to.
+ *
+ * Return 0 for success, or negative error code on failure
+ */
+int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
+                                struct drm_device *dev, struct drm_dp_aux *aux,
+                                int max_dpcd_transaction_bytes, int max_payloads,
+                                int max_lane_count, int max_link_rate,
+                                int conn_base_id)
+{
+       struct drm_dp_mst_topology_state *mst_state;
+
+       mutex_init(&mgr->lock);
+       mutex_init(&mgr->qlock);
+       mutex_init(&mgr->payload_lock);
+       mutex_init(&mgr->delayed_destroy_lock);
+       mutex_init(&mgr->up_req_lock);
+       mutex_init(&mgr->probe_lock);
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+       mutex_init(&mgr->topology_ref_history_lock);
+       stack_depot_init();
+#endif
+       INIT_LIST_HEAD(&mgr->tx_msg_downq);
+       INIT_LIST_HEAD(&mgr->destroy_port_list);
+       INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
+       INIT_LIST_HEAD(&mgr->up_req_list);
+
+       /*
+        * delayed_destroy_work will be queued on a dedicated WQ, so that any
+        * requeuing will be also flushed when deiniting the topology manager.
+        */
+       mgr->delayed_destroy_wq = alloc_ordered_workqueue("drm_dp_mst_wq", 0);
+       if (mgr->delayed_destroy_wq == NULL)
+               return -ENOMEM;
+
+       INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
+       INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
+       INIT_WORK(&mgr->delayed_destroy_work, drm_dp_delayed_destroy_work);
+       INIT_WORK(&mgr->up_req_work, drm_dp_mst_up_req_work);
+       init_waitqueue_head(&mgr->tx_waitq);
+       mgr->dev = dev;
+       mgr->aux = aux;
+       mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes;
+       mgr->max_payloads = max_payloads;
+       mgr->max_lane_count = max_lane_count;
+       mgr->max_link_rate = max_link_rate;
+       mgr->conn_base_id = conn_base_id;
+       if (max_payloads + 1 > sizeof(mgr->payload_mask) * 8 ||
+           max_payloads + 1 > sizeof(mgr->vcpi_mask) * 8)
+               return -EINVAL;
+       mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL);
+       if (!mgr->payloads)
+               return -ENOMEM;
+       mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL);
+       if (!mgr->proposed_vcpis)
+               return -ENOMEM;
+       set_bit(0, &mgr->payload_mask);
+
+       mst_state = kzalloc(sizeof(*mst_state), GFP_KERNEL);
+       if (mst_state == NULL)
+               return -ENOMEM;
+
+       mst_state->total_avail_slots = 63;
+       mst_state->start_slot = 1;
+
+       mst_state->mgr = mgr;
+       INIT_LIST_HEAD(&mst_state->vcpis);
+
+       drm_atomic_private_obj_init(dev, &mgr->base,
+                                   &mst_state->base,
+                                   &drm_dp_mst_topology_state_funcs);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
+
+/**
+ * drm_dp_mst_topology_mgr_destroy() - destroy topology manager.
+ * @mgr: manager to destroy
+ */
+void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
+{
+       drm_dp_mst_topology_mgr_set_mst(mgr, false);
+       flush_work(&mgr->work);
+       /* The following will also drain any requeued work on the WQ. */
+       if (mgr->delayed_destroy_wq) {
+               destroy_workqueue(mgr->delayed_destroy_wq);
+               mgr->delayed_destroy_wq = NULL;
+       }
+       mutex_lock(&mgr->payload_lock);
+       kfree(mgr->payloads);
+       mgr->payloads = NULL;
+       kfree(mgr->proposed_vcpis);
+       mgr->proposed_vcpis = NULL;
+       mutex_unlock(&mgr->payload_lock);
+       mgr->dev = NULL;
+       mgr->aux = NULL;
+       drm_atomic_private_obj_fini(&mgr->base);
+       mgr->funcs = NULL;
+
+       mutex_destroy(&mgr->delayed_destroy_lock);
+       mutex_destroy(&mgr->payload_lock);
+       mutex_destroy(&mgr->qlock);
+       mutex_destroy(&mgr->lock);
+       mutex_destroy(&mgr->up_req_lock);
+       mutex_destroy(&mgr->probe_lock);
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+       mutex_destroy(&mgr->topology_ref_history_lock);
+#endif
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
+
+static bool remote_i2c_read_ok(const struct i2c_msg msgs[], int num)
+{
+       int i;
+
+       if (num - 1 > DP_REMOTE_I2C_READ_MAX_TRANSACTIONS)
+               return false;
+
+       for (i = 0; i < num - 1; i++) {
+               if (msgs[i].flags & I2C_M_RD ||
+                   msgs[i].len > 0xff)
+                       return false;
+       }
+
+       return msgs[num - 1].flags & I2C_M_RD &&
+               msgs[num - 1].len <= 0xff;
+}
+
+static bool remote_i2c_write_ok(const struct i2c_msg msgs[], int num)
+{
+       int i;
+
+       for (i = 0; i < num - 1; i++) {
+               if (msgs[i].flags & I2C_M_RD || !(msgs[i].flags & I2C_M_STOP) ||
+                   msgs[i].len > 0xff)
+                       return false;
+       }
+
+       return !(msgs[num - 1].flags & I2C_M_RD) && msgs[num - 1].len <= 0xff;
+}
+
+static int drm_dp_mst_i2c_read(struct drm_dp_mst_branch *mstb,
+                              struct drm_dp_mst_port *port,
+                              struct i2c_msg *msgs, int num)
+{
+       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+       unsigned int i;
+       struct drm_dp_sideband_msg_req_body msg;
+       struct drm_dp_sideband_msg_tx *txmsg = NULL;
+       int ret;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.req_type = DP_REMOTE_I2C_READ;
+       msg.u.i2c_read.num_transactions = num - 1;
+       msg.u.i2c_read.port_number = port->port_num;
+       for (i = 0; i < num - 1; i++) {
+               msg.u.i2c_read.transactions[i].i2c_dev_id = msgs[i].addr;
+               msg.u.i2c_read.transactions[i].num_bytes = msgs[i].len;
+               msg.u.i2c_read.transactions[i].bytes = msgs[i].buf;
+               msg.u.i2c_read.transactions[i].no_stop_bit = !(msgs[i].flags & I2C_M_STOP);
+       }
+       msg.u.i2c_read.read_i2c_device_id = msgs[num - 1].addr;
+       msg.u.i2c_read.num_bytes_read = msgs[num - 1].len;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       txmsg->dst = mstb;
+       drm_dp_encode_sideband_req(&msg, txmsg);
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret > 0) {
+
+               if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
+                       ret = -EREMOTEIO;
+                       goto out;
+               }
+               if (txmsg->reply.u.remote_i2c_read_ack.num_bytes != msgs[num - 1].len) {
+                       ret = -EIO;
+                       goto out;
+               }
+               memcpy(msgs[num - 1].buf, txmsg->reply.u.remote_i2c_read_ack.bytes, msgs[num - 1].len);
+               ret = num;
+       }
+out:
+       kfree(txmsg);
+       return ret;
+}
+
+static int drm_dp_mst_i2c_write(struct drm_dp_mst_branch *mstb,
+                               struct drm_dp_mst_port *port,
+                               struct i2c_msg *msgs, int num)
+{
+       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+       unsigned int i;
+       struct drm_dp_sideband_msg_req_body msg;
+       struct drm_dp_sideband_msg_tx *txmsg = NULL;
+       int ret;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg) {
+               ret = -ENOMEM;
+               goto out;
+       }
+       for (i = 0; i < num; i++) {
+               memset(&msg, 0, sizeof(msg));
+               msg.req_type = DP_REMOTE_I2C_WRITE;
+               msg.u.i2c_write.port_number = port->port_num;
+               msg.u.i2c_write.write_i2c_device_id = msgs[i].addr;
+               msg.u.i2c_write.num_bytes = msgs[i].len;
+               msg.u.i2c_write.bytes = msgs[i].buf;
+
+               memset(txmsg, 0, sizeof(*txmsg));
+               txmsg->dst = mstb;
+
+               drm_dp_encode_sideband_req(&msg, txmsg);
+               drm_dp_queue_down_tx(mgr, txmsg);
+
+               ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+               if (ret > 0) {
+                       if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
+                               ret = -EREMOTEIO;
+                               goto out;
+                       }
+               } else {
+                       goto out;
+               }
+       }
+       ret = num;
+out:
+       kfree(txmsg);
+       return ret;
+}
+
+/* I2C device */
+static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter,
+                              struct i2c_msg *msgs, int num)
+{
+       struct drm_dp_aux *aux = adapter->algo_data;
+       struct drm_dp_mst_port *port =
+               container_of(aux, struct drm_dp_mst_port, aux);
+       struct drm_dp_mst_branch *mstb;
+       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+       int ret;
+
+       mstb = drm_dp_mst_topology_get_mstb_validated(mgr, port->parent);
+       if (!mstb)
+               return -EREMOTEIO;
+
+       if (remote_i2c_read_ok(msgs, num)) {
+               ret = drm_dp_mst_i2c_read(mstb, port, msgs, num);
+       } else if (remote_i2c_write_ok(msgs, num)) {
+               ret = drm_dp_mst_i2c_write(mstb, port, msgs, num);
+       } else {
+               drm_dbg_kms(mgr->dev, "Unsupported I2C transaction for MST device\n");
+               ret = -EIO;
+       }
+
+       drm_dp_mst_topology_put_mstb(mstb);
+       return ret;
+}
+
+static u32 drm_dp_mst_i2c_functionality(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+              I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+              I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+              I2C_FUNC_10BIT_ADDR;
+}
+
+static const struct i2c_algorithm drm_dp_mst_i2c_algo = {
+       .functionality = drm_dp_mst_i2c_functionality,
+       .master_xfer = drm_dp_mst_i2c_xfer,
+};
+
+/**
+ * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
+ * @port: The port to add the I2C bus on
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port)
+{
+       struct drm_dp_aux *aux = &port->aux;
+       struct device *parent_dev = port->mgr->dev->dev;
+
+       aux->ddc.algo = &drm_dp_mst_i2c_algo;
+       aux->ddc.algo_data = aux;
+       aux->ddc.retries = 3;
+
+       aux->ddc.class = I2C_CLASS_DDC;
+       aux->ddc.owner = THIS_MODULE;
+       /* FIXME: set the kdev of the port's connector as parent */
+       aux->ddc.dev.parent = parent_dev;
+       aux->ddc.dev.of_node = parent_dev->of_node;
+
+       strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(parent_dev),
+               sizeof(aux->ddc.name));
+
+       return i2c_add_adapter(&aux->ddc);
+}
+
+/**
+ * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
+ * @port: The port to remove the I2C bus from
+ */
+static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port)
+{
+       i2c_del_adapter(&port->aux.ddc);
+}
+
+/**
+ * drm_dp_mst_is_virtual_dpcd() - Is the given port a virtual DP Peer Device
+ * @port: The port to check
+ *
+ * A single physical MST hub object can be represented in the topology
+ * by multiple branches, with virtual ports between those branches.
+ *
+ * As of DP1.4, An MST hub with internal (virtual) ports must expose
+ * certain DPCD registers over those ports. See sections 2.6.1.1.1
+ * and 2.6.1.1.2 of Display Port specification v1.4 for details.
+ *
+ * May acquire mgr->lock
+ *
+ * Returns:
+ * true if the port is a virtual DP peer device, false otherwise
+ */
+static bool drm_dp_mst_is_virtual_dpcd(struct drm_dp_mst_port *port)
+{
+       struct drm_dp_mst_port *downstream_port;
+
+       if (!port || port->dpcd_rev < DP_DPCD_REV_14)
+               return false;
+
+       /* Virtual DP Sink (Internal Display Panel) */
+       if (port->port_num >= 8)
+               return true;
+
+       /* DP-to-HDMI Protocol Converter */
+       if (port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV &&
+           !port->mcs &&
+           port->ldps)
+               return true;
+
+       /* DP-to-DP */
+       mutex_lock(&port->mgr->lock);
+       if (port->pdt == DP_PEER_DEVICE_MST_BRANCHING &&
+           port->mstb &&
+           port->mstb->num_ports == 2) {
+               list_for_each_entry(downstream_port, &port->mstb->ports, next) {
+                       if (downstream_port->pdt == DP_PEER_DEVICE_SST_SINK &&
+                           !downstream_port->input) {
+                               mutex_unlock(&port->mgr->lock);
+                               return true;
+                       }
+               }
+       }
+       mutex_unlock(&port->mgr->lock);
+
+       return false;
+}
+
+/**
+ * drm_dp_mst_dsc_aux_for_port() - Find the correct aux for DSC
+ * @port: The port to check. A leaf of the MST tree with an attached display.
+ *
+ * Depending on the situation, DSC may be enabled via the endpoint aux,
+ * the immediately upstream aux, or the connector's physical aux.
+ *
+ * This is both the correct aux to read DSC_CAPABILITY and the
+ * correct aux to write DSC_ENABLED.
+ *
+ * This operation can be expensive (up to four aux reads), so
+ * the caller should cache the return.
+ *
+ * Returns:
+ * NULL if DSC cannot be enabled on this port, otherwise the aux device
+ */
+struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port)
+{
+       struct drm_dp_mst_port *immediate_upstream_port;
+       struct drm_dp_mst_port *fec_port;
+       struct drm_dp_desc desc = {};
+       u8 endpoint_fec;
+       u8 endpoint_dsc;
+
+       if (!port)
+               return NULL;
+
+       if (port->parent->port_parent)
+               immediate_upstream_port = port->parent->port_parent;
+       else
+               immediate_upstream_port = NULL;
+
+       fec_port = immediate_upstream_port;
+       while (fec_port) {
+               /*
+                * Each physical link (i.e. not a virtual port) between the
+                * output and the primary device must support FEC
+                */
+               if (!drm_dp_mst_is_virtual_dpcd(fec_port) &&
+                   !fec_port->fec_capable)
+                       return NULL;
+
+               fec_port = fec_port->parent->port_parent;
+       }
+
+       /* DP-to-DP peer device */
+       if (drm_dp_mst_is_virtual_dpcd(immediate_upstream_port)) {
+               u8 upstream_dsc;
+
+               if (drm_dp_dpcd_read(&port->aux,
+                                    DP_DSC_SUPPORT, &endpoint_dsc, 1) != 1)
+                       return NULL;
+               if (drm_dp_dpcd_read(&port->aux,
+                                    DP_FEC_CAPABILITY, &endpoint_fec, 1) != 1)
+                       return NULL;
+               if (drm_dp_dpcd_read(&immediate_upstream_port->aux,
+                                    DP_DSC_SUPPORT, &upstream_dsc, 1) != 1)
+                       return NULL;
+
+               /* Enpoint decompression with DP-to-DP peer device */
+               if ((endpoint_dsc & DP_DSC_DECOMPRESSION_IS_SUPPORTED) &&
+                   (endpoint_fec & DP_FEC_CAPABLE) &&
+                   (upstream_dsc & 0x2) /* DSC passthrough */)
+                       return &port->aux;
+
+               /* Virtual DPCD decompression with DP-to-DP peer device */
+               return &immediate_upstream_port->aux;
+       }
+
+       /* Virtual DPCD decompression with DP-to-HDMI or Virtual DP Sink */
+       if (drm_dp_mst_is_virtual_dpcd(port))
+               return &port->aux;
+
+       /*
+        * Synaptics quirk
+        * Applies to ports for which:
+        * - Physical aux has Synaptics OUI
+        * - DPv1.4 or higher
+        * - Port is on primary branch device
+        * - Not a VGA adapter (DP_DWN_STRM_PORT_TYPE_ANALOG)
+        */
+       if (drm_dp_read_desc(port->mgr->aux, &desc, true))
+               return NULL;
+
+       if (drm_dp_has_quirk(&desc, DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) &&
+           port->mgr->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14 &&
+           port->parent == port->mgr->mst_primary) {
+               u8 dpcd_ext[DP_RECEIVER_CAP_SIZE];
+
+               if (drm_dp_read_dpcd_caps(port->mgr->aux, dpcd_ext) < 0)
+                       return NULL;
+
+               if ((dpcd_ext[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT) &&
+                   ((dpcd_ext[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK)
+                    != DP_DWN_STRM_PORT_TYPE_ANALOG))
+                       return port->mgr->aux;
+       }
+
+       /*
+        * The check below verifies if the MST sink
+        * connected to the GPU is capable of DSC -
+        * therefore the endpoint needs to be
+        * both DSC and FEC capable.
+        */
+       if (drm_dp_dpcd_read(&port->aux,
+          DP_DSC_SUPPORT, &endpoint_dsc, 1) != 1)
+               return NULL;
+       if (drm_dp_dpcd_read(&port->aux,
+          DP_FEC_CAPABILITY, &endpoint_fec, 1) != 1)
+               return NULL;
+       if ((endpoint_dsc & DP_DSC_DECOMPRESSION_IS_SUPPORTED) &&
+          (endpoint_fec & DP_FEC_CAPABLE))
+               return &port->aux;
+
+       return NULL;
+}
+EXPORT_SYMBOL(drm_dp_mst_dsc_aux_for_port);
diff --git a/drivers/gpu/drm/dp/drm_dp_mst_topology_internal.h b/drivers/gpu/drm/dp/drm_dp_mst_topology_internal.h
new file mode 100644 (file)
index 0000000..401953b
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Declarations for DP MST related functions which are only used in selftests
+ *
+ * Copyright © 2018 Red Hat
+ * Authors:
+ *     Lyude Paul <lyude@redhat.com>
+ */
+
+#ifndef _DRM_DP_MST_HELPER_INTERNAL_H_
+#define _DRM_DP_MST_HELPER_INTERNAL_H_
+
+#include <drm/dp/drm_dp_mst_helper.h>
+
+void
+drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
+                          struct drm_dp_sideband_msg_tx *raw);
+int drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
+                              struct drm_dp_sideband_msg_req_body *req);
+void
+drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
+                                 int indent, struct drm_printer *printer);
+
+#endif /* !_DRM_DP_MST_HELPER_INTERNAL_H_ */
diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c
new file mode 100644 (file)
index 0000000..d60878b
--- /dev/null
@@ -0,0 +1,535 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2021 Intel Corporation
+ */
+
+#include <linux/kmemleak.h>
+#include <linux/module.h>
+#include <linux/sizes.h>
+
+#include <drm/drm_buddy.h>
+
+static struct kmem_cache *slab_blocks;
+
+static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm,
+                                              struct drm_buddy_block *parent,
+                                              unsigned int order,
+                                              u64 offset)
+{
+       struct drm_buddy_block *block;
+
+       BUG_ON(order > DRM_BUDDY_MAX_ORDER);
+
+       block = kmem_cache_zalloc(slab_blocks, GFP_KERNEL);
+       if (!block)
+               return NULL;
+
+       block->header = offset;
+       block->header |= order;
+       block->parent = parent;
+
+       BUG_ON(block->header & DRM_BUDDY_HEADER_UNUSED);
+       return block;
+}
+
+static void drm_block_free(struct drm_buddy *mm,
+                          struct drm_buddy_block *block)
+{
+       kmem_cache_free(slab_blocks, block);
+}
+
+static void mark_allocated(struct drm_buddy_block *block)
+{
+       block->header &= ~DRM_BUDDY_HEADER_STATE;
+       block->header |= DRM_BUDDY_ALLOCATED;
+
+       list_del(&block->link);
+}
+
+static void mark_free(struct drm_buddy *mm,
+                     struct drm_buddy_block *block)
+{
+       block->header &= ~DRM_BUDDY_HEADER_STATE;
+       block->header |= DRM_BUDDY_FREE;
+
+       list_add(&block->link,
+                &mm->free_list[drm_buddy_block_order(block)]);
+}
+
+static void mark_split(struct drm_buddy_block *block)
+{
+       block->header &= ~DRM_BUDDY_HEADER_STATE;
+       block->header |= DRM_BUDDY_SPLIT;
+
+       list_del(&block->link);
+}
+
+/**
+ * drm_buddy_init - init memory manager
+ *
+ * @mm: DRM buddy manager to initialize
+ * @size: size in bytes to manage
+ * @chunk_size: minimum page size in bytes for our allocations
+ *
+ * Initializes the memory manager and its resources.
+ *
+ * Returns:
+ * 0 on success, error code on failure.
+ */
+int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)
+{
+       unsigned int i;
+       u64 offset;
+
+       if (size < chunk_size)
+               return -EINVAL;
+
+       if (chunk_size < PAGE_SIZE)
+               return -EINVAL;
+
+       if (!is_power_of_2(chunk_size))
+               return -EINVAL;
+
+       size = round_down(size, chunk_size);
+
+       mm->size = size;
+       mm->avail = size;
+       mm->chunk_size = chunk_size;
+       mm->max_order = ilog2(size) - ilog2(chunk_size);
+
+       BUG_ON(mm->max_order > DRM_BUDDY_MAX_ORDER);
+
+       mm->free_list = kmalloc_array(mm->max_order + 1,
+                                     sizeof(struct list_head),
+                                     GFP_KERNEL);
+       if (!mm->free_list)
+               return -ENOMEM;
+
+       for (i = 0; i <= mm->max_order; ++i)
+               INIT_LIST_HEAD(&mm->free_list[i]);
+
+       mm->n_roots = hweight64(size);
+
+       mm->roots = kmalloc_array(mm->n_roots,
+                                 sizeof(struct drm_buddy_block *),
+                                 GFP_KERNEL);
+       if (!mm->roots)
+               goto out_free_list;
+
+       offset = 0;
+       i = 0;
+
+       /*
+        * Split into power-of-two blocks, in case we are given a size that is
+        * not itself a power-of-two.
+        */
+       do {
+               struct drm_buddy_block *root;
+               unsigned int order;
+               u64 root_size;
+
+               root_size = rounddown_pow_of_two(size);
+               order = ilog2(root_size) - ilog2(chunk_size);
+
+               root = drm_block_alloc(mm, NULL, order, offset);
+               if (!root)
+                       goto out_free_roots;
+
+               mark_free(mm, root);
+
+               BUG_ON(i > mm->max_order);
+               BUG_ON(drm_buddy_block_size(mm, root) < chunk_size);
+
+               mm->roots[i] = root;
+
+               offset += root_size;
+               size -= root_size;
+               i++;
+       } while (size);
+
+       return 0;
+
+out_free_roots:
+       while (i--)
+               drm_block_free(mm, mm->roots[i]);
+       kfree(mm->roots);
+out_free_list:
+       kfree(mm->free_list);
+       return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_buddy_init);
+
+/**
+ * drm_buddy_fini - tear down the memory manager
+ *
+ * @mm: DRM buddy manager to free
+ *
+ * Cleanup memory manager resources and the freelist
+ */
+void drm_buddy_fini(struct drm_buddy *mm)
+{
+       int i;
+
+       for (i = 0; i < mm->n_roots; ++i) {
+               WARN_ON(!drm_buddy_block_is_free(mm->roots[i]));
+               drm_block_free(mm, mm->roots[i]);
+       }
+
+       WARN_ON(mm->avail != mm->size);
+
+       kfree(mm->roots);
+       kfree(mm->free_list);
+}
+EXPORT_SYMBOL(drm_buddy_fini);
+
+static int split_block(struct drm_buddy *mm,
+                      struct drm_buddy_block *block)
+{
+       unsigned int block_order = drm_buddy_block_order(block) - 1;
+       u64 offset = drm_buddy_block_offset(block);
+
+       BUG_ON(!drm_buddy_block_is_free(block));
+       BUG_ON(!drm_buddy_block_order(block));
+
+       block->left = drm_block_alloc(mm, block, block_order, offset);
+       if (!block->left)
+               return -ENOMEM;
+
+       block->right = drm_block_alloc(mm, block, block_order,
+                                      offset + (mm->chunk_size << block_order));
+       if (!block->right) {
+               drm_block_free(mm, block->left);
+               return -ENOMEM;
+       }
+
+       mark_free(mm, block->left);
+       mark_free(mm, block->right);
+
+       mark_split(block);
+
+       return 0;
+}
+
+static struct drm_buddy_block *
+get_buddy(struct drm_buddy_block *block)
+{
+       struct drm_buddy_block *parent;
+
+       parent = block->parent;
+       if (!parent)
+               return NULL;
+
+       if (parent->left == block)
+               return parent->right;
+
+       return parent->left;
+}
+
+static void __drm_buddy_free(struct drm_buddy *mm,
+                            struct drm_buddy_block *block)
+{
+       struct drm_buddy_block *parent;
+
+       while ((parent = block->parent)) {
+               struct drm_buddy_block *buddy;
+
+               buddy = get_buddy(block);
+
+               if (!drm_buddy_block_is_free(buddy))
+                       break;
+
+               list_del(&buddy->link);
+
+               drm_block_free(mm, block);
+               drm_block_free(mm, buddy);
+
+               block = parent;
+       }
+
+       mark_free(mm, block);
+}
+
+/**
+ * drm_buddy_free_block - free a block
+ *
+ * @mm: DRM buddy manager
+ * @block: block to be freed
+ */
+void drm_buddy_free_block(struct drm_buddy *mm,
+                         struct drm_buddy_block *block)
+{
+       BUG_ON(!drm_buddy_block_is_allocated(block));
+       mm->avail += drm_buddy_block_size(mm, block);
+       __drm_buddy_free(mm, block);
+}
+EXPORT_SYMBOL(drm_buddy_free_block);
+
+/**
+ * drm_buddy_free_list - free blocks
+ *
+ * @mm: DRM buddy manager
+ * @objects: input list head to free blocks
+ */
+void drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects)
+{
+       struct drm_buddy_block *block, *on;
+
+       list_for_each_entry_safe(block, on, objects, link) {
+               drm_buddy_free_block(mm, block);
+               cond_resched();
+       }
+       INIT_LIST_HEAD(objects);
+}
+EXPORT_SYMBOL(drm_buddy_free_list);
+
+/**
+ * drm_buddy_alloc_blocks - allocate power-of-two blocks
+ *
+ * @mm: DRM buddy manager to allocate from
+ * @order: size of the allocation
+ *
+ * The order value here translates to:
+ *
+ * 0 = 2^0 * mm->chunk_size
+ * 1 = 2^1 * mm->chunk_size
+ * 2 = 2^2 * mm->chunk_size
+ *
+ * Returns:
+ * allocated ptr to the &drm_buddy_block on success
+ */
+struct drm_buddy_block *
+drm_buddy_alloc_blocks(struct drm_buddy *mm, unsigned int order)
+{
+       struct drm_buddy_block *block = NULL;
+       unsigned int i;
+       int err;
+
+       for (i = order; i <= mm->max_order; ++i) {
+               block = list_first_entry_or_null(&mm->free_list[i],
+                                                struct drm_buddy_block,
+                                                link);
+               if (block)
+                       break;
+       }
+
+       if (!block)
+               return ERR_PTR(-ENOSPC);
+
+       BUG_ON(!drm_buddy_block_is_free(block));
+
+       while (i != order) {
+               err = split_block(mm, block);
+               if (unlikely(err))
+                       goto out_free;
+
+               /* Go low */
+               block = block->left;
+               i--;
+       }
+
+       mark_allocated(block);
+       mm->avail -= drm_buddy_block_size(mm, block);
+       kmemleak_update_trace(block);
+       return block;
+
+out_free:
+       if (i != order)
+               __drm_buddy_free(mm, block);
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL(drm_buddy_alloc_blocks);
+
+static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
+{
+       return s1 <= e2 && e1 >= s2;
+}
+
+static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2)
+{
+       return s1 <= s2 && e1 >= e2;
+}
+
+/**
+ * drm_buddy_alloc_range - allocate range
+ *
+ * @mm: DRM buddy manager to allocate from
+ * @blocks: output list head to add allocated blocks
+ * @start: start of the allowed range for this block
+ * @size: size of the allocation
+ *
+ * Intended for pre-allocating portions of the address space, for example to
+ * reserve a block for the initial framebuffer or similar, hence the expectation
+ * here is that drm_buddy_alloc_blocks() is still the main vehicle for
+ * allocations, so if that's not the case then the drm_mm range allocator is
+ * probably a much better fit, and so you should probably go use that instead.
+ *
+ * Note that it's safe to chain together multiple alloc_ranges
+ * with the same blocks list
+ *
+ * Returns:
+ * 0 on success, error code on failure.
+ */
+int drm_buddy_alloc_range(struct drm_buddy *mm,
+                         struct list_head *blocks,
+                         u64 start, u64 size)
+{
+       struct drm_buddy_block *block;
+       struct drm_buddy_block *buddy;
+       LIST_HEAD(allocated);
+       LIST_HEAD(dfs);
+       u64 end;
+       int err;
+       int i;
+
+       if (size < mm->chunk_size)
+               return -EINVAL;
+
+       if (!IS_ALIGNED(size | start, mm->chunk_size))
+               return -EINVAL;
+
+       if (range_overflows(start, size, mm->size))
+               return -EINVAL;
+
+       for (i = 0; i < mm->n_roots; ++i)
+               list_add_tail(&mm->roots[i]->tmp_link, &dfs);
+
+       end = start + size - 1;
+
+       do {
+               u64 block_start;
+               u64 block_end;
+
+               block = list_first_entry_or_null(&dfs,
+                                                struct drm_buddy_block,
+                                                tmp_link);
+               if (!block)
+                       break;
+
+               list_del(&block->tmp_link);
+
+               block_start = drm_buddy_block_offset(block);
+               block_end = block_start + drm_buddy_block_size(mm, block) - 1;
+
+               if (!overlaps(start, end, block_start, block_end))
+                       continue;
+
+               if (drm_buddy_block_is_allocated(block)) {
+                       err = -ENOSPC;
+                       goto err_free;
+               }
+
+               if (contains(start, end, block_start, block_end)) {
+                       if (!drm_buddy_block_is_free(block)) {
+                               err = -ENOSPC;
+                               goto err_free;
+                       }
+
+                       mark_allocated(block);
+                       mm->avail -= drm_buddy_block_size(mm, block);
+                       list_add_tail(&block->link, &allocated);
+                       continue;
+               }
+
+               if (!drm_buddy_block_is_split(block)) {
+                       err = split_block(mm, block);
+                       if (unlikely(err))
+                               goto err_undo;
+               }
+
+               list_add(&block->right->tmp_link, &dfs);
+               list_add(&block->left->tmp_link, &dfs);
+       } while (1);
+
+       list_splice_tail(&allocated, blocks);
+       return 0;
+
+err_undo:
+       /*
+        * We really don't want to leave around a bunch of split blocks, since
+        * bigger is better, so make sure we merge everything back before we
+        * free the allocated blocks.
+        */
+       buddy = get_buddy(block);
+       if (buddy &&
+           (drm_buddy_block_is_free(block) &&
+            drm_buddy_block_is_free(buddy)))
+               __drm_buddy_free(mm, block);
+
+err_free:
+       drm_buddy_free_list(mm, &allocated);
+       return err;
+}
+EXPORT_SYMBOL(drm_buddy_alloc_range);
+
+/**
+ * drm_buddy_block_print - print block information
+ *
+ * @mm: DRM buddy manager
+ * @block: DRM buddy block
+ * @p: DRM printer to use
+ */
+void drm_buddy_block_print(struct drm_buddy *mm,
+                          struct drm_buddy_block *block,
+                          struct drm_printer *p)
+{
+       u64 start = drm_buddy_block_offset(block);
+       u64 size = drm_buddy_block_size(mm, block);
+
+       drm_printf(p, "%#018llx-%#018llx: %llu\n", start, start + size, size);
+}
+EXPORT_SYMBOL(drm_buddy_block_print);
+
+/**
+ * drm_buddy_print - print allocator state
+ *
+ * @mm: DRM buddy manager
+ * @p: DRM printer to use
+ */
+void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p)
+{
+       int order;
+
+       drm_printf(p, "chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB\n",
+                  mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20);
+
+       for (order = mm->max_order; order >= 0; order--) {
+               struct drm_buddy_block *block;
+               u64 count = 0, free;
+
+               list_for_each_entry(block, &mm->free_list[order], link) {
+                       BUG_ON(!drm_buddy_block_is_free(block));
+                       count++;
+               }
+
+               drm_printf(p, "order-%d ", order);
+
+               free = count * (mm->chunk_size << order);
+               if (free < SZ_1M)
+                       drm_printf(p, "free: %lluKiB", free >> 10);
+               else
+                       drm_printf(p, "free: %lluMiB", free >> 20);
+
+               drm_printf(p, ", pages: %llu\n", count);
+       }
+}
+EXPORT_SYMBOL(drm_buddy_print);
+
+static void drm_buddy_module_exit(void)
+{
+       kmem_cache_destroy(slab_blocks);
+}
+
+static int __init drm_buddy_module_init(void)
+{
+       slab_blocks = KMEM_CACHE(drm_buddy_block, 0);
+       if (!slab_blocks)
+               return -ENOMEM;
+
+       return 0;
+}
+
+module_init(drm_buddy_module_init);
+module_exit(drm_buddy_module_exit);
+
+MODULE_DESCRIPTION("DRM Buddy Allocator");
+MODULE_LICENSE("Dual MIT/GPL");
index bb14f48..9079fbe 100644 (file)
  *     driver boot-up state too. Drivers can access this blob through
  *     &drm_crtc_state.gamma_lut.
  *
+ *     Note that for mostly historical reasons stemming from Xorg heritage,
+ *     this is also used to store the color map (also sometimes color lut, CLUT
+ *     or color palette) for indexed formats like DRM_FORMAT_C8.
+ *
  * “GAMMA_LUT_SIZE”:
  *     Unsigned range property to give the size of the lookup table to be set
  *     on the GAMMA_LUT property (the size depends on the underlying hardware).
index 61e09f8..28e04e7 100644 (file)
 
 #include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
-#include <drm/drm_dp_helper.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_modes.h>
 
-/* drm_dp_aux_dev.c */
-#ifdef CONFIG_DRM_DP_AUX_CHARDEV
-int drm_dp_aux_dev_init(void);
-void drm_dp_aux_dev_exit(void);
-int drm_dp_aux_register_devnode(struct drm_dp_aux *aux);
-void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux);
-#else
-static inline int drm_dp_aux_dev_init(void)
-{
-       return 0;
-}
-
-static inline void drm_dp_aux_dev_exit(void)
-{
-}
-
-static inline int drm_dp_aux_register_devnode(struct drm_dp_aux *aux)
-{
-       return 0;
-}
-
-static inline void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
-{
-}
-#endif
-
 /* drm_probe_helper.c */
 enum drm_mode_status drm_crtc_mode_valid(struct drm_crtc *crtc,
                                         const struct drm_display_mode *mode);
diff --git a/drivers/gpu/drm/drm_dp_aux_bus.c b/drivers/gpu/drm/drm_dp_aux_bus.c
deleted file mode 100644 (file)
index 298ea7a..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright 2021 Google Inc.
- *
- * The DP AUX bus is used for devices that are connected over a DisplayPort
- * AUX bus. The devices on the far side of the bus are referred to as
- * endpoints in this code.
- *
- * Commonly there is only one device connected to the DP AUX bus: a panel.
- * Though historically panels (even DP panels) have been modeled as simple
- * platform devices, putting them under the DP AUX bus allows the panel driver
- * to perform transactions on that bus.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/pm_domain.h>
-#include <linux/pm_runtime.h>
-
-#include <drm/drm_dp_aux_bus.h>
-#include <drm/drm_dp_helper.h>
-
-/**
- * dp_aux_ep_match() - The match function for the dp_aux_bus.
- * @dev: The device to match.
- * @drv: The driver to try to match against.
- *
- * At the moment, we just match on device tree.
- *
- * Return: True if this driver matches this device; false otherwise.
- */
-static int dp_aux_ep_match(struct device *dev, struct device_driver *drv)
-{
-       return !!of_match_device(drv->of_match_table, dev);
-}
-
-/**
- * dp_aux_ep_probe() - The probe function for the dp_aux_bus.
- * @dev: The device to probe.
- *
- * Calls through to the endpoint driver probe.
- *
- * Return: 0 if no error or negative error code.
- */
-static int dp_aux_ep_probe(struct device *dev)
-{
-       struct dp_aux_ep_driver *aux_ep_drv = to_dp_aux_ep_drv(dev->driver);
-       struct dp_aux_ep_device *aux_ep = to_dp_aux_ep_dev(dev);
-       int ret;
-
-       ret = dev_pm_domain_attach(dev, true);
-       if (ret)
-               return dev_err_probe(dev, ret, "Failed to attach to PM Domain\n");
-
-       ret = aux_ep_drv->probe(aux_ep);
-       if (ret)
-               dev_pm_domain_detach(dev, true);
-
-       return ret;
-}
-
-/**
- * dp_aux_ep_remove() - The remove function for the dp_aux_bus.
- * @dev: The device to remove.
- *
- * Calls through to the endpoint driver remove.
- *
- */
-static void dp_aux_ep_remove(struct device *dev)
-{
-       struct dp_aux_ep_driver *aux_ep_drv = to_dp_aux_ep_drv(dev->driver);
-       struct dp_aux_ep_device *aux_ep = to_dp_aux_ep_dev(dev);
-
-       if (aux_ep_drv->remove)
-               aux_ep_drv->remove(aux_ep);
-       dev_pm_domain_detach(dev, true);
-}
-
-/**
- * dp_aux_ep_shutdown() - The shutdown function for the dp_aux_bus.
- * @dev: The device to shutdown.
- *
- * Calls through to the endpoint driver shutdown.
- */
-static void dp_aux_ep_shutdown(struct device *dev)
-{
-       struct dp_aux_ep_driver *aux_ep_drv;
-
-       if (!dev->driver)
-               return;
-
-       aux_ep_drv = to_dp_aux_ep_drv(dev->driver);
-       if (aux_ep_drv->shutdown)
-               aux_ep_drv->shutdown(to_dp_aux_ep_dev(dev));
-}
-
-static struct bus_type dp_aux_bus_type = {
-       .name           = "dp-aux",
-       .match          = dp_aux_ep_match,
-       .probe          = dp_aux_ep_probe,
-       .remove         = dp_aux_ep_remove,
-       .shutdown       = dp_aux_ep_shutdown,
-};
-
-static ssize_t modalias_show(struct device *dev,
-                            struct device_attribute *attr, char *buf)
-{
-       return of_device_modalias(dev, buf, PAGE_SIZE);
-}
-static DEVICE_ATTR_RO(modalias);
-
-static struct attribute *dp_aux_ep_dev_attrs[] = {
-       &dev_attr_modalias.attr,
-       NULL,
-};
-ATTRIBUTE_GROUPS(dp_aux_ep_dev);
-
-/**
- * dp_aux_ep_dev_release() - Free memory for the dp_aux_ep device
- * @dev: The device to free.
- *
- * Return: 0 if no error or negative error code.
- */
-static void dp_aux_ep_dev_release(struct device *dev)
-{
-       kfree(to_dp_aux_ep_dev(dev));
-}
-
-static struct device_type dp_aux_device_type_type = {
-       .groups         = dp_aux_ep_dev_groups,
-       .uevent         = of_device_uevent_modalias,
-       .release        = dp_aux_ep_dev_release,
-};
-
-/**
- * of_dp_aux_ep_destroy() - Destroy an DP AUX endpoint device
- * @dev: The device to destroy.
- * @data: Not used
- *
- * This is just used as a callback by of_dp_aux_depopulate_ep_devices() and
- * is called for _all_ of the child devices of the device providing the AUX bus.
- * We'll only act on those that are of type "dp_aux_bus_type".
- *
- * This function is effectively an inverse of what's in the loop
- * in of_dp_aux_populate_ep_devices().
- *
- * Return: 0 if no error or negative error code.
- */
-static int of_dp_aux_ep_destroy(struct device *dev, void *data)
-{
-       struct device_node *np = dev->of_node;
-
-       if (dev->bus != &dp_aux_bus_type)
-               return 0;
-
-       if (!of_node_check_flag(np, OF_POPULATED))
-               return 0;
-
-       of_node_clear_flag(np, OF_POPULATED);
-       of_node_put(np);
-
-       device_unregister(dev);
-
-       return 0;
-}
-
-/**
- * of_dp_aux_depopulate_ep_devices() - Undo of_dp_aux_populate_ep_devices
- * @aux: The AUX channel whose devices we want to depopulate
- *
- * This will destroy all devices that were created
- * by of_dp_aux_populate_ep_devices().
- */
-void of_dp_aux_depopulate_ep_devices(struct drm_dp_aux *aux)
-{
-       device_for_each_child_reverse(aux->dev, NULL, of_dp_aux_ep_destroy);
-}
-EXPORT_SYMBOL_GPL(of_dp_aux_depopulate_ep_devices);
-
-/**
- * of_dp_aux_populate_ep_devices() - Populate the endpoint devices on the DP AUX
- * @aux: The AUX channel whose devices we want to populate. It is required that
- *       drm_dp_aux_init() has already been called for this AUX channel.
- *
- * This will populate all the devices under the "aux-bus" node of the device
- * providing the AUX channel (AKA aux->dev).
- *
- * When this function finishes, it is _possible_ (but not guaranteed) that
- * our sub-devices will have finished probing. It should be noted that if our
- * sub-devices return -EPROBE_DEFER that we will not return any error codes
- * ourselves but our sub-devices will _not_ have actually probed successfully
- * yet. There may be other cases (maybe added in the future?) where sub-devices
- * won't have been probed yet when this function returns, so it's best not to
- * rely on that.
- *
- * If this function succeeds you should later make sure you call
- * of_dp_aux_depopulate_ep_devices() to undo it, or just use the devm version
- * of this function.
- *
- * Return: 0 if no error or negative error code.
- */
-int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
-{
-       struct device_node *bus, *np;
-       struct dp_aux_ep_device *aux_ep;
-       int ret;
-
-       /* drm_dp_aux_init() should have been called already; warn if not */
-       WARN_ON_ONCE(!aux->ddc.algo);
-
-       if (!aux->dev->of_node)
-               return 0;
-
-       bus = of_get_child_by_name(aux->dev->of_node, "aux-bus");
-       if (!bus)
-               return 0;
-
-       for_each_available_child_of_node(bus, np) {
-               if (of_node_test_and_set_flag(np, OF_POPULATED))
-                       continue;
-
-               aux_ep = kzalloc(sizeof(*aux_ep), GFP_KERNEL);
-               if (!aux_ep)
-                       continue;
-               aux_ep->aux = aux;
-
-               aux_ep->dev.parent = aux->dev;
-               aux_ep->dev.bus = &dp_aux_bus_type;
-               aux_ep->dev.type = &dp_aux_device_type_type;
-               aux_ep->dev.of_node = of_node_get(np);
-               dev_set_name(&aux_ep->dev, "aux-%s", dev_name(aux->dev));
-
-               ret = device_register(&aux_ep->dev);
-               if (ret) {
-                       dev_err(aux->dev, "Failed to create AUX EP for %pOF: %d\n", np, ret);
-                       of_node_clear_flag(np, OF_POPULATED);
-                       of_node_put(np);
-
-                       /*
-                        * As per docs of device_register(), call this instead
-                        * of kfree() directly for error cases.
-                        */
-                       put_device(&aux_ep->dev);
-
-                       /*
-                        * Following in the footsteps of of_i2c_register_devices(),
-                        * we won't fail the whole function here--we'll just
-                        * continue registering any other devices we find.
-                        */
-               }
-       }
-
-       of_node_put(bus);
-
-       return 0;
-}
-
-static void of_dp_aux_depopulate_ep_devices_void(void *data)
-{
-       of_dp_aux_depopulate_ep_devices(data);
-}
-
-/**
- * devm_of_dp_aux_populate_ep_devices() - devm wrapper for of_dp_aux_populate_ep_devices()
- * @aux: The AUX channel whose devices we want to populate
- *
- * Handles freeing w/ devm on the device "aux->dev".
- *
- * Return: 0 if no error or negative error code.
- */
-int devm_of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux)
-{
-       int ret;
-
-       ret = of_dp_aux_populate_ep_devices(aux);
-       if (ret)
-               return ret;
-
-       return devm_add_action_or_reset(aux->dev,
-                                       of_dp_aux_depopulate_ep_devices_void,
-                                       aux);
-}
-EXPORT_SYMBOL_GPL(devm_of_dp_aux_populate_ep_devices);
-
-int __dp_aux_dp_driver_register(struct dp_aux_ep_driver *drv, struct module *owner)
-{
-       drv->driver.owner = owner;
-       drv->driver.bus = &dp_aux_bus_type;
-
-       return driver_register(&drv->driver);
-}
-EXPORT_SYMBOL_GPL(__dp_aux_dp_driver_register);
-
-void dp_aux_dp_driver_unregister(struct dp_aux_ep_driver *drv)
-{
-       driver_unregister(&drv->driver);
-}
-EXPORT_SYMBOL_GPL(dp_aux_dp_driver_unregister);
-
-static int __init dp_aux_bus_init(void)
-{
-       int ret;
-
-       ret = bus_register(&dp_aux_bus_type);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-static void __exit dp_aux_bus_exit(void)
-{
-       bus_unregister(&dp_aux_bus_type);
-}
-
-subsys_initcall(dp_aux_bus_init);
-module_exit(dp_aux_bus_exit);
-
-MODULE_AUTHOR("Douglas Anderson <dianders@chromium.org>");
-MODULE_DESCRIPTION("DRM DisplayPort AUX bus");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c
deleted file mode 100644 (file)
index 06b374c..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * Copyright © 2015 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- *
- * Authors:
- *    Rafael Antognolli <rafael.antognolli@intel.com>
- *
- */
-
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched/signal.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/uio.h>
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_dp_helper.h>
-#include <drm/drm_dp_mst_helper.h>
-#include <drm/drm_print.h>
-
-#include "drm_crtc_helper_internal.h"
-
-struct drm_dp_aux_dev {
-       unsigned index;
-       struct drm_dp_aux *aux;
-       struct device *dev;
-       struct kref refcount;
-       atomic_t usecount;
-};
-
-#define DRM_AUX_MINORS 256
-#define AUX_MAX_OFFSET (1 << 20)
-static DEFINE_IDR(aux_idr);
-static DEFINE_MUTEX(aux_idr_mutex);
-static struct class *drm_dp_aux_dev_class;
-static int drm_dev_major = -1;
-
-static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_minor(unsigned index)
-{
-       struct drm_dp_aux_dev *aux_dev = NULL;
-
-       mutex_lock(&aux_idr_mutex);
-       aux_dev = idr_find(&aux_idr, index);
-       if (aux_dev && !kref_get_unless_zero(&aux_dev->refcount))
-               aux_dev = NULL;
-       mutex_unlock(&aux_idr_mutex);
-
-       return aux_dev;
-}
-
-static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux)
-{
-       struct drm_dp_aux_dev *aux_dev;
-       int index;
-
-       aux_dev = kzalloc(sizeof(*aux_dev), GFP_KERNEL);
-       if (!aux_dev)
-               return ERR_PTR(-ENOMEM);
-       aux_dev->aux = aux;
-       atomic_set(&aux_dev->usecount, 1);
-       kref_init(&aux_dev->refcount);
-
-       mutex_lock(&aux_idr_mutex);
-       index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL);
-       mutex_unlock(&aux_idr_mutex);
-       if (index < 0) {
-               kfree(aux_dev);
-               return ERR_PTR(index);
-       }
-       aux_dev->index = index;
-
-       return aux_dev;
-}
-
-static void release_drm_dp_aux_dev(struct kref *ref)
-{
-       struct drm_dp_aux_dev *aux_dev =
-               container_of(ref, struct drm_dp_aux_dev, refcount);
-
-       kfree(aux_dev);
-}
-
-static ssize_t name_show(struct device *dev,
-                        struct device_attribute *attr, char *buf)
-{
-       ssize_t res;
-       struct drm_dp_aux_dev *aux_dev =
-               drm_dp_aux_dev_get_by_minor(MINOR(dev->devt));
-
-       if (!aux_dev)
-               return -ENODEV;
-
-       res = sprintf(buf, "%s\n", aux_dev->aux->name);
-       kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
-
-       return res;
-}
-static DEVICE_ATTR_RO(name);
-
-static struct attribute *drm_dp_aux_attrs[] = {
-       &dev_attr_name.attr,
-       NULL,
-};
-ATTRIBUTE_GROUPS(drm_dp_aux);
-
-static int auxdev_open(struct inode *inode, struct file *file)
-{
-       unsigned int minor = iminor(inode);
-       struct drm_dp_aux_dev *aux_dev;
-
-       aux_dev = drm_dp_aux_dev_get_by_minor(minor);
-       if (!aux_dev)
-               return -ENODEV;
-
-       file->private_data = aux_dev;
-       return 0;
-}
-
-static loff_t auxdev_llseek(struct file *file, loff_t offset, int whence)
-{
-       return fixed_size_llseek(file, offset, whence, AUX_MAX_OFFSET);
-}
-
-static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
-{
-       struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data;
-       loff_t pos = iocb->ki_pos;
-       ssize_t res = 0;
-
-       if (!atomic_inc_not_zero(&aux_dev->usecount))
-               return -ENODEV;
-
-       iov_iter_truncate(to, AUX_MAX_OFFSET - pos);
-
-       while (iov_iter_count(to)) {
-               uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES];
-               ssize_t todo = min(iov_iter_count(to), sizeof(buf));
-
-               if (signal_pending(current)) {
-                       res = -ERESTARTSYS;
-                       break;
-               }
-
-               res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
-
-               if (res <= 0)
-                       break;
-
-               if (copy_to_iter(buf, res, to) != res) {
-                       res = -EFAULT;
-                       break;
-               }
-
-               pos += res;
-       }
-
-       if (pos != iocb->ki_pos)
-               res = pos - iocb->ki_pos;
-       iocb->ki_pos = pos;
-
-       if (atomic_dec_and_test(&aux_dev->usecount))
-               wake_up_var(&aux_dev->usecount);
-
-       return res;
-}
-
-static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
-{
-       struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data;
-       loff_t pos = iocb->ki_pos;
-       ssize_t res = 0;
-
-       if (!atomic_inc_not_zero(&aux_dev->usecount))
-               return -ENODEV;
-
-       iov_iter_truncate(from, AUX_MAX_OFFSET - pos);
-
-       while (iov_iter_count(from)) {
-               uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES];
-               ssize_t todo = min(iov_iter_count(from), sizeof(buf));
-
-               if (signal_pending(current)) {
-                       res = -ERESTARTSYS;
-                       break;
-               }
-
-               if (!copy_from_iter_full(buf, todo, from)) {
-                       res = -EFAULT;
-                       break;
-               }
-
-               res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo);
-
-               if (res <= 0)
-                       break;
-
-               pos += res;
-       }
-
-       if (pos != iocb->ki_pos)
-               res = pos - iocb->ki_pos;
-       iocb->ki_pos = pos;
-
-       if (atomic_dec_and_test(&aux_dev->usecount))
-               wake_up_var(&aux_dev->usecount);
-
-       return res;
-}
-
-static int auxdev_release(struct inode *inode, struct file *file)
-{
-       struct drm_dp_aux_dev *aux_dev = file->private_data;
-
-       kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
-       return 0;
-}
-
-static const struct file_operations auxdev_fops = {
-       .owner          = THIS_MODULE,
-       .llseek         = auxdev_llseek,
-       .read_iter      = auxdev_read_iter,
-       .write_iter     = auxdev_write_iter,
-       .open           = auxdev_open,
-       .release        = auxdev_release,
-};
-
-#define to_auxdev(d) container_of(d, struct drm_dp_aux_dev, aux)
-
-static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_aux(struct drm_dp_aux *aux)
-{
-       struct drm_dp_aux_dev *iter, *aux_dev = NULL;
-       int id;
-
-       /* don't increase kref count here because this function should only be
-        * used by drm_dp_aux_unregister_devnode. Thus, it will always have at
-        * least one reference - the one that drm_dp_aux_register_devnode
-        * created
-        */
-       mutex_lock(&aux_idr_mutex);
-       idr_for_each_entry(&aux_idr, iter, id) {
-               if (iter->aux == aux) {
-                       aux_dev = iter;
-                       break;
-               }
-       }
-       mutex_unlock(&aux_idr_mutex);
-       return aux_dev;
-}
-
-void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
-{
-       struct drm_dp_aux_dev *aux_dev;
-       unsigned int minor;
-
-       aux_dev = drm_dp_aux_dev_get_by_aux(aux);
-       if (!aux_dev) /* attach must have failed */
-               return;
-
-       /*
-        * As some AUX adapters may exist as platform devices which outlive their respective DRM
-        * devices, we clear drm_dev to ensure that we never accidentally reference a stale pointer
-        */
-       aux->drm_dev = NULL;
-
-       mutex_lock(&aux_idr_mutex);
-       idr_remove(&aux_idr, aux_dev->index);
-       mutex_unlock(&aux_idr_mutex);
-
-       atomic_dec(&aux_dev->usecount);
-       wait_var_event(&aux_dev->usecount, !atomic_read(&aux_dev->usecount));
-
-       minor = aux_dev->index;
-       if (aux_dev->dev)
-               device_destroy(drm_dp_aux_dev_class,
-                              MKDEV(drm_dev_major, minor));
-
-       DRM_DEBUG("drm_dp_aux_dev: aux [%s] unregistering\n", aux->name);
-       kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
-}
-
-int drm_dp_aux_register_devnode(struct drm_dp_aux *aux)
-{
-       struct drm_dp_aux_dev *aux_dev;
-       int res;
-
-       aux_dev = alloc_drm_dp_aux_dev(aux);
-       if (IS_ERR(aux_dev))
-               return PTR_ERR(aux_dev);
-
-       aux_dev->dev = device_create(drm_dp_aux_dev_class, aux->dev,
-                                    MKDEV(drm_dev_major, aux_dev->index), NULL,
-                                    "drm_dp_aux%d", aux_dev->index);
-       if (IS_ERR(aux_dev->dev)) {
-               res = PTR_ERR(aux_dev->dev);
-               aux_dev->dev = NULL;
-               goto error;
-       }
-
-       DRM_DEBUG("drm_dp_aux_dev: aux [%s] registered as minor %d\n",
-                 aux->name, aux_dev->index);
-       return 0;
-error:
-       drm_dp_aux_unregister_devnode(aux);
-       return res;
-}
-
-int drm_dp_aux_dev_init(void)
-{
-       int res;
-
-       drm_dp_aux_dev_class = class_create(THIS_MODULE, "drm_dp_aux_dev");
-       if (IS_ERR(drm_dp_aux_dev_class)) {
-               return PTR_ERR(drm_dp_aux_dev_class);
-       }
-       drm_dp_aux_dev_class->dev_groups = drm_dp_aux_groups;
-
-       res = register_chrdev(0, "aux", &auxdev_fops);
-       if (res < 0)
-               goto out;
-       drm_dev_major = res;
-
-       return 0;
-out:
-       class_destroy(drm_dp_aux_dev_class);
-       return res;
-}
-
-void drm_dp_aux_dev_exit(void)
-{
-       unregister_chrdev(drm_dev_major, "aux");
-       class_destroy(drm_dp_aux_dev_class);
-}
diff --git a/drivers/gpu/drm/drm_dp_cec.c b/drivers/gpu/drm/drm_dp_cec.c
deleted file mode 100644 (file)
index 3ab2609..0000000
+++ /dev/null
@@ -1,451 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * DisplayPort CEC-Tunneling-over-AUX support
- *
- * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-
-#include <media/cec.h>
-
-#include <drm/drm_connector.h>
-#include <drm/drm_device.h>
-#include <drm/drm_dp_helper.h>
-
-/*
- * Unfortunately it turns out that we have a chicken-and-egg situation
- * here. Quite a few active (mini-)DP-to-HDMI or USB-C-to-HDMI adapters
- * have a converter chip that supports CEC-Tunneling-over-AUX (usually the
- * Parade PS176), but they do not wire up the CEC pin, thus making CEC
- * useless. Note that MegaChips 2900-based adapters appear to have good
- * support for CEC tunneling. Those adapters that I have tested using
- * this chipset all have the CEC line connected.
- *
- * Sadly there is no way for this driver to know this. What happens is
- * that a /dev/cecX device is created that is isolated and unable to see
- * any of the other CEC devices. Quite literally the CEC wire is cut
- * (or in this case, never connected in the first place).
- *
- * The reason so few adapters support this is that this tunneling protocol
- * was never supported by any OS. So there was no easy way of testing it,
- * and no incentive to correctly wire up the CEC pin.
- *
- * Hopefully by creating this driver it will be easier for vendors to
- * finally fix their adapters and test the CEC functionality.
- *
- * I keep a list of known working adapters here:
- *
- * https://hverkuil.home.xs4all.nl/cec-status.txt
- *
- * Please mail me (hverkuil@xs4all.nl) if you find an adapter that works
- * and is not yet listed there.
- *
- * Note that the current implementation does not support CEC over an MST hub.
- * As far as I can see there is no mechanism defined in the DisplayPort
- * standard to transport CEC interrupts over an MST device. It might be
- * possible to do this through polling, but I have not been able to get that
- * to work.
- */
-
-/**
- * DOC: dp cec helpers
- *
- * These functions take care of supporting the CEC-Tunneling-over-AUX
- * feature of DisplayPort-to-HDMI adapters.
- */
-
-/*
- * When the EDID is unset because the HPD went low, then the CEC DPCD registers
- * typically can no longer be read (true for a DP-to-HDMI adapter since it is
- * powered by the HPD). However, some displays toggle the HPD off and on for a
- * short period for one reason or another, and that would cause the CEC adapter
- * to be removed and added again, even though nothing else changed.
- *
- * This module parameter sets a delay in seconds before the CEC adapter is
- * actually unregistered. Only if the HPD does not return within that time will
- * the CEC adapter be unregistered.
- *
- * If it is set to a value >= NEVER_UNREG_DELAY, then the CEC adapter will never
- * be unregistered for as long as the connector remains registered.
- *
- * If it is set to 0, then the CEC adapter will be unregistered immediately as
- * soon as the HPD disappears.
- *
- * The default is one second to prevent short HPD glitches from unregistering
- * the CEC adapter.
- *
- * Note that for integrated HDMI branch devices that support CEC the DPCD
- * registers remain available even if the HPD goes low since it is not powered
- * by the HPD. In that case the CEC adapter will never be unregistered during
- * the life time of the connector. At least, this is the theory since I do not
- * have hardware with an integrated HDMI branch device that supports CEC.
- */
-#define NEVER_UNREG_DELAY 1000
-static unsigned int drm_dp_cec_unregister_delay = 1;
-module_param(drm_dp_cec_unregister_delay, uint, 0600);
-MODULE_PARM_DESC(drm_dp_cec_unregister_delay,
-                "CEC unregister delay in seconds, 0: no delay, >= 1000: never unregister");
-
-static int drm_dp_cec_adap_enable(struct cec_adapter *adap, bool enable)
-{
-       struct drm_dp_aux *aux = cec_get_drvdata(adap);
-       u32 val = enable ? DP_CEC_TUNNELING_ENABLE : 0;
-       ssize_t err = 0;
-
-       err = drm_dp_dpcd_writeb(aux, DP_CEC_TUNNELING_CONTROL, val);
-       return (enable && err < 0) ? err : 0;
-}
-
-static int drm_dp_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
-{
-       struct drm_dp_aux *aux = cec_get_drvdata(adap);
-       /* Bit 15 (logical address 15) should always be set */
-       u16 la_mask = 1 << CEC_LOG_ADDR_BROADCAST;
-       u8 mask[2];
-       ssize_t err;
-
-       if (addr != CEC_LOG_ADDR_INVALID)
-               la_mask |= adap->log_addrs.log_addr_mask | (1 << addr);
-       mask[0] = la_mask & 0xff;
-       mask[1] = la_mask >> 8;
-       err = drm_dp_dpcd_write(aux, DP_CEC_LOGICAL_ADDRESS_MASK, mask, 2);
-       return (addr != CEC_LOG_ADDR_INVALID && err < 0) ? err : 0;
-}
-
-static int drm_dp_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
-                                   u32 signal_free_time, struct cec_msg *msg)
-{
-       struct drm_dp_aux *aux = cec_get_drvdata(adap);
-       unsigned int retries = min(5, attempts - 1);
-       ssize_t err;
-
-       err = drm_dp_dpcd_write(aux, DP_CEC_TX_MESSAGE_BUFFER,
-                               msg->msg, msg->len);
-       if (err < 0)
-               return err;
-
-       err = drm_dp_dpcd_writeb(aux, DP_CEC_TX_MESSAGE_INFO,
-                                (msg->len - 1) | (retries << 4) |
-                                DP_CEC_TX_MESSAGE_SEND);
-       return err < 0 ? err : 0;
-}
-
-static int drm_dp_cec_adap_monitor_all_enable(struct cec_adapter *adap,
-                                             bool enable)
-{
-       struct drm_dp_aux *aux = cec_get_drvdata(adap);
-       ssize_t err;
-       u8 val;
-
-       if (!(adap->capabilities & CEC_CAP_MONITOR_ALL))
-               return 0;
-
-       err = drm_dp_dpcd_readb(aux, DP_CEC_TUNNELING_CONTROL, &val);
-       if (err >= 0) {
-               if (enable)
-                       val |= DP_CEC_SNOOPING_ENABLE;
-               else
-                       val &= ~DP_CEC_SNOOPING_ENABLE;
-               err = drm_dp_dpcd_writeb(aux, DP_CEC_TUNNELING_CONTROL, val);
-       }
-       return (enable && err < 0) ? err : 0;
-}
-
-static void drm_dp_cec_adap_status(struct cec_adapter *adap,
-                                  struct seq_file *file)
-{
-       struct drm_dp_aux *aux = cec_get_drvdata(adap);
-       struct drm_dp_desc desc;
-       struct drm_dp_dpcd_ident *id = &desc.ident;
-
-       if (drm_dp_read_desc(aux, &desc, true))
-               return;
-       seq_printf(file, "OUI: %*phD\n",
-                  (int)sizeof(id->oui), id->oui);
-       seq_printf(file, "ID: %*pE\n",
-                  (int)strnlen(id->device_id, sizeof(id->device_id)),
-                  id->device_id);
-       seq_printf(file, "HW Rev: %d.%d\n", id->hw_rev >> 4, id->hw_rev & 0xf);
-       /*
-        * Show this both in decimal and hex: at least one vendor
-        * always reports this in hex.
-        */
-       seq_printf(file, "FW/SW Rev: %d.%d (0x%02x.0x%02x)\n",
-                  id->sw_major_rev, id->sw_minor_rev,
-                  id->sw_major_rev, id->sw_minor_rev);
-}
-
-static const struct cec_adap_ops drm_dp_cec_adap_ops = {
-       .adap_enable = drm_dp_cec_adap_enable,
-       .adap_log_addr = drm_dp_cec_adap_log_addr,
-       .adap_transmit = drm_dp_cec_adap_transmit,
-       .adap_monitor_all_enable = drm_dp_cec_adap_monitor_all_enable,
-       .adap_status = drm_dp_cec_adap_status,
-};
-
-static int drm_dp_cec_received(struct drm_dp_aux *aux)
-{
-       struct cec_adapter *adap = aux->cec.adap;
-       struct cec_msg msg;
-       u8 rx_msg_info;
-       ssize_t err;
-
-       err = drm_dp_dpcd_readb(aux, DP_CEC_RX_MESSAGE_INFO, &rx_msg_info);
-       if (err < 0)
-               return err;
-
-       if (!(rx_msg_info & DP_CEC_RX_MESSAGE_ENDED))
-               return 0;
-
-       msg.len = (rx_msg_info & DP_CEC_RX_MESSAGE_LEN_MASK) + 1;
-       err = drm_dp_dpcd_read(aux, DP_CEC_RX_MESSAGE_BUFFER, msg.msg, msg.len);
-       if (err < 0)
-               return err;
-
-       cec_received_msg(adap, &msg);
-       return 0;
-}
-
-static void drm_dp_cec_handle_irq(struct drm_dp_aux *aux)
-{
-       struct cec_adapter *adap = aux->cec.adap;
-       u8 flags;
-
-       if (drm_dp_dpcd_readb(aux, DP_CEC_TUNNELING_IRQ_FLAGS, &flags) < 0)
-               return;
-
-       if (flags & DP_CEC_RX_MESSAGE_INFO_VALID)
-               drm_dp_cec_received(aux);
-
-       if (flags & DP_CEC_TX_MESSAGE_SENT)
-               cec_transmit_attempt_done(adap, CEC_TX_STATUS_OK);
-       else if (flags & DP_CEC_TX_LINE_ERROR)
-               cec_transmit_attempt_done(adap, CEC_TX_STATUS_ERROR |
-                                               CEC_TX_STATUS_MAX_RETRIES);
-       else if (flags &
-                (DP_CEC_TX_ADDRESS_NACK_ERROR | DP_CEC_TX_DATA_NACK_ERROR))
-               cec_transmit_attempt_done(adap, CEC_TX_STATUS_NACK |
-                                               CEC_TX_STATUS_MAX_RETRIES);
-       drm_dp_dpcd_writeb(aux, DP_CEC_TUNNELING_IRQ_FLAGS, flags);
-}
-
-/**
- * drm_dp_cec_irq() - handle CEC interrupt, if any
- * @aux: DisplayPort AUX channel
- *
- * Should be called when handling an IRQ_HPD request. If CEC-tunneling-over-AUX
- * is present, then it will check for a CEC_IRQ and handle it accordingly.
- */
-void drm_dp_cec_irq(struct drm_dp_aux *aux)
-{
-       u8 cec_irq;
-       int ret;
-
-       /* No transfer function was set, so not a DP connector */
-       if (!aux->transfer)
-               return;
-
-       mutex_lock(&aux->cec.lock);
-       if (!aux->cec.adap)
-               goto unlock;
-
-       ret = drm_dp_dpcd_readb(aux, DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1,
-                               &cec_irq);
-       if (ret < 0 || !(cec_irq & DP_CEC_IRQ))
-               goto unlock;
-
-       drm_dp_cec_handle_irq(aux);
-       drm_dp_dpcd_writeb(aux, DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1, DP_CEC_IRQ);
-unlock:
-       mutex_unlock(&aux->cec.lock);
-}
-EXPORT_SYMBOL(drm_dp_cec_irq);
-
-static bool drm_dp_cec_cap(struct drm_dp_aux *aux, u8 *cec_cap)
-{
-       u8 cap = 0;
-
-       if (drm_dp_dpcd_readb(aux, DP_CEC_TUNNELING_CAPABILITY, &cap) != 1 ||
-           !(cap & DP_CEC_TUNNELING_CAPABLE))
-               return false;
-       if (cec_cap)
-               *cec_cap = cap;
-       return true;
-}
-
-/*
- * Called if the HPD was low for more than drm_dp_cec_unregister_delay
- * seconds. This unregisters the CEC adapter.
- */
-static void drm_dp_cec_unregister_work(struct work_struct *work)
-{
-       struct drm_dp_aux *aux = container_of(work, struct drm_dp_aux,
-                                             cec.unregister_work.work);
-
-       mutex_lock(&aux->cec.lock);
-       cec_unregister_adapter(aux->cec.adap);
-       aux->cec.adap = NULL;
-       mutex_unlock(&aux->cec.lock);
-}
-
-/*
- * A new EDID is set. If there is no CEC adapter, then create one. If
- * there was a CEC adapter, then check if the CEC adapter properties
- * were unchanged and just update the CEC physical address. Otherwise
- * unregister the old CEC adapter and create a new one.
- */
-void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const struct edid *edid)
-{
-       struct drm_connector *connector = aux->cec.connector;
-       u32 cec_caps = CEC_CAP_DEFAULTS | CEC_CAP_NEEDS_HPD |
-                      CEC_CAP_CONNECTOR_INFO;
-       struct cec_connector_info conn_info;
-       unsigned int num_las = 1;
-       u8 cap;
-
-       /* No transfer function was set, so not a DP connector */
-       if (!aux->transfer)
-               return;
-
-#ifndef CONFIG_MEDIA_CEC_RC
-       /*
-        * CEC_CAP_RC is part of CEC_CAP_DEFAULTS, but it is stripped by
-        * cec_allocate_adapter() if CONFIG_MEDIA_CEC_RC is undefined.
-        *
-        * Do this here as well to ensure the tests against cec_caps are
-        * correct.
-        */
-       cec_caps &= ~CEC_CAP_RC;
-#endif
-       cancel_delayed_work_sync(&aux->cec.unregister_work);
-
-       mutex_lock(&aux->cec.lock);
-       if (!drm_dp_cec_cap(aux, &cap)) {
-               /* CEC is not supported, unregister any existing adapter */
-               cec_unregister_adapter(aux->cec.adap);
-               aux->cec.adap = NULL;
-               goto unlock;
-       }
-
-       if (cap & DP_CEC_SNOOPING_CAPABLE)
-               cec_caps |= CEC_CAP_MONITOR_ALL;
-       if (cap & DP_CEC_MULTIPLE_LA_CAPABLE)
-               num_las = CEC_MAX_LOG_ADDRS;
-
-       if (aux->cec.adap) {
-               if (aux->cec.adap->capabilities == cec_caps &&
-                   aux->cec.adap->available_log_addrs == num_las) {
-                       /* Unchanged, so just set the phys addr */
-                       cec_s_phys_addr_from_edid(aux->cec.adap, edid);
-                       goto unlock;
-               }
-               /*
-                * The capabilities changed, so unregister the old
-                * adapter first.
-                */
-               cec_unregister_adapter(aux->cec.adap);
-       }
-
-       /* Create a new adapter */
-       aux->cec.adap = cec_allocate_adapter(&drm_dp_cec_adap_ops,
-                                            aux, connector->name, cec_caps,
-                                            num_las);
-       if (IS_ERR(aux->cec.adap)) {
-               aux->cec.adap = NULL;
-               goto unlock;
-       }
-
-       cec_fill_conn_info_from_drm(&conn_info, connector);
-       cec_s_conn_info(aux->cec.adap, &conn_info);
-
-       if (cec_register_adapter(aux->cec.adap, connector->dev->dev)) {
-               cec_delete_adapter(aux->cec.adap);
-               aux->cec.adap = NULL;
-       } else {
-               /*
-                * Update the phys addr for the new CEC adapter. When called
-                * from drm_dp_cec_register_connector() edid == NULL, so in
-                * that case the phys addr is just invalidated.
-                */
-               cec_s_phys_addr_from_edid(aux->cec.adap, edid);
-       }
-unlock:
-       mutex_unlock(&aux->cec.lock);
-}
-EXPORT_SYMBOL(drm_dp_cec_set_edid);
-
-/*
- * The EDID disappeared (likely because of the HPD going down).
- */
-void drm_dp_cec_unset_edid(struct drm_dp_aux *aux)
-{
-       /* No transfer function was set, so not a DP connector */
-       if (!aux->transfer)
-               return;
-
-       cancel_delayed_work_sync(&aux->cec.unregister_work);
-
-       mutex_lock(&aux->cec.lock);
-       if (!aux->cec.adap)
-               goto unlock;
-
-       cec_phys_addr_invalidate(aux->cec.adap);
-       /*
-        * We're done if we want to keep the CEC device
-        * (drm_dp_cec_unregister_delay is >= NEVER_UNREG_DELAY) or if the
-        * DPCD still indicates the CEC capability (expected for an integrated
-        * HDMI branch device).
-        */
-       if (drm_dp_cec_unregister_delay < NEVER_UNREG_DELAY &&
-           !drm_dp_cec_cap(aux, NULL)) {
-               /*
-                * Unregister the CEC adapter after drm_dp_cec_unregister_delay
-                * seconds. This to debounce short HPD off-and-on cycles from
-                * displays.
-                */
-               schedule_delayed_work(&aux->cec.unregister_work,
-                                     drm_dp_cec_unregister_delay * HZ);
-       }
-unlock:
-       mutex_unlock(&aux->cec.lock);
-}
-EXPORT_SYMBOL(drm_dp_cec_unset_edid);
-
-/**
- * drm_dp_cec_register_connector() - register a new connector
- * @aux: DisplayPort AUX channel
- * @connector: drm connector
- *
- * A new connector was registered with associated CEC adapter name and
- * CEC adapter parent device. After registering the name and parent
- * drm_dp_cec_set_edid() is called to check if the connector supports
- * CEC and to register a CEC adapter if that is the case.
- */
-void drm_dp_cec_register_connector(struct drm_dp_aux *aux,
-                                  struct drm_connector *connector)
-{
-       WARN_ON(aux->cec.adap);
-       if (WARN_ON(!aux->transfer))
-               return;
-       aux->cec.connector = connector;
-       INIT_DELAYED_WORK(&aux->cec.unregister_work,
-                         drm_dp_cec_unregister_work);
-}
-EXPORT_SYMBOL(drm_dp_cec_register_connector);
-
-/**
- * drm_dp_cec_unregister_connector() - unregister the CEC adapter, if any
- * @aux: DisplayPort AUX channel
- */
-void drm_dp_cec_unregister_connector(struct drm_dp_aux *aux)
-{
-       if (!aux->cec.adap)
-               return;
-       cancel_delayed_work_sync(&aux->cec.unregister_work);
-       cec_unregister_adapter(aux->cec.adap);
-       aux->cec.adap = NULL;
-}
-EXPORT_SYMBOL(drm_dp_cec_unregister_connector);
diff --git a/drivers/gpu/drm/drm_dp_dual_mode_helper.c b/drivers/gpu/drm/drm_dp_dual_mode_helper.c
deleted file mode 100644 (file)
index 9faf493..0000000
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
- * Copyright © 2016 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/export.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-
-#include <drm/drm_device.h>
-#include <drm/drm_dp_dual_mode_helper.h>
-#include <drm/drm_print.h>
-
-/**
- * DOC: dp dual mode helpers
- *
- * Helper functions to deal with DP dual mode (aka. DP++) adaptors.
- *
- * Type 1:
- * Adaptor registers (if any) and the sink DDC bus may be accessed via I2C.
- *
- * Type 2:
- * Adaptor registers and sink DDC bus can be accessed either via I2C or
- * I2C-over-AUX. Source devices may choose to implement either of these
- * access methods.
- */
-
-#define DP_DUAL_MODE_SLAVE_ADDRESS 0x40
-
-/**
- * drm_dp_dual_mode_read - Read from the DP dual mode adaptor register(s)
- * @adapter: I2C adapter for the DDC bus
- * @offset: register offset
- * @buffer: buffer for return data
- * @size: sizo of the buffer
- *
- * Reads @size bytes from the DP dual mode adaptor registers
- * starting at @offset.
- *
- * Returns:
- * 0 on success, negative error code on failure
- */
-ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter,
-                             u8 offset, void *buffer, size_t size)
-{
-       struct i2c_msg msgs[] = {
-               {
-                       .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
-                       .flags = 0,
-                       .len = 1,
-                       .buf = &offset,
-               },
-               {
-                       .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
-                       .flags = I2C_M_RD,
-                       .len = size,
-                       .buf = buffer,
-               },
-       };
-       int ret;
-
-       ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
-       if (ret < 0)
-               return ret;
-       if (ret != ARRAY_SIZE(msgs))
-               return -EPROTO;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_dual_mode_read);
-
-/**
- * drm_dp_dual_mode_write - Write to the DP dual mode adaptor register(s)
- * @adapter: I2C adapter for the DDC bus
- * @offset: register offset
- * @buffer: buffer for write data
- * @size: sizo of the buffer
- *
- * Writes @size bytes to the DP dual mode adaptor registers
- * starting at @offset.
- *
- * Returns:
- * 0 on success, negative error code on failure
- */
-ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter,
-                              u8 offset, const void *buffer, size_t size)
-{
-       struct i2c_msg msg = {
-               .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
-               .flags = 0,
-               .len = 1 + size,
-               .buf = NULL,
-       };
-       void *data;
-       int ret;
-
-       data = kmalloc(msg.len, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       msg.buf = data;
-
-       memcpy(data, &offset, 1);
-       memcpy(data + 1, buffer, size);
-
-       ret = i2c_transfer(adapter, &msg, 1);
-
-       kfree(data);
-
-       if (ret < 0)
-               return ret;
-       if (ret != 1)
-               return -EPROTO;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_dual_mode_write);
-
-static bool is_hdmi_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN])
-{
-       static const char dp_dual_mode_hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] =
-               "DP-HDMI ADAPTOR\x04";
-
-       return memcmp(hdmi_id, dp_dual_mode_hdmi_id,
-                     sizeof(dp_dual_mode_hdmi_id)) == 0;
-}
-
-static bool is_type1_adaptor(uint8_t adaptor_id)
-{
-       return adaptor_id == 0 || adaptor_id == 0xff;
-}
-
-static bool is_type2_adaptor(uint8_t adaptor_id)
-{
-       return adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
-                             DP_DUAL_MODE_REV_TYPE2);
-}
-
-static bool is_lspcon_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN],
-                             const uint8_t adaptor_id)
-{
-       return is_hdmi_adaptor(hdmi_id) &&
-               (adaptor_id == (DP_DUAL_MODE_TYPE_TYPE2 |
-                DP_DUAL_MODE_TYPE_HAS_DPCD));
-}
-
-/**
- * drm_dp_dual_mode_detect - Identify the DP dual mode adaptor
- * @dev: &drm_device to use
- * @adapter: I2C adapter for the DDC bus
- *
- * Attempt to identify the type of the DP dual mode adaptor used.
- *
- * Note that when the answer is @DRM_DP_DUAL_MODE_UNKNOWN it's not
- * certain whether we're dealing with a native HDMI port or
- * a type 1 DVI dual mode adaptor. The driver will have to use
- * some other hardware/driver specific mechanism to make that
- * distinction.
- *
- * Returns:
- * The type of the DP dual mode adaptor used
- */
-enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
-                                                  struct i2c_adapter *adapter)
-{
-       char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN] = {};
-       uint8_t adaptor_id = 0x00;
-       ssize_t ret;
-
-       /*
-        * Let's see if the adaptor is there the by reading the
-        * HDMI ID registers.
-        *
-        * Note that type 1 DVI adaptors are not required to implemnt
-        * any registers, and that presents a problem for detection.
-        * If the i2c transfer is nacked, we may or may not be dealing
-        * with a type 1 DVI adaptor. Some other mechanism of detecting
-        * the presence of the adaptor is required. One way would be
-        * to check the state of the CONFIG1 pin, Another method would
-        * simply require the driver to know whether the port is a DP++
-        * port or a native HDMI port. Both of these methods are entirely
-        * hardware/driver specific so we can't deal with them here.
-        */
-       ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_HDMI_ID,
-                                   hdmi_id, sizeof(hdmi_id));
-       drm_dbg_kms(dev, "DP dual mode HDMI ID: %*pE (err %zd)\n",
-                   ret ? 0 : (int)sizeof(hdmi_id), hdmi_id, ret);
-       if (ret)
-               return DRM_DP_DUAL_MODE_UNKNOWN;
-
-       /*
-        * Sigh. Some (maybe all?) type 1 adaptors are broken and ack
-        * the offset but ignore it, and instead they just always return
-        * data from the start of the HDMI ID buffer. So for a broken
-        * type 1 HDMI adaptor a single byte read will always give us
-        * 0x44, and for a type 1 DVI adaptor it should give 0x00
-        * (assuming it implements any registers). Fortunately neither
-        * of those values will match the type 2 signature of the
-        * DP_DUAL_MODE_ADAPTOR_ID register so we can proceed with
-        * the type 2 adaptor detection safely even in the presence
-        * of broken type 1 adaptors.
-        */
-       ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_ADAPTOR_ID,
-                                   &adaptor_id, sizeof(adaptor_id));
-       drm_dbg_kms(dev, "DP dual mode adaptor ID: %02x (err %zd)\n", adaptor_id, ret);
-       if (ret == 0) {
-               if (is_lspcon_adaptor(hdmi_id, adaptor_id))
-                       return DRM_DP_DUAL_MODE_LSPCON;
-               if (is_type2_adaptor(adaptor_id)) {
-                       if (is_hdmi_adaptor(hdmi_id))
-                               return DRM_DP_DUAL_MODE_TYPE2_HDMI;
-                       else
-                               return DRM_DP_DUAL_MODE_TYPE2_DVI;
-               }
-               /*
-                * If neither a proper type 1 ID nor a broken type 1 adaptor
-                * as described above, assume type 1, but let the user know
-                * that we may have misdetected the type.
-                */
-               if (!is_type1_adaptor(adaptor_id) && adaptor_id != hdmi_id[0])
-                       drm_err(dev, "Unexpected DP dual mode adaptor ID %02x\n", adaptor_id);
-
-       }
-
-       if (is_hdmi_adaptor(hdmi_id))
-               return DRM_DP_DUAL_MODE_TYPE1_HDMI;
-       else
-               return DRM_DP_DUAL_MODE_TYPE1_DVI;
-}
-EXPORT_SYMBOL(drm_dp_dual_mode_detect);
-
-/**
- * drm_dp_dual_mode_max_tmds_clock - Max TMDS clock for DP dual mode adaptor
- * @dev: &drm_device to use
- * @type: DP dual mode adaptor type
- * @adapter: I2C adapter for the DDC bus
- *
- * Determine the max TMDS clock the adaptor supports based on the
- * type of the dual mode adaptor and the DP_DUAL_MODE_MAX_TMDS_CLOCK
- * register (on type2 adaptors). As some type 1 adaptors have
- * problems with registers (see comments in drm_dp_dual_mode_detect())
- * we don't read the register on those, instead we simply assume
- * a 165 MHz limit based on the specification.
- *
- * Returns:
- * Maximum supported TMDS clock rate for the DP dual mode adaptor in kHz.
- */
-int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
-                                   struct i2c_adapter *adapter)
-{
-       uint8_t max_tmds_clock;
-       ssize_t ret;
-
-       /* native HDMI so no limit */
-       if (type == DRM_DP_DUAL_MODE_NONE)
-               return 0;
-
-       /*
-        * Type 1 adaptors are limited to 165MHz
-        * Type 2 adaptors can tells us their limit
-        */
-       if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
-               return 165000;
-
-       ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_MAX_TMDS_CLOCK,
-                                   &max_tmds_clock, sizeof(max_tmds_clock));
-       if (ret || max_tmds_clock == 0x00 || max_tmds_clock == 0xff) {
-               drm_dbg_kms(dev, "Failed to query max TMDS clock\n");
-               return 165000;
-       }
-
-       return max_tmds_clock * 5000 / 2;
-}
-EXPORT_SYMBOL(drm_dp_dual_mode_max_tmds_clock);
-
-/**
- * drm_dp_dual_mode_get_tmds_output - Get the state of the TMDS output buffers in the DP dual mode adaptor
- * @dev: &drm_device to use
- * @type: DP dual mode adaptor type
- * @adapter: I2C adapter for the DDC bus
- * @enabled: current state of the TMDS output buffers
- *
- * Get the state of the TMDS output buffers in the adaptor. For
- * type2 adaptors this is queried from the DP_DUAL_MODE_TMDS_OEN
- * register. As some type 1 adaptors have problems with registers
- * (see comments in drm_dp_dual_mode_detect()) we don't read the
- * register on those, instead we simply assume that the buffers
- * are always enabled.
- *
- * Returns:
- * 0 on success, negative error code on failure
- */
-int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev,
-                                    enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter,
-                                    bool *enabled)
-{
-       uint8_t tmds_oen;
-       ssize_t ret;
-
-       if (type < DRM_DP_DUAL_MODE_TYPE2_DVI) {
-               *enabled = true;
-               return 0;
-       }
-
-       ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
-                                   &tmds_oen, sizeof(tmds_oen));
-       if (ret) {
-               drm_dbg_kms(dev, "Failed to query state of TMDS output buffers\n");
-               return ret;
-       }
-
-       *enabled = !(tmds_oen & DP_DUAL_MODE_TMDS_DISABLE);
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_dual_mode_get_tmds_output);
-
-/**
- * drm_dp_dual_mode_set_tmds_output - Enable/disable TMDS output buffers in the DP dual mode adaptor
- * @dev: &drm_device to use
- * @type: DP dual mode adaptor type
- * @adapter: I2C adapter for the DDC bus
- * @enable: enable (as opposed to disable) the TMDS output buffers
- *
- * Set the state of the TMDS output buffers in the adaptor. For
- * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register. As
- * some type 1 adaptors have problems with registers (see comments
- * in drm_dp_dual_mode_detect()) we avoid touching the register,
- * making this function a no-op on type 1 adaptors.
- *
- * Returns:
- * 0 on success, negative error code on failure
- */
-int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
-                                    struct i2c_adapter *adapter, bool enable)
-{
-       uint8_t tmds_oen = enable ? 0 : DP_DUAL_MODE_TMDS_DISABLE;
-       ssize_t ret;
-       int retry;
-
-       if (type < DRM_DP_DUAL_MODE_TYPE2_DVI)
-               return 0;
-
-       /*
-        * LSPCON adapters in low-power state may ignore the first write, so
-        * read back and verify the written value a few times.
-        */
-       for (retry = 0; retry < 3; retry++) {
-               uint8_t tmp;
-
-               ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_TMDS_OEN,
-                                            &tmds_oen, sizeof(tmds_oen));
-               if (ret) {
-                       drm_dbg_kms(dev, "Failed to %s TMDS output buffers (%d attempts)\n",
-                                   enable ? "enable" : "disable", retry + 1);
-                       return ret;
-               }
-
-               ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_TMDS_OEN,
-                                           &tmp, sizeof(tmp));
-               if (ret) {
-                       drm_dbg_kms(dev,
-                                   "I2C read failed during TMDS output buffer %s (%d attempts)\n",
-                                   enable ? "enabling" : "disabling", retry + 1);
-                       return ret;
-               }
-
-               if (tmp == tmds_oen)
-                       return 0;
-       }
-
-       drm_dbg_kms(dev, "I2C write value mismatch during TMDS output buffer %s\n",
-                   enable ? "enabling" : "disabling");
-
-       return -EIO;
-}
-EXPORT_SYMBOL(drm_dp_dual_mode_set_tmds_output);
-
-/**
- * drm_dp_get_dual_mode_type_name - Get the name of the DP dual mode adaptor type as a string
- * @type: DP dual mode adaptor type
- *
- * Returns:
- * String representation of the DP dual mode adaptor type
- */
-const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type)
-{
-       switch (type) {
-       case DRM_DP_DUAL_MODE_NONE:
-               return "none";
-       case DRM_DP_DUAL_MODE_TYPE1_DVI:
-               return "type 1 DVI";
-       case DRM_DP_DUAL_MODE_TYPE1_HDMI:
-               return "type 1 HDMI";
-       case DRM_DP_DUAL_MODE_TYPE2_DVI:
-               return "type 2 DVI";
-       case DRM_DP_DUAL_MODE_TYPE2_HDMI:
-               return "type 2 HDMI";
-       case DRM_DP_DUAL_MODE_LSPCON:
-               return "lspcon";
-       default:
-               WARN_ON(type != DRM_DP_DUAL_MODE_UNKNOWN);
-               return "unknown";
-       }
-}
-EXPORT_SYMBOL(drm_dp_get_dual_mode_type_name);
-
-/**
- * drm_lspcon_get_mode: Get LSPCON's current mode of operation by
- * reading offset (0x80, 0x41)
- * @dev: &drm_device to use
- * @adapter: I2C-over-aux adapter
- * @mode: current lspcon mode of operation output variable
- *
- * Returns:
- * 0 on success, sets the current_mode value to appropriate mode
- * -error on failure
- */
-int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
-                       enum drm_lspcon_mode *mode)
-{
-       u8 data;
-       int ret = 0;
-       int retry;
-
-       if (!mode) {
-               drm_err(dev, "NULL input\n");
-               return -EINVAL;
-       }
-
-       /* Read Status: i2c over aux */
-       for (retry = 0; retry < 6; retry++) {
-               if (retry)
-                       usleep_range(500, 1000);
-
-               ret = drm_dp_dual_mode_read(adapter,
-                                           DP_DUAL_MODE_LSPCON_CURRENT_MODE,
-                                           &data, sizeof(data));
-               if (!ret)
-                       break;
-       }
-
-       if (ret < 0) {
-               drm_dbg_kms(dev, "LSPCON read(0x80, 0x41) failed\n");
-               return -EFAULT;
-       }
-
-       if (data & DP_DUAL_MODE_LSPCON_MODE_PCON)
-               *mode = DRM_LSPCON_MODE_PCON;
-       else
-               *mode = DRM_LSPCON_MODE_LS;
-       return 0;
-}
-EXPORT_SYMBOL(drm_lspcon_get_mode);
-
-/**
- * drm_lspcon_set_mode: Change LSPCON's mode of operation by
- * writing offset (0x80, 0x40)
- * @dev: &drm_device to use
- * @adapter: I2C-over-aux adapter
- * @mode: required mode of operation
- *
- * Returns:
- * 0 on success, -error on failure/timeout
- */
-int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
-                       enum drm_lspcon_mode mode)
-{
-       u8 data = 0;
-       int ret;
-       int time_out = 200;
-       enum drm_lspcon_mode current_mode;
-
-       if (mode == DRM_LSPCON_MODE_PCON)
-               data = DP_DUAL_MODE_LSPCON_MODE_PCON;
-
-       /* Change mode */
-       ret = drm_dp_dual_mode_write(adapter, DP_DUAL_MODE_LSPCON_MODE_CHANGE,
-                                    &data, sizeof(data));
-       if (ret < 0) {
-               drm_err(dev, "LSPCON mode change failed\n");
-               return ret;
-       }
-
-       /*
-        * Confirm mode change by reading the status bit.
-        * Sometimes, it takes a while to change the mode,
-        * so wait and retry until time out or done.
-        */
-       do {
-               ret = drm_lspcon_get_mode(dev, adapter, &current_mode);
-               if (ret) {
-                       drm_err(dev, "can't confirm LSPCON mode change\n");
-                       return ret;
-               } else {
-                       if (current_mode != mode) {
-                               msleep(10);
-                               time_out -= 10;
-                       } else {
-                               drm_dbg_kms(dev, "LSPCON mode changed to %s\n",
-                                           mode == DRM_LSPCON_MODE_LS ? "LS" : "PCON");
-                               return 0;
-                       }
-               }
-       } while (time_out);
-
-       drm_err(dev, "LSPCON mode change timed out\n");
-       return -ETIMEDOUT;
-}
-EXPORT_SYMBOL(drm_lspcon_set_mode);
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
deleted file mode 100644 (file)
index 23f9073..0000000
+++ /dev/null
@@ -1,3744 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that copyright
- * notice and this permission notice appear in supporting documentation, and
- * that the name of the copyright holders not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission.  The copyright holders make no representations
- * about the suitability of this software for any purpose.  It is provided "as
- * is" without express or implied warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
- */
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/seq_file.h>
-
-#include <drm/drm_dp_helper.h>
-#include <drm/drm_print.h>
-#include <drm/drm_vblank.h>
-#include <drm/drm_dp_mst_helper.h>
-#include <drm/drm_panel.h>
-
-#include "drm_crtc_helper_internal.h"
-
-struct dp_aux_backlight {
-       struct backlight_device *base;
-       struct drm_dp_aux *aux;
-       struct drm_edp_backlight_info info;
-       bool enabled;
-};
-
-/**
- * DOC: dp helpers
- *
- * These functions contain some common logic and helpers at various abstraction
- * levels to deal with Display Port sink devices and related things like DP aux
- * channel transfers, EDID reading over DP aux channels, decoding certain DPCD
- * blocks, ...
- */
-
-/* Helpers for DP link training */
-static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
-{
-       return link_status[r - DP_LANE0_1_STATUS];
-}
-
-static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
-                            int lane)
-{
-       int i = DP_LANE0_1_STATUS + (lane >> 1);
-       int s = (lane & 1) * 4;
-       u8 l = dp_link_status(link_status, i);
-
-       return (l >> s) & 0xf;
-}
-
-bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
-                         int lane_count)
-{
-       u8 lane_align;
-       u8 lane_status;
-       int lane;
-
-       lane_align = dp_link_status(link_status,
-                                   DP_LANE_ALIGN_STATUS_UPDATED);
-       if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
-               return false;
-       for (lane = 0; lane < lane_count; lane++) {
-               lane_status = dp_get_lane_status(link_status, lane);
-               if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
-                       return false;
-       }
-       return true;
-}
-EXPORT_SYMBOL(drm_dp_channel_eq_ok);
-
-bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
-                             int lane_count)
-{
-       int lane;
-       u8 lane_status;
-
-       for (lane = 0; lane < lane_count; lane++) {
-               lane_status = dp_get_lane_status(link_status, lane);
-               if ((lane_status & DP_LANE_CR_DONE) == 0)
-                       return false;
-       }
-       return true;
-}
-EXPORT_SYMBOL(drm_dp_clock_recovery_ok);
-
-u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
-                                    int lane)
-{
-       int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
-       int s = ((lane & 1) ?
-                DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
-                DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
-       u8 l = dp_link_status(link_status, i);
-
-       return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
-}
-EXPORT_SYMBOL(drm_dp_get_adjust_request_voltage);
-
-u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
-                                         int lane)
-{
-       int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
-       int s = ((lane & 1) ?
-                DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
-                DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
-       u8 l = dp_link_status(link_status, i);
-
-       return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
-}
-EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis);
-
-/* DP 2.0 128b/132b */
-u8 drm_dp_get_adjust_tx_ffe_preset(const u8 link_status[DP_LINK_STATUS_SIZE],
-                                  int lane)
-{
-       int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
-       int s = ((lane & 1) ?
-                DP_ADJUST_TX_FFE_PRESET_LANE1_SHIFT :
-                DP_ADJUST_TX_FFE_PRESET_LANE0_SHIFT);
-       u8 l = dp_link_status(link_status, i);
-
-       return (l >> s) & 0xf;
-}
-EXPORT_SYMBOL(drm_dp_get_adjust_tx_ffe_preset);
-
-u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZE],
-                                        unsigned int lane)
-{
-       unsigned int offset = DP_ADJUST_REQUEST_POST_CURSOR2;
-       u8 value = dp_link_status(link_status, offset);
-
-       return (value >> (lane << 1)) & 0x3;
-}
-EXPORT_SYMBOL(drm_dp_get_adjust_request_post_cursor);
-
-static int __8b10b_clock_recovery_delay_us(const struct drm_dp_aux *aux, u8 rd_interval)
-{
-       if (rd_interval > 4)
-               drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n",
-                           aux->name, rd_interval);
-
-       if (rd_interval == 0)
-               return 100;
-
-       return rd_interval * 4 * USEC_PER_MSEC;
-}
-
-static int __8b10b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval)
-{
-       if (rd_interval > 4)
-               drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n",
-                           aux->name, rd_interval);
-
-       if (rd_interval == 0)
-               return 400;
-
-       return rd_interval * 4 * USEC_PER_MSEC;
-}
-
-static int __128b132b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval)
-{
-       switch (rd_interval) {
-       default:
-               drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x\n",
-                           aux->name, rd_interval);
-               fallthrough;
-       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US:
-               return 400;
-       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS:
-               return 4000;
-       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS:
-               return 8000;
-       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS:
-               return 12000;
-       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS:
-               return 16000;
-       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS:
-               return 32000;
-       case DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS:
-               return 64000;
-       }
-}
-
-/*
- * The link training delays are different for:
- *
- *  - Clock recovery vs. channel equalization
- *  - DPRX vs. LTTPR
- *  - 128b/132b vs. 8b/10b
- *  - DPCD rev 1.3 vs. later
- *
- * Get the correct delay in us, reading DPCD if necessary.
- */
-static int __read_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                       enum drm_dp_phy dp_phy, bool uhbr, bool cr)
-{
-       int (*parse)(const struct drm_dp_aux *aux, u8 rd_interval);
-       unsigned int offset;
-       u8 rd_interval, mask;
-
-       if (dp_phy == DP_PHY_DPRX) {
-               if (uhbr) {
-                       if (cr)
-                               return 100;
-
-                       offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL;
-                       mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK;
-                       parse = __128b132b_channel_eq_delay_us;
-               } else {
-                       if (cr && dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
-                               return 100;
-
-                       offset = DP_TRAINING_AUX_RD_INTERVAL;
-                       mask = DP_TRAINING_AUX_RD_MASK;
-                       if (cr)
-                               parse = __8b10b_clock_recovery_delay_us;
-                       else
-                               parse = __8b10b_channel_eq_delay_us;
-               }
-       } else {
-               if (uhbr) {
-                       offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy);
-                       mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK;
-                       parse = __128b132b_channel_eq_delay_us;
-               } else {
-                       if (cr)
-                               return 100;
-
-                       offset = DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy);
-                       mask = DP_TRAINING_AUX_RD_MASK;
-                       parse = __8b10b_channel_eq_delay_us;
-               }
-       }
-
-       if (offset < DP_RECEIVER_CAP_SIZE) {
-               rd_interval = dpcd[offset];
-       } else {
-               if (drm_dp_dpcd_readb(aux, offset, &rd_interval) != 1) {
-                       drm_dbg_kms(aux->drm_dev, "%s: failed rd interval read\n",
-                                   aux->name);
-                       /* arbitrary default delay */
-                       return 400;
-               }
-       }
-
-       return parse(aux, rd_interval & mask);
-}
-
-int drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                                    enum drm_dp_phy dp_phy, bool uhbr)
-{
-       return __read_delay(aux, dpcd, dp_phy, uhbr, true);
-}
-EXPORT_SYMBOL(drm_dp_read_clock_recovery_delay);
-
-int drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                                enum drm_dp_phy dp_phy, bool uhbr)
-{
-       return __read_delay(aux, dpcd, dp_phy, uhbr, false);
-}
-EXPORT_SYMBOL(drm_dp_read_channel_eq_delay);
-
-void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux,
-                                           const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       u8 rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
-               DP_TRAINING_AUX_RD_MASK;
-       int delay_us;
-
-       if (dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
-               delay_us = 100;
-       else
-               delay_us = __8b10b_clock_recovery_delay_us(aux, rd_interval);
-
-       usleep_range(delay_us, delay_us * 2);
-}
-EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);
-
-static void __drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
-                                                u8 rd_interval)
-{
-       int delay_us = __8b10b_channel_eq_delay_us(aux, rd_interval);
-
-       usleep_range(delay_us, delay_us * 2);
-}
-
-void drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
-                                       const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       __drm_dp_link_train_channel_eq_delay(aux,
-                                            dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
-                                            DP_TRAINING_AUX_RD_MASK);
-}
-EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);
-
-void drm_dp_lttpr_link_train_clock_recovery_delay(void)
-{
-       usleep_range(100, 200);
-}
-EXPORT_SYMBOL(drm_dp_lttpr_link_train_clock_recovery_delay);
-
-static u8 dp_lttpr_phy_cap(const u8 phy_cap[DP_LTTPR_PHY_CAP_SIZE], int r)
-{
-       return phy_cap[r - DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1];
-}
-
-void drm_dp_lttpr_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
-                                             const u8 phy_cap[DP_LTTPR_PHY_CAP_SIZE])
-{
-       u8 interval = dp_lttpr_phy_cap(phy_cap,
-                                      DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1) &
-                     DP_TRAINING_AUX_RD_MASK;
-
-       __drm_dp_link_train_channel_eq_delay(aux, interval);
-}
-EXPORT_SYMBOL(drm_dp_lttpr_link_train_channel_eq_delay);
-
-u8 drm_dp_link_rate_to_bw_code(int link_rate)
-{
-       switch (link_rate) {
-       case 1000000:
-               return DP_LINK_BW_10;
-       case 1350000:
-               return DP_LINK_BW_13_5;
-       case 2000000:
-               return DP_LINK_BW_20;
-       default:
-               /* Spec says link_bw = link_rate / 0.27Gbps */
-               return link_rate / 27000;
-       }
-}
-EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code);
-
-int drm_dp_bw_code_to_link_rate(u8 link_bw)
-{
-       switch (link_bw) {
-       case DP_LINK_BW_10:
-               return 1000000;
-       case DP_LINK_BW_13_5:
-               return 1350000;
-       case DP_LINK_BW_20:
-               return 2000000;
-       default:
-               /* Spec says link_rate = link_bw * 0.27Gbps */
-               return link_bw * 27000;
-       }
-}
-EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate);
-
-#define AUX_RETRY_INTERVAL 500 /* us */
-
-static inline void
-drm_dp_dump_access(const struct drm_dp_aux *aux,
-                  u8 request, uint offset, void *buffer, int ret)
-{
-       const char *arrow = request == DP_AUX_NATIVE_READ ? "->" : "<-";
-
-       if (ret > 0)
-               drm_dbg_dp(aux->drm_dev, "%s: 0x%05x AUX %s (ret=%3d) %*ph\n",
-                          aux->name, offset, arrow, ret, min(ret, 20), buffer);
-       else
-               drm_dbg_dp(aux->drm_dev, "%s: 0x%05x AUX %s (ret=%3d)\n",
-                          aux->name, offset, arrow, ret);
-}
-
-/**
- * DOC: dp helpers
- *
- * The DisplayPort AUX channel is an abstraction to allow generic, driver-
- * independent access to AUX functionality. Drivers can take advantage of
- * this by filling in the fields of the drm_dp_aux structure.
- *
- * Transactions are described using a hardware-independent drm_dp_aux_msg
- * structure, which is passed into a driver's .transfer() implementation.
- * Both native and I2C-over-AUX transactions are supported.
- */
-
-static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
-                             unsigned int offset, void *buffer, size_t size)
-{
-       struct drm_dp_aux_msg msg;
-       unsigned int retry, native_reply;
-       int err = 0, ret = 0;
-
-       memset(&msg, 0, sizeof(msg));
-       msg.address = offset;
-       msg.request = request;
-       msg.buffer = buffer;
-       msg.size = size;
-
-       mutex_lock(&aux->hw_mutex);
-
-       /*
-        * The specification doesn't give any recommendation on how often to
-        * retry native transactions. We used to retry 7 times like for
-        * aux i2c transactions but real world devices this wasn't
-        * sufficient, bump to 32 which makes Dell 4k monitors happier.
-        */
-       for (retry = 0; retry < 32; retry++) {
-               if (ret != 0 && ret != -ETIMEDOUT) {
-                       usleep_range(AUX_RETRY_INTERVAL,
-                                    AUX_RETRY_INTERVAL + 100);
-               }
-
-               ret = aux->transfer(aux, &msg);
-               if (ret >= 0) {
-                       native_reply = msg.reply & DP_AUX_NATIVE_REPLY_MASK;
-                       if (native_reply == DP_AUX_NATIVE_REPLY_ACK) {
-                               if (ret == size)
-                                       goto unlock;
-
-                               ret = -EPROTO;
-                       } else
-                               ret = -EIO;
-               }
-
-               /*
-                * We want the error we return to be the error we received on
-                * the first transaction, since we may get a different error the
-                * next time we retry
-                */
-               if (!err)
-                       err = ret;
-       }
-
-       drm_dbg_kms(aux->drm_dev, "%s: Too many retries, giving up. First error: %d\n",
-                   aux->name, err);
-       ret = err;
-
-unlock:
-       mutex_unlock(&aux->hw_mutex);
-       return ret;
-}
-
-/**
- * drm_dp_dpcd_read() - read a series of bytes from the DPCD
- * @aux: DisplayPort AUX channel (SST or MST)
- * @offset: address of the (first) register to read
- * @buffer: buffer to store the register values
- * @size: number of bytes in @buffer
- *
- * Returns the number of bytes transferred on success, or a negative error
- * code on failure. -EIO is returned if the request was NAKed by the sink or
- * if the retry count was exceeded. If not all bytes were transferred, this
- * function returns -EPROTO. Errors from the underlying AUX channel transfer
- * function, with the exception of -EBUSY (which causes the transaction to
- * be retried), are propagated to the caller.
- */
-ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
-                        void *buffer, size_t size)
-{
-       int ret;
-
-       /*
-        * HP ZR24w corrupts the first DPCD access after entering power save
-        * mode. Eg. on a read, the entire buffer will be filled with the same
-        * byte. Do a throw away read to avoid corrupting anything we care
-        * about. Afterwards things will work correctly until the monitor
-        * gets woken up and subsequently re-enters power save mode.
-        *
-        * The user pressing any button on the monitor is enough to wake it
-        * up, so there is no particularly good place to do the workaround.
-        * We just have to do it before any DPCD access and hope that the
-        * monitor doesn't power down exactly after the throw away read.
-        */
-       if (!aux->is_remote) {
-               ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, DP_DPCD_REV,
-                                        buffer, 1);
-               if (ret != 1)
-                       goto out;
-       }
-
-       if (aux->is_remote)
-               ret = drm_dp_mst_dpcd_read(aux, offset, buffer, size);
-       else
-               ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset,
-                                        buffer, size);
-
-out:
-       drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, buffer, ret);
-       return ret;
-}
-EXPORT_SYMBOL(drm_dp_dpcd_read);
-
-/**
- * drm_dp_dpcd_write() - write a series of bytes to the DPCD
- * @aux: DisplayPort AUX channel (SST or MST)
- * @offset: address of the (first) register to write
- * @buffer: buffer containing the values to write
- * @size: number of bytes in @buffer
- *
- * Returns the number of bytes transferred on success, or a negative error
- * code on failure. -EIO is returned if the request was NAKed by the sink or
- * if the retry count was exceeded. If not all bytes were transferred, this
- * function returns -EPROTO. Errors from the underlying AUX channel transfer
- * function, with the exception of -EBUSY (which causes the transaction to
- * be retried), are propagated to the caller.
- */
-ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
-                         void *buffer, size_t size)
-{
-       int ret;
-
-       if (aux->is_remote)
-               ret = drm_dp_mst_dpcd_write(aux, offset, buffer, size);
-       else
-               ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset,
-                                        buffer, size);
-
-       drm_dp_dump_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, ret);
-       return ret;
-}
-EXPORT_SYMBOL(drm_dp_dpcd_write);
-
-/**
- * drm_dp_dpcd_read_link_status() - read DPCD link status (bytes 0x202-0x207)
- * @aux: DisplayPort AUX channel
- * @status: buffer to store the link status in (must be at least 6 bytes)
- *
- * Returns the number of bytes transferred on success or a negative error
- * code on failure.
- */
-int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
-                                u8 status[DP_LINK_STATUS_SIZE])
-{
-       return drm_dp_dpcd_read(aux, DP_LANE0_1_STATUS, status,
-                               DP_LINK_STATUS_SIZE);
-}
-EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
-
-/**
- * drm_dp_dpcd_read_phy_link_status - get the link status information for a DP PHY
- * @aux: DisplayPort AUX channel
- * @dp_phy: the DP PHY to get the link status for
- * @link_status: buffer to return the status in
- *
- * Fetch the AUX DPCD registers for the DPRX or an LTTPR PHY link status. The
- * layout of the returned @link_status matches the DPCD register layout of the
- * DPRX PHY link status.
- *
- * Returns 0 if the information was read successfully or a negative error code
- * on failure.
- */
-int drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux,
-                                    enum drm_dp_phy dp_phy,
-                                    u8 link_status[DP_LINK_STATUS_SIZE])
-{
-       int ret;
-
-       if (dp_phy == DP_PHY_DPRX) {
-               ret = drm_dp_dpcd_read(aux,
-                                      DP_LANE0_1_STATUS,
-                                      link_status,
-                                      DP_LINK_STATUS_SIZE);
-
-               if (ret < 0)
-                       return ret;
-
-               WARN_ON(ret != DP_LINK_STATUS_SIZE);
-
-               return 0;
-       }
-
-       ret = drm_dp_dpcd_read(aux,
-                              DP_LANE0_1_STATUS_PHY_REPEATER(dp_phy),
-                              link_status,
-                              DP_LINK_STATUS_SIZE - 1);
-
-       if (ret < 0)
-               return ret;
-
-       WARN_ON(ret != DP_LINK_STATUS_SIZE - 1);
-
-       /* Convert the LTTPR to the sink PHY link status layout */
-       memmove(&link_status[DP_SINK_STATUS - DP_LANE0_1_STATUS + 1],
-               &link_status[DP_SINK_STATUS - DP_LANE0_1_STATUS],
-               DP_LINK_STATUS_SIZE - (DP_SINK_STATUS - DP_LANE0_1_STATUS) - 1);
-       link_status[DP_SINK_STATUS - DP_LANE0_1_STATUS] = 0;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_dpcd_read_phy_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
- * @type: port type to be checked. Can be:
- *       %DP_DS_PORT_TYPE_DP, %DP_DS_PORT_TYPE_VGA, %DP_DS_PORT_TYPE_DVI,
- *       %DP_DS_PORT_TYPE_HDMI, %DP_DS_PORT_TYPE_NON_EDID,
- *       %DP_DS_PORT_TYPE_DP_DUALMODE or %DP_DS_PORT_TYPE_WIRELESS.
- *
- * 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
- * @real_edid_checksum: real edid checksum for the last block
- *
- * Returns:
- * True on success
- */
-bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux,
-                                   u8 real_edid_checksum)
-{
-       u8 link_edid_read = 0, auto_test_req = 0, test_resp = 0;
-
-       if (drm_dp_dpcd_read(aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
-                            &auto_test_req, 1) < 1) {
-               drm_err(aux->drm_dev, "%s: DPCD failed read at register 0x%x\n",
-                       aux->name, DP_DEVICE_SERVICE_IRQ_VECTOR);
-               return false;
-       }
-       auto_test_req &= DP_AUTOMATED_TEST_REQUEST;
-
-       if (drm_dp_dpcd_read(aux, DP_TEST_REQUEST, &link_edid_read, 1) < 1) {
-               drm_err(aux->drm_dev, "%s: DPCD failed read at register 0x%x\n",
-                       aux->name, DP_TEST_REQUEST);
-               return false;
-       }
-       link_edid_read &= DP_TEST_LINK_EDID_READ;
-
-       if (!auto_test_req || !link_edid_read) {
-               drm_dbg_kms(aux->drm_dev, "%s: Source DUT does not support TEST_EDID_READ\n",
-                           aux->name);
-               return false;
-       }
-
-       if (drm_dp_dpcd_write(aux, DP_DEVICE_SERVICE_IRQ_VECTOR,
-                             &auto_test_req, 1) < 1) {
-               drm_err(aux->drm_dev, "%s: DPCD failed write at register 0x%x\n",
-                       aux->name, DP_DEVICE_SERVICE_IRQ_VECTOR);
-               return false;
-       }
-
-       /* send back checksum for the last edid extension block data */
-       if (drm_dp_dpcd_write(aux, DP_TEST_EDID_CHECKSUM,
-                             &real_edid_checksum, 1) < 1) {
-               drm_err(aux->drm_dev, "%s: DPCD failed write at register 0x%x\n",
-                       aux->name, DP_TEST_EDID_CHECKSUM);
-               return false;
-       }
-
-       test_resp |= DP_TEST_EDID_CHECKSUM_WRITE;
-       if (drm_dp_dpcd_write(aux, DP_TEST_RESPONSE, &test_resp, 1) < 1) {
-               drm_err(aux->drm_dev, "%s: DPCD failed write at register 0x%x\n",
-                       aux->name, DP_TEST_RESPONSE);
-               return false;
-       }
-
-       return true;
-}
-EXPORT_SYMBOL(drm_dp_send_real_edid_checksum);
-
-static u8 drm_dp_downstream_port_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       u8 port_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_PORT_COUNT_MASK;
-
-       if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE && port_count > 4)
-               port_count = 4;
-
-       return port_count;
-}
-
-static int drm_dp_read_extended_dpcd_caps(struct drm_dp_aux *aux,
-                                         u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       u8 dpcd_ext[DP_RECEIVER_CAP_SIZE];
-       int ret;
-
-       /*
-        * Prior to DP1.3 the bit represented by
-        * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved.
-        * If it is set DP_DPCD_REV at 0000h could be at a value less than
-        * the true capability of the panel. The only way to check is to
-        * then compare 0000h and 2200h.
-        */
-       if (!(dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
-             DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT))
-               return 0;
-
-       ret = drm_dp_dpcd_read(aux, DP_DP13_DPCD_REV, &dpcd_ext,
-                              sizeof(dpcd_ext));
-       if (ret < 0)
-               return ret;
-       if (ret != sizeof(dpcd_ext))
-               return -EIO;
-
-       if (dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) {
-               drm_dbg_kms(aux->drm_dev,
-                           "%s: Extended DPCD rev less than base DPCD rev (%d > %d)\n",
-                           aux->name, dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]);
-               return 0;
-       }
-
-       if (!memcmp(dpcd, dpcd_ext, sizeof(dpcd_ext)))
-               return 0;
-
-       drm_dbg_kms(aux->drm_dev, "%s: Base DPCD: %*ph\n", aux->name, DP_RECEIVER_CAP_SIZE, dpcd);
-
-       memcpy(dpcd, dpcd_ext, sizeof(dpcd_ext));
-
-       return 0;
-}
-
-/**
- * drm_dp_read_dpcd_caps() - read DPCD caps and extended DPCD caps if
- * available
- * @aux: DisplayPort AUX channel
- * @dpcd: Buffer to store the resulting DPCD in
- *
- * Attempts to read the base DPCD caps for @aux. Additionally, this function
- * checks for and reads the extended DPRX caps (%DP_DP13_DPCD_REV) if
- * present.
- *
- * Returns: %0 if the DPCD was read successfully, negative error code
- * otherwise.
- */
-int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux,
-                         u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       int ret;
-
-       ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE);
-       if (ret < 0)
-               return ret;
-       if (ret != DP_RECEIVER_CAP_SIZE || dpcd[DP_DPCD_REV] == 0)
-               return -EIO;
-
-       ret = drm_dp_read_extended_dpcd_caps(aux, dpcd);
-       if (ret < 0)
-               return ret;
-
-       drm_dbg_kms(aux->drm_dev, "%s: DPCD: %*ph\n", aux->name, DP_RECEIVER_CAP_SIZE, dpcd);
-
-       return ret;
-}
-EXPORT_SYMBOL(drm_dp_read_dpcd_caps);
-
-/**
- * drm_dp_read_downstream_info() - read DPCD downstream port info if available
- * @aux: DisplayPort AUX channel
- * @dpcd: A cached copy of the port's DPCD
- * @downstream_ports: buffer to store the downstream port info in
- *
- * See also:
- * drm_dp_downstream_max_clock()
- * drm_dp_downstream_max_bpc()
- *
- * Returns: 0 if either the downstream port info was read successfully or
- * there was no downstream info to read, or a negative error code otherwise.
- */
-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 ret;
-       u8 len;
-
-       memset(downstream_ports, 0, DP_MAX_DOWNSTREAM_PORTS);
-
-       /* No downstream info to read */
-       if (!drm_dp_is_branch(dpcd) || dpcd[DP_DPCD_REV] == DP_DPCD_REV_10)
-               return 0;
-
-       /* Some branches advertise having 0 downstream ports, despite also advertising they have a
-        * downstream port present. The DP spec isn't clear on if this is allowed or not, but since
-        * some branches do it we need to handle it regardless.
-        */
-       len = drm_dp_downstream_port_count(dpcd);
-       if (!len)
-               return 0;
-
-       if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE)
-               len *= 4;
-
-       ret = drm_dp_dpcd_read(aux, DP_DOWNSTREAM_PORT_0, downstream_ports, len);
-       if (ret < 0)
-               return ret;
-       if (ret != len)
-               return -EIO;
-
-       drm_dbg_kms(aux->drm_dev, "%s: DPCD DFP: %*ph\n", aux->name, len, downstream_ports);
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_read_downstream_info);
-
-/**
- * drm_dp_downstream_max_dotclock() - extract downstream facing port max dot clock
- * @dpcd: DisplayPort configuration data
- * @port_cap: port capabilities
- *
- * Returns: Downstream facing port max dot clock in kHz on success,
- * or 0 if max clock not defined
- */
-int drm_dp_downstream_max_dotclock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                                  const u8 port_cap[4])
-{
-       if (!drm_dp_is_branch(dpcd))
-               return 0;
-
-       if (dpcd[DP_DPCD_REV] < 0x11)
-               return 0;
-
-       switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) {
-       case DP_DS_PORT_TYPE_VGA:
-               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.
-                *
-                * Unfortunately 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_tmds_clock);
-
-/**
- * drm_dp_downstream_min_tmds_clock() - extract downstream facing port min TMDS clock
- * @dpcd: DisplayPort configuration data
- * @port_cap: port capabilities
- * @edid: EDID
- *
- * 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 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_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:
-               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 (port_cap[2] & DP_DS_MAX_BPC_MASK) {
-               case DP_DS_8BPC:
-                       return 8;
-               case DP_DS_10BPC:
-                       return 10;
-               case DP_DS_12BPC:
-                       return 12;
-               case DP_DS_16BPC:
-                       return 16;
-               default:
-                       return 8;
-               }
-               break;
-       default:
-               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_rgb_to_ycbcr_conversion() - determine downstream facing port
- *                                               RGB->YCbCr conversion capability
- * @dpcd: DisplayPort configuration data
- * @port_cap: downstream facing port capabilities
- * @color_spc: Colorspace for which conversion cap is sought
- *
- * Returns: whether the downstream facing port can convert RGB->YCbCr for a given
- * colorspace.
- */
-bool drm_dp_downstream_rgb_to_ycbcr_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                                              const u8 port_cap[4],
-                                              u8 color_spc)
-{
-       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] & color_spc;
-       default:
-               return false;
-       }
-}
-EXPORT_SYMBOL(drm_dp_downstream_rgb_to_ycbcr_conversion);
-
-/**
- * drm_dp_downstream_mode() - return a mode for downstream facing port
- * @dev: DRM device
- * @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
- *
- * Returns branch device id on success or NULL on failure
- */
-int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6])
-{
-       return drm_dp_dpcd_read(aux, DP_BRANCH_ID, id, 6);
-}
-EXPORT_SYMBOL(drm_dp_downstream_id);
-
-/**
- * drm_dp_downstream_debug() - debug DP branch devices
- * @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],
-                            const struct edid *edid,
-                            struct drm_dp_aux *aux)
-{
-       bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
-                                DP_DETAILED_CAP_INFO_AVAILABLE;
-       int clk;
-       int bpc;
-       char id[7];
-       int len;
-       uint8_t rev[2];
-       int type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
-       bool branch_device = drm_dp_is_branch(dpcd);
-
-       seq_printf(m, "\tDP branch device present: %s\n",
-                  branch_device ? "yes" : "no");
-
-       if (!branch_device)
-               return;
-
-       switch (type) {
-       case DP_DS_PORT_TYPE_DP:
-               seq_puts(m, "\t\tType: DisplayPort\n");
-               break;
-       case DP_DS_PORT_TYPE_VGA:
-               seq_puts(m, "\t\tType: VGA\n");
-               break;
-       case DP_DS_PORT_TYPE_DVI:
-               seq_puts(m, "\t\tType: DVI\n");
-               break;
-       case DP_DS_PORT_TYPE_HDMI:
-               seq_puts(m, "\t\tType: HDMI\n");
-               break;
-       case DP_DS_PORT_TYPE_NON_EDID:
-               seq_puts(m, "\t\tType: others without EDID support\n");
-               break;
-       case DP_DS_PORT_TYPE_DP_DUALMODE:
-               seq_puts(m, "\t\tType: DP++\n");
-               break;
-       case DP_DS_PORT_TYPE_WIRELESS:
-               seq_puts(m, "\t\tType: Wireless\n");
-               break;
-       default:
-               seq_puts(m, "\t\tType: N/A\n");
-       }
-
-       memset(id, 0, sizeof(id));
-       drm_dp_downstream_id(aux, id);
-       seq_printf(m, "\t\tID: %s\n", id);
-
-       len = drm_dp_dpcd_read(aux, DP_BRANCH_HW_REV, &rev[0], 1);
-       if (len > 0)
-               seq_printf(m, "\t\tHW: %d.%d\n",
-                          (rev[0] & 0xf0) >> 4, rev[0] & 0xf);
-
-       len = drm_dp_dpcd_read(aux, DP_BRANCH_SW_REV, rev, 2);
-       if (len > 0)
-               seq_printf(m, "\t\tSW: %d.%d\n", rev[0], rev[1]);
-
-       if (detailed_cap_info) {
-               clk = drm_dp_downstream_max_dotclock(dpcd, port_cap);
-               if (clk > 0)
-                       seq_printf(m, "\t\tMax dot 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, edid);
-
-               if (bpc > 0)
-                       seq_printf(m, "\t\tMax bpc: %d\n", bpc);
-       }
-}
-EXPORT_SYMBOL(drm_dp_downstream_debug);
-
-/**
- * drm_dp_subconnector_type() - get DP branch device type
- * @dpcd: DisplayPort configuration data
- * @port_cap: port capabilities
- */
-enum drm_mode_subconnector
-drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                        const u8 port_cap[4])
-{
-       int type;
-       if (!drm_dp_is_branch(dpcd))
-               return DRM_MODE_SUBCONNECTOR_Native;
-       /* DP 1.0 approach */
-       if (dpcd[DP_DPCD_REV] == DP_DPCD_REV_10) {
-               type = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
-                      DP_DWN_STRM_PORT_TYPE_MASK;
-
-               switch (type) {
-               case DP_DWN_STRM_PORT_TYPE_TMDS:
-                       /* Can be HDMI or DVI-D, DVI-D is a safer option */
-                       return DRM_MODE_SUBCONNECTOR_DVID;
-               case DP_DWN_STRM_PORT_TYPE_ANALOG:
-                       /* Can be VGA or DVI-A, VGA is more popular */
-                       return DRM_MODE_SUBCONNECTOR_VGA;
-               case DP_DWN_STRM_PORT_TYPE_DP:
-                       return DRM_MODE_SUBCONNECTOR_DisplayPort;
-               case DP_DWN_STRM_PORT_TYPE_OTHER:
-               default:
-                       return DRM_MODE_SUBCONNECTOR_Unknown;
-               }
-       }
-       type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
-
-       switch (type) {
-       case DP_DS_PORT_TYPE_DP:
-       case DP_DS_PORT_TYPE_DP_DUALMODE:
-               return DRM_MODE_SUBCONNECTOR_DisplayPort;
-       case DP_DS_PORT_TYPE_VGA:
-               return DRM_MODE_SUBCONNECTOR_VGA;
-       case DP_DS_PORT_TYPE_DVI:
-               return DRM_MODE_SUBCONNECTOR_DVID;
-       case DP_DS_PORT_TYPE_HDMI:
-               return DRM_MODE_SUBCONNECTOR_HDMIA;
-       case DP_DS_PORT_TYPE_WIRELESS:
-               return DRM_MODE_SUBCONNECTOR_Wireless;
-       case DP_DS_PORT_TYPE_NON_EDID:
-       default:
-               return DRM_MODE_SUBCONNECTOR_Unknown;
-       }
-}
-EXPORT_SYMBOL(drm_dp_subconnector_type);
-
-/**
- * drm_dp_set_subconnector_property - set subconnector for DP connector
- * @connector: connector to set property on
- * @status: connector status
- * @dpcd: DisplayPort configuration data
- * @port_cap: port capabilities
- *
- * Called by a driver on every detect event.
- */
-void drm_dp_set_subconnector_property(struct drm_connector *connector,
-                                     enum drm_connector_status status,
-                                     const u8 *dpcd,
-                                     const u8 port_cap[4])
-{
-       enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
-
-       if (status == connector_status_connected)
-               subconnector = drm_dp_subconnector_type(dpcd, port_cap);
-       drm_object_property_set_value(&connector->base,
-                       connector->dev->mode_config.dp_subconnector_property,
-                       subconnector);
-}
-EXPORT_SYMBOL(drm_dp_set_subconnector_property);
-
-/**
- * drm_dp_read_sink_count_cap() - Check whether a given connector has a valid sink
- * count
- * @connector: The DRM connector to check
- * @dpcd: A cached copy of the connector's DPCD RX capabilities
- * @desc: A cached copy of the connector's DP descriptor
- *
- * See also: drm_dp_read_sink_count()
- *
- * Returns: %True if the (e)DP connector has a valid sink count that should
- * be probed, %false otherwise.
- */
-bool drm_dp_read_sink_count_cap(struct drm_connector *connector,
-                               const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                               const struct drm_dp_desc *desc)
-{
-       /* Some eDP panels don't set a valid value for the sink count */
-       return connector->connector_type != DRM_MODE_CONNECTOR_eDP &&
-               dpcd[DP_DPCD_REV] >= DP_DPCD_REV_11 &&
-               dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT &&
-               !drm_dp_has_quirk(desc, DP_DPCD_QUIRK_NO_SINK_COUNT);
-}
-EXPORT_SYMBOL(drm_dp_read_sink_count_cap);
-
-/**
- * drm_dp_read_sink_count() - Retrieve the sink count for a given sink
- * @aux: The DP AUX channel to use
- *
- * See also: drm_dp_read_sink_count_cap()
- *
- * Returns: The current sink count reported by @aux, or a negative error code
- * otherwise.
- */
-int drm_dp_read_sink_count(struct drm_dp_aux *aux)
-{
-       u8 count;
-       int ret;
-
-       ret = drm_dp_dpcd_readb(aux, DP_SINK_COUNT, &count);
-       if (ret < 0)
-               return ret;
-       if (ret != 1)
-               return -EIO;
-
-       return DP_GET_SINK_COUNT(count);
-}
-EXPORT_SYMBOL(drm_dp_read_sink_count);
-
-/*
- * I2C-over-AUX implementation
- */
-
-static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter)
-{
-       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
-              I2C_FUNC_SMBUS_READ_BLOCK_DATA |
-              I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
-              I2C_FUNC_10BIT_ADDR;
-}
-
-static void drm_dp_i2c_msg_write_status_update(struct drm_dp_aux_msg *msg)
-{
-       /*
-        * In case of i2c defer or short i2c ack reply to a write,
-        * we need to switch to WRITE_STATUS_UPDATE to drain the
-        * rest of the message
-        */
-       if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE) {
-               msg->request &= DP_AUX_I2C_MOT;
-               msg->request |= DP_AUX_I2C_WRITE_STATUS_UPDATE;
-       }
-}
-
-#define AUX_PRECHARGE_LEN 10 /* 10 to 16 */
-#define AUX_SYNC_LEN (16 + 4) /* preamble + AUX_SYNC_END */
-#define AUX_STOP_LEN 4
-#define AUX_CMD_LEN 4
-#define AUX_ADDRESS_LEN 20
-#define AUX_REPLY_PAD_LEN 4
-#define AUX_LENGTH_LEN 8
-
-/*
- * Calculate the duration of the AUX request/reply in usec. Gives the
- * "best" case estimate, ie. successful while as short as possible.
- */
-static int drm_dp_aux_req_duration(const struct drm_dp_aux_msg *msg)
-{
-       int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN +
-               AUX_CMD_LEN + AUX_ADDRESS_LEN + AUX_LENGTH_LEN;
-
-       if ((msg->request & DP_AUX_I2C_READ) == 0)
-               len += msg->size * 8;
-
-       return len;
-}
-
-static int drm_dp_aux_reply_duration(const struct drm_dp_aux_msg *msg)
-{
-       int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN +
-               AUX_CMD_LEN + AUX_REPLY_PAD_LEN;
-
-       /*
-        * For read we expect what was asked. For writes there will
-        * be 0 or 1 data bytes. Assume 0 for the "best" case.
-        */
-       if (msg->request & DP_AUX_I2C_READ)
-               len += msg->size * 8;
-
-       return len;
-}
-
-#define I2C_START_LEN 1
-#define I2C_STOP_LEN 1
-#define I2C_ADDR_LEN 9 /* ADDRESS + R/W + ACK/NACK */
-#define I2C_DATA_LEN 9 /* DATA + ACK/NACK */
-
-/*
- * Calculate the length of the i2c transfer in usec, assuming
- * the i2c bus speed is as specified. Gives the the "worst"
- * case estimate, ie. successful while as long as possible.
- * Doesn't account the the "MOT" bit, and instead assumes each
- * message includes a START, ADDRESS and STOP. Neither does it
- * account for additional random variables such as clock stretching.
- */
-static int drm_dp_i2c_msg_duration(const struct drm_dp_aux_msg *msg,
-                                  int i2c_speed_khz)
-{
-       /* AUX bitrate is 1MHz, i2c bitrate as specified */
-       return DIV_ROUND_UP((I2C_START_LEN + I2C_ADDR_LEN +
-                            msg->size * I2C_DATA_LEN +
-                            I2C_STOP_LEN) * 1000, i2c_speed_khz);
-}
-
-/*
- * Determine how many retries should be attempted to successfully transfer
- * the specified message, based on the estimated durations of the
- * i2c and AUX transfers.
- */
-static int drm_dp_i2c_retry_count(const struct drm_dp_aux_msg *msg,
-                             int i2c_speed_khz)
-{
-       int aux_time_us = drm_dp_aux_req_duration(msg) +
-               drm_dp_aux_reply_duration(msg);
-       int i2c_time_us = drm_dp_i2c_msg_duration(msg, i2c_speed_khz);
-
-       return DIV_ROUND_UP(i2c_time_us, aux_time_us + AUX_RETRY_INTERVAL);
-}
-
-/*
- * FIXME currently assumes 10 kHz as some real world devices seem
- * to require it. We should query/set the speed via DPCD if supported.
- */
-static int dp_aux_i2c_speed_khz __read_mostly = 10;
-module_param_unsafe(dp_aux_i2c_speed_khz, int, 0644);
-MODULE_PARM_DESC(dp_aux_i2c_speed_khz,
-                "Assumed speed of the i2c bus in kHz, (1-400, default 10)");
-
-/*
- * Transfer a single I2C-over-AUX message and handle various error conditions,
- * retrying the transaction as appropriate.  It is assumed that the
- * &drm_dp_aux.transfer function does not modify anything in the msg other than the
- * reply field.
- *
- * Returns bytes transferred on success, or a negative error code on failure.
- */
-static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
-{
-       unsigned int retry, defer_i2c;
-       int ret;
-       /*
-        * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device
-        * is required to retry at least seven times upon receiving AUX_DEFER
-        * before giving up the AUX transaction.
-        *
-        * We also try to account for the i2c bus speed.
-        */
-       int max_retries = max(7, drm_dp_i2c_retry_count(msg, dp_aux_i2c_speed_khz));
-
-       for (retry = 0, defer_i2c = 0; retry < (max_retries + defer_i2c); retry++) {
-               ret = aux->transfer(aux, msg);
-               if (ret < 0) {
-                       if (ret == -EBUSY)
-                               continue;
-
-                       /*
-                        * While timeouts can be errors, they're usually normal
-                        * behavior (for instance, when a driver tries to
-                        * communicate with a non-existent DisplayPort device).
-                        * Avoid spamming the kernel log with timeout errors.
-                        */
-                       if (ret == -ETIMEDOUT)
-                               drm_dbg_kms_ratelimited(aux->drm_dev, "%s: transaction timed out\n",
-                                                       aux->name);
-                       else
-                               drm_dbg_kms(aux->drm_dev, "%s: transaction failed: %d\n",
-                                           aux->name, ret);
-                       return ret;
-               }
-
-
-               switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) {
-               case DP_AUX_NATIVE_REPLY_ACK:
-                       /*
-                        * For I2C-over-AUX transactions this isn't enough, we
-                        * need to check for the I2C ACK reply.
-                        */
-                       break;
-
-               case DP_AUX_NATIVE_REPLY_NACK:
-                       drm_dbg_kms(aux->drm_dev, "%s: native nack (result=%d, size=%zu)\n",
-                                   aux->name, ret, msg->size);
-                       return -EREMOTEIO;
-
-               case DP_AUX_NATIVE_REPLY_DEFER:
-                       drm_dbg_kms(aux->drm_dev, "%s: native defer\n", aux->name);
-                       /*
-                        * We could check for I2C bit rate capabilities and if
-                        * available adjust this interval. We could also be
-                        * more careful with DP-to-legacy adapters where a
-                        * long legacy cable may force very low I2C bit rates.
-                        *
-                        * For now just defer for long enough to hopefully be
-                        * safe for all use-cases.
-                        */
-                       usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
-                       continue;
-
-               default:
-                       drm_err(aux->drm_dev, "%s: invalid native reply %#04x\n",
-                               aux->name, msg->reply);
-                       return -EREMOTEIO;
-               }
-
-               switch (msg->reply & DP_AUX_I2C_REPLY_MASK) {
-               case DP_AUX_I2C_REPLY_ACK:
-                       /*
-                        * Both native ACK and I2C ACK replies received. We
-                        * can assume the transfer was successful.
-                        */
-                       if (ret != msg->size)
-                               drm_dp_i2c_msg_write_status_update(msg);
-                       return ret;
-
-               case DP_AUX_I2C_REPLY_NACK:
-                       drm_dbg_kms(aux->drm_dev, "%s: I2C nack (result=%d, size=%zu)\n",
-                                   aux->name, ret, msg->size);
-                       aux->i2c_nack_count++;
-                       return -EREMOTEIO;
-
-               case DP_AUX_I2C_REPLY_DEFER:
-                       drm_dbg_kms(aux->drm_dev, "%s: I2C defer\n", aux->name);
-                       /* DP Compliance Test 4.2.2.5 Requirement:
-                        * Must have at least 7 retries for I2C defers on the
-                        * transaction to pass this test
-                        */
-                       aux->i2c_defer_count++;
-                       if (defer_i2c < 7)
-                               defer_i2c++;
-                       usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
-                       drm_dp_i2c_msg_write_status_update(msg);
-
-                       continue;
-
-               default:
-                       drm_err(aux->drm_dev, "%s: invalid I2C reply %#04x\n",
-                               aux->name, msg->reply);
-                       return -EREMOTEIO;
-               }
-       }
-
-       drm_dbg_kms(aux->drm_dev, "%s: Too many retries, giving up\n", aux->name);
-       return -EREMOTEIO;
-}
-
-static void drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg *msg,
-                                      const struct i2c_msg *i2c_msg)
-{
-       msg->request = (i2c_msg->flags & I2C_M_RD) ?
-               DP_AUX_I2C_READ : DP_AUX_I2C_WRITE;
-       if (!(i2c_msg->flags & I2C_M_STOP))
-               msg->request |= DP_AUX_I2C_MOT;
-}
-
-/*
- * Keep retrying drm_dp_i2c_do_msg until all data has been transferred.
- *
- * Returns an error code on failure, or a recommended transfer size on success.
- */
-static int drm_dp_i2c_drain_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *orig_msg)
-{
-       int err, ret = orig_msg->size;
-       struct drm_dp_aux_msg msg = *orig_msg;
-
-       while (msg.size > 0) {
-               err = drm_dp_i2c_do_msg(aux, &msg);
-               if (err <= 0)
-                       return err == 0 ? -EPROTO : err;
-
-               if (err < msg.size && err < ret) {
-                       drm_dbg_kms(aux->drm_dev,
-                                   "%s: Partial I2C reply: requested %zu bytes got %d bytes\n",
-                                   aux->name, msg.size, err);
-                       ret = err;
-               }
-
-               msg.size -= err;
-               msg.buffer += err;
-       }
-
-       return ret;
-}
-
-/*
- * Bizlink designed DP->DVI-D Dual Link adapters require the I2C over AUX
- * packets to be as large as possible. If not, the I2C transactions never
- * succeed. Hence the default is maximum.
- */
-static int dp_aux_i2c_transfer_size __read_mostly = DP_AUX_MAX_PAYLOAD_BYTES;
-module_param_unsafe(dp_aux_i2c_transfer_size, int, 0644);
-MODULE_PARM_DESC(dp_aux_i2c_transfer_size,
-                "Number of bytes to transfer in a single I2C over DP AUX CH message, (1-16, default 16)");
-
-static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
-                          int num)
-{
-       struct drm_dp_aux *aux = adapter->algo_data;
-       unsigned int i, j;
-       unsigned transfer_size;
-       struct drm_dp_aux_msg msg;
-       int err = 0;
-
-       dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, DP_AUX_MAX_PAYLOAD_BYTES);
-
-       memset(&msg, 0, sizeof(msg));
-
-       for (i = 0; i < num; i++) {
-               msg.address = msgs[i].addr;
-               drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
-               /* Send a bare address packet to start the transaction.
-                * Zero sized messages specify an address only (bare
-                * address) transaction.
-                */
-               msg.buffer = NULL;
-               msg.size = 0;
-               err = drm_dp_i2c_do_msg(aux, &msg);
-
-               /*
-                * Reset msg.request in case in case it got
-                * changed into a WRITE_STATUS_UPDATE.
-                */
-               drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
-
-               if (err < 0)
-                       break;
-               /* We want each transaction to be as large as possible, but
-                * we'll go to smaller sizes if the hardware gives us a
-                * short reply.
-                */
-               transfer_size = dp_aux_i2c_transfer_size;
-               for (j = 0; j < msgs[i].len; j += msg.size) {
-                       msg.buffer = msgs[i].buf + j;
-                       msg.size = min(transfer_size, msgs[i].len - j);
-
-                       err = drm_dp_i2c_drain_msg(aux, &msg);
-
-                       /*
-                        * Reset msg.request in case in case it got
-                        * changed into a WRITE_STATUS_UPDATE.
-                        */
-                       drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
-
-                       if (err < 0)
-                               break;
-                       transfer_size = err;
-               }
-               if (err < 0)
-                       break;
-       }
-       if (err >= 0)
-               err = num;
-       /* Send a bare address packet to close out the transaction.
-        * Zero sized messages specify an address only (bare
-        * address) transaction.
-        */
-       msg.request &= ~DP_AUX_I2C_MOT;
-       msg.buffer = NULL;
-       msg.size = 0;
-       (void)drm_dp_i2c_do_msg(aux, &msg);
-
-       return err;
-}
-
-static const struct i2c_algorithm drm_dp_i2c_algo = {
-       .functionality = drm_dp_i2c_functionality,
-       .master_xfer = drm_dp_i2c_xfer,
-};
-
-static struct drm_dp_aux *i2c_to_aux(struct i2c_adapter *i2c)
-{
-       return container_of(i2c, struct drm_dp_aux, ddc);
-}
-
-static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
-{
-       mutex_lock(&i2c_to_aux(i2c)->hw_mutex);
-}
-
-static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
-{
-       return mutex_trylock(&i2c_to_aux(i2c)->hw_mutex);
-}
-
-static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
-{
-       mutex_unlock(&i2c_to_aux(i2c)->hw_mutex);
-}
-
-static const struct i2c_lock_operations drm_dp_i2c_lock_ops = {
-       .lock_bus = lock_bus,
-       .trylock_bus = trylock_bus,
-       .unlock_bus = unlock_bus,
-};
-
-static int drm_dp_aux_get_crc(struct drm_dp_aux *aux, u8 *crc)
-{
-       u8 buf, count;
-       int ret;
-
-       ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf);
-       if (ret < 0)
-               return ret;
-
-       WARN_ON(!(buf & DP_TEST_SINK_START));
-
-       ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK_MISC, &buf);
-       if (ret < 0)
-               return ret;
-
-       count = buf & DP_TEST_COUNT_MASK;
-       if (count == aux->crc_count)
-               return -EAGAIN; /* No CRC yet */
-
-       aux->crc_count = count;
-
-       /*
-        * At DP_TEST_CRC_R_CR, there's 6 bytes containing CRC data, 2 bytes
-        * per component (RGB or CrYCb).
-        */
-       ret = drm_dp_dpcd_read(aux, DP_TEST_CRC_R_CR, crc, 6);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static void drm_dp_aux_crc_work(struct work_struct *work)
-{
-       struct drm_dp_aux *aux = container_of(work, struct drm_dp_aux,
-                                             crc_work);
-       struct drm_crtc *crtc;
-       u8 crc_bytes[6];
-       uint32_t crcs[3];
-       int ret;
-
-       if (WARN_ON(!aux->crtc))
-               return;
-
-       crtc = aux->crtc;
-       while (crtc->crc.opened) {
-               drm_crtc_wait_one_vblank(crtc);
-               if (!crtc->crc.opened)
-                       break;
-
-               ret = drm_dp_aux_get_crc(aux, crc_bytes);
-               if (ret == -EAGAIN) {
-                       usleep_range(1000, 2000);
-                       ret = drm_dp_aux_get_crc(aux, crc_bytes);
-               }
-
-               if (ret == -EAGAIN) {
-                       drm_dbg_kms(aux->drm_dev, "%s: Get CRC failed after retrying: %d\n",
-                                   aux->name, ret);
-                       continue;
-               } else if (ret) {
-                       drm_dbg_kms(aux->drm_dev, "%s: Failed to get a CRC: %d\n", aux->name, ret);
-                       continue;
-               }
-
-               crcs[0] = crc_bytes[0] | crc_bytes[1] << 8;
-               crcs[1] = crc_bytes[2] | crc_bytes[3] << 8;
-               crcs[2] = crc_bytes[4] | crc_bytes[5] << 8;
-               drm_crtc_add_crc_entry(crtc, false, 0, crcs);
-       }
-}
-
-/**
- * drm_dp_remote_aux_init() - minimally initialise a remote aux channel
- * @aux: DisplayPort AUX channel
- *
- * Used for remote aux channel in general. Merely initialize the crc work
- * struct.
- */
-void drm_dp_remote_aux_init(struct drm_dp_aux *aux)
-{
-       INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work);
-}
-EXPORT_SYMBOL(drm_dp_remote_aux_init);
-
-/**
- * drm_dp_aux_init() - minimally initialise an aux channel
- * @aux: DisplayPort AUX channel
- *
- * If you need to use the drm_dp_aux's i2c adapter prior to registering it with
- * the outside world, call drm_dp_aux_init() first. For drivers which are
- * grandparents to their AUX adapters (e.g. the AUX adapter is parented by a
- * &drm_connector), you must still call drm_dp_aux_register() once the connector
- * has been registered to allow userspace access to the auxiliary DP channel.
- * Likewise, for such drivers you should also assign &drm_dp_aux.drm_dev as
- * early as possible so that the &drm_device that corresponds to the AUX adapter
- * may be mentioned in debugging output from the DRM DP helpers.
- *
- * For devices which use a separate platform device for their AUX adapters, this
- * may be called as early as required by the driver.
- *
- */
-void drm_dp_aux_init(struct drm_dp_aux *aux)
-{
-       mutex_init(&aux->hw_mutex);
-       mutex_init(&aux->cec.lock);
-       INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work);
-
-       aux->ddc.algo = &drm_dp_i2c_algo;
-       aux->ddc.algo_data = aux;
-       aux->ddc.retries = 3;
-
-       aux->ddc.lock_ops = &drm_dp_i2c_lock_ops;
-}
-EXPORT_SYMBOL(drm_dp_aux_init);
-
-/**
- * drm_dp_aux_register() - initialise and register aux channel
- * @aux: DisplayPort AUX channel
- *
- * Automatically calls drm_dp_aux_init() if this hasn't been done yet. This
- * should only be called once the parent of @aux, &drm_dp_aux.dev, is
- * initialized. For devices which are grandparents of their AUX channels,
- * &drm_dp_aux.dev will typically be the &drm_connector &device which
- * corresponds to @aux. For these devices, it's advised to call
- * drm_dp_aux_register() in &drm_connector_funcs.late_register, and likewise to
- * call drm_dp_aux_unregister() in &drm_connector_funcs.early_unregister.
- * Functions which don't follow this will likely Oops when
- * %CONFIG_DRM_DP_AUX_CHARDEV is enabled.
- *
- * For devices where the AUX channel is a device that exists independently of
- * the &drm_device that uses it, such as SoCs and bridge devices, it is
- * recommended to call drm_dp_aux_register() after a &drm_device has been
- * assigned to &drm_dp_aux.drm_dev, and likewise to call
- * drm_dp_aux_unregister() once the &drm_device should no longer be associated
- * with the AUX channel (e.g. on bridge detach).
- *
- * Drivers which need to use the aux channel before either of the two points
- * mentioned above need to call drm_dp_aux_init() in order to use the AUX
- * channel before registration.
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_aux_register(struct drm_dp_aux *aux)
-{
-       int ret;
-
-       WARN_ON_ONCE(!aux->drm_dev);
-
-       if (!aux->ddc.algo)
-               drm_dp_aux_init(aux);
-
-       aux->ddc.class = I2C_CLASS_DDC;
-       aux->ddc.owner = THIS_MODULE;
-       aux->ddc.dev.parent = aux->dev;
-
-       strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev),
-               sizeof(aux->ddc.name));
-
-       ret = drm_dp_aux_register_devnode(aux);
-       if (ret)
-               return ret;
-
-       ret = i2c_add_adapter(&aux->ddc);
-       if (ret) {
-               drm_dp_aux_unregister_devnode(aux);
-               return ret;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_aux_register);
-
-/**
- * drm_dp_aux_unregister() - unregister an AUX adapter
- * @aux: DisplayPort AUX channel
- */
-void drm_dp_aux_unregister(struct drm_dp_aux *aux)
-{
-       drm_dp_aux_unregister_devnode(aux);
-       i2c_del_adapter(&aux->ddc);
-}
-EXPORT_SYMBOL(drm_dp_aux_unregister);
-
-#define PSR_SETUP_TIME(x) [DP_PSR_SETUP_TIME_ ## x >> DP_PSR_SETUP_TIME_SHIFT] = (x)
-
-/**
- * drm_dp_psr_setup_time() - PSR setup in time usec
- * @psr_cap: PSR capabilities from DPCD
- *
- * Returns:
- * PSR setup time for the panel in microseconds,  negative
- * error code on failure.
- */
-int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE])
-{
-       static const u16 psr_setup_time_us[] = {
-               PSR_SETUP_TIME(330),
-               PSR_SETUP_TIME(275),
-               PSR_SETUP_TIME(220),
-               PSR_SETUP_TIME(165),
-               PSR_SETUP_TIME(110),
-               PSR_SETUP_TIME(55),
-               PSR_SETUP_TIME(0),
-       };
-       int i;
-
-       i = (psr_cap[1] & DP_PSR_SETUP_TIME_MASK) >> DP_PSR_SETUP_TIME_SHIFT;
-       if (i >= ARRAY_SIZE(psr_setup_time_us))
-               return -EINVAL;
-
-       return psr_setup_time_us[i];
-}
-EXPORT_SYMBOL(drm_dp_psr_setup_time);
-
-#undef PSR_SETUP_TIME
-
-/**
- * drm_dp_start_crc() - start capture of frame CRCs
- * @aux: DisplayPort AUX channel
- * @crtc: CRTC displaying the frames whose CRCs are to be captured
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc)
-{
-       u8 buf;
-       int ret;
-
-       ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf);
-       if (ret < 0)
-               return ret;
-
-       ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf | DP_TEST_SINK_START);
-       if (ret < 0)
-               return ret;
-
-       aux->crc_count = 0;
-       aux->crtc = crtc;
-       schedule_work(&aux->crc_work);
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_start_crc);
-
-/**
- * drm_dp_stop_crc() - stop capture of frame CRCs
- * @aux: DisplayPort AUX channel
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_stop_crc(struct drm_dp_aux *aux)
-{
-       u8 buf;
-       int ret;
-
-       ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf);
-       if (ret < 0)
-               return ret;
-
-       ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf & ~DP_TEST_SINK_START);
-       if (ret < 0)
-               return ret;
-
-       flush_work(&aux->crc_work);
-       aux->crtc = NULL;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_stop_crc);
-
-struct dpcd_quirk {
-       u8 oui[3];
-       u8 device_id[6];
-       bool is_branch;
-       u32 quirks;
-};
-
-#define OUI(first, second, third) { (first), (second), (third) }
-#define DEVICE_ID(first, second, third, fourth, fifth, sixth) \
-       { (first), (second), (third), (fourth), (fifth), (sixth) }
-
-#define DEVICE_ID_ANY  DEVICE_ID(0, 0, 0, 0, 0, 0)
-
-static const struct dpcd_quirk dpcd_quirk_list[] = {
-       /* Analogix 7737 needs reduced M and N at HBR2 link rates */
-       { OUI(0x00, 0x22, 0xb9), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_CONSTANT_N) },
-       /* LG LP140WF6-SPM1 eDP panel */
-       { OUI(0x00, 0x22, 0xb9), DEVICE_ID('s', 'i', 'v', 'a', 'r', 'T'), false, BIT(DP_DPCD_QUIRK_CONSTANT_N) },
-       /* Apple panels need some additional handling to support PSR */
-       { OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) },
-       /* CH7511 seems to leave SINK_COUNT zeroed */
-       { OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) },
-       /* Synaptics DP1.4 MST hubs can support DSC without virtual DPCD */
-       { OUI(0x90, 0xCC, 0x24), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) },
-       /* Apple MacBookPro 2017 15 inch eDP Retina panel reports too low DP_MAX_LINK_RATE */
-       { OUI(0x00, 0x10, 0xfa), DEVICE_ID(101, 68, 21, 101, 98, 97), false, BIT(DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS) },
-};
-
-#undef OUI
-
-/*
- * Get a bit mask of DPCD quirks for the sink/branch device identified by
- * ident. The quirk data is shared but it's up to the drivers to act on the
- * data.
- *
- * For now, only the OUI (first three bytes) is used, but this may be extended
- * to device identification string and hardware/firmware revisions later.
- */
-static u32
-drm_dp_get_quirks(const struct drm_dp_dpcd_ident *ident, bool is_branch)
-{
-       const struct dpcd_quirk *quirk;
-       u32 quirks = 0;
-       int i;
-       u8 any_device[] = DEVICE_ID_ANY;
-
-       for (i = 0; i < ARRAY_SIZE(dpcd_quirk_list); i++) {
-               quirk = &dpcd_quirk_list[i];
-
-               if (quirk->is_branch != is_branch)
-                       continue;
-
-               if (memcmp(quirk->oui, ident->oui, sizeof(ident->oui)) != 0)
-                       continue;
-
-               if (memcmp(quirk->device_id, any_device, sizeof(any_device)) != 0 &&
-                   memcmp(quirk->device_id, ident->device_id, sizeof(ident->device_id)) != 0)
-                       continue;
-
-               quirks |= quirk->quirks;
-       }
-
-       return quirks;
-}
-
-#undef DEVICE_ID_ANY
-#undef DEVICE_ID
-
-/**
- * drm_dp_read_desc - read sink/branch descriptor from DPCD
- * @aux: DisplayPort AUX channel
- * @desc: Device descriptor to fill from DPCD
- * @is_branch: true for branch devices, false for sink devices
- *
- * Read DPCD 0x400 (sink) or 0x500 (branch) into @desc. Also debug log the
- * identification.
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc,
-                    bool is_branch)
-{
-       struct drm_dp_dpcd_ident *ident = &desc->ident;
-       unsigned int offset = is_branch ? DP_BRANCH_OUI : DP_SINK_OUI;
-       int ret, dev_id_len;
-
-       ret = drm_dp_dpcd_read(aux, offset, ident, sizeof(*ident));
-       if (ret < 0)
-               return ret;
-
-       desc->quirks = drm_dp_get_quirks(ident, is_branch);
-
-       dev_id_len = strnlen(ident->device_id, sizeof(ident->device_id));
-
-       drm_dbg_kms(aux->drm_dev,
-                   "%s: DP %s: OUI %*phD dev-ID %*pE HW-rev %d.%d SW-rev %d.%d quirks 0x%04x\n",
-                   aux->name, is_branch ? "branch" : "sink",
-                   (int)sizeof(ident->oui), ident->oui, dev_id_len,
-                   ident->device_id, ident->hw_rev >> 4, ident->hw_rev & 0xf,
-                   ident->sw_major_rev, ident->sw_minor_rev, desc->quirks);
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_read_desc);
-
-/**
- * drm_dp_dsc_sink_max_slice_count() - Get the max slice count
- * supported by the DSC sink.
- * @dsc_dpcd: DSC capabilities from DPCD
- * @is_edp: true if its eDP, false for DP
- *
- * Read the slice capabilities DPCD register from DSC sink to get
- * the maximum slice count supported. This is used to populate
- * the DSC parameters in the &struct drm_dsc_config by the driver.
- * Driver creates an infoframe using these parameters to populate
- * &struct drm_dsc_pps_infoframe. These are sent to the sink using DSC
- * infoframe using the helper function drm_dsc_pps_infoframe_pack()
- *
- * Returns:
- * Maximum slice count supported by DSC sink or 0 its invalid
- */
-u8 drm_dp_dsc_sink_max_slice_count(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE],
-                                  bool is_edp)
-{
-       u8 slice_cap1 = dsc_dpcd[DP_DSC_SLICE_CAP_1 - DP_DSC_SUPPORT];
-
-       if (is_edp) {
-               /* For eDP, register DSC_SLICE_CAPABILITIES_1 gives slice count */
-               if (slice_cap1 & DP_DSC_4_PER_DP_DSC_SINK)
-                       return 4;
-               if (slice_cap1 & DP_DSC_2_PER_DP_DSC_SINK)
-                       return 2;
-               if (slice_cap1 & DP_DSC_1_PER_DP_DSC_SINK)
-                       return 1;
-       } else {
-               /* For DP, use values from DSC_SLICE_CAP_1 and DSC_SLICE_CAP2 */
-               u8 slice_cap2 = dsc_dpcd[DP_DSC_SLICE_CAP_2 - DP_DSC_SUPPORT];
-
-               if (slice_cap2 & DP_DSC_24_PER_DP_DSC_SINK)
-                       return 24;
-               if (slice_cap2 & DP_DSC_20_PER_DP_DSC_SINK)
-                       return 20;
-               if (slice_cap2 & DP_DSC_16_PER_DP_DSC_SINK)
-                       return 16;
-               if (slice_cap1 & DP_DSC_12_PER_DP_DSC_SINK)
-                       return 12;
-               if (slice_cap1 & DP_DSC_10_PER_DP_DSC_SINK)
-                       return 10;
-               if (slice_cap1 & DP_DSC_8_PER_DP_DSC_SINK)
-                       return 8;
-               if (slice_cap1 & DP_DSC_6_PER_DP_DSC_SINK)
-                       return 6;
-               if (slice_cap1 & DP_DSC_4_PER_DP_DSC_SINK)
-                       return 4;
-               if (slice_cap1 & DP_DSC_2_PER_DP_DSC_SINK)
-                       return 2;
-               if (slice_cap1 & DP_DSC_1_PER_DP_DSC_SINK)
-                       return 1;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_dsc_sink_max_slice_count);
-
-/**
- * drm_dp_dsc_sink_line_buf_depth() - Get the line buffer depth in bits
- * @dsc_dpcd: DSC capabilities from DPCD
- *
- * Read the DSC DPCD register to parse the line buffer depth in bits which is
- * number of bits of precision within the decoder line buffer supported by
- * the DSC sink. This is used to populate the DSC parameters in the
- * &struct drm_dsc_config by the driver.
- * Driver creates an infoframe using these parameters to populate
- * &struct drm_dsc_pps_infoframe. These are sent to the sink using DSC
- * infoframe using the helper function drm_dsc_pps_infoframe_pack()
- *
- * Returns:
- * Line buffer depth supported by DSC panel or 0 its invalid
- */
-u8 drm_dp_dsc_sink_line_buf_depth(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE])
-{
-       u8 line_buf_depth = dsc_dpcd[DP_DSC_LINE_BUF_BIT_DEPTH - DP_DSC_SUPPORT];
-
-       switch (line_buf_depth & DP_DSC_LINE_BUF_BIT_DEPTH_MASK) {
-       case DP_DSC_LINE_BUF_BIT_DEPTH_9:
-               return 9;
-       case DP_DSC_LINE_BUF_BIT_DEPTH_10:
-               return 10;
-       case DP_DSC_LINE_BUF_BIT_DEPTH_11:
-               return 11;
-       case DP_DSC_LINE_BUF_BIT_DEPTH_12:
-               return 12;
-       case DP_DSC_LINE_BUF_BIT_DEPTH_13:
-               return 13;
-       case DP_DSC_LINE_BUF_BIT_DEPTH_14:
-               return 14;
-       case DP_DSC_LINE_BUF_BIT_DEPTH_15:
-               return 15;
-       case DP_DSC_LINE_BUF_BIT_DEPTH_16:
-               return 16;
-       case DP_DSC_LINE_BUF_BIT_DEPTH_8:
-               return 8;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_dsc_sink_line_buf_depth);
-
-/**
- * drm_dp_dsc_sink_supported_input_bpcs() - Get all the input bits per component
- * values supported by the DSC sink.
- * @dsc_dpcd: DSC capabilities from DPCD
- * @dsc_bpc: An array to be filled by this helper with supported
- *           input bpcs.
- *
- * Read the DSC DPCD from the sink device to parse the supported bits per
- * component values. This is used to populate the DSC parameters
- * in the &struct drm_dsc_config by the driver.
- * Driver creates an infoframe using these parameters to populate
- * &struct drm_dsc_pps_infoframe. These are sent to the sink using DSC
- * infoframe using the helper function drm_dsc_pps_infoframe_pack()
- *
- * Returns:
- * Number of input BPC values parsed from the DPCD
- */
-int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE],
-                                        u8 dsc_bpc[3])
-{
-       int num_bpc = 0;
-       u8 color_depth = dsc_dpcd[DP_DSC_DEC_COLOR_DEPTH_CAP - DP_DSC_SUPPORT];
-
-       if (color_depth & DP_DSC_12_BPC)
-               dsc_bpc[num_bpc++] = 12;
-       if (color_depth & DP_DSC_10_BPC)
-               dsc_bpc[num_bpc++] = 10;
-       if (color_depth & DP_DSC_8_BPC)
-               dsc_bpc[num_bpc++] = 8;
-
-       return num_bpc;
-}
-EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs);
-
-/**
- * drm_dp_read_lttpr_common_caps - read the LTTPR common capabilities
- * @aux: DisplayPort AUX channel
- * @caps: buffer to return the capability info in
- *
- * Read capabilities common to all LTTPRs.
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,
-                                 u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
-{
-       int ret;
-
-       ret = drm_dp_dpcd_read(aux,
-                              DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV,
-                              caps, DP_LTTPR_COMMON_CAP_SIZE);
-       if (ret < 0)
-               return ret;
-
-       WARN_ON(ret != DP_LTTPR_COMMON_CAP_SIZE);
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_read_lttpr_common_caps);
-
-/**
- * drm_dp_read_lttpr_phy_caps - read the capabilities for a given LTTPR PHY
- * @aux: DisplayPort AUX channel
- * @dp_phy: LTTPR PHY to read the capabilities for
- * @caps: buffer to return the capability info in
- *
- * Read the capabilities for the given LTTPR PHY.
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,
-                              enum drm_dp_phy dp_phy,
-                              u8 caps[DP_LTTPR_PHY_CAP_SIZE])
-{
-       int ret;
-
-       ret = drm_dp_dpcd_read(aux,
-                              DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy),
-                              caps, DP_LTTPR_PHY_CAP_SIZE);
-       if (ret < 0)
-               return ret;
-
-       WARN_ON(ret != DP_LTTPR_PHY_CAP_SIZE);
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_read_lttpr_phy_caps);
-
-static u8 dp_lttpr_common_cap(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE], int r)
-{
-       return caps[r - DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV];
-}
-
-/**
- * drm_dp_lttpr_count - get the number of detected LTTPRs
- * @caps: LTTPR common capabilities
- *
- * Get the number of detected LTTPRs from the LTTPR common capabilities info.
- *
- * Returns:
- *   -ERANGE if more than supported number (8) of LTTPRs are detected
- *   -EINVAL if the DP_PHY_REPEATER_CNT register contains an invalid value
- *   otherwise the number of detected LTTPRs
- */
-int drm_dp_lttpr_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
-{
-       u8 count = dp_lttpr_common_cap(caps, DP_PHY_REPEATER_CNT);
-
-       switch (hweight8(count)) {
-       case 0:
-               return 0;
-       case 1:
-               return 8 - ilog2(count);
-       case 8:
-               return -ERANGE;
-       default:
-               return -EINVAL;
-       }
-}
-EXPORT_SYMBOL(drm_dp_lttpr_count);
-
-/**
- * drm_dp_lttpr_max_link_rate - get the maximum link rate supported by all LTTPRs
- * @caps: LTTPR common capabilities
- *
- * Returns the maximum link rate supported by all detected LTTPRs.
- */
-int drm_dp_lttpr_max_link_rate(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
-{
-       u8 rate = dp_lttpr_common_cap(caps, DP_MAX_LINK_RATE_PHY_REPEATER);
-
-       return drm_dp_bw_code_to_link_rate(rate);
-}
-EXPORT_SYMBOL(drm_dp_lttpr_max_link_rate);
-
-/**
- * drm_dp_lttpr_max_lane_count - get the maximum lane count supported by all LTTPRs
- * @caps: LTTPR common capabilities
- *
- * Returns the maximum lane count supported by all detected LTTPRs.
- */
-int drm_dp_lttpr_max_lane_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
-{
-       u8 max_lanes = dp_lttpr_common_cap(caps, DP_MAX_LANE_COUNT_PHY_REPEATER);
-
-       return max_lanes & DP_MAX_LANE_COUNT_MASK;
-}
-EXPORT_SYMBOL(drm_dp_lttpr_max_lane_count);
-
-/**
- * drm_dp_lttpr_voltage_swing_level_3_supported - check for LTTPR vswing3 support
- * @caps: LTTPR PHY capabilities
- *
- * Returns true if the @caps for an LTTPR TX PHY indicate support for
- * voltage swing level 3.
- */
-bool
-drm_dp_lttpr_voltage_swing_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE])
-{
-       u8 txcap = dp_lttpr_phy_cap(caps, DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1);
-
-       return txcap & DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED;
-}
-EXPORT_SYMBOL(drm_dp_lttpr_voltage_swing_level_3_supported);
-
-/**
- * drm_dp_lttpr_pre_emphasis_level_3_supported - check for LTTPR preemph3 support
- * @caps: LTTPR PHY capabilities
- *
- * Returns true if the @caps for an LTTPR TX PHY indicate support for
- * pre-emphasis level 3.
- */
-bool
-drm_dp_lttpr_pre_emphasis_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE])
-{
-       u8 txcap = dp_lttpr_phy_cap(caps, DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1);
-
-       return txcap & DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED;
-}
-EXPORT_SYMBOL(drm_dp_lttpr_pre_emphasis_level_3_supported);
-
-/**
- * drm_dp_get_phy_test_pattern() - get the requested pattern from the sink.
- * @aux: DisplayPort AUX channel
- * @data: DP phy compliance test parameters.
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_get_phy_test_pattern(struct drm_dp_aux *aux,
-                               struct drm_dp_phy_test_params *data)
-{
-       int err;
-       u8 rate, lanes;
-
-       err = drm_dp_dpcd_readb(aux, DP_TEST_LINK_RATE, &rate);
-       if (err < 0)
-               return err;
-       data->link_rate = drm_dp_bw_code_to_link_rate(rate);
-
-       err = drm_dp_dpcd_readb(aux, DP_TEST_LANE_COUNT, &lanes);
-       if (err < 0)
-               return err;
-       data->num_lanes = lanes & DP_MAX_LANE_COUNT_MASK;
-
-       if (lanes & DP_ENHANCED_FRAME_CAP)
-               data->enhanced_frame_cap = true;
-
-       err = drm_dp_dpcd_readb(aux, DP_PHY_TEST_PATTERN, &data->phy_pattern);
-       if (err < 0)
-               return err;
-
-       switch (data->phy_pattern) {
-       case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
-               err = drm_dp_dpcd_read(aux, DP_TEST_80BIT_CUSTOM_PATTERN_7_0,
-                                      &data->custom80, sizeof(data->custom80));
-               if (err < 0)
-                       return err;
-
-               break;
-       case DP_PHY_TEST_PATTERN_CP2520:
-               err = drm_dp_dpcd_read(aux, DP_TEST_HBR2_SCRAMBLER_RESET,
-                                      &data->hbr2_reset,
-                                      sizeof(data->hbr2_reset));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_get_phy_test_pattern);
-
-/**
- * drm_dp_set_phy_test_pattern() - set the pattern to the sink.
- * @aux: DisplayPort AUX channel
- * @data: DP phy compliance test parameters.
- * @dp_rev: DP revision to use for compliance testing
- *
- * Returns 0 on success or a negative error code on failure.
- */
-int drm_dp_set_phy_test_pattern(struct drm_dp_aux *aux,
-                               struct drm_dp_phy_test_params *data, u8 dp_rev)
-{
-       int err, i;
-       u8 link_config[2];
-       u8 test_pattern;
-
-       link_config[0] = drm_dp_link_rate_to_bw_code(data->link_rate);
-       link_config[1] = data->num_lanes;
-       if (data->enhanced_frame_cap)
-               link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
-       err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, link_config, 2);
-       if (err < 0)
-               return err;
-
-       test_pattern = data->phy_pattern;
-       if (dp_rev < 0x12) {
-               test_pattern = (test_pattern << 2) &
-                              DP_LINK_QUAL_PATTERN_11_MASK;
-               err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET,
-                                        test_pattern);
-               if (err < 0)
-                       return err;
-       } else {
-               for (i = 0; i < data->num_lanes; i++) {
-                       err = drm_dp_dpcd_writeb(aux,
-                                                DP_LINK_QUAL_LANE0_SET + i,
-                                                test_pattern);
-                       if (err < 0)
-                               return err;
-               }
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_set_phy_test_pattern);
-
-static const char *dp_pixelformat_get_name(enum dp_pixelformat pixelformat)
-{
-       if (pixelformat < 0 || pixelformat > DP_PIXELFORMAT_RESERVED)
-               return "Invalid";
-
-       switch (pixelformat) {
-       case DP_PIXELFORMAT_RGB:
-               return "RGB";
-       case DP_PIXELFORMAT_YUV444:
-               return "YUV444";
-       case DP_PIXELFORMAT_YUV422:
-               return "YUV422";
-       case DP_PIXELFORMAT_YUV420:
-               return "YUV420";
-       case DP_PIXELFORMAT_Y_ONLY:
-               return "Y_ONLY";
-       case DP_PIXELFORMAT_RAW:
-               return "RAW";
-       default:
-               return "Reserved";
-       }
-}
-
-static const char *dp_colorimetry_get_name(enum dp_pixelformat pixelformat,
-                                          enum dp_colorimetry colorimetry)
-{
-       if (pixelformat < 0 || pixelformat > DP_PIXELFORMAT_RESERVED)
-               return "Invalid";
-
-       switch (colorimetry) {
-       case DP_COLORIMETRY_DEFAULT:
-               switch (pixelformat) {
-               case DP_PIXELFORMAT_RGB:
-                       return "sRGB";
-               case DP_PIXELFORMAT_YUV444:
-               case DP_PIXELFORMAT_YUV422:
-               case DP_PIXELFORMAT_YUV420:
-                       return "BT.601";
-               case DP_PIXELFORMAT_Y_ONLY:
-                       return "DICOM PS3.14";
-               case DP_PIXELFORMAT_RAW:
-                       return "Custom Color Profile";
-               default:
-                       return "Reserved";
-               }
-       case DP_COLORIMETRY_RGB_WIDE_FIXED: /* and DP_COLORIMETRY_BT709_YCC */
-               switch (pixelformat) {
-               case DP_PIXELFORMAT_RGB:
-                       return "Wide Fixed";
-               case DP_PIXELFORMAT_YUV444:
-               case DP_PIXELFORMAT_YUV422:
-               case DP_PIXELFORMAT_YUV420:
-                       return "BT.709";
-               default:
-                       return "Reserved";
-               }
-       case DP_COLORIMETRY_RGB_WIDE_FLOAT: /* and DP_COLORIMETRY_XVYCC_601 */
-               switch (pixelformat) {
-               case DP_PIXELFORMAT_RGB:
-                       return "Wide Float";
-               case DP_PIXELFORMAT_YUV444:
-               case DP_PIXELFORMAT_YUV422:
-               case DP_PIXELFORMAT_YUV420:
-                       return "xvYCC 601";
-               default:
-                       return "Reserved";
-               }
-       case DP_COLORIMETRY_OPRGB: /* and DP_COLORIMETRY_XVYCC_709 */
-               switch (pixelformat) {
-               case DP_PIXELFORMAT_RGB:
-                       return "OpRGB";
-               case DP_PIXELFORMAT_YUV444:
-               case DP_PIXELFORMAT_YUV422:
-               case DP_PIXELFORMAT_YUV420:
-                       return "xvYCC 709";
-               default:
-                       return "Reserved";
-               }
-       case DP_COLORIMETRY_DCI_P3_RGB: /* and DP_COLORIMETRY_SYCC_601 */
-               switch (pixelformat) {
-               case DP_PIXELFORMAT_RGB:
-                       return "DCI-P3";
-               case DP_PIXELFORMAT_YUV444:
-               case DP_PIXELFORMAT_YUV422:
-               case DP_PIXELFORMAT_YUV420:
-                       return "sYCC 601";
-               default:
-                       return "Reserved";
-               }
-       case DP_COLORIMETRY_RGB_CUSTOM: /* and DP_COLORIMETRY_OPYCC_601 */
-               switch (pixelformat) {
-               case DP_PIXELFORMAT_RGB:
-                       return "Custom Profile";
-               case DP_PIXELFORMAT_YUV444:
-               case DP_PIXELFORMAT_YUV422:
-               case DP_PIXELFORMAT_YUV420:
-                       return "OpYCC 601";
-               default:
-                       return "Reserved";
-               }
-       case DP_COLORIMETRY_BT2020_RGB: /* and DP_COLORIMETRY_BT2020_CYCC */
-               switch (pixelformat) {
-               case DP_PIXELFORMAT_RGB:
-                       return "BT.2020 RGB";
-               case DP_PIXELFORMAT_YUV444:
-               case DP_PIXELFORMAT_YUV422:
-               case DP_PIXELFORMAT_YUV420:
-                       return "BT.2020 CYCC";
-               default:
-                       return "Reserved";
-               }
-       case DP_COLORIMETRY_BT2020_YCC:
-               switch (pixelformat) {
-               case DP_PIXELFORMAT_YUV444:
-               case DP_PIXELFORMAT_YUV422:
-               case DP_PIXELFORMAT_YUV420:
-                       return "BT.2020 YCC";
-               default:
-                       return "Reserved";
-               }
-       default:
-               return "Invalid";
-       }
-}
-
-static const char *dp_dynamic_range_get_name(enum dp_dynamic_range dynamic_range)
-{
-       switch (dynamic_range) {
-       case DP_DYNAMIC_RANGE_VESA:
-               return "VESA range";
-       case DP_DYNAMIC_RANGE_CTA:
-               return "CTA range";
-       default:
-               return "Invalid";
-       }
-}
-
-static const char *dp_content_type_get_name(enum dp_content_type content_type)
-{
-       switch (content_type) {
-       case DP_CONTENT_TYPE_NOT_DEFINED:
-               return "Not defined";
-       case DP_CONTENT_TYPE_GRAPHICS:
-               return "Graphics";
-       case DP_CONTENT_TYPE_PHOTO:
-               return "Photo";
-       case DP_CONTENT_TYPE_VIDEO:
-               return "Video";
-       case DP_CONTENT_TYPE_GAME:
-               return "Game";
-       default:
-               return "Reserved";
-       }
-}
-
-void drm_dp_vsc_sdp_log(const char *level, struct device *dev,
-                       const struct drm_dp_vsc_sdp *vsc)
-{
-#define DP_SDP_LOG(fmt, ...) dev_printk(level, dev, fmt, ##__VA_ARGS__)
-       DP_SDP_LOG("DP SDP: %s, revision %u, length %u\n", "VSC",
-                  vsc->revision, vsc->length);
-       DP_SDP_LOG("    pixelformat: %s\n",
-                  dp_pixelformat_get_name(vsc->pixelformat));
-       DP_SDP_LOG("    colorimetry: %s\n",
-                  dp_colorimetry_get_name(vsc->pixelformat, vsc->colorimetry));
-       DP_SDP_LOG("    bpc: %u\n", vsc->bpc);
-       DP_SDP_LOG("    dynamic range: %s\n",
-                  dp_dynamic_range_get_name(vsc->dynamic_range));
-       DP_SDP_LOG("    content type: %s\n",
-                  dp_content_type_get_name(vsc->content_type));
-#undef DP_SDP_LOG
-}
-EXPORT_SYMBOL(drm_dp_vsc_sdp_log);
-
-/**
- * drm_dp_get_pcon_max_frl_bw() - maximum frl supported by PCON
- * @dpcd: DisplayPort configuration data
- * @port_cap: port capabilities
- *
- * Returns maximum frl bandwidth supported by PCON in GBPS,
- * returns 0 if not supported.
- */
-int drm_dp_get_pcon_max_frl_bw(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                              const u8 port_cap[4])
-{
-       int bw;
-       u8 buf;
-
-       buf = port_cap[2];
-       bw = buf & DP_PCON_MAX_FRL_BW;
-
-       switch (bw) {
-       case DP_PCON_MAX_9GBPS:
-               return 9;
-       case DP_PCON_MAX_18GBPS:
-               return 18;
-       case DP_PCON_MAX_24GBPS:
-               return 24;
-       case DP_PCON_MAX_32GBPS:
-               return 32;
-       case DP_PCON_MAX_40GBPS:
-               return 40;
-       case DP_PCON_MAX_48GBPS:
-               return 48;
-       case DP_PCON_MAX_0GBPS:
-       default:
-               return 0;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_get_pcon_max_frl_bw);
-
-/**
- * drm_dp_pcon_frl_prepare() - Prepare PCON for FRL.
- * @aux: DisplayPort AUX channel
- * @enable_frl_ready_hpd: Configure DP_PCON_ENABLE_HPD_READY.
- *
- * Returns 0 if success, else returns negative error code.
- */
-int drm_dp_pcon_frl_prepare(struct drm_dp_aux *aux, bool enable_frl_ready_hpd)
-{
-       int ret;
-       u8 buf = DP_PCON_ENABLE_SOURCE_CTL_MODE |
-                DP_PCON_ENABLE_LINK_FRL_MODE;
-
-       if (enable_frl_ready_hpd)
-               buf |= DP_PCON_ENABLE_HPD_READY;
-
-       ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, buf);
-
-       return ret;
-}
-EXPORT_SYMBOL(drm_dp_pcon_frl_prepare);
-
-/**
- * drm_dp_pcon_is_frl_ready() - Is PCON ready for FRL
- * @aux: DisplayPort AUX channel
- *
- * Returns true if success, else returns false.
- */
-bool drm_dp_pcon_is_frl_ready(struct drm_dp_aux *aux)
-{
-       int ret;
-       u8 buf;
-
-       ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_TX_LINK_STATUS, &buf);
-       if (ret < 0)
-               return false;
-
-       if (buf & DP_PCON_FRL_READY)
-               return true;
-
-       return false;
-}
-EXPORT_SYMBOL(drm_dp_pcon_is_frl_ready);
-
-/**
- * drm_dp_pcon_frl_configure_1() - Set HDMI LINK Configuration-Step1
- * @aux: DisplayPort AUX channel
- * @max_frl_gbps: maximum frl bw to be configured between PCON and HDMI sink
- * @frl_mode: FRL Training mode, it can be either Concurrent or Sequential.
- * In Concurrent Mode, the FRL link bring up can be done along with
- * DP Link training. In Sequential mode, the FRL link bring up is done prior to
- * the DP Link training.
- *
- * Returns 0 if success, else returns negative error code.
- */
-
-int drm_dp_pcon_frl_configure_1(struct drm_dp_aux *aux, int max_frl_gbps,
-                               u8 frl_mode)
-{
-       int ret;
-       u8 buf;
-
-       ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_LINK_CONFIG_1, &buf);
-       if (ret < 0)
-               return ret;
-
-       if (frl_mode == DP_PCON_ENABLE_CONCURRENT_LINK)
-               buf |= DP_PCON_ENABLE_CONCURRENT_LINK;
-       else
-               buf &= ~DP_PCON_ENABLE_CONCURRENT_LINK;
-
-       switch (max_frl_gbps) {
-       case 9:
-               buf |=  DP_PCON_ENABLE_MAX_BW_9GBPS;
-               break;
-       case 18:
-               buf |=  DP_PCON_ENABLE_MAX_BW_18GBPS;
-               break;
-       case 24:
-               buf |=  DP_PCON_ENABLE_MAX_BW_24GBPS;
-               break;
-       case 32:
-               buf |=  DP_PCON_ENABLE_MAX_BW_32GBPS;
-               break;
-       case 40:
-               buf |=  DP_PCON_ENABLE_MAX_BW_40GBPS;
-               break;
-       case 48:
-               buf |=  DP_PCON_ENABLE_MAX_BW_48GBPS;
-               break;
-       case 0:
-               buf |=  DP_PCON_ENABLE_MAX_BW_0GBPS;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, buf);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_pcon_frl_configure_1);
-
-/**
- * drm_dp_pcon_frl_configure_2() - Set HDMI Link configuration Step-2
- * @aux: DisplayPort AUX channel
- * @max_frl_mask : Max FRL BW to be tried by the PCON with HDMI Sink
- * @frl_type : FRL training type, can be Extended, or Normal.
- * In Normal FRL training, the PCON tries each frl bw from the max_frl_mask
- * starting from min, and stops when link training is successful. In Extended
- * FRL training, all frl bw selected in the mask are trained by the PCON.
- *
- * Returns 0 if success, else returns negative error code.
- */
-int drm_dp_pcon_frl_configure_2(struct drm_dp_aux *aux, int max_frl_mask,
-                               u8 frl_type)
-{
-       int ret;
-       u8 buf = max_frl_mask;
-
-       if (frl_type == DP_PCON_FRL_LINK_TRAIN_EXTENDED)
-               buf |= DP_PCON_FRL_LINK_TRAIN_EXTENDED;
-       else
-               buf &= ~DP_PCON_FRL_LINK_TRAIN_EXTENDED;
-
-       ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_2, buf);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_pcon_frl_configure_2);
-
-/**
- * drm_dp_pcon_reset_frl_config() - Re-Set HDMI Link configuration.
- * @aux: DisplayPort AUX channel
- *
- * Returns 0 if success, else returns negative error code.
- */
-int drm_dp_pcon_reset_frl_config(struct drm_dp_aux *aux)
-{
-       int ret;
-
-       ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, 0x0);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_pcon_reset_frl_config);
-
-/**
- * drm_dp_pcon_frl_enable() - Enable HDMI link through FRL
- * @aux: DisplayPort AUX channel
- *
- * Returns 0 if success, else returns negative error code.
- */
-int drm_dp_pcon_frl_enable(struct drm_dp_aux *aux)
-{
-       int ret;
-       u8 buf = 0;
-
-       ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_LINK_CONFIG_1, &buf);
-       if (ret < 0)
-               return ret;
-       if (!(buf & DP_PCON_ENABLE_SOURCE_CTL_MODE)) {
-               drm_dbg_kms(aux->drm_dev, "%s: PCON in Autonomous mode, can't enable FRL\n",
-                           aux->name);
-               return -EINVAL;
-       }
-       buf |= DP_PCON_ENABLE_HDMI_LINK;
-       ret = drm_dp_dpcd_writeb(aux, DP_PCON_HDMI_LINK_CONFIG_1, buf);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_pcon_frl_enable);
-
-/**
- * drm_dp_pcon_hdmi_link_active() - check if the PCON HDMI LINK status is active.
- * @aux: DisplayPort AUX channel
- *
- * Returns true if link is active else returns false.
- */
-bool drm_dp_pcon_hdmi_link_active(struct drm_dp_aux *aux)
-{
-       u8 buf;
-       int ret;
-
-       ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_TX_LINK_STATUS, &buf);
-       if (ret < 0)
-               return false;
-
-       return buf & DP_PCON_HDMI_TX_LINK_ACTIVE;
-}
-EXPORT_SYMBOL(drm_dp_pcon_hdmi_link_active);
-
-/**
- * drm_dp_pcon_hdmi_link_mode() - get the PCON HDMI LINK MODE
- * @aux: DisplayPort AUX channel
- * @frl_trained_mask: pointer to store bitmask of the trained bw configuration.
- * Valid only if the MODE returned is FRL. For Normal Link training mode
- * only 1 of the bits will be set, but in case of Extended mode, more than
- * one bits can be set.
- *
- * Returns the link mode : TMDS or FRL on success, else returns negative error
- * code.
- */
-int drm_dp_pcon_hdmi_link_mode(struct drm_dp_aux *aux, u8 *frl_trained_mask)
-{
-       u8 buf;
-       int mode;
-       int ret;
-
-       ret = drm_dp_dpcd_readb(aux, DP_PCON_HDMI_POST_FRL_STATUS, &buf);
-       if (ret < 0)
-               return ret;
-
-       mode = buf & DP_PCON_HDMI_LINK_MODE;
-
-       if (frl_trained_mask && DP_PCON_HDMI_MODE_FRL == mode)
-               *frl_trained_mask = (buf & DP_PCON_HDMI_FRL_TRAINED_BW) >> 1;
-
-       return mode;
-}
-EXPORT_SYMBOL(drm_dp_pcon_hdmi_link_mode);
-
-/**
- * drm_dp_pcon_hdmi_frl_link_error_count() - print the error count per lane
- * during link failure between PCON and HDMI sink
- * @aux: DisplayPort AUX channel
- * @connector: DRM connector
- * code.
- **/
-
-void drm_dp_pcon_hdmi_frl_link_error_count(struct drm_dp_aux *aux,
-                                          struct drm_connector *connector)
-{
-       u8 buf, error_count;
-       int i, num_error;
-       struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
-
-       for (i = 0; i < hdmi->max_lanes; i++) {
-               if (drm_dp_dpcd_readb(aux, DP_PCON_HDMI_ERROR_STATUS_LN0 + i, &buf) < 0)
-                       return;
-
-               error_count = buf & DP_PCON_HDMI_ERROR_COUNT_MASK;
-               switch (error_count) {
-               case DP_PCON_HDMI_ERROR_COUNT_HUNDRED_PLUS:
-                       num_error = 100;
-                       break;
-               case DP_PCON_HDMI_ERROR_COUNT_TEN_PLUS:
-                       num_error = 10;
-                       break;
-               case DP_PCON_HDMI_ERROR_COUNT_THREE_PLUS:
-                       num_error = 3;
-                       break;
-               default:
-                       num_error = 0;
-               }
-
-               drm_err(aux->drm_dev, "%s: More than %d errors since the last read for lane %d",
-                       aux->name, num_error, i);
-       }
-}
-EXPORT_SYMBOL(drm_dp_pcon_hdmi_frl_link_error_count);
-
-/*
- * drm_dp_pcon_enc_is_dsc_1_2 - Does PCON Encoder supports DSC 1.2
- * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder
- *
- * Returns true is PCON encoder is DSC 1.2 else returns false.
- */
-bool drm_dp_pcon_enc_is_dsc_1_2(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE])
-{
-       u8 buf;
-       u8 major_v, minor_v;
-
-       buf = pcon_dsc_dpcd[DP_PCON_DSC_VERSION - DP_PCON_DSC_ENCODER];
-       major_v = (buf & DP_PCON_DSC_MAJOR_MASK) >> DP_PCON_DSC_MAJOR_SHIFT;
-       minor_v = (buf & DP_PCON_DSC_MINOR_MASK) >> DP_PCON_DSC_MINOR_SHIFT;
-
-       if (major_v == 1 && minor_v == 2)
-               return true;
-
-       return false;
-}
-EXPORT_SYMBOL(drm_dp_pcon_enc_is_dsc_1_2);
-
-/*
- * drm_dp_pcon_dsc_max_slices - Get max slices supported by PCON DSC Encoder
- * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder
- *
- * Returns maximum no. of slices supported by the PCON DSC Encoder.
- */
-int drm_dp_pcon_dsc_max_slices(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE])
-{
-       u8 slice_cap1, slice_cap2;
-
-       slice_cap1 = pcon_dsc_dpcd[DP_PCON_DSC_SLICE_CAP_1 - DP_PCON_DSC_ENCODER];
-       slice_cap2 = pcon_dsc_dpcd[DP_PCON_DSC_SLICE_CAP_2 - DP_PCON_DSC_ENCODER];
-
-       if (slice_cap2 & DP_PCON_DSC_24_PER_DSC_ENC)
-               return 24;
-       if (slice_cap2 & DP_PCON_DSC_20_PER_DSC_ENC)
-               return 20;
-       if (slice_cap2 & DP_PCON_DSC_16_PER_DSC_ENC)
-               return 16;
-       if (slice_cap1 & DP_PCON_DSC_12_PER_DSC_ENC)
-               return 12;
-       if (slice_cap1 & DP_PCON_DSC_10_PER_DSC_ENC)
-               return 10;
-       if (slice_cap1 & DP_PCON_DSC_8_PER_DSC_ENC)
-               return 8;
-       if (slice_cap1 & DP_PCON_DSC_6_PER_DSC_ENC)
-               return 6;
-       if (slice_cap1 & DP_PCON_DSC_4_PER_DSC_ENC)
-               return 4;
-       if (slice_cap1 & DP_PCON_DSC_2_PER_DSC_ENC)
-               return 2;
-       if (slice_cap1 & DP_PCON_DSC_1_PER_DSC_ENC)
-               return 1;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_pcon_dsc_max_slices);
-
-/*
- * drm_dp_pcon_dsc_max_slice_width() - Get max slice width for Pcon DSC encoder
- * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder
- *
- * Returns maximum width of the slices in pixel width i.e. no. of pixels x 320.
- */
-int drm_dp_pcon_dsc_max_slice_width(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE])
-{
-       u8 buf;
-
-       buf = pcon_dsc_dpcd[DP_PCON_DSC_MAX_SLICE_WIDTH - DP_PCON_DSC_ENCODER];
-
-       return buf * DP_DSC_SLICE_WIDTH_MULTIPLIER;
-}
-EXPORT_SYMBOL(drm_dp_pcon_dsc_max_slice_width);
-
-/*
- * drm_dp_pcon_dsc_bpp_incr() - Get bits per pixel increment for PCON DSC encoder
- * @pcon_dsc_dpcd: DSC capabilities of the PCON DSC Encoder
- *
- * Returns the bpp precision supported by the PCON encoder.
- */
-int drm_dp_pcon_dsc_bpp_incr(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE])
-{
-       u8 buf;
-
-       buf = pcon_dsc_dpcd[DP_PCON_DSC_BPP_INCR - DP_PCON_DSC_ENCODER];
-
-       switch (buf & DP_PCON_DSC_BPP_INCR_MASK) {
-       case DP_PCON_DSC_ONE_16TH_BPP:
-               return 16;
-       case DP_PCON_DSC_ONE_8TH_BPP:
-               return 8;
-       case DP_PCON_DSC_ONE_4TH_BPP:
-               return 4;
-       case DP_PCON_DSC_ONE_HALF_BPP:
-               return 2;
-       case DP_PCON_DSC_ONE_BPP:
-               return 1;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_pcon_dsc_bpp_incr);
-
-static
-int drm_dp_pcon_configure_dsc_enc(struct drm_dp_aux *aux, u8 pps_buf_config)
-{
-       u8 buf;
-       int ret;
-
-       ret = drm_dp_dpcd_readb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, &buf);
-       if (ret < 0)
-               return ret;
-
-       buf |= DP_PCON_ENABLE_DSC_ENCODER;
-
-       if (pps_buf_config <= DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER) {
-               buf &= ~DP_PCON_ENCODER_PPS_OVERRIDE_MASK;
-               buf |= pps_buf_config << 2;
-       }
-
-       ret = drm_dp_dpcd_writeb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, buf);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-/**
- * drm_dp_pcon_pps_default() - Let PCON fill the default pps parameters
- * for DSC1.2 between PCON & HDMI2.1 sink
- * @aux: DisplayPort AUX channel
- *
- * Returns 0 on success, else returns negative error code.
- */
-int drm_dp_pcon_pps_default(struct drm_dp_aux *aux)
-{
-       int ret;
-
-       ret = drm_dp_pcon_configure_dsc_enc(aux, DP_PCON_ENC_PPS_OVERRIDE_DISABLED);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_pcon_pps_default);
-
-/**
- * drm_dp_pcon_pps_override_buf() - Configure PPS encoder override buffer for
- * HDMI sink
- * @aux: DisplayPort AUX channel
- * @pps_buf: 128 bytes to be written into PPS buffer for HDMI sink by PCON.
- *
- * Returns 0 on success, else returns negative error code.
- */
-int drm_dp_pcon_pps_override_buf(struct drm_dp_aux *aux, u8 pps_buf[128])
-{
-       int ret;
-
-       ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVERRIDE_BASE, &pps_buf, 128);
-       if (ret < 0)
-               return ret;
-
-       ret = drm_dp_pcon_configure_dsc_enc(aux, DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_pcon_pps_override_buf);
-
-/*
- * drm_dp_pcon_pps_override_param() - Write PPS parameters to DSC encoder
- * override registers
- * @aux: DisplayPort AUX channel
- * @pps_param: 3 Parameters (2 Bytes each) : Slice Width, Slice Height,
- * bits_per_pixel.
- *
- * Returns 0 on success, else returns negative error code.
- */
-int drm_dp_pcon_pps_override_param(struct drm_dp_aux *aux, u8 pps_param[6])
-{
-       int ret;
-
-       ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVRD_SLICE_HEIGHT, &pps_param[0], 2);
-       if (ret < 0)
-               return ret;
-       ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVRD_SLICE_WIDTH, &pps_param[2], 2);
-       if (ret < 0)
-               return ret;
-       ret = drm_dp_dpcd_write(aux, DP_PCON_HDMI_PPS_OVRD_BPP, &pps_param[4], 2);
-       if (ret < 0)
-               return ret;
-
-       ret = drm_dp_pcon_configure_dsc_enc(aux, DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_pcon_pps_override_param);
-
-/*
- * drm_dp_pcon_convert_rgb_to_ycbcr() - Configure the PCon to convert RGB to Ycbcr
- * @aux: displayPort AUX channel
- * @color_spc: Color-space/s for which conversion is to be enabled, 0 for disable.
- *
- * Returns 0 on success, else returns negative error code.
- */
-int drm_dp_pcon_convert_rgb_to_ycbcr(struct drm_dp_aux *aux, u8 color_spc)
-{
-       int ret;
-       u8 buf;
-
-       ret = drm_dp_dpcd_readb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, &buf);
-       if (ret < 0)
-               return ret;
-
-       if (color_spc & DP_CONVERSION_RGB_YCBCR_MASK)
-               buf |= (color_spc & DP_CONVERSION_RGB_YCBCR_MASK);
-       else
-               buf &= ~DP_CONVERSION_RGB_YCBCR_MASK;
-
-       ret = drm_dp_dpcd_writeb(aux, DP_PROTOCOL_CONVERTER_CONTROL_2, buf);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_pcon_convert_rgb_to_ycbcr);
-
-/**
- * drm_edp_backlight_set_level() - Set the backlight level of an eDP panel via AUX
- * @aux: The DP AUX channel to use
- * @bl: Backlight capability info from drm_edp_backlight_init()
- * @level: The brightness level to set
- *
- * Sets the brightness level of an eDP panel's backlight. Note that the panel's backlight must
- * already have been enabled by the driver by calling drm_edp_backlight_enable().
- *
- * Returns: %0 on success, negative error code on failure
- */
-int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
-                               u16 level)
-{
-       int ret;
-       u8 buf[2] = { 0 };
-
-       /* The panel uses the PWM for controlling brightness levels */
-       if (!bl->aux_set)
-               return 0;
-
-       if (bl->lsb_reg_used) {
-               buf[0] = (level & 0xff00) >> 8;
-               buf[1] = (level & 0x00ff);
-       } else {
-               buf[0] = level;
-       }
-
-       ret = drm_dp_dpcd_write(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, buf, sizeof(buf));
-       if (ret != sizeof(buf)) {
-               drm_err(aux->drm_dev,
-                       "%s: Failed to write aux backlight level: %d\n",
-                       aux->name, ret);
-               return ret < 0 ? ret : -EIO;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_edp_backlight_set_level);
-
-static int
-drm_edp_backlight_set_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
-                            bool enable)
-{
-       int ret;
-       u8 buf;
-
-       /* This panel uses the EDP_BL_PWR GPIO for enablement */
-       if (!bl->aux_enable)
-               return 0;
-
-       ret = drm_dp_dpcd_readb(aux, DP_EDP_DISPLAY_CONTROL_REGISTER, &buf);
-       if (ret != 1) {
-               drm_err(aux->drm_dev, "%s: Failed to read eDP display control register: %d\n",
-                       aux->name, ret);
-               return ret < 0 ? ret : -EIO;
-       }
-       if (enable)
-               buf |= DP_EDP_BACKLIGHT_ENABLE;
-       else
-               buf &= ~DP_EDP_BACKLIGHT_ENABLE;
-
-       ret = drm_dp_dpcd_writeb(aux, DP_EDP_DISPLAY_CONTROL_REGISTER, buf);
-       if (ret != 1) {
-               drm_err(aux->drm_dev, "%s: Failed to write eDP display control register: %d\n",
-                       aux->name, ret);
-               return ret < 0 ? ret : -EIO;
-       }
-
-       return 0;
-}
-
-/**
- * drm_edp_backlight_enable() - Enable an eDP panel's backlight using DPCD
- * @aux: The DP AUX channel to use
- * @bl: Backlight capability info from drm_edp_backlight_init()
- * @level: The initial backlight level to set via AUX, if there is one
- *
- * This function handles enabling DPCD backlight controls on a panel over DPCD, while additionally
- * restoring any important backlight state such as the given backlight level, the brightness byte
- * count, backlight frequency, etc.
- *
- * Note that certain panels do not support being enabled or disabled via DPCD, but instead require
- * that the driver handle enabling/disabling the panel through implementation-specific means using
- * the EDP_BL_PWR GPIO. For such panels, &drm_edp_backlight_info.aux_enable will be set to %false,
- * this function becomes a no-op, and the driver is expected to handle powering the panel on using
- * the EDP_BL_PWR GPIO.
- *
- * Returns: %0 on success, negative error code on failure.
- */
-int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
-                            const u16 level)
-{
-       int ret;
-       u8 dpcd_buf;
-
-       if (bl->aux_set)
-               dpcd_buf = DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
-       else
-               dpcd_buf = DP_EDP_BACKLIGHT_CONTROL_MODE_PWM;
-
-       if (bl->pwmgen_bit_count) {
-               ret = drm_dp_dpcd_writeb(aux, DP_EDP_PWMGEN_BIT_COUNT, bl->pwmgen_bit_count);
-               if (ret != 1)
-                       drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n",
-                                   aux->name, ret);
-       }
-
-       if (bl->pwm_freq_pre_divider) {
-               ret = drm_dp_dpcd_writeb(aux, DP_EDP_BACKLIGHT_FREQ_SET, bl->pwm_freq_pre_divider);
-               if (ret != 1)
-                       drm_dbg_kms(aux->drm_dev,
-                                   "%s: Failed to write aux backlight frequency: %d\n",
-                                   aux->name, ret);
-               else
-                       dpcd_buf |= DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE;
-       }
-
-       ret = drm_dp_dpcd_writeb(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, dpcd_buf);
-       if (ret != 1) {
-               drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux backlight mode: %d\n",
-                           aux->name, ret);
-               return ret < 0 ? ret : -EIO;
-       }
-
-       ret = drm_edp_backlight_set_level(aux, bl, level);
-       if (ret < 0)
-               return ret;
-       ret = drm_edp_backlight_set_enable(aux, bl, true);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_edp_backlight_enable);
-
-/**
- * drm_edp_backlight_disable() - Disable an eDP backlight using DPCD, if supported
- * @aux: The DP AUX channel to use
- * @bl: Backlight capability info from drm_edp_backlight_init()
- *
- * This function handles disabling DPCD backlight controls on a panel over AUX.
- *
- * Note that certain panels do not support being enabled or disabled via DPCD, but instead require
- * that the driver handle enabling/disabling the panel through implementation-specific means using
- * the EDP_BL_PWR GPIO. For such panels, &drm_edp_backlight_info.aux_enable will be set to %false,
- * this function becomes a no-op, and the driver is expected to handle powering the panel off using
- * the EDP_BL_PWR GPIO.
- *
- * Returns: %0 on success or no-op, negative error code on failure.
- */
-int drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl)
-{
-       int ret;
-
-       ret = drm_edp_backlight_set_enable(aux, bl, false);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_edp_backlight_disable);
-
-static inline int
-drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
-                           u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE])
-{
-       int fxp, fxp_min, fxp_max, fxp_actual, f = 1;
-       int ret;
-       u8 pn, pn_min, pn_max;
-
-       if (!bl->aux_set)
-               return 0;
-
-       ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT, &pn);
-       if (ret != 1) {
-               drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap: %d\n",
-                           aux->name, ret);
-               return -ENODEV;
-       }
-
-       pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
-       bl->max = (1 << pn) - 1;
-       if (!driver_pwm_freq_hz)
-               return 0;
-
-       /*
-        * Set PWM Frequency divider to match desired frequency provided by the driver.
-        * The PWM Frequency is calculated as 27Mhz / (F x P).
-        * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of the
-        *             EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h)
-        * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the
-        *             EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h)
-        */
-
-       /* Find desired value of (F x P)
-        * Note that, if F x P is out of supported range, the maximum value or minimum value will
-        * applied automatically. So no need to check that.
-        */
-       fxp = DIV_ROUND_CLOSEST(1000 * DP_EDP_BACKLIGHT_FREQ_BASE_KHZ, driver_pwm_freq_hz);
-
-       /* Use highest possible value of Pn for more granularity of brightness adjustment while
-        * satisfying the conditions below.
-        * - Pn is in the range of Pn_min and Pn_max
-        * - F is in the range of 1 and 255
-        * - FxP is within 25% of desired value.
-        *   Note: 25% is arbitrary value and may need some tweak.
-        */
-       ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min);
-       if (ret != 1) {
-               drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n",
-                           aux->name, ret);
-               return 0;
-       }
-       ret = drm_dp_dpcd_readb(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max);
-       if (ret != 1) {
-               drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n",
-                           aux->name, ret);
-               return 0;
-       }
-       pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
-       pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
-
-       /* Ensure frequency is within 25% of desired value */
-       fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
-       fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
-       if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) {
-               drm_dbg_kms(aux->drm_dev,
-                           "%s: Driver defined backlight frequency (%d) out of range\n",
-                           aux->name, driver_pwm_freq_hz);
-               return 0;
-       }
-
-       for (pn = pn_max; pn >= pn_min; pn--) {
-               f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
-               fxp_actual = f << pn;
-               if (fxp_min <= fxp_actual && fxp_actual <= fxp_max)
-                       break;
-       }
-
-       ret = drm_dp_dpcd_writeb(aux, DP_EDP_PWMGEN_BIT_COUNT, pn);
-       if (ret != 1) {
-               drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n",
-                           aux->name, ret);
-               return 0;
-       }
-       bl->pwmgen_bit_count = pn;
-       bl->max = (1 << pn) - 1;
-
-       if (edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP) {
-               bl->pwm_freq_pre_divider = f;
-               drm_dbg_kms(aux->drm_dev, "%s: Using backlight frequency from driver (%dHz)\n",
-                           aux->name, driver_pwm_freq_hz);
-       }
-
-       return 0;
-}
-
-static inline int
-drm_edp_backlight_probe_state(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
-                             u8 *current_mode)
-{
-       int ret;
-       u8 buf[2];
-       u8 mode_reg;
-
-       ret = drm_dp_dpcd_readb(aux, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &mode_reg);
-       if (ret != 1) {
-               drm_dbg_kms(aux->drm_dev, "%s: Failed to read backlight mode: %d\n",
-                           aux->name, ret);
-               return ret < 0 ? ret : -EIO;
-       }
-
-       *current_mode = (mode_reg & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK);
-       if (!bl->aux_set)
-               return 0;
-
-       if (*current_mode == DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD) {
-               int size = 1 + bl->lsb_reg_used;
-
-               ret = drm_dp_dpcd_read(aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB, buf, size);
-               if (ret != size) {
-                       drm_dbg_kms(aux->drm_dev, "%s: Failed to read backlight level: %d\n",
-                                   aux->name, ret);
-                       return ret < 0 ? ret : -EIO;
-               }
-
-               if (bl->lsb_reg_used)
-                       return (buf[0] << 8) | buf[1];
-               else
-                       return buf[0];
-       }
-
-       /*
-        * If we're not in DPCD control mode yet, the programmed brightness value is meaningless and
-        * the driver should assume max brightness
-        */
-       return bl->max;
-}
-
-/**
- * drm_edp_backlight_init() - Probe a display panel's TCON using the standard VESA eDP backlight
- * interface.
- * @aux: The DP aux device to use for probing
- * @bl: The &drm_edp_backlight_info struct to fill out with information on the backlight
- * @driver_pwm_freq_hz: Optional PWM frequency from the driver in hz
- * @edp_dpcd: A cached copy of the eDP DPCD
- * @current_level: Where to store the probed brightness level, if any
- * @current_mode: Where to store the currently set backlight control mode
- *
- * Initializes a &drm_edp_backlight_info struct by probing @aux for it's backlight capabilities,
- * along with also probing the current and maximum supported brightness levels.
- *
- * If @driver_pwm_freq_hz is non-zero, this will be used as the backlight frequency. Otherwise, the
- * default frequency from the panel is used.
- *
- * Returns: %0 on success, negative error code on failure.
- */
-int
-drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
-                      u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE],
-                      u16 *current_level, u8 *current_mode)
-{
-       int ret;
-
-       if (edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP)
-               bl->aux_enable = true;
-       if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)
-               bl->aux_set = true;
-       if (edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
-               bl->lsb_reg_used = true;
-
-       /* Sanity check caps */
-       if (!bl->aux_set && !(edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP)) {
-               drm_dbg_kms(aux->drm_dev,
-                           "%s: Panel supports neither AUX or PWM brightness control? Aborting\n",
-                           aux->name);
-               return -EINVAL;
-       }
-
-       ret = drm_edp_backlight_probe_max(aux, bl, driver_pwm_freq_hz, edp_dpcd);
-       if (ret < 0)
-               return ret;
-
-       ret = drm_edp_backlight_probe_state(aux, bl, current_mode);
-       if (ret < 0)
-               return ret;
-       *current_level = ret;
-
-       drm_dbg_kms(aux->drm_dev,
-                   "%s: Found backlight: aux_set=%d aux_enable=%d mode=%d\n",
-                   aux->name, bl->aux_set, bl->aux_enable, *current_mode);
-       if (bl->aux_set) {
-               drm_dbg_kms(aux->drm_dev,
-                           "%s: Backlight caps: level=%d/%d pwm_freq_pre_divider=%d lsb_reg_used=%d\n",
-                           aux->name, *current_level, bl->max, bl->pwm_freq_pre_divider,
-                           bl->lsb_reg_used);
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_edp_backlight_init);
-
-#if IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
-       (IS_MODULE(CONFIG_DRM_KMS_HELPER) && IS_MODULE(CONFIG_BACKLIGHT_CLASS_DEVICE))
-
-static int dp_aux_backlight_update_status(struct backlight_device *bd)
-{
-       struct dp_aux_backlight *bl = bl_get_data(bd);
-       u16 brightness = backlight_get_brightness(bd);
-       int ret = 0;
-
-       if (!backlight_is_blank(bd)) {
-               if (!bl->enabled) {
-                       drm_edp_backlight_enable(bl->aux, &bl->info, brightness);
-                       bl->enabled = true;
-                       return 0;
-               }
-               ret = drm_edp_backlight_set_level(bl->aux, &bl->info, brightness);
-       } else {
-               if (bl->enabled) {
-                       drm_edp_backlight_disable(bl->aux, &bl->info);
-                       bl->enabled = false;
-               }
-       }
-
-       return ret;
-}
-
-static const struct backlight_ops dp_aux_bl_ops = {
-       .update_status = dp_aux_backlight_update_status,
-};
-
-/**
- * drm_panel_dp_aux_backlight - create and use DP AUX backlight
- * @panel: DRM panel
- * @aux: The DP AUX channel to use
- *
- * Use this function to create and handle backlight if your panel
- * supports backlight control over DP AUX channel using DPCD
- * registers as per VESA's standard backlight control interface.
- *
- * When the panel is enabled backlight will be enabled after a
- * successful call to &drm_panel_funcs.enable()
- *
- * When the panel is disabled backlight will be disabled before the
- * call to &drm_panel_funcs.disable().
- *
- * A typical implementation for a panel driver supporting backlight
- * control over DP AUX will call this function at probe time.
- * Backlight will then be handled transparently without requiring
- * any intervention from the driver.
- *
- * drm_panel_dp_aux_backlight() must be called after the call to drm_panel_init().
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int drm_panel_dp_aux_backlight(struct drm_panel *panel, struct drm_dp_aux *aux)
-{
-       struct dp_aux_backlight *bl;
-       struct backlight_properties props = { 0 };
-       u16 current_level;
-       u8 current_mode;
-       u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE];
-       int ret;
-
-       if (!panel || !panel->dev || !aux)
-               return -EINVAL;
-
-       ret = drm_dp_dpcd_read(aux, DP_EDP_DPCD_REV, edp_dpcd,
-                              EDP_DISPLAY_CTL_CAP_SIZE);
-       if (ret < 0)
-               return ret;
-
-       if (!drm_edp_backlight_supported(edp_dpcd)) {
-               DRM_DEV_INFO(panel->dev, "DP AUX backlight is not supported\n");
-               return 0;
-       }
-
-       bl = devm_kzalloc(panel->dev, sizeof(*bl), GFP_KERNEL);
-       if (!bl)
-               return -ENOMEM;
-
-       bl->aux = aux;
-
-       ret = drm_edp_backlight_init(aux, &bl->info, 0, edp_dpcd,
-                                    &current_level, &current_mode);
-       if (ret < 0)
-               return ret;
-
-       props.type = BACKLIGHT_RAW;
-       props.brightness = current_level;
-       props.max_brightness = bl->info.max;
-
-       bl->base = devm_backlight_device_register(panel->dev, "dp_aux_backlight",
-                                                 panel->dev, bl,
-                                                 &dp_aux_bl_ops, &props);
-       if (IS_ERR(bl->base))
-               return PTR_ERR(bl->base);
-
-       backlight_disable(bl->base);
-
-       panel->backlight = bl->base;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_panel_dp_aux_backlight);
-
-#endif
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
deleted file mode 100644 (file)
index 8b38221..0000000
+++ /dev/null
@@ -1,5978 +0,0 @@
-/*
- * Copyright © 2014 Red Hat
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that copyright
- * notice and this permission notice appear in supporting documentation, and
- * that the name of the copyright holders not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission.  The copyright holders make no representations
- * about the suitability of this software for any purpose.  It is provided "as
- * is" without express or implied warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * 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>
-
-#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
-#include <linux/stacktrace.h>
-#include <linux/sort.h>
-#include <linux/timekeeping.h>
-#include <linux/math64.h>
-#endif
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_dp_mst_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_print.h>
-#include <drm/drm_probe_helper.h>
-
-#include "drm_crtc_helper_internal.h"
-#include "drm_dp_mst_topology_internal.h"
-
-/**
- * DOC: dp mst helper
- *
- * These functions contain parts of the DisplayPort 1.2a MultiStream Transport
- * protocol. The helpers contain a topology manager and bandwidth manager.
- * The helpers encapsulate the sending and received of sideband msgs.
- */
-struct drm_dp_pending_up_req {
-       struct drm_dp_sideband_msg_hdr hdr;
-       struct drm_dp_sideband_msg_req_body msg;
-       struct list_head next;
-};
-
-static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
-                                 char *buf);
-
-static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port);
-
-static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
-                                    int id,
-                                    struct drm_dp_payload *payload);
-
-static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
-                                struct drm_dp_mst_port *port,
-                                int offset, int size, u8 *bytes);
-static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
-                                 struct drm_dp_mst_port *port,
-                                 int offset, int size, u8 *bytes);
-
-static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
-                                   struct drm_dp_mst_branch *mstb);
-
-static void
-drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr,
-                                  struct drm_dp_mst_branch *mstb);
-
-static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
-                                          struct drm_dp_mst_branch *mstb,
-                                          struct drm_dp_mst_port *port);
-static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
-                                u8 *guid);
-
-static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port);
-static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port);
-static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr);
-
-static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port,
-                                                struct drm_dp_mst_branch *branch);
-
-#define DBG_PREFIX "[dp_mst]"
-
-#define DP_STR(x) [DP_ ## x] = #x
-
-static const char *drm_dp_mst_req_type_str(u8 req_type)
-{
-       static const char * const req_type_str[] = {
-               DP_STR(GET_MSG_TRANSACTION_VERSION),
-               DP_STR(LINK_ADDRESS),
-               DP_STR(CONNECTION_STATUS_NOTIFY),
-               DP_STR(ENUM_PATH_RESOURCES),
-               DP_STR(ALLOCATE_PAYLOAD),
-               DP_STR(QUERY_PAYLOAD),
-               DP_STR(RESOURCE_STATUS_NOTIFY),
-               DP_STR(CLEAR_PAYLOAD_ID_TABLE),
-               DP_STR(REMOTE_DPCD_READ),
-               DP_STR(REMOTE_DPCD_WRITE),
-               DP_STR(REMOTE_I2C_READ),
-               DP_STR(REMOTE_I2C_WRITE),
-               DP_STR(POWER_UP_PHY),
-               DP_STR(POWER_DOWN_PHY),
-               DP_STR(SINK_EVENT_NOTIFY),
-               DP_STR(QUERY_STREAM_ENC_STATUS),
-       };
-
-       if (req_type >= ARRAY_SIZE(req_type_str) ||
-           !req_type_str[req_type])
-               return "unknown";
-
-       return req_type_str[req_type];
-}
-
-#undef DP_STR
-#define DP_STR(x) [DP_NAK_ ## x] = #x
-
-static const char *drm_dp_mst_nak_reason_str(u8 nak_reason)
-{
-       static const char * const nak_reason_str[] = {
-               DP_STR(WRITE_FAILURE),
-               DP_STR(INVALID_READ),
-               DP_STR(CRC_FAILURE),
-               DP_STR(BAD_PARAM),
-               DP_STR(DEFER),
-               DP_STR(LINK_FAILURE),
-               DP_STR(NO_RESOURCES),
-               DP_STR(DPCD_FAIL),
-               DP_STR(I2C_NAK),
-               DP_STR(ALLOCATE_FAIL),
-       };
-
-       if (nak_reason >= ARRAY_SIZE(nak_reason_str) ||
-           !nak_reason_str[nak_reason])
-               return "unknown";
-
-       return nak_reason_str[nak_reason];
-}
-
-#undef DP_STR
-#define DP_STR(x) [DRM_DP_SIDEBAND_TX_ ## x] = #x
-
-static const char *drm_dp_mst_sideband_tx_state_str(int state)
-{
-       static const char * const sideband_reason_str[] = {
-               DP_STR(QUEUED),
-               DP_STR(START_SEND),
-               DP_STR(SENT),
-               DP_STR(RX),
-               DP_STR(TIMEOUT),
-       };
-
-       if (state >= ARRAY_SIZE(sideband_reason_str) ||
-           !sideband_reason_str[state])
-               return "unknown";
-
-       return sideband_reason_str[state];
-}
-
-static int
-drm_dp_mst_rad_to_str(const u8 rad[8], u8 lct, char *out, size_t len)
-{
-       int i;
-       u8 unpacked_rad[16];
-
-       for (i = 0; i < lct; i++) {
-               if (i % 2)
-                       unpacked_rad[i] = rad[i / 2] >> 4;
-               else
-                       unpacked_rad[i] = rad[i / 2] & BIT_MASK(4);
-       }
-
-       /* TODO: Eventually add something to printk so we can format the rad
-        * like this: 1.2.3
-        */
-       return snprintf(out, len, "%*phC", lct, unpacked_rad);
-}
-
-/* sideband msg handling */
-static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles)
-{
-       u8 bitmask = 0x80;
-       u8 bitshift = 7;
-       u8 array_index = 0;
-       int number_of_bits = num_nibbles * 4;
-       u8 remainder = 0;
-
-       while (number_of_bits != 0) {
-               number_of_bits--;
-               remainder <<= 1;
-               remainder |= (data[array_index] & bitmask) >> bitshift;
-               bitmask >>= 1;
-               bitshift--;
-               if (bitmask == 0) {
-                       bitmask = 0x80;
-                       bitshift = 7;
-                       array_index++;
-               }
-               if ((remainder & 0x10) == 0x10)
-                       remainder ^= 0x13;
-       }
-
-       number_of_bits = 4;
-       while (number_of_bits != 0) {
-               number_of_bits--;
-               remainder <<= 1;
-               if ((remainder & 0x10) != 0)
-                       remainder ^= 0x13;
-       }
-
-       return remainder;
-}
-
-static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes)
-{
-       u8 bitmask = 0x80;
-       u8 bitshift = 7;
-       u8 array_index = 0;
-       int number_of_bits = number_of_bytes * 8;
-       u16 remainder = 0;
-
-       while (number_of_bits != 0) {
-               number_of_bits--;
-               remainder <<= 1;
-               remainder |= (data[array_index] & bitmask) >> bitshift;
-               bitmask >>= 1;
-               bitshift--;
-               if (bitmask == 0) {
-                       bitmask = 0x80;
-                       bitshift = 7;
-                       array_index++;
-               }
-               if ((remainder & 0x100) == 0x100)
-                       remainder ^= 0xd5;
-       }
-
-       number_of_bits = 8;
-       while (number_of_bits != 0) {
-               number_of_bits--;
-               remainder <<= 1;
-               if ((remainder & 0x100) != 0)
-                       remainder ^= 0xd5;
-       }
-
-       return remainder & 0xff;
-}
-static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr)
-{
-       u8 size = 3;
-
-       size += (hdr->lct / 2);
-       return size;
-}
-
-static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
-                                          u8 *buf, int *len)
-{
-       int idx = 0;
-       int i;
-       u8 crc4;
-
-       buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf);
-       for (i = 0; i < (hdr->lct / 2); i++)
-               buf[idx++] = hdr->rad[i];
-       buf[idx++] = (hdr->broadcast << 7) | (hdr->path_msg << 6) |
-               (hdr->msg_len & 0x3f);
-       buf[idx++] = (hdr->somt << 7) | (hdr->eomt << 6) | (hdr->seqno << 4);
-
-       crc4 = drm_dp_msg_header_crc4(buf, (idx * 2) - 1);
-       buf[idx - 1] |= (crc4 & 0xf);
-
-       *len = idx;
-}
-
-static bool drm_dp_decode_sideband_msg_hdr(const struct drm_dp_mst_topology_mgr *mgr,
-                                          struct drm_dp_sideband_msg_hdr *hdr,
-                                          u8 *buf, int buflen, u8 *hdrlen)
-{
-       u8 crc4;
-       u8 len;
-       int i;
-       u8 idx;
-
-       if (buf[0] == 0)
-               return false;
-       len = 3;
-       len += ((buf[0] & 0xf0) >> 4) / 2;
-       if (len > buflen)
-               return false;
-       crc4 = drm_dp_msg_header_crc4(buf, (len * 2) - 1);
-
-       if ((crc4 & 0xf) != (buf[len - 1] & 0xf)) {
-               drm_dbg_kms(mgr->dev, "crc4 mismatch 0x%x 0x%x\n", crc4, buf[len - 1]);
-               return false;
-       }
-
-       hdr->lct = (buf[0] & 0xf0) >> 4;
-       hdr->lcr = (buf[0] & 0xf);
-       idx = 1;
-       for (i = 0; i < (hdr->lct / 2); i++)
-               hdr->rad[i] = buf[idx++];
-       hdr->broadcast = (buf[idx] >> 7) & 0x1;
-       hdr->path_msg = (buf[idx] >> 6) & 0x1;
-       hdr->msg_len = buf[idx] & 0x3f;
-       idx++;
-       hdr->somt = (buf[idx] >> 7) & 0x1;
-       hdr->eomt = (buf[idx] >> 6) & 0x1;
-       hdr->seqno = (buf[idx] >> 4) & 0x1;
-       idx++;
-       *hdrlen = idx;
-       return true;
-}
-
-void
-drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
-                          struct drm_dp_sideband_msg_tx *raw)
-{
-       int idx = 0;
-       int i;
-       u8 *buf = raw->msg;
-
-       buf[idx++] = req->req_type & 0x7f;
-
-       switch (req->req_type) {
-       case DP_ENUM_PATH_RESOURCES:
-       case DP_POWER_DOWN_PHY:
-       case DP_POWER_UP_PHY:
-               buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
-               idx++;
-               break;
-       case DP_ALLOCATE_PAYLOAD:
-               buf[idx] = (req->u.allocate_payload.port_number & 0xf) << 4 |
-                       (req->u.allocate_payload.number_sdp_streams & 0xf);
-               idx++;
-               buf[idx] = (req->u.allocate_payload.vcpi & 0x7f);
-               idx++;
-               buf[idx] = (req->u.allocate_payload.pbn >> 8);
-               idx++;
-               buf[idx] = (req->u.allocate_payload.pbn & 0xff);
-               idx++;
-               for (i = 0; i < req->u.allocate_payload.number_sdp_streams / 2; i++) {
-                       buf[idx] = ((req->u.allocate_payload.sdp_stream_sink[i * 2] & 0xf) << 4) |
-                               (req->u.allocate_payload.sdp_stream_sink[i * 2 + 1] & 0xf);
-                       idx++;
-               }
-               if (req->u.allocate_payload.number_sdp_streams & 1) {
-                       i = req->u.allocate_payload.number_sdp_streams - 1;
-                       buf[idx] = (req->u.allocate_payload.sdp_stream_sink[i] & 0xf) << 4;
-                       idx++;
-               }
-               break;
-       case DP_QUERY_PAYLOAD:
-               buf[idx] = (req->u.query_payload.port_number & 0xf) << 4;
-               idx++;
-               buf[idx] = (req->u.query_payload.vcpi & 0x7f);
-               idx++;
-               break;
-       case DP_REMOTE_DPCD_READ:
-               buf[idx] = (req->u.dpcd_read.port_number & 0xf) << 4;
-               buf[idx] |= ((req->u.dpcd_read.dpcd_address & 0xf0000) >> 16) & 0xf;
-               idx++;
-               buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff00) >> 8;
-               idx++;
-               buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff);
-               idx++;
-               buf[idx] = (req->u.dpcd_read.num_bytes);
-               idx++;
-               break;
-
-       case DP_REMOTE_DPCD_WRITE:
-               buf[idx] = (req->u.dpcd_write.port_number & 0xf) << 4;
-               buf[idx] |= ((req->u.dpcd_write.dpcd_address & 0xf0000) >> 16) & 0xf;
-               idx++;
-               buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff00) >> 8;
-               idx++;
-               buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff);
-               idx++;
-               buf[idx] = (req->u.dpcd_write.num_bytes);
-               idx++;
-               memcpy(&buf[idx], req->u.dpcd_write.bytes, req->u.dpcd_write.num_bytes);
-               idx += req->u.dpcd_write.num_bytes;
-               break;
-       case DP_REMOTE_I2C_READ:
-               buf[idx] = (req->u.i2c_read.port_number & 0xf) << 4;
-               buf[idx] |= (req->u.i2c_read.num_transactions & 0x3);
-               idx++;
-               for (i = 0; i < (req->u.i2c_read.num_transactions & 0x3); i++) {
-                       buf[idx] = req->u.i2c_read.transactions[i].i2c_dev_id & 0x7f;
-                       idx++;
-                       buf[idx] = req->u.i2c_read.transactions[i].num_bytes;
-                       idx++;
-                       memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes);
-                       idx += req->u.i2c_read.transactions[i].num_bytes;
-
-                       buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 4;
-                       buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf);
-                       idx++;
-               }
-               buf[idx] = (req->u.i2c_read.read_i2c_device_id) & 0x7f;
-               idx++;
-               buf[idx] = (req->u.i2c_read.num_bytes_read);
-               idx++;
-               break;
-
-       case DP_REMOTE_I2C_WRITE:
-               buf[idx] = (req->u.i2c_write.port_number & 0xf) << 4;
-               idx++;
-               buf[idx] = (req->u.i2c_write.write_i2c_device_id) & 0x7f;
-               idx++;
-               buf[idx] = (req->u.i2c_write.num_bytes);
-               idx++;
-               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;
-}
-EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_encode_sideband_req);
-
-/* Decode a sideband request we've encoded, mainly used for debugging */
-int
-drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
-                          struct drm_dp_sideband_msg_req_body *req)
-{
-       const u8 *buf = raw->msg;
-       int i, idx = 0;
-
-       req->req_type = buf[idx++] & 0x7f;
-       switch (req->req_type) {
-       case DP_ENUM_PATH_RESOURCES:
-       case DP_POWER_DOWN_PHY:
-       case DP_POWER_UP_PHY:
-               req->u.port_num.port_number = (buf[idx] >> 4) & 0xf;
-               break;
-       case DP_ALLOCATE_PAYLOAD:
-               {
-                       struct drm_dp_allocate_payload *a =
-                               &req->u.allocate_payload;
-
-                       a->number_sdp_streams = buf[idx] & 0xf;
-                       a->port_number = (buf[idx] >> 4) & 0xf;
-
-                       WARN_ON(buf[++idx] & 0x80);
-                       a->vcpi = buf[idx] & 0x7f;
-
-                       a->pbn = buf[++idx] << 8;
-                       a->pbn |= buf[++idx];
-
-                       idx++;
-                       for (i = 0; i < a->number_sdp_streams; i++) {
-                               a->sdp_stream_sink[i] =
-                                       (buf[idx + (i / 2)] >> ((i % 2) ? 0 : 4)) & 0xf;
-                       }
-               }
-               break;
-       case DP_QUERY_PAYLOAD:
-               req->u.query_payload.port_number = (buf[idx] >> 4) & 0xf;
-               WARN_ON(buf[++idx] & 0x80);
-               req->u.query_payload.vcpi = buf[idx] & 0x7f;
-               break;
-       case DP_REMOTE_DPCD_READ:
-               {
-                       struct drm_dp_remote_dpcd_read *r = &req->u.dpcd_read;
-
-                       r->port_number = (buf[idx] >> 4) & 0xf;
-
-                       r->dpcd_address = (buf[idx] << 16) & 0xf0000;
-                       r->dpcd_address |= (buf[++idx] << 8) & 0xff00;
-                       r->dpcd_address |= buf[++idx] & 0xff;
-
-                       r->num_bytes = buf[++idx];
-               }
-               break;
-       case DP_REMOTE_DPCD_WRITE:
-               {
-                       struct drm_dp_remote_dpcd_write *w =
-                               &req->u.dpcd_write;
-
-                       w->port_number = (buf[idx] >> 4) & 0xf;
-
-                       w->dpcd_address = (buf[idx] << 16) & 0xf0000;
-                       w->dpcd_address |= (buf[++idx] << 8) & 0xff00;
-                       w->dpcd_address |= buf[++idx] & 0xff;
-
-                       w->num_bytes = buf[++idx];
-
-                       w->bytes = kmemdup(&buf[++idx], w->num_bytes,
-                                          GFP_KERNEL);
-                       if (!w->bytes)
-                               return -ENOMEM;
-               }
-               break;
-       case DP_REMOTE_I2C_READ:
-               {
-                       struct drm_dp_remote_i2c_read *r = &req->u.i2c_read;
-                       struct drm_dp_remote_i2c_read_tx *tx;
-                       bool failed = false;
-
-                       r->num_transactions = buf[idx] & 0x3;
-                       r->port_number = (buf[idx] >> 4) & 0xf;
-                       for (i = 0; i < r->num_transactions; i++) {
-                               tx = &r->transactions[i];
-
-                               tx->i2c_dev_id = buf[++idx] & 0x7f;
-                               tx->num_bytes = buf[++idx];
-                               tx->bytes = kmemdup(&buf[++idx],
-                                                   tx->num_bytes,
-                                                   GFP_KERNEL);
-                               if (!tx->bytes) {
-                                       failed = true;
-                                       break;
-                               }
-                               idx += tx->num_bytes;
-                               tx->no_stop_bit = (buf[idx] >> 5) & 0x1;
-                               tx->i2c_transaction_delay = buf[idx] & 0xf;
-                       }
-
-                       if (failed) {
-                               for (i = 0; i < r->num_transactions; i++) {
-                                       tx = &r->transactions[i];
-                                       kfree(tx->bytes);
-                               }
-                               return -ENOMEM;
-                       }
-
-                       r->read_i2c_device_id = buf[++idx] & 0x7f;
-                       r->num_bytes_read = buf[++idx];
-               }
-               break;
-       case DP_REMOTE_I2C_WRITE:
-               {
-                       struct drm_dp_remote_i2c_write *w = &req->u.i2c_write;
-
-                       w->port_number = (buf[idx] >> 4) & 0xf;
-                       w->write_i2c_device_id = buf[++idx] & 0x7f;
-                       w->num_bytes = buf[++idx];
-                       w->bytes = kmemdup(&buf[++idx], w->num_bytes,
-                                          GFP_KERNEL);
-                       if (!w->bytes)
-                               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;
-}
-EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_decode_sideband_req);
-
-void
-drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
-                                 int indent, struct drm_printer *printer)
-{
-       int i;
-
-#define P(f, ...) drm_printf_indent(printer, indent, f, ##__VA_ARGS__)
-       if (req->req_type == DP_LINK_ADDRESS) {
-               /* No contents to print */
-               P("type=%s\n", drm_dp_mst_req_type_str(req->req_type));
-               return;
-       }
-
-       P("type=%s contents:\n", drm_dp_mst_req_type_str(req->req_type));
-       indent++;
-
-       switch (req->req_type) {
-       case DP_ENUM_PATH_RESOURCES:
-       case DP_POWER_DOWN_PHY:
-       case DP_POWER_UP_PHY:
-               P("port=%d\n", req->u.port_num.port_number);
-               break;
-       case DP_ALLOCATE_PAYLOAD:
-               P("port=%d vcpi=%d pbn=%d sdp_streams=%d %*ph\n",
-                 req->u.allocate_payload.port_number,
-                 req->u.allocate_payload.vcpi, req->u.allocate_payload.pbn,
-                 req->u.allocate_payload.number_sdp_streams,
-                 req->u.allocate_payload.number_sdp_streams,
-                 req->u.allocate_payload.sdp_stream_sink);
-               break;
-       case DP_QUERY_PAYLOAD:
-               P("port=%d vcpi=%d\n",
-                 req->u.query_payload.port_number,
-                 req->u.query_payload.vcpi);
-               break;
-       case DP_REMOTE_DPCD_READ:
-               P("port=%d dpcd_addr=%05x len=%d\n",
-                 req->u.dpcd_read.port_number, req->u.dpcd_read.dpcd_address,
-                 req->u.dpcd_read.num_bytes);
-               break;
-       case DP_REMOTE_DPCD_WRITE:
-               P("port=%d addr=%05x len=%d: %*ph\n",
-                 req->u.dpcd_write.port_number,
-                 req->u.dpcd_write.dpcd_address,
-                 req->u.dpcd_write.num_bytes, req->u.dpcd_write.num_bytes,
-                 req->u.dpcd_write.bytes);
-               break;
-       case DP_REMOTE_I2C_READ:
-               P("port=%d num_tx=%d id=%d size=%d:\n",
-                 req->u.i2c_read.port_number,
-                 req->u.i2c_read.num_transactions,
-                 req->u.i2c_read.read_i2c_device_id,
-                 req->u.i2c_read.num_bytes_read);
-
-               indent++;
-               for (i = 0; i < req->u.i2c_read.num_transactions; i++) {
-                       const struct drm_dp_remote_i2c_read_tx *rtx =
-                               &req->u.i2c_read.transactions[i];
-
-                       P("%d: id=%03d size=%03d no_stop_bit=%d tx_delay=%03d: %*ph\n",
-                         i, rtx->i2c_dev_id, rtx->num_bytes,
-                         rtx->no_stop_bit, rtx->i2c_transaction_delay,
-                         rtx->num_bytes, rtx->bytes);
-               }
-               break;
-       case DP_REMOTE_I2C_WRITE:
-               P("port=%d id=%d size=%d: %*ph\n",
-                 req->u.i2c_write.port_number,
-                 req->u.i2c_write.write_i2c_device_id,
-                 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;
-       }
-#undef P
-}
-EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_dump_sideband_msg_req_body);
-
-static inline void
-drm_dp_mst_dump_sideband_msg_tx(struct drm_printer *p,
-                               const struct drm_dp_sideband_msg_tx *txmsg)
-{
-       struct drm_dp_sideband_msg_req_body req;
-       char buf[64];
-       int ret;
-       int i;
-
-       drm_dp_mst_rad_to_str(txmsg->dst->rad, txmsg->dst->lct, buf,
-                             sizeof(buf));
-       drm_printf(p, "txmsg cur_offset=%x cur_len=%x seqno=%x state=%s path_msg=%d dst=%s\n",
-                  txmsg->cur_offset, txmsg->cur_len, txmsg->seqno,
-                  drm_dp_mst_sideband_tx_state_str(txmsg->state),
-                  txmsg->path_msg, buf);
-
-       ret = drm_dp_decode_sideband_req(txmsg, &req);
-       if (ret) {
-               drm_printf(p, "<failed to decode sideband req: %d>\n", ret);
-               return;
-       }
-       drm_dp_dump_sideband_msg_req_body(&req, 1, p);
-
-       switch (req.req_type) {
-       case DP_REMOTE_DPCD_WRITE:
-               kfree(req.u.dpcd_write.bytes);
-               break;
-       case DP_REMOTE_I2C_READ:
-               for (i = 0; i < req.u.i2c_read.num_transactions; i++)
-                       kfree(req.u.i2c_read.transactions[i].bytes);
-               break;
-       case DP_REMOTE_I2C_WRITE:
-               kfree(req.u.i2c_write.bytes);
-               break;
-       }
-}
-
-static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len)
-{
-       u8 crc4;
-
-       crc4 = drm_dp_msg_data_crc4(msg, len);
-       msg[len] = crc4;
-}
-
-static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body *rep,
-                                        struct drm_dp_sideband_msg_tx *raw)
-{
-       int idx = 0;
-       u8 *buf = raw->msg;
-
-       buf[idx++] = (rep->reply_type & 0x1) << 7 | (rep->req_type & 0x7f);
-
-       raw->cur_len = idx;
-}
-
-static int drm_dp_sideband_msg_set_header(struct drm_dp_sideband_msg_rx *msg,
-                                         struct drm_dp_sideband_msg_hdr *hdr,
-                                         u8 hdrlen)
-{
-       /*
-        * ignore out-of-order messages or messages that are part of a
-        * failed transaction
-        */
-       if (!hdr->somt && !msg->have_somt)
-               return false;
-
-       /* get length contained in this portion */
-       msg->curchunk_idx = 0;
-       msg->curchunk_len = hdr->msg_len;
-       msg->curchunk_hdrlen = hdrlen;
-
-       /* we have already gotten an somt - don't bother parsing */
-       if (hdr->somt && msg->have_somt)
-               return false;
-
-       if (hdr->somt) {
-               memcpy(&msg->initial_hdr, hdr,
-                      sizeof(struct drm_dp_sideband_msg_hdr));
-               msg->have_somt = true;
-       }
-       if (hdr->eomt)
-               msg->have_eomt = true;
-
-       return true;
-}
-
-/* this adds a chunk of msg to the builder to get the final msg */
-static bool drm_dp_sideband_append_payload(struct drm_dp_sideband_msg_rx *msg,
-                                          u8 *replybuf, u8 replybuflen)
-{
-       u8 crc4;
-
-       memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen);
-       msg->curchunk_idx += replybuflen;
-
-       if (msg->curchunk_idx >= msg->curchunk_len) {
-               /* do CRC */
-               crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1);
-               if (crc4 != msg->chunk[msg->curchunk_len - 1])
-                       print_hex_dump(KERN_DEBUG, "wrong crc",
-                                      DUMP_PREFIX_NONE, 16, 1,
-                                      msg->chunk,  msg->curchunk_len, false);
-               /* copy chunk into bigger msg */
-               memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1);
-               msg->curlen += msg->curchunk_len - 1;
-       }
-       return true;
-}
-
-static bool drm_dp_sideband_parse_link_address(const struct drm_dp_mst_topology_mgr *mgr,
-                                              struct drm_dp_sideband_msg_rx *raw,
-                                              struct drm_dp_sideband_msg_reply_body *repmsg)
-{
-       int idx = 1;
-       int i;
-
-       memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16);
-       idx += 16;
-       repmsg->u.link_addr.nports = raw->msg[idx] & 0xf;
-       idx++;
-       if (idx > raw->curlen)
-               goto fail_len;
-       for (i = 0; i < repmsg->u.link_addr.nports; i++) {
-               if (raw->msg[idx] & 0x80)
-                       repmsg->u.link_addr.ports[i].input_port = 1;
-
-               repmsg->u.link_addr.ports[i].peer_device_type = (raw->msg[idx] >> 4) & 0x7;
-               repmsg->u.link_addr.ports[i].port_number = (raw->msg[idx] & 0xf);
-
-               idx++;
-               if (idx > raw->curlen)
-                       goto fail_len;
-               repmsg->u.link_addr.ports[i].mcs = (raw->msg[idx] >> 7) & 0x1;
-               repmsg->u.link_addr.ports[i].ddps = (raw->msg[idx] >> 6) & 0x1;
-               if (repmsg->u.link_addr.ports[i].input_port == 0)
-                       repmsg->u.link_addr.ports[i].legacy_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
-               idx++;
-               if (idx > raw->curlen)
-                       goto fail_len;
-               if (repmsg->u.link_addr.ports[i].input_port == 0) {
-                       repmsg->u.link_addr.ports[i].dpcd_revision = (raw->msg[idx]);
-                       idx++;
-                       if (idx > raw->curlen)
-                               goto fail_len;
-                       memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16);
-                       idx += 16;
-                       if (idx > raw->curlen)
-                               goto fail_len;
-                       repmsg->u.link_addr.ports[i].num_sdp_streams = (raw->msg[idx] >> 4) & 0xf;
-                       repmsg->u.link_addr.ports[i].num_sdp_stream_sinks = (raw->msg[idx] & 0xf);
-                       idx++;
-
-               }
-               if (idx > raw->curlen)
-                       goto fail_len;
-       }
-
-       return true;
-fail_len:
-       DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
-       return false;
-}
-
-static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx *raw,
-                                                  struct drm_dp_sideband_msg_reply_body *repmsg)
-{
-       int idx = 1;
-
-       repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf;
-       idx++;
-       if (idx > raw->curlen)
-               goto fail_len;
-       repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx];
-       idx++;
-       if (idx > raw->curlen)
-               goto fail_len;
-
-       memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
-       return true;
-fail_len:
-       DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
-       return false;
-}
-
-static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx *raw,
-                                                     struct drm_dp_sideband_msg_reply_body *repmsg)
-{
-       int idx = 1;
-
-       repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf;
-       idx++;
-       if (idx > raw->curlen)
-               goto fail_len;
-       return true;
-fail_len:
-       DRM_DEBUG_KMS("parse length fail %d %d\n", idx, raw->curlen);
-       return false;
-}
-
-static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx *raw,
-                                                     struct drm_dp_sideband_msg_reply_body *repmsg)
-{
-       int idx = 1;
-
-       repmsg->u.remote_i2c_read_ack.port_number = (raw->msg[idx] & 0xf);
-       idx++;
-       if (idx > raw->curlen)
-               goto fail_len;
-       repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx];
-       idx++;
-       /* TODO check */
-       memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);
-       return true;
-fail_len:
-       DRM_DEBUG_KMS("remote i2c reply parse length fail %d %d\n", idx, raw->curlen);
-       return false;
-}
-
-static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx *raw,
-                                                         struct drm_dp_sideband_msg_reply_body *repmsg)
-{
-       int idx = 1;
-
-       repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
-       repmsg->u.path_resources.fec_capable = raw->msg[idx] & 0x1;
-       idx++;
-       if (idx > raw->curlen)
-               goto fail_len;
-       repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
-       idx += 2;
-       if (idx > raw->curlen)
-               goto fail_len;
-       repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
-       idx += 2;
-       if (idx > raw->curlen)
-               goto fail_len;
-       return true;
-fail_len:
-       DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen);
-       return false;
-}
-
-static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx *raw,
-                                                         struct drm_dp_sideband_msg_reply_body *repmsg)
-{
-       int idx = 1;
-
-       repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
-       idx++;
-       if (idx > raw->curlen)
-               goto fail_len;
-       repmsg->u.allocate_payload.vcpi = raw->msg[idx];
-       idx++;
-       if (idx > raw->curlen)
-               goto fail_len;
-       repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
-       idx += 2;
-       if (idx > raw->curlen)
-               goto fail_len;
-       return true;
-fail_len:
-       DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen);
-       return false;
-}
-
-static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx *raw,
-                                                   struct drm_dp_sideband_msg_reply_body *repmsg)
-{
-       int idx = 1;
-
-       repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
-       idx++;
-       if (idx > raw->curlen)
-               goto fail_len;
-       repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
-       idx += 2;
-       if (idx > raw->curlen)
-               goto fail_len;
-       return true;
-fail_len:
-       DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen);
-       return false;
-}
-
-static bool drm_dp_sideband_parse_power_updown_phy_ack(struct drm_dp_sideband_msg_rx *raw,
-                                                      struct drm_dp_sideband_msg_reply_body *repmsg)
-{
-       int idx = 1;
-
-       repmsg->u.port_number.port_number = (raw->msg[idx] >> 4) & 0xf;
-       idx++;
-       if (idx > raw->curlen) {
-               DRM_DEBUG_KMS("power up/down phy parse length fail %d %d\n",
-                             idx, raw->curlen);
-               return false;
-       }
-       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(const struct drm_dp_mst_topology_mgr *mgr,
-                                       struct drm_dp_sideband_msg_rx *raw,
-                                       struct drm_dp_sideband_msg_reply_body *msg)
-{
-       memset(msg, 0, sizeof(*msg));
-       msg->reply_type = (raw->msg[0] & 0x80) >> 7;
-       msg->req_type = (raw->msg[0] & 0x7f);
-
-       if (msg->reply_type == DP_SIDEBAND_REPLY_NAK) {
-               memcpy(msg->u.nak.guid, &raw->msg[1], 16);
-               msg->u.nak.reason = raw->msg[17];
-               msg->u.nak.nak_data = raw->msg[18];
-               return false;
-       }
-
-       switch (msg->req_type) {
-       case DP_LINK_ADDRESS:
-               return drm_dp_sideband_parse_link_address(mgr, raw, msg);
-       case DP_QUERY_PAYLOAD:
-               return drm_dp_sideband_parse_query_payload_ack(raw, msg);
-       case DP_REMOTE_DPCD_READ:
-               return drm_dp_sideband_parse_remote_dpcd_read(raw, msg);
-       case DP_REMOTE_DPCD_WRITE:
-               return drm_dp_sideband_parse_remote_dpcd_write(raw, msg);
-       case DP_REMOTE_I2C_READ:
-               return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg);
-       case DP_REMOTE_I2C_WRITE:
-               return true; /* since there's nothing to parse */
-       case DP_ENUM_PATH_RESOURCES:
-               return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg);
-       case DP_ALLOCATE_PAYLOAD:
-               return drm_dp_sideband_parse_allocate_payload_ack(raw, msg);
-       case DP_POWER_DOWN_PHY:
-       case DP_POWER_UP_PHY:
-               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_err(mgr->dev, "Got unknown reply 0x%02x (%s)\n",
-                       msg->req_type, drm_dp_mst_req_type_str(msg->req_type));
-               return false;
-       }
-}
-
-static bool
-drm_dp_sideband_parse_connection_status_notify(const struct drm_dp_mst_topology_mgr *mgr,
-                                              struct drm_dp_sideband_msg_rx *raw,
-                                              struct drm_dp_sideband_msg_req_body *msg)
-{
-       int idx = 1;
-
-       msg->u.conn_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
-       idx++;
-       if (idx > raw->curlen)
-               goto fail_len;
-
-       memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16);
-       idx += 16;
-       if (idx > raw->curlen)
-               goto fail_len;
-
-       msg->u.conn_stat.legacy_device_plug_status = (raw->msg[idx] >> 6) & 0x1;
-       msg->u.conn_stat.displayport_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
-       msg->u.conn_stat.message_capability_status = (raw->msg[idx] >> 4) & 0x1;
-       msg->u.conn_stat.input_port = (raw->msg[idx] >> 3) & 0x1;
-       msg->u.conn_stat.peer_device_type = (raw->msg[idx] & 0x7);
-       idx++;
-       return true;
-fail_len:
-       drm_dbg_kms(mgr->dev, "connection status reply parse length fail %d %d\n",
-                   idx, raw->curlen);
-       return false;
-}
-
-static bool drm_dp_sideband_parse_resource_status_notify(const struct drm_dp_mst_topology_mgr *mgr,
-                                                        struct drm_dp_sideband_msg_rx *raw,
-                                                        struct drm_dp_sideband_msg_req_body *msg)
-{
-       int idx = 1;
-
-       msg->u.resource_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
-       idx++;
-       if (idx > raw->curlen)
-               goto fail_len;
-
-       memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16);
-       idx += 16;
-       if (idx > raw->curlen)
-               goto fail_len;
-
-       msg->u.resource_stat.available_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
-       idx++;
-       return true;
-fail_len:
-       drm_dbg_kms(mgr->dev, "resource status reply parse length fail %d %d\n", idx, raw->curlen);
-       return false;
-}
-
-static bool drm_dp_sideband_parse_req(const struct drm_dp_mst_topology_mgr *mgr,
-                                     struct drm_dp_sideband_msg_rx *raw,
-                                     struct drm_dp_sideband_msg_req_body *msg)
-{
-       memset(msg, 0, sizeof(*msg));
-       msg->req_type = (raw->msg[0] & 0x7f);
-
-       switch (msg->req_type) {
-       case DP_CONNECTION_STATUS_NOTIFY:
-               return drm_dp_sideband_parse_connection_status_notify(mgr, raw, msg);
-       case DP_RESOURCE_STATUS_NOTIFY:
-               return drm_dp_sideband_parse_resource_status_notify(mgr, raw, msg);
-       default:
-               drm_err(mgr->dev, "Got unknown request 0x%02x (%s)\n",
-                       msg->req_type, drm_dp_mst_req_type_str(msg->req_type));
-               return false;
-       }
-}
-
-static void build_dpcd_write(struct drm_dp_sideband_msg_tx *msg,
-                            u8 port_num, u32 offset, u8 num_bytes, u8 *bytes)
-{
-       struct drm_dp_sideband_msg_req_body req;
-
-       req.req_type = DP_REMOTE_DPCD_WRITE;
-       req.u.dpcd_write.port_number = port_num;
-       req.u.dpcd_write.dpcd_address = offset;
-       req.u.dpcd_write.num_bytes = num_bytes;
-       req.u.dpcd_write.bytes = bytes;
-       drm_dp_encode_sideband_req(&req, msg);
-}
-
-static void build_link_address(struct drm_dp_sideband_msg_tx *msg)
-{
-       struct drm_dp_sideband_msg_req_body req;
-
-       req.req_type = DP_LINK_ADDRESS;
-       drm_dp_encode_sideband_req(&req, msg);
-}
-
-static void build_clear_payload_id_table(struct drm_dp_sideband_msg_tx *msg)
-{
-       struct drm_dp_sideband_msg_req_body req;
-
-       req.req_type = DP_CLEAR_PAYLOAD_ID_TABLE;
-       drm_dp_encode_sideband_req(&req, msg);
-       msg->path_msg = true;
-}
-
-static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg,
-                                    int port_num)
-{
-       struct drm_dp_sideband_msg_req_body req;
-
-       req.req_type = DP_ENUM_PATH_RESOURCES;
-       req.u.port_num.port_number = port_num;
-       drm_dp_encode_sideband_req(&req, msg);
-       msg->path_msg = true;
-       return 0;
-}
-
-static void build_allocate_payload(struct drm_dp_sideband_msg_tx *msg,
-                                  int port_num,
-                                  u8 vcpi, uint16_t pbn,
-                                  u8 number_sdp_streams,
-                                  u8 *sdp_stream_sink)
-{
-       struct drm_dp_sideband_msg_req_body req;
-
-       memset(&req, 0, sizeof(req));
-       req.req_type = DP_ALLOCATE_PAYLOAD;
-       req.u.allocate_payload.port_number = port_num;
-       req.u.allocate_payload.vcpi = vcpi;
-       req.u.allocate_payload.pbn = pbn;
-       req.u.allocate_payload.number_sdp_streams = number_sdp_streams;
-       memcpy(req.u.allocate_payload.sdp_stream_sink, sdp_stream_sink,
-                  number_sdp_streams);
-       drm_dp_encode_sideband_req(&req, msg);
-       msg->path_msg = true;
-}
-
-static void build_power_updown_phy(struct drm_dp_sideband_msg_tx *msg,
-                                  int port_num, bool power_up)
-{
-       struct drm_dp_sideband_msg_req_body req;
-
-       if (power_up)
-               req.req_type = DP_POWER_UP_PHY;
-       else
-               req.req_type = DP_POWER_DOWN_PHY;
-
-       req.u.port_num.port_number = port_num;
-       drm_dp_encode_sideband_req(&req, 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)
-{
-       int ret, vcpi_ret;
-
-       mutex_lock(&mgr->payload_lock);
-       ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1);
-       if (ret > mgr->max_payloads) {
-               ret = -EINVAL;
-               drm_dbg_kms(mgr->dev, "out of payload ids %d\n", ret);
-               goto out_unlock;
-       }
-
-       vcpi_ret = find_first_zero_bit(&mgr->vcpi_mask, mgr->max_payloads + 1);
-       if (vcpi_ret > mgr->max_payloads) {
-               ret = -EINVAL;
-               drm_dbg_kms(mgr->dev, "out of vcpi ids %d\n", ret);
-               goto out_unlock;
-       }
-
-       set_bit(ret, &mgr->payload_mask);
-       set_bit(vcpi_ret, &mgr->vcpi_mask);
-       vcpi->vcpi = vcpi_ret + 1;
-       mgr->proposed_vcpis[ret - 1] = vcpi;
-out_unlock:
-       mutex_unlock(&mgr->payload_lock);
-       return ret;
-}
-
-static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
-                                     int vcpi)
-{
-       int i;
-
-       if (vcpi == 0)
-               return;
-
-       mutex_lock(&mgr->payload_lock);
-       drm_dbg_kms(mgr->dev, "putting payload %d\n", vcpi);
-       clear_bit(vcpi - 1, &mgr->vcpi_mask);
-
-       for (i = 0; i < mgr->max_payloads; i++) {
-               if (mgr->proposed_vcpis[i] &&
-                   mgr->proposed_vcpis[i]->vcpi == vcpi) {
-                       mgr->proposed_vcpis[i] = NULL;
-                       clear_bit(i + 1, &mgr->payload_mask);
-               }
-       }
-       mutex_unlock(&mgr->payload_lock);
-}
-
-static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
-                             struct drm_dp_sideband_msg_tx *txmsg)
-{
-       unsigned int state;
-
-       /*
-        * All updates to txmsg->state are protected by mgr->qlock, and the two
-        * cases we check here are terminal states. For those the barriers
-        * provided by the wake_up/wait_event pair are enough.
-        */
-       state = READ_ONCE(txmsg->state);
-       return (state == DRM_DP_SIDEBAND_TX_RX ||
-               state == DRM_DP_SIDEBAND_TX_TIMEOUT);
-}
-
-static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
-                                   struct drm_dp_sideband_msg_tx *txmsg)
-{
-       struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
-       unsigned long wait_timeout = msecs_to_jiffies(4000);
-       unsigned long wait_expires = jiffies + wait_timeout;
-       int ret;
-
-       for (;;) {
-               /*
-                * If the driver provides a way for this, change to
-                * poll-waiting for the MST reply interrupt if we didn't receive
-                * it for 50 msec. This would cater for cases where the HPD
-                * pulse signal got lost somewhere, even though the sink raised
-                * the corresponding MST interrupt correctly. One example is the
-                * Club 3D CAC-1557 TypeC -> DP adapter which for some reason
-                * filters out short pulses with a duration less than ~540 usec.
-                *
-                * The poll period is 50 msec to avoid missing an interrupt
-                * after the sink has cleared it (after a 110msec timeout
-                * since it raised the interrupt).
-                */
-               ret = wait_event_timeout(mgr->tx_waitq,
-                                        check_txmsg_state(mgr, txmsg),
-                                        mgr->cbs->poll_hpd_irq ?
-                                               msecs_to_jiffies(50) :
-                                               wait_timeout);
-
-               if (ret || !mgr->cbs->poll_hpd_irq ||
-                   time_after(jiffies, wait_expires))
-                       break;
-
-               mgr->cbs->poll_hpd_irq(mgr);
-       }
-
-       mutex_lock(&mgr->qlock);
-       if (ret > 0) {
-               if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
-                       ret = -EIO;
-                       goto out;
-               }
-       } else {
-               drm_dbg_kms(mgr->dev, "timedout msg send %p %d %d\n",
-                           txmsg, txmsg->state, txmsg->seqno);
-
-               /* dump some state */
-               ret = -EIO;
-
-               /* remove from q */
-               if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED ||
-                   txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND ||
-                   txmsg->state == DRM_DP_SIDEBAND_TX_SENT)
-                       list_del(&txmsg->next);
-       }
-out:
-       if (unlikely(ret == -EIO) && drm_debug_enabled(DRM_UT_DP)) {
-               struct drm_printer p = drm_debug_printer(DBG_PREFIX);
-
-               drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
-       }
-       mutex_unlock(&mgr->qlock);
-
-       drm_dp_mst_kick_tx(mgr);
-       return ret;
-}
-
-static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad)
-{
-       struct drm_dp_mst_branch *mstb;
-
-       mstb = kzalloc(sizeof(*mstb), GFP_KERNEL);
-       if (!mstb)
-               return NULL;
-
-       mstb->lct = lct;
-       if (lct > 1)
-               memcpy(mstb->rad, rad, lct / 2);
-       INIT_LIST_HEAD(&mstb->ports);
-       kref_init(&mstb->topology_kref);
-       kref_init(&mstb->malloc_kref);
-       return mstb;
-}
-
-static void drm_dp_free_mst_branch_device(struct kref *kref)
-{
-       struct drm_dp_mst_branch *mstb =
-               container_of(kref, struct drm_dp_mst_branch, malloc_kref);
-
-       if (mstb->port_parent)
-               drm_dp_mst_put_port_malloc(mstb->port_parent);
-
-       kfree(mstb);
-}
-
-/**
- * DOC: Branch device and port refcounting
- *
- * Topology refcount overview
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * The refcounting schemes for &struct drm_dp_mst_branch and &struct
- * drm_dp_mst_port are somewhat unusual. Both ports and branch devices have
- * two different kinds of refcounts: topology refcounts, and malloc refcounts.
- *
- * Topology refcounts are not exposed to drivers, and are handled internally
- * by the DP MST helpers. The helpers use them in order to prevent the
- * in-memory topology state from being changed in the middle of critical
- * operations like changing the internal state of payload allocations. This
- * means each branch and port will be considered to be connected to the rest
- * of the topology until its topology refcount reaches zero. Additionally,
- * for ports this means that their associated &struct drm_connector will stay
- * registered with userspace until the port's refcount reaches 0.
- *
- * Malloc refcount overview
- * ~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * Malloc references are used to keep a &struct drm_dp_mst_port or &struct
- * drm_dp_mst_branch allocated even after all of its topology references have
- * been dropped, so that the driver or MST helpers can safely access each
- * branch's last known state before it was disconnected from the topology.
- * When the malloc refcount of a port or branch reaches 0, the memory
- * allocation containing the &struct drm_dp_mst_branch or &struct
- * drm_dp_mst_port respectively will be freed.
- *
- * For &struct drm_dp_mst_branch, malloc refcounts are not currently exposed
- * to drivers. As of writing this documentation, there are no drivers that
- * have a usecase for accessing &struct drm_dp_mst_branch outside of the MST
- * helpers. Exposing this API to drivers in a race-free manner would take more
- * tweaking of the refcounting scheme, however patches are welcome provided
- * there is a legitimate driver usecase for this.
- *
- * Refcount relationships in a topology
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * Let's take a look at why the relationship between topology and malloc
- * refcounts is designed the way it is.
- *
- * .. kernel-figure:: dp-mst/topology-figure-1.dot
- *
- *    An example of topology and malloc refs in a DP MST topology with two
- *    active payloads. Topology refcount increments are indicated by solid
- *    lines, and malloc refcount increments are indicated by dashed lines.
- *    Each starts from the branch which incremented the refcount, and ends at
- *    the branch to which the refcount belongs to, i.e. the arrow points the
- *    same way as the C pointers used to reference a structure.
- *
- * As you can see in the above figure, every branch increments the topology
- * refcount of its children, and increments the malloc refcount of its
- * parent. Additionally, every payload increments the malloc refcount of its
- * assigned port by 1.
- *
- * So, what would happen if MSTB #3 from the above figure was unplugged from
- * the system, but the driver hadn't yet removed payload #2 from port #3? The
- * topology would start to look like the figure below.
- *
- * .. kernel-figure:: dp-mst/topology-figure-2.dot
- *
- *    Ports and branch devices which have been released from memory are
- *    colored grey, and references which have been removed are colored red.
- *
- * Whenever a port or branch device's topology refcount reaches zero, it will
- * decrement the topology refcounts of all its children, the malloc refcount
- * of its parent, and finally its own malloc refcount. For MSTB #4 and port
- * #4, this means they both have been disconnected from the topology and freed
- * from memory. But, because payload #2 is still holding a reference to port
- * #3, port #3 is removed from the topology but its &struct drm_dp_mst_port
- * is still accessible from memory. This also means port #3 has not yet
- * decremented the malloc refcount of MSTB #3, so its &struct
- * drm_dp_mst_branch will also stay allocated in memory until port #3's
- * malloc refcount reaches 0.
- *
- * This relationship is necessary because in order to release payload #2, we
- * need to be able to figure out the last relative of port #3 that's still
- * connected to the topology. In this case, we would travel up the topology as
- * shown below.
- *
- * .. kernel-figure:: dp-mst/topology-figure-3.dot
- *
- * And finally, remove payload #2 by communicating with port #2 through
- * sideband transactions.
- */
-
-/**
- * drm_dp_mst_get_mstb_malloc() - Increment the malloc refcount of a branch
- * device
- * @mstb: The &struct drm_dp_mst_branch to increment the malloc refcount of
- *
- * Increments &drm_dp_mst_branch.malloc_kref. When
- * &drm_dp_mst_branch.malloc_kref reaches 0, the memory allocation for @mstb
- * will be released and @mstb may no longer be used.
- *
- * See also: drm_dp_mst_put_mstb_malloc()
- */
-static void
-drm_dp_mst_get_mstb_malloc(struct drm_dp_mst_branch *mstb)
-{
-       kref_get(&mstb->malloc_kref);
-       drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref));
-}
-
-/**
- * drm_dp_mst_put_mstb_malloc() - Decrement the malloc refcount of a branch
- * device
- * @mstb: The &struct drm_dp_mst_branch to decrement the malloc refcount of
- *
- * Decrements &drm_dp_mst_branch.malloc_kref. When
- * &drm_dp_mst_branch.malloc_kref reaches 0, the memory allocation for @mstb
- * will be released and @mstb may no longer be used.
- *
- * See also: drm_dp_mst_get_mstb_malloc()
- */
-static void
-drm_dp_mst_put_mstb_malloc(struct drm_dp_mst_branch *mstb)
-{
-       drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref) - 1);
-       kref_put(&mstb->malloc_kref, drm_dp_free_mst_branch_device);
-}
-
-static void drm_dp_free_mst_port(struct kref *kref)
-{
-       struct drm_dp_mst_port *port =
-               container_of(kref, struct drm_dp_mst_port, malloc_kref);
-
-       drm_dp_mst_put_mstb_malloc(port->parent);
-       kfree(port);
-}
-
-/**
- * drm_dp_mst_get_port_malloc() - Increment the malloc refcount of an MST port
- * @port: The &struct drm_dp_mst_port to increment the malloc refcount of
- *
- * Increments &drm_dp_mst_port.malloc_kref. When &drm_dp_mst_port.malloc_kref
- * reaches 0, the memory allocation for @port will be released and @port may
- * no longer be used.
- *
- * Because @port could potentially be freed at any time by the DP MST helpers
- * if &drm_dp_mst_port.malloc_kref reaches 0, including during a call to this
- * function, drivers that which to make use of &struct drm_dp_mst_port should
- * ensure that they grab at least one main malloc reference to their MST ports
- * in &drm_dp_mst_topology_cbs.add_connector. This callback is called before
- * there is any chance for &drm_dp_mst_port.malloc_kref to reach 0.
- *
- * See also: drm_dp_mst_put_port_malloc()
- */
-void
-drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port)
-{
-       kref_get(&port->malloc_kref);
-       drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->malloc_kref));
-}
-EXPORT_SYMBOL(drm_dp_mst_get_port_malloc);
-
-/**
- * drm_dp_mst_put_port_malloc() - Decrement the malloc refcount of an MST port
- * @port: The &struct drm_dp_mst_port to decrement the malloc refcount of
- *
- * Decrements &drm_dp_mst_port.malloc_kref. When &drm_dp_mst_port.malloc_kref
- * reaches 0, the memory allocation for @port will be released and @port may
- * no longer be used.
- *
- * See also: drm_dp_mst_get_port_malloc()
- */
-void
-drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port)
-{
-       drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->malloc_kref) - 1);
-       kref_put(&port->malloc_kref, drm_dp_free_mst_port);
-}
-EXPORT_SYMBOL(drm_dp_mst_put_port_malloc);
-
-#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
-
-#define STACK_DEPTH 8
-
-static noinline void
-__topology_ref_save(struct drm_dp_mst_topology_mgr *mgr,
-                   struct drm_dp_mst_topology_ref_history *history,
-                   enum drm_dp_mst_topology_ref_type type)
-{
-       struct drm_dp_mst_topology_ref_entry *entry = NULL;
-       depot_stack_handle_t backtrace;
-       ulong stack_entries[STACK_DEPTH];
-       uint n;
-       int i;
-
-       n = stack_trace_save(stack_entries, ARRAY_SIZE(stack_entries), 1);
-       backtrace = stack_depot_save(stack_entries, n, GFP_KERNEL);
-       if (!backtrace)
-               return;
-
-       /* Try to find an existing entry for this backtrace */
-       for (i = 0; i < history->len; i++) {
-               if (history->entries[i].backtrace == backtrace) {
-                       entry = &history->entries[i];
-                       break;
-               }
-       }
-
-       /* Otherwise add one */
-       if (!entry) {
-               struct drm_dp_mst_topology_ref_entry *new;
-               int new_len = history->len + 1;
-
-               new = krealloc(history->entries, sizeof(*new) * new_len,
-                              GFP_KERNEL);
-               if (!new)
-                       return;
-
-               entry = &new[history->len];
-               history->len = new_len;
-               history->entries = new;
-
-               entry->backtrace = backtrace;
-               entry->type = type;
-               entry->count = 0;
-       }
-       entry->count++;
-       entry->ts_nsec = ktime_get_ns();
-}
-
-static int
-topology_ref_history_cmp(const void *a, const void *b)
-{
-       const struct drm_dp_mst_topology_ref_entry *entry_a = a, *entry_b = b;
-
-       if (entry_a->ts_nsec > entry_b->ts_nsec)
-               return 1;
-       else if (entry_a->ts_nsec < entry_b->ts_nsec)
-               return -1;
-       else
-               return 0;
-}
-
-static inline const char *
-topology_ref_type_to_str(enum drm_dp_mst_topology_ref_type type)
-{
-       if (type == DRM_DP_MST_TOPOLOGY_REF_GET)
-               return "get";
-       else
-               return "put";
-}
-
-static void
-__dump_topology_ref_history(struct drm_dp_mst_topology_ref_history *history,
-                           void *ptr, const char *type_str)
-{
-       struct drm_printer p = drm_debug_printer(DBG_PREFIX);
-       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
-       int i;
-
-       if (!buf)
-               return;
-
-       if (!history->len)
-               goto out;
-
-       /* First, sort the list so that it goes from oldest to newest
-        * reference entry
-        */
-       sort(history->entries, history->len, sizeof(*history->entries),
-            topology_ref_history_cmp, NULL);
-
-       drm_printf(&p, "%s (%p) topology count reached 0, dumping history:\n",
-                  type_str, ptr);
-
-       for (i = 0; i < history->len; i++) {
-               const struct drm_dp_mst_topology_ref_entry *entry =
-                       &history->entries[i];
-               u64 ts_nsec = entry->ts_nsec;
-               u32 rem_nsec = do_div(ts_nsec, 1000000000);
-
-               stack_depot_snprint(entry->backtrace, buf, PAGE_SIZE, 4);
-
-               drm_printf(&p, "  %d %ss (last at %5llu.%06u):\n%s",
-                          entry->count,
-                          topology_ref_type_to_str(entry->type),
-                          ts_nsec, rem_nsec / 1000, buf);
-       }
-
-       /* Now free the history, since this is the only time we expose it */
-       kfree(history->entries);
-out:
-       kfree(buf);
-}
-
-static __always_inline void
-drm_dp_mst_dump_mstb_topology_history(struct drm_dp_mst_branch *mstb)
-{
-       __dump_topology_ref_history(&mstb->topology_ref_history, mstb,
-                                   "MSTB");
-}
-
-static __always_inline void
-drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port)
-{
-       __dump_topology_ref_history(&port->topology_ref_history, port,
-                                   "Port");
-}
-
-static __always_inline void
-save_mstb_topology_ref(struct drm_dp_mst_branch *mstb,
-                      enum drm_dp_mst_topology_ref_type type)
-{
-       __topology_ref_save(mstb->mgr, &mstb->topology_ref_history, type);
-}
-
-static __always_inline void
-save_port_topology_ref(struct drm_dp_mst_port *port,
-                      enum drm_dp_mst_topology_ref_type type)
-{
-       __topology_ref_save(port->mgr, &port->topology_ref_history, type);
-}
-
-static inline void
-topology_ref_history_lock(struct drm_dp_mst_topology_mgr *mgr)
-{
-       mutex_lock(&mgr->topology_ref_history_lock);
-}
-
-static inline void
-topology_ref_history_unlock(struct drm_dp_mst_topology_mgr *mgr)
-{
-       mutex_unlock(&mgr->topology_ref_history_lock);
-}
-#else
-static inline void
-topology_ref_history_lock(struct drm_dp_mst_topology_mgr *mgr) {}
-static inline void
-topology_ref_history_unlock(struct drm_dp_mst_topology_mgr *mgr) {}
-static inline void
-drm_dp_mst_dump_mstb_topology_history(struct drm_dp_mst_branch *mstb) {}
-static inline void
-drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port) {}
-#define save_mstb_topology_ref(mstb, type)
-#define save_port_topology_ref(port, type)
-#endif
-
-static void drm_dp_destroy_mst_branch_device(struct kref *kref)
-{
-       struct drm_dp_mst_branch *mstb =
-               container_of(kref, struct drm_dp_mst_branch, topology_kref);
-       struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
-
-       drm_dp_mst_dump_mstb_topology_history(mstb);
-
-       INIT_LIST_HEAD(&mstb->destroy_next);
-
-       /*
-        * This can get called under mgr->mutex, so we need to perform the
-        * actual destruction of the mstb in another worker
-        */
-       mutex_lock(&mgr->delayed_destroy_lock);
-       list_add(&mstb->destroy_next, &mgr->destroy_branch_device_list);
-       mutex_unlock(&mgr->delayed_destroy_lock);
-       queue_work(mgr->delayed_destroy_wq, &mgr->delayed_destroy_work);
-}
-
-/**
- * drm_dp_mst_topology_try_get_mstb() - Increment the topology refcount of a
- * branch device unless it's zero
- * @mstb: &struct drm_dp_mst_branch to increment the topology refcount of
- *
- * Attempts to grab a topology reference to @mstb, if it hasn't yet been
- * removed from the topology (e.g. &drm_dp_mst_branch.topology_kref has
- * reached 0). Holding a topology reference implies that a malloc reference
- * will be held to @mstb as long as the user holds the topology reference.
- *
- * Care should be taken to ensure that the user has at least one malloc
- * reference to @mstb. If you already have a topology reference to @mstb, you
- * should use drm_dp_mst_topology_get_mstb() instead.
- *
- * See also:
- * drm_dp_mst_topology_get_mstb()
- * drm_dp_mst_topology_put_mstb()
- *
- * Returns:
- * * 1: A topology reference was grabbed successfully
- * * 0: @port is no longer in the topology, no reference was grabbed
- */
-static int __must_check
-drm_dp_mst_topology_try_get_mstb(struct drm_dp_mst_branch *mstb)
-{
-       int ret;
-
-       topology_ref_history_lock(mstb->mgr);
-       ret = kref_get_unless_zero(&mstb->topology_kref);
-       if (ret) {
-               drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref));
-               save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_GET);
-       }
-
-       topology_ref_history_unlock(mstb->mgr);
-
-       return ret;
-}
-
-/**
- * drm_dp_mst_topology_get_mstb() - Increment the topology refcount of a
- * branch device
- * @mstb: The &struct drm_dp_mst_branch to increment the topology refcount of
- *
- * Increments &drm_dp_mst_branch.topology_refcount without checking whether or
- * not it's already reached 0. This is only valid to use in scenarios where
- * you are already guaranteed to have at least one active topology reference
- * to @mstb. Otherwise, drm_dp_mst_topology_try_get_mstb() must be used.
- *
- * See also:
- * drm_dp_mst_topology_try_get_mstb()
- * drm_dp_mst_topology_put_mstb()
- */
-static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb)
-{
-       topology_ref_history_lock(mstb->mgr);
-
-       save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_GET);
-       WARN_ON(kref_read(&mstb->topology_kref) == 0);
-       kref_get(&mstb->topology_kref);
-       drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref));
-
-       topology_ref_history_unlock(mstb->mgr);
-}
-
-/**
- * drm_dp_mst_topology_put_mstb() - release a topology reference to a branch
- * device
- * @mstb: The &struct drm_dp_mst_branch to release the topology reference from
- *
- * Releases a topology reference from @mstb by decrementing
- * &drm_dp_mst_branch.topology_kref.
- *
- * See also:
- * drm_dp_mst_topology_try_get_mstb()
- * drm_dp_mst_topology_get_mstb()
- */
-static void
-drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb)
-{
-       topology_ref_history_lock(mstb->mgr);
-
-       drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref) - 1);
-       save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_PUT);
-
-       topology_ref_history_unlock(mstb->mgr);
-       kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
-}
-
-static void drm_dp_destroy_port(struct kref *kref)
-{
-       struct drm_dp_mst_port *port =
-               container_of(kref, struct drm_dp_mst_port, topology_kref);
-       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
-
-       drm_dp_mst_dump_port_topology_history(port);
-
-       /* There's nothing that needs locking to destroy an input port yet */
-       if (port->input) {
-               drm_dp_mst_put_port_malloc(port);
-               return;
-       }
-
-       kfree(port->cached_edid);
-
-       /*
-        * we can't destroy the connector here, as we might be holding the
-        * mode_config.mutex from an EDID retrieval
-        */
-       mutex_lock(&mgr->delayed_destroy_lock);
-       list_add(&port->next, &mgr->destroy_port_list);
-       mutex_unlock(&mgr->delayed_destroy_lock);
-       queue_work(mgr->delayed_destroy_wq, &mgr->delayed_destroy_work);
-}
-
-/**
- * drm_dp_mst_topology_try_get_port() - Increment the topology refcount of a
- * port unless it's zero
- * @port: &struct drm_dp_mst_port to increment the topology refcount of
- *
- * Attempts to grab a topology reference to @port, if it hasn't yet been
- * removed from the topology (e.g. &drm_dp_mst_port.topology_kref has reached
- * 0). Holding a topology reference implies that a malloc reference will be
- * held to @port as long as the user holds the topology reference.
- *
- * Care should be taken to ensure that the user has at least one malloc
- * reference to @port. If you already have a topology reference to @port, you
- * should use drm_dp_mst_topology_get_port() instead.
- *
- * See also:
- * drm_dp_mst_topology_get_port()
- * drm_dp_mst_topology_put_port()
- *
- * Returns:
- * * 1: A topology reference was grabbed successfully
- * * 0: @port is no longer in the topology, no reference was grabbed
- */
-static int __must_check
-drm_dp_mst_topology_try_get_port(struct drm_dp_mst_port *port)
-{
-       int ret;
-
-       topology_ref_history_lock(port->mgr);
-       ret = kref_get_unless_zero(&port->topology_kref);
-       if (ret) {
-               drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->topology_kref));
-               save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_GET);
-       }
-
-       topology_ref_history_unlock(port->mgr);
-       return ret;
-}
-
-/**
- * drm_dp_mst_topology_get_port() - Increment the topology refcount of a port
- * @port: The &struct drm_dp_mst_port to increment the topology refcount of
- *
- * Increments &drm_dp_mst_port.topology_refcount without checking whether or
- * not it's already reached 0. This is only valid to use in scenarios where
- * you are already guaranteed to have at least one active topology reference
- * to @port. Otherwise, drm_dp_mst_topology_try_get_port() must be used.
- *
- * See also:
- * drm_dp_mst_topology_try_get_port()
- * drm_dp_mst_topology_put_port()
- */
-static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port)
-{
-       topology_ref_history_lock(port->mgr);
-
-       WARN_ON(kref_read(&port->topology_kref) == 0);
-       kref_get(&port->topology_kref);
-       drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->topology_kref));
-       save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_GET);
-
-       topology_ref_history_unlock(port->mgr);
-}
-
-/**
- * drm_dp_mst_topology_put_port() - release a topology reference to a port
- * @port: The &struct drm_dp_mst_port to release the topology reference from
- *
- * Releases a topology reference from @port by decrementing
- * &drm_dp_mst_port.topology_kref.
- *
- * See also:
- * drm_dp_mst_topology_try_get_port()
- * drm_dp_mst_topology_get_port()
- */
-static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port)
-{
-       topology_ref_history_lock(port->mgr);
-
-       drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->topology_kref) - 1);
-       save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_PUT);
-
-       topology_ref_history_unlock(port->mgr);
-       kref_put(&port->topology_kref, drm_dp_destroy_port);
-}
-
-static struct drm_dp_mst_branch *
-drm_dp_mst_topology_get_mstb_validated_locked(struct drm_dp_mst_branch *mstb,
-                                             struct drm_dp_mst_branch *to_find)
-{
-       struct drm_dp_mst_port *port;
-       struct drm_dp_mst_branch *rmstb;
-
-       if (to_find == mstb)
-               return mstb;
-
-       list_for_each_entry(port, &mstb->ports, next) {
-               if (port->mstb) {
-                       rmstb = drm_dp_mst_topology_get_mstb_validated_locked(
-                           port->mstb, to_find);
-                       if (rmstb)
-                               return rmstb;
-               }
-       }
-       return NULL;
-}
-
-static struct drm_dp_mst_branch *
-drm_dp_mst_topology_get_mstb_validated(struct drm_dp_mst_topology_mgr *mgr,
-                                      struct drm_dp_mst_branch *mstb)
-{
-       struct drm_dp_mst_branch *rmstb = NULL;
-
-       mutex_lock(&mgr->lock);
-       if (mgr->mst_primary) {
-               rmstb = drm_dp_mst_topology_get_mstb_validated_locked(
-                   mgr->mst_primary, mstb);
-
-               if (rmstb && !drm_dp_mst_topology_try_get_mstb(rmstb))
-                       rmstb = NULL;
-       }
-       mutex_unlock(&mgr->lock);
-       return rmstb;
-}
-
-static struct drm_dp_mst_port *
-drm_dp_mst_topology_get_port_validated_locked(struct drm_dp_mst_branch *mstb,
-                                             struct drm_dp_mst_port *to_find)
-{
-       struct drm_dp_mst_port *port, *mport;
-
-       list_for_each_entry(port, &mstb->ports, next) {
-               if (port == to_find)
-                       return port;
-
-               if (port->mstb) {
-                       mport = drm_dp_mst_topology_get_port_validated_locked(
-                           port->mstb, to_find);
-                       if (mport)
-                               return mport;
-               }
-       }
-       return NULL;
-}
-
-static struct drm_dp_mst_port *
-drm_dp_mst_topology_get_port_validated(struct drm_dp_mst_topology_mgr *mgr,
-                                      struct drm_dp_mst_port *port)
-{
-       struct drm_dp_mst_port *rport = NULL;
-
-       mutex_lock(&mgr->lock);
-       if (mgr->mst_primary) {
-               rport = drm_dp_mst_topology_get_port_validated_locked(
-                   mgr->mst_primary, port);
-
-               if (rport && !drm_dp_mst_topology_try_get_port(rport))
-                       rport = NULL;
-       }
-       mutex_unlock(&mgr->lock);
-       return rport;
-}
-
-static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num)
-{
-       struct drm_dp_mst_port *port;
-       int ret;
-
-       list_for_each_entry(port, &mstb->ports, next) {
-               if (port->port_num == port_num) {
-                       ret = drm_dp_mst_topology_try_get_port(port);
-                       return ret ? port : NULL;
-               }
-       }
-
-       return NULL;
-}
-
-/*
- * calculate a new RAD for this MST branch device
- * if parent has an LCT of 2 then it has 1 nibble of RAD,
- * if parent has an LCT of 3 then it has 2 nibbles of RAD,
- */
-static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
-                                u8 *rad)
-{
-       int parent_lct = port->parent->lct;
-       int shift = 4;
-       int idx = (parent_lct - 1) / 2;
-
-       if (parent_lct > 1) {
-               memcpy(rad, port->parent->rad, idx + 1);
-               shift = (parent_lct % 2) ? 4 : 0;
-       } else
-               rad[0] = 0;
-
-       rad[idx] |= port->port_num << shift;
-       return parent_lct + 1;
-}
-
-static bool drm_dp_mst_is_end_device(u8 pdt, bool mcs)
-{
-       switch (pdt) {
-       case DP_PEER_DEVICE_DP_LEGACY_CONV:
-       case DP_PEER_DEVICE_SST_SINK:
-               return true;
-       case DP_PEER_DEVICE_MST_BRANCHING:
-               /* For sst branch device */
-               if (!mcs)
-                       return true;
-
-               return false;
-       }
-       return true;
-}
-
-static int
-drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt,
-                   bool new_mcs)
-{
-       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
-       struct drm_dp_mst_branch *mstb;
-       u8 rad[8], lct;
-       int ret = 0;
-
-       if (port->pdt == new_pdt && port->mcs == new_mcs)
-               return 0;
-
-       /* Teardown the old pdt, if there is one */
-       if (port->pdt != DP_PEER_DEVICE_NONE) {
-               if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
-                       /*
-                        * If the new PDT would also have an i2c bus,
-                        * don't bother with reregistering it
-                        */
-                       if (new_pdt != DP_PEER_DEVICE_NONE &&
-                           drm_dp_mst_is_end_device(new_pdt, new_mcs)) {
-                               port->pdt = new_pdt;
-                               port->mcs = new_mcs;
-                               return 0;
-                       }
-
-                       /* remove i2c over sideband */
-                       drm_dp_mst_unregister_i2c_bus(port);
-               } else {
-                       mutex_lock(&mgr->lock);
-                       drm_dp_mst_topology_put_mstb(port->mstb);
-                       port->mstb = NULL;
-                       mutex_unlock(&mgr->lock);
-               }
-       }
-
-       port->pdt = new_pdt;
-       port->mcs = new_mcs;
-
-       if (port->pdt != DP_PEER_DEVICE_NONE) {
-               if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
-                       /* add i2c over sideband */
-                       ret = drm_dp_mst_register_i2c_bus(port);
-               } else {
-                       lct = drm_dp_calculate_rad(port, rad);
-                       mstb = drm_dp_add_mst_branch_device(lct, rad);
-                       if (!mstb) {
-                               ret = -ENOMEM;
-                               drm_err(mgr->dev, "Failed to create MSTB for port %p", port);
-                               goto out;
-                       }
-
-                       mutex_lock(&mgr->lock);
-                       port->mstb = mstb;
-                       mstb->mgr = port->mgr;
-                       mstb->port_parent = port;
-
-                       /*
-                        * Make sure this port's memory allocation stays
-                        * around until its child MSTB releases it
-                        */
-                       drm_dp_mst_get_port_malloc(port);
-                       mutex_unlock(&mgr->lock);
-
-                       /* And make sure we send a link address for this */
-                       ret = 1;
-               }
-       }
-
-out:
-       if (ret < 0)
-               port->pdt = DP_PEER_DEVICE_NONE;
-       return ret;
-}
-
-/**
- * drm_dp_mst_dpcd_read() - read a series of bytes from the DPCD via sideband
- * @aux: Fake sideband AUX CH
- * @offset: address of the (first) register to read
- * @buffer: buffer to store the register values
- * @size: number of bytes in @buffer
- *
- * Performs the same functionality for remote devices via
- * sideband messaging as drm_dp_dpcd_read() does for local
- * devices via actual AUX CH.
- *
- * Return: Number of bytes read, or negative error code on failure.
- */
-ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
-                            unsigned int offset, void *buffer, size_t size)
-{
-       struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port,
-                                                   aux);
-
-       return drm_dp_send_dpcd_read(port->mgr, port,
-                                    offset, size, buffer);
-}
-
-/**
- * drm_dp_mst_dpcd_write() - write a series of bytes to the DPCD via sideband
- * @aux: Fake sideband AUX CH
- * @offset: address of the (first) register to write
- * @buffer: buffer containing the values to write
- * @size: number of bytes in @buffer
- *
- * Performs the same functionality for remote devices via
- * sideband messaging as drm_dp_dpcd_write() does for local
- * devices via actual AUX CH.
- *
- * Return: number of bytes written on success, negative error code on failure.
- */
-ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux,
-                             unsigned int offset, void *buffer, size_t size)
-{
-       struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port,
-                                                   aux);
-
-       return drm_dp_send_dpcd_write(port->mgr, port,
-                                     offset, size, buffer);
-}
-
-static int drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, u8 *guid)
-{
-       int ret = 0;
-
-       memcpy(mstb->guid, guid, 16);
-
-       if (!drm_dp_validate_guid(mstb->mgr, mstb->guid)) {
-               if (mstb->port_parent) {
-                       ret = drm_dp_send_dpcd_write(mstb->mgr,
-                                                    mstb->port_parent,
-                                                    DP_GUID, 16, mstb->guid);
-               } else {
-                       ret = drm_dp_dpcd_write(mstb->mgr->aux,
-                                               DP_GUID, mstb->guid, 16);
-               }
-       }
-
-       if (ret < 16 && ret > 0)
-               return -EPROTO;
-
-       return ret == 16 ? 0 : ret;
-}
-
-static void build_mst_prop_path(const struct drm_dp_mst_branch *mstb,
-                               int pnum,
-                               char *proppath,
-                               size_t proppath_size)
-{
-       int i;
-       char temp[8];
-
-       snprintf(proppath, proppath_size, "mst:%d", mstb->mgr->conn_base_id);
-       for (i = 0; i < (mstb->lct - 1); i++) {
-               int shift = (i % 2) ? 0 : 4;
-               int port_num = (mstb->rad[i / 2] >> shift) & 0xf;
-
-               snprintf(temp, sizeof(temp), "-%d", port_num);
-               strlcat(proppath, temp, proppath_size);
-       }
-       snprintf(temp, sizeof(temp), "-%d", pnum);
-       strlcat(proppath, temp, proppath_size);
-}
-
-/**
- * drm_dp_mst_connector_late_register() - Late MST connector registration
- * @connector: The MST connector
- * @port: The MST port for this connector
- *
- * Helper to register the remote aux device for this MST port. Drivers should
- * call this from their mst connector's late_register hook to enable MST aux
- * devices.
- *
- * Return: 0 on success, negative error code on failure.
- */
-int drm_dp_mst_connector_late_register(struct drm_connector *connector,
-                                      struct drm_dp_mst_port *port)
-{
-       drm_dbg_kms(port->mgr->dev, "registering %s remote bus for %s\n",
-                   port->aux.name, connector->kdev->kobj.name);
-
-       port->aux.dev = connector->kdev;
-       return drm_dp_aux_register_devnode(&port->aux);
-}
-EXPORT_SYMBOL(drm_dp_mst_connector_late_register);
-
-/**
- * drm_dp_mst_connector_early_unregister() - Early MST connector unregistration
- * @connector: The MST connector
- * @port: The MST port for this connector
- *
- * Helper to unregister the remote aux device for this MST port, registered by
- * drm_dp_mst_connector_late_register(). Drivers should call this from their mst
- * connector's early_unregister hook.
- */
-void drm_dp_mst_connector_early_unregister(struct drm_connector *connector,
-                                          struct drm_dp_mst_port *port)
-{
-       drm_dbg_kms(port->mgr->dev, "unregistering %s remote bus for %s\n",
-                   port->aux.name, connector->kdev->kobj.name);
-       drm_dp_aux_unregister_devnode(&port->aux);
-}
-EXPORT_SYMBOL(drm_dp_mst_connector_early_unregister);
-
-static void
-drm_dp_mst_port_add_connector(struct drm_dp_mst_branch *mstb,
-                             struct drm_dp_mst_port *port)
-{
-       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
-       char proppath[255];
-       int ret;
-
-       build_mst_prop_path(mstb, port->port_num, proppath, sizeof(proppath));
-       port->connector = mgr->cbs->add_connector(mgr, port, proppath);
-       if (!port->connector) {
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       if (port->pdt != DP_PEER_DEVICE_NONE &&
-           drm_dp_mst_is_end_device(port->pdt, port->mcs) &&
-           port->port_num >= DP_MST_LOGICAL_PORT_0)
-               port->cached_edid = drm_get_edid(port->connector,
-                                                &port->aux.ddc);
-
-       drm_connector_register(port->connector);
-       return;
-
-error:
-       drm_err(mgr->dev, "Failed to create connector for port %p: %d\n", port, ret);
-}
-
-/*
- * Drop a topology reference, and unlink the port from the in-memory topology
- * layout
- */
-static void
-drm_dp_mst_topology_unlink_port(struct drm_dp_mst_topology_mgr *mgr,
-                               struct drm_dp_mst_port *port)
-{
-       mutex_lock(&mgr->lock);
-       port->parent->num_ports--;
-       list_del(&port->next);
-       mutex_unlock(&mgr->lock);
-       drm_dp_mst_topology_put_port(port);
-}
-
-static struct drm_dp_mst_port *
-drm_dp_mst_add_port(struct drm_device *dev,
-                   struct drm_dp_mst_topology_mgr *mgr,
-                   struct drm_dp_mst_branch *mstb, u8 port_number)
-{
-       struct drm_dp_mst_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
-
-       if (!port)
-               return NULL;
-
-       kref_init(&port->topology_kref);
-       kref_init(&port->malloc_kref);
-       port->parent = mstb;
-       port->port_num = port_number;
-       port->mgr = mgr;
-       port->aux.name = "DPMST";
-       port->aux.dev = dev->dev;
-       port->aux.is_remote = true;
-
-       /* initialize the MST downstream port's AUX crc work queue */
-       port->aux.drm_dev = dev;
-       drm_dp_remote_aux_init(&port->aux);
-
-       /*
-        * Make sure the memory allocation for our parent branch stays
-        * around until our own memory allocation is released
-        */
-       drm_dp_mst_get_mstb_malloc(mstb);
-
-       return port;
-}
-
-static int
-drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
-                                   struct drm_device *dev,
-                                   struct drm_dp_link_addr_reply_port *port_msg)
-{
-       struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
-       struct drm_dp_mst_port *port;
-       int old_ddps = 0, ret;
-       u8 new_pdt = DP_PEER_DEVICE_NONE;
-       bool new_mcs = 0;
-       bool created = false, send_link_addr = false, changed = false;
-
-       port = drm_dp_get_port(mstb, port_msg->port_number);
-       if (!port) {
-               port = drm_dp_mst_add_port(dev, mgr, mstb,
-                                          port_msg->port_number);
-               if (!port)
-                       return -ENOMEM;
-               created = true;
-               changed = true;
-       } else if (!port->input && port_msg->input_port && port->connector) {
-               /* Since port->connector can't be changed here, we create a
-                * new port if input_port changes from 0 to 1
-                */
-               drm_dp_mst_topology_unlink_port(mgr, port);
-               drm_dp_mst_topology_put_port(port);
-               port = drm_dp_mst_add_port(dev, mgr, mstb,
-                                          port_msg->port_number);
-               if (!port)
-                       return -ENOMEM;
-               changed = true;
-               created = true;
-       } else if (port->input && !port_msg->input_port) {
-               changed = true;
-       } else if (port->connector) {
-               /* We're updating a port that's exposed to userspace, so do it
-                * under lock
-                */
-               drm_modeset_lock(&mgr->base.lock, NULL);
-
-               old_ddps = port->ddps;
-               changed = port->ddps != port_msg->ddps ||
-                       (port->ddps &&
-                        (port->ldps != port_msg->legacy_device_plug_status ||
-                         port->dpcd_rev != port_msg->dpcd_revision ||
-                         port->mcs != port_msg->mcs ||
-                         port->pdt != port_msg->peer_device_type ||
-                         port->num_sdp_stream_sinks !=
-                         port_msg->num_sdp_stream_sinks));
-       }
-
-       port->input = port_msg->input_port;
-       if (!port->input)
-               new_pdt = port_msg->peer_device_type;
-       new_mcs = port_msg->mcs;
-       port->ddps = port_msg->ddps;
-       port->ldps = port_msg->legacy_device_plug_status;
-       port->dpcd_rev = port_msg->dpcd_revision;
-       port->num_sdp_streams = port_msg->num_sdp_streams;
-       port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks;
-
-       /* manage mstb port lists with mgr lock - take a reference
-          for this list */
-       if (created) {
-               mutex_lock(&mgr->lock);
-               drm_dp_mst_topology_get_port(port);
-               list_add(&port->next, &mstb->ports);
-               mstb->num_ports++;
-               mutex_unlock(&mgr->lock);
-       }
-
-       /*
-        * Reprobe PBN caps on both hotplug, and when re-probing the link
-        * for our parent mstb
-        */
-       if (old_ddps != port->ddps || !created) {
-               if (port->ddps && !port->input) {
-                       ret = drm_dp_send_enum_path_resources(mgr, mstb,
-                                                             port);
-                       if (ret == 1)
-                               changed = true;
-               } else {
-                       port->full_pbn = 0;
-               }
-       }
-
-       ret = drm_dp_port_set_pdt(port, new_pdt, new_mcs);
-       if (ret == 1) {
-               send_link_addr = true;
-       } else if (ret < 0) {
-               drm_err(dev, "Failed to change PDT on port %p: %d\n", port, ret);
-               goto fail;
-       }
-
-       /*
-        * If this port wasn't just created, then we're reprobing because
-        * we're coming out of suspend. In this case, always resend the link
-        * address if there's an MSTB on this port
-        */
-       if (!created && port->pdt == DP_PEER_DEVICE_MST_BRANCHING &&
-           port->mcs)
-               send_link_addr = true;
-
-       if (port->connector)
-               drm_modeset_unlock(&mgr->base.lock);
-       else if (!port->input)
-               drm_dp_mst_port_add_connector(mstb, port);
-
-       if (send_link_addr && port->mstb) {
-               ret = drm_dp_send_link_address(mgr, port->mstb);
-               if (ret == 1) /* MSTB below us changed */
-                       changed = true;
-               else if (ret < 0)
-                       goto fail_put;
-       }
-
-       /* put reference to this port */
-       drm_dp_mst_topology_put_port(port);
-       return changed;
-
-fail:
-       drm_dp_mst_topology_unlink_port(mgr, port);
-       if (port->connector)
-               drm_modeset_unlock(&mgr->base.lock);
-fail_put:
-       drm_dp_mst_topology_put_port(port);
-       return ret;
-}
-
-static void
-drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
-                           struct drm_dp_connection_status_notify *conn_stat)
-{
-       struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
-       struct drm_dp_mst_port *port;
-       int old_ddps, ret;
-       u8 new_pdt;
-       bool new_mcs;
-       bool dowork = false, create_connector = false;
-
-       port = drm_dp_get_port(mstb, conn_stat->port_number);
-       if (!port)
-               return;
-
-       if (port->connector) {
-               if (!port->input && conn_stat->input_port) {
-                       /*
-                        * We can't remove a connector from an already exposed
-                        * port, so just throw the port out and make sure we
-                        * reprobe the link address of it's parent MSTB
-                        */
-                       drm_dp_mst_topology_unlink_port(mgr, port);
-                       mstb->link_address_sent = false;
-                       dowork = true;
-                       goto out;
-               }
-
-               /* Locking is only needed if the port's exposed to userspace */
-               drm_modeset_lock(&mgr->base.lock, NULL);
-       } else if (port->input && !conn_stat->input_port) {
-               create_connector = true;
-               /* Reprobe link address so we get num_sdp_streams */
-               mstb->link_address_sent = false;
-               dowork = true;
-       }
-
-       old_ddps = port->ddps;
-       port->input = conn_stat->input_port;
-       port->ldps = conn_stat->legacy_device_plug_status;
-       port->ddps = conn_stat->displayport_device_plug_status;
-
-       if (old_ddps != port->ddps) {
-               if (port->ddps && !port->input)
-                       drm_dp_send_enum_path_resources(mgr, mstb, port);
-               else
-                       port->full_pbn = 0;
-       }
-
-       new_pdt = port->input ? DP_PEER_DEVICE_NONE : conn_stat->peer_device_type;
-       new_mcs = conn_stat->message_capability_status;
-       ret = drm_dp_port_set_pdt(port, new_pdt, new_mcs);
-       if (ret == 1) {
-               dowork = true;
-       } else if (ret < 0) {
-               drm_err(mgr->dev, "Failed to change PDT for port %p: %d\n", port, ret);
-               dowork = false;
-       }
-
-       if (port->connector)
-               drm_modeset_unlock(&mgr->base.lock);
-       else if (create_connector)
-               drm_dp_mst_port_add_connector(mstb, port);
-
-out:
-       drm_dp_mst_topology_put_port(port);
-       if (dowork)
-               queue_work(system_long_wq, &mstb->mgr->work);
-}
-
-static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr,
-                                                              u8 lct, u8 *rad)
-{
-       struct drm_dp_mst_branch *mstb;
-       struct drm_dp_mst_port *port;
-       int i, ret;
-       /* find the port by iterating down */
-
-       mutex_lock(&mgr->lock);
-       mstb = mgr->mst_primary;
-
-       if (!mstb)
-               goto out;
-
-       for (i = 0; i < lct - 1; i++) {
-               int shift = (i % 2) ? 0 : 4;
-               int port_num = (rad[i / 2] >> shift) & 0xf;
-
-               list_for_each_entry(port, &mstb->ports, next) {
-                       if (port->port_num == port_num) {
-                               mstb = port->mstb;
-                               if (!mstb) {
-                                       drm_err(mgr->dev,
-                                               "failed to lookup MSTB with lct %d, rad %02x\n",
-                                               lct, rad[0]);
-                                       goto out;
-                               }
-
-                               break;
-                       }
-               }
-       }
-       ret = drm_dp_mst_topology_try_get_mstb(mstb);
-       if (!ret)
-               mstb = NULL;
-out:
-       mutex_unlock(&mgr->lock);
-       return mstb;
-}
-
-static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
-       struct drm_dp_mst_branch *mstb,
-       const uint8_t *guid)
-{
-       struct drm_dp_mst_branch *found_mstb;
-       struct drm_dp_mst_port *port;
-
-       if (memcmp(mstb->guid, guid, 16) == 0)
-               return mstb;
-
-
-       list_for_each_entry(port, &mstb->ports, next) {
-               if (!port->mstb)
-                       continue;
-
-               found_mstb = get_mst_branch_device_by_guid_helper(port->mstb, guid);
-
-               if (found_mstb)
-                       return found_mstb;
-       }
-
-       return NULL;
-}
-
-static struct drm_dp_mst_branch *
-drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr *mgr,
-                                    const uint8_t *guid)
-{
-       struct drm_dp_mst_branch *mstb;
-       int ret;
-
-       /* find the port by iterating down */
-       mutex_lock(&mgr->lock);
-
-       mstb = get_mst_branch_device_by_guid_helper(mgr->mst_primary, guid);
-       if (mstb) {
-               ret = drm_dp_mst_topology_try_get_mstb(mstb);
-               if (!ret)
-                       mstb = NULL;
-       }
-
-       mutex_unlock(&mgr->lock);
-       return mstb;
-}
-
-static int drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
-                                              struct drm_dp_mst_branch *mstb)
-{
-       struct drm_dp_mst_port *port;
-       int ret;
-       bool changed = false;
-
-       if (!mstb->link_address_sent) {
-               ret = drm_dp_send_link_address(mgr, mstb);
-               if (ret == 1)
-                       changed = true;
-               else if (ret < 0)
-                       return ret;
-       }
-
-       list_for_each_entry(port, &mstb->ports, next) {
-               struct drm_dp_mst_branch *mstb_child = NULL;
-
-               if (port->input || !port->ddps)
-                       continue;
-
-               if (port->mstb)
-                       mstb_child = drm_dp_mst_topology_get_mstb_validated(
-                           mgr, port->mstb);
-
-               if (mstb_child) {
-                       ret = drm_dp_check_and_send_link_address(mgr,
-                                                                mstb_child);
-                       drm_dp_mst_topology_put_mstb(mstb_child);
-                       if (ret == 1)
-                               changed = true;
-                       else if (ret < 0)
-                               return ret;
-               }
-       }
-
-       return changed;
-}
-
-static void drm_dp_mst_link_probe_work(struct work_struct *work)
-{
-       struct drm_dp_mst_topology_mgr *mgr =
-               container_of(work, struct drm_dp_mst_topology_mgr, work);
-       struct drm_device *dev = mgr->dev;
-       struct drm_dp_mst_branch *mstb;
-       int ret;
-       bool clear_payload_id_table;
-
-       mutex_lock(&mgr->probe_lock);
-
-       mutex_lock(&mgr->lock);
-       clear_payload_id_table = !mgr->payload_id_table_cleared;
-       mgr->payload_id_table_cleared = true;
-
-       mstb = mgr->mst_primary;
-       if (mstb) {
-               ret = drm_dp_mst_topology_try_get_mstb(mstb);
-               if (!ret)
-                       mstb = NULL;
-       }
-       mutex_unlock(&mgr->lock);
-       if (!mstb) {
-               mutex_unlock(&mgr->probe_lock);
-               return;
-       }
-
-       /*
-        * Certain branch devices seem to incorrectly report an available_pbn
-        * of 0 on downstream sinks, even after clearing the
-        * DP_PAYLOAD_ALLOCATE_* registers in
-        * drm_dp_mst_topology_mgr_set_mst(). Namely, the CableMatters USB-C
-        * 2x DP hub. Sending a CLEAR_PAYLOAD_ID_TABLE message seems to make
-        * things work again.
-        */
-       if (clear_payload_id_table) {
-               drm_dbg_kms(dev, "Clearing payload ID table\n");
-               drm_dp_send_clear_payload_id_table(mgr, mstb);
-       }
-
-       ret = drm_dp_check_and_send_link_address(mgr, mstb);
-       drm_dp_mst_topology_put_mstb(mstb);
-
-       mutex_unlock(&mgr->probe_lock);
-       if (ret > 0)
-               drm_kms_helper_hotplug_event(dev);
-}
-
-static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
-                                u8 *guid)
-{
-       u64 salt;
-
-       if (memchr_inv(guid, 0, 16))
-               return true;
-
-       salt = get_jiffies_64();
-
-       memcpy(&guid[0], &salt, sizeof(u64));
-       memcpy(&guid[8], &salt, sizeof(u64));
-
-       return false;
-}
-
-static void build_dpcd_read(struct drm_dp_sideband_msg_tx *msg,
-                           u8 port_num, u32 offset, u8 num_bytes)
-{
-       struct drm_dp_sideband_msg_req_body req;
-
-       req.req_type = DP_REMOTE_DPCD_READ;
-       req.u.dpcd_read.port_number = port_num;
-       req.u.dpcd_read.dpcd_address = offset;
-       req.u.dpcd_read.num_bytes = num_bytes;
-       drm_dp_encode_sideband_req(&req, msg);
-}
-
-static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr,
-                                   bool up, u8 *msg, int len)
-{
-       int ret;
-       int regbase = up ? DP_SIDEBAND_MSG_UP_REP_BASE : DP_SIDEBAND_MSG_DOWN_REQ_BASE;
-       int tosend, total, offset;
-       int retries = 0;
-
-retry:
-       total = len;
-       offset = 0;
-       do {
-               tosend = min3(mgr->max_dpcd_transaction_bytes, 16, total);
-
-               ret = drm_dp_dpcd_write(mgr->aux, regbase + offset,
-                                       &msg[offset],
-                                       tosend);
-               if (ret != tosend) {
-                       if (ret == -EIO && retries < 5) {
-                               retries++;
-                               goto retry;
-                       }
-                       drm_dbg_kms(mgr->dev, "failed to dpcd write %d %d\n", tosend, ret);
-
-                       return -EIO;
-               }
-               offset += tosend;
-               total -= tosend;
-       } while (total > 0);
-       return 0;
-}
-
-static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr,
-                                 struct drm_dp_sideband_msg_tx *txmsg)
-{
-       struct drm_dp_mst_branch *mstb = txmsg->dst;
-       u8 req_type;
-
-       req_type = txmsg->msg[0] & 0x7f;
-       if (req_type == DP_CONNECTION_STATUS_NOTIFY ||
-               req_type == DP_RESOURCE_STATUS_NOTIFY ||
-               req_type == DP_CLEAR_PAYLOAD_ID_TABLE)
-               hdr->broadcast = 1;
-       else
-               hdr->broadcast = 0;
-       hdr->path_msg = txmsg->path_msg;
-       if (hdr->broadcast) {
-               hdr->lct = 1;
-               hdr->lcr = 6;
-       } else {
-               hdr->lct = mstb->lct;
-               hdr->lcr = mstb->lct - 1;
-       }
-
-       memcpy(hdr->rad, mstb->rad, hdr->lct / 2);
-
-       return 0;
-}
-/*
- * process a single block of the next message in the sideband queue
- */
-static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,
-                                  struct drm_dp_sideband_msg_tx *txmsg,
-                                  bool up)
-{
-       u8 chunk[48];
-       struct drm_dp_sideband_msg_hdr hdr;
-       int len, space, idx, tosend;
-       int ret;
-
-       if (txmsg->state == DRM_DP_SIDEBAND_TX_SENT)
-               return 0;
-
-       memset(&hdr, 0, sizeof(struct drm_dp_sideband_msg_hdr));
-
-       if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED)
-               txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND;
-
-       /* make hdr from dst mst */
-       ret = set_hdr_from_dst_qlock(&hdr, txmsg);
-       if (ret < 0)
-               return ret;
-
-       /* amount left to send in this message */
-       len = txmsg->cur_len - txmsg->cur_offset;
-
-       /* 48 - sideband msg size - 1 byte for data CRC, x header bytes */
-       space = 48 - 1 - drm_dp_calc_sb_hdr_size(&hdr);
-
-       tosend = min(len, space);
-       if (len == txmsg->cur_len)
-               hdr.somt = 1;
-       if (space >= len)
-               hdr.eomt = 1;
-
-
-       hdr.msg_len = tosend + 1;
-       drm_dp_encode_sideband_msg_hdr(&hdr, chunk, &idx);
-       memcpy(&chunk[idx], &txmsg->msg[txmsg->cur_offset], tosend);
-       /* add crc at end */
-       drm_dp_crc_sideband_chunk_req(&chunk[idx], tosend);
-       idx += tosend + 1;
-
-       ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx);
-       if (ret) {
-               if (drm_debug_enabled(DRM_UT_DP)) {
-                       struct drm_printer p = drm_debug_printer(DBG_PREFIX);
-
-                       drm_printf(&p, "sideband msg failed to send\n");
-                       drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
-               }
-               return ret;
-       }
-
-       txmsg->cur_offset += tosend;
-       if (txmsg->cur_offset == txmsg->cur_len) {
-               txmsg->state = DRM_DP_SIDEBAND_TX_SENT;
-               return 1;
-       }
-       return 0;
-}
-
-static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
-{
-       struct drm_dp_sideband_msg_tx *txmsg;
-       int ret;
-
-       WARN_ON(!mutex_is_locked(&mgr->qlock));
-
-       /* construct a chunk from the first msg in the tx_msg queue */
-       if (list_empty(&mgr->tx_msg_downq))
-               return;
-
-       txmsg = list_first_entry(&mgr->tx_msg_downq,
-                                struct drm_dp_sideband_msg_tx, next);
-       ret = process_single_tx_qlock(mgr, txmsg, false);
-       if (ret < 0) {
-               drm_dbg_kms(mgr->dev, "failed to send msg in q %d\n", ret);
-               list_del(&txmsg->next);
-               txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
-               wake_up_all(&mgr->tx_waitq);
-       }
-}
-
-static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
-                                struct drm_dp_sideband_msg_tx *txmsg)
-{
-       mutex_lock(&mgr->qlock);
-       list_add_tail(&txmsg->next, &mgr->tx_msg_downq);
-
-       if (drm_debug_enabled(DRM_UT_DP)) {
-               struct drm_printer p = drm_debug_printer(DBG_PREFIX);
-
-               drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
-       }
-
-       if (list_is_singular(&mgr->tx_msg_downq))
-               process_single_down_tx_qlock(mgr);
-       mutex_unlock(&mgr->qlock);
-}
-
-static void
-drm_dp_dump_link_address(const struct drm_dp_mst_topology_mgr *mgr,
-                        struct drm_dp_link_address_ack_reply *reply)
-{
-       struct drm_dp_link_addr_reply_port *port_reply;
-       int i;
-
-       for (i = 0; i < reply->nports; i++) {
-               port_reply = &reply->ports[i];
-               drm_dbg_kms(mgr->dev,
-                           "port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n",
-                           i,
-                           port_reply->input_port,
-                           port_reply->peer_device_type,
-                           port_reply->port_number,
-                           port_reply->dpcd_revision,
-                           port_reply->mcs,
-                           port_reply->ddps,
-                           port_reply->legacy_device_plug_status,
-                           port_reply->num_sdp_streams,
-                           port_reply->num_sdp_stream_sinks);
-       }
-}
-
-static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
-                                    struct drm_dp_mst_branch *mstb)
-{
-       struct drm_dp_sideband_msg_tx *txmsg;
-       struct drm_dp_link_address_ack_reply *reply;
-       struct drm_dp_mst_port *port, *tmp;
-       int i, ret, port_mask = 0;
-       bool changed = false;
-
-       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-       if (!txmsg)
-               return -ENOMEM;
-
-       txmsg->dst = mstb;
-       build_link_address(txmsg);
-
-       mstb->link_address_sent = true;
-       drm_dp_queue_down_tx(mgr, txmsg);
-
-       /* FIXME: Actually do some real error handling here */
-       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
-       if (ret <= 0) {
-               drm_err(mgr->dev, "Sending link address failed with %d\n", ret);
-               goto out;
-       }
-       if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
-               drm_err(mgr->dev, "link address NAK received\n");
-               ret = -EIO;
-               goto out;
-       }
-
-       reply = &txmsg->reply.u.link_addr;
-       drm_dbg_kms(mgr->dev, "link address reply: %d\n", reply->nports);
-       drm_dp_dump_link_address(mgr, reply);
-
-       ret = drm_dp_check_mstb_guid(mstb, reply->guid);
-       if (ret) {
-               char buf[64];
-
-               drm_dp_mst_rad_to_str(mstb->rad, mstb->lct, buf, sizeof(buf));
-               drm_err(mgr->dev, "GUID check on %s failed: %d\n", buf, ret);
-               goto out;
-       }
-
-       for (i = 0; i < reply->nports; i++) {
-               port_mask |= BIT(reply->ports[i].port_number);
-               ret = drm_dp_mst_handle_link_address_port(mstb, mgr->dev,
-                                                         &reply->ports[i]);
-               if (ret == 1)
-                       changed = true;
-               else if (ret < 0)
-                       goto out;
-       }
-
-       /* Prune any ports that are currently a part of mstb in our in-memory
-        * topology, but were not seen in this link address. Usually this
-        * means that they were removed while the topology was out of sync,
-        * e.g. during suspend/resume
-        */
-       mutex_lock(&mgr->lock);
-       list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
-               if (port_mask & BIT(port->port_num))
-                       continue;
-
-               drm_dbg_kms(mgr->dev, "port %d was not in link address, removing\n",
-                           port->port_num);
-               list_del(&port->next);
-               drm_dp_mst_topology_put_port(port);
-               changed = true;
-       }
-       mutex_unlock(&mgr->lock);
-
-out:
-       if (ret <= 0)
-               mstb->link_address_sent = false;
-       kfree(txmsg);
-       return ret < 0 ? ret : changed;
-}
-
-static void
-drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr,
-                                  struct drm_dp_mst_branch *mstb)
-{
-       struct drm_dp_sideband_msg_tx *txmsg;
-       int ret;
-
-       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-       if (!txmsg)
-               return;
-
-       txmsg->dst = mstb;
-       build_clear_payload_id_table(txmsg);
-
-       drm_dp_queue_down_tx(mgr, txmsg);
-
-       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
-       if (ret > 0 && txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
-               drm_dbg_kms(mgr->dev, "clear payload table id nak received\n");
-
-       kfree(txmsg);
-}
-
-static int
-drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
-                               struct drm_dp_mst_branch *mstb,
-                               struct drm_dp_mst_port *port)
-{
-       struct drm_dp_enum_path_resources_ack_reply *path_res;
-       struct drm_dp_sideband_msg_tx *txmsg;
-       int ret;
-
-       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-       if (!txmsg)
-               return -ENOMEM;
-
-       txmsg->dst = mstb;
-       build_enum_path_resources(txmsg, port->port_num);
-
-       drm_dp_queue_down_tx(mgr, txmsg);
-
-       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
-       if (ret > 0) {
-               ret = 0;
-               path_res = &txmsg->reply.u.path_resources;
-
-               if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
-                       drm_dbg_kms(mgr->dev, "enum path resources nak received\n");
-               } else {
-                       if (port->port_num != path_res->port_number)
-                               DRM_ERROR("got incorrect port in response\n");
-
-                       drm_dbg_kms(mgr->dev, "enum path resources %d: %d %d\n",
-                                   path_res->port_number,
-                                   path_res->full_payload_bw_number,
-                                   path_res->avail_payload_bw_number);
-
-                       /*
-                        * If something changed, make sure we send a
-                        * hotplug
-                        */
-                       if (port->full_pbn != path_res->full_payload_bw_number ||
-                           port->fec_capable != path_res->fec_capable)
-                               ret = 1;
-
-                       port->full_pbn = path_res->full_payload_bw_number;
-                       port->fec_capable = path_res->fec_capable;
-               }
-       }
-
-       kfree(txmsg);
-       return ret;
-}
-
-static struct drm_dp_mst_port *drm_dp_get_last_connected_port_to_mstb(struct drm_dp_mst_branch *mstb)
-{
-       if (!mstb->port_parent)
-               return NULL;
-
-       if (mstb->port_parent->mstb != mstb)
-               return mstb->port_parent;
-
-       return drm_dp_get_last_connected_port_to_mstb(mstb->port_parent->parent);
-}
-
-/*
- * Searches upwards in the topology starting from mstb to try to find the
- * closest available parent of mstb that's still connected to the rest of the
- * topology. This can be used in order to perform operations like releasing
- * payloads, where the branch device which owned the payload may no longer be
- * around and thus would require that the payload on the last living relative
- * be freed instead.
- */
-static struct drm_dp_mst_branch *
-drm_dp_get_last_connected_port_and_mstb(struct drm_dp_mst_topology_mgr *mgr,
-                                       struct drm_dp_mst_branch *mstb,
-                                       int *port_num)
-{
-       struct drm_dp_mst_branch *rmstb = NULL;
-       struct drm_dp_mst_port *found_port;
-
-       mutex_lock(&mgr->lock);
-       if (!mgr->mst_primary)
-               goto out;
-
-       do {
-               found_port = drm_dp_get_last_connected_port_to_mstb(mstb);
-               if (!found_port)
-                       break;
-
-               if (drm_dp_mst_topology_try_get_mstb(found_port->parent)) {
-                       rmstb = found_port->parent;
-                       *port_num = found_port->port_num;
-               } else {
-                       /* Search again, starting from this parent */
-                       mstb = found_port->parent;
-               }
-       } while (!rmstb);
-out:
-       mutex_unlock(&mgr->lock);
-       return rmstb;
-}
-
-static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
-                                  struct drm_dp_mst_port *port,
-                                  int id,
-                                  int pbn)
-{
-       struct drm_dp_sideband_msg_tx *txmsg;
-       struct drm_dp_mst_branch *mstb;
-       int ret, port_num;
-       u8 sinks[DRM_DP_MAX_SDP_STREAMS];
-       int i;
-
-       port_num = port->port_num;
-       mstb = drm_dp_mst_topology_get_mstb_validated(mgr, port->parent);
-       if (!mstb) {
-               mstb = drm_dp_get_last_connected_port_and_mstb(mgr,
-                                                              port->parent,
-                                                              &port_num);
-
-               if (!mstb)
-                       return -EINVAL;
-       }
-
-       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-       if (!txmsg) {
-               ret = -ENOMEM;
-               goto fail_put;
-       }
-
-       for (i = 0; i < port->num_sdp_streams; i++)
-               sinks[i] = i;
-
-       txmsg->dst = mstb;
-       build_allocate_payload(txmsg, port_num,
-                              id,
-                              pbn, port->num_sdp_streams, sinks);
-
-       drm_dp_queue_down_tx(mgr, txmsg);
-
-       /*
-        * FIXME: there is a small chance that between getting the last
-        * connected mstb and sending the payload message, the last connected
-        * mstb could also be removed from the topology. In the future, this
-        * needs to be fixed by restarting the
-        * drm_dp_get_last_connected_port_and_mstb() search in the event of a
-        * timeout if the topology is still connected to the system.
-        */
-       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
-       if (ret > 0) {
-               if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
-                       ret = -EINVAL;
-               else
-                       ret = 0;
-       }
-       kfree(txmsg);
-fail_put:
-       drm_dp_mst_topology_put_mstb(mstb);
-       return ret;
-}
-
-int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
-                                struct drm_dp_mst_port *port, bool power_up)
-{
-       struct drm_dp_sideband_msg_tx *txmsg;
-       int ret;
-
-       port = drm_dp_mst_topology_get_port_validated(mgr, port);
-       if (!port)
-               return -EINVAL;
-
-       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-       if (!txmsg) {
-               drm_dp_mst_topology_put_port(port);
-               return -ENOMEM;
-       }
-
-       txmsg->dst = port->parent;
-       build_power_updown_phy(txmsg, port->port_num, power_up);
-       drm_dp_queue_down_tx(mgr, txmsg);
-
-       ret = drm_dp_mst_wait_tx_reply(port->parent, txmsg);
-       if (ret > 0) {
-               if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
-                       ret = -EINVAL;
-               else
-                       ret = 0;
-       }
-       kfree(txmsg);
-       drm_dp_mst_topology_put_port(port);
-
-       return ret;
-}
-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 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;
-
-       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)
-{
-       int ret;
-
-       ret = drm_dp_dpcd_write_payload(mgr, id, payload);
-       if (ret < 0) {
-               payload->payload_state = 0;
-               return ret;
-       }
-       payload->payload_state = DP_PAYLOAD_LOCAL;
-       return 0;
-}
-
-static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
-                                      struct drm_dp_mst_port *port,
-                                      int id,
-                                      struct drm_dp_payload *payload)
-{
-       int ret;
-
-       ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn);
-       if (ret < 0)
-               return ret;
-       payload->payload_state = DP_PAYLOAD_REMOTE;
-       return ret;
-}
-
-static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
-                                       struct drm_dp_mst_port *port,
-                                       int id,
-                                       struct drm_dp_payload *payload)
-{
-       drm_dbg_kms(mgr->dev, "\n");
-       /* it's okay for these to fail */
-       if (port) {
-               drm_dp_payload_send_msg(mgr, port, id, 0);
-       }
-
-       drm_dp_dpcd_write_payload(mgr, id, payload);
-       payload->payload_state = DP_PAYLOAD_DELETE_LOCAL;
-       return 0;
-}
-
-static int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
-                                       int id,
-                                       struct drm_dp_payload *payload)
-{
-       payload->payload_state = 0;
-       return 0;
-}
-
-/**
- * drm_dp_update_payload_part1() - Execute payload update part 1
- * @mgr: manager to use.
- * @start_slot: this is the cur slot
- *
- * NOTE: start_slot is a temporary workaround for non-atomic drivers,
- * this will be removed when non-atomic mst helpers are moved out of the helper
- *
- * This iterates over all proposed virtual channels, and tries to
- * allocate space in the link for them. For 0->slots transitions,
- * this step just writes the VCPI to the MST device. For slots->0
- * transitions, this writes the updated VCPIs and removes the
- * remote VC payloads.
- *
- * after calling this the driver should generate ACT and payload
- * packets.
- */
-int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr, int start_slot)
-{
-       struct drm_dp_payload req_payload;
-       struct drm_dp_mst_port *port;
-       int i, j;
-       int cur_slots = start_slot;
-       bool skip;
-
-       mutex_lock(&mgr->payload_lock);
-       for (i = 0; i < mgr->max_payloads; i++) {
-               struct drm_dp_vcpi *vcpi = mgr->proposed_vcpis[i];
-               struct drm_dp_payload *payload = &mgr->payloads[i];
-               bool put_port = false;
-
-               /* solve the current payloads - compare to the hw ones
-                  - update the hw view */
-               req_payload.start_slot = cur_slots;
-               if (vcpi) {
-                       port = container_of(vcpi, struct drm_dp_mst_port,
-                                           vcpi);
-
-                       mutex_lock(&mgr->lock);
-                       skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary);
-                       mutex_unlock(&mgr->lock);
-
-                       if (skip) {
-                               drm_dbg_kms(mgr->dev,
-                                           "Virtual channel %d is not in current topology\n",
-                                           i);
-                               continue;
-                       }
-                       /* Validated ports don't matter if we're releasing
-                        * VCPI
-                        */
-                       if (vcpi->num_slots) {
-                               port = drm_dp_mst_topology_get_port_validated(
-                                   mgr, port);
-                               if (!port) {
-                                       if (vcpi->num_slots == payload->num_slots) {
-                                               cur_slots += vcpi->num_slots;
-                                               payload->start_slot = req_payload.start_slot;
-                                               continue;
-                                       } else {
-                                               drm_dbg_kms(mgr->dev,
-                                                           "Fail:set payload to invalid sink");
-                                               mutex_unlock(&mgr->payload_lock);
-                                               return -EINVAL;
-                                       }
-                               }
-                               put_port = true;
-                       }
-
-                       req_payload.num_slots = vcpi->num_slots;
-                       req_payload.vcpi = vcpi->vcpi;
-               } else {
-                       port = NULL;
-                       req_payload.num_slots = 0;
-               }
-
-               payload->start_slot = req_payload.start_slot;
-               /* work out what is required to happen with this payload */
-               if (payload->num_slots != req_payload.num_slots) {
-
-                       /* need to push an update for this payload */
-                       if (req_payload.num_slots) {
-                               drm_dp_create_payload_step1(mgr, vcpi->vcpi,
-                                                           &req_payload);
-                               payload->num_slots = req_payload.num_slots;
-                               payload->vcpi = req_payload.vcpi;
-
-                       } else if (payload->num_slots) {
-                               payload->num_slots = 0;
-                               drm_dp_destroy_payload_step1(mgr, port,
-                                                            payload->vcpi,
-                                                            payload);
-                               req_payload.payload_state =
-                                       payload->payload_state;
-                               payload->start_slot = 0;
-                       }
-                       payload->payload_state = req_payload.payload_state;
-               }
-               cur_slots += req_payload.num_slots;
-
-               if (put_port)
-                       drm_dp_mst_topology_put_port(port);
-       }
-
-       for (i = 0; i < mgr->max_payloads; /* do nothing */) {
-               if (mgr->payloads[i].payload_state != DP_PAYLOAD_DELETE_LOCAL) {
-                       i++;
-                       continue;
-               }
-
-               drm_dbg_kms(mgr->dev, "removing payload %d\n", i);
-               for (j = i; j < mgr->max_payloads - 1; j++) {
-                       mgr->payloads[j] = mgr->payloads[j + 1];
-                       mgr->proposed_vcpis[j] = mgr->proposed_vcpis[j + 1];
-
-                       if (mgr->proposed_vcpis[j] &&
-                           mgr->proposed_vcpis[j]->num_slots) {
-                               set_bit(j + 1, &mgr->payload_mask);
-                       } else {
-                               clear_bit(j + 1, &mgr->payload_mask);
-                       }
-               }
-
-               memset(&mgr->payloads[mgr->max_payloads - 1], 0,
-                      sizeof(struct drm_dp_payload));
-               mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL;
-               clear_bit(mgr->max_payloads, &mgr->payload_mask);
-       }
-       mutex_unlock(&mgr->payload_lock);
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_update_payload_part1);
-
-/**
- * drm_dp_update_payload_part2() - Execute payload update part 2
- * @mgr: manager to use.
- *
- * This iterates over all proposed virtual channels, and tries to
- * allocate space in the link for them. For 0->slots transitions,
- * this step writes the remote VC payload commands. For slots->0
- * this just resets some internal state.
- */
-int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
-{
-       struct drm_dp_mst_port *port;
-       int i;
-       int ret = 0;
-       bool skip;
-
-       mutex_lock(&mgr->payload_lock);
-       for (i = 0; i < mgr->max_payloads; i++) {
-
-               if (!mgr->proposed_vcpis[i])
-                       continue;
-
-               port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
-
-               mutex_lock(&mgr->lock);
-               skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary);
-               mutex_unlock(&mgr->lock);
-
-               if (skip)
-                       continue;
-
-               drm_dbg_kms(mgr->dev, "payload %d %d\n", i, mgr->payloads[i].payload_state);
-               if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) {
-                       ret = drm_dp_create_payload_step2(mgr, port, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]);
-               } else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) {
-                       ret = drm_dp_destroy_payload_step2(mgr, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]);
-               }
-               if (ret) {
-                       mutex_unlock(&mgr->payload_lock);
-                       return ret;
-               }
-       }
-       mutex_unlock(&mgr->payload_lock);
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_update_payload_part2);
-
-static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
-                                struct drm_dp_mst_port *port,
-                                int offset, int size, u8 *bytes)
-{
-       int ret = 0;
-       struct drm_dp_sideband_msg_tx *txmsg;
-       struct drm_dp_mst_branch *mstb;
-
-       mstb = drm_dp_mst_topology_get_mstb_validated(mgr, port->parent);
-       if (!mstb)
-               return -EINVAL;
-
-       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-       if (!txmsg) {
-               ret = -ENOMEM;
-               goto fail_put;
-       }
-
-       build_dpcd_read(txmsg, port->port_num, offset, size);
-       txmsg->dst = port->parent;
-
-       drm_dp_queue_down_tx(mgr, txmsg);
-
-       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
-       if (ret < 0)
-               goto fail_free;
-
-       /* DPCD read should never be NACKed */
-       if (txmsg->reply.reply_type == 1) {
-               drm_err(mgr->dev, "mstb %p port %d: DPCD read on addr 0x%x for %d bytes NAKed\n",
-                       mstb, port->port_num, offset, size);
-               ret = -EIO;
-               goto fail_free;
-       }
-
-       if (txmsg->reply.u.remote_dpcd_read_ack.num_bytes != size) {
-               ret = -EPROTO;
-               goto fail_free;
-       }
-
-       ret = min_t(size_t, txmsg->reply.u.remote_dpcd_read_ack.num_bytes,
-                   size);
-       memcpy(bytes, txmsg->reply.u.remote_dpcd_read_ack.bytes, ret);
-
-fail_free:
-       kfree(txmsg);
-fail_put:
-       drm_dp_mst_topology_put_mstb(mstb);
-
-       return ret;
-}
-
-static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
-                                 struct drm_dp_mst_port *port,
-                                 int offset, int size, u8 *bytes)
-{
-       int ret;
-       struct drm_dp_sideband_msg_tx *txmsg;
-       struct drm_dp_mst_branch *mstb;
-
-       mstb = drm_dp_mst_topology_get_mstb_validated(mgr, port->parent);
-       if (!mstb)
-               return -EINVAL;
-
-       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-       if (!txmsg) {
-               ret = -ENOMEM;
-               goto fail_put;
-       }
-
-       build_dpcd_write(txmsg, port->port_num, offset, size, bytes);
-       txmsg->dst = mstb;
-
-       drm_dp_queue_down_tx(mgr, txmsg);
-
-       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
-       if (ret > 0) {
-               if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
-                       ret = -EIO;
-               else
-                       ret = size;
-       }
-
-       kfree(txmsg);
-fail_put:
-       drm_dp_mst_topology_put_mstb(mstb);
-       return ret;
-}
-
-static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type)
-{
-       struct drm_dp_sideband_msg_reply_body reply;
-
-       reply.reply_type = DP_SIDEBAND_REPLY_ACK;
-       reply.req_type = req_type;
-       drm_dp_encode_sideband_reply(&reply, msg);
-       return 0;
-}
-
-static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr,
-                                   struct drm_dp_mst_branch *mstb,
-                                   int req_type, bool broadcast)
-{
-       struct drm_dp_sideband_msg_tx *txmsg;
-
-       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-       if (!txmsg)
-               return -ENOMEM;
-
-       txmsg->dst = mstb;
-       drm_dp_encode_up_ack_reply(txmsg, req_type);
-
-       mutex_lock(&mgr->qlock);
-       /* construct a chunk from the first msg in the tx_msg queue */
-       process_single_tx_qlock(mgr, txmsg, true);
-       mutex_unlock(&mgr->qlock);
-
-       kfree(txmsg);
-       return 0;
-}
-
-/**
- * drm_dp_get_vc_payload_bw - get the VC payload BW for an MST link
- * @mgr: The &drm_dp_mst_topology_mgr to use
- * @link_rate: link rate in 10kbits/s units
- * @link_lane_count: lane count
- *
- * Calculate the total bandwidth of a MultiStream Transport link. The returned
- * value is in units of PBNs/(timeslots/1 MTP). This value can be used to
- * convert the number of PBNs required for a given stream to the number of
- * timeslots this stream requires in each MTP.
- */
-int drm_dp_get_vc_payload_bw(const struct drm_dp_mst_topology_mgr *mgr,
-                            int link_rate, int link_lane_count)
-{
-       if (link_rate == 0 || link_lane_count == 0)
-               drm_dbg_kms(mgr->dev, "invalid link rate/lane count: (%d / %d)\n",
-                           link_rate, link_lane_count);
-
-       /* See DP v2.0 2.6.4.2, VCPayload_Bandwidth_for_OneTimeSlotPer_MTP_Allocation */
-       return link_rate * link_lane_count / 54000;
-}
-EXPORT_SYMBOL(drm_dp_get_vc_payload_bw);
-
-/**
- * drm_dp_read_mst_cap() - check whether or not a sink supports MST
- * @aux: The DP AUX channel to use
- * @dpcd: A cached copy of the DPCD capabilities for this sink
- *
- * Returns: %True if the sink supports MST, %false otherwise
- */
-bool drm_dp_read_mst_cap(struct drm_dp_aux *aux,
-                        const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       u8 mstm_cap;
-
-       if (dpcd[DP_DPCD_REV] < DP_DPCD_REV_12)
-               return false;
-
-       if (drm_dp_dpcd_readb(aux, DP_MSTM_CAP, &mstm_cap) != 1)
-               return false;
-
-       return mstm_cap & DP_MST_CAP;
-}
-EXPORT_SYMBOL(drm_dp_read_mst_cap);
-
-/**
- * drm_dp_mst_topology_mgr_set_mst() - Set the MST state for a topology manager
- * @mgr: manager to set state for
- * @mst_state: true to enable MST on this connector - false to disable.
- *
- * This is called by the driver when it detects an MST capable device plugged
- * into a DP MST capable port, or when a DP MST capable device is unplugged.
- */
-int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state)
-{
-       int ret = 0;
-       struct drm_dp_mst_branch *mstb = NULL;
-
-       mutex_lock(&mgr->payload_lock);
-       mutex_lock(&mgr->lock);
-       if (mst_state == mgr->mst_state)
-               goto out_unlock;
-
-       mgr->mst_state = mst_state;
-       /* set the device into MST mode */
-       if (mst_state) {
-               struct drm_dp_payload reset_pay;
-               int lane_count;
-               int link_rate;
-
-               WARN_ON(mgr->mst_primary);
-
-               /* get dpcd info */
-               ret = drm_dp_read_dpcd_caps(mgr->aux, mgr->dpcd);
-               if (ret < 0) {
-                       drm_dbg_kms(mgr->dev, "%s: failed to read DPCD, ret %d\n",
-                                   mgr->aux->name, ret);
-                       goto out_unlock;
-               }
-
-               lane_count = min_t(int, mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK, mgr->max_lane_count);
-               link_rate = min_t(int, drm_dp_bw_code_to_link_rate(mgr->dpcd[1]), mgr->max_link_rate);
-               mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr,
-                                                       link_rate,
-                                                       lane_count);
-               if (mgr->pbn_div == 0) {
-                       ret = -EINVAL;
-                       goto out_unlock;
-               }
-
-               /* add initial branch device at LCT 1 */
-               mstb = drm_dp_add_mst_branch_device(1, NULL);
-               if (mstb == NULL) {
-                       ret = -ENOMEM;
-                       goto out_unlock;
-               }
-               mstb->mgr = mgr;
-
-               /* give this the main reference */
-               mgr->mst_primary = mstb;
-               drm_dp_mst_topology_get_mstb(mgr->mst_primary);
-
-               ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
-                                        DP_MST_EN |
-                                        DP_UP_REQ_EN |
-                                        DP_UPSTREAM_IS_SRC);
-               if (ret < 0)
-                       goto out_unlock;
-
-               reset_pay.start_slot = 0;
-               reset_pay.num_slots = 0x3f;
-               drm_dp_dpcd_write_payload(mgr, 0, &reset_pay);
-
-               queue_work(system_long_wq, &mgr->work);
-
-               ret = 0;
-       } else {
-               /* disable MST on the device */
-               mstb = mgr->mst_primary;
-               mgr->mst_primary = NULL;
-               /* this can fail if the device is gone */
-               drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0);
-               ret = 0;
-               memset(mgr->payloads, 0,
-                      mgr->max_payloads * sizeof(mgr->payloads[0]));
-               memset(mgr->proposed_vcpis, 0,
-                      mgr->max_payloads * sizeof(mgr->proposed_vcpis[0]));
-               mgr->payload_mask = 0;
-               set_bit(0, &mgr->payload_mask);
-               mgr->vcpi_mask = 0;
-               mgr->payload_id_table_cleared = false;
-       }
-
-out_unlock:
-       mutex_unlock(&mgr->lock);
-       mutex_unlock(&mgr->payload_lock);
-       if (mstb)
-               drm_dp_mst_topology_put_mstb(mstb);
-       return ret;
-
-}
-EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst);
-
-static void
-drm_dp_mst_topology_mgr_invalidate_mstb(struct drm_dp_mst_branch *mstb)
-{
-       struct drm_dp_mst_port *port;
-
-       /* The link address will need to be re-sent on resume */
-       mstb->link_address_sent = false;
-
-       list_for_each_entry(port, &mstb->ports, next)
-               if (port->mstb)
-                       drm_dp_mst_topology_mgr_invalidate_mstb(port->mstb);
-}
-
-/**
- * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager
- * @mgr: manager to suspend
- *
- * This function tells the MST device that we can't handle UP messages
- * anymore. This should stop it from sending any since we are suspended.
- */
-void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
-{
-       mutex_lock(&mgr->lock);
-       drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
-                          DP_MST_EN | DP_UPSTREAM_IS_SRC);
-       mutex_unlock(&mgr->lock);
-       flush_work(&mgr->up_req_work);
-       flush_work(&mgr->work);
-       flush_work(&mgr->delayed_destroy_work);
-
-       mutex_lock(&mgr->lock);
-       if (mgr->mst_state && mgr->mst_primary)
-               drm_dp_mst_topology_mgr_invalidate_mstb(mgr->mst_primary);
-       mutex_unlock(&mgr->lock);
-}
-EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
-
-/**
- * drm_dp_mst_topology_mgr_resume() - resume the MST manager
- * @mgr: manager to resume
- * @sync: whether or not to perform topology reprobing synchronously
- *
- * This will fetch DPCD and see if the device is still there,
- * if it is, it will rewrite the MSTM control bits, and return.
- *
- * If the device fails this returns -1, and the driver should do
- * a full MST reprobe, in case we were undocked.
- *
- * During system resume (where it is assumed that the driver will be calling
- * drm_atomic_helper_resume()) this function should be called beforehand with
- * @sync set to true. In contexts like runtime resume where the driver is not
- * expected to be calling drm_atomic_helper_resume(), this function should be
- * called with @sync set to false in order to avoid deadlocking.
- *
- * Returns: -1 if the MST topology was removed while we were suspended, 0
- * otherwise.
- */
-int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
-                                  bool sync)
-{
-       int ret;
-       u8 guid[16];
-
-       mutex_lock(&mgr->lock);
-       if (!mgr->mst_primary)
-               goto out_fail;
-
-       ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd,
-                              DP_RECEIVER_CAP_SIZE);
-       if (ret != DP_RECEIVER_CAP_SIZE) {
-               drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
-               goto out_fail;
-       }
-
-       ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
-                                DP_MST_EN |
-                                DP_UP_REQ_EN |
-                                DP_UPSTREAM_IS_SRC);
-       if (ret < 0) {
-               drm_dbg_kms(mgr->dev, "mst write failed - undocked during suspend?\n");
-               goto out_fail;
-       }
-
-       /* Some hubs forget their guids after they resume */
-       ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
-       if (ret != 16) {
-               drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
-               goto out_fail;
-       }
-
-       ret = drm_dp_check_mstb_guid(mgr->mst_primary, guid);
-       if (ret) {
-               drm_dbg_kms(mgr->dev, "check mstb failed - undocked during suspend?\n");
-               goto out_fail;
-       }
-
-       /*
-        * For the final step of resuming the topology, we need to bring the
-        * state of our in-memory topology back into sync with reality. So,
-        * restart the probing process as if we're probing a new hub
-        */
-       queue_work(system_long_wq, &mgr->work);
-       mutex_unlock(&mgr->lock);
-
-       if (sync) {
-               drm_dbg_kms(mgr->dev,
-                           "Waiting for link probe work to finish re-syncing topology...\n");
-               flush_work(&mgr->work);
-       }
-
-       return 0;
-
-out_fail:
-       mutex_unlock(&mgr->lock);
-       return -1;
-}
-EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume);
-
-static bool
-drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up,
-                     struct drm_dp_mst_branch **mstb)
-{
-       int len;
-       u8 replyblock[32];
-       int replylen, curreply;
-       int ret;
-       u8 hdrlen;
-       struct drm_dp_sideband_msg_hdr hdr;
-       struct drm_dp_sideband_msg_rx *msg =
-               up ? &mgr->up_req_recv : &mgr->down_rep_recv;
-       int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE :
-                          DP_SIDEBAND_MSG_DOWN_REP_BASE;
-
-       if (!up)
-               *mstb = NULL;
-
-       len = min(mgr->max_dpcd_transaction_bytes, 16);
-       ret = drm_dp_dpcd_read(mgr->aux, basereg, replyblock, len);
-       if (ret != len) {
-               drm_dbg_kms(mgr->dev, "failed to read DPCD down rep %d %d\n", len, ret);
-               return false;
-       }
-
-       ret = drm_dp_decode_sideband_msg_hdr(mgr, &hdr, replyblock, len, &hdrlen);
-       if (ret == false) {
-               print_hex_dump(KERN_DEBUG, "failed hdr", DUMP_PREFIX_NONE, 16,
-                              1, replyblock, len, false);
-               drm_dbg_kms(mgr->dev, "ERROR: failed header\n");
-               return false;
-       }
-
-       if (!up) {
-               /* Caller is responsible for giving back this reference */
-               *mstb = drm_dp_get_mst_branch_device(mgr, hdr.lct, hdr.rad);
-               if (!*mstb) {
-                       drm_dbg_kms(mgr->dev, "Got MST reply from unknown device %d\n", hdr.lct);
-                       return false;
-               }
-       }
-
-       if (!drm_dp_sideband_msg_set_header(msg, &hdr, hdrlen)) {
-               drm_dbg_kms(mgr->dev, "sideband msg set header failed %d\n", replyblock[0]);
-               return false;
-       }
-
-       replylen = min(msg->curchunk_len, (u8)(len - hdrlen));
-       ret = drm_dp_sideband_append_payload(msg, replyblock + hdrlen, replylen);
-       if (!ret) {
-               drm_dbg_kms(mgr->dev, "sideband msg build failed %d\n", replyblock[0]);
-               return false;
-       }
-
-       replylen = msg->curchunk_len + msg->curchunk_hdrlen - len;
-       curreply = len;
-       while (replylen > 0) {
-               len = min3(replylen, mgr->max_dpcd_transaction_bytes, 16);
-               ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply,
-                                   replyblock, len);
-               if (ret != len) {
-                       drm_dbg_kms(mgr->dev, "failed to read a chunk (len %d, ret %d)\n",
-                                   len, ret);
-                       return false;
-               }
-
-               ret = drm_dp_sideband_append_payload(msg, replyblock, len);
-               if (!ret) {
-                       drm_dbg_kms(mgr->dev, "failed to build sideband msg\n");
-                       return false;
-               }
-
-               curreply += len;
-               replylen -= len;
-       }
-       return true;
-}
-
-static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
-{
-       struct drm_dp_sideband_msg_tx *txmsg;
-       struct drm_dp_mst_branch *mstb = NULL;
-       struct drm_dp_sideband_msg_rx *msg = &mgr->down_rep_recv;
-
-       if (!drm_dp_get_one_sb_msg(mgr, false, &mstb))
-               goto out;
-
-       /* Multi-packet message transmission, don't clear the reply */
-       if (!msg->have_eomt)
-               goto out;
-
-       /* find the message */
-       mutex_lock(&mgr->qlock);
-       txmsg = list_first_entry_or_null(&mgr->tx_msg_downq,
-                                        struct drm_dp_sideband_msg_tx, next);
-       mutex_unlock(&mgr->qlock);
-
-       /* Were we actually expecting a response, and from this mstb? */
-       if (!txmsg || txmsg->dst != mstb) {
-               struct drm_dp_sideband_msg_hdr *hdr;
-
-               hdr = &msg->initial_hdr;
-               drm_dbg_kms(mgr->dev, "Got MST reply with no msg %p %d %d %02x %02x\n",
-                           mstb, hdr->seqno, hdr->lct, hdr->rad[0], msg->msg[0]);
-               goto out_clear_reply;
-       }
-
-       drm_dp_sideband_parse_reply(mgr, msg, &txmsg->reply);
-
-       if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
-               drm_dbg_kms(mgr->dev,
-                           "Got NAK reply: req 0x%02x (%s), reason 0x%02x (%s), nak data 0x%02x\n",
-                           txmsg->reply.req_type,
-                           drm_dp_mst_req_type_str(txmsg->reply.req_type),
-                           txmsg->reply.u.nak.reason,
-                           drm_dp_mst_nak_reason_str(txmsg->reply.u.nak.reason),
-                           txmsg->reply.u.nak.nak_data);
-       }
-
-       memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx));
-       drm_dp_mst_topology_put_mstb(mstb);
-
-       mutex_lock(&mgr->qlock);
-       txmsg->state = DRM_DP_SIDEBAND_TX_RX;
-       list_del(&txmsg->next);
-       mutex_unlock(&mgr->qlock);
-
-       wake_up_all(&mgr->tx_waitq);
-
-       return 0;
-
-out_clear_reply:
-       memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx));
-out:
-       if (mstb)
-               drm_dp_mst_topology_put_mstb(mstb);
-
-       return 0;
-}
-
-static inline bool
-drm_dp_mst_process_up_req(struct drm_dp_mst_topology_mgr *mgr,
-                         struct drm_dp_pending_up_req *up_req)
-{
-       struct drm_dp_mst_branch *mstb = NULL;
-       struct drm_dp_sideband_msg_req_body *msg = &up_req->msg;
-       struct drm_dp_sideband_msg_hdr *hdr = &up_req->hdr;
-       bool hotplug = false;
-
-       if (hdr->broadcast) {
-               const u8 *guid = NULL;
-
-               if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY)
-                       guid = msg->u.conn_stat.guid;
-               else if (msg->req_type == DP_RESOURCE_STATUS_NOTIFY)
-                       guid = msg->u.resource_stat.guid;
-
-               if (guid)
-                       mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid);
-       } else {
-               mstb = drm_dp_get_mst_branch_device(mgr, hdr->lct, hdr->rad);
-       }
-
-       if (!mstb) {
-               drm_dbg_kms(mgr->dev, "Got MST reply from unknown device %d\n", hdr->lct);
-               return false;
-       }
-
-       /* TODO: Add missing handler for DP_RESOURCE_STATUS_NOTIFY events */
-       if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY) {
-               drm_dp_mst_handle_conn_stat(mstb, &msg->u.conn_stat);
-               hotplug = true;
-       }
-
-       drm_dp_mst_topology_put_mstb(mstb);
-       return hotplug;
-}
-
-static void drm_dp_mst_up_req_work(struct work_struct *work)
-{
-       struct drm_dp_mst_topology_mgr *mgr =
-               container_of(work, struct drm_dp_mst_topology_mgr,
-                            up_req_work);
-       struct drm_dp_pending_up_req *up_req;
-       bool send_hotplug = false;
-
-       mutex_lock(&mgr->probe_lock);
-       while (true) {
-               mutex_lock(&mgr->up_req_lock);
-               up_req = list_first_entry_or_null(&mgr->up_req_list,
-                                                 struct drm_dp_pending_up_req,
-                                                 next);
-               if (up_req)
-                       list_del(&up_req->next);
-               mutex_unlock(&mgr->up_req_lock);
-
-               if (!up_req)
-                       break;
-
-               send_hotplug |= drm_dp_mst_process_up_req(mgr, up_req);
-               kfree(up_req);
-       }
-       mutex_unlock(&mgr->probe_lock);
-
-       if (send_hotplug)
-               drm_kms_helper_hotplug_event(mgr->dev);
-}
-
-static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
-{
-       struct drm_dp_pending_up_req *up_req;
-
-       if (!drm_dp_get_one_sb_msg(mgr, true, NULL))
-               goto out;
-
-       if (!mgr->up_req_recv.have_eomt)
-               return 0;
-
-       up_req = kzalloc(sizeof(*up_req), GFP_KERNEL);
-       if (!up_req)
-               return -ENOMEM;
-
-       INIT_LIST_HEAD(&up_req->next);
-
-       drm_dp_sideband_parse_req(mgr, &mgr->up_req_recv, &up_req->msg);
-
-       if (up_req->msg.req_type != DP_CONNECTION_STATUS_NOTIFY &&
-           up_req->msg.req_type != DP_RESOURCE_STATUS_NOTIFY) {
-               drm_dbg_kms(mgr->dev, "Received unknown up req type, ignoring: %x\n",
-                           up_req->msg.req_type);
-               kfree(up_req);
-               goto out;
-       }
-
-       drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, up_req->msg.req_type,
-                                false);
-
-       if (up_req->msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
-               const struct drm_dp_connection_status_notify *conn_stat =
-                       &up_req->msg.u.conn_stat;
-
-               drm_dbg_kms(mgr->dev, "Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n",
-                           conn_stat->port_number,
-                           conn_stat->legacy_device_plug_status,
-                           conn_stat->displayport_device_plug_status,
-                           conn_stat->message_capability_status,
-                           conn_stat->input_port,
-                           conn_stat->peer_device_type);
-       } else if (up_req->msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
-               const struct drm_dp_resource_status_notify *res_stat =
-                       &up_req->msg.u.resource_stat;
-
-               drm_dbg_kms(mgr->dev, "Got RSN: pn: %d avail_pbn %d\n",
-                           res_stat->port_number,
-                           res_stat->available_pbn);
-       }
-
-       up_req->hdr = mgr->up_req_recv.initial_hdr;
-       mutex_lock(&mgr->up_req_lock);
-       list_add_tail(&up_req->next, &mgr->up_req_list);
-       mutex_unlock(&mgr->up_req_lock);
-       queue_work(system_long_wq, &mgr->up_req_work);
-
-out:
-       memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-       return 0;
-}
-
-/**
- * drm_dp_mst_hpd_irq() - MST hotplug IRQ notify
- * @mgr: manager to notify irq for.
- * @esi: 4 bytes from SINK_COUNT_ESI
- * @handled: whether the hpd interrupt was consumed or not
- *
- * This should be called from the driver when it detects a short IRQ,
- * along with the value of the DEVICE_SERVICE_IRQ_VECTOR_ESI0. The
- * topology manager will process the sideband messages received as a result
- * of this.
- */
-int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled)
-{
-       int ret = 0;
-       int sc;
-       *handled = false;
-       sc = esi[0] & 0x3f;
-
-       if (sc != mgr->sink_count) {
-               mgr->sink_count = sc;
-               *handled = true;
-       }
-
-       if (esi[1] & DP_DOWN_REP_MSG_RDY) {
-               ret = drm_dp_mst_handle_down_rep(mgr);
-               *handled = true;
-       }
-
-       if (esi[1] & DP_UP_REQ_MSG_RDY) {
-               ret |= drm_dp_mst_handle_up_req(mgr);
-               *handled = true;
-       }
-
-       drm_dp_mst_kick_tx(mgr);
-       return ret;
-}
-EXPORT_SYMBOL(drm_dp_mst_hpd_irq);
-
-/**
- * drm_dp_mst_detect_port() - get connection status for an MST port
- * @connector: DRM connector for this port
- * @ctx: The acquisition context to use for grabbing locks
- * @mgr: manager for this port
- * @port: pointer to a port
- *
- * This returns the current connection state for a port.
- */
-int
-drm_dp_mst_detect_port(struct drm_connector *connector,
-                      struct drm_modeset_acquire_ctx *ctx,
-                      struct drm_dp_mst_topology_mgr *mgr,
-                      struct drm_dp_mst_port *port)
-{
-       int ret;
-
-       /* we need to search for the port in the mgr in case it's gone */
-       port = drm_dp_mst_topology_get_port_validated(mgr, port);
-       if (!port)
-               return connector_status_disconnected;
-
-       ret = drm_modeset_lock(&mgr->base.lock, ctx);
-       if (ret)
-               goto out;
-
-       ret = connector_status_disconnected;
-
-       if (!port->ddps)
-               goto out;
-
-       switch (port->pdt) {
-       case DP_PEER_DEVICE_NONE:
-               break;
-       case DP_PEER_DEVICE_MST_BRANCHING:
-               if (!port->mcs)
-                       ret = connector_status_connected;
-               break;
-
-       case DP_PEER_DEVICE_SST_SINK:
-               ret = connector_status_connected;
-               /* for logical ports - cache the EDID */
-               if (port->port_num >= DP_MST_LOGICAL_PORT_0 && !port->cached_edid)
-                       port->cached_edid = drm_get_edid(connector, &port->aux.ddc);
-               break;
-       case DP_PEER_DEVICE_DP_LEGACY_CONV:
-               if (port->ldps)
-                       ret = connector_status_connected;
-               break;
-       }
-out:
-       drm_dp_mst_topology_put_port(port);
-       return ret;
-}
-EXPORT_SYMBOL(drm_dp_mst_detect_port);
-
-/**
- * drm_dp_mst_get_edid() - get EDID for an MST port
- * @connector: toplevel connector to get EDID for
- * @mgr: manager for this port
- * @port: unverified pointer to a port.
- *
- * This returns an EDID for the port connected to a connector,
- * It validates the pointer still exists so the caller doesn't require a
- * reference.
- */
-struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
-{
-       struct edid *edid = NULL;
-
-       /* we need to search for the port in the mgr in case it's gone */
-       port = drm_dp_mst_topology_get_port_validated(mgr, port);
-       if (!port)
-               return NULL;
-
-       if (port->cached_edid)
-               edid = drm_edid_duplicate(port->cached_edid);
-       else {
-               edid = drm_get_edid(connector, &port->aux.ddc);
-       }
-       port->has_audio = drm_detect_monitor_audio(edid);
-       drm_dp_mst_topology_put_port(port);
-       return edid;
-}
-EXPORT_SYMBOL(drm_dp_mst_get_edid);
-
-/**
- * drm_dp_find_vcpi_slots() - Find VCPI slots for this PBN value
- * @mgr: manager to use
- * @pbn: payload bandwidth to convert into slots.
- *
- * Calculate the number of VCPI slots that will be required for the given PBN
- * value. This function is deprecated, and should not be used in atomic
- * drivers.
- *
- * RETURNS:
- * The total slots required for this port, or error.
- */
-int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr,
-                          int pbn)
-{
-       int num_slots;
-
-       num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
-
-       /* max. time slots - one slot for MTP header */
-       if (num_slots > 63)
-               return -ENOSPC;
-       return num_slots;
-}
-EXPORT_SYMBOL(drm_dp_find_vcpi_slots);
-
-static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
-                           struct drm_dp_vcpi *vcpi, int pbn, int slots)
-{
-       int ret;
-
-       vcpi->pbn = pbn;
-       vcpi->aligned_pbn = slots * mgr->pbn_div;
-       vcpi->num_slots = slots;
-
-       ret = drm_dp_mst_assign_payload_id(mgr, vcpi);
-       if (ret < 0)
-               return ret;
-       return 0;
-}
-
-/**
- * drm_dp_atomic_find_vcpi_slots() - Find and add VCPI slots to the state
- * @state: global atomic state
- * @mgr: MST topology manager for the port
- * @port: port to find vcpi slots for
- * @pbn: bandwidth required for the mode in PBN
- * @pbn_div: divider for DSC mode that takes FEC into account
- *
- * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
- * may have had. Any atomic drivers which support MST must call this function
- * in their &drm_encoder_helper_funcs.atomic_check() callback to change the
- * current VCPI allocation for the new state, but only when
- * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set
- * to ensure compatibility with userspace applications that still use the
- * legacy modesetting UAPI.
- *
- * Allocations set by this function are not checked against the bandwidth
- * restraints of @mgr until the driver calls drm_dp_mst_atomic_check().
- *
- * Additionally, it is OK to call this function multiple times on the same
- * @port as needed. It is not OK however, to call this function and
- * drm_dp_atomic_release_vcpi_slots() in the same atomic check phase.
- *
- * See also:
- * drm_dp_atomic_release_vcpi_slots()
- * drm_dp_mst_atomic_check()
- *
- * Returns:
- * Total slots in the atomic state assigned for this port, or a negative error
- * code if the port no longer exists
- */
-int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
-                                 struct drm_dp_mst_topology_mgr *mgr,
-                                 struct drm_dp_mst_port *port, int pbn,
-                                 int pbn_div)
-{
-       struct drm_dp_mst_topology_state *topology_state;
-       struct drm_dp_vcpi_allocation *pos, *vcpi = NULL;
-       int prev_slots, prev_bw, req_slots;
-
-       topology_state = drm_atomic_get_mst_topology_state(state, mgr);
-       if (IS_ERR(topology_state))
-               return PTR_ERR(topology_state);
-
-       /* Find the current allocation for this port, if any */
-       list_for_each_entry(pos, &topology_state->vcpis, next) {
-               if (pos->port == port) {
-                       vcpi = pos;
-                       prev_slots = vcpi->vcpi;
-                       prev_bw = vcpi->pbn;
-
-                       /*
-                        * This should never happen, unless the driver tries
-                        * releasing and allocating the same VCPI allocation,
-                        * which is an error
-                        */
-                       if (WARN_ON(!prev_slots)) {
-                               drm_err(mgr->dev,
-                                       "cannot allocate and release VCPI on [MST PORT:%p] in the same state\n",
-                                       port);
-                               return -EINVAL;
-                       }
-
-                       break;
-               }
-       }
-       if (!vcpi) {
-               prev_slots = 0;
-               prev_bw = 0;
-       }
-
-       if (pbn_div <= 0)
-               pbn_div = mgr->pbn_div;
-
-       req_slots = DIV_ROUND_UP(pbn, pbn_div);
-
-       drm_dbg_atomic(mgr->dev, "[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n",
-                      port->connector->base.id, port->connector->name,
-                      port, prev_slots, req_slots);
-       drm_dbg_atomic(mgr->dev, "[CONNECTOR:%d:%s] [MST PORT:%p] PBN %d -> %d\n",
-                      port->connector->base.id, port->connector->name,
-                      port, prev_bw, pbn);
-
-       /* Add the new allocation to the state */
-       if (!vcpi) {
-               vcpi = kzalloc(sizeof(*vcpi), GFP_KERNEL);
-               if (!vcpi)
-                       return -ENOMEM;
-
-               drm_dp_mst_get_port_malloc(port);
-               vcpi->port = port;
-               list_add(&vcpi->next, &topology_state->vcpis);
-       }
-       vcpi->vcpi = req_slots;
-       vcpi->pbn = pbn;
-
-       return req_slots;
-}
-EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);
-
-/**
- * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots
- * @state: global atomic state
- * @mgr: MST topology manager for the port
- * @port: The port to release the VCPI slots from
- *
- * Releases any VCPI slots that have been allocated to a port in the atomic
- * state. Any atomic drivers which support MST must call this function in
- * their &drm_connector_helper_funcs.atomic_check() callback when the
- * connector will no longer have VCPI allocated (e.g. because its CRTC was
- * removed) when it had VCPI allocated in the previous atomic state.
- *
- * It is OK to call this even if @port has been removed from the system.
- * Additionally, it is OK to call this function multiple times on the same
- * @port as needed. It is not OK however, to call this function and
- * drm_dp_atomic_find_vcpi_slots() on the same @port in a single atomic check
- * phase.
- *
- * See also:
- * drm_dp_atomic_find_vcpi_slots()
- * drm_dp_mst_atomic_check()
- *
- * Returns:
- * 0 if all slots for this port were added back to
- * &drm_dp_mst_topology_state.avail_slots or negative error code
- */
-int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
-                                    struct drm_dp_mst_topology_mgr *mgr,
-                                    struct drm_dp_mst_port *port)
-{
-       struct drm_dp_mst_topology_state *topology_state;
-       struct drm_dp_vcpi_allocation *pos;
-       bool found = false;
-
-       topology_state = drm_atomic_get_mst_topology_state(state, mgr);
-       if (IS_ERR(topology_state))
-               return PTR_ERR(topology_state);
-
-       list_for_each_entry(pos, &topology_state->vcpis, next) {
-               if (pos->port == port) {
-                       found = true;
-                       break;
-               }
-       }
-       if (WARN_ON(!found)) {
-               drm_err(mgr->dev, "no VCPI for [MST PORT:%p] found in mst state %p\n",
-                       port, &topology_state->base);
-               return -EINVAL;
-       }
-
-       drm_dbg_atomic(mgr->dev, "[MST PORT:%p] VCPI %d -> 0\n", port, pos->vcpi);
-       if (pos->vcpi) {
-               drm_dp_mst_put_port_malloc(port);
-               pos->vcpi = 0;
-               pos->pbn = 0;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
-
-/**
- * drm_dp_mst_update_slots() - updates the slot info depending on the DP ecoding format
- * @mst_state: mst_state to update
- * @link_encoding_cap: the ecoding format on the link
- */
-void drm_dp_mst_update_slots(struct drm_dp_mst_topology_state *mst_state, uint8_t link_encoding_cap)
-{
-       if (link_encoding_cap == DP_CAP_ANSI_128B132B) {
-               mst_state->total_avail_slots = 64;
-               mst_state->start_slot = 0;
-       } else {
-               mst_state->total_avail_slots = 63;
-               mst_state->start_slot = 1;
-       }
-
-       DRM_DEBUG_KMS("%s encoding format on mst_state 0x%p\n",
-                     (link_encoding_cap == DP_CAP_ANSI_128B132B) ? "128b/132b":"8b/10b",
-                     mst_state);
-}
-EXPORT_SYMBOL(drm_dp_mst_update_slots);
-
-/**
- * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
- * @mgr: manager for this port
- * @port: port to allocate a virtual channel for.
- * @pbn: payload bandwidth number to request
- * @slots: returned number of slots for this PBN.
- */
-bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
-                             struct drm_dp_mst_port *port, int pbn, int slots)
-{
-       int ret;
-
-       if (slots < 0)
-               return false;
-
-       port = drm_dp_mst_topology_get_port_validated(mgr, port);
-       if (!port)
-               return false;
-
-       if (port->vcpi.vcpi > 0) {
-               drm_dbg_kms(mgr->dev,
-                           "payload: vcpi %d already allocated for pbn %d - requested pbn %d\n",
-                           port->vcpi.vcpi, port->vcpi.pbn, pbn);
-               if (pbn == port->vcpi.pbn) {
-                       drm_dp_mst_topology_put_port(port);
-                       return true;
-               }
-       }
-
-       ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn, slots);
-       if (ret) {
-               drm_dbg_kms(mgr->dev, "failed to init vcpi slots=%d ret=%d\n",
-                           DIV_ROUND_UP(pbn, mgr->pbn_div), ret);
-               drm_dp_mst_topology_put_port(port);
-               goto out;
-       }
-       drm_dbg_kms(mgr->dev, "initing vcpi for pbn=%d slots=%d\n", pbn, port->vcpi.num_slots);
-
-       /* Keep port allocated until its payload has been removed */
-       drm_dp_mst_get_port_malloc(port);
-       drm_dp_mst_topology_put_port(port);
-       return true;
-out:
-       return false;
-}
-EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi);
-
-int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
-{
-       int slots = 0;
-
-       port = drm_dp_mst_topology_get_port_validated(mgr, port);
-       if (!port)
-               return slots;
-
-       slots = port->vcpi.num_slots;
-       drm_dp_mst_topology_put_port(port);
-       return slots;
-}
-EXPORT_SYMBOL(drm_dp_mst_get_vcpi_slots);
-
-/**
- * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI
- * @mgr: manager for this port
- * @port: unverified pointer to a port.
- *
- * This just resets the number of slots for the ports VCPI for later programming.
- */
-void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
-{
-       /*
-        * A port with VCPI will remain allocated until its VCPI is
-        * released, no verified ref needed
-        */
-
-       port->vcpi.num_slots = 0;
-}
-EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots);
-
-/**
- * drm_dp_mst_deallocate_vcpi() - deallocate a VCPI
- * @mgr: manager for this port
- * @port: port to deallocate vcpi for
- *
- * This can be called unconditionally, regardless of whether
- * drm_dp_mst_allocate_vcpi() succeeded or not.
- */
-void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
-                               struct drm_dp_mst_port *port)
-{
-       bool skip;
-
-       if (!port->vcpi.vcpi)
-               return;
-
-       mutex_lock(&mgr->lock);
-       skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary);
-       mutex_unlock(&mgr->lock);
-
-       if (skip)
-               return;
-
-       drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
-       port->vcpi.num_slots = 0;
-       port->vcpi.pbn = 0;
-       port->vcpi.aligned_pbn = 0;
-       port->vcpi.vcpi = 0;
-       drm_dp_mst_put_port_malloc(port);
-}
-EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi);
-
-static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
-                                    int id, struct drm_dp_payload *payload)
-{
-       u8 payload_alloc[3], status;
-       int ret;
-       int retries = 0;
-
-       drm_dp_dpcd_writeb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS,
-                          DP_PAYLOAD_TABLE_UPDATED);
-
-       payload_alloc[0] = id;
-       payload_alloc[1] = payload->start_slot;
-       payload_alloc[2] = payload->num_slots;
-
-       ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3);
-       if (ret != 3) {
-               drm_dbg_kms(mgr->dev, "failed to write payload allocation %d\n", ret);
-               goto fail;
-       }
-
-retry:
-       ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
-       if (ret < 0) {
-               drm_dbg_kms(mgr->dev, "failed to read payload table status %d\n", ret);
-               goto fail;
-       }
-
-       if (!(status & DP_PAYLOAD_TABLE_UPDATED)) {
-               retries++;
-               if (retries < 20) {
-                       usleep_range(10000, 20000);
-                       goto retry;
-               }
-               drm_dbg_kms(mgr->dev, "status not set after read payload table status %d\n",
-                           status);
-               ret = -EINVAL;
-               goto fail;
-       }
-       ret = 0;
-fail:
-       return ret;
-}
-
-static int do_get_act_status(struct drm_dp_aux *aux)
-{
-       int ret;
-       u8 status;
-
-       ret = drm_dp_dpcd_readb(aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
-       if (ret < 0)
-               return ret;
-
-       return status;
-}
-
-/**
- * drm_dp_check_act_status() - Polls for ACT handled status.
- * @mgr: manager to use
- *
- * Tries waiting for the MST hub to finish updating it's payload table by
- * polling for the ACT handled bit for up to 3 seconds (yes-some hubs really
- * take that long).
- *
- * Returns:
- * 0 if the ACT was handled in time, negative error code on failure.
- */
-int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr)
-{
-       /*
-        * There doesn't seem to be any recommended retry count or timeout in
-        * the MST specification. Since some hubs have been observed to take
-        * over 1 second to update their payload allocations under certain
-        * conditions, we use a rather large timeout value.
-        */
-       const int timeout_ms = 3000;
-       int ret, status;
-
-       ret = readx_poll_timeout(do_get_act_status, mgr->aux, status,
-                                status & DP_PAYLOAD_ACT_HANDLED || status < 0,
-                                200, timeout_ms * USEC_PER_MSEC);
-       if (ret < 0 && status >= 0) {
-               drm_err(mgr->dev, "Failed to get ACT after %dms, last status: %02x\n",
-                       timeout_ms, status);
-               return -EINVAL;
-       } else if (status < 0) {
-               /*
-                * Failure here isn't unexpected - the hub may have
-                * just been unplugged
-                */
-               drm_dbg_kms(mgr->dev, "Failed to read payload table status: %d\n", status);
-               return status;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_check_act_status);
-
-/**
- * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode.
- * @clock: dot clock for the mode
- * @bpp: bpp for the mode.
- * @dsc: DSC mode. If true, bpp has units of 1/16 of a bit per pixel
- *
- * This uses the formula in the spec to calculate the PBN value for a mode.
- */
-int drm_dp_calc_pbn_mode(int clock, int bpp, bool dsc)
-{
-       /*
-        * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006
-        * The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on
-        * common multiplier to render an integer PBN for all link rate/lane
-        * counts combinations
-        * calculate
-        * peak_kbps *= (1006/1000)
-        * peak_kbps *= (64/54)
-        * peak_kbps *= 8    convert to bytes
-        *
-        * If the bpp is in units of 1/16, further divide by 16. Put this
-        * factor in the numerator rather than the denominator to avoid
-        * integer overflow
-        */
-
-       if (dsc)
-               return DIV_ROUND_UP_ULL(mul_u32_u32(clock * (bpp / 16), 64 * 1006),
-                                       8 * 54 * 1000 * 1000);
-
-       return DIV_ROUND_UP_ULL(mul_u32_u32(clock * bpp, 64 * 1006),
-                               8 * 54 * 1000 * 1000);
-}
-EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
-
-/* we want to kick the TX after we've ack the up/down IRQs. */
-static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr)
-{
-       queue_work(system_long_wq, &mgr->tx_work);
-}
-
-/*
- * Helper function for parsing DP device types into convenient strings
- * for use with dp_mst_topology
- */
-static const char *pdt_to_string(u8 pdt)
-{
-       switch (pdt) {
-       case DP_PEER_DEVICE_NONE:
-               return "NONE";
-       case DP_PEER_DEVICE_SOURCE_OR_SST:
-               return "SOURCE OR SST";
-       case DP_PEER_DEVICE_MST_BRANCHING:
-               return "MST BRANCHING";
-       case DP_PEER_DEVICE_SST_SINK:
-               return "SST SINK";
-       case DP_PEER_DEVICE_DP_LEGACY_CONV:
-               return "DP LEGACY CONV";
-       default:
-               return "ERR";
-       }
-}
-
-static void drm_dp_mst_dump_mstb(struct seq_file *m,
-                                struct drm_dp_mst_branch *mstb)
-{
-       struct drm_dp_mst_port *port;
-       int tabs = mstb->lct;
-       char prefix[10];
-       int i;
-
-       for (i = 0; i < tabs; i++)
-               prefix[i] = '\t';
-       prefix[i] = '\0';
-
-       seq_printf(m, "%smstb - [%p]: num_ports: %d\n", prefix, mstb, mstb->num_ports);
-       list_for_each_entry(port, &mstb->ports, next) {
-               seq_printf(m, "%sport %d - [%p] (%s - %s): ddps: %d, ldps: %d, sdp: %d/%d, fec: %s, conn: %p\n", 
-                          prefix,
-                          port->port_num,
-                          port,
-                          port->input ? "input" : "output",
-                          pdt_to_string(port->pdt),
-                          port->ddps,
-                          port->ldps,
-                          port->num_sdp_streams,
-                          port->num_sdp_stream_sinks,
-                          port->fec_capable ? "true" : "false",
-                          port->connector);
-               if (port->mstb)
-                       drm_dp_mst_dump_mstb(m, port->mstb);
-       }
-}
-
-#define DP_PAYLOAD_TABLE_SIZE          64
-
-static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
-                                 char *buf)
-{
-       int i;
-
-       for (i = 0; i < DP_PAYLOAD_TABLE_SIZE; i += 16) {
-               if (drm_dp_dpcd_read(mgr->aux,
-                                    DP_PAYLOAD_TABLE_UPDATE_STATUS + i,
-                                    &buf[i], 16) != 16)
-                       return false;
-       }
-       return true;
-}
-
-static void fetch_monitor_name(struct drm_dp_mst_topology_mgr *mgr,
-                              struct drm_dp_mst_port *port, char *name,
-                              int namelen)
-{
-       struct edid *mst_edid;
-
-       mst_edid = drm_dp_mst_get_edid(port->connector, mgr, port);
-       drm_edid_get_monitor_name(mst_edid, name, namelen);
-}
-
-/**
- * drm_dp_mst_dump_topology(): dump topology to seq file.
- * @m: seq_file to dump output to
- * @mgr: manager to dump current topology for.
- *
- * helper to dump MST topology to a seq file for debugfs.
- */
-void drm_dp_mst_dump_topology(struct seq_file *m,
-                             struct drm_dp_mst_topology_mgr *mgr)
-{
-       int i;
-       struct drm_dp_mst_port *port;
-
-       mutex_lock(&mgr->lock);
-       if (mgr->mst_primary)
-               drm_dp_mst_dump_mstb(m, mgr->mst_primary);
-
-       /* dump VCPIs */
-       mutex_unlock(&mgr->lock);
-
-       mutex_lock(&mgr->payload_lock);
-       seq_printf(m, "\n*** VCPI Info ***\n");
-       seq_printf(m, "payload_mask: %lx, vcpi_mask: %lx, max_payloads: %d\n", mgr->payload_mask, mgr->vcpi_mask, mgr->max_payloads);
-
-       seq_printf(m, "\n|   idx   |  port # |  vcp_id | # slots |     sink name     |\n");
-       for (i = 0; i < mgr->max_payloads; i++) {
-               if (mgr->proposed_vcpis[i]) {
-                       char name[14];
-
-                       port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
-                       fetch_monitor_name(mgr, port, name, sizeof(name));
-                       seq_printf(m, "%10d%10d%10d%10d%20s\n",
-                                  i,
-                                  port->port_num,
-                                  port->vcpi.vcpi,
-                                  port->vcpi.num_slots,
-                                  (*name != 0) ? name : "Unknown");
-               } else
-                       seq_printf(m, "%6d - Unused\n", i);
-       }
-       seq_printf(m, "\n*** Payload Info ***\n");
-       seq_printf(m, "|   idx   |  state  |  start slot  | # slots |\n");
-       for (i = 0; i < mgr->max_payloads; i++) {
-               seq_printf(m, "%10d%10d%15d%10d\n",
-                          i,
-                          mgr->payloads[i].payload_state,
-                          mgr->payloads[i].start_slot,
-                          mgr->payloads[i].num_slots);
-       }
-       mutex_unlock(&mgr->payload_lock);
-
-       seq_printf(m, "\n*** DPCD Info ***\n");
-       mutex_lock(&mgr->lock);
-       if (mgr->mst_primary) {
-               u8 buf[DP_PAYLOAD_TABLE_SIZE];
-               int ret;
-
-               ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE);
-               if (ret) {
-                       seq_printf(m, "dpcd read failed\n");
-                       goto out;
-               }
-               seq_printf(m, "dpcd: %*ph\n", DP_RECEIVER_CAP_SIZE, buf);
-
-               ret = drm_dp_dpcd_read(mgr->aux, DP_FAUX_CAP, buf, 2);
-               if (ret) {
-                       seq_printf(m, "faux/mst read failed\n");
-                       goto out;
-               }
-               seq_printf(m, "faux/mst: %*ph\n", 2, buf);
-
-               ret = drm_dp_dpcd_read(mgr->aux, DP_MSTM_CTRL, buf, 1);
-               if (ret) {
-                       seq_printf(m, "mst ctrl read failed\n");
-                       goto out;
-               }
-               seq_printf(m, "mst ctrl: %*ph\n", 1, buf);
-
-               /* dump the standard OUI branch header */
-               ret = drm_dp_dpcd_read(mgr->aux, DP_BRANCH_OUI, buf, DP_BRANCH_OUI_HEADER_SIZE);
-               if (ret) {
-                       seq_printf(m, "branch oui read failed\n");
-                       goto out;
-               }
-               seq_printf(m, "branch oui: %*phN devid: ", 3, buf);
-
-               for (i = 0x3; i < 0x8 && buf[i]; i++)
-                       seq_printf(m, "%c", buf[i]);
-               seq_printf(m, " revision: hw: %x.%x sw: %x.%x\n",
-                          buf[0x9] >> 4, buf[0x9] & 0xf, buf[0xa], buf[0xb]);
-               if (dump_dp_payload_table(mgr, buf))
-                       seq_printf(m, "payload table: %*ph\n", DP_PAYLOAD_TABLE_SIZE, buf);
-       }
-
-out:
-       mutex_unlock(&mgr->lock);
-
-}
-EXPORT_SYMBOL(drm_dp_mst_dump_topology);
-
-static void drm_dp_tx_work(struct work_struct *work)
-{
-       struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work);
-
-       mutex_lock(&mgr->qlock);
-       if (!list_empty(&mgr->tx_msg_downq))
-               process_single_down_tx_qlock(mgr);
-       mutex_unlock(&mgr->qlock);
-}
-
-static inline void
-drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
-{
-       drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE, port->mcs);
-
-       if (port->connector) {
-               drm_connector_unregister(port->connector);
-               drm_connector_put(port->connector);
-       }
-
-       drm_dp_mst_put_port_malloc(port);
-}
-
-static inline void
-drm_dp_delayed_destroy_mstb(struct drm_dp_mst_branch *mstb)
-{
-       struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
-       struct drm_dp_mst_port *port, *port_tmp;
-       struct drm_dp_sideband_msg_tx *txmsg, *txmsg_tmp;
-       bool wake_tx = false;
-
-       mutex_lock(&mgr->lock);
-       list_for_each_entry_safe(port, port_tmp, &mstb->ports, next) {
-               list_del(&port->next);
-               drm_dp_mst_topology_put_port(port);
-       }
-       mutex_unlock(&mgr->lock);
-
-       /* drop any tx slot msg */
-       mutex_lock(&mstb->mgr->qlock);
-       list_for_each_entry_safe(txmsg, txmsg_tmp, &mgr->tx_msg_downq, next) {
-               if (txmsg->dst != mstb)
-                       continue;
-
-               txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
-               list_del(&txmsg->next);
-               wake_tx = true;
-       }
-       mutex_unlock(&mstb->mgr->qlock);
-
-       if (wake_tx)
-               wake_up_all(&mstb->mgr->tx_waitq);
-
-       drm_dp_mst_put_mstb_malloc(mstb);
-}
-
-static void drm_dp_delayed_destroy_work(struct work_struct *work)
-{
-       struct drm_dp_mst_topology_mgr *mgr =
-               container_of(work, struct drm_dp_mst_topology_mgr,
-                            delayed_destroy_work);
-       bool send_hotplug = false, go_again;
-
-       /*
-        * Not a regular list traverse as we have to drop the destroy
-        * connector lock before destroying the mstb/port, to avoid AB->BA
-        * ordering between this lock and the config mutex.
-        */
-       do {
-               go_again = false;
-
-               for (;;) {
-                       struct drm_dp_mst_branch *mstb;
-
-                       mutex_lock(&mgr->delayed_destroy_lock);
-                       mstb = list_first_entry_or_null(&mgr->destroy_branch_device_list,
-                                                       struct drm_dp_mst_branch,
-                                                       destroy_next);
-                       if (mstb)
-                               list_del(&mstb->destroy_next);
-                       mutex_unlock(&mgr->delayed_destroy_lock);
-
-                       if (!mstb)
-                               break;
-
-                       drm_dp_delayed_destroy_mstb(mstb);
-                       go_again = true;
-               }
-
-               for (;;) {
-                       struct drm_dp_mst_port *port;
-
-                       mutex_lock(&mgr->delayed_destroy_lock);
-                       port = list_first_entry_or_null(&mgr->destroy_port_list,
-                                                       struct drm_dp_mst_port,
-                                                       next);
-                       if (port)
-                               list_del(&port->next);
-                       mutex_unlock(&mgr->delayed_destroy_lock);
-
-                       if (!port)
-                               break;
-
-                       drm_dp_delayed_destroy_port(port);
-                       send_hotplug = true;
-                       go_again = true;
-               }
-       } while (go_again);
-
-       if (send_hotplug)
-               drm_kms_helper_hotplug_event(mgr->dev);
-}
-
-static struct drm_private_state *
-drm_dp_mst_duplicate_state(struct drm_private_obj *obj)
-{
-       struct drm_dp_mst_topology_state *state, *old_state =
-               to_dp_mst_topology_state(obj->state);
-       struct drm_dp_vcpi_allocation *pos, *vcpi;
-
-       state = kmemdup(old_state, sizeof(*state), GFP_KERNEL);
-       if (!state)
-               return NULL;
-
-       __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
-
-       INIT_LIST_HEAD(&state->vcpis);
-
-       list_for_each_entry(pos, &old_state->vcpis, next) {
-               /* Prune leftover freed VCPI allocations */
-               if (!pos->vcpi)
-                       continue;
-
-               vcpi = kmemdup(pos, sizeof(*vcpi), GFP_KERNEL);
-               if (!vcpi)
-                       goto fail;
-
-               drm_dp_mst_get_port_malloc(vcpi->port);
-               list_add(&vcpi->next, &state->vcpis);
-       }
-
-       return &state->base;
-
-fail:
-       list_for_each_entry_safe(pos, vcpi, &state->vcpis, next) {
-               drm_dp_mst_put_port_malloc(pos->port);
-               kfree(pos);
-       }
-       kfree(state);
-
-       return NULL;
-}
-
-static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,
-                                    struct drm_private_state *state)
-{
-       struct drm_dp_mst_topology_state *mst_state =
-               to_dp_mst_topology_state(state);
-       struct drm_dp_vcpi_allocation *pos, *tmp;
-
-       list_for_each_entry_safe(pos, tmp, &mst_state->vcpis, next) {
-               /* We only keep references to ports with non-zero VCPIs */
-               if (pos->vcpi)
-                       drm_dp_mst_put_port_malloc(pos->port);
-               kfree(pos);
-       }
-
-       kfree(mst_state);
-}
-
-static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port,
-                                                struct drm_dp_mst_branch *branch)
-{
-       while (port->parent) {
-               if (port->parent == branch)
-                       return true;
-
-               if (port->parent->port_parent)
-                       port = port->parent->port_parent;
-               else
-                       break;
-       }
-       return false;
-}
-
-static int
-drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port,
-                                     struct drm_dp_mst_topology_state *state);
-
-static int
-drm_dp_mst_atomic_check_mstb_bw_limit(struct drm_dp_mst_branch *mstb,
-                                     struct drm_dp_mst_topology_state *state)
-{
-       struct drm_dp_vcpi_allocation *vcpi;
-       struct drm_dp_mst_port *port;
-       int pbn_used = 0, ret;
-       bool found = false;
-
-       /* Check that we have at least one port in our state that's downstream
-        * of this branch, otherwise we can skip this branch
-        */
-       list_for_each_entry(vcpi, &state->vcpis, next) {
-               if (!vcpi->pbn ||
-                   !drm_dp_mst_port_downstream_of_branch(vcpi->port, mstb))
-                       continue;
-
-               found = true;
-               break;
-       }
-       if (!found)
-               return 0;
-
-       if (mstb->port_parent)
-               drm_dbg_atomic(mstb->mgr->dev,
-                              "[MSTB:%p] [MST PORT:%p] Checking bandwidth limits on [MSTB:%p]\n",
-                              mstb->port_parent->parent, mstb->port_parent, mstb);
-       else
-               drm_dbg_atomic(mstb->mgr->dev, "[MSTB:%p] Checking bandwidth limits\n", mstb);
-
-       list_for_each_entry(port, &mstb->ports, next) {
-               ret = drm_dp_mst_atomic_check_port_bw_limit(port, state);
-               if (ret < 0)
-                       return ret;
-
-               pbn_used += ret;
-       }
-
-       return pbn_used;
-}
-
-static int
-drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port,
-                                     struct drm_dp_mst_topology_state *state)
-{
-       struct drm_dp_vcpi_allocation *vcpi;
-       int pbn_used = 0;
-
-       if (port->pdt == DP_PEER_DEVICE_NONE)
-               return 0;
-
-       if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
-               bool found = false;
-
-               list_for_each_entry(vcpi, &state->vcpis, next) {
-                       if (vcpi->port != port)
-                               continue;
-                       if (!vcpi->pbn)
-                               return 0;
-
-                       found = true;
-                       break;
-               }
-               if (!found)
-                       return 0;
-
-               /*
-                * This could happen if the sink deasserted its HPD line, but
-                * the branch device still reports it as attached (PDT != NONE).
-                */
-               if (!port->full_pbn) {
-                       drm_dbg_atomic(port->mgr->dev,
-                                      "[MSTB:%p] [MST PORT:%p] no BW available for the port\n",
-                                      port->parent, port);
-                       return -EINVAL;
-               }
-
-               pbn_used = vcpi->pbn;
-       } else {
-               pbn_used = drm_dp_mst_atomic_check_mstb_bw_limit(port->mstb,
-                                                                state);
-               if (pbn_used <= 0)
-                       return pbn_used;
-       }
-
-       if (pbn_used > port->full_pbn) {
-               drm_dbg_atomic(port->mgr->dev,
-                              "[MSTB:%p] [MST PORT:%p] required PBN of %d exceeds port limit of %d\n",
-                              port->parent, port, pbn_used, port->full_pbn);
-               return -ENOSPC;
-       }
-
-       drm_dbg_atomic(port->mgr->dev, "[MSTB:%p] [MST PORT:%p] uses %d out of %d PBN\n",
-                      port->parent, port, pbn_used, port->full_pbn);
-
-       return pbn_used;
-}
-
-static inline int
-drm_dp_mst_atomic_check_vcpi_alloc_limit(struct drm_dp_mst_topology_mgr *mgr,
-                                        struct drm_dp_mst_topology_state *mst_state)
-{
-       struct drm_dp_vcpi_allocation *vcpi;
-       int avail_slots = mst_state->total_avail_slots, payload_count = 0;
-
-       list_for_each_entry(vcpi, &mst_state->vcpis, next) {
-               /* Releasing VCPI is always OK-even if the port is gone */
-               if (!vcpi->vcpi) {
-                       drm_dbg_atomic(mgr->dev, "[MST PORT:%p] releases all VCPI slots\n",
-                                      vcpi->port);
-                       continue;
-               }
-
-               drm_dbg_atomic(mgr->dev, "[MST PORT:%p] requires %d vcpi slots\n",
-                              vcpi->port, vcpi->vcpi);
-
-               avail_slots -= vcpi->vcpi;
-               if (avail_slots < 0) {
-                       drm_dbg_atomic(mgr->dev,
-                                      "[MST PORT:%p] not enough VCPI slots in mst state %p (avail=%d)\n",
-                                      vcpi->port, mst_state, avail_slots + vcpi->vcpi);
-                       return -ENOSPC;
-               }
-
-               if (++payload_count > mgr->max_payloads) {
-                       drm_dbg_atomic(mgr->dev,
-                                      "[MST MGR:%p] state %p has too many payloads (max=%d)\n",
-                                      mgr, mst_state, mgr->max_payloads);
-                       return -EINVAL;
-               }
-       }
-       drm_dbg_atomic(mgr->dev, "[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n",
-                      mgr, mst_state, avail_slots, mst_state->total_avail_slots - avail_slots);
-
-       return 0;
-}
-
-/**
- * drm_dp_mst_add_affected_dsc_crtcs
- * @state: Pointer to the new struct drm_dp_mst_topology_state
- * @mgr: MST topology manager
- *
- * Whenever there is a change in mst topology
- * DSC configuration would have to be recalculated
- * therefore we need to trigger modeset on all affected
- * CRTCs in that topology
- *
- * See also:
- * drm_dp_mst_atomic_enable_dsc()
- */
-int drm_dp_mst_add_affected_dsc_crtcs(struct drm_atomic_state *state, struct drm_dp_mst_topology_mgr *mgr)
-{
-       struct drm_dp_mst_topology_state *mst_state;
-       struct drm_dp_vcpi_allocation *pos;
-       struct drm_connector *connector;
-       struct drm_connector_state *conn_state;
-       struct drm_crtc *crtc;
-       struct drm_crtc_state *crtc_state;
-
-       mst_state = drm_atomic_get_mst_topology_state(state, mgr);
-
-       if (IS_ERR(mst_state))
-               return -EINVAL;
-
-       list_for_each_entry(pos, &mst_state->vcpis, next) {
-
-               connector = pos->port->connector;
-
-               if (!connector)
-                       return -EINVAL;
-
-               conn_state = drm_atomic_get_connector_state(state, connector);
-
-               if (IS_ERR(conn_state))
-                       return PTR_ERR(conn_state);
-
-               crtc = conn_state->crtc;
-
-               if (!crtc)
-                       continue;
-
-               if (!drm_dp_mst_dsc_aux_for_port(pos->port))
-                       continue;
-
-               crtc_state = drm_atomic_get_crtc_state(mst_state->base.state, crtc);
-
-               if (IS_ERR(crtc_state))
-                       return PTR_ERR(crtc_state);
-
-               drm_dbg_atomic(mgr->dev, "[MST MGR:%p] Setting mode_changed flag on CRTC %p\n",
-                              mgr, crtc);
-
-               crtc_state->mode_changed = true;
-       }
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_mst_add_affected_dsc_crtcs);
-
-/**
- * drm_dp_mst_atomic_enable_dsc - Set DSC Enable Flag to On/Off
- * @state: Pointer to the new drm_atomic_state
- * @port: Pointer to the affected MST Port
- * @pbn: Newly recalculated bw required for link with DSC enabled
- * @pbn_div: Divider to calculate correct number of pbn per slot
- * @enable: Boolean flag to enable or disable DSC on the port
- *
- * This function enables DSC on the given Port
- * by recalculating its vcpi from pbn provided
- * and sets dsc_enable flag to keep track of which
- * ports have DSC enabled
- *
- */
-int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state,
-                                struct drm_dp_mst_port *port,
-                                int pbn, int pbn_div,
-                                bool enable)
-{
-       struct drm_dp_mst_topology_state *mst_state;
-       struct drm_dp_vcpi_allocation *pos;
-       bool found = false;
-       int vcpi = 0;
-
-       mst_state = drm_atomic_get_mst_topology_state(state, port->mgr);
-
-       if (IS_ERR(mst_state))
-               return PTR_ERR(mst_state);
-
-       list_for_each_entry(pos, &mst_state->vcpis, next) {
-               if (pos->port == port) {
-                       found = true;
-                       break;
-               }
-       }
-
-       if (!found) {
-               drm_dbg_atomic(state->dev,
-                              "[MST PORT:%p] Couldn't find VCPI allocation in mst state %p\n",
-                              port, mst_state);
-               return -EINVAL;
-       }
-
-       if (pos->dsc_enabled == enable) {
-               drm_dbg_atomic(state->dev,
-                              "[MST PORT:%p] DSC flag is already set to %d, returning %d VCPI slots\n",
-                              port, enable, pos->vcpi);
-               vcpi = pos->vcpi;
-       }
-
-       if (enable) {
-               vcpi = drm_dp_atomic_find_vcpi_slots(state, port->mgr, port, pbn, pbn_div);
-               drm_dbg_atomic(state->dev,
-                              "[MST PORT:%p] Enabling DSC flag, reallocating %d VCPI slots on the port\n",
-                              port, vcpi);
-               if (vcpi < 0)
-                       return -EINVAL;
-       }
-
-       pos->dsc_enabled = enable;
-
-       return vcpi;
-}
-EXPORT_SYMBOL(drm_dp_mst_atomic_enable_dsc);
-/**
- * drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
- * atomic update is valid
- * @state: Pointer to the new &struct drm_dp_mst_topology_state
- *
- * Checks the given topology state for an atomic update to ensure that it's
- * valid. This includes checking whether there's enough bandwidth to support
- * the new VCPI allocations in the atomic update.
- *
- * Any atomic drivers supporting DP MST must make sure to call this after
- * checking the rest of their state in their
- * &drm_mode_config_funcs.atomic_check() callback.
- *
- * See also:
- * drm_dp_atomic_find_vcpi_slots()
- * drm_dp_atomic_release_vcpi_slots()
- *
- * Returns:
- *
- * 0 if the new state is valid, negative error code otherwise.
- */
-int drm_dp_mst_atomic_check(struct drm_atomic_state *state)
-{
-       struct drm_dp_mst_topology_mgr *mgr;
-       struct drm_dp_mst_topology_state *mst_state;
-       int i, ret = 0;
-
-       for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
-               if (!mgr->mst_state)
-                       continue;
-
-               ret = drm_dp_mst_atomic_check_vcpi_alloc_limit(mgr, mst_state);
-               if (ret)
-                       break;
-
-               mutex_lock(&mgr->lock);
-               ret = drm_dp_mst_atomic_check_mstb_bw_limit(mgr->mst_primary,
-                                                           mst_state);
-               mutex_unlock(&mgr->lock);
-               if (ret < 0)
-                       break;
-               else
-                       ret = 0;
-       }
-
-       return ret;
-}
-EXPORT_SYMBOL(drm_dp_mst_atomic_check);
-
-const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs = {
-       .atomic_duplicate_state = drm_dp_mst_duplicate_state,
-       .atomic_destroy_state = drm_dp_mst_destroy_state,
-};
-EXPORT_SYMBOL(drm_dp_mst_topology_state_funcs);
-
-/**
- * drm_atomic_get_mst_topology_state: get MST topology state
- *
- * @state: global atomic state
- * @mgr: MST topology manager, also the private object in this case
- *
- * This function wraps drm_atomic_get_priv_obj_state() passing in the MST atomic
- * state vtable so that the private object state returned is that of a MST
- * topology object. Also, drm_atomic_get_private_obj_state() expects the caller
- * to care of the locking, so warn if don't hold the connection_mutex.
- *
- * RETURNS:
- *
- * The MST topology state or error pointer.
- */
-struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
-                                                                   struct drm_dp_mst_topology_mgr *mgr)
-{
-       return to_dp_mst_topology_state(drm_atomic_get_private_obj_state(state, &mgr->base));
-}
-EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
-
-/**
- * drm_dp_mst_topology_mgr_init - initialise a topology manager
- * @mgr: manager struct to initialise
- * @dev: device providing this structure - for i2c addition.
- * @aux: DP helper aux channel to talk to this device
- * @max_dpcd_transaction_bytes: hw specific DPCD transaction limit
- * @max_payloads: maximum number of payloads this GPU can source
- * @max_lane_count: maximum number of lanes this GPU supports
- * @max_link_rate: maximum link rate per lane this GPU supports in kHz
- * @conn_base_id: the connector object ID the MST device is connected to.
- *
- * Return 0 for success, or negative error code on failure
- */
-int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
-                                struct drm_device *dev, struct drm_dp_aux *aux,
-                                int max_dpcd_transaction_bytes, int max_payloads,
-                                int max_lane_count, int max_link_rate,
-                                int conn_base_id)
-{
-       struct drm_dp_mst_topology_state *mst_state;
-
-       mutex_init(&mgr->lock);
-       mutex_init(&mgr->qlock);
-       mutex_init(&mgr->payload_lock);
-       mutex_init(&mgr->delayed_destroy_lock);
-       mutex_init(&mgr->up_req_lock);
-       mutex_init(&mgr->probe_lock);
-#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
-       mutex_init(&mgr->topology_ref_history_lock);
-       stack_depot_init();
-#endif
-       INIT_LIST_HEAD(&mgr->tx_msg_downq);
-       INIT_LIST_HEAD(&mgr->destroy_port_list);
-       INIT_LIST_HEAD(&mgr->destroy_branch_device_list);
-       INIT_LIST_HEAD(&mgr->up_req_list);
-
-       /*
-        * delayed_destroy_work will be queued on a dedicated WQ, so that any
-        * requeuing will be also flushed when deiniting the topology manager.
-        */
-       mgr->delayed_destroy_wq = alloc_ordered_workqueue("drm_dp_mst_wq", 0);
-       if (mgr->delayed_destroy_wq == NULL)
-               return -ENOMEM;
-
-       INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
-       INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
-       INIT_WORK(&mgr->delayed_destroy_work, drm_dp_delayed_destroy_work);
-       INIT_WORK(&mgr->up_req_work, drm_dp_mst_up_req_work);
-       init_waitqueue_head(&mgr->tx_waitq);
-       mgr->dev = dev;
-       mgr->aux = aux;
-       mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes;
-       mgr->max_payloads = max_payloads;
-       mgr->max_lane_count = max_lane_count;
-       mgr->max_link_rate = max_link_rate;
-       mgr->conn_base_id = conn_base_id;
-       if (max_payloads + 1 > sizeof(mgr->payload_mask) * 8 ||
-           max_payloads + 1 > sizeof(mgr->vcpi_mask) * 8)
-               return -EINVAL;
-       mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL);
-       if (!mgr->payloads)
-               return -ENOMEM;
-       mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL);
-       if (!mgr->proposed_vcpis)
-               return -ENOMEM;
-       set_bit(0, &mgr->payload_mask);
-
-       mst_state = kzalloc(sizeof(*mst_state), GFP_KERNEL);
-       if (mst_state == NULL)
-               return -ENOMEM;
-
-       mst_state->total_avail_slots = 63;
-       mst_state->start_slot = 1;
-
-       mst_state->mgr = mgr;
-       INIT_LIST_HEAD(&mst_state->vcpis);
-
-       drm_atomic_private_obj_init(dev, &mgr->base,
-                                   &mst_state->base,
-                                   &drm_dp_mst_topology_state_funcs);
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
-
-/**
- * drm_dp_mst_topology_mgr_destroy() - destroy topology manager.
- * @mgr: manager to destroy
- */
-void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
-{
-       drm_dp_mst_topology_mgr_set_mst(mgr, false);
-       flush_work(&mgr->work);
-       /* The following will also drain any requeued work on the WQ. */
-       if (mgr->delayed_destroy_wq) {
-               destroy_workqueue(mgr->delayed_destroy_wq);
-               mgr->delayed_destroy_wq = NULL;
-       }
-       mutex_lock(&mgr->payload_lock);
-       kfree(mgr->payloads);
-       mgr->payloads = NULL;
-       kfree(mgr->proposed_vcpis);
-       mgr->proposed_vcpis = NULL;
-       mutex_unlock(&mgr->payload_lock);
-       mgr->dev = NULL;
-       mgr->aux = NULL;
-       drm_atomic_private_obj_fini(&mgr->base);
-       mgr->funcs = NULL;
-
-       mutex_destroy(&mgr->delayed_destroy_lock);
-       mutex_destroy(&mgr->payload_lock);
-       mutex_destroy(&mgr->qlock);
-       mutex_destroy(&mgr->lock);
-       mutex_destroy(&mgr->up_req_lock);
-       mutex_destroy(&mgr->probe_lock);
-#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
-       mutex_destroy(&mgr->topology_ref_history_lock);
-#endif
-}
-EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
-
-static bool remote_i2c_read_ok(const struct i2c_msg msgs[], int num)
-{
-       int i;
-
-       if (num - 1 > DP_REMOTE_I2C_READ_MAX_TRANSACTIONS)
-               return false;
-
-       for (i = 0; i < num - 1; i++) {
-               if (msgs[i].flags & I2C_M_RD ||
-                   msgs[i].len > 0xff)
-                       return false;
-       }
-
-       return msgs[num - 1].flags & I2C_M_RD &&
-               msgs[num - 1].len <= 0xff;
-}
-
-static bool remote_i2c_write_ok(const struct i2c_msg msgs[], int num)
-{
-       int i;
-
-       for (i = 0; i < num - 1; i++) {
-               if (msgs[i].flags & I2C_M_RD || !(msgs[i].flags & I2C_M_STOP) ||
-                   msgs[i].len > 0xff)
-                       return false;
-       }
-
-       return !(msgs[num - 1].flags & I2C_M_RD) && msgs[num - 1].len <= 0xff;
-}
-
-static int drm_dp_mst_i2c_read(struct drm_dp_mst_branch *mstb,
-                              struct drm_dp_mst_port *port,
-                              struct i2c_msg *msgs, int num)
-{
-       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
-       unsigned int i;
-       struct drm_dp_sideband_msg_req_body msg;
-       struct drm_dp_sideband_msg_tx *txmsg = NULL;
-       int ret;
-
-       memset(&msg, 0, sizeof(msg));
-       msg.req_type = DP_REMOTE_I2C_READ;
-       msg.u.i2c_read.num_transactions = num - 1;
-       msg.u.i2c_read.port_number = port->port_num;
-       for (i = 0; i < num - 1; i++) {
-               msg.u.i2c_read.transactions[i].i2c_dev_id = msgs[i].addr;
-               msg.u.i2c_read.transactions[i].num_bytes = msgs[i].len;
-               msg.u.i2c_read.transactions[i].bytes = msgs[i].buf;
-               msg.u.i2c_read.transactions[i].no_stop_bit = !(msgs[i].flags & I2C_M_STOP);
-       }
-       msg.u.i2c_read.read_i2c_device_id = msgs[num - 1].addr;
-       msg.u.i2c_read.num_bytes_read = msgs[num - 1].len;
-
-       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-       if (!txmsg) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       txmsg->dst = mstb;
-       drm_dp_encode_sideband_req(&msg, txmsg);
-
-       drm_dp_queue_down_tx(mgr, txmsg);
-
-       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
-       if (ret > 0) {
-
-               if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
-                       ret = -EREMOTEIO;
-                       goto out;
-               }
-               if (txmsg->reply.u.remote_i2c_read_ack.num_bytes != msgs[num - 1].len) {
-                       ret = -EIO;
-                       goto out;
-               }
-               memcpy(msgs[num - 1].buf, txmsg->reply.u.remote_i2c_read_ack.bytes, msgs[num - 1].len);
-               ret = num;
-       }
-out:
-       kfree(txmsg);
-       return ret;
-}
-
-static int drm_dp_mst_i2c_write(struct drm_dp_mst_branch *mstb,
-                               struct drm_dp_mst_port *port,
-                               struct i2c_msg *msgs, int num)
-{
-       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
-       unsigned int i;
-       struct drm_dp_sideband_msg_req_body msg;
-       struct drm_dp_sideband_msg_tx *txmsg = NULL;
-       int ret;
-
-       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-       if (!txmsg) {
-               ret = -ENOMEM;
-               goto out;
-       }
-       for (i = 0; i < num; i++) {
-               memset(&msg, 0, sizeof(msg));
-               msg.req_type = DP_REMOTE_I2C_WRITE;
-               msg.u.i2c_write.port_number = port->port_num;
-               msg.u.i2c_write.write_i2c_device_id = msgs[i].addr;
-               msg.u.i2c_write.num_bytes = msgs[i].len;
-               msg.u.i2c_write.bytes = msgs[i].buf;
-
-               memset(txmsg, 0, sizeof(*txmsg));
-               txmsg->dst = mstb;
-
-               drm_dp_encode_sideband_req(&msg, txmsg);
-               drm_dp_queue_down_tx(mgr, txmsg);
-
-               ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
-               if (ret > 0) {
-                       if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
-                               ret = -EREMOTEIO;
-                               goto out;
-                       }
-               } else {
-                       goto out;
-               }
-       }
-       ret = num;
-out:
-       kfree(txmsg);
-       return ret;
-}
-
-/* I2C device */
-static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter,
-                              struct i2c_msg *msgs, int num)
-{
-       struct drm_dp_aux *aux = adapter->algo_data;
-       struct drm_dp_mst_port *port =
-               container_of(aux, struct drm_dp_mst_port, aux);
-       struct drm_dp_mst_branch *mstb;
-       struct drm_dp_mst_topology_mgr *mgr = port->mgr;
-       int ret;
-
-       mstb = drm_dp_mst_topology_get_mstb_validated(mgr, port->parent);
-       if (!mstb)
-               return -EREMOTEIO;
-
-       if (remote_i2c_read_ok(msgs, num)) {
-               ret = drm_dp_mst_i2c_read(mstb, port, msgs, num);
-       } else if (remote_i2c_write_ok(msgs, num)) {
-               ret = drm_dp_mst_i2c_write(mstb, port, msgs, num);
-       } else {
-               drm_dbg_kms(mgr->dev, "Unsupported I2C transaction for MST device\n");
-               ret = -EIO;
-       }
-
-       drm_dp_mst_topology_put_mstb(mstb);
-       return ret;
-}
-
-static u32 drm_dp_mst_i2c_functionality(struct i2c_adapter *adapter)
-{
-       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
-              I2C_FUNC_SMBUS_READ_BLOCK_DATA |
-              I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
-              I2C_FUNC_10BIT_ADDR;
-}
-
-static const struct i2c_algorithm drm_dp_mst_i2c_algo = {
-       .functionality = drm_dp_mst_i2c_functionality,
-       .master_xfer = drm_dp_mst_i2c_xfer,
-};
-
-/**
- * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
- * @port: The port to add the I2C bus on
- *
- * Returns 0 on success or a negative error code on failure.
- */
-static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port)
-{
-       struct drm_dp_aux *aux = &port->aux;
-       struct device *parent_dev = port->mgr->dev->dev;
-
-       aux->ddc.algo = &drm_dp_mst_i2c_algo;
-       aux->ddc.algo_data = aux;
-       aux->ddc.retries = 3;
-
-       aux->ddc.class = I2C_CLASS_DDC;
-       aux->ddc.owner = THIS_MODULE;
-       /* FIXME: set the kdev of the port's connector as parent */
-       aux->ddc.dev.parent = parent_dev;
-       aux->ddc.dev.of_node = parent_dev->of_node;
-
-       strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(parent_dev),
-               sizeof(aux->ddc.name));
-
-       return i2c_add_adapter(&aux->ddc);
-}
-
-/**
- * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
- * @port: The port to remove the I2C bus from
- */
-static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port)
-{
-       i2c_del_adapter(&port->aux.ddc);
-}
-
-/**
- * drm_dp_mst_is_virtual_dpcd() - Is the given port a virtual DP Peer Device
- * @port: The port to check
- *
- * A single physical MST hub object can be represented in the topology
- * by multiple branches, with virtual ports between those branches.
- *
- * As of DP1.4, An MST hub with internal (virtual) ports must expose
- * certain DPCD registers over those ports. See sections 2.6.1.1.1
- * and 2.6.1.1.2 of Display Port specification v1.4 for details.
- *
- * May acquire mgr->lock
- *
- * Returns:
- * true if the port is a virtual DP peer device, false otherwise
- */
-static bool drm_dp_mst_is_virtual_dpcd(struct drm_dp_mst_port *port)
-{
-       struct drm_dp_mst_port *downstream_port;
-
-       if (!port || port->dpcd_rev < DP_DPCD_REV_14)
-               return false;
-
-       /* Virtual DP Sink (Internal Display Panel) */
-       if (port->port_num >= 8)
-               return true;
-
-       /* DP-to-HDMI Protocol Converter */
-       if (port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV &&
-           !port->mcs &&
-           port->ldps)
-               return true;
-
-       /* DP-to-DP */
-       mutex_lock(&port->mgr->lock);
-       if (port->pdt == DP_PEER_DEVICE_MST_BRANCHING &&
-           port->mstb &&
-           port->mstb->num_ports == 2) {
-               list_for_each_entry(downstream_port, &port->mstb->ports, next) {
-                       if (downstream_port->pdt == DP_PEER_DEVICE_SST_SINK &&
-                           !downstream_port->input) {
-                               mutex_unlock(&port->mgr->lock);
-                               return true;
-                       }
-               }
-       }
-       mutex_unlock(&port->mgr->lock);
-
-       return false;
-}
-
-/**
- * drm_dp_mst_dsc_aux_for_port() - Find the correct aux for DSC
- * @port: The port to check. A leaf of the MST tree with an attached display.
- *
- * Depending on the situation, DSC may be enabled via the endpoint aux,
- * the immediately upstream aux, or the connector's physical aux.
- *
- * This is both the correct aux to read DSC_CAPABILITY and the
- * correct aux to write DSC_ENABLED.
- *
- * This operation can be expensive (up to four aux reads), so
- * the caller should cache the return.
- *
- * Returns:
- * NULL if DSC cannot be enabled on this port, otherwise the aux device
- */
-struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port)
-{
-       struct drm_dp_mst_port *immediate_upstream_port;
-       struct drm_dp_mst_port *fec_port;
-       struct drm_dp_desc desc = {};
-       u8 endpoint_fec;
-       u8 endpoint_dsc;
-
-       if (!port)
-               return NULL;
-
-       if (port->parent->port_parent)
-               immediate_upstream_port = port->parent->port_parent;
-       else
-               immediate_upstream_port = NULL;
-
-       fec_port = immediate_upstream_port;
-       while (fec_port) {
-               /*
-                * Each physical link (i.e. not a virtual port) between the
-                * output and the primary device must support FEC
-                */
-               if (!drm_dp_mst_is_virtual_dpcd(fec_port) &&
-                   !fec_port->fec_capable)
-                       return NULL;
-
-               fec_port = fec_port->parent->port_parent;
-       }
-
-       /* DP-to-DP peer device */
-       if (drm_dp_mst_is_virtual_dpcd(immediate_upstream_port)) {
-               u8 upstream_dsc;
-
-               if (drm_dp_dpcd_read(&port->aux,
-                                    DP_DSC_SUPPORT, &endpoint_dsc, 1) != 1)
-                       return NULL;
-               if (drm_dp_dpcd_read(&port->aux,
-                                    DP_FEC_CAPABILITY, &endpoint_fec, 1) != 1)
-                       return NULL;
-               if (drm_dp_dpcd_read(&immediate_upstream_port->aux,
-                                    DP_DSC_SUPPORT, &upstream_dsc, 1) != 1)
-                       return NULL;
-
-               /* Enpoint decompression with DP-to-DP peer device */
-               if ((endpoint_dsc & DP_DSC_DECOMPRESSION_IS_SUPPORTED) &&
-                   (endpoint_fec & DP_FEC_CAPABLE) &&
-                   (upstream_dsc & 0x2) /* DSC passthrough */)
-                       return &port->aux;
-
-               /* Virtual DPCD decompression with DP-to-DP peer device */
-               return &immediate_upstream_port->aux;
-       }
-
-       /* Virtual DPCD decompression with DP-to-HDMI or Virtual DP Sink */
-       if (drm_dp_mst_is_virtual_dpcd(port))
-               return &port->aux;
-
-       /*
-        * Synaptics quirk
-        * Applies to ports for which:
-        * - Physical aux has Synaptics OUI
-        * - DPv1.4 or higher
-        * - Port is on primary branch device
-        * - Not a VGA adapter (DP_DWN_STRM_PORT_TYPE_ANALOG)
-        */
-       if (drm_dp_read_desc(port->mgr->aux, &desc, true))
-               return NULL;
-
-       if (drm_dp_has_quirk(&desc, DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) &&
-           port->mgr->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14 &&
-           port->parent == port->mgr->mst_primary) {
-               u8 dpcd_ext[DP_RECEIVER_CAP_SIZE];
-
-               if (drm_dp_read_dpcd_caps(port->mgr->aux, dpcd_ext) < 0)
-                       return NULL;
-
-               if ((dpcd_ext[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT) &&
-                   ((dpcd_ext[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK)
-                    != DP_DWN_STRM_PORT_TYPE_ANALOG))
-                       return port->mgr->aux;
-       }
-
-       /*
-        * The check below verifies if the MST sink
-        * connected to the GPU is capable of DSC -
-        * therefore the endpoint needs to be
-        * both DSC and FEC capable.
-        */
-       if (drm_dp_dpcd_read(&port->aux,
-          DP_DSC_SUPPORT, &endpoint_dsc, 1) != 1)
-               return NULL;
-       if (drm_dp_dpcd_read(&port->aux,
-          DP_FEC_CAPABILITY, &endpoint_fec, 1) != 1)
-               return NULL;
-       if ((endpoint_dsc & DP_DSC_DECOMPRESSION_IS_SUPPORTED) &&
-          (endpoint_fec & DP_FEC_CAPABLE))
-               return &port->aux;
-
-       return NULL;
-}
-EXPORT_SYMBOL(drm_dp_mst_dsc_aux_for_port);
diff --git a/drivers/gpu/drm/drm_dp_mst_topology_internal.h b/drivers/gpu/drm/drm_dp_mst_topology_internal.h
deleted file mode 100644 (file)
index eeda9a6..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only
- *
- * Declarations for DP MST related functions which are only used in selftests
- *
- * Copyright © 2018 Red Hat
- * Authors:
- *     Lyude Paul <lyude@redhat.com>
- */
-
-#ifndef _DRM_DP_MST_HELPER_INTERNAL_H_
-#define _DRM_DP_MST_HELPER_INTERNAL_H_
-
-#include <drm/drm_dp_mst_helper.h>
-
-void
-drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
-                          struct drm_dp_sideband_msg_tx *raw);
-int drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
-                              struct drm_dp_sideband_msg_req_body *req);
-void
-drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
-                                 int indent, struct drm_printer *printer);
-
-#endif /* !_DRM_DP_MST_HELPER_INTERNAL_H_ */
index 46a3c1b..fdd8d5f 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/errno.h>
 #include <linux/byteorder/generic.h>
 #include <drm/drm_print.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_dsc.h>
 
 /**
index 12893e7..a504542 100644 (file)
@@ -93,6 +93,8 @@ static int oui(u8 first, u8 second, u8 third)
 /* Non desktop display (i.e. HMD) */
 #define EDID_QUIRK_NON_DESKTOP                 (1 << 12)
 
+#define MICROSOFT_IEEE_OUI     0xca125c
+
 struct detailed_mode_closure {
        struct drm_connector *connector;
        struct edid *edid;
@@ -212,9 +214,7 @@ static const struct edid_quirk {
 
        /* Windows Mixed Reality Headsets */
        EDID_QUIRK('A', 'C', 'R', 0x7fce, EDID_QUIRK_NON_DESKTOP),
-       EDID_QUIRK('H', 'P', 'N', 0x3515, EDID_QUIRK_NON_DESKTOP),
        EDID_QUIRK('L', 'E', 'N', 0x0408, EDID_QUIRK_NON_DESKTOP),
-       EDID_QUIRK('L', 'E', 'N', 0xb800, EDID_QUIRK_NON_DESKTOP),
        EDID_QUIRK('F', 'U', 'J', 0x1970, EDID_QUIRK_NON_DESKTOP),
        EDID_QUIRK('D', 'E', 'L', 0x7fce, EDID_QUIRK_NON_DESKTOP),
        EDID_QUIRK('S', 'E', 'C', 0x144a, EDID_QUIRK_NON_DESKTOP),
@@ -3776,7 +3776,7 @@ static int do_y420vdb_modes(struct drm_connector *connector,
        }
 
        if (modes > 0)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB420;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
        return modes;
 }
 
@@ -4222,6 +4222,17 @@ static bool cea_db_is_hdmi_forum_vsdb(const u8 *db)
        return oui(db[3], db[2], db[1]) == HDMI_FORUM_IEEE_OUI;
 }
 
+static bool cea_db_is_microsoft_vsdb(const u8 *db)
+{
+       if (cea_db_tag(db) != VENDOR_BLOCK)
+               return false;
+
+       if (cea_db_payload_len(db) != 21)
+               return false;
+
+       return oui(db[3], db[2], db[1]) == MICROSOFT_IEEE_OUI;
+}
+
 static bool cea_db_is_vcdb(const u8 *db)
 {
        if (cea_db_tag(db) != USE_EXTENDED_TAG)
@@ -4279,7 +4290,7 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
        if (map_len == 0) {
                /* All CEA modes support ycbcr420 sampling also.*/
                hdmi->y420_cmdb_map = U64_MAX;
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB420;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
                return;
        }
 
@@ -4302,7 +4313,7 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
                map |= (u64)db[2 + count] << (8 * count);
 
        if (map)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB420;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
 
        hdmi->y420_cmdb_map = map;
 }
@@ -5075,21 +5086,21 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
 
        if (hdmi[6] & DRM_EDID_HDMI_DC_30) {
                dc_bpc = 10;
-               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30;
+               info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_30;
                DRM_DEBUG("%s: HDMI sink does deep color 30.\n",
                          connector->name);
        }
 
        if (hdmi[6] & DRM_EDID_HDMI_DC_36) {
                dc_bpc = 12;
-               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36;
+               info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_36;
                DRM_DEBUG("%s: HDMI sink does deep color 36.\n",
                          connector->name);
        }
 
        if (hdmi[6] & DRM_EDID_HDMI_DC_48) {
                dc_bpc = 16;
-               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48;
+               info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_48;
                DRM_DEBUG("%s: HDMI sink does deep color 48.\n",
                          connector->name);
        }
@@ -5104,16 +5115,9 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
                  connector->name, dc_bpc);
        info->bpc = dc_bpc;
 
-       /*
-        * Deep color support mandates RGB444 support for all video
-        * modes and forbids YCRCB422 support for all video modes per
-        * HDMI 1.3 spec.
-        */
-       info->color_formats = DRM_COLOR_FORMAT_RGB444;
-
        /* YCRCB444 is optional according to spec. */
        if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+               info->edid_hdmi_ycbcr444_dc_modes = info->edid_hdmi_rgb444_dc_modes;
                DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n",
                          connector->name);
        }
@@ -5149,6 +5153,25 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db)
        drm_parse_hdmi_deep_color_info(connector, db);
 }
 
+/*
+ * See EDID extension for head-mounted and specialized monitors, specified at:
+ * https://docs.microsoft.com/en-us/windows-hardware/drivers/display/specialized-monitors-edid-extension
+ */
+static void drm_parse_microsoft_vsdb(struct drm_connector *connector,
+                                    const u8 *db)
+{
+       struct drm_display_info *info = &connector->display_info;
+       u8 version = db[4];
+       bool desktop_usage = db[5] & BIT(6);
+
+       /* Version 1 and 2 for HMDs, version 3 flags desktop usage explicitly */
+       if (version == 1 || version == 2 || (version == 3 && !desktop_usage))
+               info->non_desktop = true;
+
+       drm_dbg_kms(connector->dev, "HMD or specialized display VSDB version %u: 0x%02x\n",
+                   version, db[5]);
+}
+
 static void drm_parse_cea_ext(struct drm_connector *connector,
                              const struct edid *edid)
 {
@@ -5165,9 +5188,9 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
        /* The existence of a CEA block should imply RGB support */
        info->color_formats = DRM_COLOR_FORMAT_RGB444;
        if (edid_ext[3] & EDID_CEA_YCRCB444)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
        if (edid_ext[3] & EDID_CEA_YCRCB422)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
 
        if (cea_db_offsets(edid_ext, &start, &end))
                return;
@@ -5179,6 +5202,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
                        drm_parse_hdmi_vsdb_video(connector, db);
                if (cea_db_is_hdmi_forum_vsdb(db))
                        drm_parse_hdmi_forum_vsdb(connector, db);
+               if (cea_db_is_microsoft_vsdb(db))
+                       drm_parse_microsoft_vsdb(connector, db);
                if (cea_db_is_y420cmdb(db))
                        drm_parse_y420cmdb_bitmap(connector, db);
                if (cea_db_is_vcdb(db))
@@ -5333,17 +5358,13 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
        info->width_mm = edid->width_cm * 10;
        info->height_mm = edid->height_cm * 10;
 
-       info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP);
-
        drm_get_monitor_range(connector, edid);
 
-       DRM_DEBUG_KMS("non_desktop set to %d\n", info->non_desktop);
-
        if (edid->revision < 3)
-               return quirks;
+               goto out;
 
        if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
-               return quirks;
+               goto out;
 
        drm_parse_cea_ext(connector, edid);
 
@@ -5363,7 +5384,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
 
        /* Only defined for 1.4 with digital displays */
        if (edid->revision < 4)
-               return quirks;
+               goto out;
 
        switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
        case DRM_EDID_DIGITAL_DEPTH_6:
@@ -5395,17 +5416,25 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
 
        info->color_formats |= DRM_COLOR_FORMAT_RGB444;
        if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
        if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
 
        drm_update_mso(connector, edid);
 
+out:
+       if (quirks & EDID_QUIRK_NON_DESKTOP) {
+               drm_dbg_kms(connector->dev, "Non-desktop display%s\n",
+                           info->non_desktop ? " (redundant quirk)" : "");
+               info->non_desktop = true;
+       }
+
        return quirks;
 }
 
 static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev,
-                                                           struct displayid_detailed_timings_1 *timings)
+                                                           struct displayid_detailed_timings_1 *timings,
+                                                           bool type_7)
 {
        struct drm_display_mode *mode;
        unsigned pixel_clock = (timings->pixel_clock[0] |
@@ -5426,7 +5455,8 @@ static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *d
        if (!mode)
                return NULL;
 
-       mode->clock = pixel_clock * 10;
+       /* resolution is kHz for type VII, and 10 kHz for type I */
+       mode->clock = type_7 ? pixel_clock : pixel_clock * 10;
        mode->hdisplay = hactive;
        mode->hsync_start = mode->hdisplay + hsync;
        mode->hsync_end = mode->hsync_start + hsync_width;
@@ -5457,6 +5487,7 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector,
        int num_timings;
        struct drm_display_mode *newmode;
        int num_modes = 0;
+       bool type_7 = block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING;
        /* blocks must be multiple of 20 bytes length */
        if (block->num_bytes % 20)
                return 0;
@@ -5465,7 +5496,7 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector,
        for (i = 0; i < num_timings; i++) {
                struct displayid_detailed_timings_1 *timings = &det->timings[i];
 
-               newmode = drm_mode_displayid_detailed(connector->dev, timings);
+               newmode = drm_mode_displayid_detailed(connector->dev, timings, type_7);
                if (!newmode)
                        continue;
 
@@ -5484,7 +5515,8 @@ static int add_displayid_detailed_modes(struct drm_connector *connector,
 
        displayid_iter_edid_begin(edid, &iter);
        displayid_iter_for_each(block, &iter) {
-               if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING)
+               if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING ||
+                   block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING)
                        num_modes += add_displayid_detailed_1_modes(connector, block);
        }
        displayid_iter_end(&iter);
@@ -5652,7 +5684,7 @@ static bool is_hdmi2_sink(const struct drm_connector *connector)
                return true;
 
        return connector->display_info.hdmi.scdc.supported ||
-               connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB420;
+               connector->display_info.color_formats & DRM_COLOR_FORMAT_YCBCR420;
 }
 
 static inline bool is_eotf_supported(u8 output_eotf, u8 sink_eotf)
@@ -5891,13 +5923,13 @@ static const u32 hdmi_colorimetry_val[] = {
 #undef ACE
 
 /**
- * drm_hdmi_avi_infoframe_colorspace() - fill the HDMI AVI infoframe
- *                                       colorspace information
+ * drm_hdmi_avi_infoframe_colorimetry() - fill the HDMI AVI infoframe
+ *                                       colorimetry information
  * @frame: HDMI AVI infoframe
  * @conn_state: connector state
  */
 void
-drm_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
+drm_hdmi_avi_infoframe_colorimetry(struct hdmi_avi_infoframe *frame,
                                  const struct drm_connector_state *conn_state)
 {
        u32 colorimetry_val;
@@ -5916,7 +5948,7 @@ drm_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
        frame->extended_colorimetry = (colorimetry_val >> 2) &
                                        EXTENDED_COLORIMETRY_MASK;
 }
-EXPORT_SYMBOL(drm_hdmi_avi_infoframe_colorspace);
+EXPORT_SYMBOL(drm_hdmi_avi_infoframe_colorimetry);
 
 /**
  * drm_hdmi_avi_infoframe_quant_range() - fill the HDMI AVI infoframe
index 47e9240..8be2008 100644 (file)
@@ -61,17 +61,3 @@ MODULE_PARM_DESC(edid_firmware,
                 "DEPRECATED. Use drm.edid_firmware module parameter instead.");
 
 #endif
-
-static int __init drm_kms_helper_init(void)
-{
-       return drm_dp_aux_dev_init();
-}
-
-static void __exit drm_kms_helper_exit(void)
-{
-       /* Call exit functions from specific kms helpers here */
-       drm_dp_aux_dev_exit();
-}
-
-module_init(drm_kms_helper_init);
-module_exit(drm_kms_helper_exit);
index 82afb85..deeec60 100644 (file)
@@ -202,17 +202,13 @@ static int create_in_format_blob(struct drm_device *dev, struct drm_plane *plane
 
        memcpy(formats_ptr(blob_data), plane->format_types, formats_size);
 
-       /* If we can't determine support, just bail */
-       if (!plane->funcs->format_mod_supported)
-               goto done;
-
        mod = modifiers_ptr(blob_data);
        for (i = 0; i < plane->modifier_count; i++) {
                for (j = 0; j < plane->format_count; j++) {
-                       if (plane->funcs->format_mod_supported(plane,
+                       if (!plane->funcs->format_mod_supported ||
+                           plane->funcs->format_mod_supported(plane,
                                                               plane->format_types[j],
                                                               plane->modifiers[i])) {
-
                                mod->formats |= 1ULL << j;
                        }
                }
@@ -223,7 +219,6 @@ static int create_in_format_blob(struct drm_device *dev, struct drm_plane *plane
                mod++;
        }
 
-done:
        drm_object_attach_property(&plane->base, config->modifiers_property,
                                   blob->base.id);
 
index beaf99e..03b149c 100644 (file)
@@ -387,7 +387,8 @@ static void drm_privacy_screen_device_release(struct device *dev)
  * * An ERR_PTR(errno) on failure.
  */
 struct drm_privacy_screen *drm_privacy_screen_register(
-       struct device *parent, const struct drm_privacy_screen_ops *ops)
+       struct device *parent, const struct drm_privacy_screen_ops *ops,
+       void *data)
 {
        struct drm_privacy_screen *priv;
        int ret;
@@ -404,6 +405,7 @@ struct drm_privacy_screen *drm_privacy_screen_register(
        priv->dev.parent = parent;
        priv->dev.release = drm_privacy_screen_device_release;
        dev_set_name(&priv->dev, "privacy_screen-%s", dev_name(parent));
+       priv->drvdata = data;
        priv->ops = ops;
 
        priv->ops->get_hw_state(priv);
@@ -439,6 +441,7 @@ void drm_privacy_screen_unregister(struct drm_privacy_screen *priv)
        mutex_unlock(&drm_privacy_screen_devs_lock);
 
        mutex_lock(&priv->lock);
+       priv->drvdata = NULL;
        priv->ops = NULL;
        mutex_unlock(&priv->lock);
 
index e7aa74a..72ed40e 100644 (file)
@@ -50,6 +50,13 @@ static bool __init detect_thinkpad_privacy_screen(void)
 }
 #endif
 
+#if IS_ENABLED(CONFIG_CHROMEOS_PRIVACY_SCREEN)
+static bool __init detect_chromeos_privacy_screen(void)
+{
+       return acpi_dev_present("GOOG0010", NULL, -1);
+}
+#endif
+
 static const struct arch_init_data arch_init_data[] __initconst = {
 #if IS_ENABLED(CONFIG_THINKPAD_ACPI)
        {
@@ -61,6 +68,16 @@ static const struct arch_init_data arch_init_data[] __initconst = {
                .detect = detect_thinkpad_privacy_screen,
        },
 #endif
+#if IS_ENABLED(CONFIG_CHROMEOS_PRIVACY_SCREEN)
+       {
+               .lookup = {
+                       .dev_id = NULL,
+                       .con_id = NULL,
+                       .provider = "privacy_screen-GOOG0010:00",
+               },
+               .detect = detect_chromeos_privacy_screen,
+       },
+#endif
 };
 
 void __init drm_privacy_screen_lookup_init(void)
index a173132..4eb00a0 100644 (file)
@@ -189,8 +189,7 @@ static int submit_fence_sync(struct etnaviv_gem_submit *submit)
                        continue;
 
                if (bo->flags & ETNA_SUBMIT_BO_WRITE) {
-                       ret = dma_resv_get_fences(robj, NULL,
-                                                 &bo->nr_shared,
+                       ret = dma_resv_get_fences(robj, true, &bo->nr_shared,
                                                  &bo->shared);
                        if (ret)
                                return ret;
index 6a251e3..f27cfd2 100644 (file)
@@ -66,6 +66,7 @@ config DRM_EXYNOS_DP
        bool "Exynos specific extensions for Analogix DP driver"
        depends on DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON
        select DRM_ANALOGIX_DP
+       select DRM_DP_HELPER
        default DRM_EXYNOS
        select DRM_PANEL
        help
index 32a3657..b7d0a4a 100644 (file)
@@ -258,6 +258,7 @@ struct exynos_dsi {
        struct list_head bridge_chain;
        struct drm_bridge *out_bridge;
        struct device *dev;
+       struct drm_display_mode mode;
 
        void __iomem *reg_base;
        struct phy *phy;
@@ -881,7 +882,7 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
 
 static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi)
 {
-       struct drm_display_mode *m = &dsi->encoder.crtc->state->adjusted_mode;
+       struct drm_display_mode *m = &dsi->mode;
        unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
        u32 reg;
 
@@ -1446,6 +1447,15 @@ static void exynos_dsi_disable(struct drm_encoder *encoder)
        pm_runtime_put_sync(dsi->dev);
 }
 
+static void exynos_dsi_mode_set(struct drm_encoder *encoder,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode)
+{
+       struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+
+       drm_mode_copy(&dsi->mode, adjusted_mode);
+}
+
 static enum drm_connector_status
 exynos_dsi_detect(struct drm_connector *connector, bool force)
 {
@@ -1513,6 +1523,7 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder)
 static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = {
        .enable = exynos_dsi_enable,
        .disable = exynos_dsi_disable,
+       .mode_set = exynos_dsi_mode_set,
 };
 
 MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
index ba6ad14..f562e91 100644 (file)
@@ -31,7 +31,7 @@
 
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 
 #include "gma_display.h"
@@ -82,7 +82,6 @@ i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading)
 {
        struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
        int mode = MODE_I2C_START;
-       int ret;
 
        if (reading)
                mode |= MODE_I2C_READ;
@@ -90,8 +89,7 @@ i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading)
                mode |= MODE_I2C_WRITE;
        algo_data->address = address;
        algo_data->running = true;
-       ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
-       return ret;
+       return i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
 }
 
 /*
@@ -122,13 +120,11 @@ static int
 i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte)
 {
        struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
-       int ret;
 
        if (!algo_data->running)
                return -EIO;
 
-       ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL);
-       return ret;
+       return i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL);
 }
 
 /*
@@ -139,13 +135,11 @@ static int
 i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret)
 {
        struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
-       int ret;
 
        if (!algo_data->running)
                return -EIO;
 
-       ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret);
-       return ret;
+       return i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret);
 }
 
 static int
index 99da311..60ba7de 100644 (file)
@@ -335,7 +335,7 @@ int gma_crtc_cursor_set(struct drm_crtc *crtc,
        struct psb_gem_object *pobj;
        struct psb_gem_object *cursor_pobj = gma_crtc->cursor_pobj;
        struct drm_gem_object *obj;
-       void *tmp_dst, *tmp_src;
+       void *tmp_dst;
        int ret = 0, i, cursor_pages;
 
        /* If we didn't get a handle then turn the cursor off */
@@ -400,9 +400,7 @@ int gma_crtc_cursor_set(struct drm_crtc *crtc,
                /* Copy the cursor to cursor mem */
                tmp_dst = dev_priv->vram_addr + cursor_pobj->offset;
                for (i = 0; i < cursor_pages; i++) {
-                       tmp_src = kmap(pobj->pages[i]);
-                       memcpy(tmp_dst, tmp_src, PAGE_SIZE);
-                       kunmap(pobj->pages[i]);
+                       memcpy_from_page(tmp_dst, pobj->pages[i], 0, PAGE_SIZE);
                        tmp_dst += PAGE_SIZE;
                }
 
index d5ca5f2..ea7c16f 100644 (file)
@@ -6,7 +6,7 @@
  *    Eric Anholt <eric@anholt.net>
  */
 #include <drm/drm.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 #include "intel_bios.h"
 #include "psb_drv.h"
index fe9ace2..a70b01c 100644 (file)
@@ -184,17 +184,17 @@ struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver,
                pd->invalid_pte = 0;
        }
 
-       v = kmap(pd->dummy_pt);
+       v = kmap_local_page(pd->dummy_pt);
        for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i)
                v[i] = pd->invalid_pte;
 
-       kunmap(pd->dummy_pt);
+       kunmap_local(v);
 
-       v = kmap(pd->p);
+       v = kmap_local_page(pd->p);
        for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i)
                v[i] = pd->invalid_pde;
 
-       kunmap(pd->p);
+       kunmap_local(v);
 
        clear_page(kmap(pd->dummy_page));
        kunmap(pd->dummy_page);
index 610fc8e..fe4269c 100644 (file)
@@ -20,6 +20,7 @@
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_vram_helper.h>
 #include <drm/drm_managed.h>
+#include <drm/drm_module.h>
 #include <drm/drm_vblank.h>
 
 #include "hibmc_drm_drv.h"
@@ -379,7 +380,7 @@ static struct pci_driver hibmc_pci_driver = {
        .driver.pm =    &hibmc_pm_ops,
 };
 
-module_pci_driver(hibmc_pci_driver);
+drm_module_pci_driver(hibmc_pci_driver);
 
 MODULE_DEVICE_TABLE(pci, hibmc_pci_table);
 MODULE_AUTHOR("RongrongZou <zourongrong@huawei.com>");
index a4c94dc..2ac220b 100644 (file)
@@ -9,6 +9,7 @@ config DRM_I915
        # the shmem_readpage() which depends upon tmpfs
        select SHMEM
        select TMPFS
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select DRM_PANEL
        select DRM_MIPI_DSI
@@ -27,6 +28,7 @@ config DRM_I915
        select CEC_CORE if CEC_NOTIFIER
        select VMAP_PFN
        select DRM_TTM
+       select DRM_BUDDY
        help
          Choose this option if you have a system that has "Intel Graphics
          Media Accelerator" or "HD Graphics" integrated graphics,
index 1b62b9f..b136234 100644 (file)
@@ -161,7 +161,6 @@ gem-y += \
 i915-y += \
          $(gem-y) \
          i915_active.o \
-         i915_buddy.o \
          i915_cmd_parser.o \
          i915_deps.o \
          i915_gem_evict.o \
index 9d989c9..1ff1f1c 100644 (file)
@@ -25,7 +25,7 @@
  *
  */
 
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 #include "display/intel_display.h"
 #include "display/intel_display_types.h"
index bf7ce68..7d55821 100644 (file)
@@ -38,7 +38,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_atomic_uapi.h>
 #include <drm/drm_damage_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_plane_helper.h>
index c9c6efa..41e3dd2 100644 (file)
 #include <linux/pwm.h>
 #include <linux/sched/clock.h>
 
+#include <drm/dp/drm_dp_dual_mode_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_crtc.h>
-#include <drm/drm_dp_dual_mode_helper.h>
-#include <drm/drm_dp_mst_helper.h>
 #include <drm/drm_dsc.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_fb_helper.h>
index b5e2508..36ab58c 100644 (file)
@@ -36,7 +36,7 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_probe_helper.h>
 
index 540a669..82d024d 100644 (file)
@@ -6,8 +6,8 @@
  * Sean Paul <seanpaul@chromium.org>
  */
 
-#include <drm/drm_dp_helper.h>
-#include <drm/drm_dp_mst_helper.h>
+#include <drm/dp/drm_dp_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
 #include <drm/drm_hdcp.h>
 #include <drm/drm_print.h>
 
index 6a3a7b3..dbfb157 100644 (file)
@@ -6,7 +6,7 @@
 #ifndef __INTEL_DP_LINK_TRAINING_H__
 #define __INTEL_DP_LINK_TRAINING_H__
 
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 struct intel_crtc_state;
 struct intel_dp;
index 3b5b9e7..52f6dc2 100644 (file)
@@ -730,7 +730,7 @@ intel_hdmi_compute_avi_infoframe(struct intel_encoder *encoder,
        else
                frame->colorspace = HDMI_COLORSPACE_RGB;
 
-       drm_hdmi_avi_infoframe_colorspace(frame, conn_state);
+       drm_hdmi_avi_infoframe_colorimetry(frame, conn_state);
 
        /* nonsense combination */
        drm_WARN_ON(encoder->base.dev, crtc_state->limited_color_range &&
@@ -1912,7 +1912,7 @@ static bool intel_hdmi_sink_bpc_possible(struct drm_connector *connector,
                if (ycbcr420_output)
                        return hdmi->y420_dc_modes & DRM_EDID_YCBCR420_DC_36;
                else
-                       return info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36;
+                       return info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36;
        case 10:
                if (!has_hdmi_sink)
                        return false;
@@ -1920,7 +1920,7 @@ static bool intel_hdmi_sink_bpc_possible(struct drm_connector *connector,
                if (ycbcr420_output)
                        return hdmi->y420_dc_modes & DRM_EDID_YCBCR420_DC_30;
                else
-                       return info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30;
+                       return info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30;
        case 8:
                return true;
        default:
index 05d2d75..012f13e 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include <drm/drm_atomic_helper.h>
-#include <drm/drm_dp_dual_mode_helper.h>
+#include <drm/dp/drm_dp_dual_mode_helper.h>
 #include <drm/drm_edid.h>
 
 #include "intel_de.h"
@@ -537,7 +537,7 @@ void lspcon_set_infoframes(struct intel_encoder *encoder,
                frame.avi.colorspace = HDMI_COLORSPACE_RGB;
 
        /* Set the Colorspace as per the HDMI spec */
-       drm_hdmi_avi_infoframe_colorspace(&frame.avi, conn_state);
+       drm_hdmi_avi_infoframe_colorimetry(&frame.avi, conn_state);
 
        /* nonsense combination */
        drm_WARN_ON(encoder->base.dev, crtc_state->limited_color_range &&
diff --git a/drivers/gpu/drm/i915/i915_buddy.c b/drivers/gpu/drm/i915/i915_buddy.c
deleted file mode 100644 (file)
index 6e2ad68..0000000
+++ /dev/null
@@ -1,466 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2021 Intel Corporation
- */
-
-#include <linux/kmemleak.h>
-#include <linux/sizes.h>
-
-#include "i915_buddy.h"
-
-#include "i915_gem.h"
-#include "i915_utils.h"
-
-static struct kmem_cache *slab_blocks;
-
-static struct i915_buddy_block *i915_block_alloc(struct i915_buddy_mm *mm,
-                                                struct i915_buddy_block *parent,
-                                                unsigned int order,
-                                                u64 offset)
-{
-       struct i915_buddy_block *block;
-
-       GEM_BUG_ON(order > I915_BUDDY_MAX_ORDER);
-
-       block = kmem_cache_zalloc(slab_blocks, GFP_KERNEL);
-       if (!block)
-               return NULL;
-
-       block->header = offset;
-       block->header |= order;
-       block->parent = parent;
-
-       GEM_BUG_ON(block->header & I915_BUDDY_HEADER_UNUSED);
-       return block;
-}
-
-static void i915_block_free(struct i915_buddy_mm *mm,
-                           struct i915_buddy_block *block)
-{
-       kmem_cache_free(slab_blocks, block);
-}
-
-static void mark_allocated(struct i915_buddy_block *block)
-{
-       block->header &= ~I915_BUDDY_HEADER_STATE;
-       block->header |= I915_BUDDY_ALLOCATED;
-
-       list_del(&block->link);
-}
-
-static void mark_free(struct i915_buddy_mm *mm,
-                     struct i915_buddy_block *block)
-{
-       block->header &= ~I915_BUDDY_HEADER_STATE;
-       block->header |= I915_BUDDY_FREE;
-
-       list_add(&block->link,
-                &mm->free_list[i915_buddy_block_order(block)]);
-}
-
-static void mark_split(struct i915_buddy_block *block)
-{
-       block->header &= ~I915_BUDDY_HEADER_STATE;
-       block->header |= I915_BUDDY_SPLIT;
-
-       list_del(&block->link);
-}
-
-int i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size)
-{
-       unsigned int i;
-       u64 offset;
-
-       if (size < chunk_size)
-               return -EINVAL;
-
-       if (chunk_size < PAGE_SIZE)
-               return -EINVAL;
-
-       if (!is_power_of_2(chunk_size))
-               return -EINVAL;
-
-       size = round_down(size, chunk_size);
-
-       mm->size = size;
-       mm->avail = size;
-       mm->chunk_size = chunk_size;
-       mm->max_order = ilog2(size) - ilog2(chunk_size);
-
-       GEM_BUG_ON(mm->max_order > I915_BUDDY_MAX_ORDER);
-
-       mm->free_list = kmalloc_array(mm->max_order + 1,
-                                     sizeof(struct list_head),
-                                     GFP_KERNEL);
-       if (!mm->free_list)
-               return -ENOMEM;
-
-       for (i = 0; i <= mm->max_order; ++i)
-               INIT_LIST_HEAD(&mm->free_list[i]);
-
-       mm->n_roots = hweight64(size);
-
-       mm->roots = kmalloc_array(mm->n_roots,
-                                 sizeof(struct i915_buddy_block *),
-                                 GFP_KERNEL);
-       if (!mm->roots)
-               goto out_free_list;
-
-       offset = 0;
-       i = 0;
-
-       /*
-        * Split into power-of-two blocks, in case we are given a size that is
-        * not itself a power-of-two.
-        */
-       do {
-               struct i915_buddy_block *root;
-               unsigned int order;
-               u64 root_size;
-
-               root_size = rounddown_pow_of_two(size);
-               order = ilog2(root_size) - ilog2(chunk_size);
-
-               root = i915_block_alloc(mm, NULL, order, offset);
-               if (!root)
-                       goto out_free_roots;
-
-               mark_free(mm, root);
-
-               GEM_BUG_ON(i > mm->max_order);
-               GEM_BUG_ON(i915_buddy_block_size(mm, root) < chunk_size);
-
-               mm->roots[i] = root;
-
-               offset += root_size;
-               size -= root_size;
-               i++;
-       } while (size);
-
-       return 0;
-
-out_free_roots:
-       while (i--)
-               i915_block_free(mm, mm->roots[i]);
-       kfree(mm->roots);
-out_free_list:
-       kfree(mm->free_list);
-       return -ENOMEM;
-}
-
-void i915_buddy_fini(struct i915_buddy_mm *mm)
-{
-       int i;
-
-       for (i = 0; i < mm->n_roots; ++i) {
-               GEM_WARN_ON(!i915_buddy_block_is_free(mm->roots[i]));
-               i915_block_free(mm, mm->roots[i]);
-       }
-
-       GEM_WARN_ON(mm->avail != mm->size);
-
-       kfree(mm->roots);
-       kfree(mm->free_list);
-}
-
-static int split_block(struct i915_buddy_mm *mm,
-                      struct i915_buddy_block *block)
-{
-       unsigned int block_order = i915_buddy_block_order(block) - 1;
-       u64 offset = i915_buddy_block_offset(block);
-
-       GEM_BUG_ON(!i915_buddy_block_is_free(block));
-       GEM_BUG_ON(!i915_buddy_block_order(block));
-
-       block->left = i915_block_alloc(mm, block, block_order, offset);
-       if (!block->left)
-               return -ENOMEM;
-
-       block->right = i915_block_alloc(mm, block, block_order,
-                                       offset + (mm->chunk_size << block_order));
-       if (!block->right) {
-               i915_block_free(mm, block->left);
-               return -ENOMEM;
-       }
-
-       mark_free(mm, block->left);
-       mark_free(mm, block->right);
-
-       mark_split(block);
-
-       return 0;
-}
-
-static struct i915_buddy_block *
-get_buddy(struct i915_buddy_block *block)
-{
-       struct i915_buddy_block *parent;
-
-       parent = block->parent;
-       if (!parent)
-               return NULL;
-
-       if (parent->left == block)
-               return parent->right;
-
-       return parent->left;
-}
-
-static void __i915_buddy_free(struct i915_buddy_mm *mm,
-                             struct i915_buddy_block *block)
-{
-       struct i915_buddy_block *parent;
-
-       while ((parent = block->parent)) {
-               struct i915_buddy_block *buddy;
-
-               buddy = get_buddy(block);
-
-               if (!i915_buddy_block_is_free(buddy))
-                       break;
-
-               list_del(&buddy->link);
-
-               i915_block_free(mm, block);
-               i915_block_free(mm, buddy);
-
-               block = parent;
-       }
-
-       mark_free(mm, block);
-}
-
-void i915_buddy_free(struct i915_buddy_mm *mm,
-                    struct i915_buddy_block *block)
-{
-       GEM_BUG_ON(!i915_buddy_block_is_allocated(block));
-       mm->avail += i915_buddy_block_size(mm, block);
-       __i915_buddy_free(mm, block);
-}
-
-void i915_buddy_free_list(struct i915_buddy_mm *mm, struct list_head *objects)
-{
-       struct i915_buddy_block *block, *on;
-
-       list_for_each_entry_safe(block, on, objects, link) {
-               i915_buddy_free(mm, block);
-               cond_resched();
-       }
-       INIT_LIST_HEAD(objects);
-}
-
-/*
- * Allocate power-of-two block. The order value here translates to:
- *
- *   0 = 2^0 * mm->chunk_size
- *   1 = 2^1 * mm->chunk_size
- *   2 = 2^2 * mm->chunk_size
- *   ...
- */
-struct i915_buddy_block *
-i915_buddy_alloc(struct i915_buddy_mm *mm, unsigned int order)
-{
-       struct i915_buddy_block *block = NULL;
-       unsigned int i;
-       int err;
-
-       for (i = order; i <= mm->max_order; ++i) {
-               block = list_first_entry_or_null(&mm->free_list[i],
-                                                struct i915_buddy_block,
-                                                link);
-               if (block)
-                       break;
-       }
-
-       if (!block)
-               return ERR_PTR(-ENOSPC);
-
-       GEM_BUG_ON(!i915_buddy_block_is_free(block));
-
-       while (i != order) {
-               err = split_block(mm, block);
-               if (unlikely(err))
-                       goto out_free;
-
-               /* Go low */
-               block = block->left;
-               i--;
-       }
-
-       mark_allocated(block);
-       mm->avail -= i915_buddy_block_size(mm, block);
-       kmemleak_update_trace(block);
-       return block;
-
-out_free:
-       if (i != order)
-               __i915_buddy_free(mm, block);
-       return ERR_PTR(err);
-}
-
-static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
-{
-       return s1 <= e2 && e1 >= s2;
-}
-
-static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2)
-{
-       return s1 <= s2 && e1 >= e2;
-}
-
-/*
- * Allocate range. Note that it's safe to chain together multiple alloc_ranges
- * with the same blocks list.
- *
- * Intended for pre-allocating portions of the address space, for example to
- * reserve a block for the initial framebuffer or similar, hence the expectation
- * here is that i915_buddy_alloc() is still the main vehicle for
- * allocations, so if that's not the case then the drm_mm range allocator is
- * probably a much better fit, and so you should probably go use that instead.
- */
-int i915_buddy_alloc_range(struct i915_buddy_mm *mm,
-                          struct list_head *blocks,
-                          u64 start, u64 size)
-{
-       struct i915_buddy_block *block;
-       struct i915_buddy_block *buddy;
-       LIST_HEAD(allocated);
-       LIST_HEAD(dfs);
-       u64 end;
-       int err;
-       int i;
-
-       if (size < mm->chunk_size)
-               return -EINVAL;
-
-       if (!IS_ALIGNED(size | start, mm->chunk_size))
-               return -EINVAL;
-
-       if (range_overflows(start, size, mm->size))
-               return -EINVAL;
-
-       for (i = 0; i < mm->n_roots; ++i)
-               list_add_tail(&mm->roots[i]->tmp_link, &dfs);
-
-       end = start + size - 1;
-
-       do {
-               u64 block_start;
-               u64 block_end;
-
-               block = list_first_entry_or_null(&dfs,
-                                                struct i915_buddy_block,
-                                                tmp_link);
-               if (!block)
-                       break;
-
-               list_del(&block->tmp_link);
-
-               block_start = i915_buddy_block_offset(block);
-               block_end = block_start + i915_buddy_block_size(mm, block) - 1;
-
-               if (!overlaps(start, end, block_start, block_end))
-                       continue;
-
-               if (i915_buddy_block_is_allocated(block)) {
-                       err = -ENOSPC;
-                       goto err_free;
-               }
-
-               if (contains(start, end, block_start, block_end)) {
-                       if (!i915_buddy_block_is_free(block)) {
-                               err = -ENOSPC;
-                               goto err_free;
-                       }
-
-                       mark_allocated(block);
-                       mm->avail -= i915_buddy_block_size(mm, block);
-                       list_add_tail(&block->link, &allocated);
-                       continue;
-               }
-
-               if (!i915_buddy_block_is_split(block)) {
-                       err = split_block(mm, block);
-                       if (unlikely(err))
-                               goto err_undo;
-               }
-
-               list_add(&block->right->tmp_link, &dfs);
-               list_add(&block->left->tmp_link, &dfs);
-       } while (1);
-
-       list_splice_tail(&allocated, blocks);
-       return 0;
-
-err_undo:
-       /*
-        * We really don't want to leave around a bunch of split blocks, since
-        * bigger is better, so make sure we merge everything back before we
-        * free the allocated blocks.
-        */
-       buddy = get_buddy(block);
-       if (buddy &&
-           (i915_buddy_block_is_free(block) &&
-            i915_buddy_block_is_free(buddy)))
-               __i915_buddy_free(mm, block);
-
-err_free:
-       i915_buddy_free_list(mm, &allocated);
-       return err;
-}
-
-void i915_buddy_block_print(struct i915_buddy_mm *mm,
-                           struct i915_buddy_block *block,
-                           struct drm_printer *p)
-{
-       u64 start = i915_buddy_block_offset(block);
-       u64 size = i915_buddy_block_size(mm, block);
-
-       drm_printf(p, "%#018llx-%#018llx: %llu\n", start, start + size, size);
-}
-
-void i915_buddy_print(struct i915_buddy_mm *mm, struct drm_printer *p)
-{
-       int order;
-
-       drm_printf(p, "chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB\n",
-                  mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20);
-
-       for (order = mm->max_order; order >= 0; order--) {
-               struct i915_buddy_block *block;
-               u64 count = 0, free;
-
-               list_for_each_entry(block, &mm->free_list[order], link) {
-                       GEM_BUG_ON(!i915_buddy_block_is_free(block));
-                       count++;
-               }
-
-               drm_printf(p, "order-%d ", order);
-
-               free = count * (mm->chunk_size << order);
-               if (free < SZ_1M)
-                       drm_printf(p, "free: %lluKiB", free >> 10);
-               else
-                       drm_printf(p, "free: %lluMiB", free >> 20);
-
-               drm_printf(p, ", pages: %llu\n", count);
-       }
-}
-
-#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
-#include "selftests/i915_buddy.c"
-#endif
-
-void i915_buddy_module_exit(void)
-{
-       kmem_cache_destroy(slab_blocks);
-}
-
-int __init i915_buddy_module_init(void)
-{
-       slab_blocks = KMEM_CACHE(i915_buddy_block, 0);
-       if (!slab_blocks)
-               return -ENOMEM;
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/i915/i915_buddy.h b/drivers/gpu/drm/i915/i915_buddy.h
deleted file mode 100644 (file)
index 7077742..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2021 Intel Corporation
- */
-
-#ifndef __I915_BUDDY_H__
-#define __I915_BUDDY_H__
-
-#include <linux/bitops.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-
-#include <drm/drm_print.h>
-
-struct i915_buddy_block {
-#define I915_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12)
-#define I915_BUDDY_HEADER_STATE  GENMASK_ULL(11, 10)
-#define   I915_BUDDY_ALLOCATED    (1 << 10)
-#define   I915_BUDDY_FREE         (2 << 10)
-#define   I915_BUDDY_SPLIT        (3 << 10)
-/* Free to be used, if needed in the future */
-#define I915_BUDDY_HEADER_UNUSED GENMASK_ULL(9, 6)
-#define I915_BUDDY_HEADER_ORDER  GENMASK_ULL(5, 0)
-       u64 header;
-
-       struct i915_buddy_block *left;
-       struct i915_buddy_block *right;
-       struct i915_buddy_block *parent;
-
-       void *private; /* owned by creator */
-
-       /*
-        * While the block is allocated by the user through i915_buddy_alloc*,
-        * the user has ownership of the link, for example to maintain within
-        * a list, if so desired. As soon as the block is freed with
-        * i915_buddy_free* ownership is given back to the mm.
-        */
-       struct list_head link;
-       struct list_head tmp_link;
-};
-
-/* Order-zero must be at least PAGE_SIZE */
-#define I915_BUDDY_MAX_ORDER (63 - PAGE_SHIFT)
-
-/*
- * Binary Buddy System.
- *
- * Locking should be handled by the user, a simple mutex around
- * i915_buddy_alloc* and i915_buddy_free* should suffice.
- */
-struct i915_buddy_mm {
-       /* Maintain a free list for each order. */
-       struct list_head *free_list;
-
-       /*
-        * Maintain explicit binary tree(s) to track the allocation of the
-        * address space. This gives us a simple way of finding a buddy block
-        * and performing the potentially recursive merge step when freeing a
-        * block.  Nodes are either allocated or free, in which case they will
-        * also exist on the respective free list.
-        */
-       struct i915_buddy_block **roots;
-
-       /*
-        * Anything from here is public, and remains static for the lifetime of
-        * the mm. Everything above is considered do-not-touch.
-        */
-       unsigned int n_roots;
-       unsigned int max_order;
-
-       /* Must be at least PAGE_SIZE */
-       u64 chunk_size;
-       u64 size;
-       u64 avail;
-};
-
-static inline u64
-i915_buddy_block_offset(struct i915_buddy_block *block)
-{
-       return block->header & I915_BUDDY_HEADER_OFFSET;
-}
-
-static inline unsigned int
-i915_buddy_block_order(struct i915_buddy_block *block)
-{
-       return block->header & I915_BUDDY_HEADER_ORDER;
-}
-
-static inline unsigned int
-i915_buddy_block_state(struct i915_buddy_block *block)
-{
-       return block->header & I915_BUDDY_HEADER_STATE;
-}
-
-static inline bool
-i915_buddy_block_is_allocated(struct i915_buddy_block *block)
-{
-       return i915_buddy_block_state(block) == I915_BUDDY_ALLOCATED;
-}
-
-static inline bool
-i915_buddy_block_is_free(struct i915_buddy_block *block)
-{
-       return i915_buddy_block_state(block) == I915_BUDDY_FREE;
-}
-
-static inline bool
-i915_buddy_block_is_split(struct i915_buddy_block *block)
-{
-       return i915_buddy_block_state(block) == I915_BUDDY_SPLIT;
-}
-
-static inline u64
-i915_buddy_block_size(struct i915_buddy_mm *mm,
-                     struct i915_buddy_block *block)
-{
-       return mm->chunk_size << i915_buddy_block_order(block);
-}
-
-int i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size);
-
-void i915_buddy_fini(struct i915_buddy_mm *mm);
-
-struct i915_buddy_block *
-i915_buddy_alloc(struct i915_buddy_mm *mm, unsigned int order);
-
-int i915_buddy_alloc_range(struct i915_buddy_mm *mm,
-                          struct list_head *blocks,
-                          u64 start, u64 size);
-
-void i915_buddy_free(struct i915_buddy_mm *mm, struct i915_buddy_block *block);
-
-void i915_buddy_free_list(struct i915_buddy_mm *mm, struct list_head *objects);
-
-void i915_buddy_print(struct i915_buddy_mm *mm, struct drm_printer *p);
-void i915_buddy_block_print(struct i915_buddy_mm *mm,
-                           struct i915_buddy_block *block,
-                           struct drm_printer *p);
-
-void i915_buddy_module_exit(void);
-int i915_buddy_module_init(void);
-
-#endif
index f6bcd2f..8451822 100644 (file)
@@ -9,7 +9,6 @@
 #include "gem/i915_gem_context.h"
 #include "gem/i915_gem_object.h"
 #include "i915_active.h"
-#include "i915_buddy.h"
 #include "i915_params.h"
 #include "i915_pci.h"
 #include "i915_perf.h"
@@ -50,8 +49,6 @@ static const struct {
        { .init = i915_check_nomodeset },
        { .init = i915_active_module_init,
          .exit = i915_active_module_exit },
-       { .init = i915_buddy_module_init,
-         .exit = i915_buddy_module_exit },
        { .init = i915_context_module_init,
          .exit = i915_context_module_exit },
        { .init = i915_gem_context_module_init,
index 41f2adb..159571b 100644 (file)
@@ -5,10 +5,9 @@
  */
 
 #include "i915_scatterlist.h"
-
-#include "i915_buddy.h"
 #include "i915_ttm_buddy_manager.h"
 
+#include <drm/drm_buddy.h>
 #include <drm/drm_mm.h>
 
 #include <linux/slab.h>
@@ -153,9 +152,9 @@ struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res,
        struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
        const u64 size = res->num_pages << PAGE_SHIFT;
        const u64 max_segment = rounddown(UINT_MAX, PAGE_SIZE);
-       struct i915_buddy_mm *mm = bman_res->mm;
+       struct drm_buddy *mm = bman_res->mm;
        struct list_head *blocks = &bman_res->blocks;
-       struct i915_buddy_block *block;
+       struct drm_buddy_block *block;
        struct i915_refct_sgt *rsgt;
        struct scatterlist *sg;
        struct sg_table *st;
@@ -181,8 +180,8 @@ struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res,
        list_for_each_entry(block, blocks, link) {
                u64 block_size, offset;
 
-               block_size = min_t(u64, size, i915_buddy_block_size(mm, block));
-               offset = i915_buddy_block_offset(block);
+               block_size = min_t(u64, size, drm_buddy_block_size(mm, block));
+               offset = drm_buddy_block_offset(block);
 
                while (block_size) {
                        u64 len;
index d59fbb0..247714b 100644 (file)
@@ -8,14 +8,15 @@
 #include <drm/ttm/ttm_bo_driver.h>
 #include <drm/ttm/ttm_placement.h>
 
+#include <drm/drm_buddy.h>
+
 #include "i915_ttm_buddy_manager.h"
 
-#include "i915_buddy.h"
 #include "i915_gem.h"
 
 struct i915_ttm_buddy_manager {
        struct ttm_resource_manager manager;
-       struct i915_buddy_mm mm;
+       struct drm_buddy mm;
        struct list_head reserved;
        struct mutex lock;
        u64 default_page_size;
@@ -34,7 +35,7 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
 {
        struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
        struct i915_ttm_buddy_resource *bman_res;
-       struct i915_buddy_mm *mm = &bman->mm;
+       struct drm_buddy *mm = &bman->mm;
        unsigned long n_pages;
        unsigned int min_order;
        u64 min_page_size;
@@ -73,7 +74,7 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
        n_pages = size >> ilog2(mm->chunk_size);
 
        do {
-               struct i915_buddy_block *block;
+               struct drm_buddy_block *block;
                unsigned int order;
 
                order = fls(n_pages) - 1;
@@ -82,7 +83,7 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
 
                do {
                        mutex_lock(&bman->lock);
-                       block = i915_buddy_alloc(mm, order);
+                       block = drm_buddy_alloc_blocks(mm, order);
                        mutex_unlock(&bman->lock);
                        if (!IS_ERR(block))
                                break;
@@ -106,9 +107,10 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
 
 err_free_blocks:
        mutex_lock(&bman->lock);
-       i915_buddy_free_list(mm, &bman_res->blocks);
+       drm_buddy_free_list(mm, &bman_res->blocks);
        mutex_unlock(&bman->lock);
 err_free_res:
+       ttm_resource_fini(man, &bman_res->base);
        kfree(bman_res);
        return err;
 }
@@ -120,9 +122,10 @@ static void i915_ttm_buddy_man_free(struct ttm_resource_manager *man,
        struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
 
        mutex_lock(&bman->lock);
-       i915_buddy_free_list(&bman->mm, &bman_res->blocks);
+       drm_buddy_free_list(&bman->mm, &bman_res->blocks);
        mutex_unlock(&bman->lock);
 
+       ttm_resource_fini(man, res);
        kfree(bman_res);
 }
 
@@ -130,17 +133,17 @@ static void i915_ttm_buddy_man_debug(struct ttm_resource_manager *man,
                                     struct drm_printer *printer)
 {
        struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
-       struct i915_buddy_block *block;
+       struct drm_buddy_block *block;
 
        mutex_lock(&bman->lock);
        drm_printf(printer, "default_page_size: %lluKiB\n",
                   bman->default_page_size >> 10);
 
-       i915_buddy_print(&bman->mm, printer);
+       drm_buddy_print(&bman->mm, printer);
 
        drm_printf(printer, "reserved:\n");
        list_for_each_entry(block, &bman->reserved, link)
-               i915_buddy_block_print(&bman->mm, block, printer);
+               drm_buddy_block_print(&bman->mm, block, printer);
        mutex_unlock(&bman->lock);
 }
 
@@ -190,7 +193,7 @@ int i915_ttm_buddy_man_init(struct ttm_device *bdev,
        if (!bman)
                return -ENOMEM;
 
-       err = i915_buddy_init(&bman->mm, size, chunk_size);
+       err = drm_buddy_init(&bman->mm, size, chunk_size);
        if (err)
                goto err_free_bman;
 
@@ -202,7 +205,7 @@ int i915_ttm_buddy_man_init(struct ttm_device *bdev,
        man = &bman->manager;
        man->use_tt = use_tt;
        man->func = &i915_ttm_buddy_manager_func;
-       ttm_resource_manager_init(man, bman->mm.size >> PAGE_SHIFT);
+       ttm_resource_manager_init(man, bdev, bman->mm.size >> PAGE_SHIFT);
 
        ttm_resource_manager_set_used(man, true);
        ttm_set_driver_manager(bdev, type, man);
@@ -228,7 +231,7 @@ int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type)
 {
        struct ttm_resource_manager *man = ttm_manager_type(bdev, type);
        struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
-       struct i915_buddy_mm *mm = &bman->mm;
+       struct drm_buddy *mm = &bman->mm;
        int ret;
 
        ttm_resource_manager_set_used(man, false);
@@ -240,8 +243,8 @@ int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type)
        ttm_set_driver_manager(bdev, type, NULL);
 
        mutex_lock(&bman->lock);
-       i915_buddy_free_list(mm, &bman->reserved);
-       i915_buddy_fini(mm);
+       drm_buddy_free_list(mm, &bman->reserved);
+       drm_buddy_fini(mm);
        mutex_unlock(&bman->lock);
 
        ttm_resource_manager_cleanup(man);
@@ -264,11 +267,11 @@ int i915_ttm_buddy_man_reserve(struct ttm_resource_manager *man,
                               u64 start, u64 size)
 {
        struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
-       struct i915_buddy_mm *mm = &bman->mm;
+       struct drm_buddy *mm = &bman->mm;
        int ret;
 
        mutex_lock(&bman->lock);
-       ret = i915_buddy_alloc_range(mm, &bman->reserved, start, size);
+       ret = drm_buddy_alloc_range(mm, &bman->reserved, start, size);
        mutex_unlock(&bman->lock);
 
        return ret;
index 0722d33..3120779 100644 (file)
@@ -13,7 +13,7 @@
 
 struct ttm_device;
 struct ttm_resource_manager;
-struct i915_buddy_mm;
+struct drm_buddy;
 
 /**
  * struct i915_ttm_buddy_resource
@@ -28,7 +28,7 @@ struct i915_buddy_mm;
 struct i915_ttm_buddy_resource {
        struct ttm_resource base;
        struct list_head blocks;
-       struct i915_buddy_mm *mm;
+       struct drm_buddy *mm;
 };
 
 /**
diff --git a/drivers/gpu/drm/i915/selftests/i915_buddy.c b/drivers/gpu/drm/i915/selftests/i915_buddy.c
deleted file mode 100644 (file)
index d61ec9c..0000000
+++ /dev/null
@@ -1,787 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2019 Intel Corporation
- */
-
-#include <linux/prime_numbers.h>
-
-#include "../i915_selftest.h"
-#include "i915_random.h"
-
-static void __igt_dump_block(struct i915_buddy_mm *mm,
-                            struct i915_buddy_block *block,
-                            bool buddy)
-{
-       pr_err("block info: header=%llx, state=%u, order=%d, offset=%llx size=%llx root=%s buddy=%s\n",
-              block->header,
-              i915_buddy_block_state(block),
-              i915_buddy_block_order(block),
-              i915_buddy_block_offset(block),
-              i915_buddy_block_size(mm, block),
-              yesno(!block->parent),
-              yesno(buddy));
-}
-
-static void igt_dump_block(struct i915_buddy_mm *mm,
-                          struct i915_buddy_block *block)
-{
-       struct i915_buddy_block *buddy;
-
-       __igt_dump_block(mm, block, false);
-
-       buddy = get_buddy(block);
-       if (buddy)
-               __igt_dump_block(mm, buddy, true);
-}
-
-static int igt_check_block(struct i915_buddy_mm *mm,
-                          struct i915_buddy_block *block)
-{
-       struct i915_buddy_block *buddy;
-       unsigned int block_state;
-       u64 block_size;
-       u64 offset;
-       int err = 0;
-
-       block_state = i915_buddy_block_state(block);
-
-       if (block_state != I915_BUDDY_ALLOCATED &&
-           block_state != I915_BUDDY_FREE &&
-           block_state != I915_BUDDY_SPLIT) {
-               pr_err("block state mismatch\n");
-               err = -EINVAL;
-       }
-
-       block_size = i915_buddy_block_size(mm, block);
-       offset = i915_buddy_block_offset(block);
-
-       if (block_size < mm->chunk_size) {
-               pr_err("block size smaller than min size\n");
-               err = -EINVAL;
-       }
-
-       if (!is_power_of_2(block_size)) {
-               pr_err("block size not power of two\n");
-               err = -EINVAL;
-       }
-
-       if (!IS_ALIGNED(block_size, mm->chunk_size)) {
-               pr_err("block size not aligned to min size\n");
-               err = -EINVAL;
-       }
-
-       if (!IS_ALIGNED(offset, mm->chunk_size)) {
-               pr_err("block offset not aligned to min size\n");
-               err = -EINVAL;
-       }
-
-       if (!IS_ALIGNED(offset, block_size)) {
-               pr_err("block offset not aligned to block size\n");
-               err = -EINVAL;
-       }
-
-       buddy = get_buddy(block);
-
-       if (!buddy && block->parent) {
-               pr_err("buddy has gone fishing\n");
-               err = -EINVAL;
-       }
-
-       if (buddy) {
-               if (i915_buddy_block_offset(buddy) != (offset ^ block_size)) {
-                       pr_err("buddy has wrong offset\n");
-                       err = -EINVAL;
-               }
-
-               if (i915_buddy_block_size(mm, buddy) != block_size) {
-                       pr_err("buddy size mismatch\n");
-                       err = -EINVAL;
-               }
-
-               if (i915_buddy_block_state(buddy) == block_state &&
-                   block_state == I915_BUDDY_FREE) {
-                       pr_err("block and its buddy are free\n");
-                       err = -EINVAL;
-               }
-       }
-
-       return err;
-}
-
-static int igt_check_blocks(struct i915_buddy_mm *mm,
-                           struct list_head *blocks,
-                           u64 expected_size,
-                           bool is_contiguous)
-{
-       struct i915_buddy_block *block;
-       struct i915_buddy_block *prev;
-       u64 total;
-       int err = 0;
-
-       block = NULL;
-       prev = NULL;
-       total = 0;
-
-       list_for_each_entry(block, blocks, link) {
-               err = igt_check_block(mm, block);
-
-               if (!i915_buddy_block_is_allocated(block)) {
-                       pr_err("block not allocated\n"),
-                       err = -EINVAL;
-               }
-
-               if (is_contiguous && prev) {
-                       u64 prev_block_size;
-                       u64 prev_offset;
-                       u64 offset;
-
-                       prev_offset = i915_buddy_block_offset(prev);
-                       prev_block_size = i915_buddy_block_size(mm, prev);
-                       offset = i915_buddy_block_offset(block);
-
-                       if (offset != (prev_offset + prev_block_size)) {
-                               pr_err("block offset mismatch\n");
-                               err = -EINVAL;
-                       }
-               }
-
-               if (err)
-                       break;
-
-               total += i915_buddy_block_size(mm, block);
-               prev = block;
-       }
-
-       if (!err) {
-               if (total != expected_size) {
-                       pr_err("size mismatch, expected=%llx, found=%llx\n",
-                              expected_size, total);
-                       err = -EINVAL;
-               }
-               return err;
-       }
-
-       if (prev) {
-               pr_err("prev block, dump:\n");
-               igt_dump_block(mm, prev);
-       }
-
-       pr_err("bad block, dump:\n");
-       igt_dump_block(mm, block);
-
-       return err;
-}
-
-static int igt_check_mm(struct i915_buddy_mm *mm)
-{
-       struct i915_buddy_block *root;
-       struct i915_buddy_block *prev;
-       unsigned int i;
-       u64 total;
-       int err = 0;
-
-       if (!mm->n_roots) {
-               pr_err("n_roots is zero\n");
-               return -EINVAL;
-       }
-
-       if (mm->n_roots != hweight64(mm->size)) {
-               pr_err("n_roots mismatch, n_roots=%u, expected=%lu\n",
-                      mm->n_roots, hweight64(mm->size));
-               return -EINVAL;
-       }
-
-       root = NULL;
-       prev = NULL;
-       total = 0;
-
-       for (i = 0; i < mm->n_roots; ++i) {
-               struct i915_buddy_block *block;
-               unsigned int order;
-
-               root = mm->roots[i];
-               if (!root) {
-                       pr_err("root(%u) is NULL\n", i);
-                       err = -EINVAL;
-                       break;
-               }
-
-               err = igt_check_block(mm, root);
-
-               if (!i915_buddy_block_is_free(root)) {
-                       pr_err("root not free\n");
-                       err = -EINVAL;
-               }
-
-               order = i915_buddy_block_order(root);
-
-               if (!i) {
-                       if (order != mm->max_order) {
-                               pr_err("max order root missing\n");
-                               err = -EINVAL;
-                       }
-               }
-
-               if (prev) {
-                       u64 prev_block_size;
-                       u64 prev_offset;
-                       u64 offset;
-
-                       prev_offset = i915_buddy_block_offset(prev);
-                       prev_block_size = i915_buddy_block_size(mm, prev);
-                       offset = i915_buddy_block_offset(root);
-
-                       if (offset != (prev_offset + prev_block_size)) {
-                               pr_err("root offset mismatch\n");
-                               err = -EINVAL;
-                       }
-               }
-
-               block = list_first_entry_or_null(&mm->free_list[order],
-                                                struct i915_buddy_block,
-                                                link);
-               if (block != root) {
-                       pr_err("root mismatch at order=%u\n", order);
-                       err = -EINVAL;
-               }
-
-               if (err)
-                       break;
-
-               prev = root;
-               total += i915_buddy_block_size(mm, root);
-       }
-
-       if (!err) {
-               if (total != mm->size) {
-                       pr_err("expected mm size=%llx, found=%llx\n", mm->size,
-                              total);
-                       err = -EINVAL;
-               }
-               return err;
-       }
-
-       if (prev) {
-               pr_err("prev root(%u), dump:\n", i - 1);
-               igt_dump_block(mm, prev);
-       }
-
-       if (root) {
-               pr_err("bad root(%u), dump:\n", i);
-               igt_dump_block(mm, root);
-       }
-
-       return err;
-}
-
-static void igt_mm_config(u64 *size, u64 *chunk_size)
-{
-       I915_RND_STATE(prng);
-       u32 s, ms;
-
-       /* Nothing fancy, just try to get an interesting bit pattern */
-
-       prandom_seed_state(&prng, i915_selftest.random_seed);
-
-       /* Let size be a random number of pages up to 8 GB (2M pages) */
-       s = 1 + i915_prandom_u32_max_state((BIT(33 - 12)) - 1, &prng);
-       /* Let the chunk size be a random power of 2 less than size */
-       ms = BIT(i915_prandom_u32_max_state(ilog2(s), &prng));
-       /* Round size down to the chunk size */
-       s &= -ms;
-
-       /* Convert from pages to bytes */
-       *chunk_size = (u64)ms << 12;
-       *size = (u64)s << 12;
-}
-
-static int igt_buddy_alloc_smoke(void *arg)
-{
-       struct i915_buddy_mm mm;
-       IGT_TIMEOUT(end_time);
-       I915_RND_STATE(prng);
-       u64 chunk_size;
-       u64 mm_size;
-       int *order;
-       int err, i;
-
-       igt_mm_config(&mm_size, &chunk_size);
-
-       pr_info("buddy_init with size=%llx, chunk_size=%llx\n", mm_size, chunk_size);
-
-       err = i915_buddy_init(&mm, mm_size, chunk_size);
-       if (err) {
-               pr_err("buddy_init failed(%d)\n", err);
-               return err;
-       }
-
-       order = i915_random_order(mm.max_order + 1, &prng);
-       if (!order)
-               goto out_fini;
-
-       for (i = 0; i <= mm.max_order; ++i) {
-               struct i915_buddy_block *block;
-               int max_order = order[i];
-               bool timeout = false;
-               LIST_HEAD(blocks);
-               int order;
-               u64 total;
-
-               err = igt_check_mm(&mm);
-               if (err) {
-                       pr_err("pre-mm check failed, abort\n");
-                       break;
-               }
-
-               pr_info("filling from max_order=%u\n", max_order);
-
-               order = max_order;
-               total = 0;
-
-               do {
-retry:
-                       block = i915_buddy_alloc(&mm, order);
-                       if (IS_ERR(block)) {
-                               err = PTR_ERR(block);
-                               if (err == -ENOMEM) {
-                                       pr_info("buddy_alloc hit -ENOMEM with order=%d\n",
-                                               order);
-                               } else {
-                                       if (order--) {
-                                               err = 0;
-                                               goto retry;
-                                       }
-
-                                       pr_err("buddy_alloc with order=%d failed(%d)\n",
-                                              order, err);
-                               }
-
-                               break;
-                       }
-
-                       list_add_tail(&block->link, &blocks);
-
-                       if (i915_buddy_block_order(block) != order) {
-                               pr_err("buddy_alloc order mismatch\n");
-                               err = -EINVAL;
-                               break;
-                       }
-
-                       total += i915_buddy_block_size(&mm, block);
-
-                       if (__igt_timeout(end_time, NULL)) {
-                               timeout = true;
-                               break;
-                       }
-               } while (total < mm.size);
-
-               if (!err)
-                       err = igt_check_blocks(&mm, &blocks, total, false);
-
-               i915_buddy_free_list(&mm, &blocks);
-
-               if (!err) {
-                       err = igt_check_mm(&mm);
-                       if (err)
-                               pr_err("post-mm check failed\n");
-               }
-
-               if (err || timeout)
-                       break;
-
-               cond_resched();
-       }
-
-       if (err == -ENOMEM)
-               err = 0;
-
-       kfree(order);
-out_fini:
-       i915_buddy_fini(&mm);
-
-       return err;
-}
-
-static int igt_buddy_alloc_pessimistic(void *arg)
-{
-       const unsigned int max_order = 16;
-       struct i915_buddy_block *block, *bn;
-       struct i915_buddy_mm mm;
-       unsigned int order;
-       LIST_HEAD(blocks);
-       int err;
-
-       /*
-        * Create a pot-sized mm, then allocate one of each possible
-        * order within. This should leave the mm with exactly one
-        * page left.
-        */
-
-       err = i915_buddy_init(&mm, PAGE_SIZE << max_order, PAGE_SIZE);
-       if (err) {
-               pr_err("buddy_init failed(%d)\n", err);
-               return err;
-       }
-       GEM_BUG_ON(mm.max_order != max_order);
-
-       for (order = 0; order < max_order; order++) {
-               block = i915_buddy_alloc(&mm, order);
-               if (IS_ERR(block)) {
-                       pr_info("buddy_alloc hit -ENOMEM with order=%d\n",
-                               order);
-                       err = PTR_ERR(block);
-                       goto err;
-               }
-
-               list_add_tail(&block->link, &blocks);
-       }
-
-       /* And now the last remaining block available */
-       block = i915_buddy_alloc(&mm, 0);
-       if (IS_ERR(block)) {
-               pr_info("buddy_alloc hit -ENOMEM on final alloc\n");
-               err = PTR_ERR(block);
-               goto err;
-       }
-       list_add_tail(&block->link, &blocks);
-
-       /* Should be completely full! */
-       for (order = max_order; order--; ) {
-               block = i915_buddy_alloc(&mm, order);
-               if (!IS_ERR(block)) {
-                       pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!",
-                               order);
-                       list_add_tail(&block->link, &blocks);
-                       err = -EINVAL;
-                       goto err;
-               }
-       }
-
-       block = list_last_entry(&blocks, typeof(*block), link);
-       list_del(&block->link);
-       i915_buddy_free(&mm, block);
-
-       /* As we free in increasing size, we make available larger blocks */
-       order = 1;
-       list_for_each_entry_safe(block, bn, &blocks, link) {
-               list_del(&block->link);
-               i915_buddy_free(&mm, block);
-
-               block = i915_buddy_alloc(&mm, order);
-               if (IS_ERR(block)) {
-                       pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n",
-                               order);
-                       err = PTR_ERR(block);
-                       goto err;
-               }
-               i915_buddy_free(&mm, block);
-               order++;
-       }
-
-       /* To confirm, now the whole mm should be available */
-       block = i915_buddy_alloc(&mm, max_order);
-       if (IS_ERR(block)) {
-               pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n",
-                       max_order);
-               err = PTR_ERR(block);
-               goto err;
-       }
-       i915_buddy_free(&mm, block);
-
-err:
-       i915_buddy_free_list(&mm, &blocks);
-       i915_buddy_fini(&mm);
-       return err;
-}
-
-static int igt_buddy_alloc_optimistic(void *arg)
-{
-       const int max_order = 16;
-       struct i915_buddy_block *block;
-       struct i915_buddy_mm mm;
-       LIST_HEAD(blocks);
-       int order;
-       int err;
-
-       /*
-        * Create a mm with one block of each order available, and
-        * try to allocate them all.
-        */
-
-       err = i915_buddy_init(&mm,
-                             PAGE_SIZE * ((1 << (max_order + 1)) - 1),
-                             PAGE_SIZE);
-       if (err) {
-               pr_err("buddy_init failed(%d)\n", err);
-               return err;
-       }
-       GEM_BUG_ON(mm.max_order != max_order);
-
-       for (order = 0; order <= max_order; order++) {
-               block = i915_buddy_alloc(&mm, order);
-               if (IS_ERR(block)) {
-                       pr_info("buddy_alloc hit -ENOMEM with order=%d\n",
-                               order);
-                       err = PTR_ERR(block);
-                       goto err;
-               }
-
-               list_add_tail(&block->link, &blocks);
-       }
-
-       /* Should be completely full! */
-       block = i915_buddy_alloc(&mm, 0);
-       if (!IS_ERR(block)) {
-               pr_info("buddy_alloc unexpectedly succeeded, it should be full!");
-               list_add_tail(&block->link, &blocks);
-               err = -EINVAL;
-               goto err;
-       }
-
-err:
-       i915_buddy_free_list(&mm, &blocks);
-       i915_buddy_fini(&mm);
-       return err;
-}
-
-static int igt_buddy_alloc_pathological(void *arg)
-{
-       const int max_order = 16;
-       struct i915_buddy_block *block;
-       struct i915_buddy_mm mm;
-       LIST_HEAD(blocks);
-       LIST_HEAD(holes);
-       int order, top;
-       int err;
-
-       /*
-        * Create a pot-sized mm, then allocate one of each possible
-        * order within. This should leave the mm with exactly one
-        * page left. Free the largest block, then whittle down again.
-        * Eventually we will have a fully 50% fragmented mm.
-        */
-
-       err = i915_buddy_init(&mm, PAGE_SIZE << max_order, PAGE_SIZE);
-       if (err) {
-               pr_err("buddy_init failed(%d)\n", err);
-               return err;
-       }
-       GEM_BUG_ON(mm.max_order != max_order);
-
-       for (top = max_order; top; top--) {
-               /* Make room by freeing the largest allocated block */
-               block = list_first_entry_or_null(&blocks, typeof(*block), link);
-               if (block) {
-                       list_del(&block->link);
-                       i915_buddy_free(&mm, block);
-               }
-
-               for (order = top; order--; ) {
-                       block = i915_buddy_alloc(&mm, order);
-                       if (IS_ERR(block)) {
-                               pr_info("buddy_alloc hit -ENOMEM with order=%d, top=%d\n",
-                                       order, top);
-                               err = PTR_ERR(block);
-                               goto err;
-                       }
-                       list_add_tail(&block->link, &blocks);
-               }
-
-               /* There should be one final page for this sub-allocation */
-               block = i915_buddy_alloc(&mm, 0);
-               if (IS_ERR(block)) {
-                       pr_info("buddy_alloc hit -ENOMEM for hole\n");
-                       err = PTR_ERR(block);
-                       goto err;
-               }
-               list_add_tail(&block->link, &holes);
-
-               block = i915_buddy_alloc(&mm, top);
-               if (!IS_ERR(block)) {
-                       pr_info("buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!",
-                               top, max_order);
-                       list_add_tail(&block->link, &blocks);
-                       err = -EINVAL;
-                       goto err;
-               }
-       }
-
-       i915_buddy_free_list(&mm, &holes);
-
-       /* Nothing larger than blocks of chunk_size now available */
-       for (order = 1; order <= max_order; order++) {
-               block = i915_buddy_alloc(&mm, order);
-               if (!IS_ERR(block)) {
-                       pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!",
-                               order);
-                       list_add_tail(&block->link, &blocks);
-                       err = -EINVAL;
-                       goto err;
-               }
-       }
-
-err:
-       list_splice_tail(&holes, &blocks);
-       i915_buddy_free_list(&mm, &blocks);
-       i915_buddy_fini(&mm);
-       return err;
-}
-
-static int igt_buddy_alloc_range(void *arg)
-{
-       struct i915_buddy_mm mm;
-       unsigned long page_num;
-       LIST_HEAD(blocks);
-       u64 chunk_size;
-       u64 offset;
-       u64 size;
-       u64 rem;
-       int err;
-
-       igt_mm_config(&size, &chunk_size);
-
-       pr_info("buddy_init with size=%llx, chunk_size=%llx\n", size, chunk_size);
-
-       err = i915_buddy_init(&mm, size, chunk_size);
-       if (err) {
-               pr_err("buddy_init failed(%d)\n", err);
-               return err;
-       }
-
-       err = igt_check_mm(&mm);
-       if (err) {
-               pr_err("pre-mm check failed, abort, abort, abort!\n");
-               goto err_fini;
-       }
-
-       rem = mm.size;
-       offset = 0;
-
-       for_each_prime_number_from(page_num, 1, ULONG_MAX - 1) {
-               struct i915_buddy_block *block;
-               LIST_HEAD(tmp);
-
-               size = min(page_num * mm.chunk_size, rem);
-
-               err = i915_buddy_alloc_range(&mm, &tmp, offset, size);
-               if (err) {
-                       if (err == -ENOMEM) {
-                               pr_info("alloc_range hit -ENOMEM with size=%llx\n",
-                                       size);
-                       } else {
-                               pr_err("alloc_range with offset=%llx, size=%llx failed(%d)\n",
-                                      offset, size, err);
-                       }
-
-                       break;
-               }
-
-               block = list_first_entry_or_null(&tmp,
-                                                struct i915_buddy_block,
-                                                link);
-               if (!block) {
-                       pr_err("alloc_range has no blocks\n");
-                       err = -EINVAL;
-                       break;
-               }
-
-               if (i915_buddy_block_offset(block) != offset) {
-                       pr_err("alloc_range start offset mismatch, found=%llx, expected=%llx\n",
-                              i915_buddy_block_offset(block), offset);
-                       err = -EINVAL;
-               }
-
-               if (!err)
-                       err = igt_check_blocks(&mm, &tmp, size, true);
-
-               list_splice_tail(&tmp, &blocks);
-
-               if (err)
-                       break;
-
-               offset += size;
-
-               rem -= size;
-               if (!rem)
-                       break;
-
-               cond_resched();
-       }
-
-       if (err == -ENOMEM)
-               err = 0;
-
-       i915_buddy_free_list(&mm, &blocks);
-
-       if (!err) {
-               err = igt_check_mm(&mm);
-               if (err)
-                       pr_err("post-mm check failed\n");
-       }
-
-err_fini:
-       i915_buddy_fini(&mm);
-
-       return err;
-}
-
-static int igt_buddy_alloc_limit(void *arg)
-{
-       struct i915_buddy_block *block;
-       struct i915_buddy_mm mm;
-       const u64 size = U64_MAX;
-       int err;
-
-       err = i915_buddy_init(&mm, size, PAGE_SIZE);
-       if (err)
-               return err;
-
-       if (mm.max_order != I915_BUDDY_MAX_ORDER) {
-               pr_err("mm.max_order(%d) != %d\n",
-                      mm.max_order, I915_BUDDY_MAX_ORDER);
-               err = -EINVAL;
-               goto out_fini;
-       }
-
-       block = i915_buddy_alloc(&mm, mm.max_order);
-       if (IS_ERR(block)) {
-               err = PTR_ERR(block);
-               goto out_fini;
-       }
-
-       if (i915_buddy_block_order(block) != mm.max_order) {
-               pr_err("block order(%d) != %d\n",
-                      i915_buddy_block_order(block), mm.max_order);
-               err = -EINVAL;
-               goto out_free;
-       }
-
-       if (i915_buddy_block_size(&mm, block) !=
-           BIT_ULL(mm.max_order) * PAGE_SIZE) {
-               pr_err("block size(%llu) != %llu\n",
-                      i915_buddy_block_size(&mm, block),
-                      BIT_ULL(mm.max_order) * PAGE_SIZE);
-               err = -EINVAL;
-               goto out_free;
-       }
-
-out_free:
-       i915_buddy_free(&mm, block);
-out_fini:
-       i915_buddy_fini(&mm);
-       return err;
-}
-
-int i915_buddy_mock_selftests(void)
-{
-       static const struct i915_subtest tests[] = {
-               SUBTEST(igt_buddy_alloc_pessimistic),
-               SUBTEST(igt_buddy_alloc_optimistic),
-               SUBTEST(igt_buddy_alloc_pathological),
-               SUBTEST(igt_buddy_alloc_smoke),
-               SUBTEST(igt_buddy_alloc_range),
-               SUBTEST(igt_buddy_alloc_limit),
-       };
-
-       return i915_subtests(tests, NULL);
-}
index 793fb28..0c22e0f 100644 (file)
@@ -33,4 +33,3 @@ selftest(evict, i915_gem_evict_mock_selftests)
 selftest(gtt, i915_gem_gtt_mock_selftests)
 selftest(hugepages, i915_gem_huge_page_mock_selftests)
 selftest(memory_region, intel_memory_region_mock_selftests)
-selftest(buddy, i915_buddy_mock_selftests)
index 8255561..7acba1d 100644 (file)
@@ -6,6 +6,8 @@
 #include <linux/prime_numbers.h>
 #include <linux/sort.h>
 
+#include <drm/drm_buddy.h>
+
 #include "../i915_selftest.h"
 
 #include "mock_drm.h"
@@ -20,7 +22,6 @@
 #include "gt/intel_engine_pm.h"
 #include "gt/intel_engine_user.h"
 #include "gt/intel_gt.h"
-#include "i915_buddy.h"
 #include "gt/intel_migrate.h"
 #include "i915_memcpy.h"
 #include "i915_ttm_buddy_manager.h"
@@ -369,7 +370,7 @@ static int igt_mock_splintered_region(void *arg)
        struct drm_i915_private *i915 = mem->i915;
        struct i915_ttm_buddy_resource *res;
        struct drm_i915_gem_object *obj;
-       struct i915_buddy_mm *mm;
+       struct drm_buddy *mm;
        unsigned int expected_order;
        LIST_HEAD(objects);
        u64 size;
@@ -454,8 +455,8 @@ static int igt_mock_max_segment(void *arg)
        struct drm_i915_private *i915 = mem->i915;
        struct i915_ttm_buddy_resource *res;
        struct drm_i915_gem_object *obj;
-       struct i915_buddy_block *block;
-       struct i915_buddy_mm *mm;
+       struct drm_buddy_block *block;
+       struct drm_buddy *mm;
        struct list_head *blocks;
        struct scatterlist *sg;
        LIST_HEAD(objects);
@@ -485,8 +486,8 @@ static int igt_mock_max_segment(void *arg)
        mm = res->mm;
        size = 0;
        list_for_each_entry(block, blocks, link) {
-               if (i915_buddy_block_size(mm, block) > size)
-                       size = i915_buddy_block_size(mm, block);
+               if (drm_buddy_block_size(mm, block) > size)
+                       size = drm_buddy_block_size(mm, block);
        }
        if (size < max_segment) {
                pr_err("%s: Failed to create a huge contiguous block [> %u], largest block %lld\n",
index 8dc2f85..24147ee 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
+#include <drm/drm_module.h>
 #include <drm/drm_of.h>
 
 #include "dcss-dev.h"
@@ -131,7 +132,7 @@ static struct platform_driver dcss_platform_driver = {
        },
 };
 
-module_platform_driver(dcss_platform_driver);
+drm_module_platform_driver(dcss_platform_driver);
 
 MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@nxp.com>");
 MODULE_DESCRIPTION("DCSS driver for i.MX8MQ");
index b4943a5..542c4af 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "ingenic-drm.h"
 
+#include <linux/bitfield.h>
 #include <linux/component.h>
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
@@ -49,6 +50,11 @@ struct ingenic_dma_hwdesc {
        u32 addr;
        u32 id;
        u32 cmd;
+       /* extended hw descriptor for jz4780 */
+       u32 offsize;
+       u32 pagewidth;
+       u32 cpos;
+       u32 dessize;
 } __aligned(16);
 
 struct ingenic_dma_hwdescs {
@@ -60,6 +66,7 @@ struct jz_soc_info {
        bool needs_dev_clk;
        bool has_osd;
        bool map_noncoherent;
+       bool use_extended_hwdesc;
        unsigned int max_width, max_height;
        const u32 *formats_f0, *formats_f1;
        unsigned int num_formats_f0, num_formats_f1;
@@ -173,7 +180,6 @@ static const struct regmap_config ingenic_drm_regmap_config = {
        .val_bits = 32,
        .reg_stride = 4,
 
-       .max_register = JZ_REG_LCD_SIZE1,
        .writeable_reg = ingenic_drm_writeable_reg,
 };
 
@@ -447,6 +453,9 @@ static int ingenic_drm_plane_atomic_check(struct drm_plane *plane,
        if (!crtc)
                return 0;
 
+       if (plane == &priv->f0)
+               return -EINVAL;
+
        crtc_state = drm_atomic_get_existing_crtc_state(state,
                                                        crtc);
        if (WARN_ON(!crtc_state))
@@ -663,6 +672,33 @@ static void ingenic_drm_plane_atomic_update(struct drm_plane *plane,
                hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4);
                hwdesc->next = dma_hwdesc_addr(priv, next_id);
 
+               if (priv->soc_info->use_extended_hwdesc) {
+                       hwdesc->cmd |= JZ_LCD_CMD_FRM_ENABLE;
+
+                       /* Extended 8-byte descriptor */
+                       hwdesc->cpos = 0;
+                       hwdesc->offsize = 0;
+                       hwdesc->pagewidth = 0;
+
+                       switch (newstate->fb->format->format) {
+                       case DRM_FORMAT_XRGB1555:
+                               hwdesc->cpos |= JZ_LCD_CPOS_RGB555;
+                               fallthrough;
+                       case DRM_FORMAT_RGB565:
+                               hwdesc->cpos |= JZ_LCD_CPOS_BPP_15_16;
+                               break;
+                       case DRM_FORMAT_XRGB8888:
+                               hwdesc->cpos |= JZ_LCD_CPOS_BPP_18_24;
+                               break;
+                       }
+                       hwdesc->cpos |= (JZ_LCD_CPOS_COEFFICIENT_1 <<
+                                        JZ_LCD_CPOS_COEFFICIENT_OFFSET);
+                       hwdesc->dessize =
+                               (0xff << JZ_LCD_DESSIZE_ALPHA_OFFSET) |
+                               FIELD_PREP(JZ_LCD_DESSIZE_HEIGHT_MASK, height - 1) |
+                               FIELD_PREP(JZ_LCD_DESSIZE_WIDTH_MASK, width - 1);
+               }
+
                if (drm_atomic_crtc_needs_modeset(crtc_state)) {
                        fourcc = newstate->fb->format->format;
 
@@ -694,6 +730,9 @@ static void ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder,
                    | JZ_LCD_CFG_SPL_DISABLE | JZ_LCD_CFG_REV_DISABLE;
        }
 
+       if (priv->soc_info->use_extended_hwdesc)
+               cfg |= JZ_LCD_CFG_DESCRIPTOR_8;
+
        if (mode->flags & DRM_MODE_FLAG_NHSYNC)
                cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW;
        if (mode->flags & DRM_MODE_FLAG_NVSYNC)
@@ -1011,6 +1050,8 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
        struct ingenic_drm_bridge *ib;
        struct drm_device *drm;
        void __iomem *base;
+       struct resource *res;
+       struct regmap_config regmap_config;
        long parent_rate;
        unsigned int i, clone_mask = 0;
        int ret, irq;
@@ -1056,14 +1097,16 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
        drm->mode_config.funcs = &ingenic_drm_mode_config_funcs;
        drm->mode_config.helper_private = &ingenic_drm_mode_config_helpers;
 
-       base = devm_platform_ioremap_resource(pdev, 0);
+       base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(base)) {
                dev_err(dev, "Failed to get memory resource\n");
                return PTR_ERR(base);
        }
 
+       regmap_config = ingenic_drm_regmap_config;
+       regmap_config.max_register = res->end - res->start;
        priv->map = devm_regmap_init_mmio(dev, base,
-                                         &ingenic_drm_regmap_config);
+                                         &regmap_config);
        if (IS_ERR(priv->map)) {
                dev_err(dev, "Failed to create regmap\n");
                return PTR_ERR(priv->map);
@@ -1465,10 +1508,23 @@ static const struct jz_soc_info jz4770_soc_info = {
        .num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0),
 };
 
+static const struct jz_soc_info jz4780_soc_info = {
+       .needs_dev_clk = true,
+       .has_osd = true,
+       .use_extended_hwdesc = true,
+       .max_width = 4096,
+       .max_height = 2048,
+       .formats_f1 = jz4770_formats_f1,
+       .num_formats_f1 = ARRAY_SIZE(jz4770_formats_f1),
+       .formats_f0 = jz4770_formats_f0,
+       .num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0),
+};
+
 static const struct of_device_id ingenic_drm_of_match[] = {
        { .compatible = "ingenic,jz4740-lcd", .data = &jz4740_soc_info },
        { .compatible = "ingenic,jz4725b-lcd", .data = &jz4725b_soc_info },
        { .compatible = "ingenic,jz4770-lcd", .data = &jz4770_soc_info },
+       { .compatible = "ingenic,jz4780-lcd", .data = &jz4780_soc_info },
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, ingenic_drm_of_match);
index 22654ac..cb1d09b 100644 (file)
 #define JZ_REG_LCD_XYP1                                0x124
 #define JZ_REG_LCD_SIZE0                       0x128
 #define JZ_REG_LCD_SIZE1                       0x12c
+#define JZ_REG_LCD_PCFG                                0x2c0
 
 #define JZ_LCD_CFG_SLCD                                BIT(31)
+#define JZ_LCD_CFG_DESCRIPTOR_8                        BIT(28)
+#define JZ_LCD_CFG_RECOVER_FIFO_UNDERRUN       BIT(25)
 #define JZ_LCD_CFG_PS_DISABLE                  BIT(23)
 #define JZ_LCD_CFG_CLS_DISABLE                 BIT(22)
 #define JZ_LCD_CFG_SPL_DISABLE                 BIT(21)
@@ -63,6 +66,7 @@
 #define JZ_LCD_CFG_DE_ACTIVE_LOW               BIT(9)
 #define JZ_LCD_CFG_VSYNC_ACTIVE_LOW            BIT(8)
 #define JZ_LCD_CFG_18_BIT                      BIT(7)
+#define JZ_LCD_CFG_24_BIT                      BIT(6)
 #define JZ_LCD_CFG_PDW                         (BIT(5) | BIT(4))
 
 #define JZ_LCD_CFG_MODE_GENERIC_16BIT          0
 #define JZ_LCD_CMD_SOF_IRQ                     BIT(31)
 #define JZ_LCD_CMD_EOF_IRQ                     BIT(30)
 #define JZ_LCD_CMD_ENABLE_PAL                  BIT(28)
+#define JZ_LCD_CMD_FRM_ENABLE                  BIT(26)
 
 #define JZ_LCD_SYNC_MASK                       0x3ff
 
 #define JZ_LCD_RGBC_EVEN_BGR                   (0x5 << 0)
 
 #define JZ_LCD_OSDC_OSDEN                      BIT(0)
+#define JZ_LCD_OSDC_ALPHAEN                    BIT(2)
 #define JZ_LCD_OSDC_F0EN                       BIT(3)
 #define JZ_LCD_OSDC_F1EN                       BIT(4)
 
 #define JZ_LCD_SIZE01_WIDTH_LSB                        0
 #define JZ_LCD_SIZE01_HEIGHT_LSB               16
 
+#define JZ_LCD_DESSIZE_ALPHA_OFFSET            24
+#define JZ_LCD_DESSIZE_HEIGHT_MASK             GENMASK(23, 12)
+#define JZ_LCD_DESSIZE_WIDTH_MASK              GENMASK(11, 0)
+
+#define JZ_LCD_CPOS_BPP_15_16                  (4 << 27)
+#define JZ_LCD_CPOS_BPP_18_24                  (5 << 27)
+#define JZ_LCD_CPOS_BPP_30                     (7 << 27)
+#define JZ_LCD_CPOS_RGB555                     BIT(30)
+#define JZ_LCD_CPOS_PREMULTIPLY_LCD            BIT(26)
+#define JZ_LCD_CPOS_COEFFICIENT_OFFSET         24
+#define JZ_LCD_CPOS_COEFFICIENT_0              0
+#define JZ_LCD_CPOS_COEFFICIENT_1              1
+#define JZ_LCD_CPOS_COEFFICIENT_ALPHA1         2
+#define JZ_LCD_CPOS_COEFFICIENT_1_ALPHA1       3
+
+#define JZ_LCD_RGBC_RGB_PADDING                        BIT(15)
+#define JZ_LCD_RGBC_RGB_PADDING_FIRST          BIT(14)
+#define JZ_LCD_RGBC_422                                BIT(8)
+#define JZ_LCD_RGBC_RGB_FORMAT_ENABLE          BIT(7)
+
+#define JZ_LCD_PCFG_PRI_MODE                   BIT(31)
+#define JZ_LCD_PCFG_HP_BST_4                   (0 << 28)
+#define JZ_LCD_PCFG_HP_BST_8                   (1 << 28)
+#define JZ_LCD_PCFG_HP_BST_16                  (2 << 28)
+#define JZ_LCD_PCFG_HP_BST_32                  (3 << 28)
+#define JZ_LCD_PCFG_HP_BST_64                  (4 << 28)
+#define JZ_LCD_PCFG_HP_BST_16_CONT             (5 << 28)
+#define JZ_LCD_PCFG_HP_BST_DISABLE             (7 << 28)
+#define JZ_LCD_PCFG_THRESHOLD2_OFFSET          18
+#define JZ_LCD_PCFG_THRESHOLD1_OFFSET          9
+#define JZ_LCD_PCFG_THRESHOLD0_OFFSET          0
+
 struct device;
 struct drm_plane;
 struct drm_plane_state;
index 80f1d43..26aeaf0 100644 (file)
@@ -302,42 +302,42 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
        if (priv->afbcd.ops) {
                ret = priv->afbcd.ops->init(priv);
                if (ret)
-                       return ret;
+                       goto free_drm;
        }
 
        /* Encoder Initialization */
 
        ret = meson_encoder_cvbs_init(priv);
        if (ret)
-               goto free_drm;
+               goto exit_afbcd;
 
        if (has_components) {
                ret = component_bind_all(drm->dev, drm);
                if (ret) {
                        dev_err(drm->dev, "Couldn't bind all components\n");
-                       goto free_drm;
+                       goto exit_afbcd;
                }
        }
 
        ret = meson_encoder_hdmi_init(priv);
        if (ret)
-               goto free_drm;
+               goto exit_afbcd;
 
        ret = meson_plane_create(priv);
        if (ret)
-               goto free_drm;
+               goto exit_afbcd;
 
        ret = meson_overlay_create(priv);
        if (ret)
-               goto free_drm;
+               goto exit_afbcd;
 
        ret = meson_crtc_create(priv);
        if (ret)
-               goto free_drm;
+               goto exit_afbcd;
 
        ret = request_irq(priv->vsync_irq, meson_irq, 0, drm->driver->name, drm);
        if (ret)
-               goto free_drm;
+               goto exit_afbcd;
 
        drm_mode_config_reset(drm);
 
@@ -355,6 +355,9 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
 
 uninstall_irq:
        free_irq(priv->vsync_irq, drm);
+exit_afbcd:
+       if (priv->afbcd.ops)
+               priv->afbcd.ops->exit(priv);
 free_drm:
        drm_dev_put(drm);
 
@@ -385,10 +388,8 @@ static void meson_drv_unbind(struct device *dev)
        free_irq(priv->vsync_irq, drm);
        drm_dev_put(drm);
 
-       if (priv->afbcd.ops) {
-               priv->afbcd.ops->reset(priv);
-               meson_rdma_free(priv);
-       }
+       if (priv->afbcd.ops)
+               priv->afbcd.ops->exit(priv);
 }
 
 static const struct component_master_ops meson_drv_master_ops = {
index ffc6b58..0cdbe89 100644 (file)
@@ -79,11 +79,6 @@ static bool meson_gxm_afbcd_supported_fmt(u64 modifier, uint32_t format)
        return meson_gxm_afbcd_pixel_fmt(modifier, format) >= 0;
 }
 
-static int meson_gxm_afbcd_init(struct meson_drm *priv)
-{
-       return 0;
-}
-
 static int meson_gxm_afbcd_reset(struct meson_drm *priv)
 {
        writel_relaxed(VIU_SW_RESET_OSD1_AFBCD,
@@ -93,6 +88,16 @@ static int meson_gxm_afbcd_reset(struct meson_drm *priv)
        return 0;
 }
 
+static int meson_gxm_afbcd_init(struct meson_drm *priv)
+{
+       return 0;
+}
+
+static void meson_gxm_afbcd_exit(struct meson_drm *priv)
+{
+       meson_gxm_afbcd_reset(priv);
+}
+
 static int meson_gxm_afbcd_enable(struct meson_drm *priv)
 {
        writel_relaxed(FIELD_PREP(OSD1_AFBCD_ID_FIFO_THRD, 0x40) |
@@ -172,6 +177,7 @@ static int meson_gxm_afbcd_setup(struct meson_drm *priv)
 
 struct meson_afbcd_ops meson_afbcd_gxm_ops = {
        .init = meson_gxm_afbcd_init,
+       .exit = meson_gxm_afbcd_exit,
        .reset = meson_gxm_afbcd_reset,
        .enable = meson_gxm_afbcd_enable,
        .disable = meson_gxm_afbcd_disable,
@@ -269,6 +275,18 @@ static bool meson_g12a_afbcd_supported_fmt(u64 modifier, uint32_t format)
        return meson_g12a_afbcd_pixel_fmt(modifier, format) >= 0;
 }
 
+static int meson_g12a_afbcd_reset(struct meson_drm *priv)
+{
+       meson_rdma_reset(priv);
+
+       meson_rdma_writel_sync(priv, VIU_SW_RESET_G12A_AFBC_ARB |
+                              VIU_SW_RESET_G12A_OSD1_AFBCD,
+                              VIU_SW_RESET);
+       meson_rdma_writel_sync(priv, 0, VIU_SW_RESET);
+
+       return 0;
+}
+
 static int meson_g12a_afbcd_init(struct meson_drm *priv)
 {
        int ret;
@@ -286,16 +304,10 @@ static int meson_g12a_afbcd_init(struct meson_drm *priv)
        return 0;
 }
 
-static int meson_g12a_afbcd_reset(struct meson_drm *priv)
+static void meson_g12a_afbcd_exit(struct meson_drm *priv)
 {
-       meson_rdma_reset(priv);
-
-       meson_rdma_writel_sync(priv, VIU_SW_RESET_G12A_AFBC_ARB |
-                              VIU_SW_RESET_G12A_OSD1_AFBCD,
-                              VIU_SW_RESET);
-       meson_rdma_writel_sync(priv, 0, VIU_SW_RESET);
-
-       return 0;
+       meson_g12a_afbcd_reset(priv);
+       meson_rdma_free(priv);
 }
 
 static int meson_g12a_afbcd_enable(struct meson_drm *priv)
@@ -380,6 +392,7 @@ static int meson_g12a_afbcd_setup(struct meson_drm *priv)
 
 struct meson_afbcd_ops meson_afbcd_g12a_ops = {
        .init = meson_g12a_afbcd_init,
+       .exit = meson_g12a_afbcd_exit,
        .reset = meson_g12a_afbcd_reset,
        .enable = meson_g12a_afbcd_enable,
        .disable = meson_g12a_afbcd_disable,
index 5e55233..e77ddeb 100644 (file)
@@ -14,6 +14,7 @@
 
 struct meson_afbcd_ops {
        int (*init)(struct meson_drm *priv);
+       void (*exit)(struct meson_drm *priv);
        int (*reset)(struct meson_drm *priv);
        int (*enable)(struct meson_drm *priv);
        int (*disable)(struct meson_drm *priv);
index b983541..cd9ba13 100644 (file)
@@ -529,7 +529,10 @@ static void mgag200_set_format_regs(struct mga_device *mdev,
        WREG_GFX(3, 0x00);
        WREG_GFX(4, 0x00);
        WREG_GFX(5, 0x40);
-       WREG_GFX(6, 0x05);
+       /* GCTL6 should be 0x05, but we configure memmapsl to 0xb8000 (text mode),
+        * so that it doesn't hang when running kexec/kdump on G200_SE rev42.
+        */
+       WREG_GFX(6, 0x0d);
        WREG_GFX(7, 0x0f);
        WREG_GFX(8, 0x0f);
 
index 1eae5a9..4fd9314 100644 (file)
@@ -12,6 +12,7 @@ config DRM_MSM
        select IOMMU_IO_PGTABLE
        select QCOM_MDT_LOADER if ARCH_QCOM
        select REGULATOR
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select DRM_PANEL
        select DRM_BRIDGE
index d7e4a39..4553f49 100644 (file)
@@ -8,7 +8,7 @@
 
 #include <linux/of_platform.h>
 
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_edid.h>
 
 #include "dp_catalog.h"
index 0728cc0..82afc8d 100644 (file)
@@ -7,7 +7,7 @@
 #define _DP_AUX_H_
 
 #include "dp_catalog.h"
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 int dp_aux_register(struct drm_dp_aux *dp_aux);
 void dp_aux_unregister(struct drm_dp_aux *dp_aux);
index 6ae9b29..8a6d3ea 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/phy/phy.h>
 #include <linux/phy/phy-dp.h>
 #include <linux/rational.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_print.h>
 
 #include "dp_catalog.h"
index c724cb0..88ca6c3 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/phy/phy-dp.h>
 #include <linux/pm_opp.h>
 #include <drm/drm_fixed.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_print.h>
 
 #include "dp_reg.h"
diff --git a/drivers/gpu/drm/msm/edp/edp.h b/drivers/gpu/drm/msm/edp/edp.h
new file mode 100644 (file)
index 0000000..1a82d7a
--- /dev/null
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __EDP_CONNECTOR_H__
+#define __EDP_CONNECTOR_H__
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <drm/dp/drm_dp_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+
+#include "msm_drv.h"
+
+#define edp_read(offset) msm_readl((offset))
+#define edp_write(offset, data) msm_writel((data), (offset))
+
+struct edp_ctrl;
+struct edp_aux;
+struct edp_phy;
+
+struct msm_edp {
+       struct drm_device *dev;
+       struct platform_device *pdev;
+
+       struct drm_connector *connector;
+       struct drm_bridge *bridge;
+
+       /* the encoder we are hooked to (outside of eDP block) */
+       struct drm_encoder *encoder;
+
+       struct edp_ctrl *ctrl;
+
+       int irq;
+};
+
+/* eDP bridge */
+struct drm_bridge *msm_edp_bridge_init(struct msm_edp *edp);
+void edp_bridge_destroy(struct drm_bridge *bridge);
+
+/* eDP connector */
+struct drm_connector *msm_edp_connector_init(struct msm_edp *edp);
+
+/* AUX */
+void *msm_edp_aux_init(struct msm_edp *edp, void __iomem *regbase, struct drm_dp_aux **drm_aux);
+void msm_edp_aux_destroy(struct device *dev, struct edp_aux *aux);
+irqreturn_t msm_edp_aux_irq(struct edp_aux *aux, u32 isr);
+void msm_edp_aux_ctrl(struct edp_aux *aux, int enable);
+
+/* Phy */
+bool msm_edp_phy_ready(struct edp_phy *phy);
+void msm_edp_phy_ctrl(struct edp_phy *phy, int enable);
+void msm_edp_phy_vm_pe_init(struct edp_phy *phy);
+void msm_edp_phy_vm_pe_cfg(struct edp_phy *phy, u32 v0, u32 v1);
+void msm_edp_phy_lane_power_ctrl(struct edp_phy *phy, bool up, u32 max_lane);
+void *msm_edp_phy_init(struct device *dev, void __iomem *regbase);
+
+/* Ctrl */
+irqreturn_t msm_edp_ctrl_irq(struct edp_ctrl *ctrl);
+void msm_edp_ctrl_power(struct edp_ctrl *ctrl, bool on);
+int msm_edp_ctrl_init(struct msm_edp *edp);
+void msm_edp_ctrl_destroy(struct edp_ctrl *ctrl);
+bool msm_edp_ctrl_panel_connected(struct edp_ctrl *ctrl);
+int msm_edp_ctrl_get_panel_info(struct edp_ctrl *ctrl,
+       struct drm_connector *connector, struct edid **edid);
+int msm_edp_ctrl_timing_cfg(struct edp_ctrl *ctrl,
+                               const struct drm_display_mode *mode,
+                               const struct drm_display_info *info);
+/* @pixel_rate is in kHz */
+bool msm_edp_ctrl_pixel_clock_valid(struct edp_ctrl *ctrl,
+       u32 pixel_rate, u32 *pm, u32 *pn);
+
+#endif /* __EDP_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/msm/edp/edp_ctrl.c b/drivers/gpu/drm/msm/edp/edp_ctrl.c
new file mode 100644 (file)
index 0000000..9f537b1
--- /dev/null
@@ -0,0 +1,1373 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <drm/dp/drm_dp_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+
+#include "edp.h"
+#include "edp.xml.h"
+
+#define VDDA_UA_ON_LOAD                100000  /* uA units */
+#define VDDA_UA_OFF_LOAD       100     /* uA units */
+
+#define DPCD_LINK_VOLTAGE_MAX          4
+#define DPCD_LINK_PRE_EMPHASIS_MAX     4
+
+#define EDP_LINK_BW_MAX                DP_LINK_BW_2_7
+
+/* Link training return value */
+#define EDP_TRAIN_FAIL         -1
+#define EDP_TRAIN_SUCCESS      0
+#define EDP_TRAIN_RECONFIG     1
+
+#define EDP_CLK_MASK_AHB               BIT(0)
+#define EDP_CLK_MASK_AUX               BIT(1)
+#define EDP_CLK_MASK_LINK              BIT(2)
+#define EDP_CLK_MASK_PIXEL             BIT(3)
+#define EDP_CLK_MASK_MDP_CORE          BIT(4)
+#define EDP_CLK_MASK_LINK_CHAN (EDP_CLK_MASK_LINK | EDP_CLK_MASK_PIXEL)
+#define EDP_CLK_MASK_AUX_CHAN  \
+       (EDP_CLK_MASK_AHB | EDP_CLK_MASK_AUX | EDP_CLK_MASK_MDP_CORE)
+#define EDP_CLK_MASK_ALL       (EDP_CLK_MASK_AUX_CHAN | EDP_CLK_MASK_LINK_CHAN)
+
+#define EDP_BACKLIGHT_MAX      255
+
+#define EDP_INTR_STATUS1       \
+       (EDP_INTERRUPT_REG_1_HPD | EDP_INTERRUPT_REG_1_AUX_I2C_DONE | \
+       EDP_INTERRUPT_REG_1_WRONG_ADDR | EDP_INTERRUPT_REG_1_TIMEOUT | \
+       EDP_INTERRUPT_REG_1_NACK_DEFER | EDP_INTERRUPT_REG_1_WRONG_DATA_CNT | \
+       EDP_INTERRUPT_REG_1_I2C_NACK | EDP_INTERRUPT_REG_1_I2C_DEFER | \
+       EDP_INTERRUPT_REG_1_PLL_UNLOCK | EDP_INTERRUPT_REG_1_AUX_ERROR)
+#define EDP_INTR_MASK1 (EDP_INTR_STATUS1 << 2)
+#define EDP_INTR_STATUS2       \
+       (EDP_INTERRUPT_REG_2_READY_FOR_VIDEO | \
+       EDP_INTERRUPT_REG_2_IDLE_PATTERNs_SENT | \
+       EDP_INTERRUPT_REG_2_FRAME_END | EDP_INTERRUPT_REG_2_CRC_UPDATED)
+#define EDP_INTR_MASK2 (EDP_INTR_STATUS2 << 2)
+
+struct edp_ctrl {
+       struct platform_device *pdev;
+
+       void __iomem *base;
+
+       /* regulators */
+       struct regulator *vdda_vreg;    /* 1.8 V */
+       struct regulator *lvl_vreg;
+
+       /* clocks */
+       struct clk *aux_clk;
+       struct clk *pixel_clk;
+       struct clk *ahb_clk;
+       struct clk *link_clk;
+       struct clk *mdp_core_clk;
+
+       /* gpios */
+       struct gpio_desc *panel_en_gpio;
+       struct gpio_desc *panel_hpd_gpio;
+
+       /* completion and mutex */
+       struct completion idle_comp;
+       struct mutex dev_mutex; /* To protect device power status */
+
+       /* work queue */
+       struct work_struct on_work;
+       struct work_struct off_work;
+       struct workqueue_struct *workqueue;
+
+       /* Interrupt register lock */
+       spinlock_t irq_lock;
+
+       bool edp_connected;
+       bool power_on;
+
+       /* edid raw data */
+       struct edid *edid;
+
+       struct drm_dp_aux *drm_aux;
+
+       /* dpcd raw data */
+       u8 dpcd[DP_RECEIVER_CAP_SIZE];
+
+       /* Link status */
+       u8 link_rate;
+       u8 lane_cnt;
+       u8 v_level;
+       u8 p_level;
+
+       /* Timing status */
+       u8 interlaced;
+       u32 pixel_rate; /* in kHz */
+       u32 color_depth;
+
+       struct edp_aux *aux;
+       struct edp_phy *phy;
+};
+
+struct edp_pixel_clk_div {
+       u32 rate; /* in kHz */
+       u32 m;
+       u32 n;
+};
+
+#define EDP_PIXEL_CLK_NUM 8
+static const struct edp_pixel_clk_div clk_divs[2][EDP_PIXEL_CLK_NUM] = {
+       { /* Link clock = 162MHz, source clock = 810MHz */
+               {119000, 31,  211}, /* WSXGA+ 1680x1050@60Hz CVT */
+               {130250, 32,  199}, /* UXGA 1600x1200@60Hz CVT */
+               {148500, 11,  60},  /* FHD 1920x1080@60Hz */
+               {154000, 50,  263}, /* WUXGA 1920x1200@60Hz CVT */
+               {209250, 31,  120}, /* QXGA 2048x1536@60Hz CVT */
+               {268500, 119, 359}, /* WQXGA 2560x1600@60Hz CVT */
+               {138530, 33,  193}, /* AUO B116HAN03.0 Panel */
+               {141400, 48,  275}, /* AUO B133HTN01.2 Panel */
+       },
+       { /* Link clock = 270MHz, source clock = 675MHz */
+               {119000, 52,  295}, /* WSXGA+ 1680x1050@60Hz CVT */
+               {130250, 11,  57},  /* UXGA 1600x1200@60Hz CVT */
+               {148500, 11,  50},  /* FHD 1920x1080@60Hz */
+               {154000, 47,  206}, /* WUXGA 1920x1200@60Hz CVT */
+               {209250, 31,  100}, /* QXGA 2048x1536@60Hz CVT */
+               {268500, 107, 269}, /* WQXGA 2560x1600@60Hz CVT */
+               {138530, 63,  307}, /* AUO B116HAN03.0 Panel */
+               {141400, 53,  253}, /* AUO B133HTN01.2 Panel */
+       },
+};
+
+static int edp_clk_init(struct edp_ctrl *ctrl)
+{
+       struct platform_device *pdev = ctrl->pdev;
+       int ret;
+
+       ctrl->aux_clk = msm_clk_get(pdev, "core");
+       if (IS_ERR(ctrl->aux_clk)) {
+               ret = PTR_ERR(ctrl->aux_clk);
+               pr_err("%s: Can't find core clock, %d\n", __func__, ret);
+               ctrl->aux_clk = NULL;
+               return ret;
+       }
+
+       ctrl->pixel_clk = msm_clk_get(pdev, "pixel");
+       if (IS_ERR(ctrl->pixel_clk)) {
+               ret = PTR_ERR(ctrl->pixel_clk);
+               pr_err("%s: Can't find pixel clock, %d\n", __func__, ret);
+               ctrl->pixel_clk = NULL;
+               return ret;
+       }
+
+       ctrl->ahb_clk = msm_clk_get(pdev, "iface");
+       if (IS_ERR(ctrl->ahb_clk)) {
+               ret = PTR_ERR(ctrl->ahb_clk);
+               pr_err("%s: Can't find iface clock, %d\n", __func__, ret);
+               ctrl->ahb_clk = NULL;
+               return ret;
+       }
+
+       ctrl->link_clk = msm_clk_get(pdev, "link");
+       if (IS_ERR(ctrl->link_clk)) {
+               ret = PTR_ERR(ctrl->link_clk);
+               pr_err("%s: Can't find link clock, %d\n", __func__, ret);
+               ctrl->link_clk = NULL;
+               return ret;
+       }
+
+       /* need mdp core clock to receive irq */
+       ctrl->mdp_core_clk = msm_clk_get(pdev, "mdp_core");
+       if (IS_ERR(ctrl->mdp_core_clk)) {
+               ret = PTR_ERR(ctrl->mdp_core_clk);
+               pr_err("%s: Can't find mdp_core clock, %d\n", __func__, ret);
+               ctrl->mdp_core_clk = NULL;
+               return ret;
+       }
+
+       return 0;
+}
+
+static int edp_clk_enable(struct edp_ctrl *ctrl, u32 clk_mask)
+{
+       int ret;
+
+       DBG("mask=%x", clk_mask);
+       /* ahb_clk should be enabled first */
+       if (clk_mask & EDP_CLK_MASK_AHB) {
+               ret = clk_prepare_enable(ctrl->ahb_clk);
+               if (ret) {
+                       pr_err("%s: Failed to enable ahb clk\n", __func__);
+                       goto f0;
+               }
+       }
+       if (clk_mask & EDP_CLK_MASK_AUX) {
+               ret = clk_set_rate(ctrl->aux_clk, 19200000);
+               if (ret) {
+                       pr_err("%s: Failed to set rate aux clk\n", __func__);
+                       goto f1;
+               }
+               ret = clk_prepare_enable(ctrl->aux_clk);
+               if (ret) {
+                       pr_err("%s: Failed to enable aux clk\n", __func__);
+                       goto f1;
+               }
+       }
+       /* Need to set rate and enable link_clk prior to pixel_clk */
+       if (clk_mask & EDP_CLK_MASK_LINK) {
+               DBG("edp->link_clk, set_rate %ld",
+                               (unsigned long)ctrl->link_rate * 27000000);
+               ret = clk_set_rate(ctrl->link_clk,
+                               (unsigned long)ctrl->link_rate * 27000000);
+               if (ret) {
+                       pr_err("%s: Failed to set rate to link clk\n",
+                               __func__);
+                       goto f2;
+               }
+
+               ret = clk_prepare_enable(ctrl->link_clk);
+               if (ret) {
+                       pr_err("%s: Failed to enable link clk\n", __func__);
+                       goto f2;
+               }
+       }
+       if (clk_mask & EDP_CLK_MASK_PIXEL) {
+               DBG("edp->pixel_clk, set_rate %ld",
+                               (unsigned long)ctrl->pixel_rate * 1000);
+               ret = clk_set_rate(ctrl->pixel_clk,
+                               (unsigned long)ctrl->pixel_rate * 1000);
+               if (ret) {
+                       pr_err("%s: Failed to set rate to pixel clk\n",
+                               __func__);
+                       goto f3;
+               }
+
+               ret = clk_prepare_enable(ctrl->pixel_clk);
+               if (ret) {
+                       pr_err("%s: Failed to enable pixel clk\n", __func__);
+                       goto f3;
+               }
+       }
+       if (clk_mask & EDP_CLK_MASK_MDP_CORE) {
+               ret = clk_prepare_enable(ctrl->mdp_core_clk);
+               if (ret) {
+                       pr_err("%s: Failed to enable mdp core clk\n", __func__);
+                       goto f4;
+               }
+       }
+
+       return 0;
+
+f4:
+       if (clk_mask & EDP_CLK_MASK_PIXEL)
+               clk_disable_unprepare(ctrl->pixel_clk);
+f3:
+       if (clk_mask & EDP_CLK_MASK_LINK)
+               clk_disable_unprepare(ctrl->link_clk);
+f2:
+       if (clk_mask & EDP_CLK_MASK_AUX)
+               clk_disable_unprepare(ctrl->aux_clk);
+f1:
+       if (clk_mask & EDP_CLK_MASK_AHB)
+               clk_disable_unprepare(ctrl->ahb_clk);
+f0:
+       return ret;
+}
+
+static void edp_clk_disable(struct edp_ctrl *ctrl, u32 clk_mask)
+{
+       if (clk_mask & EDP_CLK_MASK_MDP_CORE)
+               clk_disable_unprepare(ctrl->mdp_core_clk);
+       if (clk_mask & EDP_CLK_MASK_PIXEL)
+               clk_disable_unprepare(ctrl->pixel_clk);
+       if (clk_mask & EDP_CLK_MASK_LINK)
+               clk_disable_unprepare(ctrl->link_clk);
+       if (clk_mask & EDP_CLK_MASK_AUX)
+               clk_disable_unprepare(ctrl->aux_clk);
+       if (clk_mask & EDP_CLK_MASK_AHB)
+               clk_disable_unprepare(ctrl->ahb_clk);
+}
+
+static int edp_regulator_init(struct edp_ctrl *ctrl)
+{
+       struct device *dev = &ctrl->pdev->dev;
+       int ret;
+
+       DBG("");
+       ctrl->vdda_vreg = devm_regulator_get(dev, "vdda");
+       ret = PTR_ERR_OR_ZERO(ctrl->vdda_vreg);
+       if (ret) {
+               pr_err("%s: Could not get vdda reg, ret = %d\n", __func__,
+                               ret);
+               ctrl->vdda_vreg = NULL;
+               return ret;
+       }
+       ctrl->lvl_vreg = devm_regulator_get(dev, "lvl-vdd");
+       ret = PTR_ERR_OR_ZERO(ctrl->lvl_vreg);
+       if (ret) {
+               pr_err("%s: Could not get lvl-vdd reg, ret = %d\n", __func__,
+                               ret);
+               ctrl->lvl_vreg = NULL;
+               return ret;
+       }
+
+       return 0;
+}
+
+static int edp_regulator_enable(struct edp_ctrl *ctrl)
+{
+       int ret;
+
+       ret = regulator_set_load(ctrl->vdda_vreg, VDDA_UA_ON_LOAD);
+       if (ret < 0) {
+               pr_err("%s: vdda_vreg set regulator mode failed.\n", __func__);
+               goto vdda_set_fail;
+       }
+
+       ret = regulator_enable(ctrl->vdda_vreg);
+       if (ret) {
+               pr_err("%s: Failed to enable vdda_vreg regulator.\n", __func__);
+               goto vdda_enable_fail;
+       }
+
+       ret = regulator_enable(ctrl->lvl_vreg);
+       if (ret) {
+               pr_err("Failed to enable lvl-vdd reg regulator, %d", ret);
+               goto lvl_enable_fail;
+       }
+
+       DBG("exit");
+       return 0;
+
+lvl_enable_fail:
+       regulator_disable(ctrl->vdda_vreg);
+vdda_enable_fail:
+       regulator_set_load(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD);
+vdda_set_fail:
+       return ret;
+}
+
+static void edp_regulator_disable(struct edp_ctrl *ctrl)
+{
+       regulator_disable(ctrl->lvl_vreg);
+       regulator_disable(ctrl->vdda_vreg);
+       regulator_set_load(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD);
+}
+
+static int edp_gpio_config(struct edp_ctrl *ctrl)
+{
+       struct device *dev = &ctrl->pdev->dev;
+       int ret;
+
+       ctrl->panel_hpd_gpio = devm_gpiod_get(dev, "panel-hpd", GPIOD_IN);
+       if (IS_ERR(ctrl->panel_hpd_gpio)) {
+               ret = PTR_ERR(ctrl->panel_hpd_gpio);
+               ctrl->panel_hpd_gpio = NULL;
+               pr_err("%s: cannot get panel-hpd-gpios, %d\n", __func__, ret);
+               return ret;
+       }
+
+       ctrl->panel_en_gpio = devm_gpiod_get(dev, "panel-en", GPIOD_OUT_LOW);
+       if (IS_ERR(ctrl->panel_en_gpio)) {
+               ret = PTR_ERR(ctrl->panel_en_gpio);
+               ctrl->panel_en_gpio = NULL;
+               pr_err("%s: cannot get panel-en-gpios, %d\n", __func__, ret);
+               return ret;
+       }
+
+       DBG("gpio on");
+
+       return 0;
+}
+
+static void edp_ctrl_irq_enable(struct edp_ctrl *ctrl, int enable)
+{
+       unsigned long flags;
+
+       DBG("%d", enable);
+       spin_lock_irqsave(&ctrl->irq_lock, flags);
+       if (enable) {
+               edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_1, EDP_INTR_MASK1);
+               edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_2, EDP_INTR_MASK2);
+       } else {
+               edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_1, 0x0);
+               edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_2, 0x0);
+       }
+       spin_unlock_irqrestore(&ctrl->irq_lock, flags);
+       DBG("exit");
+}
+
+static void edp_fill_link_cfg(struct edp_ctrl *ctrl)
+{
+       u32 prate;
+       u32 lrate;
+       u32 bpp;
+       u8 max_lane = drm_dp_max_lane_count(ctrl->dpcd);
+       u8 lane;
+
+       prate = ctrl->pixel_rate;
+       bpp = ctrl->color_depth * 3;
+
+       /*
+        * By default, use the maximum link rate and minimum lane count,
+        * so that we can do rate down shift during link training.
+        */
+       ctrl->link_rate = ctrl->dpcd[DP_MAX_LINK_RATE];
+
+       prate *= bpp;
+       prate /= 8; /* in kByte */
+
+       lrate = 270000; /* in kHz */
+       lrate *= ctrl->link_rate;
+       lrate /= 10; /* in kByte, 10 bits --> 8 bits */
+
+       for (lane = 1; lane <= max_lane; lane <<= 1) {
+               if (lrate >= prate)
+                       break;
+               lrate <<= 1;
+       }
+
+       ctrl->lane_cnt = lane;
+       DBG("rate=%d lane=%d", ctrl->link_rate, ctrl->lane_cnt);
+}
+
+static void edp_config_ctrl(struct edp_ctrl *ctrl)
+{
+       u32 data;
+       enum edp_color_depth depth;
+
+       data = EDP_CONFIGURATION_CTRL_LANES(ctrl->lane_cnt - 1);
+
+       if (drm_dp_enhanced_frame_cap(ctrl->dpcd))
+               data |= EDP_CONFIGURATION_CTRL_ENHANCED_FRAMING;
+
+       depth = EDP_6BIT;
+       if (ctrl->color_depth == 8)
+               depth = EDP_8BIT;
+
+       data |= EDP_CONFIGURATION_CTRL_COLOR(depth);
+
+       if (!ctrl->interlaced)  /* progressive */
+               data |= EDP_CONFIGURATION_CTRL_PROGRESSIVE;
+
+       data |= (EDP_CONFIGURATION_CTRL_SYNC_CLK |
+               EDP_CONFIGURATION_CTRL_STATIC_MVID);
+
+       edp_write(ctrl->base + REG_EDP_CONFIGURATION_CTRL, data);
+}
+
+static void edp_state_ctrl(struct edp_ctrl *ctrl, u32 state)
+{
+       edp_write(ctrl->base + REG_EDP_STATE_CTRL, state);
+       /* Make sure H/W status is set */
+       wmb();
+}
+
+static int edp_lane_set_write(struct edp_ctrl *ctrl,
+       u8 voltage_level, u8 pre_emphasis_level)
+{
+       int i;
+       u8 buf[4];
+
+       if (voltage_level >= DPCD_LINK_VOLTAGE_MAX)
+               voltage_level |= 0x04;
+
+       if (pre_emphasis_level >= DPCD_LINK_PRE_EMPHASIS_MAX)
+               pre_emphasis_level |= 0x04;
+
+       pre_emphasis_level <<= 3;
+
+       for (i = 0; i < 4; i++)
+               buf[i] = voltage_level | pre_emphasis_level;
+
+       DBG("%s: p|v=0x%x", __func__, voltage_level | pre_emphasis_level);
+       if (drm_dp_dpcd_write(ctrl->drm_aux, 0x103, buf, 4) < 4) {
+               pr_err("%s: Set sw/pe to panel failed\n", __func__);
+               return -ENOLINK;
+       }
+
+       return 0;
+}
+
+static int edp_train_pattern_set_write(struct edp_ctrl *ctrl, u8 pattern)
+{
+       u8 p = pattern;
+
+       DBG("pattern=%x", p);
+       if (drm_dp_dpcd_write(ctrl->drm_aux,
+                               DP_TRAINING_PATTERN_SET, &p, 1) < 1) {
+               pr_err("%s: Set training pattern to panel failed\n", __func__);
+               return -ENOLINK;
+       }
+
+       return 0;
+}
+
+static void edp_sink_train_set_adjust(struct edp_ctrl *ctrl,
+       const u8 *link_status)
+{
+       int i;
+       u8 max = 0;
+       u8 data;
+
+       /* use the max level across lanes */
+       for (i = 0; i < ctrl->lane_cnt; i++) {
+               data = drm_dp_get_adjust_request_voltage(link_status, i);
+               DBG("lane=%d req_voltage_swing=0x%x", i, data);
+               if (max < data)
+                       max = data;
+       }
+
+       ctrl->v_level = max >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
+
+       /* use the max level across lanes */
+       max = 0;
+       for (i = 0; i < ctrl->lane_cnt; i++) {
+               data = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
+               DBG("lane=%d req_pre_emphasis=0x%x", i, data);
+               if (max < data)
+                       max = data;
+       }
+
+       ctrl->p_level = max >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+       DBG("v_level=%d, p_level=%d", ctrl->v_level, ctrl->p_level);
+}
+
+static void edp_host_train_set(struct edp_ctrl *ctrl, u32 train)
+{
+       int cnt = 10;
+       u32 data;
+       u32 shift = train - 1;
+
+       DBG("train=%d", train);
+
+       edp_state_ctrl(ctrl, EDP_STATE_CTRL_TRAIN_PATTERN_1 << shift);
+       while (--cnt) {
+               data = edp_read(ctrl->base + REG_EDP_MAINLINK_READY);
+               if (data & (EDP_MAINLINK_READY_TRAIN_PATTERN_1_READY << shift))
+                       break;
+       }
+
+       if (cnt == 0)
+               pr_err("%s: set link_train=%d failed\n", __func__, train);
+}
+
+static const u8 vm_pre_emphasis[4][4] = {
+       {0x03, 0x06, 0x09, 0x0C},       /* pe0, 0 db */
+       {0x03, 0x06, 0x09, 0xFF},       /* pe1, 3.5 db */
+       {0x03, 0x06, 0xFF, 0xFF},       /* pe2, 6.0 db */
+       {0x03, 0xFF, 0xFF, 0xFF}        /* pe3, 9.5 db */
+};
+
+/* voltage swing, 0.2v and 1.0v are not support */
+static const u8 vm_voltage_swing[4][4] = {
+       {0x14, 0x18, 0x1A, 0x1E}, /* sw0, 0.4v  */
+       {0x18, 0x1A, 0x1E, 0xFF}, /* sw1, 0.6 v */
+       {0x1A, 0x1E, 0xFF, 0xFF}, /* sw1, 0.8 v */
+       {0x1E, 0xFF, 0xFF, 0xFF}  /* sw1, 1.2 v, optional */
+};
+
+static int edp_voltage_pre_emphasise_set(struct edp_ctrl *ctrl)
+{
+       u32 value0;
+       u32 value1;
+
+       DBG("v=%d p=%d", ctrl->v_level, ctrl->p_level);
+
+       value0 = vm_pre_emphasis[(int)(ctrl->v_level)][(int)(ctrl->p_level)];
+       value1 = vm_voltage_swing[(int)(ctrl->v_level)][(int)(ctrl->p_level)];
+
+       /* Configure host and panel only if both values are allowed */
+       if (value0 != 0xFF && value1 != 0xFF) {
+               msm_edp_phy_vm_pe_cfg(ctrl->phy, value0, value1);
+               return edp_lane_set_write(ctrl, ctrl->v_level, ctrl->p_level);
+       }
+
+       return -EINVAL;
+}
+
+static int edp_start_link_train_1(struct edp_ctrl *ctrl)
+{
+       u8 link_status[DP_LINK_STATUS_SIZE];
+       u8 old_v_level;
+       int tries;
+       int ret;
+       int rlen;
+
+       DBG("");
+
+       edp_host_train_set(ctrl, DP_TRAINING_PATTERN_1);
+       ret = edp_voltage_pre_emphasise_set(ctrl);
+       if (ret)
+               return ret;
+       ret = edp_train_pattern_set_write(ctrl,
+                       DP_TRAINING_PATTERN_1 | DP_RECOVERED_CLOCK_OUT_EN);
+       if (ret)
+               return ret;
+
+       tries = 0;
+       old_v_level = ctrl->v_level;
+       while (1) {
+               drm_dp_link_train_clock_recovery_delay(ctrl->drm_aux, ctrl->dpcd);
+
+               rlen = drm_dp_dpcd_read_link_status(ctrl->drm_aux, link_status);
+               if (rlen < DP_LINK_STATUS_SIZE) {
+                       pr_err("%s: read link status failed\n", __func__);
+                       return -ENOLINK;
+               }
+               if (drm_dp_clock_recovery_ok(link_status, ctrl->lane_cnt)) {
+                       ret = 0;
+                       break;
+               }
+
+               if (ctrl->v_level == DPCD_LINK_VOLTAGE_MAX) {
+                       ret = -1;
+                       break;
+               }
+
+               if (old_v_level == ctrl->v_level) {
+                       tries++;
+                       if (tries >= 5) {
+                               ret = -1;
+                               break;
+                       }
+               } else {
+                       tries = 0;
+                       old_v_level = ctrl->v_level;
+               }
+
+               edp_sink_train_set_adjust(ctrl, link_status);
+               ret = edp_voltage_pre_emphasise_set(ctrl);
+               if (ret)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static int edp_start_link_train_2(struct edp_ctrl *ctrl)
+{
+       u8 link_status[DP_LINK_STATUS_SIZE];
+       int tries = 0;
+       int ret;
+       int rlen;
+
+       DBG("");
+
+       edp_host_train_set(ctrl, DP_TRAINING_PATTERN_2);
+       ret = edp_voltage_pre_emphasise_set(ctrl);
+       if (ret)
+               return ret;
+
+       ret = edp_train_pattern_set_write(ctrl,
+                       DP_TRAINING_PATTERN_2 | DP_RECOVERED_CLOCK_OUT_EN);
+       if (ret)
+               return ret;
+
+       while (1) {
+               drm_dp_link_train_channel_eq_delay(ctrl->drm_aux, ctrl->dpcd);
+
+               rlen = drm_dp_dpcd_read_link_status(ctrl->drm_aux, link_status);
+               if (rlen < DP_LINK_STATUS_SIZE) {
+                       pr_err("%s: read link status failed\n", __func__);
+                       return -ENOLINK;
+               }
+               if (drm_dp_channel_eq_ok(link_status, ctrl->lane_cnt)) {
+                       ret = 0;
+                       break;
+               }
+
+               tries++;
+               if (tries > 10) {
+                       ret = -1;
+                       break;
+               }
+
+               edp_sink_train_set_adjust(ctrl, link_status);
+               ret = edp_voltage_pre_emphasise_set(ctrl);
+               if (ret)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static int edp_link_rate_down_shift(struct edp_ctrl *ctrl)
+{
+       u32 prate, lrate, bpp;
+       u8 rate, lane, max_lane;
+       int changed = 0;
+
+       rate = ctrl->link_rate;
+       lane = ctrl->lane_cnt;
+       max_lane = drm_dp_max_lane_count(ctrl->dpcd);
+
+       bpp = ctrl->color_depth * 3;
+       prate = ctrl->pixel_rate;
+       prate *= bpp;
+       prate /= 8; /* in kByte */
+
+       if (rate > DP_LINK_BW_1_62 && rate <= EDP_LINK_BW_MAX) {
+               rate -= 4;      /* reduce rate */
+               changed++;
+       }
+
+       if (changed) {
+               if (lane >= 1 && lane < max_lane)
+                       lane <<= 1;     /* increase lane */
+
+               lrate = 270000; /* in kHz */
+               lrate *= rate;
+               lrate /= 10; /* kByte, 10 bits --> 8 bits */
+               lrate *= lane;
+
+               DBG("new lrate=%u prate=%u(kHz) rate=%d lane=%d p=%u b=%d",
+                       lrate, prate, rate, lane,
+                       ctrl->pixel_rate,
+                       bpp);
+
+               if (lrate > prate) {
+                       ctrl->link_rate = rate;
+                       ctrl->lane_cnt = lane;
+                       DBG("new rate=%d %d", rate, lane);
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int edp_clear_training_pattern(struct edp_ctrl *ctrl)
+{
+       int ret;
+
+       ret = edp_train_pattern_set_write(ctrl, 0);
+
+       drm_dp_link_train_channel_eq_delay(ctrl->drm_aux, ctrl->dpcd);
+
+       return ret;
+}
+
+static int edp_do_link_train(struct edp_ctrl *ctrl)
+{
+       u8 values[2];
+       int ret;
+
+       DBG("");
+       /*
+        * Set the current link rate and lane cnt to panel. They may have been
+        * adjusted and the values are different from them in DPCD CAP
+        */
+       values[0] = ctrl->lane_cnt;
+       values[1] = ctrl->link_rate;
+
+       if (drm_dp_enhanced_frame_cap(ctrl->dpcd))
+               values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+       if (drm_dp_dpcd_write(ctrl->drm_aux, DP_LINK_BW_SET, values,
+                             sizeof(values)) < 0)
+               return EDP_TRAIN_FAIL;
+
+       ctrl->v_level = 0; /* start from default level */
+       ctrl->p_level = 0;
+
+       edp_state_ctrl(ctrl, 0);
+       if (edp_clear_training_pattern(ctrl))
+               return EDP_TRAIN_FAIL;
+
+       ret = edp_start_link_train_1(ctrl);
+       if (ret < 0) {
+               if (edp_link_rate_down_shift(ctrl) == 0) {
+                       DBG("link reconfig");
+                       ret = EDP_TRAIN_RECONFIG;
+                       goto clear;
+               } else {
+                       pr_err("%s: Training 1 failed", __func__);
+                       ret = EDP_TRAIN_FAIL;
+                       goto clear;
+               }
+       }
+       DBG("Training 1 completed successfully");
+
+       edp_state_ctrl(ctrl, 0);
+       if (edp_clear_training_pattern(ctrl))
+               return EDP_TRAIN_FAIL;
+
+       ret = edp_start_link_train_2(ctrl);
+       if (ret < 0) {
+               if (edp_link_rate_down_shift(ctrl) == 0) {
+                       DBG("link reconfig");
+                       ret = EDP_TRAIN_RECONFIG;
+                       goto clear;
+               } else {
+                       pr_err("%s: Training 2 failed", __func__);
+                       ret = EDP_TRAIN_FAIL;
+                       goto clear;
+               }
+       }
+       DBG("Training 2 completed successfully");
+
+       edp_state_ctrl(ctrl, EDP_STATE_CTRL_SEND_VIDEO);
+clear:
+       edp_clear_training_pattern(ctrl);
+
+       return ret;
+}
+
+static void edp_clock_synchrous(struct edp_ctrl *ctrl, int sync)
+{
+       u32 data;
+       enum edp_color_depth depth;
+
+       data = edp_read(ctrl->base + REG_EDP_MISC1_MISC0);
+
+       if (sync)
+               data |= EDP_MISC1_MISC0_SYNC;
+       else
+               data &= ~EDP_MISC1_MISC0_SYNC;
+
+       /* only legacy rgb mode supported */
+       depth = EDP_6BIT; /* Default */
+       if (ctrl->color_depth == 8)
+               depth = EDP_8BIT;
+       else if (ctrl->color_depth == 10)
+               depth = EDP_10BIT;
+       else if (ctrl->color_depth == 12)
+               depth = EDP_12BIT;
+       else if (ctrl->color_depth == 16)
+               depth = EDP_16BIT;
+
+       data |= EDP_MISC1_MISC0_COLOR(depth);
+
+       edp_write(ctrl->base + REG_EDP_MISC1_MISC0, data);
+}
+
+static int edp_sw_mvid_nvid(struct edp_ctrl *ctrl, u32 m, u32 n)
+{
+       u32 n_multi, m_multi = 5;
+
+       if (ctrl->link_rate == DP_LINK_BW_1_62) {
+               n_multi = 1;
+       } else if (ctrl->link_rate == DP_LINK_BW_2_7) {
+               n_multi = 2;
+       } else {
+               pr_err("%s: Invalid link rate, %d\n", __func__,
+                       ctrl->link_rate);
+               return -EINVAL;
+       }
+
+       edp_write(ctrl->base + REG_EDP_SOFTWARE_MVID, m * m_multi);
+       edp_write(ctrl->base + REG_EDP_SOFTWARE_NVID, n * n_multi);
+
+       return 0;
+}
+
+static void edp_mainlink_ctrl(struct edp_ctrl *ctrl, int enable)
+{
+       u32 data = 0;
+
+       edp_write(ctrl->base + REG_EDP_MAINLINK_CTRL, EDP_MAINLINK_CTRL_RESET);
+       /* Make sure fully reset */
+       wmb();
+       usleep_range(500, 1000);
+
+       if (enable)
+               data |= EDP_MAINLINK_CTRL_ENABLE;
+
+       edp_write(ctrl->base + REG_EDP_MAINLINK_CTRL, data);
+}
+
+static void edp_ctrl_phy_aux_enable(struct edp_ctrl *ctrl, int enable)
+{
+       if (enable) {
+               edp_regulator_enable(ctrl);
+               edp_clk_enable(ctrl, EDP_CLK_MASK_AUX_CHAN);
+               msm_edp_phy_ctrl(ctrl->phy, 1);
+               msm_edp_aux_ctrl(ctrl->aux, 1);
+               gpiod_set_value(ctrl->panel_en_gpio, 1);
+       } else {
+               gpiod_set_value(ctrl->panel_en_gpio, 0);
+               msm_edp_aux_ctrl(ctrl->aux, 0);
+               msm_edp_phy_ctrl(ctrl->phy, 0);
+               edp_clk_disable(ctrl, EDP_CLK_MASK_AUX_CHAN);
+               edp_regulator_disable(ctrl);
+       }
+}
+
+static void edp_ctrl_link_enable(struct edp_ctrl *ctrl, int enable)
+{
+       u32 m, n;
+
+       if (enable) {
+               /* Enable link channel clocks */
+               edp_clk_enable(ctrl, EDP_CLK_MASK_LINK_CHAN);
+
+               msm_edp_phy_lane_power_ctrl(ctrl->phy, true, ctrl->lane_cnt);
+
+               msm_edp_phy_vm_pe_init(ctrl->phy);
+
+               /* Make sure phy is programed */
+               wmb();
+               msm_edp_phy_ready(ctrl->phy);
+
+               edp_config_ctrl(ctrl);
+               msm_edp_ctrl_pixel_clock_valid(ctrl, ctrl->pixel_rate, &m, &n);
+               edp_sw_mvid_nvid(ctrl, m, n);
+               edp_mainlink_ctrl(ctrl, 1);
+       } else {
+               edp_mainlink_ctrl(ctrl, 0);
+
+               msm_edp_phy_lane_power_ctrl(ctrl->phy, false, 0);
+               edp_clk_disable(ctrl, EDP_CLK_MASK_LINK_CHAN);
+       }
+}
+
+static int edp_ctrl_training(struct edp_ctrl *ctrl)
+{
+       int ret;
+
+       /* Do link training only when power is on */
+       if (!ctrl->power_on)
+               return -EINVAL;
+
+train_start:
+       ret = edp_do_link_train(ctrl);
+       if (ret == EDP_TRAIN_RECONFIG) {
+               /* Re-configure main link */
+               edp_ctrl_irq_enable(ctrl, 0);
+               edp_ctrl_link_enable(ctrl, 0);
+               msm_edp_phy_ctrl(ctrl->phy, 0);
+
+               /* Make sure link is fully disabled */
+               wmb();
+               usleep_range(500, 1000);
+
+               msm_edp_phy_ctrl(ctrl->phy, 1);
+               edp_ctrl_link_enable(ctrl, 1);
+               edp_ctrl_irq_enable(ctrl, 1);
+               goto train_start;
+       }
+
+       return ret;
+}
+
+static void edp_ctrl_on_worker(struct work_struct *work)
+{
+       struct edp_ctrl *ctrl = container_of(
+                               work, struct edp_ctrl, on_work);
+       u8 value;
+       int ret;
+
+       mutex_lock(&ctrl->dev_mutex);
+
+       if (ctrl->power_on) {
+               DBG("already on");
+               goto unlock_ret;
+       }
+
+       edp_ctrl_phy_aux_enable(ctrl, 1);
+       edp_ctrl_link_enable(ctrl, 1);
+
+       edp_ctrl_irq_enable(ctrl, 1);
+
+       /* DP_SET_POWER register is only available on DPCD v1.1 and later */
+       if (ctrl->dpcd[DP_DPCD_REV] >= 0x11) {
+               ret = drm_dp_dpcd_readb(ctrl->drm_aux, DP_SET_POWER, &value);
+               if (ret < 0)
+                       goto fail;
+
+               value &= ~DP_SET_POWER_MASK;
+               value |= DP_SET_POWER_D0;
+
+               ret = drm_dp_dpcd_writeb(ctrl->drm_aux, DP_SET_POWER, value);
+               if (ret < 0)
+                       goto fail;
+
+               /*
+                * 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);
+       }
+
+       ctrl->power_on = true;
+
+       /* Start link training */
+       ret = edp_ctrl_training(ctrl);
+       if (ret != EDP_TRAIN_SUCCESS)
+               goto fail;
+
+       DBG("DONE");
+       goto unlock_ret;
+
+fail:
+       edp_ctrl_irq_enable(ctrl, 0);
+       edp_ctrl_link_enable(ctrl, 0);
+       edp_ctrl_phy_aux_enable(ctrl, 0);
+       ctrl->power_on = false;
+unlock_ret:
+       mutex_unlock(&ctrl->dev_mutex);
+}
+
+static void edp_ctrl_off_worker(struct work_struct *work)
+{
+       struct edp_ctrl *ctrl = container_of(
+                               work, struct edp_ctrl, off_work);
+       unsigned long time_left;
+
+       mutex_lock(&ctrl->dev_mutex);
+
+       if (!ctrl->power_on) {
+               DBG("already off");
+               goto unlock_ret;
+       }
+
+       reinit_completion(&ctrl->idle_comp);
+       edp_state_ctrl(ctrl, EDP_STATE_CTRL_PUSH_IDLE);
+
+       time_left = wait_for_completion_timeout(&ctrl->idle_comp,
+                                               msecs_to_jiffies(500));
+       if (!time_left)
+               DBG("%s: idle pattern timedout\n", __func__);
+
+       edp_state_ctrl(ctrl, 0);
+
+       /* DP_SET_POWER register is only available on DPCD v1.1 and later */
+       if (ctrl->dpcd[DP_DPCD_REV] >= 0x11) {
+               u8 value;
+               int ret;
+
+               ret = drm_dp_dpcd_readb(ctrl->drm_aux, DP_SET_POWER, &value);
+               if (ret > 0) {
+                       value &= ~DP_SET_POWER_MASK;
+                       value |= DP_SET_POWER_D3;
+
+                       drm_dp_dpcd_writeb(ctrl->drm_aux, DP_SET_POWER, value);
+               }
+       }
+
+       edp_ctrl_irq_enable(ctrl, 0);
+
+       edp_ctrl_link_enable(ctrl, 0);
+
+       edp_ctrl_phy_aux_enable(ctrl, 0);
+
+       ctrl->power_on = false;
+
+unlock_ret:
+       mutex_unlock(&ctrl->dev_mutex);
+}
+
+irqreturn_t msm_edp_ctrl_irq(struct edp_ctrl *ctrl)
+{
+       u32 isr1, isr2, mask1, mask2;
+       u32 ack;
+
+       DBG("");
+       spin_lock(&ctrl->irq_lock);
+       isr1 = edp_read(ctrl->base + REG_EDP_INTERRUPT_REG_1);
+       isr2 = edp_read(ctrl->base + REG_EDP_INTERRUPT_REG_2);
+
+       mask1 = isr1 & EDP_INTR_MASK1;
+       mask2 = isr2 & EDP_INTR_MASK2;
+
+       isr1 &= ~mask1; /* remove masks bit */
+       isr2 &= ~mask2;
+
+       DBG("isr=%x mask=%x isr2=%x mask2=%x",
+                       isr1, mask1, isr2, mask2);
+
+       ack = isr1 & EDP_INTR_STATUS1;
+       ack <<= 1;      /* ack bits */
+       ack |= mask1;
+       edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_1, ack);
+
+       ack = isr2 & EDP_INTR_STATUS2;
+       ack <<= 1;      /* ack bits */
+       ack |= mask2;
+       edp_write(ctrl->base + REG_EDP_INTERRUPT_REG_2, ack);
+       spin_unlock(&ctrl->irq_lock);
+
+       if (isr1 & EDP_INTERRUPT_REG_1_HPD)
+               DBG("edp_hpd");
+
+       if (isr2 & EDP_INTERRUPT_REG_2_READY_FOR_VIDEO)
+               DBG("edp_video_ready");
+
+       if (isr2 & EDP_INTERRUPT_REG_2_IDLE_PATTERNs_SENT) {
+               DBG("idle_patterns_sent");
+               complete(&ctrl->idle_comp);
+       }
+
+       msm_edp_aux_irq(ctrl->aux, isr1);
+
+       return IRQ_HANDLED;
+}
+
+void msm_edp_ctrl_power(struct edp_ctrl *ctrl, bool on)
+{
+       if (on)
+               queue_work(ctrl->workqueue, &ctrl->on_work);
+       else
+               queue_work(ctrl->workqueue, &ctrl->off_work);
+}
+
+int msm_edp_ctrl_init(struct msm_edp *edp)
+{
+       struct edp_ctrl *ctrl = NULL;
+       struct device *dev;
+       int ret;
+
+       if (!edp) {
+               pr_err("%s: edp is NULL!\n", __func__);
+               return -EINVAL;
+       }
+
+       dev = &edp->pdev->dev;
+       ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+       if (!ctrl)
+               return -ENOMEM;
+
+       edp->ctrl = ctrl;
+       ctrl->pdev = edp->pdev;
+
+       ctrl->base = msm_ioremap(ctrl->pdev, "edp", "eDP");
+       if (IS_ERR(ctrl->base))
+               return PTR_ERR(ctrl->base);
+
+       /* Get regulator, clock, gpio, pwm */
+       ret = edp_regulator_init(ctrl);
+       if (ret) {
+               pr_err("%s:regulator init fail\n", __func__);
+               return ret;
+       }
+       ret = edp_clk_init(ctrl);
+       if (ret) {
+               pr_err("%s:clk init fail\n", __func__);
+               return ret;
+       }
+       ret = edp_gpio_config(ctrl);
+       if (ret) {
+               pr_err("%s:failed to configure GPIOs: %d", __func__, ret);
+               return ret;
+       }
+
+       /* Init aux and phy */
+       ctrl->aux = msm_edp_aux_init(edp, ctrl->base, &ctrl->drm_aux);
+       if (!ctrl->aux || !ctrl->drm_aux) {
+               pr_err("%s:failed to init aux\n", __func__);
+               return -ENOMEM;
+       }
+
+       ctrl->phy = msm_edp_phy_init(dev, ctrl->base);
+       if (!ctrl->phy) {
+               pr_err("%s:failed to init phy\n", __func__);
+               ret = -ENOMEM;
+               goto err_destory_aux;
+       }
+
+       spin_lock_init(&ctrl->irq_lock);
+       mutex_init(&ctrl->dev_mutex);
+       init_completion(&ctrl->idle_comp);
+
+       /* setup workqueue */
+       ctrl->workqueue = alloc_ordered_workqueue("edp_drm_work", 0);
+       INIT_WORK(&ctrl->on_work, edp_ctrl_on_worker);
+       INIT_WORK(&ctrl->off_work, edp_ctrl_off_worker);
+
+       return 0;
+
+err_destory_aux:
+       msm_edp_aux_destroy(dev, ctrl->aux);
+       ctrl->aux = NULL;
+       return ret;
+}
+
+void msm_edp_ctrl_destroy(struct edp_ctrl *ctrl)
+{
+       if (!ctrl)
+               return;
+
+       if (ctrl->workqueue) {
+               destroy_workqueue(ctrl->workqueue);
+               ctrl->workqueue = NULL;
+       }
+
+       if (ctrl->aux) {
+               msm_edp_aux_destroy(&ctrl->pdev->dev, ctrl->aux);
+               ctrl->aux = NULL;
+       }
+
+       kfree(ctrl->edid);
+       ctrl->edid = NULL;
+
+       mutex_destroy(&ctrl->dev_mutex);
+}
+
+bool msm_edp_ctrl_panel_connected(struct edp_ctrl *ctrl)
+{
+       mutex_lock(&ctrl->dev_mutex);
+       DBG("connect status = %d", ctrl->edp_connected);
+       if (ctrl->edp_connected) {
+               mutex_unlock(&ctrl->dev_mutex);
+               return true;
+       }
+
+       if (!ctrl->power_on) {
+               edp_ctrl_phy_aux_enable(ctrl, 1);
+               edp_ctrl_irq_enable(ctrl, 1);
+       }
+
+       if (drm_dp_dpcd_read(ctrl->drm_aux, DP_DPCD_REV, ctrl->dpcd,
+                               DP_RECEIVER_CAP_SIZE) < DP_RECEIVER_CAP_SIZE) {
+               pr_err("%s: AUX channel is NOT ready\n", __func__);
+               memset(ctrl->dpcd, 0, DP_RECEIVER_CAP_SIZE);
+       } else {
+               ctrl->edp_connected = true;
+       }
+
+       if (!ctrl->power_on) {
+               edp_ctrl_irq_enable(ctrl, 0);
+               edp_ctrl_phy_aux_enable(ctrl, 0);
+       }
+
+       DBG("exit: connect status=%d", ctrl->edp_connected);
+
+       mutex_unlock(&ctrl->dev_mutex);
+
+       return ctrl->edp_connected;
+}
+
+int msm_edp_ctrl_get_panel_info(struct edp_ctrl *ctrl,
+               struct drm_connector *connector, struct edid **edid)
+{
+       mutex_lock(&ctrl->dev_mutex);
+
+       if (ctrl->edid) {
+               if (edid) {
+                       DBG("Just return edid buffer");
+                       *edid = ctrl->edid;
+               }
+               goto unlock_ret;
+       }
+
+       if (!ctrl->power_on) {
+               edp_ctrl_phy_aux_enable(ctrl, 1);
+               edp_ctrl_irq_enable(ctrl, 1);
+       }
+
+       /* Initialize link rate as panel max link rate */
+       ctrl->link_rate = ctrl->dpcd[DP_MAX_LINK_RATE];
+
+       ctrl->edid = drm_get_edid(connector, &ctrl->drm_aux->ddc);
+       if (!ctrl->edid) {
+               pr_err("%s: edid read fail\n", __func__);
+               goto disable_ret;
+       }
+
+       if (edid)
+               *edid = ctrl->edid;
+
+disable_ret:
+       if (!ctrl->power_on) {
+               edp_ctrl_irq_enable(ctrl, 0);
+               edp_ctrl_phy_aux_enable(ctrl, 0);
+       }
+unlock_ret:
+       mutex_unlock(&ctrl->dev_mutex);
+       return 0;
+}
+
+int msm_edp_ctrl_timing_cfg(struct edp_ctrl *ctrl,
+                               const struct drm_display_mode *mode,
+                               const struct drm_display_info *info)
+{
+       u32 hstart_from_sync, vstart_from_sync;
+       u32 data;
+       int ret = 0;
+
+       mutex_lock(&ctrl->dev_mutex);
+       /*
+        * Need to keep color depth, pixel rate and
+        * interlaced information in ctrl context
+        */
+       ctrl->color_depth = info->bpc;
+       ctrl->pixel_rate = mode->clock;
+       ctrl->interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+
+       /* Fill initial link config based on passed in timing */
+       edp_fill_link_cfg(ctrl);
+
+       if (edp_clk_enable(ctrl, EDP_CLK_MASK_AHB)) {
+               pr_err("%s, fail to prepare enable ahb clk\n", __func__);
+               ret = -EINVAL;
+               goto unlock_ret;
+       }
+       edp_clock_synchrous(ctrl, 1);
+
+       /* Configure eDP timing to HW */
+       edp_write(ctrl->base + REG_EDP_TOTAL_HOR_VER,
+               EDP_TOTAL_HOR_VER_HORIZ(mode->htotal) |
+               EDP_TOTAL_HOR_VER_VERT(mode->vtotal));
+
+       vstart_from_sync = mode->vtotal - mode->vsync_start;
+       hstart_from_sync = mode->htotal - mode->hsync_start;
+       edp_write(ctrl->base + REG_EDP_START_HOR_VER_FROM_SYNC,
+               EDP_START_HOR_VER_FROM_SYNC_HORIZ(hstart_from_sync) |
+               EDP_START_HOR_VER_FROM_SYNC_VERT(vstart_from_sync));
+
+       data = EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT(
+                       mode->vsync_end - mode->vsync_start);
+       data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ(
+                       mode->hsync_end - mode->hsync_start);
+       if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+               data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_NVSYNC;
+       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+               data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_NHSYNC;
+       edp_write(ctrl->base + REG_EDP_HSYNC_VSYNC_WIDTH_POLARITY, data);
+
+       edp_write(ctrl->base + REG_EDP_ACTIVE_HOR_VER,
+               EDP_ACTIVE_HOR_VER_HORIZ(mode->hdisplay) |
+               EDP_ACTIVE_HOR_VER_VERT(mode->vdisplay));
+
+       edp_clk_disable(ctrl, EDP_CLK_MASK_AHB);
+
+unlock_ret:
+       mutex_unlock(&ctrl->dev_mutex);
+       return ret;
+}
+
+bool msm_edp_ctrl_pixel_clock_valid(struct edp_ctrl *ctrl,
+       u32 pixel_rate, u32 *pm, u32 *pn)
+{
+       const struct edp_pixel_clk_div *divs;
+       u32 err = 1; /* 1% error tolerance */
+       u32 clk_err;
+       int i;
+
+       if (ctrl->link_rate == DP_LINK_BW_1_62) {
+               divs = clk_divs[0];
+       } else if (ctrl->link_rate == DP_LINK_BW_2_7) {
+               divs = clk_divs[1];
+       } else {
+               pr_err("%s: Invalid link rate,%d\n", __func__, ctrl->link_rate);
+               return false;
+       }
+
+       for (i = 0; i < EDP_PIXEL_CLK_NUM; i++) {
+               clk_err = abs(divs[i].rate - pixel_rate);
+               if ((divs[i].rate * err / 100) >= clk_err) {
+                       if (pm)
+                               *pm = divs[i].m;
+                       if (pn)
+                               *pn = divs[i].n;
+                       return true;
+               }
+       }
+
+       DBG("pixel clock %d(kHz) not supported", pixel_rate);
+
+       return false;
+}
+
index 86d7863..375f26d 100644 (file)
@@ -374,12 +374,20 @@ static int mxsfb_remove(struct platform_device *pdev)
        struct drm_device *drm = platform_get_drvdata(pdev);
 
        drm_dev_unregister(drm);
+       drm_atomic_helper_shutdown(drm);
        mxsfb_unload(drm);
        drm_dev_put(drm);
 
        return 0;
 }
 
+static void mxsfb_shutdown(struct platform_device *pdev)
+{
+       struct drm_device *drm = platform_get_drvdata(pdev);
+
+       drm_atomic_helper_shutdown(drm);
+}
+
 #ifdef CONFIG_PM_SLEEP
 static int mxsfb_suspend(struct device *dev)
 {
@@ -403,6 +411,7 @@ static const struct dev_pm_ops mxsfb_pm_ops = {
 static struct platform_driver mxsfb_platform_driver = {
        .probe          = mxsfb_probe,
        .remove         = mxsfb_remove,
+       .shutdown       = mxsfb_shutdown,
        .driver = {
                .name           = "mxsfb",
                .of_match_table = mxsfb_dt_ids,
index 9436310..3ec690b 100644 (file)
@@ -4,6 +4,7 @@ config DRM_NOUVEAU
        depends on DRM && PCI && MMU
        select IOMMU_API
        select FW_LOADER
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select DRM_TTM
        select DRM_TTM_HELPER
index ae1f412..df58c64 100644 (file)
@@ -35,7 +35,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_plane_helper.h>
index 40f90e3..1b17319 100644 (file)
@@ -36,7 +36,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_util.h>
 
 #include "nouveau_crtc.h"
index 040ed88..724d40d 100644 (file)
@@ -22,7 +22,7 @@
  * Authors: Ben Skeggs
  */
 
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 #include "nouveau_drv.h"
 #include "nouveau_connector.h"
@@ -147,6 +147,21 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector,
        nv_encoder->dp.link_nr =
                dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
 
+       if (connector->connector_type == DRM_MODE_CONNECTOR_eDP && dpcd[DP_DPCD_REV] >= 0x13) {
+               struct drm_dp_aux *aux = &nv_connector->aux;
+               int ret, i;
+               u8 sink_rates[16];
+
+               ret = drm_dp_dpcd_read(aux, DP_SUPPORTED_LINK_RATES, sink_rates, sizeof(sink_rates));
+               if (ret == sizeof(sink_rates)) {
+                       for (i = 0; i < ARRAY_SIZE(sink_rates); i += 2) {
+                               int val = ((sink_rates[i + 1] << 8) | sink_rates[i]) * 200 / 10;
+                               if (val && (i == 0 || val > nv_encoder->dp.link_bw))
+                                       nv_encoder->dp.link_bw = val;
+                       }
+               }
+       }
+
        NV_DEBUG(drm, "display: %dx%d dpcd 0x%02x\n",
                 nv_encoder->dp.link_nr, nv_encoder->dp.link_bw,
                 dpcd[DP_DPCD_REV]);
index 77c2fed..65ed84f 100644 (file)
@@ -30,8 +30,8 @@
 #include <subdev/bios/dcb.h>
 
 #include <drm/drm_encoder_slave.h>
-#include <drm/drm_dp_helper.h>
-#include <drm/drm_dp_mst_helper.h>
+#include <drm/dp/drm_dp_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
 #include "dispnv04/disp.h"
 struct nv50_head_atom;
 struct nouveau_connector;
index 2ca3207..2e517cd 100644 (file)
@@ -162,11 +162,12 @@ nouveau_mem_vram(struct ttm_resource *reg, bool contig, u8 page)
 }
 
 void
-nouveau_mem_del(struct ttm_resource *reg)
+nouveau_mem_del(struct ttm_resource_manager *man, struct ttm_resource *reg)
 {
        struct nouveau_mem *mem = nouveau_mem(reg);
 
        nouveau_mem_fini(mem);
+       ttm_resource_fini(man, reg);
        kfree(mem);
 }
 
index 2c01166..325551e 100644 (file)
@@ -23,7 +23,8 @@ nouveau_mem(struct ttm_resource *reg)
 
 int nouveau_mem_new(struct nouveau_cli *, u8 kind, u8 comp,
                    struct ttm_resource **);
-void nouveau_mem_del(struct ttm_resource *);
+void nouveau_mem_del(struct ttm_resource_manager *man,
+                    struct ttm_resource *);
 int nouveau_mem_vram(struct ttm_resource *, bool contig, u8 page);
 int nouveau_mem_host(struct ttm_resource *, struct ttm_tt *);
 void nouveau_mem_fini(struct nouveau_mem *);
index 2ca9d9a..85f1f5a 100644 (file)
 #include <core/tegra.h>
 
 static void
-nouveau_manager_del(struct ttm_resource_manager *man, struct ttm_resource *reg)
+nouveau_manager_del(struct ttm_resource_manager *man,
+                   struct ttm_resource *reg)
 {
-       nouveau_mem_del(reg);
+       nouveau_mem_del(man, reg);
 }
 
 static int
@@ -62,7 +63,7 @@ nouveau_vram_manager_new(struct ttm_resource_manager *man,
 
        ret = nouveau_mem_vram(*res, nvbo->contig, nvbo->page);
        if (ret) {
-               nouveau_mem_del(*res);
+               nouveau_mem_del(man, *res);
                return ret;
        }
 
@@ -118,7 +119,7 @@ nv04_gart_manager_new(struct ttm_resource_manager *man,
        ret = nvif_vmm_get(&mem->cli->vmm.vmm, PTES, false, 12, 0,
                           (long)(*res)->num_pages << PAGE_SHIFT, &mem->vma[0]);
        if (ret) {
-               nouveau_mem_del(*res);
+               nouveau_mem_del(man, *res);
                return ret;
        }
 
@@ -163,7 +164,7 @@ nouveau_ttm_init_vram(struct nouveau_drm *drm)
 
                man->func = &nouveau_vram_manager;
 
-               ttm_resource_manager_init(man,
+               ttm_resource_manager_init(man, &drm->ttm.bdev,
                                          drm->gem.vram_available >> PAGE_SHIFT);
                ttm_set_driver_manager(&drm->ttm.bdev, TTM_PL_VRAM, man);
                ttm_resource_manager_set_used(man, true);
@@ -210,7 +211,7 @@ nouveau_ttm_init_gtt(struct nouveau_drm *drm)
 
        man->func = func;
        man->use_tt = true;
-       ttm_resource_manager_init(man, size_pages);
+       ttm_resource_manager_init(man, &drm->ttm.bdev, size_pages);
        ttm_set_driver_manager(&drm->ttm.bdev, TTM_PL_TT, man);
        ttm_resource_manager_set_used(man, true);
        return 0;
index 9669472..8e09315 100644 (file)
 
 struct lt_state {
        struct nvkm_dp *dp;
+
+       int repeaters;
+       int repeater;
+
        u8  stat[6];
        u8  conf[4];
        bool pc2;
@@ -52,14 +56,26 @@ static int
 nvkm_dp_train_sense(struct lt_state *lt, bool pc, u32 delay)
 {
        struct nvkm_dp *dp = lt->dp;
+       u32 addr;
        int ret;
 
-       if (dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
-               mdelay(dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
+       usleep_range(delay, delay * 2);
+
+       if (lt->repeater)
+               addr = DPCD_LTTPR_LANE0_1_STATUS(lt->repeater);
+       else
+               addr = DPCD_LS02;
+
+       ret = nvkm_rdaux(dp->aux, addr, &lt->stat[0], 3);
+       if (ret)
+               return ret;
+
+       if (lt->repeater)
+               addr = DPCD_LTTPR_LANE0_1_ADJUST(lt->repeater);
        else
-               udelay(delay);
+               addr = DPCD_LS06;
 
-       ret = nvkm_rdaux(dp->aux, DPCD_LS02, lt->stat, 6);
+       ret = nvkm_rdaux(dp->aux, addr, &lt->stat[4], 2);
        if (ret)
                return ret;
 
@@ -85,6 +101,7 @@ nvkm_dp_train_drive(struct lt_state *lt, bool pc)
        struct nvbios_dpout info;
        struct nvbios_dpcfg ocfg;
        u8  ver, hdr, cnt, len;
+       u32 addr;
        u32 data;
        int ret, i;
 
@@ -113,6 +130,9 @@ nvkm_dp_train_drive(struct lt_state *lt, bool pc)
                OUTP_TRACE(&dp->outp, "config lane %d %02x %02x",
                           i, lt->conf[i], lpc2);
 
+               if (lt->repeater != lt->repeaters)
+                       continue;
+
                data = nvbios_dpout_match(bios, dp->outp.info.hasht,
                                                dp->outp.info.hashm,
                                          &ver, &hdr, &cnt, &len, &info);
@@ -129,7 +149,12 @@ nvkm_dp_train_drive(struct lt_state *lt, bool pc)
                                            ocfg.pe, ocfg.tx_pu);
        }
 
-       ret = nvkm_wraux(dp->aux, DPCD_LC03(0), lt->conf, 4);
+       if (lt->repeater)
+               addr = DPCD_LTTPR_LANE0_SET(lt->repeater);
+       else
+               addr = DPCD_LC03(0);
+
+       ret = nvkm_wraux(dp->aux, addr, lt->conf, 4);
        if (ret)
                return ret;
 
@@ -146,32 +171,59 @@ static void
 nvkm_dp_train_pattern(struct lt_state *lt, u8 pattern)
 {
        struct nvkm_dp *dp = lt->dp;
+       u32 addr;
        u8 sink_tp;
 
        OUTP_TRACE(&dp->outp, "training pattern %d", pattern);
        dp->outp.ior->func->dp.pattern(dp->outp.ior, pattern);
 
-       nvkm_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1);
+       if (lt->repeater)
+               addr = DPCD_LTTPR_PATTERN_SET(lt->repeater);
+       else
+               addr = DPCD_LC02;
+
+       nvkm_rdaux(dp->aux, addr, &sink_tp, 1);
        sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
-       sink_tp |= pattern;
-       nvkm_wraux(dp->aux, DPCD_LC02, &sink_tp, 1);
+       sink_tp |= (pattern != 4) ? pattern : 7;
+
+       if (pattern != 0)
+               sink_tp |=  DPCD_LC02_SCRAMBLING_DISABLE;
+       else
+               sink_tp &= ~DPCD_LC02_SCRAMBLING_DISABLE;
+       nvkm_wraux(dp->aux, addr, &sink_tp, 1);
 }
 
 static int
 nvkm_dp_train_eq(struct lt_state *lt)
 {
+       struct nvkm_i2c_aux *aux = lt->dp->aux;
        bool eq_done = false, cr_done = true;
-       int tries = 0, i;
+       int tries = 0, usec = 0, i;
+       u8 data;
 
-       if (lt->dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED)
-               nvkm_dp_train_pattern(lt, 3);
-       else
-               nvkm_dp_train_pattern(lt, 2);
+       if (lt->repeater) {
+               if (!nvkm_rdaux(aux, DPCD_LTTPR_AUX_RD_INTERVAL(lt->repeater), &data, sizeof(data)))
+                       usec = (data & DPCD_RC0E_AUX_RD_INTERVAL) * 4000;
+
+               nvkm_dp_train_pattern(lt, 4);
+       } else {
+               if (lt->dp->dpcd[DPCD_RC00_DPCD_REV] >= 0x14 &&
+                   lt->dp->dpcd[DPCD_RC03] & DPCD_RC03_TPS4_SUPPORTED)
+                       nvkm_dp_train_pattern(lt, 4);
+               else
+               if (lt->dp->dpcd[DPCD_RC00_DPCD_REV] >= 0x12 &&
+                   lt->dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED)
+                       nvkm_dp_train_pattern(lt, 3);
+               else
+                       nvkm_dp_train_pattern(lt, 2);
+
+               usec = (lt->dp->dpcd[DPCD_RC0E] & DPCD_RC0E_AUX_RD_INTERVAL) * 4000;
+       }
 
        do {
                if ((tries &&
                    nvkm_dp_train_drive(lt, lt->pc2)) ||
-                   nvkm_dp_train_sense(lt, lt->pc2, 400))
+                   nvkm_dp_train_sense(lt, lt->pc2, usec ? usec : 400))
                        break;
 
                eq_done = !!(lt->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE);
@@ -193,13 +245,16 @@ nvkm_dp_train_cr(struct lt_state *lt)
 {
        bool cr_done = false, abort = false;
        int voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
-       int tries = 0, i;
+       int tries = 0, usec = 0, i;
 
        nvkm_dp_train_pattern(lt, 1);
 
+       if (lt->dp->dpcd[DPCD_RC00_DPCD_REV] < 0x14 && !lt->repeater)
+               usec = (lt->dp->dpcd[DPCD_RC0E] & DPCD_RC0E_AUX_RD_INTERVAL) * 4000;
+
        do {
                if (nvkm_dp_train_drive(lt, false) ||
-                   nvkm_dp_train_sense(lt, false, 100))
+                   nvkm_dp_train_sense(lt, false, usec ? usec : 100))
                        break;
 
                cr_done = true;
@@ -223,7 +278,7 @@ nvkm_dp_train_cr(struct lt_state *lt)
 }
 
 static int
-nvkm_dp_train_links(struct nvkm_dp *dp)
+nvkm_dp_train_links(struct nvkm_dp *dp, int rate)
 {
        struct nvkm_ior *ior = dp->outp.ior;
        struct nvkm_disp *disp = dp->outp.disp;
@@ -233,13 +288,15 @@ nvkm_dp_train_links(struct nvkm_dp *dp)
                .dp = dp,
        };
        u32 lnkcmp;
-       u8 sink[2];
+       u8 sink[2], data;
        int ret;
 
        OUTP_DBG(&dp->outp, "training %d x %d MB/s",
                 ior->dp.nr, ior->dp.bw * 27);
 
        /* Intersect misc. capabilities of the OR and sink. */
+       if (disp->engine.subdev.device->chipset < 0x110)
+               dp->dpcd[DPCD_RC03] &= ~DPCD_RC03_TPS4_SUPPORTED;
        if (disp->engine.subdev.device->chipset < 0xd0)
                dp->dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED;
        lt.pc2 = dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED;
@@ -287,8 +344,22 @@ nvkm_dp_train_links(struct nvkm_dp *dp)
 
        ior->func->dp.power(ior, ior->dp.nr);
 
+       /* Select LTTPR non-transparent mode if we have a valid configuration,
+        * use transparent mode otherwise.
+        */
+       if (dp->lttpr[0] >= 0x14) {
+               data = DPCD_LTTPR_MODE_TRANSPARENT;
+               nvkm_wraux(dp->aux, DPCD_LTTPR_MODE, &data, sizeof(data));
+
+               if (dp->lttprs) {
+                       data = DPCD_LTTPR_MODE_NON_TRANSPARENT;
+                       nvkm_wraux(dp->aux, DPCD_LTTPR_MODE, &data, sizeof(data));
+                       lt.repeaters = dp->lttprs;
+               }
+       }
+
        /* Set desired link configuration on the sink. */
-       sink[0] = ior->dp.bw;
+       sink[0] = (dp->rate[rate].dpcd < 0) ? ior->dp.bw : 0;
        sink[1] = ior->dp.nr;
        if (ior->dp.ef)
                sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
@@ -297,12 +368,33 @@ nvkm_dp_train_links(struct nvkm_dp *dp)
        if (ret)
                return ret;
 
+       if (dp->rate[rate].dpcd >= 0) {
+               ret = nvkm_rdaux(dp->aux, DPCD_LC15_LINK_RATE_SET, &sink[0], sizeof(sink[0]));
+               if (ret)
+                       return ret;
+
+               sink[0] &= ~DPCD_LC15_LINK_RATE_SET_MASK;
+               sink[0] |= dp->rate[rate].dpcd;
+
+               ret = nvkm_wraux(dp->aux, DPCD_LC15_LINK_RATE_SET, &sink[0], sizeof(sink[0]));
+               if (ret)
+                       return ret;
+       }
+
        /* Attempt to train the link in this configuration. */
-       memset(lt.stat, 0x00, sizeof(lt.stat));
-       ret = nvkm_dp_train_cr(&lt);
-       if (ret == 0)
-               ret = nvkm_dp_train_eq(&lt);
-       nvkm_dp_train_pattern(&lt, 0);
+       for (lt.repeater = lt.repeaters; lt.repeater >= 0; lt.repeater--) {
+               if (lt.repeater)
+                       OUTP_DBG(&dp->outp, "training LTTPR%d", lt.repeater);
+               else
+                       OUTP_DBG(&dp->outp, "training sink");
+
+               memset(lt.stat, 0x00, sizeof(lt.stat));
+               ret = nvkm_dp_train_cr(&lt);
+               if (ret == 0)
+                       ret = nvkm_dp_train_eq(&lt);
+               nvkm_dp_train_pattern(&lt, 0);
+       }
+
        return ret;
 }
 
@@ -345,63 +437,13 @@ nvkm_dp_train_init(struct nvkm_dp *dp)
        }
 }
 
-static const struct dp_rates {
-       u32 rate;
-       u8  bw;
-       u8  nr;
-} nvkm_dp_rates[] = {
-       { 2160000, 0x14, 4 },
-       { 1080000, 0x0a, 4 },
-       { 1080000, 0x14, 2 },
-       {  648000, 0x06, 4 },
-       {  540000, 0x0a, 2 },
-       {  540000, 0x14, 1 },
-       {  324000, 0x06, 2 },
-       {  270000, 0x0a, 1 },
-       {  162000, 0x06, 1 },
-       {}
-};
-
 static int
 nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps)
 {
        struct nvkm_ior *ior = dp->outp.ior;
-       const u8 sink_nr = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT;
-       const u8 sink_bw = dp->dpcd[DPCD_RC01_MAX_LINK_RATE];
-       const u8 outp_nr = dp->outp.info.dpconf.link_nr;
-       const u8 outp_bw = dp->outp.info.dpconf.link_bw;
-       const struct dp_rates *failsafe = NULL, *cfg;
-       int ret = -EINVAL;
+       int ret = -EINVAL, nr, rate;
        u8  pwr;
 
-       /* Find the lowest configuration of the OR that can support
-        * the required link rate.
-        *
-        * We will refuse to program the OR to lower rates, even if
-        * link training fails at higher rates (or even if the sink
-        * can't support the rate at all, though the DD is supposed
-        * to prevent such situations from happening).
-        *
-        * Attempting to do so can cause the entire display to hang,
-        * and it's better to have a failed modeset than that.
-        */
-       for (cfg = nvkm_dp_rates; cfg->rate; cfg++) {
-               if (cfg->nr <= outp_nr && cfg->bw <= outp_bw) {
-                       /* Try to respect sink limits too when selecting
-                        * lowest link configuration.
-                        */
-                       if (!failsafe ||
-                           (cfg->nr <= sink_nr && cfg->bw <= sink_bw))
-                               failsafe = cfg;
-               }
-
-               if (failsafe && cfg[1].rate < dataKBps)
-                       break;
-       }
-
-       if (WARN_ON(!failsafe))
-               return ret;
-
        /* Ensure sink is not in a low-power state. */
        if (!nvkm_rdaux(dp->aux, DPCD_SC00, &pwr, 1)) {
                if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) {
@@ -411,25 +453,22 @@ nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps)
                }
        }
 
+       ior->dp.mst = dp->lt.mst;
+       ior->dp.ef = dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP;
+       ior->dp.nr = 0;
+
        /* Link training. */
-       OUTP_DBG(&dp->outp, "training (min: %d x %d MB/s)",
-                failsafe->nr, failsafe->bw * 27);
+       OUTP_DBG(&dp->outp, "training");
        nvkm_dp_train_init(dp);
-       for (cfg = nvkm_dp_rates; ret < 0 && cfg <= failsafe; cfg++) {
-               /* Skip configurations not supported by both OR and sink. */
-               if ((cfg->nr > outp_nr || cfg->bw > outp_bw ||
-                    cfg->nr > sink_nr || cfg->bw > sink_bw)) {
-                       if (cfg != failsafe)
-                               continue;
-                       OUTP_ERR(&dp->outp, "link rate unsupported by sink");
+       for (nr = dp->links; ret < 0 && nr; nr >>= 1) {
+               for (rate = 0; ret < 0 && rate < dp->rates; rate++) {
+                       if (dp->rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) {
+                               /* Program selected link configuration. */
+                               ior->dp.bw = dp->rate[rate].rate / 27000;
+                               ior->dp.nr = nr;
+                               ret = nvkm_dp_train_links(dp, rate);
+                       }
                }
-               ior->dp.mst = dp->lt.mst;
-               ior->dp.ef = dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP;
-               ior->dp.bw = cfg->bw;
-               ior->dp.nr = cfg->nr;
-
-               /* Program selected link configuration. */
-               ret = nvkm_dp_train_links(dp);
        }
        nvkm_dp_train_fini(dp);
        if (ret < 0)
@@ -526,6 +565,47 @@ done:
        return ret;
 }
 
+static bool
+nvkm_dp_enable_supported_link_rates(struct nvkm_dp *dp)
+{
+       u8 sink_rates[DPCD_RC10_SUPPORTED_LINK_RATES__SIZE];
+       int i, j, k;
+
+       if (dp->outp.conn->info.type != DCB_CONNECTOR_eDP ||
+           dp->dpcd[DPCD_RC00_DPCD_REV] < 0x13 ||
+           nvkm_rdaux(dp->aux, DPCD_RC10_SUPPORTED_LINK_RATES(0), sink_rates, sizeof(sink_rates)))
+               return false;
+
+       for (i = 0; i < ARRAY_SIZE(sink_rates); i += 2) {
+               const u32 rate = ((sink_rates[i + 1] << 8) | sink_rates[i]) * 200 / 10;
+
+               if (!rate || WARN_ON(dp->rates == ARRAY_SIZE(dp->rate)))
+                       break;
+
+               if (rate > dp->outp.info.dpconf.link_bw * 27000) {
+                       OUTP_DBG(&dp->outp, "rate %d !outp", rate);
+                       continue;
+               }
+
+               for (j = 0; j < dp->rates; j++) {
+                       if (rate > dp->rate[j].rate) {
+                               for (k = dp->rates; k > j; k--)
+                                       dp->rate[k] = dp->rate[k - 1];
+                               break;
+                       }
+               }
+
+               dp->rate[j].dpcd = i / 2;
+               dp->rate[j].rate = rate;
+               dp->rates++;
+       }
+
+       for (i = 0; i < dp->rates; i++)
+               OUTP_DBG(&dp->outp, "link_rate[%d] = %d", dp->rate[i].dpcd, dp->rate[i].rate);
+
+       return dp->rates != 0;
+}
+
 static bool
 nvkm_dp_enable(struct nvkm_dp *dp, bool enable)
 {
@@ -538,9 +618,60 @@ nvkm_dp_enable(struct nvkm_dp *dp, bool enable)
                        dp->present = true;
                }
 
-               if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd,
-                               sizeof(dp->dpcd)))
+               /* Detect any LTTPRs before reading DPCD receiver caps. */
+               if (!nvkm_rdaux(aux, DPCD_LTTPR_REV, dp->lttpr, sizeof(dp->lttpr)) &&
+                   dp->lttpr[0] >= 0x14 && dp->lttpr[2]) {
+                       switch (dp->lttpr[2]) {
+                       case 0x80: dp->lttprs = 1; break;
+                       case 0x40: dp->lttprs = 2; break;
+                       case 0x20: dp->lttprs = 3; break;
+                       case 0x10: dp->lttprs = 4; break;
+                       case 0x08: dp->lttprs = 5; break;
+                       case 0x04: dp->lttprs = 6; break;
+                       case 0x02: dp->lttprs = 7; break;
+                       case 0x01: dp->lttprs = 8; break;
+                       default:
+                               /* Unknown LTTPR count, we'll switch to transparent mode. */
+                               WARN_ON(1);
+                               dp->lttprs = 0;
+                               break;
+                       }
+               } else {
+                       /* No LTTPR support, or zero LTTPR count - don't touch it at all. */
+                       memset(dp->lttpr, 0x00, sizeof(dp->lttpr));
+               }
+
+               if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd, sizeof(dp->dpcd))) {
+                       const u8 rates[] = { 0x1e, 0x14, 0x0a, 0x06, 0 };
+                       const u8 *rate;
+                       int rate_max;
+
+                       dp->rates = 0;
+                       dp->links = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT;
+                       dp->links = min(dp->links, dp->outp.info.dpconf.link_nr);
+                       if (dp->lttprs && dp->lttpr[4])
+                               dp->links = min_t(int, dp->links, dp->lttpr[4]);
+
+                       rate_max = dp->dpcd[DPCD_RC01_MAX_LINK_RATE];
+                       rate_max = min(rate_max, dp->outp.info.dpconf.link_bw);
+                       if (dp->lttprs && dp->lttpr[1])
+                               rate_max = min_t(int, rate_max, dp->lttpr[1]);
+
+                       if (!nvkm_dp_enable_supported_link_rates(dp)) {
+                               for (rate = rates; *rate; rate++) {
+                                       if (*rate <= rate_max) {
+                                               if (WARN_ON(dp->rates == ARRAY_SIZE(dp->rate)))
+                                                       break;
+
+                                               dp->rate[dp->rates].dpcd = -1;
+                                               dp->rate[dp->rates].rate = *rate * 27000;
+                                               dp->rates++;
+                                       }
+                               }
+                       }
+
                        return true;
+               }
        }
 
        if (dp->present) {
index e484d0c..8e59dd4 100644 (file)
@@ -9,10 +9,7 @@
 #include <subdev/bios/dp.h>
 
 struct nvkm_dp {
-       union {
-               struct nvkm_outp base;
-               struct nvkm_outp outp;
-       };
+       struct nvkm_outp outp;
 
        struct nvbios_dpout info;
        u8 version;
@@ -21,8 +18,17 @@ struct nvkm_dp {
 
        struct nvkm_notify hpd;
        bool present;
+       u8 lttpr[6];
+       u8 lttprs;
        u8 dpcd[16];
 
+       struct {
+               int dpcd; /* -1, or index into SUPPORTED_LINK_RATES table */
+               u32 rate;
+       } rate[8];
+       int rates;
+       int links;
+
        struct mutex mutex;
        struct {
                atomic_t done;
@@ -42,8 +48,12 @@ void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *);
 #define DPCD_RC02_TPS3_SUPPORTED                                           0x40
 #define DPCD_RC02_MAX_LANE_COUNT                                           0x1f
 #define DPCD_RC03                                                       0x00003
+#define DPCD_RC03_TPS4_SUPPORTED                                           0x80
 #define DPCD_RC03_MAX_DOWNSPREAD                                           0x01
-#define DPCD_RC0E_AUX_RD_INTERVAL                                       0x0000e
+#define DPCD_RC0E                                                       0x0000e
+#define DPCD_RC0E_AUX_RD_INTERVAL                                          0x7f
+#define DPCD_RC10_SUPPORTED_LINK_RATES(i)                               0x00010
+#define DPCD_RC10_SUPPORTED_LINK_RATES__SIZE                                 16
 
 /* DPCD Link Configuration */
 #define DPCD_LC00_LINK_BW_SET                                           0x00100
@@ -51,7 +61,8 @@ void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *);
 #define DPCD_LC01_ENHANCED_FRAME_EN                                        0x80
 #define DPCD_LC01_LANE_COUNT_SET                                           0x1f
 #define DPCD_LC02                                                       0x00102
-#define DPCD_LC02_TRAINING_PATTERN_SET                                     0x03
+#define DPCD_LC02_TRAINING_PATTERN_SET                                     0x0f
+#define DPCD_LC02_SCRAMBLING_DISABLE                                       0x20
 #define DPCD_LC03(l)                                            ((l) +  0x00103)
 #define DPCD_LC03_MAX_PRE_EMPHASIS_REACHED                                 0x20
 #define DPCD_LC03_PRE_EMPHASIS_SET                                         0x18
@@ -67,6 +78,8 @@ void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *);
 #define DPCD_LC10_LANE3_POST_CURSOR2_SET                                   0x30
 #define DPCD_LC10_LANE2_MAX_POST_CURSOR2_REACHED                           0x04
 #define DPCD_LC10_LANE2_POST_CURSOR2_SET                                   0x03
+#define DPCD_LC15_LINK_RATE_SET                                         0x00115
+#define DPCD_LC15_LINK_RATE_SET_MASK                                       0x07
 
 /* DPCD Link/Sink Status */
 #define DPCD_LS02                                                       0x00202
@@ -108,4 +121,14 @@ void nvkm_dp_disable(struct nvkm_outp *, struct nvkm_ior *);
 #define DPCD_SC00_SET_POWER                                                0x03
 #define DPCD_SC00_SET_POWER_D0                                             0x01
 #define DPCD_SC00_SET_POWER_D3                                             0x03
+
+#define DPCD_LTTPR_REV                                                  0xf0000
+#define DPCD_LTTPR_MODE                                                 0xf0003
+#define DPCD_LTTPR_MODE_TRANSPARENT                                        0x55
+#define DPCD_LTTPR_MODE_NON_TRANSPARENT                                    0xaa
+#define DPCD_LTTPR_PATTERN_SET(i)                     ((i - 1) * 0x50 + 0xf0010)
+#define DPCD_LTTPR_LANE0_SET(i)                       ((i - 1) * 0x50 + 0xf0011)
+#define DPCD_LTTPR_AUX_RD_INTERVAL(i)                 ((i - 1) * 0x50 + 0xf0020)
+#define DPCD_LTTPR_LANE0_1_STATUS(i)                  ((i - 1) * 0x50 + 0xf0030)
+#define DPCD_LTTPR_LANE0_1_ADJUST(i)                  ((i - 1) * 0x50 + 0xf0033)
 #endif
index 4d59d02..56b8f44 100644 (file)
@@ -77,7 +77,18 @@ g94_sor_dp_pattern(struct nvkm_ior *sor, int pattern)
 {
        struct nvkm_device *device = sor->disp->engine.subdev.device;
        const u32 loff = nv50_sor_link(sor);
-       nvkm_mask(device, 0x61c10c + loff, 0x0f000000, pattern << 24);
+       u32 data;
+
+       switch (pattern) {
+       case 0: data = 0x00001000; break;
+       case 1: data = 0x01000000; break;
+       case 2: data = 0x02000000; break;
+       default:
+               WARN_ON(1);
+               return;
+       }
+
+       nvkm_mask(device, 0x61c10c + loff, 0x0f001000, data);
 }
 
 void
index 033827d..d2c05f5 100644 (file)
@@ -37,6 +37,10 @@ ga102_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
        case 0x0a: clksor |= 0x00040000; break;
        case 0x14: clksor |= 0x00080000; break;
        case 0x1e: clksor |= 0x000c0000; break;
+       case 0x08: clksor |= 0x00100000; break;
+       case 0x09: clksor |= 0x00140000; break;
+       case 0x0c: clksor |= 0x00180000; break;
+       case 0x10: clksor |= 0x001c0000; break;
        default:
                WARN_ON(1);
                return -EINVAL;
index 3b3643f..c431e0b 100644 (file)
@@ -92,7 +92,19 @@ gf119_sor_dp_pattern(struct nvkm_ior *sor, int pattern)
 {
        struct nvkm_device *device = sor->disp->engine.subdev.device;
        const u32 soff = nv50_ior_base(sor);
-       nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern);
+       u32 data;
+
+       switch (pattern) {
+       case 0: data = 0x10101010; break;
+       case 1: data = 0x01010101; break;
+       case 2: data = 0x02020202; break;
+       case 3: data = 0x03030303; break;
+       default:
+               WARN_ON(1);
+               return;
+       }
+
+       nvkm_mask(device, 0x61c110 + soff, 0x1f1f1f1f, data);
 }
 
 int
index 38045c9..3696bfd 100644 (file)
@@ -28,11 +28,23 @@ gm107_sor_dp_pattern(struct nvkm_ior *sor, int pattern)
 {
        struct nvkm_device *device = sor->disp->engine.subdev.device;
        const u32 soff = nv50_ior_base(sor);
-       const u32 data = 0x01010101 * pattern;
+       u32 mask = 0x1f1f1f1f, data;
+
+       switch (pattern) {
+       case 0: data = 0x10101010; break;
+       case 1: data = 0x01010101; break;
+       case 2: data = 0x02020202; break;
+       case 3: data = 0x03030303; break;
+       case 4: data = 0x1b1b1b1b; break;
+       default:
+               WARN_ON(1);
+               return;
+       }
+
        if (sor->asy.link & 1)
-               nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, data);
+               nvkm_mask(device, 0x61c110 + soff, mask, data);
        else
-               nvkm_mask(device, 0x61c12c + soff, 0x0f0f0f0f, data);
+               nvkm_mask(device, 0x61c12c + soff, mask, data);
 }
 
 static const struct nvkm_ior_func
index 667fa01..a6ea89a 100644 (file)
@@ -142,11 +142,12 @@ nvkm_acr_hsfw_load_bl(struct nvkm_acr *acr, const char *name, int ver,
 
        hsfw->imem_size = desc->code_size;
        hsfw->imem_tag = desc->start_tag;
-       hsfw->imem = kmalloc(desc->code_size, GFP_KERNEL);
-       memcpy(hsfw->imem, data + desc->code_off, desc->code_size);
-
+       hsfw->imem = kmemdup(data + desc->code_off, desc->code_size, GFP_KERNEL);
        nvkm_firmware_put(fw);
-       return 0;
+       if (!hsfw->imem)
+               return -ENOMEM;
+       else
+               return 0;
 }
 
 int
index 57af3d9..ab3fc86 100644 (file)
@@ -93,6 +93,7 @@ struct dma_buf *omap_gem_prime_export(struct drm_gem_object *obj, int flags)
        exp_info.size = omap_gem_mmap_size(obj);
        exp_info.flags = flags;
        exp_info.priv = obj;
+       exp_info.resv = obj->resv;
 
        return drm_gem_dmabuf_export(obj->dev, &exp_info);
 }
index 5fcbde7..1be150a 100644 (file)
@@ -86,7 +86,7 @@ static const struct panel_init_cmd boe_tv110c9m_init_cmd[] = {
        _INIT_DCS_CMD(0x0F, 0x73),
        _INIT_DCS_CMD(0x95, 0xE6),
        _INIT_DCS_CMD(0x96, 0xF0),
-       _INIT_DCS_CMD(0x30, 0x11),
+       _INIT_DCS_CMD(0x30, 0x00),
        _INIT_DCS_CMD(0x6D, 0x66),
        _INIT_DCS_CMD(0x75, 0xA2),
        _INIT_DCS_CMD(0x77, 0x3B),
@@ -112,17 +112,17 @@ static const struct panel_init_cmd boe_tv110c9m_init_cmd[] = {
        _INIT_DCS_CMD(0xB1, 0x00, 0xD2, 0x01, 0x0B, 0x01, 0x34, 0x01, 0x76, 0x01, 0xA3, 0x01, 0xEF, 0x02, 0x27, 0x02, 0x29),
        _INIT_DCS_CMD(0xB2, 0x02, 0x5F, 0x02, 0x9E, 0x02, 0xC9, 0x03, 0x00, 0x03, 0x26, 0x03, 0x53, 0x03, 0x63, 0x03, 0x73),
 
-       _INIT_DCS_CMD(0xB3, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xA7, 0x03, 0xCF, 0x03, 0xDE, 0x03, 0xE0),
+       _INIT_DCS_CMD(0xB3, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xAF, 0x03, 0xDF, 0x03, 0xF5, 0x03, 0xE0),
        _INIT_DCS_CMD(0xB4, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x45, 0x00, 0x65, 0x00, 0x81, 0x00, 0x99, 0x00, 0xAE, 0x00, 0xC1),
        _INIT_DCS_CMD(0xB5, 0x00, 0xD2, 0x01, 0x0B, 0x01, 0x34, 0x01, 0x76, 0x01, 0xA3, 0x01, 0xEF, 0x02, 0x27, 0x02, 0x29),
        _INIT_DCS_CMD(0xB6, 0x02, 0x5F, 0x02, 0x9E, 0x02, 0xC9, 0x03, 0x00, 0x03, 0x26, 0x03, 0x53, 0x03, 0x63, 0x03, 0x73),
-       _INIT_DCS_CMD(0xB7, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xA7, 0x03, 0xCF, 0x03, 0xDE, 0x03, 0xE0),
+       _INIT_DCS_CMD(0xB7, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xAF, 0x03, 0xDF, 0x03, 0xF5, 0x03, 0xE0),
 
        _INIT_DCS_CMD(0xB8, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x45, 0x00, 0x65, 0x00, 0x81, 0x00, 0x99, 0x00, 0xAE, 0x00, 0xC1),
        _INIT_DCS_CMD(0xB9, 0x00, 0xD2, 0x01, 0x0B, 0x01, 0x34, 0x01, 0x76, 0x01, 0xA3, 0x01, 0xEF, 0x02, 0x27, 0x02, 0x29),
        _INIT_DCS_CMD(0xBA, 0x02, 0x5F, 0x02, 0x9E, 0x02, 0xC9, 0x03, 0x00, 0x03, 0x26, 0x03, 0x53, 0x03, 0x63, 0x03, 0x73),
 
-       _INIT_DCS_CMD(0xBB, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xA7, 0x03, 0xCF, 0x03, 0xDE, 0x03, 0xE0),
+       _INIT_DCS_CMD(0xBB, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xAF, 0x03, 0xDF, 0x03, 0xF5, 0x03, 0xE0),
        _INIT_DCS_CMD(0xFF, 0x24),
        _INIT_DCS_CMD(0xFB, 0x01),
 
index 176ef0c..a394a15 100644 (file)
@@ -36,8 +36,8 @@
 
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
-#include <drm/drm_dp_aux_bus.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_aux_bus.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_panel.h>
 
 /**
@@ -1745,6 +1745,19 @@ static const struct panel_delay delay_200_500_e50 = {
        .enable = 50,
 };
 
+static const struct panel_delay delay_200_500_e80_d50 = {
+       .hpd_absent = 200,
+       .unprepare = 500,
+       .enable = 80,
+       .disable = 50,
+};
+
+static const struct panel_delay delay_100_500_e200 = {
+       .hpd_absent = 100,
+       .unprepare = 500,
+       .enable = 200,
+};
+
 #define EDP_PANEL_ENTRY(vend_chr_0, vend_chr_1, vend_chr_2, product_id, _delay, _name) \
 { \
        .name = _name, \
@@ -1768,13 +1781,17 @@ static const struct edp_panel_entry edp_panels[] = {
        EDP_PANEL_ENTRY('B', 'O', 'E', 0x07d1, &boe_nv133fhm_n61.delay, "NV133FHM-N61"),
        EDP_PANEL_ENTRY('B', 'O', 'E', 0x082d, &boe_nv133fhm_n61.delay, "NV133FHM-N62"),
        EDP_PANEL_ENTRY('B', 'O', 'E', 0x098d, &boe_nv110wtm_n61.delay, "NV110WTM-N61"),
+       EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a5d, &delay_200_500_e50, "NV116WHM-N45"),
 
        EDP_PANEL_ENTRY('C', 'M', 'N', 0x114c, &innolux_n116bca_ea1.delay, "N116BCA-EA1"),
 
        EDP_PANEL_ENTRY('K', 'D', 'B', 0x0624, &kingdisplay_kd116n21_30nv_a010.delay, "116N21-30NV-A010"),
+       EDP_PANEL_ENTRY('K', 'D', 'B', 0x1120, &delay_200_500_e80_d50, "116N29-30NK-C007"),
 
        EDP_PANEL_ENTRY('S', 'H', 'P', 0x154c, &delay_200_500_p2e100, "LQ116M1JW10"),
 
+       EDP_PANEL_ENTRY('S', 'T', 'A', 0x0100, &delay_100_500_e200, "2081116HHD028001-51D"),
+
        { /* sentinal */ }
 };
 
index 221db65..20666b6 100644 (file)
@@ -14,8 +14,8 @@
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 
-#include <drm/drm_dp_aux_bus.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_aux_bus.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_panel.h>
 
index 9e46db5..0c8786e 100644 (file)
@@ -2525,6 +2525,36 @@ static const struct panel_desc mitsubishi_aa070mc01 = {
        .bus_flags = DRM_BUS_FLAG_DE_HIGH,
 };
 
+static const struct display_timing multi_inno_mi0700s4t_6_timing = {
+       .pixelclock = { 29000000, 33000000, 38000000 },
+       .hactive = { 800, 800, 800 },
+       .hfront_porch = { 180, 210, 240 },
+       .hback_porch = { 16, 16, 16 },
+       .hsync_len = { 30, 30, 30 },
+       .vactive = { 480, 480, 480 },
+       .vfront_porch = { 12, 22, 32 },
+       .vback_porch = { 10, 10, 10 },
+       .vsync_len = { 13, 13, 13 },
+       .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
+                DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE |
+                DISPLAY_FLAGS_SYNC_POSEDGE,
+};
+
+static const struct panel_desc multi_inno_mi0700s4t_6 = {
+       .timings = &multi_inno_mi0700s4t_6_timing,
+       .num_timings = 1,
+       .bpc = 8,
+       .size = {
+               .width = 154,
+               .height = 86,
+       },
+       .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+       .bus_flags = DRM_BUS_FLAG_DE_HIGH |
+                    DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE |
+                    DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE,
+       .connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
 static const struct display_timing multi_inno_mi1010ait_1cp_timing = {
        .pixelclock = { 68900000, 70000000, 73400000 },
        .hactive = { 1280, 1280, 1280 },
@@ -3871,6 +3901,9 @@ static const struct of_device_id platform_of_match[] = {
        }, {
                .compatible = "mitsubishi,aa070mc01-ca1",
                .data = &mitsubishi_aa070mc01,
+       }, {
+               .compatible = "multi-inno,mi0700s4t-6",
+               .data = &multi_inno_mi0700s4t_6,
        }, {
                .compatible = "multi-inno,mi1010ait-1cp",
                .data = &multi_inno_mi1010ait_1cp,
index 5056777..34f2bae 100644 (file)
@@ -12,24 +12,6 @@ enum panfrost_hw_feature {
        HW_FEATURE_JOBCHAIN_DISAMBIGUATION,
        HW_FEATURE_PWRON_DURING_PWROFF_TRANS,
        HW_FEATURE_XAFFINITY,
-       HW_FEATURE_OUT_OF_ORDER_EXEC,
-       HW_FEATURE_MRT,
-       HW_FEATURE_BRNDOUT_CC,
-       HW_FEATURE_INTERPIPE_REG_ALIASING,
-       HW_FEATURE_LD_ST_TILEBUFFER,
-       HW_FEATURE_MSAA_16X,
-       HW_FEATURE_32_BIT_UNIFORM_ADDRESS,
-       HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL,
-       HW_FEATURE_OPTIMIZED_COVERAGE_MASK,
-       HW_FEATURE_T7XX_PAIRING_RULES,
-       HW_FEATURE_LD_ST_LEA_TEX,
-       HW_FEATURE_LINEAR_FILTER_FLOAT,
-       HW_FEATURE_WORKGROUP_ROUND_MULTIPLE_OF_4,
-       HW_FEATURE_IMAGES_IN_FRAGMENT_SHADERS,
-       HW_FEATURE_TEST4_DATUM_MODE,
-       HW_FEATURE_NEXT_INSTRUCTION_TYPE,
-       HW_FEATURE_BRNDOUT_KILL,
-       HW_FEATURE_WARPING,
        HW_FEATURE_V4,
        HW_FEATURE_FLUSH_REDUCTION,
        HW_FEATURE_PROTECTED_MODE,
@@ -42,128 +24,31 @@ enum panfrost_hw_feature {
 };
 
 #define hw_features_t600 (\
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
        BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \
        BIT_ULL(HW_FEATURE_V4))
 
-#define hw_features_t620 (\
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \
-       BIT_ULL(HW_FEATURE_V4))
-
-#define hw_features_t720 (\
-       BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \
-       BIT_ULL(HW_FEATURE_OPTIMIZED_COVERAGE_MASK) | \
-       BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \
-       BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \
-       BIT_ULL(HW_FEATURE_WORKGROUP_ROUND_MULTIPLE_OF_4) | \
-       BIT_ULL(HW_FEATURE_WARPING) | \
-       BIT_ULL(HW_FEATURE_V4))
+#define hw_features_t620 hw_features_t600
 
+#define hw_features_t720 hw_features_t600
 
 #define hw_features_t760 (\
        BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \
        BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \
        BIT_ULL(HW_FEATURE_XAFFINITY) | \
-       BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
-       BIT_ULL(HW_FEATURE_MRT) | \
-       BIT_ULL(HW_FEATURE_MSAA_16X) | \
-       BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \
-       BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \
-       BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \
        BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT))
 
-// T860
-#define hw_features_t860 (\
-       BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \
-       BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \
-       BIT_ULL(HW_FEATURE_XAFFINITY) | \
-       BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
-       BIT_ULL(HW_FEATURE_MRT) | \
-       BIT_ULL(HW_FEATURE_MSAA_16X) | \
-       BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \
-       BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \
-       BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \
-       BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \
-       BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT))
+#define hw_features_t860 hw_features_t760
 
-#define hw_features_t880 hw_features_t860
+#define hw_features_t880 hw_features_t760
 
-#define hw_features_t830 (\
-       BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \
-       BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \
-       BIT_ULL(HW_FEATURE_XAFFINITY) | \
-       BIT_ULL(HW_FEATURE_WARPING) | \
-       BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \
-       BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
-       BIT_ULL(HW_FEATURE_MRT) | \
-       BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \
-       BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \
-       BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \
-       BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \
-       BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT))
+#define hw_features_t830 hw_features_t760
 
-#define hw_features_t820 (\
-       BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \
-       BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \
-       BIT_ULL(HW_FEATURE_XAFFINITY) | \
-       BIT_ULL(HW_FEATURE_WARPING) | \
-       BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \
-       BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
-       BIT_ULL(HW_FEATURE_MRT) | \
-       BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \
-       BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \
-       BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \
-       BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \
-       BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT))
+#define hw_features_t820 hw_features_t760
 
 #define hw_features_g71 (\
        BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \
        BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \
        BIT_ULL(HW_FEATURE_XAFFINITY) | \
-       BIT_ULL(HW_FEATURE_WARPING) | \
-       BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \
-       BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
-       BIT_ULL(HW_FEATURE_MRT) | \
-       BIT_ULL(HW_FEATURE_MSAA_16X) | \
-       BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \
-       BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \
-       BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \
-       BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \
        BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \
        BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \
        BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \
@@ -173,71 +58,18 @@ enum panfrost_hw_feature {
        BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \
        BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \
        BIT_ULL(HW_FEATURE_XAFFINITY) | \
-       BIT_ULL(HW_FEATURE_WARPING) | \
-       BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \
-       BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
-       BIT_ULL(HW_FEATURE_MRT) | \
-       BIT_ULL(HW_FEATURE_MSAA_16X) | \
-       BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \
-       BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \
-       BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \
-       BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \
        BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \
        BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \
        BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \
        BIT_ULL(HW_FEATURE_PROTECTED_DEBUG_MODE) | \
        BIT_ULL(HW_FEATURE_COHERENCY_REG))
 
-#define hw_features_g51 (\
-       BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \
-       BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \
-       BIT_ULL(HW_FEATURE_XAFFINITY) | \
-       BIT_ULL(HW_FEATURE_WARPING) | \
-       BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \
-       BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
-       BIT_ULL(HW_FEATURE_MRT) | \
-       BIT_ULL(HW_FEATURE_MSAA_16X) | \
-       BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \
-       BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \
-       BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \
-       BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \
-       BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \
-       BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \
-       BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \
-       BIT_ULL(HW_FEATURE_PROTECTED_DEBUG_MODE) | \
-       BIT_ULL(HW_FEATURE_COHERENCY_REG))
+#define hw_features_g51 hw_features_g72
 
 #define hw_features_g52 (\
        BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \
        BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \
        BIT_ULL(HW_FEATURE_XAFFINITY) | \
-       BIT_ULL(HW_FEATURE_WARPING) | \
-       BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \
-       BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
-       BIT_ULL(HW_FEATURE_MRT) | \
-       BIT_ULL(HW_FEATURE_MSAA_16X) | \
-       BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \
-       BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \
-       BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \
-       BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \
        BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \
        BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \
        BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \
@@ -248,21 +80,6 @@ enum panfrost_hw_feature {
        BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \
        BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \
        BIT_ULL(HW_FEATURE_XAFFINITY) | \
-       BIT_ULL(HW_FEATURE_WARPING) | \
-       BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \
-       BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
-       BIT_ULL(HW_FEATURE_MRT) | \
-       BIT_ULL(HW_FEATURE_MSAA_16X) | \
-       BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \
-       BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \
-       BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \
-       BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \
        BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \
        BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \
        BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \
@@ -276,21 +93,6 @@ enum panfrost_hw_feature {
        BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \
        BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \
        BIT_ULL(HW_FEATURE_XAFFINITY) | \
-       BIT_ULL(HW_FEATURE_WARPING) | \
-       BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \
-       BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \
-       BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \
-       BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \
-       BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \
-       BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \
-       BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \
-       BIT_ULL(HW_FEATURE_MRT) | \
-       BIT_ULL(HW_FEATURE_MSAA_16X) | \
-       BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \
-       BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \
-       BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \
-       BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \
        BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \
        BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \
        BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \
index bbe628b..15cec83 100644 (file)
@@ -320,19 +320,36 @@ void panfrost_gpu_power_on(struct panfrost_device *pfdev)
 {
        int ret;
        u32 val;
+       u64 core_mask = U64_MAX;
 
        panfrost_gpu_init_quirks(pfdev);
 
-       /* Just turn on everything for now */
-       gpu_write(pfdev, L2_PWRON_LO, pfdev->features.l2_present);
+       if (pfdev->features.l2_present != 1) {
+               /*
+                * Only support one core group now.
+                * ~(l2_present - 1) unsets all bits in l2_present except
+                * the bottom bit. (l2_present - 2) has all the bits in
+                * the first core group set. AND them together to generate
+                * a mask of cores in the first core group.
+                */
+               core_mask = ~(pfdev->features.l2_present - 1) &
+                            (pfdev->features.l2_present - 2);
+               dev_info_once(pfdev->dev, "using only 1st core group (%lu cores from %lu)\n",
+                             hweight64(core_mask),
+                             hweight64(pfdev->features.shader_present));
+       }
+       gpu_write(pfdev, L2_PWRON_LO, pfdev->features.l2_present & core_mask);
        ret = readl_relaxed_poll_timeout(pfdev->iomem + L2_READY_LO,
-               val, val == pfdev->features.l2_present, 100, 20000);
+               val, val == (pfdev->features.l2_present & core_mask),
+               100, 20000);
        if (ret)
                dev_err(pfdev->dev, "error powering up gpu L2");
 
-       gpu_write(pfdev, SHADER_PWRON_LO, pfdev->features.shader_present);
+       gpu_write(pfdev, SHADER_PWRON_LO,
+                 pfdev->features.shader_present & core_mask);
        ret = readl_relaxed_poll_timeout(pfdev->iomem + SHADER_READY_LO,
-               val, val == pfdev->features.shader_present, 100, 20000);
+               val, val == (pfdev->features.shader_present & core_mask),
+               100, 20000);
        if (ret)
                dev_err(pfdev->dev, "error powering up gpu shader");
 
@@ -360,8 +377,11 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
 
        panfrost_gpu_init_features(pfdev);
 
-       dma_set_mask_and_coherent(pfdev->dev,
+       err = dma_set_mask_and_coherent(pfdev->dev,
                DMA_BIT_MASK(FIELD_GET(0xff00, pfdev->features.mmu_features)));
+       if (err)
+               return err;
+
        dma_set_max_seg_size(pfdev->dev, UINT_MAX);
 
        irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu");
index 4c1e551..4798cf2 100644 (file)
@@ -30,7 +30,7 @@
 
 #include "atom.h"
 #include "atom-bits.h"
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 /* move these to drm_dp_helper.c/h */
 #define DP_LINK_CONFIGURATION_SIZE 9
index 607ad56..a7925a8 100644 (file)
@@ -27,7 +27,7 @@
 #include <drm/drm_edid.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
-#include <drm/drm_dp_mst_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/radeon_drm.h>
 #include "radeon.h"
@@ -204,7 +204,7 @@ int radeon_get_monitor_bpc(struct drm_connector *connector)
 
                        /* Check if bpc is within clock limit. Try to degrade gracefully otherwise */
                        if ((bpc == 12) && (mode_clock * 3/2 > max_tmds_clock)) {
-                               if ((connector->display_info.edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) &&
+                               if ((connector->display_info.edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30) &&
                                        (mode_clock * 5/4 <= max_tmds_clock))
                                        bpc = 10;
                                else
index 751c2c0..9f26baf 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: MIT
 
-#include <drm/drm_dp_mst_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_file.h>
 #include <drm/drm_probe_helper.h>
index fe16f14..5288dc7 100644 (file)
@@ -33,8 +33,8 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
-#include <drm/drm_dp_helper.h>
-#include <drm/drm_dp_mst_helper.h>
+#include <drm/dp/drm_dp_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
 #include <drm/drm_fixed.h>
 #include <drm/drm_crtc_helper.h>
 #include <linux/i2c.h>
index 11b21d6..0d1283c 100644 (file)
@@ -802,7 +802,7 @@ static int radeon_mm_vram_dump_table_show(struct seq_file *m, void *unused)
                                                            TTM_PL_VRAM);
        struct drm_printer p = drm_seq_file_printer(m);
 
-       man->func->debug(man, &p);
+       ttm_resource_manager_debug(man, &p);
        return 0;
 }
 
@@ -820,7 +820,7 @@ static int radeon_mm_gtt_dump_table_show(struct seq_file *m, void *unused)
                                                            TTM_PL_TT);
        struct drm_printer p = drm_seq_file_printer(m);
 
-       man->func->debug(man, &p);
+       ttm_resource_manager_debug(man, &p);
        return 0;
 }
 
index 9f1ecef..fa5cfda 100644 (file)
@@ -2,11 +2,13 @@
 config DRM_ROCKCHIP
        tristate "DRM Support for Rockchip"
        depends on DRM && ROCKCHIP_IOMMU
+       select DRM_DP_HELPER
        select DRM_GEM_CMA_HELPER
        select DRM_KMS_HELPER
        select DRM_PANEL
        select VIDEOMODE_HELPERS
        select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP
+       select DRM_DP_HELPER if ROCKCHIP_ANALOGIX_DP
        select DRM_DW_HDMI if ROCKCHIP_DW_HDMI
        select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
        select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI
index 8abb5ac..c82901d 100644 (file)
@@ -22,7 +22,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/bridge/analogix_dp.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
@@ -117,7 +117,7 @@ static int rockchip_dp_get_modes(struct analogix_dp_plat_data *plat_data,
 {
        struct drm_display_info *di = &connector->display_info;
        /* VOP couldn't output YUV video format for eDP rightly */
-       u32 mask = DRM_COLOR_FORMAT_YCRCB444 | DRM_COLOR_FORMAT_YCRCB422;
+       u32 mask = DRM_COLOR_FORMAT_YCBCR444 | DRM_COLOR_FORMAT_YCBCR422;
 
        if ((di->color_formats & mask)) {
                DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n");
index 16497c3..4740cc1 100644 (file)
@@ -16,7 +16,7 @@
 #include <sound/hdmi-codec.h>
 
 #include <drm/drm_atomic_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_of.h>
 #include <drm/drm_probe_helper.h>
index 81ac9b6..0d04414 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef _CDN_DP_CORE_H
 #define _CDN_DP_CORE_H
 
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
 
index be74c87..0b97241 100644 (file)
@@ -20,7 +20,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_bridge_connector.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
index 09be967..2494b07 100644 (file)
@@ -11,7 +11,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_bridge_connector.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
index 6b4759e..fc1deb1 100644 (file)
@@ -7,10 +7,10 @@
 
 #include <linux/random.h>
 
-#include <drm/drm_dp_mst_helper.h>
+#include <drm/dp/drm_dp_mst_helper.h>
 #include <drm/drm_print.h>
 
-#include "../drm_dp_mst_topology_internal.h"
+#include "../dp/drm_dp_mst_topology_internal.h"
 #include "test-drm_modeset_common.h"
 
 int igt_dp_mst_calc_pbn_mode(void *ignored)
@@ -131,8 +131,10 @@ sideband_msg_req_encode_decode(struct drm_dp_sideband_msg_req_body *in)
                return false;
 
        txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-       if (!txmsg)
+       if (!txmsg) {
+               kfree(out);
                return false;
+       }
 
        drm_dp_encode_sideband_req(in, txmsg);
        ret = drm_dp_decode_sideband_req(txmsg, out);
index 0a9553f..ceebeed 100644 (file)
@@ -87,11 +87,15 @@ int igt_check_plane_state(void *ignored)
                                DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
                },
        };
+       struct drm_plane plane = {
+               .dev = NULL
+       };
        struct drm_framebuffer fb = {
                .width = 2048,
                .height = 2048
        };
        struct drm_plane_state plane_state = {
+               .plane = &plane,
                .crtc = ZERO_SIZE_PTR,
                .fb = &fb,
                .rotation = DRM_MODE_ROTATE_0
index 222869b..9f441aa 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/of_platform.h>
 #include <linux/pm_runtime.h>
 
+#include <drm/drm_aperture.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
@@ -183,6 +184,10 @@ static int stm_drm_platform_probe(struct platform_device *pdev)
 
        DRM_DEBUG("%s\n", __func__);
 
+       ret = drm_aperture_remove_framebuffers(false, &drv_driver);
+       if (ret)
+               return ret;
+
        dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
 
        ddev = drm_dev_alloc(&drv_driver, dev);
index 32cb41b..89897d5 100644 (file)
@@ -247,14 +247,6 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
        int ret, bpp;
        u32 val;
 
-       /* Update lane capabilities according to hw version */
-       dsi->lane_min_kbps = LANE_MIN_KBPS;
-       dsi->lane_max_kbps = LANE_MAX_KBPS;
-       if (dsi->hw_version == HWVER_131) {
-               dsi->lane_min_kbps *= 2;
-               dsi->lane_max_kbps *= 2;
-       }
-
        pll_in_khz = (unsigned int)(clk_get_rate(dsi->pllref_clk) / 1000);
 
        /* Compute requested pll out */
@@ -330,6 +322,103 @@ dw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps,
        return 0;
 }
 
+#define CLK_TOLERANCE_HZ 50
+
+static enum drm_mode_status
+dw_mipi_dsi_stm_mode_valid(void *priv_data,
+                          const struct drm_display_mode *mode,
+                          unsigned long mode_flags, u32 lanes, u32 format)
+{
+       struct dw_mipi_dsi_stm *dsi = priv_data;
+       unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
+       int ret, bpp;
+
+       bpp = mipi_dsi_pixel_format_to_bpp(format);
+       if (bpp < 0)
+               return MODE_BAD;
+
+       /* Compute requested pll out */
+       pll_out_khz = mode->clock * bpp / lanes;
+
+       if (pll_out_khz > dsi->lane_max_kbps)
+               return MODE_CLOCK_HIGH;
+
+       if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
+               /* Add 20% to pll out to be higher than pixel bw */
+               pll_out_khz = (pll_out_khz * 12) / 10;
+       } else {
+               if (pll_out_khz < dsi->lane_min_kbps)
+                       return MODE_CLOCK_LOW;
+       }
+
+       /* Compute best pll parameters */
+       idf = 0;
+       ndiv = 0;
+       odf = 0;
+       pll_in_khz = clk_get_rate(dsi->pllref_clk) / 1000;
+       ret = dsi_pll_get_params(dsi, pll_in_khz, pll_out_khz, &idf, &ndiv, &odf);
+       if (ret) {
+               DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
+               return MODE_ERROR;
+       }
+
+       if (!(mode_flags & MIPI_DSI_MODE_VIDEO_BURST)) {
+               unsigned int px_clock_hz, target_px_clock_hz, lane_mbps;
+               int dsi_short_packet_size_px, hfp, hsync, hbp, delay_to_lp;
+               struct dw_mipi_dsi_dphy_timing dphy_timing;
+
+               /* Get the adjusted pll out value */
+               pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
+
+               px_clock_hz = DIV_ROUND_CLOSEST_ULL(1000ULL * pll_out_khz * lanes, bpp);
+               target_px_clock_hz = mode->clock * 1000;
+               /*
+                * Filter modes according to the clock value, particularly useful for
+                * hdmi modes that require precise pixel clocks.
+                */
+               if (px_clock_hz < target_px_clock_hz - CLK_TOLERANCE_HZ ||
+                   px_clock_hz > target_px_clock_hz + CLK_TOLERANCE_HZ)
+                       return MODE_CLOCK_RANGE;
+
+               /* sync packets are codes as DSI short packets (4 bytes) */
+               dsi_short_packet_size_px = DIV_ROUND_UP(4 * BITS_PER_BYTE, bpp);
+
+               hfp = mode->hsync_start - mode->hdisplay;
+               hsync = mode->hsync_end - mode->hsync_start;
+               hbp = mode->htotal - mode->hsync_end;
+
+               /* hsync must be longer than 4 bytes HSS packets */
+               if (hsync < dsi_short_packet_size_px)
+                       return MODE_HSYNC_NARROW;
+
+               if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
+                       /* HBP must be longer than 4 bytes HSE packets */
+                       if (hbp < dsi_short_packet_size_px)
+                               return MODE_HSYNC_NARROW;
+                       hbp -= dsi_short_packet_size_px;
+               } else {
+                       /* With sync events HBP extends in the hsync */
+                       hbp += hsync - dsi_short_packet_size_px;
+               }
+
+               lane_mbps = pll_out_khz / 1000;
+               ret = dw_mipi_dsi_phy_get_timing(priv_data, lane_mbps, &dphy_timing);
+               if (ret)
+                       return MODE_ERROR;
+               /*
+                * In non-burst mode DSI has to enter in LP during HFP
+                * (horizontal front porch) or HBP (horizontal back porch) to
+                * resync with LTDC pixel clock.
+                */
+               delay_to_lp = DIV_ROUND_UP((dphy_timing.data_hs2lp + dphy_timing.data_lp2hs) *
+                                          lanes * BITS_PER_BYTE, bpp);
+               if (hfp < delay_to_lp && hbp < delay_to_lp)
+                       return MODE_HSYNC;
+       }
+
+       return MODE_OK;
+}
+
 static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = {
        .init = dw_mipi_dsi_phy_init,
        .power_on = dw_mipi_dsi_phy_power_on,
@@ -340,6 +429,7 @@ static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = {
 
 static struct dw_mipi_dsi_plat_data dw_mipi_dsi_stm_plat_data = {
        .max_data_lanes = 2,
+       .mode_valid = dw_mipi_dsi_stm_mode_valid,
        .phy_ops = &dw_mipi_dsi_stm_phy_ops,
 };
 
@@ -417,6 +507,14 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
                goto err_dsi_probe;
        }
 
+       /* set lane capabilities according to hw version */
+       dsi->lane_min_kbps = LANE_MIN_KBPS;
+       dsi->lane_max_kbps = LANE_MAX_KBPS;
+       if (dsi->hw_version == HWVER_131) {
+               dsi->lane_min_kbps *= 2;
+               dsi->lane_max_kbps *= 2;
+       }
+
        dw_mipi_dsi_stm_plat_data.base = dsi->base;
        dw_mipi_dsi_stm_plat_data.priv_data = dsi;
 
index dbdee95..5eeb32c 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/regmap.h>
 #include <linux/reset.h>
 
 #include <drm/drm_atomic.h>
 #define HWVER_10200 0x010200
 #define HWVER_10300 0x010300
 #define HWVER_20101 0x020101
+#define HWVER_40100 0x040100
 
 /*
  * The address of some registers depends on the HW version: such registers have
- * an extra offset specified with reg_ofs.
+ * an extra offset specified with layer_ofs.
  */
-#define REG_OFS_NONE   0
-#define REG_OFS_4      4               /* Insertion of "Layer Conf. 2" reg */
-#define REG_OFS                (ldev->caps.reg_ofs)
-#define LAY_OFS                0x80            /* Register Offset between 2 layers */
+#define LAY_OFS_0      0x80
+#define LAY_OFS_1      0x100
+#define LAY_OFS        (ldev->caps.layer_ofs)
 
 /* Global register offsets */
 #define LTDC_IDR       0x0000          /* IDentification */
 #define LTDC_LIPCR     0x0040          /* Line Interrupt Position Conf. */
 #define LTDC_CPSR      0x0044          /* Current Position Status */
 #define LTDC_CDSR      0x0048          /* Current Display Status */
+#define LTDC_EDCR      0x0060          /* External Display Control */
+#define LTDC_FUT       0x0090          /* Fifo underrun Threshold */
 
 /* Layer register offsets */
-#define LTDC_L1LC1R    (0x80)          /* L1 Layer Configuration 1 */
-#define LTDC_L1LC2R    (0x84)          /* L1 Layer Configuration 2 */
-#define LTDC_L1CR      (0x84 + REG_OFS)/* L1 Control */
-#define LTDC_L1WHPCR   (0x88 + REG_OFS)/* L1 Window Hor Position Config */
-#define LTDC_L1WVPCR   (0x8C + REG_OFS)/* L1 Window Vert Position Config */
-#define LTDC_L1CKCR    (0x90 + REG_OFS)/* L1 Color Keying Configuration */
-#define LTDC_L1PFCR    (0x94 + REG_OFS)/* L1 Pixel Format Configuration */
-#define LTDC_L1CACR    (0x98 + REG_OFS)/* L1 Constant Alpha Config */
-#define LTDC_L1DCCR    (0x9C + REG_OFS)/* L1 Default Color Configuration */
-#define LTDC_L1BFCR    (0xA0 + REG_OFS)/* L1 Blend Factors Configuration */
-#define LTDC_L1FBBCR   (0xA4 + REG_OFS)/* L1 FrameBuffer Bus Control */
-#define LTDC_L1AFBCR   (0xA8 + REG_OFS)/* L1 AuxFB Control */
-#define LTDC_L1CFBAR   (0xAC + REG_OFS)/* L1 Color FrameBuffer Address */
-#define LTDC_L1CFBLR   (0xB0 + REG_OFS)/* L1 Color FrameBuffer Length */
-#define LTDC_L1CFBLNR  (0xB4 + REG_OFS)/* L1 Color FrameBuffer Line Nb */
-#define LTDC_L1AFBAR   (0xB8 + REG_OFS)/* L1 AuxFB Address */
-#define LTDC_L1AFBLR   (0xBC + REG_OFS)/* L1 AuxFB Length */
-#define LTDC_L1AFBLNR  (0xC0 + REG_OFS)/* L1 AuxFB Line Number */
-#define LTDC_L1CLUTWR  (0xC4 + REG_OFS)/* L1 CLUT Write */
-#define LTDC_L1YS1R    (0xE0 + REG_OFS)/* L1 YCbCr Scale 1 */
-#define LTDC_L1YS2R    (0xE4 + REG_OFS)/* L1 YCbCr Scale 2 */
+#define LTDC_L1C0R     (ldev->caps.layer_regs[0])      /* L1 configuration 0 */
+#define LTDC_L1C1R     (ldev->caps.layer_regs[1])      /* L1 configuration 1 */
+#define LTDC_L1RCR     (ldev->caps.layer_regs[2])      /* L1 reload control */
+#define LTDC_L1CR      (ldev->caps.layer_regs[3])      /* L1 control register */
+#define LTDC_L1WHPCR   (ldev->caps.layer_regs[4])      /* L1 window horizontal position configuration */
+#define LTDC_L1WVPCR   (ldev->caps.layer_regs[5])      /* L1 window vertical position configuration */
+#define LTDC_L1CKCR    (ldev->caps.layer_regs[6])      /* L1 color keying configuration */
+#define LTDC_L1PFCR    (ldev->caps.layer_regs[7])      /* L1 pixel format configuration */
+#define LTDC_L1CACR    (ldev->caps.layer_regs[8])      /* L1 constant alpha configuration */
+#define LTDC_L1DCCR    (ldev->caps.layer_regs[9])      /* L1 default color configuration */
+#define LTDC_L1BFCR    (ldev->caps.layer_regs[10])     /* L1 blending factors configuration */
+#define LTDC_L1BLCR    (ldev->caps.layer_regs[11])     /* L1 burst length configuration */
+#define LTDC_L1PCR     (ldev->caps.layer_regs[12])     /* L1 planar configuration */
+#define LTDC_L1CFBAR   (ldev->caps.layer_regs[13])     /* L1 color frame buffer address */
+#define LTDC_L1CFBLR   (ldev->caps.layer_regs[14])     /* L1 color frame buffer length */
+#define LTDC_L1CFBLNR  (ldev->caps.layer_regs[15])     /* L1 color frame buffer line number */
+#define LTDC_L1AFBA0R  (ldev->caps.layer_regs[16])     /* L1 auxiliary frame buffer address 0 */
+#define LTDC_L1AFBA1R  (ldev->caps.layer_regs[17])     /* L1 auxiliary frame buffer address 1 */
+#define LTDC_L1AFBLR   (ldev->caps.layer_regs[18])     /* L1 auxiliary frame buffer length */
+#define LTDC_L1AFBLNR  (ldev->caps.layer_regs[19])     /* L1 auxiliary frame buffer line number */
+#define LTDC_L1CLUTWR  (ldev->caps.layer_regs[20])     /* L1 CLUT write */
+#define LTDC_L1CYR0R   (ldev->caps.layer_regs[21])     /* L1 Conversion YCbCr RGB 0 */
+#define LTDC_L1CYR1R   (ldev->caps.layer_regs[22])     /* L1 Conversion YCbCr RGB 1 */
+#define LTDC_L1FPF0R   (ldev->caps.layer_regs[23])     /* L1 Flexible Pixel Format 0 */
+#define LTDC_L1FPF1R   (ldev->caps.layer_regs[24])     /* L1 Flexible Pixel Format 1 */
 
 /* Bit definitions */
 #define SSCR_VSH       GENMASK(10, 0)  /* Vertical Synchronization Height */
 #define ISR_TERRIF     BIT(2)          /* Transfer ERRor Interrupt Flag */
 #define ISR_RRIF       BIT(3)          /* Register Reload Interrupt Flag */
 
+#define EDCR_OCYEN     BIT(25)         /* Output Conversion to YCbCr 422: ENable */
+#define EDCR_OCYSEL    BIT(26)         /* Output Conversion to YCbCr 422: SELection of the CCIR */
+#define EDCR_OCYCO     BIT(27)         /* Output Conversion to YCbCr 422: Chrominance Order */
+
 #define LXCR_LEN       BIT(0)          /* Layer ENable */
 #define LXCR_COLKEN    BIT(1)          /* Color Keying Enable */
 #define LXCR_CLUTEN    BIT(4)          /* Color Look-Up Table ENable */
 #define LXWVPCR_WVSPPOS        GENMASK(26, 16) /* Window Vertical StoP POSition */
 
 #define LXPFCR_PF      GENMASK(2, 0)   /* Pixel Format */
+#define PF_FLEXIBLE    0x7             /* Flexible Pixel Format selected */
 
 #define LXCACR_CONSTA  GENMASK(7, 0)   /* CONSTant Alpha */
 
 
 #define LXCFBLNR_CFBLN GENMASK(10, 0)  /* Color Frame Buffer Line Number */
 
+#define LXCR_C1R_YIA   BIT(0)          /* Ycbcr 422 Interleaved Ability */
+#define LXCR_C1R_YSPA  BIT(1)          /* Ycbcr 420 Semi-Planar Ability */
+#define LXCR_C1R_YFPA  BIT(2)          /* Ycbcr 420 Full-Planar Ability */
+#define LXCR_C1R_SCA   BIT(31)         /* SCaling Ability*/
+
+#define LxPCR_YREN     BIT(9)          /* Y Rescale Enable for the color dynamic range */
+#define LxPCR_OF       BIT(8)          /* Odd pixel First */
+#define LxPCR_CBF      BIT(7)          /* CB component First */
+#define LxPCR_YF       BIT(6)          /* Y component First */
+#define LxPCR_YCM      GENMASK(5, 4)   /* Ycbcr Conversion Mode */
+#define YCM_I          0x0             /* Interleaved 422 */
+#define YCM_SP         0x1             /* Semi-Planar 420 */
+#define YCM_FP         0x2             /* Full-Planar 420 */
+#define LxPCR_YCEN     BIT(3)          /* YCbCr-to-RGB Conversion Enable */
+
+#define LXRCR_IMR      BIT(0)          /* IMmediate Reload */
+#define LXRCR_VBR      BIT(1)          /* Vertical Blanking Reload */
+#define LXRCR_GRMSK    BIT(2)          /* Global (centralized) Reload MaSKed */
+
 #define CLUT_SIZE      256
 
 #define CONSTA_MAX     0xFF            /* CONSTant Alpha MAX= 1.0 */
@@ -201,8 +232,12 @@ enum ltdc_pix_fmt {
        /* RGB formats */
        PF_ARGB8888,            /* ARGB [32 bits] */
        PF_RGBA8888,            /* RGBA [32 bits] */
+       PF_ABGR8888,            /* ABGR [32 bits] */
+       PF_BGRA8888,            /* BGRA [32 bits] */
        PF_RGB888,              /* RGB [24 bits] */
+       PF_BGR888,              /* BGR [24 bits] */
        PF_RGB565,              /* RGB [16 bits] */
+       PF_BGR565,              /* BGR [16 bits] */
        PF_ARGB1555,            /* ARGB A:1 bit RGB:15 bits [16 bits] */
        PF_ARGB4444,            /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */
        /* Indexed formats */
@@ -234,36 +269,198 @@ static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = {
        PF_ARGB4444             /* 0x07 */
 };
 
-static const u64 ltdc_format_modifiers[] = {
-       DRM_FORMAT_MOD_LINEAR,
-       DRM_FORMAT_MOD_INVALID
+static const enum ltdc_pix_fmt ltdc_pix_fmt_a2[NB_PF] = {
+       PF_ARGB8888,            /* 0x00 */
+       PF_ABGR8888,            /* 0x01 */
+       PF_RGBA8888,            /* 0x02 */
+       PF_BGRA8888,            /* 0x03 */
+       PF_RGB565,              /* 0x04 */
+       PF_BGR565,              /* 0x05 */
+       PF_RGB888,              /* 0x06 */
+       PF_NONE                 /* 0x07 */
 };
 
-static inline u32 reg_read(void __iomem *base, u32 reg)
-{
-       return readl_relaxed(base + reg);
-}
+static const u32 ltdc_drm_fmt_a0[] = {
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_ARGB4444,
+       DRM_FORMAT_XRGB4444,
+       DRM_FORMAT_C8
+};
 
-static inline void reg_write(void __iomem *base, u32 reg, u32 val)
-{
-       writel_relaxed(val, base + reg);
-}
+static const u32 ltdc_drm_fmt_a1[] = {
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_RGBA8888,
+       DRM_FORMAT_RGBX8888,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_ARGB4444,
+       DRM_FORMAT_XRGB4444,
+       DRM_FORMAT_C8
+};
 
-static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
-{
-       reg_write(base, reg, reg_read(base, reg) | mask);
-}
+static const u32 ltdc_drm_fmt_a2[] = {
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_ABGR8888,
+       DRM_FORMAT_XBGR8888,
+       DRM_FORMAT_RGBA8888,
+       DRM_FORMAT_RGBX8888,
+       DRM_FORMAT_BGRA8888,
+       DRM_FORMAT_BGRX8888,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_BGR565,
+       DRM_FORMAT_RGB888,
+       DRM_FORMAT_BGR888,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_ARGB4444,
+       DRM_FORMAT_XRGB4444,
+       DRM_FORMAT_C8
+};
 
-static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
-{
-       reg_write(base, reg, reg_read(base, reg) & ~mask);
-}
+static const u32 ltdc_drm_fmt_ycbcr_cp[] = {
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_YVYU,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_VYUY
+};
 
-static inline void reg_update_bits(void __iomem *base, u32 reg, u32 mask,
-                                  u32 val)
-{
-       reg_write(base, reg, (reg_read(base, reg) & ~mask) | val);
-}
+static const u32 ltdc_drm_fmt_ycbcr_sp[] = {
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV21
+};
+
+static const u32 ltdc_drm_fmt_ycbcr_fp[] = {
+       DRM_FORMAT_YUV420,
+       DRM_FORMAT_YVU420
+};
+
+/* Layer register offsets */
+static const u32 ltdc_layer_regs_a0[] = {
+       0x80,   /* L1 configuration 0 */
+       0x00,   /* not available */
+       0x00,   /* not available */
+       0x84,   /* L1 control register */
+       0x88,   /* L1 window horizontal position configuration */
+       0x8c,   /* L1 window vertical position configuration */
+       0x90,   /* L1 color keying configuration */
+       0x94,   /* L1 pixel format configuration */
+       0x98,   /* L1 constant alpha configuration */
+       0x9c,   /* L1 default color configuration */
+       0xa0,   /* L1 blending factors configuration */
+       0x00,   /* not available */
+       0x00,   /* not available */
+       0xac,   /* L1 color frame buffer address */
+       0xb0,   /* L1 color frame buffer length */
+       0xb4,   /* L1 color frame buffer line number */
+       0x00,   /* not available */
+       0x00,   /* not available */
+       0x00,   /* not available */
+       0x00,   /* not available */
+       0xc4,   /* L1 CLUT write */
+       0x00,   /* not available */
+       0x00,   /* not available */
+       0x00,   /* not available */
+       0x00    /* not available */
+};
+
+static const u32 ltdc_layer_regs_a1[] = {
+       0x80,   /* L1 configuration 0 */
+       0x84,   /* L1 configuration 1 */
+       0x00,   /* L1 reload control */
+       0x88,   /* L1 control register */
+       0x8c,   /* L1 window horizontal position configuration */
+       0x90,   /* L1 window vertical position configuration */
+       0x94,   /* L1 color keying configuration */
+       0x98,   /* L1 pixel format configuration */
+       0x9c,   /* L1 constant alpha configuration */
+       0xa0,   /* L1 default color configuration */
+       0xa4,   /* L1 blending factors configuration */
+       0xa8,   /* L1 burst length configuration */
+       0x00,   /* not available */
+       0xac,   /* L1 color frame buffer address */
+       0xb0,   /* L1 color frame buffer length */
+       0xb4,   /* L1 color frame buffer line number */
+       0xb8,   /* L1 auxiliary frame buffer address 0 */
+       0xbc,   /* L1 auxiliary frame buffer address 1 */
+       0xc0,   /* L1 auxiliary frame buffer length */
+       0xc4,   /* L1 auxiliary frame buffer line number */
+       0xc8,   /* L1 CLUT write */
+       0x00,   /* not available */
+       0x00,   /* not available */
+       0x00,   /* not available */
+       0x00    /* not available */
+};
+
+static const u32 ltdc_layer_regs_a2[] = {
+       0x100,  /* L1 configuration 0 */
+       0x104,  /* L1 configuration 1 */
+       0x108,  /* L1 reload control */
+       0x10c,  /* L1 control register */
+       0x110,  /* L1 window horizontal position configuration */
+       0x114,  /* L1 window vertical position configuration */
+       0x118,  /* L1 color keying configuration */
+       0x11c,  /* L1 pixel format configuration */
+       0x120,  /* L1 constant alpha configuration */
+       0x124,  /* L1 default color configuration */
+       0x128,  /* L1 blending factors configuration */
+       0x12c,  /* L1 burst length configuration */
+       0x130,  /* L1 planar configuration */
+       0x134,  /* L1 color frame buffer address */
+       0x138,  /* L1 color frame buffer length */
+       0x13c,  /* L1 color frame buffer line number */
+       0x140,  /* L1 auxiliary frame buffer address 0 */
+       0x144,  /* L1 auxiliary frame buffer address 1 */
+       0x148,  /* L1 auxiliary frame buffer length */
+       0x14c,  /* L1 auxiliary frame buffer line number */
+       0x150,  /* L1 CLUT write */
+       0x16c,  /* L1 Conversion YCbCr RGB 0 */
+       0x170,  /* L1 Conversion YCbCr RGB 1 */
+       0x174,  /* L1 Flexible Pixel Format 0 */
+       0x178   /* L1 Flexible Pixel Format 1 */
+};
+
+static const u64 ltdc_format_modifiers[] = {
+       DRM_FORMAT_MOD_LINEAR,
+       DRM_FORMAT_MOD_INVALID
+};
+
+static const struct regmap_config stm32_ltdc_regmap_cfg = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = sizeof(u32),
+       .max_register = 0x400,
+       .use_relaxed_mmio = true,
+       .cache_type = REGCACHE_NONE,
+};
+
+static const u32 ltdc_ycbcr2rgb_coeffs[DRM_COLOR_ENCODING_MAX][DRM_COLOR_RANGE_MAX][2] = {
+       [DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
+               0x02040199,     /* (b_cb = 516 / r_cr = 409) */
+               0x006400D0      /* (g_cb = 100 / g_cr = 208) */
+       },
+       [DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_FULL_RANGE] = {
+               0x01C60167,     /* (b_cb = 454 / r_cr = 359) */
+               0x005800B7      /* (g_cb = 88 / g_cr = 183) */
+       },
+       [DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
+               0x021D01CB,     /* (b_cb = 541 / r_cr = 459) */
+               0x00370089      /* (g_cb = 55 / g_cr = 137) */
+       },
+       [DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_FULL_RANGE] = {
+               0x01DB0193,     /* (b_cb = 475 / r_cr = 403) */
+               0x00300078      /* (g_cb = 48 / g_cr = 120) */
+       }
+       /* BT2020 not supported */
+};
 
 static inline struct ltdc_device *crtc_to_ltdc(struct drm_crtc *crtc)
 {
@@ -289,16 +486,30 @@ static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
        case DRM_FORMAT_XRGB8888:
                pf = PF_ARGB8888;
                break;
+       case DRM_FORMAT_ABGR8888:
+       case DRM_FORMAT_XBGR8888:
+               pf = PF_ABGR8888;
+               break;
        case DRM_FORMAT_RGBA8888:
        case DRM_FORMAT_RGBX8888:
                pf = PF_RGBA8888;
                break;
+       case DRM_FORMAT_BGRA8888:
+       case DRM_FORMAT_BGRX8888:
+               pf = PF_BGRA8888;
+               break;
        case DRM_FORMAT_RGB888:
                pf = PF_RGB888;
                break;
+       case DRM_FORMAT_BGR888:
+               pf = PF_BGR888;
+               break;
        case DRM_FORMAT_RGB565:
                pf = PF_RGB565;
                break;
+       case DRM_FORMAT_BGR565:
+               pf = PF_BGR565;
+               break;
        case DRM_FORMAT_ARGB1555:
        case DRM_FORMAT_XRGB1555:
                pf = PF_ARGB1555;
@@ -319,49 +530,138 @@ static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
        return pf;
 }
 
-static inline u32 to_drm_pixelformat(enum ltdc_pix_fmt pf)
+static inline u32 ltdc_set_flexible_pixel_format(struct drm_plane *plane, enum ltdc_pix_fmt pix_fmt)
 {
-       switch (pf) {
-       case PF_ARGB8888:
-               return DRM_FORMAT_ARGB8888;
-       case PF_RGBA8888:
-               return DRM_FORMAT_RGBA8888;
-       case PF_RGB888:
-               return DRM_FORMAT_RGB888;
-       case PF_RGB565:
-               return DRM_FORMAT_RGB565;
+       struct ltdc_device *ldev = plane_to_ltdc(plane);
+       u32 lofs = plane->index * LAY_OFS, ret = PF_FLEXIBLE;
+       int psize, alen, apos, rlen, rpos, glen, gpos, blen, bpos;
+
+       switch (pix_fmt) {
+       case PF_BGR888:
+               psize = 3;
+               alen = 0; apos = 0; rlen = 8; rpos = 0;
+               glen = 8; gpos = 8; blen = 8; bpos = 16;
+       break;
        case PF_ARGB1555:
-               return DRM_FORMAT_ARGB1555;
+               psize = 2;
+               alen = 1; apos = 15; rlen = 5; rpos = 10;
+               glen = 5; gpos = 5;  blen = 5; bpos = 0;
+       break;
        case PF_ARGB4444:
-               return DRM_FORMAT_ARGB4444;
+               psize = 2;
+               alen = 4; apos = 12; rlen = 4; rpos = 8;
+               glen = 4; gpos = 4; blen = 4; bpos = 0;
+       break;
        case PF_L8:
-               return DRM_FORMAT_C8;
-       case PF_AL44:           /* No DRM support */
-       case PF_AL88:           /* No DRM support */
-       case PF_NONE:
+               psize = 1;
+               alen = 0; apos = 0; rlen = 8; rpos = 0;
+               glen = 8; gpos = 0; blen = 8; bpos = 0;
+       break;
+       case PF_AL44:
+               psize = 1;
+               alen = 4; apos = 4; rlen = 4; rpos = 0;
+               glen = 4; gpos = 0; blen = 4; bpos = 0;
+       break;
+       case PF_AL88:
+               psize = 2;
+               alen = 8; apos = 8; rlen = 8; rpos = 0;
+               glen = 8; gpos = 0; blen = 8; bpos = 0;
+       break;
        default:
-               return 0;
+               ret = NB_PF; /* error case, trace msg is handled by the caller */
+       break;
        }
+
+       if (ret == PF_FLEXIBLE) {
+               regmap_write(ldev->regmap, LTDC_L1FPF0R + lofs,
+                            (rlen << 14)  + (rpos << 9) + (alen << 5) + apos);
+
+               regmap_write(ldev->regmap, LTDC_L1FPF1R + lofs,
+                            (psize << 18) + (blen << 14)  + (bpos << 9) + (glen << 5) + gpos);
+       }
+
+       return ret;
 }
 
-static inline u32 get_pixelformat_without_alpha(u32 drm)
+/*
+ * All non-alpha color formats derived from native alpha color formats are
+ * either characterized by a FourCC format code
+ */
+static inline u32 is_xrgb(u32 drm)
 {
-       switch (drm) {
-       case DRM_FORMAT_ARGB4444:
-               return DRM_FORMAT_XRGB4444;
-       case DRM_FORMAT_RGBA4444:
-               return DRM_FORMAT_RGBX4444;
-       case DRM_FORMAT_ARGB1555:
-               return DRM_FORMAT_XRGB1555;
-       case DRM_FORMAT_RGBA5551:
-               return DRM_FORMAT_RGBX5551;
-       case DRM_FORMAT_ARGB8888:
-               return DRM_FORMAT_XRGB8888;
-       case DRM_FORMAT_RGBA8888:
-               return DRM_FORMAT_RGBX8888;
+       return ((drm & 0xFF) == 'X' || ((drm >> 8) & 0xFF) == 'X');
+}
+
+static inline void ltdc_set_ycbcr_config(struct drm_plane *plane, u32 drm_pix_fmt)
+{
+       struct ltdc_device *ldev = plane_to_ltdc(plane);
+       struct drm_plane_state *state = plane->state;
+       u32 lofs = plane->index * LAY_OFS;
+       u32 val;
+
+       switch (drm_pix_fmt) {
+       case DRM_FORMAT_YUYV:
+               val = (YCM_I << 4) | LxPCR_YF | LxPCR_CBF;
+               break;
+       case DRM_FORMAT_YVYU:
+               val = (YCM_I << 4) | LxPCR_YF;
+               break;
+       case DRM_FORMAT_UYVY:
+               val = (YCM_I << 4) | LxPCR_CBF;
+               break;
+       case DRM_FORMAT_VYUY:
+               val = (YCM_I << 4);
+               break;
+       case DRM_FORMAT_NV12:
+               val = (YCM_SP << 4) | LxPCR_CBF;
+               break;
+       case DRM_FORMAT_NV21:
+               val = (YCM_SP << 4);
+               break;
+       case DRM_FORMAT_YUV420:
+       case DRM_FORMAT_YVU420:
+               val = (YCM_FP << 4);
+               break;
        default:
-               return 0;
+               /* RGB or not a YCbCr supported format */
+               break;
+       }
+
+       /* Enable limited range */
+       if (state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
+               val |= LxPCR_YREN;
+
+       /* enable ycbcr conversion */
+       val |= LxPCR_YCEN;
+
+       regmap_write(ldev->regmap, LTDC_L1PCR + lofs, val);
+}
+
+static inline void ltdc_set_ycbcr_coeffs(struct drm_plane *plane)
+{
+       struct ltdc_device *ldev = plane_to_ltdc(plane);
+       struct drm_plane_state *state = plane->state;
+       enum drm_color_encoding enc = state->color_encoding;
+       enum drm_color_range ran = state->color_range;
+       u32 lofs = plane->index * LAY_OFS;
+
+       if (enc != DRM_COLOR_YCBCR_BT601 && enc != DRM_COLOR_YCBCR_BT709) {
+               DRM_ERROR("color encoding %d not supported, use bt601 by default\n", enc);
+               /* set by default color encoding to DRM_COLOR_YCBCR_BT601 */
+               enc = DRM_COLOR_YCBCR_BT601;
+       }
+
+       if (ran != DRM_COLOR_YCBCR_LIMITED_RANGE && ran != DRM_COLOR_YCBCR_FULL_RANGE) {
+               DRM_ERROR("color range %d not supported, use limited range by default\n", ran);
+               /* set by default color range to DRM_COLOR_YCBCR_LIMITED_RANGE */
+               ran = DRM_COLOR_YCBCR_LIMITED_RANGE;
        }
+
+       DRM_DEBUG_DRIVER("Color encoding=%d, range=%d\n", enc, ran);
+       regmap_write(ldev->regmap, LTDC_L1CYR0R + lofs,
+                    ltdc_ycbcr2rgb_coeffs[enc][ran][0]);
+       regmap_write(ldev->regmap, LTDC_L1CYR1R + lofs,
+                    ltdc_ycbcr2rgb_coeffs[enc][ran][1]);
 }
 
 static irqreturn_t ltdc_irq_thread(int irq, void *arg)
@@ -390,9 +690,13 @@ static irqreturn_t ltdc_irq(int irq, void *arg)
        struct drm_device *ddev = arg;
        struct ltdc_device *ldev = ddev->dev_private;
 
-       /* Read & Clear the interrupt status */
-       ldev->irq_status = reg_read(ldev->regs, LTDC_ISR);
-       reg_write(ldev->regs, LTDC_ICR, ldev->irq_status);
+       /*
+        *  Read & Clear the interrupt status
+        *  In order to write / read registers in this critical section
+        *  very quickly, the regmap functions are not used.
+        */
+       ldev->irq_status = readl_relaxed(ldev->regs + LTDC_ISR);
+       writel_relaxed(ldev->irq_status, ldev->regs + LTDC_ICR);
 
        return IRQ_WAKE_THREAD;
 }
@@ -416,7 +720,7 @@ static void ltdc_crtc_update_clut(struct drm_crtc *crtc)
        for (i = 0; i < CLUT_SIZE; i++, lut++) {
                val = ((lut->red << 8) & 0xff0000) | (lut->green & 0xff00) |
                        (lut->blue >> 8) | (i << 24);
-               reg_write(ldev->regs, LTDC_L1CLUTWR, val);
+               regmap_write(ldev->regmap, LTDC_L1CLUTWR, val);
        }
 }
 
@@ -431,13 +735,14 @@ static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
        pm_runtime_get_sync(ddev->dev);
 
        /* Sets the background color value */
-       reg_write(ldev->regs, LTDC_BCCR, BCCR_BCBLACK);
+       regmap_write(ldev->regmap, LTDC_BCCR, BCCR_BCBLACK);
 
        /* Enable IRQ */
-       reg_set(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+       regmap_set_bits(ldev->regmap, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
 
        /* Commit shadow registers = update planes at next vblank */
-       reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR);
+       if (!ldev->caps.plane_reg_shadow)
+               regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
 
        drm_crtc_vblank_on(crtc);
 }
@@ -453,10 +758,11 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
        drm_crtc_vblank_off(crtc);
 
        /* disable IRQ */
-       reg_clear(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+       regmap_clear_bits(ldev->regmap, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
 
        /* immediately commit disable of layers before switching off LTDC */
-       reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
+       if (!ldev->caps.plane_reg_shadow)
+               regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_IMR);
 
        pm_runtime_put_sync(ddev->dev);
 }
@@ -533,6 +839,7 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
        struct drm_display_mode *mode = &crtc->state->adjusted_mode;
        u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
        u32 total_width, total_height;
+       u32 bus_formats = MEDIA_BUS_FMT_RGB888_1X24;
        u32 bus_flags = 0;
        u32 val;
        int ret;
@@ -558,8 +865,11 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
 
        if (bridge && bridge->timings)
                bus_flags = bridge->timings->input_bus_flags;
-       else if (connector)
+       else if (connector) {
                bus_flags = connector->display_info.bus_flags;
+               if (connector->display_info.num_bus_formats)
+                       bus_formats = connector->display_info.bus_formats[0];
+       }
 
        if (!pm_runtime_active(ddev->dev)) {
                ret = pm_runtime_get_sync(ddev->dev);
@@ -604,26 +914,56 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
        if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
                val |= GCR_PCPOL;
 
-       reg_update_bits(ldev->regs, LTDC_GCR,
-                       GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
+       regmap_update_bits(ldev->regmap, LTDC_GCR,
+                          GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
 
        /* Set Synchronization size */
        val = (hsync << 16) | vsync;
-       reg_update_bits(ldev->regs, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
+       regmap_update_bits(ldev->regmap, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
 
        /* Set Accumulated Back porch */
        val = (accum_hbp << 16) | accum_vbp;
-       reg_update_bits(ldev->regs, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
+       regmap_update_bits(ldev->regmap, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
 
        /* Set Accumulated Active Width */
        val = (accum_act_w << 16) | accum_act_h;
-       reg_update_bits(ldev->regs, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
+       regmap_update_bits(ldev->regmap, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
 
        /* Set total width & height */
        val = (total_width << 16) | total_height;
-       reg_update_bits(ldev->regs, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
+       regmap_update_bits(ldev->regmap, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
 
-       reg_write(ldev->regs, LTDC_LIPCR, (accum_act_h + 1));
+       regmap_write(ldev->regmap, LTDC_LIPCR, (accum_act_h + 1));
+
+       /* Configure the output format (hw version dependent) */
+       if (ldev->caps.ycbcr_output) {
+               /* Input video dynamic_range & colorimetry */
+               int vic = drm_match_cea_mode(mode);
+               u32 val;
+
+               if (vic == 6 || vic == 7 || vic == 21 || vic == 22 ||
+                   vic == 2 || vic == 3 || vic == 17 || vic == 18)
+                       /* ITU-R BT.601 */
+                       val = 0;
+               else
+                       /* ITU-R BT.709 */
+                       val = EDCR_OCYSEL;
+
+               switch (bus_formats) {
+               case MEDIA_BUS_FMT_YUYV8_1X16:
+                       /* enable ycbcr output converter */
+                       regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | val);
+                       break;
+               case MEDIA_BUS_FMT_YVYU8_1X16:
+                       /* enable ycbcr output converter & invert chrominance order */
+                       regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | EDCR_OCYCO | val);
+                       break;
+               default:
+                       /* disable ycbcr output converter */
+                       regmap_write(ldev->regmap, LTDC_EDCR, 0);
+                       break;
+               }
+       }
 }
 
 static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
@@ -638,7 +978,8 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
        ltdc_crtc_update_clut(crtc);
 
        /* Commit shadow registers = update planes at next vblank */
-       reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR);
+       if (!ldev->caps.plane_reg_shadow)
+               regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
 
        if (event) {
                crtc->state->event = NULL;
@@ -680,10 +1021,14 @@ static bool ltdc_crtc_get_scanout_position(struct drm_crtc *crtc,
         * simplify the code and only test if line > vactive_end
         */
        if (pm_runtime_active(ddev->dev)) {
-               line = reg_read(ldev->regs, LTDC_CPSR) & CPSR_CYPOS;
-               vactive_start = reg_read(ldev->regs, LTDC_BPCR) & BPCR_AVBP;
-               vactive_end = reg_read(ldev->regs, LTDC_AWCR) & AWCR_AAH;
-               vtotal = reg_read(ldev->regs, LTDC_TWCR) & TWCR_TOTALH;
+               regmap_read(ldev->regmap, LTDC_CPSR, &line);
+               line &= CPSR_CYPOS;
+               regmap_read(ldev->regmap, LTDC_BPCR, &vactive_start);
+               vactive_start &= BPCR_AVBP;
+               regmap_read(ldev->regmap, LTDC_AWCR, &vactive_end);
+               vactive_end &= AWCR_AAH;
+               regmap_read(ldev->regmap, LTDC_TWCR, &vtotal);
+               vtotal &= TWCR_TOTALH;
 
                if (line > vactive_end)
                        *vpos = line - vtotal - vactive_start;
@@ -719,7 +1064,7 @@ static int ltdc_crtc_enable_vblank(struct drm_crtc *crtc)
        DRM_DEBUG_DRIVER("\n");
 
        if (state->enable)
-               reg_set(ldev->regs, LTDC_IER, IER_LIE);
+               regmap_set_bits(ldev->regmap, LTDC_IER, IER_LIE);
        else
                return -EPERM;
 
@@ -731,7 +1076,7 @@ static void ltdc_crtc_disable_vblank(struct drm_crtc *crtc)
        struct ltdc_device *ldev = crtc_to_ltdc(crtc);
 
        DRM_DEBUG_DRIVER("\n");
-       reg_clear(ldev->regs, LTDC_IER, IER_LIE);
+       regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE);
 }
 
 static const struct drm_crtc_funcs ltdc_crtc_funcs = {
@@ -789,7 +1134,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
        u32 y0 = newstate->crtc_y;
        u32 y1 = newstate->crtc_y + newstate->crtc_h - 1;
        u32 src_x, src_y, src_w, src_h;
-       u32 val, pitch_in_bytes, line_length, paddr, ahbp, avbp, bpcr;
+       u32 val, pitch_in_bytes, line_length, line_number, paddr, ahbp, avbp, bpcr;
        enum ltdc_pix_fmt pf;
 
        if (!newstate->crtc || !fb) {
@@ -809,19 +1154,20 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
                         newstate->crtc_w, newstate->crtc_h,
                         newstate->crtc_x, newstate->crtc_y);
 
-       bpcr = reg_read(ldev->regs, LTDC_BPCR);
+       regmap_read(ldev->regmap, LTDC_BPCR, &bpcr);
+
        ahbp = (bpcr & BPCR_AHBP) >> 16;
        avbp = bpcr & BPCR_AVBP;
 
        /* Configures the horizontal start and stop position */
        val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
-       reg_update_bits(ldev->regs, LTDC_L1WHPCR + lofs,
-                       LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
+       regmap_write_bits(ldev->regmap, LTDC_L1WHPCR + lofs,
+                         LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
 
        /* Configures the vertical start and stop position */
        val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
-       reg_update_bits(ldev->regs, LTDC_L1WVPCR + lofs,
-                       LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
+       regmap_write_bits(ldev->regmap, LTDC_L1WVPCR + lofs,
+                         LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
 
        /* Specifies the pixel format */
        pf = to_ltdc_pixelformat(fb->format->format);
@@ -829,24 +1175,27 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
                if (ldev->caps.pix_fmt_hw[val] == pf)
                        break;
 
+       /* Use the flexible color format feature if necessary and available */
+       if (ldev->caps.pix_fmt_flex && val == NB_PF)
+               val = ltdc_set_flexible_pixel_format(plane, pf);
+
        if (val == NB_PF) {
                DRM_ERROR("Pixel format %.4s not supported\n",
                          (char *)&fb->format->format);
                val = 0;        /* set by default ARGB 32 bits */
        }
-       reg_update_bits(ldev->regs, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
+       regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
 
        /* Configures the color frame buffer pitch in bytes & line length */
        pitch_in_bytes = fb->pitches[0];
        line_length = fb->format->cpp[0] *
                      (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
        val = ((pitch_in_bytes << 16) | line_length);
-       reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs,
-                       LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
+       regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
 
        /* Specifies the constant alpha value */
        val = newstate->alpha >> 8;
-       reg_update_bits(ldev->regs, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
+       regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
 
        /* Specifies the blending factors */
        val = BF1_PAXCA | BF2_1PAXCA;
@@ -858,24 +1207,98 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
            plane->type != DRM_PLANE_TYPE_PRIMARY)
                val = BF1_PAXCA | BF2_1PAXCA;
 
-       reg_update_bits(ldev->regs, LTDC_L1BFCR + lofs,
-                       LXBFCR_BF2 | LXBFCR_BF1, val);
+       regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs, LXBFCR_BF2 | LXBFCR_BF1, val);
 
        /* Configures the frame buffer line number */
-       val = y1 - y0 + 1;
-       reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, val);
+       line_number = y1 - y0 + 1;
+       regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
 
        /* Sets the FB address */
        paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 0);
 
        DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
-       reg_write(ldev->regs, LTDC_L1CFBAR + lofs, paddr);
+       regmap_write(ldev->regmap, LTDC_L1CFBAR + lofs, paddr);
+
+       if (ldev->caps.ycbcr_input) {
+               if (fb->format->is_yuv) {
+                       switch (fb->format->format) {
+                       case DRM_FORMAT_NV12:
+                       case DRM_FORMAT_NV21:
+                       /* Configure the auxiliary frame buffer address 0 & 1 */
+                       paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
+                       regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
+                       regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr + 1);
+
+                       /* Configure the buffer length */
+                       val = ((pitch_in_bytes << 16) | line_length);
+                       regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
+
+                       /* Configure the frame buffer line number */
+                       val = (line_number >> 1);
+                       regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
+                       break;
+                       case DRM_FORMAT_YUV420:
+                       /* Configure the auxiliary frame buffer address 0 */
+                       paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
+                       regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
+
+                       /* Configure the auxiliary frame buffer address 1 */
+                       paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
+                       regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
+
+                       line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
+                                     (ldev->caps.bus_width >> 3) - 1;
+
+                       /* Configure the buffer length */
+                       val = (((pitch_in_bytes >> 1) << 16) | line_length);
+                       regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
+
+                       /* Configure the frame buffer line number */
+                       val = (line_number >> 1);
+                       regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
+                       break;
+                       case DRM_FORMAT_YVU420:
+                       /* Configure the auxiliary frame buffer address 0 */
+                       paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
+                       regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
+
+                       /* Configure the auxiliary frame buffer address 1 */
+                       paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
+                       regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
+
+                       line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
+                                     (ldev->caps.bus_width >> 3) - 1;
+
+                       /* Configure the buffer length */
+                       val = (((pitch_in_bytes >> 1) << 16) | line_length);
+                       regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
+
+                       /* Configure the frame buffer line number */
+                       val = (line_number >> 1);
+                       regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
+                       break;
+                       }
+
+                       /* Configure YCbC conversion coefficient */
+                       ltdc_set_ycbcr_coeffs(plane);
+
+                       /* Configure YCbCr format and enable/disable conversion */
+                       ltdc_set_ycbcr_config(plane, fb->format->format);
+               } else {
+                       /* disable ycbcr conversion */
+                       regmap_write(ldev->regmap, LTDC_L1PCR + lofs, 0);
+               }
+       }
 
        /* Enable layer and CLUT if needed */
        val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
        val |= LXCR_LEN;
-       reg_update_bits(ldev->regs, LTDC_L1CR + lofs,
-                       LXCR_LEN | LXCR_CLUTEN, val);
+       regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN, val);
+
+       /* Commit shadow registers = update plane at next vblank */
+       if (ldev->caps.plane_reg_shadow)
+               regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
+                                 LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
 
        ldev->plane_fpsi[plane->index].counter++;
 
@@ -900,7 +1323,12 @@ static void ltdc_plane_atomic_disable(struct drm_plane *plane,
        u32 lofs = plane->index * LAY_OFS;
 
        /* disable layer */
-       reg_clear(ldev->regs, LTDC_L1CR + lofs, LXCR_LEN);
+       regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN, 0);
+
+       /* Commit shadow registers = update plane at next vblank */
+       if (ldev->caps.plane_reg_shadow)
+               regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
+                                 LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
 
        DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
                         oldstate->crtc->base.id, plane->base.id);
@@ -925,16 +1353,6 @@ static void ltdc_plane_atomic_print_state(struct drm_printer *p,
        fpsi->counter = 0;
 }
 
-static bool ltdc_plane_format_mod_supported(struct drm_plane *plane,
-                                           u32 format,
-                                           u64 modifier)
-{
-       if (modifier == DRM_FORMAT_MOD_LINEAR)
-               return true;
-
-       return false;
-}
-
 static const struct drm_plane_funcs ltdc_plane_funcs = {
        .update_plane = drm_atomic_helper_update_plane,
        .disable_plane = drm_atomic_helper_disable_plane,
@@ -943,7 +1361,6 @@ static const struct drm_plane_funcs ltdc_plane_funcs = {
        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
        .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
        .atomic_print_state = ltdc_plane_atomic_print_state,
-       .format_mod_supported = ltdc_plane_format_mod_supported,
 };
 
 static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
@@ -953,36 +1370,58 @@ static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
 };
 
 static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
-                                          enum drm_plane_type type)
+                                          enum drm_plane_type type,
+                                          int index)
 {
        unsigned long possible_crtcs = CRTC_MASK;
        struct ltdc_device *ldev = ddev->dev_private;
        struct device *dev = ddev->dev;
        struct drm_plane *plane;
        unsigned int i, nb_fmt = 0;
-       u32 formats[NB_PF * 2];
-       u32 drm_fmt, drm_fmt_no_alpha;
+       u32 *formats;
+       u32 drm_fmt;
        const u64 *modifiers = ltdc_format_modifiers;
+       u32 lofs = index * LAY_OFS;
+       u32 val;
        int ret;
 
-       /* Get supported pixel formats */
-       for (i = 0; i < NB_PF; i++) {
-               drm_fmt = to_drm_pixelformat(ldev->caps.pix_fmt_hw[i]);
-               if (!drm_fmt)
-                       continue;
-               formats[nb_fmt++] = drm_fmt;
+       /* Allocate the biggest size according to supported color formats */
+       formats = devm_kzalloc(dev, (ldev->caps.pix_fmt_nb +
+                              ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) +
+                              ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) +
+                              ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp)) *
+                              sizeof(*formats), GFP_KERNEL);
 
-               /* Add the no-alpha related format if any & supported */
-               drm_fmt_no_alpha = get_pixelformat_without_alpha(drm_fmt);
-               if (!drm_fmt_no_alpha)
-                       continue;
+       for (i = 0; i < ldev->caps.pix_fmt_nb; i++) {
+               drm_fmt = ldev->caps.pix_fmt_drm[i];
 
                /* Manage hw-specific capabilities */
-               if (ldev->caps.non_alpha_only_l1 &&
-                   type != DRM_PLANE_TYPE_PRIMARY)
-                       continue;
+               if (ldev->caps.non_alpha_only_l1)
+                       /* XR24 & RX24 like formats supported only on primary layer */
+                       if (type != DRM_PLANE_TYPE_PRIMARY && is_xrgb(drm_fmt))
+                               continue;
 
-               formats[nb_fmt++] = drm_fmt_no_alpha;
+               formats[nb_fmt++] = drm_fmt;
+       }
+
+       /* Add YCbCr supported pixel formats */
+       if (ldev->caps.ycbcr_input) {
+               regmap_read(ldev->regmap, LTDC_L1C1R + lofs, &val);
+               if (val & LXCR_C1R_YIA) {
+                       memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_cp,
+                              ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) * sizeof(*formats));
+                       nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp);
+               }
+               if (val & LXCR_C1R_YSPA) {
+                       memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_sp,
+                              ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) * sizeof(*formats));
+                       nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp);
+               }
+               if (val & LXCR_C1R_YFPA) {
+                       memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_fp,
+                              ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp) * sizeof(*formats));
+                       nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp);
+               }
        }
 
        plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
@@ -995,6 +1434,17 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
        if (ret < 0)
                return NULL;
 
+       if (ldev->caps.ycbcr_input) {
+               if (val & (LXCR_C1R_YIA | LXCR_C1R_YSPA | LXCR_C1R_YFPA))
+                       drm_plane_create_color_properties(plane,
+                                                         BIT(DRM_COLOR_YCBCR_BT601) |
+                                                         BIT(DRM_COLOR_YCBCR_BT709),
+                                                         BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
+                                                         BIT(DRM_COLOR_YCBCR_FULL_RANGE),
+                                                         DRM_COLOR_YCBCR_BT601,
+                                                         DRM_COLOR_YCBCR_LIMITED_RANGE);
+       }
+
        drm_plane_helper_add(plane, &ltdc_plane_helper_funcs);
 
        drm_plane_create_alpha_property(plane);
@@ -1020,7 +1470,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
        unsigned int i;
        int ret;
 
-       primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY);
+       primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY, 0);
        if (!primary) {
                DRM_ERROR("Can not create primary plane\n");
                return -EINVAL;
@@ -1044,7 +1494,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
 
        /* Add planes. Note : the first layer is used by primary plane */
        for (i = 1; i < ldev->caps.nb_layers; i++) {
-               overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY);
+               overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY, i);
                if (!overlay) {
                        ret = -ENOMEM;
                        DRM_ERROR("Can not create overlay plane %d\n", i);
@@ -1068,7 +1518,7 @@ static void ltdc_encoder_disable(struct drm_encoder *encoder)
        DRM_DEBUG_DRIVER("\n");
 
        /* Disable LTDC */
-       reg_clear(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+       regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
 
        /* Set to sleep state the pinctrl whatever type of encoder */
        pinctrl_pm_select_sleep_state(ddev->dev);
@@ -1082,7 +1532,7 @@ static void ltdc_encoder_enable(struct drm_encoder *encoder)
        DRM_DEBUG_DRIVER("\n");
 
        /* Enable LTDC */
-       reg_set(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+       regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
 }
 
 static void ltdc_encoder_mode_set(struct drm_encoder *encoder,
@@ -1145,21 +1595,25 @@ static int ltdc_get_caps(struct drm_device *ddev)
         * at least 1 layer must be managed & the number of layers
         * must not exceed LTDC_MAX_LAYER
         */
-       lcr = reg_read(ldev->regs, LTDC_LCR);
+       regmap_read(ldev->regmap, LTDC_LCR, &lcr);
 
        ldev->caps.nb_layers = clamp((int)lcr, 1, LTDC_MAX_LAYER);
 
        /* set data bus width */
-       gc2r = reg_read(ldev->regs, LTDC_GC2R);
+       regmap_read(ldev->regmap, LTDC_GC2R, &gc2r);
        bus_width_log2 = (gc2r & GC2R_BW) >> 4;
        ldev->caps.bus_width = 8 << bus_width_log2;
-       ldev->caps.hw_version = reg_read(ldev->regs, LTDC_IDR);
+       regmap_read(ldev->regmap, LTDC_IDR, &ldev->caps.hw_version);
 
        switch (ldev->caps.hw_version) {
        case HWVER_10200:
        case HWVER_10300:
-               ldev->caps.reg_ofs = REG_OFS_NONE;
+               ldev->caps.layer_ofs = LAY_OFS_0;
+               ldev->caps.layer_regs = ltdc_layer_regs_a0;
                ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
+               ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a0;
+               ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a0);
+               ldev->caps.pix_fmt_flex = false;
                /*
                 * Hw older versions support non-alpha color formats derived
                 * from native alpha color formats only on the primary layer.
@@ -1172,13 +1626,37 @@ static int ltdc_get_caps(struct drm_device *ddev)
                if (ldev->caps.hw_version == HWVER_10200)
                        ldev->caps.pad_max_freq_hz = 65000000;
                ldev->caps.nb_irq = 2;
+               ldev->caps.ycbcr_input = false;
+               ldev->caps.ycbcr_output = false;
+               ldev->caps.plane_reg_shadow = false;
                break;
        case HWVER_20101:
-               ldev->caps.reg_ofs = REG_OFS_4;
+               ldev->caps.layer_ofs = LAY_OFS_0;
+               ldev->caps.layer_regs = ltdc_layer_regs_a1;
                ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
+               ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a1;
+               ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a1);
+               ldev->caps.pix_fmt_flex = false;
                ldev->caps.non_alpha_only_l1 = false;
                ldev->caps.pad_max_freq_hz = 150000000;
                ldev->caps.nb_irq = 4;
+               ldev->caps.ycbcr_input = false;
+               ldev->caps.ycbcr_output = false;
+               ldev->caps.plane_reg_shadow = false;
+               break;
+       case HWVER_40100:
+               ldev->caps.layer_ofs = LAY_OFS_1;
+               ldev->caps.layer_regs = ltdc_layer_regs_a2;
+               ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a2;
+               ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a2;
+               ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a2);
+               ldev->caps.pix_fmt_flex = true;
+               ldev->caps.non_alpha_only_l1 = false;
+               ldev->caps.pad_max_freq_hz = 90000000;
+               ldev->caps.nb_irq = 2;
+               ldev->caps.ycbcr_input = true;
+               ldev->caps.ycbcr_output = true;
+               ldev->caps.plane_reg_shadow = true;
                break;
        default:
                return -ENODEV;
@@ -1296,9 +1774,15 @@ int ltdc_load(struct drm_device *ddev)
                goto err;
        }
 
+       ldev->regmap = devm_regmap_init_mmio(&pdev->dev, ldev->regs, &stm32_ltdc_regmap_cfg);
+       if (IS_ERR(ldev->regmap)) {
+               DRM_ERROR("Unable to regmap ltdc registers\n");
+               ret = PTR_ERR(ldev->regmap);
+               goto err;
+       }
+
        /* Disable interrupts */
-       reg_clear(ldev->regs, LTDC_IER,
-                 IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE);
+       regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE);
 
        ret = ltdc_get_caps(ddev);
        if (ret) {
index f153b90..6968d1c 100644 (file)
 struct ltdc_caps {
        u32 hw_version;         /* hardware version */
        u32 nb_layers;          /* number of supported layers */
-       u32 reg_ofs;            /* register offset for applicable regs */
+       u32 layer_ofs;          /* layer offset for applicable regs */
+       const u32 *layer_regs;  /* layer register offset */
        u32 bus_width;          /* bus width (32 or 64 bits) */
-       const u32 *pix_fmt_hw;  /* supported pixel formats */
+       const u32 *pix_fmt_hw;  /* supported hw pixel formats */
+       const u32 *pix_fmt_drm; /* supported drm pixel formats */
+       int pix_fmt_nb;         /* number of pixel format */
+       bool pix_fmt_flex;      /* pixel format flexibility supported */
        bool non_alpha_only_l1; /* non-native no-alpha formats on layer 1 */
        int pad_max_freq_hz;    /* max frequency supported by pad */
        int nb_irq;             /* number of hardware interrupts */
+       bool ycbcr_input;       /* ycbcr input converter supported */
+       bool ycbcr_output;      /* ycbcr output converter supported */
+       bool plane_reg_shadow;  /* plane shadow registers ability */
 };
 
 #define LTDC_MAX_LAYER 4
@@ -31,6 +38,7 @@ struct fps_info {
 
 struct ltdc_device {
        void __iomem *regs;
+       struct regmap *regmap;
        struct clk *pixel_clk;  /* lcd pixel clock */
        struct mutex err_lock;  /* protecting error_status */
        struct ltdc_caps caps;
index 8cf5aeb..18c319b 100644 (file)
@@ -5,6 +5,7 @@ config DRM_TEGRA
        depends on COMMON_CLK
        depends on DRM
        depends on OF
+       select DRM_DP_HELPER
        select DRM_KMS_HELPER
        select DRM_MIPI_DSI
        select DRM_PANEL
index 70dfb7d..e4369e5 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 #include <drm/drm_crtc.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_print.h>
 
 #include "dp.h"
index 1f96e41..8ca5009 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/reset.h>
 #include <linux/workqueue.h>
 
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_panel.h>
 
 #include "dp.h"
index 0ea320c..b125572 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_debugfs.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_file.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_scdc_helper.h>
index 3ddb7c7..cc567c8 100644 (file)
@@ -60,8 +60,6 @@ void tilcdc_module_cleanup(struct tilcdc_module *mod)
        list_del(&mod->list);
 }
 
-static struct of_device_id tilcdc_of_match[];
-
 static int tilcdc_atomic_check(struct drm_device *dev,
                               struct drm_atomic_state *state)
 {
@@ -587,7 +585,7 @@ static int tilcdc_pdev_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct of_device_id tilcdc_of_match[] = {
+static const struct of_device_id tilcdc_of_match[] = {
                { .compatible = "ti,am33xx-tilcdc", },
                { .compatible = "ti,da850-tilcdc", },
                { },
index fc26a1c..ed971c8 100644 (file)
@@ -10,6 +10,7 @@
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_vram_helper.h>
 #include <drm/drm_managed.h>
+#include <drm/drm_module.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 
@@ -716,24 +717,7 @@ static struct pci_driver bochs_pci_driver = {
 /* ---------------------------------------------------------------------- */
 /* module init/exit                                                       */
 
-static int __init bochs_init(void)
-{
-       if (drm_firmware_drivers_only() && bochs_modeset == -1)
-               return -EINVAL;
-
-       if (bochs_modeset == 0)
-               return -EINVAL;
-
-       return pci_register_driver(&bochs_pci_driver);
-}
-
-static void __exit bochs_exit(void)
-{
-       pci_unregister_driver(&bochs_pci_driver);
-}
-
-module_init(bochs_init);
-module_exit(bochs_exit);
+drm_module_pci_driver_if_modeset(bochs_pci_driver, bochs_modeset);
 
 MODULE_DEVICE_TABLE(pci, bochs_pci_tbl);
 MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
index c95d9ff..ecf2475 100644 (file)
@@ -39,6 +39,7 @@
 #include <drm/drm_ioctl.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_module.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 
@@ -633,21 +634,7 @@ static struct pci_driver cirrus_pci_driver = {
        .remove = cirrus_pci_remove,
 };
 
-static int __init cirrus_init(void)
-{
-       if (drm_firmware_drivers_only())
-               return -EINVAL;
-
-       return pci_register_driver(&cirrus_pci_driver);
-}
-
-static void __exit cirrus_exit(void)
-{
-       pci_unregister_driver(&cirrus_pci_driver);
-}
-
-module_init(cirrus_init);
-module_exit(cirrus_exit);
+drm_module_pci_driver(cirrus_pci_driver)
 
 MODULE_DEVICE_TABLE(pci, pciidlist);
 MODULE_LICENSE("GPL");
index 04146da..d191e87 100644 (file)
@@ -526,21 +526,33 @@ static int simpledrm_device_init_mm(struct simpledrm_device *sdev)
 {
        struct drm_device *dev = &sdev->dev;
        struct platform_device *pdev = sdev->pdev;
-       struct resource *mem;
+       struct resource *res, *mem;
        void __iomem *screen_base;
        int ret;
 
-       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!mem)
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
                return -EINVAL;
 
-       ret = devm_aperture_acquire_from_firmware(dev, mem->start, resource_size(mem));
+       ret = devm_aperture_acquire_from_firmware(dev, res->start, resource_size(res));
        if (ret) {
                drm_err(dev, "could not acquire memory range %pr: error %d\n",
-                       mem, ret);
+                       res, ret);
                return ret;
        }
 
+       mem = devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+                                     sdev->dev.driver->name);
+       if (!mem) {
+               /*
+                * We cannot make this fatal. Sometimes this comes from magic
+                * spaces our resource handlers simply don't know about. Use
+                * the I/O-memory resource as-is and try to map that instead.
+                */
+               drm_warn(dev, "could not acquire memory region %pr\n", res);
+               mem = res;
+       }
+
        screen_base = devm_ioremap_wc(&pdev->dev, mem->start,
                                      resource_size(mem));
        if (!screen_base)
index 72a9430..544a84f 100644 (file)
@@ -241,6 +241,11 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
        if (bo->type != ttm_bo_type_sg)
                fbo->base.base.resv = &fbo->base.base._resv;
 
+       if (fbo->base.resource) {
+               ttm_resource_set_bo(fbo->base.resource, &fbo->base);
+               bo->resource = NULL;
+       }
+
        dma_resv_init(&fbo->base.base._resv);
        fbo->base.base.dev = NULL;
        ret = dma_resv_trylock(&fbo->base.base._resv);
@@ -509,7 +514,6 @@ static int ttm_bo_move_to_ghost(struct ttm_buffer_object *bo,
                ghost_obj->ttm = NULL;
        else
                bo->ttm = NULL;
-       bo->resource = NULL;
 
        dma_resv_unlock(&ghost_obj->base._resv);
        ttm_bo_put(ghost_obj);
@@ -637,7 +641,6 @@ int ttm_bo_pipeline_gutting(struct ttm_buffer_object *bo)
        dma_resv_unlock(&ghost->base._resv);
        ttm_bo_put(ghost);
        bo->ttm = ttm;
-       bo->resource = NULL;
        ttm_bo_assign_mem(bo, sys_res);
        return 0;
 
index 072e0ba..8cd4f3f 100644 (file)
@@ -89,6 +89,7 @@ static int ttm_range_man_alloc(struct ttm_resource_manager *man,
        spin_unlock(&rman->lock);
 
        if (unlikely(ret)) {
+               ttm_resource_fini(man, *res);
                kfree(node);
                return ret;
        }
@@ -108,6 +109,7 @@ static void ttm_range_man_free(struct ttm_resource_manager *man,
        drm_mm_remove_node(&node->mm_nodes[0]);
        spin_unlock(&rman->lock);
 
+       ttm_resource_fini(man, res);
        kfree(node);
 }
 
@@ -156,7 +158,7 @@ int ttm_range_man_init_nocheck(struct ttm_device *bdev,
 
        man->func = &ttm_range_manager_func;
 
-       ttm_resource_manager_init(man, p_size);
+       ttm_resource_manager_init(man, bdev, p_size);
 
        drm_mm_init(&rman->mm, 0, p_size);
        spin_lock_init(&rman->lock);
index 035d713..68344c9 100644 (file)
 #include <drm/ttm/ttm_resource.h>
 #include <drm/ttm/ttm_bo_driver.h>
 
+/**
+ * ttm_resource_init - resource object constructure
+ * @bo: buffer object this resources is allocated for
+ * @place: placement of the resource
+ * @res: the resource object to inistilize
+ *
+ * Initialize a new resource object. Counterpart of &ttm_resource_fini.
+ */
 void ttm_resource_init(struct ttm_buffer_object *bo,
                        const struct ttm_place *place,
                        struct ttm_resource *res)
@@ -41,9 +49,25 @@ void ttm_resource_init(struct ttm_buffer_object *bo,
        res->bus.offset = 0;
        res->bus.is_iomem = false;
        res->bus.caching = ttm_cached;
+       res->bo = bo;
 }
 EXPORT_SYMBOL(ttm_resource_init);
 
+/**
+ * ttm_resource_fini - resource destructor
+ * @man: the resource manager this resource belongs to
+ * @res: the resource to clean up
+ *
+ * Should be used by resource manager backends to clean up the TTM resource
+ * objects before freeing the underlying structure. Counterpart of
+ * &ttm_resource_init
+ */
+void ttm_resource_fini(struct ttm_resource_manager *man,
+                      struct ttm_resource *res)
+{
+}
+EXPORT_SYMBOL(ttm_resource_fini);
+
 int ttm_resource_alloc(struct ttm_buffer_object *bo,
                       const struct ttm_place *place,
                       struct ttm_resource **res_ptr)
@@ -116,20 +140,31 @@ bool ttm_resource_compat(struct ttm_resource *res,
 }
 EXPORT_SYMBOL(ttm_resource_compat);
 
+void ttm_resource_set_bo(struct ttm_resource *res,
+                        struct ttm_buffer_object *bo)
+{
+       spin_lock(&bo->bdev->lru_lock);
+       res->bo = bo;
+       spin_unlock(&bo->bdev->lru_lock);
+}
+
 /**
  * ttm_resource_manager_init
  *
  * @man: memory manager object to init
+ * @bdev: ttm device this manager belongs to
  * @p_size: size managed area in pages.
  *
  * Initialise core parts of a manager object.
  */
 void ttm_resource_manager_init(struct ttm_resource_manager *man,
+                              struct ttm_device *bdev,
                               unsigned long p_size)
 {
        unsigned i;
 
        spin_lock_init(&man->move_lock);
+       man->bdev = bdev;
        man->size = p_size;
 
        for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i)
index 63aca52..2ced169 100644 (file)
@@ -23,6 +23,7 @@ static int ttm_sys_man_alloc(struct ttm_resource_manager *man,
 static void ttm_sys_man_free(struct ttm_resource_manager *man,
                             struct ttm_resource *res)
 {
+       ttm_resource_fini(man, res);
        kfree(res);
 }
 
@@ -42,7 +43,7 @@ void ttm_sys_man_init(struct ttm_device *bdev)
        man->use_tt = true;
        man->func = &ttm_sys_manager_func;
 
-       ttm_resource_manager_init(man, 0);
+       ttm_resource_manager_init(man, bdev, 0);
        ttm_set_driver_manager(bdev, TTM_PL_SYSTEM, man);
        ttm_resource_manager_set_used(man, true);
 }
index bd46396..1afcd54 100644 (file)
@@ -219,6 +219,7 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
        int ret;
        u32 mmu_debug;
        u32 ident1;
+       u64 mask;
 
        v3d = devm_drm_dev_alloc(dev, &v3d_drm_driver, struct v3d_dev, drm);
        if (IS_ERR(v3d))
@@ -237,8 +238,11 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
                return ret;
 
        mmu_debug = V3D_READ(V3D_MMU_DEBUG_INFO);
-       dma_set_mask_and_coherent(dev,
-               DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_PA_WIDTH)));
+       mask = DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_PA_WIDTH));
+       ret = dma_set_mask_and_coherent(dev, mask);
+       if (ret)
+               return ret;
+
        v3d->va_width = 30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_VA_WIDTH);
 
        ident1 = V3D_READ(V3D_HUB_IDENT1);
index 6d1281a..e451cc5 100644 (file)
@@ -355,8 +355,6 @@ static struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev,
        uint32_t page_index = bo_page_index(size);
        struct vc4_bo *bo = NULL;
 
-       size = roundup(size, PAGE_SIZE);
-
        mutex_lock(&vc4->bo_lock);
        if (page_index >= vc4->bo_cache.size_list_size)
                goto out;
index 16abc3a..a03053c 100644 (file)
@@ -37,6 +37,8 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_vblank.h>
 
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
 #include "uapi/drm/vc4_drm.h"
 
 #include "vc4_drv.h"
@@ -215,6 +217,7 @@ static void vc4_match_add_drivers(struct device *dev,
 static int vc4_drm_bind(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
+       struct rpi_firmware *firmware = NULL;
        struct drm_device *drm;
        struct vc4_dev *vc4;
        struct device_node *node;
@@ -251,15 +254,34 @@ static int vc4_drm_bind(struct device *dev)
        if (ret)
                return ret;
 
-       ret = component_bind_all(dev, drm);
+       node = of_find_compatible_node(NULL, NULL, "raspberrypi,bcm2835-firmware");
+       if (node) {
+               firmware = rpi_firmware_get(node);
+               of_node_put(node);
+
+               if (!firmware)
+                       return -EPROBE_DEFER;
+       }
+
+       ret = drm_aperture_remove_framebuffers(false, &vc4_drm_driver);
        if (ret)
                return ret;
 
-       ret = vc4_plane_create_additional_planes(drm);
+       if (firmware) {
+               ret = rpi_firmware_property(firmware,
+                                           RPI_FIRMWARE_NOTIFY_DISPLAY_DONE,
+                                           NULL, 0);
+               if (ret)
+                       drm_warn(drm, "Couldn't stop firmware display driver: %d\n", ret);
+
+               rpi_firmware_put(firmware);
+       }
+
+       ret = component_bind_all(dev, drm);
        if (ret)
-               goto unbind_all;
+               return ret;
 
-       ret = drm_aperture_remove_framebuffers(false, &vc4_drm_driver);
+       ret = vc4_plane_create_additional_planes(drm);
        if (ret)
                goto unbind_all;
 
@@ -357,6 +379,9 @@ static int __init vc4_drm_register(void)
 {
        int ret;
 
+       if (drm_firmware_drivers_only())
+               return -ENODEV;
+
        ret = platform_register_drivers(component_drivers,
                                        ARRAY_SIZE(component_drivers));
        if (ret)
index 053fbaf..e3121eb 100644 (file)
@@ -104,6 +104,15 @@ static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode)
        return (mode->clock * 1000) > HDMI_14_MAX_TMDS_CLK;
 }
 
+static bool vc4_hdmi_is_full_range_rgb(struct vc4_hdmi *vc4_hdmi,
+                                      const struct drm_display_mode *mode)
+{
+       struct vc4_hdmi_encoder *vc4_encoder = &vc4_hdmi->encoder;
+
+       return !vc4_encoder->hdmi_monitor ||
+               drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL;
+}
+
 static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
 {
        struct drm_info_node *node = (struct drm_info_node *)m->private;
@@ -481,7 +490,6 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
 static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
 {
        struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
-       struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
        struct drm_connector *connector = &vc4_hdmi->connector;
        struct drm_connector_state *cstate = connector->state;
        const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
@@ -499,10 +507,10 @@ static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
 
        drm_hdmi_avi_infoframe_quant_range(&frame.avi,
                                           connector, mode,
-                                          vc4_encoder->limited_rgb_range ?
-                                          HDMI_QUANTIZATION_RANGE_LIMITED :
-                                          HDMI_QUANTIZATION_RANGE_FULL);
-       drm_hdmi_avi_infoframe_colorspace(&frame.avi, cstate);
+                                          vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode) ?
+                                          HDMI_QUANTIZATION_RANGE_FULL :
+                                          HDMI_QUANTIZATION_RANGE_LIMITED);
+       drm_hdmi_avi_infoframe_colorimetry(&frame.avi, cstate);
        drm_hdmi_avi_infoframe_bars(&frame.avi, cstate);
 
        vc4_hdmi_write_infoframe(encoder, &frame);
@@ -726,7 +734,9 @@ static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
        mutex_unlock(&vc4_hdmi->mutex);
 }
 
-static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable)
+static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
+                              struct drm_connector_state *state,
+                              const struct drm_display_mode *mode)
 {
        unsigned long flags;
        u32 csc_ctl;
@@ -736,7 +746,7 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable)
        csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
                                VC4_HD_CSC_CTL_ORDER);
 
-       if (enable) {
+       if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode)) {
                /* CEA VICs other than #1 requre limited range RGB
                 * output unless overridden by an AVI infoframe.
                 * Apply a colorspace conversion to squash 0-255 down
@@ -766,44 +776,67 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable)
        spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 }
 
-static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable)
+/*
+ * If we need to output Full Range RGB, then use the unity matrix
+ *
+ * [ 1      0      0      0]
+ * [ 0      1      0      0]
+ * [ 0      0      1      0]
+ *
+ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
+ */
+static const u16 vc5_hdmi_csc_full_rgb_unity[3][4] = {
+       { 0x2000, 0x0000, 0x0000, 0x0000 },
+       { 0x0000, 0x2000, 0x0000, 0x0000 },
+       { 0x0000, 0x0000, 0x2000, 0x0000 },
+};
+
+/*
+ * CEA VICs other than #1 require limited range RGB output unless
+ * overridden by an AVI infoframe. Apply a colorspace conversion to
+ * squash 0-255 down to 16-235. The matrix here is:
+ *
+ * [ 0.8594 0      0      16]
+ * [ 0      0.8594 0      16]
+ * [ 0      0      0.8594 16]
+ *
+ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
+ */
+static const u16 vc5_hdmi_csc_full_rgb_to_limited_rgb[3][4] = {
+       { 0x1b80, 0x0000, 0x0000, 0x0400 },
+       { 0x0000, 0x1b80, 0x0000, 0x0400 },
+       { 0x0000, 0x0000, 0x1b80, 0x0400 },
+};
+
+static void vc5_hdmi_set_csc_coeffs(struct vc4_hdmi *vc4_hdmi,
+                                   const u16 coeffs[3][4])
 {
-       unsigned long flags;
-       u32 csc_ctl;
+       lockdep_assert_held(&vc4_hdmi->hw_lock);
+
+       HDMI_WRITE(HDMI_CSC_12_11, (coeffs[0][1] << 16) | coeffs[0][0]);
+       HDMI_WRITE(HDMI_CSC_14_13, (coeffs[0][3] << 16) | coeffs[0][2]);
+       HDMI_WRITE(HDMI_CSC_22_21, (coeffs[1][1] << 16) | coeffs[1][0]);
+       HDMI_WRITE(HDMI_CSC_24_23, (coeffs[1][3] << 16) | coeffs[1][2]);
+       HDMI_WRITE(HDMI_CSC_32_31, (coeffs[2][1] << 16) | coeffs[2][0]);
+       HDMI_WRITE(HDMI_CSC_34_33, (coeffs[2][3] << 16) | coeffs[2][2]);
+}
 
-       csc_ctl = 0x07; /* RGB_CONVERT_MODE = custom matrix, || USE_RGB_TO_YCBCR */
+static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
+                              struct drm_connector_state *state,
+                              const struct drm_display_mode *mode)
+{
+       unsigned long flags;
+       u32 csc_ctl = VC5_MT_CP_CSC_CTL_ENABLE | VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
+                                                              VC5_MT_CP_CSC_CTL_MODE);
 
        spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
-       if (enable) {
-               /* CEA VICs other than #1 requre limited range RGB
-                * output unless overridden by an AVI infoframe.
-                * Apply a colorspace conversion to squash 0-255 down
-                * to 16-235.  The matrix here is:
-                *
-                * [ 0.8594 0      0      16]
-                * [ 0      0.8594 0      16]
-                * [ 0      0      0.8594 16]
-                * [ 0      0      0       1]
-                * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
-                */
-               HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x1b80);
-               HDMI_WRITE(HDMI_CSC_14_13, (0x0400 << 16) | 0x0000);
-               HDMI_WRITE(HDMI_CSC_22_21, (0x1b80 << 16) | 0x0000);
-               HDMI_WRITE(HDMI_CSC_24_23, (0x0400 << 16) | 0x0000);
-               HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000);
-               HDMI_WRITE(HDMI_CSC_34_33, (0x0400 << 16) | 0x1b80);
-       } else {
-               /* Still use the matrix for full range, but make it unity.
-                * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
-                */
-               HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x2000);
-               HDMI_WRITE(HDMI_CSC_14_13, (0x0000 << 16) | 0x0000);
-               HDMI_WRITE(HDMI_CSC_22_21, (0x2000 << 16) | 0x0000);
-               HDMI_WRITE(HDMI_CSC_24_23, (0x0000 << 16) | 0x0000);
-               HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000);
-               HDMI_WRITE(HDMI_CSC_34_33, (0x0000 << 16) | 0x2000);
-       }
+       HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021);
+
+       if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode))
+               vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_rgb);
+       else
+               vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_unity);
 
        HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
 
@@ -889,7 +922,6 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
 
        spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
-       HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021);
        HDMI_WRITE(HDMI_HORZA,
                   (vsync_pos ? VC5_HDMI_HORZA_VPOS : 0) |
                   (hsync_pos ? VC5_HDMI_HORZA_HPOS : 0) |
@@ -1113,24 +1145,16 @@ static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder,
                                             struct drm_atomic_state *state)
 {
        struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+       struct drm_connector *connector = &vc4_hdmi->connector;
        struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
-       struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+       struct drm_connector_state *conn_state =
+               drm_atomic_get_new_connector_state(state, connector);
        unsigned long flags;
 
        mutex_lock(&vc4_hdmi->mutex);
 
-       if (vc4_encoder->hdmi_monitor &&
-           drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED) {
-               if (vc4_hdmi->variant->csc_setup)
-                       vc4_hdmi->variant->csc_setup(vc4_hdmi, true);
-
-               vc4_encoder->limited_rgb_range = true;
-       } else {
-               if (vc4_hdmi->variant->csc_setup)
-                       vc4_hdmi->variant->csc_setup(vc4_hdmi, false);
-
-               vc4_encoder->limited_rgb_range = false;
-       }
+       if (vc4_hdmi->variant->csc_setup)
+               vc4_hdmi->variant->csc_setup(vc4_hdmi, conn_state, mode);
 
        spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
        HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
index 36c0b08..2b6aaaf 100644 (file)
@@ -12,7 +12,6 @@
 struct vc4_hdmi_encoder {
        struct vc4_encoder base;
        bool hdmi_monitor;
-       bool limited_rgb_range;
 };
 
 static inline struct vc4_hdmi_encoder *
@@ -77,7 +76,9 @@ struct vc4_hdmi_variant {
        void (*reset)(struct vc4_hdmi *vc4_hdmi);
 
        /* Callback to enable / disable the CSC */
-       void (*csc_setup)(struct vc4_hdmi *vc4_hdmi, bool enable);
+       void (*csc_setup)(struct vc4_hdmi *vc4_hdmi,
+                         struct drm_connector_state *state,
+                         const struct drm_display_mode *mode);
 
        /* Callback to configure the video timings in the HDMI block */
        void (*set_timings)(struct vc4_hdmi *vc4_hdmi,
index 7538b84..3341071 100644 (file)
@@ -774,6 +774,9 @@ enum {
 # define VC4_HD_CSC_CTL_RGB2YCC                        BIT(1)
 # define VC4_HD_CSC_CTL_ENABLE                 BIT(0)
 
+# define VC5_MT_CP_CSC_CTL_ENABLE              BIT(2)
+# define VC5_MT_CP_CSC_CTL_MODE_MASK           VC4_MASK(1, 0)
+
 # define VC4_DVP_HT_CLOCK_STOP_PIXEL           BIT(1)
 
 /* HVS display list information. */
index 2de61b6..48d3c99 100644 (file)
@@ -248,6 +248,9 @@ void virtio_gpu_array_put_free(struct virtio_gpu_object_array *objs)
 {
        u32 i;
 
+       if (!objs)
+               return;
+
        for (i = 0; i < objs->nents; i++)
                drm_gem_object_put(objs->objs[i]);
        virtio_gpu_array_free(objs);
index d48c23d..9496fdc 100644 (file)
@@ -20,6 +20,8 @@
 #define XRES_MAX  8192
 #define YRES_MAX  8192
 
+#define NUM_OVERLAY_PLANES 8
+
 struct vkms_writeback_job {
        struct dma_buf_map map[DRM_FORMAT_MAX_PLANES];
        struct dma_buf_map data[DRM_FORMAT_MAX_PLANES];
index 04406bd..ba0e82a 100644 (file)
@@ -32,6 +32,21 @@ static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = {
        .get_modes    = vkms_conn_get_modes,
 };
 
+static int vkms_add_overlay_plane(struct vkms_device *vkmsdev, int index,
+                                 struct drm_crtc *crtc)
+{
+       struct vkms_plane *overlay;
+
+       overlay = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_OVERLAY, index);
+       if (IS_ERR(overlay))
+               return PTR_ERR(overlay);
+
+       if (!overlay->base.possible_crtcs)
+               overlay->base.possible_crtcs = drm_crtc_mask(crtc);
+
+       return 0;
+}
+
 int vkms_output_init(struct vkms_device *vkmsdev, int index)
 {
        struct vkms_output *output = &vkmsdev->output;
@@ -39,21 +54,21 @@ int vkms_output_init(struct vkms_device *vkmsdev, int index)
        struct drm_connector *connector = &output->connector;
        struct drm_encoder *encoder = &output->encoder;
        struct drm_crtc *crtc = &output->crtc;
-       struct vkms_plane *primary, *cursor = NULL, *overlay = NULL;
+       struct vkms_plane *primary, *cursor = NULL;
        int ret;
        int writeback;
+       unsigned int n;
 
        primary = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_PRIMARY, index);
        if (IS_ERR(primary))
                return PTR_ERR(primary);
 
        if (vkmsdev->config->overlay) {
-               overlay = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_OVERLAY, index);
-               if (IS_ERR(overlay))
-                       return PTR_ERR(overlay);
-
-               if (!overlay->base.possible_crtcs)
-                       overlay->base.possible_crtcs = drm_crtc_mask(crtc);
+               for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
+                       ret = vkms_add_overlay_plane(vkmsdev, index, crtc);
+                       if (ret)
+                               return ret;
+               }
        }
 
        if (vkmsdev->config->cursor) {
index ebb4505..60e3cc5 100644 (file)
@@ -117,6 +117,7 @@ nospace:
        gman->used_gmr_pages -= (*res)->num_pages;
        spin_unlock(&gman->lock);
        ida_free(&gman->gmr_ida, id);
+       ttm_resource_fini(man, *res);
        kfree(*res);
        return -ENOSPC;
 }
@@ -130,6 +131,7 @@ static void vmw_gmrid_man_put_node(struct ttm_resource_manager *man,
        spin_lock(&gman->lock);
        gman->used_gmr_pages -= res->num_pages;
        spin_unlock(&gman->lock);
+       ttm_resource_fini(man, res);
        kfree(res);
 }
 
@@ -160,7 +162,7 @@ int vmw_gmrid_man_init(struct vmw_private *dev_priv, int type)
 
        man->func = &vmw_gmrid_manager_func;
        man->use_tt = true;
-       ttm_resource_manager_init(man, 0);
+       ttm_resource_manager_init(man, &dev_priv->bdev, 0);
        spin_lock_init(&gman->lock);
        gman->used_gmr_pages = 0;
        ida_init(&gman->gmr_ida);
index b0005b0..d3007bf 100644 (file)
@@ -49,6 +49,7 @@ static int vmw_sys_man_alloc(struct ttm_resource_manager *man,
 static void vmw_sys_man_free(struct ttm_resource_manager *man,
                             struct ttm_resource *res)
 {
+       ttm_resource_fini(man, res);
        kfree(res);
 }
 
@@ -69,7 +70,7 @@ int vmw_sys_man_init(struct vmw_private *dev_priv)
        man->use_tt = true;
        man->func = &vmw_sys_manager_func;
 
-       ttm_resource_manager_init(man, 0);
+       ttm_resource_manager_init(man, bdev, 0);
        ttm_set_driver_manager(bdev, VMW_PL_SYSTEM, man);
        ttm_resource_manager_set_used(man, true);
        return 0;
index d8d38d8..06cf477 100644 (file)
@@ -6,6 +6,7 @@ config DRM_ZYNQMP_DPSUB
        depends on PHY_XILINX_ZYNQMP
        depends on XILINX_ZYNQMP_DPDMA
        select DMA_ENGINE
+       select DRM_DP_HELPER
        select DRM_GEM_CMA_HELPER
        select DRM_KMS_HELPER
        select GENERIC_PHY
index 6f588dc..b1bbbb1 100644 (file)
@@ -13,7 +13,7 @@
 #include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_managed.h>
index ccc23d8..75e93ef 100644 (file)
@@ -243,6 +243,17 @@ config CROS_USBPD_NOTIFY
          To compile this driver as a module, choose M here: the
          module will be called cros_usbpd_notify.
 
+config CHROMEOS_PRIVACY_SCREEN
+       tristate "ChromeOS Privacy Screen support"
+       depends on ACPI
+       depends on DRM
+       select DRM_PRIVACY_SCREEN
+       help
+         This driver provides the support needed for the in-built electronic
+         privacy screen that is present on some ChromeOS devices. When enabled,
+         this should probably always be built into the kernel to avoid or
+         minimize drm probe deferral.
+
 source "drivers/platform/chrome/wilco_ec/Kconfig"
 
 endif # CHROMEOS_PLATFORMS
index f901d2e..5d4be97 100644 (file)
@@ -4,6 +4,7 @@
 CFLAGS_cros_ec_trace.o:=               -I$(src)
 
 obj-$(CONFIG_CHROMEOS_LAPTOP)          += chromeos_laptop.o
+obj-$(CONFIG_CHROMEOS_PRIVACY_SCREEN)  += chromeos_privacy_screen.o
 obj-$(CONFIG_CHROMEOS_PSTORE)          += chromeos_pstore.o
 obj-$(CONFIG_CHROMEOS_TBMC)            += chromeos_tbmc.o
 obj-$(CONFIG_CROS_EC)                  += cros_ec.o
diff --git a/drivers/platform/chrome/chromeos_privacy_screen.c b/drivers/platform/chrome/chromeos_privacy_screen.c
new file mode 100644 (file)
index 0000000..77e9f5e
--- /dev/null
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ *  ChromeOS Privacy Screen support
+ *
+ * Copyright (C) 2022 Google LLC
+ *
+ * This is the Chromeos privacy screen provider, present on certain chromebooks,
+ * represented by a GOOG0010 device in the ACPI. This ACPI device, if present,
+ * will cause the i915 drm driver to probe defer until this driver registers
+ * the privacy-screen.
+ */
+
+#include <linux/acpi.h>
+#include <drm/drm_privacy_screen_driver.h>
+
+/*
+ * The DSM (Device Specific Method) constants below are the agreed API with
+ * the firmware team, on how to control privacy screen using ACPI methods.
+ */
+#define PRIV_SCRN_DSM_REVID            1       /* DSM version */
+#define PRIV_SCRN_DSM_FN_GET_STATUS    1       /* Get privacy screen status */
+#define PRIV_SCRN_DSM_FN_ENABLE                2       /* Enable privacy screen */
+#define PRIV_SCRN_DSM_FN_DISABLE       3       /* Disable privacy screen */
+
+static const guid_t chromeos_privacy_screen_dsm_guid =
+                   GUID_INIT(0xc7033113, 0x8720, 0x4ceb,
+                             0x90, 0x90, 0x9d, 0x52, 0xb3, 0xe5, 0x2d, 0x73);
+
+static void
+chromeos_privacy_screen_get_hw_state(struct drm_privacy_screen
+                                    *drm_privacy_screen)
+{
+       union acpi_object *obj;
+       acpi_handle handle;
+       struct device *privacy_screen =
+               drm_privacy_screen_get_drvdata(drm_privacy_screen);
+
+       handle = acpi_device_handle(to_acpi_device(privacy_screen));
+       obj = acpi_evaluate_dsm(handle, &chromeos_privacy_screen_dsm_guid,
+                               PRIV_SCRN_DSM_REVID,
+                               PRIV_SCRN_DSM_FN_GET_STATUS, NULL);
+       if (!obj) {
+               dev_err(privacy_screen,
+                       "_DSM failed to get privacy-screen state\n");
+               return;
+       }
+
+       if (obj->type != ACPI_TYPE_INTEGER)
+               dev_err(privacy_screen,
+                       "Bad _DSM to get privacy-screen state\n");
+       else if (obj->integer.value == 1)
+               drm_privacy_screen->hw_state = drm_privacy_screen->sw_state =
+                       PRIVACY_SCREEN_ENABLED;
+       else
+               drm_privacy_screen->hw_state = drm_privacy_screen->sw_state =
+                       PRIVACY_SCREEN_DISABLED;
+
+       ACPI_FREE(obj);
+}
+
+static int
+chromeos_privacy_screen_set_sw_state(struct drm_privacy_screen
+                                    *drm_privacy_screen,
+                                    enum drm_privacy_screen_status state)
+{
+       union acpi_object *obj = NULL;
+       acpi_handle handle;
+       struct device *privacy_screen =
+               drm_privacy_screen_get_drvdata(drm_privacy_screen);
+
+       handle = acpi_device_handle(to_acpi_device(privacy_screen));
+
+       if (state == PRIVACY_SCREEN_DISABLED) {
+               obj = acpi_evaluate_dsm(handle,
+                                       &chromeos_privacy_screen_dsm_guid,
+                                       PRIV_SCRN_DSM_REVID,
+                                       PRIV_SCRN_DSM_FN_DISABLE, NULL);
+       } else if (state == PRIVACY_SCREEN_ENABLED) {
+               obj = acpi_evaluate_dsm(handle,
+                                       &chromeos_privacy_screen_dsm_guid,
+                                       PRIV_SCRN_DSM_REVID,
+                                       PRIV_SCRN_DSM_FN_ENABLE, NULL);
+       } else {
+               dev_err(privacy_screen,
+                       "Bad attempt to set privacy-screen status to %u\n",
+                       state);
+               return -EINVAL;
+       }
+
+       if (!obj) {
+               dev_err(privacy_screen,
+                       "_DSM failed to set privacy-screen state\n");
+               return -EIO;
+       }
+
+       drm_privacy_screen->hw_state = drm_privacy_screen->sw_state = state;
+       ACPI_FREE(obj);
+       return 0;
+}
+
+static const struct drm_privacy_screen_ops chromeos_privacy_screen_ops = {
+       .get_hw_state = chromeos_privacy_screen_get_hw_state,
+       .set_sw_state = chromeos_privacy_screen_set_sw_state,
+};
+
+static int chromeos_privacy_screen_add(struct acpi_device *adev)
+{
+       struct drm_privacy_screen *drm_privacy_screen =
+               drm_privacy_screen_register(&adev->dev,
+                                           &chromeos_privacy_screen_ops,
+                                           &adev->dev);
+
+       if (IS_ERR(drm_privacy_screen)) {
+               dev_err(&adev->dev, "Error registering privacy-screen\n");
+               return PTR_ERR(drm_privacy_screen);
+       }
+
+       adev->driver_data = drm_privacy_screen;
+       dev_info(&adev->dev, "registered privacy-screen '%s'\n",
+                dev_name(&drm_privacy_screen->dev));
+
+       return 0;
+}
+
+static int chromeos_privacy_screen_remove(struct acpi_device *adev)
+{
+       struct drm_privacy_screen *drm_privacy_screen = acpi_driver_data(adev);
+
+       drm_privacy_screen_unregister(drm_privacy_screen);
+       return 0;
+}
+
+static const struct acpi_device_id chromeos_privacy_screen_device_ids[] = {
+       {"GOOG0010", 0}, /* Google's electronic privacy screen for eDP-1 */
+       {}
+};
+MODULE_DEVICE_TABLE(acpi, chromeos_privacy_screen_device_ids);
+
+static struct acpi_driver chromeos_privacy_screen_driver = {
+       .name = "chromeos_privacy_screen_driver",
+       .class = "ChromeOS",
+       .ids = chromeos_privacy_screen_device_ids,
+       .ops = {
+               .add = chromeos_privacy_screen_add,
+               .remove = chromeos_privacy_screen_remove,
+       },
+};
+
+module_acpi_driver(chromeos_privacy_screen_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ChromeOS ACPI Privacy Screen driver");
+MODULE_AUTHOR("Rajat Jain <rajatja@google.com>");
index 098180f..99fbe90 100644 (file)
@@ -9869,7 +9869,7 @@ static int tpacpi_lcdshadow_init(struct ibm_init_struct *iibm)
                return 0;
 
        lcdshadow_dev = drm_privacy_screen_register(&tpacpi_pdev->dev,
-                                                   &lcdshadow_ops);
+                                                   &lcdshadow_ops, NULL);
        if (IS_ERR(lcdshadow_dev))
                return PTR_ERR(lcdshadow_dev);
 
index 84c56f5..f8ef625 100644 (file)
@@ -110,7 +110,7 @@ static const struct fb_ops asiliantfb_ops = {
 static void asiliant_calc_dclk2(u32 *ppixclock, u8 *dclk2_m, u8 *dclk2_n, u8 *dclk2_div)
 {
        unsigned pixclock = *ppixclock;
-       unsigned Ftarget = 1000000 * (1000000 / pixclock);
+       unsigned Ftarget;
        unsigned n;
        unsigned best_error = 0xffffffff;
        unsigned best_m = 0xffffffff,
index 0fa7ede..b585339 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/init.h>
 #include <linux/linux_logo.h>
 #include <linux/proc_fs.h>
+#include <linux/platform_device.h>
 #include <linux/seq_file.h>
 #include <linux/console.h>
 #include <linux/kmod.h>
@@ -1557,18 +1558,36 @@ static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
        /* check all firmware fbs and kick off if the base addr overlaps */
        for_each_registered_fb(i) {
                struct apertures_struct *gen_aper;
+               struct device *device;
 
                if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE))
                        continue;
 
                gen_aper = registered_fb[i]->apertures;
+               device = registered_fb[i]->device;
                if (fb_do_apertures_overlap(gen_aper, a) ||
                        (primary && gen_aper && gen_aper->count &&
                         gen_aper->ranges[0].base == VGA_FB_PHYS)) {
 
                        printk(KERN_INFO "fb%d: switching to %s from %s\n",
                               i, name, registered_fb[i]->fix.id);
-                       do_unregister_framebuffer(registered_fb[i]);
+
+                       /*
+                        * If we kick-out a firmware driver, we also want to remove
+                        * the underlying platform device, such as simple-framebuffer,
+                        * VESA, EFI, etc. A native driver will then be able to
+                        * allocate the memory range.
+                        *
+                        * If it's not a platform device, at least print a warning. A
+                        * fix would add code to remove the device from the system.
+                        */
+                       if (dev_is_platform(device)) {
+                               registered_fb[i]->forced_out = true;
+                               platform_device_unregister(to_platform_device(device));
+                       } else {
+                               pr_warn("fb%d: cannot remove device\n", i);
+                               do_unregister_framebuffer(registered_fb[i]);
+                       }
                }
        }
 }
@@ -1898,9 +1917,13 @@ EXPORT_SYMBOL(register_framebuffer);
 void
 unregister_framebuffer(struct fb_info *fb_info)
 {
-       mutex_lock(&registration_lock);
+       bool forced_out = fb_info->forced_out;
+
+       if (!forced_out)
+               mutex_lock(&registration_lock);
        do_unregister_framebuffer(fb_info);
-       mutex_unlock(&registration_lock);
+       if (!forced_out)
+               mutex_unlock(&registration_lock);
 }
 EXPORT_SYMBOL(unregister_framebuffer);
 
index 3b134e1..68408c4 100644 (file)
@@ -489,7 +489,7 @@ static int s3c_fb_set_par(struct fb_info *info)
        struct s3c_fb_win *win = info->par;
        struct s3c_fb *sfb = win->parent;
        void __iomem *regs = sfb->regs;
-       void __iomem *buf = regs;
+       void __iomem *buf;
        int win_no = win->index;
        u32 alpha = 0;
        u32 data;
index 5754188..94fc9c6 100644 (file)
@@ -66,16 +66,36 @@ static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
        return 0;
 }
 
-struct simplefb_par;
+struct simplefb_par {
+       u32 palette[PSEUDO_PALETTE_SIZE];
+       struct resource *mem;
+#if defined CONFIG_OF && defined CONFIG_COMMON_CLK
+       bool clks_enabled;
+       unsigned int clk_count;
+       struct clk **clks;
+#endif
+#if defined CONFIG_OF && defined CONFIG_REGULATOR
+       bool regulators_enabled;
+       u32 regulator_count;
+       struct regulator **regulators;
+#endif
+};
+
 static void simplefb_clocks_destroy(struct simplefb_par *par);
 static void simplefb_regulators_destroy(struct simplefb_par *par);
 
 static void simplefb_destroy(struct fb_info *info)
 {
+       struct simplefb_par *par = info->par;
+       struct resource *mem = par->mem;
+
        simplefb_regulators_destroy(info->par);
        simplefb_clocks_destroy(info->par);
        if (info->screen_base)
                iounmap(info->screen_base);
+
+       if (mem)
+               release_mem_region(mem->start, resource_size(mem));
 }
 
 static const struct fb_ops simplefb_ops = {
@@ -169,20 +189,6 @@ static int simplefb_parse_pd(struct platform_device *pdev,
        return 0;
 }
 
-struct simplefb_par {
-       u32 palette[PSEUDO_PALETTE_SIZE];
-#if defined CONFIG_OF && defined CONFIG_COMMON_CLK
-       bool clks_enabled;
-       unsigned int clk_count;
-       struct clk **clks;
-#endif
-#if defined CONFIG_OF && defined CONFIG_REGULATOR
-       bool regulators_enabled;
-       u32 regulator_count;
-       struct regulator **regulators;
-#endif
-};
-
 #if defined CONFIG_OF && defined CONFIG_COMMON_CLK
 /*
  * Clock handling code.
@@ -405,7 +411,7 @@ static int simplefb_probe(struct platform_device *pdev)
        struct simplefb_params params;
        struct fb_info *info;
        struct simplefb_par *par;
-       struct resource *mem;
+       struct resource *res, *mem;
 
        /*
         * Generic drivers must not be registered if a framebuffer exists.
@@ -430,15 +436,28 @@ static int simplefb_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!mem) {
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
                dev_err(&pdev->dev, "No memory resource\n");
                return -EINVAL;
        }
 
+       mem = request_mem_region(res->start, resource_size(res), "simplefb");
+       if (!mem) {
+               /*
+                * We cannot make this fatal. Sometimes this comes from magic
+                * spaces our resource handlers simply don't know about. Use
+                * the I/O-memory resource as-is and try to map that instead.
+                */
+               dev_warn(&pdev->dev, "simplefb: cannot reserve video memory at %pR\n", res);
+               mem = res;
+       }
+
        info = framebuffer_alloc(sizeof(struct simplefb_par), &pdev->dev);
-       if (!info)
-               return -ENOMEM;
+       if (!info) {
+               ret = -ENOMEM;
+               goto error_release_mem_region;
+       }
        platform_set_drvdata(pdev, info);
 
        par = info->par;
@@ -495,6 +514,9 @@ static int simplefb_probe(struct platform_device *pdev)
                             info->var.xres, info->var.yres,
                             info->var.bits_per_pixel, info->fix.line_length);
 
+       if (mem != res)
+               par->mem = mem; /* release in clean-up handler */
+
        ret = register_framebuffer(info);
        if (ret < 0) {
                dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret);
@@ -513,6 +535,9 @@ error_unmap:
        iounmap(info->screen_base);
 error_fb_release:
        framebuffer_release(info);
+error_release_mem_region:
+       if (mem != res)
+               release_mem_region(mem->start, resource_size(mem));
        return ret;
 }
 
index 96e312a..d21f68f 100644 (file)
@@ -1351,7 +1351,12 @@ static int vga16fb_probe(struct platform_device *dev)
        printk(KERN_INFO "vga16fb: mapped to 0x%p\n", info->screen_base);
        par = info->par;
 
+#if defined(CONFIG_X86)
+       par->isVGA = screen_info.orig_video_isVGA == VIDEO_TYPE_VGAC;
+#else
+       /* non-x86 architectures treat orig_video_isVGA as a boolean flag */
        par->isVGA = screen_info.orig_video_isVGA;
+#endif
        par->palette_blanked = 0;
        par->vesa_blanked = 0;
 
index bda8aa7..5286a53 100644 (file)
@@ -51,7 +51,9 @@ struct dw_mipi_dsi_plat_data {
        unsigned int max_data_lanes;
 
        enum drm_mode_status (*mode_valid)(void *priv_data,
-                                          const struct drm_display_mode *mode);
+                                          const struct drm_display_mode *mode,
+                                          unsigned long mode_flags,
+                                          u32 lanes, u32 format);
 
        const struct dw_mipi_dsi_phy_ops *phy_ops;
        const struct dw_mipi_dsi_host_ops *host_ops;
diff --git a/include/drm/dp/drm_dp_aux_bus.h b/include/drm/dp/drm_dp_aux_bus.h
new file mode 100644 (file)
index 0000000..4f19b20
--- /dev/null
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * The DP AUX bus is used for devices that are connected over a DisplayPort
+ * AUX bus. The devices on the far side of the bus are referred to as
+ * endpoints in this code.
+ */
+
+#ifndef _DP_AUX_BUS_H_
+#define _DP_AUX_BUS_H_
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+
+/**
+ * struct dp_aux_ep_device - Main dev structure for DP AUX endpoints
+ *
+ * This is used to instantiate devices that are connected via a DP AUX
+ * bus. Usually the device is a panel, but conceivable other devices could
+ * be hooked up there.
+ */
+struct dp_aux_ep_device {
+       /** @dev: The normal dev pointer */
+       struct device dev;
+       /** @aux: Pointer to the aux bus */
+       struct drm_dp_aux *aux;
+};
+
+struct dp_aux_ep_driver {
+       int (*probe)(struct dp_aux_ep_device *aux_ep);
+       void (*remove)(struct dp_aux_ep_device *aux_ep);
+       void (*shutdown)(struct dp_aux_ep_device *aux_ep);
+       struct device_driver driver;
+};
+
+static inline struct dp_aux_ep_device *to_dp_aux_ep_dev(struct device *dev)
+{
+       return container_of(dev, struct dp_aux_ep_device, dev);
+}
+
+static inline struct dp_aux_ep_driver *to_dp_aux_ep_drv(struct device_driver *drv)
+{
+       return container_of(drv, struct dp_aux_ep_driver, driver);
+}
+
+int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux);
+void of_dp_aux_depopulate_ep_devices(struct drm_dp_aux *aux);
+int devm_of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux);
+
+#define dp_aux_dp_driver_register(aux_ep_drv) \
+       __dp_aux_dp_driver_register(aux_ep_drv, THIS_MODULE)
+int __dp_aux_dp_driver_register(struct dp_aux_ep_driver *aux_ep_drv,
+                               struct module *owner);
+void dp_aux_dp_driver_unregister(struct dp_aux_ep_driver *aux_ep_drv);
+
+#endif /* _DP_AUX_BUS_H_ */
diff --git a/include/drm/dp/drm_dp_dual_mode_helper.h b/include/drm/dp/drm_dp_dual_mode_helper.h
new file mode 100644 (file)
index 0000000..7ee4822
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef DRM_DP_DUAL_MODE_HELPER_H
+#define DRM_DP_DUAL_MODE_HELPER_H
+
+#include <linux/types.h>
+
+/*
+ * Optional for type 1 DVI adaptors
+ * Mandatory for type 1 HDMI and type 2 adaptors
+ */
+#define DP_DUAL_MODE_HDMI_ID 0x00 /* 00-0f */
+#define  DP_DUAL_MODE_HDMI_ID_LEN 16
+/*
+ * Optional for type 1 adaptors
+ * Mandatory for type 2 adaptors
+ */
+#define DP_DUAL_MODE_ADAPTOR_ID 0x10
+#define  DP_DUAL_MODE_REV_MASK 0x07
+#define  DP_DUAL_MODE_REV_TYPE2 0x00
+#define  DP_DUAL_MODE_TYPE_MASK 0xf0
+#define  DP_DUAL_MODE_TYPE_TYPE2 0xa0
+/* This field is marked reserved in dual mode spec, used in LSPCON */
+#define  DP_DUAL_MODE_TYPE_HAS_DPCD 0x08
+#define DP_DUAL_MODE_IEEE_OUI 0x11 /* 11-13*/
+#define  DP_DUAL_IEEE_OUI_LEN 3
+#define DP_DUAL_DEVICE_ID 0x14 /* 14-19 */
+#define  DP_DUAL_DEVICE_ID_LEN 6
+#define DP_DUAL_MODE_HARDWARE_REV 0x1a
+#define DP_DUAL_MODE_FIRMWARE_MAJOR_REV 0x1b
+#define DP_DUAL_MODE_FIRMWARE_MINOR_REV 0x1c
+#define DP_DUAL_MODE_MAX_TMDS_CLOCK 0x1d
+#define DP_DUAL_MODE_I2C_SPEED_CAP 0x1e
+#define DP_DUAL_MODE_TMDS_OEN 0x20
+#define  DP_DUAL_MODE_TMDS_DISABLE 0x01
+#define DP_DUAL_MODE_HDMI_PIN_CTRL 0x21
+#define  DP_DUAL_MODE_CEC_ENABLE 0x01
+#define DP_DUAL_MODE_I2C_SPEED_CTRL 0x22
+
+/* LSPCON specific registers, defined by MCA */
+#define DP_DUAL_MODE_LSPCON_MODE_CHANGE                0x40
+#define DP_DUAL_MODE_LSPCON_CURRENT_MODE               0x41
+#define  DP_DUAL_MODE_LSPCON_MODE_PCON                 0x1
+
+struct drm_device;
+struct i2c_adapter;
+
+ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter,
+                             u8 offset, void *buffer, size_t size);
+ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter,
+                              u8 offset, const void *buffer, size_t size);
+
+/**
+ * enum drm_lspcon_mode
+ * @DRM_LSPCON_MODE_INVALID: No LSPCON.
+ * @DRM_LSPCON_MODE_LS: Level shifter mode of LSPCON
+ *     which drives DP++ to HDMI 1.4 conversion.
+ * @DRM_LSPCON_MODE_PCON: Protocol converter mode of LSPCON
+ *     which drives DP++ to HDMI 2.0 active conversion.
+ */
+enum drm_lspcon_mode {
+       DRM_LSPCON_MODE_INVALID,
+       DRM_LSPCON_MODE_LS,
+       DRM_LSPCON_MODE_PCON,
+};
+
+/**
+ * enum drm_dp_dual_mode_type - Type of the DP dual mode adaptor
+ * @DRM_DP_DUAL_MODE_NONE: No DP dual mode adaptor
+ * @DRM_DP_DUAL_MODE_UNKNOWN: Could be either none or type 1 DVI adaptor
+ * @DRM_DP_DUAL_MODE_TYPE1_DVI: Type 1 DVI adaptor
+ * @DRM_DP_DUAL_MODE_TYPE1_HDMI: Type 1 HDMI adaptor
+ * @DRM_DP_DUAL_MODE_TYPE2_DVI: Type 2 DVI adaptor
+ * @DRM_DP_DUAL_MODE_TYPE2_HDMI: Type 2 HDMI adaptor
+ * @DRM_DP_DUAL_MODE_LSPCON: Level shifter / protocol converter
+ */
+enum drm_dp_dual_mode_type {
+       DRM_DP_DUAL_MODE_NONE,
+       DRM_DP_DUAL_MODE_UNKNOWN,
+       DRM_DP_DUAL_MODE_TYPE1_DVI,
+       DRM_DP_DUAL_MODE_TYPE1_HDMI,
+       DRM_DP_DUAL_MODE_TYPE2_DVI,
+       DRM_DP_DUAL_MODE_TYPE2_HDMI,
+       DRM_DP_DUAL_MODE_LSPCON,
+};
+
+enum drm_dp_dual_mode_type
+drm_dp_dual_mode_detect(const struct drm_device *dev, struct i2c_adapter *adapter);
+int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
+                                   struct i2c_adapter *adapter);
+int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
+                                    struct i2c_adapter *adapter, bool *enabled);
+int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
+                                    struct i2c_adapter *adapter, bool enable);
+const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type);
+
+int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
+                       enum drm_lspcon_mode *current_mode);
+int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
+                       enum drm_lspcon_mode reqd_mode);
+#endif
diff --git a/include/drm/dp/drm_dp_helper.h b/include/drm/dp/drm_dp_helper.h
new file mode 100644 (file)
index 0000000..98d0208
--- /dev/null
@@ -0,0 +1,2365 @@
+/*
+ * Copyright © 2008 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _DRM_DP_HELPER_H_
+#define _DRM_DP_HELPER_H_
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <drm/drm_connector.h>
+
+struct drm_device;
+struct drm_dp_aux;
+struct drm_panel;
+
+/*
+ * 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,
+ * 1.0 devices basically don't exist in the wild.
+ *
+ * Abbreviations, in chronological order:
+ *
+ * eDP: Embedded DisplayPort version 1
+ * DPI: DisplayPort Interoperability Guideline v1.1a
+ * 1.2: DisplayPort 1.2
+ * MST: Multistream Transport - part of DP 1.2a
+ *
+ * 1.2 formally includes both eDP and DPI definitions.
+ */
+
+/* MSA (Main Stream Attribute) MISC bits (as MISC1<<8|MISC0) */
+#define DP_MSA_MISC_SYNC_CLOCK                 (1 << 0)
+#define DP_MSA_MISC_INTERLACE_VTOTAL_EVEN      (1 << 8)
+#define DP_MSA_MISC_STEREO_NO_3D               (0 << 9)
+#define DP_MSA_MISC_STEREO_PROG_RIGHT_EYE      (1 << 9)
+#define DP_MSA_MISC_STEREO_PROG_LEFT_EYE       (3 << 9)
+/* bits per component for non-RAW */
+#define DP_MSA_MISC_6_BPC                      (0 << 5)
+#define DP_MSA_MISC_8_BPC                      (1 << 5)
+#define DP_MSA_MISC_10_BPC                     (2 << 5)
+#define DP_MSA_MISC_12_BPC                     (3 << 5)
+#define DP_MSA_MISC_16_BPC                     (4 << 5)
+/* bits per component for RAW */
+#define DP_MSA_MISC_RAW_6_BPC                  (1 << 5)
+#define DP_MSA_MISC_RAW_7_BPC                  (2 << 5)
+#define DP_MSA_MISC_RAW_8_BPC                  (3 << 5)
+#define DP_MSA_MISC_RAW_10_BPC                 (4 << 5)
+#define DP_MSA_MISC_RAW_12_BPC                 (5 << 5)
+#define DP_MSA_MISC_RAW_14_BPC                 (6 << 5)
+#define DP_MSA_MISC_RAW_16_BPC                 (7 << 5)
+/* pixel encoding/colorimetry format */
+#define _DP_MSA_MISC_COLOR(misc1_7, misc0_21, misc0_3, misc0_4) \
+       ((misc1_7) << 15 | (misc0_4) << 4 | (misc0_3) << 3 | ((misc0_21) << 1))
+#define DP_MSA_MISC_COLOR_RGB                  _DP_MSA_MISC_COLOR(0, 0, 0, 0)
+#define DP_MSA_MISC_COLOR_CEA_RGB              _DP_MSA_MISC_COLOR(0, 0, 1, 0)
+#define DP_MSA_MISC_COLOR_RGB_WIDE_FIXED       _DP_MSA_MISC_COLOR(0, 3, 0, 0)
+#define DP_MSA_MISC_COLOR_RGB_WIDE_FLOAT       _DP_MSA_MISC_COLOR(0, 3, 0, 1)
+#define DP_MSA_MISC_COLOR_Y_ONLY               _DP_MSA_MISC_COLOR(1, 0, 0, 0)
+#define DP_MSA_MISC_COLOR_RAW                  _DP_MSA_MISC_COLOR(1, 1, 0, 0)
+#define DP_MSA_MISC_COLOR_YCBCR_422_BT601      _DP_MSA_MISC_COLOR(0, 1, 1, 0)
+#define DP_MSA_MISC_COLOR_YCBCR_422_BT709      _DP_MSA_MISC_COLOR(0, 1, 1, 1)
+#define DP_MSA_MISC_COLOR_YCBCR_444_BT601      _DP_MSA_MISC_COLOR(0, 2, 1, 0)
+#define DP_MSA_MISC_COLOR_YCBCR_444_BT709      _DP_MSA_MISC_COLOR(0, 2, 1, 1)
+#define DP_MSA_MISC_COLOR_XVYCC_422_BT601      _DP_MSA_MISC_COLOR(0, 1, 0, 0)
+#define DP_MSA_MISC_COLOR_XVYCC_422_BT709      _DP_MSA_MISC_COLOR(0, 1, 0, 1)
+#define DP_MSA_MISC_COLOR_XVYCC_444_BT601      _DP_MSA_MISC_COLOR(0, 2, 0, 0)
+#define DP_MSA_MISC_COLOR_XVYCC_444_BT709      _DP_MSA_MISC_COLOR(0, 2, 0, 1)
+#define DP_MSA_MISC_COLOR_OPRGB                        _DP_MSA_MISC_COLOR(0, 0, 1, 1)
+#define DP_MSA_MISC_COLOR_DCI_P3               _DP_MSA_MISC_COLOR(0, 3, 1, 0)
+#define DP_MSA_MISC_COLOR_COLOR_PROFILE                _DP_MSA_MISC_COLOR(0, 3, 1, 1)
+#define DP_MSA_MISC_COLOR_VSC_SDP              (1 << 14)
+
+#define DP_AUX_MAX_PAYLOAD_BYTES       16
+
+#define DP_AUX_I2C_WRITE               0x0
+#define DP_AUX_I2C_READ                        0x1
+#define DP_AUX_I2C_WRITE_STATUS_UPDATE 0x2
+#define DP_AUX_I2C_MOT                 0x4
+#define DP_AUX_NATIVE_WRITE            0x8
+#define DP_AUX_NATIVE_READ             0x9
+
+#define DP_AUX_NATIVE_REPLY_ACK                (0x0 << 0)
+#define DP_AUX_NATIVE_REPLY_NACK       (0x1 << 0)
+#define DP_AUX_NATIVE_REPLY_DEFER      (0x2 << 0)
+#define DP_AUX_NATIVE_REPLY_MASK       (0x3 << 0)
+
+#define DP_AUX_I2C_REPLY_ACK           (0x0 << 2)
+#define DP_AUX_I2C_REPLY_NACK          (0x1 << 2)
+#define DP_AUX_I2C_REPLY_DEFER         (0x2 << 2)
+#define DP_AUX_I2C_REPLY_MASK          (0x3 << 2)
+
+/* DPCD Field Address Mapping */
+
+/* Receiver Capability */
+#define DP_DPCD_REV                         0x000
+# define DP_DPCD_REV_10                     0x10
+# define DP_DPCD_REV_11                     0x11
+# define DP_DPCD_REV_12                     0x12
+# define DP_DPCD_REV_13                     0x13
+# define DP_DPCD_REV_14                     0x14
+
+#define DP_MAX_LINK_RATE                    0x001
+
+#define DP_MAX_LANE_COUNT                   0x002
+# define DP_MAX_LANE_COUNT_MASK                    0x1f
+# define DP_TPS3_SUPPORTED                 (1 << 6) /* 1.2 */
+# define DP_ENHANCED_FRAME_CAP             (1 << 7)
+
+#define DP_MAX_DOWNSPREAD                   0x003
+# define DP_MAX_DOWNSPREAD_0_5             (1 << 0)
+# define DP_STREAM_REGENERATION_STATUS_CAP  (1 << 1) /* 2.0 */
+# define DP_NO_AUX_HANDSHAKE_LINK_TRAINING  (1 << 6)
+# define DP_TPS4_SUPPORTED                  (1 << 7)
+
+#define DP_NORP                             0x004
+
+#define DP_DOWNSTREAMPORT_PRESENT           0x005
+# define DP_DWN_STRM_PORT_PRESENT           (1 << 0)
+# define DP_DWN_STRM_PORT_TYPE_MASK         0x06
+# define DP_DWN_STRM_PORT_TYPE_DP           (0 << 1)
+# define DP_DWN_STRM_PORT_TYPE_ANALOG       (1 << 1)
+# define DP_DWN_STRM_PORT_TYPE_TMDS         (2 << 1)
+# define DP_DWN_STRM_PORT_TYPE_OTHER        (3 << 1)
+# define DP_FORMAT_CONVERSION               (1 << 3)
+# define DP_DETAILED_CAP_INFO_AVAILABLE            (1 << 4) /* DPI */
+
+#define DP_MAIN_LINK_CHANNEL_CODING         0x006
+# define DP_CAP_ANSI_8B10B                 (1 << 0)
+# define DP_CAP_ANSI_128B132B               (1 << 1) /* 2.0 */
+
+#define DP_DOWN_STREAM_PORT_COUNT          0x007
+# define DP_PORT_COUNT_MASK                0x0f
+# define DP_MSA_TIMING_PAR_IGNORED         (1 << 6) /* eDP */
+# define DP_OUI_SUPPORT                            (1 << 7)
+
+#define DP_RECEIVE_PORT_0_CAP_0                    0x008
+# define DP_LOCAL_EDID_PRESENT             (1 << 1)
+# define DP_ASSOCIATED_TO_PRECEDING_PORT    (1 << 2)
+
+#define DP_RECEIVE_PORT_0_BUFFER_SIZE      0x009
+
+#define DP_RECEIVE_PORT_1_CAP_0                    0x00a
+#define DP_RECEIVE_PORT_1_BUFFER_SIZE       0x00b
+
+#define DP_I2C_SPEED_CAP                   0x00c    /* DPI */
+# define DP_I2C_SPEED_1K                   0x01
+# define DP_I2C_SPEED_5K                   0x02
+# define DP_I2C_SPEED_10K                  0x04
+# define DP_I2C_SPEED_100K                 0x08
+# define DP_I2C_SPEED_400K                 0x10
+# define DP_I2C_SPEED_1M                   0x20
+
+#define DP_EDP_CONFIGURATION_CAP            0x00d   /* XXX 1.2? */
+# define DP_ALTERNATE_SCRAMBLER_RESET_CAP   (1 << 0)
+# define DP_FRAMING_CHANGE_CAP             (1 << 1)
+# define DP_DPCD_DISPLAY_CONTROL_CAPABLE     (1 << 3) /* edp v1.2 or higher */
+
+#define DP_TRAINING_AUX_RD_INTERVAL             0x00e   /* XXX 1.2? */
+# define DP_TRAINING_AUX_RD_MASK                0x7F    /* DP 1.3 */
+# define DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT        (1 << 7) /* DP 1.3 */
+
+#define DP_ADAPTER_CAP                     0x00f   /* 1.2 */
+# define DP_FORCE_LOAD_SENSE_CAP           (1 << 0)
+# define DP_ALTERNATE_I2C_PATTERN_CAP      (1 << 1)
+
+#define DP_SUPPORTED_LINK_RATES                    0x010 /* eDP 1.4 */
+# define DP_MAX_SUPPORTED_RATES                     8      /* 16-bit little-endian */
+
+/* Multiple stream transport */
+#define DP_FAUX_CAP                        0x020   /* 1.2 */
+# define DP_FAUX_CAP_1                     (1 << 0)
+
+#define DP_SINK_VIDEO_FALLBACK_FORMATS      0x020   /* 2.0 */
+# define DP_FALLBACK_1024x768_60HZ_24BPP    (1 << 0)
+# define DP_FALLBACK_1280x720_60HZ_24BPP    (1 << 1)
+# define DP_FALLBACK_1920x1080_60HZ_24BPP   (1 << 2)
+
+#define DP_MSTM_CAP                        0x021   /* 1.2 */
+# define DP_MST_CAP                        (1 << 0)
+# define DP_SINGLE_STREAM_SIDEBAND_MSG      (1 << 1) /* 2.0 */
+
+#define DP_NUMBER_OF_AUDIO_ENDPOINTS       0x022   /* 1.2 */
+
+/* AV_SYNC_DATA_BLOCK                                  1.2 */
+#define DP_AV_GRANULARITY                  0x023
+# define DP_AG_FACTOR_MASK                 (0xf << 0)
+# define DP_AG_FACTOR_3MS                  (0 << 0)
+# define DP_AG_FACTOR_2MS                  (1 << 0)
+# define DP_AG_FACTOR_1MS                  (2 << 0)
+# define DP_AG_FACTOR_500US                (3 << 0)
+# define DP_AG_FACTOR_200US                (4 << 0)
+# define DP_AG_FACTOR_100US                (5 << 0)
+# define DP_AG_FACTOR_10US                 (6 << 0)
+# define DP_AG_FACTOR_1US                  (7 << 0)
+# define DP_VG_FACTOR_MASK                 (0xf << 4)
+# define DP_VG_FACTOR_3MS                  (0 << 4)
+# define DP_VG_FACTOR_2MS                  (1 << 4)
+# define DP_VG_FACTOR_1MS                  (2 << 4)
+# define DP_VG_FACTOR_500US                (3 << 4)
+# define DP_VG_FACTOR_200US                (4 << 4)
+# define DP_VG_FACTOR_100US                (5 << 4)
+
+#define DP_AUD_DEC_LAT0                            0x024
+#define DP_AUD_DEC_LAT1                            0x025
+
+#define DP_AUD_PP_LAT0                     0x026
+#define DP_AUD_PP_LAT1                     0x027
+
+#define DP_VID_INTER_LAT                   0x028
+
+#define DP_VID_PROG_LAT                            0x029
+
+#define DP_REP_LAT                         0x02a
+
+#define DP_AUD_DEL_INS0                            0x02b
+#define DP_AUD_DEL_INS1                            0x02c
+#define DP_AUD_DEL_INS2                            0x02d
+/* End of AV_SYNC_DATA_BLOCK */
+
+#define DP_RECEIVER_ALPM_CAP               0x02e   /* eDP 1.4 */
+# define DP_ALPM_CAP                       (1 << 0)
+
+#define DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP   0x02f   /* eDP 1.4 */
+# define DP_AUX_FRAME_SYNC_CAP             (1 << 0)
+
+#define DP_GUID                                    0x030   /* 1.2 */
+
+#define DP_DSC_SUPPORT                      0x060   /* DP 1.4 */
+# define DP_DSC_DECOMPRESSION_IS_SUPPORTED  (1 << 0)
+
+#define DP_DSC_REV                          0x061
+# define DP_DSC_MAJOR_MASK                  (0xf << 0)
+# define DP_DSC_MINOR_MASK                  (0xf << 4)
+# define DP_DSC_MAJOR_SHIFT                 0
+# define DP_DSC_MINOR_SHIFT                 4
+
+#define DP_DSC_RC_BUF_BLK_SIZE              0x062
+# define DP_DSC_RC_BUF_BLK_SIZE_1           0x0
+# define DP_DSC_RC_BUF_BLK_SIZE_4           0x1
+# define DP_DSC_RC_BUF_BLK_SIZE_16          0x2
+# define DP_DSC_RC_BUF_BLK_SIZE_64          0x3
+
+#define DP_DSC_RC_BUF_SIZE                  0x063
+
+#define DP_DSC_SLICE_CAP_1                  0x064
+# define DP_DSC_1_PER_DP_DSC_SINK           (1 << 0)
+# define DP_DSC_2_PER_DP_DSC_SINK           (1 << 1)
+# define DP_DSC_4_PER_DP_DSC_SINK           (1 << 3)
+# define DP_DSC_6_PER_DP_DSC_SINK           (1 << 4)
+# define DP_DSC_8_PER_DP_DSC_SINK           (1 << 5)
+# define DP_DSC_10_PER_DP_DSC_SINK          (1 << 6)
+# define DP_DSC_12_PER_DP_DSC_SINK          (1 << 7)
+
+#define DP_DSC_LINE_BUF_BIT_DEPTH           0x065
+# define DP_DSC_LINE_BUF_BIT_DEPTH_MASK     (0xf << 0)
+# define DP_DSC_LINE_BUF_BIT_DEPTH_9        0x0
+# define DP_DSC_LINE_BUF_BIT_DEPTH_10       0x1
+# define DP_DSC_LINE_BUF_BIT_DEPTH_11       0x2
+# define DP_DSC_LINE_BUF_BIT_DEPTH_12       0x3
+# define DP_DSC_LINE_BUF_BIT_DEPTH_13       0x4
+# define DP_DSC_LINE_BUF_BIT_DEPTH_14       0x5
+# define DP_DSC_LINE_BUF_BIT_DEPTH_15       0x6
+# define DP_DSC_LINE_BUF_BIT_DEPTH_16       0x7
+# define DP_DSC_LINE_BUF_BIT_DEPTH_8        0x8
+
+#define DP_DSC_BLK_PREDICTION_SUPPORT       0x066
+# define DP_DSC_BLK_PREDICTION_IS_SUPPORTED (1 << 0)
+
+#define DP_DSC_MAX_BITS_PER_PIXEL_LOW       0x067   /* eDP 1.4 */
+
+#define DP_DSC_MAX_BITS_PER_PIXEL_HI        0x068   /* eDP 1.4 */
+# define DP_DSC_MAX_BITS_PER_PIXEL_HI_MASK  (0x3 << 0)
+# define DP_DSC_MAX_BITS_PER_PIXEL_HI_SHIFT 8
+
+#define DP_DSC_DEC_COLOR_FORMAT_CAP         0x069
+# define DP_DSC_RGB                         (1 << 0)
+# define DP_DSC_YCbCr444                    (1 << 1)
+# define DP_DSC_YCbCr422_Simple             (1 << 2)
+# define DP_DSC_YCbCr422_Native             (1 << 3)
+# define DP_DSC_YCbCr420_Native             (1 << 4)
+
+#define DP_DSC_DEC_COLOR_DEPTH_CAP          0x06A
+# define DP_DSC_8_BPC                       (1 << 1)
+# define DP_DSC_10_BPC                      (1 << 2)
+# define DP_DSC_12_BPC                      (1 << 3)
+
+#define DP_DSC_PEAK_THROUGHPUT              0x06B
+# define DP_DSC_THROUGHPUT_MODE_0_MASK      (0xf << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_SHIFT     0
+# define DP_DSC_THROUGHPUT_MODE_0_UNSUPPORTED 0
+# define DP_DSC_THROUGHPUT_MODE_0_340       (1 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_400       (2 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_450       (3 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_500       (4 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_550       (5 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_600       (6 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_650       (7 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_700       (8 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_750       (9 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_800       (10 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_850       (11 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_900       (12 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_950       (13 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_1000      (14 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_170       (15 << 0) /* 1.4a */
+# define DP_DSC_THROUGHPUT_MODE_1_MASK      (0xf << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_SHIFT     4
+# define DP_DSC_THROUGHPUT_MODE_1_UNSUPPORTED 0
+# define DP_DSC_THROUGHPUT_MODE_1_340       (1 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_400       (2 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_450       (3 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_500       (4 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_550       (5 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_600       (6 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_650       (7 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_700       (8 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_750       (9 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_800       (10 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_850       (11 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_900       (12 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_950       (13 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_1000      (14 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_170       (15 << 4)
+
+#define DP_DSC_MAX_SLICE_WIDTH              0x06C
+#define DP_DSC_MIN_SLICE_WIDTH_VALUE        2560
+#define DP_DSC_SLICE_WIDTH_MULTIPLIER       320
+
+#define DP_DSC_SLICE_CAP_2                  0x06D
+# define DP_DSC_16_PER_DP_DSC_SINK          (1 << 0)
+# define DP_DSC_20_PER_DP_DSC_SINK          (1 << 1)
+# define DP_DSC_24_PER_DP_DSC_SINK          (1 << 2)
+
+#define DP_DSC_BITS_PER_PIXEL_INC           0x06F
+# define DP_DSC_BITS_PER_PIXEL_1_16         0x0
+# define DP_DSC_BITS_PER_PIXEL_1_8          0x1
+# define DP_DSC_BITS_PER_PIXEL_1_4          0x2
+# define DP_DSC_BITS_PER_PIXEL_1_2          0x3
+# define DP_DSC_BITS_PER_PIXEL_1            0x4
+
+#define DP_PSR_SUPPORT                      0x070   /* XXX 1.2? */
+# define DP_PSR_IS_SUPPORTED                1
+# define DP_PSR2_IS_SUPPORTED              2       /* eDP 1.4 */
+# define DP_PSR2_WITH_Y_COORD_IS_SUPPORTED  3      /* eDP 1.4a */
+
+#define DP_PSR_CAPS                         0x071   /* XXX 1.2? */
+# define DP_PSR_NO_TRAIN_ON_EXIT            1
+# define DP_PSR_SETUP_TIME_330              (0 << 1)
+# define DP_PSR_SETUP_TIME_275              (1 << 1)
+# define DP_PSR_SETUP_TIME_220              (2 << 1)
+# define DP_PSR_SETUP_TIME_165              (3 << 1)
+# define DP_PSR_SETUP_TIME_110              (4 << 1)
+# define DP_PSR_SETUP_TIME_55               (5 << 1)
+# define DP_PSR_SETUP_TIME_0                (6 << 1)
+# define DP_PSR_SETUP_TIME_MASK             (7 << 1)
+# define DP_PSR_SETUP_TIME_SHIFT            1
+# define DP_PSR2_SU_Y_COORDINATE_REQUIRED   (1 << 4)  /* eDP 1.4a */
+# define DP_PSR2_SU_GRANULARITY_REQUIRED    (1 << 5)  /* eDP 1.4b */
+
+#define DP_PSR2_SU_X_GRANULARITY           0x072 /* eDP 1.4b */
+#define DP_PSR2_SU_Y_GRANULARITY           0x074 /* eDP 1.4b */
+
+/*
+ * 0x80-0x8f describe downstream port capabilities, but there are two layouts
+ * based on whether DP_DETAILED_CAP_INFO_AVAILABLE was set.  If it was not,
+ * each port's descriptor is one byte wide.  If it was set, each port's is
+ * four bytes wide, starting with the one byte from the base info.  As of
+ * DP interop v1.1a only VGA defines additional detail.
+ */
+
+/* offset 0 */
+#define DP_DOWNSTREAM_PORT_0               0x80
+# define DP_DS_PORT_TYPE_MASK              (7 << 0)
+# define DP_DS_PORT_TYPE_DP                0
+# define DP_DS_PORT_TYPE_VGA               1
+# define DP_DS_PORT_TYPE_DVI               2
+# define DP_DS_PORT_TYPE_HDMI              3
+# define DP_DS_PORT_TYPE_NON_EDID          4
+# 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 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
+/* HDMI2.1 PCON FRL CONFIGURATION */
+# define DP_PCON_MAX_FRL_BW                 (7 << 2)
+# define DP_PCON_MAX_0GBPS                  (0 << 2)
+# define DP_PCON_MAX_9GBPS                  (1 << 2)
+# define DP_PCON_MAX_18GBPS                 (2 << 2)
+# define DP_PCON_MAX_24GBPS                 (3 << 2)
+# define DP_PCON_MAX_32GBPS                 (4 << 2)
+# define DP_PCON_MAX_40GBPS                 (5 << 2)
+# define DP_PCON_MAX_48GBPS                 (6 << 2)
+# define DP_PCON_SOURCE_CTL_MODE            (1 << 5)
+
+/* 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)
+
+/*
+ * VESA DP-to-HDMI PCON Specification adds caps for colorspace
+ * conversion in DFP cap DPCD 83h. Sec6.1 Table-3.
+ * Based on the available support the source can enable
+ * color conversion by writing into PROTOCOL_COVERTER_CONTROL_2
+ * DPCD 3052h.
+ */
+# define DP_DS_HDMI_BT601_RGB_YCBCR_CONV    (1 << 5)
+# define DP_DS_HDMI_BT709_RGB_YCBCR_CONV    (1 << 6)
+# define DP_DS_HDMI_BT2020_RGB_YCBCR_CONV   (1 << 7)
+
+#define DP_MAX_DOWNSTREAM_PORTS                    0x10
+
+/* DP Forward error Correction Registers */
+#define DP_FEC_CAPABILITY                  0x090    /* 1.4 */
+# define DP_FEC_CAPABLE                            (1 << 0)
+# define DP_FEC_UNCORR_BLK_ERROR_COUNT_CAP  (1 << 1)
+# define DP_FEC_CORR_BLK_ERROR_COUNT_CAP    (1 << 2)
+# define DP_FEC_BIT_ERROR_COUNT_CAP        (1 << 3)
+#define DP_FEC_CAPABILITY_1                    0x091   /* 2.0 */
+
+/* DP-HDMI2.1 PCON DSC ENCODER SUPPORT */
+#define DP_PCON_DSC_ENCODER_CAP_SIZE        0xC        /* 0x9E - 0x92 */
+#define DP_PCON_DSC_ENCODER                 0x092
+# define DP_PCON_DSC_ENCODER_SUPPORTED      (1 << 0)
+# define DP_PCON_DSC_PPS_ENC_OVERRIDE       (1 << 1)
+
+/* DP-HDMI2.1 PCON DSC Version */
+#define DP_PCON_DSC_VERSION                 0x093
+# define DP_PCON_DSC_MAJOR_MASK                    (0xF << 0)
+# define DP_PCON_DSC_MINOR_MASK                    (0xF << 4)
+# define DP_PCON_DSC_MAJOR_SHIFT           0
+# define DP_PCON_DSC_MINOR_SHIFT           4
+
+/* DP-HDMI2.1 PCON DSC RC Buffer block size */
+#define DP_PCON_DSC_RC_BUF_BLK_INFO        0x094
+# define DP_PCON_DSC_RC_BUF_BLK_SIZE       (0x3 << 0)
+# define DP_PCON_DSC_RC_BUF_BLK_1KB        0
+# define DP_PCON_DSC_RC_BUF_BLK_4KB        1
+# define DP_PCON_DSC_RC_BUF_BLK_16KB       2
+# define DP_PCON_DSC_RC_BUF_BLK_64KB       3
+
+/* DP-HDMI2.1 PCON DSC RC Buffer size */
+#define DP_PCON_DSC_RC_BUF_SIZE                    0x095
+
+/* DP-HDMI2.1 PCON DSC Slice capabilities-1 */
+#define DP_PCON_DSC_SLICE_CAP_1                    0x096
+# define DP_PCON_DSC_1_PER_DSC_ENC     (0x1 << 0)
+# define DP_PCON_DSC_2_PER_DSC_ENC     (0x1 << 1)
+# define DP_PCON_DSC_4_PER_DSC_ENC     (0x1 << 3)
+# define DP_PCON_DSC_6_PER_DSC_ENC     (0x1 << 4)
+# define DP_PCON_DSC_8_PER_DSC_ENC     (0x1 << 5)
+# define DP_PCON_DSC_10_PER_DSC_ENC    (0x1 << 6)
+# define DP_PCON_DSC_12_PER_DSC_ENC    (0x1 << 7)
+
+#define DP_PCON_DSC_BUF_BIT_DEPTH          0x097
+# define DP_PCON_DSC_BIT_DEPTH_MASK        (0xF << 0)
+# define DP_PCON_DSC_DEPTH_9_BITS          0
+# define DP_PCON_DSC_DEPTH_10_BITS         1
+# define DP_PCON_DSC_DEPTH_11_BITS         2
+# define DP_PCON_DSC_DEPTH_12_BITS         3
+# define DP_PCON_DSC_DEPTH_13_BITS         4
+# define DP_PCON_DSC_DEPTH_14_BITS         5
+# define DP_PCON_DSC_DEPTH_15_BITS         6
+# define DP_PCON_DSC_DEPTH_16_BITS         7
+# define DP_PCON_DSC_DEPTH_8_BITS          8
+
+#define DP_PCON_DSC_BLOCK_PREDICTION       0x098
+# define DP_PCON_DSC_BLOCK_PRED_SUPPORT            (0x1 << 0)
+
+#define DP_PCON_DSC_ENC_COLOR_FMT_CAP      0x099
+# define DP_PCON_DSC_ENC_RGB               (0x1 << 0)
+# define DP_PCON_DSC_ENC_YUV444                    (0x1 << 1)
+# define DP_PCON_DSC_ENC_YUV422_S          (0x1 << 2)
+# define DP_PCON_DSC_ENC_YUV422_N          (0x1 << 3)
+# define DP_PCON_DSC_ENC_YUV420_N          (0x1 << 4)
+
+#define DP_PCON_DSC_ENC_COLOR_DEPTH_CAP            0x09A
+# define DP_PCON_DSC_ENC_8BPC              (0x1 << 1)
+# define DP_PCON_DSC_ENC_10BPC             (0x1 << 2)
+# define DP_PCON_DSC_ENC_12BPC             (0x1 << 3)
+
+#define DP_PCON_DSC_MAX_SLICE_WIDTH        0x09B
+
+/* DP-HDMI2.1 PCON DSC Slice capabilities-2 */
+#define DP_PCON_DSC_SLICE_CAP_2             0x09C
+# define DP_PCON_DSC_16_PER_DSC_ENC        (0x1 << 0)
+# define DP_PCON_DSC_20_PER_DSC_ENC         (0x1 << 1)
+# define DP_PCON_DSC_24_PER_DSC_ENC         (0x1 << 2)
+
+/* DP-HDMI2.1 PCON HDMI TX Encoder Bits/pixel increment */
+#define DP_PCON_DSC_BPP_INCR               0x09E
+# define DP_PCON_DSC_BPP_INCR_MASK         (0x7 << 0)
+# define DP_PCON_DSC_ONE_16TH_BPP          0
+# define DP_PCON_DSC_ONE_8TH_BPP           1
+# define DP_PCON_DSC_ONE_4TH_BPP           2
+# define DP_PCON_DSC_ONE_HALF_BPP          3
+# define DP_PCON_DSC_ONE_BPP               4
+
+/* DP Extended DSC Capabilities */
+#define DP_DSC_BRANCH_OVERALL_THROUGHPUT_0  0x0a0   /* DP 1.4a SCR */
+#define DP_DSC_BRANCH_OVERALL_THROUGHPUT_1  0x0a1
+#define DP_DSC_BRANCH_MAX_LINE_WIDTH        0x0a2
+
+/* DFP Capability Extension */
+#define DP_DFP_CAPABILITY_EXTENSION_SUPPORT    0x0a3   /* 2.0 */
+
+/* Link Configuration */
+#define        DP_LINK_BW_SET                      0x100
+# define DP_LINK_RATE_TABLE                0x00    /* eDP 1.4 */
+# define DP_LINK_BW_1_62                   0x06
+# define DP_LINK_BW_2_7                            0x0a
+# define DP_LINK_BW_5_4                            0x14    /* 1.2 */
+# define DP_LINK_BW_8_1                            0x1e    /* 1.4 */
+# define DP_LINK_BW_10                      0x01    /* 2.0 128b/132b Link Layer */
+# define DP_LINK_BW_13_5                    0x04    /* 2.0 128b/132b Link Layer */
+# define DP_LINK_BW_20                      0x02    /* 2.0 128b/132b Link Layer */
+
+#define DP_LANE_COUNT_SET                  0x101
+# define DP_LANE_COUNT_MASK                0x0f
+# define DP_LANE_COUNT_ENHANCED_FRAME_EN    (1 << 7)
+
+#define DP_TRAINING_PATTERN_SET                    0x102
+# define DP_TRAINING_PATTERN_DISABLE       0
+# define DP_TRAINING_PATTERN_1             1
+# define DP_TRAINING_PATTERN_2             2
+# define DP_TRAINING_PATTERN_3             3       /* 1.2 */
+# define DP_TRAINING_PATTERN_4              7       /* 1.4 */
+# define DP_TRAINING_PATTERN_MASK          0x3
+# define DP_TRAINING_PATTERN_MASK_1_4      0xf
+
+/* DPCD 1.1 only. For DPCD >= 1.2 see per-lane DP_LINK_QUAL_LANEn_SET */
+# define DP_LINK_QUAL_PATTERN_11_DISABLE    (0 << 2)
+# define DP_LINK_QUAL_PATTERN_11_D10_2     (1 << 2)
+# define DP_LINK_QUAL_PATTERN_11_ERROR_RATE (2 << 2)
+# define DP_LINK_QUAL_PATTERN_11_PRBS7     (3 << 2)
+# define DP_LINK_QUAL_PATTERN_11_MASK      (3 << 2)
+
+# define DP_RECOVERED_CLOCK_OUT_EN         (1 << 4)
+# define DP_LINK_SCRAMBLING_DISABLE        (1 << 5)
+
+# define DP_SYMBOL_ERROR_COUNT_BOTH        (0 << 6)
+# define DP_SYMBOL_ERROR_COUNT_DISPARITY    (1 << 6)
+# define DP_SYMBOL_ERROR_COUNT_SYMBOL      (2 << 6)
+# define DP_SYMBOL_ERROR_COUNT_MASK        (3 << 6)
+
+#define DP_TRAINING_LANE0_SET              0x103
+#define DP_TRAINING_LANE1_SET              0x104
+#define DP_TRAINING_LANE2_SET              0x105
+#define DP_TRAINING_LANE3_SET              0x106
+
+# define DP_TRAIN_VOLTAGE_SWING_MASK       0x3
+# define DP_TRAIN_VOLTAGE_SWING_SHIFT      0
+# define DP_TRAIN_MAX_SWING_REACHED        (1 << 2)
+# define DP_TRAIN_VOLTAGE_SWING_LEVEL_0 (0 << 0)
+# define DP_TRAIN_VOLTAGE_SWING_LEVEL_1 (1 << 0)
+# define DP_TRAIN_VOLTAGE_SWING_LEVEL_2 (2 << 0)
+# define DP_TRAIN_VOLTAGE_SWING_LEVEL_3 (3 << 0)
+
+# define DP_TRAIN_PRE_EMPHASIS_MASK        (3 << 3)
+# define DP_TRAIN_PRE_EMPH_LEVEL_0             (0 << 3)
+# define DP_TRAIN_PRE_EMPH_LEVEL_1             (1 << 3)
+# define DP_TRAIN_PRE_EMPH_LEVEL_2             (2 << 3)
+# define DP_TRAIN_PRE_EMPH_LEVEL_3             (3 << 3)
+
+# define DP_TRAIN_PRE_EMPHASIS_SHIFT       3
+# define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED  (1 << 5)
+
+# define DP_TX_FFE_PRESET_VALUE_MASK        (0xf << 0) /* 2.0 128b/132b Link Layer */
+
+#define DP_DOWNSPREAD_CTRL                 0x107
+# define DP_SPREAD_AMP_0_5                 (1 << 4)
+# define DP_MSA_TIMING_PAR_IGNORE_EN       (1 << 7) /* eDP */
+
+#define DP_MAIN_LINK_CHANNEL_CODING_SET            0x108
+# define DP_SET_ANSI_8B10B                 (1 << 0)
+# define DP_SET_ANSI_128B132B               (1 << 1)
+
+#define DP_I2C_SPEED_CONTROL_STATUS        0x109   /* DPI */
+/* bitmask as for DP_I2C_SPEED_CAP */
+
+#define DP_EDP_CONFIGURATION_SET            0x10a   /* XXX 1.2? */
+# define DP_ALTERNATE_SCRAMBLER_RESET_ENABLE (1 << 0)
+# define DP_FRAMING_CHANGE_ENABLE          (1 << 1)
+# define DP_PANEL_SELF_TEST_ENABLE         (1 << 7)
+
+#define DP_LINK_QUAL_LANE0_SET             0x10b   /* DPCD >= 1.2 */
+#define DP_LINK_QUAL_LANE1_SET             0x10c
+#define DP_LINK_QUAL_LANE2_SET             0x10d
+#define DP_LINK_QUAL_LANE3_SET             0x10e
+# define DP_LINK_QUAL_PATTERN_DISABLE      0
+# define DP_LINK_QUAL_PATTERN_D10_2        1
+# define DP_LINK_QUAL_PATTERN_ERROR_RATE    2
+# define DP_LINK_QUAL_PATTERN_PRBS7        3
+# define DP_LINK_QUAL_PATTERN_80BIT_CUSTOM  4
+# define DP_LINK_QUAL_PATTERN_CP2520_PAT_1  5
+# define DP_LINK_QUAL_PATTERN_CP2520_PAT_2  6
+# define DP_LINK_QUAL_PATTERN_CP2520_PAT_3  7
+/* DP 2.0 UHBR10, UHBR13.5, UHBR20 */
+# define DP_LINK_QUAL_PATTERN_128B132B_TPS1 0x08
+# define DP_LINK_QUAL_PATTERN_128B132B_TPS2 0x10
+# define DP_LINK_QUAL_PATTERN_PRSBS9        0x18
+# define DP_LINK_QUAL_PATTERN_PRSBS11       0x20
+# define DP_LINK_QUAL_PATTERN_PRSBS15       0x28
+# define DP_LINK_QUAL_PATTERN_PRSBS23       0x30
+# define DP_LINK_QUAL_PATTERN_PRSBS31       0x38
+# define DP_LINK_QUAL_PATTERN_CUSTOM        0x40
+# define DP_LINK_QUAL_PATTERN_SQUARE        0x48
+
+#define DP_TRAINING_LANE0_1_SET2           0x10f
+#define DP_TRAINING_LANE2_3_SET2           0x110
+# define DP_LANE02_POST_CURSOR2_SET_MASK    (3 << 0)
+# define DP_LANE02_MAX_POST_CURSOR2_REACHED (1 << 2)
+# define DP_LANE13_POST_CURSOR2_SET_MASK    (3 << 4)
+# define DP_LANE13_MAX_POST_CURSOR2_REACHED (1 << 6)
+
+#define DP_MSTM_CTRL                       0x111   /* 1.2 */
+# define DP_MST_EN                         (1 << 0)
+# define DP_UP_REQ_EN                      (1 << 1)
+# define DP_UPSTREAM_IS_SRC                (1 << 2)
+
+#define DP_AUDIO_DELAY0                            0x112   /* 1.2 */
+#define DP_AUDIO_DELAY1                            0x113
+#define DP_AUDIO_DELAY2                            0x114
+
+#define DP_LINK_RATE_SET                   0x115   /* eDP 1.4 */
+# define DP_LINK_RATE_SET_SHIFT                    0
+# define DP_LINK_RATE_SET_MASK             (7 << 0)
+
+#define DP_RECEIVER_ALPM_CONFIG                    0x116   /* eDP 1.4 */
+# define DP_ALPM_ENABLE                            (1 << 0)
+# define DP_ALPM_LOCK_ERROR_IRQ_HPD_ENABLE  (1 << 1)
+
+#define DP_SINK_DEVICE_AUX_FRAME_SYNC_CONF  0x117   /* eDP 1.4 */
+# define DP_AUX_FRAME_SYNC_ENABLE          (1 << 0)
+# define DP_IRQ_HPD_ENABLE                 (1 << 1)
+
+#define DP_UPSTREAM_DEVICE_DP_PWR_NEED     0x118   /* 1.2 */
+# define DP_PWR_NOT_NEEDED                 (1 << 0)
+
+#define DP_FEC_CONFIGURATION               0x120    /* 1.4 */
+# define DP_FEC_READY                      (1 << 0)
+# define DP_FEC_ERR_COUNT_SEL_MASK         (7 << 1)
+# define DP_FEC_ERR_COUNT_DIS              (0 << 1)
+# define DP_FEC_UNCORR_BLK_ERROR_COUNT     (1 << 1)
+# define DP_FEC_CORR_BLK_ERROR_COUNT       (2 << 1)
+# define DP_FEC_BIT_ERROR_COUNT                    (3 << 1)
+# define DP_FEC_LANE_SELECT_MASK           (3 << 4)
+# define DP_FEC_LANE_0_SELECT              (0 << 4)
+# define DP_FEC_LANE_1_SELECT              (1 << 4)
+# define DP_FEC_LANE_2_SELECT              (2 << 4)
+# define DP_FEC_LANE_3_SELECT              (3 << 4)
+
+#define DP_AUX_FRAME_SYNC_VALUE                    0x15c   /* eDP 1.4 */
+# define DP_AUX_FRAME_SYNC_VALID           (1 << 0)
+
+#define DP_DSC_ENABLE                       0x160   /* DP 1.4 */
+# define DP_DECOMPRESSION_EN                (1 << 0)
+#define DP_DSC_CONFIGURATION                           0x161   /* DP 2.0 */
+
+#define DP_PSR_EN_CFG                          0x170   /* XXX 1.2? */
+# define DP_PSR_ENABLE                         BIT(0)
+# define DP_PSR_MAIN_LINK_ACTIVE               BIT(1)
+# define DP_PSR_CRC_VERIFICATION               BIT(2)
+# define DP_PSR_FRAME_CAPTURE                  BIT(3)
+# define DP_PSR_SU_REGION_SCANLINE_CAPTURE     BIT(4) /* eDP 1.4a */
+# define DP_PSR_IRQ_HPD_WITH_CRC_ERRORS                BIT(5) /* eDP 1.4a */
+# define DP_PSR_ENABLE_PSR2                    BIT(6) /* eDP 1.4a */
+
+#define DP_ADAPTER_CTRL                            0x1a0
+# define DP_ADAPTER_CTRL_FORCE_LOAD_SENSE   (1 << 0)
+
+#define DP_BRANCH_DEVICE_CTRL              0x1a1
+# define DP_BRANCH_DEVICE_IRQ_HPD          (1 << 0)
+
+#define DP_PAYLOAD_ALLOCATE_SET                    0x1c0
+#define DP_PAYLOAD_ALLOCATE_START_TIME_SLOT 0x1c1
+#define DP_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT 0x1c2
+
+/* Link/Sink Device Status */
+#define DP_SINK_COUNT                      0x200
+/* prior to 1.2 bit 7 was reserved mbz */
+# define DP_GET_SINK_COUNT(x)              ((((x) & 0x80) >> 1) | ((x) & 0x3f))
+# define DP_SINK_CP_READY                  (1 << 6)
+
+#define DP_DEVICE_SERVICE_IRQ_VECTOR       0x201
+# define DP_REMOTE_CONTROL_COMMAND_PENDING  (1 << 0)
+# define DP_AUTOMATED_TEST_REQUEST         (1 << 1)
+# define DP_CP_IRQ                         (1 << 2)
+# define DP_MCCS_IRQ                       (1 << 3)
+# define DP_DOWN_REP_MSG_RDY               (1 << 4) /* 1.2 MST */
+# define DP_UP_REQ_MSG_RDY                 (1 << 5) /* 1.2 MST */
+# define DP_SINK_SPECIFIC_IRQ              (1 << 6)
+
+#define DP_LANE0_1_STATUS                  0x202
+#define DP_LANE2_3_STATUS                  0x203
+# define DP_LANE_CR_DONE                   (1 << 0)
+# define DP_LANE_CHANNEL_EQ_DONE           (1 << 1)
+# define DP_LANE_SYMBOL_LOCKED             (1 << 2)
+
+#define DP_CHANNEL_EQ_BITS (DP_LANE_CR_DONE |          \
+                           DP_LANE_CHANNEL_EQ_DONE |   \
+                           DP_LANE_SYMBOL_LOCKED)
+
+#define DP_LANE_ALIGN_STATUS_UPDATED       0x204
+
+#define DP_INTERLANE_ALIGN_DONE                    (1 << 0)
+#define DP_DOWNSTREAM_PORT_STATUS_CHANGED   (1 << 6)
+#define DP_LINK_STATUS_UPDATED             (1 << 7)
+
+#define DP_SINK_STATUS                     0x205
+# define DP_RECEIVE_PORT_0_STATUS          (1 << 0)
+# define DP_RECEIVE_PORT_1_STATUS          (1 << 1)
+# define DP_STREAM_REGENERATION_STATUS      (1 << 2) /* 2.0 */
+# define DP_INTRA_HOP_AUX_REPLY_INDICATION     (1 << 3) /* 2.0 */
+
+#define DP_ADJUST_REQUEST_LANE0_1          0x206
+#define DP_ADJUST_REQUEST_LANE2_3          0x207
+# define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK  0x03
+# define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0
+# define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK   0x0c
+# define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT  2
+# define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK  0x30
+# define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4
+# define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK   0xc0
+# define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT  6
+
+/* DP 2.0 128b/132b Link Layer */
+# define DP_ADJUST_TX_FFE_PRESET_LANE0_MASK  (0xf << 0)
+# define DP_ADJUST_TX_FFE_PRESET_LANE0_SHIFT 0
+# define DP_ADJUST_TX_FFE_PRESET_LANE1_MASK  (0xf << 4)
+# define DP_ADJUST_TX_FFE_PRESET_LANE1_SHIFT 4
+
+#define DP_ADJUST_REQUEST_POST_CURSOR2      0x20c
+# define DP_ADJUST_POST_CURSOR2_LANE0_MASK  0x03
+# define DP_ADJUST_POST_CURSOR2_LANE0_SHIFT 0
+# define DP_ADJUST_POST_CURSOR2_LANE1_MASK  0x0c
+# define DP_ADJUST_POST_CURSOR2_LANE1_SHIFT 2
+# define DP_ADJUST_POST_CURSOR2_LANE2_MASK  0x30
+# define DP_ADJUST_POST_CURSOR2_LANE2_SHIFT 4
+# define DP_ADJUST_POST_CURSOR2_LANE3_MASK  0xc0
+# define DP_ADJUST_POST_CURSOR2_LANE3_SHIFT 6
+
+#define DP_TEST_REQUEST                            0x218
+# define DP_TEST_LINK_TRAINING             (1 << 0)
+# define DP_TEST_LINK_VIDEO_PATTERN        (1 << 1)
+# define DP_TEST_LINK_EDID_READ                    (1 << 2)
+# define DP_TEST_LINK_PHY_TEST_PATTERN     (1 << 3) /* DPCD >= 1.1 */
+# define DP_TEST_LINK_FAUX_PATTERN         (1 << 4) /* DPCD >= 1.2 */
+# define DP_TEST_LINK_AUDIO_PATTERN         (1 << 5) /* DPCD >= 1.2 */
+# define DP_TEST_LINK_AUDIO_DISABLED_VIDEO  (1 << 6) /* DPCD >= 1.2 */
+
+#define DP_TEST_LINK_RATE                  0x219
+# define DP_LINK_RATE_162                  (0x6)
+# define DP_LINK_RATE_27                   (0xa)
+
+#define DP_TEST_LANE_COUNT                 0x220
+
+#define DP_TEST_PATTERN                            0x221
+# define DP_NO_TEST_PATTERN                 0x0
+# define DP_COLOR_RAMP                      0x1
+# define DP_BLACK_AND_WHITE_VERTICAL_LINES  0x2
+# define DP_COLOR_SQUARE                    0x3
+
+#define DP_TEST_H_TOTAL_HI                  0x222
+#define DP_TEST_H_TOTAL_LO                  0x223
+
+#define DP_TEST_V_TOTAL_HI                  0x224
+#define DP_TEST_V_TOTAL_LO                  0x225
+
+#define DP_TEST_H_START_HI                  0x226
+#define DP_TEST_H_START_LO                  0x227
+
+#define DP_TEST_V_START_HI                  0x228
+#define DP_TEST_V_START_LO                  0x229
+
+#define DP_TEST_HSYNC_HI                    0x22A
+# define DP_TEST_HSYNC_POLARITY             (1 << 7)
+# define DP_TEST_HSYNC_WIDTH_HI_MASK        (127 << 0)
+#define DP_TEST_HSYNC_WIDTH_LO              0x22B
+
+#define DP_TEST_VSYNC_HI                    0x22C
+# define DP_TEST_VSYNC_POLARITY             (1 << 7)
+# define DP_TEST_VSYNC_WIDTH_HI_MASK        (127 << 0)
+#define DP_TEST_VSYNC_WIDTH_LO              0x22D
+
+#define DP_TEST_H_WIDTH_HI                  0x22E
+#define DP_TEST_H_WIDTH_LO                  0x22F
+
+#define DP_TEST_V_HEIGHT_HI                 0x230
+#define DP_TEST_V_HEIGHT_LO                 0x231
+
+#define DP_TEST_MISC0                       0x232
+# define DP_TEST_SYNC_CLOCK                 (1 << 0)
+# define DP_TEST_COLOR_FORMAT_MASK          (3 << 1)
+# define DP_TEST_COLOR_FORMAT_SHIFT         1
+# define DP_COLOR_FORMAT_RGB                (0 << 1)
+# define DP_COLOR_FORMAT_YCbCr422           (1 << 1)
+# define DP_COLOR_FORMAT_YCbCr444           (2 << 1)
+# define DP_TEST_DYNAMIC_RANGE_VESA         (0 << 3)
+# define DP_TEST_DYNAMIC_RANGE_CEA          (1 << 3)
+# define DP_TEST_YCBCR_COEFFICIENTS         (1 << 4)
+# define DP_YCBCR_COEFFICIENTS_ITU601       (0 << 4)
+# define DP_YCBCR_COEFFICIENTS_ITU709       (1 << 4)
+# define DP_TEST_BIT_DEPTH_MASK             (7 << 5)
+# define DP_TEST_BIT_DEPTH_SHIFT            5
+# define DP_TEST_BIT_DEPTH_6                (0 << 5)
+# define DP_TEST_BIT_DEPTH_8                (1 << 5)
+# define DP_TEST_BIT_DEPTH_10               (2 << 5)
+# define DP_TEST_BIT_DEPTH_12               (3 << 5)
+# define DP_TEST_BIT_DEPTH_16               (4 << 5)
+
+#define DP_TEST_MISC1                       0x233
+# define DP_TEST_REFRESH_DENOMINATOR        (1 << 0)
+# define DP_TEST_INTERLACED                 (1 << 1)
+
+#define DP_TEST_REFRESH_RATE_NUMERATOR      0x234
+
+#define DP_TEST_MISC0                       0x232
+
+#define DP_TEST_CRC_R_CR                   0x240
+#define DP_TEST_CRC_G_Y                            0x242
+#define DP_TEST_CRC_B_CB                   0x244
+
+#define DP_TEST_SINK_MISC                  0x246
+# define DP_TEST_CRC_SUPPORTED             (1 << 5)
+# define DP_TEST_COUNT_MASK                0xf
+
+#define DP_PHY_TEST_PATTERN                 0x248
+# define DP_PHY_TEST_PATTERN_SEL_MASK       0x7
+# define DP_PHY_TEST_PATTERN_NONE           0x0
+# define DP_PHY_TEST_PATTERN_D10_2          0x1
+# define DP_PHY_TEST_PATTERN_ERROR_COUNT    0x2
+# define DP_PHY_TEST_PATTERN_PRBS7          0x3
+# define DP_PHY_TEST_PATTERN_80BIT_CUSTOM   0x4
+# define DP_PHY_TEST_PATTERN_CP2520         0x5
+
+#define DP_PHY_SQUARE_PATTERN                          0x249
+
+#define DP_TEST_HBR2_SCRAMBLER_RESET        0x24A
+#define DP_TEST_80BIT_CUSTOM_PATTERN_7_0    0x250
+#define        DP_TEST_80BIT_CUSTOM_PATTERN_15_8   0x251
+#define        DP_TEST_80BIT_CUSTOM_PATTERN_23_16  0x252
+#define        DP_TEST_80BIT_CUSTOM_PATTERN_31_24  0x253
+#define        DP_TEST_80BIT_CUSTOM_PATTERN_39_32  0x254
+#define        DP_TEST_80BIT_CUSTOM_PATTERN_47_40  0x255
+#define        DP_TEST_80BIT_CUSTOM_PATTERN_55_48  0x256
+#define        DP_TEST_80BIT_CUSTOM_PATTERN_63_56  0x257
+#define        DP_TEST_80BIT_CUSTOM_PATTERN_71_64  0x258
+#define        DP_TEST_80BIT_CUSTOM_PATTERN_79_72  0x259
+
+#define DP_TEST_RESPONSE                   0x260
+# define DP_TEST_ACK                       (1 << 0)
+# define DP_TEST_NAK                       (1 << 1)
+# define DP_TEST_EDID_CHECKSUM_WRITE       (1 << 2)
+
+#define DP_TEST_EDID_CHECKSUM              0x261
+
+#define DP_TEST_SINK                       0x270
+# define DP_TEST_SINK_START                (1 << 0)
+#define DP_TEST_AUDIO_MODE                 0x271
+#define DP_TEST_AUDIO_PATTERN_TYPE         0x272
+#define DP_TEST_AUDIO_PERIOD_CH1           0x273
+#define DP_TEST_AUDIO_PERIOD_CH2           0x274
+#define DP_TEST_AUDIO_PERIOD_CH3           0x275
+#define DP_TEST_AUDIO_PERIOD_CH4           0x276
+#define DP_TEST_AUDIO_PERIOD_CH5           0x277
+#define DP_TEST_AUDIO_PERIOD_CH6           0x278
+#define DP_TEST_AUDIO_PERIOD_CH7           0x279
+#define DP_TEST_AUDIO_PERIOD_CH8           0x27A
+
+#define DP_FEC_STATUS                      0x280    /* 1.4 */
+# define DP_FEC_DECODE_EN_DETECTED         (1 << 0)
+# define DP_FEC_DECODE_DIS_DETECTED        (1 << 1)
+
+#define DP_FEC_ERROR_COUNT_LSB             0x0281    /* 1.4 */
+
+#define DP_FEC_ERROR_COUNT_MSB             0x0282    /* 1.4 */
+# define DP_FEC_ERROR_COUNT_MASK           0x7F
+# define DP_FEC_ERR_COUNT_VALID                    (1 << 7)
+
+#define DP_PAYLOAD_TABLE_UPDATE_STATUS      0x2c0   /* 1.2 MST */
+# define DP_PAYLOAD_TABLE_UPDATED           (1 << 0)
+# define DP_PAYLOAD_ACT_HANDLED             (1 << 1)
+
+#define DP_VC_PAYLOAD_ID_SLOT_1             0x2c1   /* 1.2 MST */
+/* up to ID_SLOT_63 at 0x2ff */
+
+/* Source Device-specific */
+#define DP_SOURCE_OUI                      0x300
+
+/* Sink Device-specific */
+#define DP_SINK_OUI                        0x400
+
+/* Branch Device-specific */
+#define DP_BRANCH_OUI                      0x500
+#define DP_BRANCH_ID                        0x503
+#define DP_BRANCH_REVISION_START            0x509
+#define DP_BRANCH_HW_REV                    0x509
+#define DP_BRANCH_SW_REV                    0x50A
+
+/* Link/Sink Device Power Control */
+#define DP_SET_POWER                        0x600
+# define DP_SET_POWER_D0                    0x1
+# define DP_SET_POWER_D3                    0x2
+# define DP_SET_POWER_MASK                  0x3
+# define DP_SET_POWER_D3_AUX_ON             0x5
+
+/* eDP-specific */
+#define DP_EDP_DPCD_REV                            0x700    /* eDP 1.2 */
+# define DP_EDP_11                         0x00
+# define DP_EDP_12                         0x01
+# define DP_EDP_13                         0x02
+# define DP_EDP_14                         0x03
+# define DP_EDP_14a                         0x04    /* eDP 1.4a */
+# define DP_EDP_14b                         0x05    /* eDP 1.4b */
+
+#define DP_EDP_GENERAL_CAP_1               0x701
+# define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP          (1 << 0)
+# define DP_EDP_BACKLIGHT_PIN_ENABLE_CAP               (1 << 1)
+# define DP_EDP_BACKLIGHT_AUX_ENABLE_CAP               (1 << 2)
+# define DP_EDP_PANEL_SELF_TEST_PIN_ENABLE_CAP         (1 << 3)
+# define DP_EDP_PANEL_SELF_TEST_AUX_ENABLE_CAP         (1 << 4)
+# define DP_EDP_FRC_ENABLE_CAP                         (1 << 5)
+# define DP_EDP_COLOR_ENGINE_CAP                       (1 << 6)
+# define DP_EDP_SET_POWER_CAP                          (1 << 7)
+
+#define DP_EDP_BACKLIGHT_ADJUSTMENT_CAP     0x702
+# define DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP       (1 << 0)
+# define DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP       (1 << 1)
+# define DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT                (1 << 2)
+# define DP_EDP_BACKLIGHT_AUX_PWM_PRODUCT_CAP          (1 << 3)
+# define DP_EDP_BACKLIGHT_FREQ_PWM_PIN_PASSTHRU_CAP    (1 << 4)
+# define DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP             (1 << 5)
+# define DP_EDP_DYNAMIC_BACKLIGHT_CAP                  (1 << 6)
+# define DP_EDP_VBLANK_BACKLIGHT_UPDATE_CAP            (1 << 7)
+
+#define DP_EDP_GENERAL_CAP_2               0x703
+# define DP_EDP_OVERDRIVE_ENGINE_ENABLED               (1 << 0)
+
+#define DP_EDP_GENERAL_CAP_3               0x704    /* eDP 1.4 */
+# define DP_EDP_X_REGION_CAP_MASK                      (0xf << 0)
+# define DP_EDP_X_REGION_CAP_SHIFT                     0
+# define DP_EDP_Y_REGION_CAP_MASK                      (0xf << 4)
+# define DP_EDP_Y_REGION_CAP_SHIFT                     4
+
+#define DP_EDP_DISPLAY_CONTROL_REGISTER     0x720
+# define DP_EDP_BACKLIGHT_ENABLE                       (1 << 0)
+# define DP_EDP_BLACK_VIDEO_ENABLE                     (1 << 1)
+# define DP_EDP_FRC_ENABLE                             (1 << 2)
+# define DP_EDP_COLOR_ENGINE_ENABLE                    (1 << 3)
+# define DP_EDP_VBLANK_BACKLIGHT_UPDATE_ENABLE         (1 << 7)
+
+#define DP_EDP_BACKLIGHT_MODE_SET_REGISTER  0x721
+# define DP_EDP_BACKLIGHT_CONTROL_MODE_MASK            (3 << 0)
+# define DP_EDP_BACKLIGHT_CONTROL_MODE_PWM             (0 << 0)
+# define DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET          (1 << 0)
+# define DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD            (2 << 0)
+# define DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT         (3 << 0)
+# define DP_EDP_BACKLIGHT_FREQ_PWM_PIN_PASSTHRU_ENABLE (1 << 2)
+# define DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE          (1 << 3)
+# define DP_EDP_DYNAMIC_BACKLIGHT_ENABLE               (1 << 4)
+# define DP_EDP_REGIONAL_BACKLIGHT_ENABLE              (1 << 5)
+# define DP_EDP_UPDATE_REGION_BRIGHTNESS               (1 << 6) /* eDP 1.4 */
+
+#define DP_EDP_BACKLIGHT_BRIGHTNESS_MSB     0x722
+#define DP_EDP_BACKLIGHT_BRIGHTNESS_LSB     0x723
+
+#define DP_EDP_PWMGEN_BIT_COUNT             0x724
+#define DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN     0x725
+#define DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX     0x726
+# define DP_EDP_PWMGEN_BIT_COUNT_MASK       (0x1f << 0)
+
+#define DP_EDP_BACKLIGHT_CONTROL_STATUS     0x727
+
+#define DP_EDP_BACKLIGHT_FREQ_SET           0x728
+# define DP_EDP_BACKLIGHT_FREQ_BASE_KHZ     27000
+
+#define DP_EDP_BACKLIGHT_FREQ_CAP_MIN_MSB   0x72a
+#define DP_EDP_BACKLIGHT_FREQ_CAP_MIN_MID   0x72b
+#define DP_EDP_BACKLIGHT_FREQ_CAP_MIN_LSB   0x72c
+
+#define DP_EDP_BACKLIGHT_FREQ_CAP_MAX_MSB   0x72d
+#define DP_EDP_BACKLIGHT_FREQ_CAP_MAX_MID   0x72e
+#define DP_EDP_BACKLIGHT_FREQ_CAP_MAX_LSB   0x72f
+
+#define DP_EDP_DBC_MINIMUM_BRIGHTNESS_SET   0x732
+#define DP_EDP_DBC_MAXIMUM_BRIGHTNESS_SET   0x733
+
+#define DP_EDP_REGIONAL_BACKLIGHT_BASE      0x740    /* eDP 1.4 */
+#define DP_EDP_REGIONAL_BACKLIGHT_0        0x741    /* eDP 1.4 */
+
+#define DP_EDP_MSO_LINK_CAPABILITIES        0x7a4    /* eDP 1.4 */
+# define DP_EDP_MSO_NUMBER_OF_LINKS_MASK    (7 << 0)
+# define DP_EDP_MSO_NUMBER_OF_LINKS_SHIFT   0
+# define DP_EDP_MSO_INDEPENDENT_LINK_BIT    (1 << 3)
+
+/* Sideband MSG Buffers */
+#define DP_SIDEBAND_MSG_DOWN_REQ_BASE      0x1000   /* 1.2 MST */
+#define DP_SIDEBAND_MSG_UP_REP_BASE        0x1200   /* 1.2 MST */
+#define DP_SIDEBAND_MSG_DOWN_REP_BASE      0x1400   /* 1.2 MST */
+#define DP_SIDEBAND_MSG_UP_REQ_BASE        0x1600   /* 1.2 MST */
+
+/* DPRX Event Status Indicator */
+#define DP_SINK_COUNT_ESI                   0x2002   /* same as 0x200 */
+#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0   0x2003   /* same as 0x201 */
+
+#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1   0x2004   /* 1.2 */
+# define DP_RX_GTC_MSTR_REQ_STATUS_CHANGE    (1 << 0)
+# define DP_LOCK_ACQUISITION_REQUEST         (1 << 1)
+# define DP_CEC_IRQ                          (1 << 2)
+
+#define DP_LINK_SERVICE_IRQ_VECTOR_ESI0     0x2005   /* 1.2 */
+# define RX_CAP_CHANGED                      (1 << 0)
+# define LINK_STATUS_CHANGED                 (1 << 1)
+# define STREAM_STATUS_CHANGED               (1 << 2)
+# define HDMI_LINK_STATUS_CHANGED            (1 << 3)
+# define CONNECTED_OFF_ENTRY_REQUESTED       (1 << 4)
+
+#define DP_PSR_ERROR_STATUS                 0x2006  /* XXX 1.2? */
+# define DP_PSR_LINK_CRC_ERROR              (1 << 0)
+# define DP_PSR_RFB_STORAGE_ERROR           (1 << 1)
+# define DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR (1 << 2) /* eDP 1.4 */
+
+#define DP_PSR_ESI                          0x2007  /* XXX 1.2? */
+# define DP_PSR_CAPS_CHANGE                 (1 << 0)
+
+#define DP_PSR_STATUS                       0x2008  /* XXX 1.2? */
+# define DP_PSR_SINK_INACTIVE               0
+# define DP_PSR_SINK_ACTIVE_SRC_SYNCED      1
+# define DP_PSR_SINK_ACTIVE_RFB             2
+# define DP_PSR_SINK_ACTIVE_SINK_SYNCED     3
+# define DP_PSR_SINK_ACTIVE_RESYNC          4
+# define DP_PSR_SINK_INTERNAL_ERROR         7
+# define DP_PSR_SINK_STATE_MASK             0x07
+
+#define DP_SYNCHRONIZATION_LATENCY_IN_SINK             0x2009 /* edp 1.4 */
+# define DP_MAX_RESYNC_FRAME_COUNT_MASK                        (0xf << 0)
+# define DP_MAX_RESYNC_FRAME_COUNT_SHIFT               0
+# define DP_LAST_ACTUAL_SYNCHRONIZATION_LATENCY_MASK   (0xf << 4)
+# define DP_LAST_ACTUAL_SYNCHRONIZATION_LATENCY_SHIFT  4
+
+#define DP_LAST_RECEIVED_PSR_SDP           0x200a /* eDP 1.2 */
+# define DP_PSR_STATE_BIT                  (1 << 0) /* eDP 1.2 */
+# define DP_UPDATE_RFB_BIT                 (1 << 1) /* eDP 1.2 */
+# define DP_CRC_VALID_BIT                  (1 << 2) /* eDP 1.2 */
+# define DP_SU_VALID                       (1 << 3) /* eDP 1.4 */
+# define DP_FIRST_SCAN_LINE_SU_REGION      (1 << 4) /* eDP 1.4 */
+# define DP_LAST_SCAN_LINE_SU_REGION       (1 << 5) /* eDP 1.4 */
+# define DP_Y_COORDINATE_VALID             (1 << 6) /* eDP 1.4a */
+
+#define DP_RECEIVER_ALPM_STATUS                    0x200b  /* eDP 1.4 */
+# define DP_ALPM_LOCK_TIMEOUT_ERROR        (1 << 0)
+
+#define DP_LANE0_1_STATUS_ESI                  0x200c /* status same as 0x202 */
+#define DP_LANE2_3_STATUS_ESI                  0x200d /* status same as 0x203 */
+#define DP_LANE_ALIGN_STATUS_UPDATED_ESI       0x200e /* status same as 0x204 */
+#define DP_SINK_STATUS_ESI                     0x200f /* status same as 0x205 */
+
+/* Extended Receiver Capability: See DP_DPCD_REV for definitions */
+#define DP_DP13_DPCD_REV                    0x2200
+
+#define DP_DPRX_FEATURE_ENUMERATION_LIST    0x2210  /* DP 1.3 */
+# define DP_GTC_CAP                                    (1 << 0)  /* DP 1.3 */
+# define DP_SST_SPLIT_SDP_CAP                          (1 << 1)  /* DP 1.4 */
+# define DP_AV_SYNC_CAP                                        (1 << 2)  /* DP 1.3 */
+# define DP_VSC_SDP_EXT_FOR_COLORIMETRY_SUPPORTED      (1 << 3)  /* DP 1.3 */
+# define DP_VSC_EXT_VESA_SDP_SUPPORTED                 (1 << 4)  /* DP 1.4 */
+# define DP_VSC_EXT_VESA_SDP_CHAINING_SUPPORTED                (1 << 5)  /* DP 1.4 */
+# define DP_VSC_EXT_CEA_SDP_SUPPORTED                  (1 << 6)  /* DP 1.4 */
+# define DP_VSC_EXT_CEA_SDP_CHAINING_SUPPORTED         (1 << 7)  /* DP 1.4 */
+
+#define DP_128B132B_SUPPORTED_LINK_RATES       0x2215 /* 2.0 */
+# define DP_UHBR10                             (1 << 0)
+# define DP_UHBR20                             (1 << 1)
+# define DP_UHBR13_5                           (1 << 2)
+
+#define DP_128B132B_TRAINING_AUX_RD_INTERVAL                    0x2216 /* 2.0 */
+# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK              0x7f
+# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US            0x00
+# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS              0x01
+# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS              0x02
+# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS             0x03
+# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS             0x04
+# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS             0x05
+# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS             0x06
+
+#define DP_TEST_264BIT_CUSTOM_PATTERN_7_0              0x2230
+#define DP_TEST_264BIT_CUSTOM_PATTERN_263_256  0x2250
+
+/* DSC Extended Capability Branch Total DSC Resources */
+#define DP_DSC_SUPPORT_AND_DSC_DECODER_COUNT           0x2260  /* 2.0 */
+# define DP_DSC_DECODER_COUNT_MASK                     (0b111 << 5)
+# define DP_DSC_DECODER_COUNT_SHIFT                    5
+#define DP_DSC_MAX_SLICE_COUNT_AND_AGGREGATION_0       0x2270  /* 2.0 */
+# define DP_DSC_DECODER_0_MAXIMUM_SLICE_COUNT_MASK     (1 << 0)
+# define DP_DSC_DECODER_0_AGGREGATION_SUPPORT_MASK     (0b111 << 1)
+# define DP_DSC_DECODER_0_AGGREGATION_SUPPORT_SHIFT    1
+
+/* Protocol Converter Extension */
+/* HDMI CEC tunneling over AUX DP 1.3 section 5.3.3.3.1 DPCD 1.4+ */
+#define DP_CEC_TUNNELING_CAPABILITY            0x3000
+# define DP_CEC_TUNNELING_CAPABLE               (1 << 0)
+# define DP_CEC_SNOOPING_CAPABLE                (1 << 1)
+# define DP_CEC_MULTIPLE_LA_CAPABLE             (1 << 2)
+
+#define DP_CEC_TUNNELING_CONTROL               0x3001
+# define DP_CEC_TUNNELING_ENABLE                (1 << 0)
+# define DP_CEC_SNOOPING_ENABLE                 (1 << 1)
+
+#define DP_CEC_RX_MESSAGE_INFO                 0x3002
+# define DP_CEC_RX_MESSAGE_LEN_MASK             (0xf << 0)
+# define DP_CEC_RX_MESSAGE_LEN_SHIFT            0
+# define DP_CEC_RX_MESSAGE_HPD_STATE            (1 << 4)
+# define DP_CEC_RX_MESSAGE_HPD_LOST             (1 << 5)
+# define DP_CEC_RX_MESSAGE_ACKED                (1 << 6)
+# define DP_CEC_RX_MESSAGE_ENDED                (1 << 7)
+
+#define DP_CEC_TX_MESSAGE_INFO                 0x3003
+# define DP_CEC_TX_MESSAGE_LEN_MASK             (0xf << 0)
+# define DP_CEC_TX_MESSAGE_LEN_SHIFT            0
+# define DP_CEC_TX_RETRY_COUNT_MASK             (0x7 << 4)
+# define DP_CEC_TX_RETRY_COUNT_SHIFT            4
+# define DP_CEC_TX_MESSAGE_SEND                 (1 << 7)
+
+#define DP_CEC_TUNNELING_IRQ_FLAGS             0x3004
+# define DP_CEC_RX_MESSAGE_INFO_VALID           (1 << 0)
+# define DP_CEC_RX_MESSAGE_OVERFLOW             (1 << 1)
+# define DP_CEC_TX_MESSAGE_SENT                 (1 << 4)
+# define DP_CEC_TX_LINE_ERROR                   (1 << 5)
+# define DP_CEC_TX_ADDRESS_NACK_ERROR           (1 << 6)
+# define DP_CEC_TX_DATA_NACK_ERROR              (1 << 7)
+
+#define DP_CEC_LOGICAL_ADDRESS_MASK            0x300E /* 0x300F word */
+# define DP_CEC_LOGICAL_ADDRESS_0               (1 << 0)
+# define DP_CEC_LOGICAL_ADDRESS_1               (1 << 1)
+# define DP_CEC_LOGICAL_ADDRESS_2               (1 << 2)
+# define DP_CEC_LOGICAL_ADDRESS_3               (1 << 3)
+# define DP_CEC_LOGICAL_ADDRESS_4               (1 << 4)
+# define DP_CEC_LOGICAL_ADDRESS_5               (1 << 5)
+# define DP_CEC_LOGICAL_ADDRESS_6               (1 << 6)
+# define DP_CEC_LOGICAL_ADDRESS_7               (1 << 7)
+#define DP_CEC_LOGICAL_ADDRESS_MASK_2          0x300F /* 0x300E word */
+# define DP_CEC_LOGICAL_ADDRESS_8               (1 << 0)
+# define DP_CEC_LOGICAL_ADDRESS_9               (1 << 1)
+# define DP_CEC_LOGICAL_ADDRESS_10              (1 << 2)
+# define DP_CEC_LOGICAL_ADDRESS_11              (1 << 3)
+# define DP_CEC_LOGICAL_ADDRESS_12              (1 << 4)
+# define DP_CEC_LOGICAL_ADDRESS_13              (1 << 5)
+# define DP_CEC_LOGICAL_ADDRESS_14              (1 << 6)
+# define DP_CEC_LOGICAL_ADDRESS_15              (1 << 7)
+
+#define DP_CEC_RX_MESSAGE_BUFFER               0x3010
+#define DP_CEC_TX_MESSAGE_BUFFER               0x3020
+#define DP_CEC_MESSAGE_BUFFER_LENGTH             0x10
+
+/* PCON CONFIGURE-1 FRL FOR HDMI SINK */
+#define DP_PCON_HDMI_LINK_CONFIG_1             0x305A
+# define DP_PCON_ENABLE_MAX_FRL_BW             (7 << 0)
+# define DP_PCON_ENABLE_MAX_BW_0GBPS          0
+# define DP_PCON_ENABLE_MAX_BW_9GBPS          1
+# define DP_PCON_ENABLE_MAX_BW_18GBPS         2
+# define DP_PCON_ENABLE_MAX_BW_24GBPS         3
+# define DP_PCON_ENABLE_MAX_BW_32GBPS         4
+# define DP_PCON_ENABLE_MAX_BW_40GBPS         5
+# define DP_PCON_ENABLE_MAX_BW_48GBPS         6
+# define DP_PCON_ENABLE_SOURCE_CTL_MODE       (1 << 3)
+# define DP_PCON_ENABLE_CONCURRENT_LINK       (1 << 4)
+# define DP_PCON_ENABLE_SEQUENTIAL_LINK       (0 << 4)
+# define DP_PCON_ENABLE_LINK_FRL_MODE         (1 << 5)
+# define DP_PCON_ENABLE_HPD_READY            (1 << 6)
+# define DP_PCON_ENABLE_HDMI_LINK             (1 << 7)
+
+/* PCON CONFIGURE-2 FRL FOR HDMI SINK */
+#define DP_PCON_HDMI_LINK_CONFIG_2            0x305B
+# define DP_PCON_MAX_LINK_BW_MASK             (0x3F << 0)
+# define DP_PCON_FRL_BW_MASK_9GBPS            (1 << 0)
+# define DP_PCON_FRL_BW_MASK_18GBPS           (1 << 1)
+# define DP_PCON_FRL_BW_MASK_24GBPS           (1 << 2)
+# define DP_PCON_FRL_BW_MASK_32GBPS           (1 << 3)
+# define DP_PCON_FRL_BW_MASK_40GBPS           (1 << 4)
+# define DP_PCON_FRL_BW_MASK_48GBPS           (1 << 5)
+# define DP_PCON_FRL_LINK_TRAIN_EXTENDED      (1 << 6)
+# define DP_PCON_FRL_LINK_TRAIN_NORMAL        (0 << 6)
+
+/* PCON HDMI LINK STATUS */
+#define DP_PCON_HDMI_TX_LINK_STATUS           0x303B
+# define DP_PCON_HDMI_TX_LINK_ACTIVE          (1 << 0)
+# define DP_PCON_FRL_READY                   (1 << 1)
+
+/* PCON HDMI POST FRL STATUS */
+#define DP_PCON_HDMI_POST_FRL_STATUS          0x3036
+# define DP_PCON_HDMI_LINK_MODE               (1 << 0)
+# define DP_PCON_HDMI_MODE_TMDS               0
+# define DP_PCON_HDMI_MODE_FRL                1
+# define DP_PCON_HDMI_FRL_TRAINED_BW          (0x3F << 1)
+# define DP_PCON_FRL_TRAINED_BW_9GBPS        (1 << 1)
+# define DP_PCON_FRL_TRAINED_BW_18GBPS       (1 << 2)
+# define DP_PCON_FRL_TRAINED_BW_24GBPS       (1 << 3)
+# define DP_PCON_FRL_TRAINED_BW_32GBPS       (1 << 4)
+# define DP_PCON_FRL_TRAINED_BW_40GBPS       (1 << 5)
+# define DP_PCON_FRL_TRAINED_BW_48GBPS       (1 << 6)
+
+#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_PCON_ENABLE_DSC_ENCODER            (1 << 1)
+# define DP_PCON_ENCODER_PPS_OVERRIDE_MASK     (0x3 << 2)
+# define DP_PCON_ENC_PPS_OVERRIDE_DISABLED      0
+# define DP_PCON_ENC_PPS_OVERRIDE_EN_PARAMS     1
+# define DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER     2
+# define DP_CONVERSION_RGB_YCBCR_MASK         (7 << 4)
+# define DP_CONVERSION_BT601_RGB_YCBCR_ENABLE  (1 << 4)
+# define DP_CONVERSION_BT709_RGB_YCBCR_ENABLE  (1 << 5)
+# define DP_CONVERSION_BT2020_RGB_YCBCR_ENABLE (1 << 6)
+
+/* PCON Downstream HDMI ERROR Status per Lane */
+#define DP_PCON_HDMI_ERROR_STATUS_LN0          0x3037
+#define DP_PCON_HDMI_ERROR_STATUS_LN1          0x3038
+#define DP_PCON_HDMI_ERROR_STATUS_LN2          0x3039
+#define DP_PCON_HDMI_ERROR_STATUS_LN3          0x303A
+# define DP_PCON_HDMI_ERROR_COUNT_MASK         (0x7 << 0)
+# define DP_PCON_HDMI_ERROR_COUNT_THREE_PLUS   (1 << 0)
+# define DP_PCON_HDMI_ERROR_COUNT_TEN_PLUS     (1 << 1)
+# define DP_PCON_HDMI_ERROR_COUNT_HUNDRED_PLUS (1 << 2)
+
+/* PCON HDMI CONFIG PPS Override Buffer
+ * Valid Offsets to be added to Base : 0-127
+ */
+#define DP_PCON_HDMI_PPS_OVERRIDE_BASE        0x3100
+
+/* PCON HDMI CONFIG PPS Override Parameter: Slice height
+ * Offset-0 8LSBs of the Slice height.
+ * Offset-1 8MSBs of the Slice height.
+ */
+#define DP_PCON_HDMI_PPS_OVRD_SLICE_HEIGHT    0x3180
+
+/* PCON HDMI CONFIG PPS Override Parameter: Slice width
+ * Offset-0 8LSBs of the Slice width.
+ * Offset-1 8MSBs of the Slice width.
+ */
+#define DP_PCON_HDMI_PPS_OVRD_SLICE_WIDTH    0x3182
+
+/* PCON HDMI CONFIG PPS Override Parameter: bits_per_pixel
+ * Offset-0 8LSBs of the bits_per_pixel.
+ * Offset-1 2MSBs of the bits_per_pixel.
+ */
+#define DP_PCON_HDMI_PPS_OVRD_BPP           0x3184
+
+/* HDCP 1.3 and HDCP 2.2 */
+#define DP_AUX_HDCP_BKSV               0x68000
+#define DP_AUX_HDCP_RI_PRIME           0x68005
+#define DP_AUX_HDCP_AKSV               0x68007
+#define DP_AUX_HDCP_AN                 0x6800C
+#define DP_AUX_HDCP_V_PRIME(h)         (0x68014 + h * 4)
+#define DP_AUX_HDCP_BCAPS              0x68028
+# define DP_BCAPS_REPEATER_PRESENT     BIT(1)
+# define DP_BCAPS_HDCP_CAPABLE         BIT(0)
+#define DP_AUX_HDCP_BSTATUS            0x68029
+# define DP_BSTATUS_REAUTH_REQ         BIT(3)
+# define DP_BSTATUS_LINK_FAILURE       BIT(2)
+# define DP_BSTATUS_R0_PRIME_READY     BIT(1)
+# define DP_BSTATUS_READY              BIT(0)
+#define DP_AUX_HDCP_BINFO              0x6802A
+#define DP_AUX_HDCP_KSV_FIFO           0x6802C
+#define DP_AUX_HDCP_AINFO              0x6803B
+
+/* DP HDCP2.2 parameter offsets in DPCD address space */
+#define DP_HDCP_2_2_REG_RTX_OFFSET             0x69000
+#define DP_HDCP_2_2_REG_TXCAPS_OFFSET          0x69008
+#define DP_HDCP_2_2_REG_CERT_RX_OFFSET         0x6900B
+#define DP_HDCP_2_2_REG_RRX_OFFSET             0x69215
+#define DP_HDCP_2_2_REG_RX_CAPS_OFFSET         0x6921D
+#define DP_HDCP_2_2_REG_EKPUB_KM_OFFSET                0x69220
+#define DP_HDCP_2_2_REG_EKH_KM_WR_OFFSET       0x692A0
+#define DP_HDCP_2_2_REG_M_OFFSET               0x692B0
+#define DP_HDCP_2_2_REG_HPRIME_OFFSET          0x692C0
+#define DP_HDCP_2_2_REG_EKH_KM_RD_OFFSET       0x692E0
+#define DP_HDCP_2_2_REG_RN_OFFSET              0x692F0
+#define DP_HDCP_2_2_REG_LPRIME_OFFSET          0x692F8
+#define DP_HDCP_2_2_REG_EDKEY_KS_OFFSET                0x69318
+#define        DP_HDCP_2_2_REG_RIV_OFFSET              0x69328
+#define DP_HDCP_2_2_REG_RXINFO_OFFSET          0x69330
+#define DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET       0x69332
+#define DP_HDCP_2_2_REG_VPRIME_OFFSET          0x69335
+#define DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET    0x69345
+#define DP_HDCP_2_2_REG_V_OFFSET               0x693E0
+#define DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET       0x693F0
+#define DP_HDCP_2_2_REG_K_OFFSET               0x693F3
+#define DP_HDCP_2_2_REG_STREAM_ID_TYPE_OFFSET  0x693F5
+#define DP_HDCP_2_2_REG_MPRIME_OFFSET          0x69473
+#define DP_HDCP_2_2_REG_RXSTATUS_OFFSET                0x69493
+#define DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET     0x69494
+#define DP_HDCP_2_2_REG_DBG_OFFSET             0x69518
+
+/* LTTPR: Link Training (LT)-tunable PHY Repeaters */
+#define DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV 0xf0000 /* 1.3 */
+#define DP_MAX_LINK_RATE_PHY_REPEATER                      0xf0001 /* 1.4a */
+#define DP_PHY_REPEATER_CNT                                0xf0002 /* 1.3 */
+#define DP_PHY_REPEATER_MODE                               0xf0003 /* 1.3 */
+#define DP_MAX_LANE_COUNT_PHY_REPEATER                     0xf0004 /* 1.4a */
+#define DP_Repeater_FEC_CAPABILITY                         0xf0004 /* 1.4 */
+#define DP_PHY_REPEATER_EXTENDED_WAIT_TIMEOUT              0xf0005 /* 1.4a */
+#define DP_MAIN_LINK_CHANNEL_CODING_PHY_REPEATER           0xf0006 /* 2.0 */
+# define DP_PHY_REPEATER_128B132B_SUPPORTED                (1 << 0)
+/* See DP_128B132B_SUPPORTED_LINK_RATES for values */
+#define DP_PHY_REPEATER_128B132B_RATES                     0xf0007 /* 2.0 */
+
+enum drm_dp_phy {
+       DP_PHY_DPRX,
+
+       DP_PHY_LTTPR1,
+       DP_PHY_LTTPR2,
+       DP_PHY_LTTPR3,
+       DP_PHY_LTTPR4,
+       DP_PHY_LTTPR5,
+       DP_PHY_LTTPR6,
+       DP_PHY_LTTPR7,
+       DP_PHY_LTTPR8,
+
+       DP_MAX_LTTPR_COUNT = DP_PHY_LTTPR8,
+};
+
+#define DP_PHY_LTTPR(i)                                            (DP_PHY_LTTPR1 + (i))
+
+#define __DP_LTTPR1_BASE                                   0xf0010 /* 1.3 */
+#define __DP_LTTPR2_BASE                                   0xf0060 /* 1.3 */
+#define DP_LTTPR_BASE(dp_phy) \
+       (__DP_LTTPR1_BASE + (__DP_LTTPR2_BASE - __DP_LTTPR1_BASE) * \
+               ((dp_phy) - DP_PHY_LTTPR1))
+
+#define DP_LTTPR_REG(dp_phy, lttpr1_reg) \
+       (DP_LTTPR_BASE(dp_phy) - DP_LTTPR_BASE(DP_PHY_LTTPR1) + (lttpr1_reg))
+
+#define DP_TRAINING_PATTERN_SET_PHY_REPEATER1              0xf0010 /* 1.3 */
+#define DP_TRAINING_PATTERN_SET_PHY_REPEATER(dp_phy) \
+       DP_LTTPR_REG(dp_phy, DP_TRAINING_PATTERN_SET_PHY_REPEATER1)
+
+#define DP_TRAINING_LANE0_SET_PHY_REPEATER1                0xf0011 /* 1.3 */
+#define DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy) \
+       DP_LTTPR_REG(dp_phy, DP_TRAINING_LANE0_SET_PHY_REPEATER1)
+
+#define DP_TRAINING_LANE1_SET_PHY_REPEATER1                0xf0012 /* 1.3 */
+#define DP_TRAINING_LANE2_SET_PHY_REPEATER1                0xf0013 /* 1.3 */
+#define DP_TRAINING_LANE3_SET_PHY_REPEATER1                0xf0014 /* 1.3 */
+#define DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1          0xf0020 /* 1.4a */
+#define DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy)       \
+       DP_LTTPR_REG(dp_phy, DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1)
+
+#define DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1                    0xf0021 /* 1.4a */
+# define DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED                BIT(0)
+# define DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED                 BIT(1)
+
+#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1  0xf0022 /* 2.0 */
+#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy)      \
+       DP_LTTPR_REG(dp_phy, DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1)
+/* see DP_128B132B_TRAINING_AUX_RD_INTERVAL for values */
+
+#define DP_LANE0_1_STATUS_PHY_REPEATER1                            0xf0030 /* 1.3 */
+#define DP_LANE0_1_STATUS_PHY_REPEATER(dp_phy) \
+       DP_LTTPR_REG(dp_phy, DP_LANE0_1_STATUS_PHY_REPEATER1)
+
+#define DP_LANE2_3_STATUS_PHY_REPEATER1                            0xf0031 /* 1.3 */
+
+#define DP_LANE_ALIGN_STATUS_UPDATED_PHY_REPEATER1         0xf0032 /* 1.3 */
+#define DP_ADJUST_REQUEST_LANE0_1_PHY_REPEATER1                    0xf0033 /* 1.3 */
+#define DP_ADJUST_REQUEST_LANE2_3_PHY_REPEATER1                    0xf0034 /* 1.3 */
+#define DP_SYMBOL_ERROR_COUNT_LANE0_PHY_REPEATER1          0xf0035 /* 1.3 */
+#define DP_SYMBOL_ERROR_COUNT_LANE1_PHY_REPEATER1          0xf0037 /* 1.3 */
+#define DP_SYMBOL_ERROR_COUNT_LANE2_PHY_REPEATER1          0xf0039 /* 1.3 */
+#define DP_SYMBOL_ERROR_COUNT_LANE3_PHY_REPEATER1          0xf003b /* 1.3 */
+
+#define __DP_FEC1_BASE                                     0xf0290 /* 1.4 */
+#define __DP_FEC2_BASE                                     0xf0298 /* 1.4 */
+#define DP_FEC_BASE(dp_phy) \
+       (__DP_FEC1_BASE + ((__DP_FEC2_BASE - __DP_FEC1_BASE) * \
+                          ((dp_phy) - DP_PHY_LTTPR1)))
+
+#define DP_FEC_REG(dp_phy, fec1_reg) \
+       (DP_FEC_BASE(dp_phy) - DP_FEC_BASE(DP_PHY_LTTPR1) + fec1_reg)
+
+#define DP_FEC_STATUS_PHY_REPEATER1                        0xf0290 /* 1.4 */
+#define DP_FEC_STATUS_PHY_REPEATER(dp_phy) \
+       DP_FEC_REG(dp_phy, DP_FEC_STATUS_PHY_REPEATER1)
+
+#define DP_FEC_ERROR_COUNT_PHY_REPEATER1                    0xf0291 /* 1.4 */
+#define DP_FEC_CAPABILITY_PHY_REPEATER1                     0xf0294 /* 1.4a */
+
+#define DP_LTTPR_MAX_ADD                                   0xf02ff /* 1.4 */
+
+#define DP_DPCD_MAX_ADD                                            0xfffff /* 1.4 */
+
+/* Repeater modes */
+#define DP_PHY_REPEATER_MODE_TRANSPARENT                   0x55    /* 1.3 */
+#define DP_PHY_REPEATER_MODE_NON_TRANSPARENT               0xaa    /* 1.3 */
+
+/* DP HDCP message start offsets in DPCD address space */
+#define DP_HDCP_2_2_AKE_INIT_OFFSET            DP_HDCP_2_2_REG_RTX_OFFSET
+#define DP_HDCP_2_2_AKE_SEND_CERT_OFFSET       DP_HDCP_2_2_REG_CERT_RX_OFFSET
+#define DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET    DP_HDCP_2_2_REG_EKPUB_KM_OFFSET
+#define DP_HDCP_2_2_AKE_STORED_KM_OFFSET       DP_HDCP_2_2_REG_EKH_KM_WR_OFFSET
+#define DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET     DP_HDCP_2_2_REG_HPRIME_OFFSET
+#define DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET \
+                                               DP_HDCP_2_2_REG_EKH_KM_RD_OFFSET
+#define DP_HDCP_2_2_LC_INIT_OFFSET             DP_HDCP_2_2_REG_RN_OFFSET
+#define DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET      DP_HDCP_2_2_REG_LPRIME_OFFSET
+#define DP_HDCP_2_2_SKE_SEND_EKS_OFFSET                DP_HDCP_2_2_REG_EDKEY_KS_OFFSET
+#define DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET        DP_HDCP_2_2_REG_RXINFO_OFFSET
+#define DP_HDCP_2_2_REP_SEND_ACK_OFFSET                DP_HDCP_2_2_REG_V_OFFSET
+#define DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET   DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET
+#define DP_HDCP_2_2_REP_STREAM_READY_OFFSET    DP_HDCP_2_2_REG_MPRIME_OFFSET
+
+#define HDCP_2_2_DP_RXSTATUS_LEN               1
+#define HDCP_2_2_DP_RXSTATUS_READY(x)          ((x) & BIT(0))
+#define HDCP_2_2_DP_RXSTATUS_H_PRIME(x)                ((x) & BIT(1))
+#define HDCP_2_2_DP_RXSTATUS_PAIRING(x)                ((x) & BIT(2))
+#define HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(x)     ((x) & BIT(3))
+#define HDCP_2_2_DP_RXSTATUS_LINK_FAILED(x)    ((x) & BIT(4))
+
+/* DP 1.2 Sideband message defines */
+/* peer device type - DP 1.2a Table 2-92 */
+#define DP_PEER_DEVICE_NONE            0x0
+#define DP_PEER_DEVICE_SOURCE_OR_SST   0x1
+#define DP_PEER_DEVICE_MST_BRANCHING   0x2
+#define DP_PEER_DEVICE_SST_SINK                0x3
+#define DP_PEER_DEVICE_DP_LEGACY_CONV  0x4
+
+/* DP 1.2 MST sideband request names DP 1.2a Table 2-80 */
+#define DP_GET_MSG_TRANSACTION_VERSION 0x00 /* DP 1.3 */
+#define DP_LINK_ADDRESS                        0x01
+#define DP_CONNECTION_STATUS_NOTIFY    0x02
+#define DP_ENUM_PATH_RESOURCES         0x10
+#define DP_ALLOCATE_PAYLOAD            0x11
+#define DP_QUERY_PAYLOAD               0x12
+#define DP_RESOURCE_STATUS_NOTIFY      0x13
+#define DP_CLEAR_PAYLOAD_ID_TABLE      0x14
+#define DP_REMOTE_DPCD_READ            0x20
+#define DP_REMOTE_DPCD_WRITE           0x21
+#define DP_REMOTE_I2C_READ             0x22
+#define DP_REMOTE_I2C_WRITE            0x23
+#define DP_POWER_UP_PHY                        0x24
+#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
+#define DP_SIDEBAND_REPLY_NAK          0x01
+
+/* DP 1.2 MST sideband nak reasons - table 2.84 */
+#define DP_NAK_WRITE_FAILURE           0x01
+#define DP_NAK_INVALID_READ            0x02
+#define DP_NAK_CRC_FAILURE             0x03
+#define DP_NAK_BAD_PARAM               0x04
+#define DP_NAK_DEFER                   0x05
+#define DP_NAK_LINK_FAILURE            0x06
+#define DP_NAK_NO_RESOURCES            0x07
+#define DP_NAK_DPCD_FAIL               0x08
+#define DP_NAK_I2C_NAK                 0x09
+#define DP_NAK_ALLOCATE_FAIL           0x0a
+
+#define MODE_I2C_START 1
+#define MODE_I2C_WRITE 2
+#define MODE_I2C_READ  4
+#define MODE_I2C_STOP  8
+
+/* DP 1.2 MST PORTs - Section 2.5.1 v1.2a spec */
+#define DP_MST_PHYSICAL_PORT_0 0
+#define DP_MST_LOGICAL_PORT_0 8
+
+#define DP_LINK_CONSTANT_N_VALUE 0x8000
+#define DP_LINK_STATUS_SIZE       6
+bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
+                         int lane_count);
+bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
+                             int lane_count);
+u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                    int lane);
+u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                         int lane);
+u8 drm_dp_get_adjust_tx_ffe_preset(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                  int lane);
+u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                        unsigned int lane);
+
+#define DP_BRANCH_OUI_HEADER_SIZE      0xc
+#define DP_RECEIVER_CAP_SIZE           0xf
+#define DP_DSC_RECEIVER_CAP_SIZE        0xf
+#define EDP_PSR_RECEIVER_CAP_SIZE      2
+#define EDP_DISPLAY_CTL_CAP_SIZE       3
+#define DP_LTTPR_COMMON_CAP_SIZE       8
+#define DP_LTTPR_PHY_CAP_SIZE          3
+
+int drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                                    enum drm_dp_phy dp_phy, bool uhbr);
+int drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                                enum drm_dp_phy dp_phy, bool uhbr);
+
+void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux,
+                                           const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
+void drm_dp_lttpr_link_train_clock_recovery_delay(void);
+void drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
+                                       const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
+void drm_dp_lttpr_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
+                                             const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
+
+u8 drm_dp_link_rate_to_bw_code(int link_rate);
+int drm_dp_bw_code_to_link_rate(u8 link_bw);
+
+#define DP_SDP_AUDIO_TIMESTAMP         0x01
+#define DP_SDP_AUDIO_STREAM            0x02
+#define DP_SDP_EXTENSION               0x04 /* DP 1.1 */
+#define DP_SDP_AUDIO_COPYMANAGEMENT    0x05 /* DP 1.2 */
+#define DP_SDP_ISRC                    0x06 /* DP 1.2 */
+#define DP_SDP_VSC                     0x07 /* DP 1.2 */
+#define DP_SDP_CAMERA_GENERIC(i)       (0x08 + (i)) /* 0-7, DP 1.3 */
+#define DP_SDP_PPS                     0x10 /* DP 1.4 */
+#define DP_SDP_VSC_EXT_VESA            0x20 /* DP 1.4 */
+#define DP_SDP_VSC_EXT_CEA             0x21 /* DP 1.4 */
+/* 0x80+ CEA-861 infoframe types */
+
+/**
+ * struct dp_sdp_header - DP secondary data packet header
+ * @HB0: Secondary Data Packet ID
+ * @HB1: Secondary Data Packet Type
+ * @HB2: Secondary Data Packet Specific header, Byte 0
+ * @HB3: Secondary Data packet Specific header, Byte 1
+ */
+struct dp_sdp_header {
+       u8 HB0;
+       u8 HB1;
+       u8 HB2;
+       u8 HB3;
+} __packed;
+
+#define EDP_SDP_HEADER_REVISION_MASK           0x1F
+#define EDP_SDP_HEADER_VALID_PAYLOAD_BYTES     0x1F
+#define DP_SDP_PPS_HEADER_PAYLOAD_BYTES_MINUS_1 0x7F
+
+/**
+ * struct dp_sdp - DP secondary data packet
+ * @sdp_header: DP secondary data packet header
+ * @db: DP secondaray data packet data blocks
+ * VSC SDP Payload for PSR
+ * db[0]: Stereo Interface
+ * db[1]: 0 - PSR State; 1 - Update RFB; 2 - CRC Valid
+ * db[2]: CRC value bits 7:0 of the R or Cr component
+ * db[3]: CRC value bits 15:8 of the R or Cr component
+ * db[4]: CRC value bits 7:0 of the G or Y component
+ * db[5]: CRC value bits 15:8 of the G or Y component
+ * db[6]: CRC value bits 7:0 of the B or Cb component
+ * db[7]: CRC value bits 15:8 of the B or Cb component
+ * db[8] - db[31]: Reserved
+ * VSC SDP Payload for Pixel Encoding/Colorimetry Format
+ * db[0] - db[15]: Reserved
+ * db[16]: Pixel Encoding and Colorimetry Formats
+ * db[17]: Dynamic Range and Component Bit Depth
+ * db[18]: Content Type
+ * db[19] - db[31]: Reserved
+ */
+struct dp_sdp {
+       struct dp_sdp_header sdp_header;
+       u8 db[32];
+} __packed;
+
+#define EDP_VSC_PSR_STATE_ACTIVE       (1<<0)
+#define EDP_VSC_PSR_UPDATE_RFB         (1<<1)
+#define EDP_VSC_PSR_CRC_VALUES_VALID   (1<<2)
+
+/**
+ * enum dp_pixelformat - drm DP Pixel encoding formats
+ *
+ * This enum is used to indicate DP VSC SDP Pixel encoding formats.
+ * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through
+ * DB18]
+ *
+ * @DP_PIXELFORMAT_RGB: RGB pixel encoding format
+ * @DP_PIXELFORMAT_YUV444: YCbCr 4:4:4 pixel encoding format
+ * @DP_PIXELFORMAT_YUV422: YCbCr 4:2:2 pixel encoding format
+ * @DP_PIXELFORMAT_YUV420: YCbCr 4:2:0 pixel encoding format
+ * @DP_PIXELFORMAT_Y_ONLY: Y Only pixel encoding format
+ * @DP_PIXELFORMAT_RAW: RAW pixel encoding format
+ * @DP_PIXELFORMAT_RESERVED: Reserved pixel encoding format
+ */
+enum dp_pixelformat {
+       DP_PIXELFORMAT_RGB = 0,
+       DP_PIXELFORMAT_YUV444 = 0x1,
+       DP_PIXELFORMAT_YUV422 = 0x2,
+       DP_PIXELFORMAT_YUV420 = 0x3,
+       DP_PIXELFORMAT_Y_ONLY = 0x4,
+       DP_PIXELFORMAT_RAW = 0x5,
+       DP_PIXELFORMAT_RESERVED = 0x6,
+};
+
+/**
+ * enum dp_colorimetry - drm DP Colorimetry formats
+ *
+ * This enum is used to indicate DP VSC SDP Colorimetry formats.
+ * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through
+ * DB18] and a name of enum member follows DRM_MODE_COLORIMETRY definition.
+ *
+ * @DP_COLORIMETRY_DEFAULT: sRGB (IEC 61966-2-1) or
+ *                          ITU-R BT.601 colorimetry format
+ * @DP_COLORIMETRY_RGB_WIDE_FIXED: RGB wide gamut fixed point colorimetry format
+ * @DP_COLORIMETRY_BT709_YCC: ITU-R BT.709 colorimetry format
+ * @DP_COLORIMETRY_RGB_WIDE_FLOAT: RGB wide gamut floating point
+ *                                 (scRGB (IEC 61966-2-2)) colorimetry format
+ * @DP_COLORIMETRY_XVYCC_601: xvYCC601 colorimetry format
+ * @DP_COLORIMETRY_OPRGB: OpRGB colorimetry format
+ * @DP_COLORIMETRY_XVYCC_709: xvYCC709 colorimetry format
+ * @DP_COLORIMETRY_DCI_P3_RGB: DCI-P3 (SMPTE RP 431-2) colorimetry format
+ * @DP_COLORIMETRY_SYCC_601: sYCC601 colorimetry format
+ * @DP_COLORIMETRY_RGB_CUSTOM: RGB Custom Color Profile colorimetry format
+ * @DP_COLORIMETRY_OPYCC_601: opYCC601 colorimetry format
+ * @DP_COLORIMETRY_BT2020_RGB: ITU-R BT.2020 R' G' B' colorimetry format
+ * @DP_COLORIMETRY_BT2020_CYCC: ITU-R BT.2020 Y'c C'bc C'rc colorimetry format
+ * @DP_COLORIMETRY_BT2020_YCC: ITU-R BT.2020 Y' C'b C'r colorimetry format
+ */
+enum dp_colorimetry {
+       DP_COLORIMETRY_DEFAULT = 0,
+       DP_COLORIMETRY_RGB_WIDE_FIXED = 0x1,
+       DP_COLORIMETRY_BT709_YCC = 0x1,
+       DP_COLORIMETRY_RGB_WIDE_FLOAT = 0x2,
+       DP_COLORIMETRY_XVYCC_601 = 0x2,
+       DP_COLORIMETRY_OPRGB = 0x3,
+       DP_COLORIMETRY_XVYCC_709 = 0x3,
+       DP_COLORIMETRY_DCI_P3_RGB = 0x4,
+       DP_COLORIMETRY_SYCC_601 = 0x4,
+       DP_COLORIMETRY_RGB_CUSTOM = 0x5,
+       DP_COLORIMETRY_OPYCC_601 = 0x5,
+       DP_COLORIMETRY_BT2020_RGB = 0x6,
+       DP_COLORIMETRY_BT2020_CYCC = 0x6,
+       DP_COLORIMETRY_BT2020_YCC = 0x7,
+};
+
+/**
+ * enum dp_dynamic_range - drm DP Dynamic Range
+ *
+ * This enum is used to indicate DP VSC SDP Dynamic Range.
+ * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through
+ * DB18]
+ *
+ * @DP_DYNAMIC_RANGE_VESA: VESA range
+ * @DP_DYNAMIC_RANGE_CTA: CTA range
+ */
+enum dp_dynamic_range {
+       DP_DYNAMIC_RANGE_VESA = 0,
+       DP_DYNAMIC_RANGE_CTA = 1,
+};
+
+/**
+ * enum dp_content_type - drm DP Content Type
+ *
+ * This enum is used to indicate DP VSC SDP Content Types.
+ * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through
+ * DB18]
+ * CTA-861-G defines content types and expected processing by a sink device
+ *
+ * @DP_CONTENT_TYPE_NOT_DEFINED: Not defined type
+ * @DP_CONTENT_TYPE_GRAPHICS: Graphics type
+ * @DP_CONTENT_TYPE_PHOTO: Photo type
+ * @DP_CONTENT_TYPE_VIDEO: Video type
+ * @DP_CONTENT_TYPE_GAME: Game type
+ */
+enum dp_content_type {
+       DP_CONTENT_TYPE_NOT_DEFINED = 0x00,
+       DP_CONTENT_TYPE_GRAPHICS = 0x01,
+       DP_CONTENT_TYPE_PHOTO = 0x02,
+       DP_CONTENT_TYPE_VIDEO = 0x03,
+       DP_CONTENT_TYPE_GAME = 0x04,
+};
+
+/**
+ * struct drm_dp_vsc_sdp - drm DP VSC SDP
+ *
+ * This structure represents a DP VSC SDP of drm
+ * It is based on DP 1.4 spec [Table 2-116: VSC SDP Header Bytes] and
+ * [Table 2-117: VSC SDP Payload for DB16 through DB18]
+ *
+ * @sdp_type: secondary-data packet type
+ * @revision: revision number
+ * @length: number of valid data bytes
+ * @pixelformat: pixel encoding format
+ * @colorimetry: colorimetry format
+ * @bpc: bit per color
+ * @dynamic_range: dynamic range information
+ * @content_type: CTA-861-G defines content types and expected processing by a sink device
+ */
+struct drm_dp_vsc_sdp {
+       unsigned char sdp_type;
+       unsigned char revision;
+       unsigned char length;
+       enum dp_pixelformat pixelformat;
+       enum dp_colorimetry colorimetry;
+       int bpc;
+       enum dp_dynamic_range dynamic_range;
+       enum dp_content_type content_type;
+};
+
+void drm_dp_vsc_sdp_log(const char *level, struct device *dev,
+                       const struct drm_dp_vsc_sdp *vsc);
+
+int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE]);
+
+static inline int
+drm_dp_max_link_rate(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]);
+}
+
+static inline u8
+drm_dp_max_lane_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
+}
+
+static inline bool
+drm_dp_enhanced_frame_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_DPCD_REV] >= 0x11 &&
+               (dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP);
+}
+
+static inline bool
+drm_dp_fast_training_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_DPCD_REV] >= 0x11 &&
+               (dpcd[DP_MAX_DOWNSPREAD] & DP_NO_AUX_HANDSHAKE_LINK_TRAINING);
+}
+
+static inline bool
+drm_dp_tps3_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_DPCD_REV] >= 0x12 &&
+               dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED;
+}
+
+static inline bool
+drm_dp_max_downspread(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_DPCD_REV] >= 0x11 ||
+               dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5;
+}
+
+static inline bool
+drm_dp_tps4_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_DPCD_REV] >= 0x14 &&
+               dpcd[DP_MAX_DOWNSPREAD] & DP_TPS4_SUPPORTED;
+}
+
+static inline u8
+drm_dp_training_pattern_mask(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return (dpcd[DP_DPCD_REV] >= 0x14) ? DP_TRAINING_PATTERN_MASK_1_4 :
+               DP_TRAINING_PATTERN_MASK;
+}
+
+static inline bool
+drm_dp_is_branch(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT;
+}
+
+/* DP/eDP DSC support */
+u8 drm_dp_dsc_sink_max_slice_count(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE],
+                                  bool is_edp);
+u8 drm_dp_dsc_sink_line_buf_depth(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]);
+int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpc[DP_DSC_RECEIVER_CAP_SIZE],
+                                        u8 dsc_bpc[3]);
+
+static inline bool
+drm_dp_sink_supports_dsc(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE])
+{
+       return dsc_dpcd[DP_DSC_SUPPORT - DP_DSC_SUPPORT] &
+               DP_DSC_DECOMPRESSION_IS_SUPPORTED;
+}
+
+static inline u16
+drm_edp_dsc_sink_output_bpp(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE])
+{
+       return dsc_dpcd[DP_DSC_MAX_BITS_PER_PIXEL_LOW - DP_DSC_SUPPORT] |
+               (dsc_dpcd[DP_DSC_MAX_BITS_PER_PIXEL_HI - DP_DSC_SUPPORT] &
+                DP_DSC_MAX_BITS_PER_PIXEL_HI_MASK <<
+                DP_DSC_MAX_BITS_PER_PIXEL_HI_SHIFT);
+}
+
+static inline u32
+drm_dp_dsc_sink_max_slice_width(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE])
+{
+       /* Max Slicewidth = Number of Pixels * 320 */
+       return dsc_dpcd[DP_DSC_MAX_SLICE_WIDTH - DP_DSC_SUPPORT] *
+               DP_DSC_SLICE_WIDTH_MULTIPLIER;
+}
+
+/* Forward Error Correction Support on DP 1.4 */
+static inline bool
+drm_dp_sink_supports_fec(const u8 fec_capable)
+{
+       return fec_capable & DP_FEC_CAPABLE;
+}
+
+static inline bool
+drm_dp_channel_coding_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_MAIN_LINK_CHANNEL_CODING] & DP_CAP_ANSI_8B10B;
+}
+
+static inline bool
+drm_dp_alternate_scrambler_reset_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_EDP_CONFIGURATION_CAP] &
+                       DP_ALTERNATE_SCRAMBLER_RESET_CAP;
+}
+
+/* Ignore MSA timing for Adaptive Sync support on DP 1.4 */
+static inline bool
+drm_dp_sink_can_do_video_without_timing_msa(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       return dpcd[DP_DOWN_STREAM_PORT_COUNT] &
+               DP_MSA_TIMING_PAR_IGNORED;
+}
+
+/**
+ * drm_edp_backlight_supported() - Check an eDP DPCD for VESA backlight support
+ * @edp_dpcd: The DPCD to check
+ *
+ * Note that currently this function will return %false for panels which support various DPCD
+ * backlight features but which require the brightness be set through PWM, and don't support setting
+ * the brightness level via the DPCD.
+ *
+ * Returns: %True if @edp_dpcd indicates that VESA backlight controls are supported, %false
+ * otherwise
+ */
+static inline bool
+drm_edp_backlight_supported(const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE])
+{
+       return !!(edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP);
+}
+
+/*
+ * DisplayPort AUX channel
+ */
+
+/**
+ * struct drm_dp_aux_msg - DisplayPort AUX channel transaction
+ * @address: address of the (first) register to access
+ * @request: contains the type of transaction (see DP_AUX_* macros)
+ * @reply: upon completion, contains the reply type of the transaction
+ * @buffer: pointer to a transmission or reception buffer
+ * @size: size of @buffer
+ */
+struct drm_dp_aux_msg {
+       unsigned int address;
+       u8 request;
+       u8 reply;
+       void *buffer;
+       size_t size;
+};
+
+struct cec_adapter;
+struct edid;
+struct drm_connector;
+
+/**
+ * struct drm_dp_aux_cec - DisplayPort CEC-Tunneling-over-AUX
+ * @lock: mutex protecting this struct
+ * @adap: the CEC adapter for CEC-Tunneling-over-AUX support.
+ * @connector: the connector this CEC adapter is associated with
+ * @unregister_work: unregister the CEC adapter
+ */
+struct drm_dp_aux_cec {
+       struct mutex lock;
+       struct cec_adapter *adap;
+       struct drm_connector *connector;
+       struct delayed_work unregister_work;
+};
+
+/**
+ * struct drm_dp_aux - DisplayPort AUX channel
+ *
+ * An AUX channel can also be used to transport I2C messages to a sink. A
+ * typical application of that is to access an EDID that's present in the sink
+ * device. The @transfer() function can also be used to execute such
+ * transactions. The drm_dp_aux_register() function registers an I2C adapter
+ * that can be passed to drm_probe_ddc(). Upon removal, drivers should call
+ * drm_dp_aux_unregister() to remove the I2C adapter. The I2C adapter uses long
+ * transfers by default; if a partial response is received, the adapter will
+ * drop down to the size given by the partial response for this transaction
+ * only.
+ */
+struct drm_dp_aux {
+       /**
+        * @name: user-visible name of this AUX channel and the
+        * I2C-over-AUX adapter.
+        *
+        * It's also used to specify the name of the I2C adapter. If set
+        * to %NULL, dev_name() of @dev will be used.
+        */
+       const char *name;
+
+       /**
+        * @ddc: I2C adapter that can be used for I2C-over-AUX
+        * communication
+        */
+       struct i2c_adapter ddc;
+
+       /**
+        * @dev: pointer to struct device that is the parent for this
+        * AUX channel.
+        */
+       struct device *dev;
+
+       /**
+        * @drm_dev: pointer to the &drm_device that owns this AUX channel.
+        * Beware, this may be %NULL before drm_dp_aux_register() has been
+        * called.
+        *
+        * It should be set to the &drm_device that will be using this AUX
+        * channel as early as possible. For many graphics drivers this should
+        * happen before drm_dp_aux_init(), however it's perfectly fine to set
+        * this field later so long as it's assigned before calling
+        * drm_dp_aux_register().
+        */
+       struct drm_device *drm_dev;
+
+       /**
+        * @crtc: backpointer to the crtc that is currently using this
+        * AUX channel
+        */
+       struct drm_crtc *crtc;
+
+       /**
+        * @hw_mutex: internal mutex used for locking transfers.
+        *
+        * Note that if the underlying hardware is shared among multiple
+        * channels, the driver needs to do additional locking to
+        * prevent concurrent access.
+        */
+       struct mutex hw_mutex;
+
+       /**
+        * @crc_work: worker that captures CRCs for each frame
+        */
+       struct work_struct crc_work;
+
+       /**
+        * @crc_count: counter of captured frame CRCs
+        */
+       u8 crc_count;
+
+       /**
+        * @transfer: transfers a message representing a single AUX
+        * transaction.
+        *
+        * This is a hardware-specific implementation of how
+        * transactions are executed that the drivers must provide.
+        *
+        * A pointer to a &drm_dp_aux_msg structure describing the
+        * transaction is passed into this function. Upon success, the
+        * implementation should return the number of payload bytes that
+        * were transferred, or a negative error-code on failure.
+        *
+        * Helpers will propagate these errors, with the exception of
+        * the %-EBUSY error, which causes a transaction to be retried.
+        * On a short, helpers will return %-EPROTO to make it simpler
+        * to check for failure.
+        *
+        * The @transfer() function must only modify the reply field of
+        * the &drm_dp_aux_msg structure. The retry logic and i2c
+        * helpers assume this is the case.
+        *
+        * Also note that this callback can be called no matter the
+        * state @dev is in. Drivers that need that device to be powered
+        * to perform this operation will first need to make sure it's
+        * been properly enabled.
+        */
+       ssize_t (*transfer)(struct drm_dp_aux *aux,
+                           struct drm_dp_aux_msg *msg);
+
+       /**
+        * @i2c_nack_count: Counts I2C NACKs, used for DP validation.
+        */
+       unsigned i2c_nack_count;
+       /**
+        * @i2c_defer_count: Counts I2C DEFERs, used for DP validation.
+        */
+       unsigned i2c_defer_count;
+       /**
+        * @cec: struct containing fields used for CEC-Tunneling-over-AUX.
+        */
+       struct drm_dp_aux_cec cec;
+       /**
+        * @is_remote: Is this AUX CH actually using sideband messaging.
+        */
+       bool is_remote;
+};
+
+ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
+                        void *buffer, size_t size);
+ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
+                         void *buffer, size_t size);
+
+/**
+ * drm_dp_dpcd_readb() - read a single byte from the DPCD
+ * @aux: DisplayPort AUX channel
+ * @offset: address of the register to read
+ * @valuep: location where the value of the register will be stored
+ *
+ * Returns the number of bytes transferred (1) on success, or a negative
+ * error code on failure.
+ */
+static inline ssize_t drm_dp_dpcd_readb(struct drm_dp_aux *aux,
+                                       unsigned int offset, u8 *valuep)
+{
+       return drm_dp_dpcd_read(aux, offset, valuep, 1);
+}
+
+/**
+ * drm_dp_dpcd_writeb() - write a single byte to the DPCD
+ * @aux: DisplayPort AUX channel
+ * @offset: address of the register to write
+ * @value: value to write to the register
+ *
+ * Returns the number of bytes transferred (1) on success, or a negative
+ * error code on failure.
+ */
+static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux,
+                                        unsigned int offset, u8 value)
+{
+       return drm_dp_dpcd_write(aux, offset, &value, 1);
+}
+
+int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux,
+                         u8 dpcd[DP_RECEIVER_CAP_SIZE]);
+
+int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
+                                u8 status[DP_LINK_STATUS_SIZE]);
+
+int drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux,
+                                    enum drm_dp_phy dp_phy,
+                                    u8 link_status[DP_LINK_STATUS_SIZE]);
+
+bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux,
+                                   u8 real_edid_checksum);
+
+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]);
+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 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],
+                            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]);
+void drm_dp_set_subconnector_property(struct drm_connector *connector,
+                                     enum drm_connector_status status,
+                                     const u8 *dpcd,
+                                     const u8 port_cap[4]);
+
+struct drm_dp_desc;
+bool drm_dp_read_sink_count_cap(struct drm_connector *connector,
+                               const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                               const struct drm_dp_desc *desc);
+int drm_dp_read_sink_count(struct drm_dp_aux *aux);
+
+int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,
+                                 u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
+int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,
+                              enum drm_dp_phy dp_phy,
+                              u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
+int drm_dp_lttpr_count(const u8 cap[DP_LTTPR_COMMON_CAP_SIZE]);
+int drm_dp_lttpr_max_link_rate(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
+int drm_dp_lttpr_max_lane_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
+bool drm_dp_lttpr_voltage_swing_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
+bool drm_dp_lttpr_pre_emphasis_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
+
+void drm_dp_remote_aux_init(struct drm_dp_aux *aux);
+void drm_dp_aux_init(struct drm_dp_aux *aux);
+int drm_dp_aux_register(struct drm_dp_aux *aux);
+void drm_dp_aux_unregister(struct drm_dp_aux *aux);
+
+int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc);
+int drm_dp_stop_crc(struct drm_dp_aux *aux);
+
+struct drm_dp_dpcd_ident {
+       u8 oui[3];
+       u8 device_id[6];
+       u8 hw_rev;
+       u8 sw_major_rev;
+       u8 sw_minor_rev;
+} __packed;
+
+/**
+ * struct drm_dp_desc - DP branch/sink device descriptor
+ * @ident: DP device identification from DPCD 0x400 (sink) or 0x500 (branch).
+ * @quirks: Quirks; use drm_dp_has_quirk() to query for the quirks.
+ */
+struct drm_dp_desc {
+       struct drm_dp_dpcd_ident ident;
+       u32 quirks;
+};
+
+int drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc,
+                    bool is_branch);
+
+/**
+ * enum drm_dp_quirk - Display Port sink/branch device specific quirks
+ *
+ * Display Port sink and branch devices in the wild have a variety of bugs, try
+ * to collect them here. The quirks are shared, but it's up to the drivers to
+ * implement workarounds for them.
+ */
+enum drm_dp_quirk {
+       /**
+        * @DP_DPCD_QUIRK_CONSTANT_N:
+        *
+        * The device requires main link attributes Mvid and Nvid to be limited
+        * to 16 bits. So will give a constant value (0x8000) for compatability.
+        */
+       DP_DPCD_QUIRK_CONSTANT_N,
+       /**
+        * @DP_DPCD_QUIRK_NO_PSR:
+        *
+        * The device does not support PSR even if reports that it supports or
+        * driver still need to implement proper handling for such device.
+        */
+       DP_DPCD_QUIRK_NO_PSR,
+       /**
+        * @DP_DPCD_QUIRK_NO_SINK_COUNT:
+        *
+        * The device does not set SINK_COUNT to a non-zero value.
+        * The driver should ignore SINK_COUNT during detection. Note that
+        * drm_dp_read_sink_count_cap() automatically checks for this quirk.
+        */
+       DP_DPCD_QUIRK_NO_SINK_COUNT,
+       /**
+        * @DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD:
+        *
+        * The device supports MST DSC despite not supporting Virtual DPCD.
+        * The DSC caps can be read from the physical aux instead.
+        */
+       DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD,
+       /**
+        * @DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS:
+        *
+        * The device supports a link rate of 3.24 Gbps (multiplier 0xc) despite
+        * the DP_MAX_LINK_RATE register reporting a lower max multiplier.
+        */
+       DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS,
+};
+
+/**
+ * drm_dp_has_quirk() - does the DP device have a specific quirk
+ * @desc: Device descriptor filled by drm_dp_read_desc()
+ * @quirk: Quirk to query for
+ *
+ * Return true if DP device identified by @desc has @quirk.
+ */
+static inline bool
+drm_dp_has_quirk(const struct drm_dp_desc *desc, enum drm_dp_quirk quirk)
+{
+       return desc->quirks & BIT(quirk);
+}
+
+/**
+ * struct drm_edp_backlight_info - Probed eDP backlight info struct
+ * @pwmgen_bit_count: The pwmgen bit count
+ * @pwm_freq_pre_divider: The PWM frequency pre-divider value being used for this backlight, if any
+ * @max: The maximum backlight level that may be set
+ * @lsb_reg_used: Do we also write values to the DP_EDP_BACKLIGHT_BRIGHTNESS_LSB register?
+ * @aux_enable: Does the panel support the AUX enable cap?
+ * @aux_set: Does the panel support setting the brightness through AUX?
+ *
+ * This structure contains various data about an eDP backlight, which can be populated by using
+ * drm_edp_backlight_init().
+ */
+struct drm_edp_backlight_info {
+       u8 pwmgen_bit_count;
+       u8 pwm_freq_pre_divider;
+       u16 max;
+
+       bool lsb_reg_used : 1;
+       bool aux_enable : 1;
+       bool aux_set : 1;
+};
+
+int
+drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
+                      u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE],
+                      u16 *current_level, u8 *current_mode);
+int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
+                               u16 level);
+int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
+                            u16 level);
+int drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl);
+
+#if IS_ENABLED(CONFIG_DRM_KMS_HELPER) && (IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
+       (IS_MODULE(CONFIG_DRM_KMS_HELPER) && IS_MODULE(CONFIG_BACKLIGHT_CLASS_DEVICE)))
+
+int drm_panel_dp_aux_backlight(struct drm_panel *panel, struct drm_dp_aux *aux);
+
+#else
+
+static inline int drm_panel_dp_aux_backlight(struct drm_panel *panel,
+                                            struct drm_dp_aux *aux)
+{
+       return 0;
+}
+
+#endif
+
+#ifdef CONFIG_DRM_DP_CEC
+void drm_dp_cec_irq(struct drm_dp_aux *aux);
+void drm_dp_cec_register_connector(struct drm_dp_aux *aux,
+                                  struct drm_connector *connector);
+void drm_dp_cec_unregister_connector(struct drm_dp_aux *aux);
+void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const struct edid *edid);
+void drm_dp_cec_unset_edid(struct drm_dp_aux *aux);
+#else
+static inline void drm_dp_cec_irq(struct drm_dp_aux *aux)
+{
+}
+
+static inline void
+drm_dp_cec_register_connector(struct drm_dp_aux *aux,
+                             struct drm_connector *connector)
+{
+}
+
+static inline void drm_dp_cec_unregister_connector(struct drm_dp_aux *aux)
+{
+}
+
+static inline void drm_dp_cec_set_edid(struct drm_dp_aux *aux,
+                                      const struct edid *edid)
+{
+}
+
+static inline void drm_dp_cec_unset_edid(struct drm_dp_aux *aux)
+{
+}
+
+#endif
+
+/**
+ * struct drm_dp_phy_test_params - DP Phy Compliance parameters
+ * @link_rate: Requested Link rate from DPCD 0x219
+ * @num_lanes: Number of lanes requested by sing through DPCD 0x220
+ * @phy_pattern: DP Phy test pattern from DPCD 0x248
+ * @hbr2_reset: DP HBR2_COMPLIANCE_SCRAMBLER_RESET from DCPD 0x24A and 0x24B
+ * @custom80: DP Test_80BIT_CUSTOM_PATTERN from DPCDs 0x250 through 0x259
+ * @enhanced_frame_cap: flag for enhanced frame capability.
+ */
+struct drm_dp_phy_test_params {
+       int link_rate;
+       u8 num_lanes;
+       u8 phy_pattern;
+       u8 hbr2_reset[2];
+       u8 custom80[10];
+       bool enhanced_frame_cap;
+};
+
+int drm_dp_get_phy_test_pattern(struct drm_dp_aux *aux,
+                               struct drm_dp_phy_test_params *data);
+int drm_dp_set_phy_test_pattern(struct drm_dp_aux *aux,
+                               struct drm_dp_phy_test_params *data, u8 dp_rev);
+int drm_dp_get_pcon_max_frl_bw(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                              const u8 port_cap[4]);
+int drm_dp_pcon_frl_prepare(struct drm_dp_aux *aux, bool enable_frl_ready_hpd);
+bool drm_dp_pcon_is_frl_ready(struct drm_dp_aux *aux);
+int drm_dp_pcon_frl_configure_1(struct drm_dp_aux *aux, int max_frl_gbps,
+                               u8 frl_mode);
+int drm_dp_pcon_frl_configure_2(struct drm_dp_aux *aux, int max_frl_mask,
+                               u8 frl_type);
+int drm_dp_pcon_reset_frl_config(struct drm_dp_aux *aux);
+int drm_dp_pcon_frl_enable(struct drm_dp_aux *aux);
+
+bool drm_dp_pcon_hdmi_link_active(struct drm_dp_aux *aux);
+int drm_dp_pcon_hdmi_link_mode(struct drm_dp_aux *aux, u8 *frl_trained_mask);
+void drm_dp_pcon_hdmi_frl_link_error_count(struct drm_dp_aux *aux,
+                                          struct drm_connector *connector);
+bool drm_dp_pcon_enc_is_dsc_1_2(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]);
+int drm_dp_pcon_dsc_max_slices(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]);
+int drm_dp_pcon_dsc_max_slice_width(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]);
+int drm_dp_pcon_dsc_bpp_incr(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]);
+int drm_dp_pcon_pps_default(struct drm_dp_aux *aux);
+int drm_dp_pcon_pps_override_buf(struct drm_dp_aux *aux, u8 pps_buf[128]);
+int drm_dp_pcon_pps_override_param(struct drm_dp_aux *aux, u8 pps_param[6]);
+bool drm_dp_downstream_rgb_to_ycbcr_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                                              const u8 port_cap[4], u8 color_spc);
+int drm_dp_pcon_convert_rgb_to_ycbcr(struct drm_dp_aux *aux, u8 color_spc);
+
+#endif /* _DRM_DP_HELPER_H_ */
diff --git a/include/drm/dp/drm_dp_mst_helper.h b/include/drm/dp/drm_dp_mst_helper.h
new file mode 100644 (file)
index 0000000..08276eb
--- /dev/null
@@ -0,0 +1,972 @@
+/*
+ * Copyright © 2014 Red Hat.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+#ifndef _DRM_DP_MST_HELPER_H_
+#define _DRM_DP_MST_HELPER_H_
+
+#include <linux/types.h>
+#include <drm/dp/drm_dp_helper.h>
+#include <drm/drm_atomic.h>
+
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+#include <linux/stackdepot.h>
+#include <linux/timekeeping.h>
+
+enum drm_dp_mst_topology_ref_type {
+       DRM_DP_MST_TOPOLOGY_REF_GET,
+       DRM_DP_MST_TOPOLOGY_REF_PUT,
+};
+
+struct drm_dp_mst_topology_ref_history {
+       struct drm_dp_mst_topology_ref_entry {
+               enum drm_dp_mst_topology_ref_type type;
+               int count;
+               ktime_t ts_nsec;
+               depot_stack_handle_t backtrace;
+       } *entries;
+       int len;
+};
+#endif /* IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) */
+
+struct drm_dp_mst_branch;
+
+/**
+ * struct drm_dp_vcpi - Virtual Channel Payload Identifier
+ * @vcpi: Virtual channel ID.
+ * @pbn: Payload Bandwidth Number for this channel
+ * @aligned_pbn: PBN aligned with slot size
+ * @num_slots: number of slots for this PBN
+ */
+struct drm_dp_vcpi {
+       int vcpi;
+       int pbn;
+       int aligned_pbn;
+       int num_slots;
+};
+
+/**
+ * struct drm_dp_mst_port - MST port
+ * @port_num: port number
+ * @input: if this port is an input port. Protected by
+ * &drm_dp_mst_topology_mgr.base.lock.
+ * @mcs: message capability status - DP 1.2 spec. Protected by
+ * &drm_dp_mst_topology_mgr.base.lock.
+ * @ddps: DisplayPort Device Plug Status - DP 1.2. Protected by
+ * &drm_dp_mst_topology_mgr.base.lock.
+ * @pdt: Peer Device Type. Protected by
+ * &drm_dp_mst_topology_mgr.base.lock.
+ * @ldps: Legacy Device Plug Status. Protected by
+ * &drm_dp_mst_topology_mgr.base.lock.
+ * @dpcd_rev: DPCD revision of device on this port. Protected by
+ * &drm_dp_mst_topology_mgr.base.lock.
+ * @num_sdp_streams: Number of simultaneous streams. Protected by
+ * &drm_dp_mst_topology_mgr.base.lock.
+ * @num_sdp_stream_sinks: Number of stream sinks. Protected by
+ * &drm_dp_mst_topology_mgr.base.lock.
+ * @full_pbn: Max possible bandwidth for this port. Protected by
+ * &drm_dp_mst_topology_mgr.base.lock.
+ * @next: link to next port on this branch device
+ * @aux: i2c aux transport to talk to device connected to this port, protected
+ * by &drm_dp_mst_topology_mgr.base.lock.
+ * @parent: branch device parent of this port
+ * @vcpi: Virtual Channel Payload info for this port.
+ * @connector: DRM connector this port is connected to. Protected by
+ * &drm_dp_mst_topology_mgr.base.lock.
+ * @mgr: topology manager this port lives under.
+ *
+ * This structure represents an MST port endpoint on a device somewhere
+ * in the MST topology.
+ */
+struct drm_dp_mst_port {
+       /**
+        * @topology_kref: refcount for this port's lifetime in the topology,
+        * only the DP MST helpers should need to touch this
+        */
+       struct kref topology_kref;
+
+       /**
+        * @malloc_kref: refcount for the memory allocation containing this
+        * structure. See drm_dp_mst_get_port_malloc() and
+        * drm_dp_mst_put_port_malloc().
+        */
+       struct kref malloc_kref;
+
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+       /**
+        * @topology_ref_history: A history of each topology
+        * reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.
+        */
+       struct drm_dp_mst_topology_ref_history topology_ref_history;
+#endif
+
+       u8 port_num;
+       bool input;
+       bool mcs;
+       bool ddps;
+       u8 pdt;
+       bool ldps;
+       u8 dpcd_rev;
+       u8 num_sdp_streams;
+       u8 num_sdp_stream_sinks;
+       uint16_t full_pbn;
+       struct list_head next;
+       /**
+        * @mstb: the branch device connected to this port, if there is one.
+        * This should be considered protected for reading by
+        * &drm_dp_mst_topology_mgr.lock. There are two exceptions to this:
+        * &drm_dp_mst_topology_mgr.up_req_work and
+        * &drm_dp_mst_topology_mgr.work, which do not grab
+        * &drm_dp_mst_topology_mgr.lock during reads but are the only
+        * updaters of this list and are protected from writing concurrently
+        * by &drm_dp_mst_topology_mgr.probe_lock.
+        */
+       struct drm_dp_mst_branch *mstb;
+       struct drm_dp_aux aux; /* i2c bus for this port? */
+       struct drm_dp_mst_branch *parent;
+
+       struct drm_dp_vcpi vcpi;
+       struct drm_connector *connector;
+       struct drm_dp_mst_topology_mgr *mgr;
+
+       /**
+        * @cached_edid: for DP logical ports - make tiling work by ensuring
+        * that the EDID for all connectors is read immediately.
+        */
+       struct edid *cached_edid;
+       /**
+        * @has_audio: Tracks whether the sink connector to this port is
+        * audio-capable.
+        */
+       bool has_audio;
+
+       /**
+        * @fec_capable: bool indicating if FEC can be supported up to that
+        * point in the MST topology.
+        */
+       bool fec_capable;
+};
+
+/* sideband msg header - not bit struct */
+struct drm_dp_sideband_msg_hdr {
+       u8 lct;
+       u8 lcr;
+       u8 rad[8];
+       bool broadcast;
+       bool path_msg;
+       u8 msg_len;
+       bool somt;
+       bool eomt;
+       bool seqno;
+};
+
+struct drm_dp_sideband_msg_rx {
+       u8 chunk[48];
+       u8 msg[256];
+       u8 curchunk_len;
+       u8 curchunk_idx; /* chunk we are parsing now */
+       u8 curchunk_hdrlen;
+       u8 curlen; /* total length of the msg */
+       bool have_somt;
+       bool have_eomt;
+       struct drm_dp_sideband_msg_hdr initial_hdr;
+};
+
+/**
+ * struct drm_dp_mst_branch - MST branch device.
+ * @rad: Relative Address to talk to this branch device.
+ * @lct: Link count total to talk to this branch device.
+ * @num_ports: number of ports on the branch.
+ * @port_parent: pointer to the port parent, NULL if toplevel.
+ * @mgr: topology manager for this branch device.
+ * @link_address_sent: if a link address message has been sent to this device yet.
+ * @guid: guid for DP 1.2 branch device. port under this branch can be
+ * identified by port #.
+ *
+ * This structure represents an MST branch device, there is one
+ * primary branch device at the root, along with any other branches connected
+ * to downstream port of parent branches.
+ */
+struct drm_dp_mst_branch {
+       /**
+        * @topology_kref: refcount for this branch device's lifetime in the
+        * topology, only the DP MST helpers should need to touch this
+        */
+       struct kref topology_kref;
+
+       /**
+        * @malloc_kref: refcount for the memory allocation containing this
+        * structure. See drm_dp_mst_get_mstb_malloc() and
+        * drm_dp_mst_put_mstb_malloc().
+        */
+       struct kref malloc_kref;
+
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+       /**
+        * @topology_ref_history: A history of each topology
+        * reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.
+        */
+       struct drm_dp_mst_topology_ref_history topology_ref_history;
+#endif
+
+       /**
+        * @destroy_next: linked-list entry used by
+        * drm_dp_delayed_destroy_work()
+        */
+       struct list_head destroy_next;
+
+       u8 rad[8];
+       u8 lct;
+       int num_ports;
+
+       /**
+        * @ports: the list of ports on this branch device. This should be
+        * considered protected for reading by &drm_dp_mst_topology_mgr.lock.
+        * There are two exceptions to this:
+        * &drm_dp_mst_topology_mgr.up_req_work and
+        * &drm_dp_mst_topology_mgr.work, which do not grab
+        * &drm_dp_mst_topology_mgr.lock during reads but are the only
+        * updaters of this list and are protected from updating the list
+        * concurrently by @drm_dp_mst_topology_mgr.probe_lock
+        */
+       struct list_head ports;
+
+       struct drm_dp_mst_port *port_parent;
+       struct drm_dp_mst_topology_mgr *mgr;
+
+       bool link_address_sent;
+
+       /* global unique identifier to identify branch devices */
+       u8 guid[16];
+};
+
+
+struct drm_dp_nak_reply {
+       u8 guid[16];
+       u8 reason;
+       u8 nak_data;
+};
+
+struct drm_dp_link_address_ack_reply {
+       u8 guid[16];
+       u8 nports;
+       struct drm_dp_link_addr_reply_port {
+               bool input_port;
+               u8 peer_device_type;
+               u8 port_number;
+               bool mcs;
+               bool ddps;
+               bool legacy_device_plug_status;
+               u8 dpcd_revision;
+               u8 peer_guid[16];
+               u8 num_sdp_streams;
+               u8 num_sdp_stream_sinks;
+       } ports[16];
+};
+
+struct drm_dp_remote_dpcd_read_ack_reply {
+       u8 port_number;
+       u8 num_bytes;
+       u8 bytes[255];
+};
+
+struct drm_dp_remote_dpcd_write_ack_reply {
+       u8 port_number;
+};
+
+struct drm_dp_remote_dpcd_write_nak_reply {
+       u8 port_number;
+       u8 reason;
+       u8 bytes_written_before_failure;
+};
+
+struct drm_dp_remote_i2c_read_ack_reply {
+       u8 port_number;
+       u8 num_bytes;
+       u8 bytes[255];
+};
+
+struct drm_dp_remote_i2c_read_nak_reply {
+       u8 port_number;
+       u8 nak_reason;
+       u8 i2c_nak_transaction;
+};
+
+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 {
+       u8 port_number;
+       u8 number_sdp_streams;
+       u8 vcpi;
+       u16 pbn;
+       u8 sdp_stream_sink[DRM_DP_MAX_SDP_STREAMS];
+};
+
+struct drm_dp_allocate_payload_ack_reply {
+       u8 port_number;
+       u8 vcpi;
+       u16 allocated_pbn;
+};
+
+struct drm_dp_connection_status_notify {
+       u8 guid[16];
+       u8 port_number;
+       bool legacy_device_plug_status;
+       bool displayport_device_plug_status;
+       bool message_capability_status;
+       bool input_port;
+       u8 peer_device_type;
+};
+
+struct drm_dp_remote_dpcd_read {
+       u8 port_number;
+       u32 dpcd_address;
+       u8 num_bytes;
+};
+
+struct drm_dp_remote_dpcd_write {
+       u8 port_number;
+       u32 dpcd_address;
+       u8 num_bytes;
+       u8 *bytes;
+};
+
+#define DP_REMOTE_I2C_READ_MAX_TRANSACTIONS 4
+struct drm_dp_remote_i2c_read {
+       u8 num_transactions;
+       u8 port_number;
+       struct drm_dp_remote_i2c_read_tx {
+               u8 i2c_dev_id;
+               u8 num_bytes;
+               u8 *bytes;
+               u8 no_stop_bit;
+               u8 i2c_transaction_delay;
+       } transactions[DP_REMOTE_I2C_READ_MAX_TRANSACTIONS];
+       u8 read_i2c_device_id;
+       u8 num_bytes_read;
+};
+
+struct drm_dp_remote_i2c_write {
+       u8 port_number;
+       u8 write_i2c_device_id;
+       u8 num_bytes;
+       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;
+};
+
+struct drm_dp_enum_path_resources_ack_reply {
+       u8 port_number;
+       bool fec_capable;
+       u16 full_payload_bw_number;
+       u16 avail_payload_bw_number;
+};
+
+/* covers POWER_DOWN_PHY, POWER_UP_PHY */
+struct drm_dp_port_number_rep {
+       u8 port_number;
+};
+
+struct drm_dp_query_payload {
+       u8 port_number;
+       u8 vcpi;
+};
+
+struct drm_dp_resource_status_notify {
+       u8 port_number;
+       u8 guid[16];
+       u16 available_pbn;
+};
+
+struct drm_dp_query_payload_ack_reply {
+       u8 port_number;
+       u16 allocated_pbn;
+};
+
+struct drm_dp_sideband_msg_req_body {
+       u8 req_type;
+       union ack_req {
+               struct drm_dp_connection_status_notify conn_stat;
+               struct drm_dp_port_number_req port_num;
+               struct drm_dp_resource_status_notify resource_stat;
+
+               struct drm_dp_query_payload query_payload;
+               struct drm_dp_allocate_payload allocate_payload;
+
+               struct drm_dp_remote_dpcd_read dpcd_read;
+               struct drm_dp_remote_dpcd_write dpcd_write;
+
+               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;
+};
+
+struct drm_dp_sideband_msg_reply_body {
+       u8 reply_type;
+       u8 req_type;
+       union ack_replies {
+               struct drm_dp_nak_reply nak;
+               struct drm_dp_link_address_ack_reply link_addr;
+               struct drm_dp_port_number_rep port_number;
+
+               struct drm_dp_enum_path_resources_ack_reply path_resources;
+               struct drm_dp_allocate_payload_ack_reply allocate_payload;
+               struct drm_dp_query_payload_ack_reply query_payload;
+
+               struct drm_dp_remote_dpcd_read_ack_reply remote_dpcd_read_ack;
+               struct drm_dp_remote_dpcd_write_ack_reply remote_dpcd_write_ack;
+               struct drm_dp_remote_dpcd_write_nak_reply remote_dpcd_write_nack;
+
+               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;
+};
+
+/* msg is queued to be put into a slot */
+#define DRM_DP_SIDEBAND_TX_QUEUED 0
+/* msg has started transmitting on a slot - still on msgq */
+#define DRM_DP_SIDEBAND_TX_START_SEND 1
+/* msg has finished transmitting on a slot - removed from msgq only in slot */
+#define DRM_DP_SIDEBAND_TX_SENT 2
+/* msg has received a response - removed from slot */
+#define DRM_DP_SIDEBAND_TX_RX 3
+#define DRM_DP_SIDEBAND_TX_TIMEOUT 4
+
+struct drm_dp_sideband_msg_tx {
+       u8 msg[256];
+       u8 chunk[48];
+       u8 cur_offset;
+       u8 cur_len;
+       struct drm_dp_mst_branch *dst;
+       struct list_head next;
+       int seqno;
+       int state;
+       bool path_msg;
+       struct drm_dp_sideband_msg_reply_body reply;
+};
+
+/* sideband msg handler */
+struct drm_dp_mst_topology_mgr;
+struct drm_dp_mst_topology_cbs {
+       /* create a connector for a port */
+       struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path);
+       /*
+        * Checks for any pending MST interrupts, passing them to MST core for
+        * processing, the same way an HPD IRQ pulse handler would do this.
+        * If provided MST core calls this callback from a poll-waiting loop
+        * when waiting for MST down message replies. The driver is expected
+        * to guard against a race between this callback and the driver's HPD
+        * IRQ pulse handler.
+        */
+       void (*poll_hpd_irq)(struct drm_dp_mst_topology_mgr *mgr);
+};
+
+#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
+
+#define DP_PAYLOAD_LOCAL 1
+#define DP_PAYLOAD_REMOTE 2
+#define DP_PAYLOAD_DELETE_LOCAL 3
+
+struct drm_dp_payload {
+       int payload_state;
+       int start_slot;
+       int num_slots;
+       int vcpi;
+};
+
+#define to_dp_mst_topology_state(x) container_of(x, struct drm_dp_mst_topology_state, base)
+
+struct drm_dp_vcpi_allocation {
+       struct drm_dp_mst_port *port;
+       int vcpi;
+       int pbn;
+       bool dsc_enabled;
+       struct list_head next;
+};
+
+struct drm_dp_mst_topology_state {
+       struct drm_private_state base;
+       struct list_head vcpis;
+       struct drm_dp_mst_topology_mgr *mgr;
+       u8 total_avail_slots;
+       u8 start_slot;
+};
+
+#define to_dp_mst_topology_mgr(x) container_of(x, struct drm_dp_mst_topology_mgr, base)
+
+/**
+ * struct drm_dp_mst_topology_mgr - DisplayPort MST manager
+ *
+ * This struct represents the toplevel displayport MST topology manager.
+ * There should be one instance of this for every MST capable DP connector
+ * on the GPU.
+ */
+struct drm_dp_mst_topology_mgr {
+       /**
+        * @base: Base private object for atomic
+        */
+       struct drm_private_obj base;
+
+       /**
+        * @dev: device pointer for adding i2c devices etc.
+        */
+       struct drm_device *dev;
+       /**
+        * @cbs: callbacks for connector addition and destruction.
+        */
+       const struct drm_dp_mst_topology_cbs *cbs;
+       /**
+        * @max_dpcd_transaction_bytes: maximum number of bytes to read/write
+        * in one go.
+        */
+       int max_dpcd_transaction_bytes;
+       /**
+        * @aux: AUX channel for the DP MST connector this topolgy mgr is
+        * controlling.
+        */
+       struct drm_dp_aux *aux;
+       /**
+        * @max_payloads: maximum number of payloads the GPU can generate.
+        */
+       int max_payloads;
+       /**
+        * @max_lane_count: maximum number of lanes the GPU can drive.
+        */
+       int max_lane_count;
+       /**
+        * @max_link_rate: maximum link rate per lane GPU can output, in kHz.
+        */
+       int max_link_rate;
+       /**
+        * @conn_base_id: DRM connector ID this mgr is connected to. Only used
+        * to build the MST connector path value.
+        */
+       int conn_base_id;
+
+       /**
+        * @up_req_recv: Message receiver state for up requests.
+        */
+       struct drm_dp_sideband_msg_rx up_req_recv;
+
+       /**
+        * @down_rep_recv: Message receiver state for replies to down
+        * requests.
+        */
+       struct drm_dp_sideband_msg_rx down_rep_recv;
+
+       /**
+        * @lock: protects @mst_state, @mst_primary, @dpcd, and
+        * @payload_id_table_cleared.
+        */
+       struct mutex lock;
+
+       /**
+        * @probe_lock: Prevents @work and @up_req_work, the only writers of
+        * &drm_dp_mst_port.mstb and &drm_dp_mst_branch.ports, from racing
+        * while they update the topology.
+        */
+       struct mutex probe_lock;
+
+       /**
+        * @mst_state: If this manager is enabled for an MST capable port. False
+        * if no MST sink/branch devices is connected.
+        */
+       bool mst_state : 1;
+
+       /**
+        * @payload_id_table_cleared: Whether or not we've cleared the payload
+        * ID table for @mst_primary. Protected by @lock.
+        */
+       bool payload_id_table_cleared : 1;
+
+       /**
+        * @mst_primary: Pointer to the primary/first branch device.
+        */
+       struct drm_dp_mst_branch *mst_primary;
+
+       /**
+        * @dpcd: Cache of DPCD for primary port.
+        */
+       u8 dpcd[DP_RECEIVER_CAP_SIZE];
+       /**
+        * @sink_count: Sink count from DEVICE_SERVICE_IRQ_VECTOR_ESI0.
+        */
+       u8 sink_count;
+       /**
+        * @pbn_div: PBN to slots divisor.
+        */
+       int pbn_div;
+
+       /**
+        * @funcs: Atomic helper callbacks
+        */
+       const struct drm_private_state_funcs *funcs;
+
+       /**
+        * @qlock: protects @tx_msg_downq and &drm_dp_sideband_msg_tx.state
+        */
+       struct mutex qlock;
+
+       /**
+        * @tx_msg_downq: List of pending down requests
+        */
+       struct list_head tx_msg_downq;
+
+       /**
+        * @payload_lock: Protect payload information.
+        */
+       struct mutex payload_lock;
+       /**
+        * @proposed_vcpis: Array of pointers for the new VCPI allocation. The
+        * VCPI structure itself is &drm_dp_mst_port.vcpi, and the size of
+        * this array is determined by @max_payloads.
+        */
+       struct drm_dp_vcpi **proposed_vcpis;
+       /**
+        * @payloads: Array of payloads. The size of this array is determined
+        * by @max_payloads.
+        */
+       struct drm_dp_payload *payloads;
+       /**
+        * @payload_mask: Elements of @payloads actually in use. Since
+        * reallocation of active outputs isn't possible gaps can be created by
+        * disabling outputs out of order compared to how they've been enabled.
+        */
+       unsigned long payload_mask;
+       /**
+        * @vcpi_mask: Similar to @payload_mask, but for @proposed_vcpis.
+        */
+       unsigned long vcpi_mask;
+
+       /**
+        * @tx_waitq: Wait to queue stall for the tx worker.
+        */
+       wait_queue_head_t tx_waitq;
+       /**
+        * @work: Probe work.
+        */
+       struct work_struct work;
+       /**
+        * @tx_work: Sideband transmit worker. This can nest within the main
+        * @work worker for each transaction @work launches.
+        */
+       struct work_struct tx_work;
+
+       /**
+        * @destroy_port_list: List of to be destroyed connectors.
+        */
+       struct list_head destroy_port_list;
+       /**
+        * @destroy_branch_device_list: List of to be destroyed branch
+        * devices.
+        */
+       struct list_head destroy_branch_device_list;
+       /**
+        * @delayed_destroy_lock: Protects @destroy_port_list and
+        * @destroy_branch_device_list.
+        */
+       struct mutex delayed_destroy_lock;
+
+       /**
+        * @delayed_destroy_wq: Workqueue used for delayed_destroy_work items.
+        * A dedicated WQ makes it possible to drain any requeued work items
+        * on it.
+        */
+       struct workqueue_struct *delayed_destroy_wq;
+
+       /**
+        * @delayed_destroy_work: Work item to destroy MST port and branch
+        * devices, needed to avoid locking inversion.
+        */
+       struct work_struct delayed_destroy_work;
+
+       /**
+        * @up_req_list: List of pending up requests from the topology that
+        * need to be processed, in chronological order.
+        */
+       struct list_head up_req_list;
+       /**
+        * @up_req_lock: Protects @up_req_list
+        */
+       struct mutex up_req_lock;
+       /**
+        * @up_req_work: Work item to process up requests received from the
+        * topology. Needed to avoid blocking hotplug handling and sideband
+        * transmissions.
+        */
+       struct work_struct up_req_work;
+
+#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
+       /**
+        * @topology_ref_history_lock: protects
+        * &drm_dp_mst_port.topology_ref_history and
+        * &drm_dp_mst_branch.topology_ref_history.
+        */
+       struct mutex topology_ref_history_lock;
+#endif
+};
+
+int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
+                                struct drm_device *dev, struct drm_dp_aux *aux,
+                                int max_dpcd_transaction_bytes,
+                                int max_payloads,
+                                int max_lane_count, int max_link_rate,
+                                int conn_base_id);
+
+void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr);
+
+bool drm_dp_read_mst_cap(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
+int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state);
+
+int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled);
+
+
+int
+drm_dp_mst_detect_port(struct drm_connector *connector,
+                      struct drm_modeset_acquire_ctx *ctx,
+                      struct drm_dp_mst_topology_mgr *mgr,
+                      struct drm_dp_mst_port *port);
+
+struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port);
+
+int drm_dp_get_vc_payload_bw(const struct drm_dp_mst_topology_mgr *mgr,
+                            int link_rate, int link_lane_count);
+
+int drm_dp_calc_pbn_mode(int clock, int bpp, bool dsc);
+
+bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+                             struct drm_dp_mst_port *port, int pbn, int slots);
+
+int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port);
+
+
+void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port);
+
+void drm_dp_mst_update_slots(struct drm_dp_mst_topology_state *mst_state, uint8_t link_encoding_cap);
+
+void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+                               struct drm_dp_mst_port *port);
+
+
+int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr,
+                          int pbn);
+
+
+int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr, int start_slot);
+
+
+int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr);
+
+int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr);
+
+void drm_dp_mst_dump_topology(struct seq_file *m,
+                             struct drm_dp_mst_topology_mgr *mgr);
+
+void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
+int __must_check
+drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
+                              bool sync);
+
+ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
+                            unsigned int offset, void *buffer, size_t size);
+ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux,
+                             unsigned int offset, void *buffer, size_t size);
+
+int drm_dp_mst_connector_late_register(struct drm_connector *connector,
+                                      struct drm_dp_mst_port *port);
+void drm_dp_mst_connector_early_unregister(struct drm_connector *connector,
+                                          struct drm_dp_mst_port *port);
+
+struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
+                                                                   struct drm_dp_mst_topology_mgr *mgr);
+int __must_check
+drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
+                             struct drm_dp_mst_topology_mgr *mgr,
+                             struct drm_dp_mst_port *port, int pbn,
+                             int pbn_div);
+int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state,
+                                struct drm_dp_mst_port *port,
+                                int pbn, int pbn_div,
+                                bool enable);
+int __must_check
+drm_dp_mst_add_affected_dsc_crtcs(struct drm_atomic_state *state,
+                                 struct drm_dp_mst_topology_mgr *mgr);
+int __must_check
+drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
+                                struct drm_dp_mst_topology_mgr *mgr,
+                                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);
+void drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port);
+
+struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port);
+
+extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs;
+
+/**
+ * __drm_dp_mst_state_iter_get - private atomic state iterator function for
+ * macro-internal use
+ * @state: &struct drm_atomic_state pointer
+ * @mgr: pointer to the &struct drm_dp_mst_topology_mgr iteration cursor
+ * @old_state: optional pointer to the old &struct drm_dp_mst_topology_state
+ * iteration cursor
+ * @new_state: optional pointer to the new &struct drm_dp_mst_topology_state
+ * iteration cursor
+ * @i: int iteration cursor, for macro-internal use
+ *
+ * Used by for_each_oldnew_mst_mgr_in_state(),
+ * for_each_old_mst_mgr_in_state(), and for_each_new_mst_mgr_in_state(). Don't
+ * call this directly.
+ *
+ * Returns:
+ * True if the current &struct drm_private_obj is a &struct
+ * drm_dp_mst_topology_mgr, false otherwise.
+ */
+static inline bool
+__drm_dp_mst_state_iter_get(struct drm_atomic_state *state,
+                           struct drm_dp_mst_topology_mgr **mgr,
+                           struct drm_dp_mst_topology_state **old_state,
+                           struct drm_dp_mst_topology_state **new_state,
+                           int i)
+{
+       struct __drm_private_objs_state *objs_state = &state->private_objs[i];
+
+       if (objs_state->ptr->funcs != &drm_dp_mst_topology_state_funcs)
+               return false;
+
+       *mgr = to_dp_mst_topology_mgr(objs_state->ptr);
+       if (old_state)
+               *old_state = to_dp_mst_topology_state(objs_state->old_state);
+       if (new_state)
+               *new_state = to_dp_mst_topology_state(objs_state->new_state);
+
+       return true;
+}
+
+/**
+ * for_each_oldnew_mst_mgr_in_state - iterate over all DP MST topology
+ * managers in an atomic update
+ * @__state: &struct drm_atomic_state pointer
+ * @mgr: &struct drm_dp_mst_topology_mgr iteration cursor
+ * @old_state: &struct drm_dp_mst_topology_state iteration cursor for the old
+ * state
+ * @new_state: &struct drm_dp_mst_topology_state iteration cursor for the new
+ * state
+ * @__i: int iteration cursor, for macro-internal use
+ *
+ * This iterates over all DRM DP MST topology managers in an atomic update,
+ * tracking both old and new state. This is useful in places where the state
+ * delta needs to be considered, for example in atomic check functions.
+ */
+#define for_each_oldnew_mst_mgr_in_state(__state, mgr, old_state, new_state, __i) \
+       for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
+               for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), &(new_state), (__i)))
+
+/**
+ * for_each_old_mst_mgr_in_state - iterate over all DP MST topology managers
+ * in an atomic update
+ * @__state: &struct drm_atomic_state pointer
+ * @mgr: &struct drm_dp_mst_topology_mgr iteration cursor
+ * @old_state: &struct drm_dp_mst_topology_state iteration cursor for the old
+ * state
+ * @__i: int iteration cursor, for macro-internal use
+ *
+ * This iterates over all DRM DP MST topology managers in an atomic update,
+ * tracking only the old state. This is useful in disable functions, where we
+ * need the old state the hardware is still in.
+ */
+#define for_each_old_mst_mgr_in_state(__state, mgr, old_state, __i) \
+       for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
+               for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), NULL, (__i)))
+
+/**
+ * for_each_new_mst_mgr_in_state - iterate over all DP MST topology managers
+ * in an atomic update
+ * @__state: &struct drm_atomic_state pointer
+ * @mgr: &struct drm_dp_mst_topology_mgr iteration cursor
+ * @new_state: &struct drm_dp_mst_topology_state iteration cursor for the new
+ * state
+ * @__i: int iteration cursor, for macro-internal use
+ *
+ * This iterates over all DRM DP MST topology managers in an atomic update,
+ * tracking only the new state. This is useful in enable functions, where we
+ * need the new state the hardware should be in when the atomic commit
+ * operation has completed.
+ */
+#define for_each_new_mst_mgr_in_state(__state, mgr, new_state, __i) \
+       for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
+               for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), NULL, &(new_state), (__i)))
+
+#endif
diff --git a/include/drm/drm_buddy.h b/include/drm/drm_buddy.h
new file mode 100644 (file)
index 0000000..f524db1
--- /dev/null
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2021 Intel Corporation
+ */
+
+#ifndef __DRM_BUDDY_H__
+#define __DRM_BUDDY_H__
+
+#include <linux/bitops.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#include <drm/drm_print.h>
+
+#define range_overflows(start, size, max) ({ \
+       typeof(start) start__ = (start); \
+       typeof(size) size__ = (size); \
+       typeof(max) max__ = (max); \
+       (void)(&start__ == &size__); \
+       (void)(&start__ == &max__); \
+       start__ >= max__ || size__ > max__ - start__; \
+})
+
+struct drm_buddy_block {
+#define DRM_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12)
+#define DRM_BUDDY_HEADER_STATE  GENMASK_ULL(11, 10)
+#define   DRM_BUDDY_ALLOCATED     (1 << 10)
+#define   DRM_BUDDY_FREE          (2 << 10)
+#define   DRM_BUDDY_SPLIT         (3 << 10)
+/* Free to be used, if needed in the future */
+#define DRM_BUDDY_HEADER_UNUSED GENMASK_ULL(9, 6)
+#define DRM_BUDDY_HEADER_ORDER  GENMASK_ULL(5, 0)
+       u64 header;
+
+       struct drm_buddy_block *left;
+       struct drm_buddy_block *right;
+       struct drm_buddy_block *parent;
+
+       void *private; /* owned by creator */
+
+       /*
+        * While the block is allocated by the user through drm_buddy_alloc*,
+        * the user has ownership of the link, for example to maintain within
+        * a list, if so desired. As soon as the block is freed with
+        * drm_buddy_free* ownership is given back to the mm.
+        */
+       struct list_head link;
+       struct list_head tmp_link;
+};
+
+/* Order-zero must be at least PAGE_SIZE */
+#define DRM_BUDDY_MAX_ORDER (63 - PAGE_SHIFT)
+
+/*
+ * Binary Buddy System.
+ *
+ * Locking should be handled by the user, a simple mutex around
+ * drm_buddy_alloc* and drm_buddy_free* should suffice.
+ */
+struct drm_buddy {
+       /* Maintain a free list for each order. */
+       struct list_head *free_list;
+
+       /*
+        * Maintain explicit binary tree(s) to track the allocation of the
+        * address space. This gives us a simple way of finding a buddy block
+        * and performing the potentially recursive merge step when freeing a
+        * block.  Nodes are either allocated or free, in which case they will
+        * also exist on the respective free list.
+        */
+       struct drm_buddy_block **roots;
+
+       /*
+        * Anything from here is public, and remains static for the lifetime of
+        * the mm. Everything above is considered do-not-touch.
+        */
+       unsigned int n_roots;
+       unsigned int max_order;
+
+       /* Must be at least PAGE_SIZE */
+       u64 chunk_size;
+       u64 size;
+       u64 avail;
+};
+
+static inline u64
+drm_buddy_block_offset(struct drm_buddy_block *block)
+{
+       return block->header & DRM_BUDDY_HEADER_OFFSET;
+}
+
+static inline unsigned int
+drm_buddy_block_order(struct drm_buddy_block *block)
+{
+       return block->header & DRM_BUDDY_HEADER_ORDER;
+}
+
+static inline unsigned int
+drm_buddy_block_state(struct drm_buddy_block *block)
+{
+       return block->header & DRM_BUDDY_HEADER_STATE;
+}
+
+static inline bool
+drm_buddy_block_is_allocated(struct drm_buddy_block *block)
+{
+       return drm_buddy_block_state(block) == DRM_BUDDY_ALLOCATED;
+}
+
+static inline bool
+drm_buddy_block_is_free(struct drm_buddy_block *block)
+{
+       return drm_buddy_block_state(block) == DRM_BUDDY_FREE;
+}
+
+static inline bool
+drm_buddy_block_is_split(struct drm_buddy_block *block)
+{
+       return drm_buddy_block_state(block) == DRM_BUDDY_SPLIT;
+}
+
+static inline u64
+drm_buddy_block_size(struct drm_buddy *mm,
+                    struct drm_buddy_block *block)
+{
+       return mm->chunk_size << drm_buddy_block_order(block);
+}
+
+int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size);
+
+void drm_buddy_fini(struct drm_buddy *mm);
+
+struct drm_buddy_block *
+drm_buddy_alloc_blocks(struct drm_buddy *mm, unsigned int order);
+
+int drm_buddy_alloc_range(struct drm_buddy *mm,
+                         struct list_head *blocks,
+                         u64 start, u64 size);
+
+void drm_buddy_free_block(struct drm_buddy *mm, struct drm_buddy_block *block);
+
+void drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects);
+
+void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p);
+void drm_buddy_block_print(struct drm_buddy *mm,
+                          struct drm_buddy_block *block,
+                          struct drm_printer *p);
+
+#endif
index b501d0b..64cf5f8 100644 (file)
@@ -522,9 +522,9 @@ struct drm_display_info {
        enum subpixel_order subpixel_order;
 
 #define DRM_COLOR_FORMAT_RGB444                (1<<0)
-#define DRM_COLOR_FORMAT_YCRCB444      (1<<1)
-#define DRM_COLOR_FORMAT_YCRCB422      (1<<2)
-#define DRM_COLOR_FORMAT_YCRCB420      (1<<3)
+#define DRM_COLOR_FORMAT_YCBCR444      (1<<1)
+#define DRM_COLOR_FORMAT_YCBCR422      (1<<2)
+#define DRM_COLOR_FORMAT_YCBCR420      (1<<3)
 
        /**
         * @panel_orientation: Read only connector property for built-in panels,
@@ -592,10 +592,16 @@ struct drm_display_info {
        bool rgb_quant_range_selectable;
 
        /**
-        * @edid_hdmi_dc_modes: Mask of supported hdmi deep color modes. Even
-        * more stuff redundant with @bus_formats.
+        * @edid_hdmi_dc_rgb444_modes: Mask of supported hdmi deep color modes
+        * in RGB 4:4:4. Even more stuff redundant with @bus_formats.
         */
-       u8 edid_hdmi_dc_modes;
+       u8 edid_hdmi_rgb444_dc_modes;
+
+       /**
+        * @edid_hdmi_dc_ycbcr444_modes: Mask of supported hdmi deep color
+        * modes in YCbCr 4:4:4. Even more stuff redundant with @bus_formats.
+        */
+       u8 edid_hdmi_ycbcr444_dc_modes;
 
        /**
         * @cea_rev: CEA revision of the HDMI sink.
index 13eeba2..f9f9af8 100644 (file)
@@ -285,6 +285,10 @@ struct drm_crtc_state {
         * Lookup table for converting pixel data after the color conversion
         * matrix @ctm.  See drm_crtc_enable_color_mgmt(). The blob (if not
         * NULL) is an array of &struct drm_color_lut.
+        *
+        * Note that for mostly historical reasons stemming from Xorg heritage,
+        * this is also used to store the color map (also sometimes color lut,
+        * CLUT or color palette) for indexed formats like DRM_FORMAT_C8.
         */
        struct drm_property_blob *gamma_lut;
 
@@ -1075,12 +1079,18 @@ struct drm_crtc {
        /**
         * @gamma_size: Size of legacy gamma ramp reported to userspace. Set up
         * by calling drm_mode_crtc_set_gamma_size().
+        *
+        * Note that atomic drivers need to instead use
+        * &drm_crtc_state.gamma_lut. See drm_crtc_enable_color_mgmt().
         */
        uint32_t gamma_size;
 
        /**
         * @gamma_store: Gamma ramp values used by the legacy SETGAMMA and
         * GETGAMMA IOCTls. Set up by calling drm_mode_crtc_set_gamma_size().
+        *
+        * Note that atomic drivers need to instead use
+        * &drm_crtc_state.gamma_lut. See drm_crtc_enable_color_mgmt().
         */
        uint16_t *gamma_store;
 
diff --git a/include/drm/drm_dp_aux_bus.h b/include/drm/drm_dp_aux_bus.h
deleted file mode 100644 (file)
index 4f19b20..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright 2021 Google Inc.
- *
- * The DP AUX bus is used for devices that are connected over a DisplayPort
- * AUX bus. The devices on the far side of the bus are referred to as
- * endpoints in this code.
- */
-
-#ifndef _DP_AUX_BUS_H_
-#define _DP_AUX_BUS_H_
-
-#include <linux/device.h>
-#include <linux/mod_devicetable.h>
-
-/**
- * struct dp_aux_ep_device - Main dev structure for DP AUX endpoints
- *
- * This is used to instantiate devices that are connected via a DP AUX
- * bus. Usually the device is a panel, but conceivable other devices could
- * be hooked up there.
- */
-struct dp_aux_ep_device {
-       /** @dev: The normal dev pointer */
-       struct device dev;
-       /** @aux: Pointer to the aux bus */
-       struct drm_dp_aux *aux;
-};
-
-struct dp_aux_ep_driver {
-       int (*probe)(struct dp_aux_ep_device *aux_ep);
-       void (*remove)(struct dp_aux_ep_device *aux_ep);
-       void (*shutdown)(struct dp_aux_ep_device *aux_ep);
-       struct device_driver driver;
-};
-
-static inline struct dp_aux_ep_device *to_dp_aux_ep_dev(struct device *dev)
-{
-       return container_of(dev, struct dp_aux_ep_device, dev);
-}
-
-static inline struct dp_aux_ep_driver *to_dp_aux_ep_drv(struct device_driver *drv)
-{
-       return container_of(drv, struct dp_aux_ep_driver, driver);
-}
-
-int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux);
-void of_dp_aux_depopulate_ep_devices(struct drm_dp_aux *aux);
-int devm_of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux);
-
-#define dp_aux_dp_driver_register(aux_ep_drv) \
-       __dp_aux_dp_driver_register(aux_ep_drv, THIS_MODULE)
-int __dp_aux_dp_driver_register(struct dp_aux_ep_driver *aux_ep_drv,
-                               struct module *owner);
-void dp_aux_dp_driver_unregister(struct dp_aux_ep_driver *aux_ep_drv);
-
-#endif /* _DP_AUX_BUS_H_ */
diff --git a/include/drm/drm_dp_dual_mode_helper.h b/include/drm/drm_dp_dual_mode_helper.h
deleted file mode 100644 (file)
index 7ee4822..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright © 2016 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef DRM_DP_DUAL_MODE_HELPER_H
-#define DRM_DP_DUAL_MODE_HELPER_H
-
-#include <linux/types.h>
-
-/*
- * Optional for type 1 DVI adaptors
- * Mandatory for type 1 HDMI and type 2 adaptors
- */
-#define DP_DUAL_MODE_HDMI_ID 0x00 /* 00-0f */
-#define  DP_DUAL_MODE_HDMI_ID_LEN 16
-/*
- * Optional for type 1 adaptors
- * Mandatory for type 2 adaptors
- */
-#define DP_DUAL_MODE_ADAPTOR_ID 0x10
-#define  DP_DUAL_MODE_REV_MASK 0x07
-#define  DP_DUAL_MODE_REV_TYPE2 0x00
-#define  DP_DUAL_MODE_TYPE_MASK 0xf0
-#define  DP_DUAL_MODE_TYPE_TYPE2 0xa0
-/* This field is marked reserved in dual mode spec, used in LSPCON */
-#define  DP_DUAL_MODE_TYPE_HAS_DPCD 0x08
-#define DP_DUAL_MODE_IEEE_OUI 0x11 /* 11-13*/
-#define  DP_DUAL_IEEE_OUI_LEN 3
-#define DP_DUAL_DEVICE_ID 0x14 /* 14-19 */
-#define  DP_DUAL_DEVICE_ID_LEN 6
-#define DP_DUAL_MODE_HARDWARE_REV 0x1a
-#define DP_DUAL_MODE_FIRMWARE_MAJOR_REV 0x1b
-#define DP_DUAL_MODE_FIRMWARE_MINOR_REV 0x1c
-#define DP_DUAL_MODE_MAX_TMDS_CLOCK 0x1d
-#define DP_DUAL_MODE_I2C_SPEED_CAP 0x1e
-#define DP_DUAL_MODE_TMDS_OEN 0x20
-#define  DP_DUAL_MODE_TMDS_DISABLE 0x01
-#define DP_DUAL_MODE_HDMI_PIN_CTRL 0x21
-#define  DP_DUAL_MODE_CEC_ENABLE 0x01
-#define DP_DUAL_MODE_I2C_SPEED_CTRL 0x22
-
-/* LSPCON specific registers, defined by MCA */
-#define DP_DUAL_MODE_LSPCON_MODE_CHANGE                0x40
-#define DP_DUAL_MODE_LSPCON_CURRENT_MODE               0x41
-#define  DP_DUAL_MODE_LSPCON_MODE_PCON                 0x1
-
-struct drm_device;
-struct i2c_adapter;
-
-ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter,
-                             u8 offset, void *buffer, size_t size);
-ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter,
-                              u8 offset, const void *buffer, size_t size);
-
-/**
- * enum drm_lspcon_mode
- * @DRM_LSPCON_MODE_INVALID: No LSPCON.
- * @DRM_LSPCON_MODE_LS: Level shifter mode of LSPCON
- *     which drives DP++ to HDMI 1.4 conversion.
- * @DRM_LSPCON_MODE_PCON: Protocol converter mode of LSPCON
- *     which drives DP++ to HDMI 2.0 active conversion.
- */
-enum drm_lspcon_mode {
-       DRM_LSPCON_MODE_INVALID,
-       DRM_LSPCON_MODE_LS,
-       DRM_LSPCON_MODE_PCON,
-};
-
-/**
- * enum drm_dp_dual_mode_type - Type of the DP dual mode adaptor
- * @DRM_DP_DUAL_MODE_NONE: No DP dual mode adaptor
- * @DRM_DP_DUAL_MODE_UNKNOWN: Could be either none or type 1 DVI adaptor
- * @DRM_DP_DUAL_MODE_TYPE1_DVI: Type 1 DVI adaptor
- * @DRM_DP_DUAL_MODE_TYPE1_HDMI: Type 1 HDMI adaptor
- * @DRM_DP_DUAL_MODE_TYPE2_DVI: Type 2 DVI adaptor
- * @DRM_DP_DUAL_MODE_TYPE2_HDMI: Type 2 HDMI adaptor
- * @DRM_DP_DUAL_MODE_LSPCON: Level shifter / protocol converter
- */
-enum drm_dp_dual_mode_type {
-       DRM_DP_DUAL_MODE_NONE,
-       DRM_DP_DUAL_MODE_UNKNOWN,
-       DRM_DP_DUAL_MODE_TYPE1_DVI,
-       DRM_DP_DUAL_MODE_TYPE1_HDMI,
-       DRM_DP_DUAL_MODE_TYPE2_DVI,
-       DRM_DP_DUAL_MODE_TYPE2_HDMI,
-       DRM_DP_DUAL_MODE_LSPCON,
-};
-
-enum drm_dp_dual_mode_type
-drm_dp_dual_mode_detect(const struct drm_device *dev, struct i2c_adapter *adapter);
-int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
-                                   struct i2c_adapter *adapter);
-int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
-                                    struct i2c_adapter *adapter, bool *enabled);
-int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type,
-                                    struct i2c_adapter *adapter, bool enable);
-const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type);
-
-int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
-                       enum drm_lspcon_mode *current_mode);
-int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter,
-                       enum drm_lspcon_mode reqd_mode);
-#endif
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
deleted file mode 100644 (file)
index 30359e4..0000000
+++ /dev/null
@@ -1,2368 +0,0 @@
-/*
- * Copyright © 2008 Keith Packard
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that copyright
- * notice and this permission notice appear in supporting documentation, and
- * that the name of the copyright holders not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission.  The copyright holders make no representations
- * about the suitability of this software for any purpose.  It is provided "as
- * is" without express or implied warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
- */
-
-#ifndef _DRM_DP_HELPER_H_
-#define _DRM_DP_HELPER_H_
-
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/types.h>
-#include <drm/drm_connector.h>
-
-struct drm_device;
-struct drm_dp_aux;
-struct drm_panel;
-
-/*
- * 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,
- * 1.0 devices basically don't exist in the wild.
- *
- * Abbreviations, in chronological order:
- *
- * eDP: Embedded DisplayPort version 1
- * DPI: DisplayPort Interoperability Guideline v1.1a
- * 1.2: DisplayPort 1.2
- * MST: Multistream Transport - part of DP 1.2a
- *
- * 1.2 formally includes both eDP and DPI definitions.
- */
-
-/* MSA (Main Stream Attribute) MISC bits (as MISC1<<8|MISC0) */
-#define DP_MSA_MISC_SYNC_CLOCK                 (1 << 0)
-#define DP_MSA_MISC_INTERLACE_VTOTAL_EVEN      (1 << 8)
-#define DP_MSA_MISC_STEREO_NO_3D               (0 << 9)
-#define DP_MSA_MISC_STEREO_PROG_RIGHT_EYE      (1 << 9)
-#define DP_MSA_MISC_STEREO_PROG_LEFT_EYE       (3 << 9)
-/* bits per component for non-RAW */
-#define DP_MSA_MISC_6_BPC                      (0 << 5)
-#define DP_MSA_MISC_8_BPC                      (1 << 5)
-#define DP_MSA_MISC_10_BPC                     (2 << 5)
-#define DP_MSA_MISC_12_BPC                     (3 << 5)
-#define DP_MSA_MISC_16_BPC                     (4 << 5)
-/* bits per component for RAW */
-#define DP_MSA_MISC_RAW_6_BPC                  (1 << 5)
-#define DP_MSA_MISC_RAW_7_BPC                  (2 << 5)
-#define DP_MSA_MISC_RAW_8_BPC                  (3 << 5)
-#define DP_MSA_MISC_RAW_10_BPC                 (4 << 5)
-#define DP_MSA_MISC_RAW_12_BPC                 (5 << 5)
-#define DP_MSA_MISC_RAW_14_BPC                 (6 << 5)
-#define DP_MSA_MISC_RAW_16_BPC                 (7 << 5)
-/* pixel encoding/colorimetry format */
-#define _DP_MSA_MISC_COLOR(misc1_7, misc0_21, misc0_3, misc0_4) \
-       ((misc1_7) << 15 | (misc0_4) << 4 | (misc0_3) << 3 | ((misc0_21) << 1))
-#define DP_MSA_MISC_COLOR_RGB                  _DP_MSA_MISC_COLOR(0, 0, 0, 0)
-#define DP_MSA_MISC_COLOR_CEA_RGB              _DP_MSA_MISC_COLOR(0, 0, 1, 0)
-#define DP_MSA_MISC_COLOR_RGB_WIDE_FIXED       _DP_MSA_MISC_COLOR(0, 3, 0, 0)
-#define DP_MSA_MISC_COLOR_RGB_WIDE_FLOAT       _DP_MSA_MISC_COLOR(0, 3, 0, 1)
-#define DP_MSA_MISC_COLOR_Y_ONLY               _DP_MSA_MISC_COLOR(1, 0, 0, 0)
-#define DP_MSA_MISC_COLOR_RAW                  _DP_MSA_MISC_COLOR(1, 1, 0, 0)
-#define DP_MSA_MISC_COLOR_YCBCR_422_BT601      _DP_MSA_MISC_COLOR(0, 1, 1, 0)
-#define DP_MSA_MISC_COLOR_YCBCR_422_BT709      _DP_MSA_MISC_COLOR(0, 1, 1, 1)
-#define DP_MSA_MISC_COLOR_YCBCR_444_BT601      _DP_MSA_MISC_COLOR(0, 2, 1, 0)
-#define DP_MSA_MISC_COLOR_YCBCR_444_BT709      _DP_MSA_MISC_COLOR(0, 2, 1, 1)
-#define DP_MSA_MISC_COLOR_XVYCC_422_BT601      _DP_MSA_MISC_COLOR(0, 1, 0, 0)
-#define DP_MSA_MISC_COLOR_XVYCC_422_BT709      _DP_MSA_MISC_COLOR(0, 1, 0, 1)
-#define DP_MSA_MISC_COLOR_XVYCC_444_BT601      _DP_MSA_MISC_COLOR(0, 2, 0, 0)
-#define DP_MSA_MISC_COLOR_XVYCC_444_BT709      _DP_MSA_MISC_COLOR(0, 2, 0, 1)
-#define DP_MSA_MISC_COLOR_OPRGB                        _DP_MSA_MISC_COLOR(0, 0, 1, 1)
-#define DP_MSA_MISC_COLOR_DCI_P3               _DP_MSA_MISC_COLOR(0, 3, 1, 0)
-#define DP_MSA_MISC_COLOR_COLOR_PROFILE                _DP_MSA_MISC_COLOR(0, 3, 1, 1)
-#define DP_MSA_MISC_COLOR_VSC_SDP              (1 << 14)
-
-#define DP_AUX_MAX_PAYLOAD_BYTES       16
-
-#define DP_AUX_I2C_WRITE               0x0
-#define DP_AUX_I2C_READ                        0x1
-#define DP_AUX_I2C_WRITE_STATUS_UPDATE 0x2
-#define DP_AUX_I2C_MOT                 0x4
-#define DP_AUX_NATIVE_WRITE            0x8
-#define DP_AUX_NATIVE_READ             0x9
-
-#define DP_AUX_NATIVE_REPLY_ACK                (0x0 << 0)
-#define DP_AUX_NATIVE_REPLY_NACK       (0x1 << 0)
-#define DP_AUX_NATIVE_REPLY_DEFER      (0x2 << 0)
-#define DP_AUX_NATIVE_REPLY_MASK       (0x3 << 0)
-
-#define DP_AUX_I2C_REPLY_ACK           (0x0 << 2)
-#define DP_AUX_I2C_REPLY_NACK          (0x1 << 2)
-#define DP_AUX_I2C_REPLY_DEFER         (0x2 << 2)
-#define DP_AUX_I2C_REPLY_MASK          (0x3 << 2)
-
-/* DPCD Field Address Mapping */
-
-/* Receiver Capability */
-#define DP_DPCD_REV                         0x000
-# define DP_DPCD_REV_10                     0x10
-# define DP_DPCD_REV_11                     0x11
-# define DP_DPCD_REV_12                     0x12
-# define DP_DPCD_REV_13                     0x13
-# define DP_DPCD_REV_14                     0x14
-
-#define DP_MAX_LINK_RATE                    0x001
-
-#define DP_MAX_LANE_COUNT                   0x002
-# define DP_MAX_LANE_COUNT_MASK                    0x1f
-# define DP_TPS3_SUPPORTED                 (1 << 6) /* 1.2 */
-# define DP_ENHANCED_FRAME_CAP             (1 << 7)
-
-#define DP_MAX_DOWNSPREAD                   0x003
-# define DP_MAX_DOWNSPREAD_0_5             (1 << 0)
-# define DP_STREAM_REGENERATION_STATUS_CAP  (1 << 1) /* 2.0 */
-# define DP_NO_AUX_HANDSHAKE_LINK_TRAINING  (1 << 6)
-# define DP_TPS4_SUPPORTED                  (1 << 7)
-
-#define DP_NORP                             0x004
-
-#define DP_DOWNSTREAMPORT_PRESENT           0x005
-# define DP_DWN_STRM_PORT_PRESENT           (1 << 0)
-# define DP_DWN_STRM_PORT_TYPE_MASK         0x06
-# define DP_DWN_STRM_PORT_TYPE_DP           (0 << 1)
-# define DP_DWN_STRM_PORT_TYPE_ANALOG       (1 << 1)
-# define DP_DWN_STRM_PORT_TYPE_TMDS         (2 << 1)
-# define DP_DWN_STRM_PORT_TYPE_OTHER        (3 << 1)
-# define DP_FORMAT_CONVERSION               (1 << 3)
-# define DP_DETAILED_CAP_INFO_AVAILABLE            (1 << 4) /* DPI */
-
-#define DP_MAIN_LINK_CHANNEL_CODING         0x006
-# define DP_CAP_ANSI_8B10B                 (1 << 0)
-# define DP_CAP_ANSI_128B132B               (1 << 1) /* 2.0 */
-
-#define DP_DOWN_STREAM_PORT_COUNT          0x007
-# define DP_PORT_COUNT_MASK                0x0f
-# define DP_MSA_TIMING_PAR_IGNORED         (1 << 6) /* eDP */
-# define DP_OUI_SUPPORT                            (1 << 7)
-
-#define DP_RECEIVE_PORT_0_CAP_0                    0x008
-# define DP_LOCAL_EDID_PRESENT             (1 << 1)
-# define DP_ASSOCIATED_TO_PRECEDING_PORT    (1 << 2)
-
-#define DP_RECEIVE_PORT_0_BUFFER_SIZE      0x009
-
-#define DP_RECEIVE_PORT_1_CAP_0                    0x00a
-#define DP_RECEIVE_PORT_1_BUFFER_SIZE       0x00b
-
-#define DP_I2C_SPEED_CAP                   0x00c    /* DPI */
-# define DP_I2C_SPEED_1K                   0x01
-# define DP_I2C_SPEED_5K                   0x02
-# define DP_I2C_SPEED_10K                  0x04
-# define DP_I2C_SPEED_100K                 0x08
-# define DP_I2C_SPEED_400K                 0x10
-# define DP_I2C_SPEED_1M                   0x20
-
-#define DP_EDP_CONFIGURATION_CAP            0x00d   /* XXX 1.2? */
-# define DP_ALTERNATE_SCRAMBLER_RESET_CAP   (1 << 0)
-# define DP_FRAMING_CHANGE_CAP             (1 << 1)
-# define DP_DPCD_DISPLAY_CONTROL_CAPABLE     (1 << 3) /* edp v1.2 or higher */
-
-#define DP_TRAINING_AUX_RD_INTERVAL             0x00e   /* XXX 1.2? */
-# define DP_TRAINING_AUX_RD_MASK                0x7F    /* DP 1.3 */
-# define DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT        (1 << 7) /* DP 1.3 */
-
-#define DP_ADAPTER_CAP                     0x00f   /* 1.2 */
-# define DP_FORCE_LOAD_SENSE_CAP           (1 << 0)
-# define DP_ALTERNATE_I2C_PATTERN_CAP      (1 << 1)
-
-#define DP_SUPPORTED_LINK_RATES                    0x010 /* eDP 1.4 */
-# define DP_MAX_SUPPORTED_RATES                     8      /* 16-bit little-endian */
-
-/* Multiple stream transport */
-#define DP_FAUX_CAP                        0x020   /* 1.2 */
-# define DP_FAUX_CAP_1                     (1 << 0)
-
-#define DP_SINK_VIDEO_FALLBACK_FORMATS      0x020   /* 2.0 */
-# define DP_FALLBACK_1024x768_60HZ_24BPP    (1 << 0)
-# define DP_FALLBACK_1280x720_60HZ_24BPP    (1 << 1)
-# define DP_FALLBACK_1920x1080_60HZ_24BPP   (1 << 2)
-
-#define DP_MSTM_CAP                        0x021   /* 1.2 */
-# define DP_MST_CAP                        (1 << 0)
-# define DP_SINGLE_STREAM_SIDEBAND_MSG      (1 << 1) /* 2.0 */
-
-#define DP_NUMBER_OF_AUDIO_ENDPOINTS       0x022   /* 1.2 */
-
-/* AV_SYNC_DATA_BLOCK                                  1.2 */
-#define DP_AV_GRANULARITY                  0x023
-# define DP_AG_FACTOR_MASK                 (0xf << 0)
-# define DP_AG_FACTOR_3MS                  (0 << 0)
-# define DP_AG_FACTOR_2MS                  (1 << 0)
-# define DP_AG_FACTOR_1MS                  (2 << 0)
-# define DP_AG_FACTOR_500US                (3 << 0)
-# define DP_AG_FACTOR_200US                (4 << 0)
-# define DP_AG_FACTOR_100US                (5 << 0)
-# define DP_AG_FACTOR_10US                 (6 << 0)
-# define DP_AG_FACTOR_1US                  (7 << 0)
-# define DP_VG_FACTOR_MASK                 (0xf << 4)
-# define DP_VG_FACTOR_3MS                  (0 << 4)
-# define DP_VG_FACTOR_2MS                  (1 << 4)
-# define DP_VG_FACTOR_1MS                  (2 << 4)
-# define DP_VG_FACTOR_500US                (3 << 4)
-# define DP_VG_FACTOR_200US                (4 << 4)
-# define DP_VG_FACTOR_100US                (5 << 4)
-
-#define DP_AUD_DEC_LAT0                            0x024
-#define DP_AUD_DEC_LAT1                            0x025
-
-#define DP_AUD_PP_LAT0                     0x026
-#define DP_AUD_PP_LAT1                     0x027
-
-#define DP_VID_INTER_LAT                   0x028
-
-#define DP_VID_PROG_LAT                            0x029
-
-#define DP_REP_LAT                         0x02a
-
-#define DP_AUD_DEL_INS0                            0x02b
-#define DP_AUD_DEL_INS1                            0x02c
-#define DP_AUD_DEL_INS2                            0x02d
-/* End of AV_SYNC_DATA_BLOCK */
-
-#define DP_RECEIVER_ALPM_CAP               0x02e   /* eDP 1.4 */
-# define DP_ALPM_CAP                       (1 << 0)
-
-#define DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP   0x02f   /* eDP 1.4 */
-# define DP_AUX_FRAME_SYNC_CAP             (1 << 0)
-
-#define DP_GUID                                    0x030   /* 1.2 */
-
-#define DP_DSC_SUPPORT                      0x060   /* DP 1.4 */
-# define DP_DSC_DECOMPRESSION_IS_SUPPORTED  (1 << 0)
-
-#define DP_DSC_REV                          0x061
-# define DP_DSC_MAJOR_MASK                  (0xf << 0)
-# define DP_DSC_MINOR_MASK                  (0xf << 4)
-# define DP_DSC_MAJOR_SHIFT                 0
-# define DP_DSC_MINOR_SHIFT                 4
-
-#define DP_DSC_RC_BUF_BLK_SIZE              0x062
-# define DP_DSC_RC_BUF_BLK_SIZE_1           0x0
-# define DP_DSC_RC_BUF_BLK_SIZE_4           0x1
-# define DP_DSC_RC_BUF_BLK_SIZE_16          0x2
-# define DP_DSC_RC_BUF_BLK_SIZE_64          0x3
-
-#define DP_DSC_RC_BUF_SIZE                  0x063
-
-#define DP_DSC_SLICE_CAP_1                  0x064
-# define DP_DSC_1_PER_DP_DSC_SINK           (1 << 0)
-# define DP_DSC_2_PER_DP_DSC_SINK           (1 << 1)
-# define DP_DSC_4_PER_DP_DSC_SINK           (1 << 3)
-# define DP_DSC_6_PER_DP_DSC_SINK           (1 << 4)
-# define DP_DSC_8_PER_DP_DSC_SINK           (1 << 5)
-# define DP_DSC_10_PER_DP_DSC_SINK          (1 << 6)
-# define DP_DSC_12_PER_DP_DSC_SINK          (1 << 7)
-
-#define DP_DSC_LINE_BUF_BIT_DEPTH           0x065
-# define DP_DSC_LINE_BUF_BIT_DEPTH_MASK     (0xf << 0)
-# define DP_DSC_LINE_BUF_BIT_DEPTH_9        0x0
-# define DP_DSC_LINE_BUF_BIT_DEPTH_10       0x1
-# define DP_DSC_LINE_BUF_BIT_DEPTH_11       0x2
-# define DP_DSC_LINE_BUF_BIT_DEPTH_12       0x3
-# define DP_DSC_LINE_BUF_BIT_DEPTH_13       0x4
-# define DP_DSC_LINE_BUF_BIT_DEPTH_14       0x5
-# define DP_DSC_LINE_BUF_BIT_DEPTH_15       0x6
-# define DP_DSC_LINE_BUF_BIT_DEPTH_16       0x7
-# define DP_DSC_LINE_BUF_BIT_DEPTH_8        0x8
-
-#define DP_DSC_BLK_PREDICTION_SUPPORT       0x066
-# define DP_DSC_BLK_PREDICTION_IS_SUPPORTED (1 << 0)
-
-#define DP_DSC_MAX_BITS_PER_PIXEL_LOW       0x067   /* eDP 1.4 */
-
-#define DP_DSC_MAX_BITS_PER_PIXEL_HI        0x068   /* eDP 1.4 */
-# define DP_DSC_MAX_BITS_PER_PIXEL_HI_MASK  (0x3 << 0)
-# define DP_DSC_MAX_BITS_PER_PIXEL_HI_SHIFT 8
-
-#define DP_DSC_DEC_COLOR_FORMAT_CAP         0x069
-# define DP_DSC_RGB                         (1 << 0)
-# define DP_DSC_YCbCr444                    (1 << 1)
-# define DP_DSC_YCbCr422_Simple             (1 << 2)
-# define DP_DSC_YCbCr422_Native             (1 << 3)
-# define DP_DSC_YCbCr420_Native             (1 << 4)
-
-#define DP_DSC_DEC_COLOR_DEPTH_CAP          0x06A
-# define DP_DSC_8_BPC                       (1 << 1)
-# define DP_DSC_10_BPC                      (1 << 2)
-# define DP_DSC_12_BPC                      (1 << 3)
-
-#define DP_DSC_PEAK_THROUGHPUT              0x06B
-# define DP_DSC_THROUGHPUT_MODE_0_MASK      (0xf << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_SHIFT     0
-# define DP_DSC_THROUGHPUT_MODE_0_UNSUPPORTED 0
-# define DP_DSC_THROUGHPUT_MODE_0_340       (1 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_400       (2 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_450       (3 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_500       (4 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_550       (5 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_600       (6 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_650       (7 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_700       (8 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_750       (9 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_800       (10 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_850       (11 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_900       (12 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_950       (13 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_1000      (14 << 0)
-# define DP_DSC_THROUGHPUT_MODE_0_170       (15 << 0) /* 1.4a */
-# define DP_DSC_THROUGHPUT_MODE_1_MASK      (0xf << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_SHIFT     4
-# define DP_DSC_THROUGHPUT_MODE_1_UNSUPPORTED 0
-# define DP_DSC_THROUGHPUT_MODE_1_340       (1 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_400       (2 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_450       (3 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_500       (4 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_550       (5 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_600       (6 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_650       (7 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_700       (8 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_750       (9 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_800       (10 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_850       (11 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_900       (12 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_950       (13 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_1000      (14 << 4)
-# define DP_DSC_THROUGHPUT_MODE_1_170       (15 << 4)
-
-#define DP_DSC_MAX_SLICE_WIDTH              0x06C
-#define DP_DSC_MIN_SLICE_WIDTH_VALUE        2560
-#define DP_DSC_SLICE_WIDTH_MULTIPLIER       320
-
-#define DP_DSC_SLICE_CAP_2                  0x06D
-# define DP_DSC_16_PER_DP_DSC_SINK          (1 << 0)
-# define DP_DSC_20_PER_DP_DSC_SINK          (1 << 1)
-# define DP_DSC_24_PER_DP_DSC_SINK          (1 << 2)
-
-#define DP_DSC_BITS_PER_PIXEL_INC           0x06F
-# define DP_DSC_BITS_PER_PIXEL_1_16         0x0
-# define DP_DSC_BITS_PER_PIXEL_1_8          0x1
-# define DP_DSC_BITS_PER_PIXEL_1_4          0x2
-# define DP_DSC_BITS_PER_PIXEL_1_2          0x3
-# define DP_DSC_BITS_PER_PIXEL_1            0x4
-
-#define DP_PSR_SUPPORT                      0x070   /* XXX 1.2? */
-# define DP_PSR_IS_SUPPORTED                1
-# define DP_PSR2_IS_SUPPORTED              2       /* eDP 1.4 */
-# define DP_PSR2_WITH_Y_COORD_IS_SUPPORTED  3      /* eDP 1.4a */
-
-#define DP_PSR_CAPS                         0x071   /* XXX 1.2? */
-# define DP_PSR_NO_TRAIN_ON_EXIT            1
-# define DP_PSR_SETUP_TIME_330              (0 << 1)
-# define DP_PSR_SETUP_TIME_275              (1 << 1)
-# define DP_PSR_SETUP_TIME_220              (2 << 1)
-# define DP_PSR_SETUP_TIME_165              (3 << 1)
-# define DP_PSR_SETUP_TIME_110              (4 << 1)
-# define DP_PSR_SETUP_TIME_55               (5 << 1)
-# define DP_PSR_SETUP_TIME_0                (6 << 1)
-# define DP_PSR_SETUP_TIME_MASK             (7 << 1)
-# define DP_PSR_SETUP_TIME_SHIFT            1
-# define DP_PSR2_SU_Y_COORDINATE_REQUIRED   (1 << 4)  /* eDP 1.4a */
-# define DP_PSR2_SU_GRANULARITY_REQUIRED    (1 << 5)  /* eDP 1.4b */
-
-#define DP_PSR2_SU_X_GRANULARITY           0x072 /* eDP 1.4b */
-#define DP_PSR2_SU_Y_GRANULARITY           0x074 /* eDP 1.4b */
-
-/*
- * 0x80-0x8f describe downstream port capabilities, but there are two layouts
- * based on whether DP_DETAILED_CAP_INFO_AVAILABLE was set.  If it was not,
- * each port's descriptor is one byte wide.  If it was set, each port's is
- * four bytes wide, starting with the one byte from the base info.  As of
- * DP interop v1.1a only VGA defines additional detail.
- */
-
-/* offset 0 */
-#define DP_DOWNSTREAM_PORT_0               0x80
-# define DP_DS_PORT_TYPE_MASK              (7 << 0)
-# define DP_DS_PORT_TYPE_DP                0
-# define DP_DS_PORT_TYPE_VGA               1
-# define DP_DS_PORT_TYPE_DVI               2
-# define DP_DS_PORT_TYPE_HDMI              3
-# define DP_DS_PORT_TYPE_NON_EDID          4
-# 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 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
-/* HDMI2.1 PCON FRL CONFIGURATION */
-# define DP_PCON_MAX_FRL_BW                 (7 << 2)
-# define DP_PCON_MAX_0GBPS                  (0 << 2)
-# define DP_PCON_MAX_9GBPS                  (1 << 2)
-# define DP_PCON_MAX_18GBPS                 (2 << 2)
-# define DP_PCON_MAX_24GBPS                 (3 << 2)
-# define DP_PCON_MAX_32GBPS                 (4 << 2)
-# define DP_PCON_MAX_40GBPS                 (5 << 2)
-# define DP_PCON_MAX_48GBPS                 (6 << 2)
-# define DP_PCON_SOURCE_CTL_MODE            (1 << 5)
-
-/* 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)
-
-/*
- * VESA DP-to-HDMI PCON Specification adds caps for colorspace
- * conversion in DFP cap DPCD 83h. Sec6.1 Table-3.
- * Based on the available support the source can enable
- * color conversion by writing into PROTOCOL_COVERTER_CONTROL_2
- * DPCD 3052h.
- */
-# define DP_DS_HDMI_BT601_RGB_YCBCR_CONV    (1 << 5)
-# define DP_DS_HDMI_BT709_RGB_YCBCR_CONV    (1 << 6)
-# define DP_DS_HDMI_BT2020_RGB_YCBCR_CONV   (1 << 7)
-
-#define DP_MAX_DOWNSTREAM_PORTS                    0x10
-
-/* DP Forward error Correction Registers */
-#define DP_FEC_CAPABILITY                  0x090    /* 1.4 */
-# define DP_FEC_CAPABLE                            (1 << 0)
-# define DP_FEC_UNCORR_BLK_ERROR_COUNT_CAP  (1 << 1)
-# define DP_FEC_CORR_BLK_ERROR_COUNT_CAP    (1 << 2)
-# define DP_FEC_BIT_ERROR_COUNT_CAP        (1 << 3)
-#define DP_FEC_CAPABILITY_1                    0x091   /* 2.0 */
-
-/* DP-HDMI2.1 PCON DSC ENCODER SUPPORT */
-#define DP_PCON_DSC_ENCODER_CAP_SIZE        0xC        /* 0x9E - 0x92 */
-#define DP_PCON_DSC_ENCODER                 0x092
-# define DP_PCON_DSC_ENCODER_SUPPORTED      (1 << 0)
-# define DP_PCON_DSC_PPS_ENC_OVERRIDE       (1 << 1)
-
-/* DP-HDMI2.1 PCON DSC Version */
-#define DP_PCON_DSC_VERSION                 0x093
-# define DP_PCON_DSC_MAJOR_MASK                    (0xF << 0)
-# define DP_PCON_DSC_MINOR_MASK                    (0xF << 4)
-# define DP_PCON_DSC_MAJOR_SHIFT           0
-# define DP_PCON_DSC_MINOR_SHIFT           4
-
-/* DP-HDMI2.1 PCON DSC RC Buffer block size */
-#define DP_PCON_DSC_RC_BUF_BLK_INFO        0x094
-# define DP_PCON_DSC_RC_BUF_BLK_SIZE       (0x3 << 0)
-# define DP_PCON_DSC_RC_BUF_BLK_1KB        0
-# define DP_PCON_DSC_RC_BUF_BLK_4KB        1
-# define DP_PCON_DSC_RC_BUF_BLK_16KB       2
-# define DP_PCON_DSC_RC_BUF_BLK_64KB       3
-
-/* DP-HDMI2.1 PCON DSC RC Buffer size */
-#define DP_PCON_DSC_RC_BUF_SIZE                    0x095
-
-/* DP-HDMI2.1 PCON DSC Slice capabilities-1 */
-#define DP_PCON_DSC_SLICE_CAP_1                    0x096
-# define DP_PCON_DSC_1_PER_DSC_ENC     (0x1 << 0)
-# define DP_PCON_DSC_2_PER_DSC_ENC     (0x1 << 1)
-# define DP_PCON_DSC_4_PER_DSC_ENC     (0x1 << 3)
-# define DP_PCON_DSC_6_PER_DSC_ENC     (0x1 << 4)
-# define DP_PCON_DSC_8_PER_DSC_ENC     (0x1 << 5)
-# define DP_PCON_DSC_10_PER_DSC_ENC    (0x1 << 6)
-# define DP_PCON_DSC_12_PER_DSC_ENC    (0x1 << 7)
-
-#define DP_PCON_DSC_BUF_BIT_DEPTH          0x097
-# define DP_PCON_DSC_BIT_DEPTH_MASK        (0xF << 0)
-# define DP_PCON_DSC_DEPTH_9_BITS          0
-# define DP_PCON_DSC_DEPTH_10_BITS         1
-# define DP_PCON_DSC_DEPTH_11_BITS         2
-# define DP_PCON_DSC_DEPTH_12_BITS         3
-# define DP_PCON_DSC_DEPTH_13_BITS         4
-# define DP_PCON_DSC_DEPTH_14_BITS         5
-# define DP_PCON_DSC_DEPTH_15_BITS         6
-# define DP_PCON_DSC_DEPTH_16_BITS         7
-# define DP_PCON_DSC_DEPTH_8_BITS          8
-
-#define DP_PCON_DSC_BLOCK_PREDICTION       0x098
-# define DP_PCON_DSC_BLOCK_PRED_SUPPORT            (0x1 << 0)
-
-#define DP_PCON_DSC_ENC_COLOR_FMT_CAP      0x099
-# define DP_PCON_DSC_ENC_RGB               (0x1 << 0)
-# define DP_PCON_DSC_ENC_YUV444                    (0x1 << 1)
-# define DP_PCON_DSC_ENC_YUV422_S          (0x1 << 2)
-# define DP_PCON_DSC_ENC_YUV422_N          (0x1 << 3)
-# define DP_PCON_DSC_ENC_YUV420_N          (0x1 << 4)
-
-#define DP_PCON_DSC_ENC_COLOR_DEPTH_CAP            0x09A
-# define DP_PCON_DSC_ENC_8BPC              (0x1 << 1)
-# define DP_PCON_DSC_ENC_10BPC             (0x1 << 2)
-# define DP_PCON_DSC_ENC_12BPC             (0x1 << 3)
-
-#define DP_PCON_DSC_MAX_SLICE_WIDTH        0x09B
-
-/* DP-HDMI2.1 PCON DSC Slice capabilities-2 */
-#define DP_PCON_DSC_SLICE_CAP_2             0x09C
-# define DP_PCON_DSC_16_PER_DSC_ENC        (0x1 << 0)
-# define DP_PCON_DSC_20_PER_DSC_ENC         (0x1 << 1)
-# define DP_PCON_DSC_24_PER_DSC_ENC         (0x1 << 2)
-
-/* DP-HDMI2.1 PCON HDMI TX Encoder Bits/pixel increment */
-#define DP_PCON_DSC_BPP_INCR               0x09E
-# define DP_PCON_DSC_BPP_INCR_MASK         (0x7 << 0)
-# define DP_PCON_DSC_ONE_16TH_BPP          0
-# define DP_PCON_DSC_ONE_8TH_BPP           1
-# define DP_PCON_DSC_ONE_4TH_BPP           2
-# define DP_PCON_DSC_ONE_HALF_BPP          3
-# define DP_PCON_DSC_ONE_BPP               4
-
-/* DP Extended DSC Capabilities */
-#define DP_DSC_BRANCH_OVERALL_THROUGHPUT_0  0x0a0   /* DP 1.4a SCR */
-#define DP_DSC_BRANCH_OVERALL_THROUGHPUT_1  0x0a1
-#define DP_DSC_BRANCH_MAX_LINE_WIDTH        0x0a2
-
-/* DFP Capability Extension */
-#define DP_DFP_CAPABILITY_EXTENSION_SUPPORT    0x0a3   /* 2.0 */
-
-/* Link Configuration */
-#define        DP_LINK_BW_SET                      0x100
-# define DP_LINK_RATE_TABLE                0x00    /* eDP 1.4 */
-# define DP_LINK_BW_1_62                   0x06
-# define DP_LINK_BW_2_7                            0x0a
-# define DP_LINK_BW_5_4                            0x14    /* 1.2 */
-# define DP_LINK_BW_8_1                            0x1e    /* 1.4 */
-# define DP_LINK_BW_10                      0x01    /* 2.0 128b/132b Link Layer */
-# define DP_LINK_BW_13_5                    0x04    /* 2.0 128b/132b Link Layer */
-# define DP_LINK_BW_20                      0x02    /* 2.0 128b/132b Link Layer */
-
-#define DP_LANE_COUNT_SET                  0x101
-# define DP_LANE_COUNT_MASK                0x0f
-# define DP_LANE_COUNT_ENHANCED_FRAME_EN    (1 << 7)
-
-#define DP_TRAINING_PATTERN_SET                    0x102
-# define DP_TRAINING_PATTERN_DISABLE       0
-# define DP_TRAINING_PATTERN_1             1
-# define DP_TRAINING_PATTERN_2             2
-# define DP_TRAINING_PATTERN_3             3       /* 1.2 */
-# define DP_TRAINING_PATTERN_4              7       /* 1.4 */
-# define DP_TRAINING_PATTERN_MASK          0x3
-# define DP_TRAINING_PATTERN_MASK_1_4      0xf
-
-/* DPCD 1.1 only. For DPCD >= 1.2 see per-lane DP_LINK_QUAL_LANEn_SET */
-# define DP_LINK_QUAL_PATTERN_11_DISABLE    (0 << 2)
-# define DP_LINK_QUAL_PATTERN_11_D10_2     (1 << 2)
-# define DP_LINK_QUAL_PATTERN_11_ERROR_RATE (2 << 2)
-# define DP_LINK_QUAL_PATTERN_11_PRBS7     (3 << 2)
-# define DP_LINK_QUAL_PATTERN_11_MASK      (3 << 2)
-
-# define DP_RECOVERED_CLOCK_OUT_EN         (1 << 4)
-# define DP_LINK_SCRAMBLING_DISABLE        (1 << 5)
-
-# define DP_SYMBOL_ERROR_COUNT_BOTH        (0 << 6)
-# define DP_SYMBOL_ERROR_COUNT_DISPARITY    (1 << 6)
-# define DP_SYMBOL_ERROR_COUNT_SYMBOL      (2 << 6)
-# define DP_SYMBOL_ERROR_COUNT_MASK        (3 << 6)
-
-#define DP_TRAINING_LANE0_SET              0x103
-#define DP_TRAINING_LANE1_SET              0x104
-#define DP_TRAINING_LANE2_SET              0x105
-#define DP_TRAINING_LANE3_SET              0x106
-
-# define DP_TRAIN_VOLTAGE_SWING_MASK       0x3
-# define DP_TRAIN_VOLTAGE_SWING_SHIFT      0
-# define DP_TRAIN_MAX_SWING_REACHED        (1 << 2)
-# define DP_TRAIN_VOLTAGE_SWING_LEVEL_0 (0 << 0)
-# define DP_TRAIN_VOLTAGE_SWING_LEVEL_1 (1 << 0)
-# define DP_TRAIN_VOLTAGE_SWING_LEVEL_2 (2 << 0)
-# define DP_TRAIN_VOLTAGE_SWING_LEVEL_3 (3 << 0)
-
-# define DP_TRAIN_PRE_EMPHASIS_MASK        (3 << 3)
-# define DP_TRAIN_PRE_EMPH_LEVEL_0             (0 << 3)
-# define DP_TRAIN_PRE_EMPH_LEVEL_1             (1 << 3)
-# define DP_TRAIN_PRE_EMPH_LEVEL_2             (2 << 3)
-# define DP_TRAIN_PRE_EMPH_LEVEL_3             (3 << 3)
-
-# define DP_TRAIN_PRE_EMPHASIS_SHIFT       3
-# define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED  (1 << 5)
-
-# define DP_TX_FFE_PRESET_VALUE_MASK        (0xf << 0) /* 2.0 128b/132b Link Layer */
-
-#define DP_DOWNSPREAD_CTRL                 0x107
-# define DP_SPREAD_AMP_0_5                 (1 << 4)
-# define DP_MSA_TIMING_PAR_IGNORE_EN       (1 << 7) /* eDP */
-
-#define DP_MAIN_LINK_CHANNEL_CODING_SET            0x108
-# define DP_SET_ANSI_8B10B                 (1 << 0)
-# define DP_SET_ANSI_128B132B               (1 << 1)
-
-#define DP_I2C_SPEED_CONTROL_STATUS        0x109   /* DPI */
-/* bitmask as for DP_I2C_SPEED_CAP */
-
-#define DP_EDP_CONFIGURATION_SET            0x10a   /* XXX 1.2? */
-# define DP_ALTERNATE_SCRAMBLER_RESET_ENABLE (1 << 0)
-# define DP_FRAMING_CHANGE_ENABLE          (1 << 1)
-# define DP_PANEL_SELF_TEST_ENABLE         (1 << 7)
-
-#define DP_LINK_QUAL_LANE0_SET             0x10b   /* DPCD >= 1.2 */
-#define DP_LINK_QUAL_LANE1_SET             0x10c
-#define DP_LINK_QUAL_LANE2_SET             0x10d
-#define DP_LINK_QUAL_LANE3_SET             0x10e
-# define DP_LINK_QUAL_PATTERN_DISABLE      0
-# define DP_LINK_QUAL_PATTERN_D10_2        1
-# define DP_LINK_QUAL_PATTERN_ERROR_RATE    2
-# define DP_LINK_QUAL_PATTERN_PRBS7        3
-# define DP_LINK_QUAL_PATTERN_80BIT_CUSTOM  4
-# define DP_LINK_QUAL_PATTERN_CP2520_PAT_1  5
-# define DP_LINK_QUAL_PATTERN_CP2520_PAT_2  6
-# define DP_LINK_QUAL_PATTERN_CP2520_PAT_3  7
-/* DP 2.0 UHBR10, UHBR13.5, UHBR20 */
-# define DP_LINK_QUAL_PATTERN_128B132B_TPS1 0x08
-# define DP_LINK_QUAL_PATTERN_128B132B_TPS2 0x10
-# define DP_LINK_QUAL_PATTERN_PRSBS9        0x18
-# define DP_LINK_QUAL_PATTERN_PRSBS11       0x20
-# define DP_LINK_QUAL_PATTERN_PRSBS15       0x28
-# define DP_LINK_QUAL_PATTERN_PRSBS23       0x30
-# define DP_LINK_QUAL_PATTERN_PRSBS31       0x38
-# define DP_LINK_QUAL_PATTERN_CUSTOM        0x40
-# define DP_LINK_QUAL_PATTERN_SQUARE        0x48
-
-#define DP_TRAINING_LANE0_1_SET2           0x10f
-#define DP_TRAINING_LANE2_3_SET2           0x110
-# define DP_LANE02_POST_CURSOR2_SET_MASK    (3 << 0)
-# define DP_LANE02_MAX_POST_CURSOR2_REACHED (1 << 2)
-# define DP_LANE13_POST_CURSOR2_SET_MASK    (3 << 4)
-# define DP_LANE13_MAX_POST_CURSOR2_REACHED (1 << 6)
-
-#define DP_MSTM_CTRL                       0x111   /* 1.2 */
-# define DP_MST_EN                         (1 << 0)
-# define DP_UP_REQ_EN                      (1 << 1)
-# define DP_UPSTREAM_IS_SRC                (1 << 2)
-
-#define DP_AUDIO_DELAY0                            0x112   /* 1.2 */
-#define DP_AUDIO_DELAY1                            0x113
-#define DP_AUDIO_DELAY2                            0x114
-
-#define DP_LINK_RATE_SET                   0x115   /* eDP 1.4 */
-# define DP_LINK_RATE_SET_SHIFT                    0
-# define DP_LINK_RATE_SET_MASK             (7 << 0)
-
-#define DP_RECEIVER_ALPM_CONFIG                    0x116   /* eDP 1.4 */
-# define DP_ALPM_ENABLE                            (1 << 0)
-# define DP_ALPM_LOCK_ERROR_IRQ_HPD_ENABLE  (1 << 1)
-
-#define DP_SINK_DEVICE_AUX_FRAME_SYNC_CONF  0x117   /* eDP 1.4 */
-# define DP_AUX_FRAME_SYNC_ENABLE          (1 << 0)
-# define DP_IRQ_HPD_ENABLE                 (1 << 1)
-
-#define DP_UPSTREAM_DEVICE_DP_PWR_NEED     0x118   /* 1.2 */
-# define DP_PWR_NOT_NEEDED                 (1 << 0)
-
-#define DP_FEC_CONFIGURATION               0x120    /* 1.4 */
-# define DP_FEC_READY                      (1 << 0)
-# define DP_FEC_ERR_COUNT_SEL_MASK         (7 << 1)
-# define DP_FEC_ERR_COUNT_DIS              (0 << 1)
-# define DP_FEC_UNCORR_BLK_ERROR_COUNT     (1 << 1)
-# define DP_FEC_CORR_BLK_ERROR_COUNT       (2 << 1)
-# define DP_FEC_BIT_ERROR_COUNT                    (3 << 1)
-# define DP_FEC_LANE_SELECT_MASK           (3 << 4)
-# define DP_FEC_LANE_0_SELECT              (0 << 4)
-# define DP_FEC_LANE_1_SELECT              (1 << 4)
-# define DP_FEC_LANE_2_SELECT              (2 << 4)
-# define DP_FEC_LANE_3_SELECT              (3 << 4)
-
-#define DP_AUX_FRAME_SYNC_VALUE                    0x15c   /* eDP 1.4 */
-# define DP_AUX_FRAME_SYNC_VALID           (1 << 0)
-
-#define DP_DSC_ENABLE                       0x160   /* DP 1.4 */
-# define DP_DECOMPRESSION_EN                (1 << 0)
-#define DP_DSC_CONFIGURATION                           0x161   /* DP 2.0 */
-
-#define DP_PSR_EN_CFG                          0x170   /* XXX 1.2? */
-# define DP_PSR_ENABLE                         BIT(0)
-# define DP_PSR_MAIN_LINK_ACTIVE               BIT(1)
-# define DP_PSR_CRC_VERIFICATION               BIT(2)
-# define DP_PSR_FRAME_CAPTURE                  BIT(3)
-# define DP_PSR_SU_REGION_SCANLINE_CAPTURE     BIT(4) /* eDP 1.4a */
-# define DP_PSR_IRQ_HPD_WITH_CRC_ERRORS                BIT(5) /* eDP 1.4a */
-# define DP_PSR_ENABLE_PSR2                    BIT(6) /* eDP 1.4a */
-
-#define DP_ADAPTER_CTRL                            0x1a0
-# define DP_ADAPTER_CTRL_FORCE_LOAD_SENSE   (1 << 0)
-
-#define DP_BRANCH_DEVICE_CTRL              0x1a1
-# define DP_BRANCH_DEVICE_IRQ_HPD          (1 << 0)
-
-#define DP_PAYLOAD_ALLOCATE_SET                    0x1c0
-#define DP_PAYLOAD_ALLOCATE_START_TIME_SLOT 0x1c1
-#define DP_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT 0x1c2
-
-/* Link/Sink Device Status */
-#define DP_SINK_COUNT                      0x200
-/* prior to 1.2 bit 7 was reserved mbz */
-# define DP_GET_SINK_COUNT(x)              ((((x) & 0x80) >> 1) | ((x) & 0x3f))
-# define DP_SINK_CP_READY                  (1 << 6)
-
-#define DP_DEVICE_SERVICE_IRQ_VECTOR       0x201
-# define DP_REMOTE_CONTROL_COMMAND_PENDING  (1 << 0)
-# define DP_AUTOMATED_TEST_REQUEST         (1 << 1)
-# define DP_CP_IRQ                         (1 << 2)
-# define DP_MCCS_IRQ                       (1 << 3)
-# define DP_DOWN_REP_MSG_RDY               (1 << 4) /* 1.2 MST */
-# define DP_UP_REQ_MSG_RDY                 (1 << 5) /* 1.2 MST */
-# define DP_SINK_SPECIFIC_IRQ              (1 << 6)
-
-#define DP_LANE0_1_STATUS                  0x202
-#define DP_LANE2_3_STATUS                  0x203
-# define DP_LANE_CR_DONE                   (1 << 0)
-# define DP_LANE_CHANNEL_EQ_DONE           (1 << 1)
-# define DP_LANE_SYMBOL_LOCKED             (1 << 2)
-
-#define DP_CHANNEL_EQ_BITS (DP_LANE_CR_DONE |          \
-                           DP_LANE_CHANNEL_EQ_DONE |   \
-                           DP_LANE_SYMBOL_LOCKED)
-
-#define DP_LANE_ALIGN_STATUS_UPDATED       0x204
-
-#define DP_INTERLANE_ALIGN_DONE                    (1 << 0)
-#define DP_DOWNSTREAM_PORT_STATUS_CHANGED   (1 << 6)
-#define DP_LINK_STATUS_UPDATED             (1 << 7)
-
-#define DP_SINK_STATUS                     0x205
-# define DP_RECEIVE_PORT_0_STATUS          (1 << 0)
-# define DP_RECEIVE_PORT_1_STATUS          (1 << 1)
-# define DP_STREAM_REGENERATION_STATUS      (1 << 2) /* 2.0 */
-# define DP_INTRA_HOP_AUX_REPLY_INDICATION     (1 << 3) /* 2.0 */
-
-#define DP_ADJUST_REQUEST_LANE0_1          0x206
-#define DP_ADJUST_REQUEST_LANE2_3          0x207
-# define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK  0x03
-# define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0
-# define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK   0x0c
-# define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT  2
-# define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK  0x30
-# define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4
-# define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK   0xc0
-# define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT  6
-
-/* DP 2.0 128b/132b Link Layer */
-# define DP_ADJUST_TX_FFE_PRESET_LANE0_MASK  (0xf << 0)
-# define DP_ADJUST_TX_FFE_PRESET_LANE0_SHIFT 0
-# define DP_ADJUST_TX_FFE_PRESET_LANE1_MASK  (0xf << 4)
-# define DP_ADJUST_TX_FFE_PRESET_LANE1_SHIFT 4
-
-#define DP_ADJUST_REQUEST_POST_CURSOR2      0x20c
-# define DP_ADJUST_POST_CURSOR2_LANE0_MASK  0x03
-# define DP_ADJUST_POST_CURSOR2_LANE0_SHIFT 0
-# define DP_ADJUST_POST_CURSOR2_LANE1_MASK  0x0c
-# define DP_ADJUST_POST_CURSOR2_LANE1_SHIFT 2
-# define DP_ADJUST_POST_CURSOR2_LANE2_MASK  0x30
-# define DP_ADJUST_POST_CURSOR2_LANE2_SHIFT 4
-# define DP_ADJUST_POST_CURSOR2_LANE3_MASK  0xc0
-# define DP_ADJUST_POST_CURSOR2_LANE3_SHIFT 6
-
-#define DP_TEST_REQUEST                            0x218
-# define DP_TEST_LINK_TRAINING             (1 << 0)
-# define DP_TEST_LINK_VIDEO_PATTERN        (1 << 1)
-# define DP_TEST_LINK_EDID_READ                    (1 << 2)
-# define DP_TEST_LINK_PHY_TEST_PATTERN     (1 << 3) /* DPCD >= 1.1 */
-# define DP_TEST_LINK_FAUX_PATTERN         (1 << 4) /* DPCD >= 1.2 */
-# define DP_TEST_LINK_AUDIO_PATTERN         (1 << 5) /* DPCD >= 1.2 */
-# define DP_TEST_LINK_AUDIO_DISABLED_VIDEO  (1 << 6) /* DPCD >= 1.2 */
-
-#define DP_TEST_LINK_RATE                  0x219
-# define DP_LINK_RATE_162                  (0x6)
-# define DP_LINK_RATE_27                   (0xa)
-
-#define DP_TEST_LANE_COUNT                 0x220
-
-#define DP_TEST_PATTERN                            0x221
-# define DP_NO_TEST_PATTERN                 0x0
-# define DP_COLOR_RAMP                      0x1
-# define DP_BLACK_AND_WHITE_VERTICAL_LINES  0x2
-# define DP_COLOR_SQUARE                    0x3
-
-#define DP_TEST_H_TOTAL_HI                  0x222
-#define DP_TEST_H_TOTAL_LO                  0x223
-
-#define DP_TEST_V_TOTAL_HI                  0x224
-#define DP_TEST_V_TOTAL_LO                  0x225
-
-#define DP_TEST_H_START_HI                  0x226
-#define DP_TEST_H_START_LO                  0x227
-
-#define DP_TEST_V_START_HI                  0x228
-#define DP_TEST_V_START_LO                  0x229
-
-#define DP_TEST_HSYNC_HI                    0x22A
-# define DP_TEST_HSYNC_POLARITY             (1 << 7)
-# define DP_TEST_HSYNC_WIDTH_HI_MASK        (127 << 0)
-#define DP_TEST_HSYNC_WIDTH_LO              0x22B
-
-#define DP_TEST_VSYNC_HI                    0x22C
-# define DP_TEST_VSYNC_POLARITY             (1 << 7)
-# define DP_TEST_VSYNC_WIDTH_HI_MASK        (127 << 0)
-#define DP_TEST_VSYNC_WIDTH_LO              0x22D
-
-#define DP_TEST_H_WIDTH_HI                  0x22E
-#define DP_TEST_H_WIDTH_LO                  0x22F
-
-#define DP_TEST_V_HEIGHT_HI                 0x230
-#define DP_TEST_V_HEIGHT_LO                 0x231
-
-#define DP_TEST_MISC0                       0x232
-# define DP_TEST_SYNC_CLOCK                 (1 << 0)
-# define DP_TEST_COLOR_FORMAT_MASK          (3 << 1)
-# define DP_TEST_COLOR_FORMAT_SHIFT         1
-# define DP_COLOR_FORMAT_RGB                (0 << 1)
-# define DP_COLOR_FORMAT_YCbCr422           (1 << 1)
-# define DP_COLOR_FORMAT_YCbCr444           (2 << 1)
-# define DP_TEST_DYNAMIC_RANGE_VESA         (0 << 3)
-# define DP_TEST_DYNAMIC_RANGE_CEA          (1 << 3)
-# define DP_TEST_YCBCR_COEFFICIENTS         (1 << 4)
-# define DP_YCBCR_COEFFICIENTS_ITU601       (0 << 4)
-# define DP_YCBCR_COEFFICIENTS_ITU709       (1 << 4)
-# define DP_TEST_BIT_DEPTH_MASK             (7 << 5)
-# define DP_TEST_BIT_DEPTH_SHIFT            5
-# define DP_TEST_BIT_DEPTH_6                (0 << 5)
-# define DP_TEST_BIT_DEPTH_8                (1 << 5)
-# define DP_TEST_BIT_DEPTH_10               (2 << 5)
-# define DP_TEST_BIT_DEPTH_12               (3 << 5)
-# define DP_TEST_BIT_DEPTH_16               (4 << 5)
-
-#define DP_TEST_MISC1                       0x233
-# define DP_TEST_REFRESH_DENOMINATOR        (1 << 0)
-# define DP_TEST_INTERLACED                 (1 << 1)
-
-#define DP_TEST_REFRESH_RATE_NUMERATOR      0x234
-
-#define DP_TEST_MISC0                       0x232
-
-#define DP_TEST_CRC_R_CR                   0x240
-#define DP_TEST_CRC_G_Y                            0x242
-#define DP_TEST_CRC_B_CB                   0x244
-
-#define DP_TEST_SINK_MISC                  0x246
-# define DP_TEST_CRC_SUPPORTED             (1 << 5)
-# define DP_TEST_COUNT_MASK                0xf
-
-#define DP_PHY_TEST_PATTERN                 0x248
-# define DP_PHY_TEST_PATTERN_SEL_MASK       0x7
-# define DP_PHY_TEST_PATTERN_NONE           0x0
-# define DP_PHY_TEST_PATTERN_D10_2          0x1
-# define DP_PHY_TEST_PATTERN_ERROR_COUNT    0x2
-# define DP_PHY_TEST_PATTERN_PRBS7          0x3
-# define DP_PHY_TEST_PATTERN_80BIT_CUSTOM   0x4
-# define DP_PHY_TEST_PATTERN_CP2520         0x5
-
-#define DP_PHY_SQUARE_PATTERN                          0x249
-
-#define DP_TEST_HBR2_SCRAMBLER_RESET        0x24A
-#define DP_TEST_80BIT_CUSTOM_PATTERN_7_0    0x250
-#define        DP_TEST_80BIT_CUSTOM_PATTERN_15_8   0x251
-#define        DP_TEST_80BIT_CUSTOM_PATTERN_23_16  0x252
-#define        DP_TEST_80BIT_CUSTOM_PATTERN_31_24  0x253
-#define        DP_TEST_80BIT_CUSTOM_PATTERN_39_32  0x254
-#define        DP_TEST_80BIT_CUSTOM_PATTERN_47_40  0x255
-#define        DP_TEST_80BIT_CUSTOM_PATTERN_55_48  0x256
-#define        DP_TEST_80BIT_CUSTOM_PATTERN_63_56  0x257
-#define        DP_TEST_80BIT_CUSTOM_PATTERN_71_64  0x258
-#define        DP_TEST_80BIT_CUSTOM_PATTERN_79_72  0x259
-
-#define DP_TEST_RESPONSE                   0x260
-# define DP_TEST_ACK                       (1 << 0)
-# define DP_TEST_NAK                       (1 << 1)
-# define DP_TEST_EDID_CHECKSUM_WRITE       (1 << 2)
-
-#define DP_TEST_EDID_CHECKSUM              0x261
-
-#define DP_TEST_SINK                       0x270
-# define DP_TEST_SINK_START                (1 << 0)
-#define DP_TEST_AUDIO_MODE                 0x271
-#define DP_TEST_AUDIO_PATTERN_TYPE         0x272
-#define DP_TEST_AUDIO_PERIOD_CH1           0x273
-#define DP_TEST_AUDIO_PERIOD_CH2           0x274
-#define DP_TEST_AUDIO_PERIOD_CH3           0x275
-#define DP_TEST_AUDIO_PERIOD_CH4           0x276
-#define DP_TEST_AUDIO_PERIOD_CH5           0x277
-#define DP_TEST_AUDIO_PERIOD_CH6           0x278
-#define DP_TEST_AUDIO_PERIOD_CH7           0x279
-#define DP_TEST_AUDIO_PERIOD_CH8           0x27A
-
-#define DP_FEC_STATUS                      0x280    /* 1.4 */
-# define DP_FEC_DECODE_EN_DETECTED         (1 << 0)
-# define DP_FEC_DECODE_DIS_DETECTED        (1 << 1)
-
-#define DP_FEC_ERROR_COUNT_LSB             0x0281    /* 1.4 */
-
-#define DP_FEC_ERROR_COUNT_MSB             0x0282    /* 1.4 */
-# define DP_FEC_ERROR_COUNT_MASK           0x7F
-# define DP_FEC_ERR_COUNT_VALID                    (1 << 7)
-
-#define DP_PAYLOAD_TABLE_UPDATE_STATUS      0x2c0   /* 1.2 MST */
-# define DP_PAYLOAD_TABLE_UPDATED           (1 << 0)
-# define DP_PAYLOAD_ACT_HANDLED             (1 << 1)
-
-#define DP_VC_PAYLOAD_ID_SLOT_1             0x2c1   /* 1.2 MST */
-/* up to ID_SLOT_63 at 0x2ff */
-
-/* Source Device-specific */
-#define DP_SOURCE_OUI                      0x300
-
-/* Sink Device-specific */
-#define DP_SINK_OUI                        0x400
-
-/* Branch Device-specific */
-#define DP_BRANCH_OUI                      0x500
-#define DP_BRANCH_ID                        0x503
-#define DP_BRANCH_REVISION_START            0x509
-#define DP_BRANCH_HW_REV                    0x509
-#define DP_BRANCH_SW_REV                    0x50A
-
-/* Link/Sink Device Power Control */
-#define DP_SET_POWER                        0x600
-# define DP_SET_POWER_D0                    0x1
-# define DP_SET_POWER_D3                    0x2
-# define DP_SET_POWER_MASK                  0x3
-# define DP_SET_POWER_D3_AUX_ON             0x5
-
-/* eDP-specific */
-#define DP_EDP_DPCD_REV                            0x700    /* eDP 1.2 */
-# define DP_EDP_11                         0x00
-# define DP_EDP_12                         0x01
-# define DP_EDP_13                         0x02
-# define DP_EDP_14                         0x03
-# define DP_EDP_14a                         0x04    /* eDP 1.4a */
-# define DP_EDP_14b                         0x05    /* eDP 1.4b */
-
-#define DP_EDP_GENERAL_CAP_1               0x701
-# define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP          (1 << 0)
-# define DP_EDP_BACKLIGHT_PIN_ENABLE_CAP               (1 << 1)
-# define DP_EDP_BACKLIGHT_AUX_ENABLE_CAP               (1 << 2)
-# define DP_EDP_PANEL_SELF_TEST_PIN_ENABLE_CAP         (1 << 3)
-# define DP_EDP_PANEL_SELF_TEST_AUX_ENABLE_CAP         (1 << 4)
-# define DP_EDP_FRC_ENABLE_CAP                         (1 << 5)
-# define DP_EDP_COLOR_ENGINE_CAP                       (1 << 6)
-# define DP_EDP_SET_POWER_CAP                          (1 << 7)
-
-#define DP_EDP_BACKLIGHT_ADJUSTMENT_CAP     0x702
-# define DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP       (1 << 0)
-# define DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP       (1 << 1)
-# define DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT                (1 << 2)
-# define DP_EDP_BACKLIGHT_AUX_PWM_PRODUCT_CAP          (1 << 3)
-# define DP_EDP_BACKLIGHT_FREQ_PWM_PIN_PASSTHRU_CAP    (1 << 4)
-# define DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP             (1 << 5)
-# define DP_EDP_DYNAMIC_BACKLIGHT_CAP                  (1 << 6)
-# define DP_EDP_VBLANK_BACKLIGHT_UPDATE_CAP            (1 << 7)
-
-#define DP_EDP_GENERAL_CAP_2               0x703
-# define DP_EDP_OVERDRIVE_ENGINE_ENABLED               (1 << 0)
-
-#define DP_EDP_GENERAL_CAP_3               0x704    /* eDP 1.4 */
-# define DP_EDP_X_REGION_CAP_MASK                      (0xf << 0)
-# define DP_EDP_X_REGION_CAP_SHIFT                     0
-# define DP_EDP_Y_REGION_CAP_MASK                      (0xf << 4)
-# define DP_EDP_Y_REGION_CAP_SHIFT                     4
-
-#define DP_EDP_DISPLAY_CONTROL_REGISTER     0x720
-# define DP_EDP_BACKLIGHT_ENABLE                       (1 << 0)
-# define DP_EDP_BLACK_VIDEO_ENABLE                     (1 << 1)
-# define DP_EDP_FRC_ENABLE                             (1 << 2)
-# define DP_EDP_COLOR_ENGINE_ENABLE                    (1 << 3)
-# define DP_EDP_VBLANK_BACKLIGHT_UPDATE_ENABLE         (1 << 7)
-
-#define DP_EDP_BACKLIGHT_MODE_SET_REGISTER  0x721
-# define DP_EDP_BACKLIGHT_CONTROL_MODE_MASK            (3 << 0)
-# define DP_EDP_BACKLIGHT_CONTROL_MODE_PWM             (0 << 0)
-# define DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET          (1 << 0)
-# define DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD            (2 << 0)
-# define DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT         (3 << 0)
-# define DP_EDP_BACKLIGHT_FREQ_PWM_PIN_PASSTHRU_ENABLE (1 << 2)
-# define DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE          (1 << 3)
-# define DP_EDP_DYNAMIC_BACKLIGHT_ENABLE               (1 << 4)
-# define DP_EDP_REGIONAL_BACKLIGHT_ENABLE              (1 << 5)
-# define DP_EDP_UPDATE_REGION_BRIGHTNESS               (1 << 6) /* eDP 1.4 */
-
-#define DP_EDP_BACKLIGHT_BRIGHTNESS_MSB     0x722
-#define DP_EDP_BACKLIGHT_BRIGHTNESS_LSB     0x723
-
-#define DP_EDP_PWMGEN_BIT_COUNT             0x724
-#define DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN     0x725
-#define DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX     0x726
-# define DP_EDP_PWMGEN_BIT_COUNT_MASK       (0x1f << 0)
-
-#define DP_EDP_BACKLIGHT_CONTROL_STATUS     0x727
-
-#define DP_EDP_BACKLIGHT_FREQ_SET           0x728
-# define DP_EDP_BACKLIGHT_FREQ_BASE_KHZ     27000
-
-#define DP_EDP_BACKLIGHT_FREQ_CAP_MIN_MSB   0x72a
-#define DP_EDP_BACKLIGHT_FREQ_CAP_MIN_MID   0x72b
-#define DP_EDP_BACKLIGHT_FREQ_CAP_MIN_LSB   0x72c
-
-#define DP_EDP_BACKLIGHT_FREQ_CAP_MAX_MSB   0x72d
-#define DP_EDP_BACKLIGHT_FREQ_CAP_MAX_MID   0x72e
-#define DP_EDP_BACKLIGHT_FREQ_CAP_MAX_LSB   0x72f
-
-#define DP_EDP_DBC_MINIMUM_BRIGHTNESS_SET   0x732
-#define DP_EDP_DBC_MAXIMUM_BRIGHTNESS_SET   0x733
-
-#define DP_EDP_REGIONAL_BACKLIGHT_BASE      0x740    /* eDP 1.4 */
-#define DP_EDP_REGIONAL_BACKLIGHT_0        0x741    /* eDP 1.4 */
-
-#define DP_EDP_MSO_LINK_CAPABILITIES        0x7a4    /* eDP 1.4 */
-# define DP_EDP_MSO_NUMBER_OF_LINKS_MASK    (7 << 0)
-# define DP_EDP_MSO_NUMBER_OF_LINKS_SHIFT   0
-# define DP_EDP_MSO_INDEPENDENT_LINK_BIT    (1 << 3)
-
-/* Sideband MSG Buffers */
-#define DP_SIDEBAND_MSG_DOWN_REQ_BASE      0x1000   /* 1.2 MST */
-#define DP_SIDEBAND_MSG_UP_REP_BASE        0x1200   /* 1.2 MST */
-#define DP_SIDEBAND_MSG_DOWN_REP_BASE      0x1400   /* 1.2 MST */
-#define DP_SIDEBAND_MSG_UP_REQ_BASE        0x1600   /* 1.2 MST */
-
-/* DPRX Event Status Indicator */
-#define DP_SINK_COUNT_ESI                  0x2002   /* 1.2 */
-/* 0-5 sink count */
-# define DP_SINK_COUNT_CP_READY             (1 << 6)
-
-#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0   0x2003   /* 1.2 */
-
-#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1   0x2004   /* 1.2 */
-# define DP_RX_GTC_MSTR_REQ_STATUS_CHANGE    (1 << 0)
-# define DP_LOCK_ACQUISITION_REQUEST         (1 << 1)
-# define DP_CEC_IRQ                          (1 << 2)
-
-#define DP_LINK_SERVICE_IRQ_VECTOR_ESI0     0x2005   /* 1.2 */
-# define RX_CAP_CHANGED                      (1 << 0)
-# define LINK_STATUS_CHANGED                 (1 << 1)
-# define STREAM_STATUS_CHANGED               (1 << 2)
-# define HDMI_LINK_STATUS_CHANGED            (1 << 3)
-# define CONNECTED_OFF_ENTRY_REQUESTED       (1 << 4)
-
-#define DP_PSR_ERROR_STATUS                 0x2006  /* XXX 1.2? */
-# define DP_PSR_LINK_CRC_ERROR              (1 << 0)
-# define DP_PSR_RFB_STORAGE_ERROR           (1 << 1)
-# define DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR (1 << 2) /* eDP 1.4 */
-
-#define DP_PSR_ESI                          0x2007  /* XXX 1.2? */
-# define DP_PSR_CAPS_CHANGE                 (1 << 0)
-
-#define DP_PSR_STATUS                       0x2008  /* XXX 1.2? */
-# define DP_PSR_SINK_INACTIVE               0
-# define DP_PSR_SINK_ACTIVE_SRC_SYNCED      1
-# define DP_PSR_SINK_ACTIVE_RFB             2
-# define DP_PSR_SINK_ACTIVE_SINK_SYNCED     3
-# define DP_PSR_SINK_ACTIVE_RESYNC          4
-# define DP_PSR_SINK_INTERNAL_ERROR         7
-# define DP_PSR_SINK_STATE_MASK             0x07
-
-#define DP_SYNCHRONIZATION_LATENCY_IN_SINK             0x2009 /* edp 1.4 */
-# define DP_MAX_RESYNC_FRAME_COUNT_MASK                        (0xf << 0)
-# define DP_MAX_RESYNC_FRAME_COUNT_SHIFT               0
-# define DP_LAST_ACTUAL_SYNCHRONIZATION_LATENCY_MASK   (0xf << 4)
-# define DP_LAST_ACTUAL_SYNCHRONIZATION_LATENCY_SHIFT  4
-
-#define DP_LAST_RECEIVED_PSR_SDP           0x200a /* eDP 1.2 */
-# define DP_PSR_STATE_BIT                  (1 << 0) /* eDP 1.2 */
-# define DP_UPDATE_RFB_BIT                 (1 << 1) /* eDP 1.2 */
-# define DP_CRC_VALID_BIT                  (1 << 2) /* eDP 1.2 */
-# define DP_SU_VALID                       (1 << 3) /* eDP 1.4 */
-# define DP_FIRST_SCAN_LINE_SU_REGION      (1 << 4) /* eDP 1.4 */
-# define DP_LAST_SCAN_LINE_SU_REGION       (1 << 5) /* eDP 1.4 */
-# define DP_Y_COORDINATE_VALID             (1 << 6) /* eDP 1.4a */
-
-#define DP_RECEIVER_ALPM_STATUS                    0x200b  /* eDP 1.4 */
-# define DP_ALPM_LOCK_TIMEOUT_ERROR        (1 << 0)
-
-#define DP_LANE0_1_STATUS_ESI                  0x200c /* status same as 0x202 */
-#define DP_LANE2_3_STATUS_ESI                  0x200d /* status same as 0x203 */
-#define DP_LANE_ALIGN_STATUS_UPDATED_ESI       0x200e /* status same as 0x204 */
-#define DP_SINK_STATUS_ESI                     0x200f /* status same as 0x205 */
-
-/* Extended Receiver Capability: See DP_DPCD_REV for definitions */
-#define DP_DP13_DPCD_REV                    0x2200
-
-#define DP_DPRX_FEATURE_ENUMERATION_LIST    0x2210  /* DP 1.3 */
-# define DP_GTC_CAP                                    (1 << 0)  /* DP 1.3 */
-# define DP_SST_SPLIT_SDP_CAP                          (1 << 1)  /* DP 1.4 */
-# define DP_AV_SYNC_CAP                                        (1 << 2)  /* DP 1.3 */
-# define DP_VSC_SDP_EXT_FOR_COLORIMETRY_SUPPORTED      (1 << 3)  /* DP 1.3 */
-# define DP_VSC_EXT_VESA_SDP_SUPPORTED                 (1 << 4)  /* DP 1.4 */
-# define DP_VSC_EXT_VESA_SDP_CHAINING_SUPPORTED                (1 << 5)  /* DP 1.4 */
-# define DP_VSC_EXT_CEA_SDP_SUPPORTED                  (1 << 6)  /* DP 1.4 */
-# define DP_VSC_EXT_CEA_SDP_CHAINING_SUPPORTED         (1 << 7)  /* DP 1.4 */
-
-#define DP_128B132B_SUPPORTED_LINK_RATES       0x2215 /* 2.0 */
-# define DP_UHBR10                             (1 << 0)
-# define DP_UHBR20                             (1 << 1)
-# define DP_UHBR13_5                           (1 << 2)
-
-#define DP_128B132B_TRAINING_AUX_RD_INTERVAL                    0x2216 /* 2.0 */
-# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK              0x7f
-# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US            0x00
-# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS              0x01
-# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS              0x02
-# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS             0x03
-# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS             0x04
-# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS             0x05
-# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS             0x06
-
-#define DP_TEST_264BIT_CUSTOM_PATTERN_7_0              0x2230
-#define DP_TEST_264BIT_CUSTOM_PATTERN_263_256  0x2250
-
-/* DSC Extended Capability Branch Total DSC Resources */
-#define DP_DSC_SUPPORT_AND_DSC_DECODER_COUNT           0x2260  /* 2.0 */
-# define DP_DSC_DECODER_COUNT_MASK                     (0b111 << 5)
-# define DP_DSC_DECODER_COUNT_SHIFT                    5
-#define DP_DSC_MAX_SLICE_COUNT_AND_AGGREGATION_0       0x2270  /* 2.0 */
-# define DP_DSC_DECODER_0_MAXIMUM_SLICE_COUNT_MASK     (1 << 0)
-# define DP_DSC_DECODER_0_AGGREGATION_SUPPORT_MASK     (0b111 << 1)
-# define DP_DSC_DECODER_0_AGGREGATION_SUPPORT_SHIFT    1
-
-/* Protocol Converter Extension */
-/* HDMI CEC tunneling over AUX DP 1.3 section 5.3.3.3.1 DPCD 1.4+ */
-#define DP_CEC_TUNNELING_CAPABILITY            0x3000
-# define DP_CEC_TUNNELING_CAPABLE               (1 << 0)
-# define DP_CEC_SNOOPING_CAPABLE                (1 << 1)
-# define DP_CEC_MULTIPLE_LA_CAPABLE             (1 << 2)
-
-#define DP_CEC_TUNNELING_CONTROL               0x3001
-# define DP_CEC_TUNNELING_ENABLE                (1 << 0)
-# define DP_CEC_SNOOPING_ENABLE                 (1 << 1)
-
-#define DP_CEC_RX_MESSAGE_INFO                 0x3002
-# define DP_CEC_RX_MESSAGE_LEN_MASK             (0xf << 0)
-# define DP_CEC_RX_MESSAGE_LEN_SHIFT            0
-# define DP_CEC_RX_MESSAGE_HPD_STATE            (1 << 4)
-# define DP_CEC_RX_MESSAGE_HPD_LOST             (1 << 5)
-# define DP_CEC_RX_MESSAGE_ACKED                (1 << 6)
-# define DP_CEC_RX_MESSAGE_ENDED                (1 << 7)
-
-#define DP_CEC_TX_MESSAGE_INFO                 0x3003
-# define DP_CEC_TX_MESSAGE_LEN_MASK             (0xf << 0)
-# define DP_CEC_TX_MESSAGE_LEN_SHIFT            0
-# define DP_CEC_TX_RETRY_COUNT_MASK             (0x7 << 4)
-# define DP_CEC_TX_RETRY_COUNT_SHIFT            4
-# define DP_CEC_TX_MESSAGE_SEND                 (1 << 7)
-
-#define DP_CEC_TUNNELING_IRQ_FLAGS             0x3004
-# define DP_CEC_RX_MESSAGE_INFO_VALID           (1 << 0)
-# define DP_CEC_RX_MESSAGE_OVERFLOW             (1 << 1)
-# define DP_CEC_TX_MESSAGE_SENT                 (1 << 4)
-# define DP_CEC_TX_LINE_ERROR                   (1 << 5)
-# define DP_CEC_TX_ADDRESS_NACK_ERROR           (1 << 6)
-# define DP_CEC_TX_DATA_NACK_ERROR              (1 << 7)
-
-#define DP_CEC_LOGICAL_ADDRESS_MASK            0x300E /* 0x300F word */
-# define DP_CEC_LOGICAL_ADDRESS_0               (1 << 0)
-# define DP_CEC_LOGICAL_ADDRESS_1               (1 << 1)
-# define DP_CEC_LOGICAL_ADDRESS_2               (1 << 2)
-# define DP_CEC_LOGICAL_ADDRESS_3               (1 << 3)
-# define DP_CEC_LOGICAL_ADDRESS_4               (1 << 4)
-# define DP_CEC_LOGICAL_ADDRESS_5               (1 << 5)
-# define DP_CEC_LOGICAL_ADDRESS_6               (1 << 6)
-# define DP_CEC_LOGICAL_ADDRESS_7               (1 << 7)
-#define DP_CEC_LOGICAL_ADDRESS_MASK_2          0x300F /* 0x300E word */
-# define DP_CEC_LOGICAL_ADDRESS_8               (1 << 0)
-# define DP_CEC_LOGICAL_ADDRESS_9               (1 << 1)
-# define DP_CEC_LOGICAL_ADDRESS_10              (1 << 2)
-# define DP_CEC_LOGICAL_ADDRESS_11              (1 << 3)
-# define DP_CEC_LOGICAL_ADDRESS_12              (1 << 4)
-# define DP_CEC_LOGICAL_ADDRESS_13              (1 << 5)
-# define DP_CEC_LOGICAL_ADDRESS_14              (1 << 6)
-# define DP_CEC_LOGICAL_ADDRESS_15              (1 << 7)
-
-#define DP_CEC_RX_MESSAGE_BUFFER               0x3010
-#define DP_CEC_TX_MESSAGE_BUFFER               0x3020
-#define DP_CEC_MESSAGE_BUFFER_LENGTH             0x10
-
-/* PCON CONFIGURE-1 FRL FOR HDMI SINK */
-#define DP_PCON_HDMI_LINK_CONFIG_1             0x305A
-# define DP_PCON_ENABLE_MAX_FRL_BW             (7 << 0)
-# define DP_PCON_ENABLE_MAX_BW_0GBPS          0
-# define DP_PCON_ENABLE_MAX_BW_9GBPS          1
-# define DP_PCON_ENABLE_MAX_BW_18GBPS         2
-# define DP_PCON_ENABLE_MAX_BW_24GBPS         3
-# define DP_PCON_ENABLE_MAX_BW_32GBPS         4
-# define DP_PCON_ENABLE_MAX_BW_40GBPS         5
-# define DP_PCON_ENABLE_MAX_BW_48GBPS         6
-# define DP_PCON_ENABLE_SOURCE_CTL_MODE       (1 << 3)
-# define DP_PCON_ENABLE_CONCURRENT_LINK       (1 << 4)
-# define DP_PCON_ENABLE_SEQUENTIAL_LINK       (0 << 4)
-# define DP_PCON_ENABLE_LINK_FRL_MODE         (1 << 5)
-# define DP_PCON_ENABLE_HPD_READY            (1 << 6)
-# define DP_PCON_ENABLE_HDMI_LINK             (1 << 7)
-
-/* PCON CONFIGURE-2 FRL FOR HDMI SINK */
-#define DP_PCON_HDMI_LINK_CONFIG_2            0x305B
-# define DP_PCON_MAX_LINK_BW_MASK             (0x3F << 0)
-# define DP_PCON_FRL_BW_MASK_9GBPS            (1 << 0)
-# define DP_PCON_FRL_BW_MASK_18GBPS           (1 << 1)
-# define DP_PCON_FRL_BW_MASK_24GBPS           (1 << 2)
-# define DP_PCON_FRL_BW_MASK_32GBPS           (1 << 3)
-# define DP_PCON_FRL_BW_MASK_40GBPS           (1 << 4)
-# define DP_PCON_FRL_BW_MASK_48GBPS           (1 << 5)
-# define DP_PCON_FRL_LINK_TRAIN_EXTENDED      (1 << 6)
-# define DP_PCON_FRL_LINK_TRAIN_NORMAL        (0 << 6)
-
-/* PCON HDMI LINK STATUS */
-#define DP_PCON_HDMI_TX_LINK_STATUS           0x303B
-# define DP_PCON_HDMI_TX_LINK_ACTIVE          (1 << 0)
-# define DP_PCON_FRL_READY                   (1 << 1)
-
-/* PCON HDMI POST FRL STATUS */
-#define DP_PCON_HDMI_POST_FRL_STATUS          0x3036
-# define DP_PCON_HDMI_LINK_MODE               (1 << 0)
-# define DP_PCON_HDMI_MODE_TMDS               0
-# define DP_PCON_HDMI_MODE_FRL                1
-# define DP_PCON_HDMI_FRL_TRAINED_BW          (0x3F << 1)
-# define DP_PCON_FRL_TRAINED_BW_9GBPS        (1 << 1)
-# define DP_PCON_FRL_TRAINED_BW_18GBPS       (1 << 2)
-# define DP_PCON_FRL_TRAINED_BW_24GBPS       (1 << 3)
-# define DP_PCON_FRL_TRAINED_BW_32GBPS       (1 << 4)
-# define DP_PCON_FRL_TRAINED_BW_40GBPS       (1 << 5)
-# define DP_PCON_FRL_TRAINED_BW_48GBPS       (1 << 6)
-
-#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_PCON_ENABLE_DSC_ENCODER            (1 << 1)
-# define DP_PCON_ENCODER_PPS_OVERRIDE_MASK     (0x3 << 2)
-# define DP_PCON_ENC_PPS_OVERRIDE_DISABLED      0
-# define DP_PCON_ENC_PPS_OVERRIDE_EN_PARAMS     1
-# define DP_PCON_ENC_PPS_OVERRIDE_EN_BUFFER     2
-# define DP_CONVERSION_RGB_YCBCR_MASK         (7 << 4)
-# define DP_CONVERSION_BT601_RGB_YCBCR_ENABLE  (1 << 4)
-# define DP_CONVERSION_BT709_RGB_YCBCR_ENABLE  (1 << 5)
-# define DP_CONVERSION_BT2020_RGB_YCBCR_ENABLE (1 << 6)
-
-/* PCON Downstream HDMI ERROR Status per Lane */
-#define DP_PCON_HDMI_ERROR_STATUS_LN0          0x3037
-#define DP_PCON_HDMI_ERROR_STATUS_LN1          0x3038
-#define DP_PCON_HDMI_ERROR_STATUS_LN2          0x3039
-#define DP_PCON_HDMI_ERROR_STATUS_LN3          0x303A
-# define DP_PCON_HDMI_ERROR_COUNT_MASK         (0x7 << 0)
-# define DP_PCON_HDMI_ERROR_COUNT_THREE_PLUS   (1 << 0)
-# define DP_PCON_HDMI_ERROR_COUNT_TEN_PLUS     (1 << 1)
-# define DP_PCON_HDMI_ERROR_COUNT_HUNDRED_PLUS (1 << 2)
-
-/* PCON HDMI CONFIG PPS Override Buffer
- * Valid Offsets to be added to Base : 0-127
- */
-#define DP_PCON_HDMI_PPS_OVERRIDE_BASE        0x3100
-
-/* PCON HDMI CONFIG PPS Override Parameter: Slice height
- * Offset-0 8LSBs of the Slice height.
- * Offset-1 8MSBs of the Slice height.
- */
-#define DP_PCON_HDMI_PPS_OVRD_SLICE_HEIGHT    0x3180
-
-/* PCON HDMI CONFIG PPS Override Parameter: Slice width
- * Offset-0 8LSBs of the Slice width.
- * Offset-1 8MSBs of the Slice width.
- */
-#define DP_PCON_HDMI_PPS_OVRD_SLICE_WIDTH    0x3182
-
-/* PCON HDMI CONFIG PPS Override Parameter: bits_per_pixel
- * Offset-0 8LSBs of the bits_per_pixel.
- * Offset-1 2MSBs of the bits_per_pixel.
- */
-#define DP_PCON_HDMI_PPS_OVRD_BPP           0x3184
-
-/* HDCP 1.3 and HDCP 2.2 */
-#define DP_AUX_HDCP_BKSV               0x68000
-#define DP_AUX_HDCP_RI_PRIME           0x68005
-#define DP_AUX_HDCP_AKSV               0x68007
-#define DP_AUX_HDCP_AN                 0x6800C
-#define DP_AUX_HDCP_V_PRIME(h)         (0x68014 + h * 4)
-#define DP_AUX_HDCP_BCAPS              0x68028
-# define DP_BCAPS_REPEATER_PRESENT     BIT(1)
-# define DP_BCAPS_HDCP_CAPABLE         BIT(0)
-#define DP_AUX_HDCP_BSTATUS            0x68029
-# define DP_BSTATUS_REAUTH_REQ         BIT(3)
-# define DP_BSTATUS_LINK_FAILURE       BIT(2)
-# define DP_BSTATUS_R0_PRIME_READY     BIT(1)
-# define DP_BSTATUS_READY              BIT(0)
-#define DP_AUX_HDCP_BINFO              0x6802A
-#define DP_AUX_HDCP_KSV_FIFO           0x6802C
-#define DP_AUX_HDCP_AINFO              0x6803B
-
-/* DP HDCP2.2 parameter offsets in DPCD address space */
-#define DP_HDCP_2_2_REG_RTX_OFFSET             0x69000
-#define DP_HDCP_2_2_REG_TXCAPS_OFFSET          0x69008
-#define DP_HDCP_2_2_REG_CERT_RX_OFFSET         0x6900B
-#define DP_HDCP_2_2_REG_RRX_OFFSET             0x69215
-#define DP_HDCP_2_2_REG_RX_CAPS_OFFSET         0x6921D
-#define DP_HDCP_2_2_REG_EKPUB_KM_OFFSET                0x69220
-#define DP_HDCP_2_2_REG_EKH_KM_WR_OFFSET       0x692A0
-#define DP_HDCP_2_2_REG_M_OFFSET               0x692B0
-#define DP_HDCP_2_2_REG_HPRIME_OFFSET          0x692C0
-#define DP_HDCP_2_2_REG_EKH_KM_RD_OFFSET       0x692E0
-#define DP_HDCP_2_2_REG_RN_OFFSET              0x692F0
-#define DP_HDCP_2_2_REG_LPRIME_OFFSET          0x692F8
-#define DP_HDCP_2_2_REG_EDKEY_KS_OFFSET                0x69318
-#define        DP_HDCP_2_2_REG_RIV_OFFSET              0x69328
-#define DP_HDCP_2_2_REG_RXINFO_OFFSET          0x69330
-#define DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET       0x69332
-#define DP_HDCP_2_2_REG_VPRIME_OFFSET          0x69335
-#define DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET    0x69345
-#define DP_HDCP_2_2_REG_V_OFFSET               0x693E0
-#define DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET       0x693F0
-#define DP_HDCP_2_2_REG_K_OFFSET               0x693F3
-#define DP_HDCP_2_2_REG_STREAM_ID_TYPE_OFFSET  0x693F5
-#define DP_HDCP_2_2_REG_MPRIME_OFFSET          0x69473
-#define DP_HDCP_2_2_REG_RXSTATUS_OFFSET                0x69493
-#define DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET     0x69494
-#define DP_HDCP_2_2_REG_DBG_OFFSET             0x69518
-
-/* LTTPR: Link Training (LT)-tunable PHY Repeaters */
-#define DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV 0xf0000 /* 1.3 */
-#define DP_MAX_LINK_RATE_PHY_REPEATER                      0xf0001 /* 1.4a */
-#define DP_PHY_REPEATER_CNT                                0xf0002 /* 1.3 */
-#define DP_PHY_REPEATER_MODE                               0xf0003 /* 1.3 */
-#define DP_MAX_LANE_COUNT_PHY_REPEATER                     0xf0004 /* 1.4a */
-#define DP_Repeater_FEC_CAPABILITY                         0xf0004 /* 1.4 */
-#define DP_PHY_REPEATER_EXTENDED_WAIT_TIMEOUT              0xf0005 /* 1.4a */
-#define DP_MAIN_LINK_CHANNEL_CODING_PHY_REPEATER           0xf0006 /* 2.0 */
-# define DP_PHY_REPEATER_128B132B_SUPPORTED                (1 << 0)
-/* See DP_128B132B_SUPPORTED_LINK_RATES for values */
-#define DP_PHY_REPEATER_128B132B_RATES                     0xf0007 /* 2.0 */
-
-enum drm_dp_phy {
-       DP_PHY_DPRX,
-
-       DP_PHY_LTTPR1,
-       DP_PHY_LTTPR2,
-       DP_PHY_LTTPR3,
-       DP_PHY_LTTPR4,
-       DP_PHY_LTTPR5,
-       DP_PHY_LTTPR6,
-       DP_PHY_LTTPR7,
-       DP_PHY_LTTPR8,
-
-       DP_MAX_LTTPR_COUNT = DP_PHY_LTTPR8,
-};
-
-#define DP_PHY_LTTPR(i)                                            (DP_PHY_LTTPR1 + (i))
-
-#define __DP_LTTPR1_BASE                                   0xf0010 /* 1.3 */
-#define __DP_LTTPR2_BASE                                   0xf0060 /* 1.3 */
-#define DP_LTTPR_BASE(dp_phy) \
-       (__DP_LTTPR1_BASE + (__DP_LTTPR2_BASE - __DP_LTTPR1_BASE) * \
-               ((dp_phy) - DP_PHY_LTTPR1))
-
-#define DP_LTTPR_REG(dp_phy, lttpr1_reg) \
-       (DP_LTTPR_BASE(dp_phy) - DP_LTTPR_BASE(DP_PHY_LTTPR1) + (lttpr1_reg))
-
-#define DP_TRAINING_PATTERN_SET_PHY_REPEATER1              0xf0010 /* 1.3 */
-#define DP_TRAINING_PATTERN_SET_PHY_REPEATER(dp_phy) \
-       DP_LTTPR_REG(dp_phy, DP_TRAINING_PATTERN_SET_PHY_REPEATER1)
-
-#define DP_TRAINING_LANE0_SET_PHY_REPEATER1                0xf0011 /* 1.3 */
-#define DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy) \
-       DP_LTTPR_REG(dp_phy, DP_TRAINING_LANE0_SET_PHY_REPEATER1)
-
-#define DP_TRAINING_LANE1_SET_PHY_REPEATER1                0xf0012 /* 1.3 */
-#define DP_TRAINING_LANE2_SET_PHY_REPEATER1                0xf0013 /* 1.3 */
-#define DP_TRAINING_LANE3_SET_PHY_REPEATER1                0xf0014 /* 1.3 */
-#define DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1          0xf0020 /* 1.4a */
-#define DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy)       \
-       DP_LTTPR_REG(dp_phy, DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1)
-
-#define DP_TRANSMITTER_CAPABILITY_PHY_REPEATER1                    0xf0021 /* 1.4a */
-# define DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED                BIT(0)
-# define DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED                 BIT(1)
-
-#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1  0xf0022 /* 2.0 */
-#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy)      \
-       DP_LTTPR_REG(dp_phy, DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1)
-/* see DP_128B132B_TRAINING_AUX_RD_INTERVAL for values */
-
-#define DP_LANE0_1_STATUS_PHY_REPEATER1                            0xf0030 /* 1.3 */
-#define DP_LANE0_1_STATUS_PHY_REPEATER(dp_phy) \
-       DP_LTTPR_REG(dp_phy, DP_LANE0_1_STATUS_PHY_REPEATER1)
-
-#define DP_LANE2_3_STATUS_PHY_REPEATER1                            0xf0031 /* 1.3 */
-
-#define DP_LANE_ALIGN_STATUS_UPDATED_PHY_REPEATER1         0xf0032 /* 1.3 */
-#define DP_ADJUST_REQUEST_LANE0_1_PHY_REPEATER1                    0xf0033 /* 1.3 */
-#define DP_ADJUST_REQUEST_LANE2_3_PHY_REPEATER1                    0xf0034 /* 1.3 */
-#define DP_SYMBOL_ERROR_COUNT_LANE0_PHY_REPEATER1          0xf0035 /* 1.3 */
-#define DP_SYMBOL_ERROR_COUNT_LANE1_PHY_REPEATER1          0xf0037 /* 1.3 */
-#define DP_SYMBOL_ERROR_COUNT_LANE2_PHY_REPEATER1          0xf0039 /* 1.3 */
-#define DP_SYMBOL_ERROR_COUNT_LANE3_PHY_REPEATER1          0xf003b /* 1.3 */
-
-#define __DP_FEC1_BASE                                     0xf0290 /* 1.4 */
-#define __DP_FEC2_BASE                                     0xf0298 /* 1.4 */
-#define DP_FEC_BASE(dp_phy) \
-       (__DP_FEC1_BASE + ((__DP_FEC2_BASE - __DP_FEC1_BASE) * \
-                          ((dp_phy) - DP_PHY_LTTPR1)))
-
-#define DP_FEC_REG(dp_phy, fec1_reg) \
-       (DP_FEC_BASE(dp_phy) - DP_FEC_BASE(DP_PHY_LTTPR1) + fec1_reg)
-
-#define DP_FEC_STATUS_PHY_REPEATER1                        0xf0290 /* 1.4 */
-#define DP_FEC_STATUS_PHY_REPEATER(dp_phy) \
-       DP_FEC_REG(dp_phy, DP_FEC_STATUS_PHY_REPEATER1)
-
-#define DP_FEC_ERROR_COUNT_PHY_REPEATER1                    0xf0291 /* 1.4 */
-#define DP_FEC_CAPABILITY_PHY_REPEATER1                     0xf0294 /* 1.4a */
-
-#define DP_LTTPR_MAX_ADD                                   0xf02ff /* 1.4 */
-
-#define DP_DPCD_MAX_ADD                                            0xfffff /* 1.4 */
-
-/* Repeater modes */
-#define DP_PHY_REPEATER_MODE_TRANSPARENT                   0x55    /* 1.3 */
-#define DP_PHY_REPEATER_MODE_NON_TRANSPARENT               0xaa    /* 1.3 */
-
-/* DP HDCP message start offsets in DPCD address space */
-#define DP_HDCP_2_2_AKE_INIT_OFFSET            DP_HDCP_2_2_REG_RTX_OFFSET
-#define DP_HDCP_2_2_AKE_SEND_CERT_OFFSET       DP_HDCP_2_2_REG_CERT_RX_OFFSET
-#define DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET    DP_HDCP_2_2_REG_EKPUB_KM_OFFSET
-#define DP_HDCP_2_2_AKE_STORED_KM_OFFSET       DP_HDCP_2_2_REG_EKH_KM_WR_OFFSET
-#define DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET     DP_HDCP_2_2_REG_HPRIME_OFFSET
-#define DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET \
-                                               DP_HDCP_2_2_REG_EKH_KM_RD_OFFSET
-#define DP_HDCP_2_2_LC_INIT_OFFSET             DP_HDCP_2_2_REG_RN_OFFSET
-#define DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET      DP_HDCP_2_2_REG_LPRIME_OFFSET
-#define DP_HDCP_2_2_SKE_SEND_EKS_OFFSET                DP_HDCP_2_2_REG_EDKEY_KS_OFFSET
-#define DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET        DP_HDCP_2_2_REG_RXINFO_OFFSET
-#define DP_HDCP_2_2_REP_SEND_ACK_OFFSET                DP_HDCP_2_2_REG_V_OFFSET
-#define DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET   DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET
-#define DP_HDCP_2_2_REP_STREAM_READY_OFFSET    DP_HDCP_2_2_REG_MPRIME_OFFSET
-
-#define HDCP_2_2_DP_RXSTATUS_LEN               1
-#define HDCP_2_2_DP_RXSTATUS_READY(x)          ((x) & BIT(0))
-#define HDCP_2_2_DP_RXSTATUS_H_PRIME(x)                ((x) & BIT(1))
-#define HDCP_2_2_DP_RXSTATUS_PAIRING(x)                ((x) & BIT(2))
-#define HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(x)     ((x) & BIT(3))
-#define HDCP_2_2_DP_RXSTATUS_LINK_FAILED(x)    ((x) & BIT(4))
-
-/* DP 1.2 Sideband message defines */
-/* peer device type - DP 1.2a Table 2-92 */
-#define DP_PEER_DEVICE_NONE            0x0
-#define DP_PEER_DEVICE_SOURCE_OR_SST   0x1
-#define DP_PEER_DEVICE_MST_BRANCHING   0x2
-#define DP_PEER_DEVICE_SST_SINK                0x3
-#define DP_PEER_DEVICE_DP_LEGACY_CONV  0x4
-
-/* DP 1.2 MST sideband request names DP 1.2a Table 2-80 */
-#define DP_GET_MSG_TRANSACTION_VERSION 0x00 /* DP 1.3 */
-#define DP_LINK_ADDRESS                        0x01
-#define DP_CONNECTION_STATUS_NOTIFY    0x02
-#define DP_ENUM_PATH_RESOURCES         0x10
-#define DP_ALLOCATE_PAYLOAD            0x11
-#define DP_QUERY_PAYLOAD               0x12
-#define DP_RESOURCE_STATUS_NOTIFY      0x13
-#define DP_CLEAR_PAYLOAD_ID_TABLE      0x14
-#define DP_REMOTE_DPCD_READ            0x20
-#define DP_REMOTE_DPCD_WRITE           0x21
-#define DP_REMOTE_I2C_READ             0x22
-#define DP_REMOTE_I2C_WRITE            0x23
-#define DP_POWER_UP_PHY                        0x24
-#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
-#define DP_SIDEBAND_REPLY_NAK          0x01
-
-/* DP 1.2 MST sideband nak reasons - table 2.84 */
-#define DP_NAK_WRITE_FAILURE           0x01
-#define DP_NAK_INVALID_READ            0x02
-#define DP_NAK_CRC_FAILURE             0x03
-#define DP_NAK_BAD_PARAM               0x04
-#define DP_NAK_DEFER                   0x05
-#define DP_NAK_LINK_FAILURE            0x06
-#define DP_NAK_NO_RESOURCES            0x07
-#define DP_NAK_DPCD_FAIL               0x08
-#define DP_NAK_I2C_NAK                 0x09
-#define DP_NAK_ALLOCATE_FAIL           0x0a
-
-#define MODE_I2C_START 1
-#define MODE_I2C_WRITE 2
-#define MODE_I2C_READ  4
-#define MODE_I2C_STOP  8
-
-/* DP 1.2 MST PORTs - Section 2.5.1 v1.2a spec */
-#define DP_MST_PHYSICAL_PORT_0 0
-#define DP_MST_LOGICAL_PORT_0 8
-
-#define DP_LINK_CONSTANT_N_VALUE 0x8000
-#define DP_LINK_STATUS_SIZE       6
-bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
-                         int lane_count);
-bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
-                             int lane_count);
-u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
-                                    int lane);
-u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
-                                         int lane);
-u8 drm_dp_get_adjust_tx_ffe_preset(const u8 link_status[DP_LINK_STATUS_SIZE],
-                                  int lane);
-u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZE],
-                                        unsigned int lane);
-
-#define DP_BRANCH_OUI_HEADER_SIZE      0xc
-#define DP_RECEIVER_CAP_SIZE           0xf
-#define DP_DSC_RECEIVER_CAP_SIZE        0xf
-#define EDP_PSR_RECEIVER_CAP_SIZE      2
-#define EDP_DISPLAY_CTL_CAP_SIZE       3
-#define DP_LTTPR_COMMON_CAP_SIZE       8
-#define DP_LTTPR_PHY_CAP_SIZE          3
-
-int drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                                    enum drm_dp_phy dp_phy, bool uhbr);
-int drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                                enum drm_dp_phy dp_phy, bool uhbr);
-
-void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux,
-                                           const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
-void drm_dp_lttpr_link_train_clock_recovery_delay(void);
-void drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
-                                       const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
-void drm_dp_lttpr_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
-                                             const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
-
-u8 drm_dp_link_rate_to_bw_code(int link_rate);
-int drm_dp_bw_code_to_link_rate(u8 link_bw);
-
-#define DP_SDP_AUDIO_TIMESTAMP         0x01
-#define DP_SDP_AUDIO_STREAM            0x02
-#define DP_SDP_EXTENSION               0x04 /* DP 1.1 */
-#define DP_SDP_AUDIO_COPYMANAGEMENT    0x05 /* DP 1.2 */
-#define DP_SDP_ISRC                    0x06 /* DP 1.2 */
-#define DP_SDP_VSC                     0x07 /* DP 1.2 */
-#define DP_SDP_CAMERA_GENERIC(i)       (0x08 + (i)) /* 0-7, DP 1.3 */
-#define DP_SDP_PPS                     0x10 /* DP 1.4 */
-#define DP_SDP_VSC_EXT_VESA            0x20 /* DP 1.4 */
-#define DP_SDP_VSC_EXT_CEA             0x21 /* DP 1.4 */
-/* 0x80+ CEA-861 infoframe types */
-
-/**
- * struct dp_sdp_header - DP secondary data packet header
- * @HB0: Secondary Data Packet ID
- * @HB1: Secondary Data Packet Type
- * @HB2: Secondary Data Packet Specific header, Byte 0
- * @HB3: Secondary Data packet Specific header, Byte 1
- */
-struct dp_sdp_header {
-       u8 HB0;
-       u8 HB1;
-       u8 HB2;
-       u8 HB3;
-} __packed;
-
-#define EDP_SDP_HEADER_REVISION_MASK           0x1F
-#define EDP_SDP_HEADER_VALID_PAYLOAD_BYTES     0x1F
-#define DP_SDP_PPS_HEADER_PAYLOAD_BYTES_MINUS_1 0x7F
-
-/**
- * struct dp_sdp - DP secondary data packet
- * @sdp_header: DP secondary data packet header
- * @db: DP secondaray data packet data blocks
- * VSC SDP Payload for PSR
- * db[0]: Stereo Interface
- * db[1]: 0 - PSR State; 1 - Update RFB; 2 - CRC Valid
- * db[2]: CRC value bits 7:0 of the R or Cr component
- * db[3]: CRC value bits 15:8 of the R or Cr component
- * db[4]: CRC value bits 7:0 of the G or Y component
- * db[5]: CRC value bits 15:8 of the G or Y component
- * db[6]: CRC value bits 7:0 of the B or Cb component
- * db[7]: CRC value bits 15:8 of the B or Cb component
- * db[8] - db[31]: Reserved
- * VSC SDP Payload for Pixel Encoding/Colorimetry Format
- * db[0] - db[15]: Reserved
- * db[16]: Pixel Encoding and Colorimetry Formats
- * db[17]: Dynamic Range and Component Bit Depth
- * db[18]: Content Type
- * db[19] - db[31]: Reserved
- */
-struct dp_sdp {
-       struct dp_sdp_header sdp_header;
-       u8 db[32];
-} __packed;
-
-#define EDP_VSC_PSR_STATE_ACTIVE       (1<<0)
-#define EDP_VSC_PSR_UPDATE_RFB         (1<<1)
-#define EDP_VSC_PSR_CRC_VALUES_VALID   (1<<2)
-
-/**
- * enum dp_pixelformat - drm DP Pixel encoding formats
- *
- * This enum is used to indicate DP VSC SDP Pixel encoding formats.
- * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through
- * DB18]
- *
- * @DP_PIXELFORMAT_RGB: RGB pixel encoding format
- * @DP_PIXELFORMAT_YUV444: YCbCr 4:4:4 pixel encoding format
- * @DP_PIXELFORMAT_YUV422: YCbCr 4:2:2 pixel encoding format
- * @DP_PIXELFORMAT_YUV420: YCbCr 4:2:0 pixel encoding format
- * @DP_PIXELFORMAT_Y_ONLY: Y Only pixel encoding format
- * @DP_PIXELFORMAT_RAW: RAW pixel encoding format
- * @DP_PIXELFORMAT_RESERVED: Reserved pixel encoding format
- */
-enum dp_pixelformat {
-       DP_PIXELFORMAT_RGB = 0,
-       DP_PIXELFORMAT_YUV444 = 0x1,
-       DP_PIXELFORMAT_YUV422 = 0x2,
-       DP_PIXELFORMAT_YUV420 = 0x3,
-       DP_PIXELFORMAT_Y_ONLY = 0x4,
-       DP_PIXELFORMAT_RAW = 0x5,
-       DP_PIXELFORMAT_RESERVED = 0x6,
-};
-
-/**
- * enum dp_colorimetry - drm DP Colorimetry formats
- *
- * This enum is used to indicate DP VSC SDP Colorimetry formats.
- * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through
- * DB18] and a name of enum member follows DRM_MODE_COLORIMETRY definition.
- *
- * @DP_COLORIMETRY_DEFAULT: sRGB (IEC 61966-2-1) or
- *                          ITU-R BT.601 colorimetry format
- * @DP_COLORIMETRY_RGB_WIDE_FIXED: RGB wide gamut fixed point colorimetry format
- * @DP_COLORIMETRY_BT709_YCC: ITU-R BT.709 colorimetry format
- * @DP_COLORIMETRY_RGB_WIDE_FLOAT: RGB wide gamut floating point
- *                                 (scRGB (IEC 61966-2-2)) colorimetry format
- * @DP_COLORIMETRY_XVYCC_601: xvYCC601 colorimetry format
- * @DP_COLORIMETRY_OPRGB: OpRGB colorimetry format
- * @DP_COLORIMETRY_XVYCC_709: xvYCC709 colorimetry format
- * @DP_COLORIMETRY_DCI_P3_RGB: DCI-P3 (SMPTE RP 431-2) colorimetry format
- * @DP_COLORIMETRY_SYCC_601: sYCC601 colorimetry format
- * @DP_COLORIMETRY_RGB_CUSTOM: RGB Custom Color Profile colorimetry format
- * @DP_COLORIMETRY_OPYCC_601: opYCC601 colorimetry format
- * @DP_COLORIMETRY_BT2020_RGB: ITU-R BT.2020 R' G' B' colorimetry format
- * @DP_COLORIMETRY_BT2020_CYCC: ITU-R BT.2020 Y'c C'bc C'rc colorimetry format
- * @DP_COLORIMETRY_BT2020_YCC: ITU-R BT.2020 Y' C'b C'r colorimetry format
- */
-enum dp_colorimetry {
-       DP_COLORIMETRY_DEFAULT = 0,
-       DP_COLORIMETRY_RGB_WIDE_FIXED = 0x1,
-       DP_COLORIMETRY_BT709_YCC = 0x1,
-       DP_COLORIMETRY_RGB_WIDE_FLOAT = 0x2,
-       DP_COLORIMETRY_XVYCC_601 = 0x2,
-       DP_COLORIMETRY_OPRGB = 0x3,
-       DP_COLORIMETRY_XVYCC_709 = 0x3,
-       DP_COLORIMETRY_DCI_P3_RGB = 0x4,
-       DP_COLORIMETRY_SYCC_601 = 0x4,
-       DP_COLORIMETRY_RGB_CUSTOM = 0x5,
-       DP_COLORIMETRY_OPYCC_601 = 0x5,
-       DP_COLORIMETRY_BT2020_RGB = 0x6,
-       DP_COLORIMETRY_BT2020_CYCC = 0x6,
-       DP_COLORIMETRY_BT2020_YCC = 0x7,
-};
-
-/**
- * enum dp_dynamic_range - drm DP Dynamic Range
- *
- * This enum is used to indicate DP VSC SDP Dynamic Range.
- * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through
- * DB18]
- *
- * @DP_DYNAMIC_RANGE_VESA: VESA range
- * @DP_DYNAMIC_RANGE_CTA: CTA range
- */
-enum dp_dynamic_range {
-       DP_DYNAMIC_RANGE_VESA = 0,
-       DP_DYNAMIC_RANGE_CTA = 1,
-};
-
-/**
- * enum dp_content_type - drm DP Content Type
- *
- * This enum is used to indicate DP VSC SDP Content Types.
- * It is based on DP 1.4 spec [Table 2-117: VSC SDP Payload for DB16 through
- * DB18]
- * CTA-861-G defines content types and expected processing by a sink device
- *
- * @DP_CONTENT_TYPE_NOT_DEFINED: Not defined type
- * @DP_CONTENT_TYPE_GRAPHICS: Graphics type
- * @DP_CONTENT_TYPE_PHOTO: Photo type
- * @DP_CONTENT_TYPE_VIDEO: Video type
- * @DP_CONTENT_TYPE_GAME: Game type
- */
-enum dp_content_type {
-       DP_CONTENT_TYPE_NOT_DEFINED = 0x00,
-       DP_CONTENT_TYPE_GRAPHICS = 0x01,
-       DP_CONTENT_TYPE_PHOTO = 0x02,
-       DP_CONTENT_TYPE_VIDEO = 0x03,
-       DP_CONTENT_TYPE_GAME = 0x04,
-};
-
-/**
- * struct drm_dp_vsc_sdp - drm DP VSC SDP
- *
- * This structure represents a DP VSC SDP of drm
- * It is based on DP 1.4 spec [Table 2-116: VSC SDP Header Bytes] and
- * [Table 2-117: VSC SDP Payload for DB16 through DB18]
- *
- * @sdp_type: secondary-data packet type
- * @revision: revision number
- * @length: number of valid data bytes
- * @pixelformat: pixel encoding format
- * @colorimetry: colorimetry format
- * @bpc: bit per color
- * @dynamic_range: dynamic range information
- * @content_type: CTA-861-G defines content types and expected processing by a sink device
- */
-struct drm_dp_vsc_sdp {
-       unsigned char sdp_type;
-       unsigned char revision;
-       unsigned char length;
-       enum dp_pixelformat pixelformat;
-       enum dp_colorimetry colorimetry;
-       int bpc;
-       enum dp_dynamic_range dynamic_range;
-       enum dp_content_type content_type;
-};
-
-void drm_dp_vsc_sdp_log(const char *level, struct device *dev,
-                       const struct drm_dp_vsc_sdp *vsc);
-
-int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE]);
-
-static inline int
-drm_dp_max_link_rate(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]);
-}
-
-static inline u8
-drm_dp_max_lane_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
-}
-
-static inline bool
-drm_dp_enhanced_frame_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return dpcd[DP_DPCD_REV] >= 0x11 &&
-               (dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP);
-}
-
-static inline bool
-drm_dp_fast_training_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return dpcd[DP_DPCD_REV] >= 0x11 &&
-               (dpcd[DP_MAX_DOWNSPREAD] & DP_NO_AUX_HANDSHAKE_LINK_TRAINING);
-}
-
-static inline bool
-drm_dp_tps3_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return dpcd[DP_DPCD_REV] >= 0x12 &&
-               dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED;
-}
-
-static inline bool
-drm_dp_max_downspread(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return dpcd[DP_DPCD_REV] >= 0x11 ||
-               dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5;
-}
-
-static inline bool
-drm_dp_tps4_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return dpcd[DP_DPCD_REV] >= 0x14 &&
-               dpcd[DP_MAX_DOWNSPREAD] & DP_TPS4_SUPPORTED;
-}
-
-static inline u8
-drm_dp_training_pattern_mask(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return (dpcd[DP_DPCD_REV] >= 0x14) ? DP_TRAINING_PATTERN_MASK_1_4 :
-               DP_TRAINING_PATTERN_MASK;
-}
-
-static inline bool
-drm_dp_is_branch(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT;
-}
-
-/* DP/eDP DSC support */
-u8 drm_dp_dsc_sink_max_slice_count(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE],
-                                  bool is_edp);
-u8 drm_dp_dsc_sink_line_buf_depth(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]);
-int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpc[DP_DSC_RECEIVER_CAP_SIZE],
-                                        u8 dsc_bpc[3]);
-
-static inline bool
-drm_dp_sink_supports_dsc(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE])
-{
-       return dsc_dpcd[DP_DSC_SUPPORT - DP_DSC_SUPPORT] &
-               DP_DSC_DECOMPRESSION_IS_SUPPORTED;
-}
-
-static inline u16
-drm_edp_dsc_sink_output_bpp(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE])
-{
-       return dsc_dpcd[DP_DSC_MAX_BITS_PER_PIXEL_LOW - DP_DSC_SUPPORT] |
-               (dsc_dpcd[DP_DSC_MAX_BITS_PER_PIXEL_HI - DP_DSC_SUPPORT] &
-                DP_DSC_MAX_BITS_PER_PIXEL_HI_MASK <<
-                DP_DSC_MAX_BITS_PER_PIXEL_HI_SHIFT);
-}
-
-static inline u32
-drm_dp_dsc_sink_max_slice_width(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE])
-{
-       /* Max Slicewidth = Number of Pixels * 320 */
-       return dsc_dpcd[DP_DSC_MAX_SLICE_WIDTH - DP_DSC_SUPPORT] *
-               DP_DSC_SLICE_WIDTH_MULTIPLIER;
-}
-
-/* Forward Error Correction Support on DP 1.4 */
-static inline bool
-drm_dp_sink_supports_fec(const u8 fec_capable)
-{
-       return fec_capable & DP_FEC_CAPABLE;
-}
-
-static inline bool
-drm_dp_channel_coding_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return dpcd[DP_MAIN_LINK_CHANNEL_CODING] & DP_CAP_ANSI_8B10B;
-}
-
-static inline bool
-drm_dp_alternate_scrambler_reset_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return dpcd[DP_EDP_CONFIGURATION_CAP] &
-                       DP_ALTERNATE_SCRAMBLER_RESET_CAP;
-}
-
-/* Ignore MSA timing for Adaptive Sync support on DP 1.4 */
-static inline bool
-drm_dp_sink_can_do_video_without_timing_msa(const u8 dpcd[DP_RECEIVER_CAP_SIZE])
-{
-       return dpcd[DP_DOWN_STREAM_PORT_COUNT] &
-               DP_MSA_TIMING_PAR_IGNORED;
-}
-
-/**
- * drm_edp_backlight_supported() - Check an eDP DPCD for VESA backlight support
- * @edp_dpcd: The DPCD to check
- *
- * Note that currently this function will return %false for panels which support various DPCD
- * backlight features but which require the brightness be set through PWM, and don't support setting
- * the brightness level via the DPCD.
- *
- * Returns: %True if @edp_dpcd indicates that VESA backlight controls are supported, %false
- * otherwise
- */
-static inline bool
-drm_edp_backlight_supported(const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE])
-{
-       return !!(edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP);
-}
-
-/*
- * DisplayPort AUX channel
- */
-
-/**
- * struct drm_dp_aux_msg - DisplayPort AUX channel transaction
- * @address: address of the (first) register to access
- * @request: contains the type of transaction (see DP_AUX_* macros)
- * @reply: upon completion, contains the reply type of the transaction
- * @buffer: pointer to a transmission or reception buffer
- * @size: size of @buffer
- */
-struct drm_dp_aux_msg {
-       unsigned int address;
-       u8 request;
-       u8 reply;
-       void *buffer;
-       size_t size;
-};
-
-struct cec_adapter;
-struct edid;
-struct drm_connector;
-
-/**
- * struct drm_dp_aux_cec - DisplayPort CEC-Tunneling-over-AUX
- * @lock: mutex protecting this struct
- * @adap: the CEC adapter for CEC-Tunneling-over-AUX support.
- * @connector: the connector this CEC adapter is associated with
- * @unregister_work: unregister the CEC adapter
- */
-struct drm_dp_aux_cec {
-       struct mutex lock;
-       struct cec_adapter *adap;
-       struct drm_connector *connector;
-       struct delayed_work unregister_work;
-};
-
-/**
- * struct drm_dp_aux - DisplayPort AUX channel
- *
- * An AUX channel can also be used to transport I2C messages to a sink. A
- * typical application of that is to access an EDID that's present in the sink
- * device. The @transfer() function can also be used to execute such
- * transactions. The drm_dp_aux_register() function registers an I2C adapter
- * that can be passed to drm_probe_ddc(). Upon removal, drivers should call
- * drm_dp_aux_unregister() to remove the I2C adapter. The I2C adapter uses long
- * transfers by default; if a partial response is received, the adapter will
- * drop down to the size given by the partial response for this transaction
- * only.
- */
-struct drm_dp_aux {
-       /**
-        * @name: user-visible name of this AUX channel and the
-        * I2C-over-AUX adapter.
-        *
-        * It's also used to specify the name of the I2C adapter. If set
-        * to %NULL, dev_name() of @dev will be used.
-        */
-       const char *name;
-
-       /**
-        * @ddc: I2C adapter that can be used for I2C-over-AUX
-        * communication
-        */
-       struct i2c_adapter ddc;
-
-       /**
-        * @dev: pointer to struct device that is the parent for this
-        * AUX channel.
-        */
-       struct device *dev;
-
-       /**
-        * @drm_dev: pointer to the &drm_device that owns this AUX channel.
-        * Beware, this may be %NULL before drm_dp_aux_register() has been
-        * called.
-        *
-        * It should be set to the &drm_device that will be using this AUX
-        * channel as early as possible. For many graphics drivers this should
-        * happen before drm_dp_aux_init(), however it's perfectly fine to set
-        * this field later so long as it's assigned before calling
-        * drm_dp_aux_register().
-        */
-       struct drm_device *drm_dev;
-
-       /**
-        * @crtc: backpointer to the crtc that is currently using this
-        * AUX channel
-        */
-       struct drm_crtc *crtc;
-
-       /**
-        * @hw_mutex: internal mutex used for locking transfers.
-        *
-        * Note that if the underlying hardware is shared among multiple
-        * channels, the driver needs to do additional locking to
-        * prevent concurrent access.
-        */
-       struct mutex hw_mutex;
-
-       /**
-        * @crc_work: worker that captures CRCs for each frame
-        */
-       struct work_struct crc_work;
-
-       /**
-        * @crc_count: counter of captured frame CRCs
-        */
-       u8 crc_count;
-
-       /**
-        * @transfer: transfers a message representing a single AUX
-        * transaction.
-        *
-        * This is a hardware-specific implementation of how
-        * transactions are executed that the drivers must provide.
-        *
-        * A pointer to a &drm_dp_aux_msg structure describing the
-        * transaction is passed into this function. Upon success, the
-        * implementation should return the number of payload bytes that
-        * were transferred, or a negative error-code on failure.
-        *
-        * Helpers will propagate these errors, with the exception of
-        * the %-EBUSY error, which causes a transaction to be retried.
-        * On a short, helpers will return %-EPROTO to make it simpler
-        * to check for failure.
-        *
-        * The @transfer() function must only modify the reply field of
-        * the &drm_dp_aux_msg structure. The retry logic and i2c
-        * helpers assume this is the case.
-        *
-        * Also note that this callback can be called no matter the
-        * state @dev is in. Drivers that need that device to be powered
-        * to perform this operation will first need to make sure it's
-        * been properly enabled.
-        */
-       ssize_t (*transfer)(struct drm_dp_aux *aux,
-                           struct drm_dp_aux_msg *msg);
-
-       /**
-        * @i2c_nack_count: Counts I2C NACKs, used for DP validation.
-        */
-       unsigned i2c_nack_count;
-       /**
-        * @i2c_defer_count: Counts I2C DEFERs, used for DP validation.
-        */
-       unsigned i2c_defer_count;
-       /**
-        * @cec: struct containing fields used for CEC-Tunneling-over-AUX.
-        */
-       struct drm_dp_aux_cec cec;
-       /**
-        * @is_remote: Is this AUX CH actually using sideband messaging.
-        */
-       bool is_remote;
-};
-
-ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
-                        void *buffer, size_t size);
-ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
-                         void *buffer, size_t size);
-
-/**
- * drm_dp_dpcd_readb() - read a single byte from the DPCD
- * @aux: DisplayPort AUX channel
- * @offset: address of the register to read
- * @valuep: location where the value of the register will be stored
- *
- * Returns the number of bytes transferred (1) on success, or a negative
- * error code on failure.
- */
-static inline ssize_t drm_dp_dpcd_readb(struct drm_dp_aux *aux,
-                                       unsigned int offset, u8 *valuep)
-{
-       return drm_dp_dpcd_read(aux, offset, valuep, 1);
-}
-
-/**
- * drm_dp_dpcd_writeb() - write a single byte to the DPCD
- * @aux: DisplayPort AUX channel
- * @offset: address of the register to write
- * @value: value to write to the register
- *
- * Returns the number of bytes transferred (1) on success, or a negative
- * error code on failure.
- */
-static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux,
-                                        unsigned int offset, u8 value)
-{
-       return drm_dp_dpcd_write(aux, offset, &value, 1);
-}
-
-int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux,
-                         u8 dpcd[DP_RECEIVER_CAP_SIZE]);
-
-int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
-                                u8 status[DP_LINK_STATUS_SIZE]);
-
-int drm_dp_dpcd_read_phy_link_status(struct drm_dp_aux *aux,
-                                    enum drm_dp_phy dp_phy,
-                                    u8 link_status[DP_LINK_STATUS_SIZE]);
-
-bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux,
-                                   u8 real_edid_checksum);
-
-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]);
-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 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],
-                            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]);
-void drm_dp_set_subconnector_property(struct drm_connector *connector,
-                                     enum drm_connector_status status,
-                                     const u8 *dpcd,
-                                     const u8 port_cap[4]);
-
-struct drm_dp_desc;
-bool drm_dp_read_sink_count_cap(struct drm_connector *connector,
-                               const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                               const struct drm_dp_desc *desc);
-int drm_dp_read_sink_count(struct drm_dp_aux *aux);
-
-int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,
-                                 u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
-int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,
-                              enum drm_dp_phy dp_phy,
-                              u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
-int drm_dp_lttpr_count(const u8 cap[DP_LTTPR_COMMON_CAP_SIZE]);
-int drm_dp_lttpr_max_link_rate(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
-int drm_dp_lttpr_max_lane_count(const u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
-bool drm_dp_lttpr_voltage_swing_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
-bool drm_dp_lttpr_pre_emphasis_level_3_supported(const u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
-
-void drm_dp_remote_aux_init(struct drm_dp_aux *aux);
-void drm_dp_aux_init(struct drm_dp_aux *aux);
-int drm_dp_aux_register(struct drm_dp_aux *aux);
-void drm_dp_aux_unregister(struct drm_dp_aux *aux);
-
-int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc);
-int drm_dp_stop_crc(struct drm_dp_aux *aux);
-
-struct drm_dp_dpcd_ident {
-       u8 oui[3];
-       u8 device_id[6];
-       u8 hw_rev;
-       u8 sw_major_rev;
-       u8 sw_minor_rev;
-} __packed;
-
-/**
- * struct drm_dp_desc - DP branch/sink device descriptor
- * @ident: DP device identification from DPCD 0x400 (sink) or 0x500 (branch).
- * @quirks: Quirks; use drm_dp_has_quirk() to query for the quirks.
- */
-struct drm_dp_desc {
-       struct drm_dp_dpcd_ident ident;
-       u32 quirks;
-};
-
-int drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc,
-                    bool is_branch);
-
-/**
- * enum drm_dp_quirk - Display Port sink/branch device specific quirks
- *
- * Display Port sink and branch devices in the wild have a variety of bugs, try
- * to collect them here. The quirks are shared, but it's up to the drivers to
- * implement workarounds for them.
- */
-enum drm_dp_quirk {
-       /**
-        * @DP_DPCD_QUIRK_CONSTANT_N:
-        *
-        * The device requires main link attributes Mvid and Nvid to be limited
-        * to 16 bits. So will give a constant value (0x8000) for compatability.
-        */
-       DP_DPCD_QUIRK_CONSTANT_N,
-       /**
-        * @DP_DPCD_QUIRK_NO_PSR:
-        *
-        * The device does not support PSR even if reports that it supports or
-        * driver still need to implement proper handling for such device.
-        */
-       DP_DPCD_QUIRK_NO_PSR,
-       /**
-        * @DP_DPCD_QUIRK_NO_SINK_COUNT:
-        *
-        * The device does not set SINK_COUNT to a non-zero value.
-        * The driver should ignore SINK_COUNT during detection. Note that
-        * drm_dp_read_sink_count_cap() automatically checks for this quirk.
-        */
-       DP_DPCD_QUIRK_NO_SINK_COUNT,
-       /**
-        * @DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD:
-        *
-        * The device supports MST DSC despite not supporting Virtual DPCD.
-        * The DSC caps can be read from the physical aux instead.
-        */
-       DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD,
-       /**
-        * @DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS:
-        *
-        * The device supports a link rate of 3.24 Gbps (multiplier 0xc) despite
-        * the DP_MAX_LINK_RATE register reporting a lower max multiplier.
-        */
-       DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS,
-};
-
-/**
- * drm_dp_has_quirk() - does the DP device have a specific quirk
- * @desc: Device descriptor filled by drm_dp_read_desc()
- * @quirk: Quirk to query for
- *
- * Return true if DP device identified by @desc has @quirk.
- */
-static inline bool
-drm_dp_has_quirk(const struct drm_dp_desc *desc, enum drm_dp_quirk quirk)
-{
-       return desc->quirks & BIT(quirk);
-}
-
-/**
- * struct drm_edp_backlight_info - Probed eDP backlight info struct
- * @pwmgen_bit_count: The pwmgen bit count
- * @pwm_freq_pre_divider: The PWM frequency pre-divider value being used for this backlight, if any
- * @max: The maximum backlight level that may be set
- * @lsb_reg_used: Do we also write values to the DP_EDP_BACKLIGHT_BRIGHTNESS_LSB register?
- * @aux_enable: Does the panel support the AUX enable cap?
- * @aux_set: Does the panel support setting the brightness through AUX?
- *
- * This structure contains various data about an eDP backlight, which can be populated by using
- * drm_edp_backlight_init().
- */
-struct drm_edp_backlight_info {
-       u8 pwmgen_bit_count;
-       u8 pwm_freq_pre_divider;
-       u16 max;
-
-       bool lsb_reg_used : 1;
-       bool aux_enable : 1;
-       bool aux_set : 1;
-};
-
-int
-drm_edp_backlight_init(struct drm_dp_aux *aux, struct drm_edp_backlight_info *bl,
-                      u16 driver_pwm_freq_hz, const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE],
-                      u16 *current_level, u8 *current_mode);
-int drm_edp_backlight_set_level(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
-                               u16 level);
-int drm_edp_backlight_enable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl,
-                            u16 level);
-int drm_edp_backlight_disable(struct drm_dp_aux *aux, const struct drm_edp_backlight_info *bl);
-
-#if IS_ENABLED(CONFIG_DRM_KMS_HELPER) && (IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
-       (IS_MODULE(CONFIG_DRM_KMS_HELPER) && IS_MODULE(CONFIG_BACKLIGHT_CLASS_DEVICE)))
-
-int drm_panel_dp_aux_backlight(struct drm_panel *panel, struct drm_dp_aux *aux);
-
-#else
-
-static inline int drm_panel_dp_aux_backlight(struct drm_panel *panel,
-                                            struct drm_dp_aux *aux)
-{
-       return 0;
-}
-
-#endif
-
-#ifdef CONFIG_DRM_DP_CEC
-void drm_dp_cec_irq(struct drm_dp_aux *aux);
-void drm_dp_cec_register_connector(struct drm_dp_aux *aux,
-                                  struct drm_connector *connector);
-void drm_dp_cec_unregister_connector(struct drm_dp_aux *aux);
-void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const struct edid *edid);
-void drm_dp_cec_unset_edid(struct drm_dp_aux *aux);
-#else
-static inline void drm_dp_cec_irq(struct drm_dp_aux *aux)
-{
-}
-
-static inline void
-drm_dp_cec_register_connector(struct drm_dp_aux *aux,
-                             struct drm_connector *connector)
-{
-}
-
-static inline void drm_dp_cec_unregister_connector(struct drm_dp_aux *aux)
-{
-}
-
-static inline void drm_dp_cec_set_edid(struct drm_dp_aux *aux,
-                                      const struct edid *edid)
-{
-}
-
-static inline void drm_dp_cec_unset_edid(struct drm_dp_aux *aux)
-{
-}
-
-#endif
-
-/**
- * struct drm_dp_phy_test_params - DP Phy Compliance parameters
- * @link_rate: Requested Link rate from DPCD 0x219
- * @num_lanes: Number of lanes requested by sing through DPCD 0x220
- * @phy_pattern: DP Phy test pattern from DPCD 0x248
- * @hbr2_reset: DP HBR2_COMPLIANCE_SCRAMBLER_RESET from DCPD 0x24A and 0x24B
- * @custom80: DP Test_80BIT_CUSTOM_PATTERN from DPCDs 0x250 through 0x259
- * @enhanced_frame_cap: flag for enhanced frame capability.
- */
-struct drm_dp_phy_test_params {
-       int link_rate;
-       u8 num_lanes;
-       u8 phy_pattern;
-       u8 hbr2_reset[2];
-       u8 custom80[10];
-       bool enhanced_frame_cap;
-};
-
-int drm_dp_get_phy_test_pattern(struct drm_dp_aux *aux,
-                               struct drm_dp_phy_test_params *data);
-int drm_dp_set_phy_test_pattern(struct drm_dp_aux *aux,
-                               struct drm_dp_phy_test_params *data, u8 dp_rev);
-int drm_dp_get_pcon_max_frl_bw(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                              const u8 port_cap[4]);
-int drm_dp_pcon_frl_prepare(struct drm_dp_aux *aux, bool enable_frl_ready_hpd);
-bool drm_dp_pcon_is_frl_ready(struct drm_dp_aux *aux);
-int drm_dp_pcon_frl_configure_1(struct drm_dp_aux *aux, int max_frl_gbps,
-                               u8 frl_mode);
-int drm_dp_pcon_frl_configure_2(struct drm_dp_aux *aux, int max_frl_mask,
-                               u8 frl_type);
-int drm_dp_pcon_reset_frl_config(struct drm_dp_aux *aux);
-int drm_dp_pcon_frl_enable(struct drm_dp_aux *aux);
-
-bool drm_dp_pcon_hdmi_link_active(struct drm_dp_aux *aux);
-int drm_dp_pcon_hdmi_link_mode(struct drm_dp_aux *aux, u8 *frl_trained_mask);
-void drm_dp_pcon_hdmi_frl_link_error_count(struct drm_dp_aux *aux,
-                                          struct drm_connector *connector);
-bool drm_dp_pcon_enc_is_dsc_1_2(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]);
-int drm_dp_pcon_dsc_max_slices(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]);
-int drm_dp_pcon_dsc_max_slice_width(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]);
-int drm_dp_pcon_dsc_bpp_incr(const u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]);
-int drm_dp_pcon_pps_default(struct drm_dp_aux *aux);
-int drm_dp_pcon_pps_override_buf(struct drm_dp_aux *aux, u8 pps_buf[128]);
-int drm_dp_pcon_pps_override_param(struct drm_dp_aux *aux, u8 pps_param[6]);
-bool drm_dp_downstream_rgb_to_ycbcr_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
-                                              const u8 port_cap[4], u8 color_spc);
-int drm_dp_pcon_convert_rgb_to_ycbcr(struct drm_dp_aux *aux, u8 color_spc);
-
-#endif /* _DRM_DP_HELPER_H_ */
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
deleted file mode 100644 (file)
index 78044ac..0000000
+++ /dev/null
@@ -1,972 +0,0 @@
-/*
- * Copyright © 2014 Red Hat.
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that copyright
- * notice and this permission notice appear in supporting documentation, and
- * that the name of the copyright holders not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission.  The copyright holders make no representations
- * about the suitability of this software for any purpose.  It is provided "as
- * is" without express or implied warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
- */
-#ifndef _DRM_DP_MST_HELPER_H_
-#define _DRM_DP_MST_HELPER_H_
-
-#include <linux/types.h>
-#include <drm/drm_dp_helper.h>
-#include <drm/drm_atomic.h>
-
-#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
-#include <linux/stackdepot.h>
-#include <linux/timekeeping.h>
-
-enum drm_dp_mst_topology_ref_type {
-       DRM_DP_MST_TOPOLOGY_REF_GET,
-       DRM_DP_MST_TOPOLOGY_REF_PUT,
-};
-
-struct drm_dp_mst_topology_ref_history {
-       struct drm_dp_mst_topology_ref_entry {
-               enum drm_dp_mst_topology_ref_type type;
-               int count;
-               ktime_t ts_nsec;
-               depot_stack_handle_t backtrace;
-       } *entries;
-       int len;
-};
-#endif /* IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) */
-
-struct drm_dp_mst_branch;
-
-/**
- * struct drm_dp_vcpi - Virtual Channel Payload Identifier
- * @vcpi: Virtual channel ID.
- * @pbn: Payload Bandwidth Number for this channel
- * @aligned_pbn: PBN aligned with slot size
- * @num_slots: number of slots for this PBN
- */
-struct drm_dp_vcpi {
-       int vcpi;
-       int pbn;
-       int aligned_pbn;
-       int num_slots;
-};
-
-/**
- * struct drm_dp_mst_port - MST port
- * @port_num: port number
- * @input: if this port is an input port. Protected by
- * &drm_dp_mst_topology_mgr.base.lock.
- * @mcs: message capability status - DP 1.2 spec. Protected by
- * &drm_dp_mst_topology_mgr.base.lock.
- * @ddps: DisplayPort Device Plug Status - DP 1.2. Protected by
- * &drm_dp_mst_topology_mgr.base.lock.
- * @pdt: Peer Device Type. Protected by
- * &drm_dp_mst_topology_mgr.base.lock.
- * @ldps: Legacy Device Plug Status. Protected by
- * &drm_dp_mst_topology_mgr.base.lock.
- * @dpcd_rev: DPCD revision of device on this port. Protected by
- * &drm_dp_mst_topology_mgr.base.lock.
- * @num_sdp_streams: Number of simultaneous streams. Protected by
- * &drm_dp_mst_topology_mgr.base.lock.
- * @num_sdp_stream_sinks: Number of stream sinks. Protected by
- * &drm_dp_mst_topology_mgr.base.lock.
- * @full_pbn: Max possible bandwidth for this port. Protected by
- * &drm_dp_mst_topology_mgr.base.lock.
- * @next: link to next port on this branch device
- * @aux: i2c aux transport to talk to device connected to this port, protected
- * by &drm_dp_mst_topology_mgr.base.lock.
- * @parent: branch device parent of this port
- * @vcpi: Virtual Channel Payload info for this port.
- * @connector: DRM connector this port is connected to. Protected by
- * &drm_dp_mst_topology_mgr.base.lock.
- * @mgr: topology manager this port lives under.
- *
- * This structure represents an MST port endpoint on a device somewhere
- * in the MST topology.
- */
-struct drm_dp_mst_port {
-       /**
-        * @topology_kref: refcount for this port's lifetime in the topology,
-        * only the DP MST helpers should need to touch this
-        */
-       struct kref topology_kref;
-
-       /**
-        * @malloc_kref: refcount for the memory allocation containing this
-        * structure. See drm_dp_mst_get_port_malloc() and
-        * drm_dp_mst_put_port_malloc().
-        */
-       struct kref malloc_kref;
-
-#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
-       /**
-        * @topology_ref_history: A history of each topology
-        * reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.
-        */
-       struct drm_dp_mst_topology_ref_history topology_ref_history;
-#endif
-
-       u8 port_num;
-       bool input;
-       bool mcs;
-       bool ddps;
-       u8 pdt;
-       bool ldps;
-       u8 dpcd_rev;
-       u8 num_sdp_streams;
-       u8 num_sdp_stream_sinks;
-       uint16_t full_pbn;
-       struct list_head next;
-       /**
-        * @mstb: the branch device connected to this port, if there is one.
-        * This should be considered protected for reading by
-        * &drm_dp_mst_topology_mgr.lock. There are two exceptions to this:
-        * &drm_dp_mst_topology_mgr.up_req_work and
-        * &drm_dp_mst_topology_mgr.work, which do not grab
-        * &drm_dp_mst_topology_mgr.lock during reads but are the only
-        * updaters of this list and are protected from writing concurrently
-        * by &drm_dp_mst_topology_mgr.probe_lock.
-        */
-       struct drm_dp_mst_branch *mstb;
-       struct drm_dp_aux aux; /* i2c bus for this port? */
-       struct drm_dp_mst_branch *parent;
-
-       struct drm_dp_vcpi vcpi;
-       struct drm_connector *connector;
-       struct drm_dp_mst_topology_mgr *mgr;
-
-       /**
-        * @cached_edid: for DP logical ports - make tiling work by ensuring
-        * that the EDID for all connectors is read immediately.
-        */
-       struct edid *cached_edid;
-       /**
-        * @has_audio: Tracks whether the sink connector to this port is
-        * audio-capable.
-        */
-       bool has_audio;
-
-       /**
-        * @fec_capable: bool indicating if FEC can be supported up to that
-        * point in the MST topology.
-        */
-       bool fec_capable;
-};
-
-/* sideband msg header - not bit struct */
-struct drm_dp_sideband_msg_hdr {
-       u8 lct;
-       u8 lcr;
-       u8 rad[8];
-       bool broadcast;
-       bool path_msg;
-       u8 msg_len;
-       bool somt;
-       bool eomt;
-       bool seqno;
-};
-
-struct drm_dp_sideband_msg_rx {
-       u8 chunk[48];
-       u8 msg[256];
-       u8 curchunk_len;
-       u8 curchunk_idx; /* chunk we are parsing now */
-       u8 curchunk_hdrlen;
-       u8 curlen; /* total length of the msg */
-       bool have_somt;
-       bool have_eomt;
-       struct drm_dp_sideband_msg_hdr initial_hdr;
-};
-
-/**
- * struct drm_dp_mst_branch - MST branch device.
- * @rad: Relative Address to talk to this branch device.
- * @lct: Link count total to talk to this branch device.
- * @num_ports: number of ports on the branch.
- * @port_parent: pointer to the port parent, NULL if toplevel.
- * @mgr: topology manager for this branch device.
- * @link_address_sent: if a link address message has been sent to this device yet.
- * @guid: guid for DP 1.2 branch device. port under this branch can be
- * identified by port #.
- *
- * This structure represents an MST branch device, there is one
- * primary branch device at the root, along with any other branches connected
- * to downstream port of parent branches.
- */
-struct drm_dp_mst_branch {
-       /**
-        * @topology_kref: refcount for this branch device's lifetime in the
-        * topology, only the DP MST helpers should need to touch this
-        */
-       struct kref topology_kref;
-
-       /**
-        * @malloc_kref: refcount for the memory allocation containing this
-        * structure. See drm_dp_mst_get_mstb_malloc() and
-        * drm_dp_mst_put_mstb_malloc().
-        */
-       struct kref malloc_kref;
-
-#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
-       /**
-        * @topology_ref_history: A history of each topology
-        * reference/dereference. See CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS.
-        */
-       struct drm_dp_mst_topology_ref_history topology_ref_history;
-#endif
-
-       /**
-        * @destroy_next: linked-list entry used by
-        * drm_dp_delayed_destroy_work()
-        */
-       struct list_head destroy_next;
-
-       u8 rad[8];
-       u8 lct;
-       int num_ports;
-
-       /**
-        * @ports: the list of ports on this branch device. This should be
-        * considered protected for reading by &drm_dp_mst_topology_mgr.lock.
-        * There are two exceptions to this:
-        * &drm_dp_mst_topology_mgr.up_req_work and
-        * &drm_dp_mst_topology_mgr.work, which do not grab
-        * &drm_dp_mst_topology_mgr.lock during reads but are the only
-        * updaters of this list and are protected from updating the list
-        * concurrently by @drm_dp_mst_topology_mgr.probe_lock
-        */
-       struct list_head ports;
-
-       struct drm_dp_mst_port *port_parent;
-       struct drm_dp_mst_topology_mgr *mgr;
-
-       bool link_address_sent;
-
-       /* global unique identifier to identify branch devices */
-       u8 guid[16];
-};
-
-
-struct drm_dp_nak_reply {
-       u8 guid[16];
-       u8 reason;
-       u8 nak_data;
-};
-
-struct drm_dp_link_address_ack_reply {
-       u8 guid[16];
-       u8 nports;
-       struct drm_dp_link_addr_reply_port {
-               bool input_port;
-               u8 peer_device_type;
-               u8 port_number;
-               bool mcs;
-               bool ddps;
-               bool legacy_device_plug_status;
-               u8 dpcd_revision;
-               u8 peer_guid[16];
-               u8 num_sdp_streams;
-               u8 num_sdp_stream_sinks;
-       } ports[16];
-};
-
-struct drm_dp_remote_dpcd_read_ack_reply {
-       u8 port_number;
-       u8 num_bytes;
-       u8 bytes[255];
-};
-
-struct drm_dp_remote_dpcd_write_ack_reply {
-       u8 port_number;
-};
-
-struct drm_dp_remote_dpcd_write_nak_reply {
-       u8 port_number;
-       u8 reason;
-       u8 bytes_written_before_failure;
-};
-
-struct drm_dp_remote_i2c_read_ack_reply {
-       u8 port_number;
-       u8 num_bytes;
-       u8 bytes[255];
-};
-
-struct drm_dp_remote_i2c_read_nak_reply {
-       u8 port_number;
-       u8 nak_reason;
-       u8 i2c_nak_transaction;
-};
-
-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 {
-       u8 port_number;
-       u8 number_sdp_streams;
-       u8 vcpi;
-       u16 pbn;
-       u8 sdp_stream_sink[DRM_DP_MAX_SDP_STREAMS];
-};
-
-struct drm_dp_allocate_payload_ack_reply {
-       u8 port_number;
-       u8 vcpi;
-       u16 allocated_pbn;
-};
-
-struct drm_dp_connection_status_notify {
-       u8 guid[16];
-       u8 port_number;
-       bool legacy_device_plug_status;
-       bool displayport_device_plug_status;
-       bool message_capability_status;
-       bool input_port;
-       u8 peer_device_type;
-};
-
-struct drm_dp_remote_dpcd_read {
-       u8 port_number;
-       u32 dpcd_address;
-       u8 num_bytes;
-};
-
-struct drm_dp_remote_dpcd_write {
-       u8 port_number;
-       u32 dpcd_address;
-       u8 num_bytes;
-       u8 *bytes;
-};
-
-#define DP_REMOTE_I2C_READ_MAX_TRANSACTIONS 4
-struct drm_dp_remote_i2c_read {
-       u8 num_transactions;
-       u8 port_number;
-       struct drm_dp_remote_i2c_read_tx {
-               u8 i2c_dev_id;
-               u8 num_bytes;
-               u8 *bytes;
-               u8 no_stop_bit;
-               u8 i2c_transaction_delay;
-       } transactions[DP_REMOTE_I2C_READ_MAX_TRANSACTIONS];
-       u8 read_i2c_device_id;
-       u8 num_bytes_read;
-};
-
-struct drm_dp_remote_i2c_write {
-       u8 port_number;
-       u8 write_i2c_device_id;
-       u8 num_bytes;
-       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;
-};
-
-struct drm_dp_enum_path_resources_ack_reply {
-       u8 port_number;
-       bool fec_capable;
-       u16 full_payload_bw_number;
-       u16 avail_payload_bw_number;
-};
-
-/* covers POWER_DOWN_PHY, POWER_UP_PHY */
-struct drm_dp_port_number_rep {
-       u8 port_number;
-};
-
-struct drm_dp_query_payload {
-       u8 port_number;
-       u8 vcpi;
-};
-
-struct drm_dp_resource_status_notify {
-       u8 port_number;
-       u8 guid[16];
-       u16 available_pbn;
-};
-
-struct drm_dp_query_payload_ack_reply {
-       u8 port_number;
-       u16 allocated_pbn;
-};
-
-struct drm_dp_sideband_msg_req_body {
-       u8 req_type;
-       union ack_req {
-               struct drm_dp_connection_status_notify conn_stat;
-               struct drm_dp_port_number_req port_num;
-               struct drm_dp_resource_status_notify resource_stat;
-
-               struct drm_dp_query_payload query_payload;
-               struct drm_dp_allocate_payload allocate_payload;
-
-               struct drm_dp_remote_dpcd_read dpcd_read;
-               struct drm_dp_remote_dpcd_write dpcd_write;
-
-               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;
-};
-
-struct drm_dp_sideband_msg_reply_body {
-       u8 reply_type;
-       u8 req_type;
-       union ack_replies {
-               struct drm_dp_nak_reply nak;
-               struct drm_dp_link_address_ack_reply link_addr;
-               struct drm_dp_port_number_rep port_number;
-
-               struct drm_dp_enum_path_resources_ack_reply path_resources;
-               struct drm_dp_allocate_payload_ack_reply allocate_payload;
-               struct drm_dp_query_payload_ack_reply query_payload;
-
-               struct drm_dp_remote_dpcd_read_ack_reply remote_dpcd_read_ack;
-               struct drm_dp_remote_dpcd_write_ack_reply remote_dpcd_write_ack;
-               struct drm_dp_remote_dpcd_write_nak_reply remote_dpcd_write_nack;
-
-               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;
-};
-
-/* msg is queued to be put into a slot */
-#define DRM_DP_SIDEBAND_TX_QUEUED 0
-/* msg has started transmitting on a slot - still on msgq */
-#define DRM_DP_SIDEBAND_TX_START_SEND 1
-/* msg has finished transmitting on a slot - removed from msgq only in slot */
-#define DRM_DP_SIDEBAND_TX_SENT 2
-/* msg has received a response - removed from slot */
-#define DRM_DP_SIDEBAND_TX_RX 3
-#define DRM_DP_SIDEBAND_TX_TIMEOUT 4
-
-struct drm_dp_sideband_msg_tx {
-       u8 msg[256];
-       u8 chunk[48];
-       u8 cur_offset;
-       u8 cur_len;
-       struct drm_dp_mst_branch *dst;
-       struct list_head next;
-       int seqno;
-       int state;
-       bool path_msg;
-       struct drm_dp_sideband_msg_reply_body reply;
-};
-
-/* sideband msg handler */
-struct drm_dp_mst_topology_mgr;
-struct drm_dp_mst_topology_cbs {
-       /* create a connector for a port */
-       struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path);
-       /*
-        * Checks for any pending MST interrupts, passing them to MST core for
-        * processing, the same way an HPD IRQ pulse handler would do this.
-        * If provided MST core calls this callback from a poll-waiting loop
-        * when waiting for MST down message replies. The driver is expected
-        * to guard against a race between this callback and the driver's HPD
-        * IRQ pulse handler.
-        */
-       void (*poll_hpd_irq)(struct drm_dp_mst_topology_mgr *mgr);
-};
-
-#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8)
-
-#define DP_PAYLOAD_LOCAL 1
-#define DP_PAYLOAD_REMOTE 2
-#define DP_PAYLOAD_DELETE_LOCAL 3
-
-struct drm_dp_payload {
-       int payload_state;
-       int start_slot;
-       int num_slots;
-       int vcpi;
-};
-
-#define to_dp_mst_topology_state(x) container_of(x, struct drm_dp_mst_topology_state, base)
-
-struct drm_dp_vcpi_allocation {
-       struct drm_dp_mst_port *port;
-       int vcpi;
-       int pbn;
-       bool dsc_enabled;
-       struct list_head next;
-};
-
-struct drm_dp_mst_topology_state {
-       struct drm_private_state base;
-       struct list_head vcpis;
-       struct drm_dp_mst_topology_mgr *mgr;
-       u8 total_avail_slots;
-       u8 start_slot;
-};
-
-#define to_dp_mst_topology_mgr(x) container_of(x, struct drm_dp_mst_topology_mgr, base)
-
-/**
- * struct drm_dp_mst_topology_mgr - DisplayPort MST manager
- *
- * This struct represents the toplevel displayport MST topology manager.
- * There should be one instance of this for every MST capable DP connector
- * on the GPU.
- */
-struct drm_dp_mst_topology_mgr {
-       /**
-        * @base: Base private object for atomic
-        */
-       struct drm_private_obj base;
-
-       /**
-        * @dev: device pointer for adding i2c devices etc.
-        */
-       struct drm_device *dev;
-       /**
-        * @cbs: callbacks for connector addition and destruction.
-        */
-       const struct drm_dp_mst_topology_cbs *cbs;
-       /**
-        * @max_dpcd_transaction_bytes: maximum number of bytes to read/write
-        * in one go.
-        */
-       int max_dpcd_transaction_bytes;
-       /**
-        * @aux: AUX channel for the DP MST connector this topolgy mgr is
-        * controlling.
-        */
-       struct drm_dp_aux *aux;
-       /**
-        * @max_payloads: maximum number of payloads the GPU can generate.
-        */
-       int max_payloads;
-       /**
-        * @max_lane_count: maximum number of lanes the GPU can drive.
-        */
-       int max_lane_count;
-       /**
-        * @max_link_rate: maximum link rate per lane GPU can output, in kHz.
-        */
-       int max_link_rate;
-       /**
-        * @conn_base_id: DRM connector ID this mgr is connected to. Only used
-        * to build the MST connector path value.
-        */
-       int conn_base_id;
-
-       /**
-        * @up_req_recv: Message receiver state for up requests.
-        */
-       struct drm_dp_sideband_msg_rx up_req_recv;
-
-       /**
-        * @down_rep_recv: Message receiver state for replies to down
-        * requests.
-        */
-       struct drm_dp_sideband_msg_rx down_rep_recv;
-
-       /**
-        * @lock: protects @mst_state, @mst_primary, @dpcd, and
-        * @payload_id_table_cleared.
-        */
-       struct mutex lock;
-
-       /**
-        * @probe_lock: Prevents @work and @up_req_work, the only writers of
-        * &drm_dp_mst_port.mstb and &drm_dp_mst_branch.ports, from racing
-        * while they update the topology.
-        */
-       struct mutex probe_lock;
-
-       /**
-        * @mst_state: If this manager is enabled for an MST capable port. False
-        * if no MST sink/branch devices is connected.
-        */
-       bool mst_state : 1;
-
-       /**
-        * @payload_id_table_cleared: Whether or not we've cleared the payload
-        * ID table for @mst_primary. Protected by @lock.
-        */
-       bool payload_id_table_cleared : 1;
-
-       /**
-        * @mst_primary: Pointer to the primary/first branch device.
-        */
-       struct drm_dp_mst_branch *mst_primary;
-
-       /**
-        * @dpcd: Cache of DPCD for primary port.
-        */
-       u8 dpcd[DP_RECEIVER_CAP_SIZE];
-       /**
-        * @sink_count: Sink count from DEVICE_SERVICE_IRQ_VECTOR_ESI0.
-        */
-       u8 sink_count;
-       /**
-        * @pbn_div: PBN to slots divisor.
-        */
-       int pbn_div;
-
-       /**
-        * @funcs: Atomic helper callbacks
-        */
-       const struct drm_private_state_funcs *funcs;
-
-       /**
-        * @qlock: protects @tx_msg_downq and &drm_dp_sideband_msg_tx.state
-        */
-       struct mutex qlock;
-
-       /**
-        * @tx_msg_downq: List of pending down requests
-        */
-       struct list_head tx_msg_downq;
-
-       /**
-        * @payload_lock: Protect payload information.
-        */
-       struct mutex payload_lock;
-       /**
-        * @proposed_vcpis: Array of pointers for the new VCPI allocation. The
-        * VCPI structure itself is &drm_dp_mst_port.vcpi, and the size of
-        * this array is determined by @max_payloads.
-        */
-       struct drm_dp_vcpi **proposed_vcpis;
-       /**
-        * @payloads: Array of payloads. The size of this array is determined
-        * by @max_payloads.
-        */
-       struct drm_dp_payload *payloads;
-       /**
-        * @payload_mask: Elements of @payloads actually in use. Since
-        * reallocation of active outputs isn't possible gaps can be created by
-        * disabling outputs out of order compared to how they've been enabled.
-        */
-       unsigned long payload_mask;
-       /**
-        * @vcpi_mask: Similar to @payload_mask, but for @proposed_vcpis.
-        */
-       unsigned long vcpi_mask;
-
-       /**
-        * @tx_waitq: Wait to queue stall for the tx worker.
-        */
-       wait_queue_head_t tx_waitq;
-       /**
-        * @work: Probe work.
-        */
-       struct work_struct work;
-       /**
-        * @tx_work: Sideband transmit worker. This can nest within the main
-        * @work worker for each transaction @work launches.
-        */
-       struct work_struct tx_work;
-
-       /**
-        * @destroy_port_list: List of to be destroyed connectors.
-        */
-       struct list_head destroy_port_list;
-       /**
-        * @destroy_branch_device_list: List of to be destroyed branch
-        * devices.
-        */
-       struct list_head destroy_branch_device_list;
-       /**
-        * @delayed_destroy_lock: Protects @destroy_port_list and
-        * @destroy_branch_device_list.
-        */
-       struct mutex delayed_destroy_lock;
-
-       /**
-        * @delayed_destroy_wq: Workqueue used for delayed_destroy_work items.
-        * A dedicated WQ makes it possible to drain any requeued work items
-        * on it.
-        */
-       struct workqueue_struct *delayed_destroy_wq;
-
-       /**
-        * @delayed_destroy_work: Work item to destroy MST port and branch
-        * devices, needed to avoid locking inversion.
-        */
-       struct work_struct delayed_destroy_work;
-
-       /**
-        * @up_req_list: List of pending up requests from the topology that
-        * need to be processed, in chronological order.
-        */
-       struct list_head up_req_list;
-       /**
-        * @up_req_lock: Protects @up_req_list
-        */
-       struct mutex up_req_lock;
-       /**
-        * @up_req_work: Work item to process up requests received from the
-        * topology. Needed to avoid blocking hotplug handling and sideband
-        * transmissions.
-        */
-       struct work_struct up_req_work;
-
-#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
-       /**
-        * @topology_ref_history_lock: protects
-        * &drm_dp_mst_port.topology_ref_history and
-        * &drm_dp_mst_branch.topology_ref_history.
-        */
-       struct mutex topology_ref_history_lock;
-#endif
-};
-
-int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
-                                struct drm_device *dev, struct drm_dp_aux *aux,
-                                int max_dpcd_transaction_bytes,
-                                int max_payloads,
-                                int max_lane_count, int max_link_rate,
-                                int conn_base_id);
-
-void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr);
-
-bool drm_dp_read_mst_cap(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
-int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state);
-
-int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled);
-
-
-int
-drm_dp_mst_detect_port(struct drm_connector *connector,
-                      struct drm_modeset_acquire_ctx *ctx,
-                      struct drm_dp_mst_topology_mgr *mgr,
-                      struct drm_dp_mst_port *port);
-
-struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port);
-
-int drm_dp_get_vc_payload_bw(const struct drm_dp_mst_topology_mgr *mgr,
-                            int link_rate, int link_lane_count);
-
-int drm_dp_calc_pbn_mode(int clock, int bpp, bool dsc);
-
-bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
-                             struct drm_dp_mst_port *port, int pbn, int slots);
-
-int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port);
-
-
-void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port);
-
-void drm_dp_mst_update_slots(struct drm_dp_mst_topology_state *mst_state, uint8_t link_encoding_cap);
-
-void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
-                               struct drm_dp_mst_port *port);
-
-
-int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr,
-                          int pbn);
-
-
-int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr, int start_slot);
-
-
-int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr);
-
-int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr);
-
-void drm_dp_mst_dump_topology(struct seq_file *m,
-                             struct drm_dp_mst_topology_mgr *mgr);
-
-void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
-int __must_check
-drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
-                              bool sync);
-
-ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
-                            unsigned int offset, void *buffer, size_t size);
-ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux,
-                             unsigned int offset, void *buffer, size_t size);
-
-int drm_dp_mst_connector_late_register(struct drm_connector *connector,
-                                      struct drm_dp_mst_port *port);
-void drm_dp_mst_connector_early_unregister(struct drm_connector *connector,
-                                          struct drm_dp_mst_port *port);
-
-struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
-                                                                   struct drm_dp_mst_topology_mgr *mgr);
-int __must_check
-drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
-                             struct drm_dp_mst_topology_mgr *mgr,
-                             struct drm_dp_mst_port *port, int pbn,
-                             int pbn_div);
-int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state,
-                                struct drm_dp_mst_port *port,
-                                int pbn, int pbn_div,
-                                bool enable);
-int __must_check
-drm_dp_mst_add_affected_dsc_crtcs(struct drm_atomic_state *state,
-                                 struct drm_dp_mst_topology_mgr *mgr);
-int __must_check
-drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
-                                struct drm_dp_mst_topology_mgr *mgr,
-                                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);
-void drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port);
-
-struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port);
-
-extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs;
-
-/**
- * __drm_dp_mst_state_iter_get - private atomic state iterator function for
- * macro-internal use
- * @state: &struct drm_atomic_state pointer
- * @mgr: pointer to the &struct drm_dp_mst_topology_mgr iteration cursor
- * @old_state: optional pointer to the old &struct drm_dp_mst_topology_state
- * iteration cursor
- * @new_state: optional pointer to the new &struct drm_dp_mst_topology_state
- * iteration cursor
- * @i: int iteration cursor, for macro-internal use
- *
- * Used by for_each_oldnew_mst_mgr_in_state(),
- * for_each_old_mst_mgr_in_state(), and for_each_new_mst_mgr_in_state(). Don't
- * call this directly.
- *
- * Returns:
- * True if the current &struct drm_private_obj is a &struct
- * drm_dp_mst_topology_mgr, false otherwise.
- */
-static inline bool
-__drm_dp_mst_state_iter_get(struct drm_atomic_state *state,
-                           struct drm_dp_mst_topology_mgr **mgr,
-                           struct drm_dp_mst_topology_state **old_state,
-                           struct drm_dp_mst_topology_state **new_state,
-                           int i)
-{
-       struct __drm_private_objs_state *objs_state = &state->private_objs[i];
-
-       if (objs_state->ptr->funcs != &drm_dp_mst_topology_state_funcs)
-               return false;
-
-       *mgr = to_dp_mst_topology_mgr(objs_state->ptr);
-       if (old_state)
-               *old_state = to_dp_mst_topology_state(objs_state->old_state);
-       if (new_state)
-               *new_state = to_dp_mst_topology_state(objs_state->new_state);
-
-       return true;
-}
-
-/**
- * for_each_oldnew_mst_mgr_in_state - iterate over all DP MST topology
- * managers in an atomic update
- * @__state: &struct drm_atomic_state pointer
- * @mgr: &struct drm_dp_mst_topology_mgr iteration cursor
- * @old_state: &struct drm_dp_mst_topology_state iteration cursor for the old
- * state
- * @new_state: &struct drm_dp_mst_topology_state iteration cursor for the new
- * state
- * @__i: int iteration cursor, for macro-internal use
- *
- * This iterates over all DRM DP MST topology managers in an atomic update,
- * tracking both old and new state. This is useful in places where the state
- * delta needs to be considered, for example in atomic check functions.
- */
-#define for_each_oldnew_mst_mgr_in_state(__state, mgr, old_state, new_state, __i) \
-       for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
-               for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), &(new_state), (__i)))
-
-/**
- * for_each_old_mst_mgr_in_state - iterate over all DP MST topology managers
- * in an atomic update
- * @__state: &struct drm_atomic_state pointer
- * @mgr: &struct drm_dp_mst_topology_mgr iteration cursor
- * @old_state: &struct drm_dp_mst_topology_state iteration cursor for the old
- * state
- * @__i: int iteration cursor, for macro-internal use
- *
- * This iterates over all DRM DP MST topology managers in an atomic update,
- * tracking only the old state. This is useful in disable functions, where we
- * need the old state the hardware is still in.
- */
-#define for_each_old_mst_mgr_in_state(__state, mgr, old_state, __i) \
-       for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
-               for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), NULL, (__i)))
-
-/**
- * for_each_new_mst_mgr_in_state - iterate over all DP MST topology managers
- * in an atomic update
- * @__state: &struct drm_atomic_state pointer
- * @mgr: &struct drm_dp_mst_topology_mgr iteration cursor
- * @new_state: &struct drm_dp_mst_topology_state iteration cursor for the new
- * state
- * @__i: int iteration cursor, for macro-internal use
- *
- * This iterates over all DRM DP MST topology managers in an atomic update,
- * tracking only the new state. This is useful in enable functions, where we
- * need the new state the hardware should be in when the atomic commit
- * operation has completed.
- */
-#define for_each_new_mst_mgr_in_state(__state, mgr, new_state, __i) \
-       for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
-               for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), NULL, &(new_state), (__i)))
-
-#endif
index cf43561..ca022e9 100644 (file)
@@ -8,7 +8,7 @@
 #ifndef DRM_DSC_H_
 #define DRM_DSC_H_
 
-#include <drm/drm_dp_helper.h>
+#include <drm/dp/drm_dp_helper.h>
 
 /* VESA Display Stream Compression DSC 1.2 constants */
 #define DSC_NUM_BUF_RANGES                     15
index 18f6c70..144c495 100644 (file)
@@ -401,8 +401,8 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
                                            const struct drm_display_mode *mode);
 
 void
-drm_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
-                                 const struct drm_connector_state *conn_state);
+drm_hdmi_avi_infoframe_colorimetry(struct hdmi_avi_infoframe *frame,
+                                  const struct drm_connector_state *conn_state);
 
 void
 drm_hdmi_avi_infoframe_bars(struct hdmi_avi_infoframe *frame,
index 05e1949..6fe13cc 100644 (file)
@@ -194,7 +194,7 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
 #ifdef CONFIG_DEBUG_FS
 void mipi_dbi_debugfs_init(struct drm_minor *minor);
 #else
-#define mipi_dbi_debugfs_init          NULL
+static inline void mipi_dbi_debugfs_init(struct drm_minor *minor) {}
 #endif
 
 #endif /* __LINUX_MIPI_DBI_H */
index b84693f..ec4f543 100644 (file)
@@ -34,6 +34,7 @@ struct drm_modeset_lock;
  * struct drm_modeset_acquire_ctx - locking context (see ww_acquire_ctx)
  * @ww_ctx: base acquire ctx
  * @contended: used internally for -EDEADLK handling
+ * @stack_depot: used internally for contention debugging
  * @locked: list of held locks
  * @trylock_only: trylock mode used in atomic contexts/panic notifiers
  * @interruptible: whether interruptible locking should be used.
diff --git a/include/drm/drm_module.h b/include/drm/drm_module.h
new file mode 100644 (file)
index 0000000..4db1ae0
--- /dev/null
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef DRM_MODULE_H
+#define DRM_MODULE_H
+
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_drv.h>
+
+/**
+ * DOC: overview
+ *
+ * This library provides helpers registering DRM drivers during module
+ * initialization and shutdown. The provided helpers act like bus-specific
+ * module helpers, such as module_pci_driver(), but respect additional
+ * parameters that control DRM driver registration.
+ *
+ * Below is an example of initializing a DRM driver for a device on the
+ * PCI bus.
+ *
+ * .. code-block:: c
+ *
+ *     struct pci_driver my_pci_drv = {
+ *     };
+ *
+ *     drm_module_pci_driver(my_pci_drv);
+ *
+ * The generated code will test if DRM drivers are enabled and register
+ * the PCI driver my_pci_drv. For more complex module initialization, you
+ * can still use module_init() and module_exit() in your driver.
+ */
+
+/*
+ * PCI drivers
+ */
+
+static inline int __init drm_pci_register_driver(struct pci_driver *pci_drv)
+{
+       if (drm_firmware_drivers_only())
+               return -ENODEV;
+
+       return pci_register_driver(pci_drv);
+}
+
+/**
+ * drm_module_pci_driver - Register a DRM driver for PCI-based devices
+ * @__pci_drv: the PCI driver structure
+ *
+ * Registers a DRM driver for devices on the PCI bus. The helper
+ * macro behaves like module_pci_driver() but tests the state of
+ * drm_firmware_drivers_only(). For more complex module initialization,
+ * use module_init() and module_exit() directly.
+ *
+ * Each module may only use this macro once. Calling it replaces
+ * module_init() and module_exit().
+ */
+#define drm_module_pci_driver(__pci_drv) \
+       module_driver(__pci_drv, drm_pci_register_driver, pci_unregister_driver)
+
+static inline int __init
+drm_pci_register_driver_if_modeset(struct pci_driver *pci_drv, int modeset)
+{
+       if (drm_firmware_drivers_only() && modeset == -1)
+               return -ENODEV;
+       if (modeset == 0)
+               return -ENODEV;
+
+       return pci_register_driver(pci_drv);
+}
+
+static inline void __exit
+drm_pci_unregister_driver_if_modeset(struct pci_driver *pci_drv, int modeset)
+{
+       pci_unregister_driver(pci_drv);
+}
+
+/**
+ * drm_module_pci_driver_if_modeset - Register a DRM driver for PCI-based devices
+ * @__pci_drv: the PCI driver structure
+ * @__modeset: an additional parameter that disables the driver
+ *
+ * This macro is deprecated and only provided for existing drivers. For
+ * new drivers, use drm_module_pci_driver().
+ *
+ * Registers a DRM driver for devices on the PCI bus. The helper macro
+ * behaves like drm_module_pci_driver() with an additional driver-specific
+ * flag. If __modeset is 0, the driver has been disabled, if __modeset is
+ * -1 the driver state depends on the global DRM state. For all other
+ * values, the PCI driver has been enabled. The default should be -1.
+ */
+#define drm_module_pci_driver_if_modeset(__pci_drv, __modeset) \
+       module_driver(__pci_drv, drm_pci_register_driver_if_modeset, \
+                     drm_pci_unregister_driver_if_modeset, __modeset)
+
+/*
+ * Platform drivers
+ */
+
+static inline int __init
+drm_platform_driver_register(struct platform_driver *platform_drv)
+{
+       if (drm_firmware_drivers_only())
+               return -ENODEV;
+
+       return platform_driver_register(platform_drv);
+}
+
+/**
+ * drm_module_platform_driver - Register a DRM driver for platform devices
+ * @__platform_drv: the platform driver structure
+ *
+ * Registers a DRM driver for devices on the platform bus. The helper
+ * macro behaves like module_platform_driver() but tests the state of
+ * drm_firmware_drivers_only(). For more complex module initialization,
+ * use module_init() and module_exit() directly.
+ *
+ * Each module may only use this macro once. Calling it replaces
+ * module_init() and module_exit().
+ */
+#define drm_module_platform_driver(__platform_drv) \
+       module_driver(__platform_drv, drm_platform_driver_register, \
+                     platform_driver_unregister)
+
+#endif
index 0c1102d..06759ba 100644 (file)
@@ -516,7 +516,7 @@ struct drm_plane_funcs {
         * This optional hook is used for the DRM to determine if the given
         * format/modifier combination is valid for the plane. This allows the
         * DRM to generate the correct format bitmask (which formats apply to
-        * which modifier), and to valdiate modifiers at atomic_check time.
+        * which modifier), and to validate modifiers at atomic_check time.
         *
         * If not present, then any modifier in the plane's modifier
         * list is allowed with any of the plane's formats.
index 24591b6..4ef246d 100644 (file)
@@ -73,10 +73,21 @@ struct drm_privacy_screen {
         * for more info.
         */
        enum drm_privacy_screen_status hw_state;
+       /**
+        * @drvdata: Private data owned by the privacy screen provider
+        */
+       void *drvdata;
 };
 
+static inline
+void *drm_privacy_screen_get_drvdata(struct drm_privacy_screen *priv)
+{
+       return priv->drvdata;
+}
+
 struct drm_privacy_screen *drm_privacy_screen_register(
-       struct device *parent, const struct drm_privacy_screen_ops *ops);
+       struct device *parent, const struct drm_privacy_screen_ops *ops,
+       void *data);
 void drm_privacy_screen_unregister(struct drm_privacy_screen *priv);
 
 void drm_privacy_screen_call_notifier_chain(struct drm_privacy_screen *priv);
index 5952051..69eea9d 100644 (file)
@@ -105,11 +105,11 @@ struct ttm_resource_manager_func {
  * @use_type: The memory type is enabled.
  * @use_tt: If a TT object should be used for the backing store.
  * @size: Size of the managed region.
+ * @bdev: ttm device this manager belongs to
  * @func: structure pointer implementing the range manager. See above
  * @move_lock: lock for move fence
- * static information. bdev::driver::io_mem_free is never used.
- * @lru: The lru list for this memory type.
  * @move: The fence of the last pipelined move operation.
+ * @lru: The lru list for this memory type.
  *
  * This structure is used to identify and manage memory types for a device.
  */
@@ -119,20 +119,21 @@ struct ttm_resource_manager {
         */
        bool use_type;
        bool use_tt;
+       struct ttm_device *bdev;
        uint64_t size;
        const struct ttm_resource_manager_func *func;
        spinlock_t move_lock;
 
        /*
-        * Protected by the global->lru_lock.
+        * Protected by @move_lock.
         */
-
-       struct list_head lru[TTM_MAX_BO_PRIORITY];
+       struct dma_fence *move;
 
        /*
-        * Protected by @move_lock.
+        * Protected by the global->lru_lock.
         */
-       struct dma_fence *move;
+
+       struct list_head lru[TTM_MAX_BO_PRIORITY];
 };
 
 /**
@@ -160,6 +161,7 @@ struct ttm_bus_placement {
  * @mem_type: Resource type of the allocation.
  * @placement: Placement flags.
  * @bus: Placement on io bus accessible to the CPU
+ * @bo: weak reference to the BO, protected by ttm_device::lru_lock
  *
  * Structure indicating the placement and space resources used by a
  * buffer object.
@@ -170,6 +172,7 @@ struct ttm_resource {
        uint32_t mem_type;
        uint32_t placement;
        struct ttm_bus_placement bus;
+       struct ttm_buffer_object *bo;
 };
 
 /**
@@ -261,14 +264,20 @@ ttm_resource_manager_cleanup(struct ttm_resource_manager *man)
 void ttm_resource_init(struct ttm_buffer_object *bo,
                        const struct ttm_place *place,
                        struct ttm_resource *res);
+void ttm_resource_fini(struct ttm_resource_manager *man,
+                      struct ttm_resource *res);
+
 int ttm_resource_alloc(struct ttm_buffer_object *bo,
                       const struct ttm_place *place,
                       struct ttm_resource **res);
 void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res);
 bool ttm_resource_compat(struct ttm_resource *res,
                         struct ttm_placement *placement);
+void ttm_resource_set_bo(struct ttm_resource *res,
+                        struct ttm_buffer_object *bo);
 
 void ttm_resource_manager_init(struct ttm_resource_manager *man,
+                              struct ttm_device *bdev,
                               unsigned long p_size);
 
 int ttm_resource_manager_evict_all(struct ttm_device *bdev,
index 278d489..19fa0b5 100644 (file)
  *
  *     struct dma_buf_map map = DMA_BUF_MAP_INIT_VADDR(0xdeadbeaf);
  *
- *     dma_buf_map_set_vaddr(&map. 0xdeadbeaf);
+ *     dma_buf_map_set_vaddr(&map, 0xdeadbeaf);
  *
  * To set an address in I/O memory, use dma_buf_map_set_vaddr_iomem().
  *
  * .. code-block:: c
  *
- *     dma_buf_map_set_vaddr_iomem(&map. 0xdeadbeaf);
+ *     dma_buf_map_set_vaddr_iomem(&map, 0xdeadbeaf);
  *
  * Instances of struct dma_buf_map do not have to be cleaned up, but
  * can be cleared to NULL with dma_buf_map_clear(). Cleared mappings
index eebf043..a715df9 100644 (file)
@@ -458,8 +458,8 @@ void dma_resv_fini(struct dma_resv *obj);
 int dma_resv_reserve_shared(struct dma_resv *obj, unsigned int num_fences);
 void dma_resv_add_shared_fence(struct dma_resv *obj, struct dma_fence *fence);
 void dma_resv_add_excl_fence(struct dma_resv *obj, struct dma_fence *fence);
-int dma_resv_get_fences(struct dma_resv *obj, struct dma_fence **pfence_excl,
-                       unsigned *pshared_count, struct dma_fence ***pshared);
+int dma_resv_get_fences(struct dma_resv *obj, bool write,
+                       unsigned int *num_fences, struct dma_fence ***fences);
 int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src);
 long dma_resv_wait_timeout(struct dma_resv *obj, bool wait_all, bool intr,
                           unsigned long timeout);
index 3da9584..9a14f3f 100644 (file)
@@ -502,6 +502,7 @@ struct fb_info {
        } *apertures;
 
        bool skip_vt_switch; /* no VT switch on suspend/resume required */
+       bool forced_out; /* set when being removed by another driver */
 };
 
 static inline struct apertures_struct *alloc_apertures(unsigned int max_num) {
index f934876..efa5c32 100644 (file)
@@ -230,7 +230,7 @@ extern void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *
 do {                                                           \
        typecheck(struct lockdep_map *, &(nest_lock)->dep_map); \
        _down_write_nest_lock(sem, &(nest_lock)->dep_map);      \
-} while (0);
+} while (0)
 
 /*
  * Take/release a lock when not the owner will release it.
index 73ad784..811ea66 100644 (file)
@@ -91,6 +91,7 @@ enum rpi_firmware_property_tag {
        RPI_FIRMWARE_GET_POE_HAT_VAL =                        0x00030049,
        RPI_FIRMWARE_SET_POE_HAT_VAL =                        0x00030050,
        RPI_FIRMWARE_NOTIFY_XHCI_RESET =                      0x00030058,
+       RPI_FIRMWARE_NOTIFY_DISPLAY_DONE =                    0x00030066,
 
        /* Dispmanx TAGS */
        RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE =                   0x00040001,
index 061e700..9e40277 100644 (file)
@@ -84,14 +84,14 @@ struct drm_panfrost_wait_bo {
        __s64 timeout_ns;       /* absolute */
 };
 
+/* Valid flags to pass to drm_panfrost_create_bo */
 #define PANFROST_BO_NOEXEC     1
 #define PANFROST_BO_HEAP       2
 
 /**
  * struct drm_panfrost_create_bo - ioctl argument for creating Panfrost BOs.
  *
- * There are currently no values for the flags argument, but it may be
- * used in a future extension.
+ * The flags argument is a bit mask of PANFROST_BO_* flags.
  */
 struct drm_panfrost_create_bo {
        __u32 size;