当前位置:   article > 正文

基于S3C2440的嵌入式Linux驱动——Framebuffer子系统解读_2440 framebuffer

2440 framebuffer

本文将介绍Framebuffer子系统

目标平台:TQ2440 CPU:s3c2440

LCD设备:3.5英寸,分辨率320X240


1. 概述

Framebuffer,中文名字是帧缓冲,这个帧也就是一副图像所需要的数据。因此,帧缓冲其实就是LCD设备的驱动程序。Linux中,framebuffer子系统框架如下:


核心层的代码以fbmem.c为主,核心层包括许多与具体硬件无关的代码,并且提供了API给用户空间。用户空间使用系统调用,系统调用会使用相应的API函数,最后会调用驱动层实现功能。对于不同的设备,驱动层的代码将有所不同。

接下来的内容中,首先给出framerbuffer使用的数据结构;随后简单描述framerbuffer核心层;最后,针对S3C2440,对驱动代码进行分析。

2. 数据结构

2.1 fb_info 结构

  该结构是内核用于描述一个特定framebuffer设备。其中包含了几个重要的结构,将在下面介绍。

  下列代码位于include/linux/fb.h

  1. struct fb_info {
  2. int node;
  3. int flags;
  4. struct mutex lock; /* Lock for open/release/ioctl funcs */
  5. struct fb_var_screeninfo var; /* Current var */
  6. struct fb_fix_screeninfo fix; /* Current fix */
  7. struct fb_monspecs monspecs; /* Current Monitor specs */
  8. struct work_struct queue; /* Framebuffer event queue */
  9. struct fb_pixmap pixmap; /* Image hardware mapper */
  10. struct fb_pixmap sprite; /* Cursor hardware mapper */
  11. struct fb_cmap cmap; /* Current cmap */
  12. struct list_head modelist; /* mode list */
  13. struct fb_videomode *mode; /* current mode */
  14. #ifdef CONFIG_FB_BACKLIGHT
  15. /* assigned backlight device */
  16. /* set before framebuffer registration,
  17. remove after unregister */
  18. struct backlight_device *bl_dev;
  19. /* Backlight level curve */
  20. struct mutex bl_curve_mutex;
  21. u8 bl_curve[FB_BACKLIGHT_LEVELS];
  22. #endif
  23. #ifdef CONFIG_FB_DEFERRED_IO
  24. struct delayed_work deferred_work;
  25. struct fb_deferred_io *fbdefio;
  26. #endif
  27. struct fb_ops *fbops;
  28. struct device *device; /* This is the parent */
  29. struct device *dev; /* This is this fb device */
  30. int class_flag; /* private sysfs flags */
  31. #ifdef CONFIG_FB_TILEBLITTING
  32. struct fb_tile_ops *tileops; /* Tile Blitting */
  33. #endif
  34. char __iomem *screen_base; /* Virtual address */
  35. unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
  36. void *pseudo_palette; /* Fake palette of 16 colors */
  37. #define FBINFO_STATE_RUNNING 0
  38. #define FBINFO_STATE_SUSPENDED 1
  39. u32 state; /* Hardware state i.e suspend */
  40. void *fbcon_par; /* fbcon use-only private area */
  41. /* From here on everything is device dependent */
  42. void *par;
  43. };

2.2 fb_fix_screeninfo结构

  该数据结构是不可改变的,也就是说用户空间不能改变该结构中的任何成员,在核心层我们将会看到这点。

  下列代码位于include/linux/fb.h

  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. };

2.3 fb_var_screeninfo结构

  该数据结构是可以改变的,也就是说用户空间可以改变该结构中的成员。该数据结构中的很多成员就由板级信息复制而来,在驱动代码中我们将会看到这点。

  下列代码位于include/linux/fb.h

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

2.4 fb_ops结构

  该结构描述了用于fb_info的方法,这些方法中有些是要驱动程序提供的,而有些可以使用内核提供的方法。

 下列代码位于include/linux/fb.h

  1. /*
  2. * Frame buffer operations
  3. *
  4. * LOCKING NOTE: those functions must _ALL_ be called with the console
  5. * semaphore held, this is the only suitable locking mechanism we have
  6. * in 2.6. Some may be called at interrupt time at this point though.
  7. */
  8. struct fb_ops {
  9. /* open/release and usage marking */
  10. struct module *owner;
  11. int (*fb_open)(struct fb_info *info, int user);
  12. int (*fb_release)(struct fb_info *info, int user);
  13. /* For framebuffers with strange non linear layouts or that do not
  14. * work with normal memory mapped access
  15. */
  16. ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
  17. size_t count, loff_t *ppos);
  18. ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
  19. size_t count, loff_t *ppos);
  20. /* checks var and eventually tweaks it to something supported,
  21. * DO NOT MODIFY PAR */
  22. int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
  23. /* set the video mode according to info->var */
  24. int (*fb_set_par)(struct fb_info *info);
  25. /* set color register */
  26. int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
  27. unsigned blue, unsigned transp, struct fb_info *info);
  28. /* set color registers in batch */
  29. int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
  30. /* blank display */
  31. int (*fb_blank)(int blank, struct fb_info *info);
  32. /* pan display */
  33. int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
  34. /* Draws a rectangle */
  35. void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
  36. /* Copy data from area to another */
  37. void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
  38. /* Draws a image to the display */
  39. void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
  40. /* Draws cursor */
  41. int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
  42. /* Rotates the display */
  43. void (*fb_rotate)(struct fb_info *info, int angle);
  44. /* wait for blit idle, optional */
  45. int (*fb_sync)(struct fb_info *info);
  46. /* perform fb specific ioctl (optional) */
  47. int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
  48. unsigned long arg);
  49. /* Handle 32bit compat ioctl (optional) */
  50. int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
  51. unsigned long arg);
  52. /* perform fb specific mmap */
  53. int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
  54. /* save current hardware state */
  55. void (*fb_save_state)(struct fb_info *info);
  56. /* restore saved state */
  57. void (*fb_restore_state)(struct fb_info *info);
  58. /* get capability given var */
  59. void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
  60. struct fb_var_screeninfo *var);
  61. };


3. frambuffer核心层

首先来看下frmaebuffer子系统的初始化函数。

3.1 fbmem_init和fbmem_exit

下列代码位于drivers/video/fbmem.c

  1. /**
  2. * fbmem_init - init frame buffer subsystem
  3. *
  4. * Initialize the frame buffer subsystem.
  5. *
  6. * NOTE: This function is _only_ to be called by drivers/char/mem.c.
  7. *
  8. */
  9. static int __init
  10. fbmem_init(void)
  11. {
  12. proc_create("fb", 0, NULL, &fb_proc_fops);
  13. if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) /*注册字符设备,major=29*/
  14. printk("unable to get major %d for fb devs\n", FB_MAJOR);
  15. fb_class = class_create(THIS_MODULE, "graphics"); /*创建类*/
  16. if (IS_ERR(fb_class)) {
  17. printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
  18. fb_class = NULL;
  19. }
  20. return 0;
  21. }
  22. #ifdef MODULE
  23. module_init(fbmem_init);
  24. static void __exit
  25. fbmem_exit(void)
  26. {
  27. remove_proc_entry("fb", NULL);
  28. class_destroy(fb_class);
  29. unregister_chrdev(FB_MAJOR, "fb");
  30. }
  31. module_exit(fbmem_exit);
  32. MODULE_LICENSE("GPL");
  33. MODULE_DESCRIPTION("Framebuffer base");
  34. #else
  35. subsys_initcall(fbmem_init);
  36. #endif
  37. static const struct file_operations fb_fops = {
  38.     .owner =    THIS_MODULE,
  39.     .read =        fb_read,
  40.     .write =    fb_write,
  41.     .unlocked_ioctl = fb_ioctl,
  42. #ifdef CONFIG_COMPAT
  43.     .compat_ioctl = fb_compat_ioctl,
  44. #endif
  45.     .mmap =        fb_mmap,
  46.     .open =        fb_open,
  47.     .release =    fb_release,
  48. #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
  49.     .get_unmapped_area = get_fb_unmapped_area,
  50. #endif
  51. #ifdef CONFIG_FB_DEFERRED_IO
  52.     .fsync =    fb_deferred_io_fsync,
  53. #endif
  54. };

我们看到,如果不是作为模块,那么该初始化程序将在subsys_initcall阶段被调用。初始化时,仅仅注册了一个字符设备,并创建了一个类。通过字符设备,提供了API给用户空间,包open,release,write,read等。

随后我们看看如何分配一个fb_info结构。

3.2 framebuffer_alloc

下列代码位于drivers/video/fbmem.c

  1. /**
  2. * framebuffer_alloc - creates a new frame buffer info structure
  3. *
  4. * @size: size of driver private data, can be zero
  5. * @dev: pointer to the device for this fb, this can be NULL
  6. *
  7. * Creates a new frame buffer info structure. Also reserves @size bytes
  8. * for driver private data (info->par). info->par (if any) will be
  9. * aligned to sizeof(long).
  10. *
  11. * Returns the new structure, or NULL if an error occured.
  12. *
  13. */
  14. struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
  15. {
  16. #define BYTES_PER_LONG (BITS_PER_LONG/8)
  17. #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
  18. int fb_info_size = sizeof(struct fb_info);
  19. struct fb_info *info;
  20. char *p;
  21. if (size)
  22. fb_info_size += PADDING;
  23. p = kzalloc(fb_info_size + size, GFP_KERNEL);
  24. if (!p)
  25. return NULL;
  26. info = (struct fb_info *) p;
  27. if (size)
  28. info->par = p + fb_info_size;
  29. info->device = dev;
  30. #ifdef CONFIG_FB_BACKLIGHT
  31. mutex_init(&info->bl_curve_mutex);
  32. #endif
  33. return info;
  34. #undef PADDING
  35. #undef BYTES_PER_LONG
  36. }
  37. EXPORT_SYMBOL(framebuffer_alloc);
在进行分配时,根据参数size的大小,分配了b_info_size + size的空间,然后让fb_info->par指向size的空间。因此par所指向的空间可视为设备特有的数据。

在分配了fb_info结构之后,需要将它注册到内核中。注册由register_framebuffer完成。我们来看下。

3.3 register_framebuffer

下列代码位于drivers/video/fbmem.c

  1. /**
  2. * register_framebuffer - registers a frame buffer device
  3. * @fb_info: frame buffer info structure
  4. *
  5. * Registers a frame buffer device @fb_info.
  6. *
  7. * Returns negative errno on error, or zero for success.
  8. *
  9. */
  10. int
  11. register_framebuffer(struct fb_info *fb_info)
  12. {
  13. int i;
  14. struct fb_event event;
  15. struct fb_videomode mode;
  16. if (num_registered_fb == FB_MAX) /*最多32个FB*/
  17. return -ENXIO;
  18. if (fb_check_foreignness(fb_info))
  19. return -ENOSYS;
  20. num_registered_fb++; /*对注册的FB计数*/
  21. /*寻找第一个空位*/
  22. for (i = 0 ; i < FB_MAX; i++)/*FB_MAX=32,也就是最多32个framebuffer*/
  23. if (!registered_fb[i]) /*struct fb_info *registered_fb[FB_MAX]*/
  24. break;
  25. fb_info->node = i;
  26. mutex_init(&fb_info->lock); /*初始化互斥体*/
  27. fb_info->dev = device_create(fb_class, fb_info->device,/*创建设备节点,节点名为fbx*/
  28. MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
  29. if (IS_ERR(fb_info->dev)) {
  30. /* Not fatal */
  31. printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
  32. fb_info->dev = NULL;
  33. } else
  34. fb_init_device(fb_info); /*初始化,在class/graphics/fbx/下创建设备属性*/
  35. if (fb_info->pixmap.addr == NULL) {
  36. fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);/*分配内存,1024 * 8字节*/
  37. if (fb_info->pixmap.addr) {
  38. fb_info->pixmap.size = FBPIXMAPSIZE;
  39. fb_info->pixmap.buf_align = 1;
  40. fb_info->pixmap.scan_align = 1;
  41. fb_info->pixmap.access_align = 32;
  42. fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
  43. }
  44. }
  45. fb_info->pixmap.offset = 0;
  46. if (!fb_info->pixmap.blit_x)
  47. fb_info->pixmap.blit_x = ~(u32)0;
  48. if (!fb_info->pixmap.blit_y)
  49. fb_info->pixmap.blit_y = ~(u32)0;
  50. if (!fb_info->modelist.prev || !fb_info->modelist.next) /*该链表没有指向其他节点*/
  51. INIT_LIST_HEAD(&fb_info->modelist); /*初始化链表头*/
  52. fb_var_to_videomode(&mode, &fb_info->var);/*转换fb_var_screeninfo成fb_videomode*/
  53. fb_add_videomode(&mode, &fb_info->modelist);/*添加mode至链表中*/
  54. registered_fb[i] = fb_info;
  55. event.info = fb_info;
  56. if (!lock_fb_info(fb_info))
  57. return -ENODEV;
  58. fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);/*???*/
  59. unlock_fb_info(fb_info);
  60. return 0;
  61. }

从这个函数我们可以看出,framebuffer子系统只支持32个设备。在创建了设备节点以后,建立设备属性节点,随后将fb_var_screeninfo转换成fb_videomode,最后添加fb_videomode至链表中。

我们看下其中调用的函数,首先是fb_init_device。

下列代码位于drivers/video/fbsysfs.c

  1. int fb_init_device(struct fb_info *fb_info)
  2. {
  3. int i, error = 0;
  4. dev_set_drvdata(fb_info->dev, fb_info);
  5. fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
  6. /*建立设备属性*/
  7. for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
  8. error = device_create_file(fb_info->dev, &device_attrs[i]);
  9. if (error)
  10. break;
  11. }
  12. if (error) {
  13. while (--i >= 0)
  14. device_remove_file(fb_info->dev, &device_attrs[i]);
  15. fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
  16. }
  17. return 0;
  18. }
  19. /* When cmap is added back in it should be a binary attribute
  20.  * not a text one. Consideration should also be given to converting
  21.  * fbdev to use configfs instead of sysfs */
  22. static struct device_attribute device_attrs[] = {
  23.     __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
  24.     __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
  25.     __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
  26.     __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
  27.     __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
  28.     __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
  29.     __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
  30.     __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
  31.     __ATTR(name, S_IRUGO, show_name, NULL),
  32.     __ATTR(stride, S_IRUGO, show_stride, NULL),
  33.     __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
  34.     __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
  35. #ifdef CONFIG_FB_BACKLIGHT
  36.     __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
  37. #endif
  38. };

我们可以在/sys/class/graphics/fb0下发现这些属性文件。

[root@yj423 fb0]#pwd
/sys/class/graphics/fb0
[root@yj423 fb0]#ls
bits_per_pixel  cursor          mode            pan             state           uevent
blank           dev             modes           power           stride          virtual_size
console         device          name            rotate          subsystem


接着看下fb_var_to_videomode和fb_add_videomode函数。

下列代码位于drivers/video/modedb.c和drivers/video/fb.h

  1. struct fb_videomode {
  2. const char *name; /* optional */
  3. u32 refresh; /* optional */
  4. u32 xres;
  5. u32 yres;
  6. u32 pixclock;
  7. u32 left_margin;
  8. u32 right_margin;
  9. u32 upper_margin;
  10. u32 lower_margin;
  11. u32 hsync_len;
  12. u32 vsync_len;
  13. u32 sync;
  14. u32 vmode;
  15. u32 flag;
  16. };
  17. /**
  18.  * fb_var_to_videomode - convert fb_var_screeninfo to fb_videomode
  19.  * @mode: pointer to struct fb_videomode
  20.  * @var: pointer to struct fb_var_screeninfo
  21.  */
  22. void fb_var_to_videomode(struct fb_videomode *mode,
  23.              const struct fb_var_screeninfo *var)
  24. {
  25.     u32 pixclock, hfreq, htotal, vtotal;
  26.     mode->name = NULL;
  27.     mode->xres = var->xres;
  28.     mode->yres = var->yres;
  29.     mode->pixclock = var->pixclock;
  30.     mode->hsync_len = var->hsync_len;
  31.     mode->vsync_len = var->vsync_len;
  32.     mode->left_margin = var->left_margin;
  33.     mode->right_margin = var->right_margin;
  34.     mode->upper_margin = var->upper_margin;
  35.     mode->lower_margin = var->lower_margin;
  36.     mode->sync = var->sync;
  37.     mode->vmode = var->vmode & FB_VMODE_MASK;
  38.     mode->flag = FB_MODE_IS_FROM_VAR;
  39.     mode->refresh = 0;
  40.     if (!var->pixclock)
  41.         return;
  42.     pixclock = PICOS2KHZ(var->pixclock) * 1000;
  43.     htotal = var->xres + var->right_margin + var->hsync_len +
  44.         var->left_margin;
  45.     vtotal = var->yres + var->lower_margin + var->vsync_len +
  46.         var->upper_margin;
  47.     if (var->vmode & FB_VMODE_INTERLACED)
  48.         vtotal /= 2;
  49.     if (var->vmode & FB_VMODE_DOUBLE)
  50.         vtotal *= 2;
  51.     hfreq = pixclock/htotal;
  52.     mode->refresh = hfreq/vtotal;
  53. }
  54. /**
  55.  * fb_add_videomode: adds videomode entry to modelist
  56.  * @mode: videomode to add
  57.  * @head: struct list_head of modelist
  58.  *
  59.  * NOTES:
  60.  * Will only add unmatched mode entries
  61.  */
  62. int fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)
  63. {
  64.     struct list_head *pos;
  65.     struct fb_modelist *modelist;d
  66.     struct fb_videomode *m;
  67.     int found = 0;
  68.     /*遍历所有的fb_modelist,查找mode是否存在*/
  69.     list_for_each(pos, head) {
  70.         modelist = list_entry(pos, struct fb_modelist, list);
  71.         m = &modelist->mode;
  72.         if (fb_mode_is_equal(m, mode)) {    /*比较两个fb_videomode*/
  73.             found = 1;                        /*该fb_videomode已存在*/
  74.             break;
  75.         }
  76.     }
  77.     if (!found) {    /*不存在*/
  78.         modelist = kmalloc(sizeof(struct fb_modelist),    /*分配fb_modelist*/
  79.                           GFP_KERNEL);
  80.         if (!modelist)
  81.             return -ENOMEM;
  82.         modelist->mode = *mode;            /*保存mode*/
  83.         list_add(&modelist->list, head);/*添加mode至链表中*/
  84.     }
  85.     return 0;
  86. }
  87. /**
  88.  * fb_mode_is_equal - compare 2 videomodes
  89.  * @mode1: first videomode
  90.  * @mode2: second videomode
  91.  *
  92.  * RETURNS:
  93.  * 1 if equal, 0 if not
  94.  */
  95. int fb_mode_is_equal(const struct fb_videomode *mode1,
  96.              const struct fb_videomode *mode2)
  97. {
  98.     return (mode1->xres         == mode2->xres &&
  99.         mode1->yres         == mode2->yres &&
  100.         mode1->pixclock     == mode2->pixclock &&
  101.         mode1->hsync_len    == mode2->hsync_len &&
  102.         mode1->vsync_len    == mode2->vsync_len &&
  103.         mode1->left_margin  == mode2->left_margin &&
  104.         mode1->right_margin == mode2->right_margin &&
  105.         mode1->upper_margin == mode2->upper_margin &&
  106.         mode1->lower_margin == mode2->lower_margin &&
  107.         mode1->sync         == mode2->sync &&
  108.         mode1->vmode        == mode2->vmode);
  109. }

fb_var_to_videomode函数只是将fb_var_screeninfo结构转换成fb_videomode结构。而fb_add_videomode函数查找是否该fb_videomode已经存在,如果不存在则添加到列表中。

3.4 字符设备方法

  在看过framebuffer子系统建立和注册过程后,我们看下framebuffer留给用户空间的API是怎样实现的。

本小结只分析5个常用的方法,即open,release,read,write和ioctl。

  因为所有的方法和struct fb_ops定义的方法有紧密的联系,而该结构的定义由驱动程序给出,在这里我们提前看下在驱动中是如何定义的。

 下列代码位于drivers/video/s3c2410fb..c

  1. static struct fb_ops s3c2410fb_ops = {
  2. .owner = THIS_MODULE,
  3. .fb_check_var = s3c2410fb_check_var, /*检查变量的合法性*/
  4. .fb_set_par = s3c2410fb_set_par, /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/
  5. .fb_blank = s3c2410fb_blank, /*该方法支持显示消隐和去消隐*/
  6. .fb_setcolreg = s3c2410fb_setcolreg, /*设置颜色寄存器*/
  7. .fb_fillrect = cfb_fillrect, /*用像素行填充矩形框,通用库函数*/
  8. .fb_copyarea = cfb_copyarea, /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/
  9. .fb_imageblit = cfb_imageblit, /*显示一副图像,通用库函数*/
  10. };
最下面的三个方法使用的是内核提供的库函数,而上面4个则是由驱动提供。

3.4.1 open方法

下列代码位于drivers/video/fbmem.c

  1. static int
  2. fb_open(struct inode *inode, struct file *file)
  3. __acquires(&info->lock)
  4. __releases(&info->lock)
  5. {
  6. int fbidx = iminor(inode);
  7. struct fb_info *info;
  8. int res = 0;
  9. if (fbidx >= FB_MAX)
  10. return -ENODEV;
  11. info = registered_fb[fbidx]; /*在register_framebuffer函数中已经设置了元素*/
  12. if (!info)
  13. request_module("fb%d", fbidx); /*加载模块,这里不加载*/
  14. info = registered_fb[fbidx];
  15. if (!info)
  16. return -ENODEV;
  17. mutex_lock(&info->lock); /*加锁互斥体*/
  18. if (!try_module_get(info->fbops->owner)) { /*增加模块引用计数*/
  19. res = -ENODEV;
  20. goto out;
  21. }
  22. file->private_data = info; /*保存info*/
  23. if (info->fbops->fb_open) { /*这里fb_open方法为空*/
  24. res = info->fbops->fb_open(info,1);
  25. if (res)
  26. module_put(info->fbops->owner);
  27. }
  28. #ifdef CONFIG_FB_DEFERRED_IO
  29. if (info->fbdefio)
  30. fb_deferred_io_open(info, inode, file);
  31. #endif
  32. out:
  33. mutex_unlock(&info->lock); /*解锁互斥体*/
  34. return res;
  35. }
主要的一个工作就是增加模块引用计数。还有,程序会判断是否fb_open在驱动中给出,如果有则调用该方法。我们已经知道fb_open没有给出。

3.4.2 release方法

下列代码位于drivers/video/fbmem.c

  1. static int
  2. fb_release(struct inode *inode, struct file *file)
  3. __acquires(&info->lock)
  4. __releases(&info->lock)
  5. {
  6. struct fb_info * const info = file->private_data;
  7. mutex_lock(&info->lock);
  8. if (info->fbops->fb_release) /*这里fb_release为空*/
  9. info->fbops->fb_release(info,1);
  10. module_put(info->fbops->owner); /*减少模块引用计数*/
  11. mutex_unlock(&info->lock);
  12. return 0;
  13. }

和open相反,减少模块引用计数。

3.4.3 write方法

  通过调用该方法,LCD将显示画面。

  下列代码位于drivers/video/fbmem.c

  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)    /*screen_base在驱动中给出*/
  13.         return -ENODEV;
  14.     if (info->state != FBINFO_STATE_RUNNING)
  15.         return -EPERM;
  16.     if (info->fbops->fb_write)    /*没有fb_write方法*/
  17.         return info->fbops->fb_write(info, buf, count, ppos);
  18.     
  19.     total_size = info->screen_size;    /*screen_size没有给出*/
  20.     if (total_size == 0)
  21.         total_size = info->fix.smem_len;/*153600字节,驱动probe方法中计算*/
  22.     if (p > total_size)
  23.         return -EFBIG;
  24.     if (count > total_size) {    /*要写入的字节数大于153600*/
  25.         err = -EFBIG;        /*file too big*/
  26.         count = total_size;
  27.     }
  28.     if (count + p > total_size) {/*偏移量加上字节数超出了缓冲区*/
  29.         if (!err)
  30.             err = -ENOSPC;
  31.         count = total_size - p;
  32.     }
  33.     /*分配buffer,GFP_KERNEL*/
  34.     buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
  35.              GFP_KERNEL);                /
  36.     if (!buffer)
  37.         return -ENOMEM;
  38.     dst = (u32 __iomem *) (info->screen_base + p);/*修改目的指针*/
  39.     if (info->fbops->fb_sync)    /*没有定义fb_sync*/
  40.         info->fbops->fb_sync(info);
  41.     while (count) {
  42.         c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
  43.         src = buffer;
  44.         /*从buf(用户空间)拷贝数据到src中,一开始为1页,最后为count字节*/
  45.         if (copy_from_user(src, buf, c)) {    
  46.             err = -EFAULT;
  47.             break;
  48.         }
  49.         /*一次for循环,写入4个字节数据到dst处*/
  50.         for (i = c >> 2; i--; )
  51.             fb_writel(*src++, dst++);
  52.         /*最后还有3个,2个或者1个字节*/
  53.         if (c & 3) {
  54.             u8 *src8 = (u8 *) src;
  55.             u8 __iomem *dst8 = (u8 __iomem *) dst;
  56.             /*一次写入一个字节*/
  57.             for (i = c & 3; i--; )
  58.                 fb_writeb(*src8++, dst8++);
  59.             dst = (u32 __iomem *) dst8;
  60.         }
  61.         *ppos += c;    /*用户空间偏移量增加*/
  62.         buf += c;    /*用户空间指针增加*/
  63.         cnt += c;    /*修改已发送字节数*/
  64.         count -= c;    /*减去1页*/
  65.     }
  66.     kfree(buffer);    /*释放buffer*/
  67.     return (cnt) ? cnt : err;
  68. }

这里,做了一系列的检查之后,开始拷贝数据。这里一个有三个buffer,一个是用户空间提供的buf,一个是在这里新开辟的buffer,还有就是驱动层提供的screen_base。

数据流如下:
 

用户空间的数据首先被复制到buffer中,然后从buffer中复制到screen_base中,最后被映射到LCD上,LCD就显示响应的画面了。

3.4.4 read方法

该方法用于读取屏幕画面的数据。

read和write类似,只是数据流是反响的,就不多做介绍了。

下列代码位于drivers/video/fbmem.c

  1. static ssize_t
  2. fb_read(struct file *file, 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, *dst;
  9. u32 __iomem *src;
  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_read) /*没有定义fb_read*/
  17. return info->fbops->fb_read(info, buf, count, ppos);
  18. total_size = info->screen_size;
  19. if (total_size == 0)
  20. total_size = info->fix.smem_len;
  21. if (p >= total_size)
  22. return 0;
  23. if (count >= total_size)
  24. count = total_size;
  25. if (count + p > total_size)
  26. count = total_size - p;
  27. buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
  28. GFP_KERNEL);
  29. if (!buffer)
  30. return -ENOMEM;
  31. src = (u32 __iomem *) (info->screen_base + p);
  32. if (info->fbops->fb_sync)
  33. info->fbops->fb_sync(info);/*没有定义fb_sync*/
  34. while (count) {
  35. c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
  36. dst = buffer;
  37. for (i = c >> 2; i--; )
  38. *dst++ = fb_readl(src++);
  39. if (c & 3) {
  40. u8 *dst8 = (u8 *) dst;
  41. u8 __iomem *src8 = (u8 __iomem *) src;
  42. for (i = c & 3; i--;)
  43. *dst8++ = fb_readb(src8++);
  44. src = (u32 __iomem *) src8;
  45. }
  46. if (copy_to_user(buf, buffer, c)) {
  47. err = -EFAULT;
  48. break;
  49. }
  50. *ppos += c;
  51. buf += c;
  52. cnt += c;
  53. count -= c;
  54. }
  55. kfree(buffer);
  56. return (err) ? err : cnt;
  57. }
3.4.5 ioctl方法

这里只是简单的看下ioctl方法,这个函数调用很多其他的函数,详细的请自己看吧。

下列代码位于drivers/video/fbmem.c

  1. static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  2. {
  3. /*获取inode,再获取对应的fb_info*/
  4. struct inode *inode = file->f_path.dentry->d_inode;
  5. int fbidx = iminor(inode);
  6. struct fb_info *info = registered_fb[fbidx];
  7. return do_fb_ioctl(info, cmd, arg);
  8. }
  9. static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
  10.             unsigned long arg)
  11. {
  12.     struct fb_ops *fb;
  13.     struct fb_var_screeninfo var;
  14.     struct fb_fix_screeninfo fix;
  15.     struct fb_con2fbmap con2fb;
  16.     struct fb_cmap cmap_from;
  17.     struct fb_cmap_user cmap;
  18.     struct fb_event event;
  19.     void __user *argp = (void __user *)arg;
  20.     long ret = 0;
  21.     switch (cmd) {
  22.     /*获取fb_var_screeninfo*/
  23.     case FBIOGET_VSCREENINFO:    
  24.         if (!lock_fb_info(info))    /*加锁互斥体info->lock*/
  25.             return -ENODEV;
  26.         var = info->var;            /*复制var*/
  27.         unlock_fb_info(info);
  28.         ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;    /*复制var到用户空间*/
  29.         break;
  30.     /*设置fb_var_screeninfo*/
  31.     case FBIOPUT_VSCREENINFO:
  32.         if (copy_from_user(&var, argp, sizeof(var)))    /*从用户空间获取var*/
  33.             return -EFAULT;
  34.         if (!lock_fb_info(info))
  35.             return -ENODEV;
  36.         acquire_console_sem();
  37.         info->flags |= FBINFO_MISC_USEREVENT;
  38.         ret = fb_set_var(info, &var);            /*设置var*/
  39.         info->flags &= ~FBINFO_MISC_USEREVENT;
  40.         release_console_sem();
  41.         unlock_fb_info(info);
  42.         if (!ret && copy_to_user(argp, &var, sizeof(var)))
  43.             ret = -EFAULT;
  44.         break;
  45.     /*获取fb_fix_screeninfo*/    /*fix为不可改变信息,只能获取,不能设置*/
  46.     case FBIOGET_FSCREENINFO:
  47.         if (!lock_fb_info(info))
  48.             return -ENODEV;
  49.         fix = info->fix;
  50.         unlock_fb_info(info);
  51.         ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
  52.         break;
  53.     /*设置fb_cmap*/    
  54.     case FBIOPUTCMAP:
  55.         if (copy_from_user(&cmap, argp, sizeof(cmap)))
  56.             return -EFAULT;
  57.         ret = fb_set_user_cmap(&cmap, info);    /*设置fb_cmap*/
  58.         break;
  59.     /*获取fb_cmap*/    
  60.     case FBIOGETCMAP:
  61.         if (copy_from_user(&cmap, argp, sizeof(cmap)))
  62.             return -EFAULT;
  63.         if (!lock_fb_info(info))
  64.             return -ENODEV;
  65.         cmap_from = info->cmap;
  66.         unlock_fb_info(info);
  67.         ret = fb_cmap_to_user(&cmap_from, &cmap);/*获取fb_cmp*/
  68.         break;
  69.     case FBIOPAN_DISPLAY:
  70.         if (copy_from_user(&var, argp, sizeof(var)))
  71.             return -EFAULT;
  72.         if (!lock_fb_info(info))
  73.             return -ENODEV;
  74.         acquire_console_sem();
  75.         ret = fb_pan_display(info, &var);
  76.         release_console_sem();
  77.         unlock_fb_info(info);
  78.         if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
  79.             return -EFAULT;
  80.         break;
  81.     case FBIO_CURSOR:
  82.         ret = -EINVAL;
  83.         break;
  84.     case FBIOGET_CON2FBMAP:
  85.         if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
  86.             return -EFAULT;
  87.         if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
  88.             return -EINVAL;
  89.         con2fb.framebuffer = -1;
  90.         event.data = &con2fb;
  91.         if (!lock_fb_info(info))
  92.             return -ENODEV;
  93.         event.info = info;
  94.         fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
  95.         unlock_fb_info(info);
  96.         ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
  97.         break;
  98.     case FBIOPUT_CON2FBMAP:
  99.         if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
  100.             return -EFAULT;
  101.         if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
  102.             return -EINVAL;
  103.         if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
  104.             return -EINVAL;
  105.         if (!registered_fb[con2fb.framebuffer])
  106.             request_module("fb%d", con2fb.framebuffer);
  107.         if (!registered_fb[con2fb.framebuffer]) {
  108.             ret = -EINVAL;
  109.             break;
  110.         }
  111.         event.data = &con2fb;
  112.         if (!lock_fb_info(info))
  113.             return -ENODEV;
  114.         event.info = info;
  115.         ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
  116.         unlock_fb_info(info);
  117.         break;
  118.     case FBIOBLANK:
  119.         if (!lock_fb_info(info))
  120.             return -ENODEV;
  121.         acquire_console_sem();
  122.         info->flags |= FBINFO_MISC_USEREVENT;
  123.         ret = fb_blank(info, arg);        ?*最后调用驱动提供的s3c2410fb_blank*/
  124.         info->flags &= ~FBINFO_MISC_USEREVENT;
  125.         release_console_sem();
  126.         unlock_fb_info(info);
  127.         break;
  128.     default:
  129.         if (!lock_fb_info(info))
  130.             return -ENODEV;
  131.         fb = info->fbops;
  132.         if (fb->fb_ioctl)        /*fb_ioctl为空*/
  133.             ret = fb->fb_ioctl(info, cmd, arg);
  134.         else
  135.             ret = -ENOTTY;
  136.         unlock_fb_info(info);
  137.     }
  138.     return ret;
  139. }

正如2.2小结所说的,fb_fix_screeninfo只能获取不能设置,因此,ioctl只提供了获取fb_fix_screeninfo的方法,而没有提供设置fb_fix_screeninfo的方法。

3.5 小结

  本节对frambuffer的核心层进行了介绍。包括frambuffer子系统的创建,frambuffer的注册和提供给用户空间的5个API函数。下面开始介绍驱动层。

4. 驱动层

本节将开始介绍S3C2440的frambuffer驱动,该驱动源码位于drivers/video/s3c2410fb.c

首先来看下驱动模块的初始化和清除函数。

4.1 s3c2410fb_init和s3c2410fb_cleanup

  1. static struct platform_driver s3c2410fb_driver = {
  2.     .probe        = s3c2410fb_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. };
  11. int __init s3c2410fb_init(void)
  12. {
  13. int ret = platform_driver_register(&s3c2410fb_driver);
  14. if (ret == 0)
  15. ret = platform_driver_register(&s3c2412fb_driver);;
  16. return ret;
  17. }
  18. static void __exit s3c2410fb_cleanup(void)
  19. {
  20. platform_driver_unregister(&s3c2410fb_driver);
  21. platform_driver_unregister(&s3c2412fb_driver);
  22. }
  23. module_init(s3c2410fb_init);
  24. module_exit(s3c2410fb_cleanup);

当platform_driver_register调用的最后会调用probe方法。接下来就来看看probe方法。

4.2 probe方法

  1. struct s3c2410fb_info {
  2.     struct device        *dev;
  3.     struct clk        *clk;
  4.     struct resource        *mem;
  5.     void __iomem        *io;        /*虚拟地址*/
  6.     void __iomem        *irq_base;
  7.     enum s3c_drv_type    drv_type;
  8.     struct s3c2410fb_hw    regs;
  9.     unsigned int        palette_ready;
  10.     /* keep these registers in case we need to re-write palette */
  11.     u32            palette_buffer[256];
  12.     u32            pseudo_pal[16];
  13. };
  14. struct s3c2410fb_mach_info {
  15. struct s3c2410fb_display *displays; /* attached diplays info */
  16. unsigned num_displays; /* number of defined displays */
  17. unsigned default_display;
  18. /* GPIOs */
  19. unsigned long gpcup;
  20. unsigned long gpcup_mask;
  21. unsigned long gpccon;
  22. unsigned long gpccon_mask;
  23. unsigned long gpdup;
  24. unsigned long gpdup_mask;
  25. unsigned long gpdcon;
  26. unsigned long gpdcon_mask;
  27. /* lpc3600 control register */
  28. unsigned long lpcsel;
  29. };
  30. /* LCD description */
  31. struct s3c2410fb_display {
  32.     /* LCD type */
  33.     unsigned type;
  34.     /* Screen size */
  35.     unsigned short width;
  36.     unsigned short height;
  37.     /* Screen info */
  38.     unsigned short xres;
  39.     unsigned short yres;
  40.     unsigned short bpp;
  41.     unsigned pixclock;        /* pixclock in picoseconds */
  42.     unsigned short left_margin;  /* value in pixels (TFT) or HCLKs (STN) */
  43.     unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */
  44.     unsigned short hsync_len;    /* value in pixels (TFT) or HCLKs (STN) */
  45.     unsigned short upper_margin;    /* value in lines (TFT) or 0 (STN) */
  46.     unsigned short lower_margin;    /* value in lines (TFT) or 0 (STN) */
  47.     unsigned short vsync_len;    /* value in lines (TFT) or 0 (STN) */
  48.     /* lcd configuration registers */
  49.     unsigned long    lcdcon5;
  50. };
  51. static int __init s3c24xxfb_probe(struct platform_device *pdev,
  52.                   enum s3c_drv_type drv_type)
  53. {
  54.     struct s3c2410fb_info *info;
  55.     struct s3c2410fb_display *display;
  56.     struct fb_info *fbinfo;
  57.     struct s3c2410fb_mach_info *mach_info;
  58.     struct resource *res;
  59.     int ret;
  60.     int irq;
  61.     int i;
  62.     int size;
  63.     u32 lcdcon1;
  64.     /*dev.platform_data由函数s3c24xx_fb_set_platdata(mach-smdk2410.c)设置,指向s3c2410fb_mach_info*/
  65.     mach_info = pdev->dev.platform_data;    
  66.     if (mach_info == NULL) {
  67.         dev_err(&pdev->dev,
  68.             "no platform data for lcd, cannot attach\n");
  69.         return -EINVAL;
  70.     }
  71.                                     /*在mach-smdk2440.c中,default_display=0, num_displays=1*/
  72.     if (mach_info->default_display >= mach_info->num_displays) {     
  73.         dev_err(&pdev->dev, "default is %d but only %d displays\n",
  74.             mach_info->default_display, mach_info->num_displays);
  75.         return -EINVAL;
  76.     }
  77.     display = mach_info->displays + mach_info->default_display;
  78.     irq = platform_get_irq(pdev, 0);    /*获取IRQ号,16号中断*/
  79.     if (irq < 0) {
  80.         dev_err(&pdev->dev, "no irq for device\n");
  81.         return -ENOENT;
  82.     }
  83.                                         /*分配struct fb_info 其中包括sizeof字节的私有数据区*/
  84.     fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
  85.     if (!fbinfo)
  86.         return -ENOMEM;
  87.     platform_set_drvdata(pdev, fbinfo);    /*让platform_device->dev.driver_data指向struct fb_info*/
  88.     info = fbinfo->par;                    /*par指向s3c2410fb_info*/
  89.     info->dev = &pdev->dev;
  90.     info->drv_type = drv_type;
  91.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获取平台资源*/
  92.     if (res == NULL) {
  93.         dev_err(&pdev->dev, "failed to get memory registers\n");
  94.         ret = -ENXIO;
  95.         goto dealloc_fb;
  96.     }
  97.     size = (res->end - res->start) + 1;                /*IO内存申请*/
  98.     info->mem = request_mem_region(res->start, size, pdev->name);    
  99.     if (info->mem == NULL) {
  100.         dev_err(&pdev->dev, "failed to get memory region\n");
  101.         ret = -ENOENT;
  102.         goto dealloc_fb;
  103.     }
  104.     info->io = ioremap(res->start, size);            /*IO内存映射,获取lcd第一个寄存器的映射地址*/                
  105.     if (info->io == NULL) {
  106.         dev_err(&pdev->dev, "ioremap() of registers failed\n");        
  107.         ret = -ENXIO;    
  108.         goto release_mem;
  109.     }
  110.                                             /*irq_base对应的物理地址是0X4D00 0054(寄存器LCDINTPND)*/
  111.     info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
  112.                                                                             
  113.     dprintk("devinit\n");
  114.     strcpy(fbinfo->fix.id, driver_name);    /*复制名字*/
  115.     /* Stop the video */
  116.     lcdcon1 = readl(info->io + S3C2410_LCDCON1);
  117.     writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); /*禁止LCD*/
  118.     fbinfo->fix.type        = FB_TYPE_PACKED_PIXELS;
  119.     fbinfo->fix.type_aux        = 0;
  120.     fbinfo->fix.xpanstep        = 0;
  121.     fbinfo->fix.ypanstep        = 0;
  122.     fbinfo->fix.ywrapstep        = 0;
  123.     fbinfo->fix.accel        = FB_ACCEL_NONE;    /* no hardware accelerator    */
  124.     fbinfo->var.nonstd        = 0;
  125.     fbinfo->var.activate        = FB_ACTIVATE_NOW;
  126.     fbinfo->var.accel_flags     = 0;
  127.     fbinfo->var.vmode        = FB_VMODE_NONINTERLACED;
  128.     fbinfo->fbops            = &s3c2410fb_ops;
  129.     fbinfo->flags            = FBINFO_FLAG_DEFAULT;
  130.     fbinfo->pseudo_palette      = &info->pseudo_pal;
  131.     for (i = 0; i < 256; i++)
  132.         info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
  133.     ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);    /*申请IRQ,快速中断*/
  134.     if (ret) {
  135.         dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
  136.         ret = -EBUSY;
  137.         goto release_regs;
  138.     }
  139.     info->clk = clk_get(NULL, "lcd");        /*获取时钟信息*/
  140.     if (!info->clk || IS_ERR(info->clk)) {
  141.         printk(KERN_ERR "failed to get lcd clock source\n");
  142.         ret = -ENOENT;
  143.         goto release_irq;
  144.     }
  145.     clk_enable(info->clk);                    /*使能时钟*/
  146.     dprintk("got and enabled clock\n");
  147.     msleep(1);
  148.     /* find maximum required memory size for display */
  149.     /*在多个屏幕中,找出需要的最大memory*/
  150.     for (i = 0; i < mach_info->num_displays; i++) {
  151.         unsigned long smem_len = mach_info->displays[i].xres;
  152.         /*所需的memory空间 = xres * yres * bpp / 8*/
  153.         smem_len *= mach_info->displays[i].yres;
  154.         smem_len *= mach_info->displays[i].bpp;
  155.         smem_len >>= 3;
  156.         if (fbinfo->fix.smem_len < smem_len)
  157.             fbinfo->fix.smem_len = smem_len;
  158.     }
  159.     /* Initialize video memory */    /*根据上面fix.smem_len的大小,获取DMA映射内存,一致性映射方式*/
  160.     ret = s3c2410fb_map_video_memory(fbinfo);
  161.     if (ret) {
  162.         printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
  163.         ret = -ENOMEM;
  164.         goto release_clock;
  165.     }
  166.     dprintk("got video memory\n");
  167.     fbinfo->var.xres = display->xres;            /*320*/
  168.     fbinfo->var.yres = display->yres;            /*240*/    
  169.     fbinfo->var.bits_per_pixel = display->bpp;    /*16*/
  170.     s3c2410fb_init_registers(fbinfo);            /*LCD寄存器初始化*/        
  171.     s3c2410fb_check_var(&fbinfo->var, fbinfo);
  172.     ret = register_framebuffer(fbinfo);            /*注册framebuffer*/
  173.     if (ret < 0) {
  174.         printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);
  175.         goto free_video_memory;
  176.     }
  177.     /* create device files */
  178.     ret = device_create_file(&pdev->dev, &dev_attr_debug); /*添加设备属性*/
  179.     if (ret) {
  180.         printk(KERN_ERR "failed to add debug attribute\n");
  181.     }
  182.     printk(KERN_INFO "fb%d: %s frame buffer device\n",
  183.         fbinfo->node, fbinfo->fix.id);
  184.     return 0;
  185. /*一旦某个步骤发生错误,以注册的相反顺序开始注销*/
  186. free_video_memory:
  187.     s3c2410fb_unmap_video_memory(fbinfo);
  188. release_clock:
  189.     clk_disable(info->clk);
  190.     clk_put(info->clk);
  191. release_irq:
  192.     free_irq(irq, info);
  193. release_regs:
  194.     iounmap(info->io);
  195. release_mem:
  196.     release_resource(info->mem);
  197.     kfree(info->mem);
  198. dealloc_fb:
  199.     platform_set_drvdata(pdev, NULL);
  200.     framebuffer_release(fbinfo);    
  201.     return ret;
  202. }

这里使用了三个新的数据结构。s3c2410fb_info是驱动程序使用的,里面将保存所有驱动程序所要使用的资源等。而s3c2410fb_display和s3c2410fb_mach_info,是由板级信息,通过platform总线添加到内核中。

s3c2410fb_display中的成员将被复制到fb_var_screeninfo结构中。

该板级信息的定义在arch/arm/mach-s3c2440/mach-smdk2440.c中,来看下

  1. static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
  2. .lcdcon5 = S3C2410_LCDCON5_FRM565 |
  3. S3C2410_LCDCON5_INVVLINE |
  4. S3C2410_LCDCON5_INVVFRAME |
  5. S3C2410_LCDCON5_PWREN |
  6. S3C2410_LCDCON5_HWSWP,
  7. .type = S3C2410_LCDCON1_TFT,
  8. .width = 320,//240,
  9. .height = 240,//320,
  10. .pixclock = 149000,//166667, /* HCLK 60 MHz, divisor 10 */
  11. .xres = 320,//240,
  12. .yres = 240,//320,
  13. .bpp = 16,
  14. .left_margin = 20,
  15. .right_margin = 38,//8,
  16. .hsync_len = 30,//4,
  17. .upper_margin = 15,//8,
  18. .lower_margin = 12,//7,
  19. .vsync_len = 3,//4,
  20. };
  21. static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
  22. .displays = &smdk2440_lcd_cfg,
  23. .num_displays = 1,
  24. .default_display = 0,
  25. #if 0
  26. /* currently setup by downloader */
  27. .gpccon = 0xaa940659,
  28. .gpccon_mask = 0xffffffff,
  29. .gpcup = 0x0000ffff,
  30. .gpcup_mask = 0xffffffff,
  31. .gpdcon = 0xaa84aaa0,
  32. .gpdcon_mask = 0xffffffff,
  33. .gpdup = 0x0000faff,
  34. .gpdup_mask = 0xffffffff,
  35. #endif
  36. //no
  37. // .lpcsel = ((0xCE6) & ~7) | 1<<4,
  38. };

这里NOTE:每个LCD屏幕的参数不一样,因此上面的参数会有所不同,这是需要移植的地方。

随后,我们看下在probe方法中调用的几个函数。首先是s3c2410fb_map_video_memory。

  1. /*
  2. * s3c2410fb_map_video_memory():
  3. * Allocates the DRAM memory for the frame buffer. This buffer is
  4. * remapped into a non-cached, non-buffered, memory region to
  5. * allow palette and pixel writes to occur without flushing the
  6. * cache. Once this area is remapped, all virtual memory
  7. * access to the video memory should occur at the new region.
  8. */
  9. static int __init s3c2410fb_map_video_memory(struct fb_info *info)
  10. {
  11. struct s3c2410fb_info *fbi = info->par;
  12. dma_addr_t map_dma;
  13. unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
  14. dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
  15. /*分配DMA缓冲区,并保存DMA缓冲区虚拟地址*/
  16. info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
  17. &map_dma, GFP_KERNEL);
  18. if (info->screen_base) {
  19. /* prevent initial garbage on screen */
  20. dprintk("map_video_memory: clear %p:%08x\n",
  21. info->screen_base, map_size);
  22. memset(info->screen_base, 0x00, map_size); /*DMA缓冲区清0*/
  23. info->fix.smem_start = map_dma; /*保存DMA缓冲区物理地址*/
  24. dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
  25. info->fix.smem_start, info->screen_base, map_size);
  26. }
  27. return info->screen_base ? 0 : -ENOMEM;
  28. }


该函数根据fix.smem_len的大小,分配了一个DMA缓冲区,保存了该缓冲区的物理地址和虚拟地址。

接着是s3c2410fb_init_registers:

  1. /*
  2. * s3c2410fb_init_registers - Initialise all LCD-related registers
  3. */
  4. static int s3c2410fb_init_registers(struct fb_info *info)
  5. {
  6. struct s3c2410fb_info *fbi = info->par; /*par指向s3c2410fb_info*/
  7. struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
  8. unsigned long flags;
  9. void __iomem *regs = fbi->io;
  10. void __iomem *tpal;
  11. void __iomem *lpcsel;
  12. if (is_s3c2412(fbi)) {
  13. tpal = regs + S3C2412_TPAL;
  14. lpcsel = regs + S3C2412_TCONSEL;
  15. } else {
  16. tpal = regs + S3C2410_TPAL;
  17. lpcsel = regs + S3C2410_LPCSEL;
  18. }
  19. /* Initialise LCD with values from haret */
  20. local_irq_save(flags); /*禁止所有中断*/
  21. /* modify the gpio(s) with interrupts set (bjd) */ /*初始化io管脚*/
  22. modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
  23. modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
  24. modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
  25. modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
  26. local_irq_restore(flags); /*恢复中断*/
  27. dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel); /*设置TCONSEL,禁止LPC3600*/
  28. writel(mach_info->lpcsel, lpcsel);
  29. dprintk("replacing TPAL %08x\n", readl(tpal));
  30. /* ensure temporary palette disabled */
  31. writel(0x00, tpal); /*禁止调色板*/
  32. return 0;
  33. }
  34. static inline void modify_gpio(void __iomem *reg,
  35. unsigned long set, unsigned long mask)
  36. {
  37. unsigned long tmp;
  38. tmp = readl(reg) & ~mask;
  39. writel(tmp | set, reg);
  40. }
最后是s3c2410fb_check_var:

  1. /*
  2. * s3c2410fb_check_var():
  3. * Get the video params out of 'var'. If a value doesn't fit, round it up,
  4. * if it's too big, return -EINVAL.
  5. * 检查变量的合法性
  6. */
  7. static int s3c2410fb_check_var(struct fb_var_screeninfo *var,
  8. struct fb_info *info)
  9. {
  10. struct s3c2410fb_info *fbi = info->par; /*par指向s3c2410fb_info*/
  11. struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;/*指向s3c2410fb_mach_info*/
  12. struct s3c2410fb_display *display = NULL;
  13. struct s3c2410fb_display *default_display = mach_info->displays +
  14. mach_info->default_display;
  15. int type = default_display->type; /*S3C2410_LCDCON1_TFT*/
  16. unsigned i;
  17. dprintk("check_var(var=%p, info=%p)\n", var, info);
  18. /* validate x/y resolution */
  19. /* choose default mode if possible */ /*var中的成员在probe中设置*/
  20. if (var->yres == default_display->yres &&
  21. var->xres == default_display->xres &&
  22. var->bits_per_pixel == default_display->bpp)
  23. display = default_display;
  24. else
  25. for (i = 0; i < mach_info->num_displays; i++)
  26. if (type == mach_info->displays[i].type &&
  27. var->yres == mach_info->displays[i].yres &&
  28. var->xres == mach_info->displays[i].xres &&
  29. var->bits_per_pixel == mach_info->displays[i].bpp) {
  30. display = mach_info->displays + i;
  31. break;
  32. }
  33. if (!display) {
  34. dprintk("wrong resolution or depth %dx%d at %d bpp\n",
  35. var->xres, var->yres, var->bits_per_pixel);
  36. return -EINVAL;
  37. }
  38. /* it is always the size as the display */
  39. var->xres_virtual = display->xres;
  40. var->yres_virtual = display->yres;
  41. var->height = display->height;
  42. var->width = display->width;
  43. /* copy lcd settings */
  44. var->pixclock = display->pixclock;
  45. var->left_margin = display->left_margin;
  46. var->right_margin = display->right_margin;
  47. var->upper_margin = display->upper_margin;
  48. var->lower_margin = display->lower_margin;
  49. var->vsync_len = display->vsync_len;
  50. var->hsync_len = display->hsync_len;
  51. fbi->regs.lcdcon5 = display->lcdcon5;
  52. /* set display type */
  53. fbi->regs.lcdcon1 = display->type;
  54. var->transp.offset = 0;
  55. var->transp.length = 0;
  56. /* set r/g/b positions */
  57. switch (var->bits_per_pixel) {
  58. case 1:
  59. case 2:
  60. case 4:
  61. var->red.offset = 0;
  62. var->red.length = var->bits_per_pixel;
  63. var->green = var->red;
  64. var->blue = var->red;
  65. break;
  66. case 8:
  67. if (display->type != S3C2410_LCDCON1_TFT) {
  68. /* 8 bpp 332 */
  69. var->red.length = 3;
  70. var->red.offset = 5;
  71. var->green.length = 3;
  72. var->green.offset = 2;
  73. var->blue.length = 2;
  74. var->blue.offset = 0;
  75. } else {
  76. var->red.offset = 0;
  77. var->red.length = 8;
  78. var->green = var->red;
  79. var->blue = var->red;
  80. }
  81. break;
  82. case 12:
  83. /* 12 bpp 444 */
  84. var->red.length = 4;
  85. var->red.offset = 8;
  86. var->green.length = 4;
  87. var->green.offset = 4;
  88. var->blue.length = 4;
  89. var->blue.offset = 0;
  90. break;
  91. default:
  92. case 16:
  93. if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) { /*使用565格式*/
  94. /* 16 bpp, 565 format */
  95. var->red.offset = 11;
  96. var->green.offset = 5;
  97. var->blue.offset = 0;
  98. var->red.length = 5;
  99. var->green.length = 6;
  100. var->blue.length = 5;
  101. } else {
  102. /* 16 bpp, 5551 format */
  103. var->red.offset = 11;
  104. var->green.offset = 6;
  105. var->blue.offset = 1;
  106. var->red.length = 5;
  107. var->green.length = 5;
  108. var->blue.length = 5;
  109. }
  110. break;
  111. case 32:
  112. /* 24 bpp 888 and 8 dummy */
  113. var->red.length = 8;
  114. var->red.offset = 16;
  115. var->green.length = 8;
  116. var->green.offset = 8;
  117. var->blue.length = 8;
  118. var->blue.offset = 0;
  119. break;
  120. }
  121. return 0;
  122. }
  123. /* Interpretation of offset for color fields: All offsets are from the right,
  124. * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
  125. * can use the offset as right argument to <<). A pixel afterwards is a bit
  126. * stream and is written to video memory as that unmodified.
  127. *
  128. * For pseudocolor: offset and length should be the same for all color
  129. * components. Offset specifies the position of the least significant bit
  130. * of the pallette index in a pixel value. Length indicates the number
  131. * of available palette entries (i.e. # of entries = 1 << length).
  132. */
  133. struct fb_bitfield {
  134. __u32 offset; /* beginning of bitfield */
  135. __u32 length; /* length of bitfield */
  136. __u32 msb_right; /* != 0 : Most significant bit is */
  137. /* right */
  138. };
该函数主要将板级信息s3c2410fb_display复制到对应的地方,然后根据RGB的模式设置位域。

4.3 fb_ops方法

在驱动程序中,定义了fb_ops,如下:

  1. static struct fb_ops s3c2410fb_ops = {
  2. .owner = THIS_MODULE,
  3. .fb_check_var = s3c2410fb_check_var, /*检查变量的合法性*/
  4. .fb_set_par = s3c2410fb_set_par, /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/
  5. .fb_blank = s3c2410fb_blank, /*该方法支持显示消隐和去消隐*/
  6. .fb_setcolreg = s3c2410fb_setcolreg, /*设置颜色寄存器*/
  7. .fb_fillrect = cfb_fillrect, /*用像素行填充矩形框,通用库函数*/
  8. .fb_copyarea = cfb_copyarea, /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/
  9. .fb_imageblit = cfb_imageblit, /*显示一副图像,通用库函数*/
  10. };

其中s3c2410fb_check_var在4.2节中已经分析过了,最后三个方法是通用库函数,在这里不作分析。
剩余三个也是驱动程序提供的,现在对这三个程序进行分析。

4.3. 1 s3c2410fb_set_par
  1. /*
  2. * s3c2410fb_set_par - Alters the hardware state.
  3. * @info: frame buffer structure that represents a single frame buffer
  4. * 根据var中的值设置LCD控制器的寄存器
  5. */
  6. static int s3c2410fb_set_par(struct fb_info *info)
  7. {
  8. struct fb_var_screeninfo *var = &info->var;
  9. switch (var->bits_per_pixel) {
  10. case 32:
  11. case 16:
  12. case 12:
  13. info->fix.visual = FB_VISUAL_TRUECOLOR;
  14. break;
  15. case 1:
  16. info->fix.visual = FB_VISUAL_MONO01;
  17. break;
  18. default:
  19. info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
  20. break;
  21. }
  22. info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; /* 320*16/8 = 640Bytes */
  23. /* activate this new configuration */
  24. s3c2410fb_activate_var(info);
  25. return 0;
  26. }

该函数根据像素的位数设置了视觉模式,本例为16为,使用真彩色。然后计算了每行的数据元素大小。共240行。

然后调用了s3c2410fb_activate_var来设置控制器并激活LCD。

s3c2410fb_activate_var函数如下:

  1. /* s3c2410fb_activate_var
  2. *
  3. * activate (set) the controller from the given framebuffer
  4. * information
  5. */
  6. static void s3c2410fb_activate_var(struct fb_info *info)
  7. {
  8. struct s3c2410fb_info *fbi = info->par;
  9. void __iomem *regs = fbi->io;
  10. int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/
  11. struct fb_var_screeninfo *var = &info->var;
  12. int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
  13. dprintk("%s: var->xres = %d\n", __func__, var->xres);
  14. dprintk("%s: var->yres = %d\n", __func__, var->yres);
  15. dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);
  16. if (type == S3C2410_LCDCON1_TFT) {
  17. s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/
  18. --clkdiv;
  19. if (clkdiv < 0)
  20. clkdiv = 0;
  21. } else {
  22. s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
  23. if (clkdiv < 2)
  24. clkdiv = 2;
  25. }
  26. fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/
  27. /* write new registers */
  28. dprintk("new register set:\n");
  29. dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
  30. dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
  31. dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
  32. dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
  33. dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
  34. /*把计算好的值填入LCD控制器中*/
  35. writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
  36. regs + S3C2410_LCDCON1); /*仍然禁止LCD*/
  37. writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
  38. writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
  39. writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
  40. writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
  41. /* set lcd address pointers */
  42. s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/
  43. fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
  44. writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/
  45. }
其中调用的三个函数如下:
  1. static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
  2.                       unsigned long pixclk)
  3. {
  4.     unsigned long clk = clk_get_rate(fbi->clk);         /*获取当前时钟频率(Hz)*/
  5.     unsigned long long div;
  6.     /* pixclk is in picoseconds, our clock is in Hz
  7.      *
  8.      * Hz -> picoseconds is / 10^-12
  9.      */
  10.     div = (unsigned long long)clk * pixclk;
  11.     div >>= 12;            /* div / 2^12 */
  12.     do_div(div, 625 * 625UL * 625); /* div / 5^12 */
  13.     dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
  14.     return div;
  15. }
  16. /* s3c2410fb_calculate_tft_lcd_regs
  17.  *
  18.  * calculate register values from var settings
  19.  */
  20. static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,
  21.                          struct s3c2410fb_hw *regs)
  22. {
  23.     const struct s3c2410fb_info *fbi = info->par;
  24.     const struct fb_var_screeninfo *var = &info->var;
  25.     switch (var->bits_per_pixel) {
  26.     case 1:
  27.         regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;
  28.         break;
  29.     case 2:
  30.         regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
  31.         break;
  32.     case 4:
  33.         regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
  34.         break;
  35.     case 8:
  36.         regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
  37.         regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |
  38.                  S3C2410_LCDCON5_FRM565;
  39.         regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;
  40.         break;
  41.     case 16:
  42.         regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
  43.         regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;
  44.         regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;
  45.         break;
  46.     case 32:
  47.         regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;
  48.         regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |
  49.                    S3C2410_LCDCON5_HWSWP |
  50.                    S3C2410_LCDCON5_BPP24BL);
  51.         break;
  52.     default:
  53.         /* invalid pixel depth */
  54.         dev_err(fbi->dev, "invalid bpp %d\n",
  55.             var->bits_per_pixel);
  56.     }
  57.     /* update X/Y info */
  58.     dprintk("setting vert: up=%d, low=%d, sync=%d\n",
  59.         var->upper_margin, var->lower_margin, var->vsync_len);
  60.     dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
  61.         var->left_margin, var->right_margin, var->hsync_len);
  62.     /*
  63.         所有时序参数必须减1,因为在公式中:
  64.         Frame Rate = 1/ [ { (VSPW+1) + (VBPD+1) + (LIINEVAL + 1) + (VFPD+1) } x {(HSPW+1) + (HBPD +1)
  65.         + (HFPD+1) + (HOZVAL + 1) } x { 2 x ( CLKVAL+1 ) / ( HCLK ) } ]
  66.     */
  67.     regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |
  68.             S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
  69.             S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
  70.             S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
  71.     regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
  72.             S3C2410_LCDCON3_HFPD(var->left_margin - 1) |
  73.             S3C2410_LCDCON3_HOZVAL(var->xres - 1);
  74.     regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
  75. }
  76. /* s3c2410fb_set_lcdaddr
  77. *
  78. * initialise lcd controller address pointers
  79. */
  80. static void s3c2410fb_set_lcdaddr(struct fb_info *info)
  81. {
  82. unsigned long saddr1, saddr2, saddr3;
  83. struct s3c2410fb_info *fbi = info->par;
  84. void __iomem *regs = fbi->io;
  85. saddr1 = info->fix.smem_start >> 1; /*帧缓冲区起始地址*/
  86. saddr2 = info->fix.smem_start;
  87. saddr2 += info->fix.line_length * info->var.yres;
  88. saddr2 >>= 1; /*帧缓冲区结束地址*/
  89. saddr3 = S3C2410_OFFSIZE(0) |
  90. S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff); /*offset = 0, pagewidth = 320*/
  91. dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
  92. dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
  93. dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
  94. writel(saddr1, regs + S3C2410_LCDSADDR1);
  95. writel(saddr2, regs + S3C2410_LCDSADDR2);
  96. writel(saddr3, regs + S3C2410_LCDSADDR3);
  97. }

s3c2410fb_calc_pixclk用于计算时钟频率。

s3c2410fb_calculate_tft_lcd_regs设置了LCD的控制寄存器。

s3c2410fb_set_lcdaddr设置了LCD的地址寄存器,该寄存器的设置请参考datasheet。

4.3. 2 s3c2410fb_blank

该方法完成消隐功能。

  1. /*
  2. * s3c2410fb_blank
  3. * @blank_mode: the blank mode we want.
  4. * @info: frame buffer structure that represents a single frame buffer
  5. *
  6. * Blank the screen if blank_mode != 0, else unblank. Return 0 if
  7. * blanking succeeded, != 0 if un-/blanking failed due to e.g. a
  8. * video mode which doesn't support it. Implements VESA suspend
  9. * and powerdown modes on hardware that supports disabling hsync/vsync:
  10. *
  11. * Returns negative errno on error, or zero on success.
  12. *
  13. */
  14. static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
  15. {
  16.     struct s3c2410fb_info *fbi = info->par;
  17.     void __iomem *tpal_reg = fbi->io;
  18.     dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
  19.     tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;
  20.     if (blank_mode == FB_BLANK_POWERDOWN) {    /*消隐*/
  21.         s3c2410fb_lcd_enable(fbi, 0);        /*禁止LCD*/
  22.     } else {
  23.         s3c2410fb_lcd_enable(fbi, 1);        /*启动LCD*/
  24.     }
  25.     if (blank_mode == FB_BLANK_UNBLANK)        /*去消隐*/
  26.         writel(0x0, tpal_reg);                /*禁止temporary palette*/
  27.     else { /*消隐*/
  28.         dprintk("setting TPAL to output 0x000000\n");
  29.         writel(S3C2410_TPAL_EN, tpal_reg);    /*使能temporary palette,颜色为黑色*/
  30.     }
  31.     return 0;
  32. }
在消隐时,屏幕将全黑。

4.3.3 s3c2410fb_setcolreg

该函数的功能用于设置LCD的调色板。调色板的概念请看我的转帖:LCD调色板

  1. static int s3c2410fb_setcolreg(unsigned regno,
  2.                    unsigned red, unsigned green, unsigned blue,
  3.                    unsigned transp, struct fb_info *info)
  4. {
  5.     struct s3c2410fb_info *fbi = info->par;    /*par指向s3c2410fb_info*/
  6.     void __iomem *regs = fbi->io;
  7.     unsigned int val;
  8.     /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
  9.            regno, red, green, blue); */
  10.     switch (info->fix.visual) {    
  11.     case FB_VISUAL_TRUECOLOR:    /*使用真彩色*/
  12.         /* true-colour, use pseudo-palette */
  13.         if (regno < 16) {    /*16种颜色,为什么只用16种颜色???????????*/
  14.             u32 *pal = info->pseudo_palette;
  15.             val  = chan_to_field(red,   &info->var.red);
  16.             val |= chan_to_field(green, &info->var.green);
  17.             val |= chan_to_field(blue,  &info->var.blue);
  18.             pal[regno] = val;    /*保存颜色值*/
  19.         }
  20.         break;
  21.     case FB_VISUAL_PSEUDOCOLOR:
  22.         if (regno < 256) {
  23.             /* currently assume RGB 5-6-5 mode */
  24.             val  = (red   >>  0) & 0xf800;
  25.             val |= (green >>  5) & 0x07e0;
  26.             val |= (blue  >> 11) & 0x001f;
  27.             writel(val, regs + S3C2410_TFTPAL(regno));
  28.             schedule_palette_update(fbi, regno, val);
  29.         }
  30.         break;
  31.     default:
  32.         return 1;    /* unknown type */
  33.     }
  34.     return 0;
  35. }
  36. /* from pxafb.c */
  37. static inline unsigned int chan_to_field(unsigned int chan,
  38.                      struct fb_bitfield *bf)
  39. {    
  40.     /*下面用到的length和offset在s3c2410fb_check_var函数中设置*/
  41.     chan &= 0xffff;                /*取低16位*/
  42.     chan >>= 16 - bf->length;   /*取第length为到16位为有效位*/
  43.     return chan << bf->offset;    /*移动到相应的位置。*/
  44. }

我们使用的是真彩色,可以设置16种颜色,颜色的位域值通过调用chan_to_field获得,然后保存了颜色的值。但是比较奇怪的是,这里并没有将值保存到0x4d000400为起始的内存中,不知为何。 反而倒是在伪彩色模式下, 使用了writel(val, regs + S3C2410_TFTPAL(regno))写到内存中,然后调用schedule_palette_update函数来更新palette表。我们来看下。

  1. static void schedule_palette_update(struct s3c2410fb_info *fbi,
  2. unsigned int regno, unsigned int val)
  3. {
  4. unsigned long flags;
  5. unsigned long irqen;
  6. void __iomem *irq_base = fbi->irq_base;
  7. local_irq_save(flags);
  8. fbi->palette_buffer[regno] = val;
  9. if (!fbi->palette_ready) {
  10. fbi->palette_ready = 1;
  11. /* enable IRQ */
  12. irqen = readl(irq_base + S3C24XX_LCDINTMSK);
  13. irqen &= ~S3C2410_LCDINT_FRSYNC;
  14. writel(irqen, irq_base + S3C24XX_LCDINTMSK);
  15. }
  16. local_irq_restore(flags);
  17. }
这个函数的作用就是开启了LCD的帧同步中断,这个中断在VSYNC信号从无效变成有效时产生。当中断产生时,会调用在probe方法中注册的ISR。ISR如下:

  1. static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
  2. {
  3. struct s3c2410fb_info *fbi = dev_id;
  4. void __iomem *irq_base = fbi->irq_base;
  5. unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
  6. b
  7. if (lcdirq & S3C2410_LCDINT_FRSYNC) {
  8. if (fbi->palette_ready)
  9. s3c2410fb_write_palette(fbi);
  10. writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);
  11. writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);
  12. }
  13. return IRQ_HANDLED;
  14. }
  15. static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
  16. {
  17.     unsigned int i;
  18.     void __iomem *regs = fbi->io;
  19.     fbi->palette_ready = 0; /*清除ready标志*/
  20.     for (i = 0; i < 256; i++) {
  21.         unsigned long ent = fbi->palette_buffer[i];
  22.         if (ent == PALETTE_BUFF_CLEAR)
  23.             continue;
  24.         writel(ent, regs + S3C2410_TFTPAL(i));
  25.         /* it seems the only way to know exactly
  26.          * if the palette wrote ok, is to check
  27.          * to see if the value verifies ok
  28.          */
  29.         if (readw(regs + S3C2410_TFTPAL(i)) == ent)
  30.             fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR;
  31.         else
  32.             fbi->palette_ready = 1;   /* retry */
  33.     }
  34. }
在中断函数中调用了s3c2410fb_write_palette,该函数对写入内存的颜色值进行检查。如果确认其已经写入,则将palette_buffer中的清除,否则retry。

5. 总结

本文对frambuffer子系统做了简单的介绍。frambuffer子系统的函数相当之多,在这里是不可能一一介绍的。本文首先介绍了主要的数据结构,

随后分析了frambuffer核心层,在核心层简单的分析了5个API函数,接着,对驱动层做了介绍,驱动层的大多数函数都给出了分析。




 
 
 
 
 
 
 
 
 

                
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/76007
推荐阅读
相关标签
  

闽ICP备14008679号