还是回到probe函数中。
838 if (mach_info->default_display >= mach_info->num_displays) {
839 dev_err(&pdev->dev, "default is %d but only %d displays\n",
840 mach_info->default_display, mach_info->num_displays);
841 return -EINVAL;
842 }
获取这个平台设备数据之后呢,首先判断默认的显示配置号是否超出显示配置的数目,如果超出,说明默认的显示配置有错误,打印错误信息,返回一个错误值。
844 display = mach_info->displays + mach_info->default_display;
然后呢,设置当前的现实配置为mach_info中的默认显示配置。
846 irq = platform_get_irq(pdev, 0);
847 if (irq < 0) {
848 dev_err(&pdev->dev, "no irq for device\n");
849 return -ENOENT;
850 }
获取平台设备中定义的中断号。
852 fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
853 if (!fbinfo)
854 return -ENOMEM;
linux驱动呢它都有一个固定的流程,只要你掌握了这个流程,那么这个驱动就好写了。在linux中,帧缓冲设备采用struct fb_info结构来描述,当然首先要为这个结构申请内存空间,为struct fb_info申请内存空间的函数就是这里的 framebuffer_alloc。进去看看这个函数。
/* drivers/video/fbsysfs.c */
25 /**
26 * framebuffer_alloc - creates a new frame buffer info structure
27 *
28 * @size: size of driver private data, can be zero
29 * @dev: pointer to the device for this fb, this can be NULL
30 *
31 * Creates a new frame buffer info structure. Also reserves @size bytes
32 * for driver private data (info->par). info->par (if any) will be
33 * aligned to sizeof(long).
34 *
35 * Returns the new structure, or NULL if an error occured.
36 *
37 */
38 struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
39 {
40 #define BYTES_PER_LONG (BITS_PER_LONG/8)
41 #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
42 int fb_info_size = sizeof(struct fb_info);
43 struct fb_info *info;
44 char *p;
45
46 if (size)
47 fb_info_size += PADDING;
48
49 p = kzalloc(fb_info_size + size, GFP_KERNEL);
50
51 if (!p)
52 return NULL;
53
54 info = (struct fb_info *) p;
55
56 if (size)
57 info->par = p + fb_info_size;
58
59 info->device = dev;
60
61 #ifdef CONFIG_FB_BACKLIGHT
62 mutex_init(&info->bl_curve_mutex);
63 #endif
64
65 return info;
66 #undef PADDING
67 #undef BYTES_PER_LONG
68 }
69 EXPORT_SYMBOL(framebuffer_alloc);
申请的内存空间大小除了sizeof(struct fb_info)以外,还包括传进来的sizeof(struct s3c2410fb_info)大小。指定帧缓冲的父设备为平台设备中的device。这里接触了两个结构体类型,struct s3c2410fb_info和struct fb_info,先看看这两个结构体类型的定义。
/* drivers/video/s3c2410fb.h */
21 struct s3c2410fb_info {
22 struct device *dev;
23 struct clk *clk;
24
25 struct resource *mem;
26 void __iomem *io;
27 void __iomem *irq_base;
28
29 enum s3c_drv_type drv_type;
30 struct s3c2410fb_hw regs;
31
32 unsigned long clk_rate;
33 unsigned int palette_ready;
34
35 #ifdef CONFIG_CPU_FREQ
36 struct notifier_block freq_transition;
37 #endif
38
39 /* keep these registers in case we need to re-write palette */
40 u32 palette_buffer[256];
41 u32 pseudo_pal[16];
42 };
struct s3c2410fb_info肯定和我们这里的s3c2440有关。
813 struct fb_info {
814 int node;
815 int flags;
816 struct mutex lock; /* Lock for open/release/ioctl funcs */
817 struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
818 struct fb_var_screeninfo var; /* Current var */
819 struct fb_fix_screeninfo fix; /* Current fix */
820 struct fb_monspecs monspecs; /* Current Monitor specs */
821 struct work_struct queue; /* Framebuffer event queue */
822 struct fb_pixmap pixmap; /* Image hardware mapper */
823 struct fb_pixmap sprite; /* Cursor hardware mapper */
824 struct fb_cmap cmap; /* Current cmap */
825 struct list_head modelist; /* mode list */
826 struct fb_videomode *mode; /* current mode */
827
828 #ifdef CONFIG_FB_BACKLIGHT
829 /* assigned backlight device */
830 /* set before framebuffer registration,
831 remove after unregister */
832 struct backlight_device *bl_dev;
833
834 /* Backlight level curve */
835 struct mutex bl_curve_mutex;
836 u8 bl_curve[FB_BACKLIGHT_LEVELS];
837 #endif
838 #ifdef CONFIG_FB_DEFERRED_IO
839 struct delayed_work deferred_work;
840 struct fb_deferred_io *fbdefio;
841 #endif
842
843 struct fb_ops *fbops;
844 struct device *device; /* This is the parent */
845 struct device *dev; /* This is this fb device */
846 int class_flag; /* private sysfs flags */
847 #ifdef CONFIG_FB_TILEBLITTING
848 struct fb_tile_ops *tileops; /* Tile Blitting */
849 #endif
850 char __iomem *screen_base; /* Virtual address */
851 unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
852 void *pseudo_palette; /* Fake palette of 16 colors */
853 #define FBINFO_STATE_RUNNING 0
854 #define FBINFO_STATE_SUSPENDED 1
855 u32 state; /* Hardware state i.e suspend */
856 void *fbcon_par; /* fbcon use-only private area */
857 /* From here on everything is device dependent */
858 void *par;
859 /* we need the PCI or similiar aperture base/size not
860 smem_start/size as smem_start may just be an object
861 allocated inside the aperture so may not actually overlap */
862 resource_size_t aperture_base;
863 resource_size_t aperture_size;
864 };
我们说了,帧缓冲设备就用struct fb_info这个结构来描述,那么自然这个结构在帧缓冲的驱动中就比较重要了。
还是回到probe函数中。
858 info = fbinfo->par;
859 info->dev = &pdev->dev;
860 info->drv_type = drv_type;
指定struct s3c2410fb_info中dev为平台设备中的dev。
862 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
863 if (res == NULL) {
864 dev_err(&pdev->dev, "failed to get memory registers\n");
865 ret = -ENXIO;
866 goto dealloc_fb;
867 }
获取平台设备中定义的IO资源。
869 size = (res->end - res->start) + 1;
870 info->mem = request_mem_region(res->start, size, pdev->name);
871 if (info->mem == NULL) {
872 dev_err(&pdev->dev, "failed to get memory region\n");
873 ret = -ENOENT;
874 goto dealloc_fb;
875 }
IO内存申请。
877 info->io = ioremap(res->start, size);
878 if (info->io == NULL) {
879 dev_err(&pdev->dev, "ioremap() of registers failed\n");
880 ret = -ENXIO;
881 goto release_mem;
882 }
IO内存映射。
884 info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
获取LCD的中断寄存器基地址。
888 strcpy(fbinfo->fix.id, driver_name);
得到我们的driver_name,这里为s3c2410fb。
890 /* Stop the video */
891 lcdcon1 = readl(info->io + S3C2410_LCDCON1);
892 writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
禁止LCD的数据输出。
894 fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
895 fbinfo->fix.type_aux = 0;
896 fbinfo->fix.xpanstep = 0;
897 fbinfo->fix.ypanstep = 0;
898 fbinfo->fix.ywrapstep = 0;
899 fbinfo->fix.accel = FB_ACCEL_NONE;
900
901 fbinfo->var.nonstd = 0;
902 fbinfo->var.activate = FB_ACTIVATE_NOW;
903 fbinfo->var.accel_flags = 0;
904 fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
905
906 fbinfo->fbops = &s3c2410fb_ops;
907 fbinfo->flags = FBINFO_FLAG_DEFAULT;
908 fbinfo->pseudo_palette = &info->pseudo_pal;
然后就是一堆对strcut fb_info结构的赋值。这里面对我们最重要的一个赋值就是s3c2410fb_ops。
910 for (i = 0; i < 256; i++)
911 info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
清空调色板。
913 ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
914 if (ret) {
915 dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
916 ret = -EBUSY;
917 goto release_regs;
918 }
注册中断,中断处理函数为s3c2410fb_irq。
920 info->clk = clk_get(NULL, "lcd");
921 if (!info->clk || IS_ERR(info->clk)) {
922 printk(KERN_ERR "failed to get lcd clock source\n");
923 ret = -ENOENT;
924 goto release_irq;
925 }
926
927 clk_enable(info->clk);
获取lcd的时钟控制,然后使能lcd控制器时钟。
932 info->clk_rate = clk_get_rate(info->clk);
获取lcd控制器的时钟频率,时钟为HCLK,也就是它的时钟频率为100MHz。
934 /* find maximum required memory size for display */
935 for (i = 0; i < mach_info->num_displays; i++) {
936 unsigned long smem_len = mach_info->displays[i].xres;
937
938 smem_len *= mach_info->displays[i].yres;
939 smem_len *= mach_info->displays[i].bpp;
940 smem_len >>= 3;
941 if (fbinfo->fix.smem_len < smem_len)
942 fbinfo->fix.smem_len = smem_len;
943 }
计算出lcd的显存大小,显存大小为width * height * bpp,即刚好一帧大小空间,前面计算出来的是多少bit,所以还要左移3位,计算出显存为多少字节。显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。
945 /* Initialize video memory */
946 ret = s3c2410fb_map_video_memory(fbinfo);
947 if (ret) {
948 printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
949 ret = -ENOMEM;
950 goto release_clock;
951 }
为显存分配空间。
955 fbinfo->var.xres = display->xres;
956 fbinfo->var.yres = display->yres;
957 fbinfo->var.bits_per_pixel = display->bpp;
把平台设备中定义的显示配置赋值给帧缓冲设备。
959 s3c2410fb_init_registers(fbinfo);
初始化s3c2440寄存器,去看看这个函数。
677 /*
678 * s3c2410fb_init_registers - Initialise all LCD-related registers
679 */
680 static int s3c2410fb_init_registers(struct fb_info *info)
681 {
682 struct s3c2410fb_info *fbi = info->par;
683 struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
684 unsigned long flags;
685 void __iomem *regs = fbi->io;
686 void __iomem *tpal;
687 void __iomem *lpcsel;
688
689 if (is_s3c2412(fbi)) {
690 tpal = regs + S3C2412_TPAL;
691 lpcsel = regs + S3C2412_TCONSEL;
692 } else {
693 tpal = regs + S3C2410_TPAL;
694 lpcsel = regs + S3C2410_LPCSEL;
695 }
696
697 /* Initialise LCD with values from haret */
698
699 local_irq_save(flags);
700
701 /* modify the gpio(s) with interrupts set (bjd) */
702
703 modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
704 modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
705 modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
706 modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
707
708 local_irq_restore(flags);
709
710 dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel);
711 writel(mach_info->lpcsel, lpcsel);
712
713 dprintk("replacing TPAL %08x\n", readl(tpal));
714
715 /* ensure temporary palette disabled */
716 writel(0x00, tpal);
717
718 return 0;
719 }
我们看就是将mach_info中定义的gpio口数据写入寄存器中,现在知道前面为什么要定义gpio口了吧,所以说一切都是有原因的。
969 ret = register_framebuffer(fbinfo);
970 if (ret < 0) {
971 printk(KERN_ERR "Failed to register framebuffer device: %d\n",
972 ret);
973 goto free_cpufreq;
974 }
注册帧缓冲设备。对于帧缓冲设备流程就是这样的,描述帧缓冲设备采用struct fb_info,申请空间采用framebuffer_alloc,注册呢就采用这里的函数register_framebuffer。
probe函数就这样也就结束了。