1658 lines
40 KiB
C
1658 lines
40 KiB
C
/*
|
|
* Disclaimer (blabla)
|
|
*
|
|
* Author: Manoël Trapier <manoelt@bookeen.com>
|
|
* Copyright (c) 2003-2010 Bookeen
|
|
*
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <asm/hardware.h>
|
|
#include <asm/delay.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm-arm/irq.h>
|
|
#include <asm/arch/gpio.h>
|
|
#include <asm/arch/regs-gpio.h>
|
|
#include <asm/arch/regs-irq.h>
|
|
#include <asm/arch/regs-lcd.h>
|
|
#include <asm-arm/arch-s3c2410/irqs.h>
|
|
#include <asm-arm/arch-s3c2410/gpio.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/clk.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <linux/ioctl.h>
|
|
#include <linux/auofb_ioctl.h>
|
|
|
|
#include <cybook.h>
|
|
|
|
#define DEBUG_MESSAGES
|
|
//#define DEBUG_TRACEFUNC
|
|
#define VERBOSE_LEVEL INFO_VERBOSE
|
|
//#define DEBUG_SPI
|
|
|
|
#define MODULE_NAME "AUO-TCON"
|
|
|
|
#define BUSY_WAIT_TIMEOUT (40*5*2*2) //panel time out = 1s
|
|
#define SYS_WR_CON (1<<6)
|
|
#define SYS_OE_CON (1<<7)
|
|
|
|
// ===========================================================================
|
|
#undef MSG
|
|
#undef DBG
|
|
|
|
#ifdef DEBUG_MESSAGES
|
|
|
|
enum InfoLevel
|
|
{
|
|
INFO_ERROR = 0,
|
|
INFO_WARNING,
|
|
INFO_NORMAL,
|
|
INFO_VERBOSE,
|
|
INFO_DEBUG,
|
|
};
|
|
|
|
# ifndef VERBOSE_LEVEL
|
|
# define VERBOSE_LEVEL INFO_WARNING
|
|
# endif
|
|
|
|
# ifdef DEBUG_TRACEFUNC
|
|
|
|
static int _dbg_FunctionLevel = 0;
|
|
|
|
# define MSG(str) {\
|
|
int __i;\
|
|
printk(KERN_ALERT "+");\
|
|
for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
|
|
printk("-");\
|
|
printk("||" str "\n");\
|
|
}
|
|
|
|
# define DBG(str, ...) {\
|
|
int __i;\
|
|
printk(KERN_ALERT "+");\
|
|
for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
|
|
printk("-");\
|
|
printk("||" str "\n", __VA_ARGS__);\
|
|
}
|
|
|
|
# define INFOL(level, s) do {\
|
|
if (level <= VERBOSE_LEVEL) {\
|
|
int __i;\
|
|
printk(KERN_ALERT "+");\
|
|
for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
|
|
printk("-");\
|
|
printk("<%d>%s:%s(): ", level, __FILE__, __func__); printk s; printk("\n");\
|
|
}\
|
|
} while(0)
|
|
|
|
# define FUNC_IN() {\
|
|
int __i;\
|
|
_dbg_FunctionLevel++;\
|
|
printk(KERN_ALERT "+");\
|
|
for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
|
|
printk("-");\
|
|
printk(">> %s() >>\n", __func__);\
|
|
}
|
|
|
|
# define FUNC_OUT() {\
|
|
int __i;\
|
|
printk(KERN_ALERT "+");\
|
|
for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
|
|
printk("-");\
|
|
printk("<< %s() <<\n", __func__);\
|
|
_dbg_FunctionLevel--;\
|
|
}
|
|
|
|
# define FUNC_OUTR(val) {\
|
|
int __i;\
|
|
printk(KERN_ALERT "+");\
|
|
for (__i = 0; __i < _dbg_FunctionLevel; __i++)\
|
|
printk("-");\
|
|
printk("<< %s() = %d <<\n", __func__, val);\
|
|
_dbg_FunctionLevel--;\
|
|
}
|
|
# else /* DEBUG_TRACEFUNC */
|
|
|
|
# define MSG(str) do {\
|
|
printk(KERN_ALERT MODULE_NAME ": " str "\n");\
|
|
} while(0)
|
|
|
|
# define DBG(str, ...) do {\
|
|
printk(KERN_ALERT MODULE_NAME ": " str "\n", __VA_ARGS__);\
|
|
} while(0)
|
|
# define FUNC_IN() do {\
|
|
} while(0)
|
|
|
|
# define FUNC_OUT() do {\
|
|
} while(0)
|
|
|
|
# define FUNC_OUTR(val) do {\
|
|
printk(KERN_ALERT MODULE_NAME ": %s() return %d\n", __func__, val);\
|
|
} while(0)
|
|
|
|
# define INFOL(level, s) do {\
|
|
if (level <= VERBOSE_LEVEL) {\
|
|
printk("<%d>%s:%s(): ", level, __FILE__, __func__); printk s; printk("\n");\
|
|
}\
|
|
} while(0)
|
|
|
|
# endif /* DEBUG_TRACEFUNC */
|
|
#else /* DEBUG_MESSAGES */
|
|
# define MSG(str)
|
|
# define DBG(str, ...)
|
|
# define FUNC_IN()
|
|
# define FUNC_OUT()
|
|
# define FUNC_OUTR(val)
|
|
# define INFOL(level, s)
|
|
# define INFO(s)
|
|
#endif /* DEBUG_MESSAGES */
|
|
|
|
// ===========================================================================
|
|
#define VIDCON0_S_CPU_IF_MAIN (2<<22)
|
|
#define VIDCON0_S_RGB_PAR (0<<13)
|
|
#define VIDCON0_S_VCLK_GATING_OFF (1<<5)
|
|
#define VIDCON0_S_CLKDIR_DIVIDED (1<<4)
|
|
#define VIDCON0_S_CLKSEL_HCLK (0<<2)
|
|
#define VIDCON0_CLKVAL_F_SHIFT (6)
|
|
|
|
#define VIDTCON2_LINEVAL_S (11)
|
|
|
|
static int tcon_inPortraitMode = 0;
|
|
static int tcon_lastModeWas = 0;
|
|
|
|
typedef enum Tcon_Speedclasses
|
|
{
|
|
EN_I80_NONE = -1,
|
|
EN_I80_LANDSCAPE = 0,
|
|
EN_I80_PORTRAIT,
|
|
EN_I80_LANDSCAPE_HANDWRITING,
|
|
EN_I80_PORTRAIT_HANDWRITING,
|
|
} Tcon_Speedclasses;
|
|
|
|
typedef struct Tcon_SpeedclasseValue
|
|
{
|
|
u8 cs_setup;
|
|
u8 wr_setup;
|
|
u8 wr_act;
|
|
u8 wr_hold;
|
|
} Tcon_SpeedclasseValue;
|
|
|
|
typedef enum Tcon_PowerMode
|
|
{
|
|
TCON_POWER_NORMALMODE,
|
|
TCON_POWER_STANDBYMODE,
|
|
TCON_POWER_SLEEPMODE,
|
|
TCON_POWER_OFF,
|
|
} Tcon_PowerMode;
|
|
|
|
static Tcon_SpeedclasseValue tcon_speedtable[] ={
|
|
[EN_I80_LANDSCAPE] =
|
|
{
|
|
.cs_setup = 0,
|
|
.wr_setup = 0,
|
|
.wr_act = 1,
|
|
.wr_hold = 0,
|
|
},
|
|
[EN_I80_PORTRAIT] =
|
|
{
|
|
.cs_setup = 0,
|
|
.wr_setup = 3,
|
|
.wr_act = 8,
|
|
.wr_hold = 3,
|
|
},
|
|
#if 1
|
|
[EN_I80_LANDSCAPE_HANDWRITING] =
|
|
{
|
|
.cs_setup = 0,
|
|
.wr_setup = 1,
|
|
.wr_act = 1,
|
|
.wr_hold = 0,
|
|
},
|
|
[EN_I80_PORTRAIT_HANDWRITING] =
|
|
{
|
|
.cs_setup = 0,
|
|
.wr_setup = 2,
|
|
.wr_act = 6,
|
|
.wr_hold = 2,
|
|
},
|
|
#else
|
|
[EN_I80_PORTRAIT_HANDWRITING] =
|
|
{
|
|
.cs_setup = 0,
|
|
.wr_setup = 1,
|
|
.wr_act = 1,
|
|
.wr_hold = 0,
|
|
},
|
|
[EN_I80_LANDSCAPE_HANDWRITING] =
|
|
{
|
|
.cs_setup = 0,
|
|
.wr_setup = 2,
|
|
.wr_act = 6,
|
|
.wr_hold = 2,
|
|
},
|
|
#endif
|
|
};
|
|
|
|
static struct proc_dir_entry *epaperProcEntry;
|
|
|
|
static Tcon_Speedclasses tcon_currentSpeedClass = EN_I80_NONE;
|
|
static Tcon_PowerMode tcon_currentPowerMode = TCON_POWER_NORMALMODE;
|
|
static unsigned short tcon_currentVersion = 0;
|
|
|
|
void tcon_i80bus_set_speed (Tcon_Speedclasses i80_speed,
|
|
unsigned short display_w,
|
|
unsigned short display_h,
|
|
unsigned char set_clock);
|
|
|
|
static int tcon_command (sAUOCommand *cmd, char userland);
|
|
static int tcon_send_command_start (sAUOCommand *cmd);
|
|
static int tcon_send_command_end (sAUOCommand *cmd);
|
|
|
|
// ===========================================================================
|
|
// TCON Related functions
|
|
// ===========================================================================
|
|
|
|
/*
|
|
1. loops = 1 --> 25ns
|
|
2. loops = 75 --> 1 us
|
|
3. loops = 100000 --> 1ms
|
|
*/
|
|
static inline void tcon_ndelay (unsigned long loops) //in ns
|
|
{
|
|
__asm__ volatile ("1:\n" "subs %0, %1, #1\n" "bne 1b" : "=r" (loops) : "0"(loops));
|
|
}
|
|
|
|
static inline void tcon_delay (unsigned long uTime)
|
|
{
|
|
unsigned long cnt = 0;
|
|
|
|
for ( cnt = 0; cnt < uTime; cnt++ )
|
|
tcon_ndelay(75);
|
|
}
|
|
|
|
static int tcon_wait_ready (void)
|
|
{
|
|
unsigned long tmp;
|
|
int iBusyCnt = 0;
|
|
|
|
wait_queue_head_t busy_wq;
|
|
init_waitqueue_head(&busy_wq);
|
|
|
|
tmp = __raw_readl(S3C2410_GPBDAT);
|
|
while ( (tmp & (1 << 2)) == 0 )
|
|
{
|
|
sleep_on_timeout(&busy_wq, 4);
|
|
|
|
iBusyCnt++;
|
|
if ( iBusyCnt >= BUSY_WAIT_TIMEOUT )
|
|
{
|
|
INFOL(INFO_WARNING, ("TCON Wait timedout!!!"));
|
|
return -EIO;
|
|
}
|
|
tmp = __raw_readl(S3C2410_GPBDAT);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* On the i80 port,
|
|
* i80 TCON
|
|
* -----------
|
|
* RS -> D/C
|
|
* CS0 -> CSEL
|
|
* nWE -> HWE
|
|
* OE -> HRD
|
|
*/
|
|
static inline void tcon_i80bus_init_interface (void)
|
|
{
|
|
unsigned long tmp;
|
|
FUNC_IN();
|
|
tmp = __raw_readl(S3C_SYSIFCON0); //polarity of RS, set 1 for normal access
|
|
tmp |= (1 << 2);
|
|
__raw_writel(tmp, S3C_SYSIFCON0);
|
|
|
|
tmp = __raw_readl(S3C_SIFCCON0); // command mode enable
|
|
tmp |= (1 << 0);
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
FUNC_OUT();
|
|
}
|
|
|
|
static inline void tcon_i80bus_deinit_interface (void)
|
|
{
|
|
unsigned long tmp;
|
|
FUNC_IN();
|
|
tmp = __raw_readl(S3C_SIFCCON0); // command mode disable
|
|
tmp &= ~(1 << 0);
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
FUNC_OUT();
|
|
}
|
|
|
|
void tcon_i80bus_set_speed (Tcon_Speedclasses i80_speed,
|
|
unsigned short display_w,
|
|
unsigned short display_h,
|
|
unsigned char set_clock)
|
|
{
|
|
unsigned long tmp;
|
|
unsigned char clkval = 0;
|
|
unsigned short h_cnt, v_cnt;
|
|
unsigned long vclk;
|
|
struct clk *lcd_clock;
|
|
int HCLK;
|
|
|
|
FUNC_IN();
|
|
|
|
if ( tcon_currentSpeedClass == EN_I80_NONE )
|
|
set_clock = true;
|
|
|
|
tcon_currentSpeedClass = i80_speed;
|
|
|
|
if ( set_clock == true )
|
|
{
|
|
INFOL(INFO_DEBUG, ("Will set clocks..."));
|
|
|
|
tmp = __raw_readl(S3C_VIDCON0);
|
|
tmp = VIDCON0_S_CPU_IF_MAIN | VIDCON0_S_RGB_PAR | \
|
|
VIDCON0_S_VCLK_GATING_OFF | \
|
|
VIDCON0_S_CLKDIR_DIVIDED | VIDCON0_S_CLKSEL_HCLK;
|
|
__raw_writel(tmp, S3C_VIDCON0);
|
|
|
|
v_cnt = display_h;
|
|
h_cnt = display_w;
|
|
|
|
lcd_clock = clk_get(NULL, "hclk");
|
|
HCLK = clk_get_rate(lcd_clock);
|
|
//printk(KERN_ERR "-------- HCLK: %d --------", HCLK);
|
|
clkval = (unsigned int)(HCLK / (display_w * display_h * 50 /* FPS ???? */));
|
|
|
|
vclk = (HCLK / (clkval + 1)) / 1000;
|
|
|
|
tmp = __raw_readl(S3C_VIDCON0);
|
|
tmp |= (clkval << VIDCON0_CLKVAL_F_SHIFT);
|
|
__raw_writel(tmp, S3C_VIDCON0);
|
|
}
|
|
|
|
tmp = __raw_readl(S3C_SYSIFCON0);
|
|
tmp = (tcon_speedtable[i80_speed].cs_setup << 16) |
|
|
(tcon_speedtable[i80_speed].wr_setup << 12) |
|
|
(tcon_speedtable[i80_speed].wr_act << 8) |
|
|
(tcon_speedtable[i80_speed].wr_hold << 4) |
|
|
(1 << 2) | (1 << 1) | (1);
|
|
__raw_writel(tmp, S3C_SYSIFCON0);
|
|
|
|
tmp = __raw_readl(S3C_SYSIFCON1);
|
|
tmp = (tcon_speedtable[i80_speed].cs_setup << 16) |
|
|
(tcon_speedtable[i80_speed].wr_setup << 12) |
|
|
(tcon_speedtable[i80_speed].wr_act << 8) |
|
|
(tcon_speedtable[i80_speed].wr_hold << 4) |
|
|
(1 << 2) | (1 << 1) | (1);
|
|
__raw_writel(tmp, S3C_SYSIFCON1);
|
|
|
|
tmp = __raw_readl(S3C_VIDTCON2);
|
|
tmp = ((display_h - 1) << VIDTCON2_LINEVAL_S) | ((display_w / 4) - 1);
|
|
__raw_writel(tmp, S3C_VIDTCON2);
|
|
|
|
FUNC_OUT();
|
|
}
|
|
|
|
static inline void tcon_i80bus_write (int data)
|
|
{
|
|
int tmp;
|
|
|
|
if ( (tcon_lastModeWas == 4) && (!tcon_inPortraitMode) )
|
|
tcon_delay(1);
|
|
|
|
tmp = __raw_readl(S3C_SIFCCON0); // nWE enable
|
|
tmp |= SYS_WR_CON;
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
|
|
if ( (!tcon_inPortraitMode) && (tcon_lastModeWas != 4) )
|
|
{
|
|
tcon_ndelay(25);
|
|
}
|
|
else if ( tcon_lastModeWas == 4 )
|
|
{
|
|
if ( tcon_inPortraitMode )
|
|
tcon_ndelay(15);
|
|
else
|
|
tcon_delay(1);
|
|
}
|
|
|
|
__raw_writel(data, S3C_SIFCCON1); //rSIFCCON1 = CMD;
|
|
|
|
tmp = __raw_readl(S3C_SIFCCON0); // nWE disables
|
|
tmp &= ~SYS_WR_CON;
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
}
|
|
|
|
static inline void tcon_i80bus_write_LUT (int data)
|
|
{
|
|
int tmp;
|
|
|
|
tcon_delay(1);
|
|
tmp = __raw_readl(S3C_SIFCCON0); // nWE enable
|
|
tmp |= SYS_WR_CON;
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
|
|
tcon_delay(1);
|
|
|
|
__raw_writel(data, S3C_SIFCCON1); //rSIFCCON1 = CMD;
|
|
|
|
tcon_delay(1);
|
|
|
|
tmp = __raw_readl(S3C_SIFCCON0); // nWE disable
|
|
tmp &= ~SYS_WR_CON;
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
|
|
|
|
}
|
|
|
|
static inline void tcon_i80bus_read (unsigned short *data)
|
|
{
|
|
int tmp;
|
|
|
|
tcon_delay(1);
|
|
|
|
tmp = __raw_readl(S3C_SIFCCON0); // nRD enable
|
|
tmp |= SYS_OE_CON;
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
|
|
tcon_delay(1);
|
|
|
|
*data = __raw_readl(S3C_SIFCCON2); //CMD = rSIFCCON2;
|
|
|
|
tcon_delay(1);
|
|
|
|
tmp = __raw_readl(S3C_SIFCCON0); // nRD disable
|
|
tmp &= ~SYS_OE_CON;
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
}
|
|
|
|
static inline void tcon_set_write_to_data (void)
|
|
{
|
|
unsigned long tmp;
|
|
//FUNC_IN();
|
|
tmp = __raw_readl(S3C_SIFCCON0);
|
|
// RS high -> D/nC set Data mode
|
|
tmp |= (1 << 1);
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
|
|
tcon_delay(1);
|
|
//FUNC_OUT();
|
|
}
|
|
|
|
static inline void tcon_set_write_to_command (void)
|
|
{
|
|
unsigned long tmp;
|
|
//FUNC_IN();
|
|
tmp = __raw_readl(S3C_SIFCCON0);
|
|
// RS low -> D/nC set Command mode
|
|
tmp &= ~(1 << 1);
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
|
|
tcon_delay(1);
|
|
//FUNC_OUT();
|
|
}
|
|
|
|
static inline void tcon_enable_write (void)
|
|
{
|
|
unsigned long tmp;
|
|
FUNC_IN();
|
|
tmp = __raw_readl(S3C_SIFCCON0);
|
|
// nWE -> HWE enable
|
|
tmp |= (1 << 6);
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
|
|
tcon_delay(1);
|
|
FUNC_OUT();
|
|
}
|
|
|
|
static inline void tcon_disable_write (void)
|
|
{
|
|
unsigned long tmp;
|
|
FUNC_IN();
|
|
tmp = __raw_readl(S3C_SIFCCON0);
|
|
// nWE -> HWE disable
|
|
tmp &= ~(1 << 6);
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
tcon_delay(1);
|
|
FUNC_OUT();
|
|
}
|
|
|
|
static inline void tcon_enable_read (void)
|
|
{
|
|
unsigned long tmp;
|
|
FUNC_IN();
|
|
tmp = __raw_readl(S3C_SIFCCON0); // nRD enable
|
|
tmp |= SYS_OE_CON;
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
tcon_delay(1);
|
|
FUNC_OUT();
|
|
}
|
|
|
|
static inline void tcon_disable_read (void)
|
|
{
|
|
unsigned long tmp;
|
|
FUNC_IN();
|
|
tmp = __raw_readl(S3C_SIFCCON0); // nRD disable
|
|
tmp &= ~SYS_OE_CON;
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
tcon_delay(1);
|
|
FUNC_OUT();
|
|
}
|
|
|
|
static inline void tcon_select_chip (void)
|
|
{
|
|
unsigned long tmp;
|
|
FUNC_IN();
|
|
tmp = __raw_readl(S3C_SIFCCON0); // Chip Select
|
|
tmp |= (1 << 8);
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
tcon_delay(1);
|
|
FUNC_OUT();
|
|
}
|
|
|
|
static inline void tcon_unselect_chip (void)
|
|
{
|
|
unsigned long tmp;
|
|
FUNC_IN();
|
|
tmp = __raw_readl(S3C_SIFCCON0); // nCS0(Main) enable
|
|
tmp &= ~(1 << 8);
|
|
__raw_writel(tmp, S3C_SIFCCON0);
|
|
tcon_delay(1);
|
|
FUNC_OUT();
|
|
}
|
|
|
|
static inline void tcon_goto_sleep (void)
|
|
{
|
|
/* Set GPIO accordingly */
|
|
unsigned long tmp = __raw_readl(S3C2410_GPBDAT);
|
|
tmp &= ~(1 << 1); //Set SLP_N to low
|
|
__raw_writel(tmp, S3C2410_GPBDAT);
|
|
msleep(10);
|
|
}
|
|
|
|
static inline void tcon_wakeup_sleep (void)
|
|
{
|
|
unsigned long tmp = __raw_readl(S3C2410_GPBDAT);
|
|
tmp |= (1 << 1); //Set SLP_N to high
|
|
__raw_writel(tmp, S3C2410_GPBDAT);
|
|
msleep(10);
|
|
}
|
|
|
|
static inline void tcon_reset (void)
|
|
{
|
|
// LCD module reset
|
|
unsigned long tmp = __raw_readl(S3C2410_GPDDAT);
|
|
tmp |= (1 << 9);
|
|
__raw_writel(tmp, S3C2410_GPDDAT);
|
|
|
|
tmp = __raw_readl(S3C2410_GPDDAT); // RST_N goes to LOW
|
|
tmp &= ~(1 << 9);
|
|
__raw_writel(tmp, S3C2410_GPDDAT);
|
|
|
|
tcon_delay(5);
|
|
|
|
tmp = __raw_readl(S3C2410_GPDDAT); // RST_N goes to HIGH
|
|
tmp |= (1 << 9);
|
|
__raw_writel(tmp, S3C2410_GPDDAT);
|
|
|
|
// delay about 10ms
|
|
msleep(10);
|
|
|
|
tcon_i80bus_set_speed(EN_I80_PORTRAIT, 800, 600, false);
|
|
|
|
/* Reseting the TCON will set the tcon in Level0 power mode */
|
|
tcon_currentPowerMode = TCON_POWER_NORMALMODE;
|
|
}
|
|
|
|
static inline void tcon_set_power_on (void)
|
|
{
|
|
unsigned long tmp;
|
|
tcon_i80bus_set_speed(EN_I80_LANDSCAPE, 800, 600, true);
|
|
|
|
// POWER pin config
|
|
tmp = __raw_readl(S3C2410_GPBCON);
|
|
tmp = (tmp & ~(3 << 6)) | (1 << 6);
|
|
__raw_writel(tmp, S3C2410_GPBCON);
|
|
|
|
// BUSY pin config
|
|
tmp = __raw_readl(S3C2410_GPBCON);
|
|
tmp = (tmp & ~(3 << 4));
|
|
__raw_writel(tmp, S3C2410_GPBCON);
|
|
|
|
// SLEEP pin config
|
|
tmp = __raw_readl(S3C2410_GPBCON);
|
|
tmp = (tmp & ~(3 << 2)) | (1 << 2);
|
|
__raw_writel(tmp, S3C2410_GPBCON);
|
|
|
|
msleep(1);
|
|
|
|
// Panel power on
|
|
tmp = __raw_readl(S3C2410_GPBDAT);
|
|
tmp |= (1 << 3);
|
|
//SLP_N high
|
|
tmp |= (1 << 1);
|
|
__raw_writel(tmp, S3C2410_GPBDAT);
|
|
|
|
msleep(100);
|
|
|
|
tcon_reset();
|
|
|
|
tcon_currentPowerMode = TCON_POWER_NORMALMODE;
|
|
}
|
|
|
|
static inline int tcon_set_power_off (void)
|
|
{
|
|
unsigned long tmp;
|
|
sAUOCommand cmd;
|
|
|
|
switch ( tcon_currentPowerMode )
|
|
{
|
|
default:
|
|
return -EBADF; /* Invalide power mode, can't shutdown... */
|
|
break;
|
|
|
|
case TCON_POWER_NORMALMODE:
|
|
cmd.cmd = AUOCMD_STANDBY;
|
|
tcon_command(&cmd, false);
|
|
|
|
case TCON_POWER_STANDBYMODE:
|
|
tcon_goto_sleep();
|
|
|
|
case TCON_POWER_SLEEPMODE:
|
|
/*Standby -> Off = shutdown the power pin */
|
|
tmp = __raw_readl(S3C2410_GPBDAT);
|
|
tmp &= ~(1 << 3);
|
|
__raw_writel(tmp, S3C2410_GPBDAT);
|
|
|
|
case TCON_POWER_OFF:
|
|
break;
|
|
}
|
|
|
|
tcon_currentPowerMode = TCON_POWER_OFF;
|
|
return 0;
|
|
}
|
|
|
|
struct tcon_info
|
|
{
|
|
unsigned short temperature,
|
|
epd_type,
|
|
panel_size_int,
|
|
panel_size_float,
|
|
model,
|
|
lut_version,
|
|
tcon_version;
|
|
};
|
|
|
|
static void tcon_get_info (struct tcon_info *info)
|
|
{
|
|
sAUOCommand cmd;
|
|
unsigned short buf[4] = { 0, 0, 0, 0 };
|
|
|
|
if ( info == NULL )
|
|
return;
|
|
|
|
cmd.cmd = AUOCMD_READ_FUNC;
|
|
cmd.data = buf;
|
|
cmd.datalen = 8;
|
|
|
|
tcon_command(&cmd, false);
|
|
|
|
info->temperature = buf[0] & 0x01FF;
|
|
info->epd_type = buf[1] & 0x00FF;
|
|
info->panel_size_int = (buf[2] & 0xFC00) >> 10;
|
|
info->panel_size_float = (buf[2] & 0x03C0) >> 6;
|
|
info->model = buf[2] & 0x003F;
|
|
info->lut_version = buf[3] & 0x00FF;
|
|
info->tcon_version = (buf[3] & 0xFF00) >> 8;
|
|
|
|
tcon_currentVersion = info->tcon_version;
|
|
}
|
|
|
|
static void tcon_display_info (void)
|
|
{
|
|
struct tcon_info info;
|
|
tcon_get_info(&info);
|
|
printk("TCON FW v%d, LUT v%d, EPD {v[%d];Size[%d:%d];Model[%d]}\n",
|
|
info.tcon_version, info.lut_version,
|
|
info.epd_type, info.panel_size_int, info.panel_size_float, info.model);
|
|
if ( tcon_currentVersion != AUO_FIRMWARE_VERSION )
|
|
printk(KERN_WARNING "!!! Warning: TCON Firmware version and Driver version mismatch !!!\n");
|
|
}
|
|
|
|
static int tcon_send_command_start (sAUOCommand *cmd)
|
|
{
|
|
FUNC_IN();
|
|
INFOL(INFO_DEBUG, ("cmd #%08lX", cmd->cmd));
|
|
/* First: verify that the K1900 is ready */
|
|
|
|
INFOL(INFO_DEBUG, ("/* First: verify that the K1900 is ready */"));
|
|
if ( GET_COMMAND_NEED_WAIT(cmd->cmd) != 0x00 )
|
|
{
|
|
INFOL(INFO_DEBUG, ("Wait for non BUSY..."));
|
|
// TODO: return error on timeout
|
|
if ( tcon_wait_ready() < 0 )
|
|
{
|
|
INFOL(INFO_ERROR, ("Waiting timed out"));
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
if ( AUOCMD_EQUAL(cmd->cmd, AUOCMD_STANDBY) )
|
|
{
|
|
INFOL(INFO_VERBOSE, ("TCON Switching to Standby mode (Level1)"));
|
|
tcon_currentPowerMode = TCON_POWER_STANDBYMODE;
|
|
}
|
|
else if ( AUOCMD_EQUAL(cmd->cmd, AUOCMD_WAKEUP) )
|
|
{
|
|
INFOL(INFO_VERBOSE, ("TCON Switching to Normal mode (Level0)"));
|
|
tcon_currentPowerMode = TCON_POWER_NORMALMODE;
|
|
}
|
|
else if ( AUOCMD_EQUAL(cmd->cmd, AUOCMD_INIT_SET) )
|
|
{
|
|
tcon_inPortraitMode = ~(cmd->params[0]) & (0x1 << 10);
|
|
if ( tcon_lastModeWas != 4 )
|
|
{
|
|
if ( tcon_inPortraitMode )
|
|
tcon_i80bus_set_speed(EN_I80_PORTRAIT, 800, 600, false);
|
|
else
|
|
tcon_i80bus_set_speed(EN_I80_LANDSCAPE, 600, 800, false);
|
|
}
|
|
else
|
|
{
|
|
if ( tcon_inPortraitMode )
|
|
tcon_i80bus_set_speed(EN_I80_PORTRAIT_HANDWRITING, 800, 600, false);
|
|
else
|
|
tcon_i80bus_set_speed(EN_I80_LANDSCAPE_HANDWRITING, 600, 800, false);
|
|
}
|
|
INFOL(INFO_DEBUG, ("Rotation set to 0x%08X...", tcon_inPortraitMode));
|
|
}
|
|
else if ( AUOCMD_EQUAL(cmd->cmd, AUOCMD_DISPLAY_START) )
|
|
{
|
|
INFOL(INFO_DEBUG, ("Display Start (lastMode: %d)...", tcon_lastModeWas));
|
|
if ( ((cmd->params[0] >> 12) & 0x07) == 4 ) /* Handwriting mode... */
|
|
{
|
|
if ( tcon_inPortraitMode )
|
|
tcon_i80bus_set_speed(EN_I80_PORTRAIT_HANDWRITING, 800, 600, false);
|
|
else
|
|
tcon_i80bus_set_speed(EN_I80_LANDSCAPE_HANDWRITING, 600, 800, false);
|
|
}
|
|
else if ( tcon_lastModeWas == 4 )
|
|
{
|
|
if ( tcon_inPortraitMode )
|
|
tcon_i80bus_set_speed(EN_I80_PORTRAIT, 800, 600, false);
|
|
else
|
|
tcon_i80bus_set_speed(EN_I80_LANDSCAPE, 600, 800, false);
|
|
}
|
|
tcon_lastModeWas = ((cmd->params[0] >> 12) & 0x07);
|
|
}
|
|
/* Second: init the i80 interface */
|
|
INFOL(INFO_DEBUG, ("/* Second: init the i80 interface */"));
|
|
tcon_i80bus_init_interface();
|
|
/* Third: Select the chip and set to Command mode */
|
|
INFOL(INFO_DEBUG, ("/* Third: Select the chip and set to Command mode */"));
|
|
tcon_select_chip();
|
|
tcon_set_write_to_command();
|
|
|
|
/* Fourth: Send command */
|
|
INFOL(INFO_DEBUG, ("/* Fourth: Send command */"));
|
|
tcon_i80bus_write(cmd->cmd & 0xFFFF); /* This function already manage
|
|
* no need to do it here. */
|
|
|
|
/* Sixth: If parameters is needed, send them */
|
|
INFOL(INFO_DEBUG, ("/* Sixth: If parameters is needed, send them */"));
|
|
if ( GET_COMMAND_PARAM_NUM(cmd->cmd) > 0 )
|
|
{
|
|
int i, paramNumbers = GET_COMMAND_PARAM_NUM(cmd->cmd);
|
|
|
|
INFOL(INFO_DEBUG, ("YES! We have %d parameters", paramNumbers));
|
|
|
|
tcon_set_write_to_data();
|
|
|
|
for ( i = 0; i < paramNumbers; i++ )
|
|
{
|
|
INFOL(INFO_DEBUG, (" parameter [%02d] = 0x%04X", i, cmd->params[i]));
|
|
tcon_i80bus_write(cmd->params[i]);
|
|
}
|
|
|
|
}
|
|
FUNC_OUT();
|
|
return 0;
|
|
}
|
|
|
|
static int tcon_send_data (unsigned short *buffer, unsigned long bufferLen)
|
|
{
|
|
/* Seventh: Send data if needed */
|
|
unsigned long i;
|
|
//FUNC_IN();
|
|
|
|
//INFOL(INFO_VERBOSE, ("Bufferlen: %ld", bufferLen));
|
|
|
|
tcon_set_write_to_data();
|
|
|
|
for ( i = 0; i < bufferLen; i++ )
|
|
{
|
|
tcon_i80bus_write(buffer[i]);
|
|
}
|
|
//FUNC_OUT();
|
|
return 0;
|
|
}
|
|
|
|
static int tcon_send_lut_data (unsigned short *buffer, unsigned long bufferLen)
|
|
{
|
|
/* Seventh: Send data if needed */
|
|
unsigned long i;
|
|
FUNC_IN();
|
|
|
|
INFOL(INFO_DEBUG, ("Bufferlen: %ld", bufferLen));
|
|
|
|
tcon_set_write_to_data();
|
|
|
|
for ( i = 0; i < bufferLen; i++ )
|
|
{
|
|
tcon_delay(1);
|
|
tcon_wait_ready();
|
|
tcon_delay(1);
|
|
tcon_i80bus_write_LUT(buffer[i]);
|
|
}
|
|
INFOL(INFO_DEBUG, ("Done."));
|
|
FUNC_OUT();
|
|
return 0;
|
|
}
|
|
|
|
static int tcon_read_data (unsigned short *buffer, unsigned long bufferLen)
|
|
{
|
|
unsigned long i;
|
|
FUNC_IN();
|
|
|
|
//INFOL(INFO_VERBOSE, ("Bufferlen: %ld", bufferLen));
|
|
|
|
tcon_set_write_to_data();
|
|
|
|
for ( i = 0; i < bufferLen; i++ )
|
|
{
|
|
tcon_i80bus_read(&(buffer[i]));
|
|
}
|
|
FUNC_OUT();
|
|
return 0;
|
|
}
|
|
|
|
static int tcon_send_command_end (sAUOCommand *cmd)
|
|
{
|
|
FUNC_IN();
|
|
|
|
INFOL(INFO_DEBUG, ("cmd #%08lX START:[#%08X]", cmd->cmd, AUOCMD_DISPLAY_START));
|
|
if ( AUOCMD_EQUAL(cmd->cmd, AUOCMD_DISPLAY_START) )
|
|
{
|
|
tcon_set_write_to_command();
|
|
INFOL(INFO_DEBUG, ("/* Eight: Send STOP command */"));
|
|
tcon_i80bus_write(AUOCMD_DISPLAY_STOP & 0xFFFF);
|
|
tcon_set_write_to_data();
|
|
}
|
|
|
|
INFOL(INFO_DEBUG, ("/* Nineth: Close all */"));
|
|
tcon_unselect_chip();
|
|
tcon_i80bus_deinit_interface();
|
|
|
|
FUNC_OUT();
|
|
return 0;
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Bit Bang SPI Interface
|
|
// ===========================================================================
|
|
#define TCON_SPI_CLK S3C2410_GPE13
|
|
#define TCON_SPI_DI S3C2410_GPE11
|
|
#define TCON_SPI_DO S3C2410_GPE12
|
|
#define TCON_SPI_CS S3C2410_GPL13
|
|
|
|
static int tcon_spi_init (void)
|
|
{
|
|
FUNC_IN();
|
|
/* First be sure that the Wifi is off */
|
|
/* GPK5 = PA 3.3V */
|
|
writel((readl(S3C2416_GPKDAT) & ~(1 << 5)), S3C2416_GPKDAT);
|
|
/* GPK6 = PA 1.8V */
|
|
writel((readl(S3C2416_GPKDAT) & ~(1 << 6)), S3C2416_GPKDAT);
|
|
|
|
/* Next, shutdown the TCON */
|
|
if ( tcon_set_power_off() != 0 )
|
|
return -1;
|
|
mdelay(2000);
|
|
|
|
//GPD8, RESET
|
|
s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP);
|
|
s3c2410_gpio_setpin(S3C2410_GPD8, 0);
|
|
/* Next, Switch the SPI */
|
|
s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP);
|
|
s3c2410_gpio_setpin(S3C2410_GPH6, 1);
|
|
mdelay(200);
|
|
|
|
/* Next, init GPIOs */
|
|
|
|
/* GPL13 = SPI Chip Select */
|
|
s3c2410_gpio_cfgpin(S3C2410_GPL13, S3C2410_GPL13_OUTP);
|
|
s3c2410_gpio_setpin(S3C2410_GPL13, 1);
|
|
|
|
/* GPE11 = SPI DI */
|
|
s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_INP);
|
|
s3c2410_gpio_pullup(S3C2410_GPE11, 0);
|
|
|
|
/* GPE12 = SPI DO */
|
|
s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_OUTP);
|
|
s3c2410_gpio_setpin(S3C2410_GPE12, 1);
|
|
|
|
/* GPE13 = SPI CLK */
|
|
s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_OUTP);
|
|
s3c2410_gpio_setpin(S3C2410_GPE13, 0);
|
|
|
|
mdelay(50);
|
|
|
|
/* We are ready ! */
|
|
FUNC_OUT();
|
|
return 0;
|
|
}
|
|
|
|
static void tcon_spi_deinit (void)
|
|
{
|
|
FUNC_IN();
|
|
/* First, switch the SPI */
|
|
s3c2410_gpio_setpin(S3C2410_GPH6, 1);
|
|
|
|
/* Then, restore GPIOs */
|
|
s3c2410_gpio_cfgpin(S3C2410_GPL13, S3C2410_GPL13_SS0);
|
|
/* GPE11 = SPI DI */
|
|
s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0);
|
|
/* GPE12 = SPI DO */
|
|
s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0);
|
|
/* GPE13 = SPI CLK */
|
|
s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0);
|
|
|
|
/* Next, switch back on the TCON */
|
|
tcon_set_power_on();
|
|
s3c2410_gpio_setpin(S3C2410_GPD8, 1);
|
|
mdelay(100);
|
|
/* The calling must restore the TCON status, we only set it back on */
|
|
FUNC_OUT();
|
|
}
|
|
|
|
static inline unsigned char tcon_spi_read (void)
|
|
{
|
|
unsigned char k,
|
|
tmp_data = 0,
|
|
i = 0;
|
|
u32 read_pin;
|
|
#ifdef DEBUG_SPI
|
|
char strobe_str[9];
|
|
strobe_str[8] = 0;
|
|
#endif
|
|
/* sure that the clock is low */
|
|
for ( k = 0; k < 8; k++ )
|
|
{
|
|
/* Strobe clock */
|
|
s3c2410_gpio_setpin(TCON_SPI_CLK, 1);
|
|
udelay(2);
|
|
|
|
/* Sample */
|
|
read_pin = s3c2410_gpio_getpin(TCON_SPI_DI); //(readl(S3C2410_GPEDAT) & (1 << 11));
|
|
|
|
if ( read_pin )
|
|
{
|
|
i = (0x80 >> k);
|
|
tmp_data = (tmp_data | i);
|
|
}
|
|
|
|
#ifdef DEBUG_SPI
|
|
strobe_str[k] = (read_pin ? '-' : '_');
|
|
#endif
|
|
|
|
/* ACK bit read */
|
|
s3c2410_gpio_setpin(TCON_SPI_CLK, 0);
|
|
udelay(2);
|
|
}
|
|
|
|
#ifdef DEBUG_SPI
|
|
INFOL(INFO_VERBOSE, ("WFM:[%s] return %d", strobe_str, tmp_data));
|
|
#endif
|
|
|
|
return (tmp_data);
|
|
}
|
|
|
|
static inline void tcon_spi_write (unsigned char value)
|
|
{
|
|
unsigned char k;
|
|
#ifdef DEBUG_SPI
|
|
char strobe_str[9];
|
|
strobe_str[8] = 0;
|
|
#endif
|
|
|
|
|
|
for ( k = 0; k < 8; k++ )
|
|
{
|
|
if ( (value & 0x80) )
|
|
s3c2410_gpio_setpin(TCON_SPI_DO, 1);
|
|
else
|
|
s3c2410_gpio_setpin(TCON_SPI_DO, 0);
|
|
|
|
#ifdef DEBUG_SPI
|
|
strobe_str[k] = ((value & 0x80) ? '-' : '_');
|
|
#endif
|
|
|
|
/* Strobe clock */
|
|
s3c2410_gpio_setpin(TCON_SPI_CLK, 1);
|
|
udelay(2);
|
|
s3c2410_gpio_setpin(TCON_SPI_CLK, 0);
|
|
udelay(2);
|
|
|
|
value = (value << 1);
|
|
}
|
|
|
|
#ifdef DEBUG_SPI
|
|
INFOL(INFO_VERBOSE, ("WFM:[%s]", strobe_str));
|
|
#endif
|
|
}
|
|
|
|
static unsigned char tcon_spi_flash_read (unsigned char value)
|
|
{
|
|
unsigned char ret;
|
|
s3c2410_gpio_setpin(TCON_SPI_CS, 0);
|
|
udelay(50);
|
|
|
|
tcon_spi_write(value);
|
|
udelay(2);
|
|
ret = tcon_spi_read();
|
|
|
|
s3c2410_gpio_setpin(TCON_SPI_CS, 1);
|
|
udelay(50);
|
|
return ret;
|
|
}
|
|
|
|
static void tcon_spi_flash_write (unsigned char value)
|
|
{
|
|
s3c2410_gpio_setpin(TCON_SPI_CS, 0);
|
|
udelay(50);
|
|
|
|
tcon_spi_write(value);
|
|
|
|
s3c2410_gpio_setpin(TCON_SPI_CS, 1);
|
|
udelay(50);
|
|
}
|
|
|
|
static void tcon_spi_flash_waitready (void)
|
|
{
|
|
unsigned char r_data;
|
|
FUNC_IN();
|
|
do
|
|
{
|
|
s3c2410_gpio_setpin(TCON_SPI_CS, 0);
|
|
udelay(50);
|
|
tcon_spi_write(0x05); //Write Read Status command
|
|
udelay(2);
|
|
r_data = tcon_spi_read();
|
|
udelay(50);
|
|
s3c2410_gpio_setpin(TCON_SPI_CS, 1);
|
|
INFOL(INFO_DEBUG, ("r_data: 0x%02x [SRWD:%c BP2:%d BP1:%d BP0:%d, WEL:%c, WIP:%c]\n", r_data,
|
|
(r_data & 0x80) ? 'E' : 'D',
|
|
(r_data & 0x10) ? 1 : 0,
|
|
(r_data & 0x08) ? 1 : 0,
|
|
(r_data & 0x04) ? 1 : 0,
|
|
(r_data & 0x02) ? 'E' : 'D',
|
|
(r_data & 0x01) ? 'W' : 'r'));
|
|
msleep(20);
|
|
|
|
} while ( r_data & 0x01 );
|
|
FUNC_OUT();
|
|
}
|
|
|
|
static void tcon_spi_flash_writepage (unsigned long addr, unsigned char *buffer, unsigned short buffer_len)
|
|
{
|
|
int i;
|
|
FUNC_IN();
|
|
s3c2410_gpio_setpin(TCON_SPI_CS, 0);
|
|
udelay(50);
|
|
|
|
tcon_spi_write(0x02); //Write Page Program command
|
|
tcon_spi_write((addr & 0xFF0000) >> 16);
|
|
tcon_spi_write((addr & 0xFF00) >> 8);
|
|
tcon_spi_write((addr & 0xFF));
|
|
|
|
for ( i = 0; i < buffer_len; i++ )
|
|
{
|
|
tcon_spi_write(buffer[i]);
|
|
}
|
|
|
|
s3c2410_gpio_setpin(TCON_SPI_CS, 1);
|
|
mdelay(10);
|
|
|
|
tcon_spi_flash_waitready();
|
|
|
|
FUNC_OUT();
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Device related functions
|
|
// ===========================================================================
|
|
|
|
static int tcon_open (struct inode *inode, struct file *file)
|
|
{
|
|
FUNC_IN();
|
|
|
|
FUNC_OUT();
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static int tcon_release (struct inode *inode, struct file *file)
|
|
{
|
|
FUNC_IN();
|
|
|
|
FUNC_OUT();
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static ssize_t tcon_read (struct file *file, char *buf, size_t count, loff_t *ppos)
|
|
{
|
|
FUNC_IN();
|
|
|
|
FUNC_OUT();
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static int tcon_command (sAUOCommand *cmd, char userland)
|
|
{
|
|
unsigned char buffer[2048];
|
|
unsigned char *user_buffer;
|
|
unsigned long user_buflen, copysize, copysize16;
|
|
unsigned short *ptr16;
|
|
//int block_id;
|
|
int ret = 0;
|
|
|
|
/* Now execute the command */
|
|
if ( tcon_send_command_start(cmd) != 0 )
|
|
{
|
|
ret = -EIO;
|
|
goto exit;
|
|
}
|
|
|
|
if ( GET_COMMAND_HAVE_DATA(cmd->cmd) != 0 )
|
|
{
|
|
user_buflen = cmd->datalen;
|
|
user_buffer = (unsigned char *)cmd->data;
|
|
|
|
while ( user_buflen != 0 )
|
|
{
|
|
copysize = user_buflen;
|
|
|
|
if ( user_buflen > sizeof (buffer) )
|
|
copysize = sizeof (buffer);
|
|
if ( GET_COMMAND_READ_WRITE(cmd->cmd) == 0 )
|
|
{ /* Write mode */
|
|
if (userland)
|
|
{
|
|
if ( copy_from_user(buffer, user_buffer, copysize) )
|
|
{
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memcpy(buffer, user_buffer, copysize);
|
|
}
|
|
}
|
|
|
|
copysize16 = (copysize + 1) / 2;
|
|
|
|
ptr16 = (unsigned short *)buffer;
|
|
if ( GET_COMMAND_READ_WRITE(cmd->cmd) == 0 )
|
|
{ /* Write mode */
|
|
if ( cmd->cmd == AUOCMD_LUT_START )
|
|
tcon_send_lut_data((unsigned short *)buffer, copysize16);
|
|
else
|
|
tcon_send_data((unsigned short *)buffer, copysize16);
|
|
}
|
|
else
|
|
{ /* Read mode */
|
|
tcon_read_data((unsigned short *)buffer, copysize16);
|
|
if (userland)
|
|
{
|
|
if ( copy_to_user(user_buffer, buffer, copysize) )
|
|
return -EFAULT;
|
|
}
|
|
else
|
|
{
|
|
memcpy(user_buffer, buffer, copysize);
|
|
}
|
|
}
|
|
|
|
user_buflen -= copysize;
|
|
user_buffer += copysize;
|
|
}
|
|
}
|
|
tcon_send_command_end(cmd);
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
static int tcon_ioctl (struct inode *inode, struct file *file,
|
|
unsigned int ioctl_cmd, unsigned long arg)
|
|
{
|
|
sAUOCommand cmd;
|
|
unsigned char buffer[2048];
|
|
unsigned char *user_buffer;
|
|
unsigned long user_buflen, copysize;
|
|
void __user *argp = (void __user *)arg;
|
|
int block_id;
|
|
int flash_addr;
|
|
int ret = -EINVAL;
|
|
|
|
FUNC_IN();
|
|
|
|
block_id = 0;
|
|
|
|
//INFOL(INFO_VERBOSE, ("Receive IOTCL #08%X", ioctl_cmd));
|
|
switch ( ioctl_cmd )
|
|
{
|
|
case IOCTL_AUO_SENDCOMMAND:
|
|
if ( copy_from_user(&cmd, argp, sizeof (cmd)) )
|
|
return -EFAULT;
|
|
|
|
/* Prevent to send command if the user don't provide a buffer */
|
|
if ( (GET_COMMAND_READ_WRITE(cmd.cmd) != 0) && (cmd.datalen == 0) )
|
|
return -EFAULT;
|
|
|
|
/* Prevent if we set data and data prt is NULL */
|
|
if ( (GET_COMMAND_HAVE_DATA(cmd.cmd) != 0) && (cmd.datalen > 0) && (cmd.data == NULL) )
|
|
return -EFAULT;
|
|
|
|
ret = tcon_command(&cmd, true);
|
|
|
|
//INFOL(INFO_VERBOSE, ("tcon_command returned: %d", ret));
|
|
|
|
break;
|
|
|
|
case IOCTL_AUO_RESET:
|
|
tcon_reset();
|
|
ret = 0;
|
|
break;
|
|
|
|
case IOCTL_AUO_POWEROFF:
|
|
//
|
|
INFOL(INFO_VERBOSE, ("Switching to power OFF (Level3)..."));
|
|
ret = tcon_set_power_off();
|
|
break;
|
|
|
|
case IOCTL_AUO_POWERON:
|
|
//
|
|
INFOL(INFO_VERBOSE, ("Switching to power Normal Mode (Level0)..."));
|
|
tcon_set_power_on();
|
|
ret = 0;
|
|
break;
|
|
|
|
case IOCTL_AUO_WAITBUSY:
|
|
ret = tcon_wait_ready();
|
|
break;
|
|
|
|
case IOCTL_AUO_SLEEP:
|
|
/* Only accept go to sleep if we are in standby, or else it will fail */
|
|
if ( tcon_currentPowerMode == TCON_POWER_STANDBYMODE )
|
|
{
|
|
INFOL(INFO_VERBOSE, ("Switching Standby to Sleep mode (Level2)..."));
|
|
tcon_currentPowerMode = TCON_POWER_SLEEPMODE;
|
|
|
|
tcon_goto_sleep();
|
|
|
|
ret = 0;
|
|
}
|
|
else
|
|
{
|
|
ret = -ENAVAIL;
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_AUO_WAKEUP:
|
|
/* Only accept go to sleep if we are in standby, or else it will fail */
|
|
if ( tcon_currentPowerMode == TCON_POWER_SLEEPMODE )
|
|
{
|
|
INFOL(INFO_VERBOSE, ("Switching Sleep to Standby Mode (Level1)..."));
|
|
tcon_currentPowerMode = TCON_POWER_STANDBYMODE;
|
|
/* Set GPIO accordingly */
|
|
|
|
tcon_wakeup_sleep();
|
|
|
|
ret = 0;
|
|
}
|
|
else
|
|
ret = -ENAVAIL;
|
|
break;
|
|
|
|
|
|
case IOCTL_AUO_UPDATEFW:
|
|
INFOL(INFO_VERBOSE, ("Starting update of TCON firmware..."));
|
|
if ( copy_from_user(&cmd, argp, sizeof (cmd)) )
|
|
{
|
|
INFOL(INFO_ERROR, ("Copy from User error..."));
|
|
return -EFAULT;
|
|
}
|
|
|
|
if ( (cmd.data == NULL) || (cmd.datalen == 0) )
|
|
{
|
|
INFOL(INFO_ERROR, ("Parameters error..."));
|
|
return -EFAULT;
|
|
}
|
|
|
|
/* First init SPI */
|
|
INFOL(INFO_DEBUG, ("Will init BitBang SPI..."));
|
|
tcon_spi_init();
|
|
|
|
mdelay(3000);
|
|
|
|
flash_addr = 0;
|
|
INFOL(INFO_VERBOSE, ("Erasing SPI flash..."));
|
|
/* Erase Flash */
|
|
tcon_spi_flash_write(0x06); //Setting Write Enable Latch bit
|
|
tcon_spi_flash_write(0x60); //Write Chip Erase command
|
|
msleep(100);
|
|
|
|
tcon_spi_flash_waitready();
|
|
|
|
|
|
|
|
user_buflen = cmd.datalen;
|
|
user_buffer = (unsigned char *)cmd.data;
|
|
INFOL(INFO_VERBOSE, ("Will start write of TCON firmware..."));
|
|
/* Now, Write new Flash content */
|
|
while ( user_buflen != 0 )
|
|
{
|
|
copysize = user_buflen;
|
|
INFOL(INFO_DEBUG, ("Flash page @ addr 0x%08X", flash_addr));
|
|
|
|
if ( user_buflen > 256 )
|
|
copysize = 256;
|
|
|
|
if ( copy_from_user(buffer, user_buffer, copysize) )
|
|
return -EFAULT;
|
|
|
|
/* Send Buffer data */
|
|
tcon_spi_flash_write(0x06); /* Enable write... */
|
|
tcon_spi_flash_writepage(flash_addr, buffer, copysize);
|
|
|
|
flash_addr += copysize;
|
|
user_buflen -= copysize;
|
|
user_buffer += copysize;
|
|
}
|
|
|
|
/* Restore SPI */
|
|
tcon_spi_deinit();
|
|
break;
|
|
|
|
case IOCTL_AUO_PROGRESSBAR:
|
|
{
|
|
unsigned short *buf;
|
|
copysize = (arg * 492) / 100;
|
|
copysize -= (copysize % 8);
|
|
buf = (unsigned short *)kmalloc(copysize * 24 * 4, GFP_KERNEL);
|
|
if ( buf )
|
|
{
|
|
memset(buf, 0x00, copysize * 24 * 4);
|
|
|
|
cmd.cmd = AUOCMD_DISPLAY_START;
|
|
cmd.params[0] = AUO_DSPPARAM_MODE_GRAYnFLASH | ((((600 - 500) / 2) + 4) & 0x0FFF);
|
|
cmd.params[1] = (((800 - (30 + 20)) + 4) & 0x0FFF);
|
|
cmd.params[2] = ((copysize) & 0x0FFF);
|
|
cmd.params[3] = (24 & 0x0FFF);
|
|
cmd.data = buf;
|
|
cmd.datalen = ((copysize>>1) * 24) >> 1;
|
|
tcon_command(&cmd, false);
|
|
|
|
kfree(buf);
|
|
}
|
|
|
|
ret = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
printk(KERN_WARNING "Invalid IOCTL");
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
FUNC_OUT();
|
|
return ret;
|
|
}
|
|
// ===========================================================================
|
|
static struct file_operations s_tcon_fops ={
|
|
owner : THIS_MODULE,
|
|
read : tcon_read,
|
|
ioctl : tcon_ioctl,
|
|
open : tcon_open,
|
|
release : tcon_release,
|
|
};
|
|
|
|
static struct miscdevice s_tcon_dev ={
|
|
.minor = 242,
|
|
.name = "epaper",
|
|
.fops = &s_tcon_fops,
|
|
};
|
|
// ===========================================================================
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static int tcon_probe (struct platform_device *dev)
|
|
{
|
|
int ret = -EINVAL;
|
|
FUNC_IN();
|
|
printk("tcon: probe");
|
|
if ( GET_CAPABILITY(PLAT_CAP_VTCON) )
|
|
{
|
|
unsigned long tmp;
|
|
printk(" ok\n");
|
|
ret = 0;
|
|
|
|
printk("Cybook Orizon Layout\n");
|
|
|
|
// set gpio for lcd
|
|
tmp = __raw_readl(S3C2410_GPCCON);
|
|
//tmp = (tmp & ~(0xffff03ff))|(0xaaaa02aa);
|
|
tmp = (tmp & ~(0xffff033f)) | (0xaaaa022a); //Do not config SYS_CS1(GPC3)
|
|
__raw_writel(tmp, S3C2410_GPCCON);
|
|
|
|
tmp = __raw_readl(S3C2410_GPDCON); //tmp=0x40000
|
|
tmp = (tmp & ~(0x0CFFFF)) | (0x04AAAA);
|
|
__raw_writel(tmp, S3C2410_GPDCON); // GPD9 is RST_N
|
|
|
|
__raw_writel(0, S3C2410_GPCUP); //S3C2410_GPCUP = 0;
|
|
__raw_writel(0, S3C2410_GPDUP); //S3C2410_GPDUP = 0;
|
|
|
|
#if 0
|
|
tcon_i80bus_set_speed(EN_I80_LANDSCAPE, 800, 600, true);
|
|
|
|
// POWER pin config
|
|
tmp = __raw_readl(S3C2410_GPBCON);
|
|
tmp = (tmp & ~(3 << 6)) | (1 << 6);
|
|
__raw_writel(tmp, S3C2410_GPBCON);
|
|
|
|
// BUSY pin config
|
|
tmp = __raw_readl(S3C2410_GPBCON);
|
|
tmp = (tmp & ~(3 << 4));
|
|
__raw_writel(tmp, S3C2410_GPBCON);
|
|
|
|
// SLEEP pin config
|
|
tmp = __raw_readl(S3C2410_GPBCON);
|
|
tmp = (tmp & ~(3 << 2)) | (1 << 2);
|
|
__raw_writel(tmp, S3C2410_GPBCON);
|
|
|
|
msleep(1);
|
|
|
|
// Panel power on
|
|
tmp = __raw_readl(S3C2410_GPBDAT);
|
|
tmp |= (1 << 3);
|
|
//SLP_N high
|
|
tmp |= (1 << 1);
|
|
__raw_writel(tmp, S3C2410_GPBDAT);
|
|
|
|
msleep(100);
|
|
|
|
|
|
// LCD module reset
|
|
tmp = __raw_readl(S3C2410_GPDDAT);
|
|
tmp |= (1 << 9);
|
|
__raw_writel(tmp, S3C2410_GPDDAT);
|
|
|
|
tmp = __raw_readl(S3C2410_GPDDAT); // RST_N goes to LOW
|
|
tmp &= ~(1 << 9);
|
|
__raw_writel(tmp, S3C2410_GPDDAT);
|
|
|
|
tcon_delay(5);
|
|
|
|
tmp = __raw_readl(S3C2410_GPDDAT); // RST_N goes to HIGH
|
|
tmp |= (1 << 9);
|
|
__raw_writel(tmp, S3C2410_GPDDAT);
|
|
|
|
// delay about 10ms
|
|
msleep(10);
|
|
|
|
tcon_i80bus_set_speed(EN_I80_PORTRAIT, 800, 600, false);
|
|
#else
|
|
tcon_set_power_on();
|
|
#endif
|
|
|
|
tcon_display_info();
|
|
}
|
|
else
|
|
printk(" not ok\n");
|
|
FUNC_OUTR(ret);
|
|
return ret;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static int tcon_remove (struct platform_device *dev)
|
|
{
|
|
FUNC_IN();
|
|
|
|
misc_deregister(&s_tcon_dev);
|
|
FUNC_OUT();
|
|
return 0;
|
|
}
|
|
// --------------------------------------------------------------------------
|
|
|
|
static int tcon_resume (struct platform_device *dev)
|
|
{
|
|
FUNC_IN();
|
|
|
|
//tcon_set_power_on();
|
|
|
|
if ( tcon_inPortraitMode )
|
|
tcon_i80bus_set_speed(EN_I80_PORTRAIT, 800, 600, true);
|
|
else
|
|
tcon_i80bus_set_speed(EN_I80_LANDSCAPE, 600, 800, true);
|
|
|
|
FUNC_OUT();
|
|
return 0;
|
|
}
|
|
// --------------------------------------------------------------------------
|
|
|
|
static int tcon_suspend (struct platform_device *dev, pm_message_t state)
|
|
{
|
|
FUNC_IN();
|
|
|
|
DBG("state event: %X", state.event);
|
|
|
|
//tcon_set_power_off();
|
|
|
|
FUNC_OUT();
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
static struct platform_driver tcon_driver = {
|
|
.driver =
|
|
{
|
|
.name = "epaper-tcon",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.probe = tcon_probe,
|
|
.remove = tcon_remove,
|
|
.suspend = tcon_suspend,
|
|
.resume = tcon_resume,
|
|
};
|
|
// ---------------------------------------------------------------------------
|
|
// ===========================================================================
|
|
|
|
static int tcon_procReadEpaper (char *page, char **start, off_t off, int count,
|
|
int *eof, void *data)
|
|
{
|
|
int len;
|
|
|
|
struct tcon_info info;
|
|
tcon_get_info(&info);
|
|
|
|
len = sprintf(page, "tcon_k1900;%d;%d;%d;%d;%d;%d;%d;%d;%d\n",
|
|
tcon_currentPowerMode, tcon_currentSpeedClass,
|
|
info.tcon_version, info.lut_version,
|
|
info.temperature>>1, info.model,
|
|
info.epd_type, info.panel_size_int, info.panel_size_float);
|
|
|
|
return len;
|
|
}
|
|
// ===========================================================================
|
|
|
|
static int __init tcon_init (void)
|
|
{
|
|
int ret = 0;
|
|
FUNC_IN();
|
|
|
|
printk("tcon: init\n");
|
|
|
|
if ( misc_register(&s_tcon_dev) )
|
|
{
|
|
ret = -EBUSY;
|
|
goto exit;
|
|
}
|
|
|
|
platform_driver_register(&tcon_driver);
|
|
|
|
epaperProcEntry = create_proc_entry("epaper", 0644, proc_root_driver);
|
|
epaperProcEntry->read_proc = tcon_procReadEpaper;
|
|
epaperProcEntry->owner = THIS_MODULE;
|
|
|
|
exit:
|
|
FUNC_OUTR(ret);
|
|
return ret;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static void __exit tcon_exit (void)
|
|
{
|
|
FUNC_IN();
|
|
printk("tcon: exit\n");
|
|
platform_driver_unregister(&tcon_driver);
|
|
|
|
misc_deregister(&s_tcon_dev);
|
|
remove_proc_entry("epaper", proc_root_driver);
|
|
FUNC_OUT();
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
module_init (tcon_init);
|
|
module_exit (tcon_exit);
|
|
// ---------------------------------------------------------------------------
|
|
MODULE_LICENSE ("GPL");
|
|
MODULE_AUTHOR ("Bookeen <developers@bookeen.com>");
|
|
MODULE_DESCRIPTION ("AUO ePaper TCON Driver");
|
|
MODULE_VERSION ("1.0");
|
|
// ==========================================================================
|