]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
FB: Add support for OMAP framebuffer
authorImre Deak <imre.deak@solidboot.com>
Tue, 3 Apr 2007 18:15:01 +0000 (14:15 -0400)
committerTony Lindgren <tony@atomide.com>
Mon, 16 Apr 2007 20:54:54 +0000 (20:54 +0000)
Add support for OMAP framebuffer

Signed-off-by: Imre Deak <imre.deak@solidboot.com>
28 files changed:
drivers/video/Kconfig
drivers/video/Makefile
drivers/video/omap/Kconfig [new file with mode: 0644]
drivers/video/omap/Makefile [new file with mode: 0644]
drivers/video/omap/blizzard.c [new file with mode: 0644]
drivers/video/omap/dispc.c [new file with mode: 0644]
drivers/video/omap/dispc.h [new file with mode: 0644]
drivers/video/omap/hwa742.c [new file with mode: 0644]
drivers/video/omap/lcd_ams_delta.c [new file with mode: 0644]
drivers/video/omap/lcd_apollon.c [new file with mode: 0644]
drivers/video/omap/lcd_h2.c [new file with mode: 0644]
drivers/video/omap/lcd_h3.c [new file with mode: 0644]
drivers/video/omap/lcd_h4.c [new file with mode: 0644]
drivers/video/omap/lcd_inn1510.c [new file with mode: 0644]
drivers/video/omap/lcd_inn1610.c [new file with mode: 0644]
drivers/video/omap/lcd_mipid.c [new file with mode: 0644]
drivers/video/omap/lcd_osk.c [new file with mode: 0644]
drivers/video/omap/lcd_p2.c [new file with mode: 0644]
drivers/video/omap/lcd_palmte.c [new file with mode: 0644]
drivers/video/omap/lcd_palmtt.c [new file with mode: 0644]
drivers/video/omap/lcd_palmz71.c [new file with mode: 0644]
drivers/video/omap/lcd_sx1.c [new file with mode: 0644]
drivers/video/omap/lcdc.c [new file with mode: 0644]
drivers/video/omap/lcdc.h [new file with mode: 0644]
drivers/video/omap/omapfb_main.c [new file with mode: 0644]
drivers/video/omap/rfbi.c [new file with mode: 0644]
drivers/video/omap/sossi.c [new file with mode: 0644]
include/linux/fb.h

index e4f0dd00ae85a51098a02ef5e3acdba79c93f640..d4704faa820d2a3aad6a08e280216e6563896db1 100644 (file)
@@ -1633,6 +1633,10 @@ config FB_PS3_DEFAULT_SIZE_M
          The default value can be overridden on the kernel command line
          using the "ps3fb" option (e.g. "ps3fb=9M");
 
+if ARCH_OMAP
+       source "drivers/video/omap/Kconfig"
+endif
+
 config FB_VIRTUAL
        tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
        depends on FB
index 760305c8a8416736cb61d005bccf5459c8728e4f..705c5b5d5d394b38a621da0fc066780bde3ed211 100644 (file)
@@ -105,6 +105,7 @@ obj-$(CONFIG_FB_VESA)             += vesafb.o
 obj-$(CONFIG_FB_IMAC)             += imacfb.o
 obj-$(CONFIG_FB_VGA16)            += vga16fb.o vgastate.o
 obj-$(CONFIG_FB_OF)               += offb.o
+obj-$(CONFIG_FB_OMAP)             += omap/ cfbcopyarea.o cfbfillrect.o cfbimgblt.o
 
 # the test framebuffer is last
 obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig
new file mode 100644 (file)
index 0000000..78123a2
--- /dev/null
@@ -0,0 +1,75 @@
+config FB_OMAP
+       tristate "OMAP frame buffer support (EXPERIMENTAL)"
+        depends on FB
+        help
+          Frame buffer driver for OMAP based boards.
+
+config FB_OMAP_LCDC_EXTERNAL
+       bool "External LCD controller support"
+       depends on FB_OMAP
+       help
+         Say Y here, if you want to have support for boards with an
+         external LCD controller connected to the SoSSI/RFBI interface.
+
+config FB_OMAP_LCDC_HWA742
+       bool "Epson HWA742 LCD controller support"
+       depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL
+       help
+         Say Y here if you want to have support for the external
+         Epson HWA742 LCD controller.
+
+config FB_OMAP_LCDC_BLIZZARD
+       bool "Epson Blizzard LCD controller support"
+       depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL
+       help
+         Say Y here if you want to have support for the external
+         Epson Blizzard LCD controller.
+
+config FB_OMAP_MANUAL_UPDATE
+       bool "Default to manual update mode"
+       depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL
+       help
+         Say Y here, if your user-space applications are capable of
+         notifying the frame buffer driver when a change has occured in
+          the frame buffer content and thus a reload of the image data to
+         the external frame buffer is required. If unsure, say N.
+
+config FB_OMAP_LCD_MIPID
+       bool "MIPI DBI-C/DCS compatible LCD support"
+       select SPI
+       depends on FB_OMAP
+       help
+         Say Y here if you want to have support for LCDs compatible with
+         the Mobile Industry Processor Interface DBI-C/DCS
+         specification. (Supported LCDs: Philips LPH8923, Sharp LS041Y3)
+
+config FB_OMAP_BOOTLOADER_INIT
+       bool "Check bootloader initializaion"
+       depends on FB_OMAP
+       help
+         Say Y here if you want to enable checking if the bootloader has
+         already initialized the display controller. In this case the
+         driver will skip the initialization.
+
+config FB_OMAP_CONSISTENT_DMA_SIZE
+       int "Consistent DMA memory size (MB)"
+       depends on FB_OMAP
+       range 1 14
+       default 2
+       help
+         Increase the DMA consistent memory size according to your video
+         memory needs, for example if you want to use multiple planes.
+         The size must be 2MB aligned.
+         If unsure say 1.
+
+config FB_OMAP_DMA_TUNE
+        bool "Set DMA SDRAM access priority high"
+        depends on FB_OMAP && ARCH_OMAP1
+        help
+          On systems in which video memory is in system memory
+          (SDRAM) this will speed up graphics DMA operations.
+          If you have such a system and want to use rotation
+          answer yes. Answer no if you have a dedicated video
+          memory, or don't use any of the accelerated features.
+
+
diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile
new file mode 100644 (file)
index 0000000..be25801
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Makefile for the new OMAP framebuffer device driver
+#
+
+obj-$(CONFIG_FB_OMAP) += omapfb.o
+
+objs-yy := omapfb_main.o
+
+objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o
+objs-y$(CONFIG_ARCH_OMAP2) += dispc.o
+
+objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o
+objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o
+
+objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o
+objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o
+
+objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o
+objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o
+objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o
+objs-y$(CONFIG_MACH_OMAP_H2) += lcd_h2.o
+objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o
+objs-y$(CONFIG_MACH_OMAP_PALMZ71) += lcd_palmz71.o
+objs-y$(CONFIG_MACH_OMAP_PALMTT) += lcd_palmtt.o
+objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o
+objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o
+objs-y$(CONFIG_MACH_SX1) += lcd_sx1.o
+objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o
+objs-y$(CONFIG_MACH_OMAP_PERSEUS2) += lcd_p2.o
+objs-y$(CONFIG_MACH_OMAP_APOLLON) += lcd_apollon.o
+
+objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o
+
+omapfb-objs := $(objs-yy)
+
diff --git a/drivers/video/omap/blizzard.c b/drivers/video/omap/blizzard.c
new file mode 100644 (file)
index 0000000..7905d13
--- /dev/null
@@ -0,0 +1,1570 @@
+/*
+ * File: drivers/video/omap/blizzard.c
+ *
+ * Epson Blizzard LCD controller driver
+ *
+ * Copyright (C) 2004-2005 Nokia Corporation
+ * Authors:     Juha Yrjola   <juha.yrjola@nokia.com>
+ *             Imre Deak     <imre.deak@nokia.com>
+ * YUV support: Jussi Laako   <jussi.laako@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <asm/arch/dma.h>
+#include <asm/arch/omapfb.h>
+#include <asm/arch/blizzard.h>
+
+#include "dispc.h"
+
+#define MODULE_NAME                            "blizzard"
+
+#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_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)
+
+/* Reserve 4 request slots for requests in irq context */
+#define REQ_POOL_SIZE                  24
+#define IRQ_REQ_POOL_SIZE              4
+
+#define REQ_FROM_IRQ_POOL 0x01
+
+#define REQ_COMPLETE   0
+#define REQ_PENDING    1
+
+struct blizzard_reg_list {
+       int     start;
+       int     end;
+};
+
+/* These need to be saved / restored separately from the rest. */
+static struct blizzard_reg_list blizzard_pll_regs[] = {
+       {
+               .start  = 0x04,         /* Don't save PLL ctrl (0x0C) */
+               .end    = 0x0a,
+       },
+       {
+               .start  = 0x0e,         /* Clock configuration */
+               .end    = 0x0e,
+       },
+};
+
+static struct blizzard_reg_list blizzard_gen_regs[] = {
+       {
+               .start  = 0x18,         /* SDRAM control */
+               .end    = 0x20,
+       },
+       {
+               .start  = 0x28,         /* LCD Panel configuration */
+               .end    = 0x5a,         /* HSSI interface, TV configuration */
+       },
+};
+
+static u8 blizzard_reg_cache[0x5a / 2];
+
+struct update_param {
+       int     plane;
+       int     x, y, width, height;
+       int     out_x, out_y;
+       int     out_width, out_height;
+       int     color_mode;
+       int     bpp;
+       int     flags;
+};
+
+struct blizzard_request {
+       struct list_head entry;
+       unsigned int     flags;
+
+       int              (*handler)(struct blizzard_request *req);
+       void             (*complete)(void *data);
+       void             *complete_data;
+
+       union {
+               struct update_param     update;
+               struct completion       *sync;
+       } par;
+};
+
+struct plane_info {
+       unsigned long offset;
+       int pos_x, pos_y;
+       int width, height;
+       int out_width, out_height;
+       int scr_width;
+       int color_mode;
+       int bpp;
+};
+
+struct blizzard_struct {
+       enum omapfb_update_mode update_mode;
+       enum omapfb_update_mode update_mode_before_suspend;
+
+       struct timer_list       auto_update_timer;
+       int                     stop_auto_update;
+       struct omapfb_update_window     auto_update_window;
+       int                     enabled_planes;
+       int                     vid_nonstd_color;
+       int                     vid_scaled;
+       int                     last_color_mode;
+       int                     zoom_on;
+       int                     screen_width;
+       int                     screen_height;
+       unsigned                te_connected:1;
+       unsigned                vsync_only:1;
+
+       struct plane_info       plane[OMAPFB_PLANE_NUM];
+
+       struct blizzard_request req_pool[REQ_POOL_SIZE];
+       struct list_head        pending_req_list;
+       struct list_head        free_req_list;
+       struct semaphore        req_sema;
+       spinlock_t              req_lock;
+
+       unsigned long           sys_ck_rate;
+       struct extif_timings    reg_timings, lut_timings;
+
+       u32                     max_transmit_size;
+       u32                     extif_clk_period;
+       int                     extif_clk_div;
+       unsigned long           pix_tx_time;
+       unsigned long           line_upd_time;
+
+       struct omapfb_device    *fbdev;
+       struct lcd_ctrl_extif   *extif;
+       struct lcd_ctrl         *int_ctrl;
+
+       void                    (*power_up)(struct device *dev);
+       void                    (*power_down)(struct device *dev);
+
+       int                     version;
+} blizzard;
+
+struct lcd_ctrl blizzard_ctrl;
+
+static u8 blizzard_read_reg(u8 reg)
+{
+       u8 data;
+
+       blizzard.extif->set_bits_per_cycle(8);
+       blizzard.extif->write_command(&reg, 1);
+       blizzard.extif->read_data(&data, 1);
+
+       return data;
+}
+
+static void blizzard_write_reg(u8 reg, u8 val)
+{
+       blizzard.extif->set_bits_per_cycle(8);
+       blizzard.extif->write_command(&reg, 1);
+       blizzard.extif->write_data(&val, 1);
+}
+
+static void blizzard_restart_sdram(void)
+{
+       unsigned long tmo;
+
+       blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0);
+       udelay(50);
+       blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 1);
+       tmo = jiffies + msecs_to_jiffies(200);
+       while (!(blizzard_read_reg(BLIZZARD_MEM_BANK0_STATUS) & 0x01)) {
+               if (time_after(jiffies, tmo)) {
+                       dev_err(blizzard.fbdev->dev,
+                                       "s1d1374x: SDRAM not ready");
+                       break;
+               }
+               msleep(1);
+       }
+}
+
+static void blizzard_stop_sdram(void)
+{
+       blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0);
+}
+
+/* Wait until the last window was completely written into the controllers
+ * SDRAM and we can start transferring the next window.
+ */
+static void blizzard_wait_line_buffer(void)
+{
+       unsigned long tmo = jiffies + msecs_to_jiffies(30);
+
+       while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 7)) {
+               if (time_after(jiffies, tmo)) {
+                       if (printk_ratelimit())
+                               dev_err(blizzard.fbdev->dev,
+                                       "s1d1374x: line buffer not ready\n");
+                       break;
+               }
+       }
+}
+
+/* Wait until the YYC color space converter is idle. */
+static void blizzard_wait_yyc(void)
+{
+       unsigned long tmo = jiffies + msecs_to_jiffies(30);
+
+       while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 4)) {
+               if (time_after(jiffies, tmo)) {
+                       if (printk_ratelimit())
+                               dev_err(blizzard.fbdev->dev,
+                                       "s1d1374x: YYC not ready\n");
+                       break;
+               }
+       }
+}
+
+static void disable_overlay(void)
+{
+       blizzard_write_reg(BLIZZARD_DATA_SOURCE_SELECT,
+                               BLIZZARD_SRC_DISABLE_OVERLAY);
+}
+
+static void set_window_regs(int x_start, int y_start, int x_end, int y_end,
+                           int x_out_start, int y_out_start,
+                           int x_out_end, int y_out_end, int color_mode,
+                           int zoom_off, int flags)
+{
+       u8 tmp[18];
+       u8 cmd;
+
+       x_end--;
+       y_end--;
+       tmp[0] = x_start;
+       tmp[1] = x_start >> 8;
+       tmp[2] = y_start;
+       tmp[3] = y_start >> 8;
+       tmp[4] = x_end;
+       tmp[5] = x_end >> 8;
+       tmp[6] = y_end;
+       tmp[7] = y_end >> 8;
+
+       x_out_end--;
+       y_out_end--;
+       tmp[8]  = x_out_start;
+       tmp[9]  = x_out_start >> 8;
+       tmp[10] = y_out_start;
+       tmp[11] = y_out_start >> 8;
+       tmp[12] = x_out_end;
+       tmp[13] = x_out_end >> 8;
+       tmp[14] = y_out_end;
+       tmp[15] = y_out_end >> 8;
+
+       tmp[16] = color_mode;
+       if (zoom_off && blizzard.version == BLIZZARD_VERSION_S1D13745)
+               tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND;
+       else if (flags & OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY)
+               tmp[17] = BLIZZARD_SRC_WRITE_OVERLAY_ENABLE;
+       else
+               tmp[17] = blizzard.version == BLIZZARD_VERSION_S1D13744 ?
+                               BLIZZARD_SRC_WRITE_LCD :
+                               BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE;
+
+       blizzard.extif->set_bits_per_cycle(8);
+       cmd = BLIZZARD_INPUT_WIN_X_START_0;
+       blizzard.extif->write_command(&cmd, 1);
+       blizzard.extif->write_data(tmp, 18);
+}
+
+static void enable_tearsync(int y, int width, int height, int screen_height,
+                           int out_height, int force_vsync)
+{
+       u8 b;
+
+       b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS);
+       b |= 1 << 3;
+       blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b);
+
+       if (likely(blizzard.vsync_only || force_vsync)) {
+               blizzard.extif->enable_tearsync(1, 0);
+               return;
+       }
+
+       if (width * blizzard.pix_tx_time < blizzard.line_upd_time) {
+               blizzard.extif->enable_tearsync(1, 0);
+               return;
+       }
+
+       if ((width * blizzard.pix_tx_time / 1000) * height <
+           (y + out_height) * (blizzard.line_upd_time / 1000)) {
+               blizzard.extif->enable_tearsync(1, 0);
+               return;
+       }
+
+       blizzard.extif->enable_tearsync(1, y + 1);
+}
+
+static void disable_tearsync(void)
+{
+       u8 b;
+
+       blizzard.extif->enable_tearsync(0, 0);
+       b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS);
+       b &= ~(1 << 3);
+       blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b);
+       b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS);
+}
+
+static inline void set_extif_timings(const struct extif_timings *t);
+
+static inline struct blizzard_request *alloc_req(void)
+{
+       unsigned long flags;
+       struct blizzard_request *req;
+       int req_flags = 0;
+
+       if (!in_interrupt())
+               down(&blizzard.req_sema);
+       else
+               req_flags = REQ_FROM_IRQ_POOL;
+
+       spin_lock_irqsave(&blizzard.req_lock, flags);
+       BUG_ON(list_empty(&blizzard.free_req_list));
+       req = list_entry(blizzard.free_req_list.next,
+                        struct blizzard_request, entry);
+       list_del(&req->entry);
+       spin_unlock_irqrestore(&blizzard.req_lock, flags);
+
+       INIT_LIST_HEAD(&req->entry);
+       req->flags = req_flags;
+
+       return req;
+}
+
+static inline void free_req(struct blizzard_request *req)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&blizzard.req_lock, flags);
+
+       list_del(&req->entry);
+       list_add(&req->entry, &blizzard.free_req_list);
+       if (!(req->flags & REQ_FROM_IRQ_POOL))
+               up(&blizzard.req_sema);
+
+       spin_unlock_irqrestore(&blizzard.req_lock, flags);
+}
+
+static void process_pending_requests(void)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&blizzard.req_lock, flags);
+
+       while (!list_empty(&blizzard.pending_req_list)) {
+               struct blizzard_request *req;
+               void (*complete)(void *);
+               void *complete_data;
+
+               req = list_entry(blizzard.pending_req_list.next,
+                                struct blizzard_request, entry);
+               spin_unlock_irqrestore(&blizzard.req_lock, flags);
+
+               if (req->handler(req) == REQ_PENDING)
+                       return;
+
+               complete = req->complete;
+               complete_data = req->complete_data;
+               free_req(req);
+
+               if (complete)
+                       complete(complete_data);
+
+               spin_lock_irqsave(&blizzard.req_lock, flags);
+       }
+
+       spin_unlock_irqrestore(&blizzard.req_lock, flags);
+}
+
+static void submit_req_list(struct list_head *head)
+{
+       unsigned long flags;
+       int process = 1;
+
+       spin_lock_irqsave(&blizzard.req_lock, flags);
+       if (likely(!list_empty(&blizzard.pending_req_list)))
+               process = 0;
+       list_splice_init(head, blizzard.pending_req_list.prev);
+       spin_unlock_irqrestore(&blizzard.req_lock, flags);
+
+       if (process)
+               process_pending_requests();
+}
+
+static void request_complete(void *data)
+{
+       struct blizzard_request *req = (struct blizzard_request *)data;
+       void                    (*complete)(void *);
+       void                    *complete_data;
+
+       complete = req->complete;
+       complete_data = req->complete_data;
+
+       free_req(req);
+
+       if (complete)
+               complete(complete_data);
+
+       process_pending_requests();
+}
+
+
+static int do_full_screen_update(struct blizzard_request *req)
+{
+       int i;
+       int flags;
+
+       for (i = 0; i < 3; i++) {
+               struct plane_info *p = &blizzard.plane[i];
+               if (!(blizzard.enabled_planes & (1 << i))) {
+                       blizzard.int_ctrl->enable_plane(i, 0);
+                       continue;
+               }
+               dev_dbg(blizzard.fbdev->dev, "pw %d ph %d\n",
+                       p->width, p->height);
+               blizzard.int_ctrl->setup_plane(i,
+                               OMAPFB_CHANNEL_OUT_LCD, p->offset,
+                               p->scr_width, p->pos_x, p->pos_y,
+                               p->width, p->height,
+                               p->color_mode);
+               blizzard.int_ctrl->enable_plane(i, 1);
+       }
+
+       dev_dbg(blizzard.fbdev->dev, "sw %d sh %d\n",
+               blizzard.screen_width, blizzard.screen_height);
+       blizzard_wait_line_buffer();
+       flags = req->par.update.flags;
+       if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC)
+               enable_tearsync(0, blizzard.screen_width,
+                               blizzard.screen_height,
+                               blizzard.screen_height,
+                               blizzard.screen_height,
+                               flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC);
+       else
+               disable_tearsync();
+
+       set_window_regs(0, 0, blizzard.screen_width, blizzard.screen_height,
+                       0, 0, blizzard.screen_width, blizzard.screen_height,
+                       BLIZZARD_COLOR_RGB565, blizzard.zoom_on, flags);
+       blizzard.zoom_on = 0;
+
+       blizzard.extif->set_bits_per_cycle(16);
+       /* set_window_regs has left the register index at the right
+        * place, so no need to set it here.
+        */
+       blizzard.extif->transfer_area(blizzard.screen_width,
+                                     blizzard.screen_height,
+                                     request_complete, req);
+       return REQ_PENDING;
+}
+
+/* Setup all planes with an overlapping area with the update window. */
+static int do_partial_update(struct blizzard_request *req, int plane,
+                            int x, int y, int w, int h,
+                            int x_out, int y_out, int w_out, int h_out,
+                            int wnd_color_mode, int bpp)
+{
+       int i;
+       int gx1, gy1, gx2, gy2;
+       int gx1_out, gy1_out, gx2_out, gy2_out;
+       int color_mode;
+       int flags;
+       int zoom_off;
+
+       /* Global coordinates, relative to pixel 0,0 of the LCD */
+       gx1 = x + blizzard.plane[plane].pos_x;
+       gy1 = y + blizzard.plane[plane].pos_y;
+       gx2 = gx1 + w;
+       gy2 = gy1 + h;
+
+       flags = req->par.update.flags;
+       if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) {
+               gx1_out = gx1;
+               gy1_out = gy1;
+               gx2_out = gx1 + w * 2;
+               gy2_out = gy1 + h * 2;
+       } else {
+               gx1_out = x_out + blizzard.plane[plane].pos_x;
+               gy1_out = y_out + blizzard.plane[plane].pos_y;
+               gx2_out = gx1_out + w_out;
+               gy2_out = gy1_out + h_out;
+       }
+       zoom_off = blizzard.zoom_on && gx1 == 0 && gy1 == 0 &&
+               w == blizzard.screen_width && h == blizzard.screen_height;
+       blizzard.zoom_on = (!zoom_off && blizzard.zoom_on) ||
+                          (w < w_out || h < h_out);
+
+       for (i = 0; i < OMAPFB_PLANE_NUM; i++) {
+               struct plane_info *p = &blizzard.plane[i];
+               int px1, py1;
+               int px2, py2;
+               int pw, ph;
+               int pposx, pposy;
+               unsigned long offset;
+
+               if (!(blizzard.enabled_planes & (1 << i))  ||
+                   (wnd_color_mode && i != plane)) {
+                       blizzard.int_ctrl->enable_plane(i, 0);
+                       continue;
+               }
+               /* Plane coordinates */
+               if (i == plane) {
+                       /* Plane in which we are doing the update.
+                        * Local coordinates are the one in the update
+                        * request.
+                        */
+                       px1 = x;
+                       py1 = y;
+                       px2 = x + w;
+                       py2 = y + h;
+                       pposx = 0;
+                       pposy = 0;
+               } else {
+                       /* Check if this plane has an overlapping part */
+                       px1 = gx1 - p->pos_x;
+                       py1 = gy1 - p->pos_y;
+                       px2 = gx2 - p->pos_x;
+                       py2 = gy2 - p->pos_y;
+                       if (px1 >= p->width || py1 >= p->height ||
+                           px2 <= 0 || py2 <= 0) {
+                               blizzard.int_ctrl->enable_plane(i, 0);
+                               continue;
+                       }
+                       /* Calculate the coordinates for the overlapping
+                        * part in the plane's local coordinates.
+                        */
+                       pposx = -px1;
+                       pposy = -py1;
+                       if (px1 < 0)
+                               px1 = 0;
+                       if (py1 < 0)
+                               py1 = 0;
+                       if (px2 > p->width)
+                               px2 = p->width;
+                       if (py2 > p->height)
+                               py2 = p->height;
+                       if (pposx < 0)
+                               pposx = 0;
+                       if (pposy < 0)
+                               pposy = 0;
+               }
+               pw = px2 - px1;
+               ph = py2 - py1;
+               offset = p->offset + (p->scr_width * py1 + px1) * p->bpp / 8;
+               if (wnd_color_mode)
+                       /* Window embedded in the plane with a differing
+                        * color mode / bpp. Calculate the number of DMA
+                        * transfer elements in terms of the plane's bpp.
+                        */
+                       pw = (pw + 1) * bpp / p->bpp;
+#ifdef VERBOSE
+               dev_dbg(blizzard.fbdev->dev,
+                       "plane %d offset %#08lx pposx %d pposy %d "
+                       "px1 %d py1 %d pw %d ph %d\n",
+                       i, offset, pposx, pposy, px1, py1, pw, ph);
+#endif
+               blizzard.int_ctrl->setup_plane(i,
+                               OMAPFB_CHANNEL_OUT_LCD, offset,
+                               p->scr_width,
+                               pposx, pposy, pw, ph,
+                               p->color_mode);
+
+               blizzard.int_ctrl->enable_plane(i, 1);
+       }
+
+       switch (wnd_color_mode) {
+       case OMAPFB_COLOR_YUV420:
+               color_mode = BLIZZARD_COLOR_YUV420;
+               /* Currently only the 16 bits/pixel cycle format is
+                * supported on the external interface. Adjust the number
+                * of transfer elements per line for 12bpp format.
+                */
+               w = (w + 1) * 3 / 4;
+               break;
+       default:
+               color_mode = BLIZZARD_COLOR_RGB565;
+               break;
+       }
+
+       blizzard_wait_line_buffer();
+       if (blizzard.last_color_mode == BLIZZARD_COLOR_YUV420)
+               blizzard_wait_yyc();
+       blizzard.last_color_mode = color_mode;
+       if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC)
+               enable_tearsync(gy1, w, h,
+                               blizzard.screen_height,
+                               h_out,
+                               flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC);
+       else
+               disable_tearsync();
+
+       set_window_regs(gx1, gy1, gx2, gy2, gx1_out, gy1_out, gx2_out, gy2_out,
+                       color_mode, zoom_off, flags);
+
+       blizzard.extif->set_bits_per_cycle(16);
+       /* set_window_regs has left the register index at the right
+        * place, so no need to set it here.
+        */
+       blizzard.extif->transfer_area(w, h, request_complete, req);
+
+       return REQ_PENDING;
+}
+
+static int send_frame_handler(struct blizzard_request *req)
+{
+       struct update_param *par = &req->par.update;
+       int plane = par->plane;
+
+#ifdef VERBOSE
+       dev_dbg(blizzard.fbdev->dev,
+               "send_frame: x %d y %d w %d h %d "
+               "x_out %d y_out %d w_out %d h_out %d "
+               "color_mode %04x flags %04x planes %01x\n",
+               par->x, par->y, par->width, par->height,
+               par->out_x, par->out_y, par->out_width, par->out_height,
+               par->color_mode, par->flags, blizzard.enabled_planes);
+#endif
+       if (par->flags & OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY)
+               disable_overlay();
+
+       if ((blizzard.enabled_planes & blizzard.vid_nonstd_color) ||
+            (blizzard.enabled_planes & blizzard.vid_scaled))
+               return do_full_screen_update(req);
+
+       return do_partial_update(req, plane, par->x, par->y,
+                                par->width, par->height,
+                                par->out_x, par->out_y,
+                                par->out_width, par->out_height,
+                                par->color_mode, par->bpp);
+}
+
+static void send_frame_complete(void *data)
+{
+}
+
+#define ADD_PREQ(_x, _y, _w, _h, _x_out, _y_out, _w_out, _h_out) do {  \
+       req = alloc_req();                      \
+       req->handler    = send_frame_handler;   \
+       req->complete   = send_frame_complete;  \
+       req->par.update.plane = plane_idx;      \
+       req->par.update.x = _x;                 \
+       req->par.update.y = _y;                 \
+       req->par.update.width  = _w;            \
+       req->par.update.height = _h;            \
+       req->par.update.out_x = _x_out;         \
+       req->par.update.out_y = _y_out;         \
+       req->par.update.out_width = _w_out;     \
+       req->par.update.out_height = _h_out;    \
+       req->par.update.bpp = bpp;              \
+       req->par.update.color_mode = color_mode;\
+       req->par.update.flags     = flags;      \
+       list_add_tail(&req->entry, req_head);   \
+} while(0)
+
+static void create_req_list(int plane_idx,
+                           struct omapfb_update_window *win,
+                           struct list_head *req_head)
+{
+       struct blizzard_request *req;
+       int x = win->x;
+       int y = win->y;
+       int width = win->width;
+       int height = win->height;
+       int x_out = win->out_x;
+       int y_out = win->out_y;
+       int width_out = win->out_width;
+       int height_out = win->out_height;
+       int color_mode;
+       int bpp;
+       int flags;
+       unsigned int ystart = y;
+       unsigned int yspan = height;
+       unsigned int ystart_out = y_out;
+       unsigned int yspan_out = height_out;
+
+       flags = win->format & ~OMAPFB_FORMAT_MASK;
+       color_mode = win->format & OMAPFB_FORMAT_MASK;
+       switch (color_mode) {
+       case OMAPFB_COLOR_YUV420:
+               /* Embedded window with different color mode */
+               bpp = 12;
+               /* X, Y, height must be aligned at 2, width at 4 pixels */
+               x &= ~1;
+               y &= ~1;
+               height = yspan = height & ~1;
+               width = width & ~3;
+               break;
+       default:
+               /* Same as the plane color mode */
+               bpp = blizzard.plane[plane_idx].bpp;
+               break;
+       }
+       if (width * height * bpp / 8 > blizzard.max_transmit_size) {
+               yspan = blizzard.max_transmit_size / (width * bpp / 8);
+               yspan_out = yspan * height_out / height;
+               ADD_PREQ(x, ystart, width, yspan, x_out, ystart_out,
+                        width_out, yspan_out);
+               ystart += yspan;
+               ystart_out += yspan_out;
+               yspan = height - yspan;
+               yspan_out = height_out - yspan_out;
+               flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC;
+       }
+
+       ADD_PREQ(x, ystart, width, yspan, x_out, ystart_out,
+                width_out, yspan_out);
+}
+
+static void auto_update_complete(void *data)
+{
+       if (!blizzard.stop_auto_update)
+               mod_timer(&blizzard.auto_update_timer,
+                         jiffies + BLIZZARD_AUTO_UPDATE_TIME);
+}
+
+static void blizzard_update_window_auto(unsigned long arg)
+{
+       LIST_HEAD(req_list);
+       struct blizzard_request *last;
+       struct omapfb_plane_struct *plane;
+
+       plane = blizzard.fbdev->fb_info[0]->par;
+       create_req_list(plane->idx,
+                       &blizzard.auto_update_window, &req_list);
+       last = list_entry(req_list.prev, struct blizzard_request, entry);
+
+       last->complete = auto_update_complete;
+       last->complete_data = NULL;
+
+       submit_req_list(&req_list);
+}
+
+int blizzard_update_window_async(struct fb_info *fbi,
+                                struct omapfb_update_window *win,
+                                void (*complete_callback)(void *arg),
+                                void *complete_callback_data)
+{
+       LIST_HEAD(req_list);
+       struct blizzard_request *last;
+       struct omapfb_plane_struct *plane = fbi->par;
+
+       if (unlikely(blizzard.update_mode != OMAPFB_MANUAL_UPDATE))
+               return -EINVAL;
+       if (unlikely(!blizzard.te_connected &&
+                    (win->format & OMAPFB_FORMAT_FLAG_TEARSYNC)))
+               return -EINVAL;
+
+       create_req_list(plane->idx, win, &req_list);
+       last = list_entry(req_list.prev, struct blizzard_request, entry);
+
+       last->complete = complete_callback;
+       last->complete_data = (void *)complete_callback_data;
+
+       submit_req_list(&req_list);
+
+       return 0;
+}
+EXPORT_SYMBOL(blizzard_update_window_async);
+
+static int update_full_screen(void)
+{
+       return blizzard_update_window_async(blizzard.fbdev->fb_info[0],
+                                    &blizzard.auto_update_window, NULL, NULL);
+
+}
+
+static int blizzard_setup_plane(int plane, int channel_out,
+                                 unsigned long offset, int screen_width,
+                                 int pos_x, int pos_y, int width, int height,
+                                 int color_mode)
+{
+       struct plane_info *p;
+
+#ifdef VERBOSE
+       dev_dbg(blizzard.fbdev->dev,
+                   "plane %d ch_out %d offset %#08lx scr_width %d "
+                   "pos_x %d pos_y %d width %d height %d color_mode %d\n",
+                   plane, channel_out, offset, screen_width,
+                   pos_x, pos_y, width, height, color_mode);
+#endif
+       if ((unsigned)plane > OMAPFB_PLANE_NUM)
+               return -EINVAL;
+       p = &blizzard.plane[plane];
+
+       switch (color_mode) {
+       case OMAPFB_COLOR_YUV422:
+       case OMAPFB_COLOR_YUY422:
+               p->bpp = 16;
+               blizzard.vid_nonstd_color &= ~(1 << plane);
+               break;
+       case OMAPFB_COLOR_YUV420:
+               p->bpp = 12;
+               blizzard.vid_nonstd_color |= 1 << plane;
+               break;
+       case OMAPFB_COLOR_RGB565:
+               p->bpp = 16;
+               blizzard.vid_nonstd_color &= ~(1 << plane);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       p->offset = offset;
+       p->pos_x = pos_x;
+       p->pos_y = pos_y;
+       p->width = width;
+       p->height = height;
+       p->scr_width = screen_width;
+       if (!p->out_width)
+               p->out_width = width;
+       if (!p->out_height)
+               p->out_height = height;
+
+       p->color_mode = color_mode;
+
+       return 0;
+}
+
+static int blizzard_set_scale(int plane, int orig_w, int orig_h,
+                             int out_w, int out_h)
+{
+       struct plane_info *p = &blizzard.plane[plane];
+       int r;
+
+       dev_dbg(blizzard.fbdev->dev,
+               "plane %d orig_w %d orig_h %d out_w %d out_h %d\n",
+               plane, orig_w, orig_h, out_w, out_h);
+       if ((unsigned)plane > OMAPFB_PLANE_NUM)
+               return -ENODEV;
+
+       r = blizzard.int_ctrl->set_scale(plane, orig_w, orig_h, out_w, out_h);
+       if (r < 0)
+               return r;
+
+       p->width = orig_w;
+       p->height = orig_h;
+       p->out_width = out_w;
+       p->out_height = out_h;
+       if (orig_w == out_w && orig_h == out_h)
+               blizzard.vid_scaled &= ~(1 << plane);
+       else
+               blizzard.vid_scaled |= 1 << plane;
+
+       return 0;
+}
+
+static int blizzard_enable_plane(int plane, int enable)
+{
+       if (enable)
+               blizzard.enabled_planes |= 1 << plane;
+       else
+               blizzard.enabled_planes &= ~(1 << plane);
+
+       return 0;
+}
+
+static int sync_handler(struct blizzard_request *req)
+{
+       complete(req->par.sync);
+       return REQ_COMPLETE;
+}
+
+static void blizzard_sync(void)
+{
+       LIST_HEAD(req_list);
+       struct blizzard_request *req;
+       struct completion comp;
+
+       req = alloc_req();
+
+       req->handler = sync_handler;
+       req->complete = NULL;
+       init_completion(&comp);
+       req->par.sync = &comp;
+
+       list_add(&req->entry, &req_list);
+       submit_req_list(&req_list);
+
+       wait_for_completion(&comp);
+}
+
+
+static void blizzard_bind_client(struct omapfb_notifier_block *nb)
+{
+       if (blizzard.update_mode == OMAPFB_MANUAL_UPDATE) {
+               omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_READY);
+       }
+}
+
+static int blizzard_set_update_mode(enum omapfb_update_mode mode)
+{
+       if (unlikely(mode != OMAPFB_MANUAL_UPDATE &&
+                    mode != OMAPFB_AUTO_UPDATE &&
+                    mode != OMAPFB_UPDATE_DISABLED))
+               return -EINVAL;
+
+       if (mode == blizzard.update_mode)
+               return 0;
+
+       dev_info(blizzard.fbdev->dev, "s1d1374x: setting update mode to %s\n",
+                       mode == OMAPFB_UPDATE_DISABLED ? "disabled" :
+                       (mode == OMAPFB_AUTO_UPDATE ? "auto" : "manual"));
+
+       switch (blizzard.update_mode) {
+       case OMAPFB_MANUAL_UPDATE:
+               omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_DISABLED);
+               break;
+       case OMAPFB_AUTO_UPDATE:
+               blizzard.stop_auto_update = 1;
+               del_timer_sync(&blizzard.auto_update_timer);
+               break;
+       case OMAPFB_UPDATE_DISABLED:
+               break;
+       }
+
+       blizzard.update_mode = mode;
+       blizzard_sync();
+       blizzard.stop_auto_update = 0;
+
+       switch (mode) {
+       case OMAPFB_MANUAL_UPDATE:
+               omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_READY);
+               break;
+       case OMAPFB_AUTO_UPDATE:
+               blizzard_update_window_auto(0);
+               break;
+       case OMAPFB_UPDATE_DISABLED:
+               break;
+       }
+
+       return 0;
+}
+
+static enum omapfb_update_mode blizzard_get_update_mode(void)
+{
+       return blizzard.update_mode;
+}
+
+static inline void set_extif_timings(const struct extif_timings *t)
+{
+       blizzard.extif->set_timings(t);
+}
+
+static inline unsigned long round_to_extif_ticks(unsigned long ps, int div)
+{
+       int bus_tick = blizzard.extif_clk_period * div;
+       return (ps + bus_tick - 1) / bus_tick * bus_tick;
+}
+
+static int calc_reg_timing(unsigned long sysclk, int div)
+{
+       struct extif_timings *t;
+       unsigned long systim;
+
+       /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns,
+        * AccessTime 2 ns + 12.2 ns (regs),
+        * WEOffTime = WEOnTime + 1 ns,
+        * REOffTime = REOnTime + 12 ns (regs),
+        * CSOffTime = REOffTime + 1 ns
+        * ReadCycle = 2ns + 2*SYSCLK  (regs),
+        * WriteCycle = 2*SYSCLK + 2 ns,
+        * CSPulseWidth = 10 ns */
+
+       systim = 1000000000 / (sysclk / 1000);
+       dev_dbg(blizzard.fbdev->dev,
+                 "Blizzard systim %lu ps extif_clk_period %u div %d\n",
+                 systim, blizzard.extif_clk_period, div);
+
+       t = &blizzard.reg_timings;
+       memset(t, 0, sizeof(*t));
+
+       t->clk_div = div;
+
+       t->cs_on_time = 0;
+       t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
+       t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
+       t->access_time = round_to_extif_ticks(t->re_on_time + 12200, div);
+       t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div);
+       t->re_off_time = round_to_extif_ticks(t->re_on_time + 13000, div);
+       t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div);
+       t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
+       if (t->we_cycle_time < t->we_off_time)
+               t->we_cycle_time = t->we_off_time;
+       t->re_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
+       if (t->re_cycle_time < t->re_off_time)
+               t->re_cycle_time = t->re_off_time;
+       t->cs_pulse_width = 0;
+
+       dev_dbg(blizzard.fbdev->dev, "[reg]cson %d csoff %d reon %d reoff %d\n",
+                t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time);
+       dev_dbg(blizzard.fbdev->dev, "[reg]weon %d weoff %d recyc %d wecyc %d\n",
+                t->we_on_time, t->we_off_time, t->re_cycle_time,
+                t->we_cycle_time);
+       dev_dbg(blizzard.fbdev->dev, "[reg]rdaccess %d cspulse %d\n",
+                t->access_time, t->cs_pulse_width);
+
+       return blizzard.extif->convert_timings(t);
+}
+
+static int calc_lut_timing(unsigned long sysclk, int div)
+{
+       struct extif_timings *t;
+       unsigned long systim;
+
+       /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns,
+        * AccessTime 2 ns + 4 * SYSCLK + 26 (lut),
+        * WEOffTime = WEOnTime + 1 ns,
+        * REOffTime = REOnTime + 4*SYSCLK + 26 ns (lut),
+        * CSOffTime = REOffTime + 1 ns
+        * ReadCycle = 2ns + 4*SYSCLK + 26 ns (lut),
+        * WriteCycle = 2*SYSCLK + 2 ns,
+        * CSPulseWidth = 10 ns */
+
+       systim = 1000000000 / (sysclk / 1000);
+       dev_dbg(blizzard.fbdev->dev,
+               "Blizzard systim %lu ps extif_clk_period %u div %d\n",
+               systim, blizzard.extif_clk_period, div);
+
+       t = &blizzard.lut_timings;
+       memset(t, 0, sizeof(*t));
+
+       t->clk_div = div;
+
+       t->cs_on_time = 0;
+       t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
+       t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
+       t->access_time = round_to_extif_ticks(t->re_on_time + 4 * systim +
+                                             26000, div);
+       t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div);
+       t->re_off_time = round_to_extif_ticks(t->re_on_time + 4 * systim +
+                                             26000, div);
+       t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div);
+       t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
+       if (t->we_cycle_time < t->we_off_time)
+               t->we_cycle_time = t->we_off_time;
+       t->re_cycle_time = round_to_extif_ticks(2000 + 4 * systim + 26000, div);
+       if (t->re_cycle_time < t->re_off_time)
+               t->re_cycle_time = t->re_off_time;
+       t->cs_pulse_width = 0;
+
+       dev_dbg(blizzard.fbdev->dev,
+                "[lut]cson %d csoff %d reon %d reoff %d\n",
+                t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time);
+       dev_dbg(blizzard.fbdev->dev,
+                "[lut]weon %d weoff %d recyc %d wecyc %d\n",
+                t->we_on_time, t->we_off_time, t->re_cycle_time,
+                t->we_cycle_time);
+       dev_dbg(blizzard.fbdev->dev, "[lut]rdaccess %d cspulse %d\n",
+                t->access_time, t->cs_pulse_width);
+
+       return blizzard.extif->convert_timings(t);
+}
+
+static int calc_extif_timings(unsigned long sysclk, int *extif_mem_div)
+{
+       int max_clk_div;
+       int div;
+
+       blizzard.extif->get_clk_info(&blizzard.extif_clk_period, &max_clk_div);
+       for (div = 1; div <= max_clk_div; div++) {
+               if (calc_reg_timing(sysclk, div) == 0)
+                       break;
+       }
+       if (div > max_clk_div) {
+               dev_dbg(blizzard.fbdev->dev, "reg timing failed\n");
+               goto err;
+       }
+       *extif_mem_div = div;
+
+       for (div = 1; div <= max_clk_div; div++) {
+               if (calc_lut_timing(sysclk, div) == 0)
+                       break;
+       }
+
+       if (div > max_clk_div)
+               goto err;
+
+       blizzard.extif_clk_div = div;
+
+       return 0;
+err:
+       dev_err(blizzard.fbdev->dev, "can't setup timings\n");
+       return -1;
+}
+
+static void calc_blizzard_clk_rates(unsigned long ext_clk,
+                               unsigned long *sys_clk, unsigned long *pix_clk)
+{
+       int pix_clk_src;
+       int sys_div = 0, sys_mul = 0;
+       int pix_div;
+
+       pix_clk_src = blizzard_read_reg(BLIZZARD_CLK_SRC);
+       pix_div = ((pix_clk_src >> 3) & 0x1f) + 1;
+       if ((pix_clk_src & (0x3 << 1)) == 0) {
+               /* Source is the PLL */
+               sys_div = (blizzard_read_reg(BLIZZARD_PLL_DIV) & 0x3f) + 1;
+               sys_mul = blizzard_read_reg(BLIZZARD_PLL_CLOCK_SYNTH_0);
+               sys_mul |= ((blizzard_read_reg(BLIZZARD_PLL_CLOCK_SYNTH_1)
+                               & 0x0f) << 11);
+               *sys_clk = ext_clk * sys_mul / sys_div;
+       } else  /* else source is ext clk, or oscillator */
+               *sys_clk = ext_clk;
+
+       *pix_clk = *sys_clk / pix_div;                  /* HZ */
+       dev_dbg(blizzard.fbdev->dev,
+               "ext_clk %ld pix_src %d pix_div %d sys_div %d sys_mul %d\n",
+               ext_clk, pix_clk_src & (0x3 << 1), pix_div, sys_div, sys_mul);
+       dev_dbg(blizzard.fbdev->dev, "sys_clk %ld pix_clk %ld\n",
+               *sys_clk, *pix_clk);
+}
+
+static int setup_tearsync(unsigned long pix_clk, int extif_div)
+{
+       int hdisp, vdisp;
+       int hndp, vndp;
+       int hsw, vsw;
+       int hs, vs;
+       int hs_pol_inv, vs_pol_inv;
+       int use_hsvs, use_ndp;
+       u8  b;
+
+       hsw = blizzard_read_reg(BLIZZARD_HSW);
+       vsw = blizzard_read_reg(BLIZZARD_VSW);
+       hs_pol_inv = !(hsw & 0x80);
+       vs_pol_inv = !(vsw & 0x80);
+       hsw = hsw & 0x7f;
+       vsw = vsw & 0x3f;
+
+       hdisp = blizzard_read_reg(BLIZZARD_HDISP) * 8;
+       vdisp = blizzard_read_reg(BLIZZARD_VDISP0) +
+               ((blizzard_read_reg(BLIZZARD_VDISP1) & 0x3) << 8);
+
+       hndp = blizzard_read_reg(BLIZZARD_HNDP) & 0x3f;
+       vndp = blizzard_read_reg(BLIZZARD_VNDP);
+
+       /* time to transfer one pixel (16bpp) in ps */
+       blizzard.pix_tx_time = blizzard.reg_timings.we_cycle_time;
+       if (blizzard.extif->get_max_tx_rate != NULL) {
+               /* The external interface might have a rate limitation,
+                * if so, we have to maximize our transfer rate.
+                */
+               unsigned long min_tx_time;
+               unsigned long max_tx_rate = blizzard.extif->get_max_tx_rate();
+
+               dev_dbg(blizzard.fbdev->dev, "max_tx_rate %ld HZ\n",
+                       max_tx_rate);
+               min_tx_time = 1000000000 / (max_tx_rate / 1000);  /* ps */
+               if (blizzard.pix_tx_time < min_tx_time)
+                       blizzard.pix_tx_time = min_tx_time;
+       }
+
+       /* time to update one line in ps */
+       blizzard.line_upd_time = (hdisp + hndp) * 1000000 / (pix_clk / 1000);
+       blizzard.line_upd_time *= 1000;
+       if (hdisp * blizzard.pix_tx_time > blizzard.line_upd_time)
+               /* transfer speed too low, we might have to use both
+                * HS and VS */
+               use_hsvs = 1;
+       else
+               /* decent transfer speed, we'll always use only VS */
+               use_hsvs = 0;
+
+       if (use_hsvs && (hs_pol_inv || vs_pol_inv)) {
+               /* HS or'ed with VS doesn't work, use the active high
+                * TE signal based on HNDP / VNDP */
+               use_ndp = 1;
+               hs_pol_inv = 0;
+               vs_pol_inv = 0;
+               hs = hndp;
+               vs = vndp;
+       } else {
+               /* Use HS or'ed with VS as a TE signal if both are needed
+                * or VNDP if only vsync is needed. */
+               use_ndp = 0;
+               hs = hsw;
+               vs = vsw;
+               if (!use_hsvs) {
+                       hs_pol_inv = 0;
+                       vs_pol_inv = 0;
+               }
+       }
+
+       hs = hs * 1000000 / (pix_clk / 1000);             /* ps */
+       hs *= 1000;
+
+       vs = vs * (hdisp + hndp) * 1000000 / (pix_clk / 1000); /* ps */
+       vs *= 1000;
+
+       if (vs <= hs)
+               return -EDOM;
+       /* set VS to 120% of HS to minimize VS detection time */
+       vs = hs * 12 / 10;
+       /* minimize HS too */
+       if (hs > 10000)
+               hs = 10000;
+
+       b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS);
+       b &= ~0x3;
+       b |= use_hsvs ? 1 : 0;
+       b |= (use_ndp && use_hsvs) ? 0 : 2;
+       blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b);
+
+       blizzard.vsync_only = !use_hsvs;
+
+       dev_dbg(blizzard.fbdev->dev,
+               "pix_clk %ld HZ pix_tx_time %ld ps line_upd_time %ld ps\n",
+               pix_clk, blizzard.pix_tx_time, blizzard.line_upd_time);
+       dev_dbg(blizzard.fbdev->dev,
+               "hs %d ps vs %d ps mode %d vsync_only %d\n",
+               hs, vs, b & 0x3, !use_hsvs);
+
+       return blizzard.extif->setup_tearsync(1, hs, vs,
+                                             hs_pol_inv, vs_pol_inv,
+                                             extif_div);
+}
+
+static void blizzard_get_caps(int plane, struct omapfb_caps *caps)
+{
+       blizzard.int_ctrl->get_caps(plane, caps);
+       caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE |
+               OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE |
+               OMAPFB_CAPS_WINDOW_SCALE |
+               OMAPFB_CAPS_WINDOW_OVERLAY;
+       if (blizzard.te_connected)
+               caps->ctrl |= OMAPFB_CAPS_TEARSYNC;
+       caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) |
+                          (1 << OMAPFB_COLOR_YUV420);
+}
+
+static void _save_regs(struct blizzard_reg_list *list, int cnt)
+{
+       int i;
+
+       for (i = 0; i < cnt; i++, list++) {
+               int reg;
+               for (reg = list->start; reg <= list->end; reg += 2)
+                       blizzard_reg_cache[reg / 2] = blizzard_read_reg(reg);
+       }
+}
+
+static void _restore_regs(struct blizzard_reg_list *list, int cnt)
+{
+       int i;
+
+       for (i = 0; i < cnt; i++, list++) {
+               int reg;
+               for (reg = list->start; reg <= list->end; reg += 2)
+                       blizzard_write_reg(reg, blizzard_reg_cache[reg / 2]);
+       }
+}
+
+static void blizzard_save_all_regs(void)
+{
+       _save_regs(blizzard_pll_regs, ARRAY_SIZE(blizzard_pll_regs));
+       _save_regs(blizzard_gen_regs, ARRAY_SIZE(blizzard_gen_regs));
+}
+
+static void blizzard_restore_pll_regs(void)
+{
+       _restore_regs(blizzard_pll_regs, ARRAY_SIZE(blizzard_pll_regs));
+}
+
+static void blizzard_restore_gen_regs(void)
+{
+       _restore_regs(blizzard_gen_regs, ARRAY_SIZE(blizzard_gen_regs));
+}
+
+static void blizzard_suspend(void)
+{
+       u32 l;
+       unsigned long tmo;
+
+       if (blizzard.last_color_mode) {
+               update_full_screen();
+               blizzard_sync();
+       }
+       blizzard.update_mode_before_suspend = blizzard.update_mode;
+       /* the following will disable clocks as well */
+       blizzard_set_update_mode(OMAPFB_UPDATE_DISABLED);
+
+       blizzard_save_all_regs();
+
+       blizzard_stop_sdram();
+
+       l = blizzard_read_reg(BLIZZARD_POWER_SAVE);
+       /* Standby, Sleep. We assume we use an external clock. */
+       l |= 0x03;
+       blizzard_write_reg(BLIZZARD_POWER_SAVE, l);
+
+       tmo = jiffies + msecs_to_jiffies(100);
+       while (!(blizzard_read_reg(BLIZZARD_PLL_MODE) & (1 << 1))) {
+               if (time_after(jiffies, tmo)) {
+                       dev_err(blizzard.fbdev->dev,
+                               "s1d1374x: sleep timeout, stopping PLL manually\n");
+                       l = blizzard_read_reg(BLIZZARD_PLL_MODE);
+                       l &= ~0x03;
+                       /* Disable PLL, counter function */
+                       l |= 0x2;
+                       blizzard_write_reg(BLIZZARD_PLL_MODE, l);
+                       break;
+               }
+               msleep(1);
+       }
+
+       if (blizzard.power_down != NULL)
+               blizzard.power_down(blizzard.fbdev->dev);
+}
+
+static void blizzard_resume(void)
+{
+       u32 l;
+
+       if (blizzard.power_up != NULL)
+               blizzard.power_up(blizzard.fbdev->dev);
+
+       l = blizzard_read_reg(BLIZZARD_POWER_SAVE);
+       /* Standby, Sleep */
+       l &= ~0x03;
+       blizzard_write_reg(BLIZZARD_POWER_SAVE, l);
+
+       blizzard_restore_pll_regs();
+       l = blizzard_read_reg(BLIZZARD_PLL_MODE);
+       l &= ~0x03;
+       /* Enable PLL, counter function */
+       l |= 0x1;
+       blizzard_write_reg(BLIZZARD_PLL_MODE, l);
+
+       while (!(blizzard_read_reg(BLIZZARD_PLL_DIV) & (1 << 7)))
+               msleep(1);
+
+       blizzard_restart_sdram();
+
+       blizzard_restore_gen_regs();
+
+       /* Enable display */
+       blizzard_write_reg(BLIZZARD_DISPLAY_MODE, 0x01);
+
+       /* the following will enable clocks as necessary */
+       blizzard_set_update_mode(blizzard.update_mode_before_suspend);
+
+       /* Force a background update */
+       blizzard.zoom_on = 1;
+       update_full_screen();
+       blizzard_sync();
+}
+
+static int blizzard_init(struct omapfb_device *fbdev, int ext_mode,
+                        struct omapfb_mem_desc *req_vram)
+{
+       int r = 0, i;
+       u8 rev, conf;
+       unsigned long ext_clk;
+       int extif_div;
+       unsigned long sys_clk, pix_clk;
+       struct omapfb_platform_data *omapfb_conf;
+       struct blizzard_platform_data *ctrl_conf;
+
+       blizzard.fbdev = fbdev;
+
+       BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl);
+
+       blizzard.fbdev = fbdev;
+       blizzard.extif = fbdev->ext_if;
+       blizzard.int_ctrl = fbdev->int_ctrl;
+
+       omapfb_conf = fbdev->dev->platform_data;
+       ctrl_conf = omapfb_conf->ctrl_platform_data;
+       if (ctrl_conf == NULL || ctrl_conf->get_clock_rate == NULL) {
+               dev_err(fbdev->dev, "s1d1374x: missing platform data\n");
+               r = -ENOENT;
+               goto err1;
+       }
+
+       blizzard.power_down = ctrl_conf->power_down;
+       blizzard.power_up = ctrl_conf->power_up;
+
+       spin_lock_init(&blizzard.req_lock);
+
+       if ((r = blizzard.int_ctrl->init(fbdev, 1, req_vram)) < 0)
+               goto err1;
+
+       if ((r = blizzard.extif->init(fbdev)) < 0)
+               goto err2;
+
+       blizzard_ctrl.set_color_key = blizzard.int_ctrl->set_color_key;
+       blizzard_ctrl.get_color_key = blizzard.int_ctrl->get_color_key;
+       blizzard_ctrl.setup_mem = blizzard.int_ctrl->setup_mem;
+       blizzard_ctrl.mmap = blizzard.int_ctrl->mmap;
+
+       ext_clk = ctrl_conf->get_clock_rate(fbdev->dev);
+       if ((r = calc_extif_timings(ext_clk, &extif_div)) < 0)
+               goto err3;
+
+       set_extif_timings(&blizzard.reg_timings);
+
+       if (blizzard.power_up != NULL)
+               blizzard.power_up(fbdev->dev);
+
+       calc_blizzard_clk_rates(ext_clk, &sys_clk, &pix_clk);
+
+       if ((r = calc_extif_timings(sys_clk, &extif_div)) < 0)
+               goto err3;
+       set_extif_timings(&blizzard.reg_timings);
+
+       if (!(blizzard_read_reg(BLIZZARD_PLL_DIV) & 0x80)) {
+               dev_err(fbdev->dev,
+                       "controller not initialized by the bootloader\n");
+               r = -ENODEV;
+               goto err3;
+       }
+
+       if (ctrl_conf->te_connected) {
+               if ((r = setup_tearsync(pix_clk, extif_div)) < 0)
+                       goto err3;
+               blizzard.te_connected = 1;
+       }
+
+       rev = blizzard_read_reg(BLIZZARD_REV_CODE);
+       conf = blizzard_read_reg(BLIZZARD_CONFIG);
+
+       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:
+               dev_err(fbdev->dev, "invalid s1d1374x revision %02x\n",
+                       rev);
+               r = -ENODEV;
+               goto err3;
+       }
+
+       blizzard.max_transmit_size = blizzard.extif->max_transmit_size;
+
+       blizzard.update_mode = OMAPFB_UPDATE_DISABLED;
+
+       blizzard.auto_update_window.x = 0;
+       blizzard.auto_update_window.y = 0;
+       blizzard.auto_update_window.width = fbdev->panel->x_res;
+       blizzard.auto_update_window.height = fbdev->panel->y_res;
+       blizzard.auto_update_window.out_x = 0;
+       blizzard.auto_update_window.out_x = 0;
+       blizzard.auto_update_window.out_width = fbdev->panel->x_res;
+       blizzard.auto_update_window.out_height = fbdev->panel->y_res;
+       blizzard.auto_update_window.format = 0;
+
+       blizzard.screen_width = fbdev->panel->x_res;
+       blizzard.screen_height = fbdev->panel->y_res;
+
+       init_timer(&blizzard.auto_update_timer);
+       blizzard.auto_update_timer.function = blizzard_update_window_auto;
+       blizzard.auto_update_timer.data = 0;
+
+       INIT_LIST_HEAD(&blizzard.free_req_list);
+       INIT_LIST_HEAD(&blizzard.pending_req_list);
+       for (i = 0; i < ARRAY_SIZE(blizzard.req_pool); i++)
+               list_add(&blizzard.req_pool[i].entry, &blizzard.free_req_list);
+       BUG_ON(i <= IRQ_REQ_POOL_SIZE);
+       sema_init(&blizzard.req_sema, i - IRQ_REQ_POOL_SIZE);
+
+       return 0;
+err3:
+       if (blizzard.power_down != NULL)
+               blizzard.power_down(fbdev->dev);
+       blizzard.extif->cleanup();
+err2:
+       blizzard.int_ctrl->cleanup();
+err1:
+       return r;
+}
+
+static void blizzard_cleanup(void)
+{
+       blizzard_set_update_mode(OMAPFB_UPDATE_DISABLED);
+       blizzard.extif->cleanup();
+       blizzard.int_ctrl->cleanup();
+       if (blizzard.power_down != NULL)
+               blizzard.power_down(blizzard.fbdev->dev);
+}
+
+struct lcd_ctrl blizzard_ctrl = {
+       .name                   = "blizzard",
+       .init                   = blizzard_init,
+       .cleanup                = blizzard_cleanup,
+       .bind_client            = blizzard_bind_client,
+       .get_caps               = blizzard_get_caps,
+       .set_update_mode        = blizzard_set_update_mode,
+       .get_update_mode        = blizzard_get_update_mode,
+       .setup_plane            = blizzard_setup_plane,
+       .set_scale              = blizzard_set_scale,
+       .enable_plane           = blizzard_enable_plane,
+       .update_window          = blizzard_update_window_async,
+       .sync                   = blizzard_sync,
+       .suspend                = blizzard_suspend,
+       .resume                 = blizzard_resume,
+};
+
diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c
new file mode 100644 (file)
index 0000000..5eeaddb
--- /dev/null
@@ -0,0 +1,1512 @@
+/*
+ * File: drivers/video/omap/omap2/dispc.c
+ *
+ * OMAP2 display controller support
+ *
+ * Copyright (C) 2005 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/sram.h>
+#include <asm/arch/omapfb.h>
+#include <asm/arch/board.h>
+
+#include "dispc.h"
+
+#define MODULE_NAME                    "dispc"
+
+#define DSS_BASE                       0x48050000
+#define DSS_SYSCONFIG                  0x0010
+
+#define DISPC_BASE                     0x48050400
+
+/* DISPC common */
+#define DISPC_REVISION                 0x0000
+#define DISPC_SYSCONFIG                        0x0010
+#define DISPC_SYSSTATUS                        0x0014
+#define DISPC_IRQSTATUS                        0x0018
+#define DISPC_IRQENABLE                        0x001C
+#define DISPC_CONTROL                  0x0040
+#define DISPC_CONFIG                   0x0044
+#define DISPC_CAPABLE                  0x0048
+#define DISPC_DEFAULT_COLOR0           0x004C
+#define DISPC_DEFAULT_COLOR1           0x0050
+#define DISPC_TRANS_COLOR0             0x0054
+#define DISPC_TRANS_COLOR1             0x0058
+#define DISPC_LINE_STATUS              0x005C
+#define DISPC_LINE_NUMBER              0x0060
+#define DISPC_TIMING_H                 0x0064
+#define DISPC_TIMING_V                 0x0068
+#define DISPC_POL_FREQ                 0x006C
+#define DISPC_DIVISOR                  0x0070
+#define DISPC_SIZE_DIG                 0x0078
+#define DISPC_SIZE_LCD                 0x007C
+
+#define DISPC_DATA_CYCLE1              0x01D4
+#define DISPC_DATA_CYCLE2              0x01D8
+#define DISPC_DATA_CYCLE3              0x01DC
+
+/* DISPC GFX plane */
+#define DISPC_GFX_BA0                  0x0080
+#define DISPC_GFX_BA1                  0x0084
+#define DISPC_GFX_POSITION             0x0088
+#define DISPC_GFX_SIZE                 0x008C
+#define DISPC_GFX_ATTRIBUTES           0x00A0
+#define DISPC_GFX_FIFO_THRESHOLD       0x00A4
+#define DISPC_GFX_FIFO_SIZE_STATUS     0x00A8
+#define DISPC_GFX_ROW_INC              0x00AC
+#define DISPC_GFX_PIXEL_INC            0x00B0
+#define DISPC_GFX_WINDOW_SKIP          0x00B4
+#define DISPC_GFX_TABLE_BA             0x00B8
+
+/* DISPC Video plane 1/2 */
+#define DISPC_VID1_BASE                        0x00BC
+#define DISPC_VID2_BASE                        0x014C
+
+/* Offsets into DISPC_VID1/2_BASE */
+#define DISPC_VID_BA0                  0x0000
+#define DISPC_VID_BA1                  0x0004
+#define DISPC_VID_POSITION             0x0008
+#define DISPC_VID_SIZE                 0x000C
+#define DISPC_VID_ATTRIBUTES           0x0010
+#define DISPC_VID_FIFO_THRESHOLD       0x0014
+#define DISPC_VID_FIFO_SIZE_STATUS     0x0018
+#define DISPC_VID_ROW_INC              0x001C
+#define DISPC_VID_PIXEL_INC            0x0020
+#define DISPC_VID_FIR                  0x0024
+#define DISPC_VID_PICTURE_SIZE         0x0028
+#define DISPC_VID_ACCU0                        0x002C
+#define DISPC_VID_ACCU1                        0x0030
+
+/* 8 elements in 8 byte increments */
+#define DISPC_VID_FIR_COEF_H0          0x0034
+/* 8 elements in 8 byte increments */
+#define DISPC_VID_FIR_COEF_HV0         0x0038
+/* 5 elements in 4 byte increments */
+#define DISPC_VID_CONV_COEF0           0x0074
+
+#define DISPC_IRQ_FRAMEMASK            0x0001
+#define DISPC_IRQ_VSYNC                        0x0002
+#define DISPC_IRQ_EVSYNC_EVEN          0x0004
+#define DISPC_IRQ_EVSYNC_ODD           0x0008
+#define DISPC_IRQ_ACBIAS_COUNT_STAT    0x0010
+#define DISPC_IRQ_PROG_LINE_NUM                0x0020
+#define DISPC_IRQ_GFX_FIFO_UNDERFLOW   0x0040
+#define DISPC_IRQ_GFX_END_WIN          0x0080
+#define DISPC_IRQ_PAL_GAMMA_MASK       0x0100
+#define DISPC_IRQ_OCP_ERR              0x0200
+#define DISPC_IRQ_VID1_FIFO_UNDERFLOW  0x0400
+#define DISPC_IRQ_VID1_END_WIN         0x0800
+#define DISPC_IRQ_VID2_FIFO_UNDERFLOW  0x1000
+#define DISPC_IRQ_VID2_END_WIN         0x2000
+#define DISPC_IRQ_SYNC_LOST            0x4000
+
+#define DISPC_IRQ_MASK_ALL             0x7fff
+
+#define DISPC_IRQ_MASK_ERROR           (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
+                                            DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
+                                            DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
+                                            DISPC_IRQ_SYNC_LOST)
+
+#define RFBI_CONTROL                   0x48050040
+
+#define MAX_PALETTE_SIZE               (256 * 16)
+
+#define FLD_MASK(pos, len)     (((1 << len) - 1) << pos)
+
+#define MOD_REG_FLD(reg, mask, val) \
+       dispc_write_reg((reg), (dispc_read_reg(reg) & ~(mask)) | (val));
+
+#define OMAP2_SRAM_START               0x40200000
+/* Maximum size, in reality this is smaller if SRAM is partially locked. */
+#define OMAP2_SRAM_SIZE                        0xa0000         /* 640k */
+
+/* We support the SDRAM / SRAM types. See OMAPFB_PLANE_MEMTYPE_* in omapfb.h */
+#define DISPC_MEMTYPE_NUM              2
+
+#define RESMAP_SIZE(_page_cnt)                                         \
+       ((_page_cnt + (sizeof(unsigned long) * 8) - 1) / 8)
+#define RESMAP_PTR(_res_map, _page_nr)                                 \
+       (((_res_map)->map) + (_page_nr) / (sizeof(unsigned long) * 8))
+#define RESMAP_MASK(_page_nr)                                          \
+       (1 << ((_page_nr) & (sizeof(unsigned long) * 8 - 1)))
+
+struct resmap {
+       unsigned long   start;
+       unsigned        page_cnt;
+       unsigned long   *map;
+};
+
+static struct {
+       u32             base;
+
+       struct omapfb_mem_desc  mem_desc;
+       struct resmap           *res_map[DISPC_MEMTYPE_NUM];
+       atomic_t                map_count[OMAPFB_PLANE_NUM];
+
+       dma_addr_t      palette_paddr;
+       void            *palette_vaddr;
+
+       int             ext_mode;
+
+       unsigned long   enabled_irqs;
+       void            (*irq_callback)(void *);
+       void            *irq_callback_data;
+       struct completion       frame_done;
+
+       int             fir_hinc[OMAPFB_PLANE_NUM];
+       int             fir_vinc[OMAPFB_PLANE_NUM];
+
+       struct clk      *dss_ick, *dss1_fck;
+       struct clk      *dss_54m_fck;
+
+       enum omapfb_update_mode update_mode;
+       struct omapfb_device    *fbdev;
+
+       struct omapfb_color_key color_key;
+} dispc;
+
+static void enable_lcd_clocks(int enable);
+
+static void inline dispc_write_reg(int idx, u32 val)
+{
+       __raw_writel(val, dispc.base + idx);
+}
+
+static u32 inline dispc_read_reg(int idx)
+{
+       u32 l = __raw_readl(dispc.base + idx);
+       return l;
+}
+
+/* Select RFBI or bypass mode */
+static void enable_rfbi_mode(int enable)
+{
+       u32 l;
+
+       l = dispc_read_reg(DISPC_CONTROL);
+       /* Enable RFBI, GPIO0/1 */
+       l &= ~((1 << 11) | (1 << 15) | (1 << 16));
+       l |= enable ? (1 << 11) : 0;
+       /* RFBI En: GPIO0/1=10  RFBI Dis: GPIO0/1=11 */
+       l |= 1 << 15;
+       l |= enable ? 0 : (1 << 16);
+       dispc_write_reg(DISPC_CONTROL, l);
+
+       /* Set bypass mode in RFBI module */
+       l = __raw_readl(io_p2v(RFBI_CONTROL));
+       l |= enable ? 0 : (1 << 1);
+       __raw_writel(l, io_p2v(RFBI_CONTROL));
+}
+
+static void set_lcd_data_lines(int data_lines)
+{
+       u32 l;
+       int code = 0;
+
+       switch (data_lines) {
+       case 12:
+               code = 0;
+               break;
+       case 16:
+               code = 1;
+               break;
+       case 18:
+               code = 2;
+               break;
+       case 24:
+               code = 3;
+               break;
+       default:
+               BUG();
+       }
+
+       l = dispc_read_reg(DISPC_CONTROL);
+       l &= ~(0x03 << 8);
+       l |= code << 8;
+       dispc_write_reg(DISPC_CONTROL, l);
+}
+
+static void set_load_mode(int mode)
+{
+       BUG_ON(mode & ~(DISPC_LOAD_CLUT_ONLY | DISPC_LOAD_FRAME_ONLY |
+                       DISPC_LOAD_CLUT_ONCE_FRAME));
+       MOD_REG_FLD(DISPC_CONFIG, 0x03 << 1, mode << 1);
+}
+
+void omap_dispc_set_lcd_size(int x, int y)
+{
+       BUG_ON((x > (1 << 11)) || (y > (1 << 11)));
+       enable_lcd_clocks(1);
+       MOD_REG_FLD(DISPC_SIZE_LCD, FLD_MASK(16, 11) | FLD_MASK(0, 11),
+                       ((y - 1) << 16) | (x - 1));
+       enable_lcd_clocks(0);
+}
+EXPORT_SYMBOL(omap_dispc_set_lcd_size);
+
+void omap_dispc_set_digit_size(int x, int y)
+{
+       BUG_ON((x > (1 << 11)) || (y > (1 << 11)));
+       enable_lcd_clocks(1);
+       MOD_REG_FLD(DISPC_SIZE_DIG, FLD_MASK(16, 11) | FLD_MASK(0, 11),
+                       ((y - 1) << 16) | (x - 1));
+       enable_lcd_clocks(0);
+}
+EXPORT_SYMBOL(omap_dispc_set_digit_size);
+
+static void setup_plane_fifo(int plane, int ext_mode)
+{
+       const u32 ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD,
+                               DISPC_VID1_BASE + DISPC_VID_FIFO_THRESHOLD,
+                               DISPC_VID2_BASE + DISPC_VID_FIFO_THRESHOLD };
+       const u32 fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS,
+                               DISPC_VID1_BASE + DISPC_VID_FIFO_SIZE_STATUS,
+                               DISPC_VID2_BASE + DISPC_VID_FIFO_SIZE_STATUS };
+       int low, high;
+       u32 l;
+
+       BUG_ON(plane > 2);
+
+       l = dispc_read_reg(fsz_reg[plane]);
+       l &= FLD_MASK(0, 9);
+       if (ext_mode) {
+               low = l * 3 / 4;
+               high = l;
+       } else {
+               low = l / 4;
+               high = l * 3 / 4;
+       }
+       MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 9) | FLD_MASK(0, 9),
+                       (high << 16) | low);
+}
+
+void omap_dispc_enable_lcd_out(int enable)
+{
+       enable_lcd_clocks(1);
+       MOD_REG_FLD(DISPC_CONTROL, 1, enable ? 1 : 0);
+       enable_lcd_clocks(0);
+}
+EXPORT_SYMBOL(omap_dispc_enable_lcd_out);
+
+void omap_dispc_enable_digit_out(int enable)
+{
+       enable_lcd_clocks(1);
+       MOD_REG_FLD(DISPC_CONTROL, 1 << 1, enable ? 1 << 1 : 0);
+       enable_lcd_clocks(0);
+}
+EXPORT_SYMBOL(omap_dispc_enable_digit_out);
+
+static inline int _setup_plane(int plane, int channel_out,
+                                 u32 paddr, int screen_width,
+                                 int pos_x, int pos_y, int width, int height,
+                                 int color_mode)
+{
+       const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES,
+                               DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES,
+                               DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES };
+       const u32 ba_reg[] = { DISPC_GFX_BA0, DISPC_VID1_BASE + DISPC_VID_BA0,
+                               DISPC_VID2_BASE + DISPC_VID_BA0 };
+       const u32 ps_reg[] = { DISPC_GFX_POSITION,
+                               DISPC_VID1_BASE + DISPC_VID_POSITION,
+                               DISPC_VID2_BASE + DISPC_VID_POSITION };
+       const u32 sz_reg[] = { DISPC_GFX_SIZE,
+                               DISPC_VID1_BASE + DISPC_VID_PICTURE_SIZE,
+                               DISPC_VID2_BASE + DISPC_VID_PICTURE_SIZE };
+       const u32 ri_reg[] = { DISPC_GFX_ROW_INC,
+                               DISPC_VID1_BASE + DISPC_VID_ROW_INC,
+                               DISPC_VID2_BASE + DISPC_VID_ROW_INC };
+       const u32 vs_reg[]= { 0, DISPC_VID1_BASE + DISPC_VID_SIZE,
+                               DISPC_VID2_BASE + DISPC_VID_SIZE };
+
+       int chout_shift, burst_shift;
+       int chout_val;
+       int color_code;
+       int bpp;
+       int cconv_en;
+       int set_vsize;
+       u32 l;
+
+#ifdef VERBOSE
+       dev_dbg(dispc.fbdev->dev, "plane %d channel %d paddr %#08x scr_width %d "
+                   "pos_x %d pos_y %d width %d height %d color_mode %d\n",
+                   plane, channel_out, paddr, screen_width, pos_x, pos_y,
+                   width, height, color_mode);
+#endif
+
+       set_vsize = 0;
+       switch (plane) {
+       case OMAPFB_PLANE_GFX:
+               burst_shift = 6;
+               chout_shift = 8;
+               break;
+       case OMAPFB_PLANE_VID1:
+       case OMAPFB_PLANE_VID2:
+               burst_shift = 14;
+               chout_shift = 16;
+               set_vsize = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (channel_out) {
+       case OMAPFB_CHANNEL_OUT_LCD:
+               chout_val = 0;
+               break;
+       case OMAPFB_CHANNEL_OUT_DIGIT:
+               chout_val = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       cconv_en = 0;
+       switch (color_mode) {
+       case OMAPFB_COLOR_RGB565:
+               color_code = DISPC_RGB_16_BPP;
+               bpp = 16;
+               break;
+       case OMAPFB_COLOR_YUV422:
+               if (plane == 0)
+                       return -EINVAL;
+               color_code = DISPC_UYVY_422;
+               cconv_en = 1;
+               bpp = 16;
+               break;
+       case OMAPFB_COLOR_YUY422:
+               if (plane == 0)
+                       return -EINVAL;
+               color_code = DISPC_YUV2_422;
+               cconv_en = 1;
+               bpp = 16;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       l = dispc_read_reg(at_reg[plane]);
+
+       l &= ~(0x0f << 1);
+       l |= color_code << 1;
+       l &= ~(1 << 9);
+       l |= cconv_en << 9;
+
+       l &= ~(0x03 << burst_shift);
+       l |= DISPC_BURST_8x32 << burst_shift;
+
+       l &= ~(1 << chout_shift);
+       l |= chout_val << chout_shift;
+
+       dispc_write_reg(at_reg[plane], l);
+
+       dispc_write_reg(ba_reg[plane], paddr);
+       MOD_REG_FLD(ps_reg[plane],
+                   FLD_MASK(16, 11) | FLD_MASK(0, 11), (pos_y << 16) | pos_x);
+
+       MOD_REG_FLD(sz_reg[plane], FLD_MASK(16, 11) | FLD_MASK(0, 11),
+                       ((height - 1) << 16) | (width - 1));
+
+       if (set_vsize) {
+               /* Set video size if set_scale hasn't set it */
+               if (!dispc.fir_vinc[plane])
+                       MOD_REG_FLD(vs_reg[plane],
+                               FLD_MASK(16, 11), (height - 1) << 16);
+               if (!dispc.fir_hinc[plane])
+                       MOD_REG_FLD(vs_reg[plane],
+                               FLD_MASK(0, 11), width - 1);
+       }
+
+       dispc_write_reg(ri_reg[plane], (screen_width - width) * bpp / 8 + 1);
+
+       return height * screen_width * bpp / 8;
+}
+
+static int omap_dispc_setup_plane(int plane, int channel_out,
+                                 unsigned long offset,
+                                 int screen_width,
+                                 int pos_x, int pos_y, int width, int height,
+                                 int color_mode)
+{
+       u32 paddr;
+       int r;
+
+       if ((unsigned)plane > dispc.mem_desc.region_cnt)
+               return -EINVAL;
+       paddr = dispc.mem_desc.region[plane].paddr + offset;
+       enable_lcd_clocks(1);
+       r = _setup_plane(plane, channel_out, paddr,
+                       screen_width,
+                       pos_x, pos_y, width, height, color_mode);
+       enable_lcd_clocks(0);
+       return r;
+}
+
+static void write_firh_reg(int plane, int reg, u32 value)
+{
+       u32 base;
+
+       if (plane == 1)
+               base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_H0;
+       else
+               base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_H0;
+       dispc_write_reg(base + reg * 8, value);
+}
+
+static void write_firhv_reg(int plane, int reg, u32 value)
+{
+       u32 base;
+
+       if (plane == 1)
+               base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_HV0;
+       else
+               base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_HV0;
+       dispc_write_reg(base + reg * 8, value);
+}
+
+static void set_upsampling_coef_table(int plane)
+{
+       const u32 coef[][2] = {
+               { 0x00800000, 0x00800000 },
+               { 0x0D7CF800, 0x037B02FF },
+               { 0x1E70F5FF, 0x0C6F05FE },
+               { 0x335FF5FE, 0x205907FB },
+               { 0xF74949F7, 0x00404000 },
+               { 0xF55F33FB, 0x075920FE },
+               { 0xF5701EFE, 0x056F0CFF },
+               { 0xF87C0DFF, 0x027B0300 },
+       };
+       int i;
+
+       for (i = 0; i < 8; i++) {
+               write_firh_reg(plane, i, coef[i][0]);
+               write_firhv_reg(plane, i, coef[i][1]);
+       }
+}
+
+static int omap_dispc_set_scale(int plane,
+                               int orig_width, int orig_height,
+                               int out_width, int out_height)
+{
+       const u32 at_reg[]  = { 0, DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES,
+                                  DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES };
+       const u32 vs_reg[]  = { 0, DISPC_VID1_BASE + DISPC_VID_SIZE,
+                                  DISPC_VID2_BASE + DISPC_VID_SIZE };
+       const u32 fir_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_FIR,
+                                  DISPC_VID2_BASE + DISPC_VID_FIR };
+
+       u32 l;
+       int fir_hinc;
+       int fir_vinc;
+
+       if ((unsigned)plane > OMAPFB_PLANE_NUM)
+               return -ENODEV;
+
+       if (plane == OMAPFB_PLANE_GFX &&
+           (out_width != orig_width || out_height != orig_height))
+               return -EINVAL;
+
+       enable_lcd_clocks(1);
+       if (orig_width < out_width) {
+               /* Upsampling.
+                * Currently you can only scale both dimensions in one way.
+                */
+               if (orig_height > out_height ||
+                   orig_width * 8 < out_width ||
+                   orig_height * 8 < out_height) {
+                       enable_lcd_clocks(0);
+                       return -EINVAL;
+               }
+               set_upsampling_coef_table(plane);
+       } else if (orig_width > out_width) {
+               /* Downsampling not yet supported
+               */
+
+               enable_lcd_clocks(0);
+               return -EINVAL;
+       }
+       if (!orig_width || orig_width == out_width)
+               fir_hinc = 0;
+       else
+               fir_hinc = 1024 * orig_width / out_width;
+       if (!orig_height || orig_height == out_height)
+               fir_vinc = 0;
+       else
+               fir_vinc = 1024 * orig_height / out_height;
+       dispc.fir_hinc[plane] = fir_hinc;
+       dispc.fir_vinc[plane] = fir_vinc;
+
+       MOD_REG_FLD(fir_reg[plane],
+                   FLD_MASK(16, 12) | FLD_MASK(0, 12),
+                   ((fir_vinc & 4095) << 16) |
+                   (fir_hinc & 4095));
+
+       dev_dbg(dispc.fbdev->dev, "out_width %d out_height %d orig_width %d "
+               "orig_height %d fir_hinc  %d fir_vinc %d\n",
+               out_width, out_height, orig_width, orig_height,
+               fir_hinc, fir_vinc);
+
+       MOD_REG_FLD(vs_reg[plane],
+                   FLD_MASK(16, 11) | FLD_MASK(0, 11),
+                   ((out_height - 1) << 16) | (out_width - 1));
+
+       l = dispc_read_reg(at_reg[plane]);
+       l &= ~(0x03 << 5);
+       l |= fir_hinc ? (1 << 5) : 0;
+       l |= fir_vinc ? (1 << 6) : 0;
+       dispc_write_reg(at_reg[plane], l);
+
+       enable_lcd_clocks(0);
+       return 0;
+}
+
+static int omap_dispc_enable_plane(int plane, int enable)
+{
+       const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES,
+                               DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES,
+                               DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES };
+       if ((unsigned int)plane > dispc.mem_desc.region_cnt)
+               return -EINVAL;
+
+       enable_lcd_clocks(1);
+       MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0);
+       enable_lcd_clocks(0);
+
+       return 0;
+}
+
+static int omap_dispc_set_color_key(struct omapfb_color_key *ck)
+{
+       u32 df_reg, tr_reg;
+       int shift, val;
+
+       switch (ck->channel_out) {
+       case OMAPFB_CHANNEL_OUT_LCD:
+               df_reg = DISPC_DEFAULT_COLOR0;
+               tr_reg = DISPC_TRANS_COLOR0;
+               shift = 10;
+               break;
+       case OMAPFB_CHANNEL_OUT_DIGIT:
+               df_reg = DISPC_DEFAULT_COLOR1;
+               tr_reg = DISPC_TRANS_COLOR1;
+               shift = 12;
+               break;
+       default:
+               return -EINVAL;
+       }
+       switch (ck->key_type) {
+       case OMAPFB_COLOR_KEY_DISABLED:
+               val = 0;
+               break;
+       case OMAPFB_COLOR_KEY_GFX_DST:
+               val = 1;
+               break;
+       case OMAPFB_COLOR_KEY_VID_SRC:
+               val = 3;
+               break;
+       default:
+               return -EINVAL;
+       }
+       enable_lcd_clocks(1);
+       MOD_REG_FLD(DISPC_CONFIG, FLD_MASK(shift, 2), val << shift);
+
+       if (val != 0)
+               dispc_write_reg(tr_reg, ck->trans_key);
+       dispc_write_reg(df_reg, ck->background);
+       enable_lcd_clocks(0);
+
+       dispc.color_key = *ck;
+
+       return 0;
+}
+
+static int omap_dispc_get_color_key(struct omapfb_color_key *ck)
+{
+       *ck = dispc.color_key;
+       return 0;
+}
+
+static void load_palette(void)
+{
+}
+
+static int omap_dispc_set_update_mode(enum omapfb_update_mode mode)
+{
+       int r = 0;
+
+       if (mode != dispc.update_mode) {
+               switch (mode) {
+               case OMAPFB_AUTO_UPDATE:
+               case OMAPFB_MANUAL_UPDATE:
+                       enable_lcd_clocks(1);
+                       omap_dispc_enable_lcd_out(1);
+                       dispc.update_mode = mode;
+                       break;
+               case OMAPFB_UPDATE_DISABLED:
+                       init_completion(&dispc.frame_done);
+                       omap_dispc_enable_lcd_out(0);
+                       if (!wait_for_completion_timeout(&dispc.frame_done,
+                                       msecs_to_jiffies(500))) {
+                               dev_err(dispc.fbdev->dev,
+                                        "timeout waiting for FRAME DONE\n");
+                       }
+                       dispc.update_mode = mode;
+                       enable_lcd_clocks(0);
+                       break;
+               default:
+                       r = -EINVAL;
+               }
+       }
+
+       return r;
+}
+
+static void omap_dispc_get_caps(int plane, struct omapfb_caps *caps)
+{
+       caps->ctrl |= OMAPFB_CAPS_PLANE_RELOCATE_MEM;
+       if (plane > 0)
+               caps->ctrl |= OMAPFB_CAPS_PLANE_SCALE;
+       caps->plane_color |= (1 << OMAPFB_COLOR_RGB565) |
+                            (1 << OMAPFB_COLOR_YUV422) |
+                            (1 << OMAPFB_COLOR_YUY422);
+       if (plane == 0)
+               caps->plane_color |= (1 << OMAPFB_COLOR_CLUT_8BPP) |
+                                    (1 << OMAPFB_COLOR_CLUT_4BPP) |
+                                    (1 << OMAPFB_COLOR_CLUT_2BPP) |
+                                    (1 << OMAPFB_COLOR_CLUT_1BPP) |
+                                    (1 << OMAPFB_COLOR_RGB444);
+}
+
+static enum omapfb_update_mode omap_dispc_get_update_mode(void)
+{
+       return dispc.update_mode;
+}
+
+static void setup_color_conv_coef(void)
+{
+       u32 mask = FLD_MASK(16, 11) | FLD_MASK(0, 11);
+       int cf1_reg = DISPC_VID1_BASE + DISPC_VID_CONV_COEF0;
+       int cf2_reg = DISPC_VID2_BASE + DISPC_VID_CONV_COEF0;
+       int at1_reg = DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES;
+       int at2_reg = DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES;
+       const struct color_conv_coef {
+               int  ry,  rcr,  rcb,   gy,  gcr,  gcb,   by,  bcr,  bcb;
+               int  full_range;
+       }  ctbl_bt601_5 = {
+                   298,  409,    0,  298, -208, -100,  298,    0,  517, 0,
+       };
+#if 0
+       const struct color_conv_coef ctbl_bt601_5_full = {
+                   256,  351,    0,  256, -179,  -86,  256,    0,  443, 1,
+       }, ctbl_bt709 = {
+                   298,  459,    0,  298, -137,  -55,  298,    0,  541, 0,
+       }, ctbl_bt709_f = {
+                   256,  394,    0,  256, -118,  -47,  256,    0,  465, 1,     },
+#endif
+       const struct color_conv_coef *ct;
+#define CVAL(x, y)     (((x & 2047) << 16) | (y & 2047))
+
+       ct = &ctbl_bt601_5;
+
+       MOD_REG_FLD(cf1_reg,            mask,   CVAL(ct->rcr, ct->ry));
+       MOD_REG_FLD(cf1_reg + 4,        mask,   CVAL(ct->gy,  ct->rcb));
+       MOD_REG_FLD(cf1_reg + 8,        mask,   CVAL(ct->gcb, ct->gcr));
+       MOD_REG_FLD(cf1_reg + 12,       mask,   CVAL(ct->bcr, ct->by));
+       MOD_REG_FLD(cf1_reg + 16,       mask,   CVAL(0,       ct->bcb));
+
+       MOD_REG_FLD(cf2_reg,            mask,   CVAL(ct->rcr, ct->ry));
+       MOD_REG_FLD(cf2_reg + 4,        mask,   CVAL(ct->gy,  ct->rcb));
+       MOD_REG_FLD(cf2_reg + 8,        mask,   CVAL(ct->gcb, ct->gcr));
+       MOD_REG_FLD(cf2_reg + 12,       mask,   CVAL(ct->bcr, ct->by));
+       MOD_REG_FLD(cf2_reg + 16,       mask,   CVAL(0,       ct->bcb));
+#undef CVAL
+
+       MOD_REG_FLD(at1_reg, (1 << 11), ct->full_range);
+       MOD_REG_FLD(at2_reg, (1 << 11), ct->full_range);
+}
+
+static void calc_ck_div(int is_tft, int pck, int *lck_div, int *pck_div)
+{
+       unsigned long fck, lck;
+
+       *lck_div = 1;
+       pck = max(1, pck);
+       fck = clk_get_rate(dispc.dss1_fck);
+       lck = fck;
+       *pck_div = (lck + pck - 1) / pck;
+       if (is_tft)
+               *pck_div = max(2, *pck_div);
+       else
+               *pck_div = max(3, *pck_div);
+       if (*pck_div > 255) {
+               *pck_div = 255;
+               lck = pck * *pck_div;
+               *lck_div = fck / lck;
+               BUG_ON(*lck_div < 1);
+               if (*lck_div > 255) {
+                       *lck_div = 255;
+                       dev_warn(dispc.fbdev->dev, "pixclock %d kHz too low.\n",
+                                pck / 1000);
+               }
+       }
+}
+
+static void set_lcd_tft_mode(int enable)
+{
+       u32 mask;
+
+       mask = 1 << 3;
+       MOD_REG_FLD(DISPC_CONTROL, mask, enable ? mask : 0);
+}
+
+static void set_lcd_timings(void)
+{
+       u32 l;
+       int lck_div, pck_div;
+       struct lcd_panel *panel = dispc.fbdev->panel;
+       int is_tft = panel->config & OMAP_LCDC_PANEL_TFT;
+       unsigned long fck;
+
+       l = dispc_read_reg(DISPC_TIMING_H);
+       l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8));
+       l |= ( max(1, (min(64,  panel->hsw))) - 1 ) << 0;
+       l |= ( max(1, (min(256, panel->hfp))) - 1 ) << 8;
+       l |= ( max(1, (min(256, panel->hbp))) - 1 ) << 20;
+       dispc_write_reg(DISPC_TIMING_H, l);
+
+       l = dispc_read_reg(DISPC_TIMING_V);
+       l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8));
+       l |= ( max(1, (min(64,  panel->vsw))) - 1 ) << 0;
+       l |= ( max(0, (min(255, panel->vfp))) - 0 ) << 8;
+       l |= ( max(0, (min(255, panel->vbp))) - 0 ) << 20;
+       dispc_write_reg(DISPC_TIMING_V, l);
+
+       l = dispc_read_reg(DISPC_POL_FREQ);
+       l &= ~FLD_MASK(12, 6);
+       l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 12;
+       l |= panel->acb & 0xff;
+       dispc_write_reg(DISPC_POL_FREQ, l);
+
+       calc_ck_div(is_tft, panel->pixel_clock * 1000, &lck_div, &pck_div);
+
+       l = dispc_read_reg(DISPC_DIVISOR);
+       l &= ~(FLD_MASK(16, 8) | FLD_MASK(0, 8));
+       l |= (lck_div << 16) | (pck_div << 0);
+       dispc_write_reg(DISPC_DIVISOR, l);
+
+       /* update panel info with the exact clock */
+       fck = clk_get_rate(dispc.dss1_fck);
+       panel->pixel_clock = fck / lck_div / pck_div / 1000;
+}
+
+int omap_dispc_request_irq(void (*callback)(void *data), void *data)
+{
+       int r = 0;
+
+       BUG_ON(callback == NULL);
+
+       if (dispc.irq_callback)
+               r = -EBUSY;
+       else {
+               dispc.irq_callback = callback;
+               dispc.irq_callback_data = data;
+       }
+
+       return r;
+}
+EXPORT_SYMBOL(omap_dispc_request_irq);
+
+void omap_dispc_enable_irqs(int irq_mask)
+{
+       enable_lcd_clocks(1);
+       dispc.enabled_irqs = irq_mask;
+       irq_mask |= DISPC_IRQ_MASK_ERROR;
+       MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask);
+       enable_lcd_clocks(0);
+}
+EXPORT_SYMBOL(omap_dispc_enable_irqs);
+
+void omap_dispc_disable_irqs(int irq_mask)
+{
+       enable_lcd_clocks(1);
+       dispc.enabled_irqs &= ~irq_mask;
+       irq_mask &= ~DISPC_IRQ_MASK_ERROR;
+       MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask);
+       enable_lcd_clocks(0);
+}
+EXPORT_SYMBOL(omap_dispc_disable_irqs);
+
+void omap_dispc_free_irq(void)
+{
+       enable_lcd_clocks(1);
+       omap_dispc_disable_irqs(DISPC_IRQ_MASK_ALL);
+       dispc.irq_callback = NULL;
+       dispc.irq_callback_data = NULL;
+       enable_lcd_clocks(0);
+}
+EXPORT_SYMBOL(omap_dispc_free_irq);
+
+static irqreturn_t omap_dispc_irq_handler(int irq, void *dev)
+{
+       u32 stat = dispc_read_reg(DISPC_IRQSTATUS);
+
+       if (stat & DISPC_IRQ_FRAMEMASK)
+               complete(&dispc.frame_done);
+
+       if (stat & DISPC_IRQ_MASK_ERROR) {
+               if (printk_ratelimit()) {
+                       dev_err(dispc.fbdev->dev, "irq error status %04x\n",
+                               stat & 0x7fff);
+               }
+       }
+
+       if ((stat & dispc.enabled_irqs) && dispc.irq_callback)
+               dispc.irq_callback(dispc.irq_callback_data);
+
+       dispc_write_reg(DISPC_IRQSTATUS, stat);
+
+       return IRQ_HANDLED;
+}
+
+static int get_dss_clocks(void)
+{
+       if (IS_ERR((dispc.dss_ick = clk_get(dispc.fbdev->dev, "dss_ick")))) {
+               dev_err(dispc.fbdev->dev, "can't get dss_ick");
+               return PTR_ERR(dispc.dss_ick);
+       }
+
+       if (IS_ERR((dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck")))) {
+               dev_err(dispc.fbdev->dev, "can't get dss1_fck");
+               clk_put(dispc.dss_ick);
+               return PTR_ERR(dispc.dss1_fck);
+       }
+
+       if (IS_ERR((dispc.dss_54m_fck =
+                               clk_get(dispc.fbdev->dev, "dss_54m_fck")))) {
+               dev_err(dispc.fbdev->dev, "can't get dss_54m_fck");
+               clk_put(dispc.dss_ick);
+               clk_put(dispc.dss1_fck);
+               return PTR_ERR(dispc.dss_54m_fck);
+       }
+
+       return 0;
+}
+
+static void put_dss_clocks(void)
+{
+       clk_put(dispc.dss_54m_fck);
+       clk_put(dispc.dss1_fck);
+       clk_put(dispc.dss_ick);
+}
+
+static void enable_lcd_clocks(int enable)
+{
+       if (enable)
+               clk_enable(dispc.dss1_fck);
+       else
+               clk_disable(dispc.dss1_fck);
+}
+
+static void enable_interface_clocks(int enable)
+{
+       if (enable)
+               clk_enable(dispc.dss_ick);
+       else
+               clk_disable(dispc.dss_ick);
+}
+
+static void enable_digit_clocks(int enable)
+{
+       if (enable)
+               clk_enable(dispc.dss_54m_fck);
+       else
+               clk_disable(dispc.dss_54m_fck);
+}
+
+static void omap_dispc_suspend(void)
+{
+       if (dispc.update_mode == OMAPFB_AUTO_UPDATE) {
+               init_completion(&dispc.frame_done);
+               omap_dispc_enable_lcd_out(0);
+               if (!wait_for_completion_timeout(&dispc.frame_done,
+                               msecs_to_jiffies(500))) {
+                       dev_err(dispc.fbdev->dev,
+                               "timeout waiting for FRAME DONE\n");
+               }
+               enable_lcd_clocks(0);
+       }
+}
+
+static void omap_dispc_resume(void)
+{
+       if (dispc.update_mode == OMAPFB_AUTO_UPDATE) {
+               enable_lcd_clocks(1);
+               if (!dispc.ext_mode) {
+                       set_lcd_timings();
+                       load_palette();
+               }
+               omap_dispc_enable_lcd_out(1);
+       }
+}
+
+
+static int omap_dispc_update_window(struct fb_info *fbi,
+                                struct omapfb_update_window *win,
+                                void (*complete_callback)(void *arg),
+                                void *complete_callback_data)
+{
+       return dispc.update_mode == OMAPFB_UPDATE_DISABLED ? -ENODEV : 0;
+}
+
+static int mmap_kern(struct omapfb_mem_region *region)
+{
+       struct vm_struct        *kvma;
+       struct vm_area_struct   vma;
+       pgprot_t                pgprot;
+       unsigned long           vaddr;
+
+       kvma = get_vm_area(region->size, VM_IOREMAP);
+       if (kvma == NULL) {
+               dev_err(dispc.fbdev->dev, "can't get kernel vm area\n");
+               return -ENOMEM;
+       }
+       vma.vm_mm = &init_mm;
+
+       vaddr = (unsigned long)kvma->addr;
+
+       pgprot = pgprot_writecombine(pgprot_kernel);
+       vma.vm_start = vaddr;
+       vma.vm_end = vaddr + region->size;
+       if (io_remap_pfn_range(&vma, vaddr, region->paddr >> PAGE_SHIFT,
+                          region->size, pgprot) < 0) {
+               dev_err(dispc.fbdev->dev, "kernel mmap for FBMEM failed\n");
+               return -EAGAIN;
+       }
+       region->vaddr = (void *)vaddr;
+
+       return 0;
+}
+
+static void mmap_user_open(struct vm_area_struct *vma)
+{
+       int plane = (int)vma->vm_private_data;
+
+       atomic_inc(&dispc.map_count[plane]);
+}
+
+static void mmap_user_close(struct vm_area_struct *vma)
+{
+       int plane = (int)vma->vm_private_data;
+
+       atomic_dec(&dispc.map_count[plane]);
+}
+
+static struct vm_operations_struct mmap_user_ops = {
+       .open = mmap_user_open,
+       .close = mmap_user_close,
+};
+
+static int omap_dispc_mmap_user(struct fb_info *info,
+                               struct vm_area_struct *vma)
+{
+       struct omapfb_plane_struct *plane = info->par;
+       unsigned long off;
+       unsigned long start;
+       u32 len;
+
+       if (vma->vm_end - vma->vm_start == 0)
+               return 0;
+       if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+               return -EINVAL;
+       off = vma->vm_pgoff << PAGE_SHIFT;
+
+       start = info->fix.smem_start;
+       len = info->fix.smem_len;
+       if (off >= len)
+               return -EINVAL;
+       if ((vma->vm_end - vma->vm_start + off) > len)
+               return -EINVAL;
+       off += start;
+       vma->vm_pgoff = off >> PAGE_SHIFT;
+       vma->vm_flags |= VM_IO | VM_RESERVED;
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+       vma->vm_ops = &mmap_user_ops;
+       vma->vm_private_data = (void *)plane->idx;
+       if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
+                            vma->vm_end - vma->vm_start, vma->vm_page_prot))
+               return -EAGAIN;
+       /* vm_ops.open won't be called for mmap itself. */
+       atomic_inc(&dispc.map_count[plane->idx]);
+       return 0;
+}
+
+static void unmap_kern(struct omapfb_mem_region *region)
+{
+       vunmap(region->vaddr);
+}
+
+static int alloc_palette_ram(void)
+{
+       dispc.palette_vaddr = dma_alloc_writecombine(dispc.fbdev->dev,
+               MAX_PALETTE_SIZE, &dispc.palette_paddr, GFP_KERNEL);
+       if (dispc.palette_vaddr == NULL) {
+               dev_err(dispc.fbdev->dev, "failed to alloc palette memory\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void free_palette_ram(void)
+{
+       dma_free_writecombine(dispc.fbdev->dev, MAX_PALETTE_SIZE,
+                       dispc.palette_vaddr, dispc.palette_paddr);
+}
+
+static int alloc_fbmem(struct omapfb_mem_region *region)
+{
+       region->vaddr = dma_alloc_writecombine(dispc.fbdev->dev,
+                       region->size, &region->paddr, GFP_KERNEL);
+
+       if (region->vaddr == NULL) {
+               dev_err(dispc.fbdev->dev, "unable to allocate FB DMA memory\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void free_fbmem(struct omapfb_mem_region *region)
+{
+       dma_free_writecombine(dispc.fbdev->dev, region->size,
+                             region->vaddr, region->paddr);
+}
+
+static struct resmap *init_resmap(unsigned long start, size_t size)
+{
+       unsigned page_cnt;
+       struct resmap *res_map;
+
+       page_cnt = PAGE_ALIGN(size) / PAGE_SIZE;
+       res_map =
+           kzalloc(sizeof(struct resmap) + RESMAP_SIZE(page_cnt), GFP_KERNEL);
+       if (res_map == NULL)
+               return NULL;
+       res_map->start = start;
+       res_map->page_cnt = page_cnt;
+       res_map->map = (unsigned long *)(res_map + 1);
+       return res_map;
+}
+
+static void cleanup_resmap(struct resmap *res_map)
+{
+       kfree(res_map);
+}
+
+static inline int resmap_mem_type(unsigned long start)
+{
+       if (start >= OMAP2_SRAM_START &&
+           start < OMAP2_SRAM_START + OMAP2_SRAM_SIZE)
+               return OMAPFB_MEMTYPE_SRAM;
+       else
+               return OMAPFB_MEMTYPE_SDRAM;
+}
+
+static inline int resmap_page_reserved(struct resmap *res_map, unsigned page_nr)
+{
+       return *RESMAP_PTR(res_map, page_nr) & RESMAP_MASK(page_nr) ? 1 : 0;
+}
+
+static inline void resmap_reserve_page(struct resmap *res_map, unsigned page_nr)
+{
+       BUG_ON(resmap_page_reserved(res_map, page_nr));
+       *RESMAP_PTR(res_map, page_nr) |= RESMAP_MASK(page_nr);
+}
+
+static inline void resmap_free_page(struct resmap *res_map, unsigned page_nr)
+{
+       BUG_ON(!resmap_page_reserved(res_map, page_nr));
+       *RESMAP_PTR(res_map, page_nr) &= ~RESMAP_MASK(page_nr);
+}
+
+static void resmap_reserve_region(unsigned long start, size_t size)
+{
+
+       struct resmap   *res_map;
+       unsigned        start_page;
+       unsigned        end_page;
+       int             mtype;
+       unsigned        i;
+
+       mtype = resmap_mem_type(start);
+       res_map = dispc.res_map[mtype];
+       dev_dbg(dispc.fbdev->dev, "reserve mem type %d start %08lx size %d\n",
+               mtype, start, size);
+       start_page = (start - res_map->start) / PAGE_SIZE;
+       end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE;
+       for (i = start_page; i < end_page; i++)
+               resmap_reserve_page(res_map, i);
+}
+
+static void resmap_free_region(unsigned long start, size_t size)
+{
+       struct resmap   *res_map;
+       unsigned        start_page;
+       unsigned        end_page;
+       unsigned        i;
+       int             mtype;
+
+       mtype = resmap_mem_type(start);
+       res_map = dispc.res_map[mtype];
+       dev_dbg(dispc.fbdev->dev, "free mem type %d start %08lx size %d\n",
+               mtype, start, size);
+       start_page = (start - res_map->start) / PAGE_SIZE;
+       end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE;
+       for (i = start_page; i < end_page; i++)
+               resmap_free_page(res_map, i);
+}
+
+static unsigned long resmap_alloc_region(int mtype, size_t size)
+{
+       unsigned i;
+       unsigned total;
+       unsigned start_page;
+       unsigned long start;
+       struct resmap *res_map = dispc.res_map[mtype];
+
+       BUG_ON(mtype >= DISPC_MEMTYPE_NUM || res_map == NULL || !size);
+
+       size = PAGE_ALIGN(size) / PAGE_SIZE;
+       start_page = 0;
+       total = 0;
+       for (i = 0; i < res_map->page_cnt; i++) {
+               if (resmap_page_reserved(res_map, i)) {
+                       start_page = i + 1;
+                       total = 0;
+               } else if (++total == size)
+                       break;
+       }
+       if (total < size)
+               return 0;
+
+       start = res_map->start + start_page * PAGE_SIZE;
+       resmap_reserve_region(start, size * PAGE_SIZE);
+
+       return start;
+}
+
+/* Note that this will only work for user mappings, we don't deal with
+ * kernel mappings here, so fbcon will keep using the old region.
+ */
+static int omap_dispc_setup_mem(int plane, size_t size, int mem_type,
+                               unsigned long *paddr)
+{
+       struct omapfb_mem_region *rg;
+       unsigned long new_addr = 0;
+
+       if ((unsigned)plane > dispc.mem_desc.region_cnt)
+               return -EINVAL;
+       if (mem_type >= DISPC_MEMTYPE_NUM)
+               return -EINVAL;
+       if (dispc.res_map[mem_type] == NULL)
+               return -ENOMEM;
+       rg = &dispc.mem_desc.region[plane];
+       if (size == rg->size && mem_type == rg->type)
+               return 0;
+       if (atomic_read(&dispc.map_count[plane]))
+               return -EBUSY;
+       if (rg->size != 0)
+               resmap_free_region(rg->paddr, rg->size);
+       if (size != 0) {
+               new_addr = resmap_alloc_region(mem_type, size);
+               if (!new_addr) {
+                       /* Reallocate old region. */
+                       resmap_reserve_region(rg->paddr, rg->size);
+                       return -ENOMEM;
+               }
+       }
+       rg->paddr = new_addr;
+       rg->size = size;
+       rg->type = mem_type;
+
+       *paddr = new_addr;
+
+       return 0;
+}
+
+static int setup_fbmem(struct omapfb_mem_desc *req_md)
+{
+       struct omapfb_mem_region        *rg;
+       int i;
+       int r;
+       unsigned long                   mem_start[DISPC_MEMTYPE_NUM];
+       unsigned long                   mem_end[DISPC_MEMTYPE_NUM];
+
+       if (!req_md->region_cnt) {
+               dev_err(dispc.fbdev->dev, "no memory regions defined\n");
+               return -ENOENT;
+       }
+
+       rg = &req_md->region[0];
+       memset(mem_start, 0xff, sizeof(mem_start));
+       memset(mem_end, 0, sizeof(mem_end));
+
+       for (i = 0; i < req_md->region_cnt; i++, rg++) {
+               int mtype;
+               if (rg->paddr) {
+                       rg->alloc = 0;
+                       if (rg->vaddr == NULL) {
+                               rg->map = 1;
+                               if ((r = mmap_kern(rg)) < 0)
+                                       return r;
+                       }
+               } else {
+                       if (rg->type != OMAPFB_MEMTYPE_SDRAM) {
+                               dev_err(dispc.fbdev->dev,
+                                       "unsupported memory type\n");
+                               return -EINVAL;
+                       }
+                       rg->alloc = rg->map = 1;
+                       if ((r = alloc_fbmem(rg)) < 0)
+                               return r;
+               }
+               mtype = rg->type;
+
+               if (rg->paddr < mem_start[mtype])
+                       mem_start[mtype] = rg->paddr;
+               if (rg->paddr + rg->size > mem_end[mtype])
+                       mem_end[mtype] = rg->paddr + rg->size;
+       }
+
+       for (i = 0; i < DISPC_MEMTYPE_NUM; i++) {
+               unsigned long start;
+               size_t size;
+               if (mem_end[i] == 0)
+                       continue;
+               start = mem_start[i];
+               size = mem_end[i] - start;
+               dispc.res_map[i] = init_resmap(start, size);
+               r = -ENOMEM;
+               if (dispc.res_map[i] == NULL)
+                       goto fail;
+               /* Initial state is that everything is reserved. This
+                * includes possible holes as well, which will never be
+                * freed.
+                */
+               resmap_reserve_region(start, size);
+       }
+
+       dispc.mem_desc = *req_md;
+
+       return 0;
+fail:
+       for (i = 0; i < DISPC_MEMTYPE_NUM; i++) {
+               if (dispc.res_map[i] != NULL)
+                       cleanup_resmap(dispc.res_map[i]);
+       }
+       return r;
+}
+
+static void cleanup_fbmem(void)
+{
+       struct omapfb_mem_region *rg;
+       int i;
+
+       for (i = 0; i < DISPC_MEMTYPE_NUM; i++) {
+               if (dispc.res_map[i] != NULL)
+                       cleanup_resmap(dispc.res_map[i]);
+       }
+       rg = &dispc.mem_desc.region[0];
+       for (i = 0; i < dispc.mem_desc.region_cnt; i++, rg++) {
+               if (rg->alloc)
+                       free_fbmem(rg);
+               else {
+                       if (rg->map)
+                               unmap_kern(rg);
+               }
+       }
+}
+
+static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
+                          struct omapfb_mem_desc *req_vram)
+{
+       int r;
+       u32 l;
+       struct lcd_panel *panel = fbdev->panel;
+       int tmo = 10000;
+       int skip_init = 0;
+       int i;
+
+       memset(&dispc, 0, sizeof(dispc));
+
+       dispc.base = io_p2v(DISPC_BASE);
+       dispc.fbdev = fbdev;
+       dispc.ext_mode = ext_mode;
+
+       init_completion(&dispc.frame_done);
+
+       if ((r = get_dss_clocks()) < 0)
+               return r;
+
+       enable_interface_clocks(1);
+       enable_lcd_clocks(1);
+
+#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
+       l = dispc_read_reg(DISPC_CONTROL);
+       /* LCD enabled ? */
+       if (l & 1) {
+               pr_info("omapfb: skipping hardware initialization\n");
+               skip_init = 1;
+       }
+#endif
+
+       if (!skip_init) {
+               /* Reset monitoring works only w/ the 54M clk */
+               enable_digit_clocks(1);
+
+               /* Soft reset */
+               MOD_REG_FLD(DISPC_SYSCONFIG, 1 << 1, 1 << 1);
+
+               while (!(dispc_read_reg(DISPC_SYSSTATUS) & 1)) {
+                       if (!--tmo) {
+                               dev_err(dispc.fbdev->dev, "soft reset failed\n");
+                               r = -ENODEV;
+                               enable_digit_clocks(0);
+                               goto fail1;
+                       }
+               }
+
+               enable_digit_clocks(0);
+       }
+
+       /* Enable smart idle and autoidle */
+       l = dispc_read_reg(DISPC_CONTROL);
+       l &= ~((3 << 12) | (3 << 3));
+       l |= (2 << 12) | (2 << 3) | (1 << 0);
+       dispc_write_reg(DISPC_SYSCONFIG, l);
+       omap_writel(1 << 0, DSS_BASE + DSS_SYSCONFIG);
+
+       /* Set functional clock autogating */
+       l = dispc_read_reg(DISPC_CONFIG);
+       l |= 1 << 9;
+       dispc_write_reg(DISPC_CONFIG, l);
+
+       l = dispc_read_reg(DISPC_IRQSTATUS);
+       dispc_write_reg(l, DISPC_IRQSTATUS);
+
+       /* Enable those that we handle always */
+       omap_dispc_enable_irqs(DISPC_IRQ_FRAMEMASK);
+
+       if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler,
+                          0, MODULE_NAME, fbdev)) < 0) {
+               dev_err(dispc.fbdev->dev, "can't get DSS IRQ\n");
+               goto fail1;
+       }
+
+       /* L3 firewall setting: enable access to OCM RAM */
+       __raw_writel(0x402000b0, io_p2v(0x680050a0));
+
+       if ((r = alloc_palette_ram()) < 0)
+               goto fail2;
+
+       if ((r = setup_fbmem(req_vram)) < 0)
+               goto fail3;
+
+       if (!skip_init) {
+               for (i = 0; i < dispc.mem_desc.region_cnt; i++) {
+                       memset(dispc.mem_desc.region[i].vaddr, 0,
+                               dispc.mem_desc.region[i].size);
+               }
+
+               /* Set logic clock to fck, pixel clock to fck/2 for now */
+               MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(16, 8), 1 << 16);
+               MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(0, 8), 2 << 0);
+
+               setup_plane_fifo(0, ext_mode);
+               setup_plane_fifo(1, ext_mode);
+               setup_plane_fifo(2, ext_mode);
+
+               setup_color_conv_coef();
+
+               set_lcd_tft_mode(panel->config & OMAP_LCDC_PANEL_TFT);
+               set_load_mode(DISPC_LOAD_FRAME_ONLY);
+
+               if (!ext_mode) {
+                       set_lcd_data_lines(panel->data_lines);
+                       omap_dispc_set_lcd_size(panel->x_res, panel->y_res);
+                       set_lcd_timings();
+               } else
+                       set_lcd_data_lines(panel->bpp);
+               enable_rfbi_mode(ext_mode);
+       }
+
+       l = dispc_read_reg(DISPC_REVISION);
+       pr_info("omapfb: DISPC version %d.%d initialized\n",
+                l >> 4 & 0x0f, l & 0x0f);
+       enable_lcd_clocks(0);
+
+       return 0;
+fail3:
+       free_palette_ram();
+fail2:
+       free_irq(INT_24XX_DSS_IRQ, fbdev);
+fail1:
+       enable_lcd_clocks(0);
+       enable_interface_clocks(0);
+       put_dss_clocks();
+
+       return r;
+}
+
+static void omap_dispc_cleanup(void)
+{
+       int i;
+
+       omap_dispc_set_update_mode(OMAPFB_UPDATE_DISABLED);
+       /* This will also disable clocks that are on */
+       for (i = 0; i < dispc.mem_desc.region_cnt; i++)
+               omap_dispc_enable_plane(i, 0);
+       cleanup_fbmem();
+       free_palette_ram();
+       free_irq(INT_24XX_DSS_IRQ, dispc.fbdev);
+       enable_interface_clocks(0);
+       put_dss_clocks();
+}
+
+const struct lcd_ctrl omap2_int_ctrl = {
+       .name                   = "internal",
+       .init                   = omap_dispc_init,
+       .cleanup                = omap_dispc_cleanup,
+       .get_caps               = omap_dispc_get_caps,
+       .set_update_mode        = omap_dispc_set_update_mode,
+       .get_update_mode        = omap_dispc_get_update_mode,
+       .update_window          = omap_dispc_update_window,
+       .suspend                = omap_dispc_suspend,
+       .resume                 = omap_dispc_resume,
+       .setup_plane            = omap_dispc_setup_plane,
+       .setup_mem              = omap_dispc_setup_mem,
+       .set_scale              = omap_dispc_set_scale,
+       .enable_plane           = omap_dispc_enable_plane,
+       .set_color_key          = omap_dispc_set_color_key,
+       .get_color_key          = omap_dispc_get_color_key,
+       .mmap                   = omap_dispc_mmap_user,
+};
diff --git a/drivers/video/omap/dispc.h b/drivers/video/omap/dispc.h
new file mode 100644 (file)
index 0000000..eb1512b
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef _DISPC_H
+#define _DISPC_H
+
+#include <linux/interrupt.h>
+
+#define DISPC_PLANE_GFX                        0
+#define DISPC_PLANE_VID1               1
+#define DISPC_PLANE_VID2               2
+
+#define DISPC_RGB_1_BPP                        0x00
+#define DISPC_RGB_2_BPP                        0x01
+#define DISPC_RGB_4_BPP                        0x02
+#define DISPC_RGB_8_BPP                        0x03
+#define DISPC_RGB_12_BPP               0x04
+#define DISPC_RGB_16_BPP               0x06
+#define DISPC_RGB_24_BPP               0x08
+#define DISPC_RGB_24_BPP_UNPACK_32     0x09
+#define DISPC_YUV2_422                 0x0a
+#define DISPC_UYVY_422                 0x0b
+
+#define DISPC_BURST_4x32               0
+#define DISPC_BURST_8x32               1
+#define DISPC_BURST_16x32              2
+
+#define DISPC_LOAD_CLUT_AND_FRAME      0x00
+#define DISPC_LOAD_CLUT_ONLY           0x01
+#define DISPC_LOAD_FRAME_ONLY          0x02
+#define DISPC_LOAD_CLUT_ONCE_FRAME     0x03
+
+#define DISPC_TFT_DATA_LINES_12                0
+#define DISPC_TFT_DATA_LINES_16                1
+#define DISPC_TFT_DATA_LINES_18                2
+#define DISPC_TFT_DATA_LINES_24                3
+
+extern void omap_dispc_set_lcd_size(int width, int height);
+
+extern void omap_dispc_enable_lcd_out(int enable);
+extern void omap_dispc_enable_digit_out(int enable);
+
+extern int  omap_dispc_request_irq(void (*callback)(void *data), void *data);
+extern void omap_dispc_free_irq(void);
+
+#endif
diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c
new file mode 100644 (file)
index 0000000..5d92f59
--- /dev/null
@@ -0,0 +1,1072 @@
+/*
+ * File: drivers/video/omap/hwa742.c
+ *
+ * Epson HWA742 LCD controller driver
+ *
+ * Copyright (C) 2004-2005 Nokia Corporation
+ * Authors:     Juha Yrjölä   <juha.yrjola@nokia.com>
+ *             Imre Deak     <imre.deak@nokia.com>
+ * YUV support: Jussi Laako   <jussi.laako@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <asm/arch/dma.h>
+#include <asm/arch/omapfb.h>
+#include <asm/arch/hwa742.h>
+
+#define HWA742_REV_CODE_REG       0x0
+#define HWA742_CONFIG_REG         0x2
+#define HWA742_PLL_DIV_REG        0x4
+#define HWA742_PLL_0_REG          0x6
+#define HWA742_PLL_1_REG          0x8
+#define HWA742_PLL_2_REG          0xa
+#define HWA742_PLL_3_REG          0xc
+#define HWA742_PLL_4_REG          0xe
+#define HWA742_CLK_SRC_REG        0x12
+#define HWA742_PANEL_TYPE_REG     0x14
+#define HWA742_H_DISP_REG         0x16
+#define HWA742_H_NDP_REG          0x18
+#define HWA742_V_DISP_1_REG       0x1a
+#define HWA742_V_DISP_2_REG       0x1c
+#define HWA742_V_NDP_REG          0x1e
+#define HWA742_HS_W_REG           0x20
+#define HWA742_HP_S_REG           0x22
+#define HWA742_VS_W_REG           0x24
+#define HWA742_VP_S_REG           0x26
+#define HWA742_PCLK_POL_REG       0x28
+#define HWA742_INPUT_MODE_REG     0x2a
+#define HWA742_TRANSL_MODE_REG1   0x2e
+#define HWA742_DISP_MODE_REG      0x34
+#define HWA742_WINDOW_TYPE        0x36
+#define HWA742_WINDOW_X_START_0   0x38
+#define HWA742_WINDOW_X_START_1   0x3a
+#define HWA742_WINDOW_Y_START_0   0x3c
+#define HWA742_WINDOW_Y_START_1   0x3e
+#define HWA742_WINDOW_X_END_0     0x40
+#define HWA742_WINDOW_X_END_1     0x42
+#define HWA742_WINDOW_Y_END_0     0x44
+#define HWA742_WINDOW_Y_END_1     0x46
+#define HWA742_MEMORY_WRITE_LSB   0x48
+#define HWA742_MEMORY_WRITE_MSB   0x49
+#define HWA742_MEMORY_READ_0      0x4a
+#define HWA742_MEMORY_READ_1      0x4c
+#define HWA742_MEMORY_READ_2      0x4e
+#define HWA742_POWER_SAVE         0x56
+#define HWA742_NDP_CTRL           0x58
+
+#define HWA742_AUTO_UPDATE_TIME                (HZ / 20)
+
+/* Reserve 4 request slots for requests in irq context */
+#define REQ_POOL_SIZE                  24
+#define IRQ_REQ_POOL_SIZE              4
+
+#define REQ_FROM_IRQ_POOL 0x01
+
+#define REQ_COMPLETE   0
+#define REQ_PENDING    1
+
+struct update_param {
+       int     x, y, width, height;
+       int     color_mode;
+       int     flags;
+};
+
+struct hwa742_request {
+       struct list_head entry;
+       unsigned int     flags;
+
+       int              (*handler)(struct hwa742_request *req);
+       void             (*complete)(void *data);
+       void             *complete_data;
+
+       union {
+               struct update_param     update;
+               struct completion       *sync;
+       } par;
+};
+
+struct {
+       enum omapfb_update_mode update_mode;
+       enum omapfb_update_mode update_mode_before_suspend;
+
+       struct timer_list       auto_update_timer;
+       int                     stop_auto_update;
+       struct omapfb_update_window     auto_update_window;
+       unsigned                te_connected:1;
+       unsigned                vsync_only:1;
+
+       struct hwa742_request   req_pool[REQ_POOL_SIZE];
+       struct list_head        pending_req_list;
+       struct list_head        free_req_list;
+       struct semaphore        req_sema;
+       spinlock_t              req_lock;
+
+       struct extif_timings    reg_timings, lut_timings;
+
+       int                     prev_color_mode;
+       int                     prev_flags;
+       int                     window_type;
+
+       u32                     max_transmit_size;
+       u32                     extif_clk_period;
+       unsigned long           pix_tx_time;
+       unsigned long           line_upd_time;
+
+
+       struct omapfb_device    *fbdev;
+       struct lcd_ctrl_extif   *extif;
+       struct lcd_ctrl         *int_ctrl;
+
+       void                    (*power_up)(struct device *dev);
+       void                    (*power_down)(struct device *dev);
+} hwa742;
+
+struct lcd_ctrl hwa742_ctrl;
+
+static u8 hwa742_read_reg(u8 reg)
+{
+       u8 data;
+
+       hwa742.extif->set_bits_per_cycle(8);
+       hwa742.extif->write_command(&reg, 1);
+       hwa742.extif->read_data(&data, 1);
+
+       return data;
+}
+
+static void hwa742_write_reg(u8 reg, u8 data)
+{
+       hwa742.extif->set_bits_per_cycle(8);
+       hwa742.extif->write_command(&reg, 1);
+       hwa742.extif->write_data(&data, 1);
+}
+
+static void set_window_regs(int x_start, int y_start, int x_end, int y_end)
+{
+       u8 tmp[8];
+       u8 cmd;
+
+       x_end--;
+       y_end--;
+       tmp[0] = x_start;
+       tmp[1] = x_start >> 8;
+       tmp[2] = y_start;
+       tmp[3] = y_start >> 8;
+       tmp[4] = x_end;
+       tmp[5] = x_end >> 8;
+       tmp[6] = y_end;
+       tmp[7] = y_end >> 8;
+
+       hwa742.extif->set_bits_per_cycle(8);
+       cmd = HWA742_WINDOW_X_START_0;
+
+       hwa742.extif->write_command(&cmd, 1);
+
+       hwa742.extif->write_data(tmp, 8);
+}
+
+static void set_format_regs(int conv, int transl, int flags)
+{
+       if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) {
+               hwa742.window_type = ((hwa742.window_type & 0xfc) | 0x01);
+#ifdef VERBOSE
+               dev_dbg(hwa742.fbdev->dev, "hwa742: enabled pixel doubling\n");
+#endif
+       } else {
+               hwa742.window_type = (hwa742.window_type & 0xfc);
+#ifdef VERBOSE
+               dev_dbg(hwa742.fbdev->dev, "hwa742: disabled pixel doubling\n");
+#endif
+       }
+
+       hwa742_write_reg(HWA742_INPUT_MODE_REG, conv);
+       hwa742_write_reg(HWA742_TRANSL_MODE_REG1, transl);
+       hwa742_write_reg(HWA742_WINDOW_TYPE, hwa742.window_type);
+}
+
+static void enable_tearsync(int y, int width, int height, int screen_height,
+                           int force_vsync)
+{
+       u8 b;
+
+       b = hwa742_read_reg(HWA742_NDP_CTRL);
+       b |= 1 << 2;
+       hwa742_write_reg(HWA742_NDP_CTRL, b);
+
+       if (likely(hwa742.vsync_only || force_vsync)) {
+               hwa742.extif->enable_tearsync(1, 0);
+               return;
+       }
+
+       if (width * hwa742.pix_tx_time < hwa742.line_upd_time) {
+               hwa742.extif->enable_tearsync(1, 0);
+               return;
+       }
+
+       if ((width * hwa742.pix_tx_time / 1000) * height <
+           (y + height) * (hwa742.line_upd_time / 1000)) {
+               hwa742.extif->enable_tearsync(1, 0);
+               return;
+       }
+
+       hwa742.extif->enable_tearsync(1, y + 1);
+}
+
+static void disable_tearsync(void)
+{
+       u8 b;
+
+       hwa742.extif->enable_tearsync(0, 0);
+
+       b = hwa742_read_reg(HWA742_NDP_CTRL);
+       b &= ~(1 << 2);
+       hwa742_write_reg(HWA742_NDP_CTRL, b);
+}
+
+static inline struct hwa742_request *alloc_req(void)
+{
+       unsigned long flags;
+       struct hwa742_request *req;
+       int req_flags = 0;
+
+       if (!in_interrupt())
+               down(&hwa742.req_sema);
+       else
+               req_flags = REQ_FROM_IRQ_POOL;
+
+       spin_lock_irqsave(&hwa742.req_lock, flags);
+       BUG_ON(list_empty(&hwa742.free_req_list));
+       req = list_entry(hwa742.free_req_list.next,
+                        struct hwa742_request, entry);
+       list_del(&req->entry);
+       spin_unlock_irqrestore(&hwa742.req_lock, flags);
+
+       INIT_LIST_HEAD(&req->entry);
+       req->flags = req_flags;
+
+       return req;
+}
+
+static inline void free_req(struct hwa742_request *req)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&hwa742.req_lock, flags);
+
+       list_del(&req->entry);
+       list_add(&req->entry, &hwa742.free_req_list);
+       if (!(req->flags & REQ_FROM_IRQ_POOL))
+               up(&hwa742.req_sema);
+
+       spin_unlock_irqrestore(&hwa742.req_lock, flags);
+}
+
+static void process_pending_requests(void)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&hwa742.req_lock, flags);
+
+       while (!list_empty(&hwa742.pending_req_list)) {
+               struct hwa742_request *req;
+               void (*complete)(void *);
+               void *complete_data;
+
+               req = list_entry(hwa742.pending_req_list.next,
+                                struct hwa742_request, entry);
+               spin_unlock_irqrestore(&hwa742.req_lock, flags);
+
+               if (req->handler(req) == REQ_PENDING)
+                       return;
+
+               complete = req->complete;
+               complete_data = req->complete_data;
+               free_req(req);
+
+               if (complete)
+                       complete(complete_data);
+
+               spin_lock_irqsave(&hwa742.req_lock, flags);
+       }
+
+       spin_unlock_irqrestore(&hwa742.req_lock, flags);
+}
+
+static void submit_req_list(struct list_head *head)
+{
+       unsigned long flags;
+       int process = 1;
+
+       spin_lock_irqsave(&hwa742.req_lock, flags);
+       if (likely(!list_empty(&hwa742.pending_req_list)))
+               process = 0;
+       list_splice_init(head, hwa742.pending_req_list.prev);
+       spin_unlock_irqrestore(&hwa742.req_lock, flags);
+
+       if (process)
+               process_pending_requests();
+}
+
+static void request_complete(void *data)
+{
+       struct hwa742_request   *req = (struct hwa742_request *)data;
+       void                    (*complete)(void *);
+       void                    *complete_data;
+
+       complete = req->complete;
+       complete_data = req->complete_data;
+
+       free_req(req);
+
+       if (complete)
+               complete(complete_data);
+
+       process_pending_requests();
+}
+
+static int send_frame_handler(struct hwa742_request *req)
+{
+       struct update_param *par = &req->par.update;
+       int x = par->x;
+       int y = par->y;
+       int w = par->width;
+       int h = par->height;
+       int bpp;
+       int conv, transl;
+       unsigned long offset;
+       int color_mode = par->color_mode;
+       int flags = par->flags;
+       int scr_width = hwa742.fbdev->panel->x_res;
+       int scr_height = hwa742.fbdev->panel->y_res;
+
+#ifdef VERBOSE
+       dev_dbg(hwa742.fbdev->dev, "x %d y %d w %d h %d scr_width %d "
+               "color_mode %d flags %d\n",
+               x, y, w, h, scr_width, color_mode, flags);
+#endif
+
+       switch (color_mode) {
+       case OMAPFB_COLOR_YUV422:
+               bpp = 16;
+               conv = 0x08;
+               transl = 0x25;
+               break;
+       case OMAPFB_COLOR_YUV420:
+               bpp = 12;
+               conv = 0x09;
+               transl = 0x25;
+               break;
+       case OMAPFB_COLOR_RGB565:
+               bpp = 16;
+               conv = 0x01;
+               transl = 0x05;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (hwa742.prev_flags != flags ||
+           hwa742.prev_color_mode != color_mode) {
+               set_format_regs(conv, transl, flags);
+               hwa742.prev_color_mode = color_mode;
+               hwa742.prev_flags = flags;
+       }
+       flags = req->par.update.flags;
+       if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC)
+               enable_tearsync(y, scr_width, h, scr_height,
+                               flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC);
+       else
+               disable_tearsync();
+
+       set_window_regs(x, y, x + w, y + h);
+
+       offset = (scr_width * y + x) * bpp / 8;
+
+       hwa742.int_ctrl->setup_plane(OMAPFB_PLANE_GFX,
+                       OMAPFB_CHANNEL_OUT_LCD, offset, scr_width, 0, 0, w, h,
+                       color_mode);
+
+       hwa742.extif->set_bits_per_cycle(16);
+
+       hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 1);
+       hwa742.extif->transfer_area(w, h, request_complete, req);
+
+       return REQ_PENDING;
+}
+
+static void send_frame_complete(void *data)
+{
+       hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 0);
+}
+
+#define ADD_PREQ(_x, _y, _w, _h) do {          \
+       req = alloc_req();                      \
+       req->handler    = send_frame_handler;   \
+       req->complete   = send_frame_complete;  \
+       req->par.update.x = _x;                 \
+       req->par.update.y = _y;                 \
+       req->par.update.width  = _w;            \
+       req->par.update.height = _h;            \
+       req->par.update.color_mode = color_mode;\
+       req->par.update.flags     = flags;      \
+       list_add_tail(&req->entry, req_head);   \
+} while(0)
+
+static void create_req_list(struct omapfb_update_window *win,
+                           struct list_head *req_head)
+{
+       struct hwa742_request *req;
+       int x = win->x;
+       int y = win->y;
+       int width = win->width;
+       int height = win->height;
+       int color_mode;
+       int flags;
+
+       flags = win->format & ~OMAPFB_FORMAT_MASK;
+       color_mode = win->format & OMAPFB_FORMAT_MASK;
+
+       if (x & 1) {
+               ADD_PREQ(x, y, 1, height);
+               width--;
+               x++;
+               flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC;
+       }
+       if (width & ~1) {
+               unsigned int xspan = width & ~1;
+               unsigned int ystart = y;
+               unsigned int yspan = height;
+
+               if (xspan * height * 2 > hwa742.max_transmit_size) {
+                       yspan = hwa742.max_transmit_size / (xspan * 2);
+                       ADD_PREQ(x, ystart, xspan, yspan);
+                       ystart += yspan;
+                       yspan = height - yspan;
+                       flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC;
+               }
+
+               ADD_PREQ(x, ystart, xspan, yspan);
+               x += xspan;
+               width -= xspan;
+               flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC;
+       }
+       if (width)
+               ADD_PREQ(x, y, 1, height);
+}
+
+static void auto_update_complete(void *data)
+{
+       if (!hwa742.stop_auto_update)
+               mod_timer(&hwa742.auto_update_timer,
+                         jiffies + HWA742_AUTO_UPDATE_TIME);
+}
+
+static void hwa742_update_window_auto(unsigned long arg)
+{
+       LIST_HEAD(req_list);
+       struct hwa742_request *last;
+
+       create_req_list(&hwa742.auto_update_window, &req_list);
+       last = list_entry(req_list.prev, struct hwa742_request, entry);
+
+       last->complete = auto_update_complete;
+       last->complete_data = NULL;
+
+       submit_req_list(&req_list);
+}
+
+int hwa742_update_window_async(struct fb_info *fbi,
+                                struct omapfb_update_window *win,
+                                void (*complete_callback)(void *arg),
+                                void *complete_callback_data)
+{
+       LIST_HEAD(req_list);
+       struct hwa742_request *last;
+       int r = 0;
+
+       if (hwa742.update_mode != OMAPFB_MANUAL_UPDATE) {
+               dev_dbg(hwa742.fbdev->dev, "invalid update mode\n");
+               r = -EINVAL;
+               goto out;
+       }
+       if (unlikely(win->format &
+           ~(0x03 | OMAPFB_FORMAT_FLAG_DOUBLE |
+           OMAPFB_FORMAT_FLAG_TEARSYNC | OMAPFB_FORMAT_FLAG_FORCE_VSYNC))) {
+               dev_dbg(hwa742.fbdev->dev, "invalid window flag");
+               r = -EINVAL;
+               goto out;
+       }
+
+       create_req_list(win, &req_list);
+       last = list_entry(req_list.prev, struct hwa742_request, entry);
+
+       last->complete = complete_callback;
+       last->complete_data = (void *)complete_callback_data;
+
+       submit_req_list(&req_list);
+
+out:
+       return r;
+}
+EXPORT_SYMBOL(hwa742_update_window_async);
+
+static int hwa742_setup_plane(int plane, int channel_out,
+                                 unsigned long offset, int screen_width,
+                                 int pos_x, int pos_y, int width, int height,
+                                 int color_mode)
+{
+       if (plane != OMAPFB_PLANE_GFX ||
+           channel_out != OMAPFB_CHANNEL_OUT_LCD)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int hwa742_enable_plane(int plane, int enable)
+{
+       if (plane != 0)
+               return -EINVAL;
+
+       hwa742.int_ctrl->enable_plane(plane, enable);
+
+       return 0;
+}
+
+static int sync_handler(struct hwa742_request *req)
+{
+       complete(req->par.sync);
+       return REQ_COMPLETE;
+}
+
+static void hwa742_sync(void)
+{
+       LIST_HEAD(req_list);
+       struct hwa742_request *req;
+       struct completion comp;
+
+       req = alloc_req();
+
+       req->handler = sync_handler;
+       req->complete = NULL;
+       init_completion(&comp);
+       req->par.sync = &comp;
+
+       list_add(&req->entry, &req_list);
+       submit_req_list(&req_list);
+
+       wait_for_completion(&comp);
+}
+
+static void hwa742_bind_client(struct omapfb_notifier_block *nb)
+{
+       dev_dbg(hwa742.fbdev->dev, "update_mode %d\n", hwa742.update_mode);
+       if (hwa742.update_mode == OMAPFB_MANUAL_UPDATE) {
+               omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_READY);
+       }
+}
+
+static int hwa742_set_update_mode(enum omapfb_update_mode mode)
+{
+       if (mode != OMAPFB_MANUAL_UPDATE && mode != OMAPFB_AUTO_UPDATE &&
+           mode != OMAPFB_UPDATE_DISABLED)
+               return -EINVAL;
+
+       if (mode == hwa742.update_mode)
+               return 0;
+
+       dev_info(hwa742.fbdev->dev, "HWA742: setting update mode to %s\n",
+                       mode == OMAPFB_UPDATE_DISABLED ? "disabled" :
+                       (mode == OMAPFB_AUTO_UPDATE ? "auto" : "manual"));
+
+       switch (hwa742.update_mode) {
+       case OMAPFB_MANUAL_UPDATE:
+               omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_DISABLED);
+               break;
+       case OMAPFB_AUTO_UPDATE:
+               hwa742.stop_auto_update = 1;
+               del_timer_sync(&hwa742.auto_update_timer);
+               break;
+       case OMAPFB_UPDATE_DISABLED:
+               break;
+       }
+
+       hwa742.update_mode = mode;
+       hwa742_sync();
+       hwa742.stop_auto_update = 0;
+
+       switch (mode) {
+       case OMAPFB_MANUAL_UPDATE:
+               omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_READY);
+               break;
+       case OMAPFB_AUTO_UPDATE:
+               hwa742_update_window_auto(0);
+               break;
+       case OMAPFB_UPDATE_DISABLED:
+               break;
+       }
+
+       return 0;
+}
+
+static enum omapfb_update_mode hwa742_get_update_mode(void)
+{
+       return hwa742.update_mode;
+}
+
+static unsigned long round_to_extif_ticks(unsigned long ps, int div)
+{
+       int bus_tick = hwa742.extif_clk_period * div;
+       return (ps + bus_tick - 1) / bus_tick * bus_tick;
+}
+
+static int calc_reg_timing(unsigned long sysclk, int div)
+{
+       struct extif_timings *t;
+       unsigned long systim;
+
+       /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns,
+        * AccessTime 2 ns + 12.2 ns (regs),
+        * WEOffTime = WEOnTime + 1 ns,
+        * REOffTime = REOnTime + 16 ns (regs),
+        * CSOffTime = REOffTime + 1 ns
+        * ReadCycle = 2ns + 2*SYSCLK  (regs),
+        * WriteCycle = 2*SYSCLK + 2 ns,
+        * CSPulseWidth = 10 ns */
+       systim = 1000000000 / (sysclk / 1000);
+       dev_dbg(hwa742.fbdev->dev, "HWA742 systim %lu ps extif_clk_period %u ps"
+                 "extif_clk_div %d\n", systim, hwa742.extif_clk_period, div);
+
+       t = &hwa742.reg_timings;
+       memset(t, 0, sizeof(*t));
+       t->clk_div = div;
+       t->cs_on_time = 0;
+       t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
+       t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
+       t->access_time = round_to_extif_ticks(t->re_on_time + 12200, div);
+       t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div);
+       t->re_off_time = round_to_extif_ticks(t->re_on_time + 16000, div);
+       t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div);
+       t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
+       if (t->we_cycle_time < t->we_off_time)
+               t->we_cycle_time = t->we_off_time;
+       t->re_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
+       if (t->re_cycle_time < t->re_off_time)
+               t->re_cycle_time = t->re_off_time;
+       t->cs_pulse_width = 0;
+
+       dev_dbg(hwa742.fbdev->dev, "[reg]cson %d csoff %d reon %d reoff %d\n",
+                t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time);
+       dev_dbg(hwa742.fbdev->dev, "[reg]weon %d weoff %d recyc %d wecyc %d\n",
+                t->we_on_time, t->we_off_time, t->re_cycle_time,
+                t->we_cycle_time);
+       dev_dbg(hwa742.fbdev->dev, "[reg]rdaccess %d cspulse %d\n",
+                t->access_time, t->cs_pulse_width);
+
+       return hwa742.extif->convert_timings(t);
+}
+
+static int calc_lut_timing(unsigned long sysclk, int div)
+{
+       struct extif_timings *t;
+       unsigned long systim;
+
+       /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns,
+        * AccessTime 2 ns + 4 * SYSCLK + 26 (lut),
+        * WEOffTime = WEOnTime + 1 ns,
+        * REOffTime = REOnTime + 4*SYSCLK + 26 ns (lut),
+        * CSOffTime = REOffTime + 1 ns
+        * ReadCycle = 2ns + 4*SYSCLK + 26 ns (lut),
+        * WriteCycle = 2*SYSCLK + 2 ns,
+        * CSPulseWidth = 10 ns
+        */
+       systim = 1000000000 / (sysclk / 1000);
+       dev_dbg(hwa742.fbdev->dev, "HWA742 systim %lu ps extif_clk_period %u ps"
+                 "extif_clk_div %d\n", systim, hwa742.extif_clk_period, div);
+
+       t = &hwa742.lut_timings;
+       memset(t, 0, sizeof(*t));
+
+       t->clk_div = div;
+
+       t->cs_on_time = 0;
+       t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
+       t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
+       t->access_time = round_to_extif_ticks(t->re_on_time + 4 * systim +
+                                             26000, div);
+       t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div);
+       t->re_off_time = round_to_extif_ticks(t->re_on_time + 4 * systim +
+                                             26000, div);
+       t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div);
+       t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
+       if (t->we_cycle_time < t->we_off_time)
+               t->we_cycle_time = t->we_off_time;
+       t->re_cycle_time = round_to_extif_ticks(2000 + 4 * systim + 26000, div);
+       if (t->re_cycle_time < t->re_off_time)
+               t->re_cycle_time = t->re_off_time;
+       t->cs_pulse_width = 0;
+
+       dev_dbg(hwa742.fbdev->dev, "[lut]cson %d csoff %d reon %d reoff %d\n",
+                t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time);
+       dev_dbg(hwa742.fbdev->dev, "[lut]weon %d weoff %d recyc %d wecyc %d\n",
+                t->we_on_time, t->we_off_time, t->re_cycle_time,
+                t->we_cycle_time);
+       dev_dbg(hwa742.fbdev->dev, "[lut]rdaccess %d cspulse %d\n",
+                t->access_time, t->cs_pulse_width);
+
+       return hwa742.extif->convert_timings(t);
+}
+
+static int calc_extif_timings(unsigned long sysclk, int *extif_mem_div)
+{
+       int max_clk_div;
+       int div;
+
+       hwa742.extif->get_clk_info(&hwa742.extif_clk_period, &max_clk_div);
+       for (div = 1; div < max_clk_div; div++) {
+               if (calc_reg_timing(sysclk, div) == 0)
+                       break;
+       }
+       if (div > max_clk_div)
+               goto err;
+
+       *extif_mem_div = div;
+
+       for (div = 1; div < max_clk_div; div++) {
+               if (calc_lut_timing(sysclk, div) == 0)
+                       break;
+       }
+
+       if (div > max_clk_div)
+               goto err;
+
+       return 0;
+
+err:
+       dev_err(hwa742.fbdev->dev, "can't setup timings\n");
+       return -1;
+}
+
+static void calc_hwa742_clk_rates(unsigned long ext_clk,
+                               unsigned long *sys_clk, unsigned long *pix_clk)
+{
+       int pix_clk_src;
+       int sys_div = 0, sys_mul = 0;
+       int pix_div;
+
+       pix_clk_src = hwa742_read_reg(HWA742_CLK_SRC_REG);
+       pix_div = ((pix_clk_src >> 3) & 0x1f) + 1;
+       if ((pix_clk_src & (0x3 << 1)) == 0) {
+               /* Source is the PLL */
+               sys_div = (hwa742_read_reg(HWA742_PLL_DIV_REG) & 0x3f) + 1;
+               sys_mul = (hwa742_read_reg(HWA742_PLL_4_REG) & 0x7f) + 1;
+               *sys_clk = ext_clk * sys_mul / sys_div;
+       } else  /* else source is ext clk, or oscillator */
+               *sys_clk = ext_clk;
+
+       *pix_clk = *sys_clk / pix_div;                  /* HZ */
+       dev_dbg(hwa742.fbdev->dev,
+               "ext_clk %ld pix_src %d pix_div %d sys_div %d sys_mul %d\n",
+               ext_clk, pix_clk_src & (0x3 << 1), pix_div, sys_div, sys_mul);
+       dev_dbg(hwa742.fbdev->dev, "sys_clk %ld pix_clk %ld\n",
+               *sys_clk, *pix_clk);
+}
+
+
+static int setup_tearsync(unsigned long pix_clk, int extif_div)
+{
+       int hdisp, vdisp;
+       int hndp, vndp;
+       int hsw, vsw;
+       int hs, vs;
+       int hs_pol_inv, vs_pol_inv;
+       int use_hsvs, use_ndp;
+       u8  b;
+
+       hsw = hwa742_read_reg(HWA742_HS_W_REG);
+       vsw = hwa742_read_reg(HWA742_VS_W_REG);
+       hs_pol_inv = !(hsw & 0x80);
+       vs_pol_inv = !(vsw & 0x80);
+       hsw = hsw & 0x7f;
+       vsw = vsw & 0x3f;
+
+       hdisp = (hwa742_read_reg(HWA742_H_DISP_REG) & 0x7f) * 8;
+       vdisp = hwa742_read_reg(HWA742_V_DISP_1_REG) +
+               ((hwa742_read_reg(HWA742_V_DISP_2_REG) & 0x3) << 8);
+
+       hndp = hwa742_read_reg(HWA742_H_NDP_REG) & 0x7f;
+       vndp = hwa742_read_reg(HWA742_V_NDP_REG);
+
+       /* time to transfer one pixel (16bpp) in ps */
+       hwa742.pix_tx_time = hwa742.reg_timings.we_cycle_time;
+       if (hwa742.extif->get_max_tx_rate != NULL) {
+               /* The external interface might have a rate limitation,
+                * if so, we have to maximize our transfer rate.
+                */
+               unsigned long min_tx_time;
+               unsigned long max_tx_rate = hwa742.extif->get_max_tx_rate();
+
+               dev_dbg(hwa742.fbdev->dev, "max_tx_rate %ld HZ\n",
+                       max_tx_rate);
+               min_tx_time = 1000000000 / (max_tx_rate / 1000);  /* ps */
+               if (hwa742.pix_tx_time < min_tx_time)
+                       hwa742.pix_tx_time = min_tx_time;
+       }
+
+       /* time to update one line in ps */
+       hwa742.line_upd_time = (hdisp + hndp) * 1000000 / (pix_clk / 1000);
+       hwa742.line_upd_time *= 1000;
+       if (hdisp * hwa742.pix_tx_time > hwa742.line_upd_time)
+               /* transfer speed too low, we might have to use both
+                * HS and VS */
+               use_hsvs = 1;
+       else
+               /* decent transfer speed, we'll always use only VS */
+               use_hsvs = 0;
+
+       if (use_hsvs && (hs_pol_inv || vs_pol_inv)) {
+               /* HS or'ed with VS doesn't work, use the active high
+                * TE signal based on HNDP / VNDP */
+               use_ndp = 1;
+               hs_pol_inv = 0;
+               vs_pol_inv = 0;
+               hs = hndp;
+               vs = vndp;
+       } else {
+               /* Use HS or'ed with VS as a TE signal if both are needed
+                * or VNDP if only vsync is needed. */
+               use_ndp = 0;
+               hs = hsw;
+               vs = vsw;
+               if (!use_hsvs) {
+                       hs_pol_inv = 0;
+                       vs_pol_inv = 0;
+               }
+       }
+
+       hs = hs * 1000000 / (pix_clk / 1000);                   /* ps */
+       hs *= 1000;
+
+       vs = vs * (hdisp + hndp) * 1000000 / (pix_clk / 1000);  /* ps */
+       vs *= 1000;
+
+       if (vs <= hs)
+               return -EDOM;
+       /* set VS to 120% of HS to minimize VS detection time */
+       vs = hs * 12 / 10;
+       /* minimize HS too */
+       hs = 10000;
+
+       b = hwa742_read_reg(HWA742_NDP_CTRL);
+       b &= ~0x3;
+       b |= use_hsvs ? 1 : 0;
+       b |= (use_ndp && use_hsvs) ? 0 : 2;
+       hwa742_write_reg(HWA742_NDP_CTRL, b);
+
+       hwa742.vsync_only = !use_hsvs;
+
+       dev_dbg(hwa742.fbdev->dev,
+               "pix_clk %ld HZ pix_tx_time %ld ps line_upd_time %ld ps\n",
+               pix_clk, hwa742.pix_tx_time, hwa742.line_upd_time);
+       dev_dbg(hwa742.fbdev->dev,
+               "hs %d ps vs %d ps mode %d vsync_only %d\n",
+               hs, vs, (b & 0x3), !use_hsvs);
+
+       return hwa742.extif->setup_tearsync(1, hs, vs,
+                                           hs_pol_inv, vs_pol_inv, extif_div);
+}
+
+static void hwa742_get_caps(int plane, struct omapfb_caps *caps)
+{
+       hwa742.int_ctrl->get_caps(plane, caps);
+       caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE |
+                     OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE;
+       if (hwa742.te_connected)
+               caps->ctrl |= OMAPFB_CAPS_TEARSYNC;
+       caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) |
+                          (1 << OMAPFB_COLOR_YUV420);
+}
+
+static void hwa742_suspend(void)
+{
+       hwa742.update_mode_before_suspend = hwa742.update_mode;
+       hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED);
+       /* Enable sleep mode */
+       hwa742_write_reg(HWA742_POWER_SAVE, 1 << 1);
+       if (hwa742.power_down != NULL)
+               hwa742.power_down(hwa742.fbdev->dev);
+}
+
+static void hwa742_resume(void)
+{
+       if (hwa742.power_up != NULL)
+               hwa742.power_up(hwa742.fbdev->dev);
+       /* Disable sleep mode */
+       hwa742_write_reg(HWA742_POWER_SAVE, 0);
+       while (1) {
+               /* Loop until PLL output is stabilized */
+               if (hwa742_read_reg(HWA742_PLL_DIV_REG) & (1 << 7))
+                       break;
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(msecs_to_jiffies(5));
+       }
+       hwa742_set_update_mode(hwa742.update_mode_before_suspend);
+}
+
+static int hwa742_init(struct omapfb_device *fbdev, int ext_mode,
+                      struct omapfb_mem_desc *req_vram)
+{
+       int r = 0, i;
+       u8 rev, conf;
+       unsigned long ext_clk;
+       unsigned long sys_clk, pix_clk;
+       int extif_mem_div;
+       struct omapfb_platform_data *omapfb_conf;
+       struct hwa742_platform_data *ctrl_conf;
+
+       BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl);
+
+       hwa742.fbdev = fbdev;
+       hwa742.extif = fbdev->ext_if;
+       hwa742.int_ctrl = fbdev->int_ctrl;
+
+       omapfb_conf = fbdev->dev->platform_data;
+       ctrl_conf = omapfb_conf->ctrl_platform_data;
+
+       if (ctrl_conf == NULL || ctrl_conf->get_clock_rate == NULL) {
+               dev_err(fbdev->dev, "HWA742: missing platform data\n");
+               r = -ENOENT;
+               goto err1;
+       }
+
+       hwa742.power_down = ctrl_conf->power_down;
+       hwa742.power_up = ctrl_conf->power_up;
+
+       spin_lock_init(&hwa742.req_lock);
+
+       if ((r = hwa742.int_ctrl->init(fbdev, 1, req_vram)) < 0)
+               goto err1;
+
+       if ((r = hwa742.extif->init(fbdev)) < 0)
+               goto err2;
+
+       ext_clk = ctrl_conf->get_clock_rate(fbdev->dev);
+       if ((r = calc_extif_timings(ext_clk, &extif_mem_div)) < 0)
+               goto err3;
+       hwa742.extif->set_timings(&hwa742.reg_timings);
+       if (hwa742.power_up != NULL)
+               hwa742.power_up(fbdev->dev);
+
+       calc_hwa742_clk_rates(ext_clk, &sys_clk, &pix_clk);
+       if ((r = calc_extif_timings(sys_clk, &extif_mem_div)) < 0)
+               goto err4;
+       hwa742.extif->set_timings(&hwa742.reg_timings);
+
+       rev = hwa742_read_reg(HWA742_REV_CODE_REG);
+       if ((rev & 0xfc) != 0x80) {
+               dev_err(fbdev->dev, "HWA742: invalid revision %02x\n", rev);
+               r = -ENODEV;
+               goto err4;
+       }
+
+
+       if (!(hwa742_read_reg(HWA742_PLL_DIV_REG) & 0x80)) {
+               dev_err(fbdev->dev,
+                     "HWA742: controller not initialized by the bootloader\n");
+               r = -ENODEV;
+               goto err4;
+       }
+
+       if (ctrl_conf->te_connected) {
+               if ((r = setup_tearsync(pix_clk, extif_mem_div)) < 0) {
+                       dev_err(hwa742.fbdev->dev,
+                              "HWA742: can't setup tearing synchronization\n");
+                       goto err4;
+               }
+               hwa742.te_connected = 1;
+       }
+
+       hwa742.max_transmit_size = hwa742.extif->max_transmit_size;
+
+       hwa742.update_mode = OMAPFB_UPDATE_DISABLED;
+
+       hwa742.auto_update_window.x = 0;
+       hwa742.auto_update_window.y = 0;
+       hwa742.auto_update_window.width = fbdev->panel->x_res;
+       hwa742.auto_update_window.height = fbdev->panel->y_res;
+       hwa742.auto_update_window.format = 0;
+
+       init_timer(&hwa742.auto_update_timer);
+       hwa742.auto_update_timer.function = hwa742_update_window_auto;
+       hwa742.auto_update_timer.data = 0;
+
+       hwa742.prev_color_mode = -1;
+       hwa742.prev_flags = 0;
+
+       hwa742.fbdev = fbdev;
+
+       INIT_LIST_HEAD(&hwa742.free_req_list);
+       INIT_LIST_HEAD(&hwa742.pending_req_list);
+       for (i = 0; i < ARRAY_SIZE(hwa742.req_pool); i++)
+               list_add(&hwa742.req_pool[i].entry, &hwa742.free_req_list);
+       BUG_ON(i <= IRQ_REQ_POOL_SIZE);
+       sema_init(&hwa742.req_sema, i - IRQ_REQ_POOL_SIZE);
+
+       conf = hwa742_read_reg(HWA742_CONFIG_REG);
+       dev_info(fbdev->dev, ": Epson HWA742 LCD controller rev %d "
+                       "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
+
+       return 0;
+err4:
+       if (hwa742.power_down != NULL)
+               hwa742.power_down(fbdev->dev);
+err3:
+       hwa742.extif->cleanup();
+err2:
+       hwa742.int_ctrl->cleanup();
+err1:
+       return r;
+}
+
+static void hwa742_cleanup(void)
+{
+       hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED);
+       hwa742.extif->cleanup();
+       hwa742.int_ctrl->cleanup();
+       if (hwa742.power_down != NULL)
+               hwa742.power_down(hwa742.fbdev->dev);
+}
+
+struct lcd_ctrl hwa742_ctrl = {
+       .name                   = "hwa742",
+       .init                   = hwa742_init,
+       .cleanup                = hwa742_cleanup,
+       .bind_client            = hwa742_bind_client,
+       .get_caps               = hwa742_get_caps,
+       .set_update_mode        = hwa742_set_update_mode,
+       .get_update_mode        = hwa742_get_update_mode,
+       .setup_plane            = hwa742_setup_plane,
+       .enable_plane           = hwa742_enable_plane,
+       .update_window          = hwa742_update_window_async,
+       .sync                   = hwa742_sync,
+       .suspend                = hwa742_suspend,
+       .resume                 = hwa742_resume,
+};
+
diff --git a/drivers/video/omap/lcd_ams_delta.c b/drivers/video/omap/lcd_ams_delta.c
new file mode 100644 (file)
index 0000000..3476689
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * File: drivers/video/omap/lcd_ams_delta.c
+ *
+ * Based on drivers/video/omap/lcd_inn1510.c
+ *
+ * LCD panel support for the Amstrad E3 (Delta) videophone.
+ *
+ * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+
+#include <asm/arch/board-ams-delta.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/omapfb.h>
+
+#define AMS_DELTA_DEFAULT_CONTRAST     112
+
+static int ams_delta_panel_init(struct lcd_panel *panel,
+               struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void ams_delta_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int ams_delta_panel_enable(struct lcd_panel *panel)
+{
+       ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP,
+                       AMS_DELTA_LATCH2_LCD_NDISP);
+       ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN,
+                       AMS_DELTA_LATCH2_LCD_VBLEN);
+
+       omap_writeb(1, OMAP_PWL_CLK_ENABLE);
+       omap_writeb(AMS_DELTA_DEFAULT_CONTRAST, OMAP_PWL_ENABLE);
+
+       return 0;
+}
+
+static void ams_delta_panel_disable(struct lcd_panel *panel)
+{
+       ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, 0);
+       ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, 0);
+}
+
+static unsigned long ams_delta_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+static struct lcd_panel ams_delta_panel = {
+       .name           = "ams-delta",
+       .config         = 0,
+
+       .bpp            = 12,
+       .data_lines     = 16,
+       .x_res          = 480,
+       .y_res          = 320,
+       .pixel_clock    = 4687,
+       .hsw            = 3,
+       .hfp            = 1,
+       .hbp            = 1,
+       .vsw            = 1,
+       .vfp            = 0,
+       .vbp            = 0,
+       .pcd            = 0,
+       .acb            = 37,
+
+       .init           = ams_delta_panel_init,
+       .cleanup        = ams_delta_panel_cleanup,
+       .enable         = ams_delta_panel_enable,
+       .disable        = ams_delta_panel_disable,
+       .get_caps       = ams_delta_panel_get_caps,
+};
+
+static int ams_delta_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&ams_delta_panel);
+       return 0;
+}
+
+static int ams_delta_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int ams_delta_panel_suspend(struct platform_device *pdev,
+               pm_message_t mesg)
+{
+       return 0;
+}
+
+static int ams_delta_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver ams_delta_panel_driver = {
+       .probe          = ams_delta_panel_probe,
+       .remove         = ams_delta_panel_remove,
+       .suspend        = ams_delta_panel_suspend,
+       .resume         = ams_delta_panel_resume,
+       .driver         = {
+               .name   = "lcd_ams_delta",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int ams_delta_panel_drv_init(void)
+{
+       return platform_driver_register(&ams_delta_panel_driver);
+}
+
+static void ams_delta_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&ams_delta_panel_driver);
+}
+
+module_init(ams_delta_panel_drv_init);
+module_exit(ams_delta_panel_drv_cleanup);
diff --git a/drivers/video/omap/lcd_apollon.c b/drivers/video/omap/lcd_apollon.c
new file mode 100644 (file)
index 0000000..4c0f370
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * File: drivers/video/omap/lcd_apollon.c
+ *
+ * LCD panel support for the Samsung OMAP2 Apollon board
+ *
+ * Copyright (C) 2005,2006 Samsung Electronics
+ * Author: Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Derived from drivers/video/omap/lcd-h4.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/omapfb.h>
+
+/* #define USE_35INCH_LCD 1 */
+
+static int apollon_panel_init(struct lcd_panel *panel,
+                               struct omapfb_device *fbdev)
+{
+       /* configure LCD PWR_EN */
+       omap_cfg_reg(M21_242X_GPIO11);
+       return 0;
+}
+
+static void apollon_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int apollon_panel_enable(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+static void apollon_panel_disable(struct lcd_panel *panel)
+{
+}
+
+static unsigned long apollon_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel apollon_panel = {
+       .name           = "apollon",
+       .config         = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+                         OMAP_LCDC_INV_HSYNC,
+
+       .bpp            = 16,
+       .data_lines     = 18,
+#ifdef USE_35INCH_LCD
+       .x_res          = 240,
+       .y_res          = 320,
+       .hsw            = 2,
+       .hfp            = 3,
+       .hbp            = 9,
+       .vsw            = 4,
+       .vfp            = 3,
+       .vbp            = 5,
+#else
+       .x_res          = 480,
+       .y_res          = 272,
+       .hsw            = 41,
+       .hfp            = 2,
+       .hbp            = 2,
+       .vsw            = 10,
+       .vfp            = 2,
+       .vbp            = 2,
+#endif
+       .pixel_clock    = 6250,
+
+       .init           = apollon_panel_init,
+       .cleanup        = apollon_panel_cleanup,
+       .enable         = apollon_panel_enable,
+       .disable        = apollon_panel_disable,
+       .get_caps       = apollon_panel_get_caps,
+};
+
+static int apollon_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&apollon_panel);
+       return 0;
+}
+
+static int apollon_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int apollon_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int apollon_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver apollon_panel_driver = {
+       .probe          = apollon_panel_probe,
+       .remove         = apollon_panel_remove,
+       .suspend        = apollon_panel_suspend,
+       .resume         = apollon_panel_resume,
+       .driver         = {
+               .name   = "apollon_lcd",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init apollon_panel_drv_init(void)
+{
+       return platform_driver_register(&apollon_panel_driver);
+}
+
+static void __exit apollon_panel_drv_exit(void)
+{
+       platform_driver_unregister(&apollon_panel_driver);
+}
+
+module_init(apollon_panel_drv_init);
+module_exit(apollon_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_h2.c b/drivers/video/omap/lcd_h2.c
new file mode 100644 (file)
index 0000000..288f082
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * File: drivers/video/omap/lcd-h2.c
+ *
+ * LCD panel support for the TI OMAP H2 board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/tsc2101.h>
+
+#include <asm/arch/mux.h>
+#include <asm/arch/omapfb.h>
+
+static struct {
+       struct platform_device  *lcd_dev;
+       struct spi_device       *tsc2101_dev;
+} h2_panel_dev;
+
+static int h2_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void h2_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int h2_panel_enable(struct lcd_panel *panel)
+{
+       int r;
+
+       /* Assert LCD_EN, BKLIGHT_EN pins on LCD panel
+        * page2, GPIO config reg, GPIO(0,1) to out and asserted
+        */
+       r = tsc2101_write_sync(h2_panel_dev.tsc2101_dev, 2, 0x23, 0xcc00);
+       if (r < 0)
+               dev_err(&h2_panel_dev.lcd_dev->dev,
+                       "failed to enable LCD panel\n");
+
+       return r;
+}
+
+static void h2_panel_disable(struct lcd_panel *panel)
+{
+       /* Deassert LCD_EN and BKLIGHT_EN pins on LCD panel
+        * page2, GPIO config reg, GPIO(0,1) to out and deasserted
+        */
+       if (tsc2101_write_sync(h2_panel_dev.tsc2101_dev, 2, 0x23, 0x8800))
+               dev_err(&h2_panel_dev.lcd_dev->dev,
+                       "failed to disable LCD panel\n");
+}
+
+static unsigned long h2_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel h2_panel = {
+       .name           = "h2",
+       .config         = OMAP_LCDC_PANEL_TFT,
+
+       .bpp            = 16,
+       .data_lines     = 16,
+       .x_res          = 240,
+       .y_res          = 320,
+       .pixel_clock    = 5000,
+       .hsw            = 12,
+       .hfp            = 12,
+       .hbp            = 46,
+       .vsw            = 1,
+       .vfp            = 1,
+       .vbp            = 0,
+
+       .init           = h2_panel_init,
+       .cleanup        = h2_panel_cleanup,
+       .enable         = h2_panel_enable,
+       .disable        = h2_panel_disable,
+       .get_caps       = h2_panel_get_caps,
+};
+
+static int h2_panel_probe(struct platform_device *pdev)
+{
+       struct spi_device *tsc2101;
+
+       tsc2101 = pdev->dev.platform_data;
+       if (tsc2101 == NULL) {
+               dev_err(&pdev->dev, "no platform data\n");
+               return -ENODEV;
+       }
+       if (strncmp(tsc2101->modalias, "tsc2101", 8) != 0) {
+               dev_err(&pdev->dev, "tsc2101 not found\n");
+               return -EINVAL;
+       }
+       h2_panel_dev.lcd_dev = pdev;
+       h2_panel_dev.tsc2101_dev = tsc2101;
+       omapfb_register_panel(&h2_panel);
+       return 0;
+}
+
+static int h2_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int h2_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int h2_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver h2_panel_driver = {
+       .probe          = h2_panel_probe,
+       .remove         = h2_panel_remove,
+       .suspend        = h2_panel_suspend,
+       .resume         = h2_panel_resume,
+       .driver         = {
+               .name   = "lcd_h2",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int h2_panel_drv_init(void)
+{
+       return platform_driver_register(&h2_panel_driver);
+}
+
+static void h2_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&h2_panel_driver);
+}
+
+module_init(h2_panel_drv_init);
+module_exit(h2_panel_drv_cleanup);
+
diff --git a/drivers/video/omap/lcd_h3.c b/drivers/video/omap/lcd_h3.c
new file mode 100644 (file)
index 0000000..60ad0e0
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * File: drivers/video/omap/lcd-h3.c
+ *
+ * LCD panel support for the TI OMAP H3 board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/arch/tps65010.h>
+#include <asm/arch/omapfb.h>
+
+#define MODULE_NAME    "omapfb-lcd_h3"
+
+#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args)
+
+static int h3_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void h3_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int h3_panel_enable(struct lcd_panel *panel)
+{
+       int r = 0;
+
+       /* GPIO1 and GPIO2 of TPS65010 send LCD_ENBKL and LCD_ENVDD signals */
+       r = tps65010_set_gpio_out_value(GPIO1, HIGH);
+       if (!r)
+               r = tps65010_set_gpio_out_value(GPIO2, HIGH);
+       if (r)
+               pr_err("Unable to turn on LCD panel\n");
+
+       return r;
+}
+
+static void h3_panel_disable(struct lcd_panel *panel)
+{
+       int r = 0;
+
+       /* GPIO1 and GPIO2 of TPS65010 send LCD_ENBKL and LCD_ENVDD signals */
+       r = tps65010_set_gpio_out_value(GPIO1, LOW);
+       if (!r)
+               tps65010_set_gpio_out_value(GPIO2, LOW);
+       if (r)
+               pr_err("Unable to turn off LCD panel\n");
+}
+
+static unsigned long h3_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel h3_panel = {
+       .name           = "h3",
+       .config         = OMAP_LCDC_PANEL_TFT,
+
+       .data_lines     = 16,
+       .bpp            = 16,
+       .x_res          = 240,
+       .y_res          = 320,
+       .pixel_clock    = 12000,
+       .hsw            = 12,
+       .hfp            = 14,
+       .hbp            = 72 - 12,
+       .vsw            = 1,
+       .vfp            = 1,
+       .vbp            = 0,
+       .pcd            = 0,
+
+       .init           = h3_panel_init,
+       .cleanup        = h3_panel_cleanup,
+       .enable         = h3_panel_enable,
+       .disable        = h3_panel_disable,
+       .get_caps       = h3_panel_get_caps,
+};
+
+static int h3_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&h3_panel);
+       return 0;
+}
+
+static int h3_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int h3_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int h3_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver h3_panel_driver = {
+       .probe          = h3_panel_probe,
+       .remove         = h3_panel_remove,
+       .suspend        = h3_panel_suspend,
+       .resume         = h3_panel_resume,
+       .driver         = {
+               .name   = "lcd_h3",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int h3_panel_drv_init(void)
+{
+       return platform_driver_register(&h3_panel_driver);
+}
+
+static void h3_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&h3_panel_driver);
+}
+
+module_init(h3_panel_drv_init);
+module_exit(h3_panel_drv_cleanup);
+
diff --git a/drivers/video/omap/lcd_h4.c b/drivers/video/omap/lcd_h4.c
new file mode 100644 (file)
index 0000000..dde00d4
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * File: drivers/video/omap/lcd-h4.c
+ *
+ * LCD panel support for the TI OMAP H4 board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/arch/omapfb.h>
+
+static int h4_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void h4_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int h4_panel_enable(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+static void h4_panel_disable(struct lcd_panel *panel)
+{
+}
+
+static unsigned long h4_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel h4_panel = {
+       .name           = "h4",
+       .config         = OMAP_LCDC_PANEL_TFT,
+
+       .bpp            = 16,
+       .data_lines     = 16,
+       .x_res          = 240,
+       .y_res          = 320,
+       .pixel_clock    = 6250,
+       .hsw            = 15,
+       .hfp            = 15,
+       .hbp            = 60,
+       .vsw            = 1,
+       .vfp            = 1,
+       .vbp            = 1,
+
+       .init           = h4_panel_init,
+       .cleanup        = h4_panel_cleanup,
+       .enable         = h4_panel_enable,
+       .disable        = h4_panel_disable,
+       .get_caps       = h4_panel_get_caps,
+};
+
+static int h4_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&h4_panel);
+       return 0;
+}
+
+static int h4_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int h4_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int h4_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver h4_panel_driver = {
+       .probe          = h4_panel_probe,
+       .remove         = h4_panel_remove,
+       .suspend        = h4_panel_suspend,
+       .resume         = h4_panel_resume,
+       .driver         = {
+               .name   = "lcd_h4",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int h4_panel_drv_init(void)
+{
+       return platform_driver_register(&h4_panel_driver);
+}
+
+static void h4_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&h4_panel_driver);
+}
+
+module_init(h4_panel_drv_init);
+module_exit(h4_panel_drv_cleanup);
+
diff --git a/drivers/video/omap/lcd_inn1510.c b/drivers/video/omap/lcd_inn1510.c
new file mode 100644 (file)
index 0000000..d514b94
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * File: drivers/video/omap/lcd-inn1510.c
+ *
+ * LCD panel support for the TI OMAP1510 Innovator board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/fpga.h>
+#include <asm/arch/omapfb.h>
+
+static int innovator1510_panel_init(struct lcd_panel *panel,
+                                   struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void innovator1510_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int innovator1510_panel_enable(struct lcd_panel *panel)
+{
+       fpga_write(0x7, OMAP1510_FPGA_LCD_PANEL_CONTROL);
+       return 0;
+}
+
+static void innovator1510_panel_disable(struct lcd_panel *panel)
+{
+       fpga_write(0x0, OMAP1510_FPGA_LCD_PANEL_CONTROL);
+}
+
+static unsigned long innovator1510_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel innovator1510_panel = {
+       .name           = "inn1510",
+       .config         = OMAP_LCDC_PANEL_TFT,
+
+       .bpp            = 16,
+       .data_lines     = 16,
+       .x_res          = 240,
+       .y_res          = 320,
+       .pixel_clock    = 12500,
+       .hsw            = 40,
+       .hfp            = 40,
+       .hbp            = 72,
+       .vsw            = 1,
+       .vfp            = 1,
+       .vbp            = 0,
+       .pcd            = 12,
+
+       .init           = innovator1510_panel_init,
+       .cleanup        = innovator1510_panel_cleanup,
+       .enable         = innovator1510_panel_enable,
+       .disable        = innovator1510_panel_disable,
+       .get_caps       = innovator1510_panel_get_caps,
+};
+
+static int innovator1510_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&innovator1510_panel);
+       return 0;
+}
+
+static int innovator1510_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int innovator1510_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int innovator1510_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver innovator1510_panel_driver = {
+       .probe          = innovator1510_panel_probe,
+       .remove         = innovator1510_panel_remove,
+       .suspend        = innovator1510_panel_suspend,
+       .resume         = innovator1510_panel_resume,
+       .driver         = {
+               .name   = "lcd_inn1510",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int innovator1510_panel_drv_init(void)
+{
+       return platform_driver_register(&innovator1510_panel_driver);
+}
+
+static void innovator1510_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&innovator1510_panel_driver);
+}
+
+module_init(innovator1510_panel_drv_init);
+module_exit(innovator1510_panel_drv_cleanup);
+
diff --git a/drivers/video/omap/lcd_inn1610.c b/drivers/video/omap/lcd_inn1610.c
new file mode 100644 (file)
index 0000000..cb1fa5a
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * File: drivers/video/omap/lcd-inn1610.c
+ *
+ * LCD panel support for the TI OMAP1610 Innovator board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/arch/omapfb.h>
+
+#define MODULE_NAME    "omapfb-lcd_h3"
+
+#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args)
+
+static int innovator1610_panel_init(struct lcd_panel *panel,
+                                   struct omapfb_device *fbdev)
+{
+       int r = 0;
+
+       if (omap_request_gpio(14)) {
+               pr_err("can't request GPIO 14\n");
+               r = -1;
+               goto exit;
+       }
+       if (omap_request_gpio(15)) {
+               pr_err("can't request GPIO 15\n");
+               omap_free_gpio(14);
+               r = -1;
+               goto exit;
+       }
+       /* configure GPIO(14, 15) as outputs */
+       omap_set_gpio_direction(14, 0);
+       omap_set_gpio_direction(15, 0);
+exit:
+       return r;
+}
+
+static void innovator1610_panel_cleanup(struct lcd_panel *panel)
+{
+       omap_free_gpio(15);
+       omap_free_gpio(14);
+}
+
+static int innovator1610_panel_enable(struct lcd_panel *panel)
+{
+       /* set GPIO14 and GPIO15 high */
+       omap_set_gpio_dataout(14, 1);
+       omap_set_gpio_dataout(15, 1);
+       return 0;
+}
+
+static void innovator1610_panel_disable(struct lcd_panel *panel)
+{
+       /* set GPIO13, GPIO14 and GPIO15 low */
+       omap_set_gpio_dataout(14, 0);
+       omap_set_gpio_dataout(15, 0);
+}
+
+static unsigned long innovator1610_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel innovator1610_panel = {
+       .name           = "inn1610",
+       .config         = OMAP_LCDC_PANEL_TFT,
+
+       .bpp            = 16,
+       .data_lines     = 16,
+       .x_res          = 320,
+       .y_res          = 240,
+       .pixel_clock    = 12500,
+       .hsw            = 40,
+       .hfp            = 40,
+       .hbp            = 72,
+       .vsw            = 1,
+       .vfp            = 1,
+       .vbp            = 0,
+       .pcd            = 12,
+
+       .init           = innovator1610_panel_init,
+       .cleanup        = innovator1610_panel_cleanup,
+       .enable         = innovator1610_panel_enable,
+       .disable        = innovator1610_panel_disable,
+       .get_caps       = innovator1610_panel_get_caps,
+};
+
+static int innovator1610_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&innovator1610_panel);
+       return 0;
+}
+
+static int innovator1610_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int innovator1610_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int innovator1610_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver innovator1610_panel_driver = {
+       .probe          = innovator1610_panel_probe,
+       .remove         = innovator1610_panel_remove,
+       .suspend        = innovator1610_panel_suspend,
+       .resume         = innovator1610_panel_resume,
+       .driver         = {
+               .name   = "lcd_inn1610",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int innovator1610_panel_drv_init(void)
+{
+       return platform_driver_register(&innovator1610_panel_driver);
+}
+
+static void innovator1610_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&innovator1610_panel_driver);
+}
+
+module_init(innovator1610_panel_drv_init);
+module_exit(innovator1610_panel_drv_cleanup);
+
diff --git a/drivers/video/omap/lcd_mipid.c b/drivers/video/omap/lcd_mipid.c
new file mode 100644 (file)
index 0000000..acc18ab
--- /dev/null
@@ -0,0 +1,616 @@
+/*
+ * File: drivers/video/omap/lcd_mipid.c
+ *
+ * LCD driver for MIPI DBI-C / DCS compatible LCDs
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+
+#include <asm/arch/omapfb.h>
+#include <asm/arch/lcd_mipid.h>
+
+#include "../../cbus/tahvo.h"
+
+#define MIPID_MODULE_NAME              "lcd_mipid"
+
+#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)
+
+#define to_mipid_device(p)             container_of(p, struct mipid_device, \
+                                               panel)
+struct mipid_device {
+       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 omapfb_device    *fbdev;
+       struct spi_device       *spi;
+       struct mutex            mutex;
+       struct lcd_panel        panel;
+
+       struct workqueue_struct *esd_wq;
+       struct delayed_work     esd_work;
+       void                    (*esd_check)(struct mipid_device *m);
+};
+
+static void mipid_transfer(struct mipid_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 mipid_cmd(struct mipid_device *md, int cmd)
+{
+       mipid_transfer(md, cmd, NULL, 0, NULL, 0);
+}
+
+static inline void mipid_write(struct mipid_device *md,
+                              int reg, const u8 *buf, int len)
+{
+       mipid_transfer(md, reg, buf, len, NULL, 0);
+}
+
+static inline void mipid_read(struct mipid_device *md,
+                             int reg, u8 *buf, int len)
+{
+       mipid_transfer(md, reg, NULL, 0, buf, len);
+}
+
+static void set_data_lines(struct mipid_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;
+       }
+       mipid_write(md, 0x3a, (u8 *)&par, 2);
+}
+
+static void send_init_string(struct mipid_device *md)
+{
+       u16 initpar[] = { 0x0102, 0x0100, 0x0100 };
+
+       mipid_write(md, 0xc2, (u8 *)initpar, sizeof(initpar));
+       set_data_lines(md, md->panel.data_lines);
+}
+
+static void hw_guard_start(struct mipid_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 mipid_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 mipid_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);
+       mipid_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 mipid_device *md, int enabled)
+{
+       int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF;
+
+       mipid_cmd(md, cmd);
+}
+
+static int mipid_set_bklight_level(struct lcd_panel *panel, unsigned int level)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+
+       if (level > tahvo_get_max_backlight_level())
+               return -EINVAL;
+       if (!md->enabled) {
+               md->saved_bklight_level = level;
+               return 0;
+       }
+       tahvo_set_backlight_level(level);
+
+       return 0;
+}
+
+static unsigned int mipid_get_bklight_level(struct lcd_panel *panel)
+{
+       return tahvo_get_backlight_level();
+}
+
+static unsigned int mipid_get_bklight_max(struct lcd_panel *panel)
+{
+       return tahvo_get_max_backlight_level();
+}
+
+
+static unsigned long mipid_get_caps(struct lcd_panel *panel)
+{
+       return OMAPFB_CAPS_SET_BACKLIGHT;
+}
+
+static u16 read_first_pixel(struct mipid_device *md)
+{
+       u16 pixel;
+       u8 red, green, blue;
+
+       mutex_lock(&md->mutex);
+       mipid_read(md, MIPID_CMD_READ_RED, &red, 1);
+       mipid_read(md, MIPID_CMD_READ_GREEN, &green, 1);
+       mipid_read(md, MIPID_CMD_READ_BLUE, &blue, 1);
+       mutex_unlock(&md->mutex);
+
+       switch (md->panel.data_lines) {
+       case 16:
+               pixel = ((red >> 1) << 11) | (green << 5) | (blue >> 1);
+               break;
+       case 24:
+               /* 24 bit -> 16 bit */
+               pixel = ((red >> 3) << 11) | ((green >> 2) << 5) |
+                       (blue >> 3);
+               break;
+       default:
+               BUG();
+       }
+
+       return pixel;
+}
+
+static int mipid_run_test(struct lcd_panel *panel, int test_num)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+       static const u16 test_values[4] = {
+               0x0000, 0xffff, 0xaaaa, 0x5555,
+       };
+       int i;
+
+       if (test_num != MIPID_TEST_RGB_LINES)
+               return MIPID_TEST_INVALID;
+
+       for (i = 0; i < ARRAY_SIZE(test_values); i++) {
+               int delay;
+               unsigned long tmo;
+
+               omapfb_write_first_pixel(md->fbdev, test_values[i]);
+               tmo = jiffies + msecs_to_jiffies(100);
+               delay = 25;
+               while (1) {
+                       u16 pixel;
+
+                       msleep(delay);
+                       pixel = read_first_pixel(md);
+                       if (pixel == test_values[i])
+                               break;
+                       if (time_after(jiffies, tmo)) {
+                               dev_err(&md->spi->dev,
+                                       "MIPI LCD RGB I/F test failed: "
+                                       "expecting %04x, got %04x\n",
+                                       test_values[i], pixel);
+                               return MIPID_TEST_FAILED;
+                       }
+                       delay = 10;
+               }
+       }
+
+       return 0;
+}
+
+static void ls041y3_esd_recover(struct mipid_device *md)
+{
+       dev_err(&md->spi->dev, "performing LCD ESD recovery\n");
+       set_sleep_mode(md, 1);
+       set_sleep_mode(md, 0);
+}
+
+static void ls041y3_esd_check_mode1(struct mipid_device *md)
+{
+       u8 state1, state2;
+
+       mipid_read(md, MIPID_CMD_RDDSDR, &state1, 1);
+       set_sleep_mode(md, 0);
+       mipid_read(md, MIPID_CMD_RDDSDR, &state2, 1);
+       dev_dbg(&md->spi->dev, "ESD mode 1 state1 %02x state2 %02x\n",
+               state1, state2);
+       /* Each sleep out command will trigger a self diagnostic and flip
+       * Bit6 if the test passes.
+       */
+       if (!((state1 ^ state2) & (1 << 6)))
+               ls041y3_esd_recover(md);
+}
+
+static void ls041y3_esd_check_mode2(struct mipid_device *md)
+{
+       int i;
+       u8 rbuf[2];
+       static const struct {
+               int     cmd;
+               int     wlen;
+               u16     wbuf[3];
+       } *rd, rd_ctrl[7] = {
+               { 0xb0, 4, { 0x0101, 0x01fe, } },
+               { 0xb1, 4, { 0x01de, 0x0121, } },
+               { 0xc2, 4, { 0x0100, 0x0100, } },
+               { 0xbd, 2, { 0x0100, } },
+               { 0xc2, 4, { 0x01fc, 0x0103, } },
+               { 0xb4, 0, },
+               { 0x00, 0, },
+       };
+
+       rd = rd_ctrl;
+       for (i = 0; i < 3; i++, rd++)
+               mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen);
+
+       udelay(10);
+       mipid_read(md, rd->cmd, rbuf, 2);
+       rd++;
+
+       for (i = 0; i < 3; i++, rd++) {
+               udelay(10);
+               mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen);
+       }
+
+       dev_dbg(&md->spi->dev, "ESD mode 2 state %02x\n", rbuf[1]);
+       if (rbuf[1] == 0x00)
+               ls041y3_esd_recover(md);
+}
+
+static void ls041y3_esd_check(struct mipid_device *md)
+{
+       ls041y3_esd_check_mode1(md);
+       if (md->revision >= 0x88)
+               ls041y3_esd_check_mode2(md);
+}
+
+static void mipid_esd_start_check(struct mipid_device *md)
+{
+       if (md->esd_check != NULL)
+               queue_delayed_work(md->esd_wq, &md->esd_work,
+                                  MIPID_ESD_CHECK_PERIOD);
+}
+
+static void mipid_esd_stop_check(struct mipid_device *md)
+{
+       if (md->esd_check != NULL)
+               cancel_rearming_delayed_workqueue(md->esd_wq, &md->esd_work);
+}
+
+static void mipid_esd_work(struct work_struct *work)
+{
+       struct mipid_device *md = container_of(work, struct mipid_device, esd_work.work);
+
+       mutex_lock(&md->mutex);
+       md->esd_check(md);
+       mutex_unlock(&md->mutex);
+       mipid_esd_start_check(md);
+}
+
+static int mipid_enable(struct lcd_panel *panel)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+
+       mutex_lock(&md->mutex);
+
+       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 mipid_disable(struct lcd_panel *panel)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+
+       /* A final ESD work might be called before returning,
+        * so do this without holding the lock. */
+       mipid_esd_stop_check(md);
+       mutex_lock(&md->mutex);
+
+       if (!md->enabled) {
+               mutex_unlock(&md->mutex);
+               return;
+       }
+       md->saved_bklight_level = mipid_get_bklight_level(panel);
+       mipid_set_bklight_level(panel, 0);
+       set_display_state(md, 0);
+       set_sleep_mode(md, 1);
+       md->enabled = 0;
+
+       mutex_unlock(&md->mutex);
+}
+
+static int panel_enabled(struct mipid_device *md)
+{
+       u32 disp_status;
+       int enabled;
+
+       mipid_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);
+       return enabled;
+}
+
+static int mipid_init(struct lcd_panel *panel,
+                           struct omapfb_device *fbdev)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+
+       md->fbdev = fbdev;
+       md->esd_wq = create_singlethread_workqueue("mipid_esd");
+       if (md->esd_wq == NULL) {
+               dev_err(&md->spi->dev, "can't create ESD workqueue\n");
+               return -ENOMEM;
+       }
+       INIT_DELAYED_WORK(&md->esd_work, mipid_esd_work);
+       mutex_init(&md->mutex);
+
+       md->enabled = panel_enabled(md);
+
+       if (md->enabled)
+               mipid_esd_start_check(md);
+       else
+               md->saved_bklight_level = mipid_get_bklight_level(panel);
+
+       return 0;
+}
+
+static void mipid_cleanup(struct lcd_panel *panel)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+
+       if (md->enabled)
+               mipid_esd_stop_check(md);
+       destroy_workqueue(md->esd_wq);
+}
+
+static struct lcd_panel mipid_panel = {
+       .config         = OMAP_LCDC_PANEL_TFT,
+
+       .bpp            = 16,
+       .x_res          = 800,
+       .y_res          = 480,
+       .pixel_clock    = 21940,
+       .hsw            = 50,
+       .hfp            = 20,
+       .hbp            = 15,
+       .vsw            = 2,
+       .vfp            = 1,
+       .vbp            = 3,
+
+       .init           = mipid_init,
+       .cleanup        = mipid_cleanup,
+       .enable         = mipid_enable,
+       .disable        = mipid_disable,
+       .get_caps       = mipid_get_caps,
+       .set_bklight_level= mipid_set_bklight_level,
+       .get_bklight_level= mipid_get_bklight_level,
+       .get_bklight_max= mipid_get_bklight_max,
+       .run_test       = mipid_run_test,
+};
+
+static int mipid_detect(struct mipid_device *md)
+{
+       struct mipid_platform_data *pdata;
+
+       pdata = md->spi->dev.platform_data;
+       if (pdata == NULL) {
+               dev_err(&md->spi->dev, "missing platform data\n");
+               return -ENOENT;
+       }
+
+       mipid_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];
+       md->panel.data_lines = pdata->data_lines;
+       pr_info("omapfb: %s rev %02x LCD detected\n",
+                       md->panel.name, md->revision);
+
+       return 0;
+}
+
+static int mipid_spi_probe(struct spi_device *spi)
+{
+       struct mipid_device *md;
+       int r;
+
+       md = kzalloc(sizeof(*md), GFP_KERNEL);
+       if (md == NULL) {
+               dev_err(&md->spi->dev, "out of memory\n");
+               return -ENOMEM;
+       }
+
+       spi->mode = SPI_MODE_1;
+       md->spi = spi;
+       dev_set_drvdata(&spi->dev, md);
+       md->panel = mipid_panel;
+
+       r = mipid_detect(md);
+       if (r < 0)
+               return r;
+
+       omapfb_register_panel(&md->panel);
+
+       return 0;
+}
+
+static int mipid_spi_remove(struct spi_device *spi)
+{
+       struct mipid_device *md = dev_get_drvdata(&spi->dev);
+
+       mipid_disable(&md->panel);
+       kfree(md);
+
+       return 0;
+}
+
+static struct spi_driver mipid_spi_driver = {
+       .driver = {
+               .name   = MIPID_MODULE_NAME,
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe  = mipid_spi_probe,
+       .remove = __devexit_p(mipid_spi_remove),
+};
+
+static int mipid_drv_init(void)
+{
+       spi_register_driver(&mipid_spi_driver);
+
+       return 0;
+}
+module_init(mipid_drv_init);
+
+static void mipid_drv_cleanup(void)
+{
+       spi_unregister_driver(&mipid_spi_driver);
+}
+module_exit(mipid_drv_cleanup);
+
+MODULE_DESCRIPTION("MIPI display driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap/lcd_osk.c b/drivers/video/omap/lcd_osk.c
new file mode 100644 (file)
index 0000000..cc8bb59
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * File: drivers/video/omap/lcd-osk.c
+ *
+ * LCD panel support for the TI OMAP OSK board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ * Adapted for OSK by <dirk.behme@de.bosch.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/omapfb.h>
+
+static int osk_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void osk_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int osk_panel_enable(struct lcd_panel *panel)
+{
+       /* configure PWL pin */
+       omap_cfg_reg(PWL);
+
+       /* Enable PWL unit */
+       omap_writeb(0x01, OMAP_PWL_CLK_ENABLE);
+
+       /* Set PWL level */
+       omap_writeb(0xFF, OMAP_PWL_ENABLE);
+
+       /* configure GPIO2 as output */
+       omap_set_gpio_direction(2, 0);
+
+       /* set GPIO2 high */
+       omap_set_gpio_dataout(2, 1);
+
+       return 0;
+}
+
+static void osk_panel_disable(struct lcd_panel *panel)
+{
+       /* Set PWL level to zero */
+       omap_writeb(0x00, OMAP_PWL_ENABLE);
+
+       /* Disable PWL unit */
+       omap_writeb(0x00, OMAP_PWL_CLK_ENABLE);
+
+       /* set GPIO2 low */
+       omap_set_gpio_dataout(2, 0);
+}
+
+static unsigned long osk_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel osk_panel = {
+       .name           = "osk",
+       .config         = OMAP_LCDC_PANEL_TFT,
+
+       .bpp            = 16,
+       .data_lines     = 16,
+       .x_res          = 240,
+       .y_res          = 320,
+       .pixel_clock    = 12500,
+       .hsw            = 40,
+       .hfp            = 40,
+       .hbp            = 72,
+       .vsw            = 1,
+       .vfp            = 1,
+       .vbp            = 0,
+       .pcd            = 12,
+
+       .init           = osk_panel_init,
+       .cleanup        = osk_panel_cleanup,
+       .enable         = osk_panel_enable,
+       .disable        = osk_panel_disable,
+       .get_caps       = osk_panel_get_caps,
+};
+
+static int osk_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&osk_panel);
+       return 0;
+}
+
+static int osk_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int osk_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int osk_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver osk_panel_driver = {
+       .probe          = osk_panel_probe,
+       .remove         = osk_panel_remove,
+       .suspend        = osk_panel_suspend,
+       .resume         = osk_panel_resume,
+       .driver         = {
+               .name   = "lcd_osk",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int osk_panel_drv_init(void)
+{
+       return platform_driver_register(&osk_panel_driver);
+}
+
+static void osk_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&osk_panel_driver);
+}
+
+module_init(osk_panel_drv_init);
+module_exit(osk_panel_drv_cleanup);
+
diff --git a/drivers/video/omap/lcd_p2.c b/drivers/video/omap/lcd_p2.c
new file mode 100644 (file)
index 0000000..864033d
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * File: drivers/video/omap/lcd-p2.c
+ *
+ * LCD panel support for the TI OMAP P2 board
+ *
+ * Authors:
+ *   jekyll <jekyll@mail.jekyll.idv.tw>
+ *   B Jp <lastjp_fr@yahoo.fr>
+ *   Brian Swetland <swetland@android.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <asm/arch/mux.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/omapfb.h>
+
+/*
+ * File: epson-md-tft.h
+ *
+ * This file contains definitions for Epsons MD-TF LCD Module
+ *
+ * Copyright (C) 2004 MPC-Data Limited  (http://www.mpc-data.co.uk)
+ * Author: Dave Peverley <dpeverley at mpc-data.co.uk>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  SOFTWARE  IS  PROVIDED  ``AS  IS''  AND   ANY  EXPRESS  OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT,  INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the  GNU General Public License along
+ *  with this program; if not, write  to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Please report all bugs and problems to the author.
+ *
+ */
+
+/* LCD uWire commands & params
+ * All values from Epson
+ */
+#define LCD_DISON 0xAF
+#define LCD_DISOFF 0xAE
+#define LCD_DISNOR 0xA6
+#define LCD_DISINV 0xA7
+#define LCD_DISCTL 0xCA
+#define LCD_GCP64 0xCB
+#define LCD_GCP16 0xCC
+#define LCD_GSSET 0xCD
+#define LCD_SLPIN 0x95
+#define LCD_SLPOUT 0x94
+#define LCD_SD_PSET 0x75
+#define LCD_MD_PSET 0x76
+#define LCD_SD_CSET 0x15
+#define LCD_MD_CSET 0x16
+#define LCD_DATCTL 0xBC
+#define LCD_RAMWR 0x5C
+#define LCD_RAMRD 0x5D
+#define LCD_PTLIN 0xA8
+#define LCD_PTLOUT 0xA9
+#define LCD_ASCSET 0xAA
+#define LCD_SCSTART 0xAB
+#define LCD_VOLCTL 0xC6
+#define LCD_NOP 0x25
+#define LCD_OSCISEL 0x7
+#define LCD_3500KSET 0xD1
+#define LCD_3500KEND 0xD2
+#define LCD_14MSET 0xD3
+#define LCD_14MEND 0xD4
+
+#define INIT_3500KSET 0x45
+#define INIT_14MSET 0x4B
+#define INIT_DATCTL 0x08 /* 6.6.6 bits for D-Sample */
+
+#define INIT_OSCISEL 0x05
+
+#define INIT_VOLCTL 0x77 /* Nominel "volume" */
+
+#define INIT_VOLCTL_Ton 0x98 /* Activate power-IC timer */
+#define INIT_GSSET 0x00
+
+const unsigned short INIT_DISCTL[11] =
+{
+       0xDE, 0x01, 0x64, 0x00, 0x1B, 0xF4, 0x00, 0xDC, 0x00, 0x02, 0x00
+};
+
+const unsigned short INIT_GCP64[126] =
+{
+       0x3B,0x00,0x42,0x00,0x4A,0x00,0x51,0x00,
+       0x58,0x00,0x5F,0x00,0x66,0x00,0x6E,0x00,
+       0x75,0x00,0x7C,0x00,0x83,0x00,0x8A,0x00,
+       0x92,0x00,0x99,0x00,0xA0,0x00,0xA7,0x00,
+       0xAE,0x00,0xB6,0x00,0xBD,0x00,0xC4,0x00,
+       0xCB,0x00,0xD2,0x00,0xDA,0x00,0xE1,0x00,
+       0xE8,0x00,0xEF,0x00,0xF6,0x00,0xFE,0x00,
+       0x05,0x01,0x0C,0x01,0x13,0x01,0x1A,0x01,
+       0x22,0x01,0x29,0x01,0x30,0x01,0x37,0x01,
+       0x3E,0x01,0x46,0x01,0x4D,0x01,0x54,0x01,
+       0x5B,0x01,0x62,0x01,0x6A,0x01,0x71,0x01,
+       0x78,0x01,0x7F,0x01,0x86,0x01,0x8E,0x01,
+       0x95,0x01,0x9C,0x01,0xA3,0x01,0xAA,0x01,
+       0xB2,0x01,0xB9,0x01,0xC0,0x01,0xC7,0x01,
+       0xCE,0x01,0xD6,0x01,0xDD,0x01,0xE4,0x01,
+       0xEB,0x01,0xF2,0x01,0xFA,0x01
+};
+
+const unsigned short INIT_GCP16[15] =
+{
+       0x1A,0x31,0x48,0x54,0x5F,0x67,0x70,0x76,0x7C,0x80,0x83,0x84,0x85,0x87,0x96
+};
+
+const unsigned short INIT_MD_PSET[4] = { 0, 0, 219, 0 };
+const unsigned short INIT_MD_CSET[4] = { 2, 0, 177, 0 };
+
+const unsigned short INIT_SD_PSET[4] = { 0x00, 0x01, 0x00, 0x01 };
+const unsigned short INIT_SD_CSET[4] = { 0x00, 0x02, 0x00, 0x02 };
+
+const unsigned short INIT_ASCSET[7] = { 0x00, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0x01 };
+const unsigned short INIT_SCSTART[2] = { 0x00, 0x00 };
+
+/* ----- end of epson_md_tft.h ----- */
+
+
+#include "../drivers/ssi/omap-uwire.h"
+
+#define LCD_UWIRE_CS 0
+
+static int p2_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void p2_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int p2_panel_enable(struct lcd_panel *panel)
+{
+       int i;
+       unsigned long value;
+
+               /* thwack the reset line */
+       omap_set_gpio_direction(19, 0);
+       omap_set_gpio_dataout(19, 0);
+       mdelay(2);
+       omap_set_gpio_dataout(19, 1);
+
+               /* bits 31:28 -> 0  LCD_PXL_15 .. 12 */
+       value = omap_readl(OMAP730_IO_CONF_3) & 0x0FFFFFFF;
+       omap_writel(value, OMAP730_IO_CONF_3);
+
+               /* bits 19:0 -> 0  LCD_VSYNC, AC, PXL_0, PCLK, HSYNC,
+               **                 PXL_9..1, PXL_10, PXL_11
+               */
+       value = omap_readl(OMAP730_IO_CONF_4) & 0xFFF00000;
+       omap_writel(value, OMAP730_IO_CONF_4);
+
+       omap_uwire_configure_mode(0,16);
+
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISOFF, 9, 0,NULL,1);
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SLPIN, 9, 0,NULL,1);
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISNOR, 9, 0,NULL,1);
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GSSET, 9, 0,NULL,1);
+       omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GSSET | 0x100), 9, 0,NULL,1);
+
+       /* DISCTL */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISCTL, 9, 0,NULL,1);
+       for (i = 0; i < (sizeof(INIT_DISCTL)/sizeof(unsigned short)); i++)
+               omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_DISCTL[i] | 0x100), 9, 0,NULL,1);
+
+       /* GCP64 */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GCP64, 9, 0,NULL,1);
+       for (i = 0; i < (sizeof(INIT_GCP64)/sizeof(unsigned short)); i++)
+               omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GCP64[i] | 0x100), 9, 0,NULL,1);
+
+       /* GCP16 */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GCP16, 9, 0,NULL,1);
+       for (i = 0; i < (sizeof(INIT_GCP16)/sizeof(unsigned short)); i++)
+               omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GCP16[i] | 0x100), 9, 0,NULL,1);
+
+       /* MD_CSET */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_MD_CSET, 9, 0,NULL,1);
+       for (i = 0; i < (sizeof(INIT_MD_CSET)/sizeof(unsigned short)); i++)
+               omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_MD_CSET[i] | 0x100), 9, 0,NULL,1);
+
+       /* MD_PSET */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_MD_PSET, 9, 0,NULL,1);
+       for (i = 0; i < (sizeof(INIT_MD_PSET)/sizeof(unsigned short)); i++)
+               omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_MD_PSET[i] | 0x100), 9, 0,NULL,1);
+
+       /* SD_CSET */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SD_CSET, 9, 0,NULL,1);
+       for (i = 0; i < (sizeof(INIT_SD_CSET)/sizeof(unsigned short)); i++)
+               omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_SD_CSET[i] | 0x100), 9, 0,NULL,1);
+
+       /* SD_PSET */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SD_PSET, 9, 0,NULL,1);
+       for (i = 0; i < (sizeof(INIT_SD_PSET)/sizeof(unsigned short)); i++)
+               omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_SD_PSET[i] | 0x100), 9, 0,NULL,1);
+
+       /* DATCTL */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DATCTL, 9, 0,NULL,1);
+       omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_DATCTL | 0x100), 9, 0,NULL,1);
+
+       /* OSSISEL = d'5 */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_OSCISEL, 9, 0,NULL,1);
+       omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_OSCISEL | 0x100), 9, 0,NULL,1);
+
+       /* 14MSET = d'74 */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_14MSET, 9, 0,NULL,1);
+       omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_14MSET | 0x100), 9, 0,NULL,1);
+
+       /* 14MEND */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_14MEND, 9, 0,NULL,1);
+
+       /* 3500KSET = d'69 */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_3500KSET, 9, 0,NULL,1);
+       omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_3500KSET | 0x100), 9, 0,NULL,1);
+
+       /* 3500KEND */
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_3500KEND, 9, 0,NULL,1);
+
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SLPOUT, 9, 0,NULL,1);
+
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_VOLCTL, 9, 0,NULL,1);
+       omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_VOLCTL_Ton | 0x100), 9, 0,NULL,1);
+
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_VOLCTL, 9, 0,NULL,1);
+
+       omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_VOLCTL | 0x100), 9, 0,NULL,1);
+
+       omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISON, 9, 0,NULL,1);
+
+       /* enable backlight */
+       omap_set_gpio_direction(134, 0);
+       omap_set_gpio_dataout(134, 1);
+
+       return 0;
+}
+
+static void p2_panel_disable(struct lcd_panel *panel)
+{
+}
+
+static unsigned long p2_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel p2_panel = {
+       .name           = "p2",
+       .config         = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_PIX_CLOCK,
+
+       .bpp            = 16,
+       .data_lines     = 16,
+       .x_res          = 176,
+       .y_res          = 220,
+       .pixel_clock    = 12500,
+       .hsw            = 5,
+       .hfp            = 1,
+       .hbp            = 1,
+       .vsw            = 2,
+       .vfp            = 12,
+       .vbp            = 1,
+
+       .init           = p2_panel_init,
+       .cleanup        = p2_panel_cleanup,
+       .enable         = p2_panel_enable,
+       .disable        = p2_panel_disable,
+       .get_caps       = p2_panel_get_caps,
+};
+
+static int p2_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&p2_panel);
+       return 0;
+}
+
+static int p2_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int p2_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int p2_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver p2_panel_driver = {
+       .probe          = p2_panel_probe,
+       .remove         = p2_panel_remove,
+       .suspend        = p2_panel_suspend,
+       .resume         = p2_panel_resume,
+       .driver         = {
+               .name   = "lcd_p2",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int p2_panel_drv_init(void)
+{
+       return platform_driver_register(&p2_panel_driver);
+}
+
+static void p2_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&p2_panel_driver);
+}
+
+module_init(p2_panel_drv_init);
+module_exit(p2_panel_drv_cleanup);
+
diff --git a/drivers/video/omap/lcd_palmte.c b/drivers/video/omap/lcd_palmte.c
new file mode 100644 (file)
index 0000000..1d37803
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * File: drivers/video/omap/lcd_palmte.c
+ *
+ * LCD panel support for the Palm Tungsten E
+ *
+ * Original version : Romain Goyet
+ * Current version : Laurent Gonzalez
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/fpga.h>
+#include <asm/arch/omapfb.h>
+
+static int palmte_panel_init(struct lcd_panel *panel,
+                               struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void palmte_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int palmte_panel_enable(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+static void palmte_panel_disable(struct lcd_panel *panel)
+{
+}
+
+static unsigned long palmte_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel palmte_panel = {
+       .name           = "palmte",
+       .config         = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+                         OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE |
+                         OMAP_LCDC_HSVS_OPPOSITE,
+
+       .data_lines     = 16,
+       .bpp            = 8,
+       .pixel_clock    = 12000,
+       .x_res          = 320,
+       .y_res          = 320,
+       .hsw            = 4,
+       .hfp            = 8,
+       .hbp            = 28,
+       .vsw            = 1,
+       .vfp            = 8,
+       .vbp            = 7,
+       .pcd            = 0,
+
+       .init           = palmte_panel_init,
+       .cleanup        = palmte_panel_cleanup,
+       .enable         = palmte_panel_enable,
+       .disable        = palmte_panel_disable,
+       .get_caps       = palmte_panel_get_caps,
+};
+
+static int palmte_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&palmte_panel);
+       return 0;
+}
+
+static int palmte_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int palmte_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int palmte_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver palmte_panel_driver = {
+       .probe          = palmte_panel_probe,
+       .remove         = palmte_panel_remove,
+       .suspend        = palmte_panel_suspend,
+       .resume         = palmte_panel_resume,
+       .driver         = {
+               .name   = "lcd_palmte",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int palmte_panel_drv_init(void)
+{
+       return platform_driver_register(&palmte_panel_driver);
+}
+
+static void palmte_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&palmte_panel_driver);
+}
+
+module_init(palmte_panel_drv_init);
+module_exit(palmte_panel_drv_cleanup);
+
diff --git a/drivers/video/omap/lcd_palmtt.c b/drivers/video/omap/lcd_palmtt.c
new file mode 100644 (file)
index 0000000..97d3680
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * File: drivers/video/omap/lcd_palmtt.c
+ *
+ * LCD panel support for Palm Tungsten|T
+ * Current version : Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Modified from lcd_inn1510.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/*
+GPIO11 - backlight
+GPIO12 - screen blanking
+GPIO13 - screen blanking
+*/
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/gpio.h>
+#include "asm/arch/omapfb.h"
+
+static int palmtt_panel_init(struct lcd_panel *panel,
+       struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void palmtt_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int palmtt_panel_enable(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+static void palmtt_panel_disable(struct lcd_panel *panel)
+{
+}
+
+static unsigned long palmtt_panel_get_caps(struct lcd_panel *panel)
+{
+       return OMAPFB_CAPS_SET_BACKLIGHT;
+}
+
+struct lcd_panel palmtt_panel = {
+       .name           = "palmtt",
+       .config         = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+                       OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE |
+                       OMAP_LCDC_HSVS_OPPOSITE,
+       .bpp            = 16,
+       .data_lines     = 16,
+       .x_res          = 320,
+       .y_res          = 320,
+       .pixel_clock    = 10000,
+       .hsw            = 4,
+       .hfp            = 8,
+       .hbp            = 28,
+       .vsw            = 1,
+       .vfp            = 8,
+       .vbp            = 7,
+       .pcd            = 0,
+
+       .init= palmtt_panel_init,
+       .cleanup        = palmtt_panel_cleanup,
+       .enable= palmtt_panel_enable,
+       .disable        = palmtt_panel_disable,
+       .get_caps       = palmtt_panel_get_caps,
+};
+
+static int palmtt_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&palmtt_panel);
+       return 0;
+}
+
+static int palmtt_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int palmtt_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int palmtt_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver palmtt_panel_driver = {
+       .probe          = palmtt_panel_probe,
+       .remove         = palmtt_panel_remove,
+       .suspend        = palmtt_panel_suspend,
+       .resume         = palmtt_panel_resume,
+       .driver         = {
+               .name   = "lcd_palmtt",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int palmtt_panel_drv_init(void)
+{
+       return platform_driver_register(&palmtt_panel_driver);
+}
+
+static void palmtt_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&palmtt_panel_driver);
+}
+
+module_init(palmtt_panel_drv_init);
+module_exit(palmtt_panel_drv_cleanup);
diff --git a/drivers/video/omap/lcd_palmz71.c b/drivers/video/omap/lcd_palmz71.c
new file mode 100644 (file)
index 0000000..892993f
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * File: drivers/video/omap/lcd_palmz71.c
+ *
+ * LCD panel support for the Palm Zire71
+ *
+ * Original version : Romain Goyet
+ * Current version : Laurent Gonzalez
+ * Modified for zire71 : Marek Vasut
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/arch/omapfb.h>
+
+static int palmz71_panel_init(struct lcd_panel *panel,
+                             struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void palmz71_panel_cleanup(struct lcd_panel *panel)
+{
+
+}
+
+static int palmz71_panel_enable(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+static void palmz71_panel_disable(struct lcd_panel *panel)
+{
+}
+
+static unsigned long palmz71_panel_get_caps(struct lcd_panel *panel)
+{
+       return OMAPFB_CAPS_SET_BACKLIGHT;
+}
+
+struct lcd_panel palmz71_panel = {
+       .name           = "palmz71",
+       .config         = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+                         OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE |
+                         OMAP_LCDC_HSVS_OPPOSITE,
+       .data_lines     = 16,
+       .bpp            = 16,
+       .pixel_clock    = 24000,
+       .x_res          = 320,
+       .y_res          = 320,
+       .hsw            = 4,
+       .hfp            = 8,
+       .hbp            = 28,
+       .vsw            = 1,
+       .vfp            = 8,
+       .vbp            = 7,
+       .pcd            = 0,
+
+       .init           = palmz71_panel_init,
+       .cleanup        = palmz71_panel_cleanup,
+       .enable         = palmz71_panel_enable,
+       .disable        = palmz71_panel_disable,
+       .get_caps       = palmz71_panel_get_caps,
+};
+
+static int palmz71_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&palmz71_panel);
+       return 0;
+}
+
+static int palmz71_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int palmz71_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int palmz71_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver palmz71_panel_driver = {
+       .probe          = palmz71_panel_probe,
+       .remove         = palmz71_panel_remove,
+       .suspend        = palmz71_panel_suspend,
+       .resume         = palmz71_panel_resume,
+       .driver         = {
+               .name   = "lcd_palmz71",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int palmz71_panel_drv_init(void)
+{
+       return platform_driver_register(&palmz71_panel_driver);
+}
+
+static void palmz71_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&palmz71_panel_driver);
+}
+
+module_init(palmz71_panel_drv_init);
+module_exit(palmz71_panel_drv_cleanup);
diff --git a/drivers/video/omap/lcd_sx1.c b/drivers/video/omap/lcd_sx1.c
new file mode 100644 (file)
index 0000000..0b927f6
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * File: drivers/video/omap/lcd_sx1.c
+ *
+ * LCD panel support for the Siemens SX1 mobile phone
+ *
+ * Current version : Vovan888 at gmail com, great help from FCA00000
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/omapfb.h>
+#include <asm/arch/mcbsp.h>
+#include <asm/arch/mux.h>
+
+/*
+ * OMAP310 GPIO registers
+ */
+#define GPIO_DATA_INPUT                0xfffce000
+#define GPIO_DATA_OUTPUT       0xfffce004
+#define GPIO_DIR_CONTROL       0xfffce008
+#define GPIO_INT_CONTROL       0xfffce00c
+#define GPIO_INT_MASK          0xfffce010
+#define GPIO_INT_STATUS                0xfffce014
+#define GPIO_PIN_CONTROL       0xfffce018
+
+
+#define A_LCD_SSC_RD   3
+#define A_LCD_SSC_SD   7
+#define _A_LCD_RESET   9
+#define _A_LCD_SSC_CS  12
+#define _A_LCD_SSC_A0  13
+
+#define DSP_REG                0xE1017024
+
+const unsigned char INIT_1[12] = {
+       0x1C, 0x02, 0x88, 0x00, 0x1E, 0xE0, 0x00, 0xDC, 0x00, 0x02, 0x00
+};
+
+const unsigned char INIT_2[127] = {
+       0x15, 0x00, 0x29, 0x00, 0x3E, 0x00, 0x51, 0x00, 0x65, 0x00, 0x7A, 0x00, 0x8D, 0x00, 0xA1, 0x00,
+       0xB6, 0x00, 0xC7, 0x00, 0xD8, 0x00, 0xEB, 0x00, 0xFB, 0x00, 0x0B, 0x01, 0x1B, 0x01, 0x27, 0x01,
+       0x34, 0x01, 0x41, 0x01, 0x4C, 0x01, 0x55, 0x01, 0x5F, 0x01, 0x68, 0x01, 0x70, 0x01, 0x78, 0x01,
+       0x7E, 0x01, 0x86, 0x01, 0x8C, 0x01, 0x94, 0x01, 0x9B, 0x01, 0xA1, 0x01, 0xA4, 0x01, 0xA9, 0x01,
+       0xAD, 0x01, 0xB2, 0x01, 0xB7, 0x01, 0xBC, 0x01, 0xC0, 0x01, 0xC4, 0x01, 0xC8, 0x01, 0xCB, 0x01,
+       0xCF, 0x01, 0xD2, 0x01, 0xD5, 0x01, 0xD8, 0x01, 0xDB, 0x01, 0xE0, 0x01, 0xE3, 0x01, 0xE6, 0x01,
+       0xE8, 0x01, 0xEB, 0x01, 0xEE, 0x01, 0xF1, 0x01, 0xF3, 0x01, 0xF8, 0x01, 0xF9, 0x01, 0xFC, 0x01,
+       0x00, 0x02, 0x03, 0x02, 0x07, 0x02, 0x09, 0x02, 0x0E, 0x02, 0x13, 0x02, 0x1C, 0x02, 0x00
+};
+
+const unsigned char INIT_3[15] = {
+       0x14, 0x26, 0x33, 0x3D, 0x45, 0x4D, 0x53, 0x59,
+       0x5E, 0x63, 0x67, 0x6D, 0x71, 0x78, 0xFF
+};
+
+static void epson_sendbyte(int flag, unsigned char byte)
+{
+       int i, shifter = 0x80;
+
+       if (!flag)
+               omap_set_gpio_dataout(_A_LCD_SSC_A0, 0);
+       mdelay(2);
+       omap_set_gpio_dataout(A_LCD_SSC_RD, 1);
+
+       omap_set_gpio_dataout(A_LCD_SSC_SD, flag);
+
+       OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2200);
+       OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2202);
+       for (i = 0; i < 8; i++) {
+               OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2200);
+               omap_set_gpio_dataout(A_LCD_SSC_SD, shifter & byte);
+               OMAP_MCBSP_WRITE(OMAP1510_MCBSP3_BASE, PCR0, 0x2202);
+               shifter >>= 1;
+       }
+       omap_set_gpio_dataout(_A_LCD_SSC_A0, 1);
+}
+
+static void init_system(void)
+{
+       omap_mcbsp_request(OMAP_MCBSP3);
+       omap_mcbsp_stop(OMAP_MCBSP3);
+}
+
+static void setup_GPIO(void)
+{
+       /* new wave */
+       omap_request_gpio(A_LCD_SSC_RD);
+       omap_request_gpio(A_LCD_SSC_SD);
+       omap_request_gpio(_A_LCD_RESET);
+       omap_request_gpio(_A_LCD_SSC_CS);
+       omap_request_gpio(_A_LCD_SSC_A0);
+
+       /* set all GPIOs to output */
+       omap_set_gpio_direction(A_LCD_SSC_RD, 0);
+       omap_set_gpio_direction(A_LCD_SSC_SD, 0);
+       omap_set_gpio_direction(_A_LCD_RESET, 0);
+       omap_set_gpio_direction(_A_LCD_SSC_CS, 0);
+       omap_set_gpio_direction(_A_LCD_SSC_A0, 0);
+
+       /* set GPIO data */
+       omap_set_gpio_dataout(A_LCD_SSC_RD, 1);
+       omap_set_gpio_dataout(A_LCD_SSC_SD, 0);
+       omap_set_gpio_dataout(_A_LCD_RESET, 0);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       omap_set_gpio_dataout(_A_LCD_SSC_A0, 1);
+}
+
+static void display_init(void)
+{
+       int i;
+
+       omap_cfg_reg(MCBSP3_CLKX);
+
+       mdelay(2);
+       setup_GPIO();
+       mdelay(2);
+
+       /* reset LCD */
+       omap_set_gpio_dataout(A_LCD_SSC_SD, 1);
+       epson_sendbyte(0, 0x25);
+
+       omap_set_gpio_dataout(_A_LCD_RESET, 0);
+       mdelay(10);
+       omap_set_gpio_dataout(_A_LCD_RESET, 1);
+
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       mdelay(2);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       /* init LCD, phase 1 */
+       epson_sendbyte(0, 0xCA);
+       for (i = 0; i < 10; i++)
+               epson_sendbyte(1, INIT_1[i]);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       /* init LCD phase 2 */
+       epson_sendbyte(0, 0xCB);
+       for( i = 0; i < 125; i++)
+               epson_sendbyte(1, INIT_2[i]);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       /* init LCD phase 2a */
+       epson_sendbyte(0, 0xCC);
+       for( i = 0; i < 14; i++)
+               epson_sendbyte(1, INIT_3[i]);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       /* init LCD phase 3 */
+       epson_sendbyte(0, 0xBC);
+       epson_sendbyte(1, 0x08);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       /* init LCD phase 4 */
+       epson_sendbyte(0, 0x07);
+       epson_sendbyte(1, 0x05);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       /* init LCD phase 5 */
+       epson_sendbyte(0, 0x94);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       /* init LCD phase 6 */
+       epson_sendbyte(0, 0xC6);
+       epson_sendbyte(1, 0x80);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       mdelay(100); /* used to be 1000 */
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       /* init LCD phase 7 */
+       epson_sendbyte(0, 0x16);
+       epson_sendbyte(1, 0x02);
+       epson_sendbyte(1, 0x00);
+       epson_sendbyte(1, 0xB1);
+       epson_sendbyte(1, 0x00);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       /* init LCD phase 8 */
+       epson_sendbyte(0, 0x76);
+       epson_sendbyte(1, 0x00);
+       epson_sendbyte(1, 0x00);
+       epson_sendbyte(1, 0xDB);
+       epson_sendbyte(1, 0x00);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       /* init LCD phase 9 */
+       epson_sendbyte(0, 0xAF);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+}
+
+static int sx1_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void sx1_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static void sx1_panel_disable(struct lcd_panel *panel)
+{
+       printk(KERN_INFO "SX1: LCD panel disable\n");
+       sx1_setmmipower(0);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+
+       epson_sendbyte(0, 0x25);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       epson_sendbyte(0, 0xAE);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+       mdelay(100);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 0);
+
+       epson_sendbyte(0, 0x95);
+       omap_set_gpio_dataout(_A_LCD_SSC_CS, 1);
+}
+
+static int sx1_panel_enable(struct lcd_panel *panel)
+{
+#if 0
+       sx1_panel_disable(); /* try to disable panel first, if we boot from Symbian */
+#endif
+
+       printk(KERN_INFO "lcd_sx1: LCD panel enable\n");
+       init_system();
+       display_init();
+
+       sx1_setmmipower(1);
+       sx1_setbacklight(0x18);
+       sx1_setkeylight (0x06);
+       return 0;
+}
+
+
+static unsigned long sx1_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel sx1_panel = {
+       .name           = "sx1",
+       .config         = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+                         OMAP_LCDC_INV_HSYNC | OMAP_LCDC_INV_PIX_CLOCK |
+                         OMAP_LCDC_INV_OUTPUT_EN,
+
+       .x_res          = 176,
+       .y_res          = 220,
+       .data_lines     = 16,
+       .bpp            = 16,
+       .hsw            = 5,
+       .hfp            = 5,
+       .hbp            = 5,
+       .vsw            = 2,
+       .vfp            = 1,
+       .vbp            = 1,
+       .pixel_clock    = 1500,
+
+       .init           = sx1_panel_init,
+       .cleanup        = sx1_panel_cleanup,
+       .enable         = sx1_panel_enable,
+       .disable        = sx1_panel_disable,
+       .get_caps       = sx1_panel_get_caps,
+};
+
+static int sx1_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&sx1_panel);
+       return 0;
+}
+
+static int sx1_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int sx1_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int sx1_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver sx1_panel_driver = {
+       .probe          = sx1_panel_probe,
+       .remove         = sx1_panel_remove,
+       .suspend        = sx1_panel_suspend,
+       .resume         = sx1_panel_resume,
+       .driver = {
+               .name   = "lcd_sx1",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int sx1_panel_drv_init(void)
+{
+       return platform_driver_register(&sx1_panel_driver);
+}
+
+static void sx1_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&sx1_panel_driver);
+}
+
+module_init(sx1_panel_drv_init);
+module_exit(sx1_panel_drv_cleanup);
diff --git a/drivers/video/omap/lcdc.c b/drivers/video/omap/lcdc.c
new file mode 100644 (file)
index 0000000..0f86843
--- /dev/null
@@ -0,0 +1,887 @@
+/*
+ * File: drivers/video/omap/omap1/lcdc.c
+ *
+ * OMAP1 internal LCD controller
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/clk.h>
+
+#include <asm/arch/dma.h>
+#include <asm/arch/omapfb.h>
+
+#include <asm/mach-types.h>
+
+#define MODULE_NAME                    "lcdc"
+
+#define OMAP_LCDC_BASE                 0xfffec000
+#define OMAP_LCDC_SIZE                 256
+#define OMAP_LCDC_IRQ                  INT_LCD_CTRL
+
+#define OMAP_LCDC_CONTROL              (OMAP_LCDC_BASE + 0x00)
+#define OMAP_LCDC_TIMING0              (OMAP_LCDC_BASE + 0x04)
+#define OMAP_LCDC_TIMING1              (OMAP_LCDC_BASE + 0x08)
+#define OMAP_LCDC_TIMING2              (OMAP_LCDC_BASE + 0x0c)
+#define OMAP_LCDC_STATUS               (OMAP_LCDC_BASE + 0x10)
+#define OMAP_LCDC_SUBPANEL             (OMAP_LCDC_BASE + 0x14)
+#define OMAP_LCDC_LINE_INT             (OMAP_LCDC_BASE + 0x18)
+#define OMAP_LCDC_DISPLAY_STATUS       (OMAP_LCDC_BASE + 0x1c)
+
+#define OMAP_LCDC_STAT_DONE            (1 << 0)
+#define OMAP_LCDC_STAT_VSYNC           (1 << 1)
+#define OMAP_LCDC_STAT_SYNC_LOST       (1 << 2)
+#define OMAP_LCDC_STAT_ABC             (1 << 3)
+#define OMAP_LCDC_STAT_LINE_INT                (1 << 4)
+#define OMAP_LCDC_STAT_FUF             (1 << 5)
+#define OMAP_LCDC_STAT_LOADED_PALETTE  (1 << 6)
+
+#define OMAP_LCDC_CTRL_LCD_EN          (1 << 0)
+#define OMAP_LCDC_CTRL_LCD_TFT         (1 << 7)
+#define OMAP_LCDC_CTRL_LINE_IRQ_CLR_SEL        (1 << 10)
+
+#define OMAP_LCDC_IRQ_VSYNC            (1 << 2)
+#define OMAP_LCDC_IRQ_DONE             (1 << 3)
+#define OMAP_LCDC_IRQ_LOADED_PALETTE   (1 << 4)
+#define OMAP_LCDC_IRQ_LINE_NIRQ                (1 << 5)
+#define OMAP_LCDC_IRQ_LINE             (1 << 6)
+#define OMAP_LCDC_IRQ_MASK             (((1 << 5) - 1) << 2)
+
+#define MAX_PALETTE_SIZE               PAGE_SIZE
+
+enum lcdc_load_mode {
+       OMAP_LCDC_LOAD_PALETTE,
+       OMAP_LCDC_LOAD_FRAME,
+       OMAP_LCDC_LOAD_PALETTE_AND_FRAME
+};
+
+static struct omap_lcd_controller {
+       enum omapfb_update_mode update_mode;
+       int                     ext_mode;
+
+       unsigned long           frame_offset;
+       int                     screen_width;
+       int                     xres;
+       int                     yres;
+
+       enum omapfb_color_format        color_mode;
+       int                     bpp;
+       void                    *palette_virt;
+       dma_addr_t              palette_phys;
+       int                     palette_code;
+       int                     palette_size;
+
+       unsigned int            irq_mask;
+       struct completion       last_frame_complete;
+       struct completion       palette_load_complete;
+       struct clk              *lcd_ck;
+       struct omapfb_device    *fbdev;
+
+       void                    (*dma_callback)(void *data);
+       void                    *dma_callback_data;
+
+       int                     fbmem_allocated;
+       dma_addr_t              vram_phys;
+       void                    *vram_virt;
+       unsigned long           vram_size;
+} lcdc;
+
+static void inline enable_irqs(int mask)
+{
+       lcdc.irq_mask |= mask;
+}
+
+static void inline disable_irqs(int mask)
+{
+       lcdc.irq_mask &= ~mask;
+}
+
+static void set_load_mode(enum lcdc_load_mode mode)
+{
+       u32 l;
+
+       l = omap_readl(OMAP_LCDC_CONTROL);
+       l &= ~(3 << 20);
+       switch (mode) {
+       case OMAP_LCDC_LOAD_PALETTE:
+               l |= 1 << 20;
+               break;
+       case OMAP_LCDC_LOAD_FRAME:
+               l |= 2 << 20;
+               break;
+       case OMAP_LCDC_LOAD_PALETTE_AND_FRAME:
+               break;
+       default:
+               BUG();
+       }
+       omap_writel(l, OMAP_LCDC_CONTROL);
+}
+
+static void enable_controller(void)
+{
+       u32 l;
+
+       l = omap_readl(OMAP_LCDC_CONTROL);
+       l |= OMAP_LCDC_CTRL_LCD_EN;
+       l &= ~OMAP_LCDC_IRQ_MASK;
+       l |= lcdc.irq_mask | OMAP_LCDC_IRQ_DONE;        /* enabled IRQs */
+       omap_writel(l, OMAP_LCDC_CONTROL);
+}
+
+static void disable_controller_async(void)
+{
+       u32 l;
+       u32 mask;
+
+       l = omap_readl(OMAP_LCDC_CONTROL);
+       mask = OMAP_LCDC_CTRL_LCD_EN | OMAP_LCDC_IRQ_MASK;
+       /* Preserve the DONE mask, since we still want to get the
+        * final DONE irq. It will be disabled in the IRQ handler.
+        */
+       mask &= ~OMAP_LCDC_IRQ_DONE;
+       l &= ~mask;
+       omap_writel(l, OMAP_LCDC_CONTROL);
+}
+
+static void disable_controller(void)
+{
+       init_completion(&lcdc.last_frame_complete);
+       disable_controller_async();
+       if (!wait_for_completion_timeout(&lcdc.last_frame_complete,
+                               msecs_to_jiffies(500)))
+               dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n");
+}
+
+static void reset_controller(u32 status)
+{
+       static unsigned long reset_count = 0;
+       static unsigned long last_jiffies = 0;
+
+       disable_controller_async();
+       reset_count++;
+       if (reset_count == 1 || time_after(jiffies, last_jiffies + HZ)) {
+               dev_err(lcdc.fbdev->dev,
+                         "resetting (status %#010x,reset count %lu)\n",
+                         status, reset_count);
+               last_jiffies = jiffies;
+       }
+       if (reset_count < 100) {
+               enable_controller();
+       } else {
+               reset_count = 0;
+               dev_err(lcdc.fbdev->dev,
+                       "too many reset attempts, giving up.\n");
+       }
+}
+
+/* Configure the LCD DMA according to the current mode specified by parameters
+ * in lcdc.fbdev and fbdev->var.
+ */
+static void setup_lcd_dma(void)
+{
+       static const int dma_elem_type[] = {
+               0,
+               OMAP_DMA_DATA_TYPE_S8,
+               OMAP_DMA_DATA_TYPE_S16,
+               0,
+               OMAP_DMA_DATA_TYPE_S32,
+       };
+       struct omapfb_plane_struct *plane = lcdc.fbdev->fb_info[0]->par;
+       struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var;
+       unsigned long   src;
+       int             esize, xelem, yelem;
+
+       src = lcdc.vram_phys + lcdc.frame_offset;
+
+       switch (var->rotate) {
+       case 0:
+               if (plane->info.mirror || (src & 3) ||
+                   lcdc.color_mode == OMAPFB_COLOR_YUV420 ||
+                   (lcdc.xres & 1))
+                       esize = 2;
+               else
+                       esize = 4;
+               xelem = lcdc.xres * lcdc.bpp / 8 / esize;
+               yelem = lcdc.yres;
+               break;
+       case 90:
+       case 180:
+       case 270:
+               if (cpu_is_omap15xx()) {
+                       BUG();
+               }
+               esize = 2;
+               xelem = lcdc.yres * lcdc.bpp / 16;
+               yelem = lcdc.xres;
+               break;
+       default:
+               BUG();
+               return;
+       }
+#ifdef VERBOSE
+       dev_dbg(lcdc.fbdev->dev,
+                "setup_dma: src %#010lx esize %d xelem %d yelem %d\n",
+                src, esize, xelem, yelem);
+#endif
+       omap_set_lcd_dma_b1(src, xelem, yelem, dma_elem_type[esize]);
+       if (!cpu_is_omap15xx()) {
+               int bpp = lcdc.bpp;
+
+               /* YUV support is only for external mode when we have the
+                * YUV window embedded in a 16bpp frame buffer.
+                */
+               if (lcdc.color_mode == OMAPFB_COLOR_YUV420)
+                       bpp = 16;
+               /* Set virtual xres elem size */
+               omap_set_lcd_dma_b1_vxres(
+                       lcdc.screen_width * bpp / 8 / esize);
+               /* Setup transformations */
+               omap_set_lcd_dma_b1_rotation(var->rotate);
+               omap_set_lcd_dma_b1_mirror(plane->info.mirror);
+       }
+       omap_setup_lcd_dma();
+}
+
+static irqreturn_t lcdc_irq_handler(int irq, void *dev_id)
+{
+       u32 status;
+
+       status = omap_readl(OMAP_LCDC_STATUS);
+
+       if (status & (OMAP_LCDC_STAT_FUF | OMAP_LCDC_STAT_SYNC_LOST))
+               reset_controller(status);
+       else {
+               if (status & OMAP_LCDC_STAT_DONE) {
+                       u32 l;
+
+                       /* Disable IRQ_DONE. The status bit will be cleared
+                        * only when the controller is reenabled and we don't
+                        * want to get more interrupts.
+                        */
+                       l = omap_readl(OMAP_LCDC_CONTROL);
+                       l &= ~OMAP_LCDC_IRQ_DONE;
+                       omap_writel(l, OMAP_LCDC_CONTROL);
+                       complete(&lcdc.last_frame_complete);
+               }
+               if (status & OMAP_LCDC_STAT_LOADED_PALETTE) {
+                       disable_controller_async();
+                       complete(&lcdc.palette_load_complete);
+               }
+       }
+
+       /* Clear these interrupt status bits.
+        * Sync_lost, FUF bits were cleared by disabling the LCD controller
+        * LOADED_PALETTE can be cleared this way only in palette only
+        * load mode. In other load modes it's cleared by disabling the
+        * controller.
+        */
+       status &= ~(OMAP_LCDC_STAT_VSYNC |
+                   OMAP_LCDC_STAT_LOADED_PALETTE |
+                   OMAP_LCDC_STAT_ABC |
+                   OMAP_LCDC_STAT_LINE_INT);
+       omap_writel(status, OMAP_LCDC_STATUS);
+       return IRQ_HANDLED;
+}
+
+/* Change to a new video mode. We defer this to a later time to avoid any
+ * flicker and not to mess up the current LCD DMA context. For this we disable
+ * the LCD controler, which will generate a DONE irq after the last frame has
+ * been transferred. Then it'll be safe to reconfigure both the LCD controller
+ * as well as the LCD DMA.
+ */
+static int omap_lcdc_setup_plane(int plane, int channel_out,
+                                unsigned long offset, int screen_width,
+                                int pos_x, int pos_y, int width, int height,
+                                int color_mode)
+{
+       struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var;
+       struct lcd_panel *panel = lcdc.fbdev->panel;
+       int rot_x, rot_y;
+
+       if (var->rotate == 0) {
+               rot_x = panel->x_res;
+               rot_y = panel->y_res;
+       } else {
+               rot_x = panel->y_res;
+               rot_y = panel->x_res;
+       }
+       if (plane != 0 || channel_out != 0 || pos_x != 0 || pos_y != 0 ||
+           width > rot_x || height > rot_y) {
+#ifdef VERBOSE
+               dev_dbg(lcdc.fbdev->dev,
+                       "invalid plane params plane %d pos_x %d pos_y %d "
+                       "w %d h %d\n", plane, pos_x, pos_y, width, height);
+#endif
+               return -EINVAL;
+       }
+
+       lcdc.frame_offset = offset;
+       lcdc.xres = width;
+       lcdc.yres = height;
+       lcdc.screen_width = screen_width;
+       lcdc.color_mode = color_mode;
+
+       switch (color_mode) {
+       case OMAPFB_COLOR_CLUT_8BPP:
+               lcdc.bpp = 8;
+               lcdc.palette_code = 0x3000;
+               lcdc.palette_size = 512;
+               break;
+       case OMAPFB_COLOR_RGB565:
+               lcdc.bpp = 16;
+               lcdc.palette_code = 0x4000;
+               lcdc.palette_size = 32;
+               break;
+       case OMAPFB_COLOR_RGB444:
+               lcdc.bpp = 16;
+               lcdc.palette_code = 0x4000;
+               lcdc.palette_size = 32;
+               break;
+       case OMAPFB_COLOR_YUV420:
+               if (lcdc.ext_mode) {
+                       lcdc.bpp = 12;
+                       break;
+               }
+               /* fallthrough */
+       case OMAPFB_COLOR_YUV422:
+               if (lcdc.ext_mode) {
+                       lcdc.bpp = 16;
+                       break;
+               }
+               /* fallthrough */
+       default:
+               /* FIXME: other BPPs.
+                * bpp1: code  0,     size 256
+                * bpp2: code  0x1000 size 256
+                * bpp4: code  0x2000 size 256
+                * bpp12: code 0x4000 size 32
+                */
+               dev_dbg(lcdc.fbdev->dev, "invalid color mode %d\n", color_mode);
+               BUG();
+               return -1;
+       }
+
+       if (lcdc.ext_mode) {
+               setup_lcd_dma();
+               return 0;
+       }
+
+       if (lcdc.update_mode == OMAPFB_AUTO_UPDATE) {
+               disable_controller();
+               omap_stop_lcd_dma();
+               setup_lcd_dma();
+               enable_controller();
+       }
+
+       return 0;
+}
+
+static int omap_lcdc_enable_plane(int plane, int enable)
+{
+       dev_dbg(lcdc.fbdev->dev,
+               "plane %d enable %d update_mode %d ext_mode %d\n",
+               plane, enable, lcdc.update_mode, lcdc.ext_mode);
+       if (plane != OMAPFB_PLANE_GFX)
+               return -EINVAL;
+
+       return 0;
+}
+
+/* Configure the LCD DMA for a palette load operation and do the palette
+ * downloading synchronously. We don't use the frame+palette load mode of
+ * the controller, since the palette can always be downloaded seperately.
+ */
+static void load_palette(void)
+{
+       u16     *palette;
+
+       palette = (u16 *)lcdc.palette_virt;
+
+       *(u16 *)palette &= 0x0fff;
+       *(u16 *)palette |= lcdc.palette_code;
+
+       omap_set_lcd_dma_b1(lcdc.palette_phys,
+               lcdc.palette_size / 4 + 1, 1, OMAP_DMA_DATA_TYPE_S32);
+
+       omap_set_lcd_dma_single_transfer(1);
+       omap_setup_lcd_dma();
+
+       init_completion(&lcdc.palette_load_complete);
+       enable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);
+       set_load_mode(OMAP_LCDC_LOAD_PALETTE);
+       enable_controller();
+       if (!wait_for_completion_timeout(&lcdc.palette_load_complete,
+                               msecs_to_jiffies(500)))
+               dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n");
+       /* The controller gets disabled in the irq handler */
+       disable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);
+       omap_stop_lcd_dma();
+
+       omap_set_lcd_dma_single_transfer(lcdc.ext_mode);
+}
+
+/* Used only in internal controller mode */
+static int omap_lcdc_setcolreg(u_int regno, u16 red, u16 green, u16 blue,
+                              u16 transp, int update_hw_pal)
+{
+       u16 *palette;
+
+       if (lcdc.color_mode != OMAPFB_COLOR_CLUT_8BPP || regno > 255)
+               return -EINVAL;
+
+       palette = (u16 *)lcdc.palette_virt;
+
+       palette[regno] &= ~0x0fff;
+       palette[regno] |= ((red >> 12) << 8) | ((green >> 12) << 4 ) |
+                          (blue >> 12);
+
+       if (update_hw_pal) {
+               disable_controller();
+               omap_stop_lcd_dma();
+               load_palette();
+               setup_lcd_dma();
+               set_load_mode(OMAP_LCDC_LOAD_FRAME);
+               enable_controller();
+       }
+
+       return 0;
+}
+
+static void calc_ck_div(int is_tft, int pck, int *pck_div)
+{
+       unsigned long lck;
+
+       pck = max(1, pck);
+       lck = clk_get_rate(lcdc.lcd_ck);
+       *pck_div = (lck + pck - 1) / pck;
+       if (is_tft)
+               *pck_div = max(2, *pck_div);
+       else
+               *pck_div = max(3, *pck_div);
+       if (*pck_div > 255) {
+               /* FIXME: try to adjust logic clock divider as well */
+               *pck_div = 255;
+               dev_warn(lcdc.fbdev->dev, "pixclock %d kHz too low.\n",
+                        pck / 1000);
+       }
+}
+
+static void inline setup_regs(void)
+{
+       u32 l;
+       struct lcd_panel *panel = lcdc.fbdev->panel;
+       int is_tft = panel->config & OMAP_LCDC_PANEL_TFT;
+       unsigned long lck;
+       int pcd;
+
+       l = omap_readl(OMAP_LCDC_CONTROL);
+       l &= ~OMAP_LCDC_CTRL_LCD_TFT;
+       l |= is_tft ? OMAP_LCDC_CTRL_LCD_TFT : 0;
+#ifdef CONFIG_MACH_OMAP_PALMTE
+/* FIXME:if (machine_is_omap_palmte()) { */
+               /* PalmTE uses alternate TFT setting in 8BPP mode */
+               l |= (is_tft && panel->bpp == 8) ? 0x810000 : 0;
+/*     } */
+#endif
+       omap_writel(l, OMAP_LCDC_CONTROL);
+
+       l = omap_readl(OMAP_LCDC_TIMING2);
+       l &= ~(((1 << 6) - 1) << 20);
+       l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 20;
+       omap_writel(l, OMAP_LCDC_TIMING2);
+
+       l = panel->x_res - 1;
+       l |= (panel->hsw - 1) << 10;
+       l |= (panel->hfp - 1) << 16;
+       l |= (panel->hbp - 1) << 24;
+       omap_writel(l, OMAP_LCDC_TIMING0);
+
+       l = panel->y_res - 1;
+       l |= (panel->vsw - 1) << 10;
+       l |= panel->vfp << 16;
+       l |= panel->vbp << 24;
+       omap_writel(l, OMAP_LCDC_TIMING1);
+
+       l = omap_readl(OMAP_LCDC_TIMING2);
+       l &= ~0xff;
+
+       lck = clk_get_rate(lcdc.lcd_ck);
+
+       if (!panel->pcd)
+               calc_ck_div(is_tft, panel->pixel_clock * 1000, &pcd);
+       else {
+               dev_warn(lcdc.fbdev->dev,
+                   "Pixel clock divider value is obsolete.\n"
+                   "Try to set pixel_clock to %lu and pcd to 0 "
+                   "in drivers/video/omap/lcd_%s.c and submit a patch.\n",
+                       lck / panel->pcd / 1000, panel->name);
+
+               pcd = panel->pcd;
+       }
+       l |= pcd & 0xff;
+       l |= panel->acb << 8;
+       omap_writel(l, OMAP_LCDC_TIMING2);
+
+       /* update panel info with the exact clock */
+       panel->pixel_clock = lck / pcd / 1000;
+}
+
+/* Configure the LCD controller, download the color palette and start a looped
+ * DMA transfer of the frame image data. Called only in internal
+ * controller mode.
+ */
+static int omap_lcdc_set_update_mode(enum omapfb_update_mode mode)
+{
+       int r = 0;
+
+       if (mode != lcdc.update_mode) {
+               switch (mode) {
+               case OMAPFB_AUTO_UPDATE:
+                       setup_regs();
+                       load_palette();
+
+                       /* Setup and start LCD DMA */
+                       setup_lcd_dma();
+
+                       set_load_mode(OMAP_LCDC_LOAD_FRAME);
+                       enable_irqs(OMAP_LCDC_IRQ_DONE);
+                       /* This will start the actual DMA transfer */
+                       enable_controller();
+                       lcdc.update_mode = mode;
+                       break;
+               case OMAPFB_UPDATE_DISABLED:
+                       disable_controller();
+                       omap_stop_lcd_dma();
+                       lcdc.update_mode = mode;
+                       break;
+               default:
+                       r = -EINVAL;
+               }
+       }
+
+       return r;
+}
+
+static enum omapfb_update_mode omap_lcdc_get_update_mode(void)
+{
+       return lcdc.update_mode;
+}
+
+/* PM code called only in internal controller mode */
+static void omap_lcdc_suspend(void)
+{
+       if (lcdc.update_mode == OMAPFB_AUTO_UPDATE) {
+               disable_controller();
+               omap_stop_lcd_dma();
+       }
+}
+
+static void omap_lcdc_resume(void)
+{
+       if (lcdc.update_mode == OMAPFB_AUTO_UPDATE) {
+               setup_regs();
+               load_palette();
+               setup_lcd_dma();
+               set_load_mode(OMAP_LCDC_LOAD_FRAME);
+               enable_irqs(OMAP_LCDC_IRQ_DONE);
+               enable_controller();
+       }
+}
+
+static unsigned long omap_lcdc_get_caps(void)
+{
+       return 0;
+}
+
+int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data)
+{
+       BUG_ON(callback == NULL);
+
+       if (lcdc.dma_callback)
+               return -EBUSY;
+       else {
+               lcdc.dma_callback = callback;
+               lcdc.dma_callback_data = data;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(omap_lcdc_set_dma_callback);
+
+void omap_lcdc_free_dma_callback(void)
+{
+       lcdc.dma_callback = NULL;
+}
+EXPORT_SYMBOL(omap_lcdc_free_dma_callback);
+
+static void lcdc_dma_handler(u16 status, void *data)
+{
+       if (lcdc.dma_callback)
+               lcdc.dma_callback(lcdc.dma_callback_data);
+}
+
+static int mmap_kern(void)
+{
+       struct vm_struct        *kvma;
+       struct vm_area_struct   vma;
+       pgprot_t                pgprot;
+       unsigned long           vaddr;
+
+       kvma = get_vm_area(lcdc.vram_size, VM_IOREMAP);
+       if (kvma == NULL) {
+               dev_err(lcdc.fbdev->dev, "can't get kernel vm area\n");
+               return -ENOMEM;
+       }
+       vma.vm_mm = &init_mm;
+
+       vaddr = (unsigned long)kvma->addr;
+       vma.vm_start = vaddr;
+       vma.vm_end = vaddr + lcdc.vram_size;
+
+       pgprot = pgprot_writecombine(pgprot_kernel);
+       if (io_remap_pfn_range(&vma, vaddr,
+                          lcdc.vram_phys >> PAGE_SHIFT,
+                          lcdc.vram_size, pgprot) < 0) {
+               dev_err(lcdc.fbdev->dev, "kernel mmap for FB memory failed\n");
+               return -EAGAIN;
+       }
+
+       lcdc.vram_virt = (void *)vaddr;
+
+       return 0;
+}
+
+static void unmap_kern(void)
+{
+       vunmap(lcdc.vram_virt);
+}
+
+static int alloc_palette_ram(void)
+{
+       lcdc.palette_virt = dma_alloc_writecombine(lcdc.fbdev->dev,
+               MAX_PALETTE_SIZE, &lcdc.palette_phys, GFP_KERNEL);
+       if (lcdc.palette_virt == NULL) {
+               dev_err(lcdc.fbdev->dev, "failed to alloc palette memory\n");
+               return -ENOMEM;
+       }
+       memset(lcdc.palette_virt, 0, MAX_PALETTE_SIZE);
+
+       return 0;
+}
+
+static void free_palette_ram(void)
+{
+       dma_free_writecombine(lcdc.fbdev->dev, MAX_PALETTE_SIZE,
+                       lcdc.palette_virt, lcdc.palette_phys);
+}
+
+static int alloc_fbmem(struct omapfb_mem_region *region)
+{
+       int bpp;
+       int frame_size;
+       struct lcd_panel *panel = lcdc.fbdev->panel;
+
+       bpp = panel->bpp;
+       if (bpp == 12)
+               bpp = 16;
+       frame_size = PAGE_ALIGN(panel->x_res * bpp / 8 * panel->y_res);
+       if (region->size > frame_size)
+               frame_size = region->size;
+       lcdc.vram_size = frame_size;
+       lcdc.vram_virt = dma_alloc_writecombine(lcdc.fbdev->dev,
+                       lcdc.vram_size, &lcdc.vram_phys, GFP_KERNEL);
+       if (lcdc.vram_virt == NULL) {
+               dev_err(lcdc.fbdev->dev, "unable to allocate FB DMA memory\n");
+               return -ENOMEM;
+       }
+       region->size = frame_size;
+       region->paddr = lcdc.vram_phys;
+       region->vaddr = lcdc.vram_virt;
+       region->alloc = 1;
+
+       memset(lcdc.vram_virt, 0, lcdc.vram_size);
+
+       return 0;
+}
+
+static void free_fbmem(void)
+{
+       dma_free_writecombine(lcdc.fbdev->dev, lcdc.vram_size,
+                             lcdc.vram_virt, lcdc.vram_phys);
+}
+
+static int setup_fbmem(struct omapfb_mem_desc *req_md)
+{
+       int r;
+
+       if (!req_md->region_cnt) {
+               dev_err(lcdc.fbdev->dev, "no memory regions defined\n");
+               return -EINVAL;
+       }
+
+       if (req_md->region_cnt > 1) {
+               dev_err(lcdc.fbdev->dev, "only one plane is supported\n");
+               req_md->region_cnt = 1;
+       }
+
+       if (req_md->region[0].paddr == 0) {
+               lcdc.fbmem_allocated = 1;
+               if ((r = alloc_fbmem(&req_md->region[0])) < 0)
+                       return r;
+               return 0;
+       }
+
+       lcdc.vram_phys = req_md->region[0].paddr;
+       lcdc.vram_size = req_md->region[0].size;
+
+       if ((r = mmap_kern()) < 0)
+               return r;
+
+       dev_dbg(lcdc.fbdev->dev, "vram at %08x size %08lx mapped to 0x%p\n",
+                lcdc.vram_phys, lcdc.vram_size, lcdc.vram_virt);
+
+       return 0;
+}
+
+static void cleanup_fbmem(void)
+{
+       if (lcdc.fbmem_allocated)
+               free_fbmem();
+       else
+               unmap_kern();
+}
+
+static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode,
+                         struct omapfb_mem_desc *req_vram)
+{
+       int r;
+       u32 l;
+       int rate;
+       struct clk *tc_ck;
+
+       lcdc.irq_mask = 0;
+
+       lcdc.fbdev = fbdev;
+       lcdc.ext_mode = ext_mode;
+
+       l = 0;
+       omap_writel(l, OMAP_LCDC_CONTROL);
+
+       /* FIXME:
+        * According to errata some platforms have a clock rate limitiation
+        */
+       lcdc.lcd_ck = clk_get(NULL, "lcd_ck");
+       if (IS_ERR(lcdc.lcd_ck)) {
+               dev_err(fbdev->dev, "unable to access LCD clock\n");
+               r = PTR_ERR(lcdc.lcd_ck);
+               goto fail0;
+       }
+
+       tc_ck = clk_get(NULL, "tc_ck");
+       if (IS_ERR(tc_ck)) {
+               dev_err(fbdev->dev, "unable to access TC clock\n");
+               r = PTR_ERR(tc_ck);
+               goto fail1;
+       }
+
+       rate = clk_get_rate(tc_ck);
+       clk_put(tc_ck);
+
+       if (machine_is_ams_delta())
+               rate /= 4;
+       if (machine_is_omap_h3())
+               rate /= 3;
+       r = clk_set_rate(lcdc.lcd_ck, rate);
+       if (r) {
+               dev_err(fbdev->dev, "failed to adjust LCD rate\n");
+               goto fail1;
+       }
+       clk_enable(lcdc.lcd_ck);
+
+       r = request_irq(OMAP_LCDC_IRQ, lcdc_irq_handler, 0, MODULE_NAME, fbdev);
+       if (r) {
+               dev_err(fbdev->dev, "unable to get IRQ\n");
+               goto fail2;
+       }
+
+       r = omap_request_lcd_dma(lcdc_dma_handler, NULL);
+       if (r) {
+               dev_err(fbdev->dev, "unable to get LCD DMA\n");
+               goto fail3;
+       }
+
+       omap_set_lcd_dma_single_transfer(ext_mode);
+       omap_set_lcd_dma_ext_controller(ext_mode);
+
+       if (!ext_mode)
+               if ((r = alloc_palette_ram()) < 0)
+                       goto fail4;
+
+       if ((r = setup_fbmem(req_vram)) < 0)
+               goto fail5;
+
+       pr_info("omapfb: LCDC initialized\n");
+
+       return 0;
+fail5:
+       if (!ext_mode)
+               free_palette_ram();
+fail4:
+       omap_free_lcd_dma();
+fail3:
+       free_irq(OMAP_LCDC_IRQ, lcdc.fbdev);
+fail2:
+       clk_disable(lcdc.lcd_ck);
+fail1:
+       clk_put(lcdc.lcd_ck);
+fail0:
+        return r;
+}
+
+static void omap_lcdc_cleanup(void)
+{
+       if (!lcdc.ext_mode)
+               free_palette_ram();
+       cleanup_fbmem();
+       omap_free_lcd_dma();
+       free_irq(OMAP_LCDC_IRQ, lcdc.fbdev);
+       clk_disable(lcdc.lcd_ck);
+       clk_put(lcdc.lcd_ck);
+}
+
+const struct lcd_ctrl omap1_int_ctrl = {
+       .name                   = "internal",
+       .init                   = omap_lcdc_init,
+       .cleanup                = omap_lcdc_cleanup,
+       .get_caps               = omap_lcdc_get_caps,
+       .set_update_mode        = omap_lcdc_set_update_mode,
+       .get_update_mode        = omap_lcdc_get_update_mode,
+       .update_window          = NULL,
+       .suspend                = omap_lcdc_suspend,
+       .resume                 = omap_lcdc_resume,
+       .setup_plane            = omap_lcdc_setup_plane,
+       .enable_plane           = omap_lcdc_enable_plane,
+       .setcolreg              = omap_lcdc_setcolreg,
+};
diff --git a/drivers/video/omap/lcdc.h b/drivers/video/omap/lcdc.h
new file mode 100644 (file)
index 0000000..adb731e
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef LCDC_H
+#define LCDC_H
+
+int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data);
+void omap_lcdc_free_dma_callback(void);
+
+#endif
diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c
new file mode 100644 (file)
index 0000000..7915b1e
--- /dev/null
@@ -0,0 +1,1928 @@
+/*
+ * File: drivers/video/omap/omapfb_main.c
+ *
+ * Framebuffer driver for TI OMAP boards
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * Acknowledgements:
+ *   Alex McMains <aam@ridgerun.com>       - Original driver
+ *   Juha Yrjola <juha.yrjola@nokia.com>   - Original driver and improvements
+ *   Dirk Behme <dirk.behme@de.bosch.com>  - changes for 2.6 kernel API
+ *   Texas Instruments                     - H3 support
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <linux/platform_device.h>
+
+#include <asm/uaccess.h>
+#include <asm/mach-types.h>
+#include <asm/arch/dma.h>
+#include <asm/arch/omapfb.h>
+
+#define MODULE_NAME    "omapfb"
+
+static unsigned int    def_accel;
+static unsigned long   def_vram[OMAPFB_PLANE_NUM];
+static int             def_vram_cnt;
+static unsigned long   def_vxres;
+static unsigned long   def_vyres;
+static unsigned int    def_rotate;
+static unsigned int    def_mirror;
+
+#ifdef CONFIG_FB_OMAP_MANUAL_UPDATE
+static int             manual_update = 1;
+#else
+static int             manual_update;
+#endif
+
+static struct platform_device  *fbdev_pdev;
+static struct lcd_panel                *fbdev_panel;
+static struct omapfb_device    *omapfb_dev;
+
+struct caps_table_struct {
+        unsigned long flag;
+        const char *name;
+};
+
+static struct caps_table_struct ctrl_caps[] = {
+       { OMAPFB_CAPS_MANUAL_UPDATE, "manual update" },
+       { OMAPFB_CAPS_TEARSYNC,      "tearing synchronization" },
+       { OMAPFB_CAPS_PLANE_RELOCATE_MEM, "relocate plane memory" },
+       { OMAPFB_CAPS_PLANE_SCALE,   "scale plane" },
+       { OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE, "pixel double window" },
+       { OMAPFB_CAPS_WINDOW_SCALE,  "scale window" },
+       { OMAPFB_CAPS_WINDOW_OVERLAY,"overlay window" },
+       { OMAPFB_CAPS_SET_BACKLIGHT, "backlight setting" },
+};
+
+static struct caps_table_struct color_caps[] = {
+       { 1 << OMAPFB_COLOR_RGB565,     "RGB565", },
+       { 1 << OMAPFB_COLOR_YUV422,     "YUV422", },
+       { 1 << OMAPFB_COLOR_YUV420,     "YUV420", },
+       { 1 << OMAPFB_COLOR_CLUT_8BPP,  "CLUT8", },
+       { 1 << OMAPFB_COLOR_CLUT_4BPP,  "CLUT4", },
+       { 1 << OMAPFB_COLOR_CLUT_2BPP,  "CLUT2", },
+       { 1 << OMAPFB_COLOR_CLUT_1BPP,  "CLUT1", },
+       { 1 << OMAPFB_COLOR_RGB444,     "RGB444", },
+       { 1 << OMAPFB_COLOR_YUY422,     "YUY422", },
+};
+
+/*
+ * ---------------------------------------------------------------------------
+ * LCD panel
+ * ---------------------------------------------------------------------------
+ */
+extern struct lcd_ctrl omap1_int_ctrl;
+extern struct lcd_ctrl omap2_int_ctrl;
+extern struct lcd_ctrl hwa742_ctrl;
+extern struct lcd_ctrl blizzard_ctrl;
+
+static struct lcd_ctrl *ctrls[] = {
+#ifdef CONFIG_ARCH_OMAP1
+       &omap1_int_ctrl,
+#else
+       &omap2_int_ctrl,
+#endif
+
+#ifdef CONFIG_FB_OMAP_LCDC_HWA742
+       &hwa742_ctrl,
+#endif
+#ifdef CONFIG_FB_OMAP_LCDC_BLIZZARD
+       &blizzard_ctrl,
+#endif
+};
+
+#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
+#ifdef CONFIG_ARCH_OMAP1
+extern struct lcd_ctrl_extif omap1_ext_if;
+#else
+extern struct lcd_ctrl_extif omap2_ext_if;
+#endif
+#endif
+
+static void omapfb_rqueue_lock(struct omapfb_device *fbdev)
+{
+       mutex_lock(&fbdev->rqueue_mutex);
+}
+
+static void omapfb_rqueue_unlock(struct omapfb_device *fbdev)
+{
+       mutex_unlock(&fbdev->rqueue_mutex);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * LCD controller and LCD DMA
+ * ---------------------------------------------------------------------------
+ */
+/* Lookup table to map elem size to elem type. */
+static const int dma_elem_type[] = {
+       0,
+       OMAP_DMA_DATA_TYPE_S8,
+       OMAP_DMA_DATA_TYPE_S16,
+       0,
+       OMAP_DMA_DATA_TYPE_S32,
+};
+
+/* Allocate resources needed for LCD controller and LCD DMA operations. Video
+ * memory is allocated from system memory according to the virtual display
+ * size, except if a bigger memory size is specified explicitly as a kernel
+ * parameter.
+ */
+static int ctrl_init(struct omapfb_device *fbdev)
+{
+       int r;
+       int i;
+
+       /* kernel/module vram parameters override boot tags/board config */
+       if (def_vram_cnt) {
+               for (i = 0; i < def_vram_cnt; i++)
+                       fbdev->mem_desc.region[i].size =
+                               PAGE_ALIGN(def_vram[i]);
+               fbdev->mem_desc.region_cnt = i;
+       } else {
+               struct omapfb_platform_data *conf;
+
+               conf = fbdev->dev->platform_data;
+               fbdev->mem_desc = conf->mem_desc;
+       }
+
+       if (!fbdev->mem_desc.region_cnt) {
+               struct lcd_panel *panel = fbdev->panel;
+               int def_size;
+               int bpp = panel->bpp;
+
+               /* 12 bpp is packed in 16 bits */
+               if (bpp == 12)
+                       bpp = 16;
+               def_size = def_vxres * def_vyres * bpp / 8;
+               fbdev->mem_desc.region_cnt = 1;
+               fbdev->mem_desc.region[0].size = PAGE_ALIGN(def_size);
+       }
+       r = fbdev->ctrl->init(fbdev, 0, &fbdev->mem_desc);
+       if (r < 0) {
+               dev_err(fbdev->dev, "controller initialization failed (%d)\n", r);
+               return r;
+       }
+
+#ifdef DEBUG
+       for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
+               dev_dbg(fbdev->dev, "region%d phys %08x virt %p size=%lu\n",
+                        i,
+                        fbdev->mem_desc.region[i].paddr,
+                        fbdev->mem_desc.region[i].vaddr,
+                        fbdev->mem_desc.region[i].size);
+       }
+#endif
+       return 0;
+}
+
+static void ctrl_cleanup(struct omapfb_device *fbdev)
+{
+       fbdev->ctrl->cleanup();
+}
+
+/* Must be called with fbdev->rqueue_mutex held. */
+static int ctrl_change_mode(struct fb_info *fbi)
+{
+       int r;
+       unsigned long offset;
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       struct fb_var_screeninfo *var = &fbi->var;
+
+       offset = var->yoffset * fbi->fix.line_length +
+                var->xoffset * var->bits_per_pixel / 8;
+
+       if (fbdev->ctrl->sync)
+               fbdev->ctrl->sync();
+       r = fbdev->ctrl->setup_plane(plane->idx, plane->info.channel_out,
+                                offset, var->xres_virtual,
+                                plane->info.pos_x, plane->info.pos_y,
+                                var->xres, var->yres, plane->color_mode);
+       if (fbdev->ctrl->set_scale != NULL)
+               r = fbdev->ctrl->set_scale(plane->idx,
+                                  var->xres, var->yres,
+                                  plane->info.out_width,
+                                  plane->info.out_height);
+
+       return r;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * fbdev framework callbacks and the ioctl interface
+ * ---------------------------------------------------------------------------
+ */
+/* Called each time the omapfb device is opened */
+static int omapfb_open(struct fb_info *info, int user)
+{
+       return 0;
+}
+
+static void omapfb_sync(struct fb_info *info);
+
+/* Called when the omapfb device is closed. We make sure that any pending
+ * gfx DMA operations are ended, before we return. */
+static int omapfb_release(struct fb_info *info, int user)
+{
+       omapfb_sync(info);
+       return 0;
+}
+
+/* Store a single color palette entry into a pseudo palette or the hardware
+ * palette if one is available. For now we support only 16bpp and thus store
+ * the entry only to the pseudo palette.
+ */
+static int _setcolreg(struct fb_info *info, u_int regno, u_int red, u_int green,
+                       u_int blue, u_int transp, int update_hw_pal)
+{
+       struct omapfb_plane_struct *plane = info->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       struct fb_var_screeninfo *var = &info->var;
+       int r = 0;
+
+       switch (plane->color_mode) {
+       case OMAPFB_COLOR_YUV422:
+       case OMAPFB_COLOR_YUV420:
+       case OMAPFB_COLOR_YUY422:
+               r = -EINVAL;
+               break;
+       case OMAPFB_COLOR_CLUT_8BPP:
+       case OMAPFB_COLOR_CLUT_4BPP:
+       case OMAPFB_COLOR_CLUT_2BPP:
+       case OMAPFB_COLOR_CLUT_1BPP:
+               if (fbdev->ctrl->setcolreg)
+                       r = fbdev->ctrl->setcolreg(regno, red, green, blue,
+                                                       transp, update_hw_pal);
+               /* Fallthrough */
+       case OMAPFB_COLOR_RGB565:
+       case OMAPFB_COLOR_RGB444:
+               if (r != 0)
+                       break;
+
+               if (regno < 0) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               if (regno < 16) {
+                       u16 pal;
+                       pal = ((red >> (16 - var->red.length)) <<
+                                       var->red.offset) |
+                             ((green >> (16 - var->green.length)) <<
+                                       var->green.offset) |
+                             (blue >> (16 - var->blue.length));
+                       ((u32 *)(info->pseudo_palette))[regno] = pal;
+               }
+               break;
+       default:
+               BUG();
+       }
+       return r;
+}
+
+static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                           u_int transp, struct fb_info *info)
+{
+       return _setcolreg(info, regno, red, green, blue, transp, 1);
+}
+
+static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+       int count, index, r;
+       u16 *red, *green, *blue, *transp;
+       u16 trans = 0xffff;
+
+       red     = cmap->red;
+       green   = cmap->green;
+       blue    = cmap->blue;
+       transp  = cmap->transp;
+       index   = cmap->start;
+
+       for (count = 0; count < cmap->len; count++) {
+               if (transp)
+                       trans = *transp++;
+               r = _setcolreg(info, index++, *red++, *green++, *blue++, trans,
+                               count == cmap->len - 1);
+               if (r != 0)
+                       return r;
+       }
+
+       return 0;
+}
+
+static int omapfb_update_full_screen(struct fb_info *fbi);
+
+static int omapfb_blank(int blank, struct fb_info *fbi)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       int do_update = 0;
+       int r = 0;
+
+       omapfb_rqueue_lock(fbdev);
+       switch (blank) {
+       case VESA_NO_BLANKING:
+               if (fbdev->state == OMAPFB_SUSPENDED) {
+                       if (fbdev->ctrl->resume)
+                               fbdev->ctrl->resume();
+                       fbdev->panel->enable(fbdev->panel);
+                       fbdev->state = OMAPFB_ACTIVE;
+                       if (fbdev->ctrl->get_update_mode() ==
+                                       OMAPFB_MANUAL_UPDATE)
+                               do_update = 1;
+               }
+               break;
+       case VESA_POWERDOWN:
+               if (fbdev->state == OMAPFB_ACTIVE) {
+                       fbdev->panel->disable(fbdev->panel);
+                       if (fbdev->ctrl->suspend)
+                               fbdev->ctrl->suspend();
+                       fbdev->state = OMAPFB_SUSPENDED;
+               }
+               break;
+       default:
+               r = -EINVAL;
+       }
+       omapfb_rqueue_unlock(fbdev);
+
+       if (r == 0 && do_update)
+               r = omapfb_update_full_screen(fbi);
+
+       return r;
+}
+
+static void omapfb_sync(struct fb_info *fbi)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+
+       omapfb_rqueue_lock(fbdev);
+       if (fbdev->ctrl->sync)
+               fbdev->ctrl->sync();
+       omapfb_rqueue_unlock(fbdev);
+}
+
+/* Set fb_info.fix fields and also updates fbdev.
+ * When calling this fb_info.var must be set up already.
+ */
+static void set_fb_fix(struct fb_info *fbi)
+{
+       struct fb_fix_screeninfo *fix = &fbi->fix;
+       struct fb_var_screeninfo *var = &fbi->var;
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_mem_region *rg;
+       int bpp;
+
+       rg = &plane->fbdev->mem_desc.region[plane->idx];
+       fbi->screen_base        = (char __iomem *)rg->vaddr;
+       fix->smem_start         = rg->paddr;
+       fix->smem_len           = rg->size;
+
+       fix->type = FB_TYPE_PACKED_PIXELS;
+       bpp = var->bits_per_pixel;
+       if (var->nonstd)
+               fix->visual = FB_VISUAL_PSEUDOCOLOR;
+       else switch (var->bits_per_pixel) {
+       case 16:
+       case 12:
+               fix->visual = FB_VISUAL_TRUECOLOR;
+               /* 12bpp is stored in 16 bits */
+               bpp = 16;
+               break;
+       case 1:
+       case 2:
+       case 4:
+       case 8:
+               fix->visual = FB_VISUAL_PSEUDOCOLOR;
+               break;
+       }
+       fix->accel              = FB_ACCEL_OMAP1610;
+       fix->line_length        = var->xres_virtual * bpp / 8;
+}
+
+static int set_color_mode(struct omapfb_plane_struct *plane,
+                         struct fb_var_screeninfo *var)
+{
+       switch (var->nonstd) {
+       case 0:
+               break;
+       case OMAPFB_COLOR_YUV422:
+               var->bits_per_pixel = 16;
+               plane->color_mode = var->nonstd;
+               return 0;
+       case OMAPFB_COLOR_YUV420:
+               var->bits_per_pixel = 12;
+               plane->color_mode = var->nonstd;
+               return 0;
+       case OMAPFB_COLOR_YUY422:
+               var->bits_per_pixel = 16;
+               plane->color_mode = var->nonstd;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+
+       switch (var->bits_per_pixel) {
+       case 1:
+               plane->color_mode = OMAPFB_COLOR_CLUT_1BPP;
+               return 0;
+       case 2:
+               plane->color_mode = OMAPFB_COLOR_CLUT_2BPP;
+               return 0;
+       case 4:
+               plane->color_mode = OMAPFB_COLOR_CLUT_4BPP;
+               return 0;
+       case 8:
+               plane->color_mode = OMAPFB_COLOR_CLUT_8BPP;
+               return 0;
+       case 12:
+               var->bits_per_pixel = 16;
+               plane->color_mode = OMAPFB_COLOR_RGB444;
+               return 0;
+       case 16:
+               plane->color_mode = OMAPFB_COLOR_RGB565;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+/* Check the values in var against our capabilities and in case of out of
+ * bound values try to adjust them.
+ */
+static int set_fb_var(struct fb_info *fbi,
+                     struct fb_var_screeninfo *var)
+{
+       int             bpp;
+       unsigned long   max_frame_size;
+       unsigned long   line_size;
+       int             xres_min, xres_max;
+       int             yres_min, yres_max;
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       struct lcd_panel *panel = fbdev->panel;
+
+       if (set_color_mode(plane, var) < 0)
+               return -EINVAL;
+
+       bpp = var->bits_per_pixel;
+       if (plane->color_mode == OMAPFB_COLOR_RGB444)
+               bpp = 16;
+
+       switch (var->rotate) {
+       case 0:
+       case 180:
+               xres_min = OMAPFB_PLANE_XRES_MIN;
+               xres_max = panel->x_res;
+               yres_min = OMAPFB_PLANE_YRES_MIN;
+               yres_max = panel->y_res;
+               if (cpu_is_omap15xx()) {
+                       var->xres = panel->x_res;
+                       var->yres = panel->y_res;
+               }
+               break;
+       case 90:
+       case 270:
+               xres_min = OMAPFB_PLANE_YRES_MIN;
+               xres_max = panel->y_res;
+               yres_min = OMAPFB_PLANE_XRES_MIN;
+               yres_max = panel->x_res;
+               if (cpu_is_omap15xx()) {
+                       var->xres = panel->y_res;
+                       var->yres = panel->x_res;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (var->xres < xres_min)
+               var->xres = xres_min;
+       if (var->yres < yres_min)
+               var->yres = yres_min;
+       if (var->xres > xres_max)
+               var->xres = xres_max;
+       if (var->yres > yres_max)
+               var->yres = yres_max;
+
+       if (var->xres_virtual < var->xres)
+               var->xres_virtual = var->xres;
+       if (var->yres_virtual < var->yres)
+               var->yres_virtual = var->yres;
+       max_frame_size = fbdev->mem_desc.region[plane->idx].size;
+       line_size = var->xres_virtual * bpp / 8;
+       if (line_size * var->yres_virtual > max_frame_size) {
+               /* Try to keep yres_virtual first */
+               line_size = max_frame_size / var->yres_virtual;
+               var->xres_virtual = line_size * 8 / bpp;
+               if (var->xres_virtual < var->xres) {
+                       /* Still doesn't fit. Shrink yres_virtual too */
+                       var->xres_virtual = var->xres;
+                       line_size = var->xres * bpp / 8;
+                       var->yres_virtual = max_frame_size / line_size;
+               }
+               /* Recheck this, as the virtual size changed. */
+               if (var->xres_virtual < var->xres)
+                       var->xres = var->xres_virtual;
+               if (var->yres_virtual < var->yres)
+                       var->yres = var->yres_virtual;
+               if (var->xres < xres_min || var->yres < yres_min)
+                       return -EINVAL;
+       }
+       if (var->xres + var->xoffset > var->xres_virtual)
+               var->xoffset = var->xres_virtual - var->xres;
+       if (var->yres + var->yoffset > var->yres_virtual)
+               var->yoffset = var->yres_virtual - var->yres;
+       line_size = var->xres * bpp / 8;
+
+       if (plane->color_mode == OMAPFB_COLOR_RGB444) {
+               var->red.offset   = 8; var->red.length   = 4;
+                                               var->red.msb_right   = 0;
+               var->green.offset = 4; var->green.length = 4;
+                                               var->green.msb_right = 0;
+               var->blue.offset  = 0; var->blue.length  = 4;
+                                               var->blue.msb_right  = 0;
+       } else {
+               var->red.offset  = 11; var->red.length   = 5;
+                                               var->red.msb_right   = 0;
+               var->green.offset= 5;  var->green.length = 6;
+                                               var->green.msb_right = 0;
+               var->blue.offset = 0;  var->blue.length  = 5;
+                                               var->blue.msb_right  = 0;
+       }
+
+       var->height             = -1;
+       var->width              = -1;
+       var->grayscale          = 0;
+
+       /* pixclock in ps, the rest in pixclock */
+       var->pixclock           = 10000000 / (panel->pixel_clock / 100);
+       var->left_margin        = panel->hfp;
+       var->right_margin       = panel->hbp;
+       var->upper_margin       = panel->vfp;
+       var->lower_margin       = panel->vbp;
+       var->hsync_len          = panel->hsw;
+       var->vsync_len          = panel->vsw;
+
+       /* TODO: get these from panel->config */
+       var->vmode              = FB_VMODE_NONINTERLACED;
+       var->sync               = 0;
+
+       return 0;
+}
+
+
+/* Set rotation (0, 90, 180, 270 degree), and switch to the new mode. */
+static void omapfb_rotate(struct fb_info *fbi, int rotate)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+
+       omapfb_rqueue_lock(fbdev);
+       if (cpu_is_omap15xx() && rotate != fbi->var.rotate) {
+               struct fb_var_screeninfo *new_var = &fbdev->new_var;
+
+               memcpy(new_var, &fbi->var, sizeof(*new_var));
+               new_var->rotate = rotate;
+               if (set_fb_var(fbi, new_var) == 0 &&
+                   memcmp(new_var, &fbi->var, sizeof(*new_var))) {
+                       memcpy(&fbi->var, new_var, sizeof(*new_var));
+                       ctrl_change_mode(fbi);
+               }
+       }
+       omapfb_rqueue_unlock(fbdev);
+}
+
+/* Set new x,y offsets in the virtual display for the visible area and switch
+ * to the new mode.
+ */
+static int omapfb_pan_display(struct fb_var_screeninfo *var,
+                              struct fb_info *fbi)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       int r = 0;
+
+       omapfb_rqueue_lock(fbdev);
+       if (var->xoffset != fbi->var.xoffset ||
+           var->yoffset != fbi->var.yoffset) {
+               struct fb_var_screeninfo *new_var = &fbdev->new_var;
+
+               memcpy(new_var, &fbi->var, sizeof(*new_var));
+               new_var->xoffset = var->xoffset;
+               new_var->yoffset = var->yoffset;
+               if (set_fb_var(fbi, new_var))
+                       r = -EINVAL;
+               else {
+                       memcpy(&fbi->var, new_var, sizeof(*new_var));
+                       ctrl_change_mode(fbi);
+               }
+       }
+       omapfb_rqueue_unlock(fbdev);
+
+       return r;
+}
+
+/* Set mirror to vertical axis and switch to the new mode. */
+static int omapfb_mirror(struct fb_info *fbi, int mirror)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       int r = 0;
+
+       omapfb_rqueue_lock(fbdev);
+       mirror = mirror ? 1 : 0;
+       if (cpu_is_omap15xx())
+               r = -EINVAL;
+       else if (mirror != plane->info.mirror) {
+               plane->info.mirror = mirror;
+               r = ctrl_change_mode(fbi);
+       }
+       omapfb_rqueue_unlock(fbdev);
+
+       return r;
+}
+
+/* Check values in var, try to adjust them in case of out of bound values if
+ * possible, or return error.
+ */
+static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       int r;
+
+       omapfb_rqueue_lock(fbdev);
+       if (fbdev->ctrl->sync != NULL)
+               fbdev->ctrl->sync();
+       r = set_fb_var(fbi, var);
+       omapfb_rqueue_unlock(fbdev);
+
+       return r;
+}
+
+/* Switch to a new mode. The parameters for it has been check already by
+ * omapfb_check_var.
+ */
+static int omapfb_set_par(struct fb_info *fbi)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       int r = 0;
+
+       omapfb_rqueue_lock(fbdev);
+       set_fb_fix(fbi);
+       r = ctrl_change_mode(fbi);
+       omapfb_rqueue_unlock(fbdev);
+
+       return r;
+}
+
+int omapfb_update_window_async(struct fb_info *fbi,
+                               struct omapfb_update_window *win,
+                               void (*callback)(void *),
+                               void *callback_data)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       struct fb_var_screeninfo *var;
+
+       var = &fbi->var;
+       if (win->x >= var->xres || win->y >= var->yres ||
+           win->out_x > var->xres || win->out_y >= var->yres)
+               return -EINVAL;
+
+       if (!fbdev->ctrl->update_window ||
+           fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)
+               return -ENODEV;
+
+       if (win->x + win->width >= var->xres)
+               win->width = var->xres - win->x;
+       if (win->y + win->height >= var->yres)
+               win->height = var->yres - win->y;
+       /* The out sizes should be cropped to the LCD size */
+       if (win->out_x + win->out_width > fbdev->panel->x_res)
+               win->out_width = fbdev->panel->x_res - win->out_x;
+       if (win->out_y + win->out_height > fbdev->panel->y_res)
+               win->out_height = fbdev->panel->y_res - win->out_y;
+       if (!win->width || !win->height || !win->out_width || !win->out_height)
+               return 0;
+
+       return fbdev->ctrl->update_window(fbi, win, callback, callback_data);
+}
+EXPORT_SYMBOL(omapfb_update_window_async);
+
+static int omapfb_update_win(struct fb_info *fbi,
+                               struct omapfb_update_window *win)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       int ret;
+
+       omapfb_rqueue_lock(plane->fbdev);
+       ret = omapfb_update_window_async(fbi, win, NULL, 0);
+       omapfb_rqueue_unlock(plane->fbdev);
+
+       return ret;
+}
+
+static int omapfb_update_full_screen(struct fb_info *fbi)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       struct omapfb_update_window win;
+       int r;
+
+       if (!fbdev->ctrl->update_window ||
+           fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)
+               return -ENODEV;
+
+       win.x = 0;
+       win.y = 0;
+       win.width = fbi->var.xres;
+       win.height = fbi->var.yres;
+       win.out_x = 0;
+       win.out_y = 0;
+       win.out_width = fbi->var.xres;
+       win.out_height = fbi->var.yres;
+       win.format = 0;
+
+       omapfb_rqueue_lock(fbdev);
+       r = fbdev->ctrl->update_window(fbi, &win, NULL, 0);
+       omapfb_rqueue_unlock(fbdev);
+
+       return r;
+}
+
+static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       struct lcd_panel *panel = fbdev->panel;
+       struct omapfb_plane_info old_info;
+       int r = 0;
+
+       if (pi->pos_x + pi->out_width > panel->x_res ||
+           pi->pos_y + pi->out_height > panel->y_res)
+               return -EINVAL;
+
+       omapfb_rqueue_lock(fbdev);
+       if (pi->enabled && !fbdev->mem_desc.region[plane->idx].size) {
+               /* This plane's memory was freed, can't enable it
+                * until it's reallocated.
+                */
+               r = -EINVAL;
+               goto out;
+       }
+       old_info = plane->info;
+       plane->info = *pi;
+       if (pi->enabled) {
+               r = ctrl_change_mode(fbi);
+               if (r < 0) {
+                       plane->info = old_info;
+                       goto out;
+               }
+       }
+       r = fbdev->ctrl->enable_plane(plane->idx, pi->enabled);
+       if (r < 0) {
+               plane->info = old_info;
+               goto out;
+       }
+out:
+       omapfb_rqueue_unlock(fbdev);
+       return r;
+}
+
+static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+
+       *pi = plane->info;
+       return 0;
+}
+
+static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       struct omapfb_mem_region *rg = &fbdev->mem_desc.region[plane->idx];
+       size_t size;
+       int r = 0;
+
+       if (fbdev->ctrl->setup_mem == NULL)
+               return -ENODEV;
+       if (mi->type > OMAPFB_MEMTYPE_MAX)
+               return -EINVAL;
+
+       size = PAGE_ALIGN(mi->size);
+       omapfb_rqueue_lock(fbdev);
+       if (plane->info.enabled) {
+               r = -EBUSY;
+               goto out;
+       }
+       if (rg->size != size || rg->type != mi->type) {
+               struct fb_var_screeninfo *new_var = &fbdev->new_var;
+               unsigned long old_size = rg->size;
+               u8            old_type = rg->type;
+               unsigned long paddr;
+
+               rg->size = size;
+               rg->type = mi->type;
+               /* size == 0 is a special case, for which we
+                * don't check / adjust the screen parameters.
+                * This isn't a problem since the plane can't
+                * be reenabled unless its size is > 0.
+                */
+               if (old_size != size && size) {
+                       if (size) {
+                               memcpy(new_var, &fbi->var, sizeof(*new_var));
+                               r = set_fb_var(fbi, new_var);
+                               if (r < 0)
+                                       goto out;
+                       }
+               }
+
+               if (fbdev->ctrl->sync)
+                       fbdev->ctrl->sync();
+               r = fbdev->ctrl->setup_mem(plane->idx, size, mi->type, &paddr);
+               if (r < 0) {
+                       /* Revert changes. */
+                       rg->size = old_size;
+                       rg->type = old_type;
+                       goto out;
+               }
+               rg->paddr = paddr;
+
+               if (old_size != size) {
+                       if (size) {
+                               memcpy(&fbi->var, new_var, sizeof(fbi->var));
+                               set_fb_fix(fbi);
+                       } else {
+                               /* Set these explicitly to indicate that the
+                                * plane memory is dealloce'd, the other
+                                * screen parameters in var / fix are invalid.
+                                */
+                               fbi->fix.smem_start = 0;
+                               fbi->fix.smem_len = 0;
+                       }
+               }
+       }
+out:
+       omapfb_rqueue_unlock(fbdev);
+
+       return r;
+}
+
+static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       struct omapfb_mem_region *rg;
+
+       rg = &fbdev->mem_desc.region[plane->idx];
+       memset(mi, 0, sizeof(*mi));
+       mi->size = rg->size;
+       mi->type = rg->type;
+
+       return 0;
+}
+
+static int omapfb_set_color_key(struct omapfb_device *fbdev,
+                               struct omapfb_color_key *ck)
+{
+       int r;
+
+       if (!fbdev->ctrl->set_color_key)
+               return -ENODEV;
+
+       omapfb_rqueue_lock(fbdev);
+       r = fbdev->ctrl->set_color_key(ck);
+       omapfb_rqueue_unlock(fbdev);
+
+       return r;
+}
+
+static int omapfb_get_color_key(struct omapfb_device *fbdev,
+                               struct omapfb_color_key *ck)
+{
+       int r;
+
+       if (!fbdev->ctrl->get_color_key)
+               return -ENODEV;
+
+       omapfb_rqueue_lock(fbdev);
+       r = fbdev->ctrl->get_color_key(ck);
+       omapfb_rqueue_unlock(fbdev);
+
+       return r;
+}
+
+static struct blocking_notifier_head omapfb_client_list[OMAPFB_PLANE_NUM];
+static int notifier_inited;
+
+static void omapfb_init_notifier(void)
+{
+       int i;
+
+       for (i = 0; i < OMAPFB_PLANE_NUM; i++)
+               BLOCKING_INIT_NOTIFIER_HEAD(&omapfb_client_list[i]);
+}
+
+int omapfb_register_client(struct omapfb_notifier_block *omapfb_nb,
+                           omapfb_notifier_callback_t callback,
+                           void *callback_data)
+{
+       int r;
+
+       if ((unsigned)omapfb_nb->plane_idx > OMAPFB_PLANE_NUM)
+               return -EINVAL;
+
+       if (!notifier_inited) {
+               omapfb_init_notifier();
+               notifier_inited = 1;
+       }
+
+       omapfb_nb->nb.notifier_call = (int (*)(struct notifier_block *,
+                                       unsigned long, void *))callback;
+       omapfb_nb->data = callback_data;
+       r = blocking_notifier_chain_register(
+                               &omapfb_client_list[omapfb_nb->plane_idx],
+                               &omapfb_nb->nb);
+       if (r)
+               return r;
+       if (omapfb_dev != NULL &&
+           omapfb_dev->ctrl && omapfb_dev->ctrl->bind_client) {
+               omapfb_dev->ctrl->bind_client(omapfb_nb);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(omapfb_register_client);
+
+int omapfb_unregister_client(struct omapfb_notifier_block *omapfb_nb)
+{
+       return blocking_notifier_chain_unregister(
+               &omapfb_client_list[omapfb_nb->plane_idx], &omapfb_nb->nb);
+}
+EXPORT_SYMBOL(omapfb_unregister_client);
+
+void omapfb_notify_clients(struct omapfb_device *fbdev, unsigned long event)
+{
+       int i;
+
+       if (!notifier_inited)
+               /* no client registered yet */
+               return;
+
+       for (i = 0; i < OMAPFB_PLANE_NUM; i++)
+               blocking_notifier_call_chain(&omapfb_client_list[i], event,
+                                   fbdev->fb_info[i]);
+}
+EXPORT_SYMBOL(omapfb_notify_clients);
+
+static int omapfb_set_update_mode(struct omapfb_device *fbdev,
+                                  enum omapfb_update_mode mode)
+{
+       int r;
+
+       omapfb_rqueue_lock(fbdev);
+       r = fbdev->ctrl->set_update_mode(mode);
+       omapfb_rqueue_unlock(fbdev);
+
+       return r;
+}
+
+static enum omapfb_update_mode omapfb_get_update_mode(struct omapfb_device *fbdev)
+{
+       int r;
+
+       omapfb_rqueue_lock(fbdev);
+       r = fbdev->ctrl->get_update_mode();
+       omapfb_rqueue_unlock(fbdev);
+
+       return r;
+}
+
+static void omapfb_get_caps(struct omapfb_device *fbdev, int plane,
+                                    struct omapfb_caps *caps)
+{
+       memset(caps, 0, sizeof(*caps));
+       fbdev->ctrl->get_caps(plane, caps);
+       caps->ctrl |= fbdev->panel->get_caps(fbdev->panel);
+}
+
+/* For lcd testing */
+void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval)
+{
+       omapfb_rqueue_lock(fbdev);
+       *(u16 *)fbdev->mem_desc.region[0].vaddr = pixval;
+       if (fbdev->ctrl->get_update_mode() == OMAPFB_MANUAL_UPDATE) {
+               struct omapfb_update_window win;
+
+               memset(&win, 0, sizeof(win));
+               win.width = 2;
+               win.height = 2;
+               win.out_width = 2;
+               win.out_height = 2;
+               fbdev->ctrl->update_window(fbdev->fb_info[0], &win, NULL, 0);
+       }
+       omapfb_rqueue_unlock(fbdev);
+}
+EXPORT_SYMBOL(omapfb_write_first_pixel);
+
+/* Ioctl interface. Part of the kernel mode frame buffer API is duplicated
+ * here to be accessible by user mode code.
+ */
+static int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd,
+                       unsigned long arg)
+{
+       struct omapfb_plane_struct *plane = fbi->par;
+       struct omapfb_device    *fbdev = plane->fbdev;
+       struct fb_ops           *ops = fbi->fbops;
+       union {
+               struct omapfb_update_window     update_window;
+               struct omapfb_plane_info        plane_info;
+               struct omapfb_mem_info          mem_info;
+               struct omapfb_color_key         color_key;
+               enum omapfb_update_mode         update_mode;
+               struct omapfb_caps              caps;
+               unsigned int            mirror;
+               int                     plane_out;
+               int                     enable_plane;
+       } p;
+       int r = 0;
+
+       BUG_ON(!ops);
+       switch (cmd)
+       {
+       case OMAPFB_MIRROR:
+               if (get_user(p.mirror, (int __user *)arg))
+                       r = -EFAULT;
+               else
+                       omapfb_mirror(fbi, p.mirror);
+               break;
+       case OMAPFB_SYNC_GFX:
+               omapfb_sync(fbi);
+               break;
+       case OMAPFB_VSYNC:
+               break;
+       case OMAPFB_SET_UPDATE_MODE:
+               if (get_user(p.update_mode, (int __user *)arg))
+                       r = -EFAULT;
+               else
+                       r = omapfb_set_update_mode(fbdev, p.update_mode);
+               break;
+       case OMAPFB_GET_UPDATE_MODE:
+               p.update_mode = omapfb_get_update_mode(fbdev);
+               if (put_user(p.update_mode,
+                                       (enum omapfb_update_mode __user *)arg))
+                       r = -EFAULT;
+               break;
+       case OMAPFB_UPDATE_WINDOW_OLD:
+               if (copy_from_user(&p.update_window, (void __user *)arg,
+                                  sizeof(struct omapfb_update_window_old)))
+                       r = -EFAULT;
+               else {
+                       struct omapfb_update_window *u = &p.update_window;
+                       u->out_x = u->x;
+                       u->out_y = u->y;
+                       u->out_width = u->width;
+                       u->out_height = u->height;
+                       memset(u->reserved, 0, sizeof(u->reserved));
+                       r = omapfb_update_win(fbi, u);
+               }
+               break;
+       case OMAPFB_UPDATE_WINDOW:
+               if (copy_from_user(&p.update_window, (void __user *)arg,
+                                  sizeof(p.update_window)))
+                       r = -EFAULT;
+               else
+                       r = omapfb_update_win(fbi, &p.update_window);
+               break;
+       case OMAPFB_SETUP_PLANE:
+               if (copy_from_user(&p.plane_info, (void __user *)arg,
+                                  sizeof(p.plane_info)))
+                       r = -EFAULT;
+               else
+                       r = omapfb_setup_plane(fbi, &p.plane_info);
+               break;
+       case OMAPFB_QUERY_PLANE:
+               if ((r = omapfb_query_plane(fbi, &p.plane_info)) < 0)
+                       break;
+               if (copy_to_user((void __user *)arg, &p.plane_info,
+                                  sizeof(p.plane_info)))
+                       r = -EFAULT;
+               break;
+       case OMAPFB_SETUP_MEM:
+               if (copy_from_user(&p.mem_info, (void __user *)arg,
+                                  sizeof(p.mem_info)))
+                       r = -EFAULT;
+               else
+                       r = omapfb_setup_mem(fbi, &p.mem_info);
+               break;
+       case OMAPFB_QUERY_MEM:
+               if ((r = omapfb_query_mem(fbi, &p.mem_info)) < 0)
+                       break;
+               if (copy_to_user((void __user *)arg, &p.mem_info,
+                                  sizeof(p.mem_info)))
+                       r = -EFAULT;
+               break;
+       case OMAPFB_SET_COLOR_KEY:
+               if (copy_from_user(&p.color_key, (void __user *)arg,
+                                  sizeof(p.color_key)))
+                       r = -EFAULT;
+               else
+                       r = omapfb_set_color_key(fbdev, &p.color_key);
+               break;
+       case OMAPFB_GET_COLOR_KEY:
+               if ((r = omapfb_get_color_key(fbdev, &p.color_key)) < 0)
+                       break;
+               if (copy_to_user((void __user *)arg, &p.color_key,
+                                sizeof(p.color_key)))
+                       r = -EFAULT;
+               break;
+       case OMAPFB_GET_CAPS:
+               omapfb_get_caps(fbdev, plane->idx, &p.caps);
+               if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps)))
+                       r = -EFAULT;
+               break;
+       case OMAPFB_LCD_TEST:
+               {
+                       int test_num;
+
+                       if (get_user(test_num, (int __user *)arg)) {
+                               r = -EFAULT;
+                               break;
+                       }
+                       if (!fbdev->panel->run_test) {
+                               r = -EINVAL;
+                               break;
+                       }
+                       r = fbdev->panel->run_test(fbdev->panel, test_num);
+                       break;
+               }
+       case OMAPFB_CTRL_TEST:
+               {
+                       int test_num;
+
+                       if (get_user(test_num, (int __user *)arg)) {
+                               r = -EFAULT;
+                               break;
+                       }
+                       if (!fbdev->ctrl->run_test) {
+                               r = -EINVAL;
+                               break;
+                       }
+                       r = fbdev->ctrl->run_test(test_num);
+                       break;
+               }
+       default:
+               r = -EINVAL;
+       }
+
+       return r;
+}
+
+static int omapfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+       struct omapfb_plane_struct *plane = info->par;
+       struct omapfb_device *fbdev = plane->fbdev;
+       int r;
+
+       omapfb_rqueue_lock(fbdev);
+       r = fbdev->ctrl->mmap(info, vma);
+       omapfb_rqueue_unlock(fbdev);
+
+       return r;
+}
+
+/* Callback table for the frame buffer framework. Some of these pointers
+ * will be changed according to the current setting of fb_info->accel_flags.
+ */
+static struct fb_ops omapfb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_open        = omapfb_open,
+       .fb_release     = omapfb_release,
+       .fb_setcolreg   = omapfb_setcolreg,
+       .fb_setcmap     = omapfb_setcmap,
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+       .fb_blank       = omapfb_blank,
+       .fb_ioctl       = omapfb_ioctl,
+       .fb_check_var   = omapfb_check_var,
+       .fb_set_par     = omapfb_set_par,
+       .fb_rotate      = omapfb_rotate,
+       .fb_pan_display = omapfb_pan_display,
+};
+
+/*
+ * ---------------------------------------------------------------------------
+ * Sysfs interface
+ * ---------------------------------------------------------------------------
+ */
+/* omapfbX sysfs entries */
+static ssize_t omapfb_show_caps_num(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+       int plane;
+       size_t size;
+       struct omapfb_caps caps;
+
+       plane = 0;
+       size = 0;
+       while (size < PAGE_SIZE && plane < OMAPFB_PLANE_NUM) {
+               omapfb_get_caps(fbdev, plane, &caps);
+               size += snprintf(&buf[size], PAGE_SIZE - size,
+                       "plane#%d %#010x %#010x %#010x\n",
+                       plane, caps.ctrl, caps.plane_color, caps.wnd_color);
+               plane++;
+       }
+       return size;
+}
+
+static ssize_t omapfb_show_caps_text(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+       int i;
+       struct omapfb_caps caps;
+       int plane;
+       size_t size;
+
+       plane = 0;
+       size = 0;
+       while (size < PAGE_SIZE && plane < OMAPFB_PLANE_NUM) {
+               omapfb_get_caps(fbdev, plane, &caps);
+               size += snprintf(&buf[size], PAGE_SIZE - size,
+                                "plane#%d:\n", plane);
+               for (i = 0; i < ARRAY_SIZE(ctrl_caps) &&
+                    size < PAGE_SIZE; i++) {
+                       if (ctrl_caps[i].flag & caps.ctrl)
+                               size += snprintf(&buf[size], PAGE_SIZE - size,
+                                       " %s\n", ctrl_caps[i].name);
+               }
+               size += snprintf(&buf[size], PAGE_SIZE - size,
+                                " plane colors:\n");
+               for (i = 0; i < ARRAY_SIZE(color_caps) &&
+                    size < PAGE_SIZE; i++) {
+                       if (color_caps[i].flag & caps.plane_color)
+                               size += snprintf(&buf[size], PAGE_SIZE - size,
+                                       "  %s\n", color_caps[i].name);
+               }
+               size += snprintf(&buf[size], PAGE_SIZE - size,
+                                " window colors:\n");
+               for (i = 0; i < ARRAY_SIZE(color_caps) &&
+                    size < PAGE_SIZE; i++) {
+                       if (color_caps[i].flag & caps.wnd_color)
+                               size += snprintf(&buf[size], PAGE_SIZE - size,
+                                       "  %s\n", color_caps[i].name);
+               }
+
+               plane++;
+       }
+       return size;
+}
+
+static DEVICE_ATTR(caps_num, 0444, omapfb_show_caps_num, NULL);
+static DEVICE_ATTR(caps_text, 0444, omapfb_show_caps_text, NULL);
+
+/* panel sysfs entries */
+static ssize_t omapfb_show_panel_name(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+
+       return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->panel->name);
+}
+
+static ssize_t omapfb_show_bklight_level(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+       int r;
+
+       if (fbdev->panel->get_bklight_level) {
+               r = snprintf(buf, PAGE_SIZE, "%d\n",
+                            fbdev->panel->get_bklight_level(fbdev->panel));
+       } else
+               r = -ENODEV;
+       return r;
+}
+
+static ssize_t omapfb_store_bklight_level(struct device *dev,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t size)
+{
+       struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+       int r;
+
+       if (fbdev->panel->set_bklight_level) {
+               unsigned int level;
+
+               if (sscanf(buf, "%10d", &level) == 1) {
+                       r = fbdev->panel->set_bklight_level(fbdev->panel, level);
+               } else
+                       r = -EINVAL;
+       } else
+               r = -ENODEV;
+       return r ? r : size;
+}
+
+static ssize_t omapfb_show_bklight_max(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
+{
+       struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+       int r;
+
+       if (fbdev->panel->get_bklight_level) {
+               r = snprintf(buf, PAGE_SIZE, "%d\n",
+                            fbdev->panel->get_bklight_max(fbdev->panel));
+       } else
+               r = -ENODEV;
+       return r;
+}
+
+static struct device_attribute dev_attr_panel_name =
+       __ATTR(name, 0444, omapfb_show_panel_name, NULL);
+static DEVICE_ATTR(backlight_level, 0664,
+                  omapfb_show_bklight_level, omapfb_store_bklight_level);
+static DEVICE_ATTR(backlight_max, 0444, omapfb_show_bklight_max, NULL);
+
+static struct attribute *panel_attrs[] = {
+       &dev_attr_panel_name.attr,
+       &dev_attr_backlight_level.attr,
+       &dev_attr_backlight_max.attr,
+       NULL,
+};
+
+static struct attribute_group panel_attr_grp = {
+       .name  = "panel",
+       .attrs = panel_attrs,
+};
+
+/* ctrl sysfs entries */
+static ssize_t omapfb_show_ctrl_name(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data;
+
+       return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->ctrl->name);
+}
+
+static struct device_attribute dev_attr_ctrl_name =
+       __ATTR(name, 0444, omapfb_show_ctrl_name, NULL);
+
+static struct attribute *ctrl_attrs[] = {
+       &dev_attr_ctrl_name.attr,
+       NULL,
+};
+
+static struct attribute_group ctrl_attr_grp = {
+       .name  = "ctrl",
+       .attrs = ctrl_attrs,
+};
+
+static int omapfb_register_sysfs(struct omapfb_device *fbdev)
+{
+       int r;
+
+       if ((r = device_create_file(fbdev->dev, &dev_attr_caps_num)))
+               goto fail0;
+
+       if ((r = device_create_file(fbdev->dev, &dev_attr_caps_text)))
+               goto fail1;
+
+       if ((r = sysfs_create_group(&fbdev->dev->kobj, &panel_attr_grp)))
+               goto fail2;
+
+       if ((r = sysfs_create_group(&fbdev->dev->kobj, &ctrl_attr_grp)))
+               goto fail3;
+
+       return 0;
+fail3:
+       sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);
+fail2:
+       device_remove_file(fbdev->dev, &dev_attr_caps_text);
+fail1:
+       device_remove_file(fbdev->dev, &dev_attr_caps_num);
+fail0:
+       dev_err(fbdev->dev, "unable to register sysfs interface\n");
+       return r;
+}
+
+static void omapfb_unregister_sysfs(struct omapfb_device *fbdev)
+{
+       sysfs_remove_group(&fbdev->dev->kobj, &ctrl_attr_grp);
+       sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);
+       device_remove_file(fbdev->dev, &dev_attr_caps_num);
+       device_remove_file(fbdev->dev, &dev_attr_caps_text);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * LDM callbacks
+ * ---------------------------------------------------------------------------
+ */
+/* Initialize system fb_info object and set the default video mode.
+ * The frame buffer memory already allocated by lcddma_init
+ */
+static int fbinfo_init(struct omapfb_device *fbdev, struct fb_info *info)
+{
+       struct fb_var_screeninfo        *var = &info->var;
+       struct fb_fix_screeninfo        *fix = &info->fix;
+       int                             r = 0;
+
+       info->fbops = &omapfb_ops;
+       info->flags = FBINFO_FLAG_DEFAULT;
+
+       strncpy(fix->id, MODULE_NAME, sizeof(fix->id));
+
+       info->pseudo_palette = fbdev->pseudo_palette;
+
+       var->accel_flags  = def_accel ? FB_ACCELF_TEXT : 0;
+       var->xres = def_vxres;
+       var->yres = def_vyres;
+       var->xres_virtual = def_vxres;
+       var->yres_virtual = def_vyres;
+       var->rotate       = def_rotate;
+       var->bits_per_pixel = fbdev->panel->bpp;
+
+       set_fb_var(info, var);
+       set_fb_fix(info);
+
+       r = fb_alloc_cmap(&info->cmap, 16, 0);
+       if (r != 0)
+               dev_err(fbdev->dev, "unable to allocate color map memory\n");
+
+       return r;
+}
+
+/* Release the fb_info object */
+static void fbinfo_cleanup(struct omapfb_device *fbdev, struct fb_info *fbi)
+{
+       fb_dealloc_cmap(&fbi->cmap);
+}
+
+static void planes_cleanup(struct omapfb_device *fbdev)
+{
+       int i;
+
+       for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
+               if (fbdev->fb_info[i] == NULL)
+                       break;
+               fbinfo_cleanup(fbdev, fbdev->fb_info[i]);
+               framebuffer_release(fbdev->fb_info[i]);
+       }
+}
+
+static int planes_init(struct omapfb_device *fbdev)
+{
+       struct fb_info *fbi;
+       int i;
+       int r;
+
+       for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
+               struct omapfb_plane_struct *plane;
+               fbi = framebuffer_alloc(sizeof(struct omapfb_plane_struct),
+                                       fbdev->dev);
+               if (fbi == NULL) {
+                       dev_err(fbdev->dev,
+                               "unable to allocate memory for plane info\n");
+                       planes_cleanup(fbdev);
+                       return -ENOMEM;
+               }
+               plane = fbi->par;
+               plane->idx = i;
+               plane->fbdev = fbdev;
+               plane->info.mirror = def_mirror;
+               fbdev->fb_info[i] = fbi;
+
+               if ((r = fbinfo_init(fbdev, fbi)) < 0) {
+                       framebuffer_release(fbi);
+                       planes_cleanup(fbdev);
+                       return r;
+               }
+               plane->info.out_width = fbi->var.xres;
+               plane->info.out_height = fbi->var.yres;
+       }
+       return 0;
+}
+
+/* Free driver resources. Can be called to rollback an aborted initialization
+ * sequence.
+ */
+static void omapfb_free_resources(struct omapfb_device *fbdev, int state)
+{
+       int i;
+
+       switch (state) {
+       case OMAPFB_ACTIVE:
+               for (i = 0; i < fbdev->mem_desc.region_cnt; i++)
+                       unregister_framebuffer(fbdev->fb_info[i]);
+       case 7:
+               omapfb_unregister_sysfs(fbdev);
+       case 6:
+               fbdev->panel->disable(fbdev->panel);
+       case 5:
+               omapfb_set_update_mode(fbdev, OMAPFB_UPDATE_DISABLED);
+       case 4:
+               planes_cleanup(fbdev);
+       case 3:
+               ctrl_cleanup(fbdev);
+       case 2:
+               fbdev->panel->cleanup(fbdev->panel);
+       case 1:
+               dev_set_drvdata(fbdev->dev, NULL);
+               kfree(fbdev);
+       case 0:
+               /* nothing to free */
+               break;
+       default:
+               BUG();
+       }
+}
+
+static int omapfb_find_ctrl(struct omapfb_device *fbdev)
+{
+       struct omapfb_platform_data *conf;
+       char name[17];
+       int i;
+
+       conf = fbdev->dev->platform_data;
+
+       fbdev->ctrl = NULL;
+
+       strncpy(name, conf->lcd.ctrl_name, sizeof(name) - 1);
+       name[sizeof(name) - 1] = '\0';
+
+       if (strcmp(name, "internal") == 0) {
+               fbdev->ctrl = fbdev->int_ctrl;
+               return 0;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(ctrls); i++) {
+               dev_dbg(fbdev->dev, "ctrl %s\n", ctrls[i]->name);
+               if (strcmp(ctrls[i]->name, name) == 0) {
+                       fbdev->ctrl = ctrls[i];
+                       break;
+               }
+       }
+
+       if (fbdev->ctrl == NULL) {
+               dev_dbg(fbdev->dev, "ctrl %s not supported\n", name);
+               return -1;
+       }
+
+       return 0;
+}
+
+static void check_required_callbacks(struct omapfb_device *fbdev)
+{
+#define _C(x) (fbdev->ctrl->x != NULL)
+#define _P(x) (fbdev->panel->x != NULL)
+       BUG_ON(fbdev->ctrl == NULL || fbdev->panel == NULL);
+       BUG_ON(!(_C(init) && _C(cleanup) && _C(get_caps) &&
+                _C(set_update_mode) && _C(setup_plane) && _C(enable_plane) &&
+                _P(init) && _P(cleanup) && _P(enable) && _P(disable) &&
+                _P(get_caps)));
+#undef _P
+#undef _C
+}
+
+/* Called by LDM binding to probe and attach a new device.
+ * Initialization sequence:
+ *   1. allocate system omapfb_device structure
+ *   2. select controller type according to platform configuration
+ *      init LCD panel
+ *   3. init LCD controller and LCD DMA
+ *   4. init system fb_info structure for all planes
+ *   5. setup video mode for first plane and enable it
+ *   6. enable LCD panel
+ *   7. register sysfs attributes
+ *   OMAPFB_ACTIVE: register system fb_info structure for all planes
+ */
+static int omapfb_do_probe(struct platform_device *pdev, struct lcd_panel *panel)
+{
+       struct omapfb_device    *fbdev = NULL;
+       int                     init_state;
+       unsigned long           phz, hhz, vhz;
+       unsigned long           vram;
+       int                     i;
+       int                     r = 0;
+
+       init_state = 0;
+
+       if (pdev->num_resources != 0) {
+               dev_err(&pdev->dev, "probed for an unknown device\n");
+               r = -ENODEV;
+               goto cleanup;
+       }
+
+       if (pdev->dev.platform_data == NULL) {
+               dev_err(&pdev->dev, "missing platform data\n");
+               r = -ENOENT;
+               goto cleanup;
+       }
+
+       fbdev = kzalloc(sizeof(struct omapfb_device), GFP_KERNEL);
+       if (fbdev == NULL) {
+               dev_err(&pdev->dev,
+                       "unable to allocate memory for device info\n");
+               r = -ENOMEM;
+               goto cleanup;
+       }
+       init_state++;
+
+       fbdev->dev = &pdev->dev;
+       fbdev->panel = panel;
+       platform_set_drvdata(pdev, fbdev);
+
+       mutex_init(&fbdev->rqueue_mutex);
+
+#ifdef CONFIG_ARCH_OMAP1
+       fbdev->int_ctrl = &omap1_int_ctrl;
+#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
+       fbdev->ext_if = &omap1_ext_if;
+#endif
+#else  /* OMAP2 */
+       fbdev->int_ctrl = &omap2_int_ctrl;
+#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
+       fbdev->ext_if = &omap2_ext_if;
+#endif
+#endif
+       if (omapfb_find_ctrl(fbdev) < 0) {
+               dev_err(fbdev->dev,
+                       "LCD controller not found, board not supported\n");
+               r = -ENODEV;
+               goto cleanup;
+       }
+
+       r = fbdev->panel->init(fbdev->panel, fbdev);
+       if (r)
+               goto cleanup;
+
+       pr_info("omapfb: configured for panel %s\n", fbdev->panel->name);
+
+       def_vxres = def_vxres ? : fbdev->panel->x_res;
+       def_vyres = def_vyres ? : fbdev->panel->y_res;
+
+       init_state++;
+
+       r = ctrl_init(fbdev);
+       if (r)
+               goto cleanup;
+       if (fbdev->ctrl->mmap != NULL)
+               omapfb_ops.fb_mmap = omapfb_mmap;
+       init_state++;
+
+       check_required_callbacks(fbdev);
+
+       r = planes_init(fbdev);
+       if (r)
+               goto cleanup;
+       init_state++;
+
+#ifdef CONFIG_FB_OMAP_DMA_TUNE
+       /* Set DMA priority for EMIFF access to highest */
+       if (cpu_class_is_omap1())
+               omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15);
+#endif
+
+       r = ctrl_change_mode(fbdev->fb_info[0]);
+       if (r) {
+               dev_err(fbdev->dev, "mode setting failed\n");
+               goto cleanup;
+       }
+
+       /* GFX plane is enabled by default */
+       r = fbdev->ctrl->enable_plane(OMAPFB_PLANE_GFX, 1);
+       if (r)
+               goto cleanup;
+
+       omapfb_set_update_mode(fbdev, manual_update ?
+                                  OMAPFB_MANUAL_UPDATE : OMAPFB_AUTO_UPDATE);
+       init_state++;
+
+       r = fbdev->panel->enable(fbdev->panel);
+       if (r)
+               goto cleanup;
+       init_state++;
+
+       r = omapfb_register_sysfs(fbdev);
+       if (r)
+               goto cleanup;
+       init_state++;
+
+       vram = 0;
+       for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
+               r = register_framebuffer(fbdev->fb_info[i]);
+               if (r != 0) {
+                       dev_err(fbdev->dev,
+                               "registering framebuffer %d failed\n", i);
+                       goto cleanup;
+               }
+               vram += fbdev->mem_desc.region[i].size;
+       }
+
+       fbdev->state = OMAPFB_ACTIVE;
+
+       panel = fbdev->panel;
+       phz = panel->pixel_clock * 1000;
+       hhz = phz * 10 / (panel->hfp + panel->x_res + panel->hbp + panel->hsw);
+       vhz = hhz / (panel->vfp + panel->y_res + panel->vbp + panel->vsw);
+
+       omapfb_dev = fbdev;
+
+       pr_info("omapfb: Framebuffer initialized. Total vram %lu planes %d\n",
+                       vram, fbdev->mem_desc.region_cnt);
+       pr_info("omapfb: Pixclock %lu kHz hfreq %lu.%lu kHz "
+                       "vfreq %lu.%lu Hz\n",
+                       phz / 1000, hhz / 10000, hhz % 10, vhz / 10, vhz % 10);
+
+       return 0;
+
+cleanup:
+       omapfb_free_resources(fbdev, init_state);
+
+       return r;
+}
+
+static int omapfb_probe(struct platform_device *pdev)
+{
+       BUG_ON(fbdev_pdev != NULL);
+
+       /* Delay actual initialization until the LCD is registered */
+       fbdev_pdev = pdev;
+       if (fbdev_panel != NULL)
+               omapfb_do_probe(fbdev_pdev, fbdev_panel);
+       return 0;
+}
+
+void omapfb_register_panel(struct lcd_panel *panel)
+{
+       BUG_ON(fbdev_panel != NULL);
+
+       fbdev_panel = panel;
+       if (fbdev_pdev != NULL)
+               omapfb_do_probe(fbdev_pdev, fbdev_panel);
+}
+
+/* Called when the device is being detached from the driver */
+static int omapfb_remove(struct platform_device *pdev)
+{
+       struct omapfb_device *fbdev = platform_get_drvdata(pdev);
+       enum omapfb_state saved_state = fbdev->state;
+
+       /* FIXME: wait till completion of pending events */
+
+       fbdev->state = OMAPFB_DISABLED;
+       omapfb_free_resources(fbdev, saved_state);
+
+       return 0;
+}
+
+/* PM suspend */
+static int omapfb_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       struct omapfb_device *fbdev = platform_get_drvdata(pdev);
+
+       omapfb_blank(VESA_POWERDOWN, fbdev->fb_info[0]);
+
+       return 0;
+}
+
+/* PM resume */
+static int omapfb_resume(struct platform_device *pdev)
+{
+       struct omapfb_device *fbdev = platform_get_drvdata(pdev);
+
+       omapfb_blank(VESA_NO_BLANKING, fbdev->fb_info[0]);
+       return 0;
+}
+
+static struct platform_driver omapfb_driver = {
+       .probe          = omapfb_probe,
+       .remove         = omapfb_remove,
+       .suspend        = omapfb_suspend,
+       .resume         = omapfb_resume,
+       .driver         = {
+               .name   = MODULE_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+
+#ifndef MODULE
+
+/* Process kernel command line parameters */
+static int __init omapfb_setup(char *options)
+{
+       char *this_opt = NULL;
+       int r = 0;
+
+       pr_debug("omapfb: options %s\n", options);
+
+       if (!options || !*options)
+               return 0;
+
+       while (!r && (this_opt = strsep(&options, ",")) != NULL) {
+               if (!strncmp(this_opt, "accel", 5))
+                       def_accel = 1;
+               else if (!strncmp(this_opt, "vram:", 5)) {
+                       char *suffix;
+                       unsigned long vram;
+                       vram = (simple_strtoul(this_opt + 5, &suffix, 0));
+                       switch (suffix[0]) {
+                       case '\0':
+                               break;
+                       case 'm':
+                       case 'M':
+                               vram *= 1024;
+                               /* Fall through */
+                       case 'k':
+                       case 'K':
+                               vram *= 1024;
+                               break;
+                       default:
+                               pr_debug("omapfb: invalid vram suffix %c\n",
+                                        suffix[0]);
+                               r = -1;
+                       }
+                       def_vram[def_vram_cnt++] = vram;
+               }
+               else if (!strncmp(this_opt, "vxres:", 6))
+                       def_vxres = simple_strtoul(this_opt + 6, NULL, 0);
+               else if (!strncmp(this_opt, "vyres:", 6))
+                       def_vyres = simple_strtoul(this_opt + 6, NULL, 0);
+               else if (!strncmp(this_opt, "rotate:", 7))
+                       def_rotate = (simple_strtoul(this_opt + 7, NULL, 0));
+               else if (!strncmp(this_opt, "mirror:", 7))
+                       def_mirror = (simple_strtoul(this_opt + 7, NULL, 0));
+               else if (!strncmp(this_opt, "manual_update", 13))
+                       manual_update = 1;
+               else {
+                       pr_debug("omapfb: invalid option\n");
+                       r = -1;
+               }
+       }
+
+       return r;
+}
+
+#endif
+
+/* Register both the driver and the device */
+static int __init omapfb_init(void)
+{
+#ifndef MODULE
+       char *option;
+
+       if (fb_get_options("omapfb", &option))
+               return -ENODEV;
+       omapfb_setup(option);
+#endif
+       /* Register the driver with LDM */
+       if (platform_driver_register(&omapfb_driver)) {
+               pr_debug("failed to register omapfb driver\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __exit omapfb_cleanup(void)
+{
+       platform_driver_unregister(&omapfb_driver);
+}
+
+module_param_named(accel, def_accel, uint, 0664);
+module_param_array_named(vram, def_vram, ulong, &def_vram_cnt, 0664);
+module_param_named(vxres, def_vxres, long, 0664);
+module_param_named(vyres, def_vyres, long, 0664);
+module_param_named(rotate, def_rotate, uint, 0664);
+module_param_named(mirror, def_mirror, uint, 0664);
+module_param_named(manual_update, manual_update, bool, 0664);
+
+module_init(omapfb_init);
+module_exit(omapfb_cleanup);
+
+MODULE_DESCRIPTION("TI OMAP framebuffer driver");
+MODULE_AUTHOR("Imre Deak <imre.deak@nokia.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c
new file mode 100644 (file)
index 0000000..05164ca
--- /dev/null
@@ -0,0 +1,591 @@
+/*
+ * File: drivers/video/omap/omap2/rfbi.c
+ *
+ * OMAP2 Remote Frame Buffer Interface support
+ *
+ * Copyright (C) 2005 Nokia Corporation
+ * Author: Juha Yrjölä <juha.yrjola@nokia.com>
+ *        Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/omapfb.h>
+
+#include "dispc.h"
+
+/* To work around an RFBI transfer rate limitation */
+#define OMAP_RFBI_RATE_LIMIT   1
+
+#define RFBI_BASE              0x48050800
+#define RFBI_REVISION          0x0000
+#define RFBI_SYSCONFIG         0x0010
+#define RFBI_SYSSTATUS         0x0014
+#define RFBI_CONTROL           0x0040
+#define RFBI_PIXEL_CNT         0x0044
+#define RFBI_LINE_NUMBER       0x0048
+#define RFBI_CMD               0x004c
+#define RFBI_PARAM             0x0050
+#define RFBI_DATA              0x0054
+#define RFBI_READ              0x0058
+#define RFBI_STATUS            0x005c
+#define RFBI_CONFIG0           0x0060
+#define RFBI_ONOFF_TIME0       0x0064
+#define RFBI_CYCLE_TIME0       0x0068
+#define RFBI_DATA_CYCLE1_0     0x006c
+#define RFBI_DATA_CYCLE2_0     0x0070
+#define RFBI_DATA_CYCLE3_0     0x0074
+#define RFBI_VSYNC_WIDTH       0x0090
+#define RFBI_HSYNC_WIDTH       0x0094
+
+#define DISPC_BASE             0x48050400
+#define DISPC_CONTROL          0x0040
+
+static struct {
+       u32             base;
+       void            (*lcdc_callback)(void *data);
+       void            *lcdc_callback_data;
+       unsigned long   l4_khz;
+       int             bits_per_cycle;
+       struct omapfb_device *fbdev;
+       struct clk      *dss_ick;
+       struct clk      *dss1_fck;
+       unsigned        tearsync_pin_cnt;
+       unsigned        tearsync_mode;
+} rfbi;
+
+static inline void rfbi_write_reg(int idx, u32 val)
+{
+       __raw_writel(val, rfbi.base + idx);
+}
+
+static inline u32 rfbi_read_reg(int idx)
+{
+       return __raw_readl(rfbi.base + idx);
+}
+
+static int rfbi_get_clocks(void)
+{
+       if (IS_ERR((rfbi.dss_ick = clk_get(rfbi.fbdev->dev, "dss_ick")))) {
+               dev_err(rfbi.fbdev->dev, "can't get dss_ick");
+               return PTR_ERR(rfbi.dss_ick);
+       }
+
+       if (IS_ERR((rfbi.dss1_fck = clk_get(rfbi.fbdev->dev, "dss1_fck")))) {
+               dev_err(rfbi.fbdev->dev, "can't get dss1_fck");
+               clk_put(rfbi.dss_ick);
+               return PTR_ERR(rfbi.dss1_fck);
+       }
+
+       return 0;
+}
+
+static void rfbi_put_clocks(void)
+{
+       clk_put(rfbi.dss1_fck);
+       clk_put(rfbi.dss_ick);
+}
+
+static void rfbi_enable_clocks(int enable)
+{
+       if (enable) {
+               clk_enable(rfbi.dss_ick);
+               clk_enable(rfbi.dss1_fck);
+       } else {
+               clk_disable(rfbi.dss1_fck);
+               clk_disable(rfbi.dss_ick);
+       }
+}
+
+
+#ifdef VERBOSE
+static void rfbi_print_timings(void)
+{
+       u32 l;
+       u32 time;
+
+       l = rfbi_read_reg(RFBI_CONFIG0);
+       time = 1000000000 / rfbi.l4_khz;
+       if (l & (1 << 4))
+               time *= 2;
+
+       dev_dbg(rfbi.fbdev->dev, "Tick time %u ps\n", time);
+       l = rfbi_read_reg(RFBI_ONOFF_TIME0);
+       dev_dbg(rfbi.fbdev->dev,
+               "CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, "
+               "REONTIME %d, REOFFTIME %d\n",
+               l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f,
+               (l >> 20) & 0x0f, (l >> 24) & 0x3f);
+
+       l = rfbi_read_reg(RFBI_CYCLE_TIME0);
+       dev_dbg(rfbi.fbdev->dev,
+               "WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, "
+               "ACCESSTIME %d\n",
+               (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f,
+               (l >> 22) & 0x3f);
+}
+#else
+static void rfbi_print_timings(void) {}
+#endif
+
+static void rfbi_set_timings(const struct extif_timings *t)
+{
+       u32 l;
+
+       BUG_ON(!t->converted);
+
+       rfbi_enable_clocks(1);
+       rfbi_write_reg(RFBI_ONOFF_TIME0, t->tim[0]);
+       rfbi_write_reg(RFBI_CYCLE_TIME0, t->tim[1]);
+
+       l = rfbi_read_reg(RFBI_CONFIG0);
+       l &= ~(1 << 4);
+       l |= (t->tim[2] ? 1 : 0) << 4;
+       rfbi_write_reg(RFBI_CONFIG0, l);
+
+       rfbi_print_timings();
+       rfbi_enable_clocks(0);
+}
+
+static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
+{
+       *clk_period = 1000000000 / rfbi.l4_khz;
+       *max_clk_div = 2;
+}
+
+static int ps_to_rfbi_ticks(int time, int div)
+{
+       unsigned long tick_ps;
+       int ret;
+
+       /* Calculate in picosecs to yield more exact results */
+       tick_ps = 1000000000 / (rfbi.l4_khz) * div;
+
+       ret = (time + tick_ps - 1) / tick_ps;
+
+       return ret;
+}
+
+#ifdef OMAP_RFBI_RATE_LIMIT
+static unsigned long rfbi_get_max_tx_rate(void)
+{
+       unsigned long   l4_rate, dss1_rate;
+       int             min_l4_ticks = 0;
+       int             i;
+
+       /* According to TI this can't be calculated so make the
+        * adjustments for a couple of known frequencies and warn for
+        * others.
+        */
+       static const struct {
+               unsigned long l4_clk;           /* HZ */
+               unsigned long dss1_clk;         /* HZ */
+               unsigned long min_l4_ticks;
+       } ftab[] = {
+               { 55,   132,    7, },           /* 7.86 MPix/s */
+               { 110,  110,    12, },          /* 9.16 MPix/s */
+               { 110,  132,    10, },          /* 11   Mpix/s */
+               { 120,  120,    10, },          /* 12   Mpix/s */
+               { 133,  133,    10, },          /* 13.3 Mpix/s */
+       };
+
+       l4_rate = rfbi.l4_khz / 1000;
+       dss1_rate = clk_get_rate(rfbi.dss1_fck) / 1000000;
+
+       for (i = 0; i < ARRAY_SIZE(ftab); i++) {
+               /* Use a window instead of an exact match, to account
+                * for different DPLL multiplier / divider pairs.
+                */
+               if (abs(ftab[i].l4_clk - l4_rate) < 3 &&
+                   abs(ftab[i].dss1_clk - dss1_rate) < 3) {
+                       min_l4_ticks = ftab[i].min_l4_ticks;
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(ftab)) {
+               /* Can't be sure, return anyway the maximum not
+                * rate-limited. This might cause a problem only for the
+                * tearing synchronisation.
+                */
+               dev_err(rfbi.fbdev->dev,
+                       "can't determine maximum RFBI transfer rate\n");
+               return rfbi.l4_khz * 1000;
+       }
+       return rfbi.l4_khz * 1000 / min_l4_ticks;
+}
+#else
+static int rfbi_get_max_tx_rate(void)
+{
+       return rfbi.l4_khz * 1000;
+}
+#endif
+
+
+static int rfbi_convert_timings(struct extif_timings *t)
+{
+       u32 l;
+       int reon, reoff, weon, weoff, cson, csoff, cs_pulse;
+       int actim, recyc, wecyc;
+       int div = t->clk_div;
+
+       if (div <= 0 || div > 2)
+               return -1;
+
+       /* Make sure that after conversion it still holds that:
+        * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff,
+        * csoff > cson, csoff >= max(weoff, reoff), actim > reon
+        */
+       weon = ps_to_rfbi_ticks(t->we_on_time, div);
+       weoff = ps_to_rfbi_ticks(t->we_off_time, div);
+       if (weoff <= weon)
+               weoff = weon + 1;
+       if (weon > 0x0f)
+               return -1;
+       if (weoff > 0x3f)
+               return -1;
+
+       reon = ps_to_rfbi_ticks(t->re_on_time, div);
+       reoff = ps_to_rfbi_ticks(t->re_off_time, div);
+       if (reoff <= reon)
+               reoff = reon + 1;
+       if (reon > 0x0f)
+               return -1;
+       if (reoff > 0x3f)
+               return -1;
+
+       cson = ps_to_rfbi_ticks(t->cs_on_time, div);
+       csoff = ps_to_rfbi_ticks(t->cs_off_time, div);
+       if (csoff <= cson)
+               csoff = cson + 1;
+       if (csoff < max(weoff, reoff))
+               csoff = max(weoff, reoff);
+       if (cson > 0x0f)
+               return -1;
+       if (csoff > 0x3f)
+               return -1;
+
+       l =  cson;
+       l |= csoff << 4;
+       l |= weon  << 10;
+       l |= weoff << 14;
+       l |= reon  << 20;
+       l |= reoff << 24;
+
+       t->tim[0] = l;
+
+       actim = ps_to_rfbi_ticks(t->access_time, div);
+       if (actim <= reon)
+               actim = reon + 1;
+       if (actim > 0x3f)
+               return -1;
+
+       wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div);
+       if (wecyc < weoff)
+               wecyc = weoff;
+       if (wecyc > 0x3f)
+               return -1;
+
+       recyc = ps_to_rfbi_ticks(t->re_cycle_time, div);
+       if (recyc < reoff)
+               recyc = reoff;
+       if (recyc > 0x3f)
+               return -1;
+
+       cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div);
+       if (cs_pulse > 0x3f)
+               return -1;
+
+       l =  wecyc;
+       l |= recyc    << 6;
+       l |= cs_pulse << 12;
+       l |= actim    << 22;
+
+       t->tim[1] = l;
+
+       t->tim[2] = div - 1;
+
+       t->converted = 1;
+
+       return 0;
+}
+
+static int rfbi_setup_tearsync(unsigned pin_cnt,
+                              unsigned hs_pulse_time, unsigned vs_pulse_time,
+                              int hs_pol_inv, int vs_pol_inv, int extif_div)
+{
+       int hs, vs;
+       int min;
+       u32 l;
+
+       if (pin_cnt != 1 && pin_cnt != 2)
+               return -EINVAL;
+
+       hs = ps_to_rfbi_ticks(hs_pulse_time, 1);
+       vs = ps_to_rfbi_ticks(vs_pulse_time, 1);
+       if (hs < 2)
+               return -EDOM;
+       if (pin_cnt == 2)
+               min = 2;
+       else
+               min = 4;
+       if (vs < min)
+               return -EDOM;
+       if (vs == hs)
+               return -EINVAL;
+       rfbi.tearsync_pin_cnt = pin_cnt;
+       dev_dbg(rfbi.fbdev->dev,
+               "setup_tearsync: pins %d hs %d vs %d hs_inv %d vs_inv %d\n",
+               pin_cnt, hs, vs, hs_pol_inv, vs_pol_inv);
+
+       rfbi_enable_clocks(1);
+       rfbi_write_reg(RFBI_HSYNC_WIDTH, hs);
+       rfbi_write_reg(RFBI_VSYNC_WIDTH, vs);
+
+       l = rfbi_read_reg(RFBI_CONFIG0);
+       if (hs_pol_inv)
+               l &= ~(1 << 21);
+       else
+               l |= 1 << 21;
+       if (vs_pol_inv)
+               l &= ~(1 << 20);
+       else
+               l |= 1 << 20;
+       rfbi_enable_clocks(0);
+
+       return 0;
+}
+
+static int rfbi_enable_tearsync(int enable, unsigned line)
+{
+       u32 l;
+
+       dev_dbg(rfbi.fbdev->dev, "tearsync %d line %d mode %d\n",
+               enable, line, rfbi.tearsync_mode);
+       if (line > (1 << 11) - 1)
+               return -EINVAL;
+
+       rfbi_enable_clocks(1);
+       l = rfbi_read_reg(RFBI_CONFIG0);
+       l &= ~(0x3 << 2);
+       if (enable) {
+               rfbi.tearsync_mode = rfbi.tearsync_pin_cnt;
+               l |= rfbi.tearsync_mode << 2;
+       } else
+               rfbi.tearsync_mode = 0;
+       rfbi_write_reg(RFBI_CONFIG0, l);
+       rfbi_write_reg(RFBI_LINE_NUMBER, line);
+       rfbi_enable_clocks(0);
+
+       return 0;
+}
+
+static void rfbi_write_command(const void *buf, unsigned int len)
+{
+       rfbi_enable_clocks(1);
+       if (rfbi.bits_per_cycle == 16) {
+               const u16 *w = buf;
+               BUG_ON(len & 1);
+               for (; len; len -= 2)
+                       rfbi_write_reg(RFBI_CMD, *w++);
+       } else {
+               const u8 *b = buf;
+               BUG_ON(rfbi.bits_per_cycle != 8);
+               for (; len; len--)
+                       rfbi_write_reg(RFBI_CMD, *b++);
+       }
+       rfbi_enable_clocks(0);
+}
+
+static void rfbi_read_data(void *buf, unsigned int len)
+{
+       rfbi_enable_clocks(1);
+       if (rfbi.bits_per_cycle == 16) {
+               u16 *w = buf;
+               BUG_ON(len & ~1);
+               for (; len; len -= 2) {
+                       rfbi_write_reg(RFBI_READ, 0);
+                       *w++ = rfbi_read_reg(RFBI_READ);
+               }
+       } else {
+               u8 *b = buf;
+               BUG_ON(rfbi.bits_per_cycle != 8);
+               for (; len; len--) {
+                       rfbi_write_reg(RFBI_READ, 0);
+                       *b++ = rfbi_read_reg(RFBI_READ);
+               }
+       }
+       rfbi_enable_clocks(0);
+}
+
+static void rfbi_write_data(const void *buf, unsigned int len)
+{
+       rfbi_enable_clocks(1);
+       if (rfbi.bits_per_cycle == 16) {
+               const u16 *w = buf;
+               BUG_ON(len & 1);
+               for (; len; len -= 2)
+                       rfbi_write_reg(RFBI_PARAM, *w++);
+       } else {
+               const u8 *b = buf;
+               BUG_ON(rfbi.bits_per_cycle != 8);
+               for (; len; len--)
+                       rfbi_write_reg(RFBI_PARAM, *b++);
+       }
+       rfbi_enable_clocks(0);
+}
+
+static void rfbi_transfer_area(int width, int height,
+                               void (callback)(void * data), void *data)
+{
+       u32 w;
+
+       BUG_ON(callback == NULL);
+
+       rfbi_enable_clocks(1);
+       omap_dispc_set_lcd_size(width, height);
+
+       rfbi.lcdc_callback = callback;
+       rfbi.lcdc_callback_data = data;
+
+       rfbi_write_reg(RFBI_PIXEL_CNT, width * height);
+
+       w = rfbi_read_reg(RFBI_CONTROL);
+       w |= 1;                         /* enable */
+       if (!rfbi.tearsync_mode)
+               w |= 1 << 4;            /* internal trigger, reset by HW */
+       rfbi_write_reg(RFBI_CONTROL, w);
+
+       omap_dispc_enable_lcd_out(1);
+}
+
+static inline void _stop_transfer(void)
+{
+       u32 w;
+
+       w = rfbi_read_reg(RFBI_CONTROL);
+       rfbi_write_reg(RFBI_CONTROL, w & ~(1 << 0));
+       rfbi_enable_clocks(0);
+}
+
+static void rfbi_dma_callback(void *data)
+{
+       _stop_transfer();
+       rfbi.lcdc_callback(rfbi.lcdc_callback_data);
+}
+
+static void rfbi_set_bits_per_cycle(int bpc)
+{
+       u32 l;
+
+       rfbi_enable_clocks(1);
+       l = rfbi_read_reg(RFBI_CONFIG0);
+       l &= ~(0x03 << 0);
+       switch (bpc)
+       {
+       case 8:
+               break;
+       case 16:
+               l |= 3;
+               break;
+       default:
+               BUG();
+       }
+       rfbi_write_reg(RFBI_CONFIG0, l);
+       rfbi.bits_per_cycle = bpc;
+       rfbi_enable_clocks(0);
+}
+
+static int rfbi_init(struct omapfb_device *fbdev)
+{
+       u32 l;
+       int r;
+
+       rfbi.fbdev = fbdev;
+       rfbi.base = io_p2v(RFBI_BASE);
+
+       if ((r = rfbi_get_clocks()) < 0)
+               return r;
+       rfbi_enable_clocks(1);
+
+       rfbi.l4_khz = clk_get_rate(rfbi.dss_ick) / 1000;
+
+       /* Reset */
+       rfbi_write_reg(RFBI_SYSCONFIG, 1 << 1);
+       while (!(rfbi_read_reg(RFBI_SYSSTATUS) & (1 << 0)));
+
+       l = rfbi_read_reg(RFBI_SYSCONFIG);
+       /* Enable autoidle and smart-idle */
+       l |= (1 << 0) | (2 << 3);
+       rfbi_write_reg(RFBI_SYSCONFIG, l);
+
+       /* 16-bit interface, ITE trigger mode, 16-bit data */
+       l = (0x03 << 0) | (0x00 << 2) | (0x01 << 5) | (0x02 << 7);
+       l |= (0 << 9) | (1 << 20) | (1 << 21);
+       rfbi_write_reg(RFBI_CONFIG0, l);
+
+       rfbi_write_reg(RFBI_DATA_CYCLE1_0, 0x00000010);
+
+       l = rfbi_read_reg(RFBI_CONTROL);
+       /* Select CS0, clear bypass mode */
+       l = (0x01 << 2);
+       rfbi_write_reg(RFBI_CONTROL, l);
+
+       if ((r = omap_dispc_request_irq(rfbi_dma_callback, NULL)) < 0) {
+               dev_err(fbdev->dev, "can't get DISPC irq\n");
+               rfbi_enable_clocks(0);
+               return r;
+       }
+
+       l = rfbi_read_reg(RFBI_REVISION);
+       pr_info("omapfb: RFBI version %d.%d initialized\n",
+               (l >> 4) & 0x0f, l & 0x0f);
+
+       rfbi_enable_clocks(0);
+
+       return 0;
+}
+
+static void rfbi_cleanup(void)
+{
+       omap_dispc_free_irq();
+       rfbi_put_clocks();
+}
+
+const struct lcd_ctrl_extif omap2_ext_if = {
+       .init                   = rfbi_init,
+       .cleanup                = rfbi_cleanup,
+       .get_clk_info           = rfbi_get_clk_info,
+       .get_max_tx_rate        = rfbi_get_max_tx_rate,
+       .set_bits_per_cycle     = rfbi_set_bits_per_cycle,
+       .convert_timings        = rfbi_convert_timings,
+       .set_timings            = rfbi_set_timings,
+       .write_command          = rfbi_write_command,
+       .read_data              = rfbi_read_data,
+       .write_data             = rfbi_write_data,
+       .transfer_area          = rfbi_transfer_area,
+       .setup_tearsync         = rfbi_setup_tearsync,
+       .enable_tearsync        = rfbi_enable_tearsync,
+
+       .max_transmit_size      = (u32)~0,
+};
+
diff --git a/drivers/video/omap/sossi.c b/drivers/video/omap/sossi.c
new file mode 100644 (file)
index 0000000..505b932
--- /dev/null
@@ -0,0 +1,682 @@
+/*
+ * File: drivers/video/omap/omap1/sossi.c
+ *
+ * OMAP1 Special OptimiSed Screen Interface support
+ *
+ * Copyright (C) 2004-2005 Nokia Corporation
+ * Author: Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/dma.h>
+#include <asm/arch/omapfb.h>
+
+#include "lcdc.h"
+
+#define MODULE_NAME            "omapfb-sossi"
+
+#define OMAP_SOSSI_BASE         0xfffbac00
+#define SOSSI_ID_REG           0x00
+#define SOSSI_INIT1_REG                0x04
+#define SOSSI_INIT2_REG                0x08
+#define SOSSI_INIT3_REG                0x0c
+#define SOSSI_FIFO_REG         0x10
+#define SOSSI_REOTABLE_REG     0x14
+#define SOSSI_TEARING_REG      0x18
+#define SOSSI_INIT1B_REG       0x1c
+#define SOSSI_FIFOB_REG                0x20
+
+#define DMA_GSCR          0xfffedc04
+#define DMA_LCD_CCR       0xfffee3c2
+#define DMA_LCD_CTRL      0xfffee3c4
+#define DMA_LCD_LCH_CTRL  0xfffee3ea
+
+#define CONF_SOSSI_RESET_R      (1 << 23)
+
+#define RD_ACCESS              0
+#define WR_ACCESS              1
+
+#define SOSSI_MAX_XMIT_BYTES   (512 * 1024)
+
+static struct {
+       void __iomem    *base;
+       struct clk      *fck;
+       unsigned long   fck_hz;
+       spinlock_t      lock;
+       int             bus_pick_count;
+       int             bus_pick_width;
+       int             tearsync_mode;
+       int             tearsync_line;
+       void            (*lcdc_callback)(void *data);
+       void            *lcdc_callback_data;
+       int             vsync_dma_pending;
+       /* timing for read and write access */
+       int             clk_div;
+       u8              clk_tw0[2];
+       u8              clk_tw1[2];
+       /* if last_access is the same as current we don't have to change
+        * the timings
+        */
+       int             last_access;
+
+       struct omapfb_device    *fbdev;
+} sossi;
+
+static inline u32 sossi_read_reg(int reg)
+{
+        return readl(sossi.base + reg);
+}
+
+static inline u16 sossi_read_reg16(int reg)
+{
+        return readw(sossi.base + reg);
+}
+
+static inline u8 sossi_read_reg8(int reg)
+{
+        return readb(sossi.base + reg);
+}
+
+static inline void sossi_write_reg(int reg, u32 value)
+{
+        writel(value, sossi.base + reg);
+}
+
+static inline void sossi_write_reg16(int reg, u16 value)
+{
+        writew(value, sossi.base + reg);
+}
+
+static inline void sossi_write_reg8(int reg, u8 value)
+{
+        writeb(value, sossi.base + reg);
+}
+
+static void sossi_set_bits(int reg, u32 bits)
+{
+        sossi_write_reg(reg, sossi_read_reg(reg) | bits);
+}
+
+static void sossi_clear_bits(int reg, u32 bits)
+{
+        sossi_write_reg(reg, sossi_read_reg(reg) & ~bits);
+}
+
+#define HZ_TO_PS(x)    (1000000000 / (x / 1000))
+
+static u32 ps_to_sossi_ticks(u32 ps, int div)
+{
+       u32 clk_period = HZ_TO_PS(sossi.fck_hz) * div;
+       return (clk_period + ps - 1) / clk_period;
+}
+
+static int calc_rd_timings(struct extif_timings *t)
+{
+       u32 tw0, tw1;
+       int reon, reoff, recyc, actim;
+       int div = t->clk_div;
+
+       /* Make sure that after conversion it still holds that:
+        * reoff > reon, recyc >= reoff, actim > reon
+        */
+       reon = ps_to_sossi_ticks(t->re_on_time, div);
+       /* reon will be exactly one sossi tick */
+       if (reon > 1)
+               return -1;
+
+       reoff = ps_to_sossi_ticks(t->re_off_time, div);
+
+       if (reoff <= reon)
+               reoff = reon + 1;
+
+       tw0 = reoff - reon;
+       if (tw0 > 0x10)
+               return -1;
+
+       recyc = ps_to_sossi_ticks(t->re_cycle_time, div);
+       if (recyc <= reoff)
+               recyc = reoff + 1;
+
+       tw1 = recyc - tw0;
+       /* values less then 3 result in the SOSSI block resetting itself */
+       if (tw1 < 3)
+               tw1 = 3;
+       if (tw1 > 0x40)
+               return -1;
+
+       actim = ps_to_sossi_ticks(t->access_time, div);
+       if (actim < reoff)
+               actim++;
+       /* access time (data hold time) will be exactly one sossi
+        * tick
+        */
+       if (actim - reoff > 1)
+               return -1;
+
+       t->tim[0] = tw0 - 1;
+       t->tim[1] = tw1 - 1;
+
+       return 0;
+}
+
+static int calc_wr_timings(struct extif_timings *t)
+{
+       u32 tw0, tw1;
+       int weon, weoff, wecyc;
+       int div = t->clk_div;
+
+       /* Make sure that after conversion it still holds that:
+        * weoff > weon, wecyc >= weoff
+        */
+       weon = ps_to_sossi_ticks(t->we_on_time, div);
+       /* weon will be exactly one sossi tick */
+       if (weon > 1)
+               return -1;
+
+       weoff = ps_to_sossi_ticks(t->we_off_time, div);
+       if (weoff <= weon)
+               weoff = weon + 1;
+       tw0 = weoff - weon;
+       if (tw0 > 0x10)
+               return -1;
+
+       wecyc = ps_to_sossi_ticks(t->we_cycle_time, div);
+       if (wecyc <= weoff)
+               wecyc = weoff + 1;
+
+       tw1 = wecyc - tw0;
+       /* values less then 3 result in the SOSSI block resetting itself */
+       if (tw1 < 3)
+               tw1 = 3;
+       if (tw1 > 0x40)
+               return -1;
+
+       t->tim[2] = tw0 - 1;
+       t->tim[3] = tw1 - 1;
+
+       return 0;
+}
+
+static void _set_timing(int div, int tw0, int tw1)
+{
+       u32 l;
+
+#ifdef VERBOSE
+       dev_dbg(sossi.fbdev->dev, "Using TW0 = %d, TW1 = %d, div = %d\n",
+                tw0 + 1, tw1 + 1, div);
+#endif
+
+       clk_set_rate(sossi.fck, sossi.fck_hz / div);
+       clk_enable(sossi.fck);
+       l = sossi_read_reg(SOSSI_INIT1_REG);
+       l &= ~((0x0f << 20) | (0x3f << 24));
+       l |= (tw0 << 20) | (tw1 << 24);
+       sossi_write_reg(SOSSI_INIT1_REG, l);
+       clk_disable(sossi.fck);
+}
+
+static void _set_bits_per_cycle(int bus_pick_count, int bus_pick_width)
+{
+       u32 l;
+
+       l = sossi_read_reg(SOSSI_INIT3_REG);
+       l &= ~0x3ff;
+       l |= ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f);
+       sossi_write_reg(SOSSI_INIT3_REG, l);
+}
+
+static void _set_tearsync_mode(int mode, unsigned line)
+{
+       u32 l;
+
+       l = sossi_read_reg(SOSSI_TEARING_REG);
+       l &= ~(((1 << 11) - 1) << 15);
+       l |= line << 15;
+       l &= ~(0x3 << 26);
+       l |= mode << 26;
+       sossi_write_reg(SOSSI_TEARING_REG, l);
+       if (mode)
+               sossi_set_bits(SOSSI_INIT2_REG, 1 << 6);        /* TE logic */
+       else
+               sossi_clear_bits(SOSSI_INIT2_REG, 1 << 6);
+}
+
+static inline void set_timing(int access)
+{
+       if (access != sossi.last_access) {
+               sossi.last_access = access;
+               _set_timing(sossi.clk_div,
+                           sossi.clk_tw0[access], sossi.clk_tw1[access]);
+       }
+}
+
+static void sossi_start_transfer(void)
+{
+       /* WE */
+       sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4);
+       /* CS active low */
+       sossi_clear_bits(SOSSI_INIT1_REG, 1 << 30);
+}
+
+static void sossi_stop_transfer(void)
+{
+       /* WE */
+       sossi_set_bits(SOSSI_INIT2_REG, 1 << 4);
+       /* CS active low */
+       sossi_set_bits(SOSSI_INIT1_REG, 1 << 30);
+}
+
+static void wait_end_of_write(void)
+{
+       /* Before reading we must check if some writings are going on */
+       while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3)));
+}
+
+static void send_data(const void *data, unsigned int len)
+{
+       while (len >= 4) {
+               sossi_write_reg(SOSSI_FIFO_REG, *(const u32 *) data);
+               len -= 4;
+               data += 4;
+       }
+       while (len >= 2) {
+               sossi_write_reg16(SOSSI_FIFO_REG, *(const u16 *) data);
+               len -= 2;
+               data += 2;
+       }
+       while (len) {
+               sossi_write_reg8(SOSSI_FIFO_REG, *(const u8 *) data);
+               len--;
+               data++;
+       }
+}
+
+static void set_cycles(unsigned int len)
+{
+       unsigned long nr_cycles = len / (sossi.bus_pick_width / 8);
+
+       BUG_ON((nr_cycles - 1) & ~0x3ffff);
+
+       sossi_clear_bits(SOSSI_INIT1_REG, 0x3ffff);
+       sossi_set_bits(SOSSI_INIT1_REG, (nr_cycles - 1) & 0x3ffff);
+}
+
+static int sossi_convert_timings(struct extif_timings *t)
+{
+       int r = 0;
+       int div = t->clk_div;
+
+       t->converted = 0;
+
+       if (div <= 0 || div > 8)
+               return -1;
+
+       /* no CS on SOSSI, so ignore cson, csoff, cs_pulsewidth */
+       if ((r = calc_rd_timings(t)) < 0)
+               return r;
+
+       if ((r = calc_wr_timings(t)) < 0)
+               return r;
+
+       t->tim[4] = div;
+
+       t->converted = 1;
+
+       return 0;
+}
+
+static void sossi_set_timings(const struct extif_timings *t)
+{
+       BUG_ON(!t->converted);
+
+       sossi.clk_tw0[RD_ACCESS] = t->tim[0];
+       sossi.clk_tw1[RD_ACCESS] = t->tim[1];
+
+       sossi.clk_tw0[WR_ACCESS] = t->tim[2];
+       sossi.clk_tw1[WR_ACCESS] = t->tim[3];
+
+       sossi.clk_div = t->tim[4];
+}
+
+static void sossi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
+{
+       *clk_period = HZ_TO_PS(sossi.fck_hz);
+       *max_clk_div = 8;
+}
+
+static void sossi_set_bits_per_cycle(int bpc)
+{
+       int bus_pick_count, bus_pick_width;
+
+       /* We set explicitly the the bus_pick_count as well, although
+        * with remapping/reordering disabled it will be calculated by HW
+        * as (32 / bus_pick_width).
+        */
+       switch (bpc) {
+       case 8:
+               bus_pick_count = 4;
+               bus_pick_width = 8;
+               break;
+       case 16:
+               bus_pick_count = 2;
+               bus_pick_width = 16;
+               break;
+       default:
+               BUG();
+               return;
+       }
+       sossi.bus_pick_width = bus_pick_width;
+       sossi.bus_pick_count = bus_pick_count;
+}
+
+static int sossi_setup_tearsync(unsigned pin_cnt,
+                               unsigned hs_pulse_time, unsigned vs_pulse_time,
+                               int hs_pol_inv, int vs_pol_inv, int div)
+{
+       int hs, vs;
+       u32 l;
+
+       if (pin_cnt != 1 || div < 1 || div > 8)
+               return -EINVAL;
+
+       hs = ps_to_sossi_ticks(hs_pulse_time, div);
+       vs = ps_to_sossi_ticks(vs_pulse_time, div);
+       if (vs < 8 || vs <= hs || vs >= (1 << 12))
+               return -EDOM;
+       vs /= 8;
+       vs--;
+       if (hs > 8)
+               hs = 8;
+       if (hs)
+               hs--;
+
+       dev_dbg(sossi.fbdev->dev,
+               "setup_tearsync: hs %d vs %d hs_inv %d vs_inv %d\n",
+               hs, vs, hs_pol_inv, vs_pol_inv);
+
+       clk_enable(sossi.fck);
+       l = sossi_read_reg(SOSSI_TEARING_REG);
+       l &= ~((1 << 15) - 1);
+       l |= vs << 3;
+       l |= hs;
+       if (hs_pol_inv)
+               l |= 1 << 29;
+       else
+               l &= ~(1 << 29);
+       if (vs_pol_inv)
+               l |= 1 << 28;
+       else
+               l &= ~(1 << 28);
+       sossi_write_reg(SOSSI_TEARING_REG, l);
+       clk_disable(sossi.fck);
+
+       return 0;
+}
+
+static int sossi_enable_tearsync(int enable, unsigned line)
+{
+       int mode;
+
+       dev_dbg(sossi.fbdev->dev, "tearsync %d line %d\n", enable, line);
+       if (line >= 1 << 11)
+               return -EINVAL;
+       if (enable) {
+               if (line)
+                       mode = 2;               /* HS or VS */
+               else
+                       mode = 3;               /* VS only */
+       } else
+               mode = 0;
+       sossi.tearsync_line = line;
+       sossi.tearsync_mode = mode;
+
+       return 0;
+}
+
+static void sossi_write_command(const void *data, unsigned int len)
+{
+       clk_enable(sossi.fck);
+       set_timing(WR_ACCESS);
+       _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
+       /* CMD#/DATA */
+       sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18);
+       set_cycles(len);
+       sossi_start_transfer();
+       send_data(data, len);
+       sossi_stop_transfer();
+       wait_end_of_write();
+       clk_disable(sossi.fck);
+}
+
+static void sossi_write_data(const void *data, unsigned int len)
+{
+       clk_enable(sossi.fck);
+       set_timing(WR_ACCESS);
+       _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
+       /* CMD#/DATA */
+       sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
+       set_cycles(len);
+       sossi_start_transfer();
+       send_data(data, len);
+       sossi_stop_transfer();
+       wait_end_of_write();
+       clk_disable(sossi.fck);
+}
+
+static void sossi_transfer_area(int width, int height,
+                               void (callback)(void *data), void *data)
+{
+       BUG_ON(callback == NULL);
+
+       sossi.lcdc_callback = callback;
+       sossi.lcdc_callback_data = data;
+
+       clk_enable(sossi.fck);
+       set_timing(WR_ACCESS);
+       _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
+       _set_tearsync_mode(sossi.tearsync_mode, sossi.tearsync_line);
+       /* CMD#/DATA */
+       sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
+       set_cycles(width * height * sossi.bus_pick_width / 8);
+
+       sossi_start_transfer();
+       if (sossi.tearsync_mode) {
+               /* Wait for the sync signal and start the transfer only
+                * then. We can't seem to be able to use HW sync DMA for
+                * this since LCD DMA shows huge latencies, as if it
+                * would ignore some of the DMA requests from SoSSI.
+                */
+               unsigned long flags;
+
+               spin_lock_irqsave(&sossi.lock, flags);
+               sossi.vsync_dma_pending++;
+               spin_unlock_irqrestore(&sossi.lock, flags);
+       } else
+               /* Just start the transfer right away. */
+               omap_enable_lcd_dma();
+}
+
+static void sossi_dma_callback(void *data)
+{
+       omap_stop_lcd_dma();
+       sossi_stop_transfer();
+       clk_disable(sossi.fck);
+       sossi.lcdc_callback(sossi.lcdc_callback_data);
+}
+
+static void sossi_read_data(void *data, unsigned int len)
+{
+       clk_enable(sossi.fck);
+       set_timing(RD_ACCESS);
+       _set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
+       /* CMD#/DATA */
+       sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
+       set_cycles(len);
+       sossi_start_transfer();
+       while (len >= 4) {
+               *(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG);
+               len -= 4;
+               data += 4;
+       }
+       while (len >= 2) {
+               *(u16 *) data = sossi_read_reg16(SOSSI_FIFO_REG);
+               len -= 2;
+               data += 2;
+       }
+       while (len) {
+               *(u8 *) data = sossi_read_reg8(SOSSI_FIFO_REG);
+               len--;
+               data++;
+       }
+       sossi_stop_transfer();
+       clk_disable(sossi.fck);
+}
+
+static irqreturn_t sossi_match_irq(int irq, void *data)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&sossi.lock, flags);
+       if (sossi.vsync_dma_pending) {
+               sossi.vsync_dma_pending--;
+               omap_enable_lcd_dma();
+       }
+       spin_unlock_irqrestore(&sossi.lock, flags);
+       return IRQ_HANDLED;
+}
+
+static int sossi_init(struct omapfb_device *fbdev)
+{
+       u32 l, k;
+       struct clk *fck;
+       struct clk *dpll1out_ck;
+       int r;
+
+       sossi.base = (void __iomem *)IO_ADDRESS(OMAP_SOSSI_BASE);
+       sossi.fbdev = fbdev;
+       spin_lock_init(&sossi.lock);
+
+       dpll1out_ck = clk_get(fbdev->dev, "ck_dpll1out");
+       if (IS_ERR(dpll1out_ck)) {
+               dev_err(fbdev->dev, "can't get DPLL1OUT clock\n");
+               return PTR_ERR(dpll1out_ck);
+       }
+       /* We need the parent clock rate, which we might divide further
+        * depending on the timing requirements of the controller. See
+        * _set_timings.
+        */
+       sossi.fck_hz = clk_get_rate(dpll1out_ck);
+       clk_put(dpll1out_ck);
+
+       fck = clk_get(fbdev->dev, "ck_sossi");
+       if (IS_ERR(fck)) {
+               dev_err(fbdev->dev, "can't get SoSSI functional clock\n");
+               return PTR_ERR(fck);
+       }
+       sossi.fck = fck;
+
+       /* Reset and enable the SoSSI module */
+       l = omap_readl(MOD_CONF_CTRL_1);
+       l |= CONF_SOSSI_RESET_R;
+       omap_writel(l, MOD_CONF_CTRL_1);
+       l &= ~CONF_SOSSI_RESET_R;
+       omap_writel(l, MOD_CONF_CTRL_1);
+
+       clk_enable(sossi.fck);
+       l = omap_readl(ARM_IDLECT2);
+       l &= ~(1 << 8);                 /* DMACK_REQ */
+       omap_writel(l, ARM_IDLECT2);
+
+       l = sossi_read_reg(SOSSI_INIT2_REG);
+       /* Enable and reset the SoSSI block */
+       l |= (1 << 0) | (1 << 1);
+       sossi_write_reg(SOSSI_INIT2_REG, l);
+       /* Take SoSSI out of reset */
+       l &= ~(1 << 1);
+       sossi_write_reg(SOSSI_INIT2_REG, l);
+
+       sossi_write_reg(SOSSI_ID_REG, 0);
+       l = sossi_read_reg(SOSSI_ID_REG);
+       k = sossi_read_reg(SOSSI_ID_REG);
+
+       if (l != 0x55555555 || k != 0xaaaaaaaa) {
+               dev_err(fbdev->dev,
+                       "invalid SoSSI sync pattern: %08x, %08x\n", l, k);
+               r = -ENODEV;
+               goto err;
+       }
+
+       if ((r = omap_lcdc_set_dma_callback(sossi_dma_callback, NULL)) < 0) {
+               dev_err(fbdev->dev, "can't get LCDC IRQ\n");
+               r = -ENODEV;
+               goto err;
+       }
+
+       l = sossi_read_reg(SOSSI_ID_REG); /* Component code */
+       l = sossi_read_reg(SOSSI_ID_REG);
+       dev_info(fbdev->dev, "SoSSI version %d.%d initialized\n",
+               l >> 16, l & 0xffff);
+
+       l = sossi_read_reg(SOSSI_INIT1_REG);
+       l |= (1 << 19); /* DMA_MODE */
+       l &= ~(1 << 31); /* REORDERING */
+       sossi_write_reg(SOSSI_INIT1_REG, l);
+
+       if ((r = request_irq(INT_1610_SoSSI_MATCH, sossi_match_irq,
+                            IRQT_FALLING,
+            "sossi_match", sossi.fbdev->dev)) < 0) {
+               dev_err(sossi.fbdev->dev, "can't get SoSSI match IRQ\n");
+               goto err;
+       }
+
+       clk_disable(sossi.fck);
+       return 0;
+
+err:
+       clk_disable(sossi.fck);
+       clk_put(sossi.fck);
+       return r;
+}
+
+static void sossi_cleanup(void)
+{
+       omap_lcdc_free_dma_callback();
+       clk_put(sossi.fck);
+}
+
+struct lcd_ctrl_extif omap1_ext_if = {
+       .init                   = sossi_init,
+       .cleanup                = sossi_cleanup,
+       .get_clk_info           = sossi_get_clk_info,
+       .convert_timings        = sossi_convert_timings,
+       .set_timings            = sossi_set_timings,
+       .set_bits_per_cycle     = sossi_set_bits_per_cycle,
+       .setup_tearsync         = sossi_setup_tearsync,
+       .enable_tearsync        = sossi_enable_tearsync,
+       .write_command          = sossi_write_command,
+       .read_data              = sossi_read_data,
+       .write_data             = sossi_write_data,
+       .transfer_area          = sossi_transfer_area,
+
+       .max_transmit_size      = SOSSI_MAX_XMIT_BYTES,
+};
+
index be913ec871692e58fc9dfe798aa4ad0154629bd5..74b1a68b8a5bc51ad1b47c8a130b76847053bb08 100644 (file)
 #define FB_ACCEL_NV_40          46      /* nVidia Arch 40               */
 #define FB_ACCEL_XGI_VOLARI_V  47      /* XGI Volari V3XT, V5, V8      */
 #define FB_ACCEL_XGI_VOLARI_Z  48      /* XGI Volari Z7                */
+#define FB_ACCEL_OMAP1610      49      /* TI OMAP16xx                  */
 #define FB_ACCEL_NEOMAGIC_NM2070 90    /* NeoMagic NM2070              */
 #define FB_ACCEL_NEOMAGIC_NM2090 91    /* NeoMagic NM2090              */
 #define FB_ACCEL_NEOMAGIC_NM2093 92    /* NeoMagic NM2093              */