* [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).