From 712d1de6967c79b6dc227a5a573836b799bf5386 Mon Sep 17 00:00:00 2001 From: Heechul Yun Date: Thu, 19 May 2011 17:18:56 -0700 Subject: 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 Tested-by: Niket Sirsi --- drivers/video/tegra/dc/dc.c | 175 ++++++++++++++++++++++++++++++++++---- drivers/video/tegra/dc/dc_priv.h | 16 ++++ drivers/video/tegra/dc/dc_sysfs.c | 52 ++++++++++- 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 8d10d4cc0a69..5b5631d3aaf9 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -293,11 +293,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 @@ -325,25 +325,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) { @@ -914,10 +1015,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 inline void enable_dc_irq(unsigned int irq) { @@ -1274,11 +1375,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) { @@ -1355,9 +1464,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) @@ -1627,6 +1736,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); @@ -1841,7 +1983,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"); @@ -1901,6 +2043,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); @@ -1981,7 +2124,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 1cc489c622cb..d9488a93f986 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -99,6 +99,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) @@ -153,7 +164,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 21139cebad6f..2026c32fec09 100644 --- a/drivers/video/tegra/dc/hdmi.c +++ b/drivers/video/tegra/dc/hdmi.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -62,7 +64,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; @@ -433,14 +434,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); @@ -595,7 +601,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 @@ -891,6 +938,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: -- cgit v1.2.3