S3C2410 LCD驱动学习心得(3)

2.3.2  实现消息的分派
fbmem.c实现了对系统全部FrameBuffer设备的统一管理。当用户尝试使用一个特定的FrameBuffer时,fbmem.c怎么知道该调用那个特定的设备驱动呢?
我们知道,Linux是通过主设备号和次设备号,对设备进行唯一标识。不同的FrameBuffer设备向fbmem.c注册时,程序分配给它们的主设备号是一样的,而次设备号是不一样的。于是我们就可以通过用户指明的次设备号,来觉得具体该调用哪一个FrameBuffer驱动。下面通过分析fbmem.c的fb_open()函数来说明。(注:一般我们写FrameBuffer驱动不需要实现open函数,这里只是说明函数流程。)
static int fb_open(struct inode *inode, struct file *file){
 int fbidx = iminor(inode);
 struct fb_info *info;
 int res;
    /* 得到真正驱动的函数指针 */
 if (!(info = registered_fb[fbidx])) return -ENODEV; 
 if (info->fbops->fb_open) {
  res = info->fbops->fb_open(info,1); //调用驱动的open()
  if (res)  module_put(info->fbops->owner);
 }
 return res;
}
当用户打开一个FrameBuffer设备的时,将调用这里的fb_open()函数。传进来的inode就是欲打开设备的设备号,包括主设备和次设备号。fb_open函数首先通过iminor()函数取得次设备号,然后查全局数组registered_fb得到设备的fb_info信息,而这里面存放了设备的操作函数集fb_ops。这样,我们就可以调用具体驱动的fb_open() 函数,实现open的操作。下面给出了一个LCD驱动的open() 函数的调用流程图,用以说明上面的步骤。

S3C2410 LCD驱动学习心得

 
 图2.4

2.3.3  开发板S3C2410 LCD驱动的流程
(1)在mach-smdk2410.c中,定义了初始的LCD参数。注意这是个全局变量。
static struct s3c2410fb_mach_info smdk2410_lcd_cfg = {
 .regs= {
  .lcdcon1 = S3C2410_LCDCON1_TFT16BPP |
   S3C2410_LCDCON1_TFT|
   S3C2410_LCDCON1_CLKVAL(7),
  ......
 },
 .width  = 240,   .height = 320,
 .xres = {.min = 240,.max= 240,.defval = 240},
 .bpp   = {.min = 16,  .max= 16,  .defval = 16},
 ......
};
(2)内核初始化时候调用s3c2410fb_probe函数。下面分析这个函数的做的工作。首先先动态分配s3c2410fb_info空间。
   fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info),&pdev->dev);
把域mach_info指向mach-smdk2410.c中的smdk2410_lcd_cfg 。
info->mach_info = pdev->dev.platform_data;
设置fb_info域的fix,var,fops字段。

fbinfo->fix.type  =  FB_TYPE_PACKED_PIXELS;
fbinfo->fix.type_aux     = 0;
fbinfo->fix.xpanstep     = 0;

fbinfo->var.nonstd     = 0;
fbinfo->var.activate   = FB_ACTIVATE_NOW;
fbinfo->var.height     = mach_info->height;
fbinfo->var.width     = mach_info->width;

fbinfo->fbops      = &s3c2410fb_ops;
……
该函数调用s3c2410fb_map_video_memory()申请DMA内存,即显存。

fbi->map_size = PAGE_ALIGN(fbi->fb->fix.smem_len + PAGE_SIZE);
fbi->map_cpu  = dma_alloc_writecombine(fbi->dev, fbi->map_size,
          &fbi->map_dma, GFP_KERNEL);

fbi->map_size = fbi->fb->fix.smem_len;
…….
设置控制寄存器,设置硬件寄存器。

memcpy(&info->regs, &mach_info->regs,sizeof(info->regs));
info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;
……….
调用函数s3c2410fb_init_registers(),把初始值写入寄存器。

writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);
   writel(fbi->regs.lcdcon2, S3C2410_LCDCON2);

(3)当用户调用mmap()映射内存的时候,Fbmem.c把刚才设置好的显存区域映射给用户。
  start = info->fix.smem_start;
  len = PAGE_ALIGN( (start & ~PAGE_MASK) + info->fix.smem_len);
  io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,vma->vm_page_prot);
  ……
这样就完成了驱动初始化到用户调用的整个过程。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwgsfj.html