要想使LCD工作,需要修改arch/arm/mach-s3c24xx/Mach-zhaocj2440.c文件中的相关内容。
zhaocj2440_lcd_cfg结构数组定义了各种LCD的时序及配置,我们需要在这个数组的末端添加针对自己开发板上的LCD的相关配置。
static struct s3c2410fb_display zhaocj2440_lcd_cfg[] __initdata = {
……
/*zhaocj2440 + 3.5" TFT (WXCAT35-TG3) + touchscreen*/
[4] = {
_LCD_DECLARE(
/* clock */
7,
/* xres, margin_right, margin_left, hsync */
320, 20, 38, 30,
/* yres, margin_top, margin_bottom, vsync */
240, 12, 15, 3,
/* refresh rate */
64),
.bpp = 32,
.type =(S3C2410_LCDCON1_TFT24BPP| S3C2410_LCDCON1_TFT),
.lcdcon5 = (S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_INVVLINE|
S3C2410_LCDCON5_PWREN),
},
};
下面再来修改zhaocj2440_features_str数组,该数组定义了LCD、背光、触摸屏和摄像信息,数字代表的是应用zhaocj2440_lcd_cfg数组中定义的第几个LCD,b代表背光,t代表触摸屏,c代表摄像。在这里把这个数组赋值为4t,即:
static char zhaocj2440_features_str[12]__initdata = "4t";
通过上面两个步骤的修改,就完成了LCD的添加,编译后,烧写进开发板即可。因为系统是默认配置LCD,并且默认配置了开机显示logo,因此启动系统后,会在LCD的左上角显示企鹅的logo,而且在这个logo的左下角会有一个光标在不停的闪。如果我们执行下列命令,则起到了清屏作用,其中/dev/fb0就是帧缓冲设备,也就是我们开发板上的LCD设备:
[root@zhaocj /]# dd if=/dev/zero of=/dev/fb0
下面就具体介绍一下LCD的驱动内容。
下面的宏定义是为了简化LCD的时序配置而定义的,zhaocj2440_lcd_cfg数组就用到了该宏,因为通过该宏定义,可以很容易的为s3c2410fb_display结构中的成员赋值,尤其是可以自动计算pixclock成员的值。不同的LCD,其时序是完全不同的,具体的参数要参考LCD的数据手册。
#define_LCD_DECLARE(_clock,_xres,margin_left,margin_right,hsync, \
_yres,margin_top,margin_bottom,vsync,refresh) \
.width= _xres, \ //水平分辨率,也就是LCD的宽
.xres= _xres, \
.height= _yres, \ //垂直分辨率,也就是LCD的高
.yres= _yres, \
.left_margin = margin_left, \ //左侧行切换的回扫时间
.right_margin = margin_right, \ //右侧行切换的回扫时间
.upper_margin = margin_top, \ //上侧帧切换的回扫时间
.lower_margin = margin_bottom, \ //下侧帧切换的回扫时间
.hsync_len = hsync, \ //水平同步的长度
.vsync_len = vsync, \ //垂直同步的长度
.pixclock = ((_clock*100000000000LL) / \
((refresh) * \ //场频,即刷新率,
(hsync + margin_left + _xres + margin_right)* \ //水平周期
(vsync + margin_top + _yres +margin_bottom))), \ //垂直周期
.bpp = 16,\ //每个像素的位数
.type = (S3C2410_LCDCON1_TFT16BPP |\
S3C2410_LCDCON1_TFT) //LCD的类型
在上面的LCD移植过程中,我们改变了宏定义中的bpp和type的默认值,原宏定义的颜色值为16BPP,而我们移植的颜色值为24BPP。
接着往下看,zhaocj2440_fb_info结构定义了LCD的配置,包括前面介绍的LCD的时序配置,以及s3c2440相关LCD引脚的配置。
前面在移植的过程中,介绍了zhaocj2440_features_str数组,以及各个字符的含义。而函数zhaocj2440_parse_features的作用就是根据上面的数组来配置zhaocj2440_features_t结构,并逐一给出各个平台设备。在zhaocj2440_parse_features函数内,通过下列语句给出了LCD平台设备:
features->optional[features->count++]= &s3c_device_lcd;
LCD平台设备是在Devs.c文件(arch/arm/plat-samsung目录下)内定义的:
struct platform_device s3c_device_lcd = {
.name = "s3c2410-lcd",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_lcd_resource),
.resource = s3c_lcd_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
}
};
zhaocj2440_init函数的作用是对开发板上的资源进行初始化,当然也包括了对LCD的初始化,而且通过下列语句把LCD这个平台设备添加到了平台设备总线上,并进行了注册:
platform_add_devices(features.optional,features.count);
上面主要介绍了LCD的平台设备,下面介绍LCD的平台驱动。在S3c2410fb.c文件(drivers/video目录下)内定义的。
我们先来给出三个很重要的结构:
struct fb_info {
atomic_tcount;
intnode;
intflags;
structmutex lock; /* Lock foropen/release/ioctl funcs */
structmutex mm_lock; /* Lock for fb_mmapand smem_* fields */
structfb_var_screeninfo var; /* Current var */ //可变参数
structfb_fix_screeninfo fix; /* Current fix */ //固定参数
structfb_monspecs monspecs; /* CurrentMonitor specs */ //显示器标准
structwork_struct queue; /* Framebufferevent queue */ //帧缓冲事件队列
structfb_pixmap pixmap; /* Image hardwaremapper */ //图像硬件mapper
structfb_pixmap sprite; /* Cursor hardwaremapper */ //光标硬件mapper
structfb_cmap cmap; /* Current cmap*/ //颜色表
structlist_head modelist; /* mode list */
struct fb_videomode *mode; /* current mode */ //视频模式
#ifdef CONFIG_FB_BACKLIGHT
/*assigned backlight device */
/*set before framebuffer registration,
remove after unregister */
structbacklight_device *bl_dev; //背光设备
/*Backlight level curve */ //背光调整
structmutex bl_curve_mutex;
u8bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
structdelayed_work deferred_work;
structfb_deferred_io *fbdefio;
#endif
structfb_ops *fbops; //帧缓冲操作
structdevice *device; /* This is theparent */
structdevice *dev; /* This is this fbdevice */
int class_flag; /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
structfb_tile_ops *tileops; /* Tile Blitting*/ //图块Blitting
#endif
char__iomem *screen_base; /* Virtual address*/ //虚拟基地址
unsignedlong screen_size; /* Amount ofioremapped VRAM or 0 */ //内存大小
void*pseudo_palette; /* Fake paletteof 16 colors */ //伪16色颜色表
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32state; /* Hardware state i.esuspend */
void*fbcon_par; /* fbconuse-only private area */
/*From here on everything is device dependent */
void*par;
/*we need the PCI or similar aperture base/size not
smem_start/size as smem_start may just be anobject
allocated inside the aperture so may notactually overlap */
structapertures_struct {
unsignedint count;
structaperture {
resource_size_tbase;
resource_size_tsize;
}ranges[0];
}*apertures;
};
struct fb_var_screeninfo {
__u32xres; /* visibleresolution */ //可见分辨率
__u32yres;
__u32xres_virtual; /* virtual resolution */ //虚拟分辨率
__u32yres_virtual;
__u32xoffset; /* offset fromvirtual to visible */ //虚拟到可见之间的偏移
__u32yoffset; /* resolution */
__u32bits_per_pixel; /* guess what */ //每个像素的位数,BPP
__u32grayscale; /* 0 = color, 1 =grayscale, */ //非0为灰度
/*>1 = FOURCC */
structfb_bitfield red; /* bitfield infb mem if true color, */
structfb_bitfield green; /* else only length issignificant */
structfb_bitfield blue;
structfb_bitfield transp; /* transparency */ //透明度
__u32nonstd; /* != 0 Non standardpixel format */ //非0为非标准像素格式
__u32activate; /* seeFB_ACTIVATE_* */
__u32height; /* height ofpicture in mm */ //高
__u32width; /* width of picturein mm */ //宽
__u32accel_flags; /* (OBSOLETE) seefb_info.flags */
/*Timing: All values in pixclocks, except pixclock (of course) */
__u32pixclock; /* pixel clockin ps (pico seconds) */
__u32left_margin; /* time from sync topicture */
__u32right_margin; /* time frompicture to sync */
__u32upper_margin; /* time from syncto picture */
__u32lower_margin;
__u32hsync_len; /* length ofhorizontal sync */
__u32vsync_len; /* length of verticalsync */
__u32sync; /* see FB_SYNC_* */
__u32vmode; /* see FB_VMODE_* */
__u32rotate; /* angle we rotatecounter clockwise */
__u32colorspace; /* colorspace forFOURCC-based modes */ //顺时针旋转的角度
__u32reserved[4]; /* Reserved for futurecompatibility */
};
struct fb_fix_screeninfo {
charid[16]; /* identificationstring eg "TT Builtin" */ //字符串形式的标识符
unsignedlong smem_start; /* Start of framebuffer mem */ //帧缓冲的开始位置
/*(physical address) */
__u32smem_len; /* Length offrame buffer mem */ //帧缓冲长度
__u32type; /* see FB_TYPE_* */
__u32type_aux; /* Interleavefor interleaved Planes */
__u32visual; /* see FB_VISUAL_* */
__u16xpanstep; /* zero if nohardware panning */
__u16ypanstep; /* zero if nohardware panning */
__u16ywrapstep; /* zero if no hardwareywrap */
__u32line_length; /* length of a linein bytes */ //1行的字节数
unsignedlong mmio_start; /* Start of MemoryMapped I/O */
/*(physical address) */
__u32mmio_len; /* Length ofMemory Mapped I/O */
__u32accel; /* Indicate todriver which */
/* specific chip/card we have */
__u16capabilities; /* see FB_CAP_* */
__u16reserved[2]; /* Reserved for futurecompatibility */
};
再回过头来介绍S3c2410fb.c文件的内容。在s3c2410fb_init函数内通过platform_driver_register函数注册平台驱动s3c2410fb_driver,该平台驱动就是与上面介绍的LCD平台设备相匹配的平台驱动。当设备和驱动匹配上后,系统会调用s3c24xxfb_probe函数:
static int __devinit s3c24xxfb_probe(struct platform_device *pdev,
enum s3c_drv_type drv_type)
{
structs3c2410fb_info *info;
structs3c2410fb_display *display;
structfb_info *fbinfo;
structs3c2410fb_mach_info *mach_info;
structresource *res;
intret;
intirq;
inti;
intsize;
u32lcdcon1;
//得到zhaocj2440_fb_info结构,即配置LCD的所有数据
mach_info= pdev->dev.platform_data;
if(mach_info == NULL) {
dev_err(&pdev->dev,
"noplatform data for lcd, cannot attach\n");
return-EINVAL;
}
if(mach_info->default_display >= mach_info->num_displays) {
dev_err(&pdev->dev,"default is %d but only %d displays\n",
mach_info->default_display,mach_info->num_displays);
return-EINVAL;
}
//得到zhaocj2440_lcd_cfg,即配置LCD时序等的数据
display= mach_info->displays + mach_info->default_display;
//获取中断号
irq= platform_get_irq(pdev, 0);
if(irq < 0) {
dev_err(&pdev->dev,"no irq for device\n");
return-ENOENT;
}
//为帧缓冲分配空间,得到fb_info结构
fbinfo= framebuffer_alloc(sizeof(struct s3c2410fb_info),&pdev->dev);
if(!fbinfo)
return-ENOMEM;
//把fb_info作为平台设备的私有数据
platform_set_drvdata(pdev,fbinfo);
info= fbinfo->par; //获得fb_info的私有数据
info->dev= &pdev->dev; //获得平台设备
info->drv_type= drv_type; //该LCD驱动为兼容S3C2410的
//得到该设备的内存资源
res= platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(res == NULL) {
dev_err(&pdev->dev,"failed to get memory registers\n");
ret= -ENXIO;
gotodealloc_fb;
}
//得到内存大小
size= resource_size(res);
//申请内存区
info->mem= request_mem_region(res->start, size, pdev->name);
if(info->mem == NULL) {
dev_err(&pdev->dev,"failed to get memory region\n");
ret= -ENOENT;
gotodealloc_fb;
}
//把内存物理地址转换为虚拟地址
info->io= ioremap(res->start, size);
if(info->io == NULL) {
dev_err(&pdev->dev,"ioremap() of registers failed\n");
ret= -ENXIO;
gotorelease_mem;
}
//基址
if(drv_type == DRV_S3C2412)
info->irq_base = info->io + S3C2412_LCDINTBASE;
else
info->irq_base = info->io +S3C2410_LCDINTBASE;
dprintk("devinit\n");
//复制驱动名——s3c2410fb
strcpy(fbinfo->fix.id,driver_name);
/*Stop the video */
//禁止LCD显示
lcdcon1= readl(info->io + S3C2410_LCDCON1);
writel(lcdcon1& ~S3C2410_LCDCON1_ENVID,info->io + S3C2410_LCDCON1);
//为LCD的固定参数赋值
fbinfo->fix.type =FB_TYPE_PACKED_PIXELS;
fbinfo->fix.type_aux =0;
fbinfo->fix.xpanstep = 0;
fbinfo->fix.ypanstep = 0;
fbinfo->fix.ywrapstep =0;
fbinfo->fix.accel =FB_ACCEL_NONE;
//为LCD的可变参数赋值
fbinfo->var.nonstd = 0;
fbinfo->var.activate = FB_ACTIVATE_NOW;
fbinfo->var.accel_flags = 0;
fbinfo->var.vmode =FB_VMODE_NONINTERLACED;
fbinfo->fbops = &s3c2410fb_ops; //LCD的操作函数
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette = &info->pseudo_pal;
//初始化调色板,即清空
for (i = 0; i < 256; i++)
info->palette_buffer[i] =PALETTE_BUFF_CLEAR;
//申请中断,中断函数为s3c2410fb_irq
ret =request_irq(irq, s3c2410fb_irq,0, pdev->name, info);
if(ret) {
dev_err(&pdev->dev,"cannot get irq %d - err %d\n", irq, ret);
ret= -EBUSY;
gotorelease_regs;
}
//获得时钟
info->clk= clk_get(NULL, "lcd");
if(IS_ERR(info->clk)) {
printk(KERN_ERR"failed to get lcd clock source\n");
ret = PTR_ERR(info->clk);
gotorelease_irq;
}
//打开时钟
clk_enable(info->clk);
dprintk("gotand enabled clock\n");
usleep_range(1000,1000);
//得到时钟速率
info->clk_rate= clk_get_rate(info->clk);
/*find maximum required memory size for display */
//得到所需要的最大的显存大小
for(i = 0; i < mach_info->num_displays; i++) {
unsignedlong smem_len = mach_info->displays[i].xres;
smem_len*= mach_info->displays[i].yres;
smem_len*= mach_info->displays[i].bpp;
smem_len>>= 3; //除以8,即单位为字节
if(fbinfo->fix.smem_len < smem_len)
fbinfo->fix.smem_len= smem_len;
}
/*Initialize video memory */
//为帧缓冲分配内存空间
ret= s3c2410fb_map_video_memory(fbinfo);
if(ret) {
printk(KERN_ERR"Failed to allocate video RAM: %d\n", ret);
ret= -ENOMEM;
gotorelease_clock;
}
dprintk("gotvideo memory\n");
fbinfo->var.xres= display->xres; //水平分辨率
fbinfo->var.yres= display->yres; //垂直分辨率
fbinfo->var.bits_per_pixel= display->bpp; //用几位来表示一个像素
//初始化所有与LCD有关的寄存器
s3c2410fb_init_registers(fbinfo);
//检查LCD的可变参数
s3c2410fb_check_var(&fbinfo->var, fbinfo);
ret = s3c2410fb_cpufreq_register(info);
if(ret < 0) {
dev_err(&pdev->dev,"Failed to register cpufreq\n");
gotofree_video_memory;
}
//注册帧缓冲设备
ret= register_framebuffer(fbinfo);
if(ret < 0) {
printk(KERN_ERR"Failed to register framebuffer device: %d\n",
ret);
gotofree_cpufreq;
}
/*create device files */
ret= device_create_file(&pdev->dev, &dev_attr_debug);
if(ret)
printk(KERN_ERR"failed to add debug attribute\n");
printk(KERN_INFO"fb%d: %s frame buffer device\n",
fbinfo->node,fbinfo->fix.id);
return0;
free_cpufreq:
s3c2410fb_cpufreq_deregister(info);
free_video_memory:
s3c2410fb_unmap_video_memory(fbinfo);
release_clock:
clk_disable(info->clk);
clk_put(info->clk);
release_irq:
free_irq(irq,info);
release_regs:
iounmap(info->io);
release_mem:
release_mem_region(res->start,size);
dealloc_fb:
platform_set_drvdata(pdev,NULL);
framebuffer_release(fbinfo);
returnret;
}
在上面的函数中,指定了LCD的操作集s3c2410fb_ops,即:
static struct fb_ops s3c2410fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = s3c2410fb_check_var, //检查可变参数,并调整到支持的值
.fb_set_par = s3c2410fb_set_par, //设置参数
.fb_blank = s3c2410fb_blank, //显示空白
.fb_setcolreg = s3c2410fb_setcolreg, //设置颜色寄存器
.fb_fillrect = cfb_fillrect, //矩形填充
.fb_copyarea = cfb_copyarea, //数据复制
.fb_imageblit = cfb_imageblit, //绘制图形
};
对于上面的LCD操作函数中,我们只介绍最重要的两个函数——s3c2410fb_check_var和s3c2410fb_set_par。
static int s3c2410fb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
structs3c2410fb_info *fbi =info->par;
structs3c2410fb_mach_info*mach_info = fbi->dev->platform_data;
structs3c2410fb_display *display= NULL;
structs3c2410fb_display*default_display = mach_info->displays +
mach_info->default_display;
inttype = default_display->type;
unsigned i;
dprintk("check_var(var=%p,info=%p)\n", var, info);
/* validate x/yresolution */
/*choose default mode if possible */
//检验水平、垂直分辨率,以及bpp
//先检验缺省的LCD,不行再检验其他
if(var->yres == default_display->yres &&
var->xres == default_display->xres &&
var->bits_per_pixel== default_display->bpp)
display= default_display;
else
for(i = 0; i < mach_info->num_displays; i++)
if(type == mach_info->displays[i].type &&
var->yres ==mach_info->displays[i].yres &&
var->xres == mach_info->displays[i].xres&&
var->bits_per_pixel ==mach_info->displays[i].bpp) {
display= mach_info->displays + i;
break;
}
if(!display) {
dprintk("wrongresolution or depth %dx%d at %d bpp\n",
var->xres, var->yres,var->bits_per_pixel);
return-EINVAL;
}
/*it is always the size as the display */
//为水平、垂直分辨率,和高、宽赋值
var->xres_virtual= display->xres;
var->yres_virtual= display->yres;
var->height= display->height;
var->width= display->width;
/*copy lcd settings */
//为LCD的其他时序赋值
var->pixclock= display->pixclock;
var->left_margin= display->left_margin;
var->right_margin= display->right_margin;
var->upper_margin =display->upper_margin;
var->lower_margin= display->lower_margin;
var->vsync_len =display->vsync_len;
var->hsync_len =display->hsync_len;
fbi->regs.lcdcon5 =display->lcdcon5;
/* set displaytype */
fbi->regs.lcdcon1= display->type;
var->transp.offset = 0;
var->transp.length = 0;
/* set r/g/bpositions */
//设置红、绿、蓝三色的位置
switch(var->bits_per_pixel) {
case1:
case2:
case4:
var->red.offset = 0;
var->red.length = var->bits_per_pixel;
var->green = var->red;
var->blue = var->red;
break;
case8:
if(display->type != S3C2410_LCDCON1_TFT){
/*8 bpp 332 */
var->red.length = 3;
var->red.offset = 5;
var->green.length = 3;
var->green.offset = 2;
var->blue.length = 2;
var->blue.offset = 0;
} else {
var->red.offset = 0;
var->red.length = 8;
var->green = var->red;
var->blue = var->red;
}
break;
case 12:
/* 12 bpp 444 */
var->red.length = 4;
var->red.offset = 8;
var->green.length = 4;
var->green.offset = 4;
var->blue.length = 4;
var->blue.offset = 0;
break;
default:
case 16:
if(display->lcdcon5 & S3C2410_LCDCON5_FRM565){
/* 16 bpp, 565 format */
var->red.offset = 11; //偏移量
var->green.offset = 5;
var->blue.offset = 0;
var->red.length = 5; //长度
var->green.length = 6;
var->blue.length = 5;
} else {
/* 16 bpp, 5551 format */
var->red.offset = 11;
var->green.offset = 6;
var->blue.offset = 1;
var->red.length = 5;
var->green.length = 5;
var->blue.length = 5;
}
break;
case32:
/*24 bpp 888 and 8 dummy */
var->red.length = 8;
var->red.offset = 16;
var->green.length = 8;
var->green.offset = 8;
var->blue.length = 8;
var->blue.offset = 0;
break;
}
return0;
}
在s3c2410fb_set_par函数中,最主要的作用是调用了s3c2410fb_activate_var函数:
static void s3c2410fb_activate_var(struct fb_info *info)
{
structs3c2410fb_info *fbi =info->par;
void __iomem *regs = fbi->io;
int type =fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
struct fb_var_screeninfo *var =&info->var;
int clkdiv;
//计算clkdiv
clkdiv= DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi,var->pixclock), 2);
dprintk("%s:var->xres = %d\n", __func__,var->xres);
dprintk("%s: var->yres = %d\n", __func__, var->yres);
dprintk("%s: var->bpp = %d\n", __func__,var->bits_per_pixel);
//依据是TFT还是STN,来设置LCD控制寄存器1~4
if (type == S3C2410_LCDCON1_TFT) {
s3c2410fb_calculate_tft_lcd_regs(info,&fbi->regs);
--clkdiv;
if(clkdiv < 0)
clkdiv= 0;
}else {
s3c2410fb_calculate_stn_lcd_regs(info,&fbi->regs);
if(clkdiv < 2)
clkdiv= 2;
}
//设置CLKVAL的值
fbi->regs.lcdcon1|= S3C2410_LCDCON1_CLKVAL(clkdiv);
/*write new registers */
dprintk("newregister set:\n");
dprintk("lcdcon[1]= 0x%08lx\n", fbi->regs.lcdcon1);
dprintk("lcdcon[2]= 0x%08lx\n", fbi->regs.lcdcon2);
dprintk("lcdcon[3]= 0x%08lx\n", fbi->regs.lcdcon3);
dprintk("lcdcon[4]= 0x%08lx\n", fbi->regs.lcdcon4);
dprintk("lcdcon[5]= 0x%08lx\n", fbi->regs.lcdcon5);
//向LCD控制寄存器中写新值
writel(fbi->regs.lcdcon1& ~S3C2410_LCDCON1_ENVID,
regs+ S3C2410_LCDCON1);
writel(fbi->regs.lcdcon2,regs + S3C2410_LCDCON2);
writel(fbi->regs.lcdcon3,regs + S3C2410_LCDCON3);
writel(fbi->regs.lcdcon4,regs + S3C2410_LCDCON4);
writel(fbi->regs.lcdcon5,regs + S3C2410_LCDCON5);
/*set lcd address pointers */
//设置帧缓冲地址寄存器
s3c2410fb_set_lcdaddr(info);
//使能LCD
fbi->regs.lcdcon1|= S3C2410_LCDCON1_ENVID,
writel(fbi->regs.lcdcon1,regs + S3C2410_LCDCON1);
}
我们再来看LCD的文件层程序fbmem.c(在drivers/video目录下),系统把帧缓冲设备看做一个字符设备,其操作函数集为:
static const struct file_operations fb_fops= {
.owner= THIS_MODULE,
.read= fb_read, //读
.write= fb_write, //写
.unlocked_ioctl= fb_ioctl, //控制
#ifdef CONFIG_COMPAT
.compat_ioctl= fb_compat_ioctl,
#endif
.mmap= fb_mmap, //映射
.open= fb_open, //打开
.release= fb_release, //释放
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area= get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync= fb_deferred_io_fsync,
#endif
.llseek= default_llseek,
};
在这里,我们只介绍帧缓冲控制函数fb_ioctl,在该函数中,主要调用了do_fb_ioctl函数,了解了该函数对写应用程序很有帮助:
static long do_fb_ioctl(struct fb_info*info, unsigned int cmd,
unsignedlong arg)
{
structfb_ops *fb;
structfb_var_screeninfo var;
structfb_fix_screeninfo fix;
structfb_con2fbmap con2fb;
structfb_cmap cmap_from;
structfb_cmap_user cmap;
structfb_event event;
void__user *argp = (void __user *)arg;
longret = 0;
switch(cmd) {
caseFBIOGET_VSCREENINFO: //得到可变参数数据
if(!lock_fb_info(info))
return -ENODEV;
var = info->var;
unlock_fb_info(info);
ret= copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
break;
caseFBIOPUT_VSCREENINFO: //设置可变参数数据
if(copy_from_user(&var, argp, sizeof(var)))
return-EFAULT;
if(!lock_fb_info(info))
return-ENODEV;
console_lock();
info->flags|= FBINFO_MISC_USEREVENT;
//下面函数调用了s3c2410fb_check_var函数和s3c2410fb_set_par函数
ret = fb_set_var(info, &var);
info->flags &=~FBINFO_MISC_USEREVENT;
console_unlock();
unlock_fb_info(info);
if(!ret && copy_to_user(argp, &var, sizeof(var)))
ret= -EFAULT;
break;
caseFBIOGET_FSCREENINFO: //得到固定参数数据
if(!lock_fb_info(info))
return-ENODEV;
fix= info->fix;
unlock_fb_info(info);
ret= copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
break;
caseFBIOPUTCMAP: //设置颜色表
if(copy_from_user(&cmap, argp, sizeof(cmap)))
return-EFAULT;
//下面函数调用了s3c2410fb_setcolreg函数
ret= fb_set_user_cmap(&cmap, info);
break;
caseFBIOGETCMAP: //得到颜色表
if(copy_from_user(&cmap, argp, sizeof(cmap)))
return-EFAULT;
if(!lock_fb_info(info))
return-ENODEV;
cmap_from= info->cmap;
unlock_fb_info(info);
ret= fb_cmap_to_user(&cmap_from, &cmap);
break;
caseFBIOPAN_DISPLAY: //PAN显示
if(copy_from_user(&var, argp, sizeof(var)))
return-EFAULT;
if(!lock_fb_info(info))
return-ENODEV;
console_lock();
ret= fb_pan_display(info, &var);
console_unlock();
unlock_fb_info(info);
if(ret == 0 && copy_to_user(argp, &var, sizeof(var)))
return-EFAULT;
break;
caseFBIO_CURSOR: //光标
ret= -EINVAL;
break;
caseFBIOGET_CON2FBMAP:
if(copy_from_user(&con2fb, argp, sizeof(con2fb)))
return -EFAULT;
if (con2fb.console < 1 ||con2fb.console > MAX_NR_CONSOLES)
return -EINVAL;
con2fb.framebuffer = -1;
event.data = &con2fb;
if(!lock_fb_info(info))
return-ENODEV;
event.info= info;
fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP,&event);
unlock_fb_info(info);
ret= copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
break;
caseFBIOPUT_CON2FBMAP:
if(copy_from_user(&con2fb, argp, sizeof(con2fb)))
return -EFAULT;
if (con2fb.console < 1 ||con2fb.console > MAX_NR_CONSOLES)
return -EINVAL;
if (con2fb.framebuffer < 0 ||con2fb.framebuffer >= FB_MAX)
return-EINVAL;
if(!registered_fb[con2fb.framebuffer])
request_module("fb%d",con2fb.framebuffer);
if(!registered_fb[con2fb.framebuffer]) {
ret= -EINVAL;
break;
}
event.data= &con2fb;
if(!lock_fb_info(info))
return-ENODEV;
event.info= info;
ret= fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
unlock_fb_info(info);
break;
caseFBIOBLANK: //显示空白
if(!lock_fb_info(info))
return-ENODEV;
console_lock();
info->flags|= FBINFO_MISC_USEREVENT;
//下面的函数调用了s3c2410fb_blank函数
ret = fb_blank(info, arg);
info->flags &=~FBINFO_MISC_USEREVENT;
console_unlock();
unlock_fb_info(info);
break;
default:
if(!lock_fb_info(info))
return-ENODEV;
fb= info->fbops;
if(fb->fb_ioctl)
ret= fb->fb_ioctl(info, cmd, arg);
else
ret= -ENOTTY;
unlock_fb_info(info);
}
returnret;
}
最后,我们给出一段完整的对LCD操作的应用程序:
#include<unistd.h>
#include<stdio.h>
#include<fcntl.h>
#include<linux/fb.h>
#include<sys/mman.h>
int main(void)
{
int fd_fb = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screen_size = 0;
unsigned long *fbp32 = NULL;
int x = 0, y = 0;
fd_fb = open("/dev/fb0", O_RDWR);
if (!fd_fb)
{
printf("Error: cannot open framebuffer device.\n");
return(1);
}
// Get fixed screen info
if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &finfo))
{
printf("Error reading fixed information.\n");
return(2);
}
// Get variable screen info
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &vinfo))
{
printf("Error 2 reading variable information.\n");
return(3);
}
// the size of the screen in bytes
screen_size = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
printf("%dx%d, %dbpp, screen_size = %d\n", vinfo.xres, vinfo.yres,vinfo.bits_per_pixel, screen_size );
// map framebuffer to user memory
fbp32 = (unsigned long *)mmap(0, screen_size, PROT_READ | PROT_WRITE,MAP_SHARED, fd_fb, 0);
if ((int)fbp32 == -1)
{
printf("Error: failed to map framebuffer device tomemory.\n");
return(4);
}
if(vinfo.bits_per_pixel == 32)
{
printf("24 bpp framebuffer\n");
// Red Screen
printf("Red Screen\n");
for(y = 0; y < vinfo.yres; y++)
{
for(x = 0; x < vinfo.xres; x++)
{
*(fbp32 + y *vinfo.xres + x) = 0xff0000;
}
}
}
else
{
printf("warnning: bpp is not24\n");
}
munmap(fbp32, screen_size);
close(fd_fb);
return 0;
}
上面的程序使LCD全为红色。需要说明的是该程序只针对BPP为24的情况。