video: tegra: add hotplug display support
Erik Gilling [Tue, 24 Aug 2010 04:28:12 +0000 (21:28 -0700)]
This is needed for HDMI.

Signed-off-by: Erik Gilling <konkers@android.com>

arch/arm/mach-tegra/include/mach/dc.h
arch/arm/mach-tegra/include/mach/fb.h
drivers/video/tegra/Kconfig
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv.h
drivers/video/tegra/dc/dc_reg.h
drivers/video/tegra/dc/rgb.c
drivers/video/tegra/fb.c

index 38ea7b2..fc1911d 100644 (file)
@@ -57,6 +57,10 @@ enum {
 
 struct tegra_dc_out {
        int                     type;
+       unsigned                flags;
+
+       int                     dcc_bus;
+       int                     hotplug_gpio;
 
        unsigned                order;
        unsigned                align;
@@ -64,11 +68,14 @@ struct tegra_dc_out {
        struct tegra_dc_mode    *modes;
        int                     n_modes;
 
-       int     (*init)(void);
-       int     (*suspend)(pm_message_t state);
-       int     (*resume)(void);
+       int     (*enable)(void);
+       int     (*disable)(void);
 };
 
+#define TEGRA_DC_OUT_HOTPLUG_HIGH      (0 << 1)
+#define TEGRA_DC_OUT_HOTPLUG_LOW       (1 << 1)
+#define TEGRA_DC_OUT_HOTPLUG_MASK      (1 << 1)
+
 #define TEGRA_DC_ALIGN_MSB             0
 #define TEGRA_DC_ALIGN_LSB             1
 
@@ -143,6 +150,9 @@ struct tegra_dc_platform_data {
 struct tegra_dc *tegra_dc_get_dc(unsigned idx);
 struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win);
 
+void tegra_dc_enable(struct tegra_dc *dc);
+void tegra_dc_disable(struct tegra_dc *dc);
+
 /* tegra_dc_update_windows and tegra_dc_sync_windows do not support windows
  * with differenct dcs in one call
  */
@@ -152,6 +162,6 @@ int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n);
 /* will probably be replaced with an interface describing the window order */
 void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend);
 
-int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode);
+int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode);
 
 #endif
index 58a8f41..f41a049 100644 (file)
 #ifndef __MACH_TEGRA_FB_H
 #define __MACH_TEGRA_FB_H
 
+#include <linux/fb.h>
+
 #ifdef CONFIG_FB_TEGRA
 struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
                                        struct tegra_dc *dc,
                                        struct tegra_fb_data *fb_data,
                                        struct resource *fb_mem);
 void tegra_fb_unregister(struct tegra_fb_info *fb_info);
+void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
+                             struct fb_monspecs *specs,
+                             bool (*mode_filter)(struct fb_videomode *mode));
 #else
 static inline struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
                                                      struct tegra_dc *dc,
@@ -38,6 +43,12 @@ static inline struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev
 static inline void tegra_fb_unregister(struct tegra_fb_info *fb_info)
 {
 }
+
+void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
+                             struct fb_monspecs *specs,
+                             bool (*mode_filter)(struct fb_videomode *mode))
+{
+}
 #endif
 
 #endif
index 2610d13..ee3bf59 100644 (file)
@@ -9,6 +9,7 @@ config TEGRA_DC
        tristate "Tegra Display Contoller"
        depends on ARCH_TEGRA
        select FB_MODE_HELPERS
+       select I2C
        help
          Tegra display controller support.
 
index a28b7ed..397ade7 100644 (file)
@@ -117,19 +117,6 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
        int i;
        char buff[256];
 
-       DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT);
-       DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
-       DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_ERROR);
-       DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT);
-       DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL);
-       DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_ERROR);
-       DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT);
-       DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL);
-       DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_ERROR);
-       DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT);
-       DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL);
-       DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_ERROR);
-       DUMP_REG(DC_CMD_CONT_SYNCPT_VSYNC);
        DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0);
        DUMP_REG(DC_CMD_DISPLAY_COMMAND);
        DUMP_REG(DC_CMD_SIGNAL_RAISE);
@@ -368,12 +355,17 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
        struct tegra_dc *dc;
        unsigned long update_mask = GENERAL_ACT_REQ;
        unsigned long val;
-       unsigned long flags;
        int i;
 
        dc = windows[0]->dc;
 
-       spin_lock_irqsave(&dc->lock, flags);
+       mutex_lock(&dc->lock);
+
+       if (!dc->enabled) {
+               mutex_unlock(&dc->lock);
+               return -EFAULT;
+       }
+
        for (i = 0; i < n; i++) {
                struct tegra_dc_win *win = windows[i];
                unsigned h_dda;
@@ -441,7 +433,8 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
        tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
 
        tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
-       spin_unlock_irqrestore(&dc->lock, flags);
+
+       mutex_unlock(&dc->lock);
 
        return 0;
 }
@@ -466,6 +459,9 @@ int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
        if (n < 1 || n > DC_N_WINDOWS)
                return -EINVAL;
 
+       if (!windows[0]->dc->enabled)
+               return -EFAULT;
+
        return wait_event_interruptible_timeout(windows[0]->dc->wq,
                                         tegra_dc_windows_are_clean(windows, n),
                                         HZ);
@@ -489,7 +485,12 @@ void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend)
 }
 EXPORT_SYMBOL(tegra_dc_set_blending);
 
-int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
+void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
+{
+       /* clock setup for DSI/HDMI to go here */
+}
+
+static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
 {
        unsigned long val;
        unsigned long rate;
@@ -547,6 +548,14 @@ int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
 
        return 0;
 }
+
+
+int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
+{
+       memcpy(&dc->mode, mode, sizeof(dc->mode));
+
+       return 0;
+}
 EXPORT_SYMBOL(tegra_dc_set_mode);
 
 static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
@@ -554,10 +563,7 @@ static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
        dc->out = out;
 
        if (out->n_modes > 0)
-               dc->mode = &dc->out->modes[0];
-       else
-               dev_err(&dc->ndev->dev,
-                       "No default modes specified.  Leaving output disabled.\n");
+               tegra_dc_set_mode(dc, &dc->out->modes[0]);
 
        switch (out->type) {
        case TEGRA_DC_OUT_RGB:
@@ -568,6 +574,10 @@ static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
                dc->out_ops = NULL;
                break;
        }
+
+       if (dc->out_ops && dc->out_ops->init)
+               dc->out_ops->init(dc);
+
 }
 
 
@@ -575,11 +585,9 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
 {
        struct tegra_dc *dc = ptr;
        unsigned long status;
-       unsigned long flags;
        unsigned long val;
        int i;
 
-
        status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
        tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
 
@@ -587,7 +595,6 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
                int completed = 0;
                int dirty = 0;
 
-               spin_lock_irqsave(&dc->lock, flags);
                val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
                for (i = 0; i < DC_N_WINDOWS; i++) {
                        if (!(val & (WIN_A_ACT_REQ << i))) {
@@ -604,8 +611,6 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
                        tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
                }
 
-               spin_unlock_irqrestore(&dc->lock, flags);
-
                if (completed)
                        wake_up(&dc->wq);
        }
@@ -618,7 +623,7 @@ static void tegra_dc_init(struct tegra_dc *dc)
        tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
        if (dc->ndev->id == 0)
                tegra_dc_writel(dc, 0x0000011a, DC_CMD_CONT_SYNCPT_VSYNC);
-       else if (dc->ndev->id == 1)
+       else
                tegra_dc_writel(dc, 0x0000011b, DC_CMD_CONT_SYNCPT_VSYNC);
        tegra_dc_writel(dc, 0x00004700, DC_CMD_INT_TYPE);
        tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_POLARITY);
@@ -628,12 +633,67 @@ static void tegra_dc_init(struct tegra_dc *dc)
        tegra_dc_writel(dc, 0x0001c702, DC_CMD_INT_MASK);
        tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_ENABLE);
 
-       if (dc->mode)
-               tegra_dc_set_mode(dc, dc->mode);
+       if (dc->mode.pclk)
+               tegra_dc_program_mode(dc, &dc->mode);
+}
+
+static void _tegra_dc_enable(struct tegra_dc *dc)
+{
+       if (dc->out && dc->out->enable)
+               dc->out->enable();
 
+       tegra_dc_setup_clk(dc, dc->clk);
 
-       if (dc->out_ops && dc->out_ops->init)
-               dc->out_ops->init(dc);
+       clk_enable(dc->host1x_clk);
+       clk_enable(dc->clk);
+       tegra_periph_reset_deassert(dc->clk);
+       enable_irq(dc->irq);
+
+       tegra_dc_init(dc);
+
+       if (dc->out_ops && dc->out_ops->enable)
+               dc->out_ops->enable(dc);
+
+       tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
+}
+
+void tegra_dc_enable(struct tegra_dc *dc)
+{
+       mutex_lock(&dc->lock);
+
+       if (!dc->enabled) {
+               _tegra_dc_enable(dc);
+               dc->enabled = true;
+       }
+
+       mutex_unlock(&dc->lock);
+}
+
+static void _tegra_dc_disable(struct tegra_dc *dc)
+{
+       if (dc->out_ops && dc->out_ops->disable)
+               dc->out_ops->disable(dc);
+
+       disable_irq(dc->irq);
+       tegra_periph_reset_assert(dc->clk);
+       clk_disable(dc->clk);
+       clk_disable(dc->host1x_clk);
+
+       if (dc->out && dc->out->disable)
+               dc->out->disable();
+}
+
+
+void tegra_dc_disable(struct tegra_dc *dc)
+{
+       mutex_lock(&dc->lock);
+
+       if (dc->enabled) {
+               dc->enabled = false;
+               _tegra_dc_disable(dc);
+       }
+
+       mutex_unlock(&dc->lock);
 }
 
 static int tegra_dc_probe(struct nvhost_device *ndev)
@@ -688,9 +748,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
                goto err_release_resource_reg;
        }
 
-       res = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem");
-       if (res)
-               fb_mem = request_mem_region(res->start, resource_size(res), ndev->name);
+       fb_mem = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem");
 
        host1x_clk = clk_get(&ndev->dev, "host1x");
        if (IS_ERR_OR_NULL(host1x_clk)) {
@@ -698,7 +756,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
                ret = -ENOENT;
                goto err_iounmap_reg;
        }
-       clk_enable(host1x_clk);
 
        clk = clk_get(&ndev->dev, NULL);
        if (IS_ERR_OR_NULL(clk)) {
@@ -707,8 +764,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
 
                goto err_put_host1x_clk;
        }
-       clk_enable(clk);
-       tegra_periph_reset_deassert(clk);
 
        dc->clk = clk;
        dc->host1x_clk = host1x_clk;
@@ -717,9 +772,12 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
        dc->irq = irq;
        dc->ndev = ndev;
        dc->pdata = ndev->dev.platform_data;
-       spin_lock_init(&dc->lock);
-       init_waitqueue_head(&dc->wq);
 
+       if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED)
+               dc->enabled = true;
+
+       mutex_init(&dc->lock);
+       init_waitqueue_head(&dc->wq);
 
        dc->n_windows = DC_N_WINDOWS;
        for (i = 0; i < dc->n_windows; i++) {
@@ -734,46 +792,45 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
                goto err_put_clk;
        }
 
+       /* hack to ballence enable_irq calls in _tegra_dc_enable() */
+       disable_irq(dc->irq);
+
        ret = tegra_dc_add(dc, ndev->id);
        if (ret < 0) {
                dev_err(&ndev->dev, "can't add dc\n");
                goto err_free_irq;
        }
 
-       if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) {
-               if (dc->pdata->default_out)
-                       tegra_dc_set_out(dc, dc->pdata->default_out);
-               else
-                       dev_err(&ndev->dev, "No default output specified.  Leaving output disabled.\n");
-       }
-
-       tegra_dc_init(dc);
-       if (dc->out && dc->out->init)
-               dc->out->init();
+       nvhost_set_drvdata(ndev, dc);
 
-       tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
+       if (dc->pdata->default_out)
+               tegra_dc_set_out(dc, dc->pdata->default_out);
+       else
+               dev_err(&ndev->dev, "No default output specified.  Leaving output disabled.\n");
 
-       nvhost_set_drvdata(ndev, dc);
+       if (dc->enabled)
+               _tegra_dc_enable(dc);
 
        tegra_dc_dbg_add(dc);
 
        dev_info(&ndev->dev, "probed\n");
 
-       if (fb_mem && dc->pdata->fb) {
+       if (dc->pdata->fb) {
                dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem);
                if (IS_ERR_OR_NULL(dc->fb))
                        dc->fb = NULL;
        }
 
+       if (dc->out_ops && dc->out_ops->detect)
+               dc->out_ops->detect(dc);
+
        return 0;
 
 err_free_irq:
        free_irq(irq, dc);
 err_put_clk:
-       clk_disable(clk);
        clk_put(clk);
 err_put_host1x_clk:
-       clk_disable(host1x_clk);
        clk_put(host1x_clk);
 err_iounmap_reg:
        iounmap(base);
@@ -793,14 +850,16 @@ static int tegra_dc_remove(struct nvhost_device *ndev)
 
        if (dc->fb) {
                tegra_fb_unregister(dc->fb);
-               release_resource(dc->fb_mem);
+               if (dc->fb_mem)
+                       release_resource(dc->fb_mem);
        }
 
+
+       if (dc->enabled)
+               _tegra_dc_disable(dc);
+
        free_irq(dc->irq, dc);
-       tegra_periph_reset_assert(dc->clk);
-       clk_disable(dc->clk);
        clk_put(dc->clk);
-       clk_disable(dc->host1x_clk);
        clk_put(dc->host1x_clk);
        iounmap(dc->base);
        if (dc->fb_mem)
@@ -816,12 +875,8 @@ static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state)
 
        dev_info(&ndev->dev, "suspend\n");
 
-       if (dc->out && dc->out->suspend)
-               dc->out->suspend(state);
-
-       disable_irq(dc->irq);
-       tegra_periph_reset_assert(dc->clk);
-       clk_disable(dc->clk);
+       if (dc->enabled)
+               _tegra_dc_disable(dc);
 
        return 0;
 }
@@ -834,19 +889,14 @@ static int tegra_dc_resume(struct nvhost_device *ndev)
 
        dev_info(&ndev->dev, "resume\n");
 
-       clk_enable(dc->clk);
-       tegra_periph_reset_deassert(dc->clk);
-       enable_irq(dc->irq);
-
-       for (i = 0; i < dc->n_windows; i++)
-               wins[i] = &dc->windows[i];
+       if (dc->enabled) {
+               for (i = 0; i < dc->n_windows; i++)
+                       wins[i] = &dc->windows[i];
 
-       tegra_dc_init(dc);
-       if (dc->out && dc->out->resume)
-               dc->out->resume();
+               _tegra_dc_enable(dc);
 
-       tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
-       tegra_dc_update_windows(wins, dc->n_windows);
+               tegra_dc_update_windows(wins, dc->n_windows);
+       }
 
        return 0;
 }
@@ -900,4 +950,4 @@ static void __exit tegra_dc_module_exit(void)
 }
 
 module_exit(tegra_dc_module_exit);
-module_init(tegra_dc_module_init);
+late_initcall(tegra_dc_module_init);
index a2e961e..1bc1c77 100644 (file)
 #ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H
 #define __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H
 
-#include <linux/wait.h>
-#include <linux/list.h>
 #include <linux/io.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
 
 struct tegra_dc;
 
 struct tegra_dc_out_ops {
-       void (*init)(struct tegra_dc *dc);
+       /* initialize output.  dc clocks are not on at this point */
+       int (*init)(struct tegra_dc *dc);
+       /* destroy output.  dc clocks are not on at this point */
+       void (*destroy)(struct tegra_dc *dc);
+       /* detect connected display.  can sleep.*/
+       bool (*detect)(struct tegra_dc *dc);
+       /* enable output.  dc clocks are on at this point */
+       void (*enable)(struct tegra_dc *dc);
+       /* disable output.  dc clocks are on at this point */
+       void (*disable)(struct tegra_dc *dc);
 };
 
 struct tegra_dc {
@@ -41,17 +51,20 @@ struct tegra_dc {
        struct clk                      *clk;
        struct clk                      *host1x_clk;
 
+       bool                            enabled;
+
        struct tegra_dc_out             *out;
        struct tegra_dc_out_ops         *out_ops;
+       void                            *out_data;
 
-       struct tegra_dc_mode            *mode;
+       struct tegra_dc_mode            mode;
 
        struct tegra_dc_win             windows[DC_N_WINDOWS];
        int                             n_windows;
 
        wait_queue_head_t               wq;
 
-       spinlock_t                      lock;
+       struct mutex                    lock;
 
        struct resource                 *fb_mem;
        struct tegra_fb_info            *fb;
@@ -81,6 +94,19 @@ static inline void _tegra_dc_write_table(struct tegra_dc *dc, const u32 *table,
 #define tegra_dc_write_table(dc, table)                \
        _tegra_dc_write_table(dc, table, ARRAY_SIZE(table) / 2)
 
+static inline void tegra_dc_set_outdata(struct tegra_dc *dc, void *data)
+{
+       dc->out_data = data;
+}
+
+static inline void *tegra_dc_get_outdata(struct tegra_dc *dc)
+{
+       return dc->out_data;
+}
+
+void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk);
+
 extern struct tegra_dc_out_ops tegra_dc_rgb_ops;
+extern struct tegra_dc_out_ops tegra_dc_hdmi_ops;
 
 #endif
index 6d6b3ba..f7342cc 100644 (file)
 #define DC_COM_CRC_CHECKSUM_LATCHED            0x329
 
 #define DC_DISP_DISP_SIGNAL_OPTIONS0           0x400
+#define  H_PULSE_0_ENABLE              (1 << 8)
+#define  H_PULSE_1_ENABLE              (1 << 10)
+#define  H_PULSE_2_ENABLE              (1 << 12)
+#define  V_PULSE_0_ENABLE              (1 << 16)
+#define  V_PULSE_1_ENABLE              (1 << 18)
+#define  V_PULSE_2_ENABLE              (1 << 19)
+#define  V_PULSE_3_ENABLE              (1 << 20)
+#define  M0_ENABLE                     (1 << 24)
+#define  M1_ENABLE                     (1 << 26)
+
 #define DC_DISP_DISP_SIGNAL_OPTIONS1           0x401
+#define  DI_ENABLE                     (1 << 16)
+#define  PP_ENABLE                     (1 << 18)
+
 #define DC_DISP_DISP_WIN_OPTIONS               0x402
+#define  CURSOR_ENABLE                 (1 << 16)
+#define  TVO_ENABLE                    (1 << 28)
+#define  DSI_ENABLE                    (1 << 29)
+#define  HDMI_ENABLE                   (1 << 30)
+
 #define DC_DISP_MEM_HIGH_PRIORITY              0x403
 #define DC_DISP_MEM_HIGH_PRIORITY_TIMER                0x404
 #define DC_DISP_DISP_TIMING_OPTIONS            0x405
+#define  VSYNC_H_POSITION(x)           ((x) & 0xfff)
+
 #define DC_DISP_REF_TO_SYNC                    0x406
 #define DC_DISP_SYNC_WIDTH                     0x407
 #define DC_DISP_BACK_PORCH                     0x408
 #define DC_DISP_PP_SELECT_B                    0x42b
 #define DC_DISP_PP_SELECT_C                    0x42c
 #define DC_DISP_PP_SELECT_D                    0x42d
+
+#define  PULSE_MODE_NORMAL             (0 << 3)
+#define  PULSE_MODE_ONE_CLOCK          (1 << 3)
+#define  PULSE_POLARITY_HIGH           (0 << 4)
+#define  PULSE_POLARITY_LOW            (1 << 4)
+#define  PULSE_QUAL_ALWAYS             (0 << 6)
+#define  PULSE_QUAL_VACTIVE            (2 << 6)
+#define  PULSE_QUAL_VACTIVE1           (3 << 6)
+#define  PULSE_LAST_START_A            (0 << 8)
+#define  PULSE_LAST_END_A              (1 << 8)
+#define  PULSE_LAST_START_B            (2 << 8)
+#define  PULSE_LAST_END_B              (3 << 8)
+#define  PULSE_LAST_START_C            (4 << 8)
+#define  PULSE_LAST_END_C              (5 << 8)
+#define  PULSE_LAST_START_D            (6 << 8)
+#define  PULSE_LAST_END_D              (7 << 8)
+
+#define  PULSE_START(x)                        ((x) & 0xfff)
+#define  PULSE_END(x)                  (((x) & 0xfff) << 16)
+
 #define DC_DISP_DISP_CLOCK_CONTROL             0x42e
 #define  PIXEL_CLK_DIVIDER_PCD1                (0 << 8)
 #define  PIXEL_CLK_DIVIDER_PCD1H       (1 << 8)
 #define  DISP_DATA_ORDER_BLUE_RED      (1 << 9)
 
 #define DC_DISP_DISP_COLOR_CONTROL             0x430
+#define  BASE_COLOR_SIZE666            (0 << 0)
+#define  BASE_COLOR_SIZE111            (1 << 0)
+#define  BASE_COLOR_SIZE222            (2 << 0)
+#define  BASE_COLOR_SIZE333            (3 << 0)
+#define  BASE_COLOR_SIZE444            (4 << 0)
+#define  BASE_COLOR_SIZE555            (5 << 0)
+#define  BASE_COLOR_SIZE565            (6 << 0)
+#define  BASE_COLOR_SIZE332            (7 << 0)
+#define  BASE_COLOR_SIZE888            (8 << 0)
+
+#define  DITHER_CONTROL_DISABLE                (0 << 8)
+#define  DITHER_CONTROL_ORDERED                (2 << 8)
+#define  DITHER_CONTROL_ERRDIFF                (3 << 8)
+
 #define DC_DISP_SHIFT_CLOCK_OPTIONS            0x431
 #define DC_DISP_DATA_ENABLE_OPTIONS            0x432
 #define   DE_SELECT_ACTIVE_BLANK       0x0
index de1a8fa..c56e1f9 100644 (file)
@@ -46,7 +46,7 @@ static const u32 tegra_dc_rgb_pintable[] = {
 };
 
 
-void tegra_dc_rgb_init(struct tegra_dc *dc)
+void tegra_dc_rgb_enable(struct tegra_dc *dc)
 {
        tegra_dc_writel(dc, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
                        PW4_ENABLE | PM0_ENABLE | PM1_ENABLE,
@@ -58,6 +58,6 @@ void tegra_dc_rgb_init(struct tegra_dc *dc)
 }
 
 struct tegra_dc_out_ops tegra_dc_rgb_ops = {
-       .init = tegra_dc_rgb_init,
+       .enable = tegra_dc_rgb_enable,
 };
 
index c50c056..95c7c48 100644 (file)
@@ -35,6 +35,7 @@ struct tegra_fb_info {
        struct tegra_dc_win     *win;
        struct nvhost_device    *ndev;
        struct fb_info          *info;
+       bool                    valid;
 
        struct resource         *fb_mem;
 
@@ -69,12 +70,13 @@ static int tegra_fb_release(struct fb_info *info, int user)
 static int tegra_fb_check_var(struct fb_var_screeninfo *var,
                              struct fb_info *info)
 {
-       if ((var->xres != info->var.xres) ||
-           (var->yres != info->var.yres) ||
-           (var->xres_virtual != info->var.xres_virtual) ||
-           (var->yres_virtual != info->var.yres_virtual) ||
-           (var->grayscale != info->var.grayscale))
+       if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) >
+           info->screen_size)
                return -EINVAL;
+
+       /* double yres_virtual to allow double buffering through pan_display */
+       var->yres_virtual = var->yres * 2;
+
        return 0;
 }
 
@@ -104,13 +106,39 @@ static int tegra_fb_set_par(struct fb_info *info)
                var->blue.length = 5;
                tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
                break;
+
+       case 0:
+               break;
+
        default:
                return -EINVAL;
        }
        info->fix.line_length = var->xres * var->bits_per_pixel / 8;
 
-       tegra_dc_update_windows(&tegra_fb->win, 1);
+       if (var->pixclock) {
+               struct tegra_dc_mode mode;
 
+               info->mode = (struct fb_videomode *)
+                       fb_find_best_mode(var, &info->modelist);
+               if (!info->mode) {
+                       dev_warn(&tegra_fb->ndev->dev, "can't match video mode\n");
+                       return -EINVAL;
+               }
+
+               mode.pclk = PICOS2KHZ(info->mode->pixclock) * 1000;
+               mode.h_ref_to_sync = 1;
+               mode.v_ref_to_sync = 1;
+               mode.h_sync_width = info->mode->hsync_len;
+               mode.v_sync_width = info->mode->vsync_len;
+               mode.h_back_porch = info->mode->left_margin;
+               mode.v_back_porch = info->mode->upper_margin;
+               mode.h_active = info->mode->xres;
+               mode.v_active = info->mode->yres;
+               mode.h_front_porch = info->mode->right_margin;
+               mode.v_front_porch = info->mode->lower_margin;
+
+               tegra_dc_set_mode(tegra_fb->win->dc, &mode);
+       }
        return 0;
 }
 
@@ -136,6 +164,26 @@ static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
        return 0;
 }
 
+static int tegra_fb_blank(int blank, struct fb_info *info)
+{
+       struct tegra_fb_info *tegra_fb = info->par;
+
+       switch (blank) {
+       case FB_BLANK_UNBLANK:
+               dev_dbg(&tegra_fb->ndev->dev, "unblank\n");
+               tegra_dc_enable(tegra_fb->win->dc);
+               return 0;
+
+       case FB_BLANK_POWERDOWN:
+               dev_dbg(&tegra_fb->ndev->dev, "blank\n");
+               tegra_dc_disable(tegra_fb->win->dc);
+               return 0;
+
+       default:
+               return -ENOTTY;
+       }
+}
+
 static int tegra_fb_pan_display(struct fb_var_screeninfo *var,
                                struct fb_info *info)
 {
@@ -187,12 +235,47 @@ static struct fb_ops tegra_fb_ops = {
        .fb_check_var = tegra_fb_check_var,
        .fb_set_par = tegra_fb_set_par,
        .fb_setcolreg = tegra_fb_setcolreg,
+       .fb_blank = tegra_fb_blank,
        .fb_pan_display = tegra_fb_pan_display,
        .fb_fillrect = tegra_fb_fillrect,
        .fb_copyarea = tegra_fb_copyarea,
        .fb_imageblit = tegra_fb_imageblit,
 };
 
+void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
+                             struct fb_monspecs *specs,
+                             bool (*mode_filter)(struct fb_videomode *mode))
+{
+       struct fb_event event;
+       int i;
+
+       mutex_lock(&fb_info->info->lock);
+       fb_destroy_modedb(fb_info->info->monspecs.modedb);
+
+       memcpy(&fb_info->info->monspecs, specs,
+              sizeof(fb_info->info->monspecs));
+
+       fb_destroy_modelist(&fb_info->info->modelist);
+
+       for (i = 0; i < specs->modedb_len; i++) {
+               if (mode_filter) {
+                       if (mode_filter(&specs->modedb[i]))
+                               fb_add_videomode(&specs->modedb[i],
+                                                &fb_info->info->modelist);
+               } else {
+                       fb_add_videomode(&specs->modedb[i],
+                                        &fb_info->info->modelist);
+               }
+       }
+
+       fb_info->info->mode = (struct fb_videomode *)
+               fb_find_best_display(specs, &fb_info->info->modelist);
+
+       event.info = fb_info->info;
+       fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
+       mutex_unlock(&fb_info->info->lock);
+}
+
 struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
                                        struct tegra_dc *dc,
                                        struct tegra_fb_data *fb_data,
@@ -201,9 +284,8 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        struct tegra_dc_win *win;
        struct fb_info *info;
        struct tegra_fb_info *tegra_fb;
-       void __iomem *fb_base;
-       unsigned long fb_size;
-       unsigned long fb_phys;
+       void __iomem *fb_base = NULL;
+       unsigned long fb_size = 0;      unsigned long fb_phys = 0;
        int ret = 0;
 
        win = tegra_dc_get_window(dc, fb_data->win);
@@ -219,15 +301,6 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
                goto err;
        }
 
-       fb_size = resource_size(fb_mem);
-       fb_phys = fb_mem->start;
-       fb_base = ioremap_nocache(fb_phys, fb_size);
-       if (!fb_base) {
-               dev_err(&ndev->dev, "fb can't be mapped\n");
-               ret = -EBUSY;
-               goto err_free;
-       }
-
        tegra_fb = info->par;
        tegra_fb->win = win;
        tegra_fb->ndev = ndev;
@@ -236,6 +309,18 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        tegra_fb->yres = fb_data->yres;
        atomic_set(&tegra_fb->in_use, 0);
 
+       if (fb_mem) {
+               fb_size = resource_size(fb_mem);
+               fb_phys = fb_mem->start;
+               fb_base = ioremap_nocache(fb_phys, fb_size);
+               if (!fb_base) {
+                       dev_err(&ndev->dev, "fb can't be mapped\n");
+                       ret = -EBUSY;
+                       goto err_free;
+               }
+               tegra_fb->valid = true;
+       }
+
        info->fbops = &tegra_fb_ops;
        info->pseudo_palette = pseudo_palette;
        info->screen_base = fb_base;
@@ -253,13 +338,13 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        info->var.xres                  = fb_data->xres;
        info->var.yres                  = fb_data->yres;
        info->var.xres_virtual          = fb_data->xres;
-       info->var.yres_virtual          = fb_data->yres*2;
+       info->var.yres_virtual          = fb_data->yres * 2;
        info->var.bits_per_pixel        = fb_data->bits_per_pixel;
        info->var.activate              = FB_ACTIVATE_VBL;
        /* TODO: fill in the following by querying the DC */
        info->var.height                = -1;
        info->var.width                 = -1;
-       info->var.pixclock              = 24500;
+       info->var.pixclock              = 0;
        info->var.left_margin           = 0;
        info->var.right_margin          = 0;
        info->var.upper_margin          = 0;
@@ -279,7 +364,8 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
        win->virt_addr = fb_base;
        win->flags = TEGRA_WIN_FLAG_ENABLED | TEGRA_WIN_FLAG_COLOR_EXPAND;
 
-       tegra_fb_set_par(info);
+       if (fb_mem)
+               tegra_fb_set_par(info);
 
        if (register_framebuffer(info)) {
                dev_err(&ndev->dev, "failed to register framebuffer\n");