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

Commit d2b69d5d authored by Ray Zhang's avatar Ray Zhang
Browse files

msm: mdss: add debugfs support for LCM debug



Register debugfs nodes to read from and write to the LCM registers.
Give following command in adb shell to read panel register:
	echo address feedback_count > /d/mdp/panel_off
	cat /d/mdp/panel_reg
To write panel register:
	echo address value > /d/mdp/panel_reg

Change-Id: I9ef9fb57dc1b7dea42c7e701c60018f7946ee75b
Signed-off-by: default avatarRay Zhang <rayz@codeaurora.org>
parent a571a6da
Loading
Loading
Loading
Loading
+278 −0
Original line number Diff line number Diff line
@@ -25,12 +25,290 @@
#include "mdss_mdp.h"
#include "mdss_mdp_hwio.h"
#include "mdss_debug.h"
#include "mdss_dsi.h"

#define DEFAULT_BASE_REG_CNT 0x100
#define GROUP_BYTES 4
#define ROW_BYTES 16
#define MAX_VSYNC_COUNT 0xFFFFFFF

#define DEFAULT_READ_PANEL_POWER_MODE_REG 0x0A
#define PANEL_RX_MAX_BUF 128
#define PANEL_TX_MAX_BUF 64
#define PANEL_CMD_MIN_TX_COUNT 2
#define PANEL_DATA_NODE_LEN 80

static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00};

static int panel_debug_base_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 int panel_debug_base_release(struct inode *inode, struct file *file)
{
	struct mdss_debug_base *dbg = file->private_data;
	if (dbg && dbg->buf) {
		kfree(dbg->buf);
		dbg->buf_len = 0;
		dbg->buf = NULL;
	}
	return 0;
}

static ssize_t panel_debug_base_offset_write(struct file *file,
		    const char __user *user_buf, size_t count, loff_t *ppos)
{
	struct mdss_debug_base *dbg = file->private_data;
	u32 off = 0;
	u32 cnt = DEFAULT_BASE_REG_CNT;
	char buf[PANEL_RX_MAX_BUF] = {0x0};

	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, "%x %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;
}

static ssize_t panel_debug_base_offset_read(struct file *file,
			char __user *buff, size_t count, loff_t *ppos)
{
	struct mdss_debug_base *dbg = file->private_data;
	int len = 0;
	char buf[PANEL_RX_MAX_BUF] = {0x0};

	if (!dbg)
		return -ENODEV;

	if (*ppos)
		return 0;	/* the end */

	len = snprintf(buf, sizeof(buf), "0x%02zx %zx\n", dbg->off, dbg->cnt);
	if (len < 0)
		return 0;

	if (copy_to_user(buff, buf, len))
		return -EFAULT;

	*ppos += len;	/* increase offset */

	return len;
}

static ssize_t panel_debug_base_reg_write(struct file *file,
		const char __user *user_buf, size_t count, loff_t *ppos)
{
	struct mdss_debug_base *dbg = file->private_data;

	u32 cnt, tmp, i;
	u32 len = 0;
	char buf[PANEL_TX_MAX_BUF] = {0x0};
	char *p = NULL;
	char reg[PANEL_TX_MAX_BUF] = {0x0};

	struct mdss_data_type *mdata = mdss_res;
	struct mdss_mdp_ctl *ctl = mdata->ctl_off + 0;
	struct mdss_panel_data *panel_data = ctl->panel_data;
	struct mdss_dsi_ctrl_pdata *ctrl_pdata = container_of(panel_data,
					struct mdss_dsi_ctrl_pdata, panel_data);

	struct dsi_cmd_desc dsi_write_cmd = {
		{DTYPE_GEN_LWRITE, 1, 0, 0, 0, 0/*len*/}, reg};
	struct dcs_cmd_req cmdreq;

	cmdreq.cmds = &dsi_write_cmd;
	cmdreq.cmds_cnt = 1;
	cmdreq.flags = CMD_REQ_COMMIT;
	cmdreq.rlen = 0;
	cmdreq.cb = NULL;

	if (!dbg || !mdata)
		return -ENODEV;

	if (count >= sizeof(buf))
		return -EFAULT;

	if (copy_from_user(buf, user_buf, count))
		return -EFAULT;

	buf[count] = 0;	/* end of string */

	len = count / 3;

	if (len < PANEL_CMD_MIN_TX_COUNT) {
		pr_err("wrong input reg len\n");
		return -EFAULT;
	}

	for (i = 0; i < len; i++) {
		p = buf + i * 3;
		p[2] = 0;
		pr_debug("p[%d] = %p:%s\n", i, p, p);
		cnt = sscanf(p, "%x", &tmp);
		reg[i] = tmp;
		pr_debug("reg[%d] = %x\n", i, (int)reg[i]);
	}

	if (mdata->debug_inf.debug_enable_clock)
		mdata->debug_inf.debug_enable_clock(1);

	dsi_write_cmd.dchdr.dlen = len;
	mdss_dsi_cmdlist_put(ctrl_pdata, &cmdreq);

	if (mdata->debug_inf.debug_enable_clock)
		mdata->debug_inf.debug_enable_clock(0);

	return count;
}

static ssize_t panel_debug_base_reg_read(struct file *file,
			char __user *user_buf, size_t count, loff_t *ppos)
{
	struct mdss_debug_base *dbg = file->private_data;
	int len = 0;
	int rx_len = 0;
	int i, lx = 0;
	char to_user_buf[PANEL_RX_MAX_BUF] = {0x0};
	char panel_reg_buf[PANEL_RX_MAX_BUF] = {0x0};
	char rx_buf[PANEL_RX_MAX_BUF] = {0x0};
	struct mdss_data_type *mdata = mdss_res;
	struct mdss_mdp_ctl *ctl = mdata->ctl_off + 0;
	struct mdss_panel_data *panel_data = ctl->panel_data;
	struct mdss_dsi_ctrl_pdata *ctrl_pdata = container_of(panel_data,
					struct mdss_dsi_ctrl_pdata, panel_data);

	if (!dbg)
		return -ENODEV;

	if (*ppos)
		return 0;	/* the end */

	if (mdata->debug_inf.debug_enable_clock)
		mdata->debug_inf.debug_enable_clock(1);

	panel_reg[0] = dbg->off;
	mdss_dsi_panel_cmd_read(ctrl_pdata, panel_reg[0],
		panel_reg[1], NULL, rx_buf, dbg->cnt);

	rx_len = ctrl_pdata->rx_len;

	for (i = 0; i < rx_len; i++) {
		lx += snprintf(panel_reg_buf + lx, sizeof(panel_reg_buf),
			 "%s%02x", " 0x", rx_buf[i]);
	}

	pr_debug("%s:lx =%d,panel_reg_buf= %s,data[%d]=%x\n",
		__func__, lx, panel_reg_buf, i, rx_buf[i]);

	len = snprintf(to_user_buf, sizeof(to_user_buf), "0x%02zx:%s\n",
		dbg->off, panel_reg_buf);

	if (mdata->debug_inf.debug_enable_clock)
		mdata->debug_inf.debug_enable_clock(0);

	if (len < 0)
		return 0;

	if (copy_to_user(user_buf, to_user_buf, len))
		return -EFAULT;

	*ppos += len;	/* increase offset */
	return len;
}

static const struct file_operations panel_off_fops = {
	.open = panel_debug_base_open,
	.release = panel_debug_base_release,
	.read = panel_debug_base_offset_read,
	.write = panel_debug_base_offset_write,
};

static const struct file_operations panel_reg_fops = {
	.open = panel_debug_base_open,
	.release = panel_debug_base_release,
	.read = panel_debug_base_reg_read,
	.write = panel_debug_base_reg_write,
};

int panel_debug_register_base(const char *name, void __iomem *base,
			     size_t max_offset)
{
	struct mdss_data_type *mdata = mdss_res;
	struct mdss_debug_data *mdd;
	struct mdss_debug_base *dbg;
	struct dentry *ent_off, *ent_reg;
	char dn[PANEL_DATA_NODE_LEN] = "";
	int prefix_len = 0;

	if (!mdata || !mdata->debug_inf.debug_data)
		return -ENODEV;

	mdd = mdata->debug_inf.debug_data;

	dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
	if (!dbg)
		return -ENOMEM;

	dbg->base = base;
	dbg->max_offset = max_offset;
	dbg->off = 0x0a;
	dbg->cnt = 0x01;

	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, mdd->root,
					dbg, &panel_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, mdd->root,
					dbg, &panel_reg_fops);
	if (IS_ERR_OR_NULL(ent_reg)) {
		pr_err("debugfs_create_file: reg fail\n");
		goto reg_fail;
	}

	list_add(&dbg->head, &mdd->base_list);

	return 0;
reg_fail:
	debugfs_remove(ent_off);
off_fail:
	kfree(dbg);
	return -ENODEV;
}

static int mdss_debug_base_open(struct inode *inode, struct file *file)
{
	/* non-seekable */
+6 −0
Original line number Diff line number Diff line
@@ -126,6 +126,8 @@ int mdss_debug_register_base(const char *name, void __iomem *base,
void mdss_debug_register_dump_range(struct platform_device *pdev,
	struct mdss_debug_base *blk_base, const char *ranges_prop,
	const char *name_prop);
int panel_debug_register_base(const char *name, void __iomem *base,
				    size_t max_offset);
int mdss_misr_set(struct mdss_data_type *mdata, struct mdp_misr *req,
			struct mdss_mdp_ctl *ctl);
int mdss_misr_get(struct mdss_data_type *mdata, struct mdp_misr *resp,
@@ -148,6 +150,10 @@ static inline int mdss_debug_register_base(const char *name, void __iomem *base,
static inline void mdss_debug_register_dump_range(struct platform_device *pdev,
	struct mdss_debug_base *blk_base, const char *ranges_prop,
	const char *name_prop) { return 0; }
static inline int panel_debug_register_base(const char *name,
					void __iomem *base,
					size_t max_offset)
{ return 0; }
static inline int mdss_misr_set(struct mdss_data_type *mdata,
					struct mdp_misr *req,
					struct mdss_mdp_ctl *ctl)
+3 −0
Original line number Diff line number Diff line
@@ -2060,6 +2060,9 @@ int dsi_panel_device_register(struct device_node *pan_node,
		ctrl_pdata->ndx = 1;
	}

	panel_debug_register_base("panel",
		ctrl_pdata->ctrl_base, ctrl_pdata->reg_size);

	pr_debug("%s: Panel data initialized\n", __func__);
	return 0;
}
+4 −2
Original line number Diff line number Diff line
/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2015, 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
@@ -393,6 +393,7 @@ struct mdss_dsi_ctrl_pdata {
	struct dsi_buf rx_buf;
	struct dsi_buf status_buf;
	int status_mode;
	int rx_len;

	struct dsi_pinctrl_res pin_res;

@@ -477,7 +478,8 @@ void mdss_dsi_ctrl_setup(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_dln0_phy_err(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_lp_cd_rx(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_get_hw_revision(struct mdss_dsi_ctrl_pdata *ctrl);

u32 mdss_dsi_panel_cmd_read(struct mdss_dsi_ctrl_pdata *ctrl, char cmd0,
		char cmd1, void (*fxn)(int), char *rbuf, int len);
int mdss_dsi_panel_init(struct device_node *node,
		struct mdss_dsi_ctrl_pdata *ctrl_pdata,
		bool cmd_cfg_cont_splash);
+1 −0
Original line number Diff line number Diff line
@@ -1929,6 +1929,7 @@ int mdss_dsi_cmdlist_rx(struct mdss_dsi_ctrl_pdata *ctrl,
		rp = &ctrl->rx_buf;
		len = mdss_dsi_cmds_rx(ctrl, req->cmds, req->rlen);
		memcpy(req->rbuf, rp->data, rp->len);
		ctrl->rx_len = len;
	} else {
		pr_err("%s: No rx buffer provided\n", __func__);
	}