#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <plat/gpio-cfg.h>
#include <mach/regs-gpio.h>
#include <mach/regs-irq.h>
#include <mach/regs-clock.h>
#include <linux/gpio.h>
#include <asm/io.h>
#include <asm/div64.h>
#include <asm/mach/map.h>
#include <mach/regs-gpio.h>
static struct fb_info *x210_fb_info;
static u32 pseudo_pal[16];
static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}
static int x210_lcdfb_setcolreg(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int transp, struct fb_info *info)
{
unsigned int val;
if (regno > 16)
return 1;
/* 用red,green,blue三原色构造出val */
val = chan_to_field(red, &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue, &info->var.blue);
//((u32 *)(info->pseudo_palette))[regno] = val;
pseudo_pal[regno] = val;
return 0;
}
struct fb_ops s3cfb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = x210_lcdfb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
void lcd_gpio_init(void)
{
int i;
/*RGB引脚*/
for (i = 0; i < 8; i++) {
s3c_gpio_cfgpin(S5PV210_GPF0(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF0(i), S3C_GPIO_PULL_NONE);
}
for (i = 0; i < 8; i++) {
s3c_gpio_cfgpin(S5PV210_GPF1(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF1(i), S3C_GPIO_PULL_NONE);
}
for (i = 0; i < 8; i++) {
s3c_gpio_cfgpin(S5PV210_GPF2(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF2(i), S3C_GPIO_PULL_NONE);
}
for (i = 0; i < 4; i++) {
s3c_gpio_cfgpin(S5PV210_GPF3(i), S3C_GPIO_SFN(2));
s3c_gpio_setpull(S5PV210_GPF3(i), S3C_GPIO_PULL_NONE);
}
/* mDNIe SEL: why we shall write 0x2 ? */
writel(0x2, S5P_MDNIE_SEL);
/* drive strength to max */
writel(0xffffffff, S5PV210_GPF0_BASE + 0xc);
writel(0xffffffff, S5PV210_GPF1_BASE + 0xc);
writel(0xffffffff, S5PV210_GPF2_BASE + 0xc);
writel(0x000000ff, S5PV210_GPF3_BASE + 0xc);
/*设置背光灯*/
s3c_gpio_cfgpin(S5PV210_GPD0(0), S3C_GPIO_OUTPUT);
s3c_gpio_setpull(S5PV210_GPD0(0), S3C_GPIO_PULL_UP);
gpio_set_value(S5PV210_GPD0(0), 0);
/* backlight enable pin */
s3c_gpio_cfgpin(S5PV210_GPF3(5), S3C_GPIO_OUTPUT);
s3c_gpio_setpull(S5PV210_GPF3(5), S3C_GPIO_PULL_UP);
gpio_set_value(S5PV210_GPF3(5), 1);
/* LCD_5V */
s3c_gpio_cfgpin(S5PV210_GPH0(5), S3C_GPIO_OUTPUT);
s3c_gpio_setpull(S5PV210_GPH0(5), S3C_GPIO_PULL_UP);
gpio_set_value(S5PV210_GPH0(5), 1);
/* LCD_33 */
s3c_gpio_cfgpin(S5PV210_GPH2(0), S3C_GPIO_OUTPUT);
s3c_gpio_setpull(S5PV210_GPH2(0), S3C_GPIO_PULL_DOWN);
gpio_set_value(S5PV210_GPH2(0), 0);
/* wait a moment */
mdelay(200);
}
typedef struct s5pv_lcd_reg{
volatile unsigned int VIDCON0;
volatile unsigned int VIDCON1;
volatile unsigned int VIDCON2;
volatile unsigned int VIDCON3;
volatile unsigned int VIDTCON0;
volatile unsigned int VIDTCON1;
volatile unsigned int VIDTCON2;
volatile unsigned int VIDTCON3;
}*PT_s5pv_lcd_reg;
static PT_s5pv_lcd_reg pt_s5pv_lcd_reg;
static unsigned int lcd_mem_size;
static unsigned int lcd_mem;
static unsigned char *lcd_var_mem;
//0xF800_0034
static volatile unsigned int *SHADOWCON;
//0xF800_0028
static volatile unsigned int *WINCON2;
//0xF800_0060
static volatile unsigned int *VIDOSD2A;
//0xF800_0064
static volatile unsigned int *VIDOSD2B;
//0xF800_0068
static volatile unsigned int *VIDOSD2C;
//0xF800_00B0
static volatile unsigned int *VIDW02ADD0B0;
//0xF800_00E0
static volatile unsigned int *VIDW02ADD1B0;
//0xF800_0108
static volatile unsigned int *VIDW02ADD2;
static volatile unsigned int *DISPLAY_PATH_SEL ;
static struct clk *clk;
void reg_ioremap(void)
{
SHADOWCON = ioremap(0xF8000034,4);
WINCON2 = ioremap(0xF8000028,4);
VIDOSD2A = ioremap(0xF8000060,4);
VIDOSD2B = ioremap(0xF8000064,4);
VIDOSD2C = ioremap(0xF8000068,4);
VIDW02ADD0B0 = ioremap(0xF80000B0,4);
VIDW02ADD1B0 = ioremap(0xF80000E0,4);
VIDW02ADD2 = ioremap(0xF8000108,4);
DISPLAY_PATH_SEL =ioremap(0xE0107008,4);
}
static int x210_lcd_init(void)
{
/*分配fb_info*/
x210_fb_info=framebuffer_alloc(0, NULL);
strcpy(x210_fb_info->fix.id, "mylcd");
x210_fb_info->fbops = &s3cfb_ops;
/*分配固定参数*/
x210_fb_info->fix.smem_len = 1024*600*32/8;
x210_fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
x210_fb_info->fix.visual =FB_VISUAL_TRUECOLOR;
x210_fb_info->fix.line_length =1024*4;
/*设置可变参数*/
x210_fb_info->var.xres = 1024;
x210_fb_info->var.yres = 600;
x210_fb_info->var.xres_virtual = 1024;
x210_fb_info->var.yres_virtual = 600;
x210_fb_info->var.bits_per_pixel =32;
/*设置RGB偏移值参数*/
x210_fb_info->var.red.offset= 16;
x210_fb_info->var.red.length = 8;
x210_fb_info->var.green.offset = 8;
x210_fb_info->var.green.length = 8;
x210_fb_info->var.blue.offset = 0;
x210_fb_info->var.blue.length = 8;
x210_fb_info->var.activate = FB_ACTIVATE_NOW;
/*设置其他参数*/
x210_fb_info->pseudo_palette = &pseudo_pal;
x210_fb_info->screen_size = 1024*600*32/8;
/*初始化GPIO*/
lcd_gpio_init();
reg_ioremap();
/*使能lcd控制器时钟*/
clk = clk_get(NULL,"lcd");
if (IS_ERR(clk)) {
printk("can't get clock \n");
return PTR_ERR(clk);
}
clk_enable(clk);
//11: RGB=FIMD I80=FIMD ITU=FIMD
*DISPLAY_PATH_SEL = (2<<0);
/* C2_EN_F [2] 1
*
*/
*SHADOWCON = (1<<2);
/* OSD_LeftTopX_F [21:11]
* OSD_LeftTopY_F [10:0]
*/
*VIDOSD2A = 0x00000000;
/* OSD_RightBotX_F [21:11]
* OSD_RightBotY_F [10:0]
*/
*VIDOSD2B = (1023<<11) |(599<<0) ;
*VIDOSD2C =(1024-1)*(600-1);
/*设置lcd控制器参数*/
/*分配显存*/
lcd_mem_size =1024*600*32/8;
lcd_var_mem=dma_alloc_writecombine(NULL,lcd_mem_size,(dma_addr_t *) &x210_fb_info->fix.smem_start, GFP_KERNEL);
if(lcd_var_mem == NULL){
printk("can't alloc lcd mem !\n");
}
printk("lcd mem phy address 0x%x \n",x210_fb_info->fix.smem_start);
x210_fb_info->screen_base = lcd_var_mem;
lcd_mem = x210_fb_info->fix.smem_start;
/*把显存的地址告诉控制器*/
/*start address*/
*VIDW02ADD0B0 = lcd_mem;
/*end address*/
*VIDW02ADD1B0 = lcd_mem+lcd_mem_size;
/*mem size
*OFFSIZE_F [25:13]
*PAGEWIDTH_F [12:0]
*/
//*VIDW02ADD2
pt_s5pv_lcd_reg =(PT_s5pv_lcd_reg)ioremap(0xF8000000, 4*8);
if(pt_s5pv_lcd_reg == NULL){
printk("lcd ioremap error \n");
}
/*IVCLK 7 0 Video data is fetched at VCLK falling edge
*IHSYNC 6 1 1 = Inverted
*IVSYNC 5 1 1 = Inverted
*IVDEN 4
*/
pt_s5pv_lcd_reg->VIDCON1 = (1<<6) | (1<<5);
/* RGB_SKIP_EN 0 0 = Disables
*这个暂时不设置
*/
//pt_s5pv_lcd_reg->VIDCON2 =
/*
*CG_ON 18 Enables Control Color Gain.
*/
//by quan
// pt_s5pv_lcd_reg->VIDCON3 = (1<<18);
/* timing
*VBPD [23:16] 18-1 = 21
*VFPD [15:8] 22-1 = 17
*VSPW [7:0] 1-1 =0
*/
pt_s5pv_lcd_reg->VIDTCON0 = (21<<16) |(17<<8);
/* timing
*HBPD [23:16] 45-1 = 44
*HFPD [15:8] 100-1 = 99
*HFPD [7:0] 1-1 =0
*/
pt_s5pv_lcd_reg->VIDTCON1 = (44<<16) | (99<<8);
/* LINEVAL [21:11] 600 -1 599
* HOZVAL [10:0] 1024 -1 1023
*/
pt_s5pv_lcd_reg->VIDTCON2 = (599<<11) |(1023<<0);
/* [31] 1 = Enables Enables VSYNC Signal Output.
*
*/
pt_s5pv_lcd_reg->VIDTCON3 = (1<<31);
/*
* VIDOUT [28:26] 000 RGB interface
* RGSPSEL [18] 0 RGB parallel format
* CLKVAL_F[13:6] 3 设置为4分频
* CLKDIR [4] 1 1 = Divided by CLKVAL_F
*CLKSEL_F 0 0 = HCLK
*ENVID 使能位 1
*ENVID_F 使能位 1
*/
pt_s5pv_lcd_reg->VIDCON0 = (3<<6) | (1<<4) |(1<<1) |(1<<0);
//by quan
/*WSWP_F [15] 1 = Swap Enable
*BPPMODE_F [5:2] 1011
*ENWIN_F [0] 1 = Enables the video output and video control signal.
*/
*WINCON2 = (1<<15) |(11<<2) |(1<<0);
register_framebuffer(x210_fb_info);
}
static void x210_lcd_exit(void)
{
iounmap(SHADOWCON);
iounmap(WINCON2);
iounmap(VIDOSD2A);
iounmap(VIDOSD2B);
iounmap(VIDOSD2C);
iounmap(VIDW02ADD0B0);
iounmap(VIDW02ADD1B0);
iounmap(VIDW02ADD2);
iounmap(DISPLAY_PATH_SEL);
iounmap(pt_s5pv_lcd_reg);
framebuffer_release(x210_fb_info);
dma_free_writecombine(NULL,lcd_mem_size,&lcd_mem,GFP_KERNEL);
unregister_framebuffer(x210_fb_info);
clk_put(clk);
}
module_init(x210_lcd_init);
module_exit(x210_lcd_exit);
MODULE_LICENSE("GPL");