diff options
author | Heechul Yun <hyun@nvidia.com> | 2011-05-19 17:18:56 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:45:06 -0800 |
commit | 904190d6cb2691483d669aea623c6ed81482e206 (patch) | |
tree | fa4f08b763b9a758bf735125ccdce80ef6d9360e /drivers | |
parent | 0f1646f72c92e45dfcae77be124ab19408af4954 (diff) |
video: tegra: dc: DC and HDMI debug info display
Following files will show information of DC and hdmi
/sys/kernel/debug/tegradc.[01]/{regs,mode,stats}
/sys/kernel/debug/tegra_hdmi/regs
/sys/devices/nvhost/tegradc.[01]/stats_enable
Bug 827295
Change-Id: I60bcf4454b9ea7d0ed73a6199595b06dbfa32cd7
Reviewed-on: http://git-master/r/32454
Reviewed-by: Niket Sirsi <nsirsi@nvidia.com>
Tested-by: Niket Sirsi <nsirsi@nvidia.com>
Rebase-Id: Rcd7e9f5cb51e0b7613ab3f8cc802b30f45b3092c
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/tegra/dc/dc.c | 175 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_priv.h | 16 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_sysfs.c | 52 | ||||
-rw-r--r-- | drivers/video/tegra/dc/hdmi.c | 60 |
4 files changed, 279 insertions, 24 deletions
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index 2e3bf0284671..e1697704959c 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -292,11 +292,11 @@ static void dump_regs(struct tegra_dc *dc) { _dump_regs(dc, dc, dump_regs_print); } -#else +#else /* !DEBUG */ static void dump_regs(struct tegra_dc *dc) {} -#endif +#endif /* DEBUG */ #ifdef CONFIG_DEBUG_FS @@ -324,25 +324,126 @@ static int dbg_dc_open(struct inode *inode, struct file *file) return single_open(file, dbg_dc_show, inode->i_private); } -static const struct file_operations dbg_fops = { +static const struct file_operations regs_fops = { .open = dbg_dc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; -static void tegra_dc_dbg_add(struct tegra_dc *dc) +static int dbg_dc_mode_show(struct seq_file *s, void *unused) { - char name[32]; + struct tegra_dc *dc = s->private; + struct tegra_dc_mode *m; - snprintf(name, sizeof(name), "tegra_dc%d_regs", dc->ndev->id); - (void) debugfs_create_file(name, S_IRUGO, NULL, dc, &dbg_fops); + mutex_lock(&dc->lock); + m = &dc->mode; + seq_printf(s, + "pclk: %d\n" + "h_ref_to_sync: %d\n" + "v_ref_to_sync: %d\n" + "h_sync_width: %d\n" + "v_sync_width: %d\n" + "h_back_porch: %d\n" + "v_back_porch: %d\n" + "h_active: %d\n" + "v_active: %d\n" + "h_front_porch: %d\n" + "v_front_porch: %d\n" + "stereo_mode: %d\n", + m->pclk, m->h_ref_to_sync, m->v_ref_to_sync, + m->h_sync_width, m->v_sync_width, + m->h_back_porch, m->v_back_porch, + m->h_active, m->v_active, + m->h_front_porch, m->v_front_porch, + m->stereo_mode); + mutex_unlock(&dc->lock); + return 0; } -#else -static void tegra_dc_dbg_add(struct tegra_dc *dc) {} -#endif +static int dbg_dc_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, dbg_dc_mode_show, inode->i_private); +} + +static const struct file_operations mode_fops = { + .open = dbg_dc_mode_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +static int dbg_dc_stats_show(struct seq_file *s, void *unused) +{ + struct tegra_dc *dc = s->private; + + mutex_lock(&dc->lock); + seq_printf(s, + "underflows: %u\n" + "underflows_a: %u\n" + "underflows_b: %u\n" + "underflows_c: %u\n", + dc->stats.underflows, + dc->stats.underflows_a, + dc->stats.underflows_b, + dc->stats.underflows_c); + mutex_unlock(&dc->lock); + + return 0; +} + +static int dbg_dc_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, dbg_dc_stats_show, inode->i_private); +} + +static const struct file_operations stats_fops = { + .open = dbg_dc_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void __devexit tegra_dc_remove_debugfs(struct tegra_dc *dc) +{ + if (dc->debugdir) + debugfs_remove_recursive(dc->debugdir); + dc->debugdir = NULL; +} + +static void tegra_dc_create_debugfs(struct tegra_dc *dc) +{ + struct dentry *retval; + + dc->debugdir = debugfs_create_dir(dev_name(&dc->ndev->dev), NULL); + if (!dc->debugdir) + goto remove_out; + + retval = debugfs_create_file("regs", S_IRUGO, dc->debugdir, dc, + ®s_fops); + if (!retval) + goto remove_out; + + retval = debugfs_create_file("mode", S_IRUGO, dc->debugdir, dc, + &mode_fops); + if (!retval) + goto remove_out; + + retval = debugfs_create_file("stats", S_IRUGO, dc->debugdir, dc, + &stats_fops); + if (!retval) + goto remove_out; + + return; +remove_out: + dev_err(&dc->ndev->dev, "could not create debugfs\n"); + tegra_dc_remove_debugfs(dc); +} + +#else /* !CONFIG_DEBUGFS */ +static inline void tegra_dc_create_debugfs(struct tegra_dc *dc) { }; +static inline void __devexit tegra_dc_remove_debugfs(struct tegra_dc *dc) { }; +#endif /* CONFIG_DEBUGFS */ static int tegra_dc_add(struct tegra_dc *dc, int index) { @@ -913,10 +1014,10 @@ static void print_mode(struct tegra_dc *dc, mode->pclk); } } -#else +#else /* !DEBUG */ static inline void print_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode, const char *note) { } -#endif +#endif /* DEBUG */ static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode) { @@ -1255,11 +1356,19 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr) * hosed and reset. */ underflow_mask = status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT); + if (underflow_mask) { val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE); val |= V_BLANK_INT; tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE); dc->underflow_mask |= underflow_mask; + dc->stats.underflows++; + if (status & WIN_A_UF_INT) + dc->stats.underflows_a++; + if (status & WIN_B_UF_INT) + dc->stats.underflows_b++; + if (status & WIN_C_UF_INT) + dc->stats.underflows_c++; } if (status & V_BLANK_INT) { @@ -1336,9 +1445,9 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr) } return IRQ_HANDLED; -#else +#else /* CONFIG_TEGRA_FPGA_PLATFORM */ return IRQ_NONE; -#endif +#endif /* !CONFIG_TEGRA_FPGA_PLATFORM */ } static void tegra_dc_set_color_control(struct tegra_dc *dc) @@ -1609,6 +1718,39 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc) } } +void tegra_dc_stats_enable(struct tegra_dc *dc, bool enable) +{ +#if 0 /* underflow interrupt is already enabled by dc reset worker */ + u32 val; + if (dc->enabled) { + val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE); + if (enable) + val |= (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT); + else + val &= ~(WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT); + tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE); + } +#endif +} + +bool tegra_dc_stats_get(struct tegra_dc *dc) +{ +#if 0 /* right now it is always enabled */ + u32 val; + bool res; + + if (dc->enabled) { + val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE); + res = !!(val & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)); + } else { + res = false; + } + + return res; +#endif + return true; +} + static void _tegra_dc_disable(struct tegra_dc *dc) { _tegra_dc_controller_disable(dc); @@ -1806,7 +1948,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev) _tegra_dc_enable(dc); mutex_unlock(&dc->lock); - tegra_dc_dbg_add(dc); + tegra_dc_create_debugfs(dc); dev_info(&ndev->dev, "probed\n"); @@ -1866,6 +2008,7 @@ static int tegra_dc_remove(struct nvhost_device *ndev) struct tegra_dc *dc = nvhost_get_drvdata(ndev); tegra_dc_remove_sysfs(&dc->ndev->dev); + tegra_dc_remove_debugfs(dc); if (dc->overlay) { tegra_overlay_unregister(dc->overlay); @@ -1945,7 +2088,7 @@ static int tegra_dc_resume(struct nvhost_device *ndev) return 0; } -#endif +#endif /* CONFIG_PM */ extern int suspend_set(const char *val, struct kernel_param *kp) { diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index f5a5f24a5d4b..f6b560b98801 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -96,6 +96,17 @@ struct tegra_dc { struct completion vblank_complete; struct work_struct vblank_work; + + struct { + unsigned underflows; + unsigned underflows_a; + unsigned underflows_b; + unsigned underflows_c; + } stats; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugdir; +#endif }; static inline void tegra_dc_io_start(struct tegra_dc *dc) @@ -150,7 +161,12 @@ extern struct tegra_dc_out_ops tegra_dc_rgb_ops; extern struct tegra_dc_out_ops tegra_dc_hdmi_ops; extern struct tegra_dc_out_ops tegra_dc_dsi_ops; +/* defined in dc_sysfs.c, used by dc.c */ void __devexit tegra_dc_remove_sysfs(struct device *dev); void tegra_dc_create_sysfs(struct device *dev); + +/* defined in dc.c, used by dc_sysfs.c */ +void tegra_dc_stats_enable(struct tegra_dc *dc, bool enable); +bool tegra_dc_stats_get(struct tegra_dc *dc); #endif diff --git a/drivers/video/tegra/dc/dc_sysfs.c b/drivers/video/tegra/dc/dc_sysfs.c index fbe80c1d497a..4b107a52aa86 100644 --- a/drivers/video/tegra/dc/dc_sysfs.c +++ b/drivers/video/tegra/dc/dc_sysfs.c @@ -47,6 +47,46 @@ static ssize_t mode_show(struct device *device, static DEVICE_ATTR(mode, S_IRUGO, mode_show, NULL); +/******************* + * DC Stat Enabled * + *******************/ +static ssize_t stats_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvhost_device *ndev = to_nvhost_device(dev); + struct tegra_dc *dc = nvhost_get_drvdata(ndev); + bool enabled; + + if (mutex_lock_killable(&dc->lock)) + return -EINTR; + enabled = tegra_dc_stats_get(dc); + mutex_unlock(&dc->lock); + + return snprintf(buf, PAGE_SIZE, "%d", enabled); +} + +static ssize_t stats_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct nvhost_device *ndev = to_nvhost_device(dev); + struct tegra_dc *dc = nvhost_get_drvdata(ndev); + unsigned long val = 0; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + if (mutex_lock_killable(&dc->lock)) + return -EINTR; + tegra_dc_stats_enable(dc, !!val); + mutex_unlock(&dc->lock); + + return count; +} + +static DEVICE_ATTR(stats_enable, S_IRUGO|S_IWUSR|S_IWGRP, + stats_enable_show, stats_enable_store); + + /************** * DC Enabled * **************/ @@ -68,11 +108,12 @@ static ssize_t enable_store(struct device *dev, { struct nvhost_device *ndev = to_nvhost_device(dev); struct tegra_dc *dc = nvhost_get_drvdata(ndev); - int enabled; + unsigned long val = 0; - enabled = simple_strtoul(buf, NULL, 10); + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; - if (enabled) { + if (val) { tegra_dc_enable(dc); } else { tegra_dc_disable(dc); @@ -193,8 +234,11 @@ void __devexit tegra_dc_remove_sysfs(struct device *dev) struct nvhost_device *ndev = to_nvhost_device(dev); struct tegra_dc *dc = nvhost_get_drvdata(ndev); struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings; + + device_remove_file(dev, &dev_attr_stats_enable); device_remove_file(dev, &dev_attr_mode); device_remove_file(dev, &dev_attr_enable); + if (dc->out->stereo) { device_remove_file(dev, &dev_attr_stereo_orientation); device_remove_file(dev, &dev_attr_stereo_mode); @@ -214,6 +258,8 @@ void tegra_dc_create_sysfs(struct device *dev) error |= device_create_file(dev, &dev_attr_mode); error |= device_create_file(dev, &dev_attr_enable); + error |= device_create_file(dev, &dev_attr_stats_enable); + if (dc->out->stereo) { error |= device_create_file(dev, &dev_attr_stereo_orientation); error |= device_create_file(dev, &dev_attr_stereo_mode); diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c index 0bc1b4f68238..6157d1dff9fa 100644 --- a/drivers/video/tegra/dc/hdmi.c +++ b/drivers/video/tegra/dc/hdmi.c @@ -27,6 +27,8 @@ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/workqueue.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> #include <mach/clk.h> #include <mach/dc.h> @@ -61,7 +63,6 @@ #define HDMI_ELD_PRODUCT_CODE_INDEX 18 #define HDMI_ELD_MONITOR_NAME_INDEX 20 - struct tegra_dc_hdmi_data { struct tegra_dc *dc; struct tegra_edid *edid; @@ -430,14 +431,19 @@ static inline void tegra_hdmi_clrsetbits(struct tegra_dc_hdmi_data *hdmi, tegra_hdmi_writel(hdmi, val, reg); } +#ifdef CONFIG_DEBUG_FS +static int dbg_hdmi_show(struct seq_file *s, void *unused) +{ + struct tegra_dc_hdmi_data *hdmi = s->private; + #define DUMP_REG(a) do { \ - printk("HDMI %-32s\t%03x\t%08lx\n", \ + seq_printf(s, "%-32s\t%03x\t%08lx\n", \ #a, a, tegra_hdmi_readl(hdmi, a)); \ } while (0) -#ifdef DEBUG -static void hdmi_dumpregs(struct tegra_dc_hdmi_data *hdmi) -{ + tegra_dc_io_start(hdmi->dc); + clk_enable(hdmi->clk); + DUMP_REG(HDMI_CTXSW); DUMP_REG(HDMI_NV_PDISP_SOR_STATE0); DUMP_REG(HDMI_NV_PDISP_SOR_STATE1); @@ -592,7 +598,48 @@ static void hdmi_dumpregs(struct tegra_dc_hdmi_data *hdmi) DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_3); DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG); DUMP_REG(HDMI_NV_PDISP_KEY_SKEY_INDEX); +#undef DUMP_REG + + clk_disable(hdmi->clk); + tegra_dc_io_end(hdmi->dc); + + return 0; +} + +static int dbg_hdmi_open(struct inode *inode, struct file *file) +{ + return single_open(file, dbg_hdmi_show, inode->i_private); +} + +static const struct file_operations dbg_fops = { + .open = dbg_hdmi_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *hdmidir; + +static void tegra_dc_hdmi_debug_create(struct tegra_dc_hdmi_data *hdmi) +{ + struct dentry *retval; + + hdmidir = debugfs_create_dir("tegra_hdmi", NULL); + if (!hdmidir) + return; + retval = debugfs_create_file("regs", S_IRUGO, hdmidir, hdmi, + &dbg_fops); + if (!retval) + goto free_out; + return; +free_out: + debugfs_remove_recursive(hdmidir); + hdmidir = NULL; + return; } +#else +static inline void tegra_dc_hdmi_debug_create(struct tegra_dc_hdmi_data *hdmi) +{ } #endif #define PIXCLOCK_TOLERANCE 200 @@ -882,6 +929,9 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc) tegra_nvhdcp_set_policy(hdmi->nvhdcp, TEGRA_NVHDCP_POLICY_ALWAYS_ON); } + + tegra_dc_hdmi_debug_create(hdmi); + return 0; err_edid_destroy: |