本文转载自:http://blog.csdn.net/av_geek/article/details/40897115

本文将介绍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

[cpp] view plaincopy

  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. };
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. };
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. };
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. };
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. };
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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);
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. };
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. };
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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

[cpp] view plaincopy

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

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

数据流如下:

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

3.4.4 read方法

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

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

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

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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);
[cpp] view plaincopy
  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方法

[cpp] view plaincopy

  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. dprintk("devinit\n");
  113. strcpy(fbinfo->fix.id, driver_name);    /*复制名字*/
  114. /* Stop the video */
  115. lcdcon1 = readl(info->io + S3C2410_LCDCON1);
  116. writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); /*禁止LCD*/
  117. fbinfo->fix.type        = FB_TYPE_PACKED_PIXELS;
  118. fbinfo->fix.type_aux        = 0;
  119. fbinfo->fix.xpanstep        = 0;
  120. fbinfo->fix.ypanstep        = 0;
  121. fbinfo->fix.ywrapstep        = 0;
  122. fbinfo->fix.accel        = FB_ACCEL_NONE;    /* no hardware accelerator    */
  123. fbinfo->var.nonstd        = 0;
  124. fbinfo->var.activate        = FB_ACTIVATE_NOW;
  125. fbinfo->var.accel_flags     = 0;
  126. fbinfo->var.vmode        = FB_VMODE_NONINTERLACED;
  127. fbinfo->fbops            = &s3c2410fb_ops;
  128. fbinfo->flags            = FBINFO_FLAG_DEFAULT;
  129. fbinfo->pseudo_palette      = &info->pseudo_pal;
  130. for (i = 0; i < 256; i++)
  131. info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
  132. ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);    /*申请IRQ,快速中断*/
  133. if (ret) {
  134. dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
  135. ret = -EBUSY;
  136. goto release_regs;
  137. }
  138. info->clk = clk_get(NULL, "lcd");        /*获取时钟信息*/
  139. if (!info->clk || IS_ERR(info->clk)) {
  140. printk(KERN_ERR "failed to get lcd clock source\n");
  141. ret = -ENOENT;
  142. goto release_irq;
  143. }
  144. clk_enable(info->clk);                    /*使能时钟*/
  145. dprintk("got and enabled clock\n");
  146. msleep(1);
  147. /* find maximum required memory size for display */
  148. /*在多个屏幕中,找出需要的最大memory*/
  149. for (i = 0; i < mach_info->num_displays; i++) {
  150. unsigned long smem_len = mach_info->displays[i].xres;
  151. /*所需的memory空间 = xres * yres * bpp / 8*/
  152. smem_len *= mach_info->displays[i].yres;
  153. smem_len *= mach_info->displays[i].bpp;
  154. smem_len >>= 3;
  155. if (fbinfo->fix.smem_len < smem_len)
  156. fbinfo->fix.smem_len = smem_len;
  157. }
  158. /* Initialize video memory */    /*根据上面fix.smem_len的大小,获取DMA映射内存,一致性映射方式*/
  159. ret = s3c2410fb_map_video_memory(fbinfo);
  160. if (ret) {
  161. printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
  162. ret = -ENOMEM;
  163. goto release_clock;
  164. }
  165. dprintk("got video memory\n");
  166. fbinfo->var.xres = display->xres;            /*320*/
  167. fbinfo->var.yres = display->yres;            /*240*/
  168. fbinfo->var.bits_per_pixel = display->bpp;    /*16*/
  169. s3c2410fb_init_registers(fbinfo);            /*LCD寄存器初始化*/
  170. s3c2410fb_check_var(&fbinfo->var, fbinfo);
  171. ret = register_framebuffer(fbinfo);            /*注册framebuffer*/
  172. if (ret < 0) {
  173. printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);
  174. goto free_video_memory;
  175. }
  176. /* create device files */
  177. ret = device_create_file(&pdev->dev, &dev_attr_debug); /*添加设备属性*/
  178. if (ret) {
  179. printk(KERN_ERR "failed to add debug attribute\n");
  180. }
  181. printk(KERN_INFO "fb%d: %s frame buffer device\n",
  182. fbinfo->node, fbinfo->fix.id);
  183. return 0;
  184. /*一旦某个步骤发生错误,以注册的相反顺序开始注销*/
  185. free_video_memory:
  186. s3c2410fb_unmap_video_memory(fbinfo);
  187. release_clock:
  188. clk_disable(info->clk);
  189. clk_put(info->clk);
  190. release_irq:
  191. free_irq(irq, info);
  192. release_regs:
  193. iounmap(info->io);
  194. release_mem:
  195. release_resource(info->mem);
  196. kfree(info->mem);
  197. dealloc_fb:
  198. platform_set_drvdata(pdev, NULL);
  199. framebuffer_release(fbinfo);
  200. return ret;
  201. }
[cpp] view plaincopy
  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. dprintk("devinit\n");
  113. strcpy(fbinfo->fix.id, driver_name);    /*复制名字*/
  114. /* Stop the video */
  115. lcdcon1 = readl(info->io + S3C2410_LCDCON1);
  116. writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); /*禁止LCD*/
  117. fbinfo->fix.type        = FB_TYPE_PACKED_PIXELS;
  118. fbinfo->fix.type_aux        = 0;
  119. fbinfo->fix.xpanstep        = 0;
  120. fbinfo->fix.ypanstep        = 0;
  121. fbinfo->fix.ywrapstep        = 0;
  122. fbinfo->fix.accel        = FB_ACCEL_NONE;    /* no hardware accelerator    */
  123. fbinfo->var.nonstd        = 0;
  124. fbinfo->var.activate        = FB_ACTIVATE_NOW;
  125. fbinfo->var.accel_flags     = 0;
  126. fbinfo->var.vmode        = FB_VMODE_NONINTERLACED;
  127. fbinfo->fbops            = &s3c2410fb_ops;
  128. fbinfo->flags            = FBINFO_FLAG_DEFAULT;
  129. fbinfo->pseudo_palette      = &info->pseudo_pal;
  130. for (i = 0; i < 256; i++)
  131. info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
  132. ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);    /*申请IRQ,快速中断*/
  133. if (ret) {
  134. dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
  135. ret = -EBUSY;
  136. goto release_regs;
  137. }
  138. info->clk = clk_get(NULL, "lcd");        /*获取时钟信息*/
  139. if (!info->clk || IS_ERR(info->clk)) {
  140. printk(KERN_ERR "failed to get lcd clock source\n");
  141. ret = -ENOENT;
  142. goto release_irq;
  143. }
  144. clk_enable(info->clk);                    /*使能时钟*/
  145. dprintk("got and enabled clock\n");
  146. msleep(1);
  147. /* find maximum required memory size for display */
  148. /*在多个屏幕中,找出需要的最大memory*/
  149. for (i = 0; i < mach_info->num_displays; i++) {
  150. unsigned long smem_len = mach_info->displays[i].xres;
  151. /*所需的memory空间 = xres * yres * bpp / 8*/
  152. smem_len *= mach_info->displays[i].yres;
  153. smem_len *= mach_info->displays[i].bpp;
  154. smem_len >>= 3;
  155. if (fbinfo->fix.smem_len < smem_len)
  156. fbinfo->fix.smem_len = smem_len;
  157. }
  158. /* Initialize video memory */    /*根据上面fix.smem_len的大小,获取DMA映射内存,一致性映射方式*/
  159. ret = s3c2410fb_map_video_memory(fbinfo);
  160. if (ret) {
  161. printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
  162. ret = -ENOMEM;
  163. goto release_clock;
  164. }
  165. dprintk("got video memory\n");
  166. fbinfo->var.xres = display->xres;            /*320*/
  167. fbinfo->var.yres = display->yres;            /*240*/
  168. fbinfo->var.bits_per_pixel = display->bpp;    /*16*/
  169. s3c2410fb_init_registers(fbinfo);            /*LCD寄存器初始化*/
  170. s3c2410fb_check_var(&fbinfo->var, fbinfo);
  171. ret = register_framebuffer(fbinfo);            /*注册framebuffer*/
  172. if (ret < 0) {
  173. printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);
  174. goto free_video_memory;
  175. }
  176. /* create device files */
  177. ret = device_create_file(&pdev->dev, &dev_attr_debug); /*添加设备属性*/
  178. if (ret) {
  179. printk(KERN_ERR "failed to add debug attribute\n");
  180. }
  181. printk(KERN_INFO "fb%d: %s frame buffer device\n",
  182. fbinfo->node, fbinfo->fix.id);
  183. return 0;
  184. /*一旦某个步骤发生错误,以注册的相反顺序开始注销*/
  185. free_video_memory:
  186. s3c2410fb_unmap_video_memory(fbinfo);
  187. release_clock:
  188. clk_disable(info->clk);
  189. clk_put(info->clk);
  190. release_irq:
  191. free_irq(irq, info);
  192. release_regs:
  193. iounmap(info->io);
  194. release_mem:
  195. release_resource(info->mem);
  196. kfree(info->mem);
  197. dealloc_fb:
  198. platform_set_drvdata(pdev, NULL);
  199. framebuffer_release(fbinfo);
  200. return ret;
  201. }

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

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

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

[cpp] view plaincopy

  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. };
[cpp] view plaincopy
  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。

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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:

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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:

[cpp] view plaincopy

  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. };
[cpp] view plaincopy
  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,如下:

[cpp] view plaincopy

  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. };
[cpp] view plaincopy
  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

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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函数如下:

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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. }

其中调用的三个函数如下:

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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

该方法完成消隐功能。

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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调色板。

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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表。我们来看下。

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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如下:

[cpp] view plaincopy

  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. }
[cpp] view plaincopy
  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函数,接着,对驱动层做了介绍,驱动层的大多数函数都给出了分析。

Framebuffer子系统【转】相关推荐

  1. 从零开始之驱动发开、linux驱动(二十五、framebuffer 子系统框架)

    一.概念 Framebuffer,也叫帧缓冲,其内容对应于屏幕上的界面显示,可以将其简单理解为屏幕上显示内容对应的缓存,修改Framebuffer中的内容,即表示修改屏幕上的内容,所以,直接操作Fra ...

  2. linux驱动系列学习之Framebuffer子系统(三)

    一.Framebuffer子系统简介         Framebuffer(帧缓冲)时Linux系统位显示设备提供的一个接口.属于偏底层的显示接口.它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上 ...

  3. Linux图形显示DRM子系统环境实践

    前言 学习Linux图形DRM子系统,还是需要有一个运行DRM框架的linux系统,这样无论在学习DRM应用程序还是驱动程序时,可以实际动手修改调试,运行看到效果,学习起来也是比较有动力和成就感的.下 ...

  4. Linux的notifier机制在TP中的应用【转】

    转自:https://blog.csdn.net/armfpga123/article/details/51771666 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog ...

  5. Linux 内核 notifier机制

    一般而言特定的子系统会用特定的notifier_chain_register包装函数来注册,比如tp通过fb_register_client来注册notifier_block(在具体的应用场景中会封装 ...

  6. Android双屏异显另辟蹊径---minui的移植

    minui介绍 minui是Android自带的一个画图工具,可以绘制一些简单的图形和图像,显示png图片,显示字符串等. 在Android设备中譬如,关机充电动画,自动测试程序,recovery模式 ...

  7. Linux驱动开发之RGB565转RGB888

    一.前言 在前面介绍FrameBuffer子系统曾说过一帧的概念:"我们将铺满一个lcd屏幕的数据称为一帧". 那么,在每一帧数据中又由许多个像素点构成. 这些像素点就是本文的正题 ...

  8. Linux内核 LCD 驱动程序框架

    Linux 内核 LCD 驱动程序框架 1. framebuffer 简介 1.1 什么是 framebuffer 1.2 framebuffer的作用 2. framebuffer 驱动的框架 3. ...

  9. Linux下LCD驱动的详解

    看了不少人写的LCD驱动解释,看之前很懵逼,看之后还是很懵逼.都是放一大堆内核代码,我当时就想吐槽,能写就写,写不明白放一大堆代码是啥意思.后来,实在没办法,只能去看内核代码,结果,真香,原来别人放一 ...

最新文章

  1. Ubuntu 使用国内apt源
  2. 软件需求工程与UML建模——第九组第二周工作总结
  3. 【原创】Android VMP加壳 POC
  4. 类与接口(四)方法重载解析
  5. java Calendar 小时值得到24进制格式
  6. xUtils3 图片加载模块
  7. LSTM神经网络在证券市场分析上的应用
  8. php读取excel文件_如何用PHP读取excel文件内容、获取单元格数据
  9. ROM 和 RAM 区别
  10. 西北工业大学21计算机考研,研友分享:西北工业大学计算机考研血泪史
  11. 梅捷512G固态拆机+得一微YS9082HP量产教程+YS9082HP开卡工具
  12. 2020年新疆旅游市场遭遇滑铁卢,旅游消费跌至992.12亿元[图]
  13. 木鱼网址缩短服务 短域名生成网站源码
  14. 浅谈网站导航设计经验
  15. 保险公司电子印章集中管理解决方案
  16. Goland 导入自定义包问题解决
  17. 易车上面可以买车吗?
  18. 【Cxinny】微信小程序笔记
  19. ubuntu开启键盘背光灯
  20. input表单所有属性【web前端】

热门文章

  1. WinRAR压缩解压文件
  2. android主线程和子线程的区别
  3. 【arc075F】Mirrored
  4. Java验证中文汉字、英文字母、标点符号一个字符占多少字节
  5. 小技巧-如何在任何一个文件夹中打开cmd
  6. [Win32]路径操作API函数
  7. 在国内愚人节可以开的10个玩笑
  8. 辞职信微信html,微信退款处理.html
  9. iOS如何让APP删除后不接受APNS推送消息
  10. 时间显示(模拟时钟)