您的位置:首页 > 其它

S5PV210(TQ210)学习笔记——LCD驱动编写

2014-07-12 02:37 369 查看
转载:/article/1388410.html

网上S5PV210内核移植的文章不是很多,而描述2440和6410内核移植的文章多数是讲如何移植,而非手动编写,但是,韦东山老师的视频中讲述了如何从头编写LCD驱动,当然是以2440为例的,我看过视频之后在TQ210平台上进行了实验,实验成功,详细的原理部分以我现在的水平还难以表达清楚。下载是我自己写的代码,适用于TQ210的7寸电容屏。

[cpp] view
plaincopy





#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");

将上面的代码在自己的内核环境下编译,然后下载到开发板上试运行即可。

在安装驱动程序前执行指令:

[cpp] view
plaincopy





ls /dev/fb*

如果有fb0或者其他fb*存在,应该修改内和配置,取消其他fb的配置,如果看不到fb*设备,则可以按照如下步骤进行测试。

测试前还需要修改下内核配置,有两个原因:

(1) 内核默认配置下不支持Frame buffer

(2) 我们的驱动程序中用到了三个函数:

[cpp] view
plaincopy





.fb_fillrect = cfb_fillrect,

.fb_copyarea = cfb_copyarea,

.fb_imageblit = cfb_imageblit,

这三个函数是引用的内核中的函数,不是我们自行实现的。

鉴于上面两个原因,我们需要配置内核支持Frame buffer和列举出的三个函数,另外,内核中并没有直接配置支持这三个函数的选项,权宜之计,修改下drivers/video目录下的Kconfig文件,在config FB项中添加

[cpp] view
plaincopy





select FB_CFB_FILLRECT

select FB_CFB_COPYAREA

select FB_CFB_IMAGEBLIT

添加时一定保证格式正确,参考下该文件下的其他配置项即可。配置完成后执行make menuconfig作如下配置:

[cpp] view
plaincopy





Device Drivers --->

Graphics support --->

<*> Support for frame buffer devices --->

配置后保存配置,编译内核并将编译好的内核下载到开发板或者NFS运行,同时将编译好的LCD驱动程序拷贝到开发板运行环境中进行安装,如果驱动文件名为lcd.ko,则执行:

[cpp] view
plaincopy





insmod lcd.ko

这时,你可以看到屏幕被重新初始化了。虽然LCD已经初始化了,但是不知道如何进行测试,可以按照韦东山老师视频中讲述的方法进行LCD驱动测试,但是我们移植的3.8.3内核默认不支持字库,还需要作其他配置,我是用画线的方式测试的屏幕,这里我讲一下我用的测试方法:

(1) 在Linux主机上编译下面的C++程序

[cpp] view
plaincopy





#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[i][0] = 0x00ff0000;

buffer[i][799] = 0x0000ff00;

}

for(int i = 0; i != 800; ++i){

buffer[0][i] = 0xff000000;

buffer[479][i] = 0x00ffff00;

}

for(int i = 0; i != 480; ++i){

for(int j = 0; j != 800; ++j){

put_long_hex(buffer[i][j]);

}

}

}

编译指令如下:

[cpp] view
plaincopy





g++ -o main main.cpp

然后如下方式执行程序:

[cpp] view
plaincopy





./main > /nfsroot/rootfs/test.img

我是直接将文件生成在NFS的根文件系统下了,你也可以用其他方式将生成的文件拷贝到开发板运行环境内,然后执行如下指令:

[cpp] view
plaincopy





cat test.img > /dev/fb0

这时,就可以在屏幕上看到一个矩形且矩形的四条边颜色不相同。

如果想将驱动编译进内核,并在启动时可以看到小企鹅,可以将上面的驱动拷贝到内核的drivers/video/目录下,命名为tq210_fb.c,然后在该目录下做如下修改:

(1)修改Kconfig,添加TQ210的LCD驱动配置选项

在config FB_S4C项的后面添加如下内容:

[cpp] view
plaincopy





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,添加如下内容:

[cpp] view
plaincopy





obj-$(CONFIG_FB_TQ210) += tq210_fb.o

(3)退回到内核根目录下,执行make menuconfig并按如下方式配置内核

[cpp] view
plaincopy





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下即可正常运行。

如果您在开发或配置过程遇到什么问题可以留言讨论。

本文链接:/article/1388410.html

本文作者:girlkoo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: