linux-fbdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] video: tegra: add tegra display controller and fb driver
@ 2010-08-11 23:11 Erik Gilling
  2010-08-12  4:34 ` Ryan Mallon
  2010-08-25 22:04 ` Erik Gilling
  0 siblings, 2 replies; 6+ messages in thread
From: Erik Gilling @ 2010-08-11 23:11 UTC (permalink / raw)
  To: linux-arm-kernel

This patch supersedes the previous framebuffer patch

Supports:
	* panel setup
	* overlays
	* suspend / resume

Notable ommisions:
	* support for anything but lvds panels
	* inegration with nvhost driver to sync updates with 3D
	* FB physical geometry is not set
	* lacks interface to set overlay/window x,y offset

Signed-off-by: Erik Gilling <konkers@android.com>
Cc: Colin Cross <ccross@android.com>
Cc: Travis Geiselbrecht <travis@palm.com>
---
 arch/arm/mach-tegra/include/mach/dc.h |  152 ++++++
 arch/arm/mach-tegra/include/mach/fb.h |   44 ++
 drivers/video/Kconfig                 |    1 +
 drivers/video/Makefile                |    1 +
 drivers/video/tegra/Kconfig           |   15 +
 drivers/video/tegra/Makefile          |    2 +
 drivers/video/tegra/dc/Makefile       |    2 +
 drivers/video/tegra/dc/dc.c           |  894 +++++++++++++++++++++++++++++++++
 drivers/video/tegra/dc/dc_priv.h      |   86 ++++
 drivers/video/tegra/dc/dc_reg.h       |  342 +++++++++++++
 drivers/video/tegra/dc/rgb.c          |   63 +++
 drivers/video/tegra/fb.c              |  311 ++++++++++++
 12 files changed, 1913 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-tegra/include/mach/dc.h
 create mode 100644 arch/arm/mach-tegra/include/mach/fb.h
 create mode 100644 drivers/video/tegra/Kconfig
 create mode 100644 drivers/video/tegra/Makefile
 create mode 100644 drivers/video/tegra/dc/Makefile
 create mode 100644 drivers/video/tegra/dc/dc.c
 create mode 100644 drivers/video/tegra/dc/dc_priv.h
 create mode 100644 drivers/video/tegra/dc/dc_reg.h
 create mode 100644 drivers/video/tegra/dc/rgb.c
 create mode 100644 drivers/video/tegra/fb.c

diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
new file mode 100644
index 0000000..b8a0e7a
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/dc.h
@@ -0,0 +1,152 @@
+/*
+ * arch/arm/mach-tegra/include/mach/dc.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *	Erik Gilling <konkers@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MACH_TEGRA_DC_H
+#define __MACH_TEGRA_DC_H
+
+
+#define TEGRA_MAX_DC		2
+#define DC_N_WINDOWS		3
+
+struct tegra_dc_blend {
+	u32	nokey;
+	u32	one_win;
+	u32	two_win_x;
+	u32	two_win_y;
+	u32	three_win_xy;
+};
+
+#define BLEND(key, control, weight0, weight1)				\
+	(CKEY_ ## key | BLEND_CONTROL_ ## control |			\
+	 BLEND_WEIGHT0(weight0) | BLEND_WEIGHT0(weight1))
+
+struct tegra_dc_mode {
+	int	pclk;
+	int	h_ref_to_sync;
+	int	v_ref_to_sync;
+	int	h_sync_width;
+	int	v_sync_width;
+	int	h_back_porch;
+	int	v_back_porch;
+	int	h_active;
+	int	v_active;
+	int	h_front_porch;
+	int	v_front_porch;
+};
+
+enum {
+	TEGRA_DC_OUT_RGB,
+};
+
+struct tegra_dc_out {
+	int			type;
+
+	unsigned		order;
+	unsigned		align;
+
+	struct tegra_dc_mode	*modes;
+	int			n_modes;
+};
+
+#define TEGRA_DC_ALIGN_MSB		0
+#define TEGRA_DC_ALIGN_LSB		1
+
+#define TEGRA_DC_ORDER_RED_BLUE		0
+#define TEGRA_DC_ORDER_BLUE_RED		1
+
+struct tegra_dc;
+
+struct tegra_dc_win {
+	u8			idx;
+	u8			fmt;
+	u32			flags;
+
+	void			*virt_addr;
+	dma_addr_t		phys_addr;
+	unsigned		x;
+	unsigned		y;
+	unsigned		w;
+	unsigned		h;
+	unsigned		out_w;
+	unsigned		out_h;
+
+	int			dirty;
+	struct tegra_dc		*dc;
+};
+
+#define TEGRA_WIN_FLAG_ENABLED		(1 << 0)
+#define TEGRA_WIN_FLAG_COLOR_EXPAND	(1 << 1)
+
+/* Note: These are the actual values written to the DC_WIN_COLOR_DEPTH register
+ * and may change in new tegra architectures.
+ */
+#define TEGRA_WIN_FMT_P1		0
+#define TEGRA_WIN_FMT_P2		1
+#define TEGRA_WIN_FMT_P4		2
+#define TEGRA_WIN_FMT_P8		3
+#define TEGRA_WIN_FMT_B4G4R4A4		4
+#define TEGRA_WIN_FMT_B5G5R5A		5
+#define TEGRA_WIN_FMT_B5G6R5		6
+#define TEGRA_WIN_FMT_AB5G5R5		7
+#define TEGRA_WIN_FMT_B8G8R8A8		12
+#define TEGRA_WIN_FMT_R8G8B8A8		13
+#define TEGRA_WIN_FMT_B6x2G6x2R6x2A8	14
+#define TEGRA_WIN_FMT_R6x2G6x2B6x2A8	15
+#define TEGRA_WIN_FMT_YCbCr422		16
+#define TEGRA_WIN_FMT_YUV422		17
+#define TEGRA_WIN_FMT_YCbCr420P		18
+#define TEGRA_WIN_FMT_YUV420P		19
+#define TEGRA_WIN_FMT_YCbCr422P		20
+#define TEGRA_WIN_FMT_YUV422P		21
+#define TEGRA_WIN_FMT_YCbCr422R		22
+#define TEGRA_WIN_FMT_YUV422R		23
+#define TEGRA_WIN_FMT_YCbCr422RA	24
+#define TEGRA_WIN_FMT_YUV422RA		25
+
+struct tegra_fb_data {
+	int		win;
+
+	int		xres;
+	int		yres;
+	int		bits_per_pixel;
+};
+
+struct tegra_dc_platform_data {
+	unsigned long		flags;
+	struct tegra_dc_out	*default_out;
+	struct tegra_fb_data	*fb;
+};
+
+#define TEGRA_DC_FLAG_ENABLED		(1 << 0)
+
+struct tegra_dc *tegra_dc_get_dc(unsigned idx);
+struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win);
+
+/* tegra_dc_update_windows and tegra_dc_sync_windows do not support windows
+ * with differenct dcs in one call
+ */
+int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n);
+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);
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/fb.h b/arch/arm/mach-tegra/include/mach/fb.h
new file mode 100644
index 0000000..9e5f7f8
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/fb.h
@@ -0,0 +1,44 @@
+/*
+ * arch/arm/mach-tegra/include/mach/fb.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *	Erik Gilling <konkers@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MACH_TEGRA_FB_H
+#define __MACH_TEGRA_FB_H
+
+#ifdef CONFIG_FB_TEGRA
+struct tegra_fb_info *tegra_fb_register(struct platform_device *pdev,
+					struct tegra_dc *dc,
+					struct tegra_fb_data *fb_data,
+					struct resource *fb_mem);
+void tegra_fb_unregister(struct tegra_fb_info *fb_info);
+#else
+static inline
+struct tegra_fb_info *tegra_fb_register(struct platform_device *pdev,
+					struct tegra_dc *dc,
+					struct tegra_fb_data *fb_data,
+					struct resource *fb_mem)
+{
+	return NULL;
+}
+
+static inline void tegra_fb_unregister(struct tegra_fb_info *fb_info)
+{
+}
+#endif
+
+#endif
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3d94a14..1eb0ac1 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2231,6 +2231,7 @@ config FB_BROADSHEET
 
 source "drivers/video/omap/Kconfig"
 source "drivers/video/omap2/Kconfig"
+source "drivers/video/tegra/Kconfig"
 
 source "drivers/video/backlight/Kconfig"
 source "drivers/video/display/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index ddc2af2..21b527d 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -131,6 +131,7 @@ obj-$(CONFIG_FB_CARMINE)          += carminefb.o
 obj-$(CONFIG_FB_MB862XX)	  += mb862xx/
 obj-$(CONFIG_FB_MSM)              += msm/
 obj-$(CONFIG_FB_NUC900)           += nuc900fb.o
+obj-y                             += tegra/
 
 # Platform or fallback drivers go here
 obj-$(CONFIG_FB_UVESA)            += uvesafb.o
diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig
new file mode 100644
index 0000000..b01dc95
--- /dev/null
+++ b/drivers/video/tegra/Kconfig
@@ -0,0 +1,15 @@
+config TEGRA_DC
+	tristate "Tegra Display Contoller"
+	depends on ARCH_TEGRA
+	help
+	  Tegra display controller support.
+
+config FB_TEGRA
+	tristate "Tegra Framebuffer driver"
+	depends on TEGRA_DC && FB = y
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	default FB
+	help
+	  Framebuffer device support for the Tegra display controller.
diff --git a/drivers/video/tegra/Makefile b/drivers/video/tegra/Makefile
new file mode 100644
index 0000000..8f9d0e2
--- /dev/null
+++ b/drivers/video/tegra/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_TEGRA_DC) += dc/
+obj-$(CONFIG_FB_TEGRA) += fb.o
diff --git a/drivers/video/tegra/dc/Makefile b/drivers/video/tegra/dc/Makefile
new file mode 100644
index 0000000..3ecb63c
--- /dev/null
+++ b/drivers/video/tegra/dc/Makefile
@@ -0,0 +1,2 @@
+obj-y += dc.o
+obj-y += rgb.o
\ No newline at end of file
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
new file mode 100644
index 0000000..261bced
--- /dev/null
+++ b/drivers/video/tegra/dc/dc.c
@@ -0,0 +1,894 @@
+/*
+ * drivers/video/tegra/dc/dc.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Erik Gilling <konkers@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/workqueue.h>
+#include <linux/ktime.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <mach/clk.h>
+#include <mach/dc.h>
+#include <mach/fb.h>
+
+#include "dc_reg.h"
+#include "dc_priv.h"
+
+struct tegra_dc_blend tegra_dc_blend_modes[][DC_N_WINDOWS] = {
+	{{.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
+	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
+	  .two_win_x = BLEND(NOKEY, FIX, 0x00, 0x00),
+	  .two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
+	  .three_win_xy = BLEND(NOKEY, FIX, 0x00, 0x00)},
+	 {.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
+	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
+	  .two_win_x = BLEND(NOKEY, FIX, 0xff, 0xff),
+	  .two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
+	  .three_win_xy = BLEND(NOKEY, DEPENDANT, 0x00, 0x00)},
+	 {.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
+	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
+	  .two_win_x = BLEND(NOKEY, ALPHA, 0xff, 0xff),
+	  .two_win_y = BLEND(NOKEY, ALPHA, 0xff, 0xff),
+	  .three_win_xy = BLEND(NOKEY, ALPHA, 0xff, 0xff)}
+	}
+};
+
+struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
+
+DEFINE_MUTEX(tegra_dc_lock);
+
+static inline int tegra_dc_fmt_bpp(int fmt)
+{
+	switch (fmt) {
+	case TEGRA_WIN_FMT_P1:
+		return 1;
+
+	case TEGRA_WIN_FMT_P2:
+		return 2;
+
+	case TEGRA_WIN_FMT_P4:
+		return 4;
+
+	case TEGRA_WIN_FMT_P8:
+		return 8;
+
+	case TEGRA_WIN_FMT_B4G4R4A4:
+	case TEGRA_WIN_FMT_B5G5R5A:
+	case TEGRA_WIN_FMT_B5G6R5:
+	case TEGRA_WIN_FMT_AB5G5R5:
+		return 16;
+
+	case TEGRA_WIN_FMT_B8G8R8A8:
+	case TEGRA_WIN_FMT_R8G8B8A8:
+	case TEGRA_WIN_FMT_B6x2G6x2R6x2A8:
+	case TEGRA_WIN_FMT_R6x2G6x2B6x2A8:
+		return 32;
+
+	case TEGRA_WIN_FMT_YCbCr422:
+	case TEGRA_WIN_FMT_YUV422:
+	case TEGRA_WIN_FMT_YCbCr420P:
+	case TEGRA_WIN_FMT_YUV420P:
+	case TEGRA_WIN_FMT_YCbCr422P:
+	case TEGRA_WIN_FMT_YUV422P:
+	case TEGRA_WIN_FMT_YCbCr422R:
+	case TEGRA_WIN_FMT_YUV422R:
+	case TEGRA_WIN_FMT_YCbCr422RA:
+	case TEGRA_WIN_FMT_YUV422RA:
+		/* FIXME: need to know the bpp of these formats */
+		return 0;
+	}
+	return 0;
+}
+
+#define DUMP_REG(a) do {			\
+	snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \
+		 #a, a, tegra_dc_readl(dc, a));		      \
+	print(data, buff);				      \
+	} while (0)
+
+static void _dump_regs(struct tegra_dc *dc, void *data,
+		       void (* print)(void *data, const char *str))
+{
+	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);
+	DUMP_REG(DC_CMD_INT_STATUS);
+	DUMP_REG(DC_CMD_INT_MASK);
+	DUMP_REG(DC_CMD_INT_ENABLE);
+	DUMP_REG(DC_CMD_INT_TYPE);
+	DUMP_REG(DC_CMD_INT_POLARITY);
+	DUMP_REG(DC_CMD_SIGNAL_RAISE1);
+	DUMP_REG(DC_CMD_SIGNAL_RAISE2);
+	DUMP_REG(DC_CMD_SIGNAL_RAISE3);
+	DUMP_REG(DC_CMD_STATE_ACCESS);
+	DUMP_REG(DC_CMD_STATE_CONTROL);
+	DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER);
+	DUMP_REG(DC_CMD_REG_ACT_CONTROL);
+
+	DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS0);
+	DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS1);
+	DUMP_REG(DC_DISP_DISP_WIN_OPTIONS);
+	DUMP_REG(DC_DISP_MEM_HIGH_PRIORITY);
+	DUMP_REG(DC_DISP_MEM_HIGH_PRIORITY_TIMER);
+	DUMP_REG(DC_DISP_DISP_TIMING_OPTIONS);
+	DUMP_REG(DC_DISP_REF_TO_SYNC);
+	DUMP_REG(DC_DISP_SYNC_WIDTH);
+	DUMP_REG(DC_DISP_BACK_PORCH);
+	DUMP_REG(DC_DISP_DISP_ACTIVE);
+	DUMP_REG(DC_DISP_FRONT_PORCH);
+	DUMP_REG(DC_DISP_H_PULSE0_CONTROL);
+	DUMP_REG(DC_DISP_H_PULSE0_POSITION_A);
+	DUMP_REG(DC_DISP_H_PULSE0_POSITION_B);
+	DUMP_REG(DC_DISP_H_PULSE0_POSITION_C);
+	DUMP_REG(DC_DISP_H_PULSE0_POSITION_D);
+	DUMP_REG(DC_DISP_H_PULSE1_CONTROL);
+	DUMP_REG(DC_DISP_H_PULSE1_POSITION_A);
+	DUMP_REG(DC_DISP_H_PULSE1_POSITION_B);
+	DUMP_REG(DC_DISP_H_PULSE1_POSITION_C);
+	DUMP_REG(DC_DISP_H_PULSE1_POSITION_D);
+	DUMP_REG(DC_DISP_H_PULSE2_CONTROL);
+	DUMP_REG(DC_DISP_H_PULSE2_POSITION_A);
+	DUMP_REG(DC_DISP_H_PULSE2_POSITION_B);
+	DUMP_REG(DC_DISP_H_PULSE2_POSITION_C);
+	DUMP_REG(DC_DISP_H_PULSE2_POSITION_D);
+	DUMP_REG(DC_DISP_V_PULSE0_CONTROL);
+	DUMP_REG(DC_DISP_V_PULSE0_POSITION_A);
+	DUMP_REG(DC_DISP_V_PULSE0_POSITION_B);
+	DUMP_REG(DC_DISP_V_PULSE0_POSITION_C);
+	DUMP_REG(DC_DISP_V_PULSE1_CONTROL);
+	DUMP_REG(DC_DISP_V_PULSE1_POSITION_A);
+	DUMP_REG(DC_DISP_V_PULSE1_POSITION_B);
+	DUMP_REG(DC_DISP_V_PULSE1_POSITION_C);
+	DUMP_REG(DC_DISP_V_PULSE2_CONTROL);
+	DUMP_REG(DC_DISP_V_PULSE2_POSITION_A);
+	DUMP_REG(DC_DISP_V_PULSE3_CONTROL);
+	DUMP_REG(DC_DISP_V_PULSE3_POSITION_A);
+	DUMP_REG(DC_DISP_M0_CONTROL);
+	DUMP_REG(DC_DISP_M1_CONTROL);
+	DUMP_REG(DC_DISP_DI_CONTROL);
+	DUMP_REG(DC_DISP_PP_CONTROL);
+	DUMP_REG(DC_DISP_PP_SELECT_A);
+	DUMP_REG(DC_DISP_PP_SELECT_B);
+	DUMP_REG(DC_DISP_PP_SELECT_C);
+	DUMP_REG(DC_DISP_PP_SELECT_D);
+	DUMP_REG(DC_DISP_DISP_CLOCK_CONTROL);
+	DUMP_REG(DC_DISP_DISP_INTERFACE_CONTROL);
+	DUMP_REG(DC_DISP_DISP_COLOR_CONTROL);
+	DUMP_REG(DC_DISP_SHIFT_CLOCK_OPTIONS);
+	DUMP_REG(DC_DISP_DATA_ENABLE_OPTIONS);
+	DUMP_REG(DC_DISP_SERIAL_INTERFACE_OPTIONS);
+	DUMP_REG(DC_DISP_LCD_SPI_OPTIONS);
+	DUMP_REG(DC_DISP_BORDER_COLOR);
+	DUMP_REG(DC_DISP_COLOR_KEY0_LOWER);
+	DUMP_REG(DC_DISP_COLOR_KEY0_UPPER);
+	DUMP_REG(DC_DISP_COLOR_KEY1_LOWER);
+	DUMP_REG(DC_DISP_COLOR_KEY1_UPPER);
+	DUMP_REG(DC_DISP_CURSOR_FOREGROUND);
+	DUMP_REG(DC_DISP_CURSOR_BACKGROUND);
+	DUMP_REG(DC_DISP_CURSOR_START_ADDR);
+	DUMP_REG(DC_DISP_CURSOR_START_ADDR_NS);
+	DUMP_REG(DC_DISP_CURSOR_POSITION);
+	DUMP_REG(DC_DISP_CURSOR_POSITION_NS);
+	DUMP_REG(DC_DISP_INIT_SEQ_CONTROL);
+	DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_A);
+	DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_B);
+	DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_C);
+	DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_D);
+	DUMP_REG(DC_DISP_DC_MCCIF_FIFOCTRL);
+	DUMP_REG(DC_DISP_MCCIF_DISPLAY0A_HYST);
+	DUMP_REG(DC_DISP_MCCIF_DISPLAY0B_HYST);
+	DUMP_REG(DC_DISP_MCCIF_DISPLAY0C_HYST);
+	DUMP_REG(DC_DISP_MCCIF_DISPLAY1B_HYST);
+	DUMP_REG(DC_DISP_DAC_CRT_CTRL);
+	DUMP_REG(DC_DISP_DISP_MISC_CONTROL);
+
+
+	for (i = 0; i < 3; i++) {
+		print(data, "\n");
+		snprintf(buff, sizeof(buff), "WINDOW %c:\n", 'A' + i);
+		print(data, buff);
+
+		tegra_dc_writel(dc, WINDOW_A_SELECT << i,
+				DC_CMD_DISPLAY_WINDOW_HEADER);
+		DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER);
+		DUMP_REG(DC_WIN_WIN_OPTIONS);
+		DUMP_REG(DC_WIN_BYTE_SWAP);
+		DUMP_REG(DC_WIN_BUFFER_CONTROL);
+		DUMP_REG(DC_WIN_COLOR_DEPTH);
+		DUMP_REG(DC_WIN_POSITION);
+		DUMP_REG(DC_WIN_SIZE);
+		DUMP_REG(DC_WIN_PRESCALED_SIZE);
+		DUMP_REG(DC_WIN_H_INITIAL_DDA);
+		DUMP_REG(DC_WIN_V_INITIAL_DDA);
+		DUMP_REG(DC_WIN_DDA_INCREMENT);
+		DUMP_REG(DC_WIN_LINE_STRIDE);
+		DUMP_REG(DC_WIN_BUF_STRIDE);
+		DUMP_REG(DC_WIN_BLEND_NOKEY);
+		DUMP_REG(DC_WIN_BLEND_1WIN);
+		DUMP_REG(DC_WIN_BLEND_2WIN_X);
+		DUMP_REG(DC_WIN_BLEND_2WIN_Y);
+		DUMP_REG(DC_WIN_BLEND_3WIN_XY);
+		DUMP_REG(DC_WINBUF_START_ADDR);
+		DUMP_REG(DC_WINBUF_ADDR_H_OFFSET);
+		DUMP_REG(DC_WINBUF_ADDR_V_OFFSET);
+	}
+}
+
+#undef DUMP_REG
+
+#ifdef DEBUG
+static void dump_regs_print(void *data, const char *str)
+{
+	struct tegra_dc *dc = data;
+	dev_dbg(&dc->pdev->dev, "%s", str);
+}
+
+static void dump_regs(struct tegra_dc *dc)
+{
+	_dump_regs(dc, dc, dump_regs_print);
+}
+#else
+
+static void dump_regs(struct tegra_dc *dc) {}
+
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+
+static void dbg_regs_print(void *data, const char *str)
+{
+	struct seq_file *s = data;
+
+	seq_printf(s, "%s", str);
+}
+
+#undef DUMP_REG
+
+static int dbg_dc_show(struct seq_file *s, void *unused)
+{
+	struct tegra_dc *dc = s->private;
+
+	_dump_regs(dc, s, dbg_regs_print);
+
+	return 0;
+}
+
+
+static int dbg_dc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dbg_dc_show, inode->i_private);
+}
+
+static const struct file_operations dbg_fops = {
+	.open		= dbg_dc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void tegra_dc_dbg_add(struct tegra_dc *dc)
+{
+	char name[32];
+
+	snprintf(name, sizeof(name), "tegra_dc%d_regs", dc->pdev->id);
+	(void) debugfs_create_file(name, S_IRUGO, NULL, dc, &dbg_fops);
+}
+#else
+static void tegra_dc_dbg_add(struct tegra_dc *dc) {}
+
+#endif
+
+
+static int tegra_dc_add(struct tegra_dc *dc, int index)
+{
+	int ret = 0;
+
+	mutex_lock(&tegra_dc_lock);
+	if (index >= TEGRA_MAX_DC) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (tegra_dcs[index] != NULL) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	tegra_dcs[index] = dc;
+
+out:
+	mutex_unlock(&tegra_dc_lock);
+
+	return ret;
+}
+
+struct tegra_dc *tegra_dc_get_dc(unsigned idx)
+{
+	if (idx < TEGRA_MAX_DC)
+		return tegra_dcs[idx];
+	else
+		return NULL;
+}
+EXPORT_SYMBOL(tegra_dc_get_dc);
+
+struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win)
+{
+	if (win >= dc->n_windows)
+		return NULL;
+
+	return &dc->windows[win];
+}
+EXPORT_SYMBOL(tegra_dc_get_window);
+
+/* does not support updating windows on multiple dcs in one call */
+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);
+	for (i = 0; i < n; i++) {
+		struct tegra_dc_win *win = windows[i];
+		unsigned h_dda;
+		unsigned v_dda;
+		unsigned stride;
+
+		tegra_dc_writel(dc, WINDOW_A_SELECT << win->idx,
+				DC_CMD_DISPLAY_WINDOW_HEADER);
+
+		update_mask |= WIN_A_ACT_REQ << win->idx;
+
+		if (!(win->flags & TEGRA_WIN_FLAG_ENABLED)) {
+			tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS);
+			continue;
+		}
+
+		tegra_dc_writel(dc, win->fmt, DC_WIN_COLOR_DEPTH);
+		tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
+
+		stride = win->w * tegra_dc_fmt_bpp(win->fmt) / 8;
+
+		/* TODO: implement filter on settings */
+		h_dda = (win->w * 0x1000) / (win->out_w - 1);
+		v_dda = (win->h * 0x1000) / (win->out_h - 1);
+
+		tegra_dc_writel(dc,
+				V_POSITION(win->y) | H_POSITION(win->x),
+				DC_WIN_POSITION);
+		tegra_dc_writel(dc,
+				V_SIZE(win->out_h) | H_SIZE(win->out_w),
+				DC_WIN_SIZE);
+		tegra_dc_writel(dc,
+				V_PRESCALED_SIZE(win->out_h) |
+				H_PRESCALED_SIZE(stride),
+				DC_WIN_PRESCALED_SIZE);
+		tegra_dc_writel(dc, 0, DC_WIN_H_INITIAL_DDA);
+		tegra_dc_writel(dc, 0, DC_WIN_V_INITIAL_DDA);
+		tegra_dc_writel(dc, V_DDA_INC(v_dda) | H_DDA_INC(h_dda),
+				DC_WIN_DDA_INCREMENT);
+		tegra_dc_writel(dc, stride, DC_WIN_LINE_STRIDE);
+		tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
+
+		val = WIN_ENABLE;
+		if (win->flags & TEGRA_WIN_FLAG_COLOR_EXPAND)
+			val |= COLOR_EXPAND;
+		tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS);
+
+		tegra_dc_writel(dc, (unsigned long)win->phys_addr,
+				DC_WINBUF_START_ADDR);
+		tegra_dc_writel(dc, 0, DC_WINBUF_ADDR_H_OFFSET);
+		tegra_dc_writel(dc, 0, DC_WINBUF_ADDR_V_OFFSET);
+
+		win->dirty = 1;
+
+	}
+
+	tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL);
+
+	val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+	val |= FRAME_END_INT;
+	tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+
+	val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
+	val |= FRAME_END_INT;
+	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);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_dc_update_windows);
+
+static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[],
+					     int n)
+{
+	int i;
+
+	for (i = 0; i < n; i++) {
+		if (windows[i]->dirty)
+			return false;
+	}
+
+	return true;
+}
+
+/* does not support syncing windows on multiple dcs in one call */
+int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
+{
+	if (n < 1 || n > DC_N_WINDOWS)
+		return -EINVAL;
+
+	return wait_event_interruptible_timeout(windows[0]->dc->wq,
+					 tegra_dc_windows_are_clean(windows, n),
+					 HZ);
+}
+EXPORT_SYMBOL(tegra_dc_sync_windows);
+
+void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend)
+{
+	int i;
+
+	for (i = 0; i < DC_N_WINDOWS; i++) {
+		tegra_dc_writel(dc, WINDOW_A_SELECT << i,
+				DC_CMD_DISPLAY_WINDOW_HEADER);
+		tegra_dc_writel(dc, blend[i].nokey, DC_WIN_BLEND_NOKEY);
+		tegra_dc_writel(dc, blend[i].one_win, DC_WIN_BLEND_1WIN);
+		tegra_dc_writel(dc, blend[i].two_win_x, DC_WIN_BLEND_2WIN_X);
+		tegra_dc_writel(dc, blend[i].two_win_y, DC_WIN_BLEND_2WIN_Y);
+		tegra_dc_writel(dc, blend[i].three_win_xy,
+				DC_WIN_BLEND_3WIN_XY);
+	}
+}
+EXPORT_SYMBOL(tegra_dc_set_blending);
+
+int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
+{
+	unsigned long val;
+	unsigned long rate;
+	unsigned long div;
+
+	tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
+	tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16),
+			DC_DISP_REF_TO_SYNC);
+	tegra_dc_writel(dc, mode->h_sync_width | (mode->v_sync_width << 16),
+			DC_DISP_SYNC_WIDTH);
+	tegra_dc_writel(dc, mode->h_back_porch | (mode->v_back_porch << 16),
+			DC_DISP_BACK_PORCH);
+	tegra_dc_writel(dc, mode->h_active | (mode->v_active << 16),
+			DC_DISP_DISP_ACTIVE);
+	tegra_dc_writel(dc, mode->h_front_porch | (mode->v_front_porch << 16),
+			DC_DISP_FRONT_PORCH);
+
+	tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL,
+			DC_DISP_DATA_ENABLE_OPTIONS);
+
+	/* TODO: MIPI/CRT/HDMI clock cals */
+
+	val = DISP_DATA_FORMAT_DF1P1C;
+
+	if (dc->out->align = TEGRA_DC_ALIGN_MSB)
+		val |= DISP_DATA_ALIGNMENT_MSB;
+	else
+		val |= DISP_DATA_ALIGNMENT_LSB;
+
+	if (dc->out->order = TEGRA_DC_ORDER_RED_BLUE)
+		val |= DISP_DATA_ORDER_RED_BLUE;
+	else
+		val |= DISP_DATA_ORDER_BLUE_RED;
+
+	tegra_dc_writel(dc, val, DC_DISP_DISP_INTERFACE_CONTROL);
+
+	rate = clk_get_rate(dc->clk);
+
+	div = ((rate * 2 + mode->pclk / 2) / mode->pclk) - 2;
+
+	if (rate * 2 / (div + 2) < (mode->pclk / 100 * 99) ||
+	    rate * 2 / (div + 2) > (mode->pclk / 100 * 109)) {
+		dev_err(&dc->pdev->dev,
+			"can't divide %ld clock to %d -1/+9%% %ld %d %d\n",
+			rate, mode->pclk,
+			rate / div, (mode->pclk / 100 * 99),
+			(mode->pclk / 100 * 109));
+		return -EINVAL;
+	}
+
+	tegra_dc_writel(dc, 0x00010001,
+			DC_DISP_SHIFT_CLOCK_OPTIONS);
+	tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div),
+			DC_DISP_DISP_CLOCK_CONTROL);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_dc_set_mode);
+
+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->pdev->dev,
+			"No default modes specified.  Leaving output disabled.\n");
+
+	switch (out->type) {
+	case TEGRA_DC_OUT_RGB:
+		dc->out_ops = &tegra_dc_rgb_ops;
+		break;
+
+	default:
+		dc->out_ops = NULL;
+		break;
+	}
+}
+
+
+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);
+
+	if (status & FRAME_END_INT) {
+		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))) {
+				dc->windows[i].dirty = 0;
+				completed = 1;
+			} else {
+				dirty = 1;
+			}
+		}
+
+		if (!dirty) {
+			val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+			val &= ~FRAME_END_INT;
+			tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+		}
+
+		spin_unlock_irqrestore(&dc->lock, flags);
+
+		if (completed)
+			wake_up(&dc->wq);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void tegra_dc_init(struct tegra_dc *dc)
+{
+	tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
+	if (dc->pdev->id = 0)
+		tegra_dc_writel(dc, 0x0000011a, DC_CMD_CONT_SYNCPT_VSYNC);
+	else if (dc->pdev->id = 1)
+		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);
+	tegra_dc_writel(dc, 0x00000020, DC_DISP_MEM_HIGH_PRIORITY);
+	tegra_dc_writel(dc, 0x00000001, DC_DISP_MEM_HIGH_PRIORITY_TIMER);
+
+	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->out_ops && dc->out_ops->init)
+		dc->out_ops->init(dc);
+}
+
+static int tegra_dc_probe(struct platform_device *pdev)
+{
+	struct tegra_dc *dc;
+	struct clk *clk;
+	struct clk *host1x_clk;
+	struct resource	*res;
+	struct resource *base_res;
+	struct resource *fb_mem = NULL;
+	int ret = 0;
+	void __iomem *base;
+	int irq;
+	int i;
+
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "no platform data\n");
+		return -ENOENT;
+	}
+
+	dc = kzalloc(sizeof(struct tegra_dc), GFP_KERNEL);
+	if (!dc) {
+		dev_err(&pdev->dev, "can't allocate memory for tegra_dc\n");
+		return -ENOMEM;
+	}
+
+	irq = platform_get_irq_byname(pdev, "irq");
+	if (irq <= 0) {
+		dev_err(&pdev->dev, "no irq\n");
+		ret = -ENOENT;
+		goto err_free;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+	if (!res) {
+		dev_err(&pdev->dev, "no mem resource\n");
+		ret = -ENOENT;
+		goto err_free;
+	}
+
+	base_res = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (!base_res) {
+		dev_err(&pdev->dev, "request_mem_region failed\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	base = ioremap(res->start, resource_size(res));
+	if (!base) {
+		dev_err(&pdev->dev, "registers can't be mapped\n");
+		ret = -EBUSY;
+		goto err_release_resource_reg;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fbmem");
+	if (res)
+		fb_mem = request_mem_region(res->start, resource_size(res), pdev->name);
+
+	host1x_clk = clk_get(&pdev->dev, "host1x");
+	if (IS_ERR_OR_NULL(host1x_clk)) {
+		dev_err(&pdev->dev, "can't get host1x clock\n");
+		ret = -ENOENT;
+		goto err_iounmap_reg;
+	}
+	clk_enable(host1x_clk);
+
+	clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR_OR_NULL(clk)) {
+		dev_err(&pdev->dev, "can't get clock\n");
+		ret = -ENOENT;
+
+		goto err_put_host1x_clk;
+	}
+	clk_enable(clk);
+	tegra_periph_reset_deassert(clk);
+
+	dc->clk = clk;
+	dc->host1x_clk = host1x_clk;
+	dc->base_res = base_res;
+	dc->base = base;
+	dc->irq = irq;
+	dc->pdev = pdev;
+	dc->pdata = pdev->dev.platform_data;
+	spin_lock_init(&dc->lock);
+	init_waitqueue_head(&dc->wq);
+
+
+	dc->n_windows = DC_N_WINDOWS;
+	for (i = 0; i < dc->n_windows; i++) {
+		dc->windows[i].idx = i;
+		dc->windows[i].dc = dc;
+	}
+
+	if (request_irq(irq, tegra_dc_irq, IRQF_DISABLED,
+			dev_name(&pdev->dev), dc)) {
+		dev_err(&pdev->dev, "request_irq %d failed\n", irq);
+		ret = -EBUSY;
+		goto err_put_clk;
+	}
+
+	ret = tegra_dc_add(dc, pdev->id);
+	if (ret < 0) {
+		dev_err(&pdev->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(&pdev->dev, "No default output specified.  Leaving output disabled.\n");
+	}
+
+	tegra_dc_init(dc);
+
+	tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
+
+	platform_set_drvdata(pdev, dc);
+
+	tegra_dc_dbg_add(dc);
+
+	dev_info(&pdev->dev, "probed\n");
+
+	if (fb_mem && dc->pdata->fb) {
+		dc->fb = tegra_fb_register(pdev, dc, dc->pdata->fb, fb_mem);
+		if (IS_ERR_OR_NULL(dc->fb))
+			dc->fb = NULL;
+	}
+
+	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);
+	if (fb_mem)
+		release_resource(fb_mem);
+err_release_resource_reg:
+	release_resource(base_res);
+err_free:
+	kfree(dc);
+
+	return ret;
+}
+
+static int tegra_dc_remove(struct platform_device *pdev)
+{
+	struct tegra_dc *dc = platform_get_drvdata(pdev);
+
+	if (dc->fb) {
+		tegra_fb_unregister(dc->fb);
+		release_resource(dc->fb_mem);
+	}
+
+	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);
+	release_resource(dc->base_res);
+	kfree(dc);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int tegra_dc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct tegra_dc *dc = platform_get_drvdata(pdev);
+
+	dev_info(&pdev->dev, "suspend\n");
+
+	disable_irq(dc->irq);
+	tegra_periph_reset_assert(dc->clk);
+	clk_disable(dc->clk);
+
+	return 0;
+}
+
+static int tegra_dc_resume(struct platform_device *pdev)
+{
+	struct tegra_dc *dc = platform_get_drvdata(pdev);
+	struct tegra_dc_win *wins[DC_N_WINDOWS];
+	int i;
+
+	dev_info(&pdev->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];
+
+	tegra_dc_init(dc);
+
+	tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
+	tegra_dc_update_windows(wins, dc->n_windows);
+
+	return 0;
+}
+
+#endif
+
+extern int suspend_set(const char *val, struct kernel_param *kp)
+{
+	if (!strcmp(val, "dump"))
+		dump_regs(tegra_dcs[0]);
+#ifdef CONFIG_PM
+	else if (!strcmp(val, "suspend"))
+		tegra_dc_suspend(tegra_dcs[0]->pdev, PMSG_SUSPEND);
+	else if (!strcmp(val, "resume"))
+		tegra_dc_resume(tegra_dcs[0]->pdev);
+#endif
+
+	return 0;
+}
+
+extern int suspend_get(char *buffer, struct kernel_param *kp)
+{
+	return 0;
+}
+
+int suspend;
+
+module_param_call(suspend, suspend_set, suspend_get, &suspend, 0644);
+
+struct platform_driver tegra_dc_driver = {
+	.driver = {
+		.name = "tegradc",
+		.owner = THIS_MODULE,
+	},
+	.probe = tegra_dc_probe,
+	.remove = tegra_dc_remove,
+#ifdef CONFIG_PM
+	.suspend = tegra_dc_suspend,
+	.resume = tegra_dc_resume,
+#endif
+};
+
+static int __init tegra_dc_module_init(void)
+{
+	return platform_driver_register(&tegra_dc_driver);
+}
+
+static void __exit tegra_dc_module_exit(void)
+{
+	platform_driver_unregister(&tegra_dc_driver);
+}
+
+module_exit(tegra_dc_module_exit);
+module_init(tegra_dc_module_init);
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
new file mode 100644
index 0000000..b2351b1
--- /dev/null
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -0,0 +1,86 @@
+/*
+ * drivers/video/tegra/dc/dc_priv.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Erik Gilling <konkers@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#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>
+
+struct tegra_dc;
+
+struct tegra_dc_out_ops {
+	void (*init)(struct tegra_dc *dc);
+};
+
+struct tegra_dc {
+	struct list_head		list;
+
+	struct platform_device		*pdev;
+	struct tegra_dc_platform_data	*pdata;
+
+	struct resource			*base_res;
+	void __iomem			*base;
+	int				irq;
+
+	struct clk			*clk;
+	struct clk			*host1x_clk;
+
+	struct tegra_dc_out		*out;
+	struct tegra_dc_out_ops		*out_ops;
+
+	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 resource			*fb_mem;
+	struct tegra_fb_info		*fb;
+};
+
+static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
+					   unsigned long reg)
+{
+	return readl(dc->base + reg * 4);
+}
+
+static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long val,
+				   unsigned long reg)
+{
+	writel(val, dc->base + reg * 4);
+}
+
+static inline void _tegra_dc_write_table(struct tegra_dc *dc, const u32 *table,
+					 unsigned len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		tegra_dc_writel(dc, table[i * 2 + 1], table[i * 2]);
+}
+
+#define tegra_dc_write_table(dc, table)		\
+	_tegra_dc_write_table(dc, table, ARRAY_SIZE(table) / 2)
+
+extern struct tegra_dc_out_ops tegra_dc_rgb_ops;
+
+#endif
diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h
new file mode 100644
index 0000000..6d6b3ba
--- /dev/null
+++ b/drivers/video/tegra/dc/dc_reg.h
@@ -0,0 +1,342 @@
+/*
+ * drivers/video/tegra/dc/dc_reg.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Erik Gilling <konkers@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_REG_H
+#define __DRIVERS_VIDEO_TEGRA_DC_DC_REG_H
+
+#define DC_CMD_GENERAL_INCR_SYNCPT		0x000
+#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL	0x001
+#define DC_CMD_GENERAL_INCR_SYNCPT_ERROR	0x002
+#define DC_CMD_WIN_A_INCR_SYNCPT		0x008
+#define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL		0x009
+#define DC_CMD_WIN_A_INCR_SYNCPT_ERROR		0x00a
+#define DC_CMD_WIN_B_INCR_SYNCPT		0x010
+#define DC_CMD_WIN_B_INCR_SYNCPT_CNTRL		0x011
+#define DC_CMD_WIN_B_INCR_SYNCPT_ERROR		0x012
+#define DC_CMD_WIN_C_INCR_SYNCPT		0x018
+#define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL		0x019
+#define DC_CMD_WIN_C_INCR_SYNCPT_ERROR		0x01a
+#define DC_CMD_CONT_SYNCPT_VSYNC		0x028
+#define DC_CMD_DISPLAY_COMMAND_OPTION0		0x031
+#define DC_CMD_DISPLAY_COMMAND			0x032
+#define  DISP_COMMAND_RAISE		(1 << 0)
+#define  DISP_CTRL_MODE_STOP		(0 << 5)
+#define  DISP_CTRL_MODE_C_DISPLAY	(1 << 5)
+#define  DISP_CTRL_MODE_NC_DISPLAY	(2 << 5)
+#define  DISP_COMMAND_RAISE_VECTOR(x)	(((x) & 0x1f) << 22)
+#define  DISP_COMMAND_RAISE_CHANNEL_ID(x)	(((x) & 0xf) << 27)
+
+#define DC_CMD_SIGNAL_RAISE			0x033
+#define DC_CMD_DISPLAY_POWER_CONTROL		0x036
+#define  PW0_ENABLE		(1 << 0)
+#define  PW1_ENABLE		(1 << 2)
+#define  PW2_ENABLE		(1 << 4)
+#define  PW3_ENABLE		(1 << 6)
+#define  PW4_ENABLE		(1 << 8)
+#define  PM0_ENABLE		(1 << 16)
+#define  PM1_ENABLE		(1 << 18)
+#define  SPI_ENABLE		(1 << 24)
+#define  HSPI_ENABLE		(1 << 25)
+
+#define DC_CMD_INT_STATUS			0x037
+#define DC_CMD_INT_MASK				0x038
+#define DC_CMD_INT_ENABLE			0x039
+#define DC_CMD_INT_TYPE				0x03a
+#define DC_CMD_INT_POLARITY			0x03b
+#define  CTXSW_INT		(1 << 0)
+#define  FRAME_END_INT		(1 << 1)
+#define  V_BLANK_INT		(1 << 2)
+#define  H_BLANK_INT		(1 << 3)
+#define  V_PULSE3_INT		(1 << 4)
+#define  SPI_BUSY_INT		(1 << 7)
+#define  WIN_A_UF_INT		(1 << 8)
+#define  WIN_B_UF_INT		(1 << 9)
+#define  WIN_C_UF_INT		(1 << 10)
+#define  MSF_INT		(1 << 12)
+#define  SSF_INT		(1 << 13)
+#define  WIN_A_OF_INT		(1 << 14)
+#define  WIN_B_OF_INT		(1 << 15)
+#define  WIN_C_OF_INT		(1 << 16)
+#define  GPIO_0_INT		(1 << 18)
+#define  GPIO_1_INT		(1 << 19)
+#define  GPIO_2_INT		(1 << 20)
+
+#define DC_CMD_SIGNAL_RAISE1			0x03c
+#define DC_CMD_SIGNAL_RAISE2			0x03d
+#define DC_CMD_SIGNAL_RAISE3			0x03e
+#define DC_CMD_STATE_ACCESS			0x040
+#define DC_CMD_STATE_CONTROL			0x041
+#define  GENERAL_ACT_REQ	(1 << 0)
+#define  WIN_A_ACT_REQ		(1 << 1)
+#define  WIN_B_ACT_REQ		(1 << 2)
+#define  WIN_C_ACT_REQ		(1 << 3)
+
+#define DC_CMD_DISPLAY_WINDOW_HEADER		0x042
+#define  WINDOW_A_SELECT		(1 << 4)
+#define  WINDOW_B_SELECT		(1 << 5)
+#define  WINDOW_C_SELECT		(1 << 6)
+
+#define DC_CMD_REG_ACT_CONTROL			0x043
+
+#define DC_COM_CRC_CONTROL			0x300
+#define DC_COM_CRC_CHECKSUM			0x301
+#define DC_COM_PIN_OUTPUT_ENABLE0		0x302
+#define DC_COM_PIN_OUTPUT_ENABLE1		0x303
+#define DC_COM_PIN_OUTPUT_ENABLE2		0x304
+#define DC_COM_PIN_OUTPUT_ENABLE3		0x305
+#define DC_COM_PIN_OUTPUT_POLARITY0		0x306
+#define DC_COM_PIN_OUTPUT_POLARITY1		0x307
+#define DC_COM_PIN_OUTPUT_POLARITY2		0x308
+#define DC_COM_PIN_OUTPUT_POLARITY3		0x309
+#define DC_COM_PIN_OUTPUT_DATA0			0x30a
+#define DC_COM_PIN_OUTPUT_DATA1			0x30b
+#define DC_COM_PIN_OUTPUT_DATA2			0x30c
+#define DC_COM_PIN_OUTPUT_DATA3			0x30d
+#define DC_COM_PIN_INPUT_ENABLE0		0x30e
+#define DC_COM_PIN_INPUT_ENABLE1		0x30f
+#define DC_COM_PIN_INPUT_ENABLE2		0x310
+#define DC_COM_PIN_INPUT_ENABLE3		0x311
+#define DC_COM_PIN_INPUT_DATA0			0x312
+#define DC_COM_PIN_INPUT_DATA1			0x313
+#define DC_COM_PIN_OUTPUT_SELECT0		0x314
+#define DC_COM_PIN_OUTPUT_SELECT1		0x315
+#define DC_COM_PIN_OUTPUT_SELECT2		0x316
+#define DC_COM_PIN_OUTPUT_SELECT3		0x317
+#define DC_COM_PIN_OUTPUT_SELECT4		0x318
+#define DC_COM_PIN_OUTPUT_SELECT5		0x319
+#define DC_COM_PIN_OUTPUT_SELECT6		0x31a
+#define DC_COM_PIN_MISC_CONTROL			0x31b
+#define DC_COM_PM0_CONTROL			0x31c
+#define DC_COM_PM0_DUTY_CYCLE			0x31d
+#define DC_COM_PM1_CONTROL			0x31e
+#define DC_COM_PM1_DUTY_CYCLE			0x31f
+#define DC_COM_SPI_CONTROL			0x320
+#define DC_COM_SPI_START_BYTE			0x321
+#define DC_COM_HSPI_WRITE_DATA_AB		0x322
+#define DC_COM_HSPI_WRITE_DATA_CD		0x323
+#define DC_COM_HSPI_CS_DC			0x324
+#define DC_COM_SCRATCH_REGISTER_A		0x325
+#define DC_COM_SCRATCH_REGISTER_B		0x326
+#define DC_COM_GPIO_CTRL			0x327
+#define DC_COM_GPIO_DEBOUNCE_COUNTER		0x328
+#define DC_COM_CRC_CHECKSUM_LATCHED		0x329
+
+#define DC_DISP_DISP_SIGNAL_OPTIONS0		0x400
+#define DC_DISP_DISP_SIGNAL_OPTIONS1		0x401
+#define DC_DISP_DISP_WIN_OPTIONS		0x402
+#define DC_DISP_MEM_HIGH_PRIORITY		0x403
+#define DC_DISP_MEM_HIGH_PRIORITY_TIMER		0x404
+#define DC_DISP_DISP_TIMING_OPTIONS		0x405
+#define DC_DISP_REF_TO_SYNC			0x406
+#define DC_DISP_SYNC_WIDTH			0x407
+#define DC_DISP_BACK_PORCH			0x408
+#define DC_DISP_DISP_ACTIVE			0x409
+#define DC_DISP_FRONT_PORCH			0x40a
+#define DC_DISP_H_PULSE0_CONTROL		0x40b
+#define DC_DISP_H_PULSE0_POSITION_A		0x40c
+#define DC_DISP_H_PULSE0_POSITION_B		0x40d
+#define DC_DISP_H_PULSE0_POSITION_C		0x40e
+#define DC_DISP_H_PULSE0_POSITION_D		0x40f
+#define DC_DISP_H_PULSE1_CONTROL		0x410
+#define DC_DISP_H_PULSE1_POSITION_A		0x411
+#define DC_DISP_H_PULSE1_POSITION_B		0x412
+#define DC_DISP_H_PULSE1_POSITION_C		0x413
+#define DC_DISP_H_PULSE1_POSITION_D		0x414
+#define DC_DISP_H_PULSE2_CONTROL		0x415
+#define DC_DISP_H_PULSE2_POSITION_A		0x416
+#define DC_DISP_H_PULSE2_POSITION_B		0x417
+#define DC_DISP_H_PULSE2_POSITION_C		0x418
+#define DC_DISP_H_PULSE2_POSITION_D		0x419
+#define DC_DISP_V_PULSE0_CONTROL		0x41a
+#define DC_DISP_V_PULSE0_POSITION_A		0x41b
+#define DC_DISP_V_PULSE0_POSITION_B		0x41c
+#define DC_DISP_V_PULSE0_POSITION_C		0x41d
+#define DC_DISP_V_PULSE1_CONTROL		0x41e
+#define DC_DISP_V_PULSE1_POSITION_A		0x41f
+#define DC_DISP_V_PULSE1_POSITION_B		0x420
+#define DC_DISP_V_PULSE1_POSITION_C		0x421
+#define DC_DISP_V_PULSE2_CONTROL		0x422
+#define DC_DISP_V_PULSE2_POSITION_A		0x423
+#define DC_DISP_V_PULSE3_CONTROL		0x424
+#define DC_DISP_V_PULSE3_POSITION_A		0x425
+#define DC_DISP_M0_CONTROL			0x426
+#define DC_DISP_M1_CONTROL			0x427
+#define DC_DISP_DI_CONTROL			0x428
+#define DC_DISP_PP_CONTROL			0x429
+#define DC_DISP_PP_SELECT_A			0x42a
+#define DC_DISP_PP_SELECT_B			0x42b
+#define DC_DISP_PP_SELECT_C			0x42c
+#define DC_DISP_PP_SELECT_D			0x42d
+#define DC_DISP_DISP_CLOCK_CONTROL		0x42e
+#define  PIXEL_CLK_DIVIDER_PCD1		(0 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD1H	(1 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD2		(2 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD3		(3 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD4		(4 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD6		(5 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD8		(6 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD9		(7 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD12	(8 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD16	(9 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD18	(10 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD24	(11 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD13	(12 << 8)
+#define  SHIFT_CLK_DIVIDER(x)		((x) & 0xff)
+
+#define DC_DISP_DISP_INTERFACE_CONTROL		0x42f
+#define  DISP_DATA_FORMAT_DF1P1C	(0 << 0)
+#define  DISP_DATA_FORMAT_DF1P2C24B	(1 << 0)
+#define  DISP_DATA_FORMAT_DF1P2C18B	(2 << 0)
+#define  DISP_DATA_FORMAT_DF1P2C16B	(3 << 0)
+#define  DISP_DATA_FORMAT_DF2S		(5 << 0)
+#define  DISP_DATA_FORMAT_DF3S		(6 << 0)
+#define  DISP_DATA_FORMAT_DFSPI		(7 << 0)
+#define  DISP_DATA_FORMAT_DF1P3C24B	(8 << 0)
+#define  DISP_DATA_FORMAT_DF1P3C18B	(9 << 0)
+#define  DISP_DATA_ALIGNMENT_MSB	(0 << 8)
+#define  DISP_DATA_ALIGNMENT_LSB	(1 << 8)
+#define  DISP_DATA_ORDER_RED_BLUE	(0 << 9)
+#define  DISP_DATA_ORDER_BLUE_RED	(1 << 9)
+
+#define DC_DISP_DISP_COLOR_CONTROL		0x430
+#define DC_DISP_SHIFT_CLOCK_OPTIONS		0x431
+#define DC_DISP_DATA_ENABLE_OPTIONS		0x432
+#define   DE_SELECT_ACTIVE_BLANK	0x0
+#define   DE_SELECT_ACTIVE		0x1
+#define   DE_SELECT_ACTIVE_IS		0x2
+#define   DE_CONTROL_ONECLK		(0 << 2)
+#define   DE_CONTROL_NORMAL		(1 << 2)
+#define   DE_CONTROL_EARLY_EXT		(2 << 2)
+#define   DE_CONTROL_EARLY		(3 << 2)
+#define   DE_CONTROL_ACTIVE_BLANK	(4 << 2)
+
+#define DC_DISP_SERIAL_INTERFACE_OPTIONS	0x433
+#define DC_DISP_LCD_SPI_OPTIONS			0x434
+#define DC_DISP_BORDER_COLOR			0x435
+#define DC_DISP_COLOR_KEY0_LOWER		0x436
+#define DC_DISP_COLOR_KEY0_UPPER		0x437
+#define DC_DISP_COLOR_KEY1_LOWER		0x438
+#define DC_DISP_COLOR_KEY1_UPPER		0x439
+#define DC_DISP_CURSOR_FOREGROUND		0x43c
+#define DC_DISP_CURSOR_BACKGROUND		0x43d
+#define DC_DISP_CURSOR_START_ADDR		0x43e
+#define DC_DISP_CURSOR_START_ADDR_NS		0x43f
+#define DC_DISP_CURSOR_POSITION			0x440
+#define DC_DISP_CURSOR_POSITION_NS		0x441
+#define DC_DISP_INIT_SEQ_CONTROL		0x442
+#define DC_DISP_SPI_INIT_SEQ_DATA_A		0x443
+#define DC_DISP_SPI_INIT_SEQ_DATA_B		0x444
+#define DC_DISP_SPI_INIT_SEQ_DATA_C		0x445
+#define DC_DISP_SPI_INIT_SEQ_DATA_D		0x446
+#define DC_DISP_DC_MCCIF_FIFOCTRL		0x480
+#define DC_DISP_MCCIF_DISPLAY0A_HYST		0x481
+#define DC_DISP_MCCIF_DISPLAY0B_HYST		0x482
+#define DC_DISP_MCCIF_DISPLAY0C_HYST		0x483
+#define DC_DISP_MCCIF_DISPLAY1B_HYST		0x484
+#define DC_DISP_DAC_CRT_CTRL			0x4c0
+#define DC_DISP_DISP_MISC_CONTROL		0x4c1
+
+#define DC_WINC_COLOR_PALETTE(x)		(0x500 + (x))
+
+#define DC_WINC_PALETTE_COLOR_EXT		0x600
+#define DC_WINC_H_FILTER_P(x)			(0x601 + (x))
+#define DC_WINC_CSC_YOF				0x611
+#define DC_WINC_CSC_KYRGB			0x612
+#define DC_WINC_CSC_KUR				0x613
+#define DC_WINC_CSC_KVR				0x614
+#define DC_WINC_CSC_KUG				0x615
+#define DC_WINC_CSC_KVG				0x616
+#define DC_WINC_CSC_KUB				0x617
+#define DC_WINC_CSC_KVB				0x618
+#define DC_WINC_V_FILTER_P(x)			(0x619 + (x))
+#define DC_WIN_WIN_OPTIONS			0x700
+#define  H_DIRECTION_INCREMENT		(0 << 0)
+#define  H_DIRECTION_DECREMENTT		(1 << 0)
+#define  V_DIRECTION_INCREMENT		(0 << 2)
+#define  V_DIRECTION_DECREMENTT		(1 << 2)
+#define  COLOR_EXPAND			(1 << 6)
+#define  CP_ENABLE			(1 << 16)
+#define  DV_ENABLE			(1 << 20)
+#define  WIN_ENABLE			(1 << 30)
+
+#define DC_WIN_BYTE_SWAP			0x701
+#define  BYTE_SWAP_NOSWAP		0
+#define  BYTE_SWAP_SWAP2		1
+#define  BYTE_SWAP_SWAP4		2
+#define  BYTE_SWAP_SWAP4HW		3
+
+#define DC_WIN_BUFFER_CONTROL			0x702
+#define  BUFFER_CONTROL_HOST		0
+#define  BUFFER_CONTROL_VI		1
+#define  BUFFER_CONTROL_EPP		2
+#define  BUFFER_CONTROL_MPEGE		3
+#define  BUFFER_CONTROL_SB2D		4
+
+#define DC_WIN_COLOR_DEPTH			0x703
+
+#define DC_WIN_POSITION				0x704
+#define  H_POSITION(x)		(((x) & 0xfff) << 0)
+#define  V_POSITION(x)		(((x) & 0xfff) << 16)
+
+#define DC_WIN_SIZE				0x705
+#define  H_SIZE(x)		(((x) & 0xfff) << 0)
+#define  V_SIZE(x)		(((x) & 0xfff) << 16)
+
+#define DC_WIN_PRESCALED_SIZE			0x706
+#define  H_PRESCALED_SIZE(x)	(((x) & 0x3fff) << 0)
+#define  V_PRESCALED_SIZE(x)	(((x) & 0xfff) << 16)
+
+#define DC_WIN_H_INITIAL_DDA			0x707
+#define DC_WIN_V_INITIAL_DDA			0x708
+#define DC_WIN_DDA_INCREMENT			0x709
+#define  H_DDA_INC(x)		(((x) & 0xffff) << 0)
+#define  V_DDA_INC(x)		(((x) & 0xffff) << 16)
+
+#define DC_WIN_LINE_STRIDE			0x70a
+#define DC_WIN_BUF_STRIDE			0x70b
+#define DC_WIN_UV_BUF_STRIDE			0x70c
+#define DC_WIN_BUFFER_ADDR_MODE			0x70d
+#define DC_WIN_DV_CONTROL			0x70e
+#define DC_WIN_BLEND_NOKEY			0x70f
+#define DC_WIN_BLEND_1WIN			0x710
+#define DC_WIN_BLEND_2WIN_X			0x711
+#define DC_WIN_BLEND_2WIN_Y			0x712
+#define DC_WIN_BLEND_3WIN_XY			0x713
+#define  CKEY_NOKEY			(0 << 0)
+#define  CKEY_KEY0			(1 << 0)
+#define  CKEY_KEY1			(2 << 0)
+#define  CKEY_KEY01			(3 << 0)
+#define  BLEND_CONTROL_FIX		(0 << 2)
+#define  BLEND_CONTROL_ALPHA		(1 << 2)
+#define  BLEND_CONTROL_DEPENDANT	(2 << 2)
+#define  BLEND_WEIGHT0(x)		(((x) & 0xff) << 8)
+#define  BLEND_WEIGHT1(x)		(((x) & 0xff) << 16)
+
+#define DC_WIN_HP_FETCH_CONTROL			0x714
+#define DC_WINBUF_START_ADDR			0x800
+#define DC_WINBUF_START_ADDR_NS			0x801
+#define DC_WINBUF_START_ADDR_U			0x802
+#define DC_WINBUF_START_ADDR_U_NS		0x803
+#define DC_WINBUF_START_ADDR_V			0x804
+#define DC_WINBUF_START_ADDR_V_NS		0x805
+#define DC_WINBUF_ADDR_H_OFFSET			0x806
+#define DC_WINBUF_ADDR_H_OFFSET_NS		0x807
+#define DC_WINBUF_ADDR_V_OFFSET			0x808
+#define DC_WINBUF_ADDR_V_OFFSET_NS		0x809
+#define DC_WINBUF_UFLOW_STATUS			0x80a
+
+#endif
diff --git a/drivers/video/tegra/dc/rgb.c b/drivers/video/tegra/dc/rgb.c
new file mode 100644
index 0000000..de1a8fa
--- /dev/null
+++ b/drivers/video/tegra/dc/rgb.c
@@ -0,0 +1,63 @@
+/*
+ * drivers/video/tegra/dc/rgb.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Erik Gilling <konkers@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+
+#include <mach/dc.h>
+
+#include "dc_reg.h"
+#include "dc_priv.h"
+
+
+static const u32 tegra_dc_rgb_pintable[] = {
+	DC_COM_PIN_OUTPUT_ENABLE0,	0x00000000,
+	DC_COM_PIN_OUTPUT_ENABLE1,	0x00000000,
+	DC_COM_PIN_OUTPUT_ENABLE2,	0x00000000,
+	DC_COM_PIN_OUTPUT_ENABLE3,	0x00000000,
+	DC_COM_PIN_OUTPUT_POLARITY0,	0x00000000,
+	DC_COM_PIN_OUTPUT_POLARITY1,	0x01000000,
+	DC_COM_PIN_OUTPUT_POLARITY2,	0x00000000,
+	DC_COM_PIN_OUTPUT_POLARITY3,	0x00000000,
+	DC_COM_PIN_OUTPUT_DATA0,	0x00000000,
+	DC_COM_PIN_OUTPUT_DATA1,	0x00000000,
+	DC_COM_PIN_OUTPUT_DATA2,	0x00000000,
+	DC_COM_PIN_OUTPUT_DATA3,	0x00000000,
+	DC_COM_PIN_OUTPUT_SELECT0,	0x00000000,
+	DC_COM_PIN_OUTPUT_SELECT1,	0x00000000,
+	DC_COM_PIN_OUTPUT_SELECT2,	0x00000000,
+	DC_COM_PIN_OUTPUT_SELECT3,	0x00000000,
+	DC_COM_PIN_OUTPUT_SELECT4,	0x00210222,
+	DC_COM_PIN_OUTPUT_SELECT5,	0x00002200,
+	DC_COM_PIN_OUTPUT_SELECT6,	0x00020000,
+};
+
+
+void tegra_dc_rgb_init(struct tegra_dc *dc)
+{
+	tegra_dc_writel(dc, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+			PW4_ENABLE | PM0_ENABLE | PM1_ENABLE,
+			DC_CMD_DISPLAY_POWER_CONTROL);
+
+	tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND);
+
+	tegra_dc_write_table(dc, tegra_dc_rgb_pintable);
+}
+
+struct tegra_dc_out_ops tegra_dc_rgb_ops = {
+	.init = tegra_dc_rgb_init,
+};
+
diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c
new file mode 100644
index 0000000..4db3958
--- /dev/null
+++ b/drivers/video/tegra/fb.c
@@ -0,0 +1,311 @@
+/*
+ * drivers/video/tegra/fb.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Erik Gilling <konkers@android.com>
+ *         Colin Cross <ccross@android.com>
+ *         Travis Geiselbrecht <travis@palm.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+#include <asm/atomic.h>
+
+#include <mach/dc.h>
+#include <mach/fb.h>
+
+struct tegra_fb_info {
+	struct tegra_dc_win	*win;
+	struct platform_device	*pdev;
+	struct fb_info		*info;
+
+	struct resource		*fb_mem;
+
+	int			xres;
+	int			yres;
+
+	atomic_t		in_use;
+};
+
+/* palette array used by the fbcon */
+static u32 pseudo_palette[16];
+
+static int tegra_fb_open(struct fb_info *info, int user)
+{
+	struct tegra_fb_info *tegra_fb = info->par;
+
+	if (atomic_xchg(&tegra_fb->in_use, 1))
+		return -EBUSY;
+
+	return 0;
+}
+
+static int tegra_fb_release(struct fb_info *info, int user)
+{
+	struct tegra_fb_info *tegra_fb = info->par;
+
+	WARN_ON(!atomic_xchg(&tegra_fb->in_use, 0));
+
+	return 0;
+}
+
+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))
+		return -EINVAL;
+	return 0;
+}
+
+static int tegra_fb_set_par(struct fb_info *info)
+{
+	struct tegra_fb_info *tegra_fb = info->par;
+	struct fb_var_screeninfo *var = &info->var;
+
+	/* we only support RGB ordering for now */
+	switch (var->bits_per_pixel) {
+	case 32:
+	case 24:
+		var->red.offset = 0;
+		var->red.length = 8;
+		var->green.offset = 8;
+		var->green.length = 8;
+		var->blue.offset = 16;
+		var->blue.length = 8;
+		tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8;
+		break;
+	case 16:
+		var->red.offset = 11;
+		var->red.length = 5;
+		var->green.offset = 5;
+		var->green.length = 6;
+		var->blue.offset = 0;
+		var->blue.length = 5;
+		tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
+		break;
+	default:
+		return -EINVAL;
+	}
+	info->fix.line_length = var->xres * var->bits_per_pixel / 8;
+
+	tegra_dc_update_windows(&tegra_fb->win, 1);
+
+	return 0;
+}
+
+static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+	unsigned blue, unsigned transp, struct fb_info *info)
+{
+	struct fb_var_screeninfo *var = &info->var;
+
+	if (info->fix.visual = FB_VISUAL_TRUECOLOR ||
+	    info->fix.visual = FB_VISUAL_DIRECTCOLOR) {
+		u32 v;
+
+		if (regno >= 16)
+			return -EINVAL;
+
+		v = (red << var->red.offset) |
+			(green << var->green.offset) |
+			(blue << var->blue.offset);
+
+		((u32 *)info->pseudo_palette)[regno] = v;
+	}
+
+	return 0;
+}
+
+static int tegra_fb_pan_display(struct fb_var_screeninfo *var,
+				struct fb_info *info)
+{
+	struct tegra_fb_info *tegra_fb = info->par;
+	char __iomem *flush_start;
+	char __iomem *flush_end;
+	u32 addr;
+
+	flush_start = info->screen_base + (var->yoffset * info->fix.line_length);
+	flush_end = flush_start + (var->yres * info->fix.line_length);
+
+	info->var.xoffset = var->xoffset;
+	info->var.yoffset = var->yoffset;
+
+	addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) +
+		(var->xoffset * (var->bits_per_pixel/8));
+
+	tegra_fb->win->phys_addr = addr;
+	/* TODO: update virt_addr */
+
+	tegra_dc_update_windows(&tegra_fb->win, 1);
+	tegra_dc_sync_windows(&tegra_fb->win, 1);
+
+	return 0;
+}
+
+static void tegra_fb_fillrect(struct fb_info *info,
+			      const struct fb_fillrect *rect)
+{
+	cfb_fillrect(info, rect);
+}
+
+static void tegra_fb_copyarea(struct fb_info *info,
+			      const struct fb_copyarea *region)
+{
+	cfb_copyarea(info, region);
+}
+
+static void tegra_fb_imageblit(struct fb_info *info,
+			       const struct fb_image *image)
+{
+	cfb_imageblit(info, image);
+}
+
+static struct fb_ops tegra_fb_ops = {
+	.owner = THIS_MODULE,
+	.fb_open = tegra_fb_open,
+	.fb_release = tegra_fb_release,
+	.fb_check_var = tegra_fb_check_var,
+	.fb_set_par = tegra_fb_set_par,
+	.fb_setcolreg = tegra_fb_setcolreg,
+	.fb_pan_display = tegra_fb_pan_display,
+	.fb_fillrect = tegra_fb_fillrect,
+	.fb_copyarea = tegra_fb_copyarea,
+	.fb_imageblit = tegra_fb_imageblit,
+};
+
+struct tegra_fb_info *tegra_fb_register(struct platform_device *pdev,
+					struct tegra_dc *dc,
+					struct tegra_fb_data *fb_data,
+					struct resource *fb_mem)
+{
+	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;
+	int ret = 0;
+
+	win = tegra_dc_get_window(dc, fb_data->win);
+	if (!win) {
+		dev_err(&pdev->dev, "dc does not have a window at index %d\n",
+			fb_data->win);
+		return ERR_PTR(-ENOENT);
+	}
+
+	info = framebuffer_alloc(sizeof(struct tegra_fb_info), &pdev->dev);
+	if (!info) {
+		ret = -ENOMEM;
+		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(&pdev->dev, "fb can't be mapped\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	tegra_fb = info->par;
+	tegra_fb->win = win;
+	tegra_fb->pdev = pdev;
+	tegra_fb->fb_mem = fb_mem;
+	tegra_fb->xres = fb_data->xres;
+	tegra_fb->yres = fb_data->yres;
+	atomic_set(&tegra_fb->in_use, 0);
+
+	info->fbops = &tegra_fb_ops;
+	info->pseudo_palette = pseudo_palette;
+	info->screen_base = fb_base;
+	info->screen_size = fb_size;
+
+	strlcpy(info->fix.id, "tegra_fb", sizeof(info->fix.id));
+	info->fix.type		= FB_TYPE_PACKED_PIXELS;
+	info->fix.visual	= FB_VISUAL_TRUECOLOR;
+	info->fix.xpanstep	= 1;
+	info->fix.ypanstep	= 1;
+	info->fix.accel		= FB_ACCEL_NONE;
+	info->fix.smem_start	= fb_phys;
+	info->fix.smem_len	= fb_size;
+
+	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.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.left_margin		= 0;
+	info->var.right_margin		= 0;
+	info->var.upper_margin		= 0;
+	info->var.lower_margin		= 0;
+	info->var.hsync_len		= 0;
+	info->var.vsync_len		= 0;
+	info->var.vmode			= FB_VMODE_NONINTERLACED;
+
+	win->x = 0;
+	win->y = 0;
+	win->w = fb_data->xres;
+	win->h = fb_data->yres;
+	/* TODO: set to output res dc */
+	win->out_w = fb_data->xres;
+	win->out_h = fb_data->yres;
+	win->phys_addr = fb_phys;
+	win->virt_addr = fb_base;
+	win->flags = TEGRA_WIN_FLAG_ENABLED | TEGRA_WIN_FLAG_COLOR_EXPAND;
+
+	tegra_fb_set_par(info);
+
+	if (register_framebuffer(info)) {
+		dev_err(&pdev->dev, "failed to register framebuffer\n");
+		ret = -ENODEV;
+		goto err_iounmap_fb;
+	}
+
+	tegra_fb->info = info;
+
+	dev_info(&pdev->dev, "probed\n");
+
+	return tegra_fb;
+
+err_iounmap_fb:
+	iounmap(fb_base);
+err_free:
+	framebuffer_release(info);
+err:
+	return ERR_PTR(ret);
+}
+
+void tegra_fb_unregister(struct tegra_fb_info *fb_info)
+{
+	struct fb_info *info = fb_info->info;
+
+	unregister_framebuffer(info);
+	iounmap(info->screen_base);
+	framebuffer_release(info);
+}
-- 
1.6.5.6


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] video: tegra: add tegra display controller and fb driver
  2010-08-11 23:11 [PATCH] video: tegra: add tegra display controller and fb driver Erik Gilling
@ 2010-08-12  4:34 ` Ryan Mallon
       [not found]   ` <AANLkTini+n1osaQHkomguzZi-DaHLX3i1G4gA82bK0=S@mail.gmail.com>
  2010-08-25 22:04 ` Erik Gilling
  1 sibling, 1 reply; 6+ messages in thread
From: Ryan Mallon @ 2010-08-12  4:34 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/12/2010 11:11 AM, Erik Gilling wrote:
> This patch supersedes the previous framebuffer patch
> 
> Supports:
> 	* panel setup
> 	* overlays
> 	* suspend / resume
> 
> Notable ommisions:
> 	* support for anything but lvds panels
> 	* inegration with nvhost driver to sync updates with 3D
> 	* FB physical geometry is not set
> 	* lacks interface to set overlay/window x,y offset
> 
> Signed-off-by: Erik Gilling <konkers@android.com>
> Cc: Colin Cross <ccross@android.com>
> Cc: Travis Geiselbrecht <travis@palm.com>

Hi Erik,

Just a couple of notes, not really a full review.

> +++ b/drivers/video/tegra/Kconfig
> @@ -0,0 +1,15 @@
> +config TEGRA_DC
> +	tristate "Tegra Display Contoller"
> +	depends on ARCH_TEGRA
> +	help
> +	  Tegra display controller support.
> +
> +config FB_TEGRA
> +	tristate "Tegra Framebuffer driver"
> +	depends on TEGRA_DC && FB = y

How come this depends on FB=y?

> +++ b/drivers/video/tegra/dc/Makefile
> @@ -0,0 +1,2 @@
> +obj-y += dc.o
> +obj-y += rgb.o
> \ No newline at end of file

Is that meant to be there?

> +struct tegra_dc_blend tegra_dc_blend_modes[][DC_N_WINDOWS] = {
> +	{{.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .two_win_x = BLEND(NOKEY, FIX, 0x00, 0x00),
> +	  .two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
> +	  .three_win_xy = BLEND(NOKEY, FIX, 0x00, 0x00)},
> +	 {.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .two_win_x = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
> +	  .three_win_xy = BLEND(NOKEY, DEPENDANT, 0x00, 0x00)},
> +	 {.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
> +	  .two_win_x = BLEND(NOKEY, ALPHA, 0xff, 0xff),
> +	  .two_win_y = BLEND(NOKEY, ALPHA, 0xff, 0xff),
> +	  .three_win_xy = BLEND(NOKEY, ALPHA, 0xff, 0xff)}
> +	}
> +};

Tab delimiting this would make it a bit more readable.

> +#define DUMP_REG(a) do {			\
> +	snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \
> +		 #a, a, tegra_dc_readl(dc, a));		      \
> +	print(data, buff);				      \
> +	} while (0)
> +
> +static void _dump_regs(struct tegra_dc *dc, void *data,
> +		       void (* print)(void *data, const char *str))
> +{
> +	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);

<snip>

The dump_regs code is very long for a debugging feature. Can it just be
replaced by a for loop which prints the offsets and values of each register?
> +
> +#undef DUMP_REG
> +
> +#ifdef DEBUG
> +static void dump_regs_print(void *data, const char *str)
> +{
> +	struct tegra_dc *dc = data;
> +	dev_dbg(&dc->pdev->dev, "%s", str);
> +}
> +
> +static void dump_regs(struct tegra_dc *dc)
> +{
> +	_dump_regs(dc, dc, dump_regs_print);
> +}
> +#else
> +
> +static void dump_regs(struct tegra_dc *dc) {}
> +
> +#endif
> +
> +#ifdef CONFIG_DEBUG_FS
> +
> +static void dbg_regs_print(void *data, const char *str)
> +{
> +	struct seq_file *s = data;
> +
> +	seq_printf(s, "%s", str);
> +}
> +
> +#undef DUMP_REG
> +
> +static int dbg_dc_show(struct seq_file *s, void *unused)
> +{
> +	struct tegra_dc *dc = s->private;
> +
> +	_dump_regs(dc, s, dbg_regs_print);
> +
> +	return 0;
> +}

This all looks a bit confusing (especially the undef stuff). Why do you
have both a debugfs interface to the registers and one which prints them
using dev_dbg?

> +static void tegra_dc_dbg_add(struct tegra_dc *dc)
> +{
> +	char name[32];
> +
> +	snprintf(name, sizeof(name), "tegra_dc%d_regs", dc->pdev->id);
> +	(void) debugfs_create_file(name, S_IRUGO, NULL, dc, &dbg_fops);

Don't cast the return value to void. Possibly print a warning if the
call fails?

> +/* does not support syncing windows on multiple dcs in one call */
> +int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
> +{
> +	if (n < 1 || n > DC_N_WINDOWS)
> +		return -EINVAL;

 if (n < 1 || n >= DC_N_WINDOWS) ?

> +	return wait_event_interruptible_timeout(windows[0]->dc->wq,
> +					 tegra_dc_windows_are_clean(windows, n),
> +					 HZ);
> +}
> +EXPORT_SYMBOL(tegra_dc_sync_windows);

<snip>

> +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;
> +
> +

There are a few places like this with excess blank lines.

> +	tegra_dc_init(dc);
> +
> +	tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
> +
> +	platform_set_drvdata(pdev, dc);
> +
> +	tegra_dc_dbg_add(dc);
> +
> +	dev_info(&pdev->dev, "probed\n");

dev_dbg? Also, probably don't need all those blank lines.

> +#ifdef CONFIG_PM
> +static int tegra_dc_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +	struct tegra_dc *dc = platform_get_drvdata(pdev);
> +
> +	dev_info(&pdev->dev, "suspend\n");

dev_dbg?

> +	disable_irq(dc->irq);
> +	tegra_periph_reset_assert(dc->clk);
> +	clk_disable(dc->clk);
> +
> +	return 0;
> +}
> +
> +static int tegra_dc_resume(struct platform_device *pdev)
> +{
> +	struct tegra_dc *dc = platform_get_drvdata(pdev);
> +	struct tegra_dc_win *wins[DC_N_WINDOWS];
> +	int i;
> +
> +	dev_info(&pdev->dev, "resume\n");

dev_dbg?

> +extern int suspend_get(char *buffer, struct kernel_param *kp)
> +{
> +	return 0;
> +}
> +
> +int suspend;

Should be static? Have a global called suspend is likely to cause issues
somewhere.

> +module_param_call(suspend, suspend_set, suspend_get, &suspend, 0644);
> +
> +struct platform_driver tegra_dc_driver = {
> +	.driver = {
> +		.name = "tegradc",
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = tegra_dc_probe,
> +	.remove = tegra_dc_remove,
> +#ifdef CONFIG_PM
> +	.suspend = tegra_dc_suspend,
> +	.resume = tegra_dc_resume,
> +#endif
> +};

Also should be static?

> +static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
> +					   unsigned long reg)
> +{
> +	return readl(dc->base + reg * 4);
> +}

Can these functions use __raw_readl/__raw_writel?

> +static void tegra_fb_fillrect(struct fb_info *info,
> +			      const struct fb_fillrect *rect)
> +{
> +	cfb_fillrect(info, rect);
> +}
> +
> +static void tegra_fb_copyarea(struct fb_info *info,
> +			      const struct fb_copyarea *region)
> +{
> +	cfb_copyarea(info, region);
> +}
> +
> +static void tegra_fb_imageblit(struct fb_info *info,
> +			       const struct fb_image *image)
> +{
> +	cfb_imageblit(info, image);
> +}

You can just set the cfb_ functions directly in the fb_ops. See below:

> +static struct fb_ops tegra_fb_ops = {
> +	.owner = THIS_MODULE,
> +	.fb_open = tegra_fb_open,
> +	.fb_release = tegra_fb_release,
> +	.fb_check_var = tegra_fb_check_var,
> +	.fb_set_par = tegra_fb_set_par,
> +	.fb_setcolreg = tegra_fb_setcolreg,
> +	.fb_pan_display = tegra_fb_pan_display,
> +	.fb_fillrect = tegra_fb_fillrect,
> +	.fb_copyarea = tegra_fb_copyarea,
> +	.fb_imageblit = tegra_fb_imageblit,

Should be:

	.fb_fillrect  = cfb_fillrect,
	.fb_copyarea  = cfb_fillarea,
	.fb_imageblit = cfb_imageblit,

Also (nitpicky) tab delimit to make it more readable.

~Ryan

-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751
Fax:   +64 3 3779135			  USA 1800 261 2934

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] video: tegra: add tegra display controller and fb driver
  2010-08-11 23:11 [PATCH] video: tegra: add tegra display controller and fb driver Erik Gilling
  2010-08-12  4:34 ` Ryan Mallon
@ 2010-08-25 22:04 ` Erik Gilling
  1 sibling, 0 replies; 6+ messages in thread
From: Erik Gilling @ 2010-08-25 22:04 UTC (permalink / raw)
  To: linux-arm-kernel

ping.  any more comments.  I'd like to have this ready for the next
merge window.

On Wed, Aug 11, 2010 at 4:11 PM, Erik Gilling <konkers@android.com> wrote:
> This patch supersedes the previous framebuffer patch
>
> Supports:
>        * panel setup
>        * overlays
>        * suspend / resume
>
> Notable ommisions:
>        * support for anything but lvds panels
>        * inegration with nvhost driver to sync updates with 3D
>        * FB physical geometry is not set
>        * lacks interface to set overlay/window x,y offset
>
> Signed-off-by: Erik Gilling <konkers@android.com>
> Cc: Colin Cross <ccross@android.com>
> Cc: Travis Geiselbrecht <travis@palm.com>
> ---
>  arch/arm/mach-tegra/include/mach/dc.h |  152 ++++++
>  arch/arm/mach-tegra/include/mach/fb.h |   44 ++
>  drivers/video/Kconfig                 |    1 +
>  drivers/video/Makefile                |    1 +
>  drivers/video/tegra/Kconfig           |   15 +
>  drivers/video/tegra/Makefile          |    2 +
>  drivers/video/tegra/dc/Makefile       |    2 +
>  drivers/video/tegra/dc/dc.c           |  894 +++++++++++++++++++++++++++++++++
>  drivers/video/tegra/dc/dc_priv.h      |   86 ++++
>  drivers/video/tegra/dc/dc_reg.h       |  342 +++++++++++++
>  drivers/video/tegra/dc/rgb.c          |   63 +++
>  drivers/video/tegra/fb.c              |  311 ++++++++++++
>  12 files changed, 1913 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/mach-tegra/include/mach/dc.h
>  create mode 100644 arch/arm/mach-tegra/include/mach/fb.h
>  create mode 100644 drivers/video/tegra/Kconfig
>  create mode 100644 drivers/video/tegra/Makefile
>  create mode 100644 drivers/video/tegra/dc/Makefile
>  create mode 100644 drivers/video/tegra/dc/dc.c
>  create mode 100644 drivers/video/tegra/dc/dc_priv.h
>  create mode 100644 drivers/video/tegra/dc/dc_reg.h
>  create mode 100644 drivers/video/tegra/dc/rgb.c
>  create mode 100644 drivers/video/tegra/fb.c
>
> diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
> new file mode 100644
> index 0000000..b8a0e7a
> --- /dev/null
> +++ b/arch/arm/mach-tegra/include/mach/dc.h
> @@ -0,0 +1,152 @@
> +/*
> + * arch/arm/mach-tegra/include/mach/dc.h
> + *
> + * Copyright (C) 2010 Google, Inc.
> + *
> + * Author:
> + *     Erik Gilling <konkers@google.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef __MACH_TEGRA_DC_H
> +#define __MACH_TEGRA_DC_H
> +
> +
> +#define TEGRA_MAX_DC           2
> +#define DC_N_WINDOWS           3
> +
> +struct tegra_dc_blend {
> +       u32     nokey;
> +       u32     one_win;
> +       u32     two_win_x;
> +       u32     two_win_y;
> +       u32     three_win_xy;
> +};
> +
> +#define BLEND(key, control, weight0, weight1)                          \
> +       (CKEY_ ## key | BLEND_CONTROL_ ## control |                     \
> +        BLEND_WEIGHT0(weight0) | BLEND_WEIGHT0(weight1))
> +
> +struct tegra_dc_mode {
> +       int     pclk;
> +       int     h_ref_to_sync;
> +       int     v_ref_to_sync;
> +       int     h_sync_width;
> +       int     v_sync_width;
> +       int     h_back_porch;
> +       int     v_back_porch;
> +       int     h_active;
> +       int     v_active;
> +       int     h_front_porch;
> +       int     v_front_porch;
> +};
> +
> +enum {
> +       TEGRA_DC_OUT_RGB,
> +};
> +
> +struct tegra_dc_out {
> +       int                     type;
> +
> +       unsigned                order;
> +       unsigned                align;
> +
> +       struct tegra_dc_mode    *modes;
> +       int                     n_modes;
> +};
> +
> +#define TEGRA_DC_ALIGN_MSB             0
> +#define TEGRA_DC_ALIGN_LSB             1
> +
> +#define TEGRA_DC_ORDER_RED_BLUE                0
> +#define TEGRA_DC_ORDER_BLUE_RED                1
> +
> +struct tegra_dc;
> +
> +struct tegra_dc_win {
> +       u8                      idx;
> +       u8                      fmt;
> +       u32                     flags;
> +
> +       void                    *virt_addr;
> +       dma_addr_t              phys_addr;
> +       unsigned                x;
> +       unsigned                y;
> +       unsigned                w;
> +       unsigned                h;
> +       unsigned                out_w;
> +       unsigned                out_h;
> +
> +       int                     dirty;
> +       struct tegra_dc         *dc;
> +};
> +
> +#define TEGRA_WIN_FLAG_ENABLED         (1 << 0)
> +#define TEGRA_WIN_FLAG_COLOR_EXPAND    (1 << 1)
> +
> +/* Note: These are the actual values written to the DC_WIN_COLOR_DEPTH register
> + * and may change in new tegra architectures.
> + */
> +#define TEGRA_WIN_FMT_P1               0
> +#define TEGRA_WIN_FMT_P2               1
> +#define TEGRA_WIN_FMT_P4               2
> +#define TEGRA_WIN_FMT_P8               3
> +#define TEGRA_WIN_FMT_B4G4R4A4         4
> +#define TEGRA_WIN_FMT_B5G5R5A          5
> +#define TEGRA_WIN_FMT_B5G6R5           6
> +#define TEGRA_WIN_FMT_AB5G5R5          7
> +#define TEGRA_WIN_FMT_B8G8R8A8         12
> +#define TEGRA_WIN_FMT_R8G8B8A8         13
> +#define TEGRA_WIN_FMT_B6x2G6x2R6x2A8   14
> +#define TEGRA_WIN_FMT_R6x2G6x2B6x2A8   15
> +#define TEGRA_WIN_FMT_YCbCr422         16
> +#define TEGRA_WIN_FMT_YUV422           17
> +#define TEGRA_WIN_FMT_YCbCr420P                18
> +#define TEGRA_WIN_FMT_YUV420P          19
> +#define TEGRA_WIN_FMT_YCbCr422P                20
> +#define TEGRA_WIN_FMT_YUV422P          21
> +#define TEGRA_WIN_FMT_YCbCr422R                22
> +#define TEGRA_WIN_FMT_YUV422R          23
> +#define TEGRA_WIN_FMT_YCbCr422RA       24
> +#define TEGRA_WIN_FMT_YUV422RA         25
> +
> +struct tegra_fb_data {
> +       int             win;
> +
> +       int             xres;
> +       int             yres;
> +       int             bits_per_pixel;
> +};
> +
> +struct tegra_dc_platform_data {
> +       unsigned long           flags;
> +       struct tegra_dc_out     *default_out;
> +       struct tegra_fb_data    *fb;
> +};
> +
> +#define TEGRA_DC_FLAG_ENABLED          (1 << 0)
> +
> +struct tegra_dc *tegra_dc_get_dc(unsigned idx);
> +struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win);
> +
> +/* tegra_dc_update_windows and tegra_dc_sync_windows do not support windows
> + * with differenct dcs in one call
> + */
> +int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n);
> +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);
> +
> +#endif
> diff --git a/arch/arm/mach-tegra/include/mach/fb.h b/arch/arm/mach-tegra/include/mach/fb.h
> new file mode 100644
> index 0000000..9e5f7f8
> --- /dev/null
> +++ b/arch/arm/mach-tegra/include/mach/fb.h
> @@ -0,0 +1,44 @@
> +/*
> + * arch/arm/mach-tegra/include/mach/fb.h
> + *
> + * Copyright (C) 2010 Google, Inc.
> + *
> + * Author:
> + *     Erik Gilling <konkers@google.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef __MACH_TEGRA_FB_H
> +#define __MACH_TEGRA_FB_H
> +
> +#ifdef CONFIG_FB_TEGRA
> +struct tegra_fb_info *tegra_fb_register(struct platform_device *pdev,
> +                                       struct tegra_dc *dc,
> +                                       struct tegra_fb_data *fb_data,
> +                                       struct resource *fb_mem);
> +void tegra_fb_unregister(struct tegra_fb_info *fb_info);
> +#else
> +static inline
> +struct tegra_fb_info *tegra_fb_register(struct platform_device *pdev,
> +                                       struct tegra_dc *dc,
> +                                       struct tegra_fb_data *fb_data,
> +                                       struct resource *fb_mem)
> +{
> +       return NULL;
> +}
> +
> +static inline void tegra_fb_unregister(struct tegra_fb_info *fb_info)
> +{
> +}
> +#endif
> +
> +#endif
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 3d94a14..1eb0ac1 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -2231,6 +2231,7 @@ config FB_BROADSHEET
>
>  source "drivers/video/omap/Kconfig"
>  source "drivers/video/omap2/Kconfig"
> +source "drivers/video/tegra/Kconfig"
>
>  source "drivers/video/backlight/Kconfig"
>  source "drivers/video/display/Kconfig"
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index ddc2af2..21b527d 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -131,6 +131,7 @@ obj-$(CONFIG_FB_CARMINE)          += carminefb.o
>  obj-$(CONFIG_FB_MB862XX)         += mb862xx/
>  obj-$(CONFIG_FB_MSM)              += msm/
>  obj-$(CONFIG_FB_NUC900)           += nuc900fb.o
> +obj-y                             += tegra/
>
>  # Platform or fallback drivers go here
>  obj-$(CONFIG_FB_UVESA)            += uvesafb.o
> diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig
> new file mode 100644
> index 0000000..b01dc95
> --- /dev/null
> +++ b/drivers/video/tegra/Kconfig
> @@ -0,0 +1,15 @@
> +config TEGRA_DC
> +       tristate "Tegra Display Contoller"
> +       depends on ARCH_TEGRA
> +       help
> +         Tegra display controller support.
> +
> +config FB_TEGRA
> +       tristate "Tegra Framebuffer driver"
> +       depends on TEGRA_DC && FB = y
> +       select FB_CFB_FILLRECT
> +       select FB_CFB_COPYAREA
> +       select FB_CFB_IMAGEBLIT
> +       default FB
> +       help
> +         Framebuffer device support for the Tegra display controller.
> diff --git a/drivers/video/tegra/Makefile b/drivers/video/tegra/Makefile
> new file mode 100644
> index 0000000..8f9d0e2
> --- /dev/null
> +++ b/drivers/video/tegra/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_TEGRA_DC) += dc/
> +obj-$(CONFIG_FB_TEGRA) += fb.o
> diff --git a/drivers/video/tegra/dc/Makefile b/drivers/video/tegra/dc/Makefile
> new file mode 100644
> index 0000000..3ecb63c
> --- /dev/null
> +++ b/drivers/video/tegra/dc/Makefile
> @@ -0,0 +1,2 @@
> +obj-y += dc.o
> +obj-y += rgb.o
> \ No newline at end of file
> diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
> new file mode 100644
> index 0000000..261bced
> --- /dev/null
> +++ b/drivers/video/tegra/dc/dc.c
> @@ -0,0 +1,894 @@
> +/*
> + * drivers/video/tegra/dc/dc.c
> + *
> + * Copyright (C) 2010 Google, Inc.
> + * Author: Erik Gilling <konkers@android.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/workqueue.h>
> +#include <linux/ktime.h>
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>
> +
> +#include <mach/clk.h>
> +#include <mach/dc.h>
> +#include <mach/fb.h>
> +
> +#include "dc_reg.h"
> +#include "dc_priv.h"
> +
> +struct tegra_dc_blend tegra_dc_blend_modes[][DC_N_WINDOWS] = {
> +       {{.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
> +         .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
> +         .two_win_x = BLEND(NOKEY, FIX, 0x00, 0x00),
> +         .two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
> +         .three_win_xy = BLEND(NOKEY, FIX, 0x00, 0x00)},
> +        {.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
> +         .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
> +         .two_win_x = BLEND(NOKEY, FIX, 0xff, 0xff),
> +         .two_win_y = BLEND(NOKEY, DEPENDANT, 0x00, 0x00),
> +         .three_win_xy = BLEND(NOKEY, DEPENDANT, 0x00, 0x00)},
> +        {.nokey = BLEND(NOKEY, FIX, 0xff, 0xff),
> +         .one_win = BLEND(NOKEY, FIX, 0xff, 0xff),
> +         .two_win_x = BLEND(NOKEY, ALPHA, 0xff, 0xff),
> +         .two_win_y = BLEND(NOKEY, ALPHA, 0xff, 0xff),
> +         .three_win_xy = BLEND(NOKEY, ALPHA, 0xff, 0xff)}
> +       }
> +};
> +
> +struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
> +
> +DEFINE_MUTEX(tegra_dc_lock);
> +
> +static inline int tegra_dc_fmt_bpp(int fmt)
> +{
> +       switch (fmt) {
> +       case TEGRA_WIN_FMT_P1:
> +               return 1;
> +
> +       case TEGRA_WIN_FMT_P2:
> +               return 2;
> +
> +       case TEGRA_WIN_FMT_P4:
> +               return 4;
> +
> +       case TEGRA_WIN_FMT_P8:
> +               return 8;
> +
> +       case TEGRA_WIN_FMT_B4G4R4A4:
> +       case TEGRA_WIN_FMT_B5G5R5A:
> +       case TEGRA_WIN_FMT_B5G6R5:
> +       case TEGRA_WIN_FMT_AB5G5R5:
> +               return 16;
> +
> +       case TEGRA_WIN_FMT_B8G8R8A8:
> +       case TEGRA_WIN_FMT_R8G8B8A8:
> +       case TEGRA_WIN_FMT_B6x2G6x2R6x2A8:
> +       case TEGRA_WIN_FMT_R6x2G6x2B6x2A8:
> +               return 32;
> +
> +       case TEGRA_WIN_FMT_YCbCr422:
> +       case TEGRA_WIN_FMT_YUV422:
> +       case TEGRA_WIN_FMT_YCbCr420P:
> +       case TEGRA_WIN_FMT_YUV420P:
> +       case TEGRA_WIN_FMT_YCbCr422P:
> +       case TEGRA_WIN_FMT_YUV422P:
> +       case TEGRA_WIN_FMT_YCbCr422R:
> +       case TEGRA_WIN_FMT_YUV422R:
> +       case TEGRA_WIN_FMT_YCbCr422RA:
> +       case TEGRA_WIN_FMT_YUV422RA:
> +               /* FIXME: need to know the bpp of these formats */
> +               return 0;
> +       }
> +       return 0;
> +}
> +
> +#define DUMP_REG(a) do {                       \
> +       snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n", \
> +                #a, a, tegra_dc_readl(dc, a));               \
> +       print(data, buff);                                    \
> +       } while (0)
> +
> +static void _dump_regs(struct tegra_dc *dc, void *data,
> +                      void (* print)(void *data, const char *str))
> +{
> +       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);
> +       DUMP_REG(DC_CMD_INT_STATUS);
> +       DUMP_REG(DC_CMD_INT_MASK);
> +       DUMP_REG(DC_CMD_INT_ENABLE);
> +       DUMP_REG(DC_CMD_INT_TYPE);
> +       DUMP_REG(DC_CMD_INT_POLARITY);
> +       DUMP_REG(DC_CMD_SIGNAL_RAISE1);
> +       DUMP_REG(DC_CMD_SIGNAL_RAISE2);
> +       DUMP_REG(DC_CMD_SIGNAL_RAISE3);
> +       DUMP_REG(DC_CMD_STATE_ACCESS);
> +       DUMP_REG(DC_CMD_STATE_CONTROL);
> +       DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER);
> +       DUMP_REG(DC_CMD_REG_ACT_CONTROL);
> +
> +       DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS0);
> +       DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS1);
> +       DUMP_REG(DC_DISP_DISP_WIN_OPTIONS);
> +       DUMP_REG(DC_DISP_MEM_HIGH_PRIORITY);
> +       DUMP_REG(DC_DISP_MEM_HIGH_PRIORITY_TIMER);
> +       DUMP_REG(DC_DISP_DISP_TIMING_OPTIONS);
> +       DUMP_REG(DC_DISP_REF_TO_SYNC);
> +       DUMP_REG(DC_DISP_SYNC_WIDTH);
> +       DUMP_REG(DC_DISP_BACK_PORCH);
> +       DUMP_REG(DC_DISP_DISP_ACTIVE);
> +       DUMP_REG(DC_DISP_FRONT_PORCH);
> +       DUMP_REG(DC_DISP_H_PULSE0_CONTROL);
> +       DUMP_REG(DC_DISP_H_PULSE0_POSITION_A);
> +       DUMP_REG(DC_DISP_H_PULSE0_POSITION_B);
> +       DUMP_REG(DC_DISP_H_PULSE0_POSITION_C);
> +       DUMP_REG(DC_DISP_H_PULSE0_POSITION_D);
> +       DUMP_REG(DC_DISP_H_PULSE1_CONTROL);
> +       DUMP_REG(DC_DISP_H_PULSE1_POSITION_A);
> +       DUMP_REG(DC_DISP_H_PULSE1_POSITION_B);
> +       DUMP_REG(DC_DISP_H_PULSE1_POSITION_C);
> +       DUMP_REG(DC_DISP_H_PULSE1_POSITION_D);
> +       DUMP_REG(DC_DISP_H_PULSE2_CONTROL);
> +       DUMP_REG(DC_DISP_H_PULSE2_POSITION_A);
> +       DUMP_REG(DC_DISP_H_PULSE2_POSITION_B);
> +       DUMP_REG(DC_DISP_H_PULSE2_POSITION_C);
> +       DUMP_REG(DC_DISP_H_PULSE2_POSITION_D);
> +       DUMP_REG(DC_DISP_V_PULSE0_CONTROL);
> +       DUMP_REG(DC_DISP_V_PULSE0_POSITION_A);
> +       DUMP_REG(DC_DISP_V_PULSE0_POSITION_B);
> +       DUMP_REG(DC_DISP_V_PULSE0_POSITION_C);
> +       DUMP_REG(DC_DISP_V_PULSE1_CONTROL);
> +       DUMP_REG(DC_DISP_V_PULSE1_POSITION_A);
> +       DUMP_REG(DC_DISP_V_PULSE1_POSITION_B);
> +       DUMP_REG(DC_DISP_V_PULSE1_POSITION_C);
> +       DUMP_REG(DC_DISP_V_PULSE2_CONTROL);
> +       DUMP_REG(DC_DISP_V_PULSE2_POSITION_A);
> +       DUMP_REG(DC_DISP_V_PULSE3_CONTROL);
> +       DUMP_REG(DC_DISP_V_PULSE3_POSITION_A);
> +       DUMP_REG(DC_DISP_M0_CONTROL);
> +       DUMP_REG(DC_DISP_M1_CONTROL);
> +       DUMP_REG(DC_DISP_DI_CONTROL);
> +       DUMP_REG(DC_DISP_PP_CONTROL);
> +       DUMP_REG(DC_DISP_PP_SELECT_A);
> +       DUMP_REG(DC_DISP_PP_SELECT_B);
> +       DUMP_REG(DC_DISP_PP_SELECT_C);
> +       DUMP_REG(DC_DISP_PP_SELECT_D);
> +       DUMP_REG(DC_DISP_DISP_CLOCK_CONTROL);
> +       DUMP_REG(DC_DISP_DISP_INTERFACE_CONTROL);
> +       DUMP_REG(DC_DISP_DISP_COLOR_CONTROL);
> +       DUMP_REG(DC_DISP_SHIFT_CLOCK_OPTIONS);
> +       DUMP_REG(DC_DISP_DATA_ENABLE_OPTIONS);
> +       DUMP_REG(DC_DISP_SERIAL_INTERFACE_OPTIONS);
> +       DUMP_REG(DC_DISP_LCD_SPI_OPTIONS);
> +       DUMP_REG(DC_DISP_BORDER_COLOR);
> +       DUMP_REG(DC_DISP_COLOR_KEY0_LOWER);
> +       DUMP_REG(DC_DISP_COLOR_KEY0_UPPER);
> +       DUMP_REG(DC_DISP_COLOR_KEY1_LOWER);
> +       DUMP_REG(DC_DISP_COLOR_KEY1_UPPER);
> +       DUMP_REG(DC_DISP_CURSOR_FOREGROUND);
> +       DUMP_REG(DC_DISP_CURSOR_BACKGROUND);
> +       DUMP_REG(DC_DISP_CURSOR_START_ADDR);
> +       DUMP_REG(DC_DISP_CURSOR_START_ADDR_NS);
> +       DUMP_REG(DC_DISP_CURSOR_POSITION);
> +       DUMP_REG(DC_DISP_CURSOR_POSITION_NS);
> +       DUMP_REG(DC_DISP_INIT_SEQ_CONTROL);
> +       DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_A);
> +       DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_B);
> +       DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_C);
> +       DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_D);
> +       DUMP_REG(DC_DISP_DC_MCCIF_FIFOCTRL);
> +       DUMP_REG(DC_DISP_MCCIF_DISPLAY0A_HYST);
> +       DUMP_REG(DC_DISP_MCCIF_DISPLAY0B_HYST);
> +       DUMP_REG(DC_DISP_MCCIF_DISPLAY0C_HYST);
> +       DUMP_REG(DC_DISP_MCCIF_DISPLAY1B_HYST);
> +       DUMP_REG(DC_DISP_DAC_CRT_CTRL);
> +       DUMP_REG(DC_DISP_DISP_MISC_CONTROL);
> +
> +
> +       for (i = 0; i < 3; i++) {
> +               print(data, "\n");
> +               snprintf(buff, sizeof(buff), "WINDOW %c:\n", 'A' + i);
> +               print(data, buff);
> +
> +               tegra_dc_writel(dc, WINDOW_A_SELECT << i,
> +                               DC_CMD_DISPLAY_WINDOW_HEADER);
> +               DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER);
> +               DUMP_REG(DC_WIN_WIN_OPTIONS);
> +               DUMP_REG(DC_WIN_BYTE_SWAP);
> +               DUMP_REG(DC_WIN_BUFFER_CONTROL);
> +               DUMP_REG(DC_WIN_COLOR_DEPTH);
> +               DUMP_REG(DC_WIN_POSITION);
> +               DUMP_REG(DC_WIN_SIZE);
> +               DUMP_REG(DC_WIN_PRESCALED_SIZE);
> +               DUMP_REG(DC_WIN_H_INITIAL_DDA);
> +               DUMP_REG(DC_WIN_V_INITIAL_DDA);
> +               DUMP_REG(DC_WIN_DDA_INCREMENT);
> +               DUMP_REG(DC_WIN_LINE_STRIDE);
> +               DUMP_REG(DC_WIN_BUF_STRIDE);
> +               DUMP_REG(DC_WIN_BLEND_NOKEY);
> +               DUMP_REG(DC_WIN_BLEND_1WIN);
> +               DUMP_REG(DC_WIN_BLEND_2WIN_X);
> +               DUMP_REG(DC_WIN_BLEND_2WIN_Y);
> +               DUMP_REG(DC_WIN_BLEND_3WIN_XY);
> +               DUMP_REG(DC_WINBUF_START_ADDR);
> +               DUMP_REG(DC_WINBUF_ADDR_H_OFFSET);
> +               DUMP_REG(DC_WINBUF_ADDR_V_OFFSET);
> +       }
> +}
> +
> +#undef DUMP_REG
> +
> +#ifdef DEBUG
> +static void dump_regs_print(void *data, const char *str)
> +{
> +       struct tegra_dc *dc = data;
> +       dev_dbg(&dc->pdev->dev, "%s", str);
> +}
> +
> +static void dump_regs(struct tegra_dc *dc)
> +{
> +       _dump_regs(dc, dc, dump_regs_print);
> +}
> +#else
> +
> +static void dump_regs(struct tegra_dc *dc) {}
> +
> +#endif
> +
> +#ifdef CONFIG_DEBUG_FS
> +
> +static void dbg_regs_print(void *data, const char *str)
> +{
> +       struct seq_file *s = data;
> +
> +       seq_printf(s, "%s", str);
> +}
> +
> +#undef DUMP_REG
> +
> +static int dbg_dc_show(struct seq_file *s, void *unused)
> +{
> +       struct tegra_dc *dc = s->private;
> +
> +       _dump_regs(dc, s, dbg_regs_print);
> +
> +       return 0;
> +}
> +
> +
> +static int dbg_dc_open(struct inode *inode, struct file *file)
> +{
> +       return single_open(file, dbg_dc_show, inode->i_private);
> +}
> +
> +static const struct file_operations dbg_fops = {
> +       .open           = dbg_dc_open,
> +       .read           = seq_read,
> +       .llseek         = seq_lseek,
> +       .release        = single_release,
> +};
> +
> +static void tegra_dc_dbg_add(struct tegra_dc *dc)
> +{
> +       char name[32];
> +
> +       snprintf(name, sizeof(name), "tegra_dc%d_regs", dc->pdev->id);
> +       (void) debugfs_create_file(name, S_IRUGO, NULL, dc, &dbg_fops);
> +}
> +#else
> +static void tegra_dc_dbg_add(struct tegra_dc *dc) {}
> +
> +#endif
> +
> +
> +static int tegra_dc_add(struct tegra_dc *dc, int index)
> +{
> +       int ret = 0;
> +
> +       mutex_lock(&tegra_dc_lock);
> +       if (index >= TEGRA_MAX_DC) {
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       if (tegra_dcs[index] != NULL) {
> +               ret = -EBUSY;
> +               goto out;
> +       }
> +
> +       tegra_dcs[index] = dc;
> +
> +out:
> +       mutex_unlock(&tegra_dc_lock);
> +
> +       return ret;
> +}
> +
> +struct tegra_dc *tegra_dc_get_dc(unsigned idx)
> +{
> +       if (idx < TEGRA_MAX_DC)
> +               return tegra_dcs[idx];
> +       else
> +               return NULL;
> +}
> +EXPORT_SYMBOL(tegra_dc_get_dc);
> +
> +struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win)
> +{
> +       if (win >= dc->n_windows)
> +               return NULL;
> +
> +       return &dc->windows[win];
> +}
> +EXPORT_SYMBOL(tegra_dc_get_window);
> +
> +/* does not support updating windows on multiple dcs in one call */
> +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);
> +       for (i = 0; i < n; i++) {
> +               struct tegra_dc_win *win = windows[i];
> +               unsigned h_dda;
> +               unsigned v_dda;
> +               unsigned stride;
> +
> +               tegra_dc_writel(dc, WINDOW_A_SELECT << win->idx,
> +                               DC_CMD_DISPLAY_WINDOW_HEADER);
> +
> +               update_mask |= WIN_A_ACT_REQ << win->idx;
> +
> +               if (!(win->flags & TEGRA_WIN_FLAG_ENABLED)) {
> +                       tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS);
> +                       continue;
> +               }
> +
> +               tegra_dc_writel(dc, win->fmt, DC_WIN_COLOR_DEPTH);
> +               tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
> +
> +               stride = win->w * tegra_dc_fmt_bpp(win->fmt) / 8;
> +
> +               /* TODO: implement filter on settings */
> +               h_dda = (win->w * 0x1000) / (win->out_w - 1);
> +               v_dda = (win->h * 0x1000) / (win->out_h - 1);
> +
> +               tegra_dc_writel(dc,
> +                               V_POSITION(win->y) | H_POSITION(win->x),
> +                               DC_WIN_POSITION);
> +               tegra_dc_writel(dc,
> +                               V_SIZE(win->out_h) | H_SIZE(win->out_w),
> +                               DC_WIN_SIZE);
> +               tegra_dc_writel(dc,
> +                               V_PRESCALED_SIZE(win->out_h) |
> +                               H_PRESCALED_SIZE(stride),
> +                               DC_WIN_PRESCALED_SIZE);
> +               tegra_dc_writel(dc, 0, DC_WIN_H_INITIAL_DDA);
> +               tegra_dc_writel(dc, 0, DC_WIN_V_INITIAL_DDA);
> +               tegra_dc_writel(dc, V_DDA_INC(v_dda) | H_DDA_INC(h_dda),
> +                               DC_WIN_DDA_INCREMENT);
> +               tegra_dc_writel(dc, stride, DC_WIN_LINE_STRIDE);
> +               tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
> +
> +               val = WIN_ENABLE;
> +               if (win->flags & TEGRA_WIN_FLAG_COLOR_EXPAND)
> +                       val |= COLOR_EXPAND;
> +               tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS);
> +
> +               tegra_dc_writel(dc, (unsigned long)win->phys_addr,
> +                               DC_WINBUF_START_ADDR);
> +               tegra_dc_writel(dc, 0, DC_WINBUF_ADDR_H_OFFSET);
> +               tegra_dc_writel(dc, 0, DC_WINBUF_ADDR_V_OFFSET);
> +
> +               win->dirty = 1;
> +
> +       }
> +
> +       tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL);
> +
> +       val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
> +       val |= FRAME_END_INT;
> +       tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
> +
> +       val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
> +       val |= FRAME_END_INT;
> +       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);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(tegra_dc_update_windows);
> +
> +static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[],
> +                                            int n)
> +{
> +       int i;
> +
> +       for (i = 0; i < n; i++) {
> +               if (windows[i]->dirty)
> +                       return false;
> +       }
> +
> +       return true;
> +}
> +
> +/* does not support syncing windows on multiple dcs in one call */
> +int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
> +{
> +       if (n < 1 || n > DC_N_WINDOWS)
> +               return -EINVAL;
> +
> +       return wait_event_interruptible_timeout(windows[0]->dc->wq,
> +                                        tegra_dc_windows_are_clean(windows, n),
> +                                        HZ);
> +}
> +EXPORT_SYMBOL(tegra_dc_sync_windows);
> +
> +void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend)
> +{
> +       int i;
> +
> +       for (i = 0; i < DC_N_WINDOWS; i++) {
> +               tegra_dc_writel(dc, WINDOW_A_SELECT << i,
> +                               DC_CMD_DISPLAY_WINDOW_HEADER);
> +               tegra_dc_writel(dc, blend[i].nokey, DC_WIN_BLEND_NOKEY);
> +               tegra_dc_writel(dc, blend[i].one_win, DC_WIN_BLEND_1WIN);
> +               tegra_dc_writel(dc, blend[i].two_win_x, DC_WIN_BLEND_2WIN_X);
> +               tegra_dc_writel(dc, blend[i].two_win_y, DC_WIN_BLEND_2WIN_Y);
> +               tegra_dc_writel(dc, blend[i].three_win_xy,
> +                               DC_WIN_BLEND_3WIN_XY);
> +       }
> +}
> +EXPORT_SYMBOL(tegra_dc_set_blending);
> +
> +int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
> +{
> +       unsigned long val;
> +       unsigned long rate;
> +       unsigned long div;
> +
> +       tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
> +       tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16),
> +                       DC_DISP_REF_TO_SYNC);
> +       tegra_dc_writel(dc, mode->h_sync_width | (mode->v_sync_width << 16),
> +                       DC_DISP_SYNC_WIDTH);
> +       tegra_dc_writel(dc, mode->h_back_porch | (mode->v_back_porch << 16),
> +                       DC_DISP_BACK_PORCH);
> +       tegra_dc_writel(dc, mode->h_active | (mode->v_active << 16),
> +                       DC_DISP_DISP_ACTIVE);
> +       tegra_dc_writel(dc, mode->h_front_porch | (mode->v_front_porch << 16),
> +                       DC_DISP_FRONT_PORCH);
> +
> +       tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL,
> +                       DC_DISP_DATA_ENABLE_OPTIONS);
> +
> +       /* TODO: MIPI/CRT/HDMI clock cals */
> +
> +       val = DISP_DATA_FORMAT_DF1P1C;
> +
> +       if (dc->out->align = TEGRA_DC_ALIGN_MSB)
> +               val |= DISP_DATA_ALIGNMENT_MSB;
> +       else
> +               val |= DISP_DATA_ALIGNMENT_LSB;
> +
> +       if (dc->out->order = TEGRA_DC_ORDER_RED_BLUE)
> +               val |= DISP_DATA_ORDER_RED_BLUE;
> +       else
> +               val |= DISP_DATA_ORDER_BLUE_RED;
> +
> +       tegra_dc_writel(dc, val, DC_DISP_DISP_INTERFACE_CONTROL);
> +
> +       rate = clk_get_rate(dc->clk);
> +
> +       div = ((rate * 2 + mode->pclk / 2) / mode->pclk) - 2;
> +
> +       if (rate * 2 / (div + 2) < (mode->pclk / 100 * 99) ||
> +           rate * 2 / (div + 2) > (mode->pclk / 100 * 109)) {
> +               dev_err(&dc->pdev->dev,
> +                       "can't divide %ld clock to %d -1/+9%% %ld %d %d\n",
> +                       rate, mode->pclk,
> +                       rate / div, (mode->pclk / 100 * 99),
> +                       (mode->pclk / 100 * 109));
> +               return -EINVAL;
> +       }
> +
> +       tegra_dc_writel(dc, 0x00010001,
> +                       DC_DISP_SHIFT_CLOCK_OPTIONS);
> +       tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div),
> +                       DC_DISP_DISP_CLOCK_CONTROL);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(tegra_dc_set_mode);
> +
> +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->pdev->dev,
> +                       "No default modes specified.  Leaving output disabled.\n");
> +
> +       switch (out->type) {
> +       case TEGRA_DC_OUT_RGB:
> +               dc->out_ops = &tegra_dc_rgb_ops;
> +               break;
> +
> +       default:
> +               dc->out_ops = NULL;
> +               break;
> +       }
> +}
> +
> +
> +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);
> +
> +       if (status & FRAME_END_INT) {
> +               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))) {
> +                               dc->windows[i].dirty = 0;
> +                               completed = 1;
> +                       } else {
> +                               dirty = 1;
> +                       }
> +               }
> +
> +               if (!dirty) {
> +                       val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
> +                       val &= ~FRAME_END_INT;
> +                       tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
> +               }
> +
> +               spin_unlock_irqrestore(&dc->lock, flags);
> +
> +               if (completed)
> +                       wake_up(&dc->wq);
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static void tegra_dc_init(struct tegra_dc *dc)
> +{
> +       tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
> +       if (dc->pdev->id = 0)
> +               tegra_dc_writel(dc, 0x0000011a, DC_CMD_CONT_SYNCPT_VSYNC);
> +       else if (dc->pdev->id = 1)
> +               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);
> +       tegra_dc_writel(dc, 0x00000020, DC_DISP_MEM_HIGH_PRIORITY);
> +       tegra_dc_writel(dc, 0x00000001, DC_DISP_MEM_HIGH_PRIORITY_TIMER);
> +
> +       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->out_ops && dc->out_ops->init)
> +               dc->out_ops->init(dc);
> +}
> +
> +static int tegra_dc_probe(struct platform_device *pdev)
> +{
> +       struct tegra_dc *dc;
> +       struct clk *clk;
> +       struct clk *host1x_clk;
> +       struct resource *res;
> +       struct resource *base_res;
> +       struct resource *fb_mem = NULL;
> +       int ret = 0;
> +       void __iomem *base;
> +       int irq;
> +       int i;
> +
> +       if (!pdev->dev.platform_data) {
> +               dev_err(&pdev->dev, "no platform data\n");
> +               return -ENOENT;
> +       }
> +
> +       dc = kzalloc(sizeof(struct tegra_dc), GFP_KERNEL);
> +       if (!dc) {
> +               dev_err(&pdev->dev, "can't allocate memory for tegra_dc\n");
> +               return -ENOMEM;
> +       }
> +
> +       irq = platform_get_irq_byname(pdev, "irq");
> +       if (irq <= 0) {
> +               dev_err(&pdev->dev, "no irq\n");
> +               ret = -ENOENT;
> +               goto err_free;
> +       }
> +
> +       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> +       if (!res) {
> +               dev_err(&pdev->dev, "no mem resource\n");
> +               ret = -ENOENT;
> +               goto err_free;
> +       }
> +
> +       base_res = request_mem_region(res->start, resource_size(res), pdev->name);
> +       if (!base_res) {
> +               dev_err(&pdev->dev, "request_mem_region failed\n");
> +               ret = -EBUSY;
> +               goto err_free;
> +       }
> +
> +       base = ioremap(res->start, resource_size(res));
> +       if (!base) {
> +               dev_err(&pdev->dev, "registers can't be mapped\n");
> +               ret = -EBUSY;
> +               goto err_release_resource_reg;
> +       }
> +
> +       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fbmem");
> +       if (res)
> +               fb_mem = request_mem_region(res->start, resource_size(res), pdev->name);
> +
> +       host1x_clk = clk_get(&pdev->dev, "host1x");
> +       if (IS_ERR_OR_NULL(host1x_clk)) {
> +               dev_err(&pdev->dev, "can't get host1x clock\n");
> +               ret = -ENOENT;
> +               goto err_iounmap_reg;
> +       }
> +       clk_enable(host1x_clk);
> +
> +       clk = clk_get(&pdev->dev, NULL);
> +       if (IS_ERR_OR_NULL(clk)) {
> +               dev_err(&pdev->dev, "can't get clock\n");
> +               ret = -ENOENT;
> +
> +               goto err_put_host1x_clk;
> +       }
> +       clk_enable(clk);
> +       tegra_periph_reset_deassert(clk);
> +
> +       dc->clk = clk;
> +       dc->host1x_clk = host1x_clk;
> +       dc->base_res = base_res;
> +       dc->base = base;
> +       dc->irq = irq;
> +       dc->pdev = pdev;
> +       dc->pdata = pdev->dev.platform_data;
> +       spin_lock_init(&dc->lock);
> +       init_waitqueue_head(&dc->wq);
> +
> +
> +       dc->n_windows = DC_N_WINDOWS;
> +       for (i = 0; i < dc->n_windows; i++) {
> +               dc->windows[i].idx = i;
> +               dc->windows[i].dc = dc;
> +       }
> +
> +       if (request_irq(irq, tegra_dc_irq, IRQF_DISABLED,
> +                       dev_name(&pdev->dev), dc)) {
> +               dev_err(&pdev->dev, "request_irq %d failed\n", irq);
> +               ret = -EBUSY;
> +               goto err_put_clk;
> +       }
> +
> +       ret = tegra_dc_add(dc, pdev->id);
> +       if (ret < 0) {
> +               dev_err(&pdev->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(&pdev->dev, "No default output specified.  Leaving output disabled.\n");
> +       }
> +
> +       tegra_dc_init(dc);
> +
> +       tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
> +
> +       platform_set_drvdata(pdev, dc);
> +
> +       tegra_dc_dbg_add(dc);
> +
> +       dev_info(&pdev->dev, "probed\n");
> +
> +       if (fb_mem && dc->pdata->fb) {
> +               dc->fb = tegra_fb_register(pdev, dc, dc->pdata->fb, fb_mem);
> +               if (IS_ERR_OR_NULL(dc->fb))
> +                       dc->fb = NULL;
> +       }
> +
> +       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);
> +       if (fb_mem)
> +               release_resource(fb_mem);
> +err_release_resource_reg:
> +       release_resource(base_res);
> +err_free:
> +       kfree(dc);
> +
> +       return ret;
> +}
> +
> +static int tegra_dc_remove(struct platform_device *pdev)
> +{
> +       struct tegra_dc *dc = platform_get_drvdata(pdev);
> +
> +       if (dc->fb) {
> +               tegra_fb_unregister(dc->fb);
> +               release_resource(dc->fb_mem);
> +       }
> +
> +       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);
> +       release_resource(dc->base_res);
> +       kfree(dc);
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int tegra_dc_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +       struct tegra_dc *dc = platform_get_drvdata(pdev);
> +
> +       dev_info(&pdev->dev, "suspend\n");
> +
> +       disable_irq(dc->irq);
> +       tegra_periph_reset_assert(dc->clk);
> +       clk_disable(dc->clk);
> +
> +       return 0;
> +}
> +
> +static int tegra_dc_resume(struct platform_device *pdev)
> +{
> +       struct tegra_dc *dc = platform_get_drvdata(pdev);
> +       struct tegra_dc_win *wins[DC_N_WINDOWS];
> +       int i;
> +
> +       dev_info(&pdev->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];
> +
> +       tegra_dc_init(dc);
> +
> +       tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
> +       tegra_dc_update_windows(wins, dc->n_windows);
> +
> +       return 0;
> +}
> +
> +#endif
> +
> +extern int suspend_set(const char *val, struct kernel_param *kp)
> +{
> +       if (!strcmp(val, "dump"))
> +               dump_regs(tegra_dcs[0]);
> +#ifdef CONFIG_PM
> +       else if (!strcmp(val, "suspend"))
> +               tegra_dc_suspend(tegra_dcs[0]->pdev, PMSG_SUSPEND);
> +       else if (!strcmp(val, "resume"))
> +               tegra_dc_resume(tegra_dcs[0]->pdev);
> +#endif
> +
> +       return 0;
> +}
> +
> +extern int suspend_get(char *buffer, struct kernel_param *kp)
> +{
> +       return 0;
> +}
> +
> +int suspend;
> +
> +module_param_call(suspend, suspend_set, suspend_get, &suspend, 0644);
> +
> +struct platform_driver tegra_dc_driver = {
> +       .driver = {
> +               .name = "tegradc",
> +               .owner = THIS_MODULE,
> +       },
> +       .probe = tegra_dc_probe,
> +       .remove = tegra_dc_remove,
> +#ifdef CONFIG_PM
> +       .suspend = tegra_dc_suspend,
> +       .resume = tegra_dc_resume,
> +#endif
> +};
> +
> +static int __init tegra_dc_module_init(void)
> +{
> +       return platform_driver_register(&tegra_dc_driver);
> +}
> +
> +static void __exit tegra_dc_module_exit(void)
> +{
> +       platform_driver_unregister(&tegra_dc_driver);
> +}
> +
> +module_exit(tegra_dc_module_exit);
> +module_init(tegra_dc_module_init);
> diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
> new file mode 100644
> index 0000000..b2351b1
> --- /dev/null
> +++ b/drivers/video/tegra/dc/dc_priv.h
> @@ -0,0 +1,86 @@
> +/*
> + * drivers/video/tegra/dc/dc_priv.h
> + *
> + * Copyright (C) 2010 Google, Inc.
> + * Author: Erik Gilling <konkers@android.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#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>
> +
> +struct tegra_dc;
> +
> +struct tegra_dc_out_ops {
> +       void (*init)(struct tegra_dc *dc);
> +};
> +
> +struct tegra_dc {
> +       struct list_head                list;
> +
> +       struct platform_device          *pdev;
> +       struct tegra_dc_platform_data   *pdata;
> +
> +       struct resource                 *base_res;
> +       void __iomem                    *base;
> +       int                             irq;
> +
> +       struct clk                      *clk;
> +       struct clk                      *host1x_clk;
> +
> +       struct tegra_dc_out             *out;
> +       struct tegra_dc_out_ops         *out_ops;
> +
> +       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 resource                 *fb_mem;
> +       struct tegra_fb_info            *fb;
> +};
> +
> +static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
> +                                          unsigned long reg)
> +{
> +       return readl(dc->base + reg * 4);
> +}
> +
> +static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long val,
> +                                  unsigned long reg)
> +{
> +       writel(val, dc->base + reg * 4);
> +}
> +
> +static inline void _tegra_dc_write_table(struct tegra_dc *dc, const u32 *table,
> +                                        unsigned len)
> +{
> +       int i;
> +
> +       for (i = 0; i < len; i++)
> +               tegra_dc_writel(dc, table[i * 2 + 1], table[i * 2]);
> +}
> +
> +#define tegra_dc_write_table(dc, table)                \
> +       _tegra_dc_write_table(dc, table, ARRAY_SIZE(table) / 2)
> +
> +extern struct tegra_dc_out_ops tegra_dc_rgb_ops;
> +
> +#endif
> diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h
> new file mode 100644
> index 0000000..6d6b3ba
> --- /dev/null
> +++ b/drivers/video/tegra/dc/dc_reg.h
> @@ -0,0 +1,342 @@
> +/*
> + * drivers/video/tegra/dc/dc_reg.h
> + *
> + * Copyright (C) 2010 Google, Inc.
> + * Author: Erik Gilling <konkers@android.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_REG_H
> +#define __DRIVERS_VIDEO_TEGRA_DC_DC_REG_H
> +
> +#define DC_CMD_GENERAL_INCR_SYNCPT             0x000
> +#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL       0x001
> +#define DC_CMD_GENERAL_INCR_SYNCPT_ERROR       0x002
> +#define DC_CMD_WIN_A_INCR_SYNCPT               0x008
> +#define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL         0x009
> +#define DC_CMD_WIN_A_INCR_SYNCPT_ERROR         0x00a
> +#define DC_CMD_WIN_B_INCR_SYNCPT               0x010
> +#define DC_CMD_WIN_B_INCR_SYNCPT_CNTRL         0x011
> +#define DC_CMD_WIN_B_INCR_SYNCPT_ERROR         0x012
> +#define DC_CMD_WIN_C_INCR_SYNCPT               0x018
> +#define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL         0x019
> +#define DC_CMD_WIN_C_INCR_SYNCPT_ERROR         0x01a
> +#define DC_CMD_CONT_SYNCPT_VSYNC               0x028
> +#define DC_CMD_DISPLAY_COMMAND_OPTION0         0x031
> +#define DC_CMD_DISPLAY_COMMAND                 0x032
> +#define  DISP_COMMAND_RAISE            (1 << 0)
> +#define  DISP_CTRL_MODE_STOP           (0 << 5)
> +#define  DISP_CTRL_MODE_C_DISPLAY      (1 << 5)
> +#define  DISP_CTRL_MODE_NC_DISPLAY     (2 << 5)
> +#define  DISP_COMMAND_RAISE_VECTOR(x)  (((x) & 0x1f) << 22)
> +#define  DISP_COMMAND_RAISE_CHANNEL_ID(x)      (((x) & 0xf) << 27)
> +
> +#define DC_CMD_SIGNAL_RAISE                    0x033
> +#define DC_CMD_DISPLAY_POWER_CONTROL           0x036
> +#define  PW0_ENABLE            (1 << 0)
> +#define  PW1_ENABLE            (1 << 2)
> +#define  PW2_ENABLE            (1 << 4)
> +#define  PW3_ENABLE            (1 << 6)
> +#define  PW4_ENABLE            (1 << 8)
> +#define  PM0_ENABLE            (1 << 16)
> +#define  PM1_ENABLE            (1 << 18)
> +#define  SPI_ENABLE            (1 << 24)
> +#define  HSPI_ENABLE           (1 << 25)
> +
> +#define DC_CMD_INT_STATUS                      0x037
> +#define DC_CMD_INT_MASK                                0x038
> +#define DC_CMD_INT_ENABLE                      0x039
> +#define DC_CMD_INT_TYPE                                0x03a
> +#define DC_CMD_INT_POLARITY                    0x03b
> +#define  CTXSW_INT             (1 << 0)
> +#define  FRAME_END_INT         (1 << 1)
> +#define  V_BLANK_INT           (1 << 2)
> +#define  H_BLANK_INT           (1 << 3)
> +#define  V_PULSE3_INT          (1 << 4)
> +#define  SPI_BUSY_INT          (1 << 7)
> +#define  WIN_A_UF_INT          (1 << 8)
> +#define  WIN_B_UF_INT          (1 << 9)
> +#define  WIN_C_UF_INT          (1 << 10)
> +#define  MSF_INT               (1 << 12)
> +#define  SSF_INT               (1 << 13)
> +#define  WIN_A_OF_INT          (1 << 14)
> +#define  WIN_B_OF_INT          (1 << 15)
> +#define  WIN_C_OF_INT          (1 << 16)
> +#define  GPIO_0_INT            (1 << 18)
> +#define  GPIO_1_INT            (1 << 19)
> +#define  GPIO_2_INT            (1 << 20)
> +
> +#define DC_CMD_SIGNAL_RAISE1                   0x03c
> +#define DC_CMD_SIGNAL_RAISE2                   0x03d
> +#define DC_CMD_SIGNAL_RAISE3                   0x03e
> +#define DC_CMD_STATE_ACCESS                    0x040
> +#define DC_CMD_STATE_CONTROL                   0x041
> +#define  GENERAL_ACT_REQ       (1 << 0)
> +#define  WIN_A_ACT_REQ         (1 << 1)
> +#define  WIN_B_ACT_REQ         (1 << 2)
> +#define  WIN_C_ACT_REQ         (1 << 3)
> +
> +#define DC_CMD_DISPLAY_WINDOW_HEADER           0x042
> +#define  WINDOW_A_SELECT               (1 << 4)
> +#define  WINDOW_B_SELECT               (1 << 5)
> +#define  WINDOW_C_SELECT               (1 << 6)
> +
> +#define DC_CMD_REG_ACT_CONTROL                 0x043
> +
> +#define DC_COM_CRC_CONTROL                     0x300
> +#define DC_COM_CRC_CHECKSUM                    0x301
> +#define DC_COM_PIN_OUTPUT_ENABLE0              0x302
> +#define DC_COM_PIN_OUTPUT_ENABLE1              0x303
> +#define DC_COM_PIN_OUTPUT_ENABLE2              0x304
> +#define DC_COM_PIN_OUTPUT_ENABLE3              0x305
> +#define DC_COM_PIN_OUTPUT_POLARITY0            0x306
> +#define DC_COM_PIN_OUTPUT_POLARITY1            0x307
> +#define DC_COM_PIN_OUTPUT_POLARITY2            0x308
> +#define DC_COM_PIN_OUTPUT_POLARITY3            0x309
> +#define DC_COM_PIN_OUTPUT_DATA0                        0x30a
> +#define DC_COM_PIN_OUTPUT_DATA1                        0x30b
> +#define DC_COM_PIN_OUTPUT_DATA2                        0x30c
> +#define DC_COM_PIN_OUTPUT_DATA3                        0x30d
> +#define DC_COM_PIN_INPUT_ENABLE0               0x30e
> +#define DC_COM_PIN_INPUT_ENABLE1               0x30f
> +#define DC_COM_PIN_INPUT_ENABLE2               0x310
> +#define DC_COM_PIN_INPUT_ENABLE3               0x311
> +#define DC_COM_PIN_INPUT_DATA0                 0x312
> +#define DC_COM_PIN_INPUT_DATA1                 0x313
> +#define DC_COM_PIN_OUTPUT_SELECT0              0x314
> +#define DC_COM_PIN_OUTPUT_SELECT1              0x315
> +#define DC_COM_PIN_OUTPUT_SELECT2              0x316
> +#define DC_COM_PIN_OUTPUT_SELECT3              0x317
> +#define DC_COM_PIN_OUTPUT_SELECT4              0x318
> +#define DC_COM_PIN_OUTPUT_SELECT5              0x319
> +#define DC_COM_PIN_OUTPUT_SELECT6              0x31a
> +#define DC_COM_PIN_MISC_CONTROL                        0x31b
> +#define DC_COM_PM0_CONTROL                     0x31c
> +#define DC_COM_PM0_DUTY_CYCLE                  0x31d
> +#define DC_COM_PM1_CONTROL                     0x31e
> +#define DC_COM_PM1_DUTY_CYCLE                  0x31f
> +#define DC_COM_SPI_CONTROL                     0x320
> +#define DC_COM_SPI_START_BYTE                  0x321
> +#define DC_COM_HSPI_WRITE_DATA_AB              0x322
> +#define DC_COM_HSPI_WRITE_DATA_CD              0x323
> +#define DC_COM_HSPI_CS_DC                      0x324
> +#define DC_COM_SCRATCH_REGISTER_A              0x325
> +#define DC_COM_SCRATCH_REGISTER_B              0x326
> +#define DC_COM_GPIO_CTRL                       0x327
> +#define DC_COM_GPIO_DEBOUNCE_COUNTER           0x328
> +#define DC_COM_CRC_CHECKSUM_LATCHED            0x329
> +
> +#define DC_DISP_DISP_SIGNAL_OPTIONS0           0x400
> +#define DC_DISP_DISP_SIGNAL_OPTIONS1           0x401
> +#define DC_DISP_DISP_WIN_OPTIONS               0x402
> +#define DC_DISP_MEM_HIGH_PRIORITY              0x403
> +#define DC_DISP_MEM_HIGH_PRIORITY_TIMER                0x404
> +#define DC_DISP_DISP_TIMING_OPTIONS            0x405
> +#define DC_DISP_REF_TO_SYNC                    0x406
> +#define DC_DISP_SYNC_WIDTH                     0x407
> +#define DC_DISP_BACK_PORCH                     0x408
> +#define DC_DISP_DISP_ACTIVE                    0x409
> +#define DC_DISP_FRONT_PORCH                    0x40a
> +#define DC_DISP_H_PULSE0_CONTROL               0x40b
> +#define DC_DISP_H_PULSE0_POSITION_A            0x40c
> +#define DC_DISP_H_PULSE0_POSITION_B            0x40d
> +#define DC_DISP_H_PULSE0_POSITION_C            0x40e
> +#define DC_DISP_H_PULSE0_POSITION_D            0x40f
> +#define DC_DISP_H_PULSE1_CONTROL               0x410
> +#define DC_DISP_H_PULSE1_POSITION_A            0x411
> +#define DC_DISP_H_PULSE1_POSITION_B            0x412
> +#define DC_DISP_H_PULSE1_POSITION_C            0x413
> +#define DC_DISP_H_PULSE1_POSITION_D            0x414
> +#define DC_DISP_H_PULSE2_CONTROL               0x415
> +#define DC_DISP_H_PULSE2_POSITION_A            0x416
> +#define DC_DISP_H_PULSE2_POSITION_B            0x417
> +#define DC_DISP_H_PULSE2_POSITION_C            0x418
> +#define DC_DISP_H_PULSE2_POSITION_D            0x419
> +#define DC_DISP_V_PULSE0_CONTROL               0x41a
> +#define DC_DISP_V_PULSE0_POSITION_A            0x41b
> +#define DC_DISP_V_PULSE0_POSITION_B            0x41c
> +#define DC_DISP_V_PULSE0_POSITION_C            0x41d
> +#define DC_DISP_V_PULSE1_CONTROL               0x41e
> +#define DC_DISP_V_PULSE1_POSITION_A            0x41f
> +#define DC_DISP_V_PULSE1_POSITION_B            0x420
> +#define DC_DISP_V_PULSE1_POSITION_C            0x421
> +#define DC_DISP_V_PULSE2_CONTROL               0x422
> +#define DC_DISP_V_PULSE2_POSITION_A            0x423
> +#define DC_DISP_V_PULSE3_CONTROL               0x424
> +#define DC_DISP_V_PULSE3_POSITION_A            0x425
> +#define DC_DISP_M0_CONTROL                     0x426
> +#define DC_DISP_M1_CONTROL                     0x427
> +#define DC_DISP_DI_CONTROL                     0x428
> +#define DC_DISP_PP_CONTROL                     0x429
> +#define DC_DISP_PP_SELECT_A                    0x42a
> +#define DC_DISP_PP_SELECT_B                    0x42b
> +#define DC_DISP_PP_SELECT_C                    0x42c
> +#define DC_DISP_PP_SELECT_D                    0x42d
> +#define DC_DISP_DISP_CLOCK_CONTROL             0x42e
> +#define  PIXEL_CLK_DIVIDER_PCD1                (0 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD1H       (1 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD2                (2 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD3                (3 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD4                (4 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD6                (5 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD8                (6 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD9                (7 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD12       (8 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD16       (9 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD18       (10 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD24       (11 << 8)
> +#define  PIXEL_CLK_DIVIDER_PCD13       (12 << 8)
> +#define  SHIFT_CLK_DIVIDER(x)          ((x) & 0xff)
> +
> +#define DC_DISP_DISP_INTERFACE_CONTROL         0x42f
> +#define  DISP_DATA_FORMAT_DF1P1C       (0 << 0)
> +#define  DISP_DATA_FORMAT_DF1P2C24B    (1 << 0)
> +#define  DISP_DATA_FORMAT_DF1P2C18B    (2 << 0)
> +#define  DISP_DATA_FORMAT_DF1P2C16B    (3 << 0)
> +#define  DISP_DATA_FORMAT_DF2S         (5 << 0)
> +#define  DISP_DATA_FORMAT_DF3S         (6 << 0)
> +#define  DISP_DATA_FORMAT_DFSPI                (7 << 0)
> +#define  DISP_DATA_FORMAT_DF1P3C24B    (8 << 0)
> +#define  DISP_DATA_FORMAT_DF1P3C18B    (9 << 0)
> +#define  DISP_DATA_ALIGNMENT_MSB       (0 << 8)
> +#define  DISP_DATA_ALIGNMENT_LSB       (1 << 8)
> +#define  DISP_DATA_ORDER_RED_BLUE      (0 << 9)
> +#define  DISP_DATA_ORDER_BLUE_RED      (1 << 9)
> +
> +#define DC_DISP_DISP_COLOR_CONTROL             0x430
> +#define DC_DISP_SHIFT_CLOCK_OPTIONS            0x431
> +#define DC_DISP_DATA_ENABLE_OPTIONS            0x432
> +#define   DE_SELECT_ACTIVE_BLANK       0x0
> +#define   DE_SELECT_ACTIVE             0x1
> +#define   DE_SELECT_ACTIVE_IS          0x2
> +#define   DE_CONTROL_ONECLK            (0 << 2)
> +#define   DE_CONTROL_NORMAL            (1 << 2)
> +#define   DE_CONTROL_EARLY_EXT         (2 << 2)
> +#define   DE_CONTROL_EARLY             (3 << 2)
> +#define   DE_CONTROL_ACTIVE_BLANK      (4 << 2)
> +
> +#define DC_DISP_SERIAL_INTERFACE_OPTIONS       0x433
> +#define DC_DISP_LCD_SPI_OPTIONS                        0x434
> +#define DC_DISP_BORDER_COLOR                   0x435
> +#define DC_DISP_COLOR_KEY0_LOWER               0x436
> +#define DC_DISP_COLOR_KEY0_UPPER               0x437
> +#define DC_DISP_COLOR_KEY1_LOWER               0x438
> +#define DC_DISP_COLOR_KEY1_UPPER               0x439
> +#define DC_DISP_CURSOR_FOREGROUND              0x43c
> +#define DC_DISP_CURSOR_BACKGROUND              0x43d
> +#define DC_DISP_CURSOR_START_ADDR              0x43e
> +#define DC_DISP_CURSOR_START_ADDR_NS           0x43f
> +#define DC_DISP_CURSOR_POSITION                        0x440
> +#define DC_DISP_CURSOR_POSITION_NS             0x441
> +#define DC_DISP_INIT_SEQ_CONTROL               0x442
> +#define DC_DISP_SPI_INIT_SEQ_DATA_A            0x443
> +#define DC_DISP_SPI_INIT_SEQ_DATA_B            0x444
> +#define DC_DISP_SPI_INIT_SEQ_DATA_C            0x445
> +#define DC_DISP_SPI_INIT_SEQ_DATA_D            0x446
> +#define DC_DISP_DC_MCCIF_FIFOCTRL              0x480
> +#define DC_DISP_MCCIF_DISPLAY0A_HYST           0x481
> +#define DC_DISP_MCCIF_DISPLAY0B_HYST           0x482
> +#define DC_DISP_MCCIF_DISPLAY0C_HYST           0x483
> +#define DC_DISP_MCCIF_DISPLAY1B_HYST           0x484
> +#define DC_DISP_DAC_CRT_CTRL                   0x4c0
> +#define DC_DISP_DISP_MISC_CONTROL              0x4c1
> +
> +#define DC_WINC_COLOR_PALETTE(x)               (0x500 + (x))
> +
> +#define DC_WINC_PALETTE_COLOR_EXT              0x600
> +#define DC_WINC_H_FILTER_P(x)                  (0x601 + (x))
> +#define DC_WINC_CSC_YOF                                0x611
> +#define DC_WINC_CSC_KYRGB                      0x612
> +#define DC_WINC_CSC_KUR                                0x613
> +#define DC_WINC_CSC_KVR                                0x614
> +#define DC_WINC_CSC_KUG                                0x615
> +#define DC_WINC_CSC_KVG                                0x616
> +#define DC_WINC_CSC_KUB                                0x617
> +#define DC_WINC_CSC_KVB                                0x618
> +#define DC_WINC_V_FILTER_P(x)                  (0x619 + (x))
> +#define DC_WIN_WIN_OPTIONS                     0x700
> +#define  H_DIRECTION_INCREMENT         (0 << 0)
> +#define  H_DIRECTION_DECREMENTT                (1 << 0)
> +#define  V_DIRECTION_INCREMENT         (0 << 2)
> +#define  V_DIRECTION_DECREMENTT                (1 << 2)
> +#define  COLOR_EXPAND                  (1 << 6)
> +#define  CP_ENABLE                     (1 << 16)
> +#define  DV_ENABLE                     (1 << 20)
> +#define  WIN_ENABLE                    (1 << 30)
> +
> +#define DC_WIN_BYTE_SWAP                       0x701
> +#define  BYTE_SWAP_NOSWAP              0
> +#define  BYTE_SWAP_SWAP2               1
> +#define  BYTE_SWAP_SWAP4               2
> +#define  BYTE_SWAP_SWAP4HW             3
> +
> +#define DC_WIN_BUFFER_CONTROL                  0x702
> +#define  BUFFER_CONTROL_HOST           0
> +#define  BUFFER_CONTROL_VI             1
> +#define  BUFFER_CONTROL_EPP            2
> +#define  BUFFER_CONTROL_MPEGE          3
> +#define  BUFFER_CONTROL_SB2D           4
> +
> +#define DC_WIN_COLOR_DEPTH                     0x703
> +
> +#define DC_WIN_POSITION                                0x704
> +#define  H_POSITION(x)         (((x) & 0xfff) << 0)
> +#define  V_POSITION(x)         (((x) & 0xfff) << 16)
> +
> +#define DC_WIN_SIZE                            0x705
> +#define  H_SIZE(x)             (((x) & 0xfff) << 0)
> +#define  V_SIZE(x)             (((x) & 0xfff) << 16)
> +
> +#define DC_WIN_PRESCALED_SIZE                  0x706
> +#define  H_PRESCALED_SIZE(x)   (((x) & 0x3fff) << 0)
> +#define  V_PRESCALED_SIZE(x)   (((x) & 0xfff) << 16)
> +
> +#define DC_WIN_H_INITIAL_DDA                   0x707
> +#define DC_WIN_V_INITIAL_DDA                   0x708
> +#define DC_WIN_DDA_INCREMENT                   0x709
> +#define  H_DDA_INC(x)          (((x) & 0xffff) << 0)
> +#define  V_DDA_INC(x)          (((x) & 0xffff) << 16)
> +
> +#define DC_WIN_LINE_STRIDE                     0x70a
> +#define DC_WIN_BUF_STRIDE                      0x70b
> +#define DC_WIN_UV_BUF_STRIDE                   0x70c
> +#define DC_WIN_BUFFER_ADDR_MODE                        0x70d
> +#define DC_WIN_DV_CONTROL                      0x70e
> +#define DC_WIN_BLEND_NOKEY                     0x70f
> +#define DC_WIN_BLEND_1WIN                      0x710
> +#define DC_WIN_BLEND_2WIN_X                    0x711
> +#define DC_WIN_BLEND_2WIN_Y                    0x712
> +#define DC_WIN_BLEND_3WIN_XY                   0x713
> +#define  CKEY_NOKEY                    (0 << 0)
> +#define  CKEY_KEY0                     (1 << 0)
> +#define  CKEY_KEY1                     (2 << 0)
> +#define  CKEY_KEY01                    (3 << 0)
> +#define  BLEND_CONTROL_FIX             (0 << 2)
> +#define  BLEND_CONTROL_ALPHA           (1 << 2)
> +#define  BLEND_CONTROL_DEPENDANT       (2 << 2)
> +#define  BLEND_WEIGHT0(x)              (((x) & 0xff) << 8)
> +#define  BLEND_WEIGHT1(x)              (((x) & 0xff) << 16)
> +
> +#define DC_WIN_HP_FETCH_CONTROL                        0x714
> +#define DC_WINBUF_START_ADDR                   0x800
> +#define DC_WINBUF_START_ADDR_NS                        0x801
> +#define DC_WINBUF_START_ADDR_U                 0x802
> +#define DC_WINBUF_START_ADDR_U_NS              0x803
> +#define DC_WINBUF_START_ADDR_V                 0x804
> +#define DC_WINBUF_START_ADDR_V_NS              0x805
> +#define DC_WINBUF_ADDR_H_OFFSET                        0x806
> +#define DC_WINBUF_ADDR_H_OFFSET_NS             0x807
> +#define DC_WINBUF_ADDR_V_OFFSET                        0x808
> +#define DC_WINBUF_ADDR_V_OFFSET_NS             0x809
> +#define DC_WINBUF_UFLOW_STATUS                 0x80a
> +
> +#endif
> diff --git a/drivers/video/tegra/dc/rgb.c b/drivers/video/tegra/dc/rgb.c
> new file mode 100644
> index 0000000..de1a8fa
> --- /dev/null
> +++ b/drivers/video/tegra/dc/rgb.c
> @@ -0,0 +1,63 @@
> +/*
> + * drivers/video/tegra/dc/rgb.c
> + *
> + * Copyright (C) 2010 Google, Inc.
> + * Author: Erik Gilling <konkers@android.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +
> +#include <mach/dc.h>
> +
> +#include "dc_reg.h"
> +#include "dc_priv.h"
> +
> +
> +static const u32 tegra_dc_rgb_pintable[] = {
> +       DC_COM_PIN_OUTPUT_ENABLE0,      0x00000000,
> +       DC_COM_PIN_OUTPUT_ENABLE1,      0x00000000,
> +       DC_COM_PIN_OUTPUT_ENABLE2,      0x00000000,
> +       DC_COM_PIN_OUTPUT_ENABLE3,      0x00000000,
> +       DC_COM_PIN_OUTPUT_POLARITY0,    0x00000000,
> +       DC_COM_PIN_OUTPUT_POLARITY1,    0x01000000,
> +       DC_COM_PIN_OUTPUT_POLARITY2,    0x00000000,
> +       DC_COM_PIN_OUTPUT_POLARITY3,    0x00000000,
> +       DC_COM_PIN_OUTPUT_DATA0,        0x00000000,
> +       DC_COM_PIN_OUTPUT_DATA1,        0x00000000,
> +       DC_COM_PIN_OUTPUT_DATA2,        0x00000000,
> +       DC_COM_PIN_OUTPUT_DATA3,        0x00000000,
> +       DC_COM_PIN_OUTPUT_SELECT0,      0x00000000,
> +       DC_COM_PIN_OUTPUT_SELECT1,      0x00000000,
> +       DC_COM_PIN_OUTPUT_SELECT2,      0x00000000,
> +       DC_COM_PIN_OUTPUT_SELECT3,      0x00000000,
> +       DC_COM_PIN_OUTPUT_SELECT4,      0x00210222,
> +       DC_COM_PIN_OUTPUT_SELECT5,      0x00002200,
> +       DC_COM_PIN_OUTPUT_SELECT6,      0x00020000,
> +};
> +
> +
> +void tegra_dc_rgb_init(struct tegra_dc *dc)
> +{
> +       tegra_dc_writel(dc, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
> +                       PW4_ENABLE | PM0_ENABLE | PM1_ENABLE,
> +                       DC_CMD_DISPLAY_POWER_CONTROL);
> +
> +       tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND);
> +
> +       tegra_dc_write_table(dc, tegra_dc_rgb_pintable);
> +}
> +
> +struct tegra_dc_out_ops tegra_dc_rgb_ops = {
> +       .init = tegra_dc_rgb_init,
> +};
> +
> diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c
> new file mode 100644
> index 0000000..4db3958
> --- /dev/null
> +++ b/drivers/video/tegra/fb.c
> @@ -0,0 +1,311 @@
> +/*
> + * drivers/video/tegra/fb.c
> + *
> + * Copyright (C) 2010 Google, Inc.
> + * Author: Erik Gilling <konkers@android.com>
> + *         Colin Cross <ccross@android.com>
> + *         Travis Geiselbrecht <travis@palm.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/fb.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +#include <linux/platform_device.h>
> +
> +#include <asm/atomic.h>
> +
> +#include <mach/dc.h>
> +#include <mach/fb.h>
> +
> +struct tegra_fb_info {
> +       struct tegra_dc_win     *win;
> +       struct platform_device  *pdev;
> +       struct fb_info          *info;
> +
> +       struct resource         *fb_mem;
> +
> +       int                     xres;
> +       int                     yres;
> +
> +       atomic_t                in_use;
> +};
> +
> +/* palette array used by the fbcon */
> +static u32 pseudo_palette[16];
> +
> +static int tegra_fb_open(struct fb_info *info, int user)
> +{
> +       struct tegra_fb_info *tegra_fb = info->par;
> +
> +       if (atomic_xchg(&tegra_fb->in_use, 1))
> +               return -EBUSY;
> +
> +       return 0;
> +}
> +
> +static int tegra_fb_release(struct fb_info *info, int user)
> +{
> +       struct tegra_fb_info *tegra_fb = info->par;
> +
> +       WARN_ON(!atomic_xchg(&tegra_fb->in_use, 0));
> +
> +       return 0;
> +}
> +
> +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))
> +               return -EINVAL;
> +       return 0;
> +}
> +
> +static int tegra_fb_set_par(struct fb_info *info)
> +{
> +       struct tegra_fb_info *tegra_fb = info->par;
> +       struct fb_var_screeninfo *var = &info->var;
> +
> +       /* we only support RGB ordering for now */
> +       switch (var->bits_per_pixel) {
> +       case 32:
> +       case 24:
> +               var->red.offset = 0;
> +               var->red.length = 8;
> +               var->green.offset = 8;
> +               var->green.length = 8;
> +               var->blue.offset = 16;
> +               var->blue.length = 8;
> +               tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8;
> +               break;
> +       case 16:
> +               var->red.offset = 11;
> +               var->red.length = 5;
> +               var->green.offset = 5;
> +               var->green.length = 6;
> +               var->blue.offset = 0;
> +               var->blue.length = 5;
> +               tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +       info->fix.line_length = var->xres * var->bits_per_pixel / 8;
> +
> +       tegra_dc_update_windows(&tegra_fb->win, 1);
> +
> +       return 0;
> +}
> +
> +static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
> +       unsigned blue, unsigned transp, struct fb_info *info)
> +{
> +       struct fb_var_screeninfo *var = &info->var;
> +
> +       if (info->fix.visual = FB_VISUAL_TRUECOLOR ||
> +           info->fix.visual = FB_VISUAL_DIRECTCOLOR) {
> +               u32 v;
> +
> +               if (regno >= 16)
> +                       return -EINVAL;
> +
> +               v = (red << var->red.offset) |
> +                       (green << var->green.offset) |
> +                       (blue << var->blue.offset);
> +
> +               ((u32 *)info->pseudo_palette)[regno] = v;
> +       }
> +
> +       return 0;
> +}
> +
> +static int tegra_fb_pan_display(struct fb_var_screeninfo *var,
> +                               struct fb_info *info)
> +{
> +       struct tegra_fb_info *tegra_fb = info->par;
> +       char __iomem *flush_start;
> +       char __iomem *flush_end;
> +       u32 addr;
> +
> +       flush_start = info->screen_base + (var->yoffset * info->fix.line_length);
> +       flush_end = flush_start + (var->yres * info->fix.line_length);
> +
> +       info->var.xoffset = var->xoffset;
> +       info->var.yoffset = var->yoffset;
> +
> +       addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) +
> +               (var->xoffset * (var->bits_per_pixel/8));
> +
> +       tegra_fb->win->phys_addr = addr;
> +       /* TODO: update virt_addr */
> +
> +       tegra_dc_update_windows(&tegra_fb->win, 1);
> +       tegra_dc_sync_windows(&tegra_fb->win, 1);
> +
> +       return 0;
> +}
> +
> +static void tegra_fb_fillrect(struct fb_info *info,
> +                             const struct fb_fillrect *rect)
> +{
> +       cfb_fillrect(info, rect);
> +}
> +
> +static void tegra_fb_copyarea(struct fb_info *info,
> +                             const struct fb_copyarea *region)
> +{
> +       cfb_copyarea(info, region);
> +}
> +
> +static void tegra_fb_imageblit(struct fb_info *info,
> +                              const struct fb_image *image)
> +{
> +       cfb_imageblit(info, image);
> +}
> +
> +static struct fb_ops tegra_fb_ops = {
> +       .owner = THIS_MODULE,
> +       .fb_open = tegra_fb_open,
> +       .fb_release = tegra_fb_release,
> +       .fb_check_var = tegra_fb_check_var,
> +       .fb_set_par = tegra_fb_set_par,
> +       .fb_setcolreg = tegra_fb_setcolreg,
> +       .fb_pan_display = tegra_fb_pan_display,
> +       .fb_fillrect = tegra_fb_fillrect,
> +       .fb_copyarea = tegra_fb_copyarea,
> +       .fb_imageblit = tegra_fb_imageblit,
> +};
> +
> +struct tegra_fb_info *tegra_fb_register(struct platform_device *pdev,
> +                                       struct tegra_dc *dc,
> +                                       struct tegra_fb_data *fb_data,
> +                                       struct resource *fb_mem)
> +{
> +       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;
> +       int ret = 0;
> +
> +       win = tegra_dc_get_window(dc, fb_data->win);
> +       if (!win) {
> +               dev_err(&pdev->dev, "dc does not have a window at index %d\n",
> +                       fb_data->win);
> +               return ERR_PTR(-ENOENT);
> +       }
> +
> +       info = framebuffer_alloc(sizeof(struct tegra_fb_info), &pdev->dev);
> +       if (!info) {
> +               ret = -ENOMEM;
> +               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(&pdev->dev, "fb can't be mapped\n");
> +               ret = -EBUSY;
> +               goto err_free;
> +       }
> +
> +       tegra_fb = info->par;
> +       tegra_fb->win = win;
> +       tegra_fb->pdev = pdev;
> +       tegra_fb->fb_mem = fb_mem;
> +       tegra_fb->xres = fb_data->xres;
> +       tegra_fb->yres = fb_data->yres;
> +       atomic_set(&tegra_fb->in_use, 0);
> +
> +       info->fbops = &tegra_fb_ops;
> +       info->pseudo_palette = pseudo_palette;
> +       info->screen_base = fb_base;
> +       info->screen_size = fb_size;
> +
> +       strlcpy(info->fix.id, "tegra_fb", sizeof(info->fix.id));
> +       info->fix.type          = FB_TYPE_PACKED_PIXELS;
> +       info->fix.visual        = FB_VISUAL_TRUECOLOR;
> +       info->fix.xpanstep      = 1;
> +       info->fix.ypanstep      = 1;
> +       info->fix.accel         = FB_ACCEL_NONE;
> +       info->fix.smem_start    = fb_phys;
> +       info->fix.smem_len      = fb_size;
> +
> +       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.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.left_margin           = 0;
> +       info->var.right_margin          = 0;
> +       info->var.upper_margin          = 0;
> +       info->var.lower_margin          = 0;
> +       info->var.hsync_len             = 0;
> +       info->var.vsync_len             = 0;
> +       info->var.vmode                 = FB_VMODE_NONINTERLACED;
> +
> +       win->x = 0;
> +       win->y = 0;
> +       win->w = fb_data->xres;
> +       win->h = fb_data->yres;
> +       /* TODO: set to output res dc */
> +       win->out_w = fb_data->xres;
> +       win->out_h = fb_data->yres;
> +       win->phys_addr = fb_phys;
> +       win->virt_addr = fb_base;
> +       win->flags = TEGRA_WIN_FLAG_ENABLED | TEGRA_WIN_FLAG_COLOR_EXPAND;
> +
> +       tegra_fb_set_par(info);
> +
> +       if (register_framebuffer(info)) {
> +               dev_err(&pdev->dev, "failed to register framebuffer\n");
> +               ret = -ENODEV;
> +               goto err_iounmap_fb;
> +       }
> +
> +       tegra_fb->info = info;
> +
> +       dev_info(&pdev->dev, "probed\n");
> +
> +       return tegra_fb;
> +
> +err_iounmap_fb:
> +       iounmap(fb_base);
> +err_free:
> +       framebuffer_release(info);
> +err:
> +       return ERR_PTR(ret);
> +}
> +
> +void tegra_fb_unregister(struct tegra_fb_info *fb_info)
> +{
> +       struct fb_info *info = fb_info->info;
> +
> +       unregister_framebuffer(info);
> +       iounmap(info->screen_base);
> +       framebuffer_release(info);
> +}
> --
> 1.6.5.6
>
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] video: tegra: add tegra display controller and fb driver
       [not found]     ` <4C75A039.8060701@bluewatersys.com>
@ 2010-08-25 23:56       ` Erik Gilling
  2010-08-26  0:00         ` Ryan Mallon
  0 siblings, 1 reply; 6+ messages in thread
From: Erik Gilling @ 2010-08-25 23:56 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Aug 25, 2010 at 3:59 PM, Ryan Mallon <ryan@bluewatersys.com> wrote:
> On 08/26/2010 10:03 AM, Erik Gilling wrote:
>
> You sent this just to me, did you mean to post to the list also?

oops

>>>> +config FB_TEGRA
>>>> +     tristate "Tegra Framebuffer driver"
>>>> +     depends on TEGRA_DC && FB = y
>>>
>
>>>
>>> The dump_regs code is very long for a debugging feature. Can it just be
>>> replaced by a for loop which prints the offsets and values of each register?
>>
>> Printing the names of the registers has been invaluable so far.  I
>> could move this to a separate file.
>
> Its useful for debugging while developing the driver, but it adds a lot
> of code. Do you expect the register dumping features to be required once
> the driver hits mainline?

I expect the driver will hit mainline is several phases.  This first
one.  One where HDMI works.  One where DSI works.  One which has
rotation.  This code is useful for all those phases.

>>>> +#undef DUMP_REG
>>>> +
>>>> +static int dbg_dc_show(struct seq_file *s, void *unused)
>>>> +{
>>>> +     struct tegra_dc *dc = s->private;
>>>> +
>>>> +     _dump_regs(dc, s, dbg_regs_print);
>>>> +
>>>> +     return 0;
>>>> +}
>>>
>>> This all looks a bit confusing (especially the undef stuff). Why do you
>>> have both a debugfs interface to the registers and one which prints them
>>> using dev_dbg?
>>
>> The extra #undef was a type-o.  The debugfs file is useful when
>> someone brings me a misbehaving unit and I need to dump the register
>> set.  The dump_regs funciton is usefull when I want to look at the
>> state at different points in a code-path.
>
> If you really want to keep the debug feature then I would at least pick
> one interface or the other. The debugfs one feels a bit more standard,
> and is probably more useful to end users once the driver is in mainline.

They still fill different uses.  You can't cat a debugfs file from an
interrupt handler.  You can't call dump_regs() from userspace.  I'm
still actively using both.  I could see taking one out once
development on the driver calms down.

>>>> +     tegra_dc_init(dc);
>>>> +
>>>> +     tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
>>>> +
>>>> +     platform_set_drvdata(pdev, dc);
>>>> +
>>>> +     tegra_dc_dbg_add(dc);
>>>> +
>>>> +     dev_info(&pdev->dev, "probed\n");
>>>
>>> dev_dbg?
>>
>> I find it very useful when drivers announce when new devices are added.
>>
>
> It adds more noise to the bootlog. There are other ways to determine
> that a driver has successfully probed. If you do want to keep the
> message at least make it a bit more informative so that it prints the
> configuration out or something.

I'll add some more info to the line.

-Erik

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] video: tegra: add tegra display controller and fb driver
  2010-08-25 23:56       ` Erik Gilling
@ 2010-08-26  0:00         ` Ryan Mallon
  2010-08-26  0:02           ` Erik Gilling
  0 siblings, 1 reply; 6+ messages in thread
From: Ryan Mallon @ 2010-08-26  0:00 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/26/2010 11:56 AM, Erik Gilling wrote:
> On Wed, Aug 25, 2010 at 3:59 PM, Ryan Mallon <ryan@bluewatersys.com> wrote:
>> On 08/26/2010 10:03 AM, Erik Gilling wrote:
>>
>> You sent this just to me, did you mean to post to the list also?
> 
> oops
> 
>>>>> +config FB_TEGRA
>>>>> +     tristate "Tegra Framebuffer driver"
>>>>> +     depends on TEGRA_DC && FB = y
>>>>
>>
>>>>
>>>> The dump_regs code is very long for a debugging feature. Can it just be
>>>> replaced by a for loop which prints the offsets and values of each register?
>>>
>>> Printing the names of the registers has been invaluable so far.  I
>>> could move this to a separate file.
>>
>> Its useful for debugging while developing the driver, but it adds a lot
>> of code. Do you expect the register dumping features to be required once
>> the driver hits mainline?
> 
> I expect the driver will hit mainline is several phases.  This first
> one.  One where HDMI works.  One where DSI works.  One which has
> rotation.  This code is useful for all those phases.
...
> They still fill different uses.  You can't cat a debugfs file from an
> interrupt handler.  You can't call dump_regs() from userspace.  I'm
> still actively using both.  I could see taking one out once
> development on the driver calms down.

Okay, maybe keep both with a comment stating why the debug is still
there, and that it is scheduled to be removed once the driver is
complete in mainline?

~Ryan

-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751
Fax:   +64 3 3779135			  USA 1800 261 2934

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] video: tegra: add tegra display controller and fb driver
  2010-08-26  0:00         ` Ryan Mallon
@ 2010-08-26  0:02           ` Erik Gilling
  0 siblings, 0 replies; 6+ messages in thread
From: Erik Gilling @ 2010-08-26  0:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Aug 25, 2010 at 5:00 PM, Ryan Mallon <ryan@bluewatersys.com> wrote:
>>> Its useful for debugging while developing the driver, but it adds a lot
>>> of code. Do you expect the register dumping features to be required once
>>> the driver hits mainline?
>>
>> I expect the driver will hit mainline is several phases.  This first
>> one.  One where HDMI works.  One where DSI works.  One which has
>> rotation.  This code is useful for all those phases.
> ...
>> They still fill different uses.  You can't cat a debugfs file from an
>> interrupt handler.  You can't call dump_regs() from userspace.  I'm
>> still actively using both.  I could see taking one out once
>> development on the driver calms down.
>
> Okay, maybe keep both with a comment stating why the debug is still
> there, and that it is scheduled to be removed once the driver is
> complete in mainline?

OK will do.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2010-08-26  0:02 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-08-11 23:11 [PATCH] video: tegra: add tegra display controller and fb driver Erik Gilling
2010-08-12  4:34 ` Ryan Mallon
     [not found]   ` <AANLkTini+n1osaQHkomguzZi-DaHLX3i1G4gA82bK0=S@mail.gmail.com>
     [not found]     ` <4C75A039.8060701@bluewatersys.com>
2010-08-25 23:56       ` Erik Gilling
2010-08-26  0:00         ` Ryan Mallon
2010-08-26  0:02           ` Erik Gilling
2010-08-25 22:04 ` Erik Gilling

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).