summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorHeechul Yun <hyun@nvidia.com>2011-05-19 17:18:56 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:45:06 -0800
commit904190d6cb2691483d669aea623c6ed81482e206 (patch)
treefa4f08b763b9a758bf735125ccdce80ef6d9360e /drivers
parent0f1646f72c92e45dfcae77be124ab19408af4954 (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.c175
-rw-r--r--drivers/video/tegra/dc/dc_priv.h16
-rw-r--r--drivers/video/tegra/dc/dc_sysfs.c52
-rw-r--r--drivers/video/tegra/dc/hdmi.c60
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,
+ &regs_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: