From 18a25382e81c03230e022ca2eb7e0fce24479d6a Mon Sep 17 00:00:00 2001
From: Tomi Valkeinen <tomi.valkeinen@nokia.com>
Date: Thu, 2 Apr 2009 10:31:57 +0300
Subject: [PATCH] DSS2: HACK: Add DSS2 support for N800

Works, but it an ugly quick hack.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
---
 arch/arm/mach-omap2/board-n800.c             |  216 +++++++++++---
 drivers/video/omap2/displays/Kconfig         |   10 +
 drivers/video/omap2/displays/Makefile        |    3 +
 drivers/video/omap2/displays/ctrl-blizzard.c |  279 +++++++++++++++++
 drivers/video/omap2/displays/panel-n800.c    |  435 ++++++++++++++++++++++++++
 5 files changed, 905 insertions(+), 38 deletions(-)
 create mode 100644 drivers/video/omap2/displays/ctrl-blizzard.c
 create mode 100644 drivers/video/omap2/displays/panel-n800.c

diff --git a/arch/arm/mach-omap2/board-n800.c b/arch/arm/mach-omap2/board-n800.c
index f6f6571..6de60ae 100644
--- a/arch/arm/mach-omap2/board-n800.c
+++ b/arch/arm/mach-omap2/board-n800.c
@@ -41,6 +41,8 @@
 #include <mach/clock.h>
 #include <mach/gpio-switch.h>
 #include <mach/blizzard.h>
+#include <mach/display.h>
+#include <mach/vram.h>
 
 #include <../drivers/cbus/tahvo.h>
 #include <../drivers/media/video/tcm825x.h>
@@ -161,23 +163,176 @@ static struct omap_uart_config n800_uart_config __initdata = {
 
 #include "../../../drivers/cbus/retu.h"
 
-static struct omap_fbmem_config n800_fbmem0_config __initdata = {
-	.size = 752 * 1024,
+static struct omap_tmp105_config n800_tmp105_config __initdata = {
+	.tmp105_irq_pin = 125,
+	.set_power = n800_tmp105_set_power,
 };
 
-static struct omap_fbmem_config n800_fbmem1_config __initdata = {
-	.size = 752 * 1024,
-};
 
-static struct omap_fbmem_config n800_fbmem2_config __initdata = {
-	.size = 752 * 1024,
+
+
+/* DISPLAY */
+static struct {
+	struct clk *sys_ck;
+} blizzard;
+
+static int blizzard_get_clocks(void)
+{
+	blizzard.sys_ck = clk_get(0, "osc_ck");
+	if (IS_ERR(blizzard.sys_ck)) {
+		printk(KERN_ERR "can't get Blizzard clock\n");
+		return PTR_ERR(blizzard.sys_ck);
+	}
+	return 0;
+}
+
+static unsigned long blizzard_get_clock_rate(void)
+{
+	return clk_get_rate(blizzard.sys_ck);
+}
+
+static int n800_pn800_enable(struct omap_display *display)
+{
+	if (display->hw_config.panel_reset_gpio != -1) {
+		printk("enabling panel gpio\n");
+		gpio_direction_output(display->hw_config.panel_reset_gpio, 1);
+	}
+
+	return 0;
+}
+
+static void n800_pn800_disable(struct omap_display *display)
+{
+	if (display->hw_config.panel_reset_gpio != -1) {
+		printk("disabling panel gpio\n");
+		gpio_direction_output(display->hw_config.panel_reset_gpio, 0);
+		msleep(120);
+	}
+}
+
+static int n800_blizzard_enable(struct omap_display *display)
+{
+	printk("enabling bliz powers\n");
+
+	/* Vcore to 1.475V */
+	tahvo_set_clear_reg_bits(0x07, 0, 0xf);
+	msleep(10);
+
+	clk_enable(blizzard.sys_ck);
+
+	if (display->hw_config.ctrl_reset_gpio != -1)
+		gpio_direction_output(display->hw_config.ctrl_reset_gpio, 1);
+
+	printk("osc_ck %lu\n", blizzard_get_clock_rate());
+
+	return 0;
+}
+
+static void n800_blizzard_disable(struct omap_display *display)
+{
+	printk("disabling bliz powers\n");
+
+	if (display->hw_config.ctrl_reset_gpio != -1)
+		gpio_direction_output(display->hw_config.ctrl_reset_gpio, 0);
+
+	clk_disable(blizzard.sys_ck);
+
+	/* Vcore to 1.005V */
+	tahvo_set_clear_reg_bits(0x07, 0xf, 0);
+}
+
+static int n800_set_backlight_level(struct omap_display *display, int level)
+{
+	return 0;
+}
+
+static struct omap_dss_display_config n800_dsi_display_data = {
+	.type = OMAP_DISPLAY_TYPE_DBI,
+	.name = "lcd",
+	.ctrl_name = "ctrl-blizzard",
+	.panel_name = "panel-pn800",
+	.panel_reset_gpio = -1,
+	.ctrl_reset_gpio = N800_BLIZZARD_POWERDOWN_GPIO,
+	.panel_enable = n800_pn800_enable,
+	.panel_disable = n800_pn800_disable,
+	.ctrl_enable = n800_blizzard_enable,
+	.ctrl_disable = n800_blizzard_disable,
+	.set_backlight = n800_set_backlight_level,
+	.u.rfbi = {
+		.channel = 0,
+		/* 8 for cmd mode, 16 for pixel data. ctrl-blizzard handles switching */
+		.data_lines = 8,
+	},
+	.panel_data = 0, // XXX used for panel datalines
+};
+static struct omap_dss_board_info n800_dss_data = {
+	.num_displays = 1,
+	.displays = {
+		&n800_dsi_display_data,
+	},
 };
 
-static struct omap_tmp105_config n800_tmp105_config __initdata = {
-	.tmp105_irq_pin = 125,
-	.set_power = n800_tmp105_set_power,
+static struct platform_device n800_dss_device = {
+	.name          = "omapdss",
+	.id            = -1,
+	.dev            = {
+		.platform_data = &n800_dss_data,
+	},
 };
 
+static void __init n800_display_init(void)
+{
+	int r;
+	const struct omap_lcd_config *conf;
+
+	conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
+	if (conf != NULL) {
+		n800_dsi_display_data.panel_reset_gpio = conf->nreset_gpio;
+		n800_dsi_display_data.panel_data =
+			(void*)(u32)conf->data_lines; // XXX
+		//printk("\n\nTULI %d\n\n", conf->data_lines);
+	} else {
+		printk("\n\nEI TULLU MIOTÄÄÄ\n\n");
+	}
+
+	blizzard_get_clocks();
+	clk_enable(blizzard.sys_ck); // XXX always enable
+
+	//omapfb_set_ctrl_platform_data(&n800_blizzard_data);
+	//
+	if (n800_dsi_display_data.ctrl_reset_gpio != -1) {
+		r = gpio_request(n800_dsi_display_data.ctrl_reset_gpio,
+				"Blizzard pd");
+		if (r < 0) {
+			n800_dsi_display_data.ctrl_reset_gpio = -1;
+			printk(KERN_ERR "Unable to get Blizzard GPIO\n");
+		} else {
+			gpio_direction_output(n800_dsi_display_data.ctrl_reset_gpio,
+					1);
+			// XXX always enable
+		}
+	}
+
+	if (n800_dsi_display_data.panel_reset_gpio != -1) {
+		r = gpio_request(n800_dsi_display_data.panel_reset_gpio,
+				"panel reset");
+		if (r < 0) {
+			n800_dsi_display_data.panel_reset_gpio = -1;
+			printk(KERN_ERR "Unable to get pn800 GPIO\n");
+		} else {
+			gpio_direction_output(n800_dsi_display_data.panel_reset_gpio,
+					1);
+			// XXX always enable
+		}
+	}
+}
+
+/* DISPLAY END */
+
+
+
+
+
 static void mipid_shutdown(struct mipid_platform_data *pdata)
 {
 	if (pdata->nreset_gpio != -1) {
@@ -191,6 +346,7 @@ static struct mipid_platform_data n800_mipid_platform_data = {
 	.shutdown = mipid_shutdown,
 };
 
+#if 0
 static void __init mipid_dev_init(void)
 {
 	const struct omap_lcd_config *conf;
@@ -201,26 +357,9 @@ static void __init mipid_dev_init(void)
 		n800_mipid_platform_data.data_lines = conf->data_lines;
 	}
 }
+#endif
 
-static struct {
-	struct clk *sys_ck;
-} blizzard;
-
-static int blizzard_get_clocks(void)
-{
-	blizzard.sys_ck = clk_get(0, "osc_ck");
-	if (IS_ERR(blizzard.sys_ck)) {
-		printk(KERN_ERR "can't get Blizzard clock\n");
-		return PTR_ERR(blizzard.sys_ck);
-	}
-	return 0;
-}
-
-static unsigned long blizzard_get_clock_rate(struct device *dev)
-{
-	return clk_get_rate(blizzard.sys_ck);
-}
-
+#if 0
 static void blizzard_enable_clocks(int enable)
 {
 	if (enable)
@@ -265,14 +404,12 @@ static void __init blizzard_dev_init(void)
 	gpio_direction_output(N800_BLIZZARD_POWERDOWN_GPIO, 1);
 
 	blizzard_get_clocks();
-	omapfb_set_ctrl_platform_data(&n800_blizzard_data);
+	//omapfb_set_ctrl_platform_data(&n800_blizzard_data);
 }
+#endif
 
 static struct omap_board_config_kernel n800_config[] __initdata = {
 	{ OMAP_TAG_UART,	                &n800_uart_config },
-	{ OMAP_TAG_FBMEM,			&n800_fbmem0_config },
-	{ OMAP_TAG_FBMEM,			&n800_fbmem1_config },
-	{ OMAP_TAG_FBMEM,			&n800_fbmem2_config },
 	{ OMAP_TAG_TMP105,			&n800_tmp105_config },
 };
 
@@ -379,7 +516,7 @@ static struct omap2_mcspi_device_config tsc2005_mcspi_config = {
 
 static struct spi_board_info n800_spi_board_info[] __initdata = {
 	{
-		.modalias	= "lcd_mipid",
+		.modalias	= "panel-n800",
 		.bus_num	= 1,
 		.chip_select	= 1,
 		.max_speed_hz	= 4000000,
@@ -404,7 +541,7 @@ static struct spi_board_info n800_spi_board_info[] __initdata = {
 
 static struct spi_board_info n810_spi_board_info[] __initdata = {
 	{
-		.modalias	 = "lcd_mipid",
+		.modalias	 = "panel-n800",
 		.bus_num	 = 1,
 		.chip_select	 = 1,
 		.max_speed_hz	 = 4000000,
@@ -582,6 +719,7 @@ static struct platform_device *n800_devices[] __initdata = {
 #if defined(CONFIG_CBUS_RETU_HEADSET)
 	&retu_headset_device,
 #endif
+	&n800_dss_device,
 };
 
 #ifdef CONFIG_MENELAUS
@@ -713,9 +851,10 @@ void __init nokia_n800_common_init(void)
 	if (machine_is_nokia_n810())
 		i2c_register_board_info(2, n810_i2c_board_info_2,
 			ARRAY_SIZE(n810_i2c_board_info_2));
-		
-	mipid_dev_init();
-	blizzard_dev_init();
+
+	//mipid_dev_init();
+	//blizzard_dev_init();
+	n800_display_init();
 }
 
 static void __init nokia_n800_init(void)
@@ -735,6 +874,7 @@ void __init nokia_n800_map_io(void)
 	omap_board_config_size = ARRAY_SIZE(n800_config);
 
 	omap2_set_globals_242x();
+	omap2_set_sdram_vram(800 * 480 * 2 * 3, 0);
 	omap2_map_common_io();
 }
 
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
index 0419ec8..356ceb1 100644
--- a/drivers/video/omap2/displays/Kconfig
+++ b/drivers/video/omap2/displays/Kconfig
@@ -18,4 +18,14 @@ config PANEL_SHARP_LS037V7DW01
         depends on OMAP2_DSS
         help
           LCD Panel used in TI's SDP3430 and EVM boards
+
+config PANEL_N800
+        tristate "Panel N8x0"
+        help
+          N8x0 LCD (hack)
+
+config CTRL_BLIZZARD
+        tristate "Blizzard Controller"
+        help
+          Blizzard Controller (hack)
 endmenu
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile
index a26bbd2..1b74b7e 100644
--- a/drivers/video/omap2/displays/Makefile
+++ b/drivers/video/omap2/displays/Makefile
@@ -1,3 +1,6 @@
 obj-$(CONFIG_PANEL_GENERIC) += panel-generic.o
 obj-$(CONFIG_PANEL_SAMSUNG_LTE430WQ_F0C) += panel-samsung-lte430wq-f0c.o
 obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
+
+obj-$(CONFIG_CTRL_BLIZZARD) += ctrl-blizzard.o
+obj-$(CONFIG_PANEL_N800) += panel-n800.o
diff --git a/drivers/video/omap2/displays/ctrl-blizzard.c b/drivers/video/omap2/displays/ctrl-blizzard.c
new file mode 100644
index 0000000..6698e4d
--- /dev/null
+++ b/drivers/video/omap2/displays/ctrl-blizzard.c
@@ -0,0 +1,279 @@
+
+//#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <mach/display.h>
+#include <mach/dma.h>
+
+#ifdef DEBUG
+#define DBG(format, ...) printk(KERN_DEBUG "Blizzard: " format, ## __VA_ARGS__)
+#else
+#define DBG(format, ...)
+#endif
+
+#define BLIZZARD_REV_CODE			0x00
+#define BLIZZARD_CONFIG				0x02
+#define BLIZZARD_PLL_DIV			0x04
+#define BLIZZARD_PLL_LOCK_RANGE			0x06
+#define BLIZZARD_PLL_CLOCK_SYNTH_0		0x08
+#define BLIZZARD_PLL_CLOCK_SYNTH_1		0x0a
+#define BLIZZARD_PLL_MODE			0x0c
+#define BLIZZARD_CLK_SRC			0x0e
+#define BLIZZARD_MEM_BANK0_ACTIVATE		0x10
+#define BLIZZARD_MEM_BANK0_STATUS		0x14
+#define BLIZZARD_PANEL_CONFIGURATION		0x28
+#define BLIZZARD_HDISP				0x2a
+#define BLIZZARD_HNDP				0x2c
+#define BLIZZARD_VDISP0				0x2e
+#define BLIZZARD_VDISP1				0x30
+#define BLIZZARD_VNDP				0x32
+#define BLIZZARD_HSW				0x34
+#define BLIZZARD_VSW				0x38
+#define BLIZZARD_DISPLAY_MODE			0x68
+#define BLIZZARD_INPUT_WIN_X_START_0		0x6c
+#define BLIZZARD_DATA_SOURCE_SELECT		0x8e
+#define BLIZZARD_DISP_MEM_DATA_PORT		0x90
+#define BLIZZARD_DISP_MEM_READ_ADDR0		0x92
+#define BLIZZARD_POWER_SAVE			0xE6
+#define BLIZZARD_NDISP_CTRL_STATUS		0xE8
+
+/* Data source select */
+/* For S1D13745 */
+#define BLIZZARD_SRC_WRITE_LCD_BACKGROUND	0x00
+#define BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE	0x01
+#define BLIZZARD_SRC_WRITE_OVERLAY_ENABLE	0x04
+#define BLIZZARD_SRC_DISABLE_OVERLAY		0x05
+/* For S1D13744 */
+#define BLIZZARD_SRC_WRITE_LCD			0x00
+#define BLIZZARD_SRC_BLT_LCD			0x06
+
+#define BLIZZARD_COLOR_RGB565			0x01
+#define BLIZZARD_COLOR_YUV420			0x09
+
+#define BLIZZARD_VERSION_S1D13745		0x01	/* Hailstorm */
+#define BLIZZARD_VERSION_S1D13744		0x02	/* Blizzard */
+
+#define BLIZZARD_AUTO_UPDATE_TIME		(HZ / 20)
+
+
+
+static struct {
+	int			version;
+} blizzard;
+
+
+static inline void blizzard_cmd(u8 cmd)
+{
+        omap_rfbi_write_command(&cmd, 1);
+}
+
+static inline void blizzard_write(u8 cmd, const u8 *buf, int len)
+{
+        omap_rfbi_write_command(&cmd, 1);
+        omap_rfbi_write_data(buf, len);
+}
+
+static inline void blizzard_read(u8 cmd, u8 *buf, int len)
+{
+        omap_rfbi_write_command(&cmd, 1);
+        omap_rfbi_read_data(buf, len);
+}
+
+static u8 blizzard_read_reg(u8 cmd)
+{
+	u8 data;
+	blizzard_read(cmd, &data, 1);
+	return data;
+}
+
+static int blizzard_ctrl_init(struct omap_display *display)
+{
+	DBG("blizzard_ctrl_init\n");
+
+	return 0;
+}
+
+
+static int blizzard_ctrl_enable(struct omap_display *display)
+{
+	int r = 0;
+	u8 rev, conf;
+
+	DBG("blizzard_ctrl_enable\n");
+
+	if (display->hw_config.ctrl_enable) {
+		r = display->hw_config.ctrl_enable(display);
+		if (r)
+			return r;
+	}
+
+	msleep(100);
+
+	rev = blizzard_read_reg(BLIZZARD_CLK_SRC);
+	printk("CLK_SRC %x\n", rev);
+
+	rev = blizzard_read_reg(BLIZZARD_PLL_DIV);
+	printk("PLLDIV %x\n", rev);
+
+	rev = blizzard_read_reg(BLIZZARD_REV_CODE);
+	conf = blizzard_read_reg(BLIZZARD_CONFIG);
+
+	printk("rev %x, conf %x\n", rev, conf);
+
+	switch (rev & 0xfc) {
+	case 0x9c:
+		blizzard.version = BLIZZARD_VERSION_S1D13744;
+		pr_info("omapfb: s1d13744 LCD controller rev %d "
+			"initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
+		break;
+	case 0xa4:
+		blizzard.version = BLIZZARD_VERSION_S1D13745;
+		pr_info("omapfb: s1d13745 LCD controller rev %d "
+			"initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
+		break;
+	default:
+		printk("invalid s1d1374x revision %02x\n",
+			rev);
+		r = -ENODEV;
+	}
+
+	return r;
+}
+
+static void blizzard_ctrl_disable(struct omap_display *display)
+{
+	DBG("blizzard_ctrl_disable\n");
+
+	if (display->hw_config.ctrl_disable)
+		display->hw_config.ctrl_disable(display);
+}
+
+int rfbi_configure(int rfbi_module, int bpp, int lines);
+
+static void blizzard_ctrl_setup_update(struct omap_display *display,
+				    u16 x, u16 y, u16 w, u16 h)
+{
+	u8 tmp[18];
+	int x_end, y_end;
+
+	DBG("blizzard_ctrl_setup_update\n");
+
+	x_end = x + w - 1;
+	y_end = y + h - 1;
+
+	tmp[0] = x;
+	tmp[1] = x >> 8;
+	tmp[2] = y;
+	tmp[3] = y >> 8;
+	tmp[4] = x_end;
+	tmp[5] = x_end >> 8;
+	tmp[6] = y_end;
+	tmp[7] = y_end >> 8;
+
+	/* scaling? */
+	tmp[8] = x;
+	tmp[9] = x >> 8;
+	tmp[10] = y;
+	tmp[11] = y >> 8;
+	tmp[12] = x_end;
+	tmp[13] = x_end >> 8;
+	tmp[14] = y_end;
+	tmp[15] = y_end >> 8;
+
+	tmp[16] = BLIZZARD_COLOR_RGB565; //color_mode;
+
+	if (blizzard.version == BLIZZARD_VERSION_S1D13745)
+		tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND;
+	else
+		tmp[17] = blizzard.version == BLIZZARD_VERSION_S1D13744 ?
+				BLIZZARD_SRC_WRITE_LCD :
+				BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE;
+
+	rfbi_configure(display->hw_config.u.rfbi.channel,
+		       16,
+		       8);
+
+	blizzard_write(BLIZZARD_INPUT_WIN_X_START_0, tmp, 18);
+
+	rfbi_configure(display->hw_config.u.rfbi.channel,
+		       16,
+		       16);
+}
+
+static int blizzard_ctrl_enable_te(struct omap_display *display, bool enable)
+{
+	return 0;
+}
+
+static int blizzard_ctrl_rotate(struct omap_display *display, u8 rotate)
+{
+	return 0;
+}
+
+static int blizzard_ctrl_mirror(struct omap_display *display, bool enable)
+{
+	return 0;
+}
+
+static int blizzard_run_test(struct omap_display *display, int test_num)
+{
+	return 0;
+}
+
+static struct omap_ctrl blizzard_ctrl = {
+	.owner = THIS_MODULE,
+	.name = "ctrl-blizzard",
+	.init = blizzard_ctrl_init,
+	.enable = blizzard_ctrl_enable,
+	.disable = blizzard_ctrl_disable,
+	.setup_update = blizzard_ctrl_setup_update,
+	.enable_te = blizzard_ctrl_enable_te,
+	.set_rotate = blizzard_ctrl_rotate,
+	.set_mirror = blizzard_ctrl_mirror,
+	.run_test = blizzard_run_test,
+	.pixel_size = 16,
+
+	.timings = {
+                .cs_on_time     = 0,
+
+                .we_on_time     = 9000,
+                .we_off_time    = 18000,
+                .we_cycle_time  = 36000,
+
+                .re_on_time     = 9000,
+                .re_off_time    = 27000,
+                .re_cycle_time  = 36000,
+
+                .access_time    = 27000,
+                .cs_off_time    = 36000,
+
+                .cs_pulse_width = 0,
+        },
+};
+
+
+static int __init blizzard_init(void)
+{
+	DBG("blizzard_init\n");
+	omap_dss_register_ctrl(&blizzard_ctrl);
+	return 0;
+}
+
+static void __exit blizzard_exit(void)
+{
+	DBG("blizzard_exit\n");
+
+	omap_dss_unregister_ctrl(&blizzard_ctrl);
+}
+
+module_init(blizzard_init);
+module_exit(blizzard_exit);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
+MODULE_DESCRIPTION("Blizzard Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-n800.c b/drivers/video/omap2/displays/panel-n800.c
new file mode 100644
index 0000000..91d3e37
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-n800.c
@@ -0,0 +1,435 @@
+
+/*#define DEBUG*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+
+#include <mach/display.h>
+#include <mach/dma.h>
+
+#define MIPID_CMD_READ_DISP_ID		0x04
+#define MIPID_CMD_READ_RED		0x06
+#define MIPID_CMD_READ_GREEN		0x07
+#define MIPID_CMD_READ_BLUE		0x08
+#define MIPID_CMD_READ_DISP_STATUS	0x09
+#define MIPID_CMD_RDDSDR		0x0F
+#define MIPID_CMD_SLEEP_IN		0x10
+#define MIPID_CMD_SLEEP_OUT		0x11
+#define MIPID_CMD_DISP_OFF		0x28
+#define MIPID_CMD_DISP_ON		0x29
+
+#define MIPID_VER_LPH8923		3
+#define MIPID_VER_LS041Y3		4
+
+#define MIPID_ESD_CHECK_PERIOD		msecs_to_jiffies(5000)
+
+#ifdef DEBUG
+#define DBG(format, ...) printk(KERN_DEBUG "PN800: " format, ## __VA_ARGS__)
+#else
+#define DBG(format, ...)
+#endif
+
+struct pn800_device {
+	struct backlight_device *bl_dev;
+	int		enabled;
+	int		model;
+	int		revision;
+	u8		display_id[3];
+	unsigned int	saved_bklight_level;
+	unsigned long	hw_guard_end;		/* next value of jiffies
+						   when we can issue the
+						   next sleep in/out command */
+	unsigned long	hw_guard_wait;		/* max guard time in jiffies */
+
+	struct spi_device	*spi;
+	struct mutex		mutex;
+	struct omap_panel	panel;
+	struct omap_display	*display;
+};
+
+
+static void pn800_transfer(struct pn800_device *md, int cmd,
+			      const u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+	struct spi_message	m;
+	struct spi_transfer	*x, xfer[4];
+	u16			w;
+	int			r;
+
+	BUG_ON(md->spi == NULL);
+
+	spi_message_init(&m);
+
+	memset(xfer, 0, sizeof(xfer));
+	x = &xfer[0];
+
+	cmd &=  0xff;
+	x->tx_buf = &cmd;
+	x->bits_per_word = 9;
+	x->len = 2;
+	spi_message_add_tail(x, &m);
+
+	if (wlen) {
+		x++;
+		x->tx_buf = wbuf;
+		x->len = wlen;
+		x->bits_per_word = 9;
+		spi_message_add_tail(x, &m);
+	}
+
+	if (rlen) {
+		x++;
+		x->rx_buf = &w;
+		x->len = 1;
+		spi_message_add_tail(x, &m);
+
+		if (rlen > 1) {
+			/* Arrange for the extra clock before the first
+			 * data bit.
+			 */
+			x->bits_per_word = 9;
+			x->len		 = 2;
+
+			x++;
+			x->rx_buf	 = &rbuf[1];
+			x->len		 = rlen - 1;
+			spi_message_add_tail(x, &m);
+		}
+	}
+
+	r = spi_sync(md->spi, &m);
+	if (r < 0)
+		dev_dbg(&md->spi->dev, "spi_sync %d\n", r);
+
+	if (rlen)
+		rbuf[0] = w & 0xff;
+}
+
+static inline void pn800_cmd(struct pn800_device *md, int cmd)
+{
+	pn800_transfer(md, cmd, NULL, 0, NULL, 0);
+}
+
+static inline void pn800_write(struct pn800_device *md,
+			       int reg, const u8 *buf, int len)
+{
+	pn800_transfer(md, reg, buf, len, NULL, 0);
+}
+
+static inline void pn800_read(struct pn800_device *md,
+			      int reg, u8 *buf, int len)
+{
+	pn800_transfer(md, reg, NULL, 0, buf, len);
+}
+
+static void set_data_lines(struct pn800_device *md, int data_lines)
+{
+	u16 par;
+
+	switch (data_lines) {
+	case 16:
+		par = 0x150;
+		break;
+	case 18:
+		par = 0x160;
+		break;
+	case 24:
+		par = 0x170;
+		break;
+	}
+	pn800_write(md, 0x3a, (u8 *)&par, 2);
+}
+
+static void send_init_string(struct pn800_device *md)
+{
+	u16 initpar[] = { 0x0102, 0x0100, 0x0100 };
+	int data_lines;
+
+	pn800_write(md, 0xc2, (u8 *)initpar, sizeof(initpar));
+
+	data_lines = (int)md->display->hw_config.panel_data; // XXX
+
+	set_data_lines(md, data_lines);
+}
+
+static void hw_guard_start(struct pn800_device *md, int guard_msec)
+{
+	md->hw_guard_wait = msecs_to_jiffies(guard_msec);
+	md->hw_guard_end = jiffies + md->hw_guard_wait;
+}
+
+static void hw_guard_wait(struct pn800_device *md)
+{
+	unsigned long wait = md->hw_guard_end - jiffies;
+
+	if ((long)wait > 0 && wait <= md->hw_guard_wait) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(wait);
+	}
+}
+
+static void set_sleep_mode(struct pn800_device *md, int on)
+{
+	int cmd, sleep_time = 50;
+
+	if (on)
+		cmd = MIPID_CMD_SLEEP_IN;
+	else
+		cmd = MIPID_CMD_SLEEP_OUT;
+	hw_guard_wait(md);
+	pn800_cmd(md, cmd);
+	hw_guard_start(md, 120);
+	/*
+	 * When we enable the panel, it seems we _have_ to sleep
+	 * 120 ms before sending the init string. When disabling the
+	 * panel we'll sleep for the duration of 2 frames, so that the
+	 * controller can still provide the PCLK,HS,VS signals. */
+	if (!on)
+		sleep_time = 120;
+	msleep(sleep_time);
+}
+
+static void set_display_state(struct pn800_device *md, int enabled)
+{
+	int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF;
+
+	pn800_cmd(md, cmd);
+}
+
+static int panel_enabled(struct pn800_device *md)
+{
+	u32 disp_status;
+	int enabled;
+
+	pn800_read(md, MIPID_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4);
+	disp_status = __be32_to_cpu(disp_status);
+	enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10));
+	dev_dbg(&md->spi->dev,
+		"LCD panel %s enabled by bootloader (status 0x%04x)\n",
+		enabled ? "" : "not ", disp_status);
+	DBG("status %#08x\n", disp_status);
+	return enabled;
+}
+
+static int panel_detect(struct pn800_device *md)
+{
+	pn800_read(md, MIPID_CMD_READ_DISP_ID, md->display_id, 3);
+	dev_dbg(&md->spi->dev, "MIPI display ID: %02x%02x%02x\n",
+		md->display_id[0], md->display_id[1], md->display_id[2]);
+
+	switch (md->display_id[0]) {
+	case 0x45:
+		md->model = MIPID_VER_LPH8923;
+		md->panel.name = "lph8923";
+		break;
+	case 0x83:
+		md->model = MIPID_VER_LS041Y3;
+		md->panel.name = "ls041y3";
+		//md->esd_check = ls041y3_esd_check;
+		break;
+	default:
+		md->panel.name = "unknown";
+		dev_err(&md->spi->dev, "invalid display ID\n");
+		return -ENODEV;
+	}
+
+	md->revision = md->display_id[1];
+	pr_info("omapfb: %s rev %02x LCD detected\n",
+			md->panel.name, md->revision);
+
+	return 0;
+}
+
+
+
+static int pn800_panel_enable(struct omap_display *display)
+{
+	int r;
+	struct pn800_device *md =
+		(struct pn800_device *)display->panel->priv;
+
+	DBG("pn800_panel_enable\n");
+
+	mutex_lock(&md->mutex);
+
+	if (display->hw_config.panel_enable)
+		display->hw_config.panel_enable(display);
+
+	msleep(50); // wait for power up
+
+	r = panel_detect(md);
+	if (r) {
+		mutex_unlock(&md->mutex);
+		return r;
+	}
+
+	md->enabled = panel_enabled(md);
+
+	if (md->enabled) {
+		DBG("panel already enabled\n");
+		; /*pn800_esd_start_check(md);*/
+	} else {
+		; /*md->saved_bklight_level = pn800_get_bklight_level(panel);*/
+	}
+
+
+	if (md->enabled) {
+		mutex_unlock(&md->mutex);
+		return 0;
+	}
+
+	set_sleep_mode(md, 0);
+	md->enabled = 1;
+	send_init_string(md);
+	set_display_state(md, 1);
+	//mipid_set_bklight_level(panel, md->saved_bklight_level);
+	//mipid_esd_start_check(md);
+
+	mutex_unlock(&md->mutex);
+	return 0;
+}
+
+static void pn800_panel_disable(struct omap_display *display)
+{
+	struct pn800_device *md =
+		(struct pn800_device *)display->panel->priv;
+
+	DBG("pn800_panel_disable\n");
+
+	mutex_lock(&md->mutex);
+
+	if (!md->enabled) {
+		mutex_unlock(&md->mutex);
+		return;
+	}
+	/*md->saved_bklight_level = pn800_get_bklight_level(panel);*/
+	/*pn800_set_bklight_level(panel, 0);*/
+
+	set_display_state(md, 0);
+	set_sleep_mode(md, 1);
+	md->enabled = 0;
+
+
+	if (display->hw_config.panel_disable)
+		display->hw_config.panel_disable(display);
+
+	mutex_unlock(&md->mutex);
+}
+
+static int pn800_panel_init(struct omap_display *display)
+{
+	struct pn800_device *md =
+		(struct pn800_device *)display->panel->priv;
+
+	DBG("pn800_panel_init\n");
+
+	mutex_init(&md->mutex);
+	md->display = display;
+
+	return 0;
+}
+
+static int pn800_run_test(struct omap_display *display, int test_num)
+{
+	return 0;
+}
+
+static struct omap_panel pn800_panel = {
+	.owner		= THIS_MODULE,
+	.name		= "panel-pn800",
+	.init		= pn800_panel_init,
+	/*.remove	= pn800_cleanup,*/
+	.enable		= pn800_panel_enable,
+	.disable	= pn800_panel_disable,
+	//.set_mode	= pn800_set_mode,
+	.run_test	= pn800_run_test,
+
+	.timings = {
+		.x_res = 800,
+		.y_res = 480,
+
+		.pixel_clock	= 21940,
+		.hsw		= 50,
+		.hfp		= 20,
+		.hbp		= 15,
+
+		.vsw		= 2,
+		.vfp		= 1,
+		.vbp		= 3,
+	},
+	.config		= OMAP_DSS_LCD_TFT,
+};
+
+static int pn800_spi_probe(struct spi_device *spi)
+{
+	struct pn800_device *md;
+
+	DBG("pn800_spi_probe\n");
+
+	md = kzalloc(sizeof(*md), GFP_KERNEL);
+	if (md == NULL) {
+		dev_err(&spi->dev, "out of memory\n");
+		return -ENOMEM;
+	}
+
+	spi->mode = SPI_MODE_0;
+	md->spi = spi;
+	dev_set_drvdata(&spi->dev, md);
+	md->panel = pn800_panel;
+	pn800_panel.priv = md;
+
+	omap_dss_register_panel(&pn800_panel);
+
+	return 0;
+}
+
+static int pn800_spi_remove(struct spi_device *spi)
+{
+	struct pn800_device *md = dev_get_drvdata(&spi->dev);
+
+	DBG("pn800_spi_remove\n");
+
+	omap_dss_unregister_panel(&pn800_panel);
+
+	/*pn800_disable(&md->panel);*/
+	kfree(md);
+
+	return 0;
+}
+
+static struct spi_driver pn800_spi_driver = {
+	.driver = {
+		.name	= "panel-n800",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe	= pn800_spi_probe,
+	.remove	= __devexit_p(pn800_spi_remove),
+};
+
+static int __init pn800_init(void)
+{
+	DBG("pn800_init\n");
+	return spi_register_driver(&pn800_spi_driver);
+}
+
+static void __exit pn800_exit(void)
+{
+	DBG("pn800_exit\n");
+	spi_unregister_driver(&pn800_spi_driver);
+}
+
+module_init(pn800_init);
+module_exit(pn800_exit);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
+MODULE_DESCRIPTION("N800 LCD Driver");
+MODULE_LICENSE("GPL");
-- 
1.5.6.5

