linux framebuffer study

/let's try to get the fb myers  framebuffer的学习过程

观察linux内核中的 /drivers/video/
查找包含fb的文件;会发现framebuffer的实现需要三个关键文件:
1 /linux/include/linux/fb.h
2/linux/drivers/video/fbmem.c
3/linux/drivers/video/****fb.c;比如linux内核中给我们提供的s3c2410fb.c/h;
再比如: atmel_lcdfb.c;
即根据不同的硬件要有不同的 framebuffer的实现;
这里具体分析的是 s3c2410板子的实现,以它为参考,方便我们做其他硬件设备的驱动;
一、总体概述:
用户自定义一个fb的驱动,向系统注册(系统的fbmem.c中的register_framebuffer);
然后,我们就把剩下的工作交给系统内核处理;即整个过程为:
our driver——frame buffer driver——linux kernel
(1)前提知识1:
三星的s3c2410用户手册上:LCD Controller有:
LCDCON1/2/3/4/5;LCDSADDR1/2/3(SNT/TFT分别为fb起始地址1/2,和虚拟屏幕地址);
R/G/BLUT(STN:R,G,B的查看表);DITHMODE(抖动模式);TPAL(TFT:临时调色般);
LCDINTPND/SRCPND/INTMSK(分别为 终端保留/源;编码);LPCSEL(LPC3600 控制器)
(2)前提知识2:
linux 内核的驱动管理机制(四个字,两个层面:平台+驱动,platform+driver)
定义设备资源 ,如:static struct s3c2410_uartcfg smdk2410_uartcfgs[] __initdata ={};
描述资源,定义platform(注意,这里的platform有多种,可以说有多少硬件平台就有多少platform格式);
P+D中在fb的体现就是:
static struct resource s3c_fb_resource,struct platform_device s3c_device_fb;
s3c_fb_driver,struct platform_device;

这里给处的是:/linux-2.6.*/drivers/video/platform_lcd.c中的定义
platform_device结构:
static struct platform_driver platform_lcd_driver = {
 .driver  = {
  .name = "platform-lcd",
  .owner = THIS_MODULE,
 },
 .probe  = platform_lcd_probe,
 .remove  = __devexit_p(platform_lcd_remove),
 .suspend        = platform_lcd_suspend,
 .resume         = platform_lcd_resume,
};
在/linux-2.6.*/drivers/video/s3c-fb.c中的初始化例子:
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",//注意,驱动名和设备名要对应相同。即s3c_fb_driver和s3c_device_fb的name字段都为s3c-fb
  .owner = THIS_MODULE,
 },
};
//设备定义
struct platform_device {
 const char * name;
 int  id;
 struct device dev;
 u32  num_resources;
 struct resource * resource;
 struct platform_device_id *id_entry;
 /* arch specific additions */
 struct pdev_archdata archdata;
};
//fb资源
//linux/arch/arm/plat-s3c/dev-fb.c
static struct resource s3c_fb_resource[] = {
 [0] = {
  .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,
 },
};
//fb设备

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,
};
内核处理过程:用户定义platform_device并添加到系统、注册(platform_device_register),对应在linux/arch/arm/plat-s3c/init.c中的
arch_initcall;
在来看 linux/arch/arm/mach-s3c2410/mach-smdk2410.c,这里是对整个s3c2410来说的,其中的SMDK是三星微控制器开发包的简称,即:
Samsung MCU Development Kit,其中在初始化 smdk2410_init中添加平台
platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
用户定义platform_driver添加到系统、并注册();然后控制权交给内核;
二、这里详细分解:
前提说明:所需观察文件有:
linux/drivers/video/s3c-fb.c
linux/drivers/video/s3c2410fb.c
linux/drivers/video/s3c2410fb.h
参考文件夹:
/linux/arch/arm/mach-s3c2410
/linux/arch/arm/plat-s3c
来看用户自定义部分:
在/linux/drivers/video/s3c2410fb.h中,
P+D的模式,我们先寻找driver的添加和注册部分:
(1)模块初始化
module_init(s3c2410fb_init);
说明:在 linux*/include/linux/init.h中的宏为:
#ifndef MODULE
#define module_init(x) __initcall(x);
#else
#define module_init(initfn)     \
 static inline initcall_t __inittest(void)  \
 { return initfn; }     \
 int init_module(void) __attribute__((alias(#initfn)));
而:#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn)  module_init(fn)
即,module_init是真正的驱动模块的入口,那么内核是如何区分不同的驱动呢?它是如何管理通过module_init添加的驱动呢?
来看:
#define __define_initcall(level,fn,id) \
 static initcall_t __initcall_##fn##id __used \
 __attribute__((__section__(".initcall" level ".init"))) = fn
即,我们要注册一个MODULE,必然要指定三个参数:level,fn,id;其中level和id由内核管理生成,我们只要注册一个module_init的函数入口即可。
具体信息应查看: /linux*/include/linux/compiler*.h/module.h/kobject.h/kobj_type/kset等;
(1.1)来看s3c2410fb驱动的加载,很简单,只需要
int __init s3c2410fb_init(void)
{
 int ret = platform_driver_register(&s3c2410fb_driver);

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

 return ret;
}
(2)完成加载之后呢?我们从驱动结构入手:
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,
 },
};
//一般在注册之后会有一个match匹配的过程,用于判断driver和platform的name是否一致;
在内核驱动匹配成功会,将调用probe函数,一般 probe用于具体的驱动实现。
static int __init s3c2410fb_probe(struct platform_device *pdev)
{
 return s3c24xxfb_probe(pdev, DRV_S3C2410);
}
//具体的实现为(具体含义 read the fucking code)

static char driver_name[] = "s3c2410fb";

static int __init 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;
 struct resource *res;int ret; int irq; int i; int size;u32 lcdcon1;

 mach_info = pdev->dev.platform_data;//配置信息
 if (mach_info == NULL) {
  dev_err(&pdev->dev,
   "no platform 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;

 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);//分配内存
 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 = (res->end - res->start) + 1;
 info->mem = request_mem_region(res->start, size, pdev->name);
 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;
 }

info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : 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;
 fbinfo->var.activate     = FB_ACTIVATE_NOW;
 fbinfo->var.accel_flags     = 0;
 fbinfo->var.vmode     = FB_VMODE_NONINTERLACED;

 fbinfo->fbops      = &s3c2410fb_ops;//这里是操作ops
 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, 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;
 }

info->clk = clk_get(NULL, "lcd");
 if (!info->clk || IS_ERR(info->clk)) {
  printk(KERN_ERR "failed to get lcd clock source\n");
  ret = -ENOENT;
  goto release_irq;
 }

clk_enable(info->clk);
 dprintk("got and enabled clock\n");

 msleep(1);

 info->clk_rate = clk_get_rate(info->clk);

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

  smem_len *= mach_info->displays[i].yres;
  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;

 s3c2410fb_init_registers(fbinfo);//(1) Initialise all LCD-related registers

 s3c2410fb_check_var(&fbinfo->var, fbinfo);//(2)Get the video params out of 'var'
 ret = s3c2410fb_cpufreq_register(info);
      //(3)设置s3c2410fb_cpufreq_transition等,
      //s3c2410fb_activate_var!!!!激活控制设备
     //s3c2410fb_set_lcdaddr等对控制器和其他LCD设备设置
 if (ret < 0) {
  dev_err(&pdev->dev, "Failed to register cpufreq\n");
  goto free_video_memory;
 }

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:elease_resource(info->mem);kfree(info->mem);dealloc_fb:
 platform_set_drvdata(pdev, NULL);
 framebuffer_release(fbinfo);return ret;}//(3s3c2410fb_remove,s3c2410fb_suspend,s3c2410fb_resume;


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值