S3C2410驱动分析之LCD驱动

news/2025/2/12 18:13:14/

作者:刘昊昱 

博客: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}


http://www.ppmy.cn/news/191636.html

相关文章

使用32驱动1602液晶屏

此处我用的是正点原子F3的精英板&#xff0c;去驱动普中51开发板带的配件1602液晶屏&#xff0c;由于没有直接对应的接口&#xff0c;于是只能用飞线大法了… 连接实物图如下&#xff1a; 1602液晶屏引脚说明&#xff1a; 32对应引脚连接&#xff1a; PDout(6) //读/写选择-…

22.4.6

1&#xff0c;二叉树四种遍历&#xff0c;2&#xff0c;string 去重&#xff08;去空格&#xff09;3,根据二叉树后序和中序&#xff0c;求先序&#xff1b;4,已知二叉树后序和中序&#xff0c;求层序&#xff1b;5&#xff0c;蒙德里安的梦想&#xff08;状态压缩dp&#xff0…

win10+vs2017环境中编译64位的openh264.dll库文件

一、环境配置 1、安装vs2017。 2、安装MinGW。这一步的坑是&#xff1a;在安装mingw之后还得单独下载msys压缩包&#xff0c;解压到mingw目录下 将存放make.exe的路径C:\MinGW\msys\bin配置在系统path参数里面&#xff0c;否则会找不到make命令。 3、安装nasm。 下载nas…

linux查看硬盘插槽_linux如何查看硬件信息CPU,内存,硬盘

1、 查看CPU 1.1 查看CPU个数 # cat /proc/cpuinfo | grep "physical id" | uniq | wc -l 2 //两个物理CPU **uniq命令&#xff1a;删除重复行&#xff1b;wc –l命令&#xff1a;统计行数** 1.2 查看CPU核数 # cat /proc/cpuinfo | grep "cpu cores" | un…

华大HC32A460 系列介绍(一)

[T1、华大HC32A460系列产品特性 ARM Cortex-M4 32bit MCUFPU&#xff0c;250DMIPS&#xff0c;up to 512KB Flash&#xff0c;192KB SRAM&#xff0c;USB FS&#xff08;Device/Host&#xff09;&#xff0c; 14 Timers&#xff0c;2 ADCs&#xff0c;1 PGA&#xff0c;3 CMPs&…

LCD驱动芯片(IC)-VK2C系列(VK2C21/22/23/24)多用于燃气表;水表/电气表等,具抗干扰及低功耗特性

VK2C系列&#xff0c;是点阵式存储映射的LCD驱动器&#xff0c;单片机可通过I2C接口配置显示参数和读写显示数据&#xff0c;也可通过指令进入省电模式。其高抗干扰/低功耗的特性适用于水电气表及工控仪表类。 VK2C21 特点&#xff1a; • 工作电压 2.4-5.5V • 内置32 kHz RC…

t5810做虚拟服务器,戴尔Precision T5810工作站选用CPU的问题 | 小迪的生产力工具室...

戴尔 Precision T5810 图形工作站标配的 CPU 一般都是 Intel 至强 E5-1600 系列的 CPU&#xff0c;但这并不是绝对的&#xff0c;T5810 也是支持 E5-2600 系列 CPU 的&#xff0c;这个案例就是说明这一点。 出镜产品&#xff1a; DELL Precision T5810 工作站 CPU Intel Xeon E…

3.22 66-

一.封装 1.定义 1.禁止直接访问一个对象中数据的实际表示 通过接口来访问 &#xff08;信息隐藏&#xff09; 2.属性私有 get/set 2.代码解释 1.属性私有 private 2.提供一些public 的get set方法 3.封装的意义 1.提高程序的安全性&#xff0c;保护数据 2.隐藏代码的具…