S3C2410驱动分析的LCD驱动







本文分析S3C2410的LCD驱动,该驱动程序基于Framebuffer机制。
 
一、相关数据结构
首先,我们来介绍一下基于Framebuffer的S3C2410 LCD驱动涉及的几个重要数据结构:
Framebuffer的核心数据结构是fb_info,该结构体定义在include/linux/fb.h文件中:

 
 
  1. 832struct fb_info {  
  2. 833    int node;  
  3. 834    int flags;  
  4. 835    struct mutex lock;      /* Lock for open/release/ioctl funcs */  
  5. 836    struct mutex mm_lock;       /* Lock for fb_mmap and smem_* fields */  
  6. 837    struct fb_var_screeninfo var;   /* Current var */  
  7. 838    struct fb_fix_screeninfo fix;   /* Current fix */  
  8. 839    struct fb_monspecs monspecs;    /* Current Monitor specs */  
  9. 840    struct work_struct queue;   /* Framebuffer event queue */  
  10. 841    struct fb_pixmap pixmap;    /* Image hardware mapper */  
  11. 842    struct fb_pixmap sprite;    /* Cursor hardware mapper */  
  12. 843    struct fb_cmap cmap;        /* Current cmap */  
  13. 844    struct list_head modelist;      /* mode list */  
  14. 845    struct fb_videomode *mode;  /* current mode */  
  15. 846  
  16. 847#ifdef CONFIG_FB_BACKLIGHT  
  17. 848    /* assigned backlight device */  
  18. 849    /* set before framebuffer registration, 
  19. 850       remove after unregister */  
  20. 851    struct backlight_device *bl_dev;  
  21. 852  
  22. 853    /* Backlight level curve */  
  23. 854    struct mutex bl_curve_mutex;  
  24. 855    u8 bl_curve[FB_BACKLIGHT_LEVELS];  
  25. 856#endif  
  26. 857#ifdef CONFIG_FB_DEFERRED_IO  
  27. 858    struct delayed_work deferred_work;  
  28. 859    struct fb_deferred_io *fbdefio;  
  29. 860#endif  
  30. 861  
  31. 862    struct fb_ops *fbops;  
  32. 863    struct device *device;      /* This is the parent */  
  33. 864    struct device *dev;     /* This is this fb device */  
  34. 865    int class_flag;                    /* private sysfs flags */  
  35. 866#ifdef CONFIG_FB_TILEBLITTING  
  36. 867    struct fb_tile_ops *tileops;    /* Tile Blitting */  
  37. 868#endif  
  38. 869    char __iomem *screen_base;  /* Virtual address */  
  39. 870    unsigned long screen_size;  /* Amount of ioremapped VRAM or 0 */  
  40. 871    void *pseudo_palette;       /* Fake palette of 16 colors */  
  41. 872#define FBINFO_STATE_RUNNING    0  
  42. 873#define FBINFO_STATE_SUSPENDED  1  
  43. 874    u32 state;          /* Hardware state i.e suspend */  
  44. 875    void *fbcon_par;                /* fbcon use-only private area */  
  45. 876    /* From here on everything is device dependent */  
  46. 877    void *par;  
  47. 878    /* we need the PCI or similiar aperture base/size not 
  48. 879       smem_start/size as smem_start may just be an object 
  49. 880       allocated inside the aperture so may not actually overlap */  
  50. 881    struct apertures_struct {  
  51. 882        unsigned int count;  
  52. 883        struct aperture {  
  53. 884            resource_size_t base;  
  54. 885            resource_size_t size;  
  55. 886        } ranges[0];  
  56. 887    } *apertures;  
  57. 888};  
 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的可变参数。该结构体定义如下:

 
 
  1. 238struct fb_var_screeninfo {  
  2. 239    __u32 xres;         /* visible resolution       */  
  3. 240    __u32 yres;  
  4. 241    __u32 xres_virtual;     /* virtual resolution       */  
  5. 242    __u32 yres_virtual;  
  6. 243    __u32 xoffset;          /* offset from virtual to visible */  
  7. 244    __u32 yoffset;          /* resolution           */  
  8. 245  
  9. 246    __u32 bits_per_pixel;       /* guess what           */  
  10. 247    __u32 grayscale;        /* != 0 Graylevels instead of colors */  
  11. 248  
  12. 249    struct fb_bitfield red;     /* bitfield in fb mem if true color, */  
  13. 250    struct fb_bitfield green;   /* else only length is significant */  
  14. 251    struct fb_bitfield blue;  
  15. 252    struct fb_bitfield transp;  /* transparency         */  
  16. 253  
  17. 254    __u32 nonstd;           /* != 0 Non standard pixel format */  
  18. 255  
  19. 256    __u32 activate;         /* see FB_ACTIVATE_*        */  
  20. 257  
  21. 258    __u32 height;           /* height of picture in mm    */  
  22. 259    __u32 width;            /* width of picture in mm     */  
  23. 260  
  24. 261    __u32 accel_flags;      /* (OBSOLETE) see fb_info.flags */  
  25. 262  
  26. 263    /* Timing: All values in pixclocks, except pixclock (of course) */  
  27. 264    __u32 pixclock;         /* pixel clock in ps (pico seconds) */  
  28. 265    __u32 left_margin;      /* time from sync to picture    */  
  29. 266    __u32 right_margin;     /* time from picture to sync    */  
  30. 267    __u32 upper_margin;     /* time from sync to picture    */  
  31. 268    __u32 lower_margin;  
  32. 269    __u32 hsync_len;        /* length of horizontal sync    */  
  33. 270    __u32 vsync_len;        /* length of vertical sync  */  
  34. 271    __u32 sync;         /* see FB_SYNC_*        */  
  35. 272    __u32 vmode;            /* see FB_VMODE_*       */  
  36. 273    __u32 rotate;           /* angle we rotate counter clockwise */  
  37. 274    __u32 reserved[5];      /* Reserved for future compatibility */  
  38. 275};  
 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的固定参数,该结构体定义如下:

 
 
  1. 155struct fb_fix_screeninfo {  
  2. 156    char id[16];            /* identification string eg "TT Builtin" */  
  3. 157    unsigned long smem_start;   /* Start of frame buffer mem */  
  4. 158                    /* (physical address) */  
  5. 159    __u32 smem_len;         /* Length of frame buffer mem */  
  6. 160    __u32 type;         /* see FB_TYPE_*        */  
  7. 161    __u32 type_aux;         /* Interleave for interleaved Planes */  
  8. 162    __u32 visual;           /* see FB_VISUAL_*      */  
  9. 163    __u16 xpanstep;         /* zero if no hardware panning  */  
  10. 164    __u16 ypanstep;         /* zero if no hardware panning  */  
  11. 165    __u16 ywrapstep;        /* zero if no hardware ywrap    */  
  12. 166    __u32 line_length;      /* length of a line in bytes    */  
  13. 167    unsigned long mmio_start;   /* Start of Memory Mapped I/O   */  
  14. 168                    /* (physical address) */  
  15. 169    __u32 mmio_len;         /* Length of Memory Mapped I/O  */  
  16. 170    __u32 accel;            /* Indicate to driver which */  
  17. 171                    /*  specific chip/card we have  */  
  18. 172    __u16 reserved[3];      /* Reserved for future compatibility */  
  19. 173};  
 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代表对底层硬件操作的函数集,该结构体定义如下:

 
 
  1. 621struct fb_ops {  
  2. 622    /* open/release and usage marking */  
  3. 623    struct module *owner;  
  4. 624    int (*fb_open)(struct fb_info *info, int user);  
  5. 625    int (*fb_release)(struct fb_info *info, int user);  
  6. 626  
  7. 627    /* For framebuffers with strange non linear layouts or that do not 
  8. 628     * work with normal memory mapped access 
  9. 629     */  
  10. 630    ssize_t (*fb_read)(struct fb_info *info, char __user *buf,  
  11. 631               size_t count, loff_t *ppos);  
  12. 632    ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,  
  13. 633                size_t count, loff_t *ppos);  
  14. 634  
  15. 635    /* checks var and eventually tweaks it to something supported, 
  16. 636     * DO NOT MODIFY PAR */  
  17. 637    int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);  
  18. 638  
  19. 639    /* set the video mode according to info->var */  
  20. 640    int (*fb_set_par)(struct fb_info *info);  
  21. 641  
  22. 642    /* set color register */  
  23. 643    int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,  
  24. 644                unsigned blue, unsigned transp, struct fb_info *info);  
  25. 645  
  26. 646    /* set color registers in batch */  
  27. 647    int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);  
  28. 648  
  29. 649    /* blank display */  
  30. 650    int (*fb_blank)(int blank, struct fb_info *info);  
  31. 651  
  32. 652    /* pan display */  
  33. 653    int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);  
  34. 654  
  35. 655    /* Draws a rectangle */  
  36. 656    void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);  
  37. 657    /* Copy data from area to another */  
  38. 658    void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);  
  39. 659    /* Draws a image to the display */  
  40. 660    void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);  
  41. 661  
  42. 662    /* Draws cursor */  
  43. 663    int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);  
  44. 664  
  45. 665    /* Rotates the display */  
  46. 666    void (*fb_rotate)(struct fb_info *info, int angle);  
  47. 667  
  48. 668    /* wait for blit idle, optional */  
  49. 669    int (*fb_sync)(struct fb_info *info);  
  50. 670  
  51. 671    /* perform fb specific ioctl (optional) */  
  52. 672    int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,  
  53. 673            unsigned long arg);  
  54. 674  
  55. 675    /* Handle 32bit compat ioctl (optional) */  
  56. 676    int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,  
  57. 677            unsigned long arg);  
  58. 678  
  59. 679    /* perform fb specific mmap */  
  60. 680    int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);  
  61. 681  
  62. 682    /* get capability given var */  
  63. 683    void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,  
  64. 684                struct fb_var_screeninfo *var);  
  65. 685  
  66. 686    /* teardown any resources to do with this framebuffer */  
  67. 687    void (*fb_destroy)(struct fb_info *info);  
  68. 688  
  69. 689    /* called at KDB enter and leave time to prepare the console */  
  70. 690    int (*fb_debug_enter)(struct fb_info *info);  
  71. 691    int (*fb_debug_leave)(struct fb_info *info);  
  72. 692};  
 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结构体:

 
 
  1. 21struct s3c2410fb_info {  
  2. 22    struct device       *dev;  
  3. 23    struct clk      *clk;  
  4. 24  
  5. 25    struct resource     *mem;  
  6. 26    void __iomem        *io;  
  7. 27    void __iomem        *irq_base;  
  8. 28  
  9. 29    enum s3c_drv_type   drv_type;  
  10. 30    struct s3c2410fb_hw regs;  
  11. 31  
  12. 32    unsigned long       clk_rate;  
  13. 33    unsigned int        palette_ready;  
  14. 34  
  15. 35#ifdef CONFIG_CPU_FREQ  
  16. 36    struct notifier_block   freq_transition;  
  17. 37#endif  
  18. 38  
  19. 39    /* keep these registers in case we need to re-write palette */  
  20. 40    u32         palette_buffer[256];  
  21. 41    u32         pseudo_pal[16];  
  22. 42};  
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结构体:

 
 
  1. 25/* LCD description */  
  2. 26struct s3c2410fb_display {  
  3. 27    /* LCD type */  
  4. 28    unsigned type;  
  5. 29  
  6. 30    /* Screen size */  
  7. 31    unsigned short width;  
  8. 32    unsigned short height;  
  9. 33  
  10. 34    /* Screen info */  
  11. 35    unsigned short xres;  
  12. 36    unsigned short yres;  
  13. 37    unsigned short bpp;  
  14. 38  
  15. 39    unsigned pixclock;      /* pixclock in picoseconds */  
  16. 40    unsigned short left_margin;  /* value in pixels (TFT) or HCLKs (STN) */  
  17. 41    unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */  
  18. 42    unsigned short hsync_len;    /* value in pixels (TFT) or HCLKs (STN) */  
  19. 43    unsigned short upper_margin;    /* value in lines (TFT) or 0 (STN) */  
  20. 44    unsigned short lower_margin;    /* value in lines (TFT) or 0 (STN) */  
  21. 45    unsigned short vsync_len;   /* value in lines (TFT) or 0 (STN) */  
  22. 46  
  23. 47    /* lcd configuration registers */  
  24. 48    unsigned long   lcdcon5;  
  25. 49};  
  26. 50  
  27. 51struct s3c2410fb_mach_info {  
  28. 52  
  29. 53    struct s3c2410fb_display *displays; /* attached diplays info */  
  30. 54    unsigned num_displays;          /* number of defined displays */  
  31. 55    unsigned default_display;  
  32. 56  
  33. 57    /* GPIOs */  
  34. 58  
  35. 59    unsigned long   gpcup;  
  36. 60    unsigned long   gpcup_mask;  
  37. 61    unsigned long   gpccon;  
  38. 62    unsigned long   gpccon_mask;  
  39. 63    unsigned long   gpdup;  
  40. 64    unsigned long   gpdup_mask;  
  41. 65    unsigned long   gpdcon;  
  42. 66    unsigned long   gpdcon_mask;  
  43. 67  
  44. 68    /* lpc3600 control register */  
  45. 69    unsigned long   lpcsel;  
  46. 70};  
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:

 
 
  1. 1119int __init s3c2410fb_init(void)  
  2. 1120{  
  3. 1121    int ret = platform_driver_register(&s3c2410fb_driver);  
  4. 1122  
  5. 1123    if (ret == 0)  
  6. 1124        ret = platform_driver_register(&s3c2412fb_driver);  
  7. 1125  
  8. 1126    return ret;  
  9. 1127}  
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,定义如下:

 
 
  1. 1097static struct platform_driver s3c2410fb_driver = {  
  2. 1098    .probe      = s3c2410fb_probe,  
  3. 1099    .remove     = __devexit_p(s3c2410fb_remove),  
  4. 1100    .suspend    = s3c2410fb_suspend,  
  5. 1101    .resume     = s3c2410fb_resume,  
  6. 1102    .driver     = {  
  7. 1103        .name   = "s3c2410-lcd",  
  8. 1104        .owner  = THIS_MODULE,  
  9. 1105    },  
  10. 1106};  
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函数,该函数定义如下:

 
 
  1. 1007static int __devinit s3c2410fb_probe(struct platform_device *pdev)  
  2. 1008{  
  3. 1009    return s3c24xxfb_probe(pdev, DRV_S3C2410);  
  4. 1010}  
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函数:

 
 
  1.  817static int __devinit s3c24xxfb_probe(struct platform_device *pdev,  
  2.  818                  enum s3c_drv_type drv_type)  
  3.  819{  
  4.  820    struct s3c2410fb_info *info;  
  5.  821    struct s3c2410fb_display *display;  
  6.  822    struct fb_info *fbinfo;  
  7.  823    struct s3c2410fb_mach_info *mach_info;  
  8.  824    struct resource *res;  
  9.  825    int ret;  
  10.  826    int irq;  
  11.  827    int i;  
  12.  828    int size;  
  13.  829    u32 lcdcon1;  
  14.  830  
  15.  831    mach_info = pdev->dev.platform_data;  
  16.  832    if (mach_info == NULL) {  
  17.  833        dev_err(&pdev->dev,  
  18.  834            "no platform data for lcd, cannot attach\n");  
  19.  835        return -EINVAL;  
  20.  836    }  
  21.  837  
  22.  838    if (mach_info->default_display >= mach_info->num_displays) {  
  23.  839        dev_err(&pdev->dev, "default is %d but only %d displays\n",  
  24.  840            mach_info->default_display, mach_info->num_displays);  
  25.  841        return -EINVAL;  
  26.  842    }  
  27.  843  
  28.  844    display = mach_info->displays + mach_info->default_display;  
  29.  845  
  30.  846    irq = platform_get_irq(pdev, 0);  
  31.  847    if (irq < 0) {  
  32.  848        dev_err(&pdev->dev, "no irq for device\n");  
  33.  849        return -ENOENT;  
  34.  850    }  
  35.  851  
  36.  852    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);  
  37.  853    if (!fbinfo)  
  38.  854        return -ENOMEM;  
  39.  855  
  40.  856    platform_set_drvdata(pdev, fbinfo);  
  41.  857  
  42.  858    info = fbinfo->par;  
  43.  859    info->dev = &pdev->dev;  
  44.  860    info->drv_type = drv_type;  
  45.  861  
  46.  862    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  47.  863    if (res == NULL) {  
  48.  864        dev_err(&pdev->dev, "failed to get memory registers\n");  
  49.  865        ret = -ENXIO;  
  50.  866        goto dealloc_fb;  
  51.  867    }  
  52.  868  
  53.  869    size = (res->end - res->start) + 1;  
  54.  870    info->mem = request_mem_region(res->start, size, pdev->name);  
  55.  871    if (info->mem == NULL) {  
  56.  872        dev_err(&pdev->dev, "failed to get memory region\n");  
  57.  873        ret = -ENOENT;  
  58.  874        goto dealloc_fb;  
  59.  875    }  
  60.  876  
  61.  877    info->io = ioremap(res->start, size);  
  62.  878    if (info->io == NULL) {  
  63.  879        dev_err(&pdev->dev, "ioremap() of registers failed\n");  
  64.  880        ret = -ENXIO;  
  65.  881        goto release_mem;  
  66.  882    }  
  67.  883  
  68.  884    info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);  
  69.  885  
  70.  886    dprintk("devinit\n");  
  71.  887  
  72.  888    strcpy(fbinfo->fix.id, driver_name);  
  73.  889  
  74.  890    /* Stop the video */  
  75.  891    lcdcon1 = readl(info->io + S3C2410_LCDCON1);  
  76.  892    writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);  
  77.  893  
  78.  894    fbinfo->fix.type        = FB_TYPE_PACKED_PIXELS;  
  79.  895    fbinfo->fix.type_aux        = 0;  
  80.  896    fbinfo->fix.xpanstep        = 0;  
  81.  897    fbinfo->fix.ypanstep        = 0;  
  82.  898    fbinfo->fix.ywrapstep       = 0;  
  83.  899    fbinfo->fix.accel       = FB_ACCEL_NONE;  
  84.  900  
  85.  901    fbinfo->var.nonstd      = 0;  
  86.  902    fbinfo->var.activate        = FB_ACTIVATE_NOW;  
  87.  903    fbinfo->var.accel_flags     = 0;  
  88.  904    fbinfo->var.vmode       = FB_VMODE_NONINTERLACED;  
  89.  905  
  90.  906    fbinfo->fbops           = &s3c2410fb_ops;  
  91.  907    fbinfo->flags           = FBINFO_FLAG_DEFAULT;  
  92.  908    fbinfo->pseudo_palette      = &info->pseudo_pal;  
  93.  909  
  94.  910    for (i = 0; i < 256; i++)  
  95.  911        info->palette_buffer[i] = PALETTE_BUFF_CLEAR;  
  96.  912  
  97.  913    ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);  
  98.  914    if (ret) {  
  99.  915        dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);  
  100.  916        ret = -EBUSY;  
  101.  917        goto release_regs;  
  102.  918    }  
  103.  919  
  104.  920    info->clk = clk_get(NULL, "lcd");  
  105.  921    if (!info->clk || IS_ERR(info->clk)) {  
  106.  922        printk(KERN_ERR "failed to get lcd clock source\n");  
  107.  923        ret = -ENOENT;  
  108.  924        goto release_irq;  
  109.  925    }  
  110.  926  
  111.  927    clk_enable(info->clk);  
  112.  928    dprintk("got and enabled clock\n");  
  113.  929  
  114.  930    msleep(1);  
  115.  931  
  116.  932    info->clk_rate = clk_get_rate(info->clk);  
  117.  933  
  118.  934    /* find maximum required memory size for display */  
  119.  935    for (i = 0; i < mach_info->num_displays; i++) {  
  120.  936        unsigned long smem_len = mach_info->displays[i].xres;  
  121.  937  
  122.  938        smem_len *= mach_info->displays[i].yres;  
  123.  939        smem_len *= mach_info->displays[i].bpp;  
  124.  940        smem_len >>= 3;  
  125.  941        if (fbinfo->fix.smem_len < smem_len)  
  126.  942            fbinfo->fix.smem_len = smem_len;  
  127.  943    }  
  128.  944  
  129.  945    /* Initialize video memory */  
  130.  946    ret = s3c2410fb_map_video_memory(fbinfo);  
  131.  947    if (ret) {  
  132.  948        printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);  
  133.  949        ret = -ENOMEM;  
  134.  950        goto release_clock;  
  135.  951    }  
  136.  952  
  137.  953    dprintk("got video memory\n");  
  138.  954  
  139.  955    fbinfo->var.xres = display->xres;  
  140.  956    fbinfo->var.yres = display->yres;  
  141.  957    fbinfo->var.bits_per_pixel = display->bpp;  
  142.  958  
  143.  959    s3c2410fb_init_registers(fbinfo);  
  144.  960  
  145.  961    s3c2410fb_check_var(&fbinfo->var, fbinfo);  
  146.  962  
  147.  963    ret = s3c2410fb_cpufreq_register(info);  
  148.  964    if (ret < 0) {  
  149.  965        dev_err(&pdev->dev, "Failed to register cpufreq\n");  
  150.  966        goto free_video_memory;  
  151.  967    }  
  152.  968  
  153.  969    ret = register_framebuffer(fbinfo);  
  154.  970    if (ret < 0) {  
  155.  971        printk(KERN_ERR "Failed to register framebuffer device: %d\n",  
  156.  972            ret);  
  157.  973        goto free_cpufreq;  
  158.  974    }  
  159.  975  
  160.  976    /* create device files */  
  161.  977    ret = device_create_file(&pdev->dev, &dev_attr_debug);  
  162.  978    if (ret) {  
  163.  979        printk(KERN_ERR "failed to add debug attribute\n");  
  164.  980    }  
  165.  981  
  166.  982    printk(KERN_INFO "fb%d: %s frame buffer device\n",  
  167.  983        fbinfo->node, fbinfo->fix.id);  
  168.  984  
  169.  985    return 0;  
  170.  986  
  171.  987 free_cpufreq:  
  172.  988    s3c2410fb_cpufreq_deregister(info);  
  173.  989free_video_memory:  
  174.  990    s3c2410fb_unmap_video_memory(fbinfo);  
  175.  991release_clock:  
  176.  992    clk_disable(info->clk);  
  177.  993    clk_put(info->clk);  
  178.  994release_irq:  
  179.  995    free_irq(irq, info);  
  180.  996release_regs:  
  181.  997    iounmap(info->io);  
  182.  998release_mem:  
  183.  999    release_resource(info->mem);  
  184. 1000    kfree(info->mem);  
  185. 1001dealloc_fb:  
  186. 1002    platform_set_drvdata(pdev, NULL);  
  187. 1003    framebuffer_release(fbinfo);  
  188. 1004    return ret;  
  189. 1005}  
 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函数定义如下:

 
 
  1. 626/* 
  2. 627 * s3c2410fb_map_video_memory(): 
  3. 628 *  Allocates the DRAM memory for the frame buffer.  This buffer is 
  4. 629 *  remapped into a non-cached, non-buffered, memory region to 
  5. 630 *  allow palette and pixel writes to occur without flushing the 
  6. 631 *  cache.  Once this area is remapped, all virtual memory 
  7. 632 *  access to the video memory should occur at the new region. 
  8. 633 */  
  9. 634837837static int __devinit s3c2410fb_map_video_memory(struct fb_info *info)  
  10. 635{  
  11. 636    struct s3c2410fb_info *fbi = info->par;  
  12. 637    dma_addr_t map_dma;  
  13. 638    unsigned map_size = PAGE_ALIGN(info->fix.smem_len);  
  14. 639  
  15. 640    dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);  
  16. 641  
  17. 642    info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,  
  18. 643                           &map_dma, GFP_KERNEL);  
  19. 644  
  20. 645    if (info->screen_base) {  
  21. 646        /* prevent initial garbage on screen */  
  22. 647        dprintk("map_video_memory: clear %p:%08x\n",  
  23. 648            info->screen_base, map_size);  
  24. 649        memset(info->screen_base, 0x00, map_size);  
  25. 650  
  26. 651        info->fix.smem_start = map_dma;  
  27. 652  
  28. 653        dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",  
  29. 654            info->fix.smem_start, info->screen_base, map_size);  
  30. 655    }  
  31. 656  
  32. 657    return info->screen_base ? 0 : -ENOMEM;  
  33. 658}  
 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控制器:

 
 
  1. 677/* 
  2. 678 * s3c2410fb_init_registers - Initialise all LCD-related registers 
  3. 679 */  
  4. 680static int s3c2410fb_init_registers(struct fb_info *info)  
  5. 681{  
  6. 682    struct s3c2410fb_info *fbi = info->par;  
  7. 683    struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;  
  8. 684    unsigned long flags;  
  9. 685    void __iomem *regs = fbi->io;  
  10. 686    void __iomem *tpal;  
  11. 687    void __iomem *lpcsel;  
  12. 688  
  13. 689    if (is_s3c2412(fbi)) {  
  14. 690        tpal = regs + S3C2412_TPAL;  
  15. 691        lpcsel = regs + S3C2412_TCONSEL;  
  16. 692    } else {  
  17. 693        tpal = regs + S3C2410_TPAL;  
  18. 694        lpcsel = regs + S3C2410_LPCSEL;  
  19. 695    }  
  20. 696  
  21. 697    /* Initialise LCD with values from haret */  
  22. 698  
  23. 699    local_irq_save(flags);  
  24. 700  
  25. 701    /* modify the gpio(s) with interrupts set (bjd) */  
  26. 702  
  27. 703    modify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask);  
  28. 704    modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);  
  29. 705    modify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask);  
  30. 706    modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);  
  31. 707  
  32. 708    local_irq_restore(flags);  
  33. 709  
  34. 710    dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel);  
  35. 711    writel(mach_info->lpcsel, lpcsel);  
  36. 712  
  37. 713    dprintk("replacing TPAL %08x\n", readl(tpal));  
  38. 714  
  39. 715    /* ensure temporary palette disabled */  
  40. 716    writel(0x00, tpal);  
  41. 717  
  42. 718    return 0;  
  43. 719}  
 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。

 
 
  1. 668static inline void modify_gpio(void __iomem *reg,  
  2. 669                   unsigned long set, unsigned long mask)  
  3. 670{  
  4. 671    unsigned long tmp;  
  5. 672  
  6. 673    tmp = readl(reg) & ~mask;  
  7. 674    writel(tmp | set, reg);  
  8. 675}  
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可变参数进行检查,其定义如下:

 
 
  1. 109/* 
  2. 110 *  s3c2410fb_check_var(): 
  3. 111 *  Get the video params out of 'var'. If a value doesn't fit, round it up, 
  4. 112 *  if it's too big, return -EINVAL. 
  5. 113 * 
  6. 114 */  
  7. 115static int s3c2410fb_check_var(struct fb_var_screeninfo *var,  
  8. 116                   struct fb_info *info)  
  9. 117{  
  10. 118    struct s3c2410fb_info *fbi = info->par;  
  11. 119    struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;  
  12. 120    struct s3c2410fb_display *display = NULL;  
  13. 121    struct s3c2410fb_display *default_display = mach_info->displays +  
  14. 122                            mach_info->default_display;  
  15. 123    int type = default_display->type;  
  16. 124    unsigned i;  
  17. 125  
  18. 126    dprintk("check_var(var=%p, info=%p)\n", var, info);  
  19. 127  
  20. 128    /* validate x/y resolution */  
  21. 129    /* choose default mode if possible */  
  22. 130    if (var->yres == default_display->yres &&  
  23. 131        var->xres == default_display->xres &&  
  24. 132        var->bits_per_pixel == default_display->bpp)  
  25. 133        display = default_display;  
  26. 134    else  
  27. 135        for (i = 0; i < mach_info->num_displays; i++)  
  28. 136            if (type == mach_info->displays[i].type &&  
  29. 137                var->yres == mach_info->displays[i].yres &&  
  30. 138                var->xres == mach_info->displays[i].xres &&  
  31. 139                var->bits_per_pixel == mach_info->displays[i].bpp) {  
  32. 140                display = mach_info->displays + i;  
  33. 141                break;  
  34. 142            }  
  35. 143  
  36. 144    if (!display) {  
  37. 145        dprintk("wrong resolution or depth %dx%d at %d bpp\n",  
  38. 146            var->xres, var->yres, var->bits_per_pixel);  
  39. 147        return -EINVAL;  
  40. 148    }  
  41. 149  
  42. 150    /* it is always the size as the display */  
  43. 151    var->xres_virtual = display->xres;  
  44. 152    var->yres_virtual = display->yres;  
  45. 153    var->height = display->height;  
  46. 154    var->width = display->width;  
  47. 155  
  48. 156    /* copy lcd settings */  
  49. 157    var->pixclock = display->pixclock;  
  50. 158    var->left_margin = display->left_margin;  
  51. 159    var->right_margin = display->right_margin;  
  52. 160    var->upper_margin = display->upper_margin;  
  53. 161    var->lower_margin = display->lower_margin;  
  54. 162    var->vsync_len = display->vsync_len;  
  55. 163    var->hsync_len = display->hsync_len;  
  56. 164  
  57. 165    fbi->regs.lcdcon5 = display->lcdcon5;  
  58. 166    /* set display type */  
  59. 167    fbi->regs.lcdcon1 = display->type;  
  60. 168  
  61. 169    var->transp.offset = 0;  
  62. 170    var->transp.length = 0;  
  63. 171    /* set r/g/b positions */  
  64. 172    switch (var->bits_per_pixel) {  
  65. 173    case 1:  
  66. 174    case 2:  
  67. 175    case 4:  
  68. 176        var->red.offset = 0;  
  69. 177        var->red.length = var->bits_per_pixel;  
  70. 178        var->green  = var->red;  
  71. 179        var->blue   = var->red;  
  72. 180        break;  
  73. 181    case 8:  
  74. 182        if (display->type != S3C2410_LCDCON1_TFT) {  
  75. 183            /* 8 bpp 332 */  
  76. 184            var->red.length     = 3;  
  77. 185            var->red.offset     = 5;  
  78. 186            var->green.length   = 3;  
  79. 187            var->green.offset   = 2;  
  80. 188            var->blue.length    = 2;  
  81. 189            var->blue.offset    = 0;  
  82. 190        } else {  
  83. 191            var->red.offset     = 0;  
  84. 192            var->red.length     = 8;  
  85. 193            var->green      = var->red;  
  86. 194            var->blue       = var->red;  
  87. 195        }  
  88. 196        break;  
  89. 197    case 12:  
  90. 198        /* 12 bpp 444 */  
  91. 199        var->red.length     = 4;  
  92. 200        var->red.offset     = 8;  
  93. 201        var->green.length   = 4;  
  94. 202        var->green.offset   = 4;  
  95. 203        var->blue.length    = 4;  
  96. 204        var->blue.offset    = 0;  
  97. 205        break;  
  98. 206  
  99. 207    default:  
  100. 208    case 16:  
  101. 209        if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) {  
  102. 210            /* 16 bpp, 565 format */  
  103. 211            var->red.offset     = 11;  
  104. 212            var->green.offset   = 5;  
  105. 213            var->blue.offset    = 0;  
  106. 214            var->red.length     = 5;  
  107. 215            var->green.length   = 6;  
  108. 216            var->blue.length    = 5;  
  109. 217        } else {  
  110. 218            /* 16 bpp, 5551 format */  
  111. 219            var->red.offset     = 11;  
  112. 220            var->green.offset   = 6;  
  113. 221            var->blue.offset    = 1;  
  114. 222            var->red.length     = 5;  
  115. 223            var->green.length   = 5;  
  116. 224            var->blue.length    = 5;  
  117. 225        }  
  118. 226        break;  
  119. 227    case 32:  
  120. 228        /* 24 bpp 888 and 8 dummy */  
  121. 229        var->red.length     = 8;  
  122. 230        var->red.offset     = 16;  
  123. 231        var->green.length   = 8;  
  124. 232        var->green.offset   = 8;  
  125. 233        var->blue.length    = 8;  
  126. 234        var->blue.offset    = 0;  
  127. 235        break;  
  128. 236    }  
  129. 237    return 0;  
  130. 238}  
 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:

 
 
  1. 747static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)  
  2. 748{  
  3. 749    struct s3c2410fb_info *fbi = dev_id;  
  4. 750    void __iomem *irq_base = fbi->irq_base;  
  5. 751    unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);  
  6. 752  
  7. 753    if (lcdirq & S3C2410_LCDINT_FRSYNC) {  
  8. 754        if (fbi->palette_ready)  
  9. 755            s3c2410fb_write_palette(fbi);  
  10. 756  
  11. 757        writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);  
  12. 758        writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);  
  13. 759    }  
  14. 760  
  15. 761    return IRQ_HANDLED;  
  16. 762}  
 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函数用于填充调色板,其定义如下:

 
 
  1. 721static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)  
  2. 722{  
  3. 723    unsigned int i;  
  4. 724    void __iomem *regs = fbi->io;  
  5. 725  
  6. 726    fbi->palette_ready = 0;  
  7. 727  
  8. 728    for (i = 0; i < 256; i++) {  
  9. 729        unsigned long ent = fbi->palette_buffer[i];  
  10. 730        if (ent == PALETTE_BUFF_CLEAR)  
  11. 731            continue;  
  12. 732  
  13. 733        writel(ent, regs + S3C2410_TFTPAL(i));  
  14. 734  
  15. 735        /* it seems the only way to know exactly 
  16. 736         * if the palette wrote ok, is to check 
  17. 737         * to see if the value verifies ok 
  18. 738         */  
  19. 739  
  20. 740        if (readw(regs + S3C2410_TFTPAL(i)) == ent)  
  21. 741            fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR;  
  22. 742        else  
  23. 743            fbi->palette_ready = 1;   /* retry */  
  24. 744    }  
  25. 745}  
 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函数定义如下:

 
 
  1. 1021static int __devexit s3c2410fb_remove(struct platform_device *pdev)  
  2. 1022{  
  3. 1023    struct fb_info *fbinfo = platform_get_drvdata(pdev);  
  4. 1024    struct s3c2410fb_info *info = fbinfo->par;  
  5. 1025    int irq;  
  6. 1026  
  7. 1027    unregister_framebuffer(fbinfo);  
  8. 1028    s3c2410fb_cpufreq_deregister(info);  
  9. 1029  
  10. 1030    s3c2410fb_lcd_enable(info, 0);  
  11. 1031    msleep(1);  
  12. 1032  
  13. 1033    s3c2410fb_unmap_video_memory(fbinfo);  
  14. 1034  
  15. 1035    if (info->clk) {  
  16. 1036        clk_disable(info->clk);  
  17. 1037        clk_put(info->clk);  
  18. 1038        info->clk = NULL;  
  19. 1039    }  
  20. 1040  
  21. 1041    irq = platform_get_irq(pdev, 0);  
  22. 1042    free_irq(irq, info);  
  23. 1043  
  24. 1044    iounmap(info->io);  
  25. 1045  
  26. 1046    release_resource(info->mem);  
  27. 1047    kfree(info->mem);  
  28. 1048  
  29. 1049    platform_set_drvdata(pdev, NULL);  
  30. 1050    framebuffer_release(fbinfo);  
  31. 1051  
  32. 1052    return 0;  
  33. 1053}  
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函数定义如下:

 
 
  1. 530static void s3c2410fb_lcd_enable(struct s3c2410fb_info *fbi, int enable)  
  2. 531{  
  3. 532    unsigned long flags;  
  4. 533  
  5. 534    local_irq_save(flags);  
  6. 535  
  7. 536    if (enable)  
  8. 537        fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID;  
  9. 538    else  
  10. 539        fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;  
  11. 540  
  12. 541    writel(fbi->regs.lcdcon1, fbi->io + S3C2410_LCDCON1);  
  13. 542  
  14. 543    local_irq_restore(flags);  
  15. 544}  
 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函数以支持电源管理功能。

 
 
  1. 1057/* suspend and resume support for the lcd controller */  
  2. 1058static int s3c2410fb_suspend(struct platform_device *dev, pm_message_t state)  
  3. 1059{  
  4. 1060    struct fb_info     *fbinfo = platform_get_drvdata(dev);  
  5. 1061    struct s3c2410fb_info *info = fbinfo->par;  
  6. 1062  
  7. 1063    s3c2410fb_lcd_enable(info, 0);  
  8. 1064  
  9. 1065    /* sleep before disabling the clock, we need to ensure 
  10. 1066     * the LCD DMA engine is not going to get back on the bus 
  11. 1067     * before the clock goes off again (bjd) */  
  12. 1068  
  13. 1069    msleep(1);  
  14. 1070    clk_disable(info->clk);  
  15. 1071  
  16. 1072    return 0;  
  17. 1073}  
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函数定义如下:

 
 
  1. 1075static int s3c2410fb_resume(struct platform_device *dev)  
  2. 1076{  
  3. 1077    struct fb_info     *fbinfo = platform_get_drvdata(dev);  
  4. 1078    struct s3c2410fb_info *info = fbinfo->par;  
  5. 1079  
  6. 1080    clk_enable(info->clk);  
  7. 1081    msleep(1);  
  8. 1082  
  9. 1083    s3c2410fb_init_registers(fbinfo);  
  10. 1084  
  11. 1085    /* re-activate our display after resume */  
  12. 1086    s3c2410fb_activate_var(fbinfo);  
  13. 1087    s3c2410fb_blank(FB_BLANK_UNBLANK, fbinfo);  
  14. 1088  
  15. 1089    return 0;  
  16. 1090}  
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函数集定义如下:

 
 
  1. 615static struct fb_ops s3c2410fb_ops = {  
  2. 616    .owner      = THIS_MODULE,  
  3. 617    .fb_check_var   = s3c2410fb_check_var,  
  4. 618    .fb_set_par = s3c2410fb_set_par,  
  5. 619    .fb_blank   = s3c2410fb_blank,  
  6. 620    .fb_setcolreg   = s3c2410fb_setcolreg,  
  7. 621    .fb_fillrect    = cfb_fillrect,  
  8. 622    .fb_copyarea    = cfb_copyarea,  
  9. 623    .fb_imageblit   = cfb_imageblit,  
  10. 624};  
 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函数的实现:

 
 
  1. 416/* 
  2. 417 *      s3c2410fb_set_par - Alters the hardware state. 
  3. 418 *      @info: frame buffer structure that represents a single frame buffer 
  4. 419 * 
  5. 420 */  
  6. 421static int s3c2410fb_set_par(struct fb_info *info)  
  7. 422{  
  8. 423    struct fb_var_screeninfo *var = &info->var;  
  9. 424  
  10. 425    switch (var->bits_per_pixel) {  
  11. 426    case 32:  
  12. 427    case 16:  
  13. 428    case 12:  
  14. 429        info->fix.visual = FB_VISUAL_TRUECOLOR;  
  15. 430        break;  
  16. 431    case 1:  
  17. 432        info->fix.visual = FB_VISUAL_MONO01;  
  18. 433        break;  
  19. 434    default:  
  20. 435        info->fix.visual = FB_VISUAL_PSEUDOCOLOR;  
  21. 436        break;  
  22. 437    }  
  23. 438  
  24. 439    info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;  
  25. 440  
  26. 441    /* activate this new configuration */  
  27. 442  
  28. 443    s3c2410fb_activate_var(info);  
  29. 444    return 0;  
  30. 445}  
 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指定的配置。

 
 
  1. 361/* s3c2410fb_activate_var 
  2. 362 * 
  3. 363 * activate (set) the controller from the given framebuffer 
  4. 364 * information 
  5. 365 */  
  6. 366static void s3c2410fb_activate_var(struct fb_info *info)  
  7. 367{  
  8. 368    struct s3c2410fb_info *fbi = info->par;  
  9. 369    void __iomem *regs = fbi->io;  
  10. 370    int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;  
  11. 371    struct fb_var_screeninfo *var = &info->var;  
  12. 372    int clkdiv;  
  13. 373  
  14. 374    clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi, var->pixclock), 2);  
  15. 375  
  16. 376    dprintk("%s: var->xres  = %d\n", __func__, var->xres);  
  17. 377    dprintk("%s: var->yres  = %d\n", __func__, var->yres);  
  18. 378    dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);  
  19. 379  
  20. 380    if (type == S3C2410_LCDCON1_TFT) {  
  21. 381        s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);  
  22. 382        --clkdiv;  
  23. 383        if (clkdiv < 0)  
  24. 384            clkdiv = 0;  
  25. 385    } else {  
  26. 386        s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);  
  27. 387        if (clkdiv < 2)  
  28. 388            clkdiv = 2;  
  29. 389    }  
  30. 390  
  31. 391    fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);  
  32. 392  
  33. 393    /* write new registers */  
  34. 394  
  35. 395    dprintk("new register set:\n");  
  36. 396    dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);  
  37. 397    dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);  
  38. 398    dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);  
  39. 399    dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);  
  40. 400    dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);  
  41. 401  
  42. 402    writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,  
  43. 403        regs + S3C2410_LCDCON1);  
  44. 404    writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);  
  45. 405    writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);  
  46. 406    writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);  
  47. 407    writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);  
  48. 408  
  49. 409    /* set lcd address pointers */  
  50. 410    s3c2410fb_set_lcdaddr(info);  
  51. 411  
  52. 412    fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,  
  53. 413    writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);  
  54. 414}  
 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函数定义如下:

 
 
  1.  86/* s3c2410fb_calc_pixclk() 
  2.  87 * 
  3.  88 * calculate divisor for clk->pixclk 
  4.  89 */  
  5.  90static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,  
  6.  91                      unsigned long pixclk)  
  7.  92{  
  8.  93    unsigned long clk = fbi->clk_rate;  
  9.  94    unsigned long long div;  
  10.  95  
  11.  96    /* pixclk is in picoseconds, our clock is in Hz 
  12.  97     * 
  13.  98     * Hz -> picoseconds is / 10^-12 
  14.  99     */  
  15. 100  
  16. 101    div = (unsigned long long)clk * pixclk;  
  17. 102    div >>= 12;         /* div / 2^12 */  
  18. 103    do_div(div, 625 * 625UL * 625); /* div / 5^12 */  
  19. 104  
  20. 105    dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);  
  21. 106    return div;  
  22. 107}  
  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函数:

 
 
  1. 300/* s3c2410fb_calculate_tft_lcd_regs 
  2. 301 * 
  3. 302 * calculate register values from var settings 
  4. 303 */  
  5. 304static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,  
  6. 305                         struct s3c2410fb_hw *regs)  
  7. 306{  
  8. 307    const struct s3c2410fb_info *fbi = info->par;  
  9. 308    const struct fb_var_screeninfo *var = &info->var;  
  10. 309  
  11. 310    switch (var->bits_per_pixel) {  
  12. 311    case 1:  
  13. 312        regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;  
  14. 313        break;  
  15. 314    case 2:  
  16. 315        regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;  
  17. 316        break;  
  18. 317    case 4:  
  19. 318        regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;  
  20. 319        break;  
  21. 320    case 8:  
  22. 321        regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;  
  23. 322        regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |  
  24. 323                 S3C2410_LCDCON5_FRM565;  
  25. 324        regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;  
  26. 325        break;  
  27. 326    case 16:  
  28. 327        regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;  
  29. 328        regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;  
  30. 329        regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;  
  31. 330        break;  
  32. 331    case 32:  
  33. 332        regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;  
  34. 333        regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |  
  35. 334                   S3C2410_LCDCON5_HWSWP |  
  36. 335                   S3C2410_LCDCON5_BPP24BL);  
  37. 336        break;  
  38. 337    default:  
  39. 338        /* invalid pixel depth */  
  40. 339        dev_err(fbi->dev, "invalid bpp %d\n",  
  41. 340            var->bits_per_pixel);  
  42. 341    }  
  43. 342    /* update X/Y info */  
  44. 343    dprintk("setting vert: up=%d, low=%d, sync=%d\n",  
  45. 344        var->upper_margin, var->lower_margin, var->vsync_len);  
  46. 345  
  47. 346    dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",  
  48. 347        var->left_margin, var->right_margin, var->hsync_len);  
  49. 348  
  50. 349    regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |  
  51. 350            S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |  
  52. 351            S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |  
  53. 352            S3C2410_LCDCON2_VSPW(var->vsync_len - 1);  
  54. 353  
  55. 354    regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |  
  56. 355            S3C2410_LCDCON3_HFPD(var->left_margin - 1) |  
  57. 356            S3C2410_LCDCON3_HOZVAL(var->xres - 1);  
  58. 357  
  59. 358    regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);  
  60. 359}  
 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函数:

 
 
  1. 240/* s3c2410fb_calculate_stn_lcd_regs 
  2. 241 * 
  3. 242 * calculate register values from var settings 
  4. 243 */  
  5. 244static void s3c2410fb_calculate_stn_lcd_regs(const struct fb_info *info,  
  6. 245                         struct s3c2410fb_hw *regs)  
  7. 246{  
  8. 247    const struct s3c2410fb_info *fbi = info->par;  
  9. 248    const struct fb_var_screeninfo *var = &info->var;  
  10. 249    int type = regs->lcdcon1 & ~S3C2410_LCDCON1_TFT;  
  11. 250    int hs = var->xres >> 2;  
  12. 251    unsigned wdly = (var->left_margin >> 4) - 1;  
  13. 252    unsigned wlh = (var->hsync_len >> 4) - 1;  
  14. 253  
  15. 254    if (type != S3C2410_LCDCON1_STN4)  
  16. 255        hs >>= 1;  
  17. 256  
  18. 257    switch (var->bits_per_pixel) {  
  19. 258    case 1:  
  20. 259        regs->lcdcon1 |= S3C2410_LCDCON1_STN1BPP;  
  21. 260        break;  
  22. 261    case 2:  
  23. 262        regs->lcdcon1 |= S3C2410_LCDCON1_STN2GREY;  
  24. 263        break;  
  25. 264    case 4:  
  26. 265        regs->lcdcon1 |= S3C2410_LCDCON1_STN4GREY;  
  27. 266        break;  
  28. 267    case 8:  
  29. 268        regs->lcdcon1 |= S3C2410_LCDCON1_STN8BPP;  
  30. 269        hs *= 3;  
  31. 270        break;  
  32. 271    case 12:  
  33. 272        regs->lcdcon1 |= S3C2410_LCDCON1_STN12BPP;  
  34. 273        hs *= 3;  
  35. 274        break;  
  36. 275  
  37. 276    default:  
  38. 277        /* invalid pixel depth */  
  39. 278        dev_err(fbi->dev, "invalid bpp %d\n",  
  40. 279            var->bits_per_pixel);  
  41. 280    }  
  42. 281    /* update X/Y info */  
  43. 282    dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",  
  44. 283        var->left_margin, var->right_margin, var->hsync_len);  
  45. 284  
  46. 285    regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1);  
  47. 286  
  48. 287    if (wdly > 3)  
  49. 288        wdly = 3;  
  50. 289  
  51. 290    if (wlh > 3)  
  52. 291        wlh = 3;  
  53. 292  
  54. 293    regs->lcdcon3 = S3C2410_LCDCON3_WDLY(wdly) |  
  55. 294            S3C2410_LCDCON3_LINEBLANK(var->right_margin / 8) |  
  56. 295            S3C2410_LCDCON3_HOZVAL(hs - 1);  
  57. 296  
  58. 297    regs->lcdcon4 = S3C2410_LCDCON4_WLH(wlh);  
  59. 298}  
 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:

 
 
  1. 59/* s3c2410fb_set_lcdaddr 
  2. 60 * 
  3. 61 * initialise lcd controller address pointers 
  4. 62 */  
  5. 63static void s3c2410fb_set_lcdaddr(struct fb_info *info)  
  6. 64{  
  7. 65    unsigned long saddr1, saddr2, saddr3;  
  8. 66    struct s3c2410fb_info *fbi = info->par;  
  9. 67    void __iomem *regs = fbi->io;  
  10. 68  
  11. 69    saddr1  = info->fix.smem_start >> 1;  
  12. 70    saddr2  = info->fix.smem_start;  
  13. 71    saddr2 += info->fix.line_length * info->var.yres;  
  14. 72    saddr2 >>= 1;  
  15. 73  
  16. 74    saddr3 = S3C2410_OFFSIZE(0) |  
  17. 75         S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);  
  18. 76  
  19. 77    dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);  
  20. 78    dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);  
  21. 79    dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);  
  22. 80  
  23. 81    writel(saddr1, regs + S3C2410_LCDSADDR1);  
  24. 82    writel(saddr2, regs + S3C2410_LCDSADDR2);  
  25. 83    writel(saddr3, regs + S3C2410_LCDSADDR3);  
  26. 84}  
  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 宏定义如下:

 
 
  1. #define S3C2410_OFFSIZE(x)  ((x) << 11)  
#define S3C2410_OFFSIZE(x)  ((x) << 11)

75行,S3C2410_PAGEWIDTH宏定义如下:

 
 
  1. #define S3C2410_PAGEWIDTH(x)    (x)  
#define S3C2410_PAGEWIDTH(x)    (x)

回到s3c2410fb_activate_var函数,
413行,使能LCD控制器。
至此,s3c2410fb_activate_var函数就全部分析完了。
同时,s3c2410fb_set_par函数也就全部分析完了。
下面来分析fb_ops.fb_bank函数s3c2410fb_blank的实现:

 
 
  1. 547/* 
  2. 548 *      s3c2410fb_blank 
  3. 549 *  @blank_mode: the blank mode we want. 
  4. 550 *  @info: frame buffer structure that represents a single frame buffer 
  5. 551 * 
  6. 552 *  Blank the screen if blank_mode != 0, else unblank. Return 0 if 
  7. 553 *  blanking succeeded, != 0 if un-/blanking failed due to e.g. a 
  8. 554 *  video mode which doesn't support it. Implements VESA suspend 
  9. 555 *  and powerdown modes on hardware that supports disabling hsync/vsync: 
  10. 556 * 
  11. 557 *  Returns negative errno on error, or zero on success. 
  12. 558 * 
  13. 559 */  
  14. 560static int s3c2410fb_blank(int blank_mode, struct fb_info *info)  
  15. 561{  
  16. 562    struct s3c2410fb_info *fbi = info->par;  
  17. 563    void __iomem *tpal_reg = fbi->io;  
  18. 564  
  19. 565    dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);  
  20. 566  
  21. 567    tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;  
  22. 568  
  23. 569    if (blank_mode == FB_BLANK_POWERDOWN) {  
  24. 570        s3c2410fb_lcd_enable(fbi, 0);  
  25. 571    } else {  
  26. 572        s3c2410fb_lcd_enable(fbi, 1);  
  27. 573    }  
  28. 574  
  29. 575    if (blank_mode == FB_BLANK_UNBLANK)  
  30. 576        writel(0x0, tpal_reg);  
  31. 577    else {  
  32. 578        dprintk("setting TPAL to output 0x000000\n");  
  33. 579        writel(S3C2410_TPAL_EN, tpal_reg);  
  34. 580    }  
  35. 581  
  36. 582    return 0;  
  37. 583}  
 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宏定义如下:

 
 
  1. #define S3C2410_TPAL_EN     (1<<24)  
#define S3C2410_TPAL_EN     (1<<24)

下面来分析fb_ops.fb_setcolreg函数s3c2410fb_setcolreg:

 
 
  1. 479static int s3c2410fb_setcolreg(unsigned regno,  
  2. 480                   unsigned red, unsigned green, unsigned blue,  
  3. 481                   unsigned transp, struct fb_info *info)  
  4. 482{  
  5. 483    struct s3c2410fb_info *fbi = info->par;  
  6. 484    void __iomem *regs = fbi->io;  
  7. 485    unsigned int val;  
  8. 486  
  9. 487    /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n", 
  10. 488           regno, red, green, blue); */  
  11. 489  
  12. 490    switch (info->fix.visual) {  
  13. 491    case FB_VISUAL_TRUECOLOR:  
  14. 492        /* true-colour, use pseudo-palette */  
  15. 493  
  16. 494        if (regno < 16) {  
  17. 495            u32 *pal = info->pseudo_palette;  
  18. 496  
  19. 497            val  = chan_to_field(red,   &info->var.red);  
  20. 498            val |= chan_to_field(green, &info->var.green);  
  21. 499            val |= chan_to_field(blue,  &info->var.blue);  
  22. 500  
  23. 501            pal[regno] = val;  
  24. 502        }  
  25. 503        break;  
  26. 504  
  27. 505    case FB_VISUAL_PSEUDOCOLOR:  
  28. 506        if (regno < 256) {  
  29. 507            /* currently assume RGB 5-6-5 mode */  
  30. 508  
  31. 509            val  = (red   >>  0) & 0xf800;  
  32. 510            val |= (green >>  5) & 0x07e0;  
  33. 511            val |= (blue  >> 11) & 0x001f;  
  34. 512  
  35. 513            writel(val, regs + S3C2410_TFTPAL(regno));  
  36. 514            schedule_palette_update(fbi, regno, val);  
  37. 515        }  
  38. 516  
  39. 517        break;  
  40. 518  
  41. 519    default:  
  42. 520        return 1;   /* unknown type */  
  43. 521    }  
  44. 522  
  45. 523    return 0;  
  46. 524}  
 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函数定义如下:

 
 
  1. 471static inline unsigned int chan_to_field(unsigned int chan,  
  2. 472                     struct fb_bitfield *bf)  
  3. 473{  
  4. 474    chan &= 0xffff;  
  5. 475    chan >>= 16 - bf->length;  
  6. 476    return chan << bf->offset;  
  7. 477}  
 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函数定义如下:
  1. 447static void schedule_palette_update(struct s3c2410fb_info *fbi,  
  2. 448                    unsigned int regno, unsigned int val)  
  3. 449{  
  4. 450    unsigned long flags;  
  5. 451    unsigned long irqen;  
  6. 452    void __iomem *irq_base = fbi->irq_base;  
  7. 453  
  8. 454    local_irq_save(flags);  
  9. 455  
  10. 456    fbi->palette_buffer[regno] = val;  
  11. 457  
  12. 458    if (!fbi->palette_ready) {  
  13. 459        fbi->palette_ready = 1;  
  14. 460  
  15. 461        /* enable IRQ */  
  16. 462        irqen = readl(irq_base + S3C24XX_LCDINTMSK);  
  17. 463        irqen &= ~S3C2410_LCDINT_FRSYNC;  
  18. 464        writel(irqen, irq_base + S3C24XX_LCDINTMSK);  
  19. 465    }  
  20. 466  
  21. 467    local_irq_restore(flags);  
  22. 468} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值