drm/msm: move debugfs registration to late_register callbacks

Need to move debugfs registration into the appropriate callbacks
that get called after the device is registered. Debugfs root
nodes are only called after drm_dev_register. After that, kms
needs to implement the postinit callback to register debugfs,
and drm components like crtc, connector, encoder must implement
the late_register and early_unregister callbacks to properly
add debugfs entries.

Change-Id: I862ca3b8fa0b813b36278ac9bbaebbcdcd2d58df
Signed-off-by: Lloyd Atkinson <latkinso@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 46491fb..0e5342f 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -495,6 +495,11 @@
 	return sde_connector_init_debugfs(connector);
 }
 
+static void sde_connector_early_unregister(struct drm_connector *connector)
+{
+	/* debugfs under connector->debugfs are deleted by drm_debugfs */
+}
+
 static const struct drm_connector_funcs sde_connector_ops = {
 	.dpms =                   drm_atomic_helper_connector_dpms,
 	.reset =                  sde_connector_atomic_reset,
@@ -507,6 +512,7 @@
 	.atomic_get_property =    sde_connector_atomic_get_property,
 	.set_property =           sde_connector_set_property,
 	.late_register =          sde_connector_late_register,
+	.early_unregister =       sde_connector_early_unregister,
 };
 
 static int sde_connector_get_modes(struct drm_connector *connector)
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index 67b577b..71a8bdf 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -386,7 +386,7 @@
 		atomic_set(&sde_kms->irq_obj.irq_counts[i], 0);
 	}
 
-	sde_debugfs_core_irq_init(sde_kms, sde_kms->debugfs_root);
+	sde_debugfs_core_irq_init(sde_kms, sde_debugfs_get_root(sde_kms));
 }
 
 int sde_core_irq_postinstall(struct sde_kms *sde_kms)
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.c b/drivers/gpu/drm/msm/sde/sde_core_perf.c
index 7a68f91..307c617 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.c
@@ -456,13 +456,13 @@
 	.write = _sde_core_perf_mode_write,
 };
 
-static void sde_debugfs_core_perf_destroy(struct sde_core_perf *perf)
+static void sde_core_perf_debugfs_destroy(struct sde_core_perf *perf)
 {
 	debugfs_remove_recursive(perf->debugfs_root);
 	perf->debugfs_root = NULL;
 }
 
-static int sde_debugfs_core_perf_init(struct sde_core_perf *perf,
+int sde_core_perf_debugfs_init(struct sde_core_perf *perf,
 		struct dentry *parent)
 {
 	struct sde_mdss_cfg *catalog = perf->catalog;
@@ -499,11 +499,11 @@
 	return 0;
 }
 #else
-static void sde_debugfs_core_perf_destroy(struct sde_core_perf *perf)
+static void sde_core_perf_debugfs_destroy(struct sde_core_perf *perf)
 {
 }
 
-static int sde_debugfs_core_perf_init(struct sde_core_perf *perf,
+int sde_core_perf_debugfs_init(struct sde_core_perf *perf,
 		struct dentry *parent)
 {
 	return 0;
@@ -517,7 +517,7 @@
 		return;
 	}
 
-	sde_debugfs_core_perf_destroy(perf);
+	sde_core_perf_debugfs_destroy(perf);
 	perf->max_core_clk_rate = 0;
 	perf->core_clk = NULL;
 	mutex_destroy(&perf->perf_lock);
@@ -532,11 +532,9 @@
 		struct sde_mdss_cfg *catalog,
 		struct sde_power_handle *phandle,
 		struct sde_power_client *pclient,
-		char *clk_name,
-		struct dentry *debugfs_parent)
+		char *clk_name)
 {
-	if (!perf || !catalog || !phandle || !pclient ||
-			!clk_name || !debugfs_parent) {
+	if (!perf || !dev || !catalog || !phandle || !pclient || !clk_name) {
 		SDE_ERROR("invalid parameters\n");
 		return -EINVAL;
 	}
@@ -560,8 +558,6 @@
 		goto err;
 	}
 
-	sde_debugfs_core_perf_init(perf, debugfs_parent);
-
 	return 0;
 
 err:
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.h b/drivers/gpu/drm/msm/sde/sde_core_perf.h
index e5dd9b6..20e4eb5 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.h
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, 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
@@ -10,8 +10,8 @@
  * GNU General Public License for more details.
  */
 
-#ifndef __SDE_CORE_PERF_H__
-#define __SDE_CORE_PERF_H__
+#ifndef _SDE_CORE_PERF_H_
+#define _SDE_CORE_PERF_H_
 
 #include <linux/types.h>
 #include <linux/dcache.h>
@@ -111,14 +111,20 @@
  * @phandle: Pointer to power handle
  * @pclient: Pointer to power client
  * @clk_name: core clock name
- * @debugfs_parent: Pointer to parent debugfs
  */
 int sde_core_perf_init(struct sde_core_perf *perf,
 		struct drm_device *dev,
 		struct sde_mdss_cfg *catalog,
 		struct sde_power_handle *phandle,
 		struct sde_power_client *pclient,
-		char *clk_name,
-		struct dentry *debugfs_parent);
+		char *clk_name);
 
-#endif /* __SDE_CORE_PERF_H__ */
+/**
+ * sde_core_perf_debugfs_init - initialize debugfs for core performance context
+ * @perf: Pointer to core performance context
+ * @debugfs_parent: Pointer to parent debugfs
+ */
+int sde_core_perf_debugfs_init(struct sde_core_perf *perf,
+		struct dentry *parent);
+
+#endif /* _SDE_CORE_PERF_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 76c1212..2666990 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -72,7 +72,6 @@
 	msm_property_destroy(&sde_crtc->property_info);
 	sde_cp_crtc_destroy_properties(crtc);
 
-	debugfs_remove_recursive(sde_crtc->debugfs_root);
 	mutex_destroy(&sde_crtc->crtc_lock);
 	sde_fence_deinit(&sde_crtc->output_fence);
 
@@ -1843,30 +1842,7 @@
 {
 	return single_open(file, _sde_debugfs_status_show, inode->i_private);
 }
-#endif
 
-static const struct drm_crtc_funcs sde_crtc_funcs = {
-	.set_config = drm_atomic_helper_set_config,
-	.destroy = sde_crtc_destroy,
-	.page_flip = drm_atomic_helper_page_flip,
-	.set_property = sde_crtc_set_property,
-	.atomic_set_property = sde_crtc_atomic_set_property,
-	.atomic_get_property = sde_crtc_atomic_get_property,
-	.reset = sde_crtc_reset,
-	.atomic_duplicate_state = sde_crtc_duplicate_state,
-	.atomic_destroy_state = sde_crtc_destroy_state,
-};
-
-static const struct drm_crtc_helper_funcs sde_crtc_helper_funcs = {
-	.mode_fixup = sde_crtc_mode_fixup,
-	.disable = sde_crtc_disable,
-	.enable = sde_crtc_enable,
-	.atomic_check = sde_crtc_atomic_check,
-	.atomic_begin = sde_crtc_atomic_begin,
-	.atomic_flush = sde_crtc_atomic_flush,
-};
-
-#ifdef CONFIG_DEBUG_FS
 #define DEFINE_SDE_DEBUGFS_SEQ_FOPS(__prefix)				\
 static int __prefix ## _open(struct inode *inode, struct file *file)	\
 {									\
@@ -1897,9 +1873,11 @@
 }
 DEFINE_SDE_DEBUGFS_SEQ_FOPS(sde_crtc_debugfs_state);
 
-static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
-		struct sde_kms *sde_kms)
+static int _sde_crtc_init_debugfs(struct drm_crtc *crtc)
 {
+	struct sde_crtc *sde_crtc;
+	struct sde_kms *sde_kms;
+
 	static const struct file_operations debugfs_status_fops = {
 		.open =		_sde_debugfs_status_open,
 		.read =		seq_read,
@@ -1907,27 +1885,84 @@
 		.release =	single_release,
 	};
 
-	if (sde_crtc && sde_kms) {
-		sde_crtc->debugfs_root = debugfs_create_dir(sde_crtc->name,
-				sde_debugfs_get_root(sde_kms));
-		if (sde_crtc->debugfs_root) {
-			/* don't error check these */
-			debugfs_create_file("status", 0444,
-					sde_crtc->debugfs_root,
-					sde_crtc, &debugfs_status_fops);
-			debugfs_create_file("state", 0644,
-					sde_crtc->debugfs_root,
-					&sde_crtc->base,
-					&sde_crtc_debugfs_state_fops);
-		}
-	}
+	if (!crtc)
+		return -EINVAL;
+	sde_crtc = to_sde_crtc(crtc);
+
+	sde_kms = _sde_crtc_get_kms(crtc);
+	if (!sde_kms)
+		return -EINVAL;
+
+	sde_crtc->debugfs_root = debugfs_create_dir(sde_crtc->name,
+			sde_debugfs_get_root(sde_kms));
+	if (!sde_crtc->debugfs_root)
+		return -ENOMEM;
+
+	/* don't error check these */
+	debugfs_create_file("status", 0444,
+			sde_crtc->debugfs_root,
+			sde_crtc, &debugfs_status_fops);
+	debugfs_create_file("state", 0644,
+			sde_crtc->debugfs_root,
+			&sde_crtc->base,
+			&sde_crtc_debugfs_state_fops);
+
+	return 0;
+}
+
+static void _sde_crtc_destroy_debugfs(struct drm_crtc *crtc)
+{
+	struct sde_crtc *sde_crtc;
+
+	if (!crtc)
+		return;
+	sde_crtc = to_sde_crtc(crtc);
+	debugfs_remove_recursive(sde_crtc->debugfs_root);
 }
 #else
-static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
-		struct sde_kms *sde_kms)
+static int _sde_crtc_init_debugfs(struct drm_crtc *crtc)
 {
+	return 0;
 }
-#endif
+
+static void _sde_crtc_destroy_debugfs(struct drm_crtc *crtc)
+{
+	return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int sde_crtc_late_register(struct drm_crtc *crtc)
+{
+	return _sde_crtc_init_debugfs(crtc);
+}
+
+static void sde_crtc_early_unregister(struct drm_crtc *crtc)
+{
+	_sde_crtc_destroy_debugfs(crtc);
+}
+
+static const struct drm_crtc_funcs sde_crtc_funcs = {
+	.set_config = drm_atomic_helper_set_config,
+	.destroy = sde_crtc_destroy,
+	.page_flip = drm_atomic_helper_page_flip,
+	.set_property = sde_crtc_set_property,
+	.atomic_set_property = sde_crtc_atomic_set_property,
+	.atomic_get_property = sde_crtc_atomic_get_property,
+	.reset = sde_crtc_reset,
+	.atomic_duplicate_state = sde_crtc_duplicate_state,
+	.atomic_destroy_state = sde_crtc_destroy_state,
+	.late_register = sde_crtc_late_register,
+	.early_unregister = sde_crtc_early_unregister,
+};
+
+static const struct drm_crtc_helper_funcs sde_crtc_helper_funcs = {
+	.mode_fixup = sde_crtc_mode_fixup,
+	.disable = sde_crtc_disable,
+	.enable = sde_crtc_enable,
+	.atomic_check = sde_crtc_atomic_check,
+	.atomic_begin = sde_crtc_atomic_begin,
+	.atomic_flush = sde_crtc_atomic_flush,
+};
 
 /* initialize crtc */
 struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
@@ -1974,9 +2009,6 @@
 	mutex_init(&sde_crtc->crtc_lock);
 	sde_fence_init(&sde_crtc->output_fence, sde_crtc->name, crtc->base.id);
 
-	/* initialize debugfs support */
-	_sde_crtc_init_debugfs(sde_crtc, kms);
-
 	/* create CRTC properties */
 	msm_property_init(&sde_crtc->property_info, &crtc->base, dev,
 			priv->crtc_property, sde_crtc->property_data,
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index cfdcc05..a845f4d 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -183,7 +183,6 @@
 	mutex_unlock(&sde_enc->enc_lock);
 
 	drm_encoder_cleanup(drm_enc);
-	debugfs_remove_recursive(sde_enc->debugfs_root);
 	mutex_destroy(&sde_enc->enc_lock);
 
 	kfree(sde_enc);
@@ -473,17 +472,6 @@
 	sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
 }
 
-static const struct drm_encoder_helper_funcs sde_encoder_helper_funcs = {
-	.mode_set = sde_encoder_virt_mode_set,
-	.disable = sde_encoder_virt_disable,
-	.enable = sde_encoder_virt_enable,
-	.atomic_check = sde_encoder_virt_atomic_check,
-};
-
-static const struct drm_encoder_funcs sde_encoder_funcs = {
-		.destroy = sde_encoder_destroy,
-};
-
 static enum sde_intf sde_encoder_get_intf(struct sde_mdss_cfg *catalog,
 		enum sde_intf_type type, u32 controller_id)
 {
@@ -979,6 +967,7 @@
 	return 0;
 }
 
+#ifdef CONFIG_DEBUG_FS
 static int _sde_encoder_debugfs_status_open(struct inode *inode,
 		struct file *file)
 {
@@ -1087,9 +1076,12 @@
 	return len;
 }
 
-static void _sde_encoder_init_debugfs(struct drm_encoder *drm_enc,
-	struct sde_encoder_virt *sde_enc, struct sde_kms *sde_kms)
+static int _sde_encoder_init_debugfs(struct drm_encoder *drm_enc)
 {
+	struct sde_encoder_virt *sde_enc;
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
+
 	static const struct file_operations debugfs_status_fops = {
 		.open =		_sde_encoder_debugfs_status_open,
 		.read =		seq_read,
@@ -1105,25 +1097,62 @@
 
 	char name[SDE_NAME_SIZE];
 
-	if (!drm_enc || !sde_enc || !sde_kms) {
+	if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private) {
 		SDE_ERROR("invalid encoder or kms\n");
-		return;
+		return -EINVAL;
 	}
 
+	sde_enc = to_sde_encoder_virt(drm_enc);
+	priv = drm_enc->dev->dev_private;
+	sde_kms = to_sde_kms(priv->kms);
+
 	snprintf(name, SDE_NAME_SIZE, "encoder%u", drm_enc->base.id);
 
 	/* create overall sub-directory for the encoder */
 	sde_enc->debugfs_root = debugfs_create_dir(name,
 					sde_debugfs_get_root(sde_kms));
-	if (sde_enc->debugfs_root) {
-		/* don't error check these */
-		debugfs_create_file("status", 0644,
-			sde_enc->debugfs_root, sde_enc, &debugfs_status_fops);
+	if (!sde_enc->debugfs_root)
+		return -ENOMEM;
 
-		debugfs_create_file("misr_data", 0644,
-			sde_enc->debugfs_root, drm_enc, &debugfs_misr_fops);
+	/* don't error check these */
+	debugfs_create_file("status", 0644,
+		sde_enc->debugfs_root, sde_enc, &debugfs_status_fops);
 
-	}
+	debugfs_create_file("misr_data", 0644,
+		sde_enc->debugfs_root, drm_enc, &debugfs_misr_fops);
+
+	return 0;
+}
+
+static void _sde_encoder_destroy_debugfs(struct drm_encoder *drm_enc)
+{
+	struct sde_encoder_virt *sde_enc;
+
+	if (!drm_enc)
+		return;
+
+	sde_enc = to_sde_encoder_virt(drm_enc);
+	debugfs_remove_recursive(sde_enc->debugfs_root);
+}
+#else
+static int _sde_encoder_init_debugfs(struct drm_encoder *drm_enc)
+{
+	return 0;
+}
+
+static _sde_encoder_destroy_debugfs(struct drm_encoder *drm_enc)
+{
+}
+#endif
+
+static int sde_encoder_late_register(struct drm_encoder *encoder)
+{
+	return _sde_encoder_init_debugfs(encoder);
+}
+
+static void sde_encoder_early_unregister(struct drm_encoder *encoder)
+{
+	_sde_encoder_destroy_debugfs(encoder);
 }
 
 static int sde_encoder_virt_add_phys_encs(
@@ -1360,6 +1389,19 @@
 			SDE_ENCODER_FRAME_EVENT_ERROR);
 }
 
+static const struct drm_encoder_helper_funcs sde_encoder_helper_funcs = {
+	.mode_set = sde_encoder_virt_mode_set,
+	.disable = sde_encoder_virt_disable,
+	.enable = sde_encoder_virt_enable,
+	.atomic_check = sde_encoder_virt_atomic_check,
+};
+
+static const struct drm_encoder_funcs sde_encoder_funcs = {
+		.destroy = sde_encoder_destroy,
+		.late_register = sde_encoder_late_register,
+		.early_unregister = sde_encoder_early_unregister,
+};
+
 struct drm_encoder *sde_encoder_init(
 		struct drm_device *dev,
 		struct msm_display_info *disp_info)
@@ -1394,8 +1436,6 @@
 	setup_timer(&sde_enc->frame_done_timer, sde_encoder_frame_done_timeout,
 			(unsigned long) sde_enc);
 
-	_sde_encoder_init_debugfs(drm_enc, sde_enc, sde_kms);
-
 	snprintf(name, SDE_NAME_SIZE, "rsc_enc%u", drm_enc->base.id);
 	sde_enc->rsc_client = sde_rsc_client_create(SDE_RSC_INDEX, name,
 					disp_info->is_primary);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index d8f096c..22cec11 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -89,6 +89,7 @@
 /**
  * struct sde_encoder_phys_ops - Interface the physical encoders provide to
  *	the containing virtual encoder.
+ * @late_register:		DRM Call. Add Userspace interfaces, debugfs.
  * @is_master:			Whether this phys_enc is the current master
  *				encoder. Can be switched at enable time. Based
  *				on split_role and current mode (CMD/VID).
@@ -117,6 +118,8 @@
  */
 
 struct sde_encoder_phys_ops {
+	int (*late_register)(struct sde_encoder_phys *encoder,
+			struct dentry *debugfs_root);
 	bool (*is_master)(struct sde_encoder_phys *encoder);
 	bool (*mode_fixup)(struct sde_encoder_phys *encoder,
 			const struct drm_display_mode *mode,
@@ -281,8 +284,6 @@
  * @wb_dev:		Pointer to writeback device
  * @start_time:		Start time of writeback latest request
  * @end_time:		End time of writeback latest request
- * @wb_name:		Name of this writeback device
- * @debugfs_root:	Root entry of writeback debugfs
  */
 struct sde_encoder_phys_wb {
 	struct sde_encoder_phys base;
@@ -302,10 +303,6 @@
 	struct sde_wb_device *wb_dev;
 	ktime_t start_time;
 	ktime_t end_time;
-#ifdef CONFIG_DEBUG_FS
-	char wb_name[SDE_ENCODER_NAME_MAX];
-	struct dentry *debugfs_root;
-#endif
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 13fe9bd..75eddc0 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -918,69 +918,45 @@
 #ifdef CONFIG_DEBUG_FS
 /**
  * sde_encoder_phys_wb_init_debugfs - initialize writeback encoder debugfs
- * @phys_enc:	Pointer to physical encoder
- * @sde_kms:	Pointer to SDE KMS object
+ * @phys_enc:		Pointer to physical encoder
+ * @debugfs_root:	Pointer to virtual encoder's debugfs_root dir
  */
 static int sde_encoder_phys_wb_init_debugfs(
-		struct sde_encoder_phys *phys_enc, struct sde_kms *kms)
+		struct sde_encoder_phys *phys_enc, struct dentry *debugfs_root)
 {
 	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
 
-	if (!phys_enc || !kms || !wb_enc->hw_wb)
+	if (!phys_enc || !wb_enc->hw_wb || !debugfs_root)
 		return -EINVAL;
 
-	snprintf(wb_enc->wb_name, ARRAY_SIZE(wb_enc->wb_name), "encoder_wb%d",
-			wb_enc->hw_wb->idx - WB_0);
-
-	wb_enc->debugfs_root =
-		debugfs_create_dir(wb_enc->wb_name,
-				sde_debugfs_get_root(kms));
-	if (!wb_enc->debugfs_root) {
-		SDE_ERROR("failed to create debugfs\n");
-		return -ENOMEM;
-	}
-
 	if (!debugfs_create_u32("wbdone_timeout", 0644,
-			wb_enc->debugfs_root, &wb_enc->wbdone_timeout)) {
+			debugfs_root, &wb_enc->wbdone_timeout)) {
 		SDE_ERROR("failed to create debugfs/wbdone_timeout\n");
 		return -ENOMEM;
 	}
 
 	if (!debugfs_create_u32("bypass_irqreg", 0644,
-			wb_enc->debugfs_root, &wb_enc->bypass_irqreg)) {
+			debugfs_root, &wb_enc->bypass_irqreg)) {
 		SDE_ERROR("failed to create debugfs/bypass_irqreg\n");
 		return -ENOMEM;
 	}
 
 	return 0;
 }
-
-/**
- * sde_encoder_phys_wb_destroy_debugfs - destroy writeback encoder debugfs
- * @phys_enc:	Pointer to physical encoder
- */
-static void sde_encoder_phys_wb_destroy_debugfs(
-		struct sde_encoder_phys *phys_enc)
-{
-	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
-
-	if (!phys_enc)
-		return;
-
-	debugfs_remove_recursive(wb_enc->debugfs_root);
-}
 #else
 static int sde_encoder_phys_wb_init_debugfs(
-		struct sde_encoder_phys *phys_enc, struct sde_kms *kms)
+		struct sde_encoder_phys *phys_enc, struct dentry *debugfs_root)
 {
 	return 0;
 }
-static void sde_encoder_phys_wb_destroy_debugfs(
-		struct sde_encoder_phys *phys_enc)
-{
-}
 #endif
 
+static int sde_encoder_phys_wb_late_register(struct sde_encoder_phys *phys_enc,
+		struct dentry *debugfs_root)
+{
+	return sde_encoder_phys_wb_init_debugfs(phys_enc, debugfs_root);
+}
+
 /**
  * sde_encoder_phys_wb_destroy - destroy writeback encoder
  * @phys_enc:	Pointer to physical encoder
@@ -995,8 +971,6 @@
 	if (!phys_enc)
 		return;
 
-	sde_encoder_phys_wb_destroy_debugfs(phys_enc);
-
 	kfree(wb_enc);
 }
 
@@ -1006,6 +980,7 @@
  */
 static void sde_encoder_phys_wb_init_ops(struct sde_encoder_phys_ops *ops)
 {
+	ops->late_register = sde_encoder_phys_wb_late_register;
 	ops->is_master = sde_encoder_phys_wb_is_master;
 	ops->mode_set = sde_encoder_phys_wb_mode_set;
 	ops->enable = sde_encoder_phys_wb_enable;
@@ -1103,18 +1078,11 @@
 	phys_enc->enc_spinlock = p->enc_spinlock;
 	INIT_LIST_HEAD(&wb_enc->irq_cb.list);
 
-	ret = sde_encoder_phys_wb_init_debugfs(phys_enc, p->sde_kms);
-	if (ret) {
-		SDE_ERROR("failed to init debugfs %d\n", ret);
-		goto fail_debugfs_init;
-	}
-
 	SDE_DEBUG("Created sde_encoder_phys_wb for wb %d\n",
 			wb_enc->hw_wb->idx - WB_0);
 
 	return phys_enc;
 
-fail_debugfs_init:
 fail_wb_init:
 fail_wb_check:
 fail_mdp_init:
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 39b8863..1a177d1 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -273,35 +273,35 @@
 
 void *sde_debugfs_get_root(struct sde_kms *sde_kms)
 {
-	return sde_kms ? sde_kms->debugfs_root : 0;
+	return sde_kms ? sde_kms->dev->primary->debugfs_root : 0;
 }
 
 static int _sde_debugfs_init(struct sde_kms *sde_kms)
 {
 	void *p;
+	int rc;
+	void *debugfs_root;
 
 	p = sde_hw_util_get_log_mask_ptr();
 
 	if (!sde_kms || !p)
 		return -EINVAL;
 
-	if (sde_kms->dev && sde_kms->dev->primary)
-		sde_kms->debugfs_root = sde_kms->dev->primary->debugfs_root;
-	else
-		sde_kms->debugfs_root = debugfs_create_dir(SDE_DEBUGFS_DIR, 0);
+	debugfs_root = sde_debugfs_get_root(sde_kms);
+	if (!debugfs_root)
+		return -EINVAL;
 
 	/* allow debugfs_root to be NULL */
-	debugfs_create_x32(SDE_DEBUGFS_HWMASKNAME,
-			0644, sde_kms->debugfs_root, p);
+	debugfs_create_x32(SDE_DEBUGFS_HWMASKNAME, 0644, debugfs_root, p);
 
-	/* create common folder for debug information */
-	sde_kms->debugfs_debug = debugfs_create_dir("debug",
-			sde_kms->debugfs_root);
-	if (!sde_kms->debugfs_debug)
-		SDE_ERROR("failed to create debugfs debug directory\n");
+	sde_debugfs_danger_init(sde_kms, debugfs_root);
+	sde_debugfs_vbif_init(sde_kms, debugfs_root);
 
-	sde_debugfs_danger_init(sde_kms, sde_kms->debugfs_debug);
-	sde_debugfs_vbif_init(sde_kms, sde_kms->debugfs_debug);
+	rc = sde_core_perf_debugfs_init(&sde_kms->perf, debugfs_root);
+	if (rc) {
+		SDE_ERROR("failed to init perf %d\n", rc);
+		return rc;
+	}
 
 	return 0;
 }
@@ -312,13 +312,19 @@
 	if (sde_kms) {
 		sde_debugfs_vbif_destroy(sde_kms);
 		sde_debugfs_danger_destroy(sde_kms);
-		debugfs_remove_recursive(sde_kms->debugfs_debug);
-		sde_kms->debugfs_debug = 0;
-		debugfs_remove_recursive(sde_kms->debugfs_root);
-		sde_kms->debugfs_root = 0;
 	}
 }
 #else
+static int _sde_debugfs_init(struct sde_kms *sde_kms)
+{
+	return 0;
+}
+
+static void _sde_debugfs_destroy(struct sde_kms *sde_kms)
+{
+	return 0;
+}
+
 static void sde_debugfs_danger_destroy(struct sde_kms *sde_kms,
 		struct dentry *parent)
 {
@@ -818,6 +824,7 @@
 {
 	struct sde_kms *sde_kms = to_sde_kms(kms);
 	struct drm_device *dev;
+	int rc;
 
 	if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev) {
 		SDE_ERROR("invalid sde_kms\n");
@@ -826,7 +833,11 @@
 
 	dev = sde_kms->dev;
 
-	return 0;
+	rc = _sde_debugfs_init(sde_kms);
+	if (rc)
+		SDE_ERROR("sde_debugfs init failed: %d\n", rc);
+
+	return rc;
 }
 
 static long sde_kms_round_pixclk(struct msm_kms *kms, unsigned long rate,
@@ -1218,19 +1229,8 @@
 		goto power_error;
 	}
 
-	/*
-	 * NOTE: Calling sde_debugfs_init here so that the drm_minor device for
-	 *       'primary' is already created.
-	 */
-	rc = _sde_debugfs_init(sde_kms);
-	if (rc) {
-		SDE_ERROR("sde_debugfs init failed: %d\n", rc);
-		goto power_error;
-	}
-
 	rc = sde_core_perf_init(&sde_kms->perf, dev, sde_kms->catalog,
-			&priv->phandle, priv->pclient, "core_clk_src",
-			sde_kms->debugfs_debug);
+			&priv->phandle, priv->pclient, "core_clk_src");
 	if (rc) {
 		SDE_ERROR("failed to init perf %d\n", rc);
 		goto perf_err;
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index bf1c12f..1acdf00 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -126,8 +126,6 @@
 	struct sde_power_client *core_client;
 
 	/* directory entry for debugfs */
-	void *debugfs_root;
-	struct dentry *debugfs_debug;
 	struct dentry *debugfs_danger;
 	struct dentry *debugfs_vbif;
 
@@ -235,7 +233,7 @@
 		void *parent, struct sde_debugfs_regset32 *regset);
 
 /**
- * sde_debugfs_get_root - Return root directory entry for SDE's debugfs
+ * sde_debugfs_get_root - Return root directory entry for KMS's debugfs
  *
  * The return value should be passed as the 'parent' argument to subsequent
  * debugfs create calls.
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 9dc13a2..908926c 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -2269,8 +2269,6 @@
 	if (psde) {
 		_sde_plane_set_qos_ctrl(plane, false, SDE_PLANE_QOS_PANIC_CTRL);
 
-		debugfs_remove_recursive(psde->debugfs_root);
-
 		if (psde->blob_info)
 			drm_property_unreference_blob(psde->blob_info);
 		msm_property_destroy(&psde->property_info);
@@ -2399,35 +2397,7 @@
 	plane->state = &pstate->base;
 }
 
-static const struct drm_plane_funcs sde_plane_funcs = {
-		.update_plane = drm_atomic_helper_update_plane,
-		.disable_plane = drm_atomic_helper_disable_plane,
-		.destroy = sde_plane_destroy,
-		.set_property = sde_plane_set_property,
-		.atomic_set_property = sde_plane_atomic_set_property,
-		.atomic_get_property = sde_plane_atomic_get_property,
-		.reset = sde_plane_reset,
-		.atomic_duplicate_state = sde_plane_duplicate_state,
-		.atomic_destroy_state = sde_plane_destroy_state,
-};
-
-static const struct drm_plane_helper_funcs sde_plane_helper_funcs = {
-		.prepare_fb = sde_plane_prepare_fb,
-		.cleanup_fb = sde_plane_cleanup_fb,
-		.atomic_check = sde_plane_atomic_check,
-		.atomic_update = sde_plane_atomic_update,
-};
-
-enum sde_sspp sde_plane_pipe(struct drm_plane *plane)
-{
-	return plane ? to_sde_plane(plane)->pipe : SSPP_NONE;
-}
-
-bool is_sde_plane_virtual(struct drm_plane *plane)
-{
-	return plane ? to_sde_plane(plane)->is_virtual : false;
-}
-
+#ifdef CONFIG_DEBUG_FS
 static ssize_t _sde_plane_danger_read(struct file *file,
 			char __user *buff, size_t count, loff_t *ppos)
 {
@@ -2520,72 +2490,161 @@
 	.write = _sde_plane_danger_write,
 };
 
-static void _sde_plane_init_debugfs(struct sde_plane *psde, struct sde_kms *kms)
+static int _sde_plane_init_debugfs(struct drm_plane *plane)
 {
+	struct sde_plane *psde;
+	struct sde_kms *kms;
+	struct msm_drm_private *priv;
 	const struct sde_sspp_sub_blks *sblk = 0;
 	const struct sde_sspp_cfg *cfg = 0;
 
+	if (!plane || !plane->dev) {
+		SDE_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	priv = plane->dev->dev_private;
+	if (!priv || !priv->kms) {
+		SDE_ERROR("invalid KMS reference\n");
+		return -EINVAL;
+	}
+
+	kms = to_sde_kms(priv->kms);
+	psde = to_sde_plane(plane);
+
 	if (psde && psde->pipe_hw)
 		cfg = psde->pipe_hw->cap;
 	if (cfg)
 		sblk = cfg->sblk;
 
-	if (kms && sblk) {
-		/* create overall sub-directory for the pipe */
-		psde->debugfs_root =
-			debugfs_create_dir(psde->pipe_name,
-					sde_debugfs_get_root(kms));
-		if (psde->debugfs_root) {
-			/* don't error check these */
-			debugfs_create_x32("features", 0644,
-					psde->debugfs_root, &psde->features);
+	if (!sblk)
+		return 0;
 
-			/* add register dump support */
-			sde_debugfs_setup_regset32(&psde->debugfs_src,
-					sblk->src_blk.base + cfg->base,
-					sblk->src_blk.len,
-					kms);
-			sde_debugfs_create_regset32("src_blk", 0444,
-					psde->debugfs_root, &psde->debugfs_src);
+	/* create overall sub-directory for the pipe */
+	psde->debugfs_root =
+		debugfs_create_dir(psde->pipe_name,
+				sde_debugfs_get_root(kms));
 
-			sde_debugfs_setup_regset32(&psde->debugfs_scaler,
-					sblk->scaler_blk.base + cfg->base,
-					sblk->scaler_blk.len,
-					kms);
-			sde_debugfs_create_regset32("scaler_blk", 0444,
-					psde->debugfs_root,
-					&psde->debugfs_scaler);
+	if (!psde->debugfs_root)
+		return -ENOMEM;
 
-			sde_debugfs_setup_regset32(&psde->debugfs_csc,
-					sblk->csc_blk.base + cfg->base,
-					sblk->csc_blk.len,
-					kms);
-			sde_debugfs_create_regset32("csc_blk", 0444,
-					psde->debugfs_root, &psde->debugfs_csc);
+	/* don't error check these */
+	debugfs_create_x32("features", 0644,
+			psde->debugfs_root, &psde->features);
 
-			debugfs_create_u32("xin_id",
-					0444,
-					psde->debugfs_root,
-					(u32 *) &cfg->xin_id);
-			debugfs_create_u32("clk_ctrl",
-					0444,
-					psde->debugfs_root,
-					(u32 *) &cfg->clk_ctrl);
-			debugfs_create_x32("creq_vblank",
-					0644,
-					psde->debugfs_root,
-					(u32 *) &sblk->creq_vblank);
-			debugfs_create_x32("danger_vblank",
-					0644,
-					psde->debugfs_root,
-					(u32 *) &sblk->danger_vblank);
+	/* add register dump support */
+	sde_debugfs_setup_regset32(&psde->debugfs_src,
+			sblk->src_blk.base + cfg->base,
+			sblk->src_blk.len,
+			kms);
+	sde_debugfs_create_regset32("src_blk", 0444,
+			psde->debugfs_root, &psde->debugfs_src);
 
-			debugfs_create_file("disable_danger",
-					0644,
-					psde->debugfs_root,
-					kms, &sde_plane_danger_enable);
-		}
+	if (cfg->features & BIT(SDE_SSPP_SCALER_QSEED3) ||
+			cfg->features & BIT(SDE_SSPP_SCALER_QSEED2)) {
+		sde_debugfs_setup_regset32(&psde->debugfs_scaler,
+				sblk->scaler_blk.base + cfg->base,
+				sblk->scaler_blk.len,
+				kms);
+		sde_debugfs_create_regset32("scaler_blk", 0444,
+				psde->debugfs_root,
+				&psde->debugfs_scaler);
 	}
+
+	if (cfg->features & BIT(SDE_SSPP_CSC) ||
+			cfg->features & BIT(SDE_SSPP_CSC_10BIT)) {
+		sde_debugfs_setup_regset32(&psde->debugfs_csc,
+				sblk->csc_blk.base + cfg->base,
+				sblk->csc_blk.len,
+				kms);
+		sde_debugfs_create_regset32("csc_blk", 0444,
+				psde->debugfs_root, &psde->debugfs_csc);
+	}
+
+	debugfs_create_u32("xin_id",
+			0444,
+			psde->debugfs_root,
+			(u32 *) &cfg->xin_id);
+	debugfs_create_u32("clk_ctrl",
+			0444,
+			psde->debugfs_root,
+			(u32 *) &cfg->clk_ctrl);
+	debugfs_create_x32("creq_vblank",
+			0644,
+			psde->debugfs_root,
+			(u32 *) &sblk->creq_vblank);
+	debugfs_create_x32("danger_vblank",
+			0644,
+			psde->debugfs_root,
+			(u32 *) &sblk->danger_vblank);
+
+	debugfs_create_file("disable_danger",
+			0644,
+			psde->debugfs_root,
+			kms, &sde_plane_danger_enable);
+
+	return 0;
+}
+
+static void _sde_plane_destroy_debugfs(struct drm_plane *plane)
+{
+	struct sde_plane *psde;
+
+	if (!plane)
+		return;
+	psde = to_sde_plane(plane);
+
+	debugfs_remove_recursive(psde->debugfs_root);
+}
+#else
+static int _sde_plane_init_debugfs(struct drm_plane *plane)
+{
+	return 0;
+}
+static void _sde_plane_destroy_debugfs(struct drm_plane *plane)
+{
+}
+#endif
+
+static int sde_plane_late_register(struct drm_plane *plane)
+{
+	return _sde_plane_init_debugfs(plane);
+}
+
+static void sde_plane_early_unregister(struct drm_plane *plane)
+{
+	_sde_plane_destroy_debugfs(plane);
+}
+
+static const struct drm_plane_funcs sde_plane_funcs = {
+		.update_plane = drm_atomic_helper_update_plane,
+		.disable_plane = drm_atomic_helper_disable_plane,
+		.destroy = sde_plane_destroy,
+		.set_property = sde_plane_set_property,
+		.atomic_set_property = sde_plane_atomic_set_property,
+		.atomic_get_property = sde_plane_atomic_get_property,
+		.reset = sde_plane_reset,
+		.atomic_duplicate_state = sde_plane_duplicate_state,
+		.atomic_destroy_state = sde_plane_destroy_state,
+		.late_register = sde_plane_late_register,
+		.early_unregister = sde_plane_early_unregister,
+};
+
+static const struct drm_plane_helper_funcs sde_plane_helper_funcs = {
+		.prepare_fb = sde_plane_prepare_fb,
+		.cleanup_fb = sde_plane_cleanup_fb,
+		.atomic_check = sde_plane_atomic_check,
+		.atomic_update = sde_plane_atomic_update,
+};
+
+enum sde_sspp sde_plane_pipe(struct drm_plane *plane)
+{
+	return plane ? to_sde_plane(plane)->pipe : SSPP_NONE;
+}
+
+bool is_sde_plane_virtual(struct drm_plane *plane)
+{
+	return plane ? to_sde_plane(plane)->is_virtual : false;
 }
 
 /* initialize plane */
@@ -2709,8 +2768,6 @@
 
 	mutex_init(&psde->lock);
 
-	_sde_plane_init_debugfs(psde, kms);
-
 	SDE_DEBUG("%s created for pipe %u\n", psde->pipe_name, pipe);
 	return plane;
 
diff --git a/drivers/gpu/drm/msm/sde/sde_vbif.c b/drivers/gpu/drm/msm/sde/sde_vbif.c
index fb6d9da..c0c8248 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.c
@@ -223,8 +223,7 @@
 	struct dentry *debugfs_vbif;
 	int i, j;
 
-	sde_kms->debugfs_vbif = debugfs_create_dir("vbif",
-			sde_kms->debugfs_root);
+	sde_kms->debugfs_vbif = debugfs_create_dir("vbif", debugfs_root);
 	if (!sde_kms->debugfs_vbif) {
 		SDE_ERROR("failed to create vbif debugfs\n");
 		return -EINVAL;