drm/msm: add register dumping facility to sde debug

Need the capability to dump the content of sde registers during
panic and other scenarios. Add debugfs facility for triggering
register dumping. Add registration of sde and vbif as base
hardware blocks. Add the blocks within the sde as named register
dumping ranges.

CRs-Fixed: 2005394
Change-Id: I410c4e5270447d4d3a8364287fa61748ead5410f
Signed-off-by: Lloyd Atkinson <latkinso@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index e70a534..af327f1 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -46,6 +46,7 @@
 	sde/sde_backlight.o \
 	sde/sde_color_processing.o \
 	sde/sde_vbif.o \
+	sde_dbg.o \
 	sde_dbg_evtlog.o \
 	sde_io_util.o \
 	sde/sde_hw_reg_dma_v1_color_proc.o \
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 329381e..7f6b08a 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -291,7 +291,7 @@ static int msm_drm_uninit(struct device *dev)
 			       priv->vram.paddr, attrs);
 	}
 
-	sde_evtlog_destroy();
+	sde_dbg_destroy();
 
 	sde_power_client_destroy(&priv->phandle, priv->pclient);
 	sde_power_resource_deinit(pdev, &priv->phandle);
@@ -433,12 +433,18 @@ static int msm_component_bind_all(struct device *dev,
 }
 #endif
 
+static int msm_power_enable_wrapper(void *handle, void *client, bool enable)
+{
+	return sde_power_resource_enable(handle, client, enable);
+}
+
 static int msm_drm_init(struct device *dev, struct drm_driver *drv)
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct drm_device *ddev;
 	struct msm_drm_private *priv;
 	struct msm_kms *kms;
+	struct sde_dbg_power_ctrl dbg_power_ctrl = { 0 };
 	int ret, i;
 
 	ddev = drm_dev_alloc(drv, dev);
@@ -499,9 +505,13 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
 	if (ret)
 		goto fail;
 
-	ret = sde_evtlog_init(ddev->primary->debugfs_root);
+	dbg_power_ctrl.handle = &priv->phandle;
+	dbg_power_ctrl.client = priv->pclient;
+	dbg_power_ctrl.enable_fn = msm_power_enable_wrapper;
+	ret = sde_dbg_init(ddev->primary->debugfs_root, &pdev->dev,
+			&dbg_power_ctrl);
 	if (ret) {
-		dev_err(dev, "failed to init evtlog: %d\n", ret);
+		dev_err(dev, "failed to init sde dbg: %d\n", ret);
 		goto fail;
 	}
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index 66f73bd..c0d5044 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
@@ -14,6 +14,7 @@
 #include "sde_hwio.h"
 #include "sde_hw_catalog.h"
 #include "sde_hw_cdm.h"
+#include "sde_dbg.h"
 
 #define CDM_CSC_10_OPMODE                  0x000
 #define CDM_CSC_10_BASE                    0x004
@@ -66,7 +67,6 @@ static struct sde_cdm_cfg *_cdm_offset(enum sde_cdm cdm,
 		if (cdm == m->cdm[i].id) {
 			b->base_off = addr;
 			b->blk_off = m->cdm[i].base;
-			b->length = m->cdm[i].len;
 			b->hwversion = m->hwversion;
 			b->log_mask = SDE_DBG_MASK_CDM;
 			return &m->cdm[i];
@@ -315,6 +315,9 @@ struct sde_hw_cdm *sde_hw_cdm_init(enum sde_cdm idx,
 	_setup_cdm_ops(&c->ops, c->cdm_hw_cap->features);
 	c->hw_mdp = hw_mdp;
 
+	sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+			c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
 	return c;
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 9fc7e9f..d237c4db 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -13,6 +13,7 @@
 #include <linux/delay.h>
 #include "sde_hwio.h"
 #include "sde_hw_ctl.h"
+#include "sde_dbg.h"
 
 #define   CTL_LAYER(lm)                 \
 	(((lm) == LM_5) ? (0x024) : (((lm) - LM_0) * 0x004))
@@ -490,6 +491,9 @@ struct sde_hw_ctl *sde_hw_ctl_init(enum sde_ctl idx,
 	c->mixer_count = m->mixer_count;
 	c->mixer_hw_caps = m->mixer;
 
+	sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+			c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
 	return c;
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index f9ad572..6110a07 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -15,6 +15,7 @@
 #include "sde_hw_catalog.h"
 #include "sde_hw_dspp.h"
 #include "sde_hw_color_processing.h"
+#include "sde_dbg.h"
 
 static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp,
 		struct sde_mdss_cfg *m,
@@ -123,6 +124,9 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx,
 	c->cap = cfg;
 	_setup_dspp_ops(c, c->cap->features);
 
+	sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+			c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
 	return c;
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.c b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
index 8634981..c17844d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_intf.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
@@ -13,6 +13,7 @@
 #include "sde_hwio.h"
 #include "sde_hw_catalog.h"
 #include "sde_hw_intf.h"
+#include "sde_dbg.h"
 
 #define INTF_TIMING_ENGINE_EN           0x000
 #define INTF_CONFIG                     0x004
@@ -330,9 +331,9 @@ struct sde_hw_intf *sde_hw_intf_init(enum sde_intf idx,
 	c->mdss = m;
 	_setup_intf_ops(&c->ops, c->cap->features);
 
-	/*
-	 * Perform any default initialization for the intf
-	 */
+	sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+			c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
 	return c;
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
index 2197e76..520c7b1 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
@@ -15,6 +15,7 @@
 #include "sde_hwio.h"
 #include "sde_hw_lm.h"
 #include "sde_hw_mdss.h"
+#include "sde_dbg.h"
 
 #define LM_OP_MODE                        0x00
 #define LM_OUT_SIZE                       0x04
@@ -264,9 +265,9 @@ struct sde_hw_mixer *sde_hw_lm_init(enum sde_lm idx,
 	c->cap = cfg;
 	_setup_mixer_ops(m, &c->ops, c->cap->features);
 
-	/*
-	 * Perform any default initialization for the sspp blocks
-	 */
+	sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+			c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
 	return c;
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index a63554e..c500966 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -18,6 +18,8 @@
 
 #include "msm_drv.h"
 
+#define SDE_DBG_NAME			"sde"
+
 #define SDE_NONE                        0
 
 #ifndef SDE_CSC_MATRIX_COEFF_SIZE
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
index 7f5f2c3..8488d03 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
@@ -14,6 +14,7 @@
 #include "sde_hwio.h"
 #include "sde_hw_catalog.h"
 #include "sde_hw_pingpong.h"
+#include "sde_dbg.h"
 
 #define PP_TEAR_CHECK_EN                0x000
 #define PP_SYNC_CONFIG_VSYNC            0x004
@@ -160,6 +161,9 @@ struct sde_hw_pingpong *sde_hw_pingpong_init(enum sde_pingpong idx,
 	c->pingpong_hw_cap = cfg;
 	_setup_pingpong_ops(&c->ops, c->pingpong_hw_cap->features);
 
+	sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+			c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
 	return c;
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index 1bfee6c..c7f6809 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -15,6 +15,7 @@
 #include "sde_hw_lm.h"
 #include "sde_hw_sspp.h"
 #include "sde_hw_color_processing.h"
+#include "sde_dbg.h"
 
 #define SDE_FETCH_CONFIG_RESET_VALUE   0x00000087
 
@@ -1091,6 +1092,18 @@ struct sde_hw_pipe *sde_hw_sspp_init(enum sde_sspp idx,
 	_setup_layer_ops(hw_pipe, hw_pipe->cap->features);
 	hw_pipe->highest_bank_bit = catalog->mdp[0].highest_bank_bit;
 
+	sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name,
+			hw_pipe->hw.blk_off,
+			hw_pipe->hw.blk_off + hw_pipe->hw.length,
+			hw_pipe->hw.xin_id);
+
+	if (cfg->sblk->scaler_blk.len)
+		sde_dbg_reg_register_dump_range(SDE_DBG_NAME,
+			cfg->sblk->scaler_blk.name,
+			cfg->sblk->scaler_blk.base,
+			cfg->sblk->scaler_blk.base + cfg->sblk->scaler_blk.len,
+			hw_pipe->hw.xin_id);
+
 	return hw_pipe;
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index 90281ca..c4b4592 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -13,6 +13,7 @@
 #include "sde_hwio.h"
 #include "sde_hw_catalog.h"
 #include "sde_hw_top.h"
+#include "sde_dbg.h"
 
 #define SSPP_SPARE                        0x28
 
@@ -255,9 +256,9 @@ struct sde_hw_mdp *sde_hw_mdptop_init(enum sde_mdp idx,
 	mdp->cap = cfg;
 	_setup_mdp_ops(&mdp->ops, mdp->cap->features);
 
-	/*
-	 * Perform any default initialization for the intf
-	 */
+	sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name,
+			mdp->hw.blk_off, mdp->hw.blk_off + mdp->hw.length,
+			mdp->hw.xin_id);
 
 	return mdp;
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
index c548b21..e9f54d0 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
@@ -13,6 +13,7 @@
 #include "sde_hwio.h"
 #include "sde_hw_catalog.h"
 #include "sde_hw_vbif.h"
+#include "sde_dbg.h"
 
 #define VBIF_VERSION			0x0000
 #define VBIF_CLK_FORCE_CTRL0		0x0008
@@ -157,6 +158,9 @@ struct sde_hw_vbif *sde_hw_vbif_init(enum sde_vbif idx,
 	c->cap = cfg;
 	_setup_vbif_ops(&c->ops, c->cap->features);
 
+	sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+			c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
 	return c;
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_wb.c b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
index afca715..320b05f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
@@ -15,6 +15,7 @@
 #include "sde_hw_catalog.h"
 #include "sde_hw_wb.h"
 #include "sde_formats.h"
+#include "sde_dbg.h"
 
 #define WB_DST_FORMAT			0x000
 #define WB_DST_OP_MODE			0x004
@@ -216,6 +217,9 @@ struct sde_hw_wb *sde_hw_wb_init(enum sde_wb idx,
 	c->highest_bank_bit = m->mdp[0].highest_bank_bit;
 	c->hw_mdp = hw_mdp;
 
+	sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+			c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
 	return c;
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index cedb3b7..3b998fb 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -1018,6 +1018,44 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms)
 	return ret;
 }
 
+static void __iomem *_sde_kms_ioremap(struct platform_device *pdev,
+		const char *name, unsigned long *out_size)
+{
+	struct resource *res;
+	unsigned long size;
+	void __iomem *ptr;
+
+	if (out_size)
+		*out_size = 0;
+
+	if (name)
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+	else
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	if (!res) {
+		/* availability depends on platform */
+		SDE_DEBUG("failed to get memory resource: %s\n", name);
+		return ERR_PTR(-EINVAL);
+	}
+
+	size = resource_size(res);
+
+	ptr = devm_ioremap_nocache(&pdev->dev, res->start, size);
+	if (!ptr) {
+		SDE_ERROR("failed to ioremap: %s\n", name);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	SDE_DEBUG("IO:region %s %p %08lx\n", name, ptr, size);
+
+	if (out_size)
+		*out_size = size;
+
+	return ptr;
+}
+
+
 static int sde_kms_hw_init(struct msm_kms *kms)
 {
 	struct sde_kms *sde_kms;
@@ -1043,7 +1081,8 @@ static int sde_kms_hw_init(struct msm_kms *kms)
 		goto end;
 	}
 
-	sde_kms->mmio = msm_ioremap(dev->platformdev, "mdp_phys", "SDE");
+	sde_kms->mmio = _sde_kms_ioremap(dev->platformdev, "mdp_phys",
+			&sde_kms->mmio_len);
 	if (IS_ERR(sde_kms->mmio)) {
 		rc = PTR_ERR(sde_kms->mmio);
 		SDE_ERROR("mdp register memory map failed: %d\n", rc);
@@ -1052,8 +1091,13 @@ static int sde_kms_hw_init(struct msm_kms *kms)
 	}
 	DRM_INFO("mapped mdp address space @%p\n", sde_kms->mmio);
 
-	sde_kms->vbif[VBIF_RT] = msm_ioremap(dev->platformdev,
-			"vbif_phys", "VBIF");
+	rc = sde_dbg_reg_register_base(SDE_DBG_NAME, sde_kms->mmio,
+			sde_kms->mmio_len);
+	if (rc)
+		SDE_ERROR("dbg base register kms failed: %d\n", rc);
+
+	sde_kms->vbif[VBIF_RT] = _sde_kms_ioremap(dev->platformdev, "vbif_phys",
+			&sde_kms->vbif_len[VBIF_RT]);
 	if (IS_ERR(sde_kms->vbif[VBIF_RT])) {
 		rc = PTR_ERR(sde_kms->vbif[VBIF_RT]);
 		SDE_ERROR("vbif register memory map failed: %d\n", rc);
@@ -1061,18 +1105,38 @@ static int sde_kms_hw_init(struct msm_kms *kms)
 		goto error;
 	}
 
-	sde_kms->vbif[VBIF_NRT] = msm_ioremap(dev->platformdev,
-			"vbif_nrt_phys", "VBIF_NRT");
+	rc = sde_dbg_reg_register_base("vbif_rt", sde_kms->vbif[VBIF_RT],
+				sde_kms->vbif_len[VBIF_RT]);
+	if (rc)
+		SDE_ERROR("dbg base register vbif_rt failed: %d\n", rc);
+
+	sde_kms->vbif[VBIF_NRT] = _sde_kms_ioremap(dev->platformdev,
+			"vbif_nrt_phys", &sde_kms->vbif_len[VBIF_NRT]);
 	if (IS_ERR(sde_kms->vbif[VBIF_NRT])) {
 		sde_kms->vbif[VBIF_NRT] = NULL;
 		SDE_DEBUG("VBIF NRT is not defined");
+	} else {
+		rc = sde_dbg_reg_register_base("vbif_nrt",
+				sde_kms->vbif[VBIF_NRT],
+				sde_kms->vbif_len[VBIF_NRT]);
+		if (rc)
+			SDE_ERROR("dbg base register vbif_nrt failed: %d\n",
+					rc);
 	}
 
-	sde_kms->reg_dma = msm_ioremap(dev->platformdev, "regdma_phys",
-			"REG_DMA");
+	sde_kms->reg_dma = _sde_kms_ioremap(dev->platformdev, "regdma_phys",
+		&sde_kms->reg_dma_len);
 	if (IS_ERR(sde_kms->reg_dma)) {
 		sde_kms->reg_dma = NULL;
 		SDE_DEBUG("REG_DMA is not defined");
+	} else {
+		rc =  sde_dbg_reg_register_base("vbif_nrt",
+				sde_kms->reg_dma,
+				sde_kms->reg_dma_len);
+		if (rc)
+			SDE_ERROR("dbg base register reg_dma failed: %d\n",
+					rc);
+
 	}
 
 	sde_kms->core_client = sde_power_client_create(&priv->phandle, "core");
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index abfbf310..bf1c12f 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -133,6 +133,7 @@ struct sde_kms {
 
 	/* io/register spaces: */
 	void __iomem *mmio, *vbif[VBIF_MAX], *reg_dma;
+	unsigned long mmio_len, vbif_len[VBIF_MAX], reg_dma_len;
 
 	struct regulator *vdd;
 	struct regulator *mmagic;
diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c
new file mode 100644
index 0000000..7bf18b5
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_dbg.c
@@ -0,0 +1,848 @@
+/* Copyright (c) 2009-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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
+
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/ktime.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/dma-buf.h>
+#include <linux/slab.h>
+
+#include "sde_dbg.h"
+
+#define SDE_DBG_BASE_MAX		10
+
+#define DEFAULT_PANIC		1
+#define DEFAULT_REGDUMP		SDE_DBG_DUMP_IN_MEM
+#define DEFAULT_BASE_REG_CNT	0x100
+#define GROUP_BYTES		4
+#define ROW_BYTES		16
+#define RANGE_NAME_LEN		40
+#define REG_BASE_NAME_LEN	80
+
+/**
+ * struct sde_dbg_reg_offset - tracking for start and end of region
+ * @start: start offset
+ * @start: end offset
+ */
+struct sde_dbg_reg_offset {
+	u32 start;
+	u32 end;
+};
+
+/**
+ * struct sde_dbg_reg_range - register dumping named sub-range
+ * @head: head of this node
+ * @reg_dump: address for the mem dump
+ * @range_name: name of this range
+ * @offset: offsets for range to dump
+ * @xin_id: client xin id
+ */
+struct sde_dbg_reg_range {
+	struct list_head head;
+	u32 *reg_dump;
+	char range_name[RANGE_NAME_LEN];
+	struct sde_dbg_reg_offset offset;
+	uint32_t xin_id;
+};
+
+/**
+ * struct sde_dbg_reg_base - register region base.
+ *	may sub-ranges: sub-ranges are used for dumping
+ *	or may not have sub-ranges: dumping is base -> max_offset
+ * @reg_base_head: head of this node
+ * @sub_range_list: head to the list with dump ranges
+ * @name: register base name
+ * @base: base pointer
+ * @off: cached offset of region for manual register dumping
+ * @cnt: cached range of region for manual register dumping
+ * @max_offset: length of region
+ * @buf: buffer used for manual register dumping
+ * @buf_len:  buffer length used for manual register dumping
+ * @reg_dump: address for the mem dump if no ranges used
+ */
+struct sde_dbg_reg_base {
+	struct list_head reg_base_head;
+	struct list_head sub_range_list;
+	char name[REG_BASE_NAME_LEN];
+	void __iomem *base;
+	size_t off;
+	size_t cnt;
+	size_t max_offset;
+	char *buf;
+	size_t buf_len;
+	u32 *reg_dump;
+};
+
+/**
+ * struct sde_dbg_base - global sde debug base structure
+ * @evtlog: event log instance
+ * @reg_base_list: list of register dumping regions
+ * @root: base debugfs root
+ * @dev: device pointer
+ * @power_ctrl: callback structure for enabling power for reading hw registers
+ * @req_dump_blks: list of blocks requested for dumping
+ * @panic_on_err: whether to kernel panic after triggering dump via debugfs
+ * @dump_work: work struct for deferring register dump work to separate thread
+ * @work_panic: panic after dump if internal user passed "panic" special region
+ * @enable_reg_dump: whether to dump registers into memory, kernel log, or both
+ */
+static struct sde_dbg_base {
+	struct sde_dbg_evtlog *evtlog;
+	struct list_head reg_base_list;
+	struct dentry *root;
+	struct device *dev;
+	struct sde_dbg_power_ctrl power_ctrl;
+
+	struct sde_dbg_reg_base *req_dump_blks[SDE_DBG_BASE_MAX];
+
+	u32 panic_on_err;
+	struct work_struct dump_work;
+	bool work_panic;
+	u32 enable_reg_dump;
+} sde_dbg_base;
+
+/* sde_dbg_base_evtlog - global pointer to main sde event log for macro use */
+struct sde_dbg_evtlog *sde_dbg_base_evtlog;
+
+/**
+ * _sde_dbg_enable_power - use callback to turn power on for hw register access
+ * @enable: whether to turn power on or off
+ */
+static inline void _sde_dbg_enable_power(int enable)
+{
+	if (!sde_dbg_base.power_ctrl.enable_fn)
+		return;
+	sde_dbg_base.power_ctrl.enable_fn(
+			sde_dbg_base.power_ctrl.handle,
+			sde_dbg_base.power_ctrl.client,
+			enable);
+}
+
+/**
+ * _sde_dump_reg - helper function for dumping rotator register set content
+ * @dump_name: register set name
+ * @reg_dump_flag: dumping flag controlling in-log/memory dump location
+ * @addr: starting address offset for dumping
+ * @len_bytes: range of the register set
+ * @dump_mem: output buffer for memory dump location option
+ * @from_isr: whether being called from isr context
+ */
+static void _sde_dump_reg(const char *dump_name, u32 reg_dump_flag, char *addr,
+		size_t len_bytes, u32 **dump_mem, bool from_isr)
+{
+	u32 in_log, in_mem, len_align_16, len_bytes_aligned;
+	u32 *dump_addr = NULL;
+	char *end_addr;
+	int i;
+
+	in_log = (reg_dump_flag & SDE_DBG_DUMP_IN_LOG);
+	in_mem = (reg_dump_flag & SDE_DBG_DUMP_IN_MEM);
+
+	pr_debug("reg_dump_flag=%d in_log=%d in_mem=%d\n",
+		reg_dump_flag, in_log, in_mem);
+
+	len_align_16 = (len_bytes + 15) / 16;
+	len_bytes_aligned = len_align_16 * 16;
+	end_addr = addr + len_bytes;
+
+	if (in_mem) {
+		if (dump_mem && !(*dump_mem)) {
+			phys_addr_t phys = 0;
+			*dump_mem = dma_alloc_coherent(sde_dbg_base.dev,
+					len_bytes_aligned, &phys, GFP_KERNEL);
+		}
+
+		if (dump_mem && *dump_mem) {
+			dump_addr = *dump_mem;
+			pr_info("%s: start_addr:0x%pK end_addr:0x%pK reg_addr=0x%pK\n",
+				dump_name, dump_addr,
+				dump_addr + len_bytes_aligned, addr);
+		} else {
+			in_mem = 0;
+			pr_err("dump_mem: kzalloc fails!\n");
+		}
+	}
+
+	if (!from_isr)
+		_sde_dbg_enable_power(true);
+
+	for (i = 0; i < len_align_16; i++) {
+		u32 x0, x4, x8, xc;
+
+		x0 = (addr < end_addr) ? readl_relaxed(addr + 0x0) : 0;
+		x4 = (addr + 0x4 < end_addr) ? readl_relaxed(addr + 0x4) : 0;
+		x8 = (addr + 0x8 < end_addr) ? readl_relaxed(addr + 0x8) : 0;
+		xc = (addr + 0xc < end_addr) ? readl_relaxed(addr + 0xc) : 0;
+
+		if (in_log)
+			pr_info("%pK : %08x %08x %08x %08x\n", addr, x0, x4, x8,
+				xc);
+
+		if (dump_addr) {
+			dump_addr[i * 4] = x0;
+			dump_addr[i * 4 + 1] = x4;
+			dump_addr[i * 4 + 2] = x8;
+			dump_addr[i * 4 + 3] = xc;
+		}
+
+		addr += 16;
+	}
+
+	if (!from_isr)
+		_sde_dbg_enable_power(false);
+}
+
+/**
+ * _sde_dbg_get_dump_range - helper to retrieve dump length for a range node
+ * @range_node: range node to dump
+ * @max_offset: max offset of the register base
+ * @Return: length
+ */
+static u32 _sde_dbg_get_dump_range(struct sde_dbg_reg_offset *range_node,
+		size_t max_offset)
+{
+	u32 length = 0;
+
+	if ((range_node->start > range_node->end) ||
+		(range_node->end > max_offset) || (range_node->start == 0
+		&& range_node->end == 0)) {
+		length = max_offset;
+	} else {
+		length = range_node->end - range_node->start;
+	}
+
+	return length;
+}
+
+/**
+ * _sde_dump_reg_by_ranges - dump ranges or full range of the register blk base
+ * @dbg: register blk base structure
+ * @reg_dump_flag: dump target, memory, kernel log, or both
+ */
+static void _sde_dump_reg_by_ranges(struct sde_dbg_reg_base *dbg,
+	u32 reg_dump_flag)
+{
+	char *addr;
+	size_t len;
+	struct sde_dbg_reg_range *range_node;
+
+	if (!dbg || !dbg->base) {
+		pr_err("dbg base is null!\n");
+		return;
+	}
+
+	pr_info("%s:=========%s DUMP=========\n", __func__, dbg->name);
+
+	/* If there is a list to dump the registers by ranges, use the ranges */
+	if (!list_empty(&dbg->sub_range_list)) {
+		list_for_each_entry(range_node, &dbg->sub_range_list, head) {
+			len = _sde_dbg_get_dump_range(&range_node->offset,
+				dbg->max_offset);
+			addr = dbg->base + range_node->offset.start;
+			pr_debug("%s: range_base=0x%pK start=0x%x end=0x%x\n",
+				range_node->range_name,
+				addr, range_node->offset.start,
+				range_node->offset.end);
+
+			_sde_dump_reg((const char *)range_node->range_name,
+				reg_dump_flag, addr, len, &range_node->reg_dump,
+				false);
+		}
+	} else {
+		/* If there is no list to dump ranges, dump all registers */
+		pr_info("Ranges not found, will dump full registers");
+		pr_info("base:0x%pK len:0x%zx\n", dbg->base, dbg->max_offset);
+		addr = dbg->base;
+		len = dbg->max_offset;
+		_sde_dump_reg((const char *)dbg->name, reg_dump_flag, addr,
+			len, &dbg->reg_dump, false);
+	}
+}
+
+/**
+ * _sde_dump_reg_by_blk - dump a named register base region
+ * @blk_name: register blk name
+ */
+static void _sde_dump_reg_by_blk(const char *blk_name)
+{
+	struct sde_dbg_base *dbg_base = &sde_dbg_base;
+	struct sde_dbg_reg_base *blk_base;
+
+	if (!dbg_base)
+		return;
+
+	list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head) {
+		if (strlen(blk_base->name) &&
+			!strcmp(blk_base->name, blk_name)) {
+			_sde_dump_reg_by_ranges(blk_base,
+				dbg_base->enable_reg_dump);
+			break;
+		}
+	}
+}
+
+/**
+ * _sde_dump_reg_all - dump all register regions
+ */
+static void _sde_dump_reg_all(void)
+{
+	struct sde_dbg_base *dbg_base = &sde_dbg_base;
+	struct sde_dbg_reg_base *blk_base;
+
+	if (!dbg_base)
+		return;
+
+	list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head)
+		if (strlen(blk_base->name))
+			_sde_dump_reg_by_blk(blk_base->name);
+}
+
+/**
+ * _sde_dump_get_blk_addr - retrieve register block address by name
+ * @blk_name: register blk name
+ * @Return: register blk base, or NULL
+ */
+static struct sde_dbg_reg_base *_sde_dump_get_blk_addr(const char *blk_name)
+{
+	struct sde_dbg_base *dbg_base = &sde_dbg_base;
+	struct sde_dbg_reg_base *blk_base;
+
+	list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head)
+		if (strlen(blk_base->name) && !strcmp(blk_base->name, blk_name))
+			return blk_base;
+
+	return NULL;
+}
+
+/**
+ * _sde_dump_array - dump array of register bases
+ * @blk_arr: array of register base pointers
+ * @len: length of blk_arr
+ * @dead: whether to trigger a panic after dumping
+ * @name: string indicating origin of dump
+ */
+static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[],
+	u32 len, bool dead, const char *name)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (blk_arr[i] != NULL)
+			_sde_dump_reg_by_ranges(blk_arr[i],
+				sde_dbg_base.enable_reg_dump);
+	}
+
+	sde_evtlog_dump_all(sde_dbg_base.evtlog);
+
+	if (dead && sde_dbg_base.panic_on_err)
+		panic(name);
+}
+
+/**
+ * _sde_dump_work - deferred dump work function
+ * @work: work structure
+ */
+static void _sde_dump_work(struct work_struct *work)
+{
+	_sde_dump_array(sde_dbg_base.req_dump_blks,
+		ARRAY_SIZE(sde_dbg_base.req_dump_blks),
+		sde_dbg_base.work_panic, "evtlog_workitem");
+}
+
+void sde_dbg_dump(bool queue_work, const char *name, ...)
+{
+	int i, index = 0;
+	bool dead = false;
+	va_list args;
+	char *blk_name = NULL;
+	struct sde_dbg_reg_base *blk_base = NULL;
+	struct sde_dbg_reg_base **blk_arr;
+	u32 blk_len;
+
+	if (!sde_evtlog_is_enabled(sde_dbg_base.evtlog, SDE_EVTLOG_DEFAULT))
+		return;
+
+	if (queue_work && work_pending(&sde_dbg_base.dump_work))
+		return;
+
+	blk_arr = &sde_dbg_base.req_dump_blks[0];
+	blk_len = ARRAY_SIZE(sde_dbg_base.req_dump_blks);
+
+	memset(sde_dbg_base.req_dump_blks, 0,
+			sizeof(sde_dbg_base.req_dump_blks));
+
+	va_start(args, name);
+	for (i = 0; i < SDE_EVTLOG_MAX_DATA; i++) {
+		blk_name = va_arg(args, char*);
+		if (IS_ERR_OR_NULL(blk_name))
+			break;
+
+		blk_base = _sde_dump_get_blk_addr(blk_name);
+		if (blk_base) {
+			if (index < blk_len) {
+				blk_arr[index] = blk_base;
+				index++;
+			} else {
+				pr_err("insufficient space to to dump %s\n",
+						blk_name);
+			}
+		}
+
+		if (!strcmp(blk_name, "panic"))
+			dead = true;
+	}
+	blk_name = va_arg(args, char*);
+	if (!IS_ERR_OR_NULL(blk_name))
+		pr_err("could not parse all dump arguments\n");
+	va_end(args);
+
+	if (queue_work) {
+		/* schedule work to dump later */
+		sde_dbg_base.work_panic = dead;
+		schedule_work(&sde_dbg_base.dump_work);
+	} else {
+		_sde_dump_array(blk_arr, blk_len, dead, name);
+	}
+}
+
+/*
+ * sde_dbg_debugfs_open - debugfs open handler for evtlog dump
+ * @inode: debugfs inode
+ * @file: file handle
+ */
+static int sde_dbg_debugfs_open(struct inode *inode, struct file *file)
+{
+	/* non-seekable */
+	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+/**
+ * sde_evtlog_dump_read - debugfs read handler for evtlog dump
+ * @file: file handler
+ * @buff: user buffer content for debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff,
+		size_t count, loff_t *ppos)
+{
+	ssize_t len = 0;
+	char evtlog_buf[SDE_EVTLOG_BUF_MAX];
+
+	len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf,
+			SDE_EVTLOG_BUF_MAX);
+	if (copy_to_user(buff, evtlog_buf, len))
+		return -EFAULT;
+	*ppos += len;
+
+	return len;
+}
+
+/**
+ * sde_evtlog_dump_write - debugfs write handler for evtlog dump
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_evtlog_dump_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	_sde_dump_reg_all();
+
+	sde_evtlog_dump_all(sde_dbg_base.evtlog);
+
+	if (sde_dbg_base.panic_on_err)
+		panic("sde");
+
+	return count;
+}
+
+static const struct file_operations sde_evtlog_fops = {
+	.open = sde_dbg_debugfs_open,
+	.read = sde_evtlog_dump_read,
+	.write = sde_evtlog_dump_write,
+};
+
+int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
+		struct sde_dbg_power_ctrl *power_ctrl)
+{
+	int i;
+
+	INIT_LIST_HEAD(&sde_dbg_base.reg_base_list);
+	sde_dbg_base.dev = dev;
+	sde_dbg_base.power_ctrl = *power_ctrl;
+
+
+	sde_dbg_base.evtlog = sde_evtlog_init();
+	if (IS_ERR_OR_NULL(sde_dbg_base.evtlog))
+		return PTR_ERR(sde_dbg_base.evtlog);
+
+	sde_dbg_base_evtlog = sde_dbg_base.evtlog;
+
+	sde_dbg_base.root = debugfs_create_dir("evt_dbg", debugfs_root);
+	if (IS_ERR_OR_NULL(sde_dbg_base.root)) {
+		pr_err("debugfs_create_dir fail, error %ld\n",
+		       PTR_ERR(sde_dbg_base.root));
+		sde_dbg_base.root = NULL;
+		return -ENODEV;
+	}
+
+	INIT_WORK(&sde_dbg_base.dump_work, _sde_dump_work);
+	sde_dbg_base.work_panic = false;
+
+	for (i = 0; i < SDE_EVTLOG_ENTRY; i++)
+		sde_dbg_base.evtlog->logs[i].counter = i;
+
+	debugfs_create_file("dump", 0644, sde_dbg_base.root, NULL,
+						&sde_evtlog_fops);
+	debugfs_create_u32("enable", 0644, sde_dbg_base.root,
+			    &(sde_dbg_base.evtlog->enable));
+	debugfs_create_u32("panic", 0644, sde_dbg_base.root,
+			    &sde_dbg_base.panic_on_err);
+	debugfs_create_u32("reg_dump", 0644, sde_dbg_base.root,
+			    &sde_dbg_base.enable_reg_dump);
+
+	sde_dbg_base.panic_on_err = DEFAULT_PANIC;
+	sde_dbg_base.enable_reg_dump = DEFAULT_REGDUMP;
+
+	pr_info("evtlog_status: enable:%d, panic:%d, dump:%d\n",
+		sde_dbg_base.evtlog->enable, sde_dbg_base.panic_on_err,
+		sde_dbg_base.enable_reg_dump);
+
+	return 0;
+}
+
+/**
+ * sde_dbg_destroy - destroy sde debug facilities
+ */
+void sde_dbg_destroy(void)
+{
+	debugfs_remove_recursive(sde_dbg_base.root);
+	sde_dbg_base.root = 0;
+
+	sde_dbg_base_evtlog = NULL;
+	sde_evtlog_destroy(sde_dbg_base.evtlog);
+	sde_dbg_base.evtlog = NULL;
+}
+
+/**
+ * sde_dbg_reg_base_release - release allocated reg dump file private data
+ * @inode: debugfs inode
+ * @file: file handle
+ * @Return: 0 on success
+ */
+static int sde_dbg_reg_base_release(struct inode *inode, struct file *file)
+{
+	struct sde_dbg_reg_base *dbg = file->private_data;
+
+	if (dbg && dbg->buf) {
+		kfree(dbg->buf);
+		dbg->buf_len = 0;
+		dbg->buf = NULL;
+	}
+	return 0;
+}
+
+
+/**
+ * sde_dbg_reg_base_offset_write - set new offset and len to debugfs reg base
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_offset_write(struct file *file,
+		const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct sde_dbg_reg_base *dbg = file->private_data;
+	u32 off = 0;
+	u32 cnt = DEFAULT_BASE_REG_CNT;
+	char buf[24];
+
+	if (!dbg)
+		return -ENODEV;
+
+	if (count >= sizeof(buf))
+		return -EFAULT;
+
+	if (copy_from_user(buf, user_buf, count))
+		return -EFAULT;
+
+	buf[count] = 0;	/* end of string */
+
+	if (sscanf(buf, "%5x %x", &off, &cnt) != 2)
+		return -EFAULT;
+
+	if (off > dbg->max_offset)
+		return -EINVAL;
+
+	if (cnt > (dbg->max_offset - off))
+		cnt = dbg->max_offset - off;
+
+	dbg->off = off;
+	dbg->cnt = cnt;
+
+	pr_debug("offset=%x cnt=%x\n", off, cnt);
+
+	return count;
+}
+
+/**
+ * sde_dbg_reg_base_offset_read - read current offset and len of register base
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_offset_read(struct file *file,
+			char __user *buff, size_t count, loff_t *ppos)
+{
+	struct sde_dbg_reg_base *dbg = file->private_data;
+	int len = 0;
+	char buf[24] = {'\0'};
+
+	if (!dbg)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;	/* the end */
+
+	len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
+	if (len < 0 || len >= sizeof(buf))
+		return 0;
+
+	if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+		return -EFAULT;
+
+	*ppos += len;	/* increase offset */
+
+	return len;
+}
+
+/**
+ * sde_dbg_reg_base_reg_write - write to reg base hw at offset a given value
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_reg_write(struct file *file,
+		const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct sde_dbg_reg_base *dbg = file->private_data;
+	size_t off;
+	u32 data, cnt;
+	char buf[24];
+
+	if (!dbg)
+		return -ENODEV;
+
+	if (count >= sizeof(buf))
+		return -EFAULT;
+
+	if (copy_from_user(buf, user_buf, count))
+		return -EFAULT;
+
+	buf[count] = 0;	/* end of string */
+
+	cnt = sscanf(buf, "%zx %x", &off, &data);
+
+	if (cnt < 2)
+		return -EFAULT;
+
+	if (off >= dbg->max_offset)
+		return -EFAULT;
+
+	_sde_dbg_enable_power(true);
+
+	writel_relaxed(data, dbg->base + off);
+
+	_sde_dbg_enable_power(false);
+
+	pr_debug("addr=%zx data=%x\n", off, data);
+
+	return count;
+}
+
+/**
+ * sde_dbg_reg_base_reg_read - read len from reg base hw at current offset
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_reg_read(struct file *file,
+			char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct sde_dbg_reg_base *dbg = file->private_data;
+	size_t len;
+
+	if (!dbg) {
+		pr_err("invalid handle\n");
+		return -ENODEV;
+	}
+
+	if (!dbg->buf) {
+		char dump_buf[64];
+		char *ptr;
+		int cnt, tot;
+
+		dbg->buf_len = sizeof(dump_buf) *
+			DIV_ROUND_UP(dbg->cnt, ROW_BYTES);
+		dbg->buf = kzalloc(dbg->buf_len, GFP_KERNEL);
+
+		if (!dbg->buf)
+			return -ENOMEM;
+
+		ptr = dbg->base + dbg->off;
+		tot = 0;
+
+		_sde_dbg_enable_power(true);
+
+		for (cnt = dbg->cnt; cnt > 0; cnt -= ROW_BYTES) {
+			hex_dump_to_buffer(ptr, min(cnt, ROW_BYTES),
+					   ROW_BYTES, GROUP_BYTES, dump_buf,
+					   sizeof(dump_buf), false);
+			len = scnprintf(dbg->buf + tot, dbg->buf_len - tot,
+					"0x%08x: %s\n",
+					((int) (unsigned long) ptr) -
+					((int) (unsigned long) dbg->base),
+					dump_buf);
+
+			ptr += ROW_BYTES;
+			tot += len;
+			if (tot >= dbg->buf_len)
+				break;
+		}
+
+		_sde_dbg_enable_power(false);
+
+		dbg->buf_len = tot;
+	}
+
+	if (*ppos >= dbg->buf_len)
+		return 0; /* done reading */
+
+	len = min(count, dbg->buf_len - (size_t) *ppos);
+	if (copy_to_user(user_buf, dbg->buf + *ppos, len)) {
+		pr_err("failed to copy to user\n");
+		return -EFAULT;
+	}
+
+	*ppos += len; /* increase offset */
+
+	return len;
+}
+
+static const struct file_operations sde_off_fops = {
+	.open = sde_dbg_debugfs_open,
+	.release = sde_dbg_reg_base_release,
+	.read = sde_dbg_reg_base_offset_read,
+	.write = sde_dbg_reg_base_offset_write,
+};
+
+static const struct file_operations sde_reg_fops = {
+	.open = sde_dbg_debugfs_open,
+	.release = sde_dbg_reg_base_release,
+	.read = sde_dbg_reg_base_reg_read,
+	.write = sde_dbg_reg_base_reg_write,
+};
+
+int sde_dbg_reg_register_base(const char *name, void __iomem *base,
+		size_t max_offset)
+{
+	struct sde_dbg_base *dbg_base = &sde_dbg_base;
+	struct sde_dbg_reg_base *reg_base;
+	struct dentry *ent_off, *ent_reg;
+	char dn[80] = "";
+	int prefix_len = 0;
+
+	reg_base = kzalloc(sizeof(*reg_base), GFP_KERNEL);
+	if (!reg_base)
+		return -ENOMEM;
+
+	if (name)
+		strlcpy(reg_base->name, name, sizeof(reg_base->name));
+	reg_base->base = base;
+	reg_base->max_offset = max_offset;
+	reg_base->off = 0;
+	reg_base->cnt = DEFAULT_BASE_REG_CNT;
+	reg_base->reg_dump = NULL;
+
+	if (name)
+		prefix_len = snprintf(dn, sizeof(dn), "%s_", name);
+	strlcpy(dn + prefix_len, "off", sizeof(dn) - prefix_len);
+	ent_off = debugfs_create_file(dn, 0644, dbg_base->root, reg_base,
+			&sde_off_fops);
+	if (IS_ERR_OR_NULL(ent_off)) {
+		pr_err("debugfs_create_file: offset fail\n");
+		goto off_fail;
+	}
+
+	strlcpy(dn + prefix_len, "reg", sizeof(dn) - prefix_len);
+	ent_reg = debugfs_create_file(dn, 0644, dbg_base->root, reg_base,
+			&sde_reg_fops);
+	if (IS_ERR_OR_NULL(ent_reg)) {
+		pr_err("debugfs_create_file: reg fail\n");
+		goto reg_fail;
+	}
+
+	/* Initialize list to make sure check for null list will be valid */
+	INIT_LIST_HEAD(&reg_base->sub_range_list);
+
+	pr_debug("%s base: %pK max_offset 0x%zX\n", reg_base->name,
+			reg_base->base, reg_base->max_offset);
+
+	list_add(&reg_base->reg_base_head, &dbg_base->reg_base_list);
+
+	return 0;
+reg_fail:
+	debugfs_remove(ent_off);
+off_fail:
+	kfree(reg_base);
+	return -ENODEV;
+}
+
+void sde_dbg_reg_register_dump_range(const char *base_name,
+		const char *range_name, u32 offset_start, u32 offset_end,
+		uint32_t xin_id)
+{
+	struct sde_dbg_reg_base *reg_base;
+	struct sde_dbg_reg_range *range;
+
+	reg_base = _sde_dump_get_blk_addr(base_name);
+	if (!reg_base) {
+		pr_err("error: for range %s unable to locate base %s\n",
+				range_name, base_name);
+		return;
+	}
+
+	range = kzalloc(sizeof(*range), GFP_KERNEL);
+	if (!range)
+		return;
+
+	strlcpy(range->range_name, range_name, sizeof(range->range_name));
+	range->offset.start = offset_start;
+	range->offset.end = offset_end;
+	range->xin_id = xin_id;
+	list_add_tail(&range->head, &reg_base->sub_range_list);
+
+	pr_debug("%s start: 0x%X end: 0x%X\n", range->range_name,
+			range->offset.start, range->offset.end);
+}
diff --git a/drivers/gpu/drm/msm/sde_dbg.h b/drivers/gpu/drm/msm/sde_dbg.h
index 271c41f..52d80ca 100644
--- a/drivers/gpu/drm/msm/sde_dbg.h
+++ b/drivers/gpu/drm/msm/sde_dbg.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
@@ -29,34 +29,269 @@ enum sde_dbg_evtlog_flag {
 	SDE_EVTLOG_ALL = BIT(7)
 };
 
+enum sde_dbg_dump_flag {
+	SDE_DBG_DUMP_IN_LOG = BIT(0),
+	SDE_DBG_DUMP_IN_MEM = BIT(1),
+};
+
+#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG
+#define SDE_EVTLOG_DEFAULT_ENABLE 1
+#else
+#define SDE_EVTLOG_DEFAULT_ENABLE 0
+#endif
+
+/*
+ * evtlog will print this number of entries when it is called through
+ * sysfs node or panic. This prevents kernel log from evtlog message
+ * flood.
+ */
+#define SDE_EVTLOG_PRINT_ENTRY	256
+
+/*
+ * evtlog keeps this number of entries in memory for debug purpose. This
+ * number must be greater than print entry to prevent out of bound evtlog
+ * entry array access.
+ */
+#define SDE_EVTLOG_ENTRY	(SDE_EVTLOG_PRINT_ENTRY * 4)
+#define SDE_EVTLOG_MAX_DATA 15
+#define SDE_EVTLOG_BUF_MAX 512
+#define SDE_EVTLOG_BUF_ALIGN 32
+
+struct sde_dbg_power_ctrl {
+	void *handle;
+	void *client;
+	int (*enable_fn)(void *handle, void *client, bool enable);
+};
+
+struct sde_dbg_evtlog_log {
+	u32 counter;
+	s64 time;
+	const char *name;
+	int line;
+	u32 data[SDE_EVTLOG_MAX_DATA];
+	u32 data_cnt;
+	int pid;
+};
+
+struct sde_dbg_evtlog {
+	struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY];
+	u32 first;
+	u32 last;
+	u32 curr;
+	u32 next;
+	u32 enable;
+	spinlock_t spin_lock;
+};
+
+extern struct sde_dbg_evtlog *sde_dbg_base_evtlog;
+
 /**
- * SDE_EVT32 - Write an list of 32bit values as an event into the event log
+ * SDE_EVT32 - Write a list of 32bit values to the event log, default area
  * ... - variable arguments
  */
-#define SDE_EVT32(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_DEFAULT, \
-		##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER)
-#define SDE_EVT32_IRQ(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_IRQ, \
-		##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER)
+#define SDE_EVT32(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
+		__LINE__, SDE_EVTLOG_DEFAULT, ##__VA_ARGS__, \
+		SDE_EVTLOG_DATA_LIMITER)
 
-#define SDE_DBG_DUMP(...)	\
-	sde_dbg_dump(false, __func__, ##__VA_ARGS__, \
+/**
+ * SDE_EVT32_IRQ - Write a list of 32bit values to the event log, IRQ area
+ * ... - variable arguments
+ */
+#define SDE_EVT32_IRQ(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
+		__LINE__, SDE_EVTLOG_IRQ, ##__VA_ARGS__, \
+		SDE_EVTLOG_DATA_LIMITER)
+
+/**
+ * SDE_DBG_DUMP - trigger dumping of all sde_dbg facilities
+ * @va_args:	list of named register dump ranges and regions to dump, as
+ *		registered previously through sde_dbg_reg_register_base and
+ *		sde_dbg_reg_register_dump_range.
+ *		Including the special name "panic" will trigger a panic after
+ *		the dumping work has completed.
+ */
+#define SDE_DBG_DUMP(...) sde_dbg_dump(false, __func__, ##__VA_ARGS__, \
 		SDE_DBG_DUMP_DATA_LIMITER)
 
-#define SDE_DBG_DUMP_WQ(...)	\
-	sde_dbg_dump(true, __func__, ##__VA_ARGS__, \
+/**
+ * SDE_DBG_DUMP_WQ - trigger dumping of all sde_dbg facilities, queuing the work
+ * @va_args:	list of named register dump ranges and regions to dump, as
+ *		registered previously through sde_dbg_reg_register_base and
+ *		sde_dbg_reg_register_dump_range.
+ *		Including the special name "panic" will trigger a panic after
+ *		the dumping work has completed.
+ */
+#define SDE_DBG_DUMP_WQ(...) sde_dbg_dump(true, __func__, ##__VA_ARGS__, \
 		SDE_DBG_DUMP_DATA_LIMITER)
 
 #if defined(CONFIG_DEBUG_FS)
 
-int sde_evtlog_init(struct dentry *debugfs_root);
-void sde_evtlog_destroy(void);
-void sde_evtlog(const char *name, int line, int flag, ...);
-void sde_dbg_dump(bool queue, const char *name, ...);
+/**
+ * sde_evtlog_init - allocate a new event log object
+ * Returns:	evtlog or -ERROR
+ */
+struct sde_dbg_evtlog *sde_evtlog_init(void);
+
+/**
+ * sde_evtlog_destroy - destroy previously allocated event log
+ * @evtlog:	pointer to evtlog
+ * Returns:	none
+ */
+void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog);
+
+/**
+ * sde_evtlog_log - log an entry into the event log.
+ *	log collection may be enabled/disabled entirely via debugfs
+ *	log area collection may be filtered by user provided flags via debugfs.
+ * @evtlog:	pointer to evtlog
+ * @name:	function name of call site
+ * @line:	line number of call site
+ * @flag:	log area filter flag checked against user's debugfs request
+ * Returns:	none
+ */
+void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,
+		int flag, ...);
+
+/**
+ * sde_evtlog_dump_all - print all entries in event log to kernel log
+ * @evtlog:	pointer to evtlog
+ * Returns:	none
+ */
+void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog);
+
+/**
+ * sde_evtlog_is_enabled - check whether log collection is enabled for given
+ *	event log and log area flag
+ * @evtlog:	pointer to evtlog
+ * @flag:	log area filter flag
+ * Returns:	none
+ */
+bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag);
+
+/**
+ * sde_evtlog_dump_to_buffer - print content of event log to the given buffer
+ * @evtlog:		pointer to evtlog
+ * @evtlog_buf:		target buffer to print into
+ * @evtlog_buf_size:	size of target buffer
+ * Returns:		number of bytes written to buffer
+ */
+ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+		char *evtlog_buf, ssize_t evtlog_buf_size);
+
+/**
+ * sde_dbg_init - initialize global sde debug facilities: evtlog, regdump
+ * @debugfs_root:	debugfs root in which to create sde debug entries
+ * @dev:		device handle
+ * @power_ctrl:		power control callback structure for enabling clocks
+ *			during register dumping
+ * Returns:		0 or -ERROR
+ */
+int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
+		struct sde_dbg_power_ctrl *power_ctrl);
+
+/**
+ * sde_dbg_destroy - destroy the global sde debug facilities
+ * Returns:	none
+ */
+void sde_dbg_destroy(void);
+
+/**
+ * sde_dbg_dump - trigger dumping of all sde_dbg facilities
+ * @queue_work:	whether to queue the dumping work to the work_struct
+ * @name:	string indicating origin of dump
+ * @va_args:	list of named register dump ranges and regions to dump, as
+ *		registered previously through sde_dbg_reg_register_base and
+ *		sde_dbg_reg_register_dump_range.
+ *		Including the special name "panic" will trigger a panic after
+ *		the dumping work has completed.
+ * Returns:	none
+ */
+void sde_dbg_dump(bool queue_work, const char *name, ...);
+
+/**
+ * sde_dbg_reg_register_base - register a hw register address section for later
+ *	dumping. call this before calling sde_dbg_reg_register_dump_range
+ *	to be able to specify sub-ranges within the base hw range.
+ * @name:	name of base region
+ * @base:	base pointer of region
+ * @max_offset:	length of region
+ * Returns:	0 or -ERROR
+ */
+int sde_dbg_reg_register_base(const char *name, void __iomem *base,
+		size_t max_offset);
+
+/**
+ * sde_dbg_reg_register_dump_range - register a hw register sub-region for
+ *	later register dumping associated with base specified by
+ *	sde_dbg_reg_register_base
+ * @base_name:		name of base region
+ * @range_name:		name of sub-range within base region
+ * @offset_start:	sub-range's start offset from base's base pointer
+ * @offset_end:		sub-range's end offset from base's base pointer
+ * @xin_id:		xin id
+ * Returns:		none
+ */
+void sde_dbg_reg_register_dump_range(const char *base_name,
+		const char *range_name, u32 offset_start, u32 offset_end,
+		uint32_t xin_id);
+
 #else
-static inline int sde_evtlog_init(struct dentry *debugfs_root) { return 0; }
-static inline void sde_evtlog(const char *name, int line,  flag, ...) {}
-static inline void sde_evtlog_destroy(void) { }
-static inline void sde_dbg_dump(bool queue, const char *name, ...) {}
-#endif
+static inline struct sde_dbg_evtlog *sde_evtlog_init(void)
+{
+	return NULL;
+}
+
+static inline void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog)
+{
+}
+
+static inline void sde_evtlog_log(struct sde_dbg_evtlog *evtlog,
+		const char *name, int line, int flag, ...)
+{
+}
+
+static inline void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog)
+{
+}
+
+static inline bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog,
+		u32 flag)
+{
+	return false;
+}
+
+static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+		char *evtlog_buf, ssize_t evtlog_buf_size)
+{
+	return 0;
+}
+
+static inline int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
+		struct sde_dbg_power_ctrl *power_ctrl)
+{
+	return 0;
+}
+
+static inline void sde_dbg_destroy(void)
+{
+}
+
+static inline void sde_dbg_dump(bool queue_work, const char *name, ...)
+{
+}
+
+static inline int sde_dbg_reg_register_base(const char *name,
+		void __iomem *base, size_t max_offset)
+{
+	return 0;
+}
+
+static inline void sde_dbg_reg_register_dump_range(const char *base_name,
+		const char *range_name, u32 offset_start, u32 offset_end,
+		uint32_t xin_id)
+{
+}
+
+#endif /* defined(CONFIG_DEBUG_FS) */
+
 
 #endif /* SDE_DBG_H_ */
diff --git a/drivers/gpu/drm/msm/sde_dbg_evtlog.c b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
index 7283277..759bdab 100644
--- a/drivers/gpu/drm/msm/sde_dbg_evtlog.c
+++ b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
@@ -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,7 +10,7 @@
  * GNU General Public License for more details.
  */
 
-#define pr_fmt(fmt)	"sde_evtlog:[%s] " fmt, __func__
+#define pr_fmt(fmt)	"sde_dbg:[%s] " fmt, __func__
 
 #include <linux/delay.h>
 #include <linux/spinlock.h>
@@ -18,77 +18,36 @@
 #include <linux/debugfs.h>
 #include <linux/uaccess.h>
 #include <linux/dma-buf.h>
+#include <linux/slab.h>
 
 #include "sde_dbg.h"
 #include "sde_trace.h"
 
-#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG
-#define SDE_EVTLOG_DEFAULT_ENABLE 1
-#else
-#define SDE_EVTLOG_DEFAULT_ENABLE 0
-#endif
-
-#define SDE_DBG_DEFAULT_PANIC		1
-
-/*
- * evtlog will print this number of entries when it is called through
- * sysfs node or panic. This prevents kernel log from evtlog message
- * flood.
- */
-#define SDE_EVTLOG_PRINT_ENTRY	256
-
-/*
- * evtlog keeps this number of entries in memory for debug purpose. This
- * number must be greater than print entry to prevent out of bound evtlog
- * entry array access.
- */
-#define SDE_EVTLOG_ENTRY	(SDE_EVTLOG_PRINT_ENTRY * 4)
-#define SDE_EVTLOG_MAX_DATA 15
-#define SDE_EVTLOG_BUF_MAX 512
-#define SDE_EVTLOG_BUF_ALIGN 32
-
-DEFINE_SPINLOCK(sde_evtloglock);
-
-struct tlog {
-	u32 counter;
-	s64 time;
-	const char *name;
-	int line;
-	u32 data[SDE_EVTLOG_MAX_DATA];
-	u32 data_cnt;
-	int pid;
-};
-
-static struct sde_dbg_evtlog {
-	struct tlog logs[SDE_EVTLOG_ENTRY];
-	u32 first;
-	u32 last;
-	u32 curr;
-	struct dentry *evtlog;
-	u32 evtlog_enable;
-	u32 panic_on_err;
-	struct work_struct evtlog_dump_work;
-	bool work_panic;
-} sde_dbg_evtlog;
-
-static inline bool sde_evtlog_is_enabled(u32 flag)
+bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag)
 {
-	return (flag & sde_dbg_evtlog.evtlog_enable) ||
-		(flag == SDE_EVTLOG_ALL && sde_dbg_evtlog.evtlog_enable);
+	if (!evtlog)
+		return false;
+
+	return (flag & evtlog->enable) ||
+		(flag == SDE_EVTLOG_ALL && evtlog->enable);
 }
 
-void sde_evtlog(const char *name, int line, int flag, ...)
+void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,
+		int flag, ...)
 {
 	unsigned long flags;
 	int i, val = 0;
 	va_list args;
-	struct tlog *log;
+	struct sde_dbg_evtlog_log *log;
 
-	if (!sde_evtlog_is_enabled(flag))
+	if (!evtlog)
 		return;
 
-	spin_lock_irqsave(&sde_evtloglock, flags);
-	log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.curr];
+	if (!sde_evtlog_is_enabled(evtlog, flag))
+		return;
+
+	spin_lock_irqsave(&evtlog->spin_lock, flags);
+	log = &evtlog->logs[evtlog->curr];
 	log->time = ktime_to_us(ktime_get());
 	log->name = name;
 	log->line = line;
@@ -106,26 +65,27 @@ void sde_evtlog(const char *name, int line, int flag, ...)
 	}
 	va_end(args);
 	log->data_cnt = i;
-	sde_dbg_evtlog.curr = (sde_dbg_evtlog.curr + 1) % SDE_EVTLOG_ENTRY;
-	sde_dbg_evtlog.last++;
+	evtlog->curr = (evtlog->curr + 1) % SDE_EVTLOG_ENTRY;
+	evtlog->last++;
 
 	trace_sde_evtlog(name, line, i > 0 ? log->data[0] : 0,
 			i > 1 ? log->data[1] : 0);
 
-	spin_unlock_irqrestore(&sde_evtloglock, flags);
+	spin_unlock_irqrestore(&evtlog->spin_lock, flags);
 }
 
 /* always dump the last entries which are not dumped yet */
-static bool _sde_evtlog_dump_calc_range(void)
+static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog)
 {
-	static u32 next;
 	bool need_dump = true;
 	unsigned long flags;
-	struct sde_dbg_evtlog *evtlog = &sde_dbg_evtlog;
 
-	spin_lock_irqsave(&sde_evtloglock, flags);
+	if (!evtlog)
+		return false;
 
-	evtlog->first = next;
+	spin_lock_irqsave(&evtlog->spin_lock, flags);
+
+	evtlog->first = evtlog->next;
 
 	if (evtlog->last == evtlog->first) {
 		need_dump = false;
@@ -143,27 +103,34 @@ static bool _sde_evtlog_dump_calc_range(void)
 			evtlog->last - evtlog->first);
 		evtlog->first = evtlog->last - SDE_EVTLOG_PRINT_ENTRY;
 	}
-	next = evtlog->first + 1;
+	evtlog->next = evtlog->first + 1;
 
 dump_exit:
-	spin_unlock_irqrestore(&sde_evtloglock, flags);
+	spin_unlock_irqrestore(&evtlog->spin_lock, flags);
 
 	return need_dump;
 }
 
-static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size)
+ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+		char *evtlog_buf, ssize_t evtlog_buf_size)
 {
 	int i;
 	ssize_t off = 0;
-	struct tlog *log, *prev_log;
+	struct sde_dbg_evtlog_log *log, *prev_log;
 	unsigned long flags;
 
-	spin_lock_irqsave(&sde_evtloglock, flags);
+	if (!evtlog || !evtlog_buf)
+		return 0;
 
-	log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.first %
-		SDE_EVTLOG_ENTRY];
+	/* update markers, exit if nothing to print */
+	if (!_sde_evtlog_dump_calc_range(evtlog))
+		return 0;
 
-	prev_log = &sde_dbg_evtlog.logs[(sde_dbg_evtlog.first - 1) %
+	spin_lock_irqsave(&evtlog->spin_lock, flags);
+
+	log = &evtlog->logs[evtlog->first % SDE_EVTLOG_ENTRY];
+
+	prev_log = &evtlog->logs[(evtlog->first - 1) %
 		SDE_EVTLOG_ENTRY];
 
 	off = snprintf((evtlog_buf + off), (evtlog_buf_size - off), "%s:%-4d",
@@ -175,7 +142,7 @@ static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size)
 	}
 
 	off += snprintf((evtlog_buf + off), (evtlog_buf_size - off),
-		"=>[%-8d:%-11llu:%9llu][%-4d]:", sde_dbg_evtlog.first,
+		"=>[%-8d:%-11llu:%9llu][%-4d]:", evtlog->first,
 		log->time, (log->time - prev_log->time), log->pid);
 
 	for (i = 0; i < log->data_cnt; i++)
@@ -184,143 +151,37 @@ static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size)
 
 	off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), "\n");
 
-	spin_unlock_irqrestore(&sde_evtloglock, flags);
+	spin_unlock_irqrestore(&evtlog->spin_lock, flags);
 
 	return off;
 }
 
-static void _sde_evtlog_dump_all(void)
+void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog)
 {
-	char evtlog_buf[SDE_EVTLOG_BUF_MAX];
+	char buf[SDE_EVTLOG_BUF_MAX];
 
-	while (_sde_evtlog_dump_calc_range()) {
-		sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX);
-		pr_info("%s", evtlog_buf);
-	}
-}
-
-static void _sde_dump_array(bool dead, const char *name)
-{
-	_sde_evtlog_dump_all();
-
-	if (dead && sde_dbg_evtlog.panic_on_err)
-		panic(name);
-}
-
-static void _sde_dump_work(struct work_struct *work)
-{
-	_sde_dump_array(sde_dbg_evtlog.work_panic, "evtlog_workitem");
-}
-
-void sde_dbg_dump(bool queue, const char *name, ...)
-{
-	int i;
-	bool dead = false;
-	va_list args;
-	char *blk_name = NULL;
-
-	if (!sde_evtlog_is_enabled(SDE_EVTLOG_DEFAULT))
+	if (!evtlog)
 		return;
 
-	if (queue && work_pending(&sde_dbg_evtlog.evtlog_dump_work))
-		return;
-
-	va_start(args, name);
-	for (i = 0; i < SDE_EVTLOG_MAX_DATA; i++) {
-		blk_name = va_arg(args, char*);
-		if (IS_ERR_OR_NULL(blk_name))
-			break;
-
-		if (!strcmp(blk_name, "panic"))
-			dead = true;
-	}
-	va_end(args);
-
-	if (queue) {
-		/* schedule work to dump later */
-		sde_dbg_evtlog.work_panic = dead;
-		schedule_work(&sde_dbg_evtlog.evtlog_dump_work);
-	} else {
-		_sde_dump_array(dead, name);
-	}
+	while (sde_evtlog_dump_to_buffer(evtlog, buf, sizeof(buf)))
+		pr_info("%s", buf);
 }
 
-static int sde_evtlog_dump_open(struct inode *inode, struct file *file)
+struct sde_dbg_evtlog *sde_evtlog_init(void)
 {
-	/* non-seekable */
-	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
-	file->private_data = inode->i_private;
-	return 0;
+	struct sde_dbg_evtlog *evtlog;
+
+	evtlog = kzalloc(sizeof(*evtlog), GFP_KERNEL);
+	if (!evtlog)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_init(&evtlog->spin_lock);
+	evtlog->enable = SDE_EVTLOG_DEFAULT_ENABLE;
+
+	return evtlog;
 }
 
-static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff,
-		size_t count, loff_t *ppos)
+void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog)
 {
-	ssize_t len = 0;
-	char evtlog_buf[SDE_EVTLOG_BUF_MAX];
-
-	if (_sde_evtlog_dump_calc_range()) {
-		len = sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX);
-		if (copy_to_user(buff, evtlog_buf, len))
-			return -EFAULT;
-		*ppos += len;
-	}
-
-	return len;
-}
-
-static ssize_t sde_evtlog_dump_write(struct file *file,
-	const char __user *user_buf, size_t count, loff_t *ppos)
-{
-	_sde_evtlog_dump_all();
-
-	if (sde_dbg_evtlog.panic_on_err)
-		panic("sde");
-
-	return count;
-}
-
-static const struct file_operations sde_evtlog_fops = {
-	.open = sde_evtlog_dump_open,
-	.read = sde_evtlog_dump_read,
-	.write = sde_evtlog_dump_write,
-};
-
-int sde_evtlog_init(struct dentry *debugfs_root)
-{
-	int i;
-
-	sde_dbg_evtlog.evtlog = debugfs_create_dir("evt_dbg", debugfs_root);
-	if (IS_ERR_OR_NULL(sde_dbg_evtlog.evtlog)) {
-		pr_err("debugfs_create_dir fail, error %ld\n",
-		       PTR_ERR(sde_dbg_evtlog.evtlog));
-		sde_dbg_evtlog.evtlog = NULL;
-		return -ENODEV;
-	}
-
-	INIT_WORK(&sde_dbg_evtlog.evtlog_dump_work, _sde_dump_work);
-	sde_dbg_evtlog.work_panic = false;
-
-	for (i = 0; i < SDE_EVTLOG_ENTRY; i++)
-		sde_dbg_evtlog.logs[i].counter = i;
-
-	debugfs_create_file("dump", 0644, sde_dbg_evtlog.evtlog, NULL,
-						&sde_evtlog_fops);
-	debugfs_create_u32("enable", 0644, sde_dbg_evtlog.evtlog,
-			    &sde_dbg_evtlog.evtlog_enable);
-	debugfs_create_u32("panic", 0644, sde_dbg_evtlog.evtlog,
-			    &sde_dbg_evtlog.panic_on_err);
-
-	sde_dbg_evtlog.evtlog_enable = SDE_EVTLOG_DEFAULT_ENABLE;
-	sde_dbg_evtlog.panic_on_err = SDE_DBG_DEFAULT_PANIC;
-
-	pr_info("evtlog_status: enable:%d, panic:%d\n",
-		sde_dbg_evtlog.evtlog_enable, sde_dbg_evtlog.panic_on_err);
-
-	return 0;
-}
-
-void sde_evtlog_destroy(void)
-{
-	debugfs_remove(sde_dbg_evtlog.evtlog);
+	kfree(evtlog);
 }