七 linux LCD驱动代分析
Posted bigPillow
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了七 linux LCD驱动代分析相关的知识,希望对你有一定的参考价值。
原文地址:
http://blog.csdn.net/woshidahuaidan2011/article/details/52054795
1、对LCD驱动添加设备信息
对lcd驱动程序,跟之前分析的方式一样,还是先看设备信息,其定义在Mach-smdk2440.c(arch\\arm\\mach-s3c24xx)文件中,在该文件中使用了填充了s3c2410fb_display结构体,
struct s3c2410fb_display
/*LCD type */
unsignedtype;
/*********************************************************************************
设置LCD的类型,比如TFT,STN等LCD的类型。
各种类型的宏定义在Regs-lcd.h(arch\\arm\\mach-s3c24xx\\include\\mach)文件中,比如:
#defineS3C2410_LCDCON1_STN8 (2<<5)
#defineS3C2410_LCDCON1_TFT (3<<5)
。。。。。。。。。。。。。。。。。。。。。。。等等
***********************************************************************************/
/*Screen size */
unsignedshort width;//屏幕宽度
unsignedshort height; //屏幕高度
/*Screen info */
unsignedshort xres; //X轴像素点的个数
unsignedshort yres; //Y轴像素点的个数
unsignedshort bpp; //BPP
unsignedpixclock; /*像素周期每皮秒 */
unsignedshort left_margin; /* HBPD */
unsignedshort right_margin; /* HFPD */
unsignedshort hsync_len; /* HSPW*/
unsignedshort upper_margin; /*VBPD*/
unsignedshort lower_margin; /*VFPD */
unsignedshort vsync_len; /*VSPW */
/*lcd configuration registers */
unsignedlong lcdcon5; //lcd配置寄存器
因此可以修改Mach-smdk2440.c (arch\\arm\\mach-s3c24xx)文件中的smdk2440_lcd_cfg的结构体:
static struct s3c2410fb_displaysmdk2440_lcd_cfg __initdata =
.lcdcon5 = S3C2410_LCDCON5_FRM565 | //565格式
S3C2410_LCDCON5_INVVLINE | //HSYNC极性反正
S3C2410_LCDCON5_INVVFRAME | //VSYNC极性反正
S3C2410_LCDCON5_PWREN | //使能信号输出功能
S3C2410_LCDCON5_HWSWP, //存储格式 P1 P2
.type = S3C2410_LCDCON1_TFT, //lcd类型
.width = 480, //屏幕的宽高尺寸
.height = 272,
.pixclock =100000, /* HCLK100 MHz, divisor 10*/ 像素时钟(皮秒)
.xres = 480, //图像的宽度
.yres =272, //图像的高度
.bpp = 16, //像素的位宽
.left_margin = 1, *行切换,从同步到绘图之间的延迟*/
.right_margin = 1, /*行切换,从绘图到同步之间的延迟*/
.hsync_len = 40, /*水平同步的长度*/
.upper_margin = 1, /*帧切换,从同步到绘图之间的延迟*/
.lower_margin = 1, /*帧切换,从绘图到同步之间的延迟*/
.vsync_len = 9 /*垂直同步的长度*/
;
Pixclock具体的数值的计算就是:
pixclock = 1000000* 1 /【(left_margin + right_margin+hsync_len+ xres)* upper_margin+ lower_margin +vsync_len+ yres)*lcd像素时钟周期】
=10000*[1/(1+1+40+480)*(1+1+9+272)*9000000]
=752143
然后修改smdk2440_fb_info结构体位:
static struct s3c2410fb_mach_infosmdk2440_fb_info __initdata =
.displays = &smdk2440_lcd_cfg,
.num_displays = 1, //显示器的个数
.default_display= 0, //默认显示器编号
// .lpcsel =((0xCE6) & ~7) | 1<<4, //这里要去掉,这个专门为三星公司生产的lcd设置的
;
在Mach-smdk2440.c (arch\\arm\\mach-s3c24xx)文件的,将lcd的platform_device s3c_device_lcd,加入到smdk2440_devices进而加入到platform平台上。至于lcd的platform_device,其定义在Devs.c(arch\\arm\\plat-samsung) 文件中:
structplatform_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, //DMA掩码
.coherent_dma_mask = DMA_BIT_MASK(32),
;
接下来看一下lcd所需要的资源:
static structresource s3c_lcd_resource[] =
[0] = DEFINE_RES_MEM(S3C24XX_PA_LCD,S3C24XX_SZ_LCD),
[1] = DEFINE_RES_IRQ(IRQ_LCD),
;
可以看到lcd用到内存资源和中断资源这两个资源。
其中,内存资源的起始地址为0x4D000000,大小为SZ_1M(0x4D000000是lcd寄存器组的开始地址也就是LCDCON1的地址)。
在Mach-smdk2440.c (arch\\arm\\mach-s3c24xx)文件中有中的smdk2440_machine_init函数中有:
s3c24xx_fb_set_platdata(&smdk2440_fb_info);
s3c24xx_fb_set_platdata最终会把s3c2410fb_mach_info赋值给s3c_device_lcd.dev.platform_data。
对lcd的平台资源暂时介绍到这里。
注意这里设置并没有设置背光灯。设备背光灯的方案很多,这里先列举出来其中一个(以后会列出来其他办法):
linux下面有一个通用的GPIO操作接口,那就是我要介绍的 “/sys/class/gpio” 方式,首先在内核菜单选择编译内核的时候加入 Device Drivers > GPIO Support >/sys/class/gpio/… (sysfs interface),然后编译下载内核后会在/sys/class看到gpio 的文件夹,这是 gpio_operation 通过/sys/文件接口操作IO端口 GPIO到文件系统的映射 进入gpio 会看到:
这里的gpiochip0到gpiochip224分别对应A口B口。。。。。。到H口,每类引脚对应32则,A口的起始地址就是0,A口地址就是0到31,B口就是32到63一直类推H口就是256到256+32。打开其中的文件夹里面会包括每个寄存器控制引脚的起始编base,寄存器名称,引脚总数 导出一个引脚的操作步骤 /sys/class/gpio/export文件用于通知系统需要导出控制的GPIO引脚编号,相反的/sys/class/gpio/unexport用于通知系统取消导出。其中引脚编号 = 控制引脚的寄存器基数 + 控制引脚寄存器位数 ,比如我想控制GPB0引脚只需要写数字32到 /sys/class/gpio/export就好,执行:echo 32 > /sys/class/gpio/export,命令成功后生成/sys/class/gpio/gpio32目录,如果没有出现相应的目录,说明此引脚不可导出,假如想删除/sys/class/gpio/gpio12只需要向/sys/class/gpio/unexport写入32就好,执行:echo 32> /sys/class/gpio/unexport
这里,进入/sys/class/gpio/gpio32目录:direction文件是定义输入输入方向,可以通过下面命令定义为输出 :echo “out” >/sys/class/gpio/gpio32/direction 其中direction接受的参数:in, out, high, low。high/low同时设置方向为输出, 并将value设置为相应的1/0;value文件是端口的数值,为1或0. echo 1 >/sys/class/gpio/gpio32/value 就是控制GPB0输出高电平,也就是打开lcd背光灯。
此时,打开在屏幕黑乎乎的一片,不太好看,此时可以显示内核准备好的背光图片,我们此时仅仅需要将内核设计的图片添加到内核就可以:
在内核配置菜单里面配置:
Device drivers >Graphics support >bootuplogo 选中要显示的格式。
选择完毕,重新下载到内核,打开背光灯就可以看到屏幕的左上角会出现一个小企鹅的图像。
为了每次开机都要执行
echo 32 > /sys/class/gpio/export
echo “out” >/sys/class/gpio/gpio32/direction
echo 1 >/sys/class/gpio/gpio32/value
上面的三条指令打开lcd背光灯,比较繁琐,这里只需要将在开发板的根文件系统下的/etc目录下建立profile文件(假如存在的话不直接在文件写入下面的语句就可以)
vi /etc/profile
写入
echo 32 > /sys/class/gpio/export
echo “out” >/sys/class/gpio/gpio32/direction
echo 1>/sys/class/gpio/gpio32/value
保存退出就可以。
下面开看一下测试程序。
2、对LCD驱动的测试
对于lcd,我可以通过ioctl来设置和得到参数,ioctl可用到的参数定义在Fb.h (include\\uapi\\linux) 文件中。由于该文件的内容过多,不再一一列出,因为下面的测试函数获得lcd的可变参数和固定参数,这里我们仅仅列出来这两个结构体。
structfb_fix_screeninfo 可变参数
char id[16]; /* identification string eg "TT Builtin"*/
unsigned long smem_start; /* Start of frame buffer mem */
/* (physicaladdress) */
__u32 smem_len; /* Length of frame buffer mem */
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes */
unsigned long mmio_start; /* Start of Memory Mapped I/O */
/* (physicaladdress) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Indicate to driver which */
/* specific chip/card we have */
__u16 capabilities; /* see FB_CAP_* */
__u16 reserved[2]; /* Reserved for future compatibility */
structfb_var_screeninfo 固定的参数
__u32 xres; /* visible resolution */
__u32 yres;
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* guess what */
__u32 grayscale; /* 0 = color, 1 = grayscale, */
/* >1 =FOURCC */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 nonstd; /* != 0 Non standard pixel format */
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
/* Timing: All values in pixclocks, exceptpixclock (of course) */
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[4]; /* Reserved for future compatibility */
至于上面的结构体的具体的含义暂时不再列出,在下文中将会详细的介绍每个成员的具体含义。
我们可以编写测试代码进行测试。测试函数的作用是现实一副自定义的图片,代码如下:
lcd.c文件为:
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include"class_lcd.h" //自定义的h文件
volatile unsigned short LCDBANK[272][480];
void paint_pic(const unsigned char pic[]) //将8转化成16位
int x,y;
unsigned int colour;
int p = 0;
for(y = 0 ; y < 272 ; y++ )
for( x = 0 ; x < 480 ; x++ )
colour = pic[p] | (pic[p+1]<<8) ;
//colour = pic[p] ;
if ( ( x < 480)&& ( y < 272) )
LCDBANK[y][x] =colour ;
p = p + 2 ;
//p++;
int main(void)
intfd = 0;
struct fb_var_screeninfo var; //可变参数
struct fb_fix_screeninfo fix; //固定参数
intx = 0, y = 0;
fd =open("/dev/fb0", O_RDWR);
if(!fd)
printf("error open file.\\n");
close(fd);
if (ioctl(fd, FBIOGET_FSCREENINFO,&var)) //得到可变参数
printf(" error reading fixed information .\\n");
close(fd);
if(ioctl(fd, FBIOGET_VSCREENINFO, &fix)) //得到固定参数
printf("error reading variable information.\\n");
close(fd);
paint_pic(gImage_test);//8位变为16位
int dat=write(fd,LCDBANK,sizeof(LCDBANK));
// printf(“%d”,dat );
close(fd);
return 0;
h文件为:
#ifndef __class_lcd_h__
#define __class_lcd_h__
extern const unsigned chargImage_test[];//ͼƬÊý×é
#endif //__class_led_h__
其中gImage_test[]为用取模软件获得的数组,由于数组比较大不再列出,与之前介绍的裸机代码一样,这里只是将该数组放在test.c的文件中。
接下来就是Makefile文件:
CC=arm-linux-2440-gcc
.c.o:
$CC -c $<
lcd:test.o lcd.o
$CC -o $@ $^
cp lcd /work/root/work/
clean:
rm -rf lcd lcd.o test.o
编译完毕,会elf的可执行文件lcd,只需要将生成lcd拷贝到arm的文件系统下运行即可。运行完毕,会看到图片显示在lcd中。
3、对LCD驱动的分析
lcd的驱动代码定义在S3c2410fb.c (drivers\\video)文件中,前面对于Platform平台的介绍寂静够详细的了,这里不再赘述,就从probe函数开始介绍:
static ints3c24xxfb_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;
mach_info= dev_get_platdata(&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;
display= mach_info->displays + mach_info->default_display;
/*********************************************************************************
获取设备定义结构体s3c2410fb_display的实体smdk2440_lcd_cfg的首地址。这里也就获得了平台设备对lcd设置的那些数据。后面又加了一个mach_info->default_display,是指名用的那个设备。
***********************************************************************************/
irq= platform_get_irq(pdev, 0); //得到中断号
if(irq < 0)
dev_err(&pdev->dev,"no irq for device\\n");
return-ENOENT;
fbinfo= framebuffer_alloc(sizeof(struct s3c2410fb_info),&pdev->dev);
/*********************************************************************************
struct fb_info*framebuffer_alloc(size_t size, struct device *dev)
这是在创建一个帧缓冲结构体,
size_t size是驱动私有数据的大小(这里指structs3c2410fb_info)可为0
struct device *de 为指向平台设备的的指针,可以为NULL
返回值为帧缓冲fb_info的结构体,
对于fb_info相当于电脑的显卡。其定义为:
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 */变化的参数
struct fb_fix_screeninfofix; /* Current fix */固定的参数
structfb_monspecs monspecs; /* Current Monitorspecs */当前显示器规格
structwork_struct queue; /* Framebuffer eventqueue */帧缓冲事件队列
structfb_pixmap pixmap; /* Image hardwaremapper */图像硬件映射
structfb_pixmap sprite; /* Cursor hardware mapper */光标硬件映射
structfb_cmap cmap; /* Current cmap */color映射表
structlist_head modelist; /* mode list */模式链表
structfb_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 */
intclass_flag; /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
structfb_tile_ops *tileops; /* Tile Blitting*/位图移动
#endif
char__iomem *screen_base; /* Virtual address*/
unsignedlong screen_size; /* Amount ofioremapped VRAM or 0 */ 帧缓存大小
void*pseudo_palette; /* Fakepalette of 16 colors */伪16色调色板
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32state; /* Hardwarestate i.e suspend *///
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;
boolskip_vt_switch; /* no VT switch on suspend/resume required */
;
上面有提到对于可变参数的一个结构体structfb_var_screeninfo,该结构体主要是定义的一些lcd的可变信息,比较重要,这里来看一下:
__u32 xres; /*visible resolution */可见分辨率(480x272,320x480等等)
__u32 yres;
__u32 xres_virtual; /* virtual resolution *//虚拟分辨率(这是为了屏幕显示区可移动设置的)
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */偏移量,对应2440lcd控制器的15-25页的offsize
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* guess what */ bpp数
__u32 grayscale; /* 0 = color, 1 = grayscale, */ 灰度还是颜色
/* >1 = FOURCC */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
/*********************************************************************************
分别是红绿蓝 透明度所占用的bpp的位数信息。其中:
struct fb_bitfield
__u32 offset; /* beginning of bitfield */开始位子
__u32 length; /* length of bitfield */长度
__u32 msb_right; /* != 0 : Most significant bit is */影响最大的位
/* right */
;
/*********************************************************************************/
__u32 nonstd; /*!= 0 Non standard pixel format */假如不为0代表为标准bpp
__u32 activate; /* see FB_ACTIVATE_* */由FB_ACTIVATE_*宏确定
__u32 height; /*height of picture in mm */
__u32 width; /*width of picture in mm */
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
/* Timing: All values in pixclocks, exceptpixclock (of course) */
__u32 pixclock; /* pixel clock in ps (pico seconds) */ 下面几个参数第一节已经分析
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */ HSYNC
__u32 vsync_len; /* length of vertical sync */ VSYNC
__u32 sync; /*see FB_SYNC_* */
__u32 vmode; /*see FB_VMODE_* */
__u32 rotate; /*angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[4]; /* Reserved for future compatibility */
;
那么再看一下固定参数的结构体fb_fix_screeninfo:
structfb_fix_screeninfo
char id[16]; /*identification string eg "TT Builtin" */标识符,一般是驱动的名字
unsigned long smem_start; /* Start of frame buffer mem */内存开始物理地址
/* (physicaladdress) */
__u32 smem_len; /* Length of frame buffer mem */假如有多个lcd,这里记录的是帧内存最大的内存大小
__u32 type; /*see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /*see FB_VISUAL_* */ 看宏 FB_VISUAL_*
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes */
unsigned long mmio_start; /* Start of Memory Mapped I/O */引脚起始物理地址
/* (physicaladdress) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /*Indicate to driver which */指出那个驱动
/* specific chip/card we have */
__u16 capabilities; /* see FB_CAP_* */
__u16 reserved[2]; /* Reserved for future compatibility */
;
***********************************************************************************/
if (!fbinfo)
return -ENOMEM;
platform_set_drvdata(pdev,fbinfo); //保存数据
info = fbinfo->par; //获取设备定义依赖信息
info->dev =&pdev->dev; 获得设备信息
info->drv_type =drv_type; //或许类型
res =platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取内存资源
if (res == NULL)
dev_err(&pdev->dev,"failed to get memory registers\\n");
ret = -ENXIO;
goto dealloc_fb;
size = resource_size(res); //获取内存资源大小,单位byte
info->mem =request_mem_region(res->start, size, pdev->name);
/*********************************************************************************
这个是关系到有关io内存的申请,对于一般内存的控制,比如向某个控制寄存器写入数据,这直接使用ioremap函数,将物理地址转换为虚拟地址就可以;但是对于I/OPort内存则需先申请再映射,即先调用request_mem_region,然后在ioremap,这样的话就告诉内核该引脚已经被我使用,别人不可再用,加入对于I/OPort不使用request_mem_region而直接ioremap,虽然可以也可以使用,但是可能内核并不知道相应的引脚已经被使用,于是肯呢个会会出现难以预料的错误。
********************************************************************************/
if (info->mem == NULL)
dev_err(&pdev->dev,"failed to get memory region\\n");
ret = -ENOENT;
goto dealloc_fb;
info->io =ioremap(res->start, size); //物理地址转化为虚拟地址
if (info->io == NULL)
dev_err(&pdev->dev,"ioremap() of registers failed\\n");
ret = -ENXIO;
goto release_mem;
if (drv_type == DRV_S3C2412) //判断类型
info->irq_base =info->io + S3C2412_LCDINTBASE;
else
info->irq_base =info->io + S3C2410_LCDINTBASE;
dprintk("devinit\\n");
strcpy(fbinfo->fix.id,driver_name); //拷贝名字
/* Stop the video */
lcdcon1 = readl(info->io+ S3C2410_LCDCON1); //读取寄存器的值
writel(lcdcon1 &~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
//暂时关闭信号输出使能
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; //无硬件加速
fbinfo->var.nonstd =0; 标准格式bpp
fbinfo->var.activate =FB_ACTIVATE_NOW;
//使设定值马上有效,与之相对可以设定下次打开设备设定值有效(FB_ACTIVATE_NXTOPEN)
fbinfo->var.accel_flags = 0;
fbinfo->var.vmode =FB_VMODE_NONINTERLACED;
fbinfo->fbops = &s3c2410fb_ops;
/*********************************************************************************
对于文件操作:
staticstruct fb_ops s3c2410fb_ops =
.owner =THIS_MODULE,
.fb_check_var = s3c2410fb_check_var, 检查可变参数的设定,kernel并适当调整参数值
.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, //显示图像
;
上面的这些函数会在属性文件中调用。
********************************************************************************/
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette = &info->pseudo_pal;
for (i = 0; i < 256; i++)
info->palette_buffer[i]= PALETTE_BUFF_CLEAR; 清空调色板
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;
goto release_regs;
info->clk = clk_get(NULL,"lcd"); //得到lcd的时钟Clock-s3c2410.c (arch\\arm\\mach-s3c24xx))
if (IS_ERR(info->clk))
dev_err(&pdev->dev,"failed to get lcd clock source\\n");
ret =PTR_ERR(info->clk);
goto release_irq;
clk_enable(info->clk);//时钟使能
dprintk("got andenabled clock\\n");
usleep_range(1000, 1100);等待时钟完成,等待时间为1000微妙到1100微妙之间
info->clk_rate =clk_get_rate(info->clk);//获取时钟频率
/* find maximum requiredmemory size for display */ /
for (i = 0; i <mach_info->num_displays; i++) //加入有多个lcd,获取最大的帧内存的大小
unsigned longsmem_len = mach_info->displays[i].xres; //下面三句话是得到帧内存的大小
smem_len *=mach_info->displays[i].yres;
smem_len *=mach_info->displays[i].bpp;
smem_len >>= 3;//上面得到的单位为bit,这里是将其为byte
if (fbinfo->fix.smem_len< smem_len) //记录最大值
fbinfo->fix.smem_len= smem_len;
/* Initialize video memory */
ret = s3c2410fb_map_video_memory(fbinfo);
/*********************************************************************************
s3c2410fb_map_video_memory分配DRAM的缓存区给fbinfo。这个缓存区是一个non-cached,non-buffered的 这片内存区域允许调色板和像素在写入时不刷新cache缓存 一旦这片区域重新映射,那么所有用来访问video memory的虚拟内存将会 对应另外一片新的区域,即另外一片物理地址。
函数返回的是可供mda使用的虚拟地址。
static int s3c2410fb_map_video_memory(struct fb_info*info)
structs3c2410fb_info *fbi = info->par;
dma_addr_tmap_dma; //存储申请缓冲器内存的物理地址。
unsignedmap_size = PAGE_ALIGN(info->fix.smem_len);
/*********************************************************************************
PAGE_ALIGN返回页对齐页码
#define PAGE_ALIGN(addr) ALIGN(addr,PAGE_SIZE)
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
#define PAGE_SHIFT 12
#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
替换后就是:
(addr+(1111 1111 1111))&( ~(1111 0000 00000000)
假如地址是addr 为0x12345679那么计算后就是:
(0x12345678+(11111111 1111))&(~(1111 0000 0000 0000)
0001 0010 0011 0100 0110 0110 0111 0111
1111 1111 1111 1111 1111 0000 0000 0000
0001 0010 0011 0100 0110 0000 0000 0000 也就是0x12346000
这里就设置成了4字节对齐,也就是也对齐
********************************************************************************/
dprintk("map_video_memory(fbi=%p)map_size %u\\n", fbi, map_size);
info->screen_base= dma_alloc_writecombine(fbi->dev, map_size,
以上是关于七 linux LCD驱动代分析的主要内容,如果未能解决你的问题,请参考以下文章