LCD设备驱动(三)

千淘万漉虽辛苦,吹尽狂沙始到金。      ——刘禹锡《浪淘沙》

前一篇我们分析了LCD驱动的大部分结构体,其余未介绍的结构体在分析源码的时候再一一介绍。


首先,我们的LCD源程序: linux-3.0.54/drivers/video/s3c2410fb.c, 一拿到代码,我首先找init入口函数
int __init s3c2410fb_init(void)
{
	int ret = platform_driver_register(&s3c2410fb_driver);

	if (ret == 0)
		ret = platform_driver_register(&s3c2412fb_driver);

	return ret;
}

然后我们很自然的去找这个s3c2410fb_driver
static struct platform_driver s3c2410fb_driver = {
	.probe		= s3c2410fb_probe,
	.remove		= __devexit_p(s3c2410fb_remove),
	.suspend	= s3c2410fb_suspend,
	.resume		= s3c2410fb_resume,
	.driver		= {
		.name	= "s3c2410-lcd",
		.owner	= THIS_MODULE,
	},
};
这里的name字段为s3c2410-lcd,第一反应找到同名的device,用grep在内核目录下搜一下就出来了 arch/arm/plat-s3c24xx/devs.c
struct platform_device s3c_device_lcd = {
     .name             = "s3c2410-lcd",  
     .id               = -1,
     .num_resources    = ARRAY_SIZE(s3c_lcd_resource),  //资源数量
     .resource         = s3c_lcd_resource,   //引用上面的资源
     .dev              = {
         .dma_mask       = &s3c_device_lcd_dmamask,
         .coherent_dma_mask  = 0xffffffffUL
    }
 };


这里面最重要的成员一看就是resource了,毕竟是资源嘛

好了平台设备和平台驱动都找到了,接下来找媒婆(probe)了
static int __devinit s3c2410fb_probe(struct platform_device *pdev)
{
	return s3c24xxfb_probe(pdev, DRV_S3C2410);
}
媒婆给我们指了一条明路:s3c24xxfb_probe函数:

static int __devinit s3c24xxfb_probe(struct platform_device *pdev,
                    enum s3c_drv_type drv_type)
  {
      struct s3c2410fb_info *info;
      struct s3c2410fb_display *display;
      struct fb_info *fbinfo;
      struct s3c2410fb_mach_info *mach_info;  //mach_info定义
      struct resource *res;     //我们的resource在linux-3.0.54/arch/arm/plat-s3c24xx/devs.c,源码在下面。
      int ret;
      int irq;
      int i;
      int size;
      u32 lcdcon1;
	  
	  mach_info = pdev->dev.platform_data;   //获取平台设备的平台数据,也就是上一篇提到的结构体s3c2410fb_mach_info   
      if (mach_info == NULL) {                //判断是否读写成功, 执行完上面的语句后mach_info指向s3c2440_fb_info结构体,而不为NULL  
          dev_err(&pdev->dev,
              "no platform data for lcd, cannot attach\n");
               return -EINVAL;
      }
         /*s3c2440_fb_info设置了default_dsiplay = 0,num_displays = 1,所以这句不会执行*/
      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;  /*获得在内核中定义的FrameBuffer平台设备的LCD配置信息结构体数据*/
  
      irq = platform_get_irq(pdev, 0);   /*获取设备中断号,在mach-smdk2440.c中定义,platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num)*/
      if (irq < 0) {
          dev_err(&pdev->dev, "no irq for device\n");
          return -ENOENT;
      }
	  
	  fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);    //分配一个结构体fb_info申请一个s3c2410fb_info_t结构体的空间用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据,如clk,resource,irq等。

      if (!fbinfo)
          return -ENOMEM;
  
      platform_set_drvdata(pdev, fbinfo);     //相当于pdev->dev->driver_data = fbinfo 
       /* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */  
      info = fbinfo->par;           /* 将私有数据赋给info指针变量 */  
      info->dev = &pdev->dev;       /* 指定struct s3c2410fb_info中dev为平台设备中的dev */  
      info->drv_type = drv_type;     /*驱动类型*/
      
	  /*映射内核资源(IO口,中断号,硬件地址转虚拟地址)*/
      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);   /*资源大小*/
      info->mem = request_mem_region(res->start, size, pdev->name);  /* 申请以res->start地址开始大小为size的I/O内存 */  
      if (info->mem == NULL) {
          dev_err(&pdev->dev, "failed to get memory region\n");
          ret = -ENOENT;
          goto dealloc_fb;
      }
        /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */  
      info->io = ioremap(res->start, size);
      if (info->io == NULL) {
          dev_err(&pdev->dev, "ioremap() of registers failed\n");
          ret = -ENXIO;
          goto release_mem;
      }
       /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */  
      info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
  
      dprintk("devinit\n");

      strcpy(fbinfo->fix.id, driver_name);   //strcpy函数将驱动名复制给fix.id
	  
	  /* Stop the video */
      lcdcon1 = readl(info->io + S3C2410_LCDCON1);
      writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);   //禁止视频输出
       /* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */  
      fbinfo->fix.type        = FB_TYPE_PACKED_PIXELS;
      fbinfo->fix.type_aux        = 0;    //以下这些根据fb_fix_screeninfo定义中的描述,当没有硬件是都设为0
      fbinfo->fix.xpanstep        = 0;
      fbinfo->fix.ypanstep        = 0;
      fbinfo->fix.ywrapstep       = 0;
      fbinfo->fix.accel       = FB_ACCEL_NONE;
       /* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */  
      fbinfo->var.nonstd      = 0;       //初始化fb_info中代表LCD可变参数的结构体fb_var_screeninfo
      fbinfo->var.activate        = FB_ACTIVATE_NOW;
      fbinfo->var.accel_flags     = 0;
      fbinfo->var.vmode       = FB_VMODE_NONINTERLACED;
        /* 设置fb_ops结构体 */  
      fbinfo->fbops           = &s3c2410fb_ops;
      fbinfo->flags           = FBINFO_FLAG_DEFAULT;
      fbinfo->pseudo_palette      = &info->pseudo_pal;   /* 设置假调色板 */  
	  /* palette_buffer[i] = 0x80000000,清空调色板*/
	  for (i = 0; i < 256; i++)
          info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
  
      /*映射中断相应函数  申请中断,s3c2410fb_irq是中断处理函数*/
      ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
      if (ret) {
          dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
          ret = -EBUSY;
          goto release_regs;
      }
      
	  /*打开LCD时钟并使能*/
      info->clk = clk_get(NULL, "lcd");
      if (IS_ERR(info->clk)) {
          printk(KERN_ERR "failed to get lcd clock source\n");
          ret = PTR_ERR(info->clk);
          goto release_irq;
      }
	  
	  clk_enable(info->clk);
      dprintk("got and enabled clock\n");
  
      msleep(1);
  
      info->clk_rate = clk_get_rate(info->clk);
    
      /*计算显示内存所需要的容量 显存大小为width * height * bpp所以还要左移3位,即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节,显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。*/	 
      /* find maximum required memory size for display */
      for (i = 0; i < mach_info->num_displays; i++) {
          unsigned long smem_len = mach_info->displays[i].xres;  //x方向分辨率
  
          smem_len *= mach_info->displays[i].yres;     //y方向分辨率
          smem_len *= mach_info->displays[i].bpp;
          smem_len >>= 3;
          if (fbinfo->fix.smem_len < smem_len)
              fbinfo->fix.smem_len = smem_len;
      }
	  
	  /* Initialize video memory */
      ret = s3c2410fb_map_video_memory(fbinfo);    //分配显存
      if (ret) {
          printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
          ret = -ENOMEM;
          goto release_clock;
      }
  
      dprintk("got video memory\n");
  
      fbinfo->var.xres = display->xres;
      fbinfo->var.yres = display->yres;
      fbinfo->var.bits_per_pixel = display->bpp;
  
    /*初始化所有的LCD控制寄存器,注册fbinfo,创建sysfs属性*/
      s3c2410fb_init_registers(fbinfo);
  
      s3c2410fb_check_var(&fbinfo->var, fbinfo);     //检查可变参数
	  
	  ret = s3c2410fb_cpufreq_register(info);
      if (ret < 0) {
          dev_err(&pdev->dev, "Failed to register cpufreq\n");
          goto free_video_memory;
      }
        /* 注册fb_info结构体 */  
      ret = register_framebuffer(fbinfo);
      if (ret < 0) {
          printk(KERN_ERR "Failed to register framebuffer device: %d\n",
              ret);
          goto free_cpufreq;
      }
	  
	  /* create device files */
      ret = device_create_file(&pdev->dev, &dev_attr_debug);
      if (ret) {
          printk(KERN_ERR "failed to add debug attribute\n");
      }
  
      printk(KERN_INFO "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:
      platform_set_drvdata(pdev, NULL);     /* 相当于pdev->dev->driver_data = NULL */  
      framebuffer_release(fbinfo);
      return ret;
 }



static struct resource s3c_lcd_resource[] = {
     [0] = {
         .start = S3C24XX_PA_LCD,  //控制器IO端口开始地址
         .end   = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,   //结束地址
         .flags = IORESOURCE_MEM,   //这个即为LCD控制器IO端口
     },
     [1] = {
         .start = IRQ_LCD,             //LCD中断
         .end   = IRQ_LCD,            
         .flags = IORESOURCE_IRQ,       //标志位中断
     }
 
 };

本篇主要解释了probe函数大部分的代码,下一篇将probe函数大致分段总结。

忘了给上出口函数:
static void __exit s3c2410fb_cleanup(void)
{
	platform_driver_unregister(&s3c2410fb_driver);
	platform_driver_unregister(&s3c2412fb_driver);
}






                                                                                                                                      
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值