drm/msm/sde: add preliminary debugfs support

Debugfs additions under /sys/kernel/debug/dri/0.

Add sde_debugfs_setup_regset32()/sde_debugfs_create_regset32()
for creating register dump debugfs files, and add initial debugfs
support in plane driver.

Change-Id: I7e0a368fbcf6a249a016d2224c01666fe51abae9
Signed-off-by: Clarence Ip <cip@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 221958b..38fb9c0 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -25,6 +25,10 @@
 #define LM(i)        (LM_0  + (i))
 #define INTF(i)      (INTF_0 + (i))
 
+/* uncomment to enable higher level IRQ msg's */
+/*#define DBG_IRQ      DBG*/
+#define DBG_IRQ(fmt, ...)
+
 static struct sde_kms *get_kms(struct drm_crtc *crtc)
 {
 	struct msm_drm_private *priv = crtc->dev->dev_private;
@@ -370,7 +374,7 @@
 
 	if (sde_crtc->vblank_enable) {
 		drm_handle_vblank(dev, sde_crtc->id);
-		DBG("");
+		DBG_IRQ("");
 	}
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index d8dfe23..fa54d80 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -15,8 +15,6 @@
 
 #include "drm_crtc.h"
 
-#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
-
 #define CRTC_DUAL_MIXERS	2
 #define PENDING_FLIP		2
 /* worst case one frame wait time based on 30 FPS : 33.33ms*/
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index 9697553..3a222e2 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
@@ -80,6 +80,7 @@
 			b->base_off = addr;
 			b->blk_off = m->cdm[i].base;
 			b->hwversion = m->hwversion;
+			b->log_mask = SDE_DBG_MASK_CDM;
 			return &m->cdm[i];
 		}
 	}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index fe8917f..2b98ee3 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -27,6 +27,7 @@
 			b->base_off = addr;
 			b->blk_off = m->dspp[i].base;
 			b->hwversion = m->hwversion;
+			b->log_mask = SDE_DBG_MASK_DSPP;
 			return &m->dspp[i];
 		}
 	}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.c b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
index 160ce82..f853ab6 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_intf.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
@@ -76,6 +76,7 @@
 			b->base_off = addr;
 			b->blk_off = m->intf[i].base;
 			b->hwversion = m->hwversion;
+			b->log_mask = SDE_DBG_MASK_INTF;
 			return &m->intf[i];
 		}
 	}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
index 0591d89..88af47d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
@@ -37,6 +37,7 @@
 			b->base_off = addr;
 			b->blk_off = m->mixer[i].base;
 			b->hwversion = m->hwversion;
+			b->log_mask = SDE_DBG_MASK_LM;
 			return &m->mixer[i];
 		}
 	}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.c
index ab2e5a3..7e513af 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdp_ctl.c
@@ -39,6 +39,7 @@
 			b->base_off = addr;
 			b->blk_off = m->ctl[i].base;
 			b->hwversion = m->hwversion;
+			b->log_mask = SDE_DBG_MASK_CTL;
 			return &m->ctl[i];
 		}
 	}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdp_util.c b/drivers/gpu/drm/msm/sde/sde_hw_mdp_util.c
index d8e928a..74562e3 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdp_util.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdp_util.c
@@ -9,17 +9,29 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-
+#include "msm_drv.h"
+#include "sde_hw_mdss.h"
 #include "sde_hw_mdp_util.h"
 
-void sde_hw_reg_write(void  __iomem *base, u32 blk_off, u32 reg_off, u32 val)
+/* using a file static variables for debugfs access */
+static u32 sde_hw_util_log_mask = SDE_DBG_MASK_NONE;
+
+void SDE_REG_WRITE(struct sde_hw_blk_reg_map *c, u32 reg_off, u32 val)
 {
-	writel_relaxed(val, base + blk_off + reg_off);
+	/* don't need to mutex protect this */
+	if (c->log_mask & sde_hw_util_log_mask)
+		DBG("[0x%X] <= 0x%X", c->blk_off + reg_off, val);
+	writel_relaxed(val, c->base_off + c->blk_off + reg_off);
 }
 
-u32 sde_hw_reg_read(void  __iomem *base, u32 blk_off, u32 reg_off)
+int SDE_REG_READ(struct sde_hw_blk_reg_map *c, u32 reg_off)
 {
-	return readl_relaxed(base + blk_off + reg_off);
+	return readl_relaxed(c->base_off + c->blk_off + reg_off);
+}
+
+u32 *sde_hw_util_get_log_mask_ptr(void)
+{
+	return &sde_hw_util_log_mask;
 }
 
 void sde_hw_csc_setup(struct sde_hw_blk_reg_map  *c,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdp_util.h b/drivers/gpu/drm/msm/sde/sde_hw_mdp_util.h
index 2a7af83..ba241c4 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdp_util.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdp_util.h
@@ -31,22 +31,16 @@
 	u32 blk_off;
 	u32 length;
 	u32 hwversion;
+	u32 log_mask;
 };
 
-void sde_hw_reg_write(void __iomem *base, u32 blk_offset, u32 reg, u32 val);
+u32 *sde_hw_util_get_log_mask_ptr(void);
 
-u32 sde_hw_reg_read(void __iomem *base, u32 blk_offset, u32 reg);
+void SDE_REG_WRITE(struct sde_hw_blk_reg_map *c, u32 reg_off, u32 val);
 
-static inline void SDE_REG_WRITE(struct sde_hw_blk_reg_map *c, u32 reg_off,
-		u32 val)
-{
-	sde_hw_reg_write(c->base_off, c->blk_off, reg_off, val);
-}
+int SDE_REG_READ(struct sde_hw_blk_reg_map *c, u32 reg_off);
 
-static inline int SDE_REG_READ(struct sde_hw_blk_reg_map *c, u32 reg_off)
-{
-	return sde_hw_reg_read(c->base_off, c->blk_off, reg_off);
-}
+void *sde_hw_util_get_dir(void);
 
 void sde_hw_csc_setup(struct sde_hw_blk_reg_map  *c,
 		u32 csc_reg_off,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index 55f09b5..56627fe 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -355,4 +355,17 @@
 	u32 color_3;
 };
 
+/*
+ * Define bit masks for h/w logging.
+ */
+#define SDE_DBG_MASK_NONE     (1 << 0)
+#define SDE_DBG_MASK_CDM      (1 << 1)
+#define SDE_DBG_MASK_DSPP     (1 << 2)
+#define SDE_DBG_MASK_INTF     (1 << 3)
+#define SDE_DBG_MASK_LM       (1 << 4)
+#define SDE_DBG_MASK_CTL      (1 << 5)
+#define SDE_DBG_MASK_PINGPONG (1 << 6)
+#define SDE_DBG_MASK_SSPP     (1 << 7)
+#define SDE_DBG_MASK_WB       (1 << 8)
+
 #endif  /* _SDE_HW_MDSS_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
index e678003..49a98be 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
@@ -48,6 +48,7 @@
 			b->base_off = addr;
 			b->blk_off = m->pingpong[i].base;
 			b->hwversion = m->hwversion;
+			b->log_mask = SDE_DBG_MASK_PINGPONG;
 			return &m->pingpong[i];
 		}
 	}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index 49b8d5b..59ee3b9 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -552,6 +552,7 @@
 				b->base_off = addr;
 				b->blk_off = catalog->sspp[i].base;
 				b->hwversion = catalog->hwversion;
+				b->log_mask = SDE_DBG_MASK_SSPP;
 				return &catalog->sspp[i];
 			}
 		}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_wb.c b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
index 1eaad18..ef3a35a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
@@ -27,6 +27,7 @@
 			b->base_off = addr;
 			b->blk_off = m->wb[i].base;
 			b->hwversion = m->hwversion;
+			b->log_mask = SDE_DBG_MASK_WB;
 			return &m->wb[i];
 		}
 	}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 08aa914..7a25ffa 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -11,10 +11,14 @@
  */
 
 #include <drm/drm_crtc.h>
+#include <linux/debugfs.h>
+
 #include "msm_drv.h"
 #include "msm_mmu.h"
 #include "sde_kms.h"
+#include "sde_formats.h"
 #include "sde_hw_mdss.h"
+#include "sde_hw_util.h"
 #include "sde_hw_intf.h"
 
 static const char * const iommu_ports[] = {
@@ -32,6 +36,23 @@
 
 #define DEFAULT_MDP_SRC_CLK 300000000
 
+/**
+ * Controls size of event log buffer. Specified as a power of 2.
+ */
+#define SDE_EVTLOG_SIZE	1024
+
+/*
+ * To enable overall DRM driver logging
+ * # echo 0x2 > /sys/module/drm/parameters/debug
+ *
+ * To enable DRM driver h/w logging
+ * # echo <mask> > /sys/kernel/debug/dri/0/hw_log_mask
+ *
+ * See sde_hw_mdss.h for h/w logging mask definitions (search for SDE_DBG_MASK_)
+ */
+#define SDE_DEBUGFS_DIR "msm_sde"
+#define SDE_DEBUGFS_HWMASKNAME "hw_log_mask"
+
 int sde_disable(struct sde_kms *sde_kms)
 {
 	DBG("");
@@ -58,6 +79,90 @@
 	return 0;
 }
 
+static int sde_debugfs_show_regset32(struct seq_file *s, void *data)
+{
+	struct sde_debugfs_regset32 *regset = s->private;
+	void __iomem *base;
+	int i;
+
+	base = regset->base + regset->offset;
+
+	for (i = 0; i < regset->blk_len; i += 4)
+		seq_printf(s, "[%x] 0x%08x\n",
+				regset->offset + i, readl_relaxed(base + i));
+
+	return 0;
+}
+
+static int sde_debugfs_open_regset32(struct inode *inode, struct file *file)
+{
+	return single_open(file, sde_debugfs_show_regset32, inode->i_private);
+}
+
+static const struct file_operations sde_fops_regset32 = {
+	.open =		sde_debugfs_open_regset32,
+	.read =		seq_read,
+	.llseek =	seq_lseek,
+	.release =	single_release,
+};
+
+void sde_debugfs_setup_regset32(struct sde_debugfs_regset32 *regset,
+		uint32_t offset, uint32_t length, void __iomem *base)
+{
+	if (regset) {
+		regset->offset = offset;
+		regset->blk_len = length;
+		regset->base = base;
+	}
+}
+
+void *sde_debugfs_create_regset32(const char *name, umode_t mode,
+		void *parent, struct sde_debugfs_regset32 *regset)
+{
+	if (!name || !regset || !regset->base || !regset->blk_len)
+		return NULL;
+
+	/* make sure offset is a multiple of 4 */
+	regset->offset = round_down(regset->offset, 4);
+
+	return debugfs_create_file(name, mode, parent,
+			regset, &sde_fops_regset32);
+}
+
+void *sde_debugfs_get_root(struct sde_kms *sde_kms)
+{
+	return sde_kms ? sde_kms->debugfs_root : 0;
+}
+
+static int sde_debugfs_init(struct sde_kms *sde_kms)
+{
+	void *p;
+
+	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);
+
+	/* allow debugfs_root to be NULL */
+	debugfs_create_x32(SDE_DEBUGFS_HWMASKNAME,
+			0644, sde_kms->debugfs_root, p);
+	return 0;
+}
+
+static void sde_debugfs_destroy(struct sde_kms *sde_kms)
+{
+	/* don't need to NULL check debugfs_root */
+	if (sde_kms) {
+		debugfs_remove_recursive(sde_kms->debugfs_root);
+		sde_kms->debugfs_root = 0;
+	}
+}
+
 static void sde_prepare_commit(struct msm_kms *kms,
 		struct drm_atomic_state *state)
 {
@@ -104,7 +209,7 @@
 
 		plane = sde_plane_init(dev, catalog->sspp[i].id, primary);
 		if (IS_ERR(plane)) {
-			pr_err("%s: sde_plane_init failed", __func__);
+			DRM_ERROR("sde_plane_init failed\n");
 			ret = PTR_ERR(plane);
 			goto fail;
 		}
@@ -175,6 +280,7 @@
 {
 	struct sde_kms *sde_kms = to_sde_kms(kms);
 
+	sde_debugfs_destroy(sde_kms);
 	sde_irq_domain_fini(sde_kms);
 	sde_hw_intr_destroy(sde_kms->hw_intr);
 	kfree(sde_kms);
@@ -191,7 +297,7 @@
 	.wait_for_crtc_commit_done = sde_wait_for_crtc_commit_done,
 	.enable_vblank   = sde_enable_vblank,
 	.disable_vblank  = sde_disable_vblank,
-	.get_format      = mdp_get_format,
+	.get_format      = sde_get_msm_format,
 	.round_pixclk    = sde_round_pixclk,
 	.preclose        = sde_preclose,
 	.destroy         = sde_destroy,
@@ -204,7 +310,7 @@
 	struct clk *clk = devm_clk_get(dev, name);
 
 	if (IS_ERR(clk) && mandatory) {
-		dev_err(dev, "failed to get %s (%ld)\n", name, PTR_ERR(clk));
+		DRM_ERROR("failed to get %s (%ld)\n", name, PTR_ERR(clk));
 		return PTR_ERR(clk);
 	}
 	if (IS_ERR(clk))
@@ -234,7 +340,7 @@
 		ret = PTR_ERR(sde_kms->mmio);
 		goto fail;
 	}
-	pr_err("Mapped Mdp address space @%pK", sde_kms->mmio);
+	DRM_INFO("Mapped Mdp address space @%pK\n", sde_kms->mmio);
 
 	sde_kms->vbif = msm_ioremap(pdev, "vbif_phys", "VBIF");
 	if (IS_ERR(sde_kms->vbif)) {
@@ -245,14 +351,14 @@
 	sde_kms->venus = devm_regulator_get_optional(&pdev->dev, "gdsc-venus");
 	if (IS_ERR(sde_kms->venus)) {
 		ret = PTR_ERR(sde_kms->venus);
-		DBG("failed to get Venus GDSC regulator: %d\n", ret);
+		DBG("failed to get Venus GDSC regulator: %d", ret);
 		sde_kms->venus = NULL;
 	}
 
 	if (sde_kms->venus) {
 		ret = regulator_enable(sde_kms->venus);
 		if (ret) {
-			DBG("failed to enable venus GDSC: %d\n", ret);
+			DBG("failed to enable venus GDSC: %d", ret);
 			goto fail;
 		}
 	}
@@ -265,14 +371,14 @@
 
 	ret = regulator_enable(sde_kms->vdd);
 	if (ret) {
-		DBG("failed to enable regulator vdd: %d\n", ret);
+		DBG("failed to enable regulator vdd: %d", ret);
 		goto fail;
 	}
 
 	sde_kms->mmagic = devm_regulator_get_optional(&pdev->dev, "mmagic");
 	if (IS_ERR(sde_kms->mmagic)) {
 		ret = PTR_ERR(sde_kms->mmagic);
-		DBG("failed to get mmagic GDSC regulator: %d\n", ret);
+		DBG("failed to get mmagic GDSC regulator: %d", ret);
 		sde_kms->mmagic = NULL;
 	}
 
@@ -301,15 +407,14 @@
 	if (sde_kms->mmagic) {
 		ret = regulator_enable(sde_kms->mmagic);
 		if (ret) {
-			dev_err(sde_kms->dev->dev,
-				"failed to enable mmagic GDSC: %d\n", ret);
+			DRM_ERROR("failed to enable mmagic GDSC: %d\n", ret);
 			goto fail;
 		}
 	}
 	if (sde_kms->mmagic_clk) {
 		clk_prepare_enable(sde_kms->mmagic_clk);
 		if (ret) {
-			dev_err(sde_kms->dev->dev, "failed to enable mmagic_clk\n");
+			DRM_ERROR("failed to enable mmagic_clk\n");
 			goto undo_gdsc;
 		}
 	}
@@ -328,14 +433,13 @@
 
 static int sde_translation_ctrl_pwr(struct sde_kms *sde_kms, bool on)
 {
-	struct device *dev = sde_kms->dev->dev;
 	int ret;
 
 	if (on) {
 		if (sde_kms->iommu_clk) {
 			ret = clk_prepare_enable(sde_kms->iommu_clk);
 			if (ret) {
-				dev_err(dev, "failed to enable iommu_clk\n");
+				DRM_ERROR("failed to enable iommu_clk\n");
 				goto undo_mmagic_clk;
 			}
 		}
@@ -388,16 +492,14 @@
 		mmu = msm_smmu_new(sde_kms->dev->dev, MSM_SMMU_DOMAIN_UNSECURE);
 		if (IS_ERR(mmu)) {
 			ret = PTR_ERR(mmu);
-			dev_err(sde_kms->dev->dev,
-				"failed to init iommu: %d\n", ret);
+			DRM_ERROR("failed to init iommu: %d\n", ret);
 			iommu_domain_free(iommu);
 			goto fail;
 		}
 
 		ret = sde_translation_ctrl_pwr(sde_kms, true);
 		if (ret) {
-			dev_err(sde_kms->dev->dev,
-				"failed to power iommu: %d\n", ret);
+			DRM_ERROR("failed to power iommu: %d\n", ret);
 			mmu->funcs->destroy(mmu);
 			goto fail;
 		}
@@ -405,8 +507,7 @@
 		ret = mmu->funcs->attach(mmu, (const char **)iommu_ports,
 				ARRAY_SIZE(iommu_ports));
 		if (ret) {
-			dev_err(sde_kms->dev->dev,
-				"failed to attach iommu: %d\n", ret);
+			DRM_ERROR("failed to attach iommu: %d\n", ret);
 			mmu->funcs->destroy(mmu);
 			goto fail;
 		}
@@ -420,8 +521,7 @@
 	sde_kms->mmu_id = msm_register_mmu(sde_kms->dev, mmu);
 	if (sde_kms->mmu_id < 0) {
 		ret = sde_kms->mmu_id;
-		dev_err(sde_kms->dev->dev,
-			"failed to register sde iommu: %d\n", ret);
+		DRM_ERROR("failed to register sde iommu: %d\n", ret);
 		goto fail;
 	}
 
@@ -433,6 +533,7 @@
 
 struct msm_kms *sde_kms_init(struct drm_device *dev)
 {
+	struct msm_drm_private *priv = dev->dev_private;
 	struct platform_device *pdev = dev->platformdev;
 	struct sde_mdss_cfg *catalog;
 	struct sde_kms *sde_kms;
@@ -474,6 +575,15 @@
 	sde_mmu_init(sde_kms);
 
 	/*
+	 * NOTE: Calling sde_debugfs_init here so that the drm_minor device for
+	 *       'primary' is already created.
+	 */
+	sde_debugfs_init(sde_kms);
+	msm_evtlog_init(&priv->evtlog, SDE_EVTLOG_SIZE,
+			sde_debugfs_get_root(sde_kms));
+	MSM_EVT(dev, 0, 0);
+
+	/*
 	 * modeset_init should create the DRM related objects i.e. CRTCs,
 	 * planes, encoders, connectors and so forth
 	 */
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index c5e225f..bb043df 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -84,6 +84,9 @@
 	struct msm_mmu *mmu;
 	int mmu_id;
 
+	/* directory entry for debugfs */
+	void *debugfs_root;
+
 	/* io/register spaces: */
 	void __iomem *mmio, *vbif;
 
@@ -144,6 +147,79 @@
 int sde_enable(struct sde_kms *sde_kms);
 
 /**
+ * Debugfs functions - extra helper functions for debugfs support
+ *
+ * Main debugfs documentation is located at,
+ *
+ * Documentation/filesystems/debugfs.txt
+ *
+ * @sde_debugfs_setup_regset32: Initialize data for sde_debugfs_create_regset32
+ * @sde_debugfs_create_regset32: Create 32-bit register dump file
+ * @sde_debugfs_get_root: Get root dentry for SDE_KMS's debugfs node
+ */
+
+/**
+ * Companion structure for sde_debugfs_create_regset32. Do not initialize the
+ * members of this structure explicitly; use sde_debugfs_setup_regset32 instead.
+ */
+struct sde_debugfs_regset32 {
+	uint32_t offset;
+	uint32_t blk_len;
+	void __iomem *base;
+};
+
+/**
+ * sde_debugfs_setup_regset32 - Initialize register block definition for debugfs
+ * This function is meant to initialize sde_debugfs_regset32 structures for use
+ * with sde_debugfs_create_regset32.
+ * @regset: opaque register definition structure
+ * @offset: sub-block offset
+ * @length: sub-block length, in bytes
+ * @base: base IOMEM address
+ */
+void sde_debugfs_setup_regset32(struct sde_debugfs_regset32 *regset,
+		uint32_t offset, uint32_t length, void __iomem *base);
+
+/**
+ * sde_debugfs_create_regset32 - Create register read back file for debugfs
+ *
+ * This function is almost identical to the standard debugfs_create_regset32()
+ * function, with the main difference being that a list of register
+ * names/offsets do not need to be provided. The 'read' function simply outputs
+ * sequential register values over a specified range.
+ *
+ * Similar to the related debugfs_create_regset32 API, the structure pointed to
+ * by regset needs to persist for the lifetime of the created file. The calling
+ * code is responsible for initialization/management of this structure.
+ *
+ * The structure pointed to by regset is meant to be opaque. Please use
+ * sde_debugfs_setup_regset32 to initialize it.
+ *
+ * @name:   File name within debugfs
+ * @mode:   File mode within debugfs
+ * @parent: Parent directory entry within debugfs, can be NULL
+ * @regset: Pointer to persistent register block definition
+ *
+ * Return: dentry pointer for newly created file, use either debugfs_remove()
+ *         or debugfs_remove_recursive() (on a parent directory) to remove the
+ *         file
+ */
+void *sde_debugfs_create_regset32(const char *name, umode_t mode,
+		void *parent, struct sde_debugfs_regset32 *regset);
+
+/**
+ * sde_debugfs_get_root - Return root directory entry for SDE's debugfs
+ *
+ * The return value should be passed as the 'parent' argument to subsequent
+ * debugfs create calls.
+ *
+ * @sde_kms: Pointer to SDE's KMS structure
+ *
+ * Return: dentry pointer for SDE's debugfs location
+ */
+void *sde_debugfs_get_root(struct sde_kms *sde_kms);
+
+/**
  * HW resource manager functions
  * @sde_rm_acquire_ctl_path : Allocates control path
  * @sde_rm_get_ctl_path     : returns control path driver context for already
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 7393764..6dbf346 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -9,7 +9,7 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-
+#include <linux/debugfs.h>
 #include "sde_kms.h"
 #include "sde_hwio.h"
 #include "sde_hw_mdp_ctl.h"
@@ -26,13 +26,7 @@
 #define SHARP_SMOOTH_THR_DEFAULT	8
 #define SHARP_NOISE_THR_DEFAULT	2
 
-#ifndef SDE_PLANE_DEBUG_START
-#define SDE_PLANE_DEBUG_START()
-#endif
-
-#ifndef SDE_PLANE_DEBUG_END
-#define SDE_PLANE_DEBUG_END()
-#endif
+#define SDE_PIPE_NAME_SIZE  8
 
 struct sde_plane {
 	struct drm_plane base;
@@ -49,6 +43,14 @@
 	struct sde_hw_pipe_cfg pipe_cfg;
 	struct sde_hw_pixel_ext pixel_ext;
 	struct sde_hw_sharp_cfg sharp_cfg;
+
+	char pipe_name[SDE_PIPE_NAME_SIZE];
+
+	/* debugfs related stuff */
+	struct dentry *debugfs_root;
+	struct sde_debugfs_regset32 debugfs_src;
+	struct sde_debugfs_regset32 debugfs_scaler;
+	struct sde_debugfs_regset32 debugfs_csc;
 };
 #define to_sde_plane(x) container_of(x, struct sde_plane, base)
 
@@ -249,7 +251,7 @@
 	struct sde_hw_pixel_ext *pe;
 	int ret = 0;
 
-	SDE_PLANE_DEBUG_START();
+	DBG("");
 	nplanes = drm_format_num_planes(fb->pixel_format);
 
 	pstate = to_sde_plane_state(plane->state);
@@ -263,7 +265,7 @@
 	src_w = src_w >> 16;
 	src_h = src_h >> 16;
 
-	DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", psde->name,
+	DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", psde->pipe_name,
 			fb->base.id, src_x, src_y, src_w, src_h,
 			crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h);
 
@@ -387,7 +389,6 @@
 	if (fmt->is_yuv)
 		_sde_plane_setup_csc(psde, pstate, fmt);
 
-	SDE_PLANE_DEBUG_END();
 	return ret;
 }
 
@@ -400,9 +401,7 @@
 	if (!new_state->fb)
 		return 0;
 
-	SDE_PLANE_DEBUG_START();
-	SDE_PLANE_DEBUG_END();
-	DBG("%s: prepare: FB[%u]", psde->name, fb->base.id);
+	DBG("%s: prepare: FB[%u]", psde->pipe_name, fb->base.id);
 	return msm_framebuffer_prepare(fb, psde->mmu_id);
 }
 
@@ -415,9 +414,7 @@
 	if (!fb)
 		return;
 
-	SDE_PLANE_DEBUG_START();
-	SDE_PLANE_DEBUG_END();
-	DBG("%s: cleanup: FB[%u]", psde->name, fb->base.id);
+	DBG("%s: cleanup: FB[%u]", psde->pipe_name, fb->base.id);
 	msm_framebuffer_cleanup(fb, psde->mmu_id);
 }
 
@@ -428,9 +425,7 @@
 	struct drm_plane_state *old_state = plane->state;
 	const struct mdp_format *format;
 
-	SDE_PLANE_DEBUG_START();
-	SDE_PLANE_DEBUG_END();
-	DBG("%s: check (%d -> %d)", psde->name,
+	DBG("%s: check (%d -> %d)", psde->pipe_name,
 			sde_plane_enabled(old_state), sde_plane_enabled(state));
 
 	if (sde_plane_enabled(state)) {
@@ -462,15 +457,15 @@
 		bool full_modeset = false;
 
 		if (state->fb->pixel_format != old_state->fb->pixel_format) {
-			DBG("%s: pixel_format change!", psde->name);
+			DBG("%s: pixel_format change!", psde->pipe_name);
 			full_modeset = true;
 		}
 		if (state->src_w != old_state->src_w) {
-			DBG("%s: src_w change!", psde->name);
+			DBG("%s: src_w change!", psde->pipe_name);
 			full_modeset = true;
 		}
 		if (to_sde_plane_state(old_state)->pending) {
-			DBG("%s: still pending!", psde->name);
+			DBG("%s: still pending!", psde->pipe_name);
 			full_modeset = true;
 		}
 		if (full_modeset) {
@@ -493,9 +488,8 @@
 	struct sde_plane *sde_plane = to_sde_plane(plane);
 	struct drm_plane_state *state = plane->state;
 
-	DBG("%s: update", sde_plane->name);
+	DBG("%s: update", sde_plane->pipe_name);
 
-	SDE_PLANE_DEBUG_START();
 	if (!sde_plane_enabled(state)) {
 		to_sde_plane_state(state)->pending = true;
 	} else if (to_sde_plane_state(state)->mode_changed) {
@@ -513,7 +507,6 @@
 	} else {
 		_sde_plane_set_scanout(plane, &sde_plane->pipe_cfg, state->fb);
 	}
-	SDE_PLANE_DEBUG_END();
 }
 
 static void _sde_plane_install_range_property(struct drm_plane *plane,
@@ -527,8 +520,7 @@
 			*prop = drm_property_create_range(dev,
 					0 /* flags */, name, min, max);
 			if (*prop == 0)
-				dev_warn(dev->dev,
-					"Create property %s failed\n", name);
+				DRM_ERROR("Create property %s failed\n", name);
 		}
 
 		/* always attach property, if created */
@@ -550,7 +542,7 @@
 	struct drm_device *dev = plane->dev;
 	struct msm_drm_private *dev_priv = dev->dev_private;
 
-	SDE_PLANE_DEBUG_START();
+	DBG("");
 
 	/* range/enum properties */
 	_sde_plane_install_range_property(plane, dev, "zpos", 1, 255, 1,
@@ -559,8 +551,6 @@
 	/* blob properties */
 	_sde_plane_install_blob_property(plane, dev, "pixext",
 			&(dev_priv->plane_property[PLANE_PROP_PIXEXT]));
-
-	SDE_PLANE_DEBUG_END();
 }
 
 static int sde_plane_atomic_set_property(struct drm_plane *plane,
@@ -573,7 +563,7 @@
 	struct msm_drm_private *dev_priv = dev->dev_private;
 	int idx, ret = -EINVAL;
 
-	SDE_PLANE_DEBUG_START();
+	DBG("");
 
 	pstate = to_sde_plane_state(state);
 
@@ -610,7 +600,6 @@
 	if (ret == -EINVAL)
 		dev_err(dev->dev, "Invalid property set\n");
 
-	SDE_PLANE_DEBUG_END();
 	return ret;
 }
 
@@ -619,10 +608,9 @@
 {
 	int rc;
 
-	SDE_PLANE_DEBUG_START();
+	DBG("");
 	rc = sde_plane_atomic_set_property(plane, plane->state, property,
 		val);
-	SDE_PLANE_DEBUG_END();
 	return rc;
 }
 
@@ -635,7 +623,7 @@
 	struct msm_drm_private *dev_priv = dev->dev_private;
 	int idx, ret = -EINVAL;
 
-	SDE_PLANE_DEBUG_START();
+	DBG("");
 
 	pstate = to_sde_plane_state(state);
 
@@ -651,28 +639,30 @@
 	if (ret == -EINVAL)
 		dev_err(dev->dev, "Invalid property get\n");
 
-	SDE_PLANE_DEBUG_END();
-
 	return ret;
 }
 
 static void sde_plane_destroy(struct drm_plane *plane)
 {
-	struct sde_plane *psde = to_sde_plane(plane);
+	struct sde_plane *psde;
 
-	SDE_PLANE_DEBUG_START();
+	DBG("");
 
-	if (psde->pipe_hw)
-		sde_hw_sspp_destroy(psde->pipe_hw);
+	if (plane) {
+		psde = to_sde_plane(plane);
 
-	drm_plane_helper_disable(plane);
+		debugfs_remove_recursive(psde->debugfs_root);
 
-	/* this will destroy the states as well */
-	drm_plane_cleanup(plane);
+		if (psde->pipe_hw)
+			sde_hw_sspp_destroy(psde->pipe_hw);
 
-	kfree(psde);
+		drm_plane_helper_disable(plane);
 
-	SDE_PLANE_DEBUG_END();
+		/* this will destroy the states as well */
+		drm_plane_cleanup(plane);
+
+		kfree(psde);
+	}
 }
 
 static void sde_plane_destroy_state(struct drm_plane *plane,
@@ -681,7 +671,7 @@
 	struct sde_plane_state *pstate;
 	int i;
 
-	SDE_PLANE_DEBUG_START();
+	DBG("");
 
 	/* remove ref count for frame buffers */
 	if (state->fb)
@@ -696,8 +686,6 @@
 					pstate->property_blobs[i]);
 
 	kfree(pstate);
-
-	SDE_PLANE_DEBUG_END();
 }
 
 static struct drm_plane_state *
@@ -709,7 +697,7 @@
 	if (WARN_ON(!plane->state))
 		return NULL;
 
-	SDE_PLANE_DEBUG_START();
+	DBG("");
 	pstate = kmemdup(to_sde_plane_state(plane->state),
 			sizeof(*pstate), GFP_KERNEL);
 	if (pstate) {
@@ -726,7 +714,6 @@
 		pstate->mode_changed = false;
 		pstate->pending = false;
 	}
-	SDE_PLANE_DEBUG_END();
 
 	return pstate ? &pstate->base : NULL;
 }
@@ -735,7 +722,7 @@
 {
 	struct sde_plane_state *pstate;
 
-	SDE_PLANE_DEBUG_START();
+	DBG("");
 	if (plane->state && plane->state->fb)
 		drm_framebuffer_unreference(plane->state->fb);
 
@@ -757,7 +744,6 @@
 	pstate->base.plane = plane;
 
 	plane->state = &pstate->base;
-	SDE_PLANE_DEBUG_END();
 }
 
 static const struct drm_plane_funcs sde_plane_funcs = {
@@ -786,11 +772,56 @@
 	return sde_plane->pipe;
 }
 
+static void _sde_plane_init_debugfs(struct sde_plane *psde, struct sde_kms *kms)
+{
+	const struct sde_sspp_sub_blks *sblk = 0;
+	const struct sde_sspp_cfg *cfg = 0;
+
+	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", 0444,
+					psde->debugfs_root, &psde->features);
+
+			/* add register dump support */
+			sde_debugfs_setup_regset32(&psde->debugfs_src,
+					sblk->src_blk.base + cfg->base,
+					sblk->src_blk.len,
+					kms->mmio);
+			sde_debugfs_create_regset32("src_blk", 0444,
+					psde->debugfs_root, &psde->debugfs_src);
+
+			sde_debugfs_setup_regset32(&psde->debugfs_scaler,
+					sblk->scaler_blk.base + cfg->base,
+					sblk->scaler_blk.len,
+					kms->mmio);
+			sde_debugfs_create_regset32("scaler_blk", 0444,
+					psde->debugfs_root,
+					&psde->debugfs_scaler);
+
+			sde_debugfs_setup_regset32(&psde->debugfs_csc,
+					sblk->csc_blk.base + cfg->base,
+					sblk->csc_blk.len,
+					kms->mmio);
+			sde_debugfs_create_regset32("csc_blk", 0444,
+					psde->debugfs_root, &psde->debugfs_csc);
+		}
+	}
+}
+
 /* initialize plane */
 struct drm_plane *sde_plane_init(struct drm_device *dev,
 		uint32_t pipe, bool private_plane)
 {
-	static const char tmp_name[] = "---";
 	struct drm_plane *plane = NULL;
 	struct sde_plane *psde;
 	struct msm_drm_private *priv;
@@ -811,18 +842,16 @@
 	}
 	kms = to_sde_kms(priv->kms);
 
+	/* create and zero local structure */
 	psde = kzalloc(sizeof(*psde), GFP_KERNEL);
 	if (!psde) {
 		ret = -ENOMEM;
 		goto fail;
 	}
 
-	memset(psde, 0, sizeof(*psde));
-
 	plane = &psde->base;
 
 	psde->pipe = pipe;
-	psde->name = tmp_name;
 
 	if (kms) {
 		/* mmu id for buffer mapping */
@@ -856,7 +885,12 @@
 		goto fail;
 	}
 
-	pr_err("%s: Successfully created plane\n", __func__);
+	/* save user friendly pipe name for later */
+	snprintf(psde->pipe_name, SDE_PIPE_NAME_SIZE, "pipe%u", pipe);
+
+	_sde_plane_init_debugfs(psde, kms);
+
+	DRM_INFO("Successfully created plane for %s\n", psde->pipe_name);
 	return plane;
 
 fail: