]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/video/tegra/dc/hdmi.c
tegra: dc: adding max pixclock check for hdmi
[linux-2.6.git] / drivers / video / tegra / dc / hdmi.c
index 0bc1b4f682387f7bd7f8fa59f5b569f8253bc789..666d1a6588b7526c2b83bfbb6e19c20d2a901632 100644 (file)
@@ -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
@@ -605,15 +652,32 @@ static bool tegra_dc_hdmi_mode_equal(const struct fb_videomode *mode1,
                mode1->vmode    == mode2->vmode;
 }
 
-static bool tegra_dc_hdmi_mode_filter(struct fb_videomode *mode)
+static bool tegra_dc_hdmi_valid_pixclock(const struct tegra_dc *dc,
+                                       const struct fb_videomode *mode)
+{
+       unsigned max_pixclock = tegra_dc_get_out_max_pixclock(dc);
+       if (max_pixclock) {
+               /* this might look counter-intuitive,
+                * but pixclock's unit is picos(not Khz)
+                */
+               return mode->pixclock >= max_pixclock;
+       } else {
+               return true;
+       }
+}
+
+static bool tegra_dc_hdmi_mode_filter(const struct tegra_dc *dc,
+                                       struct fb_videomode *mode)
 {
        int i;
        int clocks;
 
        for (i = 0; i < ARRAY_SIZE(tegra_dc_hdmi_supported_modes); i++) {
-               if (tegra_dc_hdmi_mode_equal(&tegra_dc_hdmi_supported_modes[i],
-                                            mode)) {
-                       memcpy(mode, &tegra_dc_hdmi_supported_modes[i], sizeof(*mode));
+               const struct fb_videomode *supported_mode
+                               = &tegra_dc_hdmi_supported_modes[i];
+               if (tegra_dc_hdmi_mode_equal(supported_mode, mode) &&
+                   tegra_dc_hdmi_valid_pixclock(dc, supported_mode)) {
+                       memcpy(mode, supported_mode, sizeof(*mode));
                        mode->flag = FB_MODE_IS_DETAILED;
                        clocks = (mode->left_margin + mode->xres + mode->right_margin + mode->hsync_len) *
                                (mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len);
@@ -836,7 +900,6 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc)
                err = -EBUSY;
                goto err_put_clock;
        }
-       enable_irq_wake(gpio_to_irq(dc->out->hotplug_gpio));
 
        hdmi->edid = tegra_edid_create(dc->out->dcc_bus);
        if (IS_ERR_OR_NULL(hdmi->edid)) {
@@ -882,12 +945,14 @@ 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:
        tegra_edid_destroy(hdmi->edid);
 err_free_irq:
-       disable_irq_wake(gpio_to_irq(dc->out->hotplug_gpio));
        free_irq(gpio_to_irq(dc->out->hotplug_gpio), dc);
 err_put_clock:
 #if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
@@ -917,7 +982,6 @@ static void tegra_dc_hdmi_destroy(struct tegra_dc *dc)
 {
        struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc);
 
-       disable_irq_wake(gpio_to_irq(dc->out->hotplug_gpio));
        free_irq(gpio_to_irq(dc->out->hotplug_gpio), dc);
        cancel_delayed_work_sync(&hdmi->work);
        iounmap(hdmi->base);