This commit adds a very basic DRM driver for NVIDIA Tegra SoCs. It
currently has rudimentary GEM support and can run a console on the
framebuffer as well as X using the xf86-video-modesetting driver.
Only the RGB output is supported. Quite a lot of things still need
to be worked out and there is a lot of room for cleanup.

Signed-off-by: Thierry Reding <thierry.reding at avionic-design.de>
---
 .../devicetree/bindings/gpu/drm/tegra.txt          |   24 +
 arch/arm/mach-tegra/board-dt-tegra20.c             |    3 +
 arch/arm/mach-tegra/tegra2_clocks.c                |    8 +-
 drivers/gpu/drm/Kconfig                            |    2 +
 drivers/gpu/drm/Makefile                           |    1 +
 drivers/gpu/drm/tegra/Kconfig                      |   10 +
 drivers/gpu/drm/tegra/Makefile                     |    5 +
 drivers/gpu/drm/tegra/tegra_drv.c                  | 2241 ++++++++++++++++++++
 drivers/gpu/drm/tegra/tegra_drv.h                  |  184 ++
 include/drm/tegra_drm.h                            |   44 +
 10 files changed, 2518 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/gpu/drm/tegra.txt
 create mode 100644 drivers/gpu/drm/tegra/Kconfig
 create mode 100644 drivers/gpu/drm/tegra/Makefile
 create mode 100644 drivers/gpu/drm/tegra/tegra_drv.c
 create mode 100644 drivers/gpu/drm/tegra/tegra_drv.h
 create mode 100644 include/drm/tegra_drm.h

diff --git a/Documentation/devicetree/bindings/gpu/drm/tegra.txt 
b/Documentation/devicetree/bindings/gpu/drm/tegra.txt
new file mode 100644
index 0000000..d39fe64
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/drm/tegra.txt
@@ -0,0 +1,24 @@
+Example:
+
+       drm at 54200000 {
+               compatible = "nvidia,tegra20-drm";
+               reg = < 0x54200000 0x00040000    /* display A */
+                       0x54240000 0x00040000    /* display B */
+                       0x58000000 0x02000000 >; /* GART aperture */
+               interrupts = < 0 73 0x04    /* display A */
+                              0 74 0x04 >; /* display B */
+
+               lvds {
+                       type = "rgb";
+                       size = <345 194>;
+
+                       default-mode {
+                               pixel-clock = <61715000>;
+                               vertical-refresh = <50>;
+                               resolution = <1366 768>;
+                               bits-per-pixel = <16>;
+                               horizontal-timings = <4 136 2 36>;
+                               vertical-timings = <2 4 21 10>;
+                       };
+               };
+       };
diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c 
b/arch/arm/mach-tegra/board-dt-tegra20.c
index bffba1b..6ce6162 100644
--- a/arch/arm/mach-tegra/board-dt-tegra20.c
+++ b/arch/arm/mach-tegra/board-dt-tegra20.c
@@ -67,6 +67,7 @@ struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
                       &tegra_ehci3_pdata),
        OF_DEV_AUXDATA("nvidia,tegra20-pwm", TEGRA_PWFM_BASE, "tegra-pwm", 
NULL),
        OF_DEV_AUXDATA("nvidia,tegra20-gart", TEGRA_MC_BASE, "tegra-gart", 
NULL),
+       OF_DEV_AUXDATA("nvidia,tegra20-drm", TEGRA_DISPLAY_BASE, "tegra-drm", 
NULL),
        {}
 };

@@ -81,6 +82,8 @@ static __initdata struct tegra_clk_init_table 
tegra_dt_clk_init_table[] = {
        { "cdev1",      NULL,           0,              true },
        { "i2s1",       "pll_a_out0",   11289600,       false},
        { "i2s2",       "pll_a_out0",   11289600,       false},
+       { "host1x",     "pll_c",        144000000,      true },
+       { "disp1",      "pll_p",        600000000,      true },
        { NULL,         NULL,           0,              0},
 };

diff --git a/arch/arm/mach-tegra/tegra2_clocks.c 
b/arch/arm/mach-tegra/tegra2_clocks.c
index f29084d..c86eae6 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -2219,8 +2219,8 @@ static struct clk tegra_list_clks[] = {
        PERIPH_CLK("tvo",       "tvo",                  NULL,   49,     0x188,  
250000000, mux_pllp_plld_pllc_clkm,     MUX | DIV_U71), /* requires min voltage 
*/
        PERIPH_CLK("hdmi",      "hdmi",                 NULL,   51,     0x18c,  
600000000, mux_pllp_plld_pllc_clkm,     MUX | DIV_U71), /* requires min voltage 
*/
        PERIPH_CLK("tvdac",     "tvdac",                NULL,   53,     0x194,  
250000000, mux_pllp_plld_pllc_clkm,     MUX | DIV_U71), /* requires min voltage 
*/
-       PERIPH_CLK("disp1",     "tegradc.0",            NULL,   27,     0x138,  
600000000, mux_pllp_plld_pllc_clkm,     MUX), /* scales with voltage and 
process_id */
-       PERIPH_CLK("disp2",     "tegradc.1",            NULL,   26,     0x13c,  
600000000, mux_pllp_plld_pllc_clkm,     MUX), /* scales with voltage and 
process_id */
+       PERIPH_CLK("disp1",     "tegra-drm",            NULL,   27,     0x138,  
600000000, mux_pllp_plld_pllc_clkm,     MUX), /* scales with voltage and 
process_id */
+       PERIPH_CLK("disp2",     "tegra-drm",            NULL,   26,     0x13c,  
600000000, mux_pllp_plld_pllc_clkm,     MUX), /* scales with voltage and 
process_id */
        PERIPH_CLK("usbd",      "fsl-tegra-udc",        NULL,   22,     0,      
480000000, mux_clk_m,                   0), /* requires min voltage */
        PERIPH_CLK("usb2",      "tegra-ehci.1",         NULL,   58,     0,      
480000000, mux_clk_m,                   0), /* requires min voltage */
        PERIPH_CLK("usb3",      "tegra-ehci.2",         NULL,   59,     0,      
480000000, mux_clk_m,                   0), /* requires min voltage */
@@ -2235,8 +2235,8 @@ static struct clk tegra_list_clks[] = {
        SHARED_CLK("avp.sclk",  "tegra-avp",            "sclk", 
&tegra_clk_sclk),
        SHARED_CLK("avp.emc",   "tegra-avp",            "emc",  &tegra_clk_emc),
        SHARED_CLK("cpu.emc",   "cpu",                  "emc",  &tegra_clk_emc),
-       SHARED_CLK("disp1.emc", "tegradc.0",            "emc",  &tegra_clk_emc),
-       SHARED_CLK("disp2.emc", "tegradc.1",            "emc",  &tegra_clk_emc),
+       SHARED_CLK("disp1.emc", "tegra-drm",            "emc",  &tegra_clk_emc),
+       SHARED_CLK("disp2.emc", "tegra-drm",            "emc",  &tegra_clk_emc),
        SHARED_CLK("hdmi.emc",  "hdmi",                 "emc",  &tegra_clk_emc),
        SHARED_CLK("host.emc",  "tegra_grhost",         "emc",  &tegra_clk_emc),
        SHARED_CLK("usbd.emc",  "fsl-tegra-udc",        "emc",  &tegra_clk_emc),
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index e354bc0..dd543f9 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -186,3 +186,5 @@ source "drivers/gpu/drm/vmwgfx/Kconfig"
 source "drivers/gpu/drm/gma500/Kconfig"

 source "drivers/gpu/drm/udl/Kconfig"
+
+source "drivers/gpu/drm/tegra/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index c20da5b..d417d7e 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -42,4 +42,5 @@ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
 obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
+obj-$(CONFIG_DRM_TEGRA) += tegra/
 obj-y                  += i2c/
diff --git a/drivers/gpu/drm/tegra/Kconfig b/drivers/gpu/drm/tegra/Kconfig
new file mode 100644
index 0000000..f3382c9
--- /dev/null
+++ b/drivers/gpu/drm/tegra/Kconfig
@@ -0,0 +1,10 @@
+config DRM_TEGRA
+       tristate "NVIDIA Tegra"
+       depends on DRM && ARCH_TEGRA
+       select DRM_KMS_ENCON
+       select DRM_KMS_HELPER
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
+       help
+         Choose this option if you have an NVIDIA Tegra SoC.
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
new file mode 100644
index 0000000..62c7e56a
--- /dev/null
+++ b/drivers/gpu/drm/tegra/Makefile
@@ -0,0 +1,5 @@
+ccflags-y := -Iinclude/drm
+
+tegra-drm-y := tegra_drv.o
+
+obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o
diff --git a/drivers/gpu/drm/tegra/tegra_drv.c 
b/drivers/gpu/drm/tegra/tegra_drv.c
new file mode 100644
index 0000000..2c691dc
--- /dev/null
+++ b/drivers/gpu/drm/tegra/tegra_drv.c
@@ -0,0 +1,2241 @@
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/iommu.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+
+#include <linux/shmem_fs.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fixed.h>
+#include <drm/tegra_drm.h>
+
+#include <mach/clk.h>
+#include <mach/iomap.h>
+
+#include "tegra_drv.h"
+
+#define DRIVER_NAME "tegra"
+#define DRIVER_DESC "NVIDIA Tegra graphics"
+#define DRIVER_DATE "20120330"
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
+
+static const unsigned int num_crtc = 1;
+
+struct tegra_gem_object;
+struct tegra_crtc_ops;
+
+struct tegra_crtc {
+       struct drm_crtc base;
+       int pipe;
+
+       struct clk *clk_emc;
+       struct clk *clk;
+
+       void __iomem *regs;
+       int irq;
+
+       struct drm_connector connector;
+       struct drm_encoder encoder;
+
+       struct tegra_drm_panel *panel;
+
+       const struct tegra_crtc_ops *ops;
+};
+
+static inline struct tegra_crtc *to_tegra_crtc(struct drm_crtc *crtc)
+{
+       return container_of(crtc, struct tegra_crtc, base);
+}
+
+struct tegra_crtc_ops {
+       int (*enable)(struct tegra_crtc *crtc);
+};
+
+static inline int tegra_crtc_enable(struct tegra_crtc *crtc)
+{
+       if (crtc && crtc->ops && crtc->ops->enable)
+               return crtc->ops->enable(crtc);
+
+       return crtc ? -ENOSYS : -EINVAL;
+}
+
+struct tegra_gem_object {
+       struct drm_gem_object base;
+       struct resource phys;
+       struct page **pages;
+       void *virt;
+};
+
+static inline struct tegra_gem_object *to_tegra_gem(struct drm_gem_object *obj)
+{
+       return container_of(obj, struct tegra_gem_object, base);
+}
+
+struct tegra_framebuffer {
+       struct drm_framebuffer base;
+       struct tegra_gem_object *obj;
+};
+
+static inline struct tegra_framebuffer *to_tegra_fb(struct drm_framebuffer *fb)
+{
+       return container_of(fb, struct tegra_framebuffer, base);
+}
+
+struct tegra_drm_private {
+       struct tegra_crtc crtc[2];
+       struct drm_encoder_connector *encon[2];
+       struct drm_fb_helper fb_helper;
+       struct tegra_framebuffer fb;
+       unsigned int num_crtcs;
+
+       struct iommu_domain *gart;
+       struct resource aperture;
+};
+
+static inline void tegra_crtc_writel(struct tegra_crtc *crtc, unsigned long 
value, unsigned long reg)
+{
+       writel(value, crtc->regs + (reg << 2));
+}
+
+static inline unsigned long tegra_crtc_readl(struct tegra_crtc *crtc, unsigned 
long reg)
+{
+       return readl(crtc->regs + (reg << 2));
+}
+
+static unsigned long pclk_best_div(unsigned long pclk, unsigned long rate)
+{
+       unsigned long div = DIV_ROUND_CLOSEST(rate * 2, pclk);
+       static const unsigned long max_pclk_khz = ULONG_MAX;
+
+       if (!div)
+               return 0;
+
+       while (rate * 2 / div > max_pclk_khz * 1000)
+               div++;
+
+       if (div < 2)
+               div = 2;
+
+       if (div > 257)
+               div = 257;
+
+       return div;
+}
+
+static unsigned long pclk_round_rate(struct clk *clk, unsigned long pclk, 
unsigned long *div)
+{
+       long rate = clk_round_rate(clk, pclk);
+
+       if (rate < 0)
+               rate = clk_get_rate(clk);
+
+       *div = pclk_best_div(pclk, rate);
+
+       return rate;
+}
+
+static int tegra_drmfb_create_handle(struct drm_framebuffer *fb, struct 
drm_file *filp, unsigned int *handle)
+{
+       struct tegra_framebuffer *privfb = to_tegra_fb(fb);
+       struct drm_device *drm = fb->dev;
+       int ret = 0;
+
+       dev_dbg(drm->dev, "> %s(fb=%p, filp=%p, handle=%p)\n", __func__, fb, 
filp, handle);
+
+       ret = drm_gem_handle_create(filp, &privfb->obj->base, handle);
+
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_drmfb_destroy(struct drm_framebuffer *fb)
+{
+       struct tegra_framebuffer *priv = to_tegra_fb(fb);
+       struct drm_gem_object *obj = &priv->obj->base;
+       struct drm_device *drm = fb->dev;
+
+       dev_dbg(drm->dev, "> %s(fb=%p)\n", __func__, fb);
+
+       drm_framebuffer_cleanup(fb);
+       drm_gem_object_unreference_unlocked(obj);
+
+       dev_dbg(drm->dev, "< %s()\n", __func__);
+}
+
+static const struct drm_framebuffer_funcs tegra_drmfb_funcs = {
+       .create_handle = tegra_drmfb_create_handle,
+       .destroy = tegra_drmfb_destroy,
+};
+
+static int tegra_fb_init(struct drm_device *drm, struct tegra_framebuffer *fb,
+                        struct drm_mode_fb_cmd2 *mode)
+{
+       int ret = 0;
+
+       dev_dbg(drm->dev, "> %s(drm=%p, fb=%p, mode=%p)\n", __func__, drm, fb, 
mode);
+
+       /* TODO: add sanity checks */
+
+       ret = drm_framebuffer_init(drm, &fb->base, &tegra_drmfb_funcs);
+       if (ret < 0) {
+               DRM_ERROR("framebuffer init failed %d\n", ret);
+               return ret;
+       }
+
+       ret = drm_helper_mode_fill_fb_struct(&fb->base, mode);
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static struct drm_framebuffer *tegra_drm_fb_create(struct drm_device *drm,
+                                                  struct drm_file *filp,
+                                                  struct drm_mode_fb_cmd2 *cmd)
+{
+       struct drm_framebuffer *drmfb = NULL;
+       struct tegra_framebuffer *fb = NULL;
+       struct drm_gem_object *obj;
+       u32 bpp, depth;
+       int err;
+
+       dev_dbg(drm->dev, "> %s(drm=%p, filp=%p, cmd=%p)\n", __func__, drm,
+               filp, cmd);
+
+       drm_fb_get_bpp_depth(cmd->pixel_format, &depth, &bpp);
+
+       obj = drm_gem_object_lookup(drm, filp, cmd->handles[0]);
+       if (!obj) {
+               drmfb = ERR_PTR(-ENOENT);
+               goto out;
+       }
+
+       fb = devm_kzalloc(drm->dev, sizeof(*fb), GFP_KERNEL);
+       if (!fb) {
+               drmfb = ERR_PTR(-ENOMEM);
+               goto out;
+       }
+
+       err = tegra_fb_init(drm, fb, cmd);
+       if (err < 0) {
+               devm_kfree(drm->dev, fb);
+               drmfb = ERR_PTR(err);
+               goto out;
+       }
+
+       fb->obj = to_tegra_gem(obj);
+       drmfb = &fb->base;
+
+out:
+       dev_dbg(drm->dev, "< %s() = %p\n", __func__, drmfb);
+       return drmfb;
+}
+
+static void tegra_drm_fb_output_poll_changed(struct drm_device *drm)
+{
+       dev_dbg(drm->dev, "> %s(drm=%p)\n", __func__, drm);
+       dev_dbg(drm->dev, "< %s()\n", __func__);
+}
+
+static struct drm_mode_config_funcs tegra_drm_mode_funcs = {
+       .fb_create = tegra_drm_fb_create,
+       .output_poll_changed = tegra_drm_fb_output_poll_changed,
+};
+
+static void tegra_crtc_save(struct drm_crtc *crtc)
+{
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p)\n", __func__, crtc);
+       dev_dbg(crtc->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_crtc_restore(struct drm_crtc *crtc)
+{
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p)\n", __func__, crtc);
+       dev_dbg(crtc->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
+                                uint32_t start, uint32_t size)
+{
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p, r=%p, g=%p, b=%p, start=%u, 
size=%u)\n",
+               __func__, crtc, r, g, b, start, size);
+       dev_dbg(crtc->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_crtc_destroy(struct drm_crtc *crtc)
+{
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p)\n", __func__, crtc);
+       dev_dbg(crtc->dev->dev, "< %s()\n", __func__);
+}
+
+static int tegra_crtc_page_flip(struct drm_crtc *crtc,
+                               struct drm_framebuffer *fb,
+                               struct drm_pending_vblank_event *event)
+{
+       int ret = 0;
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p, fb=%p, event=%p)\n", __func__, 
crtc, fb, event);
+       dev_dbg(crtc->dev->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static const struct drm_crtc_funcs tegra_crtc_funcs = {
+       .save = tegra_crtc_save,
+       .restore = tegra_crtc_restore,
+       .gamma_set = tegra_crtc_gamma_set,
+       .set_config = drm_crtc_helper_set_config,
+       .destroy = tegra_crtc_destroy,
+       .page_flip = tegra_crtc_page_flip,
+};
+
+static void tegra_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p, mode=%d)\n", __func__, crtc, 
mode);
+       dev_dbg(crtc->dev->dev, "< %s()\n", __func__);
+}
+
+static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted)
+{
+       bool ret = true;
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p, mode=%p, adjusted=%p)\n", 
__func__, crtc, mode, adjusted);
+       dev_dbg(crtc->dev->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+struct crtc_mode {
+       unsigned int width;
+       unsigned int height;
+       unsigned int href_to_sync;
+       unsigned int vref_to_sync;
+       unsigned int hsync_width;
+       unsigned int vsync_width;
+       unsigned int hback_porch;
+       unsigned int vback_porch;
+       unsigned int hfront_porch;
+       unsigned int vfront_porch;
+};
+
+struct crtc_win {
+       fixed20_12 x;
+       fixed20_12 y;
+       fixed20_12 w;
+       fixed20_12 h;
+       unsigned int outx;
+       unsigned int outy;
+       unsigned int outw;
+       unsigned int outh;
+       unsigned int stride;
+       unsigned int fmt;
+};
+
+static inline u32 compute_dda_inc(fixed20_12 inf, unsigned int out, bool v,
+                                 unsigned int bpp)
+{
+       fixed20_12 outf = dfixed_init(out);
+       u32 dda_inc;
+       int max;
+
+       if (v)
+               max = 15;
+       else {
+               switch (bpp) {
+               case 2:
+                       max = 8;
+                       break;
+
+               default:
+                       WARN_ON_ONCE(1);
+                       /* fallthrough */
+               case 4:
+                       max = 4;
+                       break;
+               }
+       }
+
+       outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
+       inf.full -= dfixed_const(1);
+
+       dda_inc = dfixed_div(inf, outf);
+       dda_inc = min_t(u32, dda_inc, dfixed_const(max));
+
+       return dda_inc;
+}
+
+static inline u32 compute_initial_dda(fixed20_12 in)
+{
+       return dfixed_frac(in);
+}
+
+static int tegra_crtc_set_timings(struct tegra_crtc *crtc, struct 
drm_display_mode *mode)
+{
+       unsigned int front_porch[2];
+       unsigned int back_porch[2];
+       unsigned int sync_width[2];
+       unsigned int active[2];
+       unsigned int reftos[2];
+
+       active[0] = mode->hdisplay;
+       active[1] = mode->vdisplay;
+       reftos[0] = 0;
+       reftos[1] = 0;
+       sync_width[0] = mode->hsync_end - mode->hsync_start;
+       sync_width[1] = mode->vsync_end - mode->vsync_start;
+       back_porch[0] = mode->hsync_start - mode->hdisplay;
+       back_porch[1] = mode->vsync_start - mode->vdisplay;
+       front_porch[0] = mode->htotal - mode->hsync_end;
+       front_porch[1] = mode->vtotal - mode->vsync_end;
+
+       tegra_crtc_writel(crtc, 0x0, DISP_TIMING_OPT);
+       tegra_crtc_writel(crtc, (reftos[1] << 16) | reftos[0], 
DISP_REF_TO_SYNC);
+       tegra_crtc_writel(crtc, (sync_width[1] << 16) | sync_width[0], 
DISP_SYNC_WIDTH);
+       tegra_crtc_writel(crtc, (back_porch[1] << 16) | back_porch[0], 
DISP_BACK_PORCH);
+       tegra_crtc_writel(crtc, (front_porch[1] << 16) | front_porch[0], 
DISP_FRONT_PORCH);
+
+       tegra_crtc_writel(crtc, (active[1] << 16) | active[0], DISP_ACTIVE);
+
+       return 0;
+}
+
+static int tegra_crtc_mode_set(struct drm_crtc *crtc,
+                              struct drm_display_mode *mode,
+                              struct drm_display_mode *adjusted,
+                              int x, int y, struct drm_framebuffer *old_fb)
+{
+       struct tegra_crtc *priv = container_of(crtc, struct tegra_crtc, base);
+       unsigned long pclk = mode->clock * 1000;
+       struct tegra_framebuffer *fb;
+       unsigned long update_mask;
+       unsigned long rate, div;
+       struct crtc_win win;
+       unsigned int h_dda;
+       unsigned int v_dda;
+       unsigned long val;
+       unsigned int bpp;
+       int ret = 0;
+
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p, mode=%p, adjusted=%p, x=%d, 
y=%d, old_fb=%p)\n",
+               __func__, crtc, mode, adjusted, x, y, old_fb);
+
+       fb = container_of(crtc->fb, struct tegra_framebuffer, base);
+       update_mask = CMD_STATE_CTRL_GENERAL_ACT_REQ;
+
+       rate = pclk_round_rate(priv->clk, pclk, &div);
+       dev_dbg(crtc->dev->dev, "  rate:%lu div:%lu\n", rate, div);
+       clk_set_rate(priv->clk, rate);
+
+       pclk = div ? (rate * 2 / div) : 0;
+       dev_dbg(crtc->dev->dev, "  pclk:%lu\n", pclk);
+
+       /* program display mode */
+       tegra_crtc_set_timings(priv, mode);
+
+       val = DISP_DATA_ENABLE_OPT_SELECT_ACTIVE |
+             DISP_DATA_ENABLE_OPT_CONTROL_NORMAL;
+       tegra_crtc_writel(priv, val, DISP_DATA_ENABLE_OPT);
+
+       val = tegra_crtc_readl(priv, COM_PIN_OUTPUT_POLARITY(1));
+       val &= ~COM_PIN_OUTPUT_POLARITY_PIN1_LVS_OUTPUT;
+       val &= ~COM_PIN_OUTPUT_POLARITY_PIN1_LHS_OUTPUT;
+       tegra_crtc_writel(priv, val, COM_PIN_OUTPUT_POLARITY(1));
+
+       val = DISP_INTERFACE_CTRL_DATA_FORMAT_DF1P1C |
+             DISP_INTERFACE_CTRL_ALIGN_MSB |
+             DISP_INTERFACE_CTRL_ORDER_RED_BLUE;
+       tegra_crtc_writel(priv, val, DISP_INTERFACE_CTRL);
+
+       tegra_crtc_writel(priv, 0x00010001, DISP_SHIFT_CLK_OPT);
+
+       val = DISP_CLK_CTRL_CLK_DIV(div - 2) | DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD1;
+       tegra_crtc_writel(priv, val, DISP_CLK_CTRL);
+
+       /* setup window parameters */
+       memset(&win, 0, sizeof(win));
+       win.x.full = dfixed_const(0);
+       win.y.full = dfixed_const(0);
+       win.w.full = dfixed_const(mode->hdisplay);
+       win.h.full = dfixed_const(mode->vdisplay);
+       win.outx = 0;
+       win.outy = 0;
+       win.outw = mode->hdisplay;
+       win.outh = mode->vdisplay;
+
+       switch (crtc->fb->pixel_format) {
+       case DRM_FORMAT_XRGB8888:
+               win.fmt = WIN_COLOR_DEPTH_R8G8B8A8;
+               break;
+
+       case DRM_FORMAT_RGB565:
+               win.fmt = WIN_COLOR_DEPTH_B5G6R5;
+               break;
+
+       default:
+               win.fmt = WIN_COLOR_DEPTH_R8G8B8A8;
+               WARN_ON(1);
+               break;
+       }
+
+       bpp = crtc->fb->bits_per_pixel / 8;
+       win.stride = win.outw * bpp;
+
+       /* program window registers */
+       val = tegra_crtc_readl(priv, CMD_WIN_HEADER);
+       val |= CMD_WIN_HEADER_WINDOW_A_SELECT;
+       tegra_crtc_writel(priv, val, CMD_WIN_HEADER);
+
+       tegra_crtc_writel(priv, win.fmt, WIN_COLOR_DEPTH);
+       tegra_crtc_writel(priv, 0, WIN_BYTE_SWAP);
+
+       val = WIN_POSITION_V(win.outy) | WIN_POSITION_H(win.outx);
+       tegra_crtc_writel(priv, val, WIN_POSITION);
+
+       val = WIN_SIZE_V(win.outh) | WIN_SIZE_H(win.outw);
+       tegra_crtc_writel(priv, val, WIN_SIZE);
+
+       val = WIN_PRESCALED_SIZE_V(dfixed_trunc(win.h)) |
+             WIN_PRESCALED_SIZE_H(dfixed_trunc(win.w) * bpp);
+       tegra_crtc_writel(priv, val, WIN_PRESCALED_SIZE);
+
+       h_dda = compute_dda_inc(win.w, win.outw, false, bpp);
+       v_dda = compute_dda_inc(win.h, win.outh, true, bpp);
+
+       val = WIN_DDA_INC_V(v_dda) | WIN_DDA_INC_H(h_dda);
+       tegra_crtc_writel(priv, val, WIN_DDA_INC);
+
+       h_dda = compute_initial_dda(win.x);
+       v_dda = compute_initial_dda(win.y);
+
+       tegra_crtc_writel(priv, h_dda, WIN_H_INITIAL_DDA);
+       tegra_crtc_writel(priv, v_dda, WIN_V_INITIAL_DDA);
+
+       tegra_crtc_writel(priv, 0, WIN_UV_BUF_STRIDE);
+       tegra_crtc_writel(priv, 0, WIN_BUF_STRIDE);
+
+       dev_dbg(crtc->dev->dev, "%s(): displaying GEM %p @%x\n", __func__,
+               fb->obj, fb->obj->phys.start);
+
+       tegra_crtc_writel(priv, fb->obj->phys.start, WINBUF_START_ADDR);
+       tegra_crtc_writel(priv, win.stride, WIN_LINE_STRIDE);
+       tegra_crtc_writel(priv, dfixed_trunc(win.x) * bpp, 
WINBUF_ADDR_H_OFFSET);
+       tegra_crtc_writel(priv, dfixed_trunc(win.y), WINBUF_ADDR_V_OFFSET);
+
+       val = WIN_OPT_ENABLE;
+
+       if (bpp < 24)
+               val |= WIN_OPT_COLOR_EXPAND;
+
+       tegra_crtc_writel(priv, val, WIN_OPT);
+
+       tegra_crtc_writel(priv, 0xff00, WIN_BLEND_NOKEY);
+       tegra_crtc_writel(priv, 0xff00, WIN_BLEND_1WIN);
+
+       update_mask |= CMD_STATE_CTRL_WIN_A_ACT_REQ;
+
+       tegra_crtc_writel(priv, update_mask << 8, CMD_STATE_CTRL);
+
+       val = tegra_crtc_readl(priv, CMD_INT_ENABLE);
+       val |= INT_FRAME_END;
+       tegra_crtc_writel(priv, val, CMD_INT_ENABLE);
+
+       val = tegra_crtc_readl(priv, CMD_INT_MASK);
+       val |= INT_FRAME_END;
+       tegra_crtc_writel(priv, val, CMD_INT_MASK);
+
+       tegra_crtc_writel(priv, update_mask, CMD_STATE_CTRL);
+
+       dev_dbg(crtc->dev->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+#if 0
+static int tegra_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+                                   struct drm_framebuffer *fb)
+{
+       struct tegra_framebuffer *privfb = to_tegra_fb(fb);
+       struct tegra_crtc *priv = to_tegra_crtc(crtc);
+       int ret = 0;
+
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p, x=%d, y=%d, fb=%p)\n", __func__, 
crtc, x, y,
+               fb);
+
+       tegra_crtc_writel(priv, privfb->phys, WINBUF_START_ADDR);
+
+       dev_dbg(crtc->dev->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+#else
+#define tegra_crtc_mode_set_base NULL
+#endif
+
+static void tegra_crtc_prepare(struct drm_crtc *crtc)
+{
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p)\n", __func__, crtc);
+       dev_dbg(crtc->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_crtc_commit(struct drm_crtc *crtc)
+{
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p)\n", __func__, crtc);
+       dev_dbg(crtc->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_crtc_load_lut(struct drm_crtc *crtc)
+{
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p)\n", __func__, crtc);
+       dev_dbg(crtc->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_crtc_disable(struct drm_crtc *crtc)
+{
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p)\n", __func__, crtc);
+       dev_dbg(crtc->dev->dev, "< %s()\n", __func__);
+}
+
+static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
+       .dpms = tegra_crtc_dpms,
+       .mode_fixup = tegra_crtc_mode_fixup,
+       .mode_set = tegra_crtc_mode_set,
+       .mode_set_base = tegra_crtc_mode_set_base,
+       .prepare = tegra_crtc_prepare,
+       .commit = tegra_crtc_commit,
+       .load_lut = tegra_crtc_load_lut,
+       .disable = tegra_crtc_disable,
+};
+
+#define PIN_REG_COUNT 4
+#define PIN_OUTPUT_SEL_COUNT 7
+
+static const u32 rgb_enable_tab[PIN_REG_COUNT] = {
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
+
+static const u32 rgb_polarity_tab[PIN_REG_COUNT] = {
+       0x00000000,
+       0x01000000,
+       0x00000000,
+       0x00000000,
+};
+
+static const u32 rgb_data_tab[PIN_REG_COUNT] = {
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+};
+
+static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = {
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00210222,
+       0x00002200,
+       0x00020000,
+};
+
+static int tegra_connector_get_modes(struct drm_connector *connector)
+{
+       struct tegra_crtc *crtc = container_of(connector, struct tegra_crtc, 
connector);
+       struct tegra_drm_panel *panel = crtc->panel;
+       unsigned int i;
+       int ret = 0;
+
+       dev_dbg(&connector->kdev, "> %s(connector=%p)\n", __func__, connector);
+
+       if (!panel) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       dev_dbg(&connector->kdev, "  panel: %d\n", panel->type);
+       dev_dbg(&connector->kdev, "    size: %ux%u\n", panel->width,
+               panel->height);
+       dev_dbg(&connector->kdev, "    modes: %u\n", panel->num_modes);
+
+       for (i = 0; i < panel->num_modes; i++) {
+               struct tegra_drm_mode *mode = &panel->modes[i];
+               struct drm_display_mode *display_mode;
+
+               display_mode = drm_mode_create(connector->dev);
+               if (!display_mode) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               display_mode->width_mm = panel->width;
+               display_mode->height_mm = panel->height;
+
+               display_mode->clock = mode->pixel_clock / 1000;
+               display_mode->vrefresh = mode->vrefresh;
+               display_mode->hdisplay = mode->width;
+               display_mode->vdisplay = mode->height;
+
+               display_mode->hsync_start = mode->width + mode->hback_porch;
+               display_mode->hsync_end = display_mode->hsync_start + 
mode->hsync_width;
+               display_mode->htotal = display_mode->hsync_end + 
mode->hfront_porch;
+
+               display_mode->vsync_start = mode->height + mode->vback_porch;
+               display_mode->vsync_end = display_mode->vsync_start + 
mode->vsync_width;
+               display_mode->vtotal = display_mode->vsync_end + 
mode->vfront_porch;
+
+               drm_mode_set_name(display_mode);
+               drm_mode_probed_add(connector, display_mode);
+       }
+
+       ret = panel->num_modes;
+
+out:
+       dev_dbg(&connector->kdev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static int tegra_connector_mode_valid(struct drm_connector *connector,
+                                     struct drm_display_mode *mode)
+{
+       int ret = MODE_OK;
+       dev_dbg(&connector->kdev, "> %s(connector=%p, mode=%p)\n", __func__, 
connector, mode);
+       dev_dbg(&connector->kdev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static struct drm_encoder *tegra_connector_best_encoder(struct drm_connector 
*connector)
+{
+       struct tegra_crtc *crtc = container_of(connector, struct tegra_crtc, 
connector);
+       struct drm_encoder *ret = &crtc->encoder;
+       dev_dbg(&connector->kdev, "> %s(connector=%p)\n", __func__, connector);
+       dev_dbg(&connector->kdev, "< %s() = %p\n", __func__, ret);
+       return ret;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+       .get_modes = tegra_connector_get_modes,
+       .mode_valid = tegra_connector_mode_valid,
+       .best_encoder = tegra_connector_best_encoder,
+};
+
+static void tegra_connector_dpms(struct drm_connector *connector, int mode)
+{
+       dev_dbg(&connector->kdev, "> %s(connector=%p, mode=%d)\n", __func__, 
connector, mode);
+       dev_dbg(&connector->kdev, "< %s()\n", __func__);
+}
+
+static void tegra_connector_save(struct drm_connector *connector)
+{
+       dev_dbg(&connector->kdev, "> %s(connector=%p)\n", __func__, connector);
+       dev_dbg(&connector->kdev, "< %s()\n", __func__);
+}
+
+static void tegra_connector_restore(struct drm_connector *connector)
+{
+       dev_dbg(&connector->kdev, "> %s(connector=%p)\n", __func__, connector);
+       dev_dbg(&connector->kdev, "< %s()\n", __func__);
+}
+
+static void tegra_connector_reset(struct drm_connector *connector)
+{
+       dev_dbg(&connector->kdev, "> %s(connector=%p)\n", __func__, connector);
+       dev_dbg(&connector->kdev, "< %s()\n", __func__);
+}
+
+static enum drm_connector_status tegra_connector_detect(struct drm_connector 
*connector, bool force)
+{
+       enum drm_connector_status status = connector_status_unknown;
+
+       dev_dbg(&connector->kdev, "> %s(connector=%p, force=%d)\n", __func__, 
connector, force);
+
+       if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+               status = connector_status_connected;
+
+       dev_dbg(&connector->kdev, "< %s() = %d\n", __func__, status);
+       return status;
+}
+
+static int tegra_connector_fill_modes(struct drm_connector *connector,
+                                     uint32_t max_width, uint32_t max_height)
+{
+       int ret = 0;
+
+       dev_dbg(&connector->kdev, "> %s(connector=%p, max_width=%u, 
max_height=%u)\n", __func__,
+               connector, max_width, max_height);
+
+       ret = drm_helper_probe_single_connector_modes(connector, max_width, 
max_height);
+
+       dev_dbg(&connector->kdev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static int tegra_connector_set_property(struct drm_connector *connector,
+                                       struct drm_property *property,
+                                       uint64_t value)
+{
+       int ret = 0;
+       dev_dbg(&connector->kdev, "> %s(connector=%p, property=%p, 
value=%llx)\n", __func__,
+               connector, property, value);
+       dev_dbg(&connector->kdev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_connector_destroy(struct drm_connector *connector)
+{
+       pr_debug("> %s(connector=%p)\n", __func__, connector);
+       drm_sysfs_connector_remove(connector);
+       drm_connector_cleanup(connector);
+       pr_debug("< %s()\n", __func__);
+}
+
+static void tegra_connector_force(struct drm_connector *connector)
+{
+       dev_dbg(&connector->kdev, "> %s(connector=%p)\n", __func__, connector);
+       dev_dbg(&connector->kdev, "< %s()\n", __func__);
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+       .dpms = tegra_connector_dpms,
+       .save = tegra_connector_save,
+       .restore = tegra_connector_restore,
+       .reset = tegra_connector_reset,
+
+       .detect = tegra_connector_detect,
+       .fill_modes = tegra_connector_fill_modes,
+       .set_property = tegra_connector_set_property,
+       .destroy = tegra_connector_destroy,
+       .force = tegra_connector_force,
+};
+
+static void tegra_encoder_reset(struct drm_encoder *encoder)
+{
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p)\n", __func__, encoder);
+       dev_dbg(encoder->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_encoder_destroy(struct drm_encoder *encoder)
+{
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p)\n", __func__, encoder);
+       dev_dbg(encoder->dev->dev, "< %s()\n", __func__);
+}
+
+static const struct drm_encoder_funcs encoder_funcs = {
+       .reset = tegra_encoder_reset,
+       .destroy = tegra_encoder_destroy,
+};
+
+static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p, mode=%d)\n", __func__, 
encoder, mode);
+       dev_dbg(encoder->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_encoder_save(struct drm_encoder *encoder)
+{
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p)\n", __func__, encoder);
+       dev_dbg(encoder->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_encoder_restore(struct drm_encoder *encoder)
+{
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p)\n", __func__, encoder);
+       dev_dbg(encoder->dev->dev, "< %s()\n", __func__);
+}
+
+static bool tegra_encoder_mode_fixup(struct drm_encoder *encoder,
+                                    struct drm_display_mode *mode,
+                                    struct drm_display_mode *adjusted)
+{
+       bool ret = true;
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p, mode=%p, adjusted=%p)\n", 
__func__, encoder, mode, adjusted);
+       dev_dbg(encoder->dev->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_encoder_prepare(struct drm_encoder *encoder)
+{
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p)\n", __func__, encoder);
+       dev_dbg(encoder->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_encoder_commit(struct drm_encoder *encoder)
+{
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p)\n", __func__, encoder);
+       dev_dbg(encoder->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_encoder_mode_set(struct drm_encoder *encoder,
+                                  struct drm_display_mode *mode,
+                                  struct drm_display_mode *adjusted)
+{
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p, mode=%p, adjusted=%p)\n", 
__func__, encoder,
+               mode, adjusted);
+       dev_dbg(encoder->dev->dev, "< %s()\n", __func__);
+}
+
+static struct drm_crtc *tegra_encoder_get_crtc(struct drm_encoder *encoder)
+{
+       struct tegra_crtc *crtc = container_of(encoder, struct tegra_crtc, 
encoder);
+       struct drm_crtc *ret = &crtc->base;
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p)\n", __func__, encoder);
+       dev_dbg(encoder->dev->dev, "< %s() = %p\n", __func__, ret);
+       return ret;
+}
+
+static enum drm_connector_status tegra_encoder_detect(struct drm_encoder 
*encoder,
+                                                     struct drm_connector 
*connector)
+{
+       enum drm_connector_status status = connector_status_unknown;
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p, connector=%p)\n", 
__func__, encoder, connector);
+       dev_dbg(encoder->dev->dev, "< %s() = %d\n", __func__, status);
+       return status;
+}
+
+static void tegra_encoder_disable(struct drm_encoder *encoder)
+{
+       dev_dbg(encoder->dev->dev, "> %s(encoder=%p)\n", __func__, encoder);
+       dev_dbg(encoder->dev->dev, "< %s()\n", __func__);
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+       .dpms = tegra_encoder_dpms,
+       .save = tegra_encoder_save,
+       .restore = tegra_encoder_restore,
+       .mode_fixup = tegra_encoder_mode_fixup,
+       .prepare = tegra_encoder_prepare,
+       .commit = tegra_encoder_commit,
+       .mode_set = tegra_encoder_mode_set,
+       .get_crtc = tegra_encoder_get_crtc,
+       .detect = tegra_encoder_detect,
+       .disable = tegra_encoder_disable,
+};
+
+static int tegra_crtc_rgb_enable(struct tegra_crtc *crtc)
+{
+       unsigned int i;
+
+       /* enable RGB output */
+       for (i = 0; i < PIN_REG_COUNT; i++) {
+               tegra_crtc_writel(crtc, rgb_enable_tab[i], 
COM_PIN_OUTPUT_ENABLE(i));
+               tegra_crtc_writel(crtc, rgb_polarity_tab[i], 
COM_PIN_OUTPUT_POLARITY(i));
+               tegra_crtc_writel(crtc, rgb_data_tab[i], 
COM_PIN_OUTPUT_DATA(i));
+       }
+
+       for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++)
+               tegra_crtc_writel(crtc, rgb_sel_tab[i], COM_PIN_OUTPUT_SEL(i));
+
+       return 0;
+}
+
+static const struct tegra_crtc_ops tegra_crtc_rgb_ops = {
+       .enable = tegra_crtc_rgb_enable,
+};
+
+static int tegra_crtc_init(struct drm_device *drm, unsigned int pipe)
+{
+       struct tegra_drm_platform_data *pdata = drm->dev->platform_data;
+       unsigned int syncpt = pipe ? SYNCPT_VBLANK1 : SYNCPT_VBLANK0;
+       struct tegra_drm_private *priv = drm->dev_private;
+       struct tegra_crtc *crtc = &priv->crtc[pipe];
+       struct tegra_drm_panel *panel;
+       unsigned long val;
+       int connector;
+       int encoder;
+       int ret = 0;
+
+       dev_dbg(drm->dev, "> %s(drm=%p, pipe=%u)\n", __func__, drm, pipe);
+
+       if (pipe >= pdata->num_panels) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       panel = crtc->panel = &pdata->panels[pipe];
+
+       switch (panel->type) {
+       case TEGRA_DRM_PANEL_RGB:
+               connector = DRM_MODE_CONNECTOR_LVDS;
+               encoder = DRM_MODE_ENCODER_LVDS;
+               crtc->ops = &tegra_crtc_rgb_ops;
+               break;
+
+       default:
+               connector = DRM_MODE_CONNECTOR_Unknown;
+               encoder = DRM_MODE_ENCODER_NONE;
+               break;
+       }
+
+       drm_connector_helper_add(&crtc->connector, &connector_helper_funcs);
+       drm_connector_init(drm, &crtc->connector, &connector_funcs, connector);
+
+       drm_encoder_init(drm, &crtc->encoder, &encoder_funcs, encoder);
+       drm_encoder_helper_add(&crtc->encoder, &encoder_helper_funcs);
+
+       drm_mode_connector_attach_encoder(&crtc->connector,
+                                         &crtc->encoder);
+       drm_sysfs_connector_add(&crtc->connector);
+
+       crtc->encoder.possible_crtcs = 1 << pipe;
+
+       /* hardware initialization */
+
+       clk_enable(crtc->clk);
+       clk_enable(crtc->clk_emc);
+       tegra_periph_reset_deassert(crtc->clk);
+       msleep(10);
+
+       /* initialize display controller */
+       tegra_crtc_writel(crtc, 0x00000100, CMD_GENERAL_INCR_SYNCPT_CTRL);
+       tegra_crtc_writel(crtc, 0x100 | syncpt, CMD_CONT_SYNCPT_VSYNC);
+
+       val = INT_WIN_A_UF | INT_WIN_B_UF | INT_WIN_C_UF | INT_WIN_A_OF;
+       tegra_crtc_writel(crtc, val, CMD_INT_TYPE);
+
+       val = INT_WIN_A_UF | INT_WIN_B_UF | INT_WIN_C_UF |
+             INT_WIN_A_OF | INT_WIN_B_OF | INT_WIN_C_OF;
+       tegra_crtc_writel(crtc, val, CMD_INT_POLARITY);
+
+       val = CMD_DISP_POWER_CTRL_PW0_ENABLE | CMD_DISP_POWER_CTRL_PW1_ENABLE |
+             CMD_DISP_POWER_CTRL_PW2_ENABLE | CMD_DISP_POWER_CTRL_PW3_ENABLE |
+             CMD_DISP_POWER_CTRL_PW4_ENABLE | CMD_DISP_POWER_CTRL_PM0_ENABLE |
+             CMD_DISP_POWER_CTRL_PM1_ENABLE;
+       tegra_crtc_writel(crtc, val, CMD_DISP_POWER_CTRL);
+
+       val = tegra_crtc_readl(crtc, CMD_DISP_CMD);
+       val |= CMD_DISP_CMD_CTRL_MODE_C_DISPLAY;
+       tegra_crtc_writel(crtc, val, CMD_DISP_CMD);
+
+       /* initialize timer */
+       tegra_crtc_writel(crtc, 0x00202020, DISP_MEM_HIGH_PRI);
+       tegra_crtc_writel(crtc, 0x00010101, DISP_MEM_HIGH_PRI_TIMER);
+
+       val = INT_VBLANK | INT_WIN_A_UF | INT_WIN_B_UF | INT_WIN_C_UF;
+       tegra_crtc_writel(crtc, val, CMD_INT_MASK);
+
+       val = INT_VBLANK | INT_WIN_A_UF | INT_WIN_B_UF | INT_WIN_C_UF;
+       tegra_crtc_writel(crtc, val, CMD_INT_ENABLE);
+
+       ret = tegra_crtc_enable(crtc);
+       if (ret < 0)
+               goto out;
+
+       drm_crtc_init(drm, &crtc->base, &tegra_crtc_funcs);
+       drm_mode_crtc_set_gamma_size(&crtc->base, 256);
+       drm_crtc_helper_add(&crtc->base, &tegra_crtc_helper_funcs);
+
+out:
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+                              u16 blue, int regno)
+{
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p, red=%u, green=%u, blue=%u, 
regno=%d)\n",
+               __func__, crtc, red, green, blue, regno);
+       dev_dbg(crtc->dev->dev, "< %s()\n", __func__);
+}
+
+static void tegra_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+                              u16 *blue, int regno)
+{
+       dev_dbg(crtc->dev->dev, "> %s(crtc=%p, red=%p, green=%p, blue=%p, 
regno=%d)\n",
+               __func__, crtc, red, green, blue, regno);
+       dev_dbg(crtc->dev->dev, "< %s()\n", __func__);
+}
+
+static struct fb_ops tegra_fb_ops = {
+       .owner = THIS_MODULE,
+       .fb_check_var = drm_fb_helper_check_var,
+       .fb_set_par = drm_fb_helper_set_par,
+       .fb_fillrect = cfb_fillrect,
+       .fb_copyarea = cfb_copyarea,
+       .fb_imageblit = cfb_imageblit,
+       .fb_pan_display = drm_fb_helper_pan_display,
+       .fb_blank = drm_fb_helper_blank,
+       .fb_setcmap = drm_fb_helper_setcmap,
+       .fb_debug_enter = drm_fb_helper_debug_enter,
+       .fb_debug_leave = drm_fb_helper_debug_leave,
+};
+
+static int tegra_gem_get_pages(struct drm_device *drm,
+                              struct tegra_gem_object *obj, gfp_t gfp)
+{
+       struct address_space *mapping;
+       unsigned int num_pages;
+       struct inode *inode;
+       struct page *page;
+       unsigned int i;
+       int ret = 0;
+
+       dev_dbg(drm->dev, "> %s(drm=%p, obj=%p, gfp=%x)\n", __func__, drm,
+               obj, gfp);
+
+       if (obj->pages)
+               goto out;
+
+       num_pages = obj->base.size / PAGE_SIZE;
+
+       obj->pages = drm_malloc_ab(num_pages, sizeof(page));
+       if (!obj->pages) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       inode = obj->base.filp->f_path.dentry->d_inode;
+       mapping = inode->i_mapping;
+       gfp |= mapping_gfp_mask(mapping);
+
+       for (i = 0; i < num_pages; i++) {
+               page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+               if (IS_ERR(page))
+                       goto err_pages;
+
+               obj->pages[i] = page;
+       }
+
+       ret = 0;
+       goto out;
+
+err_pages:
+       while (i--)
+               page_cache_release(obj->pages[i]);
+
+       ret = PTR_ERR(page);
+       drm_free_large(obj->pages);
+       obj->pages = NULL;
+out:
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_gem_put_pages(struct drm_device *drm,
+                               struct tegra_gem_object *obj)
+{
+       unsigned int num = obj->base.size / PAGE_SIZE;
+
+       dev_dbg(drm->dev, "> %s(drm=%p, obj=%p)\n", __func__, drm, obj);
+
+       while (num--)
+               page_cache_release(obj->pages[num]);
+
+       drm_free_large(obj->pages);
+
+       dev_dbg(drm->dev, "< %s()\n", __func__);
+}
+
+static int tegra_gem_map(struct drm_device *drm, struct tegra_gem_object *obj)
+{
+       unsigned int num_pages = obj->base.size / PAGE_SIZE;
+       int ret = -ENOSYS;
+       pgprot_t prot;
+
+       dev_dbg(drm->dev, "> %s(drm=%p, obj=%p)\n", __func__, drm, obj);
+
+       ret = tegra_gem_get_pages(drm, obj, GFP_KERNEL);
+       if (ret < 0)
+               goto out;
+
+       dev_dbg(drm->dev, "  num_pages: %u\n", num_pages);
+
+       prot = pgprot_writecombine(pgprot_kernel);
+
+       obj->virt = vmap(obj->pages, num_pages, 0, prot);
+       if (!obj->virt) {
+               tegra_gem_put_pages(drm, obj);
+               ret = -ENOMEM;
+       }
+
+       dev_dbg(drm->dev, "  virt: %p\n", obj->virt);
+
+out:
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_gem_unmap(struct drm_device *drm,
+                           struct tegra_gem_object *obj)
+{
+       dev_dbg(drm->dev, "> %s(drm=%p, obj=%p)\n", __func__, drm, obj);
+
+       vunmap(obj->virt);
+       tegra_gem_put_pages(drm, obj);
+
+       dev_dbg(drm->dev, "< %s()\n", __func__);
+}
+
+static int tegra_gem_gart_map(struct drm_device *drm,
+                             struct tegra_gem_object *obj)
+{
+       unsigned int num_pages = obj->base.size / PAGE_SIZE;
+       struct tegra_drm_private *priv = drm->dev_private;
+       resource_size_t min = priv->aperture.start;
+       resource_size_t max = priv->aperture.end;
+       resource_size_t size = obj->base.size;
+       phys_addr_t iova;
+       unsigned int i;
+       int ret = 0;
+
+       dev_dbg(drm->dev, "> %s(drm=%p, obj=%p)\n", __func__, drm, obj);
+
+       if (!priv->gart) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = allocate_resource(&priv->aperture, &obj->phys, size, min, max,
+                               PAGE_SIZE, NULL, NULL);
+       if (ret < 0)
+               goto out;
+
+       dev_dbg(drm->dev, "  allocation: %#x-%#x\n", obj->phys.start,
+               obj->phys.end);
+       iova = obj->phys.start;
+
+       for (i = 0; i < num_pages; i++) {
+               struct page *page = obj->pages[i];
+               phys_addr_t phys;
+               int err;
+
+               phys = page_to_phys(page);
+
+               err = iommu_map(priv->gart, iova, phys, PAGE_SIZE, 0);
+               if (err < 0)
+                       dev_err(drm->dev, "iommu_map(): %d\n", err);
+
+               iova += PAGE_SIZE;
+       }
+
+out:
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_gem_gart_unmap(struct drm_device *drm,
+                                struct tegra_gem_object *obj)
+{
+       struct tegra_drm_private *priv = drm->dev_private;
+       unsigned int num = obj->base.size / PAGE_SIZE;
+       phys_addr_t iova = obj->phys.start;
+
+       dev_dbg(drm->dev, "> %s(drm=%p, obj=%p)\n", __func__, drm, obj);
+
+       while (num--) {
+               iommu_unmap(priv->gart, iova, PAGE_SIZE);
+               iova += PAGE_SIZE;
+       }
+
+       release_resource(&obj->phys);
+
+       dev_dbg(drm->dev, "< %s()\n", __func__);
+}
+
+static struct tegra_gem_object *tegra_gem_alloc(struct drm_device *drm,
+                                               size_t size)
+{
+       struct tegra_gem_object *obj;
+       int err;
+
+       dev_dbg(drm->dev, "> %s(drm=%p, size=%zu)\n", __func__, drm, size);
+
+       obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+       if (!obj)
+               goto out;
+
+       err = drm_gem_object_init(drm, &obj->base, size);
+       if (err < 0) {
+               kfree(obj);
+               goto out;
+       }
+
+       err = tegra_gem_map(drm, obj);
+       if (err < 0) {
+               dev_err(drm->dev, "tegra_gem_vmap(): %d\n", err);
+               kfree(obj);
+               obj = NULL;
+               goto out;
+       }
+
+       err = tegra_gem_gart_map(drm, obj);
+       if (err < 0) {
+               dev_err(drm->dev, "tegra_gem_gart_map(): %d\n", err);
+               tegra_gem_unmap(drm, obj);
+               kfree(obj);
+               obj = NULL;
+               goto out;
+       }
+
+       dev_dbg(drm->dev, "%s(): GEM allocated: %p @%#x/%p\n", __func__,
+               obj, obj->phys.start, obj->virt);
+
+out:
+       dev_dbg(drm->dev, "< %s() = %p\n", __func__, obj);
+       return obj;
+}
+
+static void tegra_gem_free(struct drm_device *drm,
+                          struct tegra_gem_object *obj)
+{
+       dev_dbg(drm->dev, "> %s(drm=%p, obj=%p)\n", __func__, drm, obj);
+
+       tegra_gem_gart_unmap(drm, obj);
+       tegra_gem_unmap(drm, obj);
+       drm_gem_object_release(&obj->base);
+       kfree(obj);
+
+       dev_dbg(drm->dev, "< %s()\n", __func__);
+}
+
+static int tegra_fb_create(struct drm_fb_helper *helper,
+                          struct drm_fb_helper_surface_size *sizes)
+{
+       struct drm_device *drm = helper->dev;
+       struct tegra_drm_private *priv = drm->dev_private;
+       struct drm_framebuffer *drmfb;
+       struct drm_mode_fb_cmd2 mode;
+       struct tegra_gem_object *obj;
+       struct fb_info *info;
+       size_t size;
+       int ret = 0;
+       u32 depth;
+       u32 bpp;
+
+       dev_dbg(drm->dev, "> %s(helper=%p, sizes=%p)\n", __func__, helper, 
sizes);
+       dev_dbg(drm->dev, "  sizes:\n");
+       dev_dbg(drm->dev, "    fb: %ux%u\n", sizes->fb_width, sizes->fb_height);
+       dev_dbg(drm->dev, "    surface: %ux%u (bpp:%u, depth=%u)\n",
+                       sizes->surface_width, sizes->surface_height,
+                       sizes->surface_bpp, sizes->surface_depth);
+
+       mode.width = sizes->surface_width;
+       mode.height = sizes->surface_height;
+
+       depth = sizes->surface_depth;
+       bpp = sizes->surface_bpp;
+
+       if (bpp == 24)
+               bpp = 32;
+
+       mode.pitches[0] = mode.width * DIV_ROUND_UP(bpp, 8);
+       size = mode.pitches[0] * mode.height;
+       size = ALIGN(size, PAGE_SIZE);
+
+       info = framebuffer_alloc(0, drm->dev);
+       if (!info) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       info->par = helper;
+
+       dev_dbg(drm->dev, "  bpp:%u depth:%u\n", bpp, depth);
+
+       mode.pixel_format = drm_mode_legacy_fb_format(bpp, depth);
+
+       ret = tegra_fb_init(drm, &priv->fb, &mode);
+       if (ret < 0)
+               goto out;
+
+       strcpy(info->fix.id, "tegra-drm-fb");
+
+       drmfb = &priv->fb.base;
+       priv->fb_helper.fb = drmfb;
+       priv->fb_helper.fbdev = info;
+
+       info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
+       info->fbops = &tegra_fb_ops;
+
+       obj = tegra_gem_alloc(drm, size);
+       if (!obj) {
+               dev_err(drm->dev, "tegra_gem_alloc_object() failed\n");
+               return -ENOMEM;
+       }
+
+       dev_dbg(drm->dev, "  GEM object: %p\n", obj);
+
+       info->screen_base = obj->virt;
+       priv->fb.obj = obj;
+
+       memset(info->screen_base, 0, size);
+
+       info->fix.smem_start = priv->fb.obj->phys.start;
+       info->fix.smem_len = size;
+
+       ret = fb_alloc_cmap(&info->cmap, 256, 0);
+       if (ret < 0) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       info->screen_size = size;
+
+       drm_fb_helper_fill_fix(info, drmfb->pitches[0], drmfb->depth);
+       drm_fb_helper_fill_var(info, &priv->fb_helper, sizes->fb_width,
+                              sizes->fb_height);
+
+       dev_info(drm->dev, "allocated %ux%u\n", drmfb->width, drmfb->height);
+
+out:
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static int tegra_fb_probe(struct drm_fb_helper *helper, struct 
drm_fb_helper_surface_size *sizes)
+{
+       int ret = 0;
+
+       dev_dbg(helper->dev->dev, "> %s(helper=%p, sizes=%p)\n", __func__, 
helper, sizes);
+
+       if (!helper->fb) {
+               ret = tegra_fb_create(helper, sizes);
+               if (ret == 0)
+                       ret = 1;
+       }
+
+       dev_dbg(helper->dev->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
+       .gamma_set = tegra_fb_gamma_set,
+       .gamma_get = tegra_fb_gamma_get,
+       .fb_probe = tegra_fb_probe,
+};
+
+static int tegra_drm_fb_init(struct drm_device *drm)
+{
+       struct tegra_drm_private *priv = drm->dev_private;
+       unsigned int i;
+       int ret = 0;
+
+       dev_dbg(drm->dev, "> %s(drm=%p)\n", __func__, drm);
+
+       drm_mode_config_init(drm);
+
+       drm->mode_config.min_width = 0;
+       drm->mode_config.min_height = 0;
+
+       drm->mode_config.max_width = 4096;
+       drm->mode_config.max_height = 4096;
+
+       drm->mode_config.funcs = &tegra_drm_mode_funcs;
+
+       drm->mode_config.fb_base = 0xdeadbeef;
+
+       for (i = 0; i < num_crtc; i++) {
+               ret = tegra_crtc_init(drm, i);
+               if (ret < 0)
+                       goto out;
+
+               priv->num_crtcs++;
+       }
+
+       priv->fb_helper.funcs = &tegra_fb_helper_funcs;
+
+       ret = drm_fb_helper_init(drm, &priv->fb_helper, priv->num_crtcs,
+                                priv->num_crtcs);
+       if (ret < 0)
+               goto out;
+
+       ret = drm_fb_helper_single_add_all_connectors(&priv->fb_helper);
+       if (ret < 0)
+               goto out;
+
+       ret = drm_fb_helper_initial_config(&priv->fb_helper, 32);
+       if (ret < 0)
+               goto out;
+
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE
+       ret = drm_fb_helper_restore_fbdev_mode(&priv->fb_helper);
+       if (ret < 0)
+               goto out;
+#endif
+
+       ret = 0;
+
+out:
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static irqreturn_t tegra_drm_irq(int irq, void *data)
+{
+       struct tegra_crtc *crtc = data;
+       unsigned long underflow;
+       unsigned long status;
+
+       dev_dbg(&crtc->connector.kdev, "> %s(irq=%d, data=%p)\n", __func__, 
irq, data);
+
+       status = tegra_crtc_readl(crtc, CMD_INT_STATUS);
+       tegra_crtc_writel(crtc, status, CMD_INT_STATUS);
+
+       dev_dbg(&crtc->connector.kdev, "  status: %#lx\n", status);
+
+       if (status & INT_FRAME_END)
+               dev_dbg(&crtc->connector.kdev, "%s(): frame end\n", __func__);
+
+       if (status & INT_VBLANK) {
+               dev_dbg(&crtc->connector.kdev, "%s(): V-blank\n", __func__);
+               drm_handle_vblank(crtc->connector.dev, crtc->pipe);
+       }
+
+       underflow = INT_WIN_A_UF | INT_WIN_B_UF | INT_WIN_C_UF;
+
+       if (status & underflow)
+               dev_dbg(&crtc->connector.kdev, "%s(): underflow\n", __func__);
+
+       dev_dbg(&crtc->connector.kdev, "< %s()\n", __func__);
+       return IRQ_HANDLED;
+}
+
+static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
+{
+       struct platform_device *pdev = drm->platformdev;
+       struct tegra_drm_private *priv;
+       unsigned int i;
+       int ret = 0;
+
+       dev_dbg(drm->dev, "> %s(drm=%p, flags=%lx)\n", __func__, drm, flags);
+
+       priv = devm_kzalloc(drm->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (drm->dev->of_node) {
+               ret = of_address_to_resource(drm->dev->of_node, 2,
+                                            &priv->aperture);
+               if (ret < 0) {
+                       dev_err(drm->dev, "of_address_to_resource(): %d\n", 
ret);
+                       return ret;
+               }
+       }
+
+       if (iommu_present(drm->dev->bus)) {
+               priv->gart = iommu_domain_alloc(drm->dev->bus);
+               if (!priv->gart) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               ret = iommu_attach_device(priv->gart, drm->dev);
+               if (ret < 0) {
+                       dev_err(drm->dev, "iommu_domain_attach_device(): %d\n", 
ret);
+                       goto out;
+               }
+       }
+
+       drm->dev_private = priv;
+
+       for (i = 0; i < num_crtc; i++) {
+               struct tegra_crtc *crtc = &priv->crtc[i];
+               struct resource *regs;
+
+               dev_dbg(drm->dev, "  initializing CRTC %u: %p\n", i, crtc);
+
+               crtc->clk = clk_get(drm->dev, NULL);
+               if (IS_ERR_OR_NULL(crtc->clk)) {
+                       DRM_ERROR("failed to get clock\n");
+                       ret = -ENXIO;
+                       goto out;
+               }
+
+               crtc->clk_emc = clk_get(drm->dev, "emc");
+               if (IS_ERR_OR_NULL(crtc->clk_emc)) {
+                       DRM_ERROR("failed to get EMC clock\n");
+                       ret = -ENXIO;
+                       goto out;
+               }
+
+               regs = platform_get_resource(pdev, IORESOURCE_MEM, i);
+               if (!regs) {
+                       DRM_ERROR("failed to get registers\n");
+                       ret = -ENXIO;
+                       goto out;
+               }
+
+               crtc->regs = devm_request_and_ioremap(drm->dev, regs);
+               if (!crtc->regs) {
+                       DRM_ERROR("failed to remap registers\n");
+                       ret = -ENXIO;
+                       goto out;
+               }
+
+               crtc->irq = platform_get_irq(pdev, i);
+               if (crtc->irq < 0) {
+                       DRM_ERROR("failed to get IRQ\n");
+                       ret = -ENXIO;
+                       goto out;
+               }
+
+               crtc->pipe = i;
+
+               ret = devm_request_irq(drm->dev, crtc->irq, tegra_drm_irq,
+                                      0, "tegra-drm", crtc);
+               if (ret < 0) {
+                       DRM_ERROR("devm_request_irq(): %d\n", ret);
+                       goto out;
+               }
+       }
+
+       ret = tegra_drm_fb_init(drm);
+       if (ret < 0) {
+               dev_dbg(drm->dev, "  tegra_drm_fb_init(): %d\n", ret);
+               goto out;
+       }
+
+       drm_kms_helper_poll_init(drm);
+
+out:
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_drm_fbdev_fini(struct drm_device *drm)
+{
+       struct tegra_drm_private *priv = drm->dev_private;
+       struct drm_fb_helper *fb_helper = &priv->fb_helper;
+
+       dev_dbg(drm->dev, "> %s(drm=%p)\n", __func__, drm);
+       dev_dbg(drm->dev, "  fbdev: %p\n", fb_helper->fb);
+
+       if (fb_helper->fbdev) {
+               struct fb_info *info = fb_helper->fbdev;
+               int err;
+
+               dev_dbg(drm->dev, "  unregistering framebuffer...\n");
+
+               err = unregister_framebuffer(info);
+               if (err < 0)
+                       dev_dbg(drm->dev, "unregister_framebuffer(): %d\n", 
err);
+
+               dev_dbg(drm->dev, "  done\n");
+
+               if (info->cmap.len) {
+                       dev_dbg(drm->dev, "  deallocating cmap...\n");
+                       fb_dealloc_cmap(&info->cmap);
+                       dev_dbg(drm->dev, "  done\n");
+               }
+
+               dev_dbg(drm->dev, "  releasing framebuffer...\n");
+               framebuffer_release(info);
+               dev_dbg(drm->dev, "  done\n");
+       }
+
+       dev_dbg(drm->dev, "  finalizing DRM FB helper...\n");
+       drm_fb_helper_fini(fb_helper);
+       dev_dbg(drm->dev, "  done\n");
+
+       dev_dbg(drm->dev, "< %s()\n", __func__);
+}
+
+static int tegra_drm_unload(struct drm_device *drm)
+{
+       struct tegra_drm_private *priv = drm->dev_private;
+       int ret = 0;
+
+       dev_dbg(drm->dev, "> %s(drm=%p)\n", __func__, drm);
+
+       tegra_drm_fbdev_fini(drm);
+
+       dev_dbg(drm->dev, "  calling drm_kms_helper_poll_fini()...\n");
+       drm_kms_helper_poll_fini(drm);
+       dev_dbg(drm->dev, "  done\n");
+
+       dev_dbg(drm->dev, "  calling drm_mode_config_cleanup()...\n");
+       drm_mode_config_cleanup(drm);
+       dev_dbg(drm->dev, "  done\n");
+
+       if (priv->gart) {
+               iommu_detach_device(priv->gart, drm->dev);
+               iommu_domain_free(priv->gart);
+       }
+
+       devm_kfree(drm->dev, priv);
+
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
+{
+       int ret = 0;
+       dev_dbg(drm->dev, "> %s(drm=%p, filp=%p)\n", __func__, drm, filp);
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_drm_lastclose(struct drm_device *drm)
+{
+       struct tegra_drm_private *priv = drm->dev_private;
+       int err;
+
+       dev_dbg(drm->dev, "> %s(drm=%p)\n", __func__, drm);
+
+       err = drm_fb_helper_restore_fbdev_mode(&priv->fb_helper);
+       dev_dbg(drm->dev, "  drm_fb_helper_restore_fbdev_mode(): %d\n", err);
+
+       dev_dbg(drm->dev, "< %s()\n", __func__);
+}
+
+static int tegra_drm_suspend(struct drm_device *drm, pm_message_t state)
+{
+       int ret = -ENOSYS;
+       dev_dbg(drm->dev, "> %s(drm=%p, state=[%d])\n", __func__, drm, 
state.event);
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static int tegra_drm_resume(struct drm_device *drm)
+{
+       int ret = -ENOSYS;
+       dev_dbg(drm->dev, "> %s(drm=%p)\n", __func__, drm);
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static int tegra_drm_enable_vblank(struct drm_device *drm, int crtc)
+{
+       int ret = -ENOSYS;
+       dev_dbg(drm->dev, "> %s(drm=%p, crtc=%d)\n", __func__, drm, crtc);
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_drm_disable_vblank(struct drm_device *drm, int crtc)
+{
+       dev_dbg(drm->dev, "> %s(drm=%p, crtc=%d)\n", __func__, drm, crtc);
+       dev_dbg(drm->dev, "< %s()\n", __func__);
+}
+
+static int tegra_gem_handle_create(struct drm_device *drm,
+                                  struct drm_file *file, size_t size,
+                                  unsigned long flags, u32 *handle)
+{
+       struct tegra_gem_object *obj;
+       int err = 0;
+
+       dev_dbg(drm->dev, "> %s(drm=%p, file=%p, size=%zu, flags=%#lx, 
handle=%p)\n",
+               __func__, drm, file, size, flags, handle);
+
+       obj = tegra_gem_alloc(drm, size);
+       if (!obj) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = drm_gem_handle_create(file, &obj->base, handle);
+       if (err < 0) {
+               tegra_gem_free(drm, obj);
+               goto out;
+       }
+
+       drm_gem_object_unreference(&obj->base);
+
+out:
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, err);
+       return err;
+}
+
+static inline unsigned int align_pitch(unsigned int pitch, unsigned int width, 
unsigned int bpp)
+{
+       return max(pitch, width * DIV_ROUND_UP(bpp, 8));
+}
+
+static int tegra_gem_dumb_create(struct drm_file *file,
+                                struct drm_device *drm,
+                                struct drm_mode_create_dumb *args)
+{
+       int ret = -ENOSYS;
+       dev_dbg(drm->dev, "> %s(file=%p, drm=%p, args=%p)\n", __func__, file, 
drm, args);
+
+       args->pitch = align_pitch(args->pitch, args->width, args->bpp);
+       args->size = PAGE_ALIGN(args->pitch * args->height);
+
+       ret = tegra_gem_handle_create(drm, file, args->size, 0, &args->handle);
+
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static int tegra_gem_dumb_map_offset(struct drm_file *file,
+                                    struct drm_device *drm,
+                                    uint32_t handle, uint64_t *offset)
+{
+       struct tegra_gem_object *gem;
+       struct drm_gem_object *obj;
+       int ret = 0;
+
+       dev_dbg(drm->dev, "> %s(file=%p, drm=%p, handle=%x, offset=%p)\n", 
__func__, file, drm, handle, offset);
+
+       mutex_lock(&drm->struct_mutex);
+
+       obj = drm_gem_object_lookup(drm, file, handle);
+       if (!obj) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       gem = to_tegra_gem(obj);
+
+       ret = tegra_gem_get_pages(drm, gem, GFP_KERNEL);
+       if (ret < 0)
+               goto unref;
+
+       if (!obj->map_list.map) {
+               ret = drm_gem_create_mmap_offset(obj);
+               if (ret < 0)
+                       goto unref;
+       }
+
+       *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
+
+unref:
+       drm_gem_object_unreference(obj);
+out:
+       mutex_unlock(&drm->struct_mutex);
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static int tegra_gem_dumb_destroy(struct drm_file *file,
+                                 struct drm_device *drm,
+                                 uint32_t handle)
+{
+       int ret = -ENOSYS;
+       dev_dbg(drm->dev, "> %s(file=%p, drm=%p, handle=%x)\n", __func__, file, 
drm, handle);
+       ret = drm_gem_handle_delete(file, handle);
+       dev_dbg(drm->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static struct drm_ioctl_desc tegra_drm_ioctls[] = {
+};
+
+static int tegra_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       int ret = 0;
+
+       pr_debug("> %s(filp=%p, vma=%p)\n", __func__, filp, vma);
+
+       ret = drm_gem_mmap(filp, vma);
+       if (ret < 0)
+               goto out;
+
+       vma->vm_flags &= ~VM_PFNMAP;
+       vma->vm_flags |= VM_MIXEDMAP;
+
+out:
+       pr_debug("< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static const struct file_operations tegra_drm_fops = {
+       .owner = THIS_MODULE,
+       .open = drm_open,
+       .release = drm_release,
+       .unlocked_ioctl = drm_ioctl,
+       .mmap = tegra_drm_gem_mmap,
+       .poll = drm_poll,
+       .fasync = drm_fasync,
+       .read = drm_read,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = tegra_compat_ioctl,
+#endif
+       .llseek = noop_llseek,
+};
+
+static int tegra_debugfs_init(struct drm_minor *minor)
+{
+       int ret = 0;
+       dev_dbg(minor->dev->dev, "> %s(minor=%p)\n", __func__, minor);
+       dev_dbg(minor->dev->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_debugfs_cleanup(struct drm_minor *minor)
+{
+       dev_dbg(minor->dev->dev, "> %s(minor=%p)\n", __func__, minor);
+       dev_dbg(minor->dev->dev, "< %s()\n", __func__);
+}
+
+static int tegra_gem_init_object(struct drm_gem_object *obj)
+{
+       int ret = -ENOSYS;
+       dev_dbg(obj->dev->dev, "> %s(obj=%p)\n", __func__, obj);
+       dev_dbg(obj->dev->dev, "< %s() = %d\n", __func__, ret);
+       return ret;
+}
+
+static void tegra_gem_free_object(struct drm_gem_object *obj)
+{
+       struct tegra_gem_object *gem = to_tegra_gem(obj);
+       struct drm_device *drm = obj->dev;
+
+       dev_dbg(drm->dev, "> %s(obj=%p)\n", __func__, obj);
+       dev_dbg(drm->dev, "  map_list: %p\n", obj->map_list.map);
+
+       if (obj->map_list.map)
+               drm_gem_free_mmap_offset(obj);
+
+       tegra_gem_free(obj->dev, gem);
+
+       dev_dbg(drm->dev, "< %s()\n", __func__);
+}
+
+static int tegra_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+       struct drm_gem_object *obj = vma->vm_private_data;
+       struct tegra_gem_object *gem = to_tegra_gem(obj);
+       pgoff_t page_offset;
+       struct page *page;
+       int ret;
+
+       if (!gem->pages)
+               return VM_FAULT_SIGBUS;
+
+       page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start)
+               >> PAGE_SHIFT;
+       page = gem->pages[page_offset];
+
+       ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page);
+
+       switch (ret) {
+       case -EAGAIN:
+               set_need_resched();
+               /* fallthrough */
+       case 0:
+       case -ERESTARTSYS:
+       case -EINTR:
+               return VM_FAULT_NOPAGE;
+
+       case -ENOMEM:
+               return VM_FAULT_OOM;
+       }
+
+       return VM_FAULT_SIGBUS;
+}
+
+static struct vm_operations_struct tegra_gem_vm_ops = {
+       .fault = tegra_gem_fault,
+       .open = drm_gem_vm_open,
+       .close = drm_gem_vm_close,
+};
+
+static struct drm_driver drm_driver = {
+       .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM,
+       .load = tegra_drm_load,
+       .unload = tegra_drm_unload,
+       .open = tegra_drm_open,
+       .lastclose = tegra_drm_lastclose,
+
+       .suspend = tegra_drm_suspend,
+       .resume = tegra_drm_resume,
+
+       .enable_vblank = tegra_drm_enable_vblank,
+       .disable_vblank = tegra_drm_disable_vblank,
+       .reclaim_buffers = drm_core_reclaim_buffers,
+
+#ifdef CONFIG_DEBUG_FS
+       .debugfs_init = tegra_debugfs_init,
+       .debugfs_cleanup = tegra_debugfs_cleanup,
+#endif
+
+       .gem_init_object = tegra_gem_init_object,
+       .gem_free_object = tegra_gem_free_object,
+       .gem_vm_ops = &tegra_gem_vm_ops,
+
+       .dumb_create = tegra_gem_dumb_create,
+       .dumb_map_offset = tegra_gem_dumb_map_offset,
+       .dumb_destroy = tegra_gem_dumb_destroy,
+
+       .ioctls = tegra_drm_ioctls,
+       .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
+       .fops = &tegra_drm_fops,
+
+       .name = DRIVER_NAME,
+       .desc = DRIVER_DESC,
+       .date = DRIVER_DATE,
+       .major = DRIVER_MAJOR,
+       .minor = DRIVER_MINOR,
+       .patchlevel = DRIVER_PATCHLEVEL,
+};
+
+static const struct {
+       enum tegra_drm_panel_type type;
+       const char *name;
+} tegra_drm_panel_types[] = {
+       { TEGRA_DRM_PANEL_RGB, "rgb" },
+};
+
+static int tegra_drm_lookup_panel_type(const char *name)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(tegra_drm_panel_types); i++) {
+               if (strcasecmp(name, tegra_drm_panel_types[i].name) == 0)
+                       return tegra_drm_panel_types[i].type;
+       }
+
+       return -EINVAL;
+}
+
+static int tegra_drm_parse_dt_mode(struct device *dev,
+                                  struct device_node *node,
+                                  struct tegra_drm_mode *mode)
+{
+       u32 resolution[2];
+       u32 timings[4];
+       u32 value;
+       int err;
+
+       err = of_property_read_u32(node, "pixel-clock", &value);
+       if (err < 0)
+               return err;
+
+       mode->pixel_clock = value;
+
+       err = of_property_read_u32(node, "vertical-refresh", &value);
+       if (err < 0)
+               return err;
+
+       mode->vrefresh = value;
+
+       err = of_property_read_u32_array(node, "resolution", resolution,
+                                        ARRAY_SIZE(resolution));
+       if (err < 0)
+               return err;
+
+       mode->width = resolution[0];
+       mode->height = resolution[1];
+
+       err = of_property_read_u32(node, "bits-per-pixel", &value);
+       if (err < 0)
+               return err;
+
+       mode->bpp = value;
+
+       err = of_property_read_u32_array(node, "horizontal-timings", timings,
+                                        ARRAY_SIZE(timings));
+       if (err < 0)
+               return err;
+
+       mode->href_to_sync = timings[0];
+       mode->hsync_width = timings[1];
+       mode->hback_porch = timings[2];
+       mode->hfront_porch = timings[3];
+
+       err = of_property_read_u32_array(node, "vertical-timings", timings,
+                                        ARRAY_SIZE(timings));
+       if (err < 0)
+               return err;
+
+       mode->vref_to_sync = timings[0];
+       mode->vsync_width = timings[1];
+       mode->vback_porch = timings[2];
+       mode->vfront_porch = timings[3];
+
+       return 0;
+}
+
+static int tegra_drm_parse_dt_panel(struct device *dev,
+                                   struct device_node *node,
+                                   struct tegra_drm_panel *panel)
+{
+       struct tegra_drm_mode *mode;
+       struct device_node *child;
+       unsigned int count = 0;
+       const char *type;
+       u32 sizes[2];
+       int err;
+
+       err = of_property_read_string(node, "type", &type);
+       if (err < 0) {
+               dev_err(dev, "failed to read \"type\" property: %d\n", err);
+               return err;
+       }
+
+       err = tegra_drm_lookup_panel_type(type);
+       if (err < 0) {
+               dev_err(dev, "failed to look up panel type: %d\n", err);
+               return err;
+       }
+
+       panel->type = err;
+
+       err = of_property_read_u32_array(node, "size", sizes,
+                                        ARRAY_SIZE(sizes));
+       if (err < 0) {
+               dev_err(dev, "failed to parse \"size\" property: %d\n", err);
+               return err;
+       }
+
+       panel->width = sizes[0];
+       panel->height = sizes[1];
+
+       for_each_child_of_node(node, child)
+               count++;
+
+       if (count == 0) {
+               dev_err(dev, "no modes specified\n");
+               return -EINVAL;
+       }
+
+       panel->modes = devm_kzalloc(dev, count * sizeof(*mode), GFP_KERNEL);
+       if (!panel->modes) {
+               dev_err(dev, "failed to allocate modes\n");
+               return -ENOMEM;
+       }
+
+       for_each_child_of_node(node, child) {
+               mode = &panel->modes[panel->num_modes];
+
+               err = tegra_drm_parse_dt_mode(dev, child, mode);
+               if (err < 0) {
+                       dev_err(dev, "tegra_drm_parse_dt_mode(): %d\n", err);
+                       continue;
+               }
+
+               panel->num_modes++;
+       }
+
+       return 0;
+}
+
+static int tegra_drm_parse_dt(struct platform_device *pdev)
+{
+       struct tegra_drm_platform_data *pdata;
+       struct device *dev = &pdev->dev;
+       struct tegra_drm_panel *panel;
+       struct device_node *child;
+       unsigned int count = 0;
+       unsigned int i, j;
+       int err;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       for_each_child_of_node(dev->of_node, child)
+               count++;
+
+       if (count == 0) {
+               dev_err(dev, "no panel definitions found\n");
+               return -EINVAL;
+       }
+
+       pdata->panels = devm_kzalloc(dev, count * sizeof(*panel), GFP_KERNEL);
+       if (!pdata->panels) {
+               dev_err(dev, "failed to allocate panels\n");
+               return -ENOMEM;
+       }
+
+       for_each_child_of_node(dev->of_node, child) {
+               panel = &pdata->panels[pdata->num_panels];
+
+               err = tegra_drm_parse_dt_panel(&pdev->dev, child, panel);
+               if (err < 0) {
+                       dev_err(dev, "tegra_drm_parse_dt_panel(): %d\n", err);
+                       continue;
+               }
+
+               pdata->num_panels++;
+       }
+
+       for (i = 0; i < pdata->num_panels; i++) {
+               struct tegra_drm_panel *panel = &pdata->panels[i];
+
+               dev_dbg(dev, "panel %u:\n", i);
+               dev_dbg(dev, "  type: %d\n", panel->type);
+               dev_dbg(dev, "  size: %ux%u\n", panel->width, panel->height);
+
+               for (j = 0; j < panel->num_modes; j++) {
+                       struct tegra_drm_mode *mode = &panel->modes[j];
+
+                       dev_dbg(dev, "  mode: %u\n", j);
+                       dev_dbg(dev, "    pixel-clock: %u\n", 
mode->pixel_clock);
+                       dev_dbg(dev, "    resolution: %ux%ux%u\n",
+                                mode->width, mode->height, mode->bpp);
+
+                       dev_dbg(dev, "    horizontal timings:\n");
+                       dev_dbg(dev, "      ref-to-sync: %u\n", 
mode->href_to_sync);
+                       dev_dbg(dev, "      sync-width: %u\n", 
mode->hsync_width);
+                       dev_dbg(dev, "      back porch: %u\n", 
mode->hback_porch);
+                       dev_dbg(dev, "      front porch: %u\n", 
mode->hfront_porch);
+
+                       dev_dbg(dev, "    vertical timings:\n");
+                       dev_dbg(dev, "      ref-to-sync: %u\n", 
mode->vref_to_sync);
+                       dev_dbg(dev, "      sync-width: %u\n", 
mode->vsync_width);
+                       dev_dbg(dev, "      back porch: %u\n", 
mode->vback_porch);
+                       dev_dbg(dev, "      front porch: %u\n", 
mode->vfront_porch);
+               }
+       }
+
+       dev->platform_data = pdata;
+       return 0;
+}
+
+static int __devinit tegra_drm_probe(struct platform_device *pdev)
+{
+       struct tegra_drm_platform_data *pdata = pdev->dev.platform_data;
+       struct device_node *node = pdev->dev.of_node;
+       int err;
+
+       dev_dbg(&pdev->dev, "> %s(pdev=%p)\n", __func__, pdev);
+
+       if (!pdata && node) {
+               err = tegra_drm_parse_dt(pdev);
+               if (err < 0)
+                       goto out;
+       }
+
+       err = drm_platform_init(&drm_driver, pdev);
+
+out:
+       dev_dbg(&pdev->dev, "< %s() = %d\n", __func__, err);
+       return err;
+}
+
+static int __devexit tegra_drm_remove(struct platform_device *pdev)
+{
+       dev_dbg(&pdev->dev, "> %s(pdev=%p)\n", __func__, pdev);
+
+       drm_platform_exit(&drm_driver, pdev);
+       pdev->dev.platform_data = NULL;
+
+       dev_dbg(&pdev->dev, "< %s()\n", __func__);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id tegra_drm_of_match[] __devinitdata = {
+       { .compatible = "nvidia,tegra20-drm", },
+       { },
+};
+#endif
+
+static struct platform_driver tegra_drm_driver = {
+       .driver = {
+               .name = "tegra-drm",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(tegra_drm_of_match),
+       },
+       .probe = tegra_drm_probe,
+       .remove = __devexit_p(tegra_drm_remove),
+};
+
+module_platform_driver(tegra_drm_driver);
+
+MODULE_AUTHOR("Thierry Reding <thierry.reding at avionic-design.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tegra/tegra_drv.h 
b/drivers/gpu/drm/tegra/tegra_drv.h
new file mode 100644
index 0000000..c0ab341
--- /dev/null
+++ b/drivers/gpu/drm/tegra/tegra_drv.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef TEGRA_DRV_H
+#define TEGRA_DRV_H
+
+#define CMD_GENERAL_INCR_SYNCPT_CTRL 0x001
+#define CMD_CONT_SYNCPT_VSYNC 0x028
+#define CMD_DISP_CMD 0x032
+#define CMD_DISP_CMD_CTRL_MODE_STOP (0 << 5)
+#define CMD_DISP_CMD_CTRL_MODE_C_DISPLAY (1 << 5)
+#define CMD_DISP_CMD_CTRL_MODE_NC_DISPLAY (2 << 5)
+#define CMD_DISP_POWER_CTRL 0x036
+#define CMD_DISP_POWER_CTRL_PW0_ENABLE (1 <<  0)
+#define CMD_DISP_POWER_CTRL_PW1_ENABLE (1 <<  2)
+#define CMD_DISP_POWER_CTRL_PW2_ENABLE (1 <<  4)
+#define CMD_DISP_POWER_CTRL_PW3_ENABLE (1 <<  6)
+#define CMD_DISP_POWER_CTRL_PW4_ENABLE (1 <<  8)
+#define CMD_DISP_POWER_CTRL_PM0_ENABLE (1 << 16)
+#define CMD_DISP_POWER_CTRL_PM1_ENABLE (1 << 18)
+
+#define CMD_INT_STATUS 0x037
+#define CMD_INT_MASK 0x038
+#define CMD_INT_ENABLE 0x039
+#define CMD_INT_TYPE 0x03a
+#define CMD_INT_POLARITY 0x03b
+#define INT_FRAME_END (1 << 1)
+#define INT_VBLANK (1 << 2)
+#define INT_WIN_A_UF (1 << 8)
+#define INT_WIN_B_UF (1 << 9)
+#define INT_WIN_C_UF (1 << 10)
+#define INT_WIN_A_OF (1 << 14)
+#define INT_WIN_B_OF (1 << 15)
+#define INT_WIN_C_OF (1 << 16)
+
+#define COM_PIN_OUTPUT_ENABLE(x) (0x302 + (x))
+#define COM_PIN_OUTPUT_POLARITY(x) (0x306 + (x))
+#define COM_PIN_OUTPUT_DATA(x) (0x30a + (x))
+#define COM_PIN_OUTPUT_SEL(x) (0x314 + (x))
+
+#define COM_PIN_OUTPUT_POLARITY_PIN1_LVS_OUTPUT (1 << 28)
+#define COM_PIN_OUTPUT_POLARITY_PIN1_LHS_OUTPUT (1 << 30)
+
+#define DISP_MEM_HIGH_PRI 0x403
+#define DISP_MEM_HIGH_PRI_TIMER 0x404
+#define DISP_TIMING_OPT 0x405
+#define DISP_REF_TO_SYNC 0x406
+#define DISP_SYNC_WIDTH 0x407
+#define DISP_BACK_PORCH 0x408
+#define DISP_ACTIVE 0x409
+#define DISP_FRONT_PORCH 0x40a
+
+#define DISP_DATA_ENABLE_OPT 0x432
+#define DISP_DATA_ENABLE_OPT_SELECT_ACTIVE_BLANK (0 << 0)
+#define DISP_DATA_ENABLE_OPT_SELECT_ACTIVE (1 << 0)
+#define DISP_DATA_ENABLE_OPT_SELECT_ACTIVE_IS (2 << 0)
+#define DISP_DATA_ENABLE_OPT_CONTROL_ONECLK (0 << 2)
+#define DISP_DATA_ENABLE_OPT_CONTROL_NORMAL (1 << 2)
+#define DISP_DATA_ENABLE_OPT_CONTROL_EARLY_EXT (2 << 2)
+#define DISP_DATA_ENABLE_OPT_CONTROL_EARLY (3 << 2)
+#define DISP_DATA_ENABLE_OPT_CONTROL_ACTIVE_BLANK (4 << 2)
+
+#define DISP_INTERFACE_CTRL 0x42f
+#define DISP_INTERFACE_CTRL_DATA_FORMAT_DF1P1C (0 << 0)
+#define DISP_INTERFACE_CTRL_DATA_FORMAT_DF1P2C24B (1 << 0)
+#define DISP_INTERFACE_CTRL_DATA_FORMAT_DF1P2C18B (2 << 0)
+#define DISP_INTERFACE_CTRL_DATA_FORMAT_DF1P2C16B (3 << 0)
+#define DISP_INTERFACE_CTRL_DATA_FORMAT_DF2S (4 << 0)
+#define DISP_INTERFACE_CTRL_DATA_FORMAT_DF3S (5 << 0)
+#define DISP_INTERFACE_CTRL_DATA_FORMAT_DFSPI (6 << 0)
+#define DISP_INTERFACE_CTRL_DATA_FORMAT_DF1P3C24B (7 << 0)
+#define DISP_INTERFACE_CTRL_DATA_FORMAT_DF1P3C18B (8 << 0)
+#define DISP_INTERFACE_CTRL_ALIGN_MSB (0 << 8)
+#define DISP_INTERFACE_CTRL_ALIGN_LSB (1 << 8)
+#define DISP_INTERFACE_CTRL_ORDER_RED_BLUE (0 << 9)
+#define DISP_INTERFACE_CTRL_ORDER_BLUE_RED (1 << 9)
+
+#define DISP_SHIFT_CLK_OPT 0x431
+
+#define DISP_CLK_CTRL 0x42e
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD1 (0 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD1H (1 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD2 (2 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD3 (3 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD4 (4 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD6 (5 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD8 (6 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD9 (7 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD12 (8 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD16 (9 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD18 (10 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD24 (11 << 8)
+#define DISP_CLK_CTRL_PIXEL_CLK_DIV_PCD13 (12 << 8)
+#define DISP_CLK_CTRL_CLK_DIV(x) ((x) & 0xff)
+
+#define CMD_WIN_HEADER 0x042
+#define CMD_WIN_HEADER_WINDOW_A_SELECT (1 << 4)
+#define CMD_WIN_HEADER_WINDOW_B_SELECT (1 << 5)
+#define CMD_WIN_HEADER_WINDOW_C_SELECT (1 << 6)
+
+#define WIN_COLOR_DEPTH 0x703
+#define WIN_COLOR_DEPTH_P1 0
+#define WIN_COLOR_DEPTH_P2 1
+#define WIN_COLOR_DEPTH_P4 2
+#define WIN_COLOR_DEPTH_P8 3
+#define WIN_COLOR_DEPTH_B4G4R4A4 4
+#define WIN_COLOR_DEPTH_B5G5R5A 5
+#define WIN_COLOR_DEPTH_B5G6R5 6
+#define WIN_COLOR_DEPTH_AB5G5R5 7
+#define WIN_COLOR_DEPTH_B8G8R8A8 12
+#define WIN_COLOR_DEPTH_R8G8B8A8 13
+#define WIN_COLOR_DEPTH_B6x2G6x2R6x2A8 14
+#define WIN_COLOR_DEPTH_R6x2G6x2B6x2A8 15
+#define WIN_COLOR_DEPTH_YCbCr422 16
+#define WIN_COLOR_DEPTH_YUV422 17
+#define WIN_COLOR_DEPTH_YCbCr420P 18
+#define WIN_COLOR_DEPTH_YUV420P 19
+#define WIN_COLOR_DEPTH_YCbCr422P 20
+#define WIN_COLOR_DEPTH_YUV422P 21
+#define WIN_COLOR_DEPTH_YCbCr422R 22
+#define WIN_COLOR_DEPTH_YUV422R 23
+#define WIN_COLOR_DEPTH_YCbCr422RA 24
+#define WIN_COLOR_DEPTH_YUV422RA 25
+
+#define WIN_BYTE_SWAP 0x701
+#define WIN_BYTE_SWAP_NOSWAP (0 << 0)
+#define WIN_BYTE_SWAP_SWAP2 (1 << 0)
+#define WIN_BYTE_SWAP_SWAP4 (2 << 0)
+#define WIN_BYTE_SWAP_SWAP4HW (3 << 0)
+
+#define WIN_POSITION 0x704
+#define WIN_POSITION_H(x) (((x) & 0x1fff) <<  0)
+#define WIN_POSITION_V(x) (((x) & 0x1fff) << 16)
+
+#define WIN_SIZE 0x705
+#define WIN_SIZE_H(x) (((x) & 0x1fff) <<  0)
+#define WIN_SIZE_V(x) (((x) & 0x1fff) << 16)
+
+#define WIN_PRESCALED_SIZE 0x706
+#define WIN_PRESCALED_SIZE_H(x) (((x) & 0x7fff) <<  0)
+#define WIN_PRESCALED_SIZE_V(x) (((x) & 0x1fff) << 16)
+
+#define WIN_H_INITIAL_DDA 0x707
+#define WIN_V_INITIAL_DDA 0x708
+
+#define WIN_DDA_INC 0x709
+#define WIN_DDA_INC_H(x) (((x) & 0xffff) <<  0)
+#define WIN_DDA_INC_V(x) (((x) & 0xffff) << 16)
+
+#define WIN_LINE_STRIDE 0x70a
+#define WIN_BUF_STRIDE 0x70b
+#define WIN_UV_BUF_STRIDE 0x70c
+
+#define WIN_OPT 0x700
+#define WIN_OPT_COLOR_EXPAND (1 << 6)
+#define WIN_OPT_ENABLE (1 << 30)
+
+#define WINBUF_START_ADDR 0x800
+#define WINBUF_ADDR_H_OFFSET 0x806
+#define WINBUF_ADDR_V_OFFSET 0x808
+
+#define WIN_BLEND_NOKEY 0x70f
+#define WIN_BLEND_1WIN 0x710
+
+#define CMD_STATE_CTRL 0x041
+#define CMD_STATE_CTRL_GENERAL_ACT_REQ (1 <<  0)
+#define CMD_STATE_CTRL_WIN_A_ACT_REQ   (1 <<  1)
+#define CMD_STATE_CTRL_WIN_B_ACT_REQ   (1 <<  2)
+#define CMD_STATE_CTRL_WIN_C_ACT_REQ   (1 <<  3)
+#define CMD_STATE_CTRL_GENERAL_UPDATE  (1 <<  8)
+#define CMD_STATE_CTRL_WIN_A_UPDATE    (1 <<  9)
+#define CMD_STATE_CTRL_WIN_B_UPDATE    (1 << 10)
+#define CMD_STATE_CTRL_WIN_C_UPDATE    (1 << 11)
+
+/* synchronization points */
+#define SYNCPT_VBLANK0 26
+#define SYNCPT_VBLANK1 27
+
+#endif /* TEGRA_DRV_H */
diff --git a/include/drm/tegra_drm.h b/include/drm/tegra_drm.h
new file mode 100644
index 0000000..b3dd44a
--- /dev/null
+++ b/include/drm/tegra_drm.h
@@ -0,0 +1,44 @@
+#ifndef _TEGRA_DRM_H_
+#define _TEGRA_DRM_H_
+
+enum tegra_drm_panel_type {
+       TEGRA_DRM_PANEL_RGB,
+};
+
+struct tegra_drm_mode {
+       unsigned int pixel_clock;
+       unsigned int vrefresh;
+
+       unsigned int width;
+       unsigned int height;
+       unsigned int bpp;
+
+       unsigned int href_to_sync;
+       unsigned int hsync_width;
+       unsigned int hback_porch;
+       unsigned int hfront_porch;
+
+       unsigned int vref_to_sync;
+       unsigned int vsync_width;
+       unsigned int vback_porch;
+       unsigned int vfront_porch;
+};
+
+struct tegra_drm_panel {
+       enum tegra_drm_panel_type type;
+
+       /* physical size */
+       unsigned int width;
+       unsigned int height;
+
+       /* display modes */
+       struct tegra_drm_mode *modes;
+       unsigned int num_modes;
+};
+
+struct tegra_drm_platform_data {
+       struct tegra_drm_panel *panels;
+       unsigned int num_panels;
+};
+
+#endif
-- 
1.7.10

Reply via email to