瀏覽代碼

disp: msm: dp: add support for PLL programming

Add support for PLL programming in the DisplayPort driver.

Change-Id: I4f08a621dcae5d1f54d67bb5c34409249012cc7b
Signed-off-by: Tatenda Chipeperekwa <[email protected]>
Tatenda Chipeperekwa 5 年之前
父節點
當前提交
37412f5add
共有 15 個文件被更改,包括 1779 次插入245 次删除
  1. 2 0
      msm/Makefile
  2. 2 1
      msm/dp/dp_aux.h
  3. 287 162
      msm/dp/dp_debug.c
  4. 7 1
      msm/dp/dp_debug.h
  5. 52 31
      msm/dp/dp_display.c
  6. 17 5
      msm/dp/dp_panel.c
  7. 2 0
      msm/dp/dp_panel.h
  8. 2 1
      msm/dp/dp_parser.c
  9. 3 1
      msm/dp/dp_parser.h
  10. 143 0
      msm/dp/dp_pll.c
  11. 142 0
      msm/dp/dp_pll.h
  12. 1038 0
      msm/dp/dp_pll_5nm.c
  13. 74 39
      msm/dp/dp_power.c
  14. 6 2
      msm/dp/dp_power.h
  15. 2 2
      msm/dp/dp_reg.h

+ 2 - 0
msm/Makefile

@@ -24,6 +24,8 @@ msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_usbpd.o \
 	dp/dp_hdcp2p2.o \
 	sde_hdcp_1x.o \
 	sde_hdcp_2x.o \
+	dp/dp_pll.o \
+	dp/dp_pll_5nm.o \
 
 msm_drm-$(CONFIG_DRM_MSM_DP_MST) += dp/dp_mst_drm.o \
 

+ 2 - 1
msm/dp/dp_aux.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _DP_AUX_H_
@@ -22,6 +22,7 @@
 #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_error {
 	DP_AUX_ERR_NONE	= 0,

+ 287 - 162
msm/dp/dp_debug.c

@@ -13,6 +13,7 @@
 #include "drm_connector.h"
 #include "sde_connector.h"
 #include "dp_display.h"
+#include "dp_pll.h"
 
 #define DEBUG_NAME "drm_dp"
 
@@ -40,6 +41,7 @@ struct dp_debug_private {
 	struct dp_debug dp_debug;
 	struct dp_parser *parser;
 	struct dp_ctrl *ctrl;
+	struct dp_pll *pll;
 	struct mutex lock;
 };
 
@@ -785,35 +787,6 @@ static ssize_t dp_debug_mst_sideband_mode_write(struct file *file,
 	return count;
 }
 
-static ssize_t dp_debug_widebus_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 widebus_mode = 0;
-
-	if (!debug || !debug->parser)
-		return -ENODEV;
-
-	if (*ppos)
-		return 0;
-
-	len = min_t(size_t, count, SZ_8 - 1);
-	if (copy_from_user(buf, user_buff, len))
-		return -EFAULT;
-
-	buf[len] = '\0';
-
-	if (kstrtoint(buf, 10, &widebus_mode) != 0)
-		return -EINVAL;
-
-	debug->parser->has_widebus = widebus_mode ? true : false;
-	DP_DEBUG("widebus_enable: %d\n", widebus_mode);
-
-	return len;
-}
-
 static ssize_t dp_debug_tpg_write(struct file *file,
 		const char __user *user_buff, size_t count, loff_t *ppos)
 {
@@ -1900,126 +1873,171 @@ static const struct file_operations hdcp_fops = {
 	.read = dp_debug_read_hdcp,
 };
 
-static const struct file_operations widebus_mode_fops = {
-	.open = simple_open,
-	.write = dp_debug_widebus_mode_write,
-};
-
-static int dp_debug_init(struct dp_debug *dp_debug)
+static int dp_debug_init_mst(struct dp_debug_private *debug, struct dentry *dir)
 {
 	int rc = 0;
-	struct dp_debug_private *debug = container_of(dp_debug,
-		struct dp_debug_private, dp_debug);
-	struct dentry *dir, *file;
+	struct dentry *file;
 
-	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",
+	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);
-		goto error;
+		return rc;
 	}
 
-	debug->root = dir;
+	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("dp_debug", 0444, dir,
-				debug, &dp_debug_fops);
+	file = debugfs_create_file("mst_con_add", 0644, dir,
+					debug, &mst_con_add_fops);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs create file failed, rc=%d\n",
+		DRM_ERROR("[%s] debugfs create mst_con_add failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("edid_modes", 0644, dir,
-					debug, &edid_modes_fops);
+	file = debugfs_create_file("mst_con_remove", 0644, dir,
+					debug, &mst_con_remove_fops);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs create edid_modes failed, rc=%d\n",
+		DRM_ERROR("[%s] debugfs create mst_con_remove failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("edid_modes_mst", 0644, dir,
-					debug, &edid_modes_mst_fops);
+	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 create edid_modes_mst failed, rc=%d\n",
+		DP_ERR("[%s] debugfs mst_mode failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("mst_con_id", 0644, dir,
-					debug, &mst_con_id_fops);
+	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 create mst_con_id failed, rc=%d\n",
+		DP_ERR("[%s] debugfs mst_sideband_mode failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("mst_con_info", 0644, dir,
-					debug, &mst_conn_info_fops);
+	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 create mst_conn_info failed, rc=%d\n",
+		DP_ERR("[%s] debugfs max_bw_code failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("mst_con_add", 0644, dir,
-					debug, &mst_con_add_fops);
+	file = debugfs_create_file("max_pclk_khz", 0644, dir,
+			debug, &max_pclk_khz_fops);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DRM_ERROR("[%s] debugfs create mst_con_add failed, rc=%d\n",
+		DP_ERR("[%s] debugfs max_pclk_khz failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("mst_con_remove", 0644, dir,
-					debug, &mst_con_remove_fops);
+	file = debugfs_create_u32("max_lclk_khz", 0644, dir,
+			&debug->parser->max_lclk_khz);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DRM_ERROR("[%s] debugfs create mst_con_remove failed, rc=%d\n",
+		DP_ERR("[%s] debugfs max_lclk_khz failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("hpd", 0644, dir,
-					debug, &hpd_fops);
+	file = debugfs_create_u32("lane_count", 0644, dir,
+			&debug->panel->lane_count);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs hpd failed, rc=%d\n",
-			DEBUG_NAME, rc);
-		goto error_remove_dir;
+		DP_ERR("[%s] debugfs lane_count failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
 	}
 
-	file = debugfs_create_file("connected", 0444, dir,
-					debug, &connected_fops);
+	file = debugfs_create_u32("link_bw_code", 0644, dir,
+			&debug->panel->link_bw_code);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs connected failed, rc=%d\n",
-			DEBUG_NAME, rc);
-		goto error_remove_dir;
+		DP_ERR("[%s] debugfs link_bw_code failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
 	}
 
-	file = debugfs_create_file("max_bw_code", 0644, dir,
-			debug, &bw_code_fops);
+	return rc;
+}
+
+static int dp_debug_init_hdcp(struct dp_debug_private *debug,
+		struct dentry *dir)
+{
+	int rc = 0;
+	struct dentry *file;
+
+	file = debugfs_create_bool("hdcp_wait_sink_sync", 0644, dir,
+			&debug->dp_debug.hdcp_wait_sink_sync);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs max_bw_code failed, rc=%d\n",
+		DP_ERR("[%s] debugfs hdcp_wait_sink_sync failed, rc=%d\n",
 		       DEBUG_NAME, rc);
+		return rc;
 	}
 
-	file = debugfs_create_file("exe_mode", 0644, dir,
-			debug, &exe_mode_fops);
+	file = debugfs_create_bool("force_encryption", 0644, dir,
+			&debug->dp_debug.force_encryption);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs register failed, rc=%d\n",
+		DP_ERR("[%s] debugfs force_encryption failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
+	}
+
+	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,
@@ -2028,7 +2046,7 @@ static int dp_debug_init(struct dp_debug *dp_debug)
 		rc = PTR_ERR(file);
 		DP_ERR("[%s] debugfs edid failed, rc=%d\n",
 			DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
 	file = debugfs_create_file("dpcd", 0644, dir,
@@ -2037,160 +2055,266 @@ static int dp_debug_init(struct dp_debug *dp_debug)
 		rc = PTR_ERR(file);
 		DP_ERR("[%s] debugfs dpcd failed, rc=%d\n",
 			DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("tpg_ctrl", 0644, dir,
-			debug, &tpg_fops);
+	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 tpg failed, rc=%d\n",
+		DP_ERR("[%s] debugfs create file failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("hdr", 0400, dir,
-		debug, &hdr_fops);
+	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);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("hdr_mst", 0400, dir,
-		debug, &hdr_mst_fops);
-
+	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);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("sim", 0644, dir,
-		debug, &sim_fops);
-
+	file = debugfs_create_file("hdcp", 0644, dir, debug, &hdcp_fops);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs sim failed, rc=%d\n",
+		DP_ERR("[%s] debugfs hdcp failed, rc=%d\n",
 			DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("attention", 0644, dir,
-		debug, &attention_fops);
+	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 attention failed, rc=%d\n",
+		DP_ERR("[%s] debugfs hpd failed, rc=%d\n",
 			DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("dump", 0644, dir,
-		debug, &dump_fops);
-
+	file = debugfs_create_file("sim", 0644, dir, debug, &sim_fops);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs dump failed, rc=%d\n",
+		DP_ERR("[%s] debugfs sim failed, rc=%d\n",
 			DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("mst_mode", 0644, dir,
-			debug, &mst_mode_fops);
+	file = debugfs_create_file("attention", 0644, dir,
+			debug, &attention_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);
-		goto error_remove_dir;
+		DP_ERR("[%s] debugfs attention failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
 	}
 
-	file = debugfs_create_file("mst_sideband_mode", 0644, dir,
-			debug, &mst_sideband_mode_fops);
+	file = debugfs_create_bool("skip_uevent", 0644, dir,
+			&debug->dp_debug.skip_uevent);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs max_bw_code failed, rc=%d\n",
+		DP_ERR("[%s] debugfs skip_uevent failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("max_pclk_khz", 0644, dir,
-			debug, &max_pclk_khz_fops);
+	return rc;
+}
+
+static int dp_debug_init_dsc_fec(struct dp_debug_private *debug,
+		struct dentry *dir)
+{
+	int rc = 0;
+	struct dentry *file;
+
+	file = debugfs_create_bool("dsc_feature_enable", 0644, dir,
+			&debug->parser->dsc_feature_enable);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs max_pclk_khz failed, rc=%d\n",
+		DP_ERR("[%s] debugfs dsc_feature failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_bool("force_encryption", 0644, dir,
-			&debug->dp_debug.force_encryption);
+	file = debugfs_create_bool("fec_feature_enable", 0644, dir,
+			&debug->parser->fec_feature_enable);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs force_encryption failed, rc=%d\n",
+		DP_ERR("[%s] debugfs fec_feature_enable failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_file("hdcp", 0644, dir,
-					debug, &hdcp_fops);
+	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 hdcp failed, rc=%d\n",
-			DEBUG_NAME, rc);
-		goto error_remove_dir;
+		DP_ERR("[%s] debugfs tpg failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		return rc;
 	}
 
-	file = debugfs_create_bool("hdcp_wait_sink_sync", 0644, dir,
-			&debug->dp_debug.hdcp_wait_sink_sync);
+	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 hdcp_wait_sink_sync failed, rc=%d\n",
+		DP_ERR("[%s] debugfs register failed, rc=%d\n",
 		       DEBUG_NAME, rc);
-		goto error_remove_dir;
+		return rc;
 	}
 
-	file = debugfs_create_bool("dsc_feature_enable", 0644, dir,
-			&debug->parser->dsc_feature_enable);
+	file = debugfs_create_file("dump", 0644, dir,
+		debug, &dump_fops);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs dsc_feature failed, rc=%d\n",
-		       DEBUG_NAME, rc);
+		DP_ERR("[%s] debugfs dump failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		return rc;
 	}
 
-	file = debugfs_create_bool("fec_feature_enable", 0644, dir,
-			&debug->parser->fec_feature_enable);
+	return rc;
+}
+
+static int dp_debug_init_feature_toggle(struct dp_debug_private *debug,
+		struct dentry *dir)
+{
+	int rc = 0;
+	struct dentry *file;
+
+	file = debugfs_create_bool("ssc_enable", 0644, dir,
+			&debug->pll->ssc_en);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs fec_feature_enable failed, rc=%d\n",
+		DP_ERR("[%s] debugfs ssc_enable failed, rc=%d\n",
 		       DEBUG_NAME, rc);
+		return rc;
 	}
 
-	file = debugfs_create_file("widebus_mode", 0644, dir,
-			debug, &widebus_mode_fops);
+	file = debugfs_create_bool("widebus_mode", 0644, dir,
+			&debug->parser->has_widebus);
 	if (IS_ERR_OR_NULL(file)) {
 		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs widebus failed, rc=%d\n",
+		DP_ERR("[%s] debugfs widebus_mode failed, rc=%d\n",
 		       DEBUG_NAME, rc);
+		return rc;
 	}
 
-	file = debugfs_create_u32("max_lclk_khz", 0644, dir,
-			&debug->parser->max_lclk_khz);
-	if (IS_ERR_OR_NULL(file)) {
-		rc = PTR_ERR(file);
-		DP_ERR("[%s] debugfs max_lclk_khz failed, rc=%d\n",
+	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;
+
+	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;
+
 	return 0;
 
 error_remove_dir:
-	if (!file)
-		rc = -EINVAL;
 	debugfs_remove_recursive(dir);
 error:
 	return rc;
@@ -2229,7 +2353,7 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in)
 	struct dp_debug *dp_debug;
 
 	if (!in->dev || !in->panel || !in->hpd || !in->link ||
-	    !in->catalog || !in->ctrl) {
+	    !in->catalog || !in->ctrl || !in->pll) {
 		DP_ERR("invalid input\n");
 		rc = -EINVAL;
 		goto error;
@@ -2251,6 +2375,7 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in)
 	debug->catalog = in->catalog;
 	debug->parser = in->parser;
 	debug->ctrl = in->ctrl;
+	debug->pll = in->pll;
 
 	dp_debug = &debug->dp_debug;
 	dp_debug->vdisplay = 0;

+ 7 - 1
msm/dp/dp_debug.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _DP_DEBUG_H_
@@ -12,6 +12,7 @@
 #include "dp_usbpd.h"
 #include "dp_aux.h"
 #include "dp_display.h"
+#include "dp_pll.h"
 
 #define DP_WARN(fmt, ...)	DRM_WARN("[msm-dp-warn] "fmt, ##__VA_ARGS__)
 #define DP_ERR(fmt, ...)	DRM_DEV_ERROR(NULL, "[msm-dp-error]" fmt, \
@@ -35,6 +36,7 @@
  * @tpg_state: specifies whether tpg feature is enabled
  * @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
  * @dp_mst_connector_list: list containing all dp mst connectors
  * @mst_hpd_sim: specifies whether simulated hpd enabled
@@ -56,6 +58,7 @@ struct dp_debug {
 	bool tpg_state;
 	u32 max_pclk_khz;
 	bool force_encryption;
+	bool skip_uevent;
 	char hdcp_status[SZ_128];
 	struct dp_mst_connector dp_mst_connector_list;
 	bool mst_hpd_sim;
@@ -78,6 +81,8 @@ struct dp_debug {
  * @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
  */
 struct dp_debug_in {
 	struct device *dev;
@@ -89,6 +94,7 @@ struct dp_debug_in {
 	struct dp_catalog *catalog;
 	struct dp_parser *parser;
 	struct dp_ctrl *ctrl;
+	struct dp_pll *pll;
 };
 
 /**

+ 52 - 31
msm/dp/dp_display.c

@@ -28,6 +28,7 @@
 #include "dp_display.h"
 #include "sde_hdcp.h"
 #include "dp_debug.h"
+#include "dp_pll.h"
 
 #define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__)
 
@@ -152,6 +153,7 @@ struct dp_display_private {
 	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;
@@ -493,7 +495,8 @@ static void dp_display_deinitialize_hdcp(struct dp_display_private *dp)
 		return;
 	}
 
-	sde_dp_hdcp2p2_deinit(dp->hdcp.data);
+	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)
@@ -527,19 +530,18 @@ static int dp_display_initialize_hdcp(struct dp_display_private *dp)
 
 	fd = sde_hdcp_1x_init(&hdcp_init_data);
 	if (IS_ERR_OR_NULL(fd)) {
-		DP_ERR("Error initializing HDCP 1.x\n");
-		rc = -EINVAL;
-		goto error;
+		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_DEBUG("HDCP 1.3 initialized\n");
+	DP_INFO("HDCP 1.3 initialized\n");
 
 	fd = sde_dp_hdcp2p2_init(&hdcp_init_data);
 	if (IS_ERR_OR_NULL(fd)) {
-		DP_ERR("Error initializing HDCP 2.x\n");
+		DP_DEBUG("Error initializing HDCP 2.x\n");
 		rc = -EINVAL;
 		goto error;
 	}
@@ -547,11 +549,11 @@ static int dp_display_initialize_hdcp(struct dp_display_private *dp)
 	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_DEBUG("HDCP 2.2 initialized\n");
+	DP_INFO("HDCP 2.2 initialized\n");
 
 	return 0;
 error:
-	dp_display_deinitialize_hdcp(dp);
+	sde_hdcp_1x_deinit(dp->hdcp.dev[HDCP_VERSION_1X].fd);
 
 	return rc;
 }
@@ -661,8 +663,9 @@ static void dp_display_send_hpd_event(struct dp_display_private *dp)
 	envp[2] = bpp;
 	envp[3] = pattern;
 	envp[4] = NULL;
-	kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE,
-			envp);
+	if (!dp->debug->skip_uevent)
+		kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE,
+				envp);
 
 	if (connector->status == connector_status_connected) {
 		dp_display_state_add(DP_STATE_CONNECT_NOTIFIED);
@@ -1346,17 +1349,18 @@ static int dp_display_get_usb_extcon(struct dp_display_private *dp)
 
 static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
 {
+	dp_debug_put(dp->debug);
+	dp_hpd_put(dp->hpd);
 	dp_audio_put(dp->panel->audio);
 	dp_ctrl_put(dp->ctrl);
-	dp_link_put(dp->link);
 	dp_panel_put(dp->panel);
-	dp_aux_put(dp->aux);
+	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);
-	dp_hpd_put(dp->hpd);
 	mutex_destroy(&dp->session_lock);
-	dp_debug_put(dp->debug);
 }
 
 static int dp_init_sub_modules(struct dp_display_private *dp)
@@ -1374,6 +1378,9 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
 	struct dp_debug_in debug_in = {
 		.dev = dev,
 	};
+	struct dp_pll_in pll_in = {
+		.pdev = dp->pdev,
+	};
 
 	mutex_init(&dp->session_lock);
 
@@ -1401,21 +1408,6 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
 		goto error_catalog;
 	}
 
-	dp->power = dp_power_get(dp->parser);
-	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_aux;
-	}
-
 	dp->aux = dp_aux_get(dev, &dp->catalog->aux, dp->parser,
 			dp->aux_switch_node);
 	if (IS_ERR(dp->aux)) {
@@ -1428,6 +1420,32 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
 	rc = dp->aux->drm_aux_register(dp->aux);
 	if (rc) {
 		DP_ERR("DRM DP AUX register failed\n");
+		goto error_pll;
+	}
+
+	pll_in.aux = dp->aux;
+	pll_in.parser = dp->parser;
+
+	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;
 	}
 
@@ -1503,6 +1521,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
 	debug_in.catalog = dp->catalog;
 	debug_in.parser = dp->parser;
 	debug_in.ctrl = dp->ctrl;
+	debug_in.pll = dp->pll;
 
 	dp->debug = dp_debug_get(&debug_in);
 	if (IS_ERR(dp->debug)) {
@@ -1540,10 +1559,12 @@ error_ctrl:
 error_panel:
 	dp_link_put(dp->link);
 error_link:
-	dp_aux_put(dp->aux);
-error_aux:
 	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);

+ 17 - 5
msm/dp/dp_panel.c

@@ -1528,6 +1528,18 @@ skip_dpcd_read:
 
 	link_info->num_lanes = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
 
+	if (is_link_rate_valid(panel->dp_panel.link_bw_code)) {
+		DP_DEBUG("debug link bandwidth code: 0x%x\n",
+				panel->dp_panel.link_bw_code);
+		link_info->rate = drm_dp_bw_code_to_link_rate(
+				panel->dp_panel.link_bw_code);
+	}
+
+	if (is_lane_count_valid(panel->dp_panel.lane_count)) {
+		DP_DEBUG("debug lane count: %d\n", panel->dp_panel.lane_count);
+		link_info->num_lanes = panel->dp_panel.lane_count;
+	}
+
 	if (multi_func)
 		link_info->num_lanes = min_t(unsigned int,
 			link_info->num_lanes, 2);
@@ -2272,11 +2284,6 @@ static int dp_panel_deinit_panel_info(struct dp_panel *dp_panel, u32 flags)
 	struct drm_connector *connector;
 	struct sde_connector_state *c_state;
 
-	if (!dp_panel) {
-		DP_ERR("invalid input\n");
-		return -EINVAL;
-	}
-
 	if (flags & DP_PANEL_SRC_INITIATED_POWER_DOWN) {
 		DP_DEBUG("retain states in src initiated power down request\n");
 		return 0;
@@ -2318,6 +2325,9 @@ static int dp_panel_deinit_panel_info(struct dp_panel *dp_panel, u32 flags)
 	memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta));
 	memset(&c_state->dyn_hdr_meta, 0, sizeof(c_state->dyn_hdr_meta));
 
+	dp_panel->link_bw_code = 0;
+	dp_panel->lane_count = 0;
+
 	return rc;
 }
 
@@ -3001,6 +3011,8 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
 	dp_panel = &panel->dp_panel;
 	dp_panel->max_bw_code = DP_LINK_BW_8_1;
 	dp_panel->spd_enabled = true;
+	dp_panel->link_bw_code = 0;
+	dp_panel->lane_count = 0;
 	memcpy(panel->spd_vendor_name, vendor_name, (sizeof(u8) * 8));
 	memcpy(panel->spd_product_description, product_desc, (sizeof(u8) * 16));
 	dp_panel->connector = in->connector;

+ 2 - 0
msm/dp/dp_panel.h

@@ -108,6 +108,8 @@ struct dp_panel {
 
 	/* debug */
 	u32 max_bw_code;
+	u32 lane_count;
+	u32 link_bw_code;
 
 	/* By default, stream_id is assigned to DP_INVALID_STREAM.
 	 * Client sets the stream id value using set_stream_id interface.

+ 2 - 1
msm/dp/dp_parser.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/of_gpio.h>
@@ -297,6 +297,7 @@ static const char *dp_parser_supply_node_name(enum dp_pm_type module)
 	case DP_CORE_PM:	return "qcom,core-supply-entries";
 	case DP_CTRL_PM:	return "qcom,ctrl-supply-entries";
 	case DP_PHY_PM:		return "qcom,phy-supply-entries";
+	case DP_PLL_PM:		return "qcom,pll-supply-entries";
 	default:		return "???";
 	}
 }

+ 3 - 1
msm/dp/dp_parser.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _DP_PARSER_H_
@@ -21,6 +21,7 @@ enum dp_pm_type {
 	DP_STREAM0_PM,
 	DP_STREAM1_PM,
 	DP_LINK_PM,
+	DP_PLL_PM,
 	DP_MAX_PM
 };
 
@@ -33,6 +34,7 @@ static inline const char *dp_parser_pm_name(enum dp_pm_type module)
 	case DP_STREAM0_PM:	return "DP_STREAM0_PM";
 	case DP_STREAM1_PM:	return "DP_STREAM1_PM";
 	case DP_LINK_PM:	return "DP_LINK_PM";
+	case DP_PLL_PM:		return "DP_PLL_PM";
 	default:		return "???";
 	}
 }

+ 143 - 0
msm/dp/dp_pll.c

@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include "dp_debug.h"
+#include "dp_pll.h"
+
+static int dp_pll_fill_io(struct dp_pll *pll)
+{
+	struct dp_parser *parser = pll->parser;
+
+	pll->io.dp_phy = parser->get_io(parser, "dp_phy");
+	if (!pll->io.dp_phy) {
+		DP_ERR("Invalid dp_phy resource\n");
+		return -ENOMEM;
+	}
+
+	pll->io.dp_pll = parser->get_io(parser, "dp_pll");
+	if (!pll->io.dp_pll) {
+		DP_ERR("Invalid dp_pll resource\n");
+		return -ENOMEM;
+	}
+
+	pll->io.dp_ln_tx0 = parser->get_io(parser, "dp_ln_tx0");
+	if (!pll->io.dp_ln_tx0) {
+		DP_ERR("Invalid dp_ln_tx1 resource\n");
+		return -ENOMEM;
+	}
+
+	pll->io.dp_ln_tx1 = parser->get_io(parser, "dp_ln_tx1");
+	if (!pll->io.dp_ln_tx1) {
+		DP_ERR("Invalid dp_ln_tx1 resource\n");
+		return -ENOMEM;
+	}
+
+	pll->io.gdsc = parser->get_io(parser, "gdsc");
+	if (!pll->io.gdsc) {
+		DP_ERR("Invalid gdsc resource\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int dp_pll_clock_register(struct dp_pll *pll)
+{
+	int rc;
+
+	switch (pll->revision) {
+	case DP_PLL_5NM_V1:
+	case DP_PLL_5NM_V2:
+		rc = dp_pll_clock_register_5nm(pll);
+		break;
+	default:
+		rc = -ENOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+
+static void dp_pll_clock_unregister(struct dp_pll *pll)
+{
+	switch (pll->revision) {
+	case DP_PLL_5NM_V1:
+	case DP_PLL_5NM_V2:
+		dp_pll_clock_unregister_5nm(pll);
+		break;
+	default:
+		break;
+	}
+}
+
+struct dp_pll *dp_pll_get(struct dp_pll_in *in)
+{
+	int rc = 0;
+	struct dp_pll *pll;
+	struct dp_parser *parser;
+	const char *label = NULL;
+	struct platform_device *pdev;
+
+	if (!in || !in->pdev || !in->pdev->dev.of_node || !in->parser) {
+		DP_ERR("Invalid resource pointers\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+	pll->pdev = in->pdev;
+	pll->parser = in->parser;
+	pll->aux = in->aux;
+	parser = pll->parser;
+	pdev = pll->pdev;
+
+	label = of_get_property(pdev->dev.of_node, "qcom,pll-revision", NULL);
+	if (label) {
+		if (!strcmp(label, "5nm-v1")) {
+			pll->revision = DP_PLL_5NM_V1;
+		} else if (!strcmp(label, "5nm-v2")) {
+			pll->revision = DP_PLL_5NM_V2;
+		} else {
+			DP_ERR("Unsupported pll revision\n");
+			rc = -ENOTSUPP;
+			goto error;
+		}
+	} else {
+		DP_ERR("pll revision not specified\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	pll->ssc_en = of_property_read_bool(pdev->dev.of_node,
+						"qcom,ssc-feature-enable");
+	pll->bonding_en = of_property_read_bool(pdev->dev.of_node,
+						"qcom,bonding-feature-enable");
+
+	rc = dp_pll_fill_io(pll);
+	if (rc)
+		goto error;
+
+	rc = dp_pll_clock_register(pll);
+	if (rc)
+		goto error;
+
+	DP_INFO("revision=%s, ssc_en=%d, bonding_en=%d\n",
+			dp_pll_get_revision(pll->revision), pll->ssc_en,
+			pll->bonding_en);
+
+	return pll;
+error:
+	kfree(pll);
+	return ERR_PTR(rc);
+}
+
+void dp_pll_put(struct dp_pll *pll)
+{
+	dp_pll_clock_unregister(pll);
+	kfree(pll);
+}

+ 142 - 0
msm/dp/dp_pll.h

@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __DP_PLL_H
+#define __DP_PLL_H
+
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/of_device.h>
+#include "dp_parser.h"
+#include "sde_dbg.h"
+
+#define DP_VCO_HSCLK_RATE_1620MHZDIV1000	1620000UL
+#define DP_VCO_HSCLK_RATE_2700MHZDIV1000	2700000UL
+#define DP_VCO_HSCLK_RATE_5400MHZDIV1000	5400000UL
+#define DP_VCO_HSCLK_RATE_8100MHZDIV1000	8100000UL
+
+#define dp_pll_get_base(x) pll->io.x->io.base
+
+#define dp_pll_read(x, offset) ({ \
+		readl_relaxed((dp_pll_get_base(x)) + (offset)); \
+})
+
+#define dp_pll_write(x, offset, data) ({ \
+		DP_DEBUG(#offset", addr=0x%x, val=0x%x\n", \
+				(dp_pll_get_base(x)) + (offset), (data)); \
+		SDE_EVT32_VERBOSE((dp_pll_get_base(x)) + (offset), (data)); \
+		writel_relaxed((data), (dp_pll_get_base(x)) + (offset)); \
+})
+
+enum dp_pll_revision {
+	DP_PLL_UNKNOWN,
+	DP_PLL_5NM_V1,
+	DP_PLL_5NM_V2,
+};
+
+static inline const char *dp_pll_get_revision(enum dp_pll_revision rev)
+{
+	switch (rev) {
+	case DP_PLL_UNKNOWN:	return "DP_PLL_UNKNOWN";
+	case DP_PLL_5NM_V1:	return "DP_PLL_5NM_V1";
+	case DP_PLL_5NM_V2:	return "DP_PLL_5NM_V2";
+	default:		return "???";
+	}
+}
+
+struct dp_pll_io {
+	struct dp_io_data *dp_phy;
+	struct dp_io_data *dp_pll;
+	struct dp_io_data *dp_ln_tx0;
+	struct dp_io_data *dp_ln_tx1;
+	struct dp_io_data *gdsc;
+};
+
+struct dp_pll_vco_clk {
+	struct clk_hw hw;
+	unsigned long	rate;		/* current vco rate */
+	u64		min_rate;	/* min vco rate */
+	u64		max_rate;	/* max vco rate */
+	void		*priv;
+};
+
+struct dp_pll {
+	/*
+	 * target pll revision information
+	 */
+	u32		revision;
+
+	/*
+	 * Certain plls needs to update the same vco rate after resume in
+	 * suspend/resume scenario. Cached the vco rate for such plls.
+	 */
+	unsigned long	vco_cached_rate;
+
+	/*
+	 * PLL index if multiple index are available. Eg. in case of
+	 * DSI we have 2 plls.
+	 */
+	uint32_t index;
+
+	bool ssc_en;
+	bool bonding_en;
+
+	void *priv;
+	struct platform_device *pdev;
+	struct dp_parser *parser;
+	struct dp_power *power;
+	struct dp_aux *aux;
+	struct dp_pll_io io;
+	struct clk_onecell_data *clk_data;
+};
+
+struct dp_pll_db {
+	struct dp_pll *pll;
+
+	/* lane and orientation settings */
+	u8 lane_cnt;
+	u8 orientation;
+
+	/* COM PHY settings */
+	u32 hsclk_sel;
+	u32 dec_start_mode0;
+	u32 div_frac_start1_mode0;
+	u32 div_frac_start2_mode0;
+	u32 div_frac_start3_mode0;
+	u32 integloop_gain0_mode0;
+	u32 integloop_gain1_mode0;
+	u32 lock_cmp1_mode0;
+	u32 lock_cmp2_mode0;
+	u32 lock_cmp_en;
+	u32 ssc_step_size1_mode0;
+	u32 ssc_step_size2_mode0;
+
+	/* PHY vco divider */
+	u32 phy_vco_div;
+};
+
+
+static inline struct dp_pll_vco_clk *to_dp_vco_hw(struct clk_hw *hw)
+{
+	return container_of(hw, struct dp_pll_vco_clk, hw);
+}
+
+static inline bool is_gdsc_disabled(struct dp_pll *pll)
+{
+	return (dp_pll_read(gdsc, 0x0) & BIT(31)) ? false : true;
+}
+
+int dp_pll_clock_register_5nm(struct dp_pll *pll);
+void dp_pll_clock_unregister_5nm(struct dp_pll *pll);
+
+struct dp_pll_in {
+	struct platform_device *pdev;
+	struct dp_aux *aux;
+	struct dp_parser *parser;
+};
+
+struct dp_pll *dp_pll_get(struct dp_pll_in *in);
+void dp_pll_put(struct dp_pll *pll);
+#endif /* __DP_PLL_H */

+ 1038 - 0
msm/dp/dp_pll_5nm.c

@@ -0,0 +1,1038 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * Display Port PLL driver block diagram for branch clocks
+ *
+ *		+------------------------------+
+ *		|         DP_VCO_CLK           |
+ *		|                              |
+ *		|    +-------------------+     |
+ *		|    |   (DP PLL/VCO)    |     |
+ *		|    +---------+---------+     |
+ *		|              v               |
+ *		|   +----------+-----------+   |
+ *		|   | hsclk_divsel_clk_src |   |
+ *		|   +----------+-----------+   |
+ *		+------------------------------+
+ *				|
+ *	 +------------<---------v------------>----------+
+ *	 |                                              |
+ * +-----v------------+                                 |
+ * | dp_link_clk_src  |                                 |
+ * |    divsel_ten    |                                 |
+ * +---------+--------+                                 |
+ *	|                                               |
+ *	|                                               |
+ *	v                                               v
+ * Input to DISPCC block                                |
+ * for link clk, crypto clk                             |
+ * and interface clock                                  |
+ *							|
+ *							|
+ *	+--------<------------+-----------------+---<---+
+ *	|                     |                 |
+ * +-------v------+  +--------v-----+  +--------v------+
+ * | vco_divided  |  | vco_divided  |  | vco_divided   |
+ * |    _clk_src  |  |    _clk_src  |  |    _clk_src   |
+ * |              |  |              |  |               |
+ * |divsel_six    |  |  divsel_two  |  |  divsel_four  |
+ * +-------+------+  +-----+--------+  +--------+------+
+ *         |	           |		        |
+ *	v------->----------v-------------<------v
+ *                         |
+ *		+----------+---------+
+ *		|   vco_divided_clk  |
+ *		|       _src_mux     |
+ *		+---------+----------+
+ *                        |
+ *                        v
+ *              Input to DISPCC block
+ *              for DP pixel clock
+ *
+ */
+
+#include <dt-bindings/clock/mdss-5nm-pll-clk.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include "clk-regmap-mux.h"
+#include "dp_hpd.h"
+#include "dp_debug.h"
+#include "dp_pll.h"
+
+#define DP_PHY_CFG				0x0010
+#define DP_PHY_CFG_1				0x0014
+#define DP_PHY_PD_CTL				0x0018
+#define DP_PHY_MODE				0x001C
+
+#define DP_PHY_AUX_CFG1				0x0024
+#define DP_PHY_AUX_CFG2				0x0028
+
+#define DP_PHY_VCO_DIV				0x0070
+#define DP_PHY_TX0_TX1_LANE_CTL			0x0078
+#define DP_PHY_TX2_TX3_LANE_CTL			0x009C
+
+#define DP_PHY_SPARE0				0x00C8
+#define DP_PHY_STATUS				0x00DC
+
+/* Tx registers */
+#define TXn_CLKBUF_ENABLE			0x0008
+#define TXn_TX_EMP_POST1_LVL			0x000C
+
+#define TXn_TX_DRV_LVL				0x0014
+
+#define TXn_RESET_TSYNC_EN			0x001C
+#define TXn_PRE_STALL_LDO_BOOST_EN		0x0020
+#define TXn_TX_BAND				0x0024
+#define TXn_INTERFACE_SELECT			0x002C
+
+#define TXn_RES_CODE_LANE_OFFSET_TX		0x003C
+#define TXn_RES_CODE_LANE_OFFSET_RX		0x0040
+
+#define TXn_TRANSCEIVER_BIAS_EN			0x0054
+#define TXn_HIGHZ_DRVR_EN			0x0058
+#define TXn_TX_POL_INV				0x005C
+#define TXn_PARRATE_REC_DETECT_IDLE_EN		0x0060
+
+/* PLL register offset */
+#define QSERDES_COM_BG_TIMER			0x000C
+#define QSERDES_COM_SSC_EN_CENTER		0x0010
+#define QSERDES_COM_SSC_ADJ_PER1		0x0014
+#define QSERDES_COM_SSC_PER1			0x001C
+#define QSERDES_COM_SSC_PER2			0x0020
+#define QSERDES_COM_SSC_STEP_SIZE1_MODE0	0x0024
+#define QSERDES_COM_SSC_STEP_SIZE2_MODE0	0X0028
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN		0x0044
+#define QSERDES_COM_CLK_ENABLE1			0x0048
+#define QSERDES_COM_SYS_CLK_CTRL		0x004C
+#define QSERDES_COM_SYSCLK_BUF_ENABLE		0x0050
+#define QSERDES_COM_PLL_IVCO			0x0058
+
+#define QSERDES_COM_CP_CTRL_MODE0		0x0074
+#define QSERDES_COM_PLL_RCTRL_MODE0		0x007C
+#define QSERDES_COM_PLL_CCTRL_MODE0		0x0084
+#define QSERDES_COM_SYSCLK_EN_SEL		0x0094
+#define QSERDES_COM_RESETSM_CNTRL		0x009C
+#define QSERDES_COM_LOCK_CMP_EN			0x00A4
+#define QSERDES_COM_LOCK_CMP1_MODE0		0x00AC
+#define QSERDES_COM_LOCK_CMP2_MODE0		0x00B0
+
+#define QSERDES_COM_DEC_START_MODE0		0x00BC
+#define QSERDES_COM_DIV_FRAC_START1_MODE0	0x00CC
+#define QSERDES_COM_DIV_FRAC_START2_MODE0	0x00D0
+#define QSERDES_COM_DIV_FRAC_START3_MODE0	0x00D4
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0	0x00EC
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0	0x00F0
+#define QSERDES_COM_VCO_TUNE_CTRL		0x0108
+#define QSERDES_COM_VCO_TUNE_MAP		0x010C
+
+#define QSERDES_COM_CMN_STATUS			0x0140
+#define QSERDES_COM_CLK_SEL			0x0154
+#define QSERDES_COM_HSCLK_SEL			0x0158
+
+#define QSERDES_COM_CORECLK_DIV_MODE0		0x0168
+
+#define QSERDES_COM_CORE_CLK_EN			0x0174
+#define QSERDES_COM_C_READY_STATUS		0x0178
+#define QSERDES_COM_CMN_CONFIG			0x017C
+
+#define QSERDES_COM_SVS_MODE_CLK_SEL		0x0184
+
+/* Tx tran offsets */
+#define DP_TRAN_DRVR_EMP_EN			0x00C0
+#define DP_TX_INTERFACE_MODE			0x00C4
+
+/* Tx VMODE offsets */
+#define DP_VMODE_CTRL1				0x00C8
+
+#define DP_PHY_PLL_POLL_SLEEP_US		500
+#define DP_PHY_PLL_POLL_TIMEOUT_US		10000
+
+#define DP_VCO_RATE_8100MHZDIV1000		8100000UL
+#define DP_VCO_RATE_9720MHZDIV1000		9720000UL
+#define DP_VCO_RATE_10800MHZDIV1000		10800000UL
+
+#define DP_5NM_C_READY		BIT(0)
+#define DP_5NM_FREQ_DONE	BIT(0)
+#define DP_5NM_PLL_LOCKED	BIT(1)
+#define DP_5NM_PHY_READY	BIT(1)
+#define DP_5NM_TSYNC_DONE	BIT(0)
+
+static int dp_vco_pll_init_db_5nm(struct dp_pll_db *pdb,
+		unsigned long rate)
+{
+	struct dp_pll *pll = pdb->pll;
+	u32 spare_value = 0;
+
+	spare_value = dp_pll_read(dp_phy, DP_PHY_SPARE0);
+	pdb->lane_cnt = spare_value & 0x0F;
+	pdb->orientation = (spare_value & 0xF0) >> 4;
+
+	DP_DEBUG("spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n",
+			spare_value, pdb->lane_cnt, pdb->orientation);
+
+	pdb->div_frac_start1_mode0 = 0x00;
+	pdb->integloop_gain0_mode0 = 0x3f;
+	pdb->integloop_gain1_mode0 = 0x00;
+
+	switch (rate) {
+	case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
+		DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_9720MHZDIV1000);
+		pdb->hsclk_sel = 0x05;
+		pdb->dec_start_mode0 = 0x69;
+		pdb->div_frac_start2_mode0 = 0x80;
+		pdb->div_frac_start3_mode0 = 0x07;
+		pdb->lock_cmp1_mode0 = 0x6f;
+		pdb->lock_cmp2_mode0 = 0x08;
+		pdb->phy_vco_div = 0x1;
+		pdb->lock_cmp_en = 0x04;
+		pdb->ssc_step_size1_mode0 = 0x45;
+		pdb->ssc_step_size2_mode0 = 0x06;
+		break;
+	case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
+		DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
+		pdb->hsclk_sel = 0x03;
+		pdb->dec_start_mode0 = 0x69;
+		pdb->div_frac_start2_mode0 = 0x80;
+		pdb->div_frac_start3_mode0 = 0x07;
+		pdb->lock_cmp1_mode0 = 0x0f;
+		pdb->lock_cmp2_mode0 = 0x0e;
+		pdb->phy_vco_div = 0x1;
+		pdb->lock_cmp_en = 0x08;
+		pdb->ssc_step_size1_mode0 = 0x45;
+		pdb->ssc_step_size2_mode0 = 0x06;
+		break;
+	case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
+		DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_10800MHZDIV1000);
+		pdb->hsclk_sel = 0x01;
+		pdb->dec_start_mode0 = 0x8c;
+		pdb->div_frac_start2_mode0 = 0x00;
+		pdb->div_frac_start3_mode0 = 0x0a;
+		pdb->lock_cmp1_mode0 = 0x1f;
+		pdb->lock_cmp2_mode0 = 0x1c;
+		pdb->phy_vco_div = 0x2;
+		pdb->lock_cmp_en = 0x08;
+		pdb->ssc_step_size1_mode0 = 0x5c;
+		pdb->ssc_step_size2_mode0 = 0x08;
+		break;
+	case DP_VCO_HSCLK_RATE_8100MHZDIV1000:
+		DP_DEBUG("VCO rate: %ld\n", DP_VCO_RATE_8100MHZDIV1000);
+		pdb->hsclk_sel = 0x00;
+		pdb->dec_start_mode0 = 0x69;
+		pdb->div_frac_start2_mode0 = 0x80;
+		pdb->div_frac_start3_mode0 = 0x07;
+		pdb->lock_cmp1_mode0 = 0x2f;
+		pdb->lock_cmp2_mode0 = 0x2a;
+		pdb->phy_vco_div = 0x0;
+		pdb->lock_cmp_en = 0x08;
+		pdb->ssc_step_size1_mode0 = 0x45;
+		pdb->ssc_step_size2_mode0 = 0x06;
+		break;
+	default:
+		DP_ERR("unsupported rate %ld\n", rate);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int dp_config_vco_rate_5nm(struct dp_pll_vco_clk *vco,
+		unsigned long rate)
+{
+	int res = 0;
+	struct dp_pll *pll = vco->priv;
+	struct dp_pll_db *pdb = (struct dp_pll_db *)pll->priv;
+
+	res = dp_vco_pll_init_db_5nm(pdb, rate);
+	if (res) {
+		DP_ERR("VCO Init DB failed\n");
+		return res;
+	}
+
+	dp_pll_write(dp_phy, DP_PHY_CFG_1, 0x0F);
+
+	if (pdb->lane_cnt != 4) {
+		if (pdb->orientation == ORIENTATION_CC2)
+			dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x6d);
+		else
+			dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x75);
+	} else {
+		dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x7d);
+	}
+
+	/* Make sure the PHY register writes are done */
+	wmb();
+
+	dp_pll_write(dp_pll, QSERDES_COM_SVS_MODE_CLK_SEL, 0x05);
+	dp_pll_write(dp_pll, QSERDES_COM_SYSCLK_EN_SEL, 0x3b);
+	dp_pll_write(dp_pll, QSERDES_COM_SYS_CLK_CTRL, 0x02);
+	dp_pll_write(dp_pll, QSERDES_COM_CLK_ENABLE1, 0x0c);
+	dp_pll_write(dp_pll, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
+	dp_pll_write(dp_pll, QSERDES_COM_CLK_SEL, 0x30);
+	/* Make sure the PHY register writes are done */
+	wmb();
+
+	/* PLL Optimization */
+	dp_pll_write(dp_pll, QSERDES_COM_PLL_IVCO, 0x0f);
+	dp_pll_write(dp_pll, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
+	dp_pll_write(dp_pll, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
+	dp_pll_write(dp_pll, QSERDES_COM_CP_CTRL_MODE0, 0x06);
+	/* Make sure the PLL register writes are done */
+	wmb();
+
+	/* link rate dependent params */
+	dp_pll_write(dp_pll, QSERDES_COM_HSCLK_SEL, pdb->hsclk_sel);
+	dp_pll_write(dp_pll, QSERDES_COM_DEC_START_MODE0, pdb->dec_start_mode0);
+	dp_pll_write(dp_pll,
+		QSERDES_COM_DIV_FRAC_START1_MODE0, pdb->div_frac_start1_mode0);
+	dp_pll_write(dp_pll,
+		QSERDES_COM_DIV_FRAC_START2_MODE0, pdb->div_frac_start2_mode0);
+	dp_pll_write(dp_pll,
+		QSERDES_COM_DIV_FRAC_START3_MODE0, pdb->div_frac_start3_mode0);
+	dp_pll_write(dp_pll, QSERDES_COM_LOCK_CMP1_MODE0, pdb->lock_cmp1_mode0);
+	dp_pll_write(dp_pll, QSERDES_COM_LOCK_CMP2_MODE0, pdb->lock_cmp2_mode0);
+	dp_pll_write(dp_pll, QSERDES_COM_LOCK_CMP_EN, pdb->lock_cmp_en);
+	dp_pll_write(dp_phy, DP_PHY_VCO_DIV, pdb->phy_vco_div);
+	/* Make sure the PLL register writes are done */
+	wmb();
+
+	dp_pll_write(dp_pll, QSERDES_COM_CMN_CONFIG, 0x02);
+	dp_pll_write(dp_pll, QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x3f);
+	dp_pll_write(dp_pll, QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00);
+	dp_pll_write(dp_pll, QSERDES_COM_VCO_TUNE_MAP, 0x00);
+	/* Make sure the PHY register writes are done */
+	wmb();
+
+	dp_pll_write(dp_pll, QSERDES_COM_BG_TIMER, 0x0a);
+	dp_pll_write(dp_pll, QSERDES_COM_CORECLK_DIV_MODE0, 0x0a);
+	dp_pll_write(dp_pll, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
+	if (pll->bonding_en)
+		dp_pll_write(dp_pll, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1f);
+	else
+		dp_pll_write(dp_pll, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17);
+	dp_pll_write(dp_pll, QSERDES_COM_CORE_CLK_EN, 0x1f);
+	/* Make sure the PHY register writes are done */
+	wmb();
+
+	if (pll->ssc_en) {
+		dp_pll_write(dp_pll, QSERDES_COM_SSC_EN_CENTER, 0x01);
+		dp_pll_write(dp_pll, QSERDES_COM_SSC_ADJ_PER1, 0x00);
+		dp_pll_write(dp_pll, QSERDES_COM_SSC_PER1, 0x36);
+		dp_pll_write(dp_pll, QSERDES_COM_SSC_PER2, 0x01);
+		dp_pll_write(dp_pll, QSERDES_COM_SSC_STEP_SIZE1_MODE0,
+				pdb->ssc_step_size1_mode0);
+		dp_pll_write(dp_pll, QSERDES_COM_SSC_STEP_SIZE2_MODE0,
+				pdb->ssc_step_size2_mode0);
+	}
+
+	if (pdb->orientation == ORIENTATION_CC2)
+		dp_pll_write(dp_phy, DP_PHY_MODE, 0x4c);
+	else
+		dp_pll_write(dp_phy, DP_PHY_MODE, 0x5c);
+
+	dp_pll_write(dp_phy, DP_PHY_AUX_CFG1, 0x13);
+	dp_pll_write(dp_phy, DP_PHY_AUX_CFG2, 0xA4);
+	/* Make sure the PLL register writes are done */
+	wmb();
+
+	/* TX-0 register configuration */
+	dp_pll_write(dp_phy, DP_PHY_TX0_TX1_LANE_CTL, 0x05);
+	dp_pll_write(dp_ln_tx0, DP_VMODE_CTRL1, 0x40);
+	dp_pll_write(dp_ln_tx0, TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
+	dp_pll_write(dp_ln_tx0, TXn_INTERFACE_SELECT, 0x3b);
+	dp_pll_write(dp_ln_tx0, TXn_CLKBUF_ENABLE, 0x0f);
+	dp_pll_write(dp_ln_tx0, TXn_RESET_TSYNC_EN, 0x03);
+	dp_pll_write(dp_ln_tx0, DP_TRAN_DRVR_EMP_EN, 0xf);
+	dp_pll_write(dp_ln_tx0, TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
+	dp_pll_write(dp_ln_tx0, DP_TX_INTERFACE_MODE, 0x00);
+	dp_pll_write(dp_ln_tx0, TXn_RES_CODE_LANE_OFFSET_TX, 0x11);
+	dp_pll_write(dp_ln_tx0, TXn_RES_CODE_LANE_OFFSET_RX, 0x11);
+	dp_pll_write(dp_ln_tx0, TXn_TX_BAND, 0x04);
+	/* Make sure the PLL register writes are done */
+	wmb();
+
+	/* TX-1 register configuration */
+	dp_pll_write(dp_phy, DP_PHY_TX2_TX3_LANE_CTL, 0x05);
+	dp_pll_write(dp_ln_tx1, DP_VMODE_CTRL1, 0x40);
+	dp_pll_write(dp_ln_tx1, TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
+	dp_pll_write(dp_ln_tx1, TXn_INTERFACE_SELECT, 0x3b);
+	dp_pll_write(dp_ln_tx1, TXn_CLKBUF_ENABLE, 0x0f);
+	dp_pll_write(dp_ln_tx1, TXn_RESET_TSYNC_EN, 0x03);
+	dp_pll_write(dp_ln_tx1, DP_TRAN_DRVR_EMP_EN, 0xf);
+	dp_pll_write(dp_ln_tx1, TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
+	dp_pll_write(dp_ln_tx1, DP_TX_INTERFACE_MODE, 0x00);
+	dp_pll_write(dp_ln_tx1, TXn_RES_CODE_LANE_OFFSET_TX, 0x11);
+	dp_pll_write(dp_ln_tx1, TXn_RES_CODE_LANE_OFFSET_RX, 0x11);
+	dp_pll_write(dp_ln_tx1, TXn_TX_BAND, 0x04);
+	/* Make sure the PHY register writes are done */
+	wmb();
+
+	return res;
+}
+
+enum dp_5nm_pll_status {
+	C_READY,
+	FREQ_DONE,
+	PLL_LOCKED,
+	PHY_READY,
+	TSYNC_DONE,
+};
+
+char *dp_5nm_pll_get_status_name(enum dp_5nm_pll_status status)
+{
+	switch (status) {
+	case C_READY:
+		return "C_READY";
+	case FREQ_DONE:
+		return "FREQ_DONE";
+	case PLL_LOCKED:
+		return "PLL_LOCKED";
+	case PHY_READY:
+		return "PHY_READY";
+	case TSYNC_DONE:
+		return "TSYNC_DONE";
+	default:
+		return "unknown";
+	}
+}
+
+static bool dp_5nm_pll_get_status(struct dp_pll *pll,
+		enum dp_5nm_pll_status status)
+{
+	u32 reg, state, bit;
+	void __iomem *base;
+	bool success = true;
+
+	switch (status) {
+	case C_READY:
+		base = dp_pll_get_base(dp_pll);
+		reg = QSERDES_COM_C_READY_STATUS;
+		bit = DP_5NM_C_READY;
+		break;
+	case FREQ_DONE:
+		base = dp_pll_get_base(dp_pll);
+		reg = QSERDES_COM_CMN_STATUS;
+		bit = DP_5NM_FREQ_DONE;
+		break;
+	case PLL_LOCKED:
+		base = dp_pll_get_base(dp_pll);
+		reg = QSERDES_COM_CMN_STATUS;
+		bit = DP_5NM_PLL_LOCKED;
+		break;
+	case PHY_READY:
+		base = dp_pll_get_base(dp_phy);
+		reg = DP_PHY_STATUS;
+		bit = DP_5NM_PHY_READY;
+		break;
+	case TSYNC_DONE:
+		base = dp_pll_get_base(dp_phy);
+		reg = DP_PHY_STATUS;
+		bit = DP_5NM_TSYNC_DONE;
+		break;
+	default:
+		return false;
+	}
+
+	if (readl_poll_timeout_atomic((base + reg), state,
+			((state & bit) > 0),
+			DP_PHY_PLL_POLL_SLEEP_US,
+			DP_PHY_PLL_POLL_TIMEOUT_US)) {
+		DP_ERR("%s failed, status=%x\n",
+			dp_5nm_pll_get_status_name(status), state);
+
+		success = false;
+	}
+
+	return success;
+}
+
+static int dp_pll_enable_5nm(struct clk_hw *hw)
+{
+	int rc = 0;
+	struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
+	struct dp_pll *pll = vco->priv;
+
+	pll->aux->state &= ~DP_STATE_PLL_LOCKED;
+
+	dp_pll_write(dp_phy, DP_PHY_CFG, 0x01);
+	dp_pll_write(dp_phy, DP_PHY_CFG, 0x05);
+	dp_pll_write(dp_phy, DP_PHY_CFG, 0x01);
+	dp_pll_write(dp_phy, DP_PHY_CFG, 0x09);
+	dp_pll_write(dp_pll, QSERDES_COM_RESETSM_CNTRL, 0x20);
+	wmb();	/* Make sure the PLL register writes are done */
+
+	if (!dp_5nm_pll_get_status(pll, C_READY)) {
+		rc = -EINVAL;
+		goto lock_err;
+	}
+
+	if (!dp_5nm_pll_get_status(pll, FREQ_DONE)) {
+		rc = -EINVAL;
+		goto lock_err;
+	}
+
+	if (!dp_5nm_pll_get_status(pll, PLL_LOCKED)) {
+		rc = -EINVAL;
+		goto lock_err;
+	}
+
+	dp_pll_write(dp_phy, DP_PHY_CFG, 0x19);
+	/* Make sure the PHY register writes are done */
+	wmb();
+
+	if (!dp_5nm_pll_get_status(pll, TSYNC_DONE)) {
+		rc = -EINVAL;
+		goto lock_err;
+	}
+
+	if (!dp_5nm_pll_get_status(pll, PHY_READY)) {
+		rc = -EINVAL;
+		goto lock_err;
+	}
+
+	pll->aux->state |= DP_STATE_PLL_LOCKED;
+	DP_DEBUG("PLL is locked\n");
+lock_err:
+	return rc;
+}
+
+static int dp_pll_disable_5nm(struct clk_hw *hw)
+{
+	struct dp_pll_vco_clk *vco = to_dp_vco_hw(hw);
+	struct dp_pll *pll = vco->priv;
+
+	/* Assert DP PHY power down */
+	dp_pll_write(dp_phy, DP_PHY_PD_CTL, 0x2);
+	/*
+	 * Make sure all the register writes to disable PLL are
+	 * completed before doing any other operation
+	 */
+	wmb();
+
+	return 0;
+}
+
+static struct clk_ops mux_clk_ops;
+static struct regmap_config dp_pll_5nm_cfg = {
+	.reg_bits	= 32,
+	.reg_stride	= 4,
+	.val_bits	= 32,
+	.max_register = 0x910,
+};
+
+int dp_mux_set_parent_5nm(void *context, unsigned int reg, unsigned int val)
+{
+	struct dp_pll *pll = context;
+	u32 auxclk_div;
+
+	if (!context) {
+		DP_ERR("invalid input parameters\n");
+		return -EINVAL;
+	}
+
+	auxclk_div = dp_pll_read(dp_phy, DP_PHY_VCO_DIV);
+	auxclk_div &= ~0x03;
+
+	if (val == 0)
+		auxclk_div |= 1;
+	else if (val == 1)
+		auxclk_div |= 2;
+	else if (val == 2)
+		auxclk_div |= 0;
+
+	dp_pll_write(dp_phy, DP_PHY_VCO_DIV, auxclk_div);
+	/* Make sure the PHY registers writes are done */
+	wmb();
+	DP_DEBUG("mux=%d auxclk_div=%x\n", val, auxclk_div);
+
+	return 0;
+}
+
+int dp_mux_get_parent_5nm(void *context, unsigned int reg, unsigned int *val)
+{
+	u32 auxclk_div = 0;
+	struct dp_pll *pll = context;
+
+	if (!context || !val) {
+		DP_ERR("invalid input parameters\n");
+		return -EINVAL;
+	}
+
+	if (is_gdsc_disabled(pll))
+		return 0;
+
+	auxclk_div = dp_pll_read(dp_phy, DP_PHY_VCO_DIV);
+	auxclk_div &= 0x03;
+
+	if (auxclk_div == 1) /* Default divider */
+		*val = 0;
+	else if (auxclk_div == 2)
+		*val = 1;
+	else if (auxclk_div == 0)
+		*val = 2;
+
+	DP_DEBUG("auxclk_div=%d, val=%d\n", auxclk_div, *val);
+
+	return 0;
+}
+
+static struct regmap_bus dp_pixel_mux_regmap_ops = {
+	.reg_write = dp_mux_set_parent_5nm,
+	.reg_read = dp_mux_get_parent_5nm,
+};
+
+static int dp_vco_set_rate_5nm(struct clk_hw *hw, unsigned long rate,
+					unsigned long parent_rate)
+{
+	struct dp_pll_vco_clk *vco;
+	int rc;
+	struct dp_pll *pll;
+
+	if (!hw) {
+		DP_ERR("invalid input parameters\n");
+		return -EINVAL;
+	}
+
+	vco = to_dp_vco_hw(hw);
+	pll = vco->priv;
+
+	DP_DEBUG("DP lane CLK rate=%ld\n", rate);
+
+	rc = dp_config_vco_rate_5nm(vco, rate);
+	if (rc)
+		DP_ERR("Failed to set clk rate\n");
+
+	vco->rate = rate;
+
+	return 0;
+}
+
+static int dp_regulator_enable_5nm(struct dp_parser *parser,
+		enum dp_pm_type pm_type, bool enable)
+{
+	int rc = 0;
+	struct dss_module_power mp;
+
+	if (pm_type < DP_CORE_PM || pm_type >= DP_MAX_PM) {
+		DP_ERR("invalid resource: %d %s\n", pm_type,
+				dp_parser_pm_name(pm_type));
+		return -EINVAL;
+	}
+
+	mp = parser->mp[pm_type];
+	rc = msm_dss_enable_vreg(mp.vreg_config, mp.num_vreg, enable);
+	if (rc) {
+		DP_ERR("failed to '%s' vregs for %s\n",
+				enable ? "enable" : "disable",
+				dp_parser_pm_name(pm_type));
+		return rc;
+	}
+
+	DP_DEBUG("success: '%s' vregs for %s\n", enable ? "enable" : "disable",
+			dp_parser_pm_name(pm_type));
+	return rc;
+}
+
+static int dp_vco_prepare_5nm(struct clk_hw *hw)
+{
+	int rc = 0;
+	struct dp_pll_vco_clk *vco;
+	struct dp_pll *pll;
+
+	if (!hw) {
+		DP_ERR("invalid input parameters\n");
+		return -EINVAL;
+	}
+
+	vco = to_dp_vco_hw(hw);
+	pll = vco->priv;
+
+	DP_DEBUG("rate=%ld\n", vco->rate);
+
+	/*
+	 * Enable DP_PM_PLL regulator if the PLL revision is 5nm-V1 and the
+	 * link rate is 8.1Gbps. This will result in voting to place Mx rail in
+	 * turbo as required for V1 hardware PLL functionality.
+	 */
+	if (pll->revision == DP_PLL_5NM_V1 &&
+			vco->rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
+		dp_regulator_enable_5nm(pll->parser, DP_PLL_PM, true);
+
+	if ((pll->vco_cached_rate != 0)
+		&& (pll->vco_cached_rate == vco->rate)) {
+		rc = dp_vco_set_rate_5nm(hw, pll->vco_cached_rate,
+				pll->vco_cached_rate);
+		if (rc) {
+			DP_ERR("index=%d vco_set_rate failed. rc=%d\n",
+				rc, pll->index);
+			goto error;
+		}
+	}
+
+	rc = dp_pll_enable_5nm(hw);
+	if (rc) {
+		DP_ERR("ndx=%d failed to enable dp pll\n", pll->index);
+		goto error;
+	}
+
+error:
+	return rc;
+}
+
+static void dp_vco_unprepare_5nm(struct clk_hw *hw)
+{
+	struct dp_pll_vco_clk *vco;
+	struct dp_pll *pll;
+
+	if (!hw) {
+		DP_ERR("invalid input parameters\n");
+		return;
+	}
+
+	vco = to_dp_vco_hw(hw);
+	pll = vco->priv;
+
+	if (!pll) {
+		DP_ERR("invalid input parameter\n");
+		return;
+	}
+
+	if (pll->revision == DP_PLL_5NM_V1 &&
+			vco->rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
+		dp_regulator_enable_5nm(pll->parser, DP_PLL_PM, false);
+
+	pll->vco_cached_rate = vco->rate;
+	dp_pll_disable_5nm(hw);
+}
+
+static unsigned long dp_vco_recalc_rate_5nm(struct clk_hw *hw,
+					unsigned long parent_rate)
+{
+	struct dp_pll_vco_clk *vco;
+	u32 hsclk_sel, link_clk_divsel, hsclk_div, link_clk_div = 0;
+	unsigned long vco_rate;
+	struct dp_pll *pll;
+
+	if (!hw) {
+		DP_ERR("invalid input parameters\n");
+		return 0;
+	}
+
+	vco = to_dp_vco_hw(hw);
+	pll = vco->priv;
+
+	if (is_gdsc_disabled(pll))
+		return 0;
+
+	DP_DEBUG("input rates: parent=%lu, vco=%lu\n", parent_rate, vco->rate);
+
+	hsclk_sel = dp_pll_read(dp_pll, QSERDES_COM_HSCLK_SEL);
+	hsclk_sel &= 0x0f;
+
+	if (hsclk_sel == 5)
+		hsclk_div = 5;
+	else if (hsclk_sel == 3)
+		hsclk_div = 3;
+	else if (hsclk_sel == 1)
+		hsclk_div = 2;
+	else if (hsclk_sel == 0)
+		hsclk_div = 1;
+	else {
+		DP_DEBUG("unknown divider. forcing to default\n");
+		hsclk_div = 5;
+	}
+
+	link_clk_divsel = dp_pll_read(dp_phy, DP_PHY_AUX_CFG2);
+	link_clk_divsel >>= 2;
+	link_clk_divsel &= 0x3;
+
+	if (link_clk_divsel == 0)
+		link_clk_div = 5;
+	else if (link_clk_divsel == 1)
+		link_clk_div = 10;
+	else if (link_clk_divsel == 2)
+		link_clk_div = 20;
+	else
+		DP_ERR("unsupported div. Phy_mode: %d\n", link_clk_divsel);
+
+	if (link_clk_div == 20) {
+		vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+	} else {
+		if (hsclk_div == 5)
+			vco_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
+		else if (hsclk_div == 3)
+			vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+		else if (hsclk_div == 2)
+			vco_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
+		else
+			vco_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
+	}
+
+	DP_DEBUG("hsclk: sel=0x%x, div=0x%x; lclk: sel=%u, div=%u, rate=%lu\n",
+		hsclk_sel, hsclk_div, link_clk_divsel, link_clk_div, vco_rate);
+
+	pll->vco_cached_rate = vco->rate = vco_rate;
+
+	return vco_rate;
+}
+
+static long dp_vco_round_rate_5nm(struct clk_hw *hw, unsigned long rate,
+			unsigned long *parent_rate)
+{
+	unsigned long rrate = rate;
+	struct dp_pll_vco_clk *vco;
+
+	if (!hw) {
+		DP_ERR("invalid input parameters\n");
+		return 0;
+	}
+
+	vco = to_dp_vco_hw(hw);
+	if (rate <= vco->min_rate)
+		rrate = vco->min_rate;
+	else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
+		rrate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
+	else if (rate <= DP_VCO_HSCLK_RATE_5400MHZDIV1000)
+		rrate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
+	else
+		rrate = vco->max_rate;
+
+	DP_DEBUG("rrate=%ld\n", rrate);
+
+	if (parent_rate)
+		*parent_rate = rrate;
+	return rrate;
+}
+
+/* Op structures */
+static const struct clk_ops dp_5nm_vco_clk_ops = {
+	.recalc_rate = dp_vco_recalc_rate_5nm,
+	.set_rate = dp_vco_set_rate_5nm,
+	.round_rate = dp_vco_round_rate_5nm,
+	.prepare = dp_vco_prepare_5nm,
+	.unprepare = dp_vco_unprepare_5nm,
+};
+
+static struct dp_pll_vco_clk dp_vco_clk = {
+	.min_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000,
+	.max_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000,
+	.hw.init = &(struct clk_init_data){
+		.name = "dp_vco_clk",
+		.parent_names = (const char *[]){ "bi_tcxo" },
+		.num_parents = 1,
+		.ops = &dp_5nm_vco_clk_ops,
+	},
+};
+
+static struct clk_fixed_factor dp_phy_pll_link_clk = {
+	.div = 10,
+	.mult = 1,
+
+	.hw.init = &(struct clk_init_data){
+		.name = "dp_phy_pll_link_clk",
+		.parent_names =
+			(const char *[]){ "dp_vco_clk" },
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_fixed_factor_ops,
+	},
+};
+
+static struct clk_fixed_factor dp_vco_divsel_two_clk_src = {
+	.div = 2,
+	.mult = 1,
+
+	.hw.init = &(struct clk_init_data){
+		.name = "dp_vco_divsel_two_clk_src",
+		.parent_names =
+			(const char *[]){ "dp_vco_clk" },
+		.num_parents = 1,
+		.ops = &clk_fixed_factor_ops,
+	},
+};
+
+static struct clk_fixed_factor dp_vco_divsel_four_clk_src = {
+	.div = 4,
+	.mult = 1,
+
+	.hw.init = &(struct clk_init_data){
+		.name = "dp_vco_divsel_four_clk_src",
+		.parent_names =
+			(const char *[]){ "dp_vco_clk" },
+		.num_parents = 1,
+		.ops = &clk_fixed_factor_ops,
+	},
+};
+
+static struct clk_fixed_factor dp_vco_divsel_six_clk_src = {
+	.div = 6,
+	.mult = 1,
+
+	.hw.init = &(struct clk_init_data){
+		.name = "dp_vco_divsel_six_clk_src",
+		.parent_names =
+			(const char *[]){ "dp_vco_clk" },
+		.num_parents = 1,
+		.ops = &clk_fixed_factor_ops,
+	},
+};
+
+static struct clk_regmap_mux dp_phy_pll_vco_div_clk = {
+	.reg = 0x64,
+	.shift = 0,
+	.width = 2,
+
+	.clkr = {
+		.hw.init = &(struct clk_init_data){
+			.name = "dp_phy_pll_vco_div_clk",
+			.parent_names =
+				(const char *[]){"dp_vco_divsel_two_clk_src",
+					"dp_vco_divsel_four_clk_src",
+					"dp_vco_divsel_six_clk_src"},
+			.num_parents = 3,
+			.ops = &mux_clk_ops,
+			.flags = CLK_SET_RATE_PARENT,
+		},
+	},
+};
+
+static int clk_mux_determine_rate(struct clk_hw *hw,
+				  struct clk_rate_request *req)
+{
+	int ret = 0;
+
+	if (!hw || !req) {
+		DP_ERR("Invalid input parameters\n");
+		return -EINVAL;
+	}
+
+	ret = __clk_mux_determine_rate_closest(hw, req);
+	if (ret)
+		return ret;
+
+	/* Set the new parent of mux if there is a new valid parent */
+	if (hw->clk && req->best_parent_hw->clk)
+		clk_set_parent(hw->clk, req->best_parent_hw->clk);
+
+	return 0;
+}
+
+static unsigned long mux_recalc_rate(struct clk_hw *hw,
+					unsigned long parent_rate)
+{
+	struct clk *div_clk = NULL, *vco_clk = NULL;
+	struct dp_pll_vco_clk *vco = NULL;
+
+	if (!hw) {
+		DP_ERR("Invalid input parameter\n");
+		return 0;
+	}
+
+	div_clk = clk_get_parent(hw->clk);
+	if (!div_clk)
+		return 0;
+
+	vco_clk = clk_get_parent(div_clk);
+	if (!vco_clk)
+		return 0;
+
+	vco = to_dp_vco_hw(__clk_get_hw(vco_clk));
+	if (!vco)
+		return 0;
+
+	if (vco->rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
+		return (vco->rate / 6);
+	else if (vco->rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000)
+		return (vco->rate / 4);
+	else
+		return (vco->rate / 2);
+}
+
+static struct clk_hw *mdss_dp_pllcc_5nm[] = {
+	[DP_VCO_CLK] = &dp_vco_clk.hw,
+	[DP_LINK_CLK_DIVSEL_TEN] = &dp_phy_pll_link_clk.hw,
+	[DP_VCO_DIVIDED_TWO_CLK_SRC] = &dp_vco_divsel_two_clk_src.hw,
+	[DP_VCO_DIVIDED_FOUR_CLK_SRC] = &dp_vco_divsel_four_clk_src.hw,
+	[DP_VCO_DIVIDED_SIX_CLK_SRC] = &dp_vco_divsel_six_clk_src.hw,
+	[DP_PHY_PLL_VCO_DIV_CLK] = &dp_phy_pll_vco_div_clk.clkr.hw,
+};
+
+static struct dp_pll_db dp_pdb;
+
+int dp_pll_clock_register_5nm(struct dp_pll *pll)
+{
+	int rc = -ENOTSUPP, i = 0;
+	struct platform_device *pdev;
+	struct clk *clk;
+	struct regmap *regmap;
+	int num_clks = ARRAY_SIZE(mdss_dp_pllcc_5nm);
+
+	if (!pll) {
+		DP_ERR("pll data not initialized\n");
+		return -EINVAL;
+	}
+	pdev = pll->pdev;
+
+	pll->clk_data = kzalloc(sizeof(*pll->clk_data), GFP_KERNEL);
+	if (!pll->clk_data)
+		return -ENOMEM;
+
+	pll->clk_data->clks = kcalloc(num_clks, sizeof(struct clk *),
+			GFP_KERNEL);
+	if (!pll->clk_data->clks) {
+		kfree(pll->clk_data);
+		return -ENOMEM;
+	}
+
+	pll->clk_data->clk_num = num_clks;
+
+	pll->priv = &dp_pdb;
+	dp_pdb.pll = pll;
+
+	/* Set client data for vco, mux and div clocks */
+	regmap = regmap_init(&pdev->dev, &dp_pixel_mux_regmap_ops,
+			pll, &dp_pll_5nm_cfg);
+	mux_clk_ops = clk_regmap_mux_closest_ops;
+	mux_clk_ops.determine_rate = clk_mux_determine_rate;
+	mux_clk_ops.recalc_rate = mux_recalc_rate;
+
+	dp_vco_clk.priv = pll;
+	dp_phy_pll_vco_div_clk.clkr.regmap = regmap;
+
+	for (i = DP_VCO_CLK; i <= DP_PHY_PLL_VCO_DIV_CLK; i++) {
+		DP_DEBUG("reg clk: %d index: %d\n", i, pll->index);
+		clk = clk_register(&pdev->dev, mdss_dp_pllcc_5nm[i]);
+		if (IS_ERR(clk)) {
+			DP_ERR("clk registration failed for DP: %d\n",
+					pll->index);
+			rc = -EINVAL;
+			goto clk_reg_fail;
+		}
+		pll->clk_data->clks[i] = clk;
+	}
+
+	rc = of_clk_add_provider(pdev->dev.of_node,
+			of_clk_src_onecell_get, pll->clk_data);
+	if (rc) {
+		DP_ERR("Clock register failed rc=%d\n", rc);
+		rc = -EPROBE_DEFER;
+		goto clk_reg_fail;
+	} else {
+		DP_DEBUG("success\n");
+	}
+	return rc;
+clk_reg_fail:
+	dp_pll_clock_unregister_5nm(pll);
+	return rc;
+}
+
+void dp_pll_clock_unregister_5nm(struct dp_pll *pll)
+{
+	kfree(pll->clk_data->clks);
+	kfree(pll->clk_data);
+}

+ 74 - 39
msm/dp/dp_power.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/clk.h>
@@ -9,16 +9,17 @@
 #include "dp_power.h"
 #include "dp_catalog.h"
 #include "dp_debug.h"
+#include "dp_pll.h"
 
 #define DP_CLIENT_NAME_SIZE	20
 
 struct dp_power_private {
 	struct dp_parser *parser;
+	struct dp_pll *pll;
 	struct platform_device *pdev;
 	struct clk *pixel_clk_rcg;
 	struct clk *pixel_parent;
 	struct clk *pixel1_clk_rcg;
-	struct clk *pixel1_parent;
 
 	struct dp_power dp_power;
 
@@ -84,6 +85,17 @@ static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable)
 	parser = power->parser;
 
 	for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
+		/*
+		 * The DP_PLL_PM regulator is controlled by dp_display based
+		 * on the link configuration.
+		 */
+		if (i == DP_PLL_PM) {
+			DP_DEBUG("skipping: '%s' vregs for %s\n",
+					enable ? "enable" : "disable",
+					dp_parser_pm_name(i));
+			continue;
+		}
+
 		rc = msm_dss_enable_vreg(
 			parser->mp[i].vreg_config,
 			parser->mp[i].num_vreg, enable);
@@ -151,6 +163,20 @@ static int dp_power_pinctrl_set(struct dp_power_private *power, bool active)
 	return rc;
 }
 
+static void dp_power_clk_put(struct dp_power_private *power)
+{
+	enum dp_pm_type module;
+
+	for (module = DP_CORE_PM; module < DP_MAX_PM; module++) {
+		struct dss_module_power *pm = &power->parser->mp[module];
+
+		if (!pm->num_clk)
+			continue;
+
+		msm_dss_put_clk(pm->clk_config, pm->num_clk);
+	}
+}
+
 static int dp_power_clk_init(struct dp_power_private *power, bool enable)
 {
 	int rc = 0;
@@ -175,52 +201,53 @@ static int dp_power_clk_init(struct dp_power_private *power, bool enable)
 			}
 		}
 
-		power->pixel_clk_rcg = devm_clk_get(dev, "pixel_clk_rcg");
+		power->pixel_clk_rcg = clk_get(dev, "pixel_clk_rcg");
 		if (IS_ERR(power->pixel_clk_rcg)) {
-			DP_DEBUG("Unable to get DP pixel clk RCG\n");
+			DP_ERR("Unable to get DP pixel clk RCG: %d\n",
+					PTR_ERR(power->pixel_clk_rcg));
+			rc = PTR_ERR(power->pixel_clk_rcg);
 			power->pixel_clk_rcg = NULL;
+			goto err_pixel_clk_rcg;
 		}
 
-		power->pixel_parent = devm_clk_get(dev, "pixel_parent");
+		power->pixel_parent = clk_get(dev, "pixel_parent");
 		if (IS_ERR(power->pixel_parent)) {
-			DP_DEBUG("Unable to get DP pixel RCG parent\n");
+			DP_DEBUG("Unable to get DP pixel RCG parent: %d\n",
+					PTR_ERR(power->pixel_parent));
+			rc = PTR_ERR(power->pixel_parent);
 			power->pixel_parent = NULL;
+			goto err_pixel_parent;
 		}
 
-		power->pixel1_clk_rcg = devm_clk_get(dev, "pixel1_clk_rcg");
+		power->pixel1_clk_rcg = clk_get(dev, "pixel1_clk_rcg");
 		if (IS_ERR(power->pixel1_clk_rcg)) {
-			DP_DEBUG("Unable to get DP pixel1 clk RCG\n");
+			DP_DEBUG("Unable to get DP pixel1 clk RCG: %d\n",
+					PTR_ERR(power->pixel1_clk_rcg));
+			rc = PTR_ERR(power->pixel1_clk_rcg);
 			power->pixel1_clk_rcg = NULL;
-		}
-
-		power->pixel1_parent = devm_clk_get(dev, "pixel1_parent");
-		if (IS_ERR(power->pixel1_parent)) {
-			DP_DEBUG("Unable to get DP pixel1 RCG parent\n");
-			power->pixel1_parent = NULL;
+			goto err_pixel1_clk_rcg;
 		}
 	} else {
+		if (power->pixel1_clk_rcg)
+			clk_put(power->pixel1_clk_rcg);
+
 		if (power->pixel_parent)
-			devm_clk_put(dev, power->pixel_parent);
+			clk_put(power->pixel_parent);
 
 		if (power->pixel_clk_rcg)
-			devm_clk_put(dev, power->pixel_clk_rcg);
-
-		if (power->pixel1_parent)
-			devm_clk_put(dev, power->pixel1_parent);
-
-		if (power->pixel1_clk_rcg)
-			devm_clk_put(dev, power->pixel1_clk_rcg);
+			clk_put(power->pixel_clk_rcg);
 
-		for (module = DP_CORE_PM; module < DP_MAX_PM; module++) {
-			struct dss_module_power *pm =
-				&power->parser->mp[module];
+		dp_power_clk_put(power);
+	}
 
-			if (!pm->num_clk)
-				continue;
+	return rc;
 
-			msm_dss_put_clk(pm->clk_config, pm->num_clk);
-		}
-	}
+err_pixel1_clk_rcg:
+	clk_put(power->pixel_parent);
+err_pixel_parent:
+	clk_put(power->pixel_clk_rcg);
+err_pixel_clk_rcg:
+	dp_power_clk_put(power);
 exit:
 	return rc;
 }
@@ -373,7 +400,7 @@ static int dp_power_request_gpios(struct dp_power_private *power)
 		unsigned int gpio = mp->gpio_config[i].gpio;
 
 		if (gpio_is_valid(gpio)) {
-			rc = devm_gpio_request(dev, gpio, gpio_names[i]);
+			rc = gpio_request(gpio, gpio_names[i]);
 			if (rc) {
 				DP_ERR("request %s gpio failed, rc=%d\n",
 					       gpio_names[i], rc);
@@ -521,13 +548,20 @@ static int dp_power_set_pixel_clk_parent(struct dp_power *dp_power, u32 strm_id)
 
 	if (strm_id == DP_STREAM_0) {
 		if (power->pixel_clk_rcg && power->pixel_parent)
-			clk_set_parent(power->pixel_clk_rcg,
+			rc = clk_set_parent(power->pixel_clk_rcg,
 					power->pixel_parent);
+		else
+			DP_WARN("skipped for strm_id=%d\n", strm_id);
 	} else if (strm_id == DP_STREAM_1) {
-		if (power->pixel1_clk_rcg && power->pixel1_parent)
-			clk_set_parent(power->pixel1_clk_rcg,
-					power->pixel1_parent);
+		if (power->pixel1_clk_rcg && power->pixel_parent)
+			rc = clk_set_parent(power->pixel1_clk_rcg,
+					power->pixel_parent);
+		else
+			DP_WARN("skipped for strm_id=%d\n", strm_id);
 	}
+
+	if (rc)
+		DP_ERR("failed. strm_id=%d, rc=%d\n", strm_id, rc);
 exit:
 	return rc;
 }
@@ -653,25 +687,26 @@ exit:
 	return rc;
 }
 
-struct dp_power *dp_power_get(struct dp_parser *parser)
+struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll)
 {
 	int rc = 0;
 	struct dp_power_private *power;
 	struct dp_power *dp_power;
 
-	if (!parser) {
+	if (!parser || !pll) {
 		DP_ERR("invalid input\n");
 		rc = -EINVAL;
 		goto error;
 	}
 
-	power = devm_kzalloc(&parser->pdev->dev, sizeof(*power), GFP_KERNEL);
+	power = kzalloc(sizeof(*power), GFP_KERNEL);
 	if (!power) {
 		rc = -ENOMEM;
 		goto error;
 	}
 
 	power->parser = parser;
+	power->pll = pll;
 	power->pdev = parser->pdev;
 
 	dp_power = &power->dp_power;
@@ -698,5 +733,5 @@ void dp_power_put(struct dp_power *dp_power)
 
 	power = container_of(dp_power, struct dp_power_private, dp_power);
 
-	devm_kfree(&power->pdev->dev, power);
+	kfree(power);
 }

+ 6 - 2
msm/dp/dp_power.h

@@ -1,12 +1,13 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _DP_POWER_H_
 #define _DP_POWER_H_
 
 #include "dp_parser.h"
+#include "dp_pll.h"
 #include "sde_power_handle.h"
 
 /**
@@ -17,6 +18,8 @@
  * @clk_enable: enable/disable the DP clocks
  * @set_pixel_clk_parent: set the parent of DP pixel clock
  * @clk_get_rate: get the current rate for provided clk_name
+ * @power_client_init: configures clocks and regulators
+ * @power_client_deinit: frees clock and regulator resources
  */
 struct dp_power {
 	struct drm_device *drm_dev;
@@ -37,13 +40,14 @@ struct dp_power {
  * dp_power_get() - configure and get the DisplayPort power module data
  *
  * @parser: instance of parser module
+ * @pll: instance of pll module
  * return: pointer to allocated power module data
  *
  * This API will configure the DisplayPort's power module and provides
  * methods to be called by the client to configure the power related
  * modueles.
  */
-struct dp_power *dp_power_get(struct dp_parser *parser);
+struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll);
 
 /**
  * dp_power_put() - release the power related resources

+ 2 - 2
msm/dp/dp_reg.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _DP_REG_H_
@@ -387,7 +387,7 @@
 #define TXn_HIGHZ_DRVR_EN_V420			(0x0058)
 #define TXn_TX_POL_INV_V420			(0x005C)
 
-#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN		(0x004)
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN		(0x044)
 
 /* DP MMSS_CC registers */
 #define MMSS_DP_LINK_CMD_RCGR			(0x0138)