Commit 1d381b89 authored by Imre Deak's avatar Imre Deak Committed by Linus Torvalds
Browse files

OMAP: add external Epson Blizzard LCD controller support



- Adds Epson Blizzard lcd controller driver; used in Nokia Internet Tablet
  products.
Signed-off-by: default avatarTrilok Soni <soni.trilok@gmail.com>
Cc: Tony Lindgren <tony@atomide.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent aae76ef1
......@@ -50,3 +50,9 @@ config FB_OMAP_LCDC_HWA742
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.
......@@ -13,6 +13,7 @@ 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
omapfb-objs := $(objs-yy)
/*
* Epson Blizzard LCD controller driver
*
* Copyright (C) 2004-2005 Nokia Corporation
* Authors: Juha Yrjola <juha.yrjola@nokia.com>
* Imre Deak <imre.deak@nokia.com>
* YUV support: Jussi Laako <jussi.laako@nokia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <asm/arch/dma.h>
#include <asm/arch/omapfb.h>
#include <asm/arch/blizzard.h>
#include "dispc.h"
#define MODULE_NAME "blizzard"
#define BLIZZARD_REV_CODE 0x00
#define BLIZZARD_CONFIG 0x02
#define BLIZZARD_PLL_DIV 0x04
#define BLIZZARD_PLL_LOCK_RANGE 0x06
#define BLIZZARD_PLL_CLOCK_SYNTH_0 0x08
#define BLIZZARD_PLL_CLOCK_SYNTH_1 0x0a
#define BLIZZARD_PLL_MODE 0x0c
#define BLIZZARD_CLK_SRC 0x0e
#define BLIZZARD_MEM_BANK0_ACTIVATE 0x10
#define BLIZZARD_MEM_BANK0_STATUS 0x14
#define BLIZZARD_HDISP 0x2a
#define BLIZZARD_HNDP 0x2c
#define BLIZZARD_VDISP0 0x2e
#define BLIZZARD_VDISP1 0x30
#define BLIZZARD_VNDP 0x32
#define BLIZZARD_HSW 0x34
#define BLIZZARD_VSW 0x38
#define BLIZZARD_DISPLAY_MODE 0x68
#define BLIZZARD_INPUT_WIN_X_START_0 0x6c
#define BLIZZARD_DATA_SOURCE_SELECT 0x8e
#define BLIZZARD_DISP_MEM_DATA_PORT 0x90
#define BLIZZARD_DISP_MEM_READ_ADDR0 0x92
#define BLIZZARD_POWER_SAVE 0xE6
#define BLIZZARD_NDISP_CTRL_STATUS 0xE8
/* Data source select */
/* For S1D13745 */
#define BLIZZARD_SRC_WRITE_LCD_BACKGROUND 0x00
#define BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE 0x01
#define BLIZZARD_SRC_WRITE_OVERLAY_ENABLE 0x04
#define BLIZZARD_SRC_DISABLE_OVERLAY 0x05
/* For S1D13744 */
#define BLIZZARD_SRC_WRITE_LCD 0x00
#define BLIZZARD_SRC_BLT_LCD 0x06
#define BLIZZARD_COLOR_RGB565 0x01
#define BLIZZARD_COLOR_YUV420 0x09
#define BLIZZARD_VERSION_S1D13745 0x01 /* Hailstorm */
#define BLIZZARD_VERSION_S1D13744 0x02 /* Blizzard */
#define BLIZZARD_AUTO_UPDATE_TIME (HZ / 20)
/* Reserve 4 request slots for requests in irq context */
#define REQ_POOL_SIZE 24
#define IRQ_REQ_POOL_SIZE 4
#define REQ_FROM_IRQ_POOL 0x01
#define REQ_COMPLETE 0
#define REQ_PENDING 1
struct blizzard_reg_list {
int start;
int end;
};
/* These need to be saved / restored separately from the rest. */
static struct blizzard_reg_list blizzard_pll_regs[] = {
{
.start = 0x04, /* Don't save PLL ctrl (0x0C) */
.end = 0x0a,
},
{
.start = 0x0e, /* Clock configuration */
.end = 0x0e,
},
};
static struct blizzard_reg_list blizzard_gen_regs[] = {
{
.start = 0x18, /* SDRAM control */
.end = 0x20,
},
{
.start = 0x28, /* LCD Panel configuration */
.end = 0x5a, /* HSSI interface, TV configuration */
},
};
static u8 blizzard_reg_cache[0x5a / 2];
struct update_param {
int plane;
int x, y, width, height;
int out_x, out_y;
int out_width, out_height;
int color_mode;
int bpp;
int flags;
};
struct blizzard_request {
struct list_head entry;
unsigned int flags;
int (*handler)(struct blizzard_request *req);
void (*complete)(void *data);
void *complete_data;
union {
struct update_param update;
struct completion *sync;
} par;
};
struct plane_info {
unsigned long offset;
int pos_x, pos_y;
int width, height;
int out_width, out_height;
int scr_width;
int color_mode;
int bpp;
};
struct blizzard_struct {
enum omapfb_update_mode update_mode;
enum omapfb_update_mode update_mode_before_suspend;
struct timer_list auto_update_timer;
int stop_auto_update;
struct omapfb_update_window auto_update_window;
int enabled_planes;
int vid_nonstd_color;
int vid_scaled;
int last_color_mode;
int zoom_on;
int screen_width;
int screen_height;
unsigned te_connected:1;
unsigned vsync_only:1;
struct plane_info plane[OMAPFB_PLANE_NUM];
struct blizzard_request req_pool[REQ_POOL_SIZE];
struct list_head pending_req_list;
struct list_head free_req_list;
struct semaphore req_sema;
spinlock_t req_lock;
unsigned long sys_ck_rate;
struct extif_timings reg_timings, lut_timings;
u32 max_transmit_size;
u32 extif_clk_period;
int extif_clk_div;
unsigned long pix_tx_time;
unsigned long line_upd_time;
struct omapfb_device *fbdev;
struct lcd_ctrl_extif *extif;
struct lcd_ctrl *int_ctrl;
void (*power_up)(struct device *dev);
void (*power_down)(struct device *dev);
int version;
} blizzard;
struct lcd_ctrl blizzard_ctrl;
static u8 blizzard_read_reg(u8 reg)
{
u8 data;
blizzard.extif->set_bits_per_cycle(8);
blizzard.extif->write_command(&reg, 1);
blizzard.extif->read_data(&data, 1);
return data;
}
static void blizzard_write_reg(u8 reg, u8 val)
{
blizzard.extif->set_bits_per_cycle(8);
blizzard.extif->write_command(&reg, 1);
blizzard.extif->write_data(&val, 1);
}
static void blizzard_restart_sdram(void)
{
unsigned long tmo;
blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0);
udelay(50);
blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 1);
tmo = jiffies + msecs_to_jiffies(200);
while (!(blizzard_read_reg(BLIZZARD_MEM_BANK0_STATUS) & 0x01)) {
if (time_after(jiffies, tmo)) {
dev_err(blizzard.fbdev->dev,
"s1d1374x: SDRAM not ready");
break;
}
msleep(1);
}
}
static void blizzard_stop_sdram(void)
{
blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0);
}
/* Wait until the last window was completely written into the controllers
* SDRAM and we can start transferring the next window.
*/
static void blizzard_wait_line_buffer(void)
{
unsigned long tmo = jiffies + msecs_to_jiffies(30);
while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 7)) {
if (time_after(jiffies, tmo)) {
if (printk_ratelimit())
dev_err(blizzard.fbdev->dev,
"s1d1374x: line buffer not ready\n");
break;
}
}
}
/* Wait until the YYC color space converter is idle. */
static void blizzard_wait_yyc(void)
{
unsigned long tmo = jiffies + msecs_to_jiffies(30);
while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 4)) {
if (time_after(jiffies, tmo)) {
if (printk_ratelimit())
dev_err(blizzard.fbdev->dev,
"s1d1374x: YYC not ready\n");
break;
}
}
}
static void disable_overlay(void)
{
blizzard_write_reg(BLIZZARD_DATA_SOURCE_SELECT,
BLIZZARD_SRC_DISABLE_OVERLAY);
}
static void set_window_regs(int x_start, int y_start, int x_end, int y_end,
int x_out_start, int y_out_start,
int x_out_end, int y_out_end, int color_mode,
int zoom_off, int flags)
{
u8 tmp[18];
u8 cmd;
x_end--;
y_end--;
tmp[0] = x_start;
tmp[1] = x_start >> 8;
tmp[2] = y_start;
tmp[3] = y_start >> 8;
tmp[4] = x_end;
tmp[5] = x_end >> 8;
tmp[6] = y_end;
tmp[7] = y_end >> 8;
x_out_end--;
y_out_end--;
tmp[8] = x_out_start;
tmp[9] = x_out_start >> 8;
tmp[10] = y_out_start;
tmp[11] = y_out_start >> 8;
tmp[12] = x_out_end;
tmp[13] = x_out_end >> 8;
tmp[14] = y_out_end;
tmp[15] = y_out_end >> 8;
tmp[16] = color_mode;
if (zoom_off && blizzard.version == BLIZZARD_VERSION_S1D13745)
tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND;
else if (flags & OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY)
tmp[17] = BLIZZARD_SRC_WRITE_OVERLAY_ENABLE;
else
tmp[17] = blizzard.version == BLIZZARD_VERSION_S1D13744 ?
BLIZZARD_SRC_WRITE_LCD :
BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE;
blizzard.extif->set_bits_per_cycle(8);
cmd = BLIZZARD_INPUT_WIN_X_START_0;
blizzard.extif->write_command(&cmd, 1);
blizzard.extif->write_data(tmp, 18);
}
static void enable_tearsync(int y, int width, int height, int screen_height,
int out_height, int force_vsync)
{
u8 b;
b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS);
b |= 1 << 3;
blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b);
if (likely(blizzard.vsync_only || force_vsync)) {
blizzard.extif->enable_tearsync(1, 0);
return;
}
if (width * blizzard.pix_tx_time < blizzard.line_upd_time) {
blizzard.extif->enable_tearsync(1, 0);
return;
}
if ((width * blizzard.pix_tx_time / 1000) * height <
(y + out_height) * (blizzard.line_upd_time / 1000)) {
blizzard.extif->enable_tearsync(1, 0);
return;
}
blizzard.extif->enable_tearsync(1, y + 1);
}
static void disable_tearsync(void)
{
u8 b;
blizzard.extif->enable_tearsync(0, 0);
b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS);
b &= ~(1 << 3);
blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b);
b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS);
}
static inline void set_extif_timings(const struct extif_timings *t);
static inline struct blizzard_request *alloc_req(void)
{
unsigned long flags;
struct blizzard_request *req;
int req_flags = 0;
if (!in_interrupt())
down(&blizzard.req_sema);
else
req_flags = REQ_FROM_IRQ_POOL;
spin_lock_irqsave(&blizzard.req_lock, flags);
BUG_ON(list_empty(&blizzard.free_req_list));
req = list_entry(blizzard.free_req_list.next,
struct blizzard_request, entry);
list_del(&req->entry);
spin_unlock_irqrestore(&blizzard.req_lock, flags);
INIT_LIST_HEAD(&req->entry);
req->flags = req_flags;
return req;
}
static inline void free_req(struct blizzard_request *req)
{
unsigned long flags;
spin_lock_irqsave(&blizzard.req_lock, flags);
list_del(&req->entry);
list_add(&req->entry, &blizzard.free_req_list);
if (!(req->flags & REQ_FROM_IRQ_POOL))
up(&blizzard.req_sema);
spin_unlock_irqrestore(&blizzard.req_lock, flags);
}
static void process_pending_requests(void)
{
unsigned long flags;
spin_lock_irqsave(&blizzard.req_lock, flags);
while (!list_empty(&blizzard.pending_req_list)) {
struct blizzard_request *req;
void (*complete)(void *);
void *complete_data;
req = list_entry(blizzard.pending_req_list.next,
struct blizzard_request, entry);
spin_unlock_irqrestore(&blizzard.req_lock, flags);
if (req->handler(req) == REQ_PENDING)
return;
complete = req->complete;
complete_data = req->complete_data;
free_req(req);
if (complete)
complete(complete_data);
spin_lock_irqsave(&blizzard.req_lock, flags);
}
spin_unlock_irqrestore(&blizzard.req_lock, flags);
}
static void submit_req_list(struct list_head *head)
{
unsigned long flags;
int process = 1;
spin_lock_irqsave(&blizzard.req_lock, flags);
if (likely(!list_empty(&blizzard.pending_req_list)))
process = 0;
list_splice_init(head, blizzard.pending_req_list.prev);
spin_unlock_irqrestore(&blizzard.req_lock, flags);
if (process)
process_pending_requests();
}
static void request_complete(void *data)
{
struct blizzard_request *req = (struct blizzard_request *)data;
void (*complete)(void *);
void *complete_data;
complete = req->complete;
complete_data = req->complete_data;
free_req(req);
if (complete)
complete(complete_data);
process_pending_requests();
}
static int do_full_screen_update(struct blizzard_request *req)
{
int i;
int flags;
for (i = 0; i < 3; i++) {
struct plane_info *p = &blizzard.plane[i];
if (!(blizzard.enabled_planes & (1 << i))) {
blizzard.int_ctrl->enable_plane(i, 0);
continue;
}
dev_dbg(blizzard.fbdev->dev, "pw %d ph %d\n",
p->width, p->height);
blizzard.int_ctrl->setup_plane(i,
OMAPFB_CHANNEL_OUT_LCD, p->offset,
p->scr_width, p->pos_x, p->pos_y,
p->width, p->height,
p->color_mode);
blizzard.int_ctrl->enable_plane(i, 1);
}
dev_dbg(blizzard.fbdev->dev, "sw %d sh %d\n",
blizzard.screen_width, blizzard.screen_height);
blizzard_wait_line_buffer();
flags = req->par.update.flags;
if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC)
enable_tearsync(0, blizzard.screen_width,
blizzard.screen_height,
blizzard.screen_height,
blizzard.screen_height,
flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC);
else
disable_tearsync();
set_window_regs(0, 0, blizzard.screen_width, blizzard.screen_height,
0, 0, blizzard.screen_width, blizzard.screen_height,
BLIZZARD_COLOR_RGB565, blizzard.zoom_on, flags);
blizzard.zoom_on = 0;
blizzard.extif->set_bits_per_cycle(16);
/* set_window_regs has left the register index at the right
* place, so no need to set it here.
*/
blizzard.extif->transfer_area(blizzard.screen_width,
blizzard.screen_height,
request_complete, req);
return REQ_PENDING;
}
/* Setup all planes with an overlapping area with the update window. */
static int do_partial_update(struct blizzard_request *req, int plane,
int x, int y, int w, int h,
int x_out, int y_out, int w_out, int h_out,
int wnd_color_mode, int bpp)
{
int i;
int gx1, gy1, gx2, gy2;
int gx1_out, gy1_out, gx2_out, gy2_out;
int color_mode;
int flags;
int zoom_off;
/* Global coordinates, relative to pixel 0,0 of the LCD */
gx1 = x + blizzard.plane[plane].pos_x;
gy1 = y + blizzard.plane[plane].pos_y;
gx2 = gx1 + w;
gy2 = gy1 + h;
flags = req->par.update.flags;
if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) {
gx1_out = gx1;
gy1_out = gy1;
gx2_out = gx1 + w * 2;
gy2_out = gy1 + h * 2;
} else {
gx1_out = x_out + blizzard.plane[plane].pos_x;
gy1_out = y_out + blizzard.plane[plane].pos_y;
gx2_out = gx1_out + w_out;
gy2_out = gy1_out + h_out;
}
zoom_off = blizzard.zoom_on && gx1 == 0 && gy1 == 0 &&
w == blizzard.screen_width && h == blizzard.screen_height;
blizzard.zoom_on = (!zoom_off && blizzard.zoom_on) ||
(w < w_out || h < h_out);
for (i = 0; i < OMAPFB_PLANE_NUM; i++) {
struct plane_info *p = &blizzard.plane[i];
int px1, py1;
int px2, py2;
int pw, ph;
int pposx, pposy;
unsigned long offset;
if (!(blizzard.enabled_planes & (1 << i)) ||
(wnd_color_mode && i != plane)) {
blizzard.int_ctrl->enable_plane(i, 0);
continue;
}
/* Plane coordinates */
if (i == plane) {
/* Plane in which we are doing the update.
* Local coordinates are the one in the update
* request.
*/
px1 = x;
py1 = y;
px2 = x + w;
py2 = y + h;
pposx = 0;
pposy = 0;
} else {
/* Check if this plane has an overlapping part */
px1 = gx1 - p->pos_x;
py1 = gy1 - p->pos_y;
px2 = gx2 - p->pos_x;
py2 = gy2 - p->pos_y;
if (px1 >= p->width || py1 >= p->height ||
px2 <= 0 || py2 <= 0) {
blizzard.int_ctrl->enable_plane(i, 0);
continue;
}
/* Calculate the coordinates for the overlapping
* part in the plane's local coordinates.
*/
pposx = -px1;
pposy = -py1;
if (px1 < 0)
px1 = 0;
if (py1 < 0)
py1 = 0;
if (px2 > p->width)
px2 = p->width;
if (py2 > p->height)