linux lcd驱动分析三

还是回到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函数就这样也就结束了。


你可能感兴趣的:(linux lcd驱动分析三)