标签:创建 比较 地址 locking ftp cup address 像素 off
本文转载自: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
- struct fb_info {
- int node;
- int flags;
- struct mutex lock;
- struct fb_var_screeninfo var;
- struct fb_fix_screeninfo fix;
- struct fb_monspecs monspecs;
- struct work_struct queue;
- struct fb_pixmap pixmap;
- struct fb_pixmap sprite;
- struct fb_cmap cmap;
- struct list_head modelist;
- struct fb_videomode *mode;
-
- #ifdef CONFIG_FB_BACKLIGHT
-
-
- struct backlight_device *bl_dev;
-
-
- struct mutex bl_curve_mutex;
- u8 bl_curve[FB_BACKLIGHT_LEVELS];
- #endif
- #ifdef CONFIG_FB_DEFERRED_IO
- struct delayed_work deferred_work;
- struct fb_deferred_io *fbdefio;
- #endif
-
- struct fb_ops *fbops;
- struct device *device;
- struct device *dev;
- int class_flag;
- #ifdef CONFIG_FB_TILEBLITTING
- struct fb_tile_ops *tileops;
- #endif
- char __iomem *screen_base;
- unsigned long screen_size;
- void *pseudo_palette;
- #define FBINFO_STATE_RUNNING 0
- #define FBINFO_STATE_SUSPENDED 1
- u32 state;
- void *fbcon_par;
-
- void *par;
- };
- struct fb_info {
- int node;
- int flags;
- struct mutex lock;
- struct fb_var_screeninfo var;
- struct fb_fix_screeninfo fix;
- struct fb_monspecs monspecs;
- struct work_struct queue;
- struct fb_pixmap pixmap;
- struct fb_pixmap sprite;
- struct fb_cmap cmap;
- struct list_head modelist;
- struct fb_videomode *mode;
-
- #ifdef CONFIG_FB_BACKLIGHT
-
-
- struct backlight_device *bl_dev;
-
-
- struct mutex bl_curve_mutex;
- u8 bl_curve[FB_BACKLIGHT_LEVELS];
- #endif
- #ifdef CONFIG_FB_DEFERRED_IO
- struct delayed_work deferred_work;
- struct fb_deferred_io *fbdefio;
- #endif
-
- struct fb_ops *fbops;
- struct device *device;
- struct device *dev;
- int class_flag;
- #ifdef CONFIG_FB_TILEBLITTING
- struct fb_tile_ops *tileops;
- #endif
- char __iomem *screen_base;
- unsigned long screen_size;
- void *pseudo_palette;
- #define FBINFO_STATE_RUNNING 0
- #define FBINFO_STATE_SUSPENDED 1
- u32 state;
- void *fbcon_par;
-
- void *par;
- };
2.2 fb_fix_screeninfo结构
该数据结构是不可改变的,也就是说用户空间不能改变该结构中的任何成员,在核心层我们将会看到这点。
下列代码位于include/linux/fb.h
- struct fb_fix_screeninfo {
- char id[16];
- unsigned long smem_start;
-
- __u32 smem_len;
- __u32 type;
- __u32 type_aux;
- __u32 visual;
- __u16 xpanstep;
- __u16 ypanstep;
- __u16 ywrapstep;
- __u32 line_length;
- unsigned long mmio_start;
-
- __u32 mmio_len;
- __u32 accel;
-
- __u16 reserved[3];
- };
- struct fb_fix_screeninfo {
- char id[16];
- unsigned long smem_start;
-
- __u32 smem_len;
- __u32 type;
- __u32 type_aux;
- __u32 visual;
- __u16 xpanstep;
- __u16 ypanstep;
- __u16 ywrapstep;
- __u32 line_length;
- unsigned long mmio_start;
-
- __u32 mmio_len;
- __u32 accel;
-
- __u16 reserved[3];
- };
2.3 fb_var_screeninfo结构
该数据结构是可以改变的,也就是说用户空间可以改变该结构中的成员。该数据结构中的很多成员就由板级信息复制而来,在驱动代码中我们将会看到这点。
下列代码位于include/linux/fb.h
- struct fb_var_screeninfo {
- __u32 xres;
- __u32 yres;
- __u32 xres_virtual;
- __u32 yres_virtual;
- __u32 xoffset;
- __u32 yoffset;
-
- __u32 bits_per_pixel;
- __u32 grayscale;
-
- struct fb_bitfield red;
- struct fb_bitfield green;
- struct fb_bitfield blue;
- struct fb_bitfield transp;
-
- __u32 nonstd;
-
- __u32 activate;
-
- __u32 height;
- __u32 width;
-
- __u32 accel_flags;
-
-
- __u32 pixclock;
- __u32 left_margin;
- __u32 right_margin;
- __u32 upper_margin;
- __u32 lower_margin;
- __u32 hsync_len;
- __u32 vsync_len;
- __u32 sync;
- __u32 vmode;
- __u32 rotate;
- __u32 reserved[5];
- };
- struct fb_var_screeninfo {
- __u32 xres;
- __u32 yres;
- __u32 xres_virtual;
- __u32 yres_virtual;
- __u32 xoffset;
- __u32 yoffset;
-
- __u32 bits_per_pixel;
- __u32 grayscale;
-
- struct fb_bitfield red;
- struct fb_bitfield green;
- struct fb_bitfield blue;
- struct fb_bitfield transp;
-
- __u32 nonstd;
-
- __u32 activate;
-
- __u32 height;
- __u32 width;
-
- __u32 accel_flags;
-
-
- __u32 pixclock;
- __u32 left_margin;
- __u32 right_margin;
- __u32 upper_margin;
- __u32 lower_margin;
- __u32 hsync_len;
- __u32 vsync_len;
- __u32 sync;
- __u32 vmode;
- __u32 rotate;
- __u32 reserved[5];
- };
2.4 fb_ops结构
该结构描述了用于fb_info的方法,这些方法中有些是要驱动程序提供的,而有些可以使用内核提供的方法。
下列代码位于include/linux/fb.h
-
- struct fb_ops {
-
- struct module *owner;
- int (*fb_open)(struct fb_info *info, int user);
- int (*fb_release)(struct fb_info *info, int user);
-
-
- ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
- size_t count, loff_t *ppos);
- ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
- size_t count, loff_t *ppos);
-
-
- int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
-
-
- int (*fb_set_par)(struct fb_info *info);
-
-
- int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
- unsigned blue, unsigned transp, struct fb_info *info);
-
-
- int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
-
-
- int (*fb_blank)(int blank, struct fb_info *info);
-
-
- int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
-
-
- void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
-
- void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
-
- void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
-
-
- int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
-
-
- void (*fb_rotate)(struct fb_info *info, int angle);
-
-
- int (*fb_sync)(struct fb_info *info);
-
-
- int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
- unsigned long arg);
-
-
- int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
- unsigned long arg);
-
-
- int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
-
-
- void (*fb_save_state)(struct fb_info *info);
-
-
- void (*fb_restore_state)(struct fb_info *info);
-
-
- void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
- struct fb_var_screeninfo *var);
- };
-
- struct fb_ops {
-
- struct module *owner;
- int (*fb_open)(struct fb_info *info, int user);
- int (*fb_release)(struct fb_info *info, int user);
-
-
- ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
- size_t count, loff_t *ppos);
- ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
- size_t count, loff_t *ppos);
-
-
- int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
-
-
- int (*fb_set_par)(struct fb_info *info);
-
-
- int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
- unsigned blue, unsigned transp, struct fb_info *info);
-
-
- int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
-
-
- int (*fb_blank)(int blank, struct fb_info *info);
-
-
- int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
-
-
- void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
-
- void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
-
- void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
-
-
- int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
-
-
- void (*fb_rotate)(struct fb_info *info, int angle);
-
-
- int (*fb_sync)(struct fb_info *info);
-
-
- int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
- unsigned long arg);
-
-
- int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
- unsigned long arg);
-
-
- int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
-
-
- void (*fb_save_state)(struct fb_info *info);
-
-
- void (*fb_restore_state)(struct fb_info *info);
-
-
- void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
- struct fb_var_screeninfo *var);
- };
3. frambuffer核心层
首先来看下frmaebuffer子系统的初始化函数。
3.1 fbmem_init和fbmem_exit
下列代码位于drivers/video/fbmem.c
-
- static int __init
- fbmem_init(void)
- {
- proc_create("fb", 0, NULL, &fb_proc_fops);
-
- if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
- printk("unable to get major %d for fb devs\n", FB_MAJOR);
-
- fb_class = class_create(THIS_MODULE, "graphics");
- if (IS_ERR(fb_class)) {
- printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
- fb_class = NULL;
- }
- return 0;
- }
-
- #ifdef MODULE
- module_init(fbmem_init);
- static void __exit
- fbmem_exit(void)
- {
- remove_proc_entry("fb", NULL);
- class_destroy(fb_class);
- unregister_chrdev(FB_MAJOR, "fb");
- }
-
- module_exit(fbmem_exit);
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("Framebuffer base");
- #else
- subsys_initcall(fbmem_init);
- #endif
-
- static const struct file_operations fb_fops = {
- .owner = THIS_MODULE,
- .read = fb_read,
- .write = fb_write,
- .unlocked_ioctl = fb_ioctl,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = fb_compat_ioctl,
- #endif
- .mmap = fb_mmap,
- .open = fb_open,
- .release = fb_release,
- #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
- .get_unmapped_area = get_fb_unmapped_area,
- #endif
- #ifdef CONFIG_FB_DEFERRED_IO
- .fsync = fb_deferred_io_fsync,
- #endif
- };
-
- static int __init
- fbmem_init(void)
- {
- proc_create("fb", 0, NULL, &fb_proc_fops);
-
- if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
- printk("unable to get major %d for fb devs\n", FB_MAJOR);
-
- fb_class = class_create(THIS_MODULE, "graphics");
- if (IS_ERR(fb_class)) {
- printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
- fb_class = NULL;
- }
- return 0;
- }
-
- #ifdef MODULE
- module_init(fbmem_init);
- static void __exit
- fbmem_exit(void)
- {
- remove_proc_entry("fb", NULL);
- class_destroy(fb_class);
- unregister_chrdev(FB_MAJOR, "fb");
- }
-
- module_exit(fbmem_exit);
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("Framebuffer base");
- #else
- subsys_initcall(fbmem_init);
- #endif
-
- static const struct file_operations fb_fops = {
- .owner = THIS_MODULE,
- .read = fb_read,
- .write = fb_write,
- .unlocked_ioctl = fb_ioctl,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = fb_compat_ioctl,
- #endif
- .mmap = fb_mmap,
- .open = fb_open,
- .release = fb_release,
- #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
- .get_unmapped_area = get_fb_unmapped_area,
- #endif
- #ifdef CONFIG_FB_DEFERRED_IO
- .fsync = fb_deferred_io_fsync,
- #endif
- };
我们看到,如果不是作为模块,那么该初始化程序将在subsys_initcall阶段被调用。初始化时,仅仅注册了一个字符设备,并创建了一个类。通过字符设备,提供了API给用户空间,包open,release,write,read等。
随后我们看看如何分配一个fb_info结构。
3.2 framebuffer_alloc
下列代码位于drivers/video/fbmem.c
- struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
- {
- #define BYTES_PER_LONG (BITS_PER_LONG/8)
- #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
- int fb_info_size = sizeof(struct fb_info);
- struct fb_info *info;
- char *p;
-
- if (size)
- fb_info_size += PADDING;
-
- p = kzalloc(fb_info_size + size, GFP_KERNEL);
-
- if (!p)
- return NULL;
-
- info = (struct fb_info *) p;
-
- if (size)
- info->par = p + fb_info_size;
-
- info->device = dev;
-
- #ifdef CONFIG_FB_BACKLIGHT
- mutex_init(&info->bl_curve_mutex);
- #endif
-
- return info;
- #undef PADDING
- #undef BYTES_PER_LONG
- }
- EXPORT_SYMBOL(framebuffer_alloc);
- struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
- {
- #define BYTES_PER_LONG (BITS_PER_LONG/8)
- #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
- int fb_info_size = sizeof(struct fb_info);
- struct fb_info *info;
- char *p;
-
- if (size)
- fb_info_size += PADDING;
-
- p = kzalloc(fb_info_size + size, GFP_KERNEL);
-
- if (!p)
- return NULL;
-
- info = (struct fb_info *) p;
-
- if (size)
- info->par = p + fb_info_size;
-
- info->device = dev;
-
- #ifdef CONFIG_FB_BACKLIGHT
- mutex_init(&info->bl_curve_mutex);
- #endif
-
- return info;
- #undef PADDING
- #undef BYTES_PER_LONG
- }
- 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
-
- int
- register_framebuffer(struct fb_info *fb_info)
- {
- int i;
- struct fb_event event;
- struct fb_videomode mode;
-
- if (num_registered_fb == FB_MAX)
- return -ENXIO;
-
- if (fb_check_foreignness(fb_info))
- return -ENOSYS;
-
- num_registered_fb++;
-
- for (i = 0 ; i < FB_MAX; i++)
- if (!registered_fb[i])
- break;
- fb_info->node = i;
- mutex_init(&fb_info->lock);
-
- fb_info->dev = device_create(fb_class, fb_info->device,
- MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
- if (IS_ERR(fb_info->dev)) {
-
- printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
- fb_info->dev = NULL;
- } else
- fb_init_device(fb_info);
-
- if (fb_info->pixmap.addr == NULL) {
- fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
- if (fb_info->pixmap.addr) {
- fb_info->pixmap.size = FBPIXMAPSIZE;
- fb_info->pixmap.buf_align = 1;
- fb_info->pixmap.scan_align = 1;
- fb_info->pixmap.access_align = 32;
- fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
- }
- }
- fb_info->pixmap.offset = 0;
-
- if (!fb_info->pixmap.blit_x)
- fb_info->pixmap.blit_x = ~(u32)0;
-
- if (!fb_info->pixmap.blit_y)
- fb_info->pixmap.blit_y = ~(u32)0;
-
- if (!fb_info->modelist.prev || !fb_info->modelist.next)
- INIT_LIST_HEAD(&fb_info->modelist);
-
- fb_var_to_videomode(&mode, &fb_info->var);
- fb_add_videomode(&mode, &fb_info->modelist);
- registered_fb[i] = fb_info;
-
- event.info = fb_info;
- if (!lock_fb_info(fb_info))
- return -ENODEV;
- fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
- unlock_fb_info(fb_info);
- return 0;
- }
-
- int
- register_framebuffer(struct fb_info *fb_info)
- {
- int i;
- struct fb_event event;
- struct fb_videomode mode;
-
- if (num_registered_fb == FB_MAX)
- return -ENXIO;
-
- if (fb_check_foreignness(fb_info))
- return -ENOSYS;
-
- num_registered_fb++;
-
- for (i = 0 ; i < FB_MAX; i++)
- if (!registered_fb[i])
- break;
- fb_info->node = i;
- mutex_init(&fb_info->lock);
-
- fb_info->dev = device_create(fb_class, fb_info->device,
- MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
- if (IS_ERR(fb_info->dev)) {
-
- printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
- fb_info->dev = NULL;
- } else
- fb_init_device(fb_info);
-
- if (fb_info->pixmap.addr == NULL) {
- fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
- if (fb_info->pixmap.addr) {
- fb_info->pixmap.size = FBPIXMAPSIZE;
- fb_info->pixmap.buf_align = 1;
- fb_info->pixmap.scan_align = 1;
- fb_info->pixmap.access_align = 32;
- fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
- }
- }
- fb_info->pixmap.offset = 0;
-
- if (!fb_info->pixmap.blit_x)
- fb_info->pixmap.blit_x = ~(u32)0;
-
- if (!fb_info->pixmap.blit_y)
- fb_info->pixmap.blit_y = ~(u32)0;
-
- if (!fb_info->modelist.prev || !fb_info->modelist.next)
- INIT_LIST_HEAD(&fb_info->modelist);
-
- fb_var_to_videomode(&mode, &fb_info->var);
- fb_add_videomode(&mode, &fb_info->modelist);
- registered_fb[i] = fb_info;
-
- event.info = fb_info;
- if (!lock_fb_info(fb_info))
- return -ENODEV;
- fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
- unlock_fb_info(fb_info);
- return 0;
- }
从这个函数我们可以看出,framebuffer子系统只支持32个设备。在创建了设备节点以后,建立设备属性节点,随后将fb_var_screeninfo转换成fb_videomode,最后添加fb_videomode至链表中。
我们看下其中调用的函数,首先是fb_init_device。
下列代码位于drivers/video/fbsysfs.c
- int fb_init_device(struct fb_info *fb_info)
- {
- int i, error = 0;
-
- dev_set_drvdata(fb_info->dev, fb_info);
-
- fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
-
-
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
- error = device_create_file(fb_info->dev, &device_attrs[i]);
-
- if (error)
- break;
- }
-
- if (error) {
- while (--i >= 0)
- device_remove_file(fb_info->dev, &device_attrs[i]);
- fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
- }
-
- return 0;
- }
-
- static struct device_attribute device_attrs[] = {
- __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
- __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
- __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
- __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
- __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
- __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
- __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
- __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
- __ATTR(name, S_IRUGO, show_name, NULL),
- __ATTR(stride, S_IRUGO, show_stride, NULL),
- __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
- __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
- #ifdef CONFIG_FB_BACKLIGHT
- __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
- #endif
- };
- int fb_init_device(struct fb_info *fb_info)
- {
- int i, error = 0;
-
- dev_set_drvdata(fb_info->dev, fb_info);
-
- fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
-
-
- for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
- error = device_create_file(fb_info->dev, &device_attrs[i]);
-
- if (error)
- break;
- }
-
- if (error) {
- while (--i >= 0)
- device_remove_file(fb_info->dev, &device_attrs[i]);
- fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
- }
-
- return 0;
- }
-
- static struct device_attribute device_attrs[] = {
- __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
- __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
- __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
- __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
- __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
- __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
- __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
- __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
- __ATTR(name, S_IRUGO, show_name, NULL),
- __ATTR(stride, S_IRUGO, show_stride, NULL),
- __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
- __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
- #ifdef CONFIG_FB_BACKLIGHT
- __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
- #endif
- };
我们可以在/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
- struct fb_videomode {
- const char *name;
- u32 refresh;
- u32 xres;
- u32 yres;
- u32 pixclock;
- u32 left_margin;
- u32 right_margin;
- u32 upper_margin;
- u32 lower_margin;
- u32 hsync_len;
- u32 vsync_len;
- u32 sync;
- u32 vmode;
- u32 flag;
- };
-
- void fb_var_to_videomode(struct fb_videomode *mode,
- const struct fb_var_screeninfo *var)
- {
- u32 pixclock, hfreq, htotal, vtotal;
-
- mode->name = NULL;
- mode->xres = var->xres;
- mode->yres = var->yres;
- mode->pixclock = var->pixclock;
- mode->hsync_len = var->hsync_len;
- mode->vsync_len = var->vsync_len;
- mode->left_margin = var->left_margin;
- mode->right_margin = var->right_margin;
- mode->upper_margin = var->upper_margin;
- mode->lower_margin = var->lower_margin;
- mode->sync = var->sync;
- mode->vmode = var->vmode & FB_VMODE_MASK;
- mode->flag = FB_MODE_IS_FROM_VAR;
- mode->refresh = 0;
-
- if (!var->pixclock)
- return;
-
- pixclock = PICOS2KHZ(var->pixclock) * 1000;
-
- htotal = var->xres + var->right_margin + var->hsync_len +
- var->left_margin;
- vtotal = var->yres + var->lower_margin + var->vsync_len +
- var->upper_margin;
-
- if (var->vmode & FB_VMODE_INTERLACED)
- vtotal /= 2;
- if (var->vmode & FB_VMODE_DOUBLE)
- vtotal *= 2;
-
- hfreq = pixclock/htotal;
- mode->refresh = hfreq/vtotal;
- }
-
- int fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)
- {
- struct list_head *pos;
- struct fb_modelist *modelist;d
- struct fb_videomode *m;
- int found = 0;
-
- list_for_each(pos, head) {
- modelist = list_entry(pos, struct fb_modelist, list);
- m = &modelist->mode;
- if (fb_mode_is_equal(m, mode)) {
- found = 1;
- break;
- }
- }
- if (!found) {
- modelist = kmalloc(sizeof(struct fb_modelist),
- GFP_KERNEL);
-
- if (!modelist)
- return -ENOMEM;
- modelist->mode = *mode;
- list_add(&modelist->list, head);
- }
- return 0;
- }
-
- int fb_mode_is_equal(const struct fb_videomode *mode1,
- const struct fb_videomode *mode2)
- {
- return (mode1->xres == mode2->xres &&
- mode1->yres == mode2->yres &&
- mode1->pixclock == mode2->pixclock &&
- mode1->hsync_len == mode2->hsync_len &&
- mode1->vsync_len == mode2->vsync_len &&
- mode1->left_margin == mode2->left_margin &&
- mode1->right_margin == mode2->right_margin &&
- mode1->upper_margin == mode2->upper_margin &&
- mode1->lower_margin == mode2->lower_margin &&
- mode1->sync == mode2->sync &&
- mode1->vmode == mode2->vmode);
- }
- struct fb_videomode {
- const char *name;
- u32 refresh;
- u32 xres;
- u32 yres;
- u32 pixclock;
- u32 left_margin;
- u32 right_margin;
- u32 upper_margin;
- u32 lower_margin;
- u32 hsync_len;
- u32 vsync_len;
- u32 sync;
- u32 vmode;
- u32 flag;
- };
-
- void fb_var_to_videomode(struct fb_videomode *mode,
- const struct fb_var_screeninfo *var)
- {
- u32 pixclock, hfreq, htotal, vtotal;
-
- mode->name = NULL;
- mode->xres = var->xres;
- mode->yres = var->yres;
- mode->pixclock = var->pixclock;
- mode->hsync_len = var->hsync_len;
- mode->vsync_len = var->vsync_len;
- mode->left_margin = var->left_margin;
- mode->right_margin = var->right_margin;
- mode->upper_margin = var->upper_margin;
- mode->lower_margin = var->lower_margin;
- mode->sync = var->sync;
- mode->vmode = var->vmode & FB_VMODE_MASK;
- mode->flag = FB_MODE_IS_FROM_VAR;
- mode->refresh = 0;
-
- if (!var->pixclock)
- return;
-
- pixclock = PICOS2KHZ(var->pixclock) * 1000;
-
- htotal = var->xres + var->right_margin + var->hsync_len +
- var->left_margin;
- vtotal = var->yres + var->lower_margin + var->vsync_len +
- var->upper_margin;
-
- if (var->vmode & FB_VMODE_INTERLACED)
- vtotal /= 2;
- if (var->vmode & FB_VMODE_DOUBLE)
- vtotal *= 2;
-
- hfreq = pixclock/htotal;
- mode->refresh = hfreq/vtotal;
- }
-
- int fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)
- {
- struct list_head *pos;
- struct fb_modelist *modelist;d
- struct fb_videomode *m;
- int found = 0;
-
- list_for_each(pos, head) {
- modelist = list_entry(pos, struct fb_modelist, list);
- m = &modelist->mode;
- if (fb_mode_is_equal(m, mode)) {
- found = 1;
- break;
- }
- }
- if (!found) {
- modelist = kmalloc(sizeof(struct fb_modelist),
- GFP_KERNEL);
-
- if (!modelist)
- return -ENOMEM;
- modelist->mode = *mode;
- list_add(&modelist->list, head);
- }
- return 0;
- }
-
- int fb_mode_is_equal(const struct fb_videomode *mode1,
- const struct fb_videomode *mode2)
- {
- return (mode1->xres == mode2->xres &&
- mode1->yres == mode2->yres &&
- mode1->pixclock == mode2->pixclock &&
- mode1->hsync_len == mode2->hsync_len &&
- mode1->vsync_len == mode2->vsync_len &&
- mode1->left_margin == mode2->left_margin &&
- mode1->right_margin == mode2->right_margin &&
- mode1->upper_margin == mode2->upper_margin &&
- mode1->lower_margin == mode2->lower_margin &&
- mode1->sync == mode2->sync &&
- mode1->vmode == mode2->vmode);
- }
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
- static struct fb_ops s3c2410fb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = s3c2410fb_check_var,
- .fb_set_par = s3c2410fb_set_par,
- .fb_blank = s3c2410fb_blank,
- .fb_setcolreg = s3c2410fb_setcolreg,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- };
- static struct fb_ops s3c2410fb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = s3c2410fb_check_var,
- .fb_set_par = s3c2410fb_set_par,
- .fb_blank = s3c2410fb_blank,
- .fb_setcolreg = s3c2410fb_setcolreg,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- };
最下面的三个方法使用的是内核提供的库函数,而上面4个则是由驱动提供。
3.4.1 open方法
下列代码位于drivers/video/fbmem.c
- static int
- fb_open(struct inode *inode, struct file *file)
- __acquires(&info->lock)
- __releases(&info->lock)
- {
- int fbidx = iminor(inode);
- struct fb_info *info;
- int res = 0;
-
- if (fbidx >= FB_MAX)
- return -ENODEV;
- info = registered_fb[fbidx];
- if (!info)
- request_module("fb%d", fbidx);
- info = registered_fb[fbidx];
- if (!info)
- return -ENODEV;
- mutex_lock(&info->lock);
- if (!try_module_get(info->fbops->owner)) {
- res = -ENODEV;
- goto out;
- }
- file->private_data = info;
- if (info->fbops->fb_open) {
- res = info->fbops->fb_open(info,1);
- if (res)
- module_put(info->fbops->owner);
- }
- #ifdef CONFIG_FB_DEFERRED_IO
- if (info->fbdefio)
- fb_deferred_io_open(info, inode, file);
- #endif
- out:
- mutex_unlock(&info->lock);
- return res;
- }
- static int
- fb_open(struct inode *inode, struct file *file)
- __acquires(&info->lock)
- __releases(&info->lock)
- {
- int fbidx = iminor(inode);
- struct fb_info *info;
- int res = 0;
-
- if (fbidx >= FB_MAX)
- return -ENODEV;
- info = registered_fb[fbidx];
- if (!info)
- request_module("fb%d", fbidx);
- info = registered_fb[fbidx];
- if (!info)
- return -ENODEV;
- mutex_lock(&info->lock);
- if (!try_module_get(info->fbops->owner)) {
- res = -ENODEV;
- goto out;
- }
- file->private_data = info;
- if (info->fbops->fb_open) {
- res = info->fbops->fb_open(info,1);
- if (res)
- module_put(info->fbops->owner);
- }
- #ifdef CONFIG_FB_DEFERRED_IO
- if (info->fbdefio)
- fb_deferred_io_open(info, inode, file);
- #endif
- out:
- mutex_unlock(&info->lock);
- return res;
- }
主要的一个工作就是增加模块引用计数。还有,程序会判断是否fb_open在驱动中给出,如果有则调用该方法。我们已经知道fb_open没有给出。
3.4.2 release方法
下列代码位于drivers/video/fbmem.c
- static int
- fb_release(struct inode *inode, struct file *file)
- __acquires(&info->lock)
- __releases(&info->lock)
- {
- struct fb_info * const info = file->private_data;
-
- mutex_lock(&info->lock);
- if (info->fbops->fb_release)
- info->fbops->fb_release(info,1);
- module_put(info->fbops->owner);
- mutex_unlock(&info->lock);
- return 0;
- }
- static int
- fb_release(struct inode *inode, struct file *file)
- __acquires(&info->lock)
- __releases(&info->lock)
- {
- struct fb_info * const info = file->private_data;
-
- mutex_lock(&info->lock);
- if (info->fbops->fb_release)
- info->fbops->fb_release(info,1);
- module_put(info->fbops->owner);
- mutex_unlock(&info->lock);
- return 0;
- }
和open相反,减少模块引用计数。
3.4.3 write方法
通过调用该方法,LCD将显示画面。
下列代码位于drivers/video/fbmem.c
- static ssize_t
- fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
- {
- unsigned long p = *ppos;
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
- u32 *buffer, *src;
- u32 __iomem *dst;
- int c, i, cnt = 0, err = 0;
- unsigned long total_size;
-
- if (!info || !info->screen_base)
- return -ENODEV;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return -EPERM;
-
- if (info->fbops->fb_write)
- return info->fbops->fb_write(info, buf, count, ppos);
-
- total_size = info->screen_size;
-
- if (total_size == 0)
- total_size = info->fix.smem_len;
-
- if (p > total_size)
- return -EFBIG;
-
- if (count > total_size) {
- err = -EFBIG;
- count = total_size;
- }
-
- if (count + p > total_size) {
- if (!err)
- err = -ENOSPC;
-
- count = total_size - p;
- }
-
-
- buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
- GFP_KERNEL); /
- if (!buffer)
- return -ENOMEM;
-
- dst = (u32 __iomem *) (info->screen_base + p);
-
- if (info->fbops->fb_sync)
- info->fbops->fb_sync(info);
-
- while (count) {
- c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
- src = buffer;
-
-
- if (copy_from_user(src, buf, c)) {
- err = -EFAULT;
- break;
- }
-
- for (i = c >> 2; i--; )
- fb_writel(*src++, dst++);
-
- if (c & 3) {
- u8 *src8 = (u8 *) src;
- u8 __iomem *dst8 = (u8 __iomem *) dst;
-
- for (i = c & 3; i--; )
- fb_writeb(*src8++, dst8++);
-
- dst = (u32 __iomem *) dst8;
- }
-
- *ppos += c;
- buf += c;
- cnt += c;
- count -= c;
- }
-
- kfree(buffer);
-
- return (cnt) ? cnt : err;
- }
- static ssize_t
- fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
- {
- unsigned long p = *ppos;
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
- u32 *buffer, *src;
- u32 __iomem *dst;
- int c, i, cnt = 0, err = 0;
- unsigned long total_size;
-
- if (!info || !info->screen_base)
- return -ENODEV;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return -EPERM;
-
- if (info->fbops->fb_write)
- return info->fbops->fb_write(info, buf, count, ppos);
-
- total_size = info->screen_size;
-
- if (total_size == 0)
- total_size = info->fix.smem_len;
-
- if (p > total_size)
- return -EFBIG;
-
- if (count > total_size) {
- err = -EFBIG;
- count = total_size;
- }
-
- if (count + p > total_size) {
- if (!err)
- err = -ENOSPC;
-
- count = total_size - p;
- }
-
-
- buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
- GFP_KERNEL); /
- if (!buffer)
- return -ENOMEM;
-
- dst = (u32 __iomem *) (info->screen_base + p);
-
- if (info->fbops->fb_sync)
- info->fbops->fb_sync(info);
-
- while (count) {
- c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
- src = buffer;
-
-
- if (copy_from_user(src, buf, c)) {
- err = -EFAULT;
- break;
- }
-
- for (i = c >> 2; i--; )
- fb_writel(*src++, dst++);
-
- if (c & 3) {
- u8 *src8 = (u8 *) src;
- u8 __iomem *dst8 = (u8 __iomem *) dst;
-
- for (i = c & 3; i--; )
- fb_writeb(*src8++, dst8++);
-
- dst = (u32 __iomem *) dst8;
- }
-
- *ppos += c;
- buf += c;
- cnt += c;
- count -= c;
- }
-
- kfree(buffer);
-
- return (cnt) ? cnt : err;
- }
这里,做了一系列的检查之后,开始拷贝数据。这里一个有三个buffer,一个是用户空间提供的buf,一个是在这里新开辟的buffer,还有就是驱动层提供的screen_base。
数据流如下:
用户空间的数据首先被复制到buffer中,然后从buffer中复制到screen_base中,最后被映射到LCD上,LCD就显示响应的画面了。
3.4.4 read方法
该方法用于读取屏幕画面的数据。
read和write类似,只是数据流是反响的,就不多做介绍了。
下列代码位于drivers/video/fbmem.c
- static ssize_t
- fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
- {
- unsigned long p = *ppos;
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
- u32 *buffer, *dst;
- u32 __iomem *src;
- int c, i, cnt = 0, err = 0;
- unsigned long total_size;
-
- if (!info || ! info->screen_base)
- return -ENODEV;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return -EPERM;
-
- if (info->fbops->fb_read)
- return info->fbops->fb_read(info, buf, count, ppos);
-
- total_size = info->screen_size;
-
- if (total_size == 0)
- total_size = info->fix.smem_len;
-
- if (p >= total_size)
- return 0;
-
- if (count >= total_size)
- count = total_size;
-
- if (count + p > total_size)
- count = total_size - p;
-
- buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
- GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
- src = (u32 __iomem *) (info->screen_base + p);
-
- if (info->fbops->fb_sync)
- info->fbops->fb_sync(info);
-
- while (count) {
- c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
- dst = buffer;
- for (i = c >> 2; i--; )
- *dst++ = fb_readl(src++);
- if (c & 3) {
- u8 *dst8 = (u8 *) dst;
- u8 __iomem *src8 = (u8 __iomem *) src;
-
- for (i = c & 3; i--;)
- *dst8++ = fb_readb(src8++);
-
- src = (u32 __iomem *) src8;
- }
-
- if (copy_to_user(buf, buffer, c)) {
- err = -EFAULT;
- break;
- }
- *ppos += c;
- buf += c;
- cnt += c;
- count -= c;
- }
-
- kfree(buffer);
-
- return (err) ? err : cnt;
- }
- static ssize_t
- fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
- {
- unsigned long p = *ppos;
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
- u32 *buffer, *dst;
- u32 __iomem *src;
- int c, i, cnt = 0, err = 0;
- unsigned long total_size;
-
- if (!info || ! info->screen_base)
- return -ENODEV;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return -EPERM;
-
- if (info->fbops->fb_read)
- return info->fbops->fb_read(info, buf, count, ppos);
-
- total_size = info->screen_size;
-
- if (total_size == 0)
- total_size = info->fix.smem_len;
-
- if (p >= total_size)
- return 0;
-
- if (count >= total_size)
- count = total_size;
-
- if (count + p > total_size)
- count = total_size - p;
-
- buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
- GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
- src = (u32 __iomem *) (info->screen_base + p);
-
- if (info->fbops->fb_sync)
- info->fbops->fb_sync(info);
-
- while (count) {
- c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
- dst = buffer;
- for (i = c >> 2; i--; )
- *dst++ = fb_readl(src++);
- if (c & 3) {
- u8 *dst8 = (u8 *) dst;
- u8 __iomem *src8 = (u8 __iomem *) src;
-
- for (i = c & 3; i--;)
- *dst8++ = fb_readb(src8++);
-
- src = (u32 __iomem *) src8;
- }
-
- if (copy_to_user(buf, buffer, c)) {
- err = -EFAULT;
- break;
- }
- *ppos += c;
- buf += c;
- cnt += c;
- count -= c;
- }
-
- kfree(buffer);
-
- return (err) ? err : cnt;
- }
3.4.5 ioctl方法
这里只是简单的看下ioctl方法,这个函数调用很多其他的函数,详细的请自己看吧。
下列代码位于drivers/video/fbmem.c
- static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
- {
-
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
-
- return do_fb_ioctl(info, cmd, arg);
- }
-
- static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
- {
- struct fb_ops *fb;
- struct fb_var_screeninfo var;
- struct fb_fix_screeninfo fix;
- struct fb_con2fbmap con2fb;
- struct fb_cmap cmap_from;
- struct fb_cmap_user cmap;
- struct fb_event event;
- void __user *argp = (void __user *)arg;
- long ret = 0;
-
- switch (cmd) {
-
- case FBIOGET_VSCREENINFO:
- if (!lock_fb_info(info))
- return -ENODEV;
- var = info->var;
- unlock_fb_info(info);
-
- ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
- break;
-
- case FBIOPUT_VSCREENINFO:
- if (copy_from_user(&var, argp, sizeof(var)))
- return -EFAULT;
- if (!lock_fb_info(info))
- return -ENODEV;
- acquire_console_sem();
- info->flags |= FBINFO_MISC_USEREVENT;
- ret = fb_set_var(info, &var);
- info->flags &= ~FBINFO_MISC_USEREVENT;
- release_console_sem();
- unlock_fb_info(info);
- if (!ret && copy_to_user(argp, &var, sizeof(var)))
- ret = -EFAULT;
- break;
-
- case FBIOGET_FSCREENINFO:
- if (!lock_fb_info(info))
- return -ENODEV;
- fix = info->fix;
- unlock_fb_info(info);
-
- ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
- break;
-
- case FBIOPUTCMAP:
- if (copy_from_user(&cmap, argp, sizeof(cmap)))
- return -EFAULT;
- ret = fb_set_user_cmap(&cmap, info);
- break;
-
- case FBIOGETCMAP:
- if (copy_from_user(&cmap, argp, sizeof(cmap)))
- return -EFAULT;
- if (!lock_fb_info(info))
- return -ENODEV;
- cmap_from = info->cmap;
- unlock_fb_info(info);
- ret = fb_cmap_to_user(&cmap_from, &cmap);
- break;
- case FBIOPAN_DISPLAY:
- if (copy_from_user(&var, argp, sizeof(var)))
- return -EFAULT;
- if (!lock_fb_info(info))
- return -ENODEV;
- acquire_console_sem();
- ret = fb_pan_display(info, &var);
- release_console_sem();
- unlock_fb_info(info);
- if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
- return -EFAULT;
- break;
- case FBIO_CURSOR:
- ret = -EINVAL;
- break;
- case FBIOGET_CON2FBMAP:
- if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
- return -EFAULT;
- if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
- return -EINVAL;
- con2fb.framebuffer = -1;
- event.data = &con2fb;
- if (!lock_fb_info(info))
- return -ENODEV;
- event.info = info;
- fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
- unlock_fb_info(info);
- ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
- break;
- case FBIOPUT_CON2FBMAP:
- if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
- return -EFAULT;
- if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
- return -EINVAL;
- if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
- return -EINVAL;
- if (!registered_fb[con2fb.framebuffer])
- request_module("fb%d", con2fb.framebuffer);
- if (!registered_fb[con2fb.framebuffer]) {
- ret = -EINVAL;
- break;
- }
- event.data = &con2fb;
- if (!lock_fb_info(info))
- return -ENODEV;
- event.info = info;
- ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
- unlock_fb_info(info);
- break;
- case FBIOBLANK:
- if (!lock_fb_info(info))
- return -ENODEV;
- acquire_console_sem();
- info->flags |= FBINFO_MISC_USEREVENT;
- ret = fb_blank(info, arg); ?*最后调用驱动提供的s3c2410fb_blank*/
- info->flags &= ~FBINFO_MISC_USEREVENT;
- release_console_sem();
- unlock_fb_info(info);
- break;
- default:
- if (!lock_fb_info(info))
- return -ENODEV;
- fb = info->fbops;
- if (fb->fb_ioctl)
- ret = fb->fb_ioctl(info, cmd, arg);
- else
- ret = -ENOTTY;
- unlock_fb_info(info);
- }
- return ret;
- }
- static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
- {
-
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
-
- return do_fb_ioctl(info, cmd, arg);
- }
-
- static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
- {
- struct fb_ops *fb;
- struct fb_var_screeninfo var;
- struct fb_fix_screeninfo fix;
- struct fb_con2fbmap con2fb;
- struct fb_cmap cmap_from;
- struct fb_cmap_user cmap;
- struct fb_event event;
- void __user *argp = (void __user *)arg;
- long ret = 0;
-
- switch (cmd) {
-
- case FBIOGET_VSCREENINFO:
- if (!lock_fb_info(info))
- return -ENODEV;
- var = info->var;
- unlock_fb_info(info);
-
- ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
- break;
-
- case FBIOPUT_VSCREENINFO:
- if (copy_from_user(&var, argp, sizeof(var)))
- return -EFAULT;
- if (!lock_fb_info(info))
- return -ENODEV;
- acquire_console_sem();
- info->flags |= FBINFO_MISC_USEREVENT;
- ret = fb_set_var(info, &var);
- info->flags &= ~FBINFO_MISC_USEREVENT;
- release_console_sem();
- unlock_fb_info(info);
- if (!ret && copy_to_user(argp, &var, sizeof(var)))
- ret = -EFAULT;
- break;
-
- case FBIOGET_FSCREENINFO:
- if (!lock_fb_info(info))
- return -ENODEV;
- fix = info->fix;
- unlock_fb_info(info);
-
- ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
- break;
-
- case FBIOPUTCMAP:
- if (copy_from_user(&cmap, argp, sizeof(cmap)))
- return -EFAULT;
- ret = fb_set_user_cmap(&cmap, info);
- break;
-
- case FBIOGETCMAP:
- if (copy_from_user(&cmap, argp, sizeof(cmap)))
- return -EFAULT;
- if (!lock_fb_info(info))
- return -ENODEV;
- cmap_from = info->cmap;
- unlock_fb_info(info);
- ret = fb_cmap_to_user(&cmap_from, &cmap);
- break;
- case FBIOPAN_DISPLAY:
- if (copy_from_user(&var, argp, sizeof(var)))
- return -EFAULT;
- if (!lock_fb_info(info))
- return -ENODEV;
- acquire_console_sem();
- ret = fb_pan_display(info, &var);
- release_console_sem();
- unlock_fb_info(info);
- if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
- return -EFAULT;
- break;
- case FBIO_CURSOR:
- ret = -EINVAL;
- break;
- case FBIOGET_CON2FBMAP:
- if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
- return -EFAULT;
- if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
- return -EINVAL;
- con2fb.framebuffer = -1;
- event.data = &con2fb;
- if (!lock_fb_info(info))
- return -ENODEV;
- event.info = info;
- fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
- unlock_fb_info(info);
- ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
- break;
- case FBIOPUT_CON2FBMAP:
- if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
- return -EFAULT;
- if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
- return -EINVAL;
- if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
- return -EINVAL;
- if (!registered_fb[con2fb.framebuffer])
- request_module("fb%d", con2fb.framebuffer);
- if (!registered_fb[con2fb.framebuffer]) {
- ret = -EINVAL;
- break;
- }
- event.data = &con2fb;
- if (!lock_fb_info(info))
- return -ENODEV;
- event.info = info;
- ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
- unlock_fb_info(info);
- break;
- case FBIOBLANK:
- if (!lock_fb_info(info))
- return -ENODEV;
- acquire_console_sem();
- info->flags |= FBINFO_MISC_USEREVENT;
- ret = fb_blank(info, arg); ?*最后调用驱动提供的s3c2410fb_blank*/
- info->flags &= ~FBINFO_MISC_USEREVENT;
- release_console_sem();
- unlock_fb_info(info);
- break;
- default:
- if (!lock_fb_info(info))
- return -ENODEV;
- fb = info->fbops;
- if (fb->fb_ioctl)
- ret = fb->fb_ioctl(info, cmd, arg);
- else
- ret = -ENOTTY;
- unlock_fb_info(info);
- }
- return ret;
- }
正如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
- static struct platform_driver s3c2410fb_driver = {
- .probe = s3c2410fb_probe,
- .remove = s3c2410fb_remove,
- .suspend = s3c2410fb_suspend,
- .resume = s3c2410fb_resume,
- .driver = {
- .name = "s3c2410-lcd",
- .owner = THIS_MODULE,
- },
- };
-
- int __init s3c2410fb_init(void)
- {
- int ret = platform_driver_register(&s3c2410fb_driver);
-
- if (ret == 0)
- ret = platform_driver_register(&s3c2412fb_driver);;
-
- return ret;
- }
-
- static void __exit s3c2410fb_cleanup(void)
- {
- platform_driver_unregister(&s3c2410fb_driver);
- platform_driver_unregister(&s3c2412fb_driver);
- }
-
- module_init(s3c2410fb_init);
- module_exit(s3c2410fb_cleanup);
- static struct platform_driver s3c2410fb_driver = {
- .probe = s3c2410fb_probe,
- .remove = s3c2410fb_remove,
- .suspend = s3c2410fb_suspend,
- .resume = s3c2410fb_resume,
- .driver = {
- .name = "s3c2410-lcd",
- .owner = THIS_MODULE,
- },
- };
-
- int __init s3c2410fb_init(void)
- {
- int ret = platform_driver_register(&s3c2410fb_driver);
-
- if (ret == 0)
- ret = platform_driver_register(&s3c2412fb_driver);;
-
- return ret;
- }
-
- static void __exit s3c2410fb_cleanup(void)
- {
- platform_driver_unregister(&s3c2410fb_driver);
- platform_driver_unregister(&s3c2412fb_driver);
- }
-
- module_init(s3c2410fb_init);
- module_exit(s3c2410fb_cleanup);
当platform_driver_register调用的最后会调用probe方法。接下来就来看看probe方法。
4.2 probe方法
- struct s3c2410fb_info {
- struct device *dev;
- struct clk *clk;
-
- struct resource *mem;
- void __iomem *io;
- void __iomem *irq_base;
-
- enum s3c_drv_type drv_type;
- struct s3c2410fb_hw regs;
-
- unsigned int palette_ready;
-
-
- u32 palette_buffer[256];
- u32 pseudo_pal[16];
- };
-
- struct s3c2410fb_mach_info {
-
- struct s3c2410fb_display *displays;
- unsigned num_displays;
- unsigned default_display;
-
-
-
- unsigned long gpcup;
- unsigned long gpcup_mask;
- unsigned long gpccon;
- unsigned long gpccon_mask;
- unsigned long gpdup;
- unsigned long gpdup_mask;
- unsigned long gpdcon;
- unsigned long gpdcon_mask;
-
-
- unsigned long lpcsel;
- };
-
- struct s3c2410fb_display {
-
- unsigned type;
-
-
- unsigned short width;
- unsigned short height;
-
-
- unsigned short xres;
- unsigned short yres;
- unsigned short bpp;
-
- unsigned pixclock;
- unsigned short left_margin;
- unsigned short right_margin;
- unsigned short hsync_len;
- unsigned short upper_margin;
- unsigned short lower_margin;
- unsigned short vsync_len;
-
-
- unsigned long lcdcon5;
- };
-
- static int __init s3c24xxfb_probe(struct platform_device *pdev,
- enum s3c_drv_type drv_type)
- {
- struct s3c2410fb_info *info;
- struct s3c2410fb_display *display;
- struct fb_info *fbinfo;
- struct s3c2410fb_mach_info *mach_info;
- struct resource *res;
- int ret;
- int irq;
- int i;
- int size;
- u32 lcdcon1;
-
- mach_info = pdev->dev.platform_data;
- if (mach_info == NULL) {
- dev_err(&pdev->dev,
- "no platform data for lcd, cannot attach\n");
- return -EINVAL;
- }
-
- if (mach_info->default_display >= mach_info->num_displays) {
- dev_err(&pdev->dev, "default is %d but only %d displays\n",
- mach_info->default_display, mach_info->num_displays);
- return -EINVAL;
- }
-
- display = mach_info->displays + mach_info->default_display;
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for device\n");
- return -ENOENT;
- }
-
- fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
- if (!fbinfo)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, fbinfo);
-
- info = fbinfo->par;
- info->dev = &pdev->dev;
- info->drv_type = drv_type;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "failed to get memory registers\n");
- ret = -ENXIO;
- goto dealloc_fb;
- }
-
- size = (res->end - res->start) + 1;
- info->mem = request_mem_region(res->start, size, pdev->name);
- if (info->mem == NULL) {
- dev_err(&pdev->dev, "failed to get memory region\n");
- ret = -ENOENT;
- goto dealloc_fb;
- }
-
- info->io = ioremap(res->start, size);
- if (info->io == NULL) {
- dev_err(&pdev->dev, "ioremap() of registers failed\n");
- ret = -ENXIO;
- goto release_mem;
- }
-
- info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
-
- dprintk("devinit\n");
-
- strcpy(fbinfo->fix.id, driver_name);
-
-
- lcdcon1 = readl(info->io + S3C2410_LCDCON1);
- writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
-
- fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
- fbinfo->fix.type_aux = 0;
- fbinfo->fix.xpanstep = 0;
- fbinfo->fix.ypanstep = 0;
- fbinfo->fix.ywrapstep = 0;
- fbinfo->fix.accel = FB_ACCEL_NONE;
-
- fbinfo->var.nonstd = 0;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.accel_flags = 0;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
-
- fbinfo->fbops = &s3c2410fb_ops;
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
- fbinfo->pseudo_palette = &info->pseudo_pal;
-
- for (i = 0; i < 256; i++)
- info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
-
- ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
- if (ret) {
- dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
- ret = -EBUSY;
- goto release_regs;
- }
-
- info->clk = clk_get(NULL, "lcd");
- if (!info->clk || IS_ERR(info->clk)) {
- printk(KERN_ERR "failed to get lcd clock source\n");
- ret = -ENOENT;
- goto release_irq;
- }
-
- clk_enable(info->clk);
- dprintk("got and enabled clock\n");
-
- msleep(1);
-
-
-
- for (i = 0; i < mach_info->num_displays; i++) {
- unsigned long smem_len = mach_info->displays[i].xres;
-
- smem_len *= mach_info->displays[i].yres;
- smem_len *= mach_info->displays[i].bpp;
- smem_len >>= 3;
- if (fbinfo->fix.smem_len < smem_len)
- fbinfo->fix.smem_len = smem_len;
- }
-
-
- ret = s3c2410fb_map_video_memory(fbinfo);
- if (ret) {
- printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
- ret = -ENOMEM;
- goto release_clock;
- }
-
- dprintk("got video memory\n");
-
- fbinfo->var.xres = display->xres;
- fbinfo->var.yres = display->yres;
- fbinfo->var.bits_per_pixel = display->bpp;
-
- s3c2410fb_init_registers(fbinfo);
-
- s3c2410fb_check_var(&fbinfo->var, fbinfo);
-
- ret = register_framebuffer(fbinfo);
- if (ret < 0) {
- printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);
- goto free_video_memory;
- }
-
-
- ret = device_create_file(&pdev->dev, &dev_attr_debug);
- if (ret) {
- printk(KERN_ERR "failed to add debug attribute\n");
- }
-
- printk(KERN_INFO "fb%d: %s frame buffer device\n",
- fbinfo->node, fbinfo->fix.id);
-
- return 0;
- free_video_memory:
- s3c2410fb_unmap_video_memory(fbinfo);
- release_clock:
- clk_disable(info->clk);
- clk_put(info->clk);
- release_irq:
- free_irq(irq, info);
- release_regs:
- iounmap(info->io);
- release_mem:
- release_resource(info->mem);
- kfree(info->mem);
- dealloc_fb:
- platform_set_drvdata(pdev, NULL);
- framebuffer_release(fbinfo);
- return ret;
- }
- struct s3c2410fb_info {
- struct device *dev;
- struct clk *clk;
-
- struct resource *mem;
- void __iomem *io;
- void __iomem *irq_base;
-
- enum s3c_drv_type drv_type;
- struct s3c2410fb_hw regs;
-
- unsigned int palette_ready;
-
-
- u32 palette_buffer[256];
- u32 pseudo_pal[16];
- };
-
- struct s3c2410fb_mach_info {
-
- struct s3c2410fb_display *displays;
- unsigned num_displays;
- unsigned default_display;
-
-
-
- unsigned long gpcup;
- unsigned long gpcup_mask;
- unsigned long gpccon;
- unsigned long gpccon_mask;
- unsigned long gpdup;
- unsigned long gpdup_mask;
- unsigned long gpdcon;
- unsigned long gpdcon_mask;
-
-
- unsigned long lpcsel;
- };
-
- struct s3c2410fb_display {
-
- unsigned type;
-
-
- unsigned short width;
- unsigned short height;
-
-
- unsigned short xres;
- unsigned short yres;
- unsigned short bpp;
-
- unsigned pixclock;
- unsigned short left_margin;
- unsigned short right_margin;
- unsigned short hsync_len;
- unsigned short upper_margin;
- unsigned short lower_margin;
- unsigned short vsync_len;
-
-
- unsigned long lcdcon5;
- };
-
- static int __init s3c24xxfb_probe(struct platform_device *pdev,
- enum s3c_drv_type drv_type)
- {
- struct s3c2410fb_info *info;
- struct s3c2410fb_display *display;
- struct fb_info *fbinfo;
- struct s3c2410fb_mach_info *mach_info;
- struct resource *res;
- int ret;
- int irq;
- int i;
- int size;
- u32 lcdcon1;
-
- mach_info = pdev->dev.platform_data;
- if (mach_info == NULL) {
- dev_err(&pdev->dev,
- "no platform data for lcd, cannot attach\n");
- return -EINVAL;
- }
-
- if (mach_info->default_display >= mach_info->num_displays) {
- dev_err(&pdev->dev, "default is %d but only %d displays\n",
- mach_info->default_display, mach_info->num_displays);
- return -EINVAL;
- }
-
- display = mach_info->displays + mach_info->default_display;
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq for device\n");
- return -ENOENT;
- }
-
- fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
- if (!fbinfo)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, fbinfo);
-
- info = fbinfo->par;
- info->dev = &pdev->dev;
- info->drv_type = drv_type;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "failed to get memory registers\n");
- ret = -ENXIO;
- goto dealloc_fb;
- }
-
- size = (res->end - res->start) + 1;
- info->mem = request_mem_region(res->start, size, pdev->name);
- if (info->mem == NULL) {
- dev_err(&pdev->dev, "failed to get memory region\n");
- ret = -ENOENT;
- goto dealloc_fb;
- }
-
- info->io = ioremap(res->start, size);
- if (info->io == NULL) {
- dev_err(&pdev->dev, "ioremap() of registers failed\n");
- ret = -ENXIO;
- goto release_mem;
- }
-
- info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
-
- dprintk("devinit\n");
-
- strcpy(fbinfo->fix.id, driver_name);
-
-
- lcdcon1 = readl(info->io + S3C2410_LCDCON1);
- writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
-
- fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
- fbinfo->fix.type_aux = 0;
- fbinfo->fix.xpanstep = 0;
- fbinfo->fix.ypanstep = 0;
- fbinfo->fix.ywrapstep = 0;
- fbinfo->fix.accel = FB_ACCEL_NONE;
-
- fbinfo->var.nonstd = 0;
- fbinfo->var.activate = FB_ACTIVATE_NOW;
- fbinfo->var.accel_flags = 0;
- fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
-
- fbinfo->fbops = &s3c2410fb_ops;
- fbinfo->flags = FBINFO_FLAG_DEFAULT;
- fbinfo->pseudo_palette = &info->pseudo_pal;
-
- for (i = 0; i < 256; i++)
- info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
-
- ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
- if (ret) {
- dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
- ret = -EBUSY;
- goto release_regs;
- }
-
- info->clk = clk_get(NULL, "lcd");
- if (!info->clk || IS_ERR(info->clk)) {
- printk(KERN_ERR "failed to get lcd clock source\n");
- ret = -ENOENT;
- goto release_irq;
- }
-
- clk_enable(info->clk);
- dprintk("got and enabled clock\n");
-
- msleep(1);
-
-
-
- for (i = 0; i < mach_info->num_displays; i++) {
- unsigned long smem_len = mach_info->displays[i].xres;
-
- smem_len *= mach_info->displays[i].yres;
- smem_len *= mach_info->displays[i].bpp;
- smem_len >>= 3;
- if (fbinfo->fix.smem_len < smem_len)
- fbinfo->fix.smem_len = smem_len;
- }
-
-
- ret = s3c2410fb_map_video_memory(fbinfo);
- if (ret) {
- printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
- ret = -ENOMEM;
- goto release_clock;
- }
-
- dprintk("got video memory\n");
-
- fbinfo->var.xres = display->xres;
- fbinfo->var.yres = display->yres;
- fbinfo->var.bits_per_pixel = display->bpp;
-
- s3c2410fb_init_registers(fbinfo);
-
- s3c2410fb_check_var(&fbinfo->var, fbinfo);
-
- ret = register_framebuffer(fbinfo);
- if (ret < 0) {
- printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);
- goto free_video_memory;
- }
-
-
- ret = device_create_file(&pdev->dev, &dev_attr_debug);
- if (ret) {
- printk(KERN_ERR "failed to add debug attribute\n");
- }
-
- printk(KERN_INFO "fb%d: %s frame buffer device\n",
- fbinfo->node, fbinfo->fix.id);
-
- return 0;
- free_video_memory:
- s3c2410fb_unmap_video_memory(fbinfo);
- release_clock:
- clk_disable(info->clk);
- clk_put(info->clk);
- release_irq:
- free_irq(irq, info);
- release_regs:
- iounmap(info->io);
- release_mem:
- release_resource(info->mem);
- kfree(info->mem);
- dealloc_fb:
- platform_set_drvdata(pdev, NULL);
- framebuffer_release(fbinfo);
- return ret;
- }
这里使用了三个新的数据结构。s3c2410fb_info是驱动程序使用的,里面将保存所有驱动程序所要使用的资源等。而s3c2410fb_display和s3c2410fb_mach_info,是由板级信息,通过platform总线添加到内核中。
s3c2410fb_display中的成员将被复制到fb_var_screeninfo结构中。
该板级信息的定义在arch/arm/mach-s3c2440/mach-smdk2440.c中,来看下
- static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
-
- .lcdcon5 = S3C2410_LCDCON5_FRM565 |
- S3C2410_LCDCON5_INVVLINE |
- S3C2410_LCDCON5_INVVFRAME |
- S3C2410_LCDCON5_PWREN |
- S3C2410_LCDCON5_HWSWP,
-
- .type = S3C2410_LCDCON1_TFT,
-
- .width = 320,
- .height = 240,
-
- .pixclock = 149000,
- .xres = 320,
- .yres = 240,
- .bpp = 16,
- .left_margin = 20,
- .right_margin = 38,
- .hsync_len = 30,
- .upper_margin = 15,
- .lower_margin = 12,
- .vsync_len = 3,
- };
-
- static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
- .displays = &smdk2440_lcd_cfg,
- .num_displays = 1,
- .default_display = 0,
-
- #if 0
-
- .gpccon = 0xaa940659,
- .gpccon_mask = 0xffffffff,
- .gpcup = 0x0000ffff,
- .gpcup_mask = 0xffffffff,
- .gpdcon = 0xaa84aaa0,
- .gpdcon_mask = 0xffffffff,
- .gpdup = 0x0000faff,
- .gpdup_mask = 0xffffffff,
- #endif
- };
- static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
-
- .lcdcon5 = S3C2410_LCDCON5_FRM565 |
- S3C2410_LCDCON5_INVVLINE |
- S3C2410_LCDCON5_INVVFRAME |
- S3C2410_LCDCON5_PWREN |
- S3C2410_LCDCON5_HWSWP,
-
- .type = S3C2410_LCDCON1_TFT,
-
- .width = 320,
- .height = 240,
-
- .pixclock = 149000,
- .xres = 320,
- .yres = 240,
- .bpp = 16,
- .left_margin = 20,
- .right_margin = 38,
- .hsync_len = 30,
- .upper_margin = 15,
- .lower_margin = 12,
- .vsync_len = 3,
- };
-
- static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
- .displays = &smdk2440_lcd_cfg,
- .num_displays = 1,
- .default_display = 0,
-
- #if 0
-
- .gpccon = 0xaa940659,
- .gpccon_mask = 0xffffffff,
- .gpcup = 0x0000ffff,
- .gpcup_mask = 0xffffffff,
- .gpdcon = 0xaa84aaa0,
- .gpdcon_mask = 0xffffffff,
- .gpdup = 0x0000faff,
- .gpdup_mask = 0xffffffff,
- #endif
- };
这里NOTE:每个LCD屏幕的参数不一样,因此上面的参数会有所不同,这是需要移植的地方。
随后,我们看下在probe方法中调用的几个函数。首先是s3c2410fb_map_video_memory。
- static int __init s3c2410fb_map_video_memory(struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- dma_addr_t map_dma;
- unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
-
- dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
-
- info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
- &map_dma, GFP_KERNEL);
-
- if (info->screen_base) {
-
- dprintk("map_video_memory: clear %p:%08x\n",
- info->screen_base, map_size);
- memset(info->screen_base, 0x00, map_size);
-
- info->fix.smem_start = map_dma;
-
- dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
- info->fix.smem_start, info->screen_base, map_size);
- }
-
- return info->screen_base ? 0 : -ENOMEM;
- }
- static int __init s3c2410fb_map_video_memory(struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- dma_addr_t map_dma;
- unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
-
- dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
-
- info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
- &map_dma, GFP_KERNEL);
-
- if (info->screen_base) {
-
- dprintk("map_video_memory: clear %p:%08x\n",
- info->screen_base, map_size);
- memset(info->screen_base, 0x00, map_size);
-
- info->fix.smem_start = map_dma;
-
- dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
- info->fix.smem_start, info->screen_base, map_size);
- }
-
- return info->screen_base ? 0 : -ENOMEM;
- }
该函数根据fix.smem_len的大小,分配了一个DMA缓冲区,保存了该缓冲区的物理地址和虚拟地址。
接着是s3c2410fb_init_registers:
- static int s3c2410fb_init_registers(struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
- unsigned long flags;
- void __iomem *regs = fbi->io;
- void __iomem *tpal;
- void __iomem *lpcsel;
-
- if (is_s3c2412(fbi)) {
- tpal = regs + S3C2412_TPAL;
- lpcsel = regs + S3C2412_TCONSEL;
- } else {
- tpal = regs + S3C2410_TPAL;
- lpcsel = regs + S3C2410_LPCSEL;
- }
-
-
-
- local_irq_save(flags);
-
-
-
- modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
- modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
- modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
- modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
-
- local_irq_restore(flags);
-
- dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel);
- writel(mach_info->lpcsel, lpcsel);
-
- dprintk("replacing TPAL %08x\n", readl(tpal));
-
-
- writel(0x00, tpal);
-
- return 0;
- }
-
- static inline void modify_gpio(void __iomem *reg,
- unsigned long set, unsigned long mask)
- {
- unsigned long tmp;
-
- tmp = readl(reg) & ~mask;
- writel(tmp | set, reg);
- }
- static int s3c2410fb_init_registers(struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
- unsigned long flags;
- void __iomem *regs = fbi->io;
- void __iomem *tpal;
- void __iomem *lpcsel;
-
- if (is_s3c2412(fbi)) {
- tpal = regs + S3C2412_TPAL;
- lpcsel = regs + S3C2412_TCONSEL;
- } else {
- tpal = regs + S3C2410_TPAL;
- lpcsel = regs + S3C2410_LPCSEL;
- }
-
-
-
- local_irq_save(flags);
-
-
-
- modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
- modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
- modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
- modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
-
- local_irq_restore(flags);
-
- dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel);
- writel(mach_info->lpcsel, lpcsel);
-
- dprintk("replacing TPAL %08x\n", readl(tpal));
-
-
- writel(0x00, tpal);
-
- return 0;
- }
-
- static inline void modify_gpio(void __iomem *reg,
- unsigned long set, unsigned long mask)
- {
- unsigned long tmp;
-
- tmp = readl(reg) & ~mask;
- writel(tmp | set, reg);
- }
最后是s3c2410fb_check_var:
该函数主要将板级信息s3c2410fb_display复制到对应的地方,然后根据RGB的模式设置位域。
4.3 fb_ops方法
在驱动程序中,定义了fb_ops,如下:
- static struct fb_ops s3c2410fb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = s3c2410fb_check_var,
- .fb_set_par = s3c2410fb_set_par,
- .fb_blank = s3c2410fb_blank,
- .fb_setcolreg = s3c2410fb_setcolreg,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- };
- static struct fb_ops s3c2410fb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = s3c2410fb_check_var,
- .fb_set_par = s3c2410fb_set_par,
- .fb_blank = s3c2410fb_blank,
- .fb_setcolreg = s3c2410fb_setcolreg,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- };
其中s3c2410fb_check_var在4.2节中已经分析过了,最后三个方法是通用库函数,在这里不作分析。
剩余三个也是驱动程序提供的,现在对这三个程序进行分析。
4.3. 1 s3c2410fb_set_par
- static int s3c2410fb_set_par(struct fb_info *info)
- {
- struct fb_var_screeninfo *var = &info->var;
-
- switch (var->bits_per_pixel) {
- case 32:
- case 16:
- case 12:
- info->fix.visual = FB_VISUAL_TRUECOLOR;
- break;
- case 1:
- info->fix.visual = FB_VISUAL_MONO01;
- break;
- default:
- info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
- break;
- }
-
- info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
-
-
-
- s3c2410fb_activate_var(info);
- return 0;
- }
- static int s3c2410fb_set_par(struct fb_info *info)
- {
- struct fb_var_screeninfo *var = &info->var;
-
- switch (var->bits_per_pixel) {
- case 32:
- case 16:
- case 12:
- info->fix.visual = FB_VISUAL_TRUECOLOR;
- break;
- case 1:
- info->fix.visual = FB_VISUAL_MONO01;
- break;
- default:
- info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
- break;
- }
-
- info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
-
-
-
- s3c2410fb_activate_var(info);
- return 0;
- }
该函数根据像素的位数设置了视觉模式,本例为16为,使用真彩色。然后计算了每行的数据元素大小。共240行。
然后调用了s3c2410fb_activate_var来设置控制器并激活LCD。
s3c2410fb_activate_var函数如下:
- static void s3c2410fb_activate_var(struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *regs = fbi->io;
- int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
- struct fb_var_screeninfo *var = &info->var;
- int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
-
- dprintk("%s: var->xres = %d\n", __func__, var->xres);
- dprintk("%s: var->yres = %d\n", __func__, var->yres);
- dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);
-
- if (type == S3C2410_LCDCON1_TFT) {
- s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
- --clkdiv;
- if (clkdiv < 0)
- clkdiv = 0;
- } else {
- s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
- if (clkdiv < 2)
- clkdiv = 2;
- }
-
- fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);
-
-
-
- dprintk("new register set:\n");
- dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
- dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
- dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
- dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
- dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
-
- writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
- regs + S3C2410_LCDCON1);
- writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
- writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
- writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
- writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
-
-
- s3c2410fb_set_lcdaddr(info);
-
- fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
- writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
- }
- static void s3c2410fb_activate_var(struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *regs = fbi->io;
- int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
- struct fb_var_screeninfo *var = &info->var;
- int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
-
- dprintk("%s: var->xres = %d\n", __func__, var->xres);
- dprintk("%s: var->yres = %d\n", __func__, var->yres);
- dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);
-
- if (type == S3C2410_LCDCON1_TFT) {
- s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
- --clkdiv;
- if (clkdiv < 0)
- clkdiv = 0;
- } else {
- s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
- if (clkdiv < 2)
- clkdiv = 2;
- }
-
- fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);
-
-
-
- dprintk("new register set:\n");
- dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
- dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
- dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
- dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
- dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
-
- writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
- regs + S3C2410_LCDCON1);
- writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
- writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
- writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
- writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
-
-
- s3c2410fb_set_lcdaddr(info);
-
- fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
- writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
- }
其中调用的三个函数如下:
- static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
- unsigned long pixclk)
- {
- unsigned long clk = clk_get_rate(fbi->clk);
- unsigned long long div;
-
-
-
- div = (unsigned long long)clk * pixclk;
- div >>= 12;
- do_div(div, 625 * 625UL * 625);
-
- dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
- return div;
- }
-
- static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,
- struct s3c2410fb_hw *regs)
- {
- const struct s3c2410fb_info *fbi = info->par;
- const struct fb_var_screeninfo *var = &info->var;
-
- switch (var->bits_per_pixel) {
- case 1:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;
- break;
- case 2:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
- break;
- case 4:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
- break;
- case 8:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
- regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |
- S3C2410_LCDCON5_FRM565;
- regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;
- break;
- case 16:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
- regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;
- regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;
- break;
- case 32:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;
- regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |
- S3C2410_LCDCON5_HWSWP |
- S3C2410_LCDCON5_BPP24BL);
- break;
- default:
-
- dev_err(fbi->dev, "invalid bpp %d\n",
- var->bits_per_pixel);
- }
-
- dprintk("setting vert: up=%d, low=%d, sync=%d\n",
- var->upper_margin, var->lower_margin, var->vsync_len);
-
- dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
- var->left_margin, var->right_margin, var->hsync_len);
-
- regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |
- S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
- S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
- S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
-
- regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
- S3C2410_LCDCON3_HFPD(var->left_margin - 1) |
- S3C2410_LCDCON3_HOZVAL(var->xres - 1);
-
- regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
- }
-
- static void s3c2410fb_set_lcdaddr(struct fb_info *info)
- {
- unsigned long saddr1, saddr2, saddr3;
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *regs = fbi->io;
-
- saddr1 = info->fix.smem_start >> 1;
- saddr2 = info->fix.smem_start;
- saddr2 += info->fix.line_length * info->var.yres;
- saddr2 >>= 1;
-
- saddr3 = S3C2410_OFFSIZE(0) |
- S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);
-
- dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
- dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
- dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
-
- writel(saddr1, regs + S3C2410_LCDSADDR1);
- writel(saddr2, regs + S3C2410_LCDSADDR2);
- writel(saddr3, regs + S3C2410_LCDSADDR3);
- }
- static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
- unsigned long pixclk)
- {
- unsigned long clk = clk_get_rate(fbi->clk);
- unsigned long long div;
-
-
-
- div = (unsigned long long)clk * pixclk;
- div >>= 12;
- do_div(div, 625 * 625UL * 625);
-
- dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
- return div;
- }
-
- static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,
- struct s3c2410fb_hw *regs)
- {
- const struct s3c2410fb_info *fbi = info->par;
- const struct fb_var_screeninfo *var = &info->var;
-
- switch (var->bits_per_pixel) {
- case 1:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;
- break;
- case 2:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
- break;
- case 4:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
- break;
- case 8:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
- regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |
- S3C2410_LCDCON5_FRM565;
- regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;
- break;
- case 16:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
- regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;
- regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;
- break;
- case 32:
- regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;
- regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |
- S3C2410_LCDCON5_HWSWP |
- S3C2410_LCDCON5_BPP24BL);
- break;
- default:
-
- dev_err(fbi->dev, "invalid bpp %d\n",
- var->bits_per_pixel);
- }
-
- dprintk("setting vert: up=%d, low=%d, sync=%d\n",
- var->upper_margin, var->lower_margin, var->vsync_len);
-
- dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
- var->left_margin, var->right_margin, var->hsync_len);
-
- regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |
- S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
- S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
- S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
-
- regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
- S3C2410_LCDCON3_HFPD(var->left_margin - 1) |
- S3C2410_LCDCON3_HOZVAL(var->xres - 1);
-
- regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
- }
-
- static void s3c2410fb_set_lcdaddr(struct fb_info *info)
- {
- unsigned long saddr1, saddr2, saddr3;
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *regs = fbi->io;
-
- saddr1 = info->fix.smem_start >> 1;
- saddr2 = info->fix.smem_start;
- saddr2 += info->fix.line_length * info->var.yres;
- saddr2 >>= 1;
-
- saddr3 = S3C2410_OFFSIZE(0) |
- S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);
-
- dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
- dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
- dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
-
- writel(saddr1, regs + S3C2410_LCDSADDR1);
- writel(saddr2, regs + S3C2410_LCDSADDR2);
- writel(saddr3, regs + S3C2410_LCDSADDR3);
- }
s3c2410fb_calc_pixclk用于计算时钟频率。
s3c2410fb_calculate_tft_lcd_regs设置了LCD的控制寄存器。
s3c2410fb_set_lcdaddr设置了LCD的地址寄存器,该寄存器的设置请参考datasheet。
4.3. 2 s3c2410fb_blank
该方法完成消隐功能。
- static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *tpal_reg = fbi->io;
-
- dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
-
- tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;
-
- if (blank_mode == FB_BLANK_POWERDOWN) {
- s3c2410fb_lcd_enable(fbi, 0);
- } else {
- s3c2410fb_lcd_enable(fbi, 1);
- }
-
- if (blank_mode == FB_BLANK_UNBLANK)
- writel(0x0, tpal_reg);
- else {
- dprintk("setting TPAL to output 0x000000\n");
- writel(S3C2410_TPAL_EN, tpal_reg);
- }
-
- return 0;
- }
- static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *tpal_reg = fbi->io;
-
- dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
-
- tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;
-
- if (blank_mode == FB_BLANK_POWERDOWN) {
- s3c2410fb_lcd_enable(fbi, 0);
- } else {
- s3c2410fb_lcd_enable(fbi, 1);
- }
-
- if (blank_mode == FB_BLANK_UNBLANK)
- writel(0x0, tpal_reg);
- else {
- dprintk("setting TPAL to output 0x000000\n");
- writel(S3C2410_TPAL_EN, tpal_reg);
- }
-
- return 0;
- }
在消隐时,屏幕将全黑。
4.3.3 s3c2410fb_setcolreg
该函数的功能用于设置LCD的调色板。调色板的概念请看我的转帖:LCD调色板。
- static int s3c2410fb_setcolreg(unsigned regno,
- unsigned red, unsigned green, unsigned blue,
- unsigned transp, struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *regs = fbi->io;
- unsigned int val;
-
-
-
- switch (info->fix.visual) {
- case FB_VISUAL_TRUECOLOR:
-
-
- if (regno < 16) {
- u32 *pal = info->pseudo_palette;
-
- val = chan_to_field(red, &info->var.red);
- val |= chan_to_field(green, &info->var.green);
- val |= chan_to_field(blue, &info->var.blue);
-
- pal[regno] = val;
- }
- break;
-
- case FB_VISUAL_PSEUDOCOLOR:
- if (regno < 256) {
-
-
- val = (red >> 0) & 0xf800;
- val |= (green >> 5) & 0x07e0;
- val |= (blue >> 11) & 0x001f;
-
- writel(val, regs + S3C2410_TFTPAL(regno));
- schedule_palette_update(fbi, regno, val);
- }
-
- break;
-
- default:
- return 1;
- }
-
- return 0;
- }
- static inline unsigned int chan_to_field(unsigned int chan,
- struct fb_bitfield *bf)
- {
-
- chan &= 0xffff;
- chan >>= 16 - bf->length;
- return chan << bf->offset;
- }
- static int s3c2410fb_setcolreg(unsigned regno,
- unsigned red, unsigned green, unsigned blue,
- unsigned transp, struct fb_info *info)
- {
- struct s3c2410fb_info *fbi = info->par;
- void __iomem *regs = fbi->io;
- unsigned int val;
-
-
-
- switch (info->fix.visual) {
- case FB_VISUAL_TRUECOLOR:
-
-
- if (regno < 16) {
- u32 *pal = info->pseudo_palette;
-
- val = chan_to_field(red, &info->var.red);
- val |= chan_to_field(green, &info->var.green);
- val |= chan_to_field(blue, &info->var.blue);
-
- pal[regno] = val;
- }
- break;
-
- case FB_VISUAL_PSEUDOCOLOR:
- if (regno < 256) {
-
-
- val = (red >> 0) & 0xf800;
- val |= (green >> 5) & 0x07e0;
- val |= (blue >> 11) & 0x001f;
-
- writel(val, regs + S3C2410_TFTPAL(regno));
- schedule_palette_update(fbi, regno, val);
- }
-
- break;
-
- default:
- return 1;
- }
-
- return 0;
- }
- static inline unsigned int chan_to_field(unsigned int chan,
- struct fb_bitfield *bf)
- {
-
- chan &= 0xffff;
- chan >>= 16 - bf->length;
- return chan << bf->offset;
- }
我们使用的是真彩色,可以设置16种颜色,颜色的位域值通过调用chan_to_field获得,然后保存了颜色的值。但是比较奇怪的是,这里并没有将值保存到0x4d000400为起始的内存中,不知为何。 反而倒是在伪彩色模式下, 使用了writel(val, regs + S3C2410_TFTPAL(regno))写到内存中,然后调用schedule_palette_update函数来更新palette表。我们来看下。
- static void schedule_palette_update(struct s3c2410fb_info *fbi,
- unsigned int regno, unsigned int val)
- {
- unsigned long flags;
- unsigned long irqen;
- void __iomem *irq_base = fbi->irq_base;
-
- local_irq_save(flags);
-
- fbi->palette_buffer[regno] = val;
-
- if (!fbi->palette_ready) {
- fbi->palette_ready = 1;
-
-
- irqen = readl(irq_base + S3C24XX_LCDINTMSK);
- irqen &= ~S3C2410_LCDINT_FRSYNC;
- writel(irqen, irq_base + S3C24XX_LCDINTMSK);
- }
-
- local_irq_restore(flags);
- }
- static void schedule_palette_update(struct s3c2410fb_info *fbi,
- unsigned int regno, unsigned int val)
- {
- unsigned long flags;
- unsigned long irqen;
- void __iomem *irq_base = fbi->irq_base;
-
- local_irq_save(flags);
-
- fbi->palette_buffer[regno] = val;
-
- if (!fbi->palette_ready) {
- fbi->palette_ready = 1;
-
-
- irqen = readl(irq_base + S3C24XX_LCDINTMSK);
- irqen &= ~S3C2410_LCDINT_FRSYNC;
- writel(irqen, irq_base + S3C24XX_LCDINTMSK);
- }
-
- local_irq_restore(flags);
- }
这个函数的作用就是开启了LCD的帧同步中断,这个中断在VSYNC信号从无效变成有效时产生。当中断产生时,会调用在probe方法中注册的ISR。ISR如下:
- static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
- {
- struct s3c2410fb_info *fbi = dev_id;
- void __iomem *irq_base = fbi->irq_base;
- unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
- b
- if (lcdirq & S3C2410_LCDINT_FRSYNC) {
- if (fbi->palette_ready)
- s3c2410fb_write_palette(fbi);
-
- writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);
- writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);
- }
-
- return IRQ_HANDLED;
- }
-
- static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
- {
- unsigned int i;
- void __iomem *regs = fbi->io;
-
- fbi->palette_ready = 0;
-
- for (i = 0; i < 256; i++) {
- unsigned long ent = fbi->palette_buffer[i];
- if (ent == PALETTE_BUFF_CLEAR)
- continue;
-
- writel(ent, regs + S3C2410_TFTPAL(i));
-
-
-
- if (readw(regs + S3C2410_TFTPAL(i)) == ent)
- fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR;
- else
- fbi->palette_ready = 1;
- }
- }
- static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
- {
- struct s3c2410fb_info *fbi = dev_id;
- void __iomem *irq_base = fbi->irq_base;
- unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
- b
- if (lcdirq & S3C2410_LCDINT_FRSYNC) {
- if (fbi->palette_ready)
- s3c2410fb_write_palette(fbi);
-
- writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);
- writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);
- }
-
- return IRQ_HANDLED;
- }
-
- static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
- {
- unsigned int i;
- void __iomem *regs = fbi->io;
-
- fbi->palette_ready = 0;
-
- for (i = 0; i < 256; i++) {
- unsigned long ent = fbi->palette_buffer[i];
- if (ent == PALETTE_BUFF_CLEAR)
- continue;
-
- writel(ent, regs + S3C2410_TFTPAL(i));
-
-
-
- if (readw(regs + S3C2410_TFTPAL(i)) == ent)
- fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR;
- else
- fbi->palette_ready = 1;
- }
- }
在中断函数中调用了s3c2410fb_write_palette,该函数对写入内存的颜色值进行检查。如果确认其已经写入,则将palette_buffer中的清除,否则retry。
5. 总结
本文对frambuffer子系统做了简单的介绍。frambuffer子系统的函数相当之多,在这里是不可能一一介绍的。本文首先介绍了主要的数据结构,
随后分析了frambuffer核心层,在核心层简单的分析了5个API函数,接着,对驱动层做了介绍,驱动层的大多数函数都给出了分析。
Framebuffer子系统【转】
标签:创建 比较 地址 locking ftp cup address 像素 off
原文地址:http://www.cnblogs.com/zzb-Dream-90Time/p/7301057.html