Signed-off-by: Tomi Valkeinen <tomi.valkeinen@xxxxxxxxx> --- arch/arm/mach-omap2/board-n800.c | 214 ++++++++++++++--- drivers/video/omap2/Kconfig | 10 + drivers/video/omap2/Makefile | 3 drivers/video/omap2/ctrl-blizzard.c | 279 ++++++++++++++++++++++ drivers/video/omap2/panel-n800.c | 437 +++++++++++++++++++++++++++++++++++ 5 files changed, 905 insertions(+), 38 deletions(-) create mode 100644 drivers/video/omap2/ctrl-blizzard.c create mode 100644 drivers/video/omap2/panel-n800.c diff --git a/arch/arm/mach-omap2/board-n800.c b/arch/arm/mach-omap2/board-n800.c index b38b295..ffa5aad 100644 --- a/arch/arm/mach-omap2/board-n800.c +++ b/arch/arm/mach-omap2/board-n800.c @@ -40,6 +40,7 @@ #include <mach/gpio-switch.h> #include <mach/omapfb.h> #include <mach/blizzard.h> +#include <mach/display.h> #include <../drivers/cbus/tahvo.h> #include <../drivers/media/video/tcm825x.h> @@ -156,23 +157,175 @@ 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_display_data 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, + }, + .priv = 0, // XXX used for panel datalines +}; +static struct omap_dss_platform_data 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 = "omap-dss", + .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.priv = (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) { @@ -186,6 +339,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; @@ -196,26 +350,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) @@ -260,14 +397,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 }, }; @@ -374,7 +509,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, @@ -399,7 +534,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, @@ -567,6 +702,7 @@ static struct platform_device *n800_devices[] __initdata = { #if defined(CONFIG_CBUS_RETU) && defined(CONFIG_LEDS_OMAP_PWM) &n800_keypad_led_device, #endif + &n800_dss_device, }; #ifdef CONFIG_MENELAUS @@ -689,9 +825,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) @@ -712,6 +849,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/Kconfig b/drivers/video/omap2/Kconfig index b54c955..4e9211e 100644 --- a/drivers/video/omap2/Kconfig +++ b/drivers/video/omap2/Kconfig @@ -49,4 +49,14 @@ config PANEL_SHARP_LS037V7DW01 help LCD Panel used in TI's SDP3430 and EVM boards +config PANEL_N800 + tristate "panel n800" + help + N800 LCD + +config CTRL_BLIZZARD + tristate "blizzard ctrl" + help + Blizzard Ctrl + endmenu diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile index fe6858e..7727f9c 100644 --- a/drivers/video/omap2/Makefile +++ b/drivers/video/omap2/Makefile @@ -3,3 +3,6 @@ omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o obj-$(CONFIG_PANEL_GENERIC) += panel-generic.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/ctrl-blizzard.c b/drivers/video/omap2/ctrl-blizzard.c new file mode 100644 index 0000000..e1e5569 --- /dev/null +++ b/drivers/video/omap2/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, + int x, int y, int w, int 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, int enable) +{ + return 0; +} + +static int blizzard_ctrl_rotate(struct omap_display *display, int rotate) +{ + return 0; +} + +static int blizzard_ctrl_mirror(struct omap_display *display, int 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, + .rotate = blizzard_ctrl_rotate, + .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@xxxxxxxxx>"); +MODULE_DESCRIPTION("Blizzard Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/panel-n800.c b/drivers/video/omap2/panel-n800.c new file mode 100644 index 0000000..3ae0a16 --- /dev/null +++ b/drivers/video/omap2/panel-n800.c @@ -0,0 +1,437 @@ + +/*#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.priv; // 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, + + .bpp = 16, +}; + +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@xxxxxxxxx>"); +MODULE_DESCRIPTION("N800 LCD Driver"); +MODULE_LICENSE("GPL"); -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html