From: Imre Deak Date: Tue, 3 Apr 2007 18:15:01 +0000 (-0400) Subject: FB: Add support for OMAP framebuffer X-Git-Tag: v2.6.21-omap1~32^2~12 X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=04aab505f26f6c8afe8940617add2f8defd48a27;p=pandora-kernel.git FB: Add support for OMAP framebuffer Add support for OMAP framebuffer Signed-off-by: Imre Deak --- diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e4f0dd00ae85..d4704faa820d 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -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 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 760305c8a841..705c5b5d5d39 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -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 index 000000000000..78123a25a333 --- /dev/null +++ b/drivers/video/omap/Kconfig @@ -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 index 000000000000..be2580177995 --- /dev/null +++ b/drivers/video/omap/Makefile @@ -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 index 000000000000..7905d132b3fd --- /dev/null +++ b/drivers/video/omap/blizzard.c @@ -0,0 +1,1570 @@ +/* + * File: drivers/video/omap/blizzard.c + * + * Epson Blizzard LCD controller driver + * + * Copyright (C) 2004-2005 Nokia Corporation + * Authors: Juha Yrjola + * Imre Deak + * YUV support: Jussi Laako + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#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(®, 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(®, 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 = ∁ + + 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 index 000000000000..5eeaddb3075c --- /dev/null +++ b/drivers/video/omap/dispc.c @@ -0,0 +1,1512 @@ +/* + * File: drivers/video/omap/omap2/dispc.c + * + * OMAP2 display controller support + * + * Copyright (C) 2005 Nokia Corporation + * Author: Imre Deak + * + * 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 +#include +#include +#include + +#include + +#include +#include +#include + +#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, ®ion->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 index 000000000000..eb1512b56ce8 --- /dev/null +++ b/drivers/video/omap/dispc.h @@ -0,0 +1,43 @@ +#ifndef _DISPC_H +#define _DISPC_H + +#include + +#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 index 000000000000..5d92f593d44f --- /dev/null +++ b/drivers/video/omap/hwa742.c @@ -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ä + * Imre Deak + * YUV support: Jussi Laako + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#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(®, 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(®, 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 = ∁ + + 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 index 000000000000..3476689467d3 --- /dev/null +++ b/drivers/video/omap/lcd_ams_delta.c @@ -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 + * + * 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 +#include + +#include +#include + +#include +#include +#include + +#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); Reading git-diff-tree failed