S3C2410驱动分析之LCD驱动

作者:刘昊昱 

博客:http://blog.csdn.net/liuhaoyutz

内核版本:2.6.36

源码路径:drivers/video/s3c2410fb.c

 
本文分析S3C2410的LCD驱动,该驱动程序基于Framebuffer机制。
 
一、相关数据结构
首先,我们来介绍一下基于Framebuffer的S3C2410 LCD驱动涉及的几个重要数据结构:
Framebuffer的核心数据结构是fb_info,该结构体定义在include/linux/fb.h文件中:
 832struct fb_info {
 833    int node;
 834    int flags;
 835    struct mutex lock;      /* Lock for open/release/ioctl funcs */
 836    struct mutex mm_lock;       /* Lock for fb_mmap and smem_* fields */
 837    struct fb_var_screeninfo var;   /* Current var */
 838    struct fb_fix_screeninfo fix;   /* Current fix */
 839    struct fb_monspecs monspecs;    /* Current Monitor specs */
 840    struct work_struct queue;   /* Framebuffer event queue */
 841    struct fb_pixmap pixmap;    /* Image hardware mapper */
 842    struct fb_pixmap sprite;    /* Cursor hardware mapper */
 843    struct fb_cmap cmap;        /* Current cmap */
 844    struct list_head modelist;      /* mode list */
 845    struct fb_videomode *mode;  /* current mode */
 846
 847#ifdef CONFIG_FB_BACKLIGHT
 848    /* assigned backlight device */
 849    /* set before framebuffer registration,
 850       remove after unregister */
 851    struct backlight_device *bl_dev;
 852
 853    /* Backlight level curve */
 854    struct mutex bl_curve_mutex;
 855    u8 bl_curve[FB_BACKLIGHT_LEVELS];
 856#endif
 857#ifdef CONFIG_FB_DEFERRED_IO
 858    struct delayed_work deferred_work;
 859    struct fb_deferred_io *fbdefio;
 860#endif
 861
 862    struct fb_ops *fbops;
 863    struct device *device;      /* This is the parent */
 864    struct device *dev;     /* This is this fb device */
 865    int class_flag;                    /* private sysfs flags */
 866#ifdef CONFIG_FB_TILEBLITTING
 867    struct fb_tile_ops *tileops;    /* Tile Blitting */
 868#endif
 869    char __iomem *screen_base;  /* Virtual address */
 870    unsigned long screen_size;  /* Amount of ioremapped VRAM or 0 */
 871    void *pseudo_palette;       /* Fake palette of 16 colors */
 872#define FBINFO_STATE_RUNNING    0
 873#define FBINFO_STATE_SUSPENDED  1
 874    u32 state;          /* Hardware state i.e suspend */
 875    void *fbcon_par;                /* fbcon use-only private area */
 876    /* From here on everything is device dependent */
 877    void *par;
 878    /* we need the PCI or similiar aperture base/size not
 879       smem_start/size as smem_start may just be an object
 880       allocated inside the aperture so may not actually overlap */
 881    struct apertures_struct {
 882        unsigned int count;
 883        struct aperture {
 884            resource_size_t base;
 885            resource_size_t size;
 886        } ranges[0];
 887    } *apertures;
 888};

fb_info结构体中,最重要的三个成员是var,fix,fbops,分别代表LCD可变参数,固定参数和对底层硬件的操作函数集。
837行,fb_var_screeninfo结构体变量var代表LCD的可变参数。该结构体定义如下:
 238struct fb_var_screeninfo {
 239    __u32 xres;         /* visible resolution       */
 240    __u32 yres;
 241    __u32 xres_virtual;     /* virtual resolution       */
 242    __u32 yres_virtual;
 243    __u32 xoffset;          /* offset from virtual to visible */
 244    __u32 yoffset;          /* resolution           */
 245
 246    __u32 bits_per_pixel;       /* guess what           */
 247    __u32 grayscale;        /* != 0 Graylevels instead of colors */
 248
 249    struct fb_bitfield red;     /* bitfield in fb mem if true color, */
 250    struct fb_bitfield green;   /* else only length is significant */
 251    struct fb_bitfield blue;
 252    struct fb_bitfield transp;  /* transparency         */
 253
 254    __u32 nonstd;           /* != 0 Non standard pixel format */
 255
 256    __u32 activate;         /* see FB_ACTIVATE_*        */
 257
 258    __u32 height;           /* height of picture in mm    */
 259    __u32 width;            /* width of picture in mm     */
 260
 261    __u32 accel_flags;      /* (OBSOLETE) see fb_info.flags */
 262
 263    /* Timing: All values in pixclocks, except pixclock (of course) */
 264    __u32 pixclock;         /* pixel clock in ps (pico seconds) */
 265    __u32 left_margin;      /* time from sync to picture    */
 266    __u32 right_margin;     /* time from picture to sync    */
 267    __u32 upper_margin;     /* time from sync to picture    */
 268    __u32 lower_margin;
 269    __u32 hsync_len;        /* length of horizontal sync    */
 270    __u32 vsync_len;        /* length of vertical sync  */
 271    __u32 sync;         /* see FB_SYNC_*        */
 272    __u32 vmode;            /* see FB_VMODE_*       */
 273    __u32 rotate;           /* angle we rotate counter clockwise */
 274    __u32 reserved[5];      /* Reserved for future compatibility */
 275};

838行,fb_fix_screeninfo结构体变量fix代表LCD的固定参数,该结构体定义如下:
 155struct fb_fix_screeninfo {
 156    char id[16];            /* identification string eg "TT Builtin" */
 157    unsigned long smem_start;   /* Start of frame buffer mem */
 158                    /* (physical address) */
 159    __u32 smem_len;         /* Length of frame buffer mem */
 160    __u32 type;         /* see FB_TYPE_*        */
 161    __u32 type_aux;         /* Interleave for interleaved Planes */
 162    __u32 visual;           /* see FB_VISUAL_*      */
 163    __u16 xpanstep;         /* zero if no hardware panning  */
 164    __u16 ypanstep;         /* zero if no hardware panning  */
 165    __u16 ywrapstep;        /* zero if no hardware ywrap    */
 166    __u32 line_length;      /* length of a line in bytes    */
 167    unsigned long mmio_start;   /* Start of Memory Mapped I/O   */
 168                    /* (physical address) */
 169    __u32 mmio_len;         /* Length of Memory Mapped I/O  */
 170    __u32 accel;            /* Indicate to driver which */
 171                    /*  specific chip/card we have  */
 172    __u16 reserved[3];      /* Reserved for future compatibility */
 173};

862行,fb_ops结构体变量fbops代表对底层硬件操作的函数集,该结构体定义如下:
 621struct fb_ops {
 622    /* open/release and usage marking */
 623    struct module *owner;
 624    int (*fb_open)(struct fb_info *info, int user);
 625    int (*fb_release)(struct fb_info *info, int user);
 626
 627    /* For framebuffers with strange non linear layouts or that do not
 628     * work with normal memory mapped access
 629     */
 630    ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
 631               size_t count, loff_t *ppos);
 632    ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
 633                size_t count, loff_t *ppos);
 634
 635    /* checks var and eventually tweaks it to something supported,
 636     * DO NOT MODIFY PAR */
 637    int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
 638
 639    /* set the video mode according to info->var */
 640    int (*fb_set_par)(struct fb_info *info);
 641
 642    /* set color register */
 643    int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
 644                unsigned blue, unsigned transp, struct fb_info *info);
 645
 646    /* set color registers in batch */
 647    int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
 648
 649    /* blank display */
 650    int (*fb_blank)(int blank, struct fb_info *info);
 651
 652    /* pan display */
 653    int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
 654
 655    /* Draws a rectangle */
 656    void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
 657    /* Copy data from area to another */
 658    void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
 659    /* Draws a image to the display */
 660    void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
 661
 662    /* Draws cursor */
 663    int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
 664
 665    /* Rotates the display */
 666    void (*fb_rotate)(struct fb_info *info, int angle);
 667
 668    /* wait for blit idle, optional */
 669    int (*fb_sync)(struct fb_info *info);
 670
 671    /* perform fb specific ioctl (optional) */
 672    int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
 673            unsigned long arg);
 674
 675    /* Handle 32bit compat ioctl (optional) */
 676    int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
 677            unsigned long arg);
 678
 679    /* perform fb specific mmap */
 680    int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
 681
 682    /* get capability given var */
 683    void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
 684                struct fb_var_screeninfo *var);
 685
 686    /* teardown any resources to do with this framebuffer */
 687    void (*fb_destroy)(struct fb_info *info);
 688
 689    /* called at KDB enter and leave time to prepare the console */
 690    int (*fb_debug_enter)(struct fb_info *info);
 691    int (*fb_debug_leave)(struct fb_info *info);
 692};

S3C2410驱动还定义了几个专用结构体:
在drivers/video/s3c2410fb.h文件中定义了s3c2410fb_info结构体:
21struct 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};

在arch/arm/mach-s3c2410/include/mach/fb.h文件中定义了s3c2410fb_display和s3c2410fb_mach_info结构体:
25/* LCD description */
26struct s3c2410fb_display {
27    /* LCD type */
28    unsigned type;
29
30    /* Screen size */
31    unsigned short width;
32    unsigned short height;
33
34    /* Screen info */
35    unsigned short xres;
36    unsigned short yres;
37    unsigned short bpp;
38
39    unsigned pixclock;      /* pixclock in picoseconds */
40    unsigned short left_margin;  /* value in pixels (TFT) or HCLKs (STN) */
41    unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */
42    unsigned short hsync_len;    /* value in pixels (TFT) or HCLKs (STN) */
43    unsigned short upper_margin;    /* value in lines (TFT) or 0 (STN) */
44    unsigned short lower_margin;    /* value in lines (TFT) or 0 (STN) */
45    unsigned short vsync_len;   /* value in lines (TFT) or 0 (STN) */
46
47    /* lcd configuration registers */
48    unsigned long   lcdcon5;
49};
50
51struct s3c2410fb_mach_info {
52
53    struct s3c2410fb_display *displays; /* attached diplays info */
54    unsigned num_displays;          /* number of defined displays */
55    unsigned default_display;
56
57    /* GPIOs */
58
59    unsigned long   gpcup;
60    unsigned long   gpcup_mask;
61    unsigned long   gpccon;
62    unsigned long   gpccon_mask;
63    unsigned long   gpdup;
64    unsigned long   gpdup_mask;
65    unsigned long   gpdcon;
66    unsigned long   gpdcon_mask;
67
68    /* lpc3600 control register */
69    unsigned long   lpcsel;
70};

 
二、platform_driver probe函数分析
下面我们来看模块初始化函数s3c2410fb_init:
1119int __init s3c2410fb_init(void)
1120{
1121    int ret = platform_driver_register(&s3c2410fb_driver);
1122
1123    if (ret == 0)
1124        ret = platform_driver_register(&s3c2412fb_driver);
1125
1126    return ret;
1127}

我们只关注S3C2410,1121行,注册platform_drivers 3c2410fb_driver,定义如下:
1097static struct platform_driver s3c2410fb_driver = {
1098    .probe      = s3c2410fb_probe,
1099    .remove     = __devexit_p(s3c2410fb_remove),
1100    .suspend    = s3c2410fb_suspend,
1101    .resume     = s3c2410fb_resume,
1102    .driver     = {
1103        .name   = "s3c2410-lcd",
1104        .owner  = THIS_MODULE,
1105    },
1106};

当模块被装载时,会调用s3c2410fb_probe函数,该函数定义如下:
1007static int __devinit s3c2410fb_probe(struct platform_device *pdev)
1008{
1009    return s3c24xxfb_probe(pdev, DRV_S3C2410);
1010}

实际上s3c2410fb_probe直接调用了s3c24xxfb_probe,并传递参数DVR_S3C2410,表明是S3C2410平台。下面看s3c24xxfb_probe函数:
 817static int __devinit s3c24xxfb_probe(struct platform_device *pdev,
 818                  enum s3c_drv_type drv_type)
 819{
 820    struct s3c2410fb_info *info;
 821    struct s3c2410fb_display *display;
 822    struct fb_info *fbinfo;
 823    struct s3c2410fb_mach_info *mach_info;
 824    struct resource *res;
 825    int ret;
 826    int irq;
 827    int i;
 828    int size;
 829    u32 lcdcon1;
 830
 831    mach_info = pdev->dev.platform_data;
 832    if (mach_info == NULL) {
 833        dev_err(&pdev->dev,
 834            "no platform data for lcd, cannot attach\n");
 835        return -EINVAL;
 836    }
 837
 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    }
 843
 844    display = mach_info->displays + mach_info->default_display;
 845
 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    }
 851
 852    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
 853    if (!fbinfo)
 854        return -ENOMEM;
 855
 856    platform_set_drvdata(pdev, fbinfo);
 857
 858    info = fbinfo->par;
 859    info->dev = &pdev->dev;
 860    info->drv_type = drv_type;
 861
 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    }
 868
 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    }
 876
 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    }
 883
 884    info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
 885
 886    dprintk("devinit\n");
 887
 888    strcpy(fbinfo->fix.id, driver_name);
 889
 890    /* Stop the video */
 891    lcdcon1 = readl(info->io + S3C2410_LCDCON1);
 892    writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
 893
 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;
 909
 910    for (i = 0; i < 256; i++)
 911        info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
 912
 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    }
 919
 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);
 928    dprintk("got and enabled clock\n");
 929
 930    msleep(1);
 931
 932    info->clk_rate = clk_get_rate(info->clk);
 933
 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    }
 944
 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    }
 952
 953    dprintk("got video memory\n");
 954
 955    fbinfo->var.xres = display->xres;
 956    fbinfo->var.yres = display->yres;
 957    fbinfo->var.bits_per_pixel = display->bpp;
 958
 959    s3c2410fb_init_registers(fbinfo);
 960
 961    s3c2410fb_check_var(&fbinfo->var, fbinfo);
 962
 963    ret = s3c2410fb_cpufreq_register(info);
 964    if (ret < 0) {
 965        dev_err(&pdev->dev, "Failed to register cpufreq\n");
 966        goto free_video_memory;
 967    }
 968
 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    }
 975
 976    /* create device files */
 977    ret = device_create_file(&pdev->dev, &dev_attr_debug);
 978    if (ret) {
 979        printk(KERN_ERR "failed to add debug attribute\n");
 980    }
 981
 982    printk(KERN_INFO "fb%d: %s frame buffer device\n",
 983        fbinfo->node, fbinfo->fix.id);
 984
 985    return 0;
 986
 987 free_cpufreq:
 988    s3c2410fb_cpufreq_deregister(info);
 989free_video_memory:
 990    s3c2410fb_unmap_video_memory(fbinfo);
 991release_clock:
 992    clk_disable(info->clk);
 993    clk_put(info->clk);
 994release_irq:
 995    free_irq(irq, info);
 996release_regs:
 997    iounmap(info->io);
 998release_mem:
 999    release_resource(info->mem);
1000    kfree(info->mem);
1001dealloc_fb:
1002    platform_set_drvdata(pdev, NULL);
1003    framebuffer_release(fbinfo);
1004    return ret;
1005}

820行,定义了s3c2410fb_info结构体指针变量info。
821行,定义了s3c2410fb_display结构体指针变量display。
822行,定义了fb_info结构体指针变量fbinfo。
823行,定义了s3c2410fb_mach_info结构体指针变量mach_info。
831行,由platform数据取得mach_info。
844行,由match_info得到display。
846行,取得中断号。
852行,调用framebuffer_alloc创建fb_info结构体变量fbinfo。fb_info结构体体是LCD驱动的核心数据结构。注意传递给framebuffer_alloc函数的第一个参数是sizeof(struct s3c2410fb_info),即s3c2410fb_info结构体的大小,通过分析framebuffer_alloc函数的源码,可知,该函数除了为fb_info分配的内存空间,还多分配了第二个参数指定大小的空间,并将fb_info.par指定多分配出来的这个空间。
856行,将fbinfo保存在驱动私有数据中,以方便以后使用。
858行,将fbinfo->par指向的s3c2410fb_info结构体空间赋值给info变量。
862行,取得LCD控制器的I/O内存资源信息。
870行,调用request_mem_region取得I/O内存,这个I/O内存对应LCD控制器的寄存器集,而不是Framebuffer。
877行,通过ioremap 函数得到I/O内存的虚拟地址空间,起始地址保存在fbinfo->io中。
884行,取得LCD Interrupt Pending Register寄存器的地址保存在info->irq_base中。
891 - 892行,通过将LCDCON1寄存器的第0位设置为0,禁用LCD。
894 - 908行,初始化fbinfo。其中,特别需要注意的是906行,设置fbinfo->fbops为3c2410fb_ops
910 - 911行,将info->palette_buffer清0,info->palette_buffer对应S3C2410内部的256*16的调色板内存。
913行,申请中断,设置中断处理函数为s3c2410fb_irq。
920行,取得LCD时钟。
927行,使能LCD时钟。
932行,取得LCD时钟频率。
935 - 943行,计算需要的最大显存大小。
946行,调用s3c2410fb_map_video_memory函数申请以DMA方式访问的帧缓冲区(Framebuffer)空间。这个函数我们在后面再仔细分析。
959行,调用s3c2410fb_init_registers函数初始化LCD控制器的各个寄存器,这个函数我们在后面再仔细分析。
961行,调用s3c2410fb_check_var函数检查相关变量是否符合要求。这是framebuffer驱动要求必须进行的一个检查。这个函数我们在后面再仔细分析。
963行,s3c2410fb_cpufreq_register这个函数实现变频功能,涉及Linux cupfreq(变频)机制。根据宏CONFIG_CPU_FREQ是否被定义,可以直接返回0,也可以具体实现变频功能。
969行,调用register_framebuffer注册fb_info结构。
977行,调用device_create_file创建/sys系统文件。
s3c2410fb_map_video_memory函数定义如下:
 626/*
 627 * s3c2410fb_map_video_memory():
 628 *  Allocates the DRAM memory for the frame buffer.  This buffer is
 629 *  remapped into a non-cached, non-buffered, memory region to
 630 *  allow palette and pixel writes to occur without flushing the
 631 *  cache.  Once this area is remapped, all virtual memory
 632 *  access to the video memory should occur at the new region.
 633 */
 634837837static int __devinit s3c2410fb_map_video_memory(struct fb_info *info)
 635{
 636    struct s3c2410fb_info *fbi = info->par;
 637    dma_addr_t map_dma;
 638    unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
 639
 640    dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
 641
 642    info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
 643                           &map_dma, GFP_KERNEL);
 644
 645    if (info->screen_base) {
 646        /* prevent initial garbage on screen */
 647        dprintk("map_video_memory: clear %p:%08x\n",
 648            info->screen_base, map_size);
 649        memset(info->screen_base, 0x00, map_size);
 650
 651        info->fix.smem_start = map_dma;
 652
 653        dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
 654            info->fix.smem_start, info->screen_base, map_size);
 655    }
 656
 657    return info->screen_base ? 0 : -ENOMEM;
 658}

s3c2410fb_map_video_memory函数的作用是申请以DMA方式访问的帧缓冲区(Framebuffer)地址空间。
638行,从fb_info.fb_fix_screeninfo.smem_len的注释"Length of frame buffer mem"可以看出,这里是取得以页对齐的帧缓冲区的大小保存在map_size变量中。
642行,从fb_info->screen_base的注释"Virtual address"可以看出,这里是通过调用dma_alloc_writecombine函数,分配以DMA方式访问的帧缓冲区空间(Framebuffer),并将帧缓冲区的虚拟地址起始值返回保存在fb_info->screen_base中。
649行,将帧缓冲区清0。
651行,从fb_info.fb_fix_screeninfo.smem_start的注释"Start of frame buffer mem (physical address)"可以看出,这里是把帧缓冲区的物理地址起始地址map_dma赋值给info->fix.smem_start。
下面看s3c2410fb_init_registers函数,该函数用于初始化LCD控制器:
 677/*
 678 * s3c2410fb_init_registers - Initialise all LCD-related registers
 679 */
 680static 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}

685行,取得LCD控制器寄存器虚拟地址的起始地址,保存在regs变量中。
693行,取得临时调色板寄存器(Temp Palette Register)的虚拟地址保存在tpal变量中。
694行,取得LPCSEL寄存器的地址,该寄存器用于控制中是否启动LPC3600模式。LPC3600是专用于三星LTS350Q1-PD1/2液晶屏的控制器。
699行,修改寄存器的值之前,先调用local_irq_save屏蔽中断,并将中断状态保存到flags变量中。
703 - 706行,调用modify_gpio函数设置GPC和GPD寄存器为LCD模式。
modify_gpio函数用于设置GPIO寄存器,注意第三个参数mask的作用是把要设置的位先清0。
668static inline void modify_gpio(void __iomem *reg,
669                   unsigned long set, unsigned long mask)
670{
671    unsigned long tmp;
672
673    tmp = readl(reg) & ~mask;
674    writel(tmp | set, reg);
675}

708行,调用local_irq_restore恢复被屏蔽的中断。
711行,设置LPCSEL寄存器。
716行,清0并禁用临时调色板寄存器。
下面我们来看s3c2410fb_check_var函数,该函数用于对LCD可变参数进行检查,其定义如下:
 109/*
 110 *  s3c2410fb_check_var():
 111 *  Get the video params out of 'var'. If a value doesn't fit, round it up,
 112 *  if it's too big, return -EINVAL.
 113 *
 114 */
 115static int s3c2410fb_check_var(struct fb_var_screeninfo *var,
 116                   struct fb_info *info)
 117{
 118    struct s3c2410fb_info *fbi = info->par;
 119    struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
 120    struct s3c2410fb_display *display = NULL;
 121    struct s3c2410fb_display *default_display = mach_info->displays +
 122                            mach_info->default_display;
 123    int type = default_display->type;
 124    unsigned i;
 125
 126    dprintk("check_var(var=%p, info=%p)\n", var, info);
 127
 128    /* validate x/y resolution */
 129    /* choose default mode if possible */
 130    if (var->yres == default_display->yres &&
 131        var->xres == default_display->xres &&
 132        var->bits_per_pixel == default_display->bpp)
 133        display = default_display;
 134    else
 135        for (i = 0; i < mach_info->num_displays; i++)
 136            if (type == mach_info->displays[i].type &&
 137                var->yres == mach_info->displays[i].yres &&
 138                var->xres == mach_info->displays[i].xres &&
 139                var->bits_per_pixel == mach_info->displays[i].bpp) {
 140                display = mach_info->displays + i;
 141                break;
 142            }
 143
 144    if (!display) {
 145        dprintk("wrong resolution or depth %dx%d at %d bpp\n",
 146            var->xres, var->yres, var->bits_per_pixel);
 147        return -EINVAL;
 148    }
 149
 150    /* it is always the size as the display */
 151    var->xres_virtual = display->xres;
 152    var->yres_virtual = display->yres;
 153    var->height = display->height;
 154    var->width = display->width;
 155
 156    /* copy lcd settings */
 157    var->pixclock = display->pixclock;
 158    var->left_margin = display->left_margin;
 159    var->right_margin = display->right_margin;
 160    var->upper_margin = display->upper_margin;
 161    var->lower_margin = display->lower_margin;
 162    var->vsync_len = display->vsync_len;
 163    var->hsync_len = display->hsync_len;
 164
 165    fbi->regs.lcdcon5 = display->lcdcon5;
 166    /* set display type */
 167    fbi->regs.lcdcon1 = display->type;
 168
 169    var->transp.offset = 0;
 170    var->transp.length = 0;
 171    /* set r/g/b positions */
 172    switch (var->bits_per_pixel) {
 173    case 1:
 174    case 2:
 175    case 4:
 176        var->red.offset = 0;
 177        var->red.length = var->bits_per_pixel;
 178        var->green  = var->red;
 179        var->blue   = var->red;
 180        break;
 181    case 8:
 182        if (display->type != S3C2410_LCDCON1_TFT) {
 183            /* 8 bpp 332 */
 184            var->red.length     = 3;
 185            var->red.offset     = 5;
 186            var->green.length   = 3;
 187            var->green.offset   = 2;
 188            var->blue.length    = 2;
 189            var->blue.offset    = 0;
 190        } else {
 191            var->red.offset     = 0;
 192            var->red.length     = 8;
 193            var->green      = var->red;
 194            var->blue       = var->red;
 195        }
 196        break;
 197    case 12:
 198        /* 12 bpp 444 */
 199        var->red.length     = 4;
 200        var->red.offset     = 8;
 201        var->green.length   = 4;
 202        var->green.offset   = 4;
 203        var->blue.length    = 4;
 204        var->blue.offset    = 0;
 205        break;
 206
 207    default:
 208    case 16:
 209        if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) {
 210            /* 16 bpp, 565 format */
 211            var->red.offset     = 11;
 212            var->green.offset   = 5;
 213            var->blue.offset    = 0;
 214            var->red.length     = 5;
 215            var->green.length   = 6;
 216            var->blue.length    = 5;
 217        } else {
 218            /* 16 bpp, 5551 format */
 219            var->red.offset     = 11;
 220            var->green.offset   = 6;
 221            var->blue.offset    = 1;
 222            var->red.length     = 5;
 223            var->green.length   = 5;
 224            var->blue.length    = 5;
 225        }
 226        break;
 227    case 32:
 228        /* 24 bpp 888 and 8 dummy */
 229        var->red.length     = 8;
 230        var->red.offset     = 16;
 231        var->green.length   = 8;
 232        var->green.offset   = 8;
 233        var->blue.length    = 8;
 234        var->blue.offset    = 0;
 235        break;
 236    }
 237    return 0;
 238}

128 - 148行,验证x/y解析度。如果没有正好匹配的设置,则返回错误。
151 - 154行,设置屏幕的虚拟解析像素和高度宽度。
157 - 163行,设置时钟像素,行、帧切换值,水平同步和垂直同步长度值。
165行,配置LCDCON5寄存器。
167行,通过LCDCON1配置LCD类型。
169 - 170行,设置透明度。
172 - 236行,根据色位模式(BPP)来设置可变参数中R、G、B的颜色位域。
s3c24xxfb_probe函数的第963行,调用s3c2410fb_cpufreq_register函数实现变频功能。根据宏CONFIG_CPU_FREQ是否被定义,可以直接返回0,也可以具体实现变频功能。
下面我们要分析的中LCD中断处理函数s3c2410fb_irq:
 747static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
 748{
 749    struct s3c2410fb_info *fbi = dev_id;
 750    void __iomem *irq_base = fbi->irq_base;
 751    unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
 752
 753    if (lcdirq & S3C2410_LCDINT_FRSYNC) {
 754        if (fbi->palette_ready)
 755            s3c2410fb_write_palette(fbi);
 756
 757        writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);
 758        writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);
 759    }
 760
 761    return IRQ_HANDLED;
 762}

749行,注意s3c2410fb_info实例以dev_id参数的形式传递进来,注意理解dev_id的用法。
751行,读取LCDINTPND寄存器(LCD Interrupt Pending Register)的值到lcdirq中。
753行,如果LCDINTPND寄存器的第1位为1,说明LCD触发了中断。
754行,如果fbi->palette_ready等于1,表明需要更新fbi->palette_buffer中的调色板信息到LCD控制器的调色板内存区。相反,如果fbi->palette_ready等于0,则表明不需要更新调色板内存区。
755行,调用s3c2410fb_write_palette函数将fbi->palette_buffer中的信息写入LCD控制器的调色板内存空间中。
757行,设置LCDINTPND寄存器,表明已插入中断请求,这里不理解经过了753行的判断,应该已经设置好了,这里为什么再设置一遍?
758行,设置LCDSRCPND寄存器,表明已插入中断请求。
s3c2410fb_write_palette函数用于填充调色板,其定义如下:
 721static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
 722{
 723    unsigned int i;
 724    void __iomem *regs = fbi->io;
 725
 726    fbi->palette_ready = 0;
 727
 728    for (i = 0; i < 256; i++) {
 729        unsigned long ent = fbi->palette_buffer[i];
 730        if (ent == PALETTE_BUFF_CLEAR)
 731            continue;
 732
 733        writel(ent, regs + S3C2410_TFTPAL(i));
 734
 735        /* it seems the only way to know exactly
 736         * if the palette wrote ok, is to check
 737         * to see if the value verifies ok
 738         */
 739
 740        if (readw(regs + S3C2410_TFTPAL(i)) == ent)
 741            fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR;
 742        else
 743            fbi->palette_ready = 1;   /* retry */
 744    }
 745}

728 - 744行,将fbi->palette_buffer中的调色板信息写到LCD控制器的调色板内存区。
 
三、platform_driver remove函数分析
s3c2410fb_remove函数定义如下:
1021static int __devexit s3c2410fb_remove(struct platform_device *pdev)
1022{
1023    struct fb_info *fbinfo = platform_get_drvdata(pdev);
1024    struct s3c2410fb_info *info = fbinfo->par;
1025    int irq;
1026
1027    unregister_framebuffer(fbinfo);
1028    s3c2410fb_cpufreq_deregister(info);
1029
1030    s3c2410fb_lcd_enable(info, 0);
1031    msleep(1);
1032
1033    s3c2410fb_unmap_video_memory(fbinfo);
1034
1035    if (info->clk) {
1036        clk_disable(info->clk);
1037        clk_put(info->clk);
1038        info->clk = NULL;
1039    }
1040
1041    irq = platform_get_irq(pdev, 0);
1042    free_irq(irq, info);
1043
1044    iounmap(info->io);
1045
1046    release_resource(info->mem);
1047    kfree(info->mem);
1048
1049    platform_set_drvdata(pdev, NULL);
1050    framebuffer_release(fbinfo);
1051
1052    return 0;
1053}

1027行,调用unregister_framebuffer,注销fb_info结构实例。
1028行,注销变频。
1030行,停止LCD控制器的工作。
1031行,延迟1毫秒,因为停止LCD控制器的工作需要一定时间。
1033行,释放帧缓冲区。
1035 - 1039,释放时钟资源。
1042行,释放中断号。
1044行,取消对LCD I/O内存的映射。
1046 - 1047,释放LCD I/O内存。
1050行,释放fb_info空间。
s3c2410fb_lcd_enable函数定义如下:
 530static void s3c2410fb_lcd_enable(struct s3c2410fb_info *fbi, int enable)
 531{
 532    unsigned long flags;
 533
 534    local_irq_save(flags);
 535
 536    if (enable)
 537        fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID;
 538    else
 539        fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;
 540
 541    writel(fbi->regs.lcdcon1, fbi->io + S3C2410_LCDCON1);
 542
 543    local_irq_restore(flags);
 544}

536 - 537行,将LCDCON1寄存器的第0位设置为1,使能LCD控制器。
538 - 539行,将LCDCON1寄存器的第0位设置为0,禁用LCD控制器。
 
四、platform_driver的电源管理支持
如果定义了CONFIG_PM宏,则需要platform_driver提供suspend和resume函数以支持电源管理功能。
1057/* suspend and resume support for the lcd controller */
1058static int s3c2410fb_suspend(struct platform_device *dev, pm_message_t state)
1059{
1060    struct fb_info     *fbinfo = platform_get_drvdata(dev);
1061    struct s3c2410fb_info *info = fbinfo->par;
1062
1063    s3c2410fb_lcd_enable(info, 0);
1064
1065    /* sleep before disabling the clock, we need to ensure
1066     * the LCD DMA engine is not going to get back on the bus
1067     * before the clock goes off again (bjd) */
1068
1069    msleep(1);
1070    clk_disable(info->clk);
1071
1072    return 0;
1073}

1063行,禁用LCD控制器。
1070行,关闭时钟。
s3c2410fb_resume函数定义如下:
1075static int s3c2410fb_resume(struct platform_device *dev)
1076{
1077    struct fb_info     *fbinfo = platform_get_drvdata(dev);
1078    struct s3c2410fb_info *info = fbinfo->par;
1079
1080    clk_enable(info->clk);
1081    msleep(1);
1082
1083    s3c2410fb_init_registers(fbinfo);
1084
1085    /* re-activate our display after resume */
1086    s3c2410fb_activate_var(fbinfo);
1087    s3c2410fb_blank(FB_BLANK_UNBLANK, fbinfo);
1088
1089    return 0;
1090}

1080行,使能时钟。
1083行,重新初始化LCD寄存器。
1086行,重新激活fb_info的所有参数配置,这个函数在第五部分分析。
1087行,让LCD显示空白,这个函数在第五部分分析。
 
五、fb_ops函数集的实现
S3C2410 LCD驱动程序的fb_ops函数集定义如下:
 615static struct fb_ops s3c2410fb_ops = {
 616    .owner      = THIS_MODULE,
 617    .fb_check_var   = s3c2410fb_check_var,
 618    .fb_set_par = s3c2410fb_set_par,
 619    .fb_blank   = s3c2410fb_blank,
 620    .fb_setcolreg   = s3c2410fb_setcolreg,
 621    .fb_fillrect    = cfb_fillrect,
 622    .fb_copyarea    = cfb_copyarea,
 623    .fb_imageblit   = cfb_imageblit,
 624};

617行,s3c2410fb_check_var函数我们前面已经分析过了。
下面看s3c2410fb_set_par函数的实现:
 416/*
 417 *      s3c2410fb_set_par - Alters the hardware state.
 418 *      @info: frame buffer structure that represents a single frame buffer
 419 *
 420 */
 421static int s3c2410fb_set_par(struct fb_info *info)
 422{
 423    struct fb_var_screeninfo *var = &info->var;
 424
 425    switch (var->bits_per_pixel) {
 426    case 32:
 427    case 16:
 428    case 12:
 429        info->fix.visual = FB_VISUAL_TRUECOLOR;
 430        break;
 431    case 1:
 432        info->fix.visual = FB_VISUAL_MONO01;
 433        break;
 434    default:
 435        info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
 436        break;
 437    }
 438
 439    info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
 440
 441    /* activate this new configuration */
 442
 443    s3c2410fb_activate_var(info);
 444    return 0;
 445}

425 - 439行,根据info->var调整info->fix的值。
443行,调用s3c2410fb_activate_var函数激活info->var指定的配置。
 361/* s3c2410fb_activate_var
 362 *
 363 * activate (set) the controller from the given framebuffer
 364 * information
 365 */
 366static void s3c2410fb_activate_var(struct fb_info *info)
 367{
 368    struct s3c2410fb_info *fbi = info->par;
 369    void __iomem *regs = fbi->io;
 370    int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
 371    struct fb_var_screeninfo *var = &info->var;
 372    int clkdiv;
 373
 374    clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi, var->pixclock), 2);
 375
 376    dprintk("%s: var->xres  = %d\n", __func__, var->xres);
 377    dprintk("%s: var->yres  = %d\n", __func__, var->yres);
 378    dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);
 379
 380    if (type == S3C2410_LCDCON1_TFT) {
 381        s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
 382        --clkdiv;
 383        if (clkdiv < 0)
 384            clkdiv = 0;
 385    } else {
 386        s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
 387        if (clkdiv < 2)
 388            clkdiv = 2;
 389    }
 390
 391    fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);
 392
 393    /* write new registers */
 394
 395    dprintk("new register set:\n");
 396    dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
 397    dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
 398    dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
 399    dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
 400    dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
 401
 402    writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
 403        regs + S3C2410_LCDCON1);
 404    writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
 405    writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
 406    writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
 407    writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
 408
 409    /* set lcd address pointers */
 410    s3c2410fb_set_lcdaddr(info);
 411
 412    fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
 413    writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
 414}

370行,获取液晶屏类型。
374行,计算LCDCON1寄存器用到的CLKVAL值(从391行可以看出)。根据S3C2410 Datasheet,计算公式如下:
STN: VCLK = HCLK / ( CLKVAL x 2 ) ( CLKVAL >= 2 )
TFT: VCLK = HCLK / [(CLKVAL + 1) x 2 ] (CLKVAL >= 0 )
DIV_ROUND_UP 宏定义在include/linux/kernel.h文件中,其定义如下:
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
s3c2410fb_calc_pixclk函数定义如下:
  86/* s3c2410fb_calc_pixclk()
  87 *
  88 * calculate divisor for clk->pixclk
  89 */
  90static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
  91                      unsigned long pixclk)
  92{
  93    unsigned long clk = fbi->clk_rate;
  94    unsigned long long div;
  95
  96    /* pixclk is in picoseconds, our clock is in Hz
  97     *
  98     * Hz -> picoseconds is / 10^-12
  99     */
 100
 101    div = (unsigned long long)clk * pixclk;
 102    div >>= 12;         /* div / 2^12 */
 103    do_div(div, 625 * 625UL * 625); /* div / 5^12 */
 104
 105    dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
 106    return div;
 107}

pixclk的单位是皮秒,皮秒是天文学名词,是目前最小的时间单位,一皮秒等于10的12次方分之一秒。
do_div宏的定义在arch/arm/include/asm/div64.h文件中。看注释其作用是div/5^12,但是为什么这样做,还不清楚。
回到s3c2410fb_activate_var函数,
381行,如果是TFT屏,调用s3c2410fb_calculate_tft_lcd_regs函数:
 300/* s3c2410fb_calculate_tft_lcd_regs
 301 *
 302 * calculate register values from var settings
 303 */
 304static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,
 305                         struct s3c2410fb_hw *regs)
 306{
 307    const struct s3c2410fb_info *fbi = info->par;
 308    const struct fb_var_screeninfo *var = &info->var;
 309
 310    switch (var->bits_per_pixel) {
 311    case 1:
 312        regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;
 313        break;
 314    case 2:
 315        regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
 316        break;
 317    case 4:
 318        regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
 319        break;
 320    case 8:
 321        regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
 322        regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |
 323                 S3C2410_LCDCON5_FRM565;
 324        regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;
 325        break;
 326    case 16:
 327        regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
 328        regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;
 329        regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;
 330        break;
 331    case 32:
 332        regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;
 333        regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |
 334                   S3C2410_LCDCON5_HWSWP |
 335                   S3C2410_LCDCON5_BPP24BL);
 336        break;
 337    default:
 338        /* invalid pixel depth */
 339        dev_err(fbi->dev, "invalid bpp %d\n",
 340            var->bits_per_pixel);
 341    }
 342    /* update X/Y info */
 343    dprintk("setting vert: up=%d, low=%d, sync=%d\n",
 344        var->upper_margin, var->lower_margin, var->vsync_len);
 345
 346    dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
 347        var->left_margin, var->right_margin, var->hsync_len);
 348
 349    regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |
 350            S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
 351            S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
 352            S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
 353
 354    regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
 355            S3C2410_LCDCON3_HFPD(var->left_margin - 1) |
 356            S3C2410_LCDCON3_HOZVAL(var->xres - 1);
 357
 358    regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
 359}

针对TFT屏,这个函数通过info->var的成员变量值计算各个LCD Control寄存器的值。参考Datasheet很容易理解这个函数。
386行,如果是STN屏,则调用s3c2410fb_calculate_stn_lcd_regs函数:
 240/* s3c2410fb_calculate_stn_lcd_regs
 241 *
 242 * calculate register values from var settings
 243 */
 244static void s3c2410fb_calculate_stn_lcd_regs(const struct fb_info *info,
 245                         struct s3c2410fb_hw *regs)
 246{
 247    const struct s3c2410fb_info *fbi = info->par;
 248    const struct fb_var_screeninfo *var = &info->var;
 249    int type = regs->lcdcon1 & ~S3C2410_LCDCON1_TFT;
 250    int hs = var->xres >> 2;
 251    unsigned wdly = (var->left_margin >> 4) - 1;
 252    unsigned wlh = (var->hsync_len >> 4) - 1;
 253
 254    if (type != S3C2410_LCDCON1_STN4)
 255        hs >>= 1;
 256
 257    switch (var->bits_per_pixel) {
 258    case 1:
 259        regs->lcdcon1 |= S3C2410_LCDCON1_STN1BPP;
 260        break;
 261    case 2:
 262        regs->lcdcon1 |= S3C2410_LCDCON1_STN2GREY;
 263        break;
 264    case 4:
 265        regs->lcdcon1 |= S3C2410_LCDCON1_STN4GREY;
 266        break;
 267    case 8:
 268        regs->lcdcon1 |= S3C2410_LCDCON1_STN8BPP;
 269        hs *= 3;
 270        break;
 271    case 12:
 272        regs->lcdcon1 |= S3C2410_LCDCON1_STN12BPP;
 273        hs *= 3;
 274        break;
 275
 276    default:
 277        /* invalid pixel depth */
 278        dev_err(fbi->dev, "invalid bpp %d\n",
 279            var->bits_per_pixel);
 280    }
 281    /* update X/Y info */
 282    dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
 283        var->left_margin, var->right_margin, var->hsync_len);
 284
 285    regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1);
 286
 287    if (wdly > 3)
 288        wdly = 3;
 289
 290    if (wlh > 3)
 291        wlh = 3;
 292
 293    regs->lcdcon3 = S3C2410_LCDCON3_WDLY(wdly) |
 294            S3C2410_LCDCON3_LINEBLANK(var->right_margin / 8) |
 295            S3C2410_LCDCON3_HOZVAL(hs - 1);
 296
 297    regs->lcdcon4 = S3C2410_LCDCON4_WLH(wlh);
 298}

针对STN屏,这个函数通过info->var的成员变量值计算各个LCD Control寄存器的值。参考Datasheet理解这个函数。
回到s3c2410fb_activate_var函数,
391行,设置LCDCON1的CLKVAL。
402 - 407行,把相关设置写入LCD控制寄存器。
410行,调用s3c2410fb_set_lcdaddr函数设置帧缓冲区的地址,寄存器LCDSADDR1、LCDSADDR2、LCDSADDR3的含义参考Datasheet:
  59/* s3c2410fb_set_lcdaddr
  60 *
  61 * initialise lcd controller address pointers
  62 */
  63static void s3c2410fb_set_lcdaddr(struct fb_info *info)
  64{
  65    unsigned long saddr1, saddr2, saddr3;
  66    struct s3c2410fb_info *fbi = info->par;
  67    void __iomem *regs = fbi->io;
  68
  69    saddr1  = info->fix.smem_start >> 1;
  70    saddr2  = info->fix.smem_start;
  71    saddr2 += info->fix.line_length * info->var.yres;
  72    saddr2 >>= 1;
  73
  74    saddr3 = S3C2410_OFFSIZE(0) |
  75         S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);
  76
  77    dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
  78    dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
  79    dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
  80
  81    writel(saddr1, regs + S3C2410_LCDSADDR1);
  82    writel(saddr2, regs + S3C2410_LCDSADDR2);
  83    writel(saddr3, regs + S3C2410_LCDSADDR3);
  84}

回忆一下在s3c2410fb_map_video_memory函数的651行,帧缓冲区Framebuffer的物理地址首地址保存在fb_info.fix.smem_start中。LCDADDR1寄存器的值怎样计算呢?其实就是把Framebuffer物理地址值右移1位(因为是16位BPP),写到LCDADDR1寄存器就可以了。
LCDADDR2寄存器保存Framebuffer的结束地址,在s3c2410fb_set_par函数中,439行,将每行占用的字节数保存在fb_info.fix.line_length变量中。屏幕共有多少行,保存在fb_info.var.yres中。所以,71行得到Framebuffer的结束位置,72行将结束位置右移1位(因为是16位BPP)。
74行,S3C2410_OFFSIZE 宏定义如下:
#define S3C2410_OFFSIZE(x)  ((x) << 11)

75行,S3C2410_PAGEWIDTH宏定义如下:
#define S3C2410_PAGEWIDTH(x)    (x)

回到s3c2410fb_activate_var函数,
413行,使能LCD控制器。
至此,s3c2410fb_activate_var函数就全部分析完了。
同时,s3c2410fb_set_par函数也就全部分析完了。
下面来分析fb_ops.fb_bank函数s3c2410fb_blank的实现:
 547/*
 548 *      s3c2410fb_blank
 549 *  @blank_mode: the blank mode we want.
 550 *  @info: frame buffer structure that represents a single frame buffer
 551 *
 552 *  Blank the screen if blank_mode != 0, else unblank. Return 0 if
 553 *  blanking succeeded, != 0 if un-/blanking failed due to e.g. a
 554 *  video mode which doesn't support it. Implements VESA suspend
 555 *  and powerdown modes on hardware that supports disabling hsync/vsync:
 556 *
 557 *  Returns negative errno on error, or zero on success.
 558 *
 559 */
 560static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
 561{
 562    struct s3c2410fb_info *fbi = info->par;
 563    void __iomem *tpal_reg = fbi->io;
 564
 565    dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
 566
 567    tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;
 568
 569    if (blank_mode == FB_BLANK_POWERDOWN) {
 570        s3c2410fb_lcd_enable(fbi, 0);
 571    } else {
 572        s3c2410fb_lcd_enable(fbi, 1);
 573    }
 574
 575    if (blank_mode == FB_BLANK_UNBLANK)
 576        writel(0x0, tpal_reg);
 577    else {
 578        dprintk("setting TPAL to output 0x000000\n");
 579        writel(S3C2410_TPAL_EN, tpal_reg);
 580    }
 581
 582    return 0;
 583}

如果要输出一帧单色图像,可以在TPAL寄存器中设定这个颜色值,然后使能TPAL寄存器,这种方法可以避免修改整个调色板或帧缓冲区。
579行,使能TPAL,同时显示让屏幕清屏,S3C2410_TPAL_EN宏定义如下:
#define S3C2410_TPAL_EN     (1<<24)

下面来分析fb_ops.fb_setcolreg函数s3c2410fb_setcolreg:
 479static int s3c2410fb_setcolreg(unsigned regno,
 480                   unsigned red, unsigned green, unsigned blue,
 481                   unsigned transp, struct fb_info *info)
 482{
 483    struct s3c2410fb_info *fbi = info->par;
 484    void __iomem *regs = fbi->io;
 485    unsigned int val;
 486
 487    /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
 488           regno, red, green, blue); */
 489
 490    switch (info->fix.visual) {
 491    case FB_VISUAL_TRUECOLOR:
 492        /* true-colour, use pseudo-palette */
 493
 494        if (regno < 16) {
 495            u32 *pal = info->pseudo_palette;
 496
 497            val  = chan_to_field(red,   &info->var.red);
 498            val |= chan_to_field(green, &info->var.green);
 499            val |= chan_to_field(blue,  &info->var.blue);
 500
 501            pal[regno] = val;
 502        }
 503        break;
 504
 505    case FB_VISUAL_PSEUDOCOLOR:
 506        if (regno < 256) {
 507            /* currently assume RGB 5-6-5 mode */
 508
 509            val  = (red   >>  0) & 0xf800;
 510            val |= (green >>  5) & 0x07e0;
 511            val |= (blue  >> 11) & 0x001f;
 512
 513            writel(val, regs + S3C2410_TFTPAL(regno));
 514            schedule_palette_update(fbi, regno, val);
 515        }
 516
 517        break;
 518
 519    default:
 520        return 1;   /* unknown type */
 521    }
 522
 523    return 0;
 524}

该函数的作用是将颜色值写入调色板(或伪调色板)的指定位置。
501行,将颜色值写入伪调色板。
513行,将颜色值写入调色板。
chan_to_field函数定义如下:
 471static inline unsigned int chan_to_field(unsigned int chan,
 472                     struct fb_bitfield *bf)
 473{
 474    chan &= 0xffff;
 475    chan >>= 16 - bf->length;
 476    return chan << bf->offset;
 477}

schedule_palette_update函数定义如下:
 447static void schedule_palette_update(struct s3c2410fb_info *fbi,
 448                    unsigned int regno, unsigned int val)
 449{
 450    unsigned long flags;
 451    unsigned long irqen;
 452    void __iomem *irq_base = fbi->irq_base;
 453
 454    local_irq_save(flags);
 455
 456    fbi->palette_buffer[regno] = val;
 457
 458    if (!fbi->palette_ready) {
 459        fbi->palette_ready = 1;
 460
 461        /* enable IRQ */
 462        irqen = readl(irq_base + S3C24XX_LCDINTMSK);
 463        irqen &= ~S3C2410_LCDINT_FRSYNC;
 464        writel(irqen, irq_base + S3C24XX_LCDINTMSK);
 465    }
 466
 467    local_irq_restore(flags);
 468}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值