Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 50df4413 authored by raghavendra ambadas's avatar raghavendra ambadas
Browse files

msm: mdss: add real time debug log



To analyze issues like underruns and other timing issues
add xlogs. Add debugfs entry for xlog to enable or disable
logging and register dumping at run time.

Enable xlog: echo 1 > <debugfs>/mdp/xlog/enable

Enable register dump:
echo 1 > <debugfs>/mdp/xlog/reg_dump

Enable panic on error:
echo 1 > <debugfs>/mdp/xlog/panic

Get xlogs and register dump
cat <debugfs>/mdp/xlog/dump

Change-Id: I10c2993d9dde6ef16a37256928fff1239e6370ff
Signed-off-by: default avatarRaghavendra Ambadas <rambad@codeaurora.org>
parent 38febb2c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@ mdss-mdp-objs += mdss_mdp_wb.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o

ifeq ($(CONFIG_FB_MSM_MDSS),y)
obj-$(CONFIG_DEBUG_FS) += mdss_debug.o
obj-$(CONFIG_DEBUG_FS) += mdss_debug.o mdss_debug_xlog.o
endif

dsi-v2-objs = dsi_v2.o dsi_host_v2.o dsi_io_v2.o
+31 −16
Original line number Diff line number Diff line
@@ -30,21 +30,6 @@
#define GROUP_BYTES 4
#define ROW_BYTES 16
#define MAX_VSYNC_COUNT 0xFFFFFFF
struct mdss_debug_data {
	struct dentry *root;
	struct list_head base_list;
};

struct mdss_debug_base {
	struct mdss_debug_data *mdd;
	void __iomem *base;
	size_t off;
	size_t cnt;
	size_t max_offset;
	char *buf;
	size_t buf_len;
	struct list_head head;
};

static int mdss_debug_base_open(struct inode *inode, struct file *file)
{
@@ -266,12 +251,14 @@ int mdss_debug_register_base(const char *name, void __iomem *base,
	if (!dbg)
		return -ENOMEM;

	if (name)
		strlcpy(dbg->name, name, sizeof(dbg->name));
	dbg->base = base;
	dbg->max_offset = max_offset;
	dbg->off = 0;
	dbg->cnt = DEFAULT_BASE_REG_CNT;

	if (name)
	if (name && strcmp(name, "mdp"))
		prefix_len = snprintf(dn, sizeof(dn), "%s_", name);

	strlcpy(dn + prefix_len, "off", sizeof(dn) - prefix_len);
@@ -396,6 +383,11 @@ int mdss_debugfs_init(struct mdss_data_type *mdata)
	debugfs_create_u32("min_mdp_clk", 0644, mdd->root,
			(u32 *)&mdata->min_mdp_clk);

	if (mdss_create_xlog_debug(mdd)) {
		mdss_debugfs_cleanup(mdd);
		return -ENODEV;
	}

	mdata->debug_inf.debug_data = mdd;

	return 0;
@@ -411,6 +403,29 @@ int mdss_debugfs_remove(struct mdss_data_type *mdata)
	return 0;
}

void mdss_dump_reg(char __iomem *base, int len)
{
	char *addr;
	u32 x0, x4, x8, xc;
	int i;

	addr = base;
	if (len % 16)
		len += 16;
	len /= 16;

	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
	for (i = 0; i < len; i++) {
		x0 = readl_relaxed(addr+0x0);
		x4 = readl_relaxed(addr+0x4);
		x8 = readl_relaxed(addr+0x8);
		xc = readl_relaxed(addr+0xc);
		pr_info("%p : %08x %08x %08x %08x\n", addr, x0, x4, x8, xc);
		addr += 16;
	}
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
}

int vsync_count;
static struct mdss_mdp_misr_map {
	u32 ctrl_reg;
+50 −1
Original line number Diff line number Diff line
/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2014, 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
@@ -14,13 +14,48 @@
#ifndef MDSS_DEBUG_H
#define MDSS_DEBUG_H

#include <stdarg.h>
#include "mdss.h"

#define MISR_POLL_SLEEP		2000
#define MISR_POLL_TIMEOUT	32000
#define MISR_CRC_BATCH_CFG	0x101
#define DATA_LIMITER (-1)
#define XLOG_TOUT_DATA_LIMITER (NULL)
#define XLOG_FUNC_ENTRY	0x1111
#define XLOG_FUNC_EXIT	0x2222
#define MDSS_REG_BLOCK_NAME_LEN (5)

#define MDSS_XLOG(...) mdss_xlog(__func__, ##__VA_ARGS__, DATA_LIMITER)
#define MDSS_XLOG_TOUT_HANDLER(...)	\
	mdss_xlog_tout_handler(__func__, ##__VA_ARGS__, XLOG_TOUT_DATA_LIMITER)

#ifdef CONFIG_DEBUG_FS
struct mdss_debug_base {
	struct mdss_debug_data *mdd;
	char name[80];
	void __iomem *base;
	size_t off;
	size_t cnt;
	size_t max_offset;
	char *buf;
	size_t buf_len;
	struct list_head head;
};

struct debug_log {
	struct dentry *xlog;
	u32 xlog_enable;
	u32 panic_on_err;
	u32 enable_reg_dump;
};

struct mdss_debug_data {
	struct dentry *root;
	struct list_head base_list;
	struct debug_log logd;
};

int mdss_debugfs_init(struct mdss_data_type *mdata);
int mdss_debugfs_remove(struct mdss_data_type *mdata);
int mdss_debug_register_base(const char *name, void __iomem *base,
@@ -30,6 +65,13 @@ int mdss_misr_set(struct mdss_data_type *mdata, struct mdp_misr *req,
int mdss_misr_get(struct mdss_data_type *mdata, struct mdp_misr *resp,
			struct mdss_mdp_ctl *ctl);
void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id);

int mdss_create_xlog_debug(struct mdss_debug_data *mdd);
void mdss_xlog(const char *name, ...);
void mdss_xlog_dump(void);
void mdss_dump_reg(char __iomem *base, int len);
void mdss_dsi_debug_check_te(struct mdss_panel_data *pdata);
void mdss_xlog_tout_handler(const char *name, ...);
#else
static inline int mdss_debugfs_init(struct mdss_data_type *mdata) { return 0; }
static inline int mdss_debugfs_remove(struct mdss_data_type *mdata)
@@ -46,5 +88,12 @@ static inline int mdss_misr_get(struct mdss_data_type *mdata,
{ return 0; }
static inline void mdss_misr_crc_collect(struct mdss_data_type *mdata,
						int block_id) { }

static inline int create_xlog_debug(struct mdss_data_type *mdata) { }
static inline void mdss_xlog(const char *name, ...) { }
static inline void mdss_xlog_dump(void) { }
static inline void mdss_dump_reg(char __iomem *base, int len) { }
static inline void mdss_dsi_debug_check_te(struct mdss_panel_data *pdata) { }
static inline void mdss_xlog_tout_handler(const char *name, ...) { }
#endif
#endif /* MDSS_DEBUG_H */
+196 −0
Original line number Diff line number Diff line
/* Copyright (c) 2014, 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.
 *
 */

#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/ktime.h>
#include <linux/debugfs.h>

#include "mdss.h"
#include "mdss_mdp.h"
#include "mdss_debug.h"

#define MDSS_XLOG_ENTRY	256
#define MDSS_XLOG_MAX_DATA 6
#define MDSS_XLOG_BUF_MAX 512

struct tlog {
	u32 tick;
	const char *name;
	u32 data[MDSS_XLOG_MAX_DATA];
	u32 data_cnt;
};

struct mdss_dbg_xlog {
	struct tlog logs[MDSS_XLOG_ENTRY];
	int first;
	int last;
	spinlock_t xlock;
} mdss_dbg_xlog;

static int mdss_xlog_dump_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;
}

static ssize_t mdss_xlog_dump_read(struct file *file, char __user *buff,
		size_t count, loff_t *ppos)
{
	MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0", "dsi1", "edp", "hdmi", "panic");
	return 0;
}

static const struct file_operations mdss_xlog_fops = {
	.open = mdss_xlog_dump_open,
	.read = mdss_xlog_dump_read,
};

int mdss_create_xlog_debug(struct mdss_debug_data *mdd)
{
	spin_lock_init(&mdss_dbg_xlog.xlock);

	mdd->logd.xlog = debugfs_create_dir("xlog", mdd->root);
	if (IS_ERR_OR_NULL(mdd->logd.xlog)) {
		pr_err("debugfs_create_dir fail, error %ld\n",
		       PTR_ERR(mdd->logd.xlog));
		mdd->logd.xlog = NULL;
		return -ENODEV;
	}
	debugfs_create_file("dump", 0644, mdd->logd.xlog, NULL,
						&mdss_xlog_fops);
	debugfs_create_bool("enable", 0644, mdd->logd.xlog,
			    &mdd->logd.xlog_enable);
	debugfs_create_bool("panic", 0644, mdd->logd.xlog,
			    &mdd->logd.panic_on_err);
	debugfs_create_bool("reg_dump", 0644, mdd->logd.xlog,
			    &mdd->logd.enable_reg_dump);
	return 0;
}

void mdss_xlog(const char *name, ...)
{
	struct mdss_data_type *mdata = mdss_mdp_get_mdata();
	struct mdss_debug_data *mdd = mdata->debug_inf.debug_data;
	unsigned long flags;
	int i, val = 0;
	va_list args;
	struct tlog *log;
	ktime_t time;

	if (!mdd->logd.xlog_enable)
		return;

	spin_lock_irqsave(&mdss_dbg_xlog.xlock, flags);

	time = ktime_get();

	log = &mdss_dbg_xlog.logs[mdss_dbg_xlog.first];
	log->tick = ktime_to_us(time);
	log->name = name;
	log->data_cnt = 0;

	va_start(args, name);
	for (i = 0; i < MDSS_XLOG_MAX_DATA; i++) {

		val = va_arg(args, int);
		if (val == DATA_LIMITER)
			break;

		log->data[i] = val;
	}
	va_end(args);

	log->data_cnt = i;

	mdss_dbg_xlog.last = mdss_dbg_xlog.first;
	mdss_dbg_xlog.first++;
	mdss_dbg_xlog.first %= MDSS_XLOG_ENTRY;

	spin_unlock_irqrestore(&mdss_dbg_xlog.xlock, flags);
}

void mdss_xlog_dump(void)
{
	struct mdss_data_type *mdata = mdss_mdp_get_mdata();
	struct mdss_debug_data *mdd = mdata->debug_inf.debug_data;
	int i, n, d_cnt, off;
	unsigned long flags;
	struct tlog *log;
	char xlog_buf[MDSS_XLOG_BUF_MAX];

	if (!mdd->logd.xlog_enable)
		return;

	spin_lock_irqsave(&mdss_dbg_xlog.xlock, flags);
	i = mdss_dbg_xlog.first;
	for (n = 0; n < MDSS_XLOG_ENTRY; n++) {
		log = &mdss_dbg_xlog.logs[i];
		off = snprintf(xlog_buf, MDSS_XLOG_BUF_MAX, "%-32s => %08d: ",
							log->name, log->tick);
		for (d_cnt = 0; d_cnt < log->data_cnt;) {
			off += snprintf((xlog_buf + off),
					(MDSS_XLOG_BUF_MAX - off),
					"%x ", log->data[d_cnt]);
			d_cnt++;
		}
		pr_err("%s\n", xlog_buf);

		i = (i + 1) % MDSS_XLOG_ENTRY;
	}
	spin_unlock_irqrestore(&mdss_dbg_xlog.xlock, flags);
}

void mdss_xlog_tout_handler(const char *name, ...)
{
	struct mdss_data_type *mdata = mdss_mdp_get_mdata();
	struct mdss_debug_data *mdd = mdata->debug_inf.debug_data;
	struct mdss_debug_base *blk_base, *tmp;
	int i, dead = 0;
	va_list args;
	char *blk_name = NULL;

	if (!mdd->logd.xlog_enable)
		return;

	va_start(args, name);
	for (i = 0; i < MDSS_XLOG_MAX_DATA; i++) {

		blk_name = va_arg(args, char*);
		if (IS_ERR_OR_NULL(blk_name))
			break;

		list_for_each_entry_safe(blk_base, tmp, &mdd->base_list, head) {

			if (blk_base->name &&
				!strcmp(blk_base->name, blk_name) &&
				mdd->logd.enable_reg_dump) {
				pr_info("\n%s  :   =========%s DUMP=========\n",
						__func__, blk_base->name);
				mdss_dump_reg(blk_base->base,
						blk_base->max_offset);
			}
		}
		if (!strcmp(blk_name, "panic"))
			dead = 1;
	}
	va_end(args);

	MDSS_XLOG(0xffff, 0xffff, 0xffff, 0xffff, 0xffff);
	mdss_xlog_dump();

	if (dead && mdd->logd.panic_on_err)
		panic(name);
}
+2 −0
Original line number Diff line number Diff line
@@ -1024,6 +1024,8 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
				panel_data);
	pr_debug("%s+:event=%d\n", __func__, event);

	MDSS_XLOG(event, arg, ctrl_pdata->ndx, 0x3333);

	switch (event) {
	case MDSS_EVENT_UNBLANK:
		rc = mdss_dsi_on(pdata);
Loading