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的可变信息,比较重要,这里来看一下:

structfb_var_screeninfo {

__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,

&map_dma, GFP_KERNEL);

/*********************************************************************************

static inline void *dma_alloc_writecombine(structdevice *dev, size_t size,

dma_addr_t *dma_handle,gfp_t flag)

这里是申请得到dma的地址,fbi->dev为申请内存的设备,size_t size为申请大小,dma_addr_t*dma_handle返回申请内存的物理地址,gfp_t flag申请内核空间还是用户空间地址,

该函数的返回值是得到的虚拟空间的首地址。

********************************************************************************/

if(info->screen_base) {

/*prevent initial garbage on screen */

dprintk("map_video_memory:clear %p:%08x\n",

info->screen_base,map_size);

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;

}

********************************************************************************/

 

if (ret) {

dev_err(&pdev->dev,"Failed to allocate video RAM: %d\n", ret);

ret = -ENOMEM;

goto release_clock;

}

dprintk("got videomemory\n");

fbinfo->var.xres = display->xres;

fbinfo->var.yres =display->yres;

fbinfo->var.bits_per_pixel= display->bpp;//guess what?

s3c2410fb_init_registers(fbinfo);//初始化寄存器

/*********************************************************************************

该函数初始化所有有关lcd引脚的寄存器。

static int s3c2410fb_init_registers(structfb_info *info)

{

structs3c2410fb_info *fbi = info->par;

structs3c2410fb_mach_info *mach_info = dev_get_platdata(fbi->dev);//得到存储的数据

unsignedlong flags;

void__iomem *regs = fbi->io;  //__iomem表示指向一个I/O的内存空间

void__iomem *tpal; //调色板寄存器TPAL

void__iomem *lpcsel; //对应的是跟LPC3600/LCC3600有关的 TCONSEL

if(is_s3c2412(fbi)) {

tpal= regs + S3C2412_TPAL;

lpcsel= regs + S3C2412_TCONSEL;

}else {

tpal= regs + S3C2410_TPAL;

lpcsel= regs + S3C2410_LPCSEL;

}

/*Initialise LCD with values from haret */

local_irq_save(flags);

/*********************************************************************************

local_irq_disable() , local_irq_enable() ,local_irq_save()和 local_irq_restore()为中断处理函数,主要是在要进入临界区时禁止中断和在出临界区时使能中断。

local_irq_disable() 和local_irq_enable()配对使用;

local_irq_save() 和  local_irq_restore()  配对使用。

local_irq_disable() 和local_irq_save()都可以禁止中断,但不同的是后者可以保存中断状态。local_irq_restore()在使能中断的同时还恢复了由 local_irq_save()所保存的中断状态。

********************************************************************************/

/*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;   // 禁止内部上拉

rGPCCON = 0xaaaaaaaa;

// 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 */

writel(0x00,tpal); //不使用调色板

return0;

}

********************************************************************************/

s3c2410fb_check_var(&fbinfo->var,fbinfo);

/*********************************************************************************

s3c2410fb_check_var这个函数实现比较简单,就是检测一下设定的那些参数是否合法,对于小数部分采取向上取整的方法,如果设置的数据过大,则返回无效的参数错误:-EINVAL.

********************************************************************************/

ret = s3c2410fb_cpufreq_register(info);//cpu变频有关的函数

if (ret < 0)

dev_err(&pdev->dev,"Failed to register cpufreq\n");

gotofree_video_memory;

}

ret =register_framebuffer(fbinfo);

/*********************************************************************************

前面使用framebuffer_alloc申请一个fb_info,这里使用register_framebuffe注册一个fb_info,下面会对此函数做出详细的介绍..

********************************************************************************/

if (ret < 0) {

dev_err(&pdev->dev,"Failed to register framebuffer device: %d\n",

ret);

goto free_cpufreq;

}

/* create device files */

ret =device_create_file(&pdev->dev, &dev_attr_debug);

/*********************************************************************************

int sysfs_create_file(struct kobject * kobj, const structattribute * attr);    在kobj所在目录下创建一个属性文件,文件名为attr->name.

voidsysfs_remove_file(struct kobject * kobj, const struct attribute *attr);  将属性文件attr->name从kobj所在目录下移除

随便再说一下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

会打印出来当前的debug状态,当然也可以写入,如:

/sys/devices/platform/s3c2410-lcd #  cat "on " debug

或者/sys/devices/platform/s3c2410-lcd#  cat "1 " debug

********************************************************************************/

if (ret)

dev_err(&pdev->dev,"failed to add debug attribute\n");

dev_info(&pdev->dev,"fb%d: %s frame buffer device\n",

fbinfo->node,fbinfo->fix.id);

return 0;

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:

framebuffer_release(fbinfo);

return ret;

}

之前有介绍,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),

#ifdef CONFIG_FB_BACKLIGHT

__ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve,store_bl_curve),

#endif

};

因此他们会在/sys/devices/platform/s3c2410-lcd/graphics/fb0目录下生成这些对应的属性文件,可以查看到,确实生成可这些文件:

/sys/devices/platform/s3c2410-lcd/graphics/fb0# ls

bits_per_pixel  dev             name            state           virtual_size

blank           device          pan             stride

console         mode            power           subsystem

cursor          modes           rotate          uevent

我们随便举一个例子,看看这些属性文件都做了写什么,就以modes 为例:

在写入modes的值的时候,会调用store_modes然后依次看一下调用关系:

store_modes->

fb_new_modelist->

fb_set_var->

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 = {

.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, //显示图像

};

在学习裸机的时候,我们知道控制lcd所用到的寄存器组有LCDCON1~LCDCON5 LCDSADDR1~ LCDSADDR3等等这么多寄存器。事实上,可以从上面的介绍看得出, probe函数中并没有对这些寄存器赋值,那么lcd为何开机后就可以工作了呢,那么就是因为因为上面建立的那些属性文件,通过那些属性文件,会调用s3c2410fb_ops的成员函数,这些成员函数里面有对寄存器进行初始化工作。

那么,对于我们打开设备文件常用的open、clsoe、write等函数又是调用的什么具体的函数呢?带着这个问题,我们看一下Fbmem.c (drivers\video)文件中有:

module_init(fbmem_init);

所以会执行:

fbmem_init(void)

{

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目录

if (IS_ERR(fb_class)) {

printk(KERN_WARNING "Unable to create fb class;errno = %ld\n", PTR_ERR(fb_class));

fb_class = NULL;

}

return 0;

}

上面的函数中有proc_create,这里就涉及到proc(在开机的时候的fstab文件中把该文件系统挂载上的,创建一个proc虚拟文件,应用层通过读写该文件,即可实现与内核的交互。),那么就简单的学习一下proc文件系统的如何创建文件(可参考:http://blog.csdn.net/njuitjf/article/details/16940865 ):

对于创建文件,在sysfs文件系统中,知道可以使用device_create和device_create_file两种方式,对于proc文件系统,其创建文件的方式为:

static inline structproc_dir_entry *proc_create(const char *name, mode_t mode, structproc_dir_entry *parent, const struct file_operations *proc_fops);

name就是要创建的文件名。
mode是文件的访问权限,比如0x666。
parent与proc_mkdir中的parent类似。也是父文件夹的proc_dir_entry对象。
proc_fops就是该文件的操作函数了。

比如上面的是:proc_create("fb", 0, NULL, &fb_proc_fops);

接下来,代码有:

register_chrdev(FB_MAJOR,"fb",&fb_fops)

这是是注册一个字符设备主设备号为29,名字为fb,还有文件操作。

其文件操作就是:

static const structfile_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,

#ifdefHAVE_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,

};

这里就定义了一些通过的读写操作,和之前的相同,这里就不在一一介绍,只是抛砖引玉的介绍一下ioctl函数,ioctl主要是获取一些屏幕的设置信息:

static longdo_fb_ioctl(struct fb_info *info, unsigned int cmd,

unsigned long arg)

{

struct fb_ops *fb;

struct fb_var_screeninfo var;

struct fb_fix_screeninfo fix;

struct fb_con2fbmap con2fb;

struct fb_cmap cmap_from;

struct fb_cmap_user cmap;

struct fb_event event;

void __user *argp = (void __user *)arg;

long ret = 0;

switch (cmd) {

case FBIOGET_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;

case FBIOPUT_VSCREENINFO: //根据参数判定,这里设定可变参数

if (copy_from_user(&var, argp, sizeof(var))) //数据由用户空间到内核空间传递

return -EFAULT;

console_lock();

/**********************************************************************************

console_lock();是为了保证调用者可互斥获取控制台系统而加的锁。

*****************************************************************************/

if (!lock_fb_info(info)) {//为设置fb_info的参数而申请的互斥锁

console_unlock();

return -ENODEV;

}

info->flags |= FBINFO_MISC_USEREVENT;标记位,标志着发生了来之用户空间的请求

ret = fb_set_var(info, &var);

//设置参数,最终会调用S3c2410fb.c (drivers\video)文件中的s3c2410fb_activate_var对寄存器进行赋值

info->flags &= ~FBINFO_MISC_USEREVENT; //标记来之用户空间的数据设置完毕

unlock_fb_info(info);

console_unlock();

if (!ret && copy_to_user(argp, &var,sizeof(var)))

ret = -EFAULT;

break;

case FBIOGET_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;

case FBIOPUTCMAP:  设置rgb的颜色格式

if (copy_from_user(&cmap, argp, sizeof(cmap)))

return -EFAULT;

ret = fb_set_user_cmap(&cmap, info);

//调用S3c2410fb.c(drivers\video)的s3c2410fb_setcolreg函数设置rgb的格式

break;

case FBIOGETCMAP: //得到颜色的格式

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;

case FBIOPAN_DISPLAY:  //平移显示

if (copy_from_user(&var, argp, sizeof(var)))

return -EFAULT;

console_lock();

if (!lock_fb_info(info)) {

console_unlock();

return -ENODEV;

}

ret = fb_pan_display(info, &var);

unlock_fb_info(info);

console_unlock();

if (ret == 0 && copy_to_user(argp, &var,sizeof(var)))

return -EFAULT;

break;

case FBIO_CURSOR:  //gusee what

ret = -EINVAL;

break;

case FBIOGET_CON2FBMAP:

//FBIOGET_CON2FBMAP和FBIOPUT_CON2FBMAP跟函数Notifier.c(kernel) __blocking_notifier_call_chain有介绍但是没看懂,暂时用不到,先不去了解他

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;

case FBIOPUT_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;

console_lock();

if (!lock_fb_info(info)) {

console_unlock();

return -ENODEV;

}

event.info = info;

ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP,&event);

unlock_fb_info(info);

console_unlock();

break;

case FBIOBLANK: //清屏

console_lock();

if (!lock_fb_info(info)) {

console_unlock();

return -ENODEV;

}

info->flags |= FBINFO_MISC_USEREVENT;

ret = fb_blank(info, arg);

info->flags &= ~FBINFO_MISC_USEREVENT;

unlock_fb_info(info);

console_unlock();

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

}

return ret;

}

至于对于lcd的驱动分析就到了,这一章节主要学了有关对lcd驱动的原理和如何注册一个显示设备,主要就是填充fb_info 结构体(framebuffer_alloc函数得到fb_info 结构体,register_framebuffer注册一个framebuffer)。本章节并没有对去代码的实现做出详尽的分析,主要考虑的是,之前几个章节已经对代码进行了大量的分析,已经掌握了分析代码的方法。因此,本节主要介绍的是实现的机制。

4、在LCD驱动的添加背光控制

方案1

该方案已经在对LCD驱动添加设备信息的时候介绍个,不再赘述。

方案2

添加的方法有很多,最简单的一种就是直接在驱动函数里面添加函数,时期在初始化lcd的时候就打开lcd的背光灯,修改方法是在内核源码文件下修改S3c2410fb.c (drivers\video)文件,具体操作方法是:

vi drivers/video/s3c2410fb.c

在该文件中添加头文件:

#include <plat/gpio-cfg.h>

#include <linux/gpio.h>

#include <mach/gpio-samsung.h>

然后在该文件中的s3c2410fb_init_registers函数中添加:

(大概694行添加)

gpio_direction_output(S3C2410_GPB(0), 1); //gpb0链接的背光灯。

然后保存退出重新编译内核就可以在开机的时候自动打开背光灯。

方案3

(说明:当使用方案二的时候需要把方案1中添加的语句gpio_direction_output(S3C2410_GPB(0),1);屏蔽掉,否则由于Leds-s3c24xx.c (drivers\leds)中的probe函数使用了devm_gpio_request将导致方案3失效。)

 大家因该应该记得在介绍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 = {

.gpio             = S3C2410_GPF(7),

.flags            = S3C24XX_LEDF_ACTLOW |S3C24XX_LEDF_TRISTATE,

.name            = "led7",

};

修改为

static struct s3c24xx_led_platdata smdk_pdata_led7 = {

.gpio             = S3C2410_GPB(0),

.flags            = S3C24XX_LEDF_TRISTATE,

.name            = "lcdBackLight",

};

修改说明:lcd背光灯链接的是gpb0所以这里要使用.gpio    = S3C2410_GPB(0),,之所以.flags去掉S3C24XX_LEDF_ACTLOW因为led是当引脚给0的时候为led亮,但是背光灯是给1的时候亮。后面是名字,可以任意取。

修改完毕,重新编译内核下载即可。

下载到内核会看到在。

/sys/class/leds # ls

lcdBackLight  led4         led5          led6

进入背光文件夹看以看到:

/sys/devices/platform/s3c24xx_led.3/leds/lcdBackLight# ls

brightness      max_brightness  subsystem       uevent

device          power           trigger

此时我们写入brightness文件数值1则背光灯打开;给0,背光灯则关闭。

七 linux LCD驱动代分析相关推荐

  1. linux RTC 驱动模型分析

    linux RTC 驱动模型分析 RTC(real time clock)实时时钟,主要作用是给Linux系统提供时间.RTC因为是电池供电的,所以掉电后时间不丢失.Linux内核把RTC用作&quo ...

  2. Linux PCI驱动框架分析:(Peripheral Component Interconnect,外部设备互联)

    <DPDK 20.05 | rte_pci_bus思维导图 | 第一版> <linux系统下:IO端口,内存,PCI总线 的 读写(I/O)操作> <Linux指令:ls ...

  3. Linux USB驱动框架分析 【转】

    转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结 ...

  4. linux MISC 驱动模型分析

    linux MISC 驱动模型分析 阅读led驱动程序的代码的时候,没有发现ldd3中提到的各种字符设备注册函数,而是发现了一个misc_register函数,这说明led设备是作为杂项设备出现在内核 ...

  5. Linux PCIe驱动框架分析(第二章)

    目录 项目背景 1. 概述 2. 数据结构 3. 流程分析 3.1 设备驱动模型 3.2 初始化 3.2.1 pci_bus_match 3.2.2 pci_device_probe 3.3 枚举 项 ...

  6. 深入分析Linux PCI驱动框架分析(二)

    说明: Kernel版本:4.14 ARM64处理器 使用工具:Source Insight 3.5, Visio 1. 概述 本文将分析Linux PCI子系统的框架,主要围绕Linux PCI子系 ...

  7. LINUX设备驱动模型分析之三 驱动(DRIVER)接口分析

    上一章我们分析了bus-driver-device模型中bus接口部分,本章我们将分析driver接口,在bus-driver-device模型中,driver接口是依附于bus上,而不像device ...

  8. Linux i2c驱动框架分析 (二)

    Linux i2c驱动框架分析 (一) Linux i2c驱动框架分析 (二) Linux i2c驱动框架分析 (三) 通用i2c设备驱动分析 i2c core i2c核心(drivers/i2c/i ...

  9. Linux系统驱动之分析内核自带的LCD驱动程序_基于IMX6ULL

    百问网技术交流群,百万嵌入式工程师聚集地: https://www.100ask.net/page/2248041 资料下载 coding无法使用浏览器打开,必须用git工具下载: git clone ...

最新文章

  1. mysql命令程序_MySQL命令大全经典版
  2. 方差和协方差的数据意义
  3. 使用 Syslog 连接 Sentinel
  4. 被卡性能的时候要care数据类型(洛谷P5594TLE+RE的经历,Java语言描述)
  5. ios 数字键盘左下角添加按钮_IOS数字键盘左下角添加完成按钮的实现方法
  6. 谷歌宕机,只有运维背锅吗?
  7. Yarn 调度器Scheduler详解
  8. 溯源项目(全套源码)
  9. 华为外包员工是什么样的群体?
  10. 【新手必看系列】小鸟云服务器该如何配置?
  11. element-ui tabs标签嵌套使用时 基础下划线不显示的问题
  12. 用DIV+CSS技术设计的凤阳旅游网站(web前端网页制作课作业)HTML+CSS+JavaScript
  13. Panabit专业流量监控开源软件
  14. windows 改路径有小差异
  15. 算法工程师之劝退檄文
  16. C++实现字符串匹配KMP算法
  17. 微信朋友圈营销快速加好友技巧大全
  18. MFC中使用CPropertySheet实现Tab Control
  19. 一建 :工期索赔的计算
  20. InDesign 教程:如何创建和编辑图层?

热门文章

  1. 机器学习助推分子动力学模拟
  2. Adobe XD和Sketch,谁才是王者?
  3. 如何将多行单元格数据转变成一行?这个Excel技巧你一定不知道!
  4. CogResultsAnalysisTool工具
  5. 如何接PJ322耳机孔的引脚
  6. snipaste无法在谷歌浏览器上使用F1快捷键截图||F1截图键导致浏览器跳出标题页
  7. 安卓自定义下拉列表样式_自定义下拉列表样式
  8. chromedriver中的浏览器选项
  9. B树,B+树,B*树以及R树的介绍
  10. python光棍节快乐_2020年光棍节快乐的祝福语5条