LCD驱动程序架构和分析

一、LCD驱动程序架构
1.裸机驱动代码分析
①LCD初始化:控制器初始化,端口初始化,指明帧缓冲
②LCD图形显示:将图形数据写入帧缓冲
  1. void lcd_init()
  2. {
  3.     lcd_port_init();     //初始化gpdcon和gpccon初始化
  4.     lcd_control_init();   //时序初始化和帧缓冲初始化

  5.     //打开LCD电源
  6.     GPGCON |= 0b11<<8;
  7.     LCDCON5 |= (1<<3);
  8.     LCDCON1 |= 1;
  9. }
2.帧缓冲体验
帧缓冲:内存中的一段区域,通过对内存的修改。LCD控制器从内存中获取数据,自动的控制LCD的显示。
  1. # cat tq2440.bin > /dev/fb0
将图片显示到lcd。

3.帧缓冲架构
/dev/fb0就是帧缓冲,字符设备
fbmem_init():
  1. static int __init
  2. fbmem_init(void)
  3. {
  4.     proc_create("fb", 0, NULL, &fb_proc_fops);

  5.     if (register_chrdev(FB_MAJOR,"fb",&fb_fops))                                                    //注册设备文件,注册帧缓冲,fp_fops是操作函数集
  6.         printk("unable to get major %d for fb devs ", FB_MAJOR);

  7.     fb_class = class_create(THIS_MODULE, "graphics");
  8.     if (IS_ERR(fb_class)) {
  9.         printk(KERN_WARNING "Unable to create fb class; errno = %ld ", PTR_ERR(fb_class));
  10.         fb_class = NULL;
  11.     }
  12.     return 0;
  13. }
fb_ops:
  1. static const struct file_operations fb_fops = {
  2.     .owner =    THIS_MODULE,
  3.     .read =        fb_read,                                        //写入
  4.     .write =    fb_write,
  5.     .unlocked_ioctl = fb_ioctl,
  6. #ifdef CONFIG_COMPAT
  7.     .compat_ioctl = fb_compat_ioctl,
  8. #endif
  9.     .mmap =        fb_mmap,
  10.     .open =        fb_open,
  11.     .release =    fb_release,
  12. #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
  13.     .get_unmapped_area = get_fb_unmapped_area,
  14. #endif
  15. #ifdef CONFIG_FB_DEFERRED_IO
  16.     .fsync =    fb_deferred_io_fsync,
  17. #endif
  18. };
fb_write:
  1. static ssize_t
  2. fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
  3. {
  4.     unsigned long p = *ppos;
  5.     struct inode *inode = file->f_path.dentry->d_inode;
  6.     int fbidx = iminor(inode);
  7.     struct fb_info *info = registered_fb[fbidx];
  8.     u32 *buffer, *src;
  9.     u32 __iomem *dst;
  10.     int c, i, cnt = 0, err = 0;
  11.     unsigned long total_size;

  12.     if (!info || !info->screen_base)
  13.         return -ENODEV;

  14.     if (info->state != FBINFO_STATE_RUNNING)
  15.         return -EPERM;

  16.     if (info->fbops->fb_write)
  17.         return info->fbops->fb_write(info, buf, count, ppos);
  18.     
  19.     total_size = info->screen_size;

  20.     if (total_size == 0)
  21.         total_size = info->fix.smem_len;

  22.     if (p > total_size)
  23.         return -EFBIG;

  24.     if (count > total_size) {
  25.         err = -EFBIG;
  26.         count = total_size;
  27.     }

  28.     if (count + p > total_size) {
  29.    
  30.     .........各种info
  31. }

info结构:
  1. struct fb_info {
  2.     int node;
  3.     int flags;
  4.     struct mutex lock;                                //控制io操作锁
  5.     struct fb_var_screeninfo var;/*LCD可变参数*/
  6.     struct fb_fix_screeninfo fix;/*LCD固定参数*/
  7.     struct fb_monspecs monspecs; /*LCD显示器标准*/
  8.     struct work_struct queue; /*帧缓冲事件队列*/
  9.     struct fb_pixmap pixmap; /*图像硬件mapper*/
  10.     struct fb_pixmap sprite; /*光标硬件mapper*/
  11.     struct fb_cmap cmap; /*当前的颜色表*/
  12.     struct fb_videomode *mode; /*当前的显示模式*/
  13.  
  14. #ifdef CONFIG_FB_BACKLIGHT
  15.     struct backlight_device *bl_dev;/*对应的背光设备*/
  16.     struct mutex bl_curve_mutex;
  17.     u8 bl_curve[FB_BACKLIGHT_LEVELS];/*背光调整*/
  18. #endif
  19. #ifdef CONFIG_FB_DEFERRED_IO
  20.     struct delayed_work deferred_work;
  21.     struct fb_deferred_io *fbdefio;
  22. #endif
  23.  
  24.     struct fb_ops *fbops; /*对底层硬件操作的函数指针*/
  25.     struct device *device;
  26.     struct device *dev; /*fb设备*/
  27.     int class_flag;
  28. #ifdef CONFIG_FB_TILEBLITTING
  29.     struct fb_tile_ops *tileops; /*图块Blitting*/
  30. #endif
  31.     char __iomem *screen_base; /*虚拟基地址*/
  32.     unsigned long screen_size; /*LCD IO映射的虚拟内存大小*/
  33.     void *pseudo_palette; /*伪16色颜色表*/
  34. #define FBINFO_STATE_RUNNING 0
  35. #define FBINFO_STATE_SUSPENDED 1
  36.     u32 state; /*LCD的挂起或恢复状态*/
  37.     void *fbcon_par;
  38.     void *par;
  39. };
主要是fb_var_screeninfo、fb_fix_screeninfo、fb_ops三个结构体:
struct fb_var_screeninfo:主要记录用户可以修改的控制器的参数。
  1. struct fb_var_screeninfo {
  2.     __u32 xres;            /* visible resolution        */
  3.     __u32 yres;
  4.     __u32 xres_virtual;        /* virtual resolution        */
  5.     __u32 yres_virtual;
  6.     __u32 xoffset;            /* offset from virtual to visible */
  7.     __u32 yoffset;            /* resolution            */

  8.     __u32 bits_per_pixel;        /* guess what            */每个像素的位数即BPP
  9.     __u32 grayscale;        /* != 0 Graylevels instead of colors */

  10.     struct fb_bitfield red;        /* bitfield in fb mem if true color, */
  11.     struct fb_bitfield green;    /* else only length is significant */
  12.     struct fb_bitfield blue;
  13.     struct fb_bitfield transp;    /* transparency            */    

  14.     __u32 nonstd;            /* != 0 Non standard pixel format */

  15.     __u32 activate;            /* see FB_ACTIVATE_*        */

  16.     __u32 height;            /* height of picture in mm */
  17.     __u32 width;            /* width of picture in mm */

  18.     __u32 accel_flags;        /* (OBSOLETE) see fb_info.flags */

  19.     /* Timing: All values in pixclocks, except pixclock (of course) */
  20.     __u32 pixclock;            /* pixel clock in ps (pico seconds) */
  21.     __u32 left_margin;        /* time from sync to picture    */
  22.     __u32 right_margin;        /* time from picture to sync    */
  23.     __u32 upper_margin;        /* time from sync to picture    */
  24.     __u32 lower_margin;
  25.     __u32 hsync_len;        /* length of horizontal sync    */
  26.     __u32 vsync_len;        /* length of vertical sync    */
  27.     __u32 sync;            /* see FB_SYNC_*        */
  28.     __u32 vmode;            /* see FB_VMODE_*        */
  29.     __u32 rotate;            /* angle we rotate counter clockwise */
  30.     __u32 reserved[5];        /* Reserved for future compatibility */
  31. };
fb_fix_screeninfo:主要记录用户不可以修改的控制器的参数
  1. struct fb_fix_screeninfo {
  2.     char id[16];            /* identification string eg "TT Builtin" */
  3.     unsigned long smem_start;    /* Start of frame buffer mem */
  4.                     /* (physical address) */
  5.     __u32 smem_len;            /* Length of frame buffer mem */
  6.     __u32 type;            /* see FB_TYPE_*        */
  7.     __u32 type_aux;            /* Interleave for interleaved Planes */
  8.     __u32 visual;            /* see FB_VISUAL_*        */
  9.     __u16 xpanstep;            /* zero if no hardware panning */
  10.     __u16 ypanstep;            /* zero if no hardware panning */
  11.     __u16 ywrapstep;        /* zero if no hardware ywrap */
  12.     __u32 line_length;        /* length of a line in bytes */
  13.     unsigned long mmio_start;    /* Start of Memory Mapped I/O */
  14.                     /* (physical address) */
  15.     __u32 mmio_len;            /* Length of Memory Mapped I/O */
  16.     __u32 accel;            /* Indicate to driver which    */
  17.                     /* specific chip/card we have    */
  18.     __u16 reserved[3];        /* Reserved for future compatibility */
  19. };
fb_ops:底层硬件操作的函数指针
  1. struct fb_ops {
  2.     /* open/release and usage marking */
  3.     struct module *owner;
  4.     int (*fb_open)(struct fb_info *info, int user);
  5.     int (*fb_release)(struct fb_info *info, int user);

  6.     /* For framebuffers with strange non linear layouts or that do not
  7.      * work with normal memory mapped access
  8.      */
  9.     ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
  10.              size_t count, loff_t *ppos);
  11.     ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
  12.              size_t count, loff_t *ppos);

  13.     /* checks var and eventually tweaks it to something supported,
  14.      * DO NOT MODIFY PAR */
  15.     int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);

  16.     /* set the video mode according to info->var */
  17.     int (*fb_set_par)(struct fb_info *info);

  18.     /* set color register */
  19.     int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
  20.              unsigned blue, unsigned transp, struct fb_info *info);

  21.     /* set color registers in batch */
  22.     int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);

  23.     /* blank display */
  24.     int (*fb_blank)(int blank, struct fb_info *info);

  25.     /* pan display */
  26.     int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);

  27.     /* Draws a rectangle */
  28.     void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
  29.     /* Copy data from area to another */
  30.     void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
  31.     /* Draws a image to the display */
  32.     void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);

  33.     /* Draws cursor */
  34.     int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);

  35.     /* Rotates the display */
  36.     void (*fb_rotate)(struct fb_info *info, int angle);

  37.     /* wait for blit idle, optional */
  38.     int (*fb_sync)(struct fb_info *info);

  39.     /* perform fb specific ioctl (optional) */
  40.     int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
  41.             unsigned long arg);

  42.     /* Handle 32bit compat ioctl (optional) */
  43.     int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
  44.             unsigned long arg);

  45.     /* perform fb specific mmap */
  46.     int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);

  47.     /* save current hardware state */
  48.     void (*fb_save_state)(struct fb_info *info);

  49.     /* restore saved state */
  50.     void (*fb_restore_state)(struct fb_info *info);

  51.     /* get capability given var */
  52.     void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
  53.              struct fb_var_screeninfo *var);
  54. };
http://www.cnblogs.com/lishixian/articles/2999923.html

二、LCD驱动程序分析
s3c2410fb_init:
  1. int __init s3c2410fb_init(void)
  2. {
  3.     int ret = platform_driver_register(&s3c2410fb_driver);                        //注册平台驱动设备

  4.     if (ret == 0)
  5.         ret = platform_driver_register(&s3c2412fb_driver);;

  6.     return ret;
  7. }
s3c2410fb_driver:
  1. static struct platform_driver s3c2410fb_driver = {
  2.     .probe        = s3c2410fb_probe,                                                //probe函数,里面调用了s3c24xxfb_probe
  3.     .remove        = s3c2410fb_remove,
  4.     .suspend    = s3c2410fb_suspend,
  5.     .resume        = s3c2410fb_resume,
  6.     .driver        = {
  7.         .name    = "s3c2410-lcd",
  8.         .owner    = THIS_MODULE,
  9.     },
  10. };
s3c24xxfb_probe:
  1. static int __init s3c24xxfb_probe(struct platform_device *pdev,
  2.                  enum s3c_drv_type drv_type)
  3. {
  4.     struct s3c2410fb_info *info;
  5.     struct s3c2410fb_display *display;
  6.     struct fb_info *fbinfo;
  7.     struct s3c2410fb_mach_info *mach_info;
  8.     struct resource *res;
  9.     int ret;
  10.     int irq;
  11.     int i;
  12.     int size;
  13.     u32 lcdcon1;

  14.     mach_info = pdev->dev.platform_data;
  15.     if (mach_info == NULL) {
  16.         dev_err(&pdev->dev,
  17.             "no platform data for lcd, cannot attach ");
  18.         return -EINVAL;
  19.     }

  20.     if (mach_info->default_display >= mach_info->num_displays) {
  21.         dev_err(&pdev->dev, "default is %d but only %d displays ",
  22.             mach_info->default_display, mach_info->num_displays);
  23.         return -EINVAL;
  24.     }

  25.     display = mach_info->displays + mach_info->default_display;

  26.     irq = platform_get_irq(pdev, 0);                                                       //获取中断号
  27.     if (irq < 0) {
  28.         dev_err(&pdev->dev, "no irq for device ");
  29.         return -ENOENT;
  30.     }

  31.     fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);                 //分配fb_info结构
  32.     if (!fbinfo)
  33.         return -ENOMEM;

  34.     platform_set_drvdata(pdev, fbinfo);

  35.     info = fbinfo->par;
  36.     info->dev = &pdev->dev;
  37.     info->drv_type = drv_type;

  38.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);                                   //获取寄存器地址
  39.     if (res == NULL) {
  40.         dev_err(&pdev->dev, "failed to get memory registers ");
  41.         ret = -ENXIO;
  42.         goto dealloc_fb;
  43.     }

  44.     size = (res->end - res->start) + 1;
  45.     info->mem = request_mem_region(res->start, size, pdev->name);
  46.     if (info->mem == NULL) {
  47.         dev_err(&pdev->dev, "failed to get memory region ");
  48.         ret = -ENOENT;
  49.         goto dealloc_fb;
  50.     }

  51.     info->io = ioremap(res->start, size);                                                 //转化成虚拟地址
  52.     if (info->io == NULL) {
  53.         dev_err(&pdev->dev, "ioremap() of registers failed ");
  54.         ret = -ENXIO;
  55.         goto release_mem;
  56.     }

  57.     info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);

  58.     dprintk("devinit ");

  59.     strcpy(fbinfo->fix.id, driver_name);

  60.     /* Stop the video */                                                                   //初始化fbinfo结构
  61.     lcdcon1 = readl(info->io + S3C2410_LCDCON1);
  62.     writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);

  63.     fbinfo->fix.type     = FB_TYPE_PACKED_PIXELS;
  64.     fbinfo->fix.type_aux     = 0;
  65.     fbinfo->fix.xpanstep     = 0;
  66.     fbinfo->fix.ypanstep     = 0;
  67.     fbinfo->fix.ywrapstep     = 0;
  68.     fbinfo->fix.accel     = FB_ACCEL_NONE;

  69.     fbinfo->var.nonstd     = 0;
  70.     fbinfo->var.activate     = FB_ACTIVATE_NOW;
  71.     fbinfo->var.accel_flags = 0;
  72.     fbinfo->var.vmode     = FB_VMODE_NONINTERLACED;

  73.     fbinfo->fbops         = &s3c2410fb_ops;
  74.     fbinfo->flags         = FBINFO_FLAG_DEFAULT;
  75.     fbinfo->pseudo_palette = &info->pseudo_pal;

  76.     for (i = 0; i < 256; i++)
  77.         info->palette_buffer[i] = PALETTE_BUFF_CLEAR;

  78.     ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);                      //注册中断函数
  79.     if (ret) {
  80.         dev_err(&pdev->dev, "cannot get irq %d - err %d ", irq, ret);
  81.         ret = -EBUSY;
  82.         goto release_regs;
  83.     }

  84.     info->clk = clk_get(NULL, "lcd");
  85.     if (!info->clk || IS_ERR(info->clk)) {
  86.         printk(KERN_ERR "failed to get lcd clock source ");
  87.         ret = -ENOENT;
  88.         goto release_irq;
  89.     }

  90.     clk_enable(info->clk);
  91.     dprintk("got and enabled clock ");

  92.     msleep(1);

  93.     /* find maximum required memory size for display */
  94.     for (i = 0; i < mach_info->num_displays; i++) {
  95.         unsigned long smem_len = mach_info->displays[i].xres;

  96.         smem_len *= mach_info->displays[i].yres;
  97.         smem_len *= mach_info->displays[i].bpp;
  98.         smem_len >>= 3;
  99.         if (fbinfo->fix.smem_len < smem_len)
  100.             fbinfo->fix.smem_len = smem_len;
  101.     }

  102.     /* Initialize video memory */
  103.     ret = s3c2410fb_map_video_memory(fbinfo);                                                   //为帧缓冲分配内存空间。同时使用了dma
  104.     if (ret) {
  105.         printk(KERN_ERR "Failed to allocate video RAM: %d ", ret);
  106.         ret = -ENOMEM;
  107.         goto release_clock;
  108.     }

  109.     dprintk("got video memory ");

  110.     fbinfo->var.xres = display->xres;
  111.     fbinfo->var.yres = display->yres;
  112.     fbinfo->var.bits_per_pixel = display->bpp;

  113.     s3c2410fb_init_registers(fbinfo);                                                           //GPIO初始化

  114.     s3c2410fb_check_var(&fbinfo->var, fbinfo);                                                  //设置fb_var_screeninfo

  115.     ret = register_framebuffer(fbinfo);                                                         //注册帧缓冲-register_framebuffer
  116.     if (ret < 0) {
  117.         printk(KERN_ERR "Failed to register framebuffer device: %d ",
  118.             ret);
  119.         goto free_video_memory;
  120.     }

  121.     /* create device files */
  122.     ret = device_create_file(&pdev->dev, &dev_attr_debug);
  123.     if (ret) {
  124.         printk(KERN_ERR "failed to add debug attribute ");
  125.     }

  126.     printk(KERN_INFO "fb%d: %s frame buffer device ",
  127.         fbinfo->node, fbinfo->fix.id);

  128.     return 0;

  129. free_video_memory:
  130.     s3c2410fb_unmap_video_memory(fbinfo);
  131. release_clock:
  132.     clk_disable(info->clk);
  133.     clk_put(info->clk);
  134. release_irq:
  135.     free_irq(irq, info);
  136. release_regs:
  137.     iounmap(info->io);
  138. release_mem:
  139.     release_resource(info->mem);
  140.     kfree(info->mem);
  141. dealloc_fb:
  142.     platform_set_drvdata(pdev, NULL);
  143.     framebuffer_release(fbinfo);
  144.     return ret;
  145. }
s3c2410fb_map_vedio_memory:
  1. static int __init s3c2410fb_map_video_memory(struct fb_info *info)
  2. {
  3.     struct s3c2410fb_info *fbi = info->par;
  4.     dma_addr_t map_dma;
  5.     unsigned map_size = PAGE_ALIGN(info->fix.smem_len);

  6.     dprintk("map_video_memory(fbi=%p) map_size %u ", fbi, map_size);

  7.     info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
  8.                          &map_dma, GFP_KERNEL);                                                      //为帧缓冲分配内存空间。同时使用了dma

  9.     if (info->screen_base) {
  10.         /* prevent initial garbage on screen */
  11.         dprintk("map_video_memory: clear %p:%08x ",
  12.             info->screen_base, map_size);
  13.         memset(info->screen_base, 0x00, map_size);

  14.         info->fix.smem_start = map_dma;

  15.         dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x ",
  16.             info->fix.smem_start, info->screen_base, map_size);
  17.     }

  18.     return info->screen_base ? 0 : -ENOMEM;
  19. }

分析到这里发现并没有设置帧缓冲的地址:
于是可以通过搜索帧地址来辨别,在哪个函数中实现。
s3c2410fb_set_lcdaddr:
  1. static void s3c2410fb_set_lcdaddr(struct fb_info *info)
  2. {
  3.     unsigned long saddr1, saddr2, saddr3;
  4.     struct s3c2410fb_info *fbi = info->par;
  5.     void __iomem *regs = fbi->io;

  6.     saddr1 = info->fix.smem_start >> 1;
  7.     saddr2 = info->fix.smem_start;
  8.     saddr2 += info->fix.line_length * info->var.yres;
  9.     saddr2 >>= 1;

  10.     saddr3 = S3C2410_OFFSIZE(0) |
  11.          S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);

  12.     dprintk("LCDSADDR1 = 0x%08lx ", saddr1);
  13.     dprintk("LCDSADDR2 = 0x%08lx ", saddr2);
  14.     dprintk("LCDSADDR3 = 0x%08lx ", saddr3);

  15.     writel(saddr1, regs + S3C2410_LCDSADDR1);                                  //这里设置了帧缓冲的地址
  16.     writel(saddr2, regs + S3C2410_LCDSADDR2);
  17.     writel(saddr3, regs + S3C2410_LCDSADDR3);
  18. }
但是如何知道调用顺序呢,可以在函数中调用dump_stack():
  1. Backtrace:
  2. [<c0049048>] (dump_backtrace+0x0/0x10c) from [<c0373fac>] (dump_stack+0x18/0x1c)
  3.  r7:232f3302 r6:5bd42233 r5:00000000 r4:3a352944
  4. [<c0373f94>] (dump_stack+0x0/0x1c) from [<c01718c8>] (s3c2410fb_set_par+0x150/0x78c)
  5. [<c0171778>] (s3c2410fb_set_par+0x0/0x78c) from [<c016a578>] (fbcon_init+0x42c/0x4b8)
  6. [<c016a14c>] (fbcon_init+0x0/0x4b8) from [<c0185800>] (visual_init+0xb8/0x100)
  7. [<c0185748>] (visual_init+0x0/0x100) from [<c018a65c>] (take_over_console+0x1f4/0x3d8)
  8.  r7:00000000 r6:c03a6c90 r5:c05a75a0 r4:00000019
  9. [<c018a468>] (take_over_console+0x0/0x3d8) from [<c0167050>] (fbcon_takeover+0x7c/0xd4)
  10. [<c0166fd4>] (fbcon_takeover+0x0/0xd4) from [<c016b1b0>] (fbcon_event_notify+0x768/0x7b0)
  11.  r5:ffffffff r4:00000000
  12. [<c016aa48>] (fbcon_event_notify+0x0/0x7b0) from [<c0070784>] (notifier_call_chain+0x54/0x94)
  13. [<c0070730>] (notifier_call_chain+0x0/0x94) from [<c0070d08>] (__blocking_notifier_call_chain+0x54/0x6c)
  14.  r9:c04b5008 r8:00000005 r7:ffffffff r6:c381dd98 r5:c04c5470
  15. r4:c04c547c
  16. [<c0070cb4>] (__blocking_notifier_call_chain+0x0/0x6c) from [<c0070d40>] (blocking_notifier_call_chain+0x20/0x28)
  17.  r8:c3842a04 r7:00000000 r6:c0513120 r5:c3842800 r4:c381dd60
  18. [<c0070d20>] (blocking_notifier_call_chain+0x0/0x28) from [<c015fc48>] (fb_notifier_call_chain+0x1c/0x24)
  19. [<c015fc2c>] (fb_notifier_call_chain+0x0/0x24) from [<c0160cf0>] (register_framebuffer+0x154/0x1fc)
  20. [<c0160b9c>] (register_framebuffer+0x0/0x1fc) from [<c0014bbc>] (s3c24xxfb_probe+0x4c4/0x6fc)
  21. [<c00146f8>] (s3c24xxfb_probe+0x0/0x6fc) from [<c0014e20>] (s3c2410fb_probe+0x14/0x18)
  22. [<c0014e0c>] (s3c2410fb_probe+0x0/0x18) from [<c019369c>] (platform_drv_probe+0x20/0x24)
  23. [<c019367c>] (platform_drv_probe+0x0/0x24) from [<c0192694>] (driver_probe_device+0x8c/0x1a0)
  24. [<c0192608>] (driver_probe_device+0x0/0x1a0) from [<c019283c>] (__driver_attach+0x94/0x98)
  25. [<c01927a8>] (__driver_attach+0x0/0x98) from [<c0191f38>] (bus_for_each_dev+0x6c/0x98)
  26.  r7:c01927a8 r6:c04c56b8 r5:c381de98 r4:00000000
  27. [<c0191ecc>] (bus_for_each_dev+0x0/0x98) from [<c0192514>] (driver_attach+0x20/0x28)
  28.  r7:c04c56b8 r6:00000000 r5:c04c56b8 r4:c001fc68
  29. [<c01924f4>] (driver_attach+0x0/0x28) from [<c0191764>] (bus_add_driver+0xa4/0x244)
  30. [<c01916c0>] (bus_add_driver+0x0/0x244) from [<c0192b00>] (driver_register+0x74/0x15c)
  31. [<c0192a8c>] (driver_register+0x0/0x15c) from [<c0193a80>] (platform_driver_register+0x6c/0x88)
  32.  r7:c381c000 r6:00000000 r5:c001fa50 r4:c001fc68
  33. [<c0193a14>] (platform_driver_register+0x0/0x88) from [<c0014e38>] (s3c2410fb_init+0x14/0x30)
  34. [<c0014e24>] (s3c2410fb_init+0x0/0x30) from [<c0044284>] (do_one_initcall+0x3c/0x1bc)
  35. [<c0044248>] (do_one_initcall+0x0/0x1bc) from [<c0008438>] (kernel_init+0x88/0xf4)
  36. [<c00083b0>] (kernel_init+0x0/0xf4) from [<c005a2e8>] (do_exit+0x0/0x620)

无欲速,无见小利。欲速,则不达;见小利,则大事不成。
原文地址:https://www.cnblogs.com/ch122633/p/7363298.html