现在的位置: 首页 > 综合 > 正文

S5pc100 的linux-lcd驱动学习的总结分析

2013年12月08日 ⁄ 综合 ⁄ 共 11493字 ⁄ 字号 评论关闭

前些天在Cortex-A8做小项目时对linux的LCD驱动进行了简单的代码分析,就记录下来,以便自己日后继续学习,同时希望能帮到有需要的人。

 

首先,

Lcd是一个帧缓冲设备,他的驱动构架是基于帧缓冲子系统的。

帧缓冲子系统包括以下几个文件:

Fbmem.c (核心层、提供通用接口)

S3c-fb.c(控制器驱动层、配置相关寄存器)

Dev-fb.c(资源层、提供寄存器的地址)

 

 

 

Fbmem.c分析

static int __init  fbmem_init(void)

{

proc_create (创建虚拟文件)

register_chrdev(注册设备号、绑定操作方法接口)

 class_create
(创建一类fb设备)

}

/*实现接口,供应用程序调用*/

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

#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

};

 

static int   fb_open分析

定义一个struct fb_info *info;

int fbidx = iminor(inode);  //取得次设备号

info = registered_fb[fbidx];  //从已注册的FB数组中根据次设备号取出相应的fb_info结构体

 

file->private_data = info;//将取到的fb_info放入file的私有数据中

if (info->fbops->fb_open)

              res = info->fbops->fb_open(info,1); //如果下层驱动实现了open函数,就调用open

 

 

static ssize_t fb_read
分析

if (info->fbops->fb_read)

              return info->fbops->fb_read(info, buf, count, ppos);//如果下层实现了read就调用

下面实现的是对显存内容的读取,实际应用中很少用到

 

static int  fb_mmap分析

该函数是将物理显存映射到用户空间

一般是调用底层驱动实现的mmap函数

相关I/O的映射

static long fb_ioctl
分析(重要)

int fbidx = iminor(inode);  //获取次设备号

struct fb_info *info = registered_fb[fbidx]; //根据次设备号获取fb_info结构体

static long do_fb_ioctl()函数

switch (cmd) //分析命令

case FBIOGET_VSCREENINFO://获取可变参数

case FBIOPUT_VSCREENINFO://设置可变参数

case FBIOGET_FSCREENINFO://获取固定参数

case FBIOPUTCMAP://设置颜色表

case FBIOGETCMAP://获取颜色表

case FBIOPAN_DISPLAY:

case FBIOBLANK://lcd相关操作,包括断电,关背光

case FBIO_FILLRECT:  //这俩是我自己实现,填充一个矩形

case  FBIO_COPYAREA://拷贝一块区域

 

 

 

S3c-fb.c分析

/*本层驱动采用平台设备机制编写*/

static int __init s3c_fb_init(void)

{

       return platform_driver_register(&s3c_fb_driver); //向平台设备虚拟总线注册

}

/*实现底层控制器的操作方法,即帧缓冲设备文件操作结构体*/

static struct fb_ops s3c_fb_ops = {

       .owner           = THIS_MODULE,

       .fb_check_var = s3c_fb_check_var,  //核对可变参数

       .fb_set_par     = s3c_fb_set_par,

       .fb_blank = s3c_fb_blank,

       .fb_setcolreg   = s3c_fb_setcolreg,

       .fb_fillrect      = cfb_fillrect,           //矩形填充

       .fb_copyarea   = cfb_copyarea,  //区域拷贝

       .fb_imageblit   = cfb_imageblit,   //图形填充

};

 

/*实现帧缓冲设备驱动的模块加载、卸载及平台驱动的探测和移除函数的模块*/

static struct platform_driver s3c_fb_driver = {

       .probe            = s3c_fb_probe,

       .remove          = __devexit_p(s3c_fb_remove),

       .suspend  = s3c_fb_suspend,

       .resume          = s3c_fb_resume,

       .driver            = {

              .name      = "s3c-fb",  //驱动名

              .owner    = THIS_MODULE,

       },

};

 

 

static int s3c_fb_blank()  //LCD相关控制

 

switch (blank_mode) //分析模式

case FB_BLANK_POWERDOWN: //断电

case FB_BLANK_NORMAL:     //disable the DMA and display 0x0 (black)

case FB_BLANK_UNBLANK:    //未关背光,但是关了显示窗口

if (index == 0)  //如果是窗口0,则有将其使能或者关闭的操作

              s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0);

 

 

static int s3c_fb_set_par(struct fb_info *info) //设置lcd的参数(重点)

注:每个窗口匹配探测成功后都会执行到这个函数

 

struct fb_var_screeninfo *var = &info->var;  //获取当前fb_info的可变参数

       struct s3c_fb_win *win = info->par;        //获取窗口的当前参数

       struct s3c_fb *sfb = win->parent;          //获取窗口的父设备

       void __iomem *regs = sfb->regs;          //获取fb设备的映射后的虚拟地址

       int win_no = win->index;                            //窗口的序号

       u32 osdc_data = 0;                    

       u32 data;

       u32 pagewidth;                        //每页的宽度

       int clkdiv;

 

switch (var->bits_per_pixel)  //根据可变参数里的像素选择色彩模式

/*每行像素所占用的内存大小*/

info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;

 

/* 设置lcd参数的时候先关闭对应窗口 */

writel(0, regs + WINCON(win_no));

/*根据win0设置硬件时序*/

if (win_no == 0) {

              clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock);

 

              data = sfb->pdata->vidcon0;

              data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);

 

              if (clkdiv > 1)

                     data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR;

              else

                     data &= ~VIDCON0_CLKDIR;     /* 1:1 clock */

 

              /* write the timing data to the panel */

 

              data |= VIDCON0_ENVID | VIDCON0_ENVID_F;

              writel(data, regs + VIDCON0);

 

              data = VIDTCON0_VBPD(var->upper_margin - 1) |

                     VIDTCON0_VFPD(var->lower_margin - 1) |

                     VIDTCON0_VSPW(var->vsync_len - 1);

 

              writel(data, regs + VIDTCON0);

 

              data = VIDTCON1_HBPD(var->left_margin - 1) |

                     VIDTCON1_HFPD(var->right_margin - 1) |

                     VIDTCON1_HSPW(var->hsync_len - 1);

 

              writel(data, regs + VIDTCON1);

              /*LCD实际的大小*/

              data = VIDTCON2_LINEVAL(var->xres - 1) |

                     VIDTCON2_HOZVAL(var->yres- 1);

              writel(data, regs + VIDTCON2);

       }

 

/*将显存的地址和大小写入相关的寄存器*/

writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no));

 

       data = info->fix.smem_start + info->fix.line_length * var->yres;

       writel(data, regs + VIDW_BUF_END(win_no));

 

       pagewidth = (var->xres * var->bits_per_pixel) >> 3;

       data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) |

              VIDW_BUF_SIZE_PAGEWIDTH(pagewidth);

       writel(data, regs + VIDW_BUF_SIZE(win_no));

 

 

 

/*设置窗口位置与大小*/

窗口0

       data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0);

       writel(data, regs + VIDOSD_A(win_no));

 

       data = VIDOSDxB_BOTRIGHT_X(361) |

              VIDOSDxB_BOTRIGHT_Y(271);

 

       writel(data, regs + VIDOSD_B(win_no));

 

       data = 362* 272;

       writel(data, regs + VIDOSD_C(win_no));

      

}

 

窗口1

data = VIDOSDxA_TOPLEFT_X(364) | VIDOSDxA_TOPLEFT_Y(0);

writel(data, regs + VIDOSD_A(win_no)); //起始地址

data = VIDOSDxB_BOTRIGHT_X(479) |VIDOSDxB_BOTRIGHT_Y(271);

writel(data, regs + VIDOSD_B(win_no));//窗口结束地址

data = 116*272;

writel(data, regs + VIDOSD_D(win_no));  //窗口大小   

 

switch (var->bits_per_pixel) //根据像素选择RGB模式,我们选的是RGB:565模式

 

/*相关特效的设置,win0无特效设置*/

if (win_no > 0) {

              u32 keycon0_data = 0, keycon1_data = 0;

 

              keycon0_data = ~(WxKEYCON0_KEYBL_EN |

                            WxKEYCON0_KEYEN_F |

                            WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);

 

              keycon1_data = WxKEYCON1_COLVAL(0xff00);

 

              writel(keycon0_data, regs + WxKEYCONy(win_no-1, 0));

              writel(keycon1_data, regs + WxKEYCONy(win_no-1, 1));

       }

/*win的相关参数设置完毕,使能fb设备*/

writel(data, regs + WINCON(win_no));

       writel(0x0, regs + WINxMAP(win_no));

 

 

static int __devinit s3c_fb_probe(struct platform_device *pdev)分析,(重点)

每个驱动加载后都会用

struct bus_type platform_bus_type = {

       .name             = "platform",

       .dev_attrs       = platform_dev_attrs,

       .match           = platform_match,

       .uevent           = platform_uevent,

       .pm         = &platform_dev_pm_ops,

};

里的match函数进行设备和驱动的匹配,若匹配成功,则执行probe函数

struct device *dev = &pdev->dev;//定义并获取平台设备结构体

struct s3c_fb_platdata *pd;  //
定义帧缓冲设备的平台数据

struct s3c_fb *sfb;         //帧缓冲设备结构体

struct resource *res;       //帧缓冲平台资源

int win;

 

pd = pdev->dev.platform_data; //取得平台设备里平台数据

 

sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL);//分配帧缓冲结构体内存

 

sfb->dev = dev;

sfb->pdata = pd; //设置帧缓冲结构体相关设置

 

sfb->bus_clk = clk_get(dev, "lcd"); //获取时钟

 

clk_enable(sfb->bus_clk);//使能时钟

 

/*获取平台资源*/

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

 

/*申请内存注册并放入帧缓冲结构体*/

sfb->regs_res = request_mem_region(res->start, resource_size(res),dev_name(dev));

 

/*映射资源*/

sfb->regs = ioremap(res->start, resource_size(res));

 

/* setup gpio and output polarity controls */

 

pd->setup_gpio();

writel(pd->vidcon1, sfb->regs + VIDCON1);

 

/*设置参数前,将所有win的寄存器清零*/

for (win = 0; win < S3C_FB_MAX_WIN; win++)

              s3c_fb_clear_win(sfb, win);

/*根据不同的win,进行相应的匹配*/

ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]);

 

static int __devinit s3c_fb_probe_win()分析

struct fb_var_screeninfo *var; //帧缓冲可变参数结构体

struct fb_videomode *initmode; //多媒体模式设置

struct s3c_fb_pd_win *windata; //该窗口的平台数据

struct s3c_fb_win *win;      //用于描述本窗口的结构体

struct fb_info *fbinfo;        //帧缓冲结构体

 

/*分配fb_info结构体*/

fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) + palette_size * sizeof(u32), sfb->dev);

 

/*获取窗口平台数据*/

windata = sfb->pdata->win[win_no];

 

/*获取初始化数据*/

initmode = &windata->win_mode;

 

/*一系列数据的获取*/

win = fbinfo->par;

var = &fbinfo->var;

win->fbinfo = fbinfo;

win->parent = sfb;

win->windata = windata;

win->index = win_no;

win->palette_buffer = (u32 *)(win + 1);

 

/*分配显存*/

ret = s3c_fb_alloc_memory(sfb, win);

 

 

/* setup the r/b/g positions for the window's palette */

s3c_fb_init_palette(win_no, &win->palette);

 

/* setup the initial video mode from the window */

fb_videomode_to_var(&fbinfo->var, initmode);

 

/*设置结构体*/

fbinfo->fix.type      = FB_TYPE_PACKED_PIXELS;

fbinfo->fix.accel    = FB_ACCEL_NONE;

fbinfo->var.activate       = FB_ACTIVATE_NOW;

fbinfo->var.vmode  = FB_VMODE_NONINTERLACED;

fbinfo->var.bits_per_pixel = windata->default_bpp;

fbinfo->fbops        = &s3c_fb_ops;

fbinfo->flags          = FBINFO_FLAG_DEFAULT;

fbinfo->pseudo_palette  = &win->pseudo_palette;

 

/*核对可变参数*/

ret = s3c_fb_check_var(&fbinfo->var, fbinfo);

/*构建颜色表*/

ret = fb_alloc_cmap(&fbinfo->cmap, s3c_fb_win_pal_size(win_no), 1);

fb_set_cmap(&fbinfo->cmap, fbinfo);

 

/*设置结构体中的参数,重点,该函数上面已分析*/

s3c_fb_set_par(fbinfo);

 

/*注册帧缓冲设备*/

ret = register_framebuffer(fbinfo);

 

 

Dev-fb.c(资源层、提供寄存器的地址)

/*各种资源*/

static struct resource s3c_fb_resource[] = {

       [0] = {                    //I/O内存资源

              .start = S3C_PA_FB,

              .end   = S3C_PA_FB + SZ_16K - 1,

              .flags = IORESOURCE_MEM,

       },

       [1] = {

              .start = IRQ_LCD_VSYNC,

              .end   = IRQ_LCD_VSYNC,

              .flags = IORESOURCE_IRQ,

       },

       [2] = {

              .start = IRQ_LCD_FIFO,

              .end   = IRQ_LCD_FIFO,

              .flags = IORESOURCE_IRQ,

       },

       [3] = {

              .start = IRQ_LCD_SYSTEM,

              .end   = IRQ_LCD_SYSTEM,

              .flags = IORESOURCE_IRQ,

       },

};

 

/*平台设备结构体*/

struct platform_device s3c_device_fb = {

       .name               = "s3c-fb",

       .id             = -1,

       .num_resources       = ARRAY_SIZE(s3c_fb_resource),

       .resource   = s3c_fb_resource,

       .dev.dma_mask        = &s3c_device_fb.dev.coherent_dma_mask,

       .dev.coherent_dma_mask = 0xffffffffUL,

};

 

 

void __init s3c_fb_set_platdata ()//设置平台数据

 

 

 

Mach-smdkc100.c

 

* LCD power controller */

static void smdkc100_lcd_power_set(struct plat_lcd_data *pd,

                               unsigned int power)

{

       /* backlight */

       gpio_direction_output(S5PC100_GPD(0), power);

 

       if (power) {

              /* module reset */

              gpio_direction_output(S5PC100_GPH0(6), 1);

              mdelay(100);

              gpio_direction_output(S5PC100_GPH0(6), 0);

              mdelay(10);

              gpio_direction_output(S5PC100_GPH0(6), 1);

              mdelay(10);

       }

}

 

static struct plat_lcd_data smdkc100_lcd_power_data = { 

       .set_power     = smdkc100_lcd_power_set,

};

 

/*LCD电源控制器的驱动,在机器启动的时候执行*/

static struct platform_device smdkc100_lcd_powerdev = { 

       .name                    = "platform-lcd",

       .dev.parent             = &s3c_device_fb.dev,

       .dev.platform_data  = &smdkc100_lcd_power_data,

};

 

 

 

static void __init smdkc100_machine_init

 //该函数在机器启动的时候执行,开机就初始化的函数都放这儿

static void __init smdkc100_machine_init(void)

{

       /* I2C */

       s3c_i2c0_set_platdata(NULL);

       s3c_i2c1_set_platdata(NULL);

       i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));

       i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));

 

       s3c_fb_set_platdata(&smdkc100_lcd_pdata);

 

       /* LCD init */

       gpio_request(S5PC100_GPD(0), "GPD");

       gpio_request(S5PC100_GPH0(6), "GPH0");

       smdkc100_lcd_power_set(&smdkc100_lcd_power_data, 0);

       platform_add_devices(smdkc100_devices, ARRAY_SIZE(smdkc100_devices));

 

       /*TS*/

       #if defined(CONFIG_TOUCHSCREEN_S3C2410)

                s3c24xx_ts_set_platdata(&s5pc100_ts_cfg);

        #endif

}

 

 

 

static struct s3c_fb_platdata smdkc100_lcd_pdata __initdata = {

       .win[0]           = &smdkc100_fb_win0,

       .win[1]           = &smdkc100_fb_win1,

 

//设置 output format of Video Controller(=000)

//Selects the display mode(=00)分别为:000: RGB I/F00
= RGB Parallel format (RGB)

       .vidcon0  = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, 

 

//设置相反信号极性

       .vidcon1  = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,

 

//用来初始化管脚,配置成LCD模式。

       .setup_gpio     = s5pc100_fb_gpio_setup_24bpp,

};

抱歉!评论已关闭.