七 linux LCD驱动代分析
LCD驱动分析
原文地址:
http://blog.csdn.net/woshidahuaidan2011/article/details/52054795
/*********************************************************************************
各种类型的宏定义在Regs-lcd.h(arch\arm\mach-s3c24xx\include\mach)文件中,比如:
#defineS3C2410_LCDCON1_STN8 (2<<5)
#defineS3C2410_LCDCON1_TFT (3<<5)
***********************************************************************************/
unsignedshort xres; //X轴像素点的个数
unsignedshort yres; //Y轴像素点的个数
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类型
.pixclock =100000, /* HCLK100 MHz, divisor 10*/ 像素时钟(皮秒)
.left_margin = 1, *行切换,从同步到绘图之间的延迟*/
.right_margin = 1, /*行切换,从绘图到同步之间的延迟*/
.upper_margin = 1, /*帧切换,从同步到绘图之间的延迟*/
.lower_margin = 1, /*帧切换,从绘图到同步之间的延迟*/
=10000*[1/(1+1+40+480)*(1+1+9+272)*9000000]
static struct s3c2410fb_mach_infosmdk2440_fb_info __initdata = {
.displays = &smdk2440_lcd_cfg,
.default_display= 0, //默认显示器编号
// .lpcsel =((0xCE6) & ~7) | 1<<4, //这里要去掉,这个专门为三星公司生产的lcd设置的
structplatform_device s3c_device_lcd = {
.name = "s3c2410-lcd", //平台设备的名字
.num_resources = ARRAY_SIZE(s3c_lcd_resource),
.resource =s3c_lcd_resource, //平台设备所用到的资源
.dma_mask = &samsung_device_dma_mask, //DMA掩码
.coherent_dma_mask = DMA_BIT_MASK(32),
static structresource s3c_lcd_resource[] = {
[0] = DEFINE_RES_MEM(S3C24XX_PA_LCD,S3C24XX_SZ_LCD),
[1] = DEFINE_RES_IRQ(IRQ_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。
注意这里设置并没有设置背光灯。设备背光灯的方案很多,这里先列举出来其中一个(以后会列出来其他办法):
此时,打开在屏幕黑乎乎的一片,不太好看,此时可以显示内核准备好的背光图片,我们此时仅仅需要将内核设计的图片添加到内核就可以:
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文件(假如存在的话不直接在文件写入下面的语句就可以)
echo 32 > /sys/class/gpio/export
echo “out” >/sys/class/gpio/gpio32/direction
echo 1>/sys/class/gpio/gpio32/value
structfb_fix_screeninfo { 可变参数
char id[16]; /* identification string eg "TT Builtin"*/
unsigned long smem_start; /* Start of frame buffer mem */
__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 */
__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 xres_virtual; /* virtual resolution */
__u32 xoffset; /* offset from virtual to visible */
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* guess what */
__u32 grayscale; /* 0 = color, 1 = grayscale, */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
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 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 */
至于上面的结构体的具体的含义暂时不再列出,在下文中将会详细的介绍每个成员的具体含义。
我们可以编写测试代码进行测试。测试函数的作用是现实一副自定义的图片,代码如下:
#include"class_lcd.h" //自定义的h文件
volatile unsigned short LCDBANK[272][480];
void paint_pic(const unsigned char pic[]) //将8转化成16位
colour = pic[p] | (pic[p+1]<<8) ;
if ( ( x < 480)&& ( y < 272) )
struct fb_var_screeninfo var; //可变参数
struct fb_fix_screeninfo fix; //固定参数
if (ioctl(fd, FBIOGET_FSCREENINFO,&var)) { //得到可变参数
printf(" error reading fixed information .\n");
if(ioctl(fd, FBIOGET_VSCREENINFO, &fix)) { //得到固定参数
printf("error reading variable information.\n");
paint_pic(gImage_test);//8位变为16位
int dat=write(fd,LCDBANK,sizeof(LCDBANK));
extern const unsigned chargImage_test[];//ͼƬÊý×é
其中gImage_test[]为用取模软件获得的数组,由于数组比较大不再列出,与之前介绍的裸机代码一样,这里只是将该数组放在test.c的文件中。
编译完毕,会elf的可执行文件lcd,只需要将生成lcd拷贝到arm的文件系统下运行即可。运行完毕,会看到图片显示在lcd中。
lcd的驱动代码定义在S3c2410fb.c (drivers\video)文件中,前面对于Platform平台的介绍寂静够详细的了,这里不再赘述,就从probe函数开始介绍:
static ints3c24xxfb_probe(struct platform_device *pdev,
structs3c2410fb_display *display;
structs3c2410fb_mach_info *mach_info;
mach_info= dev_get_platdata(&pdev->dev);//得到设备数据platform_data
"noplatform data for lcd, cannot attach\n");
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);
display= mach_info->displays + mach_info->default_display;
/*********************************************************************************
***********************************************************************************/
irq= platform_get_irq(pdev, 0); //得到中断号
dev_err(&pdev->dev,"no irq for device\n");
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
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 */当前的模式
/*assigned backlight device */
/*set before framebuffer registration,
structbacklight_device *bl_dev ; //背光设备
u8bl_curve[FB_BACKLIGHT_LEVELS]; //应该设置背光的亮度
structdelayed_work deferred_work; //应该是延时工作
structfb_deferred_io *fbdefio;
structfb_ops *fbops; //缓冲区有关文件操作
structdevice *device; /* This is theparent */
structdevice *dev; /* This is this fbdevice */
intclass_flag; /* private sysfs flags */
structfb_tile_ops *tileops; /* Tile Blitting*/位图移动
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 */
/*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 */
boolskip_vt_switch; /* no VT switch on suspend/resume required */
上面有提到对于可变参数的一个结构体structfb_var_screeninfo,该结构体主要是定义的一些lcd的可变信息,比较重要,这里来看一下:
__u32 xres; /*visible resolution */可见分辨率(480x272,320x480等等)
__u32 xres_virtual; /* virtual resolution *//虚拟分辨率(这是为了屏幕显示区可移动设置的)
__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, */ 灰度还是颜色
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield transp; /* transparency */
/*********************************************************************************
__u32 offset; /* beginning of bitfield */开始位子
__u32 length; /* length of bitfield */长度
__u32 msb_right; /* != 0 : Most significant bit is */影响最大的位
/*********************************************************************************/
__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 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:
char id[16]; /*identification string eg "TT Builtin" */标识符,一般是驱动的名字
unsigned long smem_start; /* Start of frame buffer mem */内存开始物理地址
__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 */引脚起始物理地址
__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 */
***********************************************************************************/
platform_set_drvdata(pdev,fbinfo); //保存数据
info = fbinfo->par; //获取设备定义依赖信息
info->drv_type =drv_type; //或许类型
res =platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取内存资源
dev_err(&pdev->dev,"failed to get memory registers\n");
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,虽然可以也可以使用,但是可能内核并不知道相应的引脚已经被使用,于是肯呢个会会出现难以预料的错误。
********************************************************************************/
dev_err(&pdev->dev,"failed to get memory region\n");
info->io =ioremap(res->start, size); //物理地址转化为虚拟地址
dev_err(&pdev->dev,"ioremap() of registers failed\n");
if (drv_type == DRV_S3C2412) //判断类型
info->irq_base =info->io + S3C2412_LCDINTBASE;
info->irq_base =info->io + S3C2410_LCDINTBASE;
strcpy(fbinfo->fix.id,driver_name); //拷贝名字
lcdcon1 = readl(info->io+ S3C2410_LCDCON1); //读取寄存器的值
writel(lcdcon1 &~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
fbinfo->fix.type =FB_TYPE_PACKED_PIXELS; //类型
fbinfo->fix.accel =FB_ACCEL_NONE; //无硬件加速
fbinfo->var.nonstd =0; 标准格式bpp
fbinfo->var.activate =FB_ACTIVATE_NOW;
//使设定值马上有效,与之相对可以设定下次打开设备设定值有效(FB_ACTIVATE_NXTOPEN)
fbinfo->var.vmode =FB_VMODE_NONINTERLACED;
fbinfo->fbops = &s3c2410fb_ops;
/*********************************************************************************
staticstruct fb_ops s3c2410fb_ops = {
.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;
info->palette_buffer[i]= PALETTE_BUFF_CLEAR; 清空调色板
ret = request_irq(irq,s3c2410fb_irq, 0, pdev->name, info); //申请中断
dev_err(&pdev->dev,"cannot get irq %d - err %d\n", irq, ret);
info->clk = clk_get(NULL,"lcd"); //得到lcd的时钟Clock-s3c2410.c (arch\arm\mach-s3c24xx))
dev_err(&pdev->dev,"failed to get lcd clock source\n");
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;
ret = s3c2410fb_map_video_memory(fbinfo);
/*********************************************************************************
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);
/*********************************************************************************
#define PAGE_ALIGN(addr) ALIGN(addr,PAGE_SIZE)
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
(addr+(1111 1111 1111))&( ~(1111 0000 00000000)
(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
********************************************************************************/
dprintk("map_video_memory(fbi=%p)map_size %u\n", fbi, map_size);
info->screen_base= dma_alloc_writecombine(fbi->dev, map_size,
/*********************************************************************************
static inline void *dma_alloc_writecombine(structdevice *dev, size_t size,
dma_addr_t *dma_handle,gfp_t flag)
********************************************************************************/
/*prevent initial garbage on screen */
dprintk("map_video_memory:clear %p:%08x\n",
memset(info->screen_base,0x00, map_size);
/*********************************************************************************
void *memset(void *s, int c, size_t count)
将s所指向的某一块内存中的前n个 字节的内容全部设置为ch指定的ASCII值,块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作,其返回值为指向s的指针
********************************************************************************/
info->fix.smem_start= map_dma; //物理地址
dprintk("map_video_memory:dma=%08lx cpu=%p size=%08x\n",
info->fix.smem_start,info->screen_base, map_size);
returninfo->screen_base ? 0 : -ENOMEM;
********************************************************************************/
dev_err(&pdev->dev,"Failed to allocate video RAM: %d\n", ret);
fbinfo->var.xres = display->xres;
fbinfo->var.yres =display->yres;
fbinfo->var.bits_per_pixel= display->bpp;//guess what?
s3c2410fb_init_registers(fbinfo);//初始化寄存器
/*********************************************************************************
static int s3c2410fb_init_registers(structfb_info *info)
structs3c2410fb_info *fbi = info->par;
structs3c2410fb_mach_info *mach_info = dev_get_platdata(fbi->dev);//得到存储的数据
void__iomem *regs = fbi->io; //__iomem表示指向一个I/O的内存空间
void__iomem *tpal; //调色板寄存器TPAL
void__iomem *lpcsel; //对应的是跟LPC3600/LCC3600有关的 TCONSEL
lpcsel= regs + S3C2412_TCONSEL;
lpcsel= regs + S3C2410_LPCSEL;
/*Initialise LCD with values from haret */
/*********************************************************************************
local_irq_disable() 和local_irq_enable()配对使用;
local_irq_save() 和 local_irq_restore() 配对使用。
********************************************************************************/
/*modify the gpio(s) with interrupts set (bjd) */
modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
modify_gpio(S3C2410_GPCCON,mach_info->gpccon, mach_info->gpccon_mask);
modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
modify_gpio(S3C2410_GPDCON,mach_info->gpdcon, mach_info->gpdcon_mask);
/*********************************************************************************
rGPCUP = 0xffffffff; // 禁止内部上拉
// GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
rGPDUP = 0xffffffff; // 禁止内部上拉
rGPDCON = 0xaaaaaaaa; // GPIO管脚用于VD[23:8]
modify_gpio就是将第二个参数的值与第三个参数的反码按位与后,在写到第一个参数
********************************************************************************/
local_irq_restore(flags); //恢复中断
dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel);
writel(mach_info->lpcsel,lpcsel);//不使用lpcsel寄存器
dprintk("replacingTPAL %08x\n", readl(tpal));
/*ensure temporary palette disabled */
********************************************************************************/
s3c2410fb_check_var(&fbinfo->var,fbinfo);
/*********************************************************************************
s3c2410fb_check_var这个函数实现比较简单,就是检测一下设定的那些参数是否合法,对于小数部分采取向上取整的方法,如果设置的数据过大,则返回无效的参数错误:-EINVAL.
********************************************************************************/
ret = s3c2410fb_cpufreq_register(info);//cpu变频有关的函数
dev_err(&pdev->dev,"Failed to register cpufreq\n");
ret =register_framebuffer(fbinfo);
/*********************************************************************************
前面使用framebuffer_alloc申请一个fb_info,这里使用register_framebuffe注册一个fb_info,下面会对此函数做出详细的介绍..
********************************************************************************/
dev_err(&pdev->dev,"Failed to register framebuffer device: %d\n",
ret =device_create_file(&pdev->dev, &dev_attr_debug);
/*********************************************************************************
随便再说一下class_create和device_create
/*在/sys/class/目录下创建设备类别目录name*/
ret= class_create(THIS_MODULE, CLASS_NAME);
/*在/dev/目录和/sys/class/CLASS_NAME目录下分别创建设备文件DEVICE_NAME */
dev = device_create(ret, NULL, devt, NULL,DEVICE_NAME);
就以本例来说,会在sys/devices/platform/s3c2410-lcd目录下生成s3c2410-lcd属性文件debug
由于宏static DEVICE_ATTR(debug, 0666,s3c2410fb_debug_show, s3c2410fb_debug_store);
static int s3c2410fb_debug_show(struct device*dev, struct device_attribute *attr,char *buf)
s3c2410fb_debug_store(struct device*dev,struct device_attribute *attr,const char *buf, size_t len)
在/sys/devices/platform/s3c2410-lcd下执行:
/sys/devices/platform/s3c2410-lcd # cat debug
/sys/devices/platform/s3c2410-lcd # cat "on " debug
或者/sys/devices/platform/s3c2410-lcd# cat "1 " debug
********************************************************************************/
dev_err(&pdev->dev,"failed to add debug attribute\n");
dev_info(&pdev->dev,"fb%d: %s frame buffer device\n",
s3c2410fb_cpufreq_deregister(info);
s3c2410fb_unmap_video_memory(fbinfo);
release_mem_region(res->start,size);
之前有介绍,register_framebuffer函数用来注册一个fb_info,接下来看一下其大概的机制:Fbmem.c(drivers\video) 文件中有:
intregister_framebuffer(struct fb_info *fb_info)
紧接着,register_framebuffer会调用位于Fbsysfs.c (drivers\video) 文件中fb_init_device函数,该函数的函数体为:
int fb_init_device(structfb_info *fb_info)
for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
error = device_create_file(fb_info->dev,&device_attrs[i]);
。。。。。。。。。。。。。。。。。省略。。。。。。。。。。。。。。。。。。。。。
static structdevice_attribute device_attrs[] = {
__ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
__ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
__ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
__ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
__ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
__ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
__ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
__ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual,store_virtual),
__ATTR(name, S_IRUGO, show_name, NULL),
__ATTR(stride, S_IRUGO, show_stride, NULL),
__ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
__ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
__ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve,store_bl_curve),
因此他们会在/sys/devices/platform/s3c2410-lcd/graphics/fb0目录下生成这些对应的属性文件,可以查看到,确实生成可这些文件:
/sys/devices/platform/s3c2410-lcd/graphics/fb0# ls
bits_per_pixel dev name state virtual_size
我们随便举一个例子,看看这些属性文件都做了写什么,就以modes 为例:
在写入modes的值的时候,会调用store_modes然后依次看一下调用关系:
info->fbops->fb_check_var info->fbops->fb_check_var info->fbops->fb_set_par 等
上面的这些函数对调用S3c2410fb.c (drivers\video)定义的:
static struct fb_ops s3c2410fb_ops = {
.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, //显示图像
那么,对于我们打开设备文件常用的open、clsoe、write等函数又是调用的什么具体的函数呢?带着这个问题,我们看一下Fbmem.c (drivers\video)文件中有:
proc_create("fb",0, NULL, &fb_proc_fops);
if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
printk("unable to get major %d for fb devs\n",FB_MAJOR);
fb_class = class_create(THIS_MODULE, "graphics");//sysfs文件系统下创建graphics目录
printk(KERN_WARNING "Unable to create fb class;errno = %ld\n", PTR_ERR(fb_class));
对于创建文件,在sysfs文件系统中,知道可以使用device_create和device_create_file两种方式,对于proc文件系统,其创建文件的方式为:
比如上面的是:proc_create("fb", 0, NULL, &fb_proc_fops);
register_chrdev(FB_MAJOR,"fb",&fb_fops)
这是是注册一个字符设备主设备号为29,名字为fb,还有文件操作。
static const structfile_operations fb_fops = {
.compat_ioctl = fb_compat_ioctl,
#ifdefHAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
.fsync = fb_deferred_io_fsync,
这里就定义了一些通过的读写操作,和之前的相同,这里就不在一一介绍,只是抛砖引玉的介绍一下ioctl函数,ioctl主要是获取一些屏幕的设置信息:
static longdo_fb_ioctl(struct fb_info *info, unsigned int cmd,
void __user *argp = (void __user *)arg;
case FBIOGET_VSCREENINFO://根据参数判定,这里获取可变参数
if (!lock_fb_info(info)) //加互斥锁
ret = copy_to_user(argp, &var, sizeof(var)) ?-EFAULT : 0;//传递给用户空间
case FBIOPUT_VSCREENINFO: //根据参数判定,这里设定可变参数
if (copy_from_user(&var, argp, sizeof(var))) //数据由用户空间到内核空间传递
/**********************************************************************************
console_lock();是为了保证调用者可互斥获取控制台系统而加的锁。
*****************************************************************************/
if (!lock_fb_info(info)) {//为设置fb_info的参数而申请的互斥锁
info->flags |= FBINFO_MISC_USEREVENT;标记位,标志着发生了来之用户空间的请求
//设置参数,最终会调用S3c2410fb.c (drivers\video)文件中的s3c2410fb_activate_var对寄存器进行赋值
info->flags &= ~FBINFO_MISC_USEREVENT; //标记来之用户空间的数据设置完毕
if (!ret && copy_to_user(argp, &var,sizeof(var)))
case FBIOGET_FSCREENINFO: //标记这获取固定的参数
ret = copy_to_user(argp, &fix, sizeof(fix)) ?-EFAULT : 0;
if (copy_from_user(&cmap, argp, sizeof(cmap)))
ret = fb_set_user_cmap(&cmap, info);
//调用S3c2410fb.c(drivers\video)的s3c2410fb_setcolreg函数设置rgb的格式
if (copy_from_user(&cmap, argp, sizeof(cmap)))
ret = fb_cmap_to_user(&cmap_from, &cmap); //得到颜色的格式
if (copy_from_user(&var, argp, sizeof(var)))
ret = fb_pan_display(info, &var);
if (ret == 0 && copy_to_user(argp, &var,sizeof(var)))
case FBIO_CURSOR: //gusee what
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
if (con2fb.console < 1 || con2fb.console >MAX_NR_CONSOLES)
fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP,&event);
ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ?-EFAULT : 0;
if(copy_from_user(&con2fb, argp, sizeof(con2fb)))
if (con2fb.console < 1 || con2fb.console >MAX_NR_CONSOLES)
if (con2fb.framebuffer < 0 || con2fb.framebuffer>= FB_MAX)
if (!registered_fb[con2fb.framebuffer])
request_module("fb%d",con2fb.framebuffer);
if (!registered_fb[con2fb.framebuffer]) {
ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP,&event);
info->flags |= FBINFO_MISC_USEREVENT;
info->flags &= ~FBINFO_MISC_USEREVENT;
ret = fb->fb_ioctl(info, cmd, arg);
该方案已经在对LCD驱动添加设备信息的时候介绍个,不再赘述。
#include <mach/gpio-samsung.h>
然后在该文件中的s3c2410fb_init_registers函数中添加:
gpio_direction_output(S3C2410_GPB(0), 1); //gpb0链接的背光灯。
大家因该应该记得在介绍lcd的驱动额时候,当时驱动中添加了4个led的驱动,但是对于开发板来说,我们仅仅用到了其中的三个led的驱动,此时我们可以吧另外一个修改成为lcd的驱动。
修改Common-smdk.c (arch\arm\mach-s3c24xx) 文件中的:
vi arch/arm/mach-s3c24xx/common-smdk.c
static struct s3c24xx_led_platdata smdk_pdata_led7 = {
.flags = S3C24XX_LEDF_ACTLOW |S3C24XX_LEDF_TRISTATE,
static struct s3c24xx_led_platdata smdk_pdata_led7 = {
.flags = S3C24XX_LEDF_TRISTATE,
/sys/devices/platform/s3c24xx_led.3/leds/lcdBackLight# ls
brightness max_brightness subsystem uevent
此时我们写入brightness文件数值1则背光灯打开;给0,背光灯则关闭。
七 linux LCD驱动代分析相关推荐
- linux RTC 驱动模型分析
linux RTC 驱动模型分析 RTC(real time clock)实时时钟,主要作用是给Linux系统提供时间.RTC因为是电池供电的,所以掉电后时间不丢失.Linux内核把RTC用作&quo ...
- Linux PCI驱动框架分析:(Peripheral Component Interconnect,外部设备互联)
<DPDK 20.05 | rte_pci_bus思维导图 | 第一版> <linux系统下:IO端口,内存,PCI总线 的 读写(I/O)操作> <Linux指令:ls ...
- Linux USB驱动框架分析 【转】
转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结 ...
- linux MISC 驱动模型分析
linux MISC 驱动模型分析 阅读led驱动程序的代码的时候,没有发现ldd3中提到的各种字符设备注册函数,而是发现了一个misc_register函数,这说明led设备是作为杂项设备出现在内核 ...
- Linux PCIe驱动框架分析(第二章)
目录 项目背景 1. 概述 2. 数据结构 3. 流程分析 3.1 设备驱动模型 3.2 初始化 3.2.1 pci_bus_match 3.2.2 pci_device_probe 3.3 枚举 项 ...
- 深入分析Linux PCI驱动框架分析(二)
说明: Kernel版本:4.14 ARM64处理器 使用工具:Source Insight 3.5, Visio 1. 概述 本文将分析Linux PCI子系统的框架,主要围绕Linux PCI子系 ...
- LINUX设备驱动模型分析之三 驱动(DRIVER)接口分析
上一章我们分析了bus-driver-device模型中bus接口部分,本章我们将分析driver接口,在bus-driver-device模型中,driver接口是依附于bus上,而不像device ...
- Linux i2c驱动框架分析 (二)
Linux i2c驱动框架分析 (一) Linux i2c驱动框架分析 (二) Linux i2c驱动框架分析 (三) 通用i2c设备驱动分析 i2c core i2c核心(drivers/i2c/i ...
- Linux系统驱动之分析内核自带的LCD驱动程序_基于IMX6ULL
百问网技术交流群,百万嵌入式工程师聚集地: https://www.100ask.net/page/2248041 资料下载 coding无法使用浏览器打开,必须用git工具下载: git clone ...
最新文章
- mysql命令程序_MySQL命令大全经典版
- 方差和协方差的数据意义
- 使用 Syslog 连接 Sentinel
- 被卡性能的时候要care数据类型(洛谷P5594TLE+RE的经历,Java语言描述)
- ios 数字键盘左下角添加按钮_IOS数字键盘左下角添加完成按钮的实现方法
- 谷歌宕机,只有运维背锅吗?
- Yarn 调度器Scheduler详解
- 溯源项目(全套源码)
- 华为外包员工是什么样的群体?
- 【新手必看系列】小鸟云服务器该如何配置?
- element-ui tabs标签嵌套使用时 基础下划线不显示的问题
- 用DIV+CSS技术设计的凤阳旅游网站(web前端网页制作课作业)HTML+CSS+JavaScript
- Panabit专业流量监控开源软件
- windows 改路径有小差异
- 算法工程师之劝退檄文
- C++实现字符串匹配KMP算法
- 微信朋友圈营销快速加好友技巧大全
- MFC中使用CPropertySheet实现Tab Control
- 一建 :工期索赔的计算
- InDesign 教程:如何创建和编辑图层?