| 
 | 
 
S5PV210(TQ210)学习笔记——LCD驱动编写 
网上S5PV210内核移植的文章不是很多,而描述2440和6410内核移植的文章多数是讲如何移植,而非手动编写,但是,韦东山老师的视频中讲述了如何从头编写LCD驱动,当然是以2440为例的,我看过视频之后在TQ210平台上进行了实验,实验成功,详细的原理部分以我现在的水平还难以表达清楚。下载是我自己写的代码,适用于TQ210的7寸电容屏。#include <linux/module.h> 
#include <linux/fb.h> 
#include <linux/dma-mapping.h> 
#include <linux/clk.h> 
 
 
static struct fb_info *lcd_info; 
unsigned long pseudo_palette[16]; 
 
unsigned long *display_control; 
 
volatile unsigned long* gpf0con; 
volatile unsigned long* gpf1con; 
volatile unsigned long* gpf2con; 
volatile unsigned long* gpf3con; 
volatile unsigned long* gpd0con; 
volatile unsigned long* gpd0dat; 
volatile unsigned long* vidcon0; 
volatile unsigned long* vidcon1; 
volatile unsigned long* vidtcon0; 
volatile unsigned long* vidtcon1; 
volatile unsigned long* vidtcon2; 
volatile unsigned long* wincon0; 
volatile unsigned long* vidosd0a; 
volatile unsigned long* vidosd0b; 
volatile unsigned long* vidosd0c; 
volatile unsigned long* vidw00add0b0; 
volatile unsigned long* vidw00add1b0; 
volatile unsigned long* shodowcon; 
 
struct clk *lcd_clk; 
 
 
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 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_palette[regno] = val; 
        return 0; 
} 
 
static struct fb_ops lcd_fbops = { 
        .owner                = THIS_MODULE, 
        .fb_setcolreg        = lcdfb_setcolreg, 
        .fb_fillrect        = cfb_fillrect, 
        .fb_copyarea        = cfb_copyarea, 
        .fb_imageblit        = cfb_imageblit, 
}; 
 
static int lcd_init(void){ 
        int ret; 
 
        /*分配fb_info */ 
        lcd_info = framebuffer_alloc(0, NULL); 
        if(lcd_info == NULL){ 
                printk(KERN_ERR "alloc framebuffer failed!\n"); 
                return -ENOMEM; 
        } 
 
        /* 配置fb_info各成员*/ 
        /* fix */ 
        strcpy(lcd_info->fix.id, "s5pv210_lcd"); 
        lcd_info->fix.smem_len = 800*480*4; 
        lcd_info->fix.type = FB_TYPE_PACKED_PIXELS; 
        lcd_info->fix.visual = FB_VISUAL_TRUECOLOR; 
        lcd_info->fix.line_length = 800*4; 
 
        /* var */ 
        lcd_info->var.xres = 800; 
        lcd_info->var.yres = 480; 
        lcd_info->var.xres_virtual = 800; 
        lcd_info->var.yres_virtual = 480; 
        lcd_info->var.bits_per_pixel = 32; 
 
        lcd_info->var.red.offset = 16; 
        lcd_info->var.red.length = 8; 
        lcd_info->var.green.offset = 8; 
        lcd_info->var.green.length = 8; 
        lcd_info->var.blue.offset = 0; 
        lcd_info->var.blue.length = 8; 
        lcd_info->var.activate = FB_ACTIVATE_NOW; 
 
        lcd_info->screen_size = 800*480*4; 
        lcd_info->pseudo_palette = pseudo_palette; 
 
        lcd_info->fbops = &lcd_fbops; 
        /* 配置硬件资源*/ 
        /* 映射内存*/ 
        display_control = ioremap(0xe0107008,4); 
        gpf0con      = ioremap(0xE0200120, 4); 
        gpf1con      = ioremap(0xE0200140, 4); 
        gpf2con      = ioremap(0xE0200160, 4); 
        gpf3con      = ioremap(0xE0200180, 4); 
         
        gpd0con      = ioremap(0xE02000A0, 4); 
        gpd0dat      = ioremap(0xE02000A4, 4); 
         
        vidcon0      = ioremap(0xF8000000, 4); 
        vidcon1      = ioremap(0xF8000004, 4); 
        vidtcon0     = ioremap(0xF8000010, 4); 
        vidtcon1     = ioremap(0xF8000014, 4); 
        vidtcon2     = ioremap(0xF8000018, 4); 
        wincon0      = ioremap(0xF8000020, 4); 
        vidosd0a     = ioremap(0xF8000040, 4); 
        vidosd0b     = ioremap(0xF8000044, 4); 
        vidosd0c     = ioremap(0xF8000048, 4); 
        vidw00add0b0 = ioremap(0xF80000A0, 4); 
        vidw00add1b0 = ioremap(0xF80000D0, 4); 
        shodowcon    = ioremap(0xF8000034, 4); 
         
        /* 配置GPIO*/ 
        *gpf0con = 0x22222222; 
        *gpf1con = 0x22222222; 
        *gpf2con = 0x22222222; 
        *gpf3con = 0x22222222; 
        *gpd0con &= ~0xf; 
        *gpd0con |= 0x1; 
        *gpd0dat |= 1<<0; 
        *display_control = 2<<0; 
        /* 使能时钟*/ 
        lcd_clk = clk_get(NULL, "lcd"); 
        if (!lcd_clk || IS_ERR(lcd_clk)) { 
                printk(KERN_INFO "failed to get lcd clock source\n"); 
        } 
        clk_enable(lcd_clk); 
         
        /* 配置LCD控制器*/ 
        *vidcon0 = (4<<6)|(1<<4); 
        *vidcon1 = (1<<6)|(1<<5)|(1<<4); 
 
        *vidtcon0 = (17<<16)|(26<<8)|(4<<0); 
        *vidtcon1 = (40<<16)|(214<<8)|(4<<0); 
        *vidtcon2 = (479<<11)|(799<<0); 
 
        *wincon0 &= ~(0xf<<2); 
        *wincon0 |= (0xb<<2); 
 
        *vidosd0a = (0<<11)|(0<<0); 
        *vidosd0b = (799<<11)|(479<<0); 
        *vidosd0c = 480*800; 
        //物理地址 
        lcd_info->screen_base = dma_alloc_writecombine(NULL,  
                lcd_info->fix.smem_len, (dma_addr_t *)&(lcd_info->fix.smem_start), GFP_KERNEL); 
         
        *vidw00add0b0 = lcd_info->fix.smem_start; 
        *vidw00add1b0 = lcd_info->fix.smem_start + lcd_info->fix.smem_len; 
 
        *shodowcon = 0x1; 
 
        //开启状态 
        *wincon0 |= 1; 
        *vidcon0 |= 3; 
        /* 注册fb_info */ 
        ret = register_framebuffer(lcd_info); 
        return ret; 
} 
 
static void lcd_exit(void){ 
        unregister_framebuffer(lcd_info); 
        dma_free_writecombine(NULL, lcd_info->fix.smem_len,  
                (void*)lcd_info->screen_base, (dma_addr_t)lcd_info->fix.smem_start); 
 
        iounmap(shodowcon); 
        iounmap(vidw00add1b0); 
        iounmap(vidw00add0b0); 
        iounmap(vidosd0c); 
        iounmap(vidosd0b); 
        iounmap(vidosd0a); 
        iounmap(wincon0); 
        iounmap(vidtcon2); 
        iounmap(vidtcon1); 
        iounmap(vidtcon0); 
        iounmap(vidcon1); 
        iounmap(vidcon0); 
        iounmap(gpd0dat); 
        iounmap(gpd0con); 
        iounmap(gpf3con); 
        iounmap(gpf2con); 
        iounmap(gpf1con); 
        iounmap(gpf0con); 
        framebuffer_release(lcd_info); 
} 
 
module_init(lcd_init); 
module_exit(lcd_exit); 
MODULE_LICENSE("GPL");  
将上面的代码在自己的内核环境下编译,然后下载到开发板上试运行即可。 
  
在安装驱动程序前执行指令: 
 ls /dev/fb*如果有fb0或者其他fb*存在,应该修改内和配置,取消其他fb的配置,如果看不到fb*设备,则可以按照如下步骤进行测试。 
 测试前还需要修改下内核配置,有两个原因:  
 
(1) 内核默认配置下不支持Frame buffer 
  
(2) 我们的驱动程序中用到了三个函数: 
  
        .fb_fillrect        = cfb_fillrect, 
        .fb_copyarea        = cfb_copyarea, 
        .fb_imageblit        = cfb_imageblit,这三个函数是引用的内核中的函数,不是我们自行实现的。  
 
鉴于上面两个原因,我们需要配置内核支持Frame buffer和列举出的三个函数,另外,内核中并没有直接配置支持这三个函数的选项,权宜之计,修改下drivers/video目录下的Kconfig文件,在config FB项中添加 
  
select FB_CFB_FILLRECT 
select FB_CFB_COPYAREA 
select FB_CFB_IMAGEBLIT添加时一定保证格式正确,参考下该文件下的其他配置项即可。配置完成后执行make menuconfig作如下配置:Device Drivers  ---> 
        Graphics support  ---> 
                <*> Support for frame buffer devices  --->配置后保存配置,编译内核并将编译好的内核下载到开发板或者NFS运行,同时将编译好的LCD驱动程序拷贝到开发板运行环境中进行安装,如果驱动文件名为lcd.ko,则执行:insmod lcd.ko这时,你可以看到屏幕被重新初始化了。虽然LCD已经初始化了,但是不知道如何进行测试,可以按照韦东山老师视频中讲述的方法进行LCD驱动测试,但是我们移植的3.8.3内核默认不支持字库,还需要作其他配置,我是用画线的方式测试的屏幕,这里我讲一下我用的测试方法: 
  
(1) 在Linux主机上编译下面的C++程序 
 #include <iostream> 
 
unsigned long buffer[480][800] = {0}; 
 
void put_long_hex(unsigned long v){  
    for(int i = 0; i != 4; ++i){ 
        std::cout.put(static_cast<char>(0xff&(v>>(8*(3-i))))); 
    }    
} 
 
int main(){ 
    for(int i = 0; i != 480; ++i){ 
        buffer[0]   = 0x00ff0000; 
        buffer[799] = 0x0000ff00; 
    }    
 
    for(int i = 0; i != 800; ++i){ 
        buffer[0]   = 0xff000000; 
        buffer[479] = 0x00ffff00; 
    }    
 
    for(int i = 0; i != 480; ++i){ 
        for(int j = 0; j != 800; ++j){ 
            put_long_hex(buffer[j]);  
        }    
    }    
}编译指令如下:g++ -o main main.cpp然后如下方式执行程序:./main > /nfsroot/rootfs/test.img我是直接将文件生成在NFS的根文件系统下了,你也可以用其他方式将生成的文件拷贝到开发板运行环境内,然后执行如下指令:cat test.img > /dev/fb0这时,就可以在屏幕上看到一个矩形且矩形的四条边颜色不相同。  
 
如果想将驱动编译进内核,并在启动时可以看到小企鹅,可以将上面的驱动拷贝到内核的drivers/video/目录下,命名为tq210_fb.c,然后在该目录下做如下修改: 
  
(1)修改Kconfig,添加TQ210的LCD驱动配置选项 
  
在config FB_S4C项的后面添加如下内容: 
 config FB_TQ210 
    tristate "TQ210 lcd support" 
    depends on FB 
    select FB_CFB_FILLRECT 
    select FB_CFB_COPYAREA 
    select FB_CFB_IMAGEBLIT 
    ---help--- 
    Currently the suport is only for the TQ210(2)修改Makefile,添加如下内容:obj-$(CONFIG_FB_TQ210)        += tq210_fb.o  
(3)退回到内核根目录下,执行make menuconfig并按如下方式配置内核 
 Device Drivers  ---> 
        Graphics support  ---> 
                <*> Support for frame buffer devices  ---> 
                        <*>   TQ210 lcd support 
                 Bootup logo  ---> 
                           Standard black and white Linux logo  
                           Standard 16-color Linux logo 
                           Standard 224-color Linux logo  
 
 
 
然后执行指令make zImage或者make uImage来编译内核,将编译好的内核烧写到开发板或者是放到NFS下即可正常运行。  
 
转自:http://www.myexception.cn/mobile/1258956.html 
 |   
 
 
 
 |