diff options
| author | Adrian Salido-Moreno <adrianm@codeaurora.org> | 2012-09-24 10:47:46 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:13:29 -0700 |
| commit | 6bc239c0ca20f0dd9e95a1eb5ee13b32f13486c2 (patch) | |
| tree | 27abe99413194a9a67105d017b8dd071c6fd6f01 | |
| parent | 059a1e16bbdac21f601219229621407d9ae68653 (diff) | |
msm: mdss: enable display debugging through debugfs
Register debugfs nodes to allow dumping and writing of MDP and DSI
registers for debugging purposes.
Change-Id: I14d9a7f441292eef5564144707184dfa5da75d85
Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org>
[cip@codeaurora.org: Moved new file locations]
Signed-off-by: Clarence Ip <cip@codeaurora.org>
| -rw-r--r-- | drivers/video/fbdev/msm/Makefile | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss.h | 2 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_debug.c | 348 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_debug.h | 39 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.c | 23 |
5 files changed, 412 insertions, 1 deletions
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index 88a7c455df4b..a0d707e643a0 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -7,6 +7,7 @@ mdss-mdp-objs += mdss_mdp_overlay.o mdss-mdp-objs += mdss_mdp_wb.o obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o +obj-$(CONFIG_DEBUG_FS) += mdss_debug.o mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o mdss-dsi-objs += mdss_dsi_panel.o diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index b2470ceb4c4f..64a685b6cf26 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -62,6 +62,7 @@ struct mdss_data_type { struct delayed_work clk_ctrl_worker; struct platform_device *pdev; char __iomem *mdp_base; + size_t mdp_reg_size; char __iomem *vbif_base; u32 irq; @@ -94,6 +95,7 @@ struct mdss_data_type { struct mdss_iommu_map_type *iommu_map; struct early_suspend early_suspend; + void *debug_data; }; extern struct mdss_data_type *mdss_res; diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c new file mode 100644 index 000000000000..abef27d0e9f1 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_debug.c @@ -0,0 +1,348 @@ +/* Copyright (c) 2009-2012, 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) "%s: " fmt, __func__ + +#include <linux/debugfs.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/printk.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include "mdss.h" +#include "mdss_mdp.h" +#include "mdss_debug.h" + +#define DEFAULT_BASE_REG_CNT 128 +#define GROUP_BYTES 4 +#define ROW_BYTES 16 + +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) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + file->private_data = inode->i_private; + return 0; +} + +static int mdss_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 mdss_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, 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 */ + + sscanf(buf, "%5x %d", &off, &cnt); + + if (off > dbg->max_offset) + return -EINVAL; + + if (cnt <= 0) + cnt = DEFAULT_BASE_REG_CNT; + + 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 mdss_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[24]; + + if (!dbg) + return -ENODEV; + + if (*ppos) + return 0; /* the end */ + + len = snprintf(buf, sizeof(buf), "0x%08x %d\n", dbg->off, dbg->off); + if (len < 0) + return 0; + + if (copy_to_user(buff, buf, len)) + return -EFAULT; + + *ppos += len; /* increase offset */ + + return len; +} + +static ssize_t mdss_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; + 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, "%x %x", &off, &data); + + if (cnt < 2) + return -EFAULT; + + if (off >= dbg->max_offset) + return -EFAULT; + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + writel_relaxed(data, dbg->base + off); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + + pr_debug("addr=%x data=%x\n", off, data); + + return count; +} + +static ssize_t mdss_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; + 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) { + pr_err("not enough memory to hold reg dump\n"); + return -ENOMEM; + } + + ptr = dbg->base + dbg->off; + tot = 0; + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + 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)ptr) - ((int)dbg->base), + dump_buf); + + ptr += ROW_BYTES; + tot += len; + if (tot >= dbg->buf_len) + break; + } + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, 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 mdss_off_fops = { + .open = mdss_debug_base_open, + .release = mdss_debug_base_release, + .read = mdss_debug_base_offset_read, + .write = mdss_debug_base_offset_write, +}; + +static const struct file_operations mdss_reg_fops = { + .open = mdss_debug_base_open, + .release = mdss_debug_base_release, + .read = mdss_debug_base_reg_read, + .write = mdss_debug_base_reg_write, +}; + +int mdss_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[80] = ""; + int prefix_len = 0; + + if (!mdata || !mdata->debug_data) + return -ENODEV; + + mdd = mdata->debug_data; + + dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); + if (!dbg) + return -ENOMEM; + + dbg->base = base; + dbg->max_offset = max_offset; + dbg->off = 0; + dbg->cnt = DEFAULT_BASE_REG_CNT; + + 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, &mdss_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, &mdss_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_debugfs_cleanup(struct mdss_debug_data *mdd) +{ + struct mdss_debug_base *base, *tmp; + + if (!mdd) + return 0; + + list_for_each_entry_safe(base, tmp, &mdd->base_list, head) { + list_del(&base->head); + kfree(base); + } + + if (mdd->root) + debugfs_remove_recursive(mdd->root); + + kfree(mdd); + + return 0; +} + +int mdss_debugfs_init(struct mdss_data_type *mdata) +{ + struct mdss_debug_data *mdd; + + if (mdata->debug_data) { + pr_warn("mdss debugfs already initialized\n"); + return -EBUSY; + } + + mdd = kzalloc(sizeof(*mdd), GFP_KERNEL); + if (!mdd) { + pr_err("no memory to create mdss debug data\n"); + return -ENOMEM; + } + INIT_LIST_HEAD(&mdd->base_list); + + mdd->root = debugfs_create_dir("mdp", NULL); + if (IS_ERR_OR_NULL(mdd->root)) { + pr_err("debugfs_create_dir fail, error %ld\n", + PTR_ERR(mdd->root)); + mdd->root = NULL; + mdss_debugfs_cleanup(mdd); + return -ENODEV; + } + + mdata->debug_data = mdd; + + return 0; +} + +int mdss_debugfs_remove(struct mdss_data_type *mdata) +{ + struct mdss_debug_data *mdd = mdata->debug_data; + + mdss_debugfs_cleanup(mdd); + + return 0; +} diff --git a/drivers/video/fbdev/msm/mdss_debug.h b/drivers/video/fbdev/msm/mdss_debug.h new file mode 100644 index 000000000000..167fa8a01407 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_debug.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2012, 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. + * + */ + +#ifndef MDSS_DEBUG_H +#define MDSS_DEBUG_H + +#include "mdss.h" + +#ifdef CONFIG_DEBUG_FS +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, + size_t max_offset); +#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) +{ + return 0; +} +static inline int mdss_debug_register_base(const char *name, void __iomem *base, + size_t max_offset) +{ + return 0; +} +#endif +#endif /* MDSS_DEBUG_H */ diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index af3bc308d905..2a87cb8b3de1 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -49,6 +49,7 @@ #include "mdss.h" #include "mdss_fb.h" #include "mdss_mdp.h" +#include "mdss_debug.h" struct mdss_data_type *mdss_res; @@ -772,6 +773,19 @@ int mdss_iommu_init(struct mdss_data_type *mdata) return 0; } +static int mdss_mdp_debug_init(struct mdss_data_type *mdata) +{ + int rc; + + rc = mdss_debugfs_init(mdata); + if (rc) + return rc; + + mdss_debug_register_base(NULL, mdata->mdp_base, mdata->mdp_reg_size); + + return 0; +} + static int mdss_hw_init(struct mdss_data_type *mdata) { char *base = mdata->vbif_base; @@ -875,8 +889,9 @@ static int mdss_mdp_probe(struct platform_device *pdev) goto probe_done; } + mdata->mdp_reg_size = resource_size(res); mdata->mdp_base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); + mdata->mdp_reg_size); if (unlikely(!mdata->mdp_base)) { pr_err("unable to map MDP base\n"); rc = -ENOMEM; @@ -933,6 +948,11 @@ static int mdss_mdp_probe(struct platform_device *pdev) pr_err("unable to register early suspend\n"); goto probe_done; } + + rc = mdss_mdp_debug_init(mdata); + if (rc) + pr_err("unable to initialize mdp debugging\n"); + probe_done: if (IS_ERR_VALUE(rc)) { mdss_res = NULL; @@ -1103,6 +1123,7 @@ static int mdss_mdp_remove(struct platform_device *pdev) mdss_mdp_pp_term(&pdev->dev); mdss_mdp_bus_scale_unregister(mdata); mdss_mdp_remove_early_suspend(mdata); + mdss_debugfs_remove(mdata); return 0; } |
