Quellcode durchsuchen

Add 'qcom/opensource/display-drivers/' from commit '5ff96e683134b356ebe6c37069b4197034502ef9'

git-subtree-dir: qcom/opensource/display-drivers
git-subtree-mainline: 2d61911ef6d5122dcb93223e1434a9d2df14a655
git-subtree-split: 5ff96e683134b356ebe6c37069b4197034502ef9
Change-Id:
repo: https://git.codelinaro.org/clo/la/platform/vendor/opensource/display-drivers
tag: DISPLAY.LA.4.0.r2-07600-lanai.0
David Wronek vor 5 Monaten
Ursprung
Commit
1841c0f616
100 geänderte Dateien mit 27827 neuen und 0 gelöschten Zeilen
  1. 40 0
      qcom/opensource/display-drivers/Android.bp
  2. 12 0
      qcom/opensource/display-drivers/Android.mk
  3. 72 0
      qcom/opensource/display-drivers/BUILD.bazel
  4. 58 0
      qcom/opensource/display-drivers/Documentation/devicetree/bindings/drm/msm/sde-dp-sim.txt
  5. 12 0
      qcom/opensource/display-drivers/Kbuild
  6. 16 0
      qcom/opensource/display-drivers/Makefile
  7. 24 0
      qcom/opensource/display-drivers/Makefile.am
  8. 93 0
      qcom/opensource/display-drivers/NOTICE
  9. 1030 0
      qcom/opensource/display-drivers/bridge-drivers/lt9611uxc.c
  10. 18 0
      qcom/opensource/display-drivers/config/augen3disp.conf
  11. 22 0
      qcom/opensource/display-drivers/config/augen3dispconf.h
  12. 16 0
      qcom/opensource/display-drivers/config/bengaldisp.conf
  13. 15 0
      qcom/opensource/display-drivers/config/bengaldispconf.h
  14. 18 0
      qcom/opensource/display-drivers/config/gki_holidisp.conf
  15. 17 0
      qcom/opensource/display-drivers/config/gki_holidispconf.h
  16. 18 0
      qcom/opensource/display-drivers/config/gki_kalamadisp.conf
  17. 26 0
      qcom/opensource/display-drivers/config/gki_kalamadispconf.h
  18. 10 0
      qcom/opensource/display-drivers/config/gki_kalamadisptui.conf
  19. 17 0
      qcom/opensource/display-drivers/config/gki_kalamadisptuiconf.h
  20. 12 0
      qcom/opensource/display-drivers/config/gki_lahainadisp.conf
  21. 19 0
      qcom/opensource/display-drivers/config/gki_lahainadispconf.h
  22. 11 0
      qcom/opensource/display-drivers/config/gki_neodisp.conf
  23. 14 0
      qcom/opensource/display-drivers/config/gki_neodispconf.h
  24. 16 0
      qcom/opensource/display-drivers/config/gki_niobedisp.conf
  25. 22 0
      qcom/opensource/display-drivers/config/gki_niobedispconf.h
  26. 11 0
      qcom/opensource/display-drivers/config/gki_parrotdisp.conf
  27. 16 0
      qcom/opensource/display-drivers/config/gki_parrotdispconf.h
  28. 18 0
      qcom/opensource/display-drivers/config/gki_pineappledisp.conf
  29. 26 0
      qcom/opensource/display-drivers/config/gki_pineappledispconf.h
  30. 10 0
      qcom/opensource/display-drivers/config/gki_pineappledisptui.conf
  31. 17 0
      qcom/opensource/display-drivers/config/gki_pineappledisptuiconf.h
  32. 11 0
      qcom/opensource/display-drivers/config/gki_pittidisp.conf
  33. 17 0
      qcom/opensource/display-drivers/config/gki_pittidispconf.h
  34. 15 0
      qcom/opensource/display-drivers/config/gki_waipiodisp.conf
  35. 22 0
      qcom/opensource/display-drivers/config/gki_waipiodispconf.h
  36. 10 0
      qcom/opensource/display-drivers/config/gki_waipiodisptui.conf
  37. 16 0
      qcom/opensource/display-drivers/config/gki_waipiodisptuiconf.h
  38. 17 0
      qcom/opensource/display-drivers/config/holidisp.conf
  39. 15 0
      qcom/opensource/display-drivers/config/holidispconf.h
  40. 13 0
      qcom/opensource/display-drivers/config/konadisp.conf
  41. 20 0
      qcom/opensource/display-drivers/config/konadispconf.h
  42. 13 0
      qcom/opensource/display-drivers/config/lahainadisp.conf
  43. 18 0
      qcom/opensource/display-drivers/config/lahainadispconf.h
  44. 13 0
      qcom/opensource/display-drivers/config/saipdisp.conf
  45. 19 0
      qcom/opensource/display-drivers/config/saipdispconf.h
  46. 15 0
      qcom/opensource/display-drivers/display_driver_board.mk
  47. 124 0
      qcom/opensource/display-drivers/display_driver_build.bzl
  48. 14 0
      qcom/opensource/display-drivers/display_driver_product.mk
  49. 93 0
      qcom/opensource/display-drivers/display_kernel_headers.py
  50. 195 0
      qcom/opensource/display-drivers/display_modules.bzl
  51. 342 0
      qcom/opensource/display-drivers/hdcp/msm_hdcp.c
  52. 2 0
      qcom/opensource/display-drivers/include/Kbuild
  53. 6 0
      qcom/opensource/display-drivers/include/linux/Kbuild
  54. 33 0
      qcom/opensource/display-drivers/include/linux/msm_hdcp.h
  55. 136 0
      qcom/opensource/display-drivers/include/linux/sde_io_util.h
  56. 360 0
      qcom/opensource/display-drivers/include/linux/sde_rsc.h
  57. 97 0
      qcom/opensource/display-drivers/include/linux/sde_vm_event.h
  58. 6 0
      qcom/opensource/display-drivers/include/uapi/Kbuild
  59. 5 0
      qcom/opensource/display-drivers/include/uapi/display/Kbuild
  60. 5 0
      qcom/opensource/display-drivers/include/uapi/display/drm/Kbuild
  61. 817 0
      qcom/opensource/display-drivers/include/uapi/display/drm/msm_drm_pp.h
  62. 974 0
      qcom/opensource/display-drivers/include/uapi/display/drm/sde_drm.h
  63. 3 0
      qcom/opensource/display-drivers/include/uapi/display/hdcp/Kbuild
  64. 61 0
      qcom/opensource/display-drivers/include/uapi/display/hdcp/msm_hdmi_hdcp_mgr.h
  65. 4 0
      qcom/opensource/display-drivers/include/uapi/display/media/Kbuild
  66. 1472 0
      qcom/opensource/display-drivers/include/uapi/display/media/mmm_color_fmt.h
  67. 180 0
      qcom/opensource/display-drivers/include/uapi/display/media/msm_sde_rotator.h
  68. 75 0
      qcom/opensource/display-drivers/msm/Android.mk
  69. 267 0
      qcom/opensource/display-drivers/msm/Kbuild
  70. 16 0
      qcom/opensource/display-drivers/msm/Makefile
  71. 325 0
      qcom/opensource/display-drivers/msm/dp/dp_altmode.c
  72. 22 0
      qcom/opensource/display-drivers/msm/dp/dp_altmode.h
  73. 915 0
      qcom/opensource/display-drivers/msm/dp/dp_audio.c
  74. 76 0
      qcom/opensource/display-drivers/msm/dp/dp_audio.h
  75. 958 0
      qcom/opensource/display-drivers/msm/dp/dp_aux.c
  76. 77 0
      qcom/opensource/display-drivers/msm/dp/dp_aux.h
  77. 71 0
      qcom/opensource/display-drivers/msm/dp/dp_aux_bridge.c
  78. 135 0
      qcom/opensource/display-drivers/msm/dp/dp_aux_bridge.h
  79. 208 0
      qcom/opensource/display-drivers/msm/dp/dp_bridge_hpd.c
  80. 42 0
      qcom/opensource/display-drivers/msm/dp/dp_bridge_hpd.h
  81. 3130 0
      qcom/opensource/display-drivers/msm/dp/dp_catalog.c
  82. 376 0
      qcom/opensource/display-drivers/msm/dp/dp_catalog.h
  83. 272 0
      qcom/opensource/display-drivers/msm/dp/dp_catalog_v200.c
  84. 346 0
      qcom/opensource/display-drivers/msm/dp/dp_catalog_v420.c
  85. 1617 0
      qcom/opensource/display-drivers/msm/dp/dp_ctrl.c
  86. 54 0
      qcom/opensource/display-drivers/msm/dp/dp_ctrl.h
  87. 2598 0
      qcom/opensource/display-drivers/msm/dp/dp_debug.c
  88. 175 0
      qcom/opensource/display-drivers/msm/dp/dp_debug.h
  89. 3993 0
      qcom/opensource/display-drivers/msm/dp/dp_display.c
  90. 144 0
      qcom/opensource/display-drivers/msm/dp/dp_display.h
  91. 794 0
      qcom/opensource/display-drivers/msm/dp/dp_drm.c
  92. 275 0
      qcom/opensource/display-drivers/msm/dp/dp_drm.h
  93. 298 0
      qcom/opensource/display-drivers/msm/dp/dp_gpio_hpd.c
  94. 32 0
      qcom/opensource/display-drivers/msm/dp/dp_gpio_hpd.h
  95. 1039 0
      qcom/opensource/display-drivers/msm/dp/dp_hdcp2p2.c
  96. 123 0
      qcom/opensource/display-drivers/msm/dp/dp_hpd.c
  97. 122 0
      qcom/opensource/display-drivers/msm/dp/dp_hpd.h
  98. 1691 0
      qcom/opensource/display-drivers/msm/dp/dp_link.c
  99. 248 0
      qcom/opensource/display-drivers/msm/dp/dp_link.h
  100. 424 0
      qcom/opensource/display-drivers/msm/dp/dp_lphw_hpd.c

+ 40 - 0
qcom/opensource/display-drivers/Android.bp

@@ -0,0 +1,40 @@
+headers_src = [
+    "include/uapi/*/**/*.h",
+]
+
+display_headers_out = [
+    "display/drm/msm_drm_pp.h",
+    "display/drm/sde_drm.h",
+    "display/hdcp/msm_hdmi_hdcp_mgr.h",
+    "display/media/mmm_color_fmt.h",
+    "display/media/msm_sde_rotator.h",
+]
+
+display_kernel_headers_verbose = "--verbose "
+genrule {
+    name: "qti_generate_display_kernel_headers",
+    tools: [
+        "headers_install.sh",
+        "unifdef"
+    ],
+    tool_files: [
+        "display_kernel_headers.py",
+    ],
+    srcs: headers_src,
+    cmd: "python3 $(location display_kernel_headers.py) " +
+          display_kernel_headers_verbose +
+         "--header_arch arm64 " +
+         "--gen_dir $(genDir) " +
+         "--display_include_uapi $(locations include/uapi/*/**/*.h) " +
+         "--unifdef $(location unifdef) " +
+         "--headers_install $(location headers_install.sh)",
+    out: display_headers_out,
+}
+
+cc_library_headers {
+    name: "qti_display_kernel_headers",
+    generated_headers: ["qti_generate_display_kernel_headers"],
+    export_generated_headers: ["qti_generate_display_kernel_headers"],
+    vendor: true,
+    recovery_available: true
+}

+ 12 - 0
qcom/opensource/display-drivers/Android.mk

@@ -0,0 +1,12 @@
+# Android makefile for display kernel modules
+DISPLAY_DLKM_ENABLE := true
+ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true)
+	ifeq ($(TARGET_KERNEL_DLKM_DISPLAY_OVERRIDE), false)
+		DISPLAY_DLKM_ENABLE := false
+	endif
+endif
+
+ifeq ($(DISPLAY_DLKM_ENABLE),  true)
+	LOCAL_PATH := $(call my-dir)
+	include $(LOCAL_PATH)/msm/Android.mk
+endif

+ 72 - 0
qcom/opensource/display-drivers/BUILD.bazel

@@ -0,0 +1,72 @@
+load("//build/kernel/kleaf:kernel.bzl", "ddk_headers")
+
+package(
+    default_visibility = [
+      "//visibility:public"],
+)
+
+ddk_headers(
+    name = "linux_includes",
+    hdrs = glob([
+      "include/linux/*.h",
+    ]),
+    includes = ["include"]
+)
+
+ddk_headers(
+    name = "uapi_headers",
+    hdrs = glob([
+      "include/uapi/display/drm/*.h",
+      "include/uapi/display/hdcp/*.h",
+      "include/uapi/display/media/*.h",
+    ]),
+    includes = ["include/uapi/display"]
+)
+
+ddk_headers(
+    name = "dp_headers",
+    hdrs = glob([
+      "msm/dp/*.h",
+    ]),
+    includes = ["msm/dp"]
+)
+
+ddk_headers(
+    name = "dsi_headers",
+    hdrs = glob([
+      "msm/dsi/*.h",
+    ]),
+    includes = ["msm/dsi"]
+)
+
+ddk_headers(
+    name = "sde_headers",
+    hdrs = glob([
+      "msm/sde/*.h",
+    ]),
+    includes = ["msm/sde"]
+)
+
+ddk_headers(
+    name = "rotator_headers",
+    hdrs = glob([
+      "rotator/*.h",
+    ]),
+    includes = ["rotator"]
+)
+
+ddk_headers(
+    name = "msm_headers",
+    hdrs = glob([
+      "msm/*.h",
+    ]),
+    includes = ["msm"]
+)
+
+ddk_headers(
+    name = "display_drivers_headers",
+    hdrs = [ ":linux_includes", ":uapi_headers", ":msm_headers",":dp_headers",":dsi_headers",":sde_headers",":rotator_headers"]
+)
+
+load(":target.bzl", "define_display_target")
+define_display_target()

+ 58 - 0
qcom/opensource/display-drivers/Documentation/devicetree/bindings/drm/msm/sde-dp-sim.txt

@@ -0,0 +1,58 @@
+QTI Snapdragon Display Engine (SDE) DP-MST sideband message emulation driver
+
+Required properties:
+- compatible:		"qcom,dp-mst-sim"
+
+Each child node represents a port at root branch, with properties:
+- qcom,mode-h-active:      A u32 property defines the horizontal active size.
+- qcom,mode-h-front-porch: A u32 property defines the horizontal front porch.
+- qcom,mode-h-pulse-width: A u32 property defines the horizontal pulse.
+- qcom,mode-h-back-porch:  A u32 property defines the horizontal back porch.
+- qcom,mode-h-active-high: A boolean property if horizontal polarity is high.
+- qcom,mode-v-active:      A u32 property defines the vertical active size.
+- qcom,mode-v-front-porch: A u32 property defines the vertical front portch.
+- qcom,mode-v-pulse-width: A u32 property defines the vertical pulse width.
+- qcom,mode-v-back-porch:  A u32 property defines the vertical back porch.
+- qcom,mode-v-active-high: A boolean property if vertical polarity is high.
+- qcom,mode-refresh-rate:  A u32 property defines vertial refresh rate.
+- qcom,mode-clock-in-khz:  A u32 property defines clock in kHz.
+
+Example:
+
+/ {
+	...
+
+	sde_dp_mst_sim: qcom,dp-mst-sim {
+		compatible = "qcom,dp-mst-sim";
+
+		port@0 {
+			qcom,mode-h-active = <1920>;
+			qcom,mode-h-front-porch = <88>;
+			qcom,mode-h-pulse-width = <44>;
+			qcom,mode-h-back-porch = <148>;
+			qcom,mode-h-active-high;
+			qcom,mode-v-active = <1080>;
+			qcom,mode-v-front-porch = <4>;
+			qcom,mode-v-pulse-width = <5>;
+			qcom,mode-v-back-porch = <36>;
+			qcom,mode-v-active-high;
+			qcom,mode-refresh-rate = <60>;
+			qcom,mode-clock-in-khz = <148500>;
+		};
+
+		port@1 {
+			qcom,mode-h-active = <1920>;
+			qcom,mode-h-front-porch = <88>;
+			qcom,mode-h-pulse-width = <44>;
+			qcom,mode-h-back-porch = <148>;
+			qcom,mode-h-active-high;
+			qcom,mode-v-active = <1080>;
+			qcom,mode-v-front-porch = <4>;
+			qcom,mode-v-pulse-width = <5>;
+			qcom,mode-v-back-porch = <36>;
+			qcom,mode-v-active-high;
+			qcom,mode-refresh-rate = <60>;
+			qcom,mode-clock-in-khz = <148500>;
+		};
+	};
+};

+ 12 - 0
qcom/opensource/display-drivers/Kbuild

@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+ifeq ($(DISPLAY_ROOT),)
+DISPLAY_ROOT=$(srctree)/techpack/display
+endif
+
+LINUXINCLUDE    += \
+		   -I$(DISPLAY_ROOT)/include/uapi/display \
+		   -I$(DISPLAY_ROOT)/include
+USERINCLUDE     += -I$(DISPLAY_ROOT)/include/uapi/display
+
+obj-$(CONFIG_DRM_MSM) += msm/

+ 16 - 0
qcom/opensource/display-drivers/Makefile

@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+KBUILD_OPTIONS+= DISPLAY_ROOT=$(KERNEL_SRC)/$(M)
+
+all:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
+
+modules_install:
+	$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install
+
+%:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS)
+
+clean:
+	rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers
+	rm -rf .tmp_versions

+ 24 - 0
qcom/opensource/display-drivers/Makefile.am

@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+DISPLAY_ROOT=$(ROOTDIR)display/vendor/qcom/opensource/display-drivers
+CONFIG_DRM_MSM=$(MODULE_DRM_MSM)
+KBUILD_OPTIONS := DISPLAY_ROOT=$(DISPLAY_ROOT) CONFIG_DRM_MSM=$(CONFIG_DRM_MSM)
+
+ifeq ($(TARGET_SUPPORT),genericarmv8)
+	KBUILD_OPTIONS += CONFIG_ARCH_PINEAPPLE=y
+endif
+
+obj-m += msm/
+
+all:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
+
+modules_install:
+	$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install
+
+%:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS)
+
+clean:
+	rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers
+	rm -rf .tmp_versions

+ 93 - 0
qcom/opensource/display-drivers/NOTICE

@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+*/
+
+/*
+ * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2013 Red Hat
+ * Copyright (C) 2014 Red Hat
+ * Copyright (C) 2016 Red Hat
+ * Author: Rob Clark <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+ * Copyright (c) 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.
+ */
+
+
+/*
+ * Copyright (c) 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.
+ */
+
+
+/*
+ * Copyright © 2014 Red Hatt.
+ *
+ * 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.
+ */

+ 1030 - 0
qcom/opensource/display-drivers/bridge-drivers/lt9611uxc.c

@@ -0,0 +1,1030 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2020. Linaro Limited.
+ */
+
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include <sound/hdmi-codec.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#define EDID_BLOCK_SIZE	128
+#define EDID_NUM_BLOCKS	2
+
+struct lt9611uxc {
+	struct device *dev;
+	struct drm_bridge bridge;
+	struct drm_connector connector;
+
+	struct regmap *regmap;
+	/* Protects all accesses to registers by stopping the on-chip MCU */
+	struct mutex ocm_lock;
+
+	struct wait_queue_head wq;
+	struct work_struct work;
+
+	struct device_node *dsi0_node;
+	struct device_node *dsi1_node;
+	struct mipi_dsi_device *dsi0;
+	struct mipi_dsi_device *dsi1;
+	struct platform_device *audio_pdev;
+
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+
+	struct regulator_bulk_data supplies[2];
+
+	struct i2c_client *client;
+
+	bool hpd_supported;
+	bool edid_read;
+	/* can be accessed from different threads, so protect this with ocm_lock */
+	bool hdmi_connected;
+	uint8_t fw_version;
+};
+
+#define LT9611_PAGE_CONTROL	0xff
+
+static const struct regmap_range_cfg lt9611uxc_ranges[] = {
+	{
+		.name = "register_range",
+		.range_min =  0,
+		.range_max = 0xd0ff,
+		.selector_reg = LT9611_PAGE_CONTROL,
+		.selector_mask = 0xff,
+		.selector_shift = 0,
+		.window_start = 0,
+		.window_len = 0x100,
+	},
+};
+
+static const struct regmap_config lt9611uxc_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0xffff,
+	.ranges = lt9611uxc_ranges,
+	.num_ranges = ARRAY_SIZE(lt9611uxc_ranges),
+};
+
+struct lt9611uxc_mode {
+	u16 hdisplay;
+	u16 vdisplay;
+	u8 vrefresh;
+};
+
+/*
+ * This chip supports only a fixed set of modes.
+ * Enumerate them here to check whether the mode is supported.
+ */
+static struct lt9611uxc_mode lt9611uxc_modes[] = {
+	{ 1920, 1080, 60 },
+	{ 1920, 1080, 30 },
+	{ 1920, 1080, 25 },
+	{ 1366, 768, 60 },
+	{ 1360, 768, 60 },
+	{ 1280, 1024, 60 },
+	{ 1280, 800, 60 },
+	{ 1280, 720, 60 },
+	{ 1280, 720, 50 },
+	{ 1280, 720, 30 },
+	{ 1152, 864, 60 },
+	{ 1024, 768, 60 },
+	{ 800, 600, 60 },
+	{ 720, 576, 50 },
+	{ 720, 480, 60 },
+	{ 640, 480, 60 },
+};
+
+static struct lt9611uxc *bridge_to_lt9611uxc(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct lt9611uxc, bridge);
+}
+
+static struct lt9611uxc *connector_to_lt9611uxc(struct drm_connector *connector)
+{
+	return container_of(connector, struct lt9611uxc, connector);
+}
+
+static void lt9611uxc_lock(struct lt9611uxc *lt9611uxc)
+{
+	mutex_lock(&lt9611uxc->ocm_lock);
+	regmap_write(lt9611uxc->regmap, 0x80ee, 0x01);
+}
+
+static void lt9611uxc_unlock(struct lt9611uxc *lt9611uxc)
+{
+	regmap_write(lt9611uxc->regmap, 0x80ee, 0x00);
+	msleep(50);
+	mutex_unlock(&lt9611uxc->ocm_lock);
+}
+
+static irqreturn_t lt9611uxc_irq_thread_handler(int irq, void *dev_id)
+{
+	struct lt9611uxc *lt9611uxc = dev_id;
+	unsigned int irq_status = 0;
+	unsigned int hpd_status = 0;
+
+	lt9611uxc_lock(lt9611uxc);
+
+	regmap_read(lt9611uxc->regmap, 0xb022, &irq_status);
+	regmap_read(lt9611uxc->regmap, 0xb023, &hpd_status);
+	if (irq_status)
+		regmap_write(lt9611uxc->regmap, 0xb022, 0);
+
+	if (irq_status & BIT(0)) {
+		lt9611uxc->edid_read = !!(hpd_status & BIT(0));
+		wake_up_all(&lt9611uxc->wq);
+	}
+
+	if (irq_status & BIT(1)) {
+		lt9611uxc->hdmi_connected = hpd_status & BIT(1);
+		schedule_work(&lt9611uxc->work);
+	}
+
+	lt9611uxc_unlock(lt9611uxc);
+
+	return IRQ_HANDLED;
+}
+
+static void lt9611uxc_hpd_work(struct work_struct *work)
+{
+	struct lt9611uxc *lt9611uxc = container_of(work, struct lt9611uxc, work);
+	bool connected;
+
+	if (lt9611uxc->connector.dev) {
+		if (lt9611uxc->connector.dev->mode_config.funcs)
+			drm_kms_helper_hotplug_event(lt9611uxc->connector.dev);
+	} else {
+
+		mutex_lock(&lt9611uxc->ocm_lock);
+		connected = lt9611uxc->hdmi_connected;
+		mutex_unlock(&lt9611uxc->ocm_lock);
+
+		drm_bridge_hpd_notify(&lt9611uxc->bridge,
+				      connected ?
+				      connector_status_connected :
+				      connector_status_disconnected);
+	}
+}
+
+static void lt9611uxc_reset(struct lt9611uxc *lt9611uxc)
+{
+	gpiod_set_value_cansleep(lt9611uxc->reset_gpio, 1);
+	msleep(20);
+
+	gpiod_set_value_cansleep(lt9611uxc->reset_gpio, 0);
+	msleep(20);
+
+	gpiod_set_value_cansleep(lt9611uxc->reset_gpio, 1);
+	msleep(300);
+}
+
+static void lt9611uxc_assert_5v(struct lt9611uxc *lt9611uxc)
+{
+	if (!lt9611uxc->enable_gpio)
+		return;
+
+	gpiod_set_value_cansleep(lt9611uxc->enable_gpio, 1);
+	msleep(20);
+}
+
+static int lt9611uxc_regulator_init(struct lt9611uxc *lt9611uxc)
+{
+	int ret;
+
+	lt9611uxc->supplies[0].supply = "vdd";
+	lt9611uxc->supplies[1].supply = "vcc";
+
+	ret = devm_regulator_bulk_get(lt9611uxc->dev, 2, lt9611uxc->supplies);
+	if (ret < 0)
+		return ret;
+
+	return regulator_set_load(lt9611uxc->supplies[0].consumer, 200000);
+}
+
+static int lt9611uxc_regulator_enable(struct lt9611uxc *lt9611uxc)
+{
+	int ret;
+
+	ret = regulator_enable(lt9611uxc->supplies[0].consumer);
+	if (ret < 0)
+		return ret;
+
+	usleep_range(1000, 10000); /* 50000 according to dtsi */
+
+	ret = regulator_enable(lt9611uxc->supplies[1].consumer);
+	if (ret < 0) {
+		regulator_disable(lt9611uxc->supplies[0].consumer);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct lt9611uxc_mode *lt9611uxc_find_mode(const struct drm_display_mode *mode)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(lt9611uxc_modes); i++) {
+		if (lt9611uxc_modes[i].hdisplay == mode->hdisplay &&
+		    lt9611uxc_modes[i].vdisplay == mode->vdisplay &&
+		    lt9611uxc_modes[i].vrefresh == drm_mode_vrefresh(mode)) {
+			return &lt9611uxc_modes[i];
+		}
+	}
+
+	return NULL;
+}
+
+static struct mipi_dsi_device *lt9611uxc_attach_dsi(struct lt9611uxc *lt9611uxc,
+						    struct device_node *dsi_node)
+{
+	const struct mipi_dsi_device_info info = { "lt9611uxc", 0, NULL };
+	struct mipi_dsi_device *dsi;
+	struct mipi_dsi_host *host;
+	struct device *dev = lt9611uxc->dev;
+	int ret;
+
+	host = of_find_mipi_dsi_host_by_node(dsi_node);
+	if (!host) {
+		dev_err(dev, "failed to find dsi host\n");
+		return ERR_PTR(-EPROBE_DEFER);
+	}
+
+	dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
+	if (IS_ERR(dsi)) {
+		dev_err(dev, "failed to create dsi device\n");
+		return dsi;
+	}
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+			  MIPI_DSI_MODE_VIDEO_HSE;
+
+	ret = devm_mipi_dsi_attach(dev, dsi);
+	if (ret < 0) {
+		dev_err(dev, "failed to attach dsi to host\n");
+		return ERR_PTR(ret);
+	}
+
+	return dsi;
+}
+
+static int lt9611uxc_connector_get_modes(struct drm_connector *connector)
+{
+	struct lt9611uxc *lt9611uxc = connector_to_lt9611uxc(connector);
+	unsigned int count;
+	struct edid *edid;
+
+	edid = lt9611uxc->bridge.funcs->get_edid(&lt9611uxc->bridge, connector);
+	drm_connector_update_edid_property(connector, edid);
+	count = drm_add_edid_modes(connector, edid);
+	kfree(edid);
+
+	return count;
+}
+
+static enum drm_connector_status lt9611uxc_connector_detect(struct drm_connector *connector,
+							    bool force)
+{
+	struct lt9611uxc *lt9611uxc = connector_to_lt9611uxc(connector);
+
+	return lt9611uxc->bridge.funcs->detect(&lt9611uxc->bridge);
+}
+
+static enum drm_mode_status lt9611uxc_connector_mode_valid(struct drm_connector *connector,
+							   struct drm_display_mode *mode)
+{
+	struct lt9611uxc_mode *lt9611uxc_mode = lt9611uxc_find_mode(mode);
+
+	return lt9611uxc_mode ? MODE_OK : MODE_BAD;
+}
+
+static const struct drm_connector_helper_funcs lt9611uxc_bridge_connector_helper_funcs = {
+	.get_modes = lt9611uxc_connector_get_modes,
+	.mode_valid = lt9611uxc_connector_mode_valid,
+};
+
+static const struct drm_connector_funcs lt9611uxc_bridge_connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = lt9611uxc_connector_detect,
+	.destroy = drm_connector_cleanup,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int lt9611uxc_connector_init(struct drm_bridge *bridge, struct lt9611uxc *lt9611uxc)
+{
+	int ret;
+
+	if (!bridge->encoder) {
+		DRM_ERROR("Parent encoder object not found");
+		return -ENODEV;
+	}
+
+	lt9611uxc->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_helper_add(&lt9611uxc->connector,
+				 &lt9611uxc_bridge_connector_helper_funcs);
+	ret = drm_connector_init(bridge->dev, &lt9611uxc->connector,
+				 &lt9611uxc_bridge_connector_funcs,
+				 DRM_MODE_CONNECTOR_HDMIA);
+	if (ret) {
+		DRM_ERROR("Failed to initialize connector with drm\n");
+		return ret;
+	}
+
+	ret = drm_connector_attach_encoder(&lt9611uxc->connector, bridge->encoder);
+	if (ret) {
+		DRM_ERROR("Failed to link up connector to encoder: %d\n", ret);
+		return ret;
+	}
+
+	/* Attach primary DSI */
+	lt9611uxc->dsi0 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi0_node);
+	if (IS_ERR(lt9611uxc->dsi0)) {
+		ret = PTR_ERR(lt9611uxc->dsi0);
+		drm_bridge_remove(&lt9611uxc->bridge);
+		return ret;
+	}
+
+	/* Attach secondary DSI, if specified */
+	if (lt9611uxc->dsi1_node) {
+		lt9611uxc->dsi1 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi1_node);
+		if (IS_ERR(lt9611uxc->dsi1)) {
+			ret = PTR_ERR(lt9611uxc->dsi1);
+			drm_bridge_remove(&lt9611uxc->bridge);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int lt9611uxc_bridge_attach(struct drm_bridge *bridge,
+				   enum drm_bridge_attach_flags flags)
+{
+	struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge);
+	int ret;
+
+	ret = lt9611uxc_connector_init(bridge, lt9611uxc);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static enum drm_mode_status
+lt9611uxc_bridge_mode_valid(struct drm_bridge *bridge,
+			    const struct drm_display_info *info,
+			    const struct drm_display_mode *mode)
+{
+	struct lt9611uxc_mode *lt9611uxc_mode;
+
+	lt9611uxc_mode = lt9611uxc_find_mode(mode);
+
+	return lt9611uxc_mode ? MODE_OK : MODE_BAD;
+}
+
+static void lt9611uxc_video_setup(struct lt9611uxc *lt9611uxc,
+				  const struct drm_display_mode *mode)
+{
+	u32 h_total, hactive, hsync_len, hfront_porch;
+	u32 v_total, vactive, vsync_len, vfront_porch;
+
+	h_total = mode->htotal;
+	v_total = mode->vtotal;
+
+	hactive = mode->hdisplay;
+	hsync_len = mode->hsync_end - mode->hsync_start;
+	hfront_porch = mode->hsync_start - mode->hdisplay;
+
+	vactive = mode->vdisplay;
+	vsync_len = mode->vsync_end - mode->vsync_start;
+	vfront_porch = mode->vsync_start - mode->vdisplay;
+
+	regmap_write(lt9611uxc->regmap, 0xd00d, (u8)(v_total / 256));
+	regmap_write(lt9611uxc->regmap, 0xd00e, (u8)(v_total % 256));
+
+	regmap_write(lt9611uxc->regmap, 0xd00f, (u8)(vactive / 256));
+	regmap_write(lt9611uxc->regmap, 0xd010, (u8)(vactive % 256));
+
+	regmap_write(lt9611uxc->regmap, 0xd011, (u8)(h_total / 256));
+	regmap_write(lt9611uxc->regmap, 0xd012, (u8)(h_total % 256));
+
+	regmap_write(lt9611uxc->regmap, 0xd013, (u8)(hactive / 256));
+	regmap_write(lt9611uxc->regmap, 0xd014, (u8)(hactive % 256));
+
+	regmap_write(lt9611uxc->regmap, 0xd015, (u8)(vsync_len % 256));
+
+	regmap_update_bits(lt9611uxc->regmap, 0xd016, 0xf, (u8)(hsync_len / 256));
+	regmap_write(lt9611uxc->regmap, 0xd017, (u8)(hsync_len % 256));
+
+	regmap_update_bits(lt9611uxc->regmap, 0xd018, 0xf, (u8)(vfront_porch / 256));
+	regmap_write(lt9611uxc->regmap, 0xd019, (u8)(vfront_porch % 256));
+
+	regmap_update_bits(lt9611uxc->regmap, 0xd01a, 0xf, (u8)(hfront_porch / 256));
+	regmap_write(lt9611uxc->regmap, 0xd01b, (u8)(hfront_porch % 256));
+}
+
+static void lt9611uxc_bridge_mode_set(struct drm_bridge *bridge,
+				      const struct drm_display_mode *mode,
+				      const struct drm_display_mode *adj_mode)
+{
+	struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge);
+
+	lt9611uxc_lock(lt9611uxc);
+	lt9611uxc_video_setup(lt9611uxc, mode);
+	lt9611uxc_unlock(lt9611uxc);
+}
+
+static enum drm_connector_status lt9611uxc_bridge_detect(struct drm_bridge *bridge)
+{
+	struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge);
+	unsigned int reg_val = 0;
+	int ret;
+	bool connected = true;
+
+	lt9611uxc_lock(lt9611uxc);
+
+	if (lt9611uxc->hpd_supported) {
+		ret = regmap_read(lt9611uxc->regmap, 0xb023, &reg_val);
+
+		if (ret)
+			dev_err(lt9611uxc->dev, "failed to read hpd status: %d\n", ret);
+		else
+			connected  = reg_val & BIT(1);
+	}
+	lt9611uxc->hdmi_connected = connected;
+
+	lt9611uxc_unlock(lt9611uxc);
+
+	return connected ?  connector_status_connected :
+				connector_status_disconnected;
+}
+
+static int lt9611uxc_wait_for_edid(struct lt9611uxc *lt9611uxc)
+{
+	return wait_event_interruptible_timeout(lt9611uxc->wq, lt9611uxc->edid_read,
+			msecs_to_jiffies(500));
+}
+
+static int lt9611uxc_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
+{
+	struct lt9611uxc *lt9611uxc = data;
+	int ret;
+
+	if (len > EDID_BLOCK_SIZE)
+		return -EINVAL;
+
+	if (block >= EDID_NUM_BLOCKS)
+		return -EINVAL;
+
+	lt9611uxc_lock(lt9611uxc);
+
+	regmap_write(lt9611uxc->regmap, 0xb00b, 0x10);
+
+	regmap_write(lt9611uxc->regmap, 0xb00a, block * EDID_BLOCK_SIZE);
+
+	ret = regmap_noinc_read(lt9611uxc->regmap, 0xb0b0, buf, len);
+	if (ret)
+		dev_err(lt9611uxc->dev, "edid read failed: %d\n", ret);
+
+	lt9611uxc_unlock(lt9611uxc);
+
+	return 0;
+};
+
+static struct edid *lt9611uxc_bridge_get_edid(struct drm_bridge *bridge,
+					      struct drm_connector *connector)
+{
+	struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge);
+	int ret;
+
+	ret = lt9611uxc_wait_for_edid(lt9611uxc);
+	if (ret < 0) {
+		dev_err(lt9611uxc->dev, "wait for EDID failed: %d\n", ret);
+		return NULL;
+	} else if (ret == 0) {
+		dev_err(lt9611uxc->dev, "wait for EDID timeout\n");
+		return NULL;
+	}
+
+	return drm_do_get_edid(connector, lt9611uxc_get_edid_block, lt9611uxc);
+}
+
+static const struct drm_bridge_funcs lt9611uxc_bridge_funcs = {
+	.attach = lt9611uxc_bridge_attach,
+	.mode_valid = lt9611uxc_bridge_mode_valid,
+	.mode_set = lt9611uxc_bridge_mode_set,
+	.detect = lt9611uxc_bridge_detect,
+	.get_edid = lt9611uxc_bridge_get_edid,
+};
+
+static int lt9611uxc_parse_dt(struct device *dev,
+			      struct lt9611uxc *lt9611uxc)
+{
+	lt9611uxc->dsi0_node = of_graph_get_remote_node(dev->of_node, 0, -1);
+	if (!lt9611uxc->dsi0_node) {
+		dev_err(lt9611uxc->dev, "failed to get remote node for primary dsi\n");
+		return -ENODEV;
+	}
+
+	lt9611uxc->dsi1_node = of_graph_get_remote_node(dev->of_node, 1, -1);
+
+	return 0;
+}
+
+static int lt9611uxc_gpio_init(struct lt9611uxc *lt9611uxc)
+{
+	struct device *dev = lt9611uxc->dev;
+
+	lt9611uxc->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(lt9611uxc->reset_gpio)) {
+		dev_err(dev, "failed to acquire reset gpio\n");
+		return PTR_ERR(lt9611uxc->reset_gpio);
+	}
+
+	lt9611uxc->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(lt9611uxc->enable_gpio)) {
+		dev_err(dev, "failed to acquire enable gpio\n");
+		return PTR_ERR(lt9611uxc->enable_gpio);
+	}
+
+	return 0;
+}
+
+static int lt9611uxc_read_device_rev(struct lt9611uxc *lt9611uxc)
+{
+	unsigned int rev0, rev1, rev2;
+	int ret;
+
+	lt9611uxc_lock(lt9611uxc);
+
+	ret = regmap_read(lt9611uxc->regmap, 0x8100, &rev0);
+	ret |= regmap_read(lt9611uxc->regmap, 0x8101, &rev1);
+	ret |= regmap_read(lt9611uxc->regmap, 0x8102, &rev2);
+	if (ret)
+		dev_err(lt9611uxc->dev, "failed to read revision: %d\n", ret);
+	else
+		dev_info(lt9611uxc->dev, "LT9611 revision: 0x%02x.%02x.%02x\n", rev0, rev1, rev2);
+
+	lt9611uxc_unlock(lt9611uxc);
+
+	return ret;
+}
+
+static int lt9611uxc_read_version(struct lt9611uxc *lt9611uxc)
+{
+	unsigned int rev;
+	int ret;
+
+	lt9611uxc_lock(lt9611uxc);
+
+	ret = regmap_read(lt9611uxc->regmap, 0xb021, &rev);
+	if (ret)
+		dev_err(lt9611uxc->dev, "failed to read revision: %d\n", ret);
+	else
+		dev_info(lt9611uxc->dev, "LT9611 version: 0x%02x\n", rev);
+
+	lt9611uxc_unlock(lt9611uxc);
+
+	return ret < 0 ? ret : rev;
+}
+
+static int lt9611uxc_hdmi_hw_params(struct device *dev, void *data,
+				    struct hdmi_codec_daifmt *fmt,
+				    struct hdmi_codec_params *hparms)
+{
+	/*
+	 * LT9611UXC will automatically detect rate and sample size, so no need
+	 * to setup anything here.
+	 */
+	return 0;
+}
+
+static void lt9611uxc_audio_shutdown(struct device *dev, void *data)
+{
+}
+
+static int lt9611uxc_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
+					 struct device_node *endpoint)
+{
+	struct of_endpoint of_ep;
+	int ret;
+
+	ret = of_graph_parse_endpoint(endpoint, &of_ep);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * HDMI sound should be located as reg = <2>
+	 * Then, it is sound port 0
+	 */
+	if (of_ep.port == 2)
+		return 0;
+
+	return -EINVAL;
+}
+
+static const struct hdmi_codec_ops lt9611uxc_codec_ops = {
+	.hw_params	= lt9611uxc_hdmi_hw_params,
+	.audio_shutdown = lt9611uxc_audio_shutdown,
+	.get_dai_id	= lt9611uxc_hdmi_i2s_get_dai_id,
+};
+
+static int lt9611uxc_audio_init(struct device *dev, struct lt9611uxc *lt9611uxc)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &lt9611uxc_codec_ops,
+		.max_i2s_channels = 2,
+		.i2s = 1,
+		.data = lt9611uxc,
+	};
+
+	lt9611uxc->audio_pdev =
+		platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
+					      PLATFORM_DEVID_AUTO,
+					      &codec_data, sizeof(codec_data));
+
+	return PTR_ERR_OR_ZERO(lt9611uxc->audio_pdev);
+}
+
+static void lt9611uxc_audio_exit(struct lt9611uxc *lt9611uxc)
+{
+	if (lt9611uxc->audio_pdev) {
+		platform_device_unregister(lt9611uxc->audio_pdev);
+		lt9611uxc->audio_pdev = NULL;
+	}
+}
+
+#define LT9611UXC_FW_PAGE_SIZE 32
+static void lt9611uxc_firmware_write_page(struct lt9611uxc *lt9611uxc, u16 addr, const u8 *buf)
+{
+	struct reg_sequence seq_write_prepare[] = {
+		REG_SEQ0(0x805a, 0x04),
+		REG_SEQ0(0x805a, 0x00),
+
+		REG_SEQ0(0x805e, 0xdf),
+		REG_SEQ0(0x805a, 0x20),
+		REG_SEQ0(0x805a, 0x00),
+		REG_SEQ0(0x8058, 0x21),
+	};
+
+	struct reg_sequence seq_write_addr[] = {
+		REG_SEQ0(0x805b, (addr >> 16) & 0xff),
+		REG_SEQ0(0x805c, (addr >> 8) & 0xff),
+		REG_SEQ0(0x805d, addr & 0xff),
+		REG_SEQ0(0x805a, 0x10),
+		REG_SEQ0(0x805a, 0x00),
+	};
+
+	regmap_write(lt9611uxc->regmap, 0x8108, 0xbf);
+	msleep(20);
+	regmap_write(lt9611uxc->regmap, 0x8108, 0xff);
+	msleep(20);
+	regmap_multi_reg_write(lt9611uxc->regmap, seq_write_prepare, ARRAY_SIZE(seq_write_prepare));
+	regmap_noinc_write(lt9611uxc->regmap, 0x8059, buf, LT9611UXC_FW_PAGE_SIZE);
+	regmap_multi_reg_write(lt9611uxc->regmap, seq_write_addr, ARRAY_SIZE(seq_write_addr));
+	msleep(20);
+}
+
+static void lt9611uxc_firmware_read_page(struct lt9611uxc *lt9611uxc, u16 addr, char *buf)
+{
+	struct reg_sequence seq_read_page[] = {
+		REG_SEQ0(0x805a, 0xa0),
+		REG_SEQ0(0x805a, 0x80),
+		REG_SEQ0(0x805b, (addr >> 16) & 0xff),
+		REG_SEQ0(0x805c, (addr >> 8) & 0xff),
+		REG_SEQ0(0x805d, addr & 0xff),
+		REG_SEQ0(0x805a, 0x90),
+		REG_SEQ0(0x805a, 0x80),
+		REG_SEQ0(0x8058, 0x21),
+	};
+
+	regmap_multi_reg_write(lt9611uxc->regmap, seq_read_page, ARRAY_SIZE(seq_read_page));
+	regmap_noinc_read(lt9611uxc->regmap, 0x805f, buf, LT9611UXC_FW_PAGE_SIZE);
+}
+
+static char *lt9611uxc_firmware_read(struct lt9611uxc *lt9611uxc, size_t size)
+{
+	struct reg_sequence seq_read_setup[] = {
+		REG_SEQ0(0x805a, 0x84),
+		REG_SEQ0(0x805a, 0x80),
+	};
+
+	char *readbuf;
+	u16 offset;
+
+	readbuf = kzalloc(ALIGN(size, 32), GFP_KERNEL);
+	if (!readbuf)
+		return NULL;
+
+	regmap_multi_reg_write(lt9611uxc->regmap, seq_read_setup, ARRAY_SIZE(seq_read_setup));
+
+	for (offset = 0;
+	     offset < size;
+	     offset += LT9611UXC_FW_PAGE_SIZE)
+		lt9611uxc_firmware_read_page(lt9611uxc, offset, &readbuf[offset]);
+
+	return readbuf;
+}
+
+static int lt9611uxc_firmware_update(struct lt9611uxc *lt9611uxc)
+{
+	int ret;
+	u16 offset;
+	size_t remain;
+	char *readbuf;
+	const struct firmware *fw;
+
+	struct reg_sequence seq_setup[] = {
+		REG_SEQ0(0x805e, 0xdf),
+		REG_SEQ0(0x8058, 0x00),
+		REG_SEQ0(0x8059, 0x50),
+		REG_SEQ0(0x805a, 0x10),
+		REG_SEQ0(0x805a, 0x00),
+	};
+
+
+	struct reg_sequence seq_block_erase[] = {
+		REG_SEQ0(0x805a, 0x04),
+		REG_SEQ0(0x805a, 0x00),
+		REG_SEQ0(0x805b, 0x00),
+		REG_SEQ0(0x805c, 0x00),
+		REG_SEQ0(0x805d, 0x00),
+		REG_SEQ0(0x805a, 0x01),
+		REG_SEQ0(0x805a, 0x00),
+	};
+
+	ret = request_firmware(&fw, "lt9611uxc_fw.bin", lt9611uxc->dev);
+	if (ret < 0)
+		return ret;
+
+	dev_info(lt9611uxc->dev, "Updating firmware\n");
+	lt9611uxc_lock(lt9611uxc);
+
+	regmap_multi_reg_write(lt9611uxc->regmap, seq_setup, ARRAY_SIZE(seq_setup));
+
+	/*
+	 * Need erase block 2 timess here. Sometimes, block erase can fail.
+	 * This is a workaroud.
+	 */
+	regmap_multi_reg_write(lt9611uxc->regmap, seq_block_erase, ARRAY_SIZE(seq_block_erase));
+	msleep(3000);
+	regmap_multi_reg_write(lt9611uxc->regmap, seq_block_erase, ARRAY_SIZE(seq_block_erase));
+	msleep(3000);
+
+	for (offset = 0, remain = fw->size;
+	     remain >= LT9611UXC_FW_PAGE_SIZE;
+	     offset += LT9611UXC_FW_PAGE_SIZE, remain -= LT9611UXC_FW_PAGE_SIZE)
+		lt9611uxc_firmware_write_page(lt9611uxc, offset, fw->data + offset);
+
+	if (remain > 0) {
+		char buf[LT9611UXC_FW_PAGE_SIZE];
+
+		memset(buf, 0xff, LT9611UXC_FW_PAGE_SIZE);
+		memcpy(buf, fw->data + offset, remain);
+		lt9611uxc_firmware_write_page(lt9611uxc, offset, buf);
+	}
+	msleep(20);
+
+	readbuf = lt9611uxc_firmware_read(lt9611uxc, fw->size);
+	if (!readbuf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (!memcmp(readbuf, fw->data, fw->size)) {
+		dev_err(lt9611uxc->dev, "Firmware update failed\n");
+		print_hex_dump(KERN_ERR, "fw: ", DUMP_PREFIX_OFFSET,
+				16, 1, readbuf, fw->size, false);
+		ret = -EINVAL;
+	} else {
+		dev_info(lt9611uxc->dev, "Firmware updates successfully\n");
+		ret = 0;
+	}
+	kfree(readbuf);
+
+out:
+	lt9611uxc_unlock(lt9611uxc);
+	lt9611uxc_reset(lt9611uxc);
+	release_firmware(fw);
+
+	return ret;
+}
+
+static ssize_t lt9611uxc_firmware_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct lt9611uxc *lt9611uxc = dev_get_drvdata(dev);
+	int ret;
+
+	ret = lt9611uxc_firmware_update(lt9611uxc);
+	if (ret < 0)
+		return ret;
+	return len;
+}
+
+static ssize_t lt9611uxc_firmware_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct lt9611uxc *lt9611uxc = dev_get_drvdata(dev);
+
+	return sysfs_emit(buf, "%02x\n", lt9611uxc->fw_version);
+}
+
+static DEVICE_ATTR_RW(lt9611uxc_firmware);
+
+static struct attribute *lt9611uxc_attrs[] = {
+	&dev_attr_lt9611uxc_firmware.attr,
+	NULL,
+};
+
+static const struct attribute_group lt9611uxc_attr_group = {
+	.attrs = lt9611uxc_attrs,
+};
+
+static const struct attribute_group *lt9611uxc_attr_groups[] = {
+	&lt9611uxc_attr_group,
+	NULL,
+};
+
+static int lt9611uxc_probe(struct i2c_client *client)
+{
+	struct lt9611uxc *lt9611uxc;
+	struct device *dev = &client->dev;
+	int ret;
+	bool fw_updated = false;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(dev, "device doesn't support I2C\n");
+		return -ENODEV;
+	}
+
+	lt9611uxc = devm_kzalloc(dev, sizeof(*lt9611uxc), GFP_KERNEL);
+	if (!lt9611uxc)
+		return -ENOMEM;
+
+	lt9611uxc->dev = dev;
+	lt9611uxc->client = client;
+	mutex_init(&lt9611uxc->ocm_lock);
+
+	lt9611uxc->regmap = devm_regmap_init_i2c(client, &lt9611uxc_regmap_config);
+	if (IS_ERR(lt9611uxc->regmap)) {
+		dev_err(lt9611uxc->dev, "regmap i2c init failed\n");
+		return PTR_ERR(lt9611uxc->regmap);
+	}
+
+	ret = lt9611uxc_parse_dt(dev, lt9611uxc);
+	if (ret) {
+		dev_err(dev, "failed to parse device tree\n");
+		return ret;
+	}
+
+	ret = lt9611uxc_gpio_init(lt9611uxc);
+	if (ret < 0)
+		goto err_of_put;
+
+	ret = lt9611uxc_regulator_init(lt9611uxc);
+	if (ret < 0)
+		goto err_of_put;
+
+	lt9611uxc_assert_5v(lt9611uxc);
+
+	ret = lt9611uxc_regulator_enable(lt9611uxc);
+	if (ret)
+		goto err_of_put;
+
+	lt9611uxc_reset(lt9611uxc);
+
+	ret = lt9611uxc_read_device_rev(lt9611uxc);
+	if (ret) {
+		dev_err(dev, "failed to read chip rev\n");
+		goto err_disable_regulators;
+	}
+
+retry:
+	ret = lt9611uxc_read_version(lt9611uxc);
+	if (ret < 0) {
+		dev_err(dev, "failed to read FW version\n");
+		goto err_disable_regulators;
+	} else if (ret == 0) {
+		if (!fw_updated) {
+			fw_updated = true;
+			dev_err(dev, "FW version 0, enforcing firmware update\n");
+			ret = lt9611uxc_firmware_update(lt9611uxc);
+			if (ret < 0)
+				goto err_disable_regulators;
+			else
+				goto retry;
+		} else {
+			dev_err(dev, "FW version 0, update failed\n");
+			ret = -EOPNOTSUPP;
+			goto err_disable_regulators;
+		}
+	} else if (ret < 0x40) {
+		dev_info(dev, "FW version 0x%x, HPD not supported\n", ret);
+	} else {
+		lt9611uxc->hpd_supported = true;
+	}
+	lt9611uxc->fw_version = ret;
+
+	init_waitqueue_head(&lt9611uxc->wq);
+	INIT_WORK(&lt9611uxc->work, lt9611uxc_hpd_work);
+
+	ret = devm_request_threaded_irq(dev, client->irq, NULL,
+					lt9611uxc_irq_thread_handler,
+					IRQF_ONESHOT, "lt9611uxc", lt9611uxc);
+	if (ret) {
+		dev_err(dev, "failed to request irq\n");
+		goto err_disable_regulators;
+	}
+
+	i2c_set_clientdata(client, lt9611uxc);
+
+	lt9611uxc->bridge.funcs = &lt9611uxc_bridge_funcs;
+	lt9611uxc->bridge.of_node = client->dev.of_node;
+	lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
+	if (lt9611uxc->hpd_supported)
+		lt9611uxc->bridge.ops |= DRM_BRIDGE_OP_HPD;
+	lt9611uxc->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+
+	drm_bridge_add(&lt9611uxc->bridge);
+
+	return lt9611uxc_audio_init(dev, lt9611uxc);
+
+err_disable_regulators:
+	regulator_bulk_disable(ARRAY_SIZE(lt9611uxc->supplies), lt9611uxc->supplies);
+
+err_of_put:
+	of_node_put(lt9611uxc->dsi1_node);
+	of_node_put(lt9611uxc->dsi0_node);
+
+	return ret;
+}
+
+static void lt9611uxc_remove(struct i2c_client *client)
+{
+	struct lt9611uxc *lt9611uxc = i2c_get_clientdata(client);
+
+	disable_irq(client->irq);
+	cancel_work_sync(&lt9611uxc->work);
+	lt9611uxc_audio_exit(lt9611uxc);
+	drm_bridge_remove(&lt9611uxc->bridge);
+
+	mutex_destroy(&lt9611uxc->ocm_lock);
+
+	regulator_bulk_disable(ARRAY_SIZE(lt9611uxc->supplies), lt9611uxc->supplies);
+
+	of_node_put(lt9611uxc->dsi1_node);
+	of_node_put(lt9611uxc->dsi0_node);
+}
+
+static struct i2c_device_id lt9611uxc_id[] = {
+	{ "lt,lt9611uxc", 0 },
+	{ /* sentinel */ }
+};
+
+static const struct of_device_id lt9611uxc_match_table[] = {
+	{ .compatible = "lt,lt9611uxc" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lt9611uxc_match_table);
+
+static struct i2c_driver lt9611uxc_driver = {
+	.driver = {
+		.name = "lt9611uxc",
+		.of_match_table = lt9611uxc_match_table,
+		.dev_groups = lt9611uxc_attr_groups,
+	},
+	.probe = lt9611uxc_probe,
+	.remove = lt9611uxc_remove,
+	.id_table = lt9611uxc_id,
+};
+module_i2c_driver(lt9611uxc_driver);
+
+MODULE_AUTHOR("Dmitry Baryshkov <[email protected]>");
+MODULE_LICENSE("GPL v2");

+ 18 - 0
qcom/opensource/display-drivers/config/augen3disp.conf

@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DRM_MSM_DP=n
+export CONFIG_DRM_MSM_DP_MST=n
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_SDE_RSC=n
+export CONFIG_DRM_SDE_WB=n
+export CONFIG_DRM_MSM_REGISTER_LOGGING=n
+export CONFIG_SDE_RECOVERY_MANAGER=n
+export CONFIG_DRM_SDE_SHD=n
+export CONFIG_DRM_SDE_SHP=n
+export CONFIG_DRM_SDE_ROI_MISR=n
+export CONFIG_DRM_MSM_LEASE=n
+export CONFIG_DISPLAY_BUILD=m

+ 22 - 0
qcom/opensource/display-drivers/config/augen3dispconf.h

@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DRM_MSM_DP 0
+#define CONFIG_DRM_MSM_DP_MST 0
+#define CONFIG_DRM_SDE_WB 0
+#define CONFIG_DRM_SDE_RSC 0
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 0
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1
+#define CONFIG_MSM_EXT_DISPLAY 1
+#define CONFIG_DRM_SDE_ROI_MISR 0
+#define CONFIG_DRM_SDE_SHD 0
+#define CONFIG_DRM_SDE_SHP 0
+#define CONFIG_DRM_MSM_LEASE 0

+ 16 - 0
qcom/opensource/display-drivers/config/bengaldisp.conf

@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (c) 2019, The Linux Foundation. All rights reserved.
+
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DRM_MSM_DP=n
+export CONFIG_QCOM_MDSS_DP_PLL=n
+export CONFIG_DSI_PARSER=y
+export CONFIG_DRM_SDE_WB=n
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_MSM_SDE_ROTATOR=y
+export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
+export CONFIG_DRM_SDE_RSC=n

+ 15 - 0
qcom/opensource/display-drivers/config/bengaldispconf.h

@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_MSM_SDE_ROTATOR 1
+#define CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG 1

+ 18 - 0
qcom/opensource/display-drivers/config/gki_holidisp.conf

@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (c) 2020, The Linux Foundation. All rights reserved.
+
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_DRM_MSM_DP=n
+export CONFIG_DRM_MSM_DP_MST=n
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_DRM_SDE_WB=n
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_SDE_RSC=n
+export CONFIG_DISPLAY_BUILD=m
+export CONFIG_MSM_SDE_ROTATOR=y
+export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
+export CONFIG_MSM_SDE_ROTATOR_INIT_ONLY=y

+ 17 - 0
qcom/opensource/display-drivers/config/gki_holidispconf.h

@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1
+#define CONFIG_MSM_SDE_ROTATOR 1
+#define CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG 1
+#define CONFIG_MSM_SDE_ROTATOR_INIT_ONLY 1

+ 18 - 0
qcom/opensource/display-drivers/config/gki_kalamadisp.conf

@@ -0,0 +1,18 @@
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DRM_MSM_DP=y
+export CONFIG_DRM_MSM_DP_MST=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_SDE_RSC=y
+export CONFIG_DRM_SDE_WB=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_MSM_MMRM=y
+export CONFIG_DISPLAY_BUILD=m
+export CONFIG_HDCP_QSEECOM=y
+export CONFIG_DRM_SDE_VM=y
+export CONFIG_QTI_HW_FENCE=y
+export CONFIG_QCOM_SPEC_SYNC=y
+export CONFIG_QCOM_FSA4480_I2C=y

+ 26 - 0
qcom/opensource/display-drivers/config/gki_kalamadispconf.h

@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DRM_MSM_DP 1
+#define CONFIG_DRM_MSM_DP_MST 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_SDE_WB 1
+#define CONFIG_DRM_SDE_RSC 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_MSM_MMRM 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1
+#define CONFIG_MSM_EXT_DISPLAY 1
+#define CONFIG_HDCP_QSEECOM 1
+#define CONFIG_DRM_SDE_VM 1
+#define CONFIG_QTI_HW_FENCE 1
+#define CONFIG_QCOM_SPEC_SYNC 1
+#define CONFIG_QCOM_FSA4480_I2C 1

+ 10 - 0
qcom/opensource/display-drivers/config/gki_kalamadisptui.conf

@@ -0,0 +1,10 @@
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_DISPLAY_BUILD=m
+export CONFIG_DRM_SDE_VM=y
+export CONFIG_DRM_LOW_MSM_MEM_FOOTPRINT=y

+ 17 - 0
qcom/opensource/display-drivers/config/gki_kalamadisptuiconf.h

@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1
+#define CONFIG_DRM_SDE_VM 1
+#define CONFIG_DRM_MSM_LOW_MEM_FOOTPRINT 1

+ 12 - 0
qcom/opensource/display-drivers/config/gki_lahainadisp.conf

@@ -0,0 +1,12 @@
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DRM_MSM_DP=y
+export CONFIG_DRM_MSM_DP_MST=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_SDE_WB=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_DRM_SDE_RSC=y
+export CONFIG_DISPLAY_BUILD=m

+ 19 - 0
qcom/opensource/display-drivers/config/gki_lahainadispconf.h

@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DRM_MSM_DP 1
+#define CONFIG_DRM_MSM_DP_MST 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_SDE_WB 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1
+#define CONFIG_DRM_SDE_RSC 1
+

+ 11 - 0
qcom/opensource/display-drivers/config/gki_neodisp.conf

@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (c) 2021, The Linux Foundation. All rights reserved.
+
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_DISPLAY_BUILD=m

+ 14 - 0
qcom/opensource/display-drivers/config/gki_neodispconf.h

@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1

+ 16 - 0
qcom/opensource/display-drivers/config/gki_niobedisp.conf

@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_SDE_RSC=y
+export CONFIG_DRM_SDE_WB=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_MSM_MMRM=y
+export CONFIG_DISPLAY_BUILD=m
+export CONFIG_DRM_SDE_SYSTEM_SLEEP_DISABLE=y
+export CONFIG_DRM_SDE_IPCC=y
+export CONFIG_DRM_SDE_MINIDUMP_DISABLE=y

+ 22 - 0
qcom/opensource/display-drivers/config/gki_niobedispconf.h

@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_SDE_WB 1
+#define CONFIG_DRM_SDE_RSC 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_MSM_MMRM 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1
+#define CONFIG_MSM_EXT_DISPLAY 1
+#define CONFIG_DRM_SDE_SYSTEM_SLEEP_DISABLE 1
+#define CONFIG_DRM_SDE_IPCC 1
+#define CONFIG_DRM_SDE_MINIDUMP_DISABLE 1

+ 11 - 0
qcom/opensource/display-drivers/config/gki_parrotdisp.conf

@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_DRM_SDE_WB=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_DISPLAY_BUILD=m

+ 16 - 0
qcom/opensource/display-drivers/config/gki_parrotdispconf.h

@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_SDE_WB 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1

+ 18 - 0
qcom/opensource/display-drivers/config/gki_pineappledisp.conf

@@ -0,0 +1,18 @@
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DRM_MSM_DP=y
+export CONFIG_DRM_MSM_DP_MST=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_SDE_RSC=y
+export CONFIG_DRM_SDE_WB=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_MSM_MMRM=y
+export CONFIG_DISPLAY_BUILD=m
+export CONFIG_HDCP_QSEECOM=y
+export CONFIG_DRM_SDE_VM=y
+export CONFIG_QTI_HW_FENCE=y
+export CONFIG_QCOM_SPEC_SYNC=y
+export CONFIG_QCOM_WCD939X_I2C=y

+ 26 - 0
qcom/opensource/display-drivers/config/gki_pineappledispconf.h

@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DRM_MSM_DP 1
+#define CONFIG_DRM_MSM_DP_MST 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_SDE_WB 1
+#define CONFIG_DRM_SDE_RSC 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_MSM_MMRM 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1
+#define CONFIG_MSM_EXT_DISPLAY 1
+#define CONFIG_HDCP_QSEECOM 1
+#define CONFIG_DRM_SDE_VM 1
+#define CONFIG_QTI_HW_FENCE 1
+#define CONFIG_QCOM_SPEC_SYNC 1
+#define CONFIG_QCOM_WCD939X_I2C 1

+ 10 - 0
qcom/opensource/display-drivers/config/gki_pineappledisptui.conf

@@ -0,0 +1,10 @@
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_DISPLAY_BUILD=m
+export CONFIG_DRM_SDE_VM=y
+export CONFIG_DRM_LOW_MSM_MEM_FOOTPRINT=y

+ 17 - 0
qcom/opensource/display-drivers/config/gki_pineappledisptuiconf.h

@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1
+#define CONFIG_DRM_SDE_VM 1
+#define CONFIG_DRM_MSM_LOW_MEM_FOOTPRINT 1

+ 11 - 0
qcom/opensource/display-drivers/config/gki_pittidisp.conf

@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_DISPLAY_BUILD=m
+export CONFIG_QCOM_SPEC_SYNC=y

+ 17 - 0
qcom/opensource/display-drivers/config/gki_pittidispconf.h

@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1
+#define CONFIG_MSM_EXT_DISPLAY 1
+#define CONFIG_QCOM_SPEC_SYNC 1

+ 15 - 0
qcom/opensource/display-drivers/config/gki_waipiodisp.conf

@@ -0,0 +1,15 @@
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DRM_MSM_DP=y
+export CONFIG_DRM_MSM_DP_MST=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_SDE_RSC=y
+export CONFIG_DRM_SDE_WB=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_MSM_MMRM=y
+export CONFIG_DISPLAY_BUILD=m
+export CONFIG_DRM_SDE_VM=y
+export CONFIG_HDCP_QSEECOM=y

+ 22 - 0
qcom/opensource/display-drivers/config/gki_waipiodispconf.h

@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DRM_MSM_DP 1
+#define CONFIG_DRM_MSM_DP_MST 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_SDE_WB 1
+#define CONFIG_DRM_SDE_RSC 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_MSM_MMRM 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1
+#define CONFIG_MSM_EXT_DISPLAY 1
+#define CONFIG_DRM_SDE_VM 1
+#define CONFIG_HDCP_QSEECOM 1

+ 10 - 0
qcom/opensource/display-drivers/config/gki_waipiodisptui.conf

@@ -0,0 +1,10 @@
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_DISPLAY_BUILD=m
+export CONFIG_DRM_SDE_VM=y
+export CONFIG_DRM_LOW_MSM_MEM_FOOTPRINT=y

+ 16 - 0
qcom/opensource/display-drivers/config/gki_waipiodisptuiconf.h

@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_GKI_DISPLAY 1
+#define CONFIG_DRM_SDE_VM 1
+#define CONFIG_DRM_MSM_LOW_MEM_FOOTPRINT 1

+ 17 - 0
qcom/opensource/display-drivers/config/holidisp.conf

@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (c) 2020, The Linux Foundation. All rights reserved.
+
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_DRM_MSM_DP=n
+export CONFIG_DRM_MSM_DP_MST=n
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_DRM_SDE_WB=n
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_SDE_RSC=n
+export CONFIG_MSM_SDE_ROTATOR=y
+export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
+export CONFIG_DISPLAY_BUILD=y

+ 15 - 0
qcom/opensource/display-drivers/config/holidispconf.h

@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_MSM_SDE_ROTATOR 1
+#define CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG 1

+ 13 - 0
qcom/opensource/display-drivers/config/konadisp.conf

@@ -0,0 +1,13 @@
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DRM_MSM_DP=y
+export CONFIG_QCOM_MDSS_DP_PLL=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_DRM_SDE_WB=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_MSM_SDE_ROTATOR=y
+export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
+export CONFIG_DRM_SDE_RSC=y

+ 20 - 0
qcom/opensource/display-drivers/config/konadispconf.h

@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DRM_MSM_DP 1
+#define CONFIG_QCOM_MDSS_DP_PLL 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_SDE_WB 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_MSM_SDE_ROTATOR 1
+#define CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG 1
+#define CONFIG_DRM_SDE_RSC 1
+

+ 13 - 0
qcom/opensource/display-drivers/config/lahainadisp.conf

@@ -0,0 +1,13 @@
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_DRM_MSM_DP=y
+export CONFIG_DRM_MSM_DP_MST=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_DRM_SDE_WB=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_DRM_SDE_RSC=y
+export CONFIG_DISPLAY_BUILD=y
+export CONFIG_DRM_SDE_VM=y

+ 18 - 0
qcom/opensource/display-drivers/config/lahainadispconf.h

@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_DP 1
+#define CONFIG_DRM_MSM_DP_MST 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_SDE_WB 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_DRM_SDE_RSC 1
+#define CONFIG_DRM_SDE_VM 1

+ 13 - 0
qcom/opensource/display-drivers/config/saipdisp.conf

@@ -0,0 +1,13 @@
+export CONFIG_DRM_MSM=y
+export CONFIG_DRM_MSM_SDE=y
+export CONFIG_SYNC_FILE=y
+export CONFIG_DRM_MSM_DSI=y
+export CONFIG_DRM_MSM_DP=y
+export CONFIG_QCOM_MDSS_DP_PLL=y
+export CONFIG_DSI_PARSER=y
+export CONFIG_DRM_SDE_WB=y
+export CONFIG_DRM_MSM_REGISTER_LOGGING=y
+export CONFIG_QCOM_MDSS_PLL=y
+export CONFIG_MSM_SDE_ROTATOR=y
+export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
+export CONFIG_DRM_SDE_RSC=y

+ 19 - 0
qcom/opensource/display-drivers/config/saipdispconf.h

@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_DRM_MSM 1
+#define CONFIG_DRM_MSM_SDE 1
+#define CONFIG_SYNC_FILE 1
+#define CONFIG_DRM_MSM_DSI 1
+#define CONFIG_DRM_MSM_DP 1
+#define CONFIG_QCOM_MDSS_DP_PLL 1
+#define CONFIG_DSI_PARSER 1
+#define CONFIG_DRM_SDE_WB 1
+#define CONFIG_DRM_MSM_REGISTER_LOGGING 1
+#define CONFIG_DRM_SDE_EVTLOG_DEBUG 1
+#define CONFIG_QCOM_MDSS_PLL 1
+#define CONFIG_MSM_SDE_ROTATOR 1
+#define CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG 1
+#define CONFIG_DRM_SDE_RSC 1

+ 15 - 0
qcom/opensource/display-drivers/display_driver_board.mk

@@ -0,0 +1,15 @@
+#SPDX-License-Identifier: GPL-2.0-only
+DISPLAY_DLKM_ENABLE := true
+ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true)
+	ifeq ($(TARGET_KERNEL_DLKM_DISPLAY_OVERRIDE), false)
+		DISPLAY_DLKM_ENABLE := false
+	endif
+endif
+
+ifeq ($(DISPLAY_DLKM_ENABLE),  true)
+	ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true)
+		BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/msm_drm.ko
+		BOARD_VENDOR_RAMDISK_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/msm_drm.ko
+		BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD += $(KERNEL_MODULES_OUT)/msm_drm.ko
+	endif
+endif

+ 124 - 0
qcom/opensource/display-drivers/display_driver_build.bzl

@@ -0,0 +1,124 @@
+load("//build/kernel/kleaf:kernel.bzl", "ddk_module", "ddk_submodule")
+load("//build/bazel_common_rules/dist:dist.bzl", "copy_to_dist_dir")
+
+def _register_module_to_map(module_map, name, path, config_option, srcs, config_srcs, deps, config_deps):
+    processed_config_srcs = {}
+    nested_config = {}
+    processed_config_deps = {}
+
+    for config_src_name in config_srcs:
+        config_src = config_srcs[config_src_name]
+
+        if type(config_src) == "list":
+            processed_config_srcs[config_src_name] = {True: config_src}
+        else:
+            processed_config_srcs[config_src_name] = config_src
+        if type(config_src) == "dict":
+            nested_config = config_src
+
+            for nested_src, nest_name in nested_config.items():
+                if nested_src == True:
+                    processed_config_srcs[config_src_name] = {True: nest_name}
+                else:
+                    processed_config_srcs[nested_src] = {True: nest_name}
+
+    for config_deps_name in config_deps:
+         config_dep = config_deps[config_deps_name]
+
+         if type(config_dep) == "list":
+             processed_config_deps[config_deps_name] = {True: config_dep}
+         else:
+             processed_config_deps[config_deps_name] = config_dep
+    module = struct(
+        name = name,
+        path = path,
+        srcs = srcs,
+        config_srcs = processed_config_srcs,
+        config_option = config_option,
+        deps = deps,
+        config_deps = processed_config_deps
+    )
+
+    module_map[name] = module
+
+def _get_config_choices(map, options):
+    choices = []
+    for option in map:
+        choices.extend(map[option].get(option in options,[]))
+    return choices
+
+def _get_kernel_build_options(modules, config_options):
+    all_options = {option: True for option in config_options}
+    all_options = all_options | {module.config_option: True for module in modules if module.config_option}
+    return all_options
+
+def _get_kernel_build_module_srcs(module, options, formatter):
+    srcs = module.srcs + _get_config_choices(module.config_srcs, options)
+    module_path = "{}/".format(module.path) if module.path else ""
+    return ["{}{}".format(module_path, formatter(src)) for src in srcs]
+
+def _get_kernel_build_module_deps(module, options, formatter):
+    deps = module.deps + _get_config_choices(module.config_deps, options)
+    return [formatter(dep) for dep in deps]
+
+def display_module_entry(hdrs = []):
+    module_map = {}
+
+    def register(name, path = None, config_option = None, srcs = [], config_srcs = {}, deps = [], config_deps = {}):
+        _register_module_to_map(module_map, name, path, config_option, srcs, config_srcs, deps, config_deps)
+    return struct(
+        register = register,
+        get = module_map.get,
+        hdrs = hdrs,
+        module_map = module_map
+    )
+
+def define_target_variant_modules(target, variant, registry, modules, config_options = [], lunch_target=None):
+
+    kernel_build_hdr = "{}_{}".format(target, variant)
+    kernel_build_label = "//msm-kernel:{}".format(kernel_build_hdr)
+
+    if lunch_target != None:
+        kernel_build = "{}_{}_{}".format(target, variant, lunch_target)
+    else:
+        kernel_build = "{}_{}".format(target, variant)
+
+    modules = [registry.get(module_name) for module_name in modules]
+    options = _get_kernel_build_options(modules, config_options)
+    build_print = lambda message : print("{}: {}".format(kernel_build, message))
+    formatter = lambda s : s.replace("%b", kernel_build).replace("%t", target)
+    formatter_hdr = lambda s : s.replace("%b", kernel_build_hdr).replace("%t", target)
+    headers = ["//msm-kernel:all_headers"] + registry.hdrs
+    all_module_rules = []
+
+    for module in modules:
+        rule_name = "{}_{}".format(kernel_build, module.name)
+        module_srcs = _get_kernel_build_module_srcs(module, options, formatter)
+        print(rule_name)
+        if not module_srcs:
+            continue
+
+        ddk_submodule(
+            name = rule_name,
+            srcs = module_srcs,
+            out = "{}.ko".format(module.name),
+            deps = headers + _get_kernel_build_module_deps(module, options, formatter_hdr),
+            local_defines = options.keys(),
+        )
+        all_module_rules.append(rule_name)
+
+    ddk_module(
+        name = "{}_display_drivers".format(kernel_build),
+        kernel_build = kernel_build_label,
+        deps = all_module_rules,
+    )
+    copy_to_dist_dir(
+        name = "{}_display_drivers_dist".format(kernel_build),
+        data = [":{}_display_drivers".format(kernel_build)],
+        dist_dir = "out/target/product/{}/dlkm/lib/modules/".format(target),
+        flat = True,
+        wipe_dist_dir = False,
+        allow_duplicate_filenames = False,
+        mode_overrides = {"**/*": "644"},
+        log = "info",
+    )

+ 14 - 0
qcom/opensource/display-drivers/display_driver_product.mk

@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+DISPLAY_DLKM_ENABLE := true
+ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true)
+	ifeq ($(TARGET_KERNEL_DLKM_DISPLAY_OVERRIDE), false)
+		DISPLAY_DLKM_ENABLE := false
+	endif
+endif
+
+ifeq ($(DISPLAY_DLKM_ENABLE),  true)
+	PRODUCT_PACKAGES += msm_drm.ko
+endif
+
+DISPLAY_MODULES_DRIVER := msm_drm.ko

+ 93 - 0
qcom/opensource/display-drivers/display_kernel_headers.py

@@ -0,0 +1,93 @@
+ # Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ #
+ # This program is free software; you can redistribute it and/or modify it
+ # under the terms of the GNU General Public License version 2 as published by
+ # the Free Software Foundation.
+ #
+ # This program is distributed in the hope that it will be useful, but WITHOUT
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ # more details.
+ #
+ # You should have received a copy of the GNU General Public License along with
+ # this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import argparse
+import filecmp
+import os
+import re
+import subprocess
+import sys
+
+def run_headers_install(verbose, gen_dir, headers_install, unifdef, prefix, h):
+    if not h.startswith(prefix):
+        print('error: expected prefix [%s] on header [%s]' % (prefix, h))
+        return False
+
+    out_h = os.path.join(gen_dir, h[len(prefix):])
+    (out_h_dirname, out_h_basename) = os.path.split(out_h)
+    env = os.environ.copy()
+    env["LOC_UNIFDEF"] = unifdef
+    cmd = ["sh", headers_install, h, out_h]
+
+    if verbose:
+        print('run_headers_install: cmd is %s' % cmd)
+
+    result = subprocess.call(cmd, env=env)
+
+    if result != 0:
+        print('error: run_headers_install: cmd %s failed %d' % (cmd, result))
+        return False
+    return True
+
+def gen_display_headers(verbose, gen_dir, headers_install, unifdef, display_include_uapi):
+    error_count = 0
+    for h in display_include_uapi:
+        display_uapi_include_prefix = os.path.join(h.split('/include/uapi')[0], 'include', 'uapi') + os.sep
+        if not run_headers_install(
+                verbose, gen_dir, headers_install, unifdef,
+                display_uapi_include_prefix, h): error_count += 1
+    return error_count
+
+def main():
+    """Parse command line arguments and perform top level control."""
+    parser = argparse.ArgumentParser(
+            description=__doc__,
+            formatter_class=argparse.RawDescriptionHelpFormatter)
+
+    # Arguments that apply to every invocation of this script.
+    parser.add_argument(
+            '--verbose', action='store_true',
+            help='Print output that describes the workings of this script.')
+    parser.add_argument(
+            '--header_arch', required=True,
+            help='The arch for which to generate headers.')
+    parser.add_argument(
+            '--gen_dir', required=True,
+            help='Where to place the generated files.')
+    parser.add_argument(
+            '--display_include_uapi', required=True, nargs='*',
+            help='The list of techpack/*/include/uapi header files.')
+    parser.add_argument(
+            '--headers_install', required=True,
+            help='The headers_install tool to process input headers.')
+    parser.add_argument(
+              '--unifdef',
+              required=True,
+              help='The unifdef tool used by headers_install.')
+
+    args = parser.parse_args()
+
+    if args.verbose:
+        print('header_arch [%s]' % args.header_arch)
+        print('gen_dir [%s]' % args.gen_dir)
+        print('display_include_uapi [%s]' % args.display_include_uapi)
+        print('headers_install [%s]' % args.headers_install)
+        print('unifdef [%s]' % args.unifdef)
+
+    return gen_display_headers(args.verbose, args.gen_dir,
+            args.headers_install, args.unifdef, args.display_include_uapi)
+
+if __name__ == '__main__':
+    sys.exit(main())
+

+ 195 - 0
qcom/opensource/display-drivers/display_modules.bzl

@@ -0,0 +1,195 @@
+load(":display_driver_build.bzl", "display_module_entry")
+
+display_driver_modules = display_module_entry([":display_drivers_headers"])
+module_entry = display_driver_modules.register
+
+#---------- MSM-DRM MODULE -------------------------
+
+module_entry(
+      name = "msm_drm",
+      config_option = "CONFIG_DRM_MSM",
+      path = None,
+      config_srcs = {
+         "CONFIG_HDCP_QSEECOM": [
+            "hdcp/msm_hdcp.c",
+            "msm/dp/dp_hdcp2p2.c",
+            "msm/sde_hdcp_1x.c",
+            "msm/sde_hdcp_2x.c",
+         ],
+         "CONFIG_DRM_SDE_VM" : [
+            "msm/sde/sde_vm_common.c",
+            "msm/sde/sde_vm_primary.c",
+            "msm/sde/sde_vm_trusted.c",
+            "msm/sde/sde_vm_msgq.c",
+         ],
+         "CONFIG_DRM_MSM_DP" : [
+            "msm/dp/dp_altmode.c",
+            "msm/dp/dp_parser.c",
+            "msm/dp/dp_power.c",
+            "msm/dp/dp_catalog.c",
+            "msm/dp/dp_catalog_v420.c",
+            "msm/dp/dp_catalog_v200.c",
+            "msm/dp/dp_aux.c",
+            "msm/dp/dp_panel.c",
+            "msm/dp/dp_link.c",
+            "msm/dp/dp_ctrl.c",
+            "msm/dp/dp_audio.c",
+            "msm/dp/dp_debug.c",
+            "msm/dp/dp_hpd.c",
+            "msm/dp/dp_aux_bridge.c",
+            "msm/dp/dp_bridge_hpd.c",
+            "msm/dp/dp_mst_sim.c",
+            "msm/dp/dp_mst_sim_helper.c",
+            "msm/dp/dp_gpio_hpd.c",
+            "msm/dp/dp_lphw_hpd.c",
+            "msm/dp/dp_display.c",
+            "msm/dp/dp_drm.c",
+            "msm/dp/dp_pll.c",
+            "msm/dp/dp_pll_5nm.c",
+            "msm/dp/dp_pll_4nm.c",
+         ],
+         "CONFIG_DRM_MSM_DP_MST" : [
+            "msm/dp/dp_mst_drm.c",
+         ],
+         "CONFIG_DRM_MSM_DP_USBPD_LEGACY" : [
+            "msm/dp/dp_usbpd.c",
+         ],
+         "CONFIG_DRM_MSM_SDE" : [
+            "msm/sde/sde_crtc.c",
+            "msm/sde/sde_encoder.c",
+            "msm/sde/sde_encoder_dce.c",
+            "msm/sde/sde_encoder_phys_vid.c",
+            "msm/sde/sde_encoder_phys_cmd.c",
+            "msm/sde/sde_irq.c",
+            "msm/sde/sde_core_irq.c",
+            "msm/sde/sde_core_perf.c",
+            "msm/sde/sde_rm.c",
+            "msm/sde/sde_kms_utils.c",
+            "msm/sde/sde_kms.c",
+            "msm/sde/sde_plane.c",
+            "msm/sde/sde_connector.c",
+            "msm/sde/sde_color_processing.c",
+            "msm/sde/sde_vbif.c",
+            "msm/sde_dbg.c",
+            "msm/sde_dbg_evtlog.c",
+            "msm/sde_io_util.c",
+            "msm/sde_vm_event.c",
+            "msm/sde/sde_hw_reg_dma_v1_color_proc.c",
+            "msm/sde/sde_hw_color_proc_v4.c",
+            "msm/sde/sde_hw_ad4.c",
+            "msm/sde/sde_hw_uidle.c",
+            "msm/sde_edid_parser.c",
+            "msm/sde/sde_hw_catalog.c",
+            "msm/sde/sde_hw_cdm.c",
+            "msm/sde/sde_hw_dspp.c",
+            "msm/sde/sde_hw_intf.c",
+            "msm/sde/sde_hw_lm.c",
+            "msm/sde/sde_hw_ctl.c",
+            "msm/sde/sde_hw_util.c",
+            "msm/sde/sde_hw_sspp.c",
+            "msm/sde/sde_hw_wb.c",
+            "msm/sde/sde_hw_pingpong.c",
+            "msm/sde/sde_hw_top.c",
+            "msm/sde/sde_hw_interrupts.c",
+            "msm/sde/sde_hw_vbif.c",
+            "msm/sde/sde_formats.c",
+            "msm/sde_power_handle.c",
+            "msm/sde/sde_hw_color_processing_v1_7.c",
+            "msm/sde/sde_reg_dma.c",
+            "msm/sde/sde_hw_reg_dma_v1.c",
+            "msm/sde/sde_hw_dsc.c",
+            "msm/sde/sde_hw_dsc_1_2.c",
+            "msm/sde/sde_hw_vdc.c",
+            "msm/sde/sde_hw_ds.c",
+            "msm/sde/sde_fence.c",
+            "msm/sde/sde_hw_qdss.c",
+            "msm/sde_dsc_helper.c",
+            "msm/sde_vdc_helper.c",
+            "msm/sde/sde_hw_dnsc_blur.c",
+            "msm/sde/sde_hw_rc.c",
+         ],
+         "CONFIG_DRM_SDE_WB" : [
+            "msm/sde/sde_wb.c",
+            "msm/sde/sde_encoder_phys_wb.c"
+         ],
+         "CONFIG_DRM_SDE_RSC" : [
+            "msm/sde_rsc.c",
+            "msm/sde_rsc_hw.c",
+            "msm/sde_rsc_hw_v3.c",
+         ],
+         "CONFIG_DRM_MSM_DSI" : [
+            "msm/dsi/dsi_phy.c",
+            "msm/dsi/dsi_pwr.c",
+            "msm/dsi/dsi_phy_hw_v3_0.c",
+            "msm/dsi/dsi_phy_hw_v4_0.c",
+            "msm/dsi/dsi_phy_hw_v5_0.c",
+            "msm/dsi/dsi_phy_timing_calc.c",
+            "msm/dsi/dsi_phy_timing_v3_0.c",
+            "msm/dsi/dsi_phy_timing_v4_0.c",
+            "msm/dsi/dsi_pll.c",
+            "msm/dsi/dsi_pll_5nm.c",
+            "msm/dsi/dsi_pll_4nm.c",
+            "msm/dsi/dsi_ctrl_hw_cmn.c",
+            "msm/dsi/dsi_ctrl_hw_2_2.c",
+            "msm/dsi/dsi_ctrl.c",
+            "msm/dsi/dsi_catalog.c",
+            "msm/dsi/dsi_drm.c",
+            "msm/dsi/dsi_display.c",
+            "msm/dsi/dsi_panel.c",
+            "msm/dsi/dsi_clk_manager.c",
+            "msm/dsi/dsi_display_test.c",
+         ],
+         "CONFIG_DSI_PARSER" : [
+            "msm/dsi/dsi_parser.c",
+         ],
+         "CONFIG_THERMAL_OF" : [
+            "msm/msm_cooling_device.c",
+          ],
+         "CONFIG_DRM_MSM" : [
+            "msm/msm_atomic.c",
+            "msm/msm_fb.c",
+            "msm/msm_drv.c",
+            "msm/msm_gem.c",
+            "msm/msm_gem_prime.c",
+            "msm/msm_gem_vma.c",
+            "msm/msm_smmu.c",
+            "msm/msm_prop.c",
+         ],
+         "CONFIG_MSM_SDE_ROTATOR":{
+            True: [
+               "rotator/sde_rotator_dev.c",
+               "rotator/sde_rotator_core.c",
+               "rotator/sde_rotator_base.c",
+               "rotator/sde_rotator_formats.c",
+               "rotator/sde_rotator_util.c",
+               "rotator/sde_rotator_io_util.c",
+               "rotator/sde_rotator_smmu.c",
+               "rotator/sde_rotator_r1_wb.c",
+               "rotator/sde_rotator_r1_pipe.c",
+               "rotator/sde_rotator_r1_ctl.c",
+               "rotator/sde_rotator_r1.c",
+               "rotator/sde_rotator_r3.c"],
+            "CONFIG_SYNC_FILE":["rotator/sde_rotator_sync.c"],
+            "CONFIG_DEBUG_FS":["rotator/sde_rotator_debug.c",
+                              "rotator/sde_rotator_r1_debug.c",
+                              "rotator/sde_rotator_r3_debug.c"],
+            },
+      },
+      config_deps = {
+        "CONFIG_HDCP_QSEECOM" : [
+            "//vendor/qcom/opensource/securemsm-kernel:%b_hdcp_qseecom_dlkm"
+        ],
+        "CONFIG_MSM_MMRM" : [
+            "//vendor/qcom/opensource/mmrm-driver:%b_mmrm_driver"
+        ],
+        "CONFIG_QCOM_SPEC_SYNC" : [
+            "//vendor/qcom/opensource/mm-drivers/sync_fence:%b_sync_fence"
+        ],
+        "CONFIG_QTI_HW_FENCE" : [
+            "//vendor/qcom/opensource/mm-drivers/hw_fence:%b_msm_hw_fence"
+        ],
+        "CONFIG_MSM_EXT_DISPLAY" : [
+            "//vendor/qcom/opensource/mm-drivers/msm_ext_display:%b_msm_ext_display"
+        ],
+       }
+)

+ 342 - 0
qcom/opensource/display-drivers/hdcp/msm_hdcp.c

@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt)	"[msm-hdcp] %s: " fmt, __func__
+
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/uaccess.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/msm_hdcp.h>
+#include <linux/of.h>
+
+#define CLASS_NAME "hdcp"
+#define DRIVER_NAME "msm_hdcp"
+
+struct msm_hdcp {
+	struct platform_device *pdev;
+	dev_t dev_num;
+	struct cdev cdev;
+	struct class *class;
+	struct device *device;
+	struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
+	u32 tp_msgid;
+	void *client_ctx;
+	void (*cb)(void *ctx, u8 data);
+};
+
+void msm_hdcp_register_cb(struct device *dev, void *ctx,
+		void (*cb)(void *ctx, u8 data))
+{
+	struct msm_hdcp *hdcp = NULL;
+
+	if (!dev) {
+		pr_err("invalid device pointer\n");
+		return;
+	}
+
+	hdcp = dev_get_drvdata(dev);
+	if (!hdcp) {
+		pr_err("invalid driver pointer\n");
+		return;
+	}
+
+	hdcp->cb = cb;
+	hdcp->client_ctx = ctx;
+}
+EXPORT_SYMBOL(msm_hdcp_register_cb);
+
+void msm_hdcp_notify_topology(struct device *dev)
+{
+	char *envp[4];
+	char tp[SZ_16];
+	char ver[SZ_16];
+	struct msm_hdcp *hdcp = NULL;
+
+	if (!dev) {
+		pr_err("invalid device pointer\n");
+		return;
+	}
+
+	hdcp = dev_get_drvdata(dev);
+	if (!hdcp) {
+		pr_err("invalid driver pointer\n");
+		return;
+	}
+
+	snprintf(tp, SZ_16, "%d", DOWN_CHECK_TOPOLOGY);
+	snprintf(ver, SZ_16, "%d", HDCP_V1_TX);
+
+	envp[0] = "HDCP_MGR_EVENT=MSG_READY";
+	envp[1] = tp;
+	envp[2] = ver;
+	envp[3] = NULL;
+
+	kobject_uevent_env(&hdcp->device->kobj, KOBJ_CHANGE, envp);
+}
+EXPORT_SYMBOL(msm_hdcp_notify_topology);
+
+void msm_hdcp_cache_repeater_topology(struct device *dev,
+			struct HDCP_V2V1_MSG_TOPOLOGY *tp)
+{
+	struct msm_hdcp *hdcp = NULL;
+
+	if (!dev || !tp) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	hdcp = dev_get_drvdata(dev);
+	if (!hdcp) {
+		pr_err("invalid driver pointer\n");
+		return;
+	}
+
+	memcpy(&hdcp->cached_tp, tp,
+		   sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
+}
+EXPORT_SYMBOL(msm_hdcp_cache_repeater_topology);
+
+static ssize_t tp_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	ssize_t ret = 0;
+	struct msm_hdcp *hdcp = NULL;
+
+	if (!dev) {
+		pr_err("invalid device pointer\n");
+		return -ENODEV;
+	}
+
+	hdcp = dev_get_drvdata(dev);
+	if (!hdcp) {
+		pr_err("invalid driver pointer\n");
+		return -ENODEV;
+	}
+
+	switch (hdcp->tp_msgid) {
+	case DOWN_CHECK_TOPOLOGY:
+	case DOWN_REQUEST_TOPOLOGY:
+		buf[MSG_ID_IDX]   = hdcp->tp_msgid;
+		buf[RET_CODE_IDX] = HDCP_AUTHED;
+		ret = HEADER_LEN;
+
+		memcpy(buf + HEADER_LEN, &hdcp->cached_tp,
+			   sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
+
+		ret += sizeof(struct HDCP_V2V1_MSG_TOPOLOGY);
+
+		/* reset the flag once the data is written back to user space */
+		hdcp->tp_msgid = DOWN_REQUEST_TOPOLOGY;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static ssize_t tp_store(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	int msgid = 0;
+	ssize_t ret = count;
+	struct msm_hdcp *hdcp = NULL;
+
+	if (!dev) {
+		pr_err("invalid device pointer\n");
+		return -ENODEV;
+	}
+
+	hdcp = dev_get_drvdata(dev);
+	if (!hdcp) {
+		pr_err("invalid driver pointer\n");
+		return -ENODEV;
+	}
+
+	msgid = buf[0];
+
+	switch (msgid) {
+	case DOWN_CHECK_TOPOLOGY:
+	case DOWN_REQUEST_TOPOLOGY:
+		hdcp->tp_msgid = msgid;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static ssize_t min_level_change_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc;
+	int min_enc_lvl;
+	ssize_t ret = count;
+	struct msm_hdcp *hdcp = NULL;
+
+	if (!dev) {
+		pr_err("invalid device pointer\n");
+		return -ENODEV;
+	}
+
+	hdcp = dev_get_drvdata(dev);
+	if (!hdcp) {
+		pr_err("invalid driver pointer\n");
+		return -ENODEV;
+	}
+
+	rc = kstrtoint(buf, 10, &min_enc_lvl);
+	if (rc) {
+		pr_err("kstrtoint failed. rc=%d\n", rc);
+		return -EINVAL;
+	}
+
+	if (hdcp->cb && hdcp->client_ctx)
+		hdcp->cb(hdcp->client_ctx, min_enc_lvl);
+
+	return ret;
+}
+
+static DEVICE_ATTR_RW(tp);
+
+static DEVICE_ATTR_WO(min_level_change);
+
+static struct attribute *msm_hdcp_fs_attrs[] = {
+	&dev_attr_tp.attr,
+	&dev_attr_min_level_change.attr,
+	NULL
+};
+
+static struct attribute_group msm_hdcp_fs_attr_group = {
+	.attrs = msm_hdcp_fs_attrs
+};
+
+static int msm_hdcp_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int msm_hdcp_close(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static const struct file_operations msm_hdcp_fops = {
+	.owner = THIS_MODULE,
+	.open = msm_hdcp_open,
+	.release = msm_hdcp_close,
+};
+
+static const struct of_device_id msm_hdcp_dt_match[] = {
+	{ .compatible = "qcom,msm-hdcp",},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, msm_hdcp_dt_match);
+
+static int msm_hdcp_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct msm_hdcp *hdcp;
+
+	hdcp = devm_kzalloc(&pdev->dev, sizeof(struct msm_hdcp), GFP_KERNEL);
+	if (!hdcp)
+		return -ENOMEM;
+
+	hdcp->pdev = pdev;
+
+	platform_set_drvdata(pdev, hdcp);
+
+	ret = alloc_chrdev_region(&hdcp->dev_num, 0, 1, DRIVER_NAME);
+	if (ret  < 0) {
+		pr_err("alloc_chrdev_region failed ret = %d\n", ret);
+		return ret;
+	}
+
+	hdcp->class = class_create(THIS_MODULE, CLASS_NAME);
+	if (IS_ERR(hdcp->class)) {
+		ret = PTR_ERR(hdcp->class);
+		pr_err("couldn't create class rc = %d\n", ret);
+		goto error_class_create;
+	}
+
+	hdcp->device = device_create(hdcp->class, NULL,
+		hdcp->dev_num, hdcp, DRIVER_NAME);
+	if (IS_ERR(hdcp->device)) {
+		ret = PTR_ERR(hdcp->device);
+		pr_err("device_create failed %d\n", ret);
+		goto error_class_device_create;
+	}
+
+	cdev_init(&hdcp->cdev, &msm_hdcp_fops);
+	ret = cdev_add(&hdcp->cdev, MKDEV(MAJOR(hdcp->dev_num), 0), 1);
+	if (ret < 0) {
+		pr_err("cdev_add failed %d\n", ret);
+		goto error_cdev_add;
+	}
+
+	ret = sysfs_create_group(&hdcp->device->kobj, &msm_hdcp_fs_attr_group);
+	if (ret)
+		pr_err("unable to register msm_hdcp sysfs nodes\n");
+
+	hdcp->tp_msgid = DOWN_REQUEST_TOPOLOGY;
+
+	return 0;
+error_cdev_add:
+	device_destroy(hdcp->class, hdcp->dev_num);
+error_class_device_create:
+	class_destroy(hdcp->class);
+error_class_create:
+	unregister_chrdev_region(hdcp->dev_num, 1);
+	return ret;
+}
+
+static int msm_hdcp_remove(struct platform_device *pdev)
+{
+	struct msm_hdcp *hdcp;
+
+	hdcp = platform_get_drvdata(pdev);
+	if (!hdcp)
+		return -ENODEV;
+
+	sysfs_remove_group(&hdcp->device->kobj,
+	&msm_hdcp_fs_attr_group);
+	cdev_del(&hdcp->cdev);
+	device_destroy(hdcp->class, hdcp->dev_num);
+	class_destroy(hdcp->class);
+	unregister_chrdev_region(hdcp->dev_num, 1);
+
+	return 0;
+}
+
+static struct platform_driver msm_hdcp_driver = {
+	.probe = msm_hdcp_probe,
+	.remove = msm_hdcp_remove,
+	.driver = {
+		.name = "msm_hdcp",
+		.of_match_table = msm_hdcp_dt_match,
+		.pm = NULL,
+	}
+};
+
+void __init msm_hdcp_register(void)
+{
+	platform_driver_register(&msm_hdcp_driver);
+}
+
+void __exit msm_hdcp_unregister(void)
+{
+	platform_driver_unregister(&msm_hdcp_driver);
+}

+ 2 - 0
qcom/opensource/display-drivers/include/Kbuild

@@ -0,0 +1,2 @@
+# Top-level Makefile calls into asm-$(ARCH)
+# List only non-arch directories below

+ 6 - 0
qcom/opensource/display-drivers/include/linux/Kbuild

@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
+
+header-y += msm_hdcp.h
+header-y += sde_io_util.h
+header-y += sde_rsc.h
+

+ 33 - 0
qcom/opensource/display-drivers/include/linux/msm_hdcp.h

@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __MSM_HDCP_H
+#define __MSM_HDCP_H
+#include <linux/types.h>
+#include "hdcp/msm_hdmi_hdcp_mgr.h"
+
+#if IS_ENABLED(CONFIG_HDCP_QSEECOM)
+void msm_hdcp_notify_topology(struct device *dev);
+void msm_hdcp_cache_repeater_topology(struct device *dev,
+			struct HDCP_V2V1_MSG_TOPOLOGY *tp);
+void msm_hdcp_register_cb(struct device *dev, void *ctx,
+	void (*cb)(void *ctx, u8 data));
+#else
+static inline void msm_hdcp_notify_topology(struct device *dev)
+{
+}
+
+static inline void msm_hdcp_cache_repeater_topology(struct device *dev,
+			struct HDCP_V2V1_MSG_TOPOLOGY *tp)
+{
+}
+
+static inline void msm_hdcp_register_cb(struct device *dev, void *ctx,
+	void (*cb)(void *ctx, u8 data))
+{
+}
+#endif /* CONFIG_HDCP_QSEECOM*/
+
+#endif /* __MSM_HDCP_H */

+ 136 - 0
qcom/opensource/display-drivers/include/linux/sde_io_util.h

@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2012, 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __SDE_IO_UTIL_H__
+#define __SDE_IO_UTIL_H__
+
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/soc/qcom/msm_mmrm.h>
+
+#ifdef DEBUG
+#define DEV_DBG(fmt, args...)   pr_err(fmt, ##args)
+#else
+#define DEV_DBG(fmt, args...)   pr_debug(fmt, ##args)
+#endif
+#define DEV_INFO(fmt, args...)  pr_info(fmt, ##args)
+#define DEV_WARN(fmt, args...)  pr_warn(fmt, ##args)
+#define DEV_ERR(fmt, args...)   pr_err(fmt, ##args)
+
+struct dss_io_data {
+	u32 len;
+	void __iomem *base;
+};
+
+void dss_reg_w(struct dss_io_data *io, u32 offset, u32 value, u32 debug);
+u32 dss_reg_r(struct dss_io_data *io, u32 offset, u32 debug);
+void dss_reg_dump(void __iomem *base, u32 len, const char *prefix, u32 debug);
+
+#define DSS_REG_W_ND(io, offset, val)  dss_reg_w(io, offset, val, false)
+#define DSS_REG_W(io, offset, val)     dss_reg_w(io, offset, val, true)
+#define DSS_REG_R_ND(io, offset)       dss_reg_r(io, offset, false)
+#define DSS_REG_R(io, offset)          dss_reg_r(io, offset, true)
+
+enum dss_vreg_type {
+	DSS_REG_LDO,
+	DSS_REG_VS,
+};
+
+struct dss_vreg {
+	struct regulator *vreg; /* vreg handle */
+	char vreg_name[32];
+	int min_voltage;
+	int max_voltage;
+	int enable_load;
+	int disable_load;
+	int pre_on_sleep;
+	int post_on_sleep;
+	int pre_off_sleep;
+	int post_off_sleep;
+};
+
+struct dss_gpio {
+	unsigned int gpio;
+	unsigned int value;
+	char gpio_name[32];
+};
+
+enum dss_clk_type {
+	DSS_CLK_AHB, /* no set rate. rate controlled through rpm */
+	DSS_CLK_PCLK,
+	DSS_CLK_MMRM, /* set rate called through mmrm driver */
+	DSS_CLK_OTHER,
+};
+
+struct dss_clk_mmrm_cb {
+	void *phandle;
+	struct dss_clk *clk;
+};
+
+struct dss_clk_mmrm {
+	unsigned int clk_id;
+	unsigned int flags;
+	struct mmrm_client *mmrm_client;
+	struct dss_clk_mmrm_cb *mmrm_cb_data;
+	unsigned long mmrm_requested_clk;
+	wait_queue_head_t mmrm_cb_wq;
+};
+
+struct dss_clk {
+	struct clk *clk; /* clk handle */
+	char clk_name[32];
+	enum dss_clk_type type;
+	unsigned long rate;
+	unsigned long max_rate;
+	struct dss_clk_mmrm mmrm;
+};
+
+struct dss_module_power {
+	unsigned int num_vreg;
+	struct dss_vreg *vreg_config;
+	unsigned int num_gpio;
+	struct dss_gpio *gpio_config;
+	unsigned int num_clk;
+	struct dss_clk *clk_config;
+};
+
+int msm_dss_ioremap_byname(struct platform_device *pdev,
+	struct dss_io_data *io_data, const char *name);
+void msm_dss_iounmap(struct dss_io_data *io_data);
+int msm_dss_get_io_mem(struct platform_device *pdev,
+		       struct list_head *mem_list);
+void msm_dss_clean_io_mem(struct list_head *mem_list);
+int msm_dss_get_pmic_io_mem(struct platform_device *pdev,
+		       struct list_head *mem_list);
+int msm_dss_get_gpio_io_mem(const int gpio_pin, struct list_head *mem_list);
+int msm_dss_get_io_irq(struct platform_device *pdev,
+		       struct list_head *irq_list, u32 label);
+void msm_dss_clean_io_irq(struct list_head *irq_list);
+int msm_dss_enable_gpio(struct dss_gpio *in_gpio, int num_gpio, int enable);
+int msm_dss_gpio_enable(struct dss_gpio *in_gpio, int num_gpio, int enable);
+
+int msm_dss_get_vreg(struct device *dev, struct dss_vreg *in_vreg,
+	int num_vreg, int enable);
+int msm_dss_enable_vreg(struct dss_vreg *in_vreg, int num_vreg,	int enable);
+
+int msm_dss_get_clk(struct device *dev, struct dss_clk *clk_arry, int num_clk);
+int msm_dss_mmrm_register(struct device *dev, struct dss_module_power *mp,
+	int (*cb_fnc)(struct mmrm_client_notifier_data *data), void *phandle,
+	bool *mmrm_enable);
+void msm_dss_mmrm_deregister(struct device *dev, struct dss_module_power *mp);
+void msm_dss_put_clk(struct dss_clk *clk_arry, int num_clk);
+int msm_dss_clk_set_rate(struct dss_clk *clk_arry, int num_clk);
+int msm_dss_single_clk_set_rate(struct dss_clk *clk);
+int msm_dss_enable_clk(struct dss_clk *clk_arry, int num_clk, int enable);
+
+int sde_i2c_byte_read(struct i2c_client *client, uint8_t slave_addr,
+		       uint8_t reg_offset, uint8_t *read_buf);
+int sde_i2c_byte_write(struct i2c_client *client, uint8_t slave_addr,
+			uint8_t reg_offset, uint8_t *value);
+
+#endif /* __SDE_IO_UTIL_H__ */

+ 360 - 0
qcom/opensource/display-drivers/include/linux/sde_rsc.h

@@ -0,0 +1,360 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _SDE_RSC_H_
+#define _SDE_RSC_H_
+
+#include <linux/kernel.h>
+
+/* primary display rsc index */
+#define SDE_RSC_INDEX		0
+
+#define MAX_RSC_CLIENT_NAME_LEN 128
+#define NUM_RSC_PROFILING_COUNTERS 3
+
+/* DRM Object IDs are numbered excluding 0, use 0 to indicate invalid CRTC */
+#define SDE_RSC_INVALID_CRTC_ID 0
+
+/**
+ * event will be triggered before sde core power collapse,
+ * mdss gdsc is still on
+ */
+#define SDE_RSC_EVENT_PRE_CORE_PC 0x1
+/**
+ * event will be triggered after sde core collapse complete,
+ * mdss gdsc is off now
+ */
+#define SDE_RSC_EVENT_POST_CORE_PC 0x2
+/**
+ * event will be triggered before restoring the sde core from power collapse,
+ * mdss gdsc is still off
+ */
+#define SDE_RSC_EVENT_PRE_CORE_RESTORE 0x4
+/**
+ * event will be triggered after restoring the sde core from power collapse,
+ * mdss gdsc is on now
+ */
+#define SDE_RSC_EVENT_POST_CORE_RESTORE 0x8
+/**
+ * event attached with solver state enabled
+ * all clients in clk_state or cmd_state
+ */
+#define SDE_RSC_EVENT_SOLVER_ENABLED 0x10
+/**
+ * event attached with solver state disabled
+ * one of the client requested for vid state
+ */
+#define SDE_RSC_EVENT_SOLVER_DISABLED 0x20
+
+/**
+ * sde_rsc_client_type: sde rsc client type information
+ * SDE_RSC_PRIMARY_DISP_CLIENT:	A primary display client which can request
+ *				vid or cmd state switch.
+ * SDE_RSC_EXTERNAL_DISPLAY_CLIENT:An external display client which can
+ *                              request only clk state switch.
+ * SDE_RSC_CLK_CLIENT:		A clk client request for only rsc clocks
+ *				enabled and mode_2 exit state.
+ */
+enum sde_rsc_client_type {
+	SDE_RSC_PRIMARY_DISP_CLIENT,
+	SDE_RSC_EXTERNAL_DISP_CLIENT,
+	SDE_RSC_CLK_CLIENT,
+	SDE_RSC_INVALID_CLIENT,
+};
+
+/**
+ * sde_rsc_state: sde rsc state information
+ * SDE_RSC_IDLE_STATE: A client requests for idle state when there is no
+ *                    pixel or cmd transfer expected. An idle vote from
+ *                    all clients lead to power collapse state.
+ * SDE_RSC_CLK_STATE:  A client requests for clk state when it wants to
+ *                    only avoid mode-2 entry/exit. For ex: V4L2 driver,
+ *                    sde power handle, etc.
+ * SDE_RSC_CMD_STATE:  A client requests for cmd state when it wants to
+ *                    enable the solver mode.
+ * SDE_RSC_VID_STATE:  A client requests for vid state it wants to avoid
+ *                    solver enable because client is fetching data from
+ *                    continuously.
+ */
+enum sde_rsc_state {
+	SDE_RSC_IDLE_STATE,
+	SDE_RSC_CLK_STATE,
+	SDE_RSC_CMD_STATE,
+	SDE_RSC_VID_STATE,
+};
+
+/**
+ * struct sde_rsc_client: stores the rsc client for sde driver
+ * @name:	name of the client
+ * @current_state:   current client state
+ * @crtc_id:		crtc_id associated with this rsc client.
+ * @rsc_index:	rsc index of a client - only index "0" valid.
+ * @id:		Index of client. It will be assigned during client_create call
+ * @client_type: check sde_rsc_client_type information
+ * @list:	list to attach client master list
+ */
+struct sde_rsc_client {
+	char name[MAX_RSC_CLIENT_NAME_LEN];
+	short current_state;
+	int crtc_id;
+	u32 rsc_index;
+	u32 id;
+	enum sde_rsc_client_type client_type;
+	struct list_head list;
+};
+
+/**
+ * struct sde_rsc_event: local event registration entry structure
+ * @cb_func:	Pointer to desired callback function
+ * @usr:	User pointer to pass to callback on event trigger
+ * @rsc_index:	rsc index of a client - only index "0" valid.
+ * @event_type:	refer comments in event_register
+ * @list:	list to attach event master list
+ */
+struct sde_rsc_event {
+	void (*cb_func)(uint32_t event_type, void *usr);
+	void *usr;
+	u32 rsc_index;
+	uint32_t event_type;
+	struct list_head list;
+};
+
+/**
+ * struct sde_rsc_cmd_config: provides panel configuration to rsc
+ * when client is command mode. It is not required to set it during
+ * video mode.
+ *
+ * @fps:	panel te interval
+ * @vtotal:	current vertical total (height + vbp + vfp)
+ * @jitter_numer: panel jitter numerator value. This config causes rsc/solver
+ *                early before te. Default is 0.8% jitter.
+ * @jitter_denom: panel jitter denominator.
+ * @prefill_lines:	max prefill lines based on panel
+ */
+struct sde_rsc_cmd_config {
+	u32 fps;
+	u32 vtotal;
+	u32 jitter_numer;
+	u32 jitter_denom;
+	u32 prefill_lines;
+};
+
+#if IS_ENABLED(CONFIG_DRM_SDE_RSC)
+/**
+ * sde_rsc_client_create() - create the client for sde rsc.
+ * Different displays like DSI, HDMI, DP, WB, etc should call this
+ * api to register their vote for rpmh. They still need to vote for
+ * power handle to get the clocks.
+
+ * @rsc_index:   A client will be created on this RSC. As of now only
+ *               SDE_RSC_INDEX is valid rsc index.
+ * @name:	 Caller needs to provide some valid string to identify
+ *               the client. "primary", "dp", "hdmi" are suggested name.
+ * @client_type: check client_type enum for information
+ * @vsync_source: This parameter is only valid for primary display. It provides
+ *               vsync source information
+ *
+ * Return: client node pointer.
+ */
+struct sde_rsc_client *sde_rsc_client_create(u32 rsc_index, char *name,
+	enum sde_rsc_client_type client_type, u32 vsync_source);
+
+/**
+ * sde_rsc_client_destroy() - Destroy the sde rsc client.
+ *
+ * @client:	 Client pointer provided by sde_rsc_client_create().
+ *
+ * Return: none
+ */
+void sde_rsc_client_destroy(struct sde_rsc_client *client);
+
+/**
+ * sde_rsc_client_state_update() - rsc client state update
+ * Video mode, cmd mode and clk state are supported as modes. A client need to
+ * set this property during panel time. A switching client can set the
+ * property to change the state
+ *
+ * @client:	 Client pointer provided by sde_rsc_client_create().
+ * @state:	 Client state - video/cmd
+ * @config:	 fps, vtotal, porches, etc configuration for command mode
+ *               panel
+ * @crtc_id:	 current client's crtc id
+ * @wait_vblank_crtc_id:	Output parameter. If set to non-zero, rsc hw
+ *				state update requires a wait for one vblank on
+ *				the primary crtc. In that case, this output
+ *				param will be set to the crtc on which to wait.
+ *				If SDE_RSC_INVALID_CRTC_ID, no wait necessary
+ *
+ * Return: error code.
+ */
+int sde_rsc_client_state_update(struct sde_rsc_client *client,
+	enum sde_rsc_state state,
+	struct sde_rsc_cmd_config *config, int crtc_id,
+	int *wait_vblank_crtc_id);
+
+/**
+ * sde_rsc_client_get_vsync_refcount() - returns the status of the vsync
+ * refcount, to signal if the client needs to reset the refcounting logic
+ * @client:	 Client pointer provided by sde_rsc_client_create().
+ *
+ * Return: true if the state update has completed.
+ */
+int sde_rsc_client_get_vsync_refcount(
+		struct sde_rsc_client *caller_client);
+
+/**
+ * sde_rsc_client_reset_vsync_refcount() - reduces the refcounting
+ * logic that waits for the vsync.
+ * @client:	 Client pointer provided by sde_rsc_client_create().
+ *
+ * Return: true if the state update has completed.
+ */
+int sde_rsc_client_reset_vsync_refcount(
+		struct sde_rsc_client *caller_client);
+
+/**
+ * sde_rsc_client_is_state_update_complete() - check if state update is complete
+ * RSC state transition is not complete until HW receives VBLANK signal. This
+ * function checks RSC HW to determine whether that signal has been received.
+ * @client:	 Client pointer provided by sde_rsc_client_create().
+ *
+ * Return: true if the state update has completed.
+ */
+bool sde_rsc_client_is_state_update_complete(
+		struct sde_rsc_client *caller_client);
+
+/**
+ * sde_rsc_client_vote() - stores ab/ib vote for rsc client
+ *
+ * @client:	 Client pointer provided by sde_rsc_client_create().
+ * @bus_id:	 data bus identifier
+ * @ab:		 aggregated bandwidth vote from client.
+ * @ib:		 instant bandwidth vote from client.
+ *
+ * Return: error code.
+ */
+int sde_rsc_client_vote(struct sde_rsc_client *caller_client,
+	u32 bus_id, u64 ab_vote, u64 ib_vote);
+
+/**
+ * sde_rsc_register_event - register a callback function for an event
+ * @rsc_index:   A client will be created on this RSC. As of now only
+ *               SDE_RSC_INDEX is valid rsc index.
+ * @event_type:  event type to register; client sets 0x3 if it wants
+ *               to register for CORE_PC and CORE_RESTORE - both events.
+ * @cb_func:     Pointer to desired callback function
+ * @usr:         User pointer to pass to callback on event trigger
+ * Returns: sde_rsc_event pointer on success
+ */
+struct sde_rsc_event *sde_rsc_register_event(int rsc_index, uint32_t event_type,
+		void (*cb_func)(uint32_t event_type, void *usr), void *usr);
+
+/**
+ * sde_rsc_unregister_event - unregister callback for an event
+ * @sde_rsc_event: event returned by sde_rsc_register_event
+ */
+void sde_rsc_unregister_event(struct sde_rsc_event *event);
+
+/**
+ * is_sde_rsc_available - check if display rsc available.
+ * @rsc_index:   A client will be created on this RSC. As of now only
+ *               SDE_RSC_INDEX is valid rsc index.
+ * Returns: true if rsc is available; false in all other cases
+ */
+bool is_sde_rsc_available(int rsc_index);
+
+/**
+ * get_sde_rsc_current_state - gets the current state of sde rsc.
+ * @rsc_index:   A client will be created on this RSC. As of now only
+ *               SDE_RSC_INDEX is valid rsc index.
+ * Returns: current state if rsc available; SDE_RSC_IDLE_STATE for
+ *          all other cases
+ */
+enum sde_rsc_state get_sde_rsc_current_state(int rsc_index);
+
+/**
+ * sde_rsc_client_trigger_vote() - triggers ab/ib vote for rsc client
+ *
+ * @client:	 Client pointer provided by sde_rsc_client_create().
+ * @delta_vote:  if bw vote is increased or decreased
+ *
+ * Return: error code.
+ */
+int sde_rsc_client_trigger_vote(struct sde_rsc_client *caller_client,
+	bool delta_vote);
+
+#else
+
+static inline struct sde_rsc_client *sde_rsc_client_create(u32 rsc_index,
+	char *name, enum sde_rsc_client_type client_type, u32 vsync_source)
+{
+	return NULL;
+}
+
+static inline void sde_rsc_client_destroy(struct sde_rsc_client *client)
+{
+}
+
+static inline int sde_rsc_client_state_update(struct sde_rsc_client *client,
+	enum sde_rsc_state state,
+	struct sde_rsc_cmd_config *config, int crtc_id,
+	int *wait_vblank_crtc_id)
+{
+	return 0;
+}
+
+static inline int sde_rsc_client_get_vsync_refcount(
+		struct sde_rsc_client *caller_client)
+{
+	return 0;
+}
+
+static inline int sde_rsc_client_reset_vsync_refcount(
+		struct sde_rsc_client *caller_client)
+{
+	return 0;
+}
+
+static inline bool sde_rsc_client_is_state_update_complete(
+		struct sde_rsc_client *caller_client)
+{
+	return false;
+}
+
+static inline int sde_rsc_client_vote(struct sde_rsc_client *caller_client,
+	u32 bus_id, u64 ab_vote, u64 ib_vote)
+{
+	return 0;
+}
+
+static inline struct sde_rsc_event *sde_rsc_register_event(int rsc_index,
+		uint32_t event_type,
+		void (*cb_func)(uint32_t event_type, void *usr), void *usr)
+{
+	return NULL;
+}
+
+static inline void sde_rsc_unregister_event(struct sde_rsc_event *event)
+{
+}
+
+static inline bool is_sde_rsc_available(int rsc_index)
+{
+	return false;
+}
+
+static inline enum sde_rsc_state get_sde_rsc_current_state(int rsc_index)
+{
+	return SDE_RSC_IDLE_STATE;
+}
+
+static inline int sde_rsc_client_trigger_vote(
+	struct sde_rsc_client *caller_client, bool delta_vote)
+{
+	return 0;
+}
+#endif /* CONFIG_DRM_SDE_RSC */
+
+#endif /* _SDE_RSC_H_ */

+ 97 - 0
qcom/opensource/display-drivers/include/linux/sde_vm_event.h

@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __SDE_VM_EVENT_H__
+#define __SDE_VM_EVENT_H__
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <drm/drm_device.h>
+
+/**
+ * struct - msm_io_irq_entry - define irq item
+ * @label: gh_irq_label for the irq
+ * @irq_num: linux mapped irq num
+ * @list: list head pointer
+ */
+struct msm_io_irq_entry {
+	u32 label;
+	u32 irq_num;
+	struct list_head list;
+};
+
+/**
+ * struct - msm_io_mem_entry - define io memory item
+ * @base: reg base
+ * @size: size of the reg range
+ * @list: list head pointer
+ */
+struct msm_io_mem_entry {
+	phys_addr_t base;
+	phys_addr_t size;
+	struct list_head list;
+};
+
+/**
+ * struct - msm_io_res - represents the hw resources for vm sharing
+ * @irq: list of IRQ's of all the dislay sub-devices
+ * @mem: list of IO memory ranges of all the display sub-devices
+ */
+struct msm_io_res {
+	struct list_head irq;
+	struct list_head mem;
+};
+
+/**
+ * struct msm_vm_ops - hooks for communication with vm clients
+ * @vm_pre_hw_release: invoked before releasing the HW
+ * @vm_post_hw_acquire: invoked before pushing the first commit
+ * @vm_check: invoked to check the readiness of the vm_clients
+ *	      before releasing the HW
+ * @vm_get_io_resources: invoked to collect HW resources
+ */
+struct msm_vm_ops {
+	int (*vm_pre_hw_release)(void *priv_data);
+	int (*vm_post_hw_acquire)(void *priv_data);
+	int (*vm_check)(void *priv_data);
+	int (*vm_get_io_resources)(struct msm_io_res *io_res, void *priv_data);
+};
+
+/**
+ * msm_vm_client_entry - defines the vm client info
+ * @ops: client vm_ops
+ * @dev: clients device id. Used in unregister
+ * @data: client custom data
+ * @list: linked list entry
+ */
+struct msm_vm_client_entry {
+	struct msm_vm_ops ops;
+	struct device *dev;
+	void *data;
+	struct list_head list;
+};
+
+/**
+ * msm_register_vm_event - api for display dependent drivers(clients) to
+ *                         register for vm events
+ * @dev: msm device
+ * @client_dev: client device
+ * @ops: vm event hooks
+ * @priv_data: client custom data
+ */
+int msm_register_vm_event(struct device *dev, struct device *client_dev,
+			  struct msm_vm_ops *ops, void *priv_data);
+
+/**
+ * msm_unregister_vm_event - api for display dependent drivers(clients) to
+ *                           unregister from vm events
+ * @dev: msm device
+ * @client_dev: client device
+ */
+void msm_unregister_vm_event(struct device *dev, struct device *client_dev);
+
+#endif //__SDE_VM_EVENT_H__

+ 6 - 0
qcom/opensource/display-drivers/include/uapi/Kbuild

@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
+
+# Top-level Makefile calls into asm-$(ARCH)
+# List only non-arch directories below
+
+header-y += display/

+ 5 - 0
qcom/opensource/display-drivers/include/uapi/display/Kbuild

@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
+
+header-y += media/
+header-y += drm/
+header-y += hdcp/

+ 5 - 0
qcom/opensource/display-drivers/include/uapi/display/drm/Kbuild

@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
+
+header-y += msm_drm_pp.h
+header-y += sde_drm.h
+

+ 817 - 0
qcom/opensource/display-drivers/include/uapi/display/drm/msm_drm_pp.h

@@ -0,0 +1,817 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _MSM_DRM_PP_H_
+#define _MSM_DRM_PP_H_
+
+#include <linux/types.h>
+#include <drm/drm.h>
+
+#define ENABLE_EVENT_SPR_OPR_VALUE
+#define ENABLE_EVENT_INTF_MISR_SIGNATURE
+#define MAX_DSI_DISPLAY 4
+
+/**
+ * struct drm_msm_pcc_coeff - PCC coefficient structure for each color
+ *                            component.
+ * @c: constant coefficient.
+ * @r: red coefficient.
+ * @g: green coefficient.
+ * @b: blue coefficient.
+ * @rg: red green coefficient.
+ * @gb: green blue coefficient.
+ * @rb: red blue coefficient.
+ * @rgb: red blue green coefficient.
+ */
+
+struct drm_msm_pcc_coeff {
+	__u32 c;
+	__u32 r;
+	__u32 g;
+	__u32 b;
+	__u32 rg;
+	__u32 gb;
+	__u32 rb;
+	__u32 rgb;
+};
+
+#define PCC_BEFORE (1 << 0)
+
+/**
+ * struct drm_msm_pcc - pcc feature structure
+ * @flags: for customizing operations. Values can be
+ *         - PCC_BEFORE: Operate PCC using a 'before' arrangement
+ * @r: red coefficients.
+ * @g: green coefficients.
+ * @b: blue coefficients.
+ * @r_rr: second order coefficients
+ * @r_gg: second order coefficients
+ * @r_bb: second order coefficients
+ * @g_rr: second order coefficients
+ * @g_gg: second order coefficients
+ * @g_bb: second order coefficients
+ * @b_rr: second order coefficients
+ * @b_gg: second order coefficients
+ * @b_bb: second order coefficients
+ */
+#define DRM_MSM_PCC3
+struct drm_msm_pcc {
+	__u64 flags;
+	struct drm_msm_pcc_coeff r;
+	struct drm_msm_pcc_coeff g;
+	struct drm_msm_pcc_coeff b;
+	__u32 r_rr;
+	__u32 r_gg;
+	__u32 r_bb;
+	__u32 g_rr;
+	__u32 g_gg;
+	__u32 g_bb;
+	__u32 b_rr;
+	__u32 b_gg;
+	__u32 b_bb;
+};
+
+/* struct drm_msm_pa_vlut - picture adjustment vLUT structure
+ * flags: for customizing vlut operation
+ * val: vLUT values
+ */
+#define PA_VLUT_SIZE 256
+struct drm_msm_pa_vlut {
+	__u64 flags;
+	__u32 val[PA_VLUT_SIZE];
+};
+
+#define PA_HSIC_HUE_ENABLE (1 << 0)
+#define PA_HSIC_SAT_ENABLE (1 << 1)
+#define PA_HSIC_VAL_ENABLE (1 << 2)
+#define PA_HSIC_CONT_ENABLE (1 << 3)
+/**
+ * struct drm_msm_pa_hsic - pa hsic feature structure
+ * @flags: flags for the feature customization, values can be:
+ *         - PA_HSIC_HUE_ENABLE: Enable hue adjustment
+ *         - PA_HSIC_SAT_ENABLE: Enable saturation adjustment
+ *         - PA_HSIC_VAL_ENABLE: Enable value adjustment
+ *         - PA_HSIC_CONT_ENABLE: Enable contrast adjustment
+ *
+ * @hue: hue setting
+ * @saturation: saturation setting
+ * @value: value setting
+ * @contrast: contrast setting
+ */
+#define DRM_MSM_PA_HSIC
+struct drm_msm_pa_hsic {
+	__u64 flags;
+	__u32 hue;
+	__u32 saturation;
+	__u32 value;
+	__u32 contrast;
+};
+
+#define MEMCOL_PROT_HUE (1 << 0)
+#define MEMCOL_PROT_SAT (1 << 1)
+#define MEMCOL_PROT_VAL (1 << 2)
+#define MEMCOL_PROT_CONT (1 << 3)
+#define MEMCOL_PROT_SIXZONE (1 << 4)
+#define MEMCOL_PROT_BLEND (1 << 5)
+/* struct drm_msm_memcol - Memory color feature structure.
+ *                         Skin, sky, foliage features are supported.
+ * @prot_flags: Bit mask for enabling protection feature.
+ * @color_adjust_p0: Adjustment curve.
+ * @color_adjust_p1: Adjustment curve.
+ * @color_adjust_p2: Adjustment curve.
+ * @blend_gain: Blend gain weightage from othe PA features.
+ * @sat_hold: Saturation hold value.
+ * @val_hold: Value hold info.
+ * @hue_region: Hue qualifier.
+ * @sat_region: Saturation qualifier.
+ * @val_region: Value qualifier.
+ */
+#define DRM_MSM_MEMCOL
+struct drm_msm_memcol {
+	__u64 prot_flags;
+	__u32 color_adjust_p0;
+	__u32 color_adjust_p1;
+	__u32 color_adjust_p2;
+	__u32 blend_gain;
+	__u32 sat_hold;
+	__u32 val_hold;
+	__u32 hue_region;
+	__u32 sat_region;
+	__u32 val_region;
+};
+
+#define DRM_MSM_SIXZONE
+#define SIXZONE_LUT_SIZE 384
+#define SIXZONE_HUE_ENABLE (1 << 0)
+#define SIXZONE_SAT_ENABLE (1 << 1)
+#define SIXZONE_VAL_ENABLE (1 << 2)
+#define SIXZONE_SV_ENABLE (1 << 3)
+/* struct drm_msm_sixzone_curve - Sixzone HSV adjustment curve structure.
+ * @p0: Hue adjustment.
+ * @p1: Saturation/Value adjustment.
+ */
+struct drm_msm_sixzone_curve {
+	__u32 p1;
+	__u32 p0;
+};
+
+/* struct drm_msm_sixzone - Sixzone feature structure.
+ * @flags: for feature customization, values can be:
+ *         - SIXZONE_HUE_ENABLE: Enable hue adjustment
+ *         - SIXZONE_SAT_ENABLE: Enable saturation adjustment
+ *         - SIXZONE_VAL_ENABLE: Enable value adjustment
+ *         - SIXZONE_SV_ENABLE: Enable SV feature
+ * @threshold: threshold qualifier.
+ * @adjust_p0: Adjustment curve.
+ * @adjust_p1: Adjustment curve.
+ * @sat_hold: Saturation hold info.
+ * @val_hold: Value hold info.
+ * @curve: HSV adjustment curve lut.
+ * @sat_adjust_p0: Saturation adjustment curve.
+ * @sat_adjust_p1: Saturation adjustment curve.
+ * @curve_p2: Saturation Mid/Saturation High adjustment
+ */
+struct drm_msm_sixzone {
+	__u64 flags;
+	__u32 threshold;
+	__u32 adjust_p0;
+	__u32 adjust_p1;
+	__u32 sat_hold;
+	__u32 val_hold;
+	struct drm_msm_sixzone_curve curve[SIXZONE_LUT_SIZE];
+	__u32 sat_adjust_p0;
+	__u32 sat_adjust_p1;
+	__u32 curve_p2[SIXZONE_LUT_SIZE];
+};
+
+#define GAMUT_3D_MODE_17 1
+#define GAMUT_3D_MODE_5 2
+#define GAMUT_3D_MODE_13 3
+
+#define GAMUT_3D_MODE17_TBL_SZ 1229
+#define GAMUT_3D_MODE5_TBL_SZ 32
+#define GAMUT_3D_MODE13_TBL_SZ 550
+#define GAMUT_3D_SCALE_OFF_SZ 16
+#define GAMUT_3D_SCALEB_OFF_SZ 12
+#define GAMUT_3D_TBL_NUM 4
+#define GAMUT_3D_SCALE_OFF_TBL_NUM 3
+#define GAMUT_3D_MAP_EN (1 << 0)
+
+/**
+ * struct drm_msm_3d_col - 3d gamut color component structure
+ * @c0: Holds c0 value
+ * @c2_c1: Holds c2/c1 values
+ */
+struct drm_msm_3d_col {
+	__u32 c2_c1;
+	__u32 c0;
+};
+/**
+ * struct drm_msm_3d_gamut - 3d gamut feature structure
+ * @flags: flags for the feature values are:
+ *         0 - no map
+ *         GAMUT_3D_MAP_EN - enable map
+ * @mode: lut mode can take following values:
+ *        - GAMUT_3D_MODE_17
+ *        - GAMUT_3D_MODE_5
+ *        - GAMUT_3D_MODE_13
+ * @scale_off: Scale offset table
+ * @col: Color component tables
+ */
+struct drm_msm_3d_gamut {
+	__u64 flags;
+	__u32 mode;
+	__u32 scale_off[GAMUT_3D_SCALE_OFF_TBL_NUM][GAMUT_3D_SCALE_OFF_SZ];
+	struct drm_msm_3d_col col[GAMUT_3D_TBL_NUM][GAMUT_3D_MODE17_TBL_SZ];
+};
+
+#define PGC_TBL_LEN 512
+#define PGC_8B_ROUND (1 << 0)
+/**
+ * struct drm_msm_pgc_lut - pgc lut feature structure
+ * @flags: flags for the featue values can be:
+ *         - PGC_8B_ROUND
+ * @c0: color0 component lut
+ * @c1: color1 component lut
+ * @c2: color2 component lut
+ */
+struct drm_msm_pgc_lut {
+	__u64 flags;
+	__u32 c0[PGC_TBL_LEN];
+	__u32 c1[PGC_TBL_LEN];
+	__u32 c2[PGC_TBL_LEN];
+};
+
+#define IGC_TBL_LEN 256
+#define IGC_DITHER_ENABLE (1 << 0)
+/**
+ * struct drm_msm_igc_lut - igc lut feature structure
+ * @flags: flags for the feature customization, values can be:
+ *             - IGC_DITHER_ENABLE: Enable dither functionality
+ * @c0: color0 component lut
+ * @c1: color1 component lut
+ * @c2: color2 component lut
+ * @strength: dither strength, considered valid when IGC_DITHER_ENABLE
+ *            is set in flags. Strength value based on source bit width.
+ * @c0_last: color0 lut_last component
+ * @c1_last: color1 lut_last component
+ * @c2_last: color2 lut_last component
+ */
+struct drm_msm_igc_lut {
+	__u64 flags;
+	__u32 c0[IGC_TBL_LEN];
+	__u32 c1[IGC_TBL_LEN];
+	__u32 c2[IGC_TBL_LEN];
+	__u32 strength;
+	__u32 c0_last;
+	__u32 c1_last;
+	__u32 c2_last;
+};
+#define LAST_LUT 2
+
+#define HIST_V_SIZE 256
+/**
+ * struct drm_msm_hist - histogram feature structure
+ * @flags: for customizing operations
+ * @data: histogram data
+ */
+struct drm_msm_hist {
+	__u64 flags;
+	__u32 data[HIST_V_SIZE];
+};
+
+#define AD4_LUT_GRP0_SIZE 33
+#define AD4_LUT_GRP1_SIZE 32
+/*
+ * struct drm_msm_ad4_init - ad4 init structure set by user-space client.
+ *                           Init param values can change based on tuning
+ *                           hence it is passed by user-space clients.
+ */
+struct drm_msm_ad4_init {
+	__u32 init_param_001[AD4_LUT_GRP0_SIZE];
+	__u32 init_param_002[AD4_LUT_GRP0_SIZE];
+	__u32 init_param_003[AD4_LUT_GRP0_SIZE];
+	__u32 init_param_004[AD4_LUT_GRP0_SIZE];
+	__u32 init_param_005[AD4_LUT_GRP1_SIZE];
+	__u32 init_param_006[AD4_LUT_GRP1_SIZE];
+	__u32 init_param_007[AD4_LUT_GRP0_SIZE];
+	__u32 init_param_008[AD4_LUT_GRP0_SIZE];
+	__u32 init_param_009;
+	__u32 init_param_010;
+	__u32 init_param_011;
+	__u32 init_param_012;
+	__u32 init_param_013;
+	__u32 init_param_014;
+	__u32 init_param_015;
+	__u32 init_param_016;
+	__u32 init_param_017;
+	__u32 init_param_018;
+	__u32 init_param_019;
+	__u32 init_param_020;
+	__u32 init_param_021;
+	__u32 init_param_022;
+	__u32 init_param_023;
+	__u32 init_param_024;
+	__u32 init_param_025;
+	__u32 init_param_026;
+	__u32 init_param_027;
+	__u32 init_param_028;
+	__u32 init_param_029;
+	__u32 init_param_030;
+	__u32 init_param_031;
+	__u32 init_param_032;
+	__u32 init_param_033;
+	__u32 init_param_034;
+	__u32 init_param_035;
+	__u32 init_param_036;
+	__u32 init_param_037;
+	__u32 init_param_038;
+	__u32 init_param_039;
+	__u32 init_param_040;
+	__u32 init_param_041;
+	__u32 init_param_042;
+	__u32 init_param_043;
+	__u32 init_param_044;
+	__u32 init_param_045;
+	__u32 init_param_046;
+	__u32 init_param_047;
+	__u32 init_param_048;
+	__u32 init_param_049;
+	__u32 init_param_050;
+	__u32 init_param_051;
+	__u32 init_param_052;
+	__u32 init_param_053;
+	__u32 init_param_054;
+	__u32 init_param_055;
+	__u32 init_param_056;
+	__u32 init_param_057;
+	__u32 init_param_058;
+	__u32 init_param_059;
+	__u32 init_param_060;
+	__u32 init_param_061;
+	__u32 init_param_062;
+	__u32 init_param_063;
+	__u32 init_param_064;
+	__u32 init_param_065;
+	__u32 init_param_066;
+	__u32 init_param_067;
+	__u32 init_param_068;
+	__u32 init_param_069;
+	__u32 init_param_070;
+	__u32 init_param_071;
+	__u32 init_param_072;
+	__u32 init_param_073;
+	__u32 init_param_074;
+	__u32 init_param_075;
+};
+
+/*
+ * struct drm_msm_ad4_cfg - ad4 config structure set by user-space client.
+ *                           Config param values can vary based on tuning,
+ *                           hence it is passed by user-space clients.
+ */
+struct drm_msm_ad4_cfg {
+	__u32 cfg_param_001;
+	__u32 cfg_param_002;
+	__u32 cfg_param_003;
+	__u32 cfg_param_004;
+	__u32 cfg_param_005;
+	__u32 cfg_param_006;
+	__u32 cfg_param_007;
+	__u32 cfg_param_008;
+	__u32 cfg_param_009;
+	__u32 cfg_param_010;
+	__u32 cfg_param_011;
+	__u32 cfg_param_012;
+	__u32 cfg_param_013;
+	__u32 cfg_param_014;
+	__u32 cfg_param_015;
+	__u32 cfg_param_016;
+	__u32 cfg_param_017;
+	__u32 cfg_param_018;
+	__u32 cfg_param_019;
+	__u32 cfg_param_020;
+	__u32 cfg_param_021;
+	__u32 cfg_param_022;
+	__u32 cfg_param_023;
+	__u32 cfg_param_024;
+	__u32 cfg_param_025;
+	__u32 cfg_param_026;
+	__u32 cfg_param_027;
+	__u32 cfg_param_028;
+	__u32 cfg_param_029;
+	__u32 cfg_param_030;
+	__u32 cfg_param_031;
+	__u32 cfg_param_032;
+	__u32 cfg_param_033;
+	__u32 cfg_param_034;
+	__u32 cfg_param_035;
+	__u32 cfg_param_036;
+	__u32 cfg_param_037;
+	__u32 cfg_param_038;
+	__u32 cfg_param_039;
+	__u32 cfg_param_040;
+	__u32 cfg_param_041;
+	__u32 cfg_param_042;
+	__u32 cfg_param_043;
+	__u32 cfg_param_044;
+	__u32 cfg_param_045;
+	__u32 cfg_param_046;
+	__u32 cfg_param_047;
+	__u32 cfg_param_048;
+	__u32 cfg_param_049;
+	__u32 cfg_param_050;
+	__u32 cfg_param_051;
+	__u32 cfg_param_052;
+	__u32 cfg_param_053;
+};
+
+#define DITHER_MATRIX_SZ 16
+#define DITHER_LUMA_MODE (1 << 0)
+
+/**
+ * struct drm_msm_dither - dither feature structure
+ * @flags: flags for the feature customization, values can be:
+	   -DITHER_LUMA_MODE: Enable LUMA dither mode
+ * @temporal_en: temperal dither enable
+ * @c0_bitdepth: c0 component bit depth
+ * @c1_bitdepth: c1 component bit depth
+ * @c2_bitdepth: c2 component bit depth
+ * @c3_bitdepth: c2 component bit depth
+ * @matrix: dither strength matrix
+ */
+struct drm_msm_dither {
+	__u64 flags;
+	__u32 temporal_en;
+	__u32 c0_bitdepth;
+	__u32 c1_bitdepth;
+	__u32 c2_bitdepth;
+	__u32 c3_bitdepth;
+	__u32 matrix[DITHER_MATRIX_SZ];
+};
+
+/**
+ * struct drm_msm_pa_dither - dspp dither feature structure
+ * @flags: for customizing operations
+ * @strength: dither strength
+ * @offset_en: offset enable bit
+ * @matrix: dither data matrix
+ */
+#define DRM_MSM_PA_DITHER
+struct drm_msm_pa_dither {
+	__u64 flags;
+	__u32 strength;
+	__u32 offset_en;
+	__u32 matrix[DITHER_MATRIX_SZ];
+};
+
+/**
+ * struct drm_msm_ad4_roi_cfg - ad4 roi params config set
+ * by user-space client.
+ * @h_x - hotizontal direction start
+ * @h_y - hotizontal direction end
+ * @v_x - vertical direction start
+ * @v_y - vertical direction end
+ * @factor_in - the alpha value for inside roi region
+ * @factor_out - the alpha value for outside roi region
+ */
+#define DRM_MSM_AD4_ROI
+struct drm_msm_ad4_roi_cfg {
+	__u32 h_x;
+	__u32 h_y;
+	__u32 v_x;
+	__u32 v_y;
+	__u32 factor_in;
+	__u32 factor_out;
+};
+
+#define LTM_FEATURE_DEF 1
+#define LTM_DATA_SIZE_0 32
+#define LTM_DATA_SIZE_1 128
+#define LTM_DATA_SIZE_2 256
+#define LTM_DATA_SIZE_3 33
+#define LTM_BUFFER_SIZE 5
+#define LTM_GUARD_BYTES 255
+#define LTM_BLOCK_SIZE 4
+
+#define LTM_STATS_SAT (1 << 1)
+#define LTM_STATS_MERGE_SAT (1 << 2)
+#define LTM_HIST_CHECKSUM_SUPPORT (1 << 0)
+
+/*
+ * struct drm_msm_ltm_stats_data - LTM stats data structure
+ */
+struct drm_msm_ltm_stats_data {
+	__u32 stats_01[LTM_DATA_SIZE_0][LTM_DATA_SIZE_1];
+	__u32 stats_02[LTM_DATA_SIZE_2];
+	__u32 stats_03[LTM_DATA_SIZE_0];
+	__u32 stats_04[LTM_DATA_SIZE_0];
+	__u32 stats_05[LTM_DATA_SIZE_0];
+	__u32 status_flag;
+	__u32 display_h;
+	__u32 display_v;
+	__u32 init_h[LTM_BLOCK_SIZE];
+	__u32 init_v;
+	__u32 inc_h;
+	__u32 inc_v;
+	__u32 portrait_en;
+	__u32 merge_en;
+	__u32 cfg_param_01;
+	__u32 cfg_param_02;
+	__u32 cfg_param_03;
+	__u32 cfg_param_04;
+	__u32 feature_flag;
+	__u32 checksum;
+};
+
+/*
+ * struct drm_msm_ltm_init_param - LTM init param structure
+ */
+struct drm_msm_ltm_init_param {
+	__u32 init_param_01;
+	__u32 init_param_02;
+	__u32 init_param_03;
+	__u32 init_param_04;
+};
+
+/*
+ * struct drm_msm_ltm_cfg_param - LTM config param structure
+ */
+struct  drm_msm_ltm_cfg_param {
+	__u32 cfg_param_01;
+	__u32 cfg_param_02;
+	__u32 cfg_param_03;
+	__u32 cfg_param_04;
+	__u32 cfg_param_05;
+	__u32 cfg_param_06;
+};
+
+/*
+ * struct drm_msm_ltm_data - LTM data structure
+ */
+struct drm_msm_ltm_data {
+	__u32 data[LTM_DATA_SIZE_0][LTM_DATA_SIZE_3];
+};
+
+/*
+ * struct drm_msm_ltm_buffers_crtl - LTM buffer control structure.
+ *                                   This struct will be used to init and
+ *                                   de-init the LTM buffers in driver.
+ * @num_of_buffers: valid number of buffers used
+ * @fds: fd array to for all the valid buffers
+ */
+struct drm_msm_ltm_buffers_ctrl {
+	__u32 num_of_buffers;
+	__u32 fds[LTM_BUFFER_SIZE];
+};
+
+/*
+ * struct drm_msm_ltm_buffer - LTM buffer structure.
+ *                             This struct will be passed from driver to user
+ *                             space for LTM stats data notification.
+ * @fd: fd assicated with the buffer that has LTM stats data
+ * @offset: offset from base address that used for alignment
+ * @status status flag for error indication
+ */
+struct drm_msm_ltm_buffer {
+	__u32 fd;
+	__u32 offset;
+	__u32 status;
+};
+
+#define SPR_INIT_PARAM_SIZE_1 4
+#define SPR_INIT_PARAM_SIZE_2 5
+#define SPR_INIT_PARAM_SIZE_3 16
+#define SPR_INIT_PARAM_SIZE_4 24
+#define SPR_INIT_PARAM_SIZE_5 32
+#define SPR_INIT_PARAM_SIZE_6 7
+#define SPR_FLAG_BYPASS (1 << 0)
+
+/**
+ * struct drm_msm_spr_init_cfg - SPR initial configuration structure
+ */
+struct drm_msm_spr_init_cfg {
+	__u64 flags;
+	__u16 cfg0;
+	__u16 cfg1;
+	__u16 cfg2;
+	__u16 cfg3;
+	__u16 cfg4;
+	__u16 cfg5;
+	__u16 cfg6;
+	__u16 cfg7;
+	__u16 cfg8;
+	__u16 cfg9;
+	__u32 cfg10;
+	__u16 cfg11[SPR_INIT_PARAM_SIZE_1];
+	__u16 cfg12[SPR_INIT_PARAM_SIZE_1];
+	__u16 cfg13[SPR_INIT_PARAM_SIZE_1];
+	__u16 cfg14[SPR_INIT_PARAM_SIZE_2];
+	__u16 cfg15[SPR_INIT_PARAM_SIZE_5];
+	int cfg16[SPR_INIT_PARAM_SIZE_3];
+	int cfg17[SPR_INIT_PARAM_SIZE_4];
+	__u16 cfg18_en;
+	__u8 cfg18[SPR_INIT_PARAM_SIZE_6];
+};
+
+/**
+ * struct drm_msm_spr_udc_cfg - SPR UDC configuration structure
+ */
+
+#define SPR_UDC_PARAM_SIZE_1 27
+#define SPR_UDC_PARAM_SIZE_2 1536
+struct drm_msm_spr_udc_cfg {
+	__u64 flags;
+	__u16 init_cfg4;
+	__u16 init_cfg11[SPR_INIT_PARAM_SIZE_1];
+	__u16 cfg1[SPR_UDC_PARAM_SIZE_1];
+	__u16 cfg2[SPR_UDC_PARAM_SIZE_2];
+};
+
+
+#define FEATURE_DEM
+#define CFG0_PARAM_LEN 8
+#define CFG1_PARAM_LEN 8
+#define CFG1_PARAM0_LEN 153
+#define CFG0_PARAM2_LEN 256
+#define CFG5_PARAM01_LEN 4
+#define CFG3_PARAM01_LEN 4
+#define DEMURA_FLAG_0 (1 << 0)
+#define DEMURA_FLAG_1 (1 << 1)
+#define DEMURA_FLAG_2 (3 << 2)
+#define DEMURA_SKIP_CFG0_PARAM2 (1 << 4)
+#define DEMURA_PRECISION_0 (0 << 2)
+#define DEMURA_PRECISION_1 (1 << 2)
+#define DEMURA_PRECISION_2 (2 << 2)
+
+struct drm_msm_dem_cfg {
+	__u64 flags;
+	__u32 pentile;
+	__u32 cfg0_en;
+	__u32 cfg0_param0_len;
+	__u32 cfg0_param0[CFG0_PARAM_LEN];
+	__u32 cfg0_param1_len;
+	__u32 cfg0_param1[CFG0_PARAM_LEN];
+	__u32 cfg0_param2_len;
+	__u64 cfg0_param2_c0[CFG0_PARAM2_LEN];
+	__u64 cfg0_param2_c1[CFG0_PARAM2_LEN];
+	__u64 cfg0_param2_c2[CFG0_PARAM2_LEN];
+	__u32 cfg0_param3_len;
+	__u32 cfg0_param3_c0[CFG0_PARAM_LEN];
+	__u32 cfg0_param3_c1[CFG0_PARAM_LEN];
+	__u32 cfg0_param3_c2[CFG0_PARAM_LEN];
+	__u32 cfg0_param4_len;
+	__u32 cfg0_param4[CFG0_PARAM_LEN];
+
+	__u32 cfg1_en;
+	__u32 cfg1_high_idx;
+	__u32 cfg1_low_idx;
+	__u32 cfg01_param0_len;
+	__u32 cfg01_param0[CFG1_PARAM_LEN];
+	__u32 cfg1_param0_len;
+	__u32 cfg1_param0_c0[CFG1_PARAM0_LEN];
+	__u32 cfg1_param0_c1[CFG1_PARAM0_LEN];
+	__u32 cfg1_param0_c2[CFG1_PARAM0_LEN];
+
+	__u32 cfg2_en;
+	__u32 cfg3_en;
+	__u32 cfg3_param0_len;
+	__u32 cfg3_param0_a[CFG3_PARAM01_LEN];
+	__u32 cfg3_param0_b[CFG3_PARAM01_LEN];
+	__u32 cfg3_ab_adj;
+	__u32 cfg4_en;
+	__u32 cfg5_en;
+	__u32 cfg5_param0_len;
+	__u32 cfg5_param0[CFG5_PARAM01_LEN];
+	__u32 cfg5_param1_len;
+	__u32 cfg5_param1[CFG5_PARAM01_LEN];
+
+	__u32 c0_depth;
+	__u32 c1_depth;
+	__u32 c2_depth;
+	__u32 src_id;
+	__u32 cfg0_param2_idx;
+};
+
+struct drm_msm_dem_cfg0_param2 {
+	__u32 cfg0_param2_len;
+	__u64 cfg0_param2_c0[CFG0_PARAM2_LEN];
+	__u64 cfg0_param2_c1[CFG0_PARAM2_LEN];
+	__u64 cfg0_param2_c2[CFG0_PARAM2_LEN];
+};
+
+/**
+ * struct drm_msm_ad4_manual_str_cfg - ad4 manual strength config set
+ * by user-space client.
+ * @in_str - strength for inside roi region
+ * @out_str - strength for outside roi region
+ */
+#define DRM_MSM_AD4_MANUAL_STRENGTH
+struct drm_msm_ad4_manual_str_cfg {
+	__u32 in_str;
+	__u32 out_str;
+};
+
+#define RC_DATA_SIZE_MAX   2720
+#define RC_CFG_SIZE_MAX       4
+
+struct drm_msm_rc_mask_cfg {
+	__u64 flags;
+	__u32 cfg_param_01;
+	__u32 cfg_param_02;
+	__u32 cfg_param_03;
+	__u32 cfg_param_04[RC_CFG_SIZE_MAX];
+	__u32 cfg_param_05[RC_CFG_SIZE_MAX];
+	__u32 cfg_param_06[RC_CFG_SIZE_MAX];
+	__u64 cfg_param_07;
+	__u32 cfg_param_08;
+	__u64 cfg_param_09[RC_DATA_SIZE_MAX];
+	__u32 height;
+	__u32 width;
+};
+
+#define FP16_SUPPORTED
+#define FP16_GC_FLAG_ALPHA_EN (1 << 0)
+
+ /* FP16 GC mode options */
+#define FP16_GC_MODE_INVALID 0
+#define FP16_GC_MODE_SRGB 1
+#define FP16_GC_MODE_PQ 2
+
+/**
+ * struct drm_msm_fp16_gc - FP16 GC configuration structure
+ * @in flags - Settings flags for FP16 GC
+ * @in mode - Gamma correction mode to use for FP16 GC
+ */
+struct drm_msm_fp16_gc {
+	__u64 flags;
+	__u64 mode;
+};
+
+/**
+ * struct drm_msm_fp16_csc - FP16 CSC configuration structure
+ * @in flags - Settings flags for FP16 CSC. Currently unused
+ * @in cfg_param_0_len - Length of data for cfg_param_0
+ * @in cfg_param_0 - Data for param 0. Max size is FP16_CSC_CFG0_PARAM_LEN
+ * @in cfg_param_1_len - Length of data for cfg_param_1
+ * @in cfg_param_1 - Data for param 1. Max size is FP16_CSC_CFG1_PARAM_LEN
+ */
+#define FP16_CSC_CFG0_PARAM_LEN 12
+#define FP16_CSC_CFG1_PARAM_LEN 8
+struct drm_msm_fp16_csc {
+	__u64 flags;
+	__u32 cfg_param_0_len;
+	__u32 cfg_param_0[FP16_CSC_CFG0_PARAM_LEN];
+	__u32 cfg_param_1_len;
+	__u32 cfg_param_1[FP16_CSC_CFG1_PARAM_LEN];
+};
+
+#define DIMMING_ENABLE (1 << 0)
+#define DIMMING_MIN_BL_VALID (1 << 1)
+struct drm_msm_backlight_info {
+	__u32 brightness_max;
+	__u32 brightness;
+	__u32 bl_level_max;
+	__u32 bl_level;
+	__u32 bl_scale;
+	__u32 bl_scale_sv;
+	__u32 status;
+	__u32 min_bl;
+	__u32 bl_scale_max;
+	__u32 bl_scale_sv_max;
+};
+
+#define DIMMING_BL_LUT_LEN 8192
+struct drm_msm_dimming_bl_lut {
+	__u32 length;
+	__u32 mapped_bl[DIMMING_BL_LUT_LEN];
+};
+
+struct drm_msm_opr_value {
+	__u32 num_valid_opr;
+	__u32 opr_value[MAX_DSI_DISPLAY];
+};
+
+#define SDE_MAX_ROI 4
+struct drm_msm_roi {
+	__u32 num_rects;
+	struct drm_clip_rect roi[SDE_MAX_ROI];
+};
+
+struct drm_msm_misr_sign {
+	__u64 num_valid_misr;
+	struct drm_msm_roi roi_list;
+	__u64 misr_sign_value[MAX_DSI_DISPLAY];
+};
+
+#define UCSC_SUPPORTED
+
+#define UCSC_CSC_CFG0_PARAM_LEN FP16_CSC_CFG0_PARAM_LEN
+#define UCSC_CSC_CFG1_PARAM_LEN FP16_CSC_CFG1_PARAM_LEN
+
+typedef struct drm_msm_fp16_csc drm_msm_ucsc_csc;
+
+#endif /* _MSM_DRM_PP_H_ */

+ 974 - 0
qcom/opensource/display-drivers/include/uapi/display/drm/sde_drm.h

@@ -0,0 +1,974 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _SDE_DRM_H_
+#define _SDE_DRM_H_
+
+#include <drm/drm.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Total number of supported color planes */
+#define SDE_MAX_PLANES  4
+
+/* Total number of parameterized detail enhancer mapping curves */
+#define SDE_MAX_DE_CURVES 3
+
+ /* Y/RGB and UV filter configuration */
+#define FILTER_EDGE_DIRECTED_2D		0x0
+#define FILTER_CIRCULAR_2D		0x1
+#define FILTER_SEPARABLE_1D		0x2
+#define FILTER_BILINEAR			0x3
+
+/* Alpha filters */
+#define FILTER_ALPHA_DROP_REPEAT	0x0
+#define FILTER_ALPHA_BILINEAR		0x1
+#define FILTER_ALPHA_2D			0x3
+
+/* Blend filters */
+#define FILTER_BLEND_CIRCULAR_2D	0x0
+#define FILTER_BLEND_SEPARABLE_1D	0x1
+
+/* LUT configuration flags */
+#define SCALER_LUT_SWAP			0x1
+#define SCALER_LUT_DIR_WR		0x2
+#define SCALER_LUT_Y_CIR_WR		0x4
+#define SCALER_LUT_UV_CIR_WR		0x8
+#define SCALER_LUT_Y_SEP_WR		0x10
+#define SCALER_LUT_UV_SEP_WR		0x20
+
+/**
+ * DRM format modifier tokens
+ *
+ * @DRM_FORMAT_MOD_QCOM_DX:         Refers to a DX variant of the base format.
+ *                                  Implementation may be platform and
+ *                                  base-format specific.
+ */
+#define DRM_FORMAT_MOD_QCOM_DX	fourcc_mod_code(QCOM, 0x2)
+
+/**
+ * @DRM_FORMAT_MOD_QCOM_TIGHT:      Refers to a tightly packed variant of the
+ *                                  base variant. Implementation may be
+ *                                  platform and base-format specific.
+ */
+#define DRM_FORMAT_MOD_QCOM_TIGHT	fourcc_mod_code(QCOM, 0x4)
+
+/**
+ * @DRM_FORMAT_MOD_QCOM_TILE:       Refers to a tile variant of the base format.
+ *                                  Implementation may be platform and
+ *                                  base-format specific.
+ */
+#define DRM_FORMAT_MOD_QCOM_TILE	fourcc_mod_code(QCOM, 0x8)
+
+/**
+ * @DRM_FORMAT_MOD_QCOM_ALPHA_SWAP:	Refers to a pixel format for which
+ *					its alpha ordering has been reversed.
+ *					Implementation may be platform and
+ *					base-format specific.
+ */
+#define DRM_FORMAT_MOD_QCOM_ALPHA_SWAP	fourcc_mod_code(QCOM, 0x10)
+
+/**
+ * Blend operations for "blend_op" property
+ *
+ * @SDE_DRM_BLEND_OP_NOT_DEFINED:   No blend operation defined for the layer.
+ * @SDE_DRM_BLEND_OP_OPAQUE:        Apply a constant blend operation. The layer
+ *                                  would appear opaque in case fg plane alpha
+ *                                  is 0xff.
+ * @SDE_DRM_BLEND_OP_PREMULTIPLIED: Apply source over blend rule. Layer already
+ *                                  has alpha pre-multiplication done. If the fg
+ *                                  plane alpha is less than 0xff, apply
+ *                                  modulation as well. This operation is
+ *                                  intended on layers having alpha channel.
+ * @SDE_DRM_BLEND_OP_COVERAGE:      Apply source over blend rule. Layer is not
+ *                                  alpha pre-multiplied. Apply
+ *                                  pre-multiplication. If fg plane alpha is
+ *                                  less than 0xff, apply modulation as well.
+ * @SDE_DRM_BLEND_OP_MAX:           Used to track maximum blend operation
+ *                                  possible by mdp.
+ * @SDE_DRM_BLEND_OP_SKIP:          Skip staging the layer in the layer mixer.
+ */
+#define SDE_DRM_BLEND_OP_NOT_DEFINED    0
+#define SDE_DRM_BLEND_OP_OPAQUE         1
+#define SDE_DRM_BLEND_OP_PREMULTIPLIED  2
+#define SDE_DRM_BLEND_OP_COVERAGE       3
+#define SDE_DRM_BLEND_OP_MAX            4
+#define SDE_DRM_BLEND_OP_SKIP           5
+
+/**
+ * Bit masks for "src_config" property
+ * construct bitmask via (1UL << SDE_DRM_<flag>)
+ */
+#define SDE_DRM_DEINTERLACE         0   /* Specifies interlaced input */
+
+/* DRM bitmasks are restricted to 0..63 */
+#define SDE_DRM_BITMASK_COUNT       64
+
+/**
+ * Framebuffer modes for "fb_translation_mode" PLANE and CONNECTOR property
+ *
+ * @SDE_DRM_FB_NON_SEC:          IOMMU configuration for this framebuffer mode
+ *                               is non-secure domain and requires
+ *                               both stage I and stage II translations when
+ *                               this buffer is accessed by the display HW.
+ *                               This is the default mode of all frambuffers.
+ * @SDE_DRM_FB_SEC:              IOMMU configuration for this framebuffer mode
+ *                               is secure domain and requires
+ *                               both stage I and stage II translations when
+ *                               this buffer is accessed by the display HW.
+ * @SDE_DRM_FB_NON_SEC_DIR_TRANS: IOMMU configuration for this framebuffer mode
+ *                               is non-secure domain and requires
+ *                               only stage II translation when
+ *                               this buffer is accessed by the display HW.
+ * @SDE_DRM_FB_SEC_DIR_TRANS:    IOMMU configuration for this framebuffer mode
+ *                               is secure domain and requires
+ *                               only stage II translation when
+ *                               this buffer is accessed by the display HW.
+ */
+
+#define SDE_DRM_FB_NON_SEC              0
+#define SDE_DRM_FB_SEC                  1
+#define SDE_DRM_FB_NON_SEC_DIR_TRANS    2
+#define SDE_DRM_FB_SEC_DIR_TRANS        3
+
+/**
+ * Secure levels for "security_level" CRTC property.
+ *                        CRTC property which specifies what plane types
+ *                        can be attached to this CRTC. Plane component
+ *                        derives the plane type based on the FB_MODE.
+ * @ SDE_DRM_SEC_NON_SEC: Both Secure and non-secure plane types can be
+ *                        attached to this CRTC. This is the default state of
+ *                        the CRTC.
+ * @ SDE_DRM_SEC_ONLY:    Only secure planes can be added to this CRTC. If a
+ *                        CRTC is instructed to be in this mode it follows the
+ *                        platform dependent restrictions.
+ */
+#define SDE_DRM_SEC_NON_SEC            0
+#define SDE_DRM_SEC_ONLY               1
+
+/**
+ * struct sde_drm_pix_ext_v1 - version 1 of pixel ext structure
+ * @num_ext_pxls_lr: Number of total horizontal pixels
+ * @num_ext_pxls_tb: Number of total vertical lines
+ * @left_ftch:       Number of extra pixels to overfetch from left
+ * @right_ftch:      Number of extra pixels to overfetch from right
+ * @top_ftch:        Number of extra lines to overfetch from top
+ * @btm_ftch:        Number of extra lines to overfetch from bottom
+ * @left_rpt:        Number of extra pixels to repeat from left
+ * @right_rpt:       Number of extra pixels to repeat from right
+ * @top_rpt:         Number of extra lines to repeat from top
+ * @btm_rpt:         Number of extra lines to repeat from bottom
+ */
+struct sde_drm_pix_ext_v1 {
+	/*
+	 * Number of pixels ext in left, right, top and bottom direction
+	 * for all color components.
+	 */
+	__s32 num_ext_pxls_lr[SDE_MAX_PLANES];
+	__s32 num_ext_pxls_tb[SDE_MAX_PLANES];
+
+	/*
+	 * Number of pixels needs to be overfetched in left, right, top
+	 * and bottom directions from source image for scaling.
+	 */
+	__s32 left_ftch[SDE_MAX_PLANES];
+	__s32 right_ftch[SDE_MAX_PLANES];
+	__s32 top_ftch[SDE_MAX_PLANES];
+	__s32 btm_ftch[SDE_MAX_PLANES];
+	/*
+	 * Number of pixels needs to be repeated in left, right, top and
+	 * bottom directions for scaling.
+	 */
+	__s32 left_rpt[SDE_MAX_PLANES];
+	__s32 right_rpt[SDE_MAX_PLANES];
+	__s32 top_rpt[SDE_MAX_PLANES];
+	__s32 btm_rpt[SDE_MAX_PLANES];
+
+};
+
+/**
+ * struct sde_drm_scaler_v1 - version 1 of struct sde_drm_scaler
+ * @lr:            Pixel extension settings for left/right
+ * @tb:            Pixel extension settings for top/botton
+ * @init_phase_x:  Initial scaler phase values for x
+ * @phase_step_x:  Phase step values for x
+ * @init_phase_y:  Initial scaler phase values for y
+ * @phase_step_y:  Phase step values for y
+ * @horz_filter:   Horizontal filter array
+ * @vert_filter:   Vertical filter array
+ */
+struct sde_drm_scaler_v1 {
+	/*
+	 * Pix ext settings
+	 */
+	struct sde_drm_pix_ext_v1 pe;
+	/*
+	 * Phase settings
+	 */
+	__s32 init_phase_x[SDE_MAX_PLANES];
+	__s32 phase_step_x[SDE_MAX_PLANES];
+	__s32 init_phase_y[SDE_MAX_PLANES];
+	__s32 phase_step_y[SDE_MAX_PLANES];
+
+	/*
+	 * Filter type to be used for scaling in horizontal and vertical
+	 * directions
+	 */
+	__u32 horz_filter[SDE_MAX_PLANES];
+	__u32 vert_filter[SDE_MAX_PLANES];
+};
+
+/**
+ * struct sde_drm_de_v1 - version 1 of detail enhancer structure
+ * @enable:         Enables/disables detail enhancer
+ * @sharpen_level1: Sharpening strength for noise
+ * @sharpen_level2: Sharpening strength for context
+ * @clip:           Clip coefficient
+ * @limit:          Detail enhancer limit factor
+ * @thr_quiet:      Quite zone threshold
+ * @thr_dieout:     Die-out zone threshold
+ * @thr_low:        Linear zone left threshold
+ * @thr_high:       Linear zone right threshold
+ * @prec_shift:     Detail enhancer precision
+ * @adjust_a:       Mapping curves A coefficients
+ * @adjust_b:       Mapping curves B coefficients
+ * @adjust_c:       Mapping curves C coefficients
+ */
+struct sde_drm_de_v1 {
+	__u32 enable;
+	__s16 sharpen_level1;
+	__s16 sharpen_level2;
+	__u16 clip;
+	__u16 limit;
+	__u16 thr_quiet;
+	__u16 thr_dieout;
+	__u16 thr_low;
+	__u16 thr_high;
+	__u16 prec_shift;
+	__s16 adjust_a[SDE_MAX_DE_CURVES];
+	__s16 adjust_b[SDE_MAX_DE_CURVES];
+	__s16 adjust_c[SDE_MAX_DE_CURVES];
+};
+
+/*
+ * Scaler configuration flags
+ */
+
+/* Disable dynamic expansion */
+#define SDE_DYN_EXP_DISABLE 0x1
+
+#define SDE_DE_LPF_BLEND_FILT
+#define SDE_DE_LPF_BLEND_FLAG_EN (1 << 0)
+
+#define SDE_DRM_QSEED3LITE
+#define SDE_DRM_QSEED4
+#define SDE_DRM_INLINE_PREDOWNSCALE
+#define SDE_DRM_QSEED6
+
+/**
+ * struct sde_drm_scaler_v2 - version 2 of struct sde_drm_scaler
+ * @enable:            Scaler enable
+ * @dir_en:            Detail enhancer enable
+ * @pe:                Pixel extension settings
+ * @horz_decimate:     Horizontal decimation factor
+ * @vert_decimate:     Vertical decimation factor
+ * @init_phase_x:      Initial scaler phase values for x
+ * @phase_step_x:      Phase step values for x
+ * @init_phase_y:      Initial scaler phase values for y
+ * @phase_step_y:      Phase step values for y
+ * @preload_x:         Horizontal preload value
+ * @preload_y:         Vertical preload value
+ * @src_width:         Source width
+ * @src_height:        Source height
+ * @dst_width:         Destination width
+ * @dst_height:        Destination height
+ * @y_rgb_filter_cfg:  Y/RGB plane filter configuration
+ * @uv_filter_cfg:     UV plane filter configuration
+ * @alpha_filter_cfg:  Alpha filter configuration
+ * @blend_cfg:         Selection of blend coefficients
+ * @lut_flag:          LUT configuration flags
+ * @dir_lut_idx:       2d 4x4 LUT index
+ * @y_rgb_cir_lut_idx: Y/RGB circular LUT index
+ * @uv_cir_lut_idx:    UV circular LUT index
+ * @y_rgb_sep_lut_idx: Y/RGB separable LUT index
+ * @uv_sep_lut_idx:    UV separable LUT index
+ * @de:                Detail enhancer settings
+ * @dir_weight:        Directional Weight
+ * @unsharp_mask_blend: Unsharp Blend Filter Ratio
+ * @de_blend:          Ratio of two unsharp mask filters
+ * @flags:             Scaler configuration flags
+ * @pre_downscale_x_0  Pre-downscale ratio, x-direction, plane 0(Y/RGB)
+ * @pre_downscale_x_1  Pre-downscale ratio, x-direction, plane 1(UV)
+ * @pre_downscale_y_0  Pre-downscale ratio, y-direction, plane 0(Y/RGB)
+ * @pre_downscale_y_1  Pre-downscale ratio, y-direction, plane 1(UV)
+ * @de_lpf_flags:      Detail enhancer lpf blned configuration flags
+ * @de_lpf_h:          Detail enhancer lpf blend high
+ * @de_lpf_l:          Detail enhancer lpf blend low
+ * @de_lpf_m:          Detail enhancer lpf blend medium
+ * @dir45_en:          45/-45 degree direction filtering enable
+ * @cor_en:            corner enhancer enable
+ */
+struct sde_drm_scaler_v2 {
+	/*
+	 * General definitions
+	 */
+	__u32 enable;
+	__u32 dir_en;
+
+	/*
+	 * Pix ext settings
+	 */
+	struct sde_drm_pix_ext_v1 pe;
+
+	/*
+	 * Decimation settings
+	 */
+	__u32 horz_decimate;
+	__u32 vert_decimate;
+
+	/*
+	 * Phase settings
+	 */
+	__s32 init_phase_x[SDE_MAX_PLANES];
+	__s32 phase_step_x[SDE_MAX_PLANES];
+	__s32 init_phase_y[SDE_MAX_PLANES];
+	__s32 phase_step_y[SDE_MAX_PLANES];
+
+	__u32 preload_x[SDE_MAX_PLANES];
+	__u32 preload_y[SDE_MAX_PLANES];
+	__u32 src_width[SDE_MAX_PLANES];
+	__u32 src_height[SDE_MAX_PLANES];
+
+	__u32 dst_width;
+	__u32 dst_height;
+
+	__u32 y_rgb_filter_cfg;
+	__u32 uv_filter_cfg;
+	__u32 alpha_filter_cfg;
+	__u32 blend_cfg;
+
+	__u32 lut_flag;
+	__u32 dir_lut_idx;
+
+	/* for Y(RGB) and UV planes*/
+	__u32 y_rgb_cir_lut_idx;
+	__u32 uv_cir_lut_idx;
+	__u32 y_rgb_sep_lut_idx;
+	__u32 uv_sep_lut_idx;
+
+	/*
+	 * Detail enhancer settings
+	 */
+	struct sde_drm_de_v1 de;
+	__u32 dir_weight;
+	__u32 unsharp_mask_blend;
+	__u32 de_blend;
+	__u32 flags;
+
+	/*
+	 * Inline pre-downscale settings
+	 */
+	__u32 pre_downscale_x_0;
+	__u32 pre_downscale_x_1;
+	__u32 pre_downscale_y_0;
+	__u32 pre_downscale_y_1;
+
+	__u32 de_lpf_flags;
+	__u32 de_lpf_h;
+	__u32 de_lpf_l;
+	__u32 de_lpf_m;
+	__u32 dir45_en;
+	__u32 cor_en;
+};
+
+/* Number of dest scalers supported */
+#define SDE_MAX_DS_COUNT 4
+
+/*
+ * Destination scaler flag config
+ */
+#define SDE_DRM_DESTSCALER_ENABLE           0x1
+#define SDE_DRM_DESTSCALER_SCALE_UPDATE     0x2
+#define SDE_DRM_DESTSCALER_ENHANCER_UPDATE  0x4
+#define SDE_DRM_DESTSCALER_PU_ENABLE        0x8
+
+/**
+ * struct sde_drm_dest_scaler_cfg - destination scaler config structure
+ * @flags:      Flag to switch between mode for destination scaler
+ *              refer to destination scaler flag config
+ * @index:      Destination scaler selection index
+ * @lm_width:   Layer mixer width configuration
+ * @lm_height:  Layer mixer height configuration
+ * @scaler_cfg: The scaling parameters for all the mode except disable
+ *              Userspace pointer to struct sde_drm_scaler_v2
+ */
+struct sde_drm_dest_scaler_cfg {
+	__u32 flags;
+	__u32 index;
+	__u32 lm_width;
+	__u32 lm_height;
+	__u64 scaler_cfg;
+};
+
+/**
+ * struct sde_drm_dest_scaler_data - destination scaler data struct
+ * @num_dest_scaler: Number of dest scalers to be configured
+ * @ds_cfg:          Destination scaler block configuration
+ */
+struct sde_drm_dest_scaler_data {
+	__u32 num_dest_scaler;
+	struct sde_drm_dest_scaler_cfg ds_cfg[SDE_MAX_DS_COUNT];
+};
+
+/*
+ * Define constants for struct sde_drm_csc
+ */
+#define SDE_CSC_MATRIX_COEFF_SIZE   9
+#define SDE_CSC_CLAMP_SIZE          6
+#define SDE_CSC_BIAS_SIZE           3
+
+/**
+ * struct sde_drm_csc_v1 - version 1 of struct sde_drm_csc
+ * @ctm_coeff:          Matrix coefficients, in S31.32 format
+ * @pre_bias:           Pre-bias array values
+ * @post_bias:          Post-bias array values
+ * @pre_clamp:          Pre-clamp array values
+ * @post_clamp:         Post-clamp array values
+ */
+struct sde_drm_csc_v1 {
+	__s64 ctm_coeff[SDE_CSC_MATRIX_COEFF_SIZE];
+	__u32 pre_bias[SDE_CSC_BIAS_SIZE];
+	__u32 post_bias[SDE_CSC_BIAS_SIZE];
+	__u32 pre_clamp[SDE_CSC_CLAMP_SIZE];
+	__u32 post_clamp[SDE_CSC_CLAMP_SIZE];
+};
+
+/**
+ * struct sde_drm_color - struct to store the color and alpha values
+ * @color_0: Color 0 value
+ * @color_1: Color 1 value
+ * @color_2: Color 2 value
+ * @color_3: Color 3 value
+ */
+struct sde_drm_color {
+	__u32 color_0;
+	__u32 color_1;
+	__u32 color_2;
+	__u32 color_3;
+};
+
+/* Total number of supported dim layers */
+#define SDE_MAX_DIM_LAYERS 7
+
+/* SDE_DRM_DIM_LAYER_CONFIG_FLAG - flags for Dim Layer */
+/* Color fill inside of the rect, including border */
+#define SDE_DRM_DIM_LAYER_INCLUSIVE     0x1
+/* Color fill outside of the rect, excluding border */
+#define SDE_DRM_DIM_LAYER_EXCLUSIVE     0x2
+
+ /* bitmask for allowed_dsc_reservation_switch property */
+#define SDE_DP_DSC_RESERVATION_SWITCH (1 << 0)
+
+/**
+ * struct sde_drm_dim_layer - dim layer cfg struct
+ * @flags:         Refer SDE_DRM_DIM_LAYER_CONFIG_FLAG for possible values
+ * @stage:         Blending stage of the dim layer
+ * @color_fill:    Color fill for dim layer
+ * @rect:          Dim layer coordinates
+ */
+struct sde_drm_dim_layer_cfg {
+	__u32 flags;
+	__u32 stage;
+	struct sde_drm_color color_fill;
+	struct drm_clip_rect rect;
+};
+
+/**
+ * struct sde_drm_dim_layer_v1 - version 1 of dim layer struct
+ * @num_layers:    Numer of Dim Layers
+ * @layer:         Dim layer user cfgs ptr for the num_layers
+ */
+struct sde_drm_dim_layer_v1 {
+	__u32 num_layers;
+	struct sde_drm_dim_layer_cfg layer_cfg[SDE_MAX_DIM_LAYERS];
+};
+
+/* Writeback Config version definition */
+#define SDE_DRM_WB_CFG		0x1
+
+/* SDE_DRM_WB_CONFIG_FLAGS - Writeback configuration flags */
+#define SDE_DRM_WB_CFG_FLAGS_CONNECTED	(1<<0)
+
+/**
+ * struct sde_drm_wb_cfg - Writeback configuration structure
+ * @flags:		see DRM_MSM_WB_CONFIG_FLAGS
+ * @connector_id:	writeback connector identifier
+ * @count_modes:	Count of modes in modes_ptr
+ * @modes:		Pointer to struct drm_mode_modeinfo
+ */
+struct sde_drm_wb_cfg {
+	__u32 flags;
+	__u32 connector_id;
+	__u32 count_modes;
+	__u64 modes;
+};
+
+#define SDE_MAX_ROI_V1	4
+#define SDE_DRM_SPR_ROI	1
+/* DRM_ROI_CONFIG_FLAGS */
+#define SDE_DRM_ROI_SPR_FLAG_EN	(1 << 0)
+
+/**
+ * struct sde_drm_roi_v1 - list of regions of interest for a drm object
+ * @num_rects: number of valid rectangles in the roi array
+ * @roi: list of roi rectangles
+ * @roi_feature_flags: flags indicates that specific roi rect is valid or not
+ * @spr_roi: list of roi rectangles for spr
+ */
+struct sde_drm_roi_v1 {
+	__u32 num_rects;
+	struct drm_clip_rect roi[SDE_MAX_ROI_V1];
+	__u32 roi_feature_flags;
+	struct drm_clip_rect spr_roi[SDE_MAX_ROI_V1];
+};
+
+/**
+ * Define extended power modes supported by the SDE connectors.
+ */
+#define SDE_MODE_DPMS_ON	0
+#define SDE_MODE_DPMS_LP1	1
+#define SDE_MODE_DPMS_LP2	2
+#define SDE_MODE_DPMS_STANDBY	3
+#define SDE_MODE_DPMS_SUSPEND	4
+#define SDE_MODE_DPMS_OFF	5
+
+/**
+ * sde recovery events for notifying client
+ */
+#define SDE_RECOVERY_SUCCESS		0
+#define SDE_RECOVERY_CAPTURE		1
+#define SDE_RECOVERY_HARD_RESET		2
+
+/**
+ * Define UBWC statistics config
+ */
+#define UBWC_STATS_MAX_ROI		0x3
+
+/**
+ * struct sde_drm_ubwc_stats_roi - region of interest for ubwc stats
+ * y_coord0: first y offset from top of display
+ * y_coord1: second y offset from top of display
+ */
+struct sde_drm_ubwc_stats_roi {
+	__u16 y_coord0;
+	__u16 y_coord1;
+};
+
+/**
+ * struct sde_drm_ubwc_stats_data: ubwc statistics
+ * roi: region of interest
+ * worst_bw: worst bandwidth, per roi
+ * worst_bw_y_coord: y offset (row) location of worst bandwidth, per roi
+ * total_bw: total bandwidth, per roi
+ * error: error status
+ * meta_error: meta error data
+ */
+struct sde_drm_ubwc_stats_data {
+	struct sde_drm_ubwc_stats_roi roi;
+	__u16 worst_bw[UBWC_STATS_MAX_ROI];
+	__u16 worst_bw_y_coord[UBWC_STATS_MAX_ROI];
+	__u32 total_bw[UBWC_STATS_MAX_ROI];
+	__u32 error;
+	__u32 meta_error;
+};
+
+/**
+ * Define frame data config
+ */
+#define SDE_FRAME_DATA_BUFFER_MAX	0x3
+#define SDE_FRAME_DATA_GUARD_BYTES	0xFF
+#define SDE_FRAME_DATA_MAX_PLANES	0x14
+
+/**
+ * struct sde_drm_frame_data_buffers_ctrl - control frame data buffers
+ * num_buffers: number of allocated buffers
+ * fds: fd list for allocated buffers
+ */
+struct sde_drm_frame_data_buffers_ctrl {
+	__u32 num_buffers;
+	__u32 fds[SDE_FRAME_DATA_BUFFER_MAX];
+};
+
+/**
+ * struct sde_drm_frame_data_buf - frame data buffer info sent to userspace
+ * fd: buffer fd
+ * offset: offset from buffer address
+ * status: status flag
+ */
+struct sde_drm_frame_data_buf {
+	__u32 fd;
+	__u32 offset;
+	__u32 status;
+};
+
+/**
+ * struct sde_drm_plane_frame_data - definition of plane frame data struct
+ * plane_id: drm plane id
+ * ubwc_stats: ubwc statistics
+ */
+struct sde_drm_plane_frame_data {
+	__u32 plane_id;
+
+	struct sde_drm_ubwc_stats_data ubwc_stats;
+};
+
+/**
+ * struct sde_drm_frame_data_packet - definition of frame data struct
+ * frame_count: interface frame count
+ * commit_count: sw commit count
+ * plane_frame_data: data available per plane
+ */
+struct sde_drm_frame_data_packet {
+	__u32 frame_count;
+	__u64 commit_count;
+
+	struct sde_drm_plane_frame_data plane_frame_data[SDE_FRAME_DATA_MAX_PLANES];
+};
+
+/*
+ * Colorimetry Data Block values
+ * These bit nums are defined as per the CTA spec
+ * and indicate the colorspaces supported by the sink
+ */
+#define DRM_EDID_CLRMETRY_xvYCC_601   (1 << 0)
+#define DRM_EDID_CLRMETRY_xvYCC_709   (1 << 1)
+#define DRM_EDID_CLRMETRY_sYCC_601    (1 << 2)
+#define DRM_EDID_CLRMETRY_ADOBE_YCC_601  (1 << 3)
+#define DRM_EDID_CLRMETRY_ADOBE_RGB     (1 << 4)
+#define DRM_EDID_CLRMETRY_BT2020_CYCC (1 << 5)
+#define DRM_EDID_CLRMETRY_BT2020_YCC  (1 << 6)
+#define DRM_EDID_CLRMETRY_BT2020_RGB  (1 << 7)
+#define DRM_EDID_CLRMETRY_DCI_P3      (1 << 15)
+
+/*
+ * HDR Metadata
+ * These are defined as per EDID spec and shall be used by the sink
+ * to set the HDR metadata for playback from userspace.
+ */
+
+#define HDR_PRIMARIES_COUNT   3
+
+/* HDR EOTF */
+#define HDR_EOTF_SDR_LUM_RANGE	0x0
+#define HDR_EOTF_HDR_LUM_RANGE	0x1
+#define HDR_EOTF_SMTPE_ST2084	0x2
+#define HDR_EOTF_HLG		0x3
+
+#define DRM_MSM_EXT_HDR_METADATA
+#define DRM_MSM_EXT_HDR_PLUS_METADATA
+struct drm_msm_ext_hdr_metadata {
+	__u32 hdr_state;        /* HDR state */
+	__u32 eotf;             /* electro optical transfer function */
+	__u32 hdr_supported;    /* HDR supported */
+	__u32 display_primaries_x[HDR_PRIMARIES_COUNT]; /* Primaries x */
+	__u32 display_primaries_y[HDR_PRIMARIES_COUNT]; /* Primaries y */
+	__u32 white_point_x;    /* white_point_x */
+	__u32 white_point_y;    /* white_point_y */
+	__u32 max_luminance;    /* Max luminance */
+	__u32 min_luminance;    /* Min Luminance */
+	__u32 max_content_light_level; /* max content light level */
+	__u32 max_average_light_level; /* max average light level */
+
+	__u64 hdr_plus_payload;     /* user pointer to dynamic HDR payload */
+	__u32 hdr_plus_payload_size;/* size of dynamic HDR payload data */
+};
+
+/**
+ * HDR sink properties
+ * These are defined as per EDID spec and shall be used by the userspace
+ * to determine the HDR properties to be set to the sink.
+ */
+#define DRM_MSM_EXT_HDR_PROPERTIES
+#define DRM_MSM_EXT_HDR_PLUS_PROPERTIES
+struct drm_msm_ext_hdr_properties {
+	__u8 hdr_metadata_type_one;   /* static metadata type one */
+	__u32 hdr_supported;          /* HDR supported */
+	__u32 hdr_eotf;               /* electro optical transfer function */
+	__u32 hdr_max_luminance;      /* Max luminance */
+	__u32 hdr_avg_luminance;      /* Avg luminance */
+	__u32 hdr_min_luminance;      /* Min Luminance */
+
+	__u32 hdr_plus_supported;     /* HDR10+ supported */
+};
+
+/* HDR WRGB x and y index */
+#define DISPLAY_PRIMARIES_WX 0
+#define DISPLAY_PRIMARIES_WY 1
+#define DISPLAY_PRIMARIES_RX 2
+#define DISPLAY_PRIMARIES_RY 3
+#define DISPLAY_PRIMARIES_GX 4
+#define DISPLAY_PRIMARIES_GY 5
+#define DISPLAY_PRIMARIES_BX 6
+#define DISPLAY_PRIMARIES_BY 7
+#define DISPLAY_PRIMARIES_MAX 8
+
+struct drm_panel_hdr_properties {
+	__u32 hdr_enabled;
+
+	/* WRGB X and y values arrayed in format */
+	/* [WX, WY, RX, RY, GX, GY, BX, BY] */
+	__u32 display_primaries[DISPLAY_PRIMARIES_MAX];
+
+	/* peak brightness supported by panel */
+	__u32 peak_brightness;
+	/* Blackness level supported by panel */
+	__u32 blackness_level;
+};
+
+/**
+ * struct drm_msm_event_req - Payload to event enable/disable ioctls.
+ * @object_id: DRM object id. e.g.: for crtc pass crtc id.
+ * @object_type: DRM object type. e.g.: for crtc set it to DRM_MODE_OBJECT_CRTC.
+ * @event: Event for which notification is being enabled/disabled.
+ *         e.g.: for Histogram set - DRM_EVENT_HISTOGRAM.
+ * @client_context: Opaque pointer that will be returned during event response
+ *                  notification.
+ * @index: Object index(e.g.: crtc index), optional for user-space to set.
+ *         Driver will override value based on object_id and object_type.
+ */
+struct drm_msm_event_req {
+	__u32 object_id;
+	__u32 object_type;
+	__u32 event;
+	__u64 client_context;
+	__u32 index;
+};
+
+/**
+ * struct drm_msm_event_resp - payload returned when read is called for
+ *                            custom notifications.
+ * @base: Event type and length of complete notification payload.
+ * @info: Contains information about DRM that which raised this event.
+ * @data: Custom payload that driver returns for event type.
+ *        size of data = base.length - (sizeof(base) + sizeof(info))
+ */
+struct drm_msm_event_resp {
+	struct drm_event base;
+	struct drm_msm_event_req info;
+	__u8 data[];
+};
+
+/**
+ * struct drm_msm_power_ctrl: Payload to enable/disable the power vote
+ * @enable: enable/disable the power vote
+ * @flags:  operation control flags, for future use
+ */
+struct drm_msm_power_ctrl {
+	__u32 enable;
+	__u32 flags;
+};
+
+/**
+ * struct drm_msm_early_wakeup: Payload to early wake up display
+ * @wakeup_hint:  early wakeup hint.
+ * @connector_id: connector id. e.g.: for connector pass connector id.
+ */
+struct drm_msm_early_wakeup {
+	__u32 wakeup_hint;
+	__u32 connector_id;
+};
+
+/**
+ * struct drm_msm_display_hint: Payload for display hint
+ * @hint_flags:  display hint flags.
+ * @data: data struct. e.g.: for display hint parameter.
+ *        Userspace pointer to struct base on hint flags.
+ */
+struct drm_msm_display_hint {
+	__u64 data;
+	__u32 hint_flags;
+};
+
+#define DRM_NOISE_LAYER_CFG
+#define DRM_NOISE_TEMPORAL_FLAG (1 << 0)
+#define DRM_NOISE_ATTN_MAX 255
+#define DRM_NOISE_STREN_MAX 6
+
+/**
+ * struct drm_msm_noise_layer_cfg: Payload to enable/disable noise blend
+ * @flags: operation control flags, for future use
+ * @zposn: noise zorder
+ * @zposattn: attenuation zorder
+ * @attn_factor: attenuation factor in range of 1 to 255
+ * @stength: strength in range of 0 to 6
+ * @alpha_noise: attenuation in range of 1 to 255
+*/
+struct drm_msm_noise_layer_cfg {
+	__u64 flags;
+	__u32 zposn;
+	__u32 zposattn;
+	__u32 attn_factor;
+	__u32 strength;
+	__u32 alpha_noise;
+};
+
+#define FEATURE_DNSC_BLUR
+/* Downscale Blur - number of gaussian coefficient LUTs */
+#define DNSC_BLUR_COEF_NUM		64
+
+/* Downscale Blur flags */
+#define DNSC_BLUR_EN			(1 << 0)
+#define DNSC_BLUR_RND_8B_EN		(1 << 1)
+#define DNSC_BLUR_DITHER_EN		(1 << 2)
+
+#define DNSC_BLUR_MIRROR_BLK_CFG	(1 << 16)
+#define DNSC_BLUR_INDEPENDENT_BLK_CFG	(1 << 17)
+
+/* Downscale Blur horizontal/vertical filter flags */
+#define DNSC_BLUR_GAUS_FILTER		(1 << 0)
+#define DNSC_BLUR_PCMN_FILTER		(1 << 1)
+
+/* Downscale Blur Dither matrix size */
+#define DNSC_BLUR_DITHER_MATRIX_SZ	16
+
+/* Downscale Blur Dither flags */
+#define DNSC_BLUR_DITHER_LUMA_MODE	(1 << 0)
+
+/**
+ * struct sde_drm_dnsc_blur_cfg - Downscale Blur config structure
+ * @flags: Flags to indicate features enabled, values are
+ *          based on "Downscale Blur flags"
+ * @num_blocks: Active dnsc_blur blocks used for the display
+ * @src_width: Source width configuration
+ * @src_height: Source height configuration
+ * @dst_width: Destination width configuration
+ * @dst_height: Destination height configuration
+ * @flags_h: Flags for horizontal downscaling, values are
+ *            based on "Downscale Blur horizontal/vertical filter flags"
+ * @flags_v: Flags for veritcal downscaling
+ * @phase_init_h: Initial phase value for horizontal downscaling
+ * @phase_step_h: Phase step value for horizontal downscaling
+ * @phase_init_v: Initial phase value for vertical downscaling
+ * @phase_step_v: Phase step value for vertical downscaling
+ * @norm_h: Horizontal downscale normalization downshift value
+ * @ratio_h: Horizontal downscale ratio value
+ * @norm_v: Vertical downscale normalization downshift value
+ * @ratio_v: Vertical downscale ratio value
+ * @coef_hori: Horizontal downscale LUT coefficients
+ * @coef_vert: Vertical downscale LUT coefficients
+ * @dither_flags: Flags for dither customization, values are
+ *                 based on "Downscale Blur Dither flags"
+ * @temporal_en: Temperal dither enable
+ * @c0_bitdepth: c0 component bit depth
+ * @c1_bitdepth: c1 component bit depth
+ * @c2_bitdepth: c2 component bit depth
+ * @c3_bitdepth: c2 component bit depth
+ * @dither_matrix: Dither strength matrix
+ */
+struct sde_drm_dnsc_blur_cfg {
+	__u64 flags;
+	__u32 num_blocks;
+
+	__u32 src_width;
+	__u32 src_height;
+	__u32 dst_width;
+	__u32 dst_height;
+
+	__u32 flags_h;
+	__u32 flags_v;
+
+	/* pcmn filter parameters */
+	__u32 phase_init_h;
+	__u32 phase_step_h;
+	__u32 phase_init_v;
+	__u32 phase_step_v;
+
+	/* gaussian filter parameters */
+	__u32 norm_h;
+	__u32 ratio_h;
+	__u32 norm_v;
+	__u32 ratio_v;
+	__u32 coef_hori[DNSC_BLUR_COEF_NUM];
+	__u32 coef_vert[DNSC_BLUR_COEF_NUM];
+
+	/* dither configs */
+	__u64 dither_flags;
+	__u32 temporal_en;
+	__u32 c0_bitdepth;
+	__u32 c1_bitdepth;
+	__u32 c2_bitdepth;
+	__u32 c3_bitdepth;
+	__u32 dither_matrix[DNSC_BLUR_DITHER_MATRIX_SZ];
+};
+
+#define DRM_SDE_WB_CONFIG              0x40
+#define DRM_MSM_REGISTER_EVENT         0x41
+#define DRM_MSM_DEREGISTER_EVENT       0x42
+#define DRM_MSM_RMFB2                  0x43
+#define DRM_MSM_POWER_CTRL             0x44
+#define DRM_MSM_DISPLAY_HINT           0x45
+
+/* sde custom events */
+#define DRM_EVENT_HISTOGRAM 0x80000000
+#define DRM_EVENT_AD_BACKLIGHT 0x80000001
+#define DRM_EVENT_CRTC_POWER 0x80000002
+#define DRM_EVENT_SYS_BACKLIGHT 0x80000003
+#define DRM_EVENT_SDE_POWER 0x80000004
+#define DRM_EVENT_IDLE_NOTIFY 0x80000005
+#define DRM_EVENT_PANEL_DEAD 0x80000006 /* ESD event */
+#define DRM_EVENT_SDE_HW_RECOVERY 0X80000007
+#define DRM_EVENT_LTM_HIST 0X80000008
+#define DRM_EVENT_LTM_WB_PB 0X80000009
+#define DRM_EVENT_LTM_OFF 0X8000000A
+#define DRM_EVENT_MMRM_CB 0X8000000B
+#define DRM_EVENT_FRAME_DATA 0x8000000C
+#define DRM_EVENT_DIMMING_BL 0X8000000D
+#define DRM_EVENT_VM_RELEASE 0X8000000E
+#define DRM_EVENT_OPR_VALUE 0X8000000F
+#define DRM_EVENT_MISR_SIGN 0X80000010
+
+#ifndef DRM_MODE_FLAG_VID_MODE_PANEL
+#define DRM_MODE_FLAG_VID_MODE_PANEL        0x01
+#endif
+#ifndef DRM_MODE_FLAG_CMD_MODE_PANEL
+#define DRM_MODE_FLAG_CMD_MODE_PANEL        0x02
+#endif
+
+#ifndef DRM_MODE_FLAG_DSI_24BPP
+#define DRM_MODE_FLAG_DSI_24BPP             0x01
+#endif
+#ifndef DRM_MODE_FLAG_DSI_30BPP
+#define DRM_MODE_FLAG_DSI_30BPP             0x02
+#endif
+
+/* display hint flags*/
+#define DRM_MSM_DISPLAY_EARLY_WAKEUP_HINT         0x01
+#define DRM_MSM_DISPLAY_POWER_COLLAPSE_HINT       0x02
+#define DRM_MSM_DISPLAY_IDLE_TIMEOUT_HINT         0x04
+#define DRM_MSM_DISPLAY_MODE_CHANGE_HINT          0x08
+
+#define DRM_MSM_WAKE_UP_ALL_DISPLAYS        0xFFFFFFFF
+
+#define DRM_IOCTL_SDE_WB_CONFIG \
+	DRM_IOW((DRM_COMMAND_BASE + DRM_SDE_WB_CONFIG), struct sde_drm_wb_cfg)
+#define DRM_IOCTL_MSM_REGISTER_EVENT   DRM_IOW((DRM_COMMAND_BASE + \
+			DRM_MSM_REGISTER_EVENT), struct drm_msm_event_req)
+#define DRM_IOCTL_MSM_DEREGISTER_EVENT DRM_IOW((DRM_COMMAND_BASE + \
+			DRM_MSM_DEREGISTER_EVENT), struct drm_msm_event_req)
+#define DRM_IOCTL_MSM_RMFB2 DRM_IOW((DRM_COMMAND_BASE + \
+			DRM_MSM_RMFB2), unsigned int)
+#define DRM_IOCTL_MSM_POWER_CTRL DRM_IOW((DRM_COMMAND_BASE + \
+			DRM_MSM_POWER_CTRL), struct drm_msm_power_ctrl)
+#define DRM_IOCTL_MSM_DISPLAY_HINT DRM_IOW((DRM_COMMAND_BASE + \
+			DRM_MSM_DISPLAY_HINT), struct drm_msm_display_hint)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _SDE_DRM_H_ */

+ 3 - 0
qcom/opensource/display-drivers/include/uapi/display/hdcp/Kbuild

@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
+
+header-y += msm_hdmi_hdcp_mgr.h

+ 61 - 0
qcom/opensource/display-drivers/include/uapi/display/hdcp/msm_hdmi_hdcp_mgr.h

@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _UAPI__MSM_HDMI_HDCP_MGR_H
+#define _UAPI__MSM_HDMI_HDCP_MGR_H
+
+#include <linux/types.h>
+
+enum DS_TYPE {  /* type of downstream device */
+	DS_UNKNOWN,
+	DS_RECEIVER,
+	DS_REPEATER,
+};
+
+enum {
+	MSG_ID_IDX,
+	RET_CODE_IDX,
+	HEADER_LEN,
+};
+
+enum RET_CODE {
+	HDCP_NOT_AUTHED,
+	HDCP_AUTHED,
+	HDCP_DISABLE,
+};
+
+enum MSG_ID { /* List of functions expected to be called after it */
+	DOWN_CHECK_TOPOLOGY,
+	UP_REQUEST_TOPOLOGY,
+	UP_SEND_TOPOLOGY,
+	DOWN_REQUEST_TOPOLOGY,
+	MSG_NUM,
+};
+
+enum SOURCE_ID {
+	HDCP_V1_TX,
+	HDCP_V1_RX,
+	HDCP_V2_RX,
+	HDCP_V2_TX,
+	SRC_NUM,
+};
+
+/*
+ * how to parse sysfs params buffer
+ * from hdcp_tx driver.
+ */
+
+struct HDCP_V2V1_MSG_TOPOLOGY {
+	/* indicates downstream's type */
+	__u32 ds_type;
+	__u8 bksv[5];
+	__u8 dev_count;
+	__u8 depth;
+	__u8 ksv_list[5 * 127];
+	__u32 max_cascade_exceeded;
+	__u32 max_dev_exceeded;
+};
+
+#endif /* _UAPI__MSM_HDMI_HDCP_MGR_H */

+ 4 - 0
qcom/opensource/display-drivers/include/uapi/display/media/Kbuild

@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note
+
+header-y += msm_sde_rotator.h
+header-y += mmm_color_fmt.h

+ 1472 - 0
qcom/opensource/display-drivers/include/uapi/display/media/mmm_color_fmt.h

@@ -0,0 +1,1472 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef __MMM_COLOR_FMT_INFO_H__
+#define __MMM_COLOR_FMT_INFO_H__
+
+/* Width and Height should be multiple of 16 */
+#define INTERLACE_WIDTH_MAX 1920
+#define INTERLACE_HEIGHT_MAX 1920
+#define INTERLACE_MB_PER_FRAME_MAX ((1920*1088)/256)
+
+#ifndef MMM_COLOR_FMT_ALIGN
+#define MMM_COLOR_FMT_ALIGN(__sz, __align) (((__align) & ((__align) - 1)) ?\
+	((((__sz) + (__align) - 1) / (__align)) * (__align)) :\
+	(((__sz) + (__align) - 1) & (~((__align) - 1))))
+#endif
+
+#ifndef MMM_COLOR_FMT_ROUNDUP
+#define MMM_COLOR_FMT_ROUNDUP(__sz, __r) (((__sz) + ((__r) - 1)) / (__r))
+#endif
+
+enum mmm_color_fmts {
+	/* Venus NV12:
+	 * YUV 4:2:0 image with a plane of 8 bit Y samples followed
+	 * by an interleaved U/V plane containing 8 bit 2x2 subsampled
+	 * colour difference samples.
+	 *
+	 * <-------- Y/UV_Stride -------->
+	 * <------- Width ------->
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  ^           ^
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  Height      |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |          Y_Scanlines
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              V
+	 * U V U V U V U V U V U V . . . .  ^
+	 * U V U V U V U V U V U V . . . .  |
+	 * U V U V U V U V U V U V . . . .  |
+	 * U V U V U V U V U V U V . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  --> Buffer size alignment
+	 *
+	 * Y_Stride : Width aligned to 128
+	 * UV_Stride : Width aligned to 128
+	 * Y_Scanlines: Height aligned to 32
+	 * UV_Scanlines: Height/2 aligned to 16
+	 * Total size = align(Y_Stride * Y_Scanlines
+	 *          + UV_Stride * UV_Scanlines, 4096)
+	 */
+	MMM_COLOR_FMT_NV12,
+	/* Venus NV21:
+	 * YUV 4:2:0 image with a plane of 8 bit Y samples followed
+	 * by an interleaved V/U plane containing 8 bit 2x2 subsampled
+	 * colour difference samples.
+	 *
+	 * <-------- Y/UV_Stride -------->
+	 * <------- Width ------->
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  ^           ^
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  Height      |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |          Y_Scanlines
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              V
+	 * V U V U V U V U V U V U . . . .  ^
+	 * V U V U V U V U V U V U . . . .  |
+	 * V U V U V U V U V U V U . . . .  |
+	 * V U V U V U V U V U V U . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  --> Padding & Buffer size alignment
+	 *
+	 * Y_Stride : Width aligned to 128
+	 * UV_Stride : Width aligned to 128
+	 * Y_Scanlines: Height aligned to 32
+	 * UV_Scanlines: Height/2 aligned to 16
+	 * Total size = align(Y_Stride * Y_Scanlines
+	 *          + UV_Stride * UV_Scanlines, 4096)
+	 */
+	MMM_COLOR_FMT_NV21,
+	/*
+	 * The buffer can be of 2 types:
+	 * (1) Venus NV12 UBWC Progressive
+	 * (2) Venus NV12 UBWC Interlaced
+	 *
+	 * (1) Venus NV12 UBWC Progressive Buffer Format:
+	 * Compressed Macro-tile format for NV12.
+	 * Contains 4 planes in the following order -
+	 * (A) Y_Meta_Plane
+	 * (B) Y_UBWC_Plane
+	 * (C) UV_Meta_Plane
+	 * (D) UV_UBWC_Plane
+	 *
+	 * Y_Meta_Plane consists of meta information to decode compressed
+	 * tile data in Y_UBWC_Plane.
+	 * Y_UBWC_Plane consists of Y data in compressed macro-tile format.
+	 * UBWC decoder block will use the Y_Meta_Plane data together with
+	 * Y_UBWC_Plane data to produce loss-less uncompressed 8 bit Y samples.
+	 *
+	 * UV_Meta_Plane consists of meta information to decode compressed
+	 * tile data in UV_UBWC_Plane.
+	 * UV_UBWC_Plane consists of UV data in compressed macro-tile format.
+	 * UBWC decoder block will use UV_Meta_Plane data together with
+	 * UV_UBWC_Plane data to produce loss-less uncompressed 8 bit 2x2
+	 * subsampled color difference samples.
+	 *
+	 * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable
+	 * and randomly accessible. There is no dependency between tiles.
+	 *
+	 * <----- Y_Meta_Stride ---->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |         Meta_Y_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <--Compressed tile Y Stride--->
+	 * <------- Width ------->
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  ^           ^
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  Height      |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |        Macro_tile_Y_Scanlines
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 * <----- UV_Meta_Stride ---->
+	 * M M M M M M M M M M M M . .      ^
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      M_UV_Scanlines
+	 * . . . . . . . . . . . . . .      |
+	 * . . . . . . . . . . . . . .      V
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * <--Compressed tile UV Stride--->
+	 * U* V* U* V* U* V* U* V* . . . .  ^
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 *
+	 * Y_Stride = align(Width, 128)
+	 * UV_Stride = align(Width, 128)
+	 * Y_Scanlines = align(Height, 32)
+	 * UV_Scanlines = align(Height/2, 32)
+	 * Y_UBWC_Plane_size = align(Y_Stride * Y_Scanlines, 4096)
+	 * UV_UBWC_Plane_size = align(UV_Stride * UV_Scanlines, 4096)
+	 * Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64)
+	 * Y_Meta_Scanlines = align(roundup(Height, Y_TileHeight), 16)
+	 * Y_Meta_Plane_size = align(Y_Meta_Stride * Y_Meta_Scanlines, 4096)
+	 * UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64)
+	 * UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16)
+	 * UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096)
+	 *
+	 * Total size = align( Y_UBWC_Plane_size + UV_UBWC_Plane_size +
+	 *           Y_Meta_Plane_size + UV_Meta_Plane_size, 4096)
+	 *
+	 *
+	 * (2) Venus NV12 UBWC Interlaced Buffer Format:
+	 * Compressed Macro-tile format for NV12 interlaced.
+	 * Contains 8 planes in the following order -
+	 * (A) Y_Meta_Top_Field_Plane
+	 * (B) Y_UBWC_Top_Field_Plane
+	 * (C) UV_Meta_Top_Field_Plane
+	 * (D) UV_UBWC_Top_Field_Plane
+	 * (E) Y_Meta_Bottom_Field_Plane
+	 * (F) Y_UBWC_Bottom_Field_Plane
+	 * (G) UV_Meta_Bottom_Field_Plane
+	 * (H) UV_UBWC_Bottom_Field_Plane
+	 * Y_Meta_Top_Field_Plane consists of meta information to decode
+	 * compressed tile data for Y_UBWC_Top_Field_Plane.
+	 * Y_UBWC_Top_Field_Plane consists of Y data in compressed macro-tile
+	 * format for top field of an interlaced frame.
+	 * UBWC decoder block will use the Y_Meta_Top_Field_Plane data together
+	 * with Y_UBWC_Top_Field_Plane data to produce loss-less uncompressed
+	 * 8 bit Y samples for top field of an interlaced frame.
+	 *
+	 * UV_Meta_Top_Field_Plane consists of meta information to decode
+	 * compressed tile data in UV_UBWC_Top_Field_Plane.
+	 * UV_UBWC_Top_Field_Plane consists of UV data in compressed macro-tile
+	 * format for top field of an interlaced frame.
+	 * UBWC decoder block will use UV_Meta_Top_Field_Plane data together
+	 * with UV_UBWC_Top_Field_Plane data to produce loss-less uncompressed
+	 * 8 bit subsampled color difference samples for top field of an
+	 * interlaced frame.
+	 *
+	 * Each tile in Y_UBWC_Top_Field_Plane/UV_UBWC_Top_Field_Plane is
+	 * independently decodable and randomly accessible. There is no
+	 * dependency between tiles.
+	 *
+	 * Y_Meta_Bottom_Field_Plane consists of meta information to decode
+	 * compressed tile data for Y_UBWC_Bottom_Field_Plane.
+	 * Y_UBWC_Bottom_Field_Plane consists of Y data in compressed macro-tile
+	 * format for bottom field of an interlaced frame.
+	 * UBWC decoder block will use the Y_Meta_Bottom_Field_Plane data
+	 * together with Y_UBWC_Bottom_Field_Plane data to produce loss-less
+	 * uncompressed 8 bit Y samples for bottom field of an interlaced frame.
+	 *
+	 * UV_Meta_Bottom_Field_Plane consists of meta information to decode
+	 * compressed tile data in UV_UBWC_Bottom_Field_Plane.
+	 * UV_UBWC_Bottom_Field_Plane consists of UV data in compressed
+	 * macro-tile format for bottom field of an interlaced frame.
+	 * UBWC decoder block will use UV_Meta_Bottom_Field_Plane data together
+	 * with UV_UBWC_Bottom_Field_Plane data to produce loss-less
+	 * uncompressed 8 bit subsampled color difference samples for bottom
+	 * field of an interlaced frame.
+	 *
+	 * Each tile in Y_UBWC_Bottom_Field_Plane/UV_UBWC_Bottom_Field_Plane is
+	 * independently decodable and randomly accessible. There is no
+	 * dependency between tiles.
+	 *
+	 * <-----Y_TF_Meta_Stride---->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . . Half_height      |
+	 * M M M M M M M M M M M M . .      |         Meta_Y_TF_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <-Compressed tile Y_TF Stride->
+	 * <------- Width ------->
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  ^           ^
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . . Half_height  |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |        Macro_tile_Y_TF_Scanlines
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 * <----UV_TF_Meta_Stride---->
+	 * M M M M M M M M M M M M . .      ^
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      M_UV_TF_Scanlines
+	 * . . . . . . . . . . . . . .      |
+	 * . . . . . . . . . . . . . .      V
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * <-Compressed tile UV_TF Stride->
+	 * U* V* U* V* U* V* U* V* . . . .  ^
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  UV_TF_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 * <-----Y_BF_Meta_Stride---->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . . Half_height      |
+	 * M M M M M M M M M M M M . .      |         Meta_Y_BF_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <-Compressed tile Y_BF Stride->
+	 * <------- Width ------->
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  ^           ^
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . . Half_height  |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |        Macro_tile_Y_BF_Scanlines
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 * <----UV_BF_Meta_Stride---->
+	 * M M M M M M M M M M M M . .      ^
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      M_UV_BF_Scanlines
+	 * . . . . . . . . . . . . . .      |
+	 * . . . . . . . . . . . . . .      V
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * <-Compressed tile UV_BF Stride->
+	 * U* V* U* V* U* V* U* V* . . . .  ^
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  UV_BF_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 *
+	 * Half_height = (Height+1)>>1
+	 * Y_TF_Stride = align(Width, 128)
+	 * UV_TF_Stride = align(Width, 128)
+	 * Y_TF_Scanlines = align(Half_height, 32)
+	 * UV_TF_Scanlines = align((Half_height+1)/2, 32)
+	 * Y_UBWC_TF_Plane_size = align(Y_TF_Stride * Y_TF_Scanlines, 4096)
+	 * UV_UBWC_TF_Plane_size = align(UV_TF_Stride * UV_TF_Scanlines, 4096)
+	 * Y_TF_Meta_Stride = align(roundup(Width, Y_TileWidth), 64)
+	 * Y_TF_Meta_Scanlines = align(roundup(Half_height, Y_TileHeight), 16)
+	 * Y_TF_Meta_Plane_size =
+	 *     align(Y_TF_Meta_Stride * Y_TF_Meta_Scanlines, 4096)
+	 * UV_TF_Meta_Stride = align(roundup(Width, UV_TileWidth), 64)
+	 * UV_TF_Meta_Scanlines = align(roundup(Half_height, UV_TileHeight), 16)
+	 * UV_TF_Meta_Plane_size =
+	 *     align(UV_TF_Meta_Stride * UV_TF_Meta_Scanlines, 4096)
+	 * Y_BF_Stride = align(Width, 128)
+	 * UV_BF_Stride = align(Width, 128)
+	 * Y_BF_Scanlines = align(Half_height, 32)
+	 * UV_BF_Scanlines = align((Half_height+1)/2, 32)
+	 * Y_UBWC_BF_Plane_size = align(Y_BF_Stride * Y_BF_Scanlines, 4096)
+	 * UV_UBWC_BF_Plane_size = align(UV_BF_Stride * UV_BF_Scanlines, 4096)
+	 * Y_BF_Meta_Stride = align(roundup(Width, Y_TileWidth), 64)
+	 * Y_BF_Meta_Scanlines = align(roundup(Half_height, Y_TileHeight), 16)
+	 * Y_BF_Meta_Plane_size =
+	 *     align(Y_BF_Meta_Stride * Y_BF_Meta_Scanlines, 4096)
+	 * UV_BF_Meta_Stride = align(roundup(Width, UV_TileWidth), 64)
+	 * UV_BF_Meta_Scanlines = align(roundup(Half_height, UV_TileHeight), 16)
+	 * UV_BF_Meta_Plane_size =
+	 *     align(UV_BF_Meta_Stride * UV_BF_Meta_Scanlines, 4096)
+	 *
+	 * Total size = align( Y_UBWC_TF_Plane_size + UV_UBWC_TF_Plane_size +
+	 *           Y_TF_Meta_Plane_size + UV_TF_Meta_Plane_size +
+	 *			 Y_UBWC_BF_Plane_size + UV_UBWC_BF_Plane_size +
+	 *           Y_BF_Meta_Plane_size + UV_BF_Meta_Plane_size +, 4096)
+	 */
+	MMM_COLOR_FMT_NV12_UBWC,
+	/* Venus NV12 10-bit UBWC:
+	 * Compressed Macro-tile format for NV12.
+	 * Contains 4 planes in the following order -
+	 * (A) Y_Meta_Plane
+	 * (B) Y_UBWC_Plane
+	 * (C) UV_Meta_Plane
+	 * (D) UV_UBWC_Plane
+	 *
+	 * Y_Meta_Plane consists of meta information to decode compressed
+	 * tile data in Y_UBWC_Plane.
+	 * Y_UBWC_Plane consists of Y data in compressed macro-tile format.
+	 * UBWC decoder block will use the Y_Meta_Plane data together with
+	 * Y_UBWC_Plane data to produce loss-less uncompressed 10 bit Y samples.
+	 *
+	 * UV_Meta_Plane consists of meta information to decode compressed
+	 * tile data in UV_UBWC_Plane.
+	 * UV_UBWC_Plane consists of UV data in compressed macro-tile format.
+	 * UBWC decoder block will use UV_Meta_Plane data together with
+	 * UV_UBWC_Plane data to produce loss-less uncompressed 10 bit 2x2
+	 * subsampled color difference samples.
+	 *
+	 * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable
+	 * and randomly accessible. There is no dependency between tiles.
+	 *
+	 * <----- Y_Meta_Stride ----->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |         Meta_Y_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <--Compressed tile Y Stride--->
+	 * <------- Width ------->
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  ^           ^
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  Height      |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |        Macro_tile_Y_Scanlines
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 * <----- UV_Meta_Stride ---->
+	 * M M M M M M M M M M M M . .      ^
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      M_UV_Scanlines
+	 * . . . . . . . . . . . . . .      |
+	 * . . . . . . . . . . . . . .      V
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * <--Compressed tile UV Stride--->
+	 * U* V* U* V* U* V* U* V* . . . .  ^
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 *
+	 *
+	 * Y_Stride = align(Width * 4/3, 256)
+	 * UV_Stride = align(Width * 4/3, 256)
+	 * Y_Scanlines = align(Height, 32)
+	 * UV_Scanlines = align(Height/2, 16)
+	 * Y_UBWC_Plane_Size = align(Y_Stride * Y_Scanlines, 4096)
+	 * UV_UBWC_Plane_Size = align(UV_Stride * UV_Scanlines, 4096)
+	 * Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64)
+	 * Y_Meta_Scanlines = align(roundup(Height, Y_TileHeight), 16)
+	 * Y_Meta_Plane_size = align(Y_Meta_Stride * Y_Meta_Scanlines, 4096)
+	 * UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64)
+	 * UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16)
+	 * UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096)
+	 *
+	 * Total size = align(Y_UBWC_Plane_size + UV_UBWC_Plane_size +
+	 *           Y_Meta_Plane_size + UV_Meta_Plane_size, 4096)
+	 */
+	MMM_COLOR_FMT_NV12_BPP10_UBWC,
+	/* Venus RGBA8888 format:
+	 * Contains 1 plane in the following order -
+	 * (A) RGBA plane
+	 *
+	 * <-------- RGB_Stride -------->
+	 * <------- Width ------->
+	 * R R R R R R R R R R R R . . . .  ^           ^
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  Height      |
+	 * R R R R R R R R R R R R . . . .  |       RGB_Scanlines
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              V
+	 *
+	 * RGB_Stride = align(Width * 4, 256)
+	 * RGB_Scanlines = align(Height, 32)
+	 * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096)
+	 *
+	 * Total size = align(RGB_Plane_size , 4096)
+	 */
+	MMM_COLOR_FMT_RGBA8888,
+	/* Venus RGBA8888 UBWC format:
+	 * Contains 2 planes in the following order -
+	 * (A) Meta plane
+	 * (B) RGBA plane
+	 *
+	 * <--- RGB_Meta_Stride ---->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |       Meta_RGB_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <-------- RGB_Stride -------->
+	 * <------- Width ------->
+	 * R R R R R R R R R R R R . . . .  ^           ^
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  Height      |
+	 * R R R R R R R R R R R R . . . .  |       RGB_Scanlines
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .    -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 *
+	 * RGB_Stride = align(Width * 4, 256)
+	 * RGB_Scanlines = align(Height, 16)
+	 * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096)
+	 * RGB_Meta_Stride = align(roundup(Width, RGB_TileWidth), 64)
+	 * RGB_Meta_Scanline = align(roundup(Height, RGB_TileHeight), 16)
+	 * RGB_Meta_Plane_size = align(RGB_Meta_Stride *
+	 *		RGB_Meta_Scanlines, 4096)
+	 *
+	 * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size, 4096)
+	 */
+	MMM_COLOR_FMT_RGBA8888_UBWC,
+	/* Venus RGBA1010102 UBWC format:
+	 * Contains 2 planes in the following order -
+	 * (A) Meta plane
+	 * (B) RGBA plane
+	 *
+	 * <--- RGB_Meta_Stride ---->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |       Meta_RGB_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <-------- RGB_Stride -------->
+	 * <------- Width ------->
+	 * R R R R R R R R R R R R . . . .  ^           ^
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  Height      |
+	 * R R R R R R R R R R R R . . . .  |       RGB_Scanlines
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .    -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 *
+	 * RGB_Stride = align(Width * 4, 256)
+	 * RGB_Scanlines = align(Height, 16)
+	 * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096)
+	 * RGB_Meta_Stride = align(roundup(Width, RGB_TileWidth), 64)
+	 * RGB_Meta_Scanline = align(roundup(Height, RGB_TileHeight), 16)
+	 * RGB_Meta_Plane_size = align(RGB_Meta_Stride *
+	 *		RGB_Meta_Scanlines, 4096)
+	 *
+	 * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size, 4096)
+	 */
+	MMM_COLOR_FMT_RGBA1010102_UBWC,
+	/* Venus RGB565 UBWC format:
+	 * Contains 2 planes in the following order -
+	 * (A) Meta plane
+	 * (B) RGB plane
+	 *
+	 * <--- RGB_Meta_Stride ---->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |       Meta_RGB_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <-------- RGB_Stride -------->
+	 * <------- Width ------->
+	 * R R R R R R R R R R R R . . . .  ^           ^
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  Height      |
+	 * R R R R R R R R R R R R . . . .  |       RGB_Scanlines
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .    -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 *
+	 * RGB_Stride = align(Width * 2, 256)
+	 * RGB_Scanlines = align(Height, 16)
+	 * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096)
+	 * RGB_Meta_Stride = align(roundup(Width, RGB_TileWidth), 64)
+	 * RGB_Meta_Scanline = align(roundup(Height, RGB_TileHeight), 16)
+	 * RGB_Meta_Plane_size = align(RGB_Meta_Stride *
+	 *		RGB_Meta_Scanlines, 4096)
+	 *
+	 * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size, 4096)
+	 */
+	MMM_COLOR_FMT_RGB565_UBWC,
+	/* P010 UBWC:
+	 * Compressed Macro-tile format for NV12.
+	 * Contains 4 planes in the following order -
+	 * (A) Y_Meta_Plane
+	 * (B) Y_UBWC_Plane
+	 * (C) UV_Meta_Plane
+	 * (D) UV_UBWC_Plane
+	 *
+	 * Y_Meta_Plane consists of meta information to decode compressed
+	 * tile data in Y_UBWC_Plane.
+	 * Y_UBWC_Plane consists of Y data in compressed macro-tile format.
+	 * UBWC decoder block will use the Y_Meta_Plane data together with
+	 * Y_UBWC_Plane data to produce loss-less uncompressed 10 bit Y samples.
+	 *
+	 * UV_Meta_Plane consists of meta information to decode compressed
+	 * tile data in UV_UBWC_Plane.
+	 * UV_UBWC_Plane consists of UV data in compressed macro-tile format.
+	 * UBWC decoder block will use UV_Meta_Plane data together with
+	 * UV_UBWC_Plane data to produce loss-less uncompressed 10 bit 2x2
+	 * subsampled color difference samples.
+	 *
+	 * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable
+	 * and randomly accessible. There is no dependency between tiles.
+	 *
+	 * <----- Y_Meta_Stride ----->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |         Meta_Y_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <--Compressed tile Y Stride--->
+	 * <------- Width ------->
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  ^           ^
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  Height      |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |        Macro_tile_Y_Scanlines
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 * <----- UV_Meta_Stride ---->
+	 * M M M M M M M M M M M M . .      ^
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      M_UV_Scanlines
+	 * . . . . . . . . . . . . . .      |
+	 * . . . . . . . . . . . . . .      V
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * <--Compressed tile UV Stride--->
+	 * U* V* U* V* U* V* U* V* . . . .  ^
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 *
+	 *
+	 * Y_Stride = align(Width * 2, 256)
+	 * UV_Stride = align(Width * 2, 256)
+	 * Y_Scanlines = align(Height, 16)
+	 * UV_Scanlines = align(Height/2, 16)
+	 * Y_UBWC_Plane_Size = align(Y_Stride * Y_Scanlines, 4096)
+	 * UV_UBWC_Plane_Size = align(UV_Stride * UV_Scanlines, 4096)
+	 * Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64)
+	 * Y_Meta_Scanlines = align(roundup(Height, Y_TileHeight), 16)
+	 * Y_Meta_Plane_size = align(Y_Meta_Stride * Y_Meta_Scanlines, 4096)
+	 * UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64)
+	 * UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16)
+	 * UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096)
+	 *
+	 * Total size = align(Y_UBWC_Plane_size + UV_UBWC_Plane_size +
+	 *           Y_Meta_Plane_size + UV_Meta_Plane_size, 4096)
+	 */
+	MMM_COLOR_FMT_P010_UBWC,
+	/* Venus P010:
+	 * YUV 4:2:0 image with a plane of 10 bit Y samples followed
+	 * by an interleaved U/V plane containing 10 bit 2x2 subsampled
+	 * colour difference samples.
+	 *
+	 * <-------- Y/UV_Stride -------->
+	 * <------- Width ------->
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  ^           ^
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  Height      |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |          Y_Scanlines
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              V
+	 * U V U V U V U V U V U V . . . .  ^
+	 * U V U V U V U V U V U V . . . .  |
+	 * U V U V U V U V U V U V . . . .  |
+	 * U V U V U V U V U V U V . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  --> Buffer size alignment
+	 *
+	 * Y_Stride : Width * 2 aligned to 256
+	 * UV_Stride : Width * 2 aligned to 256
+	 * Y_Scanlines: Height aligned to 32
+	 * UV_Scanlines: Height/2 aligned to 16
+	 * Total size = align(Y_Stride * Y_Scanlines
+	 *          + UV_Stride * UV_Scanlines, 4096)
+	 */
+	MMM_COLOR_FMT_P010,
+	/* Venus P010_512:
+	 * YUV 4:2:0 image with a plane of 10 bit Y samples followed
+	 * by an interleaved U/V plane containing 10 bit 2x2 subsampled
+	 * colour difference samples.
+	 *
+	 * <-------- Y/UV_Stride -------->
+	 * <------- Width ------->
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  ^           ^
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  Height      |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |          Y_Scanlines
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              V
+	 * U V U V U V U V U V U V . . . .  ^
+	 * U V U V U V U V U V U V . . . .  |
+	 * U V U V U V U V U V U V . . . .  |
+	 * U V U V U V U V U V U V . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  --> Buffer size alignment
+	 *
+	 * Y_Stride : Width * 2 aligned to 512
+	 * UV_Stride : Width * 2 aligned to 512
+	 * Y_Scanlines: Height aligned to 32
+	 * UV_Scanlines: Height/2 aligned to 16
+	 * Total size = align(Y_Stride * Y_Scanlines
+	 *          + UV_Stride * UV_Scanlines, 4096)
+	 */
+	MMM_COLOR_FMT_P010_512,
+	/* Venus NV12_512:
+	 * YUV 4:2:0 image with a plane of 8 bit Y samples followed
+	 * by an interleaved U/V plane containing 8 bit 2x2 subsampled
+	 * colour difference samples.
+	 *
+	 * <-------- Y/UV_Stride -------->
+	 * <------- Width ------->
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  ^           ^
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  Height      |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |          Y_Scanlines
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              V
+	 * U V U V U V U V U V U V . . . .  ^
+	 * U V U V U V U V U V U V . . . .  |
+	 * U V U V U V U V U V U V . . . .  |
+	 * U V U V U V U V U V U V . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  --> Buffer size alignment
+	 *
+	 * Y_Stride : Width aligned to 512
+	 * UV_Stride : Width aligned to 512
+	 * Y_Scanlines: Height aligned to 512
+	 * UV_Scanlines: Height/2 aligned to 256
+	 * Total size = align((Y_Stride * Y_Scanlines
+	 *          + UV_Stride  * UV_Scanlines), 4096)
+	 */
+	MMM_COLOR_FMT_NV12_512,
+	/* Venus RGBA FP16 UBWC:
+	 * Contains 2 planes in the following order -
+	 * (A) Meta plane
+	 * (B) RGBA plane
+	 *
+	 * <--- RGB_Meta_Stride ---->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |       Meta_RGB_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <-------- RGB_Stride -------->
+	 * <------- Width ------->
+	 * R R R R R R R R R R R R . . . .  ^           ^
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  Height      |
+	 * R R R R R R R R R R R R . . . .  |       RGB_Scanlines
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .    -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 *
+	 * RGB_Stride = align(Width * 8, 256)
+	 * RGB_Scanlines = align(Height, 16)
+	 * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096)
+	 * RGB_Meta_Stride = align(roundup(Width, RGB_TileWidth), 64)
+	 * RGB_Meta_Scanline = align(roundup(Height, RGB_TileHeight), 16)
+	 * RGB_Meta_Plane_size = align(RGB_Meta_Stride *
+	 *              RGB_Meta_Scanlines, 4096)
+	 * RGB_TileWidth = 8 pixels across is 1 tile
+	 * RGB_TileHeight = 4 pixels
+	 * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size, 4096)
+	 */
+	MMM_COLOR_FMT_RGBA16161616F_UBWC,
+};
+
+/*
+ * Function arguments:
+ * @color_fmt
+ * @width
+ * Progressive: width
+ * Interlaced: width
+ */
+static inline unsigned int MMM_COLOR_FMT_Y_STRIDE(unsigned int color_fmt,
+	unsigned int width)
+{
+	unsigned int alignment, stride = 0;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_NV12_512:
+		alignment = 512;
+		stride = MMM_COLOR_FMT_ALIGN(width, alignment);
+		break;
+	case MMM_COLOR_FMT_NV12:
+	case MMM_COLOR_FMT_NV21:
+	case MMM_COLOR_FMT_NV12_UBWC:
+		alignment = 128;
+		stride = MMM_COLOR_FMT_ALIGN(width, alignment);
+		break;
+	case MMM_COLOR_FMT_NV12_BPP10_UBWC:
+		alignment = 256;
+		stride = MMM_COLOR_FMT_ALIGN(width, 192);
+		stride = MMM_COLOR_FMT_ALIGN(stride * 4/3, alignment);
+		break;
+	case MMM_COLOR_FMT_P010_UBWC:
+	case MMM_COLOR_FMT_P010:
+		alignment = 256;
+		stride = MMM_COLOR_FMT_ALIGN(width * 2, alignment);
+		break;
+	case MMM_COLOR_FMT_P010_512:
+		alignment = 512;
+		stride = MMM_COLOR_FMT_ALIGN(width * 2, alignment);
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return stride;
+}
+
+/*
+ * Function arguments:
+ * @color_fmt
+ * @width
+ * Progressive: width
+ * Interlaced: width
+ */
+static inline unsigned int MMM_COLOR_FMT_UV_STRIDE(unsigned int color_fmt,
+	unsigned int width)
+{
+	unsigned int alignment, stride = 0;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_NV12_512:
+		alignment = 512;
+		stride = MMM_COLOR_FMT_ALIGN(width, alignment);
+		break;
+	case MMM_COLOR_FMT_NV12:
+	case MMM_COLOR_FMT_NV21:
+	case MMM_COLOR_FMT_NV12_UBWC:
+		alignment = 128;
+		stride = MMM_COLOR_FMT_ALIGN(width, alignment);
+		break;
+	case MMM_COLOR_FMT_NV12_BPP10_UBWC:
+		alignment = 256;
+		stride = MMM_COLOR_FMT_ALIGN(width, 192);
+		stride = MMM_COLOR_FMT_ALIGN(stride * 4/3, alignment);
+		break;
+	case MMM_COLOR_FMT_P010_UBWC:
+	case MMM_COLOR_FMT_P010:
+		alignment = 256;
+		stride = MMM_COLOR_FMT_ALIGN(width * 2, alignment);
+		break;
+	case MMM_COLOR_FMT_P010_512:
+		alignment = 512;
+		stride = MMM_COLOR_FMT_ALIGN(width * 2, alignment);
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return stride;
+}
+
+/*
+ * Function arguments:
+ * @color_fmt
+ * @height
+ * Progressive: height
+ * Interlaced: (height+1)>>1
+ */
+static inline unsigned int MMM_COLOR_FMT_Y_SCANLINES(unsigned int color_fmt,
+	unsigned int height)
+{
+	unsigned int alignment, sclines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_NV12_512:
+		alignment = 512;
+		break;
+	case MMM_COLOR_FMT_NV12:
+	case MMM_COLOR_FMT_NV21:
+	case MMM_COLOR_FMT_NV12_UBWC:
+	case MMM_COLOR_FMT_P010_512:
+	case MMM_COLOR_FMT_P010:
+		alignment = 32;
+		break;
+	case MMM_COLOR_FMT_NV12_BPP10_UBWC:
+	case MMM_COLOR_FMT_P010_UBWC:
+		alignment = 16;
+		break;
+	default:
+		return 0;
+	}
+	sclines = MMM_COLOR_FMT_ALIGN(height, alignment);
+invalid_input:
+	return sclines;
+}
+
+/*
+ * Function arguments:
+ * @color_fmt
+ * @height
+ * Progressive: height
+ * Interlaced: (height+1)>>1
+ */
+static inline unsigned int MMM_COLOR_FMT_UV_SCANLINES(unsigned int color_fmt,
+	unsigned int height)
+{
+	unsigned int alignment, sclines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+
+	case MMM_COLOR_FMT_NV12_512:
+		alignment = 256;
+		break;
+	case MMM_COLOR_FMT_NV12:
+	case MMM_COLOR_FMT_NV21:
+	case MMM_COLOR_FMT_NV12_BPP10_UBWC:
+	case MMM_COLOR_FMT_P010_UBWC:
+	case MMM_COLOR_FMT_P010_512:
+	case MMM_COLOR_FMT_P010:
+		alignment = 16;
+		break;
+	case MMM_COLOR_FMT_NV12_UBWC:
+		alignment = 32;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	sclines = MMM_COLOR_FMT_ALIGN((height+1)>>1, alignment);
+
+invalid_input:
+	return sclines;
+}
+
+/*
+ * Function arguments:
+ * @color_fmt
+ * @width
+ * Progressive: width
+ * Interlaced: width
+ */
+static inline unsigned int MMM_COLOR_FMT_Y_META_STRIDE(unsigned int color_fmt,
+	unsigned int width)
+{
+	int y_tile_width = 0, y_meta_stride = 0;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_NV12_UBWC:
+	case MMM_COLOR_FMT_P010_UBWC:
+		y_tile_width = 32;
+		break;
+	case MMM_COLOR_FMT_NV12_BPP10_UBWC:
+		y_tile_width = 48;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	y_meta_stride = MMM_COLOR_FMT_ROUNDUP(width, y_tile_width);
+	y_meta_stride = MMM_COLOR_FMT_ALIGN(y_meta_stride, 64);
+
+invalid_input:
+	return y_meta_stride;
+}
+
+/*
+ * Function arguments:
+ * @color_fmt
+ * @height
+ * Progressive: height
+ * Interlaced: (height+1)>>1
+ */
+static inline unsigned int MMM_COLOR_FMT_Y_META_SCANLINES(
+		unsigned int color_fmt,	unsigned int height)
+{
+	int y_tile_height = 0, y_meta_scanlines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_NV12_UBWC:
+		y_tile_height = 8;
+		break;
+	case MMM_COLOR_FMT_NV12_BPP10_UBWC:
+	case MMM_COLOR_FMT_P010_UBWC:
+		y_tile_height = 4;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	y_meta_scanlines = MMM_COLOR_FMT_ROUNDUP(height, y_tile_height);
+	y_meta_scanlines = MMM_COLOR_FMT_ALIGN(y_meta_scanlines, 16);
+
+invalid_input:
+	return y_meta_scanlines;
+}
+
+/*
+ * Function arguments:
+ * @color_fmt
+ * @width
+ * Progressive: width
+ * Interlaced: width
+ */
+static inline unsigned int MMM_COLOR_FMT_UV_META_STRIDE(unsigned int color_fmt,
+	unsigned int width)
+{
+	int uv_tile_width = 0, uv_meta_stride = 0;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_NV12_UBWC:
+	case MMM_COLOR_FMT_P010_UBWC:
+		uv_tile_width = 16;
+		break;
+	case MMM_COLOR_FMT_NV12_BPP10_UBWC:
+		uv_tile_width = 24;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	uv_meta_stride = MMM_COLOR_FMT_ROUNDUP((width+1)>>1, uv_tile_width);
+	uv_meta_stride = MMM_COLOR_FMT_ALIGN(uv_meta_stride, 64);
+
+invalid_input:
+	return uv_meta_stride;
+}
+
+/*
+ * Function arguments:
+ * @color_fmt
+ * @height
+ * Progressive: height
+ * Interlaced: (height+1)>>1
+ */
+static inline unsigned int MMM_COLOR_FMT_UV_META_SCANLINES(
+		unsigned int color_fmt,	unsigned int height)
+{
+	int uv_tile_height = 0, uv_meta_scanlines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_NV12_UBWC:
+		uv_tile_height = 8;
+		break;
+	case MMM_COLOR_FMT_NV12_BPP10_UBWC:
+	case MMM_COLOR_FMT_P010_UBWC:
+		uv_tile_height = 4;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	uv_meta_scanlines = MMM_COLOR_FMT_ROUNDUP((height+1)>>1,
+							uv_tile_height);
+	uv_meta_scanlines = MMM_COLOR_FMT_ALIGN(uv_meta_scanlines, 16);
+
+invalid_input:
+	return uv_meta_scanlines;
+}
+
+static inline unsigned int MMM_COLOR_FMT_RGB_STRIDE(unsigned int color_fmt,
+	unsigned int width)
+{
+	unsigned int alignment = 0, stride = 0, bpp = 4;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_RGBA8888:
+		alignment = 256;
+		break;
+	case MMM_COLOR_FMT_RGB565_UBWC:
+		alignment = 256;
+		bpp = 2;
+		break;
+	case MMM_COLOR_FMT_RGBA8888_UBWC:
+	case MMM_COLOR_FMT_RGBA1010102_UBWC:
+		alignment = 256;
+		break;
+	case MMM_COLOR_FMT_RGBA16161616F_UBWC:
+		alignment = 256;
+		bpp = 8;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	stride = MMM_COLOR_FMT_ALIGN(width * bpp, alignment);
+
+invalid_input:
+	return stride;
+}
+
+static inline unsigned int MMM_COLOR_FMT_RGB_SCANLINES(unsigned int color_fmt,
+	unsigned int height)
+{
+	unsigned int alignment = 0, scanlines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_RGBA8888:
+		alignment = 32;
+		break;
+	case MMM_COLOR_FMT_RGBA8888_UBWC:
+	case MMM_COLOR_FMT_RGBA1010102_UBWC:
+	case MMM_COLOR_FMT_RGB565_UBWC:
+	case MMM_COLOR_FMT_RGBA16161616F_UBWC:
+		alignment = 16;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	scanlines = MMM_COLOR_FMT_ALIGN(height, alignment);
+
+invalid_input:
+	return scanlines;
+}
+
+static inline unsigned int MMM_COLOR_FMT_RGB_META_STRIDE(unsigned int color_fmt,
+	unsigned int width)
+{
+	int rgb_tile_width = 0, rgb_meta_stride = 0;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_RGBA8888_UBWC:
+	case MMM_COLOR_FMT_RGBA1010102_UBWC:
+	case MMM_COLOR_FMT_RGB565_UBWC:
+		rgb_tile_width = 16;
+		break;
+	case MMM_COLOR_FMT_RGBA16161616F_UBWC:
+		rgb_tile_width = 8;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	rgb_meta_stride = MMM_COLOR_FMT_ROUNDUP(width, rgb_tile_width);
+	rgb_meta_stride = MMM_COLOR_FMT_ALIGN(rgb_meta_stride, 64);
+
+invalid_input:
+	return rgb_meta_stride;
+}
+
+static inline unsigned int MMM_COLOR_FMT_RGB_META_SCANLINES(
+		unsigned int color_fmt,	unsigned int height)
+{
+	int rgb_tile_height = 0, rgb_meta_scanlines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_RGBA8888_UBWC:
+	case MMM_COLOR_FMT_RGBA1010102_UBWC:
+	case MMM_COLOR_FMT_RGB565_UBWC:
+	case MMM_COLOR_FMT_RGBA16161616F_UBWC:
+		rgb_tile_height = 4;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	rgb_meta_scanlines = MMM_COLOR_FMT_ROUNDUP(height, rgb_tile_height);
+	rgb_meta_scanlines = MMM_COLOR_FMT_ALIGN(rgb_meta_scanlines, 16);
+
+invalid_input:
+	return rgb_meta_scanlines;
+}
+
+/*
+ * Function arguments:
+ * @color_fmt
+ * @width
+ * Progressive: width
+ * Interlaced: width
+ * @height
+ * Progressive: height
+ * Interlaced: height
+ */
+static inline unsigned int MMM_COLOR_FMT_BUFFER_SIZE(unsigned int color_fmt,
+	unsigned int width, unsigned int height)
+{
+	unsigned int size = 0;
+	unsigned int y_plane, uv_plane, y_stride,
+		uv_stride, y_sclines, uv_sclines;
+	unsigned int y_ubwc_plane = 0, uv_ubwc_plane = 0;
+	unsigned int y_meta_stride = 0, y_meta_scanlines = 0;
+	unsigned int uv_meta_stride = 0, uv_meta_scanlines = 0;
+	unsigned int y_meta_plane = 0, uv_meta_plane = 0;
+	unsigned int rgb_stride = 0, rgb_scanlines = 0;
+	unsigned int rgb_plane = 0, rgb_ubwc_plane = 0, rgb_meta_plane = 0;
+	unsigned int rgb_meta_stride = 0, rgb_meta_scanlines = 0;
+
+	if (!width || !height)
+		goto invalid_input;
+
+	y_stride = MMM_COLOR_FMT_Y_STRIDE(color_fmt, width);
+	uv_stride = MMM_COLOR_FMT_UV_STRIDE(color_fmt, width);
+	y_sclines = MMM_COLOR_FMT_Y_SCANLINES(color_fmt, height);
+	uv_sclines = MMM_COLOR_FMT_UV_SCANLINES(color_fmt, height);
+	rgb_stride = MMM_COLOR_FMT_RGB_STRIDE(color_fmt, width);
+	rgb_scanlines = MMM_COLOR_FMT_RGB_SCANLINES(color_fmt, height);
+
+	switch (color_fmt) {
+	case MMM_COLOR_FMT_NV21:
+	case MMM_COLOR_FMT_NV12:
+	case MMM_COLOR_FMT_P010_512:
+	case MMM_COLOR_FMT_P010:
+	case MMM_COLOR_FMT_NV12_512:
+		y_plane = y_stride * y_sclines;
+		uv_plane = uv_stride * uv_sclines;
+		size = y_plane + uv_plane;
+		break;
+	case MMM_COLOR_FMT_NV12_UBWC:
+		y_meta_stride = MMM_COLOR_FMT_Y_META_STRIDE(color_fmt, width);
+		uv_meta_stride = MMM_COLOR_FMT_UV_META_STRIDE(color_fmt, width);
+		if (width <= INTERLACE_WIDTH_MAX &&
+			height <= INTERLACE_HEIGHT_MAX &&
+			(height * width) / 256 <= INTERLACE_MB_PER_FRAME_MAX) {
+			y_sclines = MMM_COLOR_FMT_Y_SCANLINES(color_fmt,
+								(height+1)>>1);
+			y_ubwc_plane =
+				MMM_COLOR_FMT_ALIGN(y_stride * y_sclines, 4096);
+			uv_sclines = MMM_COLOR_FMT_UV_SCANLINES(color_fmt,
+								(height+1)>>1);
+			uv_ubwc_plane =	MMM_COLOR_FMT_ALIGN(
+						uv_stride * uv_sclines, 4096);
+			y_meta_scanlines = MMM_COLOR_FMT_Y_META_SCANLINES(
+						color_fmt, (height+1)>>1);
+			y_meta_plane = MMM_COLOR_FMT_ALIGN(
+				y_meta_stride * y_meta_scanlines, 4096);
+			uv_meta_scanlines = MMM_COLOR_FMT_UV_META_SCANLINES(
+					color_fmt, (height+1)>>1);
+			uv_meta_plane = MMM_COLOR_FMT_ALIGN(uv_meta_stride *
+				uv_meta_scanlines, 4096);
+			size = (y_ubwc_plane + uv_ubwc_plane + y_meta_plane +
+				uv_meta_plane)*2;
+		} else {
+			y_sclines = MMM_COLOR_FMT_Y_SCANLINES(color_fmt,
+									height);
+			y_ubwc_plane =
+				MMM_COLOR_FMT_ALIGN(y_stride * y_sclines, 4096);
+			uv_sclines = MMM_COLOR_FMT_UV_SCANLINES(color_fmt,
+									height);
+			uv_ubwc_plane =
+				MMM_COLOR_FMT_ALIGN(uv_stride * uv_sclines,
+									4096);
+			y_meta_scanlines = MMM_COLOR_FMT_Y_META_SCANLINES(
+							color_fmt, height);
+			y_meta_plane = MMM_COLOR_FMT_ALIGN(
+				y_meta_stride * y_meta_scanlines, 4096);
+			uv_meta_scanlines = MMM_COLOR_FMT_UV_META_SCANLINES(
+							color_fmt, height);
+			uv_meta_plane = MMM_COLOR_FMT_ALIGN(uv_meta_stride *
+				uv_meta_scanlines, 4096);
+			size = (y_ubwc_plane + uv_ubwc_plane + y_meta_plane +
+				uv_meta_plane);
+		}
+		break;
+	case MMM_COLOR_FMT_NV12_BPP10_UBWC:
+		y_ubwc_plane = MMM_COLOR_FMT_ALIGN(y_stride * y_sclines, 4096);
+		uv_ubwc_plane = MMM_COLOR_FMT_ALIGN(uv_stride * uv_sclines,
+									4096);
+		y_meta_stride = MMM_COLOR_FMT_Y_META_STRIDE(color_fmt, width);
+		y_meta_scanlines = MMM_COLOR_FMT_Y_META_SCANLINES(color_fmt,
+									height);
+		y_meta_plane = MMM_COLOR_FMT_ALIGN(
+				y_meta_stride * y_meta_scanlines, 4096);
+		uv_meta_stride = MMM_COLOR_FMT_UV_META_STRIDE(color_fmt, width);
+		uv_meta_scanlines = MMM_COLOR_FMT_UV_META_SCANLINES(color_fmt,
+									height);
+		uv_meta_plane = MMM_COLOR_FMT_ALIGN(uv_meta_stride *
+					uv_meta_scanlines, 4096);
+
+		size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane +
+			uv_meta_plane;
+		break;
+	case MMM_COLOR_FMT_P010_UBWC:
+		y_ubwc_plane = MMM_COLOR_FMT_ALIGN(y_stride * y_sclines, 4096);
+		uv_ubwc_plane = MMM_COLOR_FMT_ALIGN(uv_stride * uv_sclines,
+									4096);
+		y_meta_stride = MMM_COLOR_FMT_Y_META_STRIDE(color_fmt, width);
+		y_meta_scanlines = MMM_COLOR_FMT_Y_META_SCANLINES(color_fmt,
+									height);
+		y_meta_plane = MMM_COLOR_FMT_ALIGN(
+				y_meta_stride * y_meta_scanlines, 4096);
+		uv_meta_stride = MMM_COLOR_FMT_UV_META_STRIDE(color_fmt, width);
+		uv_meta_scanlines = MMM_COLOR_FMT_UV_META_SCANLINES(color_fmt,
+									height);
+		uv_meta_plane = MMM_COLOR_FMT_ALIGN(uv_meta_stride *
+					uv_meta_scanlines, 4096);
+
+		size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane +
+			uv_meta_plane;
+		break;
+	case MMM_COLOR_FMT_RGBA8888:
+		rgb_plane = MMM_COLOR_FMT_ALIGN(rgb_stride  * rgb_scanlines,
+									4096);
+		size = rgb_plane;
+		break;
+	case MMM_COLOR_FMT_RGBA8888_UBWC:
+	case MMM_COLOR_FMT_RGBA1010102_UBWC:
+	case MMM_COLOR_FMT_RGB565_UBWC:
+	case MMM_COLOR_FMT_RGBA16161616F_UBWC:
+		rgb_ubwc_plane = MMM_COLOR_FMT_ALIGN(rgb_stride * rgb_scanlines,
+							4096);
+		rgb_meta_stride = MMM_COLOR_FMT_RGB_META_STRIDE(color_fmt,
+									width);
+		rgb_meta_scanlines = MMM_COLOR_FMT_RGB_META_SCANLINES(color_fmt,
+					height);
+		rgb_meta_plane = MMM_COLOR_FMT_ALIGN(rgb_meta_stride *
+					rgb_meta_scanlines, 4096);
+		size = rgb_ubwc_plane + rgb_meta_plane;
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return MMM_COLOR_FMT_ALIGN(size, 4096);
+}
+
+static inline unsigned int MMM_COLOR_FMT_BUFFER_SIZE_USED(
+		unsigned int color_fmt,	unsigned int width,
+		unsigned int height, unsigned int interlace)
+{
+	unsigned int size = 0;
+	unsigned int y_stride, uv_stride, y_sclines, uv_sclines;
+	unsigned int y_ubwc_plane = 0, uv_ubwc_plane = 0;
+	unsigned int y_meta_stride = 0, y_meta_scanlines = 0;
+	unsigned int uv_meta_stride = 0, uv_meta_scanlines = 0;
+	unsigned int y_meta_plane = 0, uv_meta_plane = 0;
+
+	if (!width || !height)
+		goto invalid_input;
+
+	if (!interlace && color_fmt == MMM_COLOR_FMT_NV12_UBWC) {
+		y_stride = MMM_COLOR_FMT_Y_STRIDE(color_fmt, width);
+		uv_stride = MMM_COLOR_FMT_UV_STRIDE(color_fmt, width);
+		y_sclines = MMM_COLOR_FMT_Y_SCANLINES(color_fmt, height);
+		y_ubwc_plane = MMM_COLOR_FMT_ALIGN(y_stride * y_sclines, 4096);
+		uv_sclines = MMM_COLOR_FMT_UV_SCANLINES(color_fmt, height);
+		uv_ubwc_plane = MMM_COLOR_FMT_ALIGN(uv_stride * uv_sclines,
+									4096);
+		y_meta_stride = MMM_COLOR_FMT_Y_META_STRIDE(color_fmt, width);
+		y_meta_scanlines =
+			MMM_COLOR_FMT_Y_META_SCANLINES(color_fmt, height);
+		y_meta_plane = MMM_COLOR_FMT_ALIGN(
+			y_meta_stride * y_meta_scanlines, 4096);
+		uv_meta_stride = MMM_COLOR_FMT_UV_META_STRIDE(color_fmt, width);
+		uv_meta_scanlines =
+			MMM_COLOR_FMT_UV_META_SCANLINES(color_fmt, height);
+		uv_meta_plane = MMM_COLOR_FMT_ALIGN(uv_meta_stride *
+			uv_meta_scanlines, 4096);
+		size = (y_ubwc_plane + uv_ubwc_plane + y_meta_plane +
+			uv_meta_plane);
+		size = MMM_COLOR_FMT_ALIGN(size, 4096);
+	} else {
+		size = MMM_COLOR_FMT_BUFFER_SIZE(color_fmt, width, height);
+	}
+invalid_input:
+	return size;
+}
+
+#endif

+ 180 - 0
qcom/opensource/display-drivers/include/uapi/display/media/msm_sde_rotator.h

@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __UAPI_MSM_SDE_ROTATOR_H__
+#define __UAPI_MSM_SDE_ROTATOR_H__
+
+#include <linux/videodev2.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* SDE Rotator pixel format definitions */
+#define SDE_PIX_FMT_XRGB_8888 \
+	v4l2_fourcc('X', 'R', '2', '4') /* 32  BGRX-8-8-8-8  */
+#define SDE_PIX_FMT_ARGB_8888 \
+	v4l2_fourcc('A', 'R', '2', '4') /* 32  BGRA-8-8-8-8  */
+#define SDE_PIX_FMT_ABGR_8888 \
+	v4l2_fourcc('R', 'A', '2', '4') /* 32-bit ABGR 8:8:8:8 */
+#define SDE_PIX_FMT_RGBA_8888 \
+	v4l2_fourcc('A', 'B', '2', '4') /* 32-bit RGBA 8:8:8:8 */
+#define SDE_PIX_FMT_BGRA_8888 \
+	v4l2_fourcc('B', 'A', '2', '4') /* 32  ARGB-8-8-8-8  */
+#define SDE_PIX_FMT_RGBX_8888 \
+	v4l2_fourcc('X', 'B', '2', '4') /* 32-bit RGBX 8:8:8:8 */
+#define SDE_PIX_FMT_BGRX_8888 \
+	v4l2_fourcc('B', 'X', '2', '4') /* 32  XRGB-8-8-8-8  */
+#define SDE_PIX_FMT_XBGR_8888 \
+	v4l2_fourcc('R', 'X', '2', '4') /* 32-bit XBGR 8:8:8:8 */
+#define SDE_PIX_FMT_RGBA_5551 \
+	v4l2_fourcc('R', 'A', '1', '5') /* 16-bit RGBA 5:5:5:1 */
+#define SDE_PIX_FMT_ARGB_1555 \
+	v4l2_fourcc('A', 'R', '1', '5') /* 16  ARGB-1-5-5-5  */
+#define SDE_PIX_FMT_ABGR_1555 \
+	v4l2_fourcc('A', 'B', '1', '5') /* 16-bit ABGR 1:5:5:5 */
+#define SDE_PIX_FMT_BGRA_5551 \
+	v4l2_fourcc('B', 'A', '1', '5') /* 16-bit BGRA 5:5:5:1 */
+#define SDE_PIX_FMT_BGRX_5551 \
+	v4l2_fourcc('B', 'X', '1', '5') /* 16-bit BGRX 5:5:5:1 */
+#define SDE_PIX_FMT_RGBX_5551 \
+	v4l2_fourcc('R', 'X', '1', '5') /* 16-bit RGBX 5:5:5:1 */
+#define SDE_PIX_FMT_XBGR_1555 \
+	v4l2_fourcc('X', 'B', '1', '5') /* 16-bit XBGR 1:5:5:5 */
+#define SDE_PIX_FMT_XRGB_1555 \
+	v4l2_fourcc('X', 'R', '1', '5') /* 16  XRGB-1-5-5-5  */
+#define SDE_PIX_FMT_ARGB_4444 \
+	v4l2_fourcc('A', 'R', '1', '2') /* 16  aaaarrrr ggggbbbb */
+#define SDE_PIX_FMT_RGBA_4444 \
+	v4l2_fourcc('R', 'A', '1', '2') /* 16-bit RGBA 4:4:4:4 */
+#define SDE_PIX_FMT_BGRA_4444 \
+	v4l2_fourcc('b', 'A', '1', '2') /* 16-bit BGRA 4:4:4:4 */
+#define SDE_PIX_FMT_ABGR_4444 \
+	v4l2_fourcc('A', 'B', '1', '2') /* 16-bit ABGR 4:4:4:4 */
+#define SDE_PIX_FMT_RGBX_4444 \
+	v4l2_fourcc('R', 'X', '1', '2') /* 16-bit RGBX 4:4:4:4 */
+#define SDE_PIX_FMT_XRGB_4444 \
+	v4l2_fourcc('X', 'R', '1', '2') /* 16  xxxxrrrr ggggbbbb */
+#define SDE_PIX_FMT_BGRX_4444 \
+	v4l2_fourcc('B', 'X', '1', '2') /* 16-bit BGRX 4:4:4:4 */
+#define SDE_PIX_FMT_XBGR_4444 \
+	v4l2_fourcc('X', 'B', '1', '2') /* 16-bit XBGR 4:4:4:4 */
+#define SDE_PIX_FMT_RGB_888 \
+	v4l2_fourcc('R', 'G', 'B', '3') /* 24  RGB-8-8-8     */
+#define SDE_PIX_FMT_BGR_888 \
+	v4l2_fourcc('B', 'G', 'R', '3') /* 24  BGR-8-8-8     */
+#define SDE_PIX_FMT_RGB_565 \
+	v4l2_fourcc('R', 'G', 'B', 'P') /* 16  RGB-5-6-5     */
+#define SDE_PIX_FMT_BGR_565 \
+	v4l2_fourcc('B', 'G', '1', '6') /* 16-bit BGR 5:6:5 */
+#define SDE_PIX_FMT_Y_CB_CR_H2V2 \
+	v4l2_fourcc('Y', 'U', '1', '2') /* 12  YUV 4:2:0     */
+#define SDE_PIX_FMT_Y_CR_CB_H2V2 \
+	v4l2_fourcc('Y', 'V', '1', '2') /* 12  YVU 4:2:0     */
+#define SDE_PIX_FMT_Y_CR_CB_GH2V2 \
+	v4l2_fourcc('Y', 'U', '4', '2') /* Planar YVU 4:2:0 A16 */
+#define SDE_PIX_FMT_Y_CBCR_H2V2 \
+	v4l2_fourcc('N', 'V', '1', '2') /* 12  Y/CbCr 4:2:0  */
+#define SDE_PIX_FMT_Y_CRCB_H2V2 \
+	v4l2_fourcc('N', 'V', '2', '1') /* 12  Y/CrCb 4:2:0  */
+#define SDE_PIX_FMT_Y_CBCR_H1V2 \
+	v4l2_fourcc('N', 'H', '1', '6') /* Y/CbCr 4:2:2 */
+#define SDE_PIX_FMT_Y_CRCB_H1V2 \
+	v4l2_fourcc('N', 'H', '6', '1') /* Y/CrCb 4:2:2 */
+#define SDE_PIX_FMT_Y_CBCR_H2V1 \
+	v4l2_fourcc('N', 'V', '1', '6') /* 16  Y/CbCr 4:2:2  */
+#define SDE_PIX_FMT_Y_CRCB_H2V1 \
+	v4l2_fourcc('N', 'V', '6', '1') /* 16  Y/CrCb 4:2:2  */
+#define SDE_PIX_FMT_YCBYCR_H2V1 \
+	v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16  YUV 4:2:2     */
+#define SDE_PIX_FMT_Y_CBCR_H2V2_VENUS \
+	v4l2_fourcc('Q', 'N', 'V', '2') /* Y/CbCr 4:2:0 Venus */
+#define SDE_PIX_FMT_Y_CRCB_H2V2_VENUS \
+	v4l2_fourcc('Q', 'N', 'V', '1') /* Y/CrCb 4:2:0 Venus */
+#define SDE_PIX_FMT_RGBA_8888_UBWC \
+	v4l2_fourcc('Q', 'R', 'G', 'B') /* RGBA 8:8:8:8 UBWC */
+#define SDE_PIX_FMT_RGBX_8888_UBWC \
+	v4l2_fourcc('Q', 'X', 'B', '4') /* RGBX 8:8:8:8 UBWC */
+#define SDE_PIX_FMT_RGB_565_UBWC \
+	v4l2_fourcc('Q', 'R', 'G', '6') /* RGB 5:6:5 UBWC */
+#define SDE_PIX_FMT_Y_CBCR_H2V2_UBWC \
+	v4l2_fourcc('Q', '1', '2', '8') /* UBWC 8-bit Y/CbCr 4:2:0 */
+#define SDE_PIX_FMT_RGBA_1010102 \
+	v4l2_fourcc('A', 'B', '3', '0') /* RGBA 10:10:10:2 */
+#define SDE_PIX_FMT_RGBX_1010102 \
+	v4l2_fourcc('X', 'B', '3', '0') /* RGBX 10:10:10:2 */
+#define SDE_PIX_FMT_ARGB_2101010 \
+	v4l2_fourcc('A', 'R', '3', '0') /* ARGB 2:10:10:10 */
+#define SDE_PIX_FMT_XRGB_2101010 \
+	v4l2_fourcc('X', 'R', '3', '0') /* XRGB 2:10:10:10 */
+#define SDE_PIX_FMT_BGRA_1010102 \
+	v4l2_fourcc('B', 'A', '3', '0') /* BGRA 10:10:10:2 */
+#define SDE_PIX_FMT_BGRX_1010102 \
+	v4l2_fourcc('B', 'X', '3', '0') /* BGRX 10:10:10:2 */
+#define SDE_PIX_FMT_ABGR_2101010 \
+	v4l2_fourcc('R', 'A', '3', '0') /* ABGR 2:10:10:10 */
+#define SDE_PIX_FMT_XBGR_2101010 \
+	v4l2_fourcc('R', 'X', '3', '0') /* XBGR 2:10:10:10 */
+#define SDE_PIX_FMT_RGBA_1010102_UBWC \
+	v4l2_fourcc('Q', 'R', 'B', 'A') /* RGBA 10:10:10:2 UBWC */
+#define SDE_PIX_FMT_RGBX_1010102_UBWC \
+	v4l2_fourcc('Q', 'X', 'B', 'A') /* RGBX 10:10:10:2 UBWC */
+#define SDE_PIX_FMT_Y_CBCR_H2V2_P010 \
+	v4l2_fourcc('P', '0', '1', '0') /* Y/CbCr 4:2:0 P10 */
+#define SDE_PIX_FMT_Y_CBCR_H2V2_P010_VENUS \
+	v4l2_fourcc('Q', 'P', '1', '0') /* Y/CbCr 4:2:0 P10 Venus*/
+#define SDE_PIX_FMT_Y_CBCR_H2V2_TP10 \
+	v4l2_fourcc('T', 'P', '1', '0') /* Y/CbCr 4:2:0 TP10 */
+#define SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC \
+	v4l2_fourcc('Q', '1', '2', 'A') /* UBWC Y/CbCr 4:2:0 TP10 */
+#define SDE_PIX_FMT_Y_CBCR_H2V2_P010_UBWC \
+	v4l2_fourcc('Q', '1', '2', 'B') /* UBWC Y/CbCr 4:2:0 P10 */
+
+/*
+ * struct msm_sde_rotator_fence - v4l2 buffer fence info
+ * @index: id number of the buffer
+ * @type: enum v4l2_buf_type; buffer type
+ * @fd: file descriptor of the fence associated with this buffer
+ */
+struct msm_sde_rotator_fence {
+	__u32	index;
+	__u32	type;
+	__s32	fd;
+	__u32	reserved[5];
+};
+
+/*
+ * struct msm_sde_rotator_comp_ratio - v4l2 buffer compression ratio
+ * @index: id number of the buffer
+ * @type: enum v4l2_buf_type; buffer type
+ * @numer: numerator of the ratio
+ * @denom: denominator of the ratio
+ */
+struct msm_sde_rotator_comp_ratio {
+	__u32	index;
+	__u32	type;
+	__u32	numer;
+	__u32	denom;
+	__u32	reserved[4];
+};
+
+/* SDE Rotator private ioctl ID */
+#define VIDIOC_G_SDE_ROTATOR_FENCE \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 10, struct msm_sde_rotator_fence)
+#define VIDIOC_S_SDE_ROTATOR_FENCE \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 11, struct msm_sde_rotator_fence)
+#define VIDIOC_G_SDE_ROTATOR_COMP_RATIO \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 12, struct msm_sde_rotator_comp_ratio)
+#define VIDIOC_S_SDE_ROTATOR_COMP_RATIO \
+	_IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct msm_sde_rotator_comp_ratio)
+
+/* SDE Rotator private control ID's */
+#define V4L2_CID_SDE_ROTATOR_SECURE	(V4L2_CID_USER_BASE + 0x1000)
+
+/*
+ * This control Id indicates this context is associated with the
+ * secure camera.
+ */
+#define V4L2_CID_SDE_ROTATOR_SECURE_CAMERA	(V4L2_CID_USER_BASE + 0x2000)
+
+#endif /* __UAPI_MSM_SDE_ROTATOR_H__ */

+ 75 - 0
qcom/opensource/display-drivers/msm/Android.mk

@@ -0,0 +1,75 @@
+DISPLAY_SELECT := CONFIG_DRM_MSM=m
+
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TARGET_BOARD_PLATFORM), niobe)
+LOCAL_MODULE_DDK_BUILD := false
+else
+LOCAL_MODULE_DDK_BUILD := true
+endif
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_DDK_SUBTARGET_REGEX := "display_drivers*"
+ifeq ($(TARGET_BOARD_PLATFORM), volcano)
+  LOCAL_MODULE_DDK_SUBTARGET_REGEX := "$(TARGET_BOARD_PLATFORM)_display_drivers.*"
+endif
+
+# This makefile is only for DLKM
+ifneq ($(findstring vendor,$(LOCAL_PATH)),)
+
+ifneq ($(findstring opensource,$(LOCAL_PATH)),)
+	DISPLAY_BLD_DIR := $(TOP)/vendor/qcom/opensource/display-drivers
+endif # opensource
+
+DLKM_DIR := $(TOP)/device/qcom/common/dlkm
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+
+# Build display.ko as msm_drm.ko
+###########################################################
+# This is set once per LOCAL_PATH, not per (kernel) module
+KBUILD_OPTIONS := DISPLAY_ROOT=$(DISPLAY_BLD_DIR)
+KBUILD_OPTIONS += MODNAME=msm_drm
+KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM)
+KBUILD_OPTIONS += $(DISPLAY_SELECT)
+
+ifneq ($(TARGET_BOARD_AUTO),true)
+ifneq ($(TARGET_BOARD_PLATFORM), pitti)
+KBUILD_OPTIONS += KBUILD_EXTRA_SYMBOLS+=$(PWD)/$(call intermediates-dir-for,DLKM,mmrm-module-symvers)/Module.symvers
+endif
+ifneq ($(TARGET_BOARD_PLATFORM), taro)
+	KBUILD_OPTIONS += KBUILD_EXTRA_SYMBOLS+=$(PWD)/$(call intermediates-dir-for,DLKM,sync-fence-module-symvers)/Module.symvers
+	KBUILD_OPTIONS += KBUILD_EXTRA_SYMBOLS+=$(PWD)/$(call intermediates-dir-for,DLKM,msm-ext-disp-module-symvers)/Module.symvers
+	KBUILD_OPTIONS += KBUILD_EXTRA_SYMBOLS+=$(PWD)/$(call intermediates-dir-for,DLKM,hw-fence-module-symvers)/Module.symvers
+	KBUILD_OPTIONS += KBUILD_EXTRA_SYMBOLS+=$(PWD)/$(call intermediates-dir-for,DLKM,sec-module-symvers)/Module.symvers
+endif
+endif
+
+###########################################################
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+LOCAL_MODULE              := msm_drm.ko
+LOCAL_MODULE_KBUILD_NAME  := msm_drm.ko
+LOCAL_MODULE_TAGS         := optional
+LOCAL_MODULE_DEBUG_ENABLE := true
+LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+
+ifneq ($(TARGET_BOARD_AUTO),true)
+ifneq ($(TARGET_BOARD_PLATFORM), pitti)
+LOCAL_REQUIRED_MODULES    += mmrm-module-symvers
+LOCAL_ADDITIONAL_DEPENDENCIES += $(call intermediates-dir-for,DLKM,mmrm-module-symvers)/Module.symvers
+endif
+ifneq ($(TARGET_BOARD_PLATFORM), taro)
+	LOCAL_REQUIRED_MODULES    += sync-fence-module-symvers
+	LOCAL_REQUIRED_MODULES    += msm-ext-disp-module-symvers
+	LOCAL_REQUIRED_MODULES    += hw-fence-module-symvers
+	LOCAL_REQUIRED_MODULES    += sec-module-symvers
+	LOCAL_ADDITIONAL_DEPENDENCIES += $(call intermediates-dir-for,DLKM,sync-fence-module-symvers)/Module.symvers
+	LOCAL_ADDITIONAL_DEPENDENCIES += $(call intermediates-dir-for,DLKM,msm-ext-disp-module-symvers)/Module.symvers
+	LOCAL_ADDITIONAL_DEPENDENCIES += $(call intermediates-dir-for,DLKM,hw-fence-module-symvers)/Module.symvers
+	LOCAL_ADDITIONAL_DEPENDENCIES += $(call intermediates-dir-for,DLKM,sec-module-symvers)/Module.symvers
+endif
+endif
+
+include $(DLKM_DIR)/Build_external_kernelmodule.mk
+###########################################################
+endif # DLKM check

+ 267 - 0
qcom/opensource/display-drivers/msm/Kbuild

@@ -0,0 +1,267 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+KDIR := $(TOP)/kernel_platform/msm-kernel
+
+ifeq ($(CONFIG_ARCH_WAIPIO), y)
+ifeq ($(CONFIG_ARCH_QTI_VM), y)
+	include $(DISPLAY_ROOT)/config/gki_waipiodisptui.conf
+	LINUX_INC +=	-include $(DISPLAY_ROOT)/config/gki_waipiodisptuiconf.h
+else
+	include $(DISPLAY_ROOT)/config/gki_waipiodisp.conf
+	LINUX_INC +=	-include $(DISPLAY_ROOT)/config/gki_waipiodispconf.h
+endif
+endif
+
+ifeq ($(CONFIG_ARCH_NEO), y)
+	include $(DISPLAY_ROOT)/config/gki_neodisp.conf
+	LINUX_INC +=	-include $(DISPLAY_ROOT)/config/gki_neodispconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_PARROT), y)
+	include $(DISPLAY_ROOT)/config/gki_parrotdisp.conf
+	LINUX_INC +=	-include $(DISPLAY_ROOT)/config/gki_parrotdispconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_PITTI), y)
+	include $(DISPLAY_ROOT)/config/gki_pittidisp.conf
+	LINUX_INC +=	-include $(DISPLAY_ROOT)/config/gki_pittidispconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_NIOBE), y)
+	include $(DISPLAY_ROOT)/config/gki_niobedisp.conf
+	LINUX_INC +=	-include $(DISPLAY_ROOT)/config/gki_niobedispconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_PINEAPPLE), y)
+ifeq ($(CONFIG_ARCH_QTI_VM), y)
+    include $(DISPLAY_ROOT)/config/gki_pineappledisptui.conf
+    LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_pineappledisptuiconf.h
+else
+	include $(DISPLAY_ROOT)/config/gki_pineappledisp.conf
+	LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_pineappledispconf.h
+endif
+endif
+
+ifeq ($(CONFIG_ARCH_KALAMA), y)
+ifeq ($(CONFIG_ARCH_QTI_VM), y)
+        include $(DISPLAY_ROOT)/config/gki_kalamadisptui.conf
+        LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_kalamadisptuiconf.h
+else
+	include $(DISPLAY_ROOT)/config/gki_kalamadisp.conf
+	LINUX_INC += -include $(DISPLAY_ROOT)/config/gki_kalamadispconf.h
+endif
+endif
+
+ifeq (y, $(findstring y, $(CONFIG_ARCH_SA8155) $(CONFIG_ARCH_SA6155) $(CONFIG_ARCH_SA8195)))
+	include $(DISPLAY_ROOT)/config/augen3disp.conf
+	LINUX_INC += -include $(DISPLAY_ROOT)/config/augen3dispconf.h
+endif
+
+LINUX_INC +=	-I$(KERNEL_SRC)/include/linux \
+		-I$(KERNEL_SRC)/include/linux/drm
+
+LINUX_INC +=	-I$(DISPLAY_ROOT) \
+	-I$(DISPLAY_ROOT)/include \
+	-I$(KERNEL_ROOT)/drivers/clk/qcom \
+	-I$(KERNEL_SRC)/drivers/clk/qcom \
+	-I$(DISPLAY_ROOT)/include/linux \
+	-I$(DISPLAY_ROOT)/rotator \
+	-I$(DISPLAY_ROOT)/msm \
+	-I$(DISPLAY_ROOT)/msm/dp \
+	-I$(DISPLAY_ROOT)/msm/dsi \
+	-I$(DISPLAY_ROOT)/msm/sde \
+	-I$(DISPLAY_ROOT)/include/uapi/display \
+
+CDEFINES +=	-DANI_LITTLE_BYTE_ENDIAN \
+	-DANI_LITTLE_BIT_ENDIAN \
+	-DDOT11F_LITTLE_ENDIAN_HOST \
+	-DANI_COMPILER_TYPE_GCC \
+	-DANI_OS_TYPE_ANDROID=6 \
+	-DPTT_SOCK_SVC_ENABLE \
+	-Wall\
+	-Werror\
+	-D__linux__
+
+KBUILD_CPPFLAGS += $(CDEFINES)
+
+ccflags-y += $(LINUX_INC)
+
+ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y)
+EXTRA_CFLAGS += -Wmaybe-uninitialized
+endif
+
+KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/display-drivers/hdcp/Module.symvers
+KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/display-drivers/msm/Module.symvers
+
+ifeq ($(call cc-option-yn, -Wheader-guard),y)
+EXTRA_CFLAGS += -Wheader-guard
+endif
+
+ccflags-y += -Wformat-extra-args -Wstrict-prototypes -Wformat-insufficient-args \
+		-Wformat-invalid-specifier -Wformat-zero-length -Wnonnull
+
+ifneq ($(MODNAME), qdsp6v2)
+CHIP_NAME ?= $(MODNAME)
+CDEFINES += -DMULTI_IF_NAME=\"$(CHIP_NAME)\"
+endif
+
+######### CONFIG_DRM_MSM ########
+obj-m += msm_drm.o
+
+msm_drm-$(CONFIG_HDCP_QSEECOM) := ../hdcp/msm_hdcp.o \
+				  dp/dp_hdcp2p2.o \
+				  sde_hdcp_1x.o \
+				  sde_hdcp_2x.o
+
+msm_drm-$(CONFIG_MSM_SDE_ROTATOR) += ../rotator/sde_rotator_dev.o \
+				     ../rotator/sde_rotator_dev.o \
+				     ../rotator/sde_rotator_core.o \
+				     ../rotator/sde_rotator_base.o \
+				     ../rotator/sde_rotator_formats.o \
+				     ../rotator/sde_rotator_util.o \
+				     ../rotator/sde_rotator_io_util.o \
+				     ../rotator/sde_rotator_smmu.o \
+				     ../rotator/sde_rotator_r1_wb.o \
+				     ../rotator/sde_rotator_r1_pipe.o \
+				     ../rotator/sde_rotator_r1_ctl.o \
+				     ../rotator/sde_rotator_r1.o \
+				     ../rotator/sde_rotator_r3.o
+
+ifeq ($(CONFIG_MSM_SDE_ROTATOR), y)
+msm_drm-$(CONFIG_SYNC_FILE) += ../rotator/sde_rotator_sync.o
+
+msm_drm-$(CONFIG_DEBUG_FS) += ../rotator/sde_rotator_debug.o \
+			      ../rotator/sde_rotator_r1_debug.o \
+			      ../rotator/sde_rotator_r3_debug.o
+endif
+
+msm_drm-$(CONFIG_DRM_SDE_VM) += sde/sde_vm_common.o \
+				sde/sde_vm_primary.o \
+				sde/sde_vm_trusted.o \
+				sde/sde_vm_msgq.o
+
+msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_altmode.o \
+				dp/dp_parser.o \
+				dp/dp_power.o \
+				dp/dp_catalog.o \
+				dp/dp_catalog_v420.o \
+				dp/dp_catalog_v200.o \
+				dp/dp_aux.o \
+				dp/dp_panel.o \
+				dp/dp_link.o \
+				dp/dp_ctrl.o \
+				dp/dp_audio.o \
+				dp/dp_debug.o \
+				dp/dp_hpd.o \
+				dp/dp_aux_bridge.o \
+				dp/dp_bridge_hpd.o \
+				dp/dp_mst_sim.o \
+				dp/dp_mst_sim_helper.o \
+				dp/dp_gpio_hpd.o \
+				dp/dp_lphw_hpd.o \
+				dp/dp_display.o \
+				dp/dp_drm.o \
+				dp/dp_pll.o \
+				dp/dp_pll_5nm.o \
+				dp/dp_pll_4nm.o
+
+msm_drm-$(CONFIG_DRM_MSM_DP_MST) += dp/dp_mst_drm.o
+
+msm_drm-$(CONFIG_DRM_MSM_DP_USBPD_LEGACY) += dp/dp_usbpd.o
+
+msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \
+				 sde/sde_encoder.o \
+				 sde/sde_encoder_dce.o \
+				 sde/sde_encoder_phys_vid.o \
+				 sde/sde_encoder_phys_cmd.o \
+				 sde/sde_irq.o sde/sde_core_irq.o \
+				 sde/sde_core_perf.o \
+				 sde/sde_rm.o \
+				 sde/sde_kms_utils.o \
+				 sde/sde_kms.o \
+				 sde/sde_plane.o \
+				 sde/sde_connector.o \
+				 sde/sde_color_processing.o \
+				 sde/sde_vbif.o \
+				 sde_dbg.o \
+				 sde_dbg_evtlog.o \
+				 sde_io_util.o \
+				 sde_vm_event.o \
+				 sde/sde_hw_reg_dma_v1_color_proc.o \
+				 sde/sde_hw_color_proc_v4.o \
+				 sde/sde_hw_ad4.o \
+				 sde/sde_hw_uidle.o \
+				 sde_edid_parser.o \
+				 sde/sde_hw_catalog.o \
+				 sde/sde_hw_cdm.o \
+				 sde/sde_hw_dspp.o \
+				 sde/sde_hw_intf.o \
+				 sde/sde_hw_lm.o \
+				 sde/sde_hw_ctl.o \
+				 sde/sde_hw_util.o \
+				 sde/sde_hw_sspp.o \
+				 sde/sde_hw_wb.o \
+				 sde/sde_hw_pingpong.o \
+				 sde/sde_hw_top.o \
+				 sde/sde_hw_interrupts.o \
+				 sde/sde_hw_vbif.o \
+				 sde/sde_formats.o \
+				 sde_power_handle.o \
+				 sde/sde_hw_color_processing_v1_7.o \
+				 sde/sde_reg_dma.o \
+				 sde/sde_hw_reg_dma_v1.o \
+				 sde/sde_hw_dsc.o \
+				 sde/sde_hw_dsc_1_2.o \
+				 sde/sde_hw_vdc.o \
+				 sde/sde_hw_ds.o \
+				 sde/sde_fence.o \
+				 sde/sde_hw_qdss.o \
+				 sde_dsc_helper.o \
+				 sde_vdc_helper.o \
+				 sde/sde_hw_dnsc_blur.o \
+				 sde/sde_hw_rc.o
+
+msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \
+				sde/sde_encoder_phys_wb.o
+
+msm_drm-$(CONFIG_DRM_SDE_RSC) += sde_rsc.o \
+				 sde_rsc_hw.o \
+				 sde_rsc_hw_v3.o
+
+msm_drm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi_phy.o \
+				 dsi/dsi_pwr.o \
+				 dsi/dsi_phy.o \
+				 dsi/dsi_phy_hw_v3_0.o \
+				 dsi/dsi_phy_hw_v4_0.o \
+				 dsi/dsi_phy_hw_v5_0.o \
+				 dsi/dsi_phy_timing_calc.o \
+				 dsi/dsi_phy_timing_v3_0.o \
+				 dsi/dsi_phy_timing_v4_0.o \
+				 dsi/dsi_pll.o \
+				 dsi/dsi_pll_5nm.o \
+				 dsi/dsi_pll_4nm.o \
+				 dsi/dsi_ctrl_hw_cmn.o \
+				 dsi/dsi_ctrl_hw_2_2.o \
+				 dsi/dsi_ctrl.o \
+				 dsi/dsi_catalog.o \
+				 dsi/dsi_drm.o \
+				 dsi/dsi_display.o \
+				 dsi/dsi_panel.o \
+				 dsi/dsi_clk_manager.o \
+				 dsi/dsi_display_test.o
+
+msm_drm-$(CONFIG_DSI_PARSER) += dsi/dsi_parser.o
+
+msm_drm-$(CONFIG_THERMAL_OF) += msm_cooling_device.o
+
+msm_drm-$(CONFIG_DRM_MSM) += msm_atomic.o \
+			     msm_fb.o \
+			     msm_drv.o \
+			     msm_gem.o \
+			     msm_gem_prime.o \
+			     msm_gem_vma.o \
+			     msm_smmu.o \
+			     msm_prop.o
+
+CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\"
+

+ 16 - 0
qcom/opensource/display-drivers/msm/Makefile

@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+
+KBUILD_OPTIONS+= DISPLAY_ROOT=$(KERNEL_SRC)/$(M)/../
+
+all: modules
+
+modules_install:
+	$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install
+
+%:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS)
+
+clean:
+	rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers
+	rm -rf .tmp_versions
+

+ 325 - 0
qcom/opensource/display-drivers/msm/dp/dp_altmode.c

@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/soc/qcom/altmode-glink.h>
+#include <linux/usb/dwc3-msm.h>
+#include <linux/usb/pd_vdo.h>
+
+#include "dp_altmode.h"
+#include "dp_debug.h"
+#include "sde_dbg.h"
+
+
+#define ALTMODE_CONFIGURE_MASK (0x3f)
+#define ALTMODE_HPD_STATE_MASK (0x40)
+#define ALTMODE_HPD_IRQ_MASK (0x80)
+
+struct dp_altmode_private {
+	bool forced_disconnect;
+	struct device *dev;
+	struct dp_hpd_cb *dp_cb;
+	struct dp_altmode dp_altmode;
+	struct altmode_client *amclient;
+	bool connected;
+	u32 lanes;
+};
+
+enum dp_altmode_pin_assignment {
+	DPAM_HPD_OUT,
+	DPAM_HPD_A,
+	DPAM_HPD_B,
+	DPAM_HPD_C,
+	DPAM_HPD_D,
+	DPAM_HPD_E,
+	DPAM_HPD_F,
+};
+
+static int dp_altmode_set_usb_dp_mode(struct dp_altmode_private *altmode)
+{
+	int rc = 0;
+	struct device_node *np;
+	struct device_node *usb_node;
+	struct platform_device *usb_pdev;
+	int timeout = 250;
+
+	if (!altmode || !altmode->dev) {
+		DP_ERR("invalid args\n");
+		return -EINVAL;
+	}
+
+	np = altmode->dev->of_node;
+
+	usb_node = of_parse_phandle(np, "usb-controller", 0);
+	if (!usb_node) {
+		DP_ERR("unable to get usb node\n");
+		return -EINVAL;
+	}
+
+	usb_pdev = of_find_device_by_node(usb_node);
+	if (!usb_pdev) {
+		of_node_put(usb_node);
+		DP_ERR("unable to get usb pdev\n");
+		return -EINVAL;
+	}
+
+	while (timeout) {
+		rc = dwc3_msm_set_dp_mode(&usb_pdev->dev, altmode->connected, altmode->lanes);
+		if (rc != -EBUSY && rc != -EAGAIN)
+			break;
+
+		DP_WARN("USB busy, retry\n");
+
+		/* wait for hw recommended delay for usb */
+		msleep(20);
+		timeout--;
+	}
+	of_node_put(usb_node);
+	platform_device_put(usb_pdev);
+
+	if (rc)
+		DP_ERR("Error releasing SS lanes: %d\n", rc);
+
+	return rc;
+}
+
+static void dp_altmode_send_pan_ack(struct altmode_client *amclient,
+		u8 port_index)
+{
+	int rc;
+	struct altmode_pan_ack_msg ack;
+
+	ack.cmd_type = ALTMODE_PAN_ACK;
+	ack.port_index = port_index;
+
+	rc = altmode_send_data(amclient, &ack, sizeof(ack));
+	if (rc < 0) {
+		DP_ERR("failed: %d\n", rc);
+		return;
+	}
+
+	DP_DEBUG("port=%d\n", port_index);
+}
+
+static int dp_altmode_notify(void *priv, void *data, size_t len)
+{
+	int rc = 0;
+	struct dp_altmode_private *altmode =
+			(struct dp_altmode_private *) priv;
+	u8 port_index, dp_data, orientation;
+	u8 *payload = (u8 *) data;
+	u8 pin, hpd_state, hpd_irq;
+	bool force_multi_func = altmode->dp_altmode.base.force_multi_func;
+
+	port_index = payload[0];
+	orientation = payload[1];
+	dp_data = payload[8];
+
+	pin = dp_data & ALTMODE_CONFIGURE_MASK;
+	hpd_state = (dp_data & ALTMODE_HPD_STATE_MASK) >> 6;
+	hpd_irq = (dp_data & ALTMODE_HPD_IRQ_MASK) >> 7;
+
+	altmode->dp_altmode.base.hpd_high = !!hpd_state;
+	altmode->dp_altmode.base.hpd_irq = !!hpd_irq;
+	altmode->dp_altmode.base.multi_func = force_multi_func ? true :
+		!(pin == DPAM_HPD_C || pin == DPAM_HPD_E || pin == DPAM_HPD_OUT);
+
+	DP_DEBUG("payload=0x%x\n", dp_data);
+	DP_DEBUG("port_index=%d, orientation=%d, pin=%d, hpd_state=%d\n",
+			port_index, orientation, pin, hpd_state);
+	DP_DEBUG("multi_func=%d, hpd_high=%d, hpd_irq=%d\n",
+			altmode->dp_altmode.base.multi_func,
+			altmode->dp_altmode.base.hpd_high,
+			altmode->dp_altmode.base.hpd_irq);
+	DP_DEBUG("connected=%d\n", altmode->connected);
+	SDE_EVT32_EXTERNAL(dp_data, port_index, orientation, pin, hpd_state,
+			altmode->dp_altmode.base.multi_func,
+			altmode->dp_altmode.base.hpd_high,
+			altmode->dp_altmode.base.hpd_irq, altmode->connected);
+
+	if (!pin) {
+		/* Cable detach */
+		if (altmode->connected) {
+			altmode->connected = false;
+			altmode->dp_altmode.base.alt_mode_cfg_done = false;
+			altmode->dp_altmode.base.orientation = ORIENTATION_NONE;
+			if (altmode->dp_cb && altmode->dp_cb->disconnect)
+				altmode->dp_cb->disconnect(altmode->dev);
+
+			rc = dp_altmode_set_usb_dp_mode(altmode);
+			if (rc)
+				DP_ERR("failed to clear usb dp mode, rc: %d\n", rc);
+		}
+		goto ack;
+	}
+
+	/* Configure */
+	if (!altmode->connected) {
+		altmode->connected = true;
+		altmode->dp_altmode.base.alt_mode_cfg_done = true;
+		altmode->forced_disconnect = false;
+		altmode->lanes = 4;
+
+		if (altmode->dp_altmode.base.multi_func)
+			altmode->lanes = 2;
+
+		DP_DEBUG("Connected=%d, lanes=%d\n",altmode->connected,altmode->lanes);
+
+		switch (orientation) {
+		case 0:
+			orientation = ORIENTATION_CC1;
+			break;
+		case 1:
+			orientation = ORIENTATION_CC2;
+			break;
+		case 2:
+			orientation = ORIENTATION_NONE;
+			break;
+		default:
+			orientation = ORIENTATION_NONE;
+			break;
+		}
+
+		altmode->dp_altmode.base.orientation = orientation;
+
+		rc = dp_altmode_set_usb_dp_mode(altmode);
+		if (rc)
+			goto ack;
+
+		if (altmode->dp_cb && altmode->dp_cb->configure)
+			altmode->dp_cb->configure(altmode->dev);
+		goto ack;
+	}
+
+	/* Attention */
+	if (altmode->forced_disconnect)
+		goto ack;
+
+	if (altmode->dp_cb && altmode->dp_cb->attention)
+		altmode->dp_cb->attention(altmode->dev);
+ack:
+	dp_altmode_send_pan_ack(altmode->amclient, port_index);
+	return rc;
+}
+
+static void dp_altmode_register(void *priv)
+{
+	struct dp_altmode_private *altmode = priv;
+	struct altmode_client_data cd = {
+		.callback	= &dp_altmode_notify,
+	};
+
+	cd.name = "displayport";
+	cd.svid = USB_SID_DISPLAYPORT;
+	cd.priv = altmode;
+
+	altmode->amclient = altmode_register_client(altmode->dev, &cd);
+	if (IS_ERR_OR_NULL(altmode->amclient))
+		DP_ERR("failed to register as client: %ld\n",
+				PTR_ERR(altmode->amclient));
+	else
+		DP_DEBUG("success\n");
+}
+
+static int dp_altmode_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
+{
+	struct dp_altmode *dp_altmode;
+	struct dp_altmode_private *altmode;
+
+	dp_altmode = container_of(dp_hpd, struct dp_altmode, base);
+	altmode = container_of(dp_altmode, struct dp_altmode_private,
+			dp_altmode);
+
+	dp_altmode->base.hpd_high = hpd;
+	altmode->forced_disconnect = !hpd;
+	altmode->dp_altmode.base.alt_mode_cfg_done = hpd;
+
+	if (hpd)
+		altmode->dp_cb->configure(altmode->dev);
+	else
+		altmode->dp_cb->disconnect(altmode->dev);
+
+	return 0;
+}
+
+static int dp_altmode_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
+{
+	struct dp_altmode *dp_altmode;
+	struct dp_altmode_private *altmode;
+	struct dp_altmode *status;
+
+	dp_altmode = container_of(dp_hpd, struct dp_altmode, base);
+	altmode = container_of(dp_altmode, struct dp_altmode_private,
+			dp_altmode);
+
+	status = &altmode->dp_altmode;
+
+	status->base.hpd_high  = (vdo & BIT(7)) ? true : false;
+	status->base.hpd_irq   = (vdo & BIT(8)) ? true : false;
+
+	if (altmode->dp_cb && altmode->dp_cb->attention)
+		altmode->dp_cb->attention(altmode->dev);
+
+	return 0;
+}
+
+struct dp_hpd *dp_altmode_get(struct device *dev, struct dp_hpd_cb *cb)
+{
+	int rc = 0;
+	struct dp_altmode_private *altmode;
+	struct dp_altmode *dp_altmode;
+
+	if (!cb) {
+		DP_ERR("invalid cb data\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	altmode = kzalloc(sizeof(*altmode), GFP_KERNEL);
+	if (!altmode)
+		return ERR_PTR(-ENOMEM);
+
+	altmode->dev = dev;
+	altmode->dp_cb = cb;
+
+	dp_altmode = &altmode->dp_altmode;
+	dp_altmode->base.register_hpd = NULL;
+	dp_altmode->base.simulate_connect = dp_altmode_simulate_connect;
+	dp_altmode->base.simulate_attention = dp_altmode_simulate_attention;
+
+	rc = altmode_register_notifier(dev, dp_altmode_register, altmode);
+	if (rc < 0) {
+		DP_ERR("altmode probe notifier registration failed: %d\n", rc);
+		goto error;
+	}
+
+	DP_DEBUG("success\n");
+
+	return &dp_altmode->base;
+error:
+	kfree(altmode);
+	return ERR_PTR(rc);
+}
+
+void dp_altmode_put(struct dp_hpd *dp_hpd)
+{
+	struct dp_altmode *dp_altmode;
+	struct dp_altmode_private *altmode;
+
+	dp_altmode = container_of(dp_hpd, struct dp_altmode, base);
+	if (!dp_altmode)
+		return;
+
+	altmode = container_of(dp_altmode, struct dp_altmode_private,
+			dp_altmode);
+
+	altmode_deregister_client(altmode->amclient);
+	altmode_deregister_notifier(altmode->dev, altmode);
+
+	kfree(altmode);
+}

+ 22 - 0
qcom/opensource/display-drivers/msm/dp/dp_altmode.h

@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_ALTMODE_H_
+#define _DP_ALTMODE_H_
+
+#include <linux/types.h>
+#include "dp_hpd.h"
+
+struct device;
+
+struct dp_altmode {
+	struct dp_hpd base;
+};
+
+struct dp_hpd *dp_altmode_get(struct device *dev, struct dp_hpd_cb *cb);
+
+void dp_altmode_put(struct dp_hpd *pd);
+#endif /* _DP_ALTMODE_H_ */
+

+ 915 - 0
qcom/opensource/display-drivers/msm/dp/dp_audio.c

@@ -0,0 +1,915 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/of_platform.h>
+#include <linux/soc/qcom/msm_ext_display.h>
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0))
+#include <drm/display/drm_dp_helper.h>
+#else
+#include <drm/drm_dp_helper.h>
+#endif
+
+#include "dp_catalog.h"
+#include "dp_audio.h"
+#include "dp_panel.h"
+#include "dp_debug.h"
+
+struct dp_audio_private {
+	struct platform_device *ext_pdev;
+	struct platform_device *pdev;
+	struct dp_catalog_audio *catalog;
+	struct msm_ext_disp_init_data ext_audio_data;
+	struct dp_panel *panel;
+
+	bool ack_enabled;
+	atomic_t session_on;
+	bool engine_on;
+
+	u32 channels;
+
+	struct completion hpd_comp;
+	struct workqueue_struct *notify_workqueue;
+	struct delayed_work notify_delayed_work;
+	struct mutex ops_lock;
+
+	struct dp_audio dp_audio;
+
+	atomic_t acked;
+};
+
+static u32 dp_audio_get_header(struct dp_catalog_audio *catalog,
+		enum dp_catalog_audio_sdp_type sdp,
+		enum dp_catalog_audio_header_type header)
+{
+	catalog->sdp_type = sdp;
+	catalog->sdp_header = header;
+	catalog->get_header(catalog);
+
+	return catalog->data;
+}
+
+static void dp_audio_set_header(struct dp_catalog_audio *catalog,
+		u32 data,
+		enum dp_catalog_audio_sdp_type sdp,
+		enum dp_catalog_audio_header_type header)
+{
+	catalog->sdp_type = sdp;
+	catalog->sdp_header = header;
+	catalog->data = data;
+	catalog->set_header(catalog);
+}
+
+static void dp_audio_stream_sdp(struct dp_audio_private *audio)
+{
+	struct dp_catalog_audio *catalog = audio->catalog;
+	u32 value, new_value;
+	u8 parity_byte;
+
+	/* Config header and parity byte 1 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_1);
+	value &= 0x0000ffff;
+
+	new_value = 0x02;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_1_BIT)
+			| (parity_byte << PARITY_BYTE_1_BIT));
+	DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_1);
+
+	/* Config header and parity byte 2 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_2);
+	value &= 0xffff0000;
+	new_value = 0x0;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_2_BIT)
+			| (parity_byte << PARITY_BYTE_2_BIT));
+	DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_2);
+
+	/* Config header and parity byte 3 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_3);
+	value &= 0x0000ffff;
+
+	new_value = audio->channels - 1;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_3_BIT)
+			| (parity_byte << PARITY_BYTE_3_BIT));
+	DP_DEBUG("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
+		value, parity_byte);
+
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_3);
+}
+
+static void dp_audio_timestamp_sdp(struct dp_audio_private *audio)
+{
+	struct dp_catalog_audio *catalog = audio->catalog;
+	u32 value, new_value;
+	u8 parity_byte;
+
+	/* Config header and parity byte 1 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_1);
+	value &= 0x0000ffff;
+
+	new_value = 0x1;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_1_BIT)
+			| (parity_byte << PARITY_BYTE_1_BIT));
+	DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+		value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_1);
+
+	/* Config header and parity byte 2 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_2);
+	value &= 0xffff0000;
+
+	new_value = 0x17;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_2_BIT)
+			| (parity_byte << PARITY_BYTE_2_BIT));
+	DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_2);
+
+	/* Config header and parity byte 3 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_3);
+	value &= 0x0000ffff;
+
+	new_value = (0x0 | (0x11 << 2));
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_3_BIT)
+			| (parity_byte << PARITY_BYTE_3_BIT));
+	DP_DEBUG("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_3);
+}
+
+static void dp_audio_infoframe_sdp(struct dp_audio_private *audio)
+{
+	struct dp_catalog_audio *catalog = audio->catalog;
+	u32 value, new_value;
+	u8 parity_byte;
+
+	/* Config header and parity byte 1 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_1);
+	value &= 0x0000ffff;
+
+	new_value = 0x84;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_1_BIT)
+			| (parity_byte << PARITY_BYTE_1_BIT));
+	DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_1);
+
+	/* Config header and parity byte 2 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_2);
+	value &= 0xffff0000;
+
+	new_value = 0x1b;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_2_BIT)
+			| (parity_byte << PARITY_BYTE_2_BIT));
+	DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_2);
+
+	/* Config header and parity byte 3 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_3);
+	value &= 0x0000ffff;
+
+	new_value = (0x0 | (0x11 << 2));
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_3_BIT)
+			| (parity_byte << PARITY_BYTE_3_BIT));
+	DP_DEBUG("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
+			new_value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_3);
+}
+
+static void dp_audio_copy_management_sdp(struct dp_audio_private *audio)
+{
+	struct dp_catalog_audio *catalog = audio->catalog;
+	u32 value, new_value;
+	u8 parity_byte;
+
+	/* Config header and parity byte 1 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_1);
+	value &= 0x0000ffff;
+
+	new_value = 0x05;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_1_BIT)
+			| (parity_byte << PARITY_BYTE_1_BIT));
+	DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_1);
+
+	/* Config header and parity byte 2 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_2);
+	value &= 0xffff0000;
+
+	new_value = 0x0F;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_2_BIT)
+			| (parity_byte << PARITY_BYTE_2_BIT));
+	DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_2);
+
+	/* Config header and parity byte 3 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_3);
+	value &= 0x0000ffff;
+
+	new_value = 0x0;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_3_BIT)
+			| (parity_byte << PARITY_BYTE_3_BIT));
+	DP_DEBUG("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_3);
+}
+
+static void dp_audio_isrc_sdp(struct dp_audio_private *audio)
+{
+	struct dp_catalog_audio *catalog = audio->catalog;
+	u32 value, new_value;
+	u8 parity_byte;
+
+	/* Config header and parity byte 1 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_1);
+	value &= 0x0000ffff;
+
+	new_value = 0x06;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_1_BIT)
+			| (parity_byte << PARITY_BYTE_1_BIT));
+	DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_1);
+
+	/* Config header and parity byte 2 */
+	value = dp_audio_get_header(catalog,
+			DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_2);
+	value &= 0xffff0000;
+
+	new_value = 0x0F;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_2_BIT)
+			| (parity_byte << PARITY_BYTE_2_BIT));
+	DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_audio_set_header(catalog, value,
+		DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_2);
+}
+
+static void dp_audio_setup_sdp(struct dp_audio_private *audio)
+{
+	if (!atomic_read(&audio->session_on)) {
+		DP_WARN("session inactive\n");
+		return;
+	}
+
+	/* always program stream 0 first before actual stream cfg */
+	audio->catalog->stream_id = DP_STREAM_0;
+	audio->catalog->config_sdp(audio->catalog);
+
+	if (audio->panel->stream_id == DP_STREAM_1) {
+		audio->catalog->stream_id = DP_STREAM_1;
+		audio->catalog->config_sdp(audio->catalog);
+	}
+
+	dp_audio_stream_sdp(audio);
+	dp_audio_timestamp_sdp(audio);
+	dp_audio_infoframe_sdp(audio);
+	dp_audio_copy_management_sdp(audio);
+	dp_audio_isrc_sdp(audio);
+}
+
+static void dp_audio_setup_acr(struct dp_audio_private *audio)
+{
+	u32 select = 0;
+	struct dp_catalog_audio *catalog = audio->catalog;
+
+	if (!atomic_read(&audio->session_on)) {
+		DP_WARN("session inactive\n");
+		return;
+	}
+
+	switch (audio->dp_audio.bw_code) {
+	case DP_LINK_BW_1_62:
+		select = 0;
+		break;
+	case DP_LINK_BW_2_7:
+		select = 1;
+		break;
+	case DP_LINK_BW_5_4:
+		select = 2;
+		break;
+	case DP_LINK_BW_8_1:
+		select = 3;
+		break;
+	default:
+		DP_DEBUG("Unknown link rate\n");
+		select = 0;
+		break;
+	}
+
+	catalog->data = select;
+	catalog->config_acr(catalog);
+}
+
+static void dp_audio_enable(struct dp_audio_private *audio, bool enable)
+{
+	struct dp_catalog_audio *catalog = audio->catalog;
+
+	audio->engine_on = enable;
+	if (!atomic_read(&audio->session_on)) {
+		DP_WARN("session inactive. enable=%d\n", enable);
+		return;
+	}
+	catalog->data = enable;
+
+	if (audio->panel->get_panel_on(audio->panel))
+		catalog->enable(catalog);
+
+}
+
+static struct dp_audio_private *dp_audio_get_data(struct platform_device *pdev)
+{
+	struct msm_ext_disp_data *ext_data;
+	struct dp_audio *dp_audio;
+
+	if (!pdev) {
+		DP_ERR("invalid input\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	ext_data = platform_get_drvdata(pdev);
+	if (!ext_data) {
+		DP_ERR("invalid ext disp data\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	dp_audio = ext_data->intf_data;
+	if (!dp_audio) {
+		DP_ERR("invalid intf data\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	return container_of(dp_audio, struct dp_audio_private, dp_audio);
+}
+
+static int dp_audio_info_setup(struct platform_device *pdev,
+	struct msm_ext_disp_audio_setup_params *params)
+{
+	int rc = 0;
+	struct dp_audio_private *audio;
+
+	audio = dp_audio_get_data(pdev);
+	if (IS_ERR(audio)) {
+		rc = PTR_ERR(audio);
+		return rc;
+	}
+
+	if (audio->dp_audio.tui_active) {
+		DP_DEBUG("TUI session active\n");
+		return 0;
+	}
+
+	mutex_lock(&audio->ops_lock);
+
+	audio->channels = params->num_of_channels;
+
+	if (audio->panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream id: %d\n",
+				audio->panel->stream_id);
+		rc = -EINVAL;
+		mutex_unlock(&audio->ops_lock);
+		return rc;
+	}
+
+	dp_audio_setup_sdp(audio);
+	dp_audio_setup_acr(audio);
+	dp_audio_enable(audio, true);
+
+	mutex_unlock(&audio->ops_lock);
+
+	DP_DEBUG("audio stream configured\n");
+
+	return rc;
+}
+
+static int dp_audio_get_edid_blk(struct platform_device *pdev,
+		struct msm_ext_disp_audio_edid_blk *blk)
+{
+	int rc = 0;
+	struct dp_audio_private *audio;
+	struct sde_edid_ctrl *edid;
+
+	if (!blk) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	audio = dp_audio_get_data(pdev);
+	if (IS_ERR(audio)) {
+		rc = PTR_ERR(audio);
+		goto end;
+	}
+
+	if (!audio->panel || !audio->panel->edid_ctrl) {
+		DP_ERR("invalid panel data\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	edid = audio->panel->edid_ctrl;
+
+	blk->audio_data_blk = edid->audio_data_block;
+	blk->audio_data_blk_size = edid->adb_size;
+
+	blk->spk_alloc_data_blk = edid->spkr_alloc_data_block;
+	blk->spk_alloc_data_blk_size = edid->sadb_size;
+end:
+	return rc;
+}
+
+static int dp_audio_get_cable_status(struct platform_device *pdev, u32 vote)
+{
+	int rc = 0;
+	struct dp_audio_private *audio;
+
+	audio = dp_audio_get_data(pdev);
+	if (IS_ERR(audio)) {
+		rc = PTR_ERR(audio);
+		goto end;
+	}
+
+	return atomic_read(&audio->session_on);
+end:
+	return rc;
+}
+
+static int dp_audio_get_intf_id(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct dp_audio_private *audio;
+
+	audio = dp_audio_get_data(pdev);
+	if (IS_ERR(audio)) {
+		rc = PTR_ERR(audio);
+		goto end;
+	}
+
+	return EXT_DISPLAY_TYPE_DP;
+end:
+	return rc;
+}
+
+static void dp_audio_teardown_done(struct platform_device *pdev)
+{
+	struct dp_audio_private *audio;
+
+	audio = dp_audio_get_data(pdev);
+	if (IS_ERR(audio))
+		return;
+
+	if (audio->dp_audio.tui_active) {
+		DP_DEBUG("TUI session active\n");
+		return;
+	}
+
+	if (audio->panel->stream_id >= DP_STREAM_MAX) {
+		DP_WARN("invalid stream id: %d\n",
+				audio->panel->stream_id);
+		return;
+	}
+
+	mutex_lock(&audio->ops_lock);
+	dp_audio_enable(audio, false);
+	mutex_unlock(&audio->ops_lock);
+
+	atomic_set(&audio->acked, 1);
+	complete_all(&audio->hpd_comp);
+
+	DP_DEBUG("audio engine disabled\n");
+}
+
+static int dp_audio_ack_done(struct platform_device *pdev, u32 ack)
+{
+	int rc = 0, ack_hpd;
+	struct dp_audio_private *audio;
+
+	audio = dp_audio_get_data(pdev);
+	if (IS_ERR(audio)) {
+		rc = PTR_ERR(audio);
+		goto end;
+	}
+
+	if (ack & AUDIO_ACK_SET_ENABLE) {
+		audio->ack_enabled = ack & AUDIO_ACK_ENABLE ?
+			true : false;
+
+		DP_DEBUG("audio ack feature %s\n",
+			audio->ack_enabled ? "enabled" : "disabled");
+		goto end;
+	}
+
+	if (!audio->ack_enabled)
+		goto end;
+
+	ack_hpd = ack & AUDIO_ACK_CONNECT;
+
+	DP_DEBUG("acknowledging audio (%d)\n", ack_hpd);
+
+	if (!audio->engine_on) {
+		atomic_set(&audio->acked, 1);
+		complete_all(&audio->hpd_comp);
+	}
+end:
+	return rc;
+}
+
+static int dp_audio_codec_ready(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct dp_audio_private *audio;
+
+	audio = dp_audio_get_data(pdev);
+	if (IS_ERR(audio)) {
+		DP_ERR("invalid input\n");
+		rc = PTR_ERR(audio);
+		goto end;
+	}
+
+	queue_delayed_work(audio->notify_workqueue,
+			&audio->notify_delayed_work, HZ/4);
+end:
+	return rc;
+}
+
+static int dp_audio_register_ext_disp(struct dp_audio_private *audio)
+{
+	int rc = 0;
+	struct device_node *pd = NULL;
+	const char *phandle = "qcom,ext-disp";
+	struct msm_ext_disp_init_data *ext;
+	struct msm_ext_disp_audio_codec_ops *ops;
+
+	ext = &audio->ext_audio_data;
+	ops = &ext->codec_ops;
+
+	ext->codec.type = EXT_DISPLAY_TYPE_DP;
+	ext->codec.ctrl_id = 0;
+	ext->codec.stream_id = audio->panel->stream_id;
+	ext->pdev = audio->pdev;
+	ext->intf_data = &audio->dp_audio;
+
+	ops->audio_info_setup   = dp_audio_info_setup;
+	ops->get_audio_edid_blk = dp_audio_get_edid_blk;
+	ops->cable_status       = dp_audio_get_cable_status;
+	ops->get_intf_id        = dp_audio_get_intf_id;
+	ops->teardown_done      = dp_audio_teardown_done;
+	ops->acknowledge        = dp_audio_ack_done;
+	ops->ready              = dp_audio_codec_ready;
+
+	if (!audio->pdev->dev.of_node) {
+		DP_ERR("cannot find audio dev.of_node\n");
+		rc = -ENODEV;
+		goto end;
+	}
+
+	pd = of_parse_phandle(audio->pdev->dev.of_node, phandle, 0);
+	if (!pd) {
+		DP_ERR("cannot parse %s handle\n", phandle);
+		rc = -ENODEV;
+		goto end;
+	}
+
+	audio->ext_pdev = of_find_device_by_node(pd);
+	if (!audio->ext_pdev) {
+		DP_ERR("cannot find %s pdev\n", phandle);
+		rc = -ENODEV;
+		goto end;
+	}
+#if IS_ENABLED(CONFIG_MSM_EXT_DISPLAY)
+	rc = msm_ext_disp_register_intf(audio->ext_pdev, ext);
+	if (rc)
+		DP_ERR("failed to register disp\n");
+#endif
+end:
+	if (pd)
+		of_node_put(pd);
+
+	return rc;
+}
+
+static int dp_audio_deregister_ext_disp(struct dp_audio_private *audio)
+{
+	int rc = 0;
+	struct device_node *pd = NULL;
+	const char *phandle = "qcom,ext-disp";
+	struct msm_ext_disp_init_data *ext;
+
+	ext = &audio->ext_audio_data;
+
+	if (!audio->pdev->dev.of_node) {
+		DP_ERR("cannot find audio dev.of_node\n");
+		rc = -ENODEV;
+		goto end;
+	}
+
+	pd = of_parse_phandle(audio->pdev->dev.of_node, phandle, 0);
+	if (!pd) {
+		DP_ERR("cannot parse %s handle\n", phandle);
+		rc = -ENODEV;
+		goto end;
+	}
+
+	audio->ext_pdev = of_find_device_by_node(pd);
+	if (!audio->ext_pdev) {
+		DP_ERR("cannot find %s pdev\n", phandle);
+		rc = -ENODEV;
+		goto end;
+	}
+
+#if IS_ENABLED(CONFIG_MSM_EXT_DISPLAY)
+	rc = msm_ext_disp_deregister_intf(audio->ext_pdev, ext);
+	if (rc)
+		DP_ERR("failed to deregister disp\n");
+#endif
+
+end:
+	return rc;
+}
+
+static int dp_audio_notify(struct dp_audio_private *audio, u32 state)
+{
+	int rc = 0;
+	struct msm_ext_disp_init_data *ext = &audio->ext_audio_data;
+
+	atomic_set(&audio->acked, 0);
+
+	if (!ext->intf_ops.audio_notify) {
+		DP_ERR("audio notify not defined\n");
+		goto end;
+	}
+
+	reinit_completion(&audio->hpd_comp);
+	rc = ext->intf_ops.audio_notify(audio->ext_pdev,
+			&ext->codec, state);
+	if (rc)
+		goto end;
+
+	if (atomic_read(&audio->acked))
+		goto end;
+
+	if (state == EXT_DISPLAY_CABLE_DISCONNECT && !audio->engine_on)
+		goto end;
+
+	if (state == EXT_DISPLAY_CABLE_CONNECT)
+		goto end;
+
+	rc = wait_for_completion_timeout(&audio->hpd_comp, HZ * 4);
+	if (!rc) {
+		DP_ERR("timeout. state=%d err=%d\n", state, rc);
+		rc = -ETIMEDOUT;
+		goto end;
+	}
+
+	DP_DEBUG("success\n");
+end:
+	return rc;
+}
+
+static int dp_audio_config(struct dp_audio_private *audio, u32 state)
+{
+	int rc = 0;
+	struct msm_ext_disp_init_data *ext = &audio->ext_audio_data;
+
+	if (!ext || !ext->intf_ops.audio_config) {
+		DP_ERR("audio_config not defined\n");
+		goto end;
+	}
+
+	/*
+	 * DP Audio sets default STREAM_0 only, other streams are
+	 * set by audio driver based on the hardware/software support.
+	 */
+	if (audio->panel->stream_id == DP_STREAM_0) {
+		rc = ext->intf_ops.audio_config(audio->ext_pdev,
+				&ext->codec, state);
+		if (rc)
+			DP_ERR("failed to config audio, err=%d\n",
+					rc);
+	}
+end:
+	return rc;
+}
+
+static int dp_audio_on(struct dp_audio *dp_audio)
+{
+	int rc = 0;
+	struct dp_audio_private *audio;
+	struct msm_ext_disp_init_data *ext;
+
+	if (!dp_audio) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
+	if (IS_ERR(audio)) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp_audio_register_ext_disp(audio);
+
+	ext = &audio->ext_audio_data;
+
+	atomic_set(&audio->session_on, 1);
+
+	rc = dp_audio_config(audio, EXT_DISPLAY_CABLE_CONNECT);
+	if (rc)
+		goto end;
+
+	rc = dp_audio_notify(audio, EXT_DISPLAY_CABLE_CONNECT);
+	if (rc)
+		goto end;
+
+	DP_DEBUG("success\n");
+end:
+	return rc;
+}
+
+static int dp_audio_off(struct dp_audio *dp_audio, bool skip_wait)
+{
+	int rc = 0;
+	struct dp_audio_private *audio;
+	struct msm_ext_disp_init_data *ext;
+	bool work_pending = false;
+
+	if (!dp_audio) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
+
+	if (!atomic_read(&audio->session_on)) {
+		DP_DEBUG("audio already off\n");
+		return rc;
+	}
+
+	ext = &audio->ext_audio_data;
+
+	work_pending = cancel_delayed_work_sync(&audio->notify_delayed_work);
+	if (work_pending)
+		DP_DEBUG("pending notification work completed\n");
+
+	if (!skip_wait) {
+		rc = dp_audio_notify(audio, EXT_DISPLAY_CABLE_DISCONNECT);
+		if (rc)
+			goto end;
+	}
+
+	DP_DEBUG("success\n");
+end:
+	dp_audio_config(audio, EXT_DISPLAY_CABLE_DISCONNECT);
+
+	atomic_set(&audio->session_on, 0);
+	audio->engine_on  = false;
+
+	dp_audio_deregister_ext_disp(audio);
+
+	return rc;
+}
+
+static void dp_audio_notify_work_fn(struct work_struct *work)
+{
+	struct dp_audio_private *audio;
+	struct delayed_work *dw = to_delayed_work(work);
+
+	audio = container_of(dw, struct dp_audio_private, notify_delayed_work);
+
+	dp_audio_notify(audio, EXT_DISPLAY_CABLE_CONNECT);
+}
+
+static int dp_audio_create_notify_workqueue(struct dp_audio_private *audio)
+{
+	audio->notify_workqueue = create_workqueue("sdm_dp_audio_notify");
+	if (IS_ERR_OR_NULL(audio->notify_workqueue)) {
+		DP_ERR("Error creating notify_workqueue\n");
+		return -EPERM;
+	}
+
+	INIT_DELAYED_WORK(&audio->notify_delayed_work, dp_audio_notify_work_fn);
+
+	return 0;
+}
+
+static void dp_audio_destroy_notify_workqueue(struct dp_audio_private *audio)
+{
+	if (audio->notify_workqueue)
+		destroy_workqueue(audio->notify_workqueue);
+}
+
+struct dp_audio *dp_audio_get(struct platform_device *pdev,
+			struct dp_panel *panel,
+			struct dp_catalog_audio *catalog)
+{
+	int rc = 0;
+	struct dp_audio_private *audio;
+	struct dp_audio *dp_audio;
+
+	if (!pdev || !panel || !catalog) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	audio = devm_kzalloc(&pdev->dev, sizeof(*audio), GFP_KERNEL);
+	if (!audio) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	rc = dp_audio_create_notify_workqueue(audio);
+	if (rc)
+		goto error_notify_workqueue;
+
+	init_completion(&audio->hpd_comp);
+
+	audio->pdev = pdev;
+	audio->panel = panel;
+	audio->catalog = catalog;
+
+	atomic_set(&audio->acked, 0);
+
+	dp_audio = &audio->dp_audio;
+
+	mutex_init(&audio->ops_lock);
+
+	dp_audio->on  = dp_audio_on;
+	dp_audio->off = dp_audio_off;
+
+	catalog->init(catalog);
+
+	return dp_audio;
+
+error_notify_workqueue:
+	devm_kfree(&pdev->dev, audio);
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_audio_put(struct dp_audio *dp_audio)
+{
+	struct dp_audio_private *audio;
+
+	if (!dp_audio)
+		return;
+
+	audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
+
+	mutex_destroy(&audio->ops_lock);
+
+	dp_audio_destroy_notify_workqueue(audio);
+
+	devm_kfree(&audio->pdev->dev, audio);
+}

+ 76 - 0
qcom/opensource/display-drivers/msm/dp/dp_audio.h

@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_AUDIO_H_
+#define _DP_AUDIO_H_
+
+#include <linux/platform_device.h>
+
+#include "dp_panel.h"
+#include "dp_catalog.h"
+
+/**
+ * struct dp_audio
+ * @lane_count: number of lanes configured in current session
+ * @bw_code: link rate's bandwidth code for current session
+ * @tui_active: set to true if TUI is active in the system
+ */
+struct dp_audio {
+	u32 lane_count;
+	u32 bw_code;
+	bool tui_active;
+
+	/**
+	 * on()
+	 *
+	 * Notifies user mode clients that DP is powered on, and that audio
+	 * playback can start on the external display.
+	 *
+	 * @dp_audio: an instance of struct dp_audio.
+	 *
+	 * Returns the error code in case of failure, 0 in success case.
+	 */
+	int (*on)(struct dp_audio *dp_audio);
+
+	/**
+	 * off()
+	 *
+	 * Notifies user mode clients that DP is shutting down, and audio
+	 * playback should be stopped on the external display.
+	 *
+	 * @dp_audio: an instance of struct dp_audio.
+	 * @skip_wait: flag to skip any waits
+	 *
+	 * Returns the error code in case of failure, 0 in success case.
+	 */
+	int (*off)(struct dp_audio *dp_audio, bool skip_wait);
+};
+
+/**
+ * dp_audio_get()
+ *
+ * Creates and instance of dp audio.
+ *
+ * @pdev: caller's platform device instance.
+ * @panel: an instance of dp_panel module.
+ * @catalog: an instance of dp_catalog_audio module.
+ *
+ * Returns the error code in case of failure, otherwize
+ * an instance of newly created dp_module.
+ */
+struct dp_audio *dp_audio_get(struct platform_device *pdev,
+			struct dp_panel *panel,
+			struct dp_catalog_audio *catalog);
+
+/**
+ * dp_audio_put()
+ *
+ * Cleans the dp_audio instance.
+ *
+ * @dp_audio: an instance of dp_audio.
+ */
+void dp_audio_put(struct dp_audio *dp_audio);
+#endif /* _DP_AUDIO_H_ */

+ 958 - 0
qcom/opensource/display-drivers/msm/dp/dp_aux.c

@@ -0,0 +1,958 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/delay.h>
+
+#if IS_ENABLED(CONFIG_QCOM_FSA4480_I2C)
+#include <linux/soc/qcom/fsa4480-i2c.h>
+#endif
+#if IS_ENABLED(CONFIG_QCOM_WCD939X_I2C)
+#include <linux/soc/qcom/wcd939x-i2c.h>
+#endif
+
+#include "dp_aux.h"
+#include "dp_hpd.h"
+#include "dp_debug.h"
+
+#define DP_AUX_ENUM_STR(x)		#x
+#define DP_AUX_IPC_NUM_PAGES 10
+
+#define DP_AUX_DEBUG(dp_aux, fmt, ...) \
+	do { \
+		if (dp_aux) \
+			ipc_log_string(dp_aux->ipc_log_context, "[d][%-4d]"fmt,\
+					current->pid, ##__VA_ARGS__); \
+		DP_DEBUG_V(fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define DP_AUX_WARN(dp_aux, fmt, ...) \
+	do { \
+		if (dp_aux) \
+			ipc_log_string(dp_aux->ipc_log_context, "[w][%-4d]"fmt,\
+					current->pid, ##__VA_ARGS__); \
+		DP_WARN_V(fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define DP_AUX_WARN_RATELIMITED(dp_aux, fmt, ...) \
+	do { \
+		if (dp_aux) \
+			ipc_log_string(dp_aux->ipc_log_context, "[w][%-4d]"fmt,\
+					current->pid, ##__VA_ARGS__); \
+		DP_WARN_RATELIMITED_V(fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define DP_AUX_ERR(dp_aux, fmt, ...) \
+	do { \
+		if (dp_aux) \
+			ipc_log_string(dp_aux->ipc_log_context, "[e][%-4d]"fmt,\
+					current->pid, ##__VA_ARGS__); \
+		DP_ERR_V(fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define DP_AUX_ERR_RATELIMITED(dp_aux, fmt, ...) \
+	do { \
+		if (dp_aux) \
+			ipc_log_string(dp_aux->ipc_log_context, "[e][%-4d]"fmt,\
+					current->pid, ##__VA_ARGS__); \
+		DP_ERR_RATELIMITED_V(fmt, ##__VA_ARGS__); \
+	} while (0)
+
+enum {
+	DP_AUX_DATA_INDEX_WRITE = BIT(31),
+};
+
+struct dp_aux_private {
+	struct device *dev;
+	struct dp_aux dp_aux;
+	struct dp_catalog_aux *catalog;
+	struct dp_aux_cfg *cfg;
+	struct device_node *aux_switch_node;
+	struct mutex mutex;
+	struct completion comp;
+	struct drm_dp_aux drm_aux;
+
+	struct dp_aux_bridge *aux_bridge;
+	struct dp_aux_bridge *sim_bridge;
+	bool bridge_in_transfer;
+	bool sim_in_transfer;
+
+	bool cmd_busy;
+	bool native;
+	bool read;
+	bool no_send_addr;
+	bool no_send_stop;
+	bool enabled;
+
+	u32 offset;
+	u32 segment;
+	u32 aux_error_num;
+	u32 retry_cnt;
+
+	bool switch_enable;
+	int switch_orientation;
+
+	atomic_t aborted;
+};
+
+static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
+		struct drm_dp_aux_msg *msg)
+{
+	char prefix[64];
+	int i, linelen, remaining = msg->size;
+	const int rowsize = 16;
+	u8 linebuf[64];
+	struct dp_aux_private *aux = container_of(drm_aux,
+		struct dp_aux_private, drm_aux);
+	struct dp_aux *dp_aux = &aux->dp_aux;
+
+	snprintf(prefix, sizeof(prefix), "%s %s %4xh(%2zu): ",
+		(msg->request & DP_AUX_I2C_MOT) ? "I2C" : "NAT",
+		(msg->request & DP_AUX_I2C_READ) ? "RD" : "WR",
+		msg->address, msg->size);
+
+	for (i = 0; i < msg->size; i += rowsize) {
+		linelen = min(remaining, rowsize);
+		remaining -= rowsize;
+
+		hex_dump_to_buffer(msg->buffer + i, linelen, rowsize, 1,
+			linebuf, sizeof(linebuf), false);
+
+		if (msg->size == 1 && msg->address == 0)
+			DP_DEBUG_V("%s%s\n", prefix, linebuf);
+		else
+			DP_AUX_DEBUG(dp_aux, "%s%s\n", prefix, linebuf);
+	}
+}
+
+static char *dp_aux_get_error(u32 aux_error)
+{
+	switch (aux_error) {
+	case DP_AUX_ERR_NONE:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_NONE);
+	case DP_AUX_ERR_ADDR:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_ADDR);
+	case DP_AUX_ERR_TOUT:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_TOUT);
+	case DP_AUX_ERR_NACK:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK);
+	case DP_AUX_ERR_DEFER:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_DEFER);
+	case DP_AUX_ERR_NACK_DEFER:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK_DEFER);
+	default:
+		return "unknown";
+	}
+}
+
+static u32 dp_aux_write(struct dp_aux_private *aux,
+		struct drm_dp_aux_msg *msg)
+{
+	u32 data[4], reg, len;
+	u8 *msgdata = msg->buffer;
+	int const aux_cmd_fifo_len = 128;
+	int i = 0;
+	struct dp_aux *dp_aux = &aux->dp_aux;
+
+	if (aux->read)
+		len = 4;
+	else
+		len = msg->size + 4;
+
+	/*
+	 * cmd fifo only has depth of 144 bytes
+	 * limit buf length to 128 bytes here
+	 */
+	if (len > aux_cmd_fifo_len) {
+		DP_AUX_ERR(dp_aux, "buf len error\n");
+		return 0;
+	}
+
+	/* Pack cmd and write to HW */
+	data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */
+	if (aux->read)
+		data[0] |=  BIT(4); /* R/W */
+
+	data[1] = (msg->address >> 8) & 0xff;	/* addr[15:8] */
+	data[2] = msg->address & 0xff;		/* addr[7:0] */
+	data[3] = (msg->size - 1) & 0xff;	/* len[7:0] */
+
+	for (i = 0; i < len; i++) {
+		reg = (i < 4) ? data[i] : msgdata[i - 4];
+		reg = ((reg) << 8) & 0x0000ff00; /* index = 0, write */
+		if (i == 0)
+			reg |= DP_AUX_DATA_INDEX_WRITE;
+		aux->catalog->data = reg;
+		aux->catalog->write_data(aux->catalog);
+	}
+
+	aux->catalog->clear_trans(aux->catalog, false);
+	aux->catalog->clear_hw_interrupts(aux->catalog);
+
+	reg = 0; /* Transaction number == 1 */
+	if (!aux->native) { /* i2c */
+		reg |= BIT(8);
+
+		if (aux->no_send_addr)
+			reg |= BIT(10);
+
+		if (aux->no_send_stop)
+			reg |= BIT(11);
+	}
+
+	reg |= BIT(9);
+	aux->catalog->data = reg;
+	aux->catalog->write_trans(aux->catalog);
+
+	return len;
+}
+
+static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
+		struct drm_dp_aux_msg *msg)
+{
+	u32 ret = 0, len = 0, timeout;
+	int const aux_timeout_ms = HZ/4;
+	struct dp_aux *dp_aux = &aux->dp_aux;
+	char prefix[64];
+
+	snprintf(prefix, sizeof(prefix), "%s %s %4xh(%2zu): ",
+			(msg->request & DP_AUX_I2C_MOT) ? "I2C" : "NAT",
+			(msg->request & DP_AUX_I2C_READ) ? "RD" : "WR",
+			msg->address, msg->size);
+
+	reinit_completion(&aux->comp);
+
+	len = dp_aux_write(aux, msg);
+	if (len == 0) {
+		DP_AUX_ERR(dp_aux, "DP AUX write failed: %s\n", prefix);
+		return -EINVAL;
+	}
+
+	timeout = wait_for_completion_timeout(&aux->comp, aux_timeout_ms);
+	if (!timeout) {
+		DP_AUX_WARN_RATELIMITED(dp_aux, "aux timeout during [%s]\n", prefix);
+		return -ETIMEDOUT;
+	}
+
+	if (aux->aux_error_num == DP_AUX_ERR_NONE) {
+		ret = len;
+	} else {
+		DP_AUX_WARN_RATELIMITED(dp_aux, "aux err [%s] during [%s]\n",
+				dp_aux_get_error(aux->aux_error_num), prefix);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void dp_aux_cmd_fifo_rx(struct dp_aux_private *aux,
+		struct drm_dp_aux_msg *msg)
+{
+	u32 data;
+	u8 *dp;
+	u32 i, actual_i;
+	u32 len = msg->size;
+	struct dp_aux *dp_aux = &aux->dp_aux;
+
+	aux->catalog->clear_trans(aux->catalog, true);
+
+	data = 0;
+	data |= DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */
+	data |= BIT(0);  /* read */
+
+	aux->catalog->data = data;
+	aux->catalog->write_data(aux->catalog);
+
+	dp = msg->buffer;
+
+	/* discard first byte */
+	data = aux->catalog->read_data(aux->catalog);
+
+	for (i = 0; i < len; i++) {
+		data = aux->catalog->read_data(aux->catalog);
+		*dp++ = (u8)((data >> 8) & 0xff);
+
+		actual_i = (data >> 16) & 0xFF;
+		if (i != actual_i)
+			DP_AUX_WARN(dp_aux, "Index mismatch: expected %d, found %d\n",
+				i, actual_i);
+	}
+}
+
+static void dp_aux_native_handler(struct dp_aux_private *aux)
+{
+	u32 isr = aux->catalog->isr;
+
+	if (isr & DP_INTR_AUX_I2C_DONE)
+		aux->aux_error_num = DP_AUX_ERR_NONE;
+	else if (isr & DP_INTR_WRONG_ADDR)
+		aux->aux_error_num = DP_AUX_ERR_ADDR;
+	else if (isr & DP_INTR_TIMEOUT)
+		aux->aux_error_num = DP_AUX_ERR_TOUT;
+	if (isr & DP_INTR_NACK_DEFER)
+		aux->aux_error_num = DP_AUX_ERR_NACK;
+	if (isr & DP_INTR_AUX_ERROR) {
+		aux->aux_error_num = DP_AUX_ERR_PHY;
+		aux->catalog->clear_hw_interrupts(aux->catalog);
+	}
+
+	complete(&aux->comp);
+}
+
+static void dp_aux_i2c_handler(struct dp_aux_private *aux)
+{
+	u32 isr = aux->catalog->isr;
+
+	if (isr & DP_INTR_AUX_I2C_DONE) {
+		if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER))
+			aux->aux_error_num = DP_AUX_ERR_NACK;
+		else
+			aux->aux_error_num = DP_AUX_ERR_NONE;
+	} else {
+		if (isr & DP_INTR_WRONG_ADDR)
+			aux->aux_error_num = DP_AUX_ERR_ADDR;
+		else if (isr & DP_INTR_TIMEOUT)
+			aux->aux_error_num = DP_AUX_ERR_TOUT;
+		if (isr & DP_INTR_NACK_DEFER)
+			aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;
+		if (isr & DP_INTR_I2C_NACK)
+			aux->aux_error_num = DP_AUX_ERR_NACK;
+		if (isr & DP_INTR_I2C_DEFER)
+			aux->aux_error_num = DP_AUX_ERR_DEFER;
+		if (isr & DP_INTR_AUX_ERROR) {
+			aux->aux_error_num = DP_AUX_ERR_PHY;
+			aux->catalog->clear_hw_interrupts(aux->catalog);
+		}
+	}
+
+	complete(&aux->comp);
+}
+
+static void dp_aux_isr(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		DP_AUX_ERR(dp_aux, "invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	aux->catalog->get_irq(aux->catalog, aux->cmd_busy);
+
+	if (!aux->cmd_busy)
+		return;
+
+	if (aux->native)
+		dp_aux_native_handler(aux);
+	else
+		dp_aux_i2c_handler(aux);
+}
+
+static void dp_aux_reconfig(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		DP_AUX_ERR(dp_aux, "invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	aux->catalog->update_aux_cfg(aux->catalog,
+			aux->cfg, PHY_AUX_CFG1);
+	aux->catalog->reset(aux->catalog);
+}
+
+static void dp_aux_abort_transaction(struct dp_aux *dp_aux, bool abort)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		DP_AUX_ERR(dp_aux, "invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	atomic_set(&aux->aborted, abort);
+}
+
+static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
+		struct drm_dp_aux_msg *input_msg)
+{
+	u32 const edid_address = 0x50;
+	u32 const segment_address = 0x30;
+	bool i2c_read = input_msg->request &
+		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+	u8 *data = NULL;
+
+	if (aux->native || i2c_read || ((input_msg->address != edid_address) &&
+		(input_msg->address != segment_address)))
+		return;
+
+
+	data = input_msg->buffer;
+	if (input_msg->address == segment_address)
+		aux->segment = *data;
+	else
+		aux->offset = *data;
+}
+
+/**
+ * dp_aux_transfer_helper() - helper function for EDID read transactions
+ *
+ * @aux: DP AUX private structure
+ * @input_msg: input message from DRM upstream APIs
+ * @send_seg: send the seg to sink
+ *
+ * return: void
+ *
+ * This helper function is used to fix EDID reads for non-compliant
+ * sinks that do not handle the i2c middle-of-transaction flag correctly.
+ */
+static void dp_aux_transfer_helper(struct dp_aux_private *aux,
+		struct drm_dp_aux_msg *input_msg, bool send_seg)
+{
+	struct drm_dp_aux_msg helper_msg;
+	u32 const message_size = 0x10;
+	u32 const segment_address = 0x30;
+	u32 const edid_block_length = 0x80;
+	bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT;
+	bool i2c_read = input_msg->request &
+		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+
+	if (!i2c_mot || !i2c_read || (input_msg->size == 0))
+		return;
+
+	/*
+	 * Sending the segment value and EDID offset will be performed
+	 * from the DRM upstream EDID driver for each block. Avoid
+	 * duplicate AUX transactions related to this while reading the
+	 * first 16 bytes of each block.
+	 */
+	if (!(aux->offset % edid_block_length) || !send_seg)
+		goto end;
+
+	aux->read = false;
+	aux->cmd_busy = true;
+	aux->no_send_addr = true;
+	aux->no_send_stop = true;
+
+	/*
+	 * Send the segment address for i2c reads for segment > 0 and for which
+	 * the middle-of-transaction flag is set. This is required to support
+	 * EDID reads of more than 2 blocks as the segment address is reset to 0
+	 * since we are overriding the middle-of-transaction flag for read
+	 * transactions.
+	 */
+	if (aux->segment) {
+		memset(&helper_msg, 0, sizeof(helper_msg));
+		helper_msg.address = segment_address;
+		helper_msg.buffer = &aux->segment;
+		helper_msg.size = 1;
+		dp_aux_cmd_fifo_tx(aux, &helper_msg);
+	}
+
+	/*
+	 * Send the offset address for every i2c read in which the
+	 * middle-of-transaction flag is set. This will ensure that the sink
+	 * will update its read pointer and return the correct portion of the
+	 * EDID buffer in the subsequent i2c read trasntion triggered in the
+	 * native AUX transfer function.
+	 */
+	memset(&helper_msg, 0, sizeof(helper_msg));
+	helper_msg.address = input_msg->address;
+	helper_msg.buffer = &aux->offset;
+	helper_msg.size = 1;
+	dp_aux_cmd_fifo_tx(aux, &helper_msg);
+end:
+	aux->offset += message_size;
+	if (aux->offset == 0x80 || aux->offset == 0x100)
+		aux->segment = 0x0; /* reset segment at end of block */
+}
+
+static int dp_aux_transfer_ready(struct dp_aux_private *aux,
+		struct drm_dp_aux_msg *msg, bool send_seg)
+{
+	int ret = 0;
+	int const aux_cmd_native_max = 16;
+	int const aux_cmd_i2c_max = 128;
+	struct dp_aux *dp_aux = &aux->dp_aux;
+
+	if (atomic_read(&aux->aborted)) {
+		ret = -ETIMEDOUT;
+		goto error;
+	}
+
+	aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
+
+	/* Ignore address only message */
+	if ((msg->size == 0) || (msg->buffer == NULL)) {
+		msg->reply = aux->native ?
+			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+		goto error;
+	}
+
+	/* msg sanity check */
+	if ((aux->native && (msg->size > aux_cmd_native_max)) ||
+		(msg->size > aux_cmd_i2c_max)) {
+		DP_AUX_ERR(dp_aux, "%s: invalid msg: size(%zu), request(%x)\n",
+			__func__, msg->size, msg->request);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	dp_aux_update_offset_and_segment(aux, msg);
+
+	dp_aux_transfer_helper(aux, msg, send_seg);
+
+	aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+
+	if (aux->read) {
+		aux->no_send_addr = true;
+		aux->no_send_stop = false;
+	} else {
+		aux->no_send_addr = true;
+		aux->no_send_stop = true;
+	}
+
+	aux->cmd_busy = true;
+error:
+	return ret;
+}
+
+static inline bool dp_aux_is_sideband_msg(u32 address, size_t size)
+{
+	return (address >= 0x1000 && address + size < 0x1800) ||
+			(address >= 0x2000 && address + size < 0x2200);
+}
+
+/*
+ * This function does the real job to process an AUX transaction.
+ * It will call aux_reset() function to reset the AUX channel,
+ * if the waiting is timeout.
+ */
+static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
+		struct drm_dp_aux_msg *msg)
+{
+	ssize_t ret;
+	int const retry_count = 5;
+	struct dp_aux_private *aux = container_of(drm_aux,
+		struct dp_aux_private, drm_aux);
+
+	mutex_lock(&aux->mutex);
+
+	ret = dp_aux_transfer_ready(aux, msg, true);
+	if (ret)
+		goto unlock_exit;
+
+	if (!aux->cmd_busy) {
+		ret = msg->size;
+		goto unlock_exit;
+	}
+
+	ret = dp_aux_cmd_fifo_tx(aux, msg);
+	if ((ret < 0) && !atomic_read(&aux->aborted)) {
+		aux->retry_cnt++;
+		if (!(aux->retry_cnt % retry_count))
+			aux->catalog->update_aux_cfg(aux->catalog,
+				aux->cfg, PHY_AUX_CFG1);
+		aux->catalog->reset(aux->catalog);
+		goto unlock_exit;
+	} else if (ret < 0) {
+		goto unlock_exit;
+	}
+
+	if (aux->aux_error_num == DP_AUX_ERR_NONE) {
+		if (aux->read)
+			dp_aux_cmd_fifo_rx(aux, msg);
+
+		dp_aux_hex_dump(drm_aux, msg);
+
+		msg->reply = aux->native ?
+			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+	} else {
+		/* Reply defer to retry */
+		msg->reply = aux->native ?
+			DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
+	}
+
+	/* Return requested size for success or retry */
+	ret = msg->size;
+	aux->retry_cnt = 0;
+
+unlock_exit:
+	aux->cmd_busy = false;
+	mutex_unlock(&aux->mutex);
+	return ret;
+}
+
+static ssize_t dp_aux_bridge_transfer(struct drm_dp_aux *drm_aux,
+		struct drm_dp_aux_msg *msg)
+{
+	struct dp_aux_private *aux = container_of(drm_aux,
+			struct dp_aux_private, drm_aux);
+	ssize_t size;
+
+	if (aux->bridge_in_transfer) {
+		size = dp_aux_transfer(drm_aux, msg);
+	} else {
+		aux->bridge_in_transfer = true;
+		size = aux->aux_bridge->transfer(aux->aux_bridge,
+				drm_aux, msg);
+		aux->bridge_in_transfer = false;
+		dp_aux_hex_dump(drm_aux, msg);
+	}
+
+	return size;
+}
+
+static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
+		struct drm_dp_aux_msg *msg)
+{
+	struct dp_aux_private *aux = container_of(drm_aux,
+			struct dp_aux_private, drm_aux);
+	ssize_t size;
+	int aborted;
+
+	mutex_lock(&aux->mutex);
+	aborted = atomic_read(&aux->aborted);
+	mutex_unlock(&aux->mutex);
+	if (aborted) {
+		size = -ETIMEDOUT;
+		goto end;
+	}
+
+	if (aux->sim_in_transfer) {
+		if (aux->aux_bridge && aux->aux_bridge->transfer)
+			size = dp_aux_bridge_transfer(drm_aux, msg);
+		else
+			size = dp_aux_transfer(drm_aux, msg);
+	} else {
+		aux->sim_in_transfer = true;
+		size = aux->sim_bridge->transfer(aux->sim_bridge,
+				drm_aux, msg);
+		aux->sim_in_transfer = false;
+		dp_aux_hex_dump(drm_aux, msg);
+	}
+end:
+	return size;
+}
+
+static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg)
+{
+	int i = 0;
+
+	for (i = 0; i < PHY_AUX_CFG_MAX; i++)
+		aux_cfg[i].current_index = 0;
+}
+
+static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux || !aux_cfg) {
+		DP_AUX_ERR(dp_aux, "invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	if (aux->enabled)
+		return;
+
+	dp_aux_reset_phy_config_indices(aux_cfg);
+	aux->catalog->setup(aux->catalog, aux_cfg);
+	aux->catalog->reset(aux->catalog);
+	aux->catalog->enable(aux->catalog, true);
+	atomic_set(&aux->aborted, 0);
+	aux->retry_cnt = 0;
+	aux->enabled = true;
+}
+
+static void dp_aux_deinit(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		DP_AUX_ERR(dp_aux, "invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	if (!aux->enabled)
+		return;
+
+	atomic_set(&aux->aborted, 1);
+	aux->catalog->enable(aux->catalog, false);
+	aux->enabled = false;
+}
+
+static int dp_aux_register(struct dp_aux *dp_aux, struct drm_device *drm_dev)
+{
+	struct dp_aux_private *aux;
+	int ret = 0;
+
+	if (!dp_aux) {
+		DP_AUX_ERR(dp_aux, "invalid input\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	aux->drm_aux.name = "sde_dp_aux";
+	aux->drm_aux.dev = aux->dev;
+	aux->drm_aux.transfer = dp_aux_transfer;
+#if (KERNEL_VERSION(5, 15, 0) <= LINUX_VERSION_CODE)
+	aux->drm_aux.drm_dev = drm_dev;
+#endif
+	atomic_set(&aux->aborted, 1);
+	ret = drm_dp_aux_register(&aux->drm_aux);
+	if (ret) {
+		DP_AUX_ERR(dp_aux, "%s: failed to register drm aux: %d\n", __func__, ret);
+		goto exit;
+	}
+	dp_aux->drm_aux = &aux->drm_aux;
+
+	/* if bridge is defined, override transfer function */
+	if (aux->aux_bridge && aux->aux_bridge->transfer)
+		aux->drm_aux.transfer = dp_aux_bridge_transfer;
+exit:
+	return ret;
+}
+
+static void dp_aux_deregister(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		DP_AUX_ERR(dp_aux, "invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+	drm_dp_aux_unregister(&aux->drm_aux);
+}
+
+static void dp_aux_set_sim_mode(struct dp_aux *dp_aux,
+		struct dp_aux_bridge *sim_bridge)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		DP_AUX_ERR(dp_aux, "invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	mutex_lock(&aux->mutex);
+
+	aux->sim_bridge = sim_bridge;
+
+	if (sim_bridge) {
+		atomic_set(&aux->aborted, 0);
+		aux->drm_aux.transfer = dp_aux_transfer_debug;
+	} else if (aux->aux_bridge && aux->aux_bridge->transfer) {
+		aux->drm_aux.transfer = dp_aux_bridge_transfer;
+	} else {
+		aux->drm_aux.transfer = dp_aux_transfer;
+	}
+
+	mutex_unlock(&aux->mutex);
+}
+
+#if IS_ENABLED(CONFIG_QCOM_FSA4480_I2C)
+static int dp_aux_configure_fsa_switch(struct dp_aux *dp_aux,
+		bool enable, int orientation)
+{
+	struct dp_aux_private *aux;
+	int rc = 0;
+	enum fsa_function event = FSA_USBC_DISPLAYPORT_DISCONNECTED;
+
+	if (!dp_aux) {
+		DP_AUX_ERR(dp_aux, "invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	if (!aux->aux_switch_node) {
+		DP_AUX_DEBUG(dp_aux, "undefined fsa4480 handle\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (enable) {
+		switch (orientation) {
+		case ORIENTATION_CC1:
+			event = FSA_USBC_ORIENTATION_CC1;
+			break;
+		case ORIENTATION_CC2:
+			event = FSA_USBC_ORIENTATION_CC2;
+			break;
+		default:
+			DP_AUX_ERR(dp_aux, "invalid orientation\n");
+			rc = -EINVAL;
+			goto end;
+		}
+	}
+
+	DP_AUX_DEBUG(dp_aux, "enable=%d, orientation=%d, event=%d\n",
+			enable, orientation, event);
+
+	rc = fsa4480_switch_event(aux->aux_switch_node, event);
+
+	if (rc)
+		DP_AUX_ERR(dp_aux, "failed to configure fsa4480 i2c device (%d)\n", rc);
+end:
+	return rc;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_QCOM_WCD939X_I2C)
+static int dp_aux_configure_wcd_switch(struct dp_aux *dp_aux,
+		bool enable, int orientation)
+{
+	struct dp_aux_private *aux;
+	int rc = 0;
+	enum wcd_usbss_cable_status status = WCD_USBSS_CABLE_DISCONNECT;
+	enum wcd_usbss_cable_types event = WCD_USBSS_DP_AUX_CC1;
+
+	if (!dp_aux) {
+		DP_AUX_ERR(dp_aux, "invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	if (!aux->aux_switch_node) {
+		DP_AUX_DEBUG(dp_aux, "undefined wcd939x switch handle\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if ((aux->switch_enable == enable) && (aux->switch_orientation == orientation))
+		goto end;
+
+	if (enable) {
+		status = WCD_USBSS_CABLE_CONNECT;
+
+		switch (orientation) {
+		case ORIENTATION_CC1:
+			event = WCD_USBSS_DP_AUX_CC1;
+			break;
+		case ORIENTATION_CC2:
+			event = WCD_USBSS_DP_AUX_CC2;
+			break;
+		default:
+			DP_AUX_ERR(dp_aux, "invalid orientation\n");
+			rc = -EINVAL;
+			goto end;
+		}
+	}
+
+	DP_AUX_DEBUG(dp_aux, "enable=%d, orientation=%d, event=%d\n",
+			enable, orientation, event);
+
+	rc = wcd_usbss_switch_update(event, status);
+	if (rc) {
+		DP_AUX_ERR(dp_aux, "failed to configure wcd939x i2c device (%d)\n", rc);
+	} else {
+		aux->switch_enable = enable;
+		aux->switch_orientation = orientation;
+	}
+end:
+	return rc;
+}
+#endif
+
+struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
+		struct dp_parser *parser, struct device_node *aux_switch,
+		struct dp_aux_bridge *aux_bridge, void *ipc_log_context,
+		enum dp_aux_switch_type switch_type)
+{
+	int rc = 0;
+	struct dp_aux_private *aux;
+	struct dp_aux *dp_aux = NULL;
+
+	if (!catalog || !parser) {
+		DP_AUX_ERR(dp_aux, "invalid input\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
+	if (!aux) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	init_completion(&aux->comp);
+	aux->cmd_busy = false;
+	mutex_init(&aux->mutex);
+
+	aux->dev = dev;
+	aux->catalog = catalog;
+	aux->cfg = parser->aux_cfg;
+	aux->aux_switch_node = aux_switch;
+	aux->aux_bridge = aux_bridge;
+	dp_aux = &aux->dp_aux;
+	aux->retry_cnt = 0;
+	aux->switch_orientation = -1;
+
+	dp_aux->isr     = dp_aux_isr;
+	dp_aux->init    = dp_aux_init;
+	dp_aux->deinit  = dp_aux_deinit;
+	dp_aux->drm_aux_register = dp_aux_register;
+	dp_aux->drm_aux_deregister = dp_aux_deregister;
+	dp_aux->reconfig = dp_aux_reconfig;
+	dp_aux->abort = dp_aux_abort_transaction;
+	dp_aux->set_sim_mode = dp_aux_set_sim_mode;
+	dp_aux->ipc_log_context = ipc_log_context;
+
+	/*Condition to avoid allocating function pointers for aux bypass mode*/
+	if (switch_type != DP_AUX_SWITCH_BYPASS) {
+#if IS_ENABLED(CONFIG_QCOM_FSA4480_I2C)
+		if (switch_type == DP_AUX_SWITCH_FSA4480) {
+			dp_aux->switch_configure = dp_aux_configure_fsa_switch;
+			dp_aux->switch_register_notifier = fsa4480_reg_notifier;
+			dp_aux->switch_unregister_notifier = fsa4480_unreg_notifier;
+		}
+#endif
+#if IS_ENABLED(CONFIG_QCOM_WCD939X_I2C)
+		if (switch_type == DP_AUX_SWITCH_WCD939x) {
+			dp_aux->switch_configure = dp_aux_configure_wcd_switch;
+			dp_aux->switch_register_notifier = wcd_usbss_reg_notifier;
+			dp_aux->switch_unregister_notifier = wcd_usbss_unreg_notifier;
+		}
+#endif
+	}
+
+	return dp_aux;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_aux_put(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux)
+		return;
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	mutex_destroy(&aux->mutex);
+
+	devm_kfree(aux->dev, aux);
+}

+ 77 - 0
qcom/opensource/display-drivers/msm/dp/dp_aux.h

@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_AUX_H_
+#define _DP_AUX_H_
+
+#include "dp_catalog.h"
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0))
+#include <drm/display/drm_dp_helper.h>
+#else
+#include <drm/drm_dp_helper.h>
+#endif
+#include "dp_aux_bridge.h"
+
+#define DP_STATE_NOTIFICATION_SENT          BIT(0)
+#define DP_STATE_TRAIN_1_STARTED            BIT(1)
+#define DP_STATE_TRAIN_1_SUCCEEDED          BIT(2)
+#define DP_STATE_TRAIN_1_FAILED             BIT(3)
+#define DP_STATE_TRAIN_2_STARTED            BIT(4)
+#define DP_STATE_TRAIN_2_SUCCEEDED          BIT(5)
+#define DP_STATE_TRAIN_2_FAILED             BIT(6)
+#define DP_STATE_CTRL_POWERED_ON            BIT(7)
+#define DP_STATE_CTRL_POWERED_OFF           BIT(8)
+#define DP_STATE_LINK_MAINTENANCE_STARTED   BIT(9)
+#define DP_STATE_LINK_MAINTENANCE_COMPLETED BIT(10)
+#define DP_STATE_LINK_MAINTENANCE_FAILED    BIT(11)
+#define DP_STATE_AUX_TIMEOUT                BIT(12)
+#define DP_STATE_PLL_LOCKED                 BIT(13)
+
+enum dp_aux_switch_type {
+	DP_AUX_SWITCH_BYPASS,
+	DP_AUX_SWITCH_FSA4480,
+	DP_AUX_SWITCH_WCD939x,
+};
+
+enum dp_aux_error {
+	DP_AUX_ERR_NONE	= 0,
+	DP_AUX_ERR_ADDR	= -1,
+	DP_AUX_ERR_TOUT	= -2,
+	DP_AUX_ERR_NACK	= -3,
+	DP_AUX_ERR_DEFER	= -4,
+	DP_AUX_ERR_NACK_DEFER	= -5,
+	DP_AUX_ERR_PHY	= -6,
+};
+
+struct dp_aux {
+	u32 state;
+
+	bool read;
+
+	struct mutex *access_lock;
+	void *ipc_log_context;
+
+	struct drm_dp_aux *drm_aux;
+	int (*drm_aux_register)(struct dp_aux *aux, struct drm_device *drm_dev);
+	void (*drm_aux_deregister)(struct dp_aux *aux);
+	void (*isr)(struct dp_aux *aux);
+	void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg);
+	void (*deinit)(struct dp_aux *aux);
+	void (*reconfig)(struct dp_aux *aux);
+	void (*abort)(struct dp_aux *aux, bool abort);
+	void (*set_sim_mode)(struct dp_aux *aux, struct dp_aux_bridge *sim_bridge);
+	int (*switch_configure)(struct dp_aux *aux, bool enable, int orientation);
+	int (*switch_register_notifier)(struct notifier_block *nb, struct device_node *node);
+	int (*switch_unregister_notifier)(struct notifier_block *nb, struct device_node *node);
+};
+
+struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
+		struct dp_parser *parser, struct device_node *aux_switch,
+		struct dp_aux_bridge *aux_bridge, void *ipc_log_context,
+		enum dp_aux_switch_type switch_type);
+void dp_aux_put(struct dp_aux *aux);
+
+#endif /*__DP_AUX_H_*/

+ 71 - 0
qcom/opensource/display-drivers/msm/dp/dp_aux_bridge.c

@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd
+ *
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. 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.
+ */
+
+#include "dp_aux_bridge.h"
+
+static DEFINE_MUTEX(dp_aux_bridge_lock);
+static LIST_HEAD(du_aux_bridge_list);
+
+int dp_aux_add_bridge(struct dp_aux_bridge *bridge)
+{
+	mutex_lock(&dp_aux_bridge_lock);
+	list_add_tail(&bridge->head, &du_aux_bridge_list);
+	mutex_unlock(&dp_aux_bridge_lock);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+struct dp_aux_bridge *of_dp_aux_find_bridge(struct device_node *np)
+{
+	struct dp_aux_bridge *bridge;
+
+	mutex_lock(&dp_aux_bridge_lock);
+
+	list_for_each_entry(bridge, &du_aux_bridge_list, head) {
+		if (bridge->of_node == np) {
+			mutex_unlock(&dp_aux_bridge_lock);
+			return bridge;
+		}
+	}
+
+	mutex_unlock(&dp_aux_bridge_lock);
+	return NULL;
+}
+#endif /* CONFIG_OF */
+

+ 135 - 0
qcom/opensource/display-drivers/msm/dp/dp_aux_bridge.h

@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * 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 _DP_AUX_BRIDGE_H_
+#define _DP_AUX_BRIDGE_H_
+
+#include <linux/version.h>
+#include <linux/types.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0))
+#include <drm/display/drm_dp_helper.h>
+#else
+#include <drm/drm_dp_helper.h>
+#endif
+
+/**
+ * enum dp_aux_bridge_flag - DP aux bridge capability flag
+ * DP_AUX_BRIDGE_HPD: HPD will be generated by DP aux bridge
+ * DP_AUX_BRIDGE_MST: MST simulator is used by DP aux bridge
+ */
+enum dp_aux_bridge_flag {
+	DP_AUX_BRIDGE_HPD = (1 << 0),
+	DP_AUX_BRIDGE_MST = (1 << 1),
+};
+
+/**
+ * struct dp_aux_bridge - DP aux bridge control structure
+ * @of_node: device node pointer to the bridge
+ * @dev_priv: pointer to the bridge driver's internal context
+ * @flag: flag for capability
+ * @mst_ctx: pointer to mst context when DP_AUX_BRIDGE_MST is set
+ * @head: to keep track of all added bridges
+ */
+struct dp_aux_bridge {
+#if IS_ENABLED(CONFIG_OF)
+	struct device_node *of_node;
+#endif /* CONFIG_OF */
+	void *dev_priv;
+	u32 flag;
+	void *mst_ctx;
+	struct list_head head;
+
+	/**
+	 * @register_hpd:
+	 *
+	 * This callback is invoked whenever bridge is registered
+	 * for HPD handling
+	 *
+	 * The attach callback is optional.
+	 *
+	 * Host will pass HPD callback handle to bridge, with
+	 * arguments @hpd_cb(void* dev, bool hpd, bool hpd_irq):
+	 *
+	 *     @dev: private handle passed in register_hpd
+	 *     @hpd: true if HPD is high, false if HPD is low
+	 *     @hpd_irq: true if this is a HPD irq. @hpd will be
+	 *     ignored when hpd_irq is true.
+	 *
+	 * RETURNS:
+	 *
+	 * Zero on success, error code on failure.
+	 */
+	int (*register_hpd)(struct dp_aux_bridge *bridge,
+			int (*hpd_cb)(void *, bool, bool), void *dev);
+
+	/**
+	 * @transfer:
+	 *
+	 * This callback is invoked whenever dp_aux transfer
+	 * is called from host. Inside @transfer bridge can still
+	 * call @drm_aux->transfer to trigger the actual
+	 * DPCD/I2C transfer at host side.
+	 *
+	 * The attach callback is optional.
+	 *
+	 * RETURNS:
+	 *
+	 * Size of the bytes transferred, error code on failure.
+	 */
+	ssize_t (*transfer)(struct dp_aux_bridge *bridge,
+			struct drm_dp_aux *drm_aux,
+			struct drm_dp_aux_msg *msg);
+};
+
+/**
+ * dp_aux_add_bridge - Register DP aux bridge
+ * @bridge: bridge pointer
+ * return: 0 if successful
+ */
+int dp_aux_add_bridge(struct dp_aux_bridge *bridge);
+
+#if IS_ENABLED(CONFIG_OF)
+/**
+ * of_dp_aux_find_bridge - Find registered DP aux bridge
+ * @np: device node pointer to the bridge
+ * return: DP aux bridge pointer, NULL if not found
+ */
+struct dp_aux_bridge *of_dp_aux_find_bridge(struct device_node *np);
+#endif /* CONFIG_OF */
+
+#endif /* _DP_AUX_BRIDGE_H_ */
+

+ 208 - 0
qcom/opensource/display-drivers/msm/dp/dp_bridge_hpd.c

@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include "dp_bridge_hpd.h"
+
+struct dp_bridge_hpd_private {
+	struct device *dev;
+	struct dp_hpd base;
+	struct dp_aux_bridge *bridge;
+	struct delayed_work work;
+	struct dp_hpd_cb *cb;
+	bool hpd;
+	bool hpd_irq;
+	struct mutex hpd_lock;
+};
+
+static int dp_bridge_hpd_connect(struct dp_bridge_hpd_private *bridge_hpd,
+		bool hpd)
+{
+	int rc = 0;
+
+	if (!bridge_hpd) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	bridge_hpd->base.hpd_high = hpd;
+	bridge_hpd->base.alt_mode_cfg_done = hpd;
+	bridge_hpd->base.hpd_irq = false;
+
+	if (!bridge_hpd->cb ||
+		!bridge_hpd->cb->configure ||
+		!bridge_hpd->cb->disconnect) {
+		pr_err("invalid cb\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (hpd)
+		rc = bridge_hpd->cb->configure(bridge_hpd->dev);
+	else
+		rc = bridge_hpd->cb->disconnect(bridge_hpd->dev);
+
+error:
+	return rc;
+}
+
+static int dp_bridge_hpd_attention(struct dp_bridge_hpd_private *bridge_hpd)
+{
+	int rc = 0;
+
+	if (!bridge_hpd) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	bridge_hpd->base.hpd_irq = true;
+
+	if (bridge_hpd->cb && bridge_hpd->cb->attention)
+		rc = bridge_hpd->cb->attention(bridge_hpd->dev);
+
+error:
+	return rc;
+}
+
+static void dp_bridge_hpd_work(struct work_struct *work)
+{
+	struct delayed_work *dw = to_delayed_work(work);
+	struct dp_bridge_hpd_private *bridge_hpd = container_of(dw,
+		struct dp_bridge_hpd_private, work);
+
+	mutex_lock(&bridge_hpd->hpd_lock);
+
+	if (bridge_hpd->hpd_irq)
+		dp_bridge_hpd_attention(bridge_hpd);
+	else
+		dp_bridge_hpd_connect(bridge_hpd, bridge_hpd->hpd);
+
+	mutex_unlock(&bridge_hpd->hpd_lock);
+}
+
+static int dp_bridge_hpd_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
+{
+	int rc = 0;
+	struct dp_bridge_hpd_private *bridge_hpd;
+
+	if (!dp_hpd) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	bridge_hpd = container_of(dp_hpd, struct dp_bridge_hpd_private, base);
+
+	dp_bridge_hpd_connect(bridge_hpd, hpd);
+error:
+	return rc;
+}
+
+static int dp_bridge_hpd_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
+{
+	int rc = 0;
+	struct dp_bridge_hpd_private *bridge_hpd;
+
+	if (!dp_hpd) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	bridge_hpd = container_of(dp_hpd, struct dp_bridge_hpd_private, base);
+
+	dp_bridge_hpd_attention(bridge_hpd);
+error:
+	return rc;
+}
+
+static int dp_bridge_hpd_cb(void *dp_hpd, bool hpd, bool hpd_irq)
+{
+	struct dp_bridge_hpd_private *bridge_hpd = dp_hpd;
+
+	mutex_lock(&bridge_hpd->hpd_lock);
+
+	bridge_hpd->hpd = hpd;
+	bridge_hpd->hpd_irq = hpd_irq;
+	queue_delayed_work(system_wq, &bridge_hpd->work, 0);
+
+	mutex_unlock(&bridge_hpd->hpd_lock);
+
+	return 0;
+}
+
+static int dp_bridge_hpd_register(struct dp_hpd *dp_hpd)
+{
+	struct dp_bridge_hpd_private *bridge_hpd;
+
+	if (!dp_hpd)
+		return -EINVAL;
+
+	bridge_hpd = container_of(dp_hpd, struct dp_bridge_hpd_private, base);
+
+	return bridge_hpd->bridge->register_hpd(bridge_hpd->bridge,
+			dp_bridge_hpd_cb, bridge_hpd);
+}
+
+struct dp_hpd *dp_bridge_hpd_get(struct device *dev,
+	struct dp_hpd_cb *cb, struct dp_aux_bridge *aux_bridge)
+{
+	int rc = 0;
+	struct dp_bridge_hpd_private *bridge_hpd;
+
+	if (!dev || !cb) {
+		pr_err("invalid device\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	bridge_hpd = devm_kzalloc(dev, sizeof(*bridge_hpd), GFP_KERNEL);
+	if (!bridge_hpd) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	bridge_hpd->dev = dev;
+	bridge_hpd->cb = cb;
+	bridge_hpd->bridge = aux_bridge;
+	mutex_init(&bridge_hpd->hpd_lock);
+	INIT_DELAYED_WORK(&bridge_hpd->work, dp_bridge_hpd_work);
+	bridge_hpd->base.simulate_connect = dp_bridge_hpd_simulate_connect;
+	bridge_hpd->base.simulate_attention = dp_bridge_hpd_simulate_attention;
+	bridge_hpd->base.register_hpd = dp_bridge_hpd_register;
+
+	return &bridge_hpd->base;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_bridge_hpd_put(struct dp_hpd *dp_hpd)
+{
+	struct dp_bridge_hpd_private *bridge_hpd;
+
+	if (!dp_hpd)
+		return;
+
+	bridge_hpd = container_of(dp_hpd, struct dp_bridge_hpd_private, base);
+
+	devm_kfree(bridge_hpd->dev, bridge_hpd);
+}

+ 42 - 0
qcom/opensource/display-drivers/msm/dp/dp_bridge_hpd.h

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_BRIDGE_HPD_H_
+#define _DP_BRIDGE_HPD_H_
+
+#include "dp_hpd.h"
+
+/**
+ * dp_bridge_hpd_get() - configure and get the DisplayPlot HPD module data
+ *
+ * @dev: device instance of the caller
+ * @cb: callback function for HPD response
+ * @aux_bridge: handle for aux_bridge driver data
+ * return: pointer to allocated gpio hpd module data
+ *
+ * This function sets up the gpio hpd module
+ */
+struct dp_hpd *dp_bridge_hpd_get(struct device *dev,
+	struct dp_hpd_cb *cb, struct dp_aux_bridge *aux_bridge);
+
+/**
+ * dp_bridge_hpd_put()
+ *
+ * Cleans up dp_hpd instance
+ *
+ * @hpd: instance of gpio_hpd
+ */
+void dp_bridge_hpd_put(struct dp_hpd *hpd);
+
+#endif /* _DP_BRIDGE_HPD_H_ */

+ 3130 - 0
qcom/opensource/display-drivers/msm/dp/dp_catalog.c

@@ -0,0 +1,3130 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+#include "dp_catalog.h"
+#include "dp_reg.h"
+#include "dp_debug.h"
+#include "dp_link.h"
+
+#define DP_GET_MSB(x)	(x >> 8)
+#define DP_GET_LSB(x)	(x & 0xff)
+
+#define DP_PHY_READY BIT(1)
+
+#define dp_catalog_get_priv(x) ({ \
+	struct dp_catalog *dp_catalog; \
+	dp_catalog = container_of(x, struct dp_catalog, x); \
+	container_of(dp_catalog, struct dp_catalog_private, \
+				dp_catalog); \
+})
+
+#define DP_INTERRUPT_STATUS1 \
+	(DP_INTR_AUX_I2C_DONE| \
+	DP_INTR_WRONG_ADDR | DP_INTR_TIMEOUT | \
+	DP_INTR_NACK_DEFER | DP_INTR_WRONG_DATA_CNT | \
+	DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER | \
+	DP_INTR_PLL_UNLOCKED | DP_INTR_AUX_ERROR)
+
+#define DP_INTR_MASK1		(DP_INTERRUPT_STATUS1 << 2)
+
+#define DP_INTERRUPT_STATUS2 \
+	(DP_INTR_READY_FOR_VIDEO | DP_INTR_IDLE_PATTERN_SENT | \
+	DP_INTR_FRAME_END | DP_INTR_CRC_UPDATED | DP_INTR_SST_FIFO_UNDERFLOW)
+
+#define DP_INTR_MASK2		(DP_INTERRUPT_STATUS2 << 2)
+
+
+#define DP_INTERRUPT_STATUS3 \
+	(DP_INTR_SST_ML_FIFO_OVERFLOW | DP_INTR_MST0_ML_FIFO_OVERFLOW | \
+	DP_INTR_MST1_ML_FIFO_OVERFLOW | DP_INTR_DP1_FRAME_END | DP_INTR_SDP0_COLLISION | \
+	DP_INTR_SDP1_COLLISION)
+
+#define DP_INTR_MASK3		(DP_INTERRUPT_STATUS3 << 2)
+
+#define DP_INTERRUPT_STATUS5 \
+	(DP_INTR_MST_DP0_VCPF_SENT | DP_INTR_MST_DP1_VCPF_SENT)
+
+#define DP_INTR_MASK5		(DP_INTERRUPT_STATUS5 << 2)
+#define DP_TPG_PATTERN_MAX	9
+#define DP_TPG_PATTERN_DEFAULT	8
+
+#define DP_INTERRUPT_STATUS6 \
+	(DP_INTR_SST_BS_LATE | DP_INTR_DP0_BACKPRESSURE_ERROR | DP_INTR_DP1_BACKPRESSURE_ERROR)
+
+#define DP_INTR_MASK6		(DP_INTERRUPT_STATUS6 << 2)
+
+#define dp_catalog_fill_io(x) { \
+	catalog->io.x = parser->get_io(parser, #x); \
+}
+
+#define dp_catalog_fill_io_buf(x) { \
+	parser->get_io_buf(parser, #x); \
+}
+
+#define dp_read(x) ({ \
+	catalog->read(catalog, io_data, x); \
+})
+
+#define dp_write(x, y) ({ \
+	catalog->write(catalog, io_data, x, y); \
+})
+
+static u8 const vm_pre_emphasis[4][4] = {
+	{0x00, 0x0B, 0x12, 0xFF},       /* pe0, 0 db */
+	{0x00, 0x0A, 0x12, 0xFF},       /* pe1, 3.5 db */
+	{0x00, 0x0C, 0xFF, 0xFF},       /* pe2, 6.0 db */
+	{0xFF, 0xFF, 0xFF, 0xFF}        /* pe3, 9.5 db */
+};
+
+/* voltage swing, 0.2v and 1.0v are not support */
+static u8 const vm_voltage_swing[4][4] = {
+	{0x07, 0x0F, 0x14, 0xFF}, /* sw0, 0.4v  */
+	{0x11, 0x1D, 0x1F, 0xFF}, /* sw1, 0.6 v */
+	{0x18, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */
+	{0xFF, 0xFF, 0xFF, 0xFF}  /* sw1, 1.2 v, optional */
+};
+
+static u8 const vm_pre_emphasis_hbr3_hbr2[4][4] = {
+	{0x00, 0x0C, 0x15, 0x1A},
+	{0x02, 0x0E, 0x16, 0xFF},
+	{0x02, 0x11, 0xFF, 0xFF},
+	{0x04, 0xFF, 0xFF, 0xFF}
+};
+
+static u8 const vm_voltage_swing_hbr3_hbr2[4][4] = {
+	{0x02, 0x12, 0x16, 0x1A},
+	{0x09, 0x19, 0x1F, 0xFF},
+	{0x10, 0x1F, 0xFF, 0xFF},
+	{0x1F, 0xFF, 0xFF, 0xFF}
+};
+
+static u8 const vm_pre_emphasis_hbr_rbr[4][4] = {
+	{0x00, 0x0C, 0x14, 0x19},
+	{0x00, 0x0B, 0x12, 0xFF},
+	{0x00, 0x0B, 0xFF, 0xFF},
+	{0x04, 0xFF, 0xFF, 0xFF}
+};
+
+static u8 const vm_voltage_swing_hbr_rbr[4][4] = {
+	{0x08, 0x0F, 0x16, 0x1F},
+	{0x11, 0x1E, 0x1F, 0xFF},
+	{0x19, 0x1F, 0xFF, 0xFF},
+	{0x1F, 0xFF, 0xFF, 0xFF}
+};
+
+enum dp_flush_bit {
+	DP_PPS_FLUSH,
+	DP_DHDR_FLUSH,
+};
+
+/* audio related catalog functions */
+struct dp_catalog_private {
+	struct device *dev;
+	struct dp_catalog_io io;
+	struct dp_parser *parser;
+
+	u32 (*read)(struct dp_catalog_private *catalog,
+		struct dp_io_data *io_data, u32 offset);
+	void (*write)(struct dp_catalog_private *catlog,
+		struct dp_io_data *io_data, u32 offset, u32 data);
+
+	u32 (*audio_map)[DP_AUDIO_SDP_HEADER_MAX];
+	struct dp_catalog dp_catalog;
+
+	char exe_mode[SZ_4];
+	u32 dp_core_version;
+	u32 dp_phy_version;
+};
+
+static u32 dp_read_sw(struct dp_catalog_private *catalog,
+		struct dp_io_data *io_data, u32 offset)
+{
+	u32 data = 0;
+
+	if (io_data->buf)
+		memcpy(&data, io_data->buf + offset, sizeof(offset));
+
+	return data;
+}
+
+static void dp_write_sw(struct dp_catalog_private *catalog,
+	struct dp_io_data *io_data, u32 offset, u32 data)
+{
+	if (io_data->buf)
+		memcpy(io_data->buf + offset, &data, sizeof(data));
+}
+
+static u32 dp_read_hw(struct dp_catalog_private *catalog,
+	struct dp_io_data *io_data, u32 offset)
+{
+	u32 data = 0;
+
+	data = readl_relaxed(io_data->io.base + offset);
+
+	return data;
+}
+
+static void dp_write_hw(struct dp_catalog_private *catalog,
+	struct dp_io_data *io_data, u32 offset, u32 data)
+{
+	writel_relaxed(data, io_data->io.base + offset);
+}
+
+static u32 dp_read_sub_sw(struct dp_catalog *dp_catalog,
+		struct dp_io_data *io_data, u32 offset)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+			struct dp_catalog_private, dp_catalog);
+
+	return dp_read_sw(catalog, io_data, offset);
+}
+
+static void dp_write_sub_sw(struct dp_catalog *dp_catalog,
+	struct dp_io_data *io_data, u32 offset, u32 data)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+			struct dp_catalog_private, dp_catalog);
+
+	dp_write_sw(catalog, io_data, offset, data);
+}
+
+static u32 dp_read_sub_hw(struct dp_catalog *dp_catalog,
+	struct dp_io_data *io_data, u32 offset)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+			struct dp_catalog_private, dp_catalog);
+
+	return dp_read_hw(catalog, io_data, offset);
+}
+
+static void dp_write_sub_hw(struct dp_catalog *dp_catalog,
+	struct dp_io_data *io_data, u32 offset, u32 data)
+{
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+			struct dp_catalog_private, dp_catalog);
+
+	dp_write_hw(catalog, io_data, offset, data);
+}
+
+/* aux related catalog functions */
+static u32 dp_catalog_aux_read_data(struct dp_catalog_aux *aux)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!aux) {
+		DP_ERR("invalid input\n");
+		goto end;
+	}
+
+	catalog = dp_catalog_get_priv(aux);
+	io_data = catalog->io.dp_aux;
+
+	return dp_read(DP_AUX_DATA);
+end:
+	return 0;
+}
+
+static int dp_catalog_aux_write_data(struct dp_catalog_aux *aux)
+{
+	int rc = 0;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!aux) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	catalog = dp_catalog_get_priv(aux);
+	io_data = catalog->io.dp_aux;
+
+	dp_write(DP_AUX_DATA, aux->data);
+end:
+	return rc;
+}
+
+static int dp_catalog_aux_write_trans(struct dp_catalog_aux *aux)
+{
+	int rc = 0;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!aux) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	catalog = dp_catalog_get_priv(aux);
+	io_data = catalog->io.dp_aux;
+
+	dp_write(DP_AUX_TRANS_CTRL, aux->data);
+end:
+	return rc;
+}
+
+static int dp_catalog_aux_clear_trans(struct dp_catalog_aux *aux, bool read)
+{
+	int rc = 0;
+	u32 data = 0;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!aux) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	catalog = dp_catalog_get_priv(aux);
+	io_data = catalog->io.dp_aux;
+
+	if (read) {
+		data = dp_read(DP_AUX_TRANS_CTRL);
+		data &= ~BIT(9);
+		dp_write(DP_AUX_TRANS_CTRL, data);
+	} else {
+		dp_write(DP_AUX_TRANS_CTRL, 0);
+	}
+end:
+	return rc;
+}
+
+static void dp_catalog_aux_clear_hw_interrupts(struct dp_catalog_aux *aux)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 data = 0;
+
+	if (!aux) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(aux);
+	io_data = catalog->io.dp_phy;
+
+	data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS);
+
+	dp_write(DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f);
+	wmb(); /* make sure 0x1f is written before next write */
+	dp_write(DP_PHY_AUX_INTERRUPT_CLEAR, 0x9f);
+	wmb(); /* make sure 0x9f is written before next write */
+	dp_write(DP_PHY_AUX_INTERRUPT_CLEAR, 0);
+	wmb(); /* make sure register is cleared */
+}
+
+static void dp_catalog_aux_reset(struct dp_catalog_aux *aux)
+{
+	u32 aux_ctrl;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!aux) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(aux);
+	io_data = catalog->io.dp_aux;
+
+	aux_ctrl = dp_read(DP_AUX_CTRL);
+
+	aux_ctrl |= BIT(1);
+	dp_write(DP_AUX_CTRL, aux_ctrl);
+	usleep_range(1000, 1010); /* h/w recommended delay */
+
+	aux_ctrl &= ~BIT(1);
+
+	dp_write(DP_AUX_CTRL, aux_ctrl);
+	wmb(); /* make sure AUX reset is done here */
+}
+
+static void dp_catalog_aux_enable(struct dp_catalog_aux *aux, bool enable)
+{
+	u32 aux_ctrl;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!aux) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(aux);
+	io_data = catalog->io.dp_aux;
+
+	aux_ctrl = dp_read(DP_AUX_CTRL);
+
+	if (enable) {
+		aux_ctrl |= BIT(0);
+		dp_write(DP_AUX_CTRL, aux_ctrl);
+		wmb(); /* make sure AUX module is enabled */
+
+		dp_write(DP_TIMEOUT_COUNT, 0xffff);
+		dp_write(DP_AUX_LIMITS, 0xffff);
+	} else {
+		aux_ctrl &= ~BIT(0);
+		dp_write(DP_AUX_CTRL, aux_ctrl);
+	}
+}
+
+static void dp_catalog_aux_update_cfg(struct dp_catalog_aux *aux,
+		struct dp_aux_cfg *cfg, enum dp_phy_aux_config_type type)
+{
+	struct dp_catalog_private *catalog;
+	u32 new_index = 0, current_index = 0;
+	struct dp_io_data *io_data;
+
+	if (!aux || !cfg || (type >= PHY_AUX_CFG_MAX)) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(aux);
+
+	io_data = catalog->io.dp_phy;
+
+	current_index = cfg[type].current_index;
+	new_index = (current_index + 1) % cfg[type].cfg_cnt;
+	DP_DEBUG("Updating %s from 0x%08x to 0x%08x\n",
+		dp_phy_aux_config_type_to_string(type),
+	cfg[type].lut[current_index], cfg[type].lut[new_index]);
+
+	dp_write(cfg[type].offset, cfg[type].lut[new_index]);
+	cfg[type].current_index = new_index;
+}
+
+static void dp_catalog_aux_setup(struct dp_catalog_aux *aux,
+		struct dp_aux_cfg *cfg)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	int i = 0;
+
+	if (!aux || !cfg) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(aux);
+
+	io_data = catalog->io.dp_phy;
+	dp_write(DP_PHY_PD_CTL, 0x65);
+	wmb(); /* make sure PD programming happened */
+
+	/* Turn on BIAS current for PHY/PLL */
+	io_data = catalog->io.dp_pll;
+	dp_write(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1b);
+
+	io_data = catalog->io.dp_phy;
+	dp_write(DP_PHY_PD_CTL, 0x02);
+	wmb(); /* make sure PD programming happened */
+	dp_write(DP_PHY_PD_CTL, 0x7d);
+
+	/* Turn on BIAS current for PHY/PLL */
+	io_data = catalog->io.dp_pll;
+	dp_write(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3f);
+
+	/* DP AUX CFG register programming */
+	io_data = catalog->io.dp_phy;
+	for (i = 0; i < PHY_AUX_CFG_MAX; i++)
+		dp_write(cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
+
+	dp_write(DP_PHY_AUX_INTERRUPT_MASK, 0x1F);
+	wmb(); /* make sure AUX configuration is done before enabling it */
+}
+
+static void dp_catalog_aux_get_irq(struct dp_catalog_aux *aux, bool cmd_busy)
+{
+	u32 ack;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!aux) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(aux);
+	io_data = catalog->io.dp_ahb;
+
+	aux->isr = dp_read(DP_INTR_STATUS);
+	aux->isr &= ~DP_INTR_MASK1;
+	ack = aux->isr & DP_INTERRUPT_STATUS1;
+	ack <<= 1;
+	ack |= DP_INTR_MASK1;
+	dp_write(DP_INTR_STATUS, ack);
+}
+
+static bool dp_catalog_ctrl_wait_for_phy_ready(
+		struct dp_catalog_private *catalog)
+{
+	u32 phy_version;
+	u32 reg, state;
+	void __iomem *base = catalog->io.dp_phy->io.base;
+	bool success = true;
+	u32 const poll_sleep_us = 500;
+	u32 const pll_timeout_us = 10000;
+
+	phy_version = dp_catalog_get_dp_phy_version(&catalog->dp_catalog);
+	if (phy_version >= 0x60000000) {
+		reg = DP_PHY_STATUS_V600;
+	} else {
+		reg = DP_PHY_STATUS;
+	}
+
+	if (readl_poll_timeout_atomic((base + reg), state,
+			((state & DP_PHY_READY) > 0),
+			poll_sleep_us, pll_timeout_us)) {
+		DP_ERR("PHY status failed, status=%x\n", state);
+
+		success = false;
+	}
+
+	return success;
+}
+
+/* controller related catalog functions */
+static int dp_catalog_ctrl_late_phy_init(struct dp_catalog_ctrl *ctrl,
+					u8 lane_cnt, bool flipped)
+{
+	int rc = 0;
+	u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	switch (lane_cnt) {
+	case 1:
+		drvr0_en = flipped ? 0x13 : 0x10;
+		bias0_en = flipped ? 0x3E : 0x15;
+		drvr1_en = flipped ? 0x10 : 0x13;
+		bias1_en = flipped ? 0x15 : 0x3E;
+		break;
+	case 2:
+		drvr0_en = flipped ? 0x10 : 0x10;
+		bias0_en = flipped ? 0x3F : 0x15;
+		drvr1_en = flipped ? 0x10 : 0x10;
+		bias1_en = flipped ? 0x15 : 0x3F;
+		break;
+	case 4:
+	default:
+		drvr0_en = 0x10;
+		bias0_en = 0x3F;
+		drvr1_en = 0x10;
+		bias1_en = 0x3F;
+		break;
+	}
+
+	io_data = catalog->io.dp_ln_tx0;
+	dp_write(TXn_HIGHZ_DRVR_EN_V420, drvr0_en);
+	dp_write(TXn_TRANSCEIVER_BIAS_EN_V420, bias0_en);
+
+	io_data = catalog->io.dp_ln_tx1;
+	dp_write(TXn_HIGHZ_DRVR_EN_V420, drvr1_en);
+	dp_write(TXn_TRANSCEIVER_BIAS_EN_V420, bias1_en);
+
+	io_data = catalog->io.dp_phy;
+	dp_write(DP_PHY_CFG, 0x18);
+	/* add hardware recommended delay */
+	udelay(2000);
+	dp_write(DP_PHY_CFG, 0x19);
+
+	/*
+	 * Make sure all the register writes are completed before
+	 * doing any other operation
+	 */
+	wmb();
+
+	if (!dp_catalog_ctrl_wait_for_phy_ready(catalog)) {
+		rc = -EINVAL;
+		goto lock_err;
+	}
+
+	io_data = catalog->io.dp_ln_tx0;
+	dp_write(TXn_TX_POL_INV_V420, 0x0a);
+	io_data = catalog->io.dp_ln_tx1;
+	dp_write(TXn_TX_POL_INV_V420, 0x0a);
+
+	io_data = catalog->io.dp_ln_tx0;
+	dp_write(TXn_TX_DRV_LVL_V420, 0x27);
+	io_data = catalog->io.dp_ln_tx1;
+	dp_write(TXn_TX_DRV_LVL_V420, 0x27);
+
+	io_data = catalog->io.dp_ln_tx0;
+	dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
+	io_data = catalog->io.dp_ln_tx1;
+	dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
+	/* Make sure the PHY register writes are done */
+	wmb();
+lock_err:
+	return rc;
+}
+
+static u32 dp_catalog_ctrl_read_hdcp_status(struct dp_catalog_ctrl *ctrl)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_ahb;
+
+	return dp_read(DP_HDCP_STATUS);
+}
+
+static void dp_catalog_panel_sdp_update(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 sdp_cfg3_off = 0;
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	if (panel->stream_id == DP_STREAM_1)
+		sdp_cfg3_off = MMSS_DP1_SDP_CFG3 - MMSS_DP_SDP_CFG3;
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	dp_write(MMSS_DP_SDP_CFG3 + sdp_cfg3_off, 0x01);
+	dp_write(MMSS_DP_SDP_CFG3 + sdp_cfg3_off, 0x00);
+}
+
+static void dp_catalog_panel_setup_vsif_infoframe_sdp(
+		struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct drm_msm_ext_hdr_metadata *hdr;
+	struct dp_io_data *io_data;
+	u32 header, parity, data, mst_offset = 0;
+	u8 buf[SZ_64], off = 0;
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	if (panel->stream_id == DP_STREAM_1)
+		mst_offset = MMSS_DP1_VSCEXT_0 - MMSS_DP_VSCEXT_0;
+
+	catalog = dp_catalog_get_priv(panel);
+	hdr = &panel->hdr_meta;
+	io_data = catalog->io.dp_link;
+
+	/* HEADER BYTE 1 */
+	header = panel->dhdr_vsif_sdp.HB1;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_1_BIT)
+			| (parity << PARITY_BYTE_1_BIT));
+	dp_write(MMSS_DP_VSCEXT_0 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	/* HEADER BYTE 2 */
+	header = panel->dhdr_vsif_sdp.HB2;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_2_BIT)
+			| (parity << PARITY_BYTE_2_BIT));
+	dp_write(MMSS_DP_VSCEXT_1 + mst_offset, data);
+
+	/* HEADER BYTE 3 */
+	header = panel->dhdr_vsif_sdp.HB3;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_3_BIT)
+			| (parity << PARITY_BYTE_3_BIT));
+	data |= dp_read(MMSS_DP_VSCEXT_1 + mst_offset);
+	dp_write(MMSS_DP_VSCEXT_1 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	print_hex_dump_debug("[drm-dp] VSCEXT: ",
+			DUMP_PREFIX_NONE, 16, 4, buf, off, false);
+}
+
+static void dp_catalog_panel_setup_hdr_infoframe_sdp(
+		struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct drm_msm_ext_hdr_metadata *hdr;
+	struct dp_io_data *io_data;
+	u32 header, parity, data, mst_offset = 0;
+	u8 buf[SZ_64], off = 0;
+	u32 const version = 0x01;
+	u32 const length = 0x1a;
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	if (panel->stream_id == DP_STREAM_1)
+		mst_offset = MMSS_DP1_GENERIC2_0 - MMSS_DP_GENERIC2_0;
+
+	catalog = dp_catalog_get_priv(panel);
+	hdr = &panel->hdr_meta;
+	io_data = catalog->io.dp_link;
+
+	/* HEADER BYTE 1 */
+	header = panel->shdr_if_sdp.HB1;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_1_BIT)
+			| (parity << PARITY_BYTE_1_BIT));
+	dp_write(MMSS_DP_GENERIC2_0 + mst_offset,
+			data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	/* HEADER BYTE 2 */
+	header = panel->shdr_if_sdp.HB2;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_2_BIT)
+			| (parity << PARITY_BYTE_2_BIT));
+	dp_write(MMSS_DP_GENERIC2_1 + mst_offset, data);
+
+	/* HEADER BYTE 3 */
+	header = panel->shdr_if_sdp.HB3;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_3_BIT)
+			| (parity << PARITY_BYTE_3_BIT));
+	data |= dp_read(MMSS_DP_VSCEXT_1 + mst_offset);
+	dp_write(MMSS_DP_GENERIC2_1 + mst_offset,
+			data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = version;
+	data |= length << 8;
+	data |= hdr->eotf << 16;
+	dp_write(MMSS_DP_GENERIC2_2 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = (DP_GET_LSB(hdr->display_primaries_x[0]) |
+		(DP_GET_MSB(hdr->display_primaries_x[0]) << 8) |
+		(DP_GET_LSB(hdr->display_primaries_y[0]) << 16) |
+		(DP_GET_MSB(hdr->display_primaries_y[0]) << 24));
+	dp_write(MMSS_DP_GENERIC2_3 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = (DP_GET_LSB(hdr->display_primaries_x[1]) |
+		(DP_GET_MSB(hdr->display_primaries_x[1]) << 8) |
+		(DP_GET_LSB(hdr->display_primaries_y[1]) << 16) |
+		(DP_GET_MSB(hdr->display_primaries_y[1]) << 24));
+	dp_write(MMSS_DP_GENERIC2_4 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = (DP_GET_LSB(hdr->display_primaries_x[2]) |
+		(DP_GET_MSB(hdr->display_primaries_x[2]) << 8) |
+		(DP_GET_LSB(hdr->display_primaries_y[2]) << 16) |
+		(DP_GET_MSB(hdr->display_primaries_y[2]) << 24));
+	dp_write(MMSS_DP_GENERIC2_5 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = (DP_GET_LSB(hdr->white_point_x) |
+		(DP_GET_MSB(hdr->white_point_x) << 8) |
+		(DP_GET_LSB(hdr->white_point_y) << 16) |
+		(DP_GET_MSB(hdr->white_point_y) << 24));
+	dp_write(MMSS_DP_GENERIC2_6 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = (DP_GET_LSB(hdr->max_luminance) |
+		(DP_GET_MSB(hdr->max_luminance) << 8) |
+		(DP_GET_LSB(hdr->min_luminance) << 16) |
+		(DP_GET_MSB(hdr->min_luminance) << 24));
+	dp_write(MMSS_DP_GENERIC2_7 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = (DP_GET_LSB(hdr->max_content_light_level) |
+		(DP_GET_MSB(hdr->max_content_light_level) << 8) |
+		(DP_GET_LSB(hdr->max_average_light_level) << 16) |
+		(DP_GET_MSB(hdr->max_average_light_level) << 24));
+	dp_write(MMSS_DP_GENERIC2_8 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = 0;
+	dp_write(MMSS_DP_GENERIC2_9 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	print_hex_dump_debug("[drm-dp] HDR: ",
+			DUMP_PREFIX_NONE, 16, 4, buf, off, false);
+}
+
+static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 header, parity, data, mst_offset = 0;
+	u8 off = 0;
+	u8 buf[SZ_128];
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	if (panel->stream_id == DP_STREAM_1)
+		mst_offset = MMSS_DP1_GENERIC0_0 - MMSS_DP_GENERIC0_0;
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	/* HEADER BYTE 1 */
+	header = panel->vsc_colorimetry.header.HB1;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_1_BIT)
+			| (parity << PARITY_BYTE_1_BIT));
+	dp_write(MMSS_DP_GENERIC0_0 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	/* HEADER BYTE 2 */
+	header = panel->vsc_colorimetry.header.HB2;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_2_BIT)
+			| (parity << PARITY_BYTE_2_BIT));
+	dp_write(MMSS_DP_GENERIC0_1 + mst_offset, data);
+
+	/* HEADER BYTE 3 */
+	header = panel->vsc_colorimetry.header.HB3;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_3_BIT)
+			| (parity << PARITY_BYTE_3_BIT));
+	data |= dp_read(MMSS_DP_GENERIC0_1 + mst_offset);
+	dp_write(MMSS_DP_GENERIC0_1 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = 0;
+	dp_write(MMSS_DP_GENERIC0_2 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	dp_write(MMSS_DP_GENERIC0_3 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	dp_write(MMSS_DP_GENERIC0_4 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	dp_write(MMSS_DP_GENERIC0_5 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = (panel->vsc_colorimetry.data[16] & 0xFF) |
+		((panel->vsc_colorimetry.data[17] & 0xFF) << 8) |
+		((panel->vsc_colorimetry.data[18] & 0x7) << 16);
+
+	dp_write(MMSS_DP_GENERIC0_6 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = 0;
+	dp_write(MMSS_DP_GENERIC0_7 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	dp_write(MMSS_DP_GENERIC0_8 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	dp_write(MMSS_DP_GENERIC0_9 + mst_offset, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	print_hex_dump_debug("[drm-dp] VSC: ",
+			DUMP_PREFIX_NONE, 16, 4, buf, off, false);
+}
+
+static void dp_catalog_panel_config_sdp(struct dp_catalog_panel *panel,
+	bool en)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 cfg, cfg2;
+	u32 sdp_cfg_off = 0;
+	u32 sdp_cfg2_off = 0;
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_1) {
+		sdp_cfg_off = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
+		sdp_cfg2_off = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
+	}
+
+	cfg = dp_read(MMSS_DP_SDP_CFG + sdp_cfg_off);
+	cfg2 = dp_read(MMSS_DP_SDP_CFG2 + sdp_cfg2_off);
+
+	if (en) {
+		/* GEN0_SDP_EN */
+		cfg |= BIT(17);
+		dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
+
+		/* GENERIC0_SDPSIZE */
+		cfg2 |= BIT(16);
+		dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
+
+		/* setup the GENERIC0 in case of en = true */
+		dp_catalog_panel_setup_vsc_sdp(panel);
+
+	} else {
+		/* GEN0_SDP_EN */
+		cfg &= ~BIT(17);
+		dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
+
+		/* GENERIC0_SDPSIZE */
+		cfg2 &= ~BIT(16);
+		dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
+	}
+
+	dp_catalog_panel_sdp_update(panel);
+}
+
+static void dp_catalog_panel_config_misc(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 reg_offset = 0;
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_1)
+		reg_offset = DP1_MISC1_MISC0 - DP_MISC1_MISC0;
+
+	DP_DEBUG("misc settings = 0x%x\n", panel->misc_val);
+	dp_write(DP_MISC1_MISC0 + reg_offset, panel->misc_val);
+}
+
+static int dp_catalog_panel_set_colorspace(struct dp_catalog_panel *panel,
+bool vsc_supported)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return -EINVAL;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (vsc_supported) {
+		dp_catalog_panel_setup_vsc_sdp(panel);
+		dp_catalog_panel_sdp_update(panel);
+	} else
+		dp_catalog_panel_config_misc(panel);
+
+	return 0;
+}
+
+static void dp_catalog_panel_config_hdr(struct dp_catalog_panel *panel, bool en,
+	u32 dhdr_max_pkts, bool flush)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 cfg, cfg2, cfg4, misc;
+	u32 sdp_cfg_off = 0;
+	u32 sdp_cfg2_off = 0;
+	u32 sdp_cfg4_off = 0;
+	u32 misc1_misc0_off = 0;
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_1) {
+		sdp_cfg_off = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
+		sdp_cfg2_off = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
+		sdp_cfg4_off = MMSS_DP1_SDP_CFG4 - MMSS_DP_SDP_CFG4;
+		misc1_misc0_off = DP1_MISC1_MISC0 - DP_MISC1_MISC0;
+	}
+
+	cfg = dp_read(MMSS_DP_SDP_CFG + sdp_cfg_off);
+	cfg2 = dp_read(MMSS_DP_SDP_CFG2 + sdp_cfg2_off);
+	misc = dp_read(DP_MISC1_MISC0 + misc1_misc0_off);
+
+	if (en) {
+		if (dhdr_max_pkts) {
+			/* VSCEXT_SDP_EN */
+			cfg |= BIT(16);
+			/* DHDR_EN, DHDR_PACKET_LIMIT */
+			cfg4 = (dhdr_max_pkts << 1) | BIT(0);
+			dp_write(MMSS_DP_SDP_CFG4 + sdp_cfg4_off, cfg4);
+			dp_catalog_panel_setup_vsif_infoframe_sdp(panel);
+		}
+
+		/* GEN2_SDP_EN */
+		cfg |= BIT(19);
+		dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
+
+		/* GENERIC2_SDPSIZE */
+		cfg2 |= BIT(20);
+		dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
+
+		dp_catalog_panel_setup_hdr_infoframe_sdp(panel);
+
+		if (panel->hdr_meta.eotf)
+			DP_DEBUG("Enabled\n");
+		else
+			DP_DEBUG("Reset\n");
+	} else {
+		/* VSCEXT_SDP_ENG */
+		cfg &= ~BIT(16) & ~BIT(19);
+		dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
+
+		/* GENERIC0_SDPSIZE GENERIC2_SDPSIZE */
+		cfg2 &= ~BIT(20);
+		dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
+
+		/* DHDR_EN, DHDR_PACKET_LIMIT */
+		cfg4 = 0;
+		dp_write(MMSS_DP_SDP_CFG4 + sdp_cfg4_off, cfg4);
+
+		DP_DEBUG("Disabled\n");
+	}
+
+	if (flush) {
+		DP_DEBUG("flushing HDR metadata\n");
+		dp_catalog_panel_sdp_update(panel);
+	}
+}
+
+static void dp_catalog_panel_update_transfer_unit(
+		struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!panel || panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	dp_write(DP_VALID_BOUNDARY, panel->valid_boundary);
+	dp_write(DP_TU, panel->dp_tu);
+	dp_write(DP_VALID_BOUNDARY_2, panel->valid_boundary2);
+}
+
+static void dp_catalog_ctrl_state_ctrl(struct dp_catalog_ctrl *ctrl, u32 state)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_link;
+
+	dp_write(DP_STATE_CTRL, state);
+	/* make sure to change the hw state */
+	wmb();
+}
+
+static void dp_catalog_ctrl_config_ctrl(struct dp_catalog_ctrl *ctrl, u8 ln_cnt)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 cfg;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_link;
+
+	cfg = dp_read(DP_CONFIGURATION_CTRL);
+	/*
+	 * Reset ASSR (alternate scrambler seed reset) by resetting BIT(10).
+	 * ASSR should be set to disable for TPS4 link training pattern.
+	 * Forcing it to 0 as the power on reset value of register enables it.
+	 */
+	cfg &= ~(BIT(4) | BIT(5) | BIT(10));
+	cfg |= (ln_cnt - 1) << 4;
+	dp_write(DP_CONFIGURATION_CTRL, cfg);
+
+	cfg = dp_read(DP_MAINLINK_CTRL);
+	cfg |= 0x02000000;
+	dp_write(DP_MAINLINK_CTRL, cfg);
+
+	DP_DEBUG("DP_MAINLINK_CTRL=0x%x\n", cfg);
+}
+
+static void dp_catalog_panel_config_ctrl(struct dp_catalog_panel *panel,
+		u32 cfg)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 strm_reg_off = 0, mainlink_ctrl;
+	u32 reg;
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_1)
+		strm_reg_off = DP1_CONFIGURATION_CTRL - DP_CONFIGURATION_CTRL;
+
+	DP_DEBUG("DP_CONFIGURATION_CTRL=0x%x\n", cfg);
+
+	dp_write(DP_CONFIGURATION_CTRL + strm_reg_off, cfg);
+
+	mainlink_ctrl = dp_read(DP_MAINLINK_CTRL);
+
+	if (panel->stream_id == DP_STREAM_0)
+		io_data = catalog->io.dp_p0;
+	else if (panel->stream_id == DP_STREAM_1)
+		io_data = catalog->io.dp_p1;
+
+	if (mainlink_ctrl & BIT(8))
+		dp_write(MMSS_DP_ASYNC_FIFO_CONFIG, 0x01);
+	else
+		dp_write(MMSS_DP_ASYNC_FIFO_CONFIG, 0x00);
+
+	reg = dp_read(MMSS_DP_TIMING_ENGINE_EN);
+	reg |= BIT(8);
+	dp_write(MMSS_DP_TIMING_ENGINE_EN, reg);
+}
+
+static void dp_catalog_panel_config_dto(struct dp_catalog_panel *panel,
+					bool ack)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 dsc_dto;
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	switch (panel->stream_id) {
+	case DP_STREAM_0:
+		io_data = catalog->io.dp_p0;
+		break;
+	case DP_STREAM_1:
+		io_data = catalog->io.dp_p1;
+		break;
+	default:
+		DP_ERR("invalid stream id\n");
+		return;
+	}
+
+	dsc_dto = dp_read(MMSS_DP_DSC_DTO);
+	if (ack)
+		dsc_dto = BIT(1);
+	else
+		dsc_dto &= ~BIT(1);
+	dp_write(MMSS_DP_DSC_DTO, dsc_dto);
+}
+
+static void dp_catalog_ctrl_lane_mapping(struct dp_catalog_ctrl *ctrl,
+						bool flipped, char *lane_map)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_link;
+
+	dp_write(DP_LOGICAL2PHYSICAL_LANE_MAPPING, 0xe4);
+}
+
+static void dp_catalog_ctrl_lane_pnswap(struct dp_catalog_ctrl *ctrl,
+						u8 ln_pnswap)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 cfg0, cfg1;
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	cfg0 = 0x0a;
+	cfg1 = 0x0a;
+
+	cfg0 |= ((ln_pnswap >> 0) & 0x1) << 0;
+	cfg0 |= ((ln_pnswap >> 1) & 0x1) << 2;
+	cfg1 |= ((ln_pnswap >> 2) & 0x1) << 0;
+	cfg1 |= ((ln_pnswap >> 3) & 0x1) << 2;
+
+	io_data = catalog->io.dp_ln_tx0;
+	dp_write(TXn_TX_POL_INV, cfg0);
+
+	io_data = catalog->io.dp_ln_tx1;
+	dp_write(TXn_TX_POL_INV, cfg1);
+}
+
+static void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog_ctrl *ctrl,
+						bool enable)
+{
+	u32 mainlink_ctrl, reg;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_link;
+
+	if (enable) {
+		reg = dp_read(DP_MAINLINK_CTRL);
+		mainlink_ctrl = reg & ~(0x03);
+		dp_write(DP_MAINLINK_CTRL, mainlink_ctrl);
+		wmb(); /* make sure mainlink is turned off before reset */
+		mainlink_ctrl = reg | 0x02;
+		dp_write(DP_MAINLINK_CTRL, mainlink_ctrl);
+		wmb(); /* make sure mainlink entered reset */
+		mainlink_ctrl = reg & ~(0x03);
+		dp_write(DP_MAINLINK_CTRL, mainlink_ctrl);
+		wmb(); /* make sure mainlink reset done */
+		mainlink_ctrl = reg | 0x01;
+		dp_write(DP_MAINLINK_CTRL, mainlink_ctrl);
+		wmb(); /* make sure mainlink turned on */
+	} else {
+		mainlink_ctrl = dp_read(DP_MAINLINK_CTRL);
+		mainlink_ctrl &= ~BIT(0);
+		dp_write(DP_MAINLINK_CTRL, mainlink_ctrl);
+	}
+}
+
+static void dp_catalog_panel_config_msa(struct dp_catalog_panel *panel,
+					u32 rate, u32 stream_rate_khz)
+{
+	u32 pixel_m, pixel_n;
+	u32 mvid, nvid;
+	u32 const nvid_fixed = 0x8000;
+	u32 const link_rate_hbr2 = 540000;
+	u32 const link_rate_hbr3 = 810000;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 strm_reg_off = 0;
+	u32 mvid_reg_off = 0, nvid_reg_off = 0;
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_mmss_cc;
+
+	if (panel->stream_id == DP_STREAM_1)
+		strm_reg_off = MMSS_DP_PIXEL1_M - MMSS_DP_PIXEL_M;
+
+	pixel_m = dp_read(MMSS_DP_PIXEL_M + strm_reg_off);
+	pixel_n = dp_read(MMSS_DP_PIXEL_N + strm_reg_off);
+	DP_DEBUG("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);
+
+	mvid = (pixel_m & 0xFFFF) * 5;
+	nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
+
+	if (nvid < nvid_fixed) {
+		u32 temp;
+
+		temp = (nvid_fixed / nvid) * nvid;
+		mvid = (nvid_fixed / nvid) * mvid;
+		nvid = temp;
+	}
+
+	DP_DEBUG("rate = %d\n", rate);
+
+	if (panel->widebus_en)
+		mvid <<= 1;
+
+	if (link_rate_hbr2 == rate)
+		nvid *= 2;
+
+	if (link_rate_hbr3 == rate)
+		nvid *= 3;
+
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_1) {
+		mvid_reg_off = DP1_SOFTWARE_MVID - DP_SOFTWARE_MVID;
+		nvid_reg_off = DP1_SOFTWARE_NVID - DP_SOFTWARE_NVID;
+	}
+
+	DP_DEBUG("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
+	dp_write(DP_SOFTWARE_MVID + mvid_reg_off, mvid);
+	dp_write(DP_SOFTWARE_NVID + nvid_reg_off, nvid);
+}
+
+static void dp_catalog_ctrl_set_pattern(struct dp_catalog_ctrl *ctrl,
+					u32 pattern)
+{
+	int bit, cnt = 10;
+	u32 data;
+	const u32 link_training_offset = 3;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_link;
+
+	switch (pattern) {
+	case DP_TRAINING_PATTERN_4:
+		bit = 3;
+		break;
+	case DP_TRAINING_PATTERN_3:
+	case DP_TRAINING_PATTERN_2:
+	case DP_TRAINING_PATTERN_1:
+		bit = pattern - 1;
+		break;
+	default:
+		DP_ERR("invalid pattern\n");
+		return;
+	}
+
+	DP_DEBUG("hw: bit=%d train=%d\n", bit, pattern);
+	dp_write(DP_STATE_CTRL, BIT(bit));
+
+	bit += link_training_offset;
+
+	while (cnt--) {
+		data = dp_read(DP_MAINLINK_READY);
+		if (data & BIT(bit))
+			break;
+	}
+
+	if (cnt == 0)
+		DP_ERR("set link_train=%d failed\n", pattern);
+}
+
+static void dp_catalog_ctrl_usb_reset(struct dp_catalog_ctrl *ctrl, bool flip)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.usb3_dp_com;
+
+	DP_DEBUG("Program PHYMODE to DP only\n");
+	dp_write(USB3_DP_COM_RESET_OVRD_CTRL, 0x0a);
+	dp_write(USB3_DP_COM_PHY_MODE_CTRL, 0x02);
+	dp_write(USB3_DP_COM_SW_RESET, 0x01);
+	/* make sure usb3 com phy software reset is done */
+	wmb();
+
+	if (!flip) /* CC1 */
+		dp_write(USB3_DP_COM_TYPEC_CTRL, 0x02);
+	else /* CC2 */
+		dp_write(USB3_DP_COM_TYPEC_CTRL, 0x03);
+
+	dp_write(USB3_DP_COM_SWI_CTRL, 0x00);
+	dp_write(USB3_DP_COM_SW_RESET, 0x00);
+	/* make sure the software reset is done */
+	wmb();
+
+	dp_write(USB3_DP_COM_POWER_DOWN_CTRL, 0x01);
+	dp_write(USB3_DP_COM_RESET_OVRD_CTRL, 0x00);
+	/* make sure phy is brought out of reset */
+	wmb();
+}
+
+static int dp_catalog_ctrl_setup_misr(struct dp_catalog_ctrl *ctrl)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 val;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_phy;
+	dp_write(DP_PHY_MISR_CTRL, 0x3);
+	/* make sure misr hw is reset */
+	wmb();
+	dp_write(DP_PHY_MISR_CTRL, 0x1);
+	/* make sure misr is brought out of reset */
+	wmb();
+
+	io_data = catalog->io.dp_link;
+	val = 1;	// frame count
+	val |= BIT(10); // clear status
+	val |= BIT(8);  // enable
+	dp_write(DP_MISR40_CTRL, val);
+	/* make sure misr control is applied */
+	wmb();
+
+	return 0;
+}
+
+static int dp_catalog_ctrl_read_misr(struct dp_catalog_ctrl *ctrl, struct dp_misr40_data *data)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 val;
+	int i, j;
+	u32 addr;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_phy;
+	val = dp_read(DP_PHY_MISR_STATUS);
+	if (!val) {
+		DP_WARN("phy misr not ready!");
+		return -EAGAIN;
+	}
+
+	addr = DP_PHY_MISR_TX0;
+	for (i = 0; i < 8; i++) {
+		data->phy_misr[i] = 0;
+		for (j = 0; j < 4; j++) {
+			val = dp_read(addr) & 0xff;
+			data->phy_misr[i] |= val << (j * 8);
+			addr += 4;
+		}
+	}
+
+	io_data = catalog->io.dp_link;
+	for (i = 0; i < 8; i++)
+		data->ctrl_misr[i] = dp_read(DP_MISR40_TX0 + (i * 4));
+
+	return 0;
+}
+
+static void dp_catalog_panel_tpg_cfg(struct dp_catalog_panel *panel, u32 pattern)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 reg;
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+
+	if (panel->stream_id == DP_STREAM_0)
+		io_data = catalog->io.dp_p0;
+	else if (panel->stream_id == DP_STREAM_1)
+		io_data = catalog->io.dp_p1;
+
+	if (!pattern) {
+		dp_write(MMSS_DP_TPG_MAIN_CONTROL, 0x0);
+		dp_write(MMSS_DP_BIST_ENABLE, 0x0);
+		reg = dp_read(MMSS_DP_TIMING_ENGINE_EN);
+		reg &= ~0x1;
+		dp_write(MMSS_DP_TIMING_ENGINE_EN, reg);
+		wmb(); /* ensure Timing generator is turned off */
+		return;
+	}
+
+	if (pattern > DP_TPG_PATTERN_MAX)
+		pattern = DP_TPG_PATTERN_DEFAULT;
+
+	dp_write(MMSS_DP_INTF_HSYNC_CTL,
+			panel->hsync_ctl);
+	dp_write(MMSS_DP_INTF_VSYNC_PERIOD_F0,
+			panel->vsync_period * panel->hsync_period);
+	dp_write(MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0,
+			panel->v_sync_width * panel->hsync_period);
+	dp_write(MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
+	dp_write(MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
+	dp_write(MMSS_DP_INTF_DISPLAY_HCTL, panel->display_hctl);
+	dp_write(MMSS_DP_INTF_ACTIVE_HCTL, 0);
+	dp_write(MMSS_INTF_DISPLAY_V_START_F0, panel->display_v_start);
+	dp_write(MMSS_DP_INTF_DISPLAY_V_END_F0, panel->display_v_end);
+	dp_write(MMSS_INTF_DISPLAY_V_START_F1, 0);
+	dp_write(MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
+	dp_write(MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
+	dp_write(MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
+	dp_write(MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
+	dp_write(MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
+	dp_write(MMSS_DP_INTF_POLARITY_CTL, 0);
+	wmb(); /* ensure TPG registers are programmed */
+
+	dp_write(MMSS_DP_TPG_MAIN_CONTROL, (1 << pattern));
+	dp_write(MMSS_DP_TPG_VIDEO_CONFIG, 0x5);
+	wmb(); /* ensure TPG config is programmed */
+	dp_write(MMSS_DP_BIST_ENABLE, 0x1);
+	reg = dp_read(MMSS_DP_TIMING_ENGINE_EN);
+	reg |= 0x1;
+	dp_write(MMSS_DP_TIMING_ENGINE_EN, reg);
+	wmb(); /* ensure Timing generator is turned on */
+}
+
+static void dp_catalog_panel_dsc_cfg(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 reg, offset;
+	int i;
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+
+	if (panel->stream_id == DP_STREAM_0)
+		io_data = catalog->io.dp_p0;
+	else
+		io_data = catalog->io.dp_p1;
+
+	dp_write(MMSS_DP_DSC_DTO_COUNT, panel->dsc.dto_count);
+
+	reg = dp_read(MMSS_DP_DSC_DTO);
+	if (panel->dsc.dto_en) {
+		reg |= BIT(0);
+		reg |= BIT(3);
+		reg |= (panel->dsc.dto_n << 8);
+		reg |= (panel->dsc.dto_d << 16);
+	}
+	dp_write(MMSS_DP_DSC_DTO, reg);
+
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_0)
+		offset = 0;
+	else
+		offset = DP1_COMPRESSION_MODE_CTRL - DP_COMPRESSION_MODE_CTRL;
+
+	dp_write(DP_PPS_HB_0_3 + offset, 0x7F1000);
+	dp_write(DP_PPS_PB_0_3 + offset, 0xA22300);
+
+	for (i = 0; i < panel->dsc.parity_word_len; i++)
+		dp_write(DP_PPS_PB_4_7 + (i << 2) + offset,
+				panel->dsc.parity_word[i]);
+
+	for (i = 0; i < panel->dsc.pps_word_len; i++)
+		dp_write(DP_PPS_PPS_0_3 + (i << 2) + offset,
+				panel->dsc.pps_word[i]);
+
+	reg = 0;
+	if (panel->dsc.dsc_en) {
+		reg = BIT(0);
+		reg |= (panel->dsc.eol_byte_num << 3);
+		reg |= (panel->dsc.slice_per_pkt << 5);
+		reg |= (panel->dsc.bytes_per_pkt << 16);
+		reg |= (panel->dsc.be_in_lane << 10);
+	}
+	dp_write(DP_COMPRESSION_MODE_CTRL + offset, reg);
+
+	DP_DEBUG("compression:0x%x for stream:%d\n",
+			reg, panel->stream_id);
+}
+
+static void dp_catalog_panel_dp_flush(struct dp_catalog_panel *panel,
+		enum dp_flush_bit flush_bit)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 dp_flush, offset;
+	struct dp_dsc_cfg_data *dsc;
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+	dsc = &panel->dsc;
+
+	if (panel->stream_id == DP_STREAM_0)
+		offset = 0;
+	else
+		offset = MMSS_DP1_FLUSH - MMSS_DP_FLUSH;
+
+	dp_flush = dp_read(MMSS_DP_FLUSH + offset);
+
+	if ((flush_bit == DP_PPS_FLUSH) &&
+		dsc->continuous_pps)
+		dp_flush &= ~BIT(2);
+
+	dp_flush |= BIT(flush_bit);
+	dp_write(MMSS_DP_FLUSH + offset, dp_flush);
+}
+
+static void dp_catalog_panel_pps_flush(struct dp_catalog_panel *panel)
+{
+	dp_catalog_panel_dp_flush(panel, DP_PPS_FLUSH);
+	DP_DEBUG("pps flush for stream:%d\n", panel->stream_id);
+}
+
+static void dp_catalog_panel_dhdr_flush(struct dp_catalog_panel *panel)
+{
+	dp_catalog_panel_dp_flush(panel, DP_DHDR_FLUSH);
+	DP_DEBUG("dhdr flush for stream:%d\n", panel->stream_id);
+}
+
+
+static bool dp_catalog_panel_dhdr_busy(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 dp_flush, offset;
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return false;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_0)
+		offset = 0;
+	else
+		offset = MMSS_DP1_FLUSH - MMSS_DP_FLUSH;
+
+	dp_flush = dp_read(MMSS_DP_FLUSH + offset);
+
+	return dp_flush & BIT(DP_DHDR_FLUSH) ? true : false;
+}
+
+static int dp_catalog_panel_get_src_crc(struct dp_catalog_panel *panel, u16 *crc)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 offset;
+	u32 reg;
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return -EINVAL;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_0)
+		offset = MMSS_DP_PSR_CRC_RG;
+	else
+		offset = MMSS_DP1_CRC_RG;
+
+	reg = dp_read(offset); //GR
+	crc[0] = reg & 0xffff;
+	crc[1] = reg >> 16;
+	crc[2] = dp_read(offset + 4); //B
+
+	return 0;
+}
+
+static void dp_catalog_ctrl_reset(struct dp_catalog_ctrl *ctrl)
+{
+	u32 sw_reset;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_ahb;
+
+	sw_reset = dp_read(DP_SW_RESET);
+
+	sw_reset |= BIT(0);
+	dp_write(DP_SW_RESET, sw_reset);
+	usleep_range(1000, 1010); /* h/w recommended delay */
+
+	sw_reset &= ~BIT(0);
+	dp_write(DP_SW_RESET, sw_reset);
+}
+
+static bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog_ctrl *ctrl)
+{
+	u32 data;
+	int cnt = 10;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		goto end;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_link;
+
+	while (--cnt) {
+		/* DP_MAINLINK_READY */
+		data = dp_read(DP_MAINLINK_READY);
+		if (data & BIT(0))
+			return true;
+
+		usleep_range(1000, 1010); /* 1ms wait before next reg read */
+	}
+	DP_ERR("mainlink not ready\n");
+end:
+	return false;
+}
+
+static void dp_catalog_ctrl_enable_irq(struct dp_catalog_ctrl *ctrl,
+						bool enable)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_ahb;
+
+	if (enable) {
+		dp_write(DP_INTR_STATUS, DP_INTR_MASK1);
+		dp_write(DP_INTR_STATUS2, DP_INTR_MASK2);
+		dp_write(DP_INTR_STATUS3, DP_INTR_MASK3);
+		dp_write(DP_INTR_STATUS5, DP_INTR_MASK5);
+		dp_write(DP_INTR_STATUS6, DP_INTR_MASK6);
+	} else {
+		/* disable interrupts */
+		dp_write(DP_INTR_STATUS, 0x00);
+		dp_write(DP_INTR_STATUS2, 0x00);
+		dp_write(DP_INTR_STATUS3, 0x00);
+		dp_write(DP_INTR_STATUS5, 0x00);
+		dp_write(DP_INTR_STATUS6, 0x00);
+		wmb();
+
+		/* clear all pending interrupts */
+		dp_write(DP_INTR_STATUS, DP_INTERRUPT_STATUS1 << 1);
+		dp_write(DP_INTR_STATUS2, DP_INTERRUPT_STATUS2 << 1);
+		dp_write(DP_INTR_STATUS3, DP_INTERRUPT_STATUS3 << 1);
+		dp_write(DP_INTR_STATUS5, DP_INTERRUPT_STATUS5 << 1);
+		dp_write(DP_INTR_STATUS6, DP_INTERRUPT_STATUS6 << 1);
+
+		wmb();
+	}
+}
+
+static void dp_catalog_ctrl_get_interrupt(struct dp_catalog_ctrl *ctrl)
+{
+	u32 ack = 0;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_ahb;
+
+	ctrl->isr = dp_read(DP_INTR_STATUS2);
+	ctrl->isr &= ~DP_INTR_MASK2;
+	ack = ctrl->isr & DP_INTERRUPT_STATUS2;
+	ack <<= 1;
+	ack |= DP_INTR_MASK2;
+	dp_write(DP_INTR_STATUS2, ack);
+
+	ctrl->isr3 = dp_read(DP_INTR_STATUS3);
+	ctrl->isr3 &= ~DP_INTR_MASK3;
+	ack = ctrl->isr3 & DP_INTERRUPT_STATUS3;
+	ack <<= 1;
+	ack |= DP_INTR_MASK3;
+	dp_write(DP_INTR_STATUS3, ack);
+
+	ctrl->isr5 = dp_read(DP_INTR_STATUS5);
+	ctrl->isr5 &= ~DP_INTR_MASK5;
+	ack = ctrl->isr5 & DP_INTERRUPT_STATUS5;
+	ack <<= 1;
+	ack |= DP_INTR_MASK5;
+	dp_write(DP_INTR_STATUS5, ack);
+
+	ctrl->isr6 = dp_read(DP_INTR_STATUS6);
+	ctrl->isr6 &= ~DP_INTR_MASK6;
+	ack = ctrl->isr6 & DP_INTERRUPT_STATUS6;
+	ack <<= 1;
+	ack |= DP_INTR_MASK6;
+	dp_write(DP_INTR_STATUS6, ack);
+
+}
+
+static void dp_catalog_ctrl_phy_reset(struct dp_catalog_ctrl *ctrl)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_ahb;
+
+	dp_write(DP_PHY_CTRL, 0x5); /* bit 0 & 2 */
+	usleep_range(1000, 1010); /* h/w recommended delay */
+	dp_write(DP_PHY_CTRL, 0x0);
+	wmb(); /* make sure PHY reset done */
+}
+
+static void dp_catalog_ctrl_phy_lane_cfg(struct dp_catalog_ctrl *ctrl,
+		bool flipped, u8 ln_cnt)
+{
+	u32 info = 0x0;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u8 orientation = BIT(!!flipped);
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_phy;
+
+	info |= (ln_cnt & 0x0F);
+	info |= ((orientation & 0x0F) << 4);
+	DP_DEBUG("Shared Info = 0x%x\n", info);
+
+	dp_write(DP_PHY_SPARE0, info);
+}
+
+static void dp_catalog_ctrl_update_vx_px(struct dp_catalog_ctrl *ctrl,
+		u8 v_level, u8 p_level, bool high)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u8 value0, value1;
+	u32 version;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	DP_DEBUG("hw: v=%d p=%d\n", v_level, p_level);
+
+	io_data = catalog->io.dp_ahb;
+	version = dp_read(DP_HW_VERSION);
+
+	if (version == 0x10020004) {
+		if (high) {
+			value0 = vm_voltage_swing_hbr3_hbr2[v_level][p_level];
+			value1 = vm_pre_emphasis_hbr3_hbr2[v_level][p_level];
+		} else {
+			value0 = vm_voltage_swing_hbr_rbr[v_level][p_level];
+			value1 = vm_pre_emphasis_hbr_rbr[v_level][p_level];
+		}
+	} else {
+		value0 = vm_voltage_swing[v_level][p_level];
+		value1 = vm_pre_emphasis[v_level][p_level];
+	}
+
+	/* program default setting first */
+
+	io_data = catalog->io.dp_ln_tx0;
+	dp_write(TXn_TX_DRV_LVL, 0x2A);
+	dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
+
+	io_data = catalog->io.dp_ln_tx1;
+	dp_write(TXn_TX_DRV_LVL, 0x2A);
+	dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
+
+	/* Enable MUX to use Cursor values from these registers */
+	value0 |= BIT(5);
+	value1 |= BIT(5);
+
+	/* Configure host and panel only if both values are allowed */
+	if (value0 != 0xFF && value1 != 0xFF) {
+		io_data = catalog->io.dp_ln_tx0;
+		dp_write(TXn_TX_DRV_LVL, value0);
+		dp_write(TXn_TX_EMP_POST1_LVL, value1);
+
+		io_data = catalog->io.dp_ln_tx1;
+		dp_write(TXn_TX_DRV_LVL, value0);
+		dp_write(TXn_TX_EMP_POST1_LVL, value1);
+
+		DP_DEBUG("hw: vx_value=0x%x px_value=0x%x\n",
+			value0, value1);
+	} else {
+		DP_ERR("invalid vx (0x%x=0x%x), px (0x%x=0x%x\n",
+			v_level, value0, p_level, value1);
+	}
+}
+
+static void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog_ctrl *ctrl,
+			u32 pattern)
+{
+	struct dp_catalog_private *catalog;
+	u32 value = 0x0;
+	struct dp_io_data *io_data = NULL;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_link;
+
+	dp_write(DP_STATE_CTRL, 0x0);
+
+	switch (pattern) {
+	case DP_PHY_TEST_PATTERN_D10_2:
+		dp_write(DP_STATE_CTRL, 0x1);
+		break;
+	case DP_PHY_TEST_PATTERN_ERROR_COUNT:
+		value &= ~(1 << 16);
+		dp_write(DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		value |= 0xFC;
+		dp_write(DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		dp_write(DP_MAINLINK_LEVELS, 0x2);
+		dp_write(DP_STATE_CTRL, 0x10);
+		break;
+	case DP_PHY_TEST_PATTERN_PRBS7:
+		dp_write(DP_STATE_CTRL, 0x20);
+		break;
+	case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
+		dp_write(DP_STATE_CTRL, 0x40);
+		/* 00111110000011111000001111100000 */
+		dp_write(DP_TEST_80BIT_CUSTOM_PATTERN_REG0, 0x3E0F83E0);
+		/* 00001111100000111110000011111000 */
+		dp_write(DP_TEST_80BIT_CUSTOM_PATTERN_REG1, 0x0F83E0F8);
+		/* 1111100000111110 */
+		dp_write(DP_TEST_80BIT_CUSTOM_PATTERN_REG2, 0x0000F83E);
+		break;
+	case DP_PHY_TEST_PATTERN_CP2520:
+		value = dp_read(DP_MAINLINK_CTRL);
+		value &= ~BIT(4);
+		dp_write(DP_MAINLINK_CTRL, value);
+
+		value = BIT(16);
+		dp_write(DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		value |= 0xFC;
+		dp_write(DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		dp_write(DP_MAINLINK_LEVELS, 0x2);
+		dp_write(DP_STATE_CTRL, 0x10);
+
+		value = dp_read(DP_MAINLINK_CTRL);
+		value |= BIT(0);
+		dp_write(DP_MAINLINK_CTRL, value);
+		break;
+	case DP_PHY_TEST_PATTERN_CP2520_3:
+		dp_write(DP_MAINLINK_CTRL, 0x01);
+		dp_write(DP_STATE_CTRL, 0x8);
+		break;
+	default:
+		DP_DEBUG("No valid test pattern requested: 0x%x\n", pattern);
+		return;
+	}
+
+	/* Make sure the test pattern is programmed in the hardware */
+	wmb();
+}
+
+static u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog_ctrl *ctrl)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data = NULL;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return 0;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_link;
+
+	return dp_read(DP_MAINLINK_READY);
+}
+
+static void dp_catalog_ctrl_fec_config(struct dp_catalog_ctrl *ctrl,
+		bool enable)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data = NULL;
+	u32 reg;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+	io_data = catalog->io.dp_link;
+
+	reg = dp_read(DP_MAINLINK_CTRL);
+
+	/*
+	 * fec_en = BIT(12)
+	 * fec_seq_mode = BIT(22)
+	 * sde_flush = BIT(23) | BIT(24)
+	 * fb_boundary_sel = BIT(25)
+	 */
+	if (enable)
+		reg |= BIT(12) | BIT(22) | BIT(23) | BIT(24) | BIT(25);
+	else
+		reg &= ~BIT(12);
+
+	dp_write(DP_MAINLINK_CTRL, reg);
+	/* make sure mainlink configuration is updated with fec sequence */
+	wmb();
+}
+
+u32 dp_catalog_get_dp_core_version(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!dp_catalog) {
+		DP_ERR("invalid input\n");
+		return 0;
+	}
+
+	catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog);
+	if (catalog->dp_core_version)
+		return catalog->dp_core_version;
+
+	io_data = catalog->io.dp_ahb;
+
+	return dp_read(DP_HW_VERSION);
+}
+
+u32 dp_catalog_get_dp_phy_version(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!dp_catalog) {
+		DP_ERR("invalid input\n");
+		return 0;
+	}
+
+	catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog);
+	if (catalog->dp_phy_version)
+		return catalog->dp_phy_version;
+
+	io_data = catalog->io.dp_phy;
+	catalog->dp_phy_version = (dp_read(DP_PHY_REVISION_ID3) << 24) |
+				(dp_read(DP_PHY_REVISION_ID2) << 16) |
+				(dp_read(DP_PHY_REVISION_ID1) << 8) |
+				dp_read(DP_PHY_REVISION_ID0);
+
+	return catalog->dp_phy_version;
+}
+
+static int dp_catalog_reg_dump(struct dp_catalog *dp_catalog,
+		char *name, u8 **out_buf, u32 *out_buf_len)
+{
+	int ret = 0;
+	u8 *buf;
+	u32 len;
+	struct dp_io_data *io_data;
+	struct dp_catalog_private *catalog;
+	struct dp_parser *parser;
+
+	if (!dp_catalog) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	catalog = container_of(dp_catalog, struct dp_catalog_private,
+		dp_catalog);
+
+	parser = catalog->parser;
+	parser->get_io_buf(parser, name);
+	io_data = parser->get_io(parser, name);
+	if (!io_data) {
+		DP_ERR("IO %s not found\n", name);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	buf = io_data->buf;
+	len = io_data->io.len;
+
+	if (!buf || !len) {
+		DP_ERR("no buffer available\n");
+		ret = -ENOMEM;
+		goto end;
+	}
+
+	if (!strcmp(catalog->exe_mode, "hw") ||
+	    !strcmp(catalog->exe_mode, "all")) {
+		u32 i, data;
+		u32 const rowsize = 4;
+		void __iomem *addr = io_data->io.base;
+
+		memset(buf, 0, len);
+
+		for (i = 0; i < len / rowsize; i++) {
+			data = readl_relaxed(addr);
+			memcpy(buf + (rowsize * i), &data, sizeof(u32));
+
+			addr += rowsize;
+		}
+	}
+
+	*out_buf = buf;
+	*out_buf_len = len;
+end:
+	if (ret)
+		parser->clear_io_buf(parser);
+
+	return ret;
+}
+
+static void dp_catalog_ctrl_mst_config(struct dp_catalog_ctrl *ctrl,
+		bool enable)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data = NULL;
+	u32 reg;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_link;
+
+	reg = dp_read(DP_MAINLINK_CTRL);
+	if (enable)
+		reg |= (0x04000100);
+	else
+		reg &= ~(0x04000100);
+
+	dp_write(DP_MAINLINK_CTRL, reg);
+	/* make sure mainlink MST configuration is updated */
+	wmb();
+}
+
+static void dp_catalog_ctrl_trigger_act(struct dp_catalog_ctrl *ctrl)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data = NULL;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_link;
+
+	dp_write(DP_MST_ACT, 0x1);
+	/* make sure ACT signal is performed */
+	wmb();
+}
+
+static void dp_catalog_ctrl_read_act_complete_sts(struct dp_catalog_ctrl *ctrl,
+		bool *sts)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data = NULL;
+	u32 reg;
+
+	if (!ctrl || !sts) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	*sts = false;
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_link;
+
+	reg = dp_read(DP_MST_ACT);
+
+	if (!reg)
+		*sts = true;
+}
+
+static void dp_catalog_ctrl_channel_alloc(struct dp_catalog_ctrl *ctrl,
+			u32 ch, u32 ch_start_slot, u32 tot_slot_cnt)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data = NULL;
+	u32 i, slot_reg_1, slot_reg_2, slot;
+	u32 reg_off = 0;
+	int const num_slots_per_reg = 32;
+
+	if (!ctrl || ch >= DP_STREAM_MAX) {
+		DP_ERR("invalid input. ch %d\n", ch);
+		return;
+	}
+
+	if (ch_start_slot > DP_MAX_TIME_SLOTS ||
+			(ch_start_slot + tot_slot_cnt > DP_MAX_TIME_SLOTS)) {
+		DP_ERR("invalid slots start %d, tot %d\n",
+			ch_start_slot, tot_slot_cnt);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_link;
+
+	DP_DEBUG("ch %d, start_slot %d, tot_slot %d\n",
+			ch, ch_start_slot, tot_slot_cnt);
+
+	if (ch == DP_STREAM_1)
+		reg_off = DP_DP1_TIMESLOT_1_32 - DP_DP0_TIMESLOT_1_32;
+
+	slot_reg_1 = 0;
+	slot_reg_2 = 0;
+
+	if (ch_start_slot && tot_slot_cnt) {
+		ch_start_slot--;
+		for (i = 0; i < tot_slot_cnt; i++) {
+			if (ch_start_slot < num_slots_per_reg) {
+				slot_reg_1 |= BIT(ch_start_slot);
+			} else {
+				slot = ch_start_slot - num_slots_per_reg;
+				slot_reg_2 |= BIT(slot);
+			}
+			ch_start_slot++;
+		}
+	}
+
+	DP_DEBUG("ch:%d slot_reg_1:%d, slot_reg_2:%d\n", ch,
+			slot_reg_1, slot_reg_2);
+
+	dp_write(DP_DP0_TIMESLOT_1_32 + reg_off, slot_reg_1);
+	dp_write(DP_DP0_TIMESLOT_33_63 + reg_off, slot_reg_2);
+}
+
+static void dp_catalog_ctrl_channel_dealloc(struct dp_catalog_ctrl *ctrl,
+			u32 ch, u32 ch_start_slot, u32 tot_slot_cnt)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data = NULL;
+	u32 i, slot_reg_1, slot_reg_2, slot;
+	u32 reg_off = 0;
+
+	if (!ctrl || ch >= DP_STREAM_MAX) {
+		DP_ERR("invalid input. ch %d\n", ch);
+		return;
+	}
+
+	if (ch_start_slot > DP_MAX_TIME_SLOTS ||
+			(ch_start_slot + tot_slot_cnt > DP_MAX_TIME_SLOTS)) {
+		DP_ERR("invalid slots start %d, tot %d\n",
+			ch_start_slot, tot_slot_cnt);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_link;
+
+	DP_DEBUG("dealloc ch %d, start_slot %d, tot_slot %d\n",
+			ch, ch_start_slot, tot_slot_cnt);
+
+	if (ch == DP_STREAM_1)
+		reg_off = DP_DP1_TIMESLOT_1_32 - DP_DP0_TIMESLOT_1_32;
+
+	slot_reg_1 = dp_read(DP_DP0_TIMESLOT_1_32 + reg_off);
+	slot_reg_2 = dp_read(DP_DP0_TIMESLOT_33_63 + reg_off);
+
+	ch_start_slot = ch_start_slot - 1;
+	for (i = 0; i < tot_slot_cnt; i++) {
+		if (ch_start_slot < 33) {
+			slot_reg_1 &= ~BIT(ch_start_slot);
+		} else {
+			slot = ch_start_slot - 33;
+			slot_reg_2 &= ~BIT(slot);
+		}
+		ch_start_slot++;
+	}
+
+	DP_DEBUG("dealloc ch:%d slot_reg_1:%d, slot_reg_2:%d\n", ch,
+			slot_reg_1, slot_reg_2);
+
+	dp_write(DP_DP0_TIMESLOT_1_32 + reg_off, slot_reg_1);
+	dp_write(DP_DP0_TIMESLOT_33_63 + reg_off, slot_reg_2);
+}
+
+static void dp_catalog_ctrl_update_rg(struct dp_catalog_ctrl *ctrl, u32 ch,
+		u32 x_int, u32 y_frac_enum)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data = NULL;
+	u32 rg, reg_off = 0;
+
+	if (!ctrl || ch >= DP_STREAM_MAX) {
+		DP_ERR("invalid input. ch %d\n", ch);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data = catalog->io.dp_link;
+
+	rg = y_frac_enum;
+	rg |= (x_int << 16);
+
+	DP_DEBUG("ch: %d x_int:%d y_frac_enum:%d rg:%d\n", ch, x_int,
+			y_frac_enum, rg);
+
+	if (ch == DP_STREAM_1)
+		reg_off = DP_DP1_RG - DP_DP0_RG;
+
+	dp_write(DP_DP0_RG + reg_off, rg);
+}
+
+static void dp_catalog_ctrl_mainlink_levels(struct dp_catalog_ctrl *ctrl,
+		u8 lane_cnt)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 mainlink_levels, safe_to_exit_level = 14;
+
+	catalog = dp_catalog_get_priv(ctrl);
+
+	io_data   = catalog->io.dp_link;
+
+	switch (lane_cnt) {
+	case 1:
+		safe_to_exit_level = 14;
+		break;
+	case 2:
+		safe_to_exit_level = 8;
+		break;
+	case 4:
+		safe_to_exit_level = 5;
+		break;
+	default:
+		DP_DEBUG("setting the default safe_to_exit_level = %u\n",
+				safe_to_exit_level);
+		break;
+	}
+
+	mainlink_levels = dp_read(DP_MAINLINK_LEVELS);
+	mainlink_levels &= 0xFE0;
+	mainlink_levels |= safe_to_exit_level;
+
+	DP_DEBUG("mainlink_level = 0x%x, safe_to_exit_level = 0x%x\n",
+			mainlink_levels, safe_to_exit_level);
+
+	dp_write(DP_MAINLINK_LEVELS, mainlink_levels);
+}
+
+
+/* panel related catalog functions */
+static int dp_catalog_panel_timing_cfg(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 offset = 0, reg;
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		goto end;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		goto end;
+	}
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_1)
+		offset = DP1_TOTAL_HOR_VER - DP_TOTAL_HOR_VER;
+
+	dp_write(DP_TOTAL_HOR_VER + offset, panel->total);
+	dp_write(DP_START_HOR_VER_FROM_SYNC + offset, panel->sync_start);
+	dp_write(DP_HSYNC_VSYNC_WIDTH_POLARITY + offset, panel->width_blanking);
+	dp_write(DP_ACTIVE_HOR_VER + offset, panel->dp_active);
+
+	if (panel->stream_id == DP_STREAM_0)
+		io_data = catalog->io.dp_p0;
+	else
+		io_data = catalog->io.dp_p1;
+
+	reg = dp_read(MMSS_DP_INTF_CONFIG);
+
+	if (panel->widebus_en)
+		reg |= BIT(4);
+	else
+		reg &= ~BIT(4);
+
+	dp_write(MMSS_DP_INTF_CONFIG, reg);
+end:
+	return 0;
+}
+
+static void dp_catalog_hpd_config_hpd(struct dp_catalog_hpd *hpd, bool en)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!hpd) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv(hpd);
+	io_data = catalog->io.dp_aux;
+
+	if (en) {
+		u32 reftimer = dp_read(DP_DP_HPD_REFTIMER);
+
+		/* Arm only the UNPLUG and HPD_IRQ interrupts */
+		dp_write(DP_DP_HPD_INT_ACK, 0xF);
+		dp_write(DP_DP_HPD_INT_MASK, 0xA);
+
+		/* Enable REFTIMER to count 1ms */
+		reftimer |= BIT(16);
+		dp_write(DP_DP_HPD_REFTIMER, reftimer);
+
+		 /* Connect_time is 250us & disconnect_time is 2ms */
+		dp_write(DP_DP_HPD_EVENT_TIME_0, 0x3E800FA);
+		dp_write(DP_DP_HPD_EVENT_TIME_1, 0x1F407D0);
+
+		/* Enable HPD */
+		dp_write(DP_DP_HPD_CTRL, 0x1);
+
+	} else {
+		/* Disable HPD */
+		dp_write(DP_DP_HPD_CTRL, 0x0);
+	}
+}
+
+static u32 dp_catalog_hpd_get_interrupt(struct dp_catalog_hpd *hpd)
+{
+	u32 isr = 0;
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+
+	if (!hpd) {
+		DP_ERR("invalid input\n");
+		return isr;
+	}
+
+	catalog = dp_catalog_get_priv(hpd);
+
+	io_data = catalog->io.dp_aux;
+	isr = dp_read(DP_DP_HPD_INT_STATUS);
+	dp_write(DP_DP_HPD_INT_ACK, (isr & 0xf));
+
+	return isr;
+}
+
+static void dp_catalog_audio_init(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	static u32 sdp_map[][DP_AUDIO_SDP_HEADER_MAX] = {
+		{
+			MMSS_DP_AUDIO_STREAM_0,
+			MMSS_DP_AUDIO_STREAM_1,
+			MMSS_DP_AUDIO_STREAM_1,
+		},
+		{
+			MMSS_DP_AUDIO_TIMESTAMP_0,
+			MMSS_DP_AUDIO_TIMESTAMP_1,
+			MMSS_DP_AUDIO_TIMESTAMP_1,
+		},
+		{
+			MMSS_DP_AUDIO_INFOFRAME_0,
+			MMSS_DP_AUDIO_INFOFRAME_1,
+			MMSS_DP_AUDIO_INFOFRAME_1,
+		},
+		{
+			MMSS_DP_AUDIO_COPYMANAGEMENT_0,
+			MMSS_DP_AUDIO_COPYMANAGEMENT_1,
+			MMSS_DP_AUDIO_COPYMANAGEMENT_1,
+		},
+		{
+			MMSS_DP_AUDIO_ISRC_0,
+			MMSS_DP_AUDIO_ISRC_1,
+			MMSS_DP_AUDIO_ISRC_1,
+		},
+	};
+
+	if (!audio)
+		return;
+
+	catalog = dp_catalog_get_priv(audio);
+
+	catalog->audio_map = sdp_map;
+}
+
+static void dp_catalog_audio_config_sdp(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 sdp_cfg = 0, sdp_cfg_off = 0;
+	u32 sdp_cfg2 = 0, sdp_cfg2_off = 0;
+
+	if (!audio)
+		return;
+
+	if (audio->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream id:%d\n", audio->stream_id);
+		return;
+	}
+
+	if (audio->stream_id == DP_STREAM_1) {
+		sdp_cfg_off = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
+		sdp_cfg2_off = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
+	}
+
+	catalog = dp_catalog_get_priv(audio);
+	io_data = catalog->io.dp_link;
+
+	sdp_cfg = dp_read(MMSS_DP_SDP_CFG + sdp_cfg_off);
+
+	/* AUDIO_TIMESTAMP_SDP_EN */
+	sdp_cfg |= BIT(1);
+	/* AUDIO_STREAM_SDP_EN */
+	sdp_cfg |= BIT(2);
+	/* AUDIO_COPY_MANAGEMENT_SDP_EN */
+	sdp_cfg |= BIT(5);
+	/* AUDIO_ISRC_SDP_EN  */
+	sdp_cfg |= BIT(6);
+	/* AUDIO_INFOFRAME_SDP_EN  */
+	sdp_cfg |= BIT(20);
+
+	DP_DEBUG("sdp_cfg = 0x%x\n", sdp_cfg);
+	dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, sdp_cfg);
+
+	sdp_cfg2 = dp_read(MMSS_DP_SDP_CFG2 + sdp_cfg_off);
+	/* IFRM_REGSRC -> Do not use reg values */
+	sdp_cfg2 &= ~BIT(0);
+	/* AUDIO_STREAM_HB3_REGSRC-> Do not use reg values */
+	sdp_cfg2 &= ~BIT(1);
+
+	DP_DEBUG("sdp_cfg2 = 0x%x\n", sdp_cfg2);
+	dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg_off, sdp_cfg2);
+}
+
+static void dp_catalog_audio_get_header(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	u32 (*sdp_map)[DP_AUDIO_SDP_HEADER_MAX];
+	struct dp_io_data *io_data;
+	enum dp_catalog_audio_sdp_type sdp;
+	enum dp_catalog_audio_header_type header;
+
+	if (!audio)
+		return;
+
+	catalog = dp_catalog_get_priv(audio);
+
+	io_data    = catalog->io.dp_link;
+	sdp_map = catalog->audio_map;
+	sdp     = audio->sdp_type;
+	header  = audio->sdp_header;
+
+	audio->data = dp_read(sdp_map[sdp][header]);
+}
+
+static void dp_catalog_audio_set_header(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	u32 (*sdp_map)[DP_AUDIO_SDP_HEADER_MAX];
+	struct dp_io_data *io_data;
+	enum dp_catalog_audio_sdp_type sdp;
+	enum dp_catalog_audio_header_type header;
+	u32 data;
+
+	if (!audio)
+		return;
+
+	catalog = dp_catalog_get_priv(audio);
+
+	io_data    = catalog->io.dp_link;
+	sdp_map = catalog->audio_map;
+	sdp     = audio->sdp_type;
+	header  = audio->sdp_header;
+	data    = audio->data;
+
+	dp_write(sdp_map[sdp][header], data);
+}
+
+static void dp_catalog_audio_config_acr(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 acr_ctrl, select;
+
+	catalog = dp_catalog_get_priv(audio);
+
+	select = audio->data;
+	io_data   = catalog->io.dp_link;
+
+	acr_ctrl = select << 4 | BIT(31) | BIT(8) | BIT(14);
+
+	DP_DEBUG("select = 0x%x, acr_ctrl = 0x%x\n", select, acr_ctrl);
+
+	dp_write(MMSS_DP_AUDIO_ACR_CTRL, acr_ctrl);
+}
+
+static void dp_catalog_audio_enable(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	bool enable;
+	u32 audio_ctrl;
+
+	catalog = dp_catalog_get_priv(audio);
+
+	io_data = catalog->io.dp_link;
+	enable = !!audio->data;
+
+	audio_ctrl = dp_read(MMSS_DP_AUDIO_CFG);
+
+	if (enable)
+		audio_ctrl |= BIT(0);
+	else
+		audio_ctrl &= ~BIT(0);
+
+	DP_DEBUG("dp_audio_cfg = 0x%x\n", audio_ctrl);
+	dp_write(MMSS_DP_AUDIO_CFG, audio_ctrl);
+
+	/* make sure audio engine is disabled */
+	wmb();
+}
+
+static void dp_catalog_config_spd_header(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 value, new_value, offset = 0;
+	u8 parity_byte;
+
+	if (!panel || panel->stream_id >= DP_STREAM_MAX)
+		return;
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_1)
+		offset = MMSS_DP1_GENERIC0_0 - MMSS_DP_GENERIC0_0;
+
+	/* Config header and parity byte 1 */
+	value = dp_read(MMSS_DP_GENERIC1_0 + offset);
+
+	new_value = 0x83;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_1_BIT)
+			| (parity_byte << PARITY_BYTE_1_BIT));
+	DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_write(MMSS_DP_GENERIC1_0 + offset, value);
+
+	/* Config header and parity byte 2 */
+	value = dp_read(MMSS_DP_GENERIC1_1 + offset);
+
+	new_value = 0x1b;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_2_BIT)
+			| (parity_byte << PARITY_BYTE_2_BIT));
+	DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_write(MMSS_DP_GENERIC1_1 + offset, value);
+
+	/* Config header and parity byte 3 */
+	value = dp_read(MMSS_DP_GENERIC1_1 + offset);
+
+	new_value = (0x0 | (0x12 << 2));
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_3_BIT)
+			| (parity_byte << PARITY_BYTE_3_BIT));
+	DP_DEBUG("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
+			new_value, parity_byte);
+	dp_write(MMSS_DP_GENERIC1_1 + offset, value);
+}
+
+static void dp_catalog_panel_config_spd(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct dp_io_data *io_data;
+	u32 spd_cfg = 0, spd_cfg2 = 0;
+	u8 *vendor = NULL, *product = NULL;
+	u32 offset = 0;
+	u32 sdp_cfg_off = 0;
+	u32 sdp_cfg2_off = 0;
+
+	/*
+	 * Source Device Information
+	 * 00h unknown
+	 * 01h Digital STB
+	 * 02h DVD
+	 * 03h D-VHS
+	 * 04h HDD Video
+	 * 05h DVC
+	 * 06h DSC
+	 * 07h Video CD
+	 * 08h Game
+	 * 09h PC general
+	 * 0ah Bluray-Disc
+	 * 0bh Super Audio CD
+	 * 0ch HD DVD
+	 * 0dh PMP
+	 * 0eh-ffh reserved
+	 */
+	u32 device_type = 0;
+
+	if (!panel || panel->stream_id >= DP_STREAM_MAX)
+		return;
+
+	catalog = dp_catalog_get_priv(panel);
+	io_data = catalog->io.dp_link;
+
+	if (panel->stream_id == DP_STREAM_1)
+		offset = MMSS_DP1_GENERIC0_0 - MMSS_DP_GENERIC0_0;
+
+	dp_catalog_config_spd_header(panel);
+
+	vendor = panel->spd_vendor_name;
+	product = panel->spd_product_description;
+
+	dp_write(MMSS_DP_GENERIC1_2 + offset,
+			((vendor[0] & 0x7f) |
+			((vendor[1] & 0x7f) << 8) |
+			((vendor[2] & 0x7f) << 16) |
+			((vendor[3] & 0x7f) << 24)));
+	dp_write(MMSS_DP_GENERIC1_3 + offset,
+			((vendor[4] & 0x7f) |
+			((vendor[5] & 0x7f) << 8) |
+			((vendor[6] & 0x7f) << 16) |
+			((vendor[7] & 0x7f) << 24)));
+	dp_write(MMSS_DP_GENERIC1_4 + offset,
+			((product[0] & 0x7f) |
+			((product[1] & 0x7f) << 8) |
+			((product[2] & 0x7f) << 16) |
+			((product[3] & 0x7f) << 24)));
+	dp_write(MMSS_DP_GENERIC1_5 + offset,
+			((product[4] & 0x7f) |
+			((product[5] & 0x7f) << 8) |
+			((product[6] & 0x7f) << 16) |
+			((product[7] & 0x7f) << 24)));
+	dp_write(MMSS_DP_GENERIC1_6 + offset,
+			((product[8] & 0x7f) |
+			((product[9] & 0x7f) << 8) |
+			((product[10] & 0x7f) << 16) |
+			((product[11] & 0x7f) << 24)));
+	dp_write(MMSS_DP_GENERIC1_7 + offset,
+			((product[12] & 0x7f) |
+			((product[13] & 0x7f) << 8) |
+			((product[14] & 0x7f) << 16) |
+			((product[15] & 0x7f) << 24)));
+	dp_write(MMSS_DP_GENERIC1_8 + offset, device_type);
+	dp_write(MMSS_DP_GENERIC1_9 + offset, 0x00);
+
+	if (panel->stream_id == DP_STREAM_1) {
+		sdp_cfg_off = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
+		sdp_cfg2_off = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
+	}
+
+	spd_cfg = dp_read(MMSS_DP_SDP_CFG + sdp_cfg_off);
+	/* GENERIC1_SDP for SPD Infoframe */
+	spd_cfg |= BIT(18);
+	dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, spd_cfg);
+
+	spd_cfg2 = dp_read(MMSS_DP_SDP_CFG2 + sdp_cfg2_off);
+	/* 28 data bytes for SPD Infoframe with GENERIC1 set */
+	spd_cfg2 |= BIT(17);
+	dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg2_off, spd_cfg2);
+
+	dp_catalog_panel_sdp_update(panel);
+}
+
+static void dp_catalog_get_io_buf(struct dp_catalog_private *catalog)
+{
+	struct dp_parser *parser = catalog->parser;
+
+	dp_catalog_fill_io_buf(dp_ahb);
+	dp_catalog_fill_io_buf(dp_aux);
+	dp_catalog_fill_io_buf(dp_link);
+	dp_catalog_fill_io_buf(dp_p0);
+	dp_catalog_fill_io_buf(dp_phy);
+	dp_catalog_fill_io_buf(dp_ln_tx0);
+	dp_catalog_fill_io_buf(dp_ln_tx1);
+	dp_catalog_fill_io_buf(dp_pll);
+	dp_catalog_fill_io_buf(usb3_dp_com);
+	dp_catalog_fill_io_buf(dp_mmss_cc);
+	dp_catalog_fill_io_buf(hdcp_physical);
+	dp_catalog_fill_io_buf(dp_p1);
+	dp_catalog_fill_io_buf(dp_tcsr);
+}
+
+static void dp_catalog_get_io(struct dp_catalog_private *catalog)
+{
+	struct dp_parser *parser = catalog->parser;
+
+	dp_catalog_fill_io(dp_ahb);
+	dp_catalog_fill_io(dp_aux);
+	dp_catalog_fill_io(dp_link);
+	dp_catalog_fill_io(dp_p0);
+	dp_catalog_fill_io(dp_phy);
+	dp_catalog_fill_io(dp_ln_tx0);
+	dp_catalog_fill_io(dp_ln_tx1);
+	dp_catalog_fill_io(dp_pll);
+	dp_catalog_fill_io(usb3_dp_com);
+	dp_catalog_fill_io(dp_mmss_cc);
+	dp_catalog_fill_io(hdcp_physical);
+	dp_catalog_fill_io(dp_p1);
+	dp_catalog_fill_io(dp_tcsr);
+}
+
+static void dp_catalog_set_exe_mode(struct dp_catalog *dp_catalog, char *mode)
+{
+	struct dp_catalog_private *catalog;
+
+	if (!dp_catalog) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = container_of(dp_catalog, struct dp_catalog_private,
+		dp_catalog);
+
+	strlcpy(catalog->exe_mode, mode, sizeof(catalog->exe_mode));
+
+	if (!strcmp(catalog->exe_mode, "hw"))
+		catalog->parser->clear_io_buf(catalog->parser);
+	else
+		dp_catalog_get_io_buf(catalog);
+
+	if (!strcmp(catalog->exe_mode, "hw") ||
+		!strcmp(catalog->exe_mode, "all")) {
+		catalog->read = dp_read_hw;
+		catalog->write = dp_write_hw;
+
+		dp_catalog->sub->read = dp_read_sub_hw;
+		dp_catalog->sub->write = dp_write_sub_hw;
+	} else {
+		catalog->read = dp_read_sw;
+		catalog->write = dp_write_sw;
+
+		dp_catalog->sub->read = dp_read_sub_sw;
+		dp_catalog->sub->write = dp_write_sub_sw;
+	}
+}
+
+static int dp_catalog_init(struct device *dev, struct dp_catalog *dp_catalog,
+			struct dp_parser *parser)
+{
+	int rc = 0;
+	struct dp_catalog_private *catalog = container_of(dp_catalog,
+				struct dp_catalog_private, dp_catalog);
+
+	if (parser->hw_cfg.phy_version >= DP_PHY_VERSION_4_2_0)
+		dp_catalog->sub = dp_catalog_get_v420(dev, dp_catalog, &catalog->io);
+	else if (parser->hw_cfg.phy_version == DP_PHY_VERSION_2_0_0)
+		dp_catalog->sub = dp_catalog_get_v200(dev, dp_catalog, &catalog->io);
+	else
+		goto end;
+
+	if (IS_ERR(dp_catalog->sub)) {
+		rc = PTR_ERR(dp_catalog->sub);
+		dp_catalog->sub = NULL;
+	} else {
+		dp_catalog->sub->read = dp_read_sub_hw;
+		dp_catalog->sub->write = dp_write_sub_hw;
+	}
+end:
+	return rc;
+}
+
+void dp_catalog_put(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog;
+
+	if (!dp_catalog)
+		return;
+
+	catalog = container_of(dp_catalog, struct dp_catalog_private,
+				dp_catalog);
+
+	if (dp_catalog->sub && dp_catalog->sub->put)
+		dp_catalog->sub->put(dp_catalog);
+
+	catalog->parser->clear_io_buf(catalog->parser);
+	devm_kfree(catalog->dev, catalog);
+}
+
+struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser)
+{
+	int rc = 0;
+	struct dp_catalog *dp_catalog;
+	struct dp_catalog_private *catalog;
+	struct dp_catalog_aux aux = {
+		.read_data     = dp_catalog_aux_read_data,
+		.write_data    = dp_catalog_aux_write_data,
+		.write_trans   = dp_catalog_aux_write_trans,
+		.clear_trans   = dp_catalog_aux_clear_trans,
+		.reset         = dp_catalog_aux_reset,
+		.update_aux_cfg = dp_catalog_aux_update_cfg,
+		.enable        = dp_catalog_aux_enable,
+		.setup         = dp_catalog_aux_setup,
+		.get_irq       = dp_catalog_aux_get_irq,
+		.clear_hw_interrupts = dp_catalog_aux_clear_hw_interrupts,
+	};
+	struct dp_catalog_ctrl ctrl = {
+		.state_ctrl     = dp_catalog_ctrl_state_ctrl,
+		.config_ctrl    = dp_catalog_ctrl_config_ctrl,
+		.lane_mapping   = dp_catalog_ctrl_lane_mapping,
+		.lane_pnswap    = dp_catalog_ctrl_lane_pnswap,
+		.mainlink_ctrl  = dp_catalog_ctrl_mainlink_ctrl,
+		.set_pattern    = dp_catalog_ctrl_set_pattern,
+		.reset          = dp_catalog_ctrl_reset,
+		.usb_reset      = dp_catalog_ctrl_usb_reset,
+		.mainlink_ready = dp_catalog_ctrl_mainlink_ready,
+		.enable_irq     = dp_catalog_ctrl_enable_irq,
+		.phy_reset      = dp_catalog_ctrl_phy_reset,
+		.phy_lane_cfg   = dp_catalog_ctrl_phy_lane_cfg,
+		.update_vx_px   = dp_catalog_ctrl_update_vx_px,
+		.get_interrupt  = dp_catalog_ctrl_get_interrupt,
+		.read_hdcp_status     = dp_catalog_ctrl_read_hdcp_status,
+		.send_phy_pattern    = dp_catalog_ctrl_send_phy_pattern,
+		.read_phy_pattern = dp_catalog_ctrl_read_phy_pattern,
+		.mst_config = dp_catalog_ctrl_mst_config,
+		.trigger_act = dp_catalog_ctrl_trigger_act,
+		.read_act_complete_sts = dp_catalog_ctrl_read_act_complete_sts,
+		.channel_alloc = dp_catalog_ctrl_channel_alloc,
+		.update_rg = dp_catalog_ctrl_update_rg,
+		.channel_dealloc = dp_catalog_ctrl_channel_dealloc,
+		.fec_config = dp_catalog_ctrl_fec_config,
+		.mainlink_levels = dp_catalog_ctrl_mainlink_levels,
+		.late_phy_init = dp_catalog_ctrl_late_phy_init,
+		.setup_misr = dp_catalog_ctrl_setup_misr,
+		.read_misr = dp_catalog_ctrl_read_misr,
+	};
+	struct dp_catalog_hpd hpd = {
+		.config_hpd	= dp_catalog_hpd_config_hpd,
+		.get_interrupt	= dp_catalog_hpd_get_interrupt,
+	};
+	struct dp_catalog_audio audio = {
+		.init       = dp_catalog_audio_init,
+		.config_acr = dp_catalog_audio_config_acr,
+		.enable     = dp_catalog_audio_enable,
+		.config_sdp = dp_catalog_audio_config_sdp,
+		.set_header = dp_catalog_audio_set_header,
+		.get_header = dp_catalog_audio_get_header,
+	};
+	struct dp_catalog_panel panel = {
+		.timing_cfg = dp_catalog_panel_timing_cfg,
+		.config_hdr = dp_catalog_panel_config_hdr,
+		.config_sdp = dp_catalog_panel_config_sdp,
+		.tpg_config = dp_catalog_panel_tpg_cfg,
+		.config_spd = dp_catalog_panel_config_spd,
+		.config_misc = dp_catalog_panel_config_misc,
+		.set_colorspace = dp_catalog_panel_set_colorspace,
+		.config_msa = dp_catalog_panel_config_msa,
+		.update_transfer_unit = dp_catalog_panel_update_transfer_unit,
+		.config_ctrl = dp_catalog_panel_config_ctrl,
+		.config_dto = dp_catalog_panel_config_dto,
+		.dsc_cfg = dp_catalog_panel_dsc_cfg,
+		.pps_flush = dp_catalog_panel_pps_flush,
+		.dhdr_flush = dp_catalog_panel_dhdr_flush,
+		.dhdr_busy = dp_catalog_panel_dhdr_busy,
+		.get_src_crc = dp_catalog_panel_get_src_crc,
+	};
+
+	if (!dev || !parser) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	catalog  = devm_kzalloc(dev, sizeof(*catalog), GFP_KERNEL);
+	if (!catalog) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	catalog->dev = dev;
+	catalog->parser = parser;
+
+	catalog->read = dp_read_hw;
+	catalog->write = dp_write_hw;
+
+	dp_catalog_get_io(catalog);
+
+	strlcpy(catalog->exe_mode, "hw", sizeof(catalog->exe_mode));
+
+	if (parser->valid_lt_params) {
+		ctrl.swing_hbr2_3 = parser->swing_hbr2_3;
+		ctrl.pre_emp_hbr2_3 = parser->pre_emp_hbr2_3;
+
+		ctrl.swing_hbr_rbr = parser->swing_hbr_rbr;
+		ctrl.pre_emp_hbr_rbr = parser->pre_emp_hbr_rbr;
+		ctrl.valid_lt_params = true;
+	} else {
+		ctrl.swing_hbr2_3 = NULL;
+		ctrl.pre_emp_hbr2_3 = NULL;
+
+		ctrl.swing_hbr_rbr = NULL;
+		ctrl.pre_emp_hbr_rbr = NULL;
+		ctrl.valid_lt_params = false;
+	}
+
+	dp_catalog = &catalog->dp_catalog;
+
+	dp_catalog->aux   = aux;
+	dp_catalog->ctrl  = ctrl;
+	dp_catalog->hpd   = hpd;
+	dp_catalog->audio = audio;
+	dp_catalog->panel = panel;
+
+	rc = dp_catalog_init(dev, dp_catalog, parser);
+	if (rc) {
+		dp_catalog_put(dp_catalog);
+		goto error;
+	}
+
+	dp_catalog->set_exe_mode = dp_catalog_set_exe_mode;
+	dp_catalog->get_reg_dump = dp_catalog_reg_dump;
+
+	return dp_catalog;
+error:
+	return ERR_PTR(rc);
+}

+ 376 - 0
qcom/opensource/display-drivers/msm/dp/dp_catalog.h

@@ -0,0 +1,376 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_CATALOG_H_
+#define _DP_CATALOG_H_
+
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0))
+#include <drm/display/drm_dp_helper.h>
+#else
+#include <drm/drm_dp_helper.h>
+#endif
+#include <drm/sde_drm.h>
+
+#include "dp_parser.h"
+
+/* interrupts */
+#define DP_INTR_HPD		BIT(0)
+#define DP_INTR_AUX_I2C_DONE	BIT(3)
+#define DP_INTR_WRONG_ADDR	BIT(6)
+#define DP_INTR_TIMEOUT		BIT(9)
+#define DP_INTR_NACK_DEFER	BIT(12)
+#define DP_INTR_WRONG_DATA_CNT	BIT(15)
+#define DP_INTR_I2C_NACK	BIT(18)
+#define DP_INTR_I2C_DEFER	BIT(21)
+#define DP_INTR_PLL_UNLOCKED	BIT(24)
+#define DP_INTR_AUX_ERROR	BIT(27)
+
+#define DP_INTR_READY_FOR_VIDEO		BIT(0)
+#define DP_INTR_IDLE_PATTERN_SENT	BIT(3)
+#define DP_INTR_FRAME_END		BIT(6)
+#define DP_INTR_CRC_UPDATED		BIT(9)
+#define DP_INTR_SST_FIFO_UNDERFLOW	BIT(28)
+
+#define DP_INTR_MST_DP0_VCPF_SENT	BIT(0)
+#define DP_INTR_MST_DP1_VCPF_SENT	BIT(3)
+
+#define DP_INTR_SST_ML_FIFO_OVERFLOW	BIT(12)
+#define DP_INTR_MST0_ML_FIFO_OVERFLOW	BIT(15)
+#define DP_INTR_MST1_ML_FIFO_OVERFLOW	BIT(18)
+#define DP_INTR_DP1_FRAME_END		BIT(21)
+#define DP_INTR_SDP0_COLLISION		BIT(24)
+#define DP_INTR_SDP1_COLLISION		BIT(27)
+
+#define DP_INTR_DP0_BACKPRESSURE_ERROR	(BIT(1) | BIT(0))
+#define DP_INTR_DP1_BACKPRESSURE_ERROR	(BIT(5) | BIT(4))
+#define DP_INTR_SST_BS_LATE	BIT(8)
+
+
+#define DP_MAX_TIME_SLOTS	64
+
+/* stream id */
+enum dp_stream_id {
+	DP_STREAM_0,
+	DP_STREAM_1,
+	DP_STREAM_MAX,
+};
+
+struct dp_catalog_vsc_sdp_colorimetry {
+	struct dp_sdp_header header;
+	u8 data[32];
+};
+
+struct dp_misr40_data {
+	u32 ctrl_misr[8];
+	u32 phy_misr[8];
+};
+
+struct dp_catalog_aux {
+	u32 data;
+	u32 isr;
+
+	u32 (*read_data)(struct dp_catalog_aux *aux);
+	int (*write_data)(struct dp_catalog_aux *aux);
+	int (*write_trans)(struct dp_catalog_aux *aux);
+	int (*clear_trans)(struct dp_catalog_aux *aux, bool read);
+	void (*reset)(struct dp_catalog_aux *aux);
+	void (*enable)(struct dp_catalog_aux *aux, bool enable);
+	void (*update_aux_cfg)(struct dp_catalog_aux *aux,
+		struct dp_aux_cfg *cfg, enum dp_phy_aux_config_type type);
+	void (*setup)(struct dp_catalog_aux *aux,
+			struct dp_aux_cfg *aux_cfg);
+	void (*get_irq)(struct dp_catalog_aux *aux, bool cmd_busy);
+	void (*clear_hw_interrupts)(struct dp_catalog_aux *aux);
+};
+
+struct dp_catalog_ctrl {
+	u32 isr;
+	u32 isr3;
+	u32 isr5;
+	u32 isr6;
+
+	u8 *swing_hbr2_3;
+	u8 *pre_emp_hbr2_3;
+	u8 *swing_hbr_rbr;
+	u8 *pre_emp_hbr_rbr;
+	bool valid_lt_params;
+
+	void (*state_ctrl)(struct dp_catalog_ctrl *ctrl, u32 state);
+	void (*config_ctrl)(struct dp_catalog_ctrl *ctrl, u8 ln_cnt);
+	void (*lane_mapping)(struct dp_catalog_ctrl *ctrl, bool flipped,
+				char *lane_map);
+	void (*lane_pnswap)(struct dp_catalog_ctrl *ctrl, u8 ln_pnswap);
+	void (*mainlink_ctrl)(struct dp_catalog_ctrl *ctrl, bool enable);
+	void (*set_pattern)(struct dp_catalog_ctrl *ctrl, u32 pattern);
+	void (*reset)(struct dp_catalog_ctrl *ctrl);
+	void (*usb_reset)(struct dp_catalog_ctrl *ctrl, bool flip);
+	bool (*mainlink_ready)(struct dp_catalog_ctrl *ctrl);
+	void (*enable_irq)(struct dp_catalog_ctrl *ctrl, bool enable);
+	void (*phy_reset)(struct dp_catalog_ctrl *ctrl);
+	void (*phy_lane_cfg)(struct dp_catalog_ctrl *ctrl, bool flipped,
+				u8 lane_cnt);
+	void (*update_vx_px)(struct dp_catalog_ctrl *ctrl, u8 v_level,
+				u8 p_level, bool high);
+	void (*get_interrupt)(struct dp_catalog_ctrl *ctrl);
+	u32 (*read_hdcp_status)(struct dp_catalog_ctrl *ctrl);
+	void (*send_phy_pattern)(struct dp_catalog_ctrl *ctrl,
+			u32 pattern);
+	u32 (*read_phy_pattern)(struct dp_catalog_ctrl *ctrl);
+	void (*mst_config)(struct dp_catalog_ctrl *ctrl, bool enable);
+	void (*trigger_act)(struct dp_catalog_ctrl *ctrl);
+	void (*read_act_complete_sts)(struct dp_catalog_ctrl *ctrl, bool *sts);
+	void (*channel_alloc)(struct dp_catalog_ctrl *ctrl,
+			u32 ch, u32 ch_start_timeslot, u32 tot_ch_cnt);
+	void (*update_rg)(struct dp_catalog_ctrl *ctrl, u32 ch, u32 x_int,
+			u32 y_frac_enum);
+	void (*channel_dealloc)(struct dp_catalog_ctrl *ctrl,
+			u32 ch, u32 ch_start_timeslot, u32 tot_ch_cnt);
+	void (*fec_config)(struct dp_catalog_ctrl *ctrl, bool enable);
+	void (*mainlink_levels)(struct dp_catalog_ctrl *ctrl, u8 lane_cnt);
+
+	int (*late_phy_init)(struct dp_catalog_ctrl *ctrl,
+					u8 lane_cnt, bool flipped);
+	int (*setup_misr)(struct dp_catalog_ctrl *ctrl);
+	int (*read_misr)(struct dp_catalog_ctrl *ctrl, struct dp_misr40_data *data);
+};
+
+struct dp_catalog_hpd {
+	void (*config_hpd)(struct dp_catalog_hpd *hpd, bool en);
+	u32 (*get_interrupt)(struct dp_catalog_hpd *hpd);
+};
+
+#define HEADER_BYTE_2_BIT	 0
+#define PARITY_BYTE_2_BIT	 8
+#define HEADER_BYTE_1_BIT	16
+#define PARITY_BYTE_1_BIT	24
+#define HEADER_BYTE_3_BIT	16
+#define PARITY_BYTE_3_BIT	24
+
+enum dp_catalog_audio_sdp_type {
+	DP_AUDIO_SDP_STREAM,
+	DP_AUDIO_SDP_TIMESTAMP,
+	DP_AUDIO_SDP_INFOFRAME,
+	DP_AUDIO_SDP_COPYMANAGEMENT,
+	DP_AUDIO_SDP_ISRC,
+	DP_AUDIO_SDP_MAX,
+};
+
+enum dp_catalog_audio_header_type {
+	DP_AUDIO_SDP_HEADER_1,
+	DP_AUDIO_SDP_HEADER_2,
+	DP_AUDIO_SDP_HEADER_3,
+	DP_AUDIO_SDP_HEADER_MAX,
+};
+
+struct dp_catalog_audio {
+	enum dp_catalog_audio_sdp_type sdp_type;
+	enum dp_catalog_audio_header_type sdp_header;
+	u32 data;
+
+	enum dp_stream_id stream_id;
+
+	void (*init)(struct dp_catalog_audio *audio);
+	void (*enable)(struct dp_catalog_audio *audio);
+	void (*config_acr)(struct dp_catalog_audio *audio);
+	void (*config_sdp)(struct dp_catalog_audio *audio);
+	void (*set_header)(struct dp_catalog_audio *audio);
+	void (*get_header)(struct dp_catalog_audio *audio);
+};
+
+struct dp_dsc_cfg_data {
+	bool dsc_en;
+	bool continuous_pps;
+	char pps[128];
+	u32 pps_len;
+	u32 pps_word[32];
+	u32 pps_word_len;
+	u8 parity[32];
+	u8 parity_len;
+	u32 parity_word[8];
+	u32 parity_word_len;
+	u32 slice_per_pkt;
+	u32 bytes_per_pkt;
+	u32 eol_byte_num;
+	u32 be_in_lane;
+	u32 dto_en;
+	u32 dto_n;
+	u32 dto_d;
+	u32 dto_count;
+};
+
+struct dp_catalog_panel {
+	u32 total;
+	u32 sync_start;
+	u32 width_blanking;
+	u32 dp_active;
+	u8 *spd_vendor_name;
+	u8 *spd_product_description;
+
+	struct dp_catalog_vsc_sdp_colorimetry vsc_colorimetry;
+	struct dp_sdp_header dhdr_vsif_sdp;
+	struct dp_sdp_header shdr_if_sdp;
+	struct drm_msm_ext_hdr_metadata hdr_meta;
+
+	/* TPG */
+	u32 hsync_period;
+	u32 vsync_period;
+	u32 display_v_start;
+	u32 display_v_end;
+	u32 v_sync_width;
+	u32 hsync_ctl;
+	u32 display_hctl;
+
+	/* TU */
+	u32 dp_tu;
+	u32 valid_boundary;
+	u32 valid_boundary2;
+
+	u32 misc_val;
+
+	enum dp_stream_id stream_id;
+
+	bool widebus_en;
+	struct dp_dsc_cfg_data dsc;
+
+	int (*timing_cfg)(struct dp_catalog_panel *panel);
+	void (*config_hdr)(struct dp_catalog_panel *panel, bool en,
+		u32 dhdr_max_pkts, bool flush);
+	void (*config_sdp)(struct dp_catalog_panel *panel, bool en);
+	int (*set_colorspace)(struct dp_catalog_panel *panel,
+		 bool vsc_supported);
+	void (*tpg_config)(struct dp_catalog_panel *panel, u32  pattern);
+	void (*config_spd)(struct dp_catalog_panel *panel);
+	void (*config_misc)(struct dp_catalog_panel *panel);
+	void (*config_msa)(struct dp_catalog_panel *panel,
+			u32 rate, u32 stream_rate_khz);
+	void (*update_transfer_unit)(struct dp_catalog_panel *panel);
+	void (*config_ctrl)(struct dp_catalog_panel *panel, u32 cfg);
+	void (*config_dto)(struct dp_catalog_panel *panel, bool ack);
+	void (*dsc_cfg)(struct dp_catalog_panel *panel);
+	void (*pps_flush)(struct dp_catalog_panel *panel);
+	void (*dhdr_flush)(struct dp_catalog_panel *panel);
+	bool (*dhdr_busy)(struct dp_catalog_panel *panel);
+	int (*get_src_crc)(struct dp_catalog_panel *panel, u16 *crc);
+};
+
+struct dp_catalog;
+struct dp_catalog_sub {
+	u32 (*read)(struct dp_catalog *dp_catalog,
+		struct dp_io_data *io_data, u32 offset);
+	void (*write)(struct dp_catalog *dp_catalog,
+		struct dp_io_data *io_data, u32 offset, u32 data);
+
+	void (*put)(struct dp_catalog *catalog);
+};
+
+struct dp_catalog_io {
+	struct dp_io_data *dp_ahb;
+	struct dp_io_data *dp_aux;
+	struct dp_io_data *dp_link;
+	struct dp_io_data *dp_p0;
+	struct dp_io_data *dp_phy;
+	struct dp_io_data *dp_ln_tx0;
+	struct dp_io_data *dp_ln_tx1;
+	struct dp_io_data *dp_mmss_cc;
+	struct dp_io_data *dp_pll;
+	struct dp_io_data *usb3_dp_com;
+	struct dp_io_data *hdcp_physical;
+	struct dp_io_data *dp_p1;
+	struct dp_io_data *dp_tcsr;
+};
+
+struct dp_catalog {
+	struct dp_catalog_aux aux;
+	struct dp_catalog_ctrl ctrl;
+	struct dp_catalog_audio audio;
+	struct dp_catalog_panel panel;
+	struct dp_catalog_hpd hpd;
+
+	struct dp_catalog_sub *sub;
+
+	void (*set_exe_mode)(struct dp_catalog *dp_catalog, char *mode);
+	int (*get_reg_dump)(struct dp_catalog *dp_catalog,
+		char *mode, u8 **out_buf, u32 *out_buf_len);
+};
+
+static inline u8 dp_ecc_get_g0_value(u8 data)
+{
+	u8 c[4];
+	u8 g[4];
+	u8 ret_data = 0;
+	u8 i;
+
+	for (i = 0; i < 4; i++)
+		c[i] = (data >> i) & 0x01;
+
+	g[0] = c[3];
+	g[1] = c[0] ^ c[3];
+	g[2] = c[1];
+	g[3] = c[2];
+
+	for (i = 0; i < 4; i++)
+		ret_data = ((g[i] & 0x01) << i) | ret_data;
+
+	return ret_data;
+}
+
+static inline u8 dp_ecc_get_g1_value(u8 data)
+{
+	u8 c[4];
+	u8 g[4];
+	u8 ret_data = 0;
+	u8 i;
+
+	for (i = 0; i < 4; i++)
+		c[i] = (data >> i) & 0x01;
+
+	g[0] = c[0] ^ c[3];
+	g[1] = c[0] ^ c[1] ^ c[3];
+	g[2] = c[1] ^ c[2];
+	g[3] = c[2] ^ c[3];
+
+	for (i = 0; i < 4; i++)
+		ret_data = ((g[i] & 0x01) << i) | ret_data;
+
+	return ret_data;
+}
+
+static inline u8 dp_header_get_parity(u32 data)
+{
+	u8 x0 = 0;
+	u8 x1 = 0;
+	u8 ci = 0;
+	u8 iData = 0;
+	u8 i = 0;
+	u8 parity_byte;
+	u8 num_byte = (data > 0xFF) ? 8 : 2;
+
+	for (i = 0; i < num_byte; i++) {
+		iData = (data >> i*4) & 0xF;
+
+		ci = iData ^ x1;
+		x1 = x0 ^ dp_ecc_get_g1_value(ci);
+		x0 = dp_ecc_get_g0_value(ci);
+	}
+
+	parity_byte = x1 | (x0 << 4);
+
+	return parity_byte;
+}
+
+struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser);
+void dp_catalog_put(struct dp_catalog *catalog);
+
+struct dp_catalog_sub *dp_catalog_get_v420(struct device *dev,
+			struct dp_catalog *catalog, struct dp_catalog_io *io);
+
+struct dp_catalog_sub *dp_catalog_get_v200(struct device *dev,
+			struct dp_catalog *catalog, struct dp_catalog_io *io);
+
+u32 dp_catalog_get_dp_core_version(struct dp_catalog *dp_catalog);
+u32 dp_catalog_get_dp_phy_version(struct dp_catalog *dp_catalog);
+#endif /* _DP_CATALOG_H_ */

+ 272 - 0
qcom/opensource/display-drivers/msm/dp/dp_catalog_v200.c

@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/delay.h>
+
+#include "dp_catalog.h"
+#include "dp_reg.h"
+#include "dp_debug.h"
+
+#define dp_catalog_get_priv_v200(x) ({ \
+	struct dp_catalog *catalog; \
+	catalog = container_of(x, struct dp_catalog, x); \
+	container_of(catalog->sub, \
+		struct dp_catalog_private_v200, sub); \
+})
+
+#define dp_read(x) ({ \
+	catalog->sub.read(catalog->dpc, io_data, x); \
+})
+
+#define dp_write(x, y) ({ \
+	catalog->sub.write(catalog->dpc, io_data, x, y); \
+})
+
+struct dp_catalog_private_v200 {
+	struct device *dev;
+	struct dp_catalog_io *io;
+	struct dp_catalog *dpc;
+	struct dp_catalog_sub sub;
+};
+
+static void dp_catalog_aux_clear_hw_int_v200(struct dp_catalog_aux *aux)
+{
+	struct dp_catalog_private_v200 *catalog;
+	struct dp_io_data *io_data;
+	u32 data = 0;
+
+	if (!aux) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv_v200(aux);
+	io_data = catalog->io->dp_phy;
+
+	data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS_V200);
+
+	dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V200, 0x1f);
+	wmb(); /* make sure 0x1f is written before next write */
+
+	dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V200, 0x9f);
+	wmb(); /* make sure 0x9f is written before next write */
+
+	dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V200, 0);
+	wmb(); /* make sure register is cleared */
+}
+
+static void dp_catalog_aux_setup_v200(struct dp_catalog_aux *aux,
+		struct dp_aux_cfg *cfg)
+{
+	struct dp_catalog_private_v200 *catalog;
+	struct dp_io_data *io_data;
+	int i = 0, sw_reset = 0;
+
+	if (!aux || !cfg) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv_v200(aux);
+	io_data = catalog->io->dp_ahb;
+
+	sw_reset = dp_read(DP_SW_RESET);
+
+	sw_reset |= BIT(0);
+	dp_write(DP_SW_RESET, sw_reset);
+	usleep_range(1000, 1010); /* h/w recommended delay */
+
+	sw_reset &= ~BIT(0);
+	dp_write(DP_SW_RESET, sw_reset);
+
+	dp_write(DP_PHY_CTRL, 0x4); /* bit 2 */
+	udelay(1000);
+	dp_write(DP_PHY_CTRL, 0x0); /* bit 2 */
+	wmb(); /* make sure programming happened */
+
+	io_data = catalog->io->dp_tcsr;
+	dp_write(0x4c, 0x1); /* bit 0 & 2 */
+	wmb(); /* make sure programming happened */
+
+	io_data = catalog->io->dp_phy;
+	dp_write(DP_PHY_PD_CTL, 0x3c);
+	wmb(); /* make sure PD programming happened */
+	dp_write(DP_PHY_PD_CTL, 0x3d);
+	wmb(); /* make sure PD programming happened */
+
+	/* DP AUX CFG register programming */
+	io_data = catalog->io->dp_phy;
+	for (i = 0; i < PHY_AUX_CFG_MAX; i++)
+		dp_write(cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
+
+	dp_write(DP_PHY_AUX_INTERRUPT_MASK_V200, 0x1F);
+	wmb(); /* make sure AUX configuration is done before enabling it */
+}
+
+static void dp_catalog_panel_config_msa_v200(struct dp_catalog_panel *panel,
+					u32 rate, u32 stream_rate_khz)
+{
+	u32 pixel_m, pixel_n;
+	u32 mvid, nvid;
+	u32 const nvid_fixed = 0x8000;
+	u32 const link_rate_hbr2 = 540000;
+	u32 const link_rate_hbr3 = 810000;
+	struct dp_catalog_private_v200 *catalog;
+	struct dp_io_data *io_data;
+	u32 strm_reg_off = 0;
+	u32 mvid_reg_off = 0, nvid_reg_off = 0;
+
+	if (!panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream_id:%d\n", panel->stream_id);
+		return;
+	}
+
+	catalog = dp_catalog_get_priv_v200(panel);
+	io_data = catalog->io->dp_mmss_cc;
+
+	if (panel->stream_id == DP_STREAM_1)
+		strm_reg_off = MMSS_DP_PIXEL1_M_V200 -
+					MMSS_DP_PIXEL_M_V200;
+
+	pixel_m = dp_read(MMSS_DP_PIXEL_M_V200 + strm_reg_off);
+	pixel_n = dp_read(MMSS_DP_PIXEL_N_V200 + strm_reg_off);
+	DP_DEBUG("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);
+
+	mvid = (pixel_m & 0xFFFF) * 5;
+	nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
+
+	if (nvid < nvid_fixed) {
+		u32 temp;
+
+		temp = (nvid_fixed / nvid) * nvid;
+		mvid = (nvid_fixed / nvid) * mvid;
+		nvid = temp;
+	}
+
+	DP_DEBUG("rate = %d\n", rate);
+
+	if (panel->widebus_en)
+		mvid <<= 1;
+
+	if (link_rate_hbr2 == rate)
+		nvid *= 2;
+
+	if (link_rate_hbr3 == rate)
+		nvid *= 3;
+
+	io_data = catalog->io->dp_link;
+
+	if (panel->stream_id == DP_STREAM_1) {
+		mvid_reg_off = DP1_SOFTWARE_MVID - DP_SOFTWARE_MVID;
+		nvid_reg_off = DP1_SOFTWARE_NVID - DP_SOFTWARE_NVID;
+	}
+
+	DP_DEBUG("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
+	dp_write(DP_SOFTWARE_MVID + mvid_reg_off, mvid);
+	dp_write(DP_SOFTWARE_NVID + nvid_reg_off, nvid);
+}
+
+static void dp_catalog_ctrl_lane_mapping_v200(struct dp_catalog_ctrl *ctrl,
+						bool flipped, char *lane_map)
+{
+	struct dp_catalog_private_v200 *catalog;
+	struct dp_io_data *io_data;
+	u8 l_map[4] = { 0 }, i = 0, j = 0;
+	u32 lane_map_reg = 0;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv_v200(ctrl);
+	io_data = catalog->io->dp_link;
+
+	/* For flip case, swap phy lanes with ML0 and ML3, ML1 and ML2 */
+	if (flipped) {
+		for (i = 0; i < DP_MAX_PHY_LN; i++) {
+			if (lane_map[i] == DP_ML0) {
+				for (j = 0; j < DP_MAX_PHY_LN; j++) {
+					if (lane_map[j] == DP_ML3) {
+						l_map[i] = DP_ML3;
+						l_map[j] = DP_ML0;
+						break;
+					}
+				}
+			} else if (lane_map[i] == DP_ML1) {
+				for (j = 0; j < DP_MAX_PHY_LN; j++) {
+					if (lane_map[j] == DP_ML2) {
+						l_map[i] = DP_ML2;
+						l_map[j] = DP_ML1;
+						break;
+					}
+				}
+			}
+		}
+	} else {
+		/* Normal orientation */
+		for (i = 0; i < DP_MAX_PHY_LN; i++)
+			l_map[i] = lane_map[i];
+	}
+
+	lane_map_reg = ((l_map[3]&3)<<6)|((l_map[2]&3)<<4)|((l_map[1]&3)<<2)
+			|(l_map[0]&3);
+
+	dp_write(DP_LOGICAL2PHYSICAL_LANE_MAPPING, lane_map_reg);
+}
+
+static void dp_catalog_ctrl_usb_reset_v200(struct dp_catalog_ctrl *ctrl,
+						bool flip)
+{
+}
+
+static void dp_catalog_put_v200(struct dp_catalog *catalog)
+{
+	struct dp_catalog_private_v200 *catalog_priv;
+
+	if (!catalog)
+		return;
+
+	catalog_priv = container_of(catalog->sub,
+			struct dp_catalog_private_v200, sub);
+
+	devm_kfree(catalog_priv->dev, catalog_priv);
+}
+
+struct dp_catalog_sub *dp_catalog_get_v200(struct device *dev,
+		struct dp_catalog *catalog, struct dp_catalog_io *io)
+{
+	struct dp_catalog_private_v200 *catalog_priv;
+
+	if (!dev || !catalog) {
+		DP_ERR("invalid input\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	catalog_priv = devm_kzalloc(dev, sizeof(*catalog_priv), GFP_KERNEL);
+	if (!catalog_priv)
+		return ERR_PTR(-ENOMEM);
+
+	catalog_priv->dev = dev;
+	catalog_priv->io = io;
+	catalog_priv->dpc = catalog;
+
+	catalog_priv->sub.put = dp_catalog_put_v200;
+
+	catalog->aux.clear_hw_interrupts = dp_catalog_aux_clear_hw_int_v200;
+	catalog->aux.setup               = dp_catalog_aux_setup_v200;
+
+	catalog->panel.config_msa        = dp_catalog_panel_config_msa_v200;
+
+	catalog->ctrl.lane_mapping       = dp_catalog_ctrl_lane_mapping_v200;
+	catalog->ctrl.usb_reset          = dp_catalog_ctrl_usb_reset_v200;
+
+	return &catalog_priv->sub;
+}

+ 346 - 0
qcom/opensource/display-drivers/msm/dp/dp_catalog_v420.c

@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+
+#include "dp_catalog.h"
+#include "dp_reg.h"
+#include "dp_debug.h"
+#include "dp_pll.h"
+#include <linux/rational.h>
+#include <drm/drm_fixed.h>
+
+#define dp_catalog_get_priv_v420(x) ({ \
+	struct dp_catalog *catalog; \
+	catalog = container_of(x, struct dp_catalog, x); \
+	container_of(catalog->sub, \
+		struct dp_catalog_private_v420, sub); \
+})
+
+#define dp_read(x) ({ \
+	catalog->sub.read(catalog->dpc, io_data, x); \
+})
+
+#define dp_write(x, y) ({ \
+	catalog->sub.write(catalog->dpc, io_data, x, y); \
+})
+
+#define MAX_VOLTAGE_LEVELS 4
+#define MAX_PRE_EMP_LEVELS 4
+
+static u8 const vm_pre_emphasis[MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = {
+	{0x00, 0x0E, 0x16, 0xFF},       /* pe0, 0 db */
+	{0x00, 0x0E, 0x16, 0xFF},       /* pe1, 3.5 db */
+	{0x00, 0x0E, 0xFF, 0xFF},       /* pe2, 6.0 db */
+	{0xFF, 0xFF, 0xFF, 0xFF}        /* pe3, 9.5 db */
+};
+
+/* voltage swing, 0.2v and 1.0v are not support */
+static u8 const vm_voltage_swing[MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = {
+	{0x07, 0x0F, 0x16, 0xFF}, /* sw0, 0.4v  */
+	{0x11, 0x1E, 0x1F, 0xFF}, /* sw1, 0.6 v */
+	{0x1A, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */
+	{0xFF, 0xFF, 0xFF, 0xFF}  /* sw1, 1.2 v, optional */
+};
+
+struct dp_catalog_private_v420 {
+	struct device *dev;
+	struct dp_catalog_sub sub;
+	struct dp_catalog_io *io;
+	struct dp_catalog *dpc;
+};
+
+static void dp_catalog_aux_setup_v420(struct dp_catalog_aux *aux,
+		struct dp_aux_cfg *cfg)
+{
+	struct dp_catalog_private_v420 *catalog;
+	struct dp_io_data *io_data;
+	int i = 0;
+	u32 phy_version;
+	if (!aux || !cfg) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv_v420(aux);
+
+	io_data = catalog->io->dp_phy;
+	dp_write(DP_PHY_PD_CTL, 0x67);
+	wmb(); /* make sure PD programming happened */
+
+	phy_version = dp_catalog_get_dp_phy_version(catalog->dpc);
+	if (phy_version >= 0x60000000) {
+		/* Turn on BIAS current for PHY/PLL */
+		io_data = catalog->io->dp_pll;
+		dp_write(QSERDES_COM_BIAS_EN_CLKBUFLR_EN_V600, 0x17);
+		wmb(); /* make sure BIAS programming happened */
+	} else {
+		/* Turn on BIAS current for PHY/PLL */
+		io_data = catalog->io->dp_pll;
+		dp_write(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17);
+		wmb(); /* make sure BIAS programming happened */
+	}
+
+	io_data = catalog->io->dp_phy;
+	/* DP AUX CFG register programming */
+	for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
+		DP_DEBUG("%s: offset=0x%08x, value=0x%08x\n",
+			dp_phy_aux_config_type_to_string(i),
+			cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
+		dp_write(cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
+	}
+	wmb(); /* make sure DP AUX CFG programming happened */
+
+	dp_write(DP_PHY_AUX_INTERRUPT_MASK_V420, 0x1F);
+}
+
+static void dp_catalog_aux_clear_hw_int_v420(struct dp_catalog_aux *aux)
+{
+	struct dp_catalog_private_v420 *catalog;
+	struct dp_io_data *io_data;
+	u32 data = 0;
+	u32 phy_version;
+	if (!aux) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv_v420(aux);
+	phy_version = dp_catalog_get_dp_phy_version(catalog->dpc);
+	io_data = catalog->io->dp_phy;
+	if (phy_version >= 0x60000000)
+		data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS_V600);
+	else
+		data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS_V420);
+
+	dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V420, 0x1f);
+	wmb(); /* make sure 0x1f is written before next write */
+
+	dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V420, 0x9f);
+	wmb(); /* make sure 0x9f is written before next write */
+
+	dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V420, 0);
+	wmb(); /* make sure register is cleared */
+}
+
+static void dp_catalog_panel_config_msa_v420(struct dp_catalog_panel *panel,
+					u32 rate, u32 stream_rate_khz)
+{
+	u32 mvid, nvid, mvid_off = 0, nvid_off = 0;
+	u32 const nvid_fixed = 0x8000;
+	struct dp_catalog *dp_catalog;
+	struct dp_catalog_private_v420 *catalog;
+	struct dp_io_data *io_data;
+	unsigned long num, den;
+	u32 const input_scale = 10;
+	u64 f1, f2;
+
+	if (!panel || !rate) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (panel->stream_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream id:%d\n", panel->stream_id);
+		return;
+	}
+
+	dp_catalog = container_of(panel, struct dp_catalog, panel);
+	catalog = container_of(dp_catalog->sub, struct dp_catalog_private_v420, sub);
+
+	/*
+	 * MND calculator requires the target clock to be less than half the input clock. To meet
+	 * this requirement, the input clock is scaled here and then the resulting M value is
+	 * scaled by the same factor to offset the pre-scale.
+	 */
+	rational_best_approximation(rate * input_scale, stream_rate_khz,
+			(unsigned long)(1 << 16) - 1,
+			(unsigned long)(1 << 16) - 1, &den, &num);
+
+	mvid = (num & 0xFFFF);
+	nvid = (den & 0xFFFF);
+	mvid *= input_scale;
+
+	if (nvid < nvid_fixed) {
+		f1 = drm_fixp_from_fraction(nvid_fixed, nvid);
+		f2 = drm_fixp_from_fraction(mvid, 1);
+		f1 = drm_fixp_mul(f1, f2);
+		mvid = drm_fixp2int(f1);
+		nvid = nvid_fixed;
+	}
+
+	io_data = catalog->io->dp_link;
+
+	if (panel->stream_id == DP_STREAM_1) {
+		mvid_off = DP1_SOFTWARE_MVID - DP_SOFTWARE_MVID;
+		nvid_off = DP1_SOFTWARE_NVID - DP_SOFTWARE_NVID;
+	}
+
+	DP_DEBUG("pclk=%ld, lclk=%ld, mvid=0x%x, nvid=0x%x\n", stream_rate_khz, rate, mvid, nvid);
+	dp_write(DP_SOFTWARE_MVID + mvid_off, mvid);
+	dp_write(DP_SOFTWARE_NVID + nvid_off, nvid);
+}
+
+static void dp_catalog_ctrl_phy_lane_cfg_v420(struct dp_catalog_ctrl *ctrl,
+		bool flipped, u8 ln_cnt)
+{
+	u32 info = 0x0;
+	struct dp_catalog_private_v420 *catalog;
+	struct dp_io_data *io_data;
+	u8 orientation = BIT(!!flipped);
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	catalog = dp_catalog_get_priv_v420(ctrl);
+	io_data = catalog->io->dp_phy;
+
+	info |= (ln_cnt & 0x0F);
+	info |= ((orientation & 0x0F) << 4);
+	DP_DEBUG("Shared Info = 0x%x\n", info);
+
+	dp_write(DP_PHY_SPARE0_V420, info);
+}
+
+static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,
+		u8 v_level, u8 p_level, bool high)
+{
+	struct dp_catalog_private_v420 *catalog;
+	struct dp_io_data *io_data;
+	u8 value0, value1;
+	u32 version;
+	u32 phy_version;
+	int idx;
+
+	if (!ctrl || !((v_level < MAX_VOLTAGE_LEVELS)
+		&& (p_level < MAX_PRE_EMP_LEVELS))) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	DP_DEBUG("hw: v=%d p=%d, high=%d\n", v_level, p_level, high);
+
+	catalog = dp_catalog_get_priv_v420(ctrl);
+	phy_version = dp_catalog_get_dp_phy_version(catalog->dpc);
+
+	io_data = catalog->io->dp_ahb;
+	version = dp_read(DP_HW_VERSION);
+	DP_DEBUG("version: 0x%x\n", version);
+
+	/*
+	 * For DP controller versions >= 1.2.3
+	 */
+	if (version >= 0x10020003 && ctrl->valid_lt_params) {
+		idx = v_level * MAX_VOLTAGE_LEVELS + p_level;
+		if (high) {
+			value0 = ctrl->swing_hbr2_3[idx];
+			value1 = ctrl->pre_emp_hbr2_3[idx];
+		} else {
+			value0 = ctrl->swing_hbr_rbr[idx];
+			value1 = ctrl->pre_emp_hbr_rbr[idx];
+		}
+	} else {
+		value0 = vm_voltage_swing[v_level][p_level];
+		value1 = vm_pre_emphasis[v_level][p_level];
+	}
+
+	/* program default setting first */
+	io_data = catalog->io->dp_ln_tx0;
+	dp_write(TXn_TX_DRV_LVL_V420, 0x2A);
+	dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
+
+	io_data = catalog->io->dp_ln_tx1;
+	dp_write(TXn_TX_DRV_LVL_V420, 0x2A);
+	dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
+
+	/* Enable MUX to use Cursor values from these registers */
+	value0 |= BIT(5);
+	value1 |= BIT(5);
+
+	/* Configure host and panel only if both values are allowed */
+	if (value0 != 0xFF && value1 != 0xFF) {
+		io_data = catalog->io->dp_ln_tx0;
+		dp_write(TXn_TX_DRV_LVL_V420, value0);
+		dp_write(TXn_TX_EMP_POST1_LVL, value1);
+
+		io_data = catalog->io->dp_ln_tx1;
+		dp_write(TXn_TX_DRV_LVL_V420, value0);
+		dp_write(TXn_TX_EMP_POST1_LVL, value1);
+
+		DP_DEBUG("hw: vx_value=0x%x px_value=0x%x\n",
+			value0, value1);
+	} else {
+		DP_ERR("invalid vx (0x%x=0x%x), px (0x%x=0x%x\n",
+			v_level, value0, p_level, value1);
+	}
+}
+
+static void dp_catalog_ctrl_lane_pnswap_v420(struct dp_catalog_ctrl *ctrl,
+						u8 ln_pnswap)
+{
+	struct dp_catalog_private_v420 *catalog;
+	struct dp_io_data *io_data;
+	u32 cfg0, cfg1;
+
+	catalog = dp_catalog_get_priv_v420(ctrl);
+
+	cfg0 = 0x0a;
+	cfg1 = 0x0a;
+
+	cfg0 |= ((ln_pnswap >> 0) & 0x1) << 0;
+	cfg0 |= ((ln_pnswap >> 1) & 0x1) << 2;
+	cfg1 |= ((ln_pnswap >> 2) & 0x1) << 0;
+	cfg1 |= ((ln_pnswap >> 3) & 0x1) << 2;
+
+	io_data = catalog->io->dp_ln_tx0;
+	dp_write(TXn_TX_POL_INV_V420, cfg0);
+
+	io_data = catalog->io->dp_ln_tx1;
+	dp_write(TXn_TX_POL_INV_V420, cfg1);
+}
+
+static void dp_catalog_put_v420(struct dp_catalog *catalog)
+{
+	struct dp_catalog_private_v420 *catalog_priv;
+
+	if (!catalog)
+		return;
+
+	catalog_priv = container_of(catalog->sub,
+			struct dp_catalog_private_v420, sub);
+	devm_kfree(catalog_priv->dev, catalog_priv);
+}
+
+struct dp_catalog_sub *dp_catalog_get_v420(struct device *dev,
+		struct dp_catalog *catalog, struct dp_catalog_io *io)
+{
+	struct dp_catalog_private_v420 *catalog_priv;
+
+	if (!dev || !catalog) {
+		DP_ERR("invalid input\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	catalog_priv = devm_kzalloc(dev, sizeof(*catalog_priv), GFP_KERNEL);
+	if (!catalog_priv)
+		return ERR_PTR(-ENOMEM);
+
+	catalog_priv->dev = dev;
+	catalog_priv->io = io;
+	catalog_priv->dpc = catalog;
+
+	catalog_priv->sub.put      = dp_catalog_put_v420;
+
+	catalog->aux.setup         = dp_catalog_aux_setup_v420;
+	catalog->aux.clear_hw_interrupts = dp_catalog_aux_clear_hw_int_v420;
+	catalog->panel.config_msa  = dp_catalog_panel_config_msa_v420;
+	catalog->ctrl.phy_lane_cfg = dp_catalog_ctrl_phy_lane_cfg_v420;
+	catalog->ctrl.update_vx_px = dp_catalog_ctrl_update_vx_px_v420;
+	catalog->ctrl.lane_pnswap = dp_catalog_ctrl_lane_pnswap_v420;
+
+	return &catalog_priv->sub;
+}

+ 1617 - 0
qcom/opensource/display-drivers/msm/dp/dp_ctrl.c

@@ -0,0 +1,1617 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <drm/drm_fixed.h>
+#include <linux/version.h>
+
+#include "dp_ctrl.h"
+#include "dp_debug.h"
+#include "sde_dbg.h"
+
+#define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)
+
+#define DP_CTRL_INTR_READY_FOR_VIDEO     BIT(0)
+#define DP_CTRL_INTR_IDLE_PATTERN_SENT  BIT(3)
+
+#define DP_CTRL_INTR_MST_DP0_VCPF_SENT	BIT(0)
+#define DP_CTRL_INTR_MST_DP1_VCPF_SENT	BIT(3)
+
+/* dp state ctrl */
+#define ST_TRAIN_PATTERN_1		BIT(0)
+#define ST_TRAIN_PATTERN_2		BIT(1)
+#define ST_TRAIN_PATTERN_3		BIT(2)
+#define ST_TRAIN_PATTERN_4		BIT(3)
+#define ST_SYMBOL_ERR_RATE_MEASUREMENT	BIT(4)
+#define ST_PRBS7			BIT(5)
+#define ST_CUSTOM_80_BIT_PATTERN	BIT(6)
+#define ST_SEND_VIDEO			BIT(7)
+#define ST_PUSH_IDLE			BIT(8)
+#define MST_DP0_PUSH_VCPF		BIT(12)
+#define MST_DP0_FORCE_VCPF		BIT(13)
+#define MST_DP1_PUSH_VCPF		BIT(14)
+#define MST_DP1_FORCE_VCPF		BIT(15)
+
+#define MR_LINK_TRAINING1  0x8
+#define MR_LINK_SYMBOL_ERM 0x80
+#define MR_LINK_PRBS7 0x100
+#define MR_LINK_CUSTOM80 0x200
+#define MR_LINK_TRAINING4  0x40
+
+#define DP_MAX_LANES 4
+
+struct dp_mst_ch_slot_info {
+	u32 start_slot;
+	u32 tot_slots;
+};
+
+struct dp_mst_channel_info {
+	struct dp_mst_ch_slot_info slot_info[DP_STREAM_MAX];
+};
+
+struct dp_ctrl_private {
+	struct dp_ctrl dp_ctrl;
+
+	struct device *dev;
+	struct dp_aux *aux;
+	struct dp_panel *panel;
+	struct dp_link *link;
+	struct dp_power *power;
+	struct dp_parser *parser;
+	struct dp_catalog_ctrl *catalog;
+	struct dp_pll *pll;
+
+	struct completion idle_comp;
+	struct completion video_comp;
+
+	bool orientation;
+	bool power_on;
+	bool mst_mode;
+	bool fec_mode;
+	bool dsc_mode;
+	bool sim_mode;
+
+	atomic_t aborted;
+
+	u8 initial_lane_count;
+	u8 initial_bw_code;
+
+	u32 vic;
+	u32 stream_count;
+	u32 training_2_pattern;
+	struct dp_mst_channel_info mst_ch_info;
+};
+
+enum notification_status {
+	NOTIFY_UNKNOWN,
+	NOTIFY_CONNECT,
+	NOTIFY_DISCONNECT,
+	NOTIFY_CONNECT_IRQ_HPD,
+	NOTIFY_DISCONNECT_IRQ_HPD,
+};
+
+static void dp_ctrl_idle_patterns_sent(struct dp_ctrl_private *ctrl)
+{
+	complete(&ctrl->idle_comp);
+}
+
+static void dp_ctrl_video_ready(struct dp_ctrl_private *ctrl)
+{
+	complete(&ctrl->video_comp);
+}
+
+static void dp_ctrl_abort(struct dp_ctrl *dp_ctrl, bool abort)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl) {
+		DP_ERR("Invalid input data\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	atomic_set(&ctrl->aborted, abort);
+}
+
+static void dp_ctrl_state_ctrl(struct dp_ctrl_private *ctrl, u32 state)
+{
+	ctrl->catalog->state_ctrl(ctrl->catalog, state);
+}
+
+static void dp_ctrl_push_idle(struct dp_ctrl_private *ctrl,
+				enum dp_stream_id strm)
+{
+	int const idle_pattern_completion_timeout_ms = HZ / 10;
+	u32 state = 0x0;
+
+	if (!ctrl->power_on)
+		return;
+
+	if (!ctrl->mst_mode) {
+		state = ST_PUSH_IDLE;
+		goto trigger_idle;
+	}
+
+	if (strm >= DP_STREAM_MAX) {
+		DP_ERR("mst push idle, invalid stream:%d\n", strm);
+		return;
+	}
+
+	state |= (strm == DP_STREAM_0) ? MST_DP0_PUSH_VCPF : MST_DP1_PUSH_VCPF;
+
+trigger_idle:
+	reinit_completion(&ctrl->idle_comp);
+	dp_ctrl_state_ctrl(ctrl, state);
+
+	if (!wait_for_completion_timeout(&ctrl->idle_comp,
+			idle_pattern_completion_timeout_ms))
+		DP_WARN("time out\n");
+	else
+		DP_DEBUG("mainlink off done\n");
+}
+
+/**
+ * dp_ctrl_configure_source_link_params() - configures DP TX source params
+ * @ctrl: Display Port Driver data
+ * @enable: enable or disable DP transmitter
+ *
+ * Configures the DP transmitter source params including details such as lane
+ * configuration, output format and sink/panel timing information.
+ */
+static void dp_ctrl_configure_source_link_params(struct dp_ctrl_private *ctrl,
+		bool enable)
+{
+	if (!ctrl->power->clk_status(ctrl->power, DP_LINK_PM)) {
+		DP_WARN("DP link clocks are off\n");
+		return;
+	}
+
+	if (!ctrl->power->clk_status(ctrl->power, DP_CORE_PM)) {
+		DP_WARN("DP core clocks are off\n");
+		return;
+	}
+
+	if (enable) {
+		ctrl->catalog->lane_mapping(ctrl->catalog, ctrl->orientation,
+						ctrl->parser->l_map);
+		ctrl->catalog->lane_pnswap(ctrl->catalog,
+						ctrl->parser->l_pnswap);
+		ctrl->catalog->mst_config(ctrl->catalog, ctrl->mst_mode);
+		ctrl->catalog->config_ctrl(ctrl->catalog,
+				ctrl->link->link_params.lane_count);
+		ctrl->catalog->mainlink_levels(ctrl->catalog,
+				ctrl->link->link_params.lane_count);
+		ctrl->catalog->mainlink_ctrl(ctrl->catalog, true);
+	} else {
+		ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+	}
+}
+
+static void dp_ctrl_wait4video_ready(struct dp_ctrl_private *ctrl)
+{
+	if (!wait_for_completion_timeout(&ctrl->video_comp, HZ / 2))
+		DP_WARN("SEND_VIDEO time out\n");
+	else
+		DP_DEBUG("SEND_VIDEO triggered\n");
+}
+
+static int dp_ctrl_update_sink_vx_px(struct dp_ctrl_private *ctrl)
+{
+	int i, ret;
+	u8 buf[DP_MAX_LANES];
+	u8 v_level = ctrl->link->phy_params.v_level;
+	u8 p_level = ctrl->link->phy_params.p_level;
+	u8 size = min_t(u8, sizeof(buf), ctrl->link->link_params.lane_count);
+	u32 max_level_reached = 0;
+
+	if (v_level == ctrl->link->phy_params.max_v_level) {
+		DP_DEBUG("max voltage swing level reached %d\n", v_level);
+		max_level_reached |= DP_TRAIN_MAX_SWING_REACHED;
+	}
+
+	if (p_level == ctrl->link->phy_params.max_p_level) {
+		DP_DEBUG("max pre-emphasis level reached %d\n", p_level);
+		max_level_reached |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+	}
+
+	p_level <<= DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+	for (i = 0; i < size; i++)
+		buf[i] = v_level | p_level | max_level_reached;
+
+	DP_DEBUG("lanes: %d, swing: 0x%x, pre-emp: 0x%x\n",
+			size, v_level, p_level);
+
+	ret = drm_dp_dpcd_write(ctrl->aux->drm_aux,
+		DP_TRAINING_LANE0_SET, buf, size);
+
+	return ret <= 0 ? -EINVAL : 0;
+}
+
+static void dp_ctrl_update_hw_vx_px(struct dp_ctrl_private *ctrl)
+{
+	struct dp_link *link = ctrl->link;
+	bool high = false;
+
+	if (ctrl->link->link_params.bw_code == DP_LINK_BW_5_4 ||
+	    ctrl->link->link_params.bw_code == DP_LINK_BW_8_1)
+		high = true;
+
+	ctrl->catalog->update_vx_px(ctrl->catalog,
+		link->phy_params.v_level, link->phy_params.p_level, high);
+}
+
+static int dp_ctrl_update_sink_pattern(struct dp_ctrl_private *ctrl, u8 pattern)
+{
+	u8 buf = pattern;
+	int ret;
+
+	DP_DEBUG("sink: pattern=%x\n", pattern);
+
+	if (pattern && pattern != DP_TRAINING_PATTERN_4)
+		buf |= DP_LINK_SCRAMBLING_DISABLE;
+
+	ret = drm_dp_dpcd_writeb(ctrl->aux->drm_aux,
+		DP_TRAINING_PATTERN_SET, buf);
+
+	return ret <= 0 ? -EINVAL : 0;
+}
+
+static int dp_ctrl_read_link_status(struct dp_ctrl_private *ctrl,
+					u8 *link_status)
+{
+	int ret = 0, len;
+	u32 const offset = DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS;
+	u32 link_status_read_max_retries = 100;
+
+	while (--link_status_read_max_retries) {
+		len = drm_dp_dpcd_read_link_status(ctrl->aux->drm_aux,
+			link_status);
+		if (len != DP_LINK_STATUS_SIZE) {
+			DP_ERR("DP link status read failed, err: %d\n", len);
+			ret = len;
+			break;
+		}
+
+		if (!(link_status[offset] & DP_LINK_STATUS_UPDATED))
+			break;
+	}
+
+	return ret;
+}
+
+static int dp_ctrl_lane_count_down_shift(struct dp_ctrl_private *ctrl)
+{
+	int ret = -EAGAIN;
+	u8 lanes = ctrl->link->link_params.lane_count;
+
+	if (ctrl->panel->link_info.revision != 0x14)
+		return -EINVAL;
+
+	switch (lanes) {
+	case 4:
+		ctrl->link->link_params.lane_count = 2;
+		break;
+	case 2:
+		ctrl->link->link_params.lane_count = 1;
+		break;
+	default:
+		if (lanes != ctrl->initial_lane_count)
+			ret = -EINVAL;
+		break;
+	}
+
+	DP_DEBUG("new lane count=%d\n", ctrl->link->link_params.lane_count);
+
+	return ret;
+}
+
+static bool dp_ctrl_is_link_rate_rbr(struct dp_ctrl_private *ctrl)
+{
+	return ctrl->link->link_params.bw_code == DP_LINK_BW_1_62;
+}
+
+static u8 dp_ctrl_get_active_lanes(struct dp_ctrl_private *ctrl,
+				u8 *link_status)
+{
+	u8 lane, count = 0;
+
+	for (lane = 0; lane < ctrl->link->link_params.lane_count; lane++) {
+		if (link_status[lane / 2] & (1 << (lane * 4)))
+			count++;
+		else
+			break;
+	}
+
+	return count;
+}
+
+static int dp_ctrl_link_training_1(struct dp_ctrl_private *ctrl)
+{
+	int tries, old_v_level, ret = -EINVAL;
+	u8 link_status[DP_LINK_STATUS_SIZE];
+	u8 pattern = 0;
+	int const maximum_retries = 5;
+
+	ctrl->aux->state &= ~DP_STATE_TRAIN_1_FAILED;
+	ctrl->aux->state &= ~DP_STATE_TRAIN_1_SUCCEEDED;
+	ctrl->aux->state |= DP_STATE_TRAIN_1_STARTED;
+
+	if (ctrl->sim_mode) {
+		DP_DEBUG("simulation enabled, skip clock recovery\n");
+		ret = 0;
+		goto skip_training;
+	}
+
+	dp_ctrl_state_ctrl(ctrl, 0);
+	/* Make sure to clear the current pattern before starting a new one */
+	wmb();
+
+	tries = 0;
+	old_v_level = ctrl->link->phy_params.v_level;
+	while (!atomic_read(&ctrl->aborted)) {
+		/* update hardware with current swing/pre-emp values */
+		dp_ctrl_update_hw_vx_px(ctrl);
+
+		if (!pattern) {
+			pattern = DP_TRAINING_PATTERN_1;
+
+			ctrl->catalog->set_pattern(ctrl->catalog, pattern);
+
+			/* update sink with current settings */
+			ret = dp_ctrl_update_sink_pattern(ctrl, pattern);
+			if (ret)
+				break;
+		}
+
+		ret = dp_ctrl_update_sink_vx_px(ctrl);
+		if (ret)
+			break;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0))
+		drm_dp_link_train_clock_recovery_delay(ctrl->aux->drm_aux, ctrl->panel->dpcd);
+#else
+		drm_dp_link_train_clock_recovery_delay(ctrl->panel->dpcd);
+#endif
+
+		ret = dp_ctrl_read_link_status(ctrl, link_status);
+		if (ret)
+			break;
+
+		if (!drm_dp_clock_recovery_ok(link_status,
+			ctrl->link->link_params.lane_count))
+			ret = -EINVAL;
+		else
+			break;
+
+		if (ctrl->link->phy_params.v_level == ctrl->link->phy_params.max_v_level) {
+			DP_ERR_RATELIMITED_V("max v_level reached\n");
+			break;
+		}
+
+		if (old_v_level == ctrl->link->phy_params.v_level) {
+			if (++tries >= maximum_retries) {
+				DP_ERR("max tries reached\n");
+				ret = -ETIMEDOUT;
+				break;
+			}
+		} else {
+			tries = 0;
+			old_v_level = ctrl->link->phy_params.v_level;
+		}
+
+		DP_DEBUG("clock recovery not done, adjusting vx px\n");
+
+		ctrl->link->adjust_levels(ctrl->link, link_status);
+	}
+
+	if (ret && dp_ctrl_is_link_rate_rbr(ctrl)) {
+		u8 active_lanes = dp_ctrl_get_active_lanes(ctrl, link_status);
+
+		if (active_lanes) {
+			ctrl->link->link_params.lane_count = active_lanes;
+			ctrl->link->link_params.bw_code = ctrl->initial_bw_code;
+
+			/* retry with new settings */
+			ret = -EAGAIN;
+		}
+	}
+
+skip_training:
+	ctrl->aux->state &= ~DP_STATE_TRAIN_1_STARTED;
+
+	if (ret)
+		ctrl->aux->state |= DP_STATE_TRAIN_1_FAILED;
+	else
+		ctrl->aux->state |= DP_STATE_TRAIN_1_SUCCEEDED;
+
+	return ret;
+}
+
+static int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
+{
+	int ret = 0;
+
+	if (!ctrl)
+		return -EINVAL;
+
+	switch (ctrl->link->link_params.bw_code) {
+	case DP_LINK_BW_8_1:
+		ctrl->link->link_params.bw_code = DP_LINK_BW_5_4;
+		break;
+	case DP_LINK_BW_5_4:
+		ctrl->link->link_params.bw_code = DP_LINK_BW_2_7;
+		break;
+	case DP_LINK_BW_2_7:
+	case DP_LINK_BW_1_62:
+	default:
+		ctrl->link->link_params.bw_code = DP_LINK_BW_1_62;
+		break;
+	}
+
+	DP_DEBUG("new bw code=0x%x\n", ctrl->link->link_params.bw_code);
+
+	return ret;
+}
+
+static void dp_ctrl_clear_training_pattern(struct dp_ctrl_private *ctrl)
+{
+	dp_ctrl_update_sink_pattern(ctrl, 0);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0))
+	drm_dp_link_train_channel_eq_delay(ctrl->aux->drm_aux, ctrl->panel->dpcd);
+#else
+	drm_dp_link_train_channel_eq_delay(ctrl->panel->dpcd);
+#endif
+}
+
+static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
+{
+	int tries = 0, ret = -EINVAL;
+	u8 dpcd_pattern, pattern = 0;
+	int const maximum_retries = 5;
+	u8 link_status[DP_LINK_STATUS_SIZE];
+
+	ctrl->aux->state &= ~DP_STATE_TRAIN_2_FAILED;
+	ctrl->aux->state &= ~DP_STATE_TRAIN_2_SUCCEEDED;
+	ctrl->aux->state |= DP_STATE_TRAIN_2_STARTED;
+
+	if (ctrl->sim_mode) {
+		DP_DEBUG("simulation enabled, skip channel equalization\n");
+		ret = 0;
+		goto skip_training;
+	}
+
+	dp_ctrl_state_ctrl(ctrl, 0);
+	/* Make sure to clear the current pattern before starting a new one */
+	wmb();
+
+	dpcd_pattern = ctrl->training_2_pattern;
+
+	while (!atomic_read(&ctrl->aborted)) {
+		/* update hardware with current swing/pre-emp values */
+		dp_ctrl_update_hw_vx_px(ctrl);
+
+		if (!pattern) {
+			pattern = dpcd_pattern;
+
+			/* program hw to send pattern */
+			ctrl->catalog->set_pattern(ctrl->catalog, pattern);
+
+			/* update sink with current pattern */
+			ret = dp_ctrl_update_sink_pattern(ctrl, pattern);
+			if (ret)
+				break;
+		}
+
+		ret = dp_ctrl_update_sink_vx_px(ctrl);
+		if (ret)
+			break;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0))
+		drm_dp_link_train_channel_eq_delay(ctrl->aux->drm_aux, ctrl->panel->dpcd);
+#else
+		drm_dp_link_train_channel_eq_delay(ctrl->panel->dpcd);
+#endif
+
+		ret = dp_ctrl_read_link_status(ctrl, link_status);
+		if (ret)
+			break;
+
+		/* check if CR bits still remain set */
+		if (!drm_dp_clock_recovery_ok(link_status,
+			ctrl->link->link_params.lane_count)) {
+			ret = -EINVAL;
+			break;
+		}
+
+		if (!drm_dp_channel_eq_ok(link_status,
+			ctrl->link->link_params.lane_count))
+			ret = -EINVAL;
+		else
+			break;
+
+		if (tries >= maximum_retries) {
+			ret = dp_ctrl_lane_count_down_shift(ctrl);
+			break;
+		}
+		tries++;
+
+		ctrl->link->adjust_levels(ctrl->link, link_status);
+	}
+
+skip_training:
+	ctrl->aux->state &= ~DP_STATE_TRAIN_2_STARTED;
+
+	if (ret)
+		ctrl->aux->state |= DP_STATE_TRAIN_2_FAILED;
+	else
+		ctrl->aux->state |= DP_STATE_TRAIN_2_SUCCEEDED;
+	return ret;
+}
+
+static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
+{
+	int ret = 0;
+	u8 const encoding = 0x1, downspread = 0x00;
+	struct drm_dp_link link_info = {0};
+
+	ctrl->link->phy_params.p_level = 0;
+	ctrl->link->phy_params.v_level = 0;
+
+	link_info.num_lanes = ctrl->link->link_params.lane_count;
+	link_info.rate = drm_dp_bw_code_to_link_rate(
+		ctrl->link->link_params.bw_code);
+	link_info.capabilities = ctrl->panel->link_info.capabilities;
+
+	ret = dp_link_configure(ctrl->aux->drm_aux, &link_info);
+	if (ret)
+		goto end;
+
+	ret = drm_dp_dpcd_writeb(ctrl->aux->drm_aux,
+		DP_DOWNSPREAD_CTRL, downspread);
+	if (ret <= 0) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	ret = drm_dp_dpcd_writeb(ctrl->aux->drm_aux,
+		DP_MAIN_LINK_CHANNEL_CODING_SET, encoding);
+	if (ret <= 0) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	/* disable FEC before link training */
+	ctrl->catalog->fec_config(ctrl->catalog, false);
+
+	ret = dp_ctrl_link_training_1(ctrl);
+	if (ret) {
+		DP_ERR("link training #1 failed\n");
+		goto end;
+	}
+
+	/* print success info as this is a result of user initiated action */
+	DP_INFO("link training #1 successful\n");
+
+	ret = dp_ctrl_link_training_2(ctrl);
+	if (ret) {
+		DP_ERR("link training #2 failed\n");
+		goto end;
+	}
+
+	/* print success info as this is a result of user initiated action */
+	DP_INFO("link training #2 successful\n");
+
+end:
+	dp_ctrl_state_ctrl(ctrl, 0);
+	/* Make sure to clear the current pattern before starting a new one */
+	wmb();
+
+	dp_ctrl_clear_training_pattern(ctrl);
+	return ret;
+}
+
+static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl)
+{
+	int ret = 0;
+
+	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
+		goto end;
+
+	/*
+	 * As part of previous calls, DP controller state might have
+	 * transitioned to PUSH_IDLE. In order to start transmitting a link
+	 * training pattern, we have to first to a DP software reset.
+	 */
+	ctrl->catalog->reset(ctrl->catalog);
+
+	if (ctrl->fec_mode)
+		drm_dp_dpcd_writeb(ctrl->aux->drm_aux, DP_FEC_CONFIGURATION,
+				0x01);
+
+	ret = dp_ctrl_link_train(ctrl);
+
+end:
+	return ret;
+}
+
+static void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl,
+		char *name, enum dp_pm_type clk_type, u32 rate)
+{
+	u32 num = ctrl->parser->mp[clk_type].num_clk;
+	struct dss_clk *cfg = ctrl->parser->mp[clk_type].clk_config;
+
+	/* convert to HZ for byte2 ops */
+	rate *= ctrl->pll->clk_factor;
+
+	while (num && strcmp(cfg->clk_name, name)) {
+		num--;
+		cfg++;
+	}
+
+	DP_DEBUG("setting rate=%d on clk=%s\n", rate, name);
+
+	if (num)
+		cfg->rate = rate;
+	else
+		DP_ERR("%s clock could not be set with rate %d\n", name, rate);
+}
+
+static int dp_ctrl_enable_link_clock(struct dp_ctrl_private *ctrl)
+{
+	int ret = 0;
+	u32 rate = drm_dp_bw_code_to_link_rate(ctrl->link->link_params.bw_code);
+	enum dp_pm_type type = DP_LINK_PM;
+
+	DP_DEBUG("rate=%d\n", rate);
+
+	dp_ctrl_set_clock_rate(ctrl, "link_clk_src", type, rate);
+
+	if (ctrl->pll->pll_cfg) {
+		ret = ctrl->pll->pll_cfg(ctrl->pll, rate);
+		if (ret < 0) {
+			DP_ERR("DP pll cfg failed\n");
+			return ret;
+		}
+	}
+
+	if (ctrl->pll->pll_prepare) {
+		ret = ctrl->pll->pll_prepare(ctrl->pll);
+		if (ret < 0) {
+			DP_ERR("DP pll prepare failed\n");
+			return ret;
+		}
+	}
+
+	ret = ctrl->power->clk_enable(ctrl->power, type, true);
+	if (ret) {
+		DP_ERR("Unabled to start link clocks\n");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void dp_ctrl_disable_link_clock(struct dp_ctrl_private *ctrl)
+{
+	int rc = 0;
+
+	ctrl->power->clk_enable(ctrl->power, DP_LINK_PM, false);
+	if (ctrl->pll->pll_unprepare) {
+		rc = ctrl->pll->pll_unprepare(ctrl->pll);
+		if (rc < 0)
+			DP_ERR("pll unprepare failed\n");
+	}
+}
+
+static void dp_ctrl_select_training_pattern(struct dp_ctrl_private *ctrl,
+						bool downgrade)
+{
+	u32 pattern;
+
+	if (drm_dp_tps4_supported(ctrl->panel->dpcd))
+		pattern = DP_TRAINING_PATTERN_4;
+	else if (drm_dp_tps3_supported(ctrl->panel->dpcd))
+		pattern = DP_TRAINING_PATTERN_3;
+	else
+		pattern = DP_TRAINING_PATTERN_2;
+
+	if (!downgrade)
+		goto end;
+
+	switch (pattern) {
+	case DP_TRAINING_PATTERN_4:
+		pattern = DP_TRAINING_PATTERN_3;
+		break;
+	case DP_TRAINING_PATTERN_3:
+		pattern = DP_TRAINING_PATTERN_2;
+		break;
+	default:
+		break;
+	}
+end:
+	ctrl->training_2_pattern = pattern;
+}
+
+static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow)
+{
+	int rc = -EINVAL;
+	bool downgrade = false;
+	u32 link_train_max_retries = 100;
+	struct dp_catalog_ctrl *catalog;
+	struct dp_link_params *link_params;
+
+	catalog = ctrl->catalog;
+	link_params = &ctrl->link->link_params;
+
+	catalog->phy_lane_cfg(catalog, ctrl->orientation,
+				link_params->lane_count);
+
+	while (1) {
+		DP_DEBUG("bw_code=%d, lane_count=%d\n",
+			link_params->bw_code, link_params->lane_count);
+
+		rc = dp_ctrl_enable_link_clock(ctrl);
+		if (rc)
+			break;
+
+		ctrl->catalog->late_phy_init(ctrl->catalog,
+			ctrl->link->link_params.lane_count,
+			ctrl->orientation);
+
+		dp_ctrl_configure_source_link_params(ctrl, true);
+
+		if (!(--link_train_max_retries % 10)) {
+			struct dp_link_params *link = &ctrl->link->link_params;
+
+			link->lane_count = ctrl->initial_lane_count;
+			link->bw_code = ctrl->initial_bw_code;
+			downgrade = true;
+		}
+
+		dp_ctrl_select_training_pattern(ctrl, downgrade);
+
+		rc = dp_ctrl_setup_main_link(ctrl);
+		if (!rc)
+			break;
+
+		/*
+		 * Shallow means link training failure is not important.
+		 * If it fails, we still keep the link clocks on.
+		 * In this mode, the system expects DP to be up
+		 * even though the cable is removed. Disconnect interrupt
+		 * will eventually trigger and shutdown DP.
+		 */
+		if (shallow) {
+			rc = 0;
+			break;
+		}
+
+		if (!link_train_max_retries || atomic_read(&ctrl->aborted)) {
+			dp_ctrl_disable_link_clock(ctrl);
+			break;
+		}
+
+		if (rc != -EAGAIN) {
+			dp_ctrl_link_rate_down_shift(ctrl);
+			ctrl->panel->init(ctrl->panel);
+		}
+
+		dp_ctrl_configure_source_link_params(ctrl, false);
+		dp_ctrl_disable_link_clock(ctrl);
+
+		/* hw recommended delays before retrying link training */
+		msleep(20);
+	}
+
+	return rc;
+}
+
+static int dp_ctrl_enable_stream_clocks(struct dp_ctrl_private *ctrl,
+		struct dp_panel *dp_panel)
+{
+	int ret = 0;
+	u32 pclk;
+	enum dp_pm_type clk_type;
+	char clk_name[32] = "";
+
+	ret = ctrl->power->set_pixel_clk_parent(ctrl->power,
+			dp_panel->stream_id);
+
+	if (ret)
+		return ret;
+
+	if (dp_panel->stream_id == DP_STREAM_0) {
+		clk_type = DP_STREAM0_PM;
+		strlcpy(clk_name, "strm0_pixel_clk", 32);
+	} else if (dp_panel->stream_id == DP_STREAM_1) {
+		clk_type = DP_STREAM1_PM;
+		strlcpy(clk_name, "strm1_pixel_clk", 32);
+	} else {
+		DP_ERR("Invalid stream:%d for clk enable\n",
+				dp_panel->stream_id);
+		return -EINVAL;
+	}
+
+	pclk = dp_panel->pinfo.widebus_en ?
+		(dp_panel->pinfo.pixel_clk_khz >> 1) :
+		(dp_panel->pinfo.pixel_clk_khz);
+
+	dp_ctrl_set_clock_rate(ctrl, clk_name, clk_type, pclk);
+
+	ret = ctrl->power->clk_enable(ctrl->power, clk_type, true);
+	if (ret) {
+		DP_ERR("Unabled to start stream:%d clocks\n",
+				dp_panel->stream_id);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int dp_ctrl_disable_stream_clocks(struct dp_ctrl_private *ctrl,
+		struct dp_panel *dp_panel)
+{
+	int ret = 0;
+
+	if (dp_panel->stream_id == DP_STREAM_0) {
+		return ctrl->power->clk_enable(ctrl->power,
+				DP_STREAM0_PM, false);
+	} else if (dp_panel->stream_id == DP_STREAM_1) {
+		return ctrl->power->clk_enable(ctrl->power,
+				DP_STREAM1_PM, false);
+	} else {
+		DP_ERR("Invalid stream:%d for clk disable\n",
+				dp_panel->stream_id);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset)
+{
+	struct dp_ctrl_private *ctrl;
+	struct dp_catalog_ctrl *catalog;
+
+	if (!dp_ctrl) {
+		DP_ERR("Invalid input data\n");
+		return -EINVAL;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	ctrl->orientation = flip;
+	catalog = ctrl->catalog;
+
+	if (reset) {
+		catalog->usb_reset(ctrl->catalog, flip);
+		catalog->phy_reset(ctrl->catalog);
+	}
+	catalog->enable_irq(ctrl->catalog, true);
+	atomic_set(&ctrl->aborted, 0);
+
+	return 0;
+}
+
+/**
+ * dp_ctrl_host_deinit() - Uninitialize DP controller
+ * @ctrl: Display Port Driver data
+ *
+ * Perform required steps to uninitialize DP controller
+ * and its resources.
+ */
+static void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl) {
+		DP_ERR("Invalid input data\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	ctrl->catalog->enable_irq(ctrl->catalog, false);
+
+	DP_DEBUG("Host deinitialized successfully\n");
+}
+
+static void dp_ctrl_send_video(struct dp_ctrl_private *ctrl)
+{
+	reinit_completion(&ctrl->video_comp);
+	ctrl->catalog->state_ctrl(ctrl->catalog, ST_SEND_VIDEO);
+}
+
+static void dp_ctrl_fec_setup(struct dp_ctrl_private *ctrl)
+{
+	u8 fec_sts = 0;
+	int i, max_retries = 3;
+	bool fec_en_detected = false;
+
+	if (!ctrl->fec_mode)
+		return;
+
+	/* FEC should be set only for the first stream */
+	if (ctrl->stream_count > 1)
+		return;
+
+	/* Need to try to enable multiple times due to BS symbols collisions */
+	for (i = 0; i < max_retries; i++) {
+		ctrl->catalog->fec_config(ctrl->catalog, ctrl->fec_mode);
+
+		/* wait for controller to start fec sequence */
+		usleep_range(900, 1000);
+
+		/* read back FEC status and check if it is enabled */
+		drm_dp_dpcd_readb(ctrl->aux->drm_aux, DP_FEC_STATUS, &fec_sts);
+		if (fec_sts & DP_FEC_DECODE_EN_DETECTED) {
+			fec_en_detected = true;
+			break;
+		}
+	}
+
+	SDE_EVT32_EXTERNAL(i, fec_en_detected);
+	DP_DEBUG("retries %d, fec_en_detected %d\n", i, fec_en_detected);
+
+	if (!fec_en_detected)
+		DP_WARN("failed to enable sink fec\n");
+}
+
+static int dp_ctrl_mst_send_act(struct dp_ctrl_private *ctrl)
+{
+	bool act_complete;
+
+	if (!ctrl->mst_mode)
+		return 0;
+
+	ctrl->catalog->trigger_act(ctrl->catalog);
+	msleep(20); /* needs 1 frame time */
+
+	ctrl->catalog->read_act_complete_sts(ctrl->catalog, &act_complete);
+
+	if (!act_complete)
+		DP_ERR("mst act trigger complete failed\n");
+	else
+		DP_MST_DEBUG("mst ACT trigger complete SUCCESS\n");
+
+	return 0;
+}
+
+static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl)
+{
+	int ret = 0;
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl) {
+		DP_ERR("Invalid input data\n");
+		return -EINVAL;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_COMPLETED;
+	ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_FAILED;
+
+	if (!ctrl->power_on) {
+		DP_ERR("ctrl off\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	if (atomic_read(&ctrl->aborted))
+		goto end;
+
+	ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_STARTED;
+	ret = dp_ctrl_setup_main_link(ctrl);
+	ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_STARTED;
+
+	if (ret) {
+		ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_FAILED;
+		goto end;
+	}
+
+	ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_COMPLETED;
+
+	if (ctrl->stream_count) {
+		dp_ctrl_send_video(ctrl);
+		dp_ctrl_mst_send_act(ctrl);
+		dp_ctrl_wait4video_ready(ctrl);
+		dp_ctrl_fec_setup(ctrl);
+	}
+end:
+	return ret;
+}
+
+static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl)
+{
+	int ret = 0;
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl) {
+		DP_ERR("Invalid input data\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	if (!ctrl->link->phy_params.phy_test_pattern_sel) {
+		DP_DEBUG("no test pattern selected by sink\n");
+		return;
+	}
+
+	DP_DEBUG("start\n");
+
+	/*
+	 * The global reset will need DP link ralated clocks to be
+	 * running. Add the global reset just before disabling the
+	 * link clocks and core clocks.
+	 */
+	ctrl->catalog->reset(ctrl->catalog);
+	ctrl->dp_ctrl.stream_pre_off(&ctrl->dp_ctrl, ctrl->panel);
+	ctrl->dp_ctrl.stream_off(&ctrl->dp_ctrl, ctrl->panel);
+	ctrl->dp_ctrl.off(&ctrl->dp_ctrl);
+
+	ctrl->aux->init(ctrl->aux, ctrl->parser->aux_cfg);
+
+	ret = ctrl->dp_ctrl.on(&ctrl->dp_ctrl, ctrl->mst_mode,
+			ctrl->fec_mode, ctrl->dsc_mode, false);
+	if (ret)
+		DP_ERR("failed to enable DP controller\n");
+
+	ctrl->dp_ctrl.stream_on(&ctrl->dp_ctrl, ctrl->panel);
+	DP_DEBUG("end\n");
+}
+
+static void dp_ctrl_send_phy_test_pattern(struct dp_ctrl_private *ctrl)
+{
+	bool success = false;
+	u32 pattern_sent = 0x0;
+	u32 pattern_requested = ctrl->link->phy_params.phy_test_pattern_sel;
+
+	dp_ctrl_update_hw_vx_px(ctrl);
+	ctrl->catalog->send_phy_pattern(ctrl->catalog, pattern_requested);
+	dp_ctrl_update_sink_vx_px(ctrl);
+	ctrl->link->send_test_response(ctrl->link);
+
+	pattern_sent = ctrl->catalog->read_phy_pattern(ctrl->catalog);
+	DP_DEBUG("pattern_request: %s. pattern_sent: 0x%x\n",
+			dp_link_get_phy_test_pattern(pattern_requested),
+			pattern_sent);
+
+	switch (pattern_sent) {
+	case MR_LINK_TRAINING1:
+		if (pattern_requested == DP_PHY_TEST_PATTERN_D10_2)
+			success = true;
+		break;
+	case MR_LINK_SYMBOL_ERM:
+		if ((pattern_requested == DP_PHY_TEST_PATTERN_ERROR_COUNT)
+			|| (pattern_requested == DP_PHY_TEST_PATTERN_CP2520))
+			success = true;
+		break;
+	case MR_LINK_PRBS7:
+		if (pattern_requested == DP_PHY_TEST_PATTERN_PRBS7)
+			success = true;
+		break;
+	case MR_LINK_CUSTOM80:
+		if (pattern_requested == DP_PHY_TEST_PATTERN_80BIT_CUSTOM)
+			success = true;
+		break;
+	case MR_LINK_TRAINING4:
+		if (pattern_requested == DP_PHY_TEST_PATTERN_CP2520_3)
+			success = true;
+		break;
+	default:
+		success = false;
+		break;
+	}
+
+	DP_DEBUG("%s: %s\n", success ? "success" : "failed",
+			dp_link_get_phy_test_pattern(pattern_requested));
+}
+
+static void dp_ctrl_mst_calculate_rg(struct dp_ctrl_private *ctrl,
+		struct dp_panel *panel, u32 *p_x_int, u32 *p_y_frac_enum)
+{
+	u64 min_slot_cnt, max_slot_cnt;
+	u64 raw_target_sc, target_sc_fixp;
+	u64 ts_denom, ts_enum, ts_int;
+	u64 pclk = panel->pinfo.pixel_clk_khz;
+	u64 lclk = 0;
+	u64 lanes = ctrl->link->link_params.lane_count;
+	u64 bpp = panel->pinfo.bpp;
+	u64 pbn = panel->pinfo.pbn_no_overhead; // before dsc/fec overhead
+	u64 numerator, denominator, temp, temp1, temp2;
+	u32 x_int = 0, y_frac_enum = 0;
+	u64 target_strm_sym, ts_int_fixp, ts_frac_fixp, y_frac_enum_fixp;
+
+	lclk = drm_dp_bw_code_to_link_rate(ctrl->link->link_params.bw_code);
+	if (panel->pinfo.comp_info.enabled)
+		bpp = panel->pinfo.comp_info.tgt_bpp;
+
+	/* min_slot_cnt */
+	numerator = pclk * bpp * 64 * 1000;
+	denominator = lclk * lanes * 8 * 1000;
+	min_slot_cnt = drm_fixp_from_fraction(numerator, denominator);
+
+	/* max_slot_cnt */
+	numerator = pbn * 54 * 1000;
+	denominator = lclk * lanes;
+	max_slot_cnt = drm_fixp_from_fraction(numerator, denominator);
+
+	/* raw_target_sc */
+	numerator = max_slot_cnt + min_slot_cnt;
+	denominator = drm_fixp_from_fraction(2, 1);
+	raw_target_sc = drm_fixp_div(numerator, denominator);
+
+	DP_DEBUG("raw_target_sc before overhead:0x%llx\n", raw_target_sc);
+	DP_DEBUG("dsc_overhead_fp:0x%llx\n", panel->pinfo.dsc_overhead_fp);
+
+	/* apply fec and dsc overhead factor */
+	if (panel->pinfo.dsc_overhead_fp)
+		raw_target_sc = drm_fixp_mul(raw_target_sc,
+					panel->pinfo.dsc_overhead_fp);
+
+	if (panel->fec_overhead_fp)
+		raw_target_sc = drm_fixp_mul(raw_target_sc,
+					panel->fec_overhead_fp);
+
+	DP_DEBUG("raw_target_sc after overhead:0x%llx\n", raw_target_sc);
+
+	/* target_sc */
+	temp = drm_fixp_from_fraction(256 * lanes, 1);
+	numerator = drm_fixp_mul(raw_target_sc, temp);
+	denominator = drm_fixp_from_fraction(256 * lanes, 1);
+	target_sc_fixp = drm_fixp_div(numerator, denominator);
+
+	ts_enum = 256 * lanes;
+	ts_denom = drm_fixp_from_fraction(256 * lanes, 1);
+	ts_int = drm_fixp2int(target_sc_fixp);
+
+	temp = drm_fixp2int_ceil(raw_target_sc);
+	if (temp != ts_int) {
+		temp = drm_fixp_from_fraction(ts_int, 1);
+		temp1 = raw_target_sc - temp;
+		temp2 = drm_fixp_mul(temp1, ts_denom);
+		ts_enum = drm_fixp2int(temp2);
+	}
+
+	/* target_strm_sym */
+	ts_int_fixp = drm_fixp_from_fraction(ts_int, 1);
+	ts_frac_fixp = drm_fixp_from_fraction(ts_enum, drm_fixp2int(ts_denom));
+	temp = ts_int_fixp + ts_frac_fixp;
+	temp1 = drm_fixp_from_fraction(lanes, 1);
+	target_strm_sym = drm_fixp_mul(temp, temp1);
+
+	/* x_int */
+	x_int = drm_fixp2int(target_strm_sym);
+
+	/* y_enum_frac */
+	temp = drm_fixp_from_fraction(x_int, 1);
+	temp1 = target_strm_sym - temp;
+	temp2 = drm_fixp_from_fraction(256, 1);
+	y_frac_enum_fixp = drm_fixp_mul(temp1, temp2);
+
+	temp1 = drm_fixp2int(y_frac_enum_fixp);
+	temp2 = drm_fixp2int_ceil(y_frac_enum_fixp);
+
+	y_frac_enum = (u32)((temp1 == temp2) ? temp1 : temp1 + 1);
+
+	panel->mst_target_sc = raw_target_sc;
+	*p_x_int = x_int;
+	*p_y_frac_enum = y_frac_enum;
+
+	DP_DEBUG("x_int: %d, y_frac_enum: %d\n", x_int, y_frac_enum);
+}
+
+static void dp_ctrl_mst_stream_setup(struct dp_ctrl_private *ctrl,
+		struct dp_panel *panel)
+{
+	u32 x_int, y_frac_enum, lanes, bw_code;
+	int i;
+
+	if (!ctrl->mst_mode)
+		return;
+
+	DP_MST_DEBUG("mst stream channel allocation\n");
+
+	for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
+		ctrl->catalog->channel_alloc(ctrl->catalog,
+				i,
+				ctrl->mst_ch_info.slot_info[i].start_slot,
+				ctrl->mst_ch_info.slot_info[i].tot_slots);
+	}
+
+	lanes = ctrl->link->link_params.lane_count;
+	bw_code = ctrl->link->link_params.bw_code;
+
+	dp_ctrl_mst_calculate_rg(ctrl, panel, &x_int, &y_frac_enum);
+
+	ctrl->catalog->update_rg(ctrl->catalog, panel->stream_id,
+			x_int, y_frac_enum);
+
+	DP_MST_DEBUG("mst stream:%d, start_slot:%d, tot_slots:%d\n",
+			panel->stream_id,
+			panel->channel_start_slot, panel->channel_total_slots);
+
+	DP_MST_DEBUG("mst lane_cnt:%d, bw:%d, x_int:%d, y_frac:%d\n",
+			lanes, bw_code, x_int, y_frac_enum);
+}
+
+static void dp_ctrl_dsc_setup(struct dp_ctrl_private *ctrl, struct dp_panel *panel)
+{
+	int rlen;
+	u32 dsc_enable;
+	struct dp_panel_info *pinfo = &panel->pinfo;
+
+	if (!ctrl->fec_mode)
+		return;
+
+	/* Set DP_DSC_ENABLE DPCD register if compression is enabled for SST monitor.
+	 * Set DP_DSC_ENABLE DPCD register if compression is enabled for
+	 * atleast 1 of the MST monitor.
+	 */
+	dsc_enable = (pinfo->comp_info.enabled == true) ? 1 : 0;
+
+	if (ctrl->mst_mode && (panel->stream_id == DP_STREAM_1) && !dsc_enable)
+		return;
+
+	rlen = drm_dp_dpcd_writeb(ctrl->aux->drm_aux, DP_DSC_ENABLE,
+			dsc_enable);
+	if (rlen < 1)
+		DP_WARN("failed to enable sink dsc\n");
+}
+
+static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)
+{
+	int rc = 0;
+	bool link_ready = false;
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl || !panel)
+		return -EINVAL;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	if (!ctrl->power_on) {
+		DP_DEBUG("controller powered off\n");
+		return -EPERM;
+	}
+
+	rc = dp_ctrl_enable_stream_clocks(ctrl, panel);
+	if (rc) {
+		DP_ERR("failure on stream clock enable\n");
+		return rc;
+	}
+
+	panel->pclk_on = true;
+	rc = panel->hw_cfg(panel, true);
+	if (rc)
+		return rc;
+
+	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
+		dp_ctrl_send_phy_test_pattern(ctrl);
+		return 0;
+	}
+
+	dp_ctrl_mst_stream_setup(ctrl, panel);
+
+	dp_ctrl_send_video(ctrl);
+
+	dp_ctrl_mst_send_act(ctrl);
+
+	dp_ctrl_wait4video_ready(ctrl);
+
+	ctrl->stream_count++;
+
+	link_ready = ctrl->catalog->mainlink_ready(ctrl->catalog);
+	DP_DEBUG("mainlink %s\n", link_ready ? "READY" : "NOT READY");
+
+	/* wait for link training completion before fec config as per spec */
+	dp_ctrl_fec_setup(ctrl);
+	dp_ctrl_dsc_setup(ctrl, panel);
+	panel->sink_crc_enable(panel, true);
+
+	return rc;
+}
+
+static void dp_ctrl_mst_stream_pre_off(struct dp_ctrl *dp_ctrl,
+		struct dp_panel *panel)
+{
+	struct dp_ctrl_private *ctrl;
+	bool act_complete;
+	int i;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	if (!ctrl->mst_mode)
+		return;
+
+	for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
+		ctrl->catalog->channel_alloc(ctrl->catalog,
+				i,
+				ctrl->mst_ch_info.slot_info[i].start_slot,
+				ctrl->mst_ch_info.slot_info[i].tot_slots);
+	}
+
+	ctrl->catalog->trigger_act(ctrl->catalog);
+	msleep(20); /* needs 1 frame time */
+	ctrl->catalog->read_act_complete_sts(ctrl->catalog, &act_complete);
+
+	if (!act_complete)
+		DP_ERR("mst stream_off act trigger complete failed\n");
+	else
+		DP_MST_DEBUG("mst stream_off ACT trigger complete SUCCESS\n");
+}
+
+static void dp_ctrl_stream_pre_off(struct dp_ctrl *dp_ctrl,
+		struct dp_panel *panel)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl || !panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	dp_ctrl_push_idle(ctrl, panel->stream_id);
+
+	dp_ctrl_mst_stream_pre_off(dp_ctrl, panel);
+}
+
+static void dp_ctrl_stream_off(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl || !panel)
+		return;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	if (!ctrl->power_on)
+		return;
+
+	panel->hw_cfg(panel, false);
+
+	panel->pclk_on = false;
+	dp_ctrl_disable_stream_clocks(ctrl, panel);
+	ctrl->stream_count--;
+}
+
+static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode,
+		bool fec_mode, bool dsc_mode, bool shallow)
+{
+	int rc = 0;
+	struct dp_ctrl_private *ctrl;
+	u32 rate = 0;
+
+	if (!dp_ctrl) {
+		rc = -EINVAL;
+		goto end;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	if (ctrl->power_on)
+		goto end;
+
+	if (atomic_read(&ctrl->aborted)) {
+		rc = -EPERM;
+		goto end;
+	}
+
+	ctrl->mst_mode = mst_mode;
+	if (fec_mode) {
+		ctrl->fec_mode = fec_mode;
+		ctrl->dsc_mode = dsc_mode;
+	}
+
+	rate = ctrl->panel->link_info.rate;
+
+	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
+		DP_DEBUG("using phy test link parameters\n");
+	} else {
+		ctrl->link->link_params.bw_code =
+			drm_dp_link_rate_to_bw_code(rate);
+		ctrl->link->link_params.lane_count =
+			ctrl->panel->link_info.num_lanes;
+	}
+
+	DP_DEBUG("bw_code=%d, lane_count=%d\n",
+		ctrl->link->link_params.bw_code,
+		ctrl->link->link_params.lane_count);
+
+	/* backup initial lane count and bw code */
+	ctrl->initial_lane_count = ctrl->link->link_params.lane_count;
+	ctrl->initial_bw_code = ctrl->link->link_params.bw_code;
+
+	rc = dp_ctrl_link_setup(ctrl, shallow);
+	if (!rc)
+		ctrl->power_on = true;
+end:
+	return rc;
+}
+
+static void dp_ctrl_off(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl)
+		return;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	if (!ctrl->power_on)
+		return;
+
+	ctrl->catalog->fec_config(ctrl->catalog, false);
+	dp_ctrl_configure_source_link_params(ctrl, false);
+	dp_ctrl_state_ctrl(ctrl, 0);
+
+	/* Make sure DP is disabled before clk disable */
+	wmb();
+
+	dp_ctrl_disable_link_clock(ctrl);
+
+	ctrl->mst_mode = false;
+	ctrl->fec_mode = false;
+	ctrl->dsc_mode = false;
+	ctrl->power_on = false;
+	memset(&ctrl->mst_ch_info, 0, sizeof(ctrl->mst_ch_info));
+	DP_DEBUG("DP off done\n");
+}
+
+static void dp_ctrl_set_mst_channel_info(struct dp_ctrl *dp_ctrl,
+		enum dp_stream_id strm,
+		u32 start_slot, u32 tot_slots)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl || strm >= DP_STREAM_MAX) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	ctrl->mst_ch_info.slot_info[strm].start_slot = start_slot;
+	ctrl->mst_ch_info.slot_info[strm].tot_slots = tot_slots;
+}
+
+static void dp_ctrl_isr(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl)
+		return;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	ctrl->catalog->get_interrupt(ctrl->catalog);
+	SDE_EVT32_EXTERNAL(ctrl->catalog->isr, ctrl->catalog->isr3, ctrl->catalog->isr5,
+			ctrl->catalog->isr6);
+
+	if (ctrl->catalog->isr & DP_CTRL_INTR_READY_FOR_VIDEO)
+		dp_ctrl_video_ready(ctrl);
+
+	if (ctrl->catalog->isr & DP_CTRL_INTR_IDLE_PATTERN_SENT)
+		dp_ctrl_idle_patterns_sent(ctrl);
+
+	if (ctrl->catalog->isr5 & DP_CTRL_INTR_MST_DP0_VCPF_SENT)
+		dp_ctrl_idle_patterns_sent(ctrl);
+
+	if (ctrl->catalog->isr5 & DP_CTRL_INTR_MST_DP1_VCPF_SENT)
+		dp_ctrl_idle_patterns_sent(ctrl);
+}
+
+void dp_ctrl_set_sim_mode(struct dp_ctrl *dp_ctrl, bool en)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl)
+		return;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+	ctrl->sim_mode = en;
+	DP_INFO("sim_mode=%d\n", ctrl->sim_mode);
+}
+
+int dp_ctrl_setup_misr(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl)
+		return -EINVAL;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	return ctrl->catalog->setup_misr(ctrl->catalog);
+}
+
+int dp_ctrl_read_misr(struct dp_ctrl *dp_ctrl, struct dp_misr40_data *data)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl)
+		return -EINVAL;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	return ctrl->catalog->read_misr(ctrl->catalog, data);
+}
+
+struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
+{
+	int rc = 0;
+	struct dp_ctrl_private *ctrl;
+	struct dp_ctrl *dp_ctrl;
+
+	if (!in->dev || !in->panel || !in->aux ||
+	    !in->link || !in->catalog) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	ctrl = devm_kzalloc(in->dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	init_completion(&ctrl->idle_comp);
+	init_completion(&ctrl->video_comp);
+
+	/* in parameters */
+	ctrl->parser   = in->parser;
+	ctrl->panel    = in->panel;
+	ctrl->power    = in->power;
+	ctrl->aux      = in->aux;
+	ctrl->link     = in->link;
+	ctrl->catalog  = in->catalog;
+	ctrl->pll  = in->pll;
+	ctrl->dev  = in->dev;
+	ctrl->mst_mode = false;
+	ctrl->fec_mode = false;
+
+	dp_ctrl = &ctrl->dp_ctrl;
+
+	/* out parameters */
+	dp_ctrl->init      = dp_ctrl_host_init;
+	dp_ctrl->deinit    = dp_ctrl_host_deinit;
+	dp_ctrl->on        = dp_ctrl_on;
+	dp_ctrl->off       = dp_ctrl_off;
+	dp_ctrl->abort     = dp_ctrl_abort;
+	dp_ctrl->isr       = dp_ctrl_isr;
+	dp_ctrl->link_maintenance = dp_ctrl_link_maintenance;
+	dp_ctrl->process_phy_test_request = dp_ctrl_process_phy_test_request;
+	dp_ctrl->stream_on = dp_ctrl_stream_on;
+	dp_ctrl->stream_off = dp_ctrl_stream_off;
+	dp_ctrl->stream_pre_off = dp_ctrl_stream_pre_off;
+	dp_ctrl->set_mst_channel_info = dp_ctrl_set_mst_channel_info;
+	dp_ctrl->set_sim_mode = dp_ctrl_set_sim_mode;
+	dp_ctrl->setup_misr = dp_ctrl_setup_misr;
+	dp_ctrl->read_misr = dp_ctrl_read_misr;
+
+	return dp_ctrl;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_ctrl_put(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl)
+		return;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	devm_kfree(ctrl->dev, ctrl);
+}

+ 54 - 0
qcom/opensource/display-drivers/msm/dp/dp_ctrl.h

@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_CTRL_H_
+#define _DP_CTRL_H_
+
+#include "dp_aux.h"
+#include "dp_panel.h"
+#include "dp_link.h"
+#include "dp_parser.h"
+#include "dp_power.h"
+#include "dp_catalog.h"
+#include "dp_debug.h"
+
+struct dp_ctrl {
+	int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset);
+	void (*deinit)(struct dp_ctrl *dp_ctrl);
+	int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode, bool fec_en,
+			bool dsc_en, bool shallow);
+	void (*off)(struct dp_ctrl *dp_ctrl);
+	void (*abort)(struct dp_ctrl *dp_ctrl, bool abort);
+	void (*isr)(struct dp_ctrl *dp_ctrl);
+	bool (*handle_sink_request)(struct dp_ctrl *dp_ctrl);
+	void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl);
+	int (*link_maintenance)(struct dp_ctrl *dp_ctrl);
+	int (*stream_on)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel);
+	void (*stream_off)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel);
+	void (*stream_pre_off)(struct dp_ctrl *dp_ctrl, struct dp_panel *panel);
+	void (*set_mst_channel_info)(struct dp_ctrl *dp_ctrl,
+			enum dp_stream_id strm,
+			u32 ch_start_slot, u32 ch_tot_slots);
+	void (*set_sim_mode)(struct dp_ctrl *dp_ctrl, bool en);
+	int (*setup_misr)(struct dp_ctrl *dp_ctrl);
+	int (*read_misr)(struct dp_ctrl *dp_ctrl, struct dp_misr40_data *data);
+};
+
+struct dp_ctrl_in {
+	struct device *dev;
+	struct dp_panel *panel;
+	struct dp_aux *aux;
+	struct dp_link *link;
+	struct dp_parser *parser;
+	struct dp_power *power;
+	struct dp_catalog_ctrl *catalog;
+	struct dp_pll *pll;
+};
+
+struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in);
+void dp_ctrl_put(struct dp_ctrl *dp_ctrl);
+
+#endif /* _DP_CTRL_H_ */

+ 2598 - 0
qcom/opensource/display-drivers/msm/dp/dp_debug.c

@@ -0,0 +1,2598 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0))
+#include <drm/display/drm_dp_mst_helper.h>
+#else
+#include <drm/drm_dp_mst_helper.h>
+#endif
+#include <drm/drm_probe_helper.h>
+
+#include "dp_power.h"
+#include "dp_catalog.h"
+#include "dp_aux.h"
+#include "dp_debug.h"
+#include "drm/drm_connector.h"
+#include "sde_connector.h"
+#include "dp_display.h"
+#include "dp_pll.h"
+#include "dp_hpd.h"
+#include "dp_mst_sim.h"
+#include "dp_mst_drm.h"
+
+#define DEBUG_NAME "drm_dp"
+
+struct dp_debug_private {
+	struct dentry *root;
+
+	u32 dpcd_offset;
+	u32 dpcd_size;
+
+	u32 mst_con_id;
+	u32 mst_edid_idx;
+	bool hotplug;
+	u32 sim_mode;
+
+	char exe_mode[SZ_32];
+	char reg_dump[SZ_32];
+
+	struct dp_hpd *hpd;
+	struct dp_link *link;
+	struct dp_panel *panel;
+	struct dp_aux *aux;
+	struct dp_catalog *catalog;
+	struct drm_connector **connector;
+	struct device *dev;
+	struct dp_debug dp_debug;
+	struct dp_parser *parser;
+	struct dp_ctrl *ctrl;
+	struct dp_pll *pll;
+	struct dp_display *display;
+	struct mutex lock;
+	struct dp_aux_bridge *sim_bridge;
+};
+
+static int dp_debug_sim_hpd_cb(void *arg, bool hpd, bool hpd_irq)
+{
+	struct dp_debug_private *debug = arg;
+	int vdo = 0;
+
+	if (hpd_irq) {
+		vdo |= BIT(7);
+
+		if (hpd)
+			vdo |= BIT(8);
+
+		return debug->hpd->simulate_attention(debug->hpd, vdo);
+	} else {
+		return debug->hpd->simulate_connect(debug->hpd, hpd);
+	}
+}
+
+static int dp_debug_attach_sim_bridge(struct dp_debug_private *debug)
+{
+	int ret;
+
+	if (!debug->sim_bridge) {
+		ret = dp_sim_create_bridge(debug->dev, &debug->sim_bridge);
+		if (ret)
+			return ret;
+
+		if (debug->sim_bridge->register_hpd)
+			debug->sim_bridge->register_hpd(debug->sim_bridge,
+					dp_debug_sim_hpd_cb, debug);
+	}
+
+	dp_sim_update_port_num(debug->sim_bridge, 1);
+
+	return 0;
+}
+
+static void dp_debug_enable_sim_mode(struct dp_debug_private *debug,
+		u32 mode_mask)
+{
+	/* return if mode is already enabled */
+	if ((debug->sim_mode & mode_mask) == mode_mask)
+		return;
+
+	/* create bridge if not yet */
+	if (dp_debug_attach_sim_bridge(debug))
+		return;
+
+	/* switch to bridge mode */
+	if (!debug->sim_mode)
+		debug->aux->set_sim_mode(debug->aux, debug->sim_bridge);
+
+	/* update sim mode */
+	debug->sim_mode |= mode_mask;
+	dp_sim_set_sim_mode(debug->sim_bridge, debug->sim_mode);
+}
+
+static void dp_debug_disable_sim_mode(struct dp_debug_private *debug,
+		u32 mode_mask)
+{
+	/* return if mode is already disabled */
+	if (!(debug->sim_mode & mode_mask))
+		return;
+
+	/* update sim mode */
+	debug->sim_mode &= ~mode_mask;
+	dp_sim_set_sim_mode(debug->sim_bridge, debug->sim_mode);
+
+	dp_sim_update_port_num(debug->sim_bridge, 0);
+
+	/* switch to normal mode */
+	if (!debug->sim_mode)
+		debug->aux->set_sim_mode(debug->aux, NULL);
+}
+
+static ssize_t dp_debug_write_edid(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	u8 *buf = NULL, *buf_t = NULL, *edid = NULL;
+	const int char_to_nib = 2;
+	size_t edid_size = 0;
+	size_t size = 0, edid_buf_index = 0;
+	ssize_t rc = count;
+
+	if (!debug)
+		return -ENODEV;
+
+	mutex_lock(&debug->lock);
+
+	if (*ppos)
+		goto bail;
+
+	size = min_t(size_t, count, SZ_1K);
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (ZERO_OR_NULL_PTR(buf)) {
+		rc = -ENOMEM;
+		goto bail;
+	}
+
+	if (copy_from_user(buf, user_buff, size))
+		goto bail;
+
+	edid_size = size / char_to_nib;
+	buf_t = buf;
+	size = edid_size;
+
+	edid = kzalloc(size, GFP_KERNEL);
+	if (!edid)
+		goto bail;
+
+	while (size--) {
+		char t[3];
+		int d;
+
+		memcpy(t, buf_t, sizeof(char) * char_to_nib);
+		t[char_to_nib] = '\0';
+
+		if (kstrtoint(t, 16, &d)) {
+			DP_ERR("kstrtoint error\n");
+			goto bail;
+		}
+
+		edid[edid_buf_index++] = d;
+		buf_t += char_to_nib;
+	}
+
+	dp_debug_enable_sim_mode(debug, DP_SIM_MODE_EDID);
+	dp_mst_clear_edid_cache(debug->display);
+	dp_sim_update_port_edid(debug->sim_bridge, debug->mst_edid_idx,
+			edid, edid_size);
+bail:
+	kfree(buf);
+	kfree(edid);
+
+	mutex_unlock(&debug->lock);
+	return rc;
+}
+
+static ssize_t dp_debug_write_dpcd(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	u8 *buf = NULL, *buf_t = NULL, *dpcd = NULL;
+	const int char_to_nib = 2;
+	size_t dpcd_size = 0;
+	size_t size = 0, dpcd_buf_index = 0;
+	ssize_t rc = count;
+	char offset_ch[5];
+	u32 offset, data_len;
+
+	if (!debug)
+		return -ENODEV;
+
+	mutex_lock(&debug->lock);
+
+	if (*ppos)
+		goto bail;
+
+	size = min_t(size_t, count, SZ_2K);
+
+	if (size < 4)
+		goto bail;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (ZERO_OR_NULL_PTR(buf)) {
+		rc = -ENOMEM;
+		goto bail;
+	}
+
+	if (copy_from_user(buf, user_buff, size))
+		goto bail;
+
+	memcpy(offset_ch, buf, 4);
+	offset_ch[4] = '\0';
+
+	if (kstrtoint(offset_ch, 16, &offset)) {
+		DP_ERR("offset kstrtoint error\n");
+		goto bail;
+	}
+	debug->dpcd_offset = offset;
+
+	size -= 4;
+	if (size < char_to_nib)
+		goto bail;
+
+	dpcd_size = size / char_to_nib;
+	data_len = dpcd_size;
+	buf_t = buf + 4;
+
+	dpcd = kzalloc(dpcd_size, GFP_KERNEL);
+	if (ZERO_OR_NULL_PTR(dpcd)) {
+		rc = -ENOMEM;
+		goto bail;
+	}
+
+	while (dpcd_size--) {
+		char t[3];
+		int d;
+
+		memcpy(t, buf_t, sizeof(char) * char_to_nib);
+		t[char_to_nib] = '\0';
+
+		if (kstrtoint(t, 16, &d)) {
+			DP_ERR("kstrtoint error\n");
+			goto bail;
+		}
+
+		dpcd[dpcd_buf_index++] = d;
+
+		buf_t += char_to_nib;
+	}
+
+	/*
+	 * if link training status registers are reprogramed,
+	 * read link training status from simulator, otherwise
+	 * read link training status from real aux channel.
+	 */
+	if (offset <= DP_LANE0_1_STATUS &&
+			offset + dpcd_buf_index > DP_LANE0_1_STATUS)
+		dp_debug_enable_sim_mode(debug,
+			DP_SIM_MODE_DPCD_READ | DP_SIM_MODE_LINK_TRAIN);
+	else
+		dp_debug_enable_sim_mode(debug, DP_SIM_MODE_DPCD_READ);
+
+	dp_sim_write_dpcd_reg(debug->sim_bridge,
+			dpcd, dpcd_buf_index, offset);
+	debug->dpcd_size = dpcd_buf_index;
+
+bail:
+	kfree(buf);
+	kfree(dpcd);
+
+	mutex_unlock(&debug->lock);
+	return rc;
+}
+
+static ssize_t dp_debug_read_dpcd(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	int const buf_size = SZ_4K;
+	u32 offset = 0;
+	u32 len = 0;
+	u8 *dpcd;
+
+	if (!debug || !debug->aux)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	buf = kzalloc(buf_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mutex_lock(&debug->lock);
+	dpcd = kzalloc(buf_size, GFP_KERNEL);
+	if (!dpcd)
+		goto bail;
+
+	/*
+	 * In simulation mode, this function returns the last written DPCD node.
+	 * For a real monitor plug in, it dumps the first byte at the last written DPCD address
+	 * unless the address is 0, in which case the first 20 bytes are dumped
+	 */
+	if (debug->dp_debug.sim_mode) {
+		dp_sim_read_dpcd_reg(debug->sim_bridge, dpcd, debug->dpcd_size, debug->dpcd_offset);
+	} else {
+		if (debug->dpcd_offset) {
+			debug->dpcd_size = 1;
+			if (drm_dp_dpcd_read(debug->aux->drm_aux, debug->dpcd_offset, dpcd,
+					debug->dpcd_size) != 1)
+				goto bail;
+		} else {
+			debug->dpcd_size = sizeof(debug->panel->dpcd);
+			memcpy(dpcd, debug->panel->dpcd, debug->dpcd_size);
+		}
+	}
+
+	len += scnprintf(buf + len, buf_size - len, "%04x: ", debug->dpcd_offset);
+
+	while (offset < debug->dpcd_size)
+		len += scnprintf(buf + len, buf_size - len, "%02x ", dpcd[offset++]);
+
+	kfree(dpcd);
+
+	len = min_t(size_t, count, len);
+	if (!copy_to_user(user_buff, buf, len))
+		*ppos += len;
+
+bail:
+	mutex_unlock(&debug->lock);
+	kfree(buf);
+
+	return len;
+}
+
+static ssize_t dp_debug_read_crc(struct file *file, char __user *user_buff, size_t count,
+		loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	int const buf_size = SZ_4K;
+	u32 len = 0;
+	u16 src_crc[3] = {0};
+	u16 sink_crc[3] = {0};
+	struct dp_misr40_data misr40 = {0};
+	u32 retries = 2;
+	struct drm_connector *drm_conn;
+	struct sde_connector *sde_conn;
+	struct dp_panel *panel;
+	int i;
+	int rc;
+
+	if (!debug || !debug->aux)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	buf = kzalloc(buf_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mutex_lock(&debug->lock);
+
+	if (!debug->panel || !debug->ctrl)
+		goto bail;
+
+	if (debug->panel->mst_state) {
+		drm_conn = drm_connector_lookup((*debug->connector)->dev, NULL, debug->mst_con_id);
+		if (!drm_conn) {
+			DP_ERR("connector %u not in mst list\n", debug->mst_con_id);
+			goto bail;
+		}
+
+		sde_conn = to_sde_connector(drm_conn);
+		panel = sde_conn->drv_panel;
+		drm_connector_put(drm_conn);
+
+		if (!panel)
+			goto bail;
+	} else {
+		panel = debug->panel;
+	}
+
+	if (!panel->pclk_on)
+		goto bail;
+
+	panel->get_sink_crc(panel, sink_crc);
+	if (!(sink_crc[0] + sink_crc[1] + sink_crc[2])) {
+		panel->sink_crc_enable(panel, true);
+		mutex_unlock(&debug->lock);
+		msleep(30);
+		mutex_lock(&debug->lock);
+		panel->get_sink_crc(panel, sink_crc);
+	}
+
+	panel->get_src_crc(panel, src_crc);
+
+	len += scnprintf(buf + len, buf_size - len, "FRAME_CRC:\nSource vs Sink\n");
+
+	len += scnprintf(buf + len, buf_size - len, "CRC_R: %04X %04X\n", src_crc[0], sink_crc[0]);
+	len += scnprintf(buf + len, buf_size - len, "CRC_G: %04X %04X\n", src_crc[1], sink_crc[1]);
+	len += scnprintf(buf + len, buf_size - len, "CRC_B: %04X %04X\n", src_crc[2], sink_crc[2]);
+
+	debug->ctrl->setup_misr(debug->ctrl);
+
+	while (retries--) {
+		mutex_unlock(&debug->lock);
+		msleep(30);
+		mutex_lock(&debug->lock);
+
+		rc = debug->ctrl->read_misr(debug->ctrl, &misr40);
+		if (rc != -EAGAIN)
+			break;
+	}
+
+	len += scnprintf(buf + len, buf_size - len, "\nMISR40:\nCTLR vs PHY\n");
+	for (i = 0; i < 4; i++) {
+		len += scnprintf(buf + len, buf_size - len, "Lane%d %08X%08X %08X%08X\n", i,
+				misr40.ctrl_misr[2 * i], misr40.ctrl_misr[(2 * i) + 1],
+				misr40.phy_misr[2 * i], misr40.phy_misr[(2 * i) + 1]);
+	}
+
+	len = min_t(size_t, count, len);
+	if (!copy_to_user(user_buff, buf, len))
+		*ppos += len;
+
+bail:
+	mutex_unlock(&debug->lock);
+	kfree(buf);
+
+	return len;
+}
+
+static ssize_t dp_debug_write_hpd(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	int const hpd_data_mask = 0x7;
+	int hpd = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto end;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &hpd) != 0)
+		goto end;
+
+	hpd &= hpd_data_mask;
+	debug->hotplug = !!(hpd & BIT(0));
+
+	debug->dp_debug.psm_enabled = !!(hpd & BIT(1));
+
+	/*
+	 * print hotplug value as this code is executed
+	 * only while running in debug mode which is manually
+	 * triggered by a tester or a script.
+	 */
+	DP_INFO("%s\n", debug->hotplug ? "[CONNECT]" : "[DISCONNECT]");
+
+	debug->hpd->simulate_connect(debug->hpd, debug->hotplug);
+end:
+	return len;
+}
+
+static ssize_t dp_debug_write_edid_modes(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	struct dp_panel *panel;
+	char buf[SZ_32];
+	size_t len = 0;
+	int hdisplay = 0, vdisplay = 0, vrefresh = 0, aspect_ratio;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		goto end;
+
+	panel = debug->panel;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_32 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto clear;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "%d %d %d %d", &hdisplay, &vdisplay, &vrefresh,
+				&aspect_ratio) != 4)
+		goto clear;
+
+	if (!hdisplay || !vdisplay || !vrefresh)
+		goto clear;
+
+	panel->mode_override = true;
+	panel->hdisplay = hdisplay;
+	panel->vdisplay = vdisplay;
+	panel->vrefresh = vrefresh;
+	panel->aspect_ratio = aspect_ratio;
+	goto end;
+clear:
+	DP_DEBUG("clearing debug modes\n");
+	panel->mode_override = false;
+end:
+	return len;
+}
+
+static ssize_t dp_debug_write_edid_modes_mst(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	struct drm_connector *connector;
+	struct sde_connector *sde_conn;
+	struct dp_panel *panel = NULL;
+	char buf[SZ_512];
+	char *read_buf;
+	size_t len = 0;
+
+	int hdisplay = 0, vdisplay = 0, vrefresh = 0, aspect_ratio = 0;
+	int con_id = 0, offset = 0, debug_en = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	mutex_lock(&debug->lock);
+
+	if (*ppos)
+		goto end;
+
+	len = min_t(size_t, count, SZ_512 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto end;
+
+	buf[len] = '\0';
+	read_buf = buf;
+
+	while (sscanf(read_buf, "%d %d %d %d %d %d%n", &debug_en, &con_id,
+			&hdisplay, &vdisplay, &vrefresh, &aspect_ratio,
+			&offset) == 6) {
+		connector = drm_connector_lookup((*debug->connector)->dev,
+				NULL, con_id);
+		if (connector) {
+			sde_conn = to_sde_connector(connector);
+			panel = sde_conn->drv_panel;
+			if (panel && sde_conn->mst_port) {
+				panel->mode_override = debug_en;
+				panel->hdisplay = hdisplay;
+				panel->vdisplay = vdisplay;
+				panel->vrefresh = vrefresh;
+				panel->aspect_ratio = aspect_ratio;
+			} else {
+				DP_ERR("connector id %d is not mst\n", con_id);
+			}
+			drm_connector_put(connector);
+		} else {
+			DP_ERR("invalid connector id %d\n", con_id);
+		}
+
+		read_buf += offset;
+	}
+end:
+	mutex_unlock(&debug->lock);
+	return len;
+}
+
+static ssize_t dp_debug_write_mst_con_id(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	struct drm_connector *connector;
+	struct sde_connector *sde_conn;
+	struct drm_dp_mst_port *mst_port;
+	struct dp_panel *dp_panel;
+	char buf[SZ_32];
+	size_t len = 0;
+	int con_id = 0, status;
+
+	if (!debug)
+		return -ENODEV;
+
+	mutex_lock(&debug->lock);
+
+	if (*ppos)
+		goto end;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_32 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto clear;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "%d %d", &con_id, &status) != 2)
+		goto end;
+
+	if (!con_id)
+		goto clear;
+
+	connector = drm_connector_lookup((*debug->connector)->dev,
+			NULL, con_id);
+	if (!connector) {
+		DP_ERR("invalid connector id %u\n", con_id);
+		goto end;
+	}
+
+	sde_conn = to_sde_connector(connector);
+
+	if (!sde_conn->drv_panel || !sde_conn->mst_port) {
+		DP_ERR("invalid connector state %d\n", con_id);
+		goto out;
+	}
+
+	debug->mst_con_id = con_id;
+
+	if (status == connector_status_unknown)
+		goto out;
+
+	if (status == connector_status_connected)
+		DP_INFO("plug mst connector %d\n", con_id);
+	else if (status == connector_status_disconnected)
+		DP_INFO("unplug mst connector %d\n", con_id);
+
+	mst_port = sde_conn->mst_port;
+	dp_panel = sde_conn->drv_panel;
+	if (!dp_panel)
+		goto out;
+
+	if (debug->dp_debug.sim_mode)
+		dp_sim_update_port_status(debug->sim_bridge, mst_port->port_num, status);
+	else
+		dp_panel->mst_hide = (status == connector_status_disconnected);
+
+	drm_kms_helper_hotplug_event(connector->dev);
+
+out:
+	drm_connector_put(connector);
+	goto end;
+clear:
+	DP_DEBUG("clearing mst_con_id\n");
+	debug->mst_con_id = 0;
+end:
+	mutex_unlock(&debug->lock);
+	return len;
+}
+
+static ssize_t dp_debug_write_mst_con_add(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_32];
+	size_t len = 0;
+	const int dp_en = BIT(3), hpd_high = BIT(7), hpd_irq = BIT(8);
+	int vdo = dp_en | hpd_high | hpd_irq;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_32 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto end;
+
+	debug->dp_debug.mst_sim_add_con = true;
+	debug->hpd->simulate_attention(debug->hpd, vdo);
+end:
+	return len;
+}
+
+static ssize_t dp_debug_write_mst_con_remove(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	struct drm_connector_list_iter conn_iter;
+	struct drm_connector *connector;
+	char buf[SZ_32];
+	size_t len = 0;
+	int con_id = 0;
+	bool in_list = false;
+	const int dp_en = BIT(3), hpd_high = BIT(7), hpd_irq = BIT(8);
+	int vdo = dp_en | hpd_high | hpd_irq;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_32 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto end;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "%d", &con_id) != 1) {
+		len = 0;
+		goto end;
+	}
+
+	if (!con_id)
+		goto end;
+
+	drm_connector_list_iter_begin((*debug->connector)->dev, &conn_iter);
+	drm_for_each_connector_iter(connector, &conn_iter) {
+		if (connector->base.id == con_id) {
+			in_list = true;
+			break;
+		}
+	}
+	drm_connector_list_iter_end(&conn_iter);
+
+	if (!in_list) {
+		DRM_ERROR("invalid connector id %u\n", con_id);
+		goto end;
+	}
+
+	debug->dp_debug.mst_sim_remove_con = true;
+	debug->dp_debug.mst_sim_remove_con_id = con_id;
+	debug->hpd->simulate_attention(debug->hpd, vdo);
+end:
+	return len;
+}
+
+static ssize_t dp_debug_mmrm_clk_cb_write(struct file *file,
+		 const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	struct dss_clk_mmrm_cb mmrm_cb_data;
+	struct mmrm_client_notifier_data notifier_data;
+	struct dp_display *dp_display;
+	int cb_type;
+
+	if (!debug)
+		return -ENODEV;
+	if (*ppos)
+		return 0;
+
+	dp_display = debug->display;
+
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		return 0;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &cb_type) != 0)
+		return 0;
+	if (cb_type != MMRM_CLIENT_RESOURCE_VALUE_CHANGE)
+		return 0;
+
+	notifier_data.cb_type = MMRM_CLIENT_RESOURCE_VALUE_CHANGE;
+	mmrm_cb_data.phandle = (void *)dp_display;
+	notifier_data.pvt_data = (void *)&mmrm_cb_data;
+
+	dp_display_mmrm_callback(&notifier_data);
+
+	return len;
+}
+
+static ssize_t dp_debug_bw_code_write(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	u32 max_bw_code = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		return 0;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &max_bw_code) != 0)
+		return 0;
+
+	if (!is_link_rate_valid(max_bw_code)) {
+		DP_ERR("Unsupported bw code %d\n", max_bw_code);
+		return len;
+	}
+	debug->panel->max_bw_code = max_bw_code;
+	DP_DEBUG("max_bw_code: %d\n", max_bw_code);
+
+	return len;
+}
+
+static ssize_t dp_debug_mst_mode_read(struct file *file,
+	char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[64];
+	ssize_t len;
+
+	len = scnprintf(buf, sizeof(buf),
+			"mst_mode = %d, mst_state = %d\n",
+			debug->parser->has_mst,
+			debug->panel->mst_state);
+
+	return simple_read_from_buffer(user_buff, count, ppos, buf, len);
+}
+
+static ssize_t dp_debug_mst_mode_write(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	u32 mst_mode = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		return 0;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &mst_mode) != 0)
+		return 0;
+
+	debug->parser->has_mst = mst_mode ? true : false;
+	DP_DEBUG("mst_enable: %d\n", mst_mode);
+
+	return len;
+}
+
+static ssize_t dp_debug_max_pclk_khz_write(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	u32 max_pclk = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		return 0;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &max_pclk) != 0)
+		return 0;
+
+	if (max_pclk > debug->parser->max_pclk_khz)
+		DP_ERR("requested: %d, max_pclk_khz:%d\n", max_pclk,
+				debug->parser->max_pclk_khz);
+	else
+		debug->dp_debug.max_pclk_khz = max_pclk;
+
+	DP_DEBUG("max_pclk_khz: %d\n", max_pclk);
+
+	return len;
+}
+
+static ssize_t dp_debug_max_pclk_khz_read(struct file *file,
+	char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	u32 len = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (ZERO_OR_NULL_PTR(buf))
+		return -ENOMEM;
+
+	len += snprintf(buf + len, (SZ_4K - len),
+			"max_pclk_khz = %d, org: %d\n",
+			debug->dp_debug.max_pclk_khz,
+			debug->parser->max_pclk_khz);
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len)) {
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	*ppos += len;
+	kfree(buf);
+	return len;
+}
+
+static ssize_t dp_debug_mst_sideband_mode_write(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	int mst_sideband_mode = 0;
+	u32 mst_port_cnt = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	mutex_lock(&debug->lock);
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "%d %u", &mst_sideband_mode, &mst_port_cnt) != 2) {
+		DP_ERR("invalid input\n");
+		goto bail;
+	}
+
+	if (!mst_port_cnt)
+		mst_port_cnt = 1;
+
+	debug->mst_edid_idx = 0;
+
+	if (mst_sideband_mode)
+		dp_debug_disable_sim_mode(debug, DP_SIM_MODE_MST);
+	else
+		dp_debug_enable_sim_mode(debug, DP_SIM_MODE_MST);
+
+	dp_sim_update_port_num(debug->sim_bridge, mst_port_cnt);
+
+	buf[0] = !mst_sideband_mode;
+	dp_sim_write_dpcd_reg(debug->sim_bridge, buf, 1, DP_MSTM_CAP);
+
+	DP_DEBUG("mst_sideband_mode: %d port_cnt:%d\n",
+			mst_sideband_mode, mst_port_cnt);
+
+bail:
+	mutex_unlock(&debug->lock);
+	return count;
+}
+
+static ssize_t dp_debug_tpg_write(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	u32 tpg_pattern = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto bail;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &tpg_pattern) != 0)
+		goto bail;
+
+	DP_DEBUG("tpg_pattern: %d\n", tpg_pattern);
+
+	if (tpg_pattern == debug->dp_debug.tpg_pattern)
+		goto bail;
+
+	if (debug->panel)
+		debug->panel->tpg_config(debug->panel, tpg_pattern);
+
+	debug->dp_debug.tpg_pattern = tpg_pattern;
+bail:
+	return len;
+}
+
+static ssize_t dp_debug_write_exe_mode(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_32];
+	size_t len = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	len = min_t(size_t, count, SZ_32 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto end;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "%3s", debug->exe_mode) != 1)
+		goto end;
+
+	if (strcmp(debug->exe_mode, "hw") &&
+	    strcmp(debug->exe_mode, "sw") &&
+	    strcmp(debug->exe_mode, "all"))
+		goto end;
+
+	debug->catalog->set_exe_mode(debug->catalog, debug->exe_mode);
+end:
+	return len;
+}
+
+static ssize_t dp_debug_read_connected(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	u32 len = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	len += snprintf(buf, SZ_8, "%d\n", debug->hpd->hpd_high);
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len))
+		return -EFAULT;
+
+	*ppos += len;
+	return len;
+}
+
+static ssize_t dp_debug_write_hdcp(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	int hdcp = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto end;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &hdcp) != 0)
+		goto end;
+
+	debug->dp_debug.hdcp_disabled = !hdcp;
+end:
+	return len;
+}
+
+static ssize_t dp_debug_read_hdcp(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	u32 len = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	len = sizeof(debug->dp_debug.hdcp_status);
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, debug->dp_debug.hdcp_status, len))
+		return -EFAULT;
+
+	*ppos += len;
+	return len;
+}
+
+static int dp_debug_check_buffer_overflow(int rc, int *max_size, int *len)
+{
+	if (rc >= *max_size) {
+		DP_ERR("buffer overflow\n");
+		return -EINVAL;
+	}
+	*len += rc;
+	*max_size = SZ_4K - *len;
+
+	return 0;
+}
+
+static ssize_t dp_debug_read_edid_modes(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	u32 len = 0, ret = 0, max_size = SZ_4K;
+	int rc = 0;
+	struct drm_connector *connector;
+	struct drm_display_mode *mode;
+
+	if (!debug) {
+		DP_ERR("invalid data\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	connector = *debug->connector;
+
+	if (!connector) {
+		DP_ERR("connector is NULL\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (*ppos)
+		goto error;
+
+	buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (ZERO_OR_NULL_PTR(buf)) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	mutex_lock(&connector->dev->mode_config.mutex);
+	list_for_each_entry(mode, &connector->modes, head) {
+		ret = snprintf(buf + len, max_size,
+		"%s %d %d %d %d %d 0x%x\n",
+		mode->name, drm_mode_vrefresh(mode), mode->picture_aspect_ratio,
+		mode->htotal, mode->vtotal, mode->clock, mode->flags);
+		if (dp_debug_check_buffer_overflow(ret, &max_size, &len))
+			break;
+	}
+	mutex_unlock(&connector->dev->mode_config.mutex);
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len)) {
+		kfree(buf);
+		rc = -EFAULT;
+		goto error;
+	}
+
+	*ppos += len;
+	kfree(buf);
+
+	return len;
+error:
+	return rc;
+}
+
+static ssize_t dp_debug_read_edid_modes_mst(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	u32 len = 0, ret = 0, max_size = SZ_4K;
+	struct drm_connector *connector;
+	struct drm_display_mode *mode;
+
+	if (!debug) {
+		DP_ERR("invalid data\n");
+		return -ENODEV;
+	}
+
+	if (*ppos)
+		return 0;
+
+	connector = drm_connector_lookup((*debug->connector)->dev,
+			NULL, debug->mst_con_id);
+	if (!connector) {
+		DP_ERR("connector %u not in mst list\n", debug->mst_con_id);
+		return 0;
+	}
+
+	buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (!buf)
+		goto clean;
+
+	mutex_lock(&connector->dev->mode_config.mutex);
+	list_for_each_entry(mode, &connector->modes, head) {
+		ret = snprintf(buf + len, max_size,
+				"%s %d %d %d %d %d 0x%x\n",
+				mode->name, drm_mode_vrefresh(mode),
+				mode->picture_aspect_ratio, mode->htotal,
+				mode->vtotal, mode->clock, mode->flags);
+		if (dp_debug_check_buffer_overflow(ret, &max_size, &len))
+			break;
+	}
+	mutex_unlock(&connector->dev->mode_config.mutex);
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len)) {
+		len = -EFAULT;
+		goto clean;
+	}
+
+	*ppos += len;
+clean:
+	kfree(buf);
+	drm_connector_put(connector);
+	return len;
+}
+
+static ssize_t dp_debug_read_mst_con_id(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	u32 len = 0, ret = 0, max_size = SZ_4K;
+	int rc = 0;
+
+	if (!debug) {
+		DP_ERR("invalid data\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	if (*ppos)
+		goto error;
+
+	buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	ret = snprintf(buf, max_size, "%u\n", debug->mst_con_id);
+	len += ret;
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len)) {
+		kfree(buf);
+		rc = -EFAULT;
+		goto error;
+	}
+
+	*ppos += len;
+	kfree(buf);
+
+	return len;
+error:
+	return rc;
+}
+
+static ssize_t dp_debug_read_mst_conn_info(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	struct drm_connector_list_iter conn_iter;
+	struct drm_connector *connector;
+	struct sde_connector *sde_conn;
+	struct dp_display *display;
+	char *buf;
+	u32 len = 0, ret = 0, max_size = SZ_4K;
+	int rc = 0;
+
+	if (!debug) {
+		DP_ERR("invalid data\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	if (*ppos)
+		goto error;
+
+	buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	drm_connector_list_iter_begin((*debug->connector)->dev, &conn_iter);
+	drm_for_each_connector_iter(connector, &conn_iter) {
+		sde_conn = to_sde_connector(connector);
+		display = sde_conn->display;
+		if (!sde_conn->mst_port ||
+				display->base_connector != (*debug->connector))
+			continue;
+		ret = scnprintf(buf + len, max_size,
+				"conn name:%s, conn id:%d state:%d\n",
+				connector->name, connector->base.id,
+				connector->status);
+		if (dp_debug_check_buffer_overflow(ret, &max_size, &len))
+			break;
+	}
+	drm_connector_list_iter_end(&conn_iter);
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len)) {
+		kfree(buf);
+		rc = -EFAULT;
+		goto error;
+	}
+
+	*ppos += len;
+	kfree(buf);
+
+	return len;
+error:
+	return rc;
+}
+
+static ssize_t dp_debug_read_info(struct file *file, char __user *user_buff,
+		size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	u32 len = 0, rc = 0;
+	u32 max_size = SZ_4K;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (ZERO_OR_NULL_PTR(buf))
+		return -ENOMEM;
+
+	rc = snprintf(buf + len, max_size, "\tstate=0x%x\n", debug->aux->state);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "\tlink_rate=%u\n",
+		debug->panel->link_info.rate);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "\tnum_lanes=%u\n",
+		debug->panel->link_info.num_lanes);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "\tresolution=%dx%d@%dHz\n",
+		debug->panel->pinfo.h_active,
+		debug->panel->pinfo.v_active,
+		debug->panel->pinfo.refresh_rate);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "\tpclock=%dKHz\n",
+		debug->panel->pinfo.pixel_clk_khz);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "\tbpp=%d\n",
+		debug->panel->pinfo.bpp);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	/* Link Information */
+	rc = snprintf(buf + len, max_size, "\ttest_req=%s\n",
+		dp_link_get_test_name(debug->link->sink_request));
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\tlane_count=%d\n", debug->link->link_params.lane_count);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\tbw_code=%d\n", debug->link->link_params.bw_code);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\tv_level=%d\n", debug->link->phy_params.v_level);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\tp_level=%d\n", debug->link->phy_params.p_level);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len))
+		goto error;
+
+	*ppos += len;
+
+	kfree(buf);
+	return len;
+error:
+	kfree(buf);
+	return -EINVAL;
+}
+
+static ssize_t dp_debug_bw_code_read(struct file *file,
+	char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	u32 len = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (ZERO_OR_NULL_PTR(buf))
+		return -ENOMEM;
+
+	len += snprintf(buf + len, (SZ_4K - len),
+			"max_bw_code = %d\n", debug->panel->max_bw_code);
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len)) {
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	*ppos += len;
+	kfree(buf);
+	return len;
+}
+
+static ssize_t dp_debug_tpg_read(struct file *file,
+	char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	u32 len = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	len += scnprintf(buf, SZ_8, "%d\n", debug->dp_debug.tpg_pattern);
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len))
+		return -EFAULT;
+
+	*ppos += len;
+	return len;
+}
+
+static int dp_debug_print_hdr_params_to_buf(struct drm_connector *connector,
+		char *buf, u32 size)
+{
+	int rc;
+	u32 i, len = 0, max_size = size;
+	struct sde_connector *c_conn;
+	struct sde_connector_state *c_state;
+	struct drm_msm_ext_hdr_metadata *hdr;
+
+	c_conn = to_sde_connector(connector);
+	c_state = to_sde_connector_state(connector->state);
+
+	hdr = &c_state->hdr_meta;
+
+	rc = snprintf(buf + len, max_size,
+		"============SINK HDR PARAMETERS===========\n");
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "eotf = %d\n",
+		c_conn->hdr_eotf);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "type_one = %d\n",
+		c_conn->hdr_metadata_type_one);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "hdr_plus_app_ver = %d\n",
+			c_conn->hdr_plus_app_ver);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "max_luminance = %d\n",
+		c_conn->hdr_max_luminance);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "avg_luminance = %d\n",
+		c_conn->hdr_avg_luminance);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "min_luminance = %d\n",
+		c_conn->hdr_min_luminance);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"============VIDEO HDR PARAMETERS===========\n");
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "hdr_state = %d\n", hdr->hdr_state);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "hdr_supported = %d\n",
+			hdr->hdr_supported);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "eotf = %d\n", hdr->eotf);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "white_point_x = %d\n",
+		hdr->white_point_x);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "white_point_y = %d\n",
+		hdr->white_point_y);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "max_luminance = %d\n",
+		hdr->max_luminance);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "min_luminance = %d\n",
+		hdr->min_luminance);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "max_content_light_level = %d\n",
+		hdr->max_content_light_level);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "min_content_light_level = %d\n",
+		hdr->max_average_light_level);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	for (i = 0; i < HDR_PRIMARIES_COUNT; i++) {
+		rc = snprintf(buf + len, max_size, "primaries_x[%d] = %d\n",
+			i, hdr->display_primaries_x[i]);
+		if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+			goto error;
+
+		rc = snprintf(buf + len, max_size, "primaries_y[%d] = %d\n",
+			i, hdr->display_primaries_y[i]);
+		if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+			goto error;
+	}
+
+	if (hdr->hdr_plus_payload && hdr->hdr_plus_payload_size) {
+		u32 rowsize = 16, rem;
+		struct sde_connector_dyn_hdr_metadata *dhdr =
+				&c_state->dyn_hdr_meta;
+
+		/**
+		 * Do not use user pointer from hdr->hdr_plus_payload directly,
+		 * instead use kernel's cached copy of payload data.
+		 */
+		for (i = 0; i < dhdr->dynamic_hdr_payload_size; i += rowsize) {
+			rc = snprintf(buf + len, max_size, "DHDR: ");
+			if (dp_debug_check_buffer_overflow(rc, &max_size,
+					&len))
+				goto error;
+
+			rem = dhdr->dynamic_hdr_payload_size - i;
+			rc = hex_dump_to_buffer(&dhdr->dynamic_hdr_payload[i],
+				min(rowsize, rem), rowsize, 1, buf + len,
+				max_size, false);
+			if (dp_debug_check_buffer_overflow(rc, &max_size,
+					&len))
+				goto error;
+
+			rc = snprintf(buf + len, max_size, "\n");
+			if (dp_debug_check_buffer_overflow(rc, &max_size,
+					&len))
+				goto error;
+		}
+	}
+
+	return len;
+error:
+	return -EOVERFLOW;
+}
+
+static ssize_t dp_debug_read_hdr(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf = NULL;
+	u32 len = 0;
+	u32 max_size = SZ_4K;
+	struct drm_connector *connector;
+
+	if (!debug) {
+		DP_ERR("invalid data\n");
+		return -ENODEV;
+	}
+
+	connector = *debug->connector;
+
+	if (!connector) {
+		DP_ERR("connector is NULL\n");
+		return -EINVAL;
+	}
+
+	if (*ppos)
+		return 0;
+
+	buf = kzalloc(max_size, GFP_KERNEL);
+	if (ZERO_OR_NULL_PTR(buf))
+		return -ENOMEM;
+
+	len = dp_debug_print_hdr_params_to_buf(connector, buf, max_size);
+	if (len == -EOVERFLOW) {
+		kfree(buf);
+		return len;
+	}
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len)) {
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	*ppos += len;
+	kfree(buf);
+	return len;
+}
+
+static ssize_t dp_debug_read_hdr_mst(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf = NULL;
+	u32 len = 0, max_size = SZ_4K;
+	struct drm_connector_list_iter conn_iter;
+	struct drm_connector *connector;
+	bool in_list = false;
+
+	if (!debug) {
+		DP_ERR("invalid data\n");
+		return -ENODEV;
+	}
+
+	drm_connector_list_iter_begin((*debug->connector)->dev, &conn_iter);
+	drm_for_each_connector_iter(connector, &conn_iter) {
+		if (connector->base.id == debug->mst_con_id) {
+			in_list = true;
+			break;
+		}
+	}
+	drm_connector_list_iter_end(&conn_iter);
+
+	if (!in_list) {
+		DP_ERR("connector %u not in mst list\n", debug->mst_con_id);
+		return -EINVAL;
+	}
+
+	if (!connector) {
+		DP_ERR("connector is NULL\n");
+		return -EINVAL;
+	}
+
+	if (*ppos)
+		return 0;
+
+
+	buf = kzalloc(max_size, GFP_KERNEL);
+	if (ZERO_OR_NULL_PTR(buf))
+		return -ENOMEM;
+
+	len = dp_debug_print_hdr_params_to_buf(connector, buf, max_size);
+	if (len == -EOVERFLOW) {
+		kfree(buf);
+		return len;
+	}
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len)) {
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	*ppos += len;
+	kfree(buf);
+	return len;
+}
+
+static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim)
+{
+	struct drm_connector_list_iter conn_iter;
+	struct drm_connector *connector;
+	struct sde_connector *sde_conn;
+	struct dp_display *display;
+	struct dp_panel *panel;
+
+	if (sim) {
+		debug->dp_debug.sim_mode = true;
+		dp_debug_enable_sim_mode(debug, DP_SIM_MODE_ALL);
+	} else {
+		if (debug->hotplug) {
+			DP_WARN("sim mode off before hotplug disconnect\n");
+			debug->hpd->simulate_connect(debug->hpd, false);
+			debug->hotplug = false;
+		}
+		debug->aux->abort(debug->aux, true);
+		debug->ctrl->abort(debug->ctrl, true);
+
+		debug->dp_debug.sim_mode = false;
+
+		debug->mst_edid_idx = 0;
+		dp_debug_disable_sim_mode(debug, DP_SIM_MODE_ALL);
+	}
+
+	/* clear override settings in panel */
+	drm_connector_list_iter_begin((*debug->connector)->dev, &conn_iter);
+	drm_for_each_connector_iter(connector, &conn_iter) {
+		sde_conn = to_sde_connector(connector);
+		display = sde_conn->display;
+		if (display->base_connector == (*debug->connector)) {
+			panel = sde_conn->drv_panel;
+			if (panel) {
+				panel->mode_override = false;
+				panel->mst_hide = false;
+			}
+		}
+	}
+	drm_connector_list_iter_end(&conn_iter);
+
+	/*
+	 * print simulation status as this code is executed
+	 * only while running in debug mode which is manually
+	 * triggered by a tester or a script.
+	 */
+	DP_INFO("%s\n", sim ? "[ON]" : "[OFF]");
+}
+
+static ssize_t dp_debug_write_sim(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	int sim;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	mutex_lock(&debug->lock);
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto end;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &sim) != 0)
+		goto end;
+
+	dp_debug_set_sim_mode(debug, sim);
+end:
+	mutex_unlock(&debug->lock);
+	return len;
+}
+
+static ssize_t dp_debug_write_attention(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	int vdo;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto end;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &vdo) != 0)
+		goto end;
+
+	debug->hpd->simulate_attention(debug->hpd, vdo);
+end:
+	return len;
+}
+
+static ssize_t dp_debug_write_dump(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_32];
+	size_t len = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_32 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto end;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "%31s", debug->reg_dump) != 1)
+		goto end;
+
+	/* qfprom register dump not supported */
+	if (!strcmp(debug->reg_dump, "qfprom_physical"))
+		strlcpy(debug->reg_dump, "clear", sizeof(debug->reg_dump));
+end:
+	return len;
+}
+
+static ssize_t dp_debug_read_dump(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	int rc = 0;
+	struct dp_debug_private *debug = file->private_data;
+	u8 *buf = NULL;
+	u32 len = 0;
+	char prefix[SZ_32];
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	if (!debug->hpd->hpd_high || !strlen(debug->reg_dump))
+		goto end;
+
+	rc = debug->catalog->get_reg_dump(debug->catalog,
+		debug->reg_dump, &buf, &len);
+	if (rc)
+		goto end;
+
+	snprintf(prefix, sizeof(prefix), "%s: ", debug->reg_dump);
+	print_hex_dump_debug(prefix, DUMP_PREFIX_NONE,
+		16, 4, buf, len, false);
+
+	len = min_t(size_t, count, len);
+	if (copy_to_user(user_buff, buf, len))
+		return -EFAULT;
+
+	*ppos += len;
+end:
+	return len;
+}
+
+static const struct file_operations dp_debug_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_info,
+};
+
+static const struct file_operations edid_modes_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_edid_modes,
+	.write = dp_debug_write_edid_modes,
+};
+
+static const struct file_operations edid_modes_mst_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_edid_modes_mst,
+	.write = dp_debug_write_edid_modes_mst,
+};
+
+static const struct file_operations mst_conn_info_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_mst_conn_info,
+};
+
+static const struct file_operations mst_con_id_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_mst_con_id,
+	.write = dp_debug_write_mst_con_id,
+};
+
+static const struct file_operations mst_con_add_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_mst_con_add,
+};
+
+static const struct file_operations mst_con_remove_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_mst_con_remove,
+};
+
+static const struct file_operations hpd_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_hpd,
+};
+
+static const struct file_operations edid_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_edid,
+};
+
+static const struct file_operations dpcd_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_dpcd,
+	.read = dp_debug_read_dpcd,
+};
+
+static const struct file_operations crc_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_crc,
+};
+
+static const struct file_operations connected_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_connected,
+};
+
+static const struct file_operations bw_code_fops = {
+	.open = simple_open,
+	.read = dp_debug_bw_code_read,
+	.write = dp_debug_bw_code_write,
+};
+static const struct file_operations exe_mode_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_exe_mode,
+};
+
+static const struct file_operations tpg_fops = {
+	.open = simple_open,
+	.read = dp_debug_tpg_read,
+	.write = dp_debug_tpg_write,
+};
+
+static const struct file_operations hdr_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_hdr,
+};
+
+static const struct file_operations hdr_mst_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_hdr_mst,
+};
+
+static const struct file_operations sim_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_sim,
+};
+
+static const struct file_operations attention_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_attention,
+};
+
+static const struct file_operations dump_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_dump,
+	.read = dp_debug_read_dump,
+};
+
+static const struct file_operations mst_mode_fops = {
+	.open = simple_open,
+	.write = dp_debug_mst_mode_write,
+	.read = dp_debug_mst_mode_read,
+};
+
+static const struct file_operations mst_sideband_mode_fops = {
+	.open = simple_open,
+	.write = dp_debug_mst_sideband_mode_write,
+};
+
+static const struct file_operations max_pclk_khz_fops = {
+	.open = simple_open,
+	.write = dp_debug_max_pclk_khz_write,
+	.read = dp_debug_max_pclk_khz_read,
+};
+
+static const struct file_operations hdcp_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_hdcp,
+	.read = dp_debug_read_hdcp,
+};
+
+static const struct file_operations mmrm_clk_cb_fops = {
+	.open = simple_open,
+	.write = dp_debug_mmrm_clk_cb_write,
+};
+
+static int dp_debug_init_mst(struct dp_debug_private *debug, struct dentry *dir)
+{
+	int rc = 0;
+	struct dentry *file;
+
+	file = debugfs_create_file("mst_con_id", 0644, dir,
+					debug, &mst_con_id_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs create mst_con_id failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("mst_con_info", 0644, dir,
+					debug, &mst_conn_info_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs create mst_conn_info failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("mst_con_add", 0644, dir,
+					debug, &mst_con_add_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DRM_ERROR("[%s] debugfs create mst_con_add failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("mst_con_remove", 0644, dir,
+					debug, &mst_con_remove_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DRM_ERROR("[%s] debugfs create mst_con_remove failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("mst_mode", 0644, dir,
+			debug, &mst_mode_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs mst_mode failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("mst_sideband_mode", 0644, dir,
+			debug, &mst_sideband_mode_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs mst_sideband_mode failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	debugfs_create_u32("mst_edid_idx", 0644, dir, &debug->mst_edid_idx);
+
+	return rc;
+}
+
+static int dp_debug_init_link(struct dp_debug_private *debug,
+		struct dentry *dir)
+{
+	int rc = 0;
+	struct dentry *file;
+
+	file = debugfs_create_file("max_bw_code", 0644, dir,
+			debug, &bw_code_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs max_bw_code failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("max_pclk_khz", 0644, dir,
+			debug, &max_pclk_khz_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs max_pclk_khz failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	debugfs_create_u32("max_lclk_khz", 0644, dir, &debug->parser->max_lclk_khz);
+
+	debugfs_create_u32("lane_count", 0644, dir, &debug->panel->lane_count);
+
+	debugfs_create_u32("link_bw_code", 0644, dir, &debug->panel->link_bw_code);
+
+	debugfs_create_u32("max_bpp", 0644, dir, &debug->panel->max_supported_bpp);
+
+	file = debugfs_create_file("mmrm_clk_cb", 0644, dir, debug, &mmrm_clk_cb_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs mmrm_clk_cb failed, rc=%d\n", DEBUG_NAME, rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int dp_debug_init_hdcp(struct dp_debug_private *debug,
+		struct dentry *dir)
+{
+	int rc = 0;
+
+	debugfs_create_bool("hdcp_wait_sink_sync", 0644, dir, &debug->dp_debug.hdcp_wait_sink_sync);
+
+	debugfs_create_bool("force_encryption", 0644, dir, &debug->dp_debug.force_encryption);
+
+	return rc;
+}
+
+static int dp_debug_init_sink_caps(struct dp_debug_private *debug,
+		struct dentry *dir)
+{
+	int rc = 0;
+	struct dentry *file;
+
+	file = debugfs_create_file("edid_modes", 0644, dir,
+					debug, &edid_modes_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs create edid_modes failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("edid_modes_mst", 0644, dir,
+					debug, &edid_modes_mst_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs create edid_modes_mst failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("edid", 0644, dir,
+					debug, &edid_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs edid failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("dpcd", 0644, dir,
+					debug, &dpcd_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs dpcd failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("crc", 0644, dir, debug, &crc_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs crc failed, rc=%d\n", DEBUG_NAME, rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int dp_debug_init_status(struct dp_debug_private *debug,
+		struct dentry *dir)
+{
+	int rc = 0;
+	struct dentry *file;
+
+	file = debugfs_create_file("dp_debug", 0444, dir,
+				debug, &dp_debug_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs create file failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("connected", 0444, dir,
+					debug, &connected_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs connected failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("hdr", 0400, dir, debug, &hdr_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs hdr failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("hdr_mst", 0400, dir, debug, &hdr_mst_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs hdr_mst failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("hdcp", 0644, dir, debug, &hdcp_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs hdcp failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int dp_debug_init_sim(struct dp_debug_private *debug, struct dentry *dir)
+{
+	int rc = 0;
+	struct dentry *file;
+
+	file = debugfs_create_file("hpd", 0644, dir, debug, &hpd_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs hpd failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("sim", 0644, dir, debug, &sim_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs sim failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("attention", 0644, dir,
+			debug, &attention_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs attention failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
+	}
+
+	debugfs_create_bool("skip_uevent", 0644, dir, &debug->dp_debug.skip_uevent);
+
+	debugfs_create_bool("force_multi_func", 0644, dir, &debug->hpd->force_multi_func);
+
+	return rc;
+}
+
+static int dp_debug_init_dsc_fec(struct dp_debug_private *debug,
+		struct dentry *dir)
+{
+	int rc = 0;
+
+	debugfs_create_bool("dsc_feature_enable", 0644, dir, &debug->parser->dsc_feature_enable);
+
+	debugfs_create_bool("fec_feature_enable", 0644, dir, &debug->parser->fec_feature_enable);
+
+	return rc;
+}
+
+static int dp_debug_init_tpg(struct dp_debug_private *debug, struct dentry *dir)
+{
+	int rc = 0;
+	struct dentry *file;
+
+	file = debugfs_create_file("tpg_ctrl", 0644, dir,
+			debug, &tpg_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs tpg failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int dp_debug_init_reg_dump(struct dp_debug_private *debug,
+		struct dentry *dir)
+{
+	int rc = 0;
+	struct dentry *file;
+
+	file = debugfs_create_file("exe_mode", 0644, dir,
+			debug, &exe_mode_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs register failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	file = debugfs_create_file("dump", 0644, dir,
+		debug, &dump_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		DP_ERR("[%s] debugfs dump failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int dp_debug_init_feature_toggle(struct dp_debug_private *debug,
+		struct dentry *dir)
+{
+	int rc = 0;
+
+	debugfs_create_bool("ssc_enable", 0644, dir, &debug->pll->ssc_en);
+
+	debugfs_create_bool("widebus_mode", 0644, dir, &debug->parser->has_widebus);
+
+	return rc;
+}
+
+static int dp_debug_init_configs(struct dp_debug_private *debug,
+		struct dentry *dir)
+{
+	int rc = 0;
+
+	debugfs_create_ulong("connect_notification_delay_ms", 0644, dir,
+		&debug->dp_debug.connect_notification_delay_ms);
+
+	debug->dp_debug.connect_notification_delay_ms =
+		DEFAULT_CONNECT_NOTIFICATION_DELAY_MS;
+
+	debugfs_create_u32("disconnect_delay_ms", 0644, dir, &debug->dp_debug.disconnect_delay_ms);
+
+	debug->dp_debug.disconnect_delay_ms = DEFAULT_DISCONNECT_DELAY_MS;
+
+	return rc;
+
+}
+
+static int dp_debug_init(struct dp_debug *dp_debug)
+{
+	int rc = 0;
+	struct dp_debug_private *debug = container_of(dp_debug,
+		struct dp_debug_private, dp_debug);
+	struct dentry *dir;
+
+	if (!IS_ENABLED(CONFIG_DEBUG_FS)) {
+		DP_WARN("Not creating debug root dir.");
+		debug->root = NULL;
+		return 0;
+	}
+
+	dir = debugfs_create_dir(DEBUG_NAME, NULL);
+	if (IS_ERR_OR_NULL(dir)) {
+		if (!dir)
+			rc = -EINVAL;
+		else
+			rc = PTR_ERR(dir);
+		DP_ERR("[%s] debugfs create dir failed, rc = %d\n",
+		       DEBUG_NAME, rc);
+		goto error;
+	}
+
+	debug->root = dir;
+
+	rc = dp_debug_init_status(debug, dir);
+	if (rc)
+		goto error_remove_dir;
+
+	rc = dp_debug_init_sink_caps(debug, dir);
+	if (rc)
+		goto error_remove_dir;
+
+	rc = dp_debug_init_mst(debug, dir);
+	if (rc)
+		goto error_remove_dir;
+
+	rc = dp_debug_init_link(debug, dir);
+	if (rc)
+		goto error_remove_dir;
+
+	rc = dp_debug_init_hdcp(debug, dir);
+	if (rc)
+		goto error_remove_dir;
+
+	rc = dp_debug_init_sim(debug, dir);
+	if (rc)
+		goto error_remove_dir;
+
+	rc = dp_debug_init_dsc_fec(debug, dir);
+	if (rc)
+		goto error_remove_dir;
+
+	rc = dp_debug_init_tpg(debug, dir);
+	if (rc)
+		goto error_remove_dir;
+
+	rc = dp_debug_init_reg_dump(debug, dir);
+	if (rc)
+		goto error_remove_dir;
+
+	rc = dp_debug_init_feature_toggle(debug, dir);
+	if (rc)
+		goto error_remove_dir;
+
+	rc = dp_debug_init_configs(debug, dir);
+	if (rc)
+		goto error_remove_dir;
+
+	return 0;
+
+error_remove_dir:
+	debugfs_remove_recursive(dir);
+error:
+	return rc;
+}
+
+static void dp_debug_abort(struct dp_debug *dp_debug)
+{
+	struct dp_debug_private *debug;
+
+	if (!dp_debug)
+		return;
+
+	debug = container_of(dp_debug, struct dp_debug_private, dp_debug);
+
+	mutex_lock(&debug->lock);
+	// disconnect has already been handled. so clear hotplug
+	debug->hotplug = false;
+	dp_debug_set_sim_mode(debug, false);
+	mutex_unlock(&debug->lock);
+}
+
+static void dp_debug_set_mst_con(struct dp_debug *dp_debug, int con_id)
+{
+	struct dp_debug_private *debug;
+
+	if (!dp_debug)
+		return;
+
+	debug = container_of(dp_debug, struct dp_debug_private, dp_debug);
+	mutex_lock(&debug->lock);
+	debug->mst_con_id = con_id;
+	mutex_unlock(&debug->lock);
+	DP_INFO("Selecting mst connector %d\n", con_id);
+}
+
+struct dp_debug *dp_debug_get(struct dp_debug_in *in)
+{
+	int rc = 0;
+	struct dp_debug_private *debug;
+	struct dp_debug *dp_debug;
+
+	if (!in->dev || !in->panel || !in->hpd || !in->link ||
+	    !in->catalog || !in->ctrl || !in->pll) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	debug = devm_kzalloc(in->dev, sizeof(*debug), GFP_KERNEL);
+	if (!debug) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	debug->hpd = in->hpd;
+	debug->link = in->link;
+	debug->panel = in->panel;
+	debug->aux = in->aux;
+	debug->dev = in->dev;
+	debug->connector = in->connector;
+	debug->catalog = in->catalog;
+	debug->parser = in->parser;
+	debug->ctrl = in->ctrl;
+	debug->pll = in->pll;
+	debug->display = in->display;
+
+	dp_debug = &debug->dp_debug;
+
+	mutex_init(&debug->lock);
+
+	rc = dp_debug_init(dp_debug);
+	if (rc) {
+		devm_kfree(in->dev, debug);
+		goto error;
+	}
+
+	debug->aux->access_lock = &debug->lock;
+	dp_debug->abort = dp_debug_abort;
+	dp_debug->set_mst_con = dp_debug_set_mst_con;
+
+	dp_debug->max_pclk_khz = debug->parser->max_pclk_khz;
+
+	return dp_debug;
+error:
+	return ERR_PTR(rc);
+}
+
+static int dp_debug_deinit(struct dp_debug *dp_debug)
+{
+	struct dp_debug_private *debug;
+
+	if (!dp_debug)
+		return -EINVAL;
+
+	debug = container_of(dp_debug, struct dp_debug_private, dp_debug);
+
+	debugfs_remove_recursive(debug->root);
+
+	if (debug->sim_bridge)
+		dp_sim_destroy_bridge(debug->sim_bridge);
+
+	return 0;
+}
+
+void dp_debug_put(struct dp_debug *dp_debug)
+{
+	struct dp_debug_private *debug;
+
+	if (!dp_debug)
+		return;
+
+	debug = container_of(dp_debug, struct dp_debug_private, dp_debug);
+
+	dp_debug_deinit(dp_debug);
+
+	mutex_destroy(&debug->lock);
+
+	devm_kfree(debug->dev, debug);
+}

+ 175 - 0
qcom/opensource/display-drivers/msm/dp/dp_debug.h

@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_DEBUG_H_
+#define _DP_DEBUG_H_
+
+#include "dp_panel.h"
+#include "dp_ctrl.h"
+#include "dp_link.h"
+#include "dp_aux.h"
+#include "dp_display.h"
+#include "dp_pll.h"
+#include <linux/ipc_logging.h>
+
+#define DP_IPC_LOG(fmt, ...) \
+	do {  \
+		void *ipc_logging_context = get_ipc_log_context(); \
+		ipc_log_string(ipc_logging_context, fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define DP_DEBUG(fmt, ...)                                                   \
+	do {                                                                 \
+		DP_IPC_LOG("[d][%-4d]"fmt, current->pid, ##__VA_ARGS__); \
+		DP_DEBUG_V(fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define DP_INFO(fmt, ...)                                                   \
+	do {                                                                 \
+		DP_IPC_LOG("[i][%-4d]"fmt, current->pid, ##__VA_ARGS__); \
+		DP_INFO_V(fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define DP_WARN(fmt, ...)                                                   \
+	do {                                                                 \
+		DP_IPC_LOG("[w][%-4d]"fmt, current->pid, ##__VA_ARGS__); \
+		DP_WARN_V(fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define DP_ERR(fmt, ...)                                                   \
+	do {                                                                 \
+		DP_IPC_LOG("[e][%-4d]"fmt, current->pid, ##__VA_ARGS__); \
+		DP_ERR_V(fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define DP_DEBUG_V(fmt, ...) \
+	do { \
+		if (drm_debug_enabled(DRM_UT_KMS))                        \
+			DRM_DEBUG("[msm-dp-debug][%-4d]"fmt, current->pid,   \
+					##__VA_ARGS__);                      \
+		else                                                         \
+			pr_debug("[drm:%s][msm-dp-debug][%-4d]"fmt, __func__,\
+				       current->pid, ##__VA_ARGS__);         \
+	} while (0)
+
+#define DP_INFO_V(fmt, ...)                                                    \
+	do {                                                                 \
+		if (drm_debug_enabled(DRM_UT_KMS))                        \
+			DRM_INFO("[msm-dp-info][%-4d]"fmt, current->pid,    \
+					##__VA_ARGS__);                      \
+		else                                                         \
+			pr_info("[drm:%s][msm-dp-info][%-4d]"fmt, __func__, \
+				       current->pid, ##__VA_ARGS__);         \
+	} while (0)
+
+#define DP_WARN_V(fmt, ...)                                    \
+		pr_warn("[drm:%s][msm-dp-warn][%-4d]"fmt, __func__,  \
+				current->pid, ##__VA_ARGS__)
+
+#define DP_WARN_RATELIMITED_V(fmt, ...)                                    \
+		pr_warn_ratelimited("[drm:%s][msm-dp-warn][%-4d]"fmt, __func__,  \
+				current->pid, ##__VA_ARGS__)
+
+#define DP_ERR_V(fmt, ...)                                    \
+		pr_err("[drm:%s][msm-dp-err][%-4d]"fmt, __func__,   \
+				current->pid, ##__VA_ARGS__)
+
+#define DP_ERR_RATELIMITED_V(fmt, ...)                                    \
+		pr_err_ratelimited("[drm:%s][msm-dp-err][%-4d]"fmt, __func__, \
+				current->pid, ##__VA_ARGS__)
+
+#define DEFAULT_DISCONNECT_DELAY_MS 0
+#define MAX_DISCONNECT_DELAY_MS 10000
+#define DEFAULT_CONNECT_NOTIFICATION_DELAY_MS 150
+#define MAX_CONNECT_NOTIFICATION_DELAY_MS 5000
+
+/**
+ * struct dp_debug
+ * @sim_mode: specifies whether sim mode enabled
+ * @psm_enabled: specifies whether psm enabled
+ * @hdcp_disabled: specifies if hdcp is disabled
+ * @hdcp_wait_sink_sync: used to wait for sink synchronization before HDCP auth
+ * @tpg_pattern: selects tpg pattern on the controller
+ * @max_pclk_khz: max pclk supported
+ * @force_encryption: enable/disable forced encryption for HDCP 2.2
+ * @skip_uevent: skip hotplug uevent to the user space
+ * @hdcp_status: string holding hdcp status information
+ * @mst_sim_add_con: specifies whether new sim connector is to be added
+ * @mst_sim_remove_con: specifies whether sim connector is to be removed
+ * @mst_sim_remove_con_id: specifies id of sim connector to be removed
+ * @connect_notification_delay_ms: time (in ms) to wait for any attention
+ *              messages before sending the connect notification uevent
+ * @disconnect_delay_ms: time (in ms) to wait before turning off the mainlink
+ *              in response to HPD low of cable disconnect event
+ */
+struct dp_debug {
+	bool sim_mode;
+	bool psm_enabled;
+	bool hdcp_disabled;
+	bool hdcp_wait_sink_sync;
+	u32 tpg_pattern;
+	u32 max_pclk_khz;
+	bool force_encryption;
+	bool skip_uevent;
+	char hdcp_status[SZ_128];
+	bool mst_sim_add_con;
+	bool mst_sim_remove_con;
+	int mst_sim_remove_con_id;
+	unsigned long connect_notification_delay_ms;
+	u32 disconnect_delay_ms;
+
+	void (*abort)(struct dp_debug *dp_debug);
+	void (*set_mst_con)(struct dp_debug *dp_debug, int con_id);
+};
+
+/**
+ * struct dp_debug_in
+ * @dev: device instance of the caller
+ * @panel: instance of panel module
+ * @hpd: instance of hpd module
+ * @link: instance of link module
+ * @aux: instance of aux module
+ * @connector: double pointer to display connector
+ * @catalog: instance of catalog module
+ * @parser: instance of parser module
+ * @ctrl: instance of controller module
+ * @pll: instance of pll module
+ * @display: instance of display module
+ */
+struct dp_debug_in {
+	struct device *dev;
+	struct dp_panel *panel;
+	struct dp_hpd *hpd;
+	struct dp_link *link;
+	struct dp_aux *aux;
+	struct drm_connector **connector;
+	struct dp_catalog *catalog;
+	struct dp_parser *parser;
+	struct dp_ctrl *ctrl;
+	struct dp_pll *pll;
+	struct dp_display *display;
+};
+
+/**
+ * dp_debug_get() - configure and get the DisplayPlot debug module data
+ *
+ * @in: input structure containing data to initialize the debug module
+ * return: pointer to allocated debug module data
+ *
+ * This function sets up the debug module and provides a way
+ * for debugfs input to be communicated with existing modules
+ */
+struct dp_debug *dp_debug_get(struct dp_debug_in *in);
+
+/**
+ * dp_debug_put()
+ *
+ * Cleans up dp_debug instance
+ *
+ * @dp_debug: instance of dp_debug
+ */
+void dp_debug_put(struct dp_debug *dp_debug);
+#endif /* _DP_DEBUG_H_ */

+ 3993 - 0
qcom/opensource/display-drivers/msm/dp/dp_display.c

@@ -0,0 +1,3993 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021-2024, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/component.h>
+#include <linux/of_irq.h>
+#include <linux/delay.h>
+#include <linux/usb/phy.h>
+#include <linux/jiffies.h>
+#include <linux/pm_qos.h>
+#include <linux/ipc_logging.h>
+
+#include "sde_connector.h"
+
+#include "msm_drv.h"
+#include "dp_hpd.h"
+#include "dp_parser.h"
+#include "dp_power.h"
+#include "dp_catalog.h"
+#include "dp_aux.h"
+#include "dp_link.h"
+#include "dp_panel.h"
+#include "dp_ctrl.h"
+#include "dp_audio.h"
+#include "dp_display.h"
+#include "sde_hdcp.h"
+#include "dp_debug.h"
+#include "dp_pll.h"
+#include "sde_dbg.h"
+
+#define DRM_DP_IPC_NUM_PAGES 10
+#define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)
+
+#define dp_display_state_show(x) { \
+	DP_ERR("%s: state (0x%x): %s\n", x, dp->state, \
+		dp_display_state_name(dp->state)); \
+	SDE_EVT32_EXTERNAL(dp->state); }
+
+#define dp_display_state_warn(x) { \
+	DP_WARN("%s: state (0x%x): %s\n", x, dp->state, \
+		dp_display_state_name(dp->state)); \
+	SDE_EVT32_EXTERNAL(dp->state); }
+
+#define dp_display_state_log(x) { \
+	DP_DEBUG("%s: state (0x%x): %s\n", x, dp->state, \
+		dp_display_state_name(dp->state)); \
+	SDE_EVT32_EXTERNAL(dp->state); }
+
+#define dp_display_state_is(x) (dp->state & (x))
+#define dp_display_state_add(x) { \
+	(dp->state |= (x)); \
+	dp_display_state_log("add "#x); }
+#define dp_display_state_remove(x) { \
+	(dp->state &= ~(x)); \
+	dp_display_state_log("remove "#x); }
+
+#define MAX_TMDS_CLOCK_HDMI_1_4 340000
+
+enum dp_display_states {
+	DP_STATE_DISCONNECTED           = 0,
+	DP_STATE_CONFIGURED             = BIT(0),
+	DP_STATE_INITIALIZED            = BIT(1),
+	DP_STATE_READY                  = BIT(2),
+	DP_STATE_CONNECTED              = BIT(3),
+	DP_STATE_CONNECT_NOTIFIED       = BIT(4),
+	DP_STATE_DISCONNECT_NOTIFIED    = BIT(5),
+	DP_STATE_ENABLED                = BIT(6),
+	DP_STATE_SUSPENDED              = BIT(7),
+	DP_STATE_ABORTED                = BIT(8),
+	DP_STATE_HDCP_ABORTED           = BIT(9),
+	DP_STATE_SRC_PWRDN              = BIT(10),
+	DP_STATE_TUI_ACTIVE             = BIT(11),
+};
+
+static char *dp_display_state_name(enum dp_display_states state)
+{
+	static char buf[SZ_1K];
+	u32 len = 0;
+
+	memset(buf, 0, SZ_1K);
+
+	if (state & DP_STATE_CONFIGURED)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"CONFIGURED");
+
+	if (state & DP_STATE_INITIALIZED)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"INITIALIZED");
+
+	if (state & DP_STATE_READY)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"READY");
+
+	if (state & DP_STATE_CONNECTED)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"CONNECTED");
+
+	if (state & DP_STATE_CONNECT_NOTIFIED)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"CONNECT_NOTIFIED");
+
+	if (state & DP_STATE_DISCONNECT_NOTIFIED)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"DISCONNECT_NOTIFIED");
+
+	if (state & DP_STATE_ENABLED)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"ENABLED");
+
+	if (state & DP_STATE_SUSPENDED)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"SUSPENDED");
+
+	if (state & DP_STATE_ABORTED)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"ABORTED");
+
+	if (state & DP_STATE_HDCP_ABORTED)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"HDCP_ABORTED");
+
+	if (state & DP_STATE_SRC_PWRDN)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"SRC_PWRDN");
+
+	if (state & DP_STATE_TUI_ACTIVE)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"TUI_ACTIVE");
+
+	if (!strlen(buf))
+		return "DISCONNECTED";
+
+	return buf;
+}
+
+static struct dp_display *g_dp_display;
+#define HPD_STRING_SIZE 30
+
+struct dp_hdcp_dev {
+	void *fd;
+	struct sde_hdcp_ops *ops;
+	enum sde_hdcp_version ver;
+};
+
+struct dp_hdcp {
+	void *data;
+	struct sde_hdcp_ops *ops;
+
+	u32 source_cap;
+
+	struct dp_hdcp_dev dev[HDCP_VERSION_MAX];
+};
+
+struct dp_mst {
+	bool mst_active;
+
+	bool drm_registered;
+	struct dp_mst_drm_cbs cbs;
+};
+
+struct dp_display_private {
+	char *name;
+	int irq;
+
+	enum drm_connector_status cached_connector_status;
+	enum dp_display_states state;
+	enum dp_aux_switch_type switch_type;
+
+	struct platform_device *pdev;
+	struct device_node *aux_switch_node;
+	bool aux_switch_ready;
+	struct dp_aux_bridge *aux_bridge;
+	struct dentry *root;
+	struct completion notification_comp;
+	struct completion attention_comp;
+
+	struct dp_hpd     *hpd;
+	struct dp_parser  *parser;
+	struct dp_power   *power;
+	struct dp_catalog *catalog;
+	struct dp_aux     *aux;
+	struct dp_link    *link;
+	struct dp_panel   *panel;
+	struct dp_ctrl    *ctrl;
+	struct dp_debug   *debug;
+	struct dp_pll     *pll;
+
+	struct dp_panel *active_panels[DP_STREAM_MAX];
+	struct dp_hdcp hdcp;
+
+	struct dp_hpd_cb hpd_cb;
+	struct dp_display_mode mode;
+	struct dp_display dp_display;
+	struct msm_drm_private *priv;
+
+	struct workqueue_struct *wq;
+	struct delayed_work hdcp_cb_work;
+	struct work_struct connect_work;
+	struct work_struct attention_work;
+	struct work_struct disconnect_work;
+	struct mutex session_lock;
+	struct mutex accounting_lock;
+	bool hdcp_delayed_off;
+	bool no_aux_switch;
+
+	u32 active_stream_cnt;
+	struct dp_mst mst;
+
+	u32 tot_dsc_blks_in_use;
+	u32 tot_lm_blks_in_use;
+
+	bool process_hpd_connect;
+	struct dev_pm_qos_request pm_qos_req[NR_CPUS];
+	bool pm_qos_requested;
+
+	struct notifier_block usb_nb;
+};
+
+static const struct of_device_id dp_dt_match[] = {
+	{.compatible = "qcom,dp-display"},
+	{}
+};
+
+static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp)
+{
+	return dp->link->hdcp_status.hdcp_version && dp->hdcp.ops;
+}
+
+static irqreturn_t dp_display_irq(int irq, void *dev_id)
+{
+	struct dp_display_private *dp = dev_id;
+
+	if (!dp) {
+		DP_ERR("invalid data\n");
+		return IRQ_NONE;
+	}
+
+	/* DP HPD isr */
+	if (dp->hpd->type ==  DP_HPD_LPHW)
+		dp->hpd->isr(dp->hpd);
+
+	/* DP controller isr */
+	dp->ctrl->isr(dp->ctrl);
+
+	/* DP aux isr */
+	dp->aux->isr(dp->aux);
+
+	/* HDCP isr */
+	if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->isr) {
+		if (dp->hdcp.ops->isr(dp->hdcp.data))
+			DP_ERR("dp_hdcp_isr failed\n");
+	}
+
+	return IRQ_HANDLED;
+}
+static bool dp_display_is_ds_bridge(struct dp_panel *panel)
+{
+	return (panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+		DP_DWN_STRM_PORT_PRESENT);
+}
+
+static bool dp_display_is_sink_count_zero(struct dp_display_private *dp)
+{
+	return dp_display_is_ds_bridge(dp->panel) &&
+		(dp->link->sink_count.count == 0);
+}
+
+static bool dp_display_is_ready(struct dp_display_private *dp)
+{
+	return dp->hpd->hpd_high && dp_display_state_is(DP_STATE_CONNECTED) &&
+		!dp_display_is_sink_count_zero(dp) &&
+		dp->hpd->alt_mode_cfg_done;
+}
+
+static void dp_audio_enable(struct dp_display_private *dp, bool enable)
+{
+	struct dp_panel *dp_panel;
+	int idx;
+
+	for (idx = DP_STREAM_0; idx < DP_STREAM_MAX; idx++) {
+		if (!dp->active_panels[idx])
+			continue;
+		dp_panel = dp->active_panels[idx];
+
+		if (dp_panel->audio_supported) {
+			if (enable) {
+				dp_panel->audio->bw_code =
+					dp->link->link_params.bw_code;
+				dp_panel->audio->lane_count =
+					dp->link->link_params.lane_count;
+				dp_panel->audio->on(dp_panel->audio);
+			} else {
+				dp_panel->audio->off(dp_panel->audio, false);
+			}
+		}
+	}
+}
+
+static void dp_display_qos_request(struct dp_display_private *dp, bool add_vote)
+{
+	struct device *cpu_dev;
+	int cpu = 0;
+	struct cpumask *cpu_mask;
+	u32 latency = dp->parser->qos_cpu_latency;
+	unsigned long mask = dp->parser->qos_cpu_mask;
+
+	if (!dp->parser->qos_cpu_mask || (dp->pm_qos_requested == add_vote))
+		return;
+
+	cpu_mask = to_cpumask(&mask);
+	for_each_cpu(cpu, cpu_mask) {
+		cpu_dev = get_cpu_device(cpu);
+		if (!cpu_dev) {
+			SDE_DEBUG("%s: failed to get cpu%d device\n", __func__, cpu);
+			continue;
+		}
+
+		if (add_vote)
+			dev_pm_qos_add_request(cpu_dev, &dp->pm_qos_req[cpu],
+				DEV_PM_QOS_RESUME_LATENCY, latency);
+		else
+			dev_pm_qos_remove_request(&dp->pm_qos_req[cpu]);
+	}
+
+	SDE_EVT32_EXTERNAL(add_vote, mask, latency);
+	dp->pm_qos_requested = add_vote;
+}
+
+static void dp_display_update_hdcp_status(struct dp_display_private *dp,
+					bool reset)
+{
+	if (reset) {
+		dp->link->hdcp_status.hdcp_state = HDCP_STATE_INACTIVE;
+		dp->link->hdcp_status.hdcp_version = HDCP_VERSION_NONE;
+	}
+
+	memset(dp->debug->hdcp_status, 0, sizeof(dp->debug->hdcp_status));
+
+	snprintf(dp->debug->hdcp_status, sizeof(dp->debug->hdcp_status),
+		"%s: %s\ncaps: %d\n",
+		sde_hdcp_version(dp->link->hdcp_status.hdcp_version),
+		sde_hdcp_state_name(dp->link->hdcp_status.hdcp_state),
+		dp->hdcp.source_cap);
+}
+
+static void dp_display_update_hdcp_info(struct dp_display_private *dp)
+{
+	void *fd = NULL;
+	struct dp_hdcp_dev *dev = NULL;
+	struct sde_hdcp_ops *ops = NULL;
+	int i = HDCP_VERSION_2P2;
+
+	dp_display_update_hdcp_status(dp, true);
+
+	dp->hdcp.data = NULL;
+	dp->hdcp.ops = NULL;
+
+	if (dp->debug->hdcp_disabled || dp->debug->sim_mode)
+		return;
+
+	while (i) {
+		dev = &dp->hdcp.dev[i];
+		ops = dev->ops;
+		fd = dev->fd;
+
+		i >>= 1;
+
+		if (!(dp->hdcp.source_cap & dev->ver))
+			continue;
+
+		if (ops->sink_support(fd)) {
+			dp->hdcp.data = fd;
+			dp->hdcp.ops = ops;
+			dp->link->hdcp_status.hdcp_version = dev->ver;
+			break;
+		}
+	}
+
+	DP_DEBUG("HDCP version supported: %s\n",
+		sde_hdcp_version(dp->link->hdcp_status.hdcp_version));
+}
+
+static void dp_display_check_source_hdcp_caps(struct dp_display_private *dp)
+{
+	int i;
+	struct dp_hdcp_dev *hdcp_dev = dp->hdcp.dev;
+
+	if (dp->debug->hdcp_disabled) {
+		DP_DEBUG("hdcp disabled\n");
+		return;
+	}
+
+	for (i = 0; i < HDCP_VERSION_MAX; i++) {
+		struct dp_hdcp_dev *dev = &hdcp_dev[i];
+		struct sde_hdcp_ops *ops = dev->ops;
+		void *fd = dev->fd;
+
+		if (!fd || !ops)
+			continue;
+
+		if (ops->set_mode && ops->set_mode(fd, dp->mst.mst_active))
+			continue;
+
+		if (!(dp->hdcp.source_cap & dev->ver) &&
+				ops->feature_supported &&
+				ops->feature_supported(fd))
+			dp->hdcp.source_cap |= dev->ver;
+	}
+
+	dp_display_update_hdcp_status(dp, false);
+}
+
+static void dp_display_hdcp_register_streams(struct dp_display_private *dp)
+{
+	int rc;
+	size_t i;
+	struct sde_hdcp_ops *ops = dp->hdcp.ops;
+	void *data = dp->hdcp.data;
+
+	if (dp_display_is_ready(dp) && dp->mst.mst_active && ops &&
+			ops->register_streams){
+		struct stream_info streams[DP_STREAM_MAX];
+		int index = 0;
+
+		DP_DEBUG("Registering all active panel streams with HDCP\n");
+		for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
+			if (!dp->active_panels[i])
+				continue;
+			streams[index].stream_id = i;
+			streams[index].virtual_channel =
+				dp->active_panels[i]->vcpi;
+			index++;
+		}
+
+		if (index > 0) {
+			rc = ops->register_streams(data, index, streams);
+			if (rc)
+				DP_ERR("failed to register streams. rc = %d\n",
+					rc);
+		}
+	}
+}
+
+static void dp_display_hdcp_deregister_stream(struct dp_display_private *dp,
+		enum dp_stream_id stream_id)
+{
+	if (dp->hdcp.ops->deregister_streams && dp->active_panels[stream_id]) {
+		struct stream_info stream = {stream_id,
+				dp->active_panels[stream_id]->vcpi};
+
+		DP_DEBUG("Deregistering stream within HDCP library\n");
+		dp->hdcp.ops->deregister_streams(dp->hdcp.data, 1, &stream);
+	}
+}
+
+static void dp_display_hdcp_process_delayed_off(struct dp_display_private *dp)
+{
+	if (dp->hdcp_delayed_off) {
+		if (dp->hdcp.ops && dp->hdcp.ops->off)
+			dp->hdcp.ops->off(dp->hdcp.data);
+		dp_display_update_hdcp_status(dp, true);
+		dp->hdcp_delayed_off = false;
+	}
+}
+
+static int dp_display_hdcp_process_sink_sync(struct dp_display_private *dp)
+{
+	u8 sink_status = 0;
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
+
+	if (dp->debug->hdcp_wait_sink_sync) {
+		drm_dp_dpcd_readb(dp->aux->drm_aux, DP_SINK_STATUS,
+				&sink_status);
+		sink_status &= (DP_RECEIVE_PORT_0_STATUS |
+				DP_RECEIVE_PORT_1_STATUS);
+		if (sink_status < 1) {
+			DP_DEBUG("Sink not synchronized. Queuing again then exiting\n");
+			queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ);
+			return -EAGAIN;
+		}
+		/*
+		 * Some sinks need more time to stabilize after synchronization
+		 * and before it can handle an HDCP authentication request.
+		 * Adding the delay for better interoperability.
+		 */
+		msleep(6000);
+	}
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT);
+
+	return 0;
+}
+
+static int dp_display_hdcp_start(struct dp_display_private *dp)
+{
+	if (dp->link->hdcp_status.hdcp_state != HDCP_STATE_INACTIVE)
+		return -EINVAL;
+
+	dp_display_check_source_hdcp_caps(dp);
+	dp_display_update_hdcp_info(dp);
+
+	if (dp_display_is_hdcp_enabled(dp)) {
+		if (dp->hdcp.ops && dp->hdcp.ops->on &&
+				dp->hdcp.ops->on(dp->hdcp.data)) {
+			dp_display_update_hdcp_status(dp, true);
+			return 0;
+		}
+	} else {
+		dp_display_update_hdcp_status(dp, true);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static void dp_display_hdcp_print_auth_state(struct dp_display_private *dp)
+{
+	u32 hdcp_auth_state;
+	int rc;
+
+	rc = dp->catalog->ctrl.read_hdcp_status(&dp->catalog->ctrl);
+	if (rc >= 0) {
+		hdcp_auth_state = (rc >> 20) & 0x3;
+		DP_DEBUG("hdcp auth state %d\n", hdcp_auth_state);
+	}
+}
+
+static void dp_display_hdcp_process_state(struct dp_display_private *dp)
+{
+	struct dp_link_hdcp_status *status;
+	struct sde_hdcp_ops *ops;
+	void *data;
+	int rc = 0;
+
+	status = &dp->link->hdcp_status;
+
+	ops = dp->hdcp.ops;
+	data = dp->hdcp.data;
+
+	if (status->hdcp_state != HDCP_STATE_AUTHENTICATED &&
+		dp->debug->force_encryption && ops && ops->force_encryption)
+		ops->force_encryption(data, dp->debug->force_encryption);
+
+	if (status->hdcp_state == HDCP_STATE_AUTHENTICATED)
+		dp_display_qos_request(dp, false);
+	else
+		dp_display_qos_request(dp, true);
+
+	switch (status->hdcp_state) {
+	case HDCP_STATE_INACTIVE:
+		dp_display_hdcp_register_streams(dp);
+		if (dp->hdcp.ops && dp->hdcp.ops->authenticate)
+			rc = dp->hdcp.ops->authenticate(data);
+		if (!rc)
+			status->hdcp_state = HDCP_STATE_AUTHENTICATING;
+		break;
+	case HDCP_STATE_AUTH_FAIL:
+		if (dp_display_is_ready(dp) &&
+		    dp_display_state_is(DP_STATE_ENABLED)) {
+			if (ops && ops->on && ops->on(data)) {
+				dp_display_update_hdcp_status(dp, true);
+				return;
+			}
+			dp_display_hdcp_register_streams(dp);
+			if (ops && ops->reauthenticate) {
+				rc = ops->reauthenticate(data);
+				if (rc)
+					DP_ERR("failed rc=%d\n", rc);
+			}
+			status->hdcp_state = HDCP_STATE_AUTHENTICATING;
+		} else {
+			DP_DEBUG("not reauthenticating, cable disconnected\n");
+		}
+		break;
+	default:
+		dp_display_hdcp_register_streams(dp);
+		break;
+	}
+}
+
+static void dp_display_abort_hdcp(struct dp_display_private *dp,
+		bool abort)
+{
+	u8 i = HDCP_VERSION_2P2;
+	struct dp_hdcp_dev *dev = NULL;
+
+	while (i) {
+		dev = &dp->hdcp.dev[i];
+		i >>= 1;
+		if (!(dp->hdcp.source_cap & dev->ver))
+			continue;
+
+		dev->ops->abort(dev->fd, abort);
+	}
+}
+
+static void dp_display_hdcp_cb_work(struct work_struct *work)
+{
+	struct dp_display_private *dp;
+	struct delayed_work *dw = to_delayed_work(work);
+	struct dp_link_hdcp_status *status;
+	int rc = 0;
+
+	dp = container_of(dw, struct dp_display_private, hdcp_cb_work);
+
+	if (!dp_display_state_is(DP_STATE_ENABLED | DP_STATE_CONNECTED) ||
+	     dp_display_state_is(DP_STATE_ABORTED | DP_STATE_HDCP_ABORTED))
+		return;
+
+	if (dp_display_state_is(DP_STATE_SUSPENDED)) {
+		DP_DEBUG("System suspending. Delay HDCP operations\n");
+		queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ);
+		return;
+	}
+
+	dp_display_hdcp_process_delayed_off(dp);
+
+	rc = dp_display_hdcp_process_sink_sync(dp);
+	if (rc)
+		return;
+
+	rc = dp_display_hdcp_start(dp);
+	if (!rc)
+		return;
+
+	dp_display_hdcp_print_auth_state(dp);
+
+	status = &dp->link->hdcp_status;
+	DP_DEBUG("%s: %s\n", sde_hdcp_version(status->hdcp_version),
+		sde_hdcp_state_name(status->hdcp_state));
+
+	dp_display_update_hdcp_status(dp, false);
+
+	dp_display_hdcp_process_state(dp);
+}
+
+static void dp_display_notify_hdcp_status_cb(void *ptr,
+		enum sde_hdcp_state state)
+{
+	struct dp_display_private *dp = ptr;
+
+	if (!dp) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY,
+					dp->link->hdcp_status.hdcp_state);
+
+	dp->link->hdcp_status.hdcp_state = state;
+
+	queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ/4);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT,
+					dp->link->hdcp_status.hdcp_state);
+}
+
+static void dp_display_deinitialize_hdcp(struct dp_display_private *dp)
+{
+	if (!dp) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	sde_hdcp_1x_deinit(dp->hdcp.dev[HDCP_VERSION_1X].fd);
+	sde_dp_hdcp2p2_deinit(dp->hdcp.dev[HDCP_VERSION_2P2].fd);
+}
+
+static int dp_display_initialize_hdcp(struct dp_display_private *dp)
+{
+	struct sde_hdcp_init_data hdcp_init_data;
+	struct dp_parser *parser;
+	void *fd;
+	int rc = 0;
+
+	if (!dp) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	parser = dp->parser;
+
+	hdcp_init_data.client_id     = HDCP_CLIENT_DP;
+	hdcp_init_data.drm_aux       = dp->aux->drm_aux;
+	hdcp_init_data.cb_data       = (void *)dp;
+	hdcp_init_data.workq         = dp->wq;
+	hdcp_init_data.sec_access    = true;
+	hdcp_init_data.notify_status = dp_display_notify_hdcp_status_cb;
+	hdcp_init_data.dp_ahb        = &parser->get_io(parser, "dp_ahb")->io;
+	hdcp_init_data.dp_aux        = &parser->get_io(parser, "dp_aux")->io;
+	hdcp_init_data.dp_link       = &parser->get_io(parser, "dp_link")->io;
+	hdcp_init_data.dp_p0         = &parser->get_io(parser, "dp_p0")->io;
+	hdcp_init_data.hdcp_io       = &parser->get_io(parser,
+						"hdcp_physical")->io;
+	hdcp_init_data.revision      = &dp->panel->link_info.revision;
+	hdcp_init_data.msm_hdcp_dev  = dp->parser->msm_hdcp_dev;
+
+	fd = sde_hdcp_1x_init(&hdcp_init_data);
+	if (IS_ERR_OR_NULL(fd)) {
+		DP_DEBUG("Error initializing HDCP 1.x\n");
+		return -EINVAL;
+	}
+
+	dp->hdcp.dev[HDCP_VERSION_1X].fd = fd;
+	dp->hdcp.dev[HDCP_VERSION_1X].ops = sde_hdcp_1x_get(fd);
+	dp->hdcp.dev[HDCP_VERSION_1X].ver = HDCP_VERSION_1X;
+	DP_INFO("HDCP 1.3 initialized\n");
+
+	fd = sde_dp_hdcp2p2_init(&hdcp_init_data);
+	if (IS_ERR_OR_NULL(fd)) {
+		DP_DEBUG("Error initializing HDCP 2.x\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	dp->hdcp.dev[HDCP_VERSION_2P2].fd = fd;
+	dp->hdcp.dev[HDCP_VERSION_2P2].ops = sde_dp_hdcp2p2_get(fd);
+	dp->hdcp.dev[HDCP_VERSION_2P2].ver = HDCP_VERSION_2P2;
+	DP_INFO("HDCP 2.2 initialized\n");
+
+	return 0;
+error:
+	sde_hdcp_1x_deinit(dp->hdcp.dev[HDCP_VERSION_1X].fd);
+
+	return rc;
+}
+
+static void dp_display_pause_audio(struct dp_display_private *dp, bool pause)
+{
+	struct dp_panel *dp_panel;
+	int idx;
+
+	for (idx = DP_STREAM_0; idx < DP_STREAM_MAX; idx++) {
+		if (!dp->active_panels[idx])
+			continue;
+		dp_panel = dp->active_panels[idx];
+
+		if (dp_panel->audio_supported)
+			dp_panel->audio->tui_active = pause;
+	}
+}
+
+static int dp_display_pre_hw_release(void *data)
+{
+	struct dp_display_private *dp;
+	struct dp_display *dp_display = data;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
+
+	if (!dp_display)
+		return -EINVAL;
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	mutex_lock(&dp->session_lock);
+
+	dp_display_state_add(DP_STATE_TUI_ACTIVE);
+	cancel_work_sync(&dp->connect_work);
+	cancel_work_sync(&dp->attention_work);
+	cancel_work_sync(&dp->disconnect_work);
+	flush_workqueue(dp->wq);
+
+	dp_display_pause_audio(dp, true);
+	disable_irq(dp->irq);
+
+	mutex_unlock(&dp->session_lock);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT);
+	return 0;
+}
+
+static int dp_display_post_hw_acquire(void *data)
+{
+	struct dp_display_private *dp;
+	struct dp_display *dp_display = data;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
+
+	if (!dp_display)
+		return -EINVAL;
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	mutex_lock(&dp->session_lock);
+
+	dp_display_state_remove(DP_STATE_TUI_ACTIVE);
+	dp_display_pause_audio(dp, false);
+	enable_irq(dp->irq);
+
+	mutex_unlock(&dp->session_lock);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT);
+	return 0;
+}
+
+
+static int dp_display_bind(struct device *dev, struct device *master,
+		void *data)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+	struct drm_device *drm;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct msm_vm_ops vm_event_ops = {
+		.vm_pre_hw_release = dp_display_pre_hw_release,
+		.vm_post_hw_acquire = dp_display_post_hw_acquire,
+	};
+
+	if (!dev || !pdev || !master) {
+		DP_ERR("invalid param(s), dev %pK, pdev %pK, master %pK\n",
+				dev, pdev, master);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	drm = dev_get_drvdata(master);
+	dp = platform_get_drvdata(pdev);
+	if (!drm || !dp) {
+		DP_ERR("invalid param(s), drm %pK, dp %pK\n",
+				drm, dp);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dp->dp_display.drm_dev = drm;
+	dp->priv = drm->dev_private;
+	msm_register_vm_event(master, dev, &vm_event_ops,
+			(void *)&dp->dp_display);
+end:
+	return rc;
+}
+
+static void dp_display_unbind(struct device *dev, struct device *master,
+		void *data)
+{
+	struct dp_display_private *dp;
+	struct platform_device *pdev = to_platform_device(dev);
+
+	if (!dev || !pdev) {
+		DP_ERR("invalid param(s)\n");
+		return;
+	}
+
+	dp = platform_get_drvdata(pdev);
+	if (!dp) {
+		DP_ERR("Invalid params\n");
+		return;
+	}
+
+	if (dp->power)
+		(void)dp->power->power_client_deinit(dp->power);
+	if (dp->aux)
+		(void)dp->aux->drm_aux_deregister(dp->aux);
+	dp_display_deinitialize_hdcp(dp);
+}
+
+static const struct component_ops dp_display_comp_ops = {
+	.bind = dp_display_bind,
+	.unbind = dp_display_unbind,
+};
+
+static bool dp_display_send_hpd_event(struct dp_display_private *dp)
+{
+	struct drm_device *dev = NULL;
+	struct drm_connector *connector;
+	char name[HPD_STRING_SIZE], status[HPD_STRING_SIZE],
+		bpp[HPD_STRING_SIZE], pattern[HPD_STRING_SIZE];
+	char *envp[5];
+	struct dp_display *display;
+	int rc = 0;
+
+	connector = dp->dp_display.base_connector;
+	display = &dp->dp_display;
+
+	if (!connector) {
+		DP_ERR("connector not set\n");
+		return false;
+	}
+
+	connector->status = display->is_sst_connected ? connector_status_connected :
+			connector_status_disconnected;
+
+	if (dp->cached_connector_status == connector->status) {
+		DP_DEBUG("connector status (%d) unchanged, skipping uevent\n",
+				dp->cached_connector_status);
+		return false;
+	}
+
+	dp->cached_connector_status = connector->status;
+
+	dev = connector->dev;
+
+	if (dp->debug->skip_uevent) {
+		DP_INFO("skipping uevent\n");
+		return false;
+	}
+
+	snprintf(name, HPD_STRING_SIZE, "name=%s", connector->name);
+	snprintf(status, HPD_STRING_SIZE, "status=%s",
+		drm_get_connector_status_name(connector->status));
+	snprintf(bpp, HPD_STRING_SIZE, "bpp=%d",
+		dp_link_bit_depth_to_bpp(
+		dp->link->test_video.test_bit_depth));
+	snprintf(pattern, HPD_STRING_SIZE, "pattern=%d",
+		dp->link->test_video.test_video_pattern);
+
+	DP_INFO("[%s]:[%s] [%s] [%s]\n", name, status, bpp, pattern);
+	envp[0] = name;
+	envp[1] = status;
+	envp[2] = bpp;
+	envp[3] = pattern;
+	envp[4] = NULL;
+
+	rc = kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
+	DP_INFO("uevent %s: %d\n", rc ? "failure" : "success", rc);
+
+	return true;
+}
+
+static int dp_display_send_hpd_notification(struct dp_display_private *dp, bool skip_wait)
+{
+	int ret = 0;
+	bool hpd = !!dp_display_state_is(DP_STATE_CONNECTED);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state, hpd);
+
+	/*
+	 * Send the notification only if there is any change. This check is
+	 * necessary since it is possible that the connect_work may or may not
+	 * skip sending the notification in order to respond to a pending
+	 * attention message. Attention work thread will always attempt to
+	 * send the notification after successfully handling the attention
+	 * message. This check here will avoid any unintended duplicate
+	 * notifications.
+	 */
+	if (dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) && hpd) {
+		DP_DEBUG("connection notified already, skip notification\n");
+		goto skip_wait;
+	} else if (dp_display_state_is(DP_STATE_DISCONNECT_NOTIFIED) && !hpd) {
+		DP_DEBUG("disonnect notified already, skip notification\n");
+		goto skip_wait;
+	}
+
+	dp->aux->state |= DP_STATE_NOTIFICATION_SENT;
+
+	reinit_completion(&dp->notification_comp);
+
+	if (!dp->mst.mst_active) {
+		dp->dp_display.is_sst_connected = hpd;
+
+		if (!dp_display_send_hpd_event(dp))
+			goto skip_wait;
+	} else {
+		dp->dp_display.is_sst_connected = false;
+
+		if (!dp->mst.cbs.hpd)
+			goto skip_wait;
+
+		dp->mst.cbs.hpd(&dp->dp_display, hpd);
+	}
+
+	if (hpd) {
+		dp_display_state_add(DP_STATE_CONNECT_NOTIFIED);
+		dp_display_state_remove(DP_STATE_DISCONNECT_NOTIFIED);
+	} else {
+		dp_display_state_add(DP_STATE_DISCONNECT_NOTIFIED);
+		dp_display_state_remove(DP_STATE_CONNECT_NOTIFIED);
+	}
+
+	/*
+	 * Skip the wait if TUI is active considering that the user mode will
+	 * not act on the notification until after the TUI session is over.
+	 */
+	if (dp_display_state_is(DP_STATE_TUI_ACTIVE)) {
+		dp_display_state_log("[TUI is active, skipping wait]");
+		goto skip_wait;
+	}
+
+	if (skip_wait || (hpd && dp->mst.mst_active))
+		goto skip_wait;
+
+	if (!dp->mst.mst_active &&
+			(!!dp_display_state_is(DP_STATE_ENABLED) == hpd))
+		goto skip_wait;
+
+	// wait 2 seconds
+	if (wait_for_completion_timeout(&dp->notification_comp, HZ * 2))
+		goto skip_wait;
+
+	//resend notification
+	if (dp->mst.mst_active)
+		dp->mst.cbs.hpd(&dp->dp_display, hpd);
+	else
+		dp_display_send_hpd_event(dp);
+
+	// wait another 3 seconds
+	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 3)) {
+		DP_WARN("%s timeout\n", hpd ? "connect" : "disconnect");
+		ret = -EINVAL;
+	}
+
+skip_wait:
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, hpd, ret);
+	return ret;
+}
+
+static void dp_display_update_mst_state(struct dp_display_private *dp,
+					bool state)
+{
+	dp->mst.mst_active = state;
+	dp->panel->mst_state = state;
+}
+
+static void dp_display_mst_init(struct dp_display_private *dp)
+{
+	bool is_mst_receiver;
+	const unsigned long clear_mstm_ctrl_timeout_us = 100000;
+	u8 old_mstm_ctrl;
+	int ret;
+
+	if (!dp->parser->has_mst || !dp->mst.drm_registered) {
+		DP_MST_DEBUG("mst not enabled. has_mst:%d, registered:%d\n",
+				dp->parser->has_mst, dp->mst.drm_registered);
+		return;
+	}
+
+	is_mst_receiver = dp->panel->read_mst_cap(dp->panel);
+
+	if (!is_mst_receiver) {
+		DP_MST_DEBUG("sink doesn't support mst\n");
+		return;
+	}
+
+	/* clear sink mst state */
+	drm_dp_dpcd_readb(dp->aux->drm_aux, DP_MSTM_CTRL, &old_mstm_ctrl);
+	drm_dp_dpcd_writeb(dp->aux->drm_aux, DP_MSTM_CTRL, 0);
+
+	/* add extra delay if MST state is not cleared */
+	if (old_mstm_ctrl) {
+		DP_MST_DEBUG("MSTM_CTRL is not cleared, wait %luus\n",
+				clear_mstm_ctrl_timeout_us);
+		usleep_range(clear_mstm_ctrl_timeout_us,
+			clear_mstm_ctrl_timeout_us + 1000);
+	}
+
+	ret = drm_dp_dpcd_writeb(dp->aux->drm_aux, DP_MSTM_CTRL,
+				DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
+	if (ret < 0) {
+		DP_ERR("sink mst enablement failed\n");
+		return;
+	}
+
+	dp_display_update_mst_state(dp, true);
+}
+
+static void dp_display_set_mst_mgr_state(struct dp_display_private *dp,
+					bool state)
+{
+	if (!dp->mst.mst_active)
+		return;
+
+	if (dp->mst.cbs.set_mgr_state)
+		dp->mst.cbs.set_mgr_state(&dp->dp_display, state);
+
+	DP_MST_DEBUG("mst_mgr_state: %d\n", state);
+}
+
+static int dp_display_host_init(struct dp_display_private *dp)
+{
+	bool flip = false;
+	bool reset;
+	int rc = 0;
+
+	if (dp_display_state_is(DP_STATE_INITIALIZED)) {
+		dp_display_state_log("[already initialized]");
+		return rc;
+	}
+
+	if (dp->hpd->orientation == ORIENTATION_CC2)
+		flip = true;
+
+	reset = dp->debug->sim_mode ? false : !dp->hpd->multi_func;
+
+	rc = dp->power->init(dp->power, flip);
+	if (rc) {
+		DP_WARN("Power init failed.\n");
+		SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_CASE1, dp->state);
+		return rc;
+	}
+
+	dp->hpd->host_init(dp->hpd, &dp->catalog->hpd);
+	rc = dp->ctrl->init(dp->ctrl, flip, reset);
+	if (rc) {
+		DP_WARN("Ctrl init Failed.\n");
+		SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_CASE2, dp->state);
+		goto error_ctrl;
+	}
+
+	enable_irq(dp->irq);
+	dp_display_abort_hdcp(dp, false);
+
+	dp_display_state_add(DP_STATE_INITIALIZED);
+
+	/* log this as it results from user action of cable connection */
+	DP_INFO("[OK]\n");
+	return rc;
+
+error_ctrl:
+	dp->hpd->host_deinit(dp->hpd, &dp->catalog->hpd);
+	dp->power->deinit(dp->power);
+	return rc;
+}
+
+static int dp_display_host_ready(struct dp_display_private *dp)
+{
+	int rc = 0;
+
+	if (!dp_display_state_is(DP_STATE_INITIALIZED)) {
+		rc = dp_display_host_init(dp);
+		if (rc) {
+			dp_display_state_show("[not initialized]");
+			return rc;
+		}
+	}
+
+	if (dp_display_state_is(DP_STATE_READY)) {
+		dp_display_state_log("[already ready]");
+		return rc;
+	}
+
+	/*
+	 * Reset the aborted state for AUX and CTRL modules. This will
+	 * allow these modules to execute normally in response to the
+	 * cable connection event.
+	 *
+	 * One corner case still exists. While the execution flow ensures
+	 * that cable disconnection flushes all pending work items on the DP
+	 * workqueue, and waits for the user module to clean up the DP
+	 * connection session, it is possible that the system delays can
+	 * lead to timeouts in the connect path. As a result, the actual
+	 * connection callback from user modules can come in late and can
+	 * race against a subsequent connection event here which would have
+	 * reset the aborted flags. There is no clear solution for this since
+	 * the connect/disconnect notifications do not currently have any
+	 * sessions IDs.
+	 */
+	dp->aux->abort(dp->aux, false);
+	dp->ctrl->abort(dp->ctrl, false);
+
+	dp->aux->init(dp->aux, dp->parser->aux_cfg);
+	dp->panel->init(dp->panel);
+
+	dp_display_state_add(DP_STATE_READY);
+	/* log this as it results from user action of cable connection */
+	DP_INFO("[OK]\n");
+	return rc;
+}
+
+static void dp_display_host_unready(struct dp_display_private *dp)
+{
+	if (!dp_display_state_is(DP_STATE_INITIALIZED)) {
+		dp_display_state_warn("[not initialized]");
+		return;
+	}
+
+	if (!dp_display_state_is(DP_STATE_READY)) {
+		dp_display_state_show("[not ready]");
+		return;
+	}
+
+	dp_display_state_remove(DP_STATE_READY);
+	dp->aux->deinit(dp->aux);
+	/* log this as it results from user action of cable disconnection */
+	DP_INFO("[OK]\n");
+}
+
+static void dp_display_host_deinit(struct dp_display_private *dp)
+{
+	if (dp->active_stream_cnt) {
+		SDE_EVT32_EXTERNAL(dp->state, dp->active_stream_cnt);
+		DP_DEBUG("active stream present\n");
+		return;
+	}
+
+	if (!dp_display_state_is(DP_STATE_INITIALIZED)) {
+		dp_display_state_show("[not initialized]");
+		return;
+	}
+
+	if (dp_display_state_is(DP_STATE_READY)) {
+		DP_DEBUG("dp deinit before unready\n");
+		dp_display_host_unready(dp);
+	}
+
+	dp_display_abort_hdcp(dp, true);
+	dp->ctrl->deinit(dp->ctrl);
+	dp->hpd->host_deinit(dp->hpd, &dp->catalog->hpd);
+	dp->power->deinit(dp->power);
+	disable_irq(dp->irq);
+	dp->aux->state = 0;
+
+	dp_display_state_remove(DP_STATE_INITIALIZED);
+
+	/* log this as it results from user action of cable dis-connection */
+	DP_INFO("[OK]\n");
+}
+
+static bool dp_display_hpd_irq_pending(struct dp_display_private *dp)
+{
+
+	unsigned long wait_timeout_ms = 0;
+	unsigned long t_out = 0;
+	unsigned long wait_time = 0;
+
+	do {
+		/*
+		 * If an IRQ HPD is pending, then do not send a connect notification.
+		 * Once this work returns, the IRQ HPD would be processed and any
+		 * required actions (such as link maintenance) would be done which
+		 * will subsequently send the HPD notification. To keep things simple,
+		 * do this only for SST use-cases. MST use cases require additional
+		 * care in order to handle the side-band communications as well.
+		 *
+		 * One of the main motivations for this is DP LL 1.4 CTS use case
+		 * where it is possible that we could get a test request right after
+		 * a connection, and the strict timing requriements of the test can
+		 * only be met if we do not wait for the e2e connection to be set up.
+		 */
+		if (!dp->mst.mst_active && (work_busy(&dp->attention_work) == WORK_BUSY_PENDING)) {
+			SDE_EVT32_EXTERNAL(dp->state, 99, jiffies_to_msecs(t_out));
+			DP_DEBUG("Attention pending, skip HPD notification\n");
+			return true;
+		}
+
+		/*
+		 * If no IRQ HPD, delay the HPD connect notification for
+		 * MAX_CONNECT_NOTIFICATION_DELAY_MS to see if sink generates any IRQ HPDs
+		 * after the HPD high. Wait for
+		 * MAX_CONNECT_NOTIFICATION_DELAY_MS to make sure any IRQ HPD from test
+		 * requests aren't missed.
+		 */
+		reinit_completion(&dp->attention_comp);
+		wait_timeout_ms = min_t(unsigned long, dp->debug->connect_notification_delay_ms,
+				(unsigned long) MAX_CONNECT_NOTIFICATION_DELAY_MS - wait_time);
+		t_out = wait_for_completion_timeout(&dp->attention_comp,
+				msecs_to_jiffies(wait_timeout_ms));
+		wait_time += (t_out == 0) ?  wait_timeout_ms : t_out;
+
+	} while ((wait_timeout_ms < wait_time) && (wait_time < MAX_CONNECT_NOTIFICATION_DELAY_MS));
+
+	DP_DEBUG("wait_timeout=%lu ms, time_waited=%lu ms\n", wait_timeout_ms, wait_time);
+
+	return false;
+
+}
+
+static int dp_display_process_hpd_high(struct dp_display_private *dp)
+{
+	int rc = -EINVAL;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	mutex_lock(&dp->session_lock);
+
+	if (dp_display_state_is(DP_STATE_CONNECTED)) {
+		DP_DEBUG("dp already connected, skipping hpd high\n");
+		mutex_unlock(&dp->session_lock);
+		return -EISCONN;
+	}
+
+	dp_display_state_add(DP_STATE_CONNECTED);
+
+	dp->dp_display.max_pclk_khz = min(dp->parser->max_pclk_khz,
+					dp->debug->max_pclk_khz);
+
+	if (!dp->debug->sim_mode && !dp->no_aux_switch && !dp->parser->gpio_aux_switch
+			&& dp->aux_switch_node && dp->aux->switch_configure) {
+		rc = dp->aux->switch_configure(dp->aux, true, dp->hpd->orientation);
+		if (rc)
+			goto err_state;
+	}
+
+	/*
+	 * If dp video session is not restored from a previous session teardown
+	 * by userspace, ensure the host_init is executed, in such a scenario,
+	 * so that all the required DP resources are enabled.
+	 *
+	 * Below is one of the sequences of events which describe the above
+	 * scenario:
+	 *  a. Source initiated power down resulting in host_deinit.
+	 *  b. Sink issues hpd low attention without physical cable disconnect.
+	 *  c. Source initiated power up sequence returns early because hpd is
+	 *     not high.
+	 *  d. Sink issues a hpd high attention event.
+	 */
+	if (dp_display_state_is(DP_STATE_SRC_PWRDN) &&
+			dp_display_state_is(DP_STATE_CONFIGURED)) {
+		rc = dp_display_host_init(dp);
+		if (rc) {
+			DP_WARN("Host init Failed");
+			if (!dp_display_state_is(DP_STATE_SUSPENDED)) {
+				/*
+				 * If not suspended no point of going forward if
+				 * resource is not enabled.
+				 */
+				dp_display_state_remove(DP_STATE_CONNECTED);
+			}
+			goto err_unlock;
+		}
+
+		/*
+		 * If device is suspended and host_init fails, there is
+		 * one more chance for host init to happen in prepare which
+		 * is why DP_STATE_SRC_PWRDN is removed only at success.
+		 */
+		dp_display_state_remove(DP_STATE_SRC_PWRDN);
+	}
+
+	rc = dp_display_host_ready(dp);
+	if (rc) {
+		dp_display_state_show("[ready failed]");
+		goto err_state;
+	}
+
+	dp->link->psm_config(dp->link, &dp->panel->link_info, false);
+	dp->debug->psm_enabled = false;
+
+	if (!dp->dp_display.base_connector)
+		goto err_unready;
+
+	rc = dp->panel->read_sink_caps(dp->panel,
+			dp->dp_display.base_connector, dp->hpd->multi_func);
+	/*
+	 * ETIMEDOUT --> cable may have been removed
+	 * ENOTCONN --> no downstream device connected
+	 */
+	if (rc == -ETIMEDOUT || rc == -ENOTCONN)
+		goto err_unready;
+
+	dp->link->process_request(dp->link);
+	dp->panel->handle_sink_request(dp->panel);
+
+	dp_display_mst_init(dp);
+
+	rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active,
+			dp->panel->fec_en, dp->panel->dsc_en, false);
+	if (rc)
+		goto err_mst;
+
+	dp->process_hpd_connect = false;
+
+	dp_display_set_mst_mgr_state(dp, true);
+
+	mutex_unlock(&dp->session_lock);
+
+	if (dp_display_hpd_irq_pending(dp))
+		goto end;
+
+	if (!rc && !dp_display_state_is(DP_STATE_ABORTED))
+		dp_display_send_hpd_notification(dp, false);
+
+	goto end;
+
+err_mst:
+	dp_display_update_mst_state(dp, false);
+err_unready:
+	dp_display_host_unready(dp);
+err_state:
+	dp_display_state_remove(DP_STATE_CONNECTED);
+err_unlock:
+	mutex_unlock(&dp->session_lock);
+end:
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, rc);
+	return rc;
+}
+
+static void dp_display_process_mst_hpd_low(struct dp_display_private *dp, bool skip_wait)
+{
+	int rc = 0;
+
+	if (dp->mst.mst_active) {
+		DP_MST_DEBUG("mst_hpd_low work\n");
+
+		/*
+		 * HPD unplug callflow:
+		 * 1. send hpd unplug on base connector so usermode can disable
+                 * all external displays.
+		 * 2. unset mst state in the topology mgr so the branch device
+		 *  can be cleaned up.
+		 */
+
+		if ((dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) ||
+				dp_display_state_is(DP_STATE_ENABLED)))
+			rc = dp_display_send_hpd_notification(dp, skip_wait);
+
+		dp_display_set_mst_mgr_state(dp, false);
+		dp_display_update_mst_state(dp, false);
+	}
+
+	DP_MST_DEBUG("mst_hpd_low. mst_active:%d\n", dp->mst.mst_active);
+}
+
+static int dp_display_process_hpd_low(struct dp_display_private *dp, bool skip_wait)
+{
+	int rc = 0;
+
+	dp_display_state_remove(DP_STATE_CONNECTED);
+	dp->process_hpd_connect = false;
+	dp_audio_enable(dp, false);
+
+	if (dp->mst.mst_active) {
+		dp_display_process_mst_hpd_low(dp, skip_wait);
+	} else {
+		if ((dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) ||
+				dp_display_state_is(DP_STATE_ENABLED)))
+			rc = dp_display_send_hpd_notification(dp, skip_wait);
+	}
+
+	mutex_lock(&dp->session_lock);
+	if (!dp->active_stream_cnt)
+		dp->ctrl->off(dp->ctrl);
+	mutex_unlock(&dp->session_lock);
+
+	dp->panel->video_test = false;
+
+	return rc;
+}
+
+static int dp_display_aux_switch_callback(struct notifier_block *self,
+		unsigned long event, void *data)
+{
+	return 0;
+}
+
+static int dp_display_init_aux_switch(struct dp_display_private *dp)
+{
+	int rc = 0;
+	struct notifier_block nb;
+	const u32 max_retries = 50;
+	u32 retry;
+
+	if (dp->aux_switch_ready)
+	       return rc;
+
+	if (!dp->aux->switch_register_notifier)
+		return rc;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
+
+	nb.notifier_call = dp_display_aux_switch_callback;
+	nb.priority = 0;
+
+	/*
+	 * Iteratively wait for reg notifier which confirms that fsa driver is probed.
+	 * Bootup DP with cable connected usecase can hit this scenario.
+	 */
+	for (retry = 0; retry < max_retries; retry++) {
+		rc = dp->aux->switch_register_notifier(&nb, dp->aux_switch_node);
+		if (rc == 0) {
+			DP_DEBUG("registered notifier successfully\n");
+			dp->aux_switch_ready = true;
+			break;
+		} else {
+			DP_DEBUG("failed to register notifier retry=%d rc=%d\n", retry, rc);
+			msleep(100);
+		}
+	}
+
+	if (retry == max_retries) {
+		DP_WARN("Failed to register fsa notifier\n");
+		dp->aux_switch_ready = false;
+		return rc;
+	}
+
+	if (dp->aux->switch_unregister_notifier)
+		dp->aux->switch_unregister_notifier(&nb, dp->aux_switch_node);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, rc);
+	return rc;
+}
+
+static int dp_display_usbpd_configure_cb(struct device *dev)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dev) {
+		DP_ERR("invalid dev\n");
+		return -EINVAL;
+	}
+
+	dp = dev_get_drvdata(dev);
+	if (!dp) {
+		DP_ERR("no driver data found\n");
+		return -ENODEV;
+	}
+
+	if (!dp->debug->sim_mode && !dp->no_aux_switch
+	    && !dp->parser->gpio_aux_switch && dp->aux_switch_node && dp->aux->switch_configure) {
+		rc = dp_display_init_aux_switch(dp);
+		if (rc)
+			return rc;
+
+		rc = dp->aux->switch_configure(dp->aux, true, dp->hpd->orientation);
+		if (rc)
+			return rc;
+	}
+
+	mutex_lock(&dp->session_lock);
+
+	if (dp_display_state_is(DP_STATE_TUI_ACTIVE)) {
+		dp_display_state_log("[TUI is active]");
+		mutex_unlock(&dp->session_lock);
+		return 0;
+	}
+
+	dp_display_state_remove(DP_STATE_ABORTED);
+	dp_display_state_add(DP_STATE_CONFIGURED);
+
+	rc = dp_display_host_init(dp);
+	if (rc) {
+		DP_ERR("Host init Failed");
+		mutex_unlock(&dp->session_lock);
+		return rc;
+	}
+
+	/* check for hpd high */
+	if (dp->hpd->hpd_high)
+		queue_work(dp->wq, &dp->connect_work);
+	else
+		dp->process_hpd_connect = true;
+	mutex_unlock(&dp->session_lock);
+
+	return 0;
+}
+
+static void dp_display_clear_reservation(struct dp_display *dp, struct dp_panel *panel)
+{
+	struct dp_display_private *dp_display;
+
+	if (!dp || !panel) {
+		DP_ERR("invalid params\n");
+		return;
+	}
+
+	dp_display = container_of(dp, struct dp_display_private, dp_display);
+
+	mutex_lock(&dp_display->accounting_lock);
+
+	dp_display->tot_lm_blks_in_use -= panel->max_lm;
+	panel->max_lm = 0;
+
+	if (!dp_display->active_stream_cnt)
+		dp_display->tot_lm_blks_in_use = 0;
+
+	mutex_unlock(&dp_display->accounting_lock);
+}
+
+static void dp_display_clear_dsc_resources(struct dp_display_private *dp,
+		struct dp_panel *panel)
+{
+	dp->tot_dsc_blks_in_use -= panel->dsc_blks_in_use;
+	panel->dsc_blks_in_use = 0;
+}
+
+static int dp_display_get_mst_pbn_div(struct dp_display *dp_display)
+{
+	struct dp_display_private *dp;
+	u32 link_rate, lane_count;
+
+	if (!dp_display) {
+		DP_ERR("invalid params\n");
+		return 0;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	link_rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code);
+	lane_count = dp->link->link_params.lane_count;
+
+	return link_rate * lane_count / 54000;
+}
+
+static int dp_display_stream_pre_disable(struct dp_display_private *dp,
+			struct dp_panel *dp_panel)
+{
+	if (!dp->active_stream_cnt) {
+		DP_WARN("streams already disabled cnt=%d\n",
+				dp->active_stream_cnt);
+		return 0;
+	}
+
+	dp->ctrl->stream_pre_off(dp->ctrl, dp_panel);
+
+	return 0;
+}
+
+static void dp_display_stream_disable(struct dp_display_private *dp,
+			struct dp_panel *dp_panel)
+{
+	if (!dp->active_stream_cnt) {
+		DP_WARN("streams already disabled cnt=%d\n",
+				dp->active_stream_cnt);
+		return;
+	}
+
+	if (dp_panel->stream_id == DP_STREAM_MAX ||
+			!dp->active_panels[dp_panel->stream_id]) {
+		DP_ERR("panel is already disabled\n");
+		return;
+	}
+
+	dp_display_clear_dsc_resources(dp, dp_panel);
+
+	DP_DEBUG("stream_id=%d, active_stream_cnt=%d, tot_dsc_blks_in_use=%d\n",
+			dp_panel->stream_id, dp->active_stream_cnt,
+			dp->tot_dsc_blks_in_use);
+
+	dp->ctrl->stream_off(dp->ctrl, dp_panel);
+	dp->active_panels[dp_panel->stream_id] = NULL;
+	dp->active_stream_cnt--;
+}
+
+static void dp_display_clean(struct dp_display_private *dp, bool skip_wait)
+{
+	int idx;
+	struct dp_panel *dp_panel;
+	struct dp_link_hdcp_status *status = &dp->link->hdcp_status;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+
+	if (dp_display_state_is(DP_STATE_TUI_ACTIVE)) {
+		DP_WARN("TUI is active\n");
+		return;
+	}
+
+	if (dp_display_is_hdcp_enabled(dp) &&
+			status->hdcp_state != HDCP_STATE_INACTIVE) {
+		cancel_delayed_work_sync(&dp->hdcp_cb_work);
+		if (dp->hdcp.ops->off)
+			dp->hdcp.ops->off(dp->hdcp.data);
+
+		dp_display_update_hdcp_status(dp, true);
+	}
+
+	for (idx = DP_STREAM_0; idx < DP_STREAM_MAX; idx++) {
+		if (!dp->active_panels[idx])
+			continue;
+
+		dp_panel = dp->active_panels[idx];
+		if (dp_panel->audio_supported)
+			dp_panel->audio->off(dp_panel->audio, skip_wait);
+
+		if (!skip_wait)
+			dp_display_stream_pre_disable(dp, dp_panel);
+		dp_display_stream_disable(dp, dp_panel);
+		dp_display_clear_reservation(&dp->dp_display, dp_panel);
+		dp_panel->deinit(dp_panel, 0);
+	}
+
+	dp_display_state_remove(DP_STATE_ENABLED | DP_STATE_CONNECTED);
+
+	dp->ctrl->off(dp->ctrl);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+}
+
+static int dp_display_handle_disconnect(struct dp_display_private *dp, bool skip_wait)
+{
+	int rc;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	rc = dp_display_process_hpd_low(dp, skip_wait);
+	if (rc) {
+		/* cancel any pending request */
+		dp->ctrl->abort(dp->ctrl, true);
+		dp->aux->abort(dp->aux, true);
+	}
+
+	mutex_lock(&dp->session_lock);
+	if (dp_display_state_is(DP_STATE_ENABLED))
+		dp_display_clean(dp, skip_wait);
+
+	dp_display_host_unready(dp);
+
+	dp->tot_lm_blks_in_use = 0;
+
+	mutex_unlock(&dp->session_lock);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+	return rc;
+}
+
+static void dp_display_disconnect_sync(struct dp_display_private *dp)
+{
+	int disconnect_delay_ms;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	/* cancel any pending request */
+	dp_display_state_add(DP_STATE_ABORTED);
+
+	dp->ctrl->abort(dp->ctrl, true);
+	dp->aux->abort(dp->aux, true);
+
+	/* wait for idle state */
+	cancel_work_sync(&dp->connect_work);
+	cancel_work_sync(&dp->attention_work);
+	cancel_work_sync(&dp->disconnect_work);
+	flush_workqueue(dp->wq);
+
+	/*
+	 * Delay the teardown of the mainlink for better interop experience.
+	 * It is possible that certain sinks can issue an HPD high immediately
+	 * following an HPD low as soon as they detect the mainlink being
+	 * turned off. This can sometimes result in the HPD low pulse getting
+	 * lost with certain cable. This issue is commonly seen when running
+	 * DP LL CTS test 4.2.1.3.
+	 */
+	disconnect_delay_ms = min_t(u32, dp->debug->disconnect_delay_ms,
+			(u32) MAX_DISCONNECT_DELAY_MS);
+	DP_DEBUG("disconnect delay = %d ms\n", disconnect_delay_ms);
+	msleep(disconnect_delay_ms);
+
+	dp_display_handle_disconnect(dp, false);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state,
+		disconnect_delay_ms);
+}
+
+static int dp_display_usbpd_disconnect_cb(struct device *dev)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dev) {
+		DP_ERR("invalid dev\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dp = dev_get_drvdata(dev);
+	if (!dp) {
+		DP_ERR("no driver data found\n");
+		rc = -ENODEV;
+		goto end;
+	}
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state,
+			dp->debug->psm_enabled);
+
+	/* skip if a disconnect is already in progress */
+	if (dp_display_state_is(DP_STATE_ABORTED) &&
+	    dp_display_state_is(DP_STATE_READY)) {
+		DP_DEBUG("disconnect already in progress\n");
+		SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_CASE1, dp->state);
+		return 0;
+	}
+
+	if (dp->debug->psm_enabled && dp_display_state_is(DP_STATE_READY))
+		dp->link->psm_config(dp->link, &dp->panel->link_info, true);
+
+	dp->ctrl->abort(dp->ctrl, true);
+	dp->aux->abort(dp->aux, true);
+
+	if (!dp->debug->sim_mode && !dp->no_aux_switch
+	    && !dp->parser->gpio_aux_switch && dp->aux->switch_configure)
+		dp->aux->switch_configure(dp->aux, false, ORIENTATION_NONE);
+
+	dp_display_disconnect_sync(dp);
+
+	mutex_lock(&dp->session_lock);
+	dp_display_host_deinit(dp);
+	dp_display_state_remove(DP_STATE_CONFIGURED);
+	mutex_unlock(&dp->session_lock);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+end:
+	return rc;
+}
+
+static int dp_display_stream_enable(struct dp_display_private *dp,
+			struct dp_panel *dp_panel)
+{
+	int rc = 0;
+
+	rc = dp->ctrl->stream_on(dp->ctrl, dp_panel);
+
+	if (dp->debug->tpg_pattern)
+		dp_panel->tpg_config(dp_panel, dp->debug->tpg_pattern);
+
+	if (!rc) {
+		dp->active_panels[dp_panel->stream_id] = dp_panel;
+		dp->active_stream_cnt++;
+	}
+
+
+	DP_DEBUG("dp active_stream_cnt:%d, tot_dsc_blks_in_use=%d\n",
+			dp->active_stream_cnt, dp->tot_dsc_blks_in_use);
+
+	return rc;
+}
+
+static void dp_display_mst_attention(struct dp_display_private *dp)
+{
+	if (dp->mst.mst_active && dp->mst.cbs.hpd_irq)
+		dp->mst.cbs.hpd_irq(&dp->dp_display);
+
+	DP_MST_DEBUG("mst_attention_work. mst_active:%d\n", dp->mst.mst_active);
+}
+
+static void dp_display_attention_work(struct work_struct *work)
+{
+	struct dp_display_private *dp = container_of(work,
+			struct dp_display_private, attention_work);
+	int rc = 0;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	mutex_lock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(dp->state);
+
+	if (dp_display_state_is(DP_STATE_ABORTED)) {
+		DP_INFO("Hpd off, not handling any attention\n");
+		mutex_unlock(&dp->session_lock);
+		goto exit;
+	}
+
+	if (!dp_display_state_is(DP_STATE_READY)) {
+		mutex_unlock(&dp->session_lock);
+		goto mst_attention;
+	}
+
+	if (dp->link->process_request(dp->link)) {
+		mutex_unlock(&dp->session_lock);
+		goto cp_irq;
+	}
+
+	mutex_unlock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(dp->state, dp->link->sink_request);
+
+	if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) {
+		SDE_EVT32_EXTERNAL(dp->state, DS_PORT_STATUS_CHANGED);
+		if (!dp->mst.mst_active) {
+			if (dp_display_is_sink_count_zero(dp)) {
+				dp_display_handle_disconnect(dp, false);
+			} else {
+				/*
+				 * connect work should take care of sending
+				 * the HPD notification.
+				 */
+				queue_work(dp->wq, &dp->connect_work);
+			}
+		}
+
+		goto mst_attention;
+	}
+
+	if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) {
+		SDE_EVT32_EXTERNAL(dp->state, DP_TEST_LINK_VIDEO_PATTERN);
+		dp_display_handle_disconnect(dp, false);
+
+		dp->panel->video_test = true;
+		/*
+		 * connect work should take care of sending
+		 * the HPD notification.
+		 */
+		queue_work(dp->wq, &dp->connect_work);
+
+		goto mst_attention;
+	}
+
+	if (dp->link->sink_request & (DP_TEST_LINK_PHY_TEST_PATTERN |
+		DP_TEST_LINK_TRAINING | DP_LINK_STATUS_UPDATED)) {
+
+		mutex_lock(&dp->session_lock);
+		dp_audio_enable(dp, false);
+
+		if (dp->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
+			SDE_EVT32_EXTERNAL(dp->state,
+					DP_TEST_LINK_PHY_TEST_PATTERN);
+			dp->ctrl->process_phy_test_request(dp->ctrl);
+		}
+
+		if (dp->link->sink_request & DP_TEST_LINK_TRAINING) {
+			SDE_EVT32_EXTERNAL(dp->state, DP_TEST_LINK_TRAINING);
+			dp->link->send_test_response(dp->link);
+			rc = dp->ctrl->link_maintenance(dp->ctrl);
+		}
+
+		if (dp->link->sink_request & DP_LINK_STATUS_UPDATED) {
+			SDE_EVT32_EXTERNAL(dp->state, DP_LINK_STATUS_UPDATED);
+			rc = dp->ctrl->link_maintenance(dp->ctrl);
+		}
+
+		if (!rc)
+			dp_audio_enable(dp, true);
+
+		mutex_unlock(&dp->session_lock);
+		if (rc)
+			goto exit;
+
+		if (dp->link->sink_request & (DP_TEST_LINK_PHY_TEST_PATTERN |
+			DP_TEST_LINK_TRAINING))
+			goto mst_attention;
+	}
+
+cp_irq:
+	if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->cp_irq)
+		dp->hdcp.ops->cp_irq(dp->hdcp.data);
+
+	if (!dp->mst.mst_active) {
+		/*
+		 * It is possible that the connect_work skipped sending
+		 * the HPD notification if the attention message was
+		 * already pending. Send the notification here to
+		 * account for that. It is possible that the test sequence
+		 * can trigger an unplug after DP_LINK_STATUS_UPDATED, before
+		 * starting the next test case. Make sure to check the HPD status.
+		 */
+		if (!dp_display_state_is(DP_STATE_ABORTED))
+			dp_display_send_hpd_notification(dp, false);
+	}
+
+mst_attention:
+	dp_display_mst_attention(dp);
+exit:
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+}
+
+static int dp_display_usbpd_attention_cb(struct device *dev)
+{
+	struct dp_display_private *dp;
+
+	if (!dev) {
+		DP_ERR("invalid dev\n");
+		return -EINVAL;
+	}
+
+	dp = dev_get_drvdata(dev);
+	if (!dp) {
+		DP_ERR("no driver data found\n");
+		return -ENODEV;
+	}
+
+	DP_DEBUG("hpd_irq:%d, hpd_high:%d, power_on:%d, is_connected:%d\n",
+			dp->hpd->hpd_irq, dp->hpd->hpd_high,
+			!!dp_display_state_is(DP_STATE_ENABLED),
+			!!dp_display_state_is(DP_STATE_CONNECTED));
+	SDE_EVT32_EXTERNAL(dp->state, dp->hpd->hpd_irq, dp->hpd->hpd_high,
+			!!dp_display_state_is(DP_STATE_ENABLED),
+			!!dp_display_state_is(DP_STATE_CONNECTED));
+
+	if (!dp->hpd->hpd_high) {
+		dp_display_disconnect_sync(dp);
+		return 0;
+	}
+
+	/*
+	 * Ignore all the attention messages except HPD LOW when TUI is
+	 * active, so user mode can be notified of the disconnect event. This
+	 * allows user mode to tear down the control path after the TUI
+	 * session is over. Ideally this should never happen, but on the off
+	 * chance that there is a race condition in which there is a IRQ HPD
+	 * during tear down of DP at TUI start then this check might help avoid
+	 * a potential issue accessing registers in attention processing.
+	 */
+	if (dp_display_state_is(DP_STATE_TUI_ACTIVE)) {
+		DP_WARN("TUI is active\n");
+		return 0;
+	}
+
+	if (dp->hpd->hpd_irq && dp_display_state_is(DP_STATE_READY)) {
+		queue_work(dp->wq, &dp->attention_work);
+		complete_all(&dp->attention_comp);
+	} else if (dp->process_hpd_connect ||
+			 !dp_display_state_is(DP_STATE_CONNECTED)) {
+		dp_display_state_remove(DP_STATE_ABORTED);
+		queue_work(dp->wq, &dp->connect_work);
+	} else {
+		DP_DEBUG("ignored\n");
+	}
+
+	return 0;
+}
+
+static void dp_display_connect_work(struct work_struct *work)
+{
+	int rc = 0;
+	struct dp_display_private *dp = container_of(work,
+			struct dp_display_private, connect_work);
+
+	if (dp_display_state_is(DP_STATE_TUI_ACTIVE)) {
+		dp_display_state_log("[TUI is active]");
+		return;
+	}
+
+	if (dp_display_state_is(DP_STATE_ABORTED)) {
+		DP_WARN("HPD off requested\n");
+		return;
+	}
+
+	if (!dp->hpd->hpd_high) {
+		DP_WARN("Sink disconnected\n");
+		return;
+	}
+
+	rc = dp_display_process_hpd_high(dp);
+
+	if (!rc && dp->panel->video_test)
+		dp->link->send_test_response(dp->link);
+}
+
+static void dp_display_disconnect_work(struct work_struct *work)
+{
+	struct dp_display_private *dp = container_of(work,
+			struct dp_display_private, disconnect_work);
+
+	dp_display_handle_disconnect(dp, false);
+
+	if (dp->debug->sim_mode && dp_display_state_is(DP_STATE_ABORTED))
+		dp_display_host_deinit(dp);
+
+	dp->debug->abort(dp->debug);
+}
+
+static int dp_display_usb_notifier(struct notifier_block *nb,
+	unsigned long action, void *data)
+{
+	struct dp_display_private *dp = container_of(nb,
+			struct dp_display_private, usb_nb);
+
+	SDE_EVT32_EXTERNAL(dp->state, dp->debug->sim_mode, action);
+	if (!action && dp->debug->sim_mode) {
+		DP_WARN("usb disconnected during simulation\n");
+		dp_display_state_add(DP_STATE_ABORTED);
+		dp->ctrl->abort(dp->ctrl, true);
+		dp->aux->abort(dp->aux, true);
+
+		dp->power->park_clocks(dp->power);
+
+		queue_work(dp->wq, &dp->disconnect_work);
+	}
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, NOTIFY_DONE);
+	return NOTIFY_DONE;
+}
+
+static void dp_display_register_usb_notifier(struct dp_display_private *dp)
+{
+	int rc = 0;
+	const char *phandle = "usb-phy";
+	struct usb_phy *usbphy;
+
+	usbphy = devm_usb_get_phy_by_phandle(&dp->pdev->dev, phandle, 0);
+	if (IS_ERR_OR_NULL(usbphy)) {
+		DP_DEBUG("unable to get usbphy\n");
+		return;
+	}
+
+	dp->usb_nb.notifier_call = dp_display_usb_notifier;
+	dp->usb_nb.priority = 2;
+	rc = usb_register_notifier(usbphy, &dp->usb_nb);
+	if (rc)
+		DP_DEBUG("failed to register for usb event: %d\n", rc);
+}
+
+int dp_display_mmrm_callback(struct mmrm_client_notifier_data *notifier_data)
+{
+	struct dss_clk_mmrm_cb *mmrm_cb_data = (struct dss_clk_mmrm_cb *)notifier_data->pvt_data;
+	struct dp_display *dp_display = (struct dp_display *)mmrm_cb_data->phandle;
+	struct dp_display_private *dp =
+		container_of(dp_display, struct dp_display_private, dp_display);
+	int ret = 0;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state, notifier_data->cb_type);
+	if (notifier_data->cb_type == MMRM_CLIENT_RESOURCE_VALUE_CHANGE
+				&& dp_display_state_is(DP_STATE_ENABLED)
+				&& !dp_display_state_is(DP_STATE_ABORTED)) {
+		ret = dp_display_handle_disconnect(dp, false);
+		if (ret)
+			DP_ERR("mmrm callback error reducing clk, ret:%d\n", ret);
+	}
+
+	DP_DEBUG("mmrm callback handled, state: 0x%x rc:%d\n", dp->state, ret);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, notifier_data->cb_type);
+	return ret;
+}
+
+static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
+{
+	dp_debug_put(dp->debug);
+	dp_hpd_put(dp->hpd);
+	if (dp->panel)
+		dp_audio_put(dp->panel->audio);
+	dp_ctrl_put(dp->ctrl);
+	dp_panel_put(dp->panel);
+	dp_link_put(dp->link);
+	dp_power_put(dp->power);
+	dp_pll_put(dp->pll);
+	dp_aux_put(dp->aux);
+	dp_catalog_put(dp->catalog);
+	dp_parser_put(dp->parser);
+	mutex_destroy(&dp->session_lock);
+}
+
+static int dp_init_sub_modules(struct dp_display_private *dp)
+{
+	int rc = 0;
+	u32 dp_core_revision = 0;
+	bool hdcp_disabled;
+	const char *phandle = "qcom,dp-aux-switch";
+	struct device *dev = &dp->pdev->dev;
+	struct dp_hpd_cb *cb = &dp->hpd_cb;
+	struct dp_ctrl_in ctrl_in = {
+		.dev = dev,
+	};
+	struct dp_panel_in panel_in = {
+		.dev = dev,
+	};
+	struct dp_debug_in debug_in = {
+		.dev = dev,
+	};
+	struct dp_pll_in pll_in = {
+		.pdev = dp->pdev,
+	};
+
+	mutex_init(&dp->session_lock);
+	mutex_init(&dp->accounting_lock);
+
+	dp->parser = dp_parser_get(dp->pdev);
+	if (IS_ERR(dp->parser)) {
+		rc = PTR_ERR(dp->parser);
+		DP_ERR("failed to initialize parser, rc = %d\n", rc);
+		dp->parser = NULL;
+		goto error;
+	}
+
+	rc = dp->parser->parse(dp->parser);
+	if (rc) {
+		DP_ERR("device tree parsing failed\n");
+		goto error_catalog;
+	}
+
+	g_dp_display->is_mst_supported = dp->parser->has_mst;
+	g_dp_display->dsc_cont_pps = dp->parser->dsc_continuous_pps;
+
+	dp->catalog = dp_catalog_get(dev, dp->parser);
+	if (IS_ERR(dp->catalog)) {
+		rc = PTR_ERR(dp->catalog);
+		DP_ERR("failed to initialize catalog, rc = %d\n", rc);
+		dp->catalog = NULL;
+		goto error_catalog;
+	}
+
+	dp_core_revision = dp_catalog_get_dp_core_version(dp->catalog);
+
+	dp->aux_switch_node = of_parse_phandle(dp->pdev->dev.of_node, phandle, 0);
+	if (!dp->aux_switch_node) {
+		dp->no_aux_switch = true;
+		DP_WARN("Aux switch node not found, assigning bypass mode as switch type\n");
+		dp->switch_type = DP_AUX_SWITCH_BYPASS;
+		goto skip_node_name;
+	}
+
+	if (!strcmp(dp->aux_switch_node->name, "fsa4480"))
+		dp->switch_type = DP_AUX_SWITCH_FSA4480;
+	else if (!strcmp(dp->aux_switch_node->name, "wcd939x_i2c"))
+		dp->switch_type = DP_AUX_SWITCH_WCD939x;
+	else
+		dp->switch_type = DP_AUX_SWITCH_BYPASS;
+
+skip_node_name:
+	dp->aux = dp_aux_get(dev, &dp->catalog->aux, dp->parser,
+			dp->aux_switch_node, dp->aux_bridge, g_dp_display->dp_aux_ipc_log,
+			dp->switch_type);
+	if (IS_ERR(dp->aux)) {
+		rc = PTR_ERR(dp->aux);
+		DP_ERR("failed to initialize aux, rc = %d\n", rc);
+		dp->aux = NULL;
+		goto error_aux;
+	}
+
+	rc = dp->aux->drm_aux_register(dp->aux, dp->dp_display.drm_dev);
+	if (rc) {
+		DP_ERR("DRM DP AUX register failed\n");
+		goto error_pll;
+	}
+
+	pll_in.aux = dp->aux;
+	pll_in.parser = dp->parser;
+	pll_in.dp_core_revision = dp_core_revision;
+
+	dp->pll = dp_pll_get(&pll_in);
+	if (IS_ERR(dp->pll)) {
+		rc = PTR_ERR(dp->pll);
+		DP_ERR("failed to initialize pll, rc = %d\n", rc);
+		dp->pll = NULL;
+		goto error_pll;
+	}
+
+	dp->power = dp_power_get(dp->parser, dp->pll);
+	if (IS_ERR(dp->power)) {
+		rc = PTR_ERR(dp->power);
+		DP_ERR("failed to initialize power, rc = %d\n", rc);
+		dp->power = NULL;
+		goto error_power;
+	}
+
+	rc = dp->power->power_client_init(dp->power, &dp->priv->phandle,
+		dp->dp_display.drm_dev);
+	if (rc) {
+		DP_ERR("Power client create failed\n");
+		goto error_link;
+	}
+
+	rc = dp->power->power_mmrm_init(dp->power, &dp->priv->phandle,
+		(void *)&dp->dp_display, dp_display_mmrm_callback);
+	if (rc) {
+		DP_ERR("failed to initialize mmrm, rc = %d\n", rc);
+		goto error_link;
+	}
+
+	dp->link = dp_link_get(dev, dp->aux, dp_core_revision);
+	if (IS_ERR(dp->link)) {
+		rc = PTR_ERR(dp->link);
+		DP_ERR("failed to initialize link, rc = %d\n", rc);
+		dp->link = NULL;
+		goto error_link;
+	}
+
+	panel_in.aux = dp->aux;
+	panel_in.catalog = &dp->catalog->panel;
+	panel_in.link = dp->link;
+	panel_in.connector = dp->dp_display.base_connector;
+	panel_in.base_panel = NULL;
+	panel_in.parser = dp->parser;
+
+	dp->panel = dp_panel_get(&panel_in);
+	if (IS_ERR(dp->panel)) {
+		rc = PTR_ERR(dp->panel);
+		DP_ERR("failed to initialize panel, rc = %d\n", rc);
+		dp->panel = NULL;
+		goto error_panel;
+	}
+
+	ctrl_in.link = dp->link;
+	ctrl_in.panel = dp->panel;
+	ctrl_in.aux = dp->aux;
+	ctrl_in.power = dp->power;
+	ctrl_in.catalog = &dp->catalog->ctrl;
+	ctrl_in.parser = dp->parser;
+	ctrl_in.pll = dp->pll;
+
+	dp->ctrl = dp_ctrl_get(&ctrl_in);
+	if (IS_ERR(dp->ctrl)) {
+		rc = PTR_ERR(dp->ctrl);
+		DP_ERR("failed to initialize ctrl, rc = %d\n", rc);
+		dp->ctrl = NULL;
+		goto error_ctrl;
+	}
+
+	dp->panel->audio = dp_audio_get(dp->pdev, dp->panel,
+						&dp->catalog->audio);
+	if (IS_ERR(dp->panel->audio)) {
+		rc = PTR_ERR(dp->panel->audio);
+		DP_ERR("failed to initialize audio, rc = %d\n", rc);
+		dp->panel->audio = NULL;
+		goto error_audio;
+	}
+
+	memset(&dp->mst, 0, sizeof(dp->mst));
+	dp->active_stream_cnt = 0;
+
+	cb->configure  = dp_display_usbpd_configure_cb;
+	cb->disconnect = dp_display_usbpd_disconnect_cb;
+	cb->attention  = dp_display_usbpd_attention_cb;
+
+	dp->hpd = dp_hpd_get(dev, dp->parser, &dp->catalog->hpd,
+			dp->aux_bridge, cb);
+	if (IS_ERR(dp->hpd)) {
+		rc = PTR_ERR(dp->hpd);
+		DP_ERR("failed to initialize hpd, rc = %d\n", rc);
+		dp->hpd = NULL;
+		goto error_hpd;
+	}
+
+	hdcp_disabled = !!dp_display_initialize_hdcp(dp);
+
+	debug_in.panel = dp->panel;
+	debug_in.hpd = dp->hpd;
+	debug_in.link = dp->link;
+	debug_in.aux = dp->aux;
+	debug_in.connector = &dp->dp_display.base_connector;
+	debug_in.catalog = dp->catalog;
+	debug_in.parser = dp->parser;
+	debug_in.ctrl = dp->ctrl;
+	debug_in.pll = dp->pll;
+	debug_in.display = &dp->dp_display;
+
+	dp->debug = dp_debug_get(&debug_in);
+	if (IS_ERR(dp->debug)) {
+		rc = PTR_ERR(dp->debug);
+		DP_ERR("failed to initialize debug, rc = %d\n", rc);
+		dp->debug = NULL;
+		goto error_debug;
+	}
+
+	dp->cached_connector_status = connector_status_disconnected;
+	dp->tot_dsc_blks_in_use = 0;
+	dp->tot_lm_blks_in_use = 0;
+
+	dp->debug->hdcp_disabled = hdcp_disabled;
+	dp_display_update_hdcp_status(dp, true);
+
+	dp_display_register_usb_notifier(dp);
+
+	if (dp->hpd->register_hpd) {
+		rc = dp->hpd->register_hpd(dp->hpd);
+		if (rc) {
+			DP_ERR("failed register hpd\n");
+			goto error_hpd_reg;
+		}
+	}
+
+	return rc;
+error_hpd_reg:
+	dp_debug_put(dp->debug);
+error_debug:
+	dp_hpd_put(dp->hpd);
+error_hpd:
+	dp_audio_put(dp->panel->audio);
+error_audio:
+	dp_ctrl_put(dp->ctrl);
+error_ctrl:
+	dp_panel_put(dp->panel);
+error_panel:
+	dp_link_put(dp->link);
+error_link:
+	dp_power_put(dp->power);
+error_power:
+	dp_pll_put(dp->pll);
+error_pll:
+	dp_aux_put(dp->aux);
+error_aux:
+	dp_catalog_put(dp->catalog);
+error_catalog:
+	dp_parser_put(dp->parser);
+error:
+	mutex_destroy(&dp->session_lock);
+	return rc;
+}
+
+static int dp_display_post_init(struct dp_display *dp_display)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	if (IS_ERR_OR_NULL(dp)) {
+		DP_ERR("invalid params\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	rc = dp_init_sub_modules(dp);
+	if (rc)
+		goto end;
+
+	dp_display->post_init = NULL;
+end:
+	DP_DEBUG("%s\n", rc ? "failed" : "success");
+	return rc;
+}
+
+static int dp_display_set_mode(struct dp_display *dp_display, void *panel,
+		struct dp_display_mode *mode)
+{
+	const u32 num_components = 3, default_bpp = 24;
+	struct dp_display_private *dp;
+	struct dp_panel *dp_panel;
+	bool dsc_en = (mode->capabilities & DP_PANEL_CAPS_DSC) ? true : false;
+
+	if (!dp_display || !panel) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp_panel = panel;
+	if (!dp_panel->connector) {
+		DP_ERR("invalid connector input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state,
+			mode->timing.h_active, mode->timing.v_active,
+			mode->timing.refresh_rate);
+
+	mutex_lock(&dp->session_lock);
+	mode->timing.bpp =
+		dp_panel->connector->display_info.bpc * num_components;
+	if (!mode->timing.bpp)
+		mode->timing.bpp = default_bpp;
+
+	mode->timing.bpp = dp->panel->get_mode_bpp(dp->panel,
+			mode->timing.bpp, mode->timing.pixel_clk_khz, dsc_en);
+
+	if (dp->mst.mst_active)
+		dp->mst.cbs.set_mst_mode_params(&dp->dp_display, mode);
+
+	dp_panel->pinfo = mode->timing;
+	mutex_unlock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+
+	return 0;
+}
+
+static int dp_display_prepare(struct dp_display *dp_display, void *panel)
+{
+	struct dp_display_private *dp;
+	struct dp_panel *dp_panel;
+	int rc = 0;
+
+	if (!dp_display || !panel) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp_panel = panel;
+	if (!dp_panel->connector) {
+		DP_ERR("invalid connector input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	mutex_lock(&dp->session_lock);
+
+	/*
+	 * If DP video session is restored by the userspace after display
+	 * disconnect notification from dongle i.e. typeC cable connected to
+	 * source but disconnected at the display side, the DP controller is
+	 * not restored to the desired configured state. So, ensure host_init
+	 * is executed in such a scenario so that all the DP controller
+	 * resources are enabled for the next connection event.
+	 */
+	if (dp_display_state_is(DP_STATE_SRC_PWRDN) &&
+			dp_display_state_is(DP_STATE_CONFIGURED)) {
+		rc = dp_display_host_init(dp);
+		if (rc) {
+			/*
+			 * Skip all the events that are similar to abort case, just that
+			 * the stream clks should be enabled so that no commit failure can
+			 * be seen.
+			 */
+			DP_ERR("Host init failed.\n");
+			goto end;
+		}
+
+		/*
+		 * Remove DP_STATE_SRC_PWRDN flag on successful host_init to
+		 * prevent cases such as below.
+		 * 1. MST stream 1 failed to do host init then stream 2 can retry again.
+		 * 2. Resume path fails, now sink sends hpd_high=0 and hpd_high=1.
+		 */
+		dp_display_state_remove(DP_STATE_SRC_PWRDN);
+	}
+
+	/*
+	 * If the physical connection to the sink is already lost by the time
+	 * we try to set up the connection, we can just skip all the steps
+	 * here safely.
+	 */
+	if (dp_display_state_is(DP_STATE_ABORTED)) {
+		dp_display_state_log("[aborted]");
+		goto end;
+	}
+
+	/*
+	 * If DP_STATE_ENABLED, there is nothing left to do.
+	 * This would happen during MST flow. So, log this.
+	 */
+	if (dp_display_state_is(DP_STATE_ENABLED)) {
+		dp_display_state_warn("[already enabled]");
+		goto end;
+	}
+
+	if (!dp_display_is_ready(dp)) {
+		dp_display_state_show("[not ready]");
+		goto end;
+	}
+
+	/* For supporting DP_PANEL_SRC_INITIATED_POWER_DOWN case */
+	rc = dp_display_host_ready(dp);
+	if (rc) {
+		dp_display_state_show("[ready failed]");
+		goto end;
+	}
+
+	if (dp->debug->psm_enabled) {
+		dp->link->psm_config(dp->link, &dp->panel->link_info, false);
+		dp->debug->psm_enabled = false;
+	}
+
+	/*
+	 * Execute the dp controller power on in shallow mode here.
+	 * In normal cases, controller should have been powered on
+	 * by now. In some cases like suspend/resume or framework
+	 * reboot, we end up here without a powered on controller.
+	 * Cable may have been removed in suspended state. In that
+	 * case, link training is bound to fail on system resume.
+	 * So, we execute in shallow mode here to do only minimal
+	 * and required things.
+	 */
+	rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, dp_panel->fec_en,
+			dp_panel->dsc_en, true);
+	if (rc)
+		goto end;
+
+end:
+	mutex_unlock(&dp->session_lock);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, rc);
+	return rc;
+}
+
+static int dp_display_set_stream_info(struct dp_display *dp_display,
+			void *panel, u32 strm_id, u32 start_slot,
+			u32 num_slots, u32 pbn, int vcpi)
+{
+	int rc = 0;
+	struct dp_panel *dp_panel;
+	struct dp_display_private *dp;
+	const int max_slots = 64;
+
+	if (!dp_display) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	if (strm_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream id:%d\n", strm_id);
+		return -EINVAL;
+	}
+
+	if (start_slot + num_slots > max_slots) {
+		DP_ERR("invalid channel info received. start:%d, slots:%d\n",
+				start_slot, num_slots);
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state, strm_id,
+			start_slot, num_slots);
+
+	mutex_lock(&dp->session_lock);
+
+	dp->ctrl->set_mst_channel_info(dp->ctrl, strm_id,
+			start_slot, num_slots);
+
+	if (panel) {
+		dp_panel = panel;
+		dp_panel->set_stream_info(dp_panel, strm_id, start_slot,
+				num_slots, pbn, vcpi);
+	}
+
+	mutex_unlock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, rc);
+
+	return rc;
+}
+
+static int dp_display_enable(struct dp_display *dp_display, void *panel)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dp_display || !panel) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	mutex_lock(&dp->session_lock);
+
+	/*
+	 * If DP_STATE_READY is not set, we should not do any HW
+	 * programming.
+	 */
+	if (!dp_display_state_is(DP_STATE_READY)) {
+		dp_display_state_show("[host not ready]");
+		goto end;
+	}
+
+	/*
+	 * It is possible that by the time we get call back to establish
+	 * the DP pipeline e2e, the physical DP connection to the sink is
+	 * already lost. In such cases, the DP_STATE_ABORTED would be set.
+	 * However, it is necessary to NOT abort the display setup here so as
+	 * to ensure that the rest of the system is in a stable state prior to
+	 * handling the disconnect notification.
+	 */
+	if (dp_display_state_is(DP_STATE_ABORTED))
+		dp_display_state_log("[aborted, but continue on]");
+
+	rc = dp_display_stream_enable(dp, panel);
+	if (rc)
+		goto end;
+
+	dp_display_state_add(DP_STATE_ENABLED);
+end:
+	mutex_unlock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, rc);
+	return rc;
+}
+
+static void dp_display_stream_post_enable(struct dp_display_private *dp,
+			struct dp_panel *dp_panel)
+{
+	dp_panel->spd_config(dp_panel);
+	dp_panel->setup_hdr(dp_panel, NULL, false, 0, true);
+}
+
+static int dp_display_post_enable(struct dp_display *dp_display, void *panel)
+{
+	struct dp_display_private *dp;
+	struct dp_panel *dp_panel;
+
+	if (!dp_display || !panel) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	dp_panel = panel;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	mutex_lock(&dp->session_lock);
+
+	/*
+	 * If DP_STATE_READY is not set, we should not do any HW
+	 * programming.
+	 */
+	if (!dp_display_state_is(DP_STATE_ENABLED)) {
+		dp_display_state_show("[not enabled]");
+		goto end;
+	}
+
+	/*
+	 * If the physical connection to the sink is already lost by the time
+	 * we try to set up the connection, we can just skip all the steps
+	 * here safely.
+	 */
+	if (dp_display_state_is(DP_STATE_ABORTED)) {
+		dp_display_state_log("[aborted]");
+		goto end;
+	}
+
+	if (!dp_display_is_ready(dp) || !dp_display_state_is(DP_STATE_READY)) {
+		dp_display_state_show("[not ready]");
+		goto end;
+	}
+
+	dp_display_stream_post_enable(dp, dp_panel);
+
+	cancel_delayed_work_sync(&dp->hdcp_cb_work);
+	queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ);
+
+	if (dp_panel->audio_supported) {
+		dp_panel->audio->bw_code = dp->link->link_params.bw_code;
+		dp_panel->audio->lane_count = dp->link->link_params.lane_count;
+		dp_panel->audio->on(dp_panel->audio);
+	}
+
+	dp->aux->state &= ~DP_STATE_CTRL_POWERED_OFF;
+	dp->aux->state |= DP_STATE_CTRL_POWERED_ON;
+	complete_all(&dp->notification_comp);
+	DP_DEBUG("display post enable complete. state: 0x%x\n", dp->state);
+end:
+	mutex_unlock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+	return 0;
+}
+
+static void dp_display_clear_colorspaces(struct dp_display *dp_display)
+{
+	struct drm_connector *connector;
+	struct sde_connector *sde_conn;
+
+	connector = dp_display->base_connector;
+	sde_conn = to_sde_connector(connector);
+	sde_conn->color_enc_fmt = 0;
+}
+
+static int dp_display_pre_disable(struct dp_display *dp_display, void *panel)
+{
+	struct dp_display_private *dp;
+	struct dp_panel *dp_panel = panel;
+	struct dp_link_hdcp_status *status;
+	int rc = 0;
+	size_t i;
+
+	if (!dp_display || !panel) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	mutex_lock(&dp->session_lock);
+
+	status = &dp->link->hdcp_status;
+
+	if (!dp_display_state_is(DP_STATE_ENABLED)) {
+		dp_display_state_show("[not enabled]");
+		goto end;
+	}
+
+	dp_display_state_add(DP_STATE_HDCP_ABORTED);
+	cancel_delayed_work_sync(&dp->hdcp_cb_work);
+	if (dp_display_is_hdcp_enabled(dp) &&
+			status->hdcp_state != HDCP_STATE_INACTIVE) {
+		bool off = true;
+
+		if (dp_display_state_is(DP_STATE_SUSPENDED)) {
+			DP_DEBUG("Can't perform HDCP cleanup while suspended. Defer\n");
+			dp->hdcp_delayed_off = true;
+			goto clean;
+		}
+
+		flush_delayed_work(&dp->hdcp_cb_work);
+		if (dp->mst.mst_active) {
+			dp_display_hdcp_deregister_stream(dp,
+				dp_panel->stream_id);
+			for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
+				if (i != dp_panel->stream_id &&
+						dp->active_panels[i]) {
+					DP_DEBUG("Streams are still active. Skip disabling HDCP\n");
+					off = false;
+				}
+			}
+		}
+
+		if (off) {
+			if (dp->hdcp.ops->off)
+				dp->hdcp.ops->off(dp->hdcp.data);
+			dp_display_update_hdcp_status(dp, true);
+		}
+	}
+
+	dp_display_clear_colorspaces(dp_display);
+
+clean:
+	if (dp_panel->audio_supported)
+		dp_panel->audio->off(dp_panel->audio, false);
+
+	rc = dp_display_stream_pre_disable(dp, dp_panel);
+
+end:
+	mutex_unlock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+	return 0;
+}
+
+static int dp_display_disable(struct dp_display *dp_display, void *panel)
+{
+	int i;
+	struct dp_display_private *dp = NULL;
+	struct dp_panel *dp_panel = NULL;
+	struct dp_link_hdcp_status *status;
+
+	if (!dp_display || !panel) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	dp_panel = panel;
+	status = &dp->link->hdcp_status;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	mutex_lock(&dp->session_lock);
+
+	if (!dp_display_state_is(DP_STATE_ENABLED)) {
+		dp_display_state_show("[not enabled]");
+		goto end;
+	}
+
+	if (!dp_display_state_is(DP_STATE_READY)) {
+		dp_display_state_show("[not ready]");
+		goto end;
+	}
+
+	dp_display_stream_disable(dp, dp_panel);
+
+	dp_display_state_remove(DP_STATE_HDCP_ABORTED);
+	for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
+		if (dp->active_panels[i]) {
+			if (status->hdcp_state != HDCP_STATE_AUTHENTICATED)
+				queue_delayed_work(dp->wq, &dp->hdcp_cb_work,
+						HZ/4);
+			break;
+		}
+	}
+end:
+	mutex_unlock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+	return 0;
+}
+
+static int dp_request_irq(struct dp_display *dp_display)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	dp->irq = irq_of_parse_and_map(dp->pdev->dev.of_node, 0);
+	if (dp->irq < 0) {
+		rc = dp->irq;
+		DP_ERR("failed to get irq: %d\n", rc);
+		return rc;
+	}
+
+	rc = devm_request_irq(&dp->pdev->dev, dp->irq, dp_display_irq,
+		IRQF_TRIGGER_HIGH, "dp_display_isr", dp);
+	if (rc < 0) {
+		DP_ERR("failed to request IRQ%u: %d\n",
+				dp->irq, rc);
+		return rc;
+	}
+	disable_irq(dp->irq);
+
+	return 0;
+}
+
+static struct dp_debug *dp_get_debug(struct dp_display *dp_display)
+{
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		DP_ERR("invalid input\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	return dp->debug;
+}
+
+static int dp_display_unprepare(struct dp_display *dp_display, void *panel)
+{
+	struct dp_display_private *dp;
+	struct dp_panel *dp_panel = panel;
+	u32 flags = 0;
+
+	if (!dp_display || !panel) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	mutex_lock(&dp->session_lock);
+
+	/*
+	 * Check if the power off sequence was triggered
+	 * by a source initialated action like framework
+	 * reboot or suspend-resume but not from normal
+	 * hot plug. If connector is in MST mode, skip
+	 * powering down host as aux needs to be kept
+	 * alive to handle hot-plug sideband message.
+	 */
+	if (dp_display_is_ready(dp) &&
+		(dp_display_state_is(DP_STATE_SUSPENDED) ||
+		!dp->mst.mst_active))
+		flags |= DP_PANEL_SRC_INITIATED_POWER_DOWN;
+
+	if (dp->active_stream_cnt)
+		goto end;
+
+	if (flags & DP_PANEL_SRC_INITIATED_POWER_DOWN) {
+		dp->link->psm_config(dp->link, &dp->panel->link_info, true);
+		dp->debug->psm_enabled = true;
+
+		dp->ctrl->off(dp->ctrl);
+		dp_display_host_unready(dp);
+		dp_display_host_deinit(dp);
+		dp_display_state_add(DP_STATE_SRC_PWRDN);
+	}
+
+	dp_display_state_remove(DP_STATE_ENABLED);
+
+	dp->aux->state &= ~DP_STATE_CTRL_POWERED_ON;
+	dp->aux->state |= DP_STATE_CTRL_POWERED_OFF;
+
+	complete_all(&dp->notification_comp);
+
+	/* log this as it results from user action of cable dis-connection */
+	DP_INFO("[OK]\n");
+end:
+	mutex_lock(&dp->accounting_lock);
+	dp->tot_lm_blks_in_use -= dp_panel->max_lm;
+	dp_panel->max_lm = 0;
+	mutex_unlock(&dp->accounting_lock);
+	dp_panel->deinit(dp_panel, flags);
+	mutex_unlock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+
+	return 0;
+}
+
+static int dp_display_validate_link_clock(struct dp_display_private *dp,
+		struct drm_display_mode *mode, struct dp_display_mode dp_mode)
+{
+	u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
+	u32 mode_bpc = 0, tmds_clock = 0;
+	bool dsc_en;
+	int rate;
+	struct msm_compression_info *c_info = &dp_mode.timing.comp_info;
+
+	dsc_en = c_info->enabled;
+
+	if (dsc_en) {
+		mode_bpp = DSC_BPP(c_info->dsc_info.config);
+		mode_bpc = c_info->dsc_info.config.bits_per_component;
+	} else {
+		mode_bpp = dp_mode.timing.bpp;
+		mode_bpc = mode_bpp / 3;
+	}
+
+	mode_rate_khz = mode->clock * mode_bpp;
+	rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code);
+	tmds_clock = mode->clock * mode_bpc / 8;
+
+	/*
+	 * For a HBR 2 dongle, limit TMDS clock to ensure a max resolution
+	 * of 4k@30fps for each MST port
+	 */
+	if (dp->mst.mst_active && rate <= 540000 && tmds_clock > MAX_TMDS_CLOCK_HDMI_1_4) {
+		DP_DEBUG("Limit mode clock: %d kHz\n", mode->clock);
+		return -EPERM;
+	}
+
+	supported_rate_khz = dp->link->link_params.lane_count * rate * 8;
+
+	if (mode_rate_khz > supported_rate_khz) {
+		DP_DEBUG("mode_rate: %d kHz, supported_rate: %d kHz\n",
+				mode_rate_khz, supported_rate_khz);
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+static int dp_display_validate_pixel_clock(struct dp_display_mode dp_mode,
+		u32 max_pclk_khz)
+{
+	u32 pclk_khz = dp_mode.timing.widebus_en ?
+		(dp_mode.timing.pixel_clk_khz >> 1) :
+		dp_mode.timing.pixel_clk_khz;
+
+	if (pclk_khz > max_pclk_khz) {
+		DP_DEBUG("clk: %d kHz, max: %d kHz\n", pclk_khz, max_pclk_khz);
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+static int dp_display_validate_topology(struct dp_display_private *dp,
+		struct dp_panel *dp_panel, struct drm_display_mode *mode,
+		struct dp_display_mode *dp_mode,
+		const struct msm_resource_caps_info *avail_res)
+{
+	int rc;
+	struct msm_drm_private *priv = dp->priv;
+	const u32 dual = 2, quad = 4;
+	u32 num_lm = 0, num_dsc = 0, num_3dmux = 0;
+	bool dsc_capable = dp_mode->capabilities & DP_PANEL_CAPS_DSC;
+	u32 fps = dp_mode->timing.refresh_rate;
+	int avail_lm = 0;
+
+	mutex_lock(&dp->accounting_lock);
+
+	rc = msm_get_mixer_count(priv, mode, avail_res, &num_lm);
+	if (rc) {
+		DP_ERR("error getting mixer count. rc:%d\n", rc);
+		goto end;
+	}
+
+	/* Merge using DSC, if enabled */
+	if (dp_panel->dsc_en && dsc_capable) {
+		rc = msm_get_dsc_count(priv, mode->hdisplay, &num_dsc);
+		if (rc) {
+			DP_ERR("error getting dsc count. rc:%d\n", rc);
+			goto end;
+		}
+
+		num_dsc = max(num_lm, num_dsc);
+		if ((num_dsc > avail_res->num_lm) ||  (num_dsc > avail_res->num_dsc)) {
+			DP_DEBUG("mode %sx%d: not enough resources for dsc %d dsc_a:%d lm_a:%d\n",
+					mode->name, fps, num_dsc, avail_res->num_dsc,
+					avail_res->num_lm);
+			/* Clear DSC caps and retry */
+			dp_mode->capabilities &= ~DP_PANEL_CAPS_DSC;
+			rc = -EAGAIN;
+			goto end;
+		} else {
+			/* Only DSCMERGE is supported on DP */
+			num_lm = num_dsc;
+		}
+	}
+
+	if (!num_dsc && (num_lm == 2) && avail_res->num_3dmux) {
+		num_3dmux = 1;
+	}
+
+	avail_lm = avail_res->num_lm + avail_res->num_lm_in_use - dp->tot_lm_blks_in_use
+			+ dp_panel->max_lm;
+
+	if (num_lm > avail_lm) {
+		DP_DEBUG("mode %sx%d is invalid, not enough lm req:%d avail:%d\n",
+				mode->name, fps, num_lm, avail_lm);
+		rc = -EPERM;
+		goto end;
+	} else if (!num_dsc && (num_lm == dual && !num_3dmux)) {
+		DP_DEBUG("mode %sx%d is invalid, not enough 3dmux %d %d\n",
+				mode->name, fps, num_3dmux, avail_res->num_3dmux);
+		rc = -EPERM;
+		goto end;
+	} else if (num_lm == quad && num_dsc != quad)  {
+		DP_DEBUG("mode %sx%d is invalid, unsupported DP topology lm:%d dsc:%d\n",
+				mode->name, fps, num_lm, num_dsc);
+		rc = -EPERM;
+		goto end;
+	}
+
+	DP_DEBUG_V("mode %sx%d is valid, supported DP topology lm:%d dsc:%d 3dmux:%d\n",
+				mode->name, fps, num_lm, num_dsc, num_3dmux);
+
+	dp_mode->lm_count = num_lm;
+	rc = 0;
+
+end:
+	mutex_unlock(&dp->accounting_lock);
+	return rc;
+}
+
+static enum drm_mode_status dp_display_validate_mode(
+		struct dp_display *dp_display,
+		void *panel, struct drm_display_mode *mode,
+		const struct msm_resource_caps_info *avail_res)
+{
+	struct dp_display_private *dp;
+	struct dp_panel *dp_panel;
+	struct dp_debug *debug;
+	enum drm_mode_status mode_status = MODE_BAD;
+	struct dp_display_mode dp_mode;
+	int rc = 0;
+
+	if (!dp_display || !mode || !panel ||
+			!avail_res || !avail_res->max_mixer_width) {
+		DP_ERR("invalid params\n");
+		return mode_status;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	mutex_lock(&dp->session_lock);
+
+	dp_panel = panel;
+	if (!dp_panel->connector) {
+		DP_ERR("invalid connector\n");
+		goto end;
+	}
+
+	debug = dp->debug;
+	if (!debug)
+		goto end;
+
+	dp_display->convert_to_dp_mode(dp_display, panel, mode, &dp_mode);
+
+	/* As per spec, 640x480 mode should always be present as fail-safe */
+	if ((dp_mode.timing.h_active == 640) && (dp_mode.timing.v_active == 480) &&
+			(dp_mode.timing.pixel_clk_khz == 25175)) {
+		goto skip_validation;
+	}
+
+	rc = dp_display_validate_topology(dp, dp_panel, mode, &dp_mode, avail_res);
+	if (rc == -EAGAIN) {
+		dp_panel->convert_to_dp_mode(dp_panel, mode, &dp_mode);
+		rc = dp_display_validate_topology(dp, dp_panel, mode, &dp_mode, avail_res);
+	}
+
+	if (rc)
+		goto end;
+
+	rc = dp_display_validate_link_clock(dp, mode, dp_mode);
+	if (rc)
+		goto end;
+
+	rc = dp_display_validate_pixel_clock(dp_mode, dp_display->max_pclk_khz);
+	if (rc)
+		goto end;
+
+skip_validation:
+	mode_status = MODE_OK;
+
+	if (!avail_res->num_lm_in_use) {
+		mutex_lock(&dp->accounting_lock);
+		dp->tot_lm_blks_in_use -= dp_panel->max_lm;
+		dp_panel->max_lm = max(dp_panel->max_lm, dp_mode.lm_count);
+		dp->tot_lm_blks_in_use += dp_panel->max_lm;
+		mutex_unlock(&dp->accounting_lock);
+	}
+
+end:
+	mutex_unlock(&dp->session_lock);
+
+	DP_DEBUG_V("[%s clk:%d] mode is %s\n", mode->name, mode->clock,
+			(mode_status == MODE_OK) ? "valid" : "invalid");
+
+	return mode_status;
+}
+
+static int dp_display_get_available_dp_resources(struct dp_display *dp_display,
+		const struct msm_resource_caps_info *avail_res,
+		struct msm_resource_caps_info *max_dp_avail_res)
+{
+	if (!dp_display || !avail_res || !max_dp_avail_res) {
+		DP_ERR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	memcpy(max_dp_avail_res, avail_res,
+			sizeof(struct msm_resource_caps_info));
+
+	max_dp_avail_res->num_lm = min(avail_res->num_lm,
+			dp_display->max_mixer_count);
+	max_dp_avail_res->num_dsc = min(avail_res->num_dsc,
+			dp_display->max_dsc_count);
+
+	DP_DEBUG_V("max_lm:%d, avail_lm:%d, dp_avail_lm:%d\n",
+			dp_display->max_mixer_count, avail_res->num_lm,
+			max_dp_avail_res->num_lm);
+
+	DP_DEBUG_V("max_dsc:%d, avail_dsc:%d, dp_avail_dsc:%d\n",
+			dp_display->max_dsc_count, avail_res->num_dsc,
+			max_dp_avail_res->num_dsc);
+
+	return 0;
+}
+
+static int dp_display_get_modes(struct dp_display *dp, void *panel,
+	struct dp_display_mode *dp_mode)
+{
+	struct dp_display_private *dp_display;
+	struct dp_panel *dp_panel;
+	int ret = 0;
+
+	if (!dp || !panel) {
+		DP_ERR("invalid params\n");
+		return 0;
+	}
+
+	dp_panel = panel;
+	if (!dp_panel->connector) {
+		DP_ERR("invalid connector\n");
+		return 0;
+	}
+
+	dp_display = container_of(dp, struct dp_display_private, dp_display);
+
+	ret = dp_panel->get_modes(dp_panel, dp_panel->connector, dp_mode);
+	if (dp_mode->timing.pixel_clk_khz)
+		dp->max_pclk_khz = dp_mode->timing.pixel_clk_khz;
+	return ret;
+}
+
+static void dp_display_convert_to_dp_mode(struct dp_display *dp_display,
+		void *panel,
+		const struct drm_display_mode *drm_mode,
+		struct dp_display_mode *dp_mode)
+{
+	int rc;
+	struct dp_display_private *dp;
+	struct dp_panel *dp_panel;
+	u32 free_dsc_blks = 0, required_dsc_blks = 0, curr_dsc = 0, new_dsc = 0;
+
+	if (!dp_display || !drm_mode || !dp_mode || !panel) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	dp_panel = panel;
+
+	memset(dp_mode, 0, sizeof(*dp_mode));
+
+	if (dp_panel->dsc_en) {
+		free_dsc_blks = dp_display->max_dsc_count -
+				dp->tot_dsc_blks_in_use +
+				dp_panel->dsc_blks_in_use;
+		DP_DEBUG_V("Before: in_use:%d, max:%d, free:%d\n",
+				dp->tot_dsc_blks_in_use,
+				dp_display->max_dsc_count, free_dsc_blks);
+
+		rc = msm_get_dsc_count(dp->priv, drm_mode->hdisplay,
+				&required_dsc_blks);
+		if (rc) {
+			DP_ERR("error getting dsc count. rc:%d\n", rc);
+			return;
+		}
+
+		curr_dsc = dp_panel->dsc_blks_in_use;
+		dp->tot_dsc_blks_in_use -= dp_panel->dsc_blks_in_use;
+		dp_panel->dsc_blks_in_use = 0;
+
+		if (free_dsc_blks >= required_dsc_blks) {
+			dp_mode->capabilities |= DP_PANEL_CAPS_DSC;
+			new_dsc = max(curr_dsc, required_dsc_blks);
+			dp_panel->dsc_blks_in_use = new_dsc;
+			dp->tot_dsc_blks_in_use += new_dsc;
+		}
+
+		DP_DEBUG_V("After: in_use:%d, max:%d, free:%d, req:%d, caps:0x%x\n",
+				dp->tot_dsc_blks_in_use,
+				dp_display->max_dsc_count,
+				free_dsc_blks, required_dsc_blks,
+				dp_mode->capabilities);
+	}
+
+	dp_panel->convert_to_dp_mode(dp_panel, drm_mode, dp_mode);
+}
+
+static int dp_display_config_hdr(struct dp_display *dp_display, void *panel,
+			struct drm_msm_ext_hdr_metadata *hdr, bool dhdr_update)
+{
+	struct dp_panel *dp_panel;
+	struct sde_connector *sde_conn;
+	struct dp_display_private *dp;
+	u64 core_clk_rate;
+	bool flush_hdr;
+
+	if (!dp_display || !panel) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp_panel = panel;
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	sde_conn =  to_sde_connector(dp_panel->connector);
+
+	core_clk_rate = dp->power->clk_get_rate(dp->power, "core_clk");
+	if (!core_clk_rate) {
+		DP_ERR("invalid rate for core_clk\n");
+		return -EINVAL;
+	}
+
+	if (!dp_display_state_is(DP_STATE_ENABLED)) {
+		dp_display_state_show("[not enabled]");
+		return 0;
+	}
+
+	/*
+	 * In rare cases where HDR metadata is updated independently
+	 * flush the HDR metadata immediately instead of relying on
+	 * the colorspace
+	 */
+	flush_hdr = !sde_conn->colorspace_updated;
+
+	if (flush_hdr)
+		DP_DEBUG("flushing the HDR metadata\n");
+	else
+		DP_DEBUG("piggy-backing with colorspace\n");
+
+	return dp_panel->setup_hdr(dp_panel, hdr, dhdr_update,
+		core_clk_rate, flush_hdr);
+}
+
+static int dp_display_setup_colospace(struct dp_display *dp_display,
+		void *panel,
+		u32 colorspace)
+{
+	struct dp_panel *dp_panel;
+	struct dp_display_private *dp;
+
+	if (!dp_display || !panel) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	if (!dp_display_state_is(DP_STATE_ENABLED)) {
+		dp_display_state_show("[not enabled]");
+		return 0;
+	}
+
+	dp_panel = panel;
+
+	return dp_panel->set_colorspace(dp_panel, colorspace);
+}
+
+static int dp_display_create_workqueue(struct dp_display_private *dp)
+{
+	dp->wq = create_singlethread_workqueue("drm_dp");
+	if (IS_ERR_OR_NULL(dp->wq)) {
+		DP_ERR("Error creating wq\n");
+		return -EPERM;
+	}
+
+	INIT_DELAYED_WORK(&dp->hdcp_cb_work, dp_display_hdcp_cb_work);
+	INIT_WORK(&dp->connect_work, dp_display_connect_work);
+	INIT_WORK(&dp->attention_work, dp_display_attention_work);
+	INIT_WORK(&dp->disconnect_work, dp_display_disconnect_work);
+
+	return 0;
+}
+
+static int dp_display_bridge_internal_hpd(void *dev, bool hpd, bool hpd_irq)
+{
+	struct dp_display_private *dp = dev;
+	struct drm_device *drm_dev = dp->dp_display.drm_dev;
+
+	if (!drm_dev || !drm_dev->mode_config.poll_enabled)
+		return -EBUSY;
+
+	if (hpd_irq)
+		dp_display_mst_attention(dp);
+	else
+		dp->hpd->simulate_connect(dp->hpd, hpd);
+
+	return 0;
+}
+
+static int dp_display_init_aux_bridge(struct dp_display_private *dp)
+{
+	int rc = 0;
+	const char *phandle = "qcom,dp-aux-bridge";
+	struct device_node *bridge_node;
+
+	if (!dp->pdev->dev.of_node) {
+		pr_err("cannot find dev.of_node\n");
+		rc = -ENODEV;
+		goto end;
+	}
+
+	bridge_node = of_parse_phandle(dp->pdev->dev.of_node,
+			phandle, 0);
+	if (!bridge_node)
+		goto end;
+
+	dp->aux_bridge = of_dp_aux_find_bridge(bridge_node);
+	if (!dp->aux_bridge) {
+		pr_err("failed to find dp aux bridge\n");
+		rc = -EPROBE_DEFER;
+		goto end;
+	}
+
+	if (dp->aux_bridge->register_hpd &&
+			!(dp->aux_bridge->flag & DP_AUX_BRIDGE_HPD))
+		dp->aux_bridge->register_hpd(dp->aux_bridge,
+				dp_display_bridge_internal_hpd, dp);
+
+end:
+	return rc;
+}
+
+static int dp_display_mst_install(struct dp_display *dp_display,
+			struct dp_mst_drm_install_info *mst_install_info)
+{
+	struct dp_display_private *dp;
+
+	if (!dp_display || !mst_install_info) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+
+	if (!mst_install_info->cbs->hpd || !mst_install_info->cbs->hpd_irq) {
+		DP_ERR("invalid mst cbs\n");
+		return -EINVAL;
+	}
+
+	dp_display->dp_mst_prv_info = mst_install_info->dp_mst_prv_info;
+
+	if (!dp->parser->has_mst) {
+		DP_DEBUG("mst not enabled\n");
+		return -EPERM;
+	}
+
+	memcpy(&dp->mst.cbs, mst_install_info->cbs, sizeof(dp->mst.cbs));
+	dp->mst.drm_registered = true;
+
+	DP_MST_DEBUG("dp mst drm installed\n");
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+
+	return 0;
+}
+
+static int dp_display_mst_uninstall(struct dp_display *dp_display)
+{
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+
+	if (!dp->mst.drm_registered) {
+		DP_DEBUG("drm mst not registered\n");
+		return -EPERM;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private,
+				dp_display);
+	memset(&dp->mst.cbs, 0, sizeof(dp->mst.cbs));
+	dp->mst.drm_registered = false;
+
+	DP_MST_DEBUG("dp mst drm uninstalled\n");
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+
+	return 0;
+}
+
+static int dp_display_mst_connector_install(struct dp_display *dp_display,
+		struct drm_connector *connector)
+{
+	int rc = 0;
+	struct dp_panel_in panel_in;
+	struct dp_panel *dp_panel;
+	struct dp_display_private *dp;
+
+	if (!dp_display || !connector) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	mutex_lock(&dp->session_lock);
+
+	if (!dp->mst.drm_registered) {
+		DP_DEBUG("drm mst not registered\n");
+		rc = -EPERM;
+		goto end;
+	}
+
+	panel_in.dev = &dp->pdev->dev;
+	panel_in.aux = dp->aux;
+	panel_in.catalog = &dp->catalog->panel;
+	panel_in.link = dp->link;
+	panel_in.connector = connector;
+	panel_in.base_panel = dp->panel;
+	panel_in.parser = dp->parser;
+
+	dp_panel = dp_panel_get(&panel_in);
+	if (IS_ERR(dp_panel)) {
+		rc = PTR_ERR(dp_panel);
+		DP_ERR("failed to initialize panel, rc = %d\n", rc);
+		goto end;
+	}
+
+	dp_panel->audio = dp_audio_get(dp->pdev, dp_panel, &dp->catalog->audio);
+	if (IS_ERR(dp_panel->audio)) {
+		rc = PTR_ERR(dp_panel->audio);
+		DP_ERR("[mst] failed to initialize audio, rc = %d\n", rc);
+		dp_panel->audio = NULL;
+		goto end;
+	}
+
+	DP_MST_DEBUG("dp mst connector installed. conn:%d\n",
+			connector->base.id);
+
+end:
+	mutex_unlock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, rc);
+
+	return rc;
+}
+
+static int dp_display_mst_connector_uninstall(struct dp_display *dp_display,
+			struct drm_connector *connector)
+{
+	int rc = 0;
+	struct sde_connector *sde_conn;
+	struct dp_panel *dp_panel;
+	struct dp_display_private *dp;
+	struct dp_audio *audio = NULL;
+
+	if (!dp_display || !connector) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
+	mutex_lock(&dp->session_lock);
+
+	if (!dp->mst.drm_registered) {
+		DP_DEBUG("drm mst not registered\n");
+		mutex_unlock(&dp->session_lock);
+		return -EPERM;
+	}
+
+	sde_conn = to_sde_connector(connector);
+	if (!sde_conn->drv_panel) {
+		DP_ERR("invalid panel for connector:%d\n", connector->base.id);
+		mutex_unlock(&dp->session_lock);
+		return -EINVAL;
+	}
+
+	dp_panel = sde_conn->drv_panel;
+
+	/* Make a copy of audio structure to call into dp_audio_put later */
+	audio = dp_panel->audio;
+	dp_panel_put(dp_panel);
+
+	DP_MST_DEBUG("dp mst connector uninstalled. conn:%d\n",
+			connector->base.id);
+
+	mutex_unlock(&dp->session_lock);
+
+	dp_audio_put(audio);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+
+	return rc;
+}
+
+static int dp_display_mst_connector_update_edid(struct dp_display *dp_display,
+			struct drm_connector *connector,
+			struct edid *edid)
+{
+	int rc = 0;
+	struct sde_connector *sde_conn;
+	struct dp_panel *dp_panel;
+	struct dp_display_private *dp;
+
+	if (!dp_display || !connector || !edid) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	if (!dp->mst.drm_registered) {
+		DP_DEBUG("drm mst not registered\n");
+		return -EPERM;
+	}
+
+	sde_conn = to_sde_connector(connector);
+	if (!sde_conn->drv_panel) {
+		DP_ERR("invalid panel for connector:%d\n", connector->base.id);
+		return -EINVAL;
+	}
+
+	dp_panel = sde_conn->drv_panel;
+	rc = dp_panel->update_edid(dp_panel, edid);
+
+	DP_MST_DEBUG("dp mst connector:%d edid updated. mode_cnt:%d\n",
+			connector->base.id, rc);
+
+	return rc;
+}
+
+static int dp_display_update_pps(struct dp_display *dp_display,
+		struct drm_connector *connector, char *pps_cmd)
+{
+	struct sde_connector *sde_conn;
+	struct dp_panel *dp_panel;
+	struct dp_display_private *dp;
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	sde_conn = to_sde_connector(connector);
+	if (!sde_conn->drv_panel) {
+		DP_ERR("invalid panel for connector:%d\n", connector->base.id);
+		return -EINVAL;
+	}
+
+	if (!dp_display_state_is(DP_STATE_ENABLED)) {
+		dp_display_state_show("[not enabled]");
+		return 0;
+	}
+
+	dp_panel = sde_conn->drv_panel;
+	dp_panel->update_pps(dp_panel, pps_cmd);
+	return 0;
+}
+
+static int dp_display_mst_connector_update_link_info(
+			struct dp_display *dp_display,
+			struct drm_connector *connector)
+{
+	int rc = 0;
+	struct sde_connector *sde_conn;
+	struct dp_panel *dp_panel;
+	struct dp_display_private *dp;
+
+	if (!dp_display || !connector) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	if (!dp->mst.drm_registered) {
+		DP_DEBUG("drm mst not registered\n");
+		return -EPERM;
+	}
+
+	sde_conn = to_sde_connector(connector);
+	if (!sde_conn->drv_panel) {
+		DP_ERR("invalid panel for connector:%d\n", connector->base.id);
+		return -EINVAL;
+	}
+
+	dp_panel = sde_conn->drv_panel;
+
+	memcpy(dp_panel->dpcd, dp->panel->dpcd,
+			DP_RECEIVER_CAP_SIZE + 1);
+	memcpy(dp_panel->dsc_dpcd, dp->panel->dsc_dpcd,
+			DP_RECEIVER_DSC_CAP_SIZE + 1);
+	memcpy(&dp_panel->link_info, &dp->panel->link_info,
+			sizeof(dp_panel->link_info));
+
+	DP_MST_DEBUG("dp mst connector:%d link info updated\n",
+		connector->base.id);
+
+	return rc;
+}
+
+static int dp_display_mst_get_fixed_topology_port(
+			struct dp_display *dp_display,
+			u32 strm_id, u32 *port_num)
+{
+	struct dp_display_private *dp;
+	u32 port;
+
+	if (!dp_display) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	if (strm_id >= DP_STREAM_MAX) {
+		DP_ERR("invalid stream id:%d\n", strm_id);
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	port = dp->parser->mst_fixed_port[strm_id];
+
+	if (!port || port > 255)
+		return -ENOENT;
+
+	if (port_num)
+		*port_num = port;
+
+	return 0;
+}
+
+static int dp_display_get_mst_caps(struct dp_display *dp_display,
+			struct dp_mst_caps *mst_caps)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dp_display || !mst_caps) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	mst_caps->has_mst = dp->parser->has_mst;
+	mst_caps->max_streams_supported = (mst_caps->has_mst) ? 2 : 0;
+	mst_caps->max_dpcd_transaction_bytes = (mst_caps->has_mst) ? 16 : 0;
+	mst_caps->drm_aux = dp->aux->drm_aux;
+
+	return rc;
+}
+
+static void dp_display_wakeup_phy_layer(struct dp_display *dp_display,
+		bool wakeup)
+{
+	struct dp_display_private *dp;
+	struct dp_hpd *hpd;
+
+	if (!dp_display) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	if (!dp->mst.drm_registered) {
+		DP_DEBUG("drm mst not registered\n");
+		return;
+	}
+
+	hpd = dp->hpd;
+	if (hpd && hpd->wakeup_phy)
+		hpd->wakeup_phy(hpd, wakeup);
+}
+
+static int dp_display_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!pdev || !pdev->dev.of_node) {
+		DP_ERR("pdev not found\n");
+		rc = -ENODEV;
+		goto bail;
+	}
+
+	dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
+	if (!dp) {
+		rc = -ENOMEM;
+		goto bail;
+	}
+
+	init_completion(&dp->notification_comp);
+	init_completion(&dp->attention_comp);
+
+	dp->pdev = pdev;
+	dp->name = "drm_dp";
+
+	memset(&dp->mst, 0, sizeof(dp->mst));
+
+	rc = dp_display_init_aux_bridge(dp);
+	if (rc)
+		goto error;
+
+	rc = dp_display_create_workqueue(dp);
+	if (rc) {
+		DP_ERR("Failed to create workqueue\n");
+		goto error;
+	}
+
+	platform_set_drvdata(pdev, dp);
+
+	g_dp_display = &dp->dp_display;
+
+	g_dp_display->dp_ipc_log = ipc_log_context_create(DRM_DP_IPC_NUM_PAGES, "drm_dp", 0);
+	if (!g_dp_display->dp_ipc_log)
+		DP_WARN("Error in creating ipc_log_context for drm_dp\n");
+	g_dp_display->dp_aux_ipc_log = ipc_log_context_create(DRM_DP_IPC_NUM_PAGES, "drm_dp_aux",
+			0);
+	if (!g_dp_display->dp_aux_ipc_log)
+		DP_WARN("Error in creating ipc_log_context for drm_dp_aux\n");
+
+	g_dp_display->enable        = dp_display_enable;
+	g_dp_display->post_enable   = dp_display_post_enable;
+	g_dp_display->pre_disable   = dp_display_pre_disable;
+	g_dp_display->disable       = dp_display_disable;
+	g_dp_display->set_mode      = dp_display_set_mode;
+	g_dp_display->validate_mode = dp_display_validate_mode;
+	g_dp_display->get_modes     = dp_display_get_modes;
+	g_dp_display->prepare       = dp_display_prepare;
+	g_dp_display->unprepare     = dp_display_unprepare;
+	g_dp_display->request_irq   = dp_request_irq;
+	g_dp_display->get_debug     = dp_get_debug;
+	g_dp_display->post_open     = NULL;
+	g_dp_display->post_init     = dp_display_post_init;
+	g_dp_display->config_hdr    = dp_display_config_hdr;
+	g_dp_display->mst_install   = dp_display_mst_install;
+	g_dp_display->mst_uninstall = dp_display_mst_uninstall;
+	g_dp_display->mst_connector_install = dp_display_mst_connector_install;
+	g_dp_display->mst_connector_uninstall =
+					dp_display_mst_connector_uninstall;
+	g_dp_display->mst_connector_update_edid =
+					dp_display_mst_connector_update_edid;
+	g_dp_display->mst_connector_update_link_info =
+				dp_display_mst_connector_update_link_info;
+	g_dp_display->get_mst_caps = dp_display_get_mst_caps;
+	g_dp_display->set_stream_info = dp_display_set_stream_info;
+	g_dp_display->update_pps = dp_display_update_pps;
+	g_dp_display->convert_to_dp_mode = dp_display_convert_to_dp_mode;
+	g_dp_display->mst_get_fixed_topology_port =
+					dp_display_mst_get_fixed_topology_port;
+	g_dp_display->wakeup_phy_layer =
+					dp_display_wakeup_phy_layer;
+	g_dp_display->set_colorspace = dp_display_setup_colospace;
+	g_dp_display->get_available_dp_resources =
+					dp_display_get_available_dp_resources;
+	g_dp_display->clear_reservation = dp_display_clear_reservation;
+	g_dp_display->get_mst_pbn_div = dp_display_get_mst_pbn_div;
+
+	rc = component_add(&pdev->dev, &dp_display_comp_ops);
+	if (rc) {
+		DP_ERR("component add failed, rc=%d\n", rc);
+		goto error;
+	}
+
+	return 0;
+error:
+	devm_kfree(&pdev->dev, dp);
+bail:
+	return rc;
+}
+
+int dp_display_get_displays(void **displays, int count)
+{
+	if (!displays) {
+		DP_ERR("invalid data\n");
+		return -EINVAL;
+	}
+
+	if (count != 1) {
+		DP_ERR("invalid number of displays\n");
+		return -EINVAL;
+	}
+
+	displays[0] = g_dp_display;
+	return count;
+}
+
+int dp_display_get_num_of_displays(void)
+{
+	if (!g_dp_display)
+		return 0;
+
+	return 1;
+}
+
+int dp_display_get_num_of_streams(void)
+{
+	return DP_STREAM_MAX;
+}
+
+static void dp_display_set_mst_state(void *dp_display,
+		enum dp_drv_state mst_state)
+{
+	struct dp_display_private *dp;
+
+	if (!g_dp_display) {
+		DP_DEBUG("dp display not initialized\n");
+		return;
+	}
+
+	dp = container_of(g_dp_display, struct dp_display_private, dp_display);
+	SDE_EVT32_EXTERNAL(mst_state, dp->mst.mst_active);
+
+	if (dp->mst.mst_active && dp->mst.cbs.set_drv_state)
+		dp->mst.cbs.set_drv_state(g_dp_display, mst_state);
+}
+
+static int dp_display_remove(struct platform_device *pdev)
+{
+	struct dp_display_private *dp;
+
+	if (!pdev)
+		return -EINVAL;
+
+	dp = platform_get_drvdata(pdev);
+
+	dp_display_deinit_sub_modules(dp);
+
+	if (dp->wq)
+		destroy_workqueue(dp->wq);
+
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(&pdev->dev, dp);
+
+	if (g_dp_display->dp_ipc_log) {
+		ipc_log_context_destroy(g_dp_display->dp_ipc_log);
+		g_dp_display->dp_ipc_log = NULL;
+	}
+
+	if (g_dp_display->dp_aux_ipc_log) {
+		ipc_log_context_destroy(g_dp_display->dp_aux_ipc_log);
+		g_dp_display->dp_aux_ipc_log = NULL;
+	}
+
+	return 0;
+}
+
+static int dp_pm_prepare(struct device *dev)
+{
+	struct dp_display_private *dp = container_of(g_dp_display,
+			struct dp_display_private, dp_display);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
+	mutex_lock(&dp->session_lock);
+	dp_display_set_mst_state(g_dp_display, PM_SUSPEND);
+
+	/*
+	 * There are a few instances where the DP is hotplugged when the device
+	 * is in PM suspend state. After hotplug, it is observed the device
+	 * enters and exits the PM suspend multiple times while aux transactions
+	 * are taking place. This may sometimes cause an unclocked register
+	 * access error. So, abort aux transactions when such a situation
+	 * arises i.e. when DP is connected but display not enabled yet.
+	 */
+	if (dp_display_state_is(DP_STATE_CONNECTED) &&
+			!dp_display_state_is(DP_STATE_ENABLED)) {
+		dp->aux->abort(dp->aux, true);
+		dp->ctrl->abort(dp->ctrl, true);
+	}
+
+	dp_display_state_add(DP_STATE_SUSPENDED);
+	mutex_unlock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+
+	return 0;
+}
+
+static void dp_pm_complete(struct device *dev)
+{
+	struct dp_display_private *dp = container_of(g_dp_display,
+			struct dp_display_private, dp_display);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
+	mutex_lock(&dp->session_lock);
+	dp_display_set_mst_state(g_dp_display, PM_DEFAULT);
+
+	/*
+	 * There are multiple PM suspend entry and exits observed before
+	 * the connect uevent is issued to userspace. The aux transactions are
+	 * aborted during PM suspend entry in dp_pm_prepare to prevent unclocked
+	 * register access. On PM suspend exit, there will be no host_init call
+	 * to reset the abort flags for ctrl and aux incase DP is connected
+	 * but display not enabled. So, resetting abort flags for aux and ctrl.
+	 */
+	if (dp_display_state_is(DP_STATE_CONNECTED) &&
+			!dp_display_state_is(DP_STATE_ENABLED)) {
+		dp->aux->abort(dp->aux, false);
+		dp->ctrl->abort(dp->ctrl, false);
+	}
+
+	dp_display_state_remove(DP_STATE_SUSPENDED);
+	mutex_unlock(&dp->session_lock);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
+}
+
+void *get_ipc_log_context(void)
+{
+	if (g_dp_display && g_dp_display->dp_ipc_log)
+		return g_dp_display->dp_ipc_log;
+	return NULL;
+}
+
+static const struct dev_pm_ops dp_pm_ops = {
+	.prepare = dp_pm_prepare,
+	.complete = dp_pm_complete,
+};
+
+static struct platform_driver dp_display_driver = {
+	.probe  = dp_display_probe,
+	.remove = dp_display_remove,
+	.driver = {
+		.name = "msm-dp-display",
+		.of_match_table = dp_dt_match,
+		.suppress_bind_attrs = true,
+		.pm = &dp_pm_ops,
+	},
+};
+
+void __init dp_display_register(void)
+{
+
+	platform_driver_register(&dp_display_driver);
+}
+
+void __exit dp_display_unregister(void)
+{
+	platform_driver_unregister(&dp_display_driver);
+}

+ 144 - 0
qcom/opensource/display-drivers/msm/dp/dp_display.h

@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_DISPLAY_H_
+#define _DP_DISPLAY_H_
+
+#include <linux/list.h>
+#include <drm/sde_drm.h>
+
+#include "dp_panel.h"
+
+
+enum dp_drv_state {
+	PM_DEFAULT,
+	PM_SUSPEND,
+};
+
+struct dp_mst_drm_cbs {
+	void (*hpd)(void *display, bool hpd_status);
+	void (*hpd_irq)(void *display);
+	void (*set_drv_state)(void *dp_display,
+			enum dp_drv_state mst_state);
+	int (*set_mgr_state)(void *dp_display, bool state);
+	void (*set_mst_mode_params)(void *dp_display, struct dp_display_mode *mode);
+};
+
+struct dp_mst_drm_install_info {
+	void *dp_mst_prv_info;
+	const struct dp_mst_drm_cbs *cbs;
+};
+
+struct dp_mst_caps {
+	bool has_mst;
+	u32 max_streams_supported;
+	u32 max_dpcd_transaction_bytes;
+	struct drm_dp_aux *drm_aux;
+};
+
+struct dp_display {
+	struct drm_device *drm_dev;
+	struct dp_bridge *bridge;
+	struct drm_connector *base_connector;
+	void *base_dp_panel;
+	bool is_sst_connected;
+	bool is_mst_supported;
+	bool dsc_cont_pps;
+	u32 max_pclk_khz;
+	void *dp_mst_prv_info;
+	u32 max_mixer_count;
+	u32 max_dsc_count;
+	void *dp_ipc_log;
+	void *dp_aux_ipc_log;
+
+	int (*enable)(struct dp_display *dp_display, void *panel);
+	int (*post_enable)(struct dp_display *dp_display, void *panel);
+
+	int (*pre_disable)(struct dp_display *dp_display, void *panel);
+	int (*disable)(struct dp_display *dp_display, void *panel);
+
+	int (*set_mode)(struct dp_display *dp_display, void *panel,
+			struct dp_display_mode *mode);
+	enum drm_mode_status (*validate_mode)(struct dp_display *dp_display,
+			void *panel, struct drm_display_mode *mode,
+			const struct msm_resource_caps_info *avail_res);
+	int (*get_modes)(struct dp_display *dp_display, void *panel,
+		struct dp_display_mode *dp_mode);
+	int (*prepare)(struct dp_display *dp_display, void *panel);
+	int (*unprepare)(struct dp_display *dp_display, void *panel);
+	int (*request_irq)(struct dp_display *dp_display);
+	struct dp_debug *(*get_debug)(struct dp_display *dp_display);
+	void (*post_open)(struct dp_display *dp_display);
+	int (*config_hdr)(struct dp_display *dp_display, void *panel,
+				struct drm_msm_ext_hdr_metadata *hdr_meta,
+				bool dhdr_update);
+	int (*set_colorspace)(struct dp_display *dp_display, void *panel,
+				u32 colorspace);
+	int (*post_init)(struct dp_display *dp_display);
+	int (*mst_install)(struct dp_display *dp_display,
+			struct dp_mst_drm_install_info *mst_install_info);
+	int (*mst_uninstall)(struct dp_display *dp_display);
+	int (*mst_connector_install)(struct dp_display *dp_display,
+			struct drm_connector *connector);
+	int (*mst_connector_uninstall)(struct dp_display *dp_display,
+			struct drm_connector *connector);
+	int (*mst_connector_update_edid)(struct dp_display *dp_display,
+			struct drm_connector *connector,
+			struct edid *edid);
+	int (*mst_connector_update_link_info)(struct dp_display *dp_display,
+			struct drm_connector *connector);
+	int (*mst_get_fixed_topology_port)(struct dp_display *dp_display,
+			u32 strm_id, u32 *port_num);
+	int (*get_mst_caps)(struct dp_display *dp_display,
+			struct dp_mst_caps *mst_caps);
+	int (*set_stream_info)(struct dp_display *dp_display, void *panel,
+			u32 strm_id, u32 start_slot, u32 num_slots, u32 pbn,
+			int vcpi);
+	void (*convert_to_dp_mode)(struct dp_display *dp_display, void *panel,
+			const struct drm_display_mode *drm_mode,
+			struct dp_display_mode *dp_mode);
+	int (*update_pps)(struct dp_display *dp_display,
+			struct drm_connector *connector, char *pps_cmd);
+	void (*wakeup_phy_layer)(struct dp_display *dp_display,
+			bool wakeup);
+	int (*get_available_dp_resources)(struct dp_display *dp_display,
+			const struct msm_resource_caps_info *avail_res,
+			struct msm_resource_caps_info *max_dp_avail_res);
+	void (*clear_reservation)(struct dp_display *dp, struct dp_panel *panel);
+	int (*get_mst_pbn_div)(struct dp_display *dp);
+};
+
+void *get_ipc_log_context(void);
+
+#if IS_ENABLED(CONFIG_DRM_MSM_DP)
+int dp_display_get_num_of_displays(void);
+int dp_display_get_displays(void **displays, int count);
+int dp_display_get_num_of_streams(void);
+int dp_display_mmrm_callback(struct mmrm_client_notifier_data *notifier_data);
+#else
+static inline int dp_display_get_num_of_displays(void)
+{
+	return 0;
+}
+static inline int dp_display_get_displays(void **displays, int count)
+{
+	return 0;
+}
+static inline int dp_display_get_num_of_streams(void)
+{
+	return 0;
+}
+static inline int dp_connector_update_pps(struct drm_connector *connector,
+		char *pps_cmd, void *display)
+{
+	return 0;
+}
+static inline int dp_display_mmrm_callback(struct mmrm_client_notifier_data *notifier_data)
+{
+	return 0;
+}
+#endif /* CONFIG_DRM_MSM_DP */
+#endif /* _DP_DISPLAY_H_ */

+ 794 - 0
qcom/opensource/display-drivers/msm/dp/dp_drm.c

@@ -0,0 +1,794 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_crtc.h>
+
+#include "msm_drv.h"
+#include "msm_kms.h"
+#include "sde_connector.h"
+#include "dp_drm.h"
+#include "dp_mst_drm.h"
+#include "dp_debug.h"
+
+#define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)
+
+#define to_dp_bridge(x)     container_of((x), struct dp_bridge, base)
+
+void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
+				struct drm_display_mode *drm_mode)
+{
+	u32 flags = 0;
+
+	memset(drm_mode, 0, sizeof(*drm_mode));
+
+	drm_mode->hdisplay = dp_mode->timing.h_active;
+	drm_mode->hsync_start = drm_mode->hdisplay +
+				dp_mode->timing.h_front_porch;
+	drm_mode->hsync_end = drm_mode->hsync_start +
+			      dp_mode->timing.h_sync_width;
+	drm_mode->htotal = drm_mode->hsync_end + dp_mode->timing.h_back_porch;
+	drm_mode->hskew = dp_mode->timing.h_skew;
+
+	drm_mode->vdisplay = dp_mode->timing.v_active;
+	drm_mode->vsync_start = drm_mode->vdisplay +
+				dp_mode->timing.v_front_porch;
+	drm_mode->vsync_end = drm_mode->vsync_start +
+			      dp_mode->timing.v_sync_width;
+	drm_mode->vtotal = drm_mode->vsync_end + dp_mode->timing.v_back_porch;
+
+	drm_mode->clock = dp_mode->timing.pixel_clk_khz;
+
+	if (dp_mode->timing.h_active_low)
+		flags |= DRM_MODE_FLAG_NHSYNC;
+	else
+		flags |= DRM_MODE_FLAG_PHSYNC;
+
+	if (dp_mode->timing.v_active_low)
+		flags |= DRM_MODE_FLAG_NVSYNC;
+	else
+		flags |= DRM_MODE_FLAG_PVSYNC;
+
+	drm_mode->flags = flags;
+
+	drm_mode->type = 0x48;
+	drm_mode_set_name(drm_mode);
+}
+
+static int dp_bridge_attach(struct drm_bridge *dp_bridge,
+				enum drm_bridge_attach_flags flags)
+{
+	struct dp_bridge *bridge = to_dp_bridge(dp_bridge);
+
+	if (!dp_bridge) {
+		DP_ERR("Invalid params\n");
+		return -EINVAL;
+	}
+
+	DP_DEBUG("[%d] attached\n", bridge->id);
+
+	return 0;
+}
+
+static void dp_bridge_pre_enable(struct drm_bridge *drm_bridge)
+{
+	int rc = 0;
+	struct dp_bridge *bridge;
+	struct dp_display *dp;
+
+	if (!drm_bridge) {
+		DP_ERR("Invalid params\n");
+		return;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	dp = bridge->display;
+
+	if (!bridge->connector) {
+		DP_ERR("Invalid connector\n");
+		return;
+	}
+
+	if (!bridge->dp_panel) {
+		DP_ERR("Invalid dp_panel\n");
+		return;
+	}
+
+	/* By this point mode should have been validated through mode_fixup */
+	rc = dp->set_mode(dp, bridge->dp_panel, &bridge->dp_mode);
+	if (rc) {
+		DP_ERR("[%d] failed to perform a mode set, rc=%d\n",
+		       bridge->id, rc);
+		return;
+	}
+
+	rc = dp->prepare(dp, bridge->dp_panel);
+	if (rc) {
+		DP_ERR("[%d] DP display prepare failed, rc=%d\n",
+		       bridge->id, rc);
+		return;
+	}
+
+	/* for SST force stream id, start slot and total slots to 0 */
+	dp->set_stream_info(dp, bridge->dp_panel, 0, 0, 0, 0, 0);
+
+	rc = dp->enable(dp, bridge->dp_panel);
+	if (rc)
+		DP_ERR("[%d] DP display enable failed, rc=%d\n",
+		       bridge->id, rc);
+}
+
+static void dp_bridge_enable(struct drm_bridge *drm_bridge)
+{
+	int rc = 0;
+	struct dp_bridge *bridge;
+	struct dp_display *dp;
+
+	if (!drm_bridge) {
+		DP_ERR("Invalid params\n");
+		return;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	if (!bridge->connector) {
+		DP_ERR("Invalid connector\n");
+		return;
+	}
+
+	if (!bridge->dp_panel) {
+		DP_ERR("Invalid dp_panel\n");
+		return;
+	}
+
+	dp = bridge->display;
+
+	rc = dp->post_enable(dp, bridge->dp_panel);
+	if (rc)
+		DP_ERR("[%d] DP display post enable failed, rc=%d\n",
+		       bridge->id, rc);
+}
+
+static void dp_bridge_disable(struct drm_bridge *drm_bridge)
+{
+	int rc = 0;
+	struct dp_bridge *bridge;
+	struct dp_display *dp;
+
+	if (!drm_bridge) {
+		DP_ERR("Invalid params\n");
+		return;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	if (!bridge->connector) {
+		DP_ERR("Invalid connector\n");
+		return;
+	}
+
+	if (!bridge->dp_panel) {
+		DP_ERR("Invalid dp_panel\n");
+		return;
+	}
+
+	dp = bridge->display;
+
+	if (!dp) {
+		DP_ERR("dp is null\n");
+		return;
+	}
+
+	if (dp)
+		sde_connector_helper_bridge_disable(bridge->connector);
+
+	rc = dp->pre_disable(dp, bridge->dp_panel);
+	if (rc) {
+		DP_ERR("[%d] DP display pre disable failed, rc=%d\n",
+		       bridge->id, rc);
+	}
+}
+
+static void dp_bridge_post_disable(struct drm_bridge *drm_bridge)
+{
+	int rc = 0;
+	struct dp_bridge *bridge;
+	struct dp_display *dp;
+
+	if (!drm_bridge) {
+		DP_ERR("Invalid params\n");
+		return;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	if (!bridge->connector) {
+		DP_ERR("Invalid connector\n");
+		return;
+	}
+
+	if (!bridge->dp_panel) {
+		DP_ERR("Invalid dp_panel\n");
+		return;
+	}
+
+	dp = bridge->display;
+
+	rc = dp->disable(dp, bridge->dp_panel);
+	if (rc) {
+		DP_ERR("[%d] DP display disable failed, rc=%d\n",
+		       bridge->id, rc);
+		return;
+	}
+
+	rc = dp->unprepare(dp, bridge->dp_panel);
+	if (rc) {
+		DP_ERR("[%d] DP display unprepare failed, rc=%d\n",
+		       bridge->id, rc);
+		return;
+	}
+}
+
+static void dp_bridge_mode_set(struct drm_bridge *drm_bridge,
+				const struct drm_display_mode *mode,
+				const struct drm_display_mode *adjusted_mode)
+{
+	struct dp_bridge *bridge;
+	struct dp_display *dp;
+
+	if (!drm_bridge || !mode || !adjusted_mode) {
+		DP_ERR("Invalid params\n");
+		return;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	if (!bridge->connector) {
+		DP_ERR("Invalid connector\n");
+		return;
+	}
+
+	if (!bridge->dp_panel) {
+		DP_ERR("Invalid dp_panel\n");
+		return;
+	}
+
+	dp = bridge->display;
+
+	dp->convert_to_dp_mode(dp, bridge->dp_panel, adjusted_mode,
+			&bridge->dp_mode);
+
+	dp->clear_reservation(dp, bridge->dp_panel);
+}
+
+static bool dp_bridge_mode_fixup(struct drm_bridge *drm_bridge,
+				  const struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	bool ret = true;
+	struct dp_display_mode dp_mode;
+	struct dp_bridge *bridge;
+	struct dp_display *dp;
+
+	if (!drm_bridge || !mode || !adjusted_mode) {
+		DP_ERR("Invalid params\n");
+		ret = false;
+		goto end;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	if (!bridge->connector) {
+		DP_ERR("Invalid connector\n");
+		ret = false;
+		goto end;
+	}
+
+	if (!bridge->dp_panel) {
+		DP_ERR("Invalid dp_panel\n");
+		ret = false;
+		goto end;
+	}
+
+	dp = bridge->display;
+
+	dp->convert_to_dp_mode(dp, bridge->dp_panel, mode, &dp_mode);
+	dp->clear_reservation(dp, bridge->dp_panel);
+	convert_to_drm_mode(&dp_mode, adjusted_mode);
+end:
+	return ret;
+}
+
+static const struct drm_bridge_funcs dp_bridge_ops = {
+	.attach       = dp_bridge_attach,
+	.mode_fixup   = dp_bridge_mode_fixup,
+	.pre_enable   = dp_bridge_pre_enable,
+	.enable       = dp_bridge_enable,
+	.disable      = dp_bridge_disable,
+	.post_disable = dp_bridge_post_disable,
+	.mode_set     = dp_bridge_mode_set,
+};
+
+int dp_connector_add_custom_mode(struct drm_connector *conn, struct dp_display_mode *dp_mode)
+{
+	struct drm_display_mode *m, drm_mode;
+
+	memset(&drm_mode, 0x0, sizeof(drm_mode));
+	convert_to_drm_mode(dp_mode, &drm_mode);
+	m = drm_mode_duplicate(conn->dev, &drm_mode);
+	if (!m) {
+		DP_ERR("failed to add mode %ux%u\n", drm_mode.hdisplay, drm_mode.vdisplay);
+		return 0;
+	}
+	m->width_mm = conn->display_info.width_mm;
+	m->height_mm = conn->display_info.height_mm;
+	drm_mode_probed_add(conn, m);
+
+	return 1;
+}
+
+void init_failsafe_mode(struct dp_display_mode *dp_mode)
+{
+	static const struct dp_panel_info fail_safe = {
+		.h_active = 640,
+		.v_active = 480,
+		.h_back_porch = 48,
+		.h_front_porch = 16,
+		.h_sync_width = 96,
+		.h_active_low = 1,
+		.v_back_porch = 33,
+		.v_front_porch = 10,
+		.v_sync_width = 2,
+		.v_active_low = 1,
+		.h_skew = 0,
+		.refresh_rate = 60,
+		.pixel_clk_khz = 25175,
+		.bpp = 24,
+		.widebus_en = true,
+	};
+
+	memcpy(&dp_mode->timing, &fail_safe, sizeof(fail_safe));
+}
+
+int dp_connector_config_hdr(struct drm_connector *connector, void *display,
+	struct sde_connector_state *c_state)
+{
+	struct dp_display *dp = display;
+	struct sde_connector *sde_conn;
+
+	if (!display || !c_state || !connector) {
+		DP_ERR("invalid params\n");
+		return -EINVAL;
+	}
+
+	sde_conn = to_sde_connector(connector);
+	if (!sde_conn->drv_panel) {
+		DP_ERR("invalid dp panel\n");
+		return -EINVAL;
+	}
+
+	return dp->config_hdr(dp, sde_conn->drv_panel, &c_state->hdr_meta,
+			c_state->dyn_hdr_meta.dynamic_hdr_update);
+}
+
+int dp_connector_set_colorspace(struct drm_connector *connector,
+	void *display)
+{
+	struct dp_display *dp_display = display;
+	struct sde_connector *sde_conn;
+
+	if (!dp_display || !connector)
+		return -EINVAL;
+
+	sde_conn = to_sde_connector(connector);
+	if (!sde_conn->drv_panel) {
+		pr_err("invalid dp panel\n");
+		return -EINVAL;
+	}
+
+	return dp_display->set_colorspace(dp_display,
+		sde_conn->drv_panel, connector->state->colorspace);
+}
+
+int dp_connector_post_init(struct drm_connector *connector, void *display)
+{
+	int rc;
+	struct dp_display *dp_display = display;
+	struct sde_connector *sde_conn;
+
+	if (!dp_display || !connector)
+		return -EINVAL;
+
+	dp_display->base_connector = connector;
+	dp_display->bridge->connector = connector;
+
+	if (dp_display->post_init) {
+		rc = dp_display->post_init(dp_display);
+		if (rc)
+			goto end;
+	}
+
+	sde_conn = to_sde_connector(connector);
+	dp_display->bridge->dp_panel = sde_conn->drv_panel;
+
+	rc = dp_mst_init(dp_display);
+
+	if (dp_display->dsc_cont_pps)
+		sde_conn->ops.update_pps = NULL;
+
+end:
+	return rc;
+}
+
+int dp_connector_get_mode_info(struct drm_connector *connector,
+		const struct drm_display_mode *drm_mode,
+		struct msm_sub_mode *sub_mode,
+		struct msm_mode_info *mode_info,
+		void *display, const struct msm_resource_caps_info *avail_res)
+{
+	const u32 single_intf = 1;
+	const u32 no_enc = 0;
+	struct msm_display_topology *topology;
+	struct sde_connector *sde_conn;
+	struct dp_panel *dp_panel;
+	struct dp_display_mode dp_mode;
+	struct dp_display *dp_disp = display;
+	struct msm_drm_private *priv;
+	struct msm_resource_caps_info avail_dp_res;
+	int rc = 0;
+
+	if (!drm_mode || !mode_info || !avail_res ||
+			!avail_res->max_mixer_width || !connector || !display ||
+			!connector->dev || !connector->dev->dev_private) {
+		DP_ERR("invalid params\n");
+		return -EINVAL;
+	}
+
+	memset(mode_info, 0, sizeof(*mode_info));
+
+	sde_conn = to_sde_connector(connector);
+	dp_panel = sde_conn->drv_panel;
+	priv = connector->dev->dev_private;
+
+	topology = &mode_info->topology;
+
+	rc = dp_disp->get_available_dp_resources(dp_disp, avail_res,
+			&avail_dp_res);
+	if (rc) {
+		DP_ERR("error getting max dp resources. rc:%d\n", rc);
+		return rc;
+	}
+
+	rc = msm_get_mixer_count(priv, drm_mode, &avail_dp_res,
+			&topology->num_lm);
+	if (rc) {
+		DP_ERR("error getting mixer count. rc:%d\n", rc);
+		return rc;
+	}
+	/* reset dp connector lm_mask for every connection event and
+	 * this will get re-populated in resource manager based on
+	 * resolution and topology of dp display.
+	 */
+	sde_conn->lm_mask = 0;
+
+	topology->num_enc = no_enc;
+	topology->num_intf = single_intf;
+
+	mode_info->frame_rate = drm_mode_vrefresh(drm_mode);
+	mode_info->vtotal = drm_mode->vtotal;
+
+	mode_info->wide_bus_en = dp_panel->widebus_en;
+
+	dp_disp->convert_to_dp_mode(dp_disp, dp_panel, drm_mode, &dp_mode);
+
+	if (dp_mode.timing.comp_info.enabled) {
+		memcpy(&mode_info->comp_info,
+			&dp_mode.timing.comp_info,
+			sizeof(mode_info->comp_info));
+
+		topology->num_enc = topology->num_lm;
+		topology->comp_type = mode_info->comp_info.comp_type;
+	}
+
+	return 0;
+}
+
+int dp_connector_get_info(struct drm_connector *connector,
+		struct msm_display_info *info, void *data)
+{
+	struct dp_display *display = data;
+
+	if (!info || !display || !display->drm_dev) {
+		DP_ERR("invalid params\n");
+		return -EINVAL;
+	}
+
+	info->intf_type = DRM_MODE_CONNECTOR_DisplayPort;
+
+	info->num_of_h_tiles = 1;
+	info->h_tile_instance[0] = 0;
+	info->is_connected = display->is_sst_connected;
+	info->curr_panel_mode = MSM_DISPLAY_VIDEO_MODE;
+	info->capabilities = MSM_DISPLAY_CAP_VID_MODE | MSM_DISPLAY_CAP_EDID |
+		MSM_DISPLAY_CAP_HOT_PLUG;
+
+	return 0;
+}
+
+enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
+		bool force,
+		void *display)
+{
+	enum drm_connector_status status = connector_status_unknown;
+	struct msm_display_info info;
+	int rc;
+
+	if (!conn || !display)
+		return status;
+
+	/* get display dp_info */
+	memset(&info, 0x0, sizeof(info));
+	rc = dp_connector_get_info(conn, &info, display);
+	if (rc) {
+		DP_ERR("failed to get display info, rc=%d\n", rc);
+		return connector_status_disconnected;
+	}
+
+	if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG)
+		status = (info.is_connected ? connector_status_connected :
+					      connector_status_disconnected);
+	else
+		status = connector_status_connected;
+
+	conn->display_info.width_mm = info.width_mm;
+	conn->display_info.height_mm = info.height_mm;
+
+	return status;
+}
+
+void dp_connector_post_open(struct drm_connector *connector, void *display)
+{
+	struct dp_display *dp;
+
+	if (!display) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	dp = display;
+
+	if (dp->post_open)
+		dp->post_open(dp);
+}
+
+int dp_connector_atomic_check(struct drm_connector *connector,
+	void *display,
+	struct drm_atomic_state *a_state)
+{
+	struct sde_connector *sde_conn;
+	struct drm_connector_state *old_state;
+	struct drm_connector_state *c_state;
+
+	if (!connector || !display || !a_state)
+		return -EINVAL;
+
+	c_state = drm_atomic_get_new_connector_state(a_state, connector);
+	old_state =
+		drm_atomic_get_old_connector_state(a_state, connector);
+
+	if (!old_state || !c_state)
+		return -EINVAL;
+
+	sde_conn = to_sde_connector(connector);
+
+	/*
+	 * Marking the colorspace has been changed
+	 * the flag shall be checked in the pre_kickoff
+	 * to configure the new colorspace in HW
+	 */
+	if (c_state->colorspace != old_state->colorspace) {
+		DP_DEBUG("colorspace has been updated\n");
+		sde_conn->colorspace_updated = true;
+	}
+
+	return 0;
+}
+
+int dp_connector_get_modes(struct drm_connector *connector,
+		void *display, const struct msm_resource_caps_info *avail_res)
+{
+	int rc = 0;
+	struct dp_display *dp;
+	struct dp_display_mode *dp_mode = NULL;
+	struct sde_connector *sde_conn;
+
+	if (!connector || !display)
+		return 0;
+
+	sde_conn = to_sde_connector(connector);
+	if (!sde_conn->drv_panel) {
+		DP_ERR("invalid dp panel\n");
+		return 0;
+	}
+
+	dp = display;
+
+	dp_mode = kzalloc(sizeof(*dp_mode),  GFP_KERNEL);
+	if (!dp_mode)
+		return 0;
+
+	/* pluggable case assumes EDID is read when HPD */
+	if (dp->is_sst_connected) {
+		/*
+		 * 1. for test request, rc = 1, and dp_mode will have test mode populated
+		 * 2. During normal operation, dp_mode will be untouched
+		 *    a. if mode query succeeds rc >= 0, valid modes will be added to connector
+		 *    b. if edid read failed, then connector mode list will be empty and rc <= 0
+		 */
+		rc = dp->get_modes(dp, sde_conn->drv_panel, dp_mode);
+		if (!rc) {
+			DP_WARN("failed to get DP sink modes, adding failsafe");
+			init_failsafe_mode(dp_mode);
+		}
+		if (dp_mode->timing.pixel_clk_khz) /* valid DP mode */
+			rc = dp_connector_add_custom_mode(connector, dp_mode);
+	} else {
+		DP_ERR("No sink connected\n");
+	}
+	kfree(dp_mode);
+
+	return rc;
+}
+
+int dp_drm_bridge_init(void *data, struct drm_encoder *encoder,
+	u32 max_mixer_count, u32 max_dsc_count)
+{
+	int rc = 0;
+	struct dp_bridge *bridge;
+	struct drm_device *dev;
+	struct dp_display *display = data;
+	struct msm_drm_private *priv = NULL;
+
+	bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+	if (!bridge) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	dev = display->drm_dev;
+	bridge->display = display;
+	bridge->base.funcs = &dp_bridge_ops;
+	bridge->base.encoder = encoder;
+
+	priv = dev->dev_private;
+
+	rc = drm_bridge_attach(encoder, &bridge->base, NULL, 0);
+	if (rc) {
+		DP_ERR("failed to attach bridge, rc=%d\n", rc);
+		goto error_free_bridge;
+	}
+
+	rc = display->request_irq(display);
+	if (rc) {
+		DP_ERR("request_irq failed, rc=%d\n", rc);
+		goto error_free_bridge;
+	}
+
+	priv->bridges[priv->num_bridges++] = &bridge->base;
+	display->bridge = bridge;
+	display->max_mixer_count = max_mixer_count;
+	display->max_dsc_count = max_dsc_count;
+
+	return 0;
+error_free_bridge:
+	kfree(bridge);
+error:
+	return rc;
+}
+
+void dp_drm_bridge_deinit(void *data)
+{
+	struct dp_display *display = data;
+	struct dp_bridge *bridge = display->bridge;
+
+	kfree(bridge);
+}
+
+enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector,
+		struct drm_display_mode *mode, void *display,
+		const struct msm_resource_caps_info *avail_res)
+{
+	int rc = 0, vrefresh;
+	struct dp_display *dp_disp;
+	struct sde_connector *sde_conn;
+	struct msm_resource_caps_info avail_dp_res;
+	struct dp_panel *dp_panel;
+
+	if (!mode || !display || !connector) {
+		DP_ERR("invalid params\n");
+		return MODE_ERROR;
+	}
+
+	sde_conn = to_sde_connector(connector);
+	if (!sde_conn->drv_panel) {
+		DP_ERR("invalid dp panel\n");
+		return MODE_ERROR;
+	}
+
+	dp_disp = display;
+	dp_panel = sde_conn->drv_panel;
+
+	vrefresh = drm_mode_vrefresh(mode);
+
+	rc = dp_disp->get_available_dp_resources(dp_disp, avail_res,
+			&avail_dp_res);
+	if (rc) {
+		DP_ERR("error getting max dp resources. rc:%d\n", rc);
+		return MODE_ERROR;
+	}
+
+	/* As per spec, failsafe mode should always be present */
+	if ((mode->hdisplay == 640) && (mode->vdisplay == 480) && (mode->clock == 25175))
+		goto validate_mode;
+
+	if (dp_panel->mode_override && (mode->hdisplay != dp_panel->hdisplay ||
+			mode->vdisplay != dp_panel->vdisplay ||
+			vrefresh != dp_panel->vrefresh ||
+			mode->picture_aspect_ratio != dp_panel->aspect_ratio))
+		return MODE_BAD;
+
+validate_mode:
+	return dp_disp->validate_mode(dp_disp, sde_conn->drv_panel,
+			mode, &avail_dp_res);
+}
+
+int dp_connector_update_pps(struct drm_connector *connector,
+		char *pps_cmd, void *display)
+{
+	struct dp_display *dp_disp;
+	struct sde_connector *sde_conn;
+
+	if (!display || !connector) {
+		DP_ERR("invalid params\n");
+		return -EINVAL;
+	}
+
+	sde_conn = to_sde_connector(connector);
+	if (!sde_conn->drv_panel) {
+		DP_ERR("invalid dp panel\n");
+		return MODE_ERROR;
+	}
+
+	dp_disp = display;
+	return dp_disp->update_pps(dp_disp, connector, pps_cmd);
+}
+
+int dp_connector_install_properties(void *display, struct drm_connector *conn)
+{
+	struct dp_display *dp_display = display;
+	struct drm_connector *base_conn;
+	int rc;
+
+	if (!display || !conn) {
+		DP_ERR("invalid params\n");
+		return -EINVAL;
+	}
+
+	base_conn = dp_display->base_connector;
+
+	/*
+	 * Create the property on the base connector during probe time and then
+	 * attach the same property onto new connector objects created for MST
+	 */
+	if (!base_conn->colorspace_property) {
+		/* This is the base connector. create the drm property */
+		rc = drm_mode_create_dp_colorspace_property(base_conn);
+		if (rc)
+			return rc;
+	} else {
+		conn->colorspace_property = base_conn->colorspace_property;
+	}
+
+	drm_object_attach_property(&conn->base, conn->colorspace_property, 0);
+
+	return 0;
+}

+ 275 - 0
qcom/opensource/display-drivers/msm/dp/dp_drm.h

@@ -0,0 +1,275 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_DRM_H_
+#define _DP_DRM_H_
+
+#include <linux/types.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_bridge.h>
+
+#include "msm_drv.h"
+#include "dp_display.h"
+
+struct dp_bridge {
+	struct drm_bridge base;
+	u32 id;
+
+	struct drm_connector *connector;
+	struct dp_display *display;
+	struct dp_display_mode dp_mode;
+	void *dp_panel;
+};
+
+
+#if IS_ENABLED(CONFIG_DRM_MSM_DP)
+/**
+ * dp_connector_config_hdr - callback to configure HDR
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ * @c_state: connect state data
+ * Returns: Zero on success
+ */
+int dp_connector_config_hdr(struct drm_connector *connector,
+		void *display,
+		struct sde_connector_state *c_state);
+
+/**
+ * dp_connector_atomic_check - callback to perform atomic
+ * check for DP
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ * @c_state: connect state data
+ * Returns: Zero on success
+ */
+int dp_connector_atomic_check(struct drm_connector *connector,
+	void *display,
+	struct drm_atomic_state *state);
+
+/**
+ * dp_connector_set_colorspace - callback to set new colorspace
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ * Returns: Zero on success
+ */
+int dp_connector_set_colorspace(struct drm_connector *connector,
+	void *display);
+
+/**
+ * dp_connector_post_init - callback to perform additional initialization steps
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ * Returns: Zero on success
+ */
+int dp_connector_post_init(struct drm_connector *connector, void *display);
+
+/**
+ * dp_connector_detect - callback to determine if connector is connected
+ * @connector: Pointer to drm connector structure
+ * @force: Force detect setting from drm framework
+ * @display: Pointer to private display handle
+ * Returns: Connector 'is connected' status
+ */
+enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
+		bool force,
+		void *display);
+
+/**
+ * dp_connector_get_modes - callback to add drm modes via drm_mode_probed_add()
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ * @avail_res: Pointer with curr available resources
+ * Returns: Number of modes added
+ */
+int dp_connector_get_modes(struct drm_connector *connector,
+		void *display, const struct msm_resource_caps_info *avail_res);
+
+/**
+ * dp_connector_mode_valid - callback to determine if specified mode is valid
+ * @connector: Pointer to drm connector structure
+ * @mode: Pointer to drm mode structure
+ * @display: Pointer to private display handle
+ * @avail_res: Pointer with curr available resources
+ * Returns: Validity status for specified mode
+ */
+enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector,
+		struct drm_display_mode *mode,
+		void *display, const struct msm_resource_caps_info *avail_res);
+
+/**
+ * dp_connector_get_mode_info - retrieve information of the mode selected
+ * @connector: Pointer to drm connector structure
+ * @drm_mode: Display mode set for the display
+ * @mode_info: Out parameter. Information of the mode
+ * @sub_mode: Additional mode info to drm display mode
+ * @display: Pointer to private display structure
+ * @avail_res: Pointer with curr available resources
+ * Returns: zero on success
+ */
+int dp_connector_get_mode_info(struct drm_connector *connector,
+		const struct drm_display_mode *drm_mode,
+		struct msm_sub_mode *sub_mode,
+		struct msm_mode_info *mode_info,
+		void *display, const struct msm_resource_caps_info *avail_res);
+
+/**
+ * dp_connector_get_info - retrieve connector display info
+ * @connector: Pointer to drm connector structure
+ * @info: Out parameter. Information of the connected display
+ * @display: Pointer to private display structure
+ * Returns: zero on success
+ */
+int dp_connector_get_info(struct drm_connector *connector,
+		struct msm_display_info *info, void *display);
+
+/**
+ * dp_connector_post_open - handle the post open functionalities
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display structure
+ */
+void dp_connector_post_open(struct drm_connector *connector, void *display);
+
+/**
+ * dp_drm_bridge_init- drm dp bridge initialize
+ * @display: Pointer to private display structure
+ * @encoder: encoder for this dp bridge
+ * @max_mixer_count: max available mixers for dp display
+ * @max_dsc_count: max available dsc for dp display
+ */
+int dp_drm_bridge_init(void *display, struct drm_encoder *encoder,
+	u32 max_mixer_count, u32 max_dsc_count);
+
+void dp_drm_bridge_deinit(void *display);
+
+/**
+ * convert_to_drm_mode - convert dp mode to drm mode
+ * @dp_mode: Point to dp mode
+ * @drm_mode: Pointer to drm mode
+ */
+void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
+				struct drm_display_mode *drm_mode);
+
+/**
+ * dp_connector_update_pps - update pps for given connector
+ * @dp_mode: Point to dp mode
+ * @pps_cmd: PPS packet
+ * @display: Pointer to private display structure
+ */
+int dp_connector_update_pps(struct drm_connector *connector,
+		char *pps_cmd, void *display);
+
+/**
+ * dp_connector_install_properties - install drm properties
+ * @display: Pointer to private display structure
+ * @conn: Pointer to connector
+ */
+int dp_connector_install_properties(void *display,
+		struct drm_connector *conn);
+
+/**
+ * init_failsafe_mode - add failsafe edid mode
+ * @dp_mode: Pointer to mode
+ */
+void init_failsafe_mode(struct dp_display_mode *dp_mode);
+
+/**
+ * dp_connector_add_custom_mode - add edid mode to connector
+ * @conn: Pointer to connector
+ * @dp_mode: Pointer to mode
+ */
+int dp_connector_add_custom_mode(struct drm_connector *conn, struct dp_display_mode *dp_mode);
+
+#else
+static inline int dp_connector_config_hdr(struct drm_connector *connector,
+		void *display, struct sde_connector_state *c_state)
+{
+	return 0;
+}
+
+static inline int dp_connector_atomic_check(struct drm_connector *connector,
+		void *display, struct drm_atomic_state *state)
+{
+	return 0;
+}
+
+static inline int dp_connector_set_colorspace(struct drm_connector *connector,
+		void *display)
+{
+	return 0;
+}
+
+static inline int dp_connector_post_init(struct drm_connector *connector,
+		void *display)
+{
+	return 0;
+}
+
+static inline enum drm_connector_status dp_connector_detect(
+		struct drm_connector *conn,
+		bool force,
+		void *display)
+{
+	return 0;
+}
+
+
+static inline int dp_connector_get_modes(struct drm_connector *connector,
+		void *display, const struct msm_resource_caps_info *avail_res)
+{
+	return 0;
+}
+
+static inline enum drm_mode_status dp_connector_mode_valid(
+		struct drm_connector *connector,
+		struct drm_display_mode *mode,
+		void *display, const struct msm_resource_caps_info *avail_res)
+{
+	return MODE_OK;
+}
+
+static inline int dp_connector_get_mode_info(struct drm_connector *connector,
+		const struct drm_display_mode *drm_mode,
+		struct msm_sub_mode *sub_mode,
+		struct msm_mode_info *mode_info,
+		void *display, const struct msm_resource_caps_info *avail_res)
+{
+	return 0;
+}
+
+static inline int dp_connector_get_info(struct drm_connector *connector,
+		struct msm_display_info *info, void *display)
+{
+	return 0;
+}
+
+static inline void dp_connector_post_open(struct drm_connector *connector,
+		void *display)
+{
+}
+
+static inline int dp_drm_bridge_init(void *display, struct drm_encoder *encoder,
+		u32 max_mixer_count, u32 max_dsc_count)
+{
+	return 0;
+}
+
+static inline void dp_drm_bridge_deinit(void *display)
+{
+}
+
+static inline void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
+				struct drm_display_mode *drm_mode)
+{
+}
+
+static int dp_connector_install_properties(void *display,
+		struct drm_connector *conn)
+{
+	return 0;
+}
+#endif /* CONFIG_DRM_MSM_DP */
+
+#endif /* _DP_DRM_H_ */

+ 298 - 0
qcom/opensource/display-drivers/msm/dp/dp_gpio_hpd.c

@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/sde_io_util.h>
+#include <linux/of_gpio.h>
+#include "dp_gpio_hpd.h"
+#include "dp_debug.h"
+
+struct dp_gpio_hpd_private {
+	struct device *dev;
+	struct dp_hpd base;
+	struct dss_gpio gpio_cfg;
+	struct delayed_work work;
+	struct dp_hpd_cb *cb;
+	int irq;
+	bool hpd;
+};
+
+static int dp_gpio_hpd_connect(struct dp_gpio_hpd_private *gpio_hpd, bool hpd)
+{
+	int rc = 0;
+
+	if (!gpio_hpd) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	gpio_hpd->base.hpd_high = hpd;
+	gpio_hpd->base.alt_mode_cfg_done = hpd;
+	gpio_hpd->base.hpd_irq = false;
+
+	if (!gpio_hpd->cb ||
+		!gpio_hpd->cb->configure ||
+		!gpio_hpd->cb->disconnect) {
+		DP_ERR("invalid cb\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (hpd)
+		rc = gpio_hpd->cb->configure(gpio_hpd->dev);
+	else
+		rc = gpio_hpd->cb->disconnect(gpio_hpd->dev);
+
+error:
+	return rc;
+}
+
+static int dp_gpio_hpd_attention(struct dp_gpio_hpd_private *gpio_hpd)
+{
+	int rc = 0;
+
+	if (!gpio_hpd) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	gpio_hpd->base.hpd_irq = true;
+
+	if (gpio_hpd->cb && gpio_hpd->cb->attention)
+		rc = gpio_hpd->cb->attention(gpio_hpd->dev);
+
+error:
+	return rc;
+}
+
+static irqreturn_t dp_gpio_isr(int unused, void *data)
+{
+	struct dp_gpio_hpd_private *gpio_hpd = data;
+	u32 const disconnect_timeout_retry = 50;
+	bool hpd;
+	int i;
+
+	if (!gpio_hpd)
+		return IRQ_NONE;
+
+	hpd = gpio_get_value_cansleep(gpio_hpd->gpio_cfg.gpio);
+
+	if (!gpio_hpd->hpd && hpd) {
+		gpio_hpd->hpd = true;
+		queue_delayed_work(system_wq, &gpio_hpd->work, 0);
+		return IRQ_HANDLED;
+	}
+
+	if (!gpio_hpd->hpd)
+		return IRQ_HANDLED;
+
+	/* In DP 1.2 spec, 100msec is recommended for the detection
+	 * of HPD connect event. Here we'll poll HPD status for
+	 * 50x2ms = 100ms and if HPD is always low, we know DP is
+	 * disconnected. If HPD is high, HPD_IRQ will be handled
+	 */
+	for (i = 0; i < disconnect_timeout_retry; i++) {
+		if (hpd) {
+			dp_gpio_hpd_attention(gpio_hpd);
+			return IRQ_HANDLED;
+		}
+		usleep_range(2000, 2100);
+		hpd = gpio_get_value_cansleep(gpio_hpd->gpio_cfg.gpio);
+	}
+
+	gpio_hpd->hpd = false;
+	queue_delayed_work(system_wq, &gpio_hpd->work, 0);
+	return IRQ_HANDLED;
+}
+
+static void dp_gpio_hpd_work(struct work_struct *work)
+{
+	struct delayed_work *dw = to_delayed_work(work);
+	struct dp_gpio_hpd_private *gpio_hpd = container_of(dw,
+		struct dp_gpio_hpd_private, work);
+	int ret;
+
+	if (gpio_hpd->hpd) {
+		devm_free_irq(gpio_hpd->dev,
+			gpio_hpd->irq, gpio_hpd);
+		ret = devm_request_threaded_irq(gpio_hpd->dev,
+			gpio_hpd->irq, NULL,
+			dp_gpio_isr,
+			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+			"dp-gpio-intp", gpio_hpd);
+		dp_gpio_hpd_connect(gpio_hpd, true);
+	} else {
+		devm_free_irq(gpio_hpd->dev,
+				gpio_hpd->irq, gpio_hpd);
+		ret = devm_request_threaded_irq(gpio_hpd->dev,
+			gpio_hpd->irq, NULL,
+			dp_gpio_isr,
+			IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+			"dp-gpio-intp", gpio_hpd);
+		dp_gpio_hpd_connect(gpio_hpd, false);
+	}
+
+	if (ret < 0)
+		DP_ERR("Cannot claim IRQ dp-gpio-intp\n");
+}
+
+static int dp_gpio_hpd_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
+{
+	int rc = 0;
+	struct dp_gpio_hpd_private *gpio_hpd;
+
+	if (!dp_hpd) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	gpio_hpd = container_of(dp_hpd, struct dp_gpio_hpd_private, base);
+
+	dp_gpio_hpd_connect(gpio_hpd, hpd);
+error:
+	return rc;
+}
+
+static int dp_gpio_hpd_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
+{
+	int rc = 0;
+	struct dp_gpio_hpd_private *gpio_hpd;
+
+	if (!dp_hpd) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	gpio_hpd = container_of(dp_hpd, struct dp_gpio_hpd_private, base);
+
+	dp_gpio_hpd_attention(gpio_hpd);
+error:
+	return rc;
+}
+
+int dp_gpio_hpd_register(struct dp_hpd *dp_hpd)
+{
+	struct dp_gpio_hpd_private *gpio_hpd;
+	int edge;
+	int rc = 0;
+
+	if (!dp_hpd)
+		return -EINVAL;
+
+	gpio_hpd = container_of(dp_hpd, struct dp_gpio_hpd_private, base);
+
+	gpio_hpd->hpd = gpio_get_value_cansleep(gpio_hpd->gpio_cfg.gpio);
+
+	edge = gpio_hpd->hpd ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
+	rc = devm_request_threaded_irq(gpio_hpd->dev, gpio_hpd->irq, NULL,
+		dp_gpio_isr,
+		edge | IRQF_ONESHOT,
+		"dp-gpio-intp", gpio_hpd);
+	if (rc) {
+		DP_ERR("Failed to request INTP threaded IRQ: %d\n", rc);
+		return rc;
+	}
+
+	if (gpio_hpd->hpd)
+		queue_delayed_work(system_wq, &gpio_hpd->work, 0);
+
+	return rc;
+}
+
+struct dp_hpd *dp_gpio_hpd_get(struct device *dev,
+	struct dp_hpd_cb *cb)
+{
+	int rc = 0;
+	const char *hpd_gpio_name = "qcom,dp-hpd-gpio";
+	struct dp_gpio_hpd_private *gpio_hpd;
+	struct dp_pinctrl pinctrl = {0};
+	unsigned int gpio;
+
+	if (!dev || !cb) {
+		DP_ERR("invalid device\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	gpio = of_get_named_gpio(dev->of_node, hpd_gpio_name, 0);
+	if (!gpio_is_valid(gpio)) {
+		DP_DEBUG("%s gpio not specified\n", hpd_gpio_name);
+		rc = -EINVAL;
+		goto error;
+	}
+
+	gpio_hpd = devm_kzalloc(dev, sizeof(*gpio_hpd), GFP_KERNEL);
+	if (!gpio_hpd) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	pinctrl.pin = devm_pinctrl_get(dev);
+	if (!IS_ERR_OR_NULL(pinctrl.pin)) {
+		pinctrl.state_hpd_active = pinctrl_lookup_state(pinctrl.pin,
+						"mdss_dp_hpd_active");
+		if (!IS_ERR_OR_NULL(pinctrl.state_hpd_active)) {
+			rc = pinctrl_select_state(pinctrl.pin,
+					pinctrl.state_hpd_active);
+			if (rc) {
+				DP_ERR("failed to set hpd active state\n");
+				goto gpio_error;
+			}
+		}
+	}
+
+	gpio_hpd->gpio_cfg.gpio = gpio;
+	strlcpy(gpio_hpd->gpio_cfg.gpio_name, hpd_gpio_name,
+		sizeof(gpio_hpd->gpio_cfg.gpio_name));
+	gpio_hpd->gpio_cfg.value = 0;
+
+	rc = gpio_request(gpio_hpd->gpio_cfg.gpio,
+		gpio_hpd->gpio_cfg.gpio_name);
+	if (rc) {
+		DP_ERR("%s: failed to request gpio\n", hpd_gpio_name);
+		goto gpio_error;
+	}
+	gpio_direction_input(gpio_hpd->gpio_cfg.gpio);
+
+	gpio_hpd->dev = dev;
+	gpio_hpd->cb = cb;
+	gpio_hpd->irq = gpio_to_irq(gpio_hpd->gpio_cfg.gpio);
+	INIT_DELAYED_WORK(&gpio_hpd->work, dp_gpio_hpd_work);
+
+	gpio_hpd->base.simulate_connect = dp_gpio_hpd_simulate_connect;
+	gpio_hpd->base.simulate_attention = dp_gpio_hpd_simulate_attention;
+	gpio_hpd->base.register_hpd = dp_gpio_hpd_register;
+
+	return &gpio_hpd->base;
+
+gpio_error:
+	devm_kfree(dev, gpio_hpd);
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_gpio_hpd_put(struct dp_hpd *dp_hpd)
+{
+	struct dp_gpio_hpd_private *gpio_hpd;
+
+	if (!dp_hpd)
+		return;
+
+	gpio_hpd = container_of(dp_hpd, struct dp_gpio_hpd_private, base);
+
+	gpio_free(gpio_hpd->gpio_cfg.gpio);
+	devm_kfree(gpio_hpd->dev, gpio_hpd);
+}

+ 32 - 0
qcom/opensource/display-drivers/msm/dp/dp_gpio_hpd.h

@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ */
+
+
+#ifndef _DP_GPIO_HPD_H_
+#define _DP_GPIO_HPD_H_
+
+#include "dp_hpd.h"
+
+/**
+ * dp_gpio_hpd_get() - configure and get the DisplayPlot HPD module data
+ *
+ * @dev: device instance of the caller
+ * return: pointer to allocated gpio hpd module data
+ *
+ * This function sets up the gpio hpd module
+ */
+struct dp_hpd *dp_gpio_hpd_get(struct device *dev,
+	struct dp_hpd_cb *cb);
+
+/**
+ * dp_gpio_hpd_put()
+ *
+ * Cleans up dp_hpd instance
+ *
+ * @hpd: instance of gpio_hpd
+ */
+void dp_gpio_hpd_put(struct dp_hpd *hpd);
+
+#endif /* _DP_GPIO_HPD_H_ */

+ 1039 - 0
qcom/opensource/display-drivers/msm/dp/dp_hdcp2p2.c

@@ -0,0 +1,1039 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/types.h>
+#include <linux/kthread.h>
+#include <linux/msm_hdcp.h>
+#include <linux/kfifo.h>
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0))
+#include <drm/display/drm_dp_helper.h>
+#else
+#include <drm/drm_dp_helper.h>
+#endif
+
+#include "sde_hdcp_2x.h"
+#include "dp_debug.h"
+
+#define DP_INTR_STATUS2				(0x00000024)
+#define DP_INTR_STATUS3				(0x00000028)
+#define dp_read(offset) readl_relaxed((offset))
+#define dp_write(offset, data) writel_relaxed((data), (offset))
+#define DP_HDCP_RXCAPS_LENGTH 3
+
+enum dp_hdcp2p2_sink_status {
+	SINK_DISCONNECTED,
+	SINK_CONNECTED
+};
+
+struct dp_hdcp2p2_ctrl {
+	DECLARE_KFIFO(cmd_q, enum hdcp_transport_wakeup_cmd, 8);
+	wait_queue_head_t wait_q;
+	atomic_t auth_state;
+	atomic_t abort;
+	enum dp_hdcp2p2_sink_status sink_status; /* Is sink connected */
+	struct dp_hdcp2p2_interrupts *intr;
+	struct sde_hdcp_init_data init_data;
+	struct mutex mutex; /* mutex to protect access to ctrl */
+	struct mutex msg_lock; /* mutex to protect access to msg buffer */
+	struct sde_hdcp_ops *ops;
+	void *lib_ctx; /* Handle to HDCP 2.2 Trustzone library */
+	struct sde_hdcp_2x_ops *lib; /* Ops for driver to call into TZ */
+
+	struct task_struct *thread;
+	struct hdcp2_buffer response;
+	struct hdcp2_buffer request;
+	uint32_t total_message_length;
+	uint32_t transaction_delay;
+	uint32_t transaction_timeout;
+	struct sde_hdcp_2x_msg_part msg_part[HDCP_MAX_MESSAGE_PARTS];
+	u8 sink_rx_status;
+	u8 rx_status;
+	char abort_mask;
+
+	bool polling;
+};
+
+struct dp_hdcp2p2_int_set {
+	u32 interrupt;
+	char *name;
+	void (*func)(struct dp_hdcp2p2_ctrl *ctrl);
+};
+
+struct dp_hdcp2p2_interrupts {
+	u32 reg;
+	struct dp_hdcp2p2_int_set *int_set;
+};
+
+static inline int dp_hdcp2p2_valid_handle(struct dp_hdcp2p2_ctrl *ctrl)
+{
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	if (!ctrl->lib_ctx) {
+		DP_ERR("HDCP library needs to be acquired\n");
+		return -EINVAL;
+	}
+
+	if (!ctrl->lib) {
+		DP_ERR("invalid lib ops data\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline bool dp_hdcp2p2_is_valid_state(struct dp_hdcp2p2_ctrl *ctrl)
+{
+	enum hdcp_transport_wakeup_cmd cmd;
+
+	if (kfifo_peek(&ctrl->cmd_q, &cmd) &&
+			cmd == HDCP_TRANSPORT_CMD_AUTHENTICATE)
+		return true;
+
+	if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE)
+		return true;
+
+	return false;
+}
+
+static int dp_hdcp2p2_copy_buf(struct dp_hdcp2p2_ctrl *ctrl,
+	struct hdcp_transport_wakeup_data *data)
+{
+	int i = 0;
+	uint32_t num_messages = 0;
+
+	if (!data || !data->message_data)
+		return 0;
+
+	mutex_lock(&ctrl->msg_lock);
+
+	num_messages = data->message_data->num_messages;
+	ctrl->total_message_length = 0; /* Total length of all messages */
+
+	for (i = 0; i < num_messages; i++)
+		ctrl->total_message_length +=
+			data->message_data->messages[i].length;
+
+	memcpy(ctrl->msg_part, data->message_data->messages,
+		sizeof(data->message_data->messages));
+
+	ctrl->rx_status = data->message_data->rx_status;
+	ctrl->abort_mask = data->abort_mask;
+
+	if (!ctrl->total_message_length) {
+		mutex_unlock(&ctrl->msg_lock);
+		return 0;
+	}
+
+	ctrl->response.data = data->buf;
+	ctrl->response.length = ctrl->total_message_length;
+	ctrl->request.data = data->buf;
+	ctrl->request.length = ctrl->total_message_length;
+
+	ctrl->transaction_delay = data->transaction_delay;
+	ctrl->transaction_timeout = data->transaction_timeout;
+
+	mutex_unlock(&ctrl->msg_lock);
+
+	return 0;
+}
+
+static void dp_hdcp2p2_send_auth_status(struct dp_hdcp2p2_ctrl *ctrl)
+{
+	ctrl->init_data.notify_status(ctrl->init_data.cb_data,
+		atomic_read(&ctrl->auth_state));
+}
+
+static void dp_hdcp2p2_set_interrupts(struct dp_hdcp2p2_ctrl *ctrl, bool enable)
+{
+	void __iomem *base = ctrl->init_data.dp_ahb->base;
+	struct dp_hdcp2p2_interrupts *intr = ctrl->intr;
+
+	if (atomic_read(&ctrl->abort))
+		return;
+
+	while (intr && intr->reg) {
+		struct dp_hdcp2p2_int_set *int_set = intr->int_set;
+		u32 interrupts = 0;
+
+		while (int_set && int_set->interrupt) {
+			interrupts |= int_set->interrupt;
+			int_set++;
+		}
+
+		if (enable)
+			dp_write(base + intr->reg,
+				dp_read(base + intr->reg) | interrupts);
+		else
+			dp_write(base + intr->reg,
+				dp_read(base + intr->reg) & ~interrupts);
+		intr++;
+	}
+}
+
+static int dp_hdcp2p2_wakeup(struct hdcp_transport_wakeup_data *data)
+{
+	struct dp_hdcp2p2_ctrl *ctrl;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
+	if (!data) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	ctrl = data->context;
+	if (!ctrl) {
+		DP_ERR("invalid ctrl\n");
+		return -EINVAL;
+	}
+
+	if (dp_hdcp2p2_copy_buf(ctrl, data))
+		goto exit;
+
+	ctrl->polling = false;
+	switch (data->cmd) {
+	case HDCP_TRANSPORT_CMD_STATUS_SUCCESS:
+		atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATED);
+		kfifo_put(&ctrl->cmd_q, data->cmd);
+		wake_up(&ctrl->wait_q);
+		break;
+	case HDCP_TRANSPORT_CMD_STATUS_FAILED:
+		atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
+		kfifo_put(&ctrl->cmd_q, data->cmd);
+		kthread_park(ctrl->thread);
+		break;
+	default:
+		kfifo_put(&ctrl->cmd_q, data->cmd);
+		wake_up(&ctrl->wait_q);
+		break;
+	}
+
+exit:
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, data->cmd);
+	return 0;
+}
+
+static inline void dp_hdcp2p2_wakeup_lib(struct dp_hdcp2p2_ctrl *ctrl,
+	struct sde_hdcp_2x_wakeup_data *data)
+{
+	int rc = 0;
+
+	if (ctrl && ctrl->lib && ctrl->lib->wakeup &&
+		data && (data->cmd != HDCP_2X_CMD_INVALID)) {
+		rc = ctrl->lib->wakeup(data);
+		if (rc)
+			DP_ERR("error sending %s to lib\n",
+				sde_hdcp_2x_cmd_to_str(data->cmd));
+	}
+}
+
+static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl)
+{
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	ctrl->sink_status = SINK_DISCONNECTED;
+	atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE);
+}
+
+static int dp_hdcp2p2_register(void *input, bool mst_enabled)
+{
+	int rc;
+	struct dp_hdcp2p2_ctrl *ctrl = input;
+	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_ENABLE};
+
+	rc = dp_hdcp2p2_valid_handle(ctrl);
+	if (rc)
+		return rc;
+
+	if (mst_enabled)
+		cdata.device_type = HDCP_TXMTR_DP_MST;
+	else
+		cdata.device_type = HDCP_TXMTR_DP;
+
+	cdata.context = ctrl->lib_ctx;
+	rc = ctrl->lib->wakeup(&cdata);
+
+	return rc;
+}
+
+static int dp_hdcp2p2_on(void *input)
+{
+	int rc = 0;
+	struct dp_hdcp2p2_ctrl *ctrl = input;
+	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};
+
+	rc = dp_hdcp2p2_valid_handle(ctrl);
+	if (rc)
+		return rc;
+
+	cdata.cmd = HDCP_2X_CMD_START;
+	cdata.context = ctrl->lib_ctx;
+	rc = ctrl->lib->wakeup(&cdata);
+	if (rc)
+		DP_ERR("Unable to start the HDCP 2.2 library (%d)\n", rc);
+
+	return rc;
+}
+
+static void dp_hdcp2p2_off(void *input)
+{
+	int rc;
+	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
+	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_DISABLE};
+
+	rc = dp_hdcp2p2_valid_handle(ctrl);
+	if (rc)
+		return;
+
+	dp_hdcp2p2_set_interrupts(ctrl, false);
+
+	dp_hdcp2p2_reset(ctrl);
+
+	kthread_park(ctrl->thread);
+
+	cdata.context = ctrl->lib_ctx;
+	ctrl->lib->wakeup(&cdata);
+}
+
+static int dp_hdcp2p2_authenticate(void *input)
+{
+	int rc;
+	struct dp_hdcp2p2_ctrl *ctrl = input;
+	struct hdcp_transport_wakeup_data cdata = {
+					HDCP_TRANSPORT_CMD_AUTHENTICATE};
+	rc = dp_hdcp2p2_valid_handle(ctrl);
+	if (rc)
+		return rc;
+
+	dp_hdcp2p2_set_interrupts(ctrl, true);
+
+	ctrl->sink_status = SINK_CONNECTED;
+	atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATING);
+
+	if (kthread_should_park())
+		kthread_park(ctrl->thread);
+	kfifo_reset(&ctrl->cmd_q);
+	kthread_unpark(ctrl->thread);
+
+	cdata.context = input;
+	dp_hdcp2p2_wakeup(&cdata);
+
+	return rc;
+}
+
+static int dp_hdcp2p2_reauthenticate(void *input)
+{
+	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp_hdcp2p2_reset((struct dp_hdcp2p2_ctrl *)input);
+
+	return  dp_hdcp2p2_authenticate(input);
+}
+
+static void dp_hdcp2p2_min_level_change(void *client_ctx,
+		u8 min_enc_level)
+{
+	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)client_ctx;
+	struct sde_hdcp_2x_wakeup_data cdata = {
+		HDCP_2X_CMD_MIN_ENC_LEVEL};
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	cdata.context = ctrl->lib_ctx;
+	cdata.min_enc_level = min_enc_level;
+	dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
+}
+
+static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl)
+{
+	int rc = 0, max_size = 16, read_size = 0, bytes_read = 0;
+	int size = ctrl->request.length, offset = ctrl->msg_part->offset;
+	u8 *buf = ctrl->request.data;
+	s64 diff_ms;
+	ktime_t start_read, finish_read;
+
+	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE ||
+		atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTH_FAIL) {
+		DP_ERR("invalid hdcp state\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (!buf) {
+		DP_ERR("invalid request buffer\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	DP_DEBUG("offset(0x%x), size(%d)\n", offset, size);
+
+	start_read = ktime_get();
+	do {
+		read_size = min(size, max_size);
+
+		bytes_read = drm_dp_dpcd_read(ctrl->init_data.drm_aux,
+				offset, buf, read_size);
+		if (bytes_read != read_size) {
+			DP_ERR("fail: offset(0x%x), size(0x%x), rc(0x%x)\n",
+					offset, read_size, bytes_read);
+			SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY,
+							offset,
+							read_size,
+							bytes_read);
+			rc = -EINVAL;
+			break;
+		}
+
+		buf += read_size;
+		offset += read_size;
+		size -= read_size;
+	} while (size > 0);
+	finish_read = ktime_get();
+	diff_ms = ktime_ms_delta(finish_read, start_read);
+
+	if (ctrl->transaction_timeout && diff_ms > ctrl->transaction_timeout) {
+		DP_ERR("HDCP read timeout exceeded (%lldms > %ums)\n", diff_ms,
+				ctrl->transaction_timeout);
+		rc = -ETIMEDOUT;
+	}
+exit:
+	return rc;
+}
+
+static int dp_hdcp2p2_aux_write_message(struct dp_hdcp2p2_ctrl *ctrl,
+	u8 *buf, int size, uint offset, uint timeout)
+{
+	int const max_size = 16;
+	int rc = 0, write_size = 0, bytes_written = 0;
+
+	DP_DEBUG("offset(0x%x), size(%d)\n", offset, size);
+
+	do {
+		write_size = min(size, max_size);
+
+		bytes_written = drm_dp_dpcd_write(ctrl->init_data.drm_aux,
+				offset, buf, write_size);
+		if (bytes_written != write_size) {
+			DP_ERR("fail: offset(0x%x), size(0x%x), rc(0x%x)\n",
+					offset, write_size, bytes_written);
+			SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY,
+							offset,
+							write_size,
+							bytes_written);
+			rc = -EINVAL;
+			break;
+		}
+
+		buf += write_size;
+		offset += write_size;
+		size -= write_size;
+	} while (size > 0);
+
+	return rc;
+}
+
+static bool dp_hdcp2p2_feature_supported(void *input)
+{
+	int rc;
+	struct dp_hdcp2p2_ctrl *ctrl = input;
+	struct sde_hdcp_2x_ops *lib = NULL;
+	bool supported = false;
+
+	rc = dp_hdcp2p2_valid_handle(ctrl);
+	if (rc)
+		return supported;
+
+	lib = ctrl->lib;
+	if (lib->feature_supported)
+		supported = lib->feature_supported(
+			ctrl->lib_ctx);
+
+	return supported;
+}
+
+static void dp_hdcp2p2_force_encryption(void *data, bool enable)
+{
+	int rc;
+	struct dp_hdcp2p2_ctrl *ctrl = data;
+	struct sde_hdcp_2x_ops *lib = NULL;
+
+	rc = dp_hdcp2p2_valid_handle(ctrl);
+	if (rc)
+		return;
+
+	lib = ctrl->lib;
+	if (lib->force_encryption)
+		lib->force_encryption(ctrl->lib_ctx, enable);
+}
+
+static void dp_hdcp2p2_send_msg(struct dp_hdcp2p2_ctrl *ctrl)
+{
+	int rc = 0;
+	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	cdata.context = ctrl->lib_ctx;
+
+	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+		DP_ERR("hdcp is off\n");
+		goto exit;
+	}
+
+	mutex_lock(&ctrl->msg_lock);
+
+	rc = dp_hdcp2p2_aux_write_message(ctrl, ctrl->response.data,
+			ctrl->response.length, ctrl->msg_part->offset,
+			ctrl->transaction_delay);
+	if (rc) {
+		DP_ERR("Error sending msg to sink %d\n", rc);
+		mutex_unlock(&ctrl->msg_lock);
+		goto exit;
+	}
+
+	cdata.cmd = HDCP_2X_CMD_MSG_SEND_SUCCESS;
+	cdata.timeout = ctrl->transaction_delay;
+	mutex_unlock(&ctrl->msg_lock);
+
+exit:
+	if (rc == -ETIMEDOUT)
+		cdata.cmd = HDCP_2X_CMD_MSG_SEND_TIMEOUT;
+	else if (rc)
+		cdata.cmd = HDCP_2X_CMD_MSG_SEND_FAILED;
+
+	dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, cdata.cmd);
+}
+
+static int dp_hdcp2p2_get_msg_from_sink(struct dp_hdcp2p2_ctrl *ctrl)
+{
+	int rc = 0;
+	struct sde_hdcp_2x_wakeup_data cdata = { HDCP_2X_CMD_INVALID };
+
+	cdata.context = ctrl->lib_ctx;
+
+	rc = dp_hdcp2p2_aux_read_message(ctrl);
+	if (rc) {
+		DP_ERR("error reading message %d\n", rc);
+		goto exit;
+	}
+
+	cdata.total_message_length = ctrl->total_message_length;
+	cdata.timeout = ctrl->transaction_delay;
+exit:
+	if (rc == -ETIMEDOUT)
+		cdata.cmd = HDCP_2X_CMD_MSG_RECV_TIMEOUT;
+	else if (rc)
+		cdata.cmd = HDCP_2X_CMD_MSG_RECV_FAILED;
+	else
+		cdata.cmd = HDCP_2X_CMD_MSG_RECV_SUCCESS;
+
+	dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
+
+	return rc;
+}
+
+static void dp_hdcp2p2_recv_msg(struct dp_hdcp2p2_ctrl *ctrl)
+{
+	struct sde_hdcp_2x_wakeup_data cdata = { HDCP_2X_CMD_INVALID };
+
+	cdata.context = ctrl->lib_ctx;
+
+	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+		DP_ERR("hdcp is off\n");
+		return;
+	}
+
+	if (ctrl->transaction_delay)
+		msleep(ctrl->transaction_delay);
+
+	dp_hdcp2p2_get_msg_from_sink(ctrl);
+}
+
+static void dp_hdcp2p2_link_check(struct dp_hdcp2p2_ctrl *ctrl)
+{
+	int rc = 0;
+	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTH_FAIL ||
+		atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+		DP_ERR("invalid hdcp state\n");
+		return;
+	}
+
+	cdata.context = ctrl->lib_ctx;
+
+	if (ctrl->sink_rx_status & ctrl->abort_mask) {
+		if (ctrl->sink_rx_status & BIT(3))
+			DP_WARN("reauth_req set by sink\n");
+
+		if (ctrl->sink_rx_status & BIT(4))
+			DP_WARN("link failure reported by sink\n");
+
+		ctrl->sink_rx_status = 0;
+		ctrl->rx_status = 0;
+
+		rc = -ENOLINK;
+
+		cdata.cmd = HDCP_2X_CMD_LINK_FAILED;
+		atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
+		goto exit;
+	}
+
+	/* check if sink has made a message available */
+	if (ctrl->polling && (ctrl->sink_rx_status & ctrl->rx_status)) {
+		ctrl->sink_rx_status = 0;
+		ctrl->rx_status = 0;
+
+		dp_hdcp2p2_get_msg_from_sink(ctrl);
+
+		ctrl->polling = false;
+	}
+exit:
+	if (rc)
+		dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
+}
+
+static void dp_hdcp2p2_start_auth(struct dp_hdcp2p2_ctrl *ctrl)
+{
+	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_START_AUTH};
+	cdata.context = ctrl->lib_ctx;
+
+	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING)
+		dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
+}
+
+static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl,
+		u8 *rx_status)
+{
+	u32 const cp_irq_dpcd_offset = 0x201;
+	u32 const rxstatus_dpcd_offset = 0x69493;
+	ssize_t const bytes_to_read = 1;
+	ssize_t bytes_read = 0;
+	u8 buf = 0;
+	int rc = 0;
+	bool cp_irq = false;
+
+	*rx_status = 0;
+
+	bytes_read = drm_dp_dpcd_read(ctrl->init_data.drm_aux,
+			cp_irq_dpcd_offset, &buf, bytes_to_read);
+	if (bytes_read != bytes_to_read) {
+		DP_ERR("cp irq read failed\n");
+		rc = bytes_read;
+		goto error;
+	}
+
+	cp_irq = buf & BIT(2);
+	DP_DEBUG("cp_irq=0x%x\n", cp_irq);
+	buf = 0;
+
+	if (cp_irq) {
+		bytes_read = drm_dp_dpcd_read(ctrl->init_data.drm_aux,
+				rxstatus_dpcd_offset, &buf, bytes_to_read);
+		if (bytes_read != bytes_to_read) {
+			DP_ERR("rxstatus read failed\n");
+			rc = bytes_read;
+			goto error;
+		}
+		*rx_status = buf;
+		DP_DEBUG("rx_status=0x%x\n", *rx_status);
+	}
+
+error:
+	return rc;
+}
+
+static int dp_hdcp2p2_cp_irq(void *input)
+{
+	int rc, retries = 15;
+	struct dp_hdcp2p2_ctrl *ctrl = input;
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
+
+	rc = dp_hdcp2p2_valid_handle(ctrl);
+	if (rc)
+		return rc;
+
+	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTH_FAIL ||
+		atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+		DP_DEBUG("invalid hdcp state\n");
+		return -EINVAL;
+	}
+
+	ctrl->sink_rx_status = 0;
+	rc = dp_hdcp2p2_read_rx_status(ctrl, &ctrl->sink_rx_status);
+	if (rc) {
+		DP_ERR("failed to read rx status\n");
+		return rc;
+	}
+
+	DP_DEBUG("sink_rx_status=0x%x\n", ctrl->sink_rx_status);
+
+	if (!ctrl->sink_rx_status) {
+		DP_DEBUG("not a hdcp 2.2 irq\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Wait for link to be transitioned to polling mode. This wait
+	 * should be done in this CP_IRQ handler and NOT in the event thread
+	 * as the transition to link polling happens in the event thread
+	 * as part of the wake up from the HDCP engine.
+	 *
+	 * One specific case where this sequence of event commonly happens
+	 * is when executing HDCP 2.3 CTS test 1B-09 with Unigraf UCD-400
+	 * test equipment (TE). As part of this test, the TE issues a CP-IRQ
+	 * right after the successful completion of the HDCP authentication
+	 * part 2. This CP-IRQ handler gets invoked even before the HDCP
+	 * state engine gets transitioned to the polling mode, which can
+	 * cause the test to fail as we would not read the
+	 * RepeaterAuth_Send_ReceiverID_List from the TE in response to the
+	 * CP_IRQ.
+	 *
+	 * Skip this wait when any of the fields in the abort mask is set.
+	 */
+	if (ctrl->sink_rx_status & ctrl->abort_mask)
+		goto exit;
+
+	while (!ctrl->polling && retries--)
+		msleep(20);
+
+exit:
+	kfifo_put(&ctrl->cmd_q, HDCP_TRANSPORT_CMD_LINK_CHECK);
+	wake_up(&ctrl->wait_q);
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT);
+
+	return 0;
+}
+
+static int dp_hdcp2p2_isr(void *input)
+{
+	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
+	int rc = 0;
+	struct dss_io_data *io;
+	struct dp_hdcp2p2_interrupts *intr;
+	u32 hdcp_int_val = 0;
+
+	if (!ctrl || !ctrl->init_data.dp_ahb) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	io = ctrl->init_data.dp_ahb;
+	intr = ctrl->intr;
+
+	while (intr && intr->reg) {
+		struct dp_hdcp2p2_int_set *int_set = intr->int_set;
+
+		hdcp_int_val = dp_read(io->base + intr->reg);
+
+		while (int_set && int_set->interrupt) {
+			if (hdcp_int_val & (int_set->interrupt >> 2)) {
+				DP_DEBUG("%s\n", int_set->name);
+
+				if (int_set->func)
+					int_set->func(ctrl);
+
+				dp_write(io->base + intr->reg, hdcp_int_val |
+					(int_set->interrupt >> 1));
+			}
+			int_set++;
+		}
+		intr++;
+	}
+end:
+	return rc;
+}
+
+static bool dp_hdcp2p2_supported(void *input)
+{
+	struct dp_hdcp2p2_ctrl *ctrl = input;
+	u32 const rxcaps_dpcd_offset = 0x6921d;
+	ssize_t bytes_read = 0;
+	u8 buf[DP_HDCP_RXCAPS_LENGTH];
+
+	DP_DEBUG("Checking sink capability\n");
+
+	bytes_read = drm_dp_dpcd_read(ctrl->init_data.drm_aux,
+			rxcaps_dpcd_offset, &buf, DP_HDCP_RXCAPS_LENGTH);
+	if (bytes_read != DP_HDCP_RXCAPS_LENGTH) {
+		DP_ERR("RxCaps read failed\n");
+		goto error;
+	}
+
+	DP_DEBUG("HDCP_CAPABLE=%lu\n", (buf[2] & BIT(1)) >> 1);
+	DP_DEBUG("VERSION=%d\n", buf[0]);
+
+	if ((buf[2] & BIT(1)) && (buf[0] == 0x2))
+		return true;
+error:
+	return false;
+}
+
+static int dp_hdcp2p2_change_streams(struct dp_hdcp2p2_ctrl *ctrl,
+		struct sde_hdcp_2x_wakeup_data *cdata)
+{
+	if (!ctrl || cdata->num_streams == 0 || !cdata->streams) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	if (!ctrl->lib_ctx) {
+		DP_ERR("HDCP library needs to be acquired\n");
+		return -EINVAL;
+	}
+
+	if (!ctrl->lib) {
+		DP_ERR("invalid lib ops data\n");
+		return -EINVAL;
+	}
+
+	cdata->context = ctrl->lib_ctx;
+	return ctrl->lib->wakeup(cdata);
+}
+
+
+static int dp_hdcp2p2_register_streams(void *input, u8 num_streams,
+			struct stream_info *streams)
+{
+	struct dp_hdcp2p2_ctrl *ctrl = input;
+	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_OPEN_STREAMS};
+
+	cdata.streams = streams;
+	cdata.num_streams = num_streams;
+	return dp_hdcp2p2_change_streams(ctrl, &cdata);
+}
+
+static int dp_hdcp2p2_deregister_streams(void *input, u8 num_streams,
+			struct stream_info *streams)
+{
+	struct dp_hdcp2p2_ctrl *ctrl = input;
+	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_CLOSE_STREAMS};
+
+	cdata.streams = streams;
+	cdata.num_streams = num_streams;
+	return dp_hdcp2p2_change_streams(ctrl, &cdata);
+}
+
+void sde_dp_hdcp2p2_deinit(void *input)
+{
+	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
+	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};
+
+	if (!ctrl) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	if (atomic_read(&ctrl->auth_state) != HDCP_STATE_AUTH_FAIL) {
+		cdata.cmd = HDCP_2X_CMD_STOP;
+		cdata.context = ctrl->lib_ctx;
+		dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
+	}
+
+	sde_hdcp_2x_deregister(ctrl->lib_ctx);
+
+	kthread_stop(ctrl->thread);
+
+	mutex_destroy(&ctrl->mutex);
+	mutex_destroy(&ctrl->msg_lock);
+	kfree(ctrl);
+}
+
+static int dp_hdcp2p2_main(void *data)
+{
+	struct dp_hdcp2p2_ctrl *ctrl = data;
+	enum hdcp_transport_wakeup_cmd cmd;
+
+	while (1) {
+		wait_event_idle(ctrl->wait_q,
+			!kfifo_is_empty(&ctrl->cmd_q) ||
+			kthread_should_stop() ||
+			kthread_should_park());
+
+		if (kthread_should_stop())
+			break;
+
+		if (kfifo_is_empty(&ctrl->cmd_q) && kthread_should_park()) {
+			kthread_parkme();
+			continue;
+		}
+
+		if (!kfifo_get(&ctrl->cmd_q, &cmd))
+			continue;
+
+		switch (cmd) {
+		case HDCP_TRANSPORT_CMD_SEND_MESSAGE:
+			dp_hdcp2p2_send_msg(ctrl);
+			break;
+		case HDCP_TRANSPORT_CMD_RECV_MESSAGE:
+			if (ctrl->rx_status)
+				ctrl->polling = true;
+			else
+				dp_hdcp2p2_recv_msg(ctrl);
+			break;
+		case HDCP_TRANSPORT_CMD_STATUS_SUCCESS:
+			dp_hdcp2p2_send_auth_status(ctrl);
+			break;
+		case HDCP_TRANSPORT_CMD_STATUS_FAILED:
+			dp_hdcp2p2_set_interrupts(ctrl, false);
+			dp_hdcp2p2_send_auth_status(ctrl);
+			break;
+		case HDCP_TRANSPORT_CMD_LINK_POLL:
+			ctrl->polling = true;
+			break;
+		case HDCP_TRANSPORT_CMD_LINK_CHECK:
+			dp_hdcp2p2_link_check(ctrl);
+			break;
+		case HDCP_TRANSPORT_CMD_AUTHENTICATE:
+			dp_hdcp2p2_start_auth(ctrl);
+			break;
+		default:
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static void dp_hdcp2p2_abort(void *input, bool abort)
+{
+	struct dp_hdcp2p2_ctrl *ctrl = input;
+
+	atomic_set(&ctrl->abort, abort);
+}
+
+void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data)
+{
+	int rc;
+	struct dp_hdcp2p2_ctrl *ctrl;
+	static struct sde_hdcp_ops ops = {
+		.isr = dp_hdcp2p2_isr,
+		.reauthenticate = dp_hdcp2p2_reauthenticate,
+		.authenticate = dp_hdcp2p2_authenticate,
+		.feature_supported = dp_hdcp2p2_feature_supported,
+		.force_encryption = dp_hdcp2p2_force_encryption,
+		.sink_support = dp_hdcp2p2_supported,
+		.set_mode = dp_hdcp2p2_register,
+		.on = dp_hdcp2p2_on,
+		.off = dp_hdcp2p2_off,
+		.abort = dp_hdcp2p2_abort,
+		.cp_irq = dp_hdcp2p2_cp_irq,
+		.register_streams = dp_hdcp2p2_register_streams,
+		.deregister_streams = dp_hdcp2p2_deregister_streams,
+	};
+
+	static struct hdcp_transport_ops client_ops = {
+		.wakeup = dp_hdcp2p2_wakeup,
+	};
+	static struct dp_hdcp2p2_int_set int_set1[] = {
+		{BIT(17), "authentication successful", NULL},
+		{BIT(20), "authentication failed", NULL},
+		{BIT(24), "encryption enabled", NULL},
+		{BIT(27), "encryption disabled", NULL},
+		{0},
+	};
+	static struct dp_hdcp2p2_int_set int_set2[] = {
+		{BIT(2),  "key fifo underflow", NULL},
+		{0},
+	};
+	static struct dp_hdcp2p2_interrupts intr[] = {
+		{DP_INTR_STATUS2, int_set1},
+		{DP_INTR_STATUS3, int_set2},
+		{0}
+	};
+	static struct sde_hdcp_2x_ops hdcp2x_ops;
+	struct sde_hdcp_2x_register_data register_data = {0};
+
+	if (!init_data || !init_data->cb_data ||
+			!init_data->notify_status || !init_data->drm_aux) {
+		DP_ERR("invalid input\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return ERR_PTR(-ENOMEM);
+
+	ctrl->init_data = *init_data;
+	ctrl->lib = &hdcp2x_ops;
+	ctrl->response.data = NULL;
+	ctrl->request.data = NULL;
+
+	ctrl->sink_status = SINK_DISCONNECTED;
+	ctrl->intr = intr;
+
+	INIT_KFIFO(ctrl->cmd_q);
+
+	init_waitqueue_head(&ctrl->wait_q);
+	atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE);
+
+	ctrl->ops = &ops;
+	mutex_init(&ctrl->mutex);
+	mutex_init(&ctrl->msg_lock);
+
+	register_data.hdcp_data = &ctrl->lib_ctx;
+	register_data.client_ops = &client_ops;
+	register_data.ops = &hdcp2x_ops;
+	register_data.client_data = ctrl;
+
+	rc = sde_hdcp_2x_register(&register_data);
+	if (rc) {
+		DP_ERR("Unable to register with HDCP 2.2 library\n");
+		goto error;
+	}
+
+	if (IS_ENABLED(CONFIG_HDCP_QSEECOM))
+		msm_hdcp_register_cb(init_data->msm_hdcp_dev, ctrl,
+				dp_hdcp2p2_min_level_change);
+
+	ctrl->thread = kthread_run(dp_hdcp2p2_main, ctrl, "dp_hdcp2p2");
+
+	if (IS_ERR(ctrl->thread)) {
+		DP_ERR("unable to start DP hdcp2p2 thread\n");
+		rc = PTR_ERR(ctrl->thread);
+		ctrl->thread = NULL;
+		goto error;
+	}
+
+	return ctrl;
+error:
+	kfree(ctrl);
+	return ERR_PTR(rc);
+}
+
+struct sde_hdcp_ops *sde_dp_hdcp2p2_get(void *input)
+{
+	return ((struct dp_hdcp2p2_ctrl *)input)->ops;
+}

+ 123 - 0
qcom/opensource/display-drivers/msm/dp/dp_hpd.c

@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include "dp_hpd.h"
+#include "dp_altmode.h"
+#include "dp_usbpd.h"
+#include "dp_gpio_hpd.h"
+#include "dp_lphw_hpd.h"
+#include "dp_debug.h"
+#include "dp_bridge_hpd.h"
+
+static void dp_hpd_host_init(struct dp_hpd *dp_hpd,
+		struct dp_catalog_hpd *catalog)
+{
+	if (!catalog) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+	catalog->config_hpd(catalog, true);
+}
+
+static void dp_hpd_host_deinit(struct dp_hpd *dp_hpd,
+		struct dp_catalog_hpd *catalog)
+{
+	if (!catalog) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+	catalog->config_hpd(catalog, false);
+}
+
+static void dp_hpd_isr(struct dp_hpd *dp_hpd)
+{
+}
+
+struct dp_hpd *dp_hpd_get(struct device *dev, struct dp_parser *parser,
+		struct dp_catalog_hpd *catalog,
+		struct dp_aux_bridge *aux_bridge,
+		struct dp_hpd_cb *cb)
+{
+	struct dp_hpd *dp_hpd = NULL;
+
+	if (aux_bridge && (aux_bridge->flag & DP_AUX_BRIDGE_HPD)) {
+		dp_hpd = dp_bridge_hpd_get(dev, cb, aux_bridge);
+		if (!IS_ERR(dp_hpd)) {
+			dp_hpd->type = DP_HPD_BRIDGE;
+			goto config;
+		}
+	}
+
+	dp_hpd = dp_lphw_hpd_get(dev, parser, catalog, cb);
+	if (!IS_ERR_OR_NULL(dp_hpd)) {
+		dp_hpd->type = DP_HPD_LPHW;
+		goto config;
+	}
+
+	dp_hpd = dp_gpio_hpd_get(dev, cb);
+	if (!IS_ERR_OR_NULL(dp_hpd)) {
+		dp_hpd->type = DP_HPD_GPIO;
+		goto config;
+	}
+
+	dp_hpd = dp_altmode_get(dev, cb);
+	if (!IS_ERR_OR_NULL(dp_hpd)) {
+		dp_hpd->type = DP_HPD_ALTMODE;
+		goto config;
+	}
+
+	dp_hpd = dp_usbpd_get(dev, cb);
+	if (!IS_ERR_OR_NULL(dp_hpd)) {
+		dp_hpd->type = DP_HPD_USBPD;
+		goto config;
+	}
+
+	DP_ERR("Failed to detect HPD type\n");
+	goto end;
+
+config:
+	if (!dp_hpd->host_init)
+		dp_hpd->host_init	= dp_hpd_host_init;
+	if (!dp_hpd->host_deinit)
+		dp_hpd->host_deinit	= dp_hpd_host_deinit;
+	if (!dp_hpd->isr)
+		dp_hpd->isr		= dp_hpd_isr;
+
+end:
+	return dp_hpd;
+}
+
+void dp_hpd_put(struct dp_hpd *dp_hpd)
+{
+	if (!dp_hpd)
+		return;
+
+	switch (dp_hpd->type) {
+	case DP_HPD_USBPD:
+		dp_usbpd_put(dp_hpd);
+		break;
+	case DP_HPD_ALTMODE:
+		dp_altmode_put(dp_hpd);
+		break;
+	case DP_HPD_GPIO:
+		dp_gpio_hpd_put(dp_hpd);
+		break;
+	case DP_HPD_LPHW:
+		dp_lphw_hpd_put(dp_hpd);
+		break;
+	case DP_HPD_BRIDGE:
+		dp_bridge_hpd_put(dp_hpd);
+		break;
+	default:
+		DP_ERR("unknown hpd type %d\n", dp_hpd->type);
+		break;
+	}
+}

+ 122 - 0
qcom/opensource/display-drivers/msm/dp/dp_hpd.h

@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _DP_HPD_H_
+#define _DP_HPD_H_
+
+#include <linux/types.h>
+#include "dp_parser.h"
+#include "dp_catalog.h"
+#include "dp_aux_bridge.h"
+
+struct device;
+
+/**
+ * enum dp_hpd_plug_orientation - plug orientation
+ * @ORIENTATION_NONE:	Undefined or unspecified
+ * @ORIENTATION_CC1:	CC1
+ * @ORIENTATION_CC2:	CC2
+ */
+enum dp_hpd_plug_orientation {
+	ORIENTATION_NONE,
+	ORIENTATION_CC1,
+	ORIENTATION_CC2,
+};
+
+/**
+ * enum dp_hpd_type - dp hpd type
+ * @DP_HPD_ALTMODE: AltMode over G-Link based HPD
+ * @DP_HPD_USBPD:   USB type-c based HPD
+ * @DP_HPD_GPIO:    GPIO based HPD
+ * @DP_HPD_LPHW:    LPHW based HPD
+ * @DP_HPD_BRIDGE:  External bridge HPD
+ */
+
+enum dp_hpd_type {
+	DP_HPD_ALTMODE,
+	DP_HPD_USBPD,
+	DP_HPD_GPIO,
+	DP_HPD_LPHW,
+	DP_HPD_BRIDGE,
+};
+
+/**
+ * struct dp_hpd_cb - callback functions provided by the client
+ *
+ * @configure: called when dp connection is ready.
+ * @disconnect: notify the cable disconnect event.
+ * @attention: notify any attention message event.
+ */
+struct dp_hpd_cb {
+	int (*configure)(struct device *dev);
+	int (*disconnect)(struct device *dev);
+	int (*attention)(struct device *dev);
+};
+
+/**
+ * struct dp_hpd - DisplayPort HPD status
+ *
+ * @type: type of HPD
+ * @orientation: plug orientation configuration, USBPD type only.
+ * @hpd_high: Hot Plug Detect signal is high.
+ * @hpd_irq: Change in the status since last message
+ * @alt_mode_cfg_done: bool to specify alt mode status
+ * @multi_func: multi-function preferred, USBPD type only
+ * @peer_usb_com: downstream supports usb data communication
+ * @force_multi_func: force multi-function preferred
+ * @isr: event interrupt, BUILTIN and LPHW type only
+ * @register_hpd: register hardware callback
+ * @host_init: source or host side setup for hpd
+ * @host_deinit: source or host side de-initializations
+ * @simulate_connect: simulate disconnect or connect for debug mode
+ * @simulate_attention: simulate attention messages for debug mode
+ * @wakeup_phy: wakeup USBPD phy layer
+ */
+struct dp_hpd {
+	enum dp_hpd_type type;
+	u32 orientation;
+	bool hpd_high;
+	bool hpd_irq;
+	bool alt_mode_cfg_done;
+	bool multi_func;
+	bool peer_usb_comm;
+	bool force_multi_func;
+
+	void (*isr)(struct dp_hpd *dp_hpd);
+	int (*register_hpd)(struct dp_hpd *dp_hpd);
+	void (*host_init)(struct dp_hpd *hpd, struct dp_catalog_hpd *catalog);
+	void (*host_deinit)(struct dp_hpd *hpd, struct dp_catalog_hpd *catalog);
+	int (*simulate_connect)(struct dp_hpd *dp_hpd, bool hpd);
+	int (*simulate_attention)(struct dp_hpd *dp_hpd, int vdo);
+	void (*wakeup_phy)(struct dp_hpd *dp_hpd, bool wakeup);
+};
+
+/**
+ * dp_hpd_get() - configure and get the DisplayPlot HPD module data
+ *
+ * @dev: device instance of the caller
+ * @parser: pointer to DP parser module
+ * @catalog: pointer to DP catalog module
+ * @aux_bridge: handle for aux_bridge driver data
+ * @cb: callback function for HPD response
+ * return: pointer to allocated hpd module data
+ *
+ * This function sets up the hpd module
+ */
+struct dp_hpd *dp_hpd_get(struct device *dev, struct dp_parser *parser,
+		struct dp_catalog_hpd *catalog,
+		struct dp_aux_bridge *aux_bridge,
+		struct dp_hpd_cb *cb);
+
+/**
+ * dp_hpd_put()
+ *
+ * Cleans up dp_hpd instance
+ *
+ * @dp_hpd: instance of dp_hpd
+ */
+void dp_hpd_put(struct dp_hpd *dp_hpd);
+
+#endif /* _DP_HPD_H_ */

+ 1691 - 0
qcom/opensource/display-drivers/msm/dp/dp_link.c

@@ -0,0 +1,1691 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (c) 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 "dp_link.h"
+#include "dp_panel.h"
+#include "dp_debug.h"
+
+enum dynamic_range {
+	DP_DYNAMIC_RANGE_RGB_VESA = 0x00,
+	DP_DYNAMIC_RANGE_RGB_CEA = 0x01,
+	DP_DYNAMIC_RANGE_UNKNOWN = 0xFFFFFFFF,
+};
+
+enum audio_sample_rate {
+	AUDIO_SAMPLE_RATE_32_KHZ	= 0x00,
+	AUDIO_SAMPLE_RATE_44_1_KHZ	= 0x01,
+	AUDIO_SAMPLE_RATE_48_KHZ	= 0x02,
+	AUDIO_SAMPLE_RATE_88_2_KHZ	= 0x03,
+	AUDIO_SAMPLE_RATE_96_KHZ	= 0x04,
+	AUDIO_SAMPLE_RATE_176_4_KHZ	= 0x05,
+	AUDIO_SAMPLE_RATE_192_KHZ	= 0x06,
+};
+
+enum audio_pattern_type {
+	AUDIO_TEST_PATTERN_OPERATOR_DEFINED	= 0x00,
+	AUDIO_TEST_PATTERN_SAWTOOTH		= 0x01,
+};
+
+struct dp_link_request {
+	u32 test_requested;
+	u32 test_link_rate;
+	u32 test_lane_count;
+};
+
+struct dp_link_private {
+	u32 prev_sink_count;
+	struct device *dev;
+	struct dp_aux *aux;
+	struct dp_link dp_link;
+
+	struct dp_link_request request;
+	u8 link_status[DP_LINK_STATUS_SIZE];
+};
+
+static char *dp_link_get_audio_test_pattern(u32 pattern)
+{
+	switch (pattern) {
+	case AUDIO_TEST_PATTERN_OPERATOR_DEFINED:
+		return DP_LINK_ENUM_STR(AUDIO_TEST_PATTERN_OPERATOR_DEFINED);
+	case AUDIO_TEST_PATTERN_SAWTOOTH:
+		return DP_LINK_ENUM_STR(AUDIO_TEST_PATTERN_SAWTOOTH);
+	default:
+		return "unknown";
+	}
+}
+
+static char *dp_link_get_audio_sample_rate(u32 rate)
+{
+	switch (rate) {
+	case AUDIO_SAMPLE_RATE_32_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_32_KHZ);
+	case AUDIO_SAMPLE_RATE_44_1_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_44_1_KHZ);
+	case AUDIO_SAMPLE_RATE_48_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_48_KHZ);
+	case AUDIO_SAMPLE_RATE_88_2_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_88_2_KHZ);
+	case AUDIO_SAMPLE_RATE_96_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_96_KHZ);
+	case AUDIO_SAMPLE_RATE_176_4_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_176_4_KHZ);
+	case AUDIO_SAMPLE_RATE_192_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_192_KHZ);
+	default:
+		return "unknown";
+	}
+}
+
+static int dp_link_get_period(struct dp_link_private *link, int const addr)
+{
+	int ret = 0;
+	u8 bp;
+	u8 data;
+	u32 const param_len = 0x1;
+	u32 const max_audio_period = 0xA;
+
+	/* TEST_AUDIO_PERIOD_CH_XX */
+	if (drm_dp_dpcd_read(link->aux->drm_aux, addr, &bp,
+		param_len) < param_len) {
+		DP_ERR("failed to read test_audio_period (0x%x)\n", addr);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	data = bp;
+
+	/* Period - Bits 3:0 */
+	data = data & 0xF;
+	if ((int)data > max_audio_period) {
+		DP_ERR("invalid test_audio_period_ch_1 = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	ret = data;
+exit:
+	return ret;
+}
+
+static int dp_link_parse_audio_channel_period(struct dp_link_private *link)
+{
+	int ret = 0;
+	struct dp_link_test_audio *req = &link->dp_link.test_audio;
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH1);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_1 = ret;
+	DP_DEBUG("test_audio_period_ch_1 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH2);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_2 = ret;
+	DP_DEBUG("test_audio_period_ch_2 = 0x%x\n", ret);
+
+	/* TEST_AUDIO_PERIOD_CH_3 (Byte 0x275) */
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH3);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_3 = ret;
+	DP_DEBUG("test_audio_period_ch_3 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH4);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_4 = ret;
+	DP_DEBUG("test_audio_period_ch_4 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH5);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_5 = ret;
+	DP_DEBUG("test_audio_period_ch_5 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH6);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_6 = ret;
+	DP_DEBUG("test_audio_period_ch_6 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH7);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_7 = ret;
+	DP_DEBUG("test_audio_period_ch_7 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH8);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_8 = ret;
+	DP_DEBUG("test_audio_period_ch_8 = 0x%x\n", ret);
+exit:
+	return ret;
+}
+
+static int dp_link_parse_audio_pattern_type(struct dp_link_private *link)
+{
+	int ret = 0;
+	u8 bp;
+	u8 data;
+	int rlen;
+	int const param_len = 0x1;
+	int const max_audio_pattern_type = 0x1;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux,
+		DP_TEST_AUDIO_PATTERN_TYPE, &bp, param_len);
+	if (rlen < param_len) {
+		DP_ERR("failed to read link audio mode data\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+
+	/* Audio Pattern Type - Bits 7:0 */
+	if ((int)data > max_audio_pattern_type) {
+		DP_ERR("invalid audio pattern type = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->dp_link.test_audio.test_audio_pattern_type = data;
+	DP_DEBUG("audio pattern type = %s\n",
+			dp_link_get_audio_test_pattern(data));
+exit:
+	return ret;
+}
+
+static int dp_link_parse_audio_mode(struct dp_link_private *link)
+{
+	int ret = 0;
+	u8 bp;
+	u8 data;
+	int rlen;
+	int const param_len = 0x1;
+	int const max_audio_sampling_rate = 0x6;
+	int const max_audio_channel_count = 0x8;
+	int sampling_rate = 0x0;
+	int channel_count = 0x0;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_AUDIO_MODE,
+			&bp, param_len);
+	if (rlen < param_len) {
+		DP_ERR("failed to read link audio mode data\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+
+	/* Sampling Rate - Bits 3:0 */
+	sampling_rate = data & 0xF;
+	if (sampling_rate > max_audio_sampling_rate) {
+		DP_ERR("sampling rate (0x%x) greater than max (0x%x)\n",
+				sampling_rate, max_audio_sampling_rate);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/* Channel Count - Bits 7:4 */
+	channel_count = ((data & 0xF0) >> 4) + 1;
+	if (channel_count > max_audio_channel_count) {
+		DP_ERR("channel_count (0x%x) greater than max (0x%x)\n",
+				channel_count, max_audio_channel_count);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->dp_link.test_audio.test_audio_sampling_rate = sampling_rate;
+	link->dp_link.test_audio.test_audio_channel_count = channel_count;
+	DP_DEBUG("sampling_rate = %s, channel_count = 0x%x\n",
+		dp_link_get_audio_sample_rate(sampling_rate), channel_count);
+exit:
+	return ret;
+}
+
+/**
+ * dp_parse_audio_pattern_params() - parses audio pattern parameters from DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the audio link pattern parameters.
+ */
+static int dp_link_parse_audio_pattern_params(struct dp_link_private *link)
+{
+	int ret = 0;
+
+	ret = dp_link_parse_audio_mode(link);
+	if (ret)
+		goto exit;
+
+	ret = dp_link_parse_audio_pattern_type(link);
+	if (ret)
+		goto exit;
+
+	ret = dp_link_parse_audio_channel_period(link);
+
+exit:
+	return ret;
+}
+
+/**
+ * dp_link_is_video_pattern_valid() - validates the video pattern
+ * @pattern: video pattern requested by the sink
+ *
+ * Returns true if the requested video pattern is supported.
+ */
+static bool dp_link_is_video_pattern_valid(u32 pattern)
+{
+	switch (pattern) {
+	case DP_NO_TEST_PATTERN:
+	case DP_COLOR_RAMP:
+	case DP_BLACK_AND_WHITE_VERTICAL_LINES:
+	case DP_COLOR_SQUARE:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static char *dp_link_video_pattern_to_string(u32 test_video_pattern)
+{
+	switch (test_video_pattern) {
+	case DP_NO_TEST_PATTERN:
+		return DP_LINK_ENUM_STR(DP_NO_TEST_PATTERN);
+	case DP_COLOR_RAMP:
+		return DP_LINK_ENUM_STR(DP_COLOR_RAMP);
+	case DP_BLACK_AND_WHITE_VERTICAL_LINES:
+		return DP_LINK_ENUM_STR(DP_BLACK_AND_WHITE_VERTICAL_LINES);
+	case DP_COLOR_SQUARE:
+		return DP_LINK_ENUM_STR(DP_COLOR_SQUARE);
+	default:
+		return "unknown";
+	}
+}
+
+/**
+ * dp_link_is_dynamic_range_valid() - validates the dynamic range
+ * @bit_depth: the dynamic range value to be checked
+ *
+ * Returns true if the dynamic range value is supported.
+ */
+static bool dp_link_is_dynamic_range_valid(u32 dr)
+{
+	switch (dr) {
+	case DP_DYNAMIC_RANGE_RGB_VESA:
+	case DP_DYNAMIC_RANGE_RGB_CEA:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static char *dp_link_dynamic_range_to_string(u32 dr)
+{
+	switch (dr) {
+	case DP_DYNAMIC_RANGE_RGB_VESA:
+		return DP_LINK_ENUM_STR(DP_DYNAMIC_RANGE_RGB_VESA);
+	case DP_DYNAMIC_RANGE_RGB_CEA:
+		return DP_LINK_ENUM_STR(DP_DYNAMIC_RANGE_RGB_CEA);
+	case DP_DYNAMIC_RANGE_UNKNOWN:
+	default:
+		return "unknown";
+	}
+}
+
+/**
+ * dp_link_is_bit_depth_valid() - validates the bit depth requested
+ * @bit_depth: bit depth requested by the sink
+ *
+ * Returns true if the requested bit depth is supported.
+ */
+static bool dp_link_is_bit_depth_valid(u32 tbd)
+{
+	/* DP_TEST_VIDEO_PATTERN_NONE is treated as invalid */
+	switch (tbd) {
+	case DP_TEST_BIT_DEPTH_6:
+	case DP_TEST_BIT_DEPTH_8:
+	case DP_TEST_BIT_DEPTH_10:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static char *dp_link_bit_depth_to_string(u32 tbd)
+{
+	switch (tbd) {
+	case DP_TEST_BIT_DEPTH_6:
+		return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_6);
+	case DP_TEST_BIT_DEPTH_8:
+		return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_8);
+	case DP_TEST_BIT_DEPTH_10:
+		return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_10);
+	case DP_TEST_BIT_DEPTH_UNKNOWN:
+	default:
+		return "unknown";
+	}
+}
+
+static int dp_link_parse_timing_params1(struct dp_link_private *link,
+	int const addr, int const len, u32 *val)
+{
+	u8 bp[2];
+	int rlen;
+
+	if (len < 2)
+		return -EINVAL;
+
+	/* Read the requested video link pattern (Byte 0x221). */
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, addr, bp, len);
+	if (rlen < len) {
+		DP_ERR("failed to read 0x%x\n", addr);
+		return -EINVAL;
+	}
+
+	*val = bp[1] | (bp[0] << 8);
+
+	return 0;
+}
+
+static int dp_link_parse_timing_params2(struct dp_link_private *link,
+	int const addr, int const len, u32 *val1, u32 *val2)
+{
+	u8 bp[2];
+	int rlen;
+
+	if (len < 2)
+		return -EINVAL;
+
+	/* Read the requested video link pattern (Byte 0x221). */
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, addr, bp, len);
+	if (rlen < len) {
+		DP_ERR("failed to read 0x%x\n", addr);
+		return -EINVAL;
+	}
+
+	*val1 = (bp[0] & BIT(7)) >> 7;
+	*val2 = bp[1] | ((bp[0] & 0x7F) << 8);
+
+	return 0;
+}
+
+static int dp_link_parse_timing_params3(struct dp_link_private *link,
+	int const addr, u32 *val)
+{
+	u8 bp;
+	u32 len = 1;
+	int rlen;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, addr, &bp, len);
+	if (rlen < 1) {
+		DP_ERR("failed to read 0x%x\n", addr);
+		return -EINVAL;
+	}
+	*val = bp;
+
+	return 0;
+}
+
+/**
+ * dp_parse_video_pattern_params() - parses video pattern parameters from DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the video link pattern and the link
+ * bit depth requested by the sink and, and if the values parsed are valid.
+ */
+static int dp_link_parse_video_pattern_params(struct dp_link_private *link)
+{
+	int ret = 0;
+	int rlen;
+	u8 bp;
+	u8 data;
+	u32 dyn_range;
+	int const param_len = 0x1;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_PATTERN,
+			&bp, param_len);
+	if (rlen < param_len) {
+		DP_ERR("failed to read link video pattern\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+
+	if (!dp_link_is_video_pattern_valid(data)) {
+		DP_ERR("invalid link video pattern = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->dp_link.test_video.test_video_pattern = data;
+	DP_DEBUG("link video pattern = 0x%x (%s)\n",
+		link->dp_link.test_video.test_video_pattern,
+		dp_link_video_pattern_to_string(
+			link->dp_link.test_video.test_video_pattern));
+
+	/* Read the requested color bit depth and dynamic range (Byte 0x232) */
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_MISC0,
+			&bp, param_len);
+	if (rlen < param_len) {
+		DP_ERR("failed to read link bit depth\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+
+	/* Dynamic Range */
+	dyn_range = (data & DP_TEST_DYNAMIC_RANGE_CEA) >> 3;
+	if (!dp_link_is_dynamic_range_valid(dyn_range)) {
+		DP_ERR("invalid link dynamic range = 0x%x\n", dyn_range);
+		ret = -EINVAL;
+		goto exit;
+	}
+	link->dp_link.test_video.test_dyn_range = dyn_range;
+	DP_DEBUG("link dynamic range = 0x%x (%s)\n",
+		link->dp_link.test_video.test_dyn_range,
+		dp_link_dynamic_range_to_string(
+			link->dp_link.test_video.test_dyn_range));
+
+	/* Color bit depth */
+	data &= DP_TEST_BIT_DEPTH_MASK;
+	if (!dp_link_is_bit_depth_valid(data)) {
+		DP_ERR("invalid link bit depth = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->dp_link.test_video.test_bit_depth = data;
+	DP_DEBUG("link bit depth = 0x%x (%s)\n",
+		link->dp_link.test_video.test_bit_depth,
+		dp_link_bit_depth_to_string(
+		link->dp_link.test_video.test_bit_depth));
+
+	/* resolution timing params */
+	ret = dp_link_parse_timing_params1(link, DP_TEST_H_TOTAL_HI, 2,
+			&link->dp_link.test_video.test_h_total);
+	if (ret) {
+		DP_ERR("failed to parse test_h_total (DP_TEST_H_TOTAL_HI)\n");
+		goto exit;
+	}
+	DP_DEBUG("TEST_H_TOTAL = %d\n", link->dp_link.test_video.test_h_total);
+
+	ret = dp_link_parse_timing_params1(link, DP_TEST_V_TOTAL_HI, 2,
+			&link->dp_link.test_video.test_v_total);
+	if (ret) {
+		DP_ERR("failed to parse test_v_total (DP_TEST_V_TOTAL_HI)\n");
+		goto exit;
+	}
+	DP_DEBUG("TEST_V_TOTAL = %d\n", link->dp_link.test_video.test_v_total);
+
+	ret = dp_link_parse_timing_params1(link, DP_TEST_H_START_HI, 2,
+			&link->dp_link.test_video.test_h_start);
+	if (ret) {
+		DP_ERR("failed to parse test_h_start (DP_TEST_H_START_HI)\n");
+		goto exit;
+	}
+	DP_DEBUG("TEST_H_START = %d\n", link->dp_link.test_video.test_h_start);
+
+	ret = dp_link_parse_timing_params1(link, DP_TEST_V_START_HI, 2,
+			&link->dp_link.test_video.test_v_start);
+	if (ret) {
+		DP_ERR("failed to parse test_v_start (DP_TEST_V_START_HI)\n");
+		goto exit;
+	}
+	DP_DEBUG("TEST_V_START = %d\n", link->dp_link.test_video.test_v_start);
+
+	ret = dp_link_parse_timing_params2(link, DP_TEST_HSYNC_HI, 2,
+			&link->dp_link.test_video.test_hsync_pol,
+			&link->dp_link.test_video.test_hsync_width);
+	if (ret) {
+		DP_ERR("failed to parse (DP_TEST_HSYNC_HI)\n");
+		goto exit;
+	}
+	DP_DEBUG("TEST_HSYNC_POL = %d\n",
+		link->dp_link.test_video.test_hsync_pol);
+	DP_DEBUG("TEST_HSYNC_WIDTH = %d\n",
+		link->dp_link.test_video.test_hsync_width);
+
+	ret = dp_link_parse_timing_params2(link, DP_TEST_VSYNC_HI, 2,
+			&link->dp_link.test_video.test_vsync_pol,
+			&link->dp_link.test_video.test_vsync_width);
+	if (ret) {
+		DP_ERR("failed to parse (DP_TEST_VSYNC_HI)\n");
+		goto exit;
+	}
+	DP_DEBUG("TEST_VSYNC_POL = %d\n",
+		link->dp_link.test_video.test_vsync_pol);
+	DP_DEBUG("TEST_VSYNC_WIDTH = %d\n",
+		link->dp_link.test_video.test_vsync_width);
+
+	ret = dp_link_parse_timing_params1(link, DP_TEST_H_WIDTH_HI, 2,
+			&link->dp_link.test_video.test_h_width);
+	if (ret) {
+		DP_ERR("failed to parse test_h_width (DP_TEST_H_WIDTH_HI)\n");
+		goto exit;
+	}
+	DP_DEBUG("TEST_H_WIDTH = %d\n", link->dp_link.test_video.test_h_width);
+
+	ret = dp_link_parse_timing_params1(link, DP_TEST_V_HEIGHT_HI, 2,
+			&link->dp_link.test_video.test_v_height);
+	if (ret) {
+		DP_ERR("failed to parse test_v_height (DP_TEST_V_HEIGHT_HI)\n");
+		goto exit;
+	}
+	DP_DEBUG("TEST_V_HEIGHT = %d\n",
+		link->dp_link.test_video.test_v_height);
+
+	ret = dp_link_parse_timing_params3(link, DP_TEST_MISC1,
+		&link->dp_link.test_video.test_rr_d);
+	link->dp_link.test_video.test_rr_d &= DP_TEST_REFRESH_DENOMINATOR;
+	if (ret) {
+		DP_ERR("failed to parse test_rr_d (DP_TEST_MISC1)\n");
+		goto exit;
+	}
+	DP_DEBUG("TEST_REFRESH_DENOMINATOR = %d\n",
+		link->dp_link.test_video.test_rr_d);
+
+	ret = dp_link_parse_timing_params3(link, DP_TEST_REFRESH_RATE_NUMERATOR,
+		&link->dp_link.test_video.test_rr_n);
+	if (ret) {
+		DP_ERR("failed to parse test_rr_n (DP_TEST_REFRESH_RATE_NUMERATOR)\n");
+		goto exit;
+	}
+	DP_DEBUG("TEST_REFRESH_NUMERATOR = %d\n",
+		link->dp_link.test_video.test_rr_n);
+exit:
+	return ret;
+}
+
+/**
+ * dp_link_parse_link_training_params() - parses link training parameters from
+ * DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the link rate (Byte 0x219) and lane
+ * count (Byte 0x220), and if these values parse are valid.
+ */
+static int dp_link_parse_link_training_params(struct dp_link_private *link)
+{
+	u8 bp;
+	u8 data;
+	int ret = 0;
+	int rlen;
+	int const param_len = 0x1;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_LINK_RATE,
+			&bp, param_len);
+	if (rlen < param_len) {
+		DP_ERR("failed to read link rate\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+
+	if (!is_link_rate_valid(data)) {
+		DP_ERR("invalid link rate = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->request.test_link_rate = data;
+	DP_DEBUG("link rate = 0x%x\n", link->request.test_link_rate);
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_LANE_COUNT,
+			&bp, param_len);
+	if (rlen < param_len) {
+		DP_ERR("failed to read lane count\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+	data &= 0x1F;
+
+	if (!is_lane_count_valid(data)) {
+		DP_ERR("invalid lane count = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->request.test_lane_count = data;
+	DP_DEBUG("lane count = 0x%x\n", link->request.test_lane_count);
+exit:
+	return ret;
+}
+
+static bool dp_link_is_phy_test_pattern_supported(u32 phy_test_pattern_sel)
+{
+	switch (phy_test_pattern_sel) {
+	case DP_PHY_TEST_PATTERN_NONE:
+	case DP_PHY_TEST_PATTERN_D10_2:
+	case DP_PHY_TEST_PATTERN_ERROR_COUNT:
+	case DP_PHY_TEST_PATTERN_PRBS7:
+	case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
+	case DP_PHY_TEST_PATTERN_CP2520:
+	case DP_PHY_TEST_PATTERN_CP2520_3:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/**
+ * dp_parse_phy_test_params() - parses the phy link parameters
+ * @link: Display Port Driver data
+ *
+ * Parses the DPCD (Byte 0x248) for the DP PHY link pattern that is being
+ * requested.
+ */
+static int dp_link_parse_phy_test_params(struct dp_link_private *link)
+{
+	u8 bp;
+	u8 data;
+	int rlen;
+	int const param_len = 0x1;
+	int ret = 0;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_PHY_TEST_PATTERN,
+			&bp, param_len);
+	if (rlen < param_len) {
+		DP_ERR("failed to read phy link pattern\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	data = bp;
+
+	link->dp_link.phy_params.phy_test_pattern_sel = data;
+
+	DP_DEBUG("phy_test_pattern_sel = %s\n",
+			dp_link_get_phy_test_pattern(data));
+
+	if (!dp_link_is_phy_test_pattern_supported(data))
+		ret = -EINVAL;
+end:
+	return ret;
+}
+
+/**
+ * dp_link_is_video_audio_test_requested() - checks for audio/video link request
+ * @link: link requested by the sink
+ *
+ * Returns true if the requested link is a permitted audio/video link.
+ */
+static bool dp_link_is_video_audio_test_requested(u32 link)
+{
+	return (link == DP_TEST_LINK_VIDEO_PATTERN) ||
+		(link == (DP_TEST_LINK_AUDIO_PATTERN |
+		DP_TEST_LINK_VIDEO_PATTERN)) ||
+		(link == DP_TEST_LINK_AUDIO_PATTERN) ||
+		(link == (DP_TEST_LINK_AUDIO_PATTERN |
+		DP_TEST_LINK_AUDIO_DISABLED_VIDEO));
+}
+
+/**
+ * dp_link_supported() - checks if link requested by sink is supported
+ * @test_requested: link requested by the sink
+ *
+ * Returns true if the requested link is supported.
+ */
+static bool dp_link_is_test_supported(u32 test_requested)
+{
+	return (test_requested == DP_TEST_LINK_TRAINING) ||
+		(test_requested == DP_TEST_LINK_EDID_READ) ||
+		(test_requested == DP_TEST_LINK_PHY_TEST_PATTERN) ||
+		dp_link_is_video_audio_test_requested(test_requested);
+}
+
+static bool dp_link_is_test_edid_read(struct dp_link_private *link)
+{
+	return (link->request.test_requested == DP_TEST_LINK_EDID_READ);
+}
+
+/**
+ * dp_sink_parse_test_request() - parses link request parameters from sink
+ * @link: Display Port Driver data
+ *
+ * Parses the DPCD to check if an automated link is requested (Byte 0x201),
+ * and what type of link automation is being requested (Byte 0x218).
+ */
+static int dp_link_parse_request(struct dp_link_private *link)
+{
+	int ret = 0;
+	u8 bp;
+	u8 data;
+	int rlen;
+	u32 const param_len = 0x1;
+
+	/**
+	 * Read the device service IRQ vector (Byte 0x201) to determine
+	 * whether an automated link has been requested by the sink.
+	 */
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux,
+		DP_DEVICE_SERVICE_IRQ_VECTOR, &bp, param_len);
+	if (rlen < param_len) {
+		DP_ERR("aux read failed\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	data = bp;
+
+	if (!(data & DP_AUTOMATED_TEST_REQUEST))
+		return 0;
+
+	/**
+	 * Read the link request byte (Byte 0x218) to determine what type
+	 * of automated link has been requested by the sink.
+	 */
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_REQUEST,
+			&bp, param_len);
+	if (rlen < param_len) {
+		DP_ERR("aux read failed\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	data = bp;
+
+	if (!dp_link_is_test_supported(data)) {
+		DP_DEBUG("link 0x%x not supported\n", data);
+		goto end;
+	}
+
+	link->request.test_requested = data;
+
+	if (link->request.test_requested == DP_TEST_LINK_PHY_TEST_PATTERN) {
+		ret = dp_link_parse_phy_test_params(link);
+		if (ret)
+			goto end;
+		ret = dp_link_parse_link_training_params(link);
+	}
+
+	if (link->request.test_requested == DP_TEST_LINK_TRAINING)
+		ret = dp_link_parse_link_training_params(link);
+
+	if (dp_link_is_video_audio_test_requested(
+			link->request.test_requested)) {
+		ret = dp_link_parse_video_pattern_params(link);
+		if (ret)
+			goto end;
+
+		ret = dp_link_parse_audio_pattern_params(link);
+	}
+end:
+	/**
+	 * Send a DP_TEST_ACK if all link parameters are valid, otherwise send
+	 * a DP_TEST_NAK.
+	 */
+	if (ret) {
+		link->dp_link.test_response = DP_TEST_NAK;
+	} else {
+		if (!dp_link_is_test_edid_read(link))
+			link->dp_link.test_response = DP_TEST_ACK;
+		else
+			link->dp_link.test_response =
+				DP_TEST_EDID_CHECKSUM_WRITE;
+	}
+
+	return ret;
+}
+
+/**
+ * dp_link_parse_sink_count() - parses the sink count
+ *
+ * Parses the DPCD to check if there is an update to the sink count
+ * (Byte 0x200), and whether all the sink devices connected have Content
+ * Protection enabled.
+ */
+static int dp_link_parse_sink_count(struct dp_link *dp_link)
+{
+	int rlen;
+	int const param_len = 0x1;
+	struct dp_link_private *link = container_of(dp_link,
+			struct dp_link_private, dp_link);
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_SINK_COUNT,
+			&link->dp_link.sink_count.count, param_len);
+	if (rlen < param_len) {
+		DP_ERR("failed to read sink count\n");
+		return -EINVAL;
+	}
+
+	link->dp_link.sink_count.cp_ready =
+		link->dp_link.sink_count.count & DP_SINK_CP_READY;
+	/* BIT 7, BIT 5:0 */
+	link->dp_link.sink_count.count =
+		DP_GET_SINK_COUNT(link->dp_link.sink_count.count);
+
+	DP_DEBUG("sink_count = 0x%x, cp_ready = 0x%x\n",
+		link->dp_link.sink_count.count,
+		link->dp_link.sink_count.cp_ready);
+	return 0;
+}
+
+static void dp_link_parse_sink_status_field(struct dp_link_private *link)
+{
+	int len = 0;
+
+	link->prev_sink_count = link->dp_link.sink_count.count;
+	dp_link_parse_sink_count(&link->dp_link);
+
+	len = drm_dp_dpcd_read_link_status(link->aux->drm_aux,
+		link->link_status);
+	if (len < DP_LINK_STATUS_SIZE)
+		DP_ERR("DP link status read failed\n");
+	dp_link_parse_request(link);
+}
+
+static bool dp_link_is_link_training_requested(struct dp_link_private *link)
+{
+	return (link->request.test_requested == DP_TEST_LINK_TRAINING);
+}
+
+/**
+ * dp_link_process_link_training_request() - processes new training requests
+ * @link: Display Port link data
+ *
+ * This function will handle new link training requests that are initiated by
+ * the sink. In particular, it will update the requested lane count and link
+ * link rate, and then trigger the link retraining procedure.
+ *
+ * The function will return 0 if a link training request has been processed,
+ * otherwise it will return -EINVAL.
+ */
+static int dp_link_process_link_training_request(struct dp_link_private *link)
+{
+	if (!dp_link_is_link_training_requested(link))
+		return -EINVAL;
+
+	DP_DEBUG("%s link rate = 0x%x, lane count = 0x%x\n",
+			dp_link_get_test_name(DP_TEST_LINK_TRAINING),
+			link->request.test_link_rate,
+			link->request.test_lane_count);
+
+	link->dp_link.link_params.lane_count = link->request.test_lane_count;
+	link->dp_link.link_params.bw_code = link->request.test_link_rate;
+
+	return 0;
+}
+
+static void dp_link_send_test_response(struct dp_link *dp_link)
+{
+	struct dp_link_private *link = NULL;
+	u32 const response_len = 0x1;
+
+	if (!dp_link) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	drm_dp_dpcd_write(link->aux->drm_aux, DP_TEST_RESPONSE,
+			&dp_link->test_response, response_len);
+}
+
+static int dp_link_psm_config(struct dp_link *dp_link,
+	struct drm_dp_link *link_info, bool enable)
+{
+	struct dp_link_private *link = NULL;
+	int ret = 0;
+
+	if (!dp_link) {
+		DP_ERR("invalid params\n");
+		return -EINVAL;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	if (enable)
+		ret = dp_link_power_down(link->aux->drm_aux, link_info);
+	else
+		ret = dp_link_power_up(link->aux->drm_aux, link_info);
+
+	if (ret)
+		DP_ERR("Failed to %s low power mode\n",
+			(enable ? "enter" : "exit"));
+
+	return ret;
+}
+
+static void dp_link_send_edid_checksum(struct dp_link *dp_link, u8 checksum)
+{
+	struct dp_link_private *link = NULL;
+	u32 const response_len = 0x1;
+
+	if (!dp_link) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	drm_dp_dpcd_write(link->aux->drm_aux, DP_TEST_EDID_CHECKSUM,
+			&checksum, response_len);
+}
+
+static int dp_link_parse_vx_px(struct dp_link_private *link)
+{
+	u8 bp;
+	u8 data;
+	int const param_len = 0x1;
+	int ret = 0;
+	u32 v0, p0, v1, p1, v2, p2, v3, p3;
+	int rlen;
+
+	DP_DEBUG("\n");
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_ADJUST_REQUEST_LANE0_1,
+			&bp, param_len);
+	if (rlen < param_len) {
+		DP_ERR("failed reading lanes 0/1\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	data = bp;
+
+	DP_DEBUG("lanes 0/1 (Byte 0x206): 0x%x\n", data);
+
+	v0 = data & 0x3;
+	data = data >> 2;
+	p0 = data & 0x3;
+	data = data >> 2;
+
+	v1 = data & 0x3;
+	data = data >> 2;
+	p1 = data & 0x3;
+	data = data >> 2;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_ADJUST_REQUEST_LANE2_3,
+			&bp, param_len);
+	if (rlen < param_len) {
+		DP_ERR("failed reading lanes 2/3\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	data = bp;
+
+	DP_DEBUG("lanes 2/3 (Byte 0x207): 0x%x\n", data);
+
+	v2 = data & 0x3;
+	data = data >> 2;
+	p2 = data & 0x3;
+	data = data >> 2;
+
+	v3 = data & 0x3;
+	data = data >> 2;
+	p3 = data & 0x3;
+	data = data >> 2;
+
+	DP_DEBUG("vx: 0=%d, 1=%d, 2=%d, 3=%d\n", v0, v1, v2, v3);
+	DP_DEBUG("px: 0=%d, 1=%d, 2=%d, 3=%d\n", p0, p1, p2, p3);
+
+	/**
+	 * Update the voltage and pre-emphasis levels as per DPCD request
+	 * vector.
+	 */
+	DP_DEBUG("Current: v_level = 0x%x, p_level = 0x%x\n",
+			link->dp_link.phy_params.v_level,
+			link->dp_link.phy_params.p_level);
+	DP_DEBUG("Requested: v_level = 0x%x, p_level = 0x%x\n", v0, p0);
+	link->dp_link.phy_params.v_level = v0;
+	link->dp_link.phy_params.p_level = p0;
+
+	DP_DEBUG("Success\n");
+end:
+	return ret;
+}
+
+/**
+ * dp_link_process_phy_test_pattern_request() - process new phy link requests
+ * @link: Display Port Driver data
+ *
+ * This function will handle new phy link pattern requests that are initiated
+ * by the sink. The function will return 0 if a phy link pattern has been
+ * processed, otherwise it will return -EINVAL.
+ */
+static int dp_link_process_phy_test_pattern_request(
+		struct dp_link_private *link)
+{
+	u32 test_link_rate = 0, test_lane_count = 0;
+
+	if (!(link->request.test_requested & DP_TEST_LINK_PHY_TEST_PATTERN)) {
+		DP_DEBUG("no phy test\n");
+		return -EINVAL;
+	}
+
+	test_link_rate = link->request.test_link_rate;
+	test_lane_count = link->request.test_lane_count;
+
+	if (!is_link_rate_valid(test_link_rate) ||
+		!is_lane_count_valid(test_lane_count)) {
+		DP_ERR("Invalid params: link rate = 0x%x, lane count = 0x%x\n",
+				test_link_rate, test_lane_count);
+		return -EINVAL;
+	}
+
+	DP_DEBUG("start\n");
+
+	DP_INFO("Current: bw_code = 0x%x, lane count = 0x%x\n",
+			link->dp_link.link_params.bw_code,
+			link->dp_link.link_params.lane_count);
+
+	DP_INFO("Requested: bw_code = 0x%x, lane count = 0x%x\n",
+			test_link_rate, test_lane_count);
+
+	link->dp_link.link_params.lane_count = link->request.test_lane_count;
+	link->dp_link.link_params.bw_code = link->request.test_link_rate;
+
+	dp_link_parse_vx_px(link);
+
+	DP_DEBUG("end\n");
+
+	return 0;
+}
+
+static u8 get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
+{
+	return link_status[r - DP_LANE0_1_STATUS];
+}
+
+/**
+ * dp_link_process_link_status_update() - processes link status updates
+ * @link: Display Port link module data
+ *
+ * This function will check for changes in the link status, e.g. clock
+ * recovery done on all lanes, and trigger link training if there is a
+ * failure/error on the link.
+ *
+ * The function will return 0 if the a link status update has been processed,
+ * otherwise it will return -EINVAL.
+ */
+static int dp_link_process_link_status_update(struct dp_link_private *link)
+{
+	bool channel_eq_done = drm_dp_channel_eq_ok(link->link_status,
+			link->dp_link.link_params.lane_count);
+	bool clock_recovery_done = drm_dp_clock_recovery_ok(link->link_status,
+			link->dp_link.link_params.lane_count);
+	DP_DEBUG("channel_eq_done = %d, clock_recovery_done = %d\n",
+			channel_eq_done, clock_recovery_done);
+
+	if (channel_eq_done && clock_recovery_done)
+		return -EINVAL;
+
+	return 0;
+}
+
+static bool dp_link_is_ds_port_status_changed(struct dp_link_private *link)
+{
+	if (get_link_status(link->link_status, DP_LANE_ALIGN_STATUS_UPDATED) &
+		DP_DOWNSTREAM_PORT_STATUS_CHANGED) /* port status changed */
+		return true;
+
+	if (link->prev_sink_count != link->dp_link.sink_count.count)
+		return true;
+
+	return false;
+}
+
+/**
+ * dp_link_process_downstream_port_status_change() - process port status changes
+ * @link: Display Port Driver data
+ *
+ * This function will handle downstream port updates that are initiated by
+ * the sink. If the downstream port status has changed, the EDID is read via
+ * AUX.
+ *
+ * The function will return 0 if a downstream port update has been
+ * processed, otherwise it will return -EINVAL.
+ */
+static int dp_link_process_ds_port_status_change(struct dp_link_private *link)
+{
+	if (!dp_link_is_ds_port_status_changed(link))
+		return -EINVAL;
+
+	/* reset prev_sink_count */
+	link->prev_sink_count = link->dp_link.sink_count.count;
+
+	return 0;
+}
+
+static bool dp_link_is_video_pattern_requested(struct dp_link_private *link)
+{
+	return (link->request.test_requested & DP_TEST_LINK_VIDEO_PATTERN)
+		&& !(link->request.test_requested &
+		DP_TEST_LINK_AUDIO_DISABLED_VIDEO);
+}
+
+static bool dp_link_is_audio_pattern_requested(struct dp_link_private *link)
+{
+	return (link->request.test_requested & DP_TEST_LINK_AUDIO_PATTERN);
+}
+
+/**
+ * dp_link_process_video_pattern_request() - process new video pattern request
+ * @link: Display Port link module's data
+ *
+ * This function will handle a new video pattern request that are initiated by
+ * the sink. This is acheieved by first sending a disconnect notification to
+ * the sink followed by a subsequent connect notification to the user modules,
+ * where it is expected that the user modules would draw the required link
+ * pattern.
+ */
+static int dp_link_process_video_pattern_request(struct dp_link_private *link)
+{
+	if (!dp_link_is_video_pattern_requested(link))
+		goto end;
+
+	DP_DEBUG("%s: bit depth=%d(%d bpp) pattern=%s\n",
+		dp_link_get_test_name(DP_TEST_LINK_VIDEO_PATTERN),
+		link->dp_link.test_video.test_bit_depth,
+		dp_link_bit_depth_to_bpp(
+		link->dp_link.test_video.test_bit_depth),
+		dp_link_video_pattern_to_string(
+			link->dp_link.test_video.test_video_pattern));
+
+	return 0;
+end:
+	return -EINVAL;
+}
+
+/**
+ * dp_link_process_audio_pattern_request() - process new audio pattern request
+ * @link: Display Port link module data
+ *
+ * This function will handle a new audio pattern request that is initiated by
+ * the sink. This is acheieved by sending the necessary secondary data packets
+ * to the sink. It is expected that any simulatenous requests for video
+ * patterns will be handled before the audio pattern is sent to the sink.
+ */
+static int dp_link_process_audio_pattern_request(struct dp_link_private *link)
+{
+	if (!dp_link_is_audio_pattern_requested(link))
+		return -EINVAL;
+
+	DP_DEBUG("sampling_rate=%s, channel_count=%d, pattern_type=%s\n",
+		dp_link_get_audio_sample_rate(
+			link->dp_link.test_audio.test_audio_sampling_rate),
+		link->dp_link.test_audio.test_audio_channel_count,
+		dp_link_get_audio_test_pattern(
+			link->dp_link.test_audio.test_audio_pattern_type));
+
+	DP_DEBUG("audio_period: ch1=0x%x, ch2=0x%x, ch3=0x%x, ch4=0x%x\n",
+		link->dp_link.test_audio.test_audio_period_ch_1,
+		link->dp_link.test_audio.test_audio_period_ch_2,
+		link->dp_link.test_audio.test_audio_period_ch_3,
+		link->dp_link.test_audio.test_audio_period_ch_4);
+
+	DP_DEBUG("audio_period: ch5=0x%x, ch6=0x%x, ch7=0x%x, ch8=0x%x\n",
+		link->dp_link.test_audio.test_audio_period_ch_5,
+		link->dp_link.test_audio.test_audio_period_ch_6,
+		link->dp_link.test_audio.test_audio_period_ch_7,
+		link->dp_link.test_audio.test_audio_period_ch_8);
+
+	return 0;
+}
+
+static void dp_link_reset_data(struct dp_link_private *link)
+{
+	link->request = (const struct dp_link_request){ 0 };
+	link->dp_link.test_video = (const struct dp_link_test_video){ 0 };
+	link->dp_link.test_video.test_bit_depth = DP_TEST_BIT_DEPTH_UNKNOWN;
+	link->dp_link.test_audio = (const struct dp_link_test_audio){ 0 };
+	link->dp_link.phy_params.phy_test_pattern_sel = 0;
+	link->dp_link.sink_request = 0;
+	link->dp_link.test_response = 0;
+}
+
+/**
+ * dp_link_process_request() - handle HPD IRQ transition to HIGH
+ * @link: pointer to link module data
+ *
+ * This function will handle the HPD IRQ state transitions from LOW to HIGH
+ * (including cases when there are back to back HPD IRQ HIGH) indicating
+ * the start of a new link training request or sink status update.
+ */
+static int dp_link_process_request(struct dp_link *dp_link)
+{
+	int ret = 0;
+	struct dp_link_private *link;
+
+	if (!dp_link) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	dp_link_reset_data(link);
+
+	dp_link_parse_sink_status_field(link);
+
+	if (dp_link_is_test_edid_read(link)) {
+		dp_link->sink_request |= DP_TEST_LINK_EDID_READ;
+		goto exit;
+	}
+
+	ret = dp_link_process_ds_port_status_change(link);
+	if (!ret) {
+		dp_link->sink_request |= DS_PORT_STATUS_CHANGED;
+		goto exit;
+	}
+
+	ret = dp_link_process_link_training_request(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_TEST_LINK_TRAINING;
+		goto exit;
+	}
+
+	ret = dp_link_process_phy_test_pattern_request(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_TEST_LINK_PHY_TEST_PATTERN;
+		goto exit;
+	}
+
+	ret = dp_link_process_link_status_update(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_LINK_STATUS_UPDATED;
+		goto exit;
+	}
+
+	ret = dp_link_process_video_pattern_request(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_TEST_LINK_VIDEO_PATTERN;
+		goto exit;
+	}
+
+	ret = dp_link_process_audio_pattern_request(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_TEST_LINK_AUDIO_PATTERN;
+		goto exit;
+	}
+
+	DP_DEBUG("no test requested\n");
+	return ret;
+exit:
+	/*
+	 * log this as it can be a use initiated action to run a DP CTS
+	 * test or in normal cases, sink has encountered a problem and
+	 * and want source to redo some part of initialization which can
+	 * be helpful in debugging.
+	 */
+	DP_INFO("event: %s\n",
+		dp_link_get_test_name(dp_link->sink_request));
+	return 0;
+}
+
+static int dp_link_get_colorimetry_config(struct dp_link *dp_link)
+{
+	u32 cc;
+	enum dynamic_range dr;
+	struct dp_link_private *link;
+
+	if (!dp_link) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	/* unless a video pattern CTS test is ongoing, use CEA_VESA */
+	if (dp_link_is_video_pattern_requested(link))
+		dr = link->dp_link.test_video.test_dyn_range;
+	else
+		dr = DP_DYNAMIC_RANGE_RGB_VESA;
+
+	/* Only RGB_VESA nd RGB_CEA supported for now */
+	switch (dr) {
+	case DP_DYNAMIC_RANGE_RGB_CEA:
+		cc = BIT(2);
+		break;
+	case DP_DYNAMIC_RANGE_RGB_VESA:
+	default:
+		cc = 0;
+	}
+
+	return cc;
+}
+
+static int dp_link_adjust_levels(struct dp_link *dp_link, u8 *link_status)
+{
+	int i;
+	int max = 0;
+	u8 data;
+	struct dp_link_private *link;
+	u8 buf[8] = {0}, offset = 0;
+
+	if (!dp_link) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	/* use the max level across lanes */
+	for (i = 0; i < dp_link->link_params.lane_count; i++) {
+		data = drm_dp_get_adjust_request_voltage(link_status, i);
+		data >>= DP_TRAIN_VOLTAGE_SWING_SHIFT;
+
+		offset = i * 2;
+		if (offset < sizeof(buf))
+			buf[offset] = data;
+
+		if (max < data)
+			max = data;
+	}
+
+	dp_link->phy_params.v_level = max;
+
+	/* use the max level across lanes */
+	max = 0;
+	for (i = 0; i < dp_link->link_params.lane_count; i++) {
+		data = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
+		data >>= DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+		offset = (i * 2) + 1;
+		if (offset < sizeof(buf))
+			buf[offset] = data;
+
+		if (max < data)
+			max = data;
+	}
+
+	dp_link->phy_params.p_level = max;
+
+	print_hex_dump_debug("[drm-dp] Req (VxPx): ",
+		DUMP_PREFIX_NONE, 8, 2, buf, sizeof(buf), false);
+
+	DP_DEBUG("Current (VxPx): 0x%x, 0x%x\n",
+		dp_link->phy_params.v_level, dp_link->phy_params.p_level);
+
+	/**
+	 * Adjust the voltage swing and pre-emphasis level combination to within
+	 * the allowable range.
+	 */
+	if (dp_link->phy_params.v_level > dp_link->phy_params.max_v_level)
+		dp_link->phy_params.v_level = dp_link->phy_params.max_v_level;
+
+	if (dp_link->phy_params.p_level > dp_link->phy_params.max_p_level)
+		dp_link->phy_params.p_level = dp_link->phy_params.max_p_level;
+
+	if ((dp_link->phy_params.p_level > DP_LINK_PRE_EMPHASIS_LEVEL_1)
+		&& (dp_link->phy_params.v_level == DP_LINK_VOLTAGE_LEVEL_2))
+		dp_link->phy_params.p_level = DP_LINK_PRE_EMPHASIS_LEVEL_1;
+
+	if ((dp_link->phy_params.p_level > DP_LINK_PRE_EMPHASIS_LEVEL_2)
+		&& (dp_link->phy_params.v_level == DP_LINK_VOLTAGE_LEVEL_1))
+		dp_link->phy_params.p_level = DP_LINK_PRE_EMPHASIS_LEVEL_2;
+
+	DP_DEBUG("Set (VxPx): 0x%x, 0x%x\n",
+		dp_link->phy_params.v_level, dp_link->phy_params.p_level);
+
+	return 0;
+}
+
+static int dp_link_send_psm_request(struct dp_link *dp_link, bool req)
+{
+	struct dp_link_private *link;
+
+	if (!dp_link) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	return 0;
+}
+
+static u32 dp_link_get_test_bits_depth(struct dp_link *dp_link, u32 bpp)
+{
+	u32 tbd;
+
+	/*
+	 * Few simplistic rules and assumptions made here:
+	 *    1. Test bit depth is bit depth per color component
+	 *    2. Assume 3 color components
+	 */
+	switch (bpp) {
+	case 18:
+		tbd = DP_TEST_BIT_DEPTH_6;
+		break;
+	case 24:
+		tbd = DP_TEST_BIT_DEPTH_8;
+		break;
+	case 30:
+		tbd = DP_TEST_BIT_DEPTH_10;
+		break;
+	default:
+		tbd = DP_TEST_BIT_DEPTH_UNKNOWN;
+		break;
+	}
+
+	if (tbd != DP_TEST_BIT_DEPTH_UNKNOWN)
+		tbd = (tbd >> DP_TEST_BIT_DEPTH_SHIFT);
+
+	return tbd;
+}
+
+/**
+ * dp_link_probe() - probe a DisplayPort link for capabilities
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to structure in which to return link capabilities
+ *
+ * The structure filled in by this function can usually be passed directly
+ * into dp_link_power_up() and dp_link_configure() to power up and
+ * configure the link based on the link's capabilities.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+	u8 values[3];
+	int ret;
+
+	memset(link, 0, sizeof(*link));
+
+	ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
+	if (ret < 3) {
+		DP_ERR("failed to probe link, ret:%d\n", ret);
+		ret = -EIO;
+	}
+
+	link->revision = values[0];
+	link->rate = drm_dp_bw_code_to_link_rate(values[1]);
+	link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
+
+	if (values[2] & DP_ENHANCED_FRAME_CAP)
+		link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
+
+	return 0;
+}
+
+/**
+ * dp_link_power_up() - power up a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+	u8 value;
+	int ret;
+
+	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
+	if (link->revision < 0x11)
+		return 0;
+
+	ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
+	if (ret != 1) {
+		DP_ERR("failed to read sink power when powering up, ret:%d\n", ret);
+		return -EIO;
+	}
+
+	value &= ~DP_SET_POWER_MASK;
+	value |= DP_SET_POWER_D0;
+
+	ret = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
+	if (ret != 1) {
+		DP_ERR("failed to power up[0x%x] sink, ret:%d\n", value, ret);
+		return -EIO;
+	}
+
+	/*
+	 * According to the DP 1.1 specification, a "Sink Device must exit the
+	 * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
+	 * Control Field" (register 0x600).
+	 */
+	usleep_range(1000, 2000);
+
+	return 0;
+}
+
+/**
+ * dp_link_power_down() - power down a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+	u8 value;
+	int ret;
+
+	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
+	if (link->revision < 0x11)
+		return 0;
+
+	ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
+	if (ret != 1) {
+		DP_ERR("failed to read sink power when powering down, ret:%d\n", ret);
+		return -EIO;
+	}
+
+	value &= ~DP_SET_POWER_MASK;
+	value |= DP_SET_POWER_D3;
+
+	ret = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
+	if (ret != 1) {
+		DP_ERR("failed to power down[0x%x] sink, ret:%d\n", value, ret);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/**
+ * dp_link_configure() - configure a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+	u8 values[2];
+	int ret;
+
+	values[0] = drm_dp_link_rate_to_bw_code(link->rate);
+	values[1] = link->num_lanes;
+
+	if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
+		values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+	ret = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
+	if (ret != 2) {
+		DP_ERR("failed to configure link, ret:%d\n", ret);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux, u32 dp_core_revision)
+{
+	int rc = 0;
+	struct dp_link_private *link;
+	struct dp_link *dp_link;
+
+	if (!dev || !aux) {
+		DP_ERR("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL);
+	if (!link) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	link->dev   = dev;
+	link->aux   = aux;
+
+	dp_link = &link->dp_link;
+
+	if (dp_core_revision >= 0x10020003)
+		dp_link->phy_params.max_v_level = DP_LINK_VOLTAGE_LEVEL_3;
+	else
+		dp_link->phy_params.max_v_level = DP_LINK_VOLTAGE_LEVEL_2;
+
+	dp_link->phy_params.max_p_level = DP_LINK_PRE_EMPHASIS_LEVEL_3;
+
+	dp_link->process_request        = dp_link_process_request;
+	dp_link->get_test_bits_depth    = dp_link_get_test_bits_depth;
+	dp_link->get_colorimetry_config = dp_link_get_colorimetry_config;
+	dp_link->adjust_levels          = dp_link_adjust_levels;
+	dp_link->send_psm_request       = dp_link_send_psm_request;
+	dp_link->send_test_response     = dp_link_send_test_response;
+	dp_link->psm_config             = dp_link_psm_config;
+	dp_link->send_edid_checksum     = dp_link_send_edid_checksum;
+
+	return dp_link;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_link_put(struct dp_link *dp_link)
+{
+	struct dp_link_private *link;
+
+	if (!dp_link)
+		return;
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	devm_kfree(link->dev, link);
+}

+ 248 - 0
qcom/opensource/display-drivers/msm/dp/dp_link.h

@@ -0,0 +1,248 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
+ *
+ * Copyright (c) 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 _DP_LINK_H_
+#define _DP_LINK_H_
+
+#include "dp_aux.h"
+
+#define DS_PORT_STATUS_CHANGED 0x200
+#define DP_TEST_BIT_DEPTH_UNKNOWN 0xFFFFFFFF
+#define DP_LINK_ENUM_STR(x)		#x
+
+#define DP_PHY_TEST_PATTERN_CP2520_2	0x6
+#define DP_PHY_TEST_PATTERN_CP2520_3	0x7
+
+struct drm_dp_aux;
+
+#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0)
+#define DP_LINK_CAP_CRC (1 << 1)
+
+struct drm_dp_link {
+	unsigned char revision;
+	unsigned int rate;
+	unsigned int num_lanes;
+	unsigned long capabilities;
+};
+
+int dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);
+int dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link);
+int dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link);
+int dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link);
+
+enum dp_link_voltage_level {
+	DP_LINK_VOLTAGE_LEVEL_0,
+	DP_LINK_VOLTAGE_LEVEL_1,
+	DP_LINK_VOLTAGE_LEVEL_2,
+	DP_LINK_VOLTAGE_LEVEL_3,
+};
+
+enum dp_link_preemaphasis_level {
+	DP_LINK_PRE_EMPHASIS_LEVEL_0,
+	DP_LINK_PRE_EMPHASIS_LEVEL_1,
+	DP_LINK_PRE_EMPHASIS_LEVEL_2,
+	DP_LINK_PRE_EMPHASIS_LEVEL_3,
+};
+
+struct dp_link_sink_count {
+	u32 count;
+	bool cp_ready;
+};
+
+struct dp_link_test_video {
+	u32 test_video_pattern;
+	u32 test_bit_depth;
+	u32 test_dyn_range;
+	u32 test_h_total;
+	u32 test_v_total;
+	u32 test_h_start;
+	u32 test_v_start;
+	u32 test_hsync_pol;
+	u32 test_hsync_width;
+	u32 test_vsync_pol;
+	u32 test_vsync_width;
+	u32 test_h_width;
+	u32 test_v_height;
+	u32 test_rr_d;
+	u32 test_rr_n;
+};
+
+struct dp_link_test_audio {
+	u32 test_audio_sampling_rate;
+	u32 test_audio_channel_count;
+	u32 test_audio_pattern_type;
+	u32 test_audio_period_ch_1;
+	u32 test_audio_period_ch_2;
+	u32 test_audio_period_ch_3;
+	u32 test_audio_period_ch_4;
+	u32 test_audio_period_ch_5;
+	u32 test_audio_period_ch_6;
+	u32 test_audio_period_ch_7;
+	u32 test_audio_period_ch_8;
+};
+
+struct dp_link_hdcp_status {
+	int hdcp_state;
+	int hdcp_version;
+};
+
+struct dp_link_phy_params {
+	u32 phy_test_pattern_sel;
+	u8 v_level;
+	u8 p_level;
+	u8 max_v_level;
+	u8 max_p_level;
+};
+
+struct dp_link_params {
+	u32 lane_count;
+	u32 bw_code;
+};
+
+static inline char *dp_link_get_test_name(u32 test_requested)
+{
+	switch (test_requested) {
+	case DP_TEST_LINK_TRAINING:
+		return DP_LINK_ENUM_STR(DP_TEST_LINK_TRAINING);
+	case DP_TEST_LINK_VIDEO_PATTERN:
+		return DP_LINK_ENUM_STR(DP_TEST_LINK_VIDEO_PATTERN);
+	case DP_TEST_LINK_EDID_READ:
+		return DP_LINK_ENUM_STR(DP_TEST_LINK_EDID_READ);
+	case DP_TEST_LINK_PHY_TEST_PATTERN:
+		return DP_LINK_ENUM_STR(DP_TEST_LINK_PHY_TEST_PATTERN);
+	case DP_TEST_LINK_AUDIO_PATTERN:
+		return DP_LINK_ENUM_STR(DP_TEST_LINK_AUDIO_PATTERN);
+	case DS_PORT_STATUS_CHANGED:
+		return DP_LINK_ENUM_STR(DS_PORT_STATUS_CHANGED);
+	case DP_LINK_STATUS_UPDATED:
+		return DP_LINK_ENUM_STR(DP_LINK_STATUS_UPDATED);
+	default:
+		return "unknown";
+	}
+}
+
+struct dp_link {
+	u32 sink_request;
+	u32 test_response;
+
+	struct dp_link_sink_count sink_count;
+	struct dp_link_test_video test_video;
+	struct dp_link_test_audio test_audio;
+	struct dp_link_phy_params phy_params;
+	struct dp_link_params link_params;
+	struct dp_link_hdcp_status hdcp_status;
+
+	u32 (*get_test_bits_depth)(struct dp_link *dp_link, u32 bpp);
+	int (*process_request)(struct dp_link *dp_link);
+	int (*get_colorimetry_config)(struct dp_link *dp_link);
+	int (*adjust_levels)(struct dp_link *dp_link, u8 *link_status);
+	int (*send_psm_request)(struct dp_link *dp_link, bool req);
+	void (*send_test_response)(struct dp_link *dp_link);
+	int (*psm_config)(struct dp_link *dp_link,
+		struct drm_dp_link *link_info, bool enable);
+	void (*send_edid_checksum)(struct dp_link *dp_link, u8 checksum);
+};
+
+static inline char *dp_link_get_phy_test_pattern(u32 phy_test_pattern_sel)
+{
+	switch (phy_test_pattern_sel) {
+	case DP_PHY_TEST_PATTERN_NONE:
+		return DP_LINK_ENUM_STR(DP_PHY_TEST_PATTERN_NONE);
+	case DP_PHY_TEST_PATTERN_D10_2:
+		return DP_LINK_ENUM_STR(
+			DP_PHY_TEST_PATTERN_D10_2);
+	case DP_PHY_TEST_PATTERN_ERROR_COUNT:
+		return DP_LINK_ENUM_STR(
+			DP_PHY_TEST_PATTERN_ERROR_COUNT);
+	case DP_PHY_TEST_PATTERN_PRBS7:
+		return DP_LINK_ENUM_STR(DP_PHY_TEST_PATTERN_PRBS7);
+	case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
+		return DP_LINK_ENUM_STR(
+			DP_PHY_TEST_PATTERN_80BIT_CUSTOM);
+	case DP_PHY_TEST_PATTERN_CP2520:
+		return DP_LINK_ENUM_STR(DP_PHY_TEST_PATTERN_CP2520);
+	case DP_PHY_TEST_PATTERN_CP2520_2:
+		return DP_LINK_ENUM_STR(DP_PHY_TEST_PATTERN_CP2520_2);
+	case DP_PHY_TEST_PATTERN_CP2520_3:
+		return DP_LINK_ENUM_STR(DP_PHY_TEST_PATTERN_CP2520_3);
+	default:
+		return "unknown";
+	}
+}
+
+/**
+ * mdss_dp_test_bit_depth_to_bpp() - convert test bit depth to bpp
+ * @tbd: test bit depth
+ *
+ * Returns the bits per pixel (bpp) to be used corresponding to the
+ * git bit depth value. This function assumes that bit depth has
+ * already been validated.
+ */
+static inline u32 dp_link_bit_depth_to_bpp(u32 tbd)
+{
+	u32 bpp;
+
+	/*
+	 * Few simplistic rules and assumptions made here:
+	 *    1. Bit depth is per color component
+	 *    2. If bit depth is unknown return 0
+	 *    3. Assume 3 color components
+	 */
+	switch (tbd) {
+	case DP_TEST_BIT_DEPTH_6:
+		bpp = 18;
+		break;
+	case DP_TEST_BIT_DEPTH_8:
+		bpp = 24;
+		break;
+	case DP_TEST_BIT_DEPTH_10:
+		bpp = 30;
+		break;
+	case DP_TEST_BIT_DEPTH_UNKNOWN:
+	default:
+		bpp = 0;
+	}
+
+	return bpp;
+}
+
+/**
+ * dp_link_get() - get the functionalities of dp test module
+ *
+ *
+ * return: a pointer to dp_link struct
+ */
+struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux, u32 dp_core_revision);
+
+/**
+ * dp_link_put() - releases the dp test module's resources
+ *
+ * @dp_link: an instance of dp_link module
+ *
+ */
+void dp_link_put(struct dp_link *dp_link);
+
+#endif /* _DP_LINK_H_ */

+ 424 - 0
qcom/opensource/display-drivers/msm/dp/dp_lphw_hpd.c

@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/sde_io_util.h>
+#include <linux/of_gpio.h>
+#include "dp_lphw_hpd.h"
+#include "dp_debug.h"
+
+struct dp_lphw_hpd_private {
+	struct device *dev;
+	struct dp_hpd base;
+	struct dp_parser *parser;
+	struct dp_catalog_hpd *catalog;
+	struct dss_gpio gpio_cfg;
+	struct workqueue_struct *connect_wq;
+	struct delayed_work work;
+	struct work_struct connect;
+	struct work_struct disconnect;
+	struct work_struct attention;
+	struct dp_hpd_cb *cb;
+	int irq;
+	bool hpd;
+};
+
+static void dp_lphw_hpd_attention(struct work_struct *work)
+{
+	struct dp_lphw_hpd_private *lphw_hpd = container_of(work,
+				struct dp_lphw_hpd_private, attention);
+
+	if (!lphw_hpd) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	lphw_hpd->base.hpd_irq = true;
+
+	if (lphw_hpd->cb && lphw_hpd->cb->attention)
+		lphw_hpd->cb->attention(lphw_hpd->dev);
+}
+
+static void dp_lphw_hpd_connect(struct work_struct *work)
+{
+	struct dp_lphw_hpd_private *lphw_hpd = container_of(work,
+				struct dp_lphw_hpd_private, connect);
+
+	if (!lphw_hpd) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	lphw_hpd->base.hpd_high = true;
+	lphw_hpd->base.alt_mode_cfg_done = true;
+	lphw_hpd->base.hpd_irq = false;
+
+	if (lphw_hpd->cb && lphw_hpd->cb->configure)
+		lphw_hpd->cb->configure(lphw_hpd->dev);
+}
+
+static void dp_lphw_hpd_disconnect(struct work_struct *work)
+{
+	struct dp_lphw_hpd_private *lphw_hpd = container_of(work,
+				struct dp_lphw_hpd_private, disconnect);
+
+	if (!lphw_hpd) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	lphw_hpd->base.hpd_high = false;
+	lphw_hpd->base.alt_mode_cfg_done = false;
+	lphw_hpd->base.hpd_irq = false;
+
+	if (lphw_hpd->cb && lphw_hpd->cb->disconnect)
+		lphw_hpd->cb->disconnect(lphw_hpd->dev);
+}
+
+static irqreturn_t dp_tlmm_isr(int unused, void *data)
+{
+	struct dp_lphw_hpd_private *lphw_hpd = data;
+	bool hpd;
+
+	if (!lphw_hpd)
+		return IRQ_NONE;
+
+	/*
+	 * According to the DP spec, HPD high event can be confirmed only after
+	 * the HPD line has een asserted continuously for more than 100ms
+	 */
+	usleep_range(99000, 100000);
+
+	hpd = gpio_get_value_cansleep(lphw_hpd->gpio_cfg.gpio);
+
+	DP_DEBUG("lphw_hpd state = %d, new hpd state = %d\n",
+			lphw_hpd->hpd, hpd);
+	if (!lphw_hpd->hpd && hpd) {
+		lphw_hpd->hpd = true;
+		queue_work(lphw_hpd->connect_wq, &lphw_hpd->connect);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void dp_lphw_hpd_host_init(struct dp_hpd *dp_hpd,
+		struct dp_catalog_hpd *catalog)
+{
+	struct dp_lphw_hpd_private *lphw_hpd;
+
+	if (!dp_hpd) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
+
+	lphw_hpd->catalog->config_hpd(lphw_hpd->catalog, true);
+
+	/*
+	 * Changing the gpio function to dp controller for the hpd line is not
+	 * stopping the tlmm interrupts generation on function 0.
+	 * So, as an additional step, disable the gpio interrupt irq also
+	 */
+	disable_irq(lphw_hpd->irq);
+}
+
+static void dp_lphw_hpd_host_deinit(struct dp_hpd *dp_hpd,
+		struct dp_catalog_hpd *catalog)
+{
+	struct dp_lphw_hpd_private *lphw_hpd;
+
+	if (!dp_hpd) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
+
+	/* Enable the tlmm interrupt irq which was disabled in host_init */
+	enable_irq(lphw_hpd->irq);
+
+	lphw_hpd->catalog->config_hpd(lphw_hpd->catalog, false);
+}
+
+static void dp_lphw_hpd_isr(struct dp_hpd *dp_hpd)
+{
+	struct dp_lphw_hpd_private *lphw_hpd;
+	u32 isr = 0;
+	int rc = 0;
+
+	if (!dp_hpd) {
+		DP_ERR("invalid input\n");
+		return;
+	}
+
+	lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
+
+	isr = lphw_hpd->catalog->get_interrupt(lphw_hpd->catalog);
+
+	if (isr & DP_HPD_UNPLUG_INT_STATUS) { /* disconnect interrupt */
+
+		DP_DEBUG("disconnect interrupt, hpd isr state: 0x%x\n", isr);
+
+		if (lphw_hpd->base.hpd_high) {
+			lphw_hpd->hpd = false;
+			lphw_hpd->base.hpd_high = false;
+			lphw_hpd->base.alt_mode_cfg_done = false;
+			lphw_hpd->base.hpd_irq = false;
+
+			rc = queue_work(lphw_hpd->connect_wq,
+					&lphw_hpd->disconnect);
+			if (!rc)
+				DP_DEBUG("disconnect not queued\n");
+		} else {
+			DP_ERR("already disconnected\n");
+		}
+
+	} else if (isr & DP_IRQ_HPD_INT_STATUS) { /* attention interrupt */
+
+		DP_DEBUG("hpd_irq interrupt, hpd isr state: 0x%x\n", isr);
+
+		rc = queue_work(lphw_hpd->connect_wq, &lphw_hpd->attention);
+		if (!rc)
+			DP_DEBUG("attention not queued\n");
+	}
+}
+
+static int dp_lphw_hpd_simulate_connect(struct dp_hpd *dp_hpd, bool hpd)
+{
+	struct dp_lphw_hpd_private *lphw_hpd;
+
+	if (!dp_hpd) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
+
+	lphw_hpd->base.hpd_high = hpd;
+	lphw_hpd->base.alt_mode_cfg_done = hpd;
+	lphw_hpd->base.hpd_irq = false;
+
+	if (!lphw_hpd->cb || !lphw_hpd->cb->configure ||
+			!lphw_hpd->cb->disconnect) {
+		DP_ERR("invalid callback\n");
+		return -EINVAL;
+	}
+
+	if (hpd)
+		lphw_hpd->cb->configure(lphw_hpd->dev);
+	else
+		lphw_hpd->cb->disconnect(lphw_hpd->dev);
+
+	return 0;
+}
+
+static int dp_lphw_hpd_simulate_attention(struct dp_hpd *dp_hpd, int vdo)
+{
+	struct dp_lphw_hpd_private *lphw_hpd;
+
+	if (!dp_hpd) {
+		DP_ERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
+
+	lphw_hpd->base.hpd_irq = true;
+
+	if (lphw_hpd->cb && lphw_hpd->cb->attention)
+		lphw_hpd->cb->attention(lphw_hpd->dev);
+
+	return 0;
+}
+
+int dp_lphw_hpd_register(struct dp_hpd *dp_hpd)
+{
+	struct dp_lphw_hpd_private *lphw_hpd;
+	int rc = 0;
+
+	if (!dp_hpd)
+		return -EINVAL;
+
+	lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
+
+	lphw_hpd->hpd = gpio_get_value_cansleep(lphw_hpd->gpio_cfg.gpio);
+
+	rc = devm_request_threaded_irq(lphw_hpd->dev, lphw_hpd->irq, NULL,
+		dp_tlmm_isr,
+		IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+		"dp-gpio-intp", lphw_hpd);
+	if (rc) {
+		DP_ERR("Failed to request INTP threaded IRQ: %d\n", rc);
+		return rc;
+	}
+	enable_irq_wake(lphw_hpd->irq);
+
+	if (lphw_hpd->hpd)
+		queue_work(lphw_hpd->connect_wq, &lphw_hpd->connect);
+
+	return rc;
+}
+
+static void dp_lphw_hpd_deinit(struct dp_lphw_hpd_private *lphw_hpd)
+{
+	struct dp_parser *parser = lphw_hpd->parser;
+	int i = 0;
+
+	for (i = 0; i < parser->mp[DP_PHY_PM].num_vreg; i++) {
+
+		if (!strcmp(parser->mp[DP_PHY_PM].vreg_config[i].vreg_name,
+					"hpd-pwr")) {
+			/* disable the hpd-pwr voltage regulator */
+			if (msm_dss_enable_vreg(
+				&parser->mp[DP_PHY_PM].vreg_config[i], 1,
+				false))
+				DP_ERR("hpd-pwr vreg not disabled\n");
+
+			break;
+		}
+	}
+}
+
+static void dp_lphw_hpd_init(struct dp_lphw_hpd_private *lphw_hpd)
+{
+	struct dp_pinctrl pinctrl = {0};
+	struct dp_parser *parser = lphw_hpd->parser;
+	int i = 0, rc = 0;
+
+	for (i = 0; i < parser->mp[DP_PHY_PM].num_vreg; i++) {
+
+		if (!strcmp(parser->mp[DP_PHY_PM].vreg_config[i].vreg_name,
+					"hpd-pwr")) {
+			/* enable the hpd-pwr voltage regulator */
+			if (msm_dss_enable_vreg(
+				&parser->mp[DP_PHY_PM].vreg_config[i], 1,
+				true))
+				DP_ERR("hpd-pwr vreg not enabled\n");
+
+			break;
+		}
+	}
+
+	pinctrl.pin = devm_pinctrl_get(lphw_hpd->dev);
+
+	if (!IS_ERR_OR_NULL(pinctrl.pin)) {
+		pinctrl.state_hpd_active = pinctrl_lookup_state(pinctrl.pin,
+						"mdss_dp_hpd_active");
+
+		if (!IS_ERR_OR_NULL(pinctrl.state_hpd_active)) {
+			rc = pinctrl_select_state(pinctrl.pin,
+					pinctrl.state_hpd_active);
+			if (rc)
+				DP_ERR("failed to set hpd_active state\n");
+		}
+		pinctrl.state_hpd_tlmm = pinctrl.state_hpd_ctrl = NULL;
+	}
+}
+
+static int dp_lphw_hpd_create_workqueue(struct dp_lphw_hpd_private *lphw_hpd)
+{
+	lphw_hpd->connect_wq = create_singlethread_workqueue("dp_lphw_work");
+	if (IS_ERR_OR_NULL(lphw_hpd->connect_wq)) {
+		DP_ERR("Error creating connect_wq\n");
+		return -EPERM;
+	}
+
+	INIT_WORK(&lphw_hpd->connect, dp_lphw_hpd_connect);
+	INIT_WORK(&lphw_hpd->disconnect, dp_lphw_hpd_disconnect);
+	INIT_WORK(&lphw_hpd->attention, dp_lphw_hpd_attention);
+
+	return 0;
+}
+
+struct dp_hpd *dp_lphw_hpd_get(struct device *dev, struct dp_parser *parser,
+	struct dp_catalog_hpd *catalog, struct dp_hpd_cb *cb)
+{
+	int rc = 0;
+	const char *hpd_gpio_name = "qcom,dp-hpd-gpio";
+	struct dp_lphw_hpd_private *lphw_hpd = NULL;
+	unsigned int gpio;
+
+	if (!dev || !parser || !cb) {
+		DP_ERR("invalid device\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	gpio = of_get_named_gpio(dev->of_node, hpd_gpio_name, 0);
+	if (!gpio_is_valid(gpio)) {
+		DP_DEBUG("%s gpio not specified\n", hpd_gpio_name);
+		rc = -EINVAL;
+		goto error;
+	}
+
+	lphw_hpd = devm_kzalloc(dev, sizeof(*lphw_hpd), GFP_KERNEL);
+	if (!lphw_hpd) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	lphw_hpd->gpio_cfg.gpio = gpio;
+	strlcpy(lphw_hpd->gpio_cfg.gpio_name, hpd_gpio_name,
+		sizeof(lphw_hpd->gpio_cfg.gpio_name));
+	lphw_hpd->gpio_cfg.value = 0;
+
+	rc = gpio_request(lphw_hpd->gpio_cfg.gpio,
+		lphw_hpd->gpio_cfg.gpio_name);
+	if (rc) {
+		DP_ERR("%s: failed to request gpio\n", hpd_gpio_name);
+		goto gpio_error;
+	}
+	gpio_direction_input(lphw_hpd->gpio_cfg.gpio);
+
+	lphw_hpd->dev = dev;
+	lphw_hpd->cb = cb;
+	lphw_hpd->irq = gpio_to_irq(lphw_hpd->gpio_cfg.gpio);
+
+	rc = dp_lphw_hpd_create_workqueue(lphw_hpd);
+	if (rc) {
+		DP_ERR("Failed to create a dp_hpd workqueue\n");
+		goto gpio_error;
+	}
+
+	lphw_hpd->parser = parser;
+	lphw_hpd->catalog = catalog;
+	lphw_hpd->base.isr = dp_lphw_hpd_isr;
+	lphw_hpd->base.host_init = dp_lphw_hpd_host_init;
+	lphw_hpd->base.host_deinit = dp_lphw_hpd_host_deinit;
+	lphw_hpd->base.simulate_connect = dp_lphw_hpd_simulate_connect;
+	lphw_hpd->base.simulate_attention = dp_lphw_hpd_simulate_attention;
+	lphw_hpd->base.register_hpd = dp_lphw_hpd_register;
+
+	dp_lphw_hpd_init(lphw_hpd);
+
+	return &lphw_hpd->base;
+
+gpio_error:
+	devm_kfree(dev, lphw_hpd);
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_lphw_hpd_put(struct dp_hpd *dp_hpd)
+{
+	struct dp_lphw_hpd_private *lphw_hpd;
+
+	if (!dp_hpd)
+		return;
+
+	lphw_hpd = container_of(dp_hpd, struct dp_lphw_hpd_private, base);
+
+	dp_lphw_hpd_deinit(lphw_hpd);
+	gpio_free(lphw_hpd->gpio_cfg.gpio);
+	devm_kfree(lphw_hpd->dev, lphw_hpd);
+}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.